chivgent 0.10.0 → 0.12.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 +142 -2
- package/README.zh-CN.md +126 -2
- package/dist/cli-options.d.ts +12 -0
- package/dist/cli-options.d.ts.map +1 -1
- package/dist/cli-options.js +51 -1
- package/dist/cli-options.js.map +1 -1
- package/dist/cli.js +267 -5
- package/dist/cli.js.map +1 -1
- package/dist/extensions/api.d.ts +38 -0
- package/dist/extensions/api.d.ts.map +1 -0
- package/dist/extensions/api.js +2 -0
- package/dist/extensions/api.js.map +1 -0
- package/dist/extensions/decide-trust.d.ts +28 -0
- package/dist/extensions/decide-trust.d.ts.map +1 -0
- package/dist/extensions/decide-trust.js +96 -0
- package/dist/extensions/decide-trust.js.map +1 -0
- package/dist/extensions/discover.d.ts +19 -0
- package/dist/extensions/discover.d.ts.map +1 -0
- package/dist/extensions/discover.js +61 -0
- package/dist/extensions/discover.js.map +1 -0
- package/dist/extensions/loader.d.ts +28 -0
- package/dist/extensions/loader.d.ts.map +1 -0
- package/dist/extensions/loader.js +60 -0
- package/dist/extensions/loader.js.map +1 -0
- package/dist/extensions/registry.d.ts +36 -0
- package/dist/extensions/registry.d.ts.map +1 -0
- package/dist/extensions/registry.js +115 -0
- package/dist/extensions/registry.js.map +1 -0
- package/dist/extensions/trust.d.ts +44 -0
- package/dist/extensions/trust.d.ts.map +1 -0
- package/dist/extensions/trust.js +129 -0
- package/dist/extensions/trust.js.map +1 -0
- package/dist/remote/client.d.ts +42 -0
- package/dist/remote/client.d.ts.map +1 -0
- package/dist/remote/client.js +173 -0
- package/dist/remote/client.js.map +1 -0
- package/dist/remote/framing.d.ts +33 -0
- package/dist/remote/framing.d.ts.map +1 -0
- package/dist/remote/framing.js +68 -0
- package/dist/remote/framing.js.map +1 -0
- package/dist/remote/protocol.d.ts +81 -0
- package/dist/remote/protocol.d.ts.map +1 -0
- package/dist/remote/protocol.js +79 -0
- package/dist/remote/protocol.js.map +1 -0
- package/dist/remote/repl.d.ts +18 -0
- package/dist/remote/repl.d.ts.map +1 -0
- package/dist/remote/repl.js +100 -0
- package/dist/remote/repl.js.map +1 -0
- package/dist/remote/server.d.ts +41 -0
- package/dist/remote/server.d.ts.map +1 -0
- package/dist/remote/server.js +214 -0
- package/dist/remote/server.js.map +1 -0
- package/dist/remote/socket-path.d.ts +33 -0
- package/dist/remote/socket-path.d.ts.map +1 -0
- package/dist/remote/socket-path.js +108 -0
- package/dist/remote/socket-path.js.map +1 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +11 -1
- package/dist/render.js.map +1 -1
- package/dist/repl.d.ts +12 -1
- package/dist/repl.d.ts.map +1 -1
- package/dist/repl.js +47 -2
- package/dist/repl.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CONFIG_DIRECTORY, EXTENSIONS_DIRECTORY } from "./trust.js";
|
|
4
|
+
import { defaultSessionHome } from "../session-store.js";
|
|
5
|
+
export function userExtensionsDirectory(environment = process.env) {
|
|
6
|
+
return path.join(defaultSessionHome(environment), EXTENSIONS_DIRECTORY);
|
|
7
|
+
}
|
|
8
|
+
export function projectExtensionsDirectory(cwd) {
|
|
9
|
+
return path.join(cwd, CONFIG_DIRECTORY, EXTENSIONS_DIRECTORY);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Lists the extension entry points in one directory.
|
|
13
|
+
*
|
|
14
|
+
* Two shapes are recognised: `name.js`, and `name/index.js`. Nesting stops
|
|
15
|
+
* there. Anything more elaborate is a package, and packaging is not what this
|
|
16
|
+
* stage is about.
|
|
17
|
+
*/
|
|
18
|
+
async function discoverIn(directory, origin) {
|
|
19
|
+
let entries;
|
|
20
|
+
try {
|
|
21
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
const found = [];
|
|
27
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
28
|
+
const entryPath = path.join(directory, entry.name);
|
|
29
|
+
if (entry.isFile() && entry.name.endsWith(".js")) {
|
|
30
|
+
found.push({ path: entryPath, origin });
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (entry.isDirectory()) {
|
|
34
|
+
const index = path.join(entryPath, "index.js");
|
|
35
|
+
try {
|
|
36
|
+
if ((await stat(index)).isFile()) {
|
|
37
|
+
found.push({ path: index, origin });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// A directory without an index is not an extension.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return found;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* User extensions come first so that a project cannot take a name the user
|
|
49
|
+
* already uses: the first registration of a name wins.
|
|
50
|
+
*/
|
|
51
|
+
export async function discoverExtensions(options) {
|
|
52
|
+
const environment = options.environment ?? process.env;
|
|
53
|
+
const found = [
|
|
54
|
+
...(await discoverIn(userExtensionsDirectory(environment), "user")),
|
|
55
|
+
];
|
|
56
|
+
if (options.includeProject) {
|
|
57
|
+
found.push(...(await discoverIn(projectExtensionsDirectory(options.cwd), "project")));
|
|
58
|
+
}
|
|
59
|
+
return found;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/extensions/discover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AASzD,MAAM,UAAU,uBAAuB,CACrC,WAAW,GAAsB,OAAO,CAAC,GAAG;IAE5C,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,EAAE,oBAAoB,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,GAAW;IACpD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,EAAE,oBAAoB,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,UAAU,CACvB,SAAiB,EACjB,MAAuB;IAEvB,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAA0B,EAAE,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACzE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;YACxC,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;YAC/C,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;oBACjC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;gBACtC,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,oDAAoD;YACtD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AASD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAwB;IAExB,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC;IACvD,MAAM,KAAK,GAAG;QACZ,GAAG,CAAC,MAAM,UAAU,CAAC,uBAAuB,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC;KACpE,CAAC;IACF,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CACR,GAAG,CAAC,MAAM,UAAU,CAAC,0BAA0B,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC,CAC1E,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DiscoveredExtension } from "./discover.js";
|
|
2
|
+
import { ExtensionRegistry, type RegistryOptions } from "./registry.js";
|
|
3
|
+
export interface LoadedExtension {
|
|
4
|
+
readonly path: string;
|
|
5
|
+
readonly origin: DiscoveredExtension["origin"];
|
|
6
|
+
}
|
|
7
|
+
export interface LoadResult {
|
|
8
|
+
readonly registry: ExtensionRegistry;
|
|
9
|
+
readonly loaded: readonly LoadedExtension[];
|
|
10
|
+
readonly failed: readonly {
|
|
11
|
+
readonly path: string;
|
|
12
|
+
readonly reason: string;
|
|
13
|
+
}[];
|
|
14
|
+
}
|
|
15
|
+
export interface LoadOptions extends RegistryOptions {
|
|
16
|
+
/** Injectable so tests do not have to write modules to disk to test loading. */
|
|
17
|
+
readonly importModule?: (path: string) => Promise<unknown>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Imports each extension and lets it register.
|
|
21
|
+
*
|
|
22
|
+
* Importing a module runs its top-level code, so this is the point of no
|
|
23
|
+
* return: everything the caller wanted to gate must already have been decided.
|
|
24
|
+
* From here on the job is containment — one broken extension must leave
|
|
25
|
+
* chivgent working.
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadExtensions(discovered: readonly DiscoveredExtension[], options: LoadOptions): Promise<LoadResult>;
|
|
28
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/extensions/loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAGxE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChF;AAED,MAAM,WAAW,WAAY,SAAQ,eAAe;IAClD,gFAAgF;IAChF,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5D;AAiBD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,SAAS,mBAAmB,EAAE,EAC1C,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CAuCrB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
import { ExtensionRegistry } from "./registry.js";
|
|
3
|
+
async function defaultImport(modulePath) {
|
|
4
|
+
// A file URL is required on Windows and harmless elsewhere.
|
|
5
|
+
return import(pathToFileURL(modulePath).href);
|
|
6
|
+
}
|
|
7
|
+
function readFactory(module) {
|
|
8
|
+
if (typeof module !== "object" || module === null) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
const candidate = module.default;
|
|
12
|
+
return typeof candidate === "function"
|
|
13
|
+
? candidate
|
|
14
|
+
: undefined;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Imports each extension and lets it register.
|
|
18
|
+
*
|
|
19
|
+
* Importing a module runs its top-level code, so this is the point of no
|
|
20
|
+
* return: everything the caller wanted to gate must already have been decided.
|
|
21
|
+
* From here on the job is containment — one broken extension must leave
|
|
22
|
+
* chivgent working.
|
|
23
|
+
*/
|
|
24
|
+
export async function loadExtensions(discovered, options) {
|
|
25
|
+
const registry = new ExtensionRegistry(options);
|
|
26
|
+
const importModule = options.importModule ?? defaultImport;
|
|
27
|
+
const loaded = [];
|
|
28
|
+
const failed = [];
|
|
29
|
+
for (const extension of discovered) {
|
|
30
|
+
let factory;
|
|
31
|
+
try {
|
|
32
|
+
factory = readFactory(await importModule(extension.path));
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
36
|
+
failed.push({ path: extension.path, reason });
|
|
37
|
+
options.onWarning(`${extension.path}: failed to load (${reason}).`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (factory === undefined) {
|
|
41
|
+
const reason = "no default export function";
|
|
42
|
+
failed.push({ path: extension.path, reason });
|
|
43
|
+
options.onWarning(`${extension.path}: ${reason}; an extension must export default a function.`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
await factory(registry.apiFor(extension.path));
|
|
48
|
+
loaded.push({ path: extension.path, origin: extension.origin });
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
52
|
+
failed.push({ path: extension.path, reason });
|
|
53
|
+
// Whatever it registered before throwing stays: partial is better than
|
|
54
|
+
// discarding work that is already valid.
|
|
55
|
+
options.onWarning(`${extension.path}: threw while registering (${reason}).`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { registry, loaded, failed };
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/extensions/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,iBAAiB,EAAwB,MAAM,eAAe,CAAC;AAmBxE,KAAK,UAAU,aAAa,CAAC,UAAkB;IAC7C,4DAA4D;IAC5D,OAAO,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,WAAW,CAAC,MAAe;IAClC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAClD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,SAAS,GAAI,MAAgC,CAAC,OAAO,CAAC;IAC5D,OAAO,OAAO,SAAS,KAAK,UAAU;QACpC,CAAC,CAAE,SAA8B;QACjC,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,UAA0C,EAC1C,OAAoB;IAEpB,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,aAAa,CAAC;IAC3D,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,MAAM,GAAuC,EAAE,CAAC;IAEtD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,OAAqC,CAAC;QAC1C,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,MAAM,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAC9C,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,IAAI,qBAAqB,MAAM,IAAI,CAAC,CAAC;YACpE,SAAS;QACX,CAAC;QAED,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,4BAA4B,CAAC;YAC5C,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAC9C,OAAO,CAAC,SAAS,CACf,GAAG,SAAS,CAAC,IAAI,KAAK,MAAM,gDAAgD,CAC7E,CAAC;YACF,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAC9C,uEAAuE;YACvE,yCAAyC;YACzC,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,IAAI,8BAA8B,MAAM,IAAI,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AgentEvent } from "../events.js";
|
|
2
|
+
import type { ExtensionAPI, RegisteredCommand, RegisteredTool } from "./api.js";
|
|
3
|
+
export interface RegistryOptions {
|
|
4
|
+
/** Names an extension may not take, so a project cannot shadow read_file. */
|
|
5
|
+
readonly reservedToolNames: readonly string[];
|
|
6
|
+
readonly reservedCommandNames: readonly string[];
|
|
7
|
+
readonly onWarning: (message: string) => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Collects what extensions register.
|
|
11
|
+
*
|
|
12
|
+
* Every rejection is reported and survivable: a conflicting registration is
|
|
13
|
+
* dropped, the rest of the extension keeps whatever it registered before and
|
|
14
|
+
* after, and the built-in stays in place.
|
|
15
|
+
*/
|
|
16
|
+
export declare class ExtensionRegistry {
|
|
17
|
+
private readonly options;
|
|
18
|
+
private readonly tools;
|
|
19
|
+
private readonly commands;
|
|
20
|
+
private readonly listeners;
|
|
21
|
+
private readonly promptContributions;
|
|
22
|
+
constructor(options: RegistryOptions);
|
|
23
|
+
/** The API handed to one extension; `source` identifies it in diagnostics. */
|
|
24
|
+
apiFor(source: string): ExtensionAPI;
|
|
25
|
+
get registeredTools(): readonly RegisteredTool[];
|
|
26
|
+
get registeredCommands(): readonly RegisteredCommand[];
|
|
27
|
+
get systemPromptContributions(): readonly string[];
|
|
28
|
+
command(name: string): RegisteredCommand | undefined;
|
|
29
|
+
/** A single listener that fans one event out to every subscribed handler. */
|
|
30
|
+
get eventListener(): (event: AgentEvent) => void;
|
|
31
|
+
private addTool;
|
|
32
|
+
private addCommand;
|
|
33
|
+
private addListener;
|
|
34
|
+
private addPrompt;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/extensions/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EACV,YAAY,EAEZ,iBAAiB,EACjB,cAAc,EACf,MAAM,UAAU,CAAC;AAIlB,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD,QAAQ,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAED;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAShB,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwC;IACjE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAGjB;IACT,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IAEpD,YAA6B,OAAO,EAAE,eAAe,EAAI;IAEzD,8EAA8E;IAC9E,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CASnC;IAED,IAAI,eAAe,IAAI,SAAS,cAAc,EAAE,CAE/C;IAED,IAAI,kBAAkB,IAAI,SAAS,iBAAiB,EAAE,CAErD;IAED,IAAI,yBAAyB,IAAI,SAAS,MAAM,EAAE,CAEjD;IAED,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAEnD;IAED,6EAA6E;IAC7E,IAAI,aAAa,IAAI,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAa/C;IAED,OAAO,CAAC,OAAO;IA2Bf,OAAO,CAAC,UAAU;IAoClB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,SAAS;CAOlB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const COMMAND_NAME = /^[a-z][a-z0-9-]*$/;
|
|
2
|
+
/**
|
|
3
|
+
* Collects what extensions register.
|
|
4
|
+
*
|
|
5
|
+
* Every rejection is reported and survivable: a conflicting registration is
|
|
6
|
+
* dropped, the rest of the extension keeps whatever it registered before and
|
|
7
|
+
* after, and the built-in stays in place.
|
|
8
|
+
*/
|
|
9
|
+
export class ExtensionRegistry {
|
|
10
|
+
options;
|
|
11
|
+
tools = new Map();
|
|
12
|
+
commands = new Map();
|
|
13
|
+
listeners = [];
|
|
14
|
+
promptContributions = [];
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
}
|
|
18
|
+
/** The API handed to one extension; `source` identifies it in diagnostics. */
|
|
19
|
+
apiFor(source) {
|
|
20
|
+
return {
|
|
21
|
+
registerTool: (tool) => this.addTool(tool, source),
|
|
22
|
+
registerCommand: (name, command) => this.addCommand(name, command, source),
|
|
23
|
+
on: (type, handler) => this.addListener(type, handler, source),
|
|
24
|
+
contributeSystemPrompt: (text) => this.addPrompt(text, source),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
get registeredTools() {
|
|
28
|
+
return [...this.tools.values()];
|
|
29
|
+
}
|
|
30
|
+
get registeredCommands() {
|
|
31
|
+
return [...this.commands.values()];
|
|
32
|
+
}
|
|
33
|
+
get systemPromptContributions() {
|
|
34
|
+
return this.promptContributions;
|
|
35
|
+
}
|
|
36
|
+
command(name) {
|
|
37
|
+
return this.commands.get(name);
|
|
38
|
+
}
|
|
39
|
+
/** A single listener that fans one event out to every subscribed handler. */
|
|
40
|
+
get eventListener() {
|
|
41
|
+
return (event) => {
|
|
42
|
+
for (const listener of this.listeners) {
|
|
43
|
+
if (listener.type !== event.type) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
listener.handler(event);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// An extension must not change the outcome of the run it observes.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
addTool(tool, source) {
|
|
56
|
+
if (typeof tool !== "object" ||
|
|
57
|
+
tool === null ||
|
|
58
|
+
typeof tool.name !== "string" ||
|
|
59
|
+
tool.name.length === 0 ||
|
|
60
|
+
typeof tool.execute !== "function") {
|
|
61
|
+
this.options.onWarning(`${source}: registerTool needs a tool with a name and an execute function.`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (this.options.reservedToolNames.includes(tool.name)) {
|
|
65
|
+
this.options.onWarning(`${source}: cannot register the tool "${tool.name}"; that name is built in.`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const existing = this.tools.get(tool.name);
|
|
69
|
+
if (existing !== undefined) {
|
|
70
|
+
this.options.onWarning(`${source}: the tool "${tool.name}" is already registered by ${existing.source}.`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
this.tools.set(tool.name, { tool, source });
|
|
74
|
+
}
|
|
75
|
+
addCommand(name, command, source) {
|
|
76
|
+
if (typeof name !== "string" || !COMMAND_NAME.test(name)) {
|
|
77
|
+
this.options.onWarning(`${source}: "${String(name)}" is not a usable command name; use lowercase letters, digits and dashes.`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (typeof command !== "object" || command === null || typeof command.run !== "function") {
|
|
81
|
+
this.options.onWarning(`${source}: the command "${name}" needs a run function.`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (this.options.reservedCommandNames.includes(name)) {
|
|
85
|
+
this.options.onWarning(`${source}: cannot register "/${name}"; that command is built in.`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const existing = this.commands.get(name);
|
|
89
|
+
if (existing !== undefined) {
|
|
90
|
+
this.options.onWarning(`${source}: "/${name}" is already registered by ${existing.source}.`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.commands.set(name, {
|
|
94
|
+
name,
|
|
95
|
+
source,
|
|
96
|
+
description: typeof command.description === "string" ? command.description : "",
|
|
97
|
+
run: command.run.bind(command),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
addListener(type, handler, source) {
|
|
101
|
+
if (typeof handler !== "function") {
|
|
102
|
+
this.options.onWarning(`${source}: on("${String(type)}") needs a function.`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.listeners.push({ type, handler });
|
|
106
|
+
}
|
|
107
|
+
addPrompt(text, source) {
|
|
108
|
+
if (typeof text !== "string" || text.trim().length === 0) {
|
|
109
|
+
this.options.onWarning(`${source}: contributeSystemPrompt needs a non-empty string.`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.promptContributions.push(text.trim());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/extensions/registry.ts"],"names":[],"mappings":"AASA,MAAM,YAAY,GAAG,mBAAmB,CAAC;AASzC;;;;;;GAMG;AACH,MAAM,OAAO,iBAAiB;IASC,OAAO;IARnB,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC1C,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;IAChD,SAAS,GAGpB,EAAE,CAAC;IACQ,mBAAmB,GAAa,EAAE,CAAC;IAEpD,YAA6B,OAAwB;uBAAxB,OAAO;IAAoB,CAAC;IAEzD,8EAA8E;IAC9E,MAAM,CAAC,MAAc;QACnB,OAAO;YACL,YAAY,EAAE,CAAC,IAAU,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;YACxD,eAAe,EAAE,CAAC,IAAY,EAAE,OAAyB,EAAE,EAAE,CAC3D,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC;YACxC,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC;YAC9D,sBAAsB,EAAE,CAAC,IAAY,EAAE,EAAE,CACvC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC;SAC/B,CAAC;IACJ,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,kBAAkB;QACpB,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,yBAAyB;QAC3B,OAAO,IAAI,CAAC,mBAAmB,CAAC;IAClC,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,6EAA6E;IAC7E,IAAI,aAAa;QACf,OAAO,CAAC,KAAiB,EAAQ,EAAE;YACjC,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACtC,IAAI,QAAQ,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;oBACjC,SAAS;gBACX,CAAC;gBACD,IAAI,CAAC;oBACH,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC1B,CAAC;gBAAC,MAAM,CAAC;oBACP,mEAAmE;gBACrE,CAAC;YACH,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IAEO,OAAO,CAAC,IAAU,EAAE,MAAc;QACxC,IACE,OAAO,IAAI,KAAK,QAAQ;YACxB,IAAI,KAAK,IAAI;YACb,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;YAC7B,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;YACtB,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,EAClC,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,MAAM,kEAAkE,CAAC,CAAC;YACpG,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvD,IAAI,CAAC,OAAO,CAAC,SAAS,CACpB,GAAG,MAAM,+BAA+B,IAAI,CAAC,IAAI,2BAA2B,CAC7E,CAAC;YACF,OAAO;QACT,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,CAAC,SAAS,CACpB,GAAG,MAAM,eAAe,IAAI,CAAC,IAAI,8BAA8B,QAAQ,CAAC,MAAM,GAAG,CAClF,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9C,CAAC;IAEO,UAAU,CAChB,IAAY,EACZ,OAAyB,EACzB,MAAc;QAEd,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACzD,IAAI,CAAC,OAAO,CAAC,SAAS,CACpB,GAAG,MAAM,MAAM,MAAM,CAAC,IAAI,CAAC,2EAA2E,CACvG,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;YACzF,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,MAAM,kBAAkB,IAAI,yBAAyB,CAAC,CAAC;YACjF,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACrD,IAAI,CAAC,OAAO,CAAC,SAAS,CACpB,GAAG,MAAM,uBAAuB,IAAI,8BAA8B,CACnE,CAAC;YACF,OAAO;QACT,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,CAAC,SAAS,CACpB,GAAG,MAAM,OAAO,IAAI,8BAA8B,QAAQ,CAAC,MAAM,GAAG,CACrE,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE;YACtB,IAAI;YACJ,MAAM;YACN,WAAW,EAAE,OAAO,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE;YAC/E,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;SAC/B,CAAC,CAAC;IACL,CAAC;IAEO,WAAW,CACjB,IAAwB,EACxB,OAAoC,EACpC,MAAc;QAEd,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;YAClC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,MAAM,SAAS,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;YAC7E,OAAO;QACT,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACzC,CAAC;IAEO,SAAS,CAAC,IAAY,EAAE,MAAc;QAC5C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzD,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,MAAM,oDAAoD,CAAC,CAAC;YACtF,OAAO;QACT,CAAC;QACD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;CACF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare const CONFIG_DIRECTORY = ".chivgent";
|
|
2
|
+
export declare const EXTENSIONS_DIRECTORY = "extensions";
|
|
3
|
+
export declare class TrustFileError extends Error {
|
|
4
|
+
constructor(message: string, options?: {
|
|
5
|
+
cause?: unknown;
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
export declare function defaultTrustFile(environment?: NodeJS.ProcessEnv): string;
|
|
9
|
+
/**
|
|
10
|
+
* Resolves a directory the way the trust store keys it.
|
|
11
|
+
*
|
|
12
|
+
* Symlinks and `..` are resolved first so that two paths naming the same
|
|
13
|
+
* directory cannot produce two entries, one of which the user never sees.
|
|
14
|
+
*/
|
|
15
|
+
export declare function canonicalDirectory(directory: string): Promise<string>;
|
|
16
|
+
export type TrustDecision = boolean;
|
|
17
|
+
export interface TrustEntry {
|
|
18
|
+
/** The directory the decision was recorded against, which may be an ancestor. */
|
|
19
|
+
readonly path: string;
|
|
20
|
+
readonly decision: TrustDecision;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Records whether a project directory may contribute code to chivgent.
|
|
24
|
+
*
|
|
25
|
+
* Decisions are looked up by nearest ancestor, so trusting `~/work` covers
|
|
26
|
+
* every repository under it. A monorepo does not ask once per package.
|
|
27
|
+
*/
|
|
28
|
+
export declare class TrustStore {
|
|
29
|
+
private readonly filePath;
|
|
30
|
+
constructor(filePath?: string);
|
|
31
|
+
get location(): string;
|
|
32
|
+
lookup(directory: string): Promise<TrustEntry | undefined>;
|
|
33
|
+
set(directory: string, decision: TrustDecision): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Removes the decision that covers this directory, including an ancestor's,
|
|
36
|
+
* so that forgetting actually makes chivgent ask again.
|
|
37
|
+
*/
|
|
38
|
+
forget(directory: string): Promise<string | undefined>;
|
|
39
|
+
private read;
|
|
40
|
+
private write;
|
|
41
|
+
}
|
|
42
|
+
/** True when the project directory carries anything trust has to gate. */
|
|
43
|
+
export declare function hasProjectExtensions(cwd: string): boolean;
|
|
44
|
+
//# sourceMappingURL=trust.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trust.d.ts","sourceRoot":"","sources":["../../src/extensions/trust.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAC5C,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAGjD,qBAAa,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAGzD;CACF;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,GAAE,MAAM,CAAC,UAAwB,GAC3C,MAAM,CAER;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS3E;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC;AAEpC,MAAM,WAAW,UAAU;IACzB,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;CAClC;AAED;;;;;GAKG;AACH,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAArC,YAA6B,QAAQ,GAAE,MAA2B,EAAI;IAEtE,IAAI,QAAQ,IAAI,MAAM,CAErB;IAEK,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAc/D;IAEK,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnE;IAED;;;OAGG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAS3D;YAEa,IAAI;YAyCJ,KAAK;CAcpB;AAED,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEzD"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { realpath } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { defaultSessionHome } from "../session-store.js";
|
|
6
|
+
export const CONFIG_DIRECTORY = ".chivgent";
|
|
7
|
+
export const EXTENSIONS_DIRECTORY = "extensions";
|
|
8
|
+
const TRUST_FILE = "trust.json";
|
|
9
|
+
export class TrustFileError extends Error {
|
|
10
|
+
constructor(message, options) {
|
|
11
|
+
super(message, options);
|
|
12
|
+
this.name = "TrustFileError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function defaultTrustFile(environment = process.env) {
|
|
16
|
+
return path.join(defaultSessionHome(environment), TRUST_FILE);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolves a directory the way the trust store keys it.
|
|
20
|
+
*
|
|
21
|
+
* Symlinks and `..` are resolved first so that two paths naming the same
|
|
22
|
+
* directory cannot produce two entries, one of which the user never sees.
|
|
23
|
+
*/
|
|
24
|
+
export async function canonicalDirectory(directory) {
|
|
25
|
+
const absolute = path.resolve(directory);
|
|
26
|
+
try {
|
|
27
|
+
return await realpath(absolute);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// A directory that does not exist cannot be resolved, but it can still be
|
|
31
|
+
// recorded; the lexical form is the best available key.
|
|
32
|
+
return absolute;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Records whether a project directory may contribute code to chivgent.
|
|
37
|
+
*
|
|
38
|
+
* Decisions are looked up by nearest ancestor, so trusting `~/work` covers
|
|
39
|
+
* every repository under it. A monorepo does not ask once per package.
|
|
40
|
+
*/
|
|
41
|
+
export class TrustStore {
|
|
42
|
+
filePath;
|
|
43
|
+
constructor(filePath = defaultTrustFile()) {
|
|
44
|
+
this.filePath = filePath;
|
|
45
|
+
}
|
|
46
|
+
get location() {
|
|
47
|
+
return this.filePath;
|
|
48
|
+
}
|
|
49
|
+
async lookup(directory) {
|
|
50
|
+
const data = await this.read();
|
|
51
|
+
let current = await canonicalDirectory(directory);
|
|
52
|
+
for (;;) {
|
|
53
|
+
const decision = data[current];
|
|
54
|
+
if (decision !== undefined) {
|
|
55
|
+
return { path: current, decision };
|
|
56
|
+
}
|
|
57
|
+
const parent = path.dirname(current);
|
|
58
|
+
if (parent === current) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
current = parent;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async set(directory, decision) {
|
|
65
|
+
const key = await canonicalDirectory(directory);
|
|
66
|
+
const data = await this.read();
|
|
67
|
+
data[key] = decision;
|
|
68
|
+
await this.write(data);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Removes the decision that covers this directory, including an ancestor's,
|
|
72
|
+
* so that forgetting actually makes chivgent ask again.
|
|
73
|
+
*/
|
|
74
|
+
async forget(directory) {
|
|
75
|
+
const entry = await this.lookup(directory);
|
|
76
|
+
if (entry === undefined) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
const data = await this.read();
|
|
80
|
+
delete data[entry.path];
|
|
81
|
+
await this.write(data);
|
|
82
|
+
return entry.path;
|
|
83
|
+
}
|
|
84
|
+
async read() {
|
|
85
|
+
let contents;
|
|
86
|
+
try {
|
|
87
|
+
contents = await readFile(this.filePath, "utf8");
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (error.code === "ENOENT") {
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
93
|
+
throw new TrustFileError(`Could not read the trust file at ${this.filePath}.`, { cause: error });
|
|
94
|
+
}
|
|
95
|
+
let parsed;
|
|
96
|
+
try {
|
|
97
|
+
parsed = JSON.parse(contents);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
throw new TrustFileError(`The trust file at ${this.filePath} is not valid JSON.`, { cause: error });
|
|
101
|
+
}
|
|
102
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
103
|
+
throw new TrustFileError(`The trust file at ${this.filePath} must contain a JSON object keyed by directory.`);
|
|
104
|
+
}
|
|
105
|
+
const data = {};
|
|
106
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
107
|
+
if (typeof value !== "boolean") {
|
|
108
|
+
throw new TrustFileError(`The trust file entry for "${key}" must be true or false.`);
|
|
109
|
+
}
|
|
110
|
+
data[key] = value;
|
|
111
|
+
}
|
|
112
|
+
return data;
|
|
113
|
+
}
|
|
114
|
+
async write(data) {
|
|
115
|
+
await mkdir(path.dirname(this.filePath), { recursive: true });
|
|
116
|
+
const sorted = {};
|
|
117
|
+
for (const key of Object.keys(data).sort()) {
|
|
118
|
+
sorted[key] = data[key];
|
|
119
|
+
}
|
|
120
|
+
// The file is meant to be read and edited by a person, so it stays sorted
|
|
121
|
+
// and indented rather than compact.
|
|
122
|
+
await writeFile(this.filePath, `${JSON.stringify(sorted, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** True when the project directory carries anything trust has to gate. */
|
|
126
|
+
export function hasProjectExtensions(cwd) {
|
|
127
|
+
return existsSync(path.join(cwd, CONFIG_DIRECTORY, EXTENSIONS_DIRECTORY));
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=trust.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trust.js","sourceRoot":"","sources":["../../src/extensions/trust.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AgentEventListener } from "../events.js";
|
|
2
|
+
import { type ServerHello, type ServerResult } from "./protocol.js";
|
|
3
|
+
export declare class RemoteError extends Error {
|
|
4
|
+
readonly code?: string | undefined;
|
|
5
|
+
constructor(message: string, code?: string | undefined);
|
|
6
|
+
}
|
|
7
|
+
export interface RemoteSessionOptions {
|
|
8
|
+
readonly socketPath: string;
|
|
9
|
+
/** Receives the server's event stream, so the existing renderers work as-is. */
|
|
10
|
+
readonly onEvent?: AgentEventListener;
|
|
11
|
+
readonly onError?: (code: string, message: string) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A client attached to a session running in another process.
|
|
15
|
+
*
|
|
16
|
+
* It holds no state of its own: it sends prompts and renders what comes back.
|
|
17
|
+
* That is what keeps a second client honest — two attached clients see one
|
|
18
|
+
* stream because there is only one session.
|
|
19
|
+
*/
|
|
20
|
+
export declare class RemoteSession {
|
|
21
|
+
private readonly options;
|
|
22
|
+
private socket;
|
|
23
|
+
private readonly decoder;
|
|
24
|
+
private hello;
|
|
25
|
+
private pending;
|
|
26
|
+
private closedReason;
|
|
27
|
+
constructor(options: RemoteSessionOptions);
|
|
28
|
+
get session(): ServerHello["session"];
|
|
29
|
+
get toolNames(): readonly string[];
|
|
30
|
+
connect(): Promise<ServerHello>;
|
|
31
|
+
private onHello;
|
|
32
|
+
private onHelloError;
|
|
33
|
+
/** Sends a prompt and resolves when the run it started finishes. */
|
|
34
|
+
prompt(text: string): Promise<ServerResult>;
|
|
35
|
+
interrupt(): void;
|
|
36
|
+
close(): void;
|
|
37
|
+
private write;
|
|
38
|
+
private receive;
|
|
39
|
+
private handleClose;
|
|
40
|
+
private failAll;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/remote/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEnE,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAEvB,qBAAa,WAAY,SAAQ,KAAK;IACP,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM;IAAnD,YAAY,OAAO,EAAE,MAAM,EAAW,IAAI,CAAC,EAAE,MAAM,YAAA,EAGlD;CACF;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5D;AAED;;;;;;GAMG;AACH,qBAAa,aAAa;IAUZ,OAAO,CAAC,QAAQ,CAAC,OAAO;IATpC,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,OAAO,CAGD;IACd,OAAO,CAAC,YAAY,CAAqB;IAEzC,YAA6B,OAAO,EAAE,oBAAoB,EAAI;IAE9D,IAAI,OAAO,IAAI,WAAW,CAAC,SAAS,CAAC,CAKpC;IAED,IAAI,SAAS,IAAI,SAAS,MAAM,EAAE,CAEjC;IAEK,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC,CAsCpC;IAED,OAAO,CAAC,OAAO,CAA6C;IAC5D,OAAO,CAAC,YAAY,CAAuC;IAE3D,oEAAoE;IACpE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAW1C;IAED,SAAS,IAAI,IAAI,CAIhB;IAED,KAAK,IAAI,IAAI,CAIZ;IAED,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,OAAO;IAiEf,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,OAAO;CAQhB"}
|