@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.
- package/dist/agent.d.ts +6 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +10 -3
- package/dist/agent.js.map +1 -1
- package/dist/apps-entry.d.ts +46 -0
- package/dist/apps-entry.d.ts.map +1 -0
- package/dist/apps-entry.js +118 -0
- package/dist/apps-entry.js.map +1 -0
- package/dist/autostart.d.ts +11 -5
- package/dist/autostart.d.ts.map +1 -1
- package/dist/autostart.js +59 -5
- package/dist/autostart.js.map +1 -1
- package/dist/awake.d.ts +31 -0
- package/dist/awake.d.ts.map +1 -0
- package/dist/awake.js +57 -0
- package/dist/awake.js.map +1 -0
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli-main.js +28 -1
- package/dist/cli-main.js.map +1 -1
- package/dist/index.d.ts +12 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/ipc.d.ts +6 -2
- package/dist/ipc.d.ts.map +1 -1
- package/dist/ipc.js +11 -7
- package/dist/ipc.js.map +1 -1
- package/dist/native-update.d.ts +83 -7
- package/dist/native-update.d.ts.map +1 -1
- package/dist/native-update.js +230 -33
- package/dist/native-update.js.map +1 -1
- package/dist/selfcheck.d.ts.map +1 -1
- package/dist/selfcheck.js +4 -1
- package/dist/selfcheck.js.map +1 -1
- package/dist/service.d.ts +20 -3
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +141 -44
- package/dist/service.js.map +1 -1
- package/dist/setup.d.ts +12 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +41 -12
- package/dist/setup.js.map +1 -1
- package/dist/tray-model.d.ts +2 -0
- package/dist/tray-model.d.ts.map +1 -1
- package/dist/tray-model.js +2 -0
- package/dist/tray-model.js.map +1 -1
- package/dist/update.d.ts +56 -12
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +209 -38
- package/dist/update.js.map +1 -1
- package/dist/upkeep.d.ts +16 -0
- package/dist/upkeep.d.ts.map +1 -0
- package/dist/upkeep.js +55 -0
- package/dist/upkeep.js.map +1 -0
- package/package.json +3 -3
package/dist/tray-model.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"tray-model.d.ts","sourceRoot":"","sources":["../src/tray-model.ts"],"names":[],"mappings":"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"}
|
package/dist/tray-model.js
CHANGED
|
@@ -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)
|
package/dist/tray-model.js.map
CHANGED
|
@@ -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
|
-
|
|
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
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
22
|
-
*
|
|
23
|
-
|
|
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
|
-
/**
|
|
29
|
-
|
|
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
|
package/dist/update.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"
|
|
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
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*
|
|
54
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
|
75
|
-
*
|
|
76
|
-
|
|
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
|
|
85
|
-
|
|
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
|
|
88
|
-
changed.push({ harness, file, reason: broken ? "the hook command could not start" : `moved to hook ${
|
|
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
|
-
/**
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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 =
|
|
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
|