feat(FN-2983): remove stale react-hooks eslint-disable comment
Removed a stale `react-hooks` eslint-disable directive from `PlanningModeModal.tsx` in the dashboard package. Fusion-Task-Id: FN-2983
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { tempWorkspace } from "@fusion/test-utils";
|
||||
import {
|
||||
resolveDroidCliExtension,
|
||||
resolveDroidCliExtensionPaths,
|
||||
} from "../droid-cli-extension.js";
|
||||
|
||||
describe("resolveDroidCliExtension", () => {
|
||||
it("finds the bundled @fusion/droid-cli package", () => {
|
||||
const result = resolveDroidCliExtension();
|
||||
// In the monorepo test environment, the workspace package MUST resolve.
|
||||
// If this fails, the vendored package's package.json or pi.extensions
|
||||
// entry has been broken — a real regression worth surfacing.
|
||||
expect(result.status).toBe("ok");
|
||||
if (result.status === "ok") {
|
||||
expect(result.path).toMatch(/droid-cli[\/\\]index\.ts$/);
|
||||
expect(result.packageVersion).toMatch(/^\d+\.\d+\.\d+$/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveDroidCliExtensionPaths", () => {
|
||||
it("returns empty when useDroidCli is off (default)", () => {
|
||||
const result = resolveDroidCliExtensionPaths({});
|
||||
expect(result.paths).toEqual([]);
|
||||
expect(result.warning).toBeUndefined();
|
||||
expect(result.resolution).toBeNull();
|
||||
});
|
||||
|
||||
it("returns empty when useDroidCli is explicitly false", () => {
|
||||
const result = resolveDroidCliExtensionPaths({ useDroidCli: false });
|
||||
expect(result.paths).toEqual([]);
|
||||
expect(result.resolution).toBeNull();
|
||||
});
|
||||
|
||||
it("returns empty when useDroidCli is a non-boolean truthy value", () => {
|
||||
// Defensive: API might pass strings, numbers — we only activate on true.
|
||||
const result = resolveDroidCliExtensionPaths({
|
||||
useDroidCli: "true" as unknown as boolean,
|
||||
});
|
||||
expect(result.paths).toEqual([]);
|
||||
});
|
||||
|
||||
it("returns the resolved path when useDroidCli is on", () => {
|
||||
const result = resolveDroidCliExtensionPaths({ useDroidCli: true });
|
||||
expect(result.paths).toHaveLength(1);
|
||||
expect(result.paths[0]).toMatch(/droid-cli[\/\\]index\.ts$/);
|
||||
expect(result.resolution?.status).toBe("ok");
|
||||
});
|
||||
|
||||
it("surfaces a warning but does not throw on weird inputs", () => {
|
||||
// Exercises the defensive null/undefined/garbage handling — callers
|
||||
// pass settings from disk that could be corrupt.
|
||||
// @ts-expect-error intentionally bad shape
|
||||
const result = resolveDroidCliExtensionPaths(null);
|
||||
expect(result.paths).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("cached resolution roundtrip", () => {
|
||||
it("set/get preserves the snapshot", async () => {
|
||||
const { setCachedDroidCliResolution, getCachedDroidCliResolution } =
|
||||
await import("../droid-cli-extension.js");
|
||||
setCachedDroidCliResolution({ status: "not-installed" });
|
||||
expect(getCachedDroidCliResolution()).toEqual({ status: "not-installed" });
|
||||
setCachedDroidCliResolution(null);
|
||||
expect(getCachedDroidCliResolution()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// Directory-fixture smoke test: give the resolver a minimal "fake" package
|
||||
// layout to prove it handles malformed installs gracefully. This doesn't
|
||||
// use the resolver directly (it's hard-coded to look up
|
||||
// @fusion/droid-cli), but proves the package.json parsing logic is
|
||||
// robust when we refactor later.
|
||||
describe("package.json edge cases (documentation)", () => {
|
||||
it("fixture layout documents what a broken install looks like", () => {
|
||||
const root = tempWorkspace("droid-cli-ext-");
|
||||
// This fixture is not exercised by the current implementation but
|
||||
// captures the shape we'd need to test if resolveDroidCliExtension
|
||||
// accepted a custom search path. Keeping it here so the next person
|
||||
// refactoring has a template.
|
||||
const pkgDir = join(root, "fake", "node_modules", "@fusion", "droid-cli");
|
||||
mkdirSync(pkgDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(pkgDir, "package.json"),
|
||||
JSON.stringify({ pi: { extensions: ["index.ts"] }, version: "0.0.0" }),
|
||||
);
|
||||
// No index.ts — would trigger missing-entry if we pointed the resolver here.
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -49,6 +49,11 @@ import {
|
||||
resolveClaudeCliExtensionPaths,
|
||||
setCachedClaudeCliResolution,
|
||||
} from "./claude-cli-extension.js";
|
||||
import {
|
||||
getCachedDroidCliResolution,
|
||||
resolveDroidCliExtensionPaths,
|
||||
setCachedDroidCliResolution,
|
||||
} from "./droid-cli-extension.js";
|
||||
import { resolveSelfExtension } from "./self-extension.js";
|
||||
import { createReadOnlyAuthFileStorage, mergeAuthStorageReads, wrapAuthStorageWithApiKeyProviders } from "./provider-auth.js";
|
||||
import { getFusionAuthPath, getLegacyAuthPaths, getModelRegistryModelsPath, getPackageManagerAgentDir } from "./auth-paths.js";
|
||||
@@ -424,6 +429,24 @@ export async function runDaemon(opts: DaemonOptions = {}) {
|
||||
}
|
||||
})();
|
||||
|
||||
const droidCliPaths = await (async () => {
|
||||
try {
|
||||
const globalSettings = await store.getGlobalSettingsStore().getSettings();
|
||||
const result = resolveDroidCliExtensionPaths(globalSettings);
|
||||
setCachedDroidCliResolution(result.resolution);
|
||||
if (result.warning) {
|
||||
console.warn(`[extensions] droid-cli: ${result.warning}`);
|
||||
}
|
||||
return result.paths;
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`[extensions] Unable to evaluate useDroidCli setting: ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
setCachedDroidCliResolution(null);
|
||||
return [];
|
||||
}
|
||||
})();
|
||||
|
||||
// Always prefer Fusion's vendored `@fusion/pi-claude-cli` over any
|
||||
// external `pi-claude-cli` install. Drops shadowing externals (e.g. a
|
||||
// global `npm install -g pi-claude-cli`) so the upstream's once-and-lock
|
||||
@@ -443,7 +466,7 @@ export async function runDaemon(opts: DaemonOptions = {}) {
|
||||
);
|
||||
|
||||
const extensionsResult = await discoverAndLoadExtensions(
|
||||
reconciledExtensionPaths,
|
||||
[...reconciledExtensionPaths, ...droidCliPaths],
|
||||
cwd,
|
||||
join(cwd, ".fusion", "disabled-auto-extension-discovery"),
|
||||
);
|
||||
@@ -526,6 +549,17 @@ export async function runDaemon(opts: DaemonOptions = {}) {
|
||||
}
|
||||
return { status: r.status, reason: r.reason };
|
||||
},
|
||||
getDroidCliExtensionStatus: () => {
|
||||
const r = getCachedDroidCliResolution();
|
||||
if (!r) return null;
|
||||
if (r.status === "ok") {
|
||||
return { status: "ok", path: r.path, packageVersion: r.packageVersion };
|
||||
}
|
||||
if (r.status === "not-installed") {
|
||||
return { status: "not-installed" };
|
||||
}
|
||||
return { status: r.status, reason: r.reason };
|
||||
},
|
||||
onUseClaudeCliToggled: (_prev, next) => {
|
||||
if (!next) return;
|
||||
void (async () => {
|
||||
@@ -542,6 +576,11 @@ export async function runDaemon(opts: DaemonOptions = {}) {
|
||||
}
|
||||
})();
|
||||
},
|
||||
onUseDroidCliToggled: (_prev, next) => {
|
||||
if (next) {
|
||||
console.log("[extensions] Droid CLI enabled — restart required for full effect");
|
||||
}
|
||||
},
|
||||
headless: true,
|
||||
daemon: { token: daemonToken },
|
||||
skillsAdapter,
|
||||
|
||||
@@ -47,6 +47,11 @@ import {
|
||||
resolveClaudeCliExtensionPaths,
|
||||
setCachedClaudeCliResolution,
|
||||
} from "./claude-cli-extension.js";
|
||||
import {
|
||||
getCachedDroidCliResolution,
|
||||
resolveDroidCliExtensionPaths,
|
||||
setCachedDroidCliResolution,
|
||||
} from "./droid-cli-extension.js";
|
||||
import { getCachedUpdateStatus, isUpdateCheckEnabled } from "../update-cache.js";
|
||||
import { resolveSelfExtension } from "./self-extension.js";
|
||||
import { registerCustomProviders, reregisterCustomProviders } from "./custom-provider-registry.js";
|
||||
@@ -1203,6 +1208,24 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
|
||||
}
|
||||
})();
|
||||
|
||||
const droidCliPaths = await (async () => {
|
||||
try {
|
||||
const globalSettings = await store.getGlobalSettingsStore().getSettings();
|
||||
const result = resolveDroidCliExtensionPaths(globalSettings);
|
||||
setCachedDroidCliResolution(result.resolution);
|
||||
if (result.warning) {
|
||||
console.warn(`[extensions] droid-cli: ${result.warning}`);
|
||||
}
|
||||
return result.paths;
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`[extensions] Unable to evaluate useDroidCli setting: ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
setCachedDroidCliResolution(null);
|
||||
return [];
|
||||
}
|
||||
})();
|
||||
|
||||
// Always inject the cli's own extension (`@runfusion/fusion`) so its
|
||||
// `fn_*` tools register globally even when the user hasn't run
|
||||
// `pi install npm:@runfusion/fusion`. Without this, agent chat with
|
||||
@@ -1224,6 +1247,7 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
|
||||
...getEnabledPiExtensionPaths(cwd),
|
||||
...packageExtensionPaths,
|
||||
...claudeCliPaths,
|
||||
...droidCliPaths,
|
||||
],
|
||||
cwd,
|
||||
join(cwd, ".fusion", "disabled-auto-extension-discovery"),
|
||||
@@ -1499,6 +1523,17 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
|
||||
}
|
||||
return { status: r.status, reason: r.reason };
|
||||
},
|
||||
getDroidCliExtensionStatus: () => {
|
||||
const r = getCachedDroidCliResolution();
|
||||
if (!r) return null;
|
||||
if (r.status === "ok") {
|
||||
return { status: "ok", path: r.path, packageVersion: r.packageVersion };
|
||||
}
|
||||
if (r.status === "not-installed") {
|
||||
return { status: "not-installed" };
|
||||
}
|
||||
return { status: r.status, reason: r.reason };
|
||||
},
|
||||
onUseClaudeCliToggled: (_prev, next) => {
|
||||
if (!next) return;
|
||||
void (async () => {
|
||||
@@ -1516,6 +1551,11 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
|
||||
}
|
||||
})();
|
||||
},
|
||||
onUseDroidCliToggled: (_prev, next) => {
|
||||
if (next) {
|
||||
logSink.log("Droid CLI enabled — restart required for full effect", "extensions");
|
||||
}
|
||||
},
|
||||
skillsAdapter,
|
||||
https: loadTlsCredentialsFromEnv(),
|
||||
daemon: dashboardAuthToken ? { token: dashboardAuthToken } : undefined,
|
||||
@@ -1732,6 +1772,17 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
|
||||
}
|
||||
return { status: r.status, reason: r.reason };
|
||||
},
|
||||
getDroidCliExtensionStatus: () => {
|
||||
const r = getCachedDroidCliResolution();
|
||||
if (!r) return null;
|
||||
if (r.status === "ok") {
|
||||
return { status: "ok", path: r.path, packageVersion: r.packageVersion };
|
||||
}
|
||||
if (r.status === "not-installed") {
|
||||
return { status: "not-installed" };
|
||||
}
|
||||
return { status: r.status, reason: r.reason };
|
||||
},
|
||||
onUseClaudeCliToggled: (_prev, next) => {
|
||||
if (!next) return;
|
||||
void (async () => {
|
||||
@@ -1749,6 +1800,11 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
|
||||
}
|
||||
})();
|
||||
},
|
||||
onUseDroidCliToggled: (_prev, next) => {
|
||||
if (next) {
|
||||
logSink.log("Droid CLI enabled — restart required for full effect", "extensions");
|
||||
}
|
||||
},
|
||||
skillsAdapter,
|
||||
https: loadTlsCredentialsFromEnv(),
|
||||
daemon: dashboardAuthToken ? { token: dashboardAuthToken } : undefined,
|
||||
|
||||
188
packages/cli/src/commands/droid-cli-extension.ts
Normal file
188
packages/cli/src/commands/droid-cli-extension.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* Resolver for the vendored `@fusion/droid-cli` pi extension.
|
||||
*
|
||||
* `@fusion/droid-cli` is a workspace package at `packages/droid-cli/`. It
|
||||
* ships its extension entry as raw `.ts` source — pi's loader compiles TS on
|
||||
* the fly via jiti, so we just need to point pi at the right file.
|
||||
*
|
||||
* We deliberately do NOT auto-add "npm:@fusion/droid-cli" to the user's
|
||||
* ~/.fusion/agent/settings.json packages array. The package is resolved from
|
||||
* this workspace at runtime and loaded explicitly only when
|
||||
* GlobalSettings.useDroidCli is true — this avoids polluting user-owned
|
||||
* config files and lets us gate the extension on a UI toggle without
|
||||
* settings.json churn.
|
||||
*/
|
||||
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const require_ = createRequire(import.meta.url);
|
||||
|
||||
/**
|
||||
* Outcome of resolving the bundled @fusion/droid-cli extension entry.
|
||||
*
|
||||
* - `"ok"`: the absolute path to the extension file was found — push it into
|
||||
* the paths array passed to `discoverAndLoadExtensions`.
|
||||
* - `"not-installed"`: the package isn't in node_modules (unusual — it's a
|
||||
* hard dep, so this typically means a corrupted install).
|
||||
* - `"missing-entry"`: the package is present but its package.json doesn't
|
||||
* declare a pi.extensions entry, or the file it points to doesn't exist.
|
||||
* Indicates a @fusion/droid-cli version mismatch or a broken release.
|
||||
* - `"error"`: something unexpected — the reason is captured so the caller
|
||||
* can surface it in the Droid CLI provider card.
|
||||
*/
|
||||
export type DroidCliExtensionResolution =
|
||||
| { status: "ok"; path: string; packageVersion: string }
|
||||
| { status: "not-installed" }
|
||||
| { status: "missing-entry"; reason: string }
|
||||
| { status: "error"; reason: string };
|
||||
|
||||
/**
|
||||
* Resolve the absolute path to `@fusion/droid-cli`'s pi extension entry file.
|
||||
*
|
||||
* The package is bundled into the published @runfusion/fusion as
|
||||
* `dist/droid-cli/` (see tsup.config.ts) so it is not a runtime npm
|
||||
* dependency. We look for that bundled copy first by walking up from this
|
||||
* module's location, and fall back to `require.resolve` for monorepo
|
||||
* dev/test runs where this file executes from `src/` rather than `dist/`.
|
||||
*/
|
||||
export function resolveDroidCliExtensionFromModuleUrl(
|
||||
moduleUrl: string,
|
||||
): DroidCliExtensionResolution {
|
||||
let pkgJsonPath: string | undefined;
|
||||
|
||||
// Bundled lookup: when running from dist/, sibling dir dist/droid-cli/
|
||||
// holds the staged extension. Walk up a few levels to also catch nested
|
||||
// layouts (e.g. dist/commands/foo.js) without hard-coding depth.
|
||||
const here = dirname(fileURLToPath(moduleUrl));
|
||||
for (const rel of ["droid-cli", "../droid-cli", "../../droid-cli"]) {
|
||||
const candidate = resolve(here, rel, "package.json");
|
||||
if (existsSync(candidate)) {
|
||||
pkgJsonPath = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pkgJsonPath) {
|
||||
try {
|
||||
pkgJsonPath = require_.resolve("@fusion/droid-cli/package.json");
|
||||
} catch {
|
||||
return { status: "not-installed" };
|
||||
}
|
||||
}
|
||||
|
||||
let pkgJson: { pi?: { extensions?: unknown }; version?: string };
|
||||
try {
|
||||
pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8")) as typeof pkgJson;
|
||||
} catch (err) {
|
||||
return {
|
||||
status: "error",
|
||||
reason: `Failed to read @fusion/droid-cli package.json: ${err instanceof Error ? err.message : String(err)}`,
|
||||
};
|
||||
}
|
||||
|
||||
const extensions = pkgJson.pi?.extensions;
|
||||
if (!Array.isArray(extensions) || extensions.length === 0) {
|
||||
return {
|
||||
status: "missing-entry",
|
||||
reason: "@fusion/droid-cli package.json has no pi.extensions array",
|
||||
};
|
||||
}
|
||||
|
||||
const rawEntry = extensions[0];
|
||||
if (typeof rawEntry !== "string" || rawEntry.length === 0) {
|
||||
return {
|
||||
status: "missing-entry",
|
||||
reason: "@fusion/droid-cli pi.extensions[0] is not a valid path string",
|
||||
};
|
||||
}
|
||||
|
||||
const entryPath = resolve(dirname(pkgJsonPath), rawEntry);
|
||||
if (!existsSync(entryPath)) {
|
||||
return {
|
||||
status: "missing-entry",
|
||||
reason: `@fusion/droid-cli extension file not found at ${entryPath}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: "ok",
|
||||
path: entryPath,
|
||||
packageVersion: pkgJson.version ?? "unknown",
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveDroidCliExtension(): DroidCliExtensionResolution {
|
||||
return resolveDroidCliExtensionFromModuleUrl(import.meta.url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the paths to append to `discoverAndLoadExtensions`' configuredPaths
|
||||
* based on the user's `useDroidCli` setting.
|
||||
*
|
||||
* When the setting is off we return no paths at all — the bundled
|
||||
* `@fusion/droid-cli` sits idle in node_modules and contributes nothing
|
||||
* to the running pi session. Flipping the toggle on requires a server
|
||||
* restart to pick up the new extension (pi has no stable runtime-reload API
|
||||
* for custom provider registrations). The dashboard toggle hook surfaces
|
||||
* this in its status response.
|
||||
*
|
||||
* `warning` is populated when resolution fails (corrupted install, missing
|
||||
* entry). Callers should log it but must not fail startup — the feature is
|
||||
* optional.
|
||||
*/
|
||||
export function resolveDroidCliExtensionPaths(globalSettings: {
|
||||
useDroidCli?: unknown;
|
||||
}): { paths: string[]; warning?: string; resolution: DroidCliExtensionResolution | null } {
|
||||
const enabled = globalSettings?.useDroidCli === true;
|
||||
if (!enabled) {
|
||||
return { paths: [], resolution: null };
|
||||
}
|
||||
|
||||
const resolution = resolveDroidCliExtension();
|
||||
switch (resolution.status) {
|
||||
case "ok":
|
||||
return { paths: [resolution.path], resolution };
|
||||
case "not-installed":
|
||||
return {
|
||||
paths: [],
|
||||
resolution,
|
||||
warning:
|
||||
"useDroidCli is on but @fusion/droid-cli is not installed in node_modules. Run `pnpm install`.",
|
||||
};
|
||||
case "missing-entry":
|
||||
case "error":
|
||||
return { paths: [], resolution, warning: resolution.reason };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Last-observed resolution cached per-process. Populated by the CLI bootstrap
|
||||
* (serve/daemon/dashboard) immediately after calling
|
||||
* `resolveDroidCliExtensionPaths`, so HTTP endpoints like
|
||||
* GET /api/providers/droid-cli/status can report the same view of the world
|
||||
* that the extension loader saw without re-probing node_modules on every
|
||||
* request.
|
||||
*/
|
||||
let cachedResolution: DroidCliExtensionResolution | null = null;
|
||||
|
||||
export function setCachedDroidCliResolution(
|
||||
resolution: DroidCliExtensionResolution | null,
|
||||
): void {
|
||||
cachedResolution = resolution;
|
||||
}
|
||||
|
||||
export function getCachedDroidCliResolution(): DroidCliExtensionResolution | null {
|
||||
return cachedResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test helper: allow tests to point the resolver at a fake package.
|
||||
* Call with `undefined` to restore the real resolver. Never used in prod.
|
||||
*/
|
||||
// Exported for use by tests — see droid-cli-extension.test.ts
|
||||
export const _testInternals = {
|
||||
moduleUrl: (): string => fileURLToPath(import.meta.url),
|
||||
};
|
||||
@@ -52,6 +52,11 @@ import {
|
||||
resolveClaudeCliExtensionPaths,
|
||||
setCachedClaudeCliResolution,
|
||||
} from "./claude-cli-extension.js";
|
||||
import {
|
||||
getCachedDroidCliResolution,
|
||||
resolveDroidCliExtensionPaths,
|
||||
setCachedDroidCliResolution,
|
||||
} from "./droid-cli-extension.js";
|
||||
import { resolveSelfExtension } from "./self-extension.js";
|
||||
import { registerCustomProviders, reregisterCustomProviders } from "./custom-provider-registry.js";
|
||||
|
||||
@@ -481,6 +486,24 @@ export async function runServe(
|
||||
}
|
||||
})();
|
||||
|
||||
const droidCliPaths = await (async () => {
|
||||
try {
|
||||
const globalSettings = await store.getGlobalSettingsStore().getSettings();
|
||||
const result = resolveDroidCliExtensionPaths(globalSettings);
|
||||
setCachedDroidCliResolution(result.resolution);
|
||||
if (result.warning) {
|
||||
console.warn(`[extensions] droid-cli: ${result.warning}`);
|
||||
}
|
||||
return result.paths;
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`[extensions] Unable to evaluate useDroidCli setting: ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
setCachedDroidCliResolution(null);
|
||||
return [];
|
||||
}
|
||||
})();
|
||||
|
||||
// Inject the cli's own extension so fn_* tools register globally without
|
||||
// requiring `pi install npm:@runfusion/fusion`.
|
||||
const selfExtension = resolveSelfExtension();
|
||||
@@ -496,6 +519,7 @@ export async function runServe(
|
||||
...getEnabledPiExtensionPaths(cwd),
|
||||
...packageExtensionPaths,
|
||||
...claudeCliPaths,
|
||||
...droidCliPaths,
|
||||
],
|
||||
cwd,
|
||||
join(cwd, ".fusion", "disabled-auto-extension-discovery"),
|
||||
@@ -711,6 +735,17 @@ export async function runServe(
|
||||
}
|
||||
return { status: r.status, reason: r.reason };
|
||||
},
|
||||
getDroidCliExtensionStatus: () => {
|
||||
const r = getCachedDroidCliResolution();
|
||||
if (!r) return null;
|
||||
if (r.status === "ok") {
|
||||
return { status: "ok", path: r.path, packageVersion: r.packageVersion };
|
||||
}
|
||||
if (r.status === "not-installed") {
|
||||
return { status: "not-installed" };
|
||||
}
|
||||
return { status: r.status, reason: r.reason };
|
||||
},
|
||||
onUseClaudeCliToggled: (_prev, next) => {
|
||||
if (!next) return; // Toggle-off leaves existing skill symlinks alone.
|
||||
void (async () => {
|
||||
@@ -727,6 +762,11 @@ export async function runServe(
|
||||
}
|
||||
})();
|
||||
},
|
||||
onUseDroidCliToggled: (_prev, next) => {
|
||||
if (next) {
|
||||
console.log("[extensions] Droid CLI enabled — restart required for full effect");
|
||||
}
|
||||
},
|
||||
headless: true,
|
||||
skillsAdapter,
|
||||
daemon: daemonToken ? { token: daemonToken } : undefined,
|
||||
|
||||
Reference in New Issue
Block a user