@north-light/cli 0.3.418 → 0.3.420

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/api/dto/docs.d.ts +7 -0
  2. package/dist/api/dto/nodes.d.ts +1 -0
  3. package/dist/builtin-memory/crouter-sdk/README.md +3 -3
  4. package/dist/builtin-memory/crouter-sdk/client.md +16 -0
  5. package/dist/builtin-memory/crouter-sdk/getting-started.md +2 -2
  6. package/dist/builtin-memory/crouter-sdk/guides/README.md +1 -1
  7. package/dist/builtin-memory/crouter-sdk/guides/apps-on-crouter-cloud.md +222 -3
  8. package/dist/builtin-memory/crouter-sdk/release-notes.md +10 -0
  9. package/dist/builtin-memory/crouter-sdk/testing.md +23 -1
  10. package/dist/clients/attach/input/controller.d.ts +1 -0
  11. package/dist/clients/attach/input/controller.js +2 -2
  12. package/dist/clients/attach/render/chat-view.d.ts +44 -0
  13. package/dist/clients/attach/render/chat-view.js +3 -3
  14. package/dist/clients/attach/session/frames.d.ts +2 -0
  15. package/dist/clients/attach/session/frames.js +1 -1
  16. package/dist/clients/attach/session/input-wiring.d.ts +3 -0
  17. package/dist/clients/attach/session/input-wiring.js +1 -1
  18. package/dist/clients/attach/slash/dispatch.d.ts +4 -0
  19. package/dist/clients/attach/slash/dispatch.js +2 -2
  20. package/dist/clients/attach/viewer.js +567 -563
  21. package/dist/core/command-plugins/bundle.d.ts +3 -0
  22. package/dist/core/command-plugins/bundle.js +1 -1
  23. package/dist/core/config.js +1 -1
  24. package/dist/core/fs-utils.d.ts +7 -0
  25. package/dist/core/fs-utils.js +3 -3
  26. package/dist/core/graph/package-docs.d.ts +4 -1
  27. package/dist/core/graph/package-docs.js +5 -5
  28. package/dist/core/keybindings/catalog.d.ts +1 -1
  29. package/dist/core/keybindings/catalog.js +1 -1
  30. package/dist/core/manifest.d.ts +15 -0
  31. package/dist/core/manifest.js +1 -1
  32. package/dist/core/plugin-quarantine.d.ts +16 -0
  33. package/dist/core/plugin-quarantine.js +1 -0
  34. package/dist/core/providers/bundles.js +2 -2
  35. package/dist/core/runtime/broker/rebind.d.ts +5 -0
  36. package/dist/core/runtime/broker/rebind.js +1 -1
  37. package/dist/core/runtime/broker/turn-replies.d.ts +33 -0
  38. package/dist/core/runtime/broker/turn-replies.js +1 -0
  39. package/dist/core/runtime/broker-protocol.d.ts +12 -1
  40. package/dist/core/runtime/broker.js +1 -1
  41. package/dist/core/runtime/node-read.js +3 -3
  42. package/dist/core/runtime/tmux-bindings.js +1 -1
  43. package/dist/core/runtime/turn-reply-summary.d.ts +6 -0
  44. package/dist/core/runtime/turn-reply-summary.js +11 -0
  45. package/dist/core/user-settings.d.ts +15 -0
  46. package/dist/core/user-settings.js +1 -1
  47. package/dist/daemon/api/handlers/nodes.js +1 -1
  48. package/dist/daemon/crtrd.js +6 -5
  49. package/dist/shared/turn-replies.d.ts +27 -0
  50. package/dist/shared/turn-replies.js +5 -0
  51. package/dist/types.d.ts +6 -0
  52. package/dist/types.js +1 -1
  53. package/docs/sdk/README.md +3 -3
  54. package/docs/sdk/client.md +16 -0
  55. package/docs/sdk/getting-started.md +2 -2
  56. package/docs/sdk/guides/README.md +1 -1
  57. package/docs/sdk/guides/apps-on-crouter-cloud.md +218 -1
  58. package/docs/sdk/release-notes.md +10 -0
  59. package/docs/sdk/testing.md +23 -1
  60. package/package.json +2 -2
  61. package/packages/crouter-identity/package.json +1 -1
  62. package/runtime.lock.json +12 -12
