@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/bin.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
|
const globalDir = path.join(os.homedir(), ".agentskit");
|
|
@@ -95,6 +109,21 @@ async function loadConfig(options) {
|
|
|
95
109
|
const local = await loadLocalConfig(cwd);
|
|
96
110
|
return mergeConfigs(global, local);
|
|
97
111
|
}
|
|
112
|
+
var SECRET_KEY = /^(api[-_]?key|token|secret|password|authorization|private[-_]?key)$/i;
|
|
113
|
+
var SECRET_ENV_KEY = /(?:^|_)(?:api_?key|token|secret|password|authorization|private_?key)$/i;
|
|
114
|
+
function isSecretKey(key) {
|
|
115
|
+
return SECRET_KEY.test(key) || SECRET_ENV_KEY.test(key);
|
|
116
|
+
}
|
|
117
|
+
function redactConfig(value) {
|
|
118
|
+
if (Array.isArray(value)) return value.map(redactConfig);
|
|
119
|
+
if (!value || typeof value !== "object") return value;
|
|
120
|
+
return Object.fromEntries(
|
|
121
|
+
Object.entries(value).map(([key, child]) => [
|
|
122
|
+
key,
|
|
123
|
+
isSecretKey(key) ? "[REDACTED]" : redactConfig(child)
|
|
124
|
+
])
|
|
125
|
+
);
|
|
126
|
+
}
|
|
98
127
|
var providers = {
|
|
99
128
|
openai: {
|
|
100
129
|
label: "OpenAI",
|
|
@@ -166,7 +195,7 @@ function createDemoAdapter(provider, model) {
|
|
|
166
195
|
].join(" ");
|
|
167
196
|
for (const chunk of reply.match(/.{1,18}/g) ?? []) {
|
|
168
197
|
if (cancelled) return;
|
|
169
|
-
await new Promise((
|
|
198
|
+
await new Promise((resolve8) => setTimeout(resolve8, 35));
|
|
170
199
|
yield { type: "text", content: chunk };
|
|
171
200
|
}
|
|
172
201
|
yield { type: "done" };
|
|
@@ -226,7 +255,8 @@ function dirFor(cwd = process.cwd()) {
|
|
|
226
255
|
return path.join(ROOT, cwdHash(cwd));
|
|
227
256
|
}
|
|
228
257
|
function ensureDir(dir) {
|
|
229
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
258
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
259
|
+
fs.chmodSync(dir, 448);
|
|
230
260
|
}
|
|
231
261
|
function generateSessionId() {
|
|
232
262
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
@@ -241,17 +271,19 @@ function metaPath(id, cwd = process.cwd()) {
|
|
|
241
271
|
return path.join(dirFor(cwd), `${id}${META_SUFFIX}`);
|
|
242
272
|
}
|
|
243
273
|
function readMeta(id, cwd = process.cwd()) {
|
|
244
|
-
const
|
|
245
|
-
if (!fs.existsSync(
|
|
274
|
+
const path6 = metaPath(id, cwd);
|
|
275
|
+
if (!fs.existsSync(path6)) return null;
|
|
246
276
|
try {
|
|
247
|
-
return JSON.parse(fs.readFileSync(
|
|
277
|
+
return JSON.parse(fs.readFileSync(path6, "utf8"));
|
|
248
278
|
} catch {
|
|
249
279
|
return null;
|
|
250
280
|
}
|
|
251
281
|
}
|
|
252
282
|
function writeSessionMeta(meta, cwd = process.cwd()) {
|
|
253
283
|
ensureDir(dirFor(cwd));
|
|
254
|
-
|
|
284
|
+
const path6 = metaPath(meta.id, cwd);
|
|
285
|
+
fs.writeFileSync(path6, JSON.stringify(meta, null, 2), { mode: 384 });
|
|
286
|
+
fs.chmodSync(path6, 384);
|
|
255
287
|
}
|
|
256
288
|
function derivePreview(messages) {
|
|
257
289
|
const firstUser = messages.find((m) => m.role === "user" && m.content.trim());
|
|
@@ -853,7 +885,7 @@ var HookDispatcher = class {
|
|
|
853
885
|
result = await handler.run(current);
|
|
854
886
|
} catch (err) {
|
|
855
887
|
this.onError(handler, err);
|
|
856
|
-
|
|
888
|
+
return { payload: current, blocked: true, reason: "hook failed" };
|
|
857
889
|
}
|
|
858
890
|
if (result.decision === "block") {
|
|
859
891
|
return { payload: current, blocked: true, reason: result.reason };
|
|
@@ -870,6 +902,7 @@ var HookDispatcher = class {
|
|
|
870
902
|
return handler.matcher.test(String(payload.tool ?? payload.prompt ?? ""));
|
|
871
903
|
}
|
|
872
904
|
};
|
|
905
|
+
var MAX_HOOK_STDOUT_BYTES = 64 * 1024;
|
|
873
906
|
function configHooksToHandlers(config) {
|
|
874
907
|
if (!config) return [];
|
|
875
908
|
const handlers = [];
|
|
@@ -899,8 +932,26 @@ function runShellHook(entry, payload) {
|
|
|
899
932
|
detached: true
|
|
900
933
|
});
|
|
901
934
|
let stdout = "";
|
|
935
|
+
let stdoutBytes = 0;
|
|
902
936
|
child.stdout.on("data", (chunk) => {
|
|
903
|
-
|
|
937
|
+
const text = chunk.toString();
|
|
938
|
+
const bytes = Buffer.byteLength(text);
|
|
939
|
+
if (stdoutBytes + bytes > MAX_HOOK_STDOUT_BYTES) {
|
|
940
|
+
if (child.pid !== void 0) {
|
|
941
|
+
try {
|
|
942
|
+
process.kill(-child.pid, "SIGKILL");
|
|
943
|
+
} catch {
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
try {
|
|
947
|
+
child.kill("SIGKILL");
|
|
948
|
+
} catch {
|
|
949
|
+
}
|
|
950
|
+
settle({ decision: "block", reason: `shell hook output exceeded ${MAX_HOOK_STDOUT_BYTES} bytes` });
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
stdoutBytes += bytes;
|
|
954
|
+
stdout += text;
|
|
904
955
|
});
|
|
905
956
|
const timer = setTimeout(() => {
|
|
906
957
|
if (child.pid !== void 0) {
|
|
@@ -932,9 +983,13 @@ function runShellHook(entry, payload) {
|
|
|
932
983
|
}
|
|
933
984
|
try {
|
|
934
985
|
const parsed = JSON.parse(trimmed);
|
|
935
|
-
|
|
986
|
+
if (!parsed || typeof parsed !== "object" || !("decision" in parsed) || !["continue", "block", "modify"].includes(String(parsed.decision))) {
|
|
987
|
+
settle({ decision: "block", reason: "shell hook returned an invalid decision" });
|
|
988
|
+
} else {
|
|
989
|
+
settle(parsed);
|
|
990
|
+
}
|
|
936
991
|
} catch {
|
|
937
|
-
settle({ decision: "
|
|
992
|
+
settle({ decision: "block", reason: "shell hook returned invalid JSON" });
|
|
938
993
|
}
|
|
939
994
|
});
|
|
940
995
|
child.on("error", (err) => {
|
|
@@ -981,8 +1036,9 @@ function ChatApp(options) {
|
|
|
981
1036
|
} = useRuntime(options);
|
|
982
1037
|
const mergedTools = React2.useMemo(() => {
|
|
983
1038
|
const extra = options.extraTools ?? [];
|
|
984
|
-
|
|
985
|
-
|
|
1039
|
+
const combined = [...tools, ...extra];
|
|
1040
|
+
return options.permissionPolicy ? applyPolicyToTools(options.permissionPolicy, combined) : combined;
|
|
1041
|
+
}, [tools, options.extraTools, options.permissionPolicy]);
|
|
986
1042
|
const mergedSkills = React2.useMemo(() => {
|
|
987
1043
|
const extra = options.extraSkills ?? [];
|
|
988
1044
|
if (!skills && extra.length === 0) return void 0;
|
|
@@ -1194,7 +1250,7 @@ async function loadPlugins(options = {}) {
|
|
|
1194
1250
|
specs = [],
|
|
1195
1251
|
pluginDirs = [],
|
|
1196
1252
|
cwd = process.cwd(),
|
|
1197
|
-
autoDiscoverUserDir =
|
|
1253
|
+
autoDiscoverUserDir = false,
|
|
1198
1254
|
onError = (spec, err) => process.stderr.write(
|
|
1199
1255
|
`[agentskit] plugin "${spec}" failed to load: ${err instanceof Error ? err.message : String(err)}
|
|
1200
1256
|
`
|
|
@@ -1285,6 +1341,8 @@ function mergePluginsIntoBundle(plugins) {
|
|
|
1285
1341
|
}
|
|
1286
1342
|
return bundle;
|
|
1287
1343
|
}
|
|
1344
|
+
var MAX_FRAME_BYTES = 1024 * 1024;
|
|
1345
|
+
var DISPOSE_GRACE_MS = 1e3;
|
|
1288
1346
|
var McpClient = class {
|
|
1289
1347
|
constructor(spec, onError = (err) => process.stderr.write(
|
|
1290
1348
|
`[agentskit] mcp[${spec.name}] error: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1301,7 +1359,8 @@ var McpClient = class {
|
|
|
1301
1359
|
if (this.child) return;
|
|
1302
1360
|
const child = child_process.spawn(this.spec.command, this.spec.args ?? [], {
|
|
1303
1361
|
env: { ...process.env, ...this.spec.env ?? {} },
|
|
1304
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1362
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1363
|
+
detached: process.platform !== "win32"
|
|
1305
1364
|
});
|
|
1306
1365
|
this.child = child;
|
|
1307
1366
|
child.stdout.on("data", (chunk) => this.onStdout(chunk.toString()));
|
|
@@ -1333,7 +1392,22 @@ var McpClient = class {
|
|
|
1333
1392
|
dispose() {
|
|
1334
1393
|
if (this.disposed) return;
|
|
1335
1394
|
this.disposed = true;
|
|
1336
|
-
this.
|
|
1395
|
+
for (const pending of this.pending.values()) {
|
|
1396
|
+
pending({ jsonrpc: "2.0", id: 0, error: { code: -32800, message: "client disposed" } });
|
|
1397
|
+
}
|
|
1398
|
+
this.pending.clear();
|
|
1399
|
+
const child = this.child;
|
|
1400
|
+
if (!child || child.killed) return;
|
|
1401
|
+
const kill = (signal) => {
|
|
1402
|
+
try {
|
|
1403
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, signal);
|
|
1404
|
+
else child.kill(signal);
|
|
1405
|
+
} catch {
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
kill("SIGTERM");
|
|
1409
|
+
const timer = setTimeout(() => kill("SIGKILL"), DISPOSE_GRACE_MS);
|
|
1410
|
+
timer.unref();
|
|
1337
1411
|
}
|
|
1338
1412
|
request(method, params) {
|
|
1339
1413
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
@@ -1361,10 +1435,20 @@ var McpClient = class {
|
|
|
1361
1435
|
}
|
|
1362
1436
|
onStdout(chunk) {
|
|
1363
1437
|
this.buffer += chunk;
|
|
1438
|
+
if (Buffer.byteLength(this.buffer, "utf8") > MAX_FRAME_BYTES && !this.buffer.includes("\n")) {
|
|
1439
|
+
this.onError(new Error(`mcp ${this.spec.name} frame exceeded ${MAX_FRAME_BYTES} bytes`));
|
|
1440
|
+
this.dispose();
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1364
1443
|
let newlineIndex = this.buffer.indexOf("\n");
|
|
1365
1444
|
while (newlineIndex !== -1) {
|
|
1366
1445
|
const line = this.buffer.slice(0, newlineIndex).trim();
|
|
1367
1446
|
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
1447
|
+
if (Buffer.byteLength(line, "utf8") > MAX_FRAME_BYTES) {
|
|
1448
|
+
this.onError(new Error(`mcp ${this.spec.name} frame exceeded ${MAX_FRAME_BYTES} bytes`));
|
|
1449
|
+
this.dispose();
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1368
1452
|
if (line) {
|
|
1369
1453
|
try {
|
|
1370
1454
|
const parsed = JSON.parse(line);
|
|
@@ -1426,7 +1510,7 @@ function registerChatCommand(program) {
|
|
|
1426
1510
|
"Extra directory to auto-discover plugin modules from (repeatable)",
|
|
1427
1511
|
(value, prev = []) => [...prev, value],
|
|
1428
1512
|
[]
|
|
1429
|
-
).option(
|
|
1513
|
+
).option("--discover-plugins", "Also auto-discover plugins from ~/.agentskit/plugins").option(
|
|
1430
1514
|
"--mode <mode>",
|
|
1431
1515
|
"Permission mode: default | plan | acceptEdits | bypassPermissions"
|
|
1432
1516
|
).action(async (options) => {
|
|
@@ -1463,7 +1547,8 @@ function registerChatCommand(program) {
|
|
|
1463
1547
|
}
|
|
1464
1548
|
const pluginBundle = await loadPlugins({
|
|
1465
1549
|
specs: config?.plugins ?? [],
|
|
1466
|
-
pluginDirs: options.pluginDir ?? []
|
|
1550
|
+
pluginDirs: options.pluginDir ?? [],
|
|
1551
|
+
autoDiscoverUserDir: Boolean(options.discoverPlugins)
|
|
1467
1552
|
});
|
|
1468
1553
|
const configHooks = configHooksToHandlers(config?.hooks);
|
|
1469
1554
|
const hookHandlers = [...configHooks, ...pluginBundle.hooks];
|
|
@@ -1551,8 +1636,7 @@ function formatEvent(event) {
|
|
|
1551
1636
|
}
|
|
1552
1637
|
async function runAgent(task, options) {
|
|
1553
1638
|
if (options.skill && options.skills) {
|
|
1554
|
-
|
|
1555
|
-
process.exit(1);
|
|
1639
|
+
throw new Error("--skill and --skills are mutually exclusive. Use one or the other.");
|
|
1556
1640
|
}
|
|
1557
1641
|
const { adapter } = resolveChatProvider({
|
|
1558
1642
|
provider: options.provider,
|
|
@@ -1744,6 +1828,69 @@ var PROVIDER_ENV_KEY = {
|
|
|
1744
1828
|
openrouter: "OPENROUTER_API_KEY",
|
|
1745
1829
|
demo: null
|
|
1746
1830
|
};
|
|
1831
|
+
var PACKAGE_RANGES = {
|
|
1832
|
+
"@agentskit/adapters": "^0.15.2",
|
|
1833
|
+
"@agentskit/angular": "^0.5.3",
|
|
1834
|
+
"@agentskit/ink": "^0.10.9",
|
|
1835
|
+
"@agentskit/memory": "^0.11.8",
|
|
1836
|
+
"@agentskit/react": "^0.8.3",
|
|
1837
|
+
"@agentskit/runtime": "^0.10.15",
|
|
1838
|
+
"@agentskit/skills": "^0.9.3",
|
|
1839
|
+
"@agentskit/svelte": "^0.5.3",
|
|
1840
|
+
"@agentskit/tools": "^0.13.6",
|
|
1841
|
+
"@agentskit/vue": "^0.5.3"
|
|
1842
|
+
};
|
|
1843
|
+
function align(files) {
|
|
1844
|
+
const result = { ...files };
|
|
1845
|
+
for (const file of ["package.json", "deno.json"]) {
|
|
1846
|
+
if (!files[file]) continue;
|
|
1847
|
+
const parsed = JSON.parse(files[file]);
|
|
1848
|
+
const sections = file === "package.json" ? ["dependencies", "devDependencies", "peerDependencies"] : ["imports"];
|
|
1849
|
+
for (const sectionName of sections) {
|
|
1850
|
+
const section = parsed[sectionName];
|
|
1851
|
+
if (!section || typeof section !== "object" || Array.isArray(section)) continue;
|
|
1852
|
+
for (const [name, range] of Object.entries(PACKAGE_RANGES)) if (name in section) {
|
|
1853
|
+
section[name] = file === "deno.json" ? `npm:${name}@${range}` : range;
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
result[file] = `${JSON.stringify(parsed, null, 2)}
|
|
1857
|
+
`;
|
|
1858
|
+
}
|
|
1859
|
+
return result;
|
|
1860
|
+
}
|
|
1861
|
+
async function existing(filePath) {
|
|
1862
|
+
try {
|
|
1863
|
+
return await promises.lstat(filePath);
|
|
1864
|
+
} catch (error) {
|
|
1865
|
+
if (error.code === "ENOENT") return void 0;
|
|
1866
|
+
throw error;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
async function writeStarterProject(options, renderers) {
|
|
1870
|
+
const ctx = { template: options.template, provider: options.provider ?? "demo", tools: options.tools ?? [], memory: options.memory ?? "none", pm: options.packageManager ?? "pnpm" };
|
|
1871
|
+
const files = align(renderers[ctx.template](ctx));
|
|
1872
|
+
await promises.mkdir(options.targetDir, { recursive: true });
|
|
1873
|
+
const target = await promises.lstat(options.targetDir);
|
|
1874
|
+
if (target.isSymbolicLink() || !target.isDirectory()) throw new Error(`Refusing to write starter into non-directory target: ${options.targetDir}`);
|
|
1875
|
+
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)`);
|
|
1876
|
+
const overwritten = [];
|
|
1877
|
+
for (const [relativePath, content] of Object.entries(files)) {
|
|
1878
|
+
const absolutePath = path__default.default.join(options.targetDir, relativePath);
|
|
1879
|
+
await promises.mkdir(path__default.default.dirname(absolutePath), { recursive: true });
|
|
1880
|
+
const destination = await existing(absolutePath);
|
|
1881
|
+
if (destination?.isSymbolicLink()) throw new Error(`Refusing to overwrite symlink: ${absolutePath}`);
|
|
1882
|
+
if (destination) overwritten.push(relativePath);
|
|
1883
|
+
const tempDir = await promises.mkdtemp(path__default.default.join(path__default.default.dirname(absolutePath), `.agentskit-init-${crypto.randomUUID()}-`));
|
|
1884
|
+
try {
|
|
1885
|
+
await promises.writeFile(path__default.default.join(tempDir, path__default.default.basename(absolutePath)), content, "utf8");
|
|
1886
|
+
await promises.rename(path__default.default.join(tempDir, path__default.default.basename(absolutePath)), absolutePath);
|
|
1887
|
+
} finally {
|
|
1888
|
+
await promises.rm(tempDir, { recursive: true, force: true }).catch(() => {
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
return overwritten;
|
|
1893
|
+
}
|
|
1747
1894
|
|
|
1748
1895
|
// src/init.ts
|
|
1749
1896
|
function adapterCall(provider, prefix = "process.env") {
|
|
@@ -3129,23 +3276,8 @@ var TEMPLATE_FN = {
|
|
|
3129
3276
|
"deno-deploy": denoDeployStarter,
|
|
3130
3277
|
angular: angularStarter
|
|
3131
3278
|
};
|
|
3132
|
-
async function
|
|
3133
|
-
|
|
3134
|
-
template: options.template,
|
|
3135
|
-
provider: options.provider ?? "demo",
|
|
3136
|
-
tools: options.tools ?? [],
|
|
3137
|
-
memory: options.memory ?? "none",
|
|
3138
|
-
pm: options.packageManager ?? "pnpm"
|
|
3139
|
-
};
|
|
3140
|
-
const files = TEMPLATE_FN[ctx.template](ctx);
|
|
3141
|
-
await promises.mkdir(options.targetDir, { recursive: true });
|
|
3142
|
-
await Promise.all(
|
|
3143
|
-
Object.entries(files).map(async ([relativePath, content]) => {
|
|
3144
|
-
const absolutePath = path__default.default.join(options.targetDir, relativePath);
|
|
3145
|
-
await promises.mkdir(path__default.default.dirname(absolutePath), { recursive: true });
|
|
3146
|
-
await promises.writeFile(absolutePath, content, "utf8");
|
|
3147
|
-
})
|
|
3148
|
-
);
|
|
3279
|
+
async function writeStarterProject2(options) {
|
|
3280
|
+
return writeStarterProject(options, TEMPLATE_FN);
|
|
3149
3281
|
}
|
|
3150
3282
|
async function runInteractiveInit(defaults = {}) {
|
|
3151
3283
|
process.stdout.write(`
|
|
@@ -3294,7 +3426,7 @@ function printNextSteps(options) {
|
|
|
3294
3426
|
|
|
3295
3427
|
// src/commands/init.ts
|
|
3296
3428
|
function registerInitCommand(program) {
|
|
3297
|
-
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) => {
|
|
3429
|
+
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) => {
|
|
3298
3430
|
const isCi = !process.stdout.isTTY || rawOptions.yes || rawOptions.template;
|
|
3299
3431
|
let resolved;
|
|
3300
3432
|
if (isCi) {
|
|
@@ -3305,7 +3437,8 @@ function registerInitCommand(program) {
|
|
|
3305
3437
|
provider: rawOptions.provider ?? "demo",
|
|
3306
3438
|
tools: rawOptions.tools ? rawOptions.tools.split(",").map((t) => t.trim()) : [],
|
|
3307
3439
|
memory: rawOptions.memory ?? "none",
|
|
3308
|
-
packageManager: rawOptions.pm ?? "pnpm"
|
|
3440
|
+
packageManager: rawOptions.pm ?? "pnpm",
|
|
3441
|
+
force: Boolean(rawOptions.force)
|
|
3309
3442
|
};
|
|
3310
3443
|
} else {
|
|
3311
3444
|
const result = await runInteractiveInit({
|
|
@@ -3315,17 +3448,22 @@ function registerInitCommand(program) {
|
|
|
3315
3448
|
if (result.cancelled) {
|
|
3316
3449
|
process.exit(0);
|
|
3317
3450
|
}
|
|
3318
|
-
resolved = result.options;
|
|
3451
|
+
resolved = { ...result.options, force: Boolean(rawOptions.force) };
|
|
3319
3452
|
}
|
|
3320
|
-
await
|
|
3453
|
+
const overwritten = await writeStarterProject2(resolved) ?? [];
|
|
3321
3454
|
if (isCi) {
|
|
3322
3455
|
process.stdout.write(
|
|
3323
3456
|
`Created ${resolved.template} starter in ${path__default.default.relative(process.cwd(), resolved.targetDir) || "."}
|
|
3324
|
-
|
|
3457
|
+
` + (overwritten.length > 0 ? `Overwritten: ${overwritten.join(", ")}
|
|
3458
|
+
` : "") + `\u2605 Star AgentsKit (solo-built \u2014 it helps a lot): https://github.com/AgentsKit-io/agentskit
|
|
3325
3459
|
`
|
|
3326
3460
|
);
|
|
3327
3461
|
} else {
|
|
3328
3462
|
printNextSteps(resolved);
|
|
3463
|
+
if (overwritten.length > 0) {
|
|
3464
|
+
process.stdout.write(`Overwritten: ${overwritten.join(", ")}
|
|
3465
|
+
`);
|
|
3466
|
+
}
|
|
3329
3467
|
}
|
|
3330
3468
|
});
|
|
3331
3469
|
}
|
|
@@ -3574,8 +3712,8 @@ async function checkPnpm() {
|
|
|
3574
3712
|
};
|
|
3575
3713
|
}
|
|
3576
3714
|
async function checkPackageJson() {
|
|
3577
|
-
const
|
|
3578
|
-
if (!fs.existsSync(
|
|
3715
|
+
const path6 = path.join(process.cwd(), "package.json");
|
|
3716
|
+
if (!fs.existsSync(path6)) {
|
|
3579
3717
|
return {
|
|
3580
3718
|
status: "warn",
|
|
3581
3719
|
name: "package.json",
|
|
@@ -3584,7 +3722,7 @@ async function checkPackageJson() {
|
|
|
3584
3722
|
};
|
|
3585
3723
|
}
|
|
3586
3724
|
try {
|
|
3587
|
-
const pkg = JSON.parse(await promises.readFile(
|
|
3725
|
+
const pkg = JSON.parse(await promises.readFile(path6, "utf8"));
|
|
3588
3726
|
const deps = {
|
|
3589
3727
|
...pkg.dependencies ?? {},
|
|
3590
3728
|
...pkg.devDependencies ?? {}
|
|
@@ -3817,6 +3955,14 @@ function registerDoctorCommand(program) {
|
|
|
3817
3955
|
if (report.fail > 0) process.exit(1);
|
|
3818
3956
|
});
|
|
3819
3957
|
}
|
|
3958
|
+
var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bin.cjs', document.baseURI).href)));
|
|
3959
|
+
function resolveTsxCli() {
|
|
3960
|
+
try {
|
|
3961
|
+
return require2.resolve("tsx/cli");
|
|
3962
|
+
} catch {
|
|
3963
|
+
throw new Error("TypeScript execution requires the packaged tsx dependency");
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3820
3966
|
var DEFAULT_WATCH = [
|
|
3821
3967
|
"**/*.ts",
|
|
3822
3968
|
"**/*.tsx",
|
|
@@ -3844,8 +3990,8 @@ function startDev(options) {
|
|
|
3844
3990
|
const stderr = options.stderr ?? process.stderr;
|
|
3845
3991
|
const debounceMs = options.debounceMs ?? 200;
|
|
3846
3992
|
const isTs = entry.endsWith(".ts") || entry.endsWith(".tsx");
|
|
3847
|
-
const cmd = isTs ?
|
|
3848
|
-
const baseArgs = [entry, ...options.scriptArgs ?? []];
|
|
3993
|
+
const cmd = isTs ? process.execPath : "node";
|
|
3994
|
+
const baseArgs = isTs ? [resolveTsxCli(), entry, ...options.scriptArgs ?? []] : [entry, ...options.scriptArgs ?? []];
|
|
3849
3995
|
const spawnFn = options.spawn ?? ((c, a) => child_process.spawn(c, a, {
|
|
3850
3996
|
stdio: ["inherit", "pipe", "pipe"],
|
|
3851
3997
|
env: { ...process.env, FORCE_COLOR: "1" }
|
|
@@ -3857,7 +4003,9 @@ function startDev(options) {
|
|
|
3857
4003
|
let child;
|
|
3858
4004
|
let restartCount = 0;
|
|
3859
4005
|
let restartTimer;
|
|
4006
|
+
let delayedStartTimer;
|
|
3860
4007
|
let stopped = false;
|
|
4008
|
+
let stdinListener;
|
|
3861
4009
|
let resolveDone;
|
|
3862
4010
|
const done = new Promise((r) => {
|
|
3863
4011
|
resolveDone = r;
|
|
@@ -3883,15 +4031,18 @@ function startDev(options) {
|
|
|
3883
4031
|
}
|
|
3884
4032
|
});
|
|
3885
4033
|
};
|
|
3886
|
-
const restart = (
|
|
4034
|
+
const restart = (path6) => {
|
|
3887
4035
|
if (restartTimer) clearTimeout(restartTimer);
|
|
3888
4036
|
restartTimer = setTimeout(() => {
|
|
3889
4037
|
restartTimer = void 0;
|
|
3890
|
-
banner(`\u21BB change detected \u2014 ${
|
|
4038
|
+
banner(`\u21BB change detected \u2014 ${path6}`, "yellow");
|
|
3891
4039
|
if (child && !child.killed && child.exitCode === null) {
|
|
3892
4040
|
child.kill("SIGTERM");
|
|
3893
4041
|
}
|
|
3894
|
-
setTimeout(
|
|
4042
|
+
delayedStartTimer = setTimeout(() => {
|
|
4043
|
+
delayedStartTimer = void 0;
|
|
4044
|
+
if (!stopped) startChild();
|
|
4045
|
+
}, 80);
|
|
3895
4046
|
}, debounceMs);
|
|
3896
4047
|
};
|
|
3897
4048
|
watcher.on("change", restart);
|
|
@@ -3902,6 +4053,9 @@ function startDev(options) {
|
|
|
3902
4053
|
if (stopped) return;
|
|
3903
4054
|
stopped = true;
|
|
3904
4055
|
if (restartTimer) clearTimeout(restartTimer);
|
|
4056
|
+
if (delayedStartTimer) clearTimeout(delayedStartTimer);
|
|
4057
|
+
if (stdinListener) process.stdin.off("data", stdinListener);
|
|
4058
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) process.stdin.setRawMode(false);
|
|
3905
4059
|
if (child && !child.killed && child.exitCode === null) {
|
|
3906
4060
|
child.kill("SIGTERM");
|
|
3907
4061
|
}
|
|
@@ -3912,11 +4066,12 @@ function startDev(options) {
|
|
|
3912
4066
|
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
3913
4067
|
process.stdin.setRawMode(true);
|
|
3914
4068
|
process.stdin.resume();
|
|
3915
|
-
|
|
4069
|
+
stdinListener = (data) => {
|
|
3916
4070
|
const key = data.toString();
|
|
3917
4071
|
if (key === "r") restart("manual");
|
|
3918
4072
|
if (key === "q" || key === "") void stop();
|
|
3919
|
-
}
|
|
4073
|
+
};
|
|
4074
|
+
process.stdin.on("data", stdinListener);
|
|
3920
4075
|
}
|
|
3921
4076
|
return {
|
|
3922
4077
|
done,
|
|
@@ -3956,7 +4111,7 @@ function registerConfigCommand(program) {
|
|
|
3956
4111
|
const targetPath = isLocal ? path__default.default.join(process.cwd(), ".agentskit.config.json") : path__default.default.join(os.homedir(), ".agentskit", "config.json");
|
|
3957
4112
|
if (action === "show") {
|
|
3958
4113
|
const config = await loadConfig();
|
|
3959
|
-
process.stdout.write(JSON.stringify(config ?? {}, null, 2) + "\n");
|
|
4114
|
+
process.stdout.write(JSON.stringify(redactConfig(config ?? {}), null, 2) + "\n");
|
|
3960
4115
|
return;
|
|
3961
4116
|
}
|
|
3962
4117
|
if (action !== "init") {
|
|
@@ -4015,6 +4170,7 @@ async function startTunnel(options) {
|
|
|
4015
4170
|
});
|
|
4016
4171
|
let requests = 0;
|
|
4017
4172
|
let stopped = false;
|
|
4173
|
+
let signalHandler;
|
|
4018
4174
|
let resolveDone;
|
|
4019
4175
|
const done = new Promise((r) => {
|
|
4020
4176
|
resolveDone = r;
|
|
@@ -4024,12 +4180,14 @@ async function startTunnel(options) {
|
|
|
4024
4180
|
});
|
|
4025
4181
|
tunnel.on("close", () => {
|
|
4026
4182
|
if (stopped) return;
|
|
4183
|
+
if (signalHandler) process.off("SIGINT", signalHandler);
|
|
4027
4184
|
banner(`tunnel closed by remote`, "yellow");
|
|
4028
4185
|
resolveDone();
|
|
4029
4186
|
});
|
|
4030
4187
|
tunnel.on("error", (...args) => {
|
|
4031
4188
|
const err = args[0];
|
|
4032
4189
|
banner(`error: ${err?.message ?? "unknown"}`, "red");
|
|
4190
|
+
resolveDone();
|
|
4033
4191
|
});
|
|
4034
4192
|
banner(`\u2713 ready`, "green");
|
|
4035
4193
|
stdout.write("\n");
|
|
@@ -4045,14 +4203,16 @@ async function startTunnel(options) {
|
|
|
4045
4203
|
const stop = async () => {
|
|
4046
4204
|
if (stopped) return;
|
|
4047
4205
|
stopped = true;
|
|
4206
|
+
if (signalHandler) process.off("SIGINT", signalHandler);
|
|
4048
4207
|
tunnel.close();
|
|
4049
4208
|
banner(`stopped \u2014 proxied ${requests} request${requests === 1 ? "" : "s"}`, "cyan");
|
|
4050
4209
|
resolveDone();
|
|
4051
4210
|
};
|
|
4052
4211
|
if (process.stdin.isTTY) {
|
|
4053
|
-
|
|
4212
|
+
signalHandler = () => {
|
|
4054
4213
|
void stop();
|
|
4055
|
-
}
|
|
4214
|
+
};
|
|
4215
|
+
process.on("SIGINT", signalHandler);
|
|
4056
4216
|
}
|
|
4057
4217
|
return {
|
|
4058
4218
|
url: tunnel.url,
|
|
@@ -4147,10 +4307,10 @@ async function indexSources(rag, config, cwd) {
|
|
|
4147
4307
|
}
|
|
4148
4308
|
}
|
|
4149
4309
|
const documents = await Promise.all(
|
|
4150
|
-
absolutePaths.map(async (
|
|
4151
|
-
id:
|
|
4152
|
-
content: await promises.readFile(
|
|
4153
|
-
source:
|
|
4310
|
+
absolutePaths.map(async (path6) => ({
|
|
4311
|
+
id: path6,
|
|
4312
|
+
content: await promises.readFile(path6, "utf8"),
|
|
4313
|
+
source: path6
|
|
4154
4314
|
}))
|
|
4155
4315
|
);
|
|
4156
4316
|
if (documents.length > 0) await rag.ingest(documents);
|
|
@@ -4285,7 +4445,7 @@ function scaffoldAgent(schema) {
|
|
|
4285
4445
|
return files;
|
|
4286
4446
|
}
|
|
4287
4447
|
async function writeScaffold(files, outDir, opts = {}) {
|
|
4288
|
-
const { writeFile: writeFile5, mkdir: mkdir5, access } = await import('fs/promises');
|
|
4448
|
+
const { writeFile: writeFile5, mkdir: mkdir5, access: access2 } = await import('fs/promises');
|
|
4289
4449
|
const { dirname: dirname5, join: join11 } = await import('path');
|
|
4290
4450
|
const written = [];
|
|
4291
4451
|
for (const f of files) {
|
|
@@ -4293,7 +4453,7 @@ async function writeScaffold(files, outDir, opts = {}) {
|
|
|
4293
4453
|
await mkdir5(dirname5(full), { recursive: true });
|
|
4294
4454
|
if (!opts.overwrite) {
|
|
4295
4455
|
try {
|
|
4296
|
-
await
|
|
4456
|
+
await access2(full);
|
|
4297
4457
|
continue;
|
|
4298
4458
|
} catch {
|
|
4299
4459
|
}
|
|
@@ -4769,66 +4929,66 @@ After writing, summarise which files landed and what each one does.
|
|
|
4769
4929
|
// src/rules.ts
|
|
4770
4930
|
var CODEX_BLOCK_START = "<!-- agentskit-codex-profile:start -->";
|
|
4771
4931
|
var CODEX_BLOCK_END = "<!-- agentskit-codex-profile:end -->";
|
|
4772
|
-
async function ensureDir2(
|
|
4773
|
-
await promises.mkdir(path.dirname(
|
|
4932
|
+
async function ensureDir2(path6) {
|
|
4933
|
+
await promises.mkdir(path.dirname(path6), { recursive: true });
|
|
4774
4934
|
}
|
|
4775
4935
|
async function writeIfChanged(absPath, contents, force) {
|
|
4776
|
-
let
|
|
4936
|
+
let existing2;
|
|
4777
4937
|
try {
|
|
4778
|
-
|
|
4938
|
+
existing2 = await promises.readFile(absPath, "utf8");
|
|
4779
4939
|
} catch {
|
|
4780
4940
|
}
|
|
4781
|
-
if (
|
|
4782
|
-
if (
|
|
4941
|
+
if (existing2 === contents) return "skipped";
|
|
4942
|
+
if (existing2 !== void 0 && !force) {
|
|
4783
4943
|
return "skipped";
|
|
4784
4944
|
}
|
|
4785
4945
|
await ensureDir2(absPath);
|
|
4786
4946
|
await promises.writeFile(absPath, contents, "utf8");
|
|
4787
|
-
return
|
|
4947
|
+
return existing2 === void 0 ? "wrote" : "updated";
|
|
4788
4948
|
}
|
|
4789
4949
|
async function writeCursor(rootDir, force) {
|
|
4790
|
-
const
|
|
4791
|
-
const action = await writeIfChanged(
|
|
4792
|
-
return [{ path:
|
|
4950
|
+
const path6 = path.join(rootDir, ".cursor", "rules", "agentskit.mdc");
|
|
4951
|
+
const action = await writeIfChanged(path6, CURSOR_RULE, force);
|
|
4952
|
+
return [{ path: path6, action }];
|
|
4793
4953
|
}
|
|
4794
4954
|
async function writeWindsurf(rootDir, force) {
|
|
4795
|
-
const
|
|
4796
|
-
const action = await writeIfChanged(
|
|
4797
|
-
return [{ path:
|
|
4955
|
+
const path6 = path.join(rootDir, ".windsurfrules");
|
|
4956
|
+
const action = await writeIfChanged(path6, WINDSURF_RULE, force);
|
|
4957
|
+
return [{ path: path6, action }];
|
|
4798
4958
|
}
|
|
4799
4959
|
async function writeCodex(rootDir, force) {
|
|
4800
|
-
const
|
|
4801
|
-
let
|
|
4960
|
+
const path6 = path.join(rootDir, "AGENTS.md");
|
|
4961
|
+
let existing2 = "";
|
|
4802
4962
|
try {
|
|
4803
|
-
|
|
4963
|
+
existing2 = await promises.readFile(path6, "utf8");
|
|
4804
4964
|
} catch {
|
|
4805
4965
|
}
|
|
4806
|
-
const startIdx =
|
|
4807
|
-
const endIdx = startIdx >= 0 ?
|
|
4966
|
+
const startIdx = existing2.indexOf(CODEX_BLOCK_START);
|
|
4967
|
+
const endIdx = startIdx >= 0 ? existing2.lastIndexOf(CODEX_BLOCK_END, existing2.length) : -1;
|
|
4808
4968
|
if (startIdx >= 0 && endIdx <= startIdx) {
|
|
4809
4969
|
throw new core.ConfigError({
|
|
4810
4970
|
code: core.ErrorCodes.AK_CONFIG_INVALID,
|
|
4811
|
-
message: `${
|
|
4971
|
+
message: `${path6}: found agentskit-codex-profile:start sentinel but no matching :end after it \u2014 refusing to modify a half-edited block.`,
|
|
4812
4972
|
hint: "Restore the closing sentinel or remove the partial block, then re-run."
|
|
4813
4973
|
});
|
|
4814
4974
|
}
|
|
4815
4975
|
let next;
|
|
4816
4976
|
if (startIdx >= 0 && endIdx > startIdx) {
|
|
4817
|
-
next =
|
|
4818
|
-
} else if (
|
|
4819
|
-
next = `${
|
|
4977
|
+
next = existing2.slice(0, startIdx) + CODEX_PROFILE.trimEnd() + existing2.slice(endIdx + CODEX_BLOCK_END.length);
|
|
4978
|
+
} else if (existing2) {
|
|
4979
|
+
next = `${existing2.replace(/\s+$/, "")}
|
|
4820
4980
|
|
|
4821
4981
|
${CODEX_PROFILE}`;
|
|
4822
4982
|
} else {
|
|
4823
4983
|
next = CODEX_PROFILE;
|
|
4824
4984
|
}
|
|
4825
|
-
if (next ===
|
|
4826
|
-
if (
|
|
4827
|
-
return [{ path:
|
|
4985
|
+
if (next === existing2) return [{ path: path6, action: "skipped" }];
|
|
4986
|
+
if (existing2 && !force) {
|
|
4987
|
+
return [{ path: path6, action: "skipped" }];
|
|
4828
4988
|
}
|
|
4829
|
-
await ensureDir2(
|
|
4830
|
-
await promises.writeFile(
|
|
4831
|
-
return [{ path:
|
|
4989
|
+
await ensureDir2(path6);
|
|
4990
|
+
await promises.writeFile(path6, next, "utf8");
|
|
4991
|
+
return [{ path: path6, action: existing2 ? "updated" : "wrote" }];
|
|
4832
4992
|
}
|
|
4833
4993
|
async function writeClaudeCode(rootDir, force) {
|
|
4834
4994
|
const out = [];
|
|
@@ -4916,17 +5076,78 @@ rules failed: ${message}
|
|
|
4916
5076
|
}
|
|
4917
5077
|
var RAW_BASE = "https://raw.githubusercontent.com/AgentsKit-io/agentskit-registry/main";
|
|
4918
5078
|
var HOSTED_BASE = `${RAW_BASE}/public/r`;
|
|
5079
|
+
var FETCH_TIMEOUT_MS = 15e3;
|
|
5080
|
+
var MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
5081
|
+
var MAX_AGENT_FILES = 100;
|
|
5082
|
+
var SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
5083
|
+
function assertSafeId(id) {
|
|
5084
|
+
if (!SAFE_ID.test(id)) throw new Error(`Invalid registry agent id: ${JSON.stringify(id)}`);
|
|
5085
|
+
}
|
|
5086
|
+
function assertSafeRelativePath(filePath) {
|
|
5087
|
+
if (!filePath || path.isAbsolute(filePath) || filePath.includes("\0")) {
|
|
5088
|
+
throw new Error(`Invalid registry file path: ${JSON.stringify(filePath)}`);
|
|
5089
|
+
}
|
|
5090
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
5091
|
+
if (normalized.split("/").some((part) => part === ".." || part === "")) {
|
|
5092
|
+
throw new Error(`Registry file path must stay relative: ${JSON.stringify(filePath)}`);
|
|
5093
|
+
}
|
|
5094
|
+
}
|
|
5095
|
+
function assertInside(parent, child) {
|
|
5096
|
+
const rel = path.relative(path.resolve(parent), path.resolve(child));
|
|
5097
|
+
if (rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) {
|
|
5098
|
+
throw new Error(`Registry destination escapes target directory: ${child}`);
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
5101
|
+
async function assertNoSymlinkAncestors(targetDir, baseDir) {
|
|
5102
|
+
for (const path6 of [targetDir, baseDir]) {
|
|
5103
|
+
try {
|
|
5104
|
+
if ((await promises.lstat(path.resolve(path6))).isSymbolicLink()) {
|
|
5105
|
+
throw new Error(`Registry destination contains a symlink ancestor: ${path.resolve(path6)}`);
|
|
5106
|
+
}
|
|
5107
|
+
} catch (error) {
|
|
5108
|
+
if (error.code !== "ENOENT") throw error;
|
|
5109
|
+
}
|
|
5110
|
+
}
|
|
5111
|
+
let current = path.resolve(baseDir);
|
|
5112
|
+
while (true) {
|
|
5113
|
+
try {
|
|
5114
|
+
if ((await promises.lstat(current)).isSymbolicLink()) {
|
|
5115
|
+
throw new Error(`Registry destination contains a symlink ancestor: ${current}`);
|
|
5116
|
+
}
|
|
5117
|
+
return;
|
|
5118
|
+
} catch (error) {
|
|
5119
|
+
if (error.code !== "ENOENT") throw error;
|
|
5120
|
+
}
|
|
5121
|
+
const parent = path.dirname(current);
|
|
5122
|
+
if (parent === current) return;
|
|
5123
|
+
current = parent;
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
async function fetchWithLimits(url, fetchImpl) {
|
|
5127
|
+
const controller = new AbortController();
|
|
5128
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
5129
|
+
try {
|
|
5130
|
+
const response = await fetchImpl(url, { signal: controller.signal });
|
|
5131
|
+
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
|
|
5132
|
+
const body = await response.arrayBuffer();
|
|
5133
|
+
if (body.byteLength > MAX_RESPONSE_BYTES) {
|
|
5134
|
+
throw new Error(`Registry response exceeded ${MAX_RESPONSE_BYTES} bytes`);
|
|
5135
|
+
}
|
|
5136
|
+
return new Response(body, { status: response.status, headers: response.headers });
|
|
5137
|
+
} finally {
|
|
5138
|
+
clearTimeout(timer);
|
|
5139
|
+
}
|
|
5140
|
+
}
|
|
4919
5141
|
async function getJson(url, fetchImpl) {
|
|
4920
|
-
const res = await
|
|
4921
|
-
|
|
4922
|
-
return res.json();
|
|
5142
|
+
const res = await fetchWithLimits(url, fetchImpl);
|
|
5143
|
+
return JSON.parse(await res.text());
|
|
4923
5144
|
}
|
|
4924
5145
|
async function getText(url, fetchImpl) {
|
|
4925
|
-
const res = await
|
|
4926
|
-
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
|
|
5146
|
+
const res = await fetchWithLimits(url, fetchImpl);
|
|
4927
5147
|
return res.text();
|
|
4928
5148
|
}
|
|
4929
5149
|
async function fetchAgent(id, options = {}) {
|
|
5150
|
+
assertSafeId(id);
|
|
4930
5151
|
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
4931
5152
|
try {
|
|
4932
5153
|
const hosted = await getJson(`${HOSTED_BASE}/${id}.json`, fetchImpl);
|
|
@@ -4934,6 +5155,10 @@ async function fetchAgent(id, options = {}) {
|
|
|
4934
5155
|
throw new Error("hosted entry missing sources");
|
|
4935
5156
|
} catch {
|
|
4936
5157
|
const meta = await getJson(`${RAW_BASE}/registry/${id}/meta.json`, fetchImpl);
|
|
5158
|
+
if (!Array.isArray(meta.files) || meta.files.length > MAX_AGENT_FILES) {
|
|
5159
|
+
throw new Error(`Registry agent contains too many files (maximum ${MAX_AGENT_FILES})`);
|
|
5160
|
+
}
|
|
5161
|
+
for (const rel of meta.files) assertSafeRelativePath(rel);
|
|
4937
5162
|
const sources = await Promise.all(
|
|
4938
5163
|
meta.files.map(async (rel) => ({
|
|
4939
5164
|
path: rel,
|
|
@@ -4950,10 +5175,10 @@ function resolveSystemPrompt(agent) {
|
|
|
4950
5175
|
const m = src.match(/systemPrompt:\s*`((?:\\.|[^`\\])*)`/);
|
|
4951
5176
|
return m ? m[1].replace(/\\`/g, "`").replace(/\\\$\{/g, "${") : null;
|
|
4952
5177
|
}
|
|
4953
|
-
async function defaultExists(
|
|
4954
|
-
const { access } = await import('fs/promises');
|
|
5178
|
+
async function defaultExists(path6) {
|
|
5179
|
+
const { access: access2 } = await import('fs/promises');
|
|
4955
5180
|
try {
|
|
4956
|
-
await
|
|
5181
|
+
await access2(path6);
|
|
4957
5182
|
return true;
|
|
4958
5183
|
} catch {
|
|
4959
5184
|
return false;
|
|
@@ -4977,17 +5202,24 @@ async function addAgent(id, options = {}) {
|
|
|
4977
5202
|
assertInstallable(agent);
|
|
4978
5203
|
const baseDir = options.outDir ?? "agents";
|
|
4979
5204
|
const targetDir = path.join(baseDir, id);
|
|
5205
|
+
assertInside(baseDir, targetDir);
|
|
5206
|
+
await assertNoSymlinkAncestors(targetDir, baseDir);
|
|
4980
5207
|
const exists = options.existsImpl ?? defaultExists;
|
|
4981
|
-
const write = options.writeFileImpl ?? (async (
|
|
4982
|
-
await promises.mkdir(path.dirname(
|
|
4983
|
-
await promises.writeFile(
|
|
5208
|
+
const write = options.writeFileImpl ?? (async (path6, content) => {
|
|
5209
|
+
await promises.mkdir(path.dirname(path6), { recursive: true });
|
|
5210
|
+
await promises.writeFile(path6, content, "utf8");
|
|
4984
5211
|
});
|
|
4985
5212
|
const written = [];
|
|
4986
5213
|
for (const file of agent.sources) {
|
|
5214
|
+
assertSafeRelativePath(file.path);
|
|
4987
5215
|
const dest = path.join(targetDir, file.path);
|
|
5216
|
+
assertInside(targetDir, dest);
|
|
4988
5217
|
if (!options.force && await exists(dest)) {
|
|
4989
5218
|
throw new Error(`${dest} already exists (re-run with --force to overwrite)`);
|
|
4990
5219
|
}
|
|
5220
|
+
}
|
|
5221
|
+
for (const file of agent.sources) {
|
|
5222
|
+
const dest = path.join(targetDir, file.path);
|
|
4991
5223
|
await write(dest, file.content);
|
|
4992
5224
|
written.push(dest);
|
|
4993
5225
|
}
|
|
@@ -5050,8 +5282,8 @@ function allDeps(pkg) {
|
|
|
5050
5282
|
if (!pkg) return {};
|
|
5051
5283
|
return { ...pkg.peerDependencies, ...pkg.devDependencies, ...pkg.dependencies };
|
|
5052
5284
|
}
|
|
5053
|
-
function readJson(fs,
|
|
5054
|
-
const raw = fs.readFile(
|
|
5285
|
+
function readJson(fs, path6) {
|
|
5286
|
+
const raw = fs.readFile(path6);
|
|
5055
5287
|
if (raw == null) return null;
|
|
5056
5288
|
try {
|
|
5057
5289
|
return JSON.parse(raw);
|
|
@@ -5235,13 +5467,13 @@ function readConfig(io, root = ".") {
|
|
|
5235
5467
|
return raw == null ? null : parseConfig(raw);
|
|
5236
5468
|
}
|
|
5237
5469
|
function writeConfig(io, config, root = ".") {
|
|
5238
|
-
const
|
|
5239
|
-
io.write(
|
|
5240
|
-
return
|
|
5470
|
+
const path6 = path.join(root, CONFIG_PATH);
|
|
5471
|
+
io.write(path6, serializeConfig(config));
|
|
5472
|
+
return path6;
|
|
5241
5473
|
}
|
|
5242
5474
|
function resolveConfig(io, scan, root = ".") {
|
|
5243
|
-
const
|
|
5244
|
-
if (
|
|
5475
|
+
const existing2 = readConfig(io, root);
|
|
5476
|
+
if (existing2) return { config: existing2, fromDisk: true };
|
|
5245
5477
|
return { config: buildConfig(scan), fromDisk: false };
|
|
5246
5478
|
}
|
|
5247
5479
|
var IntegrityError = class extends Error {
|
|
@@ -5326,7 +5558,18 @@ function resolveAuthHeader(base, config, env = {}) {
|
|
|
5326
5558
|
const map = config?.registryAuth;
|
|
5327
5559
|
if (!map) return {};
|
|
5328
5560
|
for (const [registryBase, envVar] of Object.entries(map)) {
|
|
5329
|
-
|
|
5561
|
+
let configured;
|
|
5562
|
+
let target;
|
|
5563
|
+
try {
|
|
5564
|
+
configured = new URL(registryBase);
|
|
5565
|
+
target = new URL(base);
|
|
5566
|
+
} catch {
|
|
5567
|
+
continue;
|
|
5568
|
+
}
|
|
5569
|
+
const configuredPath = configured.pathname.replace(/\/+$/, "") || "/";
|
|
5570
|
+
const targetPath = target.pathname.replace(/\/+$/, "") || "/";
|
|
5571
|
+
const samePathBoundary = configuredPath === "/" || targetPath === configuredPath || targetPath.startsWith(`${configuredPath}/`);
|
|
5572
|
+
if (target.origin === configured.origin && samePathBoundary) {
|
|
5330
5573
|
const token = env[envVar];
|
|
5331
5574
|
if (token) return { authorization: `Bearer ${token}` };
|
|
5332
5575
|
}
|
|
@@ -5345,8 +5588,8 @@ function verifyChecksums(files) {
|
|
|
5345
5588
|
function manifestUrl(ref) {
|
|
5346
5589
|
return `${ref.base}/r/${ref.itemId}.json`;
|
|
5347
5590
|
}
|
|
5348
|
-
function fileUrl(ref,
|
|
5349
|
-
return `${ref.base}/r/${ref.itemId}/${
|
|
5591
|
+
function fileUrl(ref, path6) {
|
|
5592
|
+
return `${ref.base}/r/${ref.itemId}/${path6}`;
|
|
5350
5593
|
}
|
|
5351
5594
|
async function getText2(fetchImpl, url, headers) {
|
|
5352
5595
|
const res = await fetchImpl(url, { headers });
|
|
@@ -5683,44 +5926,44 @@ async function addComponent(options, deps) {
|
|
|
5683
5926
|
}
|
|
5684
5927
|
function nodeScanFs() {
|
|
5685
5928
|
return {
|
|
5686
|
-
readFile(
|
|
5929
|
+
readFile(path6) {
|
|
5687
5930
|
try {
|
|
5688
|
-
return fs.readFileSync(
|
|
5931
|
+
return fs.readFileSync(path6, "utf8");
|
|
5689
5932
|
} catch {
|
|
5690
5933
|
return null;
|
|
5691
5934
|
}
|
|
5692
5935
|
},
|
|
5693
|
-
exists(
|
|
5694
|
-
return fs.existsSync(
|
|
5936
|
+
exists(path6) {
|
|
5937
|
+
return fs.existsSync(path6);
|
|
5695
5938
|
}
|
|
5696
5939
|
};
|
|
5697
5940
|
}
|
|
5698
5941
|
function nodeWriteFs() {
|
|
5699
5942
|
return {
|
|
5700
|
-
exists(
|
|
5701
|
-
return fs.existsSync(
|
|
5943
|
+
exists(path6) {
|
|
5944
|
+
return fs.existsSync(path6);
|
|
5702
5945
|
},
|
|
5703
|
-
write(
|
|
5704
|
-
fs.mkdirSync(path.dirname(
|
|
5705
|
-
fs.writeFileSync(
|
|
5946
|
+
write(path6, content) {
|
|
5947
|
+
fs.mkdirSync(path.dirname(path6), { recursive: true });
|
|
5948
|
+
fs.writeFileSync(path6, content, "utf8");
|
|
5706
5949
|
},
|
|
5707
|
-
remove(
|
|
5708
|
-
fs.rmSync(
|
|
5950
|
+
remove(path6) {
|
|
5951
|
+
fs.rmSync(path6, { force: true });
|
|
5709
5952
|
}
|
|
5710
5953
|
};
|
|
5711
5954
|
}
|
|
5712
5955
|
function nodeConfigIo() {
|
|
5713
5956
|
return {
|
|
5714
|
-
read(
|
|
5957
|
+
read(path6) {
|
|
5715
5958
|
try {
|
|
5716
|
-
return fs.readFileSync(
|
|
5959
|
+
return fs.readFileSync(path6, "utf8");
|
|
5717
5960
|
} catch {
|
|
5718
5961
|
return null;
|
|
5719
5962
|
}
|
|
5720
5963
|
},
|
|
5721
|
-
write(
|
|
5722
|
-
fs.mkdirSync(path.dirname(
|
|
5723
|
-
fs.writeFileSync(
|
|
5964
|
+
write(path6, content) {
|
|
5965
|
+
fs.mkdirSync(path.dirname(path6), { recursive: true });
|
|
5966
|
+
fs.writeFileSync(path6, content, "utf8");
|
|
5724
5967
|
}
|
|
5725
5968
|
};
|
|
5726
5969
|
}
|
|
@@ -5940,7 +6183,7 @@ update failed: ${err instanceof Error ? err.message : String(err)}
|
|
|
5940
6183
|
// src/commands/index.ts
|
|
5941
6184
|
function createCli() {
|
|
5942
6185
|
const program = new commander.Command();
|
|
5943
|
-
program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.");
|
|
6186
|
+
program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.").version(package_default.version);
|
|
5944
6187
|
registerChatCommand(program);
|
|
5945
6188
|
registerRunCommand(program);
|
|
5946
6189
|
registerInitCommand(program);
|