@prismer/runtime 1.9.1 → 1.9.6

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 (47) hide show
  1. package/CHANGELOG.md +178 -0
  2. package/README.md +363 -229
  3. package/dist/cli.cjs +10269 -0
  4. package/dist/cli.cjs.map +1 -0
  5. package/dist/cli.js +10257 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.cjs +10603 -0
  8. package/dist/index.cjs.map +1 -0
  9. package/dist/index.d.cts +1643 -0
  10. package/dist/index.d.ts +1496 -1916
  11. package/dist/index.js +9702 -9177
  12. package/dist/index.js.map +1 -1
  13. package/package.json +31 -33
  14. package/dist/adapter-registry-EMZFKFYK.mjs +0 -8
  15. package/dist/adapter-registry-EMZFKFYK.mjs.map +0 -1
  16. package/dist/artifacts-uploader-N2HNBSTW.mjs +0 -159
  17. package/dist/artifacts-uploader-N2HNBSTW.mjs.map +0 -1
  18. package/dist/auto-register-M4YSPJS6.mjs +0 -204
  19. package/dist/auto-register-M4YSPJS6.mjs.map +0 -1
  20. package/dist/bin/prismer.js +0 -14988
  21. package/dist/chunk-BJTO5JO5.mjs +0 -11
  22. package/dist/chunk-BJTO5JO5.mjs.map +0 -1
  23. package/dist/chunk-JIEDFDVI.mjs +0 -148
  24. package/dist/chunk-JIEDFDVI.mjs.map +0 -1
  25. package/dist/chunk-NDNX2G6O.mjs +0 -68
  26. package/dist/chunk-NDNX2G6O.mjs.map +0 -1
  27. package/dist/chunk-POWV475F.mjs +0 -45
  28. package/dist/chunk-POWV475F.mjs.map +0 -1
  29. package/dist/chunk-VTFKZAUY.mjs +0 -68
  30. package/dist/chunk-VTFKZAUY.mjs.map +0 -1
  31. package/dist/dispatch-mux-MW4HHICS.mjs +0 -8
  32. package/dist/dispatch-mux-MW4HHICS.mjs.map +0 -1
  33. package/dist/dispatch-rpc-J7H6KC2V.mjs +0 -78
  34. package/dist/dispatch-rpc-J7H6KC2V.mjs.map +0 -1
  35. package/dist/fs-rpc-OSHJZYK6.mjs +0 -58
  36. package/dist/fs-rpc-OSHJZYK6.mjs.map +0 -1
  37. package/dist/heartbeat-loop-H2LAI3V5.mjs +0 -95
  38. package/dist/heartbeat-loop-H2LAI3V5.mjs.map +0 -1
  39. package/dist/index.d.mts +0 -2063
  40. package/dist/index.mjs +0 -8807
  41. package/dist/index.mjs.map +0 -1
  42. package/dist/mode-b-DXJ7FJAL.mjs +0 -152
  43. package/dist/mode-b-DXJ7FJAL.mjs.map +0 -1
  44. package/dist/registry-ZYU2HDFL.mjs +0 -12
  45. package/dist/registry-ZYU2HDFL.mjs.map +0 -1
  46. /package/{dist → assets}/icon +0 -0
  47. /package/{dist → assets}/smallicon +0 -0
