@north-light/cli 0.3.458 → 0.3.460
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/profiles.d.ts +3 -1
- package/dist/builtin-memory/crouter-sdk/guides/apps-on-northlight.md +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +12 -0
- package/dist/clients/attach/session/profile-files.js +1 -1
- package/dist/clients/attach/viewer.js +178 -178
- package/dist/commands/profile/new.js +1 -1
- package/dist/commands/profile/show.js +1 -1
- package/dist/commands/sys/join.js +1 -1
- package/dist/commands/sys/panels/models-panel.js +1 -1
- package/dist/core/command-plugins/compose.js +1 -1
- package/dist/core/config.js +1 -1
- package/dist/core/directory/join.d.ts +4 -2
- package/dist/core/directory/join.js +1 -1
- package/dist/core/io.d.ts +12 -2
- package/dist/core/io.js +7 -7
- package/dist/core/layout-migrate/stored-paths.js +3 -3
- package/dist/core/layout-migrate/stores.js +1 -1
- package/dist/core/layout.d.ts +2 -2
- package/dist/core/layout.js +2 -2
- package/dist/core/profiles/manifest.d.ts +1 -1
- package/dist/core/profiles/manifest.js +1 -1
- package/dist/core/profiles/relocate.d.ts +8 -0
- package/dist/core/profiles/relocate.js +1 -0
- package/dist/core/providers/device.js +1 -1
- package/dist/core/render.d.ts +1 -1
- package/dist/core/render.js +6 -6
- package/dist/core/scoped-state/migrate.js +1 -1
- package/dist/core/scoped-state/profiles.js +1 -1
- package/dist/core/scoped-state/settings.js +1 -1
- package/dist/core/spaces/space.js +2 -2
- package/dist/core/storage/download.js +1 -1
- package/dist/daemon/api/map.js +2 -2
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/crtrd.js +8 -7
- package/dist/migrations/004-canvas-documents/index.js +2 -2
- package/dist/migrations/004-canvas-documents/legacy/types.d.ts +1 -1
- package/docs/sdk/guides/apps-on-northlight.md +1 -1
- package/docs/sdk/release-notes.md +12 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
var l=Object.defineProperty;var
|
|
1
|
+
var l=Object.defineProperty;var i=(e,t)=>l(e,"name",{value:t,configurable:!0});import{defineLeaf as p}from"../../core/command.js";import{usage as n}from"../../core/errors.js";import{resolve as f}from"node:path";import{cliClient as s}from"../api-client.js";import{resolveMetaPairs as c}from"./meta.js";const k=p({name:"new",description:"create a new profile",whenToUse:"you are establishing a new agent identity \u2014 a stable id, a display name, and its own documents (a profile owns documents like any other owner). Pass at most one initial project directory here (the parser has no repeatable-flag support); use `profile project add` afterward for every additional one. Use `--root` (no name) to provision the canonical stable root profile \u2014 the default identity a home falls back to \u2014 idempotently, without spawning a node.",help:{name:"profile new",summary:"create a profile of the caller's app in the scoped store, with its own app directory",params:[{kind:"flag",name:"name",type:"string",required:!1,constraint:"Display name for the profile. Also slugified into the stable profile id \u2014 the id never changes on later rename. Required unless `--root` is given; mutually exclusive with `--root`."},{kind:"flag",name:"root",type:"bool",required:!1,constraint:"Provision the canonical stable root profile (fixed id `root-00000000`, name `root`) \u2014 the fallback default identity for a home \u2014 instead of a fresh named one. Idempotent: re-running returns the existing root. Node-free (no `node new --root` needed). Mutually exclusive with `--name`/`--project`/`--default-kind`/`--meta`."},{kind:"flag",name:"project",type:"path",required:!1,constraint:"One initial project directory. Must already exist and be a directory; resolved to its absolute real path before storing. Creates a purview relationship. Singular \u2014 add more with `crtr profile project add` after creation."},{kind:"flag",name:"default-kind",type:"string",required:!1,constraint:"Installed persona kind for node creates under this profile that omit --kind. Defaults to general when omitted; the daemon validates it against its installed kind registry."},{kind:"flag",name:"meta",type:"string",required:!1,repeatable:!0,constraint:"KEY=VALUE metadata entry (repeat per entry). KEY starts with a letter or digit, followed by letters, digits, `_`, or `-`. Each entry reaches every broker launched under the profile as `CRTR_PROFILE_META_<KEY>` env; readable back via `profile show` \u2014 not for secrets (those go to `profile env`)."}],output:[{name:"profile_id",type:"string",required:!0,constraint:"Stable directory id (`<slug>-<shortid>`). Pass this, or the name, to every other `profile` verb and to `--profile` elsewhere."},{name:"profile_path",type:"string",required:!0,constraint:"Absolute path to the profile directory, `<app space>/profiles/<id>`: its settings root. Documents the profile owns are canvas documents, read with `crtr canvas read`, not files here."},{name:"projects",type:"ProfileProject[]",required:!0,constraint:"Explicit `{path}` project relationships in manifest order. `path` defines purview. Empty when --project was omitted."},{name:"app",type:"string",required:!0,constraint:"The app that owns this profile."},{name:"default_kind",type:"string",required:!0,constraint:"Default persona kind for node creates that omit --kind; `general` when none is stored."},{name:"metadata",type:"object",required:!0,constraint:"Stored metadata map; empty when --meta was omitted."},{name:"created_at",type:"string",required:!0,constraint:"ISO 8601 creation timestamp."}],outputKind:"object",effects:["Creates a profile row owned by the caller's app (the terminal app for a person) and its directory under that app's space. A name the caller's app already holds is refused."]},run:i(async e=>{const t=e.name,r=e.project,a=e.defaultKind,o=c(e.meta,"meta");if(e.root===!0){if(t!==void 0||r!==void 0||a!==void 0||Object.keys(o).length>0)throw n("`--root` provisions the canonical stable root profile and takes no `--name`/`--project`/`--default-kind`/`--meta`.");return d(await s().ensureProfile("root"))}if(t===void 0)throw n("provide `--name <name>` to create a profile, or `--root` to provision the stable root profile.");return d(await s().createProfile({name:t,...r!==void 0?{projects:[{path:f(r)}]}:{},...a!==void 0?{default_kind:a}:{},...Object.keys(o).length>0?{metadata:o}:{}}))},"run")});function d(e){return{profile_id:e.id,profile_path:e.profile_path??e.memory_path,projects:e.projects,app:e.app,default_kind:e.default_kind,metadata:e.metadata,created_at:e.created_at}}i(d,"newProfileOutput");export{k as newLeaf};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var a=Object.defineProperty;var i=(r,e)=>a(r,"name",{value:e,configurable:!0});import{defineLeaf as n}from"../../core/command.js";import{resolveProfileViaDaemon as o}from"./daemon-profiles.js";const l=n({name:"show",description:"one profile's manifest in full",whenToUse:"you need a profile's full project list, owning app, id, and timestamps \u2014 after `profile list` narrows to a name, to find where its nodes will run, or to inspect what a node's bearings named as its profile.",help:{name:"profile show",summary:"print one profile's settings and directory path",params:[{kind:"positional",name:"profile",required:!0,constraint:"Profile id or manifest name; a close-enough name resolves to the nearest profile, reported on stderr. A name shared by several profiles fails listing every matching id."}],output:[{name:"profile_id",type:"string",required:!0,constraint:"Stable directory id."},{name:"name",type:"string",required:!0,constraint:"Manifest name."},{name:"projects",type:"ProfileProject[]",required:!0,constraint:"Explicit `{path}` relationships in manifest order. `path` defines profile purview."},{name:"app",type:"string",required:!0,constraint:"The app that owns this profile."},{name:"paused_at",type:"string | null",required:!0,constraint:"ISO 8601 pause timestamp, or null when the profile is active."},{name:"default_kind",type:"string",required:!0,constraint:"Default node kind for creates that omit --kind; `general` when none is stored."},{name:"metadata",type:"object",required:!0,constraint:"Stored metadata map \u2014 each entry reaches brokers launched under the profile as `CRTR_PROFILE_META_<KEY>` env. Empty when none."},{name:"created_at",type:"string",required:!0,constraint:"ISO 8601 creation timestamp."},{name:"last_used_at",type:"string | null",required:!0,constraint:"ISO 8601 timestamp of the most recent selection, or null if never selected."},{name:"profile_path",type:"string",required:!0,constraint:"Absolute path to the profile directory, `<app space>/profiles/<id>`: its settings root. Documents the profile owns are canvas documents, read with `crtr canvas read`, not files here."},{name:"follow_up",type:"string",required:!0,constraint:"Concrete next commands, including the SQL-backed raw settings surface."}],outputKind:"object",effects:["None. Read-only."]},run:i(async r=>{const e=await o(r.profile),t=e.id;return{profile_id:t,name:e.name,projects:e.projects,app:e.app,paused_at:e.paused_at,default_kind:e.default_kind,metadata:e.metadata,created_at:e.created_at,last_used_at:e.last_used_at,profile_path:e.profile_path??e.memory_path,follow_up:e.paused_at===null?`Inspect its SQL-backed settings with \`crtr sys config get-raw --scope profile --profile ${t}\`; manage its purview with \`crtr profile project add ${t} --dir <path>\`, pause it with \`crtr profile pause ${t}\`, or set its default kind with \`crtr profile kind ${t} <kind>\`.`:`Inspect its SQL-backed settings with \`crtr sys config get-raw --scope profile --profile ${t}\`; manage its purview with \`crtr profile project add ${t} --dir <path>\`, or resume it with \`crtr profile resume ${t}\`.`}},"run")});export{l as showLeaf};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var s=Object.defineProperty;var n=(e,r)=>s(e,"name",{value:r,configurable:!0});import{defineLeaf as o}from"../../core/command.js";import{existsSync as a}from"node:fs";import{join as d}from"node:path";import{pluginsDir as l}from"../../core/scope.js";import{joinDirectory as u}from"../../core/directory/join.js";import{apiSocketPath as c}from"../../core/canvas/paths.js";import{listProcessTable as m}from"../../core/canvas/pid.js";import{daemonAnswers as h}from"../../core/layout-migrate/index.js";import{envNodeId as p}from"../../shared/env.js";import{isDaemonRunning as f}from"../../daemon/pidfile.js";import{findDaemonPids as w,spawnDaemon as y,stopAllDaemons as g}from"../../daemon/manage.js";function k(){const e=m();if(e===null||(p()??"")!=="")return!0;const r=new Map(e.map(t=>[t.pid,t])),i=new Set;for(let t=process.ppid;t>1&&!i.has(t);t=r.get(t)?.ppid??0)if(i.add(t),r.get(t)?.command.includes("broker-cli"))return!0;return!1}n(k,"insideBroker");function b(){if(!(process.platform==="linux"&&a("/run/crtr/launcher.sock")))return{running:n(async()=>f()||w().length>0||await h(c()),"running"),stop:n(async()=>{await g()},"stop"),start:n(async()=>{await y()},"start"),firstSync:n(async()=>{const e=l("user"),r=Date.now()+2e4;for(;e&&Date.now()<r&&!a(d(e,"cloud","commands.json"));)await new Promise(i=>setTimeout(i,250))},"firstSync"),insideNode:k}}n(b,"daemonControl");const T=o({name:"join",description:"register this runtime with its directory using an enrollment token, offline",whenToUse:"on your own computer, with a token from `<issuer>/account/machines`: run it from a plain terminal, with any viewers open. It stops the daemon, joins, and starts it again. Also at a hosted runtime's first boot, before the daemon starts, with the enrollment token provisioning passed in.",help:{name:"sys join",summary:"exchange a directory enrollment token for a new runtime client key and record the returned identity",guide:"Generates a new ES256 client key, sends only its public key to `PUT <directory>/v1/runtimes/registration` with the token as bearer, and learns the runtime id, client id, issuer, owner and URL from the answer. Only after that answer does it write the key file, set the user settings `runtime.issuer` and `runtime.audience`, and rewrite `user:local` profile grantees to `app:terminal`. canvas.db belongs to the daemon: its next start rewrites `user:local` there, before it serves anything. On your own computer a running daemon is no obstacle: join takes a `join.lock` in the runtime directory (so no open viewer's redial can autostart a daemon while it works), registers with the directory while the daemon still serves, stops the daemon, writes the identity, and starts the daemon again, whose start applies the canvas.db rewrite. Run it from a plain terminal, not from inside a node: a node's broker dies with the daemon, so join refuses `join_inside_node` instead. Where the launcher owns the daemon (a hosted container) join keeps the offline contract and refuses `daemon_running`. Presenting the token of the stored successful registration again finishes those local writes without calling the directory. Any other token generates a new key and registers again.",params:[{kind:"flag",name:"directory",type:"string",required:!0,constraint:"Directory base URL (http or https) that minted the token."},{kind:"flag",name:"token",type:"string",required:!0,sensitive:!0,constraint:"Single-use enrollment token. Sensitive: never echoed in output or errors."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Label shown for this machine on the account page. Default: this computer's hostname."}],output:[{name:"runtime",type:"string",required:!0,constraint:"Runtime principal from the directory, `runtime:<id>`."},{name:"client_id",type:"string",required:!0,constraint:"OAuth client id registered for the new key."},{name:"issuer",type:"string",required:!0,constraint:"Canonical issuer, written to `runtime.issuer`."},{name:"owner",type:"string",required:!0,constraint:"Owner principal, `user:<id>`, that replaces `user:local` subjects."},{name:"canvas_rewrite",type:"string",required:!0,constraint:"Always `next daemon start`: when canvas.db identity fields are rewritten."},{name:"url",type:"string",required:!0,constraint:"Runtime URL, written to `runtime.audience`."},{name:"registered",type:"boolean",required:!0,constraint:"False when the token matched the stored registration and the directory was not called."},{name:"daemon",type:"string",required:!0,constraint:"`restarted` (a daemon was stopped and started on the joined settings), `started` (none was running; join started one and waited, up to 20s, for its first grant sync to install the account's provider plugins, so `crtr cloud \u2026` resolves), `not_running` (none was started: a hosted container's launcher owns its daemon), or `restart_failed` (joined, but the new daemon did not start: see `daemon_error`, then run `crtr sys daemon start`)."},{name:"daemon_error",type:"string",required:!1,constraint:"Why the new daemon did not start, with `daemon: restart_failed`."}],outputKind:"object",effects:["Holds `<runtime>/join.lock` (its pid) from the start until the daemon is back; `ensureDaemon` and `sys daemon start` (without `--force`) decline to start a daemon while a live process holds it (including a start already in flight when the join began). A lock whose holder died is reclaimed.","A running daemon is stopped (SIGTERM, then SIGKILL, plus strays) after the directory accepts the registration and before the local writes, then started again. Every broker dies with it and is revived by the new daemon. If the join fails after the stop, the daemon is started again anyway. With no daemon running join starts one anyway, because a bare `crtr cloud \u2026` is an unknown command (it never autostarts a daemon) until a daemon's grant sync has installed the provider plugins. Refuses `join_inside_node` when run from inside a node and a daemon is running; refuses `daemon_running` where a container launcher owns the daemon.","On a new token: calls the directory once; on success writes `<runtime>/secrets/runtime-client.json` (mode 0600) with the private key, registration and the token's SHA-256, replacing any earlier key file. A refused or failed call writes nothing.","Sends name, platform, crtr version, installed plugin ids and sandbox state with the public key. Before the request it writes `runtime-client.pending.json` (mode 0600); a rerun with the same token and directory reuses that key, so a lost reply is retried safely. The file is deleted on success or a refusal.","Sets `runtime.issuer` and `runtime.audience` in user settings. When the directory says this machine is not the account's default (`is_default: false`) it replaces the whole `runtime` setting with `{issuer, audience, unsandboxed: true}`: no app listener, and only `app:terminal` nodes run.","Rewrites `user:local` profile grantees to `app:terminal`.","At every later daemon start, before any listener: exact `user:local` subjects in canvas.db nodes and node_tokens become the owner (as do missing subjects on `app:terminal` nodes), `user:local` grantees in nodes, run_events, idempotency_keys and node_tokens become `app:terminal`, references to the unjoined runtime's `local:terminal` grant are cleared, and that local grant mirror is deleted so grant sync installs the directory's. Application grantees and free text are unchanged; a start with nothing left to rewrite changes no rows."]},run:n(async e=>({...await u(e.directory,e.token,e.name||void 0,b()),canvas_rewrite:"next daemon start"}),"run")});export{T as sysJoinLeaf};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var ge=Object.defineProperty;var v=(p,e)=>ge(p,"name",{value:e,configurable:!0});import{envProfileId as pe}from"../../../shared/env.js";import{ModelSelectorComponent as $e,ThinkingSelectorComponent as ee}from"@earendil-works/pi-coding-agent";import{Input as ye,truncateToWidth as V}from"@earendil-works/pi-tui";import{basename as ve,dirname as ke,join as S}from"node:path";import{ANTHROPIC_PROVIDER_ID as be,OPENAI_CODEX_PROVIDER_ID as Re,isManagedProvider as we,readSubscriptionPool as xe}from"../../../core/subscription-state.js";import{readRawConfigAtRoot as P,persistDefaultKindModel as Me,persistModelLadders as te,persistModelRoute as ie,readMergedLaunchConfig as re}from"../../../core/config.js";import{findProjectScopeRoots as Ie,userScopeRoot as Se}from"../../../core/scope.js";import{loadProfileManifest as Pe,profileMemoryDir as Ce}from"../../../core/profiles/manifest.js";import{configuredLadderProviders as Te,inferPortableModelSelection as q,MODEL_PROVIDER_KEYS as oe,MODEL_STRENGTHS as k,THINKING_SUFFIXES as se}from"../../../core/runtime/model-selection.js";import{fitTail as U,padAnsi as f,renderRows as Fe,theme as o,wrapText as Oe}from"../../../core/tui/panel.js";const ne=3,Le={anthropic:be,openai:Re},ce={anthropic:"Anthropic",openai:"OpenAI"},de=[{mode:"base",dimension:"family"},{mode:"base",dimension:"strength"},{mode:"orchestrator",dimension:"family"},{mode:"orchestrator",dimension:"strength"}];function Ee(){const p=Se(),e=pe()||null;let t;if(e!==null)try{const s=Pe(e);t={label:`Profile: ${s.manifest.name}`,root:Ce(s.profileId)}}catch{}const i=Ie(process.cwd(),e),r=i.map(s=>({label:`Project: ${ve(ke(s))}`,root:s}));return{targets:[{label:"User",root:p},...t?[t]:[],...r],precedenceRoots:[...i,...t?[t.root]:[],p]}}v(Ee,"modelConfigTargets");function j(p){if(!p)return{};const e=p.trim(),t=e.indexOf("/"),i=t===-1?void 0:e.slice(0,t),r=t===-1?e:e.slice(t+1),s=r.match(/^(.*?):(off|minimal|low|medium|high|xhigh|max)$/i);return{provider:i,id:s?s[1]:r,level:s?s[2].toLowerCase():void 0}}v(j,"parseModelRef");function G(p){if(!p)return"\u2014";const e=p.indexOf("/");return e===-1?p:p.slice(e+1)}v(G,"shortRef");class Xe{static{v(this,"ModelsPanel")}tui;runtime;registry;matches;label;mode="list";ladders;modelRoutes;legacyModelRoutes;kinds;providerKeys=oe;targets;precedenceRoots;targetIndex=0;rawTarget;rows=[];selected=0;column=0;notice="";modelSelector;thinkingSelector;pending;choice;choiceIndex=0;routeForm;routeInput=new ye;pendingRouteCell;onChange;constructor(e,t,i,r,s){this.tui=e,this.runtime=t,this.registry=i,this.matches=r,this.label=s;const n=Ee();this.targets=n.targets,this.precedenceRoots=n.precedenceRoots,this.rawTarget=P(this.currentTarget().root);const c=re();this.ladders=c.modelLadders,this.modelRoutes=c.modelRoutes??{},this.legacyModelRoutes=c.legacyModelRoutes??{},this.kinds=c.kinds,this.providerKeys=this.offeredProviders(),this.rows=this.buildRows(),this.routeInput.onSubmit=l=>{const u=this.routeForm;!u||u.editing===null||(u.text[u.editing]=l.trim(),u.editing=null,this.routeInput.focused=!1,this.notify())},this.routeInput.onEscape=()=>{this.routeForm&&(this.routeForm.editing=null,this.routeInput.focused=!1,this.notify())}}offeredProviders(){try{return Te(e=>we(e)&&xe(e).length>0||this.registry.getProviderAuthStatus(e).configured,this.ladders)}catch{return oe}}currentTarget(){return this.targets[this.targetIndex]}cycleTarget(){this.targetIndex=(this.targetIndex+1)%this.targets.length,this.rawTarget=P(this.currentTarget().root),this.notice="",this.notify()}shadowingPath(e){for(const t of this.precedenceRoots){if(t===this.currentTarget().root)break;const i=P(t);if(i&&e(i))return S(t,"config.json")}}savedButShadowed(e){return`Saved to ${this.currentTarget().label}; overridden by ${e??"a more specific config"}`}routeIds(){return[...new Set([...Object.keys(this.modelRoutes),...Object.keys(this.legacyModelRoutes)])]}routeFamilies(){return[...new Set(this.routeIds().map(e=>(this.modelRoutes[e]??this.legacyModelRoutes[e]).family))]}buildRows(){const e=[{section:"ladder",kind:"defaultProvider"}];for(const t of k)e.push({section:"ladder",kind:"strength",strength:t});for(const t of this.routeIds())e.push({section:"route",routeId:t});e.push({section:"route",routeId:null});for(const t of Object.keys(this.kinds))t.includes("/")||e.push({section:"kind",name:t});return e}reload(){const e=re();this.ladders=e.modelLadders,this.modelRoutes=e.modelRoutes??{},this.legacyModelRoutes=e.legacyModelRoutes??{},this.kinds=e.kinds,this.rawTarget=P(this.currentTarget().root),this.providerKeys=this.offeredProviders(),this.rows=this.buildRows(),this.selected=Math.min(this.selected,this.rows.length-1),this.clampColumn()}refresh(){this.reload(),this.notify()}notify(){this.onChange?.(),this.tui.requestRender()}isPickerOpen(){return this.mode!=="list"}currentRow(){return this.rows[Math.min(this.selected,this.rows.length-1)]}columnsFor(e){return e.section==="ladder"?e.kind==="strength"?this.providerKeys.length:1:e.section==="route"?1:de.length}clampColumn(){this.column=Math.min(this.column,this.columnsFor(this.currentRow())-1)}handleInput(e){if(this.mode==="route-form"){const t=this.routeForm;return t?t.editing!==null?(this.routeInput.handleInput(e),!0):this.matches("crtr.setup.models.cancel",e)?(this.closeRouteForm(),!0):this.matches("crtr.setup.list-down",e)?(t.index=Math.min(t.fields.length-1,t.index+1),this.notice="",!0):this.matches("crtr.setup.list-up",e)?(t.index=Math.max(0,t.index-1),this.notice="",!0):((this.matches("crtr.setup.models.open",e)||this.matches("crtr.setup.models.commit",e))&&this.activateRouteField(),!0):(this.mode="list",!0)}if(this.mode==="route-delete")return this.matches("crtr.setup.confirm.accept",e)?(this.deleteRoute(),!0):(this.matches("crtr.setup.confirm.back",e)&&(this.mode="route-form",this.notify()),!0);if(this.mode==="dimension-picker")return this.matches("crtr.setup.models.commit",e)?(this.commitChoice(),!0):this.matches("crtr.setup.models.cancel",e)?(this.closePicker(),!0):this.matches("crtr.setup.list-down",e)?(this.choiceIndex=Math.min((this.choice?.options.length??1)-1,this.choiceIndex+1),!0):(this.matches("crtr.setup.list-up",e)&&(this.choiceIndex=Math.max(0,this.choiceIndex-1)),!0);if(this.mode==="model-picker"){const t=this.matches("crtr.setup.models.commit",e)?"\r":this.matches("crtr.setup.models.cancel",e)?"\x1B":e;return this.modelSelector?.handleInput(t),!0}if(this.mode==="thinking-picker"){const t=this.matches("crtr.setup.models.commit",e)?"\r":this.matches("crtr.setup.models.cancel",e)?"\x1B":e;return this.thinkingSelector?.getSelectList().handleInput(t),!0}return this.matches("crtr.setup.list-down",e)?(this.selected=Math.min(this.rows.length-1,this.selected+1),this.clampColumn(),this.notice="",!0):this.matches("crtr.setup.list-up",e)?(this.selected=Math.max(0,this.selected-1),this.clampColumn(),this.notice="",!0):this.matches("crtr.setup.cell-previous",e)?(this.column=Math.max(0,this.column-1),!0):this.matches("crtr.setup.cell-next",e)?(this.column=Math.min(this.columnsFor(this.currentRow())-1,this.column+1),!0):this.matches("crtr.setup.models.scope-next",e)?(this.cycleTarget(),!0):this.matches("crtr.setup.models.open",e)?(this.activateCell(),!0):!1}activateCell(){const e=this.currentRow();if(this.notice="",e.section==="ladder"&&e.kind==="defaultProvider"){const t=q(void 0,this.ladders).provider,i=this.providerKeys,r=i[(Math.max(0,i.indexOf(t))+1)%i.length];if(r===t)return;try{te(this.currentTarget().root,{defaultProvider:r})}catch(s){this.notice=`Could not save: ${s instanceof Error?s.message:String(s)}`,this.notify();return}if(this.reload(),(this.ladders.defaultProvider??"anthropic")!==r){const s=this.shadowingPath(n=>n.modelLadders?.defaultProvider!==void 0);this.notice=this.savedButShadowed(s)}this.notify();return}if(e.section==="ladder"&&e.kind==="strength"){this.openModelPicker(this.providerKeys[this.column],e.strength);return}if(e.section==="route"){this.openRouteForm(e.routeId);return}if(e.section==="kind"){const t=de[this.column];if(!t)return;this.openDimensionPicker(e.name,t.mode,t.dimension)}}portableSelection(e){if(e!==void 0){const i=e.lastIndexOf("/"),r=i===-1?"":e.slice(0,i),s=i===-1?"":e.slice(i+1);if(this.routeFamilies().includes(r)&&k.includes(s))return{family:r,strength:s}}const t=q(e,this.ladders);return{family:t.provider,strength:t.strength}}familyLabel(e){return e==="anthropic"||e==="openai"?ce[e]:e}familyOptions(){return[...new Set([...this.providerKeys,...this.routeFamilies()])]}kindSelection(e,t){const i=this.kinds[e],r=t==="orchestrator"?i?.orchestratorModel??i?.model:i?.model;return this.portableSelection(r)}openDimensionPicker(e,t,i){const r=this.kindSelection(e,t),s=i==="family"?this.familyOptions():k,n=i==="family"?r.family:r.strength,c=t==="orchestrator"?"orchestrator":"base",l=i==="family"?`strength ${r.strength}`:`family ${this.familyLabel(r.family)}`;this.choice={title:`Choose ${c} ${i}`,subtitle:`${e} \xB7 keeps ${c} ${l}`,options:s,label:v(u=>i==="family"?this.familyLabel(u):u,"label"),commit:v(u=>{const m=i==="family"?u:r.family,a=i==="strength"?u:r.strength;this.writeKindSelection(e,t,m,a)},"commit")},this.choiceIndex=Math.max(0,s.indexOf(n)),this.mode="dimension-picker",this.notify()}writeKindSelection(e,t,i,r){const s=`${i}/${r}`;try{Me({root:this.currentTarget().root,kind:e,model:s,orchestrator:t==="orchestrator"})}catch(l){this.notice=`Could not save: ${l instanceof Error?l.message:String(l)}`;return}this.reload();const n=this.kinds[e];if((t==="orchestrator"?n?.orchestratorModel:n?.model)!==s){const l=t==="orchestrator"?"orchestratorModel":"model",u=this.shadowingPath(m=>m.kinds?.[e]?.[l]!==void 0);this.notice=this.savedButShadowed(u)}}commitChoice(){const e=this.choice,t=e?.options[this.choiceIndex];e&&t!==void 0&&e.commit(t),this.closePicker()}openRouteForm(e){const t=e===null?void 0:this.modelRoutes[e]??this.legacyModelRoutes[e],i=[...e===null?[{kind:"text",key:"routeId",label:"Route id"}]:[],{kind:"text",key:"family",label:"Family"},{kind:"text",key:"credentialSource",label:"Credential source"},{kind:"provider",label:"Provider id"},...k.map(r=>({kind:"cell",strength:r})),{kind:"save"},...e===null?[]:[{kind:"delete"}]];this.routeForm={existing:e,text:{routeId:e??"",family:t?.family??"",credentialSource:t?.credentialSource??""},providerId:t?.providerId??"",models:{...t?.models},fields:i,index:0,editing:null},this.mode="route-form",this.notice="",this.notify()}closeRouteForm(){this.routeInput.focused=!1,this.routeForm=void 0,this.mode="list",this.notice="",this.notify()}activateRouteField(){const e=this.routeForm,t=e?.fields[e.index];if(!(!e||!t)){if(t.kind==="text"){e.editing=t.key,this.routeInput.setValue(e.text[t.key]),this.routeInput.handleInput(""),this.routeInput.focused=!0,this.notify();return}if(t.kind==="provider"){const i=[...new Set([...this.registry.getAll().map(r=>r.provider),...e.providerId?[e.providerId]:[]])].sort();this.choice={title:"Choose provider id",subtitle:"Route cells must all belong to this registry provider",options:i,label:v(r=>r,"label"),commit:v(r=>this.setRouteProvider(r),"commit")},this.choiceIndex=Math.max(0,i.indexOf(e.providerId)),this.mode="dimension-picker",this.notify();return}if(t.kind==="cell"){this.openRouteCell(t.strength);return}if(t.kind==="save"){this.saveRoute();return}this.mode="route-delete",this.notify()}}setRouteProvider(e){const t=this.routeForm;if(!t)return;t.providerId=e;const i=[];for(const r of k){const s=t.models[r];s!==void 0&&!s.startsWith(`${e}/`)&&(delete t.models[r],i.push(r))}this.notice=i.length===0?"":`Cleared ${i.join(", ")} because those models belong to another provider.`}openRouteCell(e){const t=this.routeForm;if(!t)return;if(!t.providerId){this.notice="Choose the provider id first",this.notify();return}const i=this.registry.getAll().filter(n=>n.provider===t.providerId);if(i.length===0){this.notice=`No models registered for provider "${t.providerId}" \u2014 this route cannot be edited here.`,this.notify();return}const r=j(t.models[e]),s=r.provider&&r.id?this.registry.find(r.provider,r.id):void 0;this.pendingRouteCell={strength:e},this.showModelSelector(i,s,n=>this.onRouteModelPicked(n))}onRouteModelPicked(e){const t=this.pendingRouteCell,i=this.routeForm;if(!t||!i){this.closePicker();return}if(e.provider!==i.providerId){this.notice=`That is a ${e.provider} model \u2014 pick a ${i.providerId} model for this route.`,this.closePicker();return}if(t.model=e,e.reasoning){const r=j(i.models[t.strength]).level??"medium";this.thinkingSelector=new ee(r,[...se],s=>this.onThinkingPicked(s),()=>this.closePicker()),this.modelSelector=void 0,this.mode="thinking-picker",this.notify();return}i.models[t.strength]=`${e.provider}/${e.id}`,this.closePicker()}saveRoute(){const e=this.routeForm;if(!e)return;const t=e.text.routeId.trim();if(!t){this.notice="Route id is required.",this.notify();return}if(!e.text.family.trim()){this.notice="Family is required.",this.notify();return}if(!e.text.credentialSource.trim()){this.notice="Credential source is required.",this.notify();return}if(!e.providerId.trim()){this.notice="Provider id is required.",this.notify();return}const i=k.filter(n=>e.models[n]===void 0);if(i.length>0){this.notice=`Choose models for ${i.join(", ")} before saving.`,this.notify();return}const r={family:e.text.family.trim(),credentialSource:e.text.credentialSource.trim(),providerId:e.providerId.trim(),models:{ultra:e.models.ultra,strong:e.models.strong,medium:e.models.medium,light:e.models.light}};try{ie(this.currentTarget().root,t,r)}catch(n){this.notice=`Could not save: ${n instanceof Error?n.message:String(n)}`,this.notify();return}this.routeForm=void 0,this.mode="list",this.reload();const s=this.rows.findIndex(n=>n.section==="route"&&n.routeId===t);if(s>=0&&(this.selected=s),JSON.stringify(this.modelRoutes[t])!==JSON.stringify(r)){const n=this.shadowingPath(c=>c.modelRoutes?.[t]!==void 0);this.notice=this.savedButShadowed(n)}else this.notice=`Saved route "${t}" to ${this.currentTarget().label}.`;this.notify()}declaringPath(e){for(const t of this.precedenceRoots){if(t===this.currentTarget().root)continue;if(P(t)?.modelRoutes?.[e]!==void 0)return S(t,"config.json")}}deleteRoute(){const e=this.routeForm?.existing;if(!e){this.mode="route-form",this.notify();return}try{ie(this.currentTarget().root,e,void 0)}catch(i){this.mode="route-form",this.notice=`Could not delete: ${i instanceof Error?i.message:String(i)}`,this.notify();return}const t=this.declaringPath(e);this.routeForm=void 0,this.mode="list",this.reload(),this.notice=t?`Removed route "${e}" from ${this.currentTarget().label}; it is still declared by ${t}.`:`Removed route "${e}" from ${this.currentTarget().label}.`,this.notify()}showModelSelector(e,t,i){this.modelSelector=new $e(this.tui,t,this.runtime,e.map(r=>({model:r})),i,()=>this.closePicker()),this.modelSelector.focused=!0,this.mode="model-picker",this.notify()}openModelPicker(e,t){const i=Le[e],r=this.registry,s=j(this.ladders[e][t]),n=s.provider&&s.id?r.find(s.provider,s.id):void 0,c=r.getAvailable().filter(l=>l.provider===i);if(c.length===0){this.notice=`No ${e} models available \u2014 sign in first.`,this.notify();return}this.pending={providerKey:e,strength:t,expectedRuntime:i},this.showModelSelector(c,n,l=>this.onModelPicked(l))}onModelPicked(e){if(!this.pending){this.closePicker();return}if(e.provider!==this.pending.expectedRuntime){this.notice=`That is a ${e.provider} model \u2014 pick an ${this.pending.providerKey} model for this column.`,this.closePicker();return}if(this.pending.model=e,e.reasoning){const t=j(this.ladders[this.pending.providerKey][this.pending.strength]).level??"medium";this.thinkingSelector=new ee(t,[...se],i=>this.onThinkingPicked(i),()=>this.closePicker()),this.modelSelector=void 0,this.mode="thinking-picker",this.notify();return}this.writeLadder(e,void 0),this.closePicker()}onThinkingPicked(e){if(this.pendingRouteCell?.model&&this.routeForm){const{model:t,strength:i}=this.pendingRouteCell;this.routeForm.models[i]=`${t.provider}/${t.id}${e!=="off"?`:${e}`:""}`}else this.pending?.model&&this.writeLadder(this.pending.model,e);this.closePicker()}writeLadder(e,t){if(!this.pending)return;const i=`${e.provider}/${e.id}${t&&t!=="off"?`:${t}`:""}`,{providerKey:r,strength:s}=this.pending;try{te(this.currentTarget().root,{[r]:{[s]:i}})}catch(n){this.notice=`Could not save: ${n instanceof Error?n.message:String(n)}`;return}if(this.reload(),this.ladders[r][s]!==i){const n=this.shadowingPath(c=>c.modelLadders?.[r]?.[s]!==void 0);this.notice=this.savedButShadowed(n)}}closePicker(){this.mode=this.routeForm!==void 0?"route-form":"list",this.modelSelector=void 0,this.thinkingSelector=void 0,this.pending=void 0,this.pendingRouteCell=void 0,this.choice=void 0,this.notify()}dispose(){this.routeForm=void 0,this.closePicker()}targetLadderOverride(e,t){return this.rawTarget?.modelLadders?.[e]?.[t]!==void 0}targetKindOverride(e,t){const i=this.rawTarget?.kinds,r=t?"orchestratorModel":"model";return i?.[e]?.[r]!==void 0}footerHint(){return this.mode==="dimension-picker"?`${this.label("crtr.setup.list-down")}/${this.label("crtr.setup.list-up")} choice \xB7 ${this.label("crtr.setup.models.commit")} save \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="model-picker"?`type to search \xB7 ${this.label("crtr.setup.models.commit")} select \xB7 tab all/scoped \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="thinking-picker"?`\u2191/\u2193 level \xB7 ${this.label("crtr.setup.models.commit")} set \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="route-delete"?`${this.label("crtr.setup.confirm.accept")} delete \xB7 ${this.label("crtr.setup.confirm.back")} cancel`:this.mode==="route-form"?typeof this.routeForm?.editing=="string"?"type a value \xB7 enter save \xB7 esc cancel":`${this.label("crtr.setup.list-down")}/${this.label("crtr.setup.list-up")} field \xB7 ${this.label("crtr.setup.models.open")} edit \xB7 ${this.label("crtr.setup.models.cancel")} back`:""}renderChoicePicker(){const e=this.choice;if(!e)return[o.fg("warning","No setting selected.")];const t=[o.fg("accent",o.bold(e.title)),o.fg("dim",e.subtitle),""];for(const[i,r]of e.options.entries()){const s=i===this.choiceIndex,n=s?o.fg("accent","\u25B6"):" ",c=e.label(r);t.push(`${n} ${s?o.fg("accent",`[${c}]`):o.fg("dim",` ${c} `)}`)}return t}renderRouteForm(e){const t=this.routeForm;if(!t)return[o.fg("warning","No route selected.")];const i=this.currentTarget(),r=[o.fg("accent",o.bold(t.existing===null?"New provider route":`Provider route: ${t.existing}`)),o.fg("dim",`${i.label} \xB7 ${S(i.root,"config.json")}`),""],s=20;for(const[n,c]of t.fields.entries()){const l=n===t.index,u=l?o.fg("accent","\u25B6"):" ";let m,a;if(c.kind==="text"){if(m=c.label,a=t.text[c.key]||"\u2014",l&&t.editing===c.key){const K=Math.max(8,e-s-4);a=this.routeInput.render(K)[0]??""}}else c.kind==="provider"?(m=c.label,a=t.providerId||"\u2014"):c.kind==="cell"?(m=c.strength,a=G(t.models[c.strength])):c.kind==="save"?(m="",a="Save route"):(m="",a="Delete route");const C=a==="\u2014",T=l?o.fg("accent",`[${a}]`):o.fg(C?"warning":"dim",` ${a} `);r.push(`${u} ${f(m,s)}${T}`)}return this.notice&&r.push("",o.fg("warning",this.notice)),r}renderRouteDelete(e){const t=this.routeForm?.existing??"this route",i=this.currentTarget(),r=Oe(`This removes the entry from ${i.label}. Any "${t}" declared by a lower-precedence config becomes effective again; this does not guarantee the route is gone.`,e).map(s=>o.fg("warning",s));return[o.fg("accent",o.bold(`Delete provider route: ${t}`)),o.fg("dim",S(i.root,"config.json")),"",...r,"",o.fg("dim",`${this.label("crtr.setup.confirm.accept")} delete ${this.label("crtr.setup.confirm.back")} cancel`)]}fitBody(e,t,i){if(e.length<=i)return e;const r=Math.min(ne,e.length-i),s=e.slice(r);if(s.length<=i)return s;const n=Math.max(0,t-r),c=Math.max(0,Math.min(n-i+2,s.length-i));return s.slice(c,c+i)}render(e,t){const i=Math.max(1,t),r=v(d=>Fe(d,e),"frame");if(this.mode==="dimension-picker")return r(this.fitBody(this.renderChoicePicker(),ne+this.choiceIndex,i));if(this.mode==="model-picker")return r(U(this.modelSelector?.render(e)??[],i));if(this.mode==="thinking-picker")return r(U(this.thinkingSelector?.render(e)??[],i));if(this.mode==="route-form"){const d=3+(this.routeForm?.index??0);return r(this.fitBody(this.renderRouteForm(e),d,i))}if(this.mode==="route-delete")return r(U(this.renderRouteDelete(e),i));let s=0;const n=[o.fg("accent",o.bold("Models")),o.fg("dim","global user defaults \xB7 project/profile scopes can shadow these"),""],c=this.currentRow(),l=c.section==="ladder"&&c.kind==="strength",u=Math.max(1,e),m=Math.max(12,Math.min(20,Math.floor(u*.24))),a=this.providerKeys.length,C=Math.max(1,u-2-m),T=12,K=Math.max(1,Math.floor((C+1)/(T+1))),b=Math.min(a,K),N=Math.max(T,Math.floor((C-(b-1))/b)),J=l?Math.min(this.column,a-1):0,F=Math.max(0,Math.min(J>=b?J-b+1:0,a-b)),_=F+b,X=this.providerKeys.map((d,h)=>({provider:d,column:h})).slice(F,_),Y=F>0,z=_<a,he=Y||z?` ${o.fg("dim",`${Y?"\u2039 ":""}${F+1}\u2013${_} of ${a}${z?" \u203A":""}`)}`:"",O=this.currentTarget();n.push(`${o.fg("accent","Editing config")} ${o.fg("accent",`[${O.label}]`)}${o.fg("dim",` ${this.targetIndex+1}/${this.targets.length}`)}`),n.push(o.fg("muted",S(O.root,"config.json")),""),n.push(o.fg("accent","Strength ladders")+he);const le=q(void 0,this.ladders).provider,W=this.rows.find(d=>d.section==="ladder"&&d.kind==="defaultProvider")===c;W&&(s=n.length),n.push(`${W?o.fg("accent","\u25B6"):" "} ${f(W?o.fg("accent","Default provider"):"Default provider",20)} ${o.fg("success",le)}`);const ue=v((d,h)=>{const g=f(o.bold(ce[d]),N);return l&&this.column===h?o.fg("accent",g):o.fg("dim",g)},"providerHeading"),ae=v((d,h)=>{const g=f(V(d,Math.max(1,N-2)),Math.max(1,N-2));return h?o.fg("accent",`[${g}]`):o.fg("dim",` ${g} `)},"ladderCell");n.push(`${" ".repeat(2)}${f(o.fg("dim","Strength"),m)}${X.map(({provider:d,column:h})=>ue(d,h)).join(" ")}`);for(const d of this.rows){if(d.section!=="ladder"||d.kind!=="strength")continue;const h=d===c,g=h?o.fg("accent","\u25B6"):" ";h&&(s=n.length);const $=X.map(({provider:y,column:R})=>{const w=this.targetLadderOverride(y,d.strength)?o.fg("warning","\u2022"):" ";return ae(`${w}${G(this.ladders[y][d.strength])}`,h&&R===this.column)});n.push(`${g} ${f(d.strength,m)}${$.join(" ")}`)}n.push("",o.fg("accent","Provider routes")),n.push(o.fg("dim",`${o.fg("warning","\u2022")} override in ${O.label} \xB7 a declared route replaces the ladders above at launch \xB7 id, then family \xB7 source \xB7 provider`));const A=Math.max(10,Math.floor((u-8)/4));n.push(`${" ".repeat(4)}${k.map(d=>f(o.fg("dim",d),A)).join(" ")}`),this.routeIds().length===0&&n.push(o.fg("dim"," No routes declared \u2014 the ladders above are used."));for(const d of this.rows){if(d.section!=="route"||d.routeId===null)continue;const h=d.routeId,g=this.modelRoutes[h],$=g??this.legacyModelRoutes[h],y=d===c;y&&(s=n.length);const R=y?o.fg("accent","\u25B6"):" ",w=this.rawTarget?.modelRoutes?.[h]!==void 0?o.fg("warning","\u2022"):" ",E=g?"":o.fg("warning"," \xB7 incomplete"),D=o.fg("dim",`${$.family} \xB7 ${$.credentialSource} \xB7 ${$.providerId}`);n.push(`${R} ${w}${o.fg(y?"accent":"dim",h)} ${D}${E}`);const x=k.map(fe=>{const Z=$.models[fe],me=f(V(G(Z),A),A);return o.fg(Z===void 0?"warning":"dim",me)});n.push(`${" ".repeat(4)}${x.join(" ")}`)}const B=this.rows.find(d=>d.section==="route"&&d.routeId===null)===c;B&&(s=n.length),n.push(`${B?o.fg("accent","\u25B6"):" "} ${o.fg(B?"accent":"dim","+ New route")}`),n.push("",o.fg("accent","Kind defaults")),n.push(o.fg("dim",`${o.fg("warning","\u2022")} override in ${O.label} \xB7 orchestrator inherits base until you set it`));const H=16,M=12,I=9,Q=M+1+I,L=v((d,h,g,$)=>{const y=f(V(d,Math.max(1,h-2)),Math.max(1,h-2));return g?o.fg("accent",`[${y}]`):$?o.fg("muted",` ${y} `):o.fg("dim",` ${y} `)},"cell");n.push(`${" ".repeat(2+H)}${f(o.bold(o.fg("dim","Base")),Q)} ${f(o.bold(o.fg("dim","Orchestrator")),Q)}`),n.push(`${" ".repeat(2)}${f(o.fg("dim","Kind"),H)}${f(o.fg("dim","Family"),M)} ${f(o.fg("dim","Strength"),I)} ${f(o.fg("dim","Family"),M)} ${f(o.fg("dim","Strength"),I)}`);for(const d of this.rows){if(d.section!=="kind")continue;const h=d===c,g=h?o.fg("accent","\u25B6"):" ";h&&(s=n.length);const $=this.kinds[d.name],y=this.portableSelection($?.model),R=$?.orchestratorModel===void 0,w=this.portableSelection($?.orchestratorModel??$?.model),E=this.targetKindOverride(d.name,!1)?o.fg("warning","\u2022"):" ",D=this.targetKindOverride(d.name,!0)?o.fg("warning","\u2022"):" ",x=[L(`${E}${this.familyLabel(y.family)}`,M,h&&this.column===0,!1),L(`${E}${y.strength}`,I,h&&this.column===1,!1),L(`${D}${this.familyLabel(w.family)}`,M,h&&this.column===2,R),L(`${D}${w.strength}`,I,h&&this.column===3,R)];n.push(`${g} ${f(d.name,H)}${x[0]} ${x[1]} ${x[2]} ${x[3]}`)}return this.notice&&n.push("",o.fg("warning",this.notice)),r(this.fitBody(n,s,i))}}export{Xe as ModelsPanel};
|
|
1
|
+
var ge=Object.defineProperty;var y=(p,e)=>ge(p,"name",{value:e,configurable:!0});import{envProfileId as pe}from"../../../shared/env.js";import{ModelSelectorComponent as $e,ThinkingSelectorComponent as ee}from"@earendil-works/pi-coding-agent";import{Input as ve,truncateToWidth as V}from"@earendil-works/pi-tui";import{basename as ye,dirname as ke,join as S}from"node:path";import{ANTHROPIC_PROVIDER_ID as be,OPENAI_CODEX_PROVIDER_ID as Re,isManagedProvider as we,readSubscriptionPool as xe}from"../../../core/subscription-state.js";import{readRawConfigAtRoot as P,persistDefaultKindModel as Ie,persistModelLadders as te,persistModelRoute as ie,readMergedLaunchConfig as re}from"../../../core/config.js";import{findProjectScopeRoots as Me,userScopeRoot as Se}from"../../../core/scope.js";import{loadProfileManifest as Pe,profileDir as Ce}from"../../../core/profiles/manifest.js";import{configuredLadderProviders as Te,inferPortableModelSelection as q,MODEL_PROVIDER_KEYS as oe,MODEL_STRENGTHS as k,THINKING_SUFFIXES as se}from"../../../core/runtime/model-selection.js";import{fitTail as U,padAnsi as f,renderRows as Fe,theme as o,wrapText as Oe}from"../../../core/tui/panel.js";const ne=3,Le={anthropic:be,openai:Re},ce={anthropic:"Anthropic",openai:"OpenAI"},de=[{mode:"base",dimension:"family"},{mode:"base",dimension:"strength"},{mode:"orchestrator",dimension:"family"},{mode:"orchestrator",dimension:"strength"}];function Ee(){const p=Se(),e=pe()||null;let t;if(e!==null)try{const s=Pe(e);t={label:`Profile: ${s.manifest.name}`,root:Ce(s.profileId)}}catch{}const i=Me(process.cwd(),e),r=i.map(s=>({label:`Project: ${ye(ke(s))}`,root:s}));return{targets:[{label:"User",root:p},...t?[t]:[],...r],precedenceRoots:[...i,...t?[t.root]:[],p]}}y(Ee,"modelConfigTargets");function j(p){if(!p)return{};const e=p.trim(),t=e.indexOf("/"),i=t===-1?void 0:e.slice(0,t),r=t===-1?e:e.slice(t+1),s=r.match(/^(.*?):(off|minimal|low|medium|high|xhigh|max)$/i);return{provider:i,id:s?s[1]:r,level:s?s[2].toLowerCase():void 0}}y(j,"parseModelRef");function G(p){if(!p)return"\u2014";const e=p.indexOf("/");return e===-1?p:p.slice(e+1)}y(G,"shortRef");class Xe{static{y(this,"ModelsPanel")}tui;runtime;registry;matches;label;mode="list";ladders;modelRoutes;legacyModelRoutes;kinds;providerKeys=oe;targets;precedenceRoots;targetIndex=0;rawTarget;rows=[];selected=0;column=0;notice="";modelSelector;thinkingSelector;pending;choice;choiceIndex=0;routeForm;routeInput=new ve;pendingRouteCell;onChange;constructor(e,t,i,r,s){this.tui=e,this.runtime=t,this.registry=i,this.matches=r,this.label=s;const n=Ee();this.targets=n.targets,this.precedenceRoots=n.precedenceRoots,this.rawTarget=P(this.currentTarget().root);const c=re();this.ladders=c.modelLadders,this.modelRoutes=c.modelRoutes??{},this.legacyModelRoutes=c.legacyModelRoutes??{},this.kinds=c.kinds,this.providerKeys=this.offeredProviders(),this.rows=this.buildRows(),this.routeInput.onSubmit=l=>{const u=this.routeForm;!u||u.editing===null||(u.text[u.editing]=l.trim(),u.editing=null,this.routeInput.focused=!1,this.notify())},this.routeInput.onEscape=()=>{this.routeForm&&(this.routeForm.editing=null,this.routeInput.focused=!1,this.notify())}}offeredProviders(){try{return Te(e=>we(e)&&xe(e).length>0||this.registry.getProviderAuthStatus(e).configured,this.ladders)}catch{return oe}}currentTarget(){return this.targets[this.targetIndex]}cycleTarget(){this.targetIndex=(this.targetIndex+1)%this.targets.length,this.rawTarget=P(this.currentTarget().root),this.notice="",this.notify()}shadowingPath(e){for(const t of this.precedenceRoots){if(t===this.currentTarget().root)break;const i=P(t);if(i&&e(i))return S(t,"config.json")}}savedButShadowed(e){return`Saved to ${this.currentTarget().label}; overridden by ${e??"a more specific config"}`}routeIds(){return[...new Set([...Object.keys(this.modelRoutes),...Object.keys(this.legacyModelRoutes)])]}routeFamilies(){return[...new Set(this.routeIds().map(e=>(this.modelRoutes[e]??this.legacyModelRoutes[e]).family))]}buildRows(){const e=[{section:"ladder",kind:"defaultProvider"}];for(const t of k)e.push({section:"ladder",kind:"strength",strength:t});for(const t of this.routeIds())e.push({section:"route",routeId:t});e.push({section:"route",routeId:null});for(const t of Object.keys(this.kinds))t.includes("/")||e.push({section:"kind",name:t});return e}reload(){const e=re();this.ladders=e.modelLadders,this.modelRoutes=e.modelRoutes??{},this.legacyModelRoutes=e.legacyModelRoutes??{},this.kinds=e.kinds,this.rawTarget=P(this.currentTarget().root),this.providerKeys=this.offeredProviders(),this.rows=this.buildRows(),this.selected=Math.min(this.selected,this.rows.length-1),this.clampColumn()}refresh(){this.reload(),this.notify()}notify(){this.onChange?.(),this.tui.requestRender()}isPickerOpen(){return this.mode!=="list"}currentRow(){return this.rows[Math.min(this.selected,this.rows.length-1)]}columnsFor(e){return e.section==="ladder"?e.kind==="strength"?this.providerKeys.length:1:e.section==="route"?1:de.length}clampColumn(){this.column=Math.min(this.column,this.columnsFor(this.currentRow())-1)}handleInput(e){if(this.mode==="route-form"){const t=this.routeForm;return t?t.editing!==null?(this.routeInput.handleInput(e),!0):this.matches("crtr.setup.models.cancel",e)?(this.closeRouteForm(),!0):this.matches("crtr.setup.list-down",e)?(t.index=Math.min(t.fields.length-1,t.index+1),this.notice="",!0):this.matches("crtr.setup.list-up",e)?(t.index=Math.max(0,t.index-1),this.notice="",!0):((this.matches("crtr.setup.models.open",e)||this.matches("crtr.setup.models.commit",e))&&this.activateRouteField(),!0):(this.mode="list",!0)}if(this.mode==="route-delete")return this.matches("crtr.setup.confirm.accept",e)?(this.deleteRoute(),!0):(this.matches("crtr.setup.confirm.back",e)&&(this.mode="route-form",this.notify()),!0);if(this.mode==="dimension-picker")return this.matches("crtr.setup.models.commit",e)?(this.commitChoice(),!0):this.matches("crtr.setup.models.cancel",e)?(this.closePicker(),!0):this.matches("crtr.setup.list-down",e)?(this.choiceIndex=Math.min((this.choice?.options.length??1)-1,this.choiceIndex+1),!0):(this.matches("crtr.setup.list-up",e)&&(this.choiceIndex=Math.max(0,this.choiceIndex-1)),!0);if(this.mode==="model-picker"){const t=this.matches("crtr.setup.models.commit",e)?"\r":this.matches("crtr.setup.models.cancel",e)?"\x1B":e;return this.modelSelector?.handleInput(t),!0}if(this.mode==="thinking-picker"){const t=this.matches("crtr.setup.models.commit",e)?"\r":this.matches("crtr.setup.models.cancel",e)?"\x1B":e;return this.thinkingSelector?.getSelectList().handleInput(t),!0}return this.matches("crtr.setup.list-down",e)?(this.selected=Math.min(this.rows.length-1,this.selected+1),this.clampColumn(),this.notice="",!0):this.matches("crtr.setup.list-up",e)?(this.selected=Math.max(0,this.selected-1),this.clampColumn(),this.notice="",!0):this.matches("crtr.setup.cell-previous",e)?(this.column=Math.max(0,this.column-1),!0):this.matches("crtr.setup.cell-next",e)?(this.column=Math.min(this.columnsFor(this.currentRow())-1,this.column+1),!0):this.matches("crtr.setup.models.scope-next",e)?(this.cycleTarget(),!0):this.matches("crtr.setup.models.open",e)?(this.activateCell(),!0):!1}activateCell(){const e=this.currentRow();if(this.notice="",e.section==="ladder"&&e.kind==="defaultProvider"){const t=q(void 0,this.ladders).provider,i=this.providerKeys,r=i[(Math.max(0,i.indexOf(t))+1)%i.length];if(r===t)return;try{te(this.currentTarget().root,{defaultProvider:r})}catch(s){this.notice=`Could not save: ${s instanceof Error?s.message:String(s)}`,this.notify();return}if(this.reload(),(this.ladders.defaultProvider??"anthropic")!==r){const s=this.shadowingPath(n=>n.modelLadders?.defaultProvider!==void 0);this.notice=this.savedButShadowed(s)}this.notify();return}if(e.section==="ladder"&&e.kind==="strength"){this.openModelPicker(this.providerKeys[this.column],e.strength);return}if(e.section==="route"){this.openRouteForm(e.routeId);return}if(e.section==="kind"){const t=de[this.column];if(!t)return;this.openDimensionPicker(e.name,t.mode,t.dimension)}}portableSelection(e){if(e!==void 0){const i=e.lastIndexOf("/"),r=i===-1?"":e.slice(0,i),s=i===-1?"":e.slice(i+1);if(this.routeFamilies().includes(r)&&k.includes(s))return{family:r,strength:s}}const t=q(e,this.ladders);return{family:t.provider,strength:t.strength}}familyLabel(e){return e==="anthropic"||e==="openai"?ce[e]:e}familyOptions(){return[...new Set([...this.providerKeys,...this.routeFamilies()])]}kindSelection(e,t){const i=this.kinds[e],r=t==="orchestrator"?i?.orchestratorModel??i?.model:i?.model;return this.portableSelection(r)}openDimensionPicker(e,t,i){const r=this.kindSelection(e,t),s=i==="family"?this.familyOptions():k,n=i==="family"?r.family:r.strength,c=t==="orchestrator"?"orchestrator":"base",l=i==="family"?`strength ${r.strength}`:`family ${this.familyLabel(r.family)}`;this.choice={title:`Choose ${c} ${i}`,subtitle:`${e} \xB7 keeps ${c} ${l}`,options:s,label:y(u=>i==="family"?this.familyLabel(u):u,"label"),commit:y(u=>{const m=i==="family"?u:r.family,a=i==="strength"?u:r.strength;this.writeKindSelection(e,t,m,a)},"commit")},this.choiceIndex=Math.max(0,s.indexOf(n)),this.mode="dimension-picker",this.notify()}writeKindSelection(e,t,i,r){const s=`${i}/${r}`;try{Ie({root:this.currentTarget().root,kind:e,model:s,orchestrator:t==="orchestrator"})}catch(l){this.notice=`Could not save: ${l instanceof Error?l.message:String(l)}`;return}this.reload();const n=this.kinds[e];if((t==="orchestrator"?n?.orchestratorModel:n?.model)!==s){const l=t==="orchestrator"?"orchestratorModel":"model",u=this.shadowingPath(m=>m.kinds?.[e]?.[l]!==void 0);this.notice=this.savedButShadowed(u)}}commitChoice(){const e=this.choice,t=e?.options[this.choiceIndex];e&&t!==void 0&&e.commit(t),this.closePicker()}openRouteForm(e){const t=e===null?void 0:this.modelRoutes[e]??this.legacyModelRoutes[e],i=[...e===null?[{kind:"text",key:"routeId",label:"Route id"}]:[],{kind:"text",key:"family",label:"Family"},{kind:"text",key:"credentialSource",label:"Credential source"},{kind:"provider",label:"Provider id"},...k.map(r=>({kind:"cell",strength:r})),{kind:"save"},...e===null?[]:[{kind:"delete"}]];this.routeForm={existing:e,text:{routeId:e??"",family:t?.family??"",credentialSource:t?.credentialSource??""},providerId:t?.providerId??"",models:{...t?.models},fields:i,index:0,editing:null},this.mode="route-form",this.notice="",this.notify()}closeRouteForm(){this.routeInput.focused=!1,this.routeForm=void 0,this.mode="list",this.notice="",this.notify()}activateRouteField(){const e=this.routeForm,t=e?.fields[e.index];if(!(!e||!t)){if(t.kind==="text"){e.editing=t.key,this.routeInput.setValue(e.text[t.key]),this.routeInput.handleInput(""),this.routeInput.focused=!0,this.notify();return}if(t.kind==="provider"){const i=[...new Set([...this.registry.getAll().map(r=>r.provider),...e.providerId?[e.providerId]:[]])].sort();this.choice={title:"Choose provider id",subtitle:"Route cells must all belong to this registry provider",options:i,label:y(r=>r,"label"),commit:y(r=>this.setRouteProvider(r),"commit")},this.choiceIndex=Math.max(0,i.indexOf(e.providerId)),this.mode="dimension-picker",this.notify();return}if(t.kind==="cell"){this.openRouteCell(t.strength);return}if(t.kind==="save"){this.saveRoute();return}this.mode="route-delete",this.notify()}}setRouteProvider(e){const t=this.routeForm;if(!t)return;t.providerId=e;const i=[];for(const r of k){const s=t.models[r];s!==void 0&&!s.startsWith(`${e}/`)&&(delete t.models[r],i.push(r))}this.notice=i.length===0?"":`Cleared ${i.join(", ")} because those models belong to another provider.`}openRouteCell(e){const t=this.routeForm;if(!t)return;if(!t.providerId){this.notice="Choose the provider id first",this.notify();return}const i=this.registry.getAll().filter(n=>n.provider===t.providerId);if(i.length===0){this.notice=`No models registered for provider "${t.providerId}" \u2014 this route cannot be edited here.`,this.notify();return}const r=j(t.models[e]),s=r.provider&&r.id?this.registry.find(r.provider,r.id):void 0;this.pendingRouteCell={strength:e},this.showModelSelector(i,s,n=>this.onRouteModelPicked(n))}onRouteModelPicked(e){const t=this.pendingRouteCell,i=this.routeForm;if(!t||!i){this.closePicker();return}if(e.provider!==i.providerId){this.notice=`That is a ${e.provider} model \u2014 pick a ${i.providerId} model for this route.`,this.closePicker();return}if(t.model=e,e.reasoning){const r=j(i.models[t.strength]).level??"medium";this.thinkingSelector=new ee(r,[...se],s=>this.onThinkingPicked(s),()=>this.closePicker()),this.modelSelector=void 0,this.mode="thinking-picker",this.notify();return}i.models[t.strength]=`${e.provider}/${e.id}`,this.closePicker()}saveRoute(){const e=this.routeForm;if(!e)return;const t=e.text.routeId.trim();if(!t){this.notice="Route id is required.",this.notify();return}if(!e.text.family.trim()){this.notice="Family is required.",this.notify();return}if(!e.text.credentialSource.trim()){this.notice="Credential source is required.",this.notify();return}if(!e.providerId.trim()){this.notice="Provider id is required.",this.notify();return}const i=k.filter(n=>e.models[n]===void 0);if(i.length>0){this.notice=`Choose models for ${i.join(", ")} before saving.`,this.notify();return}const r={family:e.text.family.trim(),credentialSource:e.text.credentialSource.trim(),providerId:e.providerId.trim(),models:{ultra:e.models.ultra,strong:e.models.strong,medium:e.models.medium,light:e.models.light}};try{ie(this.currentTarget().root,t,r)}catch(n){this.notice=`Could not save: ${n instanceof Error?n.message:String(n)}`,this.notify();return}this.routeForm=void 0,this.mode="list",this.reload();const s=this.rows.findIndex(n=>n.section==="route"&&n.routeId===t);if(s>=0&&(this.selected=s),JSON.stringify(this.modelRoutes[t])!==JSON.stringify(r)){const n=this.shadowingPath(c=>c.modelRoutes?.[t]!==void 0);this.notice=this.savedButShadowed(n)}else this.notice=`Saved route "${t}" to ${this.currentTarget().label}.`;this.notify()}declaringPath(e){for(const t of this.precedenceRoots){if(t===this.currentTarget().root)continue;if(P(t)?.modelRoutes?.[e]!==void 0)return S(t,"config.json")}}deleteRoute(){const e=this.routeForm?.existing;if(!e){this.mode="route-form",this.notify();return}try{ie(this.currentTarget().root,e,void 0)}catch(i){this.mode="route-form",this.notice=`Could not delete: ${i instanceof Error?i.message:String(i)}`,this.notify();return}const t=this.declaringPath(e);this.routeForm=void 0,this.mode="list",this.reload(),this.notice=t?`Removed route "${e}" from ${this.currentTarget().label}; it is still declared by ${t}.`:`Removed route "${e}" from ${this.currentTarget().label}.`,this.notify()}showModelSelector(e,t,i){this.modelSelector=new $e(this.tui,t,this.runtime,e.map(r=>({model:r})),i,()=>this.closePicker()),this.modelSelector.focused=!0,this.mode="model-picker",this.notify()}openModelPicker(e,t){const i=Le[e],r=this.registry,s=j(this.ladders[e][t]),n=s.provider&&s.id?r.find(s.provider,s.id):void 0,c=r.getAvailable().filter(l=>l.provider===i);if(c.length===0){this.notice=`No ${e} models available \u2014 sign in first.`,this.notify();return}this.pending={providerKey:e,strength:t,expectedRuntime:i},this.showModelSelector(c,n,l=>this.onModelPicked(l))}onModelPicked(e){if(!this.pending){this.closePicker();return}if(e.provider!==this.pending.expectedRuntime){this.notice=`That is a ${e.provider} model \u2014 pick an ${this.pending.providerKey} model for this column.`,this.closePicker();return}if(this.pending.model=e,e.reasoning){const t=j(this.ladders[this.pending.providerKey][this.pending.strength]).level??"medium";this.thinkingSelector=new ee(t,[...se],i=>this.onThinkingPicked(i),()=>this.closePicker()),this.modelSelector=void 0,this.mode="thinking-picker",this.notify();return}this.writeLadder(e,void 0),this.closePicker()}onThinkingPicked(e){if(this.pendingRouteCell?.model&&this.routeForm){const{model:t,strength:i}=this.pendingRouteCell;this.routeForm.models[i]=`${t.provider}/${t.id}${e!=="off"?`:${e}`:""}`}else this.pending?.model&&this.writeLadder(this.pending.model,e);this.closePicker()}writeLadder(e,t){if(!this.pending)return;const i=`${e.provider}/${e.id}${t&&t!=="off"?`:${t}`:""}`,{providerKey:r,strength:s}=this.pending;try{te(this.currentTarget().root,{[r]:{[s]:i}})}catch(n){this.notice=`Could not save: ${n instanceof Error?n.message:String(n)}`;return}if(this.reload(),this.ladders[r][s]!==i){const n=this.shadowingPath(c=>c.modelLadders?.[r]?.[s]!==void 0);this.notice=this.savedButShadowed(n)}}closePicker(){this.mode=this.routeForm!==void 0?"route-form":"list",this.modelSelector=void 0,this.thinkingSelector=void 0,this.pending=void 0,this.pendingRouteCell=void 0,this.choice=void 0,this.notify()}dispose(){this.routeForm=void 0,this.closePicker()}targetLadderOverride(e,t){return this.rawTarget?.modelLadders?.[e]?.[t]!==void 0}targetKindOverride(e,t){const i=this.rawTarget?.kinds,r=t?"orchestratorModel":"model";return i?.[e]?.[r]!==void 0}footerHint(){return this.mode==="dimension-picker"?`${this.label("crtr.setup.list-down")}/${this.label("crtr.setup.list-up")} choice \xB7 ${this.label("crtr.setup.models.commit")} save \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="model-picker"?`type to search \xB7 ${this.label("crtr.setup.models.commit")} select \xB7 tab all/scoped \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="thinking-picker"?`\u2191/\u2193 level \xB7 ${this.label("crtr.setup.models.commit")} set \xB7 ${this.label("crtr.setup.models.cancel")} cancel`:this.mode==="route-delete"?`${this.label("crtr.setup.confirm.accept")} delete \xB7 ${this.label("crtr.setup.confirm.back")} cancel`:this.mode==="route-form"?typeof this.routeForm?.editing=="string"?"type a value \xB7 enter save \xB7 esc cancel":`${this.label("crtr.setup.list-down")}/${this.label("crtr.setup.list-up")} field \xB7 ${this.label("crtr.setup.models.open")} edit \xB7 ${this.label("crtr.setup.models.cancel")} back`:""}renderChoicePicker(){const e=this.choice;if(!e)return[o.fg("warning","No setting selected.")];const t=[o.fg("accent",o.bold(e.title)),o.fg("dim",e.subtitle),""];for(const[i,r]of e.options.entries()){const s=i===this.choiceIndex,n=s?o.fg("accent","\u25B6"):" ",c=e.label(r);t.push(`${n} ${s?o.fg("accent",`[${c}]`):o.fg("dim",` ${c} `)}`)}return t}renderRouteForm(e){const t=this.routeForm;if(!t)return[o.fg("warning","No route selected.")];const i=this.currentTarget(),r=[o.fg("accent",o.bold(t.existing===null?"New provider route":`Provider route: ${t.existing}`)),o.fg("dim",`${i.label} \xB7 ${S(i.root,"config.json")}`),""],s=20;for(const[n,c]of t.fields.entries()){const l=n===t.index,u=l?o.fg("accent","\u25B6"):" ";let m,a;if(c.kind==="text"){if(m=c.label,a=t.text[c.key]||"\u2014",l&&t.editing===c.key){const K=Math.max(8,e-s-4);a=this.routeInput.render(K)[0]??""}}else c.kind==="provider"?(m=c.label,a=t.providerId||"\u2014"):c.kind==="cell"?(m=c.strength,a=G(t.models[c.strength])):c.kind==="save"?(m="",a="Save route"):(m="",a="Delete route");const C=a==="\u2014",T=l?o.fg("accent",`[${a}]`):o.fg(C?"warning":"dim",` ${a} `);r.push(`${u} ${f(m,s)}${T}`)}return this.notice&&r.push("",o.fg("warning",this.notice)),r}renderRouteDelete(e){const t=this.routeForm?.existing??"this route",i=this.currentTarget(),r=Oe(`This removes the entry from ${i.label}. Any "${t}" declared by a lower-precedence config becomes effective again; this does not guarantee the route is gone.`,e).map(s=>o.fg("warning",s));return[o.fg("accent",o.bold(`Delete provider route: ${t}`)),o.fg("dim",S(i.root,"config.json")),"",...r,"",o.fg("dim",`${this.label("crtr.setup.confirm.accept")} delete ${this.label("crtr.setup.confirm.back")} cancel`)]}fitBody(e,t,i){if(e.length<=i)return e;const r=Math.min(ne,e.length-i),s=e.slice(r);if(s.length<=i)return s;const n=Math.max(0,t-r),c=Math.max(0,Math.min(n-i+2,s.length-i));return s.slice(c,c+i)}render(e,t){const i=Math.max(1,t),r=y(d=>Fe(d,e),"frame");if(this.mode==="dimension-picker")return r(this.fitBody(this.renderChoicePicker(),ne+this.choiceIndex,i));if(this.mode==="model-picker")return r(U(this.modelSelector?.render(e)??[],i));if(this.mode==="thinking-picker")return r(U(this.thinkingSelector?.render(e)??[],i));if(this.mode==="route-form"){const d=3+(this.routeForm?.index??0);return r(this.fitBody(this.renderRouteForm(e),d,i))}if(this.mode==="route-delete")return r(U(this.renderRouteDelete(e),i));let s=0;const n=[o.fg("accent",o.bold("Models")),o.fg("dim","global user defaults \xB7 project/profile scopes can shadow these"),""],c=this.currentRow(),l=c.section==="ladder"&&c.kind==="strength",u=Math.max(1,e),m=Math.max(12,Math.min(20,Math.floor(u*.24))),a=this.providerKeys.length,C=Math.max(1,u-2-m),T=12,K=Math.max(1,Math.floor((C+1)/(T+1))),b=Math.min(a,K),N=Math.max(T,Math.floor((C-(b-1))/b)),J=l?Math.min(this.column,a-1):0,F=Math.max(0,Math.min(J>=b?J-b+1:0,a-b)),_=F+b,X=this.providerKeys.map((d,h)=>({provider:d,column:h})).slice(F,_),Y=F>0,z=_<a,he=Y||z?` ${o.fg("dim",`${Y?"\u2039 ":""}${F+1}\u2013${_} of ${a}${z?" \u203A":""}`)}`:"",O=this.currentTarget();n.push(`${o.fg("accent","Editing config")} ${o.fg("accent",`[${O.label}]`)}${o.fg("dim",` ${this.targetIndex+1}/${this.targets.length}`)}`),n.push(o.fg("muted",S(O.root,"config.json")),""),n.push(o.fg("accent","Strength ladders")+he);const le=q(void 0,this.ladders).provider,W=this.rows.find(d=>d.section==="ladder"&&d.kind==="defaultProvider")===c;W&&(s=n.length),n.push(`${W?o.fg("accent","\u25B6"):" "} ${f(W?o.fg("accent","Default provider"):"Default provider",20)} ${o.fg("success",le)}`);const ue=y((d,h)=>{const g=f(o.bold(ce[d]),N);return l&&this.column===h?o.fg("accent",g):o.fg("dim",g)},"providerHeading"),ae=y((d,h)=>{const g=f(V(d,Math.max(1,N-2)),Math.max(1,N-2));return h?o.fg("accent",`[${g}]`):o.fg("dim",` ${g} `)},"ladderCell");n.push(`${" ".repeat(2)}${f(o.fg("dim","Strength"),m)}${X.map(({provider:d,column:h})=>ue(d,h)).join(" ")}`);for(const d of this.rows){if(d.section!=="ladder"||d.kind!=="strength")continue;const h=d===c,g=h?o.fg("accent","\u25B6"):" ";h&&(s=n.length);const $=X.map(({provider:v,column:R})=>{const w=this.targetLadderOverride(v,d.strength)?o.fg("warning","\u2022"):" ";return ae(`${w}${G(this.ladders[v][d.strength])}`,h&&R===this.column)});n.push(`${g} ${f(d.strength,m)}${$.join(" ")}`)}n.push("",o.fg("accent","Provider routes")),n.push(o.fg("dim",`${o.fg("warning","\u2022")} override in ${O.label} \xB7 a declared route replaces the ladders above at launch \xB7 id, then family \xB7 source \xB7 provider`));const A=Math.max(10,Math.floor((u-8)/4));n.push(`${" ".repeat(4)}${k.map(d=>f(o.fg("dim",d),A)).join(" ")}`),this.routeIds().length===0&&n.push(o.fg("dim"," No routes declared \u2014 the ladders above are used."));for(const d of this.rows){if(d.section!=="route"||d.routeId===null)continue;const h=d.routeId,g=this.modelRoutes[h],$=g??this.legacyModelRoutes[h],v=d===c;v&&(s=n.length);const R=v?o.fg("accent","\u25B6"):" ",w=this.rawTarget?.modelRoutes?.[h]!==void 0?o.fg("warning","\u2022"):" ",E=g?"":o.fg("warning"," \xB7 incomplete"),D=o.fg("dim",`${$.family} \xB7 ${$.credentialSource} \xB7 ${$.providerId}`);n.push(`${R} ${w}${o.fg(v?"accent":"dim",h)} ${D}${E}`);const x=k.map(fe=>{const Z=$.models[fe],me=f(V(G(Z),A),A);return o.fg(Z===void 0?"warning":"dim",me)});n.push(`${" ".repeat(4)}${x.join(" ")}`)}const B=this.rows.find(d=>d.section==="route"&&d.routeId===null)===c;B&&(s=n.length),n.push(`${B?o.fg("accent","\u25B6"):" "} ${o.fg(B?"accent":"dim","+ New route")}`),n.push("",o.fg("accent","Kind defaults")),n.push(o.fg("dim",`${o.fg("warning","\u2022")} override in ${O.label} \xB7 orchestrator inherits base until you set it`));const H=16,I=12,M=9,Q=I+1+M,L=y((d,h,g,$)=>{const v=f(V(d,Math.max(1,h-2)),Math.max(1,h-2));return g?o.fg("accent",`[${v}]`):$?o.fg("muted",` ${v} `):o.fg("dim",` ${v} `)},"cell");n.push(`${" ".repeat(2+H)}${f(o.bold(o.fg("dim","Base")),Q)} ${f(o.bold(o.fg("dim","Orchestrator")),Q)}`),n.push(`${" ".repeat(2)}${f(o.fg("dim","Kind"),H)}${f(o.fg("dim","Family"),I)} ${f(o.fg("dim","Strength"),M)} ${f(o.fg("dim","Family"),I)} ${f(o.fg("dim","Strength"),M)}`);for(const d of this.rows){if(d.section!=="kind")continue;const h=d===c,g=h?o.fg("accent","\u25B6"):" ";h&&(s=n.length);const $=this.kinds[d.name],v=this.portableSelection($?.model),R=$?.orchestratorModel===void 0,w=this.portableSelection($?.orchestratorModel??$?.model),E=this.targetKindOverride(d.name,!1)?o.fg("warning","\u2022"):" ",D=this.targetKindOverride(d.name,!0)?o.fg("warning","\u2022"):" ",x=[L(`${E}${this.familyLabel(v.family)}`,I,h&&this.column===0,!1),L(`${E}${v.strength}`,M,h&&this.column===1,!1),L(`${D}${this.familyLabel(w.family)}`,I,h&&this.column===2,R),L(`${D}${w.strength}`,M,h&&this.column===3,R)];n.push(`${g} ${f(d.name,H)}${x[0]} ${x[1]} ${x[2]} ${x[3]}`)}return this.notice&&n.push("",o.fg("warning",this.notice)),r(this.fitBody(n,s,i))}}export{Xe as ModelsPanel};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var k=Object.defineProperty;var u=(n,e)=>k(n,"name",{value:e,configurable:!0});import{resolve as x}from"node:path";import{envNodeId as C}from"../../shared/env.js";import{cliClient as f}from"../../commands/api-client.js";import{apiErrorToCliError as j}from"../io.js";import{ApiError as L}from"../../api/index.js";import{defineBranch as h,defineLeaf as T}from"../command.js";import{resolveCommandRegistry as U}from"../command-manifests/registry.js";import{executeExternalLeaf as M}from"./transport/exec-invoke.js";import{invokeHttpLeaf as A}from"./transport/http-invoke.js";async function S(n){const e=await f().request("GET",`/v1/providers/${encodeURIComponent(n)}/tools`);return new Set(e.groups.flatMap(r=>r.leaves.map(a=>a.tool)))}u(S,"allowedProviderTools");function m(n,e,r){return n.children.some(a=>a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))}u(m,"hasAllowedLeaf");function N(n,e,r){return n.children.filter(a=>a.tier!=="hidden"&&(a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))).length}u(N,"visibleChildCount");function _(n,e,r){return async()=>{if(!C())return n.help;const a=await S(e.id);return{...n.help,listing:n.help.listing?.flatMap(t=>{const i=n.children.find(s=>s.name===t.name);if(i?.kind==="leaf")return a.has([...r,i.name].join(" "))?[t]:[];if(i?.kind!=="branch")return[];const o=[...r,i.name];if(!m(i,o,a))return[];const p=N(i,o,a);return[{...t,...t.subCount!==void 0?{subCount:p}:{}}]})}}}u(_,"providerHelp");const b="Runs on one of your devices. Add `--machine <machine_id>` to pick which; without it the call goes to your only device, and with several the answer is `machine_required` listing each one's machine_id. The `runtime:` prefix on the id is optional.";function g(n,e,r,a=!1){return{outputKind:"object",...a?{machineSelector:!0}:{},run:u(async(t,i)=>{const o={...t};for(const p of r.params){if(!("type"in p)||p.type!=="file")continue;const s=p.name.replace(/-([a-z])/g,(c,w)=>w.toUpperCase()),d=o[s];typeof d=="string"&&(o[s]=x(d))}try{return await f({timeoutMs:0}).request("POST","/v1/provider/call",{provider:n,tool:e,arguments:o,...i?.machine===void 0?{}:{machine:i.machine}},{timeout:0})}catch(p){throw p instanceof L?j(p,void 0,{providerCall:!0}):p}},"run")}}u(g,"providerLeafAdapter");function v(n,e){return/^[A-Za-z0-9_-]{1,64}$/.test(n)&&n!=="crtr"&&!e.has(n)}u(v,"validRootName");function O(n,e){const r=[],a=[];for(const t of n){const{provider:i,commands:o}=t;if(!Array.isArray(o)||t.status!=="available"&&!v(i,e))continue;if(t.status!=="available"){const s=t.error??"bundle installation pending",d=`Unavailable (${t.status}): ${s}`,c=h({name:i,rootEntry:{concept:t.display_name,desc:d,useWhen:d},help:{name:i,summary:d},children:[]});c.unavailable=d,a.push(c);continue}const p=u(s=>s.kind==="branch"?{...s,children:s.children.map(p)}:{...s,rest:{method:"GET",path:"/",params:{}}},"helpNode");for(const s of o)s.kind!=="branch"||!Array.isArray(s.children)||!v(s.name,e)||r.push({contributor:{name:i,scope:"user"},manifestName:s.name,node:p(s),adaptLeaf:u((d,c)=>g(i,c.slice(1).join(" "),d,t.transport==="machine"),"adaptLeaf")})}return[...I(U(r,e)),...a]}u(O,"composeSandboxProviderSubtrees");function y(n,e){return(r,a)=>{const t=n.transport,i=e(a);if(n.provider!==void 0)return g(n.provider.id,i.slice(1).join(" "),r,t.kind==="machine");if(t.kind==="exec"){const p=t.executable;if(p===void 0)throw new Error(`exec plugin "${n.plugin.name}" has an executable leaf without transport.executable`);return{outputKind:"object",run:u(async s=>M({plugin:n.plugin,executable:p,commandPath:i,output:r.output},s),"run")}}if(t.kind==="machine")throw new Error(`machine plugin "${n.plugin.name}" is not a capability provider`);const o=r;return{outputKind:o.rest.streaming===!0?"jsonl":"object",run:u((p,s)=>A({registration:{name:n.plugin.name,endpoint:t.endpoint,...t.authEnv!==void 0?{authEnv:t.authEnv}:{}},...n.manifest?.baseUrl!==void 0?{baseUrl:n.manifest.baseUrl}:{},...n.manifest?.timeouts!==void 0?{timeouts:n.manifest.timeouts}:{},params:o.params,rest:o.rest,output:o.output,commandPath:i},p,s),"run")}}}u(y,"adaptLeaf");function W(n){return{contributions:n.map(e=>({contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,manifestName:e.manifestName,adaptLeaf:y(e,r=>[e.manifestName,...r.slice(1)]),...e.provider!==void 0?{provider:e.provider}:{}})),coreMounts:[],issues:[]}}u(W,"adaptPluginContributions");function F(n){return n.map(e=>({parent:e.parent,plugin:e.plugin.name,contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,...e.provider!==void 0?{provider:e.provider}:{},adaptLeaf:y({...e,manifestName:e.node.name},r=>e.provider===void 0?[...r]:[e.provider.id,...r.slice(e.parent.length)])}))}u(F,"adaptPluginCoreMounts");function I(n){return n.contributions.map(e=>l(e,e.node,[e.node.name]))}u(I,"composeExternalSubtrees");function Z(n,e){return n.filter(r=>r.parent.join("\0")===e.join("\0")).map(r=>r.node.kind==="branch"?l(r,r.node,[...e,r.node.name],r.provider===void 0?1:e.length):E(r,r.node,[...e,r.node.name]))}u(Z,"composeCoreBranchMounts");function l(n,e,r,a=1){const t=e.rootEntry===void 0?void 0:{concept:e.rootEntry.concept,desc:e.rootEntry.description,useWhen:e.rootEntry.whenToUse},i=h({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...t!==void 0?{rootEntry:t}:{},help:{name:r.join(" "),summary:e.summary,...e.model!==void 0?{model:e.model}:{},...r.length===1&&n.extensionIssue!==void 0?{extensionIssue:n.extensionIssue}:{}},...e.passthrough!==void 0?{passthrough:e.passthrough}:{},children:e.children.map(o=>o.kind==="branch"?l(n,o,[...r,o.name],a):E(n,o,[...r,o.name]))});return n.provider!==void 0&&(i.effectiveHelp=_(i,n.provider,r.slice(a))),i}u(l,"buildBranch");function E(n,e,r){const a=n.adaptLeaf(e,r);return T({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...a.machineSelector===!0?{machineSelector:!0}:{},help:{name:r.join(" "),summary:e.summary,...a.machineSelector===!0?{guide:b}:{},params:e.params,output:e.output,outputKind:a.outputKind,effects:[...e.effects]},run:a.run})}u(E,"buildLeaf");export{W as adaptPluginContributions,F as adaptPluginCoreMounts,Z as composeCoreBranchMounts,I as composeExternalSubtrees,O as composeSandboxProviderSubtrees};
|
package/dist/core/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var Ue=Object.defineProperty;var s=(e,n)=>Ue(e,"name",{value:n,configurable:!0});import{envProfileId as Ke}from"../shared/env.js";import{basename as X,join as Q}from"node:path";import{existsSync as qe,mkdirSync as Ge,statSync as Be}from"node:fs";import{isDeepStrictEqual as We}from"node:util";import{CONFIG_FILE as He,STATE_FILE as Je,CONDENSED_HISTORY_MODES as Ze,MOUSE_MODE_DEFAULTS as Ye,WHIP_MESSAGE_MODES as Ve,DEFAULT_WHIP_MESSAGES as ee,defaultScopeConfig as k,defaultScopeState as ne,defaultModelLaddersConfig as Xe,defaultKindsConfig as Qe,defaultRemoteCanvasConfig as en}from"../types.js";import{BINDING_CATALOG as nn,BINDING_IDS as on,isAttachPaneBinding as tn}from"./keybindings/catalog.js";import{notFound as rn}from"./errors.js";import{emitEvent as sn}from"./events/emit.js";import{atomicWriteJson as oe,readJsonIfExists as K,ensureDir as cn}from"./fs-utils.js";import{scopeRoot as y,requireScopeRoot as w,findProjectScopeRoots as un,userScopeRoot as E}from"./scope.js";import{listInstalledPluginsInRoot as te}from"./installed-plugins.js";import{profileMemoryDir as q,listProfiles as re}from"./profiles/manifest.js";import{canonicalRoot as A,projectStore as dn,userStore as ie}from"./scoped-state/paths.js";import{convertLegacyScopedStore as fn,initializeFreshScopedStore as ln}from"./scoped-state/migrate.js";import{mutateRawSettings as an,readBookkeeping as mn,readRawSettings as pn,readRawSettingsWithRevision as gn,replaceRawSettings as yn,writeBookkeeping as hn,writeRawSettings as bn}from"./scoped-state/settings.js";import{withExclusiveDirectoryLock as _n}from"./exclusive-lock.js";import{SCOPE_CONFIG_KEYS as vn}from"./user-settings.js";import{normalizeWorkingGerunds as kn}from"../shared/working-activity.js";import{mergePageComponentLayers as wn,normalizePageComponents as se}from"./human/page-catalog.js";function b(e){return Q(e,He)}s(b,"configPathFor");function ce(e,n){return _n(`${e}.lock`,()=>n(e),{timeoutError:s(()=>new Error(`timed out waiting for config lock: ${e}`),"timeoutError")})}s(ce,"withConfigPathLock");function An(e){return Q(e,Je)}s(An,"statePathFor");function _o(e){const n=y(e);return n?b(n):null}s(_o,"configPath");function vo(e){const n=y(e);return n?An(n):null}s(vo,"statePath");function g(e){const n=A(e),o=A(E());if(n===o)return{store:ie(o),key:"user",kind:"user",root:o};if(X(n)===".crouter")return fn({kind:"project",root:n}),{store:dn(n),key:"project",kind:"project",root:n};for(const t of re()){const r=A(q(t.profileId));if(n===r)return{store:ie(o),key:`profile:${t.profileId}`,kind:"profile",root:r,profileId:t.profileId}}throw new Error(`settings root is not a recognized owner: ${e}`)}s(g,"settingsOwnerForRoot");function I(e){if(X(e)===".crouter"){let n;try{n=A(e)}catch(o){if(o.code==="ENOENT")return null;throw o}if(n!==A(E()))return K(b(e))}return pn(g(e))}s(I,"readRawAtRoot");function G(e,n){const o=g(e);if(o.kind==="project"){ce(b(o.root),t=>oe(t,n,{createParent:!1}));return}bn(o,n)}s(G,"writeRawAtRoot");function S(e,n){const o=g(e);return o.kind==="project"?ce(b(o.root),t=>{const r=n(K(t)??{});return oe(t,r.next,{createParent:!1}),r.value}):an(o,t=>n(t))}s(S,"mutateRawAtRoot");const P=globalThis,T=Symbol.for("crtr.broker.config-snapshot");function ko(e){P[T]=e}s(ko,"installBrokerConfigSnapshot");function wo(){return P[T]}s(wo,"brokerConfigSnapshot");function Sn(e){const n=P[T];if(n){if(e!=="user")throw new Error(`broker config snapshot has no ${e} scope`);return n.user}const o=y(e);return o?jn(o):k()}s(Sn,"readConfig");function jn(e){const n=I(e);return n?z(n):k()}s(jn,"readConfigAtRoot");function Rn(e){const n=y(e);if(!n)return ne();try{return mn(g(n))}catch(o){if(e==="project"&&!qe(n))return ne();throw o}}s(Rn,"readState");class xn extends Error{static{s(this,"InvalidRawSettingsError")}constructor(n){super(`settings cannot be merged: ${n instanceof Error?n.message:String(n)}`),this.name="InvalidRawSettingsError"}}function ue(e){if(e.scope==="user")return g(E());if(!re().some(n=>n.profileId===e.profileId))throw rn(`profile not found: ${e.profileId}`,{received:e.profileId,next:"Run `crtr profile list` to see available profiles."});return g(q(e.profileId))}s(ue,"rawSettingsOwnerForTarget");function Ao(e){return gn(ue(e))}s(Ao,"readRawSettingsForTarget");function So(e,n,o){return yn(ue(e),n,o,t=>{try{z(t)}catch(r){throw new xn(r)}})}s(So,"replaceRawSettingsForTarget");function jo(e,n){const o=w(e);return S(o,t=>{const r=n(t);return{value:r,next:r}})}s(jo,"updateRawConfigAtomically");function Ro(e,n){G(w(e),n)}s(Ro,"writeConfig");function xo(e,n){G(w(e),n)}s(xo,"writeRawConfig");function On(e,n){const o=w(e);hn(g(o),n)}s(On,"writeState");function Oo(e,n){if(e==="project")try{Ge(n)}catch(t){if(t.code!=="EEXIST"||!Be(n).isDirectory())throw t}else cn(n);const o=g(e==="user"?E():n);ln({kind:o.kind==="user"?"user":"project",root:o.store.root}),o.kind==="project"&&K(b(o.root))===null&&G(o.root,{schema_version:k().schema_version})}s(Oo,"ensureScopeInitialized");function En(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))Array.isArray(t)&&t.every(r=>typeof r=="string")&&(n[o]=[...t]);return n}s(En,"normalizeKeybindings");const In=new Set(nn.filter(tn).map(e=>e.id));function Pn(e){return Array.isArray(e)?[...new Set(e.filter(n=>typeof n=="string"&&on.has(n)&&In.has(n)))]:[]}s(Pn,"normalizeTmuxPassthrough");function C(e,n=Xe()){const o=n;if(e===null||typeof e!="object")return o;const t=e,r={anthropic:{...o.anthropic},openai:{...o.openai},...o.defaultProvider!==void 0?{defaultProvider:o.defaultProvider}:{}};(t.defaultProvider==="anthropic"||t.defaultProvider==="openai")&&(r.defaultProvider=t.defaultProvider);for(const i of["anthropic","openai"]){const c=t[i];if(!(c===null||typeof c!="object"))for(const[u,d]of Object.entries(c))(u==="ultra"||u==="strong"||u==="medium"||u==="light")&&typeof d=="string"&&(r[i][u]=d)}return r}s(C,"mergeModelLadders");function de(e){return e==="ultra"||e==="strong"||e==="medium"||e==="light"}s(de,"validStrength");const B=["ultra","strong","medium","light"];function W(){return Object.create(null)}s(W,"routeRecord");function j(e,n,o){Object.defineProperty(e,n,{value:o,writable:!0,enumerable:!0,configurable:!0})}s(j,"setRouteRecord");function fe(e){const n=W();for(const[o,t]of Object.entries(e??{}))j(n,o,t);return n}s(fe,"copyRouteRecord");function R(e,n){sn({level:"warn",event:"config.model_routes.invalid",fields:{route_id:e,rule:n}})}s(R,"routeInvalid");function $(e,n={}){const o=fe(n);if(e===null||typeof e!="object"||Array.isArray(e))return o;for(const[t,r]of Object.entries(e)){if(t.trim()===""){R(t,"route id must be a non-empty string");continue}if(r===null||typeof r!="object"||Array.isArray(r)){R(t,"route must be an object");continue}const i=r,c=o[t],u={...c};let d;for(const l of["family","credentialSource","providerId"]){const f=i[l];if(f!==void 0){if(typeof f!="string"||f.trim()===""){d=`${l} must be a non-empty string`;break}u[l]=f}}if(d===void 0&&i.models!==void 0)if(i.models===null||typeof i.models!="object"||Array.isArray(i.models))d="models must be an object";else{const l={...c?.models};for(const[f,a]of Object.entries(i.models)){if(!de(f)){d=`models.${f} is not a model strength`;break}if(typeof a!="string"||a.trim()===""){d=`models.${f} must be a non-empty provider/id ref`;break}const m=a.indexOf("/");if(m<=0||m===a.length-1){d=`models.${f} must be provider/id`;break}l[f]=a}u.models=l}if(d===void 0&&u.models!==void 0&&u.providerId!==void 0){for(const[l,f]of Object.entries(u.models))if(f.slice(0,f.indexOf("/"))!==u.providerId){d=`models.${l} provider prefix must equal providerId`;break}}if(d!==void 0){R(t,d);continue}j(o,t,u)}return o}s($,"mergeModelRoutePatches");function le(e){const n=W(),o=W();for(const[t,r]of Object.entries(e)){if(r.family===void 0||r.credentialSource===void 0||r.providerId===void 0||r.models===void 0){R(t,"route must include family, credentialSource, providerId, and models");continue}const i=B.filter(c=>r.models[c]===void 0);if(i.length>0){R(t,`incomplete route; missing strengths: ${i.join(", ")}`),j(o,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models});continue}j(n,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models})}return{complete:n,legacy:o}}s(le,"resolveModelRoutes");function Tn(e,n){if(e.trim()==="")throw new Error("model route id must be a non-empty string");for(const t of["family","credentialSource","providerId"])if(typeof n[t]!="string"||n[t].trim()==="")throw new Error(`model route "${e}" ${t} must be a non-empty string`);const o=B.filter(t=>n.models?.[t]===void 0);if(o.length>0)throw new Error(`model route "${e}" is incomplete; missing strengths: ${o.join(", ")}`);for(const t of B){const r=n.models[t];if(typeof r!="string"||r.trim()==="")throw new Error(`model route "${e}" models.${t} must be a non-empty provider/id ref`);const i=r.indexOf("/");if(i<=0||i===r.length-1)throw new Error(`model route "${e}" models.${t} must be provider/id`);if(r.slice(0,i)!==n.providerId)throw new Error(`model route "${e}" models.${t} provider prefix must equal providerId`)}}s(Tn,"assertCompleteModelRoute");function M(e,n){if(e===void 0||e===null||typeof e!="object"||Array.isArray(e))return n;const o=e,t=s(i=>Array.isArray(i)&&i.every(c=>typeof c=="string"&&c.trim()!=="")?[...i]:void 0,"strings"),r=Array.isArray(o.strengthFallback)&&o.strengthFallback.every(i=>de(i))?[...o.strengthFallback]:void 0;return{...t(o.credentialSourceOrder)!==void 0?{credentialSourceOrder:t(o.credentialSourceOrder)}:{},...t(o.familyOrder)!==void 0?{familyOrder:t(o.familyOrder)}:{},...r!==void 0?{strengthFallback:r}:{}}}s(M,"mergeModelRouting");function Cn(e){return e!==""&&e.replace(/\*/g,"")===""}s(Cn,"isInheritAllGlob");function N(e,n=[]){const o=e,t=Array.isArray(o?.allow)?o.allow.filter(r=>typeof r=="string"):[];for(const r of t)if(Cn(r))throw new Error(`spawnEnv.allow: "${r}" matches every env var name (inherit-all) \u2014 this is exactly the leak the spawn-env boundary closes. Name the specific vars/prefixes you actually need instead (e.g. "AWS_*", "GIT_ASKPASS").`);return{allow:[...new Set([...n,...t])]}}s(N,"mergeSpawnEnv");function L(e,n={}){const o={};for(const[t,r]of Object.entries(n))o[t]={...r};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){if(r===null||typeof r!="object")continue;const i={...o[t]},c=r.fastMode;typeof c=="boolean"&&(i.fastMode=c),o[t]=i}return o}s(L,"mergeProviderOptions");const ae=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,$n=new Set(["crtr","crtrd"]);function F(e,n){if(n==="bin"&&$n.has(e))return`${n}.${e} is reserved by crouter \u2014 pick another bare command name`;if(!ae.test(e))return`${n}.${e} is not a safe bare command name (expected ${ae.source})`}s(F,"bareCommandNameIssue");function Mn(e){if(e===null||typeof e!="object"||Array.isArray(e))return[{name:"bin",message:"bin must be a JSON object mapping bare command name to a relative path"}];const n=[];for(const[o,t]of Object.entries(e)){const r=F(o,"bin");r!==void 0&&n.push({name:o,message:r}),(typeof t!="string"||t.trim()==="")&&n.push({name:o,message:`bin.${o} must be a non-empty relative path to an executable inside the contributor root`})}return n}s(Mn,"binDeclarationIssues");function Nn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))F(o,"bin")===void 0&&(typeof t!="string"||t.trim()===""||(n[o]=t));return n}s(Nn,"normalizeBin");const me=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function Ln(e){if(e===null||typeof e!="object"||Array.isArray(e))return[{name:"humanActions",reason:"humanActions must be a JSON object mapping action names to {argv, cwd}",block:!0}];const n=[];for(const[o,t]of Object.entries(e)){const r=[];if(me.test(o)||r.push(`action name must match ${me.source}`),t===null||typeof t!="object"||Array.isArray(t))r.push("action must be an object with argv and cwd");else{const i=t,c=Object.keys(i).filter(u=>u!=="argv"&&u!=="cwd");c.length>0&&r.push(`action has unsupported field${c.length===1?"":"s"}: ${c.join(", ")}`),(!Array.isArray(i.argv)||i.argv.length===0||!i.argv.every(u=>typeof u=="string"&&u.length>0))&&r.push("argv must be a non-empty array of non-empty strings"),(typeof i.cwd!="string"||i.cwd.length===0)&&r.push("cwd is required and must be a non-empty string")}r.length>0&&n.push({name:o,reason:r.join("; ")})}return n}s(Ln,"humanActionDeclarationIssues");function Fn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n=new Set(Ln(e).map(t=>t.name)),o={};for(const[t,r]of Object.entries(e)){if(n.has(t))continue;const i=r;o[t]={argv:[...i.argv],cwd:i.cwd}}return o}s(Fn,"normalizeHumanActions");function Eo(e){return Mn(e).map(n=>n.message)}s(Eo,"invalidPluginBinReasons");function Io(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))F(o,"requires")===void 0&&(typeof t!="string"||t.trim()===""||/[\r\n]/.test(t)||(n[o]=t));return n}s(Io,"normalizeRequires");function Po(e){if(e===null||typeof e!="object"||Array.isArray(e))return["requires must be a JSON object mapping a bare executable name to a one-line install hint"];const n=[];for(const[o,t]of Object.entries(e)){const r=F(o,"requires");r!==void 0&&n.push(r),typeof t!="string"||t.trim()===""?n.push(`requires.${o} must be a non-empty one-line install hint`):/[\r\n]/.test(t)&&n.push(`requires.${o} must be a one-line install hint`)}return n}s(Po,"invalidPluginRequiresReasons");function pe(e){if(e===null||typeof e!="object")return null;const n=e,o={};typeof n.whenToUse=="string"&&n.whenToUse.trim()!==""&&(o.whenToUse=n.whenToUse),typeof n.model=="string"&&n.model.trim()!==""&&(o.model=n.model),typeof n.orchestratorModel=="string"&&n.orchestratorModel.trim()!==""&&(o.orchestratorModel=n.orchestratorModel);const t=s(r=>Array.isArray(r)&&r.every(i=>typeof i=="string"),"isStringArray");return t(n.tools)&&(o.tools=n.tools),t(n.extensions)&&(o.extensions=n.extensions),t(n.availableTo)&&(o.availableTo=n.availableTo),Object.keys(o).length>0?o:null}s(pe,"normalizeKindFields");function x(e,n=Qe()){const o={...n};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){const i=pe(r);if(i===null)continue;const c=o[t];c!==void 0?o[t]={...c,...i}:i.whenToUse!==void 0&&(o[t]=i)}return o}s(x,"mergeKinds");function ge(e,n,o){if(n===null)return o;const t=te(e,n).filter(i=>i.enabled&&i.manifest.kinds!==void 0).sort((i,c)=>i.name.localeCompare(c.name));let r=o;for(const i of t)r=x(i.manifest.kinds,r);return r}s(ge,"layerPluginKinds");function To(){const e=[{origin:"config.json",components:Sn("user").page_components}],n=y("user");if(n!==null){const o=te("user",n).filter(t=>t.enabled&&t.manifest.page_components!==void 0).sort((t,r)=>t.name.localeCompare(r.name));for(const t of o){const r=`plugin "${t.name}" page_components`;e.push({origin:`plugin "${t.name}"`,components:se(t.manifest.page_components,r)})}}return wn(e)}s(To,"resolvePageComponents");const H=new Set(["whenToUse","model","orchestratorModel","tools","extensions","availableTo"]);function Co(e){if(e===null||typeof e!="object"||Array.isArray(e))return["kinds must be a JSON object keyed by kind name"];const n=[],o=s(t=>Array.isArray(t)&&t.every(r=>typeof r=="string"),"isStringArray");for(const[t,r]of Object.entries(e)){if(t.trim()===""){n.push("kinds contains an empty kind name");continue}if(r===null||typeof r!="object"||Array.isArray(r)){n.push(`kinds.${t} must be a JSON object`);continue}const i=r;for(const c of Object.keys(i))H.has(c)||n.push(`kinds.${t}.${c} is not a KindConfig field (expected one of: ${[...H].join(", ")})`);i.whenToUse!==void 0&&(typeof i.whenToUse!="string"||i.whenToUse.trim()==="")&&n.push(`kinds.${t}.whenToUse must be a non-empty string`);for(const c of["model","orchestratorModel"])i[c]!==void 0&&(typeof i[c]!="string"||i[c].trim()==="")&&n.push(`kinds.${t}.${c} must be a non-empty string`);for(const c of["tools","extensions","availableTo"])i[c]!==void 0&&!o(i[c])&&n.push(`kinds.${t}.${c} must be an array of strings`);pe(r)===null&&n.push(`kinds.${t} has no valid KindConfig field \u2014 an entry must set at least one of: ${[...H].join(", ")} (whenToUse is required when the kind exists in no lower layer)`)}return n}s(Co,"invalidPluginKindsReasons");function zn(e){if(e===null||typeof e!="object")return null;const n=e;if(typeof n.previewEndpoint!="string"||n.previewEndpoint.trim()===""||typeof n.relayTokenRef!="string"||n.relayTokenRef.trim()==="")return null;const o={previewEndpoint:n.previewEndpoint,relayTokenRef:n.relayTokenRef};return typeof n.cpOrigin=="string"&&n.cpOrigin.trim()!==""&&(o.cpOrigin=n.cpOrigin),o}s(zn,"normalizeRemoteCanvasTarget");function Dn(e,n=en()){const o={...n.targets};if(e!==null&&typeof e=="object"){const t=e;if(t.targets!==null&&typeof t.targets=="object")for(const[r,i]of Object.entries(t.targets)){const c=zn(i);c!==null&&(o[r]=c)}}return{targets:o}}s(Dn,"mergeRemoteCanvas");function ye(e,n){return e===!0?"notify":e===!1?!1:e==="notify"||e==="apply"?e:n}s(ye,"normalizeMode");function O(e,n){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=1?e:n}s(O,"normalizePositiveInteger");function Un(e,n){const o=e!==null&&typeof e=="object"?e:{},t=O(o.warning,n.warning),r=O(o.automaticReviveCap,n.automaticReviveCap);return t<=r?{warning:t,automaticReviveCap:r}:{...n}}s(Un,"mergeBrokerThresholds");function Kn(e,n){return{unattendedParkMs:O((e!==null&&typeof e=="object"?e:{}).unattendedParkMs,n.unattendedParkMs)}}s(Kn,"mergeLifecycle");function qn(e){if(!Array.isArray(e))return[...ee];const n=e.filter(o=>typeof o=="string"&&o.trim()!=="").map(o=>o.trim());return n.length>0?n:[...ee]}s(qn,"normalizeWhipMessages");function Gn(e,n){if(e===null||typeof e!="object")return{...n};const o=e.tcp;return{tcp:typeof o=="string"?o:n.tcp}}s(Gn,"mergeApi");function z(e){const n=k(),o=n.schema_version,t=e.marketplaces===void 0?{}:e.marketplaces,r=e.plugins===void 0?{}:e.plugins,i=e.auto_update,c=i&&typeof i.interval_hours=="number"?i.interval_hours:void 0,u=c!==void 0&&Number.isFinite(c)&&c>=0?c:n.auto_update.interval_hours,d={crtr:ye(i?.crtr,n.auto_update.crtr),content:ye(i?.content,n.auto_update.content),interval_hours:u},l=e.max_panes_per_window,f=O(l,n.max_panes_per_window),a=typeof e.auto_open_child_viewers=="boolean"?e.auto_open_child_viewers:n.auto_open_child_viewers,m=Un(e.brokerThresholds,n.brokerThresholds),h=Kn(e.lifecycle,n.lifecycle),D=typeof e.completion_bell=="boolean"?e.completion_bell:n.completion_bell,p=e.working_gerunds===void 0?[...n.working_gerunds]:kn(e.working_gerunds),be=typeof e.whip_mode=="boolean"?e.whip_mode:n.whip_mode,_e=se(e.page_components),ve=typeof e.page_surface=="boolean"?e.page_surface:n.page_surface,ke=qn(e.whip_messages),we=Ve.includes(e.whip_message_mode)?e.whip_message_mode:n.whip_message_mode,Ae=Ye.includes(e.mouse_mode_default)?e.mouse_mode_default:n.mouse_mode_default,Se=O(e.live_cycles,n.live_cycles),je=Ze.includes(e.condensed_history)?e.condensed_history:n.condensed_history,Re=typeof e.bash_tool_purpose=="boolean"?e.bash_tool_purpose:n.bash_tool_purpose,xe=typeof e.fold_finished_tools=="boolean"?e.fold_finished_tools:n.fold_finished_tools,Oe=typeof e.summarize_tool_calls=="boolean"?e.summarize_tool_calls:n.summarize_tool_calls,Ee=typeof e.detailed_tool_recaps=="boolean"?e.detailed_tool_recaps:n.detailed_tool_recaps,Ie=typeof e.consolidate_replies=="boolean"?e.consolidate_replies:n.consolidate_replies,Pe=En(e.keybindings),Te=Pn(e.tmux_passthrough),Ce=C(e.modelLadders),$e=L(e.providerOptions,n.providerOptions),U=le($(e.modelRoutes)),J=U.complete,Z=M(e.modelRouting,void 0),Me=x(e.kinds),Ne=Dn(e.remoteCanvas),Le=Gn(e.api,n.api),_=e.paths;if(_?.user_files!==void 0&&(typeof _.user_files!="string"||!_.user_files.startsWith("/")))throw new Error("paths.user_files must be an absolute path");const Y=e.store,v=e.runtime;if(v?.person_uids!==void 0&&(!Array.isArray(v.person_uids)||!v.person_uids.every(V=>Number.isSafeInteger(V)&&V>=0)))throw new Error("runtime.person_uids must be an array of nonnegative integer uids");const Fe=N(e.spawnEnv,n.spawnEnv?.allow),ze=Nn(e.bin),De=Fn(e.humanActions);return{bin:ze,humanActions:De,schema_version:o,marketplaces:t,plugins:r,auto_update:d,max_panes_per_window:f,auto_open_child_viewers:a,brokerThresholds:m,lifecycle:h,completion_bell:D,working_gerunds:p,whip_mode:be,page_components:_e,page_surface:ve,whip_messages:ke,whip_message_mode:we,mouse_mode_default:Ae,live_cycles:Se,condensed_history:je,bash_tool_purpose:Re,fold_finished_tools:xe,summarize_tool_calls:Oe,detailed_tool_recaps:Ee,consolidate_replies:Ie,keybindings:Pe,tmux_passthrough:Te,modelLadders:Ce,providerOptions:$e,...Object.keys(J).length>0?{modelRoutes:J}:{},...Object.keys(U.legacy).length>0?{legacyModelRoutes:U.legacy}:{},...Z!==void 0?{modelRouting:Z}:{},kinds:Me,remoteCanvas:Ne,api:Le,..._?{paths:_}:{},...Y?{store:Y}:{},...v?{runtime:v}:{},spawnEnv:Fe}}s(z,"mergeConfig");function Bn(e){return I(e)}s(Bn,"readRawConfigAtRoot");function Wn(e){const n=y(e);return n?Bn(n):null}s(Wn,"readRawScopeConfig");function Hn(e){if(e===null||e==="")return null;try{return I(q(e))}catch{return null}}s(Hn,"readRawProfileConfig");function Jn(e,n){return[...un(e,n)].reverse().map(r=>({root:r,raw:I(r)}))}s(Jn,"readRawProjectScopeConfigs");function he(e=process.cwd(),n=Ke()||null){const o=P[T];if(o)return o.launch;const t=k(),r=Wn("user"),i=Hn(n),c=Jn(e,n);let u=x(r?.kinds,ge("user",y("user"),t.kinds)),d=C(r?.modelLadders,t.modelLadders),l=L(r?.providerOptions,t.providerOptions),f=$(r?.modelRoutes),a=M(r?.modelRouting,void 0),m=N(r?.spawnEnv,t.spawnEnv?.allow);i!==null&&(u=x(i.kinds,u),d=C(i.modelLadders,d),l=L(i.providerOptions,l),f=$(i.modelRoutes,f),a=M(i.modelRouting,a),m=N(i.spawnEnv,m.allow));for(const{root:D,raw:p}of c)u=x(p?.kinds,ge("project",D,u)),p!==null&&(d=C(p.modelLadders,d),l=L(p.providerOptions,l),f=$(p.modelRoutes,f),a=M(p.modelRouting,a),m=N(p.spawnEnv,m.allow));const h=le(f);return{kinds:u,modelLadders:d,providerOptions:l,...Object.keys(h.complete).length>0?{modelRoutes:h.complete}:{},...Object.keys(h.legacy).length>0?{legacyModelRoutes:h.legacy}:{},...a!==void 0?{modelRouting:a}:{},spawnEnv:m}}s(he,"readMergedLaunchConfig");function $o(e){return he().kinds[e]}s($o,"resolveKindConfig");function Mo(e){const n=he().kinds,o=[];for(const[t,r]of Object.entries(n)){const i=t.indexOf("/");if(i===-1)continue;const c=t.slice(0,i),u=r.availableTo??[c];(u.includes("*")||u.includes(e))&&o.push({kind:t,whenToUse:r.whenToUse})}return o.sort((t,r)=>t.kind.localeCompare(r.kind))}s(Mo,"subKindsAvailableTo");function Zn(e,n,o){const t={...e??{}};t.anthropic!==void 0&&(t.anthropic={...t.anthropic}),t.openai!==void 0&&(t.openai={...t.openai}),o.defaultProvider!==n.defaultProvider&&o.defaultProvider!==void 0&&(t.defaultProvider=o.defaultProvider);for(const i of["anthropic","openai"])for(const c of["ultra","strong","medium","light"])o[i][c]!==n[i][c]&&(t[i]={...t[i]??{},[c]:o[i][c]});return t.defaultProvider!==void 0||t.anthropic!==void 0||t.openai!==void 0?t:void 0}s(Zn,"sparseModelLaddersToPersist");function Yn(e,n){return S(e,o=>{const t=z(o),r=z(o);n(r);const i={...o};for(const u of vn)u!=="modelLadders"&&!We(r[u],t[u])&&(i[u]=r[u]);const c=Zn(o.modelLadders,t.modelLadders,r.modelLadders);return c!==void 0?i.modelLadders=c:delete i.modelLadders,{value:r,next:i}})}s(Yn,"mutateConfigAtRoot");function No(e,n){S(e,o=>{const t=o.modelLadders??{},r={...t};n.defaultProvider!==void 0&&(r.defaultProvider=n.defaultProvider);for(const i of["anthropic","openai"])n[i]!==void 0&&(r[i]={...t[i]??{},...n[i]});return{value:void 0,next:{...o,modelLadders:r}}})}s(No,"persistModelLadders");function Lo(e,n,o){if(o!==void 0)Tn(n,o);else if(n.trim()==="")throw new Error("model route id must be a non-empty string");S(e,t=>{const r=fe(t.modelRoutes??{});o===void 0?delete r[n]:j(r,n,{...o,models:{...o.models}});const i={...t};return Object.keys(r).length===0?delete i.modelRoutes:i.modelRoutes=r,{value:void 0,next:i}})}s(Lo,"persistModelRoute");function Vn(e,n){return Yn(e,n)}s(Vn,"updateConfigAtRoot");function Fo(e,n){return Vn(w(e),n)}s(Fo,"updateConfig");function zo(e){const n=e.orchestrator===!0?"orchestratorModel":"model";return S(e.root,o=>{const t={...o.kinds??{},[e.kind]:{...(o.kinds??{})[e.kind],[n]:e.model}};return{value:void 0,next:{...o,kinds:t}}}),{path:b(e.root),field:n}}s(zo,"persistDefaultKindModel");function Do(e,n){const o=Rn(e);return n(o),On(e,o),o}s(Do,"updateState");export{xn as InvalidRawSettingsError,Mn as binDeclarationIssues,wo as brokerConfigSnapshot,_o as configPath,Oo as ensureScopeInitialized,Ln as humanActionDeclarationIssues,ko as installBrokerConfigSnapshot,Eo as invalidPluginBinReasons,Co as invalidPluginKindsReasons,Po as invalidPluginRequiresReasons,Nn as normalizeBin,Fn as normalizeHumanActions,Io as normalizeRequires,Pn as normalizeTmuxPassthrough,zo as persistDefaultKindModel,No as persistModelLadders,Lo as persistModelRoute,Sn as readConfig,jn as readConfigAtRoot,he as readMergedLaunchConfig,Bn as readRawConfigAtRoot,Wn as readRawScopeConfig,Ao as readRawSettingsForTarget,Rn as readState,So as replaceRawSettingsForTarget,$o as resolveKindConfig,To as resolvePageComponents,vo as statePath,Mo as subKindsAvailableTo,Fo as updateConfig,Vn as updateConfigAtRoot,jo as updateRawConfigAtomically,Do as updateState,Ro as writeConfig,xo as writeRawConfig,On as writeState};
|
|
1
|
+
var Ue=Object.defineProperty;var s=(e,n)=>Ue(e,"name",{value:n,configurable:!0});import{envProfileId as Ke}from"../shared/env.js";import{basename as X,join as Q}from"node:path";import{existsSync as qe,mkdirSync as Ge,statSync as Be}from"node:fs";import{isDeepStrictEqual as We}from"node:util";import{CONFIG_FILE as He,STATE_FILE as Je,CONDENSED_HISTORY_MODES as Ze,MOUSE_MODE_DEFAULTS as Ye,WHIP_MESSAGE_MODES as Ve,DEFAULT_WHIP_MESSAGES as ee,defaultScopeConfig as k,defaultScopeState as ne,defaultModelLaddersConfig as Xe,defaultKindsConfig as Qe,defaultRemoteCanvasConfig as en}from"../types.js";import{BINDING_CATALOG as nn,BINDING_IDS as on,isAttachPaneBinding as tn}from"./keybindings/catalog.js";import{notFound as rn}from"./errors.js";import{emitEvent as sn}from"./events/emit.js";import{atomicWriteJson as oe,readJsonIfExists as K,ensureDir as cn}from"./fs-utils.js";import{scopeRoot as y,requireScopeRoot as w,findProjectScopeRoots as un,userScopeRoot as E}from"./scope.js";import{listInstalledPluginsInRoot as te}from"./installed-plugins.js";import{profileDir as q,listProfiles as re}from"./profiles/manifest.js";import{canonicalRoot as A,projectStore as dn,userStore as ie}from"./scoped-state/paths.js";import{convertLegacyScopedStore as fn,initializeFreshScopedStore as ln}from"./scoped-state/migrate.js";import{mutateRawSettings as an,readBookkeeping as mn,readRawSettings as pn,readRawSettingsWithRevision as gn,replaceRawSettings as yn,writeBookkeeping as hn,writeRawSettings as bn}from"./scoped-state/settings.js";import{withExclusiveDirectoryLock as _n}from"./exclusive-lock.js";import{SCOPE_CONFIG_KEYS as vn}from"./user-settings.js";import{normalizeWorkingGerunds as kn}from"../shared/working-activity.js";import{mergePageComponentLayers as wn,normalizePageComponents as se}from"./human/page-catalog.js";function b(e){return Q(e,He)}s(b,"configPathFor");function ce(e,n){return _n(`${e}.lock`,()=>n(e),{timeoutError:s(()=>new Error(`timed out waiting for config lock: ${e}`),"timeoutError")})}s(ce,"withConfigPathLock");function An(e){return Q(e,Je)}s(An,"statePathFor");function _o(e){const n=y(e);return n?b(n):null}s(_o,"configPath");function vo(e){const n=y(e);return n?An(n):null}s(vo,"statePath");function g(e){const n=A(e),o=A(E());if(n===o)return{store:ie(o),key:"user",kind:"user",root:o};if(X(n)===".crouter")return fn({kind:"project",root:n}),{store:dn(n),key:"project",kind:"project",root:n};for(const t of re()){const r=A(q(t.profileId));if(n===r)return{store:ie(o),key:`profile:${t.profileId}`,kind:"profile",root:r,profileId:t.profileId}}throw new Error(`settings root is not a recognized owner: ${e}`)}s(g,"settingsOwnerForRoot");function I(e){if(X(e)===".crouter"){let n;try{n=A(e)}catch(o){if(o.code==="ENOENT")return null;throw o}if(n!==A(E()))return K(b(e))}return pn(g(e))}s(I,"readRawAtRoot");function G(e,n){const o=g(e);if(o.kind==="project"){ce(b(o.root),t=>oe(t,n,{createParent:!1}));return}bn(o,n)}s(G,"writeRawAtRoot");function S(e,n){const o=g(e);return o.kind==="project"?ce(b(o.root),t=>{const r=n(K(t)??{});return oe(t,r.next,{createParent:!1}),r.value}):an(o,t=>n(t))}s(S,"mutateRawAtRoot");const P=globalThis,T=Symbol.for("crtr.broker.config-snapshot");function ko(e){P[T]=e}s(ko,"installBrokerConfigSnapshot");function wo(){return P[T]}s(wo,"brokerConfigSnapshot");function Sn(e){const n=P[T];if(n){if(e!=="user")throw new Error(`broker config snapshot has no ${e} scope`);return n.user}const o=y(e);return o?jn(o):k()}s(Sn,"readConfig");function jn(e){const n=I(e);return n?z(n):k()}s(jn,"readConfigAtRoot");function Rn(e){const n=y(e);if(!n)return ne();try{return mn(g(n))}catch(o){if(e==="project"&&!qe(n))return ne();throw o}}s(Rn,"readState");class xn extends Error{static{s(this,"InvalidRawSettingsError")}constructor(n){super(`settings cannot be merged: ${n instanceof Error?n.message:String(n)}`),this.name="InvalidRawSettingsError"}}function ue(e){if(e.scope==="user")return g(E());if(!re().some(n=>n.profileId===e.profileId))throw rn(`profile not found: ${e.profileId}`,{received:e.profileId,next:"Run `crtr profile list` to see available profiles."});return g(q(e.profileId))}s(ue,"rawSettingsOwnerForTarget");function Ao(e){return gn(ue(e))}s(Ao,"readRawSettingsForTarget");function So(e,n,o){return yn(ue(e),n,o,t=>{try{z(t)}catch(r){throw new xn(r)}})}s(So,"replaceRawSettingsForTarget");function jo(e,n){const o=w(e);return S(o,t=>{const r=n(t);return{value:r,next:r}})}s(jo,"updateRawConfigAtomically");function Ro(e,n){G(w(e),n)}s(Ro,"writeConfig");function xo(e,n){G(w(e),n)}s(xo,"writeRawConfig");function On(e,n){const o=w(e);hn(g(o),n)}s(On,"writeState");function Oo(e,n){if(e==="project")try{Ge(n)}catch(t){if(t.code!=="EEXIST"||!Be(n).isDirectory())throw t}else cn(n);const o=g(e==="user"?E():n);ln({kind:o.kind==="user"?"user":"project",root:o.store.root}),o.kind==="project"&&K(b(o.root))===null&&G(o.root,{schema_version:k().schema_version})}s(Oo,"ensureScopeInitialized");function En(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))Array.isArray(t)&&t.every(r=>typeof r=="string")&&(n[o]=[...t]);return n}s(En,"normalizeKeybindings");const In=new Set(nn.filter(tn).map(e=>e.id));function Pn(e){return Array.isArray(e)?[...new Set(e.filter(n=>typeof n=="string"&&on.has(n)&&In.has(n)))]:[]}s(Pn,"normalizeTmuxPassthrough");function C(e,n=Xe()){const o=n;if(e===null||typeof e!="object")return o;const t=e,r={anthropic:{...o.anthropic},openai:{...o.openai},...o.defaultProvider!==void 0?{defaultProvider:o.defaultProvider}:{}};(t.defaultProvider==="anthropic"||t.defaultProvider==="openai")&&(r.defaultProvider=t.defaultProvider);for(const i of["anthropic","openai"]){const c=t[i];if(!(c===null||typeof c!="object"))for(const[u,d]of Object.entries(c))(u==="ultra"||u==="strong"||u==="medium"||u==="light")&&typeof d=="string"&&(r[i][u]=d)}return r}s(C,"mergeModelLadders");function de(e){return e==="ultra"||e==="strong"||e==="medium"||e==="light"}s(de,"validStrength");const B=["ultra","strong","medium","light"];function W(){return Object.create(null)}s(W,"routeRecord");function j(e,n,o){Object.defineProperty(e,n,{value:o,writable:!0,enumerable:!0,configurable:!0})}s(j,"setRouteRecord");function fe(e){const n=W();for(const[o,t]of Object.entries(e??{}))j(n,o,t);return n}s(fe,"copyRouteRecord");function R(e,n){sn({level:"warn",event:"config.model_routes.invalid",fields:{route_id:e,rule:n}})}s(R,"routeInvalid");function $(e,n={}){const o=fe(n);if(e===null||typeof e!="object"||Array.isArray(e))return o;for(const[t,r]of Object.entries(e)){if(t.trim()===""){R(t,"route id must be a non-empty string");continue}if(r===null||typeof r!="object"||Array.isArray(r)){R(t,"route must be an object");continue}const i=r,c=o[t],u={...c};let d;for(const l of["family","credentialSource","providerId"]){const f=i[l];if(f!==void 0){if(typeof f!="string"||f.trim()===""){d=`${l} must be a non-empty string`;break}u[l]=f}}if(d===void 0&&i.models!==void 0)if(i.models===null||typeof i.models!="object"||Array.isArray(i.models))d="models must be an object";else{const l={...c?.models};for(const[f,a]of Object.entries(i.models)){if(!de(f)){d=`models.${f} is not a model strength`;break}if(typeof a!="string"||a.trim()===""){d=`models.${f} must be a non-empty provider/id ref`;break}const m=a.indexOf("/");if(m<=0||m===a.length-1){d=`models.${f} must be provider/id`;break}l[f]=a}u.models=l}if(d===void 0&&u.models!==void 0&&u.providerId!==void 0){for(const[l,f]of Object.entries(u.models))if(f.slice(0,f.indexOf("/"))!==u.providerId){d=`models.${l} provider prefix must equal providerId`;break}}if(d!==void 0){R(t,d);continue}j(o,t,u)}return o}s($,"mergeModelRoutePatches");function le(e){const n=W(),o=W();for(const[t,r]of Object.entries(e)){if(r.family===void 0||r.credentialSource===void 0||r.providerId===void 0||r.models===void 0){R(t,"route must include family, credentialSource, providerId, and models");continue}const i=B.filter(c=>r.models[c]===void 0);if(i.length>0){R(t,`incomplete route; missing strengths: ${i.join(", ")}`),j(o,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models});continue}j(n,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models})}return{complete:n,legacy:o}}s(le,"resolveModelRoutes");function Tn(e,n){if(e.trim()==="")throw new Error("model route id must be a non-empty string");for(const t of["family","credentialSource","providerId"])if(typeof n[t]!="string"||n[t].trim()==="")throw new Error(`model route "${e}" ${t} must be a non-empty string`);const o=B.filter(t=>n.models?.[t]===void 0);if(o.length>0)throw new Error(`model route "${e}" is incomplete; missing strengths: ${o.join(", ")}`);for(const t of B){const r=n.models[t];if(typeof r!="string"||r.trim()==="")throw new Error(`model route "${e}" models.${t} must be a non-empty provider/id ref`);const i=r.indexOf("/");if(i<=0||i===r.length-1)throw new Error(`model route "${e}" models.${t} must be provider/id`);if(r.slice(0,i)!==n.providerId)throw new Error(`model route "${e}" models.${t} provider prefix must equal providerId`)}}s(Tn,"assertCompleteModelRoute");function M(e,n){if(e===void 0||e===null||typeof e!="object"||Array.isArray(e))return n;const o=e,t=s(i=>Array.isArray(i)&&i.every(c=>typeof c=="string"&&c.trim()!=="")?[...i]:void 0,"strings"),r=Array.isArray(o.strengthFallback)&&o.strengthFallback.every(i=>de(i))?[...o.strengthFallback]:void 0;return{...t(o.credentialSourceOrder)!==void 0?{credentialSourceOrder:t(o.credentialSourceOrder)}:{},...t(o.familyOrder)!==void 0?{familyOrder:t(o.familyOrder)}:{},...r!==void 0?{strengthFallback:r}:{}}}s(M,"mergeModelRouting");function Cn(e){return e!==""&&e.replace(/\*/g,"")===""}s(Cn,"isInheritAllGlob");function N(e,n=[]){const o=e,t=Array.isArray(o?.allow)?o.allow.filter(r=>typeof r=="string"):[];for(const r of t)if(Cn(r))throw new Error(`spawnEnv.allow: "${r}" matches every env var name (inherit-all) \u2014 this is exactly the leak the spawn-env boundary closes. Name the specific vars/prefixes you actually need instead (e.g. "AWS_*", "GIT_ASKPASS").`);return{allow:[...new Set([...n,...t])]}}s(N,"mergeSpawnEnv");function L(e,n={}){const o={};for(const[t,r]of Object.entries(n))o[t]={...r};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){if(r===null||typeof r!="object")continue;const i={...o[t]},c=r.fastMode;typeof c=="boolean"&&(i.fastMode=c),o[t]=i}return o}s(L,"mergeProviderOptions");const ae=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,$n=new Set(["crtr","crtrd"]);function F(e,n){if(n==="bin"&&$n.has(e))return`${n}.${e} is reserved by crouter \u2014 pick another bare command name`;if(!ae.test(e))return`${n}.${e} is not a safe bare command name (expected ${ae.source})`}s(F,"bareCommandNameIssue");function Mn(e){if(e===null||typeof e!="object"||Array.isArray(e))return[{name:"bin",message:"bin must be a JSON object mapping bare command name to a relative path"}];const n=[];for(const[o,t]of Object.entries(e)){const r=F(o,"bin");r!==void 0&&n.push({name:o,message:r}),(typeof t!="string"||t.trim()==="")&&n.push({name:o,message:`bin.${o} must be a non-empty relative path to an executable inside the contributor root`})}return n}s(Mn,"binDeclarationIssues");function Nn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))F(o,"bin")===void 0&&(typeof t!="string"||t.trim()===""||(n[o]=t));return n}s(Nn,"normalizeBin");const me=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function Ln(e){if(e===null||typeof e!="object"||Array.isArray(e))return[{name:"humanActions",reason:"humanActions must be a JSON object mapping action names to {argv, cwd}",block:!0}];const n=[];for(const[o,t]of Object.entries(e)){const r=[];if(me.test(o)||r.push(`action name must match ${me.source}`),t===null||typeof t!="object"||Array.isArray(t))r.push("action must be an object with argv and cwd");else{const i=t,c=Object.keys(i).filter(u=>u!=="argv"&&u!=="cwd");c.length>0&&r.push(`action has unsupported field${c.length===1?"":"s"}: ${c.join(", ")}`),(!Array.isArray(i.argv)||i.argv.length===0||!i.argv.every(u=>typeof u=="string"&&u.length>0))&&r.push("argv must be a non-empty array of non-empty strings"),(typeof i.cwd!="string"||i.cwd.length===0)&&r.push("cwd is required and must be a non-empty string")}r.length>0&&n.push({name:o,reason:r.join("; ")})}return n}s(Ln,"humanActionDeclarationIssues");function Fn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n=new Set(Ln(e).map(t=>t.name)),o={};for(const[t,r]of Object.entries(e)){if(n.has(t))continue;const i=r;o[t]={argv:[...i.argv],cwd:i.cwd}}return o}s(Fn,"normalizeHumanActions");function Eo(e){return Mn(e).map(n=>n.message)}s(Eo,"invalidPluginBinReasons");function Io(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))F(o,"requires")===void 0&&(typeof t!="string"||t.trim()===""||/[\r\n]/.test(t)||(n[o]=t));return n}s(Io,"normalizeRequires");function Po(e){if(e===null||typeof e!="object"||Array.isArray(e))return["requires must be a JSON object mapping a bare executable name to a one-line install hint"];const n=[];for(const[o,t]of Object.entries(e)){const r=F(o,"requires");r!==void 0&&n.push(r),typeof t!="string"||t.trim()===""?n.push(`requires.${o} must be a non-empty one-line install hint`):/[\r\n]/.test(t)&&n.push(`requires.${o} must be a one-line install hint`)}return n}s(Po,"invalidPluginRequiresReasons");function pe(e){if(e===null||typeof e!="object")return null;const n=e,o={};typeof n.whenToUse=="string"&&n.whenToUse.trim()!==""&&(o.whenToUse=n.whenToUse),typeof n.model=="string"&&n.model.trim()!==""&&(o.model=n.model),typeof n.orchestratorModel=="string"&&n.orchestratorModel.trim()!==""&&(o.orchestratorModel=n.orchestratorModel);const t=s(r=>Array.isArray(r)&&r.every(i=>typeof i=="string"),"isStringArray");return t(n.tools)&&(o.tools=n.tools),t(n.extensions)&&(o.extensions=n.extensions),t(n.availableTo)&&(o.availableTo=n.availableTo),Object.keys(o).length>0?o:null}s(pe,"normalizeKindFields");function x(e,n=Qe()){const o={...n};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){const i=pe(r);if(i===null)continue;const c=o[t];c!==void 0?o[t]={...c,...i}:i.whenToUse!==void 0&&(o[t]=i)}return o}s(x,"mergeKinds");function ge(e,n,o){if(n===null)return o;const t=te(e,n).filter(i=>i.enabled&&i.manifest.kinds!==void 0).sort((i,c)=>i.name.localeCompare(c.name));let r=o;for(const i of t)r=x(i.manifest.kinds,r);return r}s(ge,"layerPluginKinds");function To(){const e=[{origin:"config.json",components:Sn("user").page_components}],n=y("user");if(n!==null){const o=te("user",n).filter(t=>t.enabled&&t.manifest.page_components!==void 0).sort((t,r)=>t.name.localeCompare(r.name));for(const t of o){const r=`plugin "${t.name}" page_components`;e.push({origin:`plugin "${t.name}"`,components:se(t.manifest.page_components,r)})}}return wn(e)}s(To,"resolvePageComponents");const H=new Set(["whenToUse","model","orchestratorModel","tools","extensions","availableTo"]);function Co(e){if(e===null||typeof e!="object"||Array.isArray(e))return["kinds must be a JSON object keyed by kind name"];const n=[],o=s(t=>Array.isArray(t)&&t.every(r=>typeof r=="string"),"isStringArray");for(const[t,r]of Object.entries(e)){if(t.trim()===""){n.push("kinds contains an empty kind name");continue}if(r===null||typeof r!="object"||Array.isArray(r)){n.push(`kinds.${t} must be a JSON object`);continue}const i=r;for(const c of Object.keys(i))H.has(c)||n.push(`kinds.${t}.${c} is not a KindConfig field (expected one of: ${[...H].join(", ")})`);i.whenToUse!==void 0&&(typeof i.whenToUse!="string"||i.whenToUse.trim()==="")&&n.push(`kinds.${t}.whenToUse must be a non-empty string`);for(const c of["model","orchestratorModel"])i[c]!==void 0&&(typeof i[c]!="string"||i[c].trim()==="")&&n.push(`kinds.${t}.${c} must be a non-empty string`);for(const c of["tools","extensions","availableTo"])i[c]!==void 0&&!o(i[c])&&n.push(`kinds.${t}.${c} must be an array of strings`);pe(r)===null&&n.push(`kinds.${t} has no valid KindConfig field \u2014 an entry must set at least one of: ${[...H].join(", ")} (whenToUse is required when the kind exists in no lower layer)`)}return n}s(Co,"invalidPluginKindsReasons");function zn(e){if(e===null||typeof e!="object")return null;const n=e;if(typeof n.previewEndpoint!="string"||n.previewEndpoint.trim()===""||typeof n.relayTokenRef!="string"||n.relayTokenRef.trim()==="")return null;const o={previewEndpoint:n.previewEndpoint,relayTokenRef:n.relayTokenRef};return typeof n.cpOrigin=="string"&&n.cpOrigin.trim()!==""&&(o.cpOrigin=n.cpOrigin),o}s(zn,"normalizeRemoteCanvasTarget");function Dn(e,n=en()){const o={...n.targets};if(e!==null&&typeof e=="object"){const t=e;if(t.targets!==null&&typeof t.targets=="object")for(const[r,i]of Object.entries(t.targets)){const c=zn(i);c!==null&&(o[r]=c)}}return{targets:o}}s(Dn,"mergeRemoteCanvas");function ye(e,n){return e===!0?"notify":e===!1?!1:e==="notify"||e==="apply"?e:n}s(ye,"normalizeMode");function O(e,n){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=1?e:n}s(O,"normalizePositiveInteger");function Un(e,n){const o=e!==null&&typeof e=="object"?e:{},t=O(o.warning,n.warning),r=O(o.automaticReviveCap,n.automaticReviveCap);return t<=r?{warning:t,automaticReviveCap:r}:{...n}}s(Un,"mergeBrokerThresholds");function Kn(e,n){return{unattendedParkMs:O((e!==null&&typeof e=="object"?e:{}).unattendedParkMs,n.unattendedParkMs)}}s(Kn,"mergeLifecycle");function qn(e){if(!Array.isArray(e))return[...ee];const n=e.filter(o=>typeof o=="string"&&o.trim()!=="").map(o=>o.trim());return n.length>0?n:[...ee]}s(qn,"normalizeWhipMessages");function Gn(e,n){if(e===null||typeof e!="object")return{...n};const o=e.tcp;return{tcp:typeof o=="string"?o:n.tcp}}s(Gn,"mergeApi");function z(e){const n=k(),o=n.schema_version,t=e.marketplaces===void 0?{}:e.marketplaces,r=e.plugins===void 0?{}:e.plugins,i=e.auto_update,c=i&&typeof i.interval_hours=="number"?i.interval_hours:void 0,u=c!==void 0&&Number.isFinite(c)&&c>=0?c:n.auto_update.interval_hours,d={crtr:ye(i?.crtr,n.auto_update.crtr),content:ye(i?.content,n.auto_update.content),interval_hours:u},l=e.max_panes_per_window,f=O(l,n.max_panes_per_window),a=typeof e.auto_open_child_viewers=="boolean"?e.auto_open_child_viewers:n.auto_open_child_viewers,m=Un(e.brokerThresholds,n.brokerThresholds),h=Kn(e.lifecycle,n.lifecycle),D=typeof e.completion_bell=="boolean"?e.completion_bell:n.completion_bell,p=e.working_gerunds===void 0?[...n.working_gerunds]:kn(e.working_gerunds),be=typeof e.whip_mode=="boolean"?e.whip_mode:n.whip_mode,_e=se(e.page_components),ve=typeof e.page_surface=="boolean"?e.page_surface:n.page_surface,ke=qn(e.whip_messages),we=Ve.includes(e.whip_message_mode)?e.whip_message_mode:n.whip_message_mode,Ae=Ye.includes(e.mouse_mode_default)?e.mouse_mode_default:n.mouse_mode_default,Se=O(e.live_cycles,n.live_cycles),je=Ze.includes(e.condensed_history)?e.condensed_history:n.condensed_history,Re=typeof e.bash_tool_purpose=="boolean"?e.bash_tool_purpose:n.bash_tool_purpose,xe=typeof e.fold_finished_tools=="boolean"?e.fold_finished_tools:n.fold_finished_tools,Oe=typeof e.summarize_tool_calls=="boolean"?e.summarize_tool_calls:n.summarize_tool_calls,Ee=typeof e.detailed_tool_recaps=="boolean"?e.detailed_tool_recaps:n.detailed_tool_recaps,Ie=typeof e.consolidate_replies=="boolean"?e.consolidate_replies:n.consolidate_replies,Pe=En(e.keybindings),Te=Pn(e.tmux_passthrough),Ce=C(e.modelLadders),$e=L(e.providerOptions,n.providerOptions),U=le($(e.modelRoutes)),J=U.complete,Z=M(e.modelRouting,void 0),Me=x(e.kinds),Ne=Dn(e.remoteCanvas),Le=Gn(e.api,n.api),_=e.paths;if(_?.user_files!==void 0&&(typeof _.user_files!="string"||!_.user_files.startsWith("/")))throw new Error("paths.user_files must be an absolute path");const Y=e.store,v=e.runtime;if(v?.person_uids!==void 0&&(!Array.isArray(v.person_uids)||!v.person_uids.every(V=>Number.isSafeInteger(V)&&V>=0)))throw new Error("runtime.person_uids must be an array of nonnegative integer uids");const Fe=N(e.spawnEnv,n.spawnEnv?.allow),ze=Nn(e.bin),De=Fn(e.humanActions);return{bin:ze,humanActions:De,schema_version:o,marketplaces:t,plugins:r,auto_update:d,max_panes_per_window:f,auto_open_child_viewers:a,brokerThresholds:m,lifecycle:h,completion_bell:D,working_gerunds:p,whip_mode:be,page_components:_e,page_surface:ve,whip_messages:ke,whip_message_mode:we,mouse_mode_default:Ae,live_cycles:Se,condensed_history:je,bash_tool_purpose:Re,fold_finished_tools:xe,summarize_tool_calls:Oe,detailed_tool_recaps:Ee,consolidate_replies:Ie,keybindings:Pe,tmux_passthrough:Te,modelLadders:Ce,providerOptions:$e,...Object.keys(J).length>0?{modelRoutes:J}:{},...Object.keys(U.legacy).length>0?{legacyModelRoutes:U.legacy}:{},...Z!==void 0?{modelRouting:Z}:{},kinds:Me,remoteCanvas:Ne,api:Le,..._?{paths:_}:{},...Y?{store:Y}:{},...v?{runtime:v}:{},spawnEnv:Fe}}s(z,"mergeConfig");function Bn(e){return I(e)}s(Bn,"readRawConfigAtRoot");function Wn(e){const n=y(e);return n?Bn(n):null}s(Wn,"readRawScopeConfig");function Hn(e){if(e===null||e==="")return null;try{return I(q(e))}catch{return null}}s(Hn,"readRawProfileConfig");function Jn(e,n){return[...un(e,n)].reverse().map(r=>({root:r,raw:I(r)}))}s(Jn,"readRawProjectScopeConfigs");function he(e=process.cwd(),n=Ke()||null){const o=P[T];if(o)return o.launch;const t=k(),r=Wn("user"),i=Hn(n),c=Jn(e,n);let u=x(r?.kinds,ge("user",y("user"),t.kinds)),d=C(r?.modelLadders,t.modelLadders),l=L(r?.providerOptions,t.providerOptions),f=$(r?.modelRoutes),a=M(r?.modelRouting,void 0),m=N(r?.spawnEnv,t.spawnEnv?.allow);i!==null&&(u=x(i.kinds,u),d=C(i.modelLadders,d),l=L(i.providerOptions,l),f=$(i.modelRoutes,f),a=M(i.modelRouting,a),m=N(i.spawnEnv,m.allow));for(const{root:D,raw:p}of c)u=x(p?.kinds,ge("project",D,u)),p!==null&&(d=C(p.modelLadders,d),l=L(p.providerOptions,l),f=$(p.modelRoutes,f),a=M(p.modelRouting,a),m=N(p.spawnEnv,m.allow));const h=le(f);return{kinds:u,modelLadders:d,providerOptions:l,...Object.keys(h.complete).length>0?{modelRoutes:h.complete}:{},...Object.keys(h.legacy).length>0?{legacyModelRoutes:h.legacy}:{},...a!==void 0?{modelRouting:a}:{},spawnEnv:m}}s(he,"readMergedLaunchConfig");function $o(e){return he().kinds[e]}s($o,"resolveKindConfig");function Mo(e){const n=he().kinds,o=[];for(const[t,r]of Object.entries(n)){const i=t.indexOf("/");if(i===-1)continue;const c=t.slice(0,i),u=r.availableTo??[c];(u.includes("*")||u.includes(e))&&o.push({kind:t,whenToUse:r.whenToUse})}return o.sort((t,r)=>t.kind.localeCompare(r.kind))}s(Mo,"subKindsAvailableTo");function Zn(e,n,o){const t={...e??{}};t.anthropic!==void 0&&(t.anthropic={...t.anthropic}),t.openai!==void 0&&(t.openai={...t.openai}),o.defaultProvider!==n.defaultProvider&&o.defaultProvider!==void 0&&(t.defaultProvider=o.defaultProvider);for(const i of["anthropic","openai"])for(const c of["ultra","strong","medium","light"])o[i][c]!==n[i][c]&&(t[i]={...t[i]??{},[c]:o[i][c]});return t.defaultProvider!==void 0||t.anthropic!==void 0||t.openai!==void 0?t:void 0}s(Zn,"sparseModelLaddersToPersist");function Yn(e,n){return S(e,o=>{const t=z(o),r=z(o);n(r);const i={...o};for(const u of vn)u!=="modelLadders"&&!We(r[u],t[u])&&(i[u]=r[u]);const c=Zn(o.modelLadders,t.modelLadders,r.modelLadders);return c!==void 0?i.modelLadders=c:delete i.modelLadders,{value:r,next:i}})}s(Yn,"mutateConfigAtRoot");function No(e,n){S(e,o=>{const t=o.modelLadders??{},r={...t};n.defaultProvider!==void 0&&(r.defaultProvider=n.defaultProvider);for(const i of["anthropic","openai"])n[i]!==void 0&&(r[i]={...t[i]??{},...n[i]});return{value:void 0,next:{...o,modelLadders:r}}})}s(No,"persistModelLadders");function Lo(e,n,o){if(o!==void 0)Tn(n,o);else if(n.trim()==="")throw new Error("model route id must be a non-empty string");S(e,t=>{const r=fe(t.modelRoutes??{});o===void 0?delete r[n]:j(r,n,{...o,models:{...o.models}});const i={...t};return Object.keys(r).length===0?delete i.modelRoutes:i.modelRoutes=r,{value:void 0,next:i}})}s(Lo,"persistModelRoute");function Vn(e,n){return Yn(e,n)}s(Vn,"updateConfigAtRoot");function Fo(e,n){return Vn(w(e),n)}s(Fo,"updateConfig");function zo(e){const n=e.orchestrator===!0?"orchestratorModel":"model";return S(e.root,o=>{const t={...o.kinds??{},[e.kind]:{...(o.kinds??{})[e.kind],[n]:e.model}};return{value:void 0,next:{...o,kinds:t}}}),{path:b(e.root),field:n}}s(zo,"persistDefaultKindModel");function Do(e,n){const o=Rn(e);return n(o),On(e,o),o}s(Do,"updateState");export{xn as InvalidRawSettingsError,Mn as binDeclarationIssues,wo as brokerConfigSnapshot,_o as configPath,Oo as ensureScopeInitialized,Ln as humanActionDeclarationIssues,ko as installBrokerConfigSnapshot,Eo as invalidPluginBinReasons,Co as invalidPluginKindsReasons,Po as invalidPluginRequiresReasons,Nn as normalizeBin,Fn as normalizeHumanActions,Io as normalizeRequires,Pn as normalizeTmuxPassthrough,zo as persistDefaultKindModel,No as persistModelLadders,Lo as persistModelRoute,Sn as readConfig,jn as readConfigAtRoot,he as readMergedLaunchConfig,Bn as readRawConfigAtRoot,Wn as readRawScopeConfig,Ao as readRawSettingsForTarget,Rn as readState,So as replaceRawSettingsForTarget,$o as resolveKindConfig,To as resolvePageComponents,vo as statePath,Mo as subKindsAvailableTo,Fo as updateConfig,Vn as updateConfigAtRoot,jo as updateRawConfigAtomically,Do as updateState,Ro as writeConfig,xo as writeRawConfig,On as writeState};
|
|
@@ -20,8 +20,8 @@ type Registration = Pick<JoinedClient, 'runtime' | 'client_id' | 'issuer' | 'own
|
|
|
20
20
|
export interface JoinResult extends Registration {
|
|
21
21
|
/** False when the token matched the stored successful registration and no directory call was made. */
|
|
22
22
|
registered: boolean;
|
|
23
|
-
/** `restarted`: a daemon was running, join stopped it and started a new one on the joined config. `
|
|
24
|
-
daemon: 'restarted' | 'not_running' | 'restart_failed';
|
|
23
|
+
/** `restarted`: a daemon was running, join stopped it and started a new one on the joined config. `started`: none was running and join started one, whose first grant sync installs the account's provider plugins (`crtr cloud …`). `not_running`: none was started (a hosted container's launcher owns its daemon). `restart_failed`: the join is committed but the new daemon did not start (see `daemon_error`). */
|
|
24
|
+
daemon: 'restarted' | 'started' | 'not_running' | 'restart_failed';
|
|
25
25
|
daemon_error?: string;
|
|
26
26
|
}
|
|
27
27
|
/** How join takes a running daemon down and brings it back. Absent, join keeps
|
|
@@ -33,6 +33,8 @@ export interface JoinDaemonControl {
|
|
|
33
33
|
stop(): Promise<void>;
|
|
34
34
|
/** Start a daemon on the settings now on disk and wait until it serves. */
|
|
35
35
|
start(): Promise<void>;
|
|
36
|
+
/** Waits, bounded, for a daemon that join started with none running to finish its first grant sync, which installs the account's provider plugins. Best effort: the join is committed either way. */
|
|
37
|
+
firstSync?(): Promise<void>;
|
|
36
38
|
/** Whether this process runs inside a node, whose broker the stop kills. */
|
|
37
39
|
insideNode(): boolean;
|
|
38
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var J=Object.defineProperty;var s=(r
|
|
1
|
+
var J=Object.defineProperty;var s=(t,r)=>J(t,"name",{value:r,configurable:!0});import{createHash as N,generateKeyPairSync as R,randomUUID as U}from"node:crypto";import{existsSync as I,mkdirSync as D,readdirSync as L,rmSync as y}from"node:fs";import{hostname as O}from"node:os";import{join as b}from"node:path";import{InputError as p}from"../io.js";import{network as k}from"../errors.js";import{secretsDir as j}from"../layout.js";import{apiSocketPath as C}from"../canvas/paths.js";import{daemonAnswers as K}from"../layout-migrate/index.js";import{atomicWriteJson as _,readJsonIfExists as x}from"../fs-utils.js";import{updateConfig as T}from"../config.js";import{launcherPresent as A}from"../grants/sandboxes.js";import{crtrVersion as W}from"../../shared/crtr-version.js";import{pluginsDir as q}from"../scope.js";import{userScopeRoot as z}from"../scope.js";import{rewriteLocalProfileGrantees as B}from"../scoped-state/profiles.js";import{withJoinLock as F}from"./join-lock.js";function l(){return b(j(),"runtime-client.json")}s(l,"clientPath");function S(t){if(typeof t!="string")return!1;try{const{protocol:r}=new URL(t);return r==="http:"||r==="https:"}catch{return!1}}s(S,"isUrl");function $(t){const r=t??{},{runtime:u,client_id:e,issuer:a,owner:n,url:o}=r;if(typeof u!="string"||!/^runtime:.+/.test(u)||typeof e!="string"||e===""||!S(a)||typeof n!="string"||!/^user:.+/.test(n)||!S(o))throw k("directory registration answer is missing runtime, client_id, issuer, owner, or url");const{machine_id:i,is_default:c}=r;return{runtime:u,client_id:e,issuer:a,owner:n,url:o,...typeof i=="string"?{machine_id:i}:{},...typeof c=="boolean"?{is_default:c}:{}}}s($,"registration");function w(){return b(j(),"runtime-client.pending.json")}s(w,"pendingPath");function G(){try{const t=q("user");return!t||!I(t)?[]:L(t,{withFileTypes:!0}).filter(r=>r.isDirectory()&&!r.name.startsWith(".")).map(r=>r.name).sort()}catch{return[]}}s(G,"installedPluginIds");async function V(t,r,u,e){const a=`${t.replace(/\/$/,"")}/v1/runtimes/registration`;let n;try{n=await fetch(a,{method:"PUT",headers:{authorization:`Bearer ${r}`,"content-type":"application/json"},body:JSON.stringify({jwk:u,name:e,platform:`${process.platform}-${process.arch}`,crtr_version:W(),plugins:G(),sandboxed:A()}),signal:AbortSignal.timeout(3e4)})}catch(i){throw k(`directory registration request to ${a} failed: ${i instanceof Error?i.message:String(i)}`)}const o=await n.text();if(!n.ok)throw n.status<500&&y(w(),{force:!0}),new p({error:"join_refused",message:`directory refused the registration: ${n.status} ${o.slice(0,500)}`,next:`Create a new join token at ${t.replace(/\/$/,"")}/account/machines (tokens are single-use and expire after 10 minutes), and run \`crtr sys join\` again.`});try{return $(JSON.parse(o))}catch(i){throw i instanceof SyntaxError?k("directory registration answer is not JSON"):i}}s(V,"register");function P(t){T("user",r=>{r.runtime=t.is_default===!1?{issuer:t.issuer,audience:t.url,unsandboxed:!0}:{...r.runtime,issuer:t.issuer,audience:t.url}}),B(z())}s(P,"commit");function mt(){const t=x(l());if(t?.token_sha256===void 0)return null;const{owner:r}=t;if(typeof r!="string"||!/^user:.+/.test(r))throw new Error(`${l()} was written by join but has a missing or invalid owner`);return r}s(mt,"joinedOwner");function v(t){return new p({error:"daemon_running",message:`cannot join while a daemon answers on ${t}`,next:"This daemon is managed outside crtr, so join cannot restart it: stop the daemon that owns this volume, then run `crtr sys join` again."})}s(v,"daemonRunningError");async function E(t,r,u,e){const a=x(l());if(a?.token_sha256===e)return{joined:$(a),registered:!1};D(j(),{recursive:!0,mode:448});let n=x(w());if(n?.token_sha256!==e||n.directory!==t||!n.jwk||!n.public_jwk){const{privateKey:h,publicKey:f}=R("ec",{namedCurve:"P-256"}),d=U();n={token_sha256:e,directory:t,jwk:{...h.export({format:"jwk"}),kid:d,alg:"ES256",use:"sig"},public_jwk:{...f.export({format:"jwk"}),kid:d,alg:"ES256",use:"sig"}},_(w(),n,{mode:384})}const{jwk:o,public_jwk:i}=n,c=await V(t,r,i,u);return{joined:c,registered:!0,client:{jwk:o,kid:o.kid,alg:"ES256",...c,token_sha256:e}}}s(E,"obtainRegistration");async function pt(t,r,u=O(),e){if(!S(t))throw new p({error:"invalid_directory",message:"directory must be an http or https URL",received:t,field:"directory",next:"Pass the directory base URL, for example https://directory.example."});if(r==="")throw new p({error:"invalid_token",message:"token is empty",field:"token",next:"Pass the enrollment token provisioning supplied."});const a=C(),n=N("sha256").update(r).digest("hex");if(e===void 0){if(await K(a))throw v(a);const{joined:o,registered:i,client:c}=await E(t,r,u,n);return c&&(_(l(),c,{mode:384}),y(w(),{force:!0})),P(o),{...o,registered:i,daemon:"not_running"}}return F(async()=>{const o=s(()=>new p({error:"join_inside_node",message:"a daemon is running and this command is inside a node: stopping the daemon would kill this node's broker and this command with it",next:"Run `crtr sys join` from a plain terminal outside any crtr node. It stops the daemon, joins, and starts it again for you."}),"refuseInsideNode");if(await e.running()&&e.insideNode())throw o();const{joined:i,registered:c,client:h}=await E(t,r,u,n);let f=!1,d;try{if(await e.running()){if(e.insideNode())throw o();if(f=!0,await e.stop(),await e.running())throw v(a)}if(h&&(_(l(),h,{mode:384}),y(w(),{force:!0})),P(i),await e.running()){if(e.insideNode())throw o();f=!0,await e.stop()}}catch(m){d=m}let g={daemon:"not_running"};if(d===void 0||f)try{await e.start(),g={daemon:f?"restarted":"started"}}catch(m){g={daemon:"restart_failed",daemon_error:m instanceof Error?m.message:String(m)}}if(d!==void 0)throw d;return g.daemon==="started"&&await e.firstSync?.(),{...i,registered:c,...g}})}s(pt,"joinDirectory");export{G as installedPluginIds,pt as joinDirectory,mt as joinedOwner};
|
package/dist/core/io.d.ts
CHANGED
|
@@ -59,6 +59,9 @@ interface ErrorPayloadBase {
|
|
|
59
59
|
http_status?: number;
|
|
60
60
|
/** Seconds the provider asked the caller to wait before retrying (a rate limit). */
|
|
61
61
|
retry_after_s?: number;
|
|
62
|
+
/** The structured details the runtime attached to a refusal the caller must act on (a `machine_required`
|
|
63
|
+
* answer's `candidates`). Rendered below the message and kept in `--json`. */
|
|
64
|
+
details?: Record<string, unknown>;
|
|
62
65
|
next: string;
|
|
63
66
|
}
|
|
64
67
|
export type ErrorPayload = ErrorPayloadBase & ({
|
|
@@ -116,9 +119,16 @@ export declare function diag(message: string): void;
|
|
|
116
119
|
* under the daemon's coarse `provider_*` code and the provider's HTTP
|
|
117
120
|
* status. The provider's code, `next`, `field` and `Retry-After` reach the
|
|
118
121
|
* caller unchanged; the status→code map would flatten them to `conflict`.
|
|
119
|
-
* 4.
|
|
122
|
+
* 4. A refusal the runtime itself rendered for `provider.call` (`options.providerCall`):
|
|
123
|
+
* its envelope code came from the runtime's own table (`machine_required`,
|
|
124
|
+
* `device_offline`, …), not from the HTTP status, so it is kept together with
|
|
125
|
+
* its `details`. The caller of `provider.call` is the only one who knows the
|
|
126
|
+
* envelope is that endpoint's, which is why this is an option and not inferred.
|
|
127
|
+
* 5. Otherwise map status→code (`STATUS_TO_CLI_CODE`) with the server message
|
|
120
128
|
* and a caller-supplied-or-default `next`. */
|
|
121
|
-
export declare function apiErrorToCliError(err: ApiError, next?: string
|
|
129
|
+
export declare function apiErrorToCliError(err: ApiError, next?: string, options?: {
|
|
130
|
+
providerCall?: boolean;
|
|
131
|
+
}): CrtrError;
|
|
122
132
|
/** Terminal error handler. Command-level failures (bad input, not-found,
|
|
123
133
|
* ambiguous) surface as the JSON response on stdout so the caller parses one
|
|
124
134
|
* contract. A daemon `ApiError` is translated to that same clean contract
|
package/dist/core/io.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
var
|
|
2
|
-
`;try{if((w(t,{throwIfNoEntry:!1})?.size??0)+Buffer.byteLength(
|
|
3
|
-
`)}
|
|
4
|
-
`)}
|
|
5
|
-
`)}
|
|
6
|
-
`),process.exitCode=e.exitCode;return}const t=e,
|
|
7
|
-
`),process.exitCode=u.GENERAL}
|
|
1
|
+
var b=Object.defineProperty;var i=(e,t)=>b(e,"name",{value:t,configurable:!0});import{appendFileSync as _,statSync as w}from"node:fs";import{CrtrError as c}from"./errors.js";import{ExitCode as u}from"../types.js";import{PREVIEW_RESULT_PATH_ENV as m}from"./preview-result-path.js";import{renderError as E}from"./render.js";import{ApiError as v}from"../api/index.js";let p=!1,d;const A=256*1024;function B(e){const t=process.env[m];d=t===void 0||t===""?void 0:{path:e,jsonl:[]}}i(B,"beginPreview");function y(e){const t=process.env[m];if(t===void 0||t==="")return;const s=`${JSON.stringify({path:d?.path??"",...e})}
|
|
2
|
+
`;try{if((w(t,{throwIfNoEntry:!1})?.size??0)+Buffer.byteLength(s,"utf8")>A)return;_(t,s,"utf8")}catch{}}i(y,"publishPreview");function q(e){d!==void 0&&(e!=null?y({result:e}):(d.jsonl.length>0||d.error!==void 0)&&y({...d.jsonl.length>0?{jsonl:d.jsonl}:{},...d.error===void 0?{}:{error:d.error}}))}i(q,"publishPreviewResult");function h(e){d?.jsonl.push(e)}i(h,"recordPreviewLine");function F(e){try{const t=JSON.parse(e);t!==null&&typeof t=="object"&&!Array.isArray(t)&&h(t)}catch{}}i(F,"recordPreviewJsonLine");function G(e){d!==void 0&&(d.error=e)}i(G,"recordPreviewError");function K(e){p=e}i(K,"setJsonOutput");function z(){return p}i(z,"isJsonOutput");class g extends c{static{i(this,"InputError")}payload;constructor(t,s=u.USAGE){super(t.error,t.message,s,{...t}),this.name="InputError",this.payload=t}}async function D(){if(process.stdin.isTTY)return"";const e=[];for await(const t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf8")}i(D,"readStdinRaw");async function V(e=200){return process.stdin.isTTY?"":await new Promise(t=>{const s=[];let n=!1;const r=i(()=>{n||(n=!0,clearTimeout(a),process.stdin.removeListener("data",o),process.stdin.removeListener("end",l),process.stdin.removeListener("error",f),process.stdin.pause(),t(Buffer.concat(s).toString("utf8")))},"finish"),o=i(x=>s.push(x),"onData"),l=i(()=>r(),"onEnd"),f=i(()=>r(),"onError"),a=setTimeout(r,e);process.stdin.on("data",o),process.stdin.on("end",l),process.stdin.on("error",f),process.stdin.resume()})}i(V,"peekStdinRaw");function Y(e){process.stdout.write(JSON.stringify(e,null,2)+`
|
|
3
|
+
`)}i(Y,"emit");function H(e){h(e),process.stdout.write(JSON.stringify(e)+`
|
|
4
|
+
`)}i(H,"emitLine");function M(e){return new Promise(t=>{let s=!1;const n=i(o=>{o.code==="EPIPE"&&r(!1)},"onErr"),r=i(o=>{s||(s=!0,process.stdout.off("error",n),t(o))},"finish");process.stdout.on("error",n);try{process.stdout.write(e,o=>r(o==null))}catch{r(!1)}})}i(M,"writeStdout");function X(e){process.stderr.write(e+`
|
|
5
|
+
`)}i(X,"diag");function S(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}i(S,"isRecord");function T(e){if(e instanceof g)return e.payload;const t=e.details!==void 0?e.details:{},s=t.primaryCompleted===!0&&t.result!==null&&typeof t.result=="object"&&!Array.isArray(t.result)?O(t.result):void 0,n=t.next!==void 0?t.next:"Inspect the error and adjust the call. See -h for the schema.",r={error:e.code,message:e.message,received:t.received,field:t.field,...typeof t.http_status=="number"?{http_status:t.http_status}:{},...typeof t.retry_after_s=="number"?{retry_after_s:t.retry_after_s}:{},...S(t.details)?{details:t.details}:{}};return s===void 0?{...r,next:n}:{...r,primaryCompleted:!0,result:s,next:n}}i(T,"payloadOf");function O(e){const t=new WeakSet;try{const s=JSON.stringify(e,(n,r)=>{if(typeof r=="bigint")return{$crtrType:"bigint",value:r.toString()};if(typeof r=="number"&&!Number.isFinite(r))return{$crtrType:"number",value:String(r)};if(typeof r>"u"||typeof r=="function"||typeof r=="symbol")return{$crtrType:typeof r};if(r!==null&&typeof r=="object"){if(t.has(r))return{$crtrType:"circular"};t.add(r)}return r});if(s!==void 0){const n=JSON.parse(s);if(n!==null&&typeof n=="object"&&!Array.isArray(n))return n}}catch{}return{$crtrType:"unavailable",message:"The completed primary result could not be represented as JSON."}}i(O,"jsonSafeResult");const N={400:"invalid_request",404:"not_found",409:"conflict",422:"unprocessable"};function R(e,t,s={}){if(e.code==="daemon_restarting")return new c("daemon_restarting",e.message,u.NETWORK,{next:"crtrd is now on the new generation \u2014 retry the command."});if(e.code==="layout_migration_required")return new c("layout_migration_required",e.message,u.NETWORK,{next:"Run `crtr sys migrate` while the runtime is offline, then restart it."});if(e.code==="daemon_unavailable")return new c("daemon_unavailable",e.message,u.NETWORK,{next:"Start it with `crtr sys daemon start`, then retry."});const n=e.details,r=j(e,n,s.providerCall===!0);if(e.status===503||s.providerCall===!0&&e.status===504&&r.code!==void 0){const f=r.code??e.code,a={next:r.next??t??"Retry the command; if it persists, inspect the unavailable service named in the error."};return r.details!==void 0&&(a.details=r.details),r.code!==void 0&&(r.field!==void 0&&(a.field=r.field),r.received!==void 0&&(a.received=r.received),r.retryAfterS!==void 0&&(a.retry_after_s=r.retryAfterS)),new c(f,e.message,u.NETWORK,a)}const o=Array.isArray(n?.violations)?n.violations.map(f=>`${String(f.field)} ${String(f.problem)}${f.received!==void 0?` (received ${JSON.stringify(f.received)})`:""}`):[],l=n&&typeof n.error=="string"&&typeof n.message=="string"&&typeof n.next=="string"?n:{error:r.code??N[e.status]??e.code,...r.field!==void 0?{field:r.field}:{},...r.received!==void 0?{received:r.received}:{},...r.retryAfterS!==void 0?{retry_after_s:r.retryAfterS}:{},...r.details!==void 0?{details:r.details}:{},message:o.length>0?`${e.message}: ${o.join("; ")}`:e.message.trim()!==""?e.message:`request refused (HTTP ${e.status}, ${e.code}) with no message`,next:r.next??t??(r.retryAfterS!==void 0?`Wait ${r.retryAfterS}s, then retry; do not retry sooner.`:e.status>=500?"This is a crtr bug, not a bad call. Retry; if it persists, report it (the daemon log holds the full cause).":e.status===404?"List nodes with `crtr node inspect list`.":"Inspect the error and adjust the call. See -h for the schema.")};return new g(l,P(l.error,e.status))}i(R,"apiErrorToCliError");function j(e,t,s){const n=i(o=>typeof o=="string"&&/^[a-z][a-z0-9_]*$/.test(o),"snake");if(s&&e.origin!==void 0&&e.origin!=="provider"&&n(e.code)){const o={code:e.code};return(e.status===503||e.status===504)&&(o.next=e.retryable===!0?"Retry the command; if it persists, the machine or service named in the error is unavailable.":"Read the error before retrying: the action may already have run."),S(e.details)&&(o.details=e.details),e.param!==void 0&&(o.field=e.param),typeof e.retryAfterS=="number"&&(o.retryAfterS=e.retryAfterS),o}if(e.origin!=="provider"||t===void 0||t===null||typeof t!="object")return{};const r={};return typeof t.status=="number"&&n(t.code)&&(r.code=t.code),typeof t.next=="string"&&t.next.trim()!==""&&(r.next=t.next),typeof t.field=="string"&&t.field!==""?r.field=t.field:e.param!==void 0&&(r.field=e.param),t.received!==void 0&&t.received!==null&&(r.received=t.received),typeof e.retryAfterS=="number"&&(r.retryAfterS=e.retryAfterS),r}i(j,"providerSupplied");function P(e,t){return e==="not_found"||t===404?u.NOT_FOUND:e==="ambiguous"?u.AMBIGUOUS:u.USAGE}i(P,"exitForCliCode");function $(e){if(e instanceof v){$(R(e));return}if(e instanceof c){const n=T(e);d!==void 0&&y({error:n});const r=p?JSON.stringify(n,null,2):E(n);process.stdout.write(r+`
|
|
6
|
+
`),process.exitCode=e.exitCode;return}const t=e,s=t!=null&&typeof t.message=="string"?t.message:String(e);process.stderr.write(JSON.stringify({error:"internal",message:s,next:"This is a crtr bug, not a bad call. Retry; if it persists, report it."},null,2)+`
|
|
7
|
+
`),process.exitCode=u.GENERAL}i($,"handle");export{g as InputError,A as PREVIEW_RESULT_MAX_BYTES,R as apiErrorToCliError,B as beginPreview,X as diag,Y as emit,H as emitLine,$ as handle,z as isJsonOutput,V as peekStdinRaw,q as publishPreviewResult,D as readStdinRaw,G as recordPreviewError,F as recordPreviewJsonLine,h as recordPreviewLine,K as setJsonOutput,M as writeStdout};
|