@scopebond/agent 0.5.1 → 0.5.3

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/agent.d.ts CHANGED
@@ -4,8 +4,10 @@ export interface CycleOptions {
4
4
  dir: string;
5
5
  fetchImpl?: typeof fetch;
6
6
  now?: () => number;
7
- /** Per-request timeout for the workspace, in milliseconds. */
7
+ /** Per-request timeout for the workspace's rules check, in milliseconds. */
8
8
  timeoutMs?: number;
9
+ /** Per-request timeout for each delivery request, its answer included (default 30 s). */
10
+ deliveryTimeoutMs?: number;
9
11
  }
10
12
  export interface CycleResult {
11
13
  at: number;
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAWA,OAAO,EAGL,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAChD,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IACxC,kBAAkB,EAAE,OAAO,EAAE,CAAC;IAC9B,yFAAyF;IACzF,SAAS,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAAC;CAC1C;AAED,4FAA4F;AAC5F,wBAAgB,iBAAiB,IAAI,OAAO,EAAE,CAE7C;AAED,gGAAgG;AAChG,wBAAgB,kBAAkB,CAAC,SAAS,GAAE,OAAO,EAAwB,GAAG,OAAO,EAAE,CAExF;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAwC1E;AAED,mGAAmG;AACnG,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAOpE"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAWA,OAAO,EAGL,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAChD,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IACxC,kBAAkB,EAAE,OAAO,EAAE,CAAC;IAC9B,yFAAyF;IACzF,SAAS,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAAC;CAC1C;AAED,4FAA4F;AAC5F,wBAAgB,iBAAiB,IAAI,OAAO,EAAE,CAE7C;AAED,gGAAgG;AAChG,wBAAgB,kBAAkB,CAAC,SAAS,GAAE,OAAO,EAAwB,GAAG,OAAO,EAAE,CAExF;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA2C1E;AAED,mGAAmG;AACnG,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAOpE"}
package/dist/agent.js CHANGED
@@ -8,7 +8,7 @@ import { join } from "node:path";
8
8
  import { createCloudExporter } from "@scopebond/gateway";
9
9
  import { SqliteCloudOutbox } from "@scopebond/gateway/node";
10
10
  import { createSigner } from "@scopebond/sdk";
11
- import { LOSSLESS_OUTBOX, OUTBOX_FILE, buildStatusJson, cursorDetected, codexDetected, hookVersion, ingestUrl, isHarnessConfigured, loadConnection, policyBuilds, recordDeliveryAttempt, summaryOptions, syncPolicy, userHarnessFile, } from "@scopebond/hook";
11
+ import { LOSSLESS_OUTBOX, OUTBOX_FILE, buildStatusJson, cursorDetected, codexDetected, hookVersion, ingestUrl, isHarnessConfigured, loadConnection, policyBuilds, recordDeliveryAttempt, repairDeliveryAt, summaryOptions, syncPolicy, userHarnessFile, } from "@scopebond/hook";
12
12
  /** The agents on this computer whose user-level settings should hold the Scopebond hook. */
