Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions OpenBitFun-Installer/scripts/build-installer.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const REQUIRED_PAYLOAD_FILES = [
"mobile-web/dist/index.html",
"resources/ext-host/extension-host.js",
"resources/worker_host.js",
"flashgrep/flashgrep-x86_64-pc-windows-msvc.exe",
];

function getMode(args) {
Expand Down Expand Up @@ -288,7 +287,6 @@ if (appExePath) {
"swiftshader",
"mobile-web",
"frontend",
"flashgrep",
];
for (const dirName of runtimeDirs) {
const srcDir = path.join(releaseDir, dirName);
Expand Down
3 changes: 1 addition & 2 deletions OpenBitFun-Installer/src-tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ use std::path::{Path, PathBuf};
use zip::write::FileOptions;
use zip::{CompressionMethod, ZipWriter};

const REQUIRED_PAYLOAD_FILES: [&str; 6] = [
const REQUIRED_PAYLOAD_FILES: [&str; 5] = [
"openbitfun-desktop.exe",
"frontend/dist/index.html",
"mobile-web/dist/index.html",
"resources/ext-host/extension-host.js",
"resources/worker_host.js",
"flashgrep/flashgrep-x86_64-pc-windows-msvc.exe",
];

fn main() {
Expand Down
3 changes: 1 addition & 2 deletions OpenBitFun-Installer/src-tauri/src/installer/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,12 @@ struct WindowsInstallState {

const MIN_WINDOWS_APP_EXE_BYTES: u64 = 5 * 1024 * 1024;
const PAYLOAD_MANIFEST_FILE: &str = "payload-manifest.json";
const REQUIRED_PAYLOAD_FILES: [&str; 6] = [
const REQUIRED_PAYLOAD_FILES: [&str; 5] = [
MAIN_APP_EXE,
"frontend/dist/index.html",
"mobile-web/dist/index.html",
"resources/ext-host/extension-host.js",
"resources/worker_host.js",
"flashgrep/flashgrep-x86_64-pc-windows-msvc.exe",
];
const INSTALLER_STATE_FILE: &str = "installer-state.json";
const DEFAULT_MODEL_CONTEXT_WINDOW: u64 = 200_000;
Expand Down
12 changes: 8 additions & 4 deletions resources/flashgrep/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
Place the prebuilt `flashgrep` daemon binary in this directory.
Flashgrep distribution is temporarily suspended. The platform binaries have been removed,
and desktop development, packaging, and the Windows installer no longer require them.
The Web UI hides accelerated-search settings and index controls, including on remote
workspaces and peer devices. Saved preferences and backend implementations are retained.

To restore the feature, restore the binaries and desktop preparation/bundling steps,
then enable `WORKSPACE_SEARCH_AVAILABLE` in the Web UI.

Pinned release:

- `v0.2.16` from `wgqqqqq/flashgrep`

Expected filenames:
Expected filenames (restoration reference):

- macOS x86_64: `flashgrep-x86_64-apple-darwin`
- macOS arm64: `flashgrep-aarch64-apple-darwin`
Expand All @@ -14,5 +20,3 @@ Expected filenames:
- Windows arm64: `flashgrep-aarch64-pc-windows-msvc.exe`

macOS binaries are ad-hoc signed after download so local development can execute them directly.

OpenBitFun dev/build scripts load the daemon from this repository-relative path.
Binary file removed resources/flashgrep/flashgrep-aarch64-apple-darwin
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed resources/flashgrep/flashgrep-x86_64-apple-darwin
Binary file not shown.
Binary file not shown.
Binary file not shown.
11 changes: 4 additions & 7 deletions scripts/desktop-tauri-build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
statSync,
writeFileSync,
} from 'fs';
import { ensureFlashgrepBinary } from './prepare-flashgrep-resource.mjs';
import { extractProductConfigArg } from './product-customization/cli.mjs';
import { productBuildEnvironment } from './product-customization/projections.mjs';
import { resolveProductDefinition } from './product-customization/resolver.mjs';
Expand Down Expand Up @@ -54,11 +53,8 @@ async function main() {

const desktopDir = join(ROOT, 'src', 'apps', 'desktop');
preparePluginHost();
const flashgrepBinary = prepareMacOSFlashgrepForSigning(
ensureFlashgrepBinary(),
desktopDir,
);
process.env.FLASHGREP_DAEMON_BIN = flashgrepBinary;
// Flashgrep distribution is temporarily suspended.
const flashgrepBinary = null;
// Tauri CLI reads CI and rejects numeric "1" (common in CI providers).
process.env.CI = 'true';
if (process.platform === 'darwin' && requestsDmgBundle(forward)) {
Expand Down Expand Up @@ -107,7 +103,7 @@ async function main() {

if (r.status === 0 && forward.includes('--no-bundle')) {
console.warn(
'[tauri-build] No bundle was produced. The raw desktop executable depends on its adjacent frontend, flashgrep, mobile-web, and resources directories and must not be distributed by itself.'
'[tauri-build] No bundle was produced. The raw desktop executable depends on its adjacent frontend, mobile-web, and resources directories and must not be distributed by itself.'
);
}

Expand Down Expand Up @@ -373,6 +369,7 @@ function injectTargetFlashgrepResource(config, desktopDir, flashgrepBinary) {
}

function bundledFlashgrepResources(primaryBinary) {
if (!primaryBinary) return [];
const binaries = [primaryBinary];

if (process.platform === 'win32') {
Expand Down
18 changes: 18 additions & 0 deletions scripts/desktop-tauri-build.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,24 @@ test('Desktop Tauri projection consumes only the resolved member identity', () =
}
});

test('Desktop packaging works without the suspended Flashgrep resource', () => {
const fixture = join(tmpdir(), `openbitfun-without-flashgrep-${process.pid}-${Date.now()}`);
mkdirSync(fixture, { recursive: true });
const baseConfig = join(fixture, 'tauri.conf.json');
writeFileSync(baseConfig, JSON.stringify({
bundle: { resources: { '../../../resources/flashgrep': 'flashgrep' } },
}));
try {
const generated = prepareTauriConfig(baseConfig, { desktopDir: fixture });
const config = JSON.parse(readFileSync(generated, 'utf8'));
assert.ok(Object.entries(config.bundle.resources).every(([source, target]) =>
!source.includes('flashgrep') && !target.includes('flashgrep')));
assert.equal(config.bundle.resources['../../../dist'], 'frontend/dist');
} finally {
rmSync(fixture, { force: true, recursive: true });
}
});

test('Windows updater installs NSIS packages without showing its progress window', () => {
const fixture = join(tmpdir(), `openbitfun-tauri-updater-${process.pid}-${Date.now()}`);
const baseConfig = join(fixture, 'tauri.conf.json');
Expand Down
82 changes: 2 additions & 80 deletions scripts/dev.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -575,63 +575,6 @@ async function startDesktopPreview() {
await new Promise(() => {});
}

function flashgrepBinaryNames() {
if (process.platform === 'win32' && process.arch === 'x64') {
return ['flashgrep-x86_64-pc-windows-msvc.exe'];
}
if (process.platform === 'win32' && process.arch === 'arm64') {
return ['flashgrep-aarch64-pc-windows-msvc.exe'];
}
if (process.platform === 'darwin' && process.arch === 'x64') {
return ['flashgrep-x86_64-apple-darwin'];
}
if (process.platform === 'darwin' && process.arch === 'arm64') {
return ['flashgrep-aarch64-apple-darwin'];
}
if (process.platform === 'linux' && process.arch === 'x64') {
return [
'flashgrep-x86_64-unknown-linux-musl',
'flashgrep-x86_64-unknown-linux-gnu',
];
}
if (process.platform === 'linux' && process.arch === 'arm64') {
return [
'flashgrep-aarch64-unknown-linux-musl',
'flashgrep-aarch64-unknown-linux-gnu',
];
}
return [process.platform === 'win32' ? 'flashgrep.exe' : 'flashgrep'];
}

function flashgrepBinaryName() {
return flashgrepBinaryNames()[0];
}

function ensureFlashgrepBinary() {
for (const binaryName of flashgrepBinaryNames()) {
const binaryPath = path.join(ROOT_DIR, 'resources', 'flashgrep', binaryName);
if (!fs.existsSync(binaryPath)) {
continue;
}
return { ok: true, binaryPath };
}

return {
ok: false,
error: new Error(
`flashgrep binary not found for ${process.platform}/${process.arch}. Expected one of: ${flashgrepBinaryNames()
.map((name) => `resources/flashgrep/${name}`)
.join(', ')}`
),
};
}

async function ensureFlashgrepBundleResource() {
const helperUrl = pathToFileURL(path.join(__dirname, 'prepare-flashgrep-resource.mjs')).href;
const helper = await import(helperUrl);
return helper.ensureFlashgrepBinary();
}

/**
* Main entry
*/
Expand Down Expand Up @@ -661,7 +604,7 @@ async function main() {
let currentStep = 1;

// Step 1: Run all independent preparation tasks in parallel.
// copy-monaco / generate-version / mobile-web / flashgrep / plugin-host have no
// copy-monaco / generate-version / mobile-web / plugin-host have no
// dependencies on each other; each task's output is line-prefixed so the
// interleaved logs stay attributable. The DeepSeek bridge is not prepared
// here: it is not a compile-time Tauri resource. Official desktop:build
Expand All @@ -670,7 +613,7 @@ async function main() {
currentStep++,
totalSteps,
desktopMode
? 'Prepare resources (parallel: monaco, version, mobile-web, flashgrep, plugin-host)'
? 'Prepare resources (parallel: monaco, version, mobile-web, plugin-host)'
: 'Prepare resources (parallel: monaco, version)'
);

Expand Down Expand Up @@ -700,27 +643,6 @@ async function main() {
name: 'Build mobile-web',
promise: runCommandPrefixed('mobile-web', 'node', ['scripts/mobile-web-build.cjs', '--install']),
});
prepTasks.push({
name: 'Prepare workspace search daemon',
promise: (async () => {
const flashgrepResult = ensureFlashgrepBinary();
if (!flashgrepResult.ok) {
return {
ok: false,
code: null,
error: flashgrepResult.error || new Error('Workspace search daemon is missing'),
};
}
process.env.FLASHGREP_DAEMON_BIN = flashgrepResult.binaryPath;

try {
await ensureFlashgrepBundleResource();
} catch (error) {
return { ok: false, code: null, error };
}
return { ok: true, code: 0, error: null };
})(),
});
}

const prepResults = await Promise.all(prepTasks.map((task) => task.promise));
Expand Down
2 changes: 1 addition & 1 deletion scripts/prepare-dsh-profile.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ test('desktop:dev does not compile the DeepSeek profile', () => {
assert.doesNotMatch(devScript, /\['run', 'prepare:dsh-profile'\]/);
assert.match(
devScript,
/Prepare resources \(parallel: monaco, version, mobile-web, flashgrep\)/,
/Prepare resources \(parallel: monaco, version, mobile-web, plugin-host\)/,
);
});

Expand Down
2 changes: 1 addition & 1 deletion src/apps/desktop/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ required. The default dev profile keeps line tables while reducing PDB size.

All commands that pass `--no-bundle` emit a staged runtime tree rather than a
single-file application. The executable depends on the adjacent `frontend`,
`flashgrep`, `mobile-web`, and `resources` directories. Use
`mobile-web`, and `resources` directories. Use
`pnpm run desktop:build:nsis` for a distributable Windows installer.

## DevTools feature (model rule)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
} from '@/shared/types';
import { SSHContext } from '@/features/ssh-remote/SSHRemoteContext';
import { useWorkspaceSearchIndex } from '@/tools/file-explorer';
import { WORKSPACE_SEARCH_AVAILABLE } from '@/infrastructure/config/workspaceSearchAvailability';
import { computeFixedPopoverPosition } from '@/shared/utils/fixedPopoverViewport';
import { scheduleAfterStartupSignal } from '@/shared/utils/startupTaskScheduling';
import {
Expand Down Expand Up @@ -164,7 +165,8 @@ const WorkspaceItem: React.FC<WorkspaceItemProps> = ({
const relatedPathCount = workspace.relatedPaths?.length ?? 0;
const workspaceIsRemote = isRemoteWorkspace(workspace);
const canShowSearchIndex =
isActive
WORKSPACE_SEARCH_AVAILABLE
&& isActive
&& workspaceSearchEnabled
&& (
workspace.workspaceKind === WorkspaceKind.Normal
Expand Down Expand Up @@ -201,6 +203,7 @@ const WorkspaceItem: React.FC<WorkspaceItemProps> = ({
}, [isActive, workspace.rootPath, workspaceIsRemote]);

useEffect(() => {
if (!WORKSPACE_SEARCH_AVAILABLE) return;
let cancelled = false;
let unsubscribeSettings: (() => void) | null = null;
const cancelStartupSchedule = scheduleAfterStartupSignal(async () => {
Expand Down
38 changes: 37 additions & 1 deletion src/web-ui/src/app/global-search/interactiveCapabilityCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { SceneTabId } from '@/app/components/SceneBar/types';
import type { SettingsDestination } from '@/app/scenes/settings/settingsTypes';
import type { ProductActionId } from './productActionCatalog';
import generatedCatalog from './generated/interactive-capabilities.json';
import { WORKSPACE_SEARCH_AVAILABLE } from '@/infrastructure/config/workspaceSearchAvailability';

export type InteractiveCapabilityKind = 'feature' | 'setting';
export type InteractiveCapabilityRisk = 'read' | 'write' | 'ui' | 'execute' | 'destructive';
Expand Down Expand Up @@ -140,7 +141,42 @@ export interface InteractiveCapabilityCatalog {
definitions: InteractiveCapabilityControlDefinition[];
}

export const INTERACTIVE_CAPABILITY_CATALOG = generatedCatalog as InteractiveCapabilityCatalog;
// Keep the shared host contract intact while hiding suspended frontend controls.
const catalog = generatedCatalog as InteractiveCapabilityCatalog;
const suspendedSearchItems = new Set(['accelerated-search', 'search-index']);
export const INTERACTIVE_CAPABILITY_CATALOG: InteractiveCapabilityCatalog = WORKSPACE_SEARCH_AVAILABLE
? catalog
: {
...catalog,
capabilities: catalog.capabilities.map((capability) => {
if (capability.id !== 'setting.workspace.session') return capability;
const items = capability.items.filter(({ id }) => !suspendedSearchItems.has(id));
const options = capability.options.filter(({ id }) => id !== 'workspace-search');
const titlesZh = items.map(({ titleZh }) => titleZh);
const titlesEn = items.map(({ titleEn }) => titleEn);
return {
...capability,
items,
options,
summaryZh: titlesZh[0],
summaryEn: titlesEn[0],
keywordsZh: [capability.titleZh, ...titlesZh],
keywordsEn: [capability.titleEn, ...titlesEn],
highlightsZh: titlesZh,
highlightsEn: titlesEn,
stepsZh: capability.stepsZh.slice(0, 2),
stepsEn: capability.stepsEn.slice(0, 2),
agentExamplesZh: capability.agentExamplesZh.slice(0, 1),
agentExamplesEn: capability.agentExamplesEn.slice(0, 1),
searchTerms: [capability.titleZh, capability.titleEn, ...titlesZh, ...titlesEn],
};
}),
definitions: catalog.definitions.flatMap((definition) => {
if (definition.capabilityId !== 'setting.workspace.session') return [definition];
const itemIds = definition.itemIds.filter((id) => !suspendedSearchItems.has(id));
return itemIds.length ? [{ ...definition, itemIds }] : [];
}),
};

const capabilityById = new Map(
INTERACTIVE_CAPABILITY_CATALOG.capabilities.map((capability) => [capability.id, capability]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@ function request(query: string): GlobalSearchRequest {
}

describe('interactiveCapabilitySearchProvider', () => {
it('hides suspended Flashgrep controls while keeping session-title controls', async () => {
const capability = INTERACTIVE_CAPABILITY_CATALOG.capabilities.find(
({ id }) => id === 'setting.workspace.session',
);
expect(capability?.options.map(({ id }) => id)).toEqual(['session-title-generation']);
expect(capability?.items.some(({ id }) => id === 'accelerated-search' || id === 'search-index'))
.toBe(false);
for (const query of ['Flashgrep', 'accelerated workspace search']) {
const result = await interactiveCapabilitySearchProvider.search(
request(query), new AbortController().signal,
);
if (query === 'Flashgrep') expect(result.items).toEqual([]);
expect(result.items.some(({ target }) => target.kind === 'capability'
&& (target.itemId === 'accelerated-search' || target.itemId === 'search-index')))
.toBe(false);
}
});
it('uses the curated feature-and-settings contract', () => {
expect(INTERACTIVE_CAPABILITY_CATALOG.capabilities).toHaveLength(
INTERACTIVE_CAPABILITY_CATALOG.counts.userFacing,
Expand Down
Loading
Loading