@di-code/coding-agent 0.1.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/dist/cli.d.ts +26 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +117 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/models-loader.d.ts +4 -0
- package/dist/config/models-loader.d.ts.map +1 -0
- package/dist/config/models-loader.js +134 -0
- package/dist/config/models-loader.js.map +1 -0
- package/dist/config/models-types.d.ts +23 -0
- package/dist/config/models-types.d.ts.map +1 -0
- package/dist/config/models-types.js +2 -0
- package/dist/config/models-types.js.map +1 -0
- package/dist/config/settings-loader.d.ts +3 -0
- package/dist/config/settings-loader.d.ts.map +1 -0
- package/dist/config/settings-loader.js +130 -0
- package/dist/config/settings-loader.js.map +1 -0
- package/dist/config/settings-types.d.ts +21 -0
- package/dist/config/settings-types.d.ts.map +1 -0
- package/dist/config/settings-types.js +2 -0
- package/dist/config/settings-types.js.map +1 -0
- package/dist/core/context-builder.d.ts +8 -0
- package/dist/core/context-builder.d.ts.map +1 -0
- package/dist/core/context-builder.js +46 -0
- package/dist/core/context-builder.js.map +1 -0
- package/dist/core/model-catalog-store.d.ts +8 -0
- package/dist/core/model-catalog-store.d.ts.map +1 -0
- package/dist/core/model-catalog-store.js +68 -0
- package/dist/core/model-catalog-store.js.map +1 -0
- package/dist/core/model-composer.d.ts +4 -0
- package/dist/core/model-composer.d.ts.map +1 -0
- package/dist/core/model-composer.js +37 -0
- package/dist/core/model-composer.js.map +1 -0
- package/dist/core/session/session-manager.d.ts +44 -0
- package/dist/core/session/session-manager.d.ts.map +1 -0
- package/dist/core/session/session-manager.js +138 -0
- package/dist/core/session/session-manager.js.map +1 -0
- package/dist/core/session/session-storage.d.ts +11 -0
- package/dist/core/session/session-storage.d.ts.map +1 -0
- package/dist/core/session/session-storage.js +382 -0
- package/dist/core/session/session-storage.js.map +1 -0
- package/dist/core/session/types.d.ts +65 -0
- package/dist/core/session/types.d.ts.map +1 -0
- package/dist/core/session/types.js +28 -0
- package/dist/core/session/types.js.map +1 -0
- package/dist/core/session.d.ts +80 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +262 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/tools/bash.d.ts +39 -0
- package/dist/core/tools/bash.d.ts.map +1 -0
- package/dist/core/tools/bash.js +208 -0
- package/dist/core/tools/bash.js.map +1 -0
- package/dist/core/tools/edit.d.ts +18 -0
- package/dist/core/tools/edit.d.ts.map +1 -0
- package/dist/core/tools/edit.js +107 -0
- package/dist/core/tools/edit.js.map +1 -0
- package/dist/core/tools/file-mutation-queue.d.ts +2 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-queue.js +24 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -0
- package/dist/core/tools/path-boundary.d.ts +3 -0
- package/dist/core/tools/path-boundary.d.ts.map +1 -0
- package/dist/core/tools/path-boundary.js +62 -0
- package/dist/core/tools/path-boundary.js.map +1 -0
- package/dist/core/tools/read.d.ts +17 -0
- package/dist/core/tools/read.d.ts.map +1 -0
- package/dist/core/tools/read.js +132 -0
- package/dist/core/tools/read.js.map +1 -0
- package/dist/core/tools/write.d.ts +17 -0
- package/dist/core/tools/write.d.ts.map +1 -0
- package/dist/core/tools/write.js +45 -0
- package/dist/core/tools/write.js.map +1 -0
- package/dist/entry.d.ts +3 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +27 -0
- package/dist/entry.js.map +1 -0
- package/dist/extensions/index.d.ts +5 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +3 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/runtime.d.ts +46 -0
- package/dist/extensions/runtime.d.ts.map +1 -0
- package/dist/extensions/runtime.js +179 -0
- package/dist/extensions/runtime.js.map +1 -0
- package/dist/extensions/trust.d.ts +8 -0
- package/dist/extensions/trust.d.ts.map +1 -0
- package/dist/extensions/trust.js +59 -0
- package/dist/extensions/trust.js.map +1 -0
- package/dist/extensions/types.d.ts +46 -0
- package/dist/extensions/types.d.ts.map +1 -0
- package/dist/extensions/types.js +2 -0
- package/dist/extensions/types.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +19 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +198 -0
- package/dist/main.js.map +1 -0
- package/dist/modes/interactive-components.d.ts +41 -0
- package/dist/modes/interactive-components.d.ts.map +1 -0
- package/dist/modes/interactive-components.js +195 -0
- package/dist/modes/interactive-components.js.map +1 -0
- package/dist/modes/interactive-layout.d.ts +20 -0
- package/dist/modes/interactive-layout.d.ts.map +1 -0
- package/dist/modes/interactive-layout.js +34 -0
- package/dist/modes/interactive-layout.js.map +1 -0
- package/dist/modes/interactive-state.d.ts +61 -0
- package/dist/modes/interactive-state.d.ts.map +1 -0
- package/dist/modes/interactive-state.js +201 -0
- package/dist/modes/interactive-state.js.map +1 -0
- package/dist/modes/interactive.d.ts +61 -0
- package/dist/modes/interactive.d.ts.map +1 -0
- package/dist/modes/interactive.js +436 -0
- package/dist/modes/interactive.js.map +1 -0
- package/dist/modes/json.d.ts +14 -0
- package/dist/modes/json.d.ts.map +1 -0
- package/dist/modes/json.js +26 -0
- package/dist/modes/json.js.map +1 -0
- package/dist/modes/print.d.ts +10 -0
- package/dist/modes/print.d.ts.map +1 -0
- package/dist/modes/print.js +28 -0
- package/dist/modes/print.js.map +1 -0
- package/dist/provider-onboarding.d.ts +13 -0
- package/dist/provider-onboarding.d.ts.map +1 -0
- package/dist/provider-onboarding.js +162 -0
- package/dist/provider-onboarding.js.map +1 -0
- package/dist/rpc/client.d.ts +38 -0
- package/dist/rpc/client.d.ts.map +1 -0
- package/dist/rpc/client.js +153 -0
- package/dist/rpc/client.js.map +1 -0
- package/dist/rpc/index.d.ts +5 -0
- package/dist/rpc/index.d.ts.map +1 -0
- package/dist/rpc/index.js +5 -0
- package/dist/rpc/index.js.map +1 -0
- package/dist/rpc/jsonl.d.ts +17 -0
- package/dist/rpc/jsonl.d.ts.map +1 -0
- package/dist/rpc/jsonl.js +52 -0
- package/dist/rpc/jsonl.js.map +1 -0
- package/dist/rpc/protocol.d.ts +80 -0
- package/dist/rpc/protocol.d.ts.map +1 -0
- package/dist/rpc/protocol.js +231 -0
- package/dist/rpc/protocol.js.map +1 -0
- package/dist/rpc/server.d.ts +41 -0
- package/dist/rpc/server.d.ts.map +1 -0
- package/dist/rpc/server.js +186 -0
- package/dist/rpc/server.js.map +1 -0
- package/dist/rpc-entry.d.ts +3 -0
- package/dist/rpc-entry.d.ts.map +1 -0
- package/dist/rpc-entry.js +28 -0
- package/dist/rpc-entry.js.map +1 -0
- package/dist/startup.d.ts +24 -0
- package/dist/startup.d.ts.map +1 -0
- package/dist/startup.js +227 -0
- package/dist/startup.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/extensions/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAEX,gBAAgB,EAEhB,gBAAgB,EAGhB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,wBAAwB,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAEtF,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACrD;AAgBD,qBAAa,aAAa;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;IAChE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4C;IAClE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwF;gBAErG,OAAO,EAAE,oBAAoB;IAIzC,IAAI,gBAAgB,IAAI,gBAAgB,CAEvC;IAED,YAAY,IAAI,SAAS,gBAAgB,EAAE;IAI3C,SAAS,IAAI,SAAS,qBAAqB,EAAE;IAIvC,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B1E,IAAI,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnF,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrD,OAAO,CACZ,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,OAAO,GACZ,OAAO,CAAC,SAAS,OAAO,aAAa,EAAE,iBAAiB,EAAE,CAAC;CAY9D;AA2BD,wBAAsB,cAAc,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA+ChG;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAEhF"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
import { readdir } from "node:fs/promises";
|
|
11
|
+
import { extname, join, resolve } from "node:path";
|
|
12
|
+
import { validateToolArguments } from "@di-code/ai";
|
|
13
|
+
function isExtensionPath(path) {
|
|
14
|
+
return extname(path) === ".js" || extname(path) === ".mjs" || extname(path) === ".ts";
|
|
15
|
+
}
|
|
16
|
+
function createContext(options) {
|
|
17
|
+
return {
|
|
18
|
+
cwd: options.cwd,
|
|
19
|
+
mode: options.mode,
|
|
20
|
+
signal: undefined,
|
|
21
|
+
isProjectTrusted: () => options.projectTrusted,
|
|
22
|
+
abort: options.abort ?? (() => { }),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export class ExtensionHost {
|
|
26
|
+
context;
|
|
27
|
+
commands = new Map();
|
|
28
|
+
tools = new Map();
|
|
29
|
+
handlers = new Map();
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.context = createContext(options);
|
|
32
|
+
}
|
|
33
|
+
get extensionContext() {
|
|
34
|
+
return this.context;
|
|
35
|
+
}
|
|
36
|
+
listCommands() {
|
|
37
|
+
return [...this.commands.values()];
|
|
38
|
+
}
|
|
39
|
+
listTools() {
|
|
40
|
+
return [...this.tools.values()];
|
|
41
|
+
}
|
|
42
|
+
async registerExtension(_path, factory) {
|
|
43
|
+
const commands = [];
|
|
44
|
+
const tools = [];
|
|
45
|
+
const handlers = new Map();
|
|
46
|
+
const api = {
|
|
47
|
+
registerCommand: (command) => commands.push(command),
|
|
48
|
+
registerTool: (tool) => tools.push(tool),
|
|
49
|
+
on: (event, handler) => {
|
|
50
|
+
const list = handlers.get(event) ?? [];
|
|
51
|
+
list.push(handler);
|
|
52
|
+
handlers.set(event, list);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
await factory(api);
|
|
56
|
+
for (const command of commands) {
|
|
57
|
+
if (this.commands.has(command.name))
|
|
58
|
+
throw new Error(`Extension command conflict: "${command.name}"`);
|
|
59
|
+
}
|
|
60
|
+
for (const tool of tools) {
|
|
61
|
+
if (this.tools.has(tool.name))
|
|
62
|
+
throw new Error(`Extension tool conflict: "${tool.name}"`);
|
|
63
|
+
}
|
|
64
|
+
for (const command of commands)
|
|
65
|
+
this.commands.set(command.name, command);
|
|
66
|
+
for (const tool of tools)
|
|
67
|
+
this.tools.set(tool.name, tool);
|
|
68
|
+
for (const [event, list] of handlers) {
|
|
69
|
+
const existing = this.handlers.get(event) ?? [];
|
|
70
|
+
existing.push(...list);
|
|
71
|
+
this.handlers.set(event, existing);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async emit(event) {
|
|
75
|
+
const list = this.handlers.get(event.type) ?? [];
|
|
76
|
+
for (const handler of list)
|
|
77
|
+
await handler(event, this.context);
|
|
78
|
+
}
|
|
79
|
+
async runCommand(name, args) {
|
|
80
|
+
const command = this.commands.get(name);
|
|
81
|
+
if (!command)
|
|
82
|
+
throw new Error(`Unknown extension command: "${name}"`);
|
|
83
|
+
const commandContext = { ...this.context, args };
|
|
84
|
+
await command.handler(commandContext);
|
|
85
|
+
}
|
|
86
|
+
async runTool(name, toolCallId, input) {
|
|
87
|
+
const tool = this.tools.get(name);
|
|
88
|
+
if (!tool)
|
|
89
|
+
throw new Error(`Unknown extension tool: "${name}"`);
|
|
90
|
+
let parameters;
|
|
91
|
+
try {
|
|
92
|
+
parameters = validateToolArguments(tool, input);
|
|
93
|
+
}
|
|
94
|
+
catch (cause) {
|
|
95
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
96
|
+
throw new Error(`Tool arguments invalid: ${message}`, { cause });
|
|
97
|
+
}
|
|
98
|
+
return tool.execute(toolCallId, parameters, this.context.signal, this.context);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function discoverProjectPaths(cwd) {
|
|
102
|
+
const candidates = [join(cwd, ".di-code", "extensions"), join(cwd, ".pi", "extensions")];
|
|
103
|
+
const paths = [];
|
|
104
|
+
for (const directory of candidates) {
|
|
105
|
+
if (!existsSync(directory))
|
|
106
|
+
continue;
|
|
107
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
108
|
+
for (const entry of entries) {
|
|
109
|
+
if (entry.isFile() && isExtensionPath(entry.name))
|
|
110
|
+
paths.push(resolve(directory, entry.name));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return paths.sort((a, b) => a.localeCompare(b));
|
|
114
|
+
}
|
|
115
|
+
async function importFactory(path) {
|
|
116
|
+
let module;
|
|
117
|
+
try {
|
|
118
|
+
module = (await import(__rewriteRelativeImportExtension(path)));
|
|
119
|
+
}
|
|
120
|
+
catch (cause) {
|
|
121
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
122
|
+
throw new Error(`Failed to import extension: ${message}`, { cause });
|
|
123
|
+
}
|
|
124
|
+
if (typeof module.default !== "function")
|
|
125
|
+
throw new Error("Extension module must export a default factory function");
|
|
126
|
+
return module.default;
|
|
127
|
+
}
|
|
128
|
+
export async function loadExtensions(options) {
|
|
129
|
+
const cwd = resolve(options.cwd);
|
|
130
|
+
const persistedDecision = options.projectTrusted === undefined ? await options.trustManager?.get(cwd) : null;
|
|
131
|
+
const projectTrusted = options.projectTrusted ?? persistedDecision === true;
|
|
132
|
+
const host = new ExtensionHost({ cwd, mode: options.mode ?? "json", projectTrusted });
|
|
133
|
+
const diagnostics = [];
|
|
134
|
+
const explicitPaths = [...(options.paths ?? [])].map((path) => resolve(cwd, path));
|
|
135
|
+
const projectPaths = projectTrusted ? await discoverProjectPaths(cwd) : [];
|
|
136
|
+
if (!projectTrusted) {
|
|
137
|
+
const projectDirectories = [join(cwd, ".di-code", "extensions"), join(cwd, ".pi", "extensions")];
|
|
138
|
+
for (const directory of projectDirectories) {
|
|
139
|
+
if (existsSync(directory)) {
|
|
140
|
+
diagnostics.push({
|
|
141
|
+
path: directory,
|
|
142
|
+
stage: "discover",
|
|
143
|
+
message: "Project extension skipped because project trust is not granted",
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const paths = [...new Set([...explicitPaths, ...projectPaths])].sort((a, b) => a.localeCompare(b));
|
|
149
|
+
const loaded = [];
|
|
150
|
+
for (const path of paths) {
|
|
151
|
+
try {
|
|
152
|
+
const factory = await importFactory(path);
|
|
153
|
+
await host.registerExtension(path, factory);
|
|
154
|
+
loaded.push({ path });
|
|
155
|
+
}
|
|
156
|
+
catch (cause) {
|
|
157
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
158
|
+
const stage = message.startsWith("Extension module")
|
|
159
|
+
? "factory"
|
|
160
|
+
: message.startsWith("Extension command conflict") || message.startsWith("Extension tool conflict")
|
|
161
|
+
? "register"
|
|
162
|
+
: message.startsWith("Failed to import extension")
|
|
163
|
+
? "import"
|
|
164
|
+
: "factory";
|
|
165
|
+
diagnostics.push({
|
|
166
|
+
path,
|
|
167
|
+
stage,
|
|
168
|
+
message: stage === "factory" && message !== "Extension module must export a default factory function"
|
|
169
|
+
? `Failed to load extension: ${message}`
|
|
170
|
+
: message,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { host, loaded, diagnostics };
|
|
175
|
+
}
|
|
176
|
+
export function createExtensionHost(options) {
|
|
177
|
+
return new ExtensionHost(options);
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/extensions/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { extname, join, resolve } from \"node:path\";\nimport { validateToolArguments } from \"@di-code/ai\";\nimport type { ProjectTrustManager } from \"./trust.ts\";\nimport type {\n\tExtensionAPI,\n\tExtensionCommand,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tExtensionEvent,\n\tExtensionEventHandler,\n\tExtensionEventMap,\n\tExtensionFactory,\n\tExtensionReadOnlyTool,\n} from \"./types.ts\";\n\nexport interface ExtensionHostOptions {\n\treadonly cwd: string;\n\treadonly mode: ExtensionContext[\"mode\"];\n\treadonly projectTrusted: boolean;\n\treadonly abort?: () => void;\n}\n\nexport interface LoadedExtension {\n\treadonly path: string;\n}\n\nexport type ExtensionDiagnosticStage = \"discover\" | \"import\" | \"factory\" | \"register\";\n\nexport interface ExtensionDiagnostic {\n\treadonly path: string;\n\treadonly stage: ExtensionDiagnosticStage;\n\treadonly message: string;\n}\n\nexport interface ExtensionLoadOptions {\n\treadonly cwd: string;\n\treadonly projectTrusted?: boolean;\n\treadonly trustManager?: ProjectTrustManager;\n\treadonly paths?: readonly string[];\n\treadonly mode?: ExtensionContext[\"mode\"];\n}\n\nexport interface ExtensionLoadResult {\n\treadonly host: ExtensionHost;\n\treadonly loaded: readonly LoadedExtension[];\n\treadonly diagnostics: readonly ExtensionDiagnostic[];\n}\n\nfunction isExtensionPath(path: string): boolean {\n\treturn extname(path) === \".js\" || extname(path) === \".mjs\" || extname(path) === \".ts\";\n}\n\nfunction createContext(options: ExtensionHostOptions): ExtensionContext {\n\treturn {\n\t\tcwd: options.cwd,\n\t\tmode: options.mode,\n\t\tsignal: undefined,\n\t\tisProjectTrusted: () => options.projectTrusted,\n\t\tabort: options.abort ?? (() => {}),\n\t};\n}\n\nexport class ExtensionHost {\n\tprivate readonly context: ExtensionContext;\n\tprivate readonly commands = new Map<string, ExtensionCommand>();\n\tprivate readonly tools = new Map<string, ExtensionReadOnlyTool>();\n\tprivate readonly handlers = new Map<keyof ExtensionEventMap, ExtensionEventHandler<keyof ExtensionEventMap>[]>();\n\n\tconstructor(options: ExtensionHostOptions) {\n\t\tthis.context = createContext(options);\n\t}\n\n\tget extensionContext(): ExtensionContext {\n\t\treturn this.context;\n\t}\n\n\tlistCommands(): readonly ExtensionCommand[] {\n\t\treturn [...this.commands.values()];\n\t}\n\n\tlistTools(): readonly ExtensionReadOnlyTool[] {\n\t\treturn [...this.tools.values()];\n\t}\n\n\tasync registerExtension(_path: string, factory: ExtensionFactory): Promise<void> {\n\t\tconst commands: ExtensionCommand[] = [];\n\t\tconst tools: ExtensionReadOnlyTool[] = [];\n\t\tconst handlers = new Map<keyof ExtensionEventMap, ExtensionEventHandler<keyof ExtensionEventMap>[]>();\n\t\tconst api: ExtensionAPI = {\n\t\t\tregisterCommand: (command) => commands.push(command),\n\t\t\tregisterTool: (tool) => tools.push(tool),\n\t\t\ton: (event, handler) => {\n\t\t\t\tconst list = handlers.get(event) ?? [];\n\t\t\t\tlist.push(handler as ExtensionEventHandler<keyof ExtensionEventMap>);\n\t\t\t\thandlers.set(event, list);\n\t\t\t},\n\t\t};\n\t\tawait factory(api);\n\n\t\tfor (const command of commands) {\n\t\t\tif (this.commands.has(command.name)) throw new Error(`Extension command conflict: \"${command.name}\"`);\n\t\t}\n\t\tfor (const tool of tools) {\n\t\t\tif (this.tools.has(tool.name)) throw new Error(`Extension tool conflict: \"${tool.name}\"`);\n\t\t}\n\t\tfor (const command of commands) this.commands.set(command.name, command);\n\t\tfor (const tool of tools) this.tools.set(tool.name, tool);\n\t\tfor (const [event, list] of handlers) {\n\t\t\tconst existing = this.handlers.get(event) ?? [];\n\t\t\texisting.push(...list);\n\t\t\tthis.handlers.set(event, existing);\n\t\t}\n\t}\n\n\tasync emit<E extends keyof ExtensionEventMap>(event: ExtensionEventMap[E]): Promise<void> {\n\t\tconst list = this.handlers.get(event.type) ?? [];\n\t\tfor (const handler of list) await handler(event as ExtensionEvent, this.context);\n\t}\n\n\tasync runCommand(name: string, args: string): Promise<void> {\n\t\tconst command = this.commands.get(name);\n\t\tif (!command) throw new Error(`Unknown extension command: \"${name}\"`);\n\t\tconst commandContext: ExtensionCommandContext = { ...this.context, args };\n\t\tawait command.handler(commandContext);\n\t}\n\n\tasync runTool(\n\t\tname: string,\n\t\ttoolCallId: string,\n\t\tinput: unknown,\n\t): Promise<readonly import(\"@di-code/ai\").ToolResultContent[]> {\n\t\tconst tool = this.tools.get(name);\n\t\tif (!tool) throw new Error(`Unknown extension tool: \"${name}\"`);\n\t\tlet parameters: unknown;\n\t\ttry {\n\t\t\tparameters = validateToolArguments(tool, input);\n\t\t} catch (cause) {\n\t\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\t\tthrow new Error(`Tool arguments invalid: ${message}`, { cause });\n\t\t}\n\t\treturn tool.execute(toolCallId, parameters as never, this.context.signal, this.context);\n\t}\n}\n\nasync function discoverProjectPaths(cwd: string): Promise<string[]> {\n\tconst candidates = [join(cwd, \".di-code\", \"extensions\"), join(cwd, \".pi\", \"extensions\")];\n\tconst paths: string[] = [];\n\tfor (const directory of candidates) {\n\t\tif (!existsSync(directory)) continue;\n\t\tconst entries = await readdir(directory, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.isFile() && isExtensionPath(entry.name)) paths.push(resolve(directory, entry.name));\n\t\t}\n\t}\n\treturn paths.sort((a, b) => a.localeCompare(b));\n}\n\nasync function importFactory(path: string): Promise<ExtensionFactory> {\n\tlet module: { default?: unknown };\n\ttry {\n\t\tmodule = (await import(path)) as { default?: unknown };\n\t} catch (cause) {\n\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\tthrow new Error(`Failed to import extension: ${message}`, { cause });\n\t}\n\tif (typeof module.default !== \"function\") throw new Error(\"Extension module must export a default factory function\");\n\treturn module.default as ExtensionFactory;\n}\n\nexport async function loadExtensions(options: ExtensionLoadOptions): Promise<ExtensionLoadResult> {\n\tconst cwd = resolve(options.cwd);\n\tconst persistedDecision = options.projectTrusted === undefined ? await options.trustManager?.get(cwd) : null;\n\tconst projectTrusted = options.projectTrusted ?? persistedDecision === true;\n\tconst host = new ExtensionHost({ cwd, mode: options.mode ?? \"json\", projectTrusted });\n\tconst diagnostics: ExtensionDiagnostic[] = [];\n\tconst explicitPaths = [...(options.paths ?? [])].map((path) => resolve(cwd, path));\n\tconst projectPaths = projectTrusted ? await discoverProjectPaths(cwd) : [];\n\tif (!projectTrusted) {\n\t\tconst projectDirectories = [join(cwd, \".di-code\", \"extensions\"), join(cwd, \".pi\", \"extensions\")];\n\t\tfor (const directory of projectDirectories) {\n\t\t\tif (existsSync(directory)) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\tpath: directory,\n\t\t\t\t\tstage: \"discover\",\n\t\t\t\t\tmessage: \"Project extension skipped because project trust is not granted\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\tconst paths = [...new Set([...explicitPaths, ...projectPaths])].sort((a, b) => a.localeCompare(b));\n\tconst loaded: LoadedExtension[] = [];\n\tfor (const path of paths) {\n\t\ttry {\n\t\t\tconst factory = await importFactory(path);\n\t\t\tawait host.registerExtension(path, factory);\n\t\t\tloaded.push({ path });\n\t\t} catch (cause) {\n\t\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\t\tconst stage: ExtensionDiagnosticStage = message.startsWith(\"Extension module\")\n\t\t\t\t? \"factory\"\n\t\t\t\t: message.startsWith(\"Extension command conflict\") || message.startsWith(\"Extension tool conflict\")\n\t\t\t\t\t? \"register\"\n\t\t\t\t\t: message.startsWith(\"Failed to import extension\")\n\t\t\t\t\t\t? \"import\"\n\t\t\t\t\t\t: \"factory\";\n\t\t\tdiagnostics.push({\n\t\t\t\tpath,\n\t\t\t\tstage,\n\t\t\t\tmessage:\n\t\t\t\t\tstage === \"factory\" && message !== \"Extension module must export a default factory function\"\n\t\t\t\t\t\t? `Failed to load extension: ${message}`\n\t\t\t\t\t\t: message,\n\t\t\t});\n\t\t}\n\t}\n\treturn { host, loaded, diagnostics };\n}\n\nexport function createExtensionHost(options: ExtensionHostOptions): ExtensionHost {\n\treturn new ExtensionHost(options);\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class ProjectTrustManager {
|
|
2
|
+
private readonly filePath;
|
|
3
|
+
constructor(filePath: string);
|
|
4
|
+
get(cwd: string): Promise<boolean | null>;
|
|
5
|
+
set(cwd: string, decision: boolean | null): Promise<void>;
|
|
6
|
+
private read;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=trust.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trust.d.ts","sourceRoot":"","sources":["../../src/extensions/trust.ts"],"names":[],"mappings":"AAgBA,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,QAAQ,EAAE,MAAM;IAItB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAYzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;YAWjD,IAAI;CASlB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, parse, resolve } from "node:path";
|
|
3
|
+
function normalize(path) {
|
|
4
|
+
return resolve(path);
|
|
5
|
+
}
|
|
6
|
+
function emptyTrustFile() {
|
|
7
|
+
return { version: 1, projects: {} };
|
|
8
|
+
}
|
|
9
|
+
export class ProjectTrustManager {
|
|
10
|
+
filePath;
|
|
11
|
+
constructor(filePath) {
|
|
12
|
+
this.filePath = resolve(filePath);
|
|
13
|
+
}
|
|
14
|
+
async get(cwd) {
|
|
15
|
+
const data = await this.read();
|
|
16
|
+
let current = normalize(cwd);
|
|
17
|
+
while (true) {
|
|
18
|
+
const decision = data.projects[current];
|
|
19
|
+
if (decision === true || decision === false)
|
|
20
|
+
return decision;
|
|
21
|
+
const parent = parse(current).root === current ? current : resolve(current, "..");
|
|
22
|
+
if (parent === current)
|
|
23
|
+
return null;
|
|
24
|
+
current = parent;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async set(cwd, decision) {
|
|
28
|
+
const data = await this.read();
|
|
29
|
+
const key = normalize(cwd);
|
|
30
|
+
if (decision === null)
|
|
31
|
+
delete data.projects[key];
|
|
32
|
+
else
|
|
33
|
+
data.projects[key] = decision;
|
|
34
|
+
await mkdir(dirname(this.filePath), { recursive: true });
|
|
35
|
+
const temporaryPath = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
36
|
+
await writeFile(temporaryPath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
37
|
+
await rename(temporaryPath, this.filePath);
|
|
38
|
+
}
|
|
39
|
+
async read() {
|
|
40
|
+
try {
|
|
41
|
+
const parsed = JSON.parse(await readFile(this.filePath, "utf8"));
|
|
42
|
+
if (!isTrustFile(parsed))
|
|
43
|
+
return emptyTrustFile();
|
|
44
|
+
return { version: 1, projects: { ...parsed.projects } };
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return emptyTrustFile();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function isTrustFile(value) {
|
|
52
|
+
if (typeof value !== "object" || value === null)
|
|
53
|
+
return false;
|
|
54
|
+
const candidate = value;
|
|
55
|
+
if (candidate.version !== 1 || typeof candidate.projects !== "object" || candidate.projects === null)
|
|
56
|
+
return false;
|
|
57
|
+
return Object.values(candidate.projects).every((decision) => typeof decision === "boolean");
|
|
58
|
+
}
|
|
59
|
+
//# 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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","sourcesContent":["import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, parse, resolve } from \"node:path\";\n\ninterface TrustFile {\n\treadonly version: 1;\n\treadonly projects: Record<string, boolean>;\n}\n\nfunction normalize(path: string): string {\n\treturn resolve(path);\n}\n\nfunction emptyTrustFile(): TrustFile {\n\treturn { version: 1, projects: {} };\n}\n\nexport class ProjectTrustManager {\n\tprivate readonly filePath: string;\n\n\tconstructor(filePath: string) {\n\t\tthis.filePath = resolve(filePath);\n\t}\n\n\tasync get(cwd: string): Promise<boolean | null> {\n\t\tconst data = await this.read();\n\t\tlet current = normalize(cwd);\n\t\twhile (true) {\n\t\t\tconst decision = data.projects[current];\n\t\t\tif (decision === true || decision === false) return decision;\n\t\t\tconst parent = parse(current).root === current ? current : resolve(current, \"..\");\n\t\t\tif (parent === current) return null;\n\t\t\tcurrent = parent;\n\t\t}\n\t}\n\n\tasync set(cwd: string, decision: boolean | null): Promise<void> {\n\t\tconst data = await this.read();\n\t\tconst key = normalize(cwd);\n\t\tif (decision === null) delete data.projects[key];\n\t\telse data.projects[key] = decision;\n\t\tawait mkdir(dirname(this.filePath), { recursive: true });\n\t\tconst temporaryPath = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;\n\t\tawait writeFile(temporaryPath, `${JSON.stringify(data, null, 2)}\\n`, \"utf8\");\n\t\tawait rename(temporaryPath, this.filePath);\n\t}\n\n\tprivate async read(): Promise<TrustFile> {\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(await readFile(this.filePath, \"utf8\"));\n\t\t\tif (!isTrustFile(parsed)) return emptyTrustFile();\n\t\t\treturn { version: 1, projects: { ...parsed.projects } };\n\t\t} catch {\n\t\t\treturn emptyTrustFile();\n\t\t}\n\t}\n}\n\nfunction isTrustFile(value: unknown): value is TrustFile {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst candidate = value as { version?: unknown; projects?: unknown };\n\tif (candidate.version !== 1 || typeof candidate.projects !== \"object\" || candidate.projects === null) return false;\n\treturn Object.values(candidate.projects).every((decision) => typeof decision === \"boolean\");\n}\n"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AgentEvent } from "@di-code/agent";
|
|
2
|
+
import type { Static, ToolResultContent, TSchema } from "@di-code/ai";
|
|
3
|
+
export type ExtensionMode = "interactive" | "print" | "json";
|
|
4
|
+
export interface ExtensionContext {
|
|
5
|
+
readonly cwd: string;
|
|
6
|
+
readonly mode: ExtensionMode;
|
|
7
|
+
readonly signal?: AbortSignal;
|
|
8
|
+
readonly isProjectTrusted: () => boolean;
|
|
9
|
+
abort: () => void;
|
|
10
|
+
}
|
|
11
|
+
export interface ExtensionCommandContext extends ExtensionContext {
|
|
12
|
+
readonly args: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ExtensionCommand {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly description: string;
|
|
17
|
+
readonly handler: (ctx: ExtensionCommandContext) => void | Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export interface ExtensionReadOnlyTool<TParameters extends TSchema = TSchema> {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly description: string;
|
|
22
|
+
readonly parameters: TParameters;
|
|
23
|
+
readonly execute: (toolCallId: string, parameters: Static<TParameters>, signal: AbortSignal | undefined, ctx: ExtensionContext) => Promise<ToolResultContent[]>;
|
|
24
|
+
}
|
|
25
|
+
export interface SessionStartEvent {
|
|
26
|
+
readonly type: "session_start";
|
|
27
|
+
readonly cwd: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SessionShutdownEvent {
|
|
30
|
+
readonly type: "session_shutdown";
|
|
31
|
+
readonly reason: "user" | "error";
|
|
32
|
+
}
|
|
33
|
+
export type ExtensionEvent = AgentEvent | SessionStartEvent | SessionShutdownEvent;
|
|
34
|
+
export type ExtensionEventMap = {
|
|
35
|
+
[E in ExtensionEvent["type"]]: Extract<ExtensionEvent, {
|
|
36
|
+
type: E;
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
export type ExtensionEventHandler<E extends keyof ExtensionEventMap> = (event: ExtensionEventMap[E], ctx: ExtensionContext) => void | Promise<void>;
|
|
40
|
+
export interface ExtensionAPI {
|
|
41
|
+
registerCommand(command: ExtensionCommand): void;
|
|
42
|
+
registerTool<TParameters extends TSchema>(tool: ExtensionReadOnlyTool<TParameters>): void;
|
|
43
|
+
on<E extends keyof ExtensionEventMap>(event: E, handler: ExtensionEventHandler<E>): void;
|
|
44
|
+
}
|
|
45
|
+
export type ExtensionFactory = (api: ExtensionAPI) => void | Promise<void>;
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/extensions/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtE,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,OAAO,CAAC;IACzC,KAAK,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,uBAAwB,SAAQ,gBAAgB;IAChE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE;AAED,MAAM,WAAW,qBAAqB,CAAC,WAAW,SAAS,OAAO,GAAG,OAAO;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,CACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,EAC/B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,GAAG,EAAE,gBAAgB,KACjB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;CAClC;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;AAEnF,MAAM,MAAM,iBAAiB,GAAG;KAC9B,CAAC,IAAI,cAAc,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,cAAc,EAAE;QAAE,IAAI,EAAE,CAAC,CAAA;KAAE,CAAC;CACnE,CAAC;AAEF,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,MAAM,iBAAiB,IAAI,CACtE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC3B,GAAG,EAAE,gBAAgB,KACjB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,WAAW,YAAY;IAC5B,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjD,YAAY,CAAC,WAAW,SAAS,OAAO,EAAE,IAAI,EAAE,qBAAqB,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC;IAC1F,EAAE,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CACzF;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/extensions/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AgentEvent } from \"@di-code/agent\";\nimport type { Static, ToolResultContent, TSchema } from \"@di-code/ai\";\n\nexport type ExtensionMode = \"interactive\" | \"print\" | \"json\";\n\nexport interface ExtensionContext {\n\treadonly cwd: string;\n\treadonly mode: ExtensionMode;\n\treadonly signal?: AbortSignal;\n\treadonly isProjectTrusted: () => boolean;\n\tabort: () => void;\n}\n\nexport interface ExtensionCommandContext extends ExtensionContext {\n\treadonly args: string;\n}\n\nexport interface ExtensionCommand {\n\treadonly name: string;\n\treadonly description: string;\n\treadonly handler: (ctx: ExtensionCommandContext) => void | Promise<void>;\n}\n\nexport interface ExtensionReadOnlyTool<TParameters extends TSchema = TSchema> {\n\treadonly name: string;\n\treadonly description: string;\n\treadonly parameters: TParameters;\n\treadonly execute: (\n\t\ttoolCallId: string,\n\t\tparameters: Static<TParameters>,\n\t\tsignal: AbortSignal | undefined,\n\t\tctx: ExtensionContext,\n\t) => Promise<ToolResultContent[]>;\n}\n\nexport interface SessionStartEvent {\n\treadonly type: \"session_start\";\n\treadonly cwd: string;\n}\n\nexport interface SessionShutdownEvent {\n\treadonly type: \"session_shutdown\";\n\treadonly reason: \"user\" | \"error\";\n}\n\nexport type ExtensionEvent = AgentEvent | SessionStartEvent | SessionShutdownEvent;\n\nexport type ExtensionEventMap = {\n\t[E in ExtensionEvent[\"type\"]]: Extract<ExtensionEvent, { type: E }>;\n};\n\nexport type ExtensionEventHandler<E extends keyof ExtensionEventMap> = (\n\tevent: ExtensionEventMap[E],\n\tctx: ExtensionContext,\n) => void | Promise<void>;\n\nexport interface ExtensionAPI {\n\tregisterCommand(command: ExtensionCommand): void;\n\tregisterTool<TParameters extends TSchema>(tool: ExtensionReadOnlyTool<TParameters>): void;\n\ton<E extends keyof ExtensionEventMap>(event: E, handler: ExtensionEventHandler<E>): void;\n}\n\nexport type ExtensionFactory = (api: ExtensionAPI) => void | Promise<void>;\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./cli.ts";
|
|
2
|
+
export * from "./core/context-builder.ts";
|
|
3
|
+
export * from "./core/session/session-manager.ts";
|
|
4
|
+
export * from "./core/session/session-storage.ts";
|
|
5
|
+
export * from "./core/session/types.ts";
|
|
6
|
+
export * from "./core/session.ts";
|
|
7
|
+
export * from "./extensions/index.ts";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./cli.js";
|
|
2
|
+
export * from "./core/context-builder.js";
|
|
3
|
+
export * from "./core/session/session-manager.js";
|
|
4
|
+
export * from "./core/session/session-storage.js";
|
|
5
|
+
export * from "./core/session/types.js";
|
|
6
|
+
export * from "./core/session.js";
|
|
7
|
+
export * from "./extensions/index.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC","sourcesContent":["export * from \"./cli.ts\";\nexport * from \"./core/context-builder.ts\";\nexport * from \"./core/session/session-manager.ts\";\nexport * from \"./core/session/session-storage.ts\";\nexport * from \"./core/session/types.ts\";\nexport * from \"./core/session.ts\";\nexport * from \"./extensions/index.ts\";\n"]}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Model, Provider } from "@di-code/ai";
|
|
2
|
+
import { type CliCommand } from "./cli.ts";
|
|
3
|
+
import { SessionManager } from "./core/session/session-manager.ts";
|
|
4
|
+
import { type PrintIo } from "./modes/print.ts";
|
|
5
|
+
export interface MainRuntime {
|
|
6
|
+
readonly provider: Provider;
|
|
7
|
+
readonly model: Model;
|
|
8
|
+
}
|
|
9
|
+
export interface MainOptions extends PrintIo {
|
|
10
|
+
readonly version: string;
|
|
11
|
+
readonly createRuntime: (command: Extract<CliCommand, {
|
|
12
|
+
kind: "run";
|
|
13
|
+
}>) => MainRuntime | undefined | Promise<MainRuntime | undefined>;
|
|
14
|
+
readonly allowedRoot?: string;
|
|
15
|
+
readonly now?: () => number;
|
|
16
|
+
}
|
|
17
|
+
export declare function formatSessionLabel(manager: SessionManager): string;
|
|
18
|
+
export declare function runMain(args: readonly string[], options: MainOptions): Promise<number>;
|
|
19
|
+
//# sourceMappingURL=main.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAAE,KAAK,UAAU,EAAgC,MAAM,UAAU,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAInE,OAAO,EAAE,KAAK,OAAO,EAAgB,MAAM,kBAAkB,CAAC;AAE9D,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,WAAY,SAAQ,OAAO;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,CACvB,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE,CAAC,KACzC,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;IAChE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC5B;AA0ED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAmBlE;AAuCD,wBAAsB,OAAO,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAsE5F"}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { access, readdir, stat } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
4
|
+
import { ProcessTerminal, TUI } from "@di-code/tui";
|
|
5
|
+
import { runCli } from "./cli.js";
|
|
6
|
+
import { SessionManager } from "./core/session/session-manager.js";
|
|
7
|
+
import { AgentSession } from "./core/session.js";
|
|
8
|
+
import { InteractiveMode } from "./modes/interactive.js";
|
|
9
|
+
import { runJsonMode } from "./modes/json.js";
|
|
10
|
+
import { runPrintMode } from "./modes/print.js";
|
|
11
|
+
const DEFAULT_SESSION_DIRECTORY = join(".di-code", "sessions");
|
|
12
|
+
const MAX_SESSION_QUESTION_LENGTH = 72;
|
|
13
|
+
async function openOrCreateSession(filePath, cwd, now) {
|
|
14
|
+
try {
|
|
15
|
+
await access(filePath);
|
|
16
|
+
return await SessionManager.open(filePath, { now });
|
|
17
|
+
}
|
|
18
|
+
catch (cause) {
|
|
19
|
+
if (!(cause instanceof Error && "code" in cause && cause.code === "ENOENT"))
|
|
20
|
+
throw cause;
|
|
21
|
+
return SessionManager.create({ filePath, cwd, now, deferCreate: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function newSessionPath(sessionDirectory, now) {
|
|
25
|
+
return join(sessionDirectory, `session-${now()}-${randomUUID().slice(0, 8)}.jsonl`);
|
|
26
|
+
}
|
|
27
|
+
async function mostRecentSessionPath(sessionDirectory) {
|
|
28
|
+
let names;
|
|
29
|
+
try {
|
|
30
|
+
names = (await readdir(sessionDirectory, { withFileTypes: true }))
|
|
31
|
+
.filter((entry) => entry.isFile() && extname(entry.name) === ".jsonl")
|
|
32
|
+
.map((entry) => entry.name);
|
|
33
|
+
}
|
|
34
|
+
catch (cause) {
|
|
35
|
+
if (cause instanceof Error && "code" in cause && cause.code === "ENOENT")
|
|
36
|
+
return undefined;
|
|
37
|
+
throw cause;
|
|
38
|
+
}
|
|
39
|
+
const candidates = await Promise.all(names.map(async (name) => {
|
|
40
|
+
const filePath = join(sessionDirectory, name);
|
|
41
|
+
try {
|
|
42
|
+
return { filePath, modifiedAt: (await stat(filePath)).mtimeMs };
|
|
43
|
+
}
|
|
44
|
+
catch (cause) {
|
|
45
|
+
if (cause instanceof Error && "code" in cause && cause.code === "ENOENT")
|
|
46
|
+
return undefined;
|
|
47
|
+
throw cause;
|
|
48
|
+
}
|
|
49
|
+
}));
|
|
50
|
+
return candidates
|
|
51
|
+
.filter((candidate) => candidate !== undefined)
|
|
52
|
+
.sort((left, right) => right.modifiedAt - left.modifiedAt || right.filePath.localeCompare(left.filePath))[0]
|
|
53
|
+
?.filePath;
|
|
54
|
+
}
|
|
55
|
+
async function selectStartupSession(command, allowedRoot, now) {
|
|
56
|
+
const sessionDirectory = resolve(allowedRoot, DEFAULT_SESSION_DIRECTORY);
|
|
57
|
+
if (command.sessionPath !== undefined) {
|
|
58
|
+
return openOrCreateSession(resolve(allowedRoot, command.sessionPath), allowedRoot, now);
|
|
59
|
+
}
|
|
60
|
+
if (command.continueSession) {
|
|
61
|
+
const recentPath = await mostRecentSessionPath(sessionDirectory);
|
|
62
|
+
if (recentPath !== undefined)
|
|
63
|
+
return SessionManager.open(recentPath, { now });
|
|
64
|
+
}
|
|
65
|
+
return SessionManager.create({
|
|
66
|
+
filePath: newSessionPath(sessionDirectory, now),
|
|
67
|
+
cwd: allowedRoot,
|
|
68
|
+
now,
|
|
69
|
+
deferCreate: true,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function formatSessionTimestamp(timestamp, fallback) {
|
|
73
|
+
const date = new Date(timestamp);
|
|
74
|
+
const isoTimestamp = Number.isNaN(date.getTime()) ? fallback : date.toISOString();
|
|
75
|
+
return isoTimestamp.slice(0, 16).replace("T", " ");
|
|
76
|
+
}
|
|
77
|
+
export function formatSessionLabel(manager) {
|
|
78
|
+
const firstUserEntry = manager.entries.find((entry) => entry.type === "message" && entry.message.role === "user");
|
|
79
|
+
const fileName = basename(manager.filePath, extname(manager.filePath));
|
|
80
|
+
if (firstUserEntry?.type !== "message" || firstUserEntry.message.role !== "user") {
|
|
81
|
+
return `${fileName} (${formatSessionTimestamp(Date.parse(manager.header.timestamp), manager.header.timestamp)})`;
|
|
82
|
+
}
|
|
83
|
+
const question = firstUserEntry.message.content
|
|
84
|
+
.filter((content) => content.type === "text")
|
|
85
|
+
.map((content) => content.text)
|
|
86
|
+
.join(" ")
|
|
87
|
+
.replace(/\s+/g, " ")
|
|
88
|
+
.trim();
|
|
89
|
+
const label = question.length > MAX_SESSION_QUESTION_LENGTH
|
|
90
|
+
? `${question.slice(0, MAX_SESSION_QUESTION_LENGTH - 3)}...`
|
|
91
|
+
: question || fileName;
|
|
92
|
+
const timestamp = formatSessionTimestamp(firstUserEntry.message.timestamp, firstUserEntry.timestamp);
|
|
93
|
+
return `${label} (${timestamp})`;
|
|
94
|
+
}
|
|
95
|
+
async function sessionChoices(sessionDirectory, currentFile, open) {
|
|
96
|
+
let names = [];
|
|
97
|
+
try {
|
|
98
|
+
names = (await readdir(sessionDirectory, { withFileTypes: true }))
|
|
99
|
+
.filter((entry) => entry.isFile() && extname(entry.name) === ".jsonl")
|
|
100
|
+
.map((entry) => entry.name)
|
|
101
|
+
.sort();
|
|
102
|
+
}
|
|
103
|
+
catch (cause) {
|
|
104
|
+
if (!(cause instanceof Error && "code" in cause && cause.code === "ENOENT"))
|
|
105
|
+
throw cause;
|
|
106
|
+
}
|
|
107
|
+
return Promise.all(names
|
|
108
|
+
.filter((name) => resolve(sessionDirectory, name) !== resolve(currentFile))
|
|
109
|
+
.map(async (name) => {
|
|
110
|
+
const filePath = join(sessionDirectory, name);
|
|
111
|
+
const id = basename(name, extname(name));
|
|
112
|
+
let label = id;
|
|
113
|
+
try {
|
|
114
|
+
label = formatSessionLabel(await SessionManager.open(filePath));
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Keep damaged sessions selectable so opening one can report the detailed load error.
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
id,
|
|
121
|
+
label,
|
|
122
|
+
description: filePath,
|
|
123
|
+
open: () => open(filePath),
|
|
124
|
+
};
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
export async function runMain(args, options) {
|
|
128
|
+
const dependencies = {
|
|
129
|
+
stdout: options.stdout,
|
|
130
|
+
stderr: options.stderr,
|
|
131
|
+
version: options.version,
|
|
132
|
+
run: async (command) => {
|
|
133
|
+
const allowedRoot = resolve(options.allowedRoot ?? process.cwd());
|
|
134
|
+
const now = options.now ?? Date.now;
|
|
135
|
+
const runtime = await options.createRuntime(command);
|
|
136
|
+
if (runtime === undefined)
|
|
137
|
+
return 0;
|
|
138
|
+
const manager = await selectStartupSession(command, allowedRoot, now);
|
|
139
|
+
const sessionFile = manager.filePath;
|
|
140
|
+
const session = new AgentSession({
|
|
141
|
+
allowedRoot,
|
|
142
|
+
provider: runtime.provider,
|
|
143
|
+
model: runtime.model,
|
|
144
|
+
now: options.now,
|
|
145
|
+
sessionManager: manager,
|
|
146
|
+
});
|
|
147
|
+
if (command.mode === "json") {
|
|
148
|
+
return runJsonMode(command.prompt, session, options);
|
|
149
|
+
}
|
|
150
|
+
if (command.mode === "interactive") {
|
|
151
|
+
const terminal = new ProcessTerminal();
|
|
152
|
+
const tui = new TUI(terminal);
|
|
153
|
+
const mode = new InteractiveMode({
|
|
154
|
+
session,
|
|
155
|
+
tui,
|
|
156
|
+
sessions: [
|
|
157
|
+
{
|
|
158
|
+
id: "new-session",
|
|
159
|
+
label: "New session",
|
|
160
|
+
description: "Start a new persistent conversation.",
|
|
161
|
+
open: async () => {
|
|
162
|
+
const filePath = newSessionPath(dirname(sessionFile), now);
|
|
163
|
+
const nextManager = await SessionManager.create({
|
|
164
|
+
filePath,
|
|
165
|
+
cwd: allowedRoot,
|
|
166
|
+
now,
|
|
167
|
+
deferCreate: true,
|
|
168
|
+
});
|
|
169
|
+
return new AgentSession({
|
|
170
|
+
allowedRoot,
|
|
171
|
+
provider: runtime.provider,
|
|
172
|
+
model: runtime.model,
|
|
173
|
+
now: options.now,
|
|
174
|
+
sessionManager: nextManager,
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
...(await sessionChoices(dirname(sessionFile), sessionFile, async (filePath) => {
|
|
179
|
+
const nextManager = await SessionManager.open(filePath, { now });
|
|
180
|
+
return new AgentSession({
|
|
181
|
+
allowedRoot,
|
|
182
|
+
provider: runtime.provider,
|
|
183
|
+
model: runtime.model,
|
|
184
|
+
now: options.now,
|
|
185
|
+
sessionManager: nextManager,
|
|
186
|
+
});
|
|
187
|
+
})),
|
|
188
|
+
],
|
|
189
|
+
});
|
|
190
|
+
mode.start(command.prompt);
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
return runPrintMode(command.prompt, session, options);
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
return runCli(args, dependencies);
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=main.js.map
|