@@ -103,6 +103,9 @@ export declare function isInstalledGeneration(name: string, scope: Scope, root:
103
103
  export declare function recordCheckedGeneration(scope: Scope, name: string, etag: string | undefined, options: {
104
104
  swapped: boolean;
105
105
  }): void;
106
+ /** Every file lands by temp file plus rename, fsynced, so a crash mid-write leaves the previous bytes or
107
+ * the whole new ones — never a 0-byte `plugin.json` or `commands.json` that crashes the next boot. */
108
+ export declare function writeStagedBundle(stagingRoot: string, manifest: PluginManifest, bundle: ValidatedBundle): void;
106
109
  export interface InstallBundleArchiveOptions<M> {
107
110
  scope: Scope;
108
111
  /** The scope root whose `tmp/` holds staging, `.prev` and the swap lock. */
@@ -1 +1 @@
1
- var J=Object.defineProperty;var p=(e,o)=>J(e,"name",{value:o,configurable:!0});import{Parser as I}from"tar";import{createHash as W}from"node:crypto";import{chmodSync as V,renameSync as w,writeFileSync as k}from"node:fs";import{dirname as z,join as _,resolve as S,sep as B}from"node:path";import{validateCommandManifest as F}from"../../api/command-manifest/manifest.js";import{ensureScopeInitialized as R,invalidPluginKindsReasons as M,readConfig as C,updateConfig as D,updateState as q}from"../config.js";import{general as E}from"../errors.js";import{atomicWriteJson as T,ensureDir as j,nowIso as O,pathExists as $,removePath as x}from"../fs-utils.js";import{invalidPageComponentsReasons as H,normalizePageComponents as U}from"../human/page-catalog.js";import{validateMemoryExtensionDeclarations as K}from"../plugin-extensions.js";import{validateHttpPluginTransport as G}from"./endpoint.js";const N=/^[A-Za-z0-9_-]{1,64}$/,X=new Set(["id","major","groups"]),Z=new Set(["group","consent_line","rate","who","allowlist"]),L=new Set(["own","user","allowlist","any"]),Y=new Set(["bundleVersion","kinds","page_components","memory_extensions","version","transport","provider"]);function Q(e,o,n,r,t,d){return{code:e,message:o,received:n,expected:r,next:t,...d!==void 0?{path:d}:{}}}p(Q,"issue");function a(e,o,n,r,t){return Q("plugin_bundle_invalid",e,o,n,r,t)}p(a,"bundleInvalid");function ee(e){if(e[0]===31&&e[1]===139)return a("bundle archive must be uncompressed tar data","gzip-compressed archive","an uncompressed tar archive","Serve the archive without compression.");if(e.length===0||e.length%512!==0)return a("bundle archive must be a non-empty whole number of 512-byte tar blocks",`${e.length} bytes`,"a non-empty tar stream with a 512-byte block length","Serve one complete uncompressed tar archive.");if(e.length<1024||!e.subarray(-1024).every(o=>o===0))return a("bundle archive is missing the required two zero-block terminator","archive tail is not 1024 zero bytes","a complete tar stream ending in two 512-byte zero blocks","Serve the complete uncompressed tar archive.")}p(ee,"archiveEnvelopeIssue");function ne(e,o){if(e.length===0||e.includes("\0")||e.includes("\\")||e.startsWith("/")||/^[A-Za-z]:($|\/)/.test(e))return a("bundle member path must be a safe relative POSIX path",e,"a relative path without absolute, drive-letter, backslash, or NUL forms","Use a safe relative POSIX member path.",e);if(o==="file"&&e.endsWith("/"))return a("only directory members may have a trailing slash",e,"a regular-file path without a trailing slash","Emit this member as a regular file path.",e);if((o==="directory"&&e.endsWith("/")?e.slice(0,-1):e).split("/").some(r=>r===""||r==="."||r===".."))return a("bundle member path contains an unsafe segment",e,"non-empty segments other than . or ..","Use a safe relative POSIX member path.",e)}p(ne,"validateMemberName");function re(e,o){if(o==="file")return e==="bundle.json"||e==="commands.json"||e==="hooks.json"||e.startsWith("memory/")&&e.endsWith(".md")||e.startsWith("hooks/")?void 0:a("bundle contains an unsupported file member",e,"bundle.json, commands.json, hooks.json, memory/**/*.md, or hooks/**","Remove unsupported members from the archive.",e);if(!(e==="memory"||e.startsWith("memory/")||e==="hooks"||e.startsWith("hooks/")))return a("bundle contains an unsupported directory member",e,"memory or hooks, or a directory below either subtree","Remove unsupported directory members from the archive.",e)}p(re,"validateMemberShape");function oe(e){return e.type==="directory"&&e.path.endsWith("/")?e.path.slice(0,-1):e.path}p(oe,"materializedPath");function se(e){const o=new Map;for(const n of e){const r=oe(n),t=o.get(r);if(t!==void 0)return a(t===n.type?"bundle contains a duplicate member":"bundle claims one path as both a file and a directory",`${r} (${t} and ${n.type})`,"each materialized path exactly once, with one type","Emit each archive path once, as a single member type.",r);o.set(r,n.type)}for(const[n,r]of o)if(r==="file"){for(const t of o.keys())if(t.startsWith(`${n}/`))return a("bundle nests a member beneath a regular-file path",`${t} beneath file ${n}`,"members nested only beneath directory paths","Emit the parent path as a directory or remove the nested member.",t)}}p(se,"validateMemberTree");async function te(e){const o=[],n=new Set;let r;return await new Promise((t,d)=>{const m=new I({strict:!0,maxMetaEntrySize:Number.MAX_SAFE_INTEGER,gzip:!1,brotli:!1,zstd:!1,onReadEntry:p(s=>{const l=s.path,f=s.type==="File"||s.type==="OldFile"?"file":s.type==="Directory"?"directory":void 0;if(f===void 0||s.unsupported||s.ignore||s.linkpath!==void 0){r??=a("bundle member must be a regular file or directory without a link target",`${l} (${s.type})`,"a regular file or directory member without link metadata","Remove links and unsupported member types from the archive.",l),s.resume();return}r??=ne(l,f),r??=re(l,f),n.has(l)&&(r??=a("bundle contains a duplicate member",l,"each member path exactly once","Emit each archive member only once.",l)),n.add(l);const i=[];s.on("data",b=>i.push(b)),s.on("end",()=>{r===void 0&&o.push({path:l,type:f,bytes:new Uint8Array(Buffer.concat(i))})})},"onReadEntry")});m.on("ignoredEntry",s=>{const l=s.path;r??=a("bundle contains an ignored or unsupported archive member",`${l} (${s.type})`,"only supported regular file or directory members","Remove ignored or unsupported members from the archive.",l),s.resume()}),m.once("error",d),m.once("end",t);try{m.end(Buffer.from(e))}catch(s){d(s)}}).catch(t=>{r??=a("bundle archive could not be parsed",t instanceof Error?t.message:String(t),"a valid complete uncompressed tar archive","Fix the archive format and serve it again.")}),r===void 0?{members:o}:{issue:r}}p(te,"parseArchive");function P(e){return typeof e=="number"&&Number.isInteger(e)&&e>=1}p(P,"isPositiveInt");function ie(e){const o=p((s,l,f,i)=>({issue:a(s,l,f,"Fix the provider block in bundle.json.",`bundle.json.${i}`)}),"bad");if(typeof e!="object"||e===null||Array.isArray(e))return o("bundle.json provider must be an object",JSON.stringify(e),"{ id, major, groups }","provider");const n=e,r=Object.keys(n).filter(s=>!X.has(s));if(r.length>0)return o("bundle.json provider has unknown keys",r.join(", "),"only: id, major, groups","provider");if(typeof n.id!="string"||!N.test(n.id))return o("provider id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(n.id),'a provider id of 1-64 letters, digits, "_" or "-"',"provider.id");if(!P(n.major))return o("provider major must be an integer of at least 1",JSON.stringify(n.major),"the served major as an integer >= 1","provider.major");const t=n.groups;if(!Array.isArray(t)||t.length===0)return o("provider groups must be a non-empty array",JSON.stringify(t),"at least one { group, consent_line, rate?, who, allowlist }","provider.groups");const d=[],m=new Set;for(let s=0;s<t.length;s++){const l=`provider.groups[${s}]`,f=t[s];if(typeof f!="object"||f===null||Array.isArray(f))return o("provider group must be an object",JSON.stringify(f),"{ group, consent_line, rate?, who, allowlist }",l);const i=f,b=Object.keys(i).filter(v=>!Z.has(v));if(b.length>0)return o("provider group has unknown keys",b.join(", "),"only: group, consent_line, rate, who, allowlist",l);if(typeof i.group!="string"||!N.test(i.group))return o("provider group id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(i.group),'a group id of 1-64 letters, digits, "_" or "-"',`${l}.group`);if(m.has(i.group))return o("provider group ids must be unique",i.group,"each group declared once",`${l}.group`);if(m.add(i.group),typeof i.consent_line!="string"||i.consent_line.trim().length===0)return o("provider group consent_line must be a non-empty string",JSON.stringify(i.consent_line),"the consent line shown for this group",`${l}.consent_line`);const h=i.rate;if(h!=null&&(typeof h!="number"||!Number.isFinite(h)||h<0))return o("provider group rate must be a non-negative number or null",JSON.stringify(h),"a finite number >= 0, null, or omitted",`${l}.rate`);if(typeof i.who!="string"||!L.has(i.who))return o("provider group who must be own|user|allowlist|any",JSON.stringify(i.who),"own | user | allowlist | any",`${l}.who`);const c=i.allowlist;if(!Array.isArray(c)||!c.every(v=>typeof v=="string"&&v.length>0))return o("provider group allowlist must be an array of non-empty strings",JSON.stringify(c),"an array of app principals ([] unless who is allowlist)",`${l}.allowlist`);if(c.length>0&&i.who!=="allowlist")return o("provider group allowlist must be empty unless who is allowlist",`who ${i.who}`,'who: "allowlist", or allowlist: []',`${l}.allowlist`);d.push({group:i.group,consent_line:i.consent_line,...h!==void 0?{rate:h}:{},who:i.who,allowlist:c})}return{provider:{id:n.id,major:n.major,groups:d}}}p(ie,"parseProviderBlock");function ae(e){const o=[],n=p((r,t)=>{const d=[...t,r.name];if(r.kind==="leaf")o.push({path:d.join(" "),leaf:r});else for(const m of r.children)n(m,d)},"walk");for(const r of e.roots)n(r,[]);for(const r of e.coreMounts)n(r.node,r.parent);return o}p(ae,"collectLeaves");function de(e,o){const n=e.provider;if(n===void 0)return;if(e.transport===void 0)return a("a provider bundle must declare its transport","(missing)",'transport { kind: "http", endpoint } in bundle.json',"Add the provider endpoint as bundle.json transport.","bundle.json.transport");if(e.transport.authEnv!==void 0)return a("a provider bundle must not declare authEnv",e.transport.authEnv,"no authEnv: the runtime presents its own outbound token","Remove transport.authEnv from bundle.json.","bundle.json.transport.authEnv");const r=new Set(n.groups.map(t=>t.group));for(const{path:t,leaf:d}of ae(o)){const m="group"in d?d.group:void 0;if(m===void 0)return a(`provider leaf "${t}" has no group`,t,"a group on every leaf of a provider bundle",`Add a group to leaf "${t}".`,`commands.json:${t}.group`);if(!r.has(m))return a(`provider leaf "${t}" names undeclared group "${m}"`,m,`one of: ${[...r].join(", ")}`,`Declare group "${m}" in the provider block, or move leaf "${t}" to a declared group.`,`commands.json:${t}.group`);if("rest"in d&&d.rest.streaming===!0)return a(`provider leaf "${t}" streams`,"rest.streaming: true","no streaming leaves in a provider bundle",`Make leaf "${t}" return one response.`,`commands.json:${t}.rest.streaming`)}}p(de,"providerBundleIssue");function ue(e){let o;try{o=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issue:a("bundle.json is not valid JSON","invalid JSON",'{"bundleVersion":1}',"Write valid bundle metadata.","bundle.json")}}if(typeof o!="object"||o===null||Array.isArray(o))return{issue:a("bundle.json must be a JSON object",JSON.stringify(o),"an object with bundleVersion set to 1 and optionally kinds",'Set bundle.json to {"bundleVersion":1}.',"bundle.json")};const n=o,r=Object.keys(n).filter(s=>!Y.has(s));if(n.bundleVersion!==1||r.length>0)return{issue:a("bundle.json must contain bundleVersion 1 and at most version, transport, provider, kinds, page_components, and memory_extensions",JSON.stringify(o),"an object with bundleVersion set to 1 and optional version, transport, provider, kinds, page_components, and memory_extensions",'Set bundle.json to {"bundleVersion":1} plus optional contribution blocks.',"bundle.json")};if(n.kinds!==void 0){const s=M(n.kinds);if(s.length>0)return{issue:a("bundle.json kinds block is invalid",s.join("; "),"each entry a full KindConfig (whenToUse required) or a sparse patch of launch fields","Fix the kinds entries in bundle.json.","bundle.json")}}if(n.page_components!==void 0){const s=H(n.page_components);if(s.length>0)return{issue:a("bundle.json page_components block is invalid",s.join("; "),"an array of page component registrations, each a kind string or {kind, description?, useWhen?, doc?, display?}","Fix the page_components entries in bundle.json.","bundle.json")}}let t;if(n.memory_extensions!==void 0)try{t=K(n.memory_extensions)}catch(s){return{issue:a("bundle.json memory_extensions block is invalid",s instanceof Error?s.message:String(s),"a valid memory_extensions declaration object","Fix the memory_extensions declarations in bundle.json.","bundle.json")}}if(n.version!==void 0&&!P(n.version))return{issue:a("bundle.json version must be an integer of at least 1",JSON.stringify(n.version),"the plugin major as an integer >= 1, or omitted","Fix version in bundle.json.","bundle.json.version")};let d;if(n.transport!==void 0){const s=G(n.transport);if(s.transport===void 0)return{issue:a("bundle.json transport is invalid",s.errors.join("; "),'{ kind: "http", endpoint, authEnv? }',"Fix transport in bundle.json.","bundle.json.transport")};d=s.transport}let m;if(n.provider!==void 0){const s=ie(n.provider);if(s.issue!==void 0)return{issue:s.issue};if(m=s.provider,n.version!==void 0&&n.version!==m.major)return{issue:a("bundle.json provider major must equal version",`version ${String(n.version)}, major ${m.major}`,"one major","Set provider.major and version to the same integer.","bundle.json.provider.major")}}return{...n.version!==void 0?{version:n.version}:{},...d!==void 0?{transport:d}:{},...m!==void 0?{provider:m}:{},...n.kinds!==void 0?{kinds:n.kinds}:{},...n.page_components!==void 0?{page_components:U(n.page_components)}:{},...t!==void 0?{memory_extensions:t}:{}}}p(ue,"parseBundleMetadata");function le(e,o,n,r=new Set){let t;try{t=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issues:[a("commands.json is not valid JSON","invalid JSON","a valid command-manifest JSON object","Regenerate commands.json.","commands.json")]}}const d=F(t,{transport:"http",reservedCoreNames:o,extensibleCoreBranches:r,...n!==void 0?{coreCommandPaths:n}:{}});return{...d.manifest!==void 0?{commands:d.manifest}:{},issues:d.issues}}p(le,"parseCommands");async function me(e,o){const n=ee(e);if(n!==void 0)return{issues:[n]};const r=await te(e);if(r.issue!==void 0||r.members===void 0)return{issues:[r.issue]};const t=se(r.members);if(t!==void 0)return{issues:[t]};const d=r.members.find(u=>u.path==="bundle.json"&&u.type==="file"),m=r.members.find(u=>u.path==="commands.json"&&u.type==="file");if(d===void 0||m===void 0)return{issues:[a("bundle must contain exactly one bundle.json and one commands.json regular file",`bundle.json=${d!==void 0}, commands.json=${m!==void 0}`,"one regular bundle.json and one regular commands.json member","Add the required metadata members to the archive.")]};const s=r.members.find(u=>u.path==="hooks.json"&&u.type==="file"),l=r.members.filter(u=>u.type==="file"&&u.path.startsWith("hooks/"));if((s!==void 0||r.members.some(u=>u.path==="hooks"||u.path.startsWith("hooks/")))&&(s===void 0||!l.some(u=>u.path==="hooks/dispatch")))return{issues:[a("bundle hook payload must contain both hooks.json and hooks/dispatch regular files",`hooks.json=${s!==void 0}, hooks/dispatch=${l.some(u=>u.path==="hooks/dispatch")}`,"both hooks.json and hooks/dispatch regular files","Add both required hook members or remove the hook payload.")]};const i=ue(d.bytes);if(i.issue!==void 0)return{issues:[i.issue]};const b=le(m.bytes,o.reservedCoreNames,o.coreCommandPaths,o.extensibleCoreBranches);if(b.commands===void 0)return{issues:b.issues};const h=b.commands.version;if(i.version!==void 0&&h!==void 0&&i.version!==h)return{issues:[a("bundle.json version and commands.json version differ",`bundle.json ${i.version}, commands.json ${h}`,"one plugin major","Emit the same version in bundle.json and commands.json.","bundle.json.version")]};const c=i.version??h;if(i.provider!==void 0&&c===void 0)return{issues:[a("a provider bundle requires a plugin version","no version in bundle.json or commands.json",`version ${i.provider.major}, the provider major`,"Emit the provider major as the plugin version.","bundle.json.version")]};if(i.provider!==void 0&&c!==i.provider.major)return{issues:[a("bundle provider major must equal the plugin version",`version ${c}, major ${i.provider.major}`,"one major","Emit the provider major as the plugin version.","bundle.json.provider.major")]};const v=de(i,b.commands);return v!==void 0?{issues:[v]}:{bundle:{bundleVersion:1,...c!==void 0?{version:c}:{},...i.transport!==void 0?{transport:i.transport}:{},...i.provider!==void 0?{provider:i.provider}:{},...i.kinds!==void 0?{kinds:i.kinds}:{},...i.page_components!==void 0?{page_components:i.page_components}:{},...i.memory_extensions!==void 0?{memory_extensions:i.memory_extensions}:{},commands:b.commands,commandsBytes:m.bytes,directories:r.members.filter(u=>u.type==="directory").map(u=>({path:u.path})),memory:r.members.filter(u=>u.type==="file"&&u.path.startsWith("memory/")).map(u=>({path:u.path,bytes:u.bytes})),...s!==void 0?{hooks:{manifest:s.bytes,files:l.map(u=>({path:u.path,bytes:u.bytes}))}}:{}},issues:[]}}p(me,"validatePluginBundle");function pe(e){return W("sha256").update(e).digest("hex").slice(0,12)}p(pe,"bundleVersion");function Ee(e,o,n,r){return C(o).plugins[e]?.version!==r?!1:$(_(n,"commands.json"))}p(Ee,"isInstalledGeneration");function ce(e,o,n,r){q(e,t=>{t.plugins[o]===void 0&&(t.plugins[o]={}),r.swapped&&(t.plugins[o].last_updated=O()),t.plugins[o].checked_at=O(),n===void 0?delete t.plugins[o].etag:t.plugins[o].etag=n})}p(ce,"recordCheckedGeneration");function g(e,o){const n=S(e,o);if(n===e||!n.startsWith(e+B))throw E(`bundle member path escapes the staging directory: ${o}`);return n}p(g,"stagedPath");function fe(e,o,n){T(g(e,".crouter-plugin/plugin.json"),o);for(const r of n.directories)j(g(e,r.path));k(g(e,"commands.json"),n.commandsBytes);for(const r of n.memory){const t=g(e,r.path);j(S(t,"..")),k(t,r.bytes)}if(n.hooks!==void 0){k(g(e,"hooks.json"),n.hooks.manifest);for(const r of n.hooks.files){const t=g(e,r.path);j(S(t,"..")),k(t,r.bytes)}V(g(e,"hooks/dispatch"),493)}}p(fe,"writeStagedBundle");async function Oe(e,o,n){const{scope:r,scopeRootPath:t,root:d}=n,m=await me(o,n.validation);if(m.bundle===void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,m.issues);const s=m.bundle,l=n.accept?.(s);if(l!==void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,[a(l,e,"the bundle this install asked for","Serve the requested bundle.")]);const f=n.version??pe(o),i=s.provider!==void 0&&s.transport!==void 0?s.transport:n.transport,b={name:e,version:f,commands:"commands.json",transport:i,bundle:n.bundle,...s.kinds!==void 0?{kinds:s.kinds}:{},...s.page_components!==void 0?{page_components:s.page_components}:{},...s.memory_extensions!==void 0?{memory_extensions:s.memory_extensions}:{},...s.hooks!==void 0?{hooks:"hooks.json",hookExecutable:"hooks/dispatch"}:{},...s.provider!==void 0?{provider:s.provider}:{}},h=_(t,"tmp"),c=_(h,`${e}.${process.pid}`),v=`${c}.prev`;if($(v))throw E(`cannot replace plugin "${e}": prior interrupted replacement remains at ${v}`);j(h),j(z(d)),x(c);let u;try{j(c),fe(c,b,s),u=await n.validateStaged(c,b);let y=!1;$(d)&&(w(d,v),y=!0);try{w(c,d)}catch(A){throw y&&w(v,d),A}y&&x(v)}finally{x(c)}return R(r,t),D(r,y=>{y.plugins[e]={enabled:n.enable?!0:y.plugins[e]?.enabled??!0,version:f}}),ce(r,e,n.etag,{swapped:!0}),{manifest:b,validated:s,version:f,root:d,staged:u}}p(Oe,"installBundleArchive");export{pe as bundleVersion,Oe as installBundleArchive,Ee as isInstalledGeneration,ce as recordCheckedGeneration,me as validatePluginBundle};
1
+ var A=Object.defineProperty;var p=(e,r)=>A(e,"name",{value:r,configurable:!0});import{Parser as J}from"tar";import{createHash as I}from"node:crypto";import{chmodSync as W,renameSync as k}from"node:fs";import{dirname as $,join as w,resolve as V,sep as z}from"node:path";import{validateCommandManifest as B}from"../../api/command-manifest/manifest.js";import{ensureScopeInitialized as F,invalidPluginKindsReasons as M,readConfig as R,updateConfig as D,updateState as C}from"../config.js";import{general as x}from"../errors.js";import{atomicWriteJson as T,atomicWriteText as q,ensureDir as j,nowIso as E,pathExists as _,removePath as S,syncDirectory as H,syncTree as U}from"../fs-utils.js";import{invalidPageComponentsReasons as K,normalizePageComponents as G}from"../human/page-catalog.js";import{validateMemoryExtensionDeclarations as X}from"../plugin-extensions.js";import{validateHttpPluginTransport as Z}from"./endpoint.js";const O=/^[A-Za-z0-9_-]{1,64}$/,L=new Set(["id","major","groups"]),Y=new Set(["group","consent_line","rate","who","allowlist"]),Q=new Set(["own","user","allowlist","any"]),ee=new Set(["bundleVersion","kinds","page_components","memory_extensions","version","transport","provider"]);function ne(e,r,n,o,s,a){return{code:e,message:r,received:n,expected:o,next:s,...a!==void 0?{path:a}:{}}}p(ne,"issue");function d(e,r,n,o,s){return ne("plugin_bundle_invalid",e,r,n,o,s)}p(d,"bundleInvalid");function re(e){if(e[0]===31&&e[1]===139)return d("bundle archive must be uncompressed tar data","gzip-compressed archive","an uncompressed tar archive","Serve the archive without compression.");if(e.length===0||e.length%512!==0)return d("bundle archive must be a non-empty whole number of 512-byte tar blocks",`${e.length} bytes`,"a non-empty tar stream with a 512-byte block length","Serve one complete uncompressed tar archive.");if(e.length<1024||!e.subarray(-1024).every(r=>r===0))return d("bundle archive is missing the required two zero-block terminator","archive tail is not 1024 zero bytes","a complete tar stream ending in two 512-byte zero blocks","Serve the complete uncompressed tar archive.")}p(re,"archiveEnvelopeIssue");function oe(e,r){if(e.length===0||e.includes("\0")||e.includes("\\")||e.startsWith("/")||/^[A-Za-z]:($|\/)/.test(e))return d("bundle member path must be a safe relative POSIX path",e,"a relative path without absolute, drive-letter, backslash, or NUL forms","Use a safe relative POSIX member path.",e);if(r==="file"&&e.endsWith("/"))return d("only directory members may have a trailing slash",e,"a regular-file path without a trailing slash","Emit this member as a regular file path.",e);if((r==="directory"&&e.endsWith("/")?e.slice(0,-1):e).split("/").some(o=>o===""||o==="."||o===".."))return d("bundle member path contains an unsafe segment",e,"non-empty segments other than . or ..","Use a safe relative POSIX member path.",e)}p(oe,"validateMemberName");function se(e,r){if(r==="file")return e==="bundle.json"||e==="commands.json"||e==="hooks.json"||e.startsWith("memory/")&&e.endsWith(".md")||e.startsWith("hooks/")?void 0:d("bundle contains an unsupported file member",e,"bundle.json, commands.json, hooks.json, memory/**/*.md, or hooks/**","Remove unsupported members from the archive.",e);if(!(e==="memory"||e.startsWith("memory/")||e==="hooks"||e.startsWith("hooks/")))return d("bundle contains an unsupported directory member",e,"memory or hooks, or a directory below either subtree","Remove unsupported directory members from the archive.",e)}p(se,"validateMemberShape");function te(e){return e.type==="directory"&&e.path.endsWith("/")?e.path.slice(0,-1):e.path}p(te,"materializedPath");function ie(e){const r=new Map;for(const n of e){const o=te(n),s=r.get(o);if(s!==void 0)return d(s===n.type?"bundle contains a duplicate member":"bundle claims one path as both a file and a directory",`${o} (${s} and ${n.type})`,"each materialized path exactly once, with one type","Emit each archive path once, as a single member type.",o);r.set(o,n.type)}for(const[n,o]of r)if(o==="file"){for(const s of r.keys())if(s.startsWith(`${n}/`))return d("bundle nests a member beneath a regular-file path",`${s} beneath file ${n}`,"members nested only beneath directory paths","Emit the parent path as a directory or remove the nested member.",s)}}p(ie,"validateMemberTree");async function ae(e){const r=[],n=new Set;let o;return await new Promise((s,a)=>{const l=new J({strict:!0,maxMetaEntrySize:Number.MAX_SAFE_INTEGER,gzip:!1,brotli:!1,zstd:!1,onReadEntry:p(t=>{const m=t.path,f=t.type==="File"||t.type==="OldFile"?"file":t.type==="Directory"?"directory":void 0;if(f===void 0||t.unsupported||t.ignore||t.linkpath!==void 0){o??=d("bundle member must be a regular file or directory without a link target",`${m} (${t.type})`,"a regular file or directory member without link metadata","Remove links and unsupported member types from the archive.",m),t.resume();return}o??=oe(m,f),o??=se(m,f),n.has(m)&&(o??=d("bundle contains a duplicate member",m,"each member path exactly once","Emit each archive member only once.",m)),n.add(m);const i=[];t.on("data",b=>i.push(b)),t.on("end",()=>{o===void 0&&r.push({path:m,type:f,bytes:new Uint8Array(Buffer.concat(i))})})},"onReadEntry")});l.on("ignoredEntry",t=>{const m=t.path;o??=d("bundle contains an ignored or unsupported archive member",`${m} (${t.type})`,"only supported regular file or directory members","Remove ignored or unsupported members from the archive.",m),t.resume()}),l.once("error",a),l.once("end",s);try{l.end(Buffer.from(e))}catch(t){a(t)}}).catch(s=>{o??=d("bundle archive could not be parsed",s instanceof Error?s.message:String(s),"a valid complete uncompressed tar archive","Fix the archive format and serve it again.")}),o===void 0?{members:r}:{issue:o}}p(ae,"parseArchive");function N(e){return typeof e=="number"&&Number.isInteger(e)&&e>=1}p(N,"isPositiveInt");function de(e){const r=p((t,m,f,i)=>({issue:d(t,m,f,"Fix the provider block in bundle.json.",`bundle.json.${i}`)}),"bad");if(typeof e!="object"||e===null||Array.isArray(e))return r("bundle.json provider must be an object",JSON.stringify(e),"{ id, major, groups }","provider");const n=e,o=Object.keys(n).filter(t=>!L.has(t));if(o.length>0)return r("bundle.json provider has unknown keys",o.join(", "),"only: id, major, groups","provider");if(typeof n.id!="string"||!O.test(n.id))return r("provider id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(n.id),'a provider id of 1-64 letters, digits, "_" or "-"',"provider.id");if(!N(n.major))return r("provider major must be an integer of at least 1",JSON.stringify(n.major),"the served major as an integer >= 1","provider.major");const s=n.groups;if(!Array.isArray(s)||s.length===0)return r("provider groups must be a non-empty array",JSON.stringify(s),"at least one { group, consent_line, rate?, who, allowlist }","provider.groups");const a=[],l=new Set;for(let t=0;t<s.length;t++){const m=`provider.groups[${t}]`,f=s[t];if(typeof f!="object"||f===null||Array.isArray(f))return r("provider group must be an object",JSON.stringify(f),"{ group, consent_line, rate?, who, allowlist }",m);const i=f,b=Object.keys(i).filter(v=>!Y.has(v));if(b.length>0)return r("provider group has unknown keys",b.join(", "),"only: group, consent_line, rate, who, allowlist",m);if(typeof i.group!="string"||!O.test(i.group))return r("provider group id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(i.group),'a group id of 1-64 letters, digits, "_" or "-"',`${m}.group`);if(l.has(i.group))return r("provider group ids must be unique",i.group,"each group declared once",`${m}.group`);if(l.add(i.group),typeof i.consent_line!="string"||i.consent_line.trim().length===0)return r("provider group consent_line must be a non-empty string",JSON.stringify(i.consent_line),"the consent line shown for this group",`${m}.consent_line`);const h=i.rate;if(h!=null&&(typeof h!="number"||!Number.isFinite(h)||h<0))return r("provider group rate must be a non-negative number or null",JSON.stringify(h),"a finite number >= 0, null, or omitted",`${m}.rate`);if(typeof i.who!="string"||!Q.has(i.who))return r("provider group who must be own|user|allowlist|any",JSON.stringify(i.who),"own | user | allowlist | any",`${m}.who`);const c=i.allowlist;if(!Array.isArray(c)||!c.every(v=>typeof v=="string"&&v.length>0))return r("provider group allowlist must be an array of non-empty strings",JSON.stringify(c),"an array of app principals ([] unless who is allowlist)",`${m}.allowlist`);if(c.length>0&&i.who!=="allowlist")return r("provider group allowlist must be empty unless who is allowlist",`who ${i.who}`,'who: "allowlist", or allowlist: []',`${m}.allowlist`);a.push({group:i.group,consent_line:i.consent_line,...h!==void 0?{rate:h}:{},who:i.who,allowlist:c})}return{provider:{id:n.id,major:n.major,groups:a}}}p(de,"parseProviderBlock");function ue(e){const r=[],n=p((o,s)=>{const a=[...s,o.name];if(o.kind==="leaf")r.push({path:a.join(" "),leaf:o});else for(const l of o.children)n(l,a)},"walk");for(const o of e.roots)n(o,[]);for(const o of e.coreMounts)n(o.node,o.parent);return r}p(ue,"collectLeaves");function le(e,r){const n=e.provider;if(n===void 0)return;if(e.transport===void 0)return d("a provider bundle must declare its transport","(missing)",'transport { kind: "http", endpoint } in bundle.json',"Add the provider endpoint as bundle.json transport.","bundle.json.transport");if(e.transport.authEnv!==void 0)return d("a provider bundle must not declare authEnv",e.transport.authEnv,"no authEnv: the runtime presents its own outbound token","Remove transport.authEnv from bundle.json.","bundle.json.transport.authEnv");const o=new Set(n.groups.map(s=>s.group));for(const{path:s,leaf:a}of ue(r)){const l="group"in a?a.group:void 0;if(l===void 0)return d(`provider leaf "${s}" has no group`,s,"a group on every leaf of a provider bundle",`Add a group to leaf "${s}".`,`commands.json:${s}.group`);if(!o.has(l))return d(`provider leaf "${s}" names undeclared group "${l}"`,l,`one of: ${[...o].join(", ")}`,`Declare group "${l}" in the provider block, or move leaf "${s}" to a declared group.`,`commands.json:${s}.group`);if("rest"in a&&a.rest.streaming===!0)return d(`provider leaf "${s}" streams`,"rest.streaming: true","no streaming leaves in a provider bundle",`Make leaf "${s}" return one response.`,`commands.json:${s}.rest.streaming`)}}p(le,"providerBundleIssue");function me(e){let r;try{r=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issue:d("bundle.json is not valid JSON","invalid JSON",'{"bundleVersion":1}',"Write valid bundle metadata.","bundle.json")}}if(typeof r!="object"||r===null||Array.isArray(r))return{issue:d("bundle.json must be a JSON object",JSON.stringify(r),"an object with bundleVersion set to 1 and optionally kinds",'Set bundle.json to {"bundleVersion":1}.',"bundle.json")};const n=r,o=Object.keys(n).filter(t=>!ee.has(t));if(n.bundleVersion!==1||o.length>0)return{issue:d("bundle.json must contain bundleVersion 1 and at most version, transport, provider, kinds, page_components, and memory_extensions",JSON.stringify(r),"an object with bundleVersion set to 1 and optional version, transport, provider, kinds, page_components, and memory_extensions",'Set bundle.json to {"bundleVersion":1} plus optional contribution blocks.',"bundle.json")};if(n.kinds!==void 0){const t=M(n.kinds);if(t.length>0)return{issue:d("bundle.json kinds block is invalid",t.join("; "),"each entry a full KindConfig (whenToUse required) or a sparse patch of launch fields","Fix the kinds entries in bundle.json.","bundle.json")}}if(n.page_components!==void 0){const t=K(n.page_components);if(t.length>0)return{issue:d("bundle.json page_components block is invalid",t.join("; "),"an array of page component registrations, each a kind string or {kind, description?, useWhen?, doc?, display?}","Fix the page_components entries in bundle.json.","bundle.json")}}let s;if(n.memory_extensions!==void 0)try{s=X(n.memory_extensions)}catch(t){return{issue:d("bundle.json memory_extensions block is invalid",t instanceof Error?t.message:String(t),"a valid memory_extensions declaration object","Fix the memory_extensions declarations in bundle.json.","bundle.json")}}if(n.version!==void 0&&!N(n.version))return{issue:d("bundle.json version must be an integer of at least 1",JSON.stringify(n.version),"the plugin major as an integer >= 1, or omitted","Fix version in bundle.json.","bundle.json.version")};let a;if(n.transport!==void 0){const t=Z(n.transport);if(t.transport===void 0)return{issue:d("bundle.json transport is invalid",t.errors.join("; "),'{ kind: "http", endpoint, authEnv? }',"Fix transport in bundle.json.","bundle.json.transport")};a=t.transport}let l;if(n.provider!==void 0){const t=de(n.provider);if(t.issue!==void 0)return{issue:t.issue};if(l=t.provider,n.version!==void 0&&n.version!==l.major)return{issue:d("bundle.json provider major must equal version",`version ${String(n.version)}, major ${l.major}`,"one major","Set provider.major and version to the same integer.","bundle.json.provider.major")}}return{...n.version!==void 0?{version:n.version}:{},...a!==void 0?{transport:a}:{},...l!==void 0?{provider:l}:{},...n.kinds!==void 0?{kinds:n.kinds}:{},...n.page_components!==void 0?{page_components:G(n.page_components)}:{},...s!==void 0?{memory_extensions:s}:{}}}p(me,"parseBundleMetadata");function pe(e,r,n,o=new Set){let s;try{s=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issues:[d("commands.json is not valid JSON","invalid JSON","a valid command-manifest JSON object","Regenerate commands.json.","commands.json")]}}const a=B(s,{transport:"http",reservedCoreNames:r,extensibleCoreBranches:o,...n!==void 0?{coreCommandPaths:n}:{}});return{...a.manifest!==void 0?{commands:a.manifest}:{},issues:a.issues}}p(pe,"parseCommands");async function ce(e,r){const n=re(e);if(n!==void 0)return{issues:[n]};const o=await ae(e);if(o.issue!==void 0||o.members===void 0)return{issues:[o.issue]};const s=ie(o.members);if(s!==void 0)return{issues:[s]};const a=o.members.find(u=>u.path==="bundle.json"&&u.type==="file"),l=o.members.find(u=>u.path==="commands.json"&&u.type==="file");if(a===void 0||l===void 0)return{issues:[d("bundle must contain exactly one bundle.json and one commands.json regular file",`bundle.json=${a!==void 0}, commands.json=${l!==void 0}`,"one regular bundle.json and one regular commands.json member","Add the required metadata members to the archive.")]};const t=o.members.find(u=>u.path==="hooks.json"&&u.type==="file"),m=o.members.filter(u=>u.type==="file"&&u.path.startsWith("hooks/"));if((t!==void 0||o.members.some(u=>u.path==="hooks"||u.path.startsWith("hooks/")))&&(t===void 0||!m.some(u=>u.path==="hooks/dispatch")))return{issues:[d("bundle hook payload must contain both hooks.json and hooks/dispatch regular files",`hooks.json=${t!==void 0}, hooks/dispatch=${m.some(u=>u.path==="hooks/dispatch")}`,"both hooks.json and hooks/dispatch regular files","Add both required hook members or remove the hook payload.")]};const i=me(a.bytes);if(i.issue!==void 0)return{issues:[i.issue]};const b=pe(l.bytes,r.reservedCoreNames,r.coreCommandPaths,r.extensibleCoreBranches);if(b.commands===void 0)return{issues:b.issues};const h=b.commands.version;if(i.version!==void 0&&h!==void 0&&i.version!==h)return{issues:[d("bundle.json version and commands.json version differ",`bundle.json ${i.version}, commands.json ${h}`,"one plugin major","Emit the same version in bundle.json and commands.json.","bundle.json.version")]};const c=i.version??h;if(i.provider!==void 0&&c===void 0)return{issues:[d("a provider bundle requires a plugin version","no version in bundle.json or commands.json",`version ${i.provider.major}, the provider major`,"Emit the provider major as the plugin version.","bundle.json.version")]};if(i.provider!==void 0&&c!==i.provider.major)return{issues:[d("bundle provider major must equal the plugin version",`version ${c}, major ${i.provider.major}`,"one major","Emit the provider major as the plugin version.","bundle.json.provider.major")]};const v=le(i,b.commands);return v!==void 0?{issues:[v]}:{bundle:{bundleVersion:1,...c!==void 0?{version:c}:{},...i.transport!==void 0?{transport:i.transport}:{},...i.provider!==void 0?{provider:i.provider}:{},...i.kinds!==void 0?{kinds:i.kinds}:{},...i.page_components!==void 0?{page_components:i.page_components}:{},...i.memory_extensions!==void 0?{memory_extensions:i.memory_extensions}:{},commands:b.commands,commandsBytes:l.bytes,directories:o.members.filter(u=>u.type==="directory").map(u=>({path:u.path})),memory:o.members.filter(u=>u.type==="file"&&u.path.startsWith("memory/")).map(u=>({path:u.path,bytes:u.bytes})),...t!==void 0?{hooks:{manifest:t.bytes,files:m.map(u=>({path:u.path,bytes:u.bytes}))}}:{}},issues:[]}}p(ce,"validatePluginBundle");function fe(e){return I("sha256").update(e).digest("hex").slice(0,12)}p(fe,"bundleVersion");function Ne(e,r,n,o){return R(r).plugins[e]?.version!==o?!1:_(w(n,"commands.json"))}p(Ne,"isInstalledGeneration");function he(e,r,n,o){C(e,s=>{s.plugins[r]===void 0&&(s.plugins[r]={}),o.swapped&&(s.plugins[r].last_updated=E()),s.plugins[r].checked_at=E(),n===void 0?delete s.plugins[r].etag:s.plugins[r].etag=n})}p(he,"recordCheckedGeneration");function g(e,r){const n=V(e,r);if(n===e||!n.startsWith(e+z))throw x(`bundle member path escapes the staging directory: ${r}`);return n}p(g,"stagedPath");function be(e,r,n){const o=p((s,a,l=420)=>q(s,a,{mode:l,durable:!0}),"write");T(g(e,".crouter-plugin/plugin.json"),r,{durable:!0});for(const s of n.directories)j(g(e,s.path));o(g(e,"commands.json"),n.commandsBytes);for(const s of n.memory)o(g(e,s.path),s.bytes);if(n.hooks!==void 0){o(g(e,"hooks.json"),n.hooks.manifest);for(const s of n.hooks.files)o(g(e,s.path),s.bytes);W(g(e,"hooks/dispatch"),493)}U(e)}p(be,"writeStagedBundle");async function Pe(e,r,n){const{scope:o,scopeRootPath:s,root:a}=n,l=await ce(r,n.validation);if(l.bundle===void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,l.issues);const t=l.bundle,m=n.accept?.(t);if(m!==void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,[d(m,e,"the bundle this install asked for","Serve the requested bundle.")]);const f=n.version??fe(r),i=t.provider!==void 0&&t.transport!==void 0?t.transport:n.transport,b={name:e,version:f,commands:"commands.json",transport:i,bundle:n.bundle,...t.kinds!==void 0?{kinds:t.kinds}:{},...t.page_components!==void 0?{page_components:t.page_components}:{},...t.memory_extensions!==void 0?{memory_extensions:t.memory_extensions}:{},...t.hooks!==void 0?{hooks:"hooks.json",hookExecutable:"hooks/dispatch"}:{},...t.provider!==void 0?{provider:t.provider}:{}},h=w(s,"tmp"),c=w(h,`${e}.${process.pid}`),v=`${c}.prev`;if(_(v))throw x(`cannot replace plugin "${e}": prior interrupted replacement remains at ${v}`);j(h),j($(a)),S(c);let u;try{j(c),be(c,b,t),u=await n.validateStaged(c,b);let y=!1;_(a)&&(k(a,v),y=!0);try{k(c,a)}catch(P){throw y&&k(v,a),P}H($(a)),y&&S(v)}finally{S(c)}return F(o,s),D(o,y=>{y.plugins[e]={enabled:n.enable?!0:y.plugins[e]?.enabled??!0,version:f}}),he(o,e,n.etag,{swapped:!0}),{manifest:b,validated:t,version:f,root:a,staged:u}}p(Pe,"installBundleArchive");export{fe as bundleVersion,Pe as installBundleArchive,Ne as isInstalledGeneration,he as recordCheckedGeneration,ce as validatePluginBundle,be as writeStagedBundle};
@@ -1 +1 @@
1
- var De=Object.defineProperty;var s=(e,n)=>De(e,"name",{value:n,configurable:!0});import{envProfileId as Ue}from"../shared/env.js";import{basename as X,join as Q}from"node:path";import{existsSync as Ke,mkdirSync as qe,statSync as Ge}from"node:fs";import{isDeepStrictEqual as Be}from"node:util";import{CONFIG_FILE as We,STATE_FILE as He,CONDENSED_HISTORY_MODES as Je,MOUSE_MODE_DEFAULTS as Ze,WHIP_MESSAGE_MODES as Ye,DEFAULT_WHIP_MESSAGES as ee,defaultScopeConfig as k,defaultScopeState as ne,defaultModelLaddersConfig as Ve,defaultKindsConfig as Xe,defaultRemoteCanvasConfig as Qe}from"../types.js";import{BINDING_CATALOG as en,BINDING_IDS as nn,isAttachPaneBinding as on}from"./keybindings/catalog.js";import{notFound as tn}from"./errors.js";import{emitEvent as rn}from"./events/emit.js";import{atomicWriteJson as oe,readJsonIfExists as K,ensureDir as sn}from"./fs-utils.js";import{scopeRoot as y,requireScopeRoot as w,findProjectScopeRoots as cn,userScopeRoot as E}from"./scope.js";import{listInstalledPluginsInRoot as te}from"./installed-plugins.js";import{profileMemoryDir as q,listProfiles as re}from"./profiles/manifest.js";import{canonicalRoot as A,projectStore as un,userStore as ie}from"./scoped-state/paths.js";import{convertLegacyScopedStore as dn,initializeFreshScopedStore as fn}from"./scoped-state/migrate.js";import{mutateRawSettings as ln,readBookkeeping as an,readRawSettings as mn,readRawSettingsWithRevision as pn,replaceRawSettings as gn,writeBookkeeping as yn,writeRawSettings as hn}from"./scoped-state/settings.js";import{withExclusiveDirectoryLock as bn}from"./exclusive-lock.js";import{SCOPE_CONFIG_KEYS as _n}from"./user-settings.js";import{normalizeWorkingGerunds as vn}from"../shared/working-activity.js";import{mergePageComponentLayers as kn,normalizePageComponents as se}from"./human/page-catalog.js";function b(e){return Q(e,We)}s(b,"configPathFor");function ce(e,n){return bn(`${e}.lock`,()=>n(e),{timeoutError:s(()=>new Error(`timed out waiting for config lock: ${e}`),"timeoutError")})}s(ce,"withConfigPathLock");function wn(e){return Q(e,He)}s(wn,"statePathFor");function bo(e){const n=y(e);return n?b(n):null}s(bo,"configPath");function _o(e){const n=y(e);return n?wn(n):null}s(_o,"statePath");function g(e){const n=A(e),o=A(E());if(n===o)return{store:ie(o),key:"user",kind:"user",root:o};if(X(n)===".crouter")return dn({kind:"project",root:n}),{store:un(n),key:"project",kind:"project",root:n};for(const t of re()){const r=A(q(t.profileId));if(n===r)return{store:ie(o),key:`profile:${t.profileId}`,kind:"profile",root:r,profileId:t.profileId}}throw new Error(`settings root is not a recognized owner: ${e}`)}s(g,"settingsOwnerForRoot");function I(e){if(X(e)===".crouter"){let n;try{n=A(e)}catch(o){if(o.code==="ENOENT")return null;throw o}if(n!==A(E()))return K(b(e))}return mn(g(e))}s(I,"readRawAtRoot");function G(e,n){const o=g(e);if(o.kind==="project"){ce(b(o.root),t=>oe(t,n,{createParent:!1}));return}hn(o,n)}s(G,"writeRawAtRoot");function S(e,n){const o=g(e);return o.kind==="project"?ce(b(o.root),t=>{const r=n(K(t)??{});return oe(t,r.next,{createParent:!1}),r.value}):ln(o,t=>n(t))}s(S,"mutateRawAtRoot");const P=globalThis,T=Symbol.for("crtr.broker.config-snapshot");function vo(e){P[T]=e}s(vo,"installBrokerConfigSnapshot");function ko(){return P[T]}s(ko,"brokerConfigSnapshot");function An(e){const n=P[T];if(n){if(e!=="user")throw new Error(`broker config snapshot has no ${e} scope`);return n.user}const o=y(e);return o?Sn(o):k()}s(An,"readConfig");function Sn(e){const n=I(e);return n?z(n):k()}s(Sn,"readConfigAtRoot");function jn(e){const n=y(e);if(!n)return ne();try{return an(g(n))}catch(o){if(e==="project"&&!Ke(n))return ne();throw o}}s(jn,"readState");class Rn extends Error{static{s(this,"InvalidRawSettingsError")}constructor(n){super(`settings cannot be merged: ${n instanceof Error?n.message:String(n)}`),this.name="InvalidRawSettingsError"}}function ue(e){if(e.scope==="user")return g(E());if(!re().some(n=>n.profileId===e.profileId))throw tn(`profile not found: ${e.profileId}`,{received:e.profileId,next:"Run `crtr profile list` to see available profiles."});return g(q(e.profileId))}s(ue,"rawSettingsOwnerForTarget");function wo(e){return pn(ue(e))}s(wo,"readRawSettingsForTarget");function Ao(e,n,o){return gn(ue(e),n,o,t=>{try{z(t)}catch(r){throw new Rn(r)}})}s(Ao,"replaceRawSettingsForTarget");function So(e,n){const o=w(e);return S(o,t=>{const r=n(t);return{value:r,next:r}})}s(So,"updateRawConfigAtomically");function jo(e,n){G(w(e),n)}s(jo,"writeConfig");function Ro(e,n){G(w(e),n)}s(Ro,"writeRawConfig");function xn(e,n){const o=w(e);yn(g(o),n)}s(xn,"writeState");function xo(e,n){if(e==="project")try{qe(n)}catch(t){if(t.code!=="EEXIST"||!Ge(n).isDirectory())throw t}else sn(n);const o=g(e==="user"?E():n);fn({kind:o.kind==="user"?"user":"project",root:o.store.root}),o.kind==="project"&&K(b(o.root))===null&&G(o.root,{schema_version:k().schema_version})}s(xo,"ensureScopeInitialized");function On(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))Array.isArray(t)&&t.every(r=>typeof r=="string")&&(n[o]=[...t]);return n}s(On,"normalizeKeybindings");const En=new Set(en.filter(on).map(e=>e.id));function In(e){return Array.isArray(e)?[...new Set(e.filter(n=>typeof n=="string"&&nn.has(n)&&En.has(n)))]:[]}s(In,"normalizeTmuxPassthrough");function C(e,n=Ve()){const o=n;if(e===null||typeof e!="object")return o;const t=e,r={anthropic:{...o.anthropic},openai:{...o.openai},...o.defaultProvider!==void 0?{defaultProvider:o.defaultProvider}:{}};(t.defaultProvider==="anthropic"||t.defaultProvider==="openai")&&(r.defaultProvider=t.defaultProvider);for(const i of["anthropic","openai"]){const c=t[i];if(!(c===null||typeof c!="object"))for(const[u,d]of Object.entries(c))(u==="ultra"||u==="strong"||u==="medium"||u==="light")&&typeof d=="string"&&(r[i][u]=d)}return r}s(C,"mergeModelLadders");function de(e){return e==="ultra"||e==="strong"||e==="medium"||e==="light"}s(de,"validStrength");const B=["ultra","strong","medium","light"];function W(){return Object.create(null)}s(W,"routeRecord");function j(e,n,o){Object.defineProperty(e,n,{value:o,writable:!0,enumerable:!0,configurable:!0})}s(j,"setRouteRecord");function fe(e){const n=W();for(const[o,t]of Object.entries(e??{}))j(n,o,t);return n}s(fe,"copyRouteRecord");function R(e,n){rn({level:"warn",event:"config.model_routes.invalid",fields:{route_id:e,rule:n}})}s(R,"routeInvalid");function $(e,n={}){const o=fe(n);if(e===null||typeof e!="object"||Array.isArray(e))return o;for(const[t,r]of Object.entries(e)){if(t.trim()===""){R(t,"route id must be a non-empty string");continue}if(r===null||typeof r!="object"||Array.isArray(r)){R(t,"route must be an object");continue}const i=r,c=o[t],u={...c};let d;for(const l of["family","credentialSource","providerId"]){const f=i[l];if(f!==void 0){if(typeof f!="string"||f.trim()===""){d=`${l} must be a non-empty string`;break}u[l]=f}}if(d===void 0&&i.models!==void 0)if(i.models===null||typeof i.models!="object"||Array.isArray(i.models))d="models must be an object";else{const l={...c?.models};for(const[f,a]of Object.entries(i.models)){if(!de(f)){d=`models.${f} is not a model strength`;break}if(typeof a!="string"||a.trim()===""){d=`models.${f} must be a non-empty provider/id ref`;break}const m=a.indexOf("/");if(m<=0||m===a.length-1){d=`models.${f} must be provider/id`;break}l[f]=a}u.models=l}if(d===void 0&&u.models!==void 0&&u.providerId!==void 0){for(const[l,f]of Object.entries(u.models))if(f.slice(0,f.indexOf("/"))!==u.providerId){d=`models.${l} provider prefix must equal providerId`;break}}if(d!==void 0){R(t,d);continue}j(o,t,u)}return o}s($,"mergeModelRoutePatches");function le(e){const n=W(),o=W();for(const[t,r]of Object.entries(e)){if(r.family===void 0||r.credentialSource===void 0||r.providerId===void 0||r.models===void 0){R(t,"route must include family, credentialSource, providerId, and models");continue}const i=B.filter(c=>r.models[c]===void 0);if(i.length>0){R(t,`incomplete route; missing strengths: ${i.join(", ")}`),j(o,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models});continue}j(n,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models})}return{complete:n,legacy:o}}s(le,"resolveModelRoutes");function Pn(e,n){if(e.trim()==="")throw new Error("model route id must be a non-empty string");for(const t of["family","credentialSource","providerId"])if(typeof n[t]!="string"||n[t].trim()==="")throw new Error(`model route "${e}" ${t} must be a non-empty string`);const o=B.filter(t=>n.models?.[t]===void 0);if(o.length>0)throw new Error(`model route "${e}" is incomplete; missing strengths: ${o.join(", ")}`);for(const t of B){const r=n.models[t];if(typeof r!="string"||r.trim()==="")throw new Error(`model route "${e}" models.${t} must be a non-empty provider/id ref`);const i=r.indexOf("/");if(i<=0||i===r.length-1)throw new Error(`model route "${e}" models.${t} must be provider/id`);if(r.slice(0,i)!==n.providerId)throw new Error(`model route "${e}" models.${t} provider prefix must equal providerId`)}}s(Pn,"assertCompleteModelRoute");function M(e,n){if(e===void 0||e===null||typeof e!="object"||Array.isArray(e))return n;const o=e,t=s(i=>Array.isArray(i)&&i.every(c=>typeof c=="string"&&c.trim()!=="")?[...i]:void 0,"strings"),r=Array.isArray(o.strengthFallback)&&o.strengthFallback.every(i=>de(i))?[...o.strengthFallback]:void 0;return{...t(o.credentialSourceOrder)!==void 0?{credentialSourceOrder:t(o.credentialSourceOrder)}:{},...t(o.familyOrder)!==void 0?{familyOrder:t(o.familyOrder)}:{},...r!==void 0?{strengthFallback:r}:{}}}s(M,"mergeModelRouting");function Tn(e){return e!==""&&e.replace(/\*/g,"")===""}s(Tn,"isInheritAllGlob");function N(e,n=[]){const o=e,t=Array.isArray(o?.allow)?o.allow.filter(r=>typeof r=="string"):[];for(const r of t)if(Tn(r))throw new Error(`spawnEnv.allow: "${r}" matches every env var name (inherit-all) \u2014 this is exactly the leak the spawn-env boundary closes. Name the specific vars/prefixes you actually need instead (e.g. "AWS_*", "GIT_ASKPASS").`);return{allow:[...new Set([...n,...t])]}}s(N,"mergeSpawnEnv");function L(e,n={}){const o={};for(const[t,r]of Object.entries(n))o[t]={...r};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){if(r===null||typeof r!="object")continue;const i={...o[t]},c=r.fastMode;typeof c=="boolean"&&(i.fastMode=c),o[t]=i}return o}s(L,"mergeProviderOptions");const ae=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,Cn=new Set(["crtr","crtrd"]);function F(e,n){if(n==="bin"&&Cn.has(e))return`${n}.${e} is reserved by crouter \u2014 pick another bare command name`;if(!ae.test(e))return`${n}.${e} is not a safe bare command name (expected ${ae.source})`}s(F,"bareCommandNameIssue");function $n(e){if(e===null||typeof e!="object"||Array.isArray(e))return[{name:"bin",message:"bin must be a JSON object mapping bare command name to a relative path"}];const n=[];for(const[o,t]of Object.entries(e)){const r=F(o,"bin");r!==void 0&&n.push({name:o,message:r}),(typeof t!="string"||t.trim()==="")&&n.push({name:o,message:`bin.${o} must be a non-empty relative path to an executable inside the contributor root`})}return n}s($n,"binDeclarationIssues");function Mn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))F(o,"bin")===void 0&&(typeof t!="string"||t.trim()===""||(n[o]=t));return n}s(Mn,"normalizeBin");const me=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function Nn(e){if(e===null||typeof e!="object"||Array.isArray(e))return[{name:"humanActions",reason:"humanActions must be a JSON object mapping action names to {argv, cwd}",block:!0}];const n=[];for(const[o,t]of Object.entries(e)){const r=[];if(me.test(o)||r.push(`action name must match ${me.source}`),t===null||typeof t!="object"||Array.isArray(t))r.push("action must be an object with argv and cwd");else{const i=t,c=Object.keys(i).filter(u=>u!=="argv"&&u!=="cwd");c.length>0&&r.push(`action has unsupported field${c.length===1?"":"s"}: ${c.join(", ")}`),(!Array.isArray(i.argv)||i.argv.length===0||!i.argv.every(u=>typeof u=="string"&&u.length>0))&&r.push("argv must be a non-empty array of non-empty strings"),(typeof i.cwd!="string"||i.cwd.length===0)&&r.push("cwd is required and must be a non-empty string")}r.length>0&&n.push({name:o,reason:r.join("; ")})}return n}s(Nn,"humanActionDeclarationIssues");function Ln(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n=new Set(Nn(e).map(t=>t.name)),o={};for(const[t,r]of Object.entries(e)){if(n.has(t))continue;const i=r;o[t]={argv:[...i.argv],cwd:i.cwd}}return o}s(Ln,"normalizeHumanActions");function Oo(e){return $n(e).map(n=>n.message)}s(Oo,"invalidPluginBinReasons");function Eo(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))F(o,"requires")===void 0&&(typeof t!="string"||t.trim()===""||/[\r\n]/.test(t)||(n[o]=t));return n}s(Eo,"normalizeRequires");function Io(e){if(e===null||typeof e!="object"||Array.isArray(e))return["requires must be a JSON object mapping a bare executable name to a one-line install hint"];const n=[];for(const[o,t]of Object.entries(e)){const r=F(o,"requires");r!==void 0&&n.push(r),typeof t!="string"||t.trim()===""?n.push(`requires.${o} must be a non-empty one-line install hint`):/[\r\n]/.test(t)&&n.push(`requires.${o} must be a one-line install hint`)}return n}s(Io,"invalidPluginRequiresReasons");function pe(e){if(e===null||typeof e!="object")return null;const n=e,o={};typeof n.whenToUse=="string"&&n.whenToUse.trim()!==""&&(o.whenToUse=n.whenToUse),typeof n.model=="string"&&n.model.trim()!==""&&(o.model=n.model),typeof n.orchestratorModel=="string"&&n.orchestratorModel.trim()!==""&&(o.orchestratorModel=n.orchestratorModel);const t=s(r=>Array.isArray(r)&&r.every(i=>typeof i=="string"),"isStringArray");return t(n.tools)&&(o.tools=n.tools),t(n.extensions)&&(o.extensions=n.extensions),t(n.availableTo)&&(o.availableTo=n.availableTo),Object.keys(o).length>0?o:null}s(pe,"normalizeKindFields");function x(e,n=Xe()){const o={...n};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){const i=pe(r);if(i===null)continue;const c=o[t];c!==void 0?o[t]={...c,...i}:i.whenToUse!==void 0&&(o[t]=i)}return o}s(x,"mergeKinds");function ge(e,n,o){if(n===null)return o;const t=te(e,n).filter(i=>i.enabled&&i.manifest.kinds!==void 0).sort((i,c)=>i.name.localeCompare(c.name));let r=o;for(const i of t)r=x(i.manifest.kinds,r);return r}s(ge,"layerPluginKinds");function Po(){const e=[{origin:"config.json",components:An("user").page_components}],n=y("user");if(n!==null){const o=te("user",n).filter(t=>t.enabled&&t.manifest.page_components!==void 0).sort((t,r)=>t.name.localeCompare(r.name));for(const t of o){const r=`plugin "${t.name}" page_components`;e.push({origin:`plugin "${t.name}"`,components:se(t.manifest.page_components,r)})}}return kn(e)}s(Po,"resolvePageComponents");const H=new Set(["whenToUse","model","orchestratorModel","tools","extensions","availableTo"]);function To(e){if(e===null||typeof e!="object"||Array.isArray(e))return["kinds must be a JSON object keyed by kind name"];const n=[],o=s(t=>Array.isArray(t)&&t.every(r=>typeof r=="string"),"isStringArray");for(const[t,r]of Object.entries(e)){if(t.trim()===""){n.push("kinds contains an empty kind name");continue}if(r===null||typeof r!="object"||Array.isArray(r)){n.push(`kinds.${t} must be a JSON object`);continue}const i=r;for(const c of Object.keys(i))H.has(c)||n.push(`kinds.${t}.${c} is not a KindConfig field (expected one of: ${[...H].join(", ")})`);i.whenToUse!==void 0&&(typeof i.whenToUse!="string"||i.whenToUse.trim()==="")&&n.push(`kinds.${t}.whenToUse must be a non-empty string`);for(const c of["model","orchestratorModel"])i[c]!==void 0&&(typeof i[c]!="string"||i[c].trim()==="")&&n.push(`kinds.${t}.${c} must be a non-empty string`);for(const c of["tools","extensions","availableTo"])i[c]!==void 0&&!o(i[c])&&n.push(`kinds.${t}.${c} must be an array of strings`);pe(r)===null&&n.push(`kinds.${t} has no valid KindConfig field \u2014 an entry must set at least one of: ${[...H].join(", ")} (whenToUse is required when the kind exists in no lower layer)`)}return n}s(To,"invalidPluginKindsReasons");function Fn(e){if(e===null||typeof e!="object")return null;const n=e;if(typeof n.previewEndpoint!="string"||n.previewEndpoint.trim()===""||typeof n.relayTokenRef!="string"||n.relayTokenRef.trim()==="")return null;const o={previewEndpoint:n.previewEndpoint,relayTokenRef:n.relayTokenRef};return typeof n.cpOrigin=="string"&&n.cpOrigin.trim()!==""&&(o.cpOrigin=n.cpOrigin),o}s(Fn,"normalizeRemoteCanvasTarget");function zn(e,n=Qe()){const o={...n.targets};if(e!==null&&typeof e=="object"){const t=e;if(t.targets!==null&&typeof t.targets=="object")for(const[r,i]of Object.entries(t.targets)){const c=Fn(i);c!==null&&(o[r]=c)}}return{targets:o}}s(zn,"mergeRemoteCanvas");function ye(e,n){return e===!0?"notify":e===!1?!1:e==="notify"||e==="apply"?e:n}s(ye,"normalizeMode");function O(e,n){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=1?e:n}s(O,"normalizePositiveInteger");function Dn(e,n){const o=e!==null&&typeof e=="object"?e:{},t=O(o.warning,n.warning),r=O(o.automaticReviveCap,n.automaticReviveCap);return t<=r?{warning:t,automaticReviveCap:r}:{...n}}s(Dn,"mergeBrokerThresholds");function Un(e,n){return{unattendedParkMs:O((e!==null&&typeof e=="object"?e:{}).unattendedParkMs,n.unattendedParkMs)}}s(Un,"mergeLifecycle");function Kn(e){if(!Array.isArray(e))return[...ee];const n=e.filter(o=>typeof o=="string"&&o.trim()!=="").map(o=>o.trim());return n.length>0?n:[...ee]}s(Kn,"normalizeWhipMessages");function qn(e,n){if(e===null||typeof e!="object")return{...n};const o=e.tcp;return{tcp:typeof o=="string"?o:n.tcp}}s(qn,"mergeApi");function z(e){const n=k(),o=n.schema_version,t=e.marketplaces===void 0?{}:e.marketplaces,r=e.plugins===void 0?{}:e.plugins,i=e.auto_update,c=i&&typeof i.interval_hours=="number"?i.interval_hours:void 0,u=c!==void 0&&Number.isFinite(c)&&c>=0?c:n.auto_update.interval_hours,d={crtr:ye(i?.crtr,n.auto_update.crtr),content:ye(i?.content,n.auto_update.content),interval_hours:u},l=e.max_panes_per_window,f=O(l,n.max_panes_per_window),a=typeof e.auto_open_child_viewers=="boolean"?e.auto_open_child_viewers:n.auto_open_child_viewers,m=Dn(e.brokerThresholds,n.brokerThresholds),h=Un(e.lifecycle,n.lifecycle),D=typeof e.completion_bell=="boolean"?e.completion_bell:n.completion_bell,p=e.working_gerunds===void 0?[...n.working_gerunds]:vn(e.working_gerunds),be=typeof e.whip_mode=="boolean"?e.whip_mode:n.whip_mode,_e=se(e.page_components),ve=typeof e.page_surface=="boolean"?e.page_surface:n.page_surface,ke=Kn(e.whip_messages),we=Ye.includes(e.whip_message_mode)?e.whip_message_mode:n.whip_message_mode,Ae=Ze.includes(e.mouse_mode_default)?e.mouse_mode_default:n.mouse_mode_default,Se=O(e.live_cycles,n.live_cycles),je=Je.includes(e.condensed_history)?e.condensed_history:n.condensed_history,Re=typeof e.bash_tool_purpose=="boolean"?e.bash_tool_purpose:n.bash_tool_purpose,xe=typeof e.fold_finished_tools=="boolean"?e.fold_finished_tools:n.fold_finished_tools,Oe=typeof e.summarize_tool_calls=="boolean"?e.summarize_tool_calls:n.summarize_tool_calls,Ee=typeof e.detailed_tool_recaps=="boolean"?e.detailed_tool_recaps:n.detailed_tool_recaps,Ie=On(e.keybindings),Pe=In(e.tmux_passthrough),Te=C(e.modelLadders),Ce=L(e.providerOptions,n.providerOptions),U=le($(e.modelRoutes)),J=U.complete,Z=M(e.modelRouting,void 0),$e=x(e.kinds),Me=zn(e.remoteCanvas),Ne=qn(e.api,n.api),_=e.paths;if(_?.user_files!==void 0&&(typeof _.user_files!="string"||!_.user_files.startsWith("/")))throw new Error("paths.user_files must be an absolute path");const Y=e.store,v=e.runtime;if(v?.person_uids!==void 0&&(!Array.isArray(v.person_uids)||!v.person_uids.every(V=>Number.isSafeInteger(V)&&V>=0)))throw new Error("runtime.person_uids must be an array of nonnegative integer uids");const Le=N(e.spawnEnv,n.spawnEnv?.allow),Fe=Mn(e.bin),ze=Ln(e.humanActions);return{bin:Fe,humanActions:ze,schema_version:o,marketplaces:t,plugins:r,auto_update:d,max_panes_per_window:f,auto_open_child_viewers:a,brokerThresholds:m,lifecycle:h,completion_bell:D,working_gerunds:p,whip_mode:be,page_components:_e,page_surface:ve,whip_messages:ke,whip_message_mode:we,mouse_mode_default:Ae,live_cycles:Se,condensed_history:je,bash_tool_purpose:Re,fold_finished_tools:xe,summarize_tool_calls:Oe,detailed_tool_recaps:Ee,keybindings:Ie,tmux_passthrough:Pe,modelLadders:Te,providerOptions:Ce,...Object.keys(J).length>0?{modelRoutes:J}:{},...Object.keys(U.legacy).length>0?{legacyModelRoutes:U.legacy}:{},...Z!==void 0?{modelRouting:Z}:{},kinds:$e,remoteCanvas:Me,api:Ne,..._?{paths:_}:{},...Y?{store:Y}:{},...v?{runtime:v}:{},spawnEnv:Le}}s(z,"mergeConfig");function Gn(e){return I(e)}s(Gn,"readRawConfigAtRoot");function Bn(e){const n=y(e);return n?Gn(n):null}s(Bn,"readRawScopeConfig");function Wn(e){if(e===null||e==="")return null;try{return I(q(e))}catch{return null}}s(Wn,"readRawProfileConfig");function Hn(e,n){return[...cn(e,n)].reverse().map(r=>({root:r,raw:I(r)}))}s(Hn,"readRawProjectScopeConfigs");function he(e=process.cwd(),n=Ue()||null){const o=P[T];if(o)return o.launch;const t=k(),r=Bn("user"),i=Wn(n),c=Hn(e,n);let u=x(r?.kinds,ge("user",y("user"),t.kinds)),d=C(r?.modelLadders,t.modelLadders),l=L(r?.providerOptions,t.providerOptions),f=$(r?.modelRoutes),a=M(r?.modelRouting,void 0),m=N(r?.spawnEnv,t.spawnEnv?.allow);i!==null&&(u=x(i.kinds,u),d=C(i.modelLadders,d),l=L(i.providerOptions,l),f=$(i.modelRoutes,f),a=M(i.modelRouting,a),m=N(i.spawnEnv,m.allow));for(const{root:D,raw:p}of c)u=x(p?.kinds,ge("project",D,u)),p!==null&&(d=C(p.modelLadders,d),l=L(p.providerOptions,l),f=$(p.modelRoutes,f),a=M(p.modelRouting,a),m=N(p.spawnEnv,m.allow));const h=le(f);return{kinds:u,modelLadders:d,providerOptions:l,...Object.keys(h.complete).length>0?{modelRoutes:h.complete}:{},...Object.keys(h.legacy).length>0?{legacyModelRoutes:h.legacy}:{},...a!==void 0?{modelRouting:a}:{},spawnEnv:m}}s(he,"readMergedLaunchConfig");function Co(e){return he().kinds[e]}s(Co,"resolveKindConfig");function $o(e){const n=he().kinds,o=[];for(const[t,r]of Object.entries(n)){const i=t.indexOf("/");if(i===-1)continue;const c=t.slice(0,i),u=r.availableTo??[c];(u.includes("*")||u.includes(e))&&o.push({kind:t,whenToUse:r.whenToUse})}return o.sort((t,r)=>t.kind.localeCompare(r.kind))}s($o,"subKindsAvailableTo");function Jn(e,n,o){const t={...e??{}};t.anthropic!==void 0&&(t.anthropic={...t.anthropic}),t.openai!==void 0&&(t.openai={...t.openai}),o.defaultProvider!==n.defaultProvider&&o.defaultProvider!==void 0&&(t.defaultProvider=o.defaultProvider);for(const i of["anthropic","openai"])for(const c of["ultra","strong","medium","light"])o[i][c]!==n[i][c]&&(t[i]={...t[i]??{},[c]:o[i][c]});return t.defaultProvider!==void 0||t.anthropic!==void 0||t.openai!==void 0?t:void 0}s(Jn,"sparseModelLaddersToPersist");function Zn(e,n){return S(e,o=>{const t=z(o),r=z(o);n(r);const i={...o};for(const u of _n)u!=="modelLadders"&&!Be(r[u],t[u])&&(i[u]=r[u]);const c=Jn(o.modelLadders,t.modelLadders,r.modelLadders);return c!==void 0?i.modelLadders=c:delete i.modelLadders,{value:r,next:i}})}s(Zn,"mutateConfigAtRoot");function Mo(e,n){S(e,o=>{const t=o.modelLadders??{},r={...t};n.defaultProvider!==void 0&&(r.defaultProvider=n.defaultProvider);for(const i of["anthropic","openai"])n[i]!==void 0&&(r[i]={...t[i]??{},...n[i]});return{value:void 0,next:{...o,modelLadders:r}}})}s(Mo,"persistModelLadders");function No(e,n,o){if(o!==void 0)Pn(n,o);else if(n.trim()==="")throw new Error("model route id must be a non-empty string");S(e,t=>{const r=fe(t.modelRoutes??{});o===void 0?delete r[n]:j(r,n,{...o,models:{...o.models}});const i={...t};return Object.keys(r).length===0?delete i.modelRoutes:i.modelRoutes=r,{value:void 0,next:i}})}s(No,"persistModelRoute");function Yn(e,n){return Zn(e,n)}s(Yn,"updateConfigAtRoot");function Lo(e,n){return Yn(w(e),n)}s(Lo,"updateConfig");function Fo(e){const n=e.orchestrator===!0?"orchestratorModel":"model";return S(e.root,o=>{const t={...o.kinds??{},[e.kind]:{...(o.kinds??{})[e.kind],[n]:e.model}};return{value:void 0,next:{...o,kinds:t}}}),{path:b(e.root),field:n}}s(Fo,"persistDefaultKindModel");function zo(e,n){const o=jn(e);return n(o),xn(e,o),o}s(zo,"updateState");export{Rn as InvalidRawSettingsError,$n as binDeclarationIssues,ko as brokerConfigSnapshot,bo as configPath,xo as ensureScopeInitialized,Nn as humanActionDeclarationIssues,vo as installBrokerConfigSnapshot,Oo as invalidPluginBinReasons,To as invalidPluginKindsReasons,Io as invalidPluginRequiresReasons,Mn as normalizeBin,Ln as normalizeHumanActions,Eo as normalizeRequires,In as normalizeTmuxPassthrough,Fo as persistDefaultKindModel,Mo as persistModelLadders,No as persistModelRoute,An as readConfig,Sn as readConfigAtRoot,he as readMergedLaunchConfig,Gn as readRawConfigAtRoot,Bn as readRawScopeConfig,wo as readRawSettingsForTarget,jn as readState,Ao as replaceRawSettingsForTarget,Co as resolveKindConfig,Po as resolvePageComponents,_o as statePath,$o as subKindsAvailableTo,Lo as updateConfig,Yn as updateConfigAtRoot,So as updateRawConfigAtomically,zo as updateState,jo as writeConfig,Ro as writeRawConfig,xn as writeState};
1
+ var Ue=Object.defineProperty;var s=(e,n)=>Ue(e,"name",{value:n,configurable:!0});import{envProfileId as Ke}from"../shared/env.js";import{basename as X,join as Q}from"node:path";import{existsSync as qe,mkdirSync as Ge,statSync as Be}from"node:fs";import{isDeepStrictEqual as We}from"node:util";import{CONFIG_FILE as He,STATE_FILE as Je,CONDENSED_HISTORY_MODES as Ze,MOUSE_MODE_DEFAULTS as Ye,WHIP_MESSAGE_MODES as Ve,DEFAULT_WHIP_MESSAGES as ee,defaultScopeConfig as k,defaultScopeState as ne,defaultModelLaddersConfig as Xe,defaultKindsConfig as Qe,defaultRemoteCanvasConfig as en}from"../types.js";import{BINDING_CATALOG as nn,BINDING_IDS as on,isAttachPaneBinding as tn}from"./keybindings/catalog.js";import{notFound as rn}from"./errors.js";import{emitEvent as sn}from"./events/emit.js";import{atomicWriteJson as oe,readJsonIfExists as K,ensureDir as cn}from"./fs-utils.js";import{scopeRoot as y,requireScopeRoot as w,findProjectScopeRoots as un,userScopeRoot as E}from"./scope.js";import{listInstalledPluginsInRoot as te}from"./installed-plugins.js";import{profileMemoryDir as q,listProfiles as re}from"./profiles/manifest.js";import{canonicalRoot as A,projectStore as dn,userStore as ie}from"./scoped-state/paths.js";import{convertLegacyScopedStore as fn,initializeFreshScopedStore as ln}from"./scoped-state/migrate.js";import{mutateRawSettings as an,readBookkeeping as mn,readRawSettings as pn,readRawSettingsWithRevision as gn,replaceRawSettings as yn,writeBookkeeping as hn,writeRawSettings as bn}from"./scoped-state/settings.js";import{withExclusiveDirectoryLock as _n}from"./exclusive-lock.js";import{SCOPE_CONFIG_KEYS as vn}from"./user-settings.js";import{normalizeWorkingGerunds as kn}from"../shared/working-activity.js";import{mergePageComponentLayers as wn,normalizePageComponents as se}from"./human/page-catalog.js";function b(e){return Q(e,He)}s(b,"configPathFor");function ce(e,n){return _n(`${e}.lock`,()=>n(e),{timeoutError:s(()=>new Error(`timed out waiting for config lock: ${e}`),"timeoutError")})}s(ce,"withConfigPathLock");function An(e){return Q(e,Je)}s(An,"statePathFor");function _o(e){const n=y(e);return n?b(n):null}s(_o,"configPath");function vo(e){const n=y(e);return n?An(n):null}s(vo,"statePath");function g(e){const n=A(e),o=A(E());if(n===o)return{store:ie(o),key:"user",kind:"user",root:o};if(X(n)===".crouter")return fn({kind:"project",root:n}),{store:dn(n),key:"project",kind:"project",root:n};for(const t of re()){const r=A(q(t.profileId));if(n===r)return{store:ie(o),key:`profile:${t.profileId}`,kind:"profile",root:r,profileId:t.profileId}}throw new Error(`settings root is not a recognized owner: ${e}`)}s(g,"settingsOwnerForRoot");function I(e){if(X(e)===".crouter"){let n;try{n=A(e)}catch(o){if(o.code==="ENOENT")return null;throw o}if(n!==A(E()))return K(b(e))}return pn(g(e))}s(I,"readRawAtRoot");function G(e,n){const o=g(e);if(o.kind==="project"){ce(b(o.root),t=>oe(t,n,{createParent:!1}));return}bn(o,n)}s(G,"writeRawAtRoot");function S(e,n){const o=g(e);return o.kind==="project"?ce(b(o.root),t=>{const r=n(K(t)??{});return oe(t,r.next,{createParent:!1}),r.value}):an(o,t=>n(t))}s(S,"mutateRawAtRoot");const P=globalThis,T=Symbol.for("crtr.broker.config-snapshot");function ko(e){P[T]=e}s(ko,"installBrokerConfigSnapshot");function wo(){return P[T]}s(wo,"brokerConfigSnapshot");function Sn(e){const n=P[T];if(n){if(e!=="user")throw new Error(`broker config snapshot has no ${e} scope`);return n.user}const o=y(e);return o?jn(o):k()}s(Sn,"readConfig");function jn(e){const n=I(e);return n?z(n):k()}s(jn,"readConfigAtRoot");function Rn(e){const n=y(e);if(!n)return ne();try{return mn(g(n))}catch(o){if(e==="project"&&!qe(n))return ne();throw o}}s(Rn,"readState");class xn extends Error{static{s(this,"InvalidRawSettingsError")}constructor(n){super(`settings cannot be merged: ${n instanceof Error?n.message:String(n)}`),this.name="InvalidRawSettingsError"}}function ue(e){if(e.scope==="user")return g(E());if(!re().some(n=>n.profileId===e.profileId))throw rn(`profile not found: ${e.profileId}`,{received:e.profileId,next:"Run `crtr profile list` to see available profiles."});return g(q(e.profileId))}s(ue,"rawSettingsOwnerForTarget");function Ao(e){return gn(ue(e))}s(Ao,"readRawSettingsForTarget");function So(e,n,o){return yn(ue(e),n,o,t=>{try{z(t)}catch(r){throw new xn(r)}})}s(So,"replaceRawSettingsForTarget");function jo(e,n){const o=w(e);return S(o,t=>{const r=n(t);return{value:r,next:r}})}s(jo,"updateRawConfigAtomically");function Ro(e,n){G(w(e),n)}s(Ro,"writeConfig");function xo(e,n){G(w(e),n)}s(xo,"writeRawConfig");function On(e,n){const o=w(e);hn(g(o),n)}s(On,"writeState");function Oo(e,n){if(e==="project")try{Ge(n)}catch(t){if(t.code!=="EEXIST"||!Be(n).isDirectory())throw t}else cn(n);const o=g(e==="user"?E():n);ln({kind:o.kind==="user"?"user":"project",root:o.store.root}),o.kind==="project"&&K(b(o.root))===null&&G(o.root,{schema_version:k().schema_version})}s(Oo,"ensureScopeInitialized");function En(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))Array.isArray(t)&&t.every(r=>typeof r=="string")&&(n[o]=[...t]);return n}s(En,"normalizeKeybindings");const In=new Set(nn.filter(tn).map(e=>e.id));function Pn(e){return Array.isArray(e)?[...new Set(e.filter(n=>typeof n=="string"&&on.has(n)&&In.has(n)))]:[]}s(Pn,"normalizeTmuxPassthrough");function C(e,n=Xe()){const o=n;if(e===null||typeof e!="object")return o;const t=e,r={anthropic:{...o.anthropic},openai:{...o.openai},...o.defaultProvider!==void 0?{defaultProvider:o.defaultProvider}:{}};(t.defaultProvider==="anthropic"||t.defaultProvider==="openai")&&(r.defaultProvider=t.defaultProvider);for(const i of["anthropic","openai"]){const c=t[i];if(!(c===null||typeof c!="object"))for(const[u,d]of Object.entries(c))(u==="ultra"||u==="strong"||u==="medium"||u==="light")&&typeof d=="string"&&(r[i][u]=d)}return r}s(C,"mergeModelLadders");function de(e){return e==="ultra"||e==="strong"||e==="medium"||e==="light"}s(de,"validStrength");const B=["ultra","strong","medium","light"];function W(){return Object.create(null)}s(W,"routeRecord");function j(e,n,o){Object.defineProperty(e,n,{value:o,writable:!0,enumerable:!0,configurable:!0})}s(j,"setRouteRecord");function fe(e){const n=W();for(const[o,t]of Object.entries(e??{}))j(n,o,t);return n}s(fe,"copyRouteRecord");function R(e,n){sn({level:"warn",event:"config.model_routes.invalid",fields:{route_id:e,rule:n}})}s(R,"routeInvalid");function $(e,n={}){const o=fe(n);if(e===null||typeof e!="object"||Array.isArray(e))return o;for(const[t,r]of Object.entries(e)){if(t.trim()===""){R(t,"route id must be a non-empty string");continue}if(r===null||typeof r!="object"||Array.isArray(r)){R(t,"route must be an object");continue}const i=r,c=o[t],u={...c};let d;for(const l of["family","credentialSource","providerId"]){const f=i[l];if(f!==void 0){if(typeof f!="string"||f.trim()===""){d=`${l} must be a non-empty string`;break}u[l]=f}}if(d===void 0&&i.models!==void 0)if(i.models===null||typeof i.models!="object"||Array.isArray(i.models))d="models must be an object";else{const l={...c?.models};for(const[f,a]of Object.entries(i.models)){if(!de(f)){d=`models.${f} is not a model strength`;break}if(typeof a!="string"||a.trim()===""){d=`models.${f} must be a non-empty provider/id ref`;break}const m=a.indexOf("/");if(m<=0||m===a.length-1){d=`models.${f} must be provider/id`;break}l[f]=a}u.models=l}if(d===void 0&&u.models!==void 0&&u.providerId!==void 0){for(const[l,f]of Object.entries(u.models))if(f.slice(0,f.indexOf("/"))!==u.providerId){d=`models.${l} provider prefix must equal providerId`;break}}if(d!==void 0){R(t,d);continue}j(o,t,u)}return o}s($,"mergeModelRoutePatches");function le(e){const n=W(),o=W();for(const[t,r]of Object.entries(e)){if(r.family===void 0||r.credentialSource===void 0||r.providerId===void 0||r.models===void 0){R(t,"route must include family, credentialSource, providerId, and models");continue}const i=B.filter(c=>r.models[c]===void 0);if(i.length>0){R(t,`incomplete route; missing strengths: ${i.join(", ")}`),j(o,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models});continue}j(n,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models})}return{complete:n,legacy:o}}s(le,"resolveModelRoutes");function Tn(e,n){if(e.trim()==="")throw new Error("model route id must be a non-empty string");for(const t of["family","credentialSource","providerId"])if(typeof n[t]!="string"||n[t].trim()==="")throw new Error(`model route "${e}" ${t} must be a non-empty string`);const o=B.filter(t=>n.models?.[t]===void 0);if(o.length>0)throw new Error(`model route "${e}" is incomplete; missing strengths: ${o.join(", ")}`);for(const t of B){const r=n.models[t];if(typeof r!="string"||r.trim()==="")throw new Error(`model route "${e}" models.${t} must be a non-empty provider/id ref`);const i=r.indexOf("/");if(i<=0||i===r.length-1)throw new Error(`model route "${e}" models.${t} must be provider/id`);if(r.slice(0,i)!==n.providerId)throw new Error(`model route "${e}" models.${t} provider prefix must equal providerId`)}}s(Tn,"assertCompleteModelRoute");function M(e,n){if(e===void 0||e===null||typeof e!="object"||Array.isArray(e))return n;const o=e,t=s(i=>Array.isArray(i)&&i.every(c=>typeof c=="string"&&c.trim()!=="")?[...i]:void 0,"strings"),r=Array.isArray(o.strengthFallback)&&o.strengthFallback.every(i=>de(i))?[...o.strengthFallback]:void 0;return{...t(o.credentialSourceOrder)!==void 0?{credentialSourceOrder:t(o.credentialSourceOrder)}:{},...t(o.familyOrder)!==void 0?{familyOrder:t(o.familyOrder)}:{},...r!==void 0?{strengthFallback:r}:{}}}s(M,"mergeModelRouting");function Cn(e){return e!==""&&e.replace(/\*/g,"")===""}s(Cn,"isInheritAllGlob");function N(e,n=[]){const o=e,t=Array.isArray(o?.allow)?o.allow.filter(r=>typeof r=="string"):[];for(const r of t)if(Cn(r))throw new Error(`spawnEnv.allow: "${r}" matches every env var name (inherit-all) \u2014 this is exactly the leak the spawn-env boundary closes. Name the specific vars/prefixes you actually need instead (e.g. "AWS_*", "GIT_ASKPASS").`);return{allow:[...new Set([...n,...t])]}}s(N,"mergeSpawnEnv");function L(e,n={}){const o={};for(const[t,r]of Object.entries(n))o[t]={...r};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){if(r===null||typeof r!="object")continue;const i={...o[t]},c=r.fastMode;typeof c=="boolean"&&(i.fastMode=c),o[t]=i}return o}s(L,"mergeProviderOptions");const ae=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,$n=new Set(["crtr","crtrd"]);function F(e,n){if(n==="bin"&&$n.has(e))return`${n}.${e} is reserved by crouter \u2014 pick another bare command name`;if(!ae.test(e))return`${n}.${e} is not a safe bare command name (expected ${ae.source})`}s(F,"bareCommandNameIssue");function Mn(e){if(e===null||typeof e!="object"||Array.isArray(e))return[{name:"bin",message:"bin must be a JSON object mapping bare command name to a relative path"}];const n=[];for(const[o,t]of Object.entries(e)){const r=F(o,"bin");r!==void 0&&n.push({name:o,message:r}),(typeof t!="string"||t.trim()==="")&&n.push({name:o,message:`bin.${o} must be a non-empty relative path to an executable inside the contributor root`})}return n}s(Mn,"binDeclarationIssues");function Nn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))F(o,"bin")===void 0&&(typeof t!="string"||t.trim()===""||(n[o]=t));return n}s(Nn,"normalizeBin");const me=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function Ln(e){if(e===null||typeof e!="object"||Array.isArray(e))return[{name:"humanActions",reason:"humanActions must be a JSON object mapping action names to {argv, cwd}",block:!0}];const n=[];for(const[o,t]of Object.entries(e)){const r=[];if(me.test(o)||r.push(`action name must match ${me.source}`),t===null||typeof t!="object"||Array.isArray(t))r.push("action must be an object with argv and cwd");else{const i=t,c=Object.keys(i).filter(u=>u!=="argv"&&u!=="cwd");c.length>0&&r.push(`action has unsupported field${c.length===1?"":"s"}: ${c.join(", ")}`),(!Array.isArray(i.argv)||i.argv.length===0||!i.argv.every(u=>typeof u=="string"&&u.length>0))&&r.push("argv must be a non-empty array of non-empty strings"),(typeof i.cwd!="string"||i.cwd.length===0)&&r.push("cwd is required and must be a non-empty string")}r.length>0&&n.push({name:o,reason:r.join("; ")})}return n}s(Ln,"humanActionDeclarationIssues");function Fn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n=new Set(Ln(e).map(t=>t.name)),o={};for(const[t,r]of Object.entries(e)){if(n.has(t))continue;const i=r;o[t]={argv:[...i.argv],cwd:i.cwd}}return o}s(Fn,"normalizeHumanActions");function Eo(e){return Mn(e).map(n=>n.message)}s(Eo,"invalidPluginBinReasons");function Io(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[o,t]of Object.entries(e))F(o,"requires")===void 0&&(typeof t!="string"||t.trim()===""||/[\r\n]/.test(t)||(n[o]=t));return n}s(Io,"normalizeRequires");function Po(e){if(e===null||typeof e!="object"||Array.isArray(e))return["requires must be a JSON object mapping a bare executable name to a one-line install hint"];const n=[];for(const[o,t]of Object.entries(e)){const r=F(o,"requires");r!==void 0&&n.push(r),typeof t!="string"||t.trim()===""?n.push(`requires.${o} must be a non-empty one-line install hint`):/[\r\n]/.test(t)&&n.push(`requires.${o} must be a one-line install hint`)}return n}s(Po,"invalidPluginRequiresReasons");function pe(e){if(e===null||typeof e!="object")return null;const n=e,o={};typeof n.whenToUse=="string"&&n.whenToUse.trim()!==""&&(o.whenToUse=n.whenToUse),typeof n.model=="string"&&n.model.trim()!==""&&(o.model=n.model),typeof n.orchestratorModel=="string"&&n.orchestratorModel.trim()!==""&&(o.orchestratorModel=n.orchestratorModel);const t=s(r=>Array.isArray(r)&&r.every(i=>typeof i=="string"),"isStringArray");return t(n.tools)&&(o.tools=n.tools),t(n.extensions)&&(o.extensions=n.extensions),t(n.availableTo)&&(o.availableTo=n.availableTo),Object.keys(o).length>0?o:null}s(pe,"normalizeKindFields");function x(e,n=Qe()){const o={...n};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){const i=pe(r);if(i===null)continue;const c=o[t];c!==void 0?o[t]={...c,...i}:i.whenToUse!==void 0&&(o[t]=i)}return o}s(x,"mergeKinds");function ge(e,n,o){if(n===null)return o;const t=te(e,n).filter(i=>i.enabled&&i.manifest.kinds!==void 0).sort((i,c)=>i.name.localeCompare(c.name));let r=o;for(const i of t)r=x(i.manifest.kinds,r);return r}s(ge,"layerPluginKinds");function To(){const e=[{origin:"config.json",components:Sn("user").page_components}],n=y("user");if(n!==null){const o=te("user",n).filter(t=>t.enabled&&t.manifest.page_components!==void 0).sort((t,r)=>t.name.localeCompare(r.name));for(const t of o){const r=`plugin "${t.name}" page_components`;e.push({origin:`plugin "${t.name}"`,components:se(t.manifest.page_components,r)})}}return wn(e)}s(To,"resolvePageComponents");const H=new Set(["whenToUse","model","orchestratorModel","tools","extensions","availableTo"]);function Co(e){if(e===null||typeof e!="object"||Array.isArray(e))return["kinds must be a JSON object keyed by kind name"];const n=[],o=s(t=>Array.isArray(t)&&t.every(r=>typeof r=="string"),"isStringArray");for(const[t,r]of Object.entries(e)){if(t.trim()===""){n.push("kinds contains an empty kind name");continue}if(r===null||typeof r!="object"||Array.isArray(r)){n.push(`kinds.${t} must be a JSON object`);continue}const i=r;for(const c of Object.keys(i))H.has(c)||n.push(`kinds.${t}.${c} is not a KindConfig field (expected one of: ${[...H].join(", ")})`);i.whenToUse!==void 0&&(typeof i.whenToUse!="string"||i.whenToUse.trim()==="")&&n.push(`kinds.${t}.whenToUse must be a non-empty string`);for(const c of["model","orchestratorModel"])i[c]!==void 0&&(typeof i[c]!="string"||i[c].trim()==="")&&n.push(`kinds.${t}.${c} must be a non-empty string`);for(const c of["tools","extensions","availableTo"])i[c]!==void 0&&!o(i[c])&&n.push(`kinds.${t}.${c} must be an array of strings`);pe(r)===null&&n.push(`kinds.${t} has no valid KindConfig field \u2014 an entry must set at least one of: ${[...H].join(", ")} (whenToUse is required when the kind exists in no lower layer)`)}return n}s(Co,"invalidPluginKindsReasons");function zn(e){if(e===null||typeof e!="object")return null;const n=e;if(typeof n.previewEndpoint!="string"||n.previewEndpoint.trim()===""||typeof n.relayTokenRef!="string"||n.relayTokenRef.trim()==="")return null;const o={previewEndpoint:n.previewEndpoint,relayTokenRef:n.relayTokenRef};return typeof n.cpOrigin=="string"&&n.cpOrigin.trim()!==""&&(o.cpOrigin=n.cpOrigin),o}s(zn,"normalizeRemoteCanvasTarget");function Dn(e,n=en()){const o={...n.targets};if(e!==null&&typeof e=="object"){const t=e;if(t.targets!==null&&typeof t.targets=="object")for(const[r,i]of Object.entries(t.targets)){const c=zn(i);c!==null&&(o[r]=c)}}return{targets:o}}s(Dn,"mergeRemoteCanvas");function ye(e,n){return e===!0?"notify":e===!1?!1:e==="notify"||e==="apply"?e:n}s(ye,"normalizeMode");function O(e,n){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=1?e:n}s(O,"normalizePositiveInteger");function Un(e,n){const o=e!==null&&typeof e=="object"?e:{},t=O(o.warning,n.warning),r=O(o.automaticReviveCap,n.automaticReviveCap);return t<=r?{warning:t,automaticReviveCap:r}:{...n}}s(Un,"mergeBrokerThresholds");function Kn(e,n){return{unattendedParkMs:O((e!==null&&typeof e=="object"?e:{}).unattendedParkMs,n.unattendedParkMs)}}s(Kn,"mergeLifecycle");function qn(e){if(!Array.isArray(e))return[...ee];const n=e.filter(o=>typeof o=="string"&&o.trim()!=="").map(o=>o.trim());return n.length>0?n:[...ee]}s(qn,"normalizeWhipMessages");function Gn(e,n){if(e===null||typeof e!="object")return{...n};const o=e.tcp;return{tcp:typeof o=="string"?o:n.tcp}}s(Gn,"mergeApi");function z(e){const n=k(),o=n.schema_version,t=e.marketplaces===void 0?{}:e.marketplaces,r=e.plugins===void 0?{}:e.plugins,i=e.auto_update,c=i&&typeof i.interval_hours=="number"?i.interval_hours:void 0,u=c!==void 0&&Number.isFinite(c)&&c>=0?c:n.auto_update.interval_hours,d={crtr:ye(i?.crtr,n.auto_update.crtr),content:ye(i?.content,n.auto_update.content),interval_hours:u},l=e.max_panes_per_window,f=O(l,n.max_panes_per_window),a=typeof e.auto_open_child_viewers=="boolean"?e.auto_open_child_viewers:n.auto_open_child_viewers,m=Un(e.brokerThresholds,n.brokerThresholds),h=Kn(e.lifecycle,n.lifecycle),D=typeof e.completion_bell=="boolean"?e.completion_bell:n.completion_bell,p=e.working_gerunds===void 0?[...n.working_gerunds]:kn(e.working_gerunds),be=typeof e.whip_mode=="boolean"?e.whip_mode:n.whip_mode,_e=se(e.page_components),ve=typeof e.page_surface=="boolean"?e.page_surface:n.page_surface,ke=qn(e.whip_messages),we=Ve.includes(e.whip_message_mode)?e.whip_message_mode:n.whip_message_mode,Ae=Ye.includes(e.mouse_mode_default)?e.mouse_mode_default:n.mouse_mode_default,Se=O(e.live_cycles,n.live_cycles),je=Ze.includes(e.condensed_history)?e.condensed_history:n.condensed_history,Re=typeof e.bash_tool_purpose=="boolean"?e.bash_tool_purpose:n.bash_tool_purpose,xe=typeof e.fold_finished_tools=="boolean"?e.fold_finished_tools:n.fold_finished_tools,Oe=typeof e.summarize_tool_calls=="boolean"?e.summarize_tool_calls:n.summarize_tool_calls,Ee=typeof e.detailed_tool_recaps=="boolean"?e.detailed_tool_recaps:n.detailed_tool_recaps,Ie=typeof e.consolidate_replies=="boolean"?e.consolidate_replies:n.consolidate_replies,Pe=En(e.keybindings),Te=Pn(e.tmux_passthrough),Ce=C(e.modelLadders),$e=L(e.providerOptions,n.providerOptions),U=le($(e.modelRoutes)),J=U.complete,Z=M(e.modelRouting,void 0),Me=x(e.kinds),Ne=Dn(e.remoteCanvas),Le=Gn(e.api,n.api),_=e.paths;if(_?.user_files!==void 0&&(typeof _.user_files!="string"||!_.user_files.startsWith("/")))throw new Error("paths.user_files must be an absolute path");const Y=e.store,v=e.runtime;if(v?.person_uids!==void 0&&(!Array.isArray(v.person_uids)||!v.person_uids.every(V=>Number.isSafeInteger(V)&&V>=0)))throw new Error("runtime.person_uids must be an array of nonnegative integer uids");const Fe=N(e.spawnEnv,n.spawnEnv?.allow),ze=Nn(e.bin),De=Fn(e.humanActions);return{bin:ze,humanActions:De,schema_version:o,marketplaces:t,plugins:r,auto_update:d,max_panes_per_window:f,auto_open_child_viewers:a,brokerThresholds:m,lifecycle:h,completion_bell:D,working_gerunds:p,whip_mode:be,page_components:_e,page_surface:ve,whip_messages:ke,whip_message_mode:we,mouse_mode_default:Ae,live_cycles:Se,condensed_history:je,bash_tool_purpose:Re,fold_finished_tools:xe,summarize_tool_calls:Oe,detailed_tool_recaps:Ee,consolidate_replies:Ie,keybindings:Pe,tmux_passthrough:Te,modelLadders:Ce,providerOptions:$e,...Object.keys(J).length>0?{modelRoutes:J}:{},...Object.keys(U.legacy).length>0?{legacyModelRoutes:U.legacy}:{},...Z!==void 0?{modelRouting:Z}:{},kinds:Me,remoteCanvas:Ne,api:Le,..._?{paths:_}:{},...Y?{store:Y}:{},...v?{runtime:v}:{},spawnEnv:Fe}}s(z,"mergeConfig");function Bn(e){return I(e)}s(Bn,"readRawConfigAtRoot");function Wn(e){const n=y(e);return n?Bn(n):null}s(Wn,"readRawScopeConfig");function Hn(e){if(e===null||e==="")return null;try{return I(q(e))}catch{return null}}s(Hn,"readRawProfileConfig");function Jn(e,n){return[...un(e,n)].reverse().map(r=>({root:r,raw:I(r)}))}s(Jn,"readRawProjectScopeConfigs");function he(e=process.cwd(),n=Ke()||null){const o=P[T];if(o)return o.launch;const t=k(),r=Wn("user"),i=Hn(n),c=Jn(e,n);let u=x(r?.kinds,ge("user",y("user"),t.kinds)),d=C(r?.modelLadders,t.modelLadders),l=L(r?.providerOptions,t.providerOptions),f=$(r?.modelRoutes),a=M(r?.modelRouting,void 0),m=N(r?.spawnEnv,t.spawnEnv?.allow);i!==null&&(u=x(i.kinds,u),d=C(i.modelLadders,d),l=L(i.providerOptions,l),f=$(i.modelRoutes,f),a=M(i.modelRouting,a),m=N(i.spawnEnv,m.allow));for(const{root:D,raw:p}of c)u=x(p?.kinds,ge("project",D,u)),p!==null&&(d=C(p.modelLadders,d),l=L(p.providerOptions,l),f=$(p.modelRoutes,f),a=M(p.modelRouting,a),m=N(p.spawnEnv,m.allow));const h=le(f);return{kinds:u,modelLadders:d,providerOptions:l,...Object.keys(h.complete).length>0?{modelRoutes:h.complete}:{},...Object.keys(h.legacy).length>0?{legacyModelRoutes:h.legacy}:{},...a!==void 0?{modelRouting:a}:{},spawnEnv:m}}s(he,"readMergedLaunchConfig");function $o(e){return he().kinds[e]}s($o,"resolveKindConfig");function Mo(e){const n=he().kinds,o=[];for(const[t,r]of Object.entries(n)){const i=t.indexOf("/");if(i===-1)continue;const c=t.slice(0,i),u=r.availableTo??[c];(u.includes("*")||u.includes(e))&&o.push({kind:t,whenToUse:r.whenToUse})}return o.sort((t,r)=>t.kind.localeCompare(r.kind))}s(Mo,"subKindsAvailableTo");function Zn(e,n,o){const t={...e??{}};t.anthropic!==void 0&&(t.anthropic={...t.anthropic}),t.openai!==void 0&&(t.openai={...t.openai}),o.defaultProvider!==n.defaultProvider&&o.defaultProvider!==void 0&&(t.defaultProvider=o.defaultProvider);for(const i of["anthropic","openai"])for(const c of["ultra","strong","medium","light"])o[i][c]!==n[i][c]&&(t[i]={...t[i]??{},[c]:o[i][c]});return t.defaultProvider!==void 0||t.anthropic!==void 0||t.openai!==void 0?t:void 0}s(Zn,"sparseModelLaddersToPersist");function Yn(e,n){return S(e,o=>{const t=z(o),r=z(o);n(r);const i={...o};for(const u of vn)u!=="modelLadders"&&!We(r[u],t[u])&&(i[u]=r[u]);const c=Zn(o.modelLadders,t.modelLadders,r.modelLadders);return c!==void 0?i.modelLadders=c:delete i.modelLadders,{value:r,next:i}})}s(Yn,"mutateConfigAtRoot");function No(e,n){S(e,o=>{const t=o.modelLadders??{},r={...t};n.defaultProvider!==void 0&&(r.defaultProvider=n.defaultProvider);for(const i of["anthropic","openai"])n[i]!==void 0&&(r[i]={...t[i]??{},...n[i]});return{value:void 0,next:{...o,modelLadders:r}}})}s(No,"persistModelLadders");function Lo(e,n,o){if(o!==void 0)Tn(n,o);else if(n.trim()==="")throw new Error("model route id must be a non-empty string");S(e,t=>{const r=fe(t.modelRoutes??{});o===void 0?delete r[n]:j(r,n,{...o,models:{...o.models}});const i={...t};return Object.keys(r).length===0?delete i.modelRoutes:i.modelRoutes=r,{value:void 0,next:i}})}s(Lo,"persistModelRoute");function Vn(e,n){return Yn(e,n)}s(Vn,"updateConfigAtRoot");function Fo(e,n){return Vn(w(e),n)}s(Fo,"updateConfig");function zo(e){const n=e.orchestrator===!0?"orchestratorModel":"model";return S(e.root,o=>{const t={...o.kinds??{},[e.kind]:{...(o.kinds??{})[e.kind],[n]:e.model}};return{value:void 0,next:{...o,kinds:t}}}),{path:b(e.root),field:n}}s(zo,"persistDefaultKindModel");function Do(e,n){const o=Rn(e);return n(o),On(e,o),o}s(Do,"updateState");export{xn as InvalidRawSettingsError,Mn as binDeclarationIssues,wo as brokerConfigSnapshot,_o as configPath,Oo as ensureScopeInitialized,Ln as humanActionDeclarationIssues,ko as installBrokerConfigSnapshot,Eo as invalidPluginBinReasons,Co as invalidPluginKindsReasons,Po as invalidPluginRequiresReasons,Nn as normalizeBin,Fn as normalizeHumanActions,Io as normalizeRequires,Pn as normalizeTmuxPassthrough,zo as persistDefaultKindModel,No as persistModelLadders,Lo as persistModelRoute,Sn as readConfig,jn as readConfigAtRoot,he as readMergedLaunchConfig,Bn as readRawConfigAtRoot,Wn as readRawScopeConfig,Ao as readRawSettingsForTarget,Rn as readState,So as replaceRawSettingsForTarget,$o as resolveKindConfig,To as resolvePageComponents,vo as statePath,Mo as subKindsAvailableTo,Fo as updateConfig,Vn as updateConfigAtRoot,jo as updateRawConfigAtomically,Do as updateState,Ro as writeConfig,xo as writeRawConfig,On as writeState};
@@ -13,6 +13,10 @@ export interface AtomicWriteOptions {
13
13
  mode?: number;
14
14
  /** Require the parent to exist, rather than recreate a removed project root. */
15
15
  createParent?: boolean;
16
+ /** Fsync the temp file before the rename and the parent directory after it. Without this a power loss
17
+ * or a killed VM can persist the rename but not the data — the 0-byte file a plain
18
+ * temp-plus-rename still leaves behind. */
19
+ durable?: boolean;
16
20
  }
