@actant/core 0.2.0 → 0.2.2
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/{skill-md-parser-2HXC4AAW.js → chunk-DYGZP4MW.js} +8 -2
- package/dist/{skill-md-parser-2HXC4AAW.js.map → chunk-DYGZP4MW.js.map} +1 -1
- package/dist/index.d.ts +224 -72
- package/dist/index.js +1431 -293
- package/dist/index.js.map +1 -1
- package/dist/skill-md-parser-HXLTZAUU.js +9 -0
- package/dist/skill-md-parser-HXLTZAUU.js.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseSkillMdContent
|
|
3
|
+
} from "./chunk-DYGZP4MW.js";
|
|
4
|
+
|
|
1
5
|
// src/builder/cursor-builder.ts
|
|
2
6
|
import { writeFile, mkdir, stat } from "fs/promises";
|
|
3
7
|
import { join } from "path";
|
|
@@ -332,9 +336,9 @@ import { createLogger as createLogger3 } from "@actant/shared";
|
|
|
332
336
|
// src/builder/handlers/skills-handler.ts
|
|
333
337
|
var skillsHandler = {
|
|
334
338
|
contextKey: "skills",
|
|
335
|
-
resolve(refs,
|
|
339
|
+
resolve(refs, manager2) {
|
|
336
340
|
if (!refs || !Array.isArray(refs) || refs.length === 0) return [];
|
|
337
|
-
return
|
|
341
|
+
return manager2?.resolve(refs) ?? refs.map((name) => ({ name, content: `- ${name}` }));
|
|
338
342
|
},
|
|
339
343
|
async materialize(workspaceDir, definitions, _backendType, builder) {
|
|
340
344
|
await builder.materializeSkills(workspaceDir, definitions);
|
|
@@ -344,9 +348,9 @@ var skillsHandler = {
|
|
|
344
348
|
// src/builder/handlers/prompts-handler.ts
|
|
345
349
|
var promptsHandler = {
|
|
346
350
|
contextKey: "prompts",
|
|
347
|
-
resolve(refs,
|
|
351
|
+
resolve(refs, manager2) {
|
|
348
352
|
if (!refs || !Array.isArray(refs) || refs.length === 0) return [];
|
|
349
|
-
return
|
|
353
|
+
return manager2?.resolve(refs) ?? refs.map((name) => ({ name, content: `- ${name}` }));
|
|
350
354
|
},
|
|
351
355
|
async materialize(workspaceDir, definitions, _backendType, builder) {
|
|
352
356
|
await builder.materializePrompts(workspaceDir, definitions);
|
|
@@ -359,7 +363,7 @@ function isMcpServerRef(obj) {
|
|
|
359
363
|
}
|
|
360
364
|
var mcpServersHandler = {
|
|
361
365
|
contextKey: "mcpServers",
|
|
362
|
-
resolve(refs,
|
|
366
|
+
resolve(refs, manager2) {
|
|
363
367
|
if (!refs || !Array.isArray(refs) || refs.length === 0) return [];
|
|
364
368
|
const arr = refs;
|
|
365
369
|
if (arr.every(isMcpServerRef)) {
|
|
@@ -370,7 +374,7 @@ var mcpServersHandler = {
|
|
|
370
374
|
env: ref.env
|
|
371
375
|
}));
|
|
372
376
|
}
|
|
373
|
-
return
|
|
377
|
+
return manager2?.resolve(arr) ?? [];
|
|
374
378
|
},
|
|
375
379
|
async materialize(workspaceDir, definitions, _backendType, builder) {
|
|
376
380
|
await builder.materializeMcpConfig(workspaceDir, definitions);
|
|
@@ -380,11 +384,11 @@ var mcpServersHandler = {
|
|
|
380
384
|
// src/builder/handlers/workflow-handler.ts
|
|
381
385
|
var workflowHandler = {
|
|
382
386
|
contextKey: "workflow",
|
|
383
|
-
resolve(refs,
|
|
387
|
+
resolve(refs, manager2) {
|
|
384
388
|
if (refs === void 0 || refs === null) return [];
|
|
385
389
|
const name = typeof refs === "string" ? refs : Array.isArray(refs) && typeof refs[0] === "string" ? refs[0] : void 0;
|
|
386
390
|
if (!name) return [];
|
|
387
|
-
const resolved =
|
|
391
|
+
const resolved = manager2?.resolve([name]);
|
|
388
392
|
if (resolved && resolved.length > 0) return resolved;
|
|
389
393
|
return [
|
|
390
394
|
{
|
|
@@ -407,9 +411,9 @@ var workflowHandler = {
|
|
|
407
411
|
// src/builder/handlers/plugins-handler.ts
|
|
408
412
|
var pluginsHandler = {
|
|
409
413
|
contextKey: "plugins",
|
|
410
|
-
resolve(refs,
|
|
414
|
+
resolve(refs, manager2) {
|
|
411
415
|
if (!refs || !Array.isArray(refs) || refs.length === 0) return [];
|
|
412
|
-
return
|
|
416
|
+
return manager2?.resolve(refs) ?? [];
|
|
413
417
|
},
|
|
414
418
|
async materialize(workspaceDir, definitions, _backendType, builder) {
|
|
415
419
|
await builder.materializePlugins(workspaceDir, definitions);
|
|
@@ -473,8 +477,8 @@ var WorkspaceBuilder = class {
|
|
|
473
477
|
const refs = domainContext[handler.contextKey];
|
|
474
478
|
if (refs === void 0 || refs === null) continue;
|
|
475
479
|
if (Array.isArray(refs) && refs.length === 0) continue;
|
|
476
|
-
const
|
|
477
|
-
const definitions = handler.resolve(refs,
|
|
480
|
+
const manager2 = this.getManager(handler.contextKey);
|
|
481
|
+
const definitions = handler.resolve(refs, manager2);
|
|
478
482
|
if (definitions.length > 0) {
|
|
479
483
|
await handler.materialize(workspaceDir, definitions, backendType, activeBuilder);
|
|
480
484
|
if (handler.contextKey === "mcpServers") {
|
|
@@ -1064,12 +1068,26 @@ var AgentBackendSchema = z2.object({
|
|
|
1064
1068
|
type: z2.enum(["cursor", "cursor-agent", "claude-code", "custom", "pi"]),
|
|
1065
1069
|
config: z2.record(z2.string(), z2.unknown()).optional()
|
|
1066
1070
|
});
|
|
1071
|
+
var ModelApiProtocolEnum = z2.enum(["openai", "anthropic", "custom"]);
|
|
1072
|
+
var DEFAULT_PROTOCOL = {
|
|
1073
|
+
anthropic: "anthropic",
|
|
1074
|
+
openai: "openai",
|
|
1075
|
+
deepseek: "openai",
|
|
1076
|
+
ollama: "openai",
|
|
1077
|
+
azure: "openai",
|
|
1078
|
+
bedrock: "anthropic",
|
|
1079
|
+
vertex: "anthropic",
|
|
1080
|
+
custom: "custom"
|
|
1081
|
+
};
|
|
1067
1082
|
var ModelProviderSchema = z2.object({
|
|
1068
|
-
type: z2.
|
|
1069
|
-
protocol:
|
|
1083
|
+
type: z2.string().min(1),
|
|
1084
|
+
protocol: ModelApiProtocolEnum.optional(),
|
|
1070
1085
|
baseUrl: z2.string().optional(),
|
|
1071
1086
|
config: z2.record(z2.string(), z2.unknown()).optional()
|
|
1072
|
-
})
|
|
1087
|
+
}).transform((val) => ({
|
|
1088
|
+
...val,
|
|
1089
|
+
protocol: val.protocol ?? DEFAULT_PROTOCOL[val.type] ?? "custom"
|
|
1090
|
+
}));
|
|
1073
1091
|
var InitializerStepSchema = z2.object({
|
|
1074
1092
|
type: z2.string().min(1),
|
|
1075
1093
|
config: z2.record(z2.string(), z2.unknown()).optional()
|
|
@@ -1127,7 +1145,7 @@ var AgentTemplateSchema = z2.object({
|
|
|
1127
1145
|
origin: ComponentOriginSchema.optional(),
|
|
1128
1146
|
tags: z2.array(z2.string()).optional(),
|
|
1129
1147
|
backend: AgentBackendSchema,
|
|
1130
|
-
provider: ModelProviderSchema,
|
|
1148
|
+
provider: ModelProviderSchema.optional(),
|
|
1131
1149
|
domainContext: DomainContextSchema,
|
|
1132
1150
|
permissions: PermissionsInputSchema.optional(),
|
|
1133
1151
|
initializer: InitializerSchema.optional(),
|
|
@@ -1255,6 +1273,58 @@ function isNodeError(err) {
|
|
|
1255
1273
|
return err instanceof Error && "code" in err;
|
|
1256
1274
|
}
|
|
1257
1275
|
|
|
1276
|
+
// src/provider/model-provider-registry.ts
|
|
1277
|
+
import { createLogger as createLogger11 } from "@actant/shared";
|
|
1278
|
+
var logger11 = createLogger11("model-provider-registry");
|
|
1279
|
+
var ModelProviderRegistry = class {
|
|
1280
|
+
descriptors = /* @__PURE__ */ new Map();
|
|
1281
|
+
defaultType;
|
|
1282
|
+
register(descriptor) {
|
|
1283
|
+
this.descriptors.set(descriptor.type, descriptor);
|
|
1284
|
+
logger11.debug({ type: descriptor.type }, "Provider registered");
|
|
1285
|
+
}
|
|
1286
|
+
get(type) {
|
|
1287
|
+
return this.descriptors.get(type);
|
|
1288
|
+
}
|
|
1289
|
+
getOrThrow(type) {
|
|
1290
|
+
const desc = this.descriptors.get(type);
|
|
1291
|
+
if (!desc) {
|
|
1292
|
+
throw new Error(
|
|
1293
|
+
`Provider "${type}" is not registered. Available providers: [${[...this.descriptors.keys()].join(", ")}].`
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
return desc;
|
|
1297
|
+
}
|
|
1298
|
+
has(type) {
|
|
1299
|
+
return this.descriptors.has(type);
|
|
1300
|
+
}
|
|
1301
|
+
list() {
|
|
1302
|
+
return [...this.descriptors.values()];
|
|
1303
|
+
}
|
|
1304
|
+
setDefault(type) {
|
|
1305
|
+
if (!this.descriptors.has(type)) {
|
|
1306
|
+
throw new Error(
|
|
1307
|
+
`Cannot set default: provider "${type}" is not registered.`
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
this.defaultType = type;
|
|
1311
|
+
logger11.info({ type }, "Default provider set");
|
|
1312
|
+
}
|
|
1313
|
+
getDefault() {
|
|
1314
|
+
if (!this.defaultType) return void 0;
|
|
1315
|
+
return this.descriptors.get(this.defaultType);
|
|
1316
|
+
}
|
|
1317
|
+
getDefaultType() {
|
|
1318
|
+
return this.defaultType;
|
|
1319
|
+
}
|
|
1320
|
+
/** @internal Test-only: clear all registrations. */
|
|
1321
|
+
_reset() {
|
|
1322
|
+
this.descriptors.clear();
|
|
1323
|
+
this.defaultType = void 0;
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
var modelProviderRegistry = new ModelProviderRegistry();
|
|
1327
|
+
|
|
1258
1328
|
// src/template/schema/config-validators.ts
|
|
1259
1329
|
function zodToIssues(zodError) {
|
|
1260
1330
|
return zodError.issues.map((i) => ({
|
|
@@ -1278,7 +1348,15 @@ function validateProviderConfig(data) {
|
|
|
1278
1348
|
if (!result.success) {
|
|
1279
1349
|
return { valid: false, errors: zodToIssues(result.error), warnings: [] };
|
|
1280
1350
|
}
|
|
1281
|
-
|
|
1351
|
+
const warnings = [];
|
|
1352
|
+
if (!modelProviderRegistry.has(result.data.type)) {
|
|
1353
|
+
warnings.push(warning(
|
|
1354
|
+
"provider.type",
|
|
1355
|
+
`Provider type "${result.data.type}" is not registered; it will be treated as a custom provider`,
|
|
1356
|
+
"UNKNOWN_PROVIDER_TYPE"
|
|
1357
|
+
));
|
|
1358
|
+
}
|
|
1359
|
+
return { valid: true, data: result.data, errors: [], warnings };
|
|
1282
1360
|
}
|
|
1283
1361
|
function validatePermissionsConfig(data) {
|
|
1284
1362
|
const result = PermissionsInputSchema.safeParse(data);
|
|
@@ -1362,12 +1440,21 @@ function validateTemplate(data) {
|
|
|
1362
1440
|
"CUSTOM_BACKEND_NO_CONFIG"
|
|
1363
1441
|
));
|
|
1364
1442
|
}
|
|
1365
|
-
if (template.provider
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1443
|
+
if (template.provider) {
|
|
1444
|
+
if (template.provider.type === "custom" && !template.provider.config) {
|
|
1445
|
+
warnings.push(warning(
|
|
1446
|
+
"provider.config",
|
|
1447
|
+
"Custom provider type without config; model routing may fail",
|
|
1448
|
+
"CUSTOM_PROVIDER_NO_CONFIG"
|
|
1449
|
+
));
|
|
1450
|
+
}
|
|
1451
|
+
if (!modelProviderRegistry.has(template.provider.type)) {
|
|
1452
|
+
warnings.push(warning(
|
|
1453
|
+
"provider.type",
|
|
1454
|
+
`Provider type "${template.provider.type}" is not registered; it will be treated as a custom provider`,
|
|
1455
|
+
"UNKNOWN_PROVIDER_TYPE"
|
|
1456
|
+
));
|
|
1457
|
+
}
|
|
1371
1458
|
}
|
|
1372
1459
|
return { valid: true, data: template, errors: [], warnings };
|
|
1373
1460
|
}
|
|
@@ -1380,13 +1467,13 @@ import { TemplateNotFoundError, ConfigValidationError as ConfigValidationError3
|
|
|
1380
1467
|
// src/domain/base-component-manager.ts
|
|
1381
1468
|
import { readFile as readFile2, writeFile as writeFile4, readdir as readdir2, stat as stat4, unlink, mkdir as mkdir4 } from "fs/promises";
|
|
1382
1469
|
import { join as join5, extname as extname2, resolve } from "path";
|
|
1383
|
-
import { ComponentReferenceError, ConfigNotFoundError as ConfigNotFoundError2, ConfigValidationError as ConfigValidationError2, createLogger as
|
|
1470
|
+
import { ComponentReferenceError, ConfigNotFoundError as ConfigNotFoundError2, ConfigValidationError as ConfigValidationError2, createLogger as createLogger12 } from "@actant/shared";
|
|
1384
1471
|
var BaseComponentManager = class {
|
|
1385
1472
|
components = /* @__PURE__ */ new Map();
|
|
1386
1473
|
logger;
|
|
1387
1474
|
persistDir;
|
|
1388
1475
|
constructor(loggerName) {
|
|
1389
|
-
this.logger =
|
|
1476
|
+
this.logger = createLogger12(loggerName);
|
|
1390
1477
|
}
|
|
1391
1478
|
setPersistDir(dir) {
|
|
1392
1479
|
this.persistDir = dir;
|
|
@@ -1719,13 +1806,13 @@ var TemplateRegistry = class extends BaseComponentManager {
|
|
|
1719
1806
|
import { watch } from "fs";
|
|
1720
1807
|
import { join as join7 } from "path";
|
|
1721
1808
|
import { access } from "fs/promises";
|
|
1722
|
-
import { createLogger as
|
|
1723
|
-
var
|
|
1809
|
+
import { createLogger as createLogger13 } from "@actant/shared";
|
|
1810
|
+
var logger12 = createLogger13("template-file-watcher");
|
|
1724
1811
|
var DEFAULT_DEBOUNCE_MS = 300;
|
|
1725
1812
|
var TemplateFileWatcher = class {
|
|
1726
|
-
constructor(templatesDir,
|
|
1813
|
+
constructor(templatesDir, registry2, options) {
|
|
1727
1814
|
this.templatesDir = templatesDir;
|
|
1728
|
-
this.registry =
|
|
1815
|
+
this.registry = registry2;
|
|
1729
1816
|
this.debounceMs = options?.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
1730
1817
|
}
|
|
1731
1818
|
watcher = null;
|
|
@@ -1742,12 +1829,12 @@ var TemplateFileWatcher = class {
|
|
|
1742
1829
|
this.handleChange(filename);
|
|
1743
1830
|
});
|
|
1744
1831
|
this.watcher.on("error", (err) => {
|
|
1745
|
-
|
|
1832
|
+
logger12.error({ error: err }, "File watcher error");
|
|
1746
1833
|
});
|
|
1747
1834
|
this.buildFileMap();
|
|
1748
|
-
|
|
1835
|
+
logger12.info({ dir: this.templatesDir }, "Template file watcher started");
|
|
1749
1836
|
} catch (err) {
|
|
1750
|
-
|
|
1837
|
+
logger12.error({ error: err, dir: this.templatesDir }, "Failed to start file watcher");
|
|
1751
1838
|
}
|
|
1752
1839
|
}
|
|
1753
1840
|
stop() {
|
|
@@ -1758,7 +1845,7 @@ var TemplateFileWatcher = class {
|
|
|
1758
1845
|
clearTimeout(timer);
|
|
1759
1846
|
}
|
|
1760
1847
|
this.debounceTimers.clear();
|
|
1761
|
-
|
|
1848
|
+
logger12.info("Template file watcher stopped");
|
|
1762
1849
|
}
|
|
1763
1850
|
get isWatching() {
|
|
1764
1851
|
return this.watcher !== null;
|
|
@@ -1777,7 +1864,7 @@ var TemplateFileWatcher = class {
|
|
|
1777
1864
|
setTimeout(() => {
|
|
1778
1865
|
this.debounceTimers.delete(filename);
|
|
1779
1866
|
this.processChange(filename).catch((err) => {
|
|
1780
|
-
|
|
1867
|
+
logger12.error({ filename, error: err }, "Error processing template file change");
|
|
1781
1868
|
});
|
|
1782
1869
|
}, this.debounceMs)
|
|
1783
1870
|
);
|
|
@@ -1795,7 +1882,7 @@ var TemplateFileWatcher = class {
|
|
|
1795
1882
|
if (previousName && this.registry.has(previousName)) {
|
|
1796
1883
|
this.registry.unregister(previousName);
|
|
1797
1884
|
this.fileToName.delete(filename);
|
|
1798
|
-
|
|
1885
|
+
logger12.info({ templateName: previousName, filename }, "Template unregistered (file deleted)");
|
|
1799
1886
|
}
|
|
1800
1887
|
return;
|
|
1801
1888
|
}
|
|
@@ -1810,9 +1897,9 @@ var TemplateFileWatcher = class {
|
|
|
1810
1897
|
}
|
|
1811
1898
|
this.registry.register(template);
|
|
1812
1899
|
this.fileToName.set(filename, template.name);
|
|
1813
|
-
|
|
1900
|
+
logger12.info({ templateName: template.name, filename }, previousName ? "Template reloaded" : "New template registered");
|
|
1814
1901
|
} catch (err) {
|
|
1815
|
-
|
|
1902
|
+
logger12.warn({ filePath, error: err }, "Failed to reload template");
|
|
1816
1903
|
}
|
|
1817
1904
|
}
|
|
1818
1905
|
};
|
|
@@ -1826,7 +1913,7 @@ import {
|
|
|
1826
1913
|
ConfigValidationError as ConfigValidationError4,
|
|
1827
1914
|
InstanceCorruptedError as InstanceCorruptedError3,
|
|
1828
1915
|
WorkspaceInitError,
|
|
1829
|
-
createLogger as
|
|
1916
|
+
createLogger as createLogger16
|
|
1830
1917
|
} from "@actant/shared";
|
|
1831
1918
|
|
|
1832
1919
|
// src/state/instance-meta-schema.ts
|
|
@@ -1848,7 +1935,7 @@ var LaunchModeSchema = z3.enum([
|
|
|
1848
1935
|
]);
|
|
1849
1936
|
var ProcessOwnershipSchema = z3.enum(["managed", "external"]);
|
|
1850
1937
|
var WorkspacePolicySchema = z3.enum(["persistent", "ephemeral"]);
|
|
1851
|
-
var AgentBackendTypeSchema = z3.
|
|
1938
|
+
var AgentBackendTypeSchema = z3.string().min(1);
|
|
1852
1939
|
var PermissionModeSchema2 = z3.enum([
|
|
1853
1940
|
"default",
|
|
1854
1941
|
"acceptEdits",
|
|
@@ -1873,6 +1960,13 @@ var PermissionsConfigSchema = z3.object({
|
|
|
1873
1960
|
sandbox: SandboxConfigSchema.optional(),
|
|
1874
1961
|
additionalDirectories: z3.array(z3.string()).optional()
|
|
1875
1962
|
});
|
|
1963
|
+
var ModelApiProtocolSchema = z3.enum(["openai", "anthropic", "custom"]);
|
|
1964
|
+
var ModelProviderConfigSchema = z3.object({
|
|
1965
|
+
type: z3.string().min(1),
|
|
1966
|
+
protocol: ModelApiProtocolSchema.optional().default("custom"),
|
|
1967
|
+
baseUrl: z3.string().optional(),
|
|
1968
|
+
config: z3.record(z3.string(), z3.unknown()).optional()
|
|
1969
|
+
});
|
|
1876
1970
|
var AgentInstanceMetaSchema = z3.object({
|
|
1877
1971
|
id: z3.string().min(1),
|
|
1878
1972
|
name: z3.string().min(1),
|
|
@@ -1880,6 +1974,7 @@ var AgentInstanceMetaSchema = z3.object({
|
|
|
1880
1974
|
templateVersion: z3.string().regex(/^\d+\.\d+\.\d+$/),
|
|
1881
1975
|
backendType: AgentBackendTypeSchema.default("cursor"),
|
|
1882
1976
|
backendConfig: z3.record(z3.string(), z3.unknown()).optional(),
|
|
1977
|
+
providerConfig: ModelProviderConfigSchema.optional(),
|
|
1883
1978
|
status: AgentStatusSchema,
|
|
1884
1979
|
launchMode: LaunchModeSchema,
|
|
1885
1980
|
workspacePolicy: WorkspacePolicySchema.default("persistent"),
|
|
@@ -1896,8 +1991,8 @@ import { readFile as readFile3, writeFile as writeFile5, rename, readdir as read
|
|
|
1896
1991
|
import { join as join8, dirname } from "path";
|
|
1897
1992
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
1898
1993
|
import { InstanceCorruptedError } from "@actant/shared";
|
|
1899
|
-
import { createLogger as
|
|
1900
|
-
var
|
|
1994
|
+
import { createLogger as createLogger14 } from "@actant/shared";
|
|
1995
|
+
var logger13 = createLogger14("instance-meta-io");
|
|
1901
1996
|
var META_FILENAME = ".actant.json";
|
|
1902
1997
|
function metaFilePath(workspaceDir) {
|
|
1903
1998
|
return join8(workspaceDir, META_FILENAME);
|
|
@@ -1949,12 +2044,12 @@ async function updateInstanceMeta(workspaceDir, patch) {
|
|
|
1949
2044
|
await writeInstanceMeta(workspaceDir, updated);
|
|
1950
2045
|
return updated;
|
|
1951
2046
|
}
|
|
1952
|
-
async function scanInstances(instancesBaseDir,
|
|
2047
|
+
async function scanInstances(instancesBaseDir, registry2) {
|
|
1953
2048
|
const valid = [];
|
|
1954
2049
|
const corrupted = [];
|
|
1955
2050
|
const validNames = /* @__PURE__ */ new Set();
|
|
1956
|
-
if (
|
|
1957
|
-
for (const entry of
|
|
2051
|
+
if (registry2) {
|
|
2052
|
+
for (const entry of registry2.list()) {
|
|
1958
2053
|
if (entry.status === "orphaned") continue;
|
|
1959
2054
|
try {
|
|
1960
2055
|
const st = await stat5(entry.workspacePath);
|
|
@@ -1966,7 +2061,7 @@ async function scanInstances(instancesBaseDir, registry3) {
|
|
|
1966
2061
|
valid.push(meta);
|
|
1967
2062
|
validNames.add(entry.name);
|
|
1968
2063
|
} catch (err) {
|
|
1969
|
-
|
|
2064
|
+
logger13.warn({ name: entry.name, path: entry.workspacePath, error: err }, "Registry entry unreachable or corrupted");
|
|
1970
2065
|
corrupted.push(entry.name);
|
|
1971
2066
|
}
|
|
1972
2067
|
}
|
|
@@ -1991,7 +2086,7 @@ async function scanInstances(instancesBaseDir, registry3) {
|
|
|
1991
2086
|
valid.push(meta);
|
|
1992
2087
|
validNames.add(meta.name);
|
|
1993
2088
|
} catch (err) {
|
|
1994
|
-
|
|
2089
|
+
logger13.warn({ dir: entry, error: err }, "Corrupted instance directory");
|
|
1995
2090
|
corrupted.push(entry);
|
|
1996
2091
|
}
|
|
1997
2092
|
}
|
|
@@ -2140,13 +2235,13 @@ function isNodeError4(err) {
|
|
|
2140
2235
|
}
|
|
2141
2236
|
|
|
2142
2237
|
// src/initializer/pipeline/initialization-pipeline.ts
|
|
2143
|
-
import { createLogger as
|
|
2144
|
-
var
|
|
2238
|
+
import { createLogger as createLogger15 } from "@actant/shared";
|
|
2239
|
+
var logger14 = createLogger15("initialization-pipeline");
|
|
2145
2240
|
var DEFAULT_STEP_TIMEOUT_MS = 6e4;
|
|
2146
2241
|
var DEFAULT_TOTAL_TIMEOUT_MS = 3e5;
|
|
2147
2242
|
var InitializationPipeline = class {
|
|
2148
|
-
constructor(
|
|
2149
|
-
this.registry =
|
|
2243
|
+
constructor(registry2, options) {
|
|
2244
|
+
this.registry = registry2;
|
|
2150
2245
|
this.stepTimeoutMs = options?.defaultStepTimeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
2151
2246
|
this.totalTimeoutMs = options?.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS;
|
|
2152
2247
|
this.onProgress = options?.onProgress;
|
|
@@ -2175,14 +2270,14 @@ var InitializationPipeline = class {
|
|
|
2175
2270
|
const errors = [];
|
|
2176
2271
|
const executed = [];
|
|
2177
2272
|
const pipelineStart = Date.now();
|
|
2178
|
-
|
|
2273
|
+
logger14.info({ stepsCount: steps.length }, "Starting initialization pipeline");
|
|
2179
2274
|
for (let i = 0; i < steps.length; i++) {
|
|
2180
2275
|
const step = steps[i];
|
|
2181
2276
|
if (!step) continue;
|
|
2182
2277
|
if (Date.now() - pipelineStart > this.totalTimeoutMs) {
|
|
2183
2278
|
const err = new Error(`Pipeline total timeout exceeded (${this.totalTimeoutMs}ms)`);
|
|
2184
2279
|
errors.push({ stepIndex: i, stepType: step.type, error: err });
|
|
2185
|
-
|
|
2280
|
+
logger14.error({ stepIndex: i, stepType: step.type }, "Pipeline timeout exceeded");
|
|
2186
2281
|
await this.rollback(executed, context, err);
|
|
2187
2282
|
return { success: false, stepsExecuted: i, stepsTotal: steps.length, errors, outputs };
|
|
2188
2283
|
}
|
|
@@ -2194,7 +2289,7 @@ var InitializationPipeline = class {
|
|
|
2194
2289
|
return { success: false, stepsExecuted: i, stepsTotal: steps.length, errors, outputs };
|
|
2195
2290
|
}
|
|
2196
2291
|
this.onProgress?.(i, steps.length, step.type);
|
|
2197
|
-
|
|
2292
|
+
logger14.debug({ stepIndex: i, stepType: step.type }, "Executing step");
|
|
2198
2293
|
try {
|
|
2199
2294
|
const result = await this.executeWithTimeout(executor, context, step.config ?? {});
|
|
2200
2295
|
if (!result.success) {
|
|
@@ -2209,18 +2304,18 @@ var InitializationPipeline = class {
|
|
|
2209
2304
|
outputs.set(`step-${i}-${step.type}`, result.output);
|
|
2210
2305
|
}
|
|
2211
2306
|
if (result.message) {
|
|
2212
|
-
|
|
2307
|
+
logger14.info({ stepIndex: i, stepType: step.type }, result.message);
|
|
2213
2308
|
}
|
|
2214
2309
|
} catch (err) {
|
|
2215
2310
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
2216
2311
|
errors.push({ stepIndex: i, stepType: step.type, error });
|
|
2217
|
-
|
|
2312
|
+
logger14.error({ stepIndex: i, stepType: step.type, error }, "Step execution failed");
|
|
2218
2313
|
executed.push({ index: i, executor, config: step.config ?? {} });
|
|
2219
2314
|
await this.rollback(executed, context, error);
|
|
2220
2315
|
return { success: false, stepsExecuted: i + 1, stepsTotal: steps.length, errors, outputs };
|
|
2221
2316
|
}
|
|
2222
2317
|
}
|
|
2223
|
-
|
|
2318
|
+
logger14.info({ stepsExecuted: steps.length, elapsedMs: Date.now() - pipelineStart }, "Pipeline completed successfully");
|
|
2224
2319
|
return { success: true, stepsExecuted: steps.length, stepsTotal: steps.length, errors, outputs };
|
|
2225
2320
|
}
|
|
2226
2321
|
async executeWithTimeout(executor, context, config) {
|
|
@@ -2242,7 +2337,7 @@ var InitializationPipeline = class {
|
|
|
2242
2337
|
}
|
|
2243
2338
|
async rollback(executed, context, triggerError) {
|
|
2244
2339
|
if (executed.length === 0) return;
|
|
2245
|
-
|
|
2340
|
+
logger14.info({ stepsToRollback: executed.length }, "Rolling back executed steps");
|
|
2246
2341
|
for (let i = executed.length - 1; i >= 0; i--) {
|
|
2247
2342
|
const entry = executed[i];
|
|
2248
2343
|
if (!entry) continue;
|
|
@@ -2250,9 +2345,9 @@ var InitializationPipeline = class {
|
|
|
2250
2345
|
if (!executor.rollback) continue;
|
|
2251
2346
|
try {
|
|
2252
2347
|
await executor.rollback(context, config, triggerError);
|
|
2253
|
-
|
|
2348
|
+
logger14.debug({ stepIndex: index, stepType: executor.type }, "Step rolled back");
|
|
2254
2349
|
} catch (rollbackErr) {
|
|
2255
|
-
|
|
2350
|
+
logger14.warn(
|
|
2256
2351
|
{ stepIndex: index, stepType: executor.type, error: rollbackErr },
|
|
2257
2352
|
"Rollback failed (best-effort)"
|
|
2258
2353
|
);
|
|
@@ -2262,7 +2357,7 @@ var InitializationPipeline = class {
|
|
|
2262
2357
|
};
|
|
2263
2358
|
|
|
2264
2359
|
// src/initializer/agent-initializer.ts
|
|
2265
|
-
var
|
|
2360
|
+
var logger15 = createLogger16("agent-initializer");
|
|
2266
2361
|
var AgentInitializer = class {
|
|
2267
2362
|
constructor(templateRegistry, instancesBaseDir, options) {
|
|
2268
2363
|
this.templateRegistry = templateRegistry;
|
|
@@ -2304,10 +2399,10 @@ var AgentInitializer = class {
|
|
|
2304
2399
|
);
|
|
2305
2400
|
case "overwrite":
|
|
2306
2401
|
await rm(workspaceDir, { recursive: true, force: true });
|
|
2307
|
-
|
|
2402
|
+
logger15.info({ workspaceDir }, "Existing directory removed (overwrite)");
|
|
2308
2403
|
break;
|
|
2309
2404
|
case "append":
|
|
2310
|
-
|
|
2405
|
+
logger15.info({ workspaceDir }, "Appending to existing directory");
|
|
2311
2406
|
break;
|
|
2312
2407
|
}
|
|
2313
2408
|
}
|
|
@@ -2335,7 +2430,7 @@ var AgentInitializer = class {
|
|
|
2335
2430
|
workspaceDir,
|
|
2336
2431
|
instanceMeta: { name, templateName: template.name },
|
|
2337
2432
|
template,
|
|
2338
|
-
logger:
|
|
2433
|
+
logger: logger15,
|
|
2339
2434
|
state: /* @__PURE__ */ new Map()
|
|
2340
2435
|
};
|
|
2341
2436
|
const pipelineResult = await this.pipeline.run(template.initializer.steps, stepContext);
|
|
@@ -2348,6 +2443,7 @@ var AgentInitializer = class {
|
|
|
2348
2443
|
}
|
|
2349
2444
|
}
|
|
2350
2445
|
const effectivePermissions = resolvePermissions(finalPermissions);
|
|
2446
|
+
const resolvedProvider = resolveProviderConfig(template.provider);
|
|
2351
2447
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2352
2448
|
const launchMode = overrides?.launchMode ?? this.options?.defaultLaunchMode ?? "direct";
|
|
2353
2449
|
const defaultPolicy = launchMode === "one-shot" ? "ephemeral" : "persistent";
|
|
@@ -2358,6 +2454,7 @@ var AgentInitializer = class {
|
|
|
2358
2454
|
templateVersion: template.version,
|
|
2359
2455
|
backendType: template.backend.type,
|
|
2360
2456
|
backendConfig: template.backend.config ? { ...template.backend.config } : void 0,
|
|
2457
|
+
providerConfig: resolvedProvider,
|
|
2361
2458
|
status: "created",
|
|
2362
2459
|
launchMode,
|
|
2363
2460
|
workspacePolicy: overrides?.workspacePolicy ?? defaultPolicy,
|
|
@@ -2372,13 +2469,13 @@ var AgentInitializer = class {
|
|
|
2372
2469
|
const linkType = process.platform === "win32" ? "junction" : "dir";
|
|
2373
2470
|
await symlink(workspaceDir, join10(this.instancesBaseDir, name), linkType);
|
|
2374
2471
|
}
|
|
2375
|
-
|
|
2472
|
+
logger15.info({ name, templateName, workspaceDir, customWorkDir: !!customWorkDir }, "Agent instance created");
|
|
2376
2473
|
return meta;
|
|
2377
2474
|
} catch (err) {
|
|
2378
2475
|
if (shouldCleanupOnError) {
|
|
2379
2476
|
await rm(workspaceDir, { recursive: true, force: true }).catch(() => {
|
|
2380
2477
|
});
|
|
2381
|
-
|
|
2478
|
+
logger15.debug({ workspaceDir }, "Cleaned up workspace after failed creation");
|
|
2382
2479
|
}
|
|
2383
2480
|
if (err instanceof ActantError) {
|
|
2384
2481
|
throw err;
|
|
@@ -2397,7 +2494,7 @@ var AgentInitializer = class {
|
|
|
2397
2494
|
if (await dirExists(workspaceDir)) {
|
|
2398
2495
|
try {
|
|
2399
2496
|
const meta2 = await readInstanceMeta(workspaceDir);
|
|
2400
|
-
|
|
2497
|
+
logger15.debug({ name }, "Existing instance found");
|
|
2401
2498
|
return { meta: meta2, created: false };
|
|
2402
2499
|
} catch (err) {
|
|
2403
2500
|
if (err instanceof InstanceCorruptedError3) {
|
|
@@ -2418,7 +2515,7 @@ var AgentInitializer = class {
|
|
|
2418
2515
|
async destroyInstance(name) {
|
|
2419
2516
|
const entryPath = join10(this.instancesBaseDir, name);
|
|
2420
2517
|
if (!await entryExists(entryPath)) {
|
|
2421
|
-
|
|
2518
|
+
logger15.warn({ name }, "Instance directory not found, nothing to destroy");
|
|
2422
2519
|
return;
|
|
2423
2520
|
}
|
|
2424
2521
|
if (await isSymlink(entryPath)) {
|
|
@@ -2428,13 +2525,30 @@ var AgentInitializer = class {
|
|
|
2428
2525
|
} catch {
|
|
2429
2526
|
}
|
|
2430
2527
|
await rm(entryPath, { recursive: true, force: true });
|
|
2431
|
-
|
|
2528
|
+
logger15.info({ name, targetDir }, "Symlinked agent instance unregistered");
|
|
2432
2529
|
} else {
|
|
2433
2530
|
await rm(entryPath, { recursive: true, force: true });
|
|
2434
|
-
|
|
2531
|
+
logger15.info({ name }, "Agent instance destroyed");
|
|
2435
2532
|
}
|
|
2436
2533
|
}
|
|
2437
2534
|
};
|
|
2535
|
+
function resolveProviderConfig(templateProvider) {
|
|
2536
|
+
if (templateProvider) {
|
|
2537
|
+
if (templateProvider.protocol) return templateProvider;
|
|
2538
|
+
const desc = modelProviderRegistry.get(templateProvider.type);
|
|
2539
|
+
return {
|
|
2540
|
+
...templateProvider,
|
|
2541
|
+
protocol: desc?.protocol ?? "custom"
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
const defaultDesc = modelProviderRegistry.getDefault();
|
|
2545
|
+
if (!defaultDesc) return void 0;
|
|
2546
|
+
return {
|
|
2547
|
+
type: defaultDesc.type,
|
|
2548
|
+
protocol: defaultDesc.protocol,
|
|
2549
|
+
baseUrl: defaultDesc.defaultBaseUrl
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2438
2552
|
async function dirExists(path) {
|
|
2439
2553
|
try {
|
|
2440
2554
|
await access2(path);
|
|
@@ -2463,8 +2577,8 @@ async function isSymlink(path) {
|
|
|
2463
2577
|
// src/initializer/context/context-materializer.ts
|
|
2464
2578
|
import { writeFile as writeFile7, mkdir as mkdir9 } from "fs/promises";
|
|
2465
2579
|
import { join as join11 } from "path";
|
|
2466
|
-
import { createLogger as
|
|
2467
|
-
var
|
|
2580
|
+
import { createLogger as createLogger17 } from "@actant/shared";
|
|
2581
|
+
var logger16 = createLogger17("context-materializer");
|
|
2468
2582
|
var BACKEND_CONFIG_DIR = {
|
|
2469
2583
|
cursor: ".cursor",
|
|
2470
2584
|
"cursor-agent": ".cursor",
|
|
@@ -2477,7 +2591,7 @@ var ContextMaterializer = class {
|
|
|
2477
2591
|
this.managers = managers;
|
|
2478
2592
|
}
|
|
2479
2593
|
async materialize(workspaceDir, domainContext, backendType = "cursor") {
|
|
2480
|
-
const configDir = BACKEND_CONFIG_DIR[backendType];
|
|
2594
|
+
const configDir = BACKEND_CONFIG_DIR[backendType] ?? ".cursor";
|
|
2481
2595
|
const tasks = [];
|
|
2482
2596
|
if (domainContext.skills && domainContext.skills.length > 0) {
|
|
2483
2597
|
tasks.push(this.materializeSkills(workspaceDir, domainContext.skills));
|
|
@@ -2495,7 +2609,7 @@ var ContextMaterializer = class {
|
|
|
2495
2609
|
tasks.push(this.materializePrompts(workspaceDir, domainContext.prompts));
|
|
2496
2610
|
}
|
|
2497
2611
|
await Promise.all(tasks);
|
|
2498
|
-
|
|
2612
|
+
logger16.debug({ workspaceDir, backendType, configDir }, "Domain context materialized");
|
|
2499
2613
|
}
|
|
2500
2614
|
async materializeSkills(workspaceDir, skillNames) {
|
|
2501
2615
|
let content;
|
|
@@ -2610,16 +2724,16 @@ var InitializerStepExecutor = class {
|
|
|
2610
2724
|
};
|
|
2611
2725
|
|
|
2612
2726
|
// src/initializer/pipeline/step-registry.ts
|
|
2613
|
-
import { createLogger as
|
|
2614
|
-
var
|
|
2727
|
+
import { createLogger as createLogger18 } from "@actant/shared";
|
|
2728
|
+
var logger17 = createLogger18("step-registry");
|
|
2615
2729
|
var StepRegistry = class {
|
|
2616
2730
|
executors = /* @__PURE__ */ new Map();
|
|
2617
2731
|
register(executor) {
|
|
2618
2732
|
if (this.executors.has(executor.type)) {
|
|
2619
|
-
|
|
2733
|
+
logger17.warn({ type: executor.type }, "Overwriting existing step executor");
|
|
2620
2734
|
}
|
|
2621
2735
|
this.executors.set(executor.type, executor);
|
|
2622
|
-
|
|
2736
|
+
logger17.debug({ type: executor.type }, "Step executor registered");
|
|
2623
2737
|
}
|
|
2624
2738
|
get(type) {
|
|
2625
2739
|
return this.executors.get(type);
|
|
@@ -2878,10 +2992,10 @@ var NpmInstallStep = class extends InitializerStepExecutor {
|
|
|
2878
2992
|
return { valid: issues.length === 0, issues };
|
|
2879
2993
|
}
|
|
2880
2994
|
async execute(context, config) {
|
|
2881
|
-
const { packageManager = "npm", cwd = ".", args = [], registry:
|
|
2995
|
+
const { packageManager = "npm", cwd = ".", args = [], registry: registry2 } = config;
|
|
2882
2996
|
const workDir = join16(context.workspaceDir, cwd);
|
|
2883
2997
|
const cmdArgs = ["install", ...args];
|
|
2884
|
-
if (
|
|
2998
|
+
if (registry2) cmdArgs.push("--registry", registry2);
|
|
2885
2999
|
context.logger.debug({ packageManager, cwd: workDir, args: cmdArgs }, "Installing dependencies");
|
|
2886
3000
|
const result = await runInstall(packageManager, cmdArgs, workDir);
|
|
2887
3001
|
if (result.exitCode !== 0) {
|
|
@@ -2923,80 +3037,294 @@ function runInstall(pm, args, cwd) {
|
|
|
2923
3037
|
|
|
2924
3038
|
// src/initializer/steps/index.ts
|
|
2925
3039
|
function createDefaultStepRegistry() {
|
|
2926
|
-
const
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
return
|
|
3040
|
+
const registry2 = new StepRegistry();
|
|
3041
|
+
registry2.register(new MkdirStep());
|
|
3042
|
+
registry2.register(new ExecStep());
|
|
3043
|
+
registry2.register(new FileCopyStep());
|
|
3044
|
+
registry2.register(new GitCloneStep());
|
|
3045
|
+
registry2.register(new NpmInstallStep());
|
|
3046
|
+
return registry2;
|
|
2933
3047
|
}
|
|
2934
3048
|
|
|
2935
3049
|
// src/manager/agent-manager.ts
|
|
2936
3050
|
import { join as join17 } from "path";
|
|
3051
|
+
import { existsSync } from "fs";
|
|
2937
3052
|
import { rename as rename3, mkdir as mkdir11 } from "fs/promises";
|
|
2938
3053
|
import {
|
|
2939
3054
|
AgentNotFoundError,
|
|
2940
3055
|
AgentAlreadyRunningError,
|
|
2941
3056
|
AgentAlreadyAttachedError,
|
|
2942
3057
|
AgentNotAttachedError,
|
|
2943
|
-
|
|
3058
|
+
AgentLaunchError,
|
|
3059
|
+
createLogger as createLogger24
|
|
2944
3060
|
} from "@actant/shared";
|
|
2945
3061
|
|
|
3062
|
+
// src/domain/backend/backend-manager.ts
|
|
3063
|
+
import { execFile } from "child_process";
|
|
3064
|
+
|
|
3065
|
+
// src/domain/backend/backend-schema.ts
|
|
3066
|
+
import { z as z4 } from "zod/v4";
|
|
3067
|
+
var PlatformCommandSchema = z4.object({
|
|
3068
|
+
win32: z4.string().min(1),
|
|
3069
|
+
default: z4.string().min(1)
|
|
3070
|
+
});
|
|
3071
|
+
var OpenSpawnOptionsSchema = z4.object({
|
|
3072
|
+
stdio: z4.enum(["inherit", "ignore"]).optional(),
|
|
3073
|
+
detached: z4.boolean().optional(),
|
|
3074
|
+
windowsHide: z4.boolean().optional(),
|
|
3075
|
+
shell: z4.boolean().optional()
|
|
3076
|
+
});
|
|
3077
|
+
var ExistenceCheckSchema = z4.object({
|
|
3078
|
+
command: z4.string().min(1),
|
|
3079
|
+
args: z4.array(z4.string()).optional(),
|
|
3080
|
+
expectedExitCode: z4.number().int().optional(),
|
|
3081
|
+
versionPattern: z4.string().optional()
|
|
3082
|
+
});
|
|
3083
|
+
var InstallMethodSchema = z4.object({
|
|
3084
|
+
type: z4.enum(["npm", "brew", "winget", "choco", "url", "manual"]),
|
|
3085
|
+
package: z4.string().optional(),
|
|
3086
|
+
platforms: z4.array(z4.string()).optional(),
|
|
3087
|
+
label: z4.string().optional(),
|
|
3088
|
+
instructions: z4.string().optional()
|
|
3089
|
+
});
|
|
3090
|
+
var BackendDefinitionSchema = z4.object({
|
|
3091
|
+
name: z4.string().min(1),
|
|
3092
|
+
version: z4.string().optional(),
|
|
3093
|
+
description: z4.string().optional(),
|
|
3094
|
+
tags: z4.array(z4.string()).optional(),
|
|
3095
|
+
supportedModes: z4.array(z4.enum(["resolve", "open", "acp"])).min(1),
|
|
3096
|
+
resolveCommand: PlatformCommandSchema.optional(),
|
|
3097
|
+
openCommand: PlatformCommandSchema.optional(),
|
|
3098
|
+
acpCommand: PlatformCommandSchema.optional(),
|
|
3099
|
+
acpOwnsProcess: z4.boolean().optional(),
|
|
3100
|
+
resolvePackage: z4.string().optional(),
|
|
3101
|
+
openWorkspaceDir: z4.enum(["arg", "cwd"]).optional(),
|
|
3102
|
+
openSpawnOptions: OpenSpawnOptionsSchema.optional(),
|
|
3103
|
+
existenceCheck: ExistenceCheckSchema.optional(),
|
|
3104
|
+
install: z4.array(InstallMethodSchema).optional()
|
|
3105
|
+
}).passthrough();
|
|
3106
|
+
|
|
3107
|
+
// src/domain/backend/backend-manager.ts
|
|
3108
|
+
var BackendManager = class extends BaseComponentManager {
|
|
3109
|
+
componentType = "Backend";
|
|
3110
|
+
acpResolvers = /* @__PURE__ */ new Map();
|
|
3111
|
+
constructor() {
|
|
3112
|
+
super("backend-manager");
|
|
3113
|
+
}
|
|
3114
|
+
// ---------------------------------------------------------------------------
|
|
3115
|
+
// ACP resolver (behavioral extension, not serializable)
|
|
3116
|
+
// ---------------------------------------------------------------------------
|
|
3117
|
+
registerAcpResolver(backendName, resolver) {
|
|
3118
|
+
this.acpResolvers.set(backendName, resolver);
|
|
3119
|
+
}
|
|
3120
|
+
getAcpResolver(backendName) {
|
|
3121
|
+
return this.acpResolvers.get(backendName);
|
|
3122
|
+
}
|
|
3123
|
+
// ---------------------------------------------------------------------------
|
|
3124
|
+
// Mode queries
|
|
3125
|
+
// ---------------------------------------------------------------------------
|
|
3126
|
+
supportsMode(backendName, mode) {
|
|
3127
|
+
const def = this.get(backendName);
|
|
3128
|
+
return def != null && def.supportedModes.includes(mode);
|
|
3129
|
+
}
|
|
3130
|
+
requireMode(backendName, mode) {
|
|
3131
|
+
const def = this.get(backendName);
|
|
3132
|
+
if (!def) {
|
|
3133
|
+
throw new Error(
|
|
3134
|
+
`Backend "${backendName}" is not registered. Ensure the backend package is installed and its definition was loaded at startup.`
|
|
3135
|
+
);
|
|
3136
|
+
}
|
|
3137
|
+
if (!def.supportedModes.includes(mode)) {
|
|
3138
|
+
const supported = def.supportedModes.join(", ");
|
|
3139
|
+
throw new Error(
|
|
3140
|
+
`Backend "${backendName}" does not support "${mode}" mode. Supported modes: [${supported}]. ` + (mode === "resolve" ? `Use \`agent start\` or \`agent run\` instead.` : mode === "open" ? `This backend has no native TUI/UI to open.` : `Use \`agent resolve\` or \`agent open\` instead.`)
|
|
3141
|
+
);
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
// ---------------------------------------------------------------------------
|
|
3145
|
+
// Platform command helper
|
|
3146
|
+
// ---------------------------------------------------------------------------
|
|
3147
|
+
getPlatformCommand(cmd) {
|
|
3148
|
+
return process.platform === "win32" ? cmd.win32 : cmd.default;
|
|
3149
|
+
}
|
|
3150
|
+
// ---------------------------------------------------------------------------
|
|
3151
|
+
// Availability check
|
|
3152
|
+
// ---------------------------------------------------------------------------
|
|
3153
|
+
/**
|
|
3154
|
+
* Probe whether a backend binary is available on the system.
|
|
3155
|
+
* Uses the `existenceCheck` field. Returns `{ available, version? }`.
|
|
3156
|
+
* If no `existenceCheck` is configured, returns `{ available: true }` (assume ok).
|
|
3157
|
+
*/
|
|
3158
|
+
async checkAvailability(backendName) {
|
|
3159
|
+
const def = this.get(backendName);
|
|
3160
|
+
if (!def) return { available: false, error: `Backend "${backendName}" not registered` };
|
|
3161
|
+
if (!def.existenceCheck) return { available: true };
|
|
3162
|
+
const { command, args = ["--version"], expectedExitCode = 0, versionPattern } = def.existenceCheck;
|
|
3163
|
+
try {
|
|
3164
|
+
const { stdout, exitCode } = await execCommand(command, args);
|
|
3165
|
+
if (exitCode !== expectedExitCode) {
|
|
3166
|
+
return { available: false, error: `Exit code ${exitCode} (expected ${expectedExitCode})` };
|
|
3167
|
+
}
|
|
3168
|
+
if (versionPattern) {
|
|
3169
|
+
const match = new RegExp(versionPattern).exec(stdout);
|
|
3170
|
+
if (!match) {
|
|
3171
|
+
return { available: false, error: `Version output didn't match pattern: ${versionPattern}` };
|
|
3172
|
+
}
|
|
3173
|
+
return { available: true, version: match[0] };
|
|
3174
|
+
}
|
|
3175
|
+
const version = stdout.trim().split("\n")[0];
|
|
3176
|
+
return { available: true, version: version || void 0 };
|
|
3177
|
+
} catch (err) {
|
|
3178
|
+
return { available: false, error: err.message };
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
/**
|
|
3182
|
+
* Get platform-appropriate install methods for a backend.
|
|
3183
|
+
* Filters by `platforms` field; returns all methods if no platform restriction.
|
|
3184
|
+
*/
|
|
3185
|
+
getInstallMethods(backendName) {
|
|
3186
|
+
const def = this.get(backendName);
|
|
3187
|
+
if (!def?.install) return [];
|
|
3188
|
+
const plat = process.platform;
|
|
3189
|
+
return def.install.filter((m) => !m.platforms || m.platforms.includes(plat));
|
|
3190
|
+
}
|
|
3191
|
+
// ---------------------------------------------------------------------------
|
|
3192
|
+
// Validation
|
|
3193
|
+
// ---------------------------------------------------------------------------
|
|
3194
|
+
validate(data, _source) {
|
|
3195
|
+
const result = BackendDefinitionSchema.safeParse(data);
|
|
3196
|
+
if (!result.success) {
|
|
3197
|
+
return {
|
|
3198
|
+
valid: false,
|
|
3199
|
+
errors: result.error.issues.map((i) => ({
|
|
3200
|
+
path: i.path.map(String).join("."),
|
|
3201
|
+
message: i.message,
|
|
3202
|
+
severity: "error"
|
|
3203
|
+
})),
|
|
3204
|
+
warnings: []
|
|
3205
|
+
};
|
|
3206
|
+
}
|
|
3207
|
+
return { valid: true, data: result.data, errors: [], warnings: [] };
|
|
3208
|
+
}
|
|
3209
|
+
};
|
|
3210
|
+
function execCommand(command, args) {
|
|
3211
|
+
return new Promise((resolve3) => {
|
|
3212
|
+
execFile(command, args, { timeout: 1e4, windowsHide: true }, (err, stdout) => {
|
|
3213
|
+
if (err && typeof err.code === "string" && err.code === "ENOENT") {
|
|
3214
|
+
resolve3({ stdout: "", exitCode: -1 });
|
|
3215
|
+
return;
|
|
3216
|
+
}
|
|
3217
|
+
const exitCode = err && "code" in err && typeof err.code === "number" ? err.code : err ? 1 : 0;
|
|
3218
|
+
resolve3({ stdout: stdout ?? "", exitCode });
|
|
3219
|
+
});
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
|
|
2946
3223
|
// src/manager/launcher/backend-registry.ts
|
|
2947
|
-
var
|
|
3224
|
+
var manager = new BackendManager();
|
|
3225
|
+
function getBackendManager() {
|
|
3226
|
+
return manager;
|
|
3227
|
+
}
|
|
2948
3228
|
function registerBackend(descriptor) {
|
|
2949
|
-
|
|
3229
|
+
const { type, acpResolver, ...rest } = descriptor;
|
|
3230
|
+
const definition = { ...rest, name: type };
|
|
3231
|
+
manager.register(definition);
|
|
3232
|
+
if (acpResolver) {
|
|
3233
|
+
manager.registerAcpResolver(type, acpResolver);
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
function registerBackendDefinition(definition) {
|
|
3237
|
+
manager.register(definition);
|
|
2950
3238
|
}
|
|
2951
3239
|
function getBackendDescriptor(type) {
|
|
2952
|
-
const
|
|
2953
|
-
if (!
|
|
3240
|
+
const def = manager.get(type);
|
|
3241
|
+
if (!def) {
|
|
2954
3242
|
throw new Error(
|
|
2955
3243
|
`Backend "${type}" is not registered. Ensure the backend package is installed and registerBackend() was called at startup.`
|
|
2956
3244
|
);
|
|
2957
3245
|
}
|
|
2958
|
-
return
|
|
3246
|
+
return def;
|
|
2959
3247
|
}
|
|
2960
3248
|
function supportsMode(type, mode) {
|
|
2961
|
-
|
|
2962
|
-
return desc != null && desc.supportedModes.includes(mode);
|
|
3249
|
+
return manager.supportsMode(type, mode);
|
|
2963
3250
|
}
|
|
2964
3251
|
function requireMode(type, mode) {
|
|
2965
|
-
|
|
2966
|
-
if (!desc.supportedModes.includes(mode)) {
|
|
2967
|
-
const supported = desc.supportedModes.join(", ");
|
|
2968
|
-
throw new Error(
|
|
2969
|
-
`Backend "${type}" does not support "${mode}" mode. Supported modes: [${supported}]. ` + (mode === "resolve" ? `Use \`agent start\` or \`agent run\` instead.` : mode === "open" ? `This backend has no native TUI/UI to open.` : `Use \`agent resolve\` or \`agent open\` instead.`)
|
|
2970
|
-
);
|
|
2971
|
-
}
|
|
3252
|
+
manager.requireMode(type, mode);
|
|
2972
3253
|
}
|
|
2973
3254
|
function getPlatformCommand(cmd) {
|
|
2974
|
-
return
|
|
3255
|
+
return manager.getPlatformCommand(cmd);
|
|
3256
|
+
}
|
|
3257
|
+
function getInstallHint(type) {
|
|
3258
|
+
const methods = manager.getInstallMethods(type);
|
|
3259
|
+
const first = methods[0];
|
|
3260
|
+
return first?.label ?? first?.instructions;
|
|
3261
|
+
}
|
|
3262
|
+
function getAcpResolver(type) {
|
|
3263
|
+
return manager.getAcpResolver(type);
|
|
2975
3264
|
}
|
|
2976
3265
|
|
|
2977
3266
|
// src/manager/launcher/builtin-backends.ts
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
3267
|
+
var BUILTIN_BACKENDS = [
|
|
3268
|
+
{
|
|
3269
|
+
name: "cursor",
|
|
3270
|
+
version: "1.0.0",
|
|
3271
|
+
description: "Cursor IDE",
|
|
3272
|
+
origin: { type: "builtin" },
|
|
2981
3273
|
supportedModes: ["resolve", "open"],
|
|
2982
3274
|
resolveCommand: { win32: "cursor.cmd", default: "cursor" },
|
|
2983
|
-
openCommand: { win32: "cursor.cmd", default: "cursor" }
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
3275
|
+
openCommand: { win32: "cursor.cmd", default: "cursor" },
|
|
3276
|
+
openSpawnOptions: { shell: process.platform === "win32" },
|
|
3277
|
+
existenceCheck: { command: "cursor", args: ["--version"] },
|
|
3278
|
+
install: [
|
|
3279
|
+
{ type: "url", package: "https://cursor.com", label: "Download Cursor", platforms: ["win32", "darwin", "linux"] },
|
|
3280
|
+
{ type: "brew", package: "cursor", label: "brew install --cask cursor", platforms: ["darwin"] },
|
|
3281
|
+
{ type: "winget", package: "Anysphere.Cursor", label: "winget install Anysphere.Cursor", platforms: ["win32"] }
|
|
3282
|
+
]
|
|
3283
|
+
},
|
|
3284
|
+
{
|
|
3285
|
+
name: "cursor-agent",
|
|
3286
|
+
version: "1.0.0",
|
|
3287
|
+
description: "Cursor Agent (TUI)",
|
|
3288
|
+
origin: { type: "builtin" },
|
|
2987
3289
|
supportedModes: ["resolve", "open", "acp"],
|
|
2988
|
-
resolveCommand: { win32: "
|
|
2989
|
-
openCommand: { win32: "
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
3290
|
+
resolveCommand: { win32: "agent", default: "agent" },
|
|
3291
|
+
openCommand: { win32: "agent", default: "agent" },
|
|
3292
|
+
openWorkspaceDir: "cwd",
|
|
3293
|
+
openSpawnOptions: { stdio: "inherit", detached: false, windowsHide: false },
|
|
3294
|
+
existenceCheck: { command: "agent", args: ["--version"] },
|
|
3295
|
+
install: [
|
|
3296
|
+
{ type: "manual", label: "Included with Cursor", instructions: "Install Cursor from https://cursor.com \u2014 the `agent` CLI is bundled." }
|
|
3297
|
+
]
|
|
3298
|
+
},
|
|
3299
|
+
{
|
|
3300
|
+
name: "claude-code",
|
|
3301
|
+
version: "1.0.0",
|
|
3302
|
+
description: "Claude Code CLI",
|
|
3303
|
+
origin: { type: "builtin" },
|
|
3304
|
+
supportedModes: ["resolve", "open", "acp"],
|
|
3305
|
+
resolveCommand: { win32: "claude-agent-acp.cmd", default: "claude-agent-acp" },
|
|
3306
|
+
openCommand: { win32: "claude.exe", default: "claude" },
|
|
3307
|
+
openWorkspaceDir: "cwd",
|
|
3308
|
+
openSpawnOptions: { stdio: "inherit", detached: false, windowsHide: false },
|
|
3309
|
+
resolvePackage: "@zed-industries/claude-agent-acp",
|
|
3310
|
+
existenceCheck: { command: "claude", args: ["--version"] },
|
|
3311
|
+
install: [
|
|
3312
|
+
{ type: "npm", package: "@anthropic-ai/claude-code", label: "npm install -g @anthropic-ai/claude-code" }
|
|
3313
|
+
]
|
|
3314
|
+
},
|
|
3315
|
+
{
|
|
3316
|
+
name: "custom",
|
|
3317
|
+
version: "1.0.0",
|
|
3318
|
+
description: "Custom backend (user-provided executable)",
|
|
3319
|
+
origin: { type: "builtin" },
|
|
2998
3320
|
supportedModes: ["resolve"]
|
|
2999
|
-
}
|
|
3321
|
+
}
|
|
3322
|
+
];
|
|
3323
|
+
function registerBuiltinBackends() {
|
|
3324
|
+
const mgr = getBackendManager();
|
|
3325
|
+
for (const def of BUILTIN_BACKENDS) {
|
|
3326
|
+
mgr.register(def);
|
|
3327
|
+
}
|
|
3000
3328
|
}
|
|
3001
3329
|
registerBuiltinBackends();
|
|
3002
3330
|
|
|
@@ -3032,25 +3360,36 @@ function resolveBackend(backendType, workspaceDir, backendConfig) {
|
|
|
3032
3360
|
})();
|
|
3033
3361
|
return {
|
|
3034
3362
|
command,
|
|
3035
|
-
args: buildArgs(backendType, workspaceDir, backendConfig)
|
|
3363
|
+
args: buildArgs(backendType, workspaceDir, backendConfig),
|
|
3364
|
+
resolvePackage: desc.resolvePackage
|
|
3036
3365
|
};
|
|
3037
3366
|
}
|
|
3367
|
+
var DEFAULT_OPEN_SPAWN = {
|
|
3368
|
+
stdio: "ignore",
|
|
3369
|
+
detached: true,
|
|
3370
|
+
windowsHide: true,
|
|
3371
|
+
shell: false
|
|
3372
|
+
};
|
|
3038
3373
|
function openBackend(backendType, workspaceDir) {
|
|
3039
3374
|
requireMode(backendType, "open");
|
|
3040
3375
|
const desc = getBackendDescriptor(backendType);
|
|
3041
3376
|
if (!desc.openCommand) {
|
|
3042
3377
|
throw new Error(`Backend "${backendType}" has no openCommand configured.`);
|
|
3043
3378
|
}
|
|
3379
|
+
const useCwd = desc.openWorkspaceDir === "cwd";
|
|
3044
3380
|
return {
|
|
3045
3381
|
command: getPlatformCommand(desc.openCommand),
|
|
3046
|
-
args: [workspaceDir]
|
|
3382
|
+
args: useCwd ? [] : [workspaceDir],
|
|
3383
|
+
cwd: useCwd ? workspaceDir : void 0,
|
|
3384
|
+
openSpawnOptions: { ...DEFAULT_OPEN_SPAWN, ...desc.openSpawnOptions }
|
|
3047
3385
|
};
|
|
3048
3386
|
}
|
|
3049
3387
|
function resolveAcpBackend(backendType, workspaceDir, backendConfig) {
|
|
3050
3388
|
requireMode(backendType, "acp");
|
|
3051
3389
|
const desc = getBackendDescriptor(backendType);
|
|
3052
|
-
|
|
3053
|
-
|
|
3390
|
+
const resolver = getAcpResolver(backendType);
|
|
3391
|
+
if (resolver) {
|
|
3392
|
+
return resolver(workspaceDir, backendConfig);
|
|
3054
3393
|
}
|
|
3055
3394
|
const explicitPath = backendConfig?.executablePath;
|
|
3056
3395
|
const commandSource = desc.acpCommand ?? desc.resolveCommand;
|
|
@@ -3059,12 +3398,13 @@ function resolveAcpBackend(backendType, workspaceDir, backendConfig) {
|
|
|
3059
3398
|
})();
|
|
3060
3399
|
return {
|
|
3061
3400
|
command,
|
|
3062
|
-
args: buildArgs(backendType, workspaceDir, backendConfig)
|
|
3401
|
+
args: buildArgs(backendType, workspaceDir, backendConfig),
|
|
3402
|
+
resolvePackage: desc.resolvePackage
|
|
3063
3403
|
};
|
|
3064
3404
|
}
|
|
3065
3405
|
|
|
3066
3406
|
// src/manager/launcher/process-watcher.ts
|
|
3067
|
-
import { createLogger as
|
|
3407
|
+
import { createLogger as createLogger19 } from "@actant/shared";
|
|
3068
3408
|
|
|
3069
3409
|
// src/manager/launcher/process-utils.ts
|
|
3070
3410
|
function isProcessAlive(pid) {
|
|
@@ -3100,7 +3440,7 @@ function isNodeError5(err) {
|
|
|
3100
3440
|
}
|
|
3101
3441
|
|
|
3102
3442
|
// src/manager/launcher/process-watcher.ts
|
|
3103
|
-
var
|
|
3443
|
+
var logger18 = createLogger19("process-watcher");
|
|
3104
3444
|
var DEFAULT_POLL_INTERVAL = 5e3;
|
|
3105
3445
|
var ProcessWatcher = class {
|
|
3106
3446
|
constructor(onProcessExit, options) {
|
|
@@ -3113,12 +3453,12 @@ var ProcessWatcher = class {
|
|
|
3113
3453
|
pollIntervalMs;
|
|
3114
3454
|
watch(instanceName, pid) {
|
|
3115
3455
|
this.watches.set(instanceName, { pid });
|
|
3116
|
-
|
|
3456
|
+
logger18.debug({ instanceName, pid }, "Watching process");
|
|
3117
3457
|
}
|
|
3118
3458
|
unwatch(instanceName) {
|
|
3119
3459
|
const removed = this.watches.delete(instanceName);
|
|
3120
3460
|
if (removed) {
|
|
3121
|
-
|
|
3461
|
+
logger18.debug({ instanceName }, "Unwatched process");
|
|
3122
3462
|
}
|
|
3123
3463
|
return removed;
|
|
3124
3464
|
}
|
|
@@ -3133,13 +3473,13 @@ var ProcessWatcher = class {
|
|
|
3133
3473
|
this.timer = setInterval(() => {
|
|
3134
3474
|
void this.poll();
|
|
3135
3475
|
}, this.pollIntervalMs);
|
|
3136
|
-
|
|
3476
|
+
logger18.info({ pollIntervalMs: this.pollIntervalMs }, "ProcessWatcher started");
|
|
3137
3477
|
}
|
|
3138
3478
|
stop() {
|
|
3139
3479
|
if (this.timer) {
|
|
3140
3480
|
clearInterval(this.timer);
|
|
3141
3481
|
this.timer = null;
|
|
3142
|
-
|
|
3482
|
+
logger18.info("ProcessWatcher stopped");
|
|
3143
3483
|
}
|
|
3144
3484
|
}
|
|
3145
3485
|
dispose() {
|
|
@@ -3161,11 +3501,11 @@ var ProcessWatcher = class {
|
|
|
3161
3501
|
}
|
|
3162
3502
|
for (const info of exited) {
|
|
3163
3503
|
this.watches.delete(info.instanceName);
|
|
3164
|
-
|
|
3504
|
+
logger18.info(info, "Process exited \u2014 removed from watch list");
|
|
3165
3505
|
try {
|
|
3166
3506
|
await this.onProcessExit(info);
|
|
3167
3507
|
} catch (err) {
|
|
3168
|
-
|
|
3508
|
+
logger18.error({ ...info, error: err }, "Error in process exit handler");
|
|
3169
3509
|
}
|
|
3170
3510
|
}
|
|
3171
3511
|
} finally {
|
|
@@ -3175,8 +3515,8 @@ var ProcessWatcher = class {
|
|
|
3175
3515
|
};
|
|
3176
3516
|
|
|
3177
3517
|
// src/manager/launch-mode-handler.ts
|
|
3178
|
-
import { createLogger as
|
|
3179
|
-
var
|
|
3518
|
+
import { createLogger as createLogger20 } from "@actant/shared";
|
|
3519
|
+
var logger19 = createLogger20("launch-mode-handler");
|
|
3180
3520
|
var DirectModeHandler = class {
|
|
3181
3521
|
mode = "direct";
|
|
3182
3522
|
getProcessExitAction(_instanceName) {
|
|
@@ -3198,11 +3538,11 @@ var AcpBackgroundModeHandler = class {
|
|
|
3198
3538
|
var AcpServiceModeHandler = class {
|
|
3199
3539
|
mode = "acp-service";
|
|
3200
3540
|
getProcessExitAction(instanceName) {
|
|
3201
|
-
|
|
3541
|
+
logger19.info({ instanceName }, "acp-service process exited \u2014 restart policy will be checked");
|
|
3202
3542
|
return { type: "restart" };
|
|
3203
3543
|
}
|
|
3204
3544
|
getRecoveryAction(instanceName) {
|
|
3205
|
-
|
|
3545
|
+
logger19.info({ instanceName }, "acp-service stale instance \u2014 will attempt recovery restart");
|
|
3206
3546
|
return { type: "restart" };
|
|
3207
3547
|
}
|
|
3208
3548
|
};
|
|
@@ -3229,8 +3569,8 @@ function getLaunchModeHandler(mode) {
|
|
|
3229
3569
|
}
|
|
3230
3570
|
|
|
3231
3571
|
// src/manager/restart-tracker.ts
|
|
3232
|
-
import { createLogger as
|
|
3233
|
-
var
|
|
3572
|
+
import { createLogger as createLogger21 } from "@actant/shared";
|
|
3573
|
+
var logger20 = createLogger21("restart-tracker");
|
|
3234
3574
|
var DEFAULT_RESTART_POLICY = {
|
|
3235
3575
|
maxRestarts: 5,
|
|
3236
3576
|
backoffBaseMs: 1e3,
|
|
@@ -3248,12 +3588,12 @@ var RestartTracker = class {
|
|
|
3248
3588
|
if (state.lastStartAt > 0) {
|
|
3249
3589
|
const stableMs = Date.now() - state.lastStartAt;
|
|
3250
3590
|
if (stableMs >= this.policy.resetAfterMs) {
|
|
3251
|
-
|
|
3591
|
+
logger20.info({ instanceName, stableMs, resetAfterMs: this.policy.resetAfterMs }, "Resetting restart counter \u2014 agent was stable");
|
|
3252
3592
|
state.count = 0;
|
|
3253
3593
|
}
|
|
3254
3594
|
}
|
|
3255
3595
|
if (state.count >= this.policy.maxRestarts) {
|
|
3256
|
-
|
|
3596
|
+
logger20.warn({ instanceName, count: state.count, maxRestarts: this.policy.maxRestarts }, "Restart limit exceeded");
|
|
3257
3597
|
return { allowed: false, delayMs: 0, attempt: state.count };
|
|
3258
3598
|
}
|
|
3259
3599
|
const delayMs = Math.min(
|
|
@@ -3266,7 +3606,7 @@ var RestartTracker = class {
|
|
|
3266
3606
|
const state = this.getOrCreate(instanceName);
|
|
3267
3607
|
state.count++;
|
|
3268
3608
|
state.lastRestartAt = Date.now();
|
|
3269
|
-
|
|
3609
|
+
logger20.debug({ instanceName, count: state.count }, "Restart recorded");
|
|
3270
3610
|
}
|
|
3271
3611
|
recordStart(instanceName) {
|
|
3272
3612
|
const state = this.getOrCreate(instanceName);
|
|
@@ -3293,8 +3633,8 @@ var RestartTracker = class {
|
|
|
3293
3633
|
|
|
3294
3634
|
// src/communicator/claude-code-communicator.ts
|
|
3295
3635
|
import { spawn as spawn4 } from "child_process";
|
|
3296
|
-
import { createLogger as
|
|
3297
|
-
var
|
|
3636
|
+
import { createLogger as createLogger22 } from "@actant/shared";
|
|
3637
|
+
var logger21 = createLogger22("claude-code-communicator");
|
|
3298
3638
|
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
3299
3639
|
var ClaudeCodeCommunicator = class {
|
|
3300
3640
|
executable;
|
|
@@ -3303,12 +3643,13 @@ var ClaudeCodeCommunicator = class {
|
|
|
3303
3643
|
}
|
|
3304
3644
|
async runPrompt(workspaceDir, prompt, options) {
|
|
3305
3645
|
const args = this.buildArgs(prompt, options, "json");
|
|
3306
|
-
|
|
3646
|
+
logger21.debug({ workspaceDir, args }, "Running claude-code prompt");
|
|
3307
3647
|
return new Promise((resolve3, reject) => {
|
|
3308
3648
|
const child = spawn4(this.executable, args, {
|
|
3309
3649
|
cwd: workspaceDir,
|
|
3310
3650
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3311
|
-
env: { ...process.env }
|
|
3651
|
+
env: { ...process.env },
|
|
3652
|
+
windowsHide: true
|
|
3312
3653
|
});
|
|
3313
3654
|
let stdout = "";
|
|
3314
3655
|
let stderr = "";
|
|
@@ -3326,7 +3667,7 @@ var ClaudeCodeCommunicator = class {
|
|
|
3326
3667
|
child.on("close", (code) => {
|
|
3327
3668
|
clearTimeout(timer);
|
|
3328
3669
|
if (code !== 0) {
|
|
3329
|
-
|
|
3670
|
+
logger21.warn({ code, stderr: stderr.slice(0, 500) }, "claude-code exited with error");
|
|
3330
3671
|
reject(new Error(`claude-code exited with code ${code}: ${stderr.slice(0, 500)}`));
|
|
3331
3672
|
return;
|
|
3332
3673
|
}
|
|
@@ -3336,7 +3677,7 @@ var ClaudeCodeCommunicator = class {
|
|
|
3336
3677
|
const sessionId = parsed["session_id"];
|
|
3337
3678
|
const subtype = parsed["subtype"];
|
|
3338
3679
|
if (subtype && subtype !== "success") {
|
|
3339
|
-
|
|
3680
|
+
logger21.warn({ subtype }, "claude-code returned non-success subtype");
|
|
3340
3681
|
}
|
|
3341
3682
|
let text;
|
|
3342
3683
|
if (result && result.length > 0) {
|
|
@@ -3344,11 +3685,18 @@ var ClaudeCodeCommunicator = class {
|
|
|
3344
3685
|
} else if (subtype === "error_max_turns") {
|
|
3345
3686
|
text = "[max turns reached \u2014 no final result text]";
|
|
3346
3687
|
} else {
|
|
3347
|
-
|
|
3688
|
+
const costUsd = parsed["cost_usd"];
|
|
3689
|
+
const duration = parsed["duration_ms"];
|
|
3690
|
+
const parts = ["[no result text]"];
|
|
3691
|
+
if (subtype) parts.push(`subtype=${subtype}`);
|
|
3692
|
+
if (costUsd != null) parts.push(`cost=$${costUsd.toFixed(4)}`);
|
|
3693
|
+
if (duration != null) parts.push(`duration=${duration}ms`);
|
|
3694
|
+
text = parts.join(" ");
|
|
3348
3695
|
}
|
|
3349
3696
|
resolve3({ text, sessionId });
|
|
3350
3697
|
} catch {
|
|
3351
|
-
|
|
3698
|
+
const firstLine = stdout.slice(0, 200).split("\n")[0] ?? "";
|
|
3699
|
+
resolve3({ text: firstLine || "[unparseable response]" });
|
|
3352
3700
|
}
|
|
3353
3701
|
});
|
|
3354
3702
|
child.on("error", (err) => {
|
|
@@ -3360,11 +3708,12 @@ var ClaudeCodeCommunicator = class {
|
|
|
3360
3708
|
}
|
|
3361
3709
|
async *streamPrompt(workspaceDir, prompt, options) {
|
|
3362
3710
|
const args = this.buildArgs(prompt, options, "stream-json");
|
|
3363
|
-
|
|
3711
|
+
logger21.debug({ workspaceDir, args }, "Streaming claude-code prompt");
|
|
3364
3712
|
const child = spawn4(this.executable, args, {
|
|
3365
3713
|
cwd: workspaceDir,
|
|
3366
3714
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3367
|
-
env: { ...process.env }
|
|
3715
|
+
env: { ...process.env },
|
|
3716
|
+
windowsHide: true
|
|
3368
3717
|
});
|
|
3369
3718
|
const timeout = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
3370
3719
|
const timer = setTimeout(() => {
|
|
@@ -3457,11 +3806,11 @@ function parseStreamEvent(event) {
|
|
|
3457
3806
|
}
|
|
3458
3807
|
|
|
3459
3808
|
// src/communicator/cursor-communicator.ts
|
|
3460
|
-
import { createLogger as
|
|
3461
|
-
var
|
|
3809
|
+
import { createLogger as createLogger23 } from "@actant/shared";
|
|
3810
|
+
var logger22 = createLogger23("cursor-communicator");
|
|
3462
3811
|
var CursorCommunicator = class {
|
|
3463
3812
|
async runPrompt(_workspaceDir, _prompt, _options) {
|
|
3464
|
-
|
|
3813
|
+
logger22.warn("Cursor backend does not yet support programmatic communication");
|
|
3465
3814
|
throw new Error(
|
|
3466
3815
|
"Cursor backend does not support programmatic communication yet. Use claude-code backend for agent.run / agent.chat functionality."
|
|
3467
3816
|
);
|
|
@@ -3475,12 +3824,12 @@ var CursorCommunicator = class {
|
|
|
3475
3824
|
};
|
|
3476
3825
|
|
|
3477
3826
|
// src/communicator/create-communicator.ts
|
|
3478
|
-
var
|
|
3827
|
+
var registry = /* @__PURE__ */ new Map();
|
|
3479
3828
|
function registerCommunicator(backendType, factory) {
|
|
3480
|
-
|
|
3829
|
+
registry.set(backendType, factory);
|
|
3481
3830
|
}
|
|
3482
3831
|
function createCommunicator(backendType, backendConfig) {
|
|
3483
|
-
const registered =
|
|
3832
|
+
const registered = registry.get(backendType);
|
|
3484
3833
|
if (registered) return registered(backendConfig);
|
|
3485
3834
|
switch (backendType) {
|
|
3486
3835
|
case "claude-code":
|
|
@@ -3488,19 +3837,15 @@ function createCommunicator(backendType, backendConfig) {
|
|
|
3488
3837
|
case "cursor":
|
|
3489
3838
|
case "cursor-agent":
|
|
3490
3839
|
return new CursorCommunicator();
|
|
3491
|
-
|
|
3840
|
+
default:
|
|
3492
3841
|
throw new Error(
|
|
3493
|
-
|
|
3494
|
-
);
|
|
3495
|
-
case "pi":
|
|
3496
|
-
throw new Error(
|
|
3497
|
-
"Pi backend communicator not registered. Ensure @actant/pi is installed and initialized."
|
|
3842
|
+
`No communicator registered for backend "${backendType}". Register one via registerCommunicator() at startup.`
|
|
3498
3843
|
);
|
|
3499
3844
|
}
|
|
3500
3845
|
}
|
|
3501
3846
|
|
|
3502
3847
|
// src/manager/agent-manager.ts
|
|
3503
|
-
var
|
|
3848
|
+
var logger23 = createLogger24("agent-manager");
|
|
3504
3849
|
var AgentManager = class {
|
|
3505
3850
|
constructor(initializer, launcher, instancesBaseDir, options) {
|
|
3506
3851
|
this.initializer = initializer;
|
|
@@ -3541,7 +3886,7 @@ var AgentManager = class {
|
|
|
3541
3886
|
const dir = join17(this.instancesBaseDir, meta.name);
|
|
3542
3887
|
const fixed = await updateInstanceMeta(dir, { status: "stopped", pid: void 0 });
|
|
3543
3888
|
this.cache.set(meta.name, fixed);
|
|
3544
|
-
|
|
3889
|
+
logger23.info({ name: meta.name, oldStatus: meta.status, launchMode: meta.launchMode, recoveryAction: action.type }, "Stale status corrected");
|
|
3545
3890
|
if (action.type === "restart") {
|
|
3546
3891
|
pendingRestarts.push(meta.name);
|
|
3547
3892
|
}
|
|
@@ -3553,7 +3898,7 @@ var AgentManager = class {
|
|
|
3553
3898
|
await this.moveToCorrupted(name);
|
|
3554
3899
|
}
|
|
3555
3900
|
this.watcher.start();
|
|
3556
|
-
|
|
3901
|
+
logger23.info({
|
|
3557
3902
|
valid: valid.length,
|
|
3558
3903
|
corrupted: corrupted.length,
|
|
3559
3904
|
pendingRestarts: pendingRestarts.length
|
|
@@ -3561,9 +3906,9 @@ var AgentManager = class {
|
|
|
3561
3906
|
for (const name of pendingRestarts) {
|
|
3562
3907
|
try {
|
|
3563
3908
|
await this.startAgent(name);
|
|
3564
|
-
|
|
3909
|
+
logger23.info({ name }, "Recovery restart succeeded");
|
|
3565
3910
|
} catch (err) {
|
|
3566
|
-
|
|
3911
|
+
logger23.error({ name, error: err }, "Recovery restart failed");
|
|
3567
3912
|
}
|
|
3568
3913
|
}
|
|
3569
3914
|
}
|
|
@@ -3613,14 +3958,19 @@ var AgentManager = class {
|
|
|
3613
3958
|
pid = proc.pid;
|
|
3614
3959
|
}
|
|
3615
3960
|
if (this.acpManager) {
|
|
3616
|
-
const
|
|
3961
|
+
const acpResolved = resolveAcpBackend(meta.backendType, dir, meta.backendConfig);
|
|
3962
|
+
const providerEnv = buildProviderEnv(meta.providerConfig);
|
|
3617
3963
|
const connResult = await this.acpManager.connect(name, {
|
|
3618
|
-
command,
|
|
3619
|
-
args,
|
|
3964
|
+
command: acpResolved.command,
|
|
3965
|
+
args: acpResolved.args,
|
|
3620
3966
|
cwd: dir,
|
|
3621
|
-
|
|
3967
|
+
resolvePackage: acpResolved.resolvePackage,
|
|
3968
|
+
connectionOptions: {
|
|
3969
|
+
autoApprove: true,
|
|
3970
|
+
...Object.keys(providerEnv).length > 0 ? { env: providerEnv } : {}
|
|
3971
|
+
}
|
|
3622
3972
|
});
|
|
3623
|
-
|
|
3973
|
+
logger23.info({ name, acpOnly, providerType: meta.providerConfig?.type }, "ACP connection established");
|
|
3624
3974
|
if (acpOnly && "pid" in connResult && typeof connResult.pid === "number") {
|
|
3625
3975
|
pid = connResult.pid;
|
|
3626
3976
|
this.processes.set(name, { pid, workspaceDir: dir, instanceName: name });
|
|
@@ -3632,7 +3982,7 @@ var AgentManager = class {
|
|
|
3632
3982
|
this.watcher.watch(name, pid);
|
|
3633
3983
|
}
|
|
3634
3984
|
this.restartTracker.recordStart(name);
|
|
3635
|
-
|
|
3985
|
+
logger23.info({ name, pid, launchMode: starting.launchMode, acp: true }, "Agent started");
|
|
3636
3986
|
} catch (err) {
|
|
3637
3987
|
if (this.acpManager?.has(name)) {
|
|
3638
3988
|
await this.acpManager.disconnect(name).catch(() => {
|
|
@@ -3640,7 +3990,11 @@ var AgentManager = class {
|
|
|
3640
3990
|
}
|
|
3641
3991
|
const errored = await updateInstanceMeta(dir, { status: "error" });
|
|
3642
3992
|
this.cache.set(name, errored);
|
|
3643
|
-
throw err;
|
|
3993
|
+
if (err instanceof AgentLaunchError) throw err;
|
|
3994
|
+
const spawnMsg = err instanceof Error ? err.message : String(err);
|
|
3995
|
+
throw new AgentLaunchError(name, new Error(
|
|
3996
|
+
isSpawnNotFound(spawnMsg) ? buildSpawnNotFoundMessage(meta.backendType) : spawnMsg
|
|
3997
|
+
));
|
|
3644
3998
|
}
|
|
3645
3999
|
}
|
|
3646
4000
|
/**
|
|
@@ -3651,7 +4005,7 @@ var AgentManager = class {
|
|
|
3651
4005
|
const meta = this.requireAgent(name);
|
|
3652
4006
|
const dir = join17(this.instancesBaseDir, name);
|
|
3653
4007
|
if (meta.status !== "running" && meta.status !== "starting") {
|
|
3654
|
-
|
|
4008
|
+
logger23.warn({ name, status: meta.status }, "Agent is not running, setting to stopped");
|
|
3655
4009
|
const stopped2 = await updateInstanceMeta(dir, { status: "stopped", pid: void 0 });
|
|
3656
4010
|
this.cache.set(name, stopped2);
|
|
3657
4011
|
return;
|
|
@@ -3661,7 +4015,7 @@ var AgentManager = class {
|
|
|
3661
4015
|
this.cache.set(name, stopping);
|
|
3662
4016
|
if (this.acpManager?.has(name)) {
|
|
3663
4017
|
await this.acpManager.disconnect(name).catch((err) => {
|
|
3664
|
-
|
|
4018
|
+
logger23.warn({ name, error: err }, "Error disconnecting ACP during stop");
|
|
3665
4019
|
});
|
|
3666
4020
|
}
|
|
3667
4021
|
const proc = this.processes.get(name);
|
|
@@ -3671,11 +4025,14 @@ var AgentManager = class {
|
|
|
3671
4025
|
}
|
|
3672
4026
|
const stopped = await updateInstanceMeta(dir, { status: "stopped", pid: void 0 });
|
|
3673
4027
|
this.cache.set(name, stopped);
|
|
3674
|
-
|
|
4028
|
+
logger23.info({ name }, "Agent stopped");
|
|
3675
4029
|
}
|
|
3676
4030
|
/** Destroy an agent — stop it if running, then remove workspace. */
|
|
3677
4031
|
async destroyAgent(name) {
|
|
3678
4032
|
const meta = this.cache.get(name);
|
|
4033
|
+
if (!meta && !existsSync(join17(this.instancesBaseDir, name))) {
|
|
4034
|
+
throw new AgentNotFoundError(name);
|
|
4035
|
+
}
|
|
3679
4036
|
if (meta && (meta.status === "running" || meta.status === "starting")) {
|
|
3680
4037
|
await this.stopAgent(name);
|
|
3681
4038
|
}
|
|
@@ -3684,7 +4041,7 @@ var AgentManager = class {
|
|
|
3684
4041
|
await this.initializer.destroyInstance(name);
|
|
3685
4042
|
this.cache.delete(name);
|
|
3686
4043
|
this.processes.delete(name);
|
|
3687
|
-
|
|
4044
|
+
logger23.info({ name }, "Agent destroyed");
|
|
3688
4045
|
}
|
|
3689
4046
|
/** Get agent metadata by name. */
|
|
3690
4047
|
getAgent(name) {
|
|
@@ -3717,14 +4074,15 @@ var AgentManager = class {
|
|
|
3717
4074
|
throw new AgentNotFoundError(name);
|
|
3718
4075
|
}
|
|
3719
4076
|
const dir = join17(this.instancesBaseDir, name);
|
|
3720
|
-
const
|
|
4077
|
+
const resolved = resolveBackend(meta.backendType, dir, meta.backendConfig);
|
|
3721
4078
|
return {
|
|
3722
4079
|
workspaceDir: dir,
|
|
3723
|
-
command,
|
|
3724
|
-
args,
|
|
4080
|
+
command: resolved.command,
|
|
4081
|
+
args: resolved.args,
|
|
3725
4082
|
instanceName: name,
|
|
3726
4083
|
backendType: meta.backendType,
|
|
3727
|
-
created
|
|
4084
|
+
created,
|
|
4085
|
+
resolvePackage: resolved.resolvePackage
|
|
3728
4086
|
};
|
|
3729
4087
|
}
|
|
3730
4088
|
/**
|
|
@@ -3735,8 +4093,7 @@ var AgentManager = class {
|
|
|
3735
4093
|
async openAgent(name) {
|
|
3736
4094
|
const meta = this.requireAgent(name);
|
|
3737
4095
|
const dir = join17(this.instancesBaseDir, name);
|
|
3738
|
-
|
|
3739
|
-
return resolved;
|
|
4096
|
+
return openBackend(meta.backendType, dir);
|
|
3740
4097
|
}
|
|
3741
4098
|
/**
|
|
3742
4099
|
* Register an externally-spawned process with the manager.
|
|
@@ -3749,6 +4106,17 @@ var AgentManager = class {
|
|
|
3749
4106
|
if (meta.status === "running" && meta.pid != null) {
|
|
3750
4107
|
throw new AgentAlreadyAttachedError(name);
|
|
3751
4108
|
}
|
|
4109
|
+
try {
|
|
4110
|
+
process.kill(pid, 0);
|
|
4111
|
+
} catch (err) {
|
|
4112
|
+
const code = err.code;
|
|
4113
|
+
if (code === "ESRCH") {
|
|
4114
|
+
throw new AgentLaunchError(
|
|
4115
|
+
name,
|
|
4116
|
+
new Error(`Process with PID ${pid} does not exist`)
|
|
4117
|
+
);
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
3752
4120
|
const dir = join17(this.instancesBaseDir, name);
|
|
3753
4121
|
const mergedMetadata = attachMetadata ? { ...meta.metadata, ...attachMetadata } : meta.metadata;
|
|
3754
4122
|
const updated = await updateInstanceMeta(dir, {
|
|
@@ -3760,7 +4128,7 @@ var AgentManager = class {
|
|
|
3760
4128
|
this.cache.set(name, updated);
|
|
3761
4129
|
this.processes.set(name, { pid, workspaceDir: dir, instanceName: name });
|
|
3762
4130
|
this.watcher.watch(name, pid);
|
|
3763
|
-
|
|
4131
|
+
logger23.info({ name, pid }, "External process attached");
|
|
3764
4132
|
return updated;
|
|
3765
4133
|
}
|
|
3766
4134
|
/**
|
|
@@ -3783,7 +4151,7 @@ var AgentManager = class {
|
|
|
3783
4151
|
processOwnership: "managed"
|
|
3784
4152
|
});
|
|
3785
4153
|
this.cache.set(name, updated);
|
|
3786
|
-
|
|
4154
|
+
logger23.info({ name }, "External process detached");
|
|
3787
4155
|
let workspaceCleaned = false;
|
|
3788
4156
|
if (options?.cleanup && meta.workspacePolicy === "ephemeral") {
|
|
3789
4157
|
await this.destroyAgent(name);
|
|
@@ -3801,7 +4169,7 @@ var AgentManager = class {
|
|
|
3801
4169
|
const conn = this.acpManager.getConnection(name);
|
|
3802
4170
|
const sessionId = this.acpManager.getPrimarySessionId(name);
|
|
3803
4171
|
if (conn && sessionId) {
|
|
3804
|
-
|
|
4172
|
+
logger23.debug({ name, sessionId }, "Sending prompt via ACP");
|
|
3805
4173
|
const result = await conn.prompt(sessionId, prompt);
|
|
3806
4174
|
return { text: result.text, sessionId };
|
|
3807
4175
|
}
|
|
@@ -3820,7 +4188,7 @@ var AgentManager = class {
|
|
|
3820
4188
|
const conn = this.acpManager.getConnection(name);
|
|
3821
4189
|
const sessionId = this.acpManager.getPrimarySessionId(name);
|
|
3822
4190
|
if (conn && sessionId) {
|
|
3823
|
-
|
|
4191
|
+
logger23.debug({ name, sessionId }, "Streaming prompt via ACP");
|
|
3824
4192
|
return this.streamFromAcp(conn, sessionId, prompt);
|
|
3825
4193
|
}
|
|
3826
4194
|
}
|
|
@@ -3892,7 +4260,7 @@ var AgentManager = class {
|
|
|
3892
4260
|
}
|
|
3893
4261
|
const handler = getLaunchModeHandler(meta.launchMode);
|
|
3894
4262
|
const action = handler.getProcessExitAction(instanceName, meta);
|
|
3895
|
-
|
|
4263
|
+
logger23.warn({ instanceName, pid, launchMode: meta.launchMode, action: action.type, previousStatus: meta.status }, "Agent process exited unexpectedly");
|
|
3896
4264
|
if (this.acpManager?.has(instanceName)) {
|
|
3897
4265
|
await this.acpManager.disconnect(instanceName).catch(() => {
|
|
3898
4266
|
});
|
|
@@ -3913,28 +4281,28 @@ var AgentManager = class {
|
|
|
3913
4281
|
if (!decision.allowed) {
|
|
3914
4282
|
const errored = await updateInstanceMeta(dir, { status: "error" });
|
|
3915
4283
|
this.cache.set(instanceName, errored);
|
|
3916
|
-
|
|
4284
|
+
logger23.error({ instanceName, attempt: decision.attempt }, "Restart limit exceeded \u2014 marking as error");
|
|
3917
4285
|
break;
|
|
3918
4286
|
}
|
|
3919
|
-
|
|
4287
|
+
logger23.info({ instanceName, attempt: decision.attempt, delayMs: decision.delayMs }, "Scheduling crash restart with backoff");
|
|
3920
4288
|
if (decision.delayMs > 0) {
|
|
3921
4289
|
await delay(decision.delayMs);
|
|
3922
4290
|
}
|
|
3923
4291
|
this.restartTracker.recordRestart(instanceName);
|
|
3924
4292
|
try {
|
|
3925
4293
|
await this.startAgent(instanceName);
|
|
3926
|
-
|
|
4294
|
+
logger23.info({ instanceName, attempt: decision.attempt }, "Crash restart succeeded");
|
|
3927
4295
|
} catch (err) {
|
|
3928
|
-
|
|
4296
|
+
logger23.error({ instanceName, attempt: decision.attempt, error: err }, "Crash restart failed");
|
|
3929
4297
|
}
|
|
3930
4298
|
break;
|
|
3931
4299
|
}
|
|
3932
4300
|
case "destroy":
|
|
3933
4301
|
try {
|
|
3934
4302
|
await this.destroyAgent(instanceName);
|
|
3935
|
-
|
|
4303
|
+
logger23.info({ instanceName }, "One-shot agent destroyed after exit");
|
|
3936
4304
|
} catch (err) {
|
|
3937
|
-
|
|
4305
|
+
logger23.error({ instanceName, error: err }, "One-shot auto-destroy failed");
|
|
3938
4306
|
}
|
|
3939
4307
|
break;
|
|
3940
4308
|
case "mark-stopped":
|
|
@@ -3954,12 +4322,39 @@ var AgentManager = class {
|
|
|
3954
4322
|
const src = join17(this.instancesBaseDir, name);
|
|
3955
4323
|
const dest = join17(this.corruptedDir, `${name}-${Date.now()}`);
|
|
3956
4324
|
await rename3(src, dest);
|
|
3957
|
-
|
|
4325
|
+
logger23.warn({ name, dest }, "Corrupted instance moved");
|
|
3958
4326
|
} catch (err) {
|
|
3959
|
-
|
|
4327
|
+
logger23.error({ name, error: err }, "Failed to move corrupted instance");
|
|
3960
4328
|
}
|
|
3961
4329
|
}
|
|
3962
4330
|
};
|
|
4331
|
+
function buildProviderEnv(providerConfig) {
|
|
4332
|
+
const env = {};
|
|
4333
|
+
const defaultDesc = modelProviderRegistry.getDefault();
|
|
4334
|
+
const providerType = providerConfig?.type ?? defaultDesc?.type;
|
|
4335
|
+
if (providerType) {
|
|
4336
|
+
env["ACTANT_PROVIDER"] = providerType;
|
|
4337
|
+
}
|
|
4338
|
+
const descriptor = providerType ? modelProviderRegistry.get(providerType) : defaultDesc;
|
|
4339
|
+
const apiKey = descriptor?.apiKey ?? process.env["ACTANT_API_KEY"];
|
|
4340
|
+
if (apiKey) {
|
|
4341
|
+
env["ACTANT_API_KEY"] = apiKey;
|
|
4342
|
+
}
|
|
4343
|
+
const baseUrl = providerConfig?.baseUrl ?? descriptor?.defaultBaseUrl;
|
|
4344
|
+
if (baseUrl) {
|
|
4345
|
+
env["ACTANT_BASE_URL"] = baseUrl;
|
|
4346
|
+
}
|
|
4347
|
+
return env;
|
|
4348
|
+
}
|
|
4349
|
+
function isSpawnNotFound(msg) {
|
|
4350
|
+
return /ENOENT|EINVAL|is not recognized|not found/i.test(msg);
|
|
4351
|
+
}
|
|
4352
|
+
function buildSpawnNotFoundMessage(backendType) {
|
|
4353
|
+
const hint = getInstallHint(backendType);
|
|
4354
|
+
const base = `Backend "${backendType}" executable not found.`;
|
|
4355
|
+
return hint ? `${base}
|
|
4356
|
+
Install with: ${hint}` : `${base} Ensure the required CLI is installed and in your PATH.`;
|
|
4357
|
+
}
|
|
3963
4358
|
|
|
3964
4359
|
// src/manager/launcher/mock-launcher.ts
|
|
3965
4360
|
var nextPid = 1e4;
|
|
@@ -3982,14 +4377,14 @@ var MockLauncher = class {
|
|
|
3982
4377
|
|
|
3983
4378
|
// src/manager/launcher/process-launcher.ts
|
|
3984
4379
|
import { spawn as spawn5 } from "child_process";
|
|
3985
|
-
import { AgentLaunchError, createLogger as
|
|
4380
|
+
import { AgentLaunchError as AgentLaunchError2, createLogger as createLogger26 } from "@actant/shared";
|
|
3986
4381
|
|
|
3987
4382
|
// src/manager/launcher/process-log-writer.ts
|
|
3988
4383
|
import { createWriteStream } from "fs";
|
|
3989
4384
|
import { mkdir as mkdir12, rename as rename4, stat as stat7, readFile as readFile5 } from "fs/promises";
|
|
3990
4385
|
import { join as join18 } from "path";
|
|
3991
|
-
import { createLogger as
|
|
3992
|
-
var
|
|
4386
|
+
import { createLogger as createLogger25 } from "@actant/shared";
|
|
4387
|
+
var logger24 = createLogger25("process-log-writer");
|
|
3993
4388
|
var DEFAULT_MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
3994
4389
|
var DEFAULT_MAX_FILES = 3;
|
|
3995
4390
|
var ProcessLogWriter = class {
|
|
@@ -4026,7 +4421,7 @@ var ProcessLogWriter = class {
|
|
|
4026
4421
|
} catch {
|
|
4027
4422
|
this.stderrBytes = 0;
|
|
4028
4423
|
}
|
|
4029
|
-
|
|
4424
|
+
logger24.debug({ logsDir: this.logsDir }, "Log writer initialized");
|
|
4030
4425
|
}
|
|
4031
4426
|
/**
|
|
4032
4427
|
* Attach to readable streams from a spawned process.
|
|
@@ -4108,7 +4503,7 @@ var ProcessLogWriter = class {
|
|
|
4108
4503
|
this.stderrStream = newStream;
|
|
4109
4504
|
this.stderrBytes = 0;
|
|
4110
4505
|
}
|
|
4111
|
-
|
|
4506
|
+
logger24.debug({ filename }, "Log file rotated");
|
|
4112
4507
|
}
|
|
4113
4508
|
closeStream(stream) {
|
|
4114
4509
|
if (!stream) return Promise.resolve();
|
|
@@ -4119,7 +4514,7 @@ var ProcessLogWriter = class {
|
|
|
4119
4514
|
};
|
|
4120
4515
|
|
|
4121
4516
|
// src/manager/launcher/process-launcher.ts
|
|
4122
|
-
var
|
|
4517
|
+
var logger25 = createLogger26("process-launcher");
|
|
4123
4518
|
var DEFAULT_TERMINATE_TIMEOUT = 5e3;
|
|
4124
4519
|
var DEFAULT_SPAWN_VERIFY_DELAY = 500;
|
|
4125
4520
|
var ProcessLauncher = class {
|
|
@@ -4141,7 +4536,7 @@ var ProcessLauncher = class {
|
|
|
4141
4536
|
meta.backendConfig
|
|
4142
4537
|
);
|
|
4143
4538
|
const useAcp = isAcpBackend(meta.backendType);
|
|
4144
|
-
|
|
4539
|
+
logger25.info({ name: meta.name, command, args, backendType: meta.backendType, acp: useAcp }, "Spawning backend process");
|
|
4145
4540
|
const captureNonAcpLogs = !useAcp && this.enableProcessLogs;
|
|
4146
4541
|
let stdio;
|
|
4147
4542
|
if (useAcp) {
|
|
@@ -4173,7 +4568,7 @@ var ProcessLauncher = class {
|
|
|
4173
4568
|
}
|
|
4174
4569
|
});
|
|
4175
4570
|
if ("error" in spawnResult) {
|
|
4176
|
-
throw new
|
|
4571
|
+
throw new AgentLaunchError2(meta.name, spawnResult.error);
|
|
4177
4572
|
}
|
|
4178
4573
|
const pid = spawnResult.pid;
|
|
4179
4574
|
if (!useAcp) {
|
|
@@ -4184,27 +4579,27 @@ var ProcessLauncher = class {
|
|
|
4184
4579
|
earlyExit = true;
|
|
4185
4580
|
});
|
|
4186
4581
|
child.on("error", (err) => {
|
|
4187
|
-
|
|
4582
|
+
logger25.error({ name: meta.name, pid, error: err }, "Backend process error after spawn");
|
|
4188
4583
|
});
|
|
4189
4584
|
if (this.spawnVerifyDelayMs > 0) {
|
|
4190
4585
|
await delay(this.spawnVerifyDelayMs);
|
|
4191
4586
|
if (earlyExit || !isProcessAlive(pid)) {
|
|
4192
|
-
throw new
|
|
4587
|
+
throw new AgentLaunchError2(
|
|
4193
4588
|
meta.name,
|
|
4194
4589
|
new Error(`Process exited immediately after spawn (pid=${pid}, command=${command})`)
|
|
4195
4590
|
);
|
|
4196
4591
|
}
|
|
4197
4592
|
}
|
|
4198
|
-
|
|
4593
|
+
logger25.info({ name: meta.name, pid, command, acp: useAcp }, "Backend process spawned");
|
|
4199
4594
|
if (captureNonAcpLogs && child.stdout && child.stderr) {
|
|
4200
4595
|
const logWriter = new ProcessLogWriter(workspaceDir, this.logWriterOptions);
|
|
4201
4596
|
try {
|
|
4202
4597
|
await logWriter.initialize();
|
|
4203
4598
|
logWriter.attach(child.stdout, child.stderr);
|
|
4204
4599
|
this.logWriters.set(meta.name, logWriter);
|
|
4205
|
-
|
|
4600
|
+
logger25.debug({ name: meta.name }, "Process log capture enabled");
|
|
4206
4601
|
} catch (err) {
|
|
4207
|
-
|
|
4602
|
+
logger25.warn({ name: meta.name, error: err }, "Failed to initialize log writer, continuing without log capture");
|
|
4208
4603
|
}
|
|
4209
4604
|
}
|
|
4210
4605
|
const result = {
|
|
@@ -4233,27 +4628,27 @@ var ProcessLauncher = class {
|
|
|
4233
4628
|
this.logWriters.delete(instanceName);
|
|
4234
4629
|
}
|
|
4235
4630
|
if (!isProcessAlive(pid)) {
|
|
4236
|
-
|
|
4631
|
+
logger25.info({ instanceName, pid }, "Process already exited");
|
|
4237
4632
|
return;
|
|
4238
4633
|
}
|
|
4239
|
-
|
|
4634
|
+
logger25.info({ instanceName, pid }, "Sending SIGTERM");
|
|
4240
4635
|
sendSignal(pid, "SIGTERM");
|
|
4241
4636
|
const deadline = Date.now() + this.terminateTimeoutMs;
|
|
4242
4637
|
const pollInterval = 200;
|
|
4243
4638
|
while (Date.now() < deadline) {
|
|
4244
4639
|
await delay(pollInterval);
|
|
4245
4640
|
if (!isProcessAlive(pid)) {
|
|
4246
|
-
|
|
4641
|
+
logger25.info({ instanceName, pid }, "Process terminated gracefully");
|
|
4247
4642
|
return;
|
|
4248
4643
|
}
|
|
4249
4644
|
}
|
|
4250
|
-
|
|
4645
|
+
logger25.warn({ instanceName, pid }, "Process did not exit after SIGTERM, sending SIGKILL");
|
|
4251
4646
|
sendSignal(pid, "SIGKILL");
|
|
4252
4647
|
await delay(500);
|
|
4253
4648
|
if (isProcessAlive(pid)) {
|
|
4254
|
-
|
|
4649
|
+
logger25.error({ instanceName, pid }, "Process still alive after SIGKILL");
|
|
4255
4650
|
} else {
|
|
4256
|
-
|
|
4651
|
+
logger25.info({ instanceName, pid }, "Process killed with SIGKILL");
|
|
4257
4652
|
}
|
|
4258
4653
|
}
|
|
4259
4654
|
};
|
|
@@ -4270,12 +4665,15 @@ function createLauncher(config) {
|
|
|
4270
4665
|
}
|
|
4271
4666
|
|
|
4272
4667
|
// src/domain/skill/skill-manager.ts
|
|
4273
|
-
import { z as
|
|
4274
|
-
var SkillDefinitionSchema =
|
|
4275
|
-
name:
|
|
4276
|
-
description:
|
|
4277
|
-
content:
|
|
4278
|
-
tags:
|
|
4668
|
+
import { z as z5 } from "zod/v4";
|
|
4669
|
+
var SkillDefinitionSchema = z5.object({
|
|
4670
|
+
name: z5.string().min(1),
|
|
4671
|
+
description: z5.string().optional(),
|
|
4672
|
+
content: z5.string().min(1),
|
|
4673
|
+
tags: z5.array(z5.string()).optional(),
|
|
4674
|
+
license: z5.string().optional(),
|
|
4675
|
+
compatibility: z5.string().optional(),
|
|
4676
|
+
allowedTools: z5.array(z5.string()).optional()
|
|
4279
4677
|
}).passthrough();
|
|
4280
4678
|
var SkillManager = class extends BaseComponentManager {
|
|
4281
4679
|
componentType = "Skill";
|
|
@@ -4315,12 +4713,12 @@ ${sections.join("\n\n---\n\n")}
|
|
|
4315
4713
|
};
|
|
4316
4714
|
|
|
4317
4715
|
// src/domain/prompt/prompt-manager.ts
|
|
4318
|
-
import { z as
|
|
4319
|
-
var PromptDefinitionSchema =
|
|
4320
|
-
name:
|
|
4321
|
-
description:
|
|
4322
|
-
content:
|
|
4323
|
-
variables:
|
|
4716
|
+
import { z as z6 } from "zod/v4";
|
|
4717
|
+
var PromptDefinitionSchema = z6.object({
|
|
4718
|
+
name: z6.string().min(1),
|
|
4719
|
+
description: z6.string().optional(),
|
|
4720
|
+
content: z6.string().min(1),
|
|
4721
|
+
variables: z6.array(z6.string()).optional()
|
|
4324
4722
|
}).passthrough();
|
|
4325
4723
|
var PromptManager = class extends BaseComponentManager {
|
|
4326
4724
|
componentType = "Prompt";
|
|
@@ -4372,13 +4770,13 @@ ${sections.join("\n\n---\n\n")}
|
|
|
4372
4770
|
};
|
|
4373
4771
|
|
|
4374
4772
|
// src/domain/mcp/mcp-config-manager.ts
|
|
4375
|
-
import { z as
|
|
4376
|
-
var McpServerDefinitionSchema =
|
|
4377
|
-
name:
|
|
4378
|
-
description:
|
|
4379
|
-
command:
|
|
4380
|
-
args:
|
|
4381
|
-
env:
|
|
4773
|
+
import { z as z7 } from "zod/v4";
|
|
4774
|
+
var McpServerDefinitionSchema = z7.object({
|
|
4775
|
+
name: z7.string().min(1),
|
|
4776
|
+
description: z7.string().optional(),
|
|
4777
|
+
command: z7.string().min(1),
|
|
4778
|
+
args: z7.array(z7.string()).optional(),
|
|
4779
|
+
env: z7.record(z7.string(), z7.string()).optional()
|
|
4382
4780
|
}).passthrough();
|
|
4383
4781
|
var McpConfigManager = class extends BaseComponentManager {
|
|
4384
4782
|
componentType = "McpServer";
|
|
@@ -4418,11 +4816,11 @@ var McpConfigManager = class extends BaseComponentManager {
|
|
|
4418
4816
|
};
|
|
4419
4817
|
|
|
4420
4818
|
// src/domain/workflow/workflow-manager.ts
|
|
4421
|
-
import { z as
|
|
4422
|
-
var WorkflowDefinitionSchema =
|
|
4423
|
-
name:
|
|
4424
|
-
description:
|
|
4425
|
-
content:
|
|
4819
|
+
import { z as z8 } from "zod/v4";
|
|
4820
|
+
var WorkflowDefinitionSchema = z8.object({
|
|
4821
|
+
name: z8.string().min(1),
|
|
4822
|
+
description: z8.string().optional(),
|
|
4823
|
+
content: z8.string().min(1)
|
|
4426
4824
|
}).passthrough();
|
|
4427
4825
|
var WorkflowManager = class extends BaseComponentManager {
|
|
4428
4826
|
componentType = "Workflow";
|
|
@@ -4451,14 +4849,14 @@ var WorkflowManager = class extends BaseComponentManager {
|
|
|
4451
4849
|
};
|
|
4452
4850
|
|
|
4453
4851
|
// src/domain/plugin/plugin-manager.ts
|
|
4454
|
-
import { z as
|
|
4455
|
-
var PluginDefinitionSchema =
|
|
4456
|
-
name:
|
|
4457
|
-
description:
|
|
4458
|
-
type:
|
|
4459
|
-
source:
|
|
4460
|
-
config:
|
|
4461
|
-
enabled:
|
|
4852
|
+
import { z as z9 } from "zod/v4";
|
|
4853
|
+
var PluginDefinitionSchema = z9.object({
|
|
4854
|
+
name: z9.string().min(1),
|
|
4855
|
+
description: z9.string().optional(),
|
|
4856
|
+
type: z9.enum(["npm", "file", "config"]),
|
|
4857
|
+
source: z9.string().optional(),
|
|
4858
|
+
config: z9.record(z9.string(), z9.unknown()).optional(),
|
|
4859
|
+
enabled: z9.boolean().optional().default(true)
|
|
4462
4860
|
}).passthrough();
|
|
4463
4861
|
var PluginManager = class extends BaseComponentManager {
|
|
4464
4862
|
componentType = "Plugin";
|
|
@@ -4503,8 +4901,8 @@ var PluginManager = class extends BaseComponentManager {
|
|
|
4503
4901
|
};
|
|
4504
4902
|
|
|
4505
4903
|
// src/permissions/permission-policy-enforcer.ts
|
|
4506
|
-
import { createLogger as
|
|
4507
|
-
var
|
|
4904
|
+
import { createLogger as createLogger27 } from "@actant/shared";
|
|
4905
|
+
var logger26 = createLogger27("permission-policy-enforcer");
|
|
4508
4906
|
var KIND_TO_TOOL = {
|
|
4509
4907
|
read: "Read",
|
|
4510
4908
|
edit: "Edit",
|
|
@@ -4521,7 +4919,7 @@ var PermissionPolicyEnforcer = class {
|
|
|
4521
4919
|
}
|
|
4522
4920
|
updateConfig(config) {
|
|
4523
4921
|
this.config = { ...config };
|
|
4524
|
-
|
|
4922
|
+
logger26.info("Permission policy config updated");
|
|
4525
4923
|
}
|
|
4526
4924
|
getConfig() {
|
|
4527
4925
|
return this.config;
|
|
@@ -4679,8 +5077,8 @@ function escapeRegex(str) {
|
|
|
4679
5077
|
}
|
|
4680
5078
|
|
|
4681
5079
|
// src/permissions/permission-audit.ts
|
|
4682
|
-
import { createLogger as
|
|
4683
|
-
var
|
|
5080
|
+
import { createLogger as createLogger28 } from "@actant/shared";
|
|
5081
|
+
var logger27 = createLogger28("permission-audit");
|
|
4684
5082
|
var PermissionAuditLogger = class {
|
|
4685
5083
|
instanceName;
|
|
4686
5084
|
constructor(instanceName) {
|
|
@@ -4693,7 +5091,7 @@ var PermissionAuditLogger = class {
|
|
|
4693
5091
|
...detail,
|
|
4694
5092
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4695
5093
|
};
|
|
4696
|
-
|
|
5094
|
+
logger27.info(entry, `[audit] ${event}`);
|
|
4697
5095
|
}
|
|
4698
5096
|
logEvaluation(toolCall, decision) {
|
|
4699
5097
|
this.log("permission.evaluated", { toolCall, decision });
|
|
@@ -4714,8 +5112,8 @@ var PermissionAuditLogger = class {
|
|
|
4714
5112
|
|
|
4715
5113
|
// src/session/session-registry.ts
|
|
4716
5114
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
4717
|
-
import { createLogger as
|
|
4718
|
-
var
|
|
5115
|
+
import { createLogger as createLogger29 } from "@actant/shared";
|
|
5116
|
+
var logger28 = createLogger29("session-registry");
|
|
4719
5117
|
var DEFAULT_IDLE_TTL_MS = 30 * 60 * 1e3;
|
|
4720
5118
|
var TTL_CHECK_INTERVAL_MS = 60 * 1e3;
|
|
4721
5119
|
var SessionRegistry = class {
|
|
@@ -4746,7 +5144,7 @@ var SessionRegistry = class {
|
|
|
4746
5144
|
idleTtlMs: opts.idleTtlMs ?? this.defaultIdleTtlMs
|
|
4747
5145
|
};
|
|
4748
5146
|
this.sessions.set(session.sessionId, session);
|
|
4749
|
-
|
|
5147
|
+
logger28.info({ sessionId: session.sessionId, agentName: opts.agentName, clientId: opts.clientId }, "Session created");
|
|
4750
5148
|
return session;
|
|
4751
5149
|
}
|
|
4752
5150
|
/** Get a session by ID, or undefined if not found. */
|
|
@@ -4778,7 +5176,7 @@ var SessionRegistry = class {
|
|
|
4778
5176
|
session.clientId = null;
|
|
4779
5177
|
session.state = "idle";
|
|
4780
5178
|
session.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4781
|
-
|
|
5179
|
+
logger28.info({ sessionId, agentName: session.agentName }, "Session released to idle");
|
|
4782
5180
|
}
|
|
4783
5181
|
/**
|
|
4784
5182
|
* Resume an idle session, binding it to a (potentially new) client.
|
|
@@ -4791,7 +5189,7 @@ var SessionRegistry = class {
|
|
|
4791
5189
|
session.clientId = clientId;
|
|
4792
5190
|
session.state = "active";
|
|
4793
5191
|
session.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4794
|
-
|
|
5192
|
+
logger28.info({ sessionId, clientId, agentName: session.agentName }, "Session resumed");
|
|
4795
5193
|
return true;
|
|
4796
5194
|
}
|
|
4797
5195
|
/** Explicitly close and remove a session. */
|
|
@@ -4799,7 +5197,7 @@ var SessionRegistry = class {
|
|
|
4799
5197
|
const session = this.sessions.get(sessionId);
|
|
4800
5198
|
if (!session) return false;
|
|
4801
5199
|
this.sessions.delete(sessionId);
|
|
4802
|
-
|
|
5200
|
+
logger28.info({ sessionId, agentName: session.agentName }, "Session closed");
|
|
4803
5201
|
return true;
|
|
4804
5202
|
}
|
|
4805
5203
|
/** Close all sessions for a given agent. */
|
|
@@ -4812,7 +5210,7 @@ var SessionRegistry = class {
|
|
|
4812
5210
|
}
|
|
4813
5211
|
}
|
|
4814
5212
|
if (count > 0) {
|
|
4815
|
-
|
|
5213
|
+
logger28.info({ agentName, count }, "Sessions closed for agent");
|
|
4816
5214
|
}
|
|
4817
5215
|
return count;
|
|
4818
5216
|
}
|
|
@@ -4836,7 +5234,7 @@ var SessionRegistry = class {
|
|
|
4836
5234
|
if (now - lastActivity > session.idleTtlMs) {
|
|
4837
5235
|
session.state = "expired";
|
|
4838
5236
|
this.sessions.delete(id);
|
|
4839
|
-
|
|
5237
|
+
logger28.info({ sessionId: id, agentName: session.agentName, idleMs: now - lastActivity }, "Session expired (TTL)");
|
|
4840
5238
|
this.onExpireCallback?.(session);
|
|
4841
5239
|
}
|
|
4842
5240
|
}
|
|
@@ -4846,15 +5244,15 @@ var SessionRegistry = class {
|
|
|
4846
5244
|
// src/source/github-source.ts
|
|
4847
5245
|
import { join as join20 } from "path";
|
|
4848
5246
|
import { mkdir as mkdir13, rm as rm5 } from "fs/promises";
|
|
4849
|
-
import { execFile } from "child_process";
|
|
5247
|
+
import { execFile as execFile2 } from "child_process";
|
|
4850
5248
|
import { promisify } from "util";
|
|
4851
|
-
import { createLogger as
|
|
5249
|
+
import { createLogger as createLogger31 } from "@actant/shared";
|
|
4852
5250
|
|
|
4853
5251
|
// src/source/local-source.ts
|
|
4854
5252
|
import { readFile as readFile6, readdir as readdir5, stat as stat8 } from "fs/promises";
|
|
4855
5253
|
import { join as join19, extname as extname3 } from "path";
|
|
4856
|
-
import { createLogger as
|
|
4857
|
-
var
|
|
5254
|
+
import { createLogger as createLogger30 } from "@actant/shared";
|
|
5255
|
+
var logger29 = createLogger30("local-source");
|
|
4858
5256
|
var LocalSource = class {
|
|
4859
5257
|
type = "local";
|
|
4860
5258
|
packageName;
|
|
@@ -4863,6 +5261,9 @@ var LocalSource = class {
|
|
|
4863
5261
|
this.packageName = packageName;
|
|
4864
5262
|
this.config = config;
|
|
4865
5263
|
}
|
|
5264
|
+
getRootDir() {
|
|
5265
|
+
return this.config.path;
|
|
5266
|
+
}
|
|
4866
5267
|
async fetch() {
|
|
4867
5268
|
return this.loadPackage();
|
|
4868
5269
|
}
|
|
@@ -4874,16 +5275,17 @@ var LocalSource = class {
|
|
|
4874
5275
|
async loadPackage() {
|
|
4875
5276
|
const rootDir = this.config.path;
|
|
4876
5277
|
const manifest = await this.loadManifest(rootDir);
|
|
4877
|
-
const [skills, prompts, mcpServers, workflows, presets, templates] = await Promise.all([
|
|
5278
|
+
const [skills, prompts, mcpServers, workflows, backends, presets, templates] = await Promise.all([
|
|
4878
5279
|
this.loadJsonDir(rootDir, manifest.components?.skills, "skills"),
|
|
4879
5280
|
this.loadJsonDir(rootDir, manifest.components?.prompts, "prompts"),
|
|
4880
5281
|
this.loadJsonDir(rootDir, manifest.components?.mcp, "mcp"),
|
|
4881
5282
|
this.loadJsonDir(rootDir, manifest.components?.workflows, "workflows"),
|
|
5283
|
+
this.loadJsonDir(rootDir, manifest.components?.backends, "backends"),
|
|
4882
5284
|
this.loadPresets(rootDir, manifest.presets),
|
|
4883
5285
|
this.loadJsonDir(rootDir, manifest.components?.templates, "templates")
|
|
4884
5286
|
]);
|
|
4885
|
-
|
|
4886
|
-
return { manifest, skills, prompts, mcpServers, workflows, presets, templates };
|
|
5287
|
+
logger29.info({ packageName: this.packageName, rootDir }, "Local package loaded");
|
|
5288
|
+
return { manifest, skills, prompts, mcpServers, workflows, backends, presets, templates };
|
|
4887
5289
|
}
|
|
4888
5290
|
async loadManifest(rootDir) {
|
|
4889
5291
|
const manifestPath = join19(rootDir, "actant.json");
|
|
@@ -4891,7 +5293,7 @@ var LocalSource = class {
|
|
|
4891
5293
|
const raw = await readFile6(manifestPath, "utf-8");
|
|
4892
5294
|
return JSON.parse(raw);
|
|
4893
5295
|
} catch {
|
|
4894
|
-
|
|
5296
|
+
logger29.debug({ rootDir }, "No actant.json found, scanning directories");
|
|
4895
5297
|
return { name: this.packageName };
|
|
4896
5298
|
}
|
|
4897
5299
|
}
|
|
@@ -4903,7 +5305,7 @@ var LocalSource = class {
|
|
|
4903
5305
|
const raw = await readFile6(join19(rootDir, relPath), "utf-8");
|
|
4904
5306
|
items2.push(JSON.parse(raw));
|
|
4905
5307
|
} catch (err) {
|
|
4906
|
-
|
|
5308
|
+
logger29.warn({ file: relPath, error: err }, `Failed to load from manifest, skipping`);
|
|
4907
5309
|
}
|
|
4908
5310
|
}
|
|
4909
5311
|
return items2;
|
|
@@ -4926,7 +5328,7 @@ var LocalSource = class {
|
|
|
4926
5328
|
const raw = await readFile6(fullPath, "utf-8");
|
|
4927
5329
|
items.push(JSON.parse(raw));
|
|
4928
5330
|
} catch (err) {
|
|
4929
|
-
|
|
5331
|
+
logger29.warn({ file, error: err }, `Failed to load ${subDir} file, skipping`);
|
|
4930
5332
|
}
|
|
4931
5333
|
}
|
|
4932
5334
|
for (const entry of entries) {
|
|
@@ -4951,7 +5353,7 @@ var LocalSource = class {
|
|
|
4951
5353
|
}
|
|
4952
5354
|
}
|
|
4953
5355
|
if (subDir === "skills") {
|
|
4954
|
-
const { parseSkillMd } = await import("./skill-md-parser-
|
|
5356
|
+
const { parseSkillMd } = await import("./skill-md-parser-HXLTZAUU.js");
|
|
4955
5357
|
for (const entry of entries) {
|
|
4956
5358
|
if (extname3(entry) === ".json") continue;
|
|
4957
5359
|
const subDirPath = join19(dirPath, entry);
|
|
@@ -4975,8 +5377,8 @@ var LocalSource = class {
|
|
|
4975
5377
|
};
|
|
4976
5378
|
|
|
4977
5379
|
// src/source/github-source.ts
|
|
4978
|
-
var execFileAsync = promisify(
|
|
4979
|
-
var
|
|
5380
|
+
var execFileAsync = promisify(execFile2);
|
|
5381
|
+
var logger30 = createLogger31("github-source");
|
|
4980
5382
|
var GitHubSource = class {
|
|
4981
5383
|
type = "github";
|
|
4982
5384
|
packageName;
|
|
@@ -4987,6 +5389,9 @@ var GitHubSource = class {
|
|
|
4987
5389
|
this.config = config;
|
|
4988
5390
|
this.cacheDir = join20(cacheDir, packageName);
|
|
4989
5391
|
}
|
|
5392
|
+
getRootDir() {
|
|
5393
|
+
return this.cacheDir;
|
|
5394
|
+
}
|
|
4990
5395
|
async fetch() {
|
|
4991
5396
|
await this.clone();
|
|
4992
5397
|
return this.readCached();
|
|
@@ -4995,21 +5400,21 @@ var GitHubSource = class {
|
|
|
4995
5400
|
try {
|
|
4996
5401
|
await this.pull();
|
|
4997
5402
|
} catch {
|
|
4998
|
-
|
|
5403
|
+
logger30.info("Pull failed, re-cloning");
|
|
4999
5404
|
await this.clone();
|
|
5000
5405
|
}
|
|
5001
5406
|
return this.readCached();
|
|
5002
5407
|
}
|
|
5003
5408
|
async dispose() {
|
|
5004
5409
|
await rm5(this.cacheDir, { recursive: true, force: true });
|
|
5005
|
-
|
|
5410
|
+
logger30.debug({ cacheDir: this.cacheDir }, "Cache cleaned");
|
|
5006
5411
|
}
|
|
5007
5412
|
async clone() {
|
|
5008
5413
|
await rm5(this.cacheDir, { recursive: true, force: true });
|
|
5009
5414
|
await mkdir13(this.cacheDir, { recursive: true });
|
|
5010
5415
|
const branch = this.config.branch ?? "main";
|
|
5011
5416
|
const args = ["clone", "--depth", "1", "--branch", branch, this.config.url, this.cacheDir];
|
|
5012
|
-
|
|
5417
|
+
logger30.info({ url: this.config.url, branch }, "Cloning repository");
|
|
5013
5418
|
await execFileAsync("git", args, { timeout: 6e4 });
|
|
5014
5419
|
}
|
|
5015
5420
|
async pull() {
|
|
@@ -5027,7 +5432,7 @@ var GitHubSource = class {
|
|
|
5027
5432
|
// src/source/source-manager.ts
|
|
5028
5433
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir14 } from "fs/promises";
|
|
5029
5434
|
import { join as join21 } from "path";
|
|
5030
|
-
import { createLogger as
|
|
5435
|
+
import { createLogger as createLogger32 } from "@actant/shared";
|
|
5031
5436
|
|
|
5032
5437
|
// src/version/sync-report.ts
|
|
5033
5438
|
function createEmptySyncReport() {
|
|
@@ -5052,7 +5457,7 @@ var DEFAULT_SOURCE_CONFIG = {
|
|
|
5052
5457
|
url: "https://github.com/blackplume233/actant-hub.git",
|
|
5053
5458
|
branch: "main"
|
|
5054
5459
|
};
|
|
5055
|
-
var
|
|
5460
|
+
var logger31 = createLogger32("source-manager");
|
|
5056
5461
|
var SourceManager = class {
|
|
5057
5462
|
sources = /* @__PURE__ */ new Map();
|
|
5058
5463
|
presets = /* @__PURE__ */ new Map();
|
|
@@ -5080,7 +5485,7 @@ var SourceManager = class {
|
|
|
5080
5485
|
this.sources.set(name, source);
|
|
5081
5486
|
this.injectComponents(name, result);
|
|
5082
5487
|
await this.persistSources();
|
|
5083
|
-
|
|
5488
|
+
logger31.info({ name, type: config.type }, "Source added");
|
|
5084
5489
|
return result;
|
|
5085
5490
|
}
|
|
5086
5491
|
async syncSource(name) {
|
|
@@ -5096,7 +5501,7 @@ var SourceManager = class {
|
|
|
5096
5501
|
const newSnapshot = this.snapshotComponents(name);
|
|
5097
5502
|
await this.persistSources();
|
|
5098
5503
|
const report = this.buildSyncReport(oldSnapshot, newSnapshot);
|
|
5099
|
-
|
|
5504
|
+
logger31.info({ name, report }, "Source synced");
|
|
5100
5505
|
return { fetchResult: result, report };
|
|
5101
5506
|
}
|
|
5102
5507
|
async syncAll() {
|
|
@@ -5109,7 +5514,7 @@ var SourceManager = class {
|
|
|
5109
5514
|
const { report } = await this.syncSourceWithReport(name);
|
|
5110
5515
|
reports.push(report);
|
|
5111
5516
|
} catch (err) {
|
|
5112
|
-
|
|
5517
|
+
logger31.warn({ name, error: err }, "Failed to sync source, skipping");
|
|
5113
5518
|
}
|
|
5114
5519
|
}
|
|
5115
5520
|
return { report: mergeSyncReports(reports) };
|
|
@@ -5122,7 +5527,7 @@ var SourceManager = class {
|
|
|
5122
5527
|
await source.dispose();
|
|
5123
5528
|
this.sources.delete(name);
|
|
5124
5529
|
await this.persistSources();
|
|
5125
|
-
|
|
5530
|
+
logger31.info({ name }, "Source removed");
|
|
5126
5531
|
return true;
|
|
5127
5532
|
}
|
|
5128
5533
|
listSources() {
|
|
@@ -5135,6 +5540,10 @@ var SourceManager = class {
|
|
|
5135
5540
|
hasSource(name) {
|
|
5136
5541
|
return this.sources.has(name);
|
|
5137
5542
|
}
|
|
5543
|
+
/** Resolve a registered source name to its filesystem root directory. */
|
|
5544
|
+
getSourceRootDir(name) {
|
|
5545
|
+
return this.getSourceOrThrow(name).getRootDir();
|
|
5546
|
+
}
|
|
5138
5547
|
// ---------------------------------------------------------------------------
|
|
5139
5548
|
// Preset operations
|
|
5140
5549
|
// ---------------------------------------------------------------------------
|
|
@@ -5206,7 +5615,7 @@ var SourceManager = class {
|
|
|
5206
5615
|
const data = JSON.parse(raw);
|
|
5207
5616
|
entries = Object.entries(data.sources ?? {}).map(([name, config]) => ({ name, config }));
|
|
5208
5617
|
} catch {
|
|
5209
|
-
|
|
5618
|
+
logger31.debug("No sources.json found, starting with empty sources");
|
|
5210
5619
|
}
|
|
5211
5620
|
for (const entry of entries) {
|
|
5212
5621
|
try {
|
|
@@ -5214,9 +5623,9 @@ var SourceManager = class {
|
|
|
5214
5623
|
const result = await source.fetch();
|
|
5215
5624
|
this.sources.set(entry.name, source);
|
|
5216
5625
|
this.injectComponents(entry.name, result);
|
|
5217
|
-
|
|
5626
|
+
logger31.info({ name: entry.name }, "Source restored from config");
|
|
5218
5627
|
} catch (err) {
|
|
5219
|
-
|
|
5628
|
+
logger31.warn({ name: entry.name, error: err }, "Failed to restore source, skipping");
|
|
5220
5629
|
}
|
|
5221
5630
|
}
|
|
5222
5631
|
await this.ensureDefaultSource();
|
|
@@ -5230,9 +5639,9 @@ var SourceManager = class {
|
|
|
5230
5639
|
if (this.sources.has(DEFAULT_SOURCE_NAME)) return;
|
|
5231
5640
|
try {
|
|
5232
5641
|
await this.addSource(DEFAULT_SOURCE_NAME, DEFAULT_SOURCE_CONFIG);
|
|
5233
|
-
|
|
5642
|
+
logger31.info("Default source registered: %s", DEFAULT_SOURCE_NAME);
|
|
5234
5643
|
} catch (err) {
|
|
5235
|
-
|
|
5644
|
+
logger31.debug({ error: err }, "Failed to register default source (offline?), skipping");
|
|
5236
5645
|
}
|
|
5237
5646
|
}
|
|
5238
5647
|
// ---------------------------------------------------------------------------
|
|
@@ -5262,22 +5671,40 @@ var SourceManager = class {
|
|
|
5262
5671
|
for (const wf of result.workflows) {
|
|
5263
5672
|
this.managers.workflowManager.register(ns(wf));
|
|
5264
5673
|
}
|
|
5674
|
+
if (this.managers.backendManager) {
|
|
5675
|
+
for (const backend of result.backends) {
|
|
5676
|
+
this.managers.backendManager.register(ns(backend));
|
|
5677
|
+
}
|
|
5678
|
+
}
|
|
5265
5679
|
for (const preset of result.presets) {
|
|
5266
5680
|
this.presets.set(`${packageName}@${preset.name}`, preset);
|
|
5267
5681
|
}
|
|
5268
5682
|
if (this.managers.templateRegistry && result.templates.length > 0) {
|
|
5683
|
+
const nsRef = (ref) => ref.includes("@") ? ref : `${packageName}@${ref}`;
|
|
5269
5684
|
for (const template of result.templates) {
|
|
5270
|
-
const
|
|
5685
|
+
const dc = template.domainContext;
|
|
5686
|
+
const nsTemplate = {
|
|
5687
|
+
...template,
|
|
5688
|
+
name: `${packageName}@${template.name}`,
|
|
5689
|
+
domainContext: {
|
|
5690
|
+
...dc,
|
|
5691
|
+
skills: dc.skills?.map(nsRef),
|
|
5692
|
+
prompts: dc.prompts?.map(nsRef),
|
|
5693
|
+
subAgents: dc.subAgents?.map(nsRef),
|
|
5694
|
+
workflow: dc.workflow ? nsRef(dc.workflow) : dc.workflow
|
|
5695
|
+
}
|
|
5696
|
+
};
|
|
5271
5697
|
this.managers.templateRegistry.register(nsTemplate);
|
|
5272
5698
|
}
|
|
5273
5699
|
}
|
|
5274
|
-
|
|
5700
|
+
logger31.debug(
|
|
5275
5701
|
{
|
|
5276
5702
|
packageName,
|
|
5277
5703
|
skills: result.skills.length,
|
|
5278
5704
|
prompts: result.prompts.length,
|
|
5279
5705
|
mcp: result.mcpServers.length,
|
|
5280
5706
|
workflows: result.workflows.length,
|
|
5707
|
+
backends: result.backends.length,
|
|
5281
5708
|
presets: result.presets.length,
|
|
5282
5709
|
templates: result.templates.length
|
|
5283
5710
|
},
|
|
@@ -5297,6 +5724,9 @@ var SourceManager = class {
|
|
|
5297
5724
|
removeFrom(this.managers.promptManager);
|
|
5298
5725
|
removeFrom(this.managers.mcpConfigManager);
|
|
5299
5726
|
removeFrom(this.managers.workflowManager);
|
|
5727
|
+
if (this.managers.backendManager) {
|
|
5728
|
+
removeFrom(this.managers.backendManager);
|
|
5729
|
+
}
|
|
5300
5730
|
if (this.managers.templateRegistry) {
|
|
5301
5731
|
for (const t of this.managers.templateRegistry.list()) {
|
|
5302
5732
|
if (t.name.startsWith(prefix)) {
|
|
@@ -5404,6 +5834,705 @@ function isMajorVersionChange(oldVersion, newVersion) {
|
|
|
5404
5834
|
if (oldMajor === void 0 || newMajor === void 0) return false;
|
|
5405
5835
|
return newMajor !== oldMajor;
|
|
5406
5836
|
}
|
|
5837
|
+
|
|
5838
|
+
// src/source/source-validator.ts
|
|
5839
|
+
import { readFile as readFile8, readdir as readdir6, stat as stat9, access as access4 } from "fs/promises";
|
|
5840
|
+
import { join as join22, relative as relative2, extname as extname4 } from "path";
|
|
5841
|
+
|
|
5842
|
+
// src/source/source-schemas.ts
|
|
5843
|
+
import { z as z10 } from "zod";
|
|
5844
|
+
var VersionedComponentFields = {
|
|
5845
|
+
name: z10.string().min(1, "name is required"),
|
|
5846
|
+
version: z10.string().optional(),
|
|
5847
|
+
description: z10.string().optional(),
|
|
5848
|
+
$type: z10.string().optional(),
|
|
5849
|
+
$version: z10.number().optional(),
|
|
5850
|
+
origin: z10.object({
|
|
5851
|
+
type: z10.enum(["builtin", "source", "local"]),
|
|
5852
|
+
sourceName: z10.string().optional(),
|
|
5853
|
+
syncHash: z10.string().optional(),
|
|
5854
|
+
syncedAt: z10.string().optional(),
|
|
5855
|
+
modified: z10.boolean().optional()
|
|
5856
|
+
}).optional(),
|
|
5857
|
+
tags: z10.array(z10.string()).optional()
|
|
5858
|
+
};
|
|
5859
|
+
var PackageManifestSchema = z10.object({
|
|
5860
|
+
name: z10.string().min(1, "name is required"),
|
|
5861
|
+
version: z10.string().optional(),
|
|
5862
|
+
description: z10.string().optional(),
|
|
5863
|
+
components: z10.object({
|
|
5864
|
+
skills: z10.array(z10.string()).optional(),
|
|
5865
|
+
prompts: z10.array(z10.string()).optional(),
|
|
5866
|
+
mcp: z10.array(z10.string()).optional(),
|
|
5867
|
+
workflows: z10.array(z10.string()).optional(),
|
|
5868
|
+
templates: z10.array(z10.string()).optional(),
|
|
5869
|
+
backends: z10.array(z10.string()).optional()
|
|
5870
|
+
}).optional(),
|
|
5871
|
+
presets: z10.array(z10.string()).optional()
|
|
5872
|
+
});
|
|
5873
|
+
var SkillDefinitionSchema2 = z10.object({
|
|
5874
|
+
...VersionedComponentFields,
|
|
5875
|
+
content: z10.string().min(1, "content is required"),
|
|
5876
|
+
license: z10.string().optional(),
|
|
5877
|
+
compatibility: z10.string().optional(),
|
|
5878
|
+
allowedTools: z10.array(z10.string()).optional()
|
|
5879
|
+
});
|
|
5880
|
+
var PromptDefinitionSchema2 = z10.object({
|
|
5881
|
+
...VersionedComponentFields,
|
|
5882
|
+
content: z10.string().min(1, "content is required"),
|
|
5883
|
+
variables: z10.array(z10.string()).optional()
|
|
5884
|
+
});
|
|
5885
|
+
var McpServerDefinitionSchema2 = z10.object({
|
|
5886
|
+
...VersionedComponentFields,
|
|
5887
|
+
command: z10.string().min(1, "command is required"),
|
|
5888
|
+
args: z10.array(z10.string()).optional(),
|
|
5889
|
+
env: z10.record(z10.string(), z10.string()).optional()
|
|
5890
|
+
});
|
|
5891
|
+
var WorkflowDefinitionSchema2 = z10.object({
|
|
5892
|
+
...VersionedComponentFields,
|
|
5893
|
+
content: z10.string().min(1, "content is required")
|
|
5894
|
+
});
|
|
5895
|
+
var PlatformCommandSchema2 = z10.object({
|
|
5896
|
+
win32: z10.string().min(1),
|
|
5897
|
+
default: z10.string().min(1)
|
|
5898
|
+
});
|
|
5899
|
+
var SourceBackendDefinitionSchema = z10.object({
|
|
5900
|
+
...VersionedComponentFields,
|
|
5901
|
+
supportedModes: z10.array(z10.enum(["resolve", "open", "acp"])).min(1),
|
|
5902
|
+
resolveCommand: PlatformCommandSchema2.optional(),
|
|
5903
|
+
openCommand: PlatformCommandSchema2.optional(),
|
|
5904
|
+
acpCommand: PlatformCommandSchema2.optional(),
|
|
5905
|
+
acpOwnsProcess: z10.boolean().optional(),
|
|
5906
|
+
resolvePackage: z10.string().optional(),
|
|
5907
|
+
openWorkspaceDir: z10.enum(["arg", "cwd"]).optional(),
|
|
5908
|
+
openSpawnOptions: z10.object({
|
|
5909
|
+
stdio: z10.enum(["inherit", "ignore"]).optional(),
|
|
5910
|
+
detached: z10.boolean().optional(),
|
|
5911
|
+
windowsHide: z10.boolean().optional(),
|
|
5912
|
+
shell: z10.boolean().optional()
|
|
5913
|
+
}).optional(),
|
|
5914
|
+
existenceCheck: z10.object({
|
|
5915
|
+
command: z10.string().min(1),
|
|
5916
|
+
args: z10.array(z10.string()).optional(),
|
|
5917
|
+
expectedExitCode: z10.number().int().optional(),
|
|
5918
|
+
versionPattern: z10.string().optional()
|
|
5919
|
+
}).optional(),
|
|
5920
|
+
install: z10.array(z10.object({
|
|
5921
|
+
type: z10.enum(["npm", "brew", "winget", "choco", "url", "manual"]),
|
|
5922
|
+
package: z10.string().optional(),
|
|
5923
|
+
platforms: z10.array(z10.string()).optional(),
|
|
5924
|
+
label: z10.string().optional(),
|
|
5925
|
+
instructions: z10.string().optional()
|
|
5926
|
+
})).optional()
|
|
5927
|
+
});
|
|
5928
|
+
var PresetDefinitionSchema = z10.object({
|
|
5929
|
+
name: z10.string().min(1, "name is required"),
|
|
5930
|
+
version: z10.string().optional(),
|
|
5931
|
+
description: z10.string().optional(),
|
|
5932
|
+
skills: z10.array(z10.string()).optional(),
|
|
5933
|
+
prompts: z10.array(z10.string()).optional(),
|
|
5934
|
+
mcpServers: z10.array(z10.string()).optional(),
|
|
5935
|
+
workflows: z10.array(z10.string()).optional(),
|
|
5936
|
+
templates: z10.array(z10.string()).optional()
|
|
5937
|
+
});
|
|
5938
|
+
|
|
5939
|
+
// src/source/source-validator.ts
|
|
5940
|
+
var COMPONENT_DIR_SCHEMAS = {
|
|
5941
|
+
skills: SkillDefinitionSchema2,
|
|
5942
|
+
prompts: PromptDefinitionSchema2,
|
|
5943
|
+
mcp: McpServerDefinitionSchema2,
|
|
5944
|
+
workflows: WorkflowDefinitionSchema2,
|
|
5945
|
+
presets: PresetDefinitionSchema
|
|
5946
|
+
};
|
|
5947
|
+
var COMPONENT_DIRS = ["skills", "prompts", "mcp", "workflows", "templates", "presets"];
|
|
5948
|
+
var SourceValidator = class {
|
|
5949
|
+
/**
|
|
5950
|
+
* Validate all assets in a source directory.
|
|
5951
|
+
* Returns a report with pass/warn/error counts and detailed issues.
|
|
5952
|
+
*/
|
|
5953
|
+
async validate(rootDir, options) {
|
|
5954
|
+
const issues = [];
|
|
5955
|
+
let passCount = 0;
|
|
5956
|
+
const validatedFiles = /* @__PURE__ */ new Set();
|
|
5957
|
+
const compat = options?.compat;
|
|
5958
|
+
const manifest = await this.validateManifest(rootDir, issues);
|
|
5959
|
+
if (manifest) passCount++;
|
|
5960
|
+
const componentNames = /* @__PURE__ */ new Map();
|
|
5961
|
+
if (manifest) {
|
|
5962
|
+
passCount += await this.validateExplicitFiles(rootDir, manifest, issues, componentNames, validatedFiles, compat);
|
|
5963
|
+
}
|
|
5964
|
+
passCount += await this.validateComponentDirs(rootDir, issues, componentNames, validatedFiles, compat);
|
|
5965
|
+
await this.validatePresetReferences(rootDir, manifest, componentNames, issues);
|
|
5966
|
+
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
5967
|
+
const warnCount = issues.filter((i) => i.severity === "warning").length;
|
|
5968
|
+
const valid = options?.strict ? errorCount === 0 && warnCount === 0 : errorCount === 0;
|
|
5969
|
+
return {
|
|
5970
|
+
valid,
|
|
5971
|
+
sourceName: manifest?.name ?? "unknown",
|
|
5972
|
+
rootDir,
|
|
5973
|
+
summary: { pass: passCount, warn: warnCount, error: errorCount },
|
|
5974
|
+
issues
|
|
5975
|
+
};
|
|
5976
|
+
}
|
|
5977
|
+
// -------------------------------------------------------------------------
|
|
5978
|
+
// Layer 1: Manifest validation
|
|
5979
|
+
// -------------------------------------------------------------------------
|
|
5980
|
+
async validateManifest(rootDir, issues) {
|
|
5981
|
+
const manifestPath = join22(rootDir, "actant.json");
|
|
5982
|
+
let raw;
|
|
5983
|
+
try {
|
|
5984
|
+
raw = await readFile8(manifestPath, "utf-8");
|
|
5985
|
+
} catch {
|
|
5986
|
+
issues.push({
|
|
5987
|
+
severity: "error",
|
|
5988
|
+
path: "actant.json",
|
|
5989
|
+
message: "actant.json not found in source root",
|
|
5990
|
+
code: "MANIFEST_MISSING"
|
|
5991
|
+
});
|
|
5992
|
+
return null;
|
|
5993
|
+
}
|
|
5994
|
+
let data;
|
|
5995
|
+
try {
|
|
5996
|
+
data = JSON.parse(raw);
|
|
5997
|
+
} catch (err) {
|
|
5998
|
+
issues.push({
|
|
5999
|
+
severity: "error",
|
|
6000
|
+
path: "actant.json",
|
|
6001
|
+
message: `Invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
6002
|
+
code: "MANIFEST_INVALID_JSON"
|
|
6003
|
+
});
|
|
6004
|
+
return null;
|
|
6005
|
+
}
|
|
6006
|
+
const result = PackageManifestSchema.safeParse(data);
|
|
6007
|
+
if (!result.success) {
|
|
6008
|
+
for (const issue of result.error.issues) {
|
|
6009
|
+
issues.push({
|
|
6010
|
+
severity: "error",
|
|
6011
|
+
path: "actant.json",
|
|
6012
|
+
message: `${issue.path.join(".")}: ${issue.message}`,
|
|
6013
|
+
code: "MANIFEST_SCHEMA"
|
|
6014
|
+
});
|
|
6015
|
+
}
|
|
6016
|
+
return null;
|
|
6017
|
+
}
|
|
6018
|
+
const manifest = result.data;
|
|
6019
|
+
await this.verifyManifestFileRefs(rootDir, manifest, issues);
|
|
6020
|
+
return manifest;
|
|
6021
|
+
}
|
|
6022
|
+
async verifyManifestFileRefs(rootDir, manifest, issues) {
|
|
6023
|
+
const allRefs = [];
|
|
6024
|
+
if (manifest.components) {
|
|
6025
|
+
for (const [type, files] of Object.entries(manifest.components)) {
|
|
6026
|
+
if (files) {
|
|
6027
|
+
for (const f of files) {
|
|
6028
|
+
allRefs.push({ ref: f, section: `components.${type}` });
|
|
6029
|
+
}
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
6032
|
+
}
|
|
6033
|
+
if (manifest.presets) {
|
|
6034
|
+
for (const f of manifest.presets) {
|
|
6035
|
+
allRefs.push({ ref: f, section: "presets" });
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
for (const { ref, section } of allRefs) {
|
|
6039
|
+
const fullPath = join22(rootDir, ref);
|
|
6040
|
+
try {
|
|
6041
|
+
await access4(fullPath);
|
|
6042
|
+
} catch {
|
|
6043
|
+
issues.push({
|
|
6044
|
+
severity: "error",
|
|
6045
|
+
path: `actant.json`,
|
|
6046
|
+
component: ref,
|
|
6047
|
+
message: `File referenced in ${section} does not exist: ${ref}`,
|
|
6048
|
+
code: "MANIFEST_FILE_MISSING"
|
|
6049
|
+
});
|
|
6050
|
+
}
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
6053
|
+
// -------------------------------------------------------------------------
|
|
6054
|
+
// Layer 2a: Validate files explicitly listed in manifest
|
|
6055
|
+
// -------------------------------------------------------------------------
|
|
6056
|
+
async validateExplicitFiles(rootDir, manifest, issues, componentNames, validatedFiles, compat) {
|
|
6057
|
+
let passCount = 0;
|
|
6058
|
+
const components = manifest.components;
|
|
6059
|
+
if (!components && !manifest.presets) return 0;
|
|
6060
|
+
if (components) {
|
|
6061
|
+
for (const [type, files] of Object.entries(components)) {
|
|
6062
|
+
if (!files) continue;
|
|
6063
|
+
for (const filePath of files) {
|
|
6064
|
+
validatedFiles.add(filePath);
|
|
6065
|
+
const ok = await this.validateSingleFile(rootDir, filePath, type, issues, componentNames);
|
|
6066
|
+
if (ok) passCount++;
|
|
6067
|
+
}
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
const presets = manifest.presets;
|
|
6071
|
+
if (presets) {
|
|
6072
|
+
for (const filePath of presets) {
|
|
6073
|
+
validatedFiles.add(filePath);
|
|
6074
|
+
const ok = await this.validateSingleFile(rootDir, filePath, "presets", issues, componentNames);
|
|
6075
|
+
if (ok) passCount++;
|
|
6076
|
+
}
|
|
6077
|
+
}
|
|
6078
|
+
void compat;
|
|
6079
|
+
return passCount;
|
|
6080
|
+
}
|
|
6081
|
+
// -------------------------------------------------------------------------
|
|
6082
|
+
// Layer 2b: Scan component directories
|
|
6083
|
+
// -------------------------------------------------------------------------
|
|
6084
|
+
async validateComponentDirs(rootDir, issues, componentNames, validatedFiles, compat) {
|
|
6085
|
+
let passCount = 0;
|
|
6086
|
+
for (const dir of COMPONENT_DIRS) {
|
|
6087
|
+
const dirPath = join22(rootDir, dir);
|
|
6088
|
+
try {
|
|
6089
|
+
const dirStat = await stat9(dirPath);
|
|
6090
|
+
if (!dirStat.isDirectory()) continue;
|
|
6091
|
+
} catch {
|
|
6092
|
+
continue;
|
|
6093
|
+
}
|
|
6094
|
+
passCount += await this.scanDirectory(rootDir, dirPath, dir, issues, componentNames, validatedFiles, compat);
|
|
6095
|
+
}
|
|
6096
|
+
return passCount;
|
|
6097
|
+
}
|
|
6098
|
+
async scanDirectory(rootDir, dirPath, componentType, issues, componentNames, validatedFiles, compat) {
|
|
6099
|
+
let passCount = 0;
|
|
6100
|
+
const entries = await readdir6(dirPath);
|
|
6101
|
+
for (const entry of entries) {
|
|
6102
|
+
const fullPath = join22(dirPath, entry);
|
|
6103
|
+
const relPath = relative2(rootDir, fullPath).replace(/\\/g, "/");
|
|
6104
|
+
const entryStat = await stat9(fullPath).catch(() => null);
|
|
6105
|
+
if (!entryStat) continue;
|
|
6106
|
+
if (entryStat.isFile() && extname4(entry) === ".json") {
|
|
6107
|
+
if (validatedFiles.has(relPath)) continue;
|
|
6108
|
+
validatedFiles.add(relPath);
|
|
6109
|
+
const ok = await this.validateSingleFile(rootDir, relPath, componentType, issues, componentNames);
|
|
6110
|
+
if (ok) passCount++;
|
|
6111
|
+
} else if (entryStat.isDirectory()) {
|
|
6112
|
+
if (componentType === "skills") {
|
|
6113
|
+
const skillMdPath = join22(fullPath, "SKILL.md");
|
|
6114
|
+
try {
|
|
6115
|
+
await access4(skillMdPath);
|
|
6116
|
+
const skillRelPath = relative2(rootDir, skillMdPath).replace(/\\/g, "/");
|
|
6117
|
+
if (!validatedFiles.has(skillRelPath)) {
|
|
6118
|
+
validatedFiles.add(skillRelPath);
|
|
6119
|
+
const ok = await this.validateSkillMd(rootDir, skillRelPath, issues, componentNames, entry, compat);
|
|
6120
|
+
if (ok) passCount++;
|
|
6121
|
+
}
|
|
6122
|
+
if (compat === "agent-skills") {
|
|
6123
|
+
await this.validateSkillDirConventions(rootDir, fullPath, entry, issues);
|
|
6124
|
+
}
|
|
6125
|
+
} catch {
|
|
6126
|
+
const manifestJsonPath = join22(fullPath, "manifest.json");
|
|
6127
|
+
try {
|
|
6128
|
+
await access4(manifestJsonPath);
|
|
6129
|
+
const mRelPath = relative2(rootDir, manifestJsonPath).replace(/\\/g, "/");
|
|
6130
|
+
if (!validatedFiles.has(mRelPath)) {
|
|
6131
|
+
validatedFiles.add(mRelPath);
|
|
6132
|
+
const ok = await this.validateSingleFile(rootDir, mRelPath, componentType, issues, componentNames);
|
|
6133
|
+
if (ok) passCount++;
|
|
6134
|
+
}
|
|
6135
|
+
} catch {
|
|
6136
|
+
}
|
|
6137
|
+
}
|
|
6138
|
+
}
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
return passCount;
|
|
6142
|
+
}
|
|
6143
|
+
// -------------------------------------------------------------------------
|
|
6144
|
+
// Single file validation
|
|
6145
|
+
// -------------------------------------------------------------------------
|
|
6146
|
+
async validateSingleFile(rootDir, relPath, componentType, issues, componentNames) {
|
|
6147
|
+
const fullPath = join22(rootDir, relPath);
|
|
6148
|
+
let raw;
|
|
6149
|
+
try {
|
|
6150
|
+
raw = await readFile8(fullPath, "utf-8");
|
|
6151
|
+
} catch {
|
|
6152
|
+
issues.push({
|
|
6153
|
+
severity: "error",
|
|
6154
|
+
path: relPath,
|
|
6155
|
+
message: "Cannot read file",
|
|
6156
|
+
code: "FILE_UNREADABLE"
|
|
6157
|
+
});
|
|
6158
|
+
return false;
|
|
6159
|
+
}
|
|
6160
|
+
let data;
|
|
6161
|
+
try {
|
|
6162
|
+
data = JSON.parse(raw);
|
|
6163
|
+
} catch (err) {
|
|
6164
|
+
issues.push({
|
|
6165
|
+
severity: "error",
|
|
6166
|
+
path: relPath,
|
|
6167
|
+
message: `Invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
6168
|
+
code: "INVALID_JSON"
|
|
6169
|
+
});
|
|
6170
|
+
return false;
|
|
6171
|
+
}
|
|
6172
|
+
if (componentType === "templates") {
|
|
6173
|
+
return this.validateTemplateComponent(relPath, data, issues, componentNames);
|
|
6174
|
+
}
|
|
6175
|
+
const schema = COMPONENT_DIR_SCHEMAS[componentType];
|
|
6176
|
+
if (!schema) return true;
|
|
6177
|
+
const result = schema.safeParse(data);
|
|
6178
|
+
if (!result.success) {
|
|
6179
|
+
for (const issue of result.error.issues) {
|
|
6180
|
+
issues.push({
|
|
6181
|
+
severity: "error",
|
|
6182
|
+
path: relPath,
|
|
6183
|
+
component: data?.name,
|
|
6184
|
+
message: `${issue.path.join(".")}: ${issue.message}`,
|
|
6185
|
+
code: "COMPONENT_SCHEMA"
|
|
6186
|
+
});
|
|
6187
|
+
}
|
|
6188
|
+
return false;
|
|
6189
|
+
}
|
|
6190
|
+
const parsed = result.data;
|
|
6191
|
+
this.trackComponentName(componentNames, componentType, parsed.name);
|
|
6192
|
+
if (!parsed.description) {
|
|
6193
|
+
issues.push({
|
|
6194
|
+
severity: "warning",
|
|
6195
|
+
path: relPath,
|
|
6196
|
+
component: parsed.name,
|
|
6197
|
+
message: `Missing "description" field`,
|
|
6198
|
+
code: "MISSING_DESCRIPTION"
|
|
6199
|
+
});
|
|
6200
|
+
}
|
|
6201
|
+
return true;
|
|
6202
|
+
}
|
|
6203
|
+
validateTemplateComponent(relPath, data, issues, componentNames) {
|
|
6204
|
+
const schemaResult = AgentTemplateSchema.safeParse(data);
|
|
6205
|
+
if (!schemaResult.success) {
|
|
6206
|
+
for (const issue of schemaResult.error.issues) {
|
|
6207
|
+
issues.push({
|
|
6208
|
+
severity: "error",
|
|
6209
|
+
path: relPath,
|
|
6210
|
+
component: data?.name,
|
|
6211
|
+
message: `${issue.path.join(".")}: ${issue.message}`,
|
|
6212
|
+
code: "TEMPLATE_SCHEMA"
|
|
6213
|
+
});
|
|
6214
|
+
}
|
|
6215
|
+
return false;
|
|
6216
|
+
}
|
|
6217
|
+
const semanticResult = validateTemplate(data);
|
|
6218
|
+
for (const w of semanticResult.warnings ?? []) {
|
|
6219
|
+
issues.push({
|
|
6220
|
+
severity: "warning",
|
|
6221
|
+
path: relPath,
|
|
6222
|
+
component: schemaResult.data.name,
|
|
6223
|
+
message: w.message,
|
|
6224
|
+
code: w.code ?? "TEMPLATE_SEMANTIC"
|
|
6225
|
+
});
|
|
6226
|
+
}
|
|
6227
|
+
this.trackComponentName(componentNames, "templates", schemaResult.data.name);
|
|
6228
|
+
return true;
|
|
6229
|
+
}
|
|
6230
|
+
// -------------------------------------------------------------------------
|
|
6231
|
+
// SKILL.md validation
|
|
6232
|
+
// -------------------------------------------------------------------------
|
|
6233
|
+
async validateSkillMd(rootDir, relPath, issues, componentNames, parentDirName, compat) {
|
|
6234
|
+
const fullPath = join22(rootDir, relPath);
|
|
6235
|
+
let raw;
|
|
6236
|
+
try {
|
|
6237
|
+
raw = await readFile8(fullPath, "utf-8");
|
|
6238
|
+
} catch {
|
|
6239
|
+
issues.push({
|
|
6240
|
+
severity: "error",
|
|
6241
|
+
path: relPath,
|
|
6242
|
+
message: "Cannot read SKILL.md file",
|
|
6243
|
+
code: "FILE_UNREADABLE"
|
|
6244
|
+
});
|
|
6245
|
+
return false;
|
|
6246
|
+
}
|
|
6247
|
+
const skill = parseSkillMdContent(raw);
|
|
6248
|
+
if (!skill) {
|
|
6249
|
+
issues.push({
|
|
6250
|
+
severity: "error",
|
|
6251
|
+
path: relPath,
|
|
6252
|
+
message: "Invalid SKILL.md: missing YAML frontmatter or required 'name' field",
|
|
6253
|
+
code: "SKILL_MD_INVALID"
|
|
6254
|
+
});
|
|
6255
|
+
return false;
|
|
6256
|
+
}
|
|
6257
|
+
this.trackComponentName(componentNames, "skills", skill.name);
|
|
6258
|
+
if (compat === "agent-skills") {
|
|
6259
|
+
this.validateAgentSkillsCompat(skill, relPath, issues, parentDirName, raw);
|
|
6260
|
+
} else {
|
|
6261
|
+
if (!skill.description) {
|
|
6262
|
+
issues.push({
|
|
6263
|
+
severity: "warning",
|
|
6264
|
+
path: relPath,
|
|
6265
|
+
component: skill.name,
|
|
6266
|
+
message: `Missing "description" in SKILL.md frontmatter`,
|
|
6267
|
+
code: "SKILL_MD_MISSING_DESCRIPTION"
|
|
6268
|
+
});
|
|
6269
|
+
}
|
|
6270
|
+
}
|
|
6271
|
+
if (!skill.content || skill.content.trim().length === 0) {
|
|
6272
|
+
issues.push({
|
|
6273
|
+
severity: "warning",
|
|
6274
|
+
path: relPath,
|
|
6275
|
+
component: skill.name,
|
|
6276
|
+
message: "SKILL.md has empty content body",
|
|
6277
|
+
code: "SKILL_MD_EMPTY_CONTENT"
|
|
6278
|
+
});
|
|
6279
|
+
}
|
|
6280
|
+
return true;
|
|
6281
|
+
}
|
|
6282
|
+
// -------------------------------------------------------------------------
|
|
6283
|
+
// Agent Skills (agentskills.io) compatibility checks
|
|
6284
|
+
// -------------------------------------------------------------------------
|
|
6285
|
+
validateAgentSkillsCompat(skill, relPath, issues, parentDirName, raw) {
|
|
6286
|
+
const name = skill.name;
|
|
6287
|
+
const NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
6288
|
+
if (name.length > 64) {
|
|
6289
|
+
issues.push({
|
|
6290
|
+
severity: "error",
|
|
6291
|
+
path: relPath,
|
|
6292
|
+
component: name,
|
|
6293
|
+
message: `Name exceeds 64 characters (${name.length})`,
|
|
6294
|
+
code: "AGENT_SKILLS_NAME_TOO_LONG"
|
|
6295
|
+
});
|
|
6296
|
+
} else if (!NAME_RE.test(name)) {
|
|
6297
|
+
issues.push({
|
|
6298
|
+
severity: "error",
|
|
6299
|
+
path: relPath,
|
|
6300
|
+
component: name,
|
|
6301
|
+
message: `Name must contain only lowercase letters, numbers, and hyphens, starting with a letter`,
|
|
6302
|
+
code: "AGENT_SKILLS_NAME_FORMAT"
|
|
6303
|
+
});
|
|
6304
|
+
} else {
|
|
6305
|
+
if (name.endsWith("-")) {
|
|
6306
|
+
issues.push({
|
|
6307
|
+
severity: "error",
|
|
6308
|
+
path: relPath,
|
|
6309
|
+
component: name,
|
|
6310
|
+
message: "Name must not end with a hyphen",
|
|
6311
|
+
code: "AGENT_SKILLS_NAME_TRAILING_HYPHEN"
|
|
6312
|
+
});
|
|
6313
|
+
}
|
|
6314
|
+
if (name.includes("--")) {
|
|
6315
|
+
issues.push({
|
|
6316
|
+
severity: "error",
|
|
6317
|
+
path: relPath,
|
|
6318
|
+
component: name,
|
|
6319
|
+
message: "Name must not contain consecutive hyphens",
|
|
6320
|
+
code: "AGENT_SKILLS_NAME_CONSECUTIVE_HYPHENS"
|
|
6321
|
+
});
|
|
6322
|
+
}
|
|
6323
|
+
}
|
|
6324
|
+
if (parentDirName && name !== parentDirName) {
|
|
6325
|
+
issues.push({
|
|
6326
|
+
severity: "error",
|
|
6327
|
+
path: relPath,
|
|
6328
|
+
component: name,
|
|
6329
|
+
message: `Name "${name}" does not match parent directory "${parentDirName}"`,
|
|
6330
|
+
code: "AGENT_SKILLS_NAME_DIR_MISMATCH"
|
|
6331
|
+
});
|
|
6332
|
+
}
|
|
6333
|
+
if (!skill.description) {
|
|
6334
|
+
issues.push({
|
|
6335
|
+
severity: "error",
|
|
6336
|
+
path: relPath,
|
|
6337
|
+
component: name,
|
|
6338
|
+
message: `Missing required "description" field (Agent Skills spec)`,
|
|
6339
|
+
code: "AGENT_SKILLS_DESCRIPTION_REQUIRED"
|
|
6340
|
+
});
|
|
6341
|
+
} else if (skill.description.length > 1024) {
|
|
6342
|
+
issues.push({
|
|
6343
|
+
severity: "warning",
|
|
6344
|
+
path: relPath,
|
|
6345
|
+
component: name,
|
|
6346
|
+
message: `Description exceeds 1024 characters (${skill.description.length})`,
|
|
6347
|
+
code: "AGENT_SKILLS_DESCRIPTION_TOO_LONG"
|
|
6348
|
+
});
|
|
6349
|
+
}
|
|
6350
|
+
if (skill.compatibility && skill.compatibility.length > 500) {
|
|
6351
|
+
issues.push({
|
|
6352
|
+
severity: "warning",
|
|
6353
|
+
path: relPath,
|
|
6354
|
+
component: name,
|
|
6355
|
+
message: `Compatibility field exceeds 500 characters (${skill.compatibility.length})`,
|
|
6356
|
+
code: "AGENT_SKILLS_COMPAT_TOO_LONG"
|
|
6357
|
+
});
|
|
6358
|
+
}
|
|
6359
|
+
if (raw) {
|
|
6360
|
+
const frontmatterEnd = raw.indexOf("---", 3);
|
|
6361
|
+
if (frontmatterEnd !== -1) {
|
|
6362
|
+
const body = raw.substring(frontmatterEnd + 3).trim();
|
|
6363
|
+
const lineCount = body.split("\n").length;
|
|
6364
|
+
if (lineCount > 500) {
|
|
6365
|
+
issues.push({
|
|
6366
|
+
severity: "warning",
|
|
6367
|
+
path: relPath,
|
|
6368
|
+
component: name,
|
|
6369
|
+
message: `SKILL.md body has ${lineCount} lines; Agent Skills spec recommends < 500 lines`,
|
|
6370
|
+
code: "AGENT_SKILLS_BODY_TOO_LONG"
|
|
6371
|
+
});
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6374
|
+
}
|
|
6375
|
+
}
|
|
6376
|
+
// -------------------------------------------------------------------------
|
|
6377
|
+
// Agent Skills directory convention (scripts/, references/, assets/)
|
|
6378
|
+
// -------------------------------------------------------------------------
|
|
6379
|
+
async validateSkillDirConventions(rootDir, skillDir, skillName, issues) {
|
|
6380
|
+
const KNOWN_DIRS = /* @__PURE__ */ new Set(["scripts", "references", "assets"]);
|
|
6381
|
+
const KNOWN_FILES = /* @__PURE__ */ new Set(["SKILL.md", "LICENSE", "LICENSE.txt", "LICENSE.md"]);
|
|
6382
|
+
let entries;
|
|
6383
|
+
try {
|
|
6384
|
+
entries = await readdir6(skillDir);
|
|
6385
|
+
} catch {
|
|
6386
|
+
return;
|
|
6387
|
+
}
|
|
6388
|
+
for (const entry of entries) {
|
|
6389
|
+
const fullPath = join22(skillDir, entry);
|
|
6390
|
+
const relPath = relative2(rootDir, fullPath).replace(/\\/g, "/");
|
|
6391
|
+
const entryStat = await stat9(fullPath).catch(() => null);
|
|
6392
|
+
if (!entryStat) continue;
|
|
6393
|
+
if (entryStat.isDirectory()) {
|
|
6394
|
+
if (KNOWN_DIRS.has(entry)) {
|
|
6395
|
+
issues.push({
|
|
6396
|
+
severity: "info",
|
|
6397
|
+
path: relPath,
|
|
6398
|
+
component: skillName,
|
|
6399
|
+
message: `Agent Skills convention: ${entry}/ directory detected`,
|
|
6400
|
+
code: "AGENT_SKILLS_DIR_FOUND"
|
|
6401
|
+
});
|
|
6402
|
+
}
|
|
6403
|
+
} else if (!KNOWN_FILES.has(entry)) {
|
|
6404
|
+
}
|
|
6405
|
+
}
|
|
6406
|
+
}
|
|
6407
|
+
// -------------------------------------------------------------------------
|
|
6408
|
+
// Layer 3: Cross-reference validation
|
|
6409
|
+
// -------------------------------------------------------------------------
|
|
6410
|
+
async validatePresetReferences(rootDir, manifest, componentNames, issues) {
|
|
6411
|
+
const presetsDir = join22(rootDir, "presets");
|
|
6412
|
+
const presetFiles = [];
|
|
6413
|
+
if (manifest?.presets) {
|
|
6414
|
+
for (const p of manifest.presets) {
|
|
6415
|
+
presetFiles.push(p);
|
|
6416
|
+
}
|
|
6417
|
+
} else {
|
|
6418
|
+
try {
|
|
6419
|
+
const entries = await readdir6(presetsDir);
|
|
6420
|
+
for (const e of entries) {
|
|
6421
|
+
if (extname4(e) === ".json") {
|
|
6422
|
+
presetFiles.push(`presets/${e}`);
|
|
6423
|
+
}
|
|
6424
|
+
}
|
|
6425
|
+
} catch {
|
|
6426
|
+
return;
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
for (const presetFile of presetFiles) {
|
|
6430
|
+
const fullPath = join22(rootDir, presetFile);
|
|
6431
|
+
let data;
|
|
6432
|
+
try {
|
|
6433
|
+
const raw = await readFile8(fullPath, "utf-8");
|
|
6434
|
+
data = JSON.parse(raw);
|
|
6435
|
+
} catch {
|
|
6436
|
+
continue;
|
|
6437
|
+
}
|
|
6438
|
+
const presetName = data.name || presetFile;
|
|
6439
|
+
const refMap = [
|
|
6440
|
+
["skills", "skills"],
|
|
6441
|
+
["prompts", "prompts"],
|
|
6442
|
+
["mcpServers", "mcp"],
|
|
6443
|
+
["workflows", "workflows"],
|
|
6444
|
+
["templates", "templates"]
|
|
6445
|
+
];
|
|
6446
|
+
for (const [field, compType] of refMap) {
|
|
6447
|
+
const refs = data[field];
|
|
6448
|
+
if (!refs) continue;
|
|
6449
|
+
const available = componentNames.get(compType) ?? /* @__PURE__ */ new Set();
|
|
6450
|
+
for (const ref of refs) {
|
|
6451
|
+
if (!available.has(ref)) {
|
|
6452
|
+
issues.push({
|
|
6453
|
+
severity: "warning",
|
|
6454
|
+
path: presetFile,
|
|
6455
|
+
component: presetName,
|
|
6456
|
+
message: `Preset references ${compType} "${ref}" which was not found in this source`,
|
|
6457
|
+
code: "PRESET_REF_MISSING"
|
|
6458
|
+
});
|
|
6459
|
+
}
|
|
6460
|
+
}
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
// -------------------------------------------------------------------------
|
|
6465
|
+
// Helpers
|
|
6466
|
+
// -------------------------------------------------------------------------
|
|
6467
|
+
trackComponentName(componentNames, type, name) {
|
|
6468
|
+
let set = componentNames.get(type);
|
|
6469
|
+
if (!set) {
|
|
6470
|
+
set = /* @__PURE__ */ new Set();
|
|
6471
|
+
componentNames.set(type, set);
|
|
6472
|
+
}
|
|
6473
|
+
set.add(name);
|
|
6474
|
+
}
|
|
6475
|
+
};
|
|
6476
|
+
|
|
6477
|
+
// src/provider/builtin-providers.ts
|
|
6478
|
+
var BUILTIN_PROVIDERS = [
|
|
6479
|
+
{
|
|
6480
|
+
type: "anthropic",
|
|
6481
|
+
displayName: "Anthropic (Claude)",
|
|
6482
|
+
protocol: "anthropic",
|
|
6483
|
+
defaultBaseUrl: "https://api.anthropic.com",
|
|
6484
|
+
models: ["claude-sonnet-4-20250514", "claude-opus-4-20250514"]
|
|
6485
|
+
},
|
|
6486
|
+
{
|
|
6487
|
+
type: "openai",
|
|
6488
|
+
displayName: "OpenAI",
|
|
6489
|
+
protocol: "openai",
|
|
6490
|
+
defaultBaseUrl: "https://api.openai.com/v1",
|
|
6491
|
+
models: ["gpt-4o", "gpt-4o-mini", "o3-mini"]
|
|
6492
|
+
},
|
|
6493
|
+
{
|
|
6494
|
+
type: "deepseek",
|
|
6495
|
+
displayName: "DeepSeek",
|
|
6496
|
+
protocol: "openai",
|
|
6497
|
+
defaultBaseUrl: "https://api.deepseek.com/v1",
|
|
6498
|
+
models: ["deepseek-chat", "deepseek-reasoner"]
|
|
6499
|
+
},
|
|
6500
|
+
{
|
|
6501
|
+
type: "ollama",
|
|
6502
|
+
displayName: "Ollama (Local)",
|
|
6503
|
+
protocol: "openai",
|
|
6504
|
+
defaultBaseUrl: "http://localhost:11434/v1"
|
|
6505
|
+
},
|
|
6506
|
+
{
|
|
6507
|
+
type: "azure",
|
|
6508
|
+
displayName: "Azure OpenAI",
|
|
6509
|
+
protocol: "openai",
|
|
6510
|
+
defaultBaseUrl: "https://<resource>.openai.azure.com"
|
|
6511
|
+
},
|
|
6512
|
+
{
|
|
6513
|
+
type: "bedrock",
|
|
6514
|
+
displayName: "AWS Bedrock",
|
|
6515
|
+
protocol: "anthropic",
|
|
6516
|
+
defaultBaseUrl: "https://bedrock-runtime.<region>.amazonaws.com"
|
|
6517
|
+
},
|
|
6518
|
+
{
|
|
6519
|
+
type: "vertex",
|
|
6520
|
+
displayName: "Google Vertex AI",
|
|
6521
|
+
protocol: "anthropic",
|
|
6522
|
+
defaultBaseUrl: "https://<region>-aiplatform.googleapis.com"
|
|
6523
|
+
},
|
|
6524
|
+
{
|
|
6525
|
+
type: "custom",
|
|
6526
|
+
displayName: "Custom",
|
|
6527
|
+
protocol: "custom",
|
|
6528
|
+
defaultBaseUrl: "http://localhost:8080"
|
|
6529
|
+
}
|
|
6530
|
+
];
|
|
6531
|
+
function registerBuiltinProviders() {
|
|
6532
|
+
for (const descriptor of BUILTIN_PROVIDERS) {
|
|
6533
|
+
modelProviderRegistry.register(descriptor);
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
5407
6536
|
export {
|
|
5408
6537
|
AgentBackendSchema,
|
|
5409
6538
|
AgentInitializer,
|
|
@@ -5411,6 +6540,8 @@ export {
|
|
|
5411
6540
|
AgentManager,
|
|
5412
6541
|
AgentStatusSchema,
|
|
5413
6542
|
AgentTemplateSchema,
|
|
6543
|
+
BUILTIN_PROVIDERS,
|
|
6544
|
+
BackendManager,
|
|
5414
6545
|
BaseComponentManager,
|
|
5415
6546
|
ClaudeCodeBuilder,
|
|
5416
6547
|
ClaudeCodeCommunicator,
|
|
@@ -5447,6 +6578,7 @@ export {
|
|
|
5447
6578
|
McpServerRefSchema,
|
|
5448
6579
|
MkdirStep,
|
|
5449
6580
|
MockLauncher,
|
|
6581
|
+
ModelProviderRegistry,
|
|
5450
6582
|
ModelProviderSchema,
|
|
5451
6583
|
NpmInstallStep,
|
|
5452
6584
|
PermissionAuditLogger,
|
|
@@ -5464,6 +6596,7 @@ export {
|
|
|
5464
6596
|
SessionRegistry,
|
|
5465
6597
|
SkillManager,
|
|
5466
6598
|
SourceManager,
|
|
6599
|
+
SourceValidator,
|
|
5467
6600
|
StepRegistry,
|
|
5468
6601
|
TaskDispatcher,
|
|
5469
6602
|
TaskQueue,
|
|
@@ -5476,15 +6609,20 @@ export {
|
|
|
5476
6609
|
createDefaultStepRegistry,
|
|
5477
6610
|
createLauncher,
|
|
5478
6611
|
getBackendDescriptor,
|
|
6612
|
+
getBackendManager,
|
|
6613
|
+
getInstallHint,
|
|
5479
6614
|
getLaunchModeHandler,
|
|
5480
6615
|
globMatch,
|
|
5481
6616
|
isAcpBackend,
|
|
5482
6617
|
isAcpOnlyBackend,
|
|
5483
6618
|
isProcessAlive,
|
|
5484
6619
|
metaFilePath,
|
|
6620
|
+
modelProviderRegistry,
|
|
5485
6621
|
openBackend,
|
|
5486
6622
|
readInstanceMeta,
|
|
5487
6623
|
registerBackend,
|
|
6624
|
+
registerBackendDefinition,
|
|
6625
|
+
registerBuiltinProviders,
|
|
5488
6626
|
registerCommunicator,
|
|
5489
6627
|
requireMode,
|
|
5490
6628
|
resolveAcpBackend,
|