@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.
Files changed (2) hide show
  1. package/dist/index.js +79 -10
  2. 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 refresh(
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 refresh(cfg, id, opts.ttl);
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 refresh(cfg, id, opts.ttl);
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 refresh(cfg, id, ttlSeconds) {
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/{id}/accept)").action(async (id, _opts, cmd) => {
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/{id}/accept", {
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/{id}/reject)").option("--reason <reason>", "Why it's being rejected").option("--reason-code <code>", "Structured reason code").action(async (id, opts, cmd) => {
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/{id}/reject", {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sechroom/cli",
3
- "version": "2026.7.21",
3
+ "version": "2026.7.23",
4
4
  "description": "Sechroom CLI — a thin, generated client over the Sechroom HTTP API. An agent/human surface alongside MCP.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",