@north-light/crouter 0.3.321 → 0.3.323

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 (111) hide show
  1. package/bin/progress-painter.mjs +89 -0
  2. package/bin/runtime-selector.mjs +123 -4
  3. package/dist/api/client.d.ts +35 -18
  4. package/dist/api/client.js +2 -2
  5. package/dist/api/dto/broker-ops.d.ts +9 -0
  6. package/dist/api/dto/canvas.d.ts +23 -1
  7. package/dist/api/dto/modelauth.d.ts +15 -0
  8. package/dist/api/dto/nodes.d.ts +8 -0
  9. package/dist/api/errors.d.ts +6 -3
  10. package/dist/api/errors.js +1 -1
  11. package/dist/api/index.d.ts +2 -2
  12. package/dist/api/index.js +1 -1
  13. package/dist/api/node-transport.d.ts +18 -0
  14. package/dist/api/node-transport.js +1 -0
  15. package/dist/api/routes.d.ts +3 -0
  16. package/dist/api/routes.js +1 -1
  17. package/dist/builtin-memory/crouter-sdk.md +86 -0
  18. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +1 -1
  19. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +1 -1
  20. package/dist/cli.js +8 -2
  21. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  22. package/dist/clients/attach/overlays/graph.d.ts +6 -5
  23. package/dist/clients/attach/overlays/graph.js +1 -1
  24. package/dist/clients/attach/render/chat-view.d.ts +5 -1
  25. package/dist/clients/attach/render/chat-view.js +1 -1
  26. package/dist/clients/attach/session/frames.d.ts +1 -0
  27. package/dist/clients/attach/session/frames.js +1 -1
  28. package/dist/clients/attach/viewer.js +576 -576
  29. package/dist/commands/api-client.js +3 -3
  30. package/dist/commands/memory/read.js +2 -2
  31. package/dist/commands/node/create.js +3 -3
  32. package/dist/commands/sys/branch.js +1 -1
  33. package/dist/commands/sys/connect.d.ts +1 -0
  34. package/dist/commands/sys/connect.js +3 -0
  35. package/dist/commands/sys/daemon.js +1 -1
  36. package/dist/commands/sys/panels/plugins-panel.js +1 -1
  37. package/dist/commands/sys/panels/provider-panel.d.ts +4 -0
  38. package/dist/commands/sys/panels/provider-panel.js +1 -1
  39. package/dist/commands/sys/provider-login.d.ts +8 -0
  40. package/dist/commands/sys/provider-login.js +9 -0
  41. package/dist/commands/sys/setup-core.d.ts +4 -0
  42. package/dist/commands/sys/setup-core.js +7 -4
  43. package/dist/commands/sys/setup-wizard.d.ts +1 -1
  44. package/dist/commands/sys/setup-wizard.js +1 -1
  45. package/dist/commands/sys.js +1 -1
  46. package/dist/core/canvas/canvas.d.ts +24 -0
  47. package/dist/core/canvas/canvas.js +29 -15
  48. package/dist/core/canvas/migrations.js +35 -32
  49. package/dist/core/canvas/nav-model.d.ts +1 -1
  50. package/dist/core/canvas/nav-model.js +1 -1
  51. package/dist/core/canvas/nav-render.d.ts +31 -25
  52. package/dist/core/canvas/nav-render.js +1 -1
  53. package/dist/core/canvas/paths.d.ts +4 -4
  54. package/dist/core/canvas/render-source.d.ts +6 -16
  55. package/dist/core/canvas/render-source.js +9 -9
  56. package/dist/core/canvas/types.d.ts +16 -0
  57. package/dist/core/config.js +1 -1
  58. package/dist/core/provider-credential.d.ts +6 -0
  59. package/dist/core/provider-credential.js +1 -0
  60. package/dist/core/runtime/boot-root.d.ts +2 -0
  61. package/dist/core/runtime/boot-root.js +1 -1
  62. package/dist/core/runtime/broker/daemon-ops.d.ts +2 -1
  63. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  64. package/dist/core/runtime/front-door-login.d.ts +2 -0
  65. package/dist/core/runtime/front-door-login.js +1 -0
  66. package/dist/core/runtime/front-door.js +7 -5
  67. package/dist/core/runtime/nerd-font.d.ts +19 -1
  68. package/dist/core/runtime/nerd-font.js +2 -2
  69. package/dist/core/runtime/progress-bar.d.ts +13 -0
  70. package/dist/core/runtime/progress-bar.js +2 -0
  71. package/dist/core/runtime/provider-credential-gate.d.ts +4 -0
  72. package/dist/core/runtime/provider-credential-gate.js +2 -0
  73. package/dist/core/runtime/spawn-env.d.ts +7 -0
  74. package/dist/core/runtime/spawn-env.js +1 -1
  75. package/dist/core/secrets.d.ts +3 -0
  76. package/dist/core/secrets.js +2 -2
  77. package/dist/core/subscription-state.d.ts +1 -1
  78. package/dist/core/subscription-state.js +3 -3
  79. package/dist/core/termrender/termrender.d.ts +3 -1
  80. package/dist/core/termrender/termrender.js +13 -13
  81. package/dist/core/user-settings.d.ts +4 -0
  82. package/dist/core/user-settings.js +1 -1
  83. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  84. package/dist/daemon/api/handlers/canvas.js +4 -4
  85. package/dist/daemon/api/handlers/health.d.ts +1 -1
  86. package/dist/daemon/api/handlers/health.js +3 -3
  87. package/dist/daemon/api/handlers/modelauth.js +1 -1
  88. package/dist/daemon/api/map.d.ts +15 -1
  89. package/dist/daemon/api/map.js +2 -2
  90. package/dist/daemon/api/server.d.ts +5 -1
  91. package/dist/daemon/api/server.js +4 -4
  92. package/dist/daemon/crtrd-cli.js +1 -1
  93. package/dist/daemon/fleet.js +1 -1
  94. package/dist/daemon/manage.d.ts +1 -0
  95. package/dist/daemon/manage.js +2 -2
  96. package/dist/pi-extensions/canvas-stophook.js +1 -1
  97. package/dist/types.d.ts +7 -0
  98. package/dist/types.js +1 -1
  99. package/docs/sdk/README.md +54 -0
  100. package/docs/sdk/client.md +92 -0
  101. package/docs/sdk/docker.md +63 -0
  102. package/docs/sdk/errors.md +85 -0
  103. package/docs/sdk/getting-started.md +160 -0
  104. package/docs/sdk/memory.md +107 -0
  105. package/docs/sdk/migration.md +108 -0
  106. package/docs/sdk/nodes.md +193 -0
  107. package/docs/sdk/resources.md +71 -0
  108. package/docs/sdk/streaming.md +124 -0
  109. package/package.json +1 -1
  110. package/runtime.lock.json +6 -6
  111. package/scripts/install-runtime.mjs +7 -3
