@scopebond/agent 0.5.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/agent.d.ts +6 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +10 -3
  4. package/dist/agent.js.map +1 -1
  5. package/dist/apps-entry.d.ts +46 -0
  6. package/dist/apps-entry.d.ts.map +1 -0
  7. package/dist/apps-entry.js +118 -0
  8. package/dist/apps-entry.js.map +1 -0
  9. package/dist/autostart.d.ts +11 -5
  10. package/dist/autostart.d.ts.map +1 -1
  11. package/dist/autostart.js +59 -5
  12. package/dist/autostart.js.map +1 -1
  13. package/dist/awake.d.ts +31 -0
  14. package/dist/awake.d.ts.map +1 -0
  15. package/dist/awake.js +57 -0
  16. package/dist/awake.js.map +1 -0
  17. package/dist/cli-main.d.ts.map +1 -1
  18. package/dist/cli-main.js +28 -1
  19. package/dist/cli-main.js.map +1 -1
  20. package/dist/index.d.ts +12 -7
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +8 -5
  23. package/dist/index.js.map +1 -1
  24. package/dist/ipc.d.ts +6 -2
  25. package/dist/ipc.d.ts.map +1 -1
  26. package/dist/ipc.js +11 -7
  27. package/dist/ipc.js.map +1 -1
  28. package/dist/native-update.d.ts +83 -7
  29. package/dist/native-update.d.ts.map +1 -1
  30. package/dist/native-update.js +230 -33
  31. package/dist/native-update.js.map +1 -1
  32. package/dist/selfcheck.d.ts.map +1 -1
  33. package/dist/selfcheck.js +4 -1
  34. package/dist/selfcheck.js.map +1 -1
  35. package/dist/service.d.ts +20 -3
  36. package/dist/service.d.ts.map +1 -1
  37. package/dist/service.js +141 -44
  38. package/dist/service.js.map +1 -1
  39. package/dist/setup.d.ts +12 -0
  40. package/dist/setup.d.ts.map +1 -1
  41. package/dist/setup.js +41 -12
  42. package/dist/setup.js.map +1 -1
  43. package/dist/tray-model.d.ts +2 -0
  44. package/dist/tray-model.d.ts.map +1 -1
  45. package/dist/tray-model.js +2 -0
  46. package/dist/tray-model.js.map +1 -1
  47. package/dist/update.d.ts +56 -12
  48. package/dist/update.d.ts.map +1 -1
  49. package/dist/update.js +209 -38
  50. package/dist/update.js.map +1 -1
  51. package/dist/upkeep.d.ts +16 -0
  52. package/dist/upkeep.d.ts.map +1 -0
  53. package/dist/upkeep.js +55 -0
  54. package/dist/upkeep.js.map +1 -0
  55. package/package.json +3 -3
@@ -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":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,iHAAiH;AACjH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,cAAc,CAAC;AAEvG,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,UAAU,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,gBAAgB,CAAC;IACxF,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,SAAS,CAAC;IACjB,qGAAqG;IACrG,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,uFAAuF;IACvF,IAAI,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,wFAAwF;IACxF,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACvB,6CAA6C;IAC7C,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,aAAa,EAAE,WAAW,EAAE,CAAC;CAC9B;AAED,6HAA6H;AAC7H,MAAM,WAAW,WAAW;IAAG,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,IAAI,CAAA;CAAE;AAElK,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,2GAA2G;IAC3G,UAAU,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC9F,gHAAgH;IAChH,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACvG,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAAC;QAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtJ,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IACnG,YAAY,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+GAA+G;IAC/G,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAID,mGAAmG;AACnG,eAAO,MAAM,oBAAoB,QAAW,CAAC;AAC7C,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,QAAW,CAAC;AAC7C,mEAAmE;AACnE,eAAO,MAAM,cAAc,QAAO,CAAC;AAEnC,wGAAwG;AACxG,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAErE;AAED,sGAAsG;AACtG,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEnE;AAED,kHAAkH;AAClH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEpE;AAED,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAG7E;AAED,wBAAgB,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAQnD;AAuBD,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CA0FrD;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,SAAS,EAAE;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAI7G"}
@@ -147,6 +147,8 @@ export function trayModel(input) {
147
147
  }