13
13
  export function expectedHarnesses() {
14
14
  return ["claude", ...(cursorDetected() ? ["cursor"] : []), ...(codexDetected() ? ["codex"] : [])];
@@ -32,10 +32,13 @@ export async function runCycle(options) {
32
32
  // 1. Deliver everything the hook queued, in batches, until the queue is empty or the workspace refuses.
33
33
  try {
34
34
  const outbox = new SqliteCloudOutbox(join(dir, OUTBOX_FILE), LOSSLESS_OUTBOX);
35
+ // First, what the queue never got: evaluations a hook process was stopped in the middle of (closed as outcome-unknown
36
+ // records) and receipts whose queue write failed. Best effort; never stops delivery.
37
+ await repairDeliveryAt(dir, outbox, now());
35
38
  const before = outbox.status().pending;
36
39
  const exporter = createCloudExporter({
37
40
  url: ingestUrl(connection), credential: connection.credential, outbox,
38
- flushMs: 24 * 60 * 60 * 1000, fetch: options.fetchImpl, now, summaries: summaryOptions(dir),
41
+ flushMs: 24 * 60 * 60 * 1000, fetch: options.fetchImpl, now, summaries: summaryOptions(dir), requestTimeoutMs: options.deliveryTimeoutMs ?? 30_000,
39
42
  });
40
43
  try {
41
44
  const lastSuccess = exporter.status().lastSuccessAt;
package/dist/agent.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["// The Scopebond Agent's work, one cycle at a time. The hook still decides every action on its\n// own (enforcement never depends on the agent); the agent keeps everything around it working:\n// it delivers what the hook queued, keeps the workspace rules and the connection current, and\n// knows when the agent settings lost their hook entry. Every step is best-effort and bounded;\n// a cycle never throws.\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createCloudExporter } from \"@scopebond/gateway\";\nimport { SqliteCloudOutbox } from \"@scopebond/gateway/node\";\nimport { createSigner } from \"@scopebond/sdk\";\nimport {\n LOSSLESS_OUTBOX, OUTBOX_FILE, buildStatusJson, cursorDetected, codexDetected, hookVersion, ingestUrl,\n isHarnessConfigured, loadConnection, policyBuilds, recordDeliveryAttempt, summaryOptions, syncPolicy, userHarnessFile,\n type Harness, type StatusJson, type SyncOutcome,\n} from \"@scopebond/hook\";\n\nexport interface CycleOptions {\n /** The Scopebond home this agent serves (the user's ~/.scopebond by default). */\n dir: string;\n fetchImpl?: typeof fetch;\n now?: () => number;\n /** Per-request timeout for the workspace, in milliseconds. */\n timeoutMs?: number;\n}\n\nexport interface CycleResult {\n at: number;\n connected: boolean;\n delivered: number;\n pending: number;\n deliveryError: string | null;\n rules: SyncOutcome[\"state\"] | \"skipped\";\n missingHookEntries: Harness[];\n /** A request from the workspace's computer page, carried on this cycle's rules check. */\n requested: \"flush\" | \"self_check\" | null;\n}\n\n/** The agents on this computer whose user-level settings should hold the Scopebond hook. */\nexport function expectedHarnesses(): Harness[] {\n return [\"claude\", ...(cursorDetected() ? [\"cursor\" as const] : []), ...(codexDetected() ? [\"codex\" as const] : [])];\n}\n\n/** Agent settings that lost their Scopebond hook entry (an update or a reset can remove it). */\nexport function missingHookEntries(harnesses: Harness[] = expectedHarnesses()): Harness[] {\n return harnesses.filter((h) => existsSync(userHarnessFile(h)) && !isHarnessConfigured(userHarnessFile(h)));\n}\n\nexport async function runCycle(options: CycleOptions): Promise<CycleResult> {\n const now = options.now ?? Date.now;\n const dir = options.dir;\n const result: CycleResult = { at: now(), connected: false, delivered: 0, pending: 0, deliveryError: null, rules: \"skipped\", missingHookEntries: [], requested: null };\n try { result.missingHookEntries = missingHookEntries(); } catch { /* reported as none */ }\n const connection = loadConnection(dir);\n if (!connection) return result;\n result.connected = true;\n\n // 1. Deliver everything the hook queued, in batches, until the queue is empty or the workspace refuses.\n try {\n const outbox = new SqliteCloudOutbox(join(dir, OUTBOX_FILE), LOSSLESS_OUTBOX);\n const before = outbox.status().pending;\n const exporter = createCloudExporter({\n url: ingestUrl(connection), credential: connection.credential, outbox,\n flushMs: 24 * 60 * 60 * 1000, fetch: options.fetchImpl, now, summaries: summaryOptions(dir),\n });\n try {\n const lastSuccess = exporter.status().lastSuccessAt;\n await exporter.flush();\n const status = exporter.status();\n recordDeliveryAttempt(dir, status, now(), lastSuccess);\n result.pending = status.pending;\n result.delivered = Math.max(0, before - status.pending);\n result.deliveryError = status.lastError;\n } finally { exporter.stop(); }\n } catch (error) {\n result.deliveryError = (error as Error).message;\n }\n\n // 2. The rules check: the workspace's rules, the queue report, and credential renewal when due.\n try {\n const keyFile = join(dir, \"agent.key\");\n if (existsSync(keyFile)) {\n const agentKid = createSigner({ privateKeyPem: readFileSync(keyFile, \"utf8\") }).kid;\n const outcome = await syncPolicy(dir, { agentKid, hookVersion: hookVersion(), policyBuilds, fetchImpl: options.fetchImpl, timeoutMs: options.timeoutMs ?? 10_000, onRequest: (kind) => { result.requested = kind; } });\n result.rules = outcome.state;\n }\n } catch { result.rules = \"unavailable\"; }\n return result;\n}\n\n/** The machine-readable status (scopebond.status.v1) for this computer, as the hook reports it. */\nexport function computerStatus(dir: string, now?: number): StatusJson {\n const expected = expectedHarnesses();\n const configured = (h: Harness) => expected.includes(h) && existsSync(userHarnessFile(h)) && isHarnessConfigured(userHarnessFile(h));\n return buildStatusJson({\n version: hookVersion(), activeDir: dir, candidateDirs: [dir], hasPolicy: existsSync(join(dir, \"policy.json\")),\n agents: { claude: configured(\"claude\"), cursor: configured(\"cursor\"), codex: configured(\"codex\") }, now,\n });\n}\n"]}
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["// The Scopebond Agent's work, one cycle at a time. The hook still decides every action on its\n// own (enforcement never depends on the agent); the agent keeps everything around it working:\n// it delivers what the hook queued, keeps the workspace rules and the connection current, and\n// knows when the agent settings lost their hook entry. Every step is best-effort and bounded;\n// a cycle never throws.\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createCloudExporter } from \"@scopebond/gateway\";\nimport { SqliteCloudOutbox } from \"@scopebond/gateway/node\";\nimport { createSigner } from \"@scopebond/sdk\";\nimport {\n LOSSLESS_OUTBOX, OUTBOX_FILE, buildStatusJson, cursorDetected, codexDetected, hookVersion, ingestUrl,\n isHarnessConfigured, loadConnection, policyBuilds, recordDeliveryAttempt, repairDeliveryAt, summaryOptions, syncPolicy, userHarnessFile,\n type Harness, type StatusJson, type SyncOutcome,\n} from \"@scopebond/hook\";\n\nexport interface CycleOptions {\n /** The Scopebond home this agent serves (the user's ~/.scopebond by default). */\n dir: string;\n fetchImpl?: typeof fetch;\n now?: () => number;\n /** Per-request timeout for the workspace's rules check, in milliseconds. */\n timeoutMs?: number;\n /** Per-request timeout for each delivery request, its answer included (default 30 s). */\n deliveryTimeoutMs?: number;\n}\n\nexport interface CycleResult {\n at: number;\n connected: boolean;\n delivered: number;\n pending: number;\n deliveryError: string | null;\n rules: SyncOutcome[\"state\"] | \"skipped\";\n missingHookEntries: Harness[];\n /** A request from the workspace's computer page, carried on this cycle's rules check. */\n requested: \"flush\" | \"self_check\" | null;\n}\n\n/** The agents on this computer whose user-level settings should hold the Scopebond hook. */\nexport function expectedHarnesses(): Harness[] {\n return [\"claude\", ...(cursorDetected() ? [\"cursor\" as const] : []), ...(codexDetected() ? [\"codex\" as const] : [])];\n}\n\n/** Agent settings that lost their Scopebond hook entry (an update or a reset can remove it). */\nexport function missingHookEntries(harnesses: Harness[] = expectedHarnesses()): Harness[] {\n return harnesses.filter((h) => existsSync(userHarnessFile(h)) && !isHarnessConfigured(userHarnessFile(h)));\n}\n\nexport async function runCycle(options: CycleOptions): Promise<CycleResult> {\n const now = options.now ?? Date.now;\n const dir = options.dir;\n const result: CycleResult = { at: now(), connected: false, delivered: 0, pending: 0, deliveryError: null, rules: \"skipped\", missingHookEntries: [], requested: null };\n try { result.missingHookEntries = missingHookEntries(); } catch { /* reported as none */ }\n const connection = loadConnection(dir);\n if (!connection) return result;\n result.connected = true;\n\n // 1. Deliver everything the hook queued, in batches, until the queue is empty or the workspace refuses.\n try {\n const outbox = new SqliteCloudOutbox(join(dir, OUTBOX_FILE), LOSSLESS_OUTBOX);\n // First, what the queue never got: evaluations a hook process was stopped in the middle of (closed as outcome-unknown\n // records) and receipts whose queue write failed. Best effort; never stops delivery.\n await repairDeliveryAt(dir, outbox, now());\n const before = outbox.status().pending;\n const exporter = createCloudExporter({\n url: ingestUrl(connection), credential: connection.credential, outbox,\n flushMs: 24 * 60 * 60 * 1000, fetch: options.fetchImpl, now, summaries: summaryOptions(dir), requestTimeoutMs: options.deliveryTimeoutMs ?? 30_000,\n });\n try {\n const lastSuccess = exporter.status().lastSuccessAt;\n await exporter.flush();\n const status = exporter.status();\n recordDeliveryAttempt(dir, status, now(), lastSuccess);\n result.pending = status.pending;\n result.delivered = Math.max(0, before - status.pending);\n result.deliveryError = status.lastError;\n } finally { exporter.stop(); }\n } catch (error) {\n result.deliveryError = (error as Error).message;\n }\n\n // 2. The rules check: the workspace's rules, the queue report, and credential renewal when due.\n try {\n const keyFile = join(dir, \"agent.key\");\n if (existsSync(keyFile)) {\n const agentKid = createSigner({ privateKeyPem: readFileSync(keyFile, \"utf8\") }).kid;\n const outcome = await syncPolicy(dir, { agentKid, hookVersion: hookVersion(), policyBuilds, fetchImpl: options.fetchImpl, timeoutMs: options.timeoutMs ?? 10_000, onRequest: (kind) => { result.requested = kind; } });\n result.rules = outcome.state;\n }\n } catch { result.rules = \"unavailable\"; }\n return result;\n}\n\n/** The machine-readable status (scopebond.status.v1) for this computer, as the hook reports it. */\nexport function computerStatus(dir: string, now?: number): StatusJson {\n const expected = expectedHarnesses();\n const configured = (h: Harness) => expected.includes(h) && existsSync(userHarnessFile(h)) && isHarnessConfigured(userHarnessFile(h));\n return buildStatusJson({\n version: hookVersion(), activeDir: dir, candidateDirs: [dir], hasPolicy: existsSync(join(dir, \"policy.json\")),\n agents: { claude: configured(\"claude\"), cursor: configured(\"cursor\"), codex: configured(\"codex\") }, now,\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;AACpD,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAA;KAAE,GAAG,IAAI,CAAC;IACzE,qDAAqD;IACrD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAuBpH"}
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;AACpD,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAA;KAAE,GAAG,IAAI,CAAC;IACzE,qDAAqD;IACrD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAwBpH"}
package/dist/health.js CHANGED
@@ -7,10 +7,11 @@ export function healthOf(status, selfCheck) {
7
7
  if (!status.delivery.connected) {
8
8
  return { level: "red", headline: "Not connected to a workspace: records stay on this computer", fix: null, hint: "Connect it: npx @scopebond/hook login https://<your-workspace>" };
9
9
  }
10
- // W20: the hook blocks every action while its delivery queue cannot be opened.
10
+ // While the hook's delivery queue cannot be opened, actions stay allowed and are recorded on this computer only; they are
11
+ // sent once the queue can be written again.
11
12
  const queueError = status.delivery.queue_error;
12
13
  if (queueError) {
13
- return { level: "red", headline: "Every action is blocked: the delivery queue cannot be opened", fix: null, hint: `${queueError}. Free some disk space, or make that file and its -wal and -shm files writable (do not delete it).` };
14
+ return { level: "red", headline: "Records stay on this computer: the delivery queue cannot be opened", fix: null, hint: `${queueError}. Free some disk space, or make that file and its -wal and -shm files writable (do not delete it).` };
14
15
  }
15
16
  if (status.delivery.connection_refused_since !== null) {
16
17
  return { level: "red", headline: "The workspace refuses this computer's connection", fix: null, hint: "Sign in again: npx @scopebond/hook login https://<your-workspace>" };
@@ -1 +1 @@
1
- {"version":3,"file":"health.js","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAAA,2HAA2H;AAC3H,2EAA2E;AAc3E,MAAM,UAAU,QAAQ,CAAC,MAAkB,EAAE,SAA+D;IAC1G,IAAI,MAAM,CAAC,KAAK,KAAK,eAAe,EAAE,CAAC;QACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,6DAA6D,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC3J,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,6DAA6D,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,gEAAgE,EAAE,CAAC;IACtL,CAAC;IACD,+EAA+E;IAC/E,MAAM,UAAU,GAAI,MAAM,CAAC,QAA4C,CAAC,WAAW,CAAC;IACpF,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,8DAA8D,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,UAAU,oGAAoG,EAAE,CAAC;IACxO,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,CAAC,wBAAwB,KAAK,IAAI,EAAE,CAAC;QACtD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,kDAAkD,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,mEAAmE,EAAE,CAAC;IAC9K,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,KAAK,mBAAmB,EAAE,CAAC;QACzC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAClC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,qBAAqB,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACtJ,CAAC;IACD,IAAI,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAC/B,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,gCAAgC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACpK,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,8DAA8D,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC7H,CAAC","sourcesContent":["// One line for a person: green, amber or red, what it means, and the one action that fixes it. The tray icon (Windows) and\n// the notifications (macOS, Linux) show this; nothing else decides colour.\n\nimport type { StatusJson } from \"@scopebond/hook\";\n\nexport type HealthLevel = \"green\" | \"amber\" | \"red\";\nexport interface Health {\n level: HealthLevel;\n headline: string;\n /** The local-channel route that fixes it, with its button label, when one exists. */\n fix: { label: string; route: \"/repair\" | \"/flush\" | \"/maintain\" } | null;\n /** What a person types when no button can fix it. */\n hint: string | null;\n}\n\nexport function healthOf(status: StatusJson, selfCheck: { ok: boolean; failed: string[] } | null | undefined): Health {\n if (status.state === \"not_governing\") {\n return { level: \"red\", headline: \"Scopebond is not checking any coding agent on this computer\", fix: { label: \"Repair\", route: \"/repair\" }, hint: null };\n }\n if (!status.delivery.connected) {\n return { level: \"red\", headline: \"Not connected to a workspace: records stay on this computer\", fix: null, hint: \"Connect it: npx @scopebond/hook login https://<your-workspace>\" };\n }\n // W20: the hook blocks every action while its delivery queue cannot be opened.\n const queueError = (status.delivery as { queue_error?: string | null }).queue_error;\n if (queueError) {\n return { level: \"red\", headline: \"Every action is blocked: the delivery queue cannot be opened\", fix: null, hint: `${queueError}. Free some disk space, or make that file and its -wal and -shm files writable (do not delete it).` };\n }\n if (status.delivery.connection_refused_since !== null) {\n return { level: \"red\", headline: \"The workspace refuses this computer's connection\", fix: null, hint: \"Sign in again: npx @scopebond/hook login https://<your-workspace>\" };\n }\n if (status.state === \"recording_locally\") {\n const n = status.delivery.pending;\n return { level: \"amber\", headline: `${n} record${n === 1 ? \"\" : \"s\"} waiting to be sent`, fix: { label: \"Send now\", route: \"/flush\" }, hint: null };\n }\n if (selfCheck && !selfCheck.ok) {\n return { level: \"amber\", headline: `The daily self-check failed: ${selfCheck.failed.join(\", \")}`, fix: { label: \"Check again\", route: \"/maintain\" }, hint: null };\n }\n return { level: \"green\", headline: \"Checking your coding agents and delivering to your workspace\", fix: null, hint: null };\n}\n"]}
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAAA,2HAA2H;AAC3H,2EAA2E;AAc3E,MAAM,UAAU,QAAQ,CAAC,MAAkB,EAAE,SAA+D;IAC1G,IAAI,MAAM,CAAC,KAAK,KAAK,eAAe,EAAE,CAAC;QACrC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,6DAA6D,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC3J,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,6DAA6D,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,gEAAgE,EAAE,CAAC;IACtL,CAAC;IACD,0HAA0H;IAC1H,4CAA4C;IAC5C,MAAM,UAAU,GAAI,MAAM,CAAC,QAA4C,CAAC,WAAW,CAAC;IACpF,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,oEAAoE,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,UAAU,oGAAoG,EAAE,CAAC;IAC9O,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,CAAC,wBAAwB,KAAK,IAAI,EAAE,CAAC;QACtD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,kDAAkD,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,mEAAmE,EAAE,CAAC;IAC9K,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,KAAK,mBAAmB,EAAE,CAAC;QACzC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAClC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,qBAAqB,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACtJ,CAAC;IACD,IAAI,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAC/B,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,gCAAgC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACpK,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,8DAA8D,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC7H,CAAC","sourcesContent":["// One line for a person: green, amber or red, what it means, and the one action that fixes it. The tray icon (Windows) and\n// the notifications (macOS, Linux) show this; nothing else decides colour.\n\nimport type { StatusJson } from \"@scopebond/hook\";\n\nexport type HealthLevel = \"green\" | \"amber\" | \"red\";\nexport interface Health {\n level: HealthLevel;\n headline: string;\n /** The local-channel route that fixes it, with its button label, when one exists. */\n fix: { label: string; route: \"/repair\" | \"/flush\" | \"/maintain\" } | null;\n /** What a person types when no button can fix it. */\n hint: string | null;\n}\n\nexport function healthOf(status: StatusJson, selfCheck: { ok: boolean; failed: string[] } | null | undefined): Health {\n if (status.state === \"not_governing\") {\n return { level: \"red\", headline: \"Scopebond is not checking any coding agent on this computer\", fix: { label: \"Repair\", route: \"/repair\" }, hint: null };\n }\n if (!status.delivery.connected) {\n return { level: \"red\", headline: \"Not connected to a workspace: records stay on this computer\", fix: null, hint: \"Connect it: npx @scopebond/hook login https://<your-workspace>\" };\n }\n // While the hook's delivery queue cannot be opened, actions stay allowed and are recorded on this computer only; they are\n // sent once the queue can be written again.\n const queueError = (status.delivery as { queue_error?: string | null }).queue_error;\n if (queueError) {\n return { level: \"red\", headline: \"Records stay on this computer: the delivery queue cannot be opened\", fix: null, hint: `${queueError}. Free some disk space, or make that file and its -wal and -shm files writable (do not delete it).` };\n }\n if (status.delivery.connection_refused_since !== null) {\n return { level: \"red\", headline: \"The workspace refuses this computer's connection\", fix: null, hint: \"Sign in again: npx @scopebond/hook login https://<your-workspace>\" };\n }\n if (status.state === \"recording_locally\") {\n const n = status.delivery.pending;\n return { level: \"amber\", headline: `${n} record${n === 1 ? \"\" : \"s\"} waiting to be sent`, fix: { label: \"Send now\", route: \"/flush\" }, hint: null };\n }\n if (selfCheck && !selfCheck.ok) {\n return { level: \"amber\", headline: `The daily self-check failed: ${selfCheck.failed.join(\", \")}`, fix: { label: \"Check again\", route: \"/maintain\" }, hint: null };\n }\n return { level: \"green\", headline: \"Checking your coding agents and delivering to your workspace\", fix: null, hint: null };\n}\n"]}
package/dist/ipc.js CHANGED
@@ -39,7 +39,7 @@ export async function startControl(dir, version, routes) {
39
39
  const token = randomBytes(24).toString("base64url");
40
40
  const expected = Buffer.from(token);
41
41
  const handle = async (req, res) => {
42
- const send = (status, body) => { res.writeHead(status, { "content-type": "application/json", "cache-control": "no-store" }); res.end(JSON.stringify(body)); };
42
+ const send = (status, body) => { res.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" }); res.end(JSON.stringify(body)); };
43
43
  const offered = Buffer.from(String(req.headers[TOKEN_HEADER] ?? ""));
44
44
  if (offered.length !== expected.length || !timingSafeEqual(offered, expected))
45
45
  return send(401, { error: "unauthorized" });
package/dist/ipc.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ipc.js","sourceRoot":"","sources":["../src/ipc.ts"],"names":[],"mappings":"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","sourcesContent":["// The agent's local control channel: HTTP with a random token, over a named pipe on Windows (a random name) or a Unix\n// socket in a folder only this user can open, and for one more release also on 127.0.0.1 at a random port (the PowerShell\n// tray uses it; SCOPEBOND_AGENT_LOOPBACK=0 turns it off). The pipe or socket, the port, the token and the process id are\n// written to agent.json in the Scopebond home, which is how the CLI, the hook's override window and the tray find it.\n// A loopback port can be reached by any program on the computer; the pipe's name and the socket's folder cannot be found\n// or opened by another user. Every request must carry the token either way.\n\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { randomBytes, timingSafeEqual } from \"node:crypto\";\nimport { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { requestOverSocket } from \"@scopebond/hook\";\n\nexport const AGENT_FILE = \"agent.json\";\nexport const TOKEN_HEADER = \"x-scopebond-agent-token\";\n\n/** `socket`: the named pipe or Unix socket (absent from agents before it); `port`: 0 when loopback is off. */\nexport interface AgentEndpoint { port: number; socket?: string | null; token: string; pid: number; started_at: number; version: string }\n\n/** Where this start's pipe or socket goes. Windows: a pipe whose name is random, so no other user can guess it or claim it\n * first. Elsewhere: `run/agent.sock` in the Scopebond home (folder 0700), or a private folder under the temporary folder\n * when that path is longer than a Unix socket path may be. */\nexport function localSocketPath(dir: string, platform: NodeJS.Platform = process.platform, random = randomBytes(16).toString(\"hex\")): string {\n if (platform === \"win32\") return `\\\\\\\\.\\\\pipe\\\\scopebond-agent-${random}`;\n const inHome = join(dir, \"run\", \"agent.sock\");\n if (Buffer.byteLength(inHome) <= 100) return inHome;\n return join(mkdtempSync(join(tmpdir(), \"scopebond-\")), \"agent.sock\");\n}\n\nexport type Handler = (body: unknown) => Promise<unknown> | unknown;\n\nexport function readEndpoint(dir: string): AgentEndpoint | null {\n const file = join(dir, AGENT_FILE);\n if (!existsSync(file)) return null;\n try { return JSON.parse(readFileSync(file, \"utf8\")) as AgentEndpoint; } catch { return null; }\n}\n\n/** Start the control server. `routes` maps \"METHOD /path\" to a handler. */\nexport async function startControl(dir: string, version: string, routes: Record<string, Handler>): Promise<{ server: Server; endpoint: AgentEndpoint; close(): Promise<void> }> {\n const token = randomBytes(24).toString(\"base64url\");\n const expected = Buffer.from(token);\n const handle = async (req: IncomingMessage, res: ServerResponse) => {\n const send = (status: number, body: unknown) => { res.writeHead(status, { \"content-type\": \"application/json\", \"cache-control\": \"no-store\" }); res.end(JSON.stringify(body)); };\n const offered = Buffer.from(String(req.headers[TOKEN_HEADER] ?? \"\"));\n if (offered.length !== expected.length || !timingSafeEqual(offered, expected)) return send(401, { error: \"unauthorized\" });\n const handler = routes[`${req.method} ${(req.url ?? \"/\").split(\"?\")[0]}`];\n if (!handler) return send(404, { error: \"not found\" });\n let raw = \"\";\n for await (const chunk of req) { raw += chunk; if (raw.length > 64 * 1024) return send(413, { error: \"too large\" }); }\n try { send(200, await handler(raw ? JSON.parse(raw) : null)); }\n catch (error) { send(500, { error: (error as Error).message.slice(0, 200) }); }\n };\n // The pipe or socket first. A Unix socket's folder is created for this user only, and the socket itself is too.\n const socketPath = localSocketPath(dir);\n const local = createServer(handle);\n let socket: string | null = socketPath;\n try {\n if (process.platform !== \"win32\") {\n mkdirSync(join(socketPath, \"..\"), { recursive: true, mode: 0o700 });\n chmodSync(join(socketPath, \"..\"), 0o700);\n rmSync(socketPath, { force: true });\n }\n await new Promise<void>((resolve, reject) => { local.once(\"error\", reject); local.listen(socketPath, () => resolve()); });\n if (process.platform !== \"win32\") chmodSync(socketPath, 0o600);\n } catch { socket = null; }\n // Loopback for one more release (the PowerShell tray), unless turned off; always when the pipe could not be made.\n const loopback = process.env.SCOPEBOND_AGENT_LOOPBACK !== \"0\" || socket === null;\n const server = createServer(handle);\n if (loopback) await new Promise<void>((resolve, reject) => { server.once(\"error\", reject); server.listen(0, \"127.0.0.1\", () => resolve()); });\n const address = loopback ? server.address() : null;\n const port = typeof address === \"object\" && address ? address.port : 0;\n const endpoint: AgentEndpoint = { port, socket, token, pid: process.pid, started_at: Date.now(), version };\n const file = join(dir, AGENT_FILE);\n const temp = `${file}.${process.pid}.tmp`;\n writeFileSync(temp, JSON.stringify(endpoint, null, 2) + \"\\n\", { mode: 0o600 });\n renameSync(temp, file);\n return {\n server, endpoint,\n close: () => new Promise<void>((resolve) => {\n let open = (loopback ? 1 : 0) + (socket ? 1 : 0);\n const done = () => { if (--open <= 0) resolve(); };\n if (open === 0) resolve();\n if (loopback) server.close(done);\n if (socket) local.close(() => { if (process.platform !== \"win32\") rmSync(socketPath, { force: true }); done(); });\n // Only remove the file if it is still ours (a newer agent may have replaced it).\n try { if (readEndpoint(dir)?.pid === process.pid) rmSync(file, { force: true }); } catch { /* best effort */ }\n }),\n };\n}\n\n/** Call a running agent, over its pipe or socket when it has one. Returns null when no agent answers. */\nexport async function callAgent(dir: string, method: \"GET\" | \"POST\", path: string, body?: unknown, timeoutMs = 5_000): Promise<unknown | null> {\n const endpoint = readEndpoint(dir);\n if (!endpoint) return null;\n if (typeof endpoint.socket === \"string\" && endpoint.socket) {\n const answer = await requestOverSocket(endpoint.socket, method, path, { [TOKEN_HEADER]: endpoint.token }, body, timeoutMs);\n if (answer) return answer.status >= 200 && answer.status < 300 ? answer.body : null;\n if (!endpoint.port) return null;\n }\n try {\n const res = await fetch(`http://127.0.0.1:${endpoint.port}${path}`, {\n method, headers: { [TOKEN_HEADER]: endpoint.token, ...(body !== undefined ? { \"content-type\": \"application/json\" } : {}) },\n body: body !== undefined ? JSON.stringify(body) : undefined, signal: AbortSignal.timeout(timeoutMs),\n });\n return res.ok ? await res.json() : null;\n } catch { return null; }\n}\n"]}
1
+ {"version":3,"file":"ipc.js","sourceRoot":"","sources":["../src/ipc.ts"],"names":[],"mappings":"AAAA,sHAAsH;AACtH,0HAA0H;AAC1H,yHAAyH;AACzH,sHAAsH;AACtH,yHAAyH;AACzH,4EAA4E;AAE5E,OAAO,EAAE,YAAY,EAA0D,MAAM,WAAW,CAAC;AACjG,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,CAAC,MAAM,UAAU,GAAG,YAAY,CAAC;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,yBAAyB,CAAC;AAKtD;;+DAE+D;AAC/D,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,WAA4B,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;IACjI,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,gCAAgC,MAAM,EAAE,CAAC;IAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAC9C,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,GAAG;QAAE,OAAO,MAAM,CAAC;IACpD,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AACvE,CAAC;AAID,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACnC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAkB,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAChG,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,OAAe,EAAE,MAA+B;IAC9F,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,KAAK,EAAE,GAAoB,EAAE,GAAmB,EAAE,EAAE;QACjE,MAAM,IAAI,GAAG,CAAC,MAAc,EAAE,IAAa,EAAE,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,iCAAiC,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9L,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACrE,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QAC3H,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QACvD,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;YAAC,GAAG,IAAI,KAAK,CAAC;YAAC,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAAC,CAAC;QACtH,IAAI,CAAC;YAAC,IAAI,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAAC,CAAC;QAC/D,OAAO,KAAK,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAG,KAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAAC,CAAC;IACjF,CAAC,CAAC;IACF,gHAAgH;IAChH,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,MAAM,GAAkB,UAAU,CAAC;IACvC,IAAI,CAAC;QACH,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACpE,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;YACzC,MAAM,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1H,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;YAAE,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QAAC,MAAM,GAAG,IAAI,CAAC;IAAC,CAAC;IAC1B,kHAAkH;IAClH,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,KAAK,GAAG,IAAI,MAAM,KAAK,IAAI,CAAC;IACjF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,QAAQ;QAAE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9I,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACnD,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAkB,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC;IAC3G,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IAC1C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACvB,OAAO;QACL,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACzC,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACjD,MAAM,IAAI,GAAG,GAAG,EAAE,GAAG,IAAI,EAAE,IAAI,IAAI,CAAC;gBAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;YACnD,IAAI,IAAI,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YAC1B,IAAI,QAAQ;gBAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,MAAM;gBAAE,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;oBAAE,MAAM,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAClH,iFAAiF;YACjF,IAAI,CAAC;gBAAC,IAAI,YAAY,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG;oBAAE,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;QAChH,CAAC,CAAC;KACH,CAAC;AACJ,CAAC;AAED,yGAAyG;AACzG,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,MAAsB,EAAE,IAAY,EAAE,IAAc,EAAE,SAAS,GAAG,KAAK;IAClH,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,OAAO,QAAQ,CAAC,MAAM,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC3D,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAC3H,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACpF,IAAI,CAAC,QAAQ,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;IAClC,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,oBAAoB,QAAQ,CAAC,IAAI,GAAG,IAAI,EAAE,EAAE;YAClE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;YAC1H,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;SACpG,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC1B,CAAC","sourcesContent":["// The agent's local control channel: HTTP with a random token, over a named pipe on Windows (a random name) or a Unix\n// socket in a folder only this user can open, and for one more release also on 127.0.0.1 at a random port (the PowerShell\n// tray uses it; SCOPEBOND_AGENT_LOOPBACK=0 turns it off). The pipe or socket, the port, the token and the process id are\n// written to agent.json in the Scopebond home, which is how the CLI, the hook's override window and the tray find it.\n// A loopback port can be reached by any program on the computer; the pipe's name and the socket's folder cannot be found\n// or opened by another user. Every request must carry the token either way.\n\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { randomBytes, timingSafeEqual } from \"node:crypto\";\nimport { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { requestOverSocket } from \"@scopebond/hook\";\n\nexport const AGENT_FILE = \"agent.json\";\nexport const TOKEN_HEADER = \"x-scopebond-agent-token\";\n\n/** `socket`: the named pipe or Unix socket (absent from agents before it); `port`: 0 when loopback is off. */\nexport interface AgentEndpoint { port: number; socket?: string | null; token: string; pid: number; started_at: number; version: string }\n\n/** Where this start's pipe or socket goes. Windows: a pipe whose name is random, so no other user can guess it or claim it\n * first. Elsewhere: `run/agent.sock` in the Scopebond home (folder 0700), or a private folder under the temporary folder\n * when that path is longer than a Unix socket path may be. */\nexport function localSocketPath(dir: string, platform: NodeJS.Platform = process.platform, random = randomBytes(16).toString(\"hex\")): string {\n if (platform === \"win32\") return `\\\\\\\\.\\\\pipe\\\\scopebond-agent-${random}`;\n const inHome = join(dir, \"run\", \"agent.sock\");\n if (Buffer.byteLength(inHome) <= 100) return inHome;\n return join(mkdtempSync(join(tmpdir(), \"scopebond-\")), \"agent.sock\");\n}\n\nexport type Handler = (body: unknown) => Promise<unknown> | unknown;\n\nexport function readEndpoint(dir: string): AgentEndpoint | null {\n const file = join(dir, AGENT_FILE);\n if (!existsSync(file)) return null;\n try { return JSON.parse(readFileSync(file, \"utf8\")) as AgentEndpoint; } catch { return null; }\n}\n\n/** Start the control server. `routes` maps \"METHOD /path\" to a handler. */\nexport async function startControl(dir: string, version: string, routes: Record<string, Handler>): Promise<{ server: Server; endpoint: AgentEndpoint; close(): Promise<void> }> {\n const token = randomBytes(24).toString(\"base64url\");\n const expected = Buffer.from(token);\n const handle = async (req: IncomingMessage, res: ServerResponse) => {\n const send = (status: number, body: unknown) => { res.writeHead(status, { \"content-type\": \"application/json; charset=utf-8\", \"cache-control\": \"no-store\" }); res.end(JSON.stringify(body)); };\n const offered = Buffer.from(String(req.headers[TOKEN_HEADER] ?? \"\"));\n if (offered.length !== expected.length || !timingSafeEqual(offered, expected)) return send(401, { error: \"unauthorized\" });\n const handler = routes[`${req.method} ${(req.url ?? \"/\").split(\"?\")[0]}`];\n if (!handler) return send(404, { error: \"not found\" });\n let raw = \"\";\n for await (const chunk of req) { raw += chunk; if (raw.length > 64 * 1024) return send(413, { error: \"too large\" }); }\n try { send(200, await handler(raw ? JSON.parse(raw) : null)); }\n catch (error) { send(500, { error: (error as Error).message.slice(0, 200) }); }\n };\n // The pipe or socket first. A Unix socket's folder is created for this user only, and the socket itself is too.\n const socketPath = localSocketPath(dir);\n const local = createServer(handle);\n let socket: string | null = socketPath;\n try {\n if (process.platform !== \"win32\") {\n mkdirSync(join(socketPath, \"..\"), { recursive: true, mode: 0o700 });\n chmodSync(join(socketPath, \"..\"), 0o700);\n rmSync(socketPath, { force: true });\n }\n await new Promise<void>((resolve, reject) => { local.once(\"error\", reject); local.listen(socketPath, () => resolve()); });\n if (process.platform !== \"win32\") chmodSync(socketPath, 0o600);\n } catch { socket = null; }\n // Loopback for one more release (the PowerShell tray), unless turned off; always when the pipe could not be made.\n const loopback = process.env.SCOPEBOND_AGENT_LOOPBACK !== \"0\" || socket === null;\n const server = createServer(handle);\n if (loopback) await new Promise<void>((resolve, reject) => { server.once(\"error\", reject); server.listen(0, \"127.0.0.1\", () => resolve()); });\n const address = loopback ? server.address() : null;\n const port = typeof address === \"object\" && address ? address.port : 0;\n const endpoint: AgentEndpoint = { port, socket, token, pid: process.pid, started_at: Date.now(), version };\n const file = join(dir, AGENT_FILE);\n const temp = `${file}.${process.pid}.tmp`;\n writeFileSync(temp, JSON.stringify(endpoint, null, 2) + \"\\n\", { mode: 0o600 });\n renameSync(temp, file);\n return {\n server, endpoint,\n close: () => new Promise<void>((resolve) => {\n let open = (loopback ? 1 : 0) + (socket ? 1 : 0);\n const done = () => { if (--open <= 0) resolve(); };\n if (open === 0) resolve();\n if (loopback) server.close(done);\n if (socket) local.close(() => { if (process.platform !== \"win32\") rmSync(socketPath, { force: true }); done(); });\n // Only remove the file if it is still ours (a newer agent may have replaced it).\n try { if (readEndpoint(dir)?.pid === process.pid) rmSync(file, { force: true }); } catch { /* best effort */ }\n }),\n };\n}\n\n/** Call a running agent, over its pipe or socket when it has one. Returns null when no agent answers. */\nexport async function callAgent(dir: string, method: \"GET\" | \"POST\", path: string, body?: unknown, timeoutMs = 5_000): Promise<unknown | null> {\n const endpoint = readEndpoint(dir);\n if (!endpoint) return null;\n if (typeof endpoint.socket === \"string\" && endpoint.socket) {\n const answer = await requestOverSocket(endpoint.socket, method, path, { [TOKEN_HEADER]: endpoint.token }, body, timeoutMs);\n if (answer) return answer.status >= 200 && answer.status < 300 ? answer.body : null;\n if (!endpoint.port) return null;\n }\n try {\n const res = await fetch(`http://127.0.0.1:${endpoint.port}${path}`, {\n method, headers: { [TOKEN_HEADER]: endpoint.token, ...(body !== undefined ? { \"content-type\": \"application/json\" } : {}) },\n body: body !== undefined ? JSON.stringify(body) : undefined, signal: AbortSignal.timeout(timeoutMs),\n });\n return res.ok ? await res.json() : null;\n } catch { return null; }\n}\n"]}
package/dist/service.d.ts CHANGED
@@ -31,7 +31,13 @@ export interface ServiceOptions {
31
31
  tray?: boolean;
32
32
  /** Called once the agent has stopped because `stop` (or `autostart off`) asked it to; the CLI exits. */
33
33
  onStopped?: () => void;
34
+ /** Each delivery request's time limit (default 30 s) and one cycle's (default 10 minutes). A cycle that runs past its
35
+ * limit is counted as a failure and the next one is scheduled; nothing waits on it. */
36
+ deliveryTimeoutMs?: number;
37
+ cycleLimitMs?: number;
34
38
  }
39
+ /** How long one delivery cycle may run before the agent stops waiting for it. */
40
+ export declare const CYCLE_LIMIT_MS: number;
35
41
  export interface Service {
36
42
  stop(): Promise<void>;
37
43
  cycleNow(): Promise<CycleResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
package/dist/service.js CHANGED
@@ -6,7 +6,7 @@ import { spawn } from "node:child_process";
6
6
  import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
7
7
  import { hostname } from "node:os";
8
8
  import { join } from "node:path";
9
- import { actOnBlocked, blockedQuestion, hookVersion, isSingleExecutable, isManaged, loadConnection, localActivity, readBlocked, readMeta, ruleReport, runStoreUpkeep, userHarnessFile, writeHarnessConfig } from "@scopebond/hook";
9
+ import { actOnBlocked, blockedQuestion, hookVersion, isSingleExecutable, isManaged, loadConnection, localActivity, readBlocked, readMeta, ruleReport, runStoreUpkeep, writeDeliveryState, userHarnessFile, writeHarnessConfig } from "@scopebond/hook";
10
10
  import { computerStatus, expectedHarnesses, missingHookEntries, runCycle } from "./agent.js";
11
11
  import { agentCliPath } from "./self.js";
12
12
  import { fetchVerifiedInstaller, installAfterExit, installKind } from "./native-update.js";
@@ -52,6 +52,12 @@ export function writeTraySettings(dir, patch) {
52
52
  export const REFRESH_LAUNCHER_ENV = "SCOPEBOND_AGENT_REFRESH_LAUNCHER";
53
53
  /** The exit code that asks a service manager to start the agent again (systemd's Restart=on-failure), used where a detached replacement would not survive. */
54
54
  export const RESTART_EXIT_CODE = 75;
55
+ /** The agent's own delivery problem, kept apart from a hook call's cut-off. */
56
+ function recordAgentError(dir, message) {
57
+ writeDeliveryState(dir, { last_error: message, last_status: null, last_error_source: "agent", last_error_at: Date.now() });
58
+ }
59
+ /** How long one delivery cycle may run before the agent stops waiting for it. */
60
+ export const CYCLE_LIMIT_MS = 10 * 60_000;
55
61
  /** Put the Scopebond hook back into agent settings that lost it. A visible change: each one is
56
62
  * logged and returned, and only settings of agents this computer has are touched. */
57
63
  export function repairHookEntries(harnesses = expectedHarnesses()) {
@@ -198,6 +204,8 @@ export async function startService(options) {
198
204
  let timer = null;
199
205
  let maintenanceTimer = null;
200
206
  let running = null;
207
+ let cycleStartedAt = null;
208
+ const cycleLimit = options.cycleLimitMs ?? CYCLE_LIMIT_MS;
201
209
  let stopped = false;
202
210
  let lastSelfCheckAt = 0;
203
211
  // SB388: time asleep never counts as records waiting. A gap between cycles longer than the schedule allows means the
@@ -247,7 +255,21 @@ export async function startService(options) {
247
255
  if (lastCycleAt && started - lastCycleAt > allowedGap)
248
256
  awakeSince = started;
249
257
  lastCycleAt = started;
250
- running = runCycle({ dir: options.dir, fetchImpl: options.fetchImpl }).then(async (result) => {
258
+ cycleStartedAt = started;
259
+ // A cycle that does not finish within its limit is left behind (its requests time out on their own), counted as
260
+ // a failure, and the loop goes on. Nothing — the next cycle, Send now, stop — waits on it.
261
+ let limitTimer;
262
+ const overrun = new Promise((resolve) => {
263
+ limitTimer = setTimeout(() => resolve({ at: started, connected: true, delivered: 0, pending: computerStatus(options.dir).delivery.pending,
264
+ deliveryError: `a delivery cycle did not finish within ${Math.round(cycleLimit / 1000)} s`, rules: "skipped", missingHookEntries: [], requested: null }), cycleLimit);
265
+ limitTimer.unref?.();
266
+ });
267
+ const work = runCycle({ dir: options.dir, fetchImpl: options.fetchImpl, deliveryTimeoutMs: options.deliveryTimeoutMs });
268
+ running = Promise.race([work, overrun]).then(async (result) => {
269
+ if (limitTimer)
270
+ clearTimeout(limitTimer);
271
+ if (result.deliveryError?.startsWith("a delivery cycle did not finish"))
272
+ recordAgentError(options.dir, result.deliveryError);
251
273
  await sendReasons();
252
274
  try {
253
275
  noticeHealth();
@@ -272,7 +294,7 @@ export async function startService(options) {
272
294
  if (result.connected && Date.now() - summaryAt > SUMMARY_EVERY_MS)
273
295
  void refreshSummary();
274
296
  return result;
275
- }).finally(() => { running = null; });
297
+ }).finally(() => { running = null; cycleStartedAt = null; });
276
298
  return running;
277
299
  };
278
300
  const maintain = async (forceSelfCheck = false) => {
@@ -460,7 +482,7 @@ export async function startService(options) {
460
482
  log(`earlier block under "${q.title}": ${done.outcome}`);
461
483
  return { outcome: done.outcome, text: blockedText(done.outcome) };
462
484
  },
463
- "GET /status": () => ({ ...computerStatus(options.dir), health: health(), agent: { pid: process.pid, version: AGENT_VERSION, last_cycle: last, last_maintenance: lastMaintenance } }),
485
+ "GET /status": () => ({ ...computerStatus(options.dir), health: health(), agent: { pid: process.pid, version: AGENT_VERSION, last_cycle: last, cycle_started_at: cycleStartedAt, last_maintenance: lastMaintenance } }),
464
486
  "POST /flush": async () => ({ cycle: await cycle() }),
465
487
  "POST /repair": () => {
466
488
  const repaired = repairHookEntries();
@@ -504,13 +526,14 @@ export async function startService(options) {
504
526
  clearTimeout(timer);
505
527
  if (maintenanceTimer)
506
528
  clearInterval(maintenanceTimer);
507
- await running?.catch(() => undefined);
529
+ // A cycle under way gets a moment to finish; stop never waits on one that does not.
530
+ if (running)
531
+ await Promise.race([running.catch(() => undefined), new Promise((resolve) => setTimeout(resolve, 2_000).unref?.())]);
508
532
  await control.close();
509
533
  releaseAgentLock(lock);
510
534
  };
511
535
  log(`Scopebond Agent ${agentVersion()} running for ${options.dir} (control on 127.0.0.1:${control.endpoint.port})`);
512
- await cycle();
513
- schedule();
536
+ // Maintenance is armed before the first cycle, so a first cycle that hangs cannot keep it from ever starting.
514
537
  if (options.maintenance !== false) {
515
538
  // Shortly after start (so a fresh sign-in is not slowed), then every six hours.
516
539
  setTimeout(() => { if (!stopped)
@@ -519,6 +542,8 @@ export async function startService(options) {
519
542
  void maintain(); }, UPDATE_EVERY_MS);
520
543
  maintenanceTimer.unref?.();
521
544
  }
545
+ await cycle();
546
+ schedule();
522
547
  return { port: control.endpoint.port, cycleNow: cycle, maintainNow: () => maintain(true), stop };
523
548
  }
524
549
  /** What the tray says after a person acted on an earlier block. */
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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","sourcesContent":["// The resident loop: one cycle now, then one every interval, sooner after a change is asked for,\n// backing off while the workspace is unreachable. Exactly one agent serves a Scopebond home: a\n// second one finds the first answering and exits. Every six hours it asks the workspace which\n// versions to run, and once a day it runs the end-to-end self-check.\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport { join } from \"node:path\";\nimport { actOnBlocked, blockedQuestion, hookVersion, isSingleExecutable, isManaged, loadConnection, localActivity, readBlocked, readMeta, ruleReport, runStoreUpkeep, userHarnessFile, writeHarnessConfig, type Harness } from \"@scopebond/hook\";\nimport { computerStatus, expectedHarnesses, missingHookEntries, runCycle, type CycleResult } from \"./agent.js\";\nimport { agentCliPath } from \"./self.js\";\nimport { fetchVerifiedInstaller, installAfterExit, installKind } from \"./native-update.js\";\nimport { AGENT_LOG_ENV, launcherIsCurrent, launcherPath, refreshLauncher, startCommands } from \"./autostart.js\";\nimport { callAgent, startControl } from \"./ipc.js\";\nimport { runSelfCheck } from \"./selfcheck.js\";\nimport { flushReasons, queueReason } from \"./override-reasons.js\";\nimport { parseQuestion, serialized, systemPrompter, type Prompter } from \"./prompt.js\";\nimport { healthOf, type HealthLevel } from \"./health.js\";\nimport { notifyChange, startTray } from \"./tray.js\";\nimport { sendAllowancesAndRequests } from \"./allowance-sender.js\";\nimport { checkResult, trayModel, type RecentBlock, type TrayModel } from \"./tray-model.js\";\nimport { fetchComputerSummary, openInBrowser, sameOrigin, type ComputerSummary } from \"./summary.js\";\nimport { startReconnect, type ReconnectStart } from \"./reconnect.js\";\nimport { agentVersion, compareVersions, fetchClientVersion, installAgent, maintainHookEntries, maintainedHookCommand } from \"./update.js\";\n\nexport const AGENT_VERSION = `agent/${agentVersion()}`;\nconst INTERVAL_MS = 60_000;\nconst MAX_BACKOFF_MS = 15 * 60_000;\nconst UPDATE_EVERY_MS = 6 * 60 * 60 * 1000;\nconst SELF_CHECK_EVERY_MS = 24 * 60 * 60 * 1000;\nconst SUMMARY_EVERY_MS = 5 * 60 * 1000;\nexport const AFTER_PID_ENV = \"SCOPEBOND_AGENT_AFTER_PID\";\n/** The person's tray settings (D143: notifications about problems by default, none about blocks). */\nexport const TRAY_SETTINGS_FILE = \"agent-settings.json\";\nexport type NotificationSetting = \"all\" | \"problems\" | \"off\";\nexport interface TraySettings { notifications: NotificationSetting }\n\nexport function readTraySettings(dir: string): TraySettings {\n try {\n const parsed = JSON.parse(readFileSync(join(dir, TRAY_SETTINGS_FILE), \"utf8\")) as Partial<TraySettings>;\n return { notifications: parsed.notifications === \"all\" || parsed.notifications === \"off\" ? parsed.notifications : \"problems\" };\n } catch { return { notifications: \"problems\" }; }\n}\n\nexport function writeTraySettings(dir: string, patch: unknown): TraySettings {\n const next = readTraySettings(dir);\n const value = (patch as { notifications?: unknown } | null)?.notifications;\n if (value === \"all\" || value === \"problems\" || value === \"off\") next.notifications = value;\n writeFileSync(join(dir, TRAY_SETTINGS_FILE), JSON.stringify(next, null, 2) + \"\\n\", { mode: 0o600 });\n return next;\n}\n\n/** Set for a replacement started directly because the launcher is an older one: it rewrites the launcher once the old agent is gone. */\nexport const REFRESH_LAUNCHER_ENV = \"SCOPEBOND_AGENT_REFRESH_LAUNCHER\";\n/** The exit code that asks a service manager to start the agent again (systemd's Restart=on-failure), used where a detached replacement would not survive. */\nexport const RESTART_EXIT_CODE = 75;\n\nexport interface ServiceOptions {\n dir: string; intervalMs?: number; fetchImpl?: typeof fetch; log?: (line: string) => void;\n /** Off in tests: the update check and self-check run only when asked. */\n maintenance?: boolean;\n /** Called after a successful update instead of restarting (tests). */\n onUpdated?: (version: string) => void;\n /** Shows the Scopebond window for an override (tests replace it). */\n prompter?: Prompter;\n /** The Windows tray icon (default on Windows; SCOPEBOND_AGENT_TRAY=off turns it off). */\n tray?: boolean;\n /** Called once the agent has stopped because `stop` (or `autostart off`) asked it to; the CLI exits. */\n onStopped?: () => void;\n}\n\nexport interface Service {\n stop(): Promise<void>;\n cycleNow(): Promise<CycleResult>;\n maintainNow(): Promise<MaintenanceResult>;\n port: number;\n}\n\nexport interface MaintenanceResult {\n at: number;\n policy: \"recommended\" | \"hold\" | \"unknown\";\n updatedTo: string | null;\n hookEntries: Array<{ harness: Harness; file: string; reason: string }>;\n selfCheck: Awaited<ReturnType<typeof runSelfCheck>>;\n /** The local store's upkeep this pass (D144): rows rewritten, receipts past retention removed, space returned. */\n store?: ReturnType<typeof runStoreUpkeep>;\n error: string | null;\n}\n\n/** Put the Scopebond hook back into agent settings that lost it. A visible change: each one is\n * logged and returned, and only settings of agents this computer has are touched. */\nexport function repairHookEntries(harnesses: Harness[] = expectedHarnesses()): Array<{ harness: Harness; file: string }> {\n const repaired: Array<{ harness: Harness; file: string }> = [];\n for (const harness of missingHookEntries(harnesses)) {\n const file = writeHarnessConfig(userHarnessFile(harness), harness, maintainedHookCommand(harness));\n repaired.push({ harness, file });\n }\n return repaired;\n}\n\n/** How the updated agent takes over from this one. */\nexport type Handover =\n | { kind: \"spawn\"; command: string; args: string[]; env: Record<string, string | undefined>; verbatim: boolean }\n | { kind: \"service-restart\" };\n\n/** Decide how to start the updated agent. Through the autostart launcher when there is one, so it finds Node the same way\n * sign-in does and is restarted after a crash. On Windows an older launcher cannot do it: it redirects the agent's output\n * into agent.log, this agent's children inherit that handle, and the new launcher's own redirect then fails, which cmd\n * takes for a clean exit. So under an older launcher the updated agent starts directly and rewrites the launcher once\n * this one is gone. Under systemd a detached child is stopped with the service, so the service manager restarts it. */\nexport function handoverPlan(o: {\n dir: string; cli: string; pid: number; execPath: string; platform: NodeJS.Platform;\n /** The single executable: the program is the agent itself, with no cli.js and no Node options. */\n singleExecutable?: boolean;\n env: Record<string, string | undefined>; launcherText: string | null;\n}): Handover {\n if (o.platform === \"linux\" && o.env.INVOCATION_ID) return { kind: \"service-restart\" };\n const launcher = launcherPath(o.dir, o.platform);\n const env = { ...o.env, [AFTER_PID_ENV]: String(o.pid), [AGENT_LOG_ENV]: join(o.dir, \"agent.log\") };\n if (o.launcherText !== null && launcherIsCurrent(o.launcherText, o.platform)) {\n if (o.platform === \"win32\") {\n const [command, args] = startCommands(launcher, o.platform)[1];\n return { kind: \"spawn\", command, args, env, verbatim: true };\n }\n return { kind: \"spawn\", command: \"/bin/sh\", args: [launcher], env, verbatim: false };\n }\n return {\n kind: \"spawn\", command: o.execPath, args: o.singleExecutable ? [\"run\"] : [\"--disable-warning=ExperimentalWarning\", o.cli, \"run\"], verbatim: false,\n env: o.launcherText !== null ? { ...env, [REFRESH_LAUNCHER_ENV]: \"1\" } : env,\n };\n}\n\n/** Start the updated agent, which waits for this process to exit. Returns false when a service manager restarts it instead\n * (this process then exits with RESTART_EXIT_CODE). */\nexport function spawnReplacement(dir: string, cli = agentCliPath()): boolean {\n let launcherText: string | null = null;\n try { launcherText = readFileSync(launcherPath(dir), \"utf8\"); } catch { /* no autostart launcher */ }\n const plan = handoverPlan({ dir, cli, pid: process.pid, execPath: process.execPath, platform: process.platform, env: process.env, launcherText, singleExecutable: isSingleExecutable() });\n if (plan.kind === \"service-restart\") return false;\n const child = spawn(plan.command, plan.args, { env: plan.env, detached: true, stdio: \"ignore\", windowsHide: true, windowsVerbatimArguments: plan.verbatim });\n child.on(\"error\", () => { /* the next sign-in starts it */ });\n child.unref();\n return true;\n}\n\n/** The first thing a starting agent does: wait for the agent it replaces to exit, then, when asked, rewrite the older launcher\n * that agent ran under (by now nothing runs it). The handover variables are dropped, so a later restart waits for nothing. */\nexport async function takeOver(dir: string, cli: string, waitMs = 30_000): Promise<void> {\n const previous = Number(process.env[AFTER_PID_ENV]);\n const refresh = process.env[REFRESH_LAUNCHER_ENV] === \"1\";\n delete process.env[AFTER_PID_ENV];\n delete process.env[REFRESH_LAUNCHER_ENV];\n if (Number.isInteger(previous) && previous > 0) await waitForExit(previous, waitMs);\n if (!refresh) return;\n // The old launcher reads its last lines after its agent exits; give it a moment to finish before its file changes.\n await new Promise((r) => setTimeout(r, 2_000));\n try { if (refreshLauncher(dir, cli)) console.log(`${new Date().toISOString()} rewrote the autostart launcher for this version`); }\n catch (error) { console.log(`${new Date().toISOString()} could not rewrite the autostart launcher: ${(error as Error).message}`); }\n}\n\nexport async function waitForExit(pid: number, timeoutMs: number): Promise<void> {\n const until = Date.now() + timeoutMs;\n while (Date.now() < until) {\n try { process.kill(pid, 0); } catch { return; }\n await new Promise((r) => setTimeout(r, 250));\n }\n}\n\nexport const AGENT_LOCK = \"agent.lock\";\n\n/** How long a lock whose agent never answered on its local channel still counts as an agent starting up. */\nexport const AGENT_LOCK_STARTING_MS = 60_000;\n\n/** Take the one-agent-per-home lock: created exclusively, or taken over when the agent named in it is\n * gone. The caller has already found no agent answering on the local channel, so a lock older than\n * AGENT_LOCK_STARTING_MS is left over even when its process id is alive: Windows reuses process ids\n * soon after a restart, and a sign-in after a reboot must not find \"already running\". A newer lock\n * is an agent still starting (or one whose id is not written yet): this start gives way.\n * Returns the lock file, or null while another agent holds it. */\nexport function acquireAgentLock(dir: string, now = Date.now()): string | null {\n const file = join(dir, AGENT_LOCK);\n mkdirSync(dir, { recursive: true });\n const mine = `${process.pid} ${now}`;\n try { writeFileSync(file, mine, { flag: \"wx\" }); return file; } catch { /* held, or left behind */ }\n let text = \"\";\n let written = now;\n try { text = readFileSync(file, \"utf8\").trim(); written = statSync(file).mtimeMs; } catch { /* removed meanwhile */ }\n const [pidText, atText] = text.split(/\\s+/);\n const holder = Number(pidText);\n const at = Number(atText);\n const since = Number.isFinite(at) && at > 0 ? at : written;\n const fresh = now - since < AGENT_LOCK_STARTING_MS;\n if (holder !== process.pid && fresh) {\n if (!(Number.isInteger(holder) && holder > 0)) return null; // being written by an agent starting this moment\n try { process.kill(holder, 0); return null; } catch { /* that agent is gone: take the lock over */ }\n }\n writeFileSync(file, mine);\n return file;\n}\n\nexport function releaseAgentLock(file: string): void {\n try { if (readFileSync(file, \"utf8\").trim().split(/\\s+/)[0] === String(process.pid)) rmSync(file, { force: true }); } catch { /* already gone */ }\n}\n\nexport async function startService(options: ServiceOptions): Promise<Service> {\n const log = options.log ?? ((line: string) => console.log(`${new Date().toISOString()} ${line}`));\n if (await callAgent(options.dir, \"GET\", \"/status\", undefined, 2_000)) throw new Error(\"a Scopebond Agent is already running for this computer\");\n // Two agents started within moments of each other (a slow first start, then a second start) both see\n // nobody answering yet: the lock file decides, so the second exits instead of orphaning the first.\n const lock = acquireAgentLock(options.dir);\n if (!lock) throw new Error(\"a Scopebond Agent is already running for this computer\");\n const interval = options.intervalMs ?? INTERVAL_MS;\n let last: CycleResult | null = null;\n let lastMaintenance: MaintenanceResult | null = null;\n let failures = 0;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let maintenanceTimer: ReturnType<typeof setInterval> | null = null;\n let running: Promise<CycleResult> | null = null;\n let stopped = false;\n let lastSelfCheckAt = 0;\n // SB388: time asleep never counts as records waiting. A gap between cycles longer than the schedule allows means the\n // computer slept (or the agent was stopped); waiting is counted again from the next cycle.\n let awakeSince = Date.now();\n let lastCycleAt = 0;\n // A long step the person should see as \"working\" (an update), or null.\n let working: string | null = null;\n // SB391: the workspace's own summary for the tray (names, links, open reviews), refreshed every five minutes; null when the\n // workspace does not answer it (a self-hosted gateway), and the tray simply leaves those rows out.\n // SB392: a sign-in started from the tray, while it waits for approval.\n let reconnecting: ReconnectStart | null = null;\n let summary: ComputerSummary | null = null;\n let summaryAt = 0;\n const refreshSummary = async () => {\n summaryAt = Date.now();\n const connection = loadConnection(options.dir);\n summary = connection ? await fetchComputerSummary(connection, options.fetchImpl) : null;\n };\n\n const prompt = serialized(options.prompter ?? systemPrompter);\n let shownLevel: HealthLevel | null = null;\n const health = () => healthOf(computerStatus(options.dir), lastMaintenance?.selfCheck ?? null);\n const noticeHealth = () => { const h = health(); notifyChange(shownLevel, h); shownLevel = h.level; };\n const sendReasons = async () => {\n const connection = loadConnection(options.dir);\n if (!connection) return;\n try { const sent = await flushReasons(options.dir, connection, options.fetchImpl); if (sent) log(`sent ${sent} override reason(s) to the workspace`); }\n catch { /* they wait for the next cycle */ }\n // D144: allowances a person made here, and their requests to an admin.\n try {\n const sent = await sendAllowancesAndRequests(options.dir, connection, options.fetchImpl);\n if (sent.allowances || sent.requests) log(`sent ${sent.allowances} allowance(s) and ${sent.requests} request(s) to the workspace`);\n } catch { /* they wait for the next cycle */ }\n };\n const cycle = async (): Promise<CycleResult> => {\n if (running) return running;\n const started = Date.now();\n const allowedGap = failures ? MAX_BACKOFF_MS + 5 * 60_000 : Math.max(3 * interval, 5 * 60_000);\n if (lastCycleAt && started - lastCycleAt > allowedGap) awakeSince = started;\n lastCycleAt = started;\n running = runCycle({ dir: options.dir, fetchImpl: options.fetchImpl }).then(async (result) => {\n await sendReasons();\n try { noticeHealth(); } catch { /* status is best effort */ }\n last = result;\n failures = result.deliveryError ? failures + 1 : 0;\n if (result.delivered) log(`delivered ${result.delivered} record(s); ${result.pending} waiting`);\n if (result.deliveryError) log(`delivery problem: ${result.deliveryError}`);\n // SB390: asked from the workspace's computer page. Send again now (the cycle sent before its rules check), or run the\n // self-check; each once.\n if (result.requested === \"flush\") { log(\"the workspace asked this computer to send now\"); setTimeout(() => { void cycle(); }, 0); }\n if (result.requested === \"self_check\") { log(\"the workspace asked this computer to check now\"); setTimeout(() => { void maintain(true); }, 0); }\n if (result.connected && Date.now() - summaryAt > SUMMARY_EVERY_MS) void refreshSummary();\n return result;\n }).finally(() => { running = null; });\n return running;\n };\n\n const maintain = async (forceSelfCheck = false): Promise<MaintenanceResult> => {\n const result: MaintenanceResult = { at: Date.now(), policy: \"unknown\", updatedTo: null, hookEntries: [], selfCheck: null, error: null };\n try {\n const connection = loadConnection(options.dir);\n const harnesses = expectedHarnesses();\n const target = connection ? await fetchClientVersion(connection, options.fetchImpl) : null;\n if (target) result.policy = target.policy;\n const current = agentVersion();\n // Hook entries first: under \"recommended\" they move to the newest hook named or carried; under \"hold\" only broken ones\n // are repaired, at the version they already name or the one the agent carries. Done before a self-update, so an update\n // whose handover fails never leaves the hook behind.\n const hookTarget = target?.policy === \"recommended\" && target.hook && compareVersions(target.hook, hookVersion()) > 0 ? target.hook : hookVersion();\n result.hookEntries = maintainHookEntries(harnesses, hookTarget, target?.policy !== \"hold\");\n for (const change of result.hookEntries) log(`${change.reason}: ${change.file}`);\n const kind = installKind();\n if (target?.policy === \"recommended\" && target.agent && compareVersions(target.agent, current) > 0 && kind === \"per-machine\") {\n // Installed for every user: the organisation that deployed it updates it.\n log(`the workspace recommends the Scopebond Agent ${target.agent}; this install is updated by your organization`);\n } else if (target?.policy === \"recommended\" && target.agent && compareVersions(target.agent, current) > 0 && kind === \"per-user\") {\n log(`updating the Scopebond Agent ${current} -> ${target.agent} (signed installer)`);\n const verified = await fetchVerifiedInstaller(target.agent, { dir: options.dir, fetchImpl: options.fetchImpl });\n if (verified.ok) {\n result.updatedTo = target.agent;\n lastMaintenance = result;\n if (options.onUpdated) options.onUpdated(target.agent);\n else {\n // The installer replaces this file, so it runs after this agent has exited, and starts the agent again.\n installAfterExit(verified.path, process.pid, existsSync(launcherPath(options.dir)) ? launcherPath(options.dir) : null);\n setTimeout(() => { setTimeout(() => process.exit(0), 5_000).unref(); void stop().finally(() => process.exit(0)); }, 500);\n }\n return result;\n }\n result.error = `update to ${target.agent} could not be verified: ${verified.reason}; nothing was installed`;\n log(result.error);\n } else if (target?.policy === \"recommended\" && target.agent && compareVersions(target.agent, current) > 0) {\n log(`updating the Scopebond Agent ${current} -> ${target.agent}`);\n const installed = await installAgent(target.agent);\n if (installed.ok) {\n result.updatedTo = target.agent;\n lastMaintenance = result;\n if (options.onUpdated) options.onUpdated(target.agent);\n else {\n const code = spawnReplacement(options.dir) ? 0 : RESTART_EXIT_CODE;\n // Hand over: stop, then exit. A stop that never finishes (a tray or window child that will not close) must not\n // keep the old agent alive with the replacement waiting on it, so the exit has a hard deadline.\n setTimeout(() => { setTimeout(() => process.exit(code), 5_000).unref(); void stop().finally(() => process.exit(code)); }, 500);\n }\n return result;\n }\n result.error = `update to ${target.agent} failed: ${installed.output.slice(-300)}`;\n log(result.error);\n }\n // The local store: older rows rewritten, acknowledged receipts past the retention window removed, space returned.\n // The agent may rewrite the whole file once (the hook never does that during a tool call).\n try {\n result.store = runStoreUpkeep(options.dir, { budgetMs: 30_000, allowFullVacuum: true });\n const kept = result.store;\n if (kept && (kept.migrated || kept.receiptsRemoved || kept.stateRemoved || kept.pagesFreed)) {\n log(`local store: ${kept.migrated} row(s) rewritten, ${kept.receiptsRemoved} receipt(s) past retention removed, ${kept.stateRemoved} finished check record(s) removed, ${kept.pagesFreed} page(s) returned`);\n }\n } catch (error) { log(`local store upkeep failed: ${(error as Error).message}`); }\n if (connection && (forceSelfCheck || Date.now() - lastSelfCheckAt >= SELF_CHECK_EVERY_MS)) {\n result.selfCheck = await runSelfCheck(options.dir, connection, harnesses, current, { fetchImpl: options.fetchImpl });\n lastSelfCheckAt = Date.now();\n log(`self-check ${result.selfCheck?.ok ? \"passed\" : `failed: ${result.selfCheck?.failed.join(\", \") ?? \"no result\"}`}`);\n }\n } catch (error) { result.error = (error as Error).message; }\n lastMaintenance = result;\n return result;\n };\n\n const schedule = () => {\n if (stopped) return;\n const delay = failures ? Math.min(MAX_BACKOFF_MS, interval * 2 ** Math.min(failures, 6)) : interval;\n timer = setTimeout(() => { void cycle().finally(schedule); }, delay);\n };\n\n // D144: a block under a rule a person may allow or ask about can be acted on afterwards, from the tray.\n const actionable = (blocks: Array<{ action_id: string | null; summary: string; at: string; rule: string | null }>, now: number): RecentBlock[] => {\n const known = new Map(readBlocked(options.dir).map((b) => [b.id, b]));\n return blocks.map((b) => {\n const item = b.action_id ? known.get(b.action_id) : undefined;\n return { ...b, rule: b.rule ?? item?.rule ?? null, can_act: Boolean(item && b.action_id && blockedQuestion(options.dir, b.action_id, now)), acted: item?.acted ?? null };\n });\n };\n\n const trayState = (): TrayModel => {\n const now = Date.now();\n const status = computerStatus(options.dir);\n const meta = readMeta(options.dir);\n const report = ruleReport(options.dir);\n const modes = report ? Object.values(report).map(([mode]) => mode) : [];\n const activity = localActivity(options.dir);\n return trayModel({\n status, health: health(), now, awakeSince, working,\n rules: report ? { checked_at: meta.checked_at ? Date.parse(meta.checked_at) : null, managed: isManaged(options.dir), block: modes.filter((m) => m === \"enforce\").length, monitor: modes.filter((m) => m === \"monitor\").length } : null,\n today: activity?.today ?? null,\n recentBlocks: actionable(activity?.recent_blocks ?? [], now),\n version: { agent: agentVersion(), hook: hookVersion(), policy: lastMaintenance?.policy ?? \"unknown\", recommendedAgent: meta.recommended?.agent ?? null, recommendedHook: meta.recommended?.hook ?? null },\n workspace: summary ? { name: summary.workspace_name, environment: summary.environment_name, computer_url: summary.computer_url } : null,\n openReviews: summary?.open_reviews ?? 0,\n computerName: hostname(),\n canReconnect: true,\n });\n };\n\n const control = await startControl(options.dir, `${AGENT_VERSION} hook/${hookVersion()}`, {\n // SB387: what the tray draws, and the person's tray settings.\n \"GET /tray\": () => ({ tray: trayState(), settings: readTraySettings(options.dir) }),\n \"POST /settings\": (body) => ({ settings: writeTraySettings(options.dir, body) }),\n // SB387: \"Check now\" always says what it found.\n \"POST /check\": async () => {\n const result = await maintain(true);\n return { text: checkResult(result.selfCheck, result.error), tray: trayState() };\n },\n // SB389: install the version the workspace recommends now, instead of at the next six-hourly check.\n \"POST /update\": async () => {\n working = \"Updating Scopebond…\";\n try { const result = await maintain(true); return { updated_to: result.updatedTo, error: result.error }; }\n finally { working = null; }\n },\n // SB389: the newest blocks on this computer, summarised the way `log` prints them (never raw arguments).\n // SB391: open this computer's page in the workspace, only on the workspace this computer is connected to.\n \"POST /open-workspace\": () => {\n const connection = loadConnection(options.dir);\n const url = summary?.computer_url;\n if (!connection || !url || !sameOrigin(url, connection.url)) return { opened: false };\n openInBrowser(url);\n return { opened: true };\n },\n // SB392: sign in again for the connected workspace; the tray shows the code, the approval page opens in the browser.\n \"POST /reconnect\": async () => {\n if (reconnecting) return reconnecting;\n working = \"Signing in again…\";\n const result = await startReconnect(options.dir, (ok) => {\n reconnecting = null; working = null;\n log(ok ? \"signed in again\" : \"the sign-in did not finish\");\n if (ok) void cycle();\n });\n if (\"error\" in result) { working = null; return { error: result.error }; }\n reconnecting = result.started;\n log(`signing in again: waiting for approval of code ${result.started.user_code}`);\n return result.started;\n },\n \"GET /recent-blocks\": () => ({ recent_blocks: actionable(localActivity(options.dir, { limit: 10 })?.recent_blocks ?? [], Date.now()) }),\n // D144: allow an earlier block, or ask an admin, from the tray. The caller names the block; only the Scopebond window\n // answers (the same window the hook uses), so whoever calls this channel cannot allow anything by itself. Scopebond never\n // runs the action: the person or the coding agent runs it again.\n \"POST /blocked\": async (body) => {\n const id = typeof (body as { action_id?: unknown } | null)?.action_id === \"string\" ? String((body as { action_id: string }).action_id) : \"\";\n const q = id ? blockedQuestion(options.dir, id) : null;\n if (!q) return { outcome: \"gone\", text: \"This block can no longer be allowed or sent to an admin from here.\" };\n log(`asking about an earlier block under \"${q.title}\"`);\n const answer = await prompt({ action_id: id, rule: q.item.rule, title: q.title, summary: q.item.summary, reason_min: q.reason_min,\n lasts: \"the next time it runs\", timeout_ms: 55_000, mode: q.mode, offers: q.offers });\n if (answer.decision === \"unavailable\") return { outcome: \"unavailable\", text: \"The Scopebond window could not be shown.\" };\n const done = actOnBlocked(options.dir, id, answer);\n if (done.outcome === \"allowed\" || done.outcome === \"proposed\" || done.outcome === \"asked\") setTimeout(() => { void sendReasons(); }, 1_000);\n log(`earlier block under \"${q.title}\": ${done.outcome}`);\n return { outcome: done.outcome, text: blockedText(done.outcome) };\n },\n \"GET /status\": () => ({ ...computerStatus(options.dir), health: health(), agent: { pid: process.pid, version: AGENT_VERSION, last_cycle: last, last_maintenance: lastMaintenance } }),\n \"POST /flush\": async () => ({ cycle: await cycle() }),\n \"POST /repair\": () => {\n const repaired = repairHookEntries();\n for (const r of repaired) log(`repaired the Scopebond hook entry in ${r.file}`);\n return { repaired };\n },\n \"POST /maintain\": async () => ({ maintenance: await maintain(true) }),\n // `scopebond-agent stop` and `autostart off`: answer first, then stop, so the caller hears back.\n \"POST /stop\": () => {\n log(\"stopping: asked to by this computer's user\");\n setTimeout(() => { void stop().finally(() => options.onStopped?.()); }, 50);\n return { stopping: true };\n },\n // Warn mode: the hook asks; only the window answers. The caller learns the answer, never decides it.\n \"POST /override\": async (body) => {\n const question = parseQuestion(body);\n if (!question) return { decision: \"unavailable\" };\n log(`asking whether to allow \"${question.title}\"`);\n const answer = await prompt(question);\n log(`override ${answer.decision === \"allow\" ? \"given\" : answer.decision === \"deny\" ? \"not given\" : \"not available\"} for \"${question.title}\"`);\n if (answer.decision === \"allow\" && answer.reason) { queueReason(options.dir, question.action_id, answer.reason); setTimeout(() => { void sendReasons(); }, 1_000); }\n // The hook writes the request or allowance after this answer; send it shortly after.\n if (answer.decision === \"ask\" || answer.lasts === \"always\" || answer.lasts === \"15m\") setTimeout(() => { void sendReasons(); }, 2_000);\n return answer;\n },\n });\n const tray = (options.tray ?? true) && process.env.SCOPEBOND_AGENT_TRAY !== \"off\" ? startTray(options.dir) : null;\n const stop = async () => {\n stopped = true;\n try { tray?.kill(); } catch { /* already gone */ }\n if (timer) clearTimeout(timer);\n if (maintenanceTimer) clearInterval(maintenanceTimer);\n await running?.catch(() => undefined);\n await control.close();\n releaseAgentLock(lock);\n };\n log(`Scopebond Agent ${agentVersion()} running for ${options.dir} (control on 127.0.0.1:${control.endpoint.port})`);\n await cycle();\n schedule();\n if (options.maintenance !== false) {\n // Shortly after start (so a fresh sign-in is not slowed), then every six hours.\n setTimeout(() => { if (!stopped) void maintain(); }, 2 * 60_000).unref?.();\n maintenanceTimer = setInterval(() => { if (!stopped) void maintain(); }, UPDATE_EVERY_MS);\n maintenanceTimer.unref?.();\n }\n return { port: control.endpoint.port, cycleNow: cycle, maintainNow: () => maintain(true), stop };\n}\n\n/** What the tray says after a person acted on an earlier block. */\nexport function blockedText(outcome: \"allowed\" | \"proposed\" | \"asked\" | \"declined\" | \"gone\"): string {\n switch (outcome) {\n case \"allowed\": return \"Allowed. Run it again, or let the coding agent retry.\";\n case \"proposed\": return \"Allowed for 15 minutes. Your workspace's admins decide whether it stands; run it again meanwhile.\";\n case \"asked\": return \"Asked your workspace's admins. The Scopebond icon shows their answer; then run it again.\";\n case \"declined\": return \"Nothing was allowed.\";\n case \"gone\": return \"This block can no longer be allowed or sent to an admin from here.\";\n }\n}\n"]}
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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","sourcesContent":["// The resident loop: one cycle now, then one every interval, sooner after a change is asked for,\n// backing off while the workspace is unreachable. Exactly one agent serves a Scopebond home: a\n// second one finds the first answering and exits. Every six hours it asks the workspace which\n// versions to run, and once a day it runs the end-to-end self-check.\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport { join } from \"node:path\";\nimport { actOnBlocked, blockedQuestion, hookVersion, isSingleExecutable, isManaged, loadConnection, localActivity, readBlocked, readMeta, ruleReport, runStoreUpkeep, writeDeliveryState, userHarnessFile, writeHarnessConfig, type Harness } from \"@scopebond/hook\";\nimport { computerStatus, expectedHarnesses, missingHookEntries, runCycle, type CycleResult } from \"./agent.js\";\nimport { agentCliPath } from \"./self.js\";\nimport { fetchVerifiedInstaller, installAfterExit, installKind } from \"./native-update.js\";\nimport { AGENT_LOG_ENV, launcherIsCurrent, launcherPath, refreshLauncher, startCommands } from \"./autostart.js\";\nimport { callAgent, startControl } from \"./ipc.js\";\nimport { runSelfCheck } from \"./selfcheck.js\";\nimport { flushReasons, queueReason } from \"./override-reasons.js\";\nimport { parseQuestion, serialized, systemPrompter, type Prompter } from \"./prompt.js\";\nimport { healthOf, type HealthLevel } from \"./health.js\";\nimport { notifyChange, startTray } from \"./tray.js\";\nimport { sendAllowancesAndRequests } from \"./allowance-sender.js\";\nimport { checkResult, trayModel, type RecentBlock, type TrayModel } from \"./tray-model.js\";\nimport { fetchComputerSummary, openInBrowser, sameOrigin, type ComputerSummary } from \"./summary.js\";\nimport { startReconnect, type ReconnectStart } from \"./reconnect.js\";\nimport { agentVersion, compareVersions, fetchClientVersion, installAgent, maintainHookEntries, maintainedHookCommand } from \"./update.js\";\n\nexport const AGENT_VERSION = `agent/${agentVersion()}`;\nconst INTERVAL_MS = 60_000;\nconst MAX_BACKOFF_MS = 15 * 60_000;\nconst UPDATE_EVERY_MS = 6 * 60 * 60 * 1000;\nconst SELF_CHECK_EVERY_MS = 24 * 60 * 60 * 1000;\nconst SUMMARY_EVERY_MS = 5 * 60 * 1000;\nexport const AFTER_PID_ENV = \"SCOPEBOND_AGENT_AFTER_PID\";\n/** The person's tray settings (D143: notifications about problems by default, none about blocks). */\nexport const TRAY_SETTINGS_FILE = \"agent-settings.json\";\nexport type NotificationSetting = \"all\" | \"problems\" | \"off\";\nexport interface TraySettings { notifications: NotificationSetting }\n\nexport function readTraySettings(dir: string): TraySettings {\n try {\n const parsed = JSON.parse(readFileSync(join(dir, TRAY_SETTINGS_FILE), \"utf8\")) as Partial<TraySettings>;\n return { notifications: parsed.notifications === \"all\" || parsed.notifications === \"off\" ? parsed.notifications : \"problems\" };\n } catch { return { notifications: \"problems\" }; }\n}\n\nexport function writeTraySettings(dir: string, patch: unknown): TraySettings {\n const next = readTraySettings(dir);\n const value = (patch as { notifications?: unknown } | null)?.notifications;\n if (value === \"all\" || value === \"problems\" || value === \"off\") next.notifications = value;\n writeFileSync(join(dir, TRAY_SETTINGS_FILE), JSON.stringify(next, null, 2) + \"\\n\", { mode: 0o600 });\n return next;\n}\n\n/** Set for a replacement started directly because the launcher is an older one: it rewrites the launcher once the old agent is gone. */\nexport const REFRESH_LAUNCHER_ENV = \"SCOPEBOND_AGENT_REFRESH_LAUNCHER\";\n/** The exit code that asks a service manager to start the agent again (systemd's Restart=on-failure), used where a detached replacement would not survive. */\nexport const RESTART_EXIT_CODE = 75;\n\nexport interface ServiceOptions {\n dir: string; intervalMs?: number; fetchImpl?: typeof fetch; log?: (line: string) => void;\n /** Off in tests: the update check and self-check run only when asked. */\n maintenance?: boolean;\n /** Called after a successful update instead of restarting (tests). */\n onUpdated?: (version: string) => void;\n /** Shows the Scopebond window for an override (tests replace it). */\n prompter?: Prompter;\n /** The Windows tray icon (default on Windows; SCOPEBOND_AGENT_TRAY=off turns it off). */\n tray?: boolean;\n /** Called once the agent has stopped because `stop` (or `autostart off`) asked it to; the CLI exits. */\n onStopped?: () => void;\n /** Each delivery request's time limit (default 30 s) and one cycle's (default 10 minutes). A cycle that runs past its\n * limit is counted as a failure and the next one is scheduled; nothing waits on it. */\n deliveryTimeoutMs?: number;\n cycleLimitMs?: number;\n}\n\n/** The agent's own delivery problem, kept apart from a hook call's cut-off. */\nfunction recordAgentError(dir: string, message: string): void {\n writeDeliveryState(dir, { last_error: message, last_status: null, last_error_source: \"agent\", last_error_at: Date.now() });\n}\n\n/** How long one delivery cycle may run before the agent stops waiting for it. */\nexport const CYCLE_LIMIT_MS = 10 * 60_000;\n\nexport interface Service {\n stop(): Promise<void>;\n cycleNow(): Promise<CycleResult>;\n maintainNow(): Promise<MaintenanceResult>;\n port: number;\n}\n\nexport interface MaintenanceResult {\n at: number;\n policy: \"recommended\" | \"hold\" | \"unknown\";\n updatedTo: string | null;\n hookEntries: Array<{ harness: Harness; file: string; reason: string }>;\n selfCheck: Awaited<ReturnType<typeof runSelfCheck>>;\n /** The local store's upkeep this pass (D144): rows rewritten, receipts past retention removed, space returned. */\n store?: ReturnType<typeof runStoreUpkeep>;\n error: string | null;\n}\n\n/** Put the Scopebond hook back into agent settings that lost it. A visible change: each one is\n * logged and returned, and only settings of agents this computer has are touched. */\nexport function repairHookEntries(harnesses: Harness[] = expectedHarnesses()): Array<{ harness: Harness; file: string }> {\n const repaired: Array<{ harness: Harness; file: string }> = [];\n for (const harness of missingHookEntries(harnesses)) {\n const file = writeHarnessConfig(userHarnessFile(harness), harness, maintainedHookCommand(harness));\n repaired.push({ harness, file });\n }\n return repaired;\n}\n\n/** How the updated agent takes over from this one. */\nexport type Handover =\n | { kind: \"spawn\"; command: string; args: string[]; env: Record<string, string | undefined>; verbatim: boolean }\n | { kind: \"service-restart\" };\n\n/** Decide how to start the updated agent. Through the autostart launcher when there is one, so it finds Node the same way\n * sign-in does and is restarted after a crash. On Windows an older launcher cannot do it: it redirects the agent's output\n * into agent.log, this agent's children inherit that handle, and the new launcher's own redirect then fails, which cmd\n * takes for a clean exit. So under an older launcher the updated agent starts directly and rewrites the launcher once\n * this one is gone. Under systemd a detached child is stopped with the service, so the service manager restarts it. */\nexport function handoverPlan(o: {\n dir: string; cli: string; pid: number; execPath: string; platform: NodeJS.Platform;\n /** The single executable: the program is the agent itself, with no cli.js and no Node options. */\n singleExecutable?: boolean;\n env: Record<string, string | undefined>; launcherText: string | null;\n}): Handover {\n if (o.platform === \"linux\" && o.env.INVOCATION_ID) return { kind: \"service-restart\" };\n const launcher = launcherPath(o.dir, o.platform);\n const env = { ...o.env, [AFTER_PID_ENV]: String(o.pid), [AGENT_LOG_ENV]: join(o.dir, \"agent.log\") };\n if (o.launcherText !== null && launcherIsCurrent(o.launcherText, o.platform)) {\n if (o.platform === \"win32\") {\n const [command, args] = startCommands(launcher, o.platform)[1];\n return { kind: \"spawn\", command, args, env, verbatim: true };\n }\n return { kind: \"spawn\", command: \"/bin/sh\", args: [launcher], env, verbatim: false };\n }\n return {\n kind: \"spawn\", command: o.execPath, args: o.singleExecutable ? [\"run\"] : [\"--disable-warning=ExperimentalWarning\", o.cli, \"run\"], verbatim: false,\n env: o.launcherText !== null ? { ...env, [REFRESH_LAUNCHER_ENV]: \"1\" } : env,\n };\n}\n\n/** Start the updated agent, which waits for this process to exit. Returns false when a service manager restarts it instead\n * (this process then exits with RESTART_EXIT_CODE). */\nexport function spawnReplacement(dir: string, cli = agentCliPath()): boolean {\n let launcherText: string | null = null;\n try { launcherText = readFileSync(launcherPath(dir), \"utf8\"); } catch { /* no autostart launcher */ }\n const plan = handoverPlan({ dir, cli, pid: process.pid, execPath: process.execPath, platform: process.platform, env: process.env, launcherText, singleExecutable: isSingleExecutable() });\n if (plan.kind === \"service-restart\") return false;\n const child = spawn(plan.command, plan.args, { env: plan.env, detached: true, stdio: \"ignore\", windowsHide: true, windowsVerbatimArguments: plan.verbatim });\n child.on(\"error\", () => { /* the next sign-in starts it */ });\n child.unref();\n return true;\n}\n\n/** The first thing a starting agent does: wait for the agent it replaces to exit, then, when asked, rewrite the older launcher\n * that agent ran under (by now nothing runs it). The handover variables are dropped, so a later restart waits for nothing. */\nexport async function takeOver(dir: string, cli: string, waitMs = 30_000): Promise<void> {\n const previous = Number(process.env[AFTER_PID_ENV]);\n const refresh = process.env[REFRESH_LAUNCHER_ENV] === \"1\";\n delete process.env[AFTER_PID_ENV];\n delete process.env[REFRESH_LAUNCHER_ENV];\n if (Number.isInteger(previous) && previous > 0) await waitForExit(previous, waitMs);\n if (!refresh) return;\n // The old launcher reads its last lines after its agent exits; give it a moment to finish before its file changes.\n await new Promise((r) => setTimeout(r, 2_000));\n try { if (refreshLauncher(dir, cli)) console.log(`${new Date().toISOString()} rewrote the autostart launcher for this version`); }\n catch (error) { console.log(`${new Date().toISOString()} could not rewrite the autostart launcher: ${(error as Error).message}`); }\n}\n\nexport async function waitForExit(pid: number, timeoutMs: number): Promise<void> {\n const until = Date.now() + timeoutMs;\n while (Date.now() < until) {\n try { process.kill(pid, 0); } catch { return; }\n await new Promise((r) => setTimeout(r, 250));\n }\n}\n\nexport const AGENT_LOCK = \"agent.lock\";\n\n/** How long a lock whose agent never answered on its local channel still counts as an agent starting up. */\nexport const AGENT_LOCK_STARTING_MS = 60_000;\n\n/** Take the one-agent-per-home lock: created exclusively, or taken over when the agent named in it is\n * gone. The caller has already found no agent answering on the local channel, so a lock older than\n * AGENT_LOCK_STARTING_MS is left over even when its process id is alive: Windows reuses process ids\n * soon after a restart, and a sign-in after a reboot must not find \"already running\". A newer lock\n * is an agent still starting (or one whose id is not written yet): this start gives way.\n * Returns the lock file, or null while another agent holds it. */\nexport function acquireAgentLock(dir: string, now = Date.now()): string | null {\n const file = join(dir, AGENT_LOCK);\n mkdirSync(dir, { recursive: true });\n const mine = `${process.pid} ${now}`;\n try { writeFileSync(file, mine, { flag: \"wx\" }); return file; } catch { /* held, or left behind */ }\n let text = \"\";\n let written = now;\n try { text = readFileSync(file, \"utf8\").trim(); written = statSync(file).mtimeMs; } catch { /* removed meanwhile */ }\n const [pidText, atText] = text.split(/\\s+/);\n const holder = Number(pidText);\n const at = Number(atText);\n const since = Number.isFinite(at) && at > 0 ? at : written;\n const fresh = now - since < AGENT_LOCK_STARTING_MS;\n if (holder !== process.pid && fresh) {\n if (!(Number.isInteger(holder) && holder > 0)) return null; // being written by an agent starting this moment\n try { process.kill(holder, 0); return null; } catch { /* that agent is gone: take the lock over */ }\n }\n writeFileSync(file, mine);\n return file;\n}\n\nexport function releaseAgentLock(file: string): void {\n try { if (readFileSync(file, \"utf8\").trim().split(/\\s+/)[0] === String(process.pid)) rmSync(file, { force: true }); } catch { /* already gone */ }\n}\n\nexport async function startService(options: ServiceOptions): Promise<Service> {\n const log = options.log ?? ((line: string) => console.log(`${new Date().toISOString()} ${line}`));\n if (await callAgent(options.dir, \"GET\", \"/status\", undefined, 2_000)) throw new Error(\"a Scopebond Agent is already running for this computer\");\n // Two agents started within moments of each other (a slow first start, then a second start) both see\n // nobody answering yet: the lock file decides, so the second exits instead of orphaning the first.\n const lock = acquireAgentLock(options.dir);\n if (!lock) throw new Error(\"a Scopebond Agent is already running for this computer\");\n const interval = options.intervalMs ?? INTERVAL_MS;\n let last: CycleResult | null = null;\n let lastMaintenance: MaintenanceResult | null = null;\n let failures = 0;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let maintenanceTimer: ReturnType<typeof setInterval> | null = null;\n let running: Promise<CycleResult> | null = null;\n let cycleStartedAt: number | null = null;\n const cycleLimit = options.cycleLimitMs ?? CYCLE_LIMIT_MS;\n let stopped = false;\n let lastSelfCheckAt = 0;\n // SB388: time asleep never counts as records waiting. A gap between cycles longer than the schedule allows means the\n // computer slept (or the agent was stopped); waiting is counted again from the next cycle.\n let awakeSince = Date.now();\n let lastCycleAt = 0;\n // A long step the person should see as \"working\" (an update), or null.\n let working: string | null = null;\n // SB391: the workspace's own summary for the tray (names, links, open reviews), refreshed every five minutes; null when the\n // workspace does not answer it (a self-hosted gateway), and the tray simply leaves those rows out.\n // SB392: a sign-in started from the tray, while it waits for approval.\n let reconnecting: ReconnectStart | null = null;\n let summary: ComputerSummary | null = null;\n let summaryAt = 0;\n const refreshSummary = async () => {\n summaryAt = Date.now();\n const connection = loadConnection(options.dir);\n summary = connection ? await fetchComputerSummary(connection, options.fetchImpl) : null;\n };\n\n const prompt = serialized(options.prompter ?? systemPrompter);\n let shownLevel: HealthLevel | null = null;\n const health = () => healthOf(computerStatus(options.dir), lastMaintenance?.selfCheck ?? null);\n const noticeHealth = () => { const h = health(); notifyChange(shownLevel, h); shownLevel = h.level; };\n const sendReasons = async () => {\n const connection = loadConnection(options.dir);\n if (!connection) return;\n try { const sent = await flushReasons(options.dir, connection, options.fetchImpl); if (sent) log(`sent ${sent} override reason(s) to the workspace`); }\n catch { /* they wait for the next cycle */ }\n // D144: allowances a person made here, and their requests to an admin.\n try {\n const sent = await sendAllowancesAndRequests(options.dir, connection, options.fetchImpl);\n if (sent.allowances || sent.requests) log(`sent ${sent.allowances} allowance(s) and ${sent.requests} request(s) to the workspace`);\n } catch { /* they wait for the next cycle */ }\n };\n const cycle = async (): Promise<CycleResult> => {\n if (running) return running;\n const started = Date.now();\n const allowedGap = failures ? MAX_BACKOFF_MS + 5 * 60_000 : Math.max(3 * interval, 5 * 60_000);\n if (lastCycleAt && started - lastCycleAt > allowedGap) awakeSince = started;\n lastCycleAt = started;\n cycleStartedAt = started;\n // A cycle that does not finish within its limit is left behind (its requests time out on their own), counted as\n // a failure, and the loop goes on. Nothing — the next cycle, Send now, stop — waits on it.\n let limitTimer: ReturnType<typeof setTimeout> | undefined;\n const overrun = new Promise<CycleResult>((resolve) => {\n limitTimer = setTimeout(() => resolve({ at: started, connected: true, delivered: 0, pending: computerStatus(options.dir).delivery.pending,\n deliveryError: `a delivery cycle did not finish within ${Math.round(cycleLimit / 1000)} s`, rules: \"skipped\", missingHookEntries: [], requested: null }), cycleLimit);\n limitTimer.unref?.();\n });\n const work = runCycle({ dir: options.dir, fetchImpl: options.fetchImpl, deliveryTimeoutMs: options.deliveryTimeoutMs });\n running = Promise.race([work, overrun]).then(async (result) => {\n if (limitTimer) clearTimeout(limitTimer);\n if (result.deliveryError?.startsWith(\"a delivery cycle did not finish\")) recordAgentError(options.dir, result.deliveryError);\n await sendReasons();\n try { noticeHealth(); } catch { /* status is best effort */ }\n last = result;\n failures = result.deliveryError ? failures + 1 : 0;\n if (result.delivered) log(`delivered ${result.delivered} record(s); ${result.pending} waiting`);\n if (result.deliveryError) log(`delivery problem: ${result.deliveryError}`);\n // SB390: asked from the workspace's computer page. Send again now (the cycle sent before its rules check), or run the\n // self-check; each once.\n if (result.requested === \"flush\") { log(\"the workspace asked this computer to send now\"); setTimeout(() => { void cycle(); }, 0); }\n if (result.requested === \"self_check\") { log(\"the workspace asked this computer to check now\"); setTimeout(() => { void maintain(true); }, 0); }\n if (result.connected && Date.now() - summaryAt > SUMMARY_EVERY_MS) void refreshSummary();\n return result;\n }).finally(() => { running = null; cycleStartedAt = null; });\n return running;\n };\n\n const maintain = async (forceSelfCheck = false): Promise<MaintenanceResult> => {\n const result: MaintenanceResult = { at: Date.now(), policy: \"unknown\", updatedTo: null, hookEntries: [], selfCheck: null, error: null };\n try {\n const connection = loadConnection(options.dir);\n const harnesses = expectedHarnesses();\n const target = connection ? await fetchClientVersion(connection, options.fetchImpl) : null;\n if (target) result.policy = target.policy;\n const current = agentVersion();\n // Hook entries first: under \"recommended\" they move to the newest hook named or carried; under \"hold\" only broken ones\n // are repaired, at the version they already name or the one the agent carries. Done before a self-update, so an update\n // whose handover fails never leaves the hook behind.\n const hookTarget = target?.policy === \"recommended\" && target.hook && compareVersions(target.hook, hookVersion()) > 0 ? target.hook : hookVersion();\n result.hookEntries = maintainHookEntries(harnesses, hookTarget, target?.policy !== \"hold\");\n for (const change of result.hookEntries) log(`${change.reason}: ${change.file}`);\n const kind = installKind();\n if (target?.policy === \"recommended\" && target.agent && compareVersions(target.agent, current) > 0 && kind === \"per-machine\") {\n // Installed for every user: the organisation that deployed it updates it.\n log(`the workspace recommends the Scopebond Agent ${target.agent}; this install is updated by your organization`);\n } else if (target?.policy === \"recommended\" && target.agent && compareVersions(target.agent, current) > 0 && kind === \"per-user\") {\n log(`updating the Scopebond Agent ${current} -> ${target.agent} (signed installer)`);\n const verified = await fetchVerifiedInstaller(target.agent, { dir: options.dir, fetchImpl: options.fetchImpl });\n if (verified.ok) {\n result.updatedTo = target.agent;\n lastMaintenance = result;\n if (options.onUpdated) options.onUpdated(target.agent);\n else {\n // The installer replaces this file, so it runs after this agent has exited, and starts the agent again.\n installAfterExit(verified.path, process.pid, existsSync(launcherPath(options.dir)) ? launcherPath(options.dir) : null);\n setTimeout(() => { setTimeout(() => process.exit(0), 5_000).unref(); void stop().finally(() => process.exit(0)); }, 500);\n }\n return result;\n }\n result.error = `update to ${target.agent} could not be verified: ${verified.reason}; nothing was installed`;\n log(result.error);\n } else if (target?.policy === \"recommended\" && target.agent && compareVersions(target.agent, current) > 0) {\n log(`updating the Scopebond Agent ${current} -> ${target.agent}`);\n const installed = await installAgent(target.agent);\n if (installed.ok) {\n result.updatedTo = target.agent;\n lastMaintenance = result;\n if (options.onUpdated) options.onUpdated(target.agent);\n else {\n const code = spawnReplacement(options.dir) ? 0 : RESTART_EXIT_CODE;\n // Hand over: stop, then exit. A stop that never finishes (a tray or window child that will not close) must not\n // keep the old agent alive with the replacement waiting on it, so the exit has a hard deadline.\n setTimeout(() => { setTimeout(() => process.exit(code), 5_000).unref(); void stop().finally(() => process.exit(code)); }, 500);\n }\n return result;\n }\n result.error = `update to ${target.agent} failed: ${installed.output.slice(-300)}`;\n log(result.error);\n }\n // The local store: older rows rewritten, acknowledged receipts past the retention window removed, space returned.\n // The agent may rewrite the whole file once (the hook never does that during a tool call).\n try {\n result.store = runStoreUpkeep(options.dir, { budgetMs: 30_000, allowFullVacuum: true });\n const kept = result.store;\n if (kept && (kept.migrated || kept.receiptsRemoved || kept.stateRemoved || kept.pagesFreed)) {\n log(`local store: ${kept.migrated} row(s) rewritten, ${kept.receiptsRemoved} receipt(s) past retention removed, ${kept.stateRemoved} finished check record(s) removed, ${kept.pagesFreed} page(s) returned`);\n }\n } catch (error) { log(`local store upkeep failed: ${(error as Error).message}`); }\n if (connection && (forceSelfCheck || Date.now() - lastSelfCheckAt >= SELF_CHECK_EVERY_MS)) {\n result.selfCheck = await runSelfCheck(options.dir, connection, harnesses, current, { fetchImpl: options.fetchImpl });\n lastSelfCheckAt = Date.now();\n log(`self-check ${result.selfCheck?.ok ? \"passed\" : `failed: ${result.selfCheck?.failed.join(\", \") ?? \"no result\"}`}`);\n }\n } catch (error) { result.error = (error as Error).message; }\n lastMaintenance = result;\n return result;\n };\n\n const schedule = () => {\n if (stopped) return;\n const delay = failures ? Math.min(MAX_BACKOFF_MS, interval * 2 ** Math.min(failures, 6)) : interval;\n timer = setTimeout(() => { void cycle().finally(schedule); }, delay);\n };\n\n // D144: a block under a rule a person may allow or ask about can be acted on afterwards, from the tray.\n const actionable = (blocks: Array<{ action_id: string | null; summary: string; at: string; rule: string | null }>, now: number): RecentBlock[] => {\n const known = new Map(readBlocked(options.dir).map((b) => [b.id, b]));\n return blocks.map((b) => {\n const item = b.action_id ? known.get(b.action_id) : undefined;\n return { ...b, rule: b.rule ?? item?.rule ?? null, can_act: Boolean(item && b.action_id && blockedQuestion(options.dir, b.action_id, now)), acted: item?.acted ?? null };\n });\n };\n\n const trayState = (): TrayModel => {\n const now = Date.now();\n const status = computerStatus(options.dir);\n const meta = readMeta(options.dir);\n const report = ruleReport(options.dir);\n const modes = report ? Object.values(report).map(([mode]) => mode) : [];\n const activity = localActivity(options.dir);\n return trayModel({\n status, health: health(), now, awakeSince, working,\n rules: report ? { checked_at: meta.checked_at ? Date.parse(meta.checked_at) : null, managed: isManaged(options.dir), block: modes.filter((m) => m === \"enforce\").length, monitor: modes.filter((m) => m === \"monitor\").length } : null,\n today: activity?.today ?? null,\n recentBlocks: actionable(activity?.recent_blocks ?? [], now),\n version: { agent: agentVersion(), hook: hookVersion(), policy: lastMaintenance?.policy ?? \"unknown\", recommendedAgent: meta.recommended?.agent ?? null, recommendedHook: meta.recommended?.hook ?? null },\n workspace: summary ? { name: summary.workspace_name, environment: summary.environment_name, computer_url: summary.computer_url } : null,\n openReviews: summary?.open_reviews ?? 0,\n computerName: hostname(),\n canReconnect: true,\n });\n };\n\n const control = await startControl(options.dir, `${AGENT_VERSION} hook/${hookVersion()}`, {\n // SB387: what the tray draws, and the person's tray settings.\n \"GET /tray\": () => ({ tray: trayState(), settings: readTraySettings(options.dir) }),\n \"POST /settings\": (body) => ({ settings: writeTraySettings(options.dir, body) }),\n // SB387: \"Check now\" always says what it found.\n \"POST /check\": async () => {\n const result = await maintain(true);\n return { text: checkResult(result.selfCheck, result.error), tray: trayState() };\n },\n // SB389: install the version the workspace recommends now, instead of at the next six-hourly check.\n \"POST /update\": async () => {\n working = \"Updating Scopebond…\";\n try { const result = await maintain(true); return { updated_to: result.updatedTo, error: result.error }; }\n finally { working = null; }\n },\n // SB389: the newest blocks on this computer, summarised the way `log` prints them (never raw arguments).\n // SB391: open this computer's page in the workspace, only on the workspace this computer is connected to.\n \"POST /open-workspace\": () => {\n const connection = loadConnection(options.dir);\n const url = summary?.computer_url;\n if (!connection || !url || !sameOrigin(url, connection.url)) return { opened: false };\n openInBrowser(url);\n return { opened: true };\n },\n // SB392: sign in again for the connected workspace; the tray shows the code, the approval page opens in the browser.\n \"POST /reconnect\": async () => {\n if (reconnecting) return reconnecting;\n working = \"Signing in again…\";\n const result = await startReconnect(options.dir, (ok) => {\n reconnecting = null; working = null;\n log(ok ? \"signed in again\" : \"the sign-in did not finish\");\n if (ok) void cycle();\n });\n if (\"error\" in result) { working = null; return { error: result.error }; }\n reconnecting = result.started;\n log(`signing in again: waiting for approval of code ${result.started.user_code}`);\n return result.started;\n },\n \"GET /recent-blocks\": () => ({ recent_blocks: actionable(localActivity(options.dir, { limit: 10 })?.recent_blocks ?? [], Date.now()) }),\n // D144: allow an earlier block, or ask an admin, from the tray. The caller names the block; only the Scopebond window\n // answers (the same window the hook uses), so whoever calls this channel cannot allow anything by itself. Scopebond never\n // runs the action: the person or the coding agent runs it again.\n \"POST /blocked\": async (body) => {\n const id = typeof (body as { action_id?: unknown } | null)?.action_id === \"string\" ? String((body as { action_id: string }).action_id) : \"\";\n const q = id ? blockedQuestion(options.dir, id) : null;\n if (!q) return { outcome: \"gone\", text: \"This block can no longer be allowed or sent to an admin from here.\" };\n log(`asking about an earlier block under \"${q.title}\"`);\n const answer = await prompt({ action_id: id, rule: q.item.rule, title: q.title, summary: q.item.summary, reason_min: q.reason_min,\n lasts: \"the next time it runs\", timeout_ms: 55_000, mode: q.mode, offers: q.offers });\n if (answer.decision === \"unavailable\") return { outcome: \"unavailable\", text: \"The Scopebond window could not be shown.\" };\n const done = actOnBlocked(options.dir, id, answer);\n if (done.outcome === \"allowed\" || done.outcome === \"proposed\" || done.outcome === \"asked\") setTimeout(() => { void sendReasons(); }, 1_000);\n log(`earlier block under \"${q.title}\": ${done.outcome}`);\n return { outcome: done.outcome, text: blockedText(done.outcome) };\n },\n \"GET /status\": () => ({ ...computerStatus(options.dir), health: health(), agent: { pid: process.pid, version: AGENT_VERSION, last_cycle: last, cycle_started_at: cycleStartedAt, last_maintenance: lastMaintenance } }),\n \"POST /flush\": async () => ({ cycle: await cycle() }),\n \"POST /repair\": () => {\n const repaired = repairHookEntries();\n for (const r of repaired) log(`repaired the Scopebond hook entry in ${r.file}`);\n return { repaired };\n },\n \"POST /maintain\": async () => ({ maintenance: await maintain(true) }),\n // `scopebond-agent stop` and `autostart off`: answer first, then stop, so the caller hears back.\n \"POST /stop\": () => {\n log(\"stopping: asked to by this computer's user\");\n setTimeout(() => { void stop().finally(() => options.onStopped?.()); }, 50);\n return { stopping: true };\n },\n // Warn mode: the hook asks; only the window answers. The caller learns the answer, never decides it.\n \"POST /override\": async (body) => {\n const question = parseQuestion(body);\n if (!question) return { decision: \"unavailable\" };\n log(`asking whether to allow \"${question.title}\"`);\n const answer = await prompt(question);\n log(`override ${answer.decision === \"allow\" ? \"given\" : answer.decision === \"deny\" ? \"not given\" : \"not available\"} for \"${question.title}\"`);\n if (answer.decision === \"allow\" && answer.reason) { queueReason(options.dir, question.action_id, answer.reason); setTimeout(() => { void sendReasons(); }, 1_000); }\n // The hook writes the request or allowance after this answer; send it shortly after.\n if (answer.decision === \"ask\" || answer.lasts === \"always\" || answer.lasts === \"15m\") setTimeout(() => { void sendReasons(); }, 2_000);\n return answer;\n },\n });\n const tray = (options.tray ?? true) && process.env.SCOPEBOND_AGENT_TRAY !== \"off\" ? startTray(options.dir) : null;\n const stop = async () => {\n stopped = true;\n try { tray?.kill(); } catch { /* already gone */ }\n if (timer) clearTimeout(timer);\n if (maintenanceTimer) clearInterval(maintenanceTimer);\n // A cycle under way gets a moment to finish; stop never waits on one that does not.\n if (running) await Promise.race([running.catch(() => undefined), new Promise((resolve) => setTimeout(resolve, 2_000).unref?.())]);\n await control.close();\n releaseAgentLock(lock);\n };\n log(`Scopebond Agent ${agentVersion()} running for ${options.dir} (control on 127.0.0.1:${control.endpoint.port})`);\n // Maintenance is armed before the first cycle, so a first cycle that hangs cannot keep it from ever starting.\n if (options.maintenance !== false) {\n // Shortly after start (so a fresh sign-in is not slowed), then every six hours.\n setTimeout(() => { if (!stopped) void maintain(); }, 2 * 60_000).unref?.();\n maintenanceTimer = setInterval(() => { if (!stopped) void maintain(); }, UPDATE_EVERY_MS);\n maintenanceTimer.unref?.();\n }\n await cycle();\n schedule();\n return { port: control.endpoint.port, cycleNow: cycle, maintainNow: () => maintain(true), stop };\n}\n\n/** What the tray says after a person acted on an earlier block. */\nexport function blockedText(outcome: \"allowed\" | \"proposed\" | \"asked\" | \"declined\" | \"gone\"): string {\n switch (outcome) {\n case \"allowed\": return \"Allowed. Run it again, or let the coding agent retry.\";\n case \"proposed\": return \"Allowed for 15 minutes. Your workspace's admins decide whether it stands; run it again meanwhile.\";\n case \"asked\": return \"Asked your workspace's admins. The Scopebond icon shows their answer; then run it again.\";\n case \"declined\": return \"Nothing was allowed.\";\n case \"gone\": return \"This block can no longer be allowed or sent to an admin from here.\";\n }\n}\n"]}
@@ -81,6 +81,12 @@ export declare const OFFLINE_ATTENTION_MS: number;
81
81
  export declare const RULES_STALE_MS: number;
82
82
  /** A delivery error that means the workspace could not be reached (no answer at all), not a refusal. */
83
83
  export declare function unreachable(error: string | null | undefined): boolean;
84
+ /** The workspace refused records because it reached its monthly limit (they stay on the computer). */
85
+ export declare function overQuota(error: string | null | undefined): boolean;
86
+ /** The workspace's plan keeps fewer agents active than it has, and this one is paused (HTTP 402 agent_paused). */
87
+ export declare function planPaused(error: string | null | undefined): boolean;
88
+ /** The HTTP status of a refusal the workspace answered (not "unreachable"), or null. */
89
+ export declare function refusedStatus(error: string | null | undefined): number | null;
84
90
  export declare function ago(at: number, now: number): string;
85
91
  export declare function trayModel(input: TrayInput): TrayModel;
86
92
  /** What "Check now" says afterwards, for five seconds (SB387): never silence. */
@@ -1 +1 @@
1
- {"version":3,"file":"tray-model.d.ts","sourceRoot":"","sources":["../src/tray-model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tray-model.d.ts","sourceRoot":"","sources":["../src/tray-model.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,19 @@ export const RULES_STALE_MS = HOUR;
13
13
  export function unreachable(error) {
14
14
  return !!error && !/HTTP \d{3}/.test(error) && /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENETUNREACH|EHOSTUNREACH|getaddrinfo|network|socket|could not reach/i.test(error);
15
15
  }
16
+ /** The workspace refused records because it reached its monthly limit (they stay on the computer). */
17
+ export function overQuota(error) {
18
+ return !!error && /HTTP 429 \(quota\)/.test(error);
19
+ }
20
+ /** The workspace's plan keeps fewer agents active than it has, and this one is paused (HTTP 402 agent_paused). */
21
+ export function planPaused(error) {
22
+ return !!error && /HTTP 402 \(agent_paused\)/.test(error);
23
+ }
24
+ /** The HTTP status of a refusal the workspace answered (not "unreachable"), or null. */
25
+ export function refusedStatus(error) {
26
+ const m = error ? /HTTP (\d{3})/.exec(error) : null;
27
+ return m ? Number(m[1]) : null;
28
+ }
16
29
  export function ago(at, now) {
17
30
  const s = Math.max(0, Math.round((now - at) / 1000));
18
31
  if (s < 60)
@@ -72,6 +85,20 @@ export function trayModel(input) {
72
85
  else
73
86
  hint = health.hint;
74
87
  }
88
+ else if (pending > 0 && overQuota(d.last_error) && health.fix?.route !== "/repair") {
89
+ // Not "sending": the workspace refuses them until its limit allows. Nothing is lost; say so, and where it is changed.
90
+ state = "attention";
91
+ headline = `Workspace limit reached: ${pending} record${pending === 1 ? "" : "s"} waiting`;
92
+ hint = "Your workspace reached its monthly limit. Records stay on this computer and send once the limit allows; a workspace owner can change the plan.";
93
+ fix = ACTIONS.open_workspace;
94
+ }
95
+ else if (pending > 0 && planPaused(d.last_error) && health.fix?.route !== "/repair") {
96
+ // Not a delivery problem to retry: the plan paused this agent. Sending again changes nothing; an owner decides.
97
+ state = "attention";
98
+ headline = `Paused by your workspace's plan: ${pending} record${pending === 1 ? "" : "s"} waiting`;
99
+ hint = "Your workspace's plan keeps fewer agents active than it has, and this one is paused. This computer keeps checking actions and keeps its records; they send once an owner keeps this agent active or changes the plan.";
100
+ fix = ACTIONS.open_workspace;
101
+ }
75
102
  else if (health.level === "red") {
76
103
  state = "problem";
77
104
  headline = health.headline;
@@ -113,7 +140,9 @@ export function trayModel(input) {
113
140
  const value = pending === 0
114
141
  ? (d.last_success_at ? `All sent · ${ago(d.last_success_at, now)}` : "Nothing to send yet")
115
142
  : offline ? `${pending} waiting since ${oldestAt ? clock(oldestAt) : "earlier"} · offline`
116
- : `${pending} waiting · sending`;
143
+ : overQuota(d.last_error) ? `${pending} waiting · workspace limit reached`
144
+ : refusedStatus(d.last_error) ? `${pending} waiting · last try refused (HTTP ${refusedStatus(d.last_error)})`
145
+ : `${pending} waiting · sending`;
117
146
  rows.push({ label: "Delivery", value });
118
147
  }
119
148
  if (input.today) {
@@ -1 +1 @@
1
- {"version":3,"file":"tray-model.js","sourceRoot":"","sources":["../src/tray-model.ts"],"names":[],"mappings":"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","sourcesContent":["// What the tray shows, as data (SB387, SB388). The tray (today a PowerShell script, later a native one) only draws this: one\n// state, one headline, the rows that have data, the one fix when something is wrong, and the actions that apply now. It never\n// works out health itself, so the tray, `status` and the workspace's computer page say the same thing.\n\nimport type { StatusJson } from \"@scopebond/hook\";\nimport type { Health } from \"./health.js\";\n\n/** The six icon states of the \"S\" tile (D143): no badge when protected; a badge with its own glyph otherwise. */\nexport type TrayState = \"protected\" | \"working\" | \"offline\" | \"attention\" | \"problem\" | \"disconnected\";\n\nexport interface TrayAction {\n id: \"send_now\" | \"update_now\" | \"check_now\" | \"repair\" | \"reconnect\" | \"open_workspace\";\n label: string;\n /** The agent's local route that does it (POST). */\n route: string;\n}\n\nexport interface TrayModel {\n state: TrayState;\n /** One line at the top, and the tooltip (Windows allows 63 characters in a classic tray tooltip). */\n headline: string;\n tooltip: string;\n /** Rules, Delivery, Today, Version: each only when there is data (never \"unknown\"). */\n rows: Array<{ label: string; value: string }>;\n /** The one fix when something is wrong (shown first, in place of the usual actions). */\n fix: TrayAction | null;\n /** Actions that apply now, in menu order. */\n actions: TrayAction[];\n /** A sign-in or similar step no button can do, in words. */\n hint: string | null;\n recent_blocks: RecentBlock[];\n}\n\n/** A recent block. `can_act`: the person may still allow it or ask an admin from the tray (D144); `acted`: what they did. */\nexport interface RecentBlock { action_id: string | null; summary: string; at: string; rule: string | null; can_act?: boolean; acted?: \"allowed\" | \"asked\" | null }\n\nexport interface TrayInput {\n status: StatusJson;\n health: Health;\n now: number;\n /** When this computer last came back from sleep or started (the agent sees the gap between its cycles). */\n awakeSince: number;\n /** A long step the agent is running (an update, a sign-in, a repair), or null. */\n working: string | null;\n rules: { checked_at: number | null; managed: boolean; block: number; monitor: number } | null;\n today: { actions: number; blocked: number; allowed_by_person: number } | null;\n recentBlocks?: RecentBlock[];\n version: { agent: string; hook: string; policy: \"recommended\" | \"hold\" | \"unknown\"; recommendedAgent: string | null; recommendedHook: string | null };\n workspace: { name: string | null; environment: string | null; computer_url: string | null } | null;\n computerName: string;\n /** Items about this computer waiting in the workspace's Review (from the workspace's summary). */\n openReviews?: number;\n /** Whether the agent can run the sign-in itself (`POST /reconnect`); until then the tray shows the command. */\n canReconnect?: boolean;\n}\n\nconst MIN = 60_000;\nconst HOUR = 60 * MIN;\n/** Records waiting this long while the computer is awake and online need attention (D143 §4.1). */\nexport const WAITING_ATTENTION_MS = 15 * MIN;\n/** Offline (the workspace unreachable) this long needs attention. */\nexport const OFFLINE_ATTENTION_MS = 4 * HOUR;\n/** Rules not checked for this long while online need attention. */\nexport const RULES_STALE_MS = HOUR;\n\n/** A delivery error that means the workspace could not be reached (no answer at all), not a refusal. */\nexport function unreachable(error: string | null | undefined): boolean {\n return !!error && !/HTTP \\d{3}/.test(error) && /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENETUNREACH|EHOSTUNREACH|getaddrinfo|network|socket|could not reach/i.test(error);\n}\n\nexport function ago(at: number, now: number): string {\n const s = Math.max(0, Math.round((now - at) / 1000));\n if (s < 60) return \"just now\";\n const m = Math.round(s / 60);\n if (m < 60) return `${m} min ago`;\n const h = Math.round(m / 60);\n if (h < 36) return `${h} h ago`;\n return `${Math.round(h / 24)} days ago`;\n}\n\nfunction clock(at: number): string {\n const d = new Date(at);\n return `${String(d.getHours()).padStart(2, \"0\")}:${String(d.getMinutes()).padStart(2, \"0\")}`;\n}\n\nconst newer = (want: string | null, have: string): boolean => {\n if (!want) return false;\n const a = want.split(\".\").map(Number), b = have.split(\".\").map(Number);\n for (let i = 0; i < 3; i++) if ((a[i] ?? 0) !== (b[i] ?? 0)) return (a[i] ?? 0) > (b[i] ?? 0);\n return false;\n};\n\nconst ACTIONS: Record<TrayAction[\"id\"], TrayAction> = {\n send_now: { id: \"send_now\", label: \"Send records now\", route: \"/flush\" },\n update_now: { id: \"update_now\", label: \"Update now\", route: \"/update\" },\n check_now: { id: \"check_now\", label: \"Check now\", route: \"/check\" },\n repair: { id: \"repair\", label: \"Repair\", route: \"/repair\" },\n reconnect: { id: \"reconnect\", label: \"Reconnect…\", route: \"/reconnect\" },\n open_workspace: { id: \"open_workspace\", label: \"Open workspace\", route: \"/open-workspace\" },\n};\n\nexport function trayModel(input: TrayInput): TrayModel {\n const { status, health, now } = input;\n const d = status.delivery;\n const pending = d.pending;\n const oldestAt = d.oldest_pending_age_s === null ? null : now - d.oldest_pending_age_s * 1000;\n // Sleep-aware (SB388): time the computer was asleep or off never counts as waiting.\n const waitingMs = oldestAt === null ? 0 : now - Math.max(oldestAt, input.awakeSince);\n const offline = d.connected && unreachable(d.last_error);\n const offlineFor = offline ? now - Math.max(d.last_success_at ?? input.awakeSince, input.awakeSince) : 0;\n const updateAvailable = input.version.policy === \"recommended\" && (newer(input.version.recommendedAgent, input.version.agent) || newer(input.version.recommendedHook, input.version.hook));\n\n let state: TrayState;\n let headline: string;\n let fix: TrayAction | null = null;\n let hint: string | null = null;\n if (input.working) {\n state = \"working\"; headline = input.working;\n } else if (health.level === \"red\" && d.connection_refused_since !== null) {\n state = \"disconnected\"; headline = \"Not connected to your workspace: using this computer's own rules\";\n if (input.canReconnect) fix = ACTIONS.reconnect; else hint = health.hint;\n } else if (health.level === \"red\") {\n state = \"problem\"; headline = health.headline; hint = health.hint;\n if (health.fix?.route === \"/repair\") fix = ACTIONS.repair;\n } else if (offline) {\n state = offlineFor > OFFLINE_ATTENTION_MS ? \"attention\" : \"offline\";\n headline = pending ? `Workspace unreachable: ${pending} record${pending === 1 ? \"\" : \"s\"} waiting` : \"Workspace unreachable: still checking actions\";\n } else if (pending > 0 && waitingMs > WAITING_ATTENTION_MS) {\n state = \"attention\"; headline = `${pending} record${pending === 1 ? \"\" : \"s\"} waiting to send`; fix = ACTIONS.send_now;\n } else if (health.level === \"amber\" && health.fix?.route === \"/maintain\") {\n state = \"attention\"; headline = health.headline; fix = ACTIONS.check_now;\n } else if (input.rules?.checked_at && d.connected && now - input.rules.checked_at > RULES_STALE_MS) {\n state = \"attention\"; headline = `Rules not checked for ${ago(input.rules.checked_at, now).replace(\" ago\", \"\")}`; fix = ACTIONS.check_now;\n } else {\n state = \"protected\"; headline = \"Protected\";\n }\n\n const rows: TrayModel[\"rows\"] = [];\n if (input.rules) {\n const when = input.rules.checked_at ? ` · checked ${ago(input.rules.checked_at, now)}` : \"\";\n const source = input.rules.managed ? \"Up to date\" : \"Using this computer's own rules\";\n const counts = input.rules.block + input.rules.monitor > 0 ? ` · ${input.rules.block} block · ${input.rules.monitor} monitor` : \"\";\n rows.push({ label: \"Rules\", value: `${input.rules.managed && input.rules.checked_at && now - input.rules.checked_at > RULES_STALE_MS ? `Not checked for ${ago(input.rules.checked_at, now).replace(\" ago\", \"\")}` : source}${when}${counts}` });\n }\n if (d.connected) {\n const value = pending === 0\n ? (d.last_success_at ? `All sent · ${ago(d.last_success_at, now)}` : \"Nothing to send yet\")\n : offline ? `${pending} waiting since ${oldestAt ? clock(oldestAt) : \"earlier\"} · offline`\n : `${pending} waiting · sending`;\n rows.push({ label: \"Delivery\", value });\n }\n if (input.today) {\n const parts = [`${input.today.actions} action${input.today.actions === 1 ? \"\" : \"s\"}`, `${input.today.blocked} blocked`];\n if (input.today.allowed_by_person) parts.push(`${input.today.allowed_by_person} allowed by a person`);\n if (input.openReviews) parts.push(`${input.openReviews} in Review`);\n rows.push({ label: \"Today\", value: parts.join(\" · \") });\n }\n rows.push({\n label: \"Version\",\n value: updateAvailable ? `Update available (agent ${input.version.recommendedAgent ?? input.version.agent})`\n : input.version.policy === \"hold\" ? `agent ${input.version.agent} · updates managed by your workspace`\n : `Up to date (agent ${input.version.agent})`,\n });\n\n const actions: TrayAction[] = [];\n if (pending > 0 && fix?.id !== \"send_now\" && state !== \"disconnected\") actions.push(ACTIONS.send_now);\n if (updateAvailable && !input.working) actions.push(ACTIONS.update_now);\n if (fix?.id !== \"check_now\") actions.push(ACTIONS.check_now);\n if (input.workspace?.computer_url) actions.push(ACTIONS.open_workspace);\n\n const where = [input.workspace?.name, input.workspace?.environment].filter(Boolean).join(\" · \");\n const label = state === \"protected\" ? `Protected${where ? ` · ${where}` : \"\"}` : headline;\n const tooltip = `Scopebond — ${label}`;\n return {\n state, headline, fix, actions, hint, rows,\n tooltip: tooltip.length > 63 ? `${tooltip.slice(0, 62)}…` : tooltip,\n recent_blocks: (input.recentBlocks ?? []).slice(0, 3),\n };\n}\n\n/** What \"Check now\" says afterwards, for five seconds (SB387): never silence. */\nexport function checkResult(selfCheck: { ok: boolean; failed: string[] } | null, error: string | null): string {\n if (error) return `Check could not finish: ${error}`.slice(0, 200);\n if (!selfCheck) return \"Checked just now: this computer is not connected to a workspace, so only local checks ran\";\n return selfCheck.ok ? \"Checked just now: all good\" : `Check found a problem: ${selfCheck.failed[0] ?? \"see status\"}`;\n}\n"]}
1
+ {"version":3,"file":"tray-model.js","sourceRoot":"","sources":["../src/tray-model.ts"],"names":[],"mappings":"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","sourcesContent":["// What the tray shows, as data (SB387, SB388). The tray (today a PowerShell script, later a native one) only draws this: one\n// state, one headline, the rows that have data, the one fix when something is wrong, and the actions that apply now. It never\n// works out health itself, so the tray, `status` and the workspace's computer page say the same thing.\n\nimport type { StatusJson } from \"@scopebond/hook\";\nimport type { Health } from \"./health.js\";\n\n/** The six icon states of the \"S\" tile (D143): no badge when protected; a badge with its own glyph otherwise. */\nexport type TrayState = \"protected\" | \"working\" | \"offline\" | \"attention\" | \"problem\" | \"disconnected\";\n\nexport interface TrayAction {\n id: \"send_now\" | \"update_now\" | \"check_now\" | \"repair\" | \"reconnect\" | \"open_workspace\";\n label: string;\n /** The agent's local route that does it (POST). */\n route: string;\n}\n\nexport interface TrayModel {\n state: TrayState;\n /** One line at the top, and the tooltip (Windows allows 63 characters in a classic tray tooltip). */\n headline: string;\n tooltip: string;\n /** Rules, Delivery, Today, Version: each only when there is data (never \"unknown\"). */\n rows: Array<{ label: string; value: string }>;\n /** The one fix when something is wrong (shown first, in place of the usual actions). */\n fix: TrayAction | null;\n /** Actions that apply now, in menu order. */\n actions: TrayAction[];\n /** A sign-in or similar step no button can do, in words. */\n hint: string | null;\n recent_blocks: RecentBlock[];\n}\n\n/** A recent block. `can_act`: the person may still allow it or ask an admin from the tray (D144); `acted`: what they did. */\nexport interface RecentBlock { action_id: string | null; summary: string; at: string; rule: string | null; can_act?: boolean; acted?: \"allowed\" | \"asked\" | null }\n\nexport interface TrayInput {\n status: StatusJson;\n health: Health;\n now: number;\n /** When this computer last came back from sleep or started (the agent sees the gap between its cycles). */\n awakeSince: number;\n /** A long step the agent is running (an update, a sign-in, a repair), or null. */\n working: string | null;\n rules: { checked_at: number | null; managed: boolean; block: number; monitor: number } | null;\n today: { actions: number; blocked: number; allowed_by_person: number } | null;\n recentBlocks?: RecentBlock[];\n version: { agent: string; hook: string; policy: \"recommended\" | \"hold\" | \"unknown\"; recommendedAgent: string | null; recommendedHook: string | null };\n workspace: { name: string | null; environment: string | null; computer_url: string | null } | null;\n computerName: string;\n /** Items about this computer waiting in the workspace's Review (from the workspace's summary). */\n openReviews?: number;\n /** Whether the agent can run the sign-in itself (`POST /reconnect`); until then the tray shows the command. */\n canReconnect?: boolean;\n}\n\nconst MIN = 60_000;\nconst HOUR = 60 * MIN;\n/** Records waiting this long while the computer is awake and online need attention (D143 §4.1). */\nexport const WAITING_ATTENTION_MS = 15 * MIN;\n/** Offline (the workspace unreachable) this long needs attention. */\nexport const OFFLINE_ATTENTION_MS = 4 * HOUR;\n/** Rules not checked for this long while online need attention. */\nexport const RULES_STALE_MS = HOUR;\n\n/** A delivery error that means the workspace could not be reached (no answer at all), not a refusal. */\nexport function unreachable(error: string | null | undefined): boolean {\n return !!error && !/HTTP \\d{3}/.test(error) && /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENETUNREACH|EHOSTUNREACH|getaddrinfo|network|socket|could not reach/i.test(error);\n}\n\n/** The workspace refused records because it reached its monthly limit (they stay on the computer). */\nexport function overQuota(error: string | null | undefined): boolean {\n return !!error && /HTTP 429 \\(quota\\)/.test(error);\n}\n\n/** The workspace's plan keeps fewer agents active than it has, and this one is paused (HTTP 402 agent_paused). */\nexport function planPaused(error: string | null | undefined): boolean {\n return !!error && /HTTP 402 \\(agent_paused\\)/.test(error);\n}\n\n/** The HTTP status of a refusal the workspace answered (not \"unreachable\"), or null. */\nexport function refusedStatus(error: string | null | undefined): number | null {\n const m = error ? /HTTP (\\d{3})/.exec(error) : null;\n return m ? Number(m[1]) : null;\n}\n\nexport function ago(at: number, now: number): string {\n const s = Math.max(0, Math.round((now - at) / 1000));\n if (s < 60) return \"just now\";\n const m = Math.round(s / 60);\n if (m < 60) return `${m} min ago`;\n const h = Math.round(m / 60);\n if (h < 36) return `${h} h ago`;\n return `${Math.round(h / 24)} days ago`;\n}\n\nfunction clock(at: number): string {\n const d = new Date(at);\n return `${String(d.getHours()).padStart(2, \"0\")}:${String(d.getMinutes()).padStart(2, \"0\")}`;\n}\n\nconst newer = (want: string | null, have: string): boolean => {\n if (!want) return false;\n const a = want.split(\".\").map(Number), b = have.split(\".\").map(Number);\n for (let i = 0; i < 3; i++) if ((a[i] ?? 0) !== (b[i] ?? 0)) return (a[i] ?? 0) > (b[i] ?? 0);\n return false;\n};\n\nconst ACTIONS: Record<TrayAction[\"id\"], TrayAction> = {\n send_now: { id: \"send_now\", label: \"Send records now\", route: \"/flush\" },\n update_now: { id: \"update_now\", label: \"Update now\", route: \"/update\" },\n check_now: { id: \"check_now\", label: \"Check now\", route: \"/check\" },\n repair: { id: \"repair\", label: \"Repair\", route: \"/repair\" },\n reconnect: { id: \"reconnect\", label: \"Reconnect…\", route: \"/reconnect\" },\n open_workspace: { id: \"open_workspace\", label: \"Open workspace\", route: \"/open-workspace\" },\n};\n\nexport function trayModel(input: TrayInput): TrayModel {\n const { status, health, now } = input;\n const d = status.delivery;\n const pending = d.pending;\n const oldestAt = d.oldest_pending_age_s === null ? null : now - d.oldest_pending_age_s * 1000;\n // Sleep-aware (SB388): time the computer was asleep or off never counts as waiting.\n const waitingMs = oldestAt === null ? 0 : now - Math.max(oldestAt, input.awakeSince);\n const offline = d.connected && unreachable(d.last_error);\n const offlineFor = offline ? now - Math.max(d.last_success_at ?? input.awakeSince, input.awakeSince) : 0;\n const updateAvailable = input.version.policy === \"recommended\" && (newer(input.version.recommendedAgent, input.version.agent) || newer(input.version.recommendedHook, input.version.hook));\n\n let state: TrayState;\n let headline: string;\n let fix: TrayAction | null = null;\n let hint: string | null = null;\n if (input.working) {\n state = \"working\"; headline = input.working;\n } else if (health.level === \"red\" && d.connection_refused_since !== null) {\n state = \"disconnected\"; headline = \"Not connected to your workspace: using this computer's own rules\";\n if (input.canReconnect) fix = ACTIONS.reconnect; else hint = health.hint;\n } else if (pending > 0 && overQuota(d.last_error) && health.fix?.route !== \"/repair\") {\n // Not \"sending\": the workspace refuses them until its limit allows. Nothing is lost; say so, and where it is changed.\n state = \"attention\"; headline = `Workspace limit reached: ${pending} record${pending === 1 ? \"\" : \"s\"} waiting`;\n hint = \"Your workspace reached its monthly limit. Records stay on this computer and send once the limit allows; a workspace owner can change the plan.\";\n fix = ACTIONS.open_workspace;\n } else if (pending > 0 && planPaused(d.last_error) && health.fix?.route !== \"/repair\") {\n // Not a delivery problem to retry: the plan paused this agent. Sending again changes nothing; an owner decides.\n state = \"attention\"; headline = `Paused by your workspace's plan: ${pending} record${pending === 1 ? \"\" : \"s\"} waiting`;\n hint = \"Your workspace's plan keeps fewer agents active than it has, and this one is paused. This computer keeps checking actions and keeps its records; they send once an owner keeps this agent active or changes the plan.\";\n fix = ACTIONS.open_workspace;\n } else if (health.level === \"red\") {\n state = \"problem\"; headline = health.headline; hint = health.hint;\n if (health.fix?.route === \"/repair\") fix = ACTIONS.repair;\n } else if (offline) {\n state = offlineFor > OFFLINE_ATTENTION_MS ? \"attention\" : \"offline\";\n headline = pending ? `Workspace unreachable: ${pending} record${pending === 1 ? \"\" : \"s\"} waiting` : \"Workspace unreachable: still checking actions\";\n } else if (pending > 0 && waitingMs > WAITING_ATTENTION_MS) {\n state = \"attention\"; headline = `${pending} record${pending === 1 ? \"\" : \"s\"} waiting to send`; fix = ACTIONS.send_now;\n } else if (health.level === \"amber\" && health.fix?.route === \"/maintain\") {\n state = \"attention\"; headline = health.headline; fix = ACTIONS.check_now;\n } else if (input.rules?.checked_at && d.connected && now - input.rules.checked_at > RULES_STALE_MS) {\n state = \"attention\"; headline = `Rules not checked for ${ago(input.rules.checked_at, now).replace(\" ago\", \"\")}`; fix = ACTIONS.check_now;\n } else {\n state = \"protected\"; headline = \"Protected\";\n }\n\n const rows: TrayModel[\"rows\"] = [];\n if (input.rules) {\n const when = input.rules.checked_at ? ` · checked ${ago(input.rules.checked_at, now)}` : \"\";\n const source = input.rules.managed ? \"Up to date\" : \"Using this computer's own rules\";\n const counts = input.rules.block + input.rules.monitor > 0 ? ` · ${input.rules.block} block · ${input.rules.monitor} monitor` : \"\";\n rows.push({ label: \"Rules\", value: `${input.rules.managed && input.rules.checked_at && now - input.rules.checked_at > RULES_STALE_MS ? `Not checked for ${ago(input.rules.checked_at, now).replace(\" ago\", \"\")}` : source}${when}${counts}` });\n }\n if (d.connected) {\n const value = pending === 0\n ? (d.last_success_at ? `All sent · ${ago(d.last_success_at, now)}` : \"Nothing to send yet\")\n : offline ? `${pending} waiting since ${oldestAt ? clock(oldestAt) : \"earlier\"} · offline`\n : overQuota(d.last_error) ? `${pending} waiting · workspace limit reached`\n : refusedStatus(d.last_error) ? `${pending} waiting · last try refused (HTTP ${refusedStatus(d.last_error)})`\n : `${pending} waiting · sending`;\n rows.push({ label: \"Delivery\", value });\n }\n if (input.today) {\n const parts = [`${input.today.actions} action${input.today.actions === 1 ? \"\" : \"s\"}`, `${input.today.blocked} blocked`];\n if (input.today.allowed_by_person) parts.push(`${input.today.allowed_by_person} allowed by a person`);\n if (input.openReviews) parts.push(`${input.openReviews} in Review`);\n rows.push({ label: \"Today\", value: parts.join(\" · \") });\n }\n rows.push({\n label: \"Version\",\n value: updateAvailable ? `Update available (agent ${input.version.recommendedAgent ?? input.version.agent})`\n : input.version.policy === \"hold\" ? `agent ${input.version.agent} · updates managed by your workspace`\n : `Up to date (agent ${input.version.agent})`,\n });\n\n const actions: TrayAction[] = [];\n if (pending > 0 && fix?.id !== \"send_now\" && state !== \"disconnected\") actions.push(ACTIONS.send_now);\n if (updateAvailable && !input.working) actions.push(ACTIONS.update_now);\n if (fix?.id !== \"check_now\") actions.push(ACTIONS.check_now);\n if (input.workspace?.computer_url) actions.push(ACTIONS.open_workspace);\n\n const where = [input.workspace?.name, input.workspace?.environment].filter(Boolean).join(\" · \");\n const label = state === \"protected\" ? `Protected${where ? ` · ${where}` : \"\"}` : headline;\n const tooltip = `Scopebond — ${label}`;\n return {\n state, headline, fix, actions, hint, rows,\n tooltip: tooltip.length > 63 ? `${tooltip.slice(0, 62)}…` : tooltip,\n recent_blocks: (input.recentBlocks ?? []).slice(0, 3),\n };\n}\n\n/** What \"Check now\" says afterwards, for five seconds (SB387): never silence. */\nexport function checkResult(selfCheck: { ok: boolean; failed: string[] } | null, error: string | null): string {\n if (error) return `Check could not finish: ${error}`.slice(0, 200);\n if (!selfCheck) return \"Checked just now: this computer is not connected to a workspace, so only local checks ran\";\n return selfCheck.ok ? \"Checked just now: all good\" : `Check found a problem: ${selfCheck.failed[0] ?? \"see status\"}`;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scopebond/agent",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Free, open-source Scopebond Agent companion: deliver queued records, maintain rules and connections, and report computer health to the separate proprietary Scopebond Cloud workspace.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Avouro LLC",
@@ -39,9 +39,9 @@
39
39
  "dist"
40
40
  ],
41
41
  "dependencies": {
42
- "@scopebond/gateway": "0.17.0",
43
- "@scopebond/hook": "0.21.1",
44
- "@scopebond/sdk": "0.1.5"
42
+ "@scopebond/gateway": "0.17.1",
43
+ "@scopebond/hook": "0.21.2",
44
+ "@scopebond/sdk": "0.1.6"
45
45
  },
46
46
  "publishConfig": {
47
47
  "access": "public"