@gendigital/sage-opencode 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/dist/extractors.d.ts +7 -0
- package/dist/extractors.d.ts.map +1 -1
- package/dist/extractors.js +89 -16
- package/dist/extractors.js.map +1 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -320
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.cjs +425 -98
- package/dist/mcp-server.cjs.map +4 -4
- package/dist/plugin-discovery.d.ts +7 -0
- package/dist/plugin-discovery.d.ts.map +1 -1
- package/dist/plugin-discovery.js +46 -11
- package/dist/plugin-discovery.js.map +1 -1
- package/dist/shared.d.ts +63 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +352 -0
- package/dist/shared.js.map +1 -0
- package/dist/tool-handler.d.ts +6 -0
- package/dist/tool-handler.d.ts.map +1 -1
- package/dist/tool-handler.js +22 -4
- package/dist/tool-handler.js.map +1 -1
- package/dist/v2-types.d.ts +95 -0
- package/dist/v2-types.d.ts.map +1 -0
- package/dist/v2-types.js +10 -0
- package/dist/v2-types.js.map +1 -0
- package/dist/v2.d.ts +24 -0
- package/dist/v2.d.ts.map +1 -0
- package/dist/v2.js +151 -0
- package/dist/v2.js.map +1 -0
- package/index.js +3 -0
- package/package.json +4 -3
- package/resources/threats/credentials.yaml +1 -1
- package/resources/threats/dummy.yaml +37 -4
- package/resources/threats/prompt-injection.yaml +13 -0
|
@@ -10,4 +10,11 @@ import type { Branding, Logger, PluginInfo } from "@gendigital/sage-core";
|
|
|
10
10
|
* 3. Loose skills (global + project) as pseudo-plugins
|
|
11
11
|
*/
|
|
12
12
|
export declare function discoverOpenCodePlugins(logger: Logger, projectDir?: string, branding?: Branding): Promise<PluginInfo[]>;
|
|
13
|
+
/**
|
|
14
|
+
* NPM package names from an `opencode.json`. OpenCode v1 lists plugins under
|
|
15
|
+
* `plugin` (`"name"` or `["name", options]`); v2 uses `plugins` (`"name"` or
|
|
16
|
+
* `{ package: "name", options }`). Both keys are read so either host's config
|
|
17
|
+
* is covered. Malformed entries and local file specs are ignored.
|
|
18
|
+
*/
|
|
19
|
+
export declare function configPluginNames(config: Record<string, unknown>): string[];
|
|
13
20
|
//# sourceMappingURL=plugin-discovery.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-discovery.d.ts","sourceRoot":"","sources":["../src/plugin-discovery.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAa,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin-discovery.d.ts","sourceRoot":"","sources":["../src/plugin-discovery.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAa,MAAM,uBAAuB,CAAC;AA+CrF;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC5C,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,GAAE,QAA0B,GAClC,OAAO,CAAC,UAAU,EAAE,CAAC,CAgCvB;AAaD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAa3E"}
|
package/dist/plugin-discovery.js
CHANGED
|
@@ -14,6 +14,8 @@ function getCacheHome() {
|
|
|
14
14
|
return process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
|
|
15
15
|
}
|
|
16
16
|
const PROJECT_CONFIG_NAME = "opencode.json";
|
|
17
|
+
/** Local plugin directory names OpenCode loads inside its config directories. */
|
|
18
|
+
const LOCAL_PLUGIN_DIRS = ["plugins", "plugin"];
|
|
17
19
|
/**
|
|
18
20
|
* Loose-skill roots OpenCode loads, at both scopes. Global (personal) skills
|
|
19
21
|
* live under the user profile; project skills live inside the project dir.
|
|
@@ -46,15 +48,18 @@ export async function discoverOpenCodePlugins(logger, projectDir, branding = def
|
|
|
46
48
|
// 1. Discover NPM plugins from config files
|
|
47
49
|
const npmPlugins = await discoverNpmPlugins(logger, projectDir);
|
|
48
50
|
plugins.push(...npmPlugins);
|
|
49
|
-
// 2. Discover local plugin files (global)
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
51
|
+
// 2. Discover local plugin files (global). OpenCode globs both `plugins/`
|
|
52
|
+
// and `plugin/` inside its config directories.
|
|
53
|
+
for (const dirName of LOCAL_PLUGIN_DIRS) {
|
|
54
|
+
const globalPluginsDir = join(getConfigHome(), "opencode", dirName);
|
|
55
|
+
plugins.push(...(await discoverLocalPlugins(globalPluginsDir, "global", logger)));
|
|
56
|
+
}
|
|
53
57
|
// 3. Discover local plugin files (project)
|
|
54
58
|
if (projectDir) {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
for (const dirName of LOCAL_PLUGIN_DIRS) {
|
|
60
|
+
const projectPluginsDir = join(projectDir, ".opencode", dirName);
|
|
61
|
+
plugins.push(...(await discoverLocalPlugins(projectPluginsDir, "project", logger)));
|
|
62
|
+
}
|
|
58
63
|
}
|
|
59
64
|
// 4. Discover loose skills as pseudo-plugins (deduped by resolved path,
|
|
60
65
|
// fails open per root) so the scan pipeline covers skills installed outside
|
|
@@ -64,6 +69,38 @@ export async function discoverOpenCodePlugins(logger, projectDir, branding = def
|
|
|
64
69
|
logger.debug(`${branding.name} plugin discovery: found ${plugins.length} plugin(s)`);
|
|
65
70
|
return plugins;
|
|
66
71
|
}
|
|
72
|
+
/** Plugin specs that point at local files rather than npm packages. */
|
|
73
|
+
function isPathSpec(spec) {
|
|
74
|
+
return (spec.startsWith("file:") ||
|
|
75
|
+
spec.startsWith("./") ||
|
|
76
|
+
spec.startsWith("../") ||
|
|
77
|
+
spec.startsWith("/") ||
|
|
78
|
+
spec.startsWith("~"));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* NPM package names from an `opencode.json`. OpenCode v1 lists plugins under
|
|
82
|
+
* `plugin` (`"name"` or `["name", options]`); v2 uses `plugins` (`"name"` or
|
|
83
|
+
* `{ package: "name", options }`). Both keys are read so either host's config
|
|
84
|
+
* is covered. Malformed entries and local file specs are ignored.
|
|
85
|
+
*/
|
|
86
|
+
export function configPluginNames(config) {
|
|
87
|
+
const names = [];
|
|
88
|
+
for (const key of ["plugin", "plugins"]) {
|
|
89
|
+
const entries = config[key];
|
|
90
|
+
if (!Array.isArray(entries))
|
|
91
|
+
continue;
|
|
92
|
+
for (const entry of entries) {
|
|
93
|
+
let spec = entry;
|
|
94
|
+
if (Array.isArray(entry))
|
|
95
|
+
spec = entry[0];
|
|
96
|
+
else if (entry && typeof entry === "object")
|
|
97
|
+
spec = entry.package;
|
|
98
|
+
if (typeof spec === "string" && spec && !isPathSpec(spec))
|
|
99
|
+
names.push(spec);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return names;
|
|
103
|
+
}
|
|
67
104
|
/**
|
|
68
105
|
* Discover NPM plugins listed in config files
|
|
69
106
|
*/
|
|
@@ -74,8 +111,7 @@ async function discoverNpmPlugins(logger, projectDir) {
|
|
|
74
111
|
try {
|
|
75
112
|
const globalConfigPath = join(getConfigHome(), "opencode", "opencode.json");
|
|
76
113
|
const globalConfig = JSON.parse(await getFileContent(globalConfigPath));
|
|
77
|
-
const
|
|
78
|
-
for (const name of globalPlugins) {
|
|
114
|
+
for (const name of configPluginNames(globalConfig)) {
|
|
79
115
|
pluginNames.add(name);
|
|
80
116
|
}
|
|
81
117
|
}
|
|
@@ -87,8 +123,7 @@ async function discoverNpmPlugins(logger, projectDir) {
|
|
|
87
123
|
try {
|
|
88
124
|
const projectConfigPath = join(projectDir, PROJECT_CONFIG_NAME);
|
|
89
125
|
const projectConfig = JSON.parse(await getFileContent(projectConfigPath));
|
|
90
|
-
const
|
|
91
|
-
for (const name of projectPlugins) {
|
|
126
|
+
for (const name of configPluginNames(projectConfig)) {
|
|
92
127
|
pluginNames.add(name);
|
|
93
128
|
}
|
|
94
129
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-discovery.js","sourceRoot":"","sources":["../src/plugin-discovery.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EACN,eAAe,EACf,8BAA8B,EAC9B,cAAc,EACd,UAAU,GACV,MAAM,uBAAuB,CAAC;AAE/B,sEAAsE;AACtE,SAAS,aAAa;IACrB,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,YAAY;IACpB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAE5C;;;;;;;;GAQG;AACH,SAAS,iBAAiB,CAAC,UAAmB;IAC7C,MAAM,KAAK,GAAgB;QAC1B,EAAE,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE;QACxF,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE;QAClF,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE;KAClF,CAAC;IACF,IAAI,UAAU,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CACT,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,EACnF,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,EAC/E,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,CAC/E,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC5C,MAAc,EACd,UAAmB,EACnB,WAAqB,eAAe;IAEpC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,IAAI,8CAA8C,CAAC,CAAC;IAE7E,MAAM,OAAO,GAAiB,EAAE,CAAC;IAEjC,4CAA4C;IAC5C,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAChE,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;IAE5B,
|
|
1
|
+
{"version":3,"file":"plugin-discovery.js","sourceRoot":"","sources":["../src/plugin-discovery.ts"],"names":[],"mappings":"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"}
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-agnostic Sage controller shared by the OpenCode v1 (`server`) and v2
|
|
3
|
+
* (`setup`) entry points: config/branding, the scan orchestration and skill
|
|
4
|
+
* verdict watcher, the tool interception pipeline, the approval lifecycle and
|
|
5
|
+
* the Sage MCP server definition. Adapters only translate host hook shapes.
|
|
6
|
+
*/
|
|
7
|
+
import { type Branding, type Logger, type PendingEntry } from "@gendigital/sage-core";
|
|
8
|
+
import { createToolHandlers } from "./tool-handler.js";
|
|
9
|
+
/** Stable plugin ID reported to both OpenCode v1 and v2 loaders. */
|
|
10
|
+
export declare const SAGE_PLUGIN_ID = "@gendigital/sage-opencode";
|
|
11
|
+
/** Key under which the Sage MCP server is registered in OpenCode's `mcp` config. */
|
|
12
|
+
export declare const SAGE_MCP_SERVER_NAME = "sage";
|
|
13
|
+
export type ToastVariant = "info" | "success" | "warning" | "error";
|
|
14
|
+
export type ToastFn = (msg: string, variant: ToastVariant, durationMs?: number) => void;
|
|
15
|
+
/** Sage MCP server launch definition (host-neutral; each adapter adds its own flags). */
|
|
16
|
+
export declare function sageMcpServerConfig(): {
|
|
17
|
+
type: "local";
|
|
18
|
+
command: string[];
|
|
19
|
+
environment: Record<string, string>;
|
|
20
|
+
};
|
|
21
|
+
export interface SageHostOptions {
|
|
22
|
+
/** Project directory used for the plugin/skill scan. */
|
|
23
|
+
directory: string;
|
|
24
|
+
/**
|
|
25
|
+
* Builds the native toast notifier once branding is resolved. Omitted on
|
|
26
|
+
* hosts without a notification API (OpenCode v2 server plugins);
|
|
27
|
+
* notifications then go to the operational log instead.
|
|
28
|
+
*/
|
|
29
|
+
createNotifier?: (branding: Branding) => ToastFn;
|
|
30
|
+
/** Host label for log messages (e.g. "v1", "v2"). */
|
|
31
|
+
host: string;
|
|
32
|
+
}
|
|
33
|
+
export interface SageController {
|
|
34
|
+
branding: Branding;
|
|
35
|
+
logger: Logger;
|
|
36
|
+
/** Canonical tool interception; throws `SageVerdictBlockError` to block. */
|
|
37
|
+
beforeToolUse: ReturnType<typeof createToolHandlers>["tool.execute.before"];
|
|
38
|
+
/** Run the plugin/skill scan once per session ID. */
|
|
39
|
+
scanSession(sessionID: string): Promise<void>;
|
|
40
|
+
/** `sage_approve` description shared by both hosts. */
|
|
41
|
+
approveToolDescription: string;
|
|
42
|
+
/** `sage_approve` actionId argument description shared by both hosts. */
|
|
43
|
+
approveArgDescription: string;
|
|
44
|
+
/**
|
|
45
|
+
* Run the `sage_approve` lifecycle. `askUser` shows the host's native
|
|
46
|
+
* dialog and must throw (or reject) when the user declines; any failure is
|
|
47
|
+
* treated as a rejection (fail closed) and the pending entry is dropped.
|
|
48
|
+
*/
|
|
49
|
+
resolveApproval(actionId: string, askUser: (pending: PendingEntry) => Promise<void>): Promise<string>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Thrown by an adapter's `askUser` when the native approval dialog could not
|
|
53
|
+
* be shown at all (as opposed to the user declining). The action still stays
|
|
54
|
+
* blocked (fail closed), but the message is returned to the agent so the user
|
|
55
|
+
* learns why no dialog appeared and how to fix it.
|
|
56
|
+
*/
|
|
57
|
+
export declare class ApprovalUnavailableError extends Error {
|
|
58
|
+
readonly name = "ApprovalUnavailableError";
|
|
59
|
+
}
|
|
60
|
+
/** Human-readable artifact labels for approval dialogs. */
|
|
61
|
+
export declare function approvalPatterns(pending: PendingEntry): string[];
|
|
62
|
+
export declare function createSageController(options: SageHostOptions): Promise<SageController>;
|
|
63
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAIN,KAAK,QAAQ,EAQb,KAAK,MAAM,EAGX,KAAK,YAAY,EAKjB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,oEAAoE;AACpE,eAAO,MAAM,cAAc,8BAA8B,CAAC;AAE1D,oFAAoF;AACpF,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAI3C,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AACpE,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAgLxF,yFAAyF;AACzF,wBAAgB,mBAAmB,IAAI;IACtC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,CAOA;AAED,MAAM,WAAW,eAAe;IAC/B,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC;IACjD,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,aAAa,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,qBAAqB,CAAC,CAAC;IAC5E,qDAAqD;IACrD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,uDAAuD;IACvD,sBAAsB,EAAE,MAAM,CAAC;IAC/B,yEAAyE;IACzE,qBAAqB,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,eAAe,CACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/C,OAAO,CAAC,MAAM,CAAC,CAAC;CACnB;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IAClD,SAAkB,IAAI,8BAA8B;CACpD;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,EAAE,CAEhE;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAsL5F"}
|
package/dist/shared.js
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-agnostic Sage controller shared by the OpenCode v1 (`server`) and v2
|
|
3
|
+
* (`setup`) entry points: config/branding, the scan orchestration and skill
|
|
4
|
+
* verdict watcher, the tool interception pipeline, the approval lifecycle and
|
|
5
|
+
* the Sage MCP server definition. Adapters only translate host hook shapes.
|
|
6
|
+
*/
|
|
7
|
+
import { mkdirSync, watchFile } from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { ApprovalStore, approveAction, checkAllowlistMigration, createOperationalLogger, foreignSourceRuntime, formatAllowlistMigrationWarning, formatConfigurationWarnings, formatNoticeById, getConfigurationWarnings, loadConfig, loadSkillVerdictCache, resolveBranding, resolvePath, } from "@gendigital/sage-core";
|
|
11
|
+
import { getBundledDataDirs } from "./bundled-dirs.js";
|
|
12
|
+
import { createSessionScanHandler } from "./startup-scan.js";
|
|
13
|
+
import { createToolHandlers } from "./tool-handler.js";
|
|
14
|
+
/** Stable plugin ID reported to both OpenCode v1 and v2 loaders. */
|
|
15
|
+
export const SAGE_PLUGIN_ID = "@gendigital/sage-opencode";
|
|
16
|
+
/** Key under which the Sage MCP server is registered in OpenCode's `mcp` config. */
|
|
17
|
+
export const SAGE_MCP_SERVER_NAME = "sage";
|
|
18
|
+
const APPROVAL_STORE_CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
19
|
+
const DEFAULT_TOAST_DURATION_MS = 5000;
|
|
20
|
+
// The startup security summary lists every risky skill, so it's shown longer to
|
|
21
|
+
// give the user time to read the list before it dismisses.
|
|
22
|
+
const STARTUP_SUMMARY_DURATION_MS = 10_000;
|
|
23
|
+
// Module-scoped so repeated plugin instantiations (OpenCode may load the
|
|
24
|
+
// plugin more than once) share a single watcher and one notified-set, instead
|
|
25
|
+
// of each instance attaching its own watcher and toasting the same verdict.
|
|
26
|
+
//
|
|
27
|
+
// This state (watcher, notified-set, session scans, toast queue/notifier) is
|
|
28
|
+
// shared by the v1 and v2 adapters. That assumes one OpenCode host per process:
|
|
29
|
+
// a host loads either `server` (v1) or `setup` (v2), never both. If both ever
|
|
30
|
+
// ran in one process, the first instance's notifier would serve both and each
|
|
31
|
+
// session would be scanned once in total, not once per adapter.
|
|
32
|
+
const notifiedSkillVerdicts = new Set();
|
|
33
|
+
let skillWatcherAttached = false;
|
|
34
|
+
// One scan per session, shared across instances: the entry guards against
|
|
35
|
+
// re-running the scan when session events fire repeatedly or on more than one
|
|
36
|
+
// plugin instance.
|
|
37
|
+
const sessionScanPromises = new Map();
|
|
38
|
+
const toastQueue = [];
|
|
39
|
+
let toastPumpRunning = false;
|
|
40
|
+
// Bound to the first instance's notifier (see createSageController).
|
|
41
|
+
let displayToast;
|
|
42
|
+
async function pumpToasts() {
|
|
43
|
+
if (toastPumpRunning)
|
|
44
|
+
return;
|
|
45
|
+
toastPumpRunning = true;
|
|
46
|
+
try {
|
|
47
|
+
while (toastQueue.length > 0) {
|
|
48
|
+
const next = toastQueue[0];
|
|
49
|
+
// No notifier bound yet — leave the item queued; a later enqueue resumes.
|
|
50
|
+
if (!displayToast || !next)
|
|
51
|
+
break;
|
|
52
|
+
displayToast(next.message, next.variant, next.durationMs);
|
|
53
|
+
toastQueue.shift();
|
|
54
|
+
// Hold the slot for this toast's full duration before showing the next,
|
|
55
|
+
// so a following toast can't replace it early (showToast overwrites).
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, next.durationMs));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
toastPumpRunning = false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function enqueueToast(message, variant, durationMs = DEFAULT_TOAST_DURATION_MS) {
|
|
64
|
+
toastQueue.push({ message, variant, durationMs });
|
|
65
|
+
void pumpToasts();
|
|
66
|
+
}
|
|
67
|
+
// Shared dedup key across the two surfacing paths: the scan-findings popup keys
|
|
68
|
+
// on `finding.artifact` (which is `skillId.slice(0, 16)`), and the cache watcher
|
|
69
|
+
// keys on the full skillId — so both reduce to this 16-char content-hash prefix
|
|
70
|
+
// and `notifiedSkillVerdicts` never double-announces the same skill version.
|
|
71
|
+
function shortSkillId(skillId) {
|
|
72
|
+
return skillId.slice(0, 16);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Read `~/.sage/skill_verdict_cache.json` and return every HIGH/CRITICAL verdict
|
|
76
|
+
* relevant to this runtime — one per cache entry, NOT deduped, since dedup is
|
|
77
|
+
* caller-specific (the popup keys on current content, the toast queue on
|
|
78
|
+
* skillId). HIGH maps to the "WARNING" label. Verdicts sourced only from other
|
|
79
|
+
* agent runtimes are skipped so one host doesn't echo another's finds.
|
|
80
|
+
*/
|
|
81
|
+
async function collectRiskySkills(agentRuntime) {
|
|
82
|
+
const cache = await loadSkillVerdictCache();
|
|
83
|
+
const out = [];
|
|
84
|
+
for (const [skillId, verdict] of Object.entries(cache.entries)) {
|
|
85
|
+
const risk = (verdict.verdict ?? "").toUpperCase();
|
|
86
|
+
if (risk !== "HIGH" && risk !== "CRITICAL")
|
|
87
|
+
continue;
|
|
88
|
+
if (foreignSourceRuntime(verdict.sources, agentRuntime))
|
|
89
|
+
continue;
|
|
90
|
+
const key = verdict.sources?.find((s) => s.containerKey)?.containerKey ||
|
|
91
|
+
verdict.skillName ||
|
|
92
|
+
skillId.slice(0, 16);
|
|
93
|
+
out.push({
|
|
94
|
+
skillId,
|
|
95
|
+
key,
|
|
96
|
+
severity: risk === "CRITICAL" ? "CRITICAL" : "WARNING",
|
|
97
|
+
summary: verdict.summary?.trim(),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Watch `~/.sage/skill_verdict_cache.json` and queue one toast per verdict the
|
|
104
|
+
* upload worker writes mid-session (it writes asynchronously, ~one session after
|
|
105
|
+
* discovery). The serial queue shows them one at a time so two skills finishing
|
|
106
|
+
* together are both seen.
|
|
107
|
+
*
|
|
108
|
+
* Verdicts cached by earlier sessions are *seeded silently* on attach — marked
|
|
109
|
+
* notified without a toast — because the plugin initialises before the TUI is
|
|
110
|
+
* ready to render toasts. Those are surfaced instead by the session-start
|
|
111
|
+
* summary popup (see `surfaceStartupSummary`), which runs after the scan when
|
|
112
|
+
* the TUI is up.
|
|
113
|
+
*
|
|
114
|
+
* Dedup keys on `skillId` (content hash), not the container key, so editing a
|
|
115
|
+
* skill — which yields a new hash under the same key — is treated as a new
|
|
116
|
+
* finding and re-announced rather than silently collapsed into the old one.
|
|
117
|
+
*/
|
|
118
|
+
function setupSkillFindingsWatcher(logger, agentRuntime) {
|
|
119
|
+
if (skillWatcherAttached)
|
|
120
|
+
return;
|
|
121
|
+
const sageDir = resolvePath("~/.sage");
|
|
122
|
+
const seedNotified = async () => {
|
|
123
|
+
try {
|
|
124
|
+
for (const s of await collectRiskySkills(agentRuntime)) {
|
|
125
|
+
notifiedSkillVerdicts.add(shortSkillId(s.skillId));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
logger.debug("Skill verdict seed failed", { error: String(e) });
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
// Mid-session: queue one toast per newly written (not-yet-notified) verdict.
|
|
133
|
+
const announceNew = async () => {
|
|
134
|
+
try {
|
|
135
|
+
for (const s of await collectRiskySkills(agentRuntime)) {
|
|
136
|
+
const short = shortSkillId(s.skillId);
|
|
137
|
+
if (notifiedSkillVerdicts.has(short))
|
|
138
|
+
continue;
|
|
139
|
+
notifiedSkillVerdicts.add(short);
|
|
140
|
+
const detail = s.summary ? `${s.key} — ${s.summary}` : `${s.key} detected`;
|
|
141
|
+
logger.debug("Skill finding toast queued", { key: s.key, severity: s.severity });
|
|
142
|
+
enqueueToast(`${detail} (${s.severity})`, s.severity === "CRITICAL" ? "error" : "warning");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
logger.debug("Skill verdict watcher scan failed", { error: String(e) });
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
try {
|
|
150
|
+
mkdirSync(sageDir, { recursive: true });
|
|
151
|
+
void seedNotified();
|
|
152
|
+
// Poll the cache file with watchFile rather than watch() the directory.
|
|
153
|
+
// On Windows, fs.watch aborts the whole process (an uncatchable libuv
|
|
154
|
+
// assertion in fs-event.c) when the watched path is an 8.3 short name
|
|
155
|
+
// (e.g. an os.tmpdir() like C:\Users\FOO~1.BAR\...) or symlinked home.
|
|
156
|
+
// watchFile is stat-based polling — immune to that. A few seconds of
|
|
157
|
+
// latency for mid-session verdicts is fine (matches the statusline poll
|
|
158
|
+
// cadence used elsewhere). Handles a not-yet-existent file (polls until
|
|
159
|
+
// it appears).
|
|
160
|
+
const cacheFile = join(sageDir, "skill_verdict_cache.json");
|
|
161
|
+
const watcher = watchFile(cacheFile, { interval: 3000 }, (curr, prev) => {
|
|
162
|
+
if (curr.mtimeMs !== prev.mtimeMs)
|
|
163
|
+
void announceNew();
|
|
164
|
+
});
|
|
165
|
+
watcher.unref?.();
|
|
166
|
+
skillWatcherAttached = true;
|
|
167
|
+
logger.debug("Skill findings watcher attached", { cacheFile });
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
// ~/.sage/ may be unwritable — no watcher, no notifications (fail-open).
|
|
171
|
+
logger.debug("Skill findings watcher setup failed", { error: String(e) });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/** Sage MCP server launch definition (host-neutral; each adapter adds its own flags). */
|
|
175
|
+
export function sageMcpServerConfig() {
|
|
176
|
+
const serverPath = join(dirname(fileURLToPath(import.meta.url)), "mcp-server.cjs");
|
|
177
|
+
return {
|
|
178
|
+
type: "local",
|
|
179
|
+
command: [process.execPath, serverPath],
|
|
180
|
+
environment: { BUN_BE_BUN: "1", SAGE_AGENT_RUNTIME: "opencode" },
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Thrown by an adapter's `askUser` when the native approval dialog could not
|
|
185
|
+
* be shown at all (as opposed to the user declining). The action still stays
|
|
186
|
+
* blocked (fail closed), but the message is returned to the agent so the user
|
|
187
|
+
* learns why no dialog appeared and how to fix it.
|
|
188
|
+
*/
|
|
189
|
+
export class ApprovalUnavailableError extends Error {
|
|
190
|
+
name = "ApprovalUnavailableError";
|
|
191
|
+
}
|
|
192
|
+
/** Human-readable artifact labels for approval dialogs. */
|
|
193
|
+
export function approvalPatterns(pending) {
|
|
194
|
+
return pending.artifacts.map((a) => `[${a.type}] ${a.value}`);
|
|
195
|
+
}
|
|
196
|
+
export async function createSageController(options) {
|
|
197
|
+
const config = await loadConfig();
|
|
198
|
+
const operationalLogger = createOperationalLogger(config.operational_logging, "opencode");
|
|
199
|
+
const logger = operationalLogger.forComponent("plugin");
|
|
200
|
+
const toolLogger = operationalLogger.forComponent("tool-handler");
|
|
201
|
+
const scanLogger = operationalLogger.forComponent("startup-scan");
|
|
202
|
+
const branding = resolveBranding(config.brand_key, logger);
|
|
203
|
+
// Hosts without a toast API still surface notifications — in the log.
|
|
204
|
+
const notify = options.createNotifier?.(branding) ??
|
|
205
|
+
((msg, variant) => {
|
|
206
|
+
const data = { host: options.host, variant };
|
|
207
|
+
if (variant === "error" || variant === "warning") {
|
|
208
|
+
logger.warn(`${branding.name}: ${msg}`, data);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
logger.info(`${branding.name}: ${msg}`, data);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
// Bind the module-scoped skill-toast queue to this (first) instance's notifier.
|
|
215
|
+
displayToast ??= notify;
|
|
216
|
+
const warningMessage = formatConfigurationWarnings(await getConfigurationWarnings(undefined, logger), branding);
|
|
217
|
+
if (warningMessage)
|
|
218
|
+
notify(warningMessage, "warning", DEFAULT_TOAST_DURATION_MS);
|
|
219
|
+
const allowlistMigration = await checkAllowlistMigration();
|
|
220
|
+
if (allowlistMigration.needed) {
|
|
221
|
+
notify(formatAllowlistMigrationWarning(allowlistMigration.entryTypes, branding), "warning", 8000);
|
|
222
|
+
}
|
|
223
|
+
const { threatsDir, trustedDomainsDir } = getBundledDataDirs();
|
|
224
|
+
const approvalStore = new ApprovalStore();
|
|
225
|
+
// Watch for verdicts the upload worker writes mid-session (queued toasts).
|
|
226
|
+
// Verdicts cached by earlier sessions are surfaced by surfaceStartupSummary.
|
|
227
|
+
setupSkillFindingsWatcher(logger, "opencode");
|
|
228
|
+
// Surface the scan's structured findings after the scan (TUI ready). Findings
|
|
229
|
+
// are install- and content-accurate: the scan hashes current on-disk content
|
|
230
|
+
// and looks up its verdict, so a deleted skill isn't discovered and an edited
|
|
231
|
+
// skill is matched by its new hash — no cache re-derivation needed.
|
|
232
|
+
//
|
|
233
|
+
// Single startup surface: one summary toast listing every risky skill from
|
|
234
|
+
// this scan. Summarized skills are marked notified so the cache watcher does
|
|
235
|
+
// not also announce them per-skill; verdicts that land *after* the scan
|
|
236
|
+
// (worker uploads) are still surfaced individually by the watcher.
|
|
237
|
+
const surfaceStartupSummary = (scanResults) => {
|
|
238
|
+
try {
|
|
239
|
+
const skills = [];
|
|
240
|
+
const seen = new Set();
|
|
241
|
+
for (const result of scanResults) {
|
|
242
|
+
for (const finding of result.findings) {
|
|
243
|
+
if (finding.threatId !== "SKILL_CHECK")
|
|
244
|
+
continue;
|
|
245
|
+
if (finding.severity !== "critical" && finding.severity !== "warning")
|
|
246
|
+
continue;
|
|
247
|
+
if (seen.has(result.plugin.key))
|
|
248
|
+
continue;
|
|
249
|
+
seen.add(result.plugin.key);
|
|
250
|
+
skills.push({
|
|
251
|
+
key: result.plugin.key,
|
|
252
|
+
severity: finding.severity === "critical" ? "CRITICAL" : "WARNING",
|
|
253
|
+
finding,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (skills.length === 0)
|
|
258
|
+
return;
|
|
259
|
+
// Mark every summarized skill as notified so the cache watcher doesn't
|
|
260
|
+
// also announce it per-skill. `finding.artifact` is the 16-char short
|
|
261
|
+
// hash — the same key the watcher dedups on. Verdicts that land after
|
|
262
|
+
// this scan (worker uploads) are still surfaced individually.
|
|
263
|
+
for (const s of skills) {
|
|
264
|
+
if (s.finding.artifact)
|
|
265
|
+
notifiedSkillVerdicts.add(s.finding.artifact);
|
|
266
|
+
}
|
|
267
|
+
// The summary: a single toast listing every risky skill, shown longer.
|
|
268
|
+
const hasCritical = skills.some((s) => s.severity === "CRITICAL");
|
|
269
|
+
const body = skills.map((s) => `${s.key} (${s.severity})`).join("\n");
|
|
270
|
+
enqueueToast(`${branding.name} detected ${skills.length} malicious/suspicious skill(s):\n${body}`, hasCritical ? "error" : "warning", STARTUP_SUMMARY_DURATION_MS);
|
|
271
|
+
}
|
|
272
|
+
catch (e) {
|
|
273
|
+
logger.debug("Skill summary popup failed", { error: String(e) });
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
// Set up the cron job that cleans up the approval store.
|
|
277
|
+
const interval = setInterval(() => {
|
|
278
|
+
approvalStore.cleanup();
|
|
279
|
+
}, APPROVAL_STORE_CLEANUP_INTERVAL_MS);
|
|
280
|
+
interval.unref?.();
|
|
281
|
+
const toolHandlers = createToolHandlers(toolLogger, approvalStore, threatsDir, trustedDomainsDir, { showToast: notify }, branding);
|
|
282
|
+
/**
|
|
283
|
+
* Run the session scan exactly once per session and cache the promise. The
|
|
284
|
+
* cached promise is the dedupe guard — a second call (repeated session
|
|
285
|
+
* events, or another plugin instance) returns the same in-flight/settled
|
|
286
|
+
* promise instead of re-scanning. The scan's structured findings drive the
|
|
287
|
+
* summary popup (see `surfaceStartupSummary`).
|
|
288
|
+
*/
|
|
289
|
+
const scanSession = (sessionID) => {
|
|
290
|
+
if (!sessionID || sessionID === "unknown")
|
|
291
|
+
return Promise.resolve();
|
|
292
|
+
const existing = sessionScanPromises.get(sessionID);
|
|
293
|
+
if (existing)
|
|
294
|
+
return existing;
|
|
295
|
+
const promise = (async () => {
|
|
296
|
+
logger.debug(`${branding.name}: starting session scan`, { sessionID, host: options.host });
|
|
297
|
+
const scanHandler = createSessionScanHandler(scanLogger, options.directory,
|
|
298
|
+
// After the scan (TUI ready): summary popup from the structured findings.
|
|
299
|
+
surfaceStartupSummary, branding,
|
|
300
|
+
// One-time notices (e.g. skill-upload consent) as info toasts.
|
|
301
|
+
(noticeIds) => {
|
|
302
|
+
for (const id of noticeIds) {
|
|
303
|
+
const text = formatNoticeById(id, branding);
|
|
304
|
+
if (text)
|
|
305
|
+
enqueueToast(text, "info", STARTUP_SUMMARY_DURATION_MS);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
await scanHandler();
|
|
309
|
+
})();
|
|
310
|
+
sessionScanPromises.set(sessionID, promise);
|
|
311
|
+
return promise;
|
|
312
|
+
};
|
|
313
|
+
const resolveApproval = async (actionId, askUser) => {
|
|
314
|
+
const pending = approvalStore.getPending(actionId);
|
|
315
|
+
if (!pending) {
|
|
316
|
+
return `No pending ${branding.name} approval found for this action ID.`;
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
await askUser(pending);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
logger.debug(`${branding.name}: approval dialog failed`, {
|
|
323
|
+
actionId,
|
|
324
|
+
host: options.host,
|
|
325
|
+
error: String(error),
|
|
326
|
+
});
|
|
327
|
+
approvalStore.deletePending(actionId);
|
|
328
|
+
if (error instanceof ApprovalUnavailableError) {
|
|
329
|
+
logger.warn(`${branding.name}: approval dialog unavailable`, {
|
|
330
|
+
actionId,
|
|
331
|
+
host: options.host,
|
|
332
|
+
error: error.message,
|
|
333
|
+
});
|
|
334
|
+
return `Rejected: the ${branding.name} approval dialog could not be shown (${error.message}). The action stays blocked.`;
|
|
335
|
+
}
|
|
336
|
+
logger.debug("OpenCode approval rejected", { actionId, host: options.host });
|
|
337
|
+
return "Rejected by user.";
|
|
338
|
+
}
|
|
339
|
+
logger.info("OpenCode approval accepted", { actionId, host: options.host });
|
|
340
|
+
return approveAction(approvalStore, actionId, branding);
|
|
341
|
+
};
|
|
342
|
+
return {
|
|
343
|
+
branding,
|
|
344
|
+
logger,
|
|
345
|
+
beforeToolUse: toolHandlers["tool.execute.before"],
|
|
346
|
+
scanSession,
|
|
347
|
+
approveToolDescription: `Review a ${branding.name}-flagged tool call. Shows a native approval dialog to the user. Call this immediately when ${branding.name} flags an action — do NOT ask the user in chat first.`,
|
|
348
|
+
approveArgDescription: `Action ID from ${branding.name} flagged message`,
|
|
349
|
+
resolveApproval,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"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"}
|
package/dist/tool-handler.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { type ApprovalStore, type Branding, type CanonicalToolType, type Logger } from "@gendigital/sage-core";
|
|
2
2
|
export declare const OPENCODE_TOOL_MAP: Record<string, CanonicalToolType>;
|
|
3
|
+
/**
|
|
4
|
+
* Map OpenCode argument names onto the Claude-style keys core reads
|
|
5
|
+
* (`file_path`, `new_string`, and `patch` for ApplyPatch AMSI scans and
|
|
6
|
+
* content snapshots).
|
|
7
|
+
*/
|
|
8
|
+
export declare function normalizeToolInput(toolName: string, args: Record<string, unknown>): Record<string, unknown>;
|
|
3
9
|
export interface ToolHandlerOptions {
|
|
4
10
|
showToast?: (msg: string, variant: "info" | "success" | "warning" | "error") => void;
|
|
5
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-handler.d.ts","sourceRoot":"","sources":["../src/tool-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,iBAAiB,EAOtB,KAAK,MAAM,EAGX,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"tool-handler.d.ts","sourceRoot":"","sources":["../src/tool-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,iBAAiB,EAOtB,KAAK,MAAM,EAGX,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAkB/D,CAAC;AASF;;;;GAIG;AACH,wBAAgB,kBAAkB,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsBzB;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,KAAK,IAAI,CAAC;CACrF;AAED,wBAAgB,kBAAkB,CACjC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,MAAM,EACzB,OAAO,CAAC,EAAE,kBAAkB,EAC5B,QAAQ,GAAE,QAA0B;mCAG5B;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,UAClD;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KACvC,OAAO,CAAC,IAAI,CAAC;EAiJhB"}
|