@agentskit/cli 0.13.37 → 0.13.39
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/bin.cjs +389 -146
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-DZKCRKFS.js → chunk-AL4GKGCU.js} +396 -153
- package/dist/chunk-AL4GKGCU.js.map +1 -0
- package/dist/index.cjs +390 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +1 -1
- package/package.json +25 -21
- package/LICENSE +0 -21
- package/dist/chunk-DZKCRKFS.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var url = require('url');
|
|
|
21
21
|
var runtime = require('@agentskit/runtime');
|
|
22
22
|
var prompts = require('@inquirer/prompts');
|
|
23
23
|
var kleur = require('kleur');
|
|
24
|
+
var module$1 = require('module');
|
|
24
25
|
var chokidar = require('chokidar');
|
|
25
26
|
var rag = require('@agentskit/rag');
|
|
26
27
|
var agentSchema = require('@agentskit/core/agent-schema');
|
|
@@ -28,6 +29,7 @@ var yaml = require('yaml');
|
|
|
28
29
|
var security = require('@agentskit/core/security');
|
|
29
30
|
var core = require('@agentskit/core');
|
|
30
31
|
|
|
32
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
31
33
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
32
34
|
|
|
33
35
|
var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
@@ -35,20 +37,30 @@ var path__default = /*#__PURE__*/_interopDefault(path);
|
|
|
35
37
|
var kleur__default = /*#__PURE__*/_interopDefault(kleur);
|
|
36
38
|
var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
// package.json
|
|
41
|
+
var package_default = {
|
|
42
|
+
version: "0.13.39"};
|
|
43
|
+
async function loadJsonConfig(path6) {
|
|
39
44
|
try {
|
|
40
|
-
const raw = await promises.readFile(
|
|
45
|
+
const raw = await promises.readFile(path6, "utf8");
|
|
41
46
|
return JSON.parse(raw);
|
|
42
|
-
} catch {
|
|
43
|
-
return void 0;
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err.code === "ENOENT") return void 0;
|
|
49
|
+
throw new Error(`Failed to load JSON config ${path6}: ${err instanceof Error ? err.message : String(err)}`);
|
|
44
50
|
}
|
|
45
51
|
}
|
|
46
|
-
async function loadTsConfig(
|
|
52
|
+
async function loadTsConfig(path6) {
|
|
53
|
+
try {
|
|
54
|
+
await promises.access(path6);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (err.code === "ENOENT") return void 0;
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
47
59
|
try {
|
|
48
|
-
const mod = await import(
|
|
60
|
+
const mod = await import(`${path6}?agentskit_config=${Date.now()}`);
|
|
49
61
|
return mod.default ?? mod;
|
|
50
|
-
} catch {
|
|
51
|
-
|
|
62
|
+
} catch (err) {
|
|
63
|
+
throw new Error(`Failed to load TypeScript config ${path6}: ${err instanceof Error ? err.message : String(err)}`);
|
|
52
64
|
}
|
|
53
65
|
}
|
|
54
66
|
async function loadPackageJsonConfig(dir) {
|
|
@@ -59,29 +71,31 @@ async function loadPackageJsonConfig(dir) {
|
|
|
59
71
|
return pkg.agentskit;
|
|
60
72
|
}
|
|
61
73
|
return void 0;
|
|
62
|
-
} catch {
|
|
63
|
-
return void 0;
|
|
74
|
+
} catch (err) {
|
|
75
|
+
if (err.code === "ENOENT") return void 0;
|
|
76
|
+
throw new Error(`Failed to load package.json config ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
64
77
|
}
|
|
65
78
|
}
|
|
66
79
|
function mergeConfigs(base, override) {
|
|
67
80
|
if (!base && !override) return void 0;
|
|
68
81
|
if (!base) return override;
|
|
69
82
|
if (!override) return base;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
const merge = (left, right) => {
|
|
84
|
+
if (!left || typeof left !== "object" || Array.isArray(left)) return right;
|
|
85
|
+
if (!right || typeof right !== "object" || Array.isArray(right)) return right;
|
|
86
|
+
const out = { ...left };
|
|
87
|
+
for (const [key, value] of Object.entries(right)) {
|
|
88
|
+
out[key] = key in out ? merge(out[key], value) : value;
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
77
91
|
};
|
|
92
|
+
return merge(base, override);
|
|
78
93
|
}
|
|
79
94
|
async function loadLocalConfig(cwd) {
|
|
80
95
|
const tsConfig = await loadTsConfig(path.join(cwd, ".agentskit.config.ts"));
|
|
81
|
-
if (tsConfig) return tsConfig;
|
|
82
96
|
const jsonConfig = await loadJsonConfig(path.join(cwd, ".agentskit.config.json"));
|
|
83
|
-
|
|
84
|
-
return
|
|
97
|
+
const packageConfig = await loadPackageJsonConfig(cwd);
|
|
98
|
+
return mergeConfigs(mergeConfigs(packageConfig, jsonConfig), tsConfig);
|
|
85
99
|
}
|
|
86
100
|
async function loadGlobalConfig(home) {
|
|
87
101
|
if (home === null) return void 0;
|
|
@@ -96,6 +110,21 @@ async function loadConfig(options) {
|
|
|
96
110
|
const local = await loadLocalConfig(cwd);
|
|
97
111
|
return mergeConfigs(global, local);
|
|
98
112
|
}
|
|
113
|
+
var SECRET_KEY = /^(api[-_]?key|token|secret|password|authorization|private[-_]?key)$/i;
|
|
114
|
+
var SECRET_ENV_KEY = /(?:^|_)(?:api_?key|token|secret|password|authorization|private_?key)$/i;
|
|
115
|
+
function isSecretKey(key) {
|
|
116
|
+
return SECRET_KEY.test(key) || SECRET_ENV_KEY.test(key);
|
|
117
|
+
}
|
|
118
|
+
function redactConfig(value) {
|
|
119
|
+
if (Array.isArray(value)) return value.map(redactConfig);
|
|
120
|
+
if (!value || typeof value !== "object") return value;
|
|
121
|
+
return Object.fromEntries(
|
|
122
|
+
Object.entries(value).map(([key, child]) => [
|
|
123
|
+
key,
|
|
124
|
+
isSecretKey(key) ? "[REDACTED]" : redactConfig(child)
|
|
125
|
+
])
|
|
126
|
+
);
|
|
127
|
+
}
|
|
99
128
|
var providers = {
|
|
100
129
|
openai: {
|
|
101
130
|
label: "OpenAI",
|
|
@@ -167,7 +196,7 @@ function createDemoAdapter(provider, model) {
|
|
|
167
196
|
].join(" ");
|
|
168
197
|
for (const chunk of reply.match(/.{1,18}/g) ?? []) {
|
|
169
198
|
if (cancelled) return;
|
|
170
|
-
await new Promise((
|
|
199
|
+
await new Promise((resolve8) => setTimeout(resolve8, 35));
|
|
171
200
|
yield { type: "text", content: chunk };
|
|
172
201
|
}
|
|
173
202
|
yield { type: "done" };
|
|
@@ -227,7 +256,8 @@ function dirFor(cwd = process.cwd()) {
|
|
|
227
256
|
return path.join(ROOT, cwdHash(cwd));
|
|
228
257
|
}
|
|
229
258
|
function ensureDir(dir) {
|
|
230
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
259
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
260
|
+
fs.chmodSync(dir, 448);
|
|
231
261
|
}
|
|
232
262
|
function generateSessionId() {
|
|
233
263
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
@@ -242,17 +272,19 @@ function metaPath(id, cwd = process.cwd()) {
|
|
|
242
272
|
return path.join(dirFor(cwd), `${id}${META_SUFFIX}`);
|
|
243
273
|
}
|
|
244
274
|
function readMeta(id, cwd = process.cwd()) {
|
|
245
|
-
const
|
|
246
|
-
if (!fs.existsSync(
|
|
275
|
+
const path6 = metaPath(id, cwd);
|
|
276
|
+
if (!fs.existsSync(path6)) return null;
|
|
247
277
|
try {
|
|
248
|
-
return JSON.parse(fs.readFileSync(
|
|
278
|
+
return JSON.parse(fs.readFileSync(path6, "utf8"));
|
|
249
279
|
} catch {
|
|
250
280
|
return null;
|
|
251
281
|
}
|
|
252
282
|
}
|
|
253
283
|
function writeSessionMeta(meta, cwd = process.cwd()) {
|
|
254
284
|
ensureDir(dirFor(cwd));
|
|
255
|
-
|
|
285
|
+
const path6 = metaPath(meta.id, cwd);
|
|
286
|
+
fs.writeFileSync(path6, JSON.stringify(meta, null, 2), { mode: 384 });
|
|
287
|
+
fs.chmodSync(path6, 384);
|
|
256
288
|
}
|
|
257
289
|
function derivePreview(messages) {
|
|
258
290
|
const firstUser = messages.find((m) => m.role === "user" && m.content.trim());
|
|
@@ -861,7 +893,7 @@ var HookDispatcher = class {
|
|
|
861
893
|
result = await handler.run(current);
|
|
862
894
|
} catch (err) {
|
|
863
895
|
this.onError(handler, err);
|
|
864
|
-
|
|
896
|
+
return { payload: current, blocked: true, reason: "hook failed" };
|
|
865
897
|
}
|
|
866
898
|
if (result.decision === "block") {
|
|
867
899
|
return { payload: current, blocked: true, reason: result.reason };
|
|
@@ -878,6 +910,7 @@ var HookDispatcher = class {
|
|
|
878
910
|
return handler.matcher.test(String(payload.tool ?? payload.prompt ?? ""));
|
|
879
911
|
}
|
|
880
912
|
};
|
|
913
|
+
var MAX_HOOK_STDOUT_BYTES = 64 * 1024;
|
|
881
914
|
function configHooksToHandlers(config) {
|
|
882
915
|
if (!config) return [];
|
|
883
916
|
const handlers = [];
|
|
@@ -907,8 +940,26 @@ function runShellHook(entry, payload) {
|
|
|
907
940
|
detached: true
|
|
908
941
|
});
|
|
909
942
|
let stdout = "";
|
|
943
|
+
let stdoutBytes = 0;
|
|
910
944
|
child.stdout.on("data", (chunk) => {
|
|
911
|
-
|
|
945
|
+
const text = chunk.toString();
|
|
946
|
+
const bytes = Buffer.byteLength(text);
|
|
947
|
+
if (stdoutBytes + bytes > MAX_HOOK_STDOUT_BYTES) {
|
|
948
|
+
if (child.pid !== void 0) {
|
|
949
|
+
try {
|
|
950
|
+
process.kill(-child.pid, "SIGKILL");
|
|
951
|
+
} catch {
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
try {
|
|
955
|
+
child.kill("SIGKILL");
|
|
956
|
+
} catch {
|
|
957
|
+
}
|
|
958
|
+
settle({ decision: "block", reason: `shell hook output exceeded ${MAX_HOOK_STDOUT_BYTES} bytes` });
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
stdoutBytes += bytes;
|
|
962
|
+
stdout += text;
|
|
912
963
|
});
|
|
913
964
|
const timer = setTimeout(() => {
|
|
914
965
|
if (child.pid !== void 0) {
|
|
@@ -940,9 +991,13 @@ function runShellHook(entry, payload) {
|
|
|
940
991
|
}
|
|
941
992
|
try {
|
|
942
993
|
const parsed = JSON.parse(trimmed);
|
|
943
|
-
|
|
994
|
+
if (!parsed || typeof parsed !== "object" || !("decision" in parsed) || !["continue", "block", "modify"].includes(String(parsed.decision))) {
|
|
995
|
+
settle({ decision: "block", reason: "shell hook returned an invalid decision" });
|
|
996
|
+
} else {
|
|
997
|
+
settle(parsed);
|
|
998
|
+
}
|
|
944
999
|
} catch {
|
|
945
|
-
settle({ decision: "
|
|
1000
|
+
settle({ decision: "block", reason: "shell hook returned invalid JSON" });
|
|
946
1001
|
}
|
|
947
1002
|
});
|
|
948
1003
|
child.on("error", (err) => {
|
|
@@ -989,8 +1044,9 @@ function ChatApp(options) {
|
|
|
989
1044
|
} = useRuntime(options);
|
|
990
1045
|
const mergedTools = React2.useMemo(() => {
|
|
991
1046
|
const extra = options.extraTools ?? [];
|
|
992
|
-
|
|
993
|
-
|
|
1047
|
+
const combined = [...tools, ...extra];
|
|
1048
|
+
return options.permissionPolicy ? applyPolicyToTools(options.permissionPolicy, combined) : combined;
|
|
1049
|
+
}, [tools, options.extraTools, options.permissionPolicy]);
|
|
994
1050
|
const mergedSkills = React2.useMemo(() => {
|
|
995
1051
|
const extra = options.extraSkills ?? [];
|
|
996
1052
|
if (!skills && extra.length === 0) return void 0;
|
|
@@ -1202,7 +1258,7 @@ async function loadPlugins(options = {}) {
|
|
|
1202
1258
|
specs = [],
|
|
1203
1259
|
pluginDirs = [],
|
|
1204
1260
|
cwd = process.cwd(),
|
|
1205
|
-
autoDiscoverUserDir =
|
|
1261
|
+
autoDiscoverUserDir = false,
|
|
1206
1262
|
onError = (spec, err) => process.stderr.write(
|
|
1207
1263
|
`[agentskit] plugin "${spec}" failed to load: ${err instanceof Error ? err.message : String(err)}
|
|
1208
1264
|
`
|
|
@@ -1293,6 +1349,8 @@ function mergePluginsIntoBundle(plugins) {
|
|
|
1293
1349
|
}
|
|
1294
1350
|
return bundle;
|
|
1295
1351
|
}
|
|
1352
|
+
var MAX_FRAME_BYTES = 1024 * 1024;
|
|
1353
|
+
var DISPOSE_GRACE_MS = 1e3;
|
|
1296
1354
|
var McpClient = class {
|
|
1297
1355
|
constructor(spec, onError = (err) => process.stderr.write(
|
|
1298
1356
|
`[agentskit] mcp[${spec.name}] error: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1309,7 +1367,8 @@ var McpClient = class {
|
|
|
1309
1367
|
if (this.child) return;
|
|
1310
1368
|
const child = child_process.spawn(this.spec.command, this.spec.args ?? [], {
|
|
1311
1369
|
env: { ...process.env, ...this.spec.env ?? {} },
|
|
1312
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1370
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1371
|
+
detached: process.platform !== "win32"
|
|
1313
1372
|
});
|
|
1314
1373
|
this.child = child;
|
|
1315
1374
|
child.stdout.on("data", (chunk) => this.onStdout(chunk.toString()));
|
|
@@ -1341,7 +1400,22 @@ var McpClient = class {
|
|
|
1341
1400
|
dispose() {
|
|
1342
1401
|
if (this.disposed) return;
|
|
1343
1402
|
this.disposed = true;
|
|
1344
|
-
this.
|
|
1403
|
+
for (const pending of this.pending.values()) {
|
|
1404
|
+
pending({ jsonrpc: "2.0", id: 0, error: { code: -32800, message: "client disposed" } });
|
|
1405
|
+
}
|
|
1406
|
+
this.pending.clear();
|
|
1407
|
+
const child = this.child;
|
|
1408
|
+
if (!child || child.killed) return;
|
|
1409
|
+
const kill = (signal) => {
|
|
1410
|
+
try {
|
|
1411
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, signal);
|
|
1412
|
+
else child.kill(signal);
|
|
1413
|
+
} catch {
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
kill("SIGTERM");
|
|
1417
|
+
const timer = setTimeout(() => kill("SIGKILL"), DISPOSE_GRACE_MS);
|
|
1418
|
+
timer.unref();
|
|
1345
1419
|
}
|
|
1346
1420
|
request(method, params) {
|
|
1347
1421
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
@@ -1369,10 +1443,20 @@ var McpClient = class {
|
|
|
1369
1443
|
}
|
|
1370
1444
|
onStdout(chunk) {
|
|
1371
1445
|
this.buffer += chunk;
|
|
1446
|
+
if (Buffer.byteLength(this.buffer, "utf8") > MAX_FRAME_BYTES && !this.buffer.includes("\n")) {
|
|
1447
|
+
this.onError(new Error(`mcp ${this.spec.name} frame exceeded ${MAX_FRAME_BYTES} bytes`));
|
|
1448
|
+
this.dispose();
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1372
1451
|
let newlineIndex = this.buffer.indexOf("\n");
|
|
1373
1452
|
while (newlineIndex !== -1) {
|
|
1374
1453
|
const line = this.buffer.slice(0, newlineIndex).trim();
|
|
1375
1454
|
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
1455
|
+
if (Buffer.byteLength(line, "utf8") > MAX_FRAME_BYTES) {
|
|
1456
|
+
this.onError(new Error(`mcp ${this.spec.name} frame exceeded ${MAX_FRAME_BYTES} bytes`));
|
|
1457
|
+
this.dispose();
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1376
1460
|
if (line) {
|
|
1377
1461
|
try {
|
|
1378
1462
|
const parsed = JSON.parse(line);
|
|
@@ -1434,7 +1518,7 @@ function registerChatCommand(program) {
|
|
|
1434
1518
|
"Extra directory to auto-discover plugin modules from (repeatable)",
|
|
1435
1519
|
(value, prev = []) => [...prev, value],
|
|
1436
1520
|
[]
|
|
1437
|
-
).option(
|
|
1521
|
+
).option("--discover-plugins", "Also auto-discover plugins from ~/.agentskit/plugins").option(
|
|
1438
1522
|
"--mode <mode>",
|
|
1439
1523
|
"Permission mode: default | plan | acceptEdits | bypassPermissions"
|
|
1440
1524
|
).action(async (options) => {
|
|
@@ -1471,7 +1555,8 @@ function registerChatCommand(program) {
|
|
|
1471
1555
|
}
|
|
1472
1556
|
const pluginBundle = await loadPlugins({
|
|
1473
1557
|
specs: config?.plugins ?? [],
|
|
1474
|
-
pluginDirs: options.pluginDir ?? []
|
|
1558
|
+
pluginDirs: options.pluginDir ?? [],
|
|
1559
|
+
autoDiscoverUserDir: Boolean(options.discoverPlugins)
|
|
1475
1560
|
});
|
|
1476
1561
|
const configHooks = configHooksToHandlers(config?.hooks);
|
|
1477
1562
|
const hookHandlers = [...configHooks, ...pluginBundle.hooks];
|
|
@@ -1559,8 +1644,7 @@ function formatEvent(event) {
|
|
|
1559
1644
|
}
|
|
1560
1645
|
async function runAgent(task, options) {
|
|
1561
1646
|
if (options.skill && options.skills) {
|
|
1562
|
-
|
|
1563
|
-
process.exit(1);
|
|
1647
|
+
throw new Error("--skill and --skills are mutually exclusive. Use one or the other.");
|
|
1564
1648
|
}
|
|
1565
1649
|
const { adapter } = resolveChatProvider({
|
|
1566
1650
|
provider: options.provider,
|
|
@@ -1752,6 +1836,69 @@ var PROVIDER_ENV_KEY = {
|
|
|
1752
1836
|
openrouter: "OPENROUTER_API_KEY",
|
|
1753
1837
|
demo: null
|
|
1754
1838
|
};
|
|
1839
|
+
var PACKAGE_RANGES = {
|
|
1840
|
+
"@agentskit/adapters": "^0.15.2",
|
|
1841
|
+
"@agentskit/angular": "^0.5.3",
|
|
1842
|
+
"@agentskit/ink": "^0.10.9",
|
|
1843
|
+
"@agentskit/memory": "^0.11.8",
|
|
1844
|
+
"@agentskit/react": "^0.8.3",
|
|
1845
|
+
"@agentskit/runtime": "^0.10.15",
|
|
1846
|
+
"@agentskit/skills": "^0.9.3",
|
|
1847
|
+
"@agentskit/svelte": "^0.5.3",
|
|
1848
|
+
"@agentskit/tools": "^0.13.6",
|
|
1849
|
+
"@agentskit/vue": "^0.5.3"
|
|
1850
|
+
};
|
|
1851
|
+
function align(files) {
|
|
1852
|
+
const result = { ...files };
|
|
1853
|
+
for (const file of ["package.json", "deno.json"]) {
|
|
1854
|
+
if (!files[file]) continue;
|
|
1855
|
+
const parsed = JSON.parse(files[file]);
|
|
1856
|
+
const sections = file === "package.json" ? ["dependencies", "devDependencies", "peerDependencies"] : ["imports"];
|
|
1857
|
+
for (const sectionName of sections) {
|
|
1858
|
+
const section = parsed[sectionName];
|
|
1859
|
+
if (!section || typeof section !== "object" || Array.isArray(section)) continue;
|
|
1860
|
+
for (const [name, range] of Object.entries(PACKAGE_RANGES)) if (name in section) {
|
|
1861
|
+
section[name] = file === "deno.json" ? `npm:${name}@${range}` : range;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
result[file] = `${JSON.stringify(parsed, null, 2)}
|
|
1865
|
+
`;
|
|
1866
|
+
}
|
|
1867
|
+
return result;
|
|
1868
|
+
}
|
|
1869
|
+
async function existing(filePath) {
|
|
1870
|
+
try {
|
|
1871
|
+
return await promises.lstat(filePath);
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
if (error.code === "ENOENT") return void 0;
|
|
1874
|
+
throw error;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
async function writeStarterProject(options, renderers) {
|
|
1878
|
+
const ctx = { template: options.template, provider: options.provider ?? "demo", tools: options.tools ?? [], memory: options.memory ?? "none", pm: options.packageManager ?? "pnpm" };
|
|
1879
|
+
const files = align(renderers[ctx.template](ctx));
|
|
1880
|
+
await promises.mkdir(options.targetDir, { recursive: true });
|
|
1881
|
+
const target = await promises.lstat(options.targetDir);
|
|
1882
|
+
if (target.isSymbolicLink() || !target.isDirectory()) throw new Error(`Refusing to write starter into non-directory target: ${options.targetDir}`);
|
|
1883
|
+
if ((await promises.readdir(options.targetDir)).length > 0 && !options.force) throw new Error(`Target directory is not empty: ${options.targetDir} (re-run with --force to overwrite generated files)`);
|
|
1884
|
+
const overwritten = [];
|
|
1885
|
+
for (const [relativePath, content] of Object.entries(files)) {
|
|
1886
|
+
const absolutePath = path__default.default.join(options.targetDir, relativePath);
|
|
1887
|
+
await promises.mkdir(path__default.default.dirname(absolutePath), { recursive: true });
|
|
1888
|
+
const destination = await existing(absolutePath);
|
|
1889
|
+
if (destination?.isSymbolicLink()) throw new Error(`Refusing to overwrite symlink: ${absolutePath}`);
|
|
1890
|
+
if (destination) overwritten.push(relativePath);
|
|
1891
|
+
const tempDir = await promises.mkdtemp(path__default.default.join(path__default.default.dirname(absolutePath), `.agentskit-init-${crypto.randomUUID()}-`));
|
|
1892
|
+
try {
|
|
1893
|
+
await promises.writeFile(path__default.default.join(tempDir, path__default.default.basename(absolutePath)), content, "utf8");
|
|
1894
|
+
await promises.rename(path__default.default.join(tempDir, path__default.default.basename(absolutePath)), absolutePath);
|
|
1895
|
+
} finally {
|
|
1896
|
+
await promises.rm(tempDir, { recursive: true, force: true }).catch(() => {
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
return overwritten;
|
|
1901
|
+
}
|
|
1755
1902
|
|
|
1756
1903
|
// src/init.ts
|
|
1757
1904
|
function adapterCall(provider, prefix = "process.env") {
|
|
@@ -3137,23 +3284,8 @@ var TEMPLATE_FN = {
|
|
|
3137
3284
|
"deno-deploy": denoDeployStarter,
|
|
3138
3285
|
angular: angularStarter
|
|
3139
3286
|
};
|
|
3140
|
-
async function
|
|
3141
|
-
|
|
3142
|
-
template: options.template,
|
|
3143
|
-
provider: options.provider ?? "demo",
|
|
3144
|
-
tools: options.tools ?? [],
|
|
3145
|
-
memory: options.memory ?? "none",
|
|
3146
|
-
pm: options.packageManager ?? "pnpm"
|
|
3147
|
-
};
|
|
3148
|
-
const files = TEMPLATE_FN[ctx.template](ctx);
|
|
3149
|
-
await promises.mkdir(options.targetDir, { recursive: true });
|
|
3150
|
-
await Promise.all(
|
|
3151
|
-
Object.entries(files).map(async ([relativePath, content]) => {
|
|
3152
|
-
const absolutePath = path__default.default.join(options.targetDir, relativePath);
|
|
3153
|
-
await promises.mkdir(path__default.default.dirname(absolutePath), { recursive: true });
|
|
3154
|
-
await promises.writeFile(absolutePath, content, "utf8");
|
|
3155
|
-
})
|
|
3156
|
-
);
|
|
3287
|
+
async function writeStarterProject2(options) {
|
|
3288
|
+
return writeStarterProject(options, TEMPLATE_FN);
|
|
3157
3289
|
}
|
|
3158
3290
|
async function runInteractiveInit(defaults = {}) {
|
|
3159
3291
|
process.stdout.write(`
|
|
@@ -3302,7 +3434,7 @@ function printNextSteps(options) {
|
|
|
3302
3434
|
|
|
3303
3435
|
// src/commands/init.ts
|
|
3304
3436
|
function registerInitCommand(program) {
|
|
3305
|
-
program.command("init").description("Generate a starter project. Run with no flags for interactive mode.").option("--template <template>", "Starter template (react|nextjs|sveltekit|nuxt|ink|vite-ink|cloudflare-workers|bun|deno-deploy|expo|angular|runtime|multi-agent)").option("--dir <directory>", "Target directory", "agentskit-app").option("--provider <provider>", "LLM provider (openai|anthropic|gemini|ollama|deepseek|grok|kimi|groq|openrouter|demo)").option("--tools <tools>", "Comma-separated tools (web_search,filesystem,shell)").option("--memory <backend>", "Memory backend (none|file|sqlite)").option("--pm <packageManager>", "Package manager (pnpm|npm|yarn|bun)").option("-y, --yes", "Skip interactive prompts; use flag values + defaults").action(async (rawOptions) => {
|
|
3437
|
+
program.command("init").description("Generate a starter project. Run with no flags for interactive mode.").option("--template <template>", "Starter template (react|nextjs|sveltekit|nuxt|ink|vite-ink|cloudflare-workers|bun|deno-deploy|expo|angular|runtime|multi-agent)").option("--dir <directory>", "Target directory", "agentskit-app").option("--provider <provider>", "LLM provider (openai|anthropic|gemini|ollama|deepseek|grok|kimi|groq|openrouter|demo)").option("--tools <tools>", "Comma-separated tools (web_search,filesystem,shell)").option("--memory <backend>", "Memory backend (none|file|sqlite)").option("--pm <packageManager>", "Package manager (pnpm|npm|yarn|bun)").option("--force", "Overwrite generated files in a non-empty target directory").option("-y, --yes", "Skip interactive prompts; use flag values + defaults").action(async (rawOptions) => {
|
|
3306
3438
|
const isCi = !process.stdout.isTTY || rawOptions.yes || rawOptions.template;
|
|
3307
3439
|
let resolved;
|
|
3308
3440
|
if (isCi) {
|
|
@@ -3313,7 +3445,8 @@ function registerInitCommand(program) {
|
|
|
3313
3445
|
provider: rawOptions.provider ?? "demo",
|
|
3314
3446
|
tools: rawOptions.tools ? rawOptions.tools.split(",").map((t) => t.trim()) : [],
|
|
3315
3447
|
memory: rawOptions.memory ?? "none",
|
|
3316
|
-
packageManager: rawOptions.pm ?? "pnpm"
|
|
3448
|
+
packageManager: rawOptions.pm ?? "pnpm",
|
|
3449
|
+
force: Boolean(rawOptions.force)
|
|
3317
3450
|
};
|
|
3318
3451
|
} else {
|
|
3319
3452
|
const result = await runInteractiveInit({
|
|
@@ -3323,17 +3456,22 @@ function registerInitCommand(program) {
|
|
|
3323
3456
|
if (result.cancelled) {
|
|
3324
3457
|
process.exit(0);
|
|
3325
3458
|
}
|
|
3326
|
-
resolved = result.options;
|
|
3459
|
+
resolved = { ...result.options, force: Boolean(rawOptions.force) };
|
|
3327
3460
|
}
|
|
3328
|
-
await
|
|
3461
|
+
const overwritten = await writeStarterProject2(resolved) ?? [];
|
|
3329
3462
|
if (isCi) {
|
|
3330
3463
|
process.stdout.write(
|
|
3331
3464
|
`Created ${resolved.template} starter in ${path__default.default.relative(process.cwd(), resolved.targetDir) || "."}
|
|
3332
|
-
|
|
3465
|
+
` + (overwritten.length > 0 ? `Overwritten: ${overwritten.join(", ")}
|
|
3466
|
+
` : "") + `\u2605 Star AgentsKit (solo-built \u2014 it helps a lot): https://github.com/AgentsKit-io/agentskit
|
|
3333
3467
|
`
|
|
3334
3468
|
);
|
|
3335
3469
|
} else {
|
|
3336
3470
|
printNextSteps(resolved);
|
|
3471
|
+
if (overwritten.length > 0) {
|
|
3472
|
+
process.stdout.write(`Overwritten: ${overwritten.join(", ")}
|
|
3473
|
+
`);
|
|
3474
|
+
}
|
|
3337
3475
|
}
|
|
3338
3476
|
});
|
|
3339
3477
|
}
|
|
@@ -3582,8 +3720,8 @@ async function checkPnpm() {
|
|
|
3582
3720
|
};
|
|
3583
3721
|
}
|
|
3584
3722
|
async function checkPackageJson() {
|
|
3585
|
-
const
|
|
3586
|
-
if (!fs.existsSync(
|
|
3723
|
+
const path6 = path.join(process.cwd(), "package.json");
|
|
3724
|
+
if (!fs.existsSync(path6)) {
|
|
3587
3725
|
return {
|
|
3588
3726
|
status: "warn",
|
|
3589
3727
|
name: "package.json",
|
|
@@ -3592,7 +3730,7 @@ async function checkPackageJson() {
|
|
|
3592
3730
|
};
|
|
3593
3731
|
}
|
|
3594
3732
|
try {
|
|
3595
|
-
const pkg = JSON.parse(await promises.readFile(
|
|
3733
|
+
const pkg = JSON.parse(await promises.readFile(path6, "utf8"));
|
|
3596
3734
|
const deps = {
|
|
3597
3735
|
...pkg.dependencies ?? {},
|
|
3598
3736
|
...pkg.devDependencies ?? {}
|
|
@@ -3825,6 +3963,14 @@ function registerDoctorCommand(program) {
|
|
|
3825
3963
|
if (report.fail > 0) process.exit(1);
|
|
3826
3964
|
});
|
|
3827
3965
|
}
|
|
3966
|
+
var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
3967
|
+
function resolveTsxCli() {
|
|
3968
|
+
try {
|
|
3969
|
+
return require2.resolve("tsx/cli");
|
|
3970
|
+
} catch {
|
|
3971
|
+
throw new Error("TypeScript execution requires the packaged tsx dependency");
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3828
3974
|
var DEFAULT_WATCH = [
|
|
3829
3975
|
"**/*.ts",
|
|
3830
3976
|
"**/*.tsx",
|
|
@@ -3852,8 +3998,8 @@ function startDev(options) {
|
|
|
3852
3998
|
const stderr = options.stderr ?? process.stderr;
|
|
3853
3999
|
const debounceMs = options.debounceMs ?? 200;
|
|
3854
4000
|
const isTs = entry.endsWith(".ts") || entry.endsWith(".tsx");
|
|
3855
|
-
const cmd = isTs ?
|
|
3856
|
-
const baseArgs = [entry, ...options.scriptArgs ?? []];
|
|
4001
|
+
const cmd = isTs ? process.execPath : "node";
|
|
4002
|
+
const baseArgs = isTs ? [resolveTsxCli(), entry, ...options.scriptArgs ?? []] : [entry, ...options.scriptArgs ?? []];
|
|
3857
4003
|
const spawnFn = options.spawn ?? ((c, a) => child_process.spawn(c, a, {
|
|
3858
4004
|
stdio: ["inherit", "pipe", "pipe"],
|
|
3859
4005
|
env: { ...process.env, FORCE_COLOR: "1" }
|
|
@@ -3865,7 +4011,9 @@ function startDev(options) {
|
|
|
3865
4011
|
let child;
|
|
3866
4012
|
let restartCount = 0;
|
|
3867
4013
|
let restartTimer;
|
|
4014
|
+
let delayedStartTimer;
|
|
3868
4015
|
let stopped = false;
|
|
4016
|
+
let stdinListener;
|
|
3869
4017
|
let resolveDone;
|
|
3870
4018
|
const done = new Promise((r) => {
|
|
3871
4019
|
resolveDone = r;
|
|
@@ -3891,15 +4039,18 @@ function startDev(options) {
|
|
|
3891
4039
|
}
|
|
3892
4040
|
});
|
|
3893
4041
|
};
|
|
3894
|
-
const restart = (
|
|
4042
|
+
const restart = (path6) => {
|
|
3895
4043
|
if (restartTimer) clearTimeout(restartTimer);
|
|
3896
4044
|
restartTimer = setTimeout(() => {
|
|
3897
4045
|
restartTimer = void 0;
|
|
3898
|
-
banner(`\u21BB change detected \u2014 ${
|
|
4046
|
+
banner(`\u21BB change detected \u2014 ${path6}`, "yellow");
|
|
3899
4047
|
if (child && !child.killed && child.exitCode === null) {
|
|
3900
4048
|
child.kill("SIGTERM");
|
|
3901
4049
|
}
|
|
3902
|
-
setTimeout(
|
|
4050
|
+
delayedStartTimer = setTimeout(() => {
|
|
4051
|
+
delayedStartTimer = void 0;
|
|
4052
|
+
if (!stopped) startChild();
|
|
4053
|
+
}, 80);
|
|
3903
4054
|
}, debounceMs);
|
|
3904
4055
|
};
|
|
3905
4056
|
watcher.on("change", restart);
|
|
@@ -3910,6 +4061,9 @@ function startDev(options) {
|
|
|
3910
4061
|
if (stopped) return;
|
|
3911
4062
|
stopped = true;
|
|
3912
4063
|
if (restartTimer) clearTimeout(restartTimer);
|
|
4064
|
+
if (delayedStartTimer) clearTimeout(delayedStartTimer);
|
|
4065
|
+
if (stdinListener) process.stdin.off("data", stdinListener);
|
|
4066
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) process.stdin.setRawMode(false);
|
|
3913
4067
|
if (child && !child.killed && child.exitCode === null) {
|
|
3914
4068
|
child.kill("SIGTERM");
|
|
3915
4069
|
}
|
|
@@ -3920,11 +4074,12 @@ function startDev(options) {
|
|
|
3920
4074
|
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
3921
4075
|
process.stdin.setRawMode(true);
|
|
3922
4076
|
process.stdin.resume();
|
|
3923
|
-
|
|
4077
|
+
stdinListener = (data) => {
|
|
3924
4078
|
const key = data.toString();
|
|
3925
4079
|
if (key === "r") restart("manual");
|
|
3926
4080
|
if (key === "q" || key === "") void stop();
|
|
3927
|
-
}
|
|
4081
|
+
};
|
|
4082
|
+
process.stdin.on("data", stdinListener);
|
|
3928
4083
|
}
|
|
3929
4084
|
return {
|
|
3930
4085
|
done,
|
|
@@ -3964,7 +4119,7 @@ function registerConfigCommand(program) {
|
|
|
3964
4119
|
const targetPath = isLocal ? path__default.default.join(process.cwd(), ".agentskit.config.json") : path__default.default.join(os.homedir(), ".agentskit", "config.json");
|
|
3965
4120
|
if (action === "show") {
|
|
3966
4121
|
const config = await loadConfig();
|
|
3967
|
-
process.stdout.write(JSON.stringify(config ?? {}, null, 2) + "\n");
|
|
4122
|
+
process.stdout.write(JSON.stringify(redactConfig(config ?? {}), null, 2) + "\n");
|
|
3968
4123
|
return;
|
|
3969
4124
|
}
|
|
3970
4125
|
if (action !== "init") {
|
|
@@ -4023,6 +4178,7 @@ async function startTunnel(options) {
|
|
|
4023
4178
|
});
|
|
4024
4179
|
let requests = 0;
|
|
4025
4180
|
let stopped = false;
|
|
4181
|
+
let signalHandler;
|
|
4026
4182
|
let resolveDone;
|
|
4027
4183
|
const done = new Promise((r) => {
|
|
4028
4184
|
resolveDone = r;
|
|
@@ -4032,12 +4188,14 @@ async function startTunnel(options) {
|
|
|
4032
4188
|
});
|
|
4033
4189
|
tunnel.on("close", () => {
|
|
4034
4190
|
if (stopped) return;
|
|
4191
|
+
if (signalHandler) process.off("SIGINT", signalHandler);
|
|
4035
4192
|
banner(`tunnel closed by remote`, "yellow");
|
|
4036
4193
|
resolveDone();
|
|
4037
4194
|
});
|
|
4038
4195
|
tunnel.on("error", (...args) => {
|
|
4039
4196
|
const err = args[0];
|
|
4040
4197
|
banner(`error: ${err?.message ?? "unknown"}`, "red");
|
|
4198
|
+
resolveDone();
|
|
4041
4199
|
});
|
|
4042
4200
|
banner(`\u2713 ready`, "green");
|
|
4043
4201
|
stdout.write("\n");
|
|
@@ -4053,14 +4211,16 @@ async function startTunnel(options) {
|
|
|
4053
4211
|
const stop = async () => {
|
|
4054
4212
|
if (stopped) return;
|
|
4055
4213
|
stopped = true;
|
|
4214
|
+
if (signalHandler) process.off("SIGINT", signalHandler);
|
|
4056
4215
|
tunnel.close();
|
|
4057
4216
|
banner(`stopped \u2014 proxied ${requests} request${requests === 1 ? "" : "s"}`, "cyan");
|
|
4058
4217
|
resolveDone();
|
|
4059
4218
|
};
|
|
4060
4219
|
if (process.stdin.isTTY) {
|
|
4061
|
-
|
|
4220
|
+
signalHandler = () => {
|
|
4062
4221
|
void stop();
|
|
4063
|
-
}
|
|
4222
|
+
};
|
|
4223
|
+
process.on("SIGINT", signalHandler);
|
|
4064
4224
|
}
|
|
4065
4225
|
return {
|
|
4066
4226
|
url: tunnel.url,
|
|
@@ -4155,10 +4315,10 @@ async function indexSources(rag, config, cwd) {
|
|
|
4155
4315
|
}
|
|
4156
4316
|
}
|
|
4157
4317
|
const documents = await Promise.all(
|
|
4158
|
-
absolutePaths.map(async (
|
|
4159
|
-
id:
|
|
4160
|
-
content: await promises.readFile(
|
|
4161
|
-
source:
|
|
4318
|
+
absolutePaths.map(async (path6) => ({
|
|
4319
|
+
id: path6,
|
|
4320
|
+
content: await promises.readFile(path6, "utf8"),
|
|
4321
|
+
source: path6
|
|
4162
4322
|
}))
|
|
4163
4323
|
);
|
|
4164
4324
|
if (documents.length > 0) await rag.ingest(documents);
|
|
@@ -4293,7 +4453,7 @@ function scaffoldAgent(schema) {
|
|
|
4293
4453
|
return files;
|
|
4294
4454
|
}
|
|
4295
4455
|
async function writeScaffold(files, outDir, opts = {}) {
|
|
4296
|
-
const { writeFile: writeFile5, mkdir: mkdir5, access } = await import('fs/promises');
|
|
4456
|
+
const { writeFile: writeFile5, mkdir: mkdir5, access: access2 } = await import('fs/promises');
|
|
4297
4457
|
const { dirname: dirname5, join: join11 } = await import('path');
|
|
4298
4458
|
const written = [];
|
|
4299
4459
|
for (const f of files) {
|
|
@@ -4301,7 +4461,7 @@ async function writeScaffold(files, outDir, opts = {}) {
|
|
|
4301
4461
|
await mkdir5(dirname5(full), { recursive: true });
|
|
4302
4462
|
if (!opts.overwrite) {
|
|
4303
4463
|
try {
|
|
4304
|
-
await
|
|
4464
|
+
await access2(full);
|
|
4305
4465
|
continue;
|
|
4306
4466
|
} catch {
|
|
4307
4467
|
}
|
|
@@ -4777,66 +4937,66 @@ After writing, summarise which files landed and what each one does.
|
|
|
4777
4937
|
// src/rules.ts
|
|
4778
4938
|
var CODEX_BLOCK_START = "<!-- agentskit-codex-profile:start -->";
|
|
4779
4939
|
var CODEX_BLOCK_END = "<!-- agentskit-codex-profile:end -->";
|
|
4780
|
-
async function ensureDir2(
|
|
4781
|
-
await promises.mkdir(path.dirname(
|
|
4940
|
+
async function ensureDir2(path6) {
|
|
4941
|
+
await promises.mkdir(path.dirname(path6), { recursive: true });
|
|
4782
4942
|
}
|
|
4783
4943
|
async function writeIfChanged(absPath, contents, force) {
|
|
4784
|
-
let
|
|
4944
|
+
let existing2;
|
|
4785
4945
|
try {
|
|
4786
|
-
|
|
4946
|
+
existing2 = await promises.readFile(absPath, "utf8");
|
|
4787
4947
|
} catch {
|
|
4788
4948
|
}
|
|
4789
|
-
if (
|
|
4790
|
-
if (
|
|
4949
|
+
if (existing2 === contents) return "skipped";
|
|
4950
|
+
if (existing2 !== void 0 && !force) {
|
|
4791
4951
|
return "skipped";
|
|
4792
4952
|
}
|
|
4793
4953
|
await ensureDir2(absPath);
|
|
4794
4954
|
await promises.writeFile(absPath, contents, "utf8");
|
|
4795
|
-
return
|
|
4955
|
+
return existing2 === void 0 ? "wrote" : "updated";
|
|
4796
4956
|
}
|
|
4797
4957
|
async function writeCursor(rootDir, force) {
|
|
4798
|
-
const
|
|
4799
|
-
const action = await writeIfChanged(
|
|
4800
|
-
return [{ path:
|
|
4958
|
+
const path6 = path.join(rootDir, ".cursor", "rules", "agentskit.mdc");
|
|
4959
|
+
const action = await writeIfChanged(path6, CURSOR_RULE, force);
|
|
4960
|
+
return [{ path: path6, action }];
|
|
4801
4961
|
}
|
|
4802
4962
|
async function writeWindsurf(rootDir, force) {
|
|
4803
|
-
const
|
|
4804
|
-
const action = await writeIfChanged(
|
|
4805
|
-
return [{ path:
|
|
4963
|
+
const path6 = path.join(rootDir, ".windsurfrules");
|
|
4964
|
+
const action = await writeIfChanged(path6, WINDSURF_RULE, force);
|
|
4965
|
+
return [{ path: path6, action }];
|
|
4806
4966
|
}
|
|
4807
4967
|
async function writeCodex(rootDir, force) {
|
|
4808
|
-
const
|
|
4809
|
-
let
|
|
4968
|
+
const path6 = path.join(rootDir, "AGENTS.md");
|
|
4969
|
+
let existing2 = "";
|
|
4810
4970
|
try {
|
|
4811
|
-
|
|
4971
|
+
existing2 = await promises.readFile(path6, "utf8");
|
|
4812
4972
|
} catch {
|
|
4813
4973
|
}
|
|
4814
|
-
const startIdx =
|
|
4815
|
-
const endIdx = startIdx >= 0 ?
|
|
4974
|
+
const startIdx = existing2.indexOf(CODEX_BLOCK_START);
|
|
4975
|
+
const endIdx = startIdx >= 0 ? existing2.lastIndexOf(CODEX_BLOCK_END, existing2.length) : -1;
|
|
4816
4976
|
if (startIdx >= 0 && endIdx <= startIdx) {
|
|
4817
4977
|
throw new core.ConfigError({
|
|
4818
4978
|
code: core.ErrorCodes.AK_CONFIG_INVALID,
|
|
4819
|
-
message: `${
|
|
4979
|
+
message: `${path6}: found agentskit-codex-profile:start sentinel but no matching :end after it \u2014 refusing to modify a half-edited block.`,
|
|
4820
4980
|
hint: "Restore the closing sentinel or remove the partial block, then re-run."
|
|
4821
4981
|
});
|
|
4822
4982
|
}
|
|
4823
4983
|
let next;
|
|
4824
4984
|
if (startIdx >= 0 && endIdx > startIdx) {
|
|
4825
|
-
next =
|
|
4826
|
-
} else if (
|
|
4827
|
-
next = `${
|
|
4985
|
+
next = existing2.slice(0, startIdx) + CODEX_PROFILE.trimEnd() + existing2.slice(endIdx + CODEX_BLOCK_END.length);
|
|
4986
|
+
} else if (existing2) {
|
|
4987
|
+
next = `${existing2.replace(/\s+$/, "")}
|
|
4828
4988
|
|
|
4829
4989
|
${CODEX_PROFILE}`;
|
|
4830
4990
|
} else {
|
|
4831
4991
|
next = CODEX_PROFILE;
|
|
4832
4992
|
}
|
|
4833
|
-
if (next ===
|
|
4834
|
-
if (
|
|
4835
|
-
return [{ path:
|
|
4993
|
+
if (next === existing2) return [{ path: path6, action: "skipped" }];
|
|
4994
|
+
if (existing2 && !force) {
|
|
4995
|
+
return [{ path: path6, action: "skipped" }];
|
|
4836
4996
|
}
|
|
4837
|
-
await ensureDir2(
|
|
4838
|
-
await promises.writeFile(
|
|
4839
|
-
return [{ path:
|
|
4997
|
+
await ensureDir2(path6);
|
|
4998
|
+
await promises.writeFile(path6, next, "utf8");
|
|
4999
|
+
return [{ path: path6, action: existing2 ? "updated" : "wrote" }];
|
|
4840
5000
|
}
|
|
4841
5001
|
async function writeClaudeCode(rootDir, force) {
|
|
4842
5002
|
const out = [];
|
|
@@ -4924,17 +5084,78 @@ rules failed: ${message}
|
|
|
4924
5084
|
}
|
|
4925
5085
|
var RAW_BASE = "https://raw.githubusercontent.com/AgentsKit-io/agentskit-registry/main";
|
|
4926
5086
|
var HOSTED_BASE = `${RAW_BASE}/public/r`;
|
|
5087
|
+
var FETCH_TIMEOUT_MS = 15e3;
|
|
5088
|
+
var MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
5089
|
+
var MAX_AGENT_FILES = 100;
|
|
5090
|
+
var SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
5091
|
+
function assertSafeId(id) {
|
|
5092
|
+
if (!SAFE_ID.test(id)) throw new Error(`Invalid registry agent id: ${JSON.stringify(id)}`);
|
|
5093
|
+
}
|
|
5094
|
+
function assertSafeRelativePath(filePath) {
|
|
5095
|
+
if (!filePath || path.isAbsolute(filePath) || filePath.includes("\0")) {
|
|
5096
|
+
throw new Error(`Invalid registry file path: ${JSON.stringify(filePath)}`);
|
|
5097
|
+
}
|
|
5098
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
5099
|
+
if (normalized.split("/").some((part) => part === ".." || part === "")) {
|
|
5100
|
+
throw new Error(`Registry file path must stay relative: ${JSON.stringify(filePath)}`);
|
|
5101
|
+
}
|
|
5102
|
+
}
|
|
5103
|
+
function assertInside(parent, child) {
|
|
5104
|
+
const rel = path.relative(path.resolve(parent), path.resolve(child));
|
|
5105
|
+
if (rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) {
|
|
5106
|
+
throw new Error(`Registry destination escapes target directory: ${child}`);
|
|
5107
|
+
}
|
|
5108
|
+
}
|
|
5109
|
+
async function assertNoSymlinkAncestors(targetDir, baseDir) {
|
|
5110
|
+
for (const path6 of [targetDir, baseDir]) {
|
|
5111
|
+
try {
|
|
5112
|
+
if ((await promises.lstat(path.resolve(path6))).isSymbolicLink()) {
|
|
5113
|
+
throw new Error(`Registry destination contains a symlink ancestor: ${path.resolve(path6)}`);
|
|
5114
|
+
}
|
|
5115
|
+
} catch (error) {
|
|
5116
|
+
if (error.code !== "ENOENT") throw error;
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
let current = path.resolve(baseDir);
|
|
5120
|
+
while (true) {
|
|
5121
|
+
try {
|
|
5122
|
+
if ((await promises.lstat(current)).isSymbolicLink()) {
|
|
5123
|
+
throw new Error(`Registry destination contains a symlink ancestor: ${current}`);
|
|
5124
|
+
}
|
|
5125
|
+
return;
|
|
5126
|
+
} catch (error) {
|
|
5127
|
+
if (error.code !== "ENOENT") throw error;
|
|
5128
|
+
}
|
|
5129
|
+
const parent = path.dirname(current);
|
|
5130
|
+
if (parent === current) return;
|
|
5131
|
+
current = parent;
|
|
5132
|
+
}
|
|
5133
|
+
}
|
|
5134
|
+
async function fetchWithLimits(url, fetchImpl) {
|
|
5135
|
+
const controller = new AbortController();
|
|
5136
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
5137
|
+
try {
|
|
5138
|
+
const response = await fetchImpl(url, { signal: controller.signal });
|
|
5139
|
+
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
|
|
5140
|
+
const body = await response.arrayBuffer();
|
|
5141
|
+
if (body.byteLength > MAX_RESPONSE_BYTES) {
|
|
5142
|
+
throw new Error(`Registry response exceeded ${MAX_RESPONSE_BYTES} bytes`);
|
|
5143
|
+
}
|
|
5144
|
+
return new Response(body, { status: response.status, headers: response.headers });
|
|
5145
|
+
} finally {
|
|
5146
|
+
clearTimeout(timer);
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
4927
5149
|
async function getJson(url, fetchImpl) {
|
|
4928
|
-
const res = await
|
|
4929
|
-
|
|
4930
|
-
return res.json();
|
|
5150
|
+
const res = await fetchWithLimits(url, fetchImpl);
|
|
5151
|
+
return JSON.parse(await res.text());
|
|
4931
5152
|
}
|
|
4932
5153
|
async function getText(url, fetchImpl) {
|
|
4933
|
-
const res = await
|
|
4934
|
-
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
|
|
5154
|
+
const res = await fetchWithLimits(url, fetchImpl);
|
|
4935
5155
|
return res.text();
|
|
4936
5156
|
}
|
|
4937
5157
|
async function fetchAgent(id, options = {}) {
|
|
5158
|
+
assertSafeId(id);
|
|
4938
5159
|
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
4939
5160
|
try {
|
|
4940
5161
|
const hosted = await getJson(`${HOSTED_BASE}/${id}.json`, fetchImpl);
|
|
@@ -4942,6 +5163,10 @@ async function fetchAgent(id, options = {}) {
|
|
|
4942
5163
|
throw new Error("hosted entry missing sources");
|
|
4943
5164
|
} catch {
|
|
4944
5165
|
const meta = await getJson(`${RAW_BASE}/registry/${id}/meta.json`, fetchImpl);
|
|
5166
|
+
if (!Array.isArray(meta.files) || meta.files.length > MAX_AGENT_FILES) {
|
|
5167
|
+
throw new Error(`Registry agent contains too many files (maximum ${MAX_AGENT_FILES})`);
|
|
5168
|
+
}
|
|
5169
|
+
for (const rel of meta.files) assertSafeRelativePath(rel);
|
|
4945
5170
|
const sources = await Promise.all(
|
|
4946
5171
|
meta.files.map(async (rel) => ({
|
|
4947
5172
|
path: rel,
|
|
@@ -4958,10 +5183,10 @@ function resolveSystemPrompt(agent) {
|
|
|
4958
5183
|
const m = src.match(/systemPrompt:\s*`((?:\\.|[^`\\])*)`/);
|
|
4959
5184
|
return m ? m[1].replace(/\\`/g, "`").replace(/\\\$\{/g, "${") : null;
|
|
4960
5185
|
}
|
|
4961
|
-
async function defaultExists(
|
|
4962
|
-
const { access } = await import('fs/promises');
|
|
5186
|
+
async function defaultExists(path6) {
|
|
5187
|
+
const { access: access2 } = await import('fs/promises');
|
|
4963
5188
|
try {
|
|
4964
|
-
await
|
|
5189
|
+
await access2(path6);
|
|
4965
5190
|
return true;
|
|
4966
5191
|
} catch {
|
|
4967
5192
|
return false;
|
|
@@ -4985,17 +5210,24 @@ async function addAgent(id, options = {}) {
|
|
|
4985
5210
|
assertInstallable(agent);
|
|
4986
5211
|
const baseDir = options.outDir ?? "agents";
|
|
4987
5212
|
const targetDir = path.join(baseDir, id);
|
|
5213
|
+
assertInside(baseDir, targetDir);
|
|
5214
|
+
await assertNoSymlinkAncestors(targetDir, baseDir);
|
|
4988
5215
|
const exists = options.existsImpl ?? defaultExists;
|
|
4989
|
-
const write = options.writeFileImpl ?? (async (
|
|
4990
|
-
await promises.mkdir(path.dirname(
|
|
4991
|
-
await promises.writeFile(
|
|
5216
|
+
const write = options.writeFileImpl ?? (async (path6, content) => {
|
|
5217
|
+
await promises.mkdir(path.dirname(path6), { recursive: true });
|
|
5218
|
+
await promises.writeFile(path6, content, "utf8");
|
|
4992
5219
|
});
|
|
4993
5220
|
const written = [];
|
|
4994
5221
|
for (const file of agent.sources) {
|
|
5222
|
+
assertSafeRelativePath(file.path);
|
|
4995
5223
|
const dest = path.join(targetDir, file.path);
|
|
5224
|
+
assertInside(targetDir, dest);
|
|
4996
5225
|
if (!options.force && await exists(dest)) {
|
|
4997
5226
|
throw new Error(`${dest} already exists (re-run with --force to overwrite)`);
|
|
4998
5227
|
}
|
|
5228
|
+
}
|
|
5229
|
+
for (const file of agent.sources) {
|
|
5230
|
+
const dest = path.join(targetDir, file.path);
|
|
4999
5231
|
await write(dest, file.content);
|
|
5000
5232
|
written.push(dest);
|
|
5001
5233
|
}
|
|
@@ -5058,8 +5290,8 @@ function allDeps(pkg) {
|
|
|
5058
5290
|
if (!pkg) return {};
|
|
5059
5291
|
return { ...pkg.peerDependencies, ...pkg.devDependencies, ...pkg.dependencies };
|
|
5060
5292
|
}
|
|
5061
|
-
function readJson(fs,
|
|
5062
|
-
const raw = fs.readFile(
|
|
5293
|
+
function readJson(fs, path6) {
|
|
5294
|
+
const raw = fs.readFile(path6);
|
|
5063
5295
|
if (raw == null) return null;
|
|
5064
5296
|
try {
|
|
5065
5297
|
return JSON.parse(raw);
|
|
@@ -5243,13 +5475,13 @@ function readConfig(io, root = ".") {
|
|
|
5243
5475
|
return raw == null ? null : parseConfig(raw);
|
|
5244
5476
|
}
|
|
5245
5477
|
function writeConfig(io, config, root = ".") {
|
|
5246
|
-
const
|
|
5247
|
-
io.write(
|
|
5248
|
-
return
|
|
5478
|
+
const path6 = path.join(root, CONFIG_PATH);
|
|
5479
|
+
io.write(path6, serializeConfig(config));
|
|
5480
|
+
return path6;
|
|
5249
5481
|
}
|
|
5250
5482
|
function resolveConfig(io, scan, root = ".") {
|
|
5251
|
-
const
|
|
5252
|
-
if (
|
|
5483
|
+
const existing2 = readConfig(io, root);
|
|
5484
|
+
if (existing2) return { config: existing2, fromDisk: true };
|
|
5253
5485
|
return { config: buildConfig(scan), fromDisk: false };
|
|
5254
5486
|
}
|
|
5255
5487
|
var IntegrityError = class extends Error {
|
|
@@ -5334,7 +5566,18 @@ function resolveAuthHeader(base, config, env = {}) {
|
|
|
5334
5566
|
const map = config?.registryAuth;
|
|
5335
5567
|
if (!map) return {};
|
|
5336
5568
|
for (const [registryBase, envVar] of Object.entries(map)) {
|
|
5337
|
-
|
|
5569
|
+
let configured;
|
|
5570
|
+
let target;
|
|
5571
|
+
try {
|
|
5572
|
+
configured = new URL(registryBase);
|
|
5573
|
+
target = new URL(base);
|
|
5574
|
+
} catch {
|
|
5575
|
+
continue;
|
|
5576
|
+
}
|
|
5577
|
+
const configuredPath = configured.pathname.replace(/\/+$/, "") || "/";
|
|
5578
|
+
const targetPath = target.pathname.replace(/\/+$/, "") || "/";
|
|
5579
|
+
const samePathBoundary = configuredPath === "/" || targetPath === configuredPath || targetPath.startsWith(`${configuredPath}/`);
|
|
5580
|
+
if (target.origin === configured.origin && samePathBoundary) {
|
|
5338
5581
|
const token = env[envVar];
|
|
5339
5582
|
if (token) return { authorization: `Bearer ${token}` };
|
|
5340
5583
|
}
|
|
@@ -5353,8 +5596,8 @@ function verifyChecksums(files) {
|
|
|
5353
5596
|
function manifestUrl(ref) {
|
|
5354
5597
|
return `${ref.base}/r/${ref.itemId}.json`;
|
|
5355
5598
|
}
|
|
5356
|
-
function fileUrl(ref,
|
|
5357
|
-
return `${ref.base}/r/${ref.itemId}/${
|
|
5599
|
+
function fileUrl(ref, path6) {
|
|
5600
|
+
return `${ref.base}/r/${ref.itemId}/${path6}`;
|
|
5358
5601
|
}
|
|
5359
5602
|
async function getText2(fetchImpl, url, headers) {
|
|
5360
5603
|
const res = await fetchImpl(url, { headers });
|
|
@@ -5691,44 +5934,44 @@ async function addComponent(options, deps) {
|
|
|
5691
5934
|
}
|
|
5692
5935
|
function nodeScanFs() {
|
|
5693
5936
|
return {
|
|
5694
|
-
readFile(
|
|
5937
|
+
readFile(path6) {
|
|
5695
5938
|
try {
|
|
5696
|
-
return fs.readFileSync(
|
|
5939
|
+
return fs.readFileSync(path6, "utf8");
|
|
5697
5940
|
} catch {
|
|
5698
5941
|
return null;
|
|
5699
5942
|
}
|
|
5700
5943
|
},
|
|
5701
|
-
exists(
|
|
5702
|
-
return fs.existsSync(
|
|
5944
|
+
exists(path6) {
|
|
5945
|
+
return fs.existsSync(path6);
|
|
5703
5946
|
}
|
|
5704
5947
|
};
|
|
5705
5948
|
}
|
|
5706
5949
|
function nodeWriteFs() {
|
|
5707
5950
|
return {
|
|
5708
|
-
exists(
|
|
5709
|
-
return fs.existsSync(
|
|
5951
|
+
exists(path6) {
|
|
5952
|
+
return fs.existsSync(path6);
|
|
5710
5953
|
},
|
|
5711
|
-
write(
|
|
5712
|
-
fs.mkdirSync(path.dirname(
|
|
5713
|
-
fs.writeFileSync(
|
|
5954
|
+
write(path6, content) {
|
|
5955
|
+
fs.mkdirSync(path.dirname(path6), { recursive: true });
|
|
5956
|
+
fs.writeFileSync(path6, content, "utf8");
|
|
5714
5957
|
},
|
|
5715
|
-
remove(
|
|
5716
|
-
fs.rmSync(
|
|
5958
|
+
remove(path6) {
|
|
5959
|
+
fs.rmSync(path6, { force: true });
|
|
5717
5960
|
}
|
|
5718
5961
|
};
|
|
5719
5962
|
}
|
|
5720
5963
|
function nodeConfigIo() {
|
|
5721
5964
|
return {
|
|
5722
|
-
read(
|
|
5965
|
+
read(path6) {
|
|
5723
5966
|
try {
|
|
5724
|
-
return fs.readFileSync(
|
|
5967
|
+
return fs.readFileSync(path6, "utf8");
|
|
5725
5968
|
} catch {
|
|
5726
5969
|
return null;
|
|
5727
5970
|
}
|
|
5728
5971
|
},
|
|
5729
|
-
write(
|
|
5730
|
-
fs.mkdirSync(path.dirname(
|
|
5731
|
-
fs.writeFileSync(
|
|
5972
|
+
write(path6, content) {
|
|
5973
|
+
fs.mkdirSync(path.dirname(path6), { recursive: true });
|
|
5974
|
+
fs.writeFileSync(path6, content, "utf8");
|
|
5732
5975
|
}
|
|
5733
5976
|
};
|
|
5734
5977
|
}
|
|
@@ -5948,7 +6191,7 @@ update failed: ${err instanceof Error ? err.message : String(err)}
|
|
|
5948
6191
|
// src/commands/index.ts
|
|
5949
6192
|
function createCli() {
|
|
5950
6193
|
const program = new commander.Command();
|
|
5951
|
-
program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.");
|
|
6194
|
+
program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.").version(package_default.version);
|
|
5952
6195
|
registerChatCommand(program);
|
|
5953
6196
|
registerRunCommand(program);
|
|
5954
6197
|
registerInitCommand(program);
|
|
@@ -6004,6 +6247,6 @@ exports.sessionFilePath = sessionFilePath;
|
|
|
6004
6247
|
exports.startDev = startDev;
|
|
6005
6248
|
exports.startTunnel = startTunnel;
|
|
6006
6249
|
exports.writeSessionMeta = writeSessionMeta;
|
|
6007
|
-
exports.writeStarterProject =
|
|
6250
|
+
exports.writeStarterProject = writeStarterProject2;
|
|
6008
6251
|
//# sourceMappingURL=index.cjs.map
|
|
6009
6252
|
//# sourceMappingURL=index.cjs.map
|