17
21
  export declare function atomicWriteJson(path: string, data: unknown, opts?: AtomicWriteOptions): void;
18
22
  /** Force a file's written bytes to stable storage. */
@@ -23,6 +27,9 @@ export declare function syncDirectory(path: string): void;
23
27
  * tmux inbox watcher, or crtrd serving a ticket read — sees either the old
24
28
  * bytes or the new ones and never a truncated file mid-write. */
25
29
  export declare function atomicWriteText(path: string, content: string | Uint8Array, opts?: AtomicWriteOptions): void;
30
+ /** Fsync every file and directory under `root` (and `root` itself), so a tree about to be renamed into place
31
+ * reaches stable storage before the rename can. */
32
+ export declare function syncTree(root: string): void;
26
33
  export declare function readJson<T = unknown>(path: string): T;
27
34
  export declare function readJsonIfExists<T = unknown>(path: string): T | null;
28
35
  /** Missing or corrupt reads as absent, so a crash artifact never throws. */
@@ -1,3 +1,3 @@
1
- var v=Object.defineProperty;var n=(r,t)=>v(r,"name",{value:t,configurable:!0});import{existsSync as c,lstatSync as D,mkdirSync as E,readdirSync as y,readFileSync as f,rmSync as S,statSync as d,symlinkSync as W,writeFileSync as a,cpSync as w,readlinkSync as g,renameSync as I,chmodSync as $,realpathSync as b,closeSync as J,fsyncSync as O,openSync as T}from"node:fs";import{dirname as s,join as h,relative as L,sep as j}from"node:path";import{homedir as k,platform as F}from"node:os";function N(r){try{return b(r)}catch{return r}}n(N,"realpathOrSelf");function Q(r,t=k()){return r==="~"?t:r.startsWith("~/")||F()==="win32"&&r.startsWith("~\\")?h(t,r.slice(2)):r}n(Q,"expandTilde");function R(r,t=k()){return r===t?"~":r.startsWith(t+j)?"~"+r.slice(t.length):r}n(R,"tildify");function l(r){E(r,{recursive:!0})}n(l,"ensureDir");function U(r,t){l(s(r)),a(r,JSON.stringify(t,null,2)+`
2
- `,"utf8")}n(U,"writeJson");function V(r,t,i){C(r,JSON.stringify(t,null,2)+`
3
- `,i)}n(V,"atomicWriteJson");function X(r){const t=T(r,"r");try{O(t)}finally{J(t)}}n(X,"syncFile");function Y(r){const t=T(r,"r");try{O(t)}finally{J(t)}}n(Y,"syncDirectory");function C(r,t,i){i?.createParent!==!1&&l(s(r));const e=`${r}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`,o=i?.mode??(c(r)?d(r).mode&511:384);try{a(e,t,{mode:o}),$(e,o),I(e,r)}finally{c(e)&&S(e,{force:!0})}}n(C,"atomicWriteText");function M(r){return JSON.parse(f(r,"utf8"))}n(M,"readJson");function Z(r){try{return M(r)}catch(t){if(t.code==="ENOENT")return null;throw t}}n(Z,"readJsonIfExists");function _(r){try{return JSON.parse(f(r,"utf8"))}catch{return null}}n(_,"readJsonOrNull");function P(r){return c(r)?f(r,"utf8"):null}n(P,"readTextIfExists");function rr(r){return f(r,"utf8")}n(rr,"readText");function tr(r,t){l(s(r)),a(r,t,"utf8")}n(tr,"writeText");function q(r){try{return d(r).isDirectory()}catch{return!1}}n(q,"isDir");function z(r){try{return D(r).isSymbolicLink()}catch{return!1}}n(z,"isSymlink");function nr(r){return c(r)}n(nr,"pathExists");function er(r){return c(r)?y(r,{withFileTypes:!0}).filter(t=>t.isDirectory()||t.isSymbolicLink()).map(t=>t.name):[]}n(er,"listDirs");function ir(r){return c(r)?y(r):[]}n(ir,"listEntries");function A(r){!c(r)&&!z(r)||S(r,{recursive:!0,force:!0})}n(A,"removePath");function or(r,t,i={}){l(s(t)),A(t);const e=F()==="win32";if(!i.noSymlink&&!e){const o=L(N(s(t)),N(r));try{return W(o,t,q(r)?"dir":"file"),"symlink"}catch{return w(r,t,{recursive:!0}),"copy"}}return w(r,t,{recursive:!0}),"copy"}n(or,"linkOrCopy");function cr(r){try{return g(r)}catch{return null}}n(cr,"readSymlinkTarget");function ur(r,t=()=>!0,i=()=>!1){const e=[];if(!c(r))return e;const o=[r];for(;o.length;){const x=o.pop();let p;try{p=y(x,{withFileTypes:!0})}catch{continue}for(const u of p){const m=h(x,u.name);u.isDirectory()?i(u.name)||o.push(m):u.isFile()&&t(u.name)&&e.push(m)}}return e}n(ur,"walkFiles");function sr(){return new Date().toISOString()}n(sr,"nowIso");export{V as atomicWriteJson,C as atomicWriteText,l as ensureDir,Q as expandTilde,q as isDir,z as isSymlink,or as linkOrCopy,er as listDirs,ir as listEntries,sr as nowIso,nr as pathExists,M as readJson,Z as readJsonIfExists,_ as readJsonOrNull,cr as readSymlinkTarget,rr as readText,P as readTextIfExists,N as realpathOrSelf,A as removePath,Y as syncDirectory,X as syncFile,R as tildify,ur as walkFiles,U as writeJson,tr as writeText};
1
+ var E=Object.defineProperty;var e=(r,t)=>E(r,"name",{value:t,configurable:!0});import{existsSync as c,lstatSync as W,mkdirSync as b,readdirSync as f,readFileSync as l,rmSync as S,statSync as w,symlinkSync as g,writeFileSync as a,cpSync as T,readlinkSync as I,renameSync as $,chmodSync as L,realpathSync as j,closeSync as F,fsyncSync as J,openSync as O}from"node:fs";import{dirname as u,join as x,relative as C,sep as M}from"node:path";import{homedir as h,platform as k}from"node:os";function D(r){try{return j(r)}catch{return r}}e(D,"realpathOrSelf");function V(r,t=h()){return r==="~"?t:r.startsWith("~/")||k()==="win32"&&r.startsWith("~\\")?x(t,r.slice(2)):r}e(V,"expandTilde");function X(r,t=h()){return r===t?"~":r.startsWith(t+M)?"~"+r.slice(t.length):r}e(X,"tildify");function y(r){b(r,{recursive:!0})}e(y,"ensureDir");function Y(r,t){y(u(r)),a(r,JSON.stringify(t,null,2)+`
2
+ `,"utf8")}e(Y,"writeJson");function Z(r,t,n){q(r,JSON.stringify(t,null,2)+`
3
+ `,n)}e(Z,"atomicWriteJson");function N(r){const t=O(r,"r");try{J(t)}finally{F(t)}}e(N,"syncFile");function v(r){const t=O(r,"r");try{J(t)}finally{F(t)}}e(v,"syncDirectory");function q(r,t,n){n?.createParent!==!1&&y(u(r));const i=`${r}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`,o=n?.mode??(c(r)?w(r).mode&511:384);try{a(i,t,{mode:o}),L(i,o),n?.durable===!0&&N(i),$(i,r),n?.durable===!0&&v(u(r))}finally{c(i)&&S(i,{force:!0})}}e(q,"atomicWriteText");function z(r){for(const t of f(r,{withFileTypes:!0})){const n=x(r,t.name);t.isDirectory()?z(n):t.isFile()&&N(n)}v(r)}e(z,"syncTree");function A(r){return JSON.parse(l(r,"utf8"))}e(A,"readJson");function _(r){try{return A(r)}catch(t){if(t.code==="ENOENT")return null;throw t}}e(_,"readJsonIfExists");function P(r){try{return JSON.parse(l(r,"utf8"))}catch{return null}}e(P,"readJsonOrNull");function rr(r){return c(r)?l(r,"utf8"):null}e(rr,"readTextIfExists");function tr(r){return l(r,"utf8")}e(tr,"readText");function er(r,t){y(u(r)),a(r,t,"utf8")}e(er,"writeText");function B(r){try{return w(r).isDirectory()}catch{return!1}}e(B,"isDir");function G(r){try{return W(r).isSymbolicLink()}catch{return!1}}e(G,"isSymlink");function nr(r){return c(r)}e(nr,"pathExists");function ir(r){return c(r)?f(r,{withFileTypes:!0}).filter(t=>t.isDirectory()||t.isSymbolicLink()).map(t=>t.name):[]}e(ir,"listDirs");function or(r){return c(r)?f(r):[]}e(or,"listEntries");function H(r){!c(r)&&!G(r)||S(r,{recursive:!0,force:!0})}e(H,"removePath");function cr(r,t,n={}){y(u(t)),H(t);const i=k()==="win32";if(!n.noSymlink&&!i){const o=C(D(u(t)),D(r));try{return g(o,t,B(r)?"dir":"file"),"symlink"}catch{return T(r,t,{recursive:!0}),"copy"}}return T(r,t,{recursive:!0}),"copy"}e(cr,"linkOrCopy");function ur(r){try{return I(r)}catch{return null}}e(ur,"readSymlinkTarget");function sr(r,t=()=>!0,n=()=>!1){const i=[];if(!c(r))return i;const o=[r];for(;o.length;){const m=o.pop();let p;try{p=f(m,{withFileTypes:!0})}catch{continue}for(const s of p){const d=x(m,s.name);s.isDirectory()?n(s.name)||o.push(d):s.isFile()&&t(s.name)&&i.push(d)}}return i}e(sr,"walkFiles");function fr(){return new Date().toISOString()}e(fr,"nowIso");export{Z as atomicWriteJson,q as atomicWriteText,y as ensureDir,V as expandTilde,B as isDir,G as isSymlink,cr as linkOrCopy,ir as listDirs,or as listEntries,fr as nowIso,nr as pathExists,A as readJson,_ as readJsonIfExists,P as readJsonOrNull,ur as readSymlinkTarget,tr as readText,rr as readTextIfExists,D as realpathOrSelf,H as removePath,v as syncDirectory,N as syncFile,z as syncTree,X as tildify,sr as walkFiles,Y as writeJson,er as writeText};
@@ -4,6 +4,7 @@
4
4
  * A plugin is an app: its documents belong to the app `<plugin>` (identity `app:<plugin>`,
5
5
  * the one `ownerForGrantee` gives), and a document with a `source_path` is shipped. */
