@sechroom/cli 2026.7.21 → 2026.7.23
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/index.js +79 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2213,7 +2213,7 @@ function registerExecutor(program2) {
|
|
|
2213
2213
|
);
|
|
2214
2214
|
});
|
|
2215
2215
|
executor.command("refresh <id>").description("Refresh one advertisement lease once").option("--ttl <seconds>", "Advertisement TTL (30-600)", parseInteger, 120).action(async (id, opts, cmd) => {
|
|
2216
|
-
const data = await
|
|
2216
|
+
const data = await refreshExecutorInstance(
|
|
2217
2217
|
resolveConfig(cmd.optsWithGlobals()),
|
|
2218
2218
|
id,
|
|
2219
2219
|
opts.ttl
|
|
@@ -2224,13 +2224,13 @@ function registerExecutor(program2) {
|
|
|
2224
2224
|
if (opts.interval >= opts.ttl)
|
|
2225
2225
|
fail("heartbeat interval must be shorter than the TTL");
|
|
2226
2226
|
const cfg = resolveConfig(cmd.optsWithGlobals());
|
|
2227
|
-
await
|
|
2227
|
+
await refreshExecutorInstance(cfg, id, opts.ttl);
|
|
2228
2228
|
process.stderr.write(
|
|
2229
2229
|
style.green("executor heartbeat active") + style.dim(` \u2014 ${id}
|
|
2230
2230
|
`)
|
|
2231
2231
|
);
|
|
2232
2232
|
await holdHeartbeat(async () => {
|
|
2233
|
-
await
|
|
2233
|
+
await refreshExecutorInstance(cfg, id, opts.ttl);
|
|
2234
2234
|
}, opts.interval * 1e3);
|
|
2235
2235
|
});
|
|
2236
2236
|
executor.command("offers <id>").description("List live dispatch offers addressed to this exact instance").action(async (id, _opts, cmd) => {
|
|
@@ -2275,7 +2275,7 @@ function parseTransport(value) {
|
|
|
2275
2275
|
return fail("transport must be push or pull");
|
|
2276
2276
|
}
|
|
2277
2277
|
}
|
|
2278
|
-
async function
|
|
2278
|
+
async function refreshExecutorInstance(cfg, id, ttlSeconds) {
|
|
2279
2279
|
return api(
|
|
2280
2280
|
cfg,
|
|
2281
2281
|
`/me/executor-instances/${encodeURIComponent(id)}/refresh`,
|
|
@@ -2404,6 +2404,10 @@ function registerChannel(program2) {
|
|
|
2404
2404
|
);
|
|
2405
2405
|
await drain();
|
|
2406
2406
|
const stopReconciliation = startOfferReconciliation(drain);
|
|
2407
|
+
const stopHeartbeat = startExecutorHeartbeat(
|
|
2408
|
+
() => refreshExecutorInstance(cfg, instance.id, located.state.ttlSeconds),
|
|
2409
|
+
located.state.refreshAfterSeconds * 1e3
|
|
2410
|
+
);
|
|
2407
2411
|
if (json) {
|
|
2408
2412
|
emit(
|
|
2409
2413
|
{
|
|
@@ -2427,6 +2431,7 @@ function registerChannel(program2) {
|
|
|
2427
2431
|
await holdOpen(conn);
|
|
2428
2432
|
} finally {
|
|
2429
2433
|
stopReconciliation();
|
|
2434
|
+
stopHeartbeat();
|
|
2430
2435
|
}
|
|
2431
2436
|
});
|
|
2432
2437
|
withFilterOpts(
|
|
@@ -2469,6 +2474,10 @@ function registerChannel(program2) {
|
|
|
2469
2474
|
);
|
|
2470
2475
|
await drain();
|
|
2471
2476
|
const stopReconciliation = startOfferReconciliation(drain);
|
|
2477
|
+
const stopHeartbeat = startExecutorHeartbeat(
|
|
2478
|
+
() => refreshExecutorInstance(cfg, instance.id, located.state.ttlSeconds),
|
|
2479
|
+
located.state.refreshAfterSeconds * 1e3
|
|
2480
|
+
);
|
|
2472
2481
|
process.stderr.write(
|
|
2473
2482
|
style.dim(
|
|
2474
2483
|
`sechroom channel (mcp) \u2014 tenant ${cfg.tenant}, executor ${located.state.instanceKey}
|
|
@@ -2479,6 +2488,7 @@ function registerChannel(program2) {
|
|
|
2479
2488
|
await holdOpen(conn);
|
|
2480
2489
|
} finally {
|
|
2481
2490
|
stopReconciliation();
|
|
2491
|
+
stopHeartbeat();
|
|
2482
2492
|
}
|
|
2483
2493
|
});
|
|
2484
2494
|
channel.command("install").description(
|
|
@@ -2584,6 +2594,16 @@ function startOfferReconciliation(drain, intervalMilliseconds = 5e3, dependencie
|
|
|
2584
2594
|
}, intervalMilliseconds);
|
|
2585
2595
|
return () => cancel(timer);
|
|
2586
2596
|
}
|
|
2597
|
+
function startExecutorHeartbeat(refresh, intervalMilliseconds, dependencies = {}) {
|
|
2598
|
+
const schedule = dependencies.setInterval ?? setInterval;
|
|
2599
|
+
const cancel = dependencies.clearInterval ?? clearInterval;
|
|
2600
|
+
const onError = dependencies.onError ?? ((error) => process.stderr.write(err(`channel heartbeat failed: ${String(error)}
|
|
2601
|
+
`)));
|
|
2602
|
+
const timer = schedule(() => {
|
|
2603
|
+
void refresh().catch(onError);
|
|
2604
|
+
}, intervalMilliseconds);
|
|
2605
|
+
return () => cancel(timer);
|
|
2606
|
+
}
|
|
2587
2607
|
async function drainClaims(cfg, executorInstanceId, deliver, dependencies = {}) {
|
|
2588
2608
|
const request = dependencies.request ?? api;
|
|
2589
2609
|
const sleep = dependencies.sleep ?? ((milliseconds) => new Promise((resolve3) => setTimeout(resolve3, milliseconds)));
|
|
@@ -5902,23 +5922,23 @@ Examples:
|
|
|
5902
5922
|
});
|
|
5903
5923
|
emit(data, cmd.optsWithGlobals().json);
|
|
5904
5924
|
});
|
|
5905
|
-
suggestion.command("accept <id>").description("Accept a suggestion (POST /relationship-suggestions/{
|
|
5925
|
+
suggestion.command("accept <id>").description("Accept a suggestion (POST /relationship-suggestions/{instanceId}/accept)").action(async (id, _opts, cmd) => {
|
|
5906
5926
|
const cfg = resolveConfig(cmd.optsWithGlobals());
|
|
5907
5927
|
const data = await runApi("Accepting suggestion", async () => {
|
|
5908
5928
|
const client = await makeClient(cfg);
|
|
5909
|
-
return client.POST("/relationship-suggestions/{
|
|
5910
|
-
params: { path: { id } },
|
|
5929
|
+
return client.POST("/relationship-suggestions/{instanceId}/accept", {
|
|
5930
|
+
params: { path: { instanceId: id } },
|
|
5911
5931
|
body: {}
|
|
5912
5932
|
});
|
|
5913
5933
|
});
|
|
5914
5934
|
emitAction(`accepted suggestion ${style.bold(id)}`, data, cmd.optsWithGlobals().json);
|
|
5915
5935
|
});
|
|
5916
|
-
suggestion.command("reject <id>").description("Reject a suggestion (POST /relationship-suggestions/{
|
|
5936
|
+
suggestion.command("reject <id>").description("Reject a suggestion (POST /relationship-suggestions/{instanceId}/reject)").option("--reason <reason>", "Why it's being rejected").option("--reason-code <code>", "Structured reason code").action(async (id, opts, cmd) => {
|
|
5917
5937
|
const cfg = resolveConfig(cmd.optsWithGlobals());
|
|
5918
5938
|
const data = await runApi("Rejecting suggestion", async () => {
|
|
5919
5939
|
const client = await makeClient(cfg);
|
|
5920
|
-
return client.POST("/relationship-suggestions/{
|
|
5921
|
-
params: { path: { id } },
|
|
5940
|
+
return client.POST("/relationship-suggestions/{instanceId}/reject", {
|
|
5941
|
+
params: { path: { instanceId: id } },
|
|
5922
5942
|
body: {
|
|
5923
5943
|
reason: opts.reason ?? null,
|
|
5924
5944
|
...opts.reasonCode ? { reasonCode: opts.reasonCode } : {}
|
|
@@ -6893,6 +6913,54 @@ Examples:
|
|
|
6893
6913
|
});
|
|
6894
6914
|
}
|
|
6895
6915
|
|
|
6916
|
+
// src/commands/work-brief.ts
|
|
6917
|
+
function registerWorkBrief(program2) {
|
|
6918
|
+
const workBrief = program2.command("work-brief").description("Control an active work brief run");
|
|
6919
|
+
workBrief.addHelpText(
|
|
6920
|
+
"after",
|
|
6921
|
+
`
|
|
6922
|
+
Examples:
|
|
6923
|
+
$ sechroom work-brief pause mem_XXXX --reason-code operator-hold --source claude-code-chris
|
|
6924
|
+
$ sechroom work-brief resume mem_XXXX --reason-code operator-resume --source claude-code-chris --reason "Ready to continue"`
|
|
6925
|
+
);
|
|
6926
|
+
registerLifecycleAction(workBrief, "pause");
|
|
6927
|
+
registerLifecycleAction(workBrief, "resume");
|
|
6928
|
+
}
|
|
6929
|
+
function registerLifecycleAction(workBrief, action) {
|
|
6930
|
+
const presentParticiple = action === "pause" ? "Pausing" : "Resuming";
|
|
6931
|
+
const pastTense = action === "pause" ? "paused" : "resumed";
|
|
6932
|
+
workBrief.command(`${action} <briefId>`).description(`${capitalize(action)} an active work brief run`).requiredOption(
|
|
6933
|
+
"--reason-code <code>",
|
|
6934
|
+
"Stable machine-readable reason code"
|
|
6935
|
+
).requiredOption(
|
|
6936
|
+
"--source <source>",
|
|
6937
|
+
"Calling surface or lane recorded in the audit"
|
|
6938
|
+
).option("--reason <text>", "Optional human-readable reason").action(async (briefId, opts, cmd) => {
|
|
6939
|
+
const globals = cmd.optsWithGlobals();
|
|
6940
|
+
const cfg = resolveConfig(globals);
|
|
6941
|
+
const data = await runApi(`${presentParticiple} work brief`, async () => {
|
|
6942
|
+
const client = await makeClient(cfg);
|
|
6943
|
+
return client.POST("/work-briefs/{id}/lifecycle", {
|
|
6944
|
+
params: { path: { id: briefId } },
|
|
6945
|
+
body: {
|
|
6946
|
+
action,
|
|
6947
|
+
reasonCode: opts.reasonCode,
|
|
6948
|
+
source: opts.source,
|
|
6949
|
+
reasonText: opts.reason ?? null
|
|
6950
|
+
}
|
|
6951
|
+
});
|
|
6952
|
+
});
|
|
6953
|
+
emitAction(
|
|
6954
|
+
`${pastTense} work brief ${style.bold(briefId)} \u2192 ${data.outcome}`,
|
|
6955
|
+
data,
|
|
6956
|
+
globals.json
|
|
6957
|
+
);
|
|
6958
|
+
});
|
|
6959
|
+
}
|
|
6960
|
+
function capitalize(value) {
|
|
6961
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
6962
|
+
}
|
|
6963
|
+
|
|
6896
6964
|
// src/index.ts
|
|
6897
6965
|
function resolveVersion() {
|
|
6898
6966
|
try {
|
|
@@ -7055,6 +7123,7 @@ registerLookup(program);
|
|
|
7055
7123
|
registerRelationships(program);
|
|
7056
7124
|
registerWorkspace(program);
|
|
7057
7125
|
registerProject(program);
|
|
7126
|
+
registerWorkBrief(program);
|
|
7058
7127
|
registerDecomposition(program);
|
|
7059
7128
|
registerExecutor(program);
|
|
7060
7129
|
registerClose(program);
|
package/package.json
CHANGED