@@ -1,95 +0,0 @@
1
- import {
2
- loadPublishedRegistry
3
- } from "./chunk-POWV475F.mjs";
4
- import "./chunk-BJTO5JO5.mjs";
5
-
6
- // src/agents/heartbeat-loop.ts
7
- function startHeartbeatLoop(opts) {
8
- const intervalMs = opts.intervalMs ?? 3e4;
9
- const offlineProtectMs = opts.offlineProtectMs ?? 5 * 6e4;
10
- const fetchImpl = opts.fetchImpl ?? fetch;
11
- const statusFor = opts.statusFor ?? (() => "online");
12
- let running = true;
13
- let consecutiveFailures = 0;
14
- let lastSuccessAt = 0;
15
- let timer = null;
16
- async function tick() {
17
- if (!running) return;
18
- let agents;
19
- try {
20
- agents = loadPublishedRegistry(opts.registryFile);
21
- } catch (err) {
22
- console.error("[heartbeat] failed to load registry:", err.message);
23
- return;
24
- }
25
- if (agents.length === 0) return;
26
- const body = {
27
- daemonId: opts.daemonId,
28
- agents: agents.map((a) => ({
29
- cloudAgentId: a.cloudAgentId,
30
- status: statusFor(a)
31
- }))
32
- };
33
- try {
34
- const resp = await fetchImpl(`${opts.cloudApiBase}/api/im/me/agents/heartbeat`, {
35
- method: "POST",
36
- headers: {
37
- "Content-Type": "application/json",
38
- Authorization: `Bearer ${opts.apiKey}`
39
- },
40
- body: JSON.stringify(body)
41
- });
42
- if (!resp.ok) {
43
- consecutiveFailures++;
44
- let text = "";
45
- try {
46
- text = (await resp.text()).slice(0, 200);
47
- } catch {
48
- }
49
- if (consecutiveFailures % 10 === 1) {
50
- console.warn(`[heartbeat] tick failed ${consecutiveFailures}\xD7 (HTTP ${resp.status}): ${text}`);
51
- }
52
- if (consecutiveFailures * intervalMs > offlineProtectMs) {
53
- }
54
- return;
55
- }
56
- consecutiveFailures = 0;
57
- lastSuccessAt = Date.now();
58
- } catch (err) {
59
- consecutiveFailures++;
60
- if (consecutiveFailures % 10 === 1) {
61
- console.warn("[heartbeat] tick failed (network):", err.message);
62
- }
63
- }
64
- }
65
- function schedule() {
66
- if (!running) return;
67
- timer = setTimeout(async () => {
68
- await tick();
69
- schedule();
70
- }, intervalMs);
71
- if (timer && typeof timer.unref === "function") {
72
- timer.unref();
73
- }
74
- }
75
- if (opts.fireImmediately !== false) {
76
- void tick();
77
- }
78
- schedule();
79
- return {
80
- stop() {
81
- running = false;
82
- if (timer) {
83
- clearTimeout(timer);
84
- timer = null;
85
- }
86
- },
87
- consecutiveFailures: () => consecutiveFailures,
88
- lastSuccessAt: () => lastSuccessAt,
89
- tick
90
- };
91
- }
92
- export {
93
- startHeartbeatLoop
94
- };
95
- //# sourceMappingURL=heartbeat-loop-H2LAI3V5.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/agents/heartbeat-loop.ts"],"sourcesContent":["/**\n * Prismer Runtime — Background heartbeat loop (Sprint A2.3, D3 liveness).\n *\n * For every agent in `~/.prismer/published-agents.toml`, send a single\n * bulk heartbeat to cloud every `intervalMs` (default 30s). Cloud's\n * sweep job marks agents offline after `heartbeatTimeoutMs` (default 90s)\n * of silence — so 3 missed beats == offline.\n *\n * Design notes:\n *\n * • Bulk in one HTTP call per tick so 5 agents = 1 request, not 5.\n * • Best-effort: a 4xx/5xx or network error is logged but never throws\n * up the call stack — the loop just retries on the next tick. The\n * daemon itself stays alive even if cloud is unreachable.\n * • Local \"offline protection\": if cloud has been unreachable for\n * more than `offlineProtectMs`, we keep the daemon's local view\n * of the world (don't surface the cloud-side `offline` to local\n * consumers). This is policy that the supervisor can read via\n * `consecutiveFailures`.\n * • Stop is cooperative — the loop checks `running` between ticks.\n */\n\nimport {\n loadPublishedRegistry,\n type PublishedAgent,\n} from './published-registry.js';\n\nexport interface HeartbeatLoopOptions {\n apiKey: string;\n daemonId: string;\n cloudApiBase: string;\n /** Tick interval in ms. Default 30_000. */\n intervalMs?: number;\n /** ms after which we treat the cloud as durably unreachable. Default 5*60_000. */\n offlineProtectMs?: number;\n /** Override fetch (tests). */\n fetchImpl?: typeof fetch;\n /** Override registry path (tests). */\n registryFile?: string;\n /** Status reporter — defaults to \"online\" for everything. Override to surface\n * per-agent crashes from the supervisor. */\n statusFor?: (agent: PublishedAgent) => 'online' | 'busy' | 'idle' | 'crashed';\n /** Fire the first tick synchronously on start. Default true (production:\n * don't make the user wait `intervalMs` to see initial liveness). Tests\n * set this to false so they can drive ticks deterministically. */\n fireImmediately?: boolean;\n}\n\nexport interface HeartbeatLoopHandle {\n stop(): void;\n /** Number of consecutive ticks where the cloud round-trip failed. */\n consecutiveFailures(): number;\n /** Last successful tick timestamp (ms epoch), or 0 if never. */\n lastSuccessAt(): number;\n /** Trigger one tick immediately (test helper / manual flush). */\n tick(): Promise<void>;\n}\n\ninterface HeartbeatRequest {\n cloudAgentId: string;\n status: 'online' | 'busy' | 'idle' | 'crashed';\n load?: number;\n}\n\nexport function startHeartbeatLoop(opts: HeartbeatLoopOptions): HeartbeatLoopHandle {\n const intervalMs = opts.intervalMs ?? 30_000;\n const offlineProtectMs = opts.offlineProtectMs ?? 5 * 60_000;\n const fetchImpl = opts.fetchImpl ?? fetch;\n const statusFor = opts.statusFor ?? (() => 'online' as const);\n\n let running = true;\n let consecutiveFailures = 0;\n let lastSuccessAt = 0;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n async function tick(): Promise<void> {\n if (!running) return;\n\n let agents: PublishedAgent[];\n try {\n agents = loadPublishedRegistry(opts.registryFile);\n } catch (err) {\n // Registry corruption shouldn't take the daemon down.\n console.error('[heartbeat] failed to load registry:', (err as Error).message);\n return;\n }\n\n if (agents.length === 0) return;\n\n const body: { daemonId: string; agents: HeartbeatRequest[] } = {\n daemonId: opts.daemonId,\n agents: agents.map((a) => ({\n cloudAgentId: a.cloudAgentId,\n status: statusFor(a),\n })),\n };\n\n try {\n const resp = await fetchImpl(`${opts.cloudApiBase}/api/im/me/agents/heartbeat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${opts.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n if (!resp.ok) {\n consecutiveFailures++;\n // Read the body for the log but do not let parse errors propagate.\n let text = '';\n try {\n text = (await resp.text()).slice(0, 200);\n } catch {\n /* ignore */\n }\n if (consecutiveFailures % 10 === 1) {\n // Log every 1st, 11th, 21st failure to avoid log spam during\n // sustained outages while still surfacing the issue.\n console.warn(`[heartbeat] tick failed ${consecutiveFailures}× (HTTP ${resp.status}): ${text}`);\n }\n if (consecutiveFailures * intervalMs > offlineProtectMs) {\n // Past the protection window — let the supervisor decide whether\n // to flip local state. We don't take action ourselves so the\n // daemon stays useful for LAN-only consumers.\n }\n return;\n }\n consecutiveFailures = 0;\n lastSuccessAt = Date.now();\n } catch (err) {\n consecutiveFailures++;\n if (consecutiveFailures % 10 === 1) {\n console.warn('[heartbeat] tick failed (network):', (err as Error).message);\n }\n }\n }\n\n function schedule(): void {\n if (!running) return;\n timer = setTimeout(async () => {\n await tick();\n schedule();\n }, intervalMs);\n // Don't keep the event loop alive just for the heartbeat — daemon\n // stops when its other resources stop.\n if (timer && typeof (timer as any).unref === 'function') {\n (timer as any).unref();\n }\n }\n\n if (opts.fireImmediately !== false) {\n // Fire one tick immediately so we don't wait `intervalMs` on startup.\n void tick();\n }\n schedule();\n\n return {\n stop() {\n running = false;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n },\n consecutiveFailures: () => consecutiveFailures,\n lastSuccessAt: () => lastSuccessAt,\n tick,\n };\n}\n"],"mappings":";;;;;;AAgEO,SAAS,mBAAmB,MAAiD;AAClF,QAAM,aAAa,KAAK,cAAc;AACtC,QAAM,mBAAmB,KAAK,oBAAoB,IAAI;AACtD,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,YAAY,KAAK,cAAc,MAAM;AAE3C,MAAI,UAAU;AACd,MAAI,sBAAsB;AAC1B,MAAI,gBAAgB;AACpB,MAAI,QAA8C;AAElD,iBAAe,OAAsB;AACnC,QAAI,CAAC,QAAS;AAEd,QAAI;AACJ,QAAI;AACF,eAAS,sBAAsB,KAAK,YAAY;AAAA,IAClD,SAAS,KAAK;AAEZ,cAAQ,MAAM,wCAAyC,IAAc,OAAO;AAC5E;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,EAAG;AAEzB,UAAM,OAAyD;AAAA,MAC7D,UAAU,KAAK;AAAA,MACf,QAAQ,OAAO,IAAI,CAAC,OAAO;AAAA,QACzB,cAAc,EAAE;AAAA,QAChB,QAAQ,UAAU,CAAC;AAAA,MACrB,EAAE;AAAA,IACJ;AAEA,QAAI;AACF,YAAM,OAAO,MAAM,UAAU,GAAG,KAAK,YAAY,+BAA+B;AAAA,QAC9E,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,UAAU,KAAK,MAAM;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B,CAAC;AACD,UAAI,CAAC,KAAK,IAAI;AACZ;AAEA,YAAI,OAAO;AACX,YAAI;AACF,kBAAQ,MAAM,KAAK,KAAK,GAAG,MAAM,GAAG,GAAG;AAAA,QACzC,QAAQ;AAAA,QAER;AACA,YAAI,sBAAsB,OAAO,GAAG;AAGlC,kBAAQ,KAAK,2BAA2B,mBAAmB,cAAW,KAAK,MAAM,MAAM,IAAI,EAAE;AAAA,QAC/F;AACA,YAAI,sBAAsB,aAAa,kBAAkB;AAAA,QAIzD;AACA;AAAA,MACF;AACA,4BAAsB;AACtB,sBAAgB,KAAK,IAAI;AAAA,IAC3B,SAAS,KAAK;AACZ;AACA,UAAI,sBAAsB,OAAO,GAAG;AAClC,gBAAQ,KAAK,sCAAuC,IAAc,OAAO;AAAA,MAC3E;AAAA,IACF;AAAA,EACF;AAEA,WAAS,WAAiB;AACxB,QAAI,CAAC,QAAS;AACd,YAAQ,WAAW,YAAY;AAC7B,YAAM,KAAK;AACX,eAAS;AAAA,IACX,GAAG,UAAU;AAGb,QAAI,SAAS,OAAQ,MAAc,UAAU,YAAY;AACvD,MAAC,MAAc,MAAM;AAAA,IACvB;AAAA,EACF;AAEA,MAAI,KAAK,oBAAoB,OAAO;AAElC,SAAK,KAAK;AAAA,EACZ;AACA,WAAS;AAET,SAAO;AAAA,IACL,OAAO;AACL,gBAAU;AACV,UAAI,OAAO;AACT,qBAAa,KAAK;AAClB,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,qBAAqB,MAAM;AAAA,IAC3B,eAAe,MAAM;AAAA,IACrB;AAAA,EACF;AACF;","names":[]}