@byok-sdk/client 0.15.0 → 0.17.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/bin/byok-agent.js +1065 -1061
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/daemon/agent-message-outbox.d.ts +2 -0
- package/dist/diagnostics/diagnostics.d.ts +2 -10
- package/dist/diagnostics/operator-actions.d.ts +63 -0
- package/dist/diagnostics/types.d.ts +10 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +641 -55
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/bin/byok-agent.js
CHANGED
|
@@ -2379,7 +2379,7 @@ function resolveLocalAgentReleaseIdentity(input) {
|
|
|
2379
2379
|
|
|
2380
2380
|
// src/bin/official-release.ts
|
|
2381
2381
|
var OFFICIAL_LOCAL_AGENT_RELEASE = resolveLocalAgentReleaseIdentity({
|
|
2382
|
-
version: "0.
|
|
2382
|
+
version: "0.17.0"
|
|
2383
2383
|
});
|
|
2384
2384
|
|
|
2385
2385
|
// src/bin/config.ts
|
|
@@ -14266,6 +14266,10 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
14266
14266
|
this.revokedTasks.add(taskId);
|
|
14267
14267
|
});
|
|
14268
14268
|
}
|
|
14269
|
+
/** The same terminal classification used by operator receipts and archival. */
|
|
14270
|
+
terminalRecords() {
|
|
14271
|
+
return Object.freeze(this.records().filter((record4) => this.isTerminalEvidence(record4.taskId)));
|
|
14272
|
+
}
|
|
14269
14273
|
isTerminalEvidence(taskId) {
|
|
14270
14274
|
return this.revokedTasks.has(taskId) || this.dispositionByTask.get(taskId)?.outcome === "refused";
|
|
14271
14275
|
}
|
|
@@ -14357,7 +14361,7 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
14357
14361
|
async archiveTerminalRecords(archiveDirectory) {
|
|
14358
14362
|
return this.exclusive(async () => {
|
|
14359
14363
|
if (!path14__default.isAbsolute(archiveDirectory)) throw new AgentMessageOutboxError("message archive directory must be absolute");
|
|
14360
|
-
const terminal = this.
|
|
14364
|
+
const terminal = this.terminalRecords();
|
|
14361
14365
|
if (terminal.length === 0) return void 0;
|
|
14362
14366
|
await ensureSecureDir(archiveDirectory);
|
|
14363
14367
|
const archivePath = path14__default.join(archiveDirectory, `message-terminal-${randomUUID()}.jsonl`);
|
|
@@ -22910,1004 +22914,1192 @@ async function repairDeviceEnrollmentMetadata(config, input) {
|
|
|
22910
22914
|
throw new DeviceMetadataRepairError(failureCode);
|
|
22911
22915
|
}
|
|
22912
22916
|
}
|
|
22913
|
-
|
|
22914
|
-
|
|
22915
|
-
function quote(text) {
|
|
22916
|
-
return JSON.stringify(text);
|
|
22917
|
+
function auditLogPath(storeDir) {
|
|
22918
|
+
return path14__default.join(storeDir, "audit.jsonl");
|
|
22917
22919
|
}
|
|
22918
|
-
|
|
22919
|
-
|
|
22920
|
+
var AUDIT_LOG_MODE = 384;
|
|
22921
|
+
var AUDIT_STORE_DIR_MODE = 448;
|
|
22922
|
+
var MAX_AUDIT_LOG_BYTES = 10 * 1024 * 1024;
|
|
22923
|
+
var AUDIT_LOG_TRIM_TARGET_LINES = 5e3;
|
|
22924
|
+
var MAX_LIVE_TASK_ANCHORS = 500;
|
|
22925
|
+
function byteSize(text) {
|
|
22926
|
+
return text === void 0 ? void 0 : Buffer.byteLength(text, "utf8");
|
|
22927
|
+
}
|
|
22928
|
+
function valueByteSize(value) {
|
|
22929
|
+
if (value === void 0) return void 0;
|
|
22930
|
+
try {
|
|
22931
|
+
const json = JSON.stringify(value);
|
|
22932
|
+
return json === void 0 ? void 0 : Buffer.byteLength(json, "utf8");
|
|
22933
|
+
} catch {
|
|
22934
|
+
return void 0;
|
|
22935
|
+
}
|
|
22936
|
+
}
|
|
22937
|
+
function placeholderFor(size, spilled) {
|
|
22938
|
+
const suffix = spilled ? ", spilled" : "";
|
|
22939
|
+
return size === void 0 ? `[redacted${suffix}]` : `[redacted: ${size} bytes${suffix}]`;
|
|
22920
22940
|
}
|
|
22921
22941
|
var STABLE_GIT_ERROR_CATEGORIES = new Set(GIT_ERROR_CATEGORIES);
|
|
22922
22942
|
function stableGitErrorCategory(value) {
|
|
22923
|
-
return value
|
|
22943
|
+
return typeof value === "string" && STABLE_GIT_ERROR_CATEGORIES.has(value) ? value : void 0;
|
|
22924
22944
|
}
|
|
22925
|
-
function
|
|
22945
|
+
function gitCount(value) {
|
|
22946
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
22947
|
+
}
|
|
22948
|
+
function gitDirty(value) {
|
|
22949
|
+
const dirty = asRecord2(value);
|
|
22950
|
+
const staged = gitCount(dirty.staged);
|
|
22951
|
+
const unstaged = gitCount(dirty.unstaged);
|
|
22952
|
+
const untracked = gitCount(dirty.untracked);
|
|
22953
|
+
const conflicted = gitCount(dirty.conflicted);
|
|
22954
|
+
return staged !== void 0 && unstaged !== void 0 && untracked !== void 0 && conflicted !== void 0 ? { staged, unstaged, untracked, conflicted } : void 0;
|
|
22955
|
+
}
|
|
22956
|
+
function redactAgentEvent(event) {
|
|
22926
22957
|
switch (event.type) {
|
|
22927
22958
|
case "progress":
|
|
22928
|
-
return
|
|
22959
|
+
return { type: "progress", textSize: byteSize(event.text) };
|
|
22929
22960
|
case "tool_use":
|
|
22930
|
-
return
|
|
22961
|
+
return event.spill !== void 0 ? { type: "tool_use", tool: event.tool, inputSize: event.spill.totalBytes, inputSpilled: true } : { type: "tool_use", tool: event.tool, inputSize: valueByteSize(event.input) };
|
|
22931
22962
|
case "tool_result":
|
|
22932
|
-
return
|
|
22963
|
+
return event.spill !== void 0 ? { type: "tool_result", tool: event.tool, outputSize: event.spill.totalBytes, outputSpilled: true } : { type: "tool_result", tool: event.tool, outputSize: valueByteSize(event.output) };
|
|
22933
22964
|
case "artifact":
|
|
22934
|
-
return
|
|
22965
|
+
return { type: "artifact", name: event.name, contentType: event.contentType };
|
|
22935
22966
|
case "needs_approval":
|
|
22936
|
-
return
|
|
22967
|
+
return { type: "needs_approval", summarySize: byteSize(event.summary) };
|
|
22937
22968
|
case "turn_end":
|
|
22938
|
-
return "turn_end";
|
|
22969
|
+
return { type: "turn_end" };
|
|
22939
22970
|
case "error":
|
|
22940
|
-
return
|
|
22941
|
-
case "usage":
|
|
22942
|
-
|
|
22943
|
-
|
|
22944
|
-
|
|
22945
|
-
|
|
22946
|
-
|
|
22947
|
-
|
|
22971
|
+
return { type: "error", messageSize: byteSize(event.message) };
|
|
22972
|
+
case "usage":
|
|
22973
|
+
return {
|
|
22974
|
+
type: "usage",
|
|
22975
|
+
inputTokens: event.inputTokens,
|
|
22976
|
+
cachedInputTokens: event.cachedInputTokens,
|
|
22977
|
+
outputTokens: event.outputTokens,
|
|
22978
|
+
reasoningTokens: event.reasoningTokens,
|
|
22979
|
+
totalTokens: event.totalTokens
|
|
22980
|
+
};
|
|
22948
22981
|
}
|
|
22949
22982
|
}
|
|
22950
|
-
function
|
|
22951
|
-
const
|
|
22983
|
+
function redactForAudit(event) {
|
|
22984
|
+
const base = { kind: event.kind, ts: event.ts };
|
|
22952
22985
|
switch (event.kind) {
|
|
22953
22986
|
case "offered":
|
|
22954
|
-
return
|
|
22987
|
+
return { ...base, taskId: event.taskId, runtime: event.runtime };
|
|
22955
22988
|
case "claimed":
|
|
22956
|
-
return
|
|
22989
|
+
return { ...base, taskId: event.taskId, claimedRuntime: event.claimedRuntime };
|
|
22957
22990
|
case "started":
|
|
22958
|
-
return
|
|
22991
|
+
return { ...base, taskId: event.taskId };
|
|
22959
22992
|
case "progress":
|
|
22960
|
-
return
|
|
22993
|
+
return { ...base, taskId: event.taskId, event: redactAgentEvent(event.event) };
|
|
22961
22994
|
case "artifact":
|
|
22962
|
-
return
|
|
22963
|
-
|
|
22964
|
-
|
|
22965
|
-
|
|
22966
|
-
|
|
22995
|
+
return {
|
|
22996
|
+
...base,
|
|
22997
|
+
taskId: event.taskId,
|
|
22998
|
+
name: event.name,
|
|
22999
|
+
contentType: event.contentType,
|
|
23000
|
+
// `inline` is base64 file bytes (up to the 64KB inline cap) — never
|
|
23001
|
+
// persisted. `blobRef.url` is a presigned URL — itself effectively a
|
|
23002
|
+
// time-limited bearer credential for the actual bytes, so it's
|
|
23003
|
+
// dropped too; only the safe pointer/size metadata survives.
|
|
23004
|
+
inlineSize: byteSize(event.inline),
|
|
23005
|
+
blobRef: event.blobRef ? {
|
|
23006
|
+
blobId: event.blobRef.blobId,
|
|
23007
|
+
contentHash: event.blobRef.contentHash,
|
|
23008
|
+
size: event.blobRef.size,
|
|
23009
|
+
contentType: event.blobRef.contentType
|
|
23010
|
+
} : void 0
|
|
23011
|
+
};
|
|
23012
|
+
case "awaiting-approval":
|
|
23013
|
+
return { ...base, taskId: event.taskId, summarySize: byteSize(event.summary) };
|
|
22967
23014
|
case "completed":
|
|
22968
|
-
return
|
|
23015
|
+
return { ...base, taskId: event.taskId, summarySize: byteSize(event.summary), sessionRef: event.sessionRef };
|
|
22969
23016
|
case "failed":
|
|
22970
|
-
return
|
|
23017
|
+
return {
|
|
23018
|
+
...base,
|
|
23019
|
+
taskId: event.taskId,
|
|
23020
|
+
reasonSize: byteSize(event.reason),
|
|
23021
|
+
retryable: event.retryable,
|
|
23022
|
+
preClaim: event.preClaim
|
|
23023
|
+
};
|
|
22971
23024
|
case "cancelled":
|
|
22972
|
-
return
|
|
23025
|
+
return { ...base, taskId: event.taskId, reasonSize: byteSize(event.reason) };
|
|
22973
23026
|
case "connection":
|
|
22974
|
-
return
|
|
23027
|
+
return { ...base, state: event.state };
|
|
22975
23028
|
case "paired":
|
|
22976
|
-
return
|
|
23029
|
+
return { ...base, deviceId: event.deviceId };
|
|
22977
23030
|
case "unpaired":
|
|
22978
|
-
return
|
|
23031
|
+
return { ...base };
|
|
22979
23032
|
case "runtimes-detected":
|
|
22980
|
-
return
|
|
23033
|
+
return { ...base, runtimes: event.runtimes };
|
|
22981
23034
|
case "shutdown-requested":
|
|
22982
|
-
return
|
|
23035
|
+
return { ...base, reason: event.reason };
|
|
22983
23036
|
case "shutdown-complete":
|
|
22984
|
-
return
|
|
23037
|
+
return { ...base, reason: event.reason, undeliveredOutboxCount: event.undeliveredOutboxCount };
|
|
22985
23038
|
case "stale-approval-decision":
|
|
22986
|
-
return
|
|
23039
|
+
return { ...base, taskId: event.taskId, decision: event.decision, reasonSize: byteSize(event.reason) };
|
|
22987
23040
|
case "runtime-disposal-failed":
|
|
22988
|
-
return
|
|
22989
|
-
case "
|
|
22990
|
-
|
|
22991
|
-
|
|
22992
|
-
|
|
22993
|
-
|
|
22994
|
-
|
|
22995
|
-
|
|
22996
|
-
|
|
22997
|
-
|
|
22998
|
-
|
|
22999
|
-
|
|
23000
|
-
|
|
23001
|
-
|
|
23002
|
-
|
|
23003
|
-
|
|
23004
|
-
|
|
23005
|
-
|
|
23006
|
-
|
|
23007
|
-
|
|
23008
|
-
`${prefix} device-assertion result=denied`,
|
|
23009
|
-
`reason=${event.reason}`,
|
|
23010
|
-
event.audienceSize !== void 0 ? `audienceSize=${event.audienceSize}` : void 0
|
|
23011
|
-
].filter((part) => part !== void 0);
|
|
23012
|
-
return parts.join(" ");
|
|
23013
|
-
}
|
|
23041
|
+
return { ...base, taskId: event.taskId, runtimeId: event.runtimeId, stage: event.stage, reason: event.reason };
|
|
23042
|
+
case "device-assertion":
|
|
23043
|
+
return event.result === "issued" ? {
|
|
23044
|
+
...base,
|
|
23045
|
+
result: "issued",
|
|
23046
|
+
audience: event.audience,
|
|
23047
|
+
jti: event.jti,
|
|
23048
|
+
expiresAt: event.expiresAt
|
|
23049
|
+
} : { ...base, result: "denied", reason: event.reason, audienceSize: event.audienceSize };
|
|
23050
|
+
case "git-workspace":
|
|
23051
|
+
return {
|
|
23052
|
+
...base,
|
|
23053
|
+
taskId: event.taskId,
|
|
23054
|
+
workspaceId: event.workspaceId,
|
|
23055
|
+
phase: event.phase,
|
|
23056
|
+
headChanged: event.headChanged,
|
|
23057
|
+
commitsSinceBaseline: event.commitsSinceBaseline,
|
|
23058
|
+
dirty: event.dirty,
|
|
23059
|
+
errorCategory: stableGitErrorCategory(event.errorCategory)
|
|
23060
|
+
};
|
|
23014
23061
|
}
|
|
23015
23062
|
}
|
|
23016
|
-
function
|
|
23017
|
-
|
|
23018
|
-
const gitStatus = git ? [
|
|
23019
|
-
`git=${git.phase}`,
|
|
23020
|
-
git.commitsSinceBaseline !== void 0 ? `commits=${git.commitsSinceBaseline}` : void 0,
|
|
23021
|
-
git.dirty ? `dirty=${git.dirty.staged}/${git.dirty.unstaged}/${git.dirty.untracked}/${git.dirty.conflicted}` : void 0
|
|
23022
|
-
].filter((part) => part !== void 0).join(" ") : void 0;
|
|
23023
|
-
const parts = [
|
|
23024
|
-
task.taskId,
|
|
23025
|
-
task.state,
|
|
23026
|
-
task.runtime ? `runtime=${task.runtime}` : void 0,
|
|
23027
|
-
gitStatus,
|
|
23028
|
-
task.claimedRuntime !== void 0 ? `claimedRuntime=${task.claimedRuntime}` : void 0,
|
|
23029
|
-
`updatedAt=${task.updatedAt}`,
|
|
23030
|
-
task.sessionRef ? `sessionRef=${task.sessionRef}` : void 0,
|
|
23031
|
-
task.declined ? "declined=true" : void 0,
|
|
23032
|
-
task.summary ? `summary=${quote(task.summary)}` : void 0
|
|
23033
|
-
].filter((part) => Boolean(part));
|
|
23034
|
-
return parts.join(" ");
|
|
23063
|
+
function asRecord2(value) {
|
|
23064
|
+
return value !== null && typeof value === "object" ? value : {};
|
|
23035
23065
|
}
|
|
23036
|
-
function
|
|
23037
|
-
|
|
23038
|
-
return tasks.map(formatTaskLine);
|
|
23066
|
+
function str(value, fallback = "") {
|
|
23067
|
+
return typeof value === "string" ? value : fallback;
|
|
23039
23068
|
}
|
|
23040
|
-
function
|
|
23041
|
-
|
|
23042
|
-
return runtimes.map((r) => {
|
|
23043
|
-
if (!r.present) return `${r.id}: ${r.outcome}`;
|
|
23044
|
-
const caps = [];
|
|
23045
|
-
if (r.steer) caps.push("steer");
|
|
23046
|
-
if (r.resume) caps.push("resume");
|
|
23047
|
-
const parts = [
|
|
23048
|
-
"present",
|
|
23049
|
-
r.version ? `version=${r.version}` : void 0,
|
|
23050
|
-
r.authPresent !== void 0 ? `authPresent=${r.authPresent}` : void 0,
|
|
23051
|
-
`capabilities=${caps.length ? caps.join(",") : "(none)"}`,
|
|
23052
|
-
`modes=${r.permissionModes.length ? r.permissionModes.join(",") : "(none)"}`
|
|
23053
|
-
].filter((part) => Boolean(part));
|
|
23054
|
-
return `${r.id}: ${parts.join(" ")}`;
|
|
23055
|
-
});
|
|
23069
|
+
function num(value) {
|
|
23070
|
+
return typeof value === "number" ? value : void 0;
|
|
23056
23071
|
}
|
|
23057
|
-
function
|
|
23058
|
-
|
|
23059
|
-
const label = view.branding?.displayName ?? view.productName;
|
|
23060
|
-
lines.push(`product: ${label} (${view.productId})`);
|
|
23061
|
-
lines.push(
|
|
23062
|
-
`local-agent-release: ${view.localAgentRelease.version}${view.localAgentRelease.buildId ? ` buildId=${view.localAgentRelease.buildId}` : ""}`
|
|
23063
|
-
);
|
|
23064
|
-
if (view.branding?.supportUrl) lines.push(`support: ${view.branding.supportUrl}`);
|
|
23065
|
-
lines.push(`paired: ${view.paired ? "yes" : "no"}${view.deviceId ? ` deviceId=${view.deviceId}` : ""}`);
|
|
23066
|
-
lines.push(
|
|
23067
|
-
view.connection ? `connection: last-known=${view.connection.state} at=${view.connection.ts}` : "connection: unknown (no audit log yet \u2014 run `byok-agent start` at least once to begin observing)"
|
|
23068
|
-
);
|
|
23069
|
-
const runtimeSummary = view.runtimes.map((r) => `${r.id}=${r.present ? "present" : r.outcome}`).join(" ");
|
|
23070
|
-
lines.push(`runtimes: ${runtimeSummary || "(none configured)"}`);
|
|
23071
|
-
const c = view.taskCounts;
|
|
23072
|
-
lines.push(
|
|
23073
|
-
`tasks: total=${c.total} offered=${c.Offered} claimed=${c.Claimed} running=${c.Running} awaitApproval=${c.AwaitApproval} complete=${c.Complete} failed=${c.Failed} cancelled=${c.Cancelled}`
|
|
23074
|
-
);
|
|
23075
|
-
lines.push(`audit-log: ${view.auditLogPath} (${view.auditLogLineCount} event${view.auditLogLineCount === 1 ? "" : "s"})`);
|
|
23076
|
-
return lines;
|
|
23072
|
+
function bool(value, fallback) {
|
|
23073
|
+
return typeof value === "boolean" ? value : fallback;
|
|
23077
23074
|
}
|
|
23078
|
-
function
|
|
23079
|
-
const
|
|
23080
|
-
const
|
|
23081
|
-
|
|
23082
|
-
|
|
23083
|
-
|
|
23084
|
-
|
|
23085
|
-
|
|
23086
|
-
|
|
23087
|
-
|
|
23088
|
-
|
|
23089
|
-
|
|
23090
|
-
|
|
23091
|
-
|
|
23092
|
-
|
|
23093
|
-
|
|
23094
|
-
|
|
23075
|
+
function reconstructAgentEvent(raw) {
|
|
23076
|
+
const r = asRecord2(raw);
|
|
23077
|
+
const type = str(r.type);
|
|
23078
|
+
switch (type) {
|
|
23079
|
+
case "progress":
|
|
23080
|
+
return { type: "progress", text: placeholderFor(num(r.textSize)) };
|
|
23081
|
+
case "tool_use":
|
|
23082
|
+
return { type: "tool_use", tool: str(r.tool), input: placeholderFor(num(r.inputSize), bool(r.inputSpilled, false)) };
|
|
23083
|
+
case "tool_result":
|
|
23084
|
+
return { type: "tool_result", tool: str(r.tool), output: placeholderFor(num(r.outputSize), bool(r.outputSpilled, false)) };
|
|
23085
|
+
case "artifact":
|
|
23086
|
+
return { type: "artifact", name: str(r.name), contentType: str(r.contentType) };
|
|
23087
|
+
case "needs_approval":
|
|
23088
|
+
return { type: "needs_approval", summary: placeholderFor(num(r.summarySize)) };
|
|
23089
|
+
case "turn_end":
|
|
23090
|
+
return { type: "turn_end" };
|
|
23091
|
+
case "error":
|
|
23092
|
+
return { type: "error", message: placeholderFor(num(r.messageSize)) };
|
|
23093
|
+
case "usage":
|
|
23094
|
+
return {
|
|
23095
|
+
type: "usage",
|
|
23096
|
+
inputTokens: num(r.inputTokens),
|
|
23097
|
+
cachedInputTokens: num(r.cachedInputTokens),
|
|
23098
|
+
outputTokens: num(r.outputTokens),
|
|
23099
|
+
reasoningTokens: num(r.reasoningTokens),
|
|
23100
|
+
totalTokens: num(r.totalTokens)
|
|
23101
|
+
};
|
|
23102
|
+
default:
|
|
23103
|
+
return { type: "error", message: `[unrecognized audit event type: ${type || "(missing)"}]` };
|
|
23095
23104
|
}
|
|
23096
|
-
|
|
23097
|
-
|
|
23098
|
-
|
|
23099
|
-
|
|
23100
|
-
|
|
23105
|
+
}
|
|
23106
|
+
function reconstructDaemonEvent(raw) {
|
|
23107
|
+
const kind = str(raw.kind);
|
|
23108
|
+
const ts = str(raw.ts);
|
|
23109
|
+
switch (kind) {
|
|
23110
|
+
case "offered":
|
|
23111
|
+
return { kind: "offered", ts, taskId: str(raw.taskId), runtime: typeof raw.runtime === "string" ? raw.runtime : void 0 };
|
|
23112
|
+
case "claimed": {
|
|
23113
|
+
const claimedRuntime = typeof raw.claimedRuntime === "string" ? raw.claimedRuntime : void 0;
|
|
23114
|
+
return claimedRuntime === void 0 ? { kind: "claimed", ts, taskId: str(raw.taskId) } : { kind: "claimed", ts, taskId: str(raw.taskId), claimedRuntime };
|
|
23101
23115
|
}
|
|
23102
|
-
|
|
23103
|
-
|
|
23104
|
-
|
|
23105
|
-
|
|
23106
|
-
|
|
23107
|
-
|
|
23108
|
-
|
|
23116
|
+
case "started":
|
|
23117
|
+
return { kind: "started", ts, taskId: str(raw.taskId) };
|
|
23118
|
+
case "progress":
|
|
23119
|
+
return { kind: "progress", ts, taskId: str(raw.taskId), event: reconstructAgentEvent(raw.event) };
|
|
23120
|
+
case "artifact": {
|
|
23121
|
+
const blobRefPresent = raw.blobRef !== void 0 && raw.blobRef !== null;
|
|
23122
|
+
const blobRefRaw = asRecord2(raw.blobRef);
|
|
23123
|
+
const blobRef = blobRefPresent ? {
|
|
23124
|
+
blobId: str(blobRefRaw.blobId),
|
|
23125
|
+
contentHash: str(blobRefRaw.contentHash),
|
|
23126
|
+
size: num(blobRefRaw.size) ?? 0,
|
|
23127
|
+
contentType: str(blobRefRaw.contentType)
|
|
23128
|
+
} : void 0;
|
|
23129
|
+
const inlineSize = num(raw.inlineSize);
|
|
23130
|
+
return {
|
|
23131
|
+
kind: "artifact",
|
|
23132
|
+
ts,
|
|
23133
|
+
taskId: str(raw.taskId),
|
|
23134
|
+
name: str(raw.name),
|
|
23135
|
+
contentType: str(raw.contentType),
|
|
23136
|
+
inline: inlineSize === void 0 ? void 0 : placeholderFor(inlineSize),
|
|
23137
|
+
blobRef
|
|
23138
|
+
};
|
|
23109
23139
|
}
|
|
23110
|
-
|
|
23111
|
-
|
|
23112
|
-
|
|
23113
|
-
|
|
23114
|
-
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23140
|
+
case "awaiting-approval":
|
|
23141
|
+
return { kind: "awaiting-approval", ts, taskId: str(raw.taskId), summary: placeholderFor(num(raw.summarySize)) };
|
|
23142
|
+
case "completed":
|
|
23143
|
+
return {
|
|
23144
|
+
kind: "completed",
|
|
23145
|
+
ts,
|
|
23146
|
+
taskId: str(raw.taskId),
|
|
23147
|
+
summary: placeholderFor(num(raw.summarySize)),
|
|
23148
|
+
sessionRef: str(raw.sessionRef)
|
|
23149
|
+
};
|
|
23150
|
+
case "failed":
|
|
23151
|
+
return {
|
|
23152
|
+
kind: "failed",
|
|
23153
|
+
ts,
|
|
23154
|
+
taskId: str(raw.taskId),
|
|
23155
|
+
reason: placeholderFor(num(raw.reasonSize)),
|
|
23156
|
+
retryable: bool(raw.retryable, false),
|
|
23157
|
+
preClaim: typeof raw.preClaim === "boolean" ? raw.preClaim : void 0
|
|
23158
|
+
};
|
|
23159
|
+
case "cancelled": {
|
|
23160
|
+
const reasonSize = num(raw.reasonSize);
|
|
23161
|
+
return {
|
|
23162
|
+
kind: "cancelled",
|
|
23163
|
+
ts,
|
|
23164
|
+
taskId: str(raw.taskId),
|
|
23165
|
+
reason: reasonSize === void 0 ? void 0 : placeholderFor(reasonSize)
|
|
23166
|
+
};
|
|
23118
23167
|
}
|
|
23119
|
-
|
|
23120
|
-
|
|
23121
|
-
|
|
23122
|
-
|
|
23123
|
-
|
|
23124
|
-
|
|
23125
|
-
|
|
23126
|
-
|
|
23168
|
+
case "connection":
|
|
23169
|
+
return { kind: "connection", ts, state: str(raw.state) };
|
|
23170
|
+
case "paired":
|
|
23171
|
+
return { kind: "paired", ts, deviceId: str(raw.deviceId) };
|
|
23172
|
+
case "unpaired":
|
|
23173
|
+
return { kind: "unpaired", ts };
|
|
23174
|
+
case "runtimes-detected":
|
|
23175
|
+
return { kind: "runtimes-detected", ts, runtimes: Array.isArray(raw.runtimes) ? raw.runtimes : [] };
|
|
23176
|
+
case "shutdown-requested":
|
|
23177
|
+
return { kind: "shutdown-requested", ts, reason: str(raw.reason) };
|
|
23178
|
+
case "shutdown-complete":
|
|
23179
|
+
return { kind: "shutdown-complete", ts, reason: str(raw.reason), undeliveredOutboxCount: num(raw.undeliveredOutboxCount) };
|
|
23180
|
+
case "stale-approval-decision": {
|
|
23181
|
+
const reasonSize = num(raw.reasonSize);
|
|
23182
|
+
return {
|
|
23183
|
+
kind: "stale-approval-decision",
|
|
23184
|
+
ts,
|
|
23185
|
+
taskId: str(raw.taskId),
|
|
23186
|
+
decision: str(raw.decision),
|
|
23187
|
+
reason: reasonSize === void 0 ? void 0 : placeholderFor(reasonSize)
|
|
23188
|
+
};
|
|
23127
23189
|
}
|
|
23128
|
-
|
|
23129
|
-
|
|
23130
|
-
|
|
23131
|
-
|
|
23190
|
+
case "runtime-disposal-failed":
|
|
23191
|
+
return {
|
|
23192
|
+
kind: "runtime-disposal-failed",
|
|
23193
|
+
ts,
|
|
23194
|
+
taskId: str(raw.taskId),
|
|
23195
|
+
runtimeId: str(raw.runtimeId),
|
|
23196
|
+
stage: str(raw.stage),
|
|
23197
|
+
reason: str(raw.reason)
|
|
23198
|
+
};
|
|
23199
|
+
case "device-assertion": {
|
|
23200
|
+
if (raw.result === "issued") {
|
|
23201
|
+
return {
|
|
23202
|
+
kind: "device-assertion",
|
|
23203
|
+
ts,
|
|
23204
|
+
result: "issued",
|
|
23205
|
+
audience: typeof raw.audience === "string" ? raw.audience : "",
|
|
23206
|
+
jti: typeof raw.jti === "string" ? raw.jti : "",
|
|
23207
|
+
expiresAt: typeof raw.expiresAt === "string" ? raw.expiresAt : ""
|
|
23208
|
+
};
|
|
23209
|
+
}
|
|
23210
|
+
const audienceSize = num(raw.audienceSize);
|
|
23211
|
+
return {
|
|
23212
|
+
kind: "device-assertion",
|
|
23213
|
+
ts,
|
|
23214
|
+
result: "denied",
|
|
23215
|
+
reason: typeof raw.reason === "string" ? raw.reason : "",
|
|
23216
|
+
...audienceSize === void 0 ? {} : { audienceSize }
|
|
23217
|
+
};
|
|
23218
|
+
}
|
|
23219
|
+
case "git-workspace": {
|
|
23220
|
+
const commitsSinceBaseline = gitCount(raw.commitsSinceBaseline);
|
|
23221
|
+
const dirty = gitDirty(raw.dirty);
|
|
23222
|
+
return {
|
|
23223
|
+
kind: "git-workspace",
|
|
23224
|
+
ts,
|
|
23225
|
+
taskId: str(raw.taskId),
|
|
23226
|
+
workspaceId: str(raw.workspaceId),
|
|
23227
|
+
phase: str(raw.phase),
|
|
23228
|
+
headChanged: typeof raw.headChanged === "boolean" ? raw.headChanged : void 0,
|
|
23229
|
+
commitsSinceBaseline,
|
|
23230
|
+
dirty,
|
|
23231
|
+
errorCategory: stableGitErrorCategory(raw.errorCategory)
|
|
23232
|
+
};
|
|
23132
23233
|
}
|
|
23234
|
+
default:
|
|
23235
|
+
return void 0;
|
|
23133
23236
|
}
|
|
23134
|
-
return lines;
|
|
23135
23237
|
}
|
|
23136
|
-
function
|
|
23137
|
-
|
|
23138
|
-
|
|
23139
|
-
`${prefix}: ${toolset.id}`,
|
|
23140
|
-
`servers=${toolset.serverCount}`,
|
|
23141
|
-
`definitionRevision=${toolset.definitionRevision}`,
|
|
23142
|
-
`state=${observation?.state ?? "unobserved"}`,
|
|
23143
|
-
observation?.version === void 0 ? void 0 : `version=${quote(observation.version)}`,
|
|
23144
|
-
observation?.observedAt === void 0 ? void 0 : `observedAt=${observation.observedAt}`,
|
|
23145
|
-
observation?.reasonCode === void 0 ? void 0 : `reasonCode=${observation.reasonCode}`
|
|
23146
|
-
].filter((part) => part !== void 0).join(" ");
|
|
23147
|
-
}
|
|
23148
|
-
function formatToolsetsReloadReceiptLines(receipt) {
|
|
23149
|
-
return [
|
|
23150
|
-
`toolsets-reload: changed=${receipt.changed ? "yes" : "no"} previousRevision=${receipt.previousRevision} revision=${receipt.revision}`,
|
|
23151
|
-
...receipt.toolsets.map((toolset) => formatToolsetStatusLine("toolset", toolset))
|
|
23152
|
-
];
|
|
23153
|
-
}
|
|
23154
|
-
var SUMMARY_EXCERPT_MAX_LEN = 60;
|
|
23155
|
-
function summaryExcerpt(summary) {
|
|
23156
|
-
if (!summary) return "(no summary)";
|
|
23157
|
-
return summary.length > SUMMARY_EXCERPT_MAX_LEN ? `${summary.slice(0, SUMMARY_EXCERPT_MAX_LEN)}\u2026` : summary;
|
|
23158
|
-
}
|
|
23159
|
-
function formatAge(ms) {
|
|
23160
|
-
const totalSeconds = Math.floor(Math.max(0, ms) / 1e3);
|
|
23161
|
-
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
23162
|
-
const totalMinutes = Math.floor(totalSeconds / 60);
|
|
23163
|
-
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
23164
|
-
const totalHours = Math.floor(totalMinutes / 60);
|
|
23165
|
-
if (totalHours < 24) return `${totalHours}h`;
|
|
23166
|
-
const totalDays = Math.floor(totalHours / 24);
|
|
23167
|
-
return `${totalDays}d`;
|
|
23168
|
-
}
|
|
23169
|
-
function formatApprovalsListLines(approvals, nowMs) {
|
|
23170
|
-
if (approvals.length === 0) return ["(no pending approvals)"];
|
|
23171
|
-
return approvals.map((approval) => {
|
|
23172
|
-
const ageMs = nowMs - Date.parse(approval.createdAt);
|
|
23173
|
-
return `${approval.approvalId} taskId=${approval.taskId} age=${formatAge(ageMs)} summary=${quote(summaryExcerpt(approval.summary))}`;
|
|
23238
|
+
async function appendAuditEvent(storeDir, event) {
|
|
23239
|
+
await promises.mkdir(storeDir, { recursive: true, mode: AUDIT_STORE_DIR_MODE });
|
|
23240
|
+
await promises.chmod(storeDir, AUDIT_STORE_DIR_MODE).catch(() => {
|
|
23174
23241
|
});
|
|
23175
|
-
|
|
23176
|
-
|
|
23177
|
-
|
|
23178
|
-
|
|
23179
|
-
const log = deps.log ?? ((line) => console.log(line));
|
|
23180
|
-
const error = deps.error ?? ((line) => console.error(line));
|
|
23181
|
-
const connectControl = deps.connectControl ?? connectControlClient;
|
|
23182
|
-
const now = deps.now ?? (() => Date.now());
|
|
23183
|
-
const conn = await connectControl({ storeDir, productId });
|
|
23184
|
-
if (!conn.ok) {
|
|
23185
|
-
const message = `cannot list approvals: daemon not reachable (${conn.reason}) \u2014 is \`byok-agent start\` (or the installed service) running?`;
|
|
23186
|
-
error(message);
|
|
23187
|
-
throw new Error(message);
|
|
23188
|
-
}
|
|
23242
|
+
const filePath = auditLogPath(storeDir);
|
|
23243
|
+
const line = `${JSON.stringify(redactForAudit(event))}
|
|
23244
|
+
`;
|
|
23245
|
+
const handle = await promises.open(filePath, "a", AUDIT_LOG_MODE);
|
|
23189
23246
|
try {
|
|
23190
|
-
|
|
23191
|
-
|
|
23192
|
-
} catch (err) {
|
|
23193
|
-
if (err instanceof ControlError) {
|
|
23194
|
-
error(`approvals list failed: ${err.message}`);
|
|
23195
|
-
} else {
|
|
23196
|
-
error(`approvals list failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23197
|
-
}
|
|
23198
|
-
throw err;
|
|
23247
|
+
await handle.chmod(AUDIT_LOG_MODE);
|
|
23248
|
+
await handle.appendFile(line, "utf8");
|
|
23199
23249
|
} finally {
|
|
23200
|
-
|
|
23250
|
+
await handle.close();
|
|
23201
23251
|
}
|
|
23252
|
+
await rotateIfNeeded(filePath);
|
|
23202
23253
|
}
|
|
23203
|
-
|
|
23204
|
-
|
|
23205
|
-
|
|
23206
|
-
return
|
|
23254
|
+
var TERMINAL_EVENT_KINDS = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
23255
|
+
function eventTaskId(event) {
|
|
23256
|
+
if (event.kind === "git-workspace") return void 0;
|
|
23257
|
+
return "taskId" in event ? event.taskId : void 0;
|
|
23207
23258
|
}
|
|
23208
|
-
|
|
23209
|
-
|
|
23259
|
+
function compactPreservingLiveTasks(lines, targetLines) {
|
|
23260
|
+
if (lines.length <= targetLines) return [...lines];
|
|
23261
|
+
const cutIndex = lines.length - targetLines;
|
|
23262
|
+
const hasTerminalEvent = /* @__PURE__ */ new Set();
|
|
23263
|
+
const lastIndexForTask = /* @__PURE__ */ new Map();
|
|
23264
|
+
for (let i = 0; i < lines.length; i++) {
|
|
23265
|
+
const line = lines[i];
|
|
23266
|
+
if (line === void 0) continue;
|
|
23267
|
+
const event = parseAuditLine(line);
|
|
23268
|
+
if (!event) continue;
|
|
23269
|
+
const taskId = eventTaskId(event);
|
|
23270
|
+
if (taskId === void 0) continue;
|
|
23271
|
+
lastIndexForTask.set(taskId, i);
|
|
23272
|
+
if (TERMINAL_EVENT_KINDS.has(event.kind)) hasTerminalEvent.add(taskId);
|
|
23273
|
+
}
|
|
23274
|
+
let anchorIndices = [];
|
|
23275
|
+
for (const [taskId, lastIndex] of lastIndexForTask) {
|
|
23276
|
+
if (hasTerminalEvent.has(taskId)) continue;
|
|
23277
|
+
if (lastIndex < cutIndex) anchorIndices.push(lastIndex);
|
|
23278
|
+
}
|
|
23279
|
+
anchorIndices.sort((a, b) => a - b);
|
|
23280
|
+
if (anchorIndices.length > MAX_LIVE_TASK_ANCHORS) {
|
|
23281
|
+
const totalCandidates = anchorIndices.length;
|
|
23282
|
+
const droppedCount = totalCandidates - MAX_LIVE_TASK_ANCHORS;
|
|
23283
|
+
anchorIndices = anchorIndices.slice(-MAX_LIVE_TASK_ANCHORS);
|
|
23284
|
+
console.warn(
|
|
23285
|
+
`[byok/client] audit log rotation: ${totalCandidates} non-terminal task lifecycle anchors exceeded MAX_LIVE_TASK_ANCHORS (${MAX_LIVE_TASK_ANCHORS}) \u2014 dropped the oldest ${droppedCount}, kept the most recently-touched ${MAX_LIVE_TASK_ANCHORS}; dropped tasks will stop appearing in tasks/status once their own events age out of the retained tail`
|
|
23286
|
+
);
|
|
23287
|
+
}
|
|
23288
|
+
return [...anchorIndices.map((i) => lines[i]), ...lines.slice(cutIndex)].filter((l) => l !== void 0);
|
|
23210
23289
|
}
|
|
23211
|
-
async function
|
|
23212
|
-
|
|
23213
|
-
|
|
23214
|
-
|
|
23215
|
-
|
|
23216
|
-
|
|
23217
|
-
if (!conn.ok) {
|
|
23218
|
-
const message = `cannot ${decision} approvalId=${approvalId}: daemon not reachable (${conn.reason}) \u2014 is \`byok-agent start\` (or the installed service) running?`;
|
|
23219
|
-
error(message);
|
|
23220
|
-
throw new Error(message);
|
|
23290
|
+
async function rotateIfNeeded(filePath) {
|
|
23291
|
+
let size;
|
|
23292
|
+
try {
|
|
23293
|
+
size = (await promises.stat(filePath)).size;
|
|
23294
|
+
} catch {
|
|
23295
|
+
return;
|
|
23221
23296
|
}
|
|
23297
|
+
if (size <= MAX_AUDIT_LOG_BYTES) return;
|
|
23298
|
+
const lines = await readCompleteLines(filePath);
|
|
23299
|
+
const kept = compactPreservingLiveTasks(lines, AUDIT_LOG_TRIM_TARGET_LINES);
|
|
23300
|
+
const content = kept.length > 0 ? `${kept.join("\n")}
|
|
23301
|
+
` : "";
|
|
23302
|
+
await atomicWriteFile(filePath, content, { mode: AUDIT_LOG_MODE });
|
|
23303
|
+
}
|
|
23304
|
+
function createAuditAppender(storeDir, onError = () => {
|
|
23305
|
+
}) {
|
|
23306
|
+
let chain = Promise.resolve();
|
|
23307
|
+
return (event) => {
|
|
23308
|
+
chain = chain.then(() => appendAuditEvent(storeDir, event)).catch(onError);
|
|
23309
|
+
};
|
|
23310
|
+
}
|
|
23311
|
+
function isAuditRecordShaped(value) {
|
|
23312
|
+
return typeof value === "object" && value !== null && typeof value.kind === "string" && typeof value.ts === "string";
|
|
23313
|
+
}
|
|
23314
|
+
function parseAuditLine(line) {
|
|
23315
|
+
const trimmed = line.trim();
|
|
23316
|
+
if (!trimmed) return void 0;
|
|
23222
23317
|
try {
|
|
23223
|
-
|
|
23224
|
-
|
|
23318
|
+
const parsed = JSON.parse(trimmed);
|
|
23319
|
+
if (!isAuditRecordShaped(parsed)) return void 0;
|
|
23320
|
+
return reconstructDaemonEvent(parsed);
|
|
23321
|
+
} catch {
|
|
23322
|
+
return void 0;
|
|
23323
|
+
}
|
|
23324
|
+
}
|
|
23325
|
+
function completeLines(raw) {
|
|
23326
|
+
const parts = raw.split("\n");
|
|
23327
|
+
parts.pop();
|
|
23328
|
+
return parts;
|
|
23329
|
+
}
|
|
23330
|
+
async function readCompleteLines(filePath) {
|
|
23331
|
+
let raw;
|
|
23332
|
+
try {
|
|
23333
|
+
raw = await promises.readFile(filePath, "utf8");
|
|
23225
23334
|
} catch (err) {
|
|
23226
|
-
if (err
|
|
23227
|
-
error(`${err.message} \u2014 run \`byok-agent approvals\` to see pending approvalIds`);
|
|
23228
|
-
} else {
|
|
23229
|
-
error(`${decision} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23230
|
-
}
|
|
23335
|
+
if (err.code === "ENOENT") return [];
|
|
23231
23336
|
throw err;
|
|
23232
|
-
} finally {
|
|
23233
|
-
conn.client.close();
|
|
23234
23337
|
}
|
|
23338
|
+
return completeLines(raw);
|
|
23235
23339
|
}
|
|
23236
|
-
|
|
23237
|
-
|
|
23238
|
-
|
|
23239
|
-
const
|
|
23240
|
-
|
|
23241
|
-
|
|
23242
|
-
log(`paired: deviceId=${result2.deviceId}`);
|
|
23243
|
-
return;
|
|
23340
|
+
async function readAuditEvents(storeDir) {
|
|
23341
|
+
const lines = await readCompleteLines(auditLogPath(storeDir));
|
|
23342
|
+
const events = [];
|
|
23343
|
+
for (const line of lines) {
|
|
23344
|
+
const event = parseAuditLine(line);
|
|
23345
|
+
if (event) events.push(event);
|
|
23244
23346
|
}
|
|
23245
|
-
|
|
23246
|
-
|
|
23247
|
-
|
|
23347
|
+
return events;
|
|
23348
|
+
}
|
|
23349
|
+
function delay3(ms, signal) {
|
|
23350
|
+
if (signal.aborted) return Promise.resolve();
|
|
23351
|
+
return new Promise((resolve) => {
|
|
23352
|
+
const onAbort = () => {
|
|
23353
|
+
clearTimeout(timer);
|
|
23354
|
+
resolve();
|
|
23355
|
+
};
|
|
23356
|
+
const timer = setTimeout(() => {
|
|
23357
|
+
signal.removeEventListener("abort", onAbort);
|
|
23358
|
+
resolve();
|
|
23359
|
+
}, ms);
|
|
23360
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
23361
|
+
});
|
|
23362
|
+
}
|
|
23363
|
+
async function followAuditLog(filePath, onEvent, options) {
|
|
23364
|
+
const { signal, pollIntervalMs = 200, fromEnd = true } = options;
|
|
23365
|
+
let offset = 0;
|
|
23366
|
+
let pending = Buffer.alloc(0);
|
|
23367
|
+
let lastFileIdentity;
|
|
23368
|
+
if (fromEnd) {
|
|
23248
23369
|
try {
|
|
23249
|
-
const
|
|
23250
|
-
|
|
23251
|
-
|
|
23252
|
-
}
|
|
23253
|
-
|
|
23370
|
+
const st = await promises.stat(filePath);
|
|
23371
|
+
offset = st.size;
|
|
23372
|
+
lastFileIdentity = `${st.dev}:${st.ino}`;
|
|
23373
|
+
} catch (err) {
|
|
23374
|
+
if (err.code !== "ENOENT") throw err;
|
|
23375
|
+
offset = 0;
|
|
23254
23376
|
}
|
|
23255
23377
|
}
|
|
23256
|
-
|
|
23257
|
-
|
|
23258
|
-
|
|
23259
|
-
|
|
23260
|
-
|
|
23261
|
-
|
|
23262
|
-
|
|
23263
|
-
constructor() {
|
|
23264
|
-
super("doctor --fix requires explicit --yes confirmation");
|
|
23265
|
-
this.name = "DoctorConfirmationRequiredError";
|
|
23266
|
-
}
|
|
23267
|
-
};
|
|
23268
|
-
var DoctorDaemonRunningError = class extends Error {
|
|
23269
|
-
constructor() {
|
|
23270
|
-
super("doctor --fix refuses while the daemon control socket is reachable; stop the daemon first");
|
|
23271
|
-
this.name = "DoctorDaemonRunningError";
|
|
23272
|
-
}
|
|
23273
|
-
};
|
|
23274
|
-
function formatDoctorLines(snapshot, fixResult) {
|
|
23275
|
-
const lines = [
|
|
23276
|
-
`doctor: productHash=${snapshot.product.nameHash} generatedAt=${snapshot.generatedAt}`,
|
|
23277
|
-
`system: node=${snapshot.system.node} platform=${snapshot.system.platform} arch=${snapshot.system.arch} sqlite=${snapshot.system.sqliteAvailable ? "available" : "unavailable"}`
|
|
23278
|
-
];
|
|
23279
|
-
for (const check of snapshot.checks) lines.push(`check ${check.id}: ${check.status} (${check.summary})`);
|
|
23280
|
-
if (snapshot.health.status === "corrupt" || snapshot.health.status === "unavailable") {
|
|
23281
|
-
lines.push(`health-detail: ${snapshot.health.reason}; bytes=${snapshot.health.sizeBytes ?? "unknown"}`);
|
|
23282
|
-
}
|
|
23283
|
-
if (snapshot.quarantine.truncated) {
|
|
23284
|
-
lines.push(`quarantine-detail: inventory truncated after ${snapshot.quarantine.scannedCount} scanned entries`);
|
|
23285
|
-
}
|
|
23286
|
-
if (fixResult?.status === "quarantined") {
|
|
23287
|
-
lines.push(
|
|
23288
|
-
`fix: quarantined ${fixResult.sizeBytes} bytes as ${fixResult.evidenceName}; manifest=${fixResult.manifestName}; sha256=${fixResult.sha256}`
|
|
23289
|
-
);
|
|
23290
|
-
} else if (fixResult) {
|
|
23291
|
-
lines.push(`fix: no change (${fixResult.reason})`);
|
|
23292
|
-
}
|
|
23293
|
-
return lines;
|
|
23294
|
-
}
|
|
23295
|
-
async function runDoctorCommand(config, options = {}) {
|
|
23296
|
-
if (options.repair !== void 0) {
|
|
23297
|
-
if (options.fix || options.repair !== "restore-enrollment-metadata") {
|
|
23298
|
-
throw new Error("doctor requires one supported repair action; --fix cannot be combined with --repair");
|
|
23378
|
+
async function readNewBytes() {
|
|
23379
|
+
let handle;
|
|
23380
|
+
try {
|
|
23381
|
+
handle = await promises.open(filePath, "r");
|
|
23382
|
+
} catch (err) {
|
|
23383
|
+
if (err.code === "ENOENT") return Buffer.alloc(0);
|
|
23384
|
+
throw err;
|
|
23299
23385
|
}
|
|
23300
|
-
const log2 = options.log ?? ((line) => console.log(line));
|
|
23301
23386
|
try {
|
|
23302
|
-
const
|
|
23303
|
-
|
|
23304
|
-
|
|
23305
|
-
|
|
23306
|
-
|
|
23307
|
-
|
|
23308
|
-
|
|
23309
|
-
|
|
23310
|
-
if (options.json && error instanceof DeviceMetadataRepairError) {
|
|
23311
|
-
log2(JSON.stringify({ repair: { action: "restore-enrollment-metadata", scope: "device", status: "failed", code: error.code } }, null, 2));
|
|
23387
|
+
const st = await handle.stat();
|
|
23388
|
+
const identity = `${st.dev}:${st.ino}`;
|
|
23389
|
+
if (lastFileIdentity !== void 0 && identity !== lastFileIdentity) {
|
|
23390
|
+
offset = 0;
|
|
23391
|
+
pending = Buffer.alloc(0);
|
|
23392
|
+
} else if (st.size < offset) {
|
|
23393
|
+
offset = 0;
|
|
23394
|
+
pending = Buffer.alloc(0);
|
|
23312
23395
|
}
|
|
23313
|
-
|
|
23396
|
+
lastFileIdentity = identity;
|
|
23397
|
+
if (st.size <= offset) return Buffer.alloc(0);
|
|
23398
|
+
const length = st.size - offset;
|
|
23399
|
+
const buffer = Buffer.alloc(length);
|
|
23400
|
+
const { bytesRead } = await handle.read(buffer, 0, length, offset);
|
|
23401
|
+
offset += bytesRead;
|
|
23402
|
+
return buffer.subarray(0, bytesRead);
|
|
23403
|
+
} finally {
|
|
23404
|
+
await handle.close();
|
|
23314
23405
|
}
|
|
23315
23406
|
}
|
|
23316
|
-
|
|
23317
|
-
|
|
23318
|
-
|
|
23319
|
-
|
|
23320
|
-
|
|
23321
|
-
|
|
23322
|
-
|
|
23323
|
-
|
|
23324
|
-
|
|
23325
|
-
|
|
23326
|
-
|
|
23327
|
-
|
|
23328
|
-
|
|
23329
|
-
|
|
23330
|
-
|
|
23331
|
-
|
|
23407
|
+
while (!signal.aborted) {
|
|
23408
|
+
const chunk = await readNewBytes();
|
|
23409
|
+
if (chunk.length > 0) {
|
|
23410
|
+
const combined = pending.length > 0 ? Buffer.concat([pending, chunk]) : chunk;
|
|
23411
|
+
const lastNewline = combined.lastIndexOf(10);
|
|
23412
|
+
if (lastNewline === -1) {
|
|
23413
|
+
pending = combined;
|
|
23414
|
+
} else {
|
|
23415
|
+
const completeText = combined.subarray(0, lastNewline + 1).toString("utf8");
|
|
23416
|
+
pending = Buffer.from(combined.subarray(lastNewline + 1));
|
|
23417
|
+
for (const line of completeText.split("\n")) {
|
|
23418
|
+
if (!line) continue;
|
|
23419
|
+
const event = parseAuditLine(line);
|
|
23420
|
+
if (event) onEvent(event);
|
|
23421
|
+
}
|
|
23422
|
+
}
|
|
23423
|
+
}
|
|
23424
|
+
if (signal.aborted) break;
|
|
23425
|
+
await delay3(pollIntervalMs, signal);
|
|
23332
23426
|
}
|
|
23333
|
-
for (const line of formatDoctorLines(snapshot, fixResult)) log(line);
|
|
23334
23427
|
}
|
|
23335
23428
|
|
|
23336
|
-
// src/
|
|
23337
|
-
|
|
23338
|
-
|
|
23339
|
-
|
|
23340
|
-
|
|
23341
|
-
|
|
23342
|
-
|
|
23343
|
-
|
|
23344
|
-
|
|
23345
|
-
|
|
23346
|
-
|
|
23347
|
-
|
|
23348
|
-
|
|
23349
|
-
|
|
23350
|
-
|
|
23351
|
-
|
|
23352
|
-
|
|
23353
|
-
|
|
23354
|
-
|
|
23355
|
-
|
|
23356
|
-
|
|
23357
|
-
|
|
23358
|
-
|
|
23359
|
-
|
|
23360
|
-
return definition;
|
|
23361
|
-
}
|
|
23362
|
-
function buildLifecycle(config, configPath, rest, deps) {
|
|
23363
|
-
return deps.lifecycle ?? createServiceLifecycle(buildServiceDefinition(config, configPath, rest));
|
|
23364
|
-
}
|
|
23365
|
-
function serviceNameFor(config, rest) {
|
|
23366
|
-
return argValue(rest, "--name") ?? config.productId;
|
|
23367
|
-
}
|
|
23368
|
-
async function runInstallCommand(config, configPath, rest, deps = {}) {
|
|
23369
|
-
const log = deps.log ?? ((line) => console.log(line));
|
|
23370
|
-
const lifecycle = buildLifecycle(config, configPath, rest, deps);
|
|
23371
|
-
await lifecycle.install();
|
|
23372
|
-
log(`service installed and started: ${serviceNameFor(config, rest)}`);
|
|
23373
|
-
}
|
|
23374
|
-
async function runUninstallCommand(config, configPath, rest, deps = {}) {
|
|
23375
|
-
const log = deps.log ?? ((line) => console.log(line));
|
|
23376
|
-
const lifecycle = buildLifecycle(config, configPath, rest, deps);
|
|
23377
|
-
await lifecycle.uninstall();
|
|
23378
|
-
log(`service uninstalled: ${serviceNameFor(config, rest)}`);
|
|
23379
|
-
}
|
|
23380
|
-
async function runServiceStartCommand(config, configPath, rest, deps = {}) {
|
|
23381
|
-
const log = deps.log ?? ((line) => console.log(line));
|
|
23382
|
-
const lifecycle = buildLifecycle(config, configPath, rest, deps);
|
|
23383
|
-
await lifecycle.start();
|
|
23384
|
-
log(`service started: ${serviceNameFor(config, rest)}`);
|
|
23385
|
-
}
|
|
23386
|
-
async function runServiceStopCommand(config, configPath, rest, deps = {}) {
|
|
23387
|
-
const log = deps.log ?? ((line) => console.log(line));
|
|
23388
|
-
const lifecycle = buildLifecycle(config, configPath, rest, deps);
|
|
23389
|
-
await lifecycle.stop();
|
|
23390
|
-
log(`service stopped: ${serviceNameFor(config, rest)}`);
|
|
23391
|
-
}
|
|
23392
|
-
async function runServiceStatusCommand(config, configPath, rest, deps = {}) {
|
|
23393
|
-
const log = deps.log ?? ((line) => console.log(line));
|
|
23394
|
-
const lifecycle = buildLifecycle(config, configPath, rest, deps);
|
|
23395
|
-
const status = await lifecycle.status();
|
|
23396
|
-
log(`installed: ${status.installed ? "yes" : "no"}`);
|
|
23397
|
-
const runningLabel = status.running ? "yes" : status.determinate ? "no" : "unknown (could not query the service manager)";
|
|
23398
|
-
log(`running: ${runningLabel}`);
|
|
23399
|
-
log(`detail: ${status.detail.trim() || "(none)"}`);
|
|
23400
|
-
}
|
|
23401
|
-
function auditLogPath(storeDir) {
|
|
23402
|
-
return path14__default.join(storeDir, "audit.jsonl");
|
|
23429
|
+
// src/diagnostics/support-bundle.ts
|
|
23430
|
+
var MAX_SUPPORT_AUDIT_TAIL_BYTES = 256 * 1024;
|
|
23431
|
+
var MAX_SUPPORT_AUDIT_FACTS = 200;
|
|
23432
|
+
var AUDIT_KINDS = /* @__PURE__ */ new Set([
|
|
23433
|
+
"artifact",
|
|
23434
|
+
"awaiting-approval",
|
|
23435
|
+
"cancelled",
|
|
23436
|
+
"claimed",
|
|
23437
|
+
"completed",
|
|
23438
|
+
"connection",
|
|
23439
|
+
"failed",
|
|
23440
|
+
"git-workspace",
|
|
23441
|
+
"offered",
|
|
23442
|
+
"paired",
|
|
23443
|
+
"progress",
|
|
23444
|
+
"runtimes-detected",
|
|
23445
|
+
"shutdown-complete",
|
|
23446
|
+
"shutdown-requested",
|
|
23447
|
+
"stale-approval-decision",
|
|
23448
|
+
"started",
|
|
23449
|
+
"unpaired"
|
|
23450
|
+
]);
|
|
23451
|
+
function isAuditKind(value) {
|
|
23452
|
+
return typeof value === "string" && AUDIT_KINDS.has(value);
|
|
23403
23453
|
}
|
|
23404
|
-
|
|
23405
|
-
|
|
23406
|
-
var MAX_AUDIT_LOG_BYTES = 10 * 1024 * 1024;
|
|
23407
|
-
var AUDIT_LOG_TRIM_TARGET_LINES = 5e3;
|
|
23408
|
-
var MAX_LIVE_TASK_ANCHORS = 500;
|
|
23409
|
-
function byteSize(text) {
|
|
23410
|
-
return text === void 0 ? void 0 : Buffer.byteLength(text, "utf8");
|
|
23454
|
+
function sameFileState6(left, right) {
|
|
23455
|
+
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
|
|
23411
23456
|
}
|
|
23412
|
-
function
|
|
23413
|
-
|
|
23457
|
+
async function recentAuditFacts(storeDir) {
|
|
23458
|
+
const filePath = auditLogPath(storeDir);
|
|
23459
|
+
let pathStat;
|
|
23414
23460
|
try {
|
|
23415
|
-
|
|
23416
|
-
|
|
23417
|
-
|
|
23418
|
-
|
|
23461
|
+
pathStat = await promises.lstat(filePath, { bigint: true });
|
|
23462
|
+
if (!pathStat.isFile() || pathStat.isSymbolicLink()) {
|
|
23463
|
+
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
23464
|
+
}
|
|
23465
|
+
} catch (err) {
|
|
23466
|
+
if (err.code === "ENOENT") {
|
|
23467
|
+
return { status: "missing", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
23468
|
+
}
|
|
23469
|
+
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
23419
23470
|
}
|
|
23420
|
-
|
|
23421
|
-
|
|
23422
|
-
|
|
23423
|
-
|
|
23424
|
-
|
|
23425
|
-
|
|
23426
|
-
|
|
23427
|
-
|
|
23428
|
-
}
|
|
23429
|
-
|
|
23430
|
-
|
|
23431
|
-
}
|
|
23432
|
-
function gitDirty(value) {
|
|
23433
|
-
const dirty = asRecord2(value);
|
|
23434
|
-
const staged = gitCount(dirty.staged);
|
|
23435
|
-
const unstaged = gitCount(dirty.unstaged);
|
|
23436
|
-
const untracked = gitCount(dirty.untracked);
|
|
23437
|
-
const conflicted = gitCount(dirty.conflicted);
|
|
23438
|
-
return staged !== void 0 && unstaged !== void 0 && untracked !== void 0 && conflicted !== void 0 ? { staged, unstaged, untracked, conflicted } : void 0;
|
|
23439
|
-
}
|
|
23440
|
-
function redactAgentEvent(event) {
|
|
23441
|
-
switch (event.type) {
|
|
23442
|
-
case "progress":
|
|
23443
|
-
return { type: "progress", textSize: byteSize(event.text) };
|
|
23444
|
-
case "tool_use":
|
|
23445
|
-
return event.spill !== void 0 ? { type: "tool_use", tool: event.tool, inputSize: event.spill.totalBytes, inputSpilled: true } : { type: "tool_use", tool: event.tool, inputSize: valueByteSize(event.input) };
|
|
23446
|
-
case "tool_result":
|
|
23447
|
-
return event.spill !== void 0 ? { type: "tool_result", tool: event.tool, outputSize: event.spill.totalBytes, outputSpilled: true } : { type: "tool_result", tool: event.tool, outputSize: valueByteSize(event.output) };
|
|
23448
|
-
case "artifact":
|
|
23449
|
-
return { type: "artifact", name: event.name, contentType: event.contentType };
|
|
23450
|
-
case "needs_approval":
|
|
23451
|
-
return { type: "needs_approval", summarySize: byteSize(event.summary) };
|
|
23452
|
-
case "turn_end":
|
|
23453
|
-
return { type: "turn_end" };
|
|
23454
|
-
case "error":
|
|
23455
|
-
return { type: "error", messageSize: byteSize(event.message) };
|
|
23456
|
-
case "usage":
|
|
23457
|
-
return {
|
|
23458
|
-
type: "usage",
|
|
23459
|
-
inputTokens: event.inputTokens,
|
|
23460
|
-
cachedInputTokens: event.cachedInputTokens,
|
|
23461
|
-
outputTokens: event.outputTokens,
|
|
23462
|
-
reasoningTokens: event.reasoningTokens,
|
|
23463
|
-
totalTokens: event.totalTokens
|
|
23464
|
-
};
|
|
23471
|
+
let handle;
|
|
23472
|
+
try {
|
|
23473
|
+
handle = await promises.open(
|
|
23474
|
+
filePath,
|
|
23475
|
+
constants.O_RDONLY | constants.O_NONBLOCK | (constants.O_NOFOLLOW ?? 0)
|
|
23476
|
+
);
|
|
23477
|
+
} catch (err) {
|
|
23478
|
+
if (err.code === "ENOENT") {
|
|
23479
|
+
return { status: "missing", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
23480
|
+
}
|
|
23481
|
+
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
23465
23482
|
}
|
|
23466
|
-
|
|
23467
|
-
|
|
23468
|
-
|
|
23469
|
-
|
|
23470
|
-
|
|
23471
|
-
|
|
23472
|
-
|
|
23473
|
-
|
|
23474
|
-
|
|
23475
|
-
|
|
23476
|
-
|
|
23477
|
-
|
|
23478
|
-
|
|
23479
|
-
|
|
23480
|
-
|
|
23481
|
-
|
|
23482
|
-
|
|
23483
|
-
|
|
23484
|
-
|
|
23485
|
-
|
|
23486
|
-
|
|
23487
|
-
|
|
23488
|
-
|
|
23489
|
-
|
|
23490
|
-
|
|
23491
|
-
|
|
23492
|
-
|
|
23493
|
-
|
|
23494
|
-
|
|
23495
|
-
|
|
23496
|
-
|
|
23497
|
-
|
|
23498
|
-
|
|
23499
|
-
|
|
23500
|
-
case "failed":
|
|
23501
|
-
return {
|
|
23502
|
-
...base,
|
|
23503
|
-
taskId: event.taskId,
|
|
23504
|
-
reasonSize: byteSize(event.reason),
|
|
23505
|
-
retryable: event.retryable,
|
|
23506
|
-
preClaim: event.preClaim
|
|
23507
|
-
};
|
|
23508
|
-
case "cancelled":
|
|
23509
|
-
return { ...base, taskId: event.taskId, reasonSize: byteSize(event.reason) };
|
|
23510
|
-
case "connection":
|
|
23511
|
-
return { ...base, state: event.state };
|
|
23512
|
-
case "paired":
|
|
23513
|
-
return { ...base, deviceId: event.deviceId };
|
|
23514
|
-
case "unpaired":
|
|
23515
|
-
return { ...base };
|
|
23516
|
-
case "runtimes-detected":
|
|
23517
|
-
return { ...base, runtimes: event.runtimes };
|
|
23518
|
-
case "shutdown-requested":
|
|
23519
|
-
return { ...base, reason: event.reason };
|
|
23520
|
-
case "shutdown-complete":
|
|
23521
|
-
return { ...base, reason: event.reason, undeliveredOutboxCount: event.undeliveredOutboxCount };
|
|
23522
|
-
case "stale-approval-decision":
|
|
23523
|
-
return { ...base, taskId: event.taskId, decision: event.decision, reasonSize: byteSize(event.reason) };
|
|
23524
|
-
case "runtime-disposal-failed":
|
|
23525
|
-
return { ...base, taskId: event.taskId, runtimeId: event.runtimeId, stage: event.stage, reason: event.reason };
|
|
23526
|
-
case "device-assertion":
|
|
23527
|
-
return event.result === "issued" ? {
|
|
23528
|
-
...base,
|
|
23529
|
-
result: "issued",
|
|
23530
|
-
audience: event.audience,
|
|
23531
|
-
jti: event.jti,
|
|
23532
|
-
expiresAt: event.expiresAt
|
|
23533
|
-
} : { ...base, result: "denied", reason: event.reason, audienceSize: event.audienceSize };
|
|
23534
|
-
case "git-workspace":
|
|
23535
|
-
return {
|
|
23536
|
-
...base,
|
|
23537
|
-
taskId: event.taskId,
|
|
23538
|
-
workspaceId: event.workspaceId,
|
|
23539
|
-
phase: event.phase,
|
|
23540
|
-
headChanged: event.headChanged,
|
|
23541
|
-
commitsSinceBaseline: event.commitsSinceBaseline,
|
|
23542
|
-
dirty: event.dirty,
|
|
23543
|
-
errorCategory: stableGitErrorCategory2(event.errorCategory)
|
|
23544
|
-
};
|
|
23483
|
+
try {
|
|
23484
|
+
const stat = await handle.stat({ bigint: true });
|
|
23485
|
+
const namedAfterOpen = await promises.lstat(filePath, { bigint: true });
|
|
23486
|
+
if (!stat.isFile() || !namedAfterOpen.isFile() || namedAfterOpen.isSymbolicLink() || !sameFileState6(pathStat, stat) || !sameFileState6(stat, namedAfterOpen)) {
|
|
23487
|
+
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
23488
|
+
}
|
|
23489
|
+
const sourceSize = Number(stat.size);
|
|
23490
|
+
const length = Math.min(sourceSize, MAX_SUPPORT_AUDIT_TAIL_BYTES);
|
|
23491
|
+
const start = Math.max(0, sourceSize - length);
|
|
23492
|
+
const buffer = Buffer.alloc(length);
|
|
23493
|
+
const { bytesRead } = await handle.read(buffer, 0, length, start);
|
|
23494
|
+
const afterRead = await handle.stat({ bigint: true });
|
|
23495
|
+
const namedAfterRead = await promises.lstat(filePath, { bigint: true });
|
|
23496
|
+
if (bytesRead !== length || namedAfterRead.isSymbolicLink() || !sameFileState6(stat, afterRead) || !sameFileState6(afterRead, namedAfterRead)) {
|
|
23497
|
+
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
23498
|
+
}
|
|
23499
|
+
let text = buffer.subarray(0, bytesRead).toString("utf8");
|
|
23500
|
+
if (start > 0) text = text.slice(Math.max(0, text.indexOf("\n") + 1));
|
|
23501
|
+
const lines = text.split("\n").filter(Boolean).slice(-MAX_SUPPORT_AUDIT_FACTS);
|
|
23502
|
+
const facts = [];
|
|
23503
|
+
for (const line of lines) {
|
|
23504
|
+
try {
|
|
23505
|
+
const value = JSON.parse(line);
|
|
23506
|
+
if (isAuditKind(value.kind) && typeof value.ts === "string" && Number.isFinite(Date.parse(value.ts))) {
|
|
23507
|
+
facts.push({ kind: value.kind, ts: new Date(Date.parse(value.ts)).toISOString() });
|
|
23508
|
+
}
|
|
23509
|
+
} catch {
|
|
23510
|
+
}
|
|
23511
|
+
}
|
|
23512
|
+
return { status: "available", included: facts.length, sourceBytesRead: bytesRead, sourceTruncated: sourceSize > bytesRead, facts };
|
|
23513
|
+
} catch {
|
|
23514
|
+
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
23515
|
+
} finally {
|
|
23516
|
+
await handle.close();
|
|
23545
23517
|
}
|
|
23546
23518
|
}
|
|
23547
|
-
function
|
|
23548
|
-
|
|
23519
|
+
function projectControl(control) {
|
|
23520
|
+
if (control.status === "offline") return { status: "offline" };
|
|
23521
|
+
const transport = ["connecting", "open", "closed", "revoked"].includes(control.transport) ? control.transport : "unavailable";
|
|
23522
|
+
const operationalHealth = control.operationalHealth.availability === "available" ? {
|
|
23523
|
+
availability: "available",
|
|
23524
|
+
state: control.operationalHealth.state,
|
|
23525
|
+
failureCount: control.operationalHealth.failureCount,
|
|
23526
|
+
windowMs: control.operationalHealth.windowMs,
|
|
23527
|
+
failureThreshold: control.operationalHealth.failureThreshold,
|
|
23528
|
+
crashCount: control.operationalHealth.crashCount
|
|
23529
|
+
} : { availability: "unavailable" };
|
|
23530
|
+
return {
|
|
23531
|
+
status: "online",
|
|
23532
|
+
pid: control.pid,
|
|
23533
|
+
uptimeMs: control.uptimeMs,
|
|
23534
|
+
transport,
|
|
23535
|
+
activeTaskCount: control.activeTaskCount,
|
|
23536
|
+
pendingApprovalCount: control.pendingApprovalCount,
|
|
23537
|
+
operationalHealth,
|
|
23538
|
+
storagePresent: control.storage !== void 0
|
|
23539
|
+
};
|
|
23540
|
+
}
|
|
23541
|
+
async function createSupportBundle(config, storeDir, options = {}) {
|
|
23542
|
+
const [snapshot, recentEvents] = await Promise.all([collectDiagnostics(config, storeDir, options), recentAuditFacts(storeDir)]);
|
|
23543
|
+
return {
|
|
23544
|
+
version: 1,
|
|
23545
|
+
generatedAt: snapshot.generatedAt,
|
|
23546
|
+
product: snapshot.product,
|
|
23547
|
+
system: snapshot.system,
|
|
23548
|
+
config: {
|
|
23549
|
+
serverProtocol: snapshot.config.serverProtocol,
|
|
23550
|
+
customStoreDir: snapshot.config.customStoreDir,
|
|
23551
|
+
hostedJournal: snapshot.config.hostedJournal,
|
|
23552
|
+
...snapshot.config.runtimeAllowlistCount === void 0 ? {} : { runtimeAllowlistCount: snapshot.config.runtimeAllowlistCount }
|
|
23553
|
+
},
|
|
23554
|
+
device: {
|
|
23555
|
+
status: snapshot.device.status,
|
|
23556
|
+
...snapshot.device.deviceIdHash ? { deviceIdHash: snapshot.device.deviceIdHash } : {}
|
|
23557
|
+
},
|
|
23558
|
+
runtimes: snapshot.runtimes,
|
|
23559
|
+
control: projectControl(snapshot.control),
|
|
23560
|
+
health: snapshot.health,
|
|
23561
|
+
journal: snapshot.journal,
|
|
23562
|
+
workspace: snapshot.workspace,
|
|
23563
|
+
quarantine: {
|
|
23564
|
+
status: snapshot.quarantine.status,
|
|
23565
|
+
count: snapshot.quarantine.count,
|
|
23566
|
+
scannedCount: snapshot.quarantine.scannedCount,
|
|
23567
|
+
truncated: snapshot.quarantine.truncated,
|
|
23568
|
+
entries: snapshot.quarantine.entries
|
|
23569
|
+
},
|
|
23570
|
+
checks: snapshot.checks.map(({ id, status }) => ({ id, status })),
|
|
23571
|
+
recentEvents,
|
|
23572
|
+
redaction: {
|
|
23573
|
+
policy: "allowlist-v1",
|
|
23574
|
+
omitted: [
|
|
23575
|
+
"server URL host/path/query",
|
|
23576
|
+
"store/workspace/config paths",
|
|
23577
|
+
"control token and provider credentials",
|
|
23578
|
+
"task, prompt, tool input/output and approval text",
|
|
23579
|
+
"raw audit and quarantine contents"
|
|
23580
|
+
],
|
|
23581
|
+
transformed: ["productName/productId/deviceId/runtimeId/quarantine filenames -> SHA-256", "audit events -> closed kind/timestamp only"]
|
|
23582
|
+
}
|
|
23583
|
+
};
|
|
23549
23584
|
}
|
|
23550
|
-
function
|
|
23551
|
-
|
|
23585
|
+
async function writeSupportBundle(outputPath, bundle, secureFileOptions = {}) {
|
|
23586
|
+
const dir = path14__default.dirname(outputPath);
|
|
23587
|
+
const parentStat = await promises.stat(dir);
|
|
23588
|
+
if (!parentStat.isDirectory()) throw new Error("support bundle output parent is not a directory");
|
|
23589
|
+
const privateDir = path14__default.join(dir, `.${path14__default.basename(outputPath)}.${process.pid}.${randomUUID()}.private`);
|
|
23590
|
+
const tempPath = path14__default.join(privateDir, "bundle.tmp");
|
|
23591
|
+
try {
|
|
23592
|
+
await promises.mkdir(privateDir, { mode: 448 });
|
|
23593
|
+
await ensureSecureDir(privateDir, secureFileOptions);
|
|
23594
|
+
await atomicWriteFile(tempPath, `${JSON.stringify(bundle, null, 2)}
|
|
23595
|
+
`, { mode: 384, fsync: true });
|
|
23596
|
+
await ensureSecureFile(tempPath, secureFileOptions);
|
|
23597
|
+
await promises.link(tempPath, outputPath);
|
|
23598
|
+
const published = await promises.open(outputPath, "r+");
|
|
23599
|
+
try {
|
|
23600
|
+
await published.sync();
|
|
23601
|
+
} finally {
|
|
23602
|
+
await published.close();
|
|
23603
|
+
}
|
|
23604
|
+
if (process.platform !== "win32") {
|
|
23605
|
+
const parent = await promises.open(dir, "r");
|
|
23606
|
+
try {
|
|
23607
|
+
await parent.sync();
|
|
23608
|
+
} finally {
|
|
23609
|
+
await parent.close();
|
|
23610
|
+
}
|
|
23611
|
+
}
|
|
23612
|
+
} finally {
|
|
23613
|
+
await promises.rm(privateDir, { recursive: true, force: true }).catch(() => void 0);
|
|
23614
|
+
}
|
|
23552
23615
|
}
|
|
23553
|
-
|
|
23554
|
-
|
|
23616
|
+
|
|
23617
|
+
// src/bin/format.ts
|
|
23618
|
+
function quote(text) {
|
|
23619
|
+
return JSON.stringify(text);
|
|
23555
23620
|
}
|
|
23556
|
-
function
|
|
23557
|
-
return
|
|
23621
|
+
function redactedByteCountPlaceholder(text) {
|
|
23622
|
+
return `[redacted: ${Buffer.byteLength(text, "utf8")} bytes]`;
|
|
23558
23623
|
}
|
|
23559
|
-
|
|
23560
|
-
|
|
23561
|
-
|
|
23562
|
-
|
|
23624
|
+
var STABLE_GIT_ERROR_CATEGORIES2 = new Set(GIT_ERROR_CATEGORIES);
|
|
23625
|
+
function stableGitErrorCategory2(value) {
|
|
23626
|
+
return value !== void 0 && STABLE_GIT_ERROR_CATEGORIES2.has(value) ? value : void 0;
|
|
23627
|
+
}
|
|
23628
|
+
function formatAgentEvent(event) {
|
|
23629
|
+
switch (event.type) {
|
|
23563
23630
|
case "progress":
|
|
23564
|
-
return
|
|
23631
|
+
return `progress: ${quote(event.text)}`;
|
|
23565
23632
|
case "tool_use":
|
|
23566
|
-
return
|
|
23633
|
+
return `tool_use: ${event.tool}`;
|
|
23567
23634
|
case "tool_result":
|
|
23568
|
-
return
|
|
23635
|
+
return `tool_result: ${event.tool}`;
|
|
23569
23636
|
case "artifact":
|
|
23570
|
-
return
|
|
23637
|
+
return `artifact: ${event.name} (${event.contentType})`;
|
|
23571
23638
|
case "needs_approval":
|
|
23572
|
-
return
|
|
23639
|
+
return `needs_approval: ${quote(event.summary)}`;
|
|
23573
23640
|
case "turn_end":
|
|
23574
|
-
return
|
|
23641
|
+
return "turn_end";
|
|
23575
23642
|
case "error":
|
|
23576
|
-
return
|
|
23577
|
-
case "usage":
|
|
23578
|
-
|
|
23579
|
-
|
|
23580
|
-
|
|
23581
|
-
|
|
23582
|
-
|
|
23583
|
-
|
|
23584
|
-
totalTokens: num(r.totalTokens)
|
|
23585
|
-
};
|
|
23586
|
-
default:
|
|
23587
|
-
return { type: "error", message: `[unrecognized audit event type: ${type || "(missing)"}]` };
|
|
23643
|
+
return `error: ${quote(event.message)}`;
|
|
23644
|
+
case "usage": {
|
|
23645
|
+
const parts = [];
|
|
23646
|
+
if (event.inputTokens !== void 0) parts.push(`in=${event.inputTokens}`);
|
|
23647
|
+
if (event.outputTokens !== void 0) parts.push(`out=${event.outputTokens}`);
|
|
23648
|
+
if (event.totalTokens !== void 0) parts.push(`total=${event.totalTokens}`);
|
|
23649
|
+
return `usage: ${parts.length ? parts.join(" ") : "(no fields reported)"}`;
|
|
23650
|
+
}
|
|
23588
23651
|
}
|
|
23589
23652
|
}
|
|
23590
|
-
function
|
|
23591
|
-
const
|
|
23592
|
-
|
|
23593
|
-
switch (kind) {
|
|
23653
|
+
function formatDaemonEventLine(event, options = {}) {
|
|
23654
|
+
const prefix = `[${event.ts}]`;
|
|
23655
|
+
switch (event.kind) {
|
|
23594
23656
|
case "offered":
|
|
23595
|
-
return {
|
|
23596
|
-
case "claimed":
|
|
23597
|
-
|
|
23598
|
-
|
|
23599
|
-
|
|
23600
|
-
case "
|
|
23601
|
-
return {
|
|
23602
|
-
case "
|
|
23603
|
-
return {
|
|
23604
|
-
case "
|
|
23605
|
-
const
|
|
23606
|
-
|
|
23607
|
-
const blobRef = blobRefPresent ? {
|
|
23608
|
-
blobId: str(blobRefRaw.blobId),
|
|
23609
|
-
contentHash: str(blobRefRaw.contentHash),
|
|
23610
|
-
size: num(blobRefRaw.size) ?? 0,
|
|
23611
|
-
contentType: str(blobRefRaw.contentType)
|
|
23612
|
-
} : void 0;
|
|
23613
|
-
const inlineSize = num(raw.inlineSize);
|
|
23614
|
-
return {
|
|
23615
|
-
kind: "artifact",
|
|
23616
|
-
ts,
|
|
23617
|
-
taskId: str(raw.taskId),
|
|
23618
|
-
name: str(raw.name),
|
|
23619
|
-
contentType: str(raw.contentType),
|
|
23620
|
-
inline: inlineSize === void 0 ? void 0 : placeholderFor(inlineSize),
|
|
23621
|
-
blobRef
|
|
23622
|
-
};
|
|
23623
|
-
}
|
|
23624
|
-
case "awaiting-approval":
|
|
23625
|
-
return { kind: "awaiting-approval", ts, taskId: str(raw.taskId), summary: placeholderFor(num(raw.summarySize)) };
|
|
23626
|
-
case "completed":
|
|
23627
|
-
return {
|
|
23628
|
-
kind: "completed",
|
|
23629
|
-
ts,
|
|
23630
|
-
taskId: str(raw.taskId),
|
|
23631
|
-
summary: placeholderFor(num(raw.summarySize)),
|
|
23632
|
-
sessionRef: str(raw.sessionRef)
|
|
23633
|
-
};
|
|
23634
|
-
case "failed":
|
|
23635
|
-
return {
|
|
23636
|
-
kind: "failed",
|
|
23637
|
-
ts,
|
|
23638
|
-
taskId: str(raw.taskId),
|
|
23639
|
-
reason: placeholderFor(num(raw.reasonSize)),
|
|
23640
|
-
retryable: bool(raw.retryable, false),
|
|
23641
|
-
preClaim: typeof raw.preClaim === "boolean" ? raw.preClaim : void 0
|
|
23642
|
-
};
|
|
23643
|
-
case "cancelled": {
|
|
23644
|
-
const reasonSize = num(raw.reasonSize);
|
|
23645
|
-
return {
|
|
23646
|
-
kind: "cancelled",
|
|
23647
|
-
ts,
|
|
23648
|
-
taskId: str(raw.taskId),
|
|
23649
|
-
reason: reasonSize === void 0 ? void 0 : placeholderFor(reasonSize)
|
|
23650
|
-
};
|
|
23651
|
-
}
|
|
23652
|
-
case "connection":
|
|
23653
|
-
return { kind: "connection", ts, state: str(raw.state) };
|
|
23654
|
-
case "paired":
|
|
23655
|
-
return { kind: "paired", ts, deviceId: str(raw.deviceId) };
|
|
23656
|
-
case "unpaired":
|
|
23657
|
-
return { kind: "unpaired", ts };
|
|
23658
|
-
case "runtimes-detected":
|
|
23659
|
-
return { kind: "runtimes-detected", ts, runtimes: Array.isArray(raw.runtimes) ? raw.runtimes : [] };
|
|
23660
|
-
case "shutdown-requested":
|
|
23661
|
-
return { kind: "shutdown-requested", ts, reason: str(raw.reason) };
|
|
23662
|
-
case "shutdown-complete":
|
|
23663
|
-
return { kind: "shutdown-complete", ts, reason: str(raw.reason), undeliveredOutboxCount: num(raw.undeliveredOutboxCount) };
|
|
23664
|
-
case "stale-approval-decision": {
|
|
23665
|
-
const reasonSize = num(raw.reasonSize);
|
|
23666
|
-
return {
|
|
23667
|
-
kind: "stale-approval-decision",
|
|
23668
|
-
ts,
|
|
23669
|
-
taskId: str(raw.taskId),
|
|
23670
|
-
decision: str(raw.decision),
|
|
23671
|
-
reason: reasonSize === void 0 ? void 0 : placeholderFor(reasonSize)
|
|
23672
|
-
};
|
|
23673
|
-
}
|
|
23674
|
-
case "runtime-disposal-failed":
|
|
23675
|
-
return {
|
|
23676
|
-
kind: "runtime-disposal-failed",
|
|
23677
|
-
ts,
|
|
23678
|
-
taskId: str(raw.taskId),
|
|
23679
|
-
runtimeId: str(raw.runtimeId),
|
|
23680
|
-
stage: str(raw.stage),
|
|
23681
|
-
reason: str(raw.reason)
|
|
23682
|
-
};
|
|
23683
|
-
case "device-assertion": {
|
|
23684
|
-
if (raw.result === "issued") {
|
|
23685
|
-
return {
|
|
23686
|
-
kind: "device-assertion",
|
|
23687
|
-
ts,
|
|
23688
|
-
result: "issued",
|
|
23689
|
-
audience: typeof raw.audience === "string" ? raw.audience : "",
|
|
23690
|
-
jti: typeof raw.jti === "string" ? raw.jti : "",
|
|
23691
|
-
expiresAt: typeof raw.expiresAt === "string" ? raw.expiresAt : ""
|
|
23692
|
-
};
|
|
23693
|
-
}
|
|
23694
|
-
const audienceSize = num(raw.audienceSize);
|
|
23695
|
-
return {
|
|
23696
|
-
kind: "device-assertion",
|
|
23697
|
-
ts,
|
|
23698
|
-
result: "denied",
|
|
23699
|
-
reason: typeof raw.reason === "string" ? raw.reason : "",
|
|
23700
|
-
...audienceSize === void 0 ? {} : { audienceSize }
|
|
23701
|
-
};
|
|
23657
|
+
return `${prefix} offered taskId=${event.taskId}${event.runtime ? ` runtime=${event.runtime}` : ""}`;
|
|
23658
|
+
case "claimed":
|
|
23659
|
+
return `${prefix} claimed taskId=${event.taskId}${event.claimedRuntime !== void 0 ? ` claimedRuntime=${event.claimedRuntime}` : ""}`;
|
|
23660
|
+
case "started":
|
|
23661
|
+
return `${prefix} started taskId=${event.taskId}`;
|
|
23662
|
+
case "progress":
|
|
23663
|
+
return `${prefix} progress taskId=${event.taskId} ${formatAgentEvent(event.event)}`;
|
|
23664
|
+
case "artifact":
|
|
23665
|
+
return `${prefix} artifact taskId=${event.taskId} name=${event.name} contentType=${event.contentType}`;
|
|
23666
|
+
case "awaiting-approval": {
|
|
23667
|
+
const summary = options.redactApprovalSummary ? redactedByteCountPlaceholder(event.summary) : event.summary;
|
|
23668
|
+
return `${prefix} awaiting-approval taskId=${event.taskId}${event.approvalId ? ` approvalId=${event.approvalId}` : ""} summary=${quote(summary)}`;
|
|
23702
23669
|
}
|
|
23670
|
+
case "completed":
|
|
23671
|
+
return `${prefix} completed taskId=${event.taskId} sessionRef=${event.sessionRef} summary=${quote(event.summary)}`;
|
|
23672
|
+
case "failed":
|
|
23673
|
+
return `${prefix} failed taskId=${event.taskId} retryable=${event.retryable}${event.preClaim ? " preClaim=true" : ""} reason=${quote(event.reason)}`;
|
|
23674
|
+
case "cancelled":
|
|
23675
|
+
return `${prefix} cancelled taskId=${event.taskId}${event.reason ? ` reason=${quote(event.reason)}` : ""}`;
|
|
23676
|
+
case "connection":
|
|
23677
|
+
return `${prefix} connection state=${event.state}`;
|
|
23678
|
+
case "paired":
|
|
23679
|
+
return `${prefix} paired deviceId=${event.deviceId}`;
|
|
23680
|
+
case "unpaired":
|
|
23681
|
+
return `${prefix} unpaired`;
|
|
23682
|
+
case "runtimes-detected":
|
|
23683
|
+
return `${prefix} runtimes-detected ids=${event.runtimes.map((r) => r.id).join(",") || "(none)"}`;
|
|
23684
|
+
case "shutdown-requested":
|
|
23685
|
+
return `${prefix} shutdown-requested reason=${quote(event.reason)}`;
|
|
23686
|
+
case "shutdown-complete":
|
|
23687
|
+
return `${prefix} shutdown-complete reason=${quote(event.reason)}${event.undeliveredOutboxCount !== void 0 ? ` undeliveredOutboxCount=${event.undeliveredOutboxCount}` : ""}`;
|
|
23688
|
+
case "stale-approval-decision":
|
|
23689
|
+
return `${prefix} stale-approval-decision taskId=${event.taskId} decision=${event.decision}${event.reason ? ` reason=${quote(event.reason)}` : ""}`;
|
|
23690
|
+
case "runtime-disposal-failed":
|
|
23691
|
+
return `${prefix} runtime-disposal-failed taskId=${event.taskId} runtime=${event.runtimeId} stage=${event.stage} reason=${quote(event.reason)}`;
|
|
23703
23692
|
case "git-workspace": {
|
|
23704
|
-
const
|
|
23705
|
-
|
|
23706
|
-
|
|
23707
|
-
|
|
23708
|
-
|
|
23709
|
-
|
|
23710
|
-
|
|
23711
|
-
|
|
23712
|
-
|
|
23713
|
-
|
|
23714
|
-
|
|
23715
|
-
|
|
23716
|
-
|
|
23693
|
+
const parts = [
|
|
23694
|
+
`${prefix} git-workspace taskId=${event.taskId}`,
|
|
23695
|
+
`workspaceId=${event.workspaceId}`,
|
|
23696
|
+
`phase=${event.phase}`,
|
|
23697
|
+
event.headChanged !== void 0 ? `headChanged=${event.headChanged}` : void 0,
|
|
23698
|
+
event.commitsSinceBaseline !== void 0 ? `commits=${event.commitsSinceBaseline}` : void 0,
|
|
23699
|
+
event.dirty ? `dirty=${event.dirty.staged}/${event.dirty.unstaged}/${event.dirty.untracked}/${event.dirty.conflicted}` : void 0,
|
|
23700
|
+
stableGitErrorCategory2(event.errorCategory) ? `errorCategory=${stableGitErrorCategory2(event.errorCategory)}` : void 0
|
|
23701
|
+
].filter((part) => part !== void 0);
|
|
23702
|
+
return parts.join(" ");
|
|
23703
|
+
}
|
|
23704
|
+
case "device-assertion": {
|
|
23705
|
+
const parts = event.result === "issued" ? [
|
|
23706
|
+
`${prefix} device-assertion result=issued`,
|
|
23707
|
+
`audience=${quote(event.audience)}`,
|
|
23708
|
+
`jti=${event.jti}`,
|
|
23709
|
+
`expiresAt=${event.expiresAt}`
|
|
23710
|
+
] : [
|
|
23711
|
+
`${prefix} device-assertion result=denied`,
|
|
23712
|
+
`reason=${event.reason}`,
|
|
23713
|
+
event.audienceSize !== void 0 ? `audienceSize=${event.audienceSize}` : void 0
|
|
23714
|
+
].filter((part) => part !== void 0);
|
|
23715
|
+
return parts.join(" ");
|
|
23717
23716
|
}
|
|
23718
|
-
default:
|
|
23719
|
-
return void 0;
|
|
23720
23717
|
}
|
|
23721
23718
|
}
|
|
23722
|
-
|
|
23723
|
-
|
|
23724
|
-
|
|
23719
|
+
function formatTaskLine(task) {
|
|
23720
|
+
const git = "git" in task ? task.git : void 0;
|
|
23721
|
+
const gitStatus = git ? [
|
|
23722
|
+
`git=${git.phase}`,
|
|
23723
|
+
git.commitsSinceBaseline !== void 0 ? `commits=${git.commitsSinceBaseline}` : void 0,
|
|
23724
|
+
git.dirty ? `dirty=${git.dirty.staged}/${git.dirty.unstaged}/${git.dirty.untracked}/${git.dirty.conflicted}` : void 0
|
|
23725
|
+
].filter((part) => part !== void 0).join(" ") : void 0;
|
|
23726
|
+
const parts = [
|
|
23727
|
+
task.taskId,
|
|
23728
|
+
task.state,
|
|
23729
|
+
task.runtime ? `runtime=${task.runtime}` : void 0,
|
|
23730
|
+
gitStatus,
|
|
23731
|
+
task.claimedRuntime !== void 0 ? `claimedRuntime=${task.claimedRuntime}` : void 0,
|
|
23732
|
+
`updatedAt=${task.updatedAt}`,
|
|
23733
|
+
task.sessionRef ? `sessionRef=${task.sessionRef}` : void 0,
|
|
23734
|
+
task.declined ? "declined=true" : void 0,
|
|
23735
|
+
task.summary ? `summary=${quote(task.summary)}` : void 0
|
|
23736
|
+
].filter((part) => Boolean(part));
|
|
23737
|
+
return parts.join(" ");
|
|
23738
|
+
}
|
|
23739
|
+
function formatTaskListLines(tasks) {
|
|
23740
|
+
if (tasks.length === 0) return ["(no tasks observed yet)"];
|
|
23741
|
+
return tasks.map(formatTaskLine);
|
|
23742
|
+
}
|
|
23743
|
+
function formatRuntimeLines(runtimes) {
|
|
23744
|
+
if (runtimes.length === 0) return ["(no runtimes configured \u2014 check runtimeAllowlist)"];
|
|
23745
|
+
return runtimes.map((r) => {
|
|
23746
|
+
if (!r.present) return `${r.id}: ${r.outcome}`;
|
|
23747
|
+
const caps = [];
|
|
23748
|
+
if (r.steer) caps.push("steer");
|
|
23749
|
+
if (r.resume) caps.push("resume");
|
|
23750
|
+
const parts = [
|
|
23751
|
+
"present",
|
|
23752
|
+
r.version ? `version=${r.version}` : void 0,
|
|
23753
|
+
r.authPresent !== void 0 ? `authPresent=${r.authPresent}` : void 0,
|
|
23754
|
+
`capabilities=${caps.length ? caps.join(",") : "(none)"}`,
|
|
23755
|
+
`modes=${r.permissionModes.length ? r.permissionModes.join(",") : "(none)"}`
|
|
23756
|
+
].filter((part) => Boolean(part));
|
|
23757
|
+
return `${r.id}: ${parts.join(" ")}`;
|
|
23725
23758
|
});
|
|
23726
|
-
const filePath = auditLogPath(storeDir);
|
|
23727
|
-
const line = `${JSON.stringify(redactForAudit(event))}
|
|
23728
|
-
`;
|
|
23729
|
-
const handle = await promises.open(filePath, "a", AUDIT_LOG_MODE);
|
|
23730
|
-
try {
|
|
23731
|
-
await handle.chmod(AUDIT_LOG_MODE);
|
|
23732
|
-
await handle.appendFile(line, "utf8");
|
|
23733
|
-
} finally {
|
|
23734
|
-
await handle.close();
|
|
23735
|
-
}
|
|
23736
|
-
await rotateIfNeeded(filePath);
|
|
23737
23759
|
}
|
|
23738
|
-
|
|
23739
|
-
|
|
23740
|
-
|
|
23741
|
-
|
|
23760
|
+
function formatStatusLines(view) {
|
|
23761
|
+
const lines = [];
|
|
23762
|
+
const label = view.branding?.displayName ?? view.productName;
|
|
23763
|
+
lines.push(`product: ${label} (${view.productId})`);
|
|
23764
|
+
lines.push(
|
|
23765
|
+
`local-agent-release: ${view.localAgentRelease.version}${view.localAgentRelease.buildId ? ` buildId=${view.localAgentRelease.buildId}` : ""}`
|
|
23766
|
+
);
|
|
23767
|
+
if (view.branding?.supportUrl) lines.push(`support: ${view.branding.supportUrl}`);
|
|
23768
|
+
lines.push(`paired: ${view.paired ? "yes" : "no"}${view.deviceId ? ` deviceId=${view.deviceId}` : ""}`);
|
|
23769
|
+
lines.push(
|
|
23770
|
+
view.connection ? `connection: last-known=${view.connection.state} at=${view.connection.ts}` : "connection: unknown (no audit log yet \u2014 run `byok-agent start` at least once to begin observing)"
|
|
23771
|
+
);
|
|
23772
|
+
const runtimeSummary = view.runtimes.map((r) => `${r.id}=${r.present ? "present" : r.outcome}`).join(" ");
|
|
23773
|
+
lines.push(`runtimes: ${runtimeSummary || "(none configured)"}`);
|
|
23774
|
+
const c = view.taskCounts;
|
|
23775
|
+
lines.push(
|
|
23776
|
+
`tasks: total=${c.total} offered=${c.Offered} claimed=${c.Claimed} running=${c.Running} awaitApproval=${c.AwaitApproval} complete=${c.Complete} failed=${c.Failed} cancelled=${c.Cancelled}`
|
|
23777
|
+
);
|
|
23778
|
+
lines.push(`audit-log: ${view.auditLogPath} (${view.auditLogLineCount} event${view.auditLogLineCount === 1 ? "" : "s"})`);
|
|
23779
|
+
return lines;
|
|
23742
23780
|
}
|
|
23743
|
-
function
|
|
23744
|
-
|
|
23745
|
-
const
|
|
23746
|
-
|
|
23747
|
-
|
|
23748
|
-
|
|
23749
|
-
|
|
23750
|
-
|
|
23751
|
-
|
|
23752
|
-
|
|
23753
|
-
|
|
23754
|
-
|
|
23755
|
-
|
|
23756
|
-
|
|
23781
|
+
function formatLiveStatusLines(live) {
|
|
23782
|
+
const liveRelease = live.localAgentRelease;
|
|
23783
|
+
const lines = [
|
|
23784
|
+
`live: pid=${live.pid} uptimeMs=${live.uptimeMs} transport=${live.transport}`,
|
|
23785
|
+
liveRelease ? `live-local-agent-release: ${liveRelease.version}${liveRelease.buildId ? ` buildId=${liveRelease.buildId}` : ""}` : "live-local-agent-release: unknown",
|
|
23786
|
+
`live-paired: ${live.paired ? "yes" : "no"}${live.deviceId ? ` deviceId=${live.deviceId}` : ""}`,
|
|
23787
|
+
`live-runtimes: ${live.runtimeIds.length ? live.runtimeIds.join(",") : "(none)"}`,
|
|
23788
|
+
`live-toolsets: revision=${live.toolsets.revision} count=${live.toolsets.toolsets.length}`
|
|
23789
|
+
];
|
|
23790
|
+
for (const toolset of live.toolsets.toolsets) lines.push(formatToolsetStatusLine("live-toolset", toolset));
|
|
23791
|
+
const health = live.operationalHealth;
|
|
23792
|
+
if (health.availability === "unavailable") {
|
|
23793
|
+
lines.push(`live-operational-health: unavailable reason=${quote(health.reason)}`);
|
|
23794
|
+
} else {
|
|
23795
|
+
lines.push(
|
|
23796
|
+
`live-operational-health: state=${health.state} failures=${health.failureCount}/${health.failureThreshold} windowMs=${health.windowMs} crashes=${health.crashCount}${health.lastCrashAt ? ` lastCrashAt=${health.lastCrashAt}` : ""}`
|
|
23797
|
+
);
|
|
23757
23798
|
}
|
|
23758
|
-
|
|
23759
|
-
|
|
23760
|
-
|
|
23761
|
-
|
|
23799
|
+
if (live.activeTasks.length === 0) {
|
|
23800
|
+
lines.push("live-active-tasks: (none)");
|
|
23801
|
+
} else {
|
|
23802
|
+
for (const task of live.activeTasks) {
|
|
23803
|
+
lines.push(`live-active-task: ${task.taskId} ${task.state}`);
|
|
23804
|
+
}
|
|
23762
23805
|
}
|
|
23763
|
-
|
|
23764
|
-
if (
|
|
23765
|
-
|
|
23766
|
-
|
|
23767
|
-
|
|
23768
|
-
|
|
23769
|
-
|
|
23806
|
+
lines.push(`live-approvals-pending: ${live.approvalsPending}`);
|
|
23807
|
+
if (live.approvals.length === 0) {
|
|
23808
|
+
lines.push("live-approvals: (none)");
|
|
23809
|
+
} else {
|
|
23810
|
+
for (const approval of live.approvals) {
|
|
23811
|
+
lines.push(`live-approval: ${approval.approvalId} taskId=${approval.taskId} summary=${quote(summaryExcerpt(approval.summary))}`);
|
|
23812
|
+
}
|
|
23813
|
+
}
|
|
23814
|
+
if (live.queueWatermarks.length === 0) {
|
|
23815
|
+
lines.push("live-queue-watermarks: (none)");
|
|
23816
|
+
} else {
|
|
23817
|
+
for (const watermark of live.queueWatermarks) {
|
|
23818
|
+
lines.push(
|
|
23819
|
+
`live-queue-watermark: ${watermark.taskId} progressBatcherPending=${watermark.progressBatcherPending} pendingApprovals=${watermark.pendingApprovals}`
|
|
23820
|
+
);
|
|
23821
|
+
}
|
|
23822
|
+
}
|
|
23823
|
+
const storage = live.storage;
|
|
23824
|
+
if (storage !== void 0) {
|
|
23825
|
+
lines.push(
|
|
23826
|
+
`live-storage: state=${storage.pressureState} used=${storage.usedBytes} budget=${storage.budgetBytes} free=${storage.freeBytes} measuredAt=${storage.measuredAt}`
|
|
23770
23827
|
);
|
|
23828
|
+
for (const category of storage.categories) {
|
|
23829
|
+
lines.push(`live-storage-category: ${category.category} bytes=${category.bytes}${category.approximate ? " (approximate)" : ""}`);
|
|
23830
|
+
}
|
|
23831
|
+
if (storage.lastCompaction) {
|
|
23832
|
+
lines.push(
|
|
23833
|
+
`live-storage-compaction: checkpointed=${storage.lastCompaction.checkpointed} walFramesRemaining=${storage.lastCompaction.walFramesRemaining} pagesVacuumed=${storage.lastCompaction.pagesVacuumed} durationMs=${storage.lastCompaction.durationMs} at=${storage.lastCompaction.at}`
|
|
23834
|
+
);
|
|
23835
|
+
}
|
|
23771
23836
|
}
|
|
23772
|
-
return
|
|
23837
|
+
return lines;
|
|
23773
23838
|
}
|
|
23774
|
-
|
|
23775
|
-
|
|
23776
|
-
|
|
23777
|
-
|
|
23778
|
-
|
|
23779
|
-
|
|
23780
|
-
|
|
23781
|
-
|
|
23782
|
-
|
|
23783
|
-
|
|
23784
|
-
|
|
23785
|
-
|
|
23786
|
-
|
|
23839
|
+
function formatToolsetStatusLine(prefix, toolset) {
|
|
23840
|
+
const observation = toolset.observation;
|
|
23841
|
+
return [
|
|
23842
|
+
`${prefix}: ${toolset.id}`,
|
|
23843
|
+
`servers=${toolset.serverCount}`,
|
|
23844
|
+
`definitionRevision=${toolset.definitionRevision}`,
|
|
23845
|
+
`state=${observation?.state ?? "unobserved"}`,
|
|
23846
|
+
observation?.version === void 0 ? void 0 : `version=${quote(observation.version)}`,
|
|
23847
|
+
observation?.observedAt === void 0 ? void 0 : `observedAt=${observation.observedAt}`,
|
|
23848
|
+
observation?.reasonCode === void 0 ? void 0 : `reasonCode=${observation.reasonCode}`
|
|
23849
|
+
].filter((part) => part !== void 0).join(" ");
|
|
23850
|
+
}
|
|
23851
|
+
function formatToolsetsReloadReceiptLines(receipt) {
|
|
23852
|
+
return [
|
|
23853
|
+
`toolsets-reload: changed=${receipt.changed ? "yes" : "no"} previousRevision=${receipt.previousRevision} revision=${receipt.revision}`,
|
|
23854
|
+
...receipt.toolsets.map((toolset) => formatToolsetStatusLine("toolset", toolset))
|
|
23855
|
+
];
|
|
23856
|
+
}
|
|
23857
|
+
var SUMMARY_EXCERPT_MAX_LEN = 60;
|
|
23858
|
+
function summaryExcerpt(summary) {
|
|
23859
|
+
if (!summary) return "(no summary)";
|
|
23860
|
+
return summary.length > SUMMARY_EXCERPT_MAX_LEN ? `${summary.slice(0, SUMMARY_EXCERPT_MAX_LEN)}\u2026` : summary;
|
|
23787
23861
|
}
|
|
23788
|
-
function
|
|
23789
|
-
|
|
23790
|
-
|
|
23791
|
-
|
|
23792
|
-
|
|
23793
|
-
|
|
23862
|
+
function formatAge(ms) {
|
|
23863
|
+
const totalSeconds = Math.floor(Math.max(0, ms) / 1e3);
|
|
23864
|
+
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
23865
|
+
const totalMinutes = Math.floor(totalSeconds / 60);
|
|
23866
|
+
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
23867
|
+
const totalHours = Math.floor(totalMinutes / 60);
|
|
23868
|
+
if (totalHours < 24) return `${totalHours}h`;
|
|
23869
|
+
const totalDays = Math.floor(totalHours / 24);
|
|
23870
|
+
return `${totalDays}d`;
|
|
23794
23871
|
}
|
|
23795
|
-
function
|
|
23796
|
-
|
|
23872
|
+
function formatApprovalsListLines(approvals, nowMs) {
|
|
23873
|
+
if (approvals.length === 0) return ["(no pending approvals)"];
|
|
23874
|
+
return approvals.map((approval) => {
|
|
23875
|
+
const ageMs = nowMs - Date.parse(approval.createdAt);
|
|
23876
|
+
return `${approval.approvalId} taskId=${approval.taskId} age=${formatAge(ageMs)} summary=${quote(summaryExcerpt(approval.summary))}`;
|
|
23877
|
+
});
|
|
23797
23878
|
}
|
|
23798
|
-
|
|
23799
|
-
|
|
23800
|
-
|
|
23879
|
+
|
|
23880
|
+
// src/bin/commands/approvals.ts
|
|
23881
|
+
async function runApprovalsCommand(storeDir, productId, deps = {}) {
|
|
23882
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
23883
|
+
const error = deps.error ?? ((line) => console.error(line));
|
|
23884
|
+
const connectControl = deps.connectControl ?? connectControlClient;
|
|
23885
|
+
const now = deps.now ?? (() => Date.now());
|
|
23886
|
+
const conn = await connectControl({ storeDir, productId });
|
|
23887
|
+
if (!conn.ok) {
|
|
23888
|
+
const message = `cannot list approvals: daemon not reachable (${conn.reason}) \u2014 is \`byok-agent start\` (or the installed service) running?`;
|
|
23889
|
+
error(message);
|
|
23890
|
+
throw new Error(message);
|
|
23891
|
+
}
|
|
23801
23892
|
try {
|
|
23802
|
-
const
|
|
23803
|
-
|
|
23804
|
-
|
|
23805
|
-
|
|
23806
|
-
|
|
23893
|
+
const result = await conn.client.request("approvals.list");
|
|
23894
|
+
for (const line of formatApprovalsListLines(result.approvals, now())) log(line);
|
|
23895
|
+
} catch (err) {
|
|
23896
|
+
if (err instanceof ControlError) {
|
|
23897
|
+
error(`approvals list failed: ${err.message}`);
|
|
23898
|
+
} else {
|
|
23899
|
+
error(`approvals list failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23900
|
+
}
|
|
23901
|
+
throw err;
|
|
23902
|
+
} finally {
|
|
23903
|
+
conn.client.close();
|
|
23807
23904
|
}
|
|
23808
23905
|
}
|
|
23809
|
-
|
|
23810
|
-
|
|
23811
|
-
|
|
23812
|
-
return
|
|
23906
|
+
|
|
23907
|
+
// src/bin/commands/approve-reject.ts
|
|
23908
|
+
async function runApproveCommand(storeDir, productId, approvalId, deps = {}) {
|
|
23909
|
+
return resolveApproval(storeDir, productId, approvalId, "approve", void 0, deps);
|
|
23813
23910
|
}
|
|
23814
|
-
async function
|
|
23815
|
-
|
|
23911
|
+
async function runRejectCommand(storeDir, productId, approvalId, reason, deps = {}) {
|
|
23912
|
+
return resolveApproval(storeDir, productId, approvalId, "reject", reason, deps);
|
|
23913
|
+
}
|
|
23914
|
+
async function resolveApproval(storeDir, productId, approvalId, decision, reason, deps) {
|
|
23915
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
23916
|
+
const error = deps.error ?? ((line) => console.error(line));
|
|
23917
|
+
const connectControl = deps.connectControl ?? connectControlClient;
|
|
23918
|
+
const verb = decision === "approve" ? "approved" : "rejected";
|
|
23919
|
+
const conn = await connectControl({ storeDir, productId });
|
|
23920
|
+
if (!conn.ok) {
|
|
23921
|
+
const message = `cannot ${decision} approvalId=${approvalId}: daemon not reachable (${conn.reason}) \u2014 is \`byok-agent start\` (or the installed service) running?`;
|
|
23922
|
+
error(message);
|
|
23923
|
+
throw new Error(message);
|
|
23924
|
+
}
|
|
23816
23925
|
try {
|
|
23817
|
-
|
|
23926
|
+
await conn.client.request("approvals.resolve", { approvalId, decision, reason });
|
|
23927
|
+
log(`${verb}: approvalId=${approvalId}${reason ? ` reason=${JSON.stringify(reason)}` : ""}`);
|
|
23818
23928
|
} catch (err) {
|
|
23819
|
-
if (err.code === "
|
|
23929
|
+
if (err instanceof ControlError && err.code === "not_found") {
|
|
23930
|
+
error(`${err.message} \u2014 run \`byok-agent approvals\` to see pending approvalIds`);
|
|
23931
|
+
} else {
|
|
23932
|
+
error(`${decision} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23933
|
+
}
|
|
23820
23934
|
throw err;
|
|
23935
|
+
} finally {
|
|
23936
|
+
conn.client.close();
|
|
23821
23937
|
}
|
|
23822
|
-
return completeLines(raw);
|
|
23823
23938
|
}
|
|
23824
|
-
|
|
23825
|
-
|
|
23826
|
-
|
|
23827
|
-
|
|
23828
|
-
|
|
23829
|
-
|
|
23939
|
+
|
|
23940
|
+
// src/bin/commands/pair.ts
|
|
23941
|
+
async function runPairCommand(config, code, deps = {}) {
|
|
23942
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
23943
|
+
if (deps.daemon) {
|
|
23944
|
+
const result2 = await deps.daemon.pair(code);
|
|
23945
|
+
log(`paired: deviceId=${result2.deviceId}`);
|
|
23946
|
+
return;
|
|
23830
23947
|
}
|
|
23831
|
-
|
|
23832
|
-
}
|
|
23833
|
-
|
|
23834
|
-
if (signal.aborted) return Promise.resolve();
|
|
23835
|
-
return new Promise((resolve) => {
|
|
23836
|
-
const onAbort = () => {
|
|
23837
|
-
clearTimeout(timer);
|
|
23838
|
-
resolve();
|
|
23839
|
-
};
|
|
23840
|
-
const timer = setTimeout(() => {
|
|
23841
|
-
signal.removeEventListener("abort", onAbort);
|
|
23842
|
-
resolve();
|
|
23843
|
-
}, ms);
|
|
23844
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
23845
|
-
});
|
|
23846
|
-
}
|
|
23847
|
-
async function followAuditLog(filePath, onEvent, options) {
|
|
23848
|
-
const { signal, pollIntervalMs = 200, fromEnd = true } = options;
|
|
23849
|
-
let offset = 0;
|
|
23850
|
-
let pending = Buffer.alloc(0);
|
|
23851
|
-
let lastFileIdentity;
|
|
23852
|
-
if (fromEnd) {
|
|
23948
|
+
const connectControl = deps.connectControl ?? connectControlClient;
|
|
23949
|
+
const conn = await connectControl({ storeDir: resolveStoreDir(config), productId: config.productId });
|
|
23950
|
+
if (conn.ok) {
|
|
23853
23951
|
try {
|
|
23854
|
-
const
|
|
23855
|
-
|
|
23856
|
-
|
|
23857
|
-
}
|
|
23858
|
-
|
|
23859
|
-
offset = 0;
|
|
23952
|
+
const result2 = await conn.client.request("enrollment.pair", { pairingCode: code });
|
|
23953
|
+
log(`paired: deviceId=${result2.deviceId}`);
|
|
23954
|
+
return;
|
|
23955
|
+
} finally {
|
|
23956
|
+
conn.client.close();
|
|
23860
23957
|
}
|
|
23861
23958
|
}
|
|
23862
|
-
|
|
23863
|
-
|
|
23864
|
-
|
|
23865
|
-
|
|
23866
|
-
|
|
23867
|
-
|
|
23868
|
-
|
|
23959
|
+
const daemon = createDaemon(config);
|
|
23960
|
+
const result = await daemon.pair(code);
|
|
23961
|
+
log(`paired: deviceId=${result.deviceId}`);
|
|
23962
|
+
}
|
|
23963
|
+
|
|
23964
|
+
// src/bin/commands/doctor.ts
|
|
23965
|
+
var DoctorConfirmationRequiredError = class extends Error {
|
|
23966
|
+
constructor() {
|
|
23967
|
+
super("doctor --fix requires explicit --yes confirmation");
|
|
23968
|
+
this.name = "DoctorConfirmationRequiredError";
|
|
23969
|
+
}
|
|
23970
|
+
};
|
|
23971
|
+
var DoctorDaemonRunningError = class extends Error {
|
|
23972
|
+
constructor() {
|
|
23973
|
+
super("doctor --fix refuses while the daemon control socket is reachable; stop the daemon first");
|
|
23974
|
+
this.name = "DoctorDaemonRunningError";
|
|
23975
|
+
}
|
|
23976
|
+
};
|
|
23977
|
+
function formatDoctorLines(snapshot, fixResult) {
|
|
23978
|
+
const lines = [
|
|
23979
|
+
`doctor: productHash=${snapshot.product.nameHash} generatedAt=${snapshot.generatedAt}`,
|
|
23980
|
+
`system: node=${snapshot.system.node} platform=${snapshot.system.platform} arch=${snapshot.system.arch} sqlite=${snapshot.system.sqliteAvailable ? "available" : "unavailable"}`
|
|
23981
|
+
];
|
|
23982
|
+
for (const check of snapshot.checks) lines.push(`check ${check.id}: ${check.status} (${check.summary})`);
|
|
23983
|
+
if (snapshot.health.status === "corrupt" || snapshot.health.status === "unavailable") {
|
|
23984
|
+
lines.push(`health-detail: ${snapshot.health.reason}; bytes=${snapshot.health.sizeBytes ?? "unknown"}`);
|
|
23985
|
+
}
|
|
23986
|
+
if (snapshot.quarantine.truncated) {
|
|
23987
|
+
lines.push(`quarantine-detail: inventory truncated after ${snapshot.quarantine.scannedCount} scanned entries`);
|
|
23988
|
+
}
|
|
23989
|
+
if (fixResult?.status === "quarantined") {
|
|
23990
|
+
lines.push(
|
|
23991
|
+
`fix: quarantined ${fixResult.sizeBytes} bytes as ${fixResult.evidenceName}; manifest=${fixResult.manifestName}; sha256=${fixResult.sha256}`
|
|
23992
|
+
);
|
|
23993
|
+
} else if (fixResult) {
|
|
23994
|
+
lines.push(`fix: no change (${fixResult.reason})`);
|
|
23995
|
+
}
|
|
23996
|
+
return lines;
|
|
23997
|
+
}
|
|
23998
|
+
async function runDoctorCommand(config, options = {}) {
|
|
23999
|
+
if (options.repair !== void 0) {
|
|
24000
|
+
if (options.fix || options.repair !== "restore-enrollment-metadata") {
|
|
24001
|
+
throw new Error("doctor requires one supported repair action; --fix cannot be combined with --repair");
|
|
23869
24002
|
}
|
|
24003
|
+
const log2 = options.log ?? ((line) => console.log(line));
|
|
23870
24004
|
try {
|
|
23871
|
-
const
|
|
23872
|
-
|
|
23873
|
-
|
|
23874
|
-
|
|
23875
|
-
|
|
23876
|
-
}
|
|
23877
|
-
|
|
23878
|
-
|
|
24005
|
+
const repair = await repairDeviceEnrollmentMetadata(config, {
|
|
24006
|
+
confirmed: options.confirmed,
|
|
24007
|
+
expectedDeviceId: options.expectedDeviceId,
|
|
24008
|
+
expectedTenantId: options.expectedTenantId
|
|
24009
|
+
});
|
|
24010
|
+
log2(options.json ? JSON.stringify({ repair }, null, 2) : `repair ${repair.action}: ${repair.status} (${repair.scope})`);
|
|
24011
|
+
return;
|
|
24012
|
+
} catch (error) {
|
|
24013
|
+
if (options.json && error instanceof DeviceMetadataRepairError) {
|
|
24014
|
+
log2(JSON.stringify({ repair: { action: "restore-enrollment-metadata", scope: "device", status: "failed", code: error.code } }, null, 2));
|
|
23879
24015
|
}
|
|
23880
|
-
|
|
23881
|
-
if (st.size <= offset) return Buffer.alloc(0);
|
|
23882
|
-
const length = st.size - offset;
|
|
23883
|
-
const buffer = Buffer.alloc(length);
|
|
23884
|
-
const { bytesRead } = await handle.read(buffer, 0, length, offset);
|
|
23885
|
-
offset += bytesRead;
|
|
23886
|
-
return buffer.subarray(0, bytesRead);
|
|
23887
|
-
} finally {
|
|
23888
|
-
await handle.close();
|
|
24016
|
+
throw error;
|
|
23889
24017
|
}
|
|
23890
24018
|
}
|
|
23891
|
-
|
|
23892
|
-
|
|
23893
|
-
|
|
23894
|
-
|
|
23895
|
-
|
|
23896
|
-
|
|
23897
|
-
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
|
|
23901
|
-
|
|
23902
|
-
|
|
23903
|
-
|
|
23904
|
-
|
|
23905
|
-
|
|
23906
|
-
|
|
23907
|
-
|
|
23908
|
-
|
|
23909
|
-
|
|
24019
|
+
if (options.expectedDeviceId !== void 0 || options.expectedTenantId !== void 0) {
|
|
24020
|
+
throw new Error("expected identity flags require --repair restore-enrollment-metadata");
|
|
24021
|
+
}
|
|
24022
|
+
if (options.fix && !options.confirmed) throw new DoctorConfirmationRequiredError();
|
|
24023
|
+
const storeDir = resolveStoreDir(config);
|
|
24024
|
+
let snapshot = await collectDiagnostics(config, storeDir, options);
|
|
24025
|
+
let fixResult;
|
|
24026
|
+
if (options.fix) {
|
|
24027
|
+
if (snapshot.control.status === "online") throw new DoctorDaemonRunningError();
|
|
24028
|
+
fixResult = await quarantineCorruptOperationalHealth(storeDir, { clock: options.clock });
|
|
24029
|
+
snapshot = await collectDiagnostics(config, storeDir, options);
|
|
24030
|
+
}
|
|
24031
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
24032
|
+
if (options.json) {
|
|
24033
|
+
log(JSON.stringify({ diagnostics: snapshot, ...fixResult ? { fix: fixResult } : {} }, null, 2));
|
|
24034
|
+
return;
|
|
24035
|
+
}
|
|
24036
|
+
for (const line of formatDoctorLines(snapshot, fixResult)) log(line);
|
|
24037
|
+
}
|
|
24038
|
+
|
|
24039
|
+
// src/bin/commands/runtimes.ts
|
|
24040
|
+
async function runRuntimesCommand(config, deps = {}) {
|
|
24041
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
24042
|
+
const adapters = deps.adapters ?? defaultRuntimeAdapters(config.runtimeAllowlist);
|
|
24043
|
+
const runtimes = await probeRuntimes(adapters);
|
|
24044
|
+
for (const line of formatRuntimeLines(runtimes)) log(line);
|
|
24045
|
+
}
|
|
24046
|
+
function buildServiceDefinition(config, configPath, rest) {
|
|
24047
|
+
const name = argValue(rest, "--name") ?? config.productId;
|
|
24048
|
+
const agentBin = argValue(rest, "--agent-bin") ?? process.argv[1] ?? "byok-agent";
|
|
24049
|
+
const nodeBin = argValue(rest, "--node-bin") ?? process.execPath;
|
|
24050
|
+
const absoluteConfigPath = path14__default.resolve(configPath);
|
|
24051
|
+
const logDir = path14__default.join(resolveStoreDir(config), "service-logs");
|
|
24052
|
+
const definition = {
|
|
24053
|
+
name,
|
|
24054
|
+
displayName: config.branding?.displayName ?? config.productName,
|
|
24055
|
+
program: nodeAgentProgram({ agentBin, configPath: absoluteConfigPath, nodeBin }),
|
|
24056
|
+
logDir
|
|
24057
|
+
};
|
|
24058
|
+
const winswBin = argValue(rest, "--winsw-bin");
|
|
24059
|
+
if (winswBin) {
|
|
24060
|
+
const installDir = argValue(rest, "--winsw-install-dir");
|
|
24061
|
+
definition.windows = installDir ? { winswBin, installDir } : { winswBin };
|
|
23910
24062
|
}
|
|
24063
|
+
return definition;
|
|
24064
|
+
}
|
|
24065
|
+
function buildLifecycle(config, configPath, rest, deps) {
|
|
24066
|
+
return deps.lifecycle ?? createServiceLifecycle(buildServiceDefinition(config, configPath, rest));
|
|
24067
|
+
}
|
|
24068
|
+
function serviceNameFor(config, rest) {
|
|
24069
|
+
return argValue(rest, "--name") ?? config.productId;
|
|
24070
|
+
}
|
|
24071
|
+
async function runInstallCommand(config, configPath, rest, deps = {}) {
|
|
24072
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
24073
|
+
const lifecycle = buildLifecycle(config, configPath, rest, deps);
|
|
24074
|
+
await lifecycle.install();
|
|
24075
|
+
log(`service installed and started: ${serviceNameFor(config, rest)}`);
|
|
24076
|
+
}
|
|
24077
|
+
async function runUninstallCommand(config, configPath, rest, deps = {}) {
|
|
24078
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
24079
|
+
const lifecycle = buildLifecycle(config, configPath, rest, deps);
|
|
24080
|
+
await lifecycle.uninstall();
|
|
24081
|
+
log(`service uninstalled: ${serviceNameFor(config, rest)}`);
|
|
24082
|
+
}
|
|
24083
|
+
async function runServiceStartCommand(config, configPath, rest, deps = {}) {
|
|
24084
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
24085
|
+
const lifecycle = buildLifecycle(config, configPath, rest, deps);
|
|
24086
|
+
await lifecycle.start();
|
|
24087
|
+
log(`service started: ${serviceNameFor(config, rest)}`);
|
|
24088
|
+
}
|
|
24089
|
+
async function runServiceStopCommand(config, configPath, rest, deps = {}) {
|
|
24090
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
24091
|
+
const lifecycle = buildLifecycle(config, configPath, rest, deps);
|
|
24092
|
+
await lifecycle.stop();
|
|
24093
|
+
log(`service stopped: ${serviceNameFor(config, rest)}`);
|
|
24094
|
+
}
|
|
24095
|
+
async function runServiceStatusCommand(config, configPath, rest, deps = {}) {
|
|
24096
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
24097
|
+
const lifecycle = buildLifecycle(config, configPath, rest, deps);
|
|
24098
|
+
const status = await lifecycle.status();
|
|
24099
|
+
log(`installed: ${status.installed ? "yes" : "no"}`);
|
|
24100
|
+
const runningLabel = status.running ? "yes" : status.determinate ? "no" : "unknown (could not query the service manager)";
|
|
24101
|
+
log(`running: ${runningLabel}`);
|
|
24102
|
+
log(`detail: ${status.detail.trim() || "(none)"}`);
|
|
23911
24103
|
}
|
|
23912
24104
|
|
|
23913
24105
|
// src/bin/commands/start.ts
|
|
@@ -24082,194 +24274,6 @@ async function runStatusCommand(config, deps = {}) {
|
|
|
24082
24274
|
conn.client.close();
|
|
24083
24275
|
}
|
|
24084
24276
|
}
|
|
24085
|
-
var MAX_SUPPORT_AUDIT_TAIL_BYTES = 256 * 1024;
|
|
24086
|
-
var MAX_SUPPORT_AUDIT_FACTS = 200;
|
|
24087
|
-
var AUDIT_KINDS = /* @__PURE__ */ new Set([
|
|
24088
|
-
"artifact",
|
|
24089
|
-
"awaiting-approval",
|
|
24090
|
-
"cancelled",
|
|
24091
|
-
"claimed",
|
|
24092
|
-
"completed",
|
|
24093
|
-
"connection",
|
|
24094
|
-
"failed",
|
|
24095
|
-
"git-workspace",
|
|
24096
|
-
"offered",
|
|
24097
|
-
"paired",
|
|
24098
|
-
"progress",
|
|
24099
|
-
"runtimes-detected",
|
|
24100
|
-
"shutdown-complete",
|
|
24101
|
-
"shutdown-requested",
|
|
24102
|
-
"stale-approval-decision",
|
|
24103
|
-
"started",
|
|
24104
|
-
"unpaired"
|
|
24105
|
-
]);
|
|
24106
|
-
function isAuditKind(value) {
|
|
24107
|
-
return typeof value === "string" && AUDIT_KINDS.has(value);
|
|
24108
|
-
}
|
|
24109
|
-
function sameFileState6(left, right) {
|
|
24110
|
-
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
|
|
24111
|
-
}
|
|
24112
|
-
async function recentAuditFacts(storeDir) {
|
|
24113
|
-
const filePath = auditLogPath(storeDir);
|
|
24114
|
-
let pathStat;
|
|
24115
|
-
try {
|
|
24116
|
-
pathStat = await promises.lstat(filePath, { bigint: true });
|
|
24117
|
-
if (!pathStat.isFile() || pathStat.isSymbolicLink()) {
|
|
24118
|
-
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
24119
|
-
}
|
|
24120
|
-
} catch (err) {
|
|
24121
|
-
if (err.code === "ENOENT") {
|
|
24122
|
-
return { status: "missing", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
24123
|
-
}
|
|
24124
|
-
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
24125
|
-
}
|
|
24126
|
-
let handle;
|
|
24127
|
-
try {
|
|
24128
|
-
handle = await promises.open(
|
|
24129
|
-
filePath,
|
|
24130
|
-
constants.O_RDONLY | constants.O_NONBLOCK | (constants.O_NOFOLLOW ?? 0)
|
|
24131
|
-
);
|
|
24132
|
-
} catch (err) {
|
|
24133
|
-
if (err.code === "ENOENT") {
|
|
24134
|
-
return { status: "missing", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
24135
|
-
}
|
|
24136
|
-
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
24137
|
-
}
|
|
24138
|
-
try {
|
|
24139
|
-
const stat = await handle.stat({ bigint: true });
|
|
24140
|
-
const namedAfterOpen = await promises.lstat(filePath, { bigint: true });
|
|
24141
|
-
if (!stat.isFile() || !namedAfterOpen.isFile() || namedAfterOpen.isSymbolicLink() || !sameFileState6(pathStat, stat) || !sameFileState6(stat, namedAfterOpen)) {
|
|
24142
|
-
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
24143
|
-
}
|
|
24144
|
-
const sourceSize = Number(stat.size);
|
|
24145
|
-
const length = Math.min(sourceSize, MAX_SUPPORT_AUDIT_TAIL_BYTES);
|
|
24146
|
-
const start = Math.max(0, sourceSize - length);
|
|
24147
|
-
const buffer = Buffer.alloc(length);
|
|
24148
|
-
const { bytesRead } = await handle.read(buffer, 0, length, start);
|
|
24149
|
-
const afterRead = await handle.stat({ bigint: true });
|
|
24150
|
-
const namedAfterRead = await promises.lstat(filePath, { bigint: true });
|
|
24151
|
-
if (bytesRead !== length || namedAfterRead.isSymbolicLink() || !sameFileState6(stat, afterRead) || !sameFileState6(afterRead, namedAfterRead)) {
|
|
24152
|
-
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
24153
|
-
}
|
|
24154
|
-
let text = buffer.subarray(0, bytesRead).toString("utf8");
|
|
24155
|
-
if (start > 0) text = text.slice(Math.max(0, text.indexOf("\n") + 1));
|
|
24156
|
-
const lines = text.split("\n").filter(Boolean).slice(-MAX_SUPPORT_AUDIT_FACTS);
|
|
24157
|
-
const facts = [];
|
|
24158
|
-
for (const line of lines) {
|
|
24159
|
-
try {
|
|
24160
|
-
const value = JSON.parse(line);
|
|
24161
|
-
if (isAuditKind(value.kind) && typeof value.ts === "string" && Number.isFinite(Date.parse(value.ts))) {
|
|
24162
|
-
facts.push({ kind: value.kind, ts: new Date(Date.parse(value.ts)).toISOString() });
|
|
24163
|
-
}
|
|
24164
|
-
} catch {
|
|
24165
|
-
}
|
|
24166
|
-
}
|
|
24167
|
-
return { status: "available", included: facts.length, sourceBytesRead: bytesRead, sourceTruncated: sourceSize > bytesRead, facts };
|
|
24168
|
-
} catch {
|
|
24169
|
-
return { status: "unavailable", included: 0, sourceBytesRead: 0, sourceTruncated: false, facts: [] };
|
|
24170
|
-
} finally {
|
|
24171
|
-
await handle.close();
|
|
24172
|
-
}
|
|
24173
|
-
}
|
|
24174
|
-
function projectControl(control) {
|
|
24175
|
-
if (control.status === "offline") return { status: "offline" };
|
|
24176
|
-
const transport = ["connecting", "open", "closed", "revoked"].includes(control.transport) ? control.transport : "unavailable";
|
|
24177
|
-
const operationalHealth = control.operationalHealth.availability === "available" ? {
|
|
24178
|
-
availability: "available",
|
|
24179
|
-
state: control.operationalHealth.state,
|
|
24180
|
-
failureCount: control.operationalHealth.failureCount,
|
|
24181
|
-
windowMs: control.operationalHealth.windowMs,
|
|
24182
|
-
failureThreshold: control.operationalHealth.failureThreshold,
|
|
24183
|
-
crashCount: control.operationalHealth.crashCount
|
|
24184
|
-
} : { availability: "unavailable" };
|
|
24185
|
-
return {
|
|
24186
|
-
status: "online",
|
|
24187
|
-
pid: control.pid,
|
|
24188
|
-
uptimeMs: control.uptimeMs,
|
|
24189
|
-
transport,
|
|
24190
|
-
activeTaskCount: control.activeTaskCount,
|
|
24191
|
-
pendingApprovalCount: control.pendingApprovalCount,
|
|
24192
|
-
operationalHealth,
|
|
24193
|
-
storagePresent: control.storage !== void 0
|
|
24194
|
-
};
|
|
24195
|
-
}
|
|
24196
|
-
async function createSupportBundle(config, storeDir, options = {}) {
|
|
24197
|
-
const [snapshot, recentEvents] = await Promise.all([collectDiagnostics(config, storeDir, options), recentAuditFacts(storeDir)]);
|
|
24198
|
-
return {
|
|
24199
|
-
version: 1,
|
|
24200
|
-
generatedAt: snapshot.generatedAt,
|
|
24201
|
-
product: snapshot.product,
|
|
24202
|
-
system: snapshot.system,
|
|
24203
|
-
config: {
|
|
24204
|
-
serverProtocol: snapshot.config.serverProtocol,
|
|
24205
|
-
customStoreDir: snapshot.config.customStoreDir,
|
|
24206
|
-
hostedJournal: snapshot.config.hostedJournal,
|
|
24207
|
-
...snapshot.config.runtimeAllowlistCount === void 0 ? {} : { runtimeAllowlistCount: snapshot.config.runtimeAllowlistCount }
|
|
24208
|
-
},
|
|
24209
|
-
device: {
|
|
24210
|
-
status: snapshot.device.status,
|
|
24211
|
-
...snapshot.device.deviceIdHash ? { deviceIdHash: snapshot.device.deviceIdHash } : {}
|
|
24212
|
-
},
|
|
24213
|
-
runtimes: snapshot.runtimes,
|
|
24214
|
-
control: projectControl(snapshot.control),
|
|
24215
|
-
health: snapshot.health,
|
|
24216
|
-
journal: snapshot.journal,
|
|
24217
|
-
workspace: snapshot.workspace,
|
|
24218
|
-
quarantine: {
|
|
24219
|
-
status: snapshot.quarantine.status,
|
|
24220
|
-
count: snapshot.quarantine.count,
|
|
24221
|
-
scannedCount: snapshot.quarantine.scannedCount,
|
|
24222
|
-
truncated: snapshot.quarantine.truncated,
|
|
24223
|
-
entries: snapshot.quarantine.entries
|
|
24224
|
-
},
|
|
24225
|
-
checks: snapshot.checks.map(({ id, status }) => ({ id, status })),
|
|
24226
|
-
recentEvents,
|
|
24227
|
-
redaction: {
|
|
24228
|
-
policy: "allowlist-v1",
|
|
24229
|
-
omitted: [
|
|
24230
|
-
"server URL host/path/query",
|
|
24231
|
-
"store/workspace/config paths",
|
|
24232
|
-
"control token and provider credentials",
|
|
24233
|
-
"task, prompt, tool input/output and approval text",
|
|
24234
|
-
"raw audit and quarantine contents"
|
|
24235
|
-
],
|
|
24236
|
-
transformed: ["productName/productId/deviceId/runtimeId/quarantine filenames -> SHA-256", "audit events -> closed kind/timestamp only"]
|
|
24237
|
-
}
|
|
24238
|
-
};
|
|
24239
|
-
}
|
|
24240
|
-
async function writeSupportBundle(outputPath, bundle, secureFileOptions = {}) {
|
|
24241
|
-
const dir = path14__default.dirname(outputPath);
|
|
24242
|
-
const parentStat = await promises.stat(dir);
|
|
24243
|
-
if (!parentStat.isDirectory()) throw new Error("support bundle output parent is not a directory");
|
|
24244
|
-
const privateDir = path14__default.join(dir, `.${path14__default.basename(outputPath)}.${process.pid}.${randomUUID()}.private`);
|
|
24245
|
-
const tempPath = path14__default.join(privateDir, "bundle.tmp");
|
|
24246
|
-
try {
|
|
24247
|
-
await promises.mkdir(privateDir, { mode: 448 });
|
|
24248
|
-
await ensureSecureDir(privateDir, secureFileOptions);
|
|
24249
|
-
await atomicWriteFile(tempPath, `${JSON.stringify(bundle, null, 2)}
|
|
24250
|
-
`, { mode: 384, fsync: true });
|
|
24251
|
-
await ensureSecureFile(tempPath, secureFileOptions);
|
|
24252
|
-
await promises.link(tempPath, outputPath);
|
|
24253
|
-
const published = await promises.open(outputPath, "r+");
|
|
24254
|
-
try {
|
|
24255
|
-
await published.sync();
|
|
24256
|
-
} finally {
|
|
24257
|
-
await published.close();
|
|
24258
|
-
}
|
|
24259
|
-
if (process.platform !== "win32") {
|
|
24260
|
-
const parent = await promises.open(dir, "r");
|
|
24261
|
-
try {
|
|
24262
|
-
await parent.sync();
|
|
24263
|
-
} finally {
|
|
24264
|
-
await parent.close();
|
|
24265
|
-
}
|
|
24266
|
-
}
|
|
24267
|
-
} finally {
|
|
24268
|
-
await promises.rm(privateDir, { recursive: true, force: true }).catch(() => void 0);
|
|
24269
|
-
}
|
|
24270
|
-
}
|
|
24271
|
-
|
|
24272
|
-
// src/bin/commands/support-bundle.ts
|
|
24273
24277
|
async function runSupportBundleCommand(config, options) {
|
|
24274
24278
|
if (!options.outputPath) throw new Error("support-bundle requires --output <path>");
|
|
24275
24279
|
const outputPath = path14__default.resolve(options.outputPath);
|