@jentrix/cli 0.5.21 → 0.5.22
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/main.js +71 -19
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -20966,7 +20966,7 @@ function refreshAccessToken(input) {
|
|
|
20966
20966
|
|
|
20967
20967
|
// src/client.ts
|
|
20968
20968
|
var CLI_NAME = "stacks-cli";
|
|
20969
|
-
var CLI_VERSION = "0.5.
|
|
20969
|
+
var CLI_VERSION = "0.5.22";
|
|
20970
20970
|
var DEAD_TOKEN_MESSAGE = "authentication failed (HTTP 401): the token is dead or expired \u2014 mint a new PAT at /account/tokens on your Jentrix server and update STACKS_TOKEN (or --token / the config file).";
|
|
20971
20971
|
var RELOGIN_MESSAGE = "OAuth session expired and could not be refreshed \u2014 run `jentrix login` to sign in again.";
|
|
20972
20972
|
function originOf2(url2) {
|
|
@@ -21388,6 +21388,9 @@ function buildAlignDisclosures(catalog) {
|
|
|
21388
21388
|
disclosures.push(
|
|
21389
21389
|
catalog.captureMode === "on" ? "TRACE capture: ON \u2014 the full transcript will be recorded" : "TRACE capture: OFF (MVP default) \u2014 typed artifacts only, no transcript"
|
|
21390
21390
|
);
|
|
21391
|
+
disclosures.push(
|
|
21392
|
+
catalog.skeletonMode === "on" ? "activity skeleton: ON (default) \u2014 content-free counts/timing (turns, tool names, files-touched), independent of capture; --no-skeleton opts out" : "activity skeleton: OFF \u2014 no counts, no timing, no files-touched; the session's Session activity section will read as not observed"
|
|
21393
|
+
);
|
|
21391
21394
|
disclosures.push("write the local alignment marker for this checkout");
|
|
21392
21395
|
const mcp = catalog.mcpConfig;
|
|
21393
21396
|
if (mcp && mcp.action !== "unchanged") {
|
|
@@ -22450,20 +22453,39 @@ function readLiveHostMarker(deps2, sessionId) {
|
|
|
22450
22453
|
return null;
|
|
22451
22454
|
}
|
|
22452
22455
|
}
|
|
22453
|
-
async function
|
|
22456
|
+
async function waitForHostEnd(deps2, sessionId, pid, timeoutMs) {
|
|
22454
22457
|
const sleep = deps2.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
22455
|
-
const
|
|
22458
|
+
const dir = join2(deps2.spoolRoot, sessionId);
|
|
22459
|
+
const path = join2(dir, "host.json");
|
|
22456
22460
|
const deadline = Date.now() + timeoutMs;
|
|
22457
22461
|
while (Date.now() < deadline) {
|
|
22458
22462
|
await sleep(500);
|
|
22463
|
+
const refusal = readEndRefusal(deps2, sessionId);
|
|
22464
|
+
if (refusal) return { kind: "refused", message: refusal };
|
|
22459
22465
|
try {
|
|
22460
22466
|
const marker = JSON.parse(readFileSync2(path, "utf8"));
|
|
22461
|
-
if (marker.exitedAt) return
|
|
22467
|
+
if (marker.exitedAt) return { kind: "exited" };
|
|
22462
22468
|
} catch {
|
|
22463
22469
|
}
|
|
22464
|
-
if (!(deps2.isPidAlive ?? defaultIsPidAlive)(pid)) return
|
|
22470
|
+
if (!(deps2.isPidAlive ?? defaultIsPidAlive)(pid)) return { kind: "gone" };
|
|
22471
|
+
}
|
|
22472
|
+
return { kind: "gone" };
|
|
22473
|
+
}
|
|
22474
|
+
function readEndRefusal(deps2, sessionId) {
|
|
22475
|
+
try {
|
|
22476
|
+
const marker = JSON.parse(
|
|
22477
|
+
readFileSync2(join2(deps2.spoolRoot, sessionId, "end-refusal.json"), "utf8")
|
|
22478
|
+
);
|
|
22479
|
+
return typeof marker.message === "string" && marker.message.trim() ? marker.message : null;
|
|
22480
|
+
} catch {
|
|
22481
|
+
return null;
|
|
22482
|
+
}
|
|
22483
|
+
}
|
|
22484
|
+
function clearEndRefusal(deps2, sessionId) {
|
|
22485
|
+
try {
|
|
22486
|
+
unlinkSync(join2(deps2.spoolRoot, sessionId, "end-refusal.json"));
|
|
22487
|
+
} catch {
|
|
22465
22488
|
}
|
|
22466
|
-
return false;
|
|
22467
22489
|
}
|
|
22468
22490
|
function localCaptureLines(deps2, sessionId, serverStatus) {
|
|
22469
22491
|
const dir = join2(deps2.spoolRoot, sessionId);
|
|
@@ -23163,14 +23185,12 @@ async function buildAttestedDiffBody(git, root, startHead, endHead, dirty) {
|
|
|
23163
23185
|
}
|
|
23164
23186
|
body = notice + kept;
|
|
23165
23187
|
}
|
|
23188
|
+
let uncommitted = null;
|
|
23166
23189
|
if (dirty) {
|
|
23167
23190
|
const delta = await git(["diff", "--stat", "HEAD"], root);
|
|
23168
|
-
|
|
23169
|
-
|
|
23170
|
-
Uncommitted delta at end (stat only \u2014 not part of the attested range):
|
|
23171
|
-
${delta.code === 0 && delta.stdout.trim() ? delta.stdout : "(unreadable or empty)"}`;
|
|
23191
|
+
uncommitted = delta.code === 0 && delta.stdout.trim() ? delta.stdout.trim() : null;
|
|
23172
23192
|
}
|
|
23173
|
-
return { body: header + body, commitCount };
|
|
23193
|
+
return { body: header + body, commitCount, uncommitted };
|
|
23174
23194
|
}
|
|
23175
23195
|
async function postAttestedArtifact(deps2, sessionId, input) {
|
|
23176
23196
|
const credentials = deps2.resolveTarget();
|
|
@@ -23298,6 +23318,12 @@ async function runSessionEnd(sessionIdFlag, flags, deps2) {
|
|
|
23298
23318
|
deps2.writeOut(
|
|
23299
23319
|
`Attested diff pushed \u2192 artifact ${pushed.artifactId ?? "(unknown)"} (${built.commitCount} commit(s), source: cli)`
|
|
23300
23320
|
);
|
|
23321
|
+
if (built.uncommitted) {
|
|
23322
|
+
deps2.writeOut(
|
|
23323
|
+
`note: uncommitted delta at end (stat only \u2014 not part of the attested range):
|
|
23324
|
+
${built.uncommitted}`
|
|
23325
|
+
);
|
|
23326
|
+
}
|
|
23301
23327
|
} else {
|
|
23302
23328
|
deps2.writeErr(
|
|
23303
23329
|
`note: attested diff push failed (${pushed.detail}) \u2014 the E1 delivery check will refuse the close; retry \`jentrix session end\`, or acknowledge with --acknowledge-evidence-gaps.`
|
|
@@ -23320,21 +23346,32 @@ async function runSessionEnd(sessionIdFlag, flags, deps2) {
|
|
|
23320
23346
|
};
|
|
23321
23347
|
const live = readLiveHostMarker(deps2, sessionId);
|
|
23322
23348
|
if (live) {
|
|
23349
|
+
clearEndRefusal(deps2, sessionId);
|
|
23323
23350
|
writeFileSync2(
|
|
23324
23351
|
join2(deps2.spoolRoot, sessionId, "end-request.json"),
|
|
23325
|
-
JSON.stringify({
|
|
23352
|
+
JSON.stringify({
|
|
23353
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23354
|
+
// JEN-167: the host closes the session, so the operator's
|
|
23355
|
+
// acknowledgement has to travel with the request — otherwise the
|
|
23356
|
+
// floor refuses a close they explicitly asked to force. The
|
|
23357
|
+
// CLI-counted commits ride along for the same reason: only the
|
|
23358
|
+
// CLI can count them, and the host's close is the one that lands.
|
|
23359
|
+
...flags.acknowledgeEvidenceGaps ? { acknowledgeEvidenceGaps: true } : {},
|
|
23360
|
+
...commitCount !== null ? { commitCount } : {}
|
|
23361
|
+
}),
|
|
23326
23362
|
{ mode: 384 }
|
|
23327
23363
|
);
|
|
23328
23364
|
deps2.writeOut(
|
|
23329
23365
|
`Local capture host (pid ${live.pid}) is finalizing capture\u2026`
|
|
23330
23366
|
);
|
|
23331
|
-
const
|
|
23332
|
-
|
|
23333
|
-
|
|
23334
|
-
|
|
23335
|
-
|
|
23336
|
-
|
|
23337
|
-
|
|
23367
|
+
const outcome = await waitForHostEnd(deps2, sessionId, live.pid, 9e4);
|
|
23368
|
+
if (outcome.kind === "refused") {
|
|
23369
|
+
deps2.writeErr(
|
|
23370
|
+
`local capture host (pid ${live.pid}) is still running \u2014 the skeleton, heartbeats and telemetry keep accumulating through the comply work; re-run \`jentrix session end ${sessionId}\` once the evidence is pushed`
|
|
23371
|
+
);
|
|
23372
|
+
throw new Error(outcome.message);
|
|
23373
|
+
}
|
|
23374
|
+
if (outcome.kind === "exited") {
|
|
23338
23375
|
const final = await call(caller, "get_agent_session", { sessionId });
|
|
23339
23376
|
const stillOpen = final.status === "STARTING" || final.status === "ACTIVE";
|
|
23340
23377
|
if (!stillOpen) {
|
|
@@ -24185,6 +24222,9 @@ async function buildCatalog(caller, flags, detection) {
|
|
|
24185
24222
|
// checkout root); defaults here are the no-live-host reading.
|
|
24186
24223
|
liveHostPid: null,
|
|
24187
24224
|
captureMode: decideCaptureMode(flags.capture, false),
|
|
24225
|
+
// JEN-169: only `--no-skeleton` acts (the same rule submit applies at
|
|
24226
|
+
// line ~1273), so the disclosure needs no server round trip.
|
|
24227
|
+
skeletonMode: flags.skeleton === false ? "off" : "on",
|
|
24188
24228
|
producer: producerFromFlags(flags),
|
|
24189
24229
|
tokenBudget: budgetFromFlags(flags),
|
|
24190
24230
|
preconditionNotices: [],
|
|
@@ -26574,6 +26614,10 @@ function parseArgsJson(raw, source) {
|
|
|
26574
26614
|
}
|
|
26575
26615
|
return { ok: true, value: parsed };
|
|
26576
26616
|
}
|
|
26617
|
+
function workspaceIdShapeError(value) {
|
|
26618
|
+
if (/^[a-z0-9]{20,}$/.test(value)) return null;
|
|
26619
|
+
return `workspaceId "${value}" is not a workspace id (ids look like "cmt2cuhyo000004l38ddsim1y"). Find yours with \`jentrix workspace list\`, set it once as \`defaults.workspace\` in the config file, or use \`jentrix align --workspace <id-or-slug>\` \u2014 align is the command that accepts a slug.`;
|
|
26620
|
+
}
|
|
26577
26621
|
async function runToolCommand(name, flags, deps2) {
|
|
26578
26622
|
const maxWaitSeconds = Number(flags.maxWait);
|
|
26579
26623
|
if (!Number.isFinite(maxWaitSeconds) || maxWaitSeconds < 0) {
|
|
@@ -26610,6 +26654,14 @@ async function runToolCommand(name, flags, deps2) {
|
|
|
26610
26654
|
deps2.writeErr(`error: ${parsed.error}`);
|
|
26611
26655
|
return EXIT_CODES.INVALID_INPUT;
|
|
26612
26656
|
}
|
|
26657
|
+
const workspaceId = parsed.value.workspaceId;
|
|
26658
|
+
if (typeof workspaceId === "string") {
|
|
26659
|
+
const shapeError = workspaceIdShapeError(workspaceId);
|
|
26660
|
+
if (shapeError) {
|
|
26661
|
+
deps2.writeErr(`error: ${shapeError}`);
|
|
26662
|
+
return EXIT_CODES.INVALID_INPUT;
|
|
26663
|
+
}
|
|
26664
|
+
}
|
|
26613
26665
|
let config2;
|
|
26614
26666
|
try {
|
|
26615
26667
|
config2 = resolveConfig({
|