@north-light/crouter 0.3.321 → 0.3.322
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/bin/runtime-selector.mjs +52 -3
- package/dist/api/client.d.ts +29 -16
- package/dist/api/client.js +2 -2
- package/dist/api/dto/modelauth.d.ts +15 -0
- package/dist/api/errors.d.ts +6 -3
- package/dist/api/errors.js +1 -1
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.js +1 -1
- package/dist/api/node-transport.d.ts +18 -0
- package/dist/api/node-transport.js +1 -0
- package/dist/api/routes.d.ts +1 -0
- package/dist/api/routes.js +1 -1
- package/dist/builtin-memory/crouter-sdk.md +86 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +1 -1
- package/dist/cli.js +8 -2
- package/dist/clients/attach/render/chat-view.d.ts +5 -1
- package/dist/clients/attach/render/chat-view.js +1 -1
- package/dist/clients/attach/session/frames.d.ts +1 -0
- package/dist/clients/attach/session/frames.js +1 -1
- package/dist/clients/attach/viewer.js +583 -583
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/memory/read.js +2 -2
- package/dist/commands/sys/branch.js +1 -1
- package/dist/commands/sys/connect.d.ts +1 -0
- package/dist/commands/sys/connect.js +3 -0
- package/dist/commands/sys/daemon.js +1 -1
- package/dist/commands/sys/panels/provider-panel.js +1 -1
- package/dist/commands/sys/provider-login.d.ts +8 -0
- package/dist/commands/sys/provider-login.js +9 -0
- package/dist/commands/sys/setup-core.d.ts +4 -0
- package/dist/commands/sys/setup-core.js +4 -4
- package/dist/commands/sys.js +1 -1
- package/dist/core/canvas/paths.d.ts +4 -4
- package/dist/core/config.js +1 -1
- package/dist/core/runtime/broker/daemon-ops.js +1 -1
- package/dist/core/secrets.d.ts +3 -0
- package/dist/core/secrets.js +2 -2
- package/dist/core/subscription-state.d.ts +1 -1
- package/dist/core/subscription-state.js +3 -3
- package/dist/core/termrender/termrender.d.ts +3 -1
- package/dist/core/termrender/termrender.js +13 -13
- package/dist/core/user-settings.d.ts +4 -0
- package/dist/core/user-settings.js +1 -1
- package/dist/daemon/api/handlers/health.d.ts +1 -1
- package/dist/daemon/api/handlers/health.js +3 -3
- package/dist/daemon/api/handlers/modelauth.js +1 -1
- package/dist/daemon/api/server.d.ts +5 -1
- package/dist/daemon/api/server.js +4 -4
- package/dist/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/manage.d.ts +1 -0
- package/dist/daemon/manage.js +2 -2
- package/dist/types.d.ts +7 -0
- package/dist/types.js +1 -1
- package/docs/sdk/README.md +54 -0
- package/docs/sdk/client.md +92 -0
- package/docs/sdk/docker.md +63 -0
- package/docs/sdk/errors.md +85 -0
- package/docs/sdk/getting-started.md +160 -0
- package/docs/sdk/memory.md +107 -0
- package/docs/sdk/migration.md +108 -0
- package/docs/sdk/nodes.md +193 -0
- package/docs/sdk/resources.md +71 -0
- package/docs/sdk/streaming.md +124 -0
- package/package.json +1 -1
- package/runtime.lock.json +6 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).filter(
|
|
1
|
+
var m=Object.defineProperty;var a=(n,t)=>m(n,"name",{value:t,configurable:!0});import{closeSync as p,fstatSync as w,openSync as b,readSync as g}from"node:fs";import{join as _}from"node:path";import{ApiError as u}from"../api/index.js";import{localClient as d}from"../api/node-transport.js";import{apiErrorToCliError as y}from"../core/io.js";import{autostartDisabled as C,ensureDaemon as S}from"../daemon/manage.js";import{DAEMON_STARTUP_WINDOW_MS as N}from"../daemon/startup-policy.js";import{readState as v}from"../core/config.js";import{resolveRemoteCanvasTarget as T}from"../core/remote-canvas-target.js";import{RemoteCanvasSource as A}from"../core/canvas/remote-canvas-source.js";import{crtrHome as I}from"../core/canvas/paths.js";function l(n){return{...n,fault:n.fault??null}}a(l,"withRowFault");function M(n,t){throw n instanceof u?y(n,t):n}a(M,"rethrowAsCliError");async function V(n){try{return await h().getNode(n)}catch(t){if(t instanceof u&&t.status===404)return null;M(t)}}a(V,"getNodeOrNull");const O=4096,D=20;function R(){let n;try{n=b(_(I(),"crtrd.log"),"r");const{size:t}=w(n),e=Math.max(0,t-O),r=t-e;if(r<=0)return;const s=Buffer.alloc(r);g(n,s,0,r,e);const o=s.toString("utf8").split(`
|
|
2
|
+
`).filter(c=>c.trim()!=="").slice(-D).join(`
|
|
3
3
|
`).trim();return o===""?void 0:`crtrd.log (tail):
|
|
4
|
-
${o}`}catch{return}finally{if(
|
|
4
|
+
${o}`}catch{return}finally{if(n!==void 0)try{p(n)}catch{}}}a(R,"readColdStartDiagnostic");function h(n){return C()?d({autostart:!1,...n?.timeoutMs!==void 0?{timeoutMs:n.timeoutMs}:{}}):d({...n?.timeoutMs!==void 0?{timeoutMs:n.timeoutMs}:{},onColdSocket:a(async()=>{S()},"onColdSocket"),coldStartDiagnostic:R,coldStartPollWindowMs:N})}a(h,"cliClient");const x=2e3;class E{static{a(this,"ApiCanvasSource")}client;rosterCache=null;rosterInFlight=null;constructor(t=h()){this.client=t}async roster(){const t=Date.now();return this.rosterCache!==null&&t-this.rosterCache.at<x?this.rosterCache.index:this.rosterInFlight!==null?this.rosterInFlight:(this.rosterInFlight=this.client.canvasRoster().then(e=>{const r={subscriptions:new Map,subscribers:new Map};for(const s of e.edges){const i={node_id:s.to_id,active:s.active,created:s.created},o={node_id:s.from_id,active:s.active,created:s.created},c=r.subscriptions.get(s.from_id);c===void 0?r.subscriptions.set(s.from_id,[i]):c.push(i);const f=r.subscribers.get(s.to_id);f===void 0?r.subscribers.set(s.to_id,[o]):f.push(o)}return this.rosterCache={at:Date.now(),index:r},r}).finally(()=>{this.rosterInFlight=null}),this.rosterInFlight)}async orNull(t){try{return await t()}catch(e){if(e instanceof u&&e.status===404)return null;throw e}}async getNode(t){const e=await this.orNull(()=>this.client.getNode(t));return e===null?null:l(e)}async getRow(t){const e=await this.orNull(()=>this.client.getNode(t));return e===null?null:l(e)}async listNodes(t){const e=t?.status;let r;if(Array.isArray(e)){const s=new Set(e);r=(await this.client.listNodes()).filter(o=>s.has(o.status))}else e!==void 0?r=await this.client.listNodes({status:e}):r=await this.client.listNodes();return r.map(s=>l(s))}async subscriptionsOf(t){return this.roster().then(e=>e.subscriptions.get(t)??[])}async subscribersOf(t){return this.roster().then(e=>e.subscribers.get(t)??[])}async view(t){const e=new Set([t]),r=[],s=(await this.subscriptionsOf(t)).map(i=>i.node_id);for(;s.length>0;){const i=s.shift();if(!e.has(i)){e.add(i),r.push(i);for(const o of await this.subscriptionsOf(i))e.has(o.node_id)||s.push(o.node_id)}}return r}async ticketCountsForNodes(t){return(await this.client.attentionCounts([...t])).counts}async ticketCountsForView(t){return this.ticketCountsForNodes([t,...await this.view(t)])}async hasActiveLiveSubscription(t){const e=(await this.subscriptionsOf(t)).filter(r=>r.active);for(const r of e){const s=await this.getRow(r.node_id);if(s&&(s.status==="active"||s.status==="idle"))return!0}return!1}}function Y(n){const t=n?.scope??"user",e=n?.canvasName??v(t).activeCanvas??null;if(!e)return new E;const{previewEndpoint:r,relayToken:s}=T(e,t);return new A(r,s)}a(Y,"cliCanvasSource");export{E as ApiCanvasSource,O as COLD_START_DIAGNOSTIC_TAIL_BYTES,Y as cliCanvasSource,h as cliClient,V as getNodeOrNull,R as readColdStartDiagnostic,M as rethrowAsCliError};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var
|
|
1
|
+
var L=Object.defineProperty;var i=(e,r)=>L(e,"name",{value:r,configurable:!0});import{envNodeId as F}from"../../shared/env.js";import{localClient as I}from"../../api/node-transport.js";import{defineLeaf as O}from"../../core/command.js";import{memoryExtensionEffectiveCatalog as K,projectEffectiveMemoryExtensions as Y}from"../../core/memory/extensions.js";import{docLinkNames as U}from"../../core/memory/doc-link-grammar.js";import{ambientMemoryTarget as E,createMemoryDocSnapshot as V,listAllMemoryDocs as W,loadMemoryStoreView as X,loadMemoryTargetView as $,readMemoryDocContent as R,resolveMemoryDocs as z}from"../../core/memory-resolver.js";import{expandShellBlocks as G,hasShellBlocks as H,makeNodeShellRunner as J}from"../../core/runtime/shell-expansion.js";import{emptyContextExposureState as Q,exposureTarget as _,loadContextExposureState as S,registerDocumentExposure as Z,registerExposure as ee,saveContextExposureState as D}from"../../core/substrate/injected-store.js";import{dirDedupKey as te,docsByName as g,isDirName as j,renderDirListing as ne}from"../../core/substrate/listings.js";import{contentReadAttachments as re}from"../../core/substrate/on-read.js";import{renderResult as oe}from"../../core/render.js";import{effectiveDocKind as ae}from"../../core/substrate/schema.js";import{canonicalSegments as ie}from"../../core/memory/identity.js";import{MEMORY_KINDS as se,requireMountedStore as de,resolveReadSelector as ce,selectorParam as M}from"./shared.js";async function le(e){if(e===void 0)return null;try{return await I({autostart:!1,coldStartPollWindowMs:0}).nodeSubject(e)}catch{return null}}i(le,"fetchSubject");function me(){try{return W(void 0,!0,!0)}catch{return[]}}i(me,"corpusDocs");function he(e,r){return{winner:e===r,scope:e.scope,store_root:e.root,physical_relative_path:e.physicalRelativePath,path:e.path}}i(he,"candidateRow");const ue=O({name:"read",description:"load a memory document body by name",whenToUse:"a task in front of you matches a stored document and you already know its canonical name \u2014 read it before improvising. Resolution is exact and takes the full canonical name (a project document is `<namespace>/<local name>`); among documents sharing one name the winner is the nearest scope (node > project stack > profile > user > builtin). A directory name is a valid target too: it answers with that directory\u2019s own document, when it has one, together with its immediate listing, and walking directories is the sanctioned browse move. You name the document by its crtr identifier, never a file path \u2014 do not cat or find the markdown off disk. Reach for `crtr memory find` first when you do not yet know which document applies.",help:{name:"memory read",summary:"resolve an exact canonical name to its document body (frontmatter stripped unless --frontmatter), plus the directory listing when that name has members",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical memory name \u2014 a project document is `<namespace>/<local name>`, every other store\u2019s local names are already canonical, where a nested store's namespace includes its path under the repository root. Resolution is exact: a file path, a bare leaf, a `<scope>/<name>` spelling, and a trailing `/INDEX` all fail. A name that carries members returns its listing alongside its body; a name that is only a directory returns the listing alone."},{kind:"flag",name:"kind",type:"enum",choices:[...se],required:!1,constraint:"Narrows resolution when the name is ambiguous across kinds."},{kind:"flag",name:"frontmatter",type:"bool",required:!1,constraint:"When present, includes the YAML frontmatter in the returned body. Off by default \u2014 only the body is returned."},M("scope-read"),M("dir",{},"Reads that store alone: the name resolves against its documents only, and the listing shows its members only.")],output:[{name:"name",type:"string",required:!0,constraint:"Resolved canonical document name, or the directory name on a directory read."},{name:"local_name",type:"string",required:!1,constraint:"Store-local identity, before the store namespace composed the canonical name. Absent on a directory read."},{name:"kind",type:"string",required:!1,constraint:"Resolved kind: knowledge or preference. Absent on a directory read."},{name:"scope",type:"string",required:!1,constraint:"Scope the document was resolved from: node, project, profile, user, or builtin. Absent on a directory read."},{name:"path",type:"string",required:!1,constraint:"Absolute path to the document on disk. Revise it with `crtr memory edit`, never by editing this file \u2014 an edit records why the change happened and lands in the doc\u2019s revision history. Absent on a directory read."},{name:"store_root",type:"string",required:!1,constraint:"Absolute memory store root the document loaded from. Absent on a directory read."},{name:"physical_relative_path",type:"string",required:!1,constraint:"Store-relative physical markdown path, ordering prefixes and any `INDEX.md` kept. Absent on a directory read."},{name:"representation",type:"string",required:!1,constraint:"Physical representation of this identity: leaf-file or directory-index. Absent on a directory read."},{name:"candidates",type:"object[]",required:!1,constraint:"Present only when several physical documents share this canonical name: one row per candidate in source order, {winner, scope, store_root, physical_relative_path, path}. The winner is what this read returned; reach a non-winning one with `--dir <its project>`."},{name:"content",type:"string",required:!1,constraint:"Document body. Frontmatter stripped unless --frontmatter is set. A document may embed shell as `!`cmd`` or a ```! fenced block; each runs once per read, in the current working directory, and is replaced by its output. Read `path` off disk when you need the literal unexecuted text. May be preceded by one `<auto-loaded-context>` block carrying the doc\u2019s neighborhood listings and any docs routed to this read. Absent on a directory read."},{name:"listing",type:"string[]",required:!1,constraint:"Present when the name has members \u2014 a directory document returns its body AND this listing, a bare directory returns the listing alone: one `[[name]]: <when-and-why>` line per member doc, one bare `[[name]]` per subdirectory. Follow any line with `crtr memory read <name>`."},{name:"extensions",type:"object",required:!1,constraint:"Effective valid plugin-owned metadata, keyed by plugin namespace then field. Includes enabled declaration defaults without writing them to frontmatter; disabled and unresolved namespaces are omitted. Present only on a document read."},{name:"links",type:"string[]",required:!1,constraint:"Canonical names this document links to via `[[name]]` that resolve in the current corpus \u2014 further reading, loaded only on demand with `crtr memory read <name>`. A directory link returns that directory\u2019s listing. Omitted when the body carries no resolvable links."},{name:"follow_up",type:"string",required:!1,constraint:"Present whenever the read returned a listing or resolvable body links."}],outputKind:"object",effects:["Reads the document. A document that embeds shell (`!`cmd`` or a ```! block) runs those commands once, in the current working directory.","Records a content delivery for this node: a document whose profile project caps automatic boot and workspace-open disclosure below content is still returned whole here, and unsolicited channels stay quiet about it afterwards."]},run:i(async e=>{const r=e.name,l=e.kind,N=e.frontmatter,m=ce({scope:e.scope,dir:e.dir}),d=l===void 0?{}:{kind:l};let s=null,p,c,h;if(m.store!==null){const t=X(de(m.store),!0);c=g(t.docs),h=g([...t.docs,...me()]),s=t.candidates(r,d),p=i(()=>t.resolve(r,d),"fail")}else{const t={...d,...m.scope===void 0?{}:{scope:m.scope},includeDescendants:!0},a=$(E(),{...t,quiet:!0,reportDiscovery:!0});c=h=g(a.docs),s=a.candidates(r,d),p=i(()=>a.resolve(r,d),"fail")}const o=F()||void 0;if(s===null){const t=ie(r.trim()).join("/");if(t!==""&&j(c,t)){const a=o!==void 0?S(o):null,b=a===null?null:_(a,"transcript"),B=ne(c,t,b,!1);return o!==void 0&&a!==null&&b!==null&&(ee(b,te(t),"preview"),D(o,a)),{name:t,listing:B,follow_up:"Each line is readable with `crtr memory read <name>` \u2014 a `[[name]]: <line>` entry is a doc, a bare `[[name]]` is a subdirectory whose read browses deeper. Browse the whole inventory with `crtr memory list`."}}p()}const n=s.winner,P=ae(n),y=R(n.path,N),k=H(y)?await G(y,J({cwd:process.cwd()})):y,u=U(n.body).filter(t=>h.has(t)||j(h,t)),v=o!==void 0?S(o):Q(),x=_(v,"transcript"),C=await le(o);Z(x,n.path,n.body,"content");const f=re(n,{subject:C,target:x,byName:c,memoryTarget:E(),listings:"blocks",explicit:!0});o!==void 0&&D(o,v);const T=f.blocks.length===0?k:`<auto-loaded-context>
|
|
2
2
|
${f.blocks.join(`
|
|
3
3
|
`)}
|
|
4
4
|
</auto-loaded-context>
|
|
5
5
|
|
|
6
|
-
${k}`,w=f.listing,q=w!==void 0&&w.length>0,A=q?u.length>0?"Each `[[name]]` line in `listing` is one of this directory\u2019s immediate members, and the `[[name]]` links in the body are further reading \u2014 follow either with `crtr memory read <name>` only when the task needs that depth.":"Each `[[name]]` line in `listing` is one of this directory\u2019s immediate members, readable with `crtr memory read <name>` \u2014 follow one only when the task needs that depth.":u.length>0?"The `[[name]]` links in the body are further reading \u2014 follow one with `crtr memory read <name>` only when the task needs that depth.":null;return{name:n.name,local_name:n.localName,kind:
|
|
6
|
+
${k}`,w=f.listing,q=w!==void 0&&w.length>0,A=q?u.length>0?"Each `[[name]]` line in `listing` is one of this directory\u2019s immediate members, and the `[[name]]` links in the body are further reading \u2014 follow either with `crtr memory read <name>` only when the task needs that depth.":"Each `[[name]]` line in `listing` is one of this directory\u2019s immediate members, readable with `crtr memory read <name>` \u2014 follow one only when the task needs that depth.":u.length>0?"The `[[name]]` links in the body are further reading \u2014 follow one with `crtr memory read <name>` only when the task needs that depth.":null;return{name:n.name,local_name:n.localName,kind:P,scope:n.scope,path:n.path,store_root:n.root,physical_relative_path:n.physicalRelativePath,representation:n.representation,...s.candidates.length>1?{candidates:s.candidates.map(t=>he(t,n))}:{},content:T,...q?{listing:w}:{},extensions:Y(n.frontmatter?.extensions,K(n)),...u.length>0?{links:u}:{},...A===null?{}:{follow_up:A}}},"run"),render:i(e=>{const{extensions:r,...l}=e;return oe(l,ue.help)},"render")});export{he as candidateRow,V as createMemoryDocSnapshot,ue as readLeaf,R as readMemoryDocContent,z as resolveMemoryDocs};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var s=Object.defineProperty;var e=(n,t)=>s(n,"name",{value:t,configurable:!0});import{defineBranch as o}from"../../core/command.js";const r={concept:"crtr configuration, diagnostics, and self-management",desc:"settings, config, setup, doctor, context, sync, migrate, update, version, feedback, logs, support, daemon",useWhen:"managing the crtr installation, inspecting diagnostics, or operating the crtrd supervisor process"},a={name:"sys",summary:"crtr system configuration, settings, diagnostics, and self-management",model:"Configuration and settings own durable behavior; diagnostics and support observe it. Setup, migration, sync, and update change installed or generated state, while daemon control owns the supervisor lifecycle."};function d(n){return o({name:"sys",rootEntry:r,help:{...a},children:n})}e(d,"defineSysBranch");export{d as defineSysBranch};
|
|
1
|
+
var s=Object.defineProperty;var e=(n,t)=>s(n,"name",{value:t,configurable:!0});import{defineBranch as o}from"../../core/command.js";const r={concept:"crtr configuration, diagnostics, and self-management",desc:"settings, config, connect, setup, doctor, context, sync, migrate, update, version, feedback, logs, support, daemon",useWhen:"managing the crtr installation, inspecting diagnostics, or operating the crtrd supervisor process"},a={name:"sys",summary:"crtr system configuration, settings, diagnostics, and self-management",model:"Configuration and settings own durable behavior; diagnostics and support observe it. Setup, migration, sync, and update change installed or generated state, while daemon control owns the supervisor lifecycle."};function d(n){return o({name:"sys",rootEntry:r,help:{...a},children:n})}e(d,"defineSysBranch");export{d as defineSysBranch};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sysConnectLeaf: import("../../core/command.js").LeafDef;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var v=Object.defineProperty;var s=(e,t)=>v(e,"name",{value:t,configurable:!0});import{randomBytes as k}from"node:crypto";import C from"node:path";import{defineLeaf as _}from"../../core/command.js";import{readConfig as h,updateConfig as m}from"../../core/config.js";import{emit as P,InputError as u,isJsonOutput as $}from"../../core/io.js";import{renderResult as R}from"../../core/render.js";import{getApiToken as g,setApiToken as x}from"../../core/secrets.js";import{envCrtrdTcp as L,envCrtrdToken as w,envNodeId as I}from"../../shared/env.js";import{shellQuote as d}from"../../shared/shell-quote.js";import{cliClient as y}from"../api-client.js";const A="127.0.0.1:8787",b=3e4,E=100;function D(){return k(32).toString("hex")}s(D,"token");function N(e){$()?P(e):process.stdout.write(R(e,O.help)+`
|
|
2
|
+
`)}s(N,"printCredentials");function S(e){const t=e.lastIndexOf(":");return t===-1||Number(e.slice(t+1))!==0?e:`${e.slice(0,t+1)}8787`}s(S,"fixedTcpPort");async function q(){const e=y(),t=Date.now()+b;let n;for(;Date.now()<t;){try{const r=await e.status();if(r.listeners.tcp!==null)return r}catch(r){n=r}await new Promise(r=>setTimeout(r,E))}throw new u({error:"listener_unavailable",message:`the successor did not expose a TCP listener within ${b}ms${n instanceof Error?`: ${n.message}`:""}`,next:"Run `crtr sys daemon status` to inspect the daemon, then retry `crtr sys connect`."})}s(q,"awaitTcpListener");async function T(e,t){const n=await e.getModelAuthReadiness(t);if(n.credential==="ready")return;process.stderr.write(`Logging in to ${n.provider} for ${n.model}.
|
|
3
|
+
`);const{loginProvider:r,openProviderLoginRuntime:o,terminalAuthInteraction:a}=await import("./provider-login.js");await r(await o(),n.provider,a());const i=await e.getModelAuthReadiness(t);if(i.credential!=="ready")throw new u({error:"provider_login_incomplete",message:`provider ${i.provider} credential is ${i.credential} after login`,next:`Run \`${M(t)}\` again and complete the provider login.`})}s(T,"ensureProviderCredential");function M(e){return["crtr sys connect",e.profile===void 0?void 0:`--profile ${d(e.profile)}`,e.cwd===void 0?void 0:`--cwd ${d(e.cwd)}`,e.kind===void 0?void 0:`--kind ${d(e.kind)}`,e.model===void 0?void 0:`--model ${d(e.model)}`].filter(t=>t!==void 0).join(" ")}s(M,"connectCommand");const O=_({name:"connect",description:"enable browser access to this daemon",whenToUse:"an application needs a browser-reachable daemon URL and bearer token; this enables the local TCP listener and reports the credentials to paste into that application.",help:{name:"sys connect",summary:"enable authenticated browser access and log in to the selected model provider when needed; a handover reports browser credentials before it begins",params:[{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Profile setting selecting the provider a root launch with these settings would use."},{kind:"flag",name:"cwd",type:"string",required:!1,constraint:"Directory setting selecting the provider a root launch with these settings would use. Default: this directory."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Node kind setting selecting the provider a root launch with these settings would use."},{kind:"flag",name:"model",type:"string",required:!1,constraint:"Model setting selecting the provider a root launch with these settings would use."}],output:[{name:"base_url",type:"string",required:!0,constraint:"The HTTP URL configured for the TCP listener."},{name:"token",type:"string",required:!0,constraint:"The bearer token accepted by that listener; paste it only into the application that needs daemon access."}],outputKind:"object",effects:["Stores a missing listener address in user settings and a missing bearer token in the 0600 user secrets store.","When the listener is not already up with both settings present, asks the daemon to hand over to a successor so it starts the listener.","After the listener is available, resolves the provider selected by these params (default: this directory) and runs its existing OAuth login flow when that provider has no usable credential."]},run:s(async e=>{const t={profile:e.profile,cwd:C.resolve(e.cwd??process.cwd()),kind:e.kind,model:e.model},n=I(),o=h("user").api.tcp,a=g("user"),i=y(),p=await i.status();o===""?m("user",l=>{l.api.tcp=A}):Number(o.slice(o.lastIndexOf(":")+1))===0&&m("user",l=>{l.api.tcp=S(o)});const c=L()??h("user").api.tcp;if(Number(c.slice(c.lastIndexOf(":")+1))===0)throw new u({error:"dynamic_tcp_port",message:"CRTRD_TCP must use a fixed port for `crtr sys connect`",next:"Set CRTRD_TCP to a host and fixed port, then run `crtr sys connect` again."});if(o!==""&&a!==void 0&&p.listeners.tcp!==null)return await T(i,t),{base_url:`http://${p.listeners.tcp}`,token:w()??a};a===void 0&&x(D(),"user");const f=w()??g("user");if(f===void 0)throw new Error("TCP listener cannot start without a bearer token");N({base_url:`http://${c}`,token:f}),await i.restartDaemon(),await q(),await T(i,t),n!==void 0&&await i.sendMessage(n,{body:"Browser access is ready.",tier:"urgent",from:n})},"run")});export{O as sysConnectLeaf};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var s=(r,e)=>c(r,"name",{value:e,configurable:!0});import{defineLeaf as d,defineBranch as w}from"../../core/command.js";import{InputError as o}from"../../core/io.js";import{parseDurationMs as f}from"../../core/wake.js";import{DaemonNotPreparedError as g,probeDaemonHealth as u,spawnDaemon as h,stopDaemonProcess as y,sweepStrayDaemons as b}from"../../daemon/manage.js";import{isDaemonRunning as v,readPidfile as m,isPidAlive as l}from"../../daemon/pidfile.js";import{cliClient as k}from"../api-client.js";import{CrtrClient as _}from"../../api/index.js";import{defineSysBranch as T}from"./branch.js";const q=d({name:"start",description:"start the daemon in the background",whenToUse:"bringing the crtrd supervisor up for the first time in a session so node window-exits get handled (no-op only when its Unix API socket already serves)",help:{name:"sys daemon start",summary:"start the crtrd supervisor daemon in the background (no-op only if its Unix API socket serves)",params:[{kind:"flag",name:"prepare",type:"bool",required:!1,constraint:"Hold normal daemon work after storage preparation until `sys daemon admit`; omit for ordinary startup."}],output:[{name:"started",type:"boolean",required:!0,constraint:"True when a new daemon process was spawned; false when one was already running."},{name:"pid",type:"number",required:!1,constraint:"PID of the newly spawned daemon."},{name:"existing_pid",type:"number",required:!1,constraint:"PID of the already-running daemon (when started=false)."},{name:"running",type:"boolean",required:!1,constraint:"True only when a live pidfile owner does not serve its Unix API socket."},{name:"serving",type:"boolean",required:!1,constraint:"False only when a live pidfile owner does not answer /healthz."},{name:"probe_error",type:"string",required:!1,constraint:"The final /healthz probe failure when a live owner missed readiness."},{name:"startup_phase",type:"string",required:!1,constraint:"The observed daemon phase when it was reachable."}],outputKind:"object",effects:["Spawns dist/daemon/crtrd-cli.js detached + unref'd; it outlives this process.","Writes a pidfile at CRTR_PIDFILE if set, otherwise crtrHome()/crtrd.pid."]},run:s(async r=>{try{return await h({targetPhase:r.prepare===!0?"prepared":"ready"})}catch(e){throw e instanceof g?new o({error:"daemon_not_prepared",message:e.message,next:"Stop the live daemon, then start a new one with `crtr sys daemon start --prepare` before setup."}):e}},"run")}),x=100;async function D(r,e){const n=Date.now(),a=n+e;let i="initializing",p="the daemon socket was not reachable";for(;Date.now()<a;){try{const t=await u(Math.min(1e3,Math.max(1,a-Date.now())));if(i=t.startup_phase,p=t.startup_blocked??`daemon is ${t.startup_phase}`,t.startup_phase==="blocked")throw new o({error:"startup_blocked",message:t.startup_blocked??"daemon startup is blocked",received:t.startup_phase,field:"phase",next:"Repair the reported blocker paths with `crtr sys migrate`, then retry the await."});if(t.startup_phase===r)return{startup_phase:r,waited_ms:Date.now()-n};if(r==="prepared"&&t.startup_phase==="ready")throw new o({error:"await_missed_phase",message:"daemon is already ready and cannot return to prepared in this process",received:t.startup_phase,field:"phase",next:"Start a new daemon with `crtr sys daemon start --prepare` before awaiting prepared."})}catch(t){if(t instanceof o)throw t;p=t instanceof Error?t.message:String(t)}await new Promise(t=>setTimeout(t,Math.min(x,Math.max(0,a-Date.now()))))}throw new o({error:"await_timeout",message:`daemon did not reach ${r} within ${e}ms (last phase: ${i}; last observation: ${p})`,received:i,field:"phase",next:"Inspect `crtr sys daemon status` and resolve any reported startup blocker before retrying."})}s(D,"awaitDaemonPhase");const I=d({name:"await",description:"wait for a daemon startup phase",whenToUse:"a caller started or was given a daemon that must reach prepared or ready before its next step.",help:{name:"sys daemon await",summary:"wait for the running daemon to report one startup phase",params:[{kind:"flag",name:"phase",type:"enum",choices:["prepared","ready"],required:!0,constraint:"The phase that permits the caller\u2019s next step."},{kind:"flag",name:"timeout",type:"string",required:!1,default:"5m",constraint:"Maximum wait as a positive duration such as 90s, 5m, or 1h30m."}],output:[{name:"startup_phase",type:"string",required:!0,constraint:"The requested phase."},{name:"waited_ms",type:"number",required:!0,constraint:"Elapsed wall-clock time before the phase was observed."}],outputKind:"object",effects:["Read-only: polls /healthz until the requested phase is observed or the caller-bounded timeout expires."]},run:s(async r=>{const e=r.timeout,n=f(e);if(n===null||n<=0)throw new o({error:"invalid_duration",message:`not a positive duration: ${e}`,received:e,field:"timeout",next:"Use a positive duration such as 90s, 5m, or 1h30m."});return D(r.phase,n)},"run")}),S=d({name:"admit",description:"admit normal work after daemon preparation",whenToUse:"the caller has completed its setup against a prepared daemon and is ready for normal daemon work to start.",help:{name:"sys daemon admit",summary:"ask a prepared daemon to start normal services",params:[],output:[{name:"admitted",type:"boolean",required:!0,constraint:"True only when this request began admission."},{name:"already_admitted",type:"boolean",required:!0,constraint:"True when admission had already begun or completed."},{name:"startup_phase",type:"string",required:!0,constraint:"The phase after admission settles."}],outputKind:"object",effects:["Starts normal daemon services when the daemon is prepared."]},run:s(async r=>await _.forLocalSocket({autostart:!1}).admitDaemon(),"run")}),E=d({name:"status",description:"check whether the daemon is running",whenToUse:"operator/dev diagnostic \u2014 probing whether the crtrd supervisor process is alive while troubleshooting revive/supervision behavior",help:{name:"sys daemon status",summary:"check whether the crtrd supervisor daemon is currently running",params:[],output:[{name:"running",type:"boolean",required:!0,constraint:"True when the daemon pidfile owner is alive."},{name:"serving",type:"boolean",required:!1,constraint:"True when the live daemon answers /healthz; false when one probe does not."},{name:"pid",type:"number",required:!1,constraint:"PID of the running daemon."},{name:"probe_error",type:"string",required:!1,constraint:"The failed /healthz observation when the live owner did not answer."},{name:"startup_phase",type:"string",required:!1,constraint:"The observed daemon startup phase when the socket answered."}],outputKind:"object",effects:["Read-only: reads the pidfile, probes the pid, and requests /healthz without autostart."]},run:s(async r=>{const e=m();if(!(e!==null&&l(e)))return{running:!1};try{const a=await u();return{running:!0,serving:a.ok,pid:e,...a.startup_phase===void 0?{}:{startup_phase:a.startup_phase}}}catch(a){return{running:!0,serving:!1,pid:e,probe_error:a.message}}},"run")}),P=d({name:"restart",description:"put the selected runtime generation in service via a daemon-owned handover",whenToUse:"adopting a newly installed runtime generation \u2014 the routine restart. Use this instead of stop+start: the daemon answers before it hands over, so a caller running inside a node (whose broker the handover tears down) still gets a settled result and is resumed automatically.",help:{name:"sys daemon restart",summary:"ask the running daemon to replace itself with a successor on the selected runtime generation",params:[],output:[{name:"restarted",type:"boolean",required:!0,constraint:"True when a running daemon accepted the handover. False when none was running \u2014 one was started instead."},{name:"started",type:"boolean",required:!1,constraint:"True when no daemon was running and this call started one (restarted=false)."},{name:"pid",type:"number",required:!1,constraint:"The daemon that accepted the handover, or the one just started."},{name:"grace_ms",type:"number",required:!1,constraint:"How long the daemon waits after answering before teardown begins."},{name:"note",type:"string",required:!1,constraint:"What happens next to the calling node. Read it before acting on an interrupted turn."}],outputKind:"object",effects:["The daemon acknowledges immediately, then after a short grace tears its whole broker fleet down, releases its claim, spawns a successor on the runtime generation currently selected, and exits.","Each torn-down broker hands its foreground bash commands to the file-backed background job system; they keep running and report completion by urgent inbox message.","Every node it tore down \u2014 including the caller \u2014 is resolved by the successor through the ordinary row-driven supervision tick.","The calling process is killed as part of that teardown, AFTER this result is returned."]},run:s(async r=>{if(!v()){const e=await h();return{restarted:!1,started:e.started,...e.pid!==void 0?{pid:e.pid}:{},...e.existing_pid!==void 0?{pid:e.existing_pid}:{},note:"No daemon was running, so there was nothing to hand over; one was started on the selected runtime generation."}}try{const e=await k().restartDaemon();return{restarted:e.accepted,pid:e.predecessor_pid,grace_ms:e.grace_ms,note:"Handover accepted. If you are running inside a node, your broker is about to be torn down and resumed on the new generation \u2014 THIS RESTART ALREADY SUCCEEDED. When your turn resumes with this result in the transcript, do not re-run it."}}catch(e){throw new o({error:"daemon_restart_failed",message:`the daemon did not accept a handover: ${e.message}`,next:"Run `crtr sys daemon status` to inspect the daemon."})}},"run")}),R=d({name:"stop",description:"stop the running daemon",whenToUse:"shutting the crtrd supervisor down for good, ending auto-revival of nodes on window exit. Not the way to pick up a new runtime generation \u2014 `restart` is, because stopping kills every broker of this epoch, including the caller's own, so this command cannot report its result to an agent that runs it.",help:{name:"sys daemon stop",summary:"stop the crtrd supervisor daemon and confirm it exited",params:[],output:[{name:"stopped",type:"boolean",required:!0,constraint:"True when a daemon (recorded or stray) was found and killed. False when none was running."},{name:"pid",type:"number",required:!1,constraint:"The recorded pidfile pid that exited (when one was recorded and alive)."},{name:"strays_killed",type:"array",required:!1,constraint:"PIDs of stray crtrd daemons (alive but not in the pidfile) swept alongside the recorded one \u2014 the backstop against a daemon that survived a prior stop."},{name:"force_killed",type:"boolean",required:!1,constraint:"True when the recorded daemon ignored SIGTERM and had to be SIGKILLed."}],outputKind:"object",effects:["Sends SIGTERM to the recorded pid and waits for it to exit and release its pidfile, escalating to SIGKILL if it does not.","Then sweeps any OTHER live crtrd daemon process (SIGTERM, escalating to SIGKILL after 2s) so a stray daemon orphaned from the pidfile cannot keep supervising."]},run:s(async r=>{const e=m(),n=e!==null&&l(e);try{let a=!1;n&&(a=await y(e)==="killed");const i=b(null);return!n&&i.length===0?{stopped:!1}:{stopped:!0,...n?{pid:e}:{},...i.length>0?{strays_killed:i}:{},...a?{force_killed:!0}:{}}}catch(a){throw new o({error:"daemon_stop_failed",message:`failed to stop daemon${n?` pid ${e}`:""}: ${a.message}`,next:"Run `crtr sys daemon status` to inspect the daemon before retrying."})}},"run")}),A=w({name:"daemon",description:"manage the crtrd supervisor process",whenToUse:"managing the crtrd supervisor that auto-revives nodes on window exit \u2014 start it, restart it onto a new runtime generation, check its status, or stop it",help:{name:"sys daemon",summary:"manage the crtrd canvas supervisor daemon",model:"crtrd is a thin background daemon that polls active+idle nodes and acts on window exit: crashed windows become dead; refresh-yield windows get a fresh respawn. It holds no orchestration logic \u2014 just process supervision. These are operator/dev self-management, not something a normal agent turn needs to check. Picking up a newly installed runtime generation is `restart` (a handover the daemon performs on itself); `stop` is the terminal one, and it kills the caller along with the rest of the fleet."},children:[q,I,S,P,E,R]});function G(){return T([A])}s(G,"registerDaemonControlSys");export{A as daemonBranch,G as registerDaemonControlSys};
|
|
1
|
+
var c=Object.defineProperty;var s=(r,e)=>c(r,"name",{value:e,configurable:!0});import{defineLeaf as d,defineBranch as w}from"../../core/command.js";import{InputError as o}from"../../core/io.js";import{parseDurationMs as f}from"../../core/wake.js";import{DaemonNotPreparedError as g,probeDaemonHealth as u,probeErrorMessage as y,spawnDaemon as h,stopDaemonProcess as b,sweepStrayDaemons as v}from"../../daemon/manage.js";import{isDaemonRunning as _,readPidfile as m,isPidAlive as l}from"../../daemon/pidfile.js";import{cliClient as k}from"../api-client.js";import{localClient as T}from"../../api/node-transport.js";import{defineSysBranch as q}from"./branch.js";const x=d({name:"start",description:"start the daemon in the background",whenToUse:"bringing the crtrd supervisor up for the first time in a session so node window-exits get handled (no-op only when its Unix API socket already serves)",help:{name:"sys daemon start",summary:"start the crtrd supervisor daemon in the background (no-op only if its Unix API socket serves)",params:[{kind:"flag",name:"prepare",type:"bool",required:!1,constraint:"Hold normal daemon work after storage preparation until `sys daemon admit`; omit for ordinary startup."}],output:[{name:"started",type:"boolean",required:!0,constraint:"True when a new daemon process was spawned; false when one was already running."},{name:"pid",type:"number",required:!1,constraint:"PID of the newly spawned daemon."},{name:"existing_pid",type:"number",required:!1,constraint:"PID of the already-running daemon (when started=false)."},{name:"running",type:"boolean",required:!1,constraint:"True only when a live pidfile owner does not serve its Unix API socket."},{name:"serving",type:"boolean",required:!1,constraint:"False only when a live pidfile owner does not answer /healthz."},{name:"probe_error",type:"string",required:!1,constraint:"The final /healthz probe failure when a live owner missed readiness."},{name:"startup_phase",type:"string",required:!1,constraint:"The observed daemon phase when it was reachable."}],outputKind:"object",effects:["Spawns dist/daemon/crtrd-cli.js detached + unref'd; it outlives this process.","Writes a pidfile at CRTR_PIDFILE if set, otherwise crtrHome()/crtrd.pid."]},run:s(async r=>{try{return await h({targetPhase:r.prepare===!0?"prepared":"ready"})}catch(e){throw e instanceof g?new o({error:"daemon_not_prepared",message:e.message,next:"Stop the live daemon, then start a new one with `crtr sys daemon start --prepare` before setup."}):e}},"run")}),D=100;async function I(r,e){const n=Date.now(),a=n+e;let i="initializing",p="the daemon socket was not reachable";for(;Date.now()<a;){try{const t=await u(Math.min(1e3,Math.max(1,a-Date.now())));if(i=t.startup_phase,p=t.startup_blocked??`daemon is ${t.startup_phase}`,t.startup_phase==="blocked")throw new o({error:"startup_blocked",message:t.startup_blocked??"daemon startup is blocked",received:t.startup_phase,field:"phase",next:"Repair the reported blocker paths with `crtr sys migrate`, then retry the await."});if(t.startup_phase===r)return{startup_phase:r,waited_ms:Date.now()-n};if(r==="prepared"&&t.startup_phase==="ready")throw new o({error:"await_missed_phase",message:"daemon is already ready and cannot return to prepared in this process",received:t.startup_phase,field:"phase",next:"Start a new daemon with `crtr sys daemon start --prepare` before awaiting prepared."})}catch(t){if(t instanceof o)throw t;p=t instanceof Error?t.message:String(t)}await new Promise(t=>setTimeout(t,Math.min(D,Math.max(0,a-Date.now()))))}throw new o({error:"await_timeout",message:`daemon did not reach ${r} within ${e}ms (last phase: ${i}; last observation: ${p})`,received:i,field:"phase",next:"Inspect `crtr sys daemon status` and resolve any reported startup blocker before retrying."})}s(I,"awaitDaemonPhase");const S=d({name:"await",description:"wait for a daemon startup phase",whenToUse:"a caller started or was given a daemon that must reach prepared or ready before its next step.",help:{name:"sys daemon await",summary:"wait for the running daemon to report one startup phase",params:[{kind:"flag",name:"phase",type:"enum",choices:["prepared","ready"],required:!0,constraint:"The phase that permits the caller\u2019s next step."},{kind:"flag",name:"timeout",type:"string",required:!1,default:"5m",constraint:"Maximum wait as a positive duration such as 90s, 5m, or 1h30m."}],output:[{name:"startup_phase",type:"string",required:!0,constraint:"The requested phase."},{name:"waited_ms",type:"number",required:!0,constraint:"Elapsed wall-clock time before the phase was observed."}],outputKind:"object",effects:["Read-only: polls /healthz until the requested phase is observed or the caller-bounded timeout expires."]},run:s(async r=>{const e=r.timeout,n=f(e);if(n===null||n<=0)throw new o({error:"invalid_duration",message:`not a positive duration: ${e}`,received:e,field:"timeout",next:"Use a positive duration such as 90s, 5m, or 1h30m."});return I(r.phase,n)},"run")}),E=d({name:"admit",description:"admit normal work after daemon preparation",whenToUse:"the caller has completed its setup against a prepared daemon and is ready for normal daemon work to start.",help:{name:"sys daemon admit",summary:"ask a prepared daemon to start normal services",params:[],output:[{name:"admitted",type:"boolean",required:!0,constraint:"True only when this request began admission."},{name:"already_admitted",type:"boolean",required:!0,constraint:"True when admission had already begun or completed."},{name:"startup_phase",type:"string",required:!0,constraint:"The phase after admission settles."}],outputKind:"object",effects:["Starts normal daemon services when the daemon is prepared."]},run:s(async r=>await T({autostart:!1}).admitDaemon(),"run")}),P=d({name:"status",description:"check whether the daemon is running",whenToUse:"operator/dev diagnostic \u2014 probing whether the crtrd supervisor process is alive while troubleshooting revive/supervision behavior",help:{name:"sys daemon status",summary:"check whether the crtrd supervisor daemon is currently running",params:[],output:[{name:"running",type:"boolean",required:!0,constraint:"True when the daemon pidfile owner is alive."},{name:"serving",type:"boolean",required:!1,constraint:"True when the live daemon answers /healthz; false when one probe does not."},{name:"pid",type:"number",required:!1,constraint:"PID of the running daemon."},{name:"probe_error",type:"string",required:!1,constraint:"The failed /healthz observation when the live owner did not answer."},{name:"startup_phase",type:"string",required:!1,constraint:"The observed daemon startup phase when the socket answered."}],outputKind:"object",effects:["Read-only: reads the pidfile, probes the pid, and requests /healthz without autostart."]},run:s(async r=>{const e=m();if(!(e!==null&&l(e)))return{running:!1};try{const a=await u();return{running:!0,serving:a.ok,pid:e,...a.startup_phase===void 0?{}:{startup_phase:a.startup_phase}}}catch(a){return{running:!0,serving:!1,pid:e,probe_error:y(a)}}},"run")}),R=d({name:"restart",description:"put the selected runtime generation in service via a daemon-owned handover",whenToUse:"adopting a newly installed runtime generation \u2014 the routine restart. Use this instead of stop+start: the daemon answers before it hands over, so a caller running inside a node (whose broker the handover tears down) still gets a settled result and is resumed automatically.",help:{name:"sys daemon restart",summary:"ask the running daemon to replace itself with a successor on the selected runtime generation",params:[],output:[{name:"restarted",type:"boolean",required:!0,constraint:"True when a running daemon accepted the handover. False when none was running \u2014 one was started instead."},{name:"started",type:"boolean",required:!1,constraint:"True when no daemon was running and this call started one (restarted=false)."},{name:"pid",type:"number",required:!1,constraint:"The daemon that accepted the handover, or the one just started."},{name:"grace_ms",type:"number",required:!1,constraint:"How long the daemon waits after answering before teardown begins."},{name:"note",type:"string",required:!1,constraint:"What happens next to the calling node. Read it before acting on an interrupted turn."}],outputKind:"object",effects:["The daemon acknowledges immediately, then after a short grace tears its whole broker fleet down, releases its claim, spawns a successor on the runtime generation currently selected, and exits.","Each torn-down broker hands its foreground bash commands to the file-backed background job system; they keep running and report completion by urgent inbox message.","Every node it tore down \u2014 including the caller \u2014 is resolved by the successor through the ordinary row-driven supervision tick.","The calling process is killed as part of that teardown, AFTER this result is returned."]},run:s(async r=>{if(!_()){const e=await h();return{restarted:!1,started:e.started,...e.pid!==void 0?{pid:e.pid}:{},...e.existing_pid!==void 0?{pid:e.existing_pid}:{},note:"No daemon was running, so there was nothing to hand over; one was started on the selected runtime generation."}}try{const e=await k().restartDaemon();return{restarted:e.accepted,pid:e.predecessor_pid,grace_ms:e.grace_ms,note:"Handover accepted. If you are running inside a node, your broker is about to be torn down and resumed on the new generation \u2014 THIS RESTART ALREADY SUCCEEDED. When your turn resumes with this result in the transcript, do not re-run it."}}catch(e){throw new o({error:"daemon_restart_failed",message:`the daemon did not accept a handover: ${e.message}`,next:"Run `crtr sys daemon status` to inspect the daemon."})}},"run")}),A=d({name:"stop",description:"stop the running daemon",whenToUse:"shutting the crtrd supervisor down for good, ending auto-revival of nodes on window exit. Not the way to pick up a new runtime generation \u2014 `restart` is, because stopping kills every broker of this epoch, including the caller's own, so this command cannot report its result to an agent that runs it.",help:{name:"sys daemon stop",summary:"stop the crtrd supervisor daemon and confirm it exited",params:[],output:[{name:"stopped",type:"boolean",required:!0,constraint:"True when a daemon (recorded or stray) was found and killed. False when none was running."},{name:"pid",type:"number",required:!1,constraint:"The recorded pidfile pid that exited (when one was recorded and alive)."},{name:"strays_killed",type:"array",required:!1,constraint:"PIDs of stray crtrd daemons (alive but not in the pidfile) swept alongside the recorded one \u2014 the backstop against a daemon that survived a prior stop."},{name:"force_killed",type:"boolean",required:!1,constraint:"True when the recorded daemon ignored SIGTERM and had to be SIGKILLed."}],outputKind:"object",effects:["Sends SIGTERM to the recorded pid and waits for it to exit and release its pidfile, escalating to SIGKILL if it does not.","Then sweeps any OTHER live crtrd daemon process (SIGTERM, escalating to SIGKILL after 2s) so a stray daemon orphaned from the pidfile cannot keep supervising."]},run:s(async r=>{const e=m(),n=e!==null&&l(e);try{let a=!1;n&&(a=await b(e)==="killed");const i=v(null);return!n&&i.length===0?{stopped:!1}:{stopped:!0,...n?{pid:e}:{},...i.length>0?{strays_killed:i}:{},...a?{force_killed:!0}:{}}}catch(a){throw new o({error:"daemon_stop_failed",message:`failed to stop daemon${n?` pid ${e}`:""}: ${a.message}`,next:"Run `crtr sys daemon status` to inspect the daemon before retrying."})}},"run")}),U=w({name:"daemon",description:"manage the crtrd supervisor process",whenToUse:"managing the crtrd supervisor that auto-revives nodes on window exit \u2014 start it, restart it onto a new runtime generation, check its status, or stop it",help:{name:"sys daemon",summary:"manage the crtrd canvas supervisor daemon",model:"crtrd is a thin background daemon that polls active+idle nodes and acts on window exit: crashed windows become dead; refresh-yield windows get a fresh respawn. It holds no orchestration logic \u2014 just process supervision. These are operator/dev self-management, not something a normal agent turn needs to check. Picking up a newly installed runtime generation is `restart` (a handover the daemon performs on itself); `stop` is the terminal one, and it kills the caller along with the rest of the fleet."},children:[x,S,E,R,P,A]});function F(){return q([U])}s(F,"registerDaemonControlSys");export{U as daemonBranch,F as registerDaemonControlSys};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var S=Object.defineProperty;var m=(u,e)=>S(u,"name",{value:e,configurable:!0});import{LoginDialogComponent as k}from"@earendil-works/pi-coding-agent";import{Input as R,matchesKey as h,truncateToWidth as D}from"@earendil-works/pi-tui";import{execFile as E}from"node:child_process";import{dialogAuthInteraction as B}from"../../../core/auth-interaction.js";import{isManagedProvider as F,promoteSubscription as N,readSubscriptionPool as T,renameSubscription as U}from"../../../core/subscription-state.js";import{removeProviderRow as _}from"../../../core/provider-management.js";import{cliClient as V}from"../../api-client.js";import{loginProvider as j}from"../provider-login.js";import{fitTail as C,theme as l,visibleRange as A}from"../../../core/tui/panel.js";function M(u,e){const t=u.getProviders().filter(i=>i.auth.oauth!==void 0),o=new Set(t.map(i=>i.id)),n=new Set;return[...t.map(i=>({providerId:i.id,name:i.name,managed:F(i.id),authType:"oauth"})),...e.getAll().map(i=>({providerId:i.provider,name:e.getProviderDisplayName(i.provider),managed:F(i.provider),authType:o.has(i.provider)?"oauth":"api_key"}))].filter(i=>!n.has(i.providerId)&&!!n.add(i.providerId))}m(M,"buildProviderCatalog");function q(u,e){return e||u instanceof Error&&u.name==="AbortError"?"Login cancelled":`Login error: ${u instanceof Error?u.message:String(u)}`}m(q,"classifyLoginFailure");class Z{static{m(this,"ProviderPanel")}tui;agentDir;runtime;registry;renameInput=new R;rows=[];selected=0;mode="list";notice="";catalog=[];catalogIndex=0;loginProvider;loginFlow;mutationInFlight=!1;onChange;constructor(e,t,o,n){this.tui=e,this.agentDir=t,this.runtime=o,this.registry=n,this.renameInput.onSubmit=()=>this.rename(),this.renameInput.onEscape=()=>{this.mode="list"},this.reload()}notifyChange(){this.onChange?.(),this.tui.requestRender()}reloadAfterAuthChange(){this.reload(),this.registry.refresh().then(()=>{this.reload(),this.notifyChange()})}reload(){const e=this.registry,t=[];for(const o of M(this.runtime,e)){const n=e.getProviderAuthStatus(o.providerId);if(o.managed){const i=T(o.providerId);if(i.length===0&&!n.source)continue;t.push({...o,configured:i.length>0||n.configured,source:n.source,sourceLabel:n.label}),i.forEach((s,c)=>t.push({...o,label:s.label,preferred:c===0,cooldownUntil:s.rateLimitedUntil,accountId:s.accountId}));continue}n.source&&t.push({...o,configured:n.configured,source:n.source,sourceLabel:n.label})}this.rows=t,this.selected=Math.min(this.selected,Math.max(0,t.length))}selectedRow(){return this.rows[this.selected]}openBrowser(e){const t=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",o=process.platform==="win32"?["/c","start","",e]:[e];E(t,o,{timeout:5e3},()=>{})}openCatalog(){this.catalog=M(this.runtime,this.registry),this.catalogIndex=0,this.mode="catalog"}startLogin(e,t){this.loginProvider=e;let o;const n=m(r=>{!o.active||this.loginFlow!==o||(o.active=!1,this.loginFlow=void 0,this.mode="list",this.notice=r,this.reloadAfterAuthChange(),this.notifyChange())},"complete"),i=new k(this.tui,e.providerId,(r,p)=>{r||n(p?`Login failed: ${p}`:"Login cancelled")},e.name);o={dialog:i,active:!0,committing:!1},this.loginFlow=o,this.mode="login";const s=i.handleInput.bind(i);i.handleInput=r=>{o.committing||s(r)};const c=m(()=>o.active&&this.loginFlow===o&&!i.signal.aborted,"isActive"),d=m(()=>c()?(o.committing=!0,i.showDetails(["Saving credentials\u2026"]),!0):!1,"beginCommit"),g=m(r=>{n(q(r,i.signal.aborted))},"fail");if(e.authType==="api_key"){i.showPrompt(`Enter API key for ${e.name}:`,"sk-...").then(async r=>{if(c()){if(!r.trim()){n("No key entered \u2014 login cancelled");return}d()&&(await V().installCredential(e.providerId,{kind:"api_key",api_key:r.trim()}),n(`API key saved for ${e.name}`))}}).catch(g);return}const $=B(i,{openBrowser:m(r=>this.openBrowser(r),"openBrowser"),onChange:m(()=>this.notifyChange(),"onChange"),isActive:c});j(this.runtime,e.providerId,$,{label:t,beforeInstall:d}).then(r=>{r&&c()&&n(`Logged in to ${e.name}`)}).catch(g)}isNestedFlowActive(){return this.mode==="login"}dispose(){const e=this.loginFlow;e&&(e.active=!1,this.loginFlow=void 0,e.dialog.handleInput("\x1B"))}runMutation(e){this.mutationInFlight||(this.mutationInFlight=!0,e().catch(t=>{this.notice=t instanceof Error?t.message:String(t)}).finally(()=>{this.mutationInFlight=!1,this.notifyChange()}))}rename(){const e=this.selectedRow();!e?.managed||!e.label||(this.mode="list",this.runMutation(async()=>{await U(e.providerId,e.label,this.renameInput.getValue()),this.notice=`Renamed ${e.name} account`,this.reload()}))}remove(e){this.runMutation(async()=>{this.notice=await _(this.agentDir,e),this.reloadAfterAuthChange()})}promote(e){this.runMutation(async()=>{await N(e.providerId,e.label),this.notice=`Preferred ${e.name} account`,this.reload()})}handleInput(e){if(this.mode==="login")return this.loginFlow?.dialog.handleInput(e),!0;if(this.mutationInFlight)return!0;if(this.mode==="rename"||this.mode==="label")return this.renameInput.handleInput(e),!0;if(this.mode==="catalog"){if(h(e,"escape"))return this.mode="list",!0;if(h(e,"j")||h(e,"down"))return this.catalogIndex=Math.min(this.catalog.length-1,this.catalogIndex+1),!0;if(h(e,"k")||h(e,"up"))return this.catalogIndex=Math.max(0,this.catalogIndex-1),!0;if(h(e,"enter")){const n=this.catalog[this.catalogIndex];return n.managed?(this.loginProvider=n,this.renameInput.setValue(""),this.renameInput.onSubmit=()=>{const i=this.renameInput.getValue().trim();i&&this.startLogin(n,i)},this.mode="label"):this.startLogin(n),!0}return!0}if(this.mode==="remove"){if(h(e,"y")||h(e,"enter")){const n=this.selectedRow();n&&(n.managed||n.configured)&&this.remove(n)}return this.mode="list",!0}if(h(e,"j")||h(e,"down"))return this.selected=Math.min(this.rows.length,this.selected+1),!0;if(h(e,"k")||h(e,"up"))return this.selected=Math.max(0,this.selected-1),!0;const t=this.selectedRow();if(h(e,"p")&&t?.managed&&t.label)return this.promote(t),!0;if(h(e,"r")&&t?.managed&&t.label)return this.renameInput.setValue(t.label),this.renameInput.onSubmit=()=>this.rename(),this.mode="rename",!0;if(h(e,"x")&&(t?.managed||t?.configured))return this.mode="remove",!0;if(h(e,"a"))return this.openCatalog(),!0;const o=t?.source==="environment"||t?.source==="runtime";return h(e,"l")&&t&&!o?(t.managed&&t.label?this.startLogin(t,t.label):this.startLogin(t),!0):h(e,"enter")?(t&&!o?this.startLogin(t,t.managed&&t.label?t.label:void 0):t||this.openCatalog(),!0):!1}badge(){return String(this.rows.filter(e=>e.label===void 0).length)}render(e,t){const o=l.fg("accent",l.bold("Providers")),n=l.fg("dim",`${this.rows.filter(c=>c.label===void 0).length} configured`),i=Math.max(1,t);let s;if(this.mode==="login")s=C(this.loginFlow?.dialog.render(e)??[],i);else if(this.mode==="rename"||this.mode==="label")s=C([o,n,"",l.fg("accent",this.mode==="rename"?"Rename account":`Name the ${this.loginProvider?.name??"provider"} account`),...this.renameInput.render(Math.max(1,e-8))],i);else if(this.mode==="remove"){const c=this.selectedRow(),d=c?.label??(c?.managed?`${c.name} \xB7 all accounts`:c?.name??"account");s=C([o,n,"",l.fg("warning",`Remove ${d}? y/enter confirm \xB7 any other key cancel`)],i)}else{const c=[o,n].slice(0,Math.max(0,i-1)),d=i-c.length;if(s=[...c],this.mode==="catalog"){const g=this.catalog.length,r=g>d&&d>=2,p=r?d-1:d,{start:I,end:v}=A(g,this.catalogIndex,p);let w=d-(v-I)-(r?1:0);w>0&&(s.push(l.fg("dim","Select a provider to add \xB7 esc back")),w-=1);for(let f=I;f<v;f++){const x=this.catalog[f];s.push(`${f===this.catalogIndex?l.fg("accent","\u25B6"):" "} ${x.name}`)}r&&s.push(l.fg("dim",`(${this.catalogIndex+1}/${g})`))}else{const g=this.rows.length+1,r=g>d&&d>=2,p=r?d-1:d,{start:I,end:v}=A(g,this.selected,p);let w=d-(v-I)-(r?1:0);const f=this.rows.length===0&&w>0;f&&(w-=1);const x=!!this.notice&&w>0;f&&s.push(l.fg("warning","No provider configured yet."));for(let b=I;b<v;b++){if(b===this.rows.length){s.push(`${this.selected===this.rows.length?l.fg("accent","\u25B6"):" "} ${l.fg("accent","\uFF0B Add provider")}`);continue}const a=this.rows[b],P=b===this.selected?l.fg("accent","\u25B6"):" ";if(a.label){const L=a.cooldownUntil&&a.cooldownUntil>Date.now()?`cooldown ${Math.ceil((a.cooldownUntil-Date.now())/6e4)}m`:"ready",y=a.accountId?` \xB7 ${a.accountId.length>12?`${a.accountId.slice(0,8)}\u2026${a.accountId.slice(-4)}`:a.accountId}`:"";s.push(`${P} ${a.preferred?l.fg("success","\u25CF"):" "} ${a.label}${l.fg("dim",y)} ${l.fg("dim",L)}${a.preferred?l.fg("accent"," preferred"):""}`)}else{const L=a.managed?`${this.rows.filter(y=>y.providerId===a.providerId&&y.label).length} accounts`:a.source==="environment"||a.source==="runtime"?`${a.source}${a.sourceLabel?` \xB7 ${a.sourceLabel}`:""} (view only)`:"configured";s.push(`${P} ${l.fg("accent",a.name)}${l.fg("dim",` ${L}`)}`)}}r&&s.push(l.fg("dim",`(${this.selected+1}/${g})`)),x&&s.push(l.fg("dim",this.notice))}}return s.map(c=>D(c,e))}}export{Z as ProviderPanel,M as buildProviderCatalog,q as classifyLoginFailure};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ModelRuntime } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import type { ProviderAuthInteraction } from '@earendil-works/pi-ai';
|
|
3
|
+
export declare function openProviderLoginRuntime(): Promise<ModelRuntime>;
|
|
4
|
+
export declare function loginProvider(runtime: ModelRuntime, providerId: string, interaction: ProviderAuthInteraction, options?: {
|
|
5
|
+
label?: string;
|
|
6
|
+
beforeInstall?: () => boolean;
|
|
7
|
+
}): Promise<boolean>;
|
|
8
|
+
export declare function terminalAuthInteraction(): ProviderAuthInteraction;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
var y=Object.defineProperty;var o=(e,r)=>y(e,"name",{value:r,configurable:!0});import{execFile as b}from"node:child_process";import{createInterface as x}from"node:readline/promises";import{join as u}from"node:path";import{ModelRuntime as $}from"@earendil-works/pi-coding-agent";import{cliClient as d}from"../api-client.js";import{InputError as f}from"../../core/io.js";import{PoolCredentialStore as k}from"../../core/pool-credential-store.js";import{isManagedProvider as m,readSubscriptionPool as C}from"../../core/subscription-state.js";import{piAgentDir as P}from"../../core/runtime/package-health.js";async function U(){const e=P();return $.create({credentials:new k(u(e,"auth.json")),modelsPath:u(e,"models.json")})}o(U,"openProviderLoginRuntime");async function Y(e,r,i,t={}){const c=e.getProvider(r)?.auth.oauth;if(c===void 0)throw new f({error:"provider_login_unavailable",message:`provider ${r} has no OAuth login flow`,next:"Configure an API key through `crtr sys setup`, then run `crtr sys connect` again."});const g=m(r)?Math.max(0,...C(r).map(a=>a.lastRateLimitedAt)):void 0,n=await c.login(i);if(t.beforeInstall?.()===!1)return!1;if(m(r)){const a=typeof n.accountId=="string"&&n.accountId.trim()?n.accountId.trim():void 0;return await d().installCredential(r,{kind:"managed_account",...t.label?{label:t.label}:{auto_label:!0},...a?{account_id:a}:{},access_token:n.access,refresh_token:n.refresh,expires_at:new Date(n.expires).toISOString(),last_rate_limited_at:g}),!0}const{type:I,access:h,refresh:w,expires:_,...l}=n;return await d().installCredential(r,{kind:"oauth",access_token:h,refresh_token:w,expires_at:new Date(_).toISOString(),...Object.keys(l).length>0?{extra:l}:{}}),!0}o(Y,"loginProvider");function p(e){const r=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",i=process.platform==="win32"?["/c","start","",e]:[e];b(r,i,{timeout:5e3},()=>{})}o(p,"openBrowser");function A(e){switch(e.type){case"auth_url":process.stderr.write(`${e.instructions}
|
|
2
|
+
${e.url}
|
|
3
|
+
`),p(e.url);break;case"device_code":process.stderr.write(`${e.verificationUri}
|
|
4
|
+
Code: ${e.userCode}
|
|
5
|
+
`),p(e.verificationUri);break;case"info":case"progress":process.stderr.write(`${e.message}
|
|
6
|
+
`);break}}o(A,"notify");async function S(e){if(!process.stdin.isTTY||!process.stderr.isTTY)throw new f({error:"interactive_login_required",message:"provider login requires an interactive terminal",next:"Run `crtr sys connect` from an interactive terminal, or configure the provider credential through `crtr sys setup` first."});const r=e.type==="select"?`${e.message}
|
|
7
|
+
${e.options.map((t,s)=>`${s+1}. ${t.label}`).join(`
|
|
8
|
+
`)}
|
|
9
|
+
Select: `:`${e.message}${e.placeholder?` (${e.placeholder})`:""}: `,i=x({input:process.stdin,output:process.stderr});try{const t=await i.question(r,{signal:e.signal});if(e.type!=="select")return t;const s=e.options[Number.parseInt(t,10)-1]?.id;if(s===void 0)throw new Error("Login cancelled");return s}finally{i.close()}}o(S,"promptTerminal");function B(){return{signal:new AbortController().signal,notify:A,prompt:S}}o(B,"terminalAuthInteraction");export{Y as loginProvider,U as openProviderLoginRuntime,B as terminalAuthInteraction};
|
|
@@ -102,4 +102,8 @@ export declare function renderSetupStaticInstructions(state: SetupInitialState):
|
|
|
102
102
|
/** Refresh crouter-owned tmux bindings. Setup must never fail because tmux is unavailable. */
|
|
103
103
|
export declare function reconcileInboxFrontDoor(): Promise<void>;
|
|
104
104
|
export declare function formatSetupExecutionSummary(summary: SetupExecutionSummary): string;
|
|
105
|
+
/** The non-interactive first command reconciles only crouter-owned packages.
|
|
106
|
+
* Individual failures are reported by the CLI but never prevent later work or
|
|
107
|
+
* writing the once-per-machine marker. */
|
|
108
|
+
export declare function reconcileFirstRun(onFailure: (item: string, error: unknown) => void): Promise<string>;
|
|
105
109
|
export declare function executeSetupSubmission(submission: SetupSubmission): Promise<SetupExecutionSummary>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var T=Object.defineProperty;var s=(e,t)=>T(e,"name",{value:t,configurable:!0});import{spawnSync as g}from"node:child_process";import{homedir as m}from"node:os";import{basename as N,dirname as _,join as v,resolve as L}from"node:path";import{isDeepStrictEqual as f}from"node:util";import{BINDING_CATALOG as j,normalizeGesture as M,splitGestureExpression as K,inboxShortcut as F,resolveKeybindings as k}from"../../core/keybindings/index.js";import{builtinPiPackageDir as b}from"../../core/scope.js";import{readJsonIfExists as G}from"../../core/fs-utils.js";import{resolveBundledPiCliPath as B}from"../../core/runtime/pi-cli.js";import{installTmuxBindings as U}from"../../core/runtime/tmux-chrome.js";import{normalizeTmuxPassthrough as z}from"../../core/config.js";import{detectNerdFont as Y,nerdFontInstallCommand as q,NERD_FONT_SELECT_HINT as H}from"../../core/runtime/nerd-font.js";import{ensureRenderer as W,isRendererReady as w}from"../../core/termrender/termrender.js";import{installFromMarketplace as V}from"../pkg/plugin-manage.js";import{addMarketplace as S}from"../pkg/market-manage.js";import{findPluginByName as J,findMarketplaceByName as A}from"../../core/resolver.js";import{expandTilde as X}from"../../core/fs-utils.js";const C="crouter-official-marketplace",R="https://github.com/crouton-labs/crouter-official-marketplace.git",$="main";function E(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}s(E,"isKeybindingsObject");function h(e){return E(e)?Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Array.isArray(n)?[...n]:n])):{}}s(h,"cloneKeybindings");class Ee{static{s(this,"SetupKeybindingsDraft")}expected;baseline;current;baselineInvalidRoot;invalidRoot;resolutionCache;expectedPassthrough;baselinePassthrough;currentPassthrough;constructor(t,n=void 0){this.expected=t,this.baseline=h(t),this.current=h(t),this.baselineInvalidRoot=t!==void 0&&!E(t)?t:void 0,this.invalidRoot=this.baselineInvalidRoot,this.expectedPassthrough=n,this.baselinePassthrough=z(n),this.currentPassthrough=[...this.baselinePassthrough]}get dirty(){return!f(this.current,this.baseline)||!f(this.invalidRoot,this.baselineInvalidRoot)||!f(this.currentPassthrough,this.baselinePassthrough)}overrides(){return h(this.current)}expectedKeybindings(){return this.expected}tmuxPassthrough(){return[...this.currentPassthrough]}expectedTmuxPassthrough(){return this.expectedPassthrough}usesTmuxPassthrough(t){return this.currentPassthrough.includes(t)}toggleTmuxPassthrough(t){this.currentPassthrough=this.currentPassthrough.includes(t)?this.currentPassthrough.filter(n=>n!==t):[...this.currentPassthrough,t]}resolution(){return this.resolutionCache===void 0&&(this.resolutionCache=k(this.invalidRoot===void 0?this.current:this.invalidRoot)),this.resolutionCache}hasInvalidRoot(){return this.invalidRoot!==void 0}expression(t){return this.resolution().gestures(t).join(", ")}edit(t,n){if(this.invalidRoot!==void 0)return{ok:!1,diagnostics:[{severity:"error",code:"invalid-keybindings",offendingValue:this.invalidRoot,message:"Reset all before editing: the saved keybindings root is invalid."}]};const i=j.find(u=>u.id===t);let a;try{a=n.trim()===""?[]:K(n).map(u=>M(u.trim(),i))}catch(u){return{ok:!1,diagnostics:[{severity:"error",code:"invalid-override",bindingId:t,offendingValue:n,message:u instanceof Error?u.message:String(u)}]}}const r=[...new Set(a)],c={...this.current};delete c[t];const d=[...k(c).gestures(t)],o=f(r,d)?c:{...this.current,[t]:r},l=k(o),p=l.diagnostics.filter(u=>u.bindingId===t||u.relatedBindingIds?.includes(t));return p.length>0?{ok:!1,diagnostics:p}:(this.current=o,this.resolutionCache=void 0,{ok:!0,diagnostics:l.diagnostics})}unbind(t){return this.edit(t,"")}resetRow(t){delete this.current[t],this.resolutionCache=void 0}resetAll(){this.current={},this.currentPassthrough=[],this.invalidRoot=void 0,this.resolutionCache=void 0}discard(){this.current=h(this.baseline),this.currentPassthrough=[...this.baselinePassthrough],this.invalidRoot=this.baselineInvalidRoot,this.resolutionCache=void 0}markSaved(t,n=[]){this.expected=Object.keys(t).length===0?void 0:h(t),this.baseline=h(t),this.current=h(t),this.expectedPassthrough=n.length===0?void 0:[...n],this.baselinePassthrough=[...n],this.currentPassthrough=[...n],this.baselineInvalidRoot=void 0,this.invalidRoot=void 0,this.resolutionCache=void 0}}const Q={"official-marketplace":{name:C,shortDescription:"the official crtr plugin catalog \u2014 registered as a source so its plugins are installable by ref.",longDescription:"Shallow-clones github.com/crouton-labs/crouter-official-marketplace into the user scope and registers it, which is what makes `crtr pkg plugin install crouter-official-marketplace/<name>` resolve and what fills `crtr pkg market browse`. Registering the source installs no plugin by itself. Uncheck it on a machine that should reach no external catalog \u2014 a plugin selected below still registers the marketplace it comes from, because its ref cannot resolve otherwise."},"crtr-extensions":{name:"pi-crtr-extensions",shortDescription:"crtr companion extensions \u2014 command palette, help gating, subscription/OAuth rotation, nested context.",longDescription:"Bundles the extensions built for crtr: a crtr slash-command palette, crtr/crouter help gating, surfacing of Claude Code marketplace plugin commands, nested AGENTS.md/CLAUDE.md loading, OAuth login + rotation across multiple Claude subscriptions (provider-rotation), and system-prompt trimming. The OAuth/rotation piece is what lets crtr authenticate your Claude account."},"oauth-adapter":{name:"pi-claude-oauth-adapter",shortDescription:"Anthropic OAuth / Claude Code credential compatibility for pi.",longDescription:"Third-party adapter (minzique/dotfiles-agents) letting pi authenticate with Claude using Claude Code OAuth credentials. Installed from npm, pinned to 0.1.2."},"capture-plugin":{name:"capture",shortDescription:"crtr plugin mounting `crtr capture` \u2014 browser automation and UI measurement over CDP.",longDescription:"Installs the `capture` plugin from the official crtr marketplace. It mounts `crtr capture` as a transparent passthrough to the external Capture CLI (`npm install -g @crouton-kit/capture`) for CDP screenshots, rendered-geometry measurement, HAR, accessibility trees, and JS execution against a live page. Uncheck it on a machine where agents never drive a browser \u2014 the command then costs nothing in their root prompt."},"search-plugin":{name:"search",shortDescription:"crtr plugin mounting `crtr search` \u2014 public-web discovery, grounded answers, and content extraction.",longDescription:"Installs the `search` plugin from the official crtr marketplace. It mounts `crtr search` and calls Exa directly, resolving its own API key from EXA_API_KEY or ~/.crouter/exa.key \u2014 crouter never handles that credential. Uncheck it on a machine whose agents get web search from somewhere else, such as a Northlight home where the platform serves `crtr search` from its own plugin."}},P={"official-marketplace":{kind:"marketplace-source",source:R},"crtr-extensions":{kind:"local-path",source:b("pi-crtr-extensions")},"oauth-adapter":{kind:"npm",source:"npm:pi-claude-oauth-adapter@0.1.2"},"capture-plugin":{kind:"marketplace-plugin",source:"crouter-official-marketplace/capture"},"search-plugin":{kind:"marketplace-plugin",source:"crouter-official-marketplace/search"}},Z={"official-marketplace":[],"crtr-extensions":["pi-crtr-extensions"],"oauth-adapter":[],"capture-plugin":[],"search-plugin":[]};function D(e=b){return["official-marketplace","crtr-extensions","oauth-adapter","capture-plugin","search-plugin"].map(t=>{const n=Q[t],i=P[t].kind==="local-path"?e(n.name):P[t].source;return{id:t,name:n.name,sourceKind:P[t].kind,installSource:i,shortDescription:n.shortDescription,longDescription:n.longDescription}})}s(D,"buildSetupManifest");function De(e){switch(e.sourceKind){case"marketplace-source":return`clone + register ${e.installSource}`;case"marketplace-plugin":return`crtr pkg plugin install ${e.installSource}`;default:return`pi install ${e.installSource}`}}s(De,"packagePlanAction");function ee(e){if(!e.startsWith("npm:"))return null;const t=e.slice(4),n=t.startsWith("@")?t.indexOf("@",1):t.lastIndexOf("@");return n>0?t.slice(0,n):t}s(ee,"npmPackageName");function O(e,t,n){const i=ee(e);if(i!==null)return`npm:${i}`;const a=X(e,n);return L(t,a)}s(O,"normalizePackageIdentity");function x(e){return`package:${e}`}s(x,"packageNameIdentity");function te(e){return e.startsWith("npm:")?[]:[x(N(e))]}s(te,"localPackageNameIdentities");function ne(e,t,n){const i=[O(e.installSource,t,n)];if(e.sourceKind==="local-path"){i.push(x(e.name));for(const a of Z[e.id])i.push(x(a))}return i}s(ne,"manifestIdentities");function re(e,t=D(),n=v(m(),".pi","agent","settings.json"),i=m()){const a=_(n),r=new Set,c=new Map;for(const o of t)for(const l of ne(o,a,i))c.set(l,o.id);const d=e&&typeof e=="object"?e.packages:void 0;if(!Array.isArray(d))return r;for(const o of d){const l=typeof o=="string"?o:o&&typeof o=="object"&&typeof o.source=="string"?o.source:null;if(l===null)continue;const p=[O(l,a,i),...te(l)];for(const u of p){const I=c.get(u);I!==void 0&&r.add(I)}}return r}s(re,"detectInstalledSetupPackageIds");function y(e){return g("sh",["-lc",`command -v ${e} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}s(y,"detectCommandOnPath");function ie(e=process.platform,t=y){switch(e){case"darwin":return t("brew")?"brew":"manual";case"linux":return t("apt-get")?"apt-get":t("dnf")?"dnf":t("pacman")?"pacman":"manual";default:return"manual"}}s(ie,"detectPackageManager");function se(e,t,n=y){if(e==="nerd-font"?Y().installed:n(e))return{tool:e,installed:!0,command:null,hint:"already installed",autoInstall:!1};if(e==="nerd-font"){const r=q(t);return{tool:e,installed:!1,command:r.command,hint:`${r.hint} \u2014 ${H}`,autoInstall:r.autoInstall}}const a=e==="nvim"?"neovim":"tmux";switch(t){case"brew":return{tool:e,installed:!1,command:`brew install ${a}`,hint:"Homebrew install",autoInstall:!0};case"apt-get":return{tool:e,installed:!1,command:`sudo apt-get update && sudo apt-get install -y ${a}`,hint:"apt-get install",autoInstall:!0};case"dnf":return{tool:e,installed:!1,command:`sudo dnf install -y ${a}`,hint:"dnf install",autoInstall:!0};case"pacman":return{tool:e,installed:!1,command:`sudo pacman -S --noconfirm ${a}`,hint:"pacman install",autoInstall:!0};default:return{tool:e,installed:!1,command:null,hint:`manual install required for ${e}`,autoInstall:!1}}}s(se,"buildSystemInstallPlan");function ae(e=m()){return v(e,".pi","agent","settings.json")}s(ae,"resolveSettingsPath");function Oe(e={}){const t=e.homeDir??m(),n=e.settingsPath??ae(t),i=e.manifest??D(b),a=G(n),r=re(a,i,n,t);for(const l of de(i))r.add(l);const c=e.commandExists??y,d=e.packageManager??ie(process.platform,c),o=["tmux","nvim","nerd-font"].map(l=>se(l,d,c));return{manifest:i,installedPackageIds:r,systemPlans:o}}s(Oe,"buildSetupInitialState");function Te(e){const t=e.manifest.map(i=>{const a=e.installedPackageIds.has(i.id)?"installed":"not installed";return`- ${i.name} \u2014 ${i.shortDescription} (${a})`}),n=e.systemPlans.map(i=>i.installed?`- ${i.tool}: \u2713 installed`:i.command?`- ${i.tool}: ${i.command}`:`- ${i.tool}: ${i.hint}`);return["crtr sys setup (non-interactive)","","Provider login: open the wizard in a TTY and use the Providers tab (same flow as /login) to authenticate a model provider.","","Packages:",...t,"","System deps:",...n,"",`Inbox: the inbox-toggle key (${F()??"disabled"}) is a crouter keybinding \u2014 view or rebind it in \`crtr sys settings\`; it is installed on the same tmux server as every other crouter binding.`,"Run `crtr sys setup` in a TTY to open the wizard."].join(`
|
|
1
|
+
var M=Object.defineProperty;var a=(e,t)=>M(e,"name",{value:t,configurable:!0});import{spawnSync as k}from"node:child_process";import{homedir as m}from"node:os";import{basename as j,dirname as F,join as w,resolve as G}from"node:path";import{isDeepStrictEqual as f}from"node:util";import{BINDING_CATALOG as B,normalizeGesture as U,splitGestureExpression as z,inboxShortcut as Y,resolveKeybindings as b}from"../../core/keybindings/index.js";import{builtinPiPackageDir as g}from"../../core/scope.js";import{readJsonIfExists as q,readJsonOrNull as H}from"../../core/fs-utils.js";import{resolveBundledPiCliPath as V}from"../../core/runtime/pi-cli.js";import{crtrVersion as W}from"../../shared/crtr-version.js";import{installTmuxBindings as J}from"../../core/runtime/tmux-chrome.js";import{normalizeTmuxPassthrough as X}from"../../core/config.js";import{detectNerdFont as Q,nerdFontInstallCommand as Z,NERD_FONT_SELECT_HINT as ee}from"../../core/runtime/nerd-font.js";import{ensureRenderer as te,isRendererReady as S}from"../../core/termrender/termrender.js";import{installFromMarketplace as ne}from"../pkg/plugin-manage.js";import{addMarketplace as A}from"../pkg/market-manage.js";import{findPluginByName as re,findMarketplaceByName as C}from"../../core/resolver.js";import{expandTilde as ie}from"../../core/fs-utils.js";const R="crouter-official-marketplace",E="https://github.com/crouton-labs/crouter-official-marketplace.git",$="main";function D(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}a(D,"isKeybindingsObject");function d(e){return D(e)?Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Array.isArray(n)?[...n]:n])):{}}a(d,"cloneKeybindings");class Ne{static{a(this,"SetupKeybindingsDraft")}expected;baseline;current;baselineInvalidRoot;invalidRoot;resolutionCache;expectedPassthrough;baselinePassthrough;currentPassthrough;constructor(t,n=void 0){this.expected=t,this.baseline=d(t),this.current=d(t),this.baselineInvalidRoot=t!==void 0&&!D(t)?t:void 0,this.invalidRoot=this.baselineInvalidRoot,this.expectedPassthrough=n,this.baselinePassthrough=X(n),this.currentPassthrough=[...this.baselinePassthrough]}get dirty(){return!f(this.current,this.baseline)||!f(this.invalidRoot,this.baselineInvalidRoot)||!f(this.currentPassthrough,this.baselinePassthrough)}overrides(){return d(this.current)}expectedKeybindings(){return this.expected}tmuxPassthrough(){return[...this.currentPassthrough]}expectedTmuxPassthrough(){return this.expectedPassthrough}usesTmuxPassthrough(t){return this.currentPassthrough.includes(t)}toggleTmuxPassthrough(t){this.currentPassthrough=this.currentPassthrough.includes(t)?this.currentPassthrough.filter(n=>n!==t):[...this.currentPassthrough,t]}resolution(){return this.resolutionCache===void 0&&(this.resolutionCache=b(this.invalidRoot===void 0?this.current:this.invalidRoot)),this.resolutionCache}hasInvalidRoot(){return this.invalidRoot!==void 0}expression(t){return this.resolution().gestures(t).join(", ")}edit(t,n){if(this.invalidRoot!==void 0)return{ok:!1,diagnostics:[{severity:"error",code:"invalid-keybindings",offendingValue:this.invalidRoot,message:"Reset all before editing: the saved keybindings root is invalid."}]};const r=B.find(u=>u.id===t);let i;try{i=n.trim()===""?[]:z(n).map(u=>U(u.trim(),r))}catch(u){return{ok:!1,diagnostics:[{severity:"error",code:"invalid-override",bindingId:t,offendingValue:n,message:u instanceof Error?u.message:String(u)}]}}const s=[...new Set(i)],o={...this.current};delete o[t];const p=[...b(o).gestures(t)],c=f(s,p)?o:{...this.current,[t]:s},l=b(c),h=l.diagnostics.filter(u=>u.bindingId===t||u.relatedBindingIds?.includes(t));return h.length>0?{ok:!1,diagnostics:h}:(this.current=c,this.resolutionCache=void 0,{ok:!0,diagnostics:l.diagnostics})}unbind(t){return this.edit(t,"")}resetRow(t){delete this.current[t],this.resolutionCache=void 0}resetAll(){this.current={},this.currentPassthrough=[],this.invalidRoot=void 0,this.resolutionCache=void 0}discard(){this.current=d(this.baseline),this.currentPassthrough=[...this.baselinePassthrough],this.invalidRoot=this.baselineInvalidRoot,this.resolutionCache=void 0}markSaved(t,n=[]){this.expected=Object.keys(t).length===0?void 0:d(t),this.baseline=d(t),this.current=d(t),this.expectedPassthrough=n.length===0?void 0:[...n],this.baselinePassthrough=[...n],this.currentPassthrough=[...n],this.baselineInvalidRoot=void 0,this.invalidRoot=void 0,this.resolutionCache=void 0}}const se={"official-marketplace":{name:R,shortDescription:"the official crtr plugin catalog \u2014 registered as a source so its plugins are installable by ref.",longDescription:"Shallow-clones github.com/crouton-labs/crouter-official-marketplace into the user scope and registers it, which is what makes `crtr pkg plugin install crouter-official-marketplace/<name>` resolve and what fills `crtr pkg market browse`. Registering the source installs no plugin by itself. Uncheck it on a machine that should reach no external catalog \u2014 a plugin selected below still registers the marketplace it comes from, because its ref cannot resolve otherwise."},"crtr-extensions":{name:"pi-crtr-extensions",shortDescription:"crtr companion extensions \u2014 command palette, help gating, subscription/OAuth rotation, nested context.",longDescription:"Bundles the extensions built for crtr: a crtr slash-command palette, crtr/crouter help gating, surfacing of Claude Code marketplace plugin commands, nested AGENTS.md/CLAUDE.md loading, OAuth login + rotation across multiple Claude subscriptions (provider-rotation), and system-prompt trimming. The OAuth/rotation piece is what lets crtr authenticate your Claude account."},"oauth-adapter":{name:"pi-claude-oauth-adapter",shortDescription:"Anthropic OAuth / Claude Code credential compatibility for pi.",longDescription:"Third-party adapter (minzique/dotfiles-agents) letting pi authenticate with Claude using Claude Code OAuth credentials. Installed from npm, pinned to 0.1.2."},"capture-plugin":{name:"capture",shortDescription:"crtr plugin mounting `crtr capture` \u2014 browser automation and UI measurement over CDP.",longDescription:"Installs the `capture` plugin from the official crtr marketplace. It mounts `crtr capture` as a transparent passthrough to the external Capture CLI (`npm install -g @crouton-kit/capture`) for CDP screenshots, rendered-geometry measurement, HAR, accessibility trees, and JS execution against a live page. Uncheck it on a machine where agents never drive a browser \u2014 the command then costs nothing in their root prompt."},"search-plugin":{name:"search",shortDescription:"crtr plugin mounting `crtr search` \u2014 public-web discovery, grounded answers, and content extraction.",longDescription:"Installs the `search` plugin from the official crtr marketplace. It mounts `crtr search` and calls Exa directly, resolving its own API key from EXA_API_KEY or ~/.crouter/exa.key \u2014 crouter never handles that credential. Uncheck it on a machine whose agents get web search from somewhere else, such as a Northlight home where the platform serves `crtr search` from its own plugin."}},P={"official-marketplace":{kind:"marketplace-source",source:E},"crtr-extensions":{kind:"local-path",source:g("pi-crtr-extensions")},"oauth-adapter":{kind:"npm",source:"npm:pi-claude-oauth-adapter@0.1.2"},"capture-plugin":{kind:"marketplace-plugin",source:"crouter-official-marketplace/capture"},"search-plugin":{kind:"marketplace-plugin",source:"crouter-official-marketplace/search"}},ae={"official-marketplace":[],"crtr-extensions":["pi-crtr-extensions"],"oauth-adapter":[],"capture-plugin":[],"search-plugin":[]};function x(e=g){return["official-marketplace","crtr-extensions","oauth-adapter","capture-plugin","search-plugin"].map(t=>{const n=se[t],r=P[t].kind==="local-path"?e(n.name):P[t].source;return{id:t,name:n.name,sourceKind:P[t].kind,installSource:r,shortDescription:n.shortDescription,longDescription:n.longDescription}})}a(x,"buildSetupManifest");function _e(e){switch(e.sourceKind){case"marketplace-source":return`clone + register ${e.installSource}`;case"marketplace-plugin":return`crtr pkg plugin install ${e.installSource}`;default:return`pi install ${e.installSource}`}}a(_e,"packagePlanAction");function oe(e){if(!e.startsWith("npm:"))return null;const t=e.slice(4),n=t.startsWith("@")?t.indexOf("@",1):t.lastIndexOf("@");return n>0?t.slice(0,n):t}a(oe,"npmPackageName");function O(e,t,n){const r=oe(e);if(r!==null)return`npm:${r}`;const i=ie(e,n);return G(t,i)}a(O,"normalizePackageIdentity");function v(e){return`package:${e}`}a(v,"packageNameIdentity");function ce(e){return e.startsWith("npm:")?[]:[v(j(e))]}a(ce,"localPackageNameIdentities");function le(e,t,n){const r=[O(e.installSource,t,n)];if(e.sourceKind==="local-path"){r.push(v(e.name));for(const i of ae[e.id])r.push(v(i))}return r}a(le,"manifestIdentities");function T(e,t=x(),n=w(m(),".pi","agent","settings.json"),r=m()){const i=F(n),s=new Set,o=new Map;for(const c of t)for(const l of le(c,i,r))o.set(l,c.id);const p=e&&typeof e=="object"?e.packages:void 0;if(!Array.isArray(p))return s;for(const c of p){const l=typeof c=="string"?c:c&&typeof c=="object"&&typeof c.source=="string"?c.source:null;if(l===null)continue;const h=[O(l,i,r),...ce(l)];for(const u of h){const y=o.get(u);y!==void 0&&s.add(y)}}return s}a(T,"detectInstalledSetupPackageIds");function I(e){return k("sh",["-lc",`command -v ${e} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}a(I,"detectCommandOnPath");function ue(e=process.platform,t=I){switch(e){case"darwin":return t("brew")?"brew":"manual";case"linux":return t("apt-get")?"apt-get":t("dnf")?"dnf":t("pacman")?"pacman":"manual";default:return"manual"}}a(ue,"detectPackageManager");function de(e,t,n=I){if(e==="nerd-font"?Q().installed:n(e))return{tool:e,installed:!0,command:null,hint:"already installed",autoInstall:!1};if(e==="nerd-font"){const s=Z(t);return{tool:e,installed:!1,command:s.command,hint:`${s.hint} \u2014 ${ee}`,autoInstall:s.autoInstall}}const i=e==="nvim"?"neovim":"tmux";switch(t){case"brew":return{tool:e,installed:!1,command:`brew install ${i}`,hint:"Homebrew install",autoInstall:!0};case"apt-get":return{tool:e,installed:!1,command:`sudo apt-get update && sudo apt-get install -y ${i}`,hint:"apt-get install",autoInstall:!0};case"dnf":return{tool:e,installed:!1,command:`sudo dnf install -y ${i}`,hint:"dnf install",autoInstall:!0};case"pacman":return{tool:e,installed:!1,command:`sudo pacman -S --noconfirm ${i}`,hint:"pacman install",autoInstall:!0};default:return{tool:e,installed:!1,command:null,hint:`manual install required for ${e}`,autoInstall:!1}}}a(de,"buildSystemInstallPlan");function N(e=m()){return w(e,".pi","agent","settings.json")}a(N,"resolveSettingsPath");function Ke(e={}){const t=e.homeDir??m(),n=e.settingsPath??N(t),r=e.manifest??x(g),i=q(n),s=T(i,r,n,t);for(const l of K(r))s.add(l);const o=e.commandExists??I,p=e.packageManager??ue(process.platform,o),c=["tmux","nvim","nerd-font"].map(l=>de(l,p,o));return{manifest:r,installedPackageIds:s,systemPlans:c}}a(Ke,"buildSetupInitialState");function Le(e){const t=e.manifest.map(r=>{const i=e.installedPackageIds.has(r.id)?"installed":"not installed";return`- ${r.name} \u2014 ${r.shortDescription} (${i})`}),n=e.systemPlans.map(r=>r.installed?`- ${r.tool}: \u2713 installed`:r.command?`- ${r.tool}: ${r.command}`:`- ${r.tool}: ${r.hint}`);return["crtr sys setup (non-interactive)","","Provider login: open the wizard in a TTY and use the Providers tab (same flow as /login) to authenticate a model provider.","","Packages:",...t,"","System deps:",...n,"",`Inbox: the inbox-toggle key (${Y()??"disabled"}) is a crouter keybinding \u2014 view or rebind it in \`crtr sys settings\`; it is installed on the same tmux server as every other crouter binding.`,"Run `crtr sys setup` in a TTY to open the wizard."].join(`
|
|
2
2
|
`)+`
|
|
3
|
-
`}
|
|
4
|
-
`),W(),w()?"provisioned":"unavailable")}s(le,"ensureManagedRenderer");async function ue(){try{U()}catch{}}s(ue,"reconcileInboxFrontDoor");function Ne(e){return["crtr sys setup complete",e.installed.length>0?`installed: ${e.installed.join(", ")}`:"installed: none",e.skipped.length>0?`skipped: ${e.skipped.join(", ")}`:"skipped: none"].join(`
|
|
3
|
+
`}a(Le,"renderSetupStaticInstructions");function pe(e){if(k("sh",["-lc",e],{stdio:"inherit"}).status!==0)throw new Error(`command failed: ${e}`)}a(pe,"runShellCommand");function he(e,t=!1,n=V()){if(k(process.execPath,[n,"install",e],{stdio:t?"pipe":"inherit"}).status!==0)throw new Error(`pi install failed: ${e}`)}a(he,"installPiPackage");function _(e,t){return S()?"current":(e?.(),te(t),S()?"provisioned":"unavailable")}a(_,"ensureManagedRenderer");async function me(){try{J()}catch{}}a(me,"reconcileInboxFrontDoor");function Me(e){return["crtr sys setup complete",e.installed.length>0?`installed: ${e.installed.join(", ")}`:"installed: none",e.skipped.length>0?`skipped: ${e.skipped.join(", ")}`:"skipped: none"].join(`
|
|
5
4
|
`)+`
|
|
6
|
-
`}
|
|
5
|
+
`}a(Me,"formatSetupExecutionSummary");function K(e,t){const n=[];for(const r of e)try{r.sourceKind==="marketplace-source"?C(r.name,t)!==null&&n.push(r.id):r.sourceKind==="marketplace-plugin"&&re(r.name,t)!==null&&n.push(r.id)}catch{}return n}a(K,"detectInstalledMarketplaceEntries");function fe(e,t="user"){if(C(e,t)!==null)return null;if(e!==R)throw new Error(`marketplace not registered: ${e}`);return A({url:E,ref:$,scope:t}),e}a(fe,"ensureMarketplaceRegistered");async function L(e,t=!1){if(e.sourceKind==="marketplace-source")return A({url:e.installSource,ref:$,scope:"user"}),[e.name];if(e.sourceKind==="marketplace-plugin"){const[n,r]=e.installSource.split("/"),i=fe(n);return await ne({marketplace:n,plugin:r},"user"),i===null?[e.name]:[i,e.name]}return he(e.installSource,t),[e.name]}a(L,"installSetupPackage");async function je(e){const t=x(g),n=T(H(N()),t);for(const i of K(t,"user"))n.add(i);const r=new Map(t.map(i=>[i.id,i]));for(const i of["crtr-extensions","official-marketplace","capture-plugin","search-plugin"]){const s=r.get(i);if(!n.has(i))try{await L(s,!0)}catch(o){e(s.name,o)}}try{_(void 0,()=>{})==="unavailable"&&e("termrender renderer",new Error("unavailable \u2014 install uv"))}catch(i){e("termrender renderer",i)}return W()}a(je,"reconcileFirstRun");async function Fe(e){const t=[],n=[],r=[],i=[];for(const s of e.packages){if(e.installedPackageIds.has(s.id)){n.push(`${s.name} (already installed)`);continue}try{t.push(...await L(s))}catch(o){if(s.sourceKind!=="marketplace-source"&&s.sourceKind!=="marketplace-plugin")throw o;n.push(`${s.name} (${o instanceof Error?o.message:String(o)})`)}}for(const s of e.systemDeps){if(s.installed){i.push(`${s.tool} (already installed)`);continue}if(!s.autoInstall||!s.command){i.push(`${s.tool} (${s.hint})`);continue}pe(s.command),r.push(s.tool)}switch(_(()=>process.stdout.write(`Provisioning the pinned termrender renderer\u2026
|
|
6
|
+
`))){case"provisioned":t.push("termrender renderer");break;case"current":n.push("termrender renderer (current)");break;case"unavailable":n.push("termrender renderer (unavailable \u2014 install uv)");break}return await me(),{installed:[...t,...r],skipped:[...n,...i]}}a(Fe,"executeSetupSubmission");export{R as OFFICIAL_MARKETPLACE_NAME,$ as OFFICIAL_MARKETPLACE_REF,E as OFFICIAL_MARKETPLACE_URL,Ne as SetupKeybindingsDraft,Ke as buildSetupInitialState,x as buildSetupManifest,de as buildSystemInstallPlan,I as detectCommandOnPath,T as detectInstalledSetupPackageIds,ue as detectPackageManager,Fe as executeSetupSubmission,Me as formatSetupExecutionSummary,O as normalizePackageIdentity,_e as packagePlanAction,je as reconcileFirstRun,me as reconcileInboxFrontDoor,Le as renderSetupStaticInstructions,N as resolveSettingsPath};
|
package/dist/commands/sys.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var s=Object.defineProperty;var r=(o,m)=>s(o,"name",{value:m,configurable:!0});import{defineSysBranch as t}from"./sys/branch.js";import{configBranch as e}from"./sys/config.js";import{daemonBranch as f}from"./sys/daemon.js";import{sysDoctorLeaf as i}from"./sys/doctor.js";import{sysFeedbackLeaf as a}from"./sys/feedback.js";import{sysLogsLeaf as p}from"./sys/logs.js";import{sysWorktreesLeaf as n}from"./sys/worktrees.js";import{sysSettingsLeaf as y}from"./sys/settings.js";import{
|
|
1
|
+
var s=Object.defineProperty;var r=(o,m)=>s(o,"name",{value:m,configurable:!0});import{defineSysBranch as t}from"./sys/branch.js";import{configBranch as e}from"./sys/config.js";import{daemonBranch as f}from"./sys/daemon.js";import{sysDoctorLeaf as i}from"./sys/doctor.js";import{sysFeedbackLeaf as a}from"./sys/feedback.js";import{sysLogsLeaf as p}from"./sys/logs.js";import{sysWorktreesLeaf as n}from"./sys/worktrees.js";import{sysSettingsLeaf as y}from"./sys/settings.js";import{sysConnectLeaf as c}from"./sys/connect.js";import{sysSetupLeaf as L}from"./sys/setup.js";import{supportBranch as h}from"./sys/support.js";import{contextBranch as B}from"./sys/context.js";import{sysSyncBranch as g}from"./sys/sync.js";import{sysMigrateLeaf as S}from"./sys/migrate.js";import{sysUpdateLeaf as d,sysVersionLeaf as u}from"./sys/update.js";function A(){return t([y,e,c,L,i,B,a,p,n,h,d,u,S,g,f])}r(A,"registerSys");export{A as registerSys};
|
|
@@ -26,10 +26,10 @@ export declare function ensureReviewDir(reviewId: string): void;
|
|
|
26
26
|
* `canvasDbPath()` under the canvas home (`~/.crouter/canvas/crtrd.sock` unless
|
|
27
27
|
* `CRTR_HOME` is set). Pure path builder — no db reach — so both the daemon
|
|
28
28
|
* (which binds it) and a CLI-side resolver can import it freely. `src/api`'s
|
|
29
|
-
* `
|
|
30
|
-
* Node-builtin `join(crtrHome-equivalent, 'crtrd.sock')`
|
|
31
|
-
* here, to keep the exported `/api` contract
|
|
32
|
-
* agree. */
|
|
29
|
+
* `defaultSocketPath()` (in `api/node-transport.ts`) deliberately re-derives
|
|
30
|
+
* this same path from a Node-builtin `join(crtrHome-equivalent, 'crtrd.sock')`
|
|
31
|
+
* rather than importing here, to keep the exported `/api` contract
|
|
32
|
+
* dependency-light; the two must agree. */
|
|
33
33
|
export declare function apiSocketPath(): string;
|
|
34
34
|
/** Global, cwd-agnostic broker cache of per-session picker metadata, keyed by
|
|
35
35
|
* absolute `.jsonl` path. Shared across every node's broker (paths are absolute)
|
package/dist/core/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var Te=Object.defineProperty;var s=(e,n)=>Te(e,"name",{value:n,configurable:!0});import{envProfileId as $e}from"../shared/env.js";import{basename as Ce,dirname as Me,join as H}from"node:path";import{isDeepStrictEqual as Le}from"node:util";import{CONFIG_FILE as Ne,STATE_FILE as Fe,CONDENSED_HISTORY_MODES as ze,MOUSE_MODE_DEFAULTS as De,WHIP_MESSAGE_MODES as Ue,DEFAULT_WHIP_MESSAGES as J,defaultScopeConfig as b,defaultScopeState as Ke,defaultModelLaddersConfig as qe,defaultKindsConfig as Ge,defaultRemoteCanvasConfig as Be}from"../types.js";import{BINDING_CATALOG as He,BINDING_IDS as Je,isAttachPaneBinding as We}from"./keybindings/catalog.js";import{notFound as Ze,usage as Ye}from"./errors.js";import{emitEvent as Ve}from"./events/emit.js";import{atomicWriteJson as W,readJsonIfExists as M,ensureDir as Z}from"./fs-utils.js";import{scopeRoot as y,requireScopeRoot as _,findProjectScopeRoots as Qe,userScopeRoot as L}from"./scope.js";import{listInstalledPluginsInRoot as Y}from"./installed-plugins.js";import{profileRoot as N,listProfiles as V}from"./profiles/manifest.js";import{canonicalRoot as F,projectStore as Xe,userStore as Q}from"./scoped-state/paths.js";import{convertLegacyScopedStore as en,initializeFreshScopedStore as nn}from"./scoped-state/migrate.js";import{mutateRawSettings as on,readBookkeeping as tn,readRawSettings as rn,readRawSettingsWithRevision as sn,replaceRawSettings as dn,writeBookkeeping as cn,writeRawSettings as un}from"./scoped-state/settings.js";import{withExclusiveDirectoryLock as ln}from"./exclusive-lock.js";import{SCOPE_CONFIG_KEYS as fn}from"./user-settings.js";import{normalizeWorkingGerunds as an}from"../shared/working-activity.js";import{mergePageComponentLayers as mn,normalizePageComponents as X}from"./human/page-catalog.js";function h(e){return H(e,Ne)}s(h,"configPathFor");function ee(e,n){return Z(Me(e)),ln(`${e}.lock`,()=>n(e),{timeoutError:s(()=>new Error(`timed out waiting for config lock: ${e}`),"timeoutError")})}s(ee,"withConfigPathLock");function pn(e){return H(e,Fe)}s(pn,"statePathFor");function co(e){const n=y(e);return n?h(n):null}s(co,"configPath");function uo(e){const n=y(e);return n?pn(n):null}s(uo,"statePath");function g(e){const n=F(e),t=F(L());if(n===t)return{store:Q(t),key:"user",kind:"user",root:t};if(Ce(n)===".crouter")return en({kind:"project",root:n}),{store:Xe(n),key:"project",kind:"project",root:n};for(const o of V()){const r=F(N(o.profileId));if(n===r)return{store:Q(t),key:`profile:${o.profileId}`,kind:"profile",root:r,profileId:o.profileId}}throw new Error(`settings root is not a recognized owner: ${e}`)}s(g,"settingsOwnerForRoot");function j(e){const n=g(e);return n.kind==="project"?M(h(n.root)):rn(n)}s(j,"readRawAtRoot");function z(e,n){const t=g(e);if(t.kind==="project"){ee(h(t.root),o=>W(o,n));return}un(t,n)}s(z,"writeRawAtRoot");function v(e,n){const t=g(e);return t.kind==="project"?ee(h(t.root),o=>{const r=n(M(o)??{});return W(o,r.next),r.value}):on(t,o=>n(o))}s(v,"mutateRawAtRoot");function gn(e){const n=y(e);return n?yn(n):b()}s(gn,"readConfig");function yn(e){const n=j(e);return n?T(n):b()}s(yn,"readConfigAtRoot");function hn(e){const n=y(e);return n?tn(g(n)):Ke()}s(hn,"readState");class bn extends Error{static{s(this,"InvalidRawSettingsError")}constructor(n){super(`settings cannot be merged: ${n instanceof Error?n.message:String(n)}`),this.name="InvalidRawSettingsError"}}function ne(e){if(e.scope==="user")return g(L());if(!V().some(n=>n.profileId===e.profileId))throw Ze(`profile not found: ${e.profileId}`,{received:e.profileId,next:"Run `crtr profile list` to see available profiles."});return g(N(e.profileId))}s(ne,"rawSettingsOwnerForTarget");function lo(e){return sn(ne(e))}s(lo,"readRawSettingsForTarget");function fo(e,n,t){return dn(ne(e),n,t,o=>{try{T(o)}catch(r){throw new bn(r)}})}s(fo,"replaceRawSettingsForTarget");function ao(e,n){const t=_(e);return v(t,o=>{const r=n(o);return{value:r,next:r}})}s(ao,"updateRawConfigAtomically");function mo(e,n){z(_(e),n)}s(mo,"writeConfig");function po(e,n){z(_(e),n)}s(po,"writeRawConfig");function _n(e,n){const t=_(e);cn(g(t),n)}s(_n,"writeState");function go(e,n){Z(n);const t=g(e==="user"?L():n);if(nn({kind:t.kind==="user"?"user":"project",root:t.store.root}),t.kind==="project"&&M(h(t.root))===null){const{modelLadders:o,keybindings:r,tmux_passthrough:i,brokerThresholds:d,lifecycle:c,...u}=b();z(t.root,u)}}s(go,"ensureScopeInitialized");function vn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[t,o]of Object.entries(e))Array.isArray(o)&&o.every(r=>typeof r=="string")&&(n[t]=[...o]);return n}s(vn,"normalizeKeybindings");const kn=new Set(He.filter(We).map(e=>e.id));function wn(e){return Array.isArray(e)?[...new Set(e.filter(n=>typeof n=="string"&&Je.has(n)&&kn.has(n)))]:[]}s(wn,"normalizeTmuxPassthrough");function S(e,n=qe()){const t=n;if(e===null||typeof e!="object")return t;const o=e,r={anthropic:{...t.anthropic},openai:{...t.openai},...t.defaultProvider!==void 0?{defaultProvider:t.defaultProvider}:{}};(o.defaultProvider==="anthropic"||o.defaultProvider==="openai")&&(r.defaultProvider=o.defaultProvider);for(const i of["anthropic","openai"]){const d=o[i];if(!(d===null||typeof d!="object"))for(const[c,u]of Object.entries(d))(c==="ultra"||c==="strong"||c==="medium"||c==="light")&&typeof u=="string"&&(r[i][c]=u)}return r}s(S,"mergeModelLadders");function oe(e){return e==="ultra"||e==="strong"||e==="medium"||e==="light"}s(oe,"validStrength");const D=["ultra","strong","medium","light"];function U(){return Object.create(null)}s(U,"routeRecord");function k(e,n,t){Object.defineProperty(e,n,{value:t,writable:!0,enumerable:!0,configurable:!0})}s(k,"setRouteRecord");function te(e){const n=U();for(const[t,o]of Object.entries(e??{}))k(n,t,o);return n}s(te,"copyRouteRecord");function w(e,n){Ve({level:"warn",event:"config.model_routes.invalid",fields:{route_id:e,rule:n}})}s(w,"routeInvalid");function x(e,n={}){const t=te(n);if(e===null||typeof e!="object"||Array.isArray(e))return t;for(const[o,r]of Object.entries(e)){if(o.trim()===""){w(o,"route id must be a non-empty string");continue}if(r===null||typeof r!="object"||Array.isArray(r)){w(o,"route must be an object");continue}const i=r,d=t[o],c={...d};let u;for(const f of["family","credentialSource","providerId"]){const l=i[f];if(l!==void 0){if(typeof l!="string"||l.trim()===""){u=`${f} must be a non-empty string`;break}c[f]=l}}if(u===void 0&&i.models!==void 0)if(i.models===null||typeof i.models!="object"||Array.isArray(i.models))u="models must be an object";else{const f={...d?.models};for(const[l,a]of Object.entries(i.models)){if(!oe(l)){u=`models.${l} is not a model strength`;break}if(typeof a!="string"||a.trim()===""){u=`models.${l} must be a non-empty provider/id ref`;break}const m=a.indexOf("/");if(m<=0||m===a.length-1){u=`models.${l} must be provider/id`;break}f[l]=a}c.models=f}if(u===void 0&&c.models!==void 0&&c.providerId!==void 0){for(const[f,l]of Object.entries(c.models))if(l.slice(0,l.indexOf("/"))!==c.providerId){u=`models.${f} provider prefix must equal providerId`;break}}if(u!==void 0){w(o,u);continue}k(t,o,c)}return t}s(x,"mergeModelRoutePatches");function re(e){const n=U(),t=U();for(const[o,r]of Object.entries(e)){if(r.family===void 0||r.credentialSource===void 0||r.providerId===void 0||r.models===void 0){w(o,"route must include family, credentialSource, providerId, and models");continue}const i=D.filter(d=>r.models[d]===void 0);if(i.length>0){w(o,`incomplete route; missing strengths: ${i.join(", ")}`),k(t,o,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models});continue}k(n,o,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models})}return{complete:n,legacy:t}}s(re,"resolveModelRoutes");function An(e,n){if(e.trim()==="")throw new Error("model route id must be a non-empty string");for(const o of["family","credentialSource","providerId"])if(typeof n[o]!="string"||n[o].trim()==="")throw new Error(`model route "${e}" ${o} must be a non-empty string`);const t=D.filter(o=>n.models?.[o]===void 0);if(t.length>0)throw new Error(`model route "${e}" is incomplete; missing strengths: ${t.join(", ")}`);for(const o of D){const r=n.models[o];if(typeof r!="string"||r.trim()==="")throw new Error(`model route "${e}" models.${o} 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.${o} must be provider/id`);if(r.slice(0,i)!==n.providerId)throw new Error(`model route "${e}" models.${o} provider prefix must equal providerId`)}}s(An,"assertCompleteModelRoute");function O(e,n){if(e===void 0||e===null||typeof e!="object"||Array.isArray(e))return n;const t=e,o=s(i=>Array.isArray(i)&&i.every(d=>typeof d=="string"&&d.trim()!=="")?[...i]:void 0,"strings"),r=Array.isArray(t.strengthFallback)&&t.strengthFallback.every(i=>oe(i))?[...t.strengthFallback]:void 0;return{...o(t.credentialSourceOrder)!==void 0?{credentialSourceOrder:o(t.credentialSourceOrder)}:{},...o(t.familyOrder)!==void 0?{familyOrder:o(t.familyOrder)}:{},...r!==void 0?{strengthFallback:r}:{}}}s(O,"mergeModelRouting");function Rn(e){return e!==""&&e.replace(/\*/g,"")===""}s(Rn,"isInheritAllGlob");function E(e,n=[]){const t=e,o=Array.isArray(t?.allow)?t.allow.filter(r=>typeof r=="string"):[];for(const r of o)if(Rn(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,...o])]}}s(E,"mergeSpawnEnv");function I(e,n={}){const t={};for(const[o,r]of Object.entries(n))t[o]={...r};if(e!==null&&typeof e=="object")for(const[o,r]of Object.entries(e)){if(r===null||typeof r!="object")continue;const i={...t[o]},d=r.fastMode;typeof d=="boolean"&&(i.fastMode=d),t[o]=i}return t}s(I,"mergeProviderOptions");const ie=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,jn=new Set(["crtr","crtrd"]);function P(e,n){if(n==="bin"&&jn.has(e))return`${n}.${e} is reserved by crouter \u2014 pick another bare command name`;if(!ie.test(e))return`${n}.${e} is not a safe bare command name (expected ${ie.source})`}s(P,"bareCommandNameIssue");function Sn(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[t,o]of Object.entries(e)){const r=P(t,"bin");r!==void 0&&n.push({name:t,message:r}),(typeof o!="string"||o.trim()==="")&&n.push({name:t,message:`bin.${t} must be a non-empty relative path to an executable inside the contributor root`})}return n}s(Sn,"binDeclarationIssues");function xn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[t,o]of Object.entries(e))P(t,"bin")===void 0&&(typeof o!="string"||o.trim()===""||(n[t]=o));return n}s(xn,"normalizeBin");const se=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function On(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[t,o]of Object.entries(e)){const r=[];if(se.test(t)||r.push(`action name must match ${se.source}`),o===null||typeof o!="object"||Array.isArray(o))r.push("action must be an object with argv and cwd");else{const i=o,d=Object.keys(i).filter(c=>c!=="argv"&&c!=="cwd");d.length>0&&r.push(`action has unsupported field${d.length===1?"":"s"}: ${d.join(", ")}`),(!Array.isArray(i.argv)||i.argv.length===0||!i.argv.every(c=>typeof c=="string"&&c.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:t,reason:r.join("; ")})}return n}s(On,"humanActionDeclarationIssues");function En(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n=new Set(On(e).map(o=>o.name)),t={};for(const[o,r]of Object.entries(e)){if(n.has(o))continue;const i=r;t[o]={argv:[...i.argv],cwd:i.cwd}}return t}s(En,"normalizeHumanActions");function yo(e){return Sn(e).map(n=>n.message)}s(yo,"invalidPluginBinReasons");function ho(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[t,o]of Object.entries(e))P(t,"requires")===void 0&&(typeof o!="string"||o.trim()===""||/[\r\n]/.test(o)||(n[t]=o));return n}s(ho,"normalizeRequires");function bo(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[t,o]of Object.entries(e)){const r=P(t,"requires");r!==void 0&&n.push(r),typeof o!="string"||o.trim()===""?n.push(`requires.${t} must be a non-empty one-line install hint`):/[\r\n]/.test(o)&&n.push(`requires.${t} must be a one-line install hint`)}return n}s(bo,"invalidPluginRequiresReasons");function de(e){if(e===null||typeof e!="object")return null;const n=e,t={};typeof n.whenToUse=="string"&&n.whenToUse.trim()!==""&&(t.whenToUse=n.whenToUse),typeof n.model=="string"&&n.model.trim()!==""&&(t.model=n.model),typeof n.orchestratorModel=="string"&&n.orchestratorModel.trim()!==""&&(t.orchestratorModel=n.orchestratorModel);const o=s(r=>Array.isArray(r)&&r.every(i=>typeof i=="string"),"isStringArray");return o(n.tools)&&(t.tools=n.tools),o(n.extensions)&&(t.extensions=n.extensions),o(n.availableTo)&&(t.availableTo=n.availableTo),Object.keys(t).length>0?t:null}s(de,"normalizeKindFields");function A(e,n=Ge()){const t={...n};if(e!==null&&typeof e=="object")for(const[o,r]of Object.entries(e)){const i=de(r);if(i===null)continue;const d=t[o];d!==void 0?t[o]={...d,...i}:i.whenToUse!==void 0&&(t[o]=i)}return t}s(A,"mergeKinds");function ce(e,n,t){if(n===null)return t;const o=Y(e,n).filter(i=>i.enabled&&i.manifest.kinds!==void 0).sort((i,d)=>i.name.localeCompare(d.name));let r=t;for(const i of o)r=A(i.manifest.kinds,r);return r}s(ce,"layerPluginKinds");function _o(){const e=[{origin:"config.json",components:gn("user").page_components}],n=y("user");if(n!==null){const t=Y("user",n).filter(o=>o.enabled&&o.manifest.page_components!==void 0).sort((o,r)=>o.name.localeCompare(r.name));for(const o of t){const r=`plugin "${o.name}" page_components`;e.push({origin:`plugin "${o.name}"`,components:X(o.manifest.page_components,r)})}}return mn(e)}s(_o,"resolvePageComponents");const K=new Set(["whenToUse","model","orchestratorModel","tools","extensions","availableTo"]);function vo(e){if(e===null||typeof e!="object"||Array.isArray(e))return["kinds must be a JSON object keyed by kind name"];const n=[],t=s(o=>Array.isArray(o)&&o.every(r=>typeof r=="string"),"isStringArray");for(const[o,r]of Object.entries(e)){if(o.trim()===""){n.push("kinds contains an empty kind name");continue}if(r===null||typeof r!="object"||Array.isArray(r)){n.push(`kinds.${o} must be a JSON object`);continue}const i=r;for(const d of Object.keys(i))K.has(d)||n.push(`kinds.${o}.${d} is not a KindConfig field (expected one of: ${[...K].join(", ")})`);i.whenToUse!==void 0&&(typeof i.whenToUse!="string"||i.whenToUse.trim()==="")&&n.push(`kinds.${o}.whenToUse must be a non-empty string`);for(const d of["model","orchestratorModel"])i[d]!==void 0&&(typeof i[d]!="string"||i[d].trim()==="")&&n.push(`kinds.${o}.${d} must be a non-empty string`);for(const d of["tools","extensions","availableTo"])i[d]!==void 0&&!t(i[d])&&n.push(`kinds.${o}.${d} must be an array of strings`);de(r)===null&&n.push(`kinds.${o} has no valid KindConfig field \u2014 an entry must set at least one of: ${[...K].join(", ")} (whenToUse is required when the kind exists in no lower layer)`)}return n}s(vo,"invalidPluginKindsReasons");function In(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 t={previewEndpoint:n.previewEndpoint,relayTokenRef:n.relayTokenRef};return typeof n.cpOrigin=="string"&&n.cpOrigin.trim()!==""&&(t.cpOrigin=n.cpOrigin),t}s(In,"normalizeRemoteCanvasTarget");function Pn(e,n=Be()){const t={...n.targets};if(e!==null&&typeof e=="object"){const o=e;if(o.targets!==null&&typeof o.targets=="object")for(const[r,i]of Object.entries(o.targets)){const d=In(i);d!==null&&(t[r]=d)}}return{targets:t}}s(Pn,"mergeRemoteCanvas");function ue(e,n){return e===!0?"notify":e===!1?!1:e==="notify"||e==="apply"?e:n}s(ue,"normalizeMode");function R(e,n){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=1?e:n}s(R,"normalizePositiveInteger");function Tn(e,n){const t=e!==null&&typeof e=="object"?e:{},o=R(t.warning,n.warning),r=R(t.automaticReviveCap,n.automaticReviveCap);return o<=r?{warning:o,automaticReviveCap:r}:{...n}}s(Tn,"mergeBrokerThresholds");function $n(e,n){return{unattendedParkMs:R((e!==null&&typeof e=="object"?e:{}).unattendedParkMs,n.unattendedParkMs)}}s($n,"mergeLifecycle");function Cn(e){if(!Array.isArray(e))return[...J];const n=e.filter(t=>typeof t=="string"&&t.trim()!=="").map(t=>t.trim());return n.length>0?n:[...J]}s(Cn,"normalizeWhipMessages");function T(e){const n=b(),t=n.schema_version,o=e.marketplaces===void 0?{}:e.marketplaces,r=e.plugins===void 0?{}:e.plugins,i=e.auto_update,d=i&&typeof i.interval_hours=="number"?i.interval_hours:void 0,c=d!==void 0&&Number.isFinite(d)&&d>=0?d:n.auto_update.interval_hours,u={crtr:ue(i?.crtr,n.auto_update.crtr),content:ue(i?.content,n.auto_update.content),interval_hours:c},f=e.max_panes_per_window,l=R(f,n.max_panes_per_window),a=typeof e.auto_open_child_viewers=="boolean"?e.auto_open_child_viewers:n.auto_open_child_viewers,m=Tn(e.brokerThresholds,n.brokerThresholds),$=$n(e.lifecycle,n.lifecycle),p=typeof e.completion_bell=="boolean"?e.completion_bell:n.completion_bell,le=e.working_gerunds===void 0?[...n.working_gerunds]:an(e.working_gerunds),fe=typeof e.whip_mode=="boolean"?e.whip_mode:n.whip_mode,ae=X(e.page_components),me=typeof e.page_surface=="boolean"?e.page_surface:n.page_surface,pe=Cn(e.whip_messages),ge=Ue.includes(e.whip_message_mode)?e.whip_message_mode:n.whip_message_mode,ye=De.includes(e.mouse_mode_default)?e.mouse_mode_default:n.mouse_mode_default,he=R(e.live_cycles,n.live_cycles),be=ze.includes(e.condensed_history)?e.condensed_history:n.condensed_history,_e=typeof e.bash_tool_purpose=="boolean"?e.bash_tool_purpose:n.bash_tool_purpose,ve=typeof e.fold_finished_tools=="boolean"?e.fold_finished_tools:n.fold_finished_tools,ke=typeof e.summarize_tool_calls=="boolean"?e.summarize_tool_calls:n.summarize_tool_calls,we=typeof e.detailed_tool_recaps=="boolean"?e.detailed_tool_recaps:n.detailed_tool_recaps,Ae=vn(e.keybindings),Re=wn(e.tmux_passthrough),je=S(e.modelLadders),Se=I(e.providerOptions,n.providerOptions),C=re(x(e.modelRoutes)),G=C.complete,B=O(e.modelRouting,void 0),xe=A(e.kinds),Oe=Pn(e.remoteCanvas),Ee=E(e.spawnEnv,n.spawnEnv?.allow),Ie=xn(e.bin),Pe=En(e.humanActions);return{bin:Ie,humanActions:Pe,schema_version:t,marketplaces:o,plugins:r,auto_update:u,max_panes_per_window:l,auto_open_child_viewers:a,brokerThresholds:m,lifecycle:$,completion_bell:p,working_gerunds:le,whip_mode:fe,page_components:ae,page_surface:me,whip_messages:pe,whip_message_mode:ge,mouse_mode_default:ye,live_cycles:he,condensed_history:be,bash_tool_purpose:_e,fold_finished_tools:ve,summarize_tool_calls:ke,detailed_tool_recaps:we,keybindings:Ae,tmux_passthrough:Re,modelLadders:je,providerOptions:Se,...Object.keys(G).length>0?{modelRoutes:G}:{},...Object.keys(C.legacy).length>0?{legacyModelRoutes:C.legacy}:{},...B!==void 0?{modelRouting:B}:{},kinds:xe,remoteCanvas:Oe,spawnEnv:Ee}}s(T,"mergeConfig");function Mn(e){return j(e)}s(Mn,"readRawConfigAtRoot");function Ln(e){const n=y(e);return n?Mn(n):null}s(Ln,"readRawScopeConfig");function Nn(e){if(e===null||e==="")return null;try{return j(N(e))}catch{return null}}s(Nn,"readRawProfileConfig");function Fn(e,n){return[...Qe(e,n)].reverse().map(r=>({root:r,raw:j(r)}))}s(Fn,"readRawProjectScopeConfigs");function q(e=process.cwd(),n=$e()||null){const t=b(),o=Ln("user"),r=Nn(n),i=Fn(e,n);let d=A(o?.kinds,ce("user",y("user"),t.kinds)),c=S(o?.modelLadders,t.modelLadders),u=I(o?.providerOptions,t.providerOptions),f=x(o?.modelRoutes),l=O(o?.modelRouting,void 0),a=E(o?.spawnEnv,t.spawnEnv?.allow);r!==null&&(d=A(r.kinds,d),c=S(r.modelLadders,c),u=I(r.providerOptions,u),f=x(r.modelRoutes,f),l=O(r.modelRouting,l),a=E(r.spawnEnv,a.allow));for(const{root:$,raw:p}of i)d=A(p?.kinds,ce("project",$,d)),p!==null&&(c=S(p.modelLadders,c),u=I(p.providerOptions,u),f=x(p.modelRoutes,f),l=O(p.modelRouting,l),a=E(p.spawnEnv,a.allow));const m=re(f);return{kinds:d,modelLadders:c,providerOptions:u,...Object.keys(m.complete).length>0?{modelRoutes:m.complete}:{},...Object.keys(m.legacy).length>0?{legacyModelRoutes:m.legacy}:{},...l!==void 0?{modelRouting:l}:{},spawnEnv:a}}s(q,"readMergedLaunchConfig");function ko(e){return q().kinds[e]}s(ko,"resolveKindConfig");function wo(e){const n=Object.keys(q().kinds).sort();if(!n.includes(e))throw Ye(`unknown kind: ${e}`,{received:e,field:"kind",next:`Valid kinds: ${n.join(", ")}.`})}s(wo,"assertInstalledKind");function Ao(e){const n=q().kinds,t=[];for(const[o,r]of Object.entries(n)){const i=o.indexOf("/");if(i===-1)continue;const d=o.slice(0,i),c=r.availableTo??[d];(c.includes("*")||c.includes(e))&&t.push({kind:o,whenToUse:r.whenToUse})}return t.sort((o,r)=>o.kind.localeCompare(r.kind))}s(Ao,"subKindsAvailableTo");function zn(e,n,t){const o={...e??{}};o.anthropic!==void 0&&(o.anthropic={...o.anthropic}),o.openai!==void 0&&(o.openai={...o.openai}),t.defaultProvider!==n.defaultProvider&&t.defaultProvider!==void 0&&(o.defaultProvider=t.defaultProvider);for(const i of["anthropic","openai"])for(const d of["ultra","strong","medium","light"])t[i][d]!==n[i][d]&&(o[i]={...o[i]??{},[d]:t[i][d]});return o.defaultProvider!==void 0||o.anthropic!==void 0||o.openai!==void 0?o:void 0}s(zn,"sparseModelLaddersToPersist");function Dn(e,n){return v(e,t=>{const o=T(t),r=T(t);n(r);const i={...t};for(const c of fn)c!=="modelLadders"&&!Le(r[c],o[c])&&(i[c]=r[c]);const d=zn(t.modelLadders,o.modelLadders,r.modelLadders);return d!==void 0?i.modelLadders=d:delete i.modelLadders,{value:r,next:i}})}s(Dn,"mutateConfigAtRoot");function Ro(e,n){v(e,t=>{const o=t.modelLadders??{},r={...o};n.defaultProvider!==void 0&&(r.defaultProvider=n.defaultProvider);for(const i of["anthropic","openai"])n[i]!==void 0&&(r[i]={...o[i]??{},...n[i]});return{value:void 0,next:{...t,modelLadders:r}}})}s(Ro,"persistModelLadders");function jo(e,n,t){if(t!==void 0)An(n,t);else if(n.trim()==="")throw new Error("model route id must be a non-empty string");v(e,o=>{const r=te(o.modelRoutes??{});t===void 0?delete r[n]:k(r,n,{...t,models:{...t.models}});const i={...o};return Object.keys(r).length===0?delete i.modelRoutes:i.modelRoutes=r,{value:void 0,next:i}})}s(jo,"persistModelRoute");function Un(e,n){return Dn(e,n)}s(Un,"updateConfigAtRoot");function So(e,n){return Un(_(e),n)}s(So,"updateConfig");function xo(e){const n=e.orchestrator===!0?"orchestratorModel":"model";return v(e.root,t=>{const o={...t.kinds??{},[e.kind]:{...(t.kinds??{})[e.kind],[n]:e.model}};return{value:void 0,next:{...t,kinds:o}}}),{path:h(e.root),field:n}}s(xo,"persistDefaultKindModel");function Oo(e,n){const t=hn(e);return n(t),_n(e,t),t}s(Oo,"updateState");export{bn as InvalidRawSettingsError,wo as assertInstalledKind,Sn as binDeclarationIssues,co as configPath,go as ensureScopeInitialized,On as humanActionDeclarationIssues,yo as invalidPluginBinReasons,vo as invalidPluginKindsReasons,bo as invalidPluginRequiresReasons,xn as normalizeBin,En as normalizeHumanActions,ho as normalizeRequires,wn as normalizeTmuxPassthrough,xo as persistDefaultKindModel,Ro as persistModelLadders,jo as persistModelRoute,gn as readConfig,yn as readConfigAtRoot,q as readMergedLaunchConfig,Mn as readRawConfigAtRoot,Ln as readRawScopeConfig,lo as readRawSettingsForTarget,hn as readState,fo as replaceRawSettingsForTarget,ko as resolveKindConfig,_o as resolvePageComponents,uo as statePath,Ao as subKindsAvailableTo,So as updateConfig,Un as updateConfigAtRoot,ao as updateRawConfigAtomically,Oo as updateState,mo as writeConfig,po as writeRawConfig,_n as writeState};
|
|
1
|
+
var $e=Object.defineProperty;var s=(e,n)=>$e(e,"name",{value:n,configurable:!0});import{envProfileId as Ce}from"../shared/env.js";import{basename as Me,dirname as Le,join as H}from"node:path";import{isDeepStrictEqual as Ne}from"node:util";import{CONFIG_FILE as Fe,STATE_FILE as ze,CONDENSED_HISTORY_MODES as De,MOUSE_MODE_DEFAULTS as Ue,WHIP_MESSAGE_MODES as Ke,DEFAULT_WHIP_MESSAGES as J,defaultScopeConfig as b,defaultScopeState as qe,defaultModelLaddersConfig as Ge,defaultKindsConfig as Be,defaultRemoteCanvasConfig as He}from"../types.js";import{BINDING_CATALOG as Je,BINDING_IDS as We,isAttachPaneBinding as Ze}from"./keybindings/catalog.js";import{notFound as Ye,usage as Ve}from"./errors.js";import{emitEvent as Qe}from"./events/emit.js";import{atomicWriteJson as W,readJsonIfExists as M,ensureDir as Z}from"./fs-utils.js";import{scopeRoot as y,requireScopeRoot as _,findProjectScopeRoots as Xe,userScopeRoot as L}from"./scope.js";import{listInstalledPluginsInRoot as Y}from"./installed-plugins.js";import{profileRoot as N,listProfiles as V}from"./profiles/manifest.js";import{canonicalRoot as F,projectStore as en,userStore as Q}from"./scoped-state/paths.js";import{convertLegacyScopedStore as nn,initializeFreshScopedStore as on}from"./scoped-state/migrate.js";import{mutateRawSettings as tn,readBookkeeping as rn,readRawSettings as sn,readRawSettingsWithRevision as cn,replaceRawSettings as dn,writeBookkeeping as un,writeRawSettings as ln}from"./scoped-state/settings.js";import{withExclusiveDirectoryLock as fn}from"./exclusive-lock.js";import{SCOPE_CONFIG_KEYS as an}from"./user-settings.js";import{normalizeWorkingGerunds as mn}from"../shared/working-activity.js";import{mergePageComponentLayers as pn,normalizePageComponents as X}from"./human/page-catalog.js";function h(e){return H(e,Fe)}s(h,"configPathFor");function ee(e,n){return Z(Le(e)),fn(`${e}.lock`,()=>n(e),{timeoutError:s(()=>new Error(`timed out waiting for config lock: ${e}`),"timeoutError")})}s(ee,"withConfigPathLock");function gn(e){return H(e,ze)}s(gn,"statePathFor");function lo(e){const n=y(e);return n?h(n):null}s(lo,"configPath");function fo(e){const n=y(e);return n?gn(n):null}s(fo,"statePath");function g(e){const n=F(e),t=F(L());if(n===t)return{store:Q(t),key:"user",kind:"user",root:t};if(Me(n)===".crouter")return nn({kind:"project",root:n}),{store:en(n),key:"project",kind:"project",root:n};for(const o of V()){const r=F(N(o.profileId));if(n===r)return{store:Q(t),key:`profile:${o.profileId}`,kind:"profile",root:r,profileId:o.profileId}}throw new Error(`settings root is not a recognized owner: ${e}`)}s(g,"settingsOwnerForRoot");function R(e){const n=g(e);return n.kind==="project"?M(h(n.root)):sn(n)}s(R,"readRawAtRoot");function z(e,n){const t=g(e);if(t.kind==="project"){ee(h(t.root),o=>W(o,n));return}ln(t,n)}s(z,"writeRawAtRoot");function v(e,n){const t=g(e);return t.kind==="project"?ee(h(t.root),o=>{const r=n(M(o)??{});return W(o,r.next),r.value}):tn(t,o=>n(o))}s(v,"mutateRawAtRoot");function yn(e){const n=y(e);return n?hn(n):b()}s(yn,"readConfig");function hn(e){const n=R(e);return n?T(n):b()}s(hn,"readConfigAtRoot");function bn(e){const n=y(e);return n?rn(g(n)):qe()}s(bn,"readState");class _n extends Error{static{s(this,"InvalidRawSettingsError")}constructor(n){super(`settings cannot be merged: ${n instanceof Error?n.message:String(n)}`),this.name="InvalidRawSettingsError"}}function ne(e){if(e.scope==="user")return g(L());if(!V().some(n=>n.profileId===e.profileId))throw Ye(`profile not found: ${e.profileId}`,{received:e.profileId,next:"Run `crtr profile list` to see available profiles."});return g(N(e.profileId))}s(ne,"rawSettingsOwnerForTarget");function ao(e){return cn(ne(e))}s(ao,"readRawSettingsForTarget");function mo(e,n,t){return dn(ne(e),n,t,o=>{try{T(o)}catch(r){throw new _n(r)}})}s(mo,"replaceRawSettingsForTarget");function po(e,n){const t=_(e);return v(t,o=>{const r=n(o);return{value:r,next:r}})}s(po,"updateRawConfigAtomically");function go(e,n){z(_(e),n)}s(go,"writeConfig");function yo(e,n){z(_(e),n)}s(yo,"writeRawConfig");function vn(e,n){const t=_(e);un(g(t),n)}s(vn,"writeState");function ho(e,n){Z(n);const t=g(e==="user"?L():n);if(on({kind:t.kind==="user"?"user":"project",root:t.store.root}),t.kind==="project"&&M(h(t.root))===null){const{modelLadders:o,keybindings:r,tmux_passthrough:i,brokerThresholds:c,lifecycle:d,api:u,...f}=b();z(t.root,f)}}s(ho,"ensureScopeInitialized");function kn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[t,o]of Object.entries(e))Array.isArray(o)&&o.every(r=>typeof r=="string")&&(n[t]=[...o]);return n}s(kn,"normalizeKeybindings");const An=new Set(Je.filter(Ze).map(e=>e.id));function wn(e){return Array.isArray(e)?[...new Set(e.filter(n=>typeof n=="string"&&We.has(n)&&An.has(n)))]:[]}s(wn,"normalizeTmuxPassthrough");function S(e,n=Ge()){const t=n;if(e===null||typeof e!="object")return t;const o=e,r={anthropic:{...t.anthropic},openai:{...t.openai},...t.defaultProvider!==void 0?{defaultProvider:t.defaultProvider}:{}};(o.defaultProvider==="anthropic"||o.defaultProvider==="openai")&&(r.defaultProvider=o.defaultProvider);for(const i of["anthropic","openai"]){const c=o[i];if(!(c===null||typeof c!="object"))for(const[d,u]of Object.entries(c))(d==="ultra"||d==="strong"||d==="medium"||d==="light")&&typeof u=="string"&&(r[i][d]=u)}return r}s(S,"mergeModelLadders");function oe(e){return e==="ultra"||e==="strong"||e==="medium"||e==="light"}s(oe,"validStrength");const D=["ultra","strong","medium","light"];function U(){return Object.create(null)}s(U,"routeRecord");function k(e,n,t){Object.defineProperty(e,n,{value:t,writable:!0,enumerable:!0,configurable:!0})}s(k,"setRouteRecord");function te(e){const n=U();for(const[t,o]of Object.entries(e??{}))k(n,t,o);return n}s(te,"copyRouteRecord");function A(e,n){Qe({level:"warn",event:"config.model_routes.invalid",fields:{route_id:e,rule:n}})}s(A,"routeInvalid");function x(e,n={}){const t=te(n);if(e===null||typeof e!="object"||Array.isArray(e))return t;for(const[o,r]of Object.entries(e)){if(o.trim()===""){A(o,"route id must be a non-empty string");continue}if(r===null||typeof r!="object"||Array.isArray(r)){A(o,"route must be an object");continue}const i=r,c=t[o],d={...c};let u;for(const f of["family","credentialSource","providerId"]){const l=i[f];if(l!==void 0){if(typeof l!="string"||l.trim()===""){u=`${f} must be a non-empty string`;break}d[f]=l}}if(u===void 0&&i.models!==void 0)if(i.models===null||typeof i.models!="object"||Array.isArray(i.models))u="models must be an object";else{const f={...c?.models};for(const[l,a]of Object.entries(i.models)){if(!oe(l)){u=`models.${l} is not a model strength`;break}if(typeof a!="string"||a.trim()===""){u=`models.${l} must be a non-empty provider/id ref`;break}const m=a.indexOf("/");if(m<=0||m===a.length-1){u=`models.${l} must be provider/id`;break}f[l]=a}d.models=f}if(u===void 0&&d.models!==void 0&&d.providerId!==void 0){for(const[f,l]of Object.entries(d.models))if(l.slice(0,l.indexOf("/"))!==d.providerId){u=`models.${f} provider prefix must equal providerId`;break}}if(u!==void 0){A(o,u);continue}k(t,o,d)}return t}s(x,"mergeModelRoutePatches");function re(e){const n=U(),t=U();for(const[o,r]of Object.entries(e)){if(r.family===void 0||r.credentialSource===void 0||r.providerId===void 0||r.models===void 0){A(o,"route must include family, credentialSource, providerId, and models");continue}const i=D.filter(c=>r.models[c]===void 0);if(i.length>0){A(o,`incomplete route; missing strengths: ${i.join(", ")}`),k(t,o,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models});continue}k(n,o,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models})}return{complete:n,legacy:t}}s(re,"resolveModelRoutes");function jn(e,n){if(e.trim()==="")throw new Error("model route id must be a non-empty string");for(const o of["family","credentialSource","providerId"])if(typeof n[o]!="string"||n[o].trim()==="")throw new Error(`model route "${e}" ${o} must be a non-empty string`);const t=D.filter(o=>n.models?.[o]===void 0);if(t.length>0)throw new Error(`model route "${e}" is incomplete; missing strengths: ${t.join(", ")}`);for(const o of D){const r=n.models[o];if(typeof r!="string"||r.trim()==="")throw new Error(`model route "${e}" models.${o} 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.${o} must be provider/id`);if(r.slice(0,i)!==n.providerId)throw new Error(`model route "${e}" models.${o} provider prefix must equal providerId`)}}s(jn,"assertCompleteModelRoute");function O(e,n){if(e===void 0||e===null||typeof e!="object"||Array.isArray(e))return n;const t=e,o=s(i=>Array.isArray(i)&&i.every(c=>typeof c=="string"&&c.trim()!=="")?[...i]:void 0,"strings"),r=Array.isArray(t.strengthFallback)&&t.strengthFallback.every(i=>oe(i))?[...t.strengthFallback]:void 0;return{...o(t.credentialSourceOrder)!==void 0?{credentialSourceOrder:o(t.credentialSourceOrder)}:{},...o(t.familyOrder)!==void 0?{familyOrder:o(t.familyOrder)}:{},...r!==void 0?{strengthFallback:r}:{}}}s(O,"mergeModelRouting");function Rn(e){return e!==""&&e.replace(/\*/g,"")===""}s(Rn,"isInheritAllGlob");function E(e,n=[]){const t=e,o=Array.isArray(t?.allow)?t.allow.filter(r=>typeof r=="string"):[];for(const r of o)if(Rn(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,...o])]}}s(E,"mergeSpawnEnv");function I(e,n={}){const t={};for(const[o,r]of Object.entries(n))t[o]={...r};if(e!==null&&typeof e=="object")for(const[o,r]of Object.entries(e)){if(r===null||typeof r!="object")continue;const i={...t[o]},c=r.fastMode;typeof c=="boolean"&&(i.fastMode=c),t[o]=i}return t}s(I,"mergeProviderOptions");const ie=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,Sn=new Set(["crtr","crtrd"]);function P(e,n){if(n==="bin"&&Sn.has(e))return`${n}.${e} is reserved by crouter \u2014 pick another bare command name`;if(!ie.test(e))return`${n}.${e} is not a safe bare command name (expected ${ie.source})`}s(P,"bareCommandNameIssue");function xn(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[t,o]of Object.entries(e)){const r=P(t,"bin");r!==void 0&&n.push({name:t,message:r}),(typeof o!="string"||o.trim()==="")&&n.push({name:t,message:`bin.${t} must be a non-empty relative path to an executable inside the contributor root`})}return n}s(xn,"binDeclarationIssues");function On(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[t,o]of Object.entries(e))P(t,"bin")===void 0&&(typeof o!="string"||o.trim()===""||(n[t]=o));return n}s(On,"normalizeBin");const se=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function En(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[t,o]of Object.entries(e)){const r=[];if(se.test(t)||r.push(`action name must match ${se.source}`),o===null||typeof o!="object"||Array.isArray(o))r.push("action must be an object with argv and cwd");else{const i=o,c=Object.keys(i).filter(d=>d!=="argv"&&d!=="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(d=>typeof d=="string"&&d.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:t,reason:r.join("; ")})}return n}s(En,"humanActionDeclarationIssues");function In(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n=new Set(En(e).map(o=>o.name)),t={};for(const[o,r]of Object.entries(e)){if(n.has(o))continue;const i=r;t[o]={argv:[...i.argv],cwd:i.cwd}}return t}s(In,"normalizeHumanActions");function bo(e){return xn(e).map(n=>n.message)}s(bo,"invalidPluginBinReasons");function _o(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[t,o]of Object.entries(e))P(t,"requires")===void 0&&(typeof o!="string"||o.trim()===""||/[\r\n]/.test(o)||(n[t]=o));return n}s(_o,"normalizeRequires");function vo(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[t,o]of Object.entries(e)){const r=P(t,"requires");r!==void 0&&n.push(r),typeof o!="string"||o.trim()===""?n.push(`requires.${t} must be a non-empty one-line install hint`):/[\r\n]/.test(o)&&n.push(`requires.${t} must be a one-line install hint`)}return n}s(vo,"invalidPluginRequiresReasons");function ce(e){if(e===null||typeof e!="object")return null;const n=e,t={};typeof n.whenToUse=="string"&&n.whenToUse.trim()!==""&&(t.whenToUse=n.whenToUse),typeof n.model=="string"&&n.model.trim()!==""&&(t.model=n.model),typeof n.orchestratorModel=="string"&&n.orchestratorModel.trim()!==""&&(t.orchestratorModel=n.orchestratorModel);const o=s(r=>Array.isArray(r)&&r.every(i=>typeof i=="string"),"isStringArray");return o(n.tools)&&(t.tools=n.tools),o(n.extensions)&&(t.extensions=n.extensions),o(n.availableTo)&&(t.availableTo=n.availableTo),Object.keys(t).length>0?t:null}s(ce,"normalizeKindFields");function w(e,n=Be()){const t={...n};if(e!==null&&typeof e=="object")for(const[o,r]of Object.entries(e)){const i=ce(r);if(i===null)continue;const c=t[o];c!==void 0?t[o]={...c,...i}:i.whenToUse!==void 0&&(t[o]=i)}return t}s(w,"mergeKinds");function de(e,n,t){if(n===null)return t;const o=Y(e,n).filter(i=>i.enabled&&i.manifest.kinds!==void 0).sort((i,c)=>i.name.localeCompare(c.name));let r=t;for(const i of o)r=w(i.manifest.kinds,r);return r}s(de,"layerPluginKinds");function ko(){const e=[{origin:"config.json",components:yn("user").page_components}],n=y("user");if(n!==null){const t=Y("user",n).filter(o=>o.enabled&&o.manifest.page_components!==void 0).sort((o,r)=>o.name.localeCompare(r.name));for(const o of t){const r=`plugin "${o.name}" page_components`;e.push({origin:`plugin "${o.name}"`,components:X(o.manifest.page_components,r)})}}return pn(e)}s(ko,"resolvePageComponents");const K=new Set(["whenToUse","model","orchestratorModel","tools","extensions","availableTo"]);function Ao(e){if(e===null||typeof e!="object"||Array.isArray(e))return["kinds must be a JSON object keyed by kind name"];const n=[],t=s(o=>Array.isArray(o)&&o.every(r=>typeof r=="string"),"isStringArray");for(const[o,r]of Object.entries(e)){if(o.trim()===""){n.push("kinds contains an empty kind name");continue}if(r===null||typeof r!="object"||Array.isArray(r)){n.push(`kinds.${o} must be a JSON object`);continue}const i=r;for(const c of Object.keys(i))K.has(c)||n.push(`kinds.${o}.${c} is not a KindConfig field (expected one of: ${[...K].join(", ")})`);i.whenToUse!==void 0&&(typeof i.whenToUse!="string"||i.whenToUse.trim()==="")&&n.push(`kinds.${o}.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.${o}.${c} must be a non-empty string`);for(const c of["tools","extensions","availableTo"])i[c]!==void 0&&!t(i[c])&&n.push(`kinds.${o}.${c} must be an array of strings`);ce(r)===null&&n.push(`kinds.${o} has no valid KindConfig field \u2014 an entry must set at least one of: ${[...K].join(", ")} (whenToUse is required when the kind exists in no lower layer)`)}return n}s(Ao,"invalidPluginKindsReasons");function Pn(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 t={previewEndpoint:n.previewEndpoint,relayTokenRef:n.relayTokenRef};return typeof n.cpOrigin=="string"&&n.cpOrigin.trim()!==""&&(t.cpOrigin=n.cpOrigin),t}s(Pn,"normalizeRemoteCanvasTarget");function Tn(e,n=He()){const t={...n.targets};if(e!==null&&typeof e=="object"){const o=e;if(o.targets!==null&&typeof o.targets=="object")for(const[r,i]of Object.entries(o.targets)){const c=Pn(i);c!==null&&(t[r]=c)}}return{targets:t}}s(Tn,"mergeRemoteCanvas");function ue(e,n){return e===!0?"notify":e===!1?!1:e==="notify"||e==="apply"?e:n}s(ue,"normalizeMode");function j(e,n){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=1?e:n}s(j,"normalizePositiveInteger");function $n(e,n){const t=e!==null&&typeof e=="object"?e:{},o=j(t.warning,n.warning),r=j(t.automaticReviveCap,n.automaticReviveCap);return o<=r?{warning:o,automaticReviveCap:r}:{...n}}s($n,"mergeBrokerThresholds");function Cn(e,n){return{unattendedParkMs:j((e!==null&&typeof e=="object"?e:{}).unattendedParkMs,n.unattendedParkMs)}}s(Cn,"mergeLifecycle");function Mn(e){if(!Array.isArray(e))return[...J];const n=e.filter(t=>typeof t=="string"&&t.trim()!=="").map(t=>t.trim());return n.length>0?n:[...J]}s(Mn,"normalizeWhipMessages");function Ln(e,n){if(e===null||typeof e!="object")return{...n};const t=e.tcp;return{tcp:typeof t=="string"?t:n.tcp}}s(Ln,"mergeApi");function T(e){const n=b(),t=n.schema_version,o=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,d=c!==void 0&&Number.isFinite(c)&&c>=0?c:n.auto_update.interval_hours,u={crtr:ue(i?.crtr,n.auto_update.crtr),content:ue(i?.content,n.auto_update.content),interval_hours:d},f=e.max_panes_per_window,l=j(f,n.max_panes_per_window),a=typeof e.auto_open_child_viewers=="boolean"?e.auto_open_child_viewers:n.auto_open_child_viewers,m=$n(e.brokerThresholds,n.brokerThresholds),$=Cn(e.lifecycle,n.lifecycle),p=typeof e.completion_bell=="boolean"?e.completion_bell:n.completion_bell,le=e.working_gerunds===void 0?[...n.working_gerunds]:mn(e.working_gerunds),fe=typeof e.whip_mode=="boolean"?e.whip_mode:n.whip_mode,ae=X(e.page_components),me=typeof e.page_surface=="boolean"?e.page_surface:n.page_surface,pe=Mn(e.whip_messages),ge=Ke.includes(e.whip_message_mode)?e.whip_message_mode:n.whip_message_mode,ye=Ue.includes(e.mouse_mode_default)?e.mouse_mode_default:n.mouse_mode_default,he=j(e.live_cycles,n.live_cycles),be=De.includes(e.condensed_history)?e.condensed_history:n.condensed_history,_e=typeof e.bash_tool_purpose=="boolean"?e.bash_tool_purpose:n.bash_tool_purpose,ve=typeof e.fold_finished_tools=="boolean"?e.fold_finished_tools:n.fold_finished_tools,ke=typeof e.summarize_tool_calls=="boolean"?e.summarize_tool_calls:n.summarize_tool_calls,Ae=typeof e.detailed_tool_recaps=="boolean"?e.detailed_tool_recaps:n.detailed_tool_recaps,we=kn(e.keybindings),je=wn(e.tmux_passthrough),Re=S(e.modelLadders),Se=I(e.providerOptions,n.providerOptions),C=re(x(e.modelRoutes)),G=C.complete,B=O(e.modelRouting,void 0),xe=w(e.kinds),Oe=Tn(e.remoteCanvas),Ee=Ln(e.api,n.api),Ie=E(e.spawnEnv,n.spawnEnv?.allow),Pe=On(e.bin),Te=In(e.humanActions);return{bin:Pe,humanActions:Te,schema_version:t,marketplaces:o,plugins:r,auto_update:u,max_panes_per_window:l,auto_open_child_viewers:a,brokerThresholds:m,lifecycle:$,completion_bell:p,working_gerunds:le,whip_mode:fe,page_components:ae,page_surface:me,whip_messages:pe,whip_message_mode:ge,mouse_mode_default:ye,live_cycles:he,condensed_history:be,bash_tool_purpose:_e,fold_finished_tools:ve,summarize_tool_calls:ke,detailed_tool_recaps:Ae,keybindings:we,tmux_passthrough:je,modelLadders:Re,providerOptions:Se,...Object.keys(G).length>0?{modelRoutes:G}:{},...Object.keys(C.legacy).length>0?{legacyModelRoutes:C.legacy}:{},...B!==void 0?{modelRouting:B}:{},kinds:xe,remoteCanvas:Oe,api:Ee,spawnEnv:Ie}}s(T,"mergeConfig");function Nn(e){return R(e)}s(Nn,"readRawConfigAtRoot");function Fn(e){const n=y(e);return n?Nn(n):null}s(Fn,"readRawScopeConfig");function zn(e){if(e===null||e==="")return null;try{return R(N(e))}catch{return null}}s(zn,"readRawProfileConfig");function Dn(e,n){return[...Xe(e,n)].reverse().map(r=>({root:r,raw:R(r)}))}s(Dn,"readRawProjectScopeConfigs");function q(e=process.cwd(),n=Ce()||null){const t=b(),o=Fn("user"),r=zn(n),i=Dn(e,n);let c=w(o?.kinds,de("user",y("user"),t.kinds)),d=S(o?.modelLadders,t.modelLadders),u=I(o?.providerOptions,t.providerOptions),f=x(o?.modelRoutes),l=O(o?.modelRouting,void 0),a=E(o?.spawnEnv,t.spawnEnv?.allow);r!==null&&(c=w(r.kinds,c),d=S(r.modelLadders,d),u=I(r.providerOptions,u),f=x(r.modelRoutes,f),l=O(r.modelRouting,l),a=E(r.spawnEnv,a.allow));for(const{root:$,raw:p}of i)c=w(p?.kinds,de("project",$,c)),p!==null&&(d=S(p.modelLadders,d),u=I(p.providerOptions,u),f=x(p.modelRoutes,f),l=O(p.modelRouting,l),a=E(p.spawnEnv,a.allow));const m=re(f);return{kinds:c,modelLadders:d,providerOptions:u,...Object.keys(m.complete).length>0?{modelRoutes:m.complete}:{},...Object.keys(m.legacy).length>0?{legacyModelRoutes:m.legacy}:{},...l!==void 0?{modelRouting:l}:{},spawnEnv:a}}s(q,"readMergedLaunchConfig");function wo(e){return q().kinds[e]}s(wo,"resolveKindConfig");function jo(e){const n=Object.keys(q().kinds).sort();if(!n.includes(e))throw Ve(`unknown kind: ${e}`,{received:e,field:"kind",next:`Valid kinds: ${n.join(", ")}.`})}s(jo,"assertInstalledKind");function Ro(e){const n=q().kinds,t=[];for(const[o,r]of Object.entries(n)){const i=o.indexOf("/");if(i===-1)continue;const c=o.slice(0,i),d=r.availableTo??[c];(d.includes("*")||d.includes(e))&&t.push({kind:o,whenToUse:r.whenToUse})}return t.sort((o,r)=>o.kind.localeCompare(r.kind))}s(Ro,"subKindsAvailableTo");function Un(e,n,t){const o={...e??{}};o.anthropic!==void 0&&(o.anthropic={...o.anthropic}),o.openai!==void 0&&(o.openai={...o.openai}),t.defaultProvider!==n.defaultProvider&&t.defaultProvider!==void 0&&(o.defaultProvider=t.defaultProvider);for(const i of["anthropic","openai"])for(const c of["ultra","strong","medium","light"])t[i][c]!==n[i][c]&&(o[i]={...o[i]??{},[c]:t[i][c]});return o.defaultProvider!==void 0||o.anthropic!==void 0||o.openai!==void 0?o:void 0}s(Un,"sparseModelLaddersToPersist");function Kn(e,n){return v(e,t=>{const o=T(t),r=T(t);n(r);const i={...t};for(const d of an)d!=="modelLadders"&&!Ne(r[d],o[d])&&(i[d]=r[d]);const c=Un(t.modelLadders,o.modelLadders,r.modelLadders);return c!==void 0?i.modelLadders=c:delete i.modelLadders,{value:r,next:i}})}s(Kn,"mutateConfigAtRoot");function So(e,n){v(e,t=>{const o=t.modelLadders??{},r={...o};n.defaultProvider!==void 0&&(r.defaultProvider=n.defaultProvider);for(const i of["anthropic","openai"])n[i]!==void 0&&(r[i]={...o[i]??{},...n[i]});return{value:void 0,next:{...t,modelLadders:r}}})}s(So,"persistModelLadders");function xo(e,n,t){if(t!==void 0)jn(n,t);else if(n.trim()==="")throw new Error("model route id must be a non-empty string");v(e,o=>{const r=te(o.modelRoutes??{});t===void 0?delete r[n]:k(r,n,{...t,models:{...t.models}});const i={...o};return Object.keys(r).length===0?delete i.modelRoutes:i.modelRoutes=r,{value:void 0,next:i}})}s(xo,"persistModelRoute");function qn(e,n){return Kn(e,n)}s(qn,"updateConfigAtRoot");function Oo(e,n){return qn(_(e),n)}s(Oo,"updateConfig");function Eo(e){const n=e.orchestrator===!0?"orchestratorModel":"model";return v(e.root,t=>{const o={...t.kinds??{},[e.kind]:{...(t.kinds??{})[e.kind],[n]:e.model}};return{value:void 0,next:{...t,kinds:o}}}),{path:h(e.root),field:n}}s(Eo,"persistDefaultKindModel");function Io(e,n){const t=bn(e);return n(t),vn(e,t),t}s(Io,"updateState");export{_n as InvalidRawSettingsError,jo as assertInstalledKind,xn as binDeclarationIssues,lo as configPath,ho as ensureScopeInitialized,En as humanActionDeclarationIssues,bo as invalidPluginBinReasons,Ao as invalidPluginKindsReasons,vo as invalidPluginRequiresReasons,On as normalizeBin,In as normalizeHumanActions,_o as normalizeRequires,wn as normalizeTmuxPassthrough,Eo as persistDefaultKindModel,So as persistModelLadders,xo as persistModelRoute,yn as readConfig,hn as readConfigAtRoot,q as readMergedLaunchConfig,Nn as readRawConfigAtRoot,Fn as readRawScopeConfig,ao as readRawSettingsForTarget,bn as readState,mo as replaceRawSettingsForTarget,wo as resolveKindConfig,ko as resolvePageComponents,fo as statePath,Ro as subKindsAvailableTo,Oo as updateConfig,qn as updateConfigAtRoot,po as updateRawConfigAtomically,Io as updateState,go as writeConfig,yo as writeRawConfig,vn as writeState};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var o=(e,r)=>c(e,"name",{value:r,configurable:!0});import{
|
|
1
|
+
var c=Object.defineProperty;var o=(e,r)=>c(e,"name",{value:r,configurable:!0});import{localClient as u}from"../../../api/node-transport.js";import{ApiError as d}from"../../../api/errors.js";import{envExecutionId as a}from"../../../shared/env.js";function t(){return u({autostart:!1})}o(t,"daemonClient");function n(){const e=a();if(e===void 0||e==="")throw new Error("broker launch is missing CRTR_EXECUTION_ID");return e}o(n,"brokerExecutionId");function f(e,r){return t().bindBrokerSession(e,{...r,executionId:n()})}o(f,"bindBrokerSession");function m(e,r){return t().settleBroker(e,{...r,expected_execution_id:n()})}o(m,"settleBroker");function B(e,r){return t().completeBrokerPark(e,{...r,expected_execution_id:n()})}o(B,"completeBrokerPark");function _(e){return t().recordBrokerParkActivity(e,{expected_execution_id:n()})}o(_,"recordBrokerParkActivity");function s(e,r){return t().claimNodeMail(e,{execution_id:n(),channel:r})}o(s,"claimBrokerMail");function v(e,r){return t().acknowledgeNodeMail(e,{execution_id:n(),deliveries:r})}o(v,"acknowledgeBrokerMail");function E(e,r){return t().recordBrokerTurn(e,{...r,expected_execution_id:n()})}o(E,"recordBrokerTurn");function R(e,r){return t().mutateBrokerProviderRetry(e,{...r,expected_execution_id:n()})}o(R,"mutateBrokerProviderRetry");function h(e,r){return t().mutateBrokerFault(e,{...r,expected_execution_id:n()})}o(h,"mutateBrokerFault");function y(e){return t().getBrokerRecovery(e,n())}o(y,"brokerRecovery");function N(e,r){return t().recordNodeFault(e,r)}o(N,"recordNodeFault");function b(e,r={}){return t().clearNodeFault(e,{...r.link===void 0?{}:{link:r.link},...r.preserveEpisode===void 0?{}:{preserve_episode:r.preserveEpisode}}).then(i=>i.cleared)}o(b,"clearNodeFault");function P(e,r){return t().commitBrokerModel(e,{...r,expected_execution_id:n()})}o(P,"commitBrokerModel");function w(e){return t().brokerExtensionState(e)}o(w,"brokerExtensionState");function F(e,r){return t().commitBrokerGeneratedName(e,{...r,expected_execution_id:n()})}o(F,"commitBrokerGeneratedName");function M(e,r){return t().commitBrokerPersonaAck(e,{...r,expected_execution_id:n()})}o(M,"commitBrokerPersonaAck");async function A(e,r){try{await t().pushReport(e,r)}catch(i){if(r.tier==="final"&&i instanceof d&&i.code==="already_finalized")return;throw i}}o(A,"publishBrokerReport");function I(e){return t().relaunchRoot(e)}o(I,"relaunchBrokerRoot");export{v as acknowledgeBrokerMail,f as bindBrokerSession,n as brokerExecutionId,w as brokerExtensionState,y as brokerRecovery,s as claimBrokerMail,b as clearNodeFault,F as commitBrokerGeneratedName,P as commitBrokerModel,M as commitBrokerPersonaAck,B as completeBrokerPark,h as mutateBrokerFault,R as mutateBrokerProviderRetry,A as publishBrokerReport,_ as recordBrokerParkActivity,E as recordBrokerTurn,N as recordNodeFault,I as relaunchBrokerRoot,m as settleBroker};
|
package/dist/core/secrets.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { Scope } from '../types.js';
|
|
|
8
8
|
* the target name directly, so a rename doesn't orphan the secret. */
|
|
9
9
|
export interface SecretsStore {
|
|
10
10
|
relayTokens: Record<string, string>;
|
|
11
|
+
apiToken?: string;
|
|
11
12
|
}
|
|
12
13
|
export declare function readSecrets(scope: Scope): SecretsStore;
|
|
13
14
|
/** Writes secrets.json directly (never via `fs-utils.writeJson`, which has no
|
|
@@ -18,6 +19,8 @@ export declare function writeSecrets(scope: Scope, store: SecretsStore): void;
|
|
|
18
19
|
export declare function getRelayToken(ref: string, scope: Scope): string | undefined;
|
|
19
20
|
export declare function setRelayToken(ref: string, token: string, scope: Scope): void;
|
|
20
21
|
export declare function deleteRelayToken(ref: string, scope: Scope): void;
|
|
22
|
+
export declare function getApiToken(scope: Scope): string | undefined;
|
|
23
|
+
export declare function setApiToken(token: string, scope: Scope): void;
|
|
21
24
|
/** A short, non-reversible display fingerprint of a raw token — safe to print
|
|
22
25
|
* in `canvas config list`/`show` output. NEVER derive this from anything
|
|
23
26
|
* that could round-trip back to the token, and never log/return the raw
|