@@ -1,7 +1,9 @@
1
- var w=Object.defineProperty;var u=(r,n)=>w(r,"name",{value:n,configurable:!0});import{existsSync as R,statSync as g}from"node:fs";import{resolve as m}from"node:path";import{SUBTREE_NAMES as O}from"../../build-root.js";import{FRONT_DOOR_ENV as D}from"./front-door-env.js";function p(r){try{return R(r)&&g(r).isDirectory()}catch{return!1}}u(p,"isDir");const y=new Set(["--name","--kind","--profile","--pick-profile","--continue","-c","--resume","-r"]);function _(r){let n=process.cwd(),t,c,f,a=!1,l;const o=[];for(let i=0;i<r.length;i++){const e=r[i];e==="--name"?t=r[++i]:e==="--kind"?c=r[++i]:e==="--profile"?f=r[++i]:e==="--pick-profile"?a=!0:e==="--continue"||e==="-c"?l="continue":e==="--resume"||e==="-r"?l="pick":e.startsWith("--")||o.push(e)}o.length>0&&p(m(o[0]))&&(n=m(o.shift()));const s=o.length>0?o.join(" "):void 0;return{cwd:n,prompt:s,name:t,kind:c,profile:f,pickProfile:a,resume:l}}u(_,"parseRootArgs");import{FRONT_DOOR_ENV as L}from"./front-door-env.js";async function P(r){const n=r.slice(2),t=n[0];if(process.env[D]||t==="-h"||t==="--help"||t==="--version"||t==="-v"||t!==void 0&&O.includes(t))return!1;if(t!==void 0){const i=/\s/.test(t),e=!t.startsWith("-")&&p(m(t)),d=y.has(t);if(!i&&!e&&!d)return!1;if(e){const{buildRoot:h}=await import("../../build-root.js");if((await h()).subtrees.some(k=>k.name===t))return!1}}const[{bootRoot:c},{resumeRoot:f},{rendererWarning:a,missingPackageWarning:l,piAgentDir:o}]=await Promise.all([import("./boot-root.js"),import("./resume-root.js"),import("./package-health.js")]),s=_(n);try{const i=l(o());i!==null&&process.stderr.write(`
2
- ${i}
1
+ var E=Object.defineProperty;var p=(r,i)=>E(r,"name",{value:i,configurable:!0});import{existsSync as w,statSync as F}from"node:fs";import{homedir as v}from"node:os";import{join as I,resolve as m}from"node:path";import{SUBTREE_NAMES as _}from"../../build-root.js";import{FRONT_DOOR_ENV as N}from"./front-door-env.js";function g(r){try{return w(r)&&F(r).isDirectory()}catch{return!1}}p(g,"isDir");const A=new Set(["--name","--kind","--profile","--pick-profile","--continue","-c","--resume","-r"]),h=I(v(),".crouter","setup-completed.json");async function C(){let r=!1;const i=p(()=>{r=!0},"onSigint");process.once("SIGINT",i);try{const{runSetup:e}=await import("../../commands/sys/setup-wizard.js"),t=await e();if(r){const c=new Error("Interrupted");throw c.name="AbortError",c}const[{atomicWriteJson:a},{crtrVersion:f}]=await Promise.all([import("../fs-utils.js"),import("../../shared/crtr-version.js")]);a(h,{packageVersion:f(),completedAt:new Date().toISOString(),outcome:t})}finally{process.removeListener("SIGINT",i)}}p(C,"runFirstSetup");function L(r){let i=process.cwd(),e,t,a,f=!1,c;const l=[];for(let n=0;n<r.length;n++){const o=r[n];o==="--name"?e=r[++n]:o==="--kind"?t=r[++n]:o==="--profile"?a=r[++n]:o==="--pick-profile"?f=!0:o==="--continue"||o==="-c"?c="continue":o==="--resume"||o==="-r"?c="pick":o.startsWith("--")||l.push(o)}l.length>0&&g(m(l[0]))&&(i=m(l.shift()));const d=l.length>0?l.join(" "):void 0;return{cwd:i,prompt:d,name:e,kind:t,profile:a,pickProfile:f,resume:c}}p(L,"parseRootArgs");import{FRONT_DOOR_ENV as Y}from"./front-door-env.js";async function B(r){const i=r.slice(2),e=i[0];if(process.env[N]||e==="-h"||e==="--help"||e==="--version"||e==="-v"||e!==void 0&&_.includes(e))return!1;if(e!==void 0){const s=/\s/.test(e),u=!e.startsWith("-")&&g(m(e)),T=A.has(e);if(!s&&!u&&!T)return!1;if(u){const{buildRoot:y}=await import("../../build-root.js");if((await y()).subtrees.some(D=>D.name===e))return!1}}const t=L(i);if(t.profile!==void 0){const{resolveProfileOperand:s}=await import("../profiles/manifest.js");t.profile=s(t.profile).profileId}const a=process.stdin.isTTY&&process.stdout.isTTY;let f=!1;a&&!w(h)&&(await C(),f=!0);const{selectProfileForCwd:c,selectProfileForCwdReadOnly:l}=await import("../profiles/select.js");f&&t.profile===void 0&&process.stdout.write(`
2
+ Choose a profile
3
+ `);const d=t.profile??(a?await c(t.cwd,void 0,t.pickProfile):l(t.cwd)),{exitForMissingProviderCredential:n,hasConfiguredProviderCredential:o}=await import("./provider-credential-gate.js");if(!await o(d)){a||n();const{runFrontDoorLogin:s}=await import("./front-door-login.js");await s()||n()}const[{bootRoot:R},{resumeRoot:P},{rendererWarning:k,missingPackageWarning:S,piAgentDir:O}]=await Promise.all([import("./boot-root.js"),import("./resume-root.js"),import("./package-health.js")]);try{const s=S(O());s!==null&&process.stderr.write(`
4
+ ${s}
3
5
 
4
- `);const e=a();e!==null&&process.stderr.write(`
5
- ${e}
6
+ `);const u=k();u!==null&&process.stderr.write(`
7
+ ${u}
6
8
 
7
- `)}catch{}return s.resume!==void 0&&await f(s.cwd,s.resume==="pick")==="aborted"||await c({...s}),!0}u(P,"maybeBootRoot");export{L as FRONT_DOOR_ENV,P as maybeBootRoot};
9
+ `)}catch{}return t.resume!==void 0&&await P(t.cwd,t.resume==="pick")==="aborted"||await R({...t,selectedProfileId:d}),!0}p(B,"maybeBootRoot");export{Y as FRONT_DOOR_ENV,B as maybeBootRoot};
@@ -10,8 +10,26 @@ export declare function detectNerdFont(platform?: NodeJS.Platform, home?: string
10
10
  /** Download the upstream symbols-only patch into the user's font dir. Works on
11
11
  * every Linux regardless of package manager — Debian/Ubuntu and Fedora ship no
12
12
  * Nerd Font package, so there is nothing native to install there. Needs no
13
- * sudo: it writes to `~/.local/share/fonts`. */
13
+ * sudo: it writes to `~/.local/share/fonts`.
14
+ *
15
+ * curl runs with its progress meter on (no `-s`) because this is the one
16
+ * install step with a real download percentage: setup captures that stderr
17
+ * and drives the step's progress bar from it (`parseCurlPercent`). */
14
18
  export declare const NERD_FONT_MANUAL_LINUX_COMMAND: string;
19
+ /** Reads percent complete out of curl's default progress meter as its stderr
20
+ * arrives. Rows are separated by `\r` and look like
21
+ * `100 1234k 100 1234k 0 0 987k 0 --:--:-- …`; the leading
22
+ * column is the percentage of the whole transfer, and the header rows
23
+ * (` % Total % Received …`) start with `%` so they never match.
24
+ *
25
+ * Stateful because curl's writes split rows anywhere: the tail of a chunk is
26
+ * held back until its row is terminated. Reading a half-row instead lands on
27
+ * a different column and reports a number that was never true — in practice
28
+ * the completed download's own summary row parsed as 0%.
29
+ *
30
+ * Returns the percentage of the last complete row in this chunk, or null when
31
+ * the chunk completed no row. */
32
+ export declare function createCurlPercentReader(): (chunk: string) => number | null;
15
33
  export interface NerdFontInstallCommand {
16
34
  command: string | null;
17
35
  hint: string;
@@ -1,2 +1,2 @@
1
- var u=Object.defineProperty;var e=(t,n)=>u(t,"name",{value:n,configurable:!0});import{spawnSync as i}from"node:child_process";import{readdirSync as c}from"node:fs";import{homedir as d}from"node:os";import{join as s}from"node:path";const f=/nerd[ _-]?font|symbolsnerdfont|symbols-only/i;function m(t,n){return t==="darwin"?[s(n,"Library","Fonts"),"/Library/Fonts","/System/Library/Fonts"]:[s(n,".local","share","fonts"),s(n,".fonts"),"/usr/local/share/fonts","/usr/share/fonts"]}e(m,"fontDirs");function a(t,n){if(n<0)return null;let o;try{o=c(t,{withFileTypes:!0})}catch{return null}for(const r of o){if(r.isDirectory()){const l=a(s(t,r.name),n-1);if(l!==null)return l;continue}if(/\.(ttf|otf|ttc)$/i.test(r.name)&&f.test(r.name))return r.name}return null}e(a,"scanForNerdFont");function y(){if(i("sh",["-lc","command -v fc-list >/dev/null 2>&1"],{stdio:"ignore"}).status!==0)return null;const n=i("fc-list",[":","family"],{encoding:"utf8",timeout:5e3});if(n.status!==0||typeof n.stdout!="string")return null;for(const o of n.stdout.split(`
2
- `))if(f.test(o))return o.split(",")[0].trim();return""}e(y,"queryFontconfig");function S(t=process.platform,n=d()){if(t!=="darwin"){const o=y();if(o!==null&&o!=="")return{installed:!0,evidence:o,source:"fc-list"}}for(const o of m(t,n)){const r=a(o,3);if(r!==null)return{installed:!0,evidence:r,source:"font-dir"}}return{installed:!1,evidence:null,source:null}}e(S,"detectNerdFont");const p="mkdir -p ~/.local/share/fonts && curl -fsSL -o /tmp/NerdFontsSymbolsOnly.zip https://github.com/ryanoasis/nerd-fonts/releases/latest/download/NerdFontsSymbolsOnly.zip && unzip -oq /tmp/NerdFontsSymbolsOnly.zip -d ~/.local/share/fonts && fc-cache -f";function _(t,n=process.platform){const o=n!=="darwin"||t==="apt-get"||t==="dnf"||t==="pacman";return t==="brew"?{command:"brew install --cask font-symbols-only-nerd-font",hint:"Homebrew cask",autoInstall:!0}:t==="pacman"?{command:"sudo pacman -S --noconfirm ttf-nerd-fonts-symbols",hint:"pacman install",autoInstall:!0}:o?{command:p,hint:"download symbols-only patch to ~/.local/share/fonts (needs curl + unzip)",autoInstall:!0}:{command:null,hint:"install Homebrew, or download a Nerd Font from https://nerdfonts.com and add it in Font Book",autoInstall:!1}}e(_,"nerdFontInstallCommand");const I=`then set your terminal profile's font to a Nerd Font (e.g. add "Symbols Nerd Font Mono" as the non-ASCII/fallback font) \u2014 installing it does not select it`;export{p as NERD_FONT_MANUAL_LINUX_COMMAND,I as NERD_FONT_SELECT_HINT,S as detectNerdFont,_ as nerdFontInstallCommand};
1
+ var d=Object.defineProperty;var e=(n,t)=>d(n,"name",{value:t,configurable:!0});import{spawnSync as u}from"node:child_process";import{readdirSync as m}from"node:fs";import{homedir as p}from"node:os";import{join as l}from"node:path";const c=/nerd[ _-]?font|symbolsnerdfont|symbols-only/i;function y(n,t){return n==="darwin"?[l(t,"Library","Fonts"),"/Library/Fonts","/System/Library/Fonts"]:[l(t,".local","share","fonts"),l(t,".fonts"),"/usr/local/share/fonts","/usr/share/fonts"]}e(y,"fontDirs");function a(n,t){if(t<0)return null;let o;try{o=m(n,{withFileTypes:!0})}catch{return null}for(const r of o){if(r.isDirectory()){const s=a(l(n,r.name),t-1);if(s!==null)return s;continue}if(/\.(ttf|otf|ttc)$/i.test(r.name)&&c.test(r.name))return r.name}return null}e(a,"scanForNerdFont");function b(){if(u("sh",["-lc","command -v fc-list >/dev/null 2>&1"],{stdio:"ignore"}).status!==0)return null;const t=u("fc-list",[":","family"],{encoding:"utf8",timeout:5e3});if(t.status!==0||typeof t.stdout!="string")return null;for(const o of t.stdout.split(`
2
+ `))if(c.test(o))return o.split(",")[0].trim();return""}e(b,"queryFontconfig");function I(n=process.platform,t=p()){if(n!=="darwin"){const o=b();if(o!==null&&o!=="")return{installed:!0,evidence:o,source:"fc-list"}}for(const o of y(n,t)){const r=a(o,3);if(r!==null)return{installed:!0,evidence:r,source:"font-dir"}}return{installed:!1,evidence:null,source:null}}e(I,"detectNerdFont");const N="mkdir -p ~/.local/share/fonts && curl -fL -o /tmp/NerdFontsSymbolsOnly.zip https://github.com/ryanoasis/nerd-fonts/releases/latest/download/NerdFontsSymbolsOnly.zip && unzip -oq /tmp/NerdFontsSymbolsOnly.zip -d ~/.local/share/fonts && fc-cache -f";function L(){let n="";return t=>{n+=t;const o=n.split(/[\r\n]/);n=o.pop()??"";let r=null;for(const s of o){const i=/^\s*(\d{1,3})\s+\S+\s+\d{1,3}\s/.exec(s);if(i===null)continue;const f=Number(i[1]);f<=100&&(r=f)}return r}}e(L,"createCurlPercentReader");function T(n,t=process.platform){const o=t!=="darwin"||n==="apt-get"||n==="dnf"||n==="pacman";return n==="brew"?{command:"brew install --cask font-symbols-only-nerd-font",hint:"Homebrew cask",autoInstall:!0}:n==="pacman"?{command:"sudo pacman -S --noconfirm ttf-nerd-fonts-symbols",hint:"pacman install",autoInstall:!0}:o?{command:N,hint:"download symbols-only patch to ~/.local/share/fonts (needs curl + unzip)",autoInstall:!0}:{command:null,hint:"install Homebrew, or download a Nerd Font from https://nerdfonts.com and add it in Font Book",autoInstall:!1}}e(T,"nerdFontInstallCommand");const x=`then set your terminal profile's font to a Nerd Font (e.g. add "Symbols Nerd Font Mono" as the non-ASCII/fallback font) \u2014 installing it does not select it`;export{N as NERD_FONT_MANUAL_LINUX_COMMAND,x as NERD_FONT_SELECT_HINT,L as createCurlPercentReader,I as detectNerdFont,T as nerdFontInstallCommand};
@@ -0,0 +1,13 @@
1
+ /** A running progress bar line. `setFraction` fills it from a real count;
2
+ * leaving it unset keeps the indeterminate slider. `stop` erases the line and
3
+ * resolves once the painter has released the terminal, so the caller can
4
+ * write its own outcome line without interleaving. */
5
+ export interface ProgressBar {
6
+ setFraction(fraction: number): void;
7
+ stop(): Promise<void>;
8
+ }
9
+ /** Start an animated bar on `stream`, drawn by a child process so it keeps
10
+ * moving while this process blocks inside the step it describes. */
11
+ export declare function startProgressBar(label: string, stream?: NodeJS.WriteStream & {
12
+ fd: number;
13
+ }): ProgressBar;
@@ -0,0 +1,2 @@
1
+ var p=Object.defineProperty;var n=(o,e)=>p(o,"name",{value:e,configurable:!0});import{spawn as f}from"node:child_process";import{dirname as m,join as d}from"node:path";import{assetRoot as u}from"../asset-root.js";const s={setFraction(){},stop:n(async()=>{},"stop")};function l(){return d(m(u()),"bin","progress-painter.mjs")}n(l,"painterPath");function b(o,e=process.stdout){if(!e.isTTY)return s;let t;try{t=f(process.execPath,[l(),o],{stdio:["pipe",e.fd,"ignore"]})}catch{return s}let i=!0;t.stdin?.on("error",()=>{i=!1});const a=new Promise(r=>{t.once("exit",()=>{i=!1,r()}),t.once("error",()=>{i=!1,r()})});return{setFraction(r){i&&t.stdin?.write(`${Math.min(1,Math.max(0,r))}
2
+ `)},async stop(){i&&t.stdin?.end();const r=new Promise(c=>setTimeout(()=>{t.kill("SIGKILL"),c()},1e3).unref());await Promise.race([a,r]),e.write("\r\x1B[2K\x1B[?25h")}}}n(b,"startProgressBar");export{b as startProgressBar};
@@ -0,0 +1,4 @@
1
+ export declare const NO_PROVIDER_CREDENTIAL_MESSAGE = "crtr: no provider credential; run `crtr sys setup` to log in.";
2
+ /** Check the setup catalog before a path creates a broker-backed node. */
3
+ export declare function hasConfiguredProviderCredential(profileId?: string | null): Promise<boolean>;
4
+ export declare function exitForMissingProviderCredential(): never;
@@ -0,0 +1,2 @@
1
+ var m=Object.defineProperty;var o=(r,e)=>m(r,"name",{value:e,configurable:!0});import{ModelRegistry as a,ModelRuntime as d}from"@earendil-works/pi-coding-agent";import{join as s}from"node:path";import{PoolCredentialStore as l}from"../pool-credential-store.js";import{readProfileEnvVars as p}from"../profiles/env-store.js";import{hasProviderCredential as c}from"../provider-credential.js";import{piAgentDir as f}from"./package-health.js";import{PROVIDER_ENV_KEYS as u}from"./spawn-env.js";const E="crtr: no provider credential; run `crtr sys setup` to log in.";async function h(r=null){const e=p(r);if(Object.values(u).some(n=>(e[n]??process.env[n])?.trim()))return!0;const t=f(),i=await d.create({credentials:new l(s(t,"auth.json")),modelsPath:s(t,"models.json")});return c(i,new a(i))}o(h,"hasConfiguredProviderCredential");function j(){process.stderr.write(`${E}
2
+ `),process.exit(1)}o(j,"exitForMissingProviderCredential");export{E as NO_PROVIDER_CREDENTIAL_MESSAGE,j as exitForMissingProviderCredential,h as hasConfiguredProviderCredential};
@@ -1,4 +1,5 @@
1
1
  import type { PiInvocation } from './invocation.js';
2
+ import type { ModelProvider } from '../../types.js';
2
3
  /** The unshimmed PATH admitted from the host. Broker and cron shells resolve
3
4
  * crouter-owned executables from this value, while commands those shells run
4
5
  * still receive the shim-prepended PATH. */
@@ -7,6 +8,12 @@ export declare const ADMITTED_HOST_PATH_ENV = "CRTR_ADMITTED_HOST_PATH";
7
8
  * The result is absolute, or null when no PATH entry supplies the command. */
8
9
  export declare function resolvePathExecutable(name: string, env: NodeJS.ProcessEnv, cwd: string): string | null;
9
10
  export declare function resolveAdmittedHostCommand(name: string, env: NodeJS.ProcessEnv, cwd: string): string;
11
+ /** B — the exact env-var name of each crtr-configured model provider's API
12
+ * key. Deliberately an explicit provider→env-key mapping, NOT inferred from
13
+ * `MODEL_PROVIDER_KEYS` in launch.ts (that constant is a provider-ID list —
14
+ * `['anthropic', 'openai']` — not env-var names; conflating the two would
15
+ * silently admit the wrong names the moment either list changes). */
16
+ export declare const PROVIDER_ENV_KEYS: Record<ModelProvider, string>;
10
17
  /** Sources A–C: the operational base shared by the broker boundary (this
11
18
  * module's `buildBrokerEnv`) and cron executor — the single shared definition
12
19
  * of "non-secret host state the runtime exposes to any spawned shell." Does not include
@@ -1 +1 @@
1
- var u=Object.defineProperty;var a=(e,t)=>u(e,"name",{value:t,configurable:!0});import{statSync as d}from"node:fs";import{delimiter as P,resolve as E}from"node:path";import{readMergedLaunchConfig as T}from"../config.js";import{readProfileEnvVars as _}from"../profiles/env-store.js";import{metadataEnvName as p,readProfileMetadata as m}from"../profiles/manifest.js";import{binShimDirFor as A}from"./bin-contributions.js";import{FRONT_DOOR_ENV as O}from"./front-door-env.js";const R=new Set(["HOME","PATH","SHELL","USER","LOGNAME","PWD","TMPDIR","TERM","TERMINFO","COLORTERM","LANG","LANGUAGE","TZ","NO_PROXY","no_proxy","SSH_AUTH_SOCK","CRTR_BASH_VALVE_MS"]),h=["LC_","XDG_","PI_"],f="CRTR_ADMITTED_HOST_PATH";function H(e,t,o){const n=t.PATH;if(n===void 0||n==="")return null;for(const s of n.split(P)){const c=E(o,s===""?".":s,e);try{const r=d(c);if(r.isFile()&&(r.mode&73)!==0)return c}catch{}}return null}a(H,"resolvePathExecutable");function $(e,t,o){const n=t[f]??t.PATH;if(n===void 0||n==="")throw new Error(`cannot resolve crouter-owned ${e}: the admitted host PATH is empty`);const s=H(e,{PATH:n},o);if(s!==null)return s;throw new Error(`cannot resolve crouter-owned ${e} from the admitted host PATH`)}a($,"resolveAdmittedHostCommand");const v=["HTTP_PROXY","HTTPS_PROXY","http_proxy","https_proxy"],I={anthropic:"ANTHROPIC_API_KEY",openai:"OPENAI_API_KEY"};function w(e){let t;try{t=new URL(e)}catch{return!1}return!(t.protocol!=="http:"&&t.protocol!=="https:"||t.hostname===""||t.username!==""||t.password!=="")}a(w,"isCredentialFreeProxyValue");function N(e,t){if(!t.includes("*"))return e===t;const o=t.split("*").map(n=>n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join(".*");return new RegExp(`^${o}$`).test(e)}a(N,"matchesAllowPattern");function S(e){const t=e.host??process.env,o={};for(const[r,i]of Object.entries(t))i!==void 0&&(R.has(r)||h.some(l=>r.startsWith(l)))&&(o[r]=i);for(const r of v){const i=t[r];i!==void 0&&w(i)&&(o[r]=i)}for(const r of Object.values(I)){const i=t[r];i!==void 0&&(o[r]=i)}const n=T(e.targetCwd,e.targetProfileId).spawnEnv.allow;if(n.length>0)for(const[r,i]of Object.entries(t))i!==void 0&&n.some(l=>N(r,l))&&(o[r]=i);const s=t[f]??o.PATH;s!==void 0&&s!==""&&(o[f]=s);const c=A(e.targetCwd,e.targetProfileId);if(c!==null){const r=o.PATH;o.PATH=r===void 0||r===""?c:`${c}:${r}`}return o}a(S,"buildOperationalEnvBase");function C(e){const t={};for(const[o,n]of Object.entries(m(e)))t[p(o)]=n;return t}a(C,"profileMetadataEnv");function F(e){const t=e.env.CRTR_NODE_CWD??process.cwd(),o=e.env.CRTR_PROFILE_ID||null,n=S({targetCwd:t,targetProfileId:o});return{...n,...C(o),..._(o),...e.env,[f]:n[f],[O]:"1"}}a(F,"buildBrokerEnv");export{f as ADMITTED_HOST_PATH_ENV,F as buildBrokerEnv,S as buildOperationalEnvBase,C as profileMetadataEnv,$ as resolveAdmittedHostCommand,H as resolvePathExecutable};
1
+ var u=Object.defineProperty;var a=(e,t)=>u(e,"name",{value:t,configurable:!0});import{statSync as d}from"node:fs";import{delimiter as P,resolve as E}from"node:path";import{readMergedLaunchConfig as p}from"../config.js";import{readProfileEnvVars as T}from"../profiles/env-store.js";import{metadataEnvName as _,readProfileMetadata as m}from"../profiles/manifest.js";import{binShimDirFor as A}from"./bin-contributions.js";import{FRONT_DOOR_ENV as O}from"./front-door-env.js";const R=new Set(["HOME","PATH","SHELL","USER","LOGNAME","PWD","TMPDIR","TERM","TERMINFO","COLORTERM","LANG","LANGUAGE","TZ","NO_PROXY","no_proxy","SSH_AUTH_SOCK","CRTR_BASH_VALVE_MS"]),h=["LC_","XDG_","PI_"],f="CRTR_ADMITTED_HOST_PATH";function H(e,t,o){const n=t.PATH;if(n===void 0||n==="")return null;for(const s of n.split(P)){const c=E(o,s===""?".":s,e);try{const r=d(c);if(r.isFile()&&(r.mode&73)!==0)return c}catch{}}return null}a(H,"resolvePathExecutable");function $(e,t,o){const n=t[f]??t.PATH;if(n===void 0||n==="")throw new Error(`cannot resolve crouter-owned ${e}: the admitted host PATH is empty`);const s=H(e,{PATH:n},o);if(s!==null)return s;throw new Error(`cannot resolve crouter-owned ${e} from the admitted host PATH`)}a($,"resolveAdmittedHostCommand");const v=["HTTP_PROXY","HTTPS_PROXY","http_proxy","https_proxy"],I={anthropic:"ANTHROPIC_API_KEY",openai:"OPENAI_API_KEY"};function w(e){let t;try{t=new URL(e)}catch{return!1}return!(t.protocol!=="http:"&&t.protocol!=="https:"||t.hostname===""||t.username!==""||t.password!=="")}a(w,"isCredentialFreeProxyValue");function N(e,t){if(!t.includes("*"))return e===t;const o=t.split("*").map(n=>n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join(".*");return new RegExp(`^${o}$`).test(e)}a(N,"matchesAllowPattern");function S(e){const t=e.host??process.env,o={};for(const[r,i]of Object.entries(t))i!==void 0&&(R.has(r)||h.some(l=>r.startsWith(l)))&&(o[r]=i);for(const r of v){const i=t[r];i!==void 0&&w(i)&&(o[r]=i)}for(const r of Object.values(I)){const i=t[r];i!==void 0&&(o[r]=i)}const n=p(e.targetCwd,e.targetProfileId).spawnEnv.allow;if(n.length>0)for(const[r,i]of Object.entries(t))i!==void 0&&n.some(l=>N(r,l))&&(o[r]=i);const s=t[f]??o.PATH;s!==void 0&&s!==""&&(o[f]=s);const c=A(e.targetCwd,e.targetProfileId);if(c!==null){const r=o.PATH;o.PATH=r===void 0||r===""?c:`${c}:${r}`}return o}a(S,"buildOperationalEnvBase");function C(e){const t={};for(const[o,n]of Object.entries(m(e)))t[_(o)]=n;return t}a(C,"profileMetadataEnv");function F(e){const t=e.env.CRTR_NODE_CWD??process.cwd(),o=e.env.CRTR_PROFILE_ID||null,n=S({targetCwd:t,targetProfileId:o});return{...n,...C(o),...T(o),...e.env,[f]:n[f],[O]:"1"}}a(F,"buildBrokerEnv");export{f as ADMITTED_HOST_PATH_ENV,I as PROVIDER_ENV_KEYS,F as buildBrokerEnv,S as buildOperationalEnvBase,C as profileMetadataEnv,$ as resolveAdmittedHostCommand,H as resolvePathExecutable};
@@ -8,6 +8,7 @@ import type { Scope } from '../types.js';
8
8
  * the target name directly, so a rename doesn't orphan the secret. */
9
9
  export interface SecretsStore {
10
10
  relayTokens: Record<string, string>;
11
+ apiToken?: string;
11
12
  }
12
13
  export declare function readSecrets(scope: Scope): SecretsStore;
13
14
  /** Writes secrets.json directly (never via `fs-utils.writeJson`, which has no
@@ -18,6 +19,8 @@ export declare function writeSecrets(scope: Scope, store: SecretsStore): void;
18
19
  export declare function getRelayToken(ref: string, scope: Scope): string | undefined;
19
20
  export declare function setRelayToken(ref: string, token: string, scope: Scope): void;
20
21
  export declare function deleteRelayToken(ref: string, scope: Scope): void;
22
+ export declare function getApiToken(scope: Scope): string | undefined;
23
+ export declare function setApiToken(token: string, scope: Scope): void;
21
24
  /** A short, non-reversible display fingerprint of a raw token — safe to print
22
25
  * in `canvas config list`/`show` output. NEVER derive this from anything
23
26
  * that could round-trip back to the token, and never log/return the raw
@@ -1,2 +1,2 @@
1
- var a=Object.defineProperty;var t=(e,r)=>a(e,"name",{value:r,configurable:!0});import{createHash as u}from"node:crypto";import{existsSync as l,readFileSync as p,writeFileSync as f,chmodSync as y}from"node:fs";import{join as m}from"node:path";import{scopeRoot as T,requireScopeRoot as k}from"./scope.js";import{ensureDir as S}from"./fs-utils.js";const d="secrets.json";function c(e){return m(e,d)}t(c,"secretsPathFor");function s(e){const r=T(e);if(!r)return{relayTokens:{}};const o=c(r);if(!l(o))return{relayTokens:{}};try{return{relayTokens:JSON.parse(p(o,"utf8")).relayTokens??{}}}catch{return{relayTokens:{}}}}t(s,"readSecrets");function i(e,r){const o=k(e);S(o);const n=c(o);f(n,JSON.stringify(r,null,2)+`
2
- `,{mode:384}),y(n,384)}t(i,"writeSecrets");function j(e,r){return s(r).relayTokens[e]}t(j,"getRelayToken");function w(e,r,o){const n=s(o);n.relayTokens[e]=r,i(o,n)}t(w,"setRelayToken");function J(e,r){const o=s(r);delete o.relayTokens[e],i(r,o)}t(J,"deleteRelayToken");function N(e){return u("sha256").update(e).digest("hex").slice(0,12)}t(N,"fingerprint");export{J as deleteRelayToken,N as fingerprint,j as getRelayToken,s as readSecrets,w as setRelayToken,i as writeSecrets};
1
+ var a=Object.defineProperty;var r=(e,o)=>a(e,"name",{value:o,configurable:!0});import{createHash as p}from"node:crypto";import{existsSync as u,readFileSync as f,writeFileSync as T,chmodSync as k}from"node:fs";import{join as l}from"node:path";import{scopeRoot as y,requireScopeRoot as m}from"./scope.js";import{ensureDir as S}from"./fs-utils.js";const x="secrets.json";function c(e){return l(e,x)}r(c,"secretsPathFor");function i(e){const o=y(e);if(!o)return{relayTokens:{}};const n=c(o);if(!u(n))return{relayTokens:{}};try{const t=JSON.parse(f(n,"utf8"));return{relayTokens:t.relayTokens??{},...typeof t.apiToken=="string"&&t.apiToken!==""?{apiToken:t.apiToken}:{}}}catch{return{relayTokens:{}}}}r(i,"readSecrets");function s(e,o){const n=m(e);S(n);const t=c(n);T(t,JSON.stringify(o,null,2)+`
2
+ `,{mode:384}),k(t,384)}r(s,"writeSecrets");function j(e,o){return i(o).relayTokens[e]}r(j,"getRelayToken");function w(e,o,n){const t=i(n);t.relayTokens[e]=o,s(n,t)}r(w,"setRelayToken");function A(e,o){const n=i(o);delete n.relayTokens[e],s(o,n)}r(A,"deleteRelayToken");function J(e){return i(e).apiToken}r(J,"getApiToken");function N(e,o){const n=i(o);n.apiToken=e,s(o,n)}r(N,"setApiToken");function O(e){return p("sha256").update(e).digest("hex").slice(0,12)}r(O,"fingerprint");export{A as deleteRelayToken,O as fingerprint,J as getApiToken,j as getRelayToken,i as readSecrets,N as setApiToken,w as setRelayToken,s as writeSecrets};
@@ -59,7 +59,7 @@ export declare function removeAllManagedAccounts(provider: ManagedProviderId): P
59
59
  export declare function removeManagedProvider(provider: ManagedProviderId, label?: string): Promise<SubscriptionCredential[]>;
60
60
  export declare function promoteSubscription(provider: ManagedProviderId, ref: string): Promise<SubscriptionCredential[]>;
61
61
  export declare function markSubscriptionAttempt(provider: ManagedProviderId, ref: string, attemptAt?: number): Promise<SubscriptionCredential[]>;
62
- export declare function markSubscriptionAuthenticationFailed(provider: ManagedProviderId, ref: string, retryAfterMs: number, attemptAt?: number): Promise<SubscriptionCredential[]>;
62
+ export declare function markSubscriptionAuthenticationFailed(provider: ManagedProviderId, ref: string, attempted: Pick<SubscriptionCredential, 'refresh' | 'access'>, retryAfterMs: number, attemptAt?: number): Promise<SubscriptionCredential[]>;
63
63
  export declare function markSubscriptionRateLimited(provider: ManagedProviderId, ref: string, retryAfterMs: number, attemptAt?: number, rateLimitedAt?: number): Promise<SubscriptionCredential[]>;
64
64
  export declare function markSubscriptionSuccess(provider: ManagedProviderId, ref: string, attemptAt?: number): Promise<SubscriptionCredential[]>;
65
65
  export declare function refreshSubscriptionCredential(provider: ManagedProviderId, label: string, exchange: (refreshToken: string) => Promise<{
@@ -1,3 +1,3 @@
1
- var B=Object.defineProperty;var s=(t,n)=>B(t,"name",{value:n,configurable:!0});import{createHash as z,randomUUID as F}from"node:crypto";import{join as J}from"node:path";import{LockedJsonFile as Q}from"./locked-file.js";import{piAgentDir as G}from"./runtime/package-health.js";import{openScopedStore as X}from"./scoped-state/db.js";import{providerStore as Y}from"./scoped-state/paths.js";import{providerRowByReference as Z,providerRows as v,providerWrite as A}from"./scoped-state/providers.js";const P="anthropic",tt="openai-codex",et=720*60*60*1e3,W="strong",H={anthropic:{label:"Claude",credentials:"anthropic-credentials.json"},"openai-codex":{label:"OpenAI Codex",credentials:"openai-codex-credentials.json"}};function b(){return G()}s(b,"agentDir");function N(t){return J(b(),H[t].credentials)}s(N,"credentialFile");function K(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}s(K,"normalizeStrength");function f(t){return t.toLowerCase()}s(f,"key");function nt(t){const n=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",a=typeof t.access=="string"?t.access.trim():"",r=Number(t.expires);if(!n||!e||!a||!Number.isFinite(r))return;const i=typeof t.accountId=="string"&&t.accountId.trim()?t.accountId.trim():void 0;return{label:n,refresh:e,access:a,expires:r,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...i?{accountId:i}:{}}}s(nt,"credential");function $(t,n){if(!n.label?.trim())throw new Error(`${h(t)} subscription label cannot be empty`);const e=nt(n);if(!e)throw new Error(`Refusing to persist incomplete ${h(t)} subscription "${n.label.trim()}"; pool rows require both OAuth tokens.`);return e}s($,"assertCredential");function j(t,n){if(!n||n.trim()==="")return{version:1,accounts:{}};let e;try{e=JSON.parse(n)}catch(a){throw new Error(`managed credential file ${t} is corrupt: ${a instanceof Error?a.message:String(a)}`)}if(typeof e!="object"||e===null||Array.isArray(e)||e.version!==1||typeof e.accounts!="object"||e.accounts===null||Array.isArray(e.accounts))throw new Error(`managed credential file ${t} is corrupt`);return e}s(j,"parseCredentials");function at(t,n){const e=n.accounts[t.id];if(!e||typeof e.refresh!="string"||typeof e.access!="string"||!Number.isFinite(e.expires))throw new Error(`managed ${t.providerId} account ${t.id} has no credential record`);return{label:t.label,refresh:e.refresh,access:e.access,expires:e.expires,rateLimitedUntil:t.rateLimitedUntil,lastAttemptAt:t.lastAttemptAt,lastRateLimitedAt:t.lastRateLimitedAt,...t.authFailure?{authFailure:t.authFailure}:{},...e.accountId?{accountId:e.accountId}:{}}}s(at,"toPublic");function D(t,n){return v(b(),t).map(e=>({row:e,entry:at(e,n)}))}s(D,"rowsWithCredentials");function q(t){return new Q(N(t),!0,`{"version":1,"accounts":{}}
2
- `)}s(q,"managedCredentialFile");function rt(t){return j(N(t),q(t).readUnlocked())}s(rt,"readFile");function y(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${z("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}s(y,"identity");function V(t,n,e){const a=e.refresh?.trim()??"",r=e.access?.trim()??"",i=Number(e.expires);if(!a||!r||!Number.isFinite(i))throw new Error(`${h(t)} subscription "${n}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:a,access:r,expires:i}}s(V,"assertTokens");function gt(t){return t===P||t===tt}s(gt,"isManagedProvider");function h(t){return H[t].label}s(h,"getProviderLabel");function xt(t,n=Date.now(),e={}){const a=[],r=[];for(let i=0;i<t.length;i++){const c=t[i],u=c.registered?c.automaticFallback===!1?"non-managed-fallback":c.authenticated?(c.coolingUntil??0)>n?"cooling":void 0:"unauthenticated":"unregistered";if(!u)a.push(c);else if(r.push({candidate:c,reason:u}),e.pinnedToFirst&&i===0&&u!=="cooling")break}return{candidates:a,rejected:r}}s(xt,"resolveProviderCandidates");class I extends Error{static{s(this,"DuplicateSubscriptionError")}constructor(n){super(n),this.name="DuplicateSubscriptionError"}}function M(t){return D(t,rt(t)).map(({entry:n})=>n)}s(M,"readSubscriptionPool");async function x(t,n){const e=N(t);return q(t).withLockAsync(async(a,r,i)=>{const c=j(e,a);return{result:await n(c,D(t,c),u=>i(`${JSON.stringify(u)}
3
- `))}})}s(x,"locked");function S(t,n,e){t.prepare("INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)").run(n,e.id,e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null)}s(S,"insert");function E(t,n){t.prepare("UPDATE managed_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE provider_id=? AND id=?").run(n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,n.providerId,n.id)}s(E,"update");function L(t,n){return D(t,n).map(({entry:e})=>e)}s(L,"publicPool");function R(t,n){try{n()}catch(e){throw new Error(`managed ${h(t)} credential file was published, but its SQL membership update failed; SQL membership remains authoritative: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}s(R,"writeMembershipAfterCredentialPublish");async function Lt(t,n){return x(t,async(e,a,r)=>{const i=n.map(d=>$(t,d)),c=i.map((d,l)=>({entry:d,id:a[l]?.row.id??F()})),u={};for(const{entry:d,id:l}of c)u[l]={refresh:d.refresh,access:d.access,expires:d.expires,...d.accountId?{accountId:d.accountId}:{}};return r({version:1,accounts:u}),R(t,()=>A(b(),d=>{d.prepare("DELETE FROM managed_accounts WHERE provider_id=?").run(t),c.forEach(({entry:l,id:m},o)=>S(d,t,{providerId:t,id:m,label:l.label,labelKey:f(l.label),position:o,lastAttemptAt:l.lastAttemptAt,lastRateLimitedAt:l.lastRateLimitedAt,rateLimitedUntil:l.rateLimitedUntil,...l.authFailure?{authFailure:l.authFailure}:{}}))})),i})}s(Lt,"writeSubscriptionPool");function U(t,n){const e=n.trim().toLowerCase(),a=Number.parseInt(e,10);return Number.isFinite(a)&&String(a)===e&&a>=1&&a<=t.length?a-1:t.findIndex(({entry:r})=>f(r.label)===e)}s(U,"resolve");async function it(t,n){const e=$(t,n);return x(t,async(a,r,i)=>{if(r.some(({entry:l})=>f(l.label)===f(e.label)))throw new I(`${h(t)} subscription already exists: ${e.label}`);const c=y(e),u=r.find(({entry:l})=>y(l)===c);if(c&&u)throw new I(`This ${h(t)} account is already added as \u201C${u.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const d=F();return a.accounts[d]={refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{}},i(a),R(t,()=>A(b(),l=>S(l,t,{providerId:t,id:d,label:e.label,labelKey:f(e.label),position:r.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}}))),L(t,a)})}s(it,"addSubscription");async function _t(t,n){const e=$(t,n);return M(t).find(r=>f(r.label)===f(e.label))?st(t,e):it(t,e)}s(_t,"upsertSubscription");async function st(t,n){const e=$(t,n);return x(t,async(a,r,i)=>{const c=r.findIndex(({entry:o})=>f(o.label)===f(e.label)),u=r[c],d=r.find(({entry:o},g)=>g!==c&&y(o)===y(e));if(y(e)&&d)throw new I(`This ${h(t)} account is already added as \u201C${d.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);if(!u){const o=F();return a.accounts[o]={refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{}},i(a),R(t,()=>A(b(),g=>S(g,t,{providerId:t,id:o,label:e.label,labelKey:f(e.label),position:r.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil}))),L(t,a)}const l=u.row,m=e.lastRateLimitedAt>=l.lastRateLimitedAt;return a.accounts[l.id]={refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{}},i(a),R(t,()=>A(b(),o=>E(o,{...l,label:e.label,labelKey:f(e.label),lastAttemptAt:Math.max(l.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:l.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:l.rateLimitedUntil}))),L(t,a)})}s(st,"upsertSubscriptionWithUniqueAccount");async function It(t,n,e){return x(t,async(a,r)=>{const i=U(r,n);if(i<0)throw new Error(`Unknown ${h(t)} subscription: ${n}`);const c=e.trim();if(!c)throw new Error(`${h(t)} subscription label cannot be empty`);if(r.some((u,d)=>d!==i&&f(u.entry.label)===f(c)))throw new I(`${h(t)} subscription already exists: ${c}`);return A(b(),u=>E(u,{...r[i].row,label:c,labelKey:f(c)})),L(t,a)})}s(It,"renameSubscription");async function T(t,n){return x(t,async(e,a,r)=>{const i=U(a,n);if(i<0)throw new Error(`Unknown ${h(t)} subscription: ${n}`);const c=a[i].row;return A(b(),u=>u.prepare("DELETE FROM managed_accounts WHERE provider_id=? AND id=?").run(t,c.id)),delete e.accounts[c.id],r(e),L(t,e)})}s(T,"remove");async function Et(t,n){return T(t,n)}s(Et,"removeManagedAccount");async function Rt(t,n){return T(t,n)}s(Rt,"removeSubscription");async function ct(t){return x(t,async(n,e,a)=>{A(b(),r=>r.prepare("DELETE FROM managed_accounts WHERE provider_id=?").run(t));for(const{row:r}of e)delete n.accounts[r.id];return a(n),e.length})}s(ct,"removeAllManagedAccounts");async function wt(t,n){return n===void 0?(await ct(t),[]):T(t,n)}s(wt,"removeManagedProvider");async function $t(t,n){return x(t,async(e,a)=>{const r=U(a,n);if(r<0)throw new Error(`Unknown ${h(t)} subscription: ${n}`);return A(b(),i=>{[a[r],...a.filter((u,d)=>d!==r)].forEach((u,d)=>E(i,{...u.row,position:d}))}),L(t,e)})}s($t,"promoteSubscription");async function k(t,n,e){return A(b(),a=>{const r=Z(a,t,n);if(!r)throw new Error(`Unknown ${h(t)} subscription: ${n}`);const i=e(r);i&&E(a,i)}),M(t)}s(k,"mutateHealth");async function Ft(t,n,e=Date.now()){return k(t,n,a=>({...a,lastAttemptAt:Math.max(a.lastAttemptAt,e)}))}s(Ft,"markSubscriptionAttempt");async function St(t,n,e,a=Date.now()){return k(t,n,r=>({...r,authFailure:"invalid_grant",lastAttemptAt:Math.max(r.lastAttemptAt,a),rateLimitedUntil:Math.max(r.rateLimitedUntil,a+Math.max(0,e))}))}s(St,"markSubscriptionAuthenticationFailed");async function Ut(t,n,e,a=Date.now(),r=Date.now()){return k(t,n,i=>r<i.lastRateLimitedAt?null:{...i,lastAttemptAt:Math.max(i.lastAttemptAt,a),lastRateLimitedAt:r,rateLimitedUntil:r+Math.max(0,e)})}s(Ut,"markSubscriptionRateLimited");async function kt(t,n,e=Date.now()){return k(t,n,a=>e<a.lastRateLimitedAt?null:{...a,authFailure:void 0,lastAttemptAt:Math.max(a.lastAttemptAt,e),rateLimitedUntil:0})}s(kt,"markSubscriptionSuccess");async function Nt(t,n,e){return x(t,async(a,r,i)=>{const c=U(r,n);if(c<0)throw new Error(`${h(t)} subscription "${n}" no longer exists`);const u=r[c];if(u.entry.expires>Date.now())return u.entry;const d=V(t,u.entry.label,await e(u.entry.refresh));return a.accounts[u.row.id]={...a.accounts[u.row.id],...d},i(a),{...u.entry,...d}})}s(Nt,"refreshSubscriptionCredential");function ot(t){const n=t,e=[n?.errorMessage,n?.message,t instanceof Error?t.message:""].filter(r=>typeof r=="string").join(" ");return(typeof n?.status=="number"?n.status:Number(e.match(/\bstatus[=:]\s*(\d+)/i)?.[1]))===400&&/invalid_grant|refresh token .*invalid|refresh token .*not found/i.test(e)}s(ot,"isInvalidRefreshTokenError");async function Dt(t,n,e){return x(t,async(a,r,i)=>{const c=new Set;let u;for(;c.size<r.length;){const d=n(r.filter(({entry:m})=>!c.has(m.label)).map(({entry:m})=>m));if(!d)break;const l=r.find(({entry:m})=>m.label===d.label);c.add(l.entry.label);try{const m=await e(l.entry);if(m){const o=V(t,l.entry.label,m);return a.accounts[l.row.id]={...a.accounts[l.row.id],...o},i(a),{...l.entry,...o}}return l.entry}catch(m){if(!ot(m))throw m;u=m;const o=Date.now();A(b(),g=>E(g,{...l.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(l.row.lastAttemptAt,o),rateLimitedUntil:Math.max(l.row.rateLimitedUntil,o+et)}))}}if(u)throw u})}s(Dt,"modifyElectedSubscription");async function lt(t,n){const e=n.accountId?.trim();if(t!==P)return e?{accountId:e}:{};try{const a=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${n.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!a.ok)return e?{accountId:e}:{};const r=await a.json(),i=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,c=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...i?{accountId:i}:{},...c?{email:c}:{}}}catch{return e?{accountId:e}:{}}}s(lt,"enrichManagedLoginIdentity");async function Mt(t,n,e){const a=await lt(t,e);return x(t,async(r,i,c)=>{const u=e.autoLabel?void 0:n?.trim(),d=y({refresh:e.refresh,accountId:a.accountId}),l=(u?i.find(({entry:p})=>f(p.label)===f(u)):void 0)??i.find(({entry:p})=>y(p)===d);let m=l?.entry.label??u;if(!m&&e.autoLabel){const p=a.email||n?.trim()||`Account ${i.length+1}`;m=p;for(let w=2;i.some(({entry:_})=>f(_.label)===f(m));w++)m=`${p} (${w})`}if(!m)throw new Error(`${h(t)} subscription label cannot be empty`);const o=$(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...a.accountId?{accountId:a.accountId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!l){if(i.some(({entry:_})=>f(_.label)===f(o.label)))throw new I(`${h(t)} subscription already exists: ${o.label}`);const p=i.find(({entry:_})=>y(_)===y(o));if(y(o)&&p)throw new I(`This ${h(t)} account is already added as \u201C${p.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const w=F();return r.accounts[w]={refresh:o.refresh,access:o.access,expires:o.expires,...o.accountId?{accountId:o.accountId}:{}},c(r),R(t,()=>A(b(),_=>S(_,t,{providerId:t,id:w,label:o.label,labelKey:f(o.label),position:i.length,lastAttemptAt:o.lastAttemptAt,lastRateLimitedAt:o.lastRateLimitedAt,rateLimitedUntil:o.rateLimitedUntil}))),L(t,r)}const g=l.row,O=i.find(({entry:p})=>p.label!==l.entry.label&&y(p)===y(o));if(y(o)&&O)throw new I(`This ${h(t)} account is already added as \u201C${O.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);r.accounts[g.id]={refresh:o.refresh,access:o.access,expires:o.expires,...o.accountId?{accountId:o.accountId}:{}},c(r);const C=o.lastRateLimitedAt>=g.lastRateLimitedAt;return R(t,()=>A(b(),p=>E(p,{...g,lastAttemptAt:Math.max(g.lastAttemptAt,o.lastAttemptAt),lastRateLimitedAt:C?o.lastRateLimitedAt:g.lastRateLimitedAt,rateLimitedUntil:C?0:g.rateLimitedUntil}))),L(t,r)})}s(Mt,"commitManagedLogin");function Tt(t,n=Date.now()){if(!t?.trim())return;const e=Number(t.trim());if(Number.isFinite(e))return Math.max(0,Math.round(e*1e3));const a=Date.parse(t);return Number.isFinite(a)?Math.max(0,a-n):void 0}s(Tt,"parseRetryAfterHeader");function ut(){const t=X(Y(b())).prepare("SELECT default_fallback_strength,revert_when_available FROM rotation_settings WHERE singleton=1").get();return{defaultFallbackStrength:K(t?.default_fallback_strength)??W,revertWhenAvailable:t?.revert_when_available===void 0||t.revert_when_available===null?!0:!!t.revert_when_available}}s(ut,"readRotationConfig");function Ot(t){const n=ut(),e={defaultFallbackStrength:K(t.defaultFallbackStrength)??n.defaultFallbackStrength??W,revertWhenAvailable:t.revertWhenAvailable??n.revertWhenAvailable??!0};return A(b(),a=>a.prepare("INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?) ON CONFLICT(singleton) DO UPDATE SET default_fallback_strength=excluded.default_fallback_strength,revert_when_available=excluded.revert_when_available").run(e.defaultFallbackStrength,e.revertWhenAvailable?1:0)),e}s(Ot,"writeRotationConfig");function Ct(t,n=Date.now()){const e=M(t),a=e.filter(i=>i.authFailure===void 0&&i.rateLimitedUntil<=n),r=h(t);return a.length===0?`${r} pool exhausted`:`${r} ${a[0]?.label??t} (${a.length}/${e.length} ready)`}s(Ct,"formatStatusLine");export{P as ANTHROPIC_PROVIDER_ID,I as DuplicateSubscriptionError,et as INVALID_REFRESH_TOKEN_BACKOFF_MS,tt as OPENAI_CODEX_PROVIDER_ID,it as addSubscription,Mt as commitManagedLogin,lt as enrichManagedLoginIdentity,Ct as formatStatusLine,h as getProviderLabel,ot as isInvalidRefreshTokenError,gt as isManagedProvider,Ft as markSubscriptionAttempt,St as markSubscriptionAuthenticationFailed,Ut as markSubscriptionRateLimited,kt as markSubscriptionSuccess,Dt as modifyElectedSubscription,Tt as parseRetryAfterHeader,$t as promoteSubscription,ut as readRotationConfig,M as readSubscriptionPool,Nt as refreshSubscriptionCredential,ct as removeAllManagedAccounts,Et as removeManagedAccount,wt as removeManagedProvider,Rt as removeSubscription,It as renameSubscription,xt as resolveProviderCandidates,_t as upsertSubscription,st as upsertSubscriptionWithUniqueAccount,Ot as writeRotationConfig,Lt as writeSubscriptionPool};
1
+ var B=Object.defineProperty;var s=(t,n)=>B(t,"name",{value:n,configurable:!0});import{createHash as z,randomUUID as F}from"node:crypto";import{join as J}from"node:path";import{LockedJsonFile as Q}from"./locked-file.js";import{piAgentDir as G}from"./runtime/package-health.js";import{openScopedStore as X}from"./scoped-state/db.js";import{providerStore as Y}from"./scoped-state/paths.js";import{providerRowByReference as Z,providerRows as v,providerWrite as A}from"./scoped-state/providers.js";const P="anthropic",tt="openai-codex",et=720*60*60*1e3,W="strong",H={anthropic:{label:"Claude",credentials:"anthropic-credentials.json"},"openai-codex":{label:"OpenAI Codex",credentials:"openai-codex-credentials.json"}};function b(){return G()}s(b,"agentDir");function k(t){return J(b(),H[t].credentials)}s(k,"credentialFile");function K(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}s(K,"normalizeStrength");function f(t){return t.toLowerCase()}s(f,"key");function nt(t){const n=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",a=typeof t.access=="string"?t.access.trim():"",r=Number(t.expires);if(!n||!e||!a||!Number.isFinite(r))return;const i=typeof t.accountId=="string"&&t.accountId.trim()?t.accountId.trim():void 0;return{label:n,refresh:e,access:a,expires:r,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...i?{accountId:i}:{}}}s(nt,"credential");function $(t,n){if(!n.label?.trim())throw new Error(`${h(t)} subscription label cannot be empty`);const e=nt(n);if(!e)throw new Error(`Refusing to persist incomplete ${h(t)} subscription "${n.label.trim()}"; pool rows require both OAuth tokens.`);return e}s($,"assertCredential");function j(t,n){if(!n||n.trim()==="")return{version:1,accounts:{}};let e;try{e=JSON.parse(n)}catch(a){throw new Error(`managed credential file ${t} is corrupt: ${a instanceof Error?a.message:String(a)}`)}if(typeof e!="object"||e===null||Array.isArray(e)||e.version!==1||typeof e.accounts!="object"||e.accounts===null||Array.isArray(e.accounts))throw new Error(`managed credential file ${t} is corrupt`);return e}s(j,"parseCredentials");function at(t,n){const e=n.accounts[t.id];if(!e||typeof e.refresh!="string"||typeof e.access!="string"||!Number.isFinite(e.expires))throw new Error(`managed ${t.providerId} account ${t.id} has no credential record`);return{label:t.label,refresh:e.refresh,access:e.access,expires:e.expires,rateLimitedUntil:t.rateLimitedUntil,lastAttemptAt:t.lastAttemptAt,lastRateLimitedAt:t.lastRateLimitedAt,...t.authFailure?{authFailure:t.authFailure}:{},...e.accountId?{accountId:e.accountId}:{}}}s(at,"toPublic");function N(t,n){return v(b(),t).map(e=>({row:e,entry:at(e,n)}))}s(N,"rowsWithCredentials");function q(t){return new Q(k(t),!0,`{"version":1,"accounts":{}}
2
+ `)}s(q,"managedCredentialFile");function rt(t){return j(k(t),q(t).readUnlocked())}s(rt,"readFile");function y(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${z("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}s(y,"identity");function V(t,n,e){const a=e.refresh?.trim()??"",r=e.access?.trim()??"",i=Number(e.expires);if(!a||!r||!Number.isFinite(i))throw new Error(`${h(t)} subscription "${n}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:a,access:r,expires:i}}s(V,"assertTokens");function gt(t){return t===P||t===tt}s(gt,"isManagedProvider");function h(t){return H[t].label}s(h,"getProviderLabel");function xt(t,n=Date.now(),e={}){const a=[],r=[];for(let i=0;i<t.length;i++){const o=t[i],c=o.registered?o.automaticFallback===!1?"non-managed-fallback":o.authenticated?(o.coolingUntil??0)>n?"cooling":void 0:"unauthenticated":"unregistered";if(!c)a.push(o);else if(r.push({candidate:o,reason:c}),e.pinnedToFirst&&i===0&&c!=="cooling")break}return{candidates:a,rejected:r}}s(xt,"resolveProviderCandidates");class E extends Error{static{s(this,"DuplicateSubscriptionError")}constructor(n){super(n),this.name="DuplicateSubscriptionError"}}function D(t){return N(t,rt(t)).map(({entry:n})=>n)}s(D,"readSubscriptionPool");async function x(t,n){const e=k(t);return q(t).withLockAsync(async(a,r,i)=>{const o=j(e,a);return{result:await n(o,N(t,o),c=>i(`${JSON.stringify(c)}
3
+ `))}})}s(x,"locked");function S(t,n,e){t.prepare("INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)").run(n,e.id,e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null)}s(S,"insert");function I(t,n){t.prepare("UPDATE managed_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE provider_id=? AND id=?").run(n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,n.providerId,n.id)}s(I,"update");function L(t,n){return N(t,n).map(({entry:e})=>e)}s(L,"publicPool");function w(t,n){try{n()}catch(e){throw new Error(`managed ${h(t)} credential file was published, but its SQL membership update failed; SQL membership remains authoritative: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}s(w,"writeMembershipAfterCredentialPublish");async function Lt(t,n){return x(t,async(e,a,r)=>{const i=n.map(d=>$(t,d)),o=i.map((d,u)=>({entry:d,id:a[u]?.row.id??F()})),c={};for(const{entry:d,id:u}of o)c[u]={refresh:d.refresh,access:d.access,expires:d.expires,...d.accountId?{accountId:d.accountId}:{}};return r({version:1,accounts:c}),w(t,()=>A(b(),d=>{d.prepare("DELETE FROM managed_accounts WHERE provider_id=?").run(t),o.forEach(({entry:u,id:m},l)=>S(d,t,{providerId:t,id:m,label:u.label,labelKey:f(u.label),position:l,lastAttemptAt:u.lastAttemptAt,lastRateLimitedAt:u.lastRateLimitedAt,rateLimitedUntil:u.rateLimitedUntil,...u.authFailure?{authFailure:u.authFailure}:{}}))})),i})}s(Lt,"writeSubscriptionPool");function U(t,n){const e=n.trim().toLowerCase(),a=Number.parseInt(e,10);return Number.isFinite(a)&&String(a)===e&&a>=1&&a<=t.length?a-1:t.findIndex(({entry:r})=>f(r.label)===e)}s(U,"resolve");async function it(t,n){const e=$(t,n);return x(t,async(a,r,i)=>{if(r.some(({entry:u})=>f(u.label)===f(e.label)))throw new E(`${h(t)} subscription already exists: ${e.label}`);const o=y(e),c=r.find(({entry:u})=>y(u)===o);if(o&&c)throw new E(`This ${h(t)} account is already added as \u201C${c.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const d=F();return a.accounts[d]={refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{}},i(a),w(t,()=>A(b(),u=>S(u,t,{providerId:t,id:d,label:e.label,labelKey:f(e.label),position:r.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}}))),L(t,a)})}s(it,"addSubscription");async function _t(t,n){const e=$(t,n);return D(t).find(r=>f(r.label)===f(e.label))?st(t,e):it(t,e)}s(_t,"upsertSubscription");async function st(t,n){const e=$(t,n);return x(t,async(a,r,i)=>{const o=r.findIndex(({entry:l})=>f(l.label)===f(e.label)),c=r[o],d=r.find(({entry:l},g)=>g!==o&&y(l)===y(e));if(y(e)&&d)throw new E(`This ${h(t)} account is already added as \u201C${d.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);if(!c){const l=F();return a.accounts[l]={refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{}},i(a),w(t,()=>A(b(),g=>S(g,t,{providerId:t,id:l,label:e.label,labelKey:f(e.label),position:r.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil}))),L(t,a)}const u=c.row,m=e.lastRateLimitedAt>=u.lastRateLimitedAt;return a.accounts[u.id]={refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{}},i(a),w(t,()=>A(b(),l=>I(l,{...u,label:e.label,labelKey:f(e.label),lastAttemptAt:Math.max(u.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:u.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:u.rateLimitedUntil}))),L(t,a)})}s(st,"upsertSubscriptionWithUniqueAccount");async function Et(t,n,e){return x(t,async(a,r)=>{const i=U(r,n);if(i<0)throw new Error(`Unknown ${h(t)} subscription: ${n}`);const o=e.trim();if(!o)throw new Error(`${h(t)} subscription label cannot be empty`);if(r.some((c,d)=>d!==i&&f(c.entry.label)===f(o)))throw new E(`${h(t)} subscription already exists: ${o}`);return A(b(),c=>I(c,{...r[i].row,label:o,labelKey:f(o)})),L(t,a)})}s(Et,"renameSubscription");async function M(t,n){return x(t,async(e,a,r)=>{const i=U(a,n);if(i<0)throw new Error(`Unknown ${h(t)} subscription: ${n}`);const o=a[i].row;return A(b(),c=>c.prepare("DELETE FROM managed_accounts WHERE provider_id=? AND id=?").run(t,o.id)),delete e.accounts[o.id],r(e),L(t,e)})}s(M,"remove");async function It(t,n){return M(t,n)}s(It,"removeManagedAccount");async function wt(t,n){return M(t,n)}s(wt,"removeSubscription");async function ct(t){return x(t,async(n,e,a)=>{A(b(),r=>r.prepare("DELETE FROM managed_accounts WHERE provider_id=?").run(t));for(const{row:r}of e)delete n.accounts[r.id];return a(n),e.length})}s(ct,"removeAllManagedAccounts");async function Rt(t,n){return n===void 0?(await ct(t),[]):M(t,n)}s(Rt,"removeManagedProvider");async function $t(t,n){return x(t,async(e,a)=>{const r=U(a,n);if(r<0)throw new Error(`Unknown ${h(t)} subscription: ${n}`);return A(b(),i=>{[a[r],...a.filter((c,d)=>d!==r)].forEach((c,d)=>I(i,{...c.row,position:d}))}),L(t,e)})}s($t,"promoteSubscription");async function T(t,n,e){return A(b(),a=>{const r=Z(a,t,n);if(!r)throw new Error(`Unknown ${h(t)} subscription: ${n}`);const i=e(r);i&&I(a,i)}),D(t)}s(T,"mutateHealth");async function Ft(t,n,e=Date.now()){return T(t,n,a=>({...a,lastAttemptAt:Math.max(a.lastAttemptAt,e)}))}s(Ft,"markSubscriptionAttempt");async function St(t,n,e,a,r=Date.now()){return x(t,async(i,o)=>{const c=o.find(({entry:d})=>f(d.label)===f(n));if(!c)throw new Error(`Unknown ${h(t)} subscription: ${n}`);return c.entry.refresh!==e.refresh||c.entry.access!==e.access||A(b(),d=>I(d,{...c.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(c.row.lastAttemptAt,r),rateLimitedUntil:Math.max(c.row.rateLimitedUntil,r+Math.max(0,a))})),L(t,i)})}s(St,"markSubscriptionAuthenticationFailed");async function Ut(t,n,e,a=Date.now(),r=Date.now()){return T(t,n,i=>r<i.lastRateLimitedAt?null:{...i,lastAttemptAt:Math.max(i.lastAttemptAt,a),lastRateLimitedAt:r,rateLimitedUntil:r+Math.max(0,e)})}s(Ut,"markSubscriptionRateLimited");async function kt(t,n,e=Date.now()){return T(t,n,a=>e<a.lastRateLimitedAt?null:{...a,authFailure:void 0,lastAttemptAt:Math.max(a.lastAttemptAt,e),rateLimitedUntil:0})}s(kt,"markSubscriptionSuccess");async function Nt(t,n,e){return x(t,async(a,r,i)=>{const o=U(r,n);if(o<0)throw new Error(`${h(t)} subscription "${n}" no longer exists`);const c=r[o];if(c.entry.expires>Date.now())return c.entry;const d=V(t,c.entry.label,await e(c.entry.refresh));return a.accounts[c.row.id]={...a.accounts[c.row.id],...d},i(a),{...c.entry,...d}})}s(Nt,"refreshSubscriptionCredential");function ot(t){const n=t,e=[n?.errorMessage,n?.message,t instanceof Error?t.message:""].filter(r=>typeof r=="string").join(" ");return(typeof n?.status=="number"?n.status:Number(e.match(/\bstatus[=:]\s*(\d+)/i)?.[1]))===400&&/invalid_grant|refresh token .*invalid|refresh token .*not found/i.test(e)}s(ot,"isInvalidRefreshTokenError");async function Dt(t,n,e){return x(t,async(a,r,i)=>{const o=new Set;let c;for(;o.size<r.length;){const d=n(r.filter(({entry:m})=>!o.has(m.label)).map(({entry:m})=>m));if(!d)break;const u=r.find(({entry:m})=>m.label===d.label);o.add(u.entry.label);try{const m=await e(u.entry);if(m){const l=V(t,u.entry.label,m);return a.accounts[u.row.id]={...a.accounts[u.row.id],...l},i(a),{...u.entry,...l}}return u.entry}catch(m){if(!ot(m))throw m;c=m;const l=Date.now();A(b(),g=>I(g,{...u.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(u.row.lastAttemptAt,l),rateLimitedUntil:Math.max(u.row.rateLimitedUntil,l+et)}))}}if(c)throw c})}s(Dt,"modifyElectedSubscription");async function lt(t,n){const e=n.accountId?.trim();if(t!==P)return e?{accountId:e}:{};try{const a=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${n.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!a.ok)return e?{accountId:e}:{};const r=await a.json(),i=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,o=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...i?{accountId:i}:{},...o?{email:o}:{}}}catch{return e?{accountId:e}:{}}}s(lt,"enrichManagedLoginIdentity");async function Mt(t,n,e){const a=await lt(t,e);return x(t,async(r,i,o)=>{const c=e.autoLabel?void 0:n?.trim(),d=y({refresh:e.refresh,accountId:a.accountId}),u=(c?i.find(({entry:p})=>f(p.label)===f(c)):void 0)??i.find(({entry:p})=>y(p)===d);let m=u?.entry.label??c;if(!m&&e.autoLabel){const p=a.email||n?.trim()||`Account ${i.length+1}`;m=p;for(let R=2;i.some(({entry:_})=>f(_.label)===f(m));R++)m=`${p} (${R})`}if(!m)throw new Error(`${h(t)} subscription label cannot be empty`);const l=$(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...a.accountId?{accountId:a.accountId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!u){if(i.some(({entry:_})=>f(_.label)===f(l.label)))throw new E(`${h(t)} subscription already exists: ${l.label}`);const p=i.find(({entry:_})=>y(_)===y(l));if(y(l)&&p)throw new E(`This ${h(t)} account is already added as \u201C${p.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const R=F();return r.accounts[R]={refresh:l.refresh,access:l.access,expires:l.expires,...l.accountId?{accountId:l.accountId}:{}},o(r),w(t,()=>A(b(),_=>S(_,t,{providerId:t,id:R,label:l.label,labelKey:f(l.label),position:i.length,lastAttemptAt:l.lastAttemptAt,lastRateLimitedAt:l.lastRateLimitedAt,rateLimitedUntil:l.rateLimitedUntil}))),L(t,r)}const g=u.row,O=i.find(({entry:p})=>p.label!==u.entry.label&&y(p)===y(l));if(y(l)&&O)throw new E(`This ${h(t)} account is already added as \u201C${O.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);r.accounts[g.id]={refresh:l.refresh,access:l.access,expires:l.expires,...l.accountId?{accountId:l.accountId}:{}},o(r);const C=l.lastRateLimitedAt>=g.lastRateLimitedAt;return w(t,()=>A(b(),p=>I(p,{...g,authFailure:void 0,lastAttemptAt:Math.max(g.lastAttemptAt,l.lastAttemptAt),lastRateLimitedAt:C?l.lastRateLimitedAt:g.lastRateLimitedAt,rateLimitedUntil:C?0:g.rateLimitedUntil}))),L(t,r)})}s(Mt,"commitManagedLogin");function Tt(t,n=Date.now()){if(!t?.trim())return;const e=Number(t.trim());if(Number.isFinite(e))return Math.max(0,Math.round(e*1e3));const a=Date.parse(t);return Number.isFinite(a)?Math.max(0,a-n):void 0}s(Tt,"parseRetryAfterHeader");function ut(){const t=X(Y(b())).prepare("SELECT default_fallback_strength,revert_when_available FROM rotation_settings WHERE singleton=1").get();return{defaultFallbackStrength:K(t?.default_fallback_strength)??W,revertWhenAvailable:t?.revert_when_available===void 0||t.revert_when_available===null?!0:!!t.revert_when_available}}s(ut,"readRotationConfig");function Ot(t){const n=ut(),e={defaultFallbackStrength:K(t.defaultFallbackStrength)??n.defaultFallbackStrength??W,revertWhenAvailable:t.revertWhenAvailable??n.revertWhenAvailable??!0};return A(b(),a=>a.prepare("INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?) ON CONFLICT(singleton) DO UPDATE SET default_fallback_strength=excluded.default_fallback_strength,revert_when_available=excluded.revert_when_available").run(e.defaultFallbackStrength,e.revertWhenAvailable?1:0)),e}s(Ot,"writeRotationConfig");function Ct(t,n=Date.now()){const e=D(t),a=e.filter(i=>i.authFailure===void 0&&i.rateLimitedUntil<=n),r=h(t);return a.length===0?`${r} pool exhausted`:`${r} ${a[0]?.label??t} (${a.length}/${e.length} ready)`}s(Ct,"formatStatusLine");export{P as ANTHROPIC_PROVIDER_ID,E as DuplicateSubscriptionError,et as INVALID_REFRESH_TOKEN_BACKOFF_MS,tt as OPENAI_CODEX_PROVIDER_ID,it as addSubscription,Mt as commitManagedLogin,lt as enrichManagedLoginIdentity,Ct as formatStatusLine,h as getProviderLabel,ot as isInvalidRefreshTokenError,gt as isManagedProvider,Ft as markSubscriptionAttempt,St as markSubscriptionAuthenticationFailed,Ut as markSubscriptionRateLimited,kt as markSubscriptionSuccess,Dt as modifyElectedSubscription,Tt as parseRetryAfterHeader,$t as promoteSubscription,ut as readRotationConfig,D as readSubscriptionPool,Nt as refreshSubscriptionCredential,ct as removeAllManagedAccounts,It as removeManagedAccount,Rt as removeManagedProvider,wt as removeSubscription,Et as renameSubscription,xt as resolveProviderCandidates,_t as upsertSubscription,st as upsertSubscriptionWithUniqueAccount,Ot as writeRotationConfig,Lt as writeSubscriptionPool};
@@ -1,3 +1,4 @@
1
+ type RendererDiagnostic = (message: string) => void;
1
2
  /**
2
3
  * Memoized, self-healing. Guarantees at most one authoritative renderer
3
4
  * lifecycle per process: trust a valid stamp outright (zero subprocess spawns),
@@ -12,7 +13,7 @@
12
13
  * Invoked at postinstall AND lazily on the first render/check/display call,
13
14
  * so `npm ci --ignore-scripts` consumers still self-heal on first use.
14
15
  */
15
- export declare function ensureRenderer(): void;
16
+ export declare function ensureRenderer(report?: RendererDiagnostic): void;
16
17
  /** Cheap predicate — true when the pinned managed binary is verified ready. Does not install or spawn. */
17
18
  export declare function isRendererReady(): boolean;
18
19
  /** Render markdown to terminal lines via the pinned binary; plaintext fallback.
@@ -67,3 +68,4 @@ export declare function checkMarkdown(md: string): {
67
68
  /** Shell command for a pane that live-renders `path` with crouter's pinned
68
69
  * termrender. Null means the managed renderer could not be provisioned. */
69
70
  export declare function documentWatchCommand(path: string): string | null;
71
+ export {};
@@ -1,14 +1,14 @@
1
- var j=Object.defineProperty;var a=(r,e)=>j(r,"name",{value:e,configurable:!0});import{execFileSync as g,spawnSync as G}from"node:child_process";import{existsSync as A,readFileSync as X,writeFileSync as Y,statSync as q,mkdirSync as Q,unlinkSync as C,renameSync as W,accessSync as Z,realpathSync as I,constants as ee}from"node:fs";import{homedir as re}from"node:os";import{join as b,resolve as te}from"node:path";import N from"string-width";import{shellQuote as V}from"../../shared/shell-quote.js";import{withExclusiveDirectoryLock as ne}from"../exclusive-lock.js";import{TERMRENDER_VERSION as w}from"./version.js";const R=b(te(process.env.XDG_CACHE_HOME||b(re(),".cache")),"crouter","termrender",w),E=b(R,"venv"),h=b(E,"bin/termrender"),m=b(E,"bin/python"),k=b(E,".crtr-termrender-stamp.json"),se=b(R,".crtr-termrender.lock"),ie=36e4;let f="unchecked";function T(r){const e=r;return!!e&&typeof e.mtimeMs=="number"&&typeof e.size=="number"&&typeof e.mode=="number"}a(T,"isFp");function S(r){try{const e=q(r);return{mtimeMs:e.mtimeMs,size:e.size,mode:e.mode}}catch{return null}}a(S,"fingerprint");function D(r,e){return!!r&&r.mtimeMs===e.mtimeMs&&r.size===e.size&&r.mode===e.mode}a(D,"fpMatch");function oe(){try{const r=JSON.parse(X(k,"utf8"));return r&&typeof r.version=="string"&&T(r.bin)&&T(r.python)&&typeof r.pythonRealpath=="string"?r:null}catch{return null}}a(oe,"readStamp");function le(){const r=S(h),e=S(m);if(!r||!e){process.stderr.write(`[crtr] termrender stamp skipped: venv files vanished immediately after verify
2
- `);return}let n;try{n=I(m)}catch{n=m}const t={version:w,bin:r,python:e,pythonRealpath:n},s=`${k}.${process.pid}.tmp`;try{Y(s,JSON.stringify(t)),W(s,k)}catch(o){try{C(s)}catch{}process.stderr.write(`[crtr] termrender ready but stamp not persisted (${o instanceof Error?o.message:String(o)}); future launches will re-verify the slow way
3
- `)}}a(le,"publishStamp");function x(r){let e=!0;try{C(k)}catch(n){n.code!=="ENOENT"&&(e=!1)}f="unavailable",e?process.stderr.write(`[crtr] termrender invocation failed (${r}); invalidated readiness, future launches will repair
4
- `):process.stderr.write(`[crtr] termrender invocation failed (${r}) but its readiness marker could not be removed; future launches may keep trusting a broken renderer until ${k} is deleted
5
- `)}a(x,"invalidateRenderer");function B(r){const e=r;return e?.code==="ETIMEDOUT"||!!e?.killed&&!!e?.signal}a(B,"isTimeout");function _(){const r=oe();if(!r||r.version!==w)return!1;try{Z(h,ee.X_OK)}catch{return!1}if(!D(S(h),r.bin)||!D(S(m),r.python))return!1;try{if(I(m)!==r.pythonRealpath)return!1}catch{return!1}return!0}a(_,"stampValid");function ae(r){try{Q(R,{recursive:!0})}catch(e){f="unavailable",process.stderr.write(`[crtr] termrender unavailable \u2014 renderer cache cannot be created (${e instanceof Error?e.message:String(e)}); using plaintext fallback
6
- `);return}try{ne(se,r,{timeoutMs:ie})}catch{f="unavailable"}}a(ae,"withProvisionLock");function L(){if(!A(h))return!1;try{return g(h,["-h"],{encoding:"utf8",stdio:["ignore","pipe","pipe"],timeout:5e3}),!0}catch{return!1}}a(L,"binaryOk");function z(){if(!A(m))return null;try{return g(m,["-c",'import importlib.metadata as m; print(m.version("termrender"))'],{encoding:"utf8",stdio:["ignore","pipe","pipe"],timeout:5e3}).trim()||null}catch{return null}}a(z,"installedVersion");function ue(){try{return g("uv",["--version"],{stdio:"pipe",timeout:5e3}),!0}catch{return!1}}a(ue,"uvAvailable");function M(){if(f==="unchecked"){if(process.platform==="win32"){f="unavailable";return}if(_()){f="ready";return}ce()}}a(M,"ensureRenderer");function ce(){ae(()=>{if(_()){f="ready";return}let r=L()&&z()===w;if(!r){if(!ue()){process.stderr.write(`[crtr] termrender unavailable \u2014 install uv to enable rich rendering:
1
+ var j=Object.defineProperty;var a=(t,e)=>j(t,"name",{value:e,configurable:!0});import{execFileSync as g,spawnSync as G}from"node:child_process";import{existsSync as A,readFileSync as X,writeFileSync as Y,statSync as q,mkdirSync as Q,unlinkSync as C,renameSync as W,accessSync as Z,realpathSync as I,constants as ee}from"node:fs";import{homedir as te}from"node:os";import{join as b,resolve as re}from"node:path";import N from"string-width";import{shellQuote as V}from"../../shared/shell-quote.js";import{withExclusiveDirectoryLock as ne}from"../exclusive-lock.js";import{TERMRENDER_VERSION as v}from"./version.js";const R=b(re(process.env.XDG_CACHE_HOME||b(te(),".cache")),"crouter","termrender",v),E=b(R,"venv"),h=b(E,"bin/termrender"),m=b(E,"bin/python"),w=b(E,".crtr-termrender-stamp.json"),se=b(R,".crtr-termrender.lock"),ie=36e4;let f="unchecked";function T(t){const e=t;return!!e&&typeof e.mtimeMs=="number"&&typeof e.size=="number"&&typeof e.mode=="number"}a(T,"isFp");function S(t){try{const e=q(t);return{mtimeMs:e.mtimeMs,size:e.size,mode:e.mode}}catch{return null}}a(S,"fingerprint");function D(t,e){return!!t&&t.mtimeMs===e.mtimeMs&&t.size===e.size&&t.mode===e.mode}a(D,"fpMatch");function oe(){try{const t=JSON.parse(X(w,"utf8"));return t&&typeof t.version=="string"&&T(t.bin)&&T(t.python)&&typeof t.pythonRealpath=="string"?t:null}catch{return null}}a(oe,"readStamp");function le(t){const e=S(h),n=S(m);if(!e||!n){t(`[crtr] termrender stamp skipped: venv files vanished immediately after verify
2
+ `);return}let r;try{r=I(m)}catch{r=m}const i={version:v,bin:e,python:n,pythonRealpath:r},o=`${w}.${process.pid}.tmp`;try{Y(o,JSON.stringify(i)),W(o,w)}catch(u){try{C(o)}catch{}t(`[crtr] termrender ready but stamp not persisted (${u instanceof Error?u.message:String(u)}); future launches will re-verify the slow way
3
+ `)}}a(le,"publishStamp");function x(t){let e=!0;try{C(w)}catch(n){n.code!=="ENOENT"&&(e=!1)}f="unavailable",e?process.stderr.write(`[crtr] termrender invocation failed (${t}); invalidated readiness, future launches will repair
4
+ `):process.stderr.write(`[crtr] termrender invocation failed (${t}) but its readiness marker could not be removed; future launches may keep trusting a broken renderer until ${w} is deleted
5
+ `)}a(x,"invalidateRenderer");function B(t){const e=t;return e?.code==="ETIMEDOUT"||!!e?.killed&&!!e?.signal}a(B,"isTimeout");function _(){const t=oe();if(!t||t.version!==v)return!1;try{Z(h,ee.X_OK)}catch{return!1}if(!D(S(h),t.bin)||!D(S(m),t.python))return!1;try{if(I(m)!==t.pythonRealpath)return!1}catch{return!1}return!0}a(_,"stampValid");function ae(t,e){try{Q(R,{recursive:!0})}catch(n){f="unavailable",e(`[crtr] termrender unavailable \u2014 renderer cache cannot be created (${n instanceof Error?n.message:String(n)}); using plaintext fallback
6
+ `);return}try{ne(se,t,{timeoutMs:ie})}catch{f="unavailable"}}a(ae,"withProvisionLock");function L(){if(!A(h))return!1;try{return g(h,["-h"],{encoding:"utf8",stdio:["ignore","pipe","pipe"],timeout:5e3}),!0}catch{return!1}}a(L,"binaryOk");function z(){if(!A(m))return null;try{return g(m,["-c",'import importlib.metadata as m; print(m.version("termrender"))'],{encoding:"utf8",stdio:["ignore","pipe","pipe"],timeout:5e3}).trim()||null}catch{return null}}a(z,"installedVersion");function ue(){try{return g("uv",["--version"],{stdio:"pipe",timeout:5e3}),!0}catch{return!1}}a(ue,"uvAvailable");function M(t=e=>process.stderr.write(e)){if(f==="unchecked"){if(process.platform==="win32"){f="unavailable";return}if(_()){f="ready";return}ce(t)}}a(M,"ensureRenderer");function ce(t){ae(()=>{if(_()){f="ready";return}let e=L()&&z()===v;if(!e){if(!ue()){t(`[crtr] termrender unavailable \u2014 install uv to enable rich rendering:
7
7
  curl -LsSf https://astral.sh/uv/install.sh | sh
8
- `),f="unavailable";return}try{A(m)||g("uv",["venv","--clear",E],{stdio:"pipe",timeout:6e4}),g("uv",["pip","install","--reinstall","--python",m,`termrender==${w}`],{stdio:"pipe",timeout:12e4})}catch(e){process.stderr.write(`[crtr] termrender install failed (${e instanceof Error?e.message:String(e)}); using plaintext fallback
9
- `),f="unavailable";return}r=L()&&z()===w}if(!r){f="unavailable",process.stderr.write(`[crtr] termrender install completed but health check failed; using plaintext fallback
10
- `);return}le(),f="ready"})}a(ce,"provisionAndPublish");function Me(){return f==="ready"?!0:f==="unavailable"?!1:process.platform!=="win32"&&_()}a(Me,"isRendererReady");const pe=/\x1b\[[0-9;?]*[a-zA-Z]|\x1b[@-_]|[\x00-\x08\x0B\x0E-\x1F\x7F-\x9F]/g;function F(r){return typeof r!="string"?"":r.replace(pe,"")}a(F,"sanitize");function fe(r,e){let n=0,t="";for(const s of r){const o=N(s);if(n+o>e)break;t+=s,n+=o}return t===""&&r.length>0&&(t=[...r][0]),t}a(fe,"sliceByWidth");function P(r,e){if(e<1)return[r];const n=[],t=r.split(`
11
- `);for(let s=0;s<t.length;s++){const o=t[s];if(o===""){n.push("");continue}const c=o.split(/[ \t]+/).filter(Boolean);let p="";for(let l of c){for(;N(l)>e;){p&&(n.push(p),p="");const u=fe(l,e);n.push(u),l=l.slice(u.length)}const i=p?`${p} ${l}`:l;N(i)<=e?p=i:(p&&n.push(p),p=l)}p&&n.push(p)}return n.length>0?n:[""]}a(P,"wrap");class H{static{a(this,"BoundedLRUCache")}map=new Map;maxEntries;constructor(e){this.maxEntries=e}get(e){if(!this.map.has(e))return;const n=this.map.get(e);return this.map.delete(e),this.map.set(e,n),n}set(e,n){if(this.map.has(e))this.map.delete(e);else if(this.map.size>=this.maxEntries){const t=this.map.keys().next().value;this.map.delete(t)}this.map.set(e,n)}}const $=new H(20);function Ae(r,e){const n=`${r}\0${e}`,t=$.get(n);if(t)return[...t];if(M(),f==="ready")try{const c=g(h,["doc","render","--width",String(e),"--color","on"],{input:r,encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"]}).split(`
12
- `);return c.length>0&&c[c.length-1]===""&&c.pop(),$.set(n,c),[...c]}catch(o){B(o)||x("doc render")}const s=P(F(r),e);return $.set(n,s),[...s]}a(Ae,"renderMarkdown");const K=new Set(["mermaid"]);function de(r,e){let n;try{n=JSON.parse(r)}catch{return null}const t=n;if(!Array.isArray(t.lines)||!Array.isArray(t.rows)||!Array.isArray(t.spans)||!Array.isArray(t.blocks)||t.rows.length!==t.lines.length||t.spans.length!==t.lines.length||!t.lines.every(i=>typeof i=="string"))return null;const s=t.blocks.length;if(!t.rows.every(i=>i===null||typeof i=="number"&&Number.isInteger(i)&&i>=0&&i<s))return null;const o=Math.max(1,e.split(`
13
- `).length),c=[];let p=0;for(const i of t.blocks){if(typeof i!="object"||i===null||typeof i.type!="string"||i.type===""||i.start!=null&&!Number.isInteger(i.start)||i.end!=null&&!Number.isInteger(i.end))return null;const u=typeof i.start=="number"?i.start:p+1,d=typeof i.end=="number"?Math.max(i.end,u):u,y=Math.max(1,Math.min(u,o)),v=Math.max(y,Math.min(d,o));c.push({type:i.type,start:y,end:v}),p=v}c.length===0&&c.push({type:"paragraph",start:1,end:o});const l=[];for(let i=0;i<t.spans.length;i++){const u=t.spans[i];if(u===null){l.push(null);continue}if(!Array.isArray(u)||u.length!==2)return null;const[d,y]=u;if(!Number.isInteger(d)||!Number.isInteger(y)||d<1||y<d||y>o)return null;const v=t.rows[i];if(v===null)return null;const O=c[v];if(d<O.start||y>O.end)return null;l.push([d,y])}return{lines:t.lines,rows:t.rows,spans:l,blocks:c}}a(de,"parseRenderedDoc");function he(r,e){const n=F(r).split(`
14
- `),t=[];let s=null;for(let l=0;l<n.length;l++)n[l].trim()!==""?s===null&&(s=l):s!==null&&(t.push({type:"paragraph",start:s+1,end:l}),s=null);s!==null&&t.push({type:"paragraph",start:s+1,end:n.length}),t.length===0&&t.push({type:"paragraph",start:1,end:Math.max(1,n.length)});const o=[],c=[],p=[];return t.forEach((l,i)=>{i>0&&(o.push(""),c.push(null),p.push(null));for(let u=l.start;u<=l.end;u++)for(const d of P(n[u-1]??"",e))o.push(d),c.push(i),p.push([u,u])}),{lines:o,rows:c,spans:p,blocks:t}}a(he,"fallbackDocWithMap");const J=new H(20);function U(r,e){const n=`${r}\0${e}`,t=J.get(n);if(t)return t;if(M(),f==="ready")try{const s=g(h,["doc","render","--width",String(e),"--color","on","--line-map"],{input:r,encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"],maxBuffer:67108864}),o=de(s.trim(),r);if(o!==null)return J.set(n,o),o;x("doc render --line-map (malformed output)")}catch(s){B(s)||x("doc render --line-map")}return he(r,e)}a(U,"renderMarkdownWithMap");function me(r,e,n){const t=U(r,e);if(n<=e||!t.blocks.some(l=>K.has(l.type)))return t;const s=U(r,n);if(s.blocks.length!==t.blocks.length)return t;const o=[],c=[],p=[];for(let l=0;l<t.lines.length;l++){const i=t.rows[l]??null;if(i===null||!K.has(t.blocks[i].type)){o.push(t.lines[l]),c.push(i),p.push(t.spans[l]??null);continue}if(l===0||t.rows[l-1]!==i)for(let u=0;u<s.lines.length;u++)s.rows[u]===i&&(o.push(s.lines[u]),c.push(i),p.push(s.spans[u]??null))}return{lines:o,rows:c,spans:p,blocks:t.blocks}}a(me,"renderMarkdownBlockAware");function Ne(r,e,n){return[...me(r,e,n).lines]}a(Ne,"renderMarkdownBlockAwareLines");function Re(r){if(M(),f!=="ready")return{ok:!0};const e=G(h,["doc","check"],{input:r,encoding:"utf-8",timeout:5e3});if(e.error)return e.error.code==="ETIMEDOUT"||e.signal==="SIGTERM"||x("doc check"),{ok:!1,error:`termrender: invocation failed: ${e.error.message}`};let n=null;const t=typeof e.stdout=="string"?e.stdout:"";if(t)try{n=JSON.parse(t.trim())}catch{}if(n!==null){if(n.ok)return{ok:!0};const s=Array.isArray(n.errors)?n.errors[0]:void 0;return{ok:!1,error:`termrender: ${s&&typeof s.message=="string"&&s.message?s.message:"invalid markdown"}`}}return e.status!==0?{ok:!1,error:`termrender: doc check exited ${e.status}`}:{ok:!0}}a(Re,"checkMarkdown");function _e(r){return M(),f!=="ready"?null:`${V(h)} doc watch ${V(r)}`}a(_e,"documentWatchCommand");export{Re as checkMarkdown,_e as documentWatchCommand,M as ensureRenderer,Me as isRendererReady,Ae as renderMarkdown,me as renderMarkdownBlockAware,Ne as renderMarkdownBlockAwareLines,U as renderMarkdownWithMap};
8
+ `),f="unavailable";return}try{A(m)||g("uv",["venv","--clear",E],{stdio:"pipe",timeout:6e4}),g("uv",["pip","install","--reinstall","--python",m,`termrender==${v}`],{stdio:"pipe",timeout:12e4})}catch(n){t(`[crtr] termrender install failed (${n instanceof Error?n.message:String(n)}); using plaintext fallback
9
+ `),f="unavailable";return}e=L()&&z()===v}if(!e){f="unavailable",t(`[crtr] termrender install completed but health check failed; using plaintext fallback
10
+ `);return}le(t),f="ready"},t)}a(ce,"provisionAndPublish");function Me(){return f==="ready"?!0:f==="unavailable"?!1:process.platform!=="win32"&&_()}a(Me,"isRendererReady");const pe=/\x1b\[[0-9;?]*[a-zA-Z]|\x1b[@-_]|[\x00-\x08\x0B\x0E-\x1F\x7F-\x9F]/g;function F(t){return typeof t!="string"?"":t.replace(pe,"")}a(F,"sanitize");function fe(t,e){let n=0,r="";for(const i of t){const o=N(i);if(n+o>e)break;r+=i,n+=o}return r===""&&t.length>0&&(r=[...t][0]),r}a(fe,"sliceByWidth");function P(t,e){if(e<1)return[t];const n=[],r=t.split(`
11
+ `);for(let i=0;i<r.length;i++){const o=r[i];if(o===""){n.push("");continue}const u=o.split(/[ \t]+/).filter(Boolean);let p="";for(let l of u){for(;N(l)>e;){p&&(n.push(p),p="");const c=fe(l,e);n.push(c),l=l.slice(c.length)}const s=p?`${p} ${l}`:l;N(s)<=e?p=s:(p&&n.push(p),p=l)}p&&n.push(p)}return n.length>0?n:[""]}a(P,"wrap");class H{static{a(this,"BoundedLRUCache")}map=new Map;maxEntries;constructor(e){this.maxEntries=e}get(e){if(!this.map.has(e))return;const n=this.map.get(e);return this.map.delete(e),this.map.set(e,n),n}set(e,n){if(this.map.has(e))this.map.delete(e);else if(this.map.size>=this.maxEntries){const r=this.map.keys().next().value;this.map.delete(r)}this.map.set(e,n)}}const $=new H(20);function Ae(t,e){const n=`${t}\0${e}`,r=$.get(n);if(r)return[...r];if(M(),f==="ready")try{const u=g(h,["doc","render","--width",String(e),"--color","on"],{input:t,encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"]}).split(`
12
+ `);return u.length>0&&u[u.length-1]===""&&u.pop(),$.set(n,u),[...u]}catch(o){B(o)||x("doc render")}const i=P(F(t),e);return $.set(n,i),[...i]}a(Ae,"renderMarkdown");const K=new Set(["mermaid"]);function de(t,e){let n;try{n=JSON.parse(t)}catch{return null}const r=n;if(!Array.isArray(r.lines)||!Array.isArray(r.rows)||!Array.isArray(r.spans)||!Array.isArray(r.blocks)||r.rows.length!==r.lines.length||r.spans.length!==r.lines.length||!r.lines.every(s=>typeof s=="string"))return null;const i=r.blocks.length;if(!r.rows.every(s=>s===null||typeof s=="number"&&Number.isInteger(s)&&s>=0&&s<i))return null;const o=Math.max(1,e.split(`
13
+ `).length),u=[];let p=0;for(const s of r.blocks){if(typeof s!="object"||s===null||typeof s.type!="string"||s.type===""||s.start!=null&&!Number.isInteger(s.start)||s.end!=null&&!Number.isInteger(s.end))return null;const c=typeof s.start=="number"?s.start:p+1,d=typeof s.end=="number"?Math.max(s.end,c):c,y=Math.max(1,Math.min(c,o)),k=Math.max(y,Math.min(d,o));u.push({type:s.type,start:y,end:k}),p=k}u.length===0&&u.push({type:"paragraph",start:1,end:o});const l=[];for(let s=0;s<r.spans.length;s++){const c=r.spans[s];if(c===null){l.push(null);continue}if(!Array.isArray(c)||c.length!==2)return null;const[d,y]=c;if(!Number.isInteger(d)||!Number.isInteger(y)||d<1||y<d||y>o)return null;const k=r.rows[s];if(k===null)return null;const O=u[k];if(d<O.start||y>O.end)return null;l.push([d,y])}return{lines:r.lines,rows:r.rows,spans:l,blocks:u}}a(de,"parseRenderedDoc");function he(t,e){const n=F(t).split(`
14
+ `),r=[];let i=null;for(let l=0;l<n.length;l++)n[l].trim()!==""?i===null&&(i=l):i!==null&&(r.push({type:"paragraph",start:i+1,end:l}),i=null);i!==null&&r.push({type:"paragraph",start:i+1,end:n.length}),r.length===0&&r.push({type:"paragraph",start:1,end:Math.max(1,n.length)});const o=[],u=[],p=[];return r.forEach((l,s)=>{s>0&&(o.push(""),u.push(null),p.push(null));for(let c=l.start;c<=l.end;c++)for(const d of P(n[c-1]??"",e))o.push(d),u.push(s),p.push([c,c])}),{lines:o,rows:u,spans:p,blocks:r}}a(he,"fallbackDocWithMap");const J=new H(20);function U(t,e){const n=`${t}\0${e}`,r=J.get(n);if(r)return r;if(M(),f==="ready")try{const i=g(h,["doc","render","--width",String(e),"--color","on","--line-map"],{input:t,encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"],maxBuffer:67108864}),o=de(i.trim(),t);if(o!==null)return J.set(n,o),o;x("doc render --line-map (malformed output)")}catch(i){B(i)||x("doc render --line-map")}return he(t,e)}a(U,"renderMarkdownWithMap");function me(t,e,n){const r=U(t,e);if(n<=e||!r.blocks.some(l=>K.has(l.type)))return r;const i=U(t,n);if(i.blocks.length!==r.blocks.length)return r;const o=[],u=[],p=[];for(let l=0;l<r.lines.length;l++){const s=r.rows[l]??null;if(s===null||!K.has(r.blocks[s].type)){o.push(r.lines[l]),u.push(s),p.push(r.spans[l]??null);continue}if(l===0||r.rows[l-1]!==s)for(let c=0;c<i.lines.length;c++)i.rows[c]===s&&(o.push(i.lines[c]),u.push(s),p.push(i.spans[c]??null))}return{lines:o,rows:u,spans:p,blocks:r.blocks}}a(me,"renderMarkdownBlockAware");function Ne(t,e,n){return[...me(t,e,n).lines]}a(Ne,"renderMarkdownBlockAwareLines");function Re(t){if(M(),f!=="ready")return{ok:!0};const e=G(h,["doc","check"],{input:t,encoding:"utf-8",timeout:5e3});if(e.error)return e.error.code==="ETIMEDOUT"||e.signal==="SIGTERM"||x("doc check"),{ok:!1,error:`termrender: invocation failed: ${e.error.message}`};let n=null;const r=typeof e.stdout=="string"?e.stdout:"";if(r)try{n=JSON.parse(r.trim())}catch{}if(n!==null){if(n.ok)return{ok:!0};const i=Array.isArray(n.errors)?n.errors[0]:void 0;return{ok:!1,error:`termrender: ${i&&typeof i.message=="string"&&i.message?i.message:"invalid markdown"}`}}return e.status!==0?{ok:!1,error:`termrender: doc check exited ${e.status}`}:{ok:!0}}a(Re,"checkMarkdown");function _e(t){return M(),f!=="ready"?null:`${V(h)} doc watch ${V(t)}`}a(_e,"documentWatchCommand");export{Re as checkMarkdown,_e as documentWatchCommand,M as ensureRenderer,Me as isRendererReady,Ae as renderMarkdown,me as renderMarkdownBlockAware,Ne as renderMarkdownBlockAwareLines,U as renderMarkdownWithMap};
@@ -339,6 +339,10 @@ export declare const CONFIG_SETTINGS_COVERAGE: {
339
339
  surface: "specialized";
340
340
  owner: string;
341
341
  };
342
+ api: {
343
+ surface: "specialized";
344
+ owner: string;
345
+ };
342
346
  spawnEnv: {
343
347
  surface: "specialized";
344
348
  owner: string;
@@ -1 +1 @@
1
- var v=Object.defineProperty;var n=(e,t)=>v(e,"name",{value:t,configurable:!0});import{CONDENSED_HISTORY_MODES as u,MOUSE_MODE_DEFAULTS as p,WHIP_MESSAGE_MODES as d}from"../types.js";function i(e,t,o){const r=e.indexOf(t),a=r>=0?r:0;return e[(a+o+e.length)%e.length]}n(i,"cycleChoice");function c(e,t,o){const r=e.indexOf(t);return r>=0?e[(r+o+e.length)%e.length]:o>=0?e.find(a=>a>t)??e[0]:[...e].reverse().find(a=>a<t)??e[e.length-1]}n(c,"cycleNumber");function s(e,t){return e==="true"?!0:e==="false"?!1:`${t} must be true or false`}n(s,"booleanFromString");function f(e,t){if(!/^\d+$/.test(e))return`${t} must be an integer >= 0`;const o=Number(e);return Number.isSafeInteger(o)?o:`${t} must be an integer >= 0`}n(f,"nonNegativeIntegerFromString");function _(e,t){const o=f(e,t);return typeof o=="string"||o<1?`${t} must be an integer >= 1`:o}n(_,"positiveIntegerFromString");const m=[!1,"notify","apply"],w=[0,1,6,12,24,48,72,168],b=[1,2,3,4,5,6,8,10,12],l="openai-codex";function g(e,t){const o=e.providerOptions[t];if(o!==void 0)return o;const r={};return e.providerOptions[t]=r,r}n(g,"providerOptionsFor");const x=[1,2,3,4,5,6],h={schema_version:{surface:"specialized",owner:"internal schema migration"},marketplaces:{surface:"specialized",owner:"Plugins and pkg market commands"},plugins:{surface:"specialized",owner:"Plugins tab and pkg plugin commands"},auto_update:{surface:"settings",settings:{crtr:{key:"auto_update.crtr",label:"crouter updates",description:"Notify about crouter releases, install them automatically, or turn checks off.",valueConstraint:"true|false|notify|apply; true stores notify",applies:"Applies on the next crouter command.",group:"updates",order:70,format:n(e=>e.auto_update.crtr===!1?"off":e.auto_update.crtr,"format"),cycle:n((e,t)=>{e.auto_update.crtr=i(m,e.auto_update.crtr,t)},"cycle"),setFromString:n((e,t)=>{const o=t==="true"?"notify":t;return o!=="notify"&&o!=="apply"&&o!=="false"?"auto_update.crtr must be notify, apply, or false":(e.auto_update.crtr=o==="false"?!1:o,null)},"setFromString")},content:{key:"auto_update.content",label:"Content updates",description:"Notify about plugin and marketplace updates, apply them automatically, or turn checks off.",valueConstraint:"false|notify|apply",applies:"Applies on the next crouter command.",group:"updates",order:80,format:n(e=>e.auto_update.content===!1?"off":e.auto_update.content,"format"),cycle:n((e,t)=>{e.auto_update.content=i(m,e.auto_update.content,t)},"cycle"),setFromString:n((e,t)=>t!=="notify"&&t!=="apply"&&t!=="false"?"auto_update.content must be notify, apply, or false":(e.auto_update.content=t==="false"?!1:t,null),"setFromString")},interval_hours:{key:"auto_update.interval_hours",label:"Update interval",description:"Minimum hours between automatic update checks. Zero checks on every command.",valueConstraint:"integer >= 0",applies:"Applies on the next crouter command.",group:"updates",order:90,format:n(e=>`${e.auto_update.interval_hours}h`,"format"),cycle:n((e,t)=>{e.auto_update.interval_hours=c(w,e.auto_update.interval_hours,t)},"cycle"),setFromString:n((e,t)=>{const o=f(t,"auto_update.interval_hours");return typeof o=="string"?o:(e.auto_update.interval_hours=o,null)},"setFromString")}}},max_panes_per_window:{surface:"settings",setting:{key:"max_panes_per_window",label:"Maximum panes",description:"Maximum human-review panes placed in one tmux window before using another window.",valueConstraint:"integer >= 1",applies:"Applies to the next pane placement.",group:"panes",order:20,format:n(e=>String(e.max_panes_per_window),"format"),cycle:n((e,t)=>{e.max_panes_per_window=c(b,e.max_panes_per_window,t)},"cycle"),setFromString:n((e,t)=>{const o=_(t,"max_panes_per_window");return typeof o=="string"?o:(e.max_panes_per_window=o,null)},"setFromString")}},auto_open_child_viewers:{surface:"settings",setting:{key:"auto_open_child_viewers",label:"Open child panes",description:"Open each managed child beside its watched parent. Off keeps new children running in the background.",valueConstraint:"true|false",applies:"Applies to the next managed child created; independent roots still open their attended viewer.",group:"panes",order:10,format:n(e=>e.auto_open_child_viewers?"on":"off","format"),cycle:n(e=>{e.auto_open_child_viewers=!e.auto_open_child_viewers},"cycle"),setFromString:n((e,t)=>{const o=s(t,"auto_open_child_viewers");return typeof o=="string"?o:(e.auto_open_child_viewers=o,null)},"setFromString")}},brokerThresholds:{surface:"specialized",owner:"Broker limits tab"},lifecycle:{surface:"specialized",owner:"sys config set lifecycle.unattendedParkMs"},completion_bell:{surface:"settings",setting:{key:"completion_bell",label:"Completion bell",description:"Ring a short sound when the node in an attached viewer finishes a turn.",valueConstraint:"true|false",applies:"Applies on the next turn end.",group:"display",order:41,format:n(e=>e.completion_bell?"on":"off","format"),cycle:n(e=>{e.completion_bell=!e.completion_bell},"cycle"),setFromString:n((e,t)=>{const o=s(t,"completion_bell");return typeof o=="string"?o:(e.completion_bell=o,null)},"setFromString")}},working_gerunds:{surface:"specialized",owner:"sys config set working_gerunds"},whip_mode:{surface:"settings",setting:{key:"whip_mode",label:"Whip mode",description:"Play the whip animation when you send a prompt and when a viewer opens for an agent with an initial prompt.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:39,format:n(e=>e.whip_mode?"on":"off","format"),cycle:n(e=>{e.whip_mode=!e.whip_mode},"cycle"),setFromString:n((e,t)=>{const o=s(t,"whip_mode");return typeof o=="string"?o:(e.whip_mode=o,null)},"setFromString")}},page_surface:{surface:"settings",setting:{key:"page_surface",label:"Page human surface",description:"This host reads pages in a browser rather than a terminal. Agents are told the page module runs as written, HTML pages are offered, and document review and live display leave the command tree.",valueConstraint:"true|false",applies:"Applies when human command help is assembled.",group:"display",order:37,format:n(e=>e.page_surface?"on":"off","format"),cycle:n(e=>{e.page_surface=!e.page_surface},"cycle"),setFromString:n((e,t)=>{const o=s(t,"page_surface");return typeof o=="string"?o:(e.page_surface=o,null)},"setFromString")}},whip_messages:{surface:"specialized",owner:"sys config set whip_messages"},whip_message_mode:{surface:"settings",setting:{key:"whip_message_mode",label:"Whip message",description:"What the whip action sends: a random line from whip_messages, or the fixed \u201CGo faster\u201D.",valueConstraint:d.join("|"),applies:"Applies on the next attach.",group:"display",order:40,format:n(e=>e.whip_message_mode==="go-faster"?"go faster":"random","format"),cycle:n((e,t)=>{e.whip_message_mode=i(d,e.whip_message_mode,t)},"cycle"),setFromString:n((e,t)=>d.includes(t)?(e.whip_message_mode=t,null):`whip_message_mode must be one of: ${d.join("|")}`,"setFromString")}},mouse_mode_default:{surface:"settings",setting:{key:"mouse_mode_default",label:"Default mouse mode",description:"Initial mouse wheel scrolling for each attached viewer.",valueConstraint:p.join("|"),applies:"Applies on the next attach.",group:"display",order:38,format:n(e=>e.mouse_mode_default==="tmux"?"follow tmux":e.mouse_mode_default,"format"),cycle:n((e,t)=>{e.mouse_mode_default=i(p,e.mouse_mode_default,t)},"cycle"),setFromString:n((e,t)=>p.includes(t)?(e.mouse_mode_default=t,null):`mouse_mode_default must be one of: ${p.join("|")}`,"setFromString")}},live_cycles:{surface:"settings",setting:{key:"live_cycles",label:"Live cycles",description:"Trailing refresh cycles rendered at full fidelity; older cycles condense to text.",valueConstraint:"integer >= 1",applies:"Applies on the next attach.",group:"display",order:50,format:n(e=>String(e.live_cycles),"format"),cycle:n((e,t)=>{e.live_cycles=c(x,e.live_cycles,t)},"cycle"),setFromString:n((e,t)=>{const o=_(t,"live_cycles");return typeof o=="string"?o:(e.live_cycles=o,null)},"setFromString")}},condensed_history:{surface:"settings",setting:{key:"condensed_history",label:"Condensed history",description:"Messages retained from condensed cycles. Tool calls are never kept.",valueConstraint:u.join("|"),applies:"Applies on the next attach.",group:"display",order:60,format:n(e=>e.condensed_history,"format"),cycle:n((e,t)=>{e.condensed_history=i(u,e.condensed_history,t)},"cycle"),setFromString:n((e,t)=>u.includes(t)?(e.condensed_history=t,null):`condensed_history must be one of: ${u.join("|")}`,"setFromString")}},bash_tool_purpose:{surface:"settings",setting:{key:"bash_tool_purpose",label:"Bash purpose field",description:"Expose the optional purpose label to agents in the bash tool schema. Off removes the field without disabling the bash valve.",valueConstraint:"true|false",applies:"Applies on the next node session.",group:"display",order:25,format:n(e=>e.bash_tool_purpose?"on":"off","format"),cycle:n(e=>{e.bash_tool_purpose=!e.bash_tool_purpose},"cycle"),setFromString:n((e,t)=>{const o=s(t,"bash_tool_purpose");return typeof o=="string"?o:(e.bash_tool_purpose=o,null)},"setFromString")}},fold_finished_tools:{surface:"settings",setting:{key:"fold_finished_tools",label:"Fold finished tools",description:"Fold every settled tool call to its call line. Toggle live with /fold-tools.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:30,format:n(e=>e.fold_finished_tools?"on":"off","format"),cycle:n(e=>{e.fold_finished_tools=!e.fold_finished_tools},"cycle"),setFromString:n((e,t)=>{const o=s(t,"fold_finished_tools");return typeof o=="string"?o:(e.fold_finished_tools=o,null)},"setFromString")}},summarize_tool_calls:{surface:"settings",setting:{key:"summarize_tool_calls",label:"Summarize tool calls",description:"Replace completed folded tool-call groups with a one-line broker summary when available.",valueConstraint:"true|false",applies:"Applies when the next visible message closes a tool-call group.",group:"display",order:35,format:n(e=>e.summarize_tool_calls?"on":"off","format"),cycle:n(e=>{e.summarize_tool_calls=!e.summarize_tool_calls},"cycle"),setFromString:n((e,t)=>{const o=s(t,"summarize_tool_calls");return typeof o=="string"?o:(e.summarize_tool_calls=o,null)},"setFromString")}},detailed_tool_recaps:{surface:"settings",setting:{key:"detailed_tool_recaps",label:"Detailed tool recaps",description:"Show each memory read and edited file in folded tool recaps. Off shows a compact count line instead.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:36,format:n(e=>e.detailed_tool_recaps?"on":"off","format"),cycle:n(e=>{e.detailed_tool_recaps=!e.detailed_tool_recaps},"cycle"),setFromString:n((e,t)=>{const o=s(t,"detailed_tool_recaps");return typeof o=="string"?o:(e.detailed_tool_recaps=o,null)},"setFromString")}},keybindings:{surface:"specialized",owner:"Keybindings tab"},tmux_passthrough:{surface:"specialized",owner:"Keybindings tab"},providerOptions:{surface:"settings",settings:{"openai-codex.fastMode":{key:`providerOptions.${l}.fastMode`,label:"Codex fast mode",description:"Serve Codex turns at the ChatGPT priority tier: about 1.5x faster generation for about 2x the credits. Displayed cost does not reflect the premium.",valueConstraint:"true|false",applies:"Applies on each node's next turn.",group:"providers",order:40,format:n(e=>e.providerOptions[l]?.fastMode?"on":"off","format"),cycle:n(e=>{const t=g(e,l);t.fastMode=!t.fastMode},"cycle"),setFromString:n((e,t)=>{const o=s(t,`providerOptions.${l}.fastMode`);return typeof o=="string"?o:(g(e,l).fastMode=o,null)},"setFromString")}}},modelLadders:{surface:"specialized",owner:"Models tab"},modelRoutes:{surface:"specialized",owner:"Models route declarations"},modelRouting:{surface:"specialized",owner:"Models route declarations"},kinds:{surface:"specialized",owner:"Models tab and kind registry"},page_components:{surface:"specialized",owner:"Page component catalog: product-registered page component kinds"},remoteCanvas:{surface:"specialized",owner:"canvas config commands"},spawnEnv:{surface:"specialized",owner:"spawn environment allow-list"},bin:{surface:"specialized",owner:"bare-binary contributions on every node PATH"},humanActions:{surface:"specialized",owner:"scope-configured human completion actions"}},O=Object.keys(h);function S(){const e=[];for(const t of Object.values(h))t.surface==="settings"&&("setting"in t?e.push(t.setting):e.push(...Object.values(t.settings)));return e.sort((t,o)=>t.order-o.order)}n(S,"collectUserSettings");const y=S();function A(e){return y.find(t=>t.key===e)}n(A,"findUserSetting");function E(e){return y.filter(t=>t.group===e)}n(E,"userSettingsInGroup");export{h as CONFIG_SETTINGS_COVERAGE,O as SCOPE_CONFIG_KEYS,y as USER_SETTINGS,A as findUserSetting,E as userSettingsInGroup};
1
+ var v=Object.defineProperty;var s=(e,t)=>v(e,"name",{value:t,configurable:!0});import{CONDENSED_HISTORY_MODES as u,MOUSE_MODE_DEFAULTS as p,WHIP_MESSAGE_MODES as d}from"../types.js";function i(e,t,o){const r=e.indexOf(t),a=r>=0?r:0;return e[(a+o+e.length)%e.length]}s(i,"cycleChoice");function c(e,t,o){const r=e.indexOf(t);return r>=0?e[(r+o+e.length)%e.length]:o>=0?e.find(a=>a>t)??e[0]:[...e].reverse().find(a=>a<t)??e[e.length-1]}s(c,"cycleNumber");function n(e,t){return e==="true"?!0:e==="false"?!1:`${t} must be true or false`}s(n,"booleanFromString");function f(e,t){if(!/^\d+$/.test(e))return`${t} must be an integer >= 0`;const o=Number(e);return Number.isSafeInteger(o)?o:`${t} must be an integer >= 0`}s(f,"nonNegativeIntegerFromString");function _(e,t){const o=f(e,t);return typeof o=="string"||o<1?`${t} must be an integer >= 1`:o}s(_,"positiveIntegerFromString");const m=[!1,"notify","apply"],w=[0,1,6,12,24,48,72,168],b=[1,2,3,4,5,6,8,10,12],l="openai-codex";function g(e,t){const o=e.providerOptions[t];if(o!==void 0)return o;const r={};return e.providerOptions[t]=r,r}s(g,"providerOptionsFor");const x=[1,2,3,4,5,6],h={schema_version:{surface:"specialized",owner:"internal schema migration"},marketplaces:{surface:"specialized",owner:"Plugins and pkg market commands"},plugins:{surface:"specialized",owner:"Plugins tab and pkg plugin commands"},auto_update:{surface:"settings",settings:{crtr:{key:"auto_update.crtr",label:"crouter updates",description:"Notify about crouter releases, install them automatically, or turn checks off.",valueConstraint:"true|false|notify|apply; true stores notify",applies:"Applies on the next crouter command.",group:"updates",order:70,format:s(e=>e.auto_update.crtr===!1?"off":e.auto_update.crtr,"format"),cycle:s((e,t)=>{e.auto_update.crtr=i(m,e.auto_update.crtr,t)},"cycle"),setFromString:s((e,t)=>{const o=t==="true"?"notify":t;return o!=="notify"&&o!=="apply"&&o!=="false"?"auto_update.crtr must be notify, apply, or false":(e.auto_update.crtr=o==="false"?!1:o,null)},"setFromString")},content:{key:"auto_update.content",label:"Content updates",description:"Notify about plugin and marketplace updates, apply them automatically, or turn checks off.",valueConstraint:"false|notify|apply",applies:"Applies on the next crouter command.",group:"updates",order:80,format:s(e=>e.auto_update.content===!1?"off":e.auto_update.content,"format"),cycle:s((e,t)=>{e.auto_update.content=i(m,e.auto_update.content,t)},"cycle"),setFromString:s((e,t)=>t!=="notify"&&t!=="apply"&&t!=="false"?"auto_update.content must be notify, apply, or false":(e.auto_update.content=t==="false"?!1:t,null),"setFromString")},interval_hours:{key:"auto_update.interval_hours",label:"Update interval",description:"Minimum hours between automatic update checks. Zero checks on every command.",valueConstraint:"integer >= 0",applies:"Applies on the next crouter command.",group:"updates",order:90,format:s(e=>`${e.auto_update.interval_hours}h`,"format"),cycle:s((e,t)=>{e.auto_update.interval_hours=c(w,e.auto_update.interval_hours,t)},"cycle"),setFromString:s((e,t)=>{const o=f(t,"auto_update.interval_hours");return typeof o=="string"?o:(e.auto_update.interval_hours=o,null)},"setFromString")}}},max_panes_per_window:{surface:"settings",setting:{key:"max_panes_per_window",label:"Maximum panes",description:"Maximum human-review panes placed in one tmux window before using another window.",valueConstraint:"integer >= 1",applies:"Applies to the next pane placement.",group:"panes",order:20,format:s(e=>String(e.max_panes_per_window),"format"),cycle:s((e,t)=>{e.max_panes_per_window=c(b,e.max_panes_per_window,t)},"cycle"),setFromString:s((e,t)=>{const o=_(t,"max_panes_per_window");return typeof o=="string"?o:(e.max_panes_per_window=o,null)},"setFromString")}},auto_open_child_viewers:{surface:"settings",setting:{key:"auto_open_child_viewers",label:"Open child panes",description:"Open each managed child beside its watched parent. Off keeps new children running in the background.",valueConstraint:"true|false",applies:"Applies to the next managed child created; independent roots still open their attended viewer.",group:"panes",order:10,format:s(e=>e.auto_open_child_viewers?"on":"off","format"),cycle:s(e=>{e.auto_open_child_viewers=!e.auto_open_child_viewers},"cycle"),setFromString:s((e,t)=>{const o=n(t,"auto_open_child_viewers");return typeof o=="string"?o:(e.auto_open_child_viewers=o,null)},"setFromString")}},brokerThresholds:{surface:"specialized",owner:"Broker limits tab"},lifecycle:{surface:"specialized",owner:"sys config set lifecycle.unattendedParkMs"},completion_bell:{surface:"settings",setting:{key:"completion_bell",label:"Completion bell",description:"Ring a short sound when the node in an attached viewer finishes a turn.",valueConstraint:"true|false",applies:"Applies on the next turn end.",group:"display",order:41,format:s(e=>e.completion_bell?"on":"off","format"),cycle:s(e=>{e.completion_bell=!e.completion_bell},"cycle"),setFromString:s((e,t)=>{const o=n(t,"completion_bell");return typeof o=="string"?o:(e.completion_bell=o,null)},"setFromString")}},working_gerunds:{surface:"specialized",owner:"sys config set working_gerunds"},whip_mode:{surface:"settings",setting:{key:"whip_mode",label:"Whip mode",description:"Play the whip animation when you send a prompt and when a viewer opens for an agent with an initial prompt.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:39,format:s(e=>e.whip_mode?"on":"off","format"),cycle:s(e=>{e.whip_mode=!e.whip_mode},"cycle"),setFromString:s((e,t)=>{const o=n(t,"whip_mode");return typeof o=="string"?o:(e.whip_mode=o,null)},"setFromString")}},page_surface:{surface:"settings",setting:{key:"page_surface",label:"Page human surface",description:"This host reads pages in a browser rather than a terminal. Agents are told the page module runs as written, HTML pages are offered, and document review and live display leave the command tree.",valueConstraint:"true|false",applies:"Applies when human command help is assembled.",group:"display",order:37,format:s(e=>e.page_surface?"on":"off","format"),cycle:s(e=>{e.page_surface=!e.page_surface},"cycle"),setFromString:s((e,t)=>{const o=n(t,"page_surface");return typeof o=="string"?o:(e.page_surface=o,null)},"setFromString")}},whip_messages:{surface:"specialized",owner:"sys config set whip_messages"},whip_message_mode:{surface:"settings",setting:{key:"whip_message_mode",label:"Whip message",description:"What the whip action sends: a random line from whip_messages, or the fixed \u201CGo faster\u201D.",valueConstraint:d.join("|"),applies:"Applies on the next attach.",group:"display",order:40,format:s(e=>e.whip_message_mode==="go-faster"?"go faster":"random","format"),cycle:s((e,t)=>{e.whip_message_mode=i(d,e.whip_message_mode,t)},"cycle"),setFromString:s((e,t)=>d.includes(t)?(e.whip_message_mode=t,null):`whip_message_mode must be one of: ${d.join("|")}`,"setFromString")}},mouse_mode_default:{surface:"settings",setting:{key:"mouse_mode_default",label:"Default mouse mode",description:"Initial mouse wheel scrolling for each attached viewer.",valueConstraint:p.join("|"),applies:"Applies on the next attach.",group:"display",order:38,format:s(e=>e.mouse_mode_default==="tmux"?"follow tmux":e.mouse_mode_default,"format"),cycle:s((e,t)=>{e.mouse_mode_default=i(p,e.mouse_mode_default,t)},"cycle"),setFromString:s((e,t)=>p.includes(t)?(e.mouse_mode_default=t,null):`mouse_mode_default must be one of: ${p.join("|")}`,"setFromString")}},live_cycles:{surface:"settings",setting:{key:"live_cycles",label:"Live cycles",description:"Trailing refresh cycles rendered at full fidelity; older cycles condense to text.",valueConstraint:"integer >= 1",applies:"Applies on the next attach.",group:"display",order:50,format:s(e=>String(e.live_cycles),"format"),cycle:s((e,t)=>{e.live_cycles=c(x,e.live_cycles,t)},"cycle"),setFromString:s((e,t)=>{const o=_(t,"live_cycles");return typeof o=="string"?o:(e.live_cycles=o,null)},"setFromString")}},condensed_history:{surface:"settings",setting:{key:"condensed_history",label:"Condensed history",description:"Messages retained from condensed cycles. Tool calls are never kept.",valueConstraint:u.join("|"),applies:"Applies on the next attach.",group:"display",order:60,format:s(e=>e.condensed_history,"format"),cycle:s((e,t)=>{e.condensed_history=i(u,e.condensed_history,t)},"cycle"),setFromString:s((e,t)=>u.includes(t)?(e.condensed_history=t,null):`condensed_history must be one of: ${u.join("|")}`,"setFromString")}},bash_tool_purpose:{surface:"settings",setting:{key:"bash_tool_purpose",label:"Bash purpose field",description:"Expose the optional purpose label to agents in the bash tool schema. Off removes the field without disabling the bash valve.",valueConstraint:"true|false",applies:"Applies on the next node session.",group:"display",order:25,format:s(e=>e.bash_tool_purpose?"on":"off","format"),cycle:s(e=>{e.bash_tool_purpose=!e.bash_tool_purpose},"cycle"),setFromString:s((e,t)=>{const o=n(t,"bash_tool_purpose");return typeof o=="string"?o:(e.bash_tool_purpose=o,null)},"setFromString")}},fold_finished_tools:{surface:"settings",setting:{key:"fold_finished_tools",label:"Fold finished tools",description:"Fold every settled tool call to its call line. Toggle live with /fold-tools.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:30,format:s(e=>e.fold_finished_tools?"on":"off","format"),cycle:s(e=>{e.fold_finished_tools=!e.fold_finished_tools},"cycle"),setFromString:s((e,t)=>{const o=n(t,"fold_finished_tools");return typeof o=="string"?o:(e.fold_finished_tools=o,null)},"setFromString")}},summarize_tool_calls:{surface:"settings",setting:{key:"summarize_tool_calls",label:"Summarize tool calls",description:"Replace completed folded tool-call groups with a one-line broker summary when available.",valueConstraint:"true|false",applies:"Applies when the next visible message closes a tool-call group.",group:"display",order:35,format:s(e=>e.summarize_tool_calls?"on":"off","format"),cycle:s(e=>{e.summarize_tool_calls=!e.summarize_tool_calls},"cycle"),setFromString:s((e,t)=>{const o=n(t,"summarize_tool_calls");return typeof o=="string"?o:(e.summarize_tool_calls=o,null)},"setFromString")}},detailed_tool_recaps:{surface:"settings",setting:{key:"detailed_tool_recaps",label:"Detailed tool recaps",description:"Show each memory read and edited file in folded tool recaps. Off shows a compact count line instead.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:36,format:s(e=>e.detailed_tool_recaps?"on":"off","format"),cycle:s(e=>{e.detailed_tool_recaps=!e.detailed_tool_recaps},"cycle"),setFromString:s((e,t)=>{const o=n(t,"detailed_tool_recaps");return typeof o=="string"?o:(e.detailed_tool_recaps=o,null)},"setFromString")}},keybindings:{surface:"specialized",owner:"Keybindings tab"},tmux_passthrough:{surface:"specialized",owner:"Keybindings tab"},providerOptions:{surface:"settings",settings:{"openai-codex.fastMode":{key:`providerOptions.${l}.fastMode`,label:"Codex fast mode",description:"Serve Codex turns at the ChatGPT priority tier: about 1.5x faster generation for about 2x the credits. Displayed cost does not reflect the premium.",valueConstraint:"true|false",applies:"Applies on each node's next turn.",group:"providers",order:40,format:s(e=>e.providerOptions[l]?.fastMode?"on":"off","format"),cycle:s(e=>{const t=g(e,l);t.fastMode=!t.fastMode},"cycle"),setFromString:s((e,t)=>{const o=n(t,`providerOptions.${l}.fastMode`);return typeof o=="string"?o:(g(e,l).fastMode=o,null)},"setFromString")}}},modelLadders:{surface:"specialized",owner:"Models tab"},modelRoutes:{surface:"specialized",owner:"Models route declarations"},modelRouting:{surface:"specialized",owner:"Models route declarations"},kinds:{surface:"specialized",owner:"Models tab and kind registry"},page_components:{surface:"specialized",owner:"Page component catalog: product-registered page component kinds"},remoteCanvas:{surface:"specialized",owner:"canvas config commands"},api:{surface:"specialized",owner:"sys connect"},spawnEnv:{surface:"specialized",owner:"spawn environment allow-list"},bin:{surface:"specialized",owner:"bare-binary contributions on every node PATH"},humanActions:{surface:"specialized",owner:"scope-configured human completion actions"}},O=Object.keys(h);function S(){const e=[];for(const t of Object.values(h))t.surface==="settings"&&("setting"in t?e.push(t.setting):e.push(...Object.values(t.settings)));return e.sort((t,o)=>t.order-o.order)}s(S,"collectUserSettings");const y=S();function A(e){return y.find(t=>t.key===e)}s(A,"findUserSetting");function E(e){return y.filter(t=>t.group===e)}s(E,"userSettingsInGroup");export{h as CONFIG_SETTINGS_COVERAGE,O as SCOPE_CONFIG_KEYS,y as USER_SETTINGS,A as findUserSetting,E as userSettingsInGroup};
@@ -1 +1 @@
1
- var A=Object.defineProperty;var r=(e,t)=>A(e,"name",{value:t,configurable:!0});import{canonicalSessionFile as N,findNodeBySessionFile as P,getNode as k,isWarmSpare as R,listNodeIdentities as T,recordSettledTurn as $,setPresence as _,subscriptionsOf as C,updateNode as g}from"../../../core/canvas/canvas.js";import{recordExecutionPid as I}from"../../../core/canvas/executions.js";import{bindSession as j,readLiveSession as q,transferSession as E}from"../../../core/canvas/sessions.js";import{isSafeNodeId as M}from"../../../core/canvas/paths.js";import{isPidAlive as L}from"../../../core/canvas/pid.js";import{editorLabel as B}from"../../../core/canvas/labels.js";import{assembleNodeSubject as G}from"../../../core/substrate/subject.js";import{withCanvasWrite as l}from"../../../core/canvas/db.js";import{notFound as v,usage as a}from"../../../core/errors.js";import{hasUnseenWakeMail as O}from"../../../core/canvas/deliveries.js";import{clearYieldMessage as J}from"../../../core/runtime/kickoff.js";import{transition as b}from"../../../core/runtime/lifecycle.js";import{focusOf as w,tearDownNode as S}from"../../../core/runtime/placement.js";import{hasAttachedBrokerViewers as W}from"../../../core/runtime/broker/client-registry.js";import{evaluateStop as D}from"../../../core/runtime/stop-guard.js";import{clearFault as f,recordFault as U}from"../../../core/canvas/recovery.js";import{updateModelRecipe as V}from"../../../core/runtime/model-swap.js";import{completeCompanionRequestedSubmit as Y}from"../../review/finish.js";import{emitEvent as z}from"../../../core/events/emit.js";import{consumePendingPark as H,recordPendingParkActivity as K}from"../../park-pending.js";import{shouldParkAfterSummary as Q}from"../../park-activity.js";import{requireCurrentExecution as m}from"./broker-recovery.js";function p(e,t){if(typeof e!="object"||e===null||Array.isArray(e))throw a(`${t} requires a JSON body`);return e}r(p,"objectBody");function X(e){const t=p(e,"broker session-bound");if(typeof t.executionId!="string"||t.executionId==="")throw a("executionId must be a non-empty string");if(typeof t.piSessionId!="string"||t.piSessionId==="")throw a("piSessionId must be a non-empty string");if(t.sessionFile!==null&&typeof t.sessionFile!="string")throw a("sessionFile must be a string or null");if(!Number.isSafeInteger(t.pid)||t.pid<=0)throw a("pid must be a positive safe integer");if(t.reason!==null&&typeof t.reason!="string")throw a("reason must be a string or null");if(!Array.isArray(t.reviewBoundaryIds)||!t.reviewBoundaryIds.every(n=>typeof n=="string"))throw a("reviewBoundaryIds must be an array of strings");return t}r(X,"parseSessionBoundBody");function Z(e){const t=p(e,"broker settle");if(typeof t.stopReason!="string")throw a("stopReason must be a string");for(const n of["backgroundJobsRunning","pushedFinal"])if(typeof t[n]!="boolean")throw a(`${n} must be a boolean`);return t}r(Z,"parseSettleBody");function ee(e){const t=p(e,"broker park-complete");if(t.outcome!=="completed"&&t.outcome!=="failed")throw a("outcome must be completed or failed");return t}r(ee,"parseParkCompleteBody");function te(e){const t=p(e,"broker model");if(typeof t.spec!="string"||t.spec.trim()==="")throw a("spec must be a non-empty string");if(t.pinnedOverride!==void 0&&typeof t.pinnedOverride!="boolean")throw a("pinnedOverride must be a boolean when present");if(typeof t.userSelected!="boolean")throw a("userSelected must be a boolean");return t}r(te,"parseModelCommitBody");function h(e){const t=k(e);if(t===null)throw v(`unknown node: ${e}`,{received:e});return t}r(h,"requireNode");function y(e){return{node_id:e.node_id,name:e.name,...e.description===void 0?{}:{description:e.description},...e.title===void 0?{}:{title:e.title},...e.icon===void 0?{}:{icon:e.icon},kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,status:e.status,cwd:e.cwd,parent:e.parent??null,fork_from:e.fork_from??null,profile_id:e.profile_id??null,...e.managed_worktree===void 0?{}:{managed_worktree:e.managed_worktree===null?null:{state:e.managed_worktree.state,...e.managed_worktree.cleanup===void 0?{}:{cleanup:e.managed_worktree.cleanup},path:e.managed_worktree.path,branch:e.managed_worktree.branch,base_ref:e.managed_worktree.base_ref,base_sha:e.managed_worktree.base_sha}},...e.review_binding===void 0?{}:{review_binding:e.review_binding??null},intent:e.intent??null,...e.persona_ack===void 0?{}:{persona_ack:e.persona_ack},created:e.created}}r(y,"extensionNode");function ne(e){const t=h(e),n=[],o=new Set([e]);let i=t.parent??null;for(;i!==null&&i!==""&&!o.has(i);){o.add(i);const c=k(i);if(c===null)break;n.unshift(y(c)),i=c.parent??null}const s=C(e).map(c=>k(c.node_id)).filter(c=>c!==null&&c.kind!=="human").sort((c,x)=>c.status===x.status?0:c.status==="active"?-1:x.status==="active"?1:0).map(y),u=typeof t.fork_source_file=="string"?P(t.fork_source_file):null,d=G(e);if(d===null)throw v(`unknown node: ${e}`,{received:e});return{node:y(t),warm_spare:R(e),ancestors:n,children:s,fork_source:u===null?null:y(u),subject:d,report_nodes:T()}}r(ne,"extensionState");function F(e,t){return e?.kind===t.kind&&e?.mode===t.mode&&e?.lifecycle===t.lifecycle}r(F,"samePersona");function ie(e){const t=p(e,"broker generated-name");if(t.kind!=="initial"&&t.kind!=="recap")throw a("kind must be initial or recap");if(typeof t.description!="string"||typeof t.title!="string"||typeof t.icon!="string")throw a("description, title, and icon must be strings");if(t.kind==="initial")return t;const n=t.expected;if(typeof n!="object"||n===null||Array.isArray(n)||["name","description","title","icon","kind"].some(o=>typeof n[o]!="string"))throw a("recap expected must contain name, description, title, icon, and kind strings");return t}r(ie,"parseGeneratedNameBody");function oe(e){const t=p(e,"broker persona-ack"),n=r(o=>{const i=t[o];if(typeof i!="object"||i===null||Array.isArray(i)||typeof i.kind!="string"||i.mode!=="base"&&i.mode!=="orchestrator"||i.lifecycle!=="terminal"&&i.lifecycle!=="resident")throw a(`${o} must be a persona state`);return i},"parse");return{expected_execution_id:t.expected_execution_id,from:n("from"),to:n("to")}}r(oe,"parsePersonaAckBody");function re(e){const t=e.params.id,n=X(e.body),o=n.sessionFile===null?null:N(n.sessionFile);return{status:200,body:l(()=>{const s=h(t);if(I(n.executionId,t,n.pid)==="shutdown")return{action:"shutdown"};const u=s.review_binding?.review_id;if(u!==void 0&&!n.reviewBoundaryIds.includes(u))return{action:"shutdown"};if(n.reason==="new"&&s.parent==null)return{action:"relaunch_root"};if(n.reason!=="new"&&s.fork_from!=null&&s.pi_session_id==null&&o===null)return{action:"shutdown"};const d=o===null?null:P(o);if(d!==null&&d.node_id!==t){if(L(d.pi_pid))return U(t,{link:"daemon\u2192node",op:"session-file-ownership",kind:"protocol",retry:{disposition:"fatal"},message:`refusing to bind ${o}: it is owned by live node ${d.node_id}`}),{action:"shutdown"};const c=q(d.node_id);if(c===null||E({fromNodeId:d.node_id,fromBindingId:c.bindingId,toNodeId:t,executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})!=="transferred")return{action:"shutdown"}}else if(j(t,{executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})==="shutdown")return{action:"shutdown"};return n.reason!=="new"&&(J(t),s.intent==="refresh"&&b(t,"revive")),{action:"none"}})}}r(re,"handleSessionBound");function se(e,t){const n=k(e);if(n===null){if(t.pushedFinal)return{action:"shutdown"};throw v(`unknown node: ${e}`,{received:e})}if($(e,new Date().toISOString()),t.stopReason==="quit")return(n.status==="active"||n.status==="idle")&&n.intent===null&&(b(e,"finish"),f(e),w(e)!==null&&S(e),_(e,{pane:null,tmux_session:null,window:null})),{action:"shutdown"};if(t.stopReason!=="stop"&&t.stopReason!=="length")return f(e,{link:"daemon\u2192node"}),n.intent==="refresh"?{action:"shutdown"}:{action:"stay_dormant"};const o={pushedFinal:t.pushedFinal};if(o.pushedFinal||n.status==="done")return f(e),w(e)!==null&&S(e),_(e,{pane:null,tmux_session:null,window:null}),{action:"shutdown"};if(n.intent==="refresh")return f(e),{action:"shutdown"};const i=D(e,o,t.backgroundJobsRunning);return f(e),i.action==="reprompt"?{action:"reprompt",prompt:i.message}:i.reason==="finished"?O(e)?{action:"stay_dormant"}:{action:"shutdown"}:i.reason!=="awaiting"&&i.reason!=="scheduled"?{action:"stay_dormant"}:w(e)!==null||W(e)||O(e)?{action:"stay_dormant"}:(b(e,"release"),{action:"shutdown"})}r(se,"settleBroker");function ae(e){const t=e.params.id,n=Z(e.body);m(t,n.expected_execution_id);const o=l(()=>se(t,n));return o.action!=="reprompt"&&Y(t),{status:200,body:o}}r(ae,"handleSettle");function de(e){const t=e.params.id,n=ee(e.body);return m(t,n.expected_execution_id),{status:200,body:l(()=>{const i=H(t);if(i===void 0)return{action:"stay_dormant"};const s=h(t);return Q(t,s,i)?(f(t),b(t,"park"),w(t)!==null&&S(t),_(t,{pane:null,tmux_session:null,window:null}),z({level:n.outcome==="completed"?"info":"warn",event:"broker.park.complete",...M(t)?{node_id:t}:{},outcome:n.outcome==="completed"?"succeeded":"failed"}),{action:"shutdown"}):{action:"stay_dormant"}})}}r(de,"handleParkComplete");function ue(e){const t=e.params.id;return m(t,p(e.body,"broker park-activity").expected_execution_id),{status:200,body:l(()=>({activity:K(t)?"recorded":"none"}))}}r(ue,"handleParkActivity");function ce(e){const t=e.params.id,n=te(e.body);return m(t,n.expected_execution_id),{status:200,body:{modelOverride:l(()=>V(h(t),n.spec,n.pinnedOverride,n.userSelected)).model_override}}}r(ce,"handleModelCommit");function le(e){return{status:200,body:ne(e.params.id)}}r(le,"handleExtensionState");function pe(e){const t=e.params.id,n=ie(e.body);return m(t,n.expected_execution_id),{status:200,body:l(()=>{const i=h(t);if(n.kind==="initial"){if((i.description??"").trim()!=="")return{applied:!1};const d=g(t,{description:n.description,...n.title.trim()===""?{}:{title:n.title},...n.icon.trim()===""?{}:{icon:n.icon}});return{applied:!0,editorLabel:B(d),description:d.description??"",title:d.title??"",icon:d.icon??""}}const s=n.expected;if(i.name!==s.name||(i.description??"")!==s.description||(i.title??"")!==s.title||(i.icon??"")!==s.icon||i.kind!==s.kind)return{applied:!1};const u=g(t,{description:n.description,title:n.title.trim()===""?void 0:n.title,icon:n.icon.trim()===""?void 0:n.icon});return{applied:!0,editorLabel:B(u),description:u.description??"",title:u.title??"",icon:u.icon??""}})}}r(pe,"handleGeneratedName");function fe(e){const t=e.params.id,n=oe(e.body);return m(t,n.expected_execution_id),{status:200,body:l(()=>{const i=h(t),s={kind:i.kind,mode:i.mode,lifecycle:i.lifecycle},u=i.persona_ack,d=u===void 0?s:{kind:u.kind??i.kind,mode:u.mode,lifecycle:u.lifecycle};return!F(s,n.to)||!F(d,n.from)?{applied:!1}:(g(t,{persona_ack:n.to}),{applied:!0})})}}r(fe,"handlePersonaAck");const qe=[{method:"POST",pattern:"/v1/nodes/:id/broker/session-bound",handler:re},{method:"POST",pattern:"/v1/nodes/:id/broker/settle",handler:ae},{method:"POST",pattern:"/v1/nodes/:id/broker/park-complete",handler:de},{method:"POST",pattern:"/v1/nodes/:id/broker/park-activity",handler:ue},{method:"POST",pattern:"/v1/nodes/:id/broker/model",handler:ce},{method:"GET",pattern:"/v1/nodes/:id/broker/extension-state",handler:le},{method:"POST",pattern:"/v1/nodes/:id/broker/generated-name",handler:pe},{method:"POST",pattern:"/v1/nodes/:id/broker/persona-ack",handler:fe}];export{qe as brokerOperationRoutes};
1
+ var F=Object.defineProperty;var r=(t,e)=>F(t,"name",{value:e,configurable:!0});import{canonicalSessionFile as A,findNodeBySessionFile as P,getNode as k,isWarmSpare as T,listNodeIdentities as I,recordSettledTurn as R,setPresence as w,subscriptionsOf as $,updateNode as g,updateNodeTelemetry as q}from"../../../core/canvas/canvas.js";import{recordExecutionPid as C}from"../../../core/canvas/executions.js";import{bindSession as j,readLiveSession as E,transferSession as M}from"../../../core/canvas/sessions.js";import{isSafeNodeId as D}from"../../../core/canvas/paths.js";import{isPidAlive as L}from"../../../core/canvas/pid.js";import{editorLabel as N}from"../../../core/canvas/labels.js";import{assembleNodeSubject as G}from"../../../core/substrate/subject.js";import{withCanvasWrite as l}from"../../../core/canvas/db.js";import{notFound as v,usage as s}from"../../../core/errors.js";import{hasUnseenWakeMail as O}from"../../../core/canvas/deliveries.js";import{clearYieldMessage as J}from"../../../core/runtime/kickoff.js";import{transition as h}from"../../../core/runtime/lifecycle.js";import{focusOf as b,tearDownNode as x}from"../../../core/runtime/placement.js";import{hasAttachedBrokerViewers as W}from"../../../core/runtime/broker/client-registry.js";import{evaluateStop as U}from"../../../core/runtime/stop-guard.js";import{clearFault as m,recordFault as V}from"../../../core/canvas/recovery.js";import{updateModelRecipe as Y}from"../../../core/runtime/model-swap.js";import{completeCompanionRequestedSubmit as z}from"../../review/finish.js";import{emitEvent as H}from"../../../core/events/emit.js";import{consumePendingPark as K,recordPendingParkActivity as Q}from"../../park-pending.js";import{shouldParkAfterSummary as X}from"../../park-activity.js";import{requireCurrentExecution as f}from"./broker-recovery.js";function p(t,e){if(typeof t!="object"||t===null||Array.isArray(t))throw s(`${e} requires a JSON body`);return t}r(p,"objectBody");function Z(t){const e=p(t,"broker session-bound");if(typeof e.executionId!="string"||e.executionId==="")throw s("executionId must be a non-empty string");if(typeof e.piSessionId!="string"||e.piSessionId==="")throw s("piSessionId must be a non-empty string");if(e.sessionFile!==null&&typeof e.sessionFile!="string")throw s("sessionFile must be a string or null");if(!Number.isSafeInteger(e.pid)||e.pid<=0)throw s("pid must be a positive safe integer");if(e.reason!==null&&typeof e.reason!="string")throw s("reason must be a string or null");if(!Array.isArray(e.reviewBoundaryIds)||!e.reviewBoundaryIds.every(n=>typeof n=="string"))throw s("reviewBoundaryIds must be an array of strings");return e}r(Z,"parseSessionBoundBody");function ee(t){const e=p(t,"broker settle");if(typeof e.stopReason!="string")throw s("stopReason must be a string");for(const n of["backgroundJobsRunning","pushedFinal"])if(typeof e[n]!="boolean")throw s(`${n} must be a boolean`);return e}r(ee,"parseSettleBody");function te(t){const e=p(t,"broker park-complete");if(e.outcome!=="completed"&&e.outcome!=="failed")throw s("outcome must be completed or failed");return e}r(te,"parseParkCompleteBody");function ne(t){const e=p(t,"broker telemetry");if(typeof e.expected_execution_id!="string"||e.expected_execution_id==="")throw s("expected_execution_id must be a non-empty string");if(typeof e.tokens_in!="number"||!Number.isFinite(e.tokens_in)||e.tokens_in<0)throw s("tokens_in must be a non-negative finite number");if(e.context_tokens!==null&&(typeof e.context_tokens!="number"||!Number.isFinite(e.context_tokens)||e.context_tokens<0))throw s("context_tokens must be a non-negative finite number or null");if(e.last_activity!==null&&typeof e.last_activity!="string")throw s("last_activity must be a string or null");if(typeof e.updated_at!="string"||Number.isNaN(Date.parse(e.updated_at)))throw s("updated_at must be an ISO timestamp");return{...e,updated_at:new Date(e.updated_at).toISOString()}}r(ne,"parseTelemetryBody");function ie(t){const e=p(t,"broker model");if(typeof e.spec!="string"||e.spec.trim()==="")throw s("spec must be a non-empty string");if(e.pinnedOverride!==void 0&&typeof e.pinnedOverride!="boolean")throw s("pinnedOverride must be a boolean when present");if(typeof e.userSelected!="boolean")throw s("userSelected must be a boolean");return e}r(ie,"parseModelCommitBody");function _(t){const e=k(t);if(e===null)throw v(`unknown node: ${t}`,{received:t});return e}r(_,"requireNode");function y(t){return{node_id:t.node_id,name:t.name,...t.description===void 0?{}:{description:t.description},...t.title===void 0?{}:{title:t.title},...t.icon===void 0?{}:{icon:t.icon},kind:t.kind,mode:t.mode,lifecycle:t.lifecycle,status:t.status,cwd:t.cwd,parent:t.parent??null,fork_from:t.fork_from??null,profile_id:t.profile_id??null,...t.managed_worktree===void 0?{}:{managed_worktree:t.managed_worktree===null?null:{state:t.managed_worktree.state,...t.managed_worktree.cleanup===void 0?{}:{cleanup:t.managed_worktree.cleanup},path:t.managed_worktree.path,branch:t.managed_worktree.branch,base_ref:t.managed_worktree.base_ref,base_sha:t.managed_worktree.base_sha}},...t.review_binding===void 0?{}:{review_binding:t.review_binding??null},intent:t.intent??null,...t.persona_ack===void 0?{}:{persona_ack:t.persona_ack},created:t.created}}r(y,"extensionNode");function oe(t){const e=_(t),n=[],o=new Set([t]);let i=e.parent??null;for(;i!==null&&i!==""&&!o.has(i);){o.add(i);const c=k(i);if(c===null)break;n.unshift(y(c)),i=c.parent??null}const a=$(t).map(c=>k(c.node_id)).filter(c=>c!==null&&c.kind!=="human").sort((c,S)=>c.status===S.status?0:c.status==="active"?-1:S.status==="active"?1:0).map(y),u=typeof e.fork_source_file=="string"?P(e.fork_source_file):null,d=G(t);if(d===null)throw v(`unknown node: ${t}`,{received:t});return{node:y(e),warm_spare:T(t),ancestors:n,children:a,fork_source:u===null?null:y(u),subject:d,report_nodes:I()}}r(oe,"extensionState");function B(t,e){return t?.kind===e.kind&&t?.mode===e.mode&&t?.lifecycle===e.lifecycle}r(B,"samePersona");function re(t){const e=p(t,"broker generated-name");if(e.kind!=="initial"&&e.kind!=="recap")throw s("kind must be initial or recap");if(typeof e.description!="string"||typeof e.title!="string"||typeof e.icon!="string")throw s("description, title, and icon must be strings");if(e.kind==="initial")return e;const n=e.expected;if(typeof n!="object"||n===null||Array.isArray(n)||["name","description","title","icon","kind"].some(o=>typeof n[o]!="string"))throw s("recap expected must contain name, description, title, icon, and kind strings");return e}r(re,"parseGeneratedNameBody");function se(t){const e=p(t,"broker persona-ack"),n=r(o=>{const i=e[o];if(typeof i!="object"||i===null||Array.isArray(i)||typeof i.kind!="string"||i.mode!=="base"&&i.mode!=="orchestrator"||i.lifecycle!=="terminal"&&i.lifecycle!=="resident")throw s(`${o} must be a persona state`);return i},"parse");return{expected_execution_id:e.expected_execution_id,from:n("from"),to:n("to")}}r(se,"parsePersonaAckBody");function ae(t){const e=t.params.id,n=Z(t.body),o=n.sessionFile===null?null:A(n.sessionFile);return{status:200,body:l(()=>{const a=_(e);if(C(n.executionId,e,n.pid)==="shutdown")return{action:"shutdown"};const u=a.review_binding?.review_id;if(u!==void 0&&!n.reviewBoundaryIds.includes(u))return{action:"shutdown"};if(n.reason==="new"&&a.parent==null)return{action:"relaunch_root"};if(n.reason!=="new"&&a.fork_from!=null&&a.pi_session_id==null&&o===null)return{action:"shutdown"};const d=o===null?null:P(o);if(d!==null&&d.node_id!==e){if(L(d.pi_pid))return V(e,{link:"daemon\u2192node",op:"session-file-ownership",kind:"protocol",retry:{disposition:"fatal"},message:`refusing to bind ${o}: it is owned by live node ${d.node_id}`}),{action:"shutdown"};const c=E(d.node_id);if(c===null||M({fromNodeId:d.node_id,fromBindingId:c.bindingId,toNodeId:e,executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})!=="transferred")return{action:"shutdown"}}else if(j(e,{executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})==="shutdown")return{action:"shutdown"};return n.reason!=="new"&&(J(e),a.intent==="refresh"&&h(e,"revive")),{action:"none"}})}}r(ae,"handleSessionBound");function de(t,e){const n=k(t);if(n===null){if(e.pushedFinal)return{action:"shutdown"};throw v(`unknown node: ${t}`,{received:t})}if(R(t,new Date().toISOString()),e.stopReason==="quit")return(n.status==="active"||n.status==="idle")&&n.intent===null&&(h(t,"finish"),m(t),b(t)!==null&&x(t),w(t,{pane:null,tmux_session:null,window:null})),{action:"shutdown"};if(e.stopReason!=="stop"&&e.stopReason!=="length")return m(t,{link:"daemon\u2192node"}),n.intent==="refresh"?{action:"shutdown"}:{action:"stay_dormant"};const o={pushedFinal:e.pushedFinal};if(o.pushedFinal||n.status==="done")return m(t),b(t)!==null&&x(t),w(t,{pane:null,tmux_session:null,window:null}),{action:"shutdown"};if(n.intent==="refresh")return m(t),{action:"shutdown"};const i=U(t,o,e.backgroundJobsRunning);return m(t),i.action==="reprompt"?{action:"reprompt",prompt:i.message}:i.reason==="finished"?O(t)?{action:"stay_dormant"}:{action:"shutdown"}:i.reason!=="awaiting"&&i.reason!=="scheduled"?{action:"stay_dormant"}:b(t)!==null||W(t)||O(t)?{action:"stay_dormant"}:(h(t,"release"),{action:"shutdown"})}r(de,"settleBroker");function ue(t){const e=t.params.id,n=ee(t.body);f(e,n.expected_execution_id);const o=l(()=>de(e,n));return o.action!=="reprompt"&&z(e),{status:200,body:o}}r(ue,"handleSettle");function ce(t){const e=t.params.id,n=te(t.body);return f(e,n.expected_execution_id),{status:200,body:l(()=>{const i=K(e);if(i===void 0)return{action:"stay_dormant"};const a=_(e);return X(e,a,i)?(m(e),h(e,"park"),b(e)!==null&&x(e),w(e,{pane:null,tmux_session:null,window:null}),H({level:n.outcome==="completed"?"info":"warn",event:"broker.park.complete",...D(e)?{node_id:e}:{},outcome:n.outcome==="completed"?"succeeded":"failed"}),{action:"shutdown"}):{action:"stay_dormant"}})}}r(ce,"handleParkComplete");function le(t){const e=t.params.id;return f(e,p(t.body,"broker park-activity").expected_execution_id),{status:200,body:l(()=>({activity:Q(e)?"recorded":"none"}))}}r(le,"handleParkActivity");function pe(t){const e=t.params.id,n=ne(t.body);return f(e,n.expected_execution_id),l(()=>{q(e,{tokensIn:n.tokens_in,contextTokens:n.context_tokens,lastActivity:n.last_activity,updatedAt:n.updated_at})}),{status:204}}r(pe,"handleTelemetry");function fe(t){const e=t.params.id,n=ie(t.body);return f(e,n.expected_execution_id),{status:200,body:{modelOverride:l(()=>Y(_(e),n.spec,n.pinnedOverride,n.userSelected)).model_override}}}r(fe,"handleModelCommit");function me(t){return{status:200,body:oe(t.params.id)}}r(me,"handleExtensionState");function _e(t){const e=t.params.id,n=re(t.body);return f(e,n.expected_execution_id),{status:200,body:l(()=>{const i=_(e);if(n.kind==="initial"){if((i.description??"").trim()!=="")return{applied:!1};const d=g(e,{description:n.description,...n.title.trim()===""?{}:{title:n.title},...n.icon.trim()===""?{}:{icon:n.icon}});return{applied:!0,editorLabel:N(d),description:d.description??"",title:d.title??"",icon:d.icon??""}}const a=n.expected;if(i.name!==a.name||(i.description??"")!==a.description||(i.title??"")!==a.title||(i.icon??"")!==a.icon||i.kind!==a.kind)return{applied:!1};const u=g(e,{description:n.description,title:n.title.trim()===""?void 0:n.title,icon:n.icon.trim()===""?void 0:n.icon});return{applied:!0,editorLabel:N(u),description:u.description??"",title:u.title??"",icon:u.icon??""}})}}r(_e,"handleGeneratedName");function ke(t){const e=t.params.id,n=se(t.body);return f(e,n.expected_execution_id),{status:200,body:l(()=>{const i=_(e),a={kind:i.kind,mode:i.mode,lifecycle:i.lifecycle},u=i.persona_ack,d=u===void 0?a:{kind:u.kind??i.kind,mode:u.mode,lifecycle:u.lifecycle};return!B(a,n.to)||!B(d,n.from)?{applied:!1}:(g(e,{persona_ack:n.to}),{applied:!0})})}}r(ke,"handlePersonaAck");const De=[{method:"POST",pattern:"/v1/nodes/:id/broker/session-bound",handler:ae},{method:"POST",pattern:"/v1/nodes/:id/broker/settle",handler:ue},{method:"POST",pattern:"/v1/nodes/:id/broker/park-complete",handler:ce},{method:"POST",pattern:"/v1/nodes/:id/broker/park-activity",handler:le},{method:"POST",pattern:"/v1/nodes/:id/broker/telemetry",handler:pe},{method:"POST",pattern:"/v1/nodes/:id/broker/model",handler:fe},{method:"GET",pattern:"/v1/nodes/:id/broker/extension-state",handler:me},{method:"POST",pattern:"/v1/nodes/:id/broker/generated-name",handler:_e},{method:"POST",pattern:"/v1/nodes/:id/broker/persona-ack",handler:ke}];export{De as brokerOperationRoutes};