@dugleelabs/copair 1.3.0 → 1.4.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 +49 -0
- package/dist/api.d.ts +738 -0
- package/dist/api.js +7799 -0
- package/dist/api.js.map +1 -0
- package/dist/index.js +267 -43
- package/dist/index.js.map +1 -1
- package/package.json +15 -4
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/
|
|
3
|
+
// src/bootstrap.ts
|
|
4
4
|
import { join as join15 } from "path";
|
|
5
5
|
import { existsSync as existsSync18, readFileSync as readFileSync10 } from "fs";
|
|
6
|
+
import { createRequire as createRequire3 } from "module";
|
|
7
|
+
import { resolve as resolve10, dirname as dirname7 } from "path";
|
|
8
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6
9
|
|
|
7
10
|
// src/cli/args.ts
|
|
8
11
|
import { Command } from "commander";
|
|
@@ -18,7 +21,7 @@ var pkg = (() => {
|
|
|
18
21
|
} catch {
|
|
19
22
|
}
|
|
20
23
|
}
|
|
21
|
-
return { name: "copair", version: "0.
|
|
24
|
+
return { name: "copair", version: process.env["COPAIR_VERSION"] ?? "0.0.0-dev" };
|
|
22
25
|
})();
|
|
23
26
|
function parseArgs(argv = process.argv) {
|
|
24
27
|
const program = new Command();
|
|
@@ -1201,6 +1204,7 @@ var Agent = class {
|
|
|
1201
1204
|
_model;
|
|
1202
1205
|
formatter;
|
|
1203
1206
|
textFilter;
|
|
1207
|
+
pluginManager;
|
|
1204
1208
|
constructor(provider, model, toolRegistry, executor, options = {}) {
|
|
1205
1209
|
this.provider = provider;
|
|
1206
1210
|
this._model = model;
|
|
@@ -1212,6 +1216,7 @@ var Agent = class {
|
|
|
1212
1216
|
this.options = options;
|
|
1213
1217
|
this.formatter = resolveFormatter(provider.name, model, options.toolCallFormat);
|
|
1214
1218
|
this.textFilter = buildStreamingFilter(this.formatter);
|
|
1219
|
+
this.pluginManager = options.pluginManager;
|
|
1215
1220
|
}
|
|
1216
1221
|
get model() {
|
|
1217
1222
|
return this._model;
|
|
@@ -1265,6 +1270,7 @@ ${summary}`
|
|
|
1265
1270
|
this.conversation.appendText("user", userInput);
|
|
1266
1271
|
let totalUsage = null;
|
|
1267
1272
|
let lastInputTokens = 0;
|
|
1273
|
+
const meta = {};
|
|
1268
1274
|
let agentWebSearchFailed = false;
|
|
1269
1275
|
while (true) {
|
|
1270
1276
|
const messages = await this.contextWindow.checkAndTruncate(
|
|
@@ -1284,10 +1290,33 @@ ${summary}`
|
|
|
1284
1290
|
const webSearchHint = allTools.some((t) => t.name === "web_search") ? "When the user asks you to search the web, or requests current/up-to-date information, you MUST call the web_search tool. Never answer such queries from training knowledge alone \u2014 always invoke the tool and base your response on its results." : void 0;
|
|
1285
1291
|
const systemPrompt = [INJECTION_PREAMBLE, this.options.systemPrompt, toolSystemPrompt, webSearchHint].filter(Boolean).join("\n\n") || void 0;
|
|
1286
1292
|
logger.debug("agent", `System prompt (${systemPrompt?.length ?? 0} chars): preamble=${systemPrompt?.includes("CONTEXT DATA") ?? false} knowledge=${systemPrompt?.includes("<knowledge") ?? false}`);
|
|
1287
|
-
|
|
1293
|
+
let activeProvider = this.provider;
|
|
1294
|
+
let activeMessages = messages;
|
|
1295
|
+
let activeTools = tools;
|
|
1296
|
+
let activeSystemPrompt = systemPrompt;
|
|
1297
|
+
if (this.pluginManager) {
|
|
1298
|
+
const preEvent = await this.pluginManager.preRequest({
|
|
1299
|
+
messages,
|
|
1300
|
+
tools,
|
|
1301
|
+
systemPrompt: systemPrompt ?? "",
|
|
1302
|
+
provider: this.provider,
|
|
1303
|
+
model: this._model,
|
|
1304
|
+
meta
|
|
1305
|
+
});
|
|
1306
|
+
activeMessages = preEvent.messages;
|
|
1307
|
+
activeTools = preEvent.tools;
|
|
1308
|
+
activeSystemPrompt = preEvent.systemPrompt || void 0;
|
|
1309
|
+
activeProvider = this.pluginManager.interceptProvider({
|
|
1310
|
+
currentProvider: this.provider,
|
|
1311
|
+
model: this._model,
|
|
1312
|
+
messages: activeMessages,
|
|
1313
|
+
tokenCount: 0
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
const stream = activeProvider.chat(activeMessages, activeTools, {
|
|
1288
1317
|
model: this._model,
|
|
1289
1318
|
stream: true,
|
|
1290
|
-
systemPrompt,
|
|
1319
|
+
systemPrompt: activeSystemPrompt,
|
|
1291
1320
|
maxTokens: this.options.maxTokens,
|
|
1292
1321
|
temperature: this.options.temperature
|
|
1293
1322
|
});
|
|
@@ -1320,6 +1349,23 @@ ${summary}`
|
|
|
1320
1349
|
outputTokens: totalUsage.outputTokens + usage.outputTokens
|
|
1321
1350
|
} : { ...usage };
|
|
1322
1351
|
}
|
|
1352
|
+
if (this.pluginManager) {
|
|
1353
|
+
await this.pluginManager.postRequest({
|
|
1354
|
+
messages: activeMessages,
|
|
1355
|
+
response: {
|
|
1356
|
+
text: cleanedText ?? "",
|
|
1357
|
+
toolCalls: toolCalls.map((tc) => ({
|
|
1358
|
+
id: tc.id,
|
|
1359
|
+
name: tc.name,
|
|
1360
|
+
input: JSON.parse(tc.arguments || "{}")
|
|
1361
|
+
})),
|
|
1362
|
+
usage: usage ?? null
|
|
1363
|
+
},
|
|
1364
|
+
provider: activeProvider,
|
|
1365
|
+
model: this._model,
|
|
1366
|
+
meta
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1323
1369
|
if (toolCalls.length === 0) {
|
|
1324
1370
|
if (cleanedText && cleanedText.trim()) {
|
|
1325
1371
|
this.conversation.appendText("assistant", cleanedText);
|
|
@@ -1514,6 +1560,7 @@ var CopairConfigSchema = z.object({
|
|
|
1514
1560
|
permissions: PermissionsConfigSchema.default(() => PermissionsConfigSchema.parse({})),
|
|
1515
1561
|
feature_flags: FeatureFlagsSchema.default({ model_routing: false }),
|
|
1516
1562
|
mcp_servers: z.array(McpServerConfigSchema).default([]),
|
|
1563
|
+
plugins: z.array(z.string()).optional().default([]),
|
|
1517
1564
|
web_search: WebSearchConfigSchema.optional(),
|
|
1518
1565
|
identity: IdentityConfigSchema.default({ name: "Copair", email: "copair[bot]@noreply.dugleelabs.io" }),
|
|
1519
1566
|
context: ContextConfigSchema.default(() => ContextConfigSchema.parse({})),
|
|
@@ -2873,7 +2920,7 @@ var McpClientManager = class {
|
|
|
2873
2920
|
env
|
|
2874
2921
|
});
|
|
2875
2922
|
const client = new Client(
|
|
2876
|
-
{ name: "copair", version: "0.
|
|
2923
|
+
{ name: "copair", version: process.env["COPAIR_VERSION"] ?? "0.0.0-dev" },
|
|
2877
2924
|
{ capabilities: {} }
|
|
2878
2925
|
);
|
|
2879
2926
|
await client.connect(transport);
|
|
@@ -3144,18 +3191,18 @@ async function presentSessionPicker(sessions) {
|
|
|
3144
3191
|
console.log(` ${sessions.length + 1}. Start fresh`);
|
|
3145
3192
|
process.stdout.write(`
|
|
3146
3193
|
Select [1-${sessions.length + 1}]: `);
|
|
3147
|
-
return new Promise((
|
|
3194
|
+
return new Promise((resolve11) => {
|
|
3148
3195
|
const rl = createInterface({ input: process.stdin, terminal: false });
|
|
3149
3196
|
rl.once("line", (line) => {
|
|
3150
3197
|
rl.close();
|
|
3151
3198
|
const choice = parseInt(line.trim(), 10);
|
|
3152
3199
|
if (choice >= 1 && choice <= sessions.length) {
|
|
3153
|
-
|
|
3200
|
+
resolve11(sessions[choice - 1].id);
|
|
3154
3201
|
} else {
|
|
3155
|
-
|
|
3202
|
+
resolve11(null);
|
|
3156
3203
|
}
|
|
3157
3204
|
});
|
|
3158
|
-
rl.once("close", () =>
|
|
3205
|
+
rl.once("close", () => resolve11(null));
|
|
3159
3206
|
});
|
|
3160
3207
|
}
|
|
3161
3208
|
var SessionManager = class _SessionManager {
|
|
@@ -3518,7 +3565,7 @@ import { existsSync as existsSync6 } from "fs";
|
|
|
3518
3565
|
// src/commands/interpolate.ts
|
|
3519
3566
|
import { execSync as execSync6 } from "child_process";
|
|
3520
3567
|
async function interpolate(template, args, context) {
|
|
3521
|
-
const
|
|
3568
|
+
const resolve11 = (key) => {
|
|
3522
3569
|
if (key.startsWith("env.")) {
|
|
3523
3570
|
return process.env[key.slice(4)] ?? "";
|
|
3524
3571
|
}
|
|
@@ -3529,10 +3576,10 @@ async function interpolate(template, args, context) {
|
|
|
3529
3576
|
return null;
|
|
3530
3577
|
};
|
|
3531
3578
|
let result = template.replace(/\{\{([^}]+)\}\}/g, (_match, key) => {
|
|
3532
|
-
return
|
|
3579
|
+
return resolve11(key.trim()) ?? _match;
|
|
3533
3580
|
});
|
|
3534
3581
|
result = result.replace(/\$([A-Z][A-Z0-9_]*)/g, (_match, key) => {
|
|
3535
|
-
return
|
|
3582
|
+
return resolve11(key) ?? _match;
|
|
3536
3583
|
});
|
|
3537
3584
|
return result;
|
|
3538
3585
|
}
|
|
@@ -4316,8 +4363,8 @@ var SessionSummarizer = class {
|
|
|
4316
4363
|
return text.trim();
|
|
4317
4364
|
}
|
|
4318
4365
|
timeout() {
|
|
4319
|
-
return new Promise((
|
|
4320
|
-
setTimeout(() =>
|
|
4366
|
+
return new Promise((resolve11) => {
|
|
4367
|
+
setTimeout(() => resolve11(null), this.timeoutMs);
|
|
4321
4368
|
});
|
|
4322
4369
|
}
|
|
4323
4370
|
};
|
|
@@ -4362,7 +4409,7 @@ var pkg2 = (() => {
|
|
|
4362
4409
|
} catch {
|
|
4363
4410
|
}
|
|
4364
4411
|
}
|
|
4365
|
-
return { name: "copair", version: "0.
|
|
4412
|
+
return { name: "copair", version: process.env["COPAIR_VERSION"] ?? "0.0.0-dev" };
|
|
4366
4413
|
})();
|
|
4367
4414
|
var CACHE_DIR = resolve7(process.env["HOME"] ?? "~", ".copair");
|
|
4368
4415
|
var CACHE_FILE = join5(CACHE_DIR, "version-check.json");
|
|
@@ -4594,7 +4641,7 @@ var ApprovalGate = class {
|
|
|
4594
4641
|
}
|
|
4595
4642
|
/** Bridge-based approval: emit event and await response from ink UI. */
|
|
4596
4643
|
bridgePrompt(toolName, input, key) {
|
|
4597
|
-
return new Promise((
|
|
4644
|
+
return new Promise((resolve11) => {
|
|
4598
4645
|
const summary = formatSummary(toolName, input);
|
|
4599
4646
|
const warning = typeof input._sensitivePathWarning === "string" ? input._sensitivePathWarning : void 0;
|
|
4600
4647
|
this.bridge.emit("approval-request", {
|
|
@@ -4608,29 +4655,29 @@ var ApprovalGate = class {
|
|
|
4608
4655
|
switch (answer) {
|
|
4609
4656
|
case "allow":
|
|
4610
4657
|
void this.auditLog?.append({ event: "approval", tool: toolName, approved_by: "user", outcome: "allowed" });
|
|
4611
|
-
|
|
4658
|
+
resolve11(true);
|
|
4612
4659
|
break;
|
|
4613
4660
|
case "always":
|
|
4614
4661
|
this.alwaysAllow.add(key);
|
|
4615
4662
|
void this.auditLog?.append({ event: "approval", tool: toolName, approved_by: "user", outcome: "allowed", detail: "always" });
|
|
4616
|
-
|
|
4663
|
+
resolve11(true);
|
|
4617
4664
|
break;
|
|
4618
4665
|
case "all":
|
|
4619
4666
|
this.bridge.approveAllForTurn = true;
|
|
4620
4667
|
void this.auditLog?.append({ event: "approval", tool: toolName, approved_by: "user", outcome: "allowed", detail: "approve-all" });
|
|
4621
|
-
|
|
4668
|
+
resolve11(true);
|
|
4622
4669
|
break;
|
|
4623
4670
|
case "similar": {
|
|
4624
4671
|
const similarKey = similarSessionKey(toolName, input);
|
|
4625
4672
|
this.alwaysAllow.add(similarKey);
|
|
4626
4673
|
void this.auditLog?.append({ event: "approval", tool: toolName, approved_by: "user", outcome: "allowed", detail: "similar" });
|
|
4627
|
-
|
|
4674
|
+
resolve11(true);
|
|
4628
4675
|
break;
|
|
4629
4676
|
}
|
|
4630
4677
|
case "deny":
|
|
4631
4678
|
default:
|
|
4632
4679
|
void this.auditLog?.append({ event: "denial", tool: toolName, outcome: "denied", detail: "user denied" });
|
|
4633
|
-
|
|
4680
|
+
resolve11(false);
|
|
4634
4681
|
break;
|
|
4635
4682
|
}
|
|
4636
4683
|
});
|
|
@@ -6354,10 +6401,18 @@ import chalk6 from "chalk";
|
|
|
6354
6401
|
// package.json
|
|
6355
6402
|
var package_default = {
|
|
6356
6403
|
name: "@dugleelabs/copair",
|
|
6357
|
-
version: "1.
|
|
6404
|
+
version: "1.4.0",
|
|
6358
6405
|
description: "Model-agnostic AI coding agent for the terminal",
|
|
6359
6406
|
type: "module",
|
|
6360
|
-
main: "dist/
|
|
6407
|
+
main: "dist/api.js",
|
|
6408
|
+
types: "dist/api.d.ts",
|
|
6409
|
+
exports: {
|
|
6410
|
+
".": {
|
|
6411
|
+
import: "./dist/api.js",
|
|
6412
|
+
types: "./dist/api.d.ts"
|
|
6413
|
+
},
|
|
6414
|
+
"./cli": "./dist/index.js"
|
|
6415
|
+
},
|
|
6361
6416
|
bin: {
|
|
6362
6417
|
copair: "dist/index.js"
|
|
6363
6418
|
},
|
|
@@ -6368,6 +6423,7 @@ var package_default = {
|
|
|
6368
6423
|
lint: "eslint src/",
|
|
6369
6424
|
"lint:fix": "eslint src/ --fix",
|
|
6370
6425
|
dev: "tsup --watch",
|
|
6426
|
+
"build:sea": "node scripts/build-sea.mjs",
|
|
6371
6427
|
prepublishOnly: "pnpm lint && pnpm test && pnpm build"
|
|
6372
6428
|
},
|
|
6373
6429
|
files: [
|
|
@@ -6392,7 +6448,7 @@ var package_default = {
|
|
|
6392
6448
|
url: "https://github.com/dugleelabs/copair.git"
|
|
6393
6449
|
},
|
|
6394
6450
|
engines: {
|
|
6395
|
-
node: ">=
|
|
6451
|
+
node: ">=22.0.0"
|
|
6396
6452
|
},
|
|
6397
6453
|
pnpm: {
|
|
6398
6454
|
onlyBuiltDependencies: [
|
|
@@ -6405,7 +6461,9 @@ var package_default = {
|
|
|
6405
6461
|
"@types/node": "^25.5.0",
|
|
6406
6462
|
"@types/react": "^19.2.14",
|
|
6407
6463
|
"@types/which": "^3.0.4",
|
|
6464
|
+
esbuild: "^0.28.0",
|
|
6408
6465
|
eslint: "^10.0.3",
|
|
6466
|
+
postject: "1.0.0-alpha.6",
|
|
6409
6467
|
tsup: "^8.5.1",
|
|
6410
6468
|
typescript: "^5.9.3",
|
|
6411
6469
|
"typescript-eslint": "^8.57.1",
|
|
@@ -7193,7 +7251,162 @@ async function runAuditCommand(argv) {
|
|
|
7193
7251
|
printEntries(entries, !!opts.json);
|
|
7194
7252
|
}
|
|
7195
7253
|
|
|
7196
|
-
// src/
|
|
7254
|
+
// src/core/plugin-manager.ts
|
|
7255
|
+
var PluginManager = class {
|
|
7256
|
+
plugins = [];
|
|
7257
|
+
/**
|
|
7258
|
+
* Register a plugin instance. Called during bootstrap
|
|
7259
|
+
* (either from config.yaml or programmatic API).
|
|
7260
|
+
*/
|
|
7261
|
+
register(plugin) {
|
|
7262
|
+
this.plugins.push(plugin);
|
|
7263
|
+
logger.debug("PluginManager", `Registered plugin: ${plugin.name}@${plugin.version}`);
|
|
7264
|
+
}
|
|
7265
|
+
/**
|
|
7266
|
+
* Load plugins from config.yaml `plugins` array.
|
|
7267
|
+
* Each entry is a package name or local path resolved via import().
|
|
7268
|
+
*/
|
|
7269
|
+
async loadFromConfig(pluginPaths) {
|
|
7270
|
+
const start = performance.now();
|
|
7271
|
+
for (const path of pluginPaths) {
|
|
7272
|
+
try {
|
|
7273
|
+
const mod = await import(path);
|
|
7274
|
+
const plugin = mod.default ?? mod;
|
|
7275
|
+
if (!plugin.name || !plugin.version) {
|
|
7276
|
+
logger.warn("PluginManager", `Plugin at "${path}" missing name or version, skipping`);
|
|
7277
|
+
continue;
|
|
7278
|
+
}
|
|
7279
|
+
this.register(plugin);
|
|
7280
|
+
} catch (err) {
|
|
7281
|
+
logger.warn("PluginManager", `Failed to load plugin "${path}": ${err}`);
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7284
|
+
logger.debug("PluginManager", `loadFromConfig completed in ${(performance.now() - start).toFixed(1)}ms (${pluginPaths.length} paths)`);
|
|
7285
|
+
}
|
|
7286
|
+
/** Initialize all plugins (called once during bootstrap). */
|
|
7287
|
+
async initialize(context) {
|
|
7288
|
+
const start = performance.now();
|
|
7289
|
+
for (const plugin of this.plugins) {
|
|
7290
|
+
try {
|
|
7291
|
+
await plugin.initialize?.(context);
|
|
7292
|
+
logger.debug("PluginManager", `Initialized plugin: ${plugin.name}`);
|
|
7293
|
+
} catch (err) {
|
|
7294
|
+
logger.warn("PluginManager", `Plugin "${plugin.name}" initialize failed: ${err}`);
|
|
7295
|
+
}
|
|
7296
|
+
}
|
|
7297
|
+
logger.debug("PluginManager", `initialize completed in ${(performance.now() - start).toFixed(1)}ms (${this.plugins.length} plugins)`);
|
|
7298
|
+
}
|
|
7299
|
+
/**
|
|
7300
|
+
* Run preRequest hooks in registration order.
|
|
7301
|
+
* Each plugin receives the (possibly modified) event from the prior plugin.
|
|
7302
|
+
*/
|
|
7303
|
+
async preRequest(event) {
|
|
7304
|
+
let current = event;
|
|
7305
|
+
for (const plugin of this.plugins) {
|
|
7306
|
+
try {
|
|
7307
|
+
if (plugin.preRequest) {
|
|
7308
|
+
current = await plugin.preRequest(current);
|
|
7309
|
+
}
|
|
7310
|
+
} catch (err) {
|
|
7311
|
+
logger.warn("PluginManager", `Plugin "${plugin.name}" preRequest failed: ${err}`);
|
|
7312
|
+
}
|
|
7313
|
+
}
|
|
7314
|
+
return current;
|
|
7315
|
+
}
|
|
7316
|
+
/** Run postRequest hooks (observation only). */
|
|
7317
|
+
async postRequest(event) {
|
|
7318
|
+
for (const plugin of this.plugins) {
|
|
7319
|
+
try {
|
|
7320
|
+
await plugin.postRequest?.(event);
|
|
7321
|
+
} catch (err) {
|
|
7322
|
+
logger.warn("PluginManager", `Plugin "${plugin.name}" postRequest failed: ${err}`);
|
|
7323
|
+
}
|
|
7324
|
+
}
|
|
7325
|
+
}
|
|
7326
|
+
/**
|
|
7327
|
+
* Run providerInterceptor hooks. First plugin to return
|
|
7328
|
+
* a non-undefined provider wins (short-circuit).
|
|
7329
|
+
*/
|
|
7330
|
+
interceptProvider(event) {
|
|
7331
|
+
for (const plugin of this.plugins) {
|
|
7332
|
+
try {
|
|
7333
|
+
const override = plugin.providerInterceptor?.(event);
|
|
7334
|
+
if (override) return override;
|
|
7335
|
+
} catch (err) {
|
|
7336
|
+
logger.warn("PluginManager", `Plugin "${plugin.name}" providerInterceptor failed: ${err}`);
|
|
7337
|
+
}
|
|
7338
|
+
}
|
|
7339
|
+
return event.currentProvider;
|
|
7340
|
+
}
|
|
7341
|
+
// ── P1 hooks (stubbed, wired later) ──
|
|
7342
|
+
async preToolCall(event) {
|
|
7343
|
+
let current = event;
|
|
7344
|
+
for (const plugin of this.plugins) {
|
|
7345
|
+
try {
|
|
7346
|
+
if (plugin.preToolCall) {
|
|
7347
|
+
current = await plugin.preToolCall(current);
|
|
7348
|
+
}
|
|
7349
|
+
} catch (err) {
|
|
7350
|
+
logger.warn("PluginManager", `Plugin "${plugin.name}" preToolCall failed: ${err}`);
|
|
7351
|
+
}
|
|
7352
|
+
}
|
|
7353
|
+
return current;
|
|
7354
|
+
}
|
|
7355
|
+
async postToolCall(event) {
|
|
7356
|
+
for (const plugin of this.plugins) {
|
|
7357
|
+
try {
|
|
7358
|
+
await plugin.postToolCall?.(event);
|
|
7359
|
+
} catch (err) {
|
|
7360
|
+
logger.warn("PluginManager", `Plugin "${plugin.name}" postToolCall failed: ${err}`);
|
|
7361
|
+
}
|
|
7362
|
+
}
|
|
7363
|
+
}
|
|
7364
|
+
async sessionStart(event) {
|
|
7365
|
+
for (const plugin of this.plugins) {
|
|
7366
|
+
try {
|
|
7367
|
+
await plugin.sessionStart?.(event);
|
|
7368
|
+
} catch (err) {
|
|
7369
|
+
logger.warn("PluginManager", `Plugin "${plugin.name}" sessionStart failed: ${err}`);
|
|
7370
|
+
}
|
|
7371
|
+
}
|
|
7372
|
+
}
|
|
7373
|
+
async sessionEnd(event) {
|
|
7374
|
+
for (const plugin of this.plugins) {
|
|
7375
|
+
try {
|
|
7376
|
+
await plugin.sessionEnd?.(event);
|
|
7377
|
+
} catch (err) {
|
|
7378
|
+
logger.warn("PluginManager", `Plugin "${plugin.name}" sessionEnd failed: ${err}`);
|
|
7379
|
+
}
|
|
7380
|
+
}
|
|
7381
|
+
}
|
|
7382
|
+
/** Destroy all plugins on shutdown. */
|
|
7383
|
+
async destroy() {
|
|
7384
|
+
for (const plugin of this.plugins) {
|
|
7385
|
+
try {
|
|
7386
|
+
await plugin.destroy?.();
|
|
7387
|
+
} catch (err) {
|
|
7388
|
+
logger.warn("PluginManager", `Plugin "${plugin.name}" destroy failed: ${err}`);
|
|
7389
|
+
}
|
|
7390
|
+
}
|
|
7391
|
+
}
|
|
7392
|
+
/** Get the count of registered plugins. */
|
|
7393
|
+
get count() {
|
|
7394
|
+
return this.plugins.length;
|
|
7395
|
+
}
|
|
7396
|
+
};
|
|
7397
|
+
|
|
7398
|
+
// src/bootstrap.ts
|
|
7399
|
+
var _dir3 = dirname7(fileURLToPath3(import.meta.url));
|
|
7400
|
+
var _require2 = createRequire3(import.meta.url);
|
|
7401
|
+
var _pkg = (() => {
|
|
7402
|
+
for (const rel of ["../package.json", "../../package.json"]) {
|
|
7403
|
+
try {
|
|
7404
|
+
return _require2(resolve10(_dir3, rel));
|
|
7405
|
+
} catch {
|
|
7406
|
+
}
|
|
7407
|
+
}
|
|
7408
|
+
return { version: process.env["COPAIR_VERSION"] ?? "0.0.0-dev" };
|
|
7409
|
+
})();
|
|
7197
7410
|
function detectTestFramework(cwd) {
|
|
7198
7411
|
const patterns = [
|
|
7199
7412
|
"vitest.config.ts",
|
|
@@ -7263,8 +7476,13 @@ Continue from where we left off.`
|
|
|
7263
7476
|
);
|
|
7264
7477
|
return true;
|
|
7265
7478
|
}
|
|
7266
|
-
async function
|
|
7267
|
-
const
|
|
7479
|
+
async function bootstrapCLI(options = {}) {
|
|
7480
|
+
const rawArgv = options.argv ?? process.argv;
|
|
7481
|
+
if (rawArgv[2] === "audit") {
|
|
7482
|
+
await runAuditCommand(rawArgv.slice(3));
|
|
7483
|
+
return;
|
|
7484
|
+
}
|
|
7485
|
+
const cliOpts = parseArgs(options.argv);
|
|
7268
7486
|
if (cliOpts.debug) {
|
|
7269
7487
|
logger.setLevel(3 /* DEBUG */);
|
|
7270
7488
|
} else if (cliOpts.verbose) {
|
|
@@ -7288,17 +7506,29 @@ async function main() {
|
|
|
7288
7506
|
config,
|
|
7289
7507
|
cliOpts.model
|
|
7290
7508
|
);
|
|
7509
|
+
const pluginManager = new PluginManager();
|
|
7510
|
+
for (const plugin of options.plugins ?? []) {
|
|
7511
|
+
pluginManager.register(plugin);
|
|
7512
|
+
}
|
|
7513
|
+
await pluginManager.loadFromConfig(config.plugins ?? []);
|
|
7291
7514
|
const providerRegistry = new ProviderRegistry();
|
|
7292
7515
|
providerRegistry.register("openai", createOpenAIProvider);
|
|
7293
7516
|
providerRegistry.register("anthropic", createAnthropicProvider);
|
|
7294
7517
|
providerRegistry.register("google", createGoogleProvider);
|
|
7295
7518
|
providerRegistry.register("openai-compatible", createOpenAICompatibleProvider);
|
|
7296
|
-
const providerType = getProviderType(providerName, providerConfig);
|
|
7297
|
-
const provider = providerRegistry.resolve(providerType, resolveProviderConfig(providerConfig, config.network?.provider_timeout_ms), modelAlias);
|
|
7298
7519
|
const toolRegistry = createDefaultToolRegistry(config);
|
|
7299
7520
|
const allowList = loadAllowList();
|
|
7300
7521
|
const gate = new ApprovalGate(config.permissions.mode, allowList);
|
|
7301
7522
|
const executor = new ToolExecutor(toolRegistry, gate);
|
|
7523
|
+
await pluginManager.initialize({
|
|
7524
|
+
config,
|
|
7525
|
+
providerRegistry,
|
|
7526
|
+
toolRegistry,
|
|
7527
|
+
version: _pkg.version,
|
|
7528
|
+
edition: options.edition ?? "community"
|
|
7529
|
+
});
|
|
7530
|
+
const providerType = getProviderType(providerName, providerConfig);
|
|
7531
|
+
const provider = providerRegistry.resolve(providerType, resolveProviderConfig(providerConfig, config.network?.provider_timeout_ms), modelAlias);
|
|
7302
7532
|
const agentBridge = new AgentBridge();
|
|
7303
7533
|
gate.setBridge(agentBridge);
|
|
7304
7534
|
const mcpManager = new McpClientManager();
|
|
@@ -7329,6 +7559,7 @@ async function main() {
|
|
|
7329
7559
|
setKnowledgeBase(knowledgeBase);
|
|
7330
7560
|
const agent = new Agent(provider, modelAlias, toolRegistry, executor, {
|
|
7331
7561
|
bridge: agentBridge,
|
|
7562
|
+
pluginManager,
|
|
7332
7563
|
systemPrompt: `You are Copair, an AI coding assistant.
|
|
7333
7564
|
|
|
7334
7565
|
Environment:
|
|
@@ -7434,6 +7665,7 @@ Environment:
|
|
|
7434
7665
|
await auditLog.append({ event: "session_end", outcome: "allowed" });
|
|
7435
7666
|
await sessionManager.close(messages, summarizer);
|
|
7436
7667
|
await mcpManager.shutdown();
|
|
7668
|
+
await pluginManager.destroy();
|
|
7437
7669
|
appHandle?.unmount();
|
|
7438
7670
|
console.log("\nGoodbye!");
|
|
7439
7671
|
process.exit(0);
|
|
@@ -7445,8 +7677,6 @@ Environment:
|
|
|
7445
7677
|
completionEngine,
|
|
7446
7678
|
initialContext: {
|
|
7447
7679
|
hasTestFramework: detectTestFramework(cwd),
|
|
7448
|
-
// Session picker already ran before ink — user chose resume or fresh.
|
|
7449
|
-
// No need to re-suggest resuming.
|
|
7450
7680
|
sessionCount: 0
|
|
7451
7681
|
},
|
|
7452
7682
|
onHistoryAppend: (entry) => {
|
|
@@ -7549,16 +7779,10 @@ Environment:
|
|
|
7549
7779
|
}
|
|
7550
7780
|
await appHandle.waitForExit().then(doExit);
|
|
7551
7781
|
}
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
`);
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
} else {
|
|
7559
|
-
main().catch((err) => {
|
|
7560
|
-
console.error(`Error: ${err.message}`);
|
|
7561
|
-
process.exit(1);
|
|
7562
|
-
});
|
|
7563
|
-
}
|
|
7782
|
+
|
|
7783
|
+
// src/index.ts
|
|
7784
|
+
bootstrapCLI({ edition: "community" }).catch((err) => {
|
|
7785
|
+
console.error(`Error: ${err.message}`);
|
|
7786
|
+
process.exit(1);
|
|
7787
|
+
});
|
|
7564
7788
|
//# sourceMappingURL=index.js.map
|