zelari-code 2.22.0 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/main.bundled.js +464 -207
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/safety/selfKillGuard.js +147 -0
- package/dist/cli/safety/selfKillGuard.js.map +1 -0
- package/dist/cli/serve/harnessServer.js +11 -0
- package/dist/cli/serve/harnessServer.js.map +1 -1
- package/dist/cli/serve/spineLockSweep.js +76 -0
- package/dist/cli/serve/spineLockSweep.js.map +1 -0
- package/dist/cli/sessionSpine.js +11 -3
- package/dist/cli/sessionSpine.js.map +1 -1
- package/dist/cli/toolRegistry.js +21 -0
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/execProcess.js +11 -0
- package/dist/cli/tools/execProcess.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -3288,10 +3288,10 @@ function mergeDefs(...defs) {
|
|
|
3288
3288
|
function cloneDef(schema) {
|
|
3289
3289
|
return mergeDefs(schema._zod.def);
|
|
3290
3290
|
}
|
|
3291
|
-
function getElementAtPath(obj,
|
|
3292
|
-
if (!
|
|
3291
|
+
function getElementAtPath(obj, path92) {
|
|
3292
|
+
if (!path92)
|
|
3293
3293
|
return obj;
|
|
3294
|
-
return
|
|
3294
|
+
return path92.reduce((acc, key) => acc?.[key], obj);
|
|
3295
3295
|
}
|
|
3296
3296
|
function promiseAllObject(promisesObj) {
|
|
3297
3297
|
const keys = Object.keys(promisesObj);
|
|
@@ -3619,11 +3619,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
3619
3619
|
}
|
|
3620
3620
|
return false;
|
|
3621
3621
|
}
|
|
3622
|
-
function prefixIssues(
|
|
3622
|
+
function prefixIssues(path92, issues) {
|
|
3623
3623
|
return issues.map((iss) => {
|
|
3624
3624
|
var _a3;
|
|
3625
3625
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
3626
|
-
iss.path.unshift(
|
|
3626
|
+
iss.path.unshift(path92);
|
|
3627
3627
|
return iss;
|
|
3628
3628
|
});
|
|
3629
3629
|
}
|
|
@@ -3841,16 +3841,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
3841
3841
|
}
|
|
3842
3842
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
3843
3843
|
const fieldErrors = { _errors: [] };
|
|
3844
|
-
const processError = (error52,
|
|
3844
|
+
const processError = (error52, path92 = []) => {
|
|
3845
3845
|
for (const issue2 of error52.issues) {
|
|
3846
3846
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
3847
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
3847
|
+
issue2.errors.map((issues) => processError({ issues }, [...path92, ...issue2.path]));
|
|
3848
3848
|
} else if (issue2.code === "invalid_key") {
|
|
3849
|
-
processError({ issues: issue2.issues }, [...
|
|
3849
|
+
processError({ issues: issue2.issues }, [...path92, ...issue2.path]);
|
|
3850
3850
|
} else if (issue2.code === "invalid_element") {
|
|
3851
|
-
processError({ issues: issue2.issues }, [...
|
|
3851
|
+
processError({ issues: issue2.issues }, [...path92, ...issue2.path]);
|
|
3852
3852
|
} else {
|
|
3853
|
-
const fullpath = [...
|
|
3853
|
+
const fullpath = [...path92, ...issue2.path];
|
|
3854
3854
|
if (fullpath.length === 0) {
|
|
3855
3855
|
fieldErrors._errors.push(mapper(issue2));
|
|
3856
3856
|
} else {
|
|
@@ -3877,17 +3877,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
3877
3877
|
}
|
|
3878
3878
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
3879
3879
|
const result = { errors: [] };
|
|
3880
|
-
const processError = (error52,
|
|
3880
|
+
const processError = (error52, path92 = []) => {
|
|
3881
3881
|
var _a3, _b;
|
|
3882
3882
|
for (const issue2 of error52.issues) {
|
|
3883
3883
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
3884
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
3884
|
+
issue2.errors.map((issues) => processError({ issues }, [...path92, ...issue2.path]));
|
|
3885
3885
|
} else if (issue2.code === "invalid_key") {
|
|
3886
|
-
processError({ issues: issue2.issues }, [...
|
|
3886
|
+
processError({ issues: issue2.issues }, [...path92, ...issue2.path]);
|
|
3887
3887
|
} else if (issue2.code === "invalid_element") {
|
|
3888
|
-
processError({ issues: issue2.issues }, [...
|
|
3888
|
+
processError({ issues: issue2.issues }, [...path92, ...issue2.path]);
|
|
3889
3889
|
} else {
|
|
3890
|
-
const fullpath = [...
|
|
3890
|
+
const fullpath = [...path92, ...issue2.path];
|
|
3891
3891
|
if (fullpath.length === 0) {
|
|
3892
3892
|
result.errors.push(mapper(issue2));
|
|
3893
3893
|
continue;
|
|
@@ -3919,8 +3919,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
3919
3919
|
}
|
|
3920
3920
|
function toDotPath(_path) {
|
|
3921
3921
|
const segs = [];
|
|
3922
|
-
const
|
|
3923
|
-
for (const seg of
|
|
3922
|
+
const path92 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
3923
|
+
for (const seg of path92) {
|
|
3924
3924
|
if (typeof seg === "number")
|
|
3925
3925
|
segs.push(`[${seg}]`);
|
|
3926
3926
|
else if (typeof seg === "symbol")
|
|
@@ -17423,13 +17423,13 @@ function resolveRef(ref, ctx) {
|
|
|
17423
17423
|
if (!ref.startsWith("#")) {
|
|
17424
17424
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
17425
17425
|
}
|
|
17426
|
-
const
|
|
17427
|
-
if (
|
|
17426
|
+
const path92 = ref.slice(1).split("/").filter(Boolean);
|
|
17427
|
+
if (path92.length === 0) {
|
|
17428
17428
|
return ctx.rootSchema;
|
|
17429
17429
|
}
|
|
17430
17430
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
17431
|
-
if (
|
|
17432
|
-
const key =
|
|
17431
|
+
if (path92[0] === defsKey) {
|
|
17432
|
+
const key = path92[1];
|
|
17433
17433
|
if (!key || !ctx.defs[key]) {
|
|
17434
17434
|
throw new Error(`Reference not found: ${ref}`);
|
|
17435
17435
|
}
|
|
@@ -19887,11 +19887,11 @@ var init_tools = __esm({
|
|
|
19887
19887
|
if (!ctx.addDocument)
|
|
19888
19888
|
return "Knowledge vault tool not available.";
|
|
19889
19889
|
const title = args["title"] || "New Document";
|
|
19890
|
-
const
|
|
19890
|
+
const path92 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
19891
19891
|
const content = args["content"] || "";
|
|
19892
19892
|
const tags = args["tags"] || [];
|
|
19893
19893
|
ctx.addDocument({
|
|
19894
|
-
path:
|
|
19894
|
+
path: path92,
|
|
19895
19895
|
title,
|
|
19896
19896
|
content,
|
|
19897
19897
|
format: "markdown",
|
|
@@ -19900,7 +19900,7 @@ var init_tools = __esm({
|
|
|
19900
19900
|
workspaceId: ctx.workspaceId
|
|
19901
19901
|
});
|
|
19902
19902
|
ctx.addActivity("vault", "created document", title);
|
|
19903
|
-
return `Document "${title}" created at "${
|
|
19903
|
+
return `Document "${title}" created at "${path92}".`;
|
|
19904
19904
|
}
|
|
19905
19905
|
}
|
|
19906
19906
|
];
|
|
@@ -26246,11 +26246,11 @@ var init_synthesisAudit = __esm({
|
|
|
26246
26246
|
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
26247
26247
|
import { join as join4 } from "node:path";
|
|
26248
26248
|
function loadNfrSpec(zelariRoot) {
|
|
26249
|
-
const
|
|
26250
|
-
if (!existsSync7(
|
|
26249
|
+
const path92 = join4(zelariRoot, "nfr-spec.json");
|
|
26250
|
+
if (!existsSync7(path92))
|
|
26251
26251
|
return null;
|
|
26252
26252
|
try {
|
|
26253
|
-
const raw = JSON.parse(readFileSync7(
|
|
26253
|
+
const raw = JSON.parse(readFileSync7(path92, "utf8"));
|
|
26254
26254
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
26255
26255
|
return null;
|
|
26256
26256
|
return raw;
|
|
@@ -28556,9 +28556,9 @@ var init_types5 = __esm({
|
|
|
28556
28556
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
28557
28557
|
import { join as join10 } from "node:path";
|
|
28558
28558
|
function readLessonsDeduped(zelariRoot) {
|
|
28559
|
-
const
|
|
28559
|
+
const path92 = join10(zelariRoot, LESSONS_FILE);
|
|
28560
28560
|
try {
|
|
28561
|
-
const raw = readFileSync12(
|
|
28561
|
+
const raw = readFileSync12(path92, "utf8");
|
|
28562
28562
|
const byId = /* @__PURE__ */ new Map();
|
|
28563
28563
|
for (const line of raw.split(/\r?\n/)) {
|
|
28564
28564
|
if (!line.trim())
|
|
@@ -28659,8 +28659,8 @@ function keywordsFrom(check2, signature) {
|
|
|
28659
28659
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
28660
28660
|
}
|
|
28661
28661
|
function writeLesson(zelariRoot, lesson) {
|
|
28662
|
-
const
|
|
28663
|
-
appendFileSync(
|
|
28662
|
+
const path92 = join11(zelariRoot, LESSONS_FILE);
|
|
28663
|
+
appendFileSync(path92, `${JSON.stringify(lesson)}
|
|
28664
28664
|
`, "utf8");
|
|
28665
28665
|
}
|
|
28666
28666
|
function findSimilar(lessons, signature) {
|
|
@@ -30624,9 +30624,9 @@ function findCycle(nodes) {
|
|
|
30624
30624
|
if (color.get(start) !== WHITE)
|
|
30625
30625
|
continue;
|
|
30626
30626
|
const stack = [[start, 0]];
|
|
30627
|
-
const
|
|
30627
|
+
const path92 = [];
|
|
30628
30628
|
color.set(start, GRAY);
|
|
30629
|
-
|
|
30629
|
+
path92.push(start);
|
|
30630
30630
|
while (stack.length > 0) {
|
|
30631
30631
|
const top = stack[stack.length - 1];
|
|
30632
30632
|
const [id3, idx] = top;
|
|
@@ -30639,17 +30639,17 @@ function findCycle(nodes) {
|
|
|
30639
30639
|
continue;
|
|
30640
30640
|
const c = color.get(dep);
|
|
30641
30641
|
if (c === GRAY) {
|
|
30642
|
-
const at =
|
|
30643
|
-
return [...
|
|
30642
|
+
const at = path92.indexOf(dep);
|
|
30643
|
+
return [...path92.slice(at), dep];
|
|
30644
30644
|
}
|
|
30645
30645
|
if (c === WHITE) {
|
|
30646
30646
|
color.set(dep, GRAY);
|
|
30647
|
-
|
|
30647
|
+
path92.push(dep);
|
|
30648
30648
|
stack.push([dep, 0]);
|
|
30649
30649
|
}
|
|
30650
30650
|
} else {
|
|
30651
30651
|
color.set(id3, BLACK);
|
|
30652
|
-
|
|
30652
|
+
path92.pop();
|
|
30653
30653
|
stack.pop();
|
|
30654
30654
|
}
|
|
30655
30655
|
}
|
|
@@ -31569,8 +31569,8 @@ var init_runner = __esm({
|
|
|
31569
31569
|
failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
|
|
31570
31570
|
pending: []
|
|
31571
31571
|
};
|
|
31572
|
-
const
|
|
31573
|
-
this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${
|
|
31572
|
+
const path92 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
|
|
31573
|
+
this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path92}`);
|
|
31574
31574
|
return snapshot;
|
|
31575
31575
|
}
|
|
31576
31576
|
callLog(msg, data) {
|
|
@@ -32206,7 +32206,40 @@ var init_agentAdapter = __esm({
|
|
|
32206
32206
|
import { promises as fs9 } from "node:fs";
|
|
32207
32207
|
import path14 from "node:path";
|
|
32208
32208
|
import crypto2 from "node:crypto";
|
|
32209
|
-
|
|
32209
|
+
function defaultLockLivenessProbe(pid) {
|
|
32210
|
+
try {
|
|
32211
|
+
process.kill(pid, 0);
|
|
32212
|
+
return true;
|
|
32213
|
+
} catch (err) {
|
|
32214
|
+
return err.code !== "ESRCH";
|
|
32215
|
+
}
|
|
32216
|
+
}
|
|
32217
|
+
function resolveHeartbeatStaleMs(env = process.env) {
|
|
32218
|
+
const raw = env.ZELARI_SPINE_HEARTBEAT_STALE_MS;
|
|
32219
|
+
if (raw) {
|
|
32220
|
+
const parsed = Number.parseInt(raw, 10);
|
|
32221
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
32222
|
+
return parsed;
|
|
32223
|
+
}
|
|
32224
|
+
return DEFAULT_HEARTBEAT_STALE_MS;
|
|
32225
|
+
}
|
|
32226
|
+
function evaluateLockTakeover(lockInfo, opts) {
|
|
32227
|
+
const heartbeatStaleMs = opts.heartbeatStaleMs ?? DEFAULT_HEARTBEAT_STALE_MS;
|
|
32228
|
+
const staleLockMs = opts.staleLockMs ?? DEFAULT_STALE_LOCK_MS;
|
|
32229
|
+
const ts = typeof lockInfo.ts === "number" && Number.isFinite(lockInfo.ts) && lockInfo.ts > 0 ? lockInfo.ts : 0;
|
|
32230
|
+
const pid = typeof lockInfo.pid === "number" && Number.isFinite(lockInfo.pid) && lockInfo.pid > 0 ? lockInfo.pid : void 0;
|
|
32231
|
+
if (pid !== void 0) {
|
|
32232
|
+
if (!opts.probe(pid))
|
|
32233
|
+
return { takeover: true, reason: "pid-dead" };
|
|
32234
|
+
if (ts > 0 && opts.now - ts < heartbeatStaleMs)
|
|
32235
|
+
return { takeover: false, reason: "alive-fresh" };
|
|
32236
|
+
return { takeover: true, reason: "heartbeat-stale" };
|
|
32237
|
+
}
|
|
32238
|
+
if (ts > 0 && opts.now - ts > staleLockMs)
|
|
32239
|
+
return { takeover: true, reason: "stale" };
|
|
32240
|
+
return { takeover: false, reason: "no-pid-locked" };
|
|
32241
|
+
}
|
|
32242
|
+
var SessionLogLockedError, DEFAULT_STALE_LOCK_MS, DEFAULT_HEARTBEAT_STALE_MS, SessionLogWriter;
|
|
32210
32243
|
var init_writer = __esm({
|
|
32211
32244
|
"packages/core/dist/session/writer.js"() {
|
|
32212
32245
|
"use strict";
|
|
@@ -32223,13 +32256,18 @@ var init_writer = __esm({
|
|
|
32223
32256
|
}
|
|
32224
32257
|
};
|
|
32225
32258
|
DEFAULT_STALE_LOCK_MS = 10 * 60 * 1e3;
|
|
32259
|
+
DEFAULT_HEARTBEAT_STALE_MS = 12e4;
|
|
32226
32260
|
SessionLogWriter = class _SessionLogWriter {
|
|
32227
32261
|
sessionId;
|
|
32228
32262
|
eventsPath;
|
|
32229
32263
|
lockPath;
|
|
32230
32264
|
ownership = crypto2.randomUUID();
|
|
32231
32265
|
staleLockMs;
|
|
32266
|
+
heartbeatStaleMs;
|
|
32267
|
+
probe;
|
|
32232
32268
|
now;
|
|
32269
|
+
/** Last heartbeat ts written into the lock (throttle window anchor). */
|
|
32270
|
+
lastHeartbeatAt = 0;
|
|
32233
32271
|
nextSeq;
|
|
32234
32272
|
closed = false;
|
|
32235
32273
|
chain = Promise.resolve();
|
|
@@ -32239,6 +32277,8 @@ var init_writer = __esm({
|
|
|
32239
32277
|
this.lockPath = path14.join(sessionDir, "writer.lock");
|
|
32240
32278
|
this.nextSeq = startSeq;
|
|
32241
32279
|
this.staleLockMs = options.staleLockMs ?? DEFAULT_STALE_LOCK_MS;
|
|
32280
|
+
this.heartbeatStaleMs = options.heartbeatStaleMs ?? resolveHeartbeatStaleMs();
|
|
32281
|
+
this.probe = options.probe ?? defaultLockLivenessProbe;
|
|
32242
32282
|
this.now = options.now ?? Date.now;
|
|
32243
32283
|
}
|
|
32244
32284
|
/** Create the writer and acquire the ownership lock. */
|
|
@@ -32261,6 +32301,7 @@ var init_writer = __esm({
|
|
|
32261
32301
|
const payload = this.lockPayload();
|
|
32262
32302
|
try {
|
|
32263
32303
|
await fs9.writeFile(this.lockPath, payload, { encoding: "utf-8", flag: "wx", mode: 420 });
|
|
32304
|
+
this.lastHeartbeatAt = this.now();
|
|
32264
32305
|
return;
|
|
32265
32306
|
} catch (err) {
|
|
32266
32307
|
if (err.code !== "EEXIST")
|
|
@@ -32268,19 +32309,43 @@ var init_writer = __esm({
|
|
|
32268
32309
|
}
|
|
32269
32310
|
const existing = await fs9.readFile(this.lockPath, "utf-8").catch(() => null);
|
|
32270
32311
|
let owner = "unknown";
|
|
32271
|
-
|
|
32312
|
+
const lockInfo = {};
|
|
32272
32313
|
if (existing) {
|
|
32273
32314
|
try {
|
|
32274
32315
|
const parsed = JSON.parse(existing);
|
|
32275
32316
|
owner = parsed.ownership ?? owner;
|
|
32276
|
-
|
|
32317
|
+
lockInfo.ownership = parsed.ownership;
|
|
32318
|
+
lockInfo.pid = parsed.pid;
|
|
32319
|
+
lockInfo.ts = parsed.ts;
|
|
32277
32320
|
} catch {
|
|
32278
32321
|
}
|
|
32279
32322
|
}
|
|
32280
|
-
const
|
|
32281
|
-
|
|
32323
|
+
const verdict = evaluateLockTakeover(lockInfo, {
|
|
32324
|
+
now: this.now(),
|
|
32325
|
+
probe: this.probe,
|
|
32326
|
+
heartbeatStaleMs: this.heartbeatStaleMs,
|
|
32327
|
+
staleLockMs: this.staleLockMs
|
|
32328
|
+
});
|
|
32329
|
+
if (!verdict.takeover)
|
|
32282
32330
|
throw new SessionLogLockedError(this.eventsPath, owner);
|
|
32283
32331
|
await fs9.writeFile(this.lockPath, payload, { encoding: "utf-8" });
|
|
32332
|
+
this.lastHeartbeatAt = this.now();
|
|
32333
|
+
}
|
|
32334
|
+
/** Min interval between lock-ts refreshes (≤ ~1 rewrite/second). */
|
|
32335
|
+
static HEARTBEAT_MIN_INTERVAL_MS = 1e3;
|
|
32336
|
+
/**
|
|
32337
|
+
* Lock heartbeat: while we own the lock, refresh its ts so concurrent
|
|
32338
|
+
* writers can tell a live owner from a hung one. Best-effort and throttled
|
|
32339
|
+
* to at most ~1 rewrite/second; it rides the append chain so close() always
|
|
32340
|
+
* lands AFTER the last heartbeat (never resurrects a removed lock).
|
|
32341
|
+
*/
|
|
32342
|
+
maybeHeartbeat() {
|
|
32343
|
+
const ts = this.now();
|
|
32344
|
+
if (ts - this.lastHeartbeatAt < _SessionLogWriter.HEARTBEAT_MIN_INTERVAL_MS)
|
|
32345
|
+
return;
|
|
32346
|
+
this.lastHeartbeatAt = ts;
|
|
32347
|
+
void fs9.writeFile(this.lockPath, this.lockPayload(), { encoding: "utf-8" }).catch(() => {
|
|
32348
|
+
});
|
|
32284
32349
|
}
|
|
32285
32350
|
/** Append one event; returns the validated envelope with its assigned seq. */
|
|
32286
32351
|
async append(input) {
|
|
@@ -32297,6 +32362,7 @@ var init_writer = __esm({
|
|
|
32297
32362
|
};
|
|
32298
32363
|
SessionEventEnvelopeSchema.parse(envelope);
|
|
32299
32364
|
this.nextSeq += 1;
|
|
32365
|
+
this.chain = this.chain.then(() => this.maybeHeartbeat());
|
|
32300
32366
|
const line = JSON.stringify(envelope) + "\n";
|
|
32301
32367
|
this.chain = this.chain.then(() => fs9.appendFile(this.eventsPath, line, "utf-8"));
|
|
32302
32368
|
await this.chain;
|
|
@@ -33771,7 +33837,7 @@ async function createExecutionContext(options = {}) {
|
|
|
33771
33837
|
workspaceKind: workspace.kind
|
|
33772
33838
|
}
|
|
33773
33839
|
});
|
|
33774
|
-
const
|
|
33840
|
+
const fs46 = options.fs ?? new NodeFsProvider(workspace);
|
|
33775
33841
|
const shell = options.shell ?? new NodeShellProvider(workspace);
|
|
33776
33842
|
const subagent = options.subagent ?? NOOP_SUBAGENT_PROVIDER;
|
|
33777
33843
|
const env = options.env ?? process.env;
|
|
@@ -33779,7 +33845,7 @@ async function createExecutionContext(options = {}) {
|
|
|
33779
33845
|
sessionId: sessionId2,
|
|
33780
33846
|
profile,
|
|
33781
33847
|
workspace,
|
|
33782
|
-
fs:
|
|
33848
|
+
fs: fs46,
|
|
33783
33849
|
shell,
|
|
33784
33850
|
subagent,
|
|
33785
33851
|
appendSessionEvent: (input) => writer.append(input),
|
|
@@ -34030,16 +34096,16 @@ function runRetentionFromEnv() {
|
|
|
34030
34096
|
maxTotalBytes: Number.isFinite(parseMb) && parseMb > 0 ? Math.round(parseMb * 1024 * 1024) : DEFAULT_RUN_RETENTION_MAX_MB * 1024 * 1024
|
|
34031
34097
|
};
|
|
34032
34098
|
}
|
|
34033
|
-
async function dirSize(
|
|
34099
|
+
async function dirSize(path92) {
|
|
34034
34100
|
let total = 0;
|
|
34035
34101
|
let entries;
|
|
34036
34102
|
try {
|
|
34037
|
-
entries = await readdir(
|
|
34103
|
+
entries = await readdir(path92, { withFileTypes: true });
|
|
34038
34104
|
} catch {
|
|
34039
34105
|
return 0;
|
|
34040
34106
|
}
|
|
34041
34107
|
for (const entry of entries) {
|
|
34042
|
-
const child = join13(
|
|
34108
|
+
const child = join13(path92, entry.name);
|
|
34043
34109
|
if (entry.isDirectory())
|
|
34044
34110
|
total += await dirSize(child);
|
|
34045
34111
|
else {
|
|
@@ -34066,19 +34132,19 @@ async function enforceRunRetention(runsDir, options = {}) {
|
|
|
34066
34132
|
for (const entry of entries) {
|
|
34067
34133
|
if (!entry.isDirectory())
|
|
34068
34134
|
continue;
|
|
34069
|
-
const
|
|
34135
|
+
const path92 = join13(runsDir, entry.name);
|
|
34070
34136
|
let startedAt = 0;
|
|
34071
34137
|
let endedAt;
|
|
34072
34138
|
let completed = false;
|
|
34073
34139
|
try {
|
|
34074
|
-
const manifest = JSON.parse(await readFile(join13(
|
|
34140
|
+
const manifest = JSON.parse(await readFile(join13(path92, "manifest.json"), "utf8"));
|
|
34075
34141
|
startedAt = manifest.startedAt ?? 0;
|
|
34076
34142
|
endedAt = manifest.endedAt;
|
|
34077
34143
|
completed = Boolean(endedAt) && manifest.status !== "running";
|
|
34078
34144
|
} catch {
|
|
34079
34145
|
completed = false;
|
|
34080
34146
|
}
|
|
34081
|
-
infos.push({ name: entry.name, path:
|
|
34147
|
+
infos.push({ name: entry.name, path: path92, startedAt, endedAt, completed, bytes: await dirSize(path92) });
|
|
34082
34148
|
}
|
|
34083
34149
|
const remove = async (info) => {
|
|
34084
34150
|
await rm(info.path, { recursive: true, force: true });
|
|
@@ -34850,12 +34916,12 @@ var init_engine = __esm({
|
|
|
34850
34916
|
* content digest) and the returned ref carries the event seq when the
|
|
34851
34917
|
* emitter resolved one.
|
|
34852
34918
|
*/
|
|
34853
|
-
async fsEvidence(observation,
|
|
34919
|
+
async fsEvidence(observation, path92, sha256, content, extra = {}) {
|
|
34854
34920
|
const digest = sha256 && content !== void 0 ? sha256(content) : void 0;
|
|
34855
|
-
const seq = await this.emitEvidence({ observation, path:
|
|
34921
|
+
const seq = await this.emitEvidence({ observation, path: path92, ...extra, ...digest ? { digest } : {} });
|
|
34856
34922
|
return {
|
|
34857
34923
|
tier: "fs-observation",
|
|
34858
|
-
ref:
|
|
34924
|
+
ref: path92,
|
|
34859
34925
|
capturedAt: Date.now(),
|
|
34860
34926
|
...digest ? { digest } : {},
|
|
34861
34927
|
...seq !== void 0 ? { seq } : {}
|
|
@@ -34863,25 +34929,25 @@ var init_engine = __esm({
|
|
|
34863
34929
|
}
|
|
34864
34930
|
async evalFileExists(check2, done) {
|
|
34865
34931
|
const sha256 = this.options.sha256 ?? defaultSha256;
|
|
34866
|
-
const
|
|
34867
|
-
if (!
|
|
34932
|
+
const fs46 = this.services.fs;
|
|
34933
|
+
if (!fs46)
|
|
34868
34934
|
return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
|
|
34869
|
-
const exists = await
|
|
34935
|
+
const exists = await fs46.exists(check2.path);
|
|
34870
34936
|
if (!exists) {
|
|
34871
34937
|
await this.emitEvidence({ observation: "file-exists", path: check2.path, exists: false });
|
|
34872
34938
|
return done({ status: "fail", evidence: [], detail: `file not found: ${check2.path}` });
|
|
34873
34939
|
}
|
|
34874
|
-
const content = await
|
|
34940
|
+
const content = await fs46.readFile(check2.path).catch(() => "");
|
|
34875
34941
|
return done({
|
|
34876
34942
|
status: "pass",
|
|
34877
34943
|
evidence: [await this.fsEvidence("file-exists", check2.path, sha256, content, { exists: true })]
|
|
34878
34944
|
});
|
|
34879
34945
|
}
|
|
34880
34946
|
async evalFileAbsent(check2, done) {
|
|
34881
|
-
const
|
|
34882
|
-
if (!
|
|
34947
|
+
const fs46 = this.services.fs;
|
|
34948
|
+
if (!fs46)
|
|
34883
34949
|
return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
|
|
34884
|
-
const exists = await
|
|
34950
|
+
const exists = await fs46.exists(check2.path);
|
|
34885
34951
|
if (exists) {
|
|
34886
34952
|
await this.emitEvidence({ observation: "file-absent", path: check2.path, exists: true });
|
|
34887
34953
|
return done({ status: "fail", evidence: [], detail: `file still present: ${check2.path}` });
|
|
@@ -34893,12 +34959,12 @@ var init_engine = __esm({
|
|
|
34893
34959
|
}
|
|
34894
34960
|
async evalFileContains(check2, done) {
|
|
34895
34961
|
const sha256 = this.options.sha256 ?? defaultSha256;
|
|
34896
|
-
const
|
|
34897
|
-
if (!
|
|
34962
|
+
const fs46 = this.services.fs;
|
|
34963
|
+
if (!fs46)
|
|
34898
34964
|
return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
|
|
34899
34965
|
let content;
|
|
34900
34966
|
try {
|
|
34901
|
-
content = await
|
|
34967
|
+
content = await fs46.readFile(check2.path);
|
|
34902
34968
|
} catch {
|
|
34903
34969
|
await this.emitEvidence({ observation: "file-contains", path: check2.path, readable: false });
|
|
34904
34970
|
return done({ status: "fail", evidence: [], detail: `file not readable: ${check2.path}` });
|
|
@@ -35762,6 +35828,7 @@ __export(dist_exports, {
|
|
|
35762
35828
|
CriterionSchema: () => CriterionSchema,
|
|
35763
35829
|
CriterionSourceSchema: () => CriterionSourceSchema,
|
|
35764
35830
|
DEFAULT_CONTEXT_POLICY: () => DEFAULT_CONTEXT_POLICY,
|
|
35831
|
+
DEFAULT_HEARTBEAT_STALE_MS: () => DEFAULT_HEARTBEAT_STALE_MS,
|
|
35765
35832
|
DEFAULT_MAX_NODES: () => DEFAULT_MAX_NODES,
|
|
35766
35833
|
DEFAULT_MAX_TENTACLES: () => DEFAULT_MAX_TENTACLES,
|
|
35767
35834
|
DEFAULT_MEMORY_SCORING_WEIGHTS: () => DEFAULT_MEMORY_SCORING_WEIGHTS,
|
|
@@ -35992,6 +36059,7 @@ __export(dist_exports, {
|
|
|
35992
36059
|
createGraph: () => createGraph,
|
|
35993
36060
|
createRecorderObserver: () => createRecorderObserver,
|
|
35994
36061
|
createRoutedRequestSnapshot: () => createRoutedRequestSnapshot,
|
|
36062
|
+
defaultLockLivenessProbe: () => defaultLockLivenessProbe,
|
|
35995
36063
|
defaultPersonaParse: () => defaultPersonaParse,
|
|
35996
36064
|
defaultResourcePolicy: () => defaultResourcePolicy,
|
|
35997
36065
|
deriveInitialContract: () => deriveInitialContract,
|
|
@@ -36009,6 +36077,7 @@ __export(dist_exports, {
|
|
|
36009
36077
|
enforceRunRetention: () => enforceRunRetention,
|
|
36010
36078
|
evaluateBudgetContinuation: () => evaluateBudgetContinuation,
|
|
36011
36079
|
evaluateCompletion: () => evaluateCompletion,
|
|
36080
|
+
evaluateLockTakeover: () => evaluateLockTakeover,
|
|
36012
36081
|
evaluateMissionContinuation: () => evaluateMissionContinuation,
|
|
36013
36082
|
evaluateResourceReserveGate: () => evaluateResourceReserveGate,
|
|
36014
36083
|
executeTool: () => executeTool,
|
|
@@ -36160,6 +36229,7 @@ __export(dist_exports, {
|
|
|
36160
36229
|
replayChairmanTextTools: () => replayChairmanTextTools,
|
|
36161
36230
|
resolveAgentSkills: () => resolveAgentSkills,
|
|
36162
36231
|
resolveCouncilRunMode: () => resolveCouncilRunMode,
|
|
36232
|
+
resolveHeartbeatStaleMs: () => resolveHeartbeatStaleMs,
|
|
36163
36233
|
resolveInterventions: () => resolveInterventions,
|
|
36164
36234
|
resolveJailed: () => resolveJailed,
|
|
36165
36235
|
resolveMaxTentacles: () => resolveMaxTentacles,
|
|
@@ -37360,6 +37430,11 @@ var init_sessionSpine = __esm({
|
|
|
37360
37430
|
} catch (err) {
|
|
37361
37431
|
if (err instanceof SessionLogLockedError) {
|
|
37362
37432
|
mirror.status = "locked";
|
|
37433
|
+
mirror.warnOnce(
|
|
37434
|
+
err,
|
|
37435
|
+
`session lock held by another (possibly dead) owner: ${err.message} \u2014 the turn resumed WITHOUT derived spine context`,
|
|
37436
|
+
"locked"
|
|
37437
|
+
);
|
|
37363
37438
|
} else {
|
|
37364
37439
|
mirror.status = "degraded";
|
|
37365
37440
|
mirror.warnOnce(err);
|
|
@@ -37662,13 +37737,13 @@ var init_sessionSpine = __esm({
|
|
|
37662
37737
|
appendEvent(input) {
|
|
37663
37738
|
return this.append(input);
|
|
37664
37739
|
}
|
|
37665
|
-
warnOnce(err) {
|
|
37740
|
+
warnOnce(err, message, label = "degraded") {
|
|
37666
37741
|
if (this.warned || this.options.quiet) return;
|
|
37667
37742
|
this.warned = true;
|
|
37668
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
37743
|
+
const msg = message ?? (err instanceof Error ? err.message : String(err));
|
|
37669
37744
|
try {
|
|
37670
37745
|
process.stderr.write(
|
|
37671
|
-
`[zelari] session spine
|
|
37746
|
+
`[zelari] session spine ${label} for ${this.sessionId.slice(0, 8)}\u2026: ${msg} (transcript continues on the 1.x log)
|
|
37672
37747
|
`
|
|
37673
37748
|
);
|
|
37674
37749
|
} catch {
|
|
@@ -38029,9 +38104,9 @@ function spillToolOutput(fullText, meta3) {
|
|
|
38029
38104
|
const rnd = randomBytes3(3).toString("hex");
|
|
38030
38105
|
const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
|
|
38031
38106
|
const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
|
|
38032
|
-
const
|
|
38033
|
-
writeFileSync11(
|
|
38034
|
-
return
|
|
38107
|
+
const path92 = join14(dir, file2);
|
|
38108
|
+
writeFileSync11(path92, fullText, "utf8");
|
|
38109
|
+
return path92;
|
|
38035
38110
|
} catch {
|
|
38036
38111
|
return null;
|
|
38037
38112
|
}
|
|
@@ -38077,10 +38152,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
|
|
|
38077
38152
|
${tail2}`;
|
|
38078
38153
|
}
|
|
38079
38154
|
if (doSpill) {
|
|
38080
|
-
const
|
|
38081
|
-
if (
|
|
38155
|
+
const path92 = spillToolOutput(text, { toolName: opts.toolName });
|
|
38156
|
+
if (path92) {
|
|
38082
38157
|
const spillNote = `
|
|
38083
|
-
\u2026 [full output spilled to: ${
|
|
38158
|
+
\u2026 [full output spilled to: ${path92} \u2014 re-read with read_file if you need the complete text] \u2026`;
|
|
38084
38159
|
if (preview.includes("] \u2026\n")) {
|
|
38085
38160
|
preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
|
|
38086
38161
|
`);
|
|
@@ -42052,28 +42127,28 @@ var init_storage = __esm({
|
|
|
42052
42127
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
42053
42128
|
Storage = class {
|
|
42054
42129
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
42055
|
-
read(
|
|
42056
|
-
if (!existsSync19(
|
|
42057
|
-
throw new Error(`File not found: ${
|
|
42130
|
+
read(path92) {
|
|
42131
|
+
if (!existsSync19(path92)) {
|
|
42132
|
+
throw new Error(`File not found: ${path92}`);
|
|
42058
42133
|
}
|
|
42059
|
-
const md = readFileSync16(
|
|
42134
|
+
const md = readFileSync16(path92, "utf8");
|
|
42060
42135
|
return parseFrontmatter(md);
|
|
42061
42136
|
}
|
|
42062
42137
|
/** Read a Markdown file; returns null if not found. */
|
|
42063
|
-
readIfExists(
|
|
42064
|
-
if (!existsSync19(
|
|
42065
|
-
return this.read(
|
|
42138
|
+
readIfExists(path92) {
|
|
42139
|
+
if (!existsSync19(path92)) return null;
|
|
42140
|
+
return this.read(path92);
|
|
42066
42141
|
}
|
|
42067
42142
|
/**
|
|
42068
42143
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
42069
42144
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
42070
42145
|
*/
|
|
42071
|
-
write(
|
|
42072
|
-
mkdirSync10(dirname2(
|
|
42073
|
-
const tmp =
|
|
42146
|
+
write(path92, meta3, body) {
|
|
42147
|
+
mkdirSync10(dirname2(path92), { recursive: true });
|
|
42148
|
+
const tmp = path92 + ".tmp-" + process.pid;
|
|
42074
42149
|
const md = serializeFrontmatter(meta3, body);
|
|
42075
42150
|
writeFileSync13(tmp, md, "utf8");
|
|
42076
|
-
renameSync(tmp,
|
|
42151
|
+
renameSync(tmp, path92);
|
|
42077
42152
|
}
|
|
42078
42153
|
/** List all .md files in a directory (non-recursive). */
|
|
42079
42154
|
listMarkdown(dir) {
|
|
@@ -42135,8 +42210,8 @@ function nextPlanTaskId(store6) {
|
|
|
42135
42210
|
return `t${store6.counter}`;
|
|
42136
42211
|
}
|
|
42137
42212
|
function writePlanTaskArtifact(rootDir, task) {
|
|
42138
|
-
const
|
|
42139
|
-
mkdirSync11(dirname3(
|
|
42213
|
+
const path92 = join18(rootDir, "plan-tasks", `${task.id}.md`);
|
|
42214
|
+
mkdirSync11(dirname3(path92), { recursive: true });
|
|
42140
42215
|
const meta3 = {
|
|
42141
42216
|
kind: "task",
|
|
42142
42217
|
id: task.id,
|
|
@@ -42157,7 +42232,7 @@ function writePlanTaskArtifact(rootDir, task) {
|
|
|
42157
42232
|
task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
|
|
42158
42233
|
""
|
|
42159
42234
|
].filter((l) => l !== null).join("\n");
|
|
42160
|
-
new Storage().write(
|
|
42235
|
+
new Storage().write(path92, meta3, body);
|
|
42161
42236
|
}
|
|
42162
42237
|
function loadHandle(rootDir) {
|
|
42163
42238
|
const jsonPath = join18(rootDir, "plan.json");
|
|
@@ -42863,6 +42938,100 @@ var init_inspectCommand = __esm({
|
|
|
42863
42938
|
}
|
|
42864
42939
|
});
|
|
42865
42940
|
|
|
42941
|
+
// src/cli/safety/selfKillGuard.ts
|
|
42942
|
+
function cleanToken(raw) {
|
|
42943
|
+
return raw.replace(/^["']+|["',]+\s*$/g, "").trim();
|
|
42944
|
+
}
|
|
42945
|
+
function isProtectedImage(name) {
|
|
42946
|
+
return PROTECTED_IMAGE_RE.test(cleanToken(name));
|
|
42947
|
+
}
|
|
42948
|
+
function stripSubstitutions(cmd) {
|
|
42949
|
+
return cmd.replace(/\$\([^)]*\)/g, " ( ) ").replace(/\$\{[^}]*\}/g, " ").replace(/`[^`]*`/g, " ` ` ");
|
|
42950
|
+
}
|
|
42951
|
+
function findImageKill(cmd) {
|
|
42952
|
+
const im = cmd.match(/\/{1,2}im\s+["']?([a-z0-9_.-]+)/i);
|
|
42953
|
+
if (im && /\btaskkill\b/i.test(cmd) && isProtectedImage(im[1])) {
|
|
42954
|
+
return `taskkill /IM ${cleanToken(im[1])}`;
|
|
42955
|
+
}
|
|
42956
|
+
const fi = cmd.match(/imagename\s+eq\s+["']?([a-z0-9_.-]+)/i);
|
|
42957
|
+
if (fi && /\btaskkill\b/i.test(cmd) && isProtectedImage(fi[1])) {
|
|
42958
|
+
return `taskkill /FI "IMAGENAME eq ${cleanToken(fi[1])}"`;
|
|
42959
|
+
}
|
|
42960
|
+
const nm = cmd.match(/\b(?:stop-process|kill)\b[^|;&]*-name\s+["']?([a-z0-9_.-]+)/i);
|
|
42961
|
+
if (nm && isProtectedImage(nm[1])) {
|
|
42962
|
+
return `Stop-Process -Name ${cleanToken(nm[1])}`;
|
|
42963
|
+
}
|
|
42964
|
+
if (/\bget-process\b[^|;&]*\bnode(?:\.exe)?\b/i.test(cmd) && /\|\s*[^|]*?(?:stop-process|\.\s*kill\s*\(|\bkill\b)/i.test(cmd)) {
|
|
42965
|
+
return "Get-Process node | Stop-Process";
|
|
42966
|
+
}
|
|
42967
|
+
if (/\bpkill\b[^|;&]*\bnode(?:\.exe)?\b/i.test(cmd)) return "pkill node";
|
|
42968
|
+
if (/\bkillall\b[^|;&]*\bnode(?:\.exe)?\b/i.test(cmd)) return "killall node";
|
|
42969
|
+
if (/\bwmic\b[^|;&]*name\s*=\s*["']?node(?:\.exe)?["']?/i.test(cmd) && /\b(?:delete|terminate)\b/i.test(cmd)) {
|
|
42970
|
+
return "wmic process delete node.exe";
|
|
42971
|
+
}
|
|
42972
|
+
return null;
|
|
42973
|
+
}
|
|
42974
|
+
function findPsPipelineKill(cmd) {
|
|
42975
|
+
if (!/\bps\b[^|;&]*\|\s*(?:grep|findstr|awk|sed)[^|;&]*\bnode(?:\.exe)?\b/i.test(cmd)) {
|
|
42976
|
+
return null;
|
|
42977
|
+
}
|
|
42978
|
+
if (!/\b(?:xargs\s+)?(?:kill|taskkill|pkill|killall|stop-process)\b/i.test(cmd)) {
|
|
42979
|
+
return null;
|
|
42980
|
+
}
|
|
42981
|
+
return "ps | grep node | kill";
|
|
42982
|
+
}
|
|
42983
|
+
function findProtectedPidKill(cmd, ctx) {
|
|
42984
|
+
const protectedPids = /* @__PURE__ */ new Set([
|
|
42985
|
+
ctx.selfPid,
|
|
42986
|
+
...ctx.parentPid !== void 0 ? [ctx.parentPid] : [],
|
|
42987
|
+
...ctx.extraProtectedPids ?? []
|
|
42988
|
+
]);
|
|
42989
|
+
const hit = (pid, shape) => protectedPids.has(pid) ? `${shape} ${pid}` : null;
|
|
42990
|
+
const stripped = stripSubstitutions(cmd);
|
|
42991
|
+
for (const m of stripped.matchAll(/\/{1,2}pid(?:list)?\s+([\d,\s]+)/gi)) {
|
|
42992
|
+
for (const tok of m[1].split(/[\s,]+/)) {
|
|
42993
|
+
const pid = Number.parseInt(tok, 10);
|
|
42994
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
42995
|
+
const h = hit(pid, "taskkill /PID");
|
|
42996
|
+
if (h) return h;
|
|
42997
|
+
}
|
|
42998
|
+
}
|
|
42999
|
+
}
|
|
43000
|
+
for (const m of stripped.matchAll(/\b(?:stop-process|kill)\b[^|;&]*?-(?:id|pid)\s+(\d+)/gi)) {
|
|
43001
|
+
const h = hit(Number.parseInt(m[1], 10), "Stop-Process -Id");
|
|
43002
|
+
if (h) return h;
|
|
43003
|
+
}
|
|
43004
|
+
for (const m of stripped.matchAll(/\bkill\b([^|;&]*)/gi)) {
|
|
43005
|
+
for (const tok of m[1].split(/\s+/)) {
|
|
43006
|
+
if (!/^\d+$/.test(tok)) continue;
|
|
43007
|
+
const h = hit(Number.parseInt(tok, 10), "kill");
|
|
43008
|
+
if (h) return h;
|
|
43009
|
+
}
|
|
43010
|
+
}
|
|
43011
|
+
return null;
|
|
43012
|
+
}
|
|
43013
|
+
function blockedVerdict(reason, match) {
|
|
43014
|
+
return { blocked: true, reason, match, message: SELF_KILL_DENIAL_MESSAGE };
|
|
43015
|
+
}
|
|
43016
|
+
function inspectCommand(command, ctx) {
|
|
43017
|
+
if (typeof command !== "string" || command.length === 0) return { blocked: false };
|
|
43018
|
+
const imageMatch = findImageKill(command);
|
|
43019
|
+
if (imageMatch) return blockedVerdict("node-image", imageMatch);
|
|
43020
|
+
const pipelineMatch = findPsPipelineKill(command);
|
|
43021
|
+
if (pipelineMatch) return blockedVerdict("ps-pipeline", pipelineMatch);
|
|
43022
|
+
const pidMatch = findProtectedPidKill(command, ctx);
|
|
43023
|
+
if (pidMatch) return blockedVerdict("protected-pid", pidMatch);
|
|
43024
|
+
return { blocked: false };
|
|
43025
|
+
}
|
|
43026
|
+
var SELF_KILL_DENIAL_MESSAGE, PROTECTED_IMAGE_RE;
|
|
43027
|
+
var init_selfKillGuard = __esm({
|
|
43028
|
+
"src/cli/safety/selfKillGuard.ts"() {
|
|
43029
|
+
"use strict";
|
|
43030
|
+
SELF_KILL_DENIAL_MESSAGE = "Blocked: this command would kill the agent host process itself (node image / own process tree). To stop a dev server, kill by PORT instead: run `netstat -ano | findstr :<port>` to find the listener PID, then `taskkill //PID <listener-pid>` \u2014 never kill node by image name.";
|
|
43031
|
+
PROTECTED_IMAGE_RE = /^(?:node(?:\.exe)?|zelari(?:-code)?(?:\.exe)?)$/i;
|
|
43032
|
+
}
|
|
43033
|
+
});
|
|
43034
|
+
|
|
42866
43035
|
// src/cli/safety/policyLoadMode.ts
|
|
42867
43036
|
var policyLoadMode_exports = {};
|
|
42868
43037
|
__export(policyLoadMode_exports, {
|
|
@@ -43252,6 +43421,13 @@ function createExecProcessTool(root, opts = {}) {
|
|
|
43252
43421
|
if (blocked) {
|
|
43253
43422
|
return typedErr(`[shell-blocked] exec_process refused (${blocked.reason}): ${blocked.pattern}`);
|
|
43254
43423
|
}
|
|
43424
|
+
const selfKill = inspectCommand([input.program, ...argv].join(" "), {
|
|
43425
|
+
selfPid: process.pid,
|
|
43426
|
+
parentPid: process.ppid
|
|
43427
|
+
});
|
|
43428
|
+
if (selfKill.blocked && selfKill.message) {
|
|
43429
|
+
return typedErr(`[self-kill] exec_process refused: ${selfKill.message}`);
|
|
43430
|
+
}
|
|
43255
43431
|
let cwd;
|
|
43256
43432
|
try {
|
|
43257
43433
|
cwd = resolveSandboxedPath(input.cwd ?? ".", { root });
|
|
@@ -43326,6 +43502,7 @@ var init_execProcess = __esm({
|
|
|
43326
43502
|
init_zod();
|
|
43327
43503
|
init_toolTypes();
|
|
43328
43504
|
init_shellBlocklist();
|
|
43505
|
+
init_selfKillGuard();
|
|
43329
43506
|
init_sandboxPath();
|
|
43330
43507
|
init_osJail();
|
|
43331
43508
|
execProcessInputSchema = external_exports.object({
|
|
@@ -45831,21 +46008,21 @@ function normalizeAuth(auth) {
|
|
|
45831
46008
|
return "agent";
|
|
45832
46009
|
}
|
|
45833
46010
|
function readSecrets() {
|
|
45834
|
-
const
|
|
45835
|
-
if (!existsSync25(
|
|
46011
|
+
const path92 = getSshSecretsPath();
|
|
46012
|
+
if (!existsSync25(path92)) return {};
|
|
45836
46013
|
try {
|
|
45837
|
-
return JSON.parse(readFileSync20(
|
|
46014
|
+
return JSON.parse(readFileSync20(path92, "utf8"));
|
|
45838
46015
|
} catch {
|
|
45839
46016
|
return {};
|
|
45840
46017
|
}
|
|
45841
46018
|
}
|
|
45842
46019
|
function writeSecrets(data) {
|
|
45843
|
-
const
|
|
45844
|
-
mkdirSync12(dirname4(
|
|
45845
|
-
writeFileSync15(
|
|
46020
|
+
const path92 = getSshSecretsPath();
|
|
46021
|
+
mkdirSync12(dirname4(path92), { recursive: true });
|
|
46022
|
+
writeFileSync15(path92, `${JSON.stringify(data, null, 2)}
|
|
45846
46023
|
`, "utf8");
|
|
45847
46024
|
try {
|
|
45848
|
-
chmodSync(
|
|
46025
|
+
chmodSync(path92, 384);
|
|
45849
46026
|
} catch {
|
|
45850
46027
|
}
|
|
45851
46028
|
}
|
|
@@ -45874,10 +46051,10 @@ function deleteSshPassword(id3) {
|
|
|
45874
46051
|
writeSecrets({ passwords });
|
|
45875
46052
|
}
|
|
45876
46053
|
function readStore2() {
|
|
45877
|
-
const
|
|
45878
|
-
if (!existsSync25(
|
|
46054
|
+
const path92 = getSshTargetsPath();
|
|
46055
|
+
if (!existsSync25(path92)) return [];
|
|
45879
46056
|
try {
|
|
45880
|
-
const parsed = JSON.parse(readFileSync20(
|
|
46057
|
+
const parsed = JSON.parse(readFileSync20(path92, "utf8"));
|
|
45881
46058
|
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
45882
46059
|
return list.filter(
|
|
45883
46060
|
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
@@ -45892,11 +46069,11 @@ function readStore2() {
|
|
|
45892
46069
|
}
|
|
45893
46070
|
}
|
|
45894
46071
|
function writeStore2(targets) {
|
|
45895
|
-
const
|
|
45896
|
-
mkdirSync12(dirname4(
|
|
46072
|
+
const path92 = getSshTargetsPath();
|
|
46073
|
+
mkdirSync12(dirname4(path92), { recursive: true });
|
|
45897
46074
|
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
45898
46075
|
writeFileSync15(
|
|
45899
|
-
|
|
46076
|
+
path92,
|
|
45900
46077
|
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
45901
46078
|
`,
|
|
45902
46079
|
"utf8"
|
|
@@ -46142,11 +46319,11 @@ function formatSshTargetsForPrompt() {
|
|
|
46142
46319
|
];
|
|
46143
46320
|
for (const t of targets) {
|
|
46144
46321
|
const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
|
|
46145
|
-
const
|
|
46322
|
+
const path92 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
|
|
46146
46323
|
const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
|
|
46147
46324
|
const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
|
|
46148
46325
|
lines.push(
|
|
46149
|
-
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${
|
|
46326
|
+
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path92}${tags}${allow}`
|
|
46150
46327
|
);
|
|
46151
46328
|
}
|
|
46152
46329
|
return lines.join("\n");
|
|
@@ -49201,6 +49378,23 @@ function wrapWithShellSafety(original, audit, sessionId2, root, jail) {
|
|
|
49201
49378
|
}
|
|
49202
49379
|
throw err;
|
|
49203
49380
|
}
|
|
49381
|
+
const selfKill = inspectCommand(cmd, { selfPid: process.pid, parentPid: process.ppid });
|
|
49382
|
+
if (selfKill.blocked && selfKill.message) {
|
|
49383
|
+
await audit.append({
|
|
49384
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49385
|
+
sessionId: sessionId2,
|
|
49386
|
+
tool: original.name,
|
|
49387
|
+
args: redactForAudit(args),
|
|
49388
|
+
ok: false,
|
|
49389
|
+
resultSummary: selfKill.match ?? "self_kill",
|
|
49390
|
+
durationMs: 0,
|
|
49391
|
+
error: "self_kill_blocked"
|
|
49392
|
+
});
|
|
49393
|
+
return {
|
|
49394
|
+
ok: false,
|
|
49395
|
+
error: `[self-kill] ${selfKill.message}`
|
|
49396
|
+
};
|
|
49397
|
+
}
|
|
49204
49398
|
}
|
|
49205
49399
|
const rawCwd = args["cwd"];
|
|
49206
49400
|
try {
|
|
@@ -49327,6 +49521,7 @@ var init_toolRegistry = __esm({
|
|
|
49327
49521
|
init_resourceClaims();
|
|
49328
49522
|
init_osJail();
|
|
49329
49523
|
init_toolResultCache();
|
|
49524
|
+
init_selfKillGuard();
|
|
49330
49525
|
init_toolTypes();
|
|
49331
49526
|
init_skills2();
|
|
49332
49527
|
HARNESS_BUILTIN_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -54634,8 +54829,8 @@ function readPlan(ctx) {
|
|
|
54634
54829
|
} catch {
|
|
54635
54830
|
}
|
|
54636
54831
|
}
|
|
54637
|
-
const
|
|
54638
|
-
const doc = ctx.storage.readIfExists(
|
|
54832
|
+
const path92 = workspaceFile(ctx.rootDir, "plan");
|
|
54833
|
+
const doc = ctx.storage.readIfExists(path92);
|
|
54639
54834
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
54640
54835
|
const meta3 = doc.meta;
|
|
54641
54836
|
return {
|
|
@@ -54813,7 +55008,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
54813
55008
|
dueDate: input.dueDate,
|
|
54814
55009
|
targetVersion: version2
|
|
54815
55010
|
});
|
|
54816
|
-
const
|
|
55011
|
+
const path92 = join31(ctx.rootDir, "milestones", `${id3}.md`);
|
|
54817
55012
|
const meta3 = {
|
|
54818
55013
|
kind: "milestone",
|
|
54819
55014
|
id: id3,
|
|
@@ -54830,7 +55025,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
54830
55025
|
`Target version: ${version2}`,
|
|
54831
55026
|
""
|
|
54832
55027
|
].join("\n");
|
|
54833
|
-
ctx.storage.write(
|
|
55028
|
+
ctx.storage.write(path92, meta3, body);
|
|
54834
55029
|
return { id: id3, created: true };
|
|
54835
55030
|
}
|
|
54836
55031
|
function readPlanSummary(ctx) {
|
|
@@ -55034,7 +55229,7 @@ function addIdeaStub(ctx) {
|
|
|
55034
55229
|
const tags = args["tags"] ?? [];
|
|
55035
55230
|
const category = args["category"] ?? "General";
|
|
55036
55231
|
const id3 = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
55037
|
-
const
|
|
55232
|
+
const path92 = workspaceArtifact(ctx.rootDir, "decisions", id3);
|
|
55038
55233
|
const meta3 = {
|
|
55039
55234
|
kind: "adr",
|
|
55040
55235
|
status: "proposed",
|
|
@@ -55060,7 +55255,7 @@ function addIdeaStub(ctx) {
|
|
|
55060
55255
|
...consequences.map((c) => `- ${c}`),
|
|
55061
55256
|
""
|
|
55062
55257
|
].join("\n");
|
|
55063
|
-
ctx.storage.write(
|
|
55258
|
+
ctx.storage.write(path92, meta3, body);
|
|
55064
55259
|
return `ADR ${id3} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
55065
55260
|
});
|
|
55066
55261
|
}
|
|
@@ -55142,14 +55337,14 @@ function createDocumentStub(ctx) {
|
|
|
55142
55337
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
55143
55338
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
55144
55339
|
}
|
|
55145
|
-
const
|
|
55340
|
+
const path92 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
55146
55341
|
const meta3 = {
|
|
55147
55342
|
kind: "doc",
|
|
55148
55343
|
id: slug,
|
|
55149
55344
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
55150
55345
|
tags
|
|
55151
55346
|
};
|
|
55152
|
-
ctx.storage.write(
|
|
55347
|
+
ctx.storage.write(path92, meta3, content);
|
|
55153
55348
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
55154
55349
|
});
|
|
55155
55350
|
}
|
|
@@ -55775,10 +55970,10 @@ function getUserMcpPath() {
|
|
|
55775
55970
|
function getProjectMcpPath(projectRoot) {
|
|
55776
55971
|
return join32(projectRoot, ".zelari", "mcp.json");
|
|
55777
55972
|
}
|
|
55778
|
-
function readFile6(
|
|
55779
|
-
if (!existsSync37(
|
|
55973
|
+
function readFile6(path92) {
|
|
55974
|
+
if (!existsSync37(path92)) return {};
|
|
55780
55975
|
try {
|
|
55781
|
-
const parsed = JSON.parse(readFileSync29(
|
|
55976
|
+
const parsed = JSON.parse(readFileSync29(path92, "utf8"));
|
|
55782
55977
|
const out = {};
|
|
55783
55978
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
55784
55979
|
const hasCommand = !!cfg && typeof cfg.command === "string" && !!cfg.command.trim();
|
|
@@ -55800,10 +55995,10 @@ function readFile6(path91) {
|
|
|
55800
55995
|
return {};
|
|
55801
55996
|
}
|
|
55802
55997
|
}
|
|
55803
|
-
function writeFile2(
|
|
55804
|
-
mkdirSync16(dirname9(
|
|
55998
|
+
function writeFile2(path92, servers) {
|
|
55999
|
+
mkdirSync16(dirname9(path92), { recursive: true });
|
|
55805
56000
|
const body = { mcpServers: servers };
|
|
55806
|
-
writeFileSync18(
|
|
56001
|
+
writeFileSync18(path92, `${JSON.stringify(body, null, 2)}
|
|
55807
56002
|
`, "utf8");
|
|
55808
56003
|
}
|
|
55809
56004
|
function listMcpServers(projectRoot) {
|
|
@@ -55841,9 +56036,9 @@ function upsertMcpServer(opts) {
|
|
|
55841
56036
|
error: "either command (stdio) or url (http) is required"
|
|
55842
56037
|
};
|
|
55843
56038
|
}
|
|
55844
|
-
let
|
|
56039
|
+
let path92;
|
|
55845
56040
|
if (opts.scope === "user") {
|
|
55846
|
-
|
|
56041
|
+
path92 = getUserMcpPath();
|
|
55847
56042
|
} else {
|
|
55848
56043
|
const root = opts.projectRoot?.trim();
|
|
55849
56044
|
if (!root) {
|
|
@@ -55852,9 +56047,9 @@ function upsertMcpServer(opts) {
|
|
|
55852
56047
|
error: "projectRoot required for project scope (Open Folder first)"
|
|
55853
56048
|
};
|
|
55854
56049
|
}
|
|
55855
|
-
|
|
56050
|
+
path92 = getProjectMcpPath(root);
|
|
55856
56051
|
}
|
|
55857
|
-
const current = readFile6(
|
|
56052
|
+
const current = readFile6(path92);
|
|
55858
56053
|
current[name] = {
|
|
55859
56054
|
command: hasCommand ? opts.config.command.trim() : void 0,
|
|
55860
56055
|
args: opts.config.args,
|
|
@@ -55865,21 +56060,21 @@ function upsertMcpServer(opts) {
|
|
|
55865
56060
|
serial: opts.config.serial,
|
|
55866
56061
|
enabled: opts.config.enabled !== false
|
|
55867
56062
|
};
|
|
55868
|
-
writeFile2(
|
|
55869
|
-
return { ok: true, path:
|
|
56063
|
+
writeFile2(path92, current);
|
|
56064
|
+
return { ok: true, path: path92 };
|
|
55870
56065
|
}
|
|
55871
56066
|
function removeMcpServer(opts) {
|
|
55872
|
-
const
|
|
55873
|
-
if (!
|
|
56067
|
+
const path92 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
|
|
56068
|
+
if (!path92) {
|
|
55874
56069
|
return { ok: false, error: "projectRoot required for project scope" };
|
|
55875
56070
|
}
|
|
55876
|
-
const current = readFile6(
|
|
56071
|
+
const current = readFile6(path92);
|
|
55877
56072
|
if (!(opts.name in current)) {
|
|
55878
|
-
return { ok: false, error: `Server "${opts.name}" not found in ${
|
|
56073
|
+
return { ok: false, error: `Server "${opts.name}" not found in ${path92}` };
|
|
55879
56074
|
}
|
|
55880
56075
|
delete current[opts.name];
|
|
55881
|
-
writeFile2(
|
|
55882
|
-
return { ok: true, path:
|
|
56076
|
+
writeFile2(path92, current);
|
|
56077
|
+
return { ok: true, path: path92 };
|
|
55883
56078
|
}
|
|
55884
56079
|
var init_mcpConfigIo = __esm({
|
|
55885
56080
|
"src/cli/mcp/mcpConfigIo.ts"() {
|
|
@@ -56348,10 +56543,10 @@ import { createHash as createHash20 } from "node:crypto";
|
|
|
56348
56543
|
import { join as join34 } from "node:path";
|
|
56349
56544
|
import { readFile as readFile7 } from "node:fs/promises";
|
|
56350
56545
|
async function readPackageJson3(projectRoot) {
|
|
56351
|
-
const
|
|
56352
|
-
if (!existsSync39(
|
|
56546
|
+
const path92 = join34(projectRoot, "package.json");
|
|
56547
|
+
if (!existsSync39(path92)) return null;
|
|
56353
56548
|
try {
|
|
56354
|
-
return JSON.parse(await readFile7(
|
|
56549
|
+
return JSON.parse(await readFile7(path92, "utf8"));
|
|
56355
56550
|
} catch {
|
|
56356
56551
|
return null;
|
|
56357
56552
|
}
|
|
@@ -56433,9 +56628,9 @@ async function genBuild(ctx) {
|
|
|
56433
56628
|
].join("\n");
|
|
56434
56629
|
}
|
|
56435
56630
|
async function genOpenQuestions(ctx) {
|
|
56436
|
-
const
|
|
56437
|
-
if (!existsSync39(
|
|
56438
|
-
const content = readFileSync31(
|
|
56631
|
+
const path92 = join34(ctx.rootDir, "risks.md");
|
|
56632
|
+
if (!existsSync39(path92)) return "_No open questions._";
|
|
56633
|
+
const content = readFileSync31(path92, "utf8");
|
|
56439
56634
|
const lines = content.split("\n");
|
|
56440
56635
|
const questions = [];
|
|
56441
56636
|
let currentTitle = "";
|
|
@@ -56709,9 +56904,9 @@ function versionKey(value) {
|
|
|
56709
56904
|
function firstString2(v) {
|
|
56710
56905
|
return typeof v === "string" && v.trim().length > 0 ? v : null;
|
|
56711
56906
|
}
|
|
56712
|
-
function readFileSyncSafe(
|
|
56907
|
+
function readFileSyncSafe(path92) {
|
|
56713
56908
|
try {
|
|
56714
|
-
return readFileSync32(
|
|
56909
|
+
return readFileSync32(path92, "utf8");
|
|
56715
56910
|
} catch {
|
|
56716
56911
|
return null;
|
|
56717
56912
|
}
|
|
@@ -57174,8 +57369,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
57174
57369
|
sources: scope.sources
|
|
57175
57370
|
} : void 0
|
|
57176
57371
|
});
|
|
57177
|
-
const
|
|
57178
|
-
completionHook = { ran: true, path:
|
|
57372
|
+
const path92 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
57373
|
+
completionHook = { ran: true, path: path92, completion };
|
|
57179
57374
|
} catch (err) {
|
|
57180
57375
|
completionHook = {
|
|
57181
57376
|
ran: true,
|
|
@@ -58511,7 +58706,7 @@ function safeSocketPath(socketPath) {
|
|
|
58511
58706
|
return socketPath.trim();
|
|
58512
58707
|
}
|
|
58513
58708
|
function startPermissionBroker(socketPath, handlers, opts) {
|
|
58514
|
-
const
|
|
58709
|
+
const path92 = safeSocketPath(socketPath);
|
|
58515
58710
|
const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
|
|
58516
58711
|
const sockets = /* @__PURE__ */ new Set();
|
|
58517
58712
|
const server = createServer2((socket) => {
|
|
@@ -58611,10 +58806,10 @@ function startPermissionBroker(socketPath, handlers, opts) {
|
|
|
58611
58806
|
return new Promise((resolve9, reject) => {
|
|
58612
58807
|
const onError = (err) => reject(err);
|
|
58613
58808
|
server.once("error", onError);
|
|
58614
|
-
server.listen(
|
|
58809
|
+
server.listen(path92, () => {
|
|
58615
58810
|
server.removeListener("error", onError);
|
|
58616
58811
|
resolve9({
|
|
58617
|
-
socketPath:
|
|
58812
|
+
socketPath: path92,
|
|
58618
58813
|
stop: () => new Promise((res) => {
|
|
58619
58814
|
for (const s of sockets) s.destroy();
|
|
58620
58815
|
sockets.clear();
|
|
@@ -58625,7 +58820,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
|
|
|
58625
58820
|
if (done) return;
|
|
58626
58821
|
done = true;
|
|
58627
58822
|
if (process.platform !== "win32") {
|
|
58628
|
-
unlink(
|
|
58823
|
+
unlink(path92, () => res());
|
|
58629
58824
|
} else {
|
|
58630
58825
|
res();
|
|
58631
58826
|
}
|
|
@@ -58638,11 +58833,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
|
|
|
58638
58833
|
});
|
|
58639
58834
|
}
|
|
58640
58835
|
function requestBrokerAsk(socketPath, ask, opts) {
|
|
58641
|
-
const
|
|
58836
|
+
const path92 = safeSocketPath(socketPath);
|
|
58642
58837
|
const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
|
|
58643
58838
|
const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
|
|
58644
58839
|
return new Promise((resolve9, reject) => {
|
|
58645
|
-
const socket = connect(
|
|
58840
|
+
const socket = connect(path92);
|
|
58646
58841
|
let buffer = "";
|
|
58647
58842
|
let settled = false;
|
|
58648
58843
|
const settle = (fn) => {
|
|
@@ -58657,7 +58852,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
|
|
|
58657
58852
|
settle(
|
|
58658
58853
|
() => reject(
|
|
58659
58854
|
new Error(
|
|
58660
|
-
`permission broker unavailable at "${
|
|
58855
|
+
`permission broker unavailable at "${path92}" (connect timed out after ${connectTimeoutMs}ms)`
|
|
58661
58856
|
)
|
|
58662
58857
|
)
|
|
58663
58858
|
);
|
|
@@ -64475,7 +64670,7 @@ async function loadExtensionsFromDirs(dirs, options = {}) {
|
|
|
64475
64670
|
skipped
|
|
64476
64671
|
};
|
|
64477
64672
|
const fsRoot = options.fsRoot ?? process.cwd();
|
|
64478
|
-
let
|
|
64673
|
+
let fs46 = null;
|
|
64479
64674
|
const pending = [];
|
|
64480
64675
|
for (const dir of dirs) {
|
|
64481
64676
|
const files = candidateFiles(dir.path);
|
|
@@ -64530,9 +64725,9 @@ async function loadExtensionsFromDirs(dirs, options = {}) {
|
|
|
64530
64725
|
skipped.push(`${file2}: missing ZelariExtension export`);
|
|
64531
64726
|
continue;
|
|
64532
64727
|
}
|
|
64533
|
-
if (!
|
|
64728
|
+
if (!fs46) fs46 = bindSandboxedFs(fsRoot);
|
|
64534
64729
|
try {
|
|
64535
|
-
await runtime.registry.registerExtension(ext, { fs:
|
|
64730
|
+
await runtime.registry.registerExtension(ext, { fs: fs46 });
|
|
64536
64731
|
runtime.loaded.push({ id: ext.id, file: file2, scope });
|
|
64537
64732
|
} catch (err) {
|
|
64538
64733
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -67846,7 +68041,7 @@ function upsertSkill(opts) {
|
|
|
67846
68041
|
}
|
|
67847
68042
|
dir = getProjectSkillsDir(root);
|
|
67848
68043
|
}
|
|
67849
|
-
const
|
|
68044
|
+
const path92 = skillFilePath(dir, name);
|
|
67850
68045
|
const content = serializeSkillMd({
|
|
67851
68046
|
name,
|
|
67852
68047
|
description,
|
|
@@ -67855,13 +68050,13 @@ function upsertSkill(opts) {
|
|
|
67855
68050
|
tools: opts.tools,
|
|
67856
68051
|
cost: opts.cost
|
|
67857
68052
|
});
|
|
67858
|
-
const parsed = parseSkillMd(content,
|
|
68053
|
+
const parsed = parseSkillMd(content, path92);
|
|
67859
68054
|
if (!parsed) {
|
|
67860
68055
|
return { ok: false, error: "Generated SKILL.md failed validation" };
|
|
67861
68056
|
}
|
|
67862
|
-
mkdirSync21(dirname13(
|
|
67863
|
-
writeFileSync24(
|
|
67864
|
-
return { ok: true, path:
|
|
68057
|
+
mkdirSync21(dirname13(path92), { recursive: true });
|
|
68058
|
+
writeFileSync24(path92, content, "utf8");
|
|
68059
|
+
return { ok: true, path: path92 };
|
|
67865
68060
|
}
|
|
67866
68061
|
function removeSkill(opts) {
|
|
67867
68062
|
const name = opts.name.trim().toLowerCase();
|
|
@@ -67879,8 +68074,8 @@ function removeSkill(opts) {
|
|
|
67879
68074
|
dir = getProjectSkillsDir(root);
|
|
67880
68075
|
}
|
|
67881
68076
|
const skillDir = join46(dir, name);
|
|
67882
|
-
const
|
|
67883
|
-
if (!existsSync52(
|
|
68077
|
+
const path92 = skillFilePath(dir, name);
|
|
68078
|
+
if (!existsSync52(path92) && !existsSync52(skillDir)) {
|
|
67884
68079
|
return { ok: false, error: `Skill "${name}" not found in ${dir}` };
|
|
67885
68080
|
}
|
|
67886
68081
|
try {
|
|
@@ -67891,7 +68086,7 @@ function removeSkill(opts) {
|
|
|
67891
68086
|
error: err instanceof Error ? err.message : String(err)
|
|
67892
68087
|
};
|
|
67893
68088
|
}
|
|
67894
|
-
return { ok: true, path:
|
|
68089
|
+
return { ok: true, path: path92 };
|
|
67895
68090
|
}
|
|
67896
68091
|
var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
|
|
67897
68092
|
var init_skillConfigIo = __esm({
|
|
@@ -68710,12 +68905,12 @@ function ensureHome() {
|
|
|
68710
68905
|
}
|
|
68711
68906
|
}
|
|
68712
68907
|
function loadCompanionConfig() {
|
|
68713
|
-
const
|
|
68714
|
-
if (!existsSync53(
|
|
68908
|
+
const path92 = getCompanionConfigPath();
|
|
68909
|
+
if (!existsSync53(path92)) {
|
|
68715
68910
|
return { projects: [] };
|
|
68716
68911
|
}
|
|
68717
68912
|
try {
|
|
68718
|
-
const raw = JSON.parse(readFileSync41(
|
|
68913
|
+
const raw = JSON.parse(readFileSync41(path92, "utf8"));
|
|
68719
68914
|
const projects = Array.isArray(raw.projects) ? raw.projects.filter(
|
|
68720
68915
|
(p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
|
|
68721
68916
|
).map((p3) => ({
|
|
@@ -68753,16 +68948,16 @@ function loadOrCreateToken(explicit) {
|
|
|
68753
68948
|
return { token: explicit.trim(), created: false };
|
|
68754
68949
|
}
|
|
68755
68950
|
ensureHome();
|
|
68756
|
-
const
|
|
68757
|
-
if (existsSync53(
|
|
68758
|
-
const t = readFileSync41(
|
|
68951
|
+
const path92 = getCompanionTokenPath();
|
|
68952
|
+
if (existsSync53(path92)) {
|
|
68953
|
+
const t = readFileSync41(path92, "utf8").trim();
|
|
68759
68954
|
if (t) return { token: t, created: false };
|
|
68760
68955
|
}
|
|
68761
68956
|
const token = randomBytes7(24).toString("base64url");
|
|
68762
|
-
writeFileSync25(
|
|
68957
|
+
writeFileSync25(path92, token + "\n", "utf8");
|
|
68763
68958
|
try {
|
|
68764
|
-
const
|
|
68765
|
-
|
|
68959
|
+
const fs46 = __require("node:fs");
|
|
68960
|
+
fs46.chmodSync?.(path92, 384);
|
|
68766
68961
|
} catch {
|
|
68767
68962
|
}
|
|
68768
68963
|
return { token, created: true };
|
|
@@ -68787,17 +68982,17 @@ function mergeProjects(cfg, extraPaths) {
|
|
|
68787
68982
|
byId.set(p3.id, p3);
|
|
68788
68983
|
}
|
|
68789
68984
|
for (const raw of extraPaths) {
|
|
68790
|
-
const
|
|
68791
|
-
if (!
|
|
68792
|
-
let id3 = slugFromPath(
|
|
68985
|
+
const path92 = raw.trim();
|
|
68986
|
+
if (!path92) continue;
|
|
68987
|
+
let id3 = slugFromPath(path92);
|
|
68793
68988
|
let n = 2;
|
|
68794
|
-
while (byId.has(id3) && byId.get(id3).path !==
|
|
68795
|
-
id3 = `${slugFromPath(
|
|
68989
|
+
while (byId.has(id3) && byId.get(id3).path !== path92) {
|
|
68990
|
+
id3 = `${slugFromPath(path92)}-${n++}`;
|
|
68796
68991
|
}
|
|
68797
68992
|
byId.set(id3, {
|
|
68798
68993
|
id: id3,
|
|
68799
|
-
name: slugFromPath(
|
|
68800
|
-
path:
|
|
68994
|
+
name: slugFromPath(path92),
|
|
68995
|
+
path: path92
|
|
68801
68996
|
});
|
|
68802
68997
|
}
|
|
68803
68998
|
return [...byId.values()];
|
|
@@ -68844,6 +69039,63 @@ var init_config = __esm({
|
|
|
68844
69039
|
}
|
|
68845
69040
|
});
|
|
68846
69041
|
|
|
69042
|
+
// src/cli/serve/spineLockSweep.ts
|
|
69043
|
+
import { promises as fs45 } from "node:fs";
|
|
69044
|
+
import path88 from "node:path";
|
|
69045
|
+
async function sweepOrphanSpineLocks(sessionsDir, options = {}) {
|
|
69046
|
+
const dir = sessionsDir ?? resolveSessionsDir();
|
|
69047
|
+
const onSwept = options.onSwept ?? ((sessionId2, reason) => {
|
|
69048
|
+
console.error(
|
|
69049
|
+
`[zelari] spine-lock sweep: removed orphan writer.lock for session ${sessionId2} (${reason})`
|
|
69050
|
+
);
|
|
69051
|
+
});
|
|
69052
|
+
const now = options.now ?? Date.now;
|
|
69053
|
+
const probe = options.probe ?? defaultLockLivenessProbe;
|
|
69054
|
+
const result = { swept: 0, kept: 0, errors: 0 };
|
|
69055
|
+
let entries;
|
|
69056
|
+
try {
|
|
69057
|
+
entries = await fs45.readdir(dir);
|
|
69058
|
+
} catch {
|
|
69059
|
+
return result;
|
|
69060
|
+
}
|
|
69061
|
+
for (const sessionId2 of entries) {
|
|
69062
|
+
const lockPath2 = path88.join(dir, sessionId2, "writer.lock");
|
|
69063
|
+
try {
|
|
69064
|
+
const raw = await fs45.readFile(lockPath2, "utf-8");
|
|
69065
|
+
let lockInfo = {};
|
|
69066
|
+
try {
|
|
69067
|
+
lockInfo = JSON.parse(raw);
|
|
69068
|
+
} catch {
|
|
69069
|
+
result.kept += 1;
|
|
69070
|
+
continue;
|
|
69071
|
+
}
|
|
69072
|
+
const verdict = evaluateLockTakeover(lockInfo, {
|
|
69073
|
+
now: now(),
|
|
69074
|
+
probe,
|
|
69075
|
+
heartbeatStaleMs: options.heartbeatStaleMs ?? resolveHeartbeatStaleMs()
|
|
69076
|
+
});
|
|
69077
|
+
if (!verdict.takeover) {
|
|
69078
|
+
result.kept += 1;
|
|
69079
|
+
continue;
|
|
69080
|
+
}
|
|
69081
|
+
await fs45.rm(lockPath2, { force: true });
|
|
69082
|
+
result.swept += 1;
|
|
69083
|
+
onSwept(sessionId2, verdict.reason);
|
|
69084
|
+
} catch (err) {
|
|
69085
|
+
const code = err.code;
|
|
69086
|
+
if (code === "ENOENT" || code === "ENOTDIR") continue;
|
|
69087
|
+
result.errors += 1;
|
|
69088
|
+
}
|
|
69089
|
+
}
|
|
69090
|
+
return result;
|
|
69091
|
+
}
|
|
69092
|
+
var init_spineLockSweep = __esm({
|
|
69093
|
+
"src/cli/serve/spineLockSweep.ts"() {
|
|
69094
|
+
"use strict";
|
|
69095
|
+
init_session();
|
|
69096
|
+
}
|
|
69097
|
+
});
|
|
69098
|
+
|
|
68847
69099
|
// src/cli/serve/harnessServer.ts
|
|
68848
69100
|
var harnessServer_exports = {};
|
|
68849
69101
|
__export(harnessServer_exports, {
|
|
@@ -69091,6 +69343,10 @@ function startHarnessServer(options = {}) {
|
|
|
69091
69343
|
async function runHarnessServer() {
|
|
69092
69344
|
setActivePolicyLoadSurface("headless");
|
|
69093
69345
|
setActiveProofPersistenceSurface("headless");
|
|
69346
|
+
try {
|
|
69347
|
+
await sweepOrphanSpineLocks();
|
|
69348
|
+
} catch {
|
|
69349
|
+
}
|
|
69094
69350
|
process.env.ZELARI_SERVE_HARNESS = "1";
|
|
69095
69351
|
const started = startHarnessServer();
|
|
69096
69352
|
await new Promise((resolvePromise) => {
|
|
@@ -69115,6 +69371,7 @@ var init_harnessServer = __esm({
|
|
|
69115
69371
|
init_completionProofPersist();
|
|
69116
69372
|
init_policyGate();
|
|
69117
69373
|
init_policyLoadMode();
|
|
69374
|
+
init_spineLockSweep();
|
|
69118
69375
|
}
|
|
69119
69376
|
});
|
|
69120
69377
|
|
|
@@ -69772,9 +70029,9 @@ async function runCompanionServe(opts = {}) {
|
|
|
69772
70029
|
return;
|
|
69773
70030
|
}
|
|
69774
70031
|
const url2 = parseUrl(req);
|
|
69775
|
-
const
|
|
70032
|
+
const path92 = url2.pathname.replace(/\/+$/, "") || "/";
|
|
69776
70033
|
try {
|
|
69777
|
-
if (req.method === "GET" && (
|
|
70034
|
+
if (req.method === "GET" && (path92 === "/health" || path92 === "/v1/health")) {
|
|
69778
70035
|
sendJson2(res, 200, {
|
|
69779
70036
|
ok: true,
|
|
69780
70037
|
service: "zelari-companion",
|
|
@@ -69786,18 +70043,18 @@ async function runCompanionServe(opts = {}) {
|
|
|
69786
70043
|
});
|
|
69787
70044
|
return;
|
|
69788
70045
|
}
|
|
69789
|
-
if (
|
|
70046
|
+
if (path92.startsWith("/v1")) {
|
|
69790
70047
|
if (!tokenMatches(token, getBearer(req))) {
|
|
69791
70048
|
sendJson2(res, 401, { ok: false, error: "unauthorized" });
|
|
69792
70049
|
return;
|
|
69793
70050
|
}
|
|
69794
70051
|
}
|
|
69795
|
-
if (req.method === "GET" &&
|
|
70052
|
+
if (req.method === "GET" && path92 === "/v1/config") {
|
|
69796
70053
|
const snap = buildDesktopConfigSnapshot();
|
|
69797
70054
|
sendJson2(res, 200, { ok: true, ...snap });
|
|
69798
70055
|
return;
|
|
69799
70056
|
}
|
|
69800
|
-
if (req.method === "GET" &&
|
|
70057
|
+
if (req.method === "GET" && path92 === "/v1/projects") {
|
|
69801
70058
|
sendJson2(res, 200, {
|
|
69802
70059
|
ok: true,
|
|
69803
70060
|
projects: projects.map((p3) => ({
|
|
@@ -69808,7 +70065,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
69808
70065
|
});
|
|
69809
70066
|
return;
|
|
69810
70067
|
}
|
|
69811
|
-
if (req.method === "GET" &&
|
|
70068
|
+
if (req.method === "GET" && path92 === "/v1/runs") {
|
|
69812
70069
|
sendJson2(res, 200, {
|
|
69813
70070
|
ok: true,
|
|
69814
70071
|
active: runs.getActive(),
|
|
@@ -69826,7 +70083,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
69826
70083
|
});
|
|
69827
70084
|
return;
|
|
69828
70085
|
}
|
|
69829
|
-
if (req.method === "POST" &&
|
|
70086
|
+
if (req.method === "POST" && path92 === "/v1/runs") {
|
|
69830
70087
|
const raw = await readBody(req);
|
|
69831
70088
|
let body = {};
|
|
69832
70089
|
try {
|
|
@@ -69874,7 +70131,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
69874
70131
|
});
|
|
69875
70132
|
return;
|
|
69876
70133
|
}
|
|
69877
|
-
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(
|
|
70134
|
+
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path92);
|
|
69878
70135
|
if (req.method === "GET" && eventsMatch) {
|
|
69879
70136
|
const runId = eventsMatch[1];
|
|
69880
70137
|
const run = runs.getRun(runId);
|
|
@@ -69939,7 +70196,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
69939
70196
|
}, 500);
|
|
69940
70197
|
return;
|
|
69941
70198
|
}
|
|
69942
|
-
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(
|
|
70199
|
+
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path92);
|
|
69943
70200
|
if (req.method === "POST" && cancelMatch) {
|
|
69944
70201
|
const runId = cancelMatch[1];
|
|
69945
70202
|
const result = runs.cancel(runId);
|
|
@@ -69950,7 +70207,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
69950
70207
|
sendJson2(res, 200, { ok: true, cancelled: runId });
|
|
69951
70208
|
return;
|
|
69952
70209
|
}
|
|
69953
|
-
const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(
|
|
70210
|
+
const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(path92);
|
|
69954
70211
|
if (req.method === "POST" && steerMatch) {
|
|
69955
70212
|
const runId = steerMatch[1];
|
|
69956
70213
|
const raw = await readBody(req);
|
|
@@ -70117,11 +70374,11 @@ import { execSync as execSync2 } from "node:child_process";
|
|
|
70117
70374
|
import { existsSync as existsSync55, readFileSync as readFileSync42, readlinkSync, statSync as statSync10 } from "node:fs";
|
|
70118
70375
|
import { createRequire as createRequire3 } from "node:module";
|
|
70119
70376
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
70120
|
-
import
|
|
70377
|
+
import path89 from "node:path";
|
|
70121
70378
|
function findPackageRoot(start) {
|
|
70122
70379
|
let dir = start;
|
|
70123
70380
|
for (let i = 0; i < 6; i += 1) {
|
|
70124
|
-
const candidate =
|
|
70381
|
+
const candidate = path89.join(dir, "package.json");
|
|
70125
70382
|
if (existsSync55(candidate)) {
|
|
70126
70383
|
try {
|
|
70127
70384
|
const pkg = JSON.parse(readFileSync42(candidate, "utf8"));
|
|
@@ -70129,11 +70386,11 @@ function findPackageRoot(start) {
|
|
|
70129
70386
|
} catch {
|
|
70130
70387
|
}
|
|
70131
70388
|
}
|
|
70132
|
-
const parent =
|
|
70389
|
+
const parent = path89.dirname(dir);
|
|
70133
70390
|
if (parent === dir) break;
|
|
70134
70391
|
dir = parent;
|
|
70135
70392
|
}
|
|
70136
|
-
return
|
|
70393
|
+
return path89.resolve(__dirname3, "..", "..", "..");
|
|
70137
70394
|
}
|
|
70138
70395
|
function tryExec(cmd) {
|
|
70139
70396
|
try {
|
|
@@ -70147,7 +70404,7 @@ function tryExec(cmd) {
|
|
|
70147
70404
|
}
|
|
70148
70405
|
function readPackageJson4() {
|
|
70149
70406
|
try {
|
|
70150
|
-
const pkgPath =
|
|
70407
|
+
const pkgPath = path89.join(packageRoot, "package.json");
|
|
70151
70408
|
return JSON.parse(readFileSync42(pkgPath, "utf8"));
|
|
70152
70409
|
} catch {
|
|
70153
70410
|
return null;
|
|
@@ -70163,7 +70420,7 @@ function checkShim(pkgName) {
|
|
|
70163
70420
|
}
|
|
70164
70421
|
const isWin = process.platform === "win32";
|
|
70165
70422
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
70166
|
-
const shimPath =
|
|
70423
|
+
const shimPath = path89.join(prefix, shimName);
|
|
70167
70424
|
if (!existsSync55(shimPath)) {
|
|
70168
70425
|
return FAIL(
|
|
70169
70426
|
`shim not found at ${shimPath}
|
|
@@ -70191,8 +70448,8 @@ function checkShim(pkgName) {
|
|
|
70191
70448
|
fix: npm install -g ${pkgName}@latest --force`
|
|
70192
70449
|
);
|
|
70193
70450
|
}
|
|
70194
|
-
const resolved =
|
|
70195
|
-
const expected =
|
|
70451
|
+
const resolved = path89.resolve(path89.dirname(shimPath), target);
|
|
70452
|
+
const expected = path89.join(
|
|
70196
70453
|
prefix,
|
|
70197
70454
|
"node_modules",
|
|
70198
70455
|
pkgName,
|
|
@@ -70234,7 +70491,7 @@ function checkNode(pkg) {
|
|
|
70234
70491
|
return OK(`node ${raw} (engines.node ${enginesNode ?? ">= 20.0.0"})`);
|
|
70235
70492
|
}
|
|
70236
70493
|
function checkBundle() {
|
|
70237
|
-
const bundle =
|
|
70494
|
+
const bundle = path89.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
70238
70495
|
if (!existsSync55(bundle)) {
|
|
70239
70496
|
return FAIL(
|
|
70240
70497
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
@@ -70255,7 +70512,7 @@ function checkRuntimeDeps() {
|
|
|
70255
70512
|
const missing = [];
|
|
70256
70513
|
for (const dep of required2) {
|
|
70257
70514
|
try {
|
|
70258
|
-
const localReq = createRequire3(
|
|
70515
|
+
const localReq = createRequire3(path89.join(packageRoot, "package.json"));
|
|
70259
70516
|
localReq.resolve(dep);
|
|
70260
70517
|
} catch {
|
|
70261
70518
|
missing.push(dep);
|
|
@@ -70455,7 +70712,7 @@ var init_doctor = __esm({
|
|
|
70455
70712
|
init_metrics3();
|
|
70456
70713
|
init_contextGrowthSummary();
|
|
70457
70714
|
require3 = createRequire3(import.meta.url);
|
|
70458
|
-
__dirname3 =
|
|
70715
|
+
__dirname3 = path89.dirname(fileURLToPath3(import.meta.url));
|
|
70459
70716
|
packageRoot = findPackageRoot(__dirname3);
|
|
70460
70717
|
OK = (message) => ({
|
|
70461
70718
|
ok: true,
|
|
@@ -70639,7 +70896,7 @@ __export(inspectSession_exports, {
|
|
|
70639
70896
|
renderInspectReport: () => renderInspectReport,
|
|
70640
70897
|
runInspectSession: () => runInspectSession
|
|
70641
70898
|
});
|
|
70642
|
-
import
|
|
70899
|
+
import path90 from "node:path";
|
|
70643
70900
|
import { existsSync as existsSync56 } from "node:fs";
|
|
70644
70901
|
function formatLimit(limit) {
|
|
70645
70902
|
return `${Math.round(limit / 1e3)}k`;
|
|
@@ -70673,8 +70930,8 @@ function renderInspectReport(state3) {
|
|
|
70673
70930
|
}
|
|
70674
70931
|
async function runInspectSession(opts) {
|
|
70675
70932
|
const sessionsDir = resolveSessionsDir({ workspaceRoot: opts.cwd ?? process.cwd() });
|
|
70676
|
-
const sessionDir =
|
|
70677
|
-
const eventsPath =
|
|
70933
|
+
const sessionDir = path90.join(sessionsDir, opts.sessionId);
|
|
70934
|
+
const eventsPath = path90.join(sessionDir, "events.jsonl");
|
|
70678
70935
|
if (!existsSync56(sessionDir)) {
|
|
70679
70936
|
console.error(`zelari-code inspect: no session directory at ${sessionDir}`);
|
|
70680
70937
|
return 1;
|
|
@@ -70708,15 +70965,15 @@ __export(inspect_exports, {
|
|
|
70708
70965
|
collectInspectReport: () => collectInspectReport,
|
|
70709
70966
|
runInspect: () => runInspect
|
|
70710
70967
|
});
|
|
70711
|
-
import
|
|
70968
|
+
import path91 from "node:path";
|
|
70712
70969
|
import { existsSync as existsSync57, readFileSync as readFileSync43, readdirSync as readdirSync12 } from "node:fs";
|
|
70713
70970
|
import { homedir as homedir17 } from "node:os";
|
|
70714
70971
|
async function collectInspectReport(cwd = process.cwd()) {
|
|
70715
70972
|
ensureBuiltinSkillsLoadedSync();
|
|
70716
70973
|
const snap = listSkillsSnapshot(cwd);
|
|
70717
70974
|
const mcp = listMcpServers(cwd);
|
|
70718
|
-
const userMcpPath =
|
|
70719
|
-
const projectMcpPath =
|
|
70975
|
+
const userMcpPath = path91.join(homedir17(), ".zelari-code", "mcp.json");
|
|
70976
|
+
const projectMcpPath = path91.join(cwd, ".zelari", "mcp.json");
|
|
70720
70977
|
const globalHooks = globalHooksDir();
|
|
70721
70978
|
const projectHooks = projectHooksDir(cwd);
|
|
70722
70979
|
const projectTrusted = isFolderTrusted(cwd);
|
|
@@ -70744,9 +71001,9 @@ async function collectInspectReport(cwd = process.cwd()) {
|
|
|
70744
71001
|
configSources: [
|
|
70745
71002
|
{ path: userMcpPath, exists: existsSync57(userMcpPath) },
|
|
70746
71003
|
{ path: projectMcpPath, exists: existsSync57(projectMcpPath) },
|
|
70747
|
-
{ path:
|
|
70748
|
-
{ path:
|
|
70749
|
-
{ path:
|
|
71004
|
+
{ path: path91.join(homedir17(), ".zelari-code", "provider.json"), exists: existsSync57(path91.join(homedir17(), ".zelari-code", "provider.json")) },
|
|
71005
|
+
{ path: path91.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync57(path91.join(cwd, ".zelari", "AGENTS.md")) },
|
|
71006
|
+
{ path: path91.join(cwd, "AGENTS.md"), exists: existsSync57(path91.join(cwd, "AGENTS.md")) }
|
|
70750
71007
|
],
|
|
70751
71008
|
skills: {
|
|
70752
71009
|
total: snap.skills.length,
|
|
@@ -70786,8 +71043,8 @@ function listJsonFiles(dir) {
|
|
|
70786
71043
|
}
|
|
70787
71044
|
function findAgentsMd(cwd) {
|
|
70788
71045
|
const candidates = [
|
|
70789
|
-
|
|
70790
|
-
|
|
71046
|
+
path91.join(cwd, "AGENTS.md"),
|
|
71047
|
+
path91.join(cwd, ".zelari", "AGENTS.md")
|
|
70791
71048
|
];
|
|
70792
71049
|
const found = [];
|
|
70793
71050
|
for (const c of candidates) {
|
|
@@ -80432,8 +80689,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
|
|
|
80432
80689
|
let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
80433
80690
|
if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
|
|
80434
80691
|
try {
|
|
80435
|
-
const
|
|
80436
|
-
name =
|
|
80692
|
+
const path92 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
80693
|
+
name = path92 && /^[a-z0-9]/.test(path92) ? path92 : "imported-skill";
|
|
80437
80694
|
} catch {
|
|
80438
80695
|
name = "imported-skill";
|
|
80439
80696
|
}
|