6
6
  import type { DatabaseSync } from 'node:sqlite';
7
+ import { type QuarantinedPlugin } from '../plugin-quarantine.js';
7
8
  export interface PackageSource {
8
9
  plugin: string;
9
10
  dir: string;
@@ -23,10 +24,12 @@ export interface PackageReconcileReport {
23
24
  path: string;
24
25
  error: string;
25
26
  }>;
27
+ /** plugin directories with an unreadable manifest or command file, moved aside before reconciling */
28
+ quarantined: QuarantinedPlugin[];
26
29
  }
27
30
  /** Converge every package's documents onto its files. Run inside the caller's transaction.
28
31
  * Idempotent: a second run over unchanged files changes no document, revision or rule (it re-derives
29
32
  * each document's link edges, which land the same). A changed body replaces the
30
33
  * document's single revision and publishes `edited`; a vanished file (or plugin) marks its
31
34
  * document deleted and publishes `deleted`. */
32
- export declare function reconcilePackageDocuments(db: DatabaseSync, sources?: readonly PackageSource[]): PackageReconcileReport;
35
+ export declare function reconcilePackageDocuments(db: DatabaseSync, explicitSources?: readonly PackageSource[]): PackageReconcileReport;
@@ -1,5 +1,5 @@
1
- var D=Object.defineProperty;var a=(e,s)=>D(e,"name",{value:s,configurable:!0});import{prepared as m}from"../sqlite-statements.js";import{createHash as _,randomUUID as v}from"node:crypto";import{CrtrError as N}from"../errors.js";import{listInstalledPlugins as I}from"../resolver.js";import{builtinMemoryRoot as A,pluginMemoryDir as L}from"../scope.js";import{documentFields as k}from"../../migrations/004-canvas-documents/fields.js";import{applyDocumentFields as j,deleteDocument as T,documentRecord as R,heads as x,importDocument as U,linksIn as F,moveDocument as b}from"./documents.js";import{isShipped as J}from"./access.js";import{setLinks as C}from"./edges.js";import{publish as H}from"./events.js";import{resolveName as W}from"./names.js";import{packageFiles as M}from"./package-files.js";import{ensureOwner as P,getByRowid as E,setObjectKind as K}from"./objects.js";import{checkoutsForCwd as $}from"./repo-sync/repos.js";import{rulesOf as B}from"./rules.js";function q(){return[{plugin:"crtr",dir:A()},...I("user").filter(e=>e.enabled).map(e=>({plugin:e.name,dir:L(e)}))]}a(q,"packageSources");const z=a(e=>_("sha256").update(e,"utf8").digest("hex"),"sha256");function pe(e,s=q()){const o={created:0,updated:0,deleted:0,conflicts:[],invalid:[]},c=a(t=>$(e,t),"reposFn"),p=new Set,n=[],r=new Set;for(const t of s){let i;try{if(r.has(t.plugin))throw new N("conflict",`plugin "${t.plugin}" is already provided by another package`);r.add(t.plugin),i=P(e,"app","app",`app:${t.plugin}`,t.plugin)}catch(u){if(!(u instanceof N)||u.code!=="conflict")throw u;o.conflicts.push({plugin:t.plugin,message:u.message});continue}p.add(i.rowid);const{files:d,invalid:w}=M(t.dir);o.invalid.push(...w);const g=new Set;for(const u of d){const h=Q(e,i,u,o,c);g.add(h),n.push({owner:i,rowid:h,body:u.body})}O(e,i,g,o,c)}const f=m(e,`SELECT DISTINCT o.owner AS rowid FROM objects o JOIN documents d ON d.object = o.rowid
2
- WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{rowid:t}of f)p.has(t)||O(e,E(e,t),new Set,o,c);for(const{owner:t,rowid:i,body:d}of n)G(e,t,E(e,i),d);return o}a(pe,"reconcilePackageDocuments");function G(e,s,o,c){if(o.deleted_at!==null)return;const p={class:"app",app:s,scopes:[]},n=[];for(const t of F(c)){const i=W(e,p,t);"object"in i&&i.object.rowid!==o.rowid&&J(e,i.object)&&n.push({written_as:t,dst:i.object})}const r=a(t=>JSON.stringify(t.map(i=>[i.dst,i.written_as]).sort()),"key"),f=m(e,"SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ?").all(o.rowid);r(f)!==r(n.map(t=>({dst:t.dst.rowid,written_as:t.written_as})))&&C(e,o,n)}a(G,"deriveLinks");function Q(e,s,o,c,p){const n=k(o.frontmatter,o.body,{missingPrivacy:"public"}),r={preview:n.preview.trim()===""?o.name:n.preview,summary:n.summary,whyItExists:n.whyItExists,unlisted:n.unlisted,rules:n.rules,lintIgnore:Array.isArray(n.lintIgnore)?n.lintIgnore.filter(l=>typeof l=="string"):null,extensions:n.extensions!==null&&typeof n.extensions=="object"&&!Array.isArray(n.extensions)?n.extensions:null},f=z(o.body);let t=m(e,`SELECT o.* FROM objects o JOIN documents d ON d.object = o.rowid
3
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path = ?`).get(s.rowid,o.path);if(t||(t=m(e,`SELECT o.* FROM objects o JOIN documents d ON d.object = o.rowid
4
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(s.rowid,o.name),t&&m(e,"UPDATE documents SET source_path = ? WHERE object = ?").run(o.path,t.rowid)),!t){const l=U(e,{owner:s,name:o.name,kind:n.kind,privacy:"public",...r,revisions:[{body_hash:f,author:s,rationale:null,created_at:new Date().toISOString()}]});return m(e,"UPDATE documents SET source_path = ? WHERE object = ?").run(o.path,l.rowid),c.created+=1,l.rowid}const i=t.name!==o.name||t.kind!==n.kind;t.name!==o.name&&(t=b(e,null,t,{name:o.name})),t.kind!==n.kind&&(K(e,t,n.kind),t=E(e,t.rowid));const d=R(e,t),[w]=x(e,t),g=w?.body_hash!==f,u=t.preview!==r.preview||d.summary!==r.summary||d.why_it_exists!==r.whyItExists||d.unlisted!==r.unlisted||JSON.stringify(d.lint_ignore)!==JSON.stringify(r.lintIgnore)||JSON.stringify(d.extensions)!==JSON.stringify(r.extensions)||JSON.stringify(B(e,t).map(S))!==JSON.stringify(r.rules.map(S));if(!g&&!u&&!i)return t.rowid;const h=j(e,t,r);if(g&&w){const l=v(),y=w.number+1;m(e,"UPDATE revisions SET rev_id = ?, number = ?, body_hash = ?, created_at = ? WHERE rowid = ?").run(l,y,f,new Date().toISOString(),w.rowid),m(e,"UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(l,y,h.rowid),H(e,{event:"edited",object:h,actor:s,pointer:l,attrs:{revision:y,from_revision:w.number,rationale:"package file changed"}},p)}return c.updated+=1,h.rowid}a(Q,"convergeFile");function S(e){return[e.on,e.deliver,e.match??null,e.matchFrontmatter??null,e.if??null]}a(S,"ruleKey");function O(e,s,o,c,p){const n=m(e,`SELECT o.* FROM objects o JOIN documents d ON d.object = o.rowid
5
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(s.rowid);for(const r of n)o.has(r.rowid)||(T(e,null,r,p),c.deleted+=1)}a(O,"deleteMissing");export{q as packageSources,pe as reconcilePackageDocuments};
1
+ var O=Object.defineProperty;var u=(e,s)=>O(e,"name",{value:s,configurable:!0});import{prepared as p}from"../sqlite-statements.js";import{createHash as _,randomUUID as D}from"node:crypto";import{CrtrError as N}from"../errors.js";import{emitEvent as I}from"../events/emit.js";import{quarantineBrokenPlugins as A}from"../plugin-quarantine.js";import{listInstalledPlugins as k}from"../resolver.js";import{builtinMemoryRoot as L,pluginMemoryDir as T}from"../scope.js";import{documentFields as j}from"../../migrations/004-canvas-documents/fields.js";import{applyDocumentFields as R,deleteDocument as x,documentRecord as U,heads as F,importDocument as b,linksIn as J,moveDocument as C}from"./documents.js";import{isShipped as H}from"./access.js";import{setLinks as W}from"./edges.js";import{publish as M}from"./events.js";import{resolveName as P}from"./names.js";import{packageFiles as q}from"./package-files.js";import{ensureOwner as B,getByRowid as E,setObjectKind as K}from"./objects.js";import{checkoutsForCwd as $}from"./repo-sync/repos.js";import{rulesOf as z}from"./rules.js";function G(){return[{plugin:"crtr",dir:L()},...k("user").filter(e=>e.enabled).map(e=>({plugin:e.name,dir:T(e)}))]}u(G,"packageSources");const Q=u(e=>_("sha256").update(e,"utf8").digest("hex"),"sha256");function ge(e,s){const n={created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:[]};if(s===void 0){n.quarantined=A();for(const t of n.quarantined)I({level:"error",event:"daemon.plugins.quarantined",fields:{plugin:t.name,file:t.file,reason:t.reason,...t.movedTo!==void 0?{moved_to:t.movedTo}:{}}})}const f=s??G(),m=u(t=>$(e,t),"reposFn"),r=new Set,i=[],w=new Set;for(const t of f){let a;try{if(w.has(t.plugin))throw new N("conflict",`plugin "${t.plugin}" is already provided by another package`);w.add(t.plugin),a=B(e,"app","app",`app:${t.plugin}`,t.plugin)}catch(d){if(!(d instanceof N)||d.code!=="conflict")throw d;n.conflicts.push({plugin:t.plugin,message:d.message});continue}r.add(a.rowid);const{files:l,invalid:g}=q(t.dir);n.invalid.push(...g);const h=new Set;for(const d of l){const c=X(e,a,d,n,m);h.add(c),i.push({owner:a,rowid:c,body:d.body})}v(e,a,h,n,m)}const o=p(e,`SELECT DISTINCT o.owner AS rowid FROM objects o JOIN documents d ON d.object = o.rowid
2
+ WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{rowid:t}of o)r.has(t)||v(e,E(e,t),new Set,n,m);for(const{owner:t,rowid:a,body:l}of i)V(e,t,E(e,a),l);return n}u(ge,"reconcilePackageDocuments");function V(e,s,n,f){if(n.deleted_at!==null)return;const m={class:"app",app:s,scopes:[]},r=[];for(const o of J(f)){const t=P(e,m,o);"object"in t&&t.object.rowid!==n.rowid&&H(e,t.object)&&r.push({written_as:o,dst:t.object})}const i=u(o=>JSON.stringify(o.map(t=>[t.dst,t.written_as]).sort()),"key"),w=p(e,"SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ?").all(n.rowid);i(w)!==i(r.map(o=>({dst:o.dst.rowid,written_as:o.written_as})))&&W(e,n,r)}u(V,"deriveLinks");function X(e,s,n,f,m){const r=j(n.frontmatter,n.body,{missingPrivacy:"public"}),i={preview:r.preview.trim()===""?n.name:r.preview,summary:r.summary,whyItExists:r.whyItExists,unlisted:r.unlisted,rules:r.rules,lintIgnore:Array.isArray(r.lintIgnore)?r.lintIgnore.filter(c=>typeof c=="string"):null,extensions:r.extensions!==null&&typeof r.extensions=="object"&&!Array.isArray(r.extensions)?r.extensions:null},w=Q(n.body);let o=p(e,`SELECT o.* FROM objects o JOIN documents d ON d.object = o.rowid
3
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path = ?`).get(s.rowid,n.path);if(o||(o=p(e,`SELECT o.* FROM objects o JOIN documents d ON d.object = o.rowid
4
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(s.rowid,n.name),o&&p(e,"UPDATE documents SET source_path = ? WHERE object = ?").run(n.path,o.rowid)),!o){const c=b(e,{owner:s,name:n.name,kind:r.kind,privacy:"public",...i,revisions:[{body_hash:w,author:s,rationale:null,created_at:new Date().toISOString()}]});return p(e,"UPDATE documents SET source_path = ? WHERE object = ?").run(n.path,c.rowid),f.created+=1,c.rowid}const t=o.name!==n.name||o.kind!==r.kind;o.name!==n.name&&(o=C(e,null,o,{name:n.name})),o.kind!==r.kind&&(K(e,o,r.kind),o=E(e,o.rowid));const a=U(e,o),[l]=F(e,o),g=l?.body_hash!==w,h=o.preview!==i.preview||a.summary!==i.summary||a.why_it_exists!==i.whyItExists||a.unlisted!==i.unlisted||JSON.stringify(a.lint_ignore)!==JSON.stringify(i.lintIgnore)||JSON.stringify(a.extensions)!==JSON.stringify(i.extensions)||JSON.stringify(z(e,o).map(S))!==JSON.stringify(i.rules.map(S));if(!g&&!h&&!t)return o.rowid;const d=R(e,o,i);if(g&&l){const c=D(),y=l.number+1;p(e,"UPDATE revisions SET rev_id = ?, number = ?, body_hash = ?, created_at = ? WHERE rowid = ?").run(c,y,w,new Date().toISOString(),l.rowid),p(e,"UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(c,y,d.rowid),M(e,{event:"edited",object:d,actor:s,pointer:c,attrs:{revision:y,from_revision:l.number,rationale:"package file changed"}},m)}return f.updated+=1,d.rowid}u(X,"convergeFile");function S(e){return[e.on,e.deliver,e.match??null,e.matchFrontmatter??null,e.if??null]}u(S,"ruleKey");function v(e,s,n,f,m){const r=p(e,`SELECT o.* FROM objects o JOIN documents d ON d.object = o.rowid
5
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(s.rowid);for(const i of r)n.has(i.rowid)||(x(e,null,i,m),f.deleted+=1)}u(v,"deleteMissing");export{G as packageSources,ge as reconcilePackageDocuments};
@@ -1,6 +1,6 @@
1
1
  import type { BindingDefinition, SafetyRequirement } from './types.js';
2
2
  import type { AttachBindingId, AttachMenuBindingId } from './attach-control.js';
3
- type TmuxMenuAction = 'open' | 'attach' | 'show' | 'view-settings' | 'provider-options' | 'fold-tools' | 'metadata' | 'promote' | 'demote-mode' | 'resume' | 'fork' | 'demote' | 'make-resident' | 'detach' | 'close-subtree' | 'background-bash' | 'file-review' | 'pin' | 'reload-viewer' | 'recap' | 'context' | 'context-admin' | 'providers' | 'plugins' | 'analytics' | 'graph' | 'focus-manager' | 'copy-id' | 'issues';
3
+ type TmuxMenuAction = 'open' | 'attach' | 'show' | 'view-settings' | 'provider-options' | 'fold-tools' | 'show-working' | 'metadata' | 'promote' | 'demote-mode' | 'resume' | 'fork' | 'demote' | 'make-resident' | 'detach' | 'close-subtree' | 'background-bash' | 'file-review' | 'pin' | 'reload-viewer' | 'recap' | 'context' | 'context-admin' | 'providers' | 'plugins' | 'analytics' | 'graph' | 'focus-manager' | 'copy-id' | 'issues';
4
4
  type TmuxMenuEntryAction = Exclude<TmuxMenuAction, 'open' | 'attach'> | `issue.${'idea' | 'bug'}`;
5
5
  type GraphAction = 'down' | 'up' | 'first' | 'last' | 'collapse-or-manager' | 'expand-or-child' | 'focus' | 'revive' | 'focus-manager' | 'close-subtree' | 'dismiss';
6
6
  type BrowseAction = 'quit' | 'down' | 'up' | 'first' | 'last' | 'expand-or-child' | 'collapse-or-parent' | 'toggle-fold' | 'tab-next' | 'tab-previous' | `tab.${1 | 2 | 3 | 4 | 5}` | 'close-subtree' | 'confirm-close' | 'sort' | 'scope' | 'profile' | 'residents' | 'preview' | 'report' | 'pin' | 'search' | 'revive-now' | 'focus' | 'focus-background' | 'yank-id';
@@ -1 +1 @@
1
- var g=Object.defineProperty;var i=(t,n)=>g(t,"name",{value:n,configurable:!0});function f(t,n,s,c,p,l,d,u,m){return Object.freeze({id:t,group:n,name:s,description:c,defaults:Object.freeze([...p]),inputKind:l,contexts:Object.freeze([...d]),...u===void 0?{}:{safety:u},...m===void 0?{}:{pair:Object.freeze({...m})}})}i(f,"binding");const e=f;function r(t,n,s,c,p){const l=`crtr.tmux.menu.${t}`,d=`crtr.tmux.menu-global.${t}`;return[e(l,"Global tmux",n,s,c,"tmux-menu-selector",p,void 0,{field:"menu",pairedId:d}),e(d,"Global tmux",n,s,[],"tmux-global-action",["tmux.root"],void 0,{field:"global",pairedId:l})]}i(r,"menuEntry");function o(t,n,s,c,p,l){return[e(t,"Attach",s,c,p,"tmux-menu-selector",["tmux.menu.attach"],void 0,{field:"menu",pairedId:n}),e(n,"Attach",s,c,l,"host-global",a,void 0,{field:"direct",pairedId:t})]}i(o,"attachEntry");const a=["attach","graph","graph.confirm"],b=["setup.login","setup.list","setup.input","setup.keybindings","setup.keybindings.edit","setup.keybindings.filter","setup.models"],h=["setup.login","setup.list","setup.keybindings","setup.models"],w=Object.freeze([e("crtr.tmux.menu.open","Global tmux","Open crouter menu","Open the crouter action menu.",["alt+c"],"host-global",["tmux.root"]),e("crtr.tmux.menu.attach","Global tmux","Attach actions","Open the attach-viewer action submenu.",["a"],"tmux-menu-selector",["tmux.menu"]),...r("promote","Promote to orchestrator","Promote the focused node to orchestrator \u2014 it holds a roadmap and delegates.",["o"],["tmux.menu"]),...r("demote-mode","Demote to base node","Demote the focused orchestrator back to a base node \u2014 hands-on again in its own window, no roadmap to drive.",["shift+o"],["tmux.menu"]),...r("resume","Find node","Open the canvas navigator and go to the chosen node.",["/"],["tmux.menu"]),...r("fork","Fork node","Fork the focused node into an independent idle root.",["f"],["tmux.menu"]),...r("demote","Demote to terminal","Demote the focused node to terminal \u2014 it keeps running, owes a final up the spine, and reaps when done.",["d"],["tmux.menu"]),...r("make-resident","Make resident","Flip the focused node to resident \u2014 interactable, never forced to submit a final, and wakeable while attached or awaiting something live.",["shift+r"],["tmux.menu"]),...r("detach","Demote and close viewer","Demote the focused node and close its viewer; its broker keeps running off-screen.",["shift+d"],["tmux.menu"]),...r("close-subtree","Close subtree","Close the focused node and its subtree.",["x"],["tmux.menu"]),...r("background-bash","Background running bash","Hand the focused node\u2019s running bash command to its background job system.",["b"],["tmux.menu"]),...r("file-review","Review a file","Open a line-anchored review of a file named in the transcript.",["v"],["tmux.menu"]),...r("pin","Pin conversation","Pin or unpin this conversation in the canvas navigator.",["p"],["tmux.menu"]),...r("show","Inspect node","Open the focused node\u2019s inspection menu.",["i"],["tmux.menu"]),...r("view-settings","Display settings","Open the viewer display-settings menu.",["z"],["tmux.menu"]),...r("fold-tools","Fold finished tool calls","Fold every finished tool call in the transcript to its single call line.",["f"],["tmux.menu.view-settings"]),...r("metadata","Show metadata","Show the focused node\u2019s metadata dossier.",["m"],["tmux.menu.show"]),...r("context","Browse context","Browse the focused node context.",["c"],["tmux.menu.show"]),...r("providers","Open settings","Open the crouter settings panel.",[","],["tmux.menu"]),...r("provider-options","Provider options","Open provider options \u2014 how an authenticated provider serves your turns, including Codex fast mode.",["."],["tmux.menu"]),...r("plugins","Browse plugins","Open the crouter plugin browser.",["l"],["tmux.menu"]),...r("analytics","Analytics","Open the Analytics page: how agents worked in the last 24 hours or 7 days.",["shift+a"],["tmux.menu"]),...r("context-admin","Context surface","Open the context surface: which documents each event delivers, and the surfaces, gates, and caps that decide it.",["shift+c"],["tmux.menu"]),...r("graph","Canvas graph","Open the canvas graph.",["g"],["tmux.menu"]),...r("focus-manager","Go to manager","Focus this node\u2019s manager.",["h"],["tmux.menu"]),...r("copy-id","Copy node id","Copy the focused node id to the system clipboard.",["y"],["tmux.menu"]),...r("reload-viewer","Reload viewer","Respawn this pane\u2019s viewer onto the current runtime generation.",["r"],["tmux.menu"]),...r("recap","Generate recap","Generate the focused node\u2019s Goal and State recap and refresh its generated title.",["s"],["tmux.menu"]),...r("issues","Open create menu","Open the create submenu.",["c"],["tmux.menu"]),...r("issue.idea","Create idea","Create an idea issue.",["i"],["tmux.menu.issue"]),...r("issue.bug","Create bug","Create a bug issue.",["b"],["tmux.menu.issue"]),e("crtr.tmux.node.next","Global tmux","Next node","Focus the next canvas node.",["alt+]"],"host-global",["tmux.root"]),e("crtr.tmux.node.previous","Global tmux","Previous node","Focus the previous canvas node.",["alt+["],"host-global",["tmux.root"]),e("crtr.tmux.inbox.toggle","Global tmux","Toggle inbox","Open or close the human inbox in the focused viewer.",["alt+i"],"host-global",["tmux.root"]),...o("crtr.tmux.menu.attach.detach","crtr.attach.detach","Detach","Detach from the node.",["d"],["ctrl+d"]),...o("crtr.tmux.menu.attach.clear-or-detach","crtr.attach.clear-or-detach","Clear input (cancel agent on repeat)","Clear the input; press again within a second to cancel the running agent turn. Detach lives on its own binding.",["c"],["ctrl+c"]),...o("crtr.tmux.menu.attach.graph.toggle","crtr.attach.graph.toggle","Toggle graph","Open or close the graph overlay.",["g"],["alt+g"]),...o("crtr.tmux.menu.attach.help.toggle","crtr.attach.help.toggle","Help","Open the attach help overlay.",["?"],["alt+shift+k"]),...o("crtr.tmux.menu.attach.inbox.toggle","crtr.attach.inbox.toggle","Toggle inbox","Open or close the human inbox surface in this viewer.",["i"],[]),...o("crtr.tmux.menu.attach.inbox-strip","crtr.attach.inbox-strip","Pending requests","Jump to the pending human-request strip at the top of the pane.",["u"],["alt+u"]),...o("crtr.tmux.menu.attach.model-ladder.next","crtr.attach.model-ladder.next","Next model","Move to the next model ladder rung.",["]"],["alt+m"]),...o("crtr.tmux.menu.attach.model-ladder.previous","crtr.attach.model-ladder.previous","Previous model","Move to the previous model ladder rung.",["["],["alt+shift+m"]),...o("crtr.tmux.menu.attach.command.inspect","crtr.attach.command.inspect","Inspect loaded command","Open the loaded slash command\u2019s expanded prompt.",["h"],["alt+shift+h"]),...o("crtr.tmux.menu.attach.file-review","crtr.attach.file-review","Review a file","Open a line-anchored review of a file named in the transcript.",[],["alt+shift+r"]),...o("crtr.tmux.menu.attach.profile-files","crtr.attach.profile-files","Search profile files","Fuzzy-match path segments across the node family\u2019s working directories, their context directories (files), profile projects and the profile\u2019s own directory; Ctrl+Y copies a selected path.",["f"],["ctrl+f"]),...o("crtr.tmux.menu.attach.search","crtr.attach.search","Search transcript","Search the transcript as it is currently displayed \u2014 folded tool output is not searched until it is unfolded.",["/"],["alt+/"]),...o("crtr.tmux.menu.attach.whip","crtr.attach.whip","Whip agent","Interrupt the agent and send a playful command.",["w"],[]),e("crtr.attach.scroll.up","Attach","Scroll up","Scroll the transcript one line up.",["shift+up"],"terminal",a),e("crtr.attach.scroll.down","Attach","Scroll down","Scroll the transcript one line down.",["shift+down"],"terminal",a),e("crtr.attach.scroll.page-up","Attach","Scroll page up","Scroll the transcript one page up.",["pageup"],"terminal",a),e("crtr.attach.scroll.page-down","Attach","Scroll page down","Scroll the transcript one page down.",["pagedown"],"terminal",a),e("crtr.attach.scroll.live","Attach","Return to live","Return the transcript to the live bottom.",["shift+end"],"terminal",a),e("crtr.attach.mouse.toggle","Attach","Toggle mouse wheel scrolling","Toggle mouse wheel scrolling on or off.",["alt+shift+o"],"terminal",a),e("crtr.attach.copy.message","Attach","Copy visible message","Copy the newest message visible in the transcript viewport.",["alt+shift+y"],"terminal",a),e("crtr.attach.search.next","Attach","Next match","Move to the next transcript search match.",["n"],"terminal",["attach.search"]),e("crtr.attach.search.previous","Attach","Previous match","Move to the previous transcript search match.",["shift+n"],"terminal",["attach.search"]),e("crtr.attach.search.edit","Attach","Edit query","Return to editing the transcript search query.",["/"],"terminal",["attach.search"]),e("crtr.attach.search.close","Attach","Close search","Leave transcript search and clear its highlights.",["escape"],"terminal",["attach.search"],"dismiss-modal"),e("crtr.graph.down","GRAPH","Move down","Move to the next graph row.",["j","down"],"terminal",["graph"]),e("crtr.graph.up","GRAPH","Move up","Move to the previous graph row.",["k","up"],"terminal",["graph"]),e("crtr.graph.first","GRAPH","First row","Move to the first graph row.",["g"],"terminal",["graph"]),e("crtr.graph.last","GRAPH","Last row","Move to the last graph row.",["shift+g"],"terminal",["graph"]),e("crtr.graph.collapse-or-manager","GRAPH","Collapse or manager","Collapse a node or move to its manager.",["h","left"],"terminal",["graph"]),e("crtr.graph.expand-or-child","GRAPH","Expand or child","Expand a node or move to its child.",["l","right"],"terminal",["graph"]),e("crtr.graph.focus","GRAPH","Focus node","Focus the selected node.",["enter"],"terminal",["graph"]),e("crtr.graph.revive","GRAPH","Revive node","Revive and open the selected dormant node.",["r"],"terminal",["graph"]),e("crtr.graph.focus-manager","GRAPH","Focus manager","Focus the selected node manager.",["m"],"terminal",["graph"]),e("crtr.graph.close-subtree","GRAPH","Close subtree","Ask to close the selected subtree.",["x"],"terminal",["graph"]),e("crtr.graph.dismiss","GRAPH","Dismiss graph","Close the graph overlay.",["escape"],"terminal",["graph"],"dismiss-modal"),e("crtr.graph.confirm.accept","GRAPH","Confirm close","Confirm closing the selected subtree.",["y"],"terminal",["graph.confirm"]),e("crtr.graph.confirm.cancel","GRAPH","Cancel close","Cancel closing the selected subtree.",["n","escape"],"terminal",["graph.confirm"],"dismiss-modal"),e("crtr.inspector.force-quit","Inspector","Force quit Inspector","Immediately leave the Inspector.",["ctrl+c"],"host-global",["inspector"],"leave-screen"),e("crtr.inspector.down","Inspector","Move down","Move the Inspector selection or scroll cursor down.",["j","down"],"terminal",["inspector"]),e("crtr.inspector.up","Inspector","Move up","Move the Inspector selection or scroll cursor up.",["k","up"],"terminal",["inspector"]),e("crtr.inspector.section-next","Inspector","Next section","Move to the next Inspector section.",["tab"],"terminal",["inspector"]),e("crtr.inspector.section-prev","Inspector","Previous section","Move to the previous Inspector section.",["shift+tab"],"terminal",["inspector"]),e("crtr.inspector.page-down","Inspector","Page down","Scroll the Inspector detail body down.",["ctrl+d","pagedown"],"terminal",["inspector"]),e("crtr.inspector.page-up","Inspector","Page up","Scroll the Inspector detail body up.",["ctrl+u","pageup"],"terminal",["inspector"]),e("crtr.inspector.toggle-detail","Inspector","Toggle item detail","Expand or collapse the selected cron or background job.",["enter"],"terminal",["inspector"]),e("crtr.inspector.cancel-item","Inspector","Cancel selected item","Arm cancellation for the selected cron, or stopping for the selected background job.",["x"],"terminal",["inspector"]),e("crtr.inspector.open-log","Inspector","Open job log","Follow the selected background job\u2019s log in a new tmux window.",["o"],"terminal",["inspector"]),e("crtr.inspector.confirm","Inspector","Confirm action","Confirm the armed Inspector action.",["y"],"terminal",["inspector"]),e("crtr.inspector.dismiss","Inspector","Dismiss action","Dismiss an Inspector confirmation or banner.",["escape"],"terminal",["inspector"],"dismiss-modal"),e("crtr.inspector.retarget","Inspector","Retarget","Inspect the selected cron anchor or creator node.",["t"],"terminal",["inspector"]),e("crtr.inspector.refresh","Inspector","Refresh","Refresh the Inspector now.",["g","r"],"terminal",["inspector"]),e("crtr.inspector.quit","Inspector","Quit","Leave the Inspector.",["q"],"terminal",["inspector"],"leave-screen"),...v(),...x(),...k()]);function v(){const t="browse";return[e("crtr.browse.quit","Browse and pickers","Quit browse","Leave canvas browse.",["q","escape","ctrl+c"],"terminal",[t],"leave-screen"),e("crtr.browse.down","Browse and pickers","Move down","Move to the next row.",["j","down"],"terminal",[t]),e("crtr.browse.up","Browse and pickers","Move up","Move to the previous row.",["k","up"],"terminal",[t]),e("crtr.browse.first","Browse and pickers","First row","Move to the first row.",["g"],"terminal",[t]),e("crtr.browse.last","Browse and pickers","Last row","Move to the last row.",["shift+g"],"terminal",[t]),e("crtr.browse.expand-or-child","Browse and pickers","Expand or child","Expand a row or move to its child.",["l","right"],"terminal",[t]),e("crtr.browse.collapse-or-parent","Browse and pickers","Collapse or parent","Collapse a row or move to its parent.",["h","left"],"terminal",[t]),e("crtr.browse.toggle-fold","Browse and pickers","Toggle fold","Toggle the selected fold.",["space"],"terminal",[t]),e("crtr.browse.tab-next","Browse and pickers","Next tab","Select the next browse tab.",["tab","]"],"terminal",[t]),e("crtr.browse.tab-previous","Browse and pickers","Previous tab","Select the previous browse tab.",["shift+tab","["],"terminal",[t]),...[1,2,3,4,5].map(n=>e(`crtr.browse.tab.${n}`,"Browse and pickers",`Tab ${n}`,`Select browse tab ${n}.`,[`${n}`],"terminal",[t])),e("crtr.browse.close-subtree","Browse and pickers","Close subtree","Ask to close the selected subtree.",["x"],"terminal",[t]),e("crtr.browse.confirm-close","Browse and pickers","Confirm close","Confirm closing the selected subtree.",["y"],"terminal",["browse.confirm"]),e("crtr.browse.sort","Browse and pickers","Change sort","Change the browse sort.",["s"],"terminal",[t]),e("crtr.browse.scope","Browse and pickers","Change scope","Toggle the browse cwd scope.",["c"],"terminal",[t]),e("crtr.browse.profile","Browse and pickers","Toggle profile","Toggle the browse profile scope.",["shift+p"],"terminal",[t]),e("crtr.browse.residents","Browse and pickers","Toggle residents","Toggle resident display.",["r"],"terminal",[t]),e("crtr.browse.preview","Browse and pickers","Toggle preview","Toggle the preview panel.",["v"],"terminal",[t]),e("crtr.browse.report","Browse and pickers","Toggle report","Swap the preview panel between the conversation and the node's newest report.",["shift+v"],"terminal",[t]),e("crtr.browse.pin","Browse and pickers","Pin node","Star or unstar the selected node.",["p"],"terminal",[t]),e("crtr.browse.search","Browse and pickers","Search","Open browse search.",["/"],"terminal",[t]),e("crtr.browse.revive-now","Browse and pickers","Revive now","Revive the selected node now.",["shift+k"],"terminal",[t]),e("crtr.browse.focus","Browse and pickers","Focus node","Focus the selected node.",["enter","o"],"terminal",[t]),e("crtr.browse.focus-background","Browse and pickers","Focus node (keep browsing)","Focus the selected node without leaving the navigator.",["shift+o"],"terminal",[t]),e("crtr.browse.yank-id","Browse and pickers","Yank node id","Copy the selected node id to the system clipboard.",["y"],"terminal",[t]),e("crtr.browse.search.cancel","Browse and pickers","Cancel search","Cancel browse search.",["escape"],"terminal",["browse.search"],"dismiss-modal"),e("crtr.browse.search.commit","Browse and pickers","Commit search","Apply browse search.",["enter"],"terminal",["browse.search"]),e("crtr.browse.search.delete","Browse and pickers","Delete search text","Delete the previous search character.",["backspace"],"terminal",["browse.search"]),e("crtr.browse.search.quit","Browse and pickers","Quit browse","Leave canvas browse from search.",["ctrl+c"],"terminal",["browse.search"],"leave-screen")]}i(v,"browseBindings");function x(){const t="profile.menu",n="profile.search";return[e("crtr.profile.menu.up","Profiles","Move up","Move to the previous profile.",["k","up"],"terminal",[t]),e("crtr.profile.menu.down","Profiles","Move down","Move to the next profile.",["j","down"],"terminal",[t]),e("crtr.profile.menu.select","Profiles","Select profile","Select the highlighted profile.",["enter"],"terminal",[t]),e("crtr.profile.menu.accept-default","Profiles","Accept default","Accept the default profile.",["escape"],"terminal",[t],"leave-screen"),e("crtr.profile.menu.abort","Profiles","Abort","Leave profile selection.",["ctrl+c"],"terminal",[t],"leave-screen"),...[1,2,3,4,5,6,7,8,9].map(s=>e(`crtr.profile.menu.slot.${s}`,"Profiles",`Profile slot ${s}`,`Select profile slot ${s}.`,[`${s}`],"terminal",[t])),e("crtr.profile.menu.create","Profiles","Create profile","Create a profile.",["c"],"terminal",[t]),e("crtr.profile.menu.search-existing","Profiles","Search profiles","Search existing profiles.",["s"],"terminal",[t]),e("crtr.profile.menu.use-root","Profiles","Use root profile","Use the root profile.",["r"],"terminal",[t]),e("crtr.profile.menu.set-default","Profiles","Set default","Set the selected profile as default.",["d"],"terminal",[t]),e("crtr.profile.menu.add-directory","Profiles","Add directory","Add a directory to the profile.",["a"],"terminal",[t]),e("crtr.profile.menu.unpin","Profiles","Unpin directory","Unpin the selected directory.",["u"],"terminal",[t]),e("crtr.profile.menu.manage","Profiles","Manage profiles","Open the profiles settings page in a popup.",["v"],"terminal",[t]),e("crtr.profile.search.up","Profiles","Move up","Move to the previous search result.",["up","ctrl+p"],"terminal",[n]),e("crtr.profile.search.down","Profiles","Move down","Move to the next search result.",["down","ctrl+n"],"terminal",[n]),e("crtr.profile.search.pick","Profiles","Pick profile","Pick the highlighted profile.",["enter"],"terminal",[n]),e("crtr.profile.search.back","Profiles","Back","Return to profile selection.",["escape"],"terminal",[n],"leave-screen"),e("crtr.profile.search.delete","Profiles","Delete filter text","Delete the previous filter character.",["backspace"],"terminal",[n]),e("crtr.profile.search.abort","Profiles","Abort","Leave profile search.",["ctrl+c"],"terminal",[n],"leave-screen")]}i(x,"profileBindings");function k(){return[e("crtr.setup.cancel","Setup","Cancel setup","Leave the setup wizard.",["q","ctrl+c","ctrl+d"],"terminal",b,"leave-screen"),e("crtr.setup.tab-next","Setup","Next tab","Select the next setup tab.",["tab","right"],"terminal",h),e("crtr.setup.tab-previous","Setup","Previous tab","Select the previous setup tab.",["shift+tab","left"],"terminal",h),e("crtr.setup.open","Setup","Open","Open the selected setup item.",["enter"],"terminal",["setup.login"]),e("crtr.setup.list-down","Setup","Move down","Move to the next setup item.",["j","down"],"terminal",["setup.list","setup.models","setup.models.dimension-picker","setup.models.route-form"]),e("crtr.setup.list-up","Setup","Move up","Move to the previous setup item.",["k","up"],"terminal",["setup.list","setup.models","setup.models.dimension-picker","setup.models.route-form"]),e("crtr.setup.toggle","Setup","Toggle","Toggle the selected setup item.",["space"],"terminal",["setup.list","setup.keybindings"]),e("crtr.setup.details","Setup","Show details","Show details for the selected item.",["?"],"terminal",["setup.list"]),e("crtr.setup.advance","Setup","Advance","Advance the setup flow.",["enter"],"terminal",["setup.list"]),e("crtr.setup.confirm.accept","Setup","Confirm","Accept the setup confirmation.",["y","enter"],"terminal",["setup.confirm","setup.models.confirm"]),e("crtr.setup.confirm.back","Setup","Go back","Cancel the setup confirmation.",["n","escape","q"],"terminal",["setup.confirm","setup.models.confirm"],"dismiss-modal"),e("crtr.setup.keybindings.edit","Setup","Edit binding","Edit the selected binding.",["enter"],"terminal",["setup.keybindings"]),e("crtr.setup.keybindings.commit-edit","Setup","Commit edit","Commit the active binding edit.",["enter"],"terminal",["setup.keybindings.edit"]),e("crtr.setup.keybindings.cancel-edit","Setup","Cancel edit","Cancel the active binding edit.",["escape"],"terminal",["setup.keybindings.edit"],"dismiss-modal"),e("crtr.setup.keybindings.unbind","Setup","Unbind","Disable the selected binding.",["delete"],"terminal",["setup.keybindings"]),e("crtr.setup.keybindings.reset-row","Setup","Reset binding","Reset the selected binding.",["r"],"terminal",["setup.keybindings"]),e("crtr.setup.keybindings.reset-all","Setup","Reset all bindings","Reset all bindings to defaults.",["shift+r"],"terminal",["setup.keybindings"]),e("crtr.setup.keybindings.save","Setup","Save bindings","Save keybinding changes.",["s"],"terminal",["setup.keybindings"],"save-keybindings"),e("crtr.setup.keybindings.discard","Setup","Discard changes","Discard keybinding changes.",["escape"],"terminal",["setup.keybindings"],"leave-screen"),e("crtr.setup.keybindings.page-up","Setup","Page up","Move up one keybinding page.",["pageup"],"terminal",["setup.keybindings","setup.keybindings.edit"]),e("crtr.setup.keybindings.page-down","Setup","Page down","Move down one keybinding page.",["pagedown"],"terminal",["setup.keybindings","setup.keybindings.edit"]),e("crtr.setup.keybindings.filter","Setup","Filter bindings","Open the keybinding filter.",["/"],"terminal",["setup.keybindings"]),e("crtr.setup.keybindings.filter-delete","Setup","Delete filter text","Delete the previous filter character.",["backspace"],"terminal",["setup.keybindings.filter"]),e("crtr.setup.keybindings.filter-clear","Setup","Clear filter","Clear the keybinding filter.",["escape"],"terminal",["setup.keybindings.filter"],"dismiss-modal"),e("crtr.setup.cell-previous","Setup","Previous field","Select the previous field or setup table cell.",["h"],"terminal",["setup.models","setup.keybindings"]),e("crtr.setup.cell-next","Setup","Next field","Select the next field or setup table cell.",["l"],"terminal",["setup.models","setup.keybindings"]),e("crtr.setup.models.open","Setup","Edit model","Edit the selected model cell or route field.",["enter"],"terminal",["setup.models","setup.models.route-form"]),e("crtr.setup.models.scope-next","Setup","Next model config scope","Edit the next applicable user, profile, or project config.",["s"],"terminal",["setup.models"]),e("crtr.setup.models.commit","Setup","Commit pick","Commit the provider, strength, model, or thinking-level pick.",["enter"],"terminal",["setup.models.picker","setup.models.dimension-picker"]),e("crtr.setup.models.cancel","Setup","Cancel pick","Cancel the model picker or route form.",["escape"],"terminal",["setup.models.picker","setup.models.dimension-picker","setup.models.route-form"],"dismiss-modal")]}i(k,"setupBindings");const S=new Set(w.map(t=>t.id));function C(t){return t.contexts.includes("attach")&&(t.inputKind==="terminal"||t.inputKind==="host-global")}i(C,"isAttachPaneBinding");const P=Object.freeze([{context:"graph",capability:"dismiss-modal"},{context:"graph.confirm",capability:"dismiss-modal"},{context:"inspector",capability:"leave-screen"},{context:"inspector",capability:"dismiss-modal"},{context:"attach.search",capability:"dismiss-modal"},{context:"browse",capability:"leave-screen"},{context:"browse.search",capability:"dismiss-modal"},{context:"profile.menu",capability:"leave-screen"},{context:"profile.search",capability:"leave-screen"},{context:"setup.login",capability:"leave-screen"},{context:"setup.list",capability:"leave-screen"},{context:"setup.input",capability:"leave-screen"},{context:"setup.confirm",capability:"dismiss-modal"},{context:"setup.keybindings",capability:"leave-screen"},{context:"setup.keybindings",capability:"save-keybindings"},{context:"setup.keybindings.edit",capability:"dismiss-modal"},{context:"setup.keybindings.filter",capability:"dismiss-modal"},{context:"setup.models",capability:"leave-screen"},{context:"setup.models.picker",capability:"dismiss-modal"},{context:"setup.models.dimension-picker",capability:"dismiss-modal"},{context:"setup.models.route-form",capability:"dismiss-modal"},{context:"setup.models.confirm",capability:"dismiss-modal"}]);export{w as BINDING_CATALOG,S as BINDING_IDS,P as SAFETY_REQUIREMENTS,C as isAttachPaneBinding};
1
+ var g=Object.defineProperty;var i=(t,o)=>g(t,"name",{value:o,configurable:!0});function f(t,o,s,c,p,l,d,u,m){return Object.freeze({id:t,group:o,name:s,description:c,defaults:Object.freeze([...p]),inputKind:l,contexts:Object.freeze([...d]),...u===void 0?{}:{safety:u},...m===void 0?{}:{pair:Object.freeze({...m})}})}i(f,"binding");const e=f;function r(t,o,s,c,p){const l=`crtr.tmux.menu.${t}`,d=`crtr.tmux.menu-global.${t}`;return[e(l,"Global tmux",o,s,c,"tmux-menu-selector",p,void 0,{field:"menu",pairedId:d}),e(d,"Global tmux",o,s,[],"tmux-global-action",["tmux.root"],void 0,{field:"global",pairedId:l})]}i(r,"menuEntry");function n(t,o,s,c,p,l){return[e(t,"Attach",s,c,p,"tmux-menu-selector",["tmux.menu.attach"],void 0,{field:"menu",pairedId:o}),e(o,"Attach",s,c,l,"host-global",a,void 0,{field:"direct",pairedId:t})]}i(n,"attachEntry");const a=["attach","graph","graph.confirm"],b=["setup.login","setup.list","setup.input","setup.keybindings","setup.keybindings.edit","setup.keybindings.filter","setup.models"],h=["setup.login","setup.list","setup.keybindings","setup.models"],w=Object.freeze([e("crtr.tmux.menu.open","Global tmux","Open crouter menu","Open the crouter action menu.",["alt+c"],"host-global",["tmux.root"]),e("crtr.tmux.menu.attach","Global tmux","Attach actions","Open the attach-viewer action submenu.",["a"],"tmux-menu-selector",["tmux.menu"]),...r("promote","Promote to orchestrator","Promote the focused node to orchestrator \u2014 it holds a roadmap and delegates.",["o"],["tmux.menu"]),...r("demote-mode","Demote to base node","Demote the focused orchestrator back to a base node \u2014 hands-on again in its own window, no roadmap to drive.",["shift+o"],["tmux.menu"]),...r("resume","Find node","Open the canvas navigator and go to the chosen node.",["/"],["tmux.menu"]),...r("fork","Fork node","Fork the focused node into an independent idle root.",["f"],["tmux.menu"]),...r("demote","Demote to terminal","Demote the focused node to terminal \u2014 it keeps running, owes a final up the spine, and reaps when done.",["d"],["tmux.menu"]),...r("make-resident","Make resident","Flip the focused node to resident \u2014 interactable, never forced to submit a final, and wakeable while attached or awaiting something live.",["shift+r"],["tmux.menu"]),...r("detach","Demote and close viewer","Demote the focused node and close its viewer; its broker keeps running off-screen.",["shift+d"],["tmux.menu"]),...r("close-subtree","Close subtree","Close the focused node and its subtree.",["x"],["tmux.menu"]),...r("background-bash","Background running bash","Hand the focused node\u2019s running bash command to its background job system.",["b"],["tmux.menu"]),...r("file-review","Review a file","Open a line-anchored review of a file named in the transcript.",["v"],["tmux.menu"]),...r("pin","Pin conversation","Pin or unpin this conversation in the canvas navigator.",["p"],["tmux.menu"]),...r("show","Inspect node","Open the focused node\u2019s inspection menu.",["i"],["tmux.menu"]),...r("view-settings","Display settings","Open the viewer display-settings menu.",["z"],["tmux.menu"]),...r("fold-tools","Fold finished tool calls","Fold every finished tool call in the transcript to its single call line.",["f"],["tmux.menu.view-settings"]),...r("show-working","Show working","With consolidated replies on, show or hide the messages and tool calls behind each reply.",["w"],["tmux.menu.view-settings"]),...r("metadata","Show metadata","Show the focused node\u2019s metadata dossier.",["m"],["tmux.menu.show"]),...r("context","Browse context","Browse the focused node context.",["c"],["tmux.menu.show"]),...r("providers","Open settings","Open the crouter settings panel.",[","],["tmux.menu"]),...r("provider-options","Provider options","Open provider options \u2014 how an authenticated provider serves your turns, including Codex fast mode.",["."],["tmux.menu"]),...r("plugins","Browse plugins","Open the crouter plugin browser.",["l"],["tmux.menu"]),...r("analytics","Analytics","Open the Analytics page: how agents worked in the last 24 hours or 7 days.",["shift+a"],["tmux.menu"]),...r("context-admin","Context surface","Open the context surface: which documents each event delivers, and the surfaces, gates, and caps that decide it.",["shift+c"],["tmux.menu"]),...r("graph","Canvas graph","Open the canvas graph.",["g"],["tmux.menu"]),...r("focus-manager","Go to manager","Focus this node\u2019s manager.",["h"],["tmux.menu"]),...r("copy-id","Copy node id","Copy the focused node id to the system clipboard.",["y"],["tmux.menu"]),...r("reload-viewer","Reload viewer","Respawn this pane\u2019s viewer onto the current runtime generation.",["r"],["tmux.menu"]),...r("recap","Generate recap","Generate the focused node\u2019s Goal and State recap and refresh its generated title.",["s"],["tmux.menu"]),...r("issues","Open create menu","Open the create submenu.",["c"],["tmux.menu"]),...r("issue.idea","Create idea","Create an idea issue.",["i"],["tmux.menu.issue"]),...r("issue.bug","Create bug","Create a bug issue.",["b"],["tmux.menu.issue"]),e("crtr.tmux.node.next","Global tmux","Next node","Focus the next canvas node.",["alt+]"],"host-global",["tmux.root"]),e("crtr.tmux.node.previous","Global tmux","Previous node","Focus the previous canvas node.",["alt+["],"host-global",["tmux.root"]),e("crtr.tmux.inbox.toggle","Global tmux","Toggle inbox","Open or close the human inbox in the focused viewer.",["alt+i"],"host-global",["tmux.root"]),...n("crtr.tmux.menu.attach.detach","crtr.attach.detach","Detach","Detach from the node.",["d"],["ctrl+d"]),...n("crtr.tmux.menu.attach.clear-or-detach","crtr.attach.clear-or-detach","Clear input (cancel agent on repeat)","Clear the input; press again within a second to cancel the running agent turn. Detach lives on its own binding.",["c"],["ctrl+c"]),...n("crtr.tmux.menu.attach.graph.toggle","crtr.attach.graph.toggle","Toggle graph","Open or close the graph overlay.",["g"],["alt+g"]),...n("crtr.tmux.menu.attach.help.toggle","crtr.attach.help.toggle","Help","Open the attach help overlay.",["?"],["alt+shift+k"]),...n("crtr.tmux.menu.attach.inbox.toggle","crtr.attach.inbox.toggle","Toggle inbox","Open or close the human inbox surface in this viewer.",["i"],[]),...n("crtr.tmux.menu.attach.inbox-strip","crtr.attach.inbox-strip","Pending requests","Jump to the pending human-request strip at the top of the pane.",["u"],["alt+u"]),...n("crtr.tmux.menu.attach.model-ladder.next","crtr.attach.model-ladder.next","Next model","Move to the next model ladder rung.",["]"],["alt+m"]),...n("crtr.tmux.menu.attach.model-ladder.previous","crtr.attach.model-ladder.previous","Previous model","Move to the previous model ladder rung.",["["],["alt+shift+m"]),...n("crtr.tmux.menu.attach.command.inspect","crtr.attach.command.inspect","Inspect loaded command","Open the loaded slash command\u2019s expanded prompt.",["h"],["alt+shift+h"]),...n("crtr.tmux.menu.attach.file-review","crtr.attach.file-review","Review a file","Open a line-anchored review of a file named in the transcript.",[],["alt+shift+r"]),...n("crtr.tmux.menu.attach.profile-files","crtr.attach.profile-files","Search profile files","Fuzzy-match path segments across the node family\u2019s working directories, their context directories (files), profile projects and the profile\u2019s own directory; Ctrl+Y copies a selected path.",["f"],["ctrl+f"]),...n("crtr.tmux.menu.attach.search","crtr.attach.search","Search transcript","Search the transcript as it is currently displayed \u2014 folded tool output is not searched until it is unfolded.",["/"],["alt+/"]),...n("crtr.tmux.menu.attach.whip","crtr.attach.whip","Whip agent","Interrupt the agent and send a playful command.",["w"],[]),e("crtr.attach.scroll.up","Attach","Scroll up","Scroll the transcript one line up.",["shift+up"],"terminal",a),e("crtr.attach.scroll.down","Attach","Scroll down","Scroll the transcript one line down.",["shift+down"],"terminal",a),e("crtr.attach.scroll.page-up","Attach","Scroll page up","Scroll the transcript one page up.",["pageup"],"terminal",a),e("crtr.attach.scroll.page-down","Attach","Scroll page down","Scroll the transcript one page down.",["pagedown"],"terminal",a),e("crtr.attach.scroll.live","Attach","Return to live","Return the transcript to the live bottom.",["shift+end"],"terminal",a),e("crtr.attach.mouse.toggle","Attach","Toggle mouse wheel scrolling","Toggle mouse wheel scrolling on or off.",["alt+shift+o"],"terminal",a),e("crtr.attach.copy.message","Attach","Copy visible message","Copy the newest message visible in the transcript viewport.",["alt+shift+y"],"terminal",a),e("crtr.attach.search.next","Attach","Next match","Move to the next transcript search match.",["n"],"terminal",["attach.search"]),e("crtr.attach.search.previous","Attach","Previous match","Move to the previous transcript search match.",["shift+n"],"terminal",["attach.search"]),e("crtr.attach.search.edit","Attach","Edit query","Return to editing the transcript search query.",["/"],"terminal",["attach.search"]),e("crtr.attach.search.close","Attach","Close search","Leave transcript search and clear its highlights.",["escape"],"terminal",["attach.search"],"dismiss-modal"),e("crtr.graph.down","GRAPH","Move down","Move to the next graph row.",["j","down"],"terminal",["graph"]),e("crtr.graph.up","GRAPH","Move up","Move to the previous graph row.",["k","up"],"terminal",["graph"]),e("crtr.graph.first","GRAPH","First row","Move to the first graph row.",["g"],"terminal",["graph"]),e("crtr.graph.last","GRAPH","Last row","Move to the last graph row.",["shift+g"],"terminal",["graph"]),e("crtr.graph.collapse-or-manager","GRAPH","Collapse or manager","Collapse a node or move to its manager.",["h","left"],"terminal",["graph"]),e("crtr.graph.expand-or-child","GRAPH","Expand or child","Expand a node or move to its child.",["l","right"],"terminal",["graph"]),e("crtr.graph.focus","GRAPH","Focus node","Focus the selected node.",["enter"],"terminal",["graph"]),e("crtr.graph.revive","GRAPH","Revive node","Revive and open the selected dormant node.",["r"],"terminal",["graph"]),e("crtr.graph.focus-manager","GRAPH","Focus manager","Focus the selected node manager.",["m"],"terminal",["graph"]),e("crtr.graph.close-subtree","GRAPH","Close subtree","Ask to close the selected subtree.",["x"],"terminal",["graph"]),e("crtr.graph.dismiss","GRAPH","Dismiss graph","Close the graph overlay.",["escape"],"terminal",["graph"],"dismiss-modal"),e("crtr.graph.confirm.accept","GRAPH","Confirm close","Confirm closing the selected subtree.",["y"],"terminal",["graph.confirm"]),e("crtr.graph.confirm.cancel","GRAPH","Cancel close","Cancel closing the selected subtree.",["n","escape"],"terminal",["graph.confirm"],"dismiss-modal"),e("crtr.inspector.force-quit","Inspector","Force quit Inspector","Immediately leave the Inspector.",["ctrl+c"],"host-global",["inspector"],"leave-screen"),e("crtr.inspector.down","Inspector","Move down","Move the Inspector selection or scroll cursor down.",["j","down"],"terminal",["inspector"]),e("crtr.inspector.up","Inspector","Move up","Move the Inspector selection or scroll cursor up.",["k","up"],"terminal",["inspector"]),e("crtr.inspector.section-next","Inspector","Next section","Move to the next Inspector section.",["tab"],"terminal",["inspector"]),e("crtr.inspector.section-prev","Inspector","Previous section","Move to the previous Inspector section.",["shift+tab"],"terminal",["inspector"]),e("crtr.inspector.page-down","Inspector","Page down","Scroll the Inspector detail body down.",["ctrl+d","pagedown"],"terminal",["inspector"]),e("crtr.inspector.page-up","Inspector","Page up","Scroll the Inspector detail body up.",["ctrl+u","pageup"],"terminal",["inspector"]),e("crtr.inspector.toggle-detail","Inspector","Toggle item detail","Expand or collapse the selected cron or background job.",["enter"],"terminal",["inspector"]),e("crtr.inspector.cancel-item","Inspector","Cancel selected item","Arm cancellation for the selected cron, or stopping for the selected background job.",["x"],"terminal",["inspector"]),e("crtr.inspector.open-log","Inspector","Open job log","Follow the selected background job\u2019s log in a new tmux window.",["o"],"terminal",["inspector"]),e("crtr.inspector.confirm","Inspector","Confirm action","Confirm the armed Inspector action.",["y"],"terminal",["inspector"]),e("crtr.inspector.dismiss","Inspector","Dismiss action","Dismiss an Inspector confirmation or banner.",["escape"],"terminal",["inspector"],"dismiss-modal"),e("crtr.inspector.retarget","Inspector","Retarget","Inspect the selected cron anchor or creator node.",["t"],"terminal",["inspector"]),e("crtr.inspector.refresh","Inspector","Refresh","Refresh the Inspector now.",["g","r"],"terminal",["inspector"]),e("crtr.inspector.quit","Inspector","Quit","Leave the Inspector.",["q"],"terminal",["inspector"],"leave-screen"),...v(),...x(),...k()]);function v(){const t="browse";return[e("crtr.browse.quit","Browse and pickers","Quit browse","Leave canvas browse.",["q","escape","ctrl+c"],"terminal",[t],"leave-screen"),e("crtr.browse.down","Browse and pickers","Move down","Move to the next row.",["j","down"],"terminal",[t]),e("crtr.browse.up","Browse and pickers","Move up","Move to the previous row.",["k","up"],"terminal",[t]),e("crtr.browse.first","Browse and pickers","First row","Move to the first row.",["g"],"terminal",[t]),e("crtr.browse.last","Browse and pickers","Last row","Move to the last row.",["shift+g"],"terminal",[t]),e("crtr.browse.expand-or-child","Browse and pickers","Expand or child","Expand a row or move to its child.",["l","right"],"terminal",[t]),e("crtr.browse.collapse-or-parent","Browse and pickers","Collapse or parent","Collapse a row or move to its parent.",["h","left"],"terminal",[t]),e("crtr.browse.toggle-fold","Browse and pickers","Toggle fold","Toggle the selected fold.",["space"],"terminal",[t]),e("crtr.browse.tab-next","Browse and pickers","Next tab","Select the next browse tab.",["tab","]"],"terminal",[t]),e("crtr.browse.tab-previous","Browse and pickers","Previous tab","Select the previous browse tab.",["shift+tab","["],"terminal",[t]),...[1,2,3,4,5].map(o=>e(`crtr.browse.tab.${o}`,"Browse and pickers",`Tab ${o}`,`Select browse tab ${o}.`,[`${o}`],"terminal",[t])),e("crtr.browse.close-subtree","Browse and pickers","Close subtree","Ask to close the selected subtree.",["x"],"terminal",[t]),e("crtr.browse.confirm-close","Browse and pickers","Confirm close","Confirm closing the selected subtree.",["y"],"terminal",["browse.confirm"]),e("crtr.browse.sort","Browse and pickers","Change sort","Change the browse sort.",["s"],"terminal",[t]),e("crtr.browse.scope","Browse and pickers","Change scope","Toggle the browse cwd scope.",["c"],"terminal",[t]),e("crtr.browse.profile","Browse and pickers","Toggle profile","Toggle the browse profile scope.",["shift+p"],"terminal",[t]),e("crtr.browse.residents","Browse and pickers","Toggle residents","Toggle resident display.",["r"],"terminal",[t]),e("crtr.browse.preview","Browse and pickers","Toggle preview","Toggle the preview panel.",["v"],"terminal",[t]),e("crtr.browse.report","Browse and pickers","Toggle report","Swap the preview panel between the conversation and the node's newest report.",["shift+v"],"terminal",[t]),e("crtr.browse.pin","Browse and pickers","Pin node","Star or unstar the selected node.",["p"],"terminal",[t]),e("crtr.browse.search","Browse and pickers","Search","Open browse search.",["/"],"terminal",[t]),e("crtr.browse.revive-now","Browse and pickers","Revive now","Revive the selected node now.",["shift+k"],"terminal",[t]),e("crtr.browse.focus","Browse and pickers","Focus node","Focus the selected node.",["enter","o"],"terminal",[t]),e("crtr.browse.focus-background","Browse and pickers","Focus node (keep browsing)","Focus the selected node without leaving the navigator.",["shift+o"],"terminal",[t]),e("crtr.browse.yank-id","Browse and pickers","Yank node id","Copy the selected node id to the system clipboard.",["y"],"terminal",[t]),e("crtr.browse.search.cancel","Browse and pickers","Cancel search","Cancel browse search.",["escape"],"terminal",["browse.search"],"dismiss-modal"),e("crtr.browse.search.commit","Browse and pickers","Commit search","Apply browse search.",["enter"],"terminal",["browse.search"]),e("crtr.browse.search.delete","Browse and pickers","Delete search text","Delete the previous search character.",["backspace"],"terminal",["browse.search"]),e("crtr.browse.search.quit","Browse and pickers","Quit browse","Leave canvas browse from search.",["ctrl+c"],"terminal",["browse.search"],"leave-screen")]}i(v,"browseBindings");function x(){const t="profile.menu",o="profile.search";return[e("crtr.profile.menu.up","Profiles","Move up","Move to the previous profile.",["k","up"],"terminal",[t]),e("crtr.profile.menu.down","Profiles","Move down","Move to the next profile.",["j","down"],"terminal",[t]),e("crtr.profile.menu.select","Profiles","Select profile","Select the highlighted profile.",["enter"],"terminal",[t]),e("crtr.profile.menu.accept-default","Profiles","Accept default","Accept the default profile.",["escape"],"terminal",[t],"leave-screen"),e("crtr.profile.menu.abort","Profiles","Abort","Leave profile selection.",["ctrl+c"],"terminal",[t],"leave-screen"),...[1,2,3,4,5,6,7,8,9].map(s=>e(`crtr.profile.menu.slot.${s}`,"Profiles",`Profile slot ${s}`,`Select profile slot ${s}.`,[`${s}`],"terminal",[t])),e("crtr.profile.menu.create","Profiles","Create profile","Create a profile.",["c"],"terminal",[t]),e("crtr.profile.menu.search-existing","Profiles","Search profiles","Search existing profiles.",["s"],"terminal",[t]),e("crtr.profile.menu.use-root","Profiles","Use root profile","Use the root profile.",["r"],"terminal",[t]),e("crtr.profile.menu.set-default","Profiles","Set default","Set the selected profile as default.",["d"],"terminal",[t]),e("crtr.profile.menu.add-directory","Profiles","Add directory","Add a directory to the profile.",["a"],"terminal",[t]),e("crtr.profile.menu.unpin","Profiles","Unpin directory","Unpin the selected directory.",["u"],"terminal",[t]),e("crtr.profile.menu.manage","Profiles","Manage profiles","Open the profiles settings page in a popup.",["v"],"terminal",[t]),e("crtr.profile.search.up","Profiles","Move up","Move to the previous search result.",["up","ctrl+p"],"terminal",[o]),e("crtr.profile.search.down","Profiles","Move down","Move to the next search result.",["down","ctrl+n"],"terminal",[o]),e("crtr.profile.search.pick","Profiles","Pick profile","Pick the highlighted profile.",["enter"],"terminal",[o]),e("crtr.profile.search.back","Profiles","Back","Return to profile selection.",["escape"],"terminal",[o],"leave-screen"),e("crtr.profile.search.delete","Profiles","Delete filter text","Delete the previous filter character.",["backspace"],"terminal",[o]),e("crtr.profile.search.abort","Profiles","Abort","Leave profile search.",["ctrl+c"],"terminal",[o],"leave-screen")]}i(x,"profileBindings");function k(){return[e("crtr.setup.cancel","Setup","Cancel setup","Leave the setup wizard.",["q","ctrl+c","ctrl+d"],"terminal",b,"leave-screen"),e("crtr.setup.tab-next","Setup","Next tab","Select the next setup tab.",["tab","right"],"terminal",h),e("crtr.setup.tab-previous","Setup","Previous tab","Select the previous setup tab.",["shift+tab","left"],"terminal",h),e("crtr.setup.open","Setup","Open","Open the selected setup item.",["enter"],"terminal",["setup.login"]),e("crtr.setup.list-down","Setup","Move down","Move to the next setup item.",["j","down"],"terminal",["setup.list","setup.models","setup.models.dimension-picker","setup.models.route-form"]),e("crtr.setup.list-up","Setup","Move up","Move to the previous setup item.",["k","up"],"terminal",["setup.list","setup.models","setup.models.dimension-picker","setup.models.route-form"]),e("crtr.setup.toggle","Setup","Toggle","Toggle the selected setup item.",["space"],"terminal",["setup.list","setup.keybindings"]),e("crtr.setup.details","Setup","Show details","Show details for the selected item.",["?"],"terminal",["setup.list"]),e("crtr.setup.advance","Setup","Advance","Advance the setup flow.",["enter"],"terminal",["setup.list"]),e("crtr.setup.confirm.accept","Setup","Confirm","Accept the setup confirmation.",["y","enter"],"terminal",["setup.confirm","setup.models.confirm"]),e("crtr.setup.confirm.back","Setup","Go back","Cancel the setup confirmation.",["n","escape","q"],"terminal",["setup.confirm","setup.models.confirm"],"dismiss-modal"),e("crtr.setup.keybindings.edit","Setup","Edit binding","Edit the selected binding.",["enter"],"terminal",["setup.keybindings"]),e("crtr.setup.keybindings.commit-edit","Setup","Commit edit","Commit the active binding edit.",["enter"],"terminal",["setup.keybindings.edit"]),e("crtr.setup.keybindings.cancel-edit","Setup","Cancel edit","Cancel the active binding edit.",["escape"],"terminal",["setup.keybindings.edit"],"dismiss-modal"),e("crtr.setup.keybindings.unbind","Setup","Unbind","Disable the selected binding.",["delete"],"terminal",["setup.keybindings"]),e("crtr.setup.keybindings.reset-row","Setup","Reset binding","Reset the selected binding.",["r"],"terminal",["setup.keybindings"]),e("crtr.setup.keybindings.reset-all","Setup","Reset all bindings","Reset all bindings to defaults.",["shift+r"],"terminal",["setup.keybindings"]),e("crtr.setup.keybindings.save","Setup","Save bindings","Save keybinding changes.",["s"],"terminal",["setup.keybindings"],"save-keybindings"),e("crtr.setup.keybindings.discard","Setup","Discard changes","Discard keybinding changes.",["escape"],"terminal",["setup.keybindings"],"leave-screen"),e("crtr.setup.keybindings.page-up","Setup","Page up","Move up one keybinding page.",["pageup"],"terminal",["setup.keybindings","setup.keybindings.edit"]),e("crtr.setup.keybindings.page-down","Setup","Page down","Move down one keybinding page.",["pagedown"],"terminal",["setup.keybindings","setup.keybindings.edit"]),e("crtr.setup.keybindings.filter","Setup","Filter bindings","Open the keybinding filter.",["/"],"terminal",["setup.keybindings"]),e("crtr.setup.keybindings.filter-delete","Setup","Delete filter text","Delete the previous filter character.",["backspace"],"terminal",["setup.keybindings.filter"]),e("crtr.setup.keybindings.filter-clear","Setup","Clear filter","Clear the keybinding filter.",["escape"],"terminal",["setup.keybindings.filter"],"dismiss-modal"),e("crtr.setup.cell-previous","Setup","Previous field","Select the previous field or setup table cell.",["h"],"terminal",["setup.models","setup.keybindings"]),e("crtr.setup.cell-next","Setup","Next field","Select the next field or setup table cell.",["l"],"terminal",["setup.models","setup.keybindings"]),e("crtr.setup.models.open","Setup","Edit model","Edit the selected model cell or route field.",["enter"],"terminal",["setup.models","setup.models.route-form"]),e("crtr.setup.models.scope-next","Setup","Next model config scope","Edit the next applicable user, profile, or project config.",["s"],"terminal",["setup.models"]),e("crtr.setup.models.commit","Setup","Commit pick","Commit the provider, strength, model, or thinking-level pick.",["enter"],"terminal",["setup.models.picker","setup.models.dimension-picker"]),e("crtr.setup.models.cancel","Setup","Cancel pick","Cancel the model picker or route form.",["escape"],"terminal",["setup.models.picker","setup.models.dimension-picker","setup.models.route-form"],"dismiss-modal")]}i(k,"setupBindings");const S=new Set(w.map(t=>t.id));function C(t){return t.contexts.includes("attach")&&(t.inputKind==="terminal"||t.inputKind==="host-global")}i(C,"isAttachPaneBinding");const P=Object.freeze([{context:"graph",capability:"dismiss-modal"},{context:"graph.confirm",capability:"dismiss-modal"},{context:"inspector",capability:"leave-screen"},{context:"inspector",capability:"dismiss-modal"},{context:"attach.search",capability:"dismiss-modal"},{context:"browse",capability:"leave-screen"},{context:"browse.search",capability:"dismiss-modal"},{context:"profile.menu",capability:"leave-screen"},{context:"profile.search",capability:"leave-screen"},{context:"setup.login",capability:"leave-screen"},{context:"setup.list",capability:"leave-screen"},{context:"setup.input",capability:"leave-screen"},{context:"setup.confirm",capability:"dismiss-modal"},{context:"setup.keybindings",capability:"leave-screen"},{context:"setup.keybindings",capability:"save-keybindings"},{context:"setup.keybindings.edit",capability:"dismiss-modal"},{context:"setup.keybindings.filter",capability:"dismiss-modal"},{context:"setup.models",capability:"leave-screen"},{context:"setup.models.picker",capability:"dismiss-modal"},{context:"setup.models.dimension-picker",capability:"dismiss-modal"},{context:"setup.models.route-form",capability:"dismiss-modal"},{context:"setup.models.confirm",capability:"dismiss-modal"}]);export{w as BINDING_CATALOG,S as BINDING_IDS,P as SAFETY_REQUIREMENTS,C as isAttachPaneBinding};
@@ -1,5 +1,20 @@
1
1
  import type { MarketplaceManifest, PluginManifest } from '../types.js';
2
2
  export declare function pluginManifestPath(pluginRoot: string): string;
3
3
  export declare function marketplaceManifestPath(mktRoot: string): string;
4
+ /** What a manifest file holds: `absent` (no file, or JSON `null`), `ok`, or `broken` —
5
+ * present but unreadable, not JSON (a 0-byte file from a write cut short), or not an object. */
6
+ export type ManifestRead<T> = {
7
+ status: 'absent';
8
+ } | {
9
+ status: 'ok';
10
+ manifest: T;
11
+ } | {
12
+ status: 'broken';
13
+ error: string;
14
+ };
15
+ export declare function readPluginManifestChecked(pluginRoot: string): ManifestRead<PluginManifest>;
16
+ /** The plugin's manifest, or null when it has none or it is unreadable: one damaged plugin
17
+ * directory must read as "not installed", never throw through whoever lists plugins. The daemon
18
+ * quarantines such a directory (`quarantineBrokenPlugins`); `readPluginManifestChecked` says why. */
4
19
  export declare function readPluginManifest(pluginRoot: string): PluginManifest | null;
5
20
  export declare function readMarketplaceManifest(mktRoot: string): MarketplaceManifest | null;