148
148
  if (input.today) {
149
149
  const parts = [`${input.today.actions} action${input.today.actions === 1 ? "" : "s"}`, `${input.today.blocked} blocked`];
150
+ if (input.today.recorded_after)
151
+ parts.push(`${input.today.recorded_after} recorded, not prevented`);
150
152
  if (input.today.allowed_by_person)
151
153
  parts.push(`${input.today.allowed_by_person} allowed by a person`);
152
154
  if (input.openReviews)
@@ -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\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"]}
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 /** `recorded_after`: out of policy but reported only after it ran (a Cursor edit); never counted as blocked. */\n today: { actions: number; blocked: number; recorded_after?: 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.recorded_after) parts.push(`${input.today.recorded_after} recorded, not prevented`);\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/dist/update.d.ts CHANGED
@@ -1,33 +1,77 @@
1
1
  import { type Harness, type HookConnection } from "@scopebond/hook";
2
+ /** The largest answer to /v1/client-version the agent reads. */
3
+ export declare const CLIENT_VERSION_MAX_BYTES = 4096;
4
+ /** The only registry the agent installs from. */
5
+ export declare const NPM_REGISTRY = "https://registry.npmjs.org/";
6
+ /** The repository and branch an installed agent's npm provenance must name. */
7
+ export declare const PROVENANCE_REPOSITORY = "https://github.com/avouro-com/scopebond";
8
+ export declare const PROVENANCE_REF = "refs/heads/main";
2
9
  export declare function agentVersion(): string;
10
+ export declare const isVersion: (v: unknown) => v is string;
3
11
  export interface ClientVersion {
4
12
  policy: "recommended" | "hold";
5
13
  hook: string | null;
6
14
  agent: string | null;
7
15
  }
8
- export declare function fetchClientVersion(connection: Pick<HookConnection, "url" | "credential">, fetchImpl?: typeof fetch, timeoutMs?: number): Promise<ClientVersion | null>;
16
+ /** The versions the workspace names for this computer, or null when there is no usable answer. Anything unexpected — an
17
+ * answer over the size limit, not an object, a policy other than "recommended" or "hold", a version that is not a strict
18
+ * x.y.z or is more than one major version ahead — refuses the whole answer, so nothing changes. */
19
+ export declare function fetchClientVersion(connection: Pick<HookConnection, "url" | "credential">, fetchImpl?: typeof fetch, timeoutMs?: number, running?: {
20
+ agent: string;
21
+ hook: string;
22
+ }): Promise<ClientVersion | null>;
9
23
  /** Compare x.y.z versions: negative when a < b. */
10
24
  export declare function compareVersions(a: string, b: string): number;
11
25
  /** The hook version a settings command runs, when it names one (an `npx` pin or a pinned copy). */
12
26
  export declare function commandHookVersion(command: string): string | null;
13
- /** The command an agent-maintained settings entry runs: the hook version this agent carries,
14
- * through npx, so it resolves Node at run time and an agent update never swaps files under it. */
15
- /** The hook command upkeep writes. The hook this agent carries is pinned by its absolute path (fast,
16
- * and it needs neither the registry nor npx on the coding tool's PATH); another version, or a pin that
17
- * cannot be made, falls back to the portable npx form. */
18
- export declare function maintainedHookCommand(harness: Harness, version?: string): string;
27
+ /** The hook command upkeep writes: always the hook this agent carries, by its absolute path (a durable pinned copy when the
28
+ * carried one lives somewhere npm may clear). Never an `npx` form, which would resolve a package from the registry each
29
+ * time the coding tool runs it. */
30
+ export declare function maintainedHookCommand(harness: Harness): string;
19
31
  /** This Node's own npm, run directly (no shell, and never another Node's npm first on PATH), or null. */
20
32
  export declare function ownNpm(): [string, string[]] | null;
21
- /** Bring the user-level Scopebond hook entries to `version`, and repair any that cannot start.
22
- * Entries of other tools are never touched. Returns the files changed. */
23
- export declare function maintainHookEntries(harnesses: Harness[], version?: string, moveVersions?: boolean): Array<{
33
+ /** Bring the user-level Scopebond hook entries to the hook this agent carries, and repair any that cannot start. Under
34
+ * "recommended" (`moveVersions`) an entry naming an older hook, or any `npx` entry, is pinned to the carried hook; under
35
+ * "hold" only broken entries and `npx` entries that name no version or the carried one are (their version does not
36
+ * change). Entries of other tools are never touched. Returns the files changed. */
37
+ export declare function maintainHookEntries(harnesses: Harness[], moveVersions?: boolean): Array<{
24
38
  harness: Harness;
25
39
  file: string;
26
40
  reason: string;
27
41
  }>;
28
- /** Install `@scopebond/agent@version` globally with this computer's npm. */
29
- export declare function installAgent(version: string, timeoutMs?: number): Promise<{
42
+ /** npm's arguments for a global install of `spec`: package scripts off, the public registry pinned (also for the
43
+ * @scopebond scope, which a scope registry setting would otherwise take over). */
44
+ export declare function npmInstallArgs(spec: string): string[];
45
+ /** The environment npm runs with: this process's, without settings that would change the registry or turn scripts back on. */
46
+ export declare function npmEnvironment(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
47
+ export type ProvenanceResult = {
48
+ ok: true;
49
+ } | {
50
+ ok: false;
51
+ reason: string;
52
+ };
53
+ /** Check, before installing, that npm's provenance for exactly `@scopebond/agent@version` says it was built by this
54
+ * repository's main branch, for the tarball the registry serves. The statement is the one the registry verified when the
55
+ * package was published; it is read here, not re-verified against Sigstore. */
56
+ export declare function checkProvenance(version: string, fetchImpl?: typeof fetch, timeoutMs?: number): Promise<ProvenanceResult>;
57
+ /** Install `@scopebond/agent@version` globally with this computer's npm: provenance checked first (unless `provenance` is
58
+ * false, for going back to the version already running), package scripts off, the public registry pinned. */
59
+ export declare function installAgent(version: string, o?: {
60
+ timeoutMs?: number;
61
+ fetchImpl?: typeof fetch;
62
+ provenance?: boolean;
63
+ }): Promise<{
64
+ ok: boolean;
65
+ output: string;
66
+ }>;
67
+ /** Run the agent program now on disk (`version`) and confirm it starts and reports `version`. */
68
+ export declare function startCheck(version: string, cli?: string, timeoutMs?: number): Promise<{
30
69
  ok: boolean;
31
70
  output: string;
32
71
  }>;
72
+ /** A version that was installed but did not start, kept so it is not installed again for a while. */
73
+ export declare const FAILED_UPDATE_FILE = "update-failed.json";
74
+ export declare const FAILED_UPDATE_RETRY_MS: number;
75
+ export declare function recordFailedUpdate(dir: string, version: string, now?: number): void;
76
+ export declare function recentlyFailedUpdate(dir: string, version: string, now?: number): boolean;
33
77
  //# sourceMappingURL=update.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"AAQA,OAAO,EAEmD,KAAK,OAAO,EAAE,KAAK,cAAc,EAC1F,MAAM,iBAAiB,CAAC;AAOzB,wBAAgB,YAAY,IAAI,MAAM,CAMrC;AAED,MAAM,WAAW,aAAa;IAAG,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AAE5G,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,YAAY,CAAC,EAAE,SAAS,GAAE,OAAO,KAAa,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAUnL;AAED,mDAAmD;AACnD,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5D;AAED,mGAAmG;AACnG,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIjE;AAED;mGACmG;AACnG;;2DAE2D;AAC3D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,SAAgB,GAAG,MAAM,CAUvF;AAED,yGAAyG;AACzG,wBAAgB,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAGlD;AAED;2EAC2E;AAC3E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,OAAO,SAAgB,EAAE,YAAY,UAAO,GAAG,KAAK,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAajK;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,SAAa,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB9G"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"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"}
package/dist/update.js CHANGED
@@ -1,12 +1,25 @@
1
- // Updates under the workspace's control (D130). The agent asks the workspace which versions this
2
- // computer should run; with "recommended" it installs the named agent from npm (which brings the
3
- // matching hook, pinned exactly) and restarts; with "hold" it changes nothing. The hook entries in
4
- // the agent settings are moved to the hook version the agent carries.
1
+ // Updates under the workspace's control. The agent asks the workspace which versions this computer should run; with
2
+ // "recommended" it installs the named agent from npm (which brings the matching hook, pinned exactly) and restarts; with
3
+ // "hold" it changes nothing. The hook entries in the agent settings are pinned to the hook version the agent carries.
4
+ //
5
+ // The npm path is hardened: the workspace's answer is bounded (size, strict x.y.z, no more than one major version ahead),
6
+ // the exact version must carry npm provenance from this repository's main branch, npm runs with package scripts off and
7
+ // the public registry pinned, the new program must start and report the version before the old one hands over, and a
8
+ // version that did not start is rolled back and not retried for a day.
5
9
  import { spawn } from "node:child_process";
6
- import { existsSync, readFileSync } from "node:fs";
10
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
7
11
  import { dirname, join } from "node:path";
8
12
  import { absoluteHookCommand, configuredHookCommands, ensureDurableRuntime, hookCliPath, hookCommandResolves, hookVersion, isScopebondHookCommand, isSingleExecutable, nativeHookCommand, userHarnessFile, writeHarnessConfig, } from "@scopebond/hook";
9
- const VERSION = /^[0-9]+\.[0-9]+\.[0-9]+$/;
13
+ import { agentCliPath } from "./self.js";
14
+ /** x.y.z with no leading zeros, no tags, and each part of a sane size. */
15
+ const VERSION = /^(0|[1-9][0-9]{0,3})\.(0|[1-9][0-9]{0,3})\.(0|[1-9][0-9]{0,5})$/;
16
+ /** The largest answer to /v1/client-version the agent reads. */
17
+ export const CLIENT_VERSION_MAX_BYTES = 4096;
18
+ /** The only registry the agent installs from. */
19
+ export const NPM_REGISTRY = "https://registry.npmjs.org/";
20
+ /** The repository and branch an installed agent's npm provenance must name. */
21
+ export const PROVENANCE_REPOSITORY = "https://github.com/avouro-com/scopebond";
22
+ export const PROVENANCE_REF = "refs/heads/main";
10
23
  export function agentVersion() {
11
24
  if (typeof __SCOPEBOND_AGENT_VERSION__ === "string")
12
25
  return __SCOPEBOND_AGENT_VERSION__;
@@ -18,16 +31,64 @@ export function agentVersion() {
18
31
  return "0.0.0";
19
32
  }
20
33
  }
21
- export async function fetchClientVersion(connection, fetchImpl = fetch, timeoutMs = 10_000) {
34
+ export const isVersion = (v) => typeof v === "string" && VERSION.test(v);
35
+ /** A response body as text, or null when it is larger than `max` bytes. */
36
+ async function boundedText(res, max) {
37
+ const declared = Number(res.headers.get("content-length"));
38
+ if (Number.isFinite(declared) && declared > max)
39
+ return null;
40
+ if (!res.body)
41
+ return "";
42
+ const reader = res.body.getReader();
43
+ const chunks = [];
44
+ let size = 0;
45
+ for (;;) {
46
+ const { done, value } = await reader.read();
47
+ if (done)
48
+ break;
49
+ size += value.byteLength;
50
+ if (size > max) {
51
+ void reader.cancel().catch(() => { });
52
+ return null;
53
+ }
54
+ chunks.push(value);
55
+ }
56
+ return Buffer.concat(chunks).toString("utf8");
57
+ }
58
+ /** Whether `named` is a version this computer may be moved to from `running`: well formed and at most one major ahead. */
59
+ function inRange(named, running) {
60
+ const major = Number(named.split(".")[0]);
61
+ const current = Number((isVersion(running) ? running : "0.0.0").split(".")[0]);
62
+ return major <= current + 1;
63
+ }
64
+ /** The versions the workspace names for this computer, or null when there is no usable answer. Anything unexpected — an
65
+ * answer over the size limit, not an object, a policy other than "recommended" or "hold", a version that is not a strict
66
+ * x.y.z or is more than one major version ahead — refuses the whole answer, so nothing changes. */
67
+ export async function fetchClientVersion(connection, fetchImpl = fetch, timeoutMs = 10_000, running = { agent: agentVersion(), hook: hookVersion() }) {
22
68
  try {
23
69
  const res = await fetchImpl(new URL("/v1/client-version", connection.url).toString(), {
24
70
  headers: { authorization: `Bearer ${connection.credential}` }, redirect: "error", signal: AbortSignal.timeout(timeoutMs),
25
71
  });
26
72
  if (!res.ok)
27
73
  return null;
28
- const body = await res.json();
29
- const ok = (v) => typeof v === "string" && VERSION.test(v) ? v : null;
30
- return { policy: body.policy === "hold" ? "hold" : "recommended", hook: ok(body.hook), agent: ok(body.agent) };
74
+ const text = await boundedText(res, CLIENT_VERSION_MAX_BYTES);
75
+ if (text === null)
76
+ return null;
77
+ const body = JSON.parse(text);
78
+ if (!body || typeof body !== "object" || Array.isArray(body))
79
+ return null;
80
+ const { policy, hook, agent } = body;
81
+ if (policy !== "recommended" && policy !== "hold")
82
+ return null;
83
+ const version = (v, current) => {
84
+ if (v === null || v === undefined)
85
+ return null;
86
+ return isVersion(v) && inRange(v, current) ? v : undefined;
87
+ };
88
+ const h = version(hook, running.hook), a = version(agent, running.agent);
89
+ if (h === undefined || a === undefined)
90
+ return null;
91
+ return { policy, hook: h, agent: a };
31
92
  }
32
93
  catch {
33
94
  return null;
@@ -47,59 +108,131 @@ export function commandHookVersion(command) {
47
108
  ?? /[\\/]runtime[\\/]([0-9]+\.[0-9]+\.[0-9]+)[\\/]/.exec(command)?.[1]
48
109
  ?? null;
49
110
  }
50
- /** The command an agent-maintained settings entry runs: the hook version this agent carries,
51
- * through npx, so it resolves Node at run time and an agent update never swaps files under it. */
52
- /** The hook command upkeep writes. The hook this agent carries is pinned by its absolute path (fast,
53
- * and it needs neither the registry nor npx on the coding tool's PATH); another version, or a pin that
54
- * cannot be made, falls back to the portable npx form. */
55
- export function maintainedHookCommand(harness, version = hookVersion()) {
111
+ const isNpxCommand = (command) => /(^|\s)npx(\.cmd)?\s/.test(command);
112
+ /** The hook command upkeep writes: always the hook this agent carries, by its absolute path (a durable pinned copy when the
113
+ * carried one lives somewhere npm may clear). Never an `npx` form, which would resolve a package from the registry each
114
+ * time the coding tool runs it. */
115
+ export function maintainedHookCommand(harness) {
56
116
  // The single executable carries its own hook, at the path the installer keeps.
57
117
  if (isSingleExecutable())
58
118
  return nativeHookCommand(harness);
59
- if (version === hookVersion()) {
60
- try {
61
- const pin = ensureDurableRuntime(hookCliPath(), version);
62
- if (pin.cli)
63
- return absoluteHookCommand(pin.cli, harness);
64
- }
65
- catch { /* fall back below */ }
119
+ const cli = hookCliPath();
120
+ try {
121
+ const pin = ensureDurableRuntime(cli, hookVersion());
122
+ if (pin.cli)
123
+ return absoluteHookCommand(pin.cli, harness);
66
124
  }
67
- return `npx -y @scopebond/hook@${version} ${harness}`;
125
+ catch { /* the carried hook's own path below */ }
126
+ return absoluteHookCommand(cli, harness);
68
127
  }
69
128
  /** This Node's own npm, run directly (no shell, and never another Node's npm first on PATH), or null. */
70
129
  export function ownNpm() {
71
130
  const cli = join(dirname(process.execPath), process.platform === "win32" ? "" : "../lib", "node_modules", "npm", "bin", "npm-cli.js");
72
131
  return existsSync(cli) ? [process.execPath, [cli]] : null;
73
132
  }
74
- /** Bring the user-level Scopebond hook entries to `version`, and repair any that cannot start.
75
- * Entries of other tools are never touched. Returns the files changed. */
76
- export function maintainHookEntries(harnesses, version = hookVersion(), moveVersions = true) {
133
+ /** Bring the user-level Scopebond hook entries to the hook this agent carries, and repair any that cannot start. Under
134
+ * "recommended" (`moveVersions`) an entry naming an older hook, or any `npx` entry, is pinned to the carried hook; under
135
+ * "hold" only broken entries and `npx` entries that name no version or the carried one are (their version does not
136
+ * change). Entries of other tools are never touched. Returns the files changed. */
137
+ export function maintainHookEntries(harnesses, moveVersions = true) {
138
+ const carried = hookVersion();
77
139
  const changed = [];
78
140
  for (const harness of harnesses) {
79
141
  const file = userHarnessFile(harness);
80
142
  const ours = configuredHookCommands(file).filter((c) => isScopebondHookCommand(c));
81
143
  if (!ours.length)
82
144
  continue;
83
- const broken = ours.some((c) => !hookCommandResolves(c));
84
- const behind = moveVersions && ours.some((c) => { const v = commandHookVersion(c); return v !== null && compareVersions(v, version) < 0; });
85
- if (!broken && !behind)
145
+ const broken = ours.some((c) => !isNpxCommand(c) && !hookCommandResolves(c));
146
+ const npx = ours.some((c) => { if (!isNpxCommand(c))
147
+ return false; const v = commandHookVersion(c); return moveVersions || v === null || v === carried; });
148
+ const behind = moveVersions && ours.some((c) => { const v = commandHookVersion(c); return v !== null && compareVersions(v, carried) < 0; });
149
+ if (!broken && !npx && !behind)
86
150
  continue;
87
- writeHarnessConfig(file, harness, maintainedHookCommand(harness, version));
88
- changed.push({ harness, file, reason: broken ? "the hook command could not start" : `moved to hook ${version}` });
151
+ writeHarnessConfig(file, harness, maintainedHookCommand(harness));
152
+ changed.push({ harness, file, reason: broken ? "the hook command could not start" : npx ? `pinned to the hook this agent carries (${carried})` : `moved to hook ${carried}` });
89
153
  }
90
154
  return changed;
91
155
  }
92
- /** Install `@scopebond/agent@version` globally with this computer's npm. */
93
- export function installAgent(version, timeoutMs = 5 * 60_000) {
94
- if (!VERSION.test(version))
95
- return Promise.resolve({ ok: false, output: "not a version" });
156
+ /** npm's arguments for a global install of `spec`: package scripts off, the public registry pinned (also for the
157
+ * @scopebond scope, which a scope registry setting would otherwise take over). */
158
+ export function npmInstallArgs(spec) {
159
+ return ["install", "-g", spec, "--ignore-scripts", `--registry=${NPM_REGISTRY}`, `--@scopebond:registry=${NPM_REGISTRY}`, "--no-fund", "--no-audit"];
160
+ }
161
+ /** The environment npm runs with: this process's, without settings that would change the registry or turn scripts back on. */
162
+ export function npmEnvironment(env = process.env) {
163
+ const out = {};
164
+ for (const [key, value] of Object.entries(env)) {
165
+ const k = key.toLowerCase();
166
+ if (k.startsWith("npm_config_") && (k.endsWith("registry") || k.includes("ignore_scripts") || k.includes("ignore-scripts") || k.includes("script_shell") || k.includes("script-shell")))
167
+ continue;
168
+ out[key] = value;
169
+ }
170
+ return out;
171
+ }
172
+ /** Check, before installing, that npm's provenance for exactly `@scopebond/agent@version` says it was built by this
173
+ * repository's main branch, for the tarball the registry serves. The statement is the one the registry verified when the
174
+ * package was published; it is read here, not re-verified against Sigstore. */
175
+ export async function checkProvenance(version, fetchImpl = fetch, timeoutMs = 30_000) {
176
+ const get = async (url, max) => {
177
+ const res = await fetchImpl(url, { redirect: "error", signal: AbortSignal.timeout(timeoutMs), headers: { accept: "application/json" } });
178
+ if (!res.ok)
179
+ throw new Error(`${res.status}`);
180
+ const text = await boundedText(res, max);
181
+ if (text === null)
182
+ throw new Error("too large");
183
+ return JSON.parse(text);
184
+ };
185
+ try {
186
+ const meta = await get(`${NPM_REGISTRY}@scopebond%2fagent/${version}`, 512 * 1024);
187
+ if (meta?.version !== version)
188
+ return { ok: false, reason: "the registry did not describe this version" };
189
+ const integrity = meta.dist?.integrity ?? "";
190
+ const att = meta.dist?.attestations;
191
+ if (!att?.url || !att.provenance?.predicateType?.startsWith("https://slsa.dev/provenance/"))
192
+ return { ok: false, reason: "this version has no npm provenance" };
193
+ if (!integrity.startsWith("sha512-"))
194
+ return { ok: false, reason: "this version has no sha512 integrity" };
195
+ const want = Buffer.from(integrity.slice("sha512-".length), "base64").toString("hex");
196
+ if (!att.url.startsWith(`${NPM_REGISTRY}-/npm/v1/attestations/`))
197
+ return { ok: false, reason: "the provenance is not on the registry" };
198
+ const bundle = await get(att.url, 1024 * 1024);
199
+ const slsa = bundle?.attestations?.find((a) => a.predicateType?.startsWith("https://slsa.dev/provenance/"));
200
+ if (!slsa?.bundle?.dsseEnvelope?.payload)
201
+ return { ok: false, reason: "this version has no npm provenance" };
202
+ const statement = JSON.parse(Buffer.from(slsa.bundle.dsseEnvelope.payload, "base64").toString("utf8"));
203
+ const subject = statement.subject?.find((s) => s.name === `pkg:npm/%40scopebond/agent@${version}`);
204
+ if (!subject || subject.digest?.sha512 !== want)
205
+ return { ok: false, reason: "the provenance is for another tarball" };
206
+ const workflow = statement.predicate?.buildDefinition?.externalParameters?.workflow;
207
+ if (workflow?.repository !== PROVENANCE_REPOSITORY)
208
+ return { ok: false, reason: `the provenance names ${workflow?.repository ?? "no repository"}, not ${PROVENANCE_REPOSITORY}` };
209
+ if (workflow.ref !== PROVENANCE_REF)
210
+ return { ok: false, reason: `the provenance names ${workflow.ref ?? "no branch"}, not ${PROVENANCE_REF}` };
211
+ return { ok: true };
212
+ }
213
+ catch (error) {
214
+ return { ok: false, reason: `the registry could not be asked (${error.message.slice(0, 80)})` };
215
+ }
216
+ }
217
+ /** Install `@scopebond/agent@version` globally with this computer's npm: provenance checked first (unless `provenance` is
218
+ * false, for going back to the version already running), package scripts off, the public registry pinned. */
219
+ export async function installAgent(version, o = {}) {
220
+ if (!isVersion(version))
221
+ return { ok: false, output: "not a version" };
222
+ if (o.provenance !== false) {
223
+ const checked = await checkProvenance(version, o.fetchImpl);
224
+ if (!checked.ok)
225
+ return { ok: false, output: `npm provenance check failed: ${checked.reason}; nothing was installed` };
226
+ }
227
+ const timeoutMs = o.timeoutMs ?? 5 * 60_000;
96
228
  return new Promise((resolve) => {
97
229
  const win = process.platform === "win32";
98
- const args = ["install", "-g", `@scopebond/agent@${version}`, "--no-fund", "--no-audit"];
230
+ const args = npmInstallArgs(`@scopebond/agent@${version}`);
231
+ const env = npmEnvironment();
99
232
  // This Node's own npm when it can be found; otherwise npm on PATH (a .cmd on Windows, which Node only
100
233
  // starts through a shell; the arguments are fixed and the version is validated).
101
234
  const own = ownNpm();
102
- const child = own ? spawn(own[0], [...own[1], ...args], { windowsHide: true }) : spawn(win ? "npm.cmd" : "npm", args, { shell: win, windowsHide: true });
235
+ const child = own ? spawn(own[0], [...own[1], ...args], { windowsHide: true, env }) : spawn(win ? "npm.cmd" : "npm", args, { shell: win, windowsHide: true, env });
103
236
  let output = "";
104
237
  child.stdout?.on("data", (d) => { output += d; });
105
238
  child.stderr?.on("data", (d) => { output += d; });
@@ -108,4 +241,42 @@ export function installAgent(version, timeoutMs = 5 * 60_000) {
108
241
  child.on("close", (code) => { clearTimeout(timer); resolve({ ok: code === 0, output: output.slice(-2000) }); });
109
242
  });
110
243
  }
244
+ /** Run the agent program now on disk (`version`) and confirm it starts and reports `version`. */
245
+ export function startCheck(version, cli = agentCliPath(), timeoutMs = 60_000) {
246
+ return new Promise((resolve) => {
247
+ const child = spawn(process.execPath, ["--disable-warning=ExperimentalWarning", cli, "version"], {
248
+ windowsHide: true, stdio: ["ignore", "pipe", "pipe"], env: { ...process.env, SCOPEBOND_AGENT_TRAY: "off" },
249
+ });
250
+ let output = "";
251
+ child.stdout?.on("data", (d) => { if (output.length < 4096)
252
+ output += d; });
253
+ child.stderr?.on("data", (d) => { if (output.length < 4096)
254
+ output += d; });
255
+ const timer = setTimeout(() => { child.kill(); resolve({ ok: false, output: `${output}\ntimed out` }); }, timeoutMs);
256
+ child.on("error", (error) => { clearTimeout(timer); resolve({ ok: false, output: String(error) }); });
257
+ child.on("close", (code) => {
258
+ clearTimeout(timer);
259
+ const first = output.trim().split(/\r?\n/)[0] ?? "";
260
+ resolve({ ok: code === 0 && first.split(/\s+/)[0] === `agent/${version}`, output: output.trim().slice(-500) });
261
+ });
262
+ });
263
+ }
264
+ /** A version that was installed but did not start, kept so it is not installed again for a while. */
265
+ export const FAILED_UPDATE_FILE = "update-failed.json";
266
+ export const FAILED_UPDATE_RETRY_MS = 24 * 60 * 60 * 1000;
267
+ export function recordFailedUpdate(dir, version, now = Date.now()) {
268
+ try {
269
+ writeFileSync(join(dir, FAILED_UPDATE_FILE), `${JSON.stringify({ version, at: now })}\n`, { mode: 0o600 });
270
+ }
271
+ catch { /* best effort */ }
272
+ }
273
+ export function recentlyFailedUpdate(dir, version, now = Date.now()) {
274
+ try {
275
+ const r = JSON.parse(readFileSync(join(dir, FAILED_UPDATE_FILE), "utf8"));
276
+ return r.version === version && typeof r.at === "number" && now - r.at >= 0 && now - r.at < FAILED_UPDATE_RETRY_MS;
277
+ }
278
+ catch {
279
+ return false;
280
+ }
281
+ }
111
282
  //# sourceMappingURL=update.js.map