@north-light/cli 0.3.589 → 0.3.591

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 (74) hide show
  1. package/dist/api/client.d.ts +4 -1
  2. package/dist/api/client.js +1 -1
  3. package/dist/api/contract.d.ts +1 -1
  4. package/dist/api/contract.js +1 -1
  5. package/dist/api/dto/docs.d.ts +5 -0
  6. package/dist/api/dto/settings.d.ts +21 -0
  7. package/dist/build-root.d.ts +2 -0
  8. package/dist/build-root.js +1 -1
  9. package/dist/cli.js +8 -8
  10. package/dist/clients/attach/viewer.js +468 -468
  11. package/dist/commands/pkg/app-installs.d.ts +53 -0
  12. package/dist/commands/pkg/app-installs.js +2 -0
  13. package/dist/commands/pkg/market-inspect.js +1 -1
  14. package/dist/commands/pkg/market-manage.js +1 -1
  15. package/dist/commands/pkg/plugin-inspect.js +1 -1
  16. package/dist/commands/pkg/plugin-manage.js +13 -13
  17. package/dist/core/canvas/schema-pg/011-package-revision.sql +12 -0
  18. package/dist/core/canvas/schema-version.d.ts +1 -1
  19. package/dist/core/canvas/schema-version.js +1 -1
  20. package/dist/core/config.d.ts +1 -0
  21. package/dist/core/config.js +1 -1
  22. package/dist/core/graph/names.js +8 -8
  23. package/dist/core/graph/package-docs.d.ts +6 -15
  24. package/dist/core/graph/package-docs.js +8 -6
  25. package/dist/core/graph/package-publish.d.ts +35 -5
  26. package/dist/core/graph/package-publish.js +3 -3
  27. package/dist/core/graph/package-reconcile.d.ts +20 -1
  28. package/dist/core/graph/package-reconcile.js +3 -1
  29. package/dist/core/import/export/dump.js +4 -3
  30. package/dist/core/import/load/stores.js +2 -2
  31. package/dist/core/installed-plugins.js +1 -1
  32. package/dist/core/packages/directory-sync.d.ts +41 -0
  33. package/dist/core/packages/directory-sync.js +6 -0
  34. package/dist/core/packages/eligibility.d.ts +16 -0
  35. package/dist/core/packages/eligibility.js +4 -0
  36. package/dist/core/packages/launch-kinds.d.ts +6 -0
  37. package/dist/core/packages/launch-kinds.js +2 -0
  38. package/dist/core/packages/source-url.d.ts +3 -0
  39. package/dist/core/packages/source-url.js +1 -0
  40. package/dist/core/permissions/ceiling.d.ts +9 -2
  41. package/dist/core/permissions/ceiling.js +11 -12
  42. package/dist/core/permissions/evaluator.js +2 -2
  43. package/dist/core/runtime/launch-config-worker.js +1 -1
  44. package/dist/core/runtime/launch-target.d.ts +6 -2
  45. package/dist/core/runtime/launch-target.js +1 -1
  46. package/dist/core/runtime/launch.d.ts +8 -1
  47. package/dist/core/runtime/launch.js +2 -2
  48. package/dist/core/runtime/promote.js +1 -1
  49. package/dist/core/runtime/recycle.js +1 -1
  50. package/dist/core/runtime/reset.js +1 -1
  51. package/dist/core/runtime/spawn.js +2 -2
  52. package/dist/core/runtime/warm-pool.js +1 -1
  53. package/dist/core/scoped-state/legacy-profiles.js +1 -1
  54. package/dist/core/scoped-state/migrate-app-installs.d.ts +13 -0
  55. package/dist/core/scoped-state/migrate-app-installs.js +1 -0
  56. package/dist/core/scoped-state/person-rows.js +4 -6
  57. package/dist/core/scoped-state/person-store.d.ts +23 -0
  58. package/dist/core/scoped-state/person-store.js +1 -1
  59. package/dist/daemon/api/handlers/execution-start.d.ts +2 -0
  60. package/dist/daemon/api/handlers/execution-start.js +2 -2
  61. package/dist/daemon/api/handlers/nodes.js +1 -1
  62. package/dist/daemon/api/handlers/package-docs.d.ts +1 -1
  63. package/dist/daemon/api/handlers/package-docs.js +1 -1
  64. package/dist/daemon/api/handlers/settings.d.ts +4 -1
  65. package/dist/daemon/api/handlers/settings.js +1 -1
  66. package/dist/daemon/crtrd.js +19 -19
  67. package/dist/daemon/reconcilers/grant-effects.js +1 -1
  68. package/package.json +2 -2
  69. package/packages/crouter-identity/package.json +1 -1
  70. package/runtime.lock.json +13 -13
  71. package/dist/core/graph/package-kinds.d.ts +0 -22
  72. package/dist/core/graph/package-kinds.js +0 -1
  73. package/dist/core/scoped-state/materialized-plugins.d.ts +0 -20
  74. package/dist/core/scoped-state/materialized-plugins.js +0 -1
@@ -1 +1 @@
1
- var Ze=Object.defineProperty;var s=(e,n)=>Ze(e,"name",{value:n,configurable:!0});import{envProfileId as Ye}from"../shared/env.js";import{basename as oe,join as te}from"node:path";import{existsSync as Ve,mkdirSync as Xe,statSync as Qe}from"node:fs";import{isDeepStrictEqual as en}from"node:util";import{CONFIG_FILE as nn,STATE_FILE as on,CONDENSED_HISTORY_MODES as tn,MOUSE_MODE_DEFAULTS as rn,WHIP_MESSAGE_MODES as sn,DEFAULT_WHIP_MESSAGES as re,defaultScopeConfig as v,defaultScopeState as ie,defaultModelLaddersConfig as cn,defaultKindsConfig as un,defaultRemoteCanvasConfig as dn}from"../types.js";import{BINDING_CATALOG as an,BINDING_IDS as fn,isAttachPaneBinding as ln}from"./keybindings/catalog.js";import{notFound as mn}from"./errors.js";import{emitEvent as se}from"./events/emit.js";import{LEDGER_ENV as pn}from"./ledger/rates.js";import{atomicWriteJson as U,readJsonIfExists as E,ensureDir as gn}from"./fs-utils.js";import{scopeRoot as g,requireScopeRoot as w,findProjectScopeRoots as yn,userScopeRoot as ce}from"./scope.js";import{listInstalledPluginsInRoot as ue}from"./installed-plugins.js";import{profileDir as de,listProfiles as ae}from"./profiles/manifest.js";import{canonicalRoot as k}from"./scoped-state/paths.js";import{readCachedSettings as G,readCachedState as hn,revisionOf as q,SettingsRevisionConflictError as fe,writeSettings as B}from"./scoped-state/person-store.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 wn,normalizePageComponents as le}from"./human/page-catalog.js";function h(e){return te(e,nn)}s(h,"configPathFor");function W(e,n){return bn(`${e}.lock`,()=>n(e),{timeoutError:s(()=>new Error(`timed out waiting for config lock: ${e}`),"timeoutError")})}s(W,"withConfigPathLock");function H(e){return te(e,on)}s(H,"statePathFor");function _o(e){const n=g(e);return n?h(n):null}s(_o,"configPath");function vo(e){const n=g(e);return n?H(n):null}s(vo,"statePath");function A(e){const n=k(e),o=k(ce());if(n===o)return{key:"user",kind:"user",root:o};if(oe(n)===".crouter")return{key:"project",kind:"project",root:n};for(const t of ae()){const r=k(de(t.profileId));if(n===r)return{key:`profile:${t.profileId}`,kind:"profile",root:r,profileId:t.profileId}}throw new Error(`settings root is not a recognized owner: ${e}`)}s(A,"settingsOwnerForRoot");function P(e){if(oe(e)===".crouter"){let n;try{n=k(e)}catch(o){if(o.code==="ENOENT")return null;throw o}if(n!==k(ce()))return E(h(e))}return G(A(e).key).settings}s(P,"readRawAtRoot");async function me(e,n){let o;return await B(t=>{const r=t.settings[e],i=n(structuredClone(r??{}));return o=i.value,[{op:"settings.replace",owner:e,raw:i.next,ifRevision:q(r)}]}),o}s(me,"mutatePersonSettings");async function J(e,n){const o=A(e);if(o.kind==="project"){W(h(o.root),t=>U(t,n,{createParent:!1}));return}await me(o.key,()=>({value:void 0,next:n}))}s(J,"writeRawAtRoot");async function S(e,n){const o=A(e);return o.kind==="project"?W(h(o.root),t=>{const r=n(E(t)??{});return U(t,r.next,{createParent:!1}),r.value}):await me(o.key,n)}s(S,"mutateRawAtRoot");const I=globalThis,C=Symbol.for("crtr.broker.config-snapshot");function wo(e){I[C]=e}s(wo,"installBrokerConfigSnapshot");function ko(){return I[C]}s(ko,"brokerConfigSnapshot");function kn(e){const n=I[C];if(n){if(e!=="user")throw new Error(`broker config snapshot has no ${e} scope`);return n.user}const o=g(e);return o?An(o):v()}s(kn,"readConfig");function An(e){const n=P(e);return n?D(n):v()}s(An,"readConfigAtRoot");function Sn(e){const n=E(H(e));return{marketplaces:n?.marketplaces??{},plugins:n?.plugins??{},activeCanvas:n?.activeCanvas??null,...n?.last_self_check===void 0?{}:{last_self_check:n.last_self_check}}}s(Sn,"readProjectState");function jn(e){const n=g(e);return n?e==="project"?Ve(n)?Sn(n):ie():hn(A(n).key):ie()}s(jn,"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 pe(e){if(e.scope==="user")return"user";if(!ae().some(n=>n.profileId===e.profileId))throw mn(`profile not found: ${e.profileId}`,{received:e.profileId,next:"Run `crtr profile list` to see available profiles."});return`profile:${e.profileId}`}s(pe,"rawSettingsKeyForTarget");function Ao(e){return G(pe(e))}s(Ao,"readRawSettingsForTarget");async function So(e,n,o){const t=pe(e);try{D(n)}catch(u){throw new xn(u)}if(G(t).revision!==o)throw new fe;const i=await B(u=>q(u.settings[t])===o?[{op:"settings.replace",owner:t,raw:n,ifRevision:o}]:(()=>{throw new fe})()),c=q(i.settings[t]);return{revision:c,changed:c!==o}}s(So,"replaceRawSettingsForTarget");async function jo(e,n){const o=w(e);return await S(o,t=>{const r=n(t);return{value:r,next:r}})}s(jo,"updateRawConfigAtomically");async function xo(e,n){await J(w(e),n)}s(xo,"writeConfig");async function Ro(e,n){await J(w(e),n)}s(Ro,"writeRawConfig");function Rn(e,n){W(H(e),o=>U(o,n,{createParent:!1}))}s(Rn,"writeProjectState");async function On(e,n){const o=w(e);if(e==="project"){Rn(o,n);return}const t=A(o).key;await B(()=>[{op:"state.write",owner:t,state:n}])}s(On,"writeState");async function Oo(e,n){if(e!=="project"){gn(n);return}try{Xe(n)}catch(o){if(o.code!=="EEXIST"||!Qe(n).isDirectory())throw o}E(h(n))===null&&await J(n,{schema_version:v().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 Pn=new Set(an.filter(ln).map(e=>e.id));function In(e){return Array.isArray(e)?[...new Set(e.filter(n=>typeof n=="string"&&fn.has(n)&&Pn.has(n)))]:[]}s(In,"normalizeTmuxPassthrough");function T(e,n=cn()){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(T,"mergeModelLadders");function ge(e){return e==="ultra"||e==="strong"||e==="medium"||e==="light"}s(ge,"validStrength");const Z=["ultra","strong","medium","light"];function Y(){return Object.create(null)}s(Y,"routeRecord");function j(e,n,o){Object.defineProperty(e,n,{value:o,writable:!0,enumerable:!0,configurable:!0})}s(j,"setRouteRecord");function ye(e){const n=Y();for(const[o,t]of Object.entries(e??{}))j(n,o,t);return n}s(ye,"copyRouteRecord");function x(e,n){se({level:"warn",event:"config.model_routes.invalid",fields:{route_id:e,rule:n}})}s(x,"routeInvalid");function $(e,n={}){const o=ye(n);if(e===null||typeof e!="object"||Array.isArray(e))return o;for(const[t,r]of Object.entries(e)){if(t.trim()===""){x(t,"route id must be a non-empty string");continue}if(r===null||typeof r!="object"||Array.isArray(r)){x(t,"route must be an object");continue}const i=r,c=o[t],u={...c};let d;for(const f of["family","credentialSource","providerId"]){const a=i[f];if(a!==void 0){if(typeof a!="string"||a.trim()===""){d=`${f} must be a non-empty string`;break}u[f]=a}}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 f={...c?.models};for(const[a,l]of Object.entries(i.models)){if(!ge(a)){d=`models.${a} is not a model strength`;break}if(typeof l!="string"||l.trim()===""){d=`models.${a} must be a non-empty provider/id ref`;break}const m=l.indexOf("/");if(m<=0||m===l.length-1){d=`models.${a} must be provider/id`;break}f[a]=l}u.models=f}if(d===void 0&&u.models!==void 0&&u.providerId!==void 0){for(const[f,a]of Object.entries(u.models))if(a.slice(0,a.indexOf("/"))!==u.providerId){d=`models.${f} provider prefix must equal providerId`;break}}if(d!==void 0){x(t,d);continue}j(o,t,u)}return o}s($,"mergeModelRoutePatches");function he(e){const n=Y(),o=Y();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){x(t,"route must include family, credentialSource, providerId, and models");continue}const i=Z.filter(c=>r.models[c]===void 0);if(i.length>0){x(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(he,"resolveModelRoutes");function Cn(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=Z.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 Z){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(Cn,"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=>ge(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 be=/^[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(!be.test(e))return`${n}.${e} is not a safe bare command name (expected ${be.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 _e=/^[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(_e.test(o)||r.push(`action name must match ${_e.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 Po(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(Po,"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 ve(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(ve,"normalizeKindFields");function R(e,n=un()){const o={...n};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){const i=ve(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(R,"mergeKinds");function we(e,n,o){if(n===null)return o;const t=ue(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=R(i.manifest.kinds,r);return r}s(we,"layerPluginKinds");function Co(){const e=[{origin:"config.json",components:kn("user").page_components}],n=g("user");if(n!==null){const o=ue("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:le(t.manifest.page_components,r)})}}return wn(e)}s(Co,"resolvePageComponents");const V=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))V.has(c)||n.push(`kinds.${t}.${c} is not a KindConfig field (expected one of: ${[...V].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`);ve(r)===null&&n.push(`kinds.${t} has no valid KindConfig field \u2014 an entry must set at least one of: ${[...V].join(", ")} (whenToUse is required when the kind exists in no lower layer)`)}return n}s(To,"invalidPluginKindsReasons");function Dn(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(Dn,"normalizeRemoteCanvasTarget");function zn(e,n=dn()){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=Dn(i);c!==null&&(o[r]=c)}}return{targets:o}}s(zn,"mergeRemoteCanvas");function ke(e,n){return e===!0?"notify":e===!1?!1:e==="notify"||e==="apply"?e:n}s(ke,"normalizeMode");function O(e,n){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=1?e:n}s(O,"normalizePositiveInteger");function Kn(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(Kn,"mergeBrokerThresholds");function Un(e,n){return{unattendedParkMs:O((e!==null&&typeof e=="object"?e:{}).unattendedParkMs,n.unattendedParkMs)}}s(Un,"mergeLifecycle");function Gn(e){if(!Array.isArray(e))return[...re];const n=e.filter(o=>typeof o=="string"&&o.trim()!=="").map(o=>o.trim());return n.length>0?n:[...re]}s(Gn,"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");let Ae=!1;function D(e){!Ae&&e.ledger!==void 0&&(Ae=!0,se({level:"warn",event:"config.ledger.ignored",fields:{variables:Object.values(pn)}}));const n=v(),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:ke(i?.crtr,n.auto_update.crtr),content:ke(i?.content,n.auto_update.content),interval_hours:u},f=e.max_panes_per_window,a=O(f,n.max_panes_per_window),l=typeof e.auto_open_child_viewers=="boolean"?e.auto_open_child_viewers:n.auto_open_child_viewers,m=Kn(e.brokerThresholds,n.brokerThresholds),y=Un(e.lifecycle,n.lifecycle),z=typeof e.completion_bell=="boolean"?e.completion_bell:n.completion_bell,p=e.working_gerunds===void 0?[...n.working_gerunds]:vn(e.working_gerunds),je=typeof e.whip_mode=="boolean"?e.whip_mode:n.whip_mode,xe=le(e.page_components),Re=typeof e.page_surface=="boolean"?e.page_surface:n.page_surface,Oe=Gn(e.whip_messages),Ee=sn.includes(e.whip_message_mode)?e.whip_message_mode:n.whip_message_mode,Pe=rn.includes(e.mouse_mode_default)?e.mouse_mode_default:n.mouse_mode_default,Ie=O(e.live_cycles,n.live_cycles),Ce=tn.includes(e.condensed_history)?e.condensed_history:n.condensed_history,Te=typeof e.bash_tool_purpose=="boolean"?e.bash_tool_purpose:n.bash_tool_purpose,$e=typeof e.fold_finished_tools=="boolean"?e.fold_finished_tools:n.fold_finished_tools,Me=typeof e.summarize_tool_calls=="boolean"?e.summarize_tool_calls:n.summarize_tool_calls,Ne=typeof e.detailed_tool_recaps=="boolean"?e.detailed_tool_recaps:n.detailed_tool_recaps,Le=Number.isInteger(e.document_port)&&e.document_port>=1&&e.document_port<=65535?e.document_port:n.document_port,Fe=typeof e.consolidate_replies=="boolean"?e.consolidate_replies:n.consolidate_replies,De=En(e.keybindings),ze=In(e.tmux_passthrough),Ke=T(e.modelLadders),Ue=L(e.providerOptions,n.providerOptions),K=he($(e.modelRoutes)),X=K.complete,Q=M(e.modelRouting,void 0),Ge=R(e.kinds),qe=zn(e.remoteCanvas),Be=qn(e.api,n.api),b=e.paths;if(b?.user_files!==void 0&&(typeof b.user_files!="string"||!b.user_files.startsWith("/")))throw new Error("paths.user_files must be an absolute path");const ee=e.store,_=e.runtime;if(_?.person_uids!==void 0&&(!Array.isArray(_.person_uids)||!_.person_uids.every(ne=>Number.isSafeInteger(ne)&&ne>=0)))throw new Error("runtime.person_uids must be an array of nonnegative integer uids");const We=N(e.spawnEnv,n.spawnEnv?.allow),He=Nn(e.bin),Je=Fn(e.humanActions);return{bin:He,humanActions:Je,schema_version:o,marketplaces:t,plugins:r,auto_update:d,max_panes_per_window:a,auto_open_child_viewers:l,brokerThresholds:m,lifecycle:y,completion_bell:z,working_gerunds:p,whip_mode:je,page_components:xe,page_surface:Re,whip_messages:Oe,whip_message_mode:Ee,mouse_mode_default:Pe,live_cycles:Ie,condensed_history:Ce,bash_tool_purpose:Te,fold_finished_tools:$e,summarize_tool_calls:Me,detailed_tool_recaps:Ne,document_port:Le,consolidate_replies:Fe,keybindings:De,tmux_passthrough:ze,modelLadders:Ke,providerOptions:Ue,...Object.keys(X).length>0?{modelRoutes:X}:{},...Object.keys(K.legacy).length>0?{legacyModelRoutes:K.legacy}:{},...Q!==void 0?{modelRouting:Q}:{},kinds:Ge,remoteCanvas:qe,api:Be,...b?{paths:b}:{},...ee?{store:ee}:{},..._?{runtime:_}:{},spawnEnv:We}}s(D,"mergeConfig");function Bn(e){return P(e)}s(Bn,"readRawConfigAtRoot");function Wn(e){const n=g(e);return n?Bn(n):null}s(Wn,"readRawScopeConfig");function Hn(e){if(e===null||e==="")return null;try{return P(de(e))}catch{return null}}s(Hn,"readRawProfileConfig");function Jn(e,n){return[...yn(e,n)].reverse().map(r=>({root:r,raw:P(r)}))}s(Jn,"readRawProjectScopeConfigs");function Se(e=process.cwd(),n=Ye()||null){const o=I[C];if(o)return o.launch;const t=v(),r=Wn("user"),i=Hn(n),c=Jn(e,n);let u=R(r?.kinds,we("user",g("user"),t.kinds)),d=T(r?.modelLadders,t.modelLadders),f=L(r?.providerOptions,t.providerOptions),a=$(r?.modelRoutes),l=M(r?.modelRouting,void 0),m=N(r?.spawnEnv,t.spawnEnv?.allow);i!==null&&(u=R(i.kinds,u),d=T(i.modelLadders,d),f=L(i.providerOptions,f),a=$(i.modelRoutes,a),l=M(i.modelRouting,l),m=N(i.spawnEnv,m.allow));for(const{root:z,raw:p}of c)u=R(p?.kinds,we("project",z,u)),p!==null&&(d=T(p.modelLadders,d),f=L(p.providerOptions,f),a=$(p.modelRoutes,a),l=M(p.modelRouting,l),m=N(p.spawnEnv,m.allow));const y=he(a);return{kinds:u,modelLadders:d,providerOptions:f,...Object.keys(y.complete).length>0?{modelRoutes:y.complete}:{},...Object.keys(y.legacy).length>0?{legacyModelRoutes:y.legacy}:{},...l!==void 0?{modelRouting:l}:{},spawnEnv:m}}s(Se,"readMergedLaunchConfig");function $o(e){return Se().kinds[e]}s($o,"resolveKindConfig");function Mo(e){const n=Se().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");async function Yn(e,n){return await S(e,o=>{const t=D(o),r=D(o);n(r);const i={...o};for(const u of _n)u!=="modelLadders"&&!en(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");async function No(e,n){await 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");async function Lo(e,n,o){if(o!==void 0)Cn(n,o);else if(n.trim()==="")throw new Error("model route id must be a non-empty string");await S(e,t=>{const r=ye(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");async function Vn(e,n){return await Yn(e,n)}s(Vn,"updateConfigAtRoot");async function Fo(e,n){return await Vn(w(e),n)}s(Fo,"updateConfig");async function Do(e){const n=e.orchestrator===!0?"orchestratorModel":"model";return await 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:h(e.root),field:n}}s(Do,"persistDefaultKindModel");async function zo(e,n){const o=jn(e);return n(o),await On(e,o),o}s(zo,"updateState");export{xn as InvalidRawSettingsError,Mn as binDeclarationIssues,ko as brokerConfigSnapshot,_o as configPath,Oo as ensureScopeInitialized,Ln as humanActionDeclarationIssues,wo as installBrokerConfigSnapshot,Eo as invalidPluginBinReasons,To as invalidPluginKindsReasons,Io as invalidPluginRequiresReasons,Nn as normalizeBin,Fn as normalizeHumanActions,Po as normalizeRequires,In as normalizeTmuxPassthrough,Do as persistDefaultKindModel,No as persistModelLadders,Lo as persistModelRoute,kn as readConfig,An as readConfigAtRoot,Se as readMergedLaunchConfig,Bn as readRawConfigAtRoot,Wn as readRawScopeConfig,Ao as readRawSettingsForTarget,jn as readState,So as replaceRawSettingsForTarget,$o as resolveKindConfig,Co as resolvePageComponents,vo as statePath,Mo as subKindsAvailableTo,Fo as updateConfig,Vn as updateConfigAtRoot,jo as updateRawConfigAtomically,zo as updateState,xo as writeConfig,Ro as writeRawConfig,On as writeState};
1
+ var Ye=Object.defineProperty;var s=(e,n)=>Ye(e,"name",{value:n,configurable:!0});import{envProfileId as Ve}from"../shared/env.js";import{basename as te,join as re}from"node:path";import{existsSync as Xe,mkdirSync as Qe,statSync as en}from"node:fs";import{isDeepStrictEqual as nn}from"node:util";import{CONFIG_FILE as on,STATE_FILE as tn,CONDENSED_HISTORY_MODES as rn,MOUSE_MODE_DEFAULTS as sn,WHIP_MESSAGE_MODES as cn,DEFAULT_WHIP_MESSAGES as ie,defaultScopeConfig as w,defaultScopeState as se,defaultModelLaddersConfig as un,defaultKindsConfig as dn,defaultRemoteCanvasConfig as an}from"../types.js";import{BINDING_CATALOG as fn,BINDING_IDS as ln,isAttachPaneBinding as mn}from"./keybindings/catalog.js";import{notFound as pn}from"./errors.js";import{emitEvent as ce}from"./events/emit.js";import{LEDGER_ENV as gn}from"./ledger/rates.js";import{atomicWriteJson as G,readJsonIfExists as E,ensureDir as yn}from"./fs-utils.js";import{scopeRoot as g,requireScopeRoot as k,findProjectScopeRoots as hn,userScopeRoot as ue}from"./scope.js";import{listInstalledPluginsInRoot as de}from"./installed-plugins.js";import{profileDir as ae,listProfiles as fe}from"./profiles/manifest.js";import{canonicalRoot as A}from"./scoped-state/paths.js";import{readCachedSettings as q,readCachedState as bn,revisionOf as B,SettingsRevisionConflictError as le,writeSettings as W}from"./scoped-state/person-store.js";import{withExclusiveDirectoryLock as _n}from"./exclusive-lock.js";import{SCOPE_CONFIG_KEYS as vn}from"./user-settings.js";import{normalizeWorkingGerunds as wn}from"../shared/working-activity.js";import{mergePageComponentLayers as kn,normalizePageComponents as me}from"./human/page-catalog.js";function h(e){return re(e,on)}s(h,"configPathFor");function H(e,n){return _n(`${e}.lock`,()=>n(e),{timeoutError:s(()=>new Error(`timed out waiting for config lock: ${e}`),"timeoutError")})}s(H,"withConfigPathLock");function J(e){return re(e,tn)}s(J,"statePathFor");function vo(e){const n=g(e);return n?h(n):null}s(vo,"configPath");function wo(e){const n=g(e);return n?J(n):null}s(wo,"statePath");function S(e){const n=A(e),o=A(ue());if(n===o)return{key:"user",kind:"user",root:o};if(te(n)===".crouter")return{key:"project",kind:"project",root:n};for(const t of fe()){const r=A(ae(t.profileId));if(n===r)return{key:`profile:${t.profileId}`,kind:"profile",root:r,profileId:t.profileId}}throw new Error(`settings root is not a recognized owner: ${e}`)}s(S,"settingsOwnerForRoot");function P(e){if(te(e)===".crouter"){let n;try{n=A(e)}catch(o){if(o.code==="ENOENT")return null;throw o}if(n!==A(ue()))return E(h(e))}return q(S(e).key).settings}s(P,"readRawAtRoot");async function pe(e,n){let o;return await W(t=>{const r=t.settings[e],i=n(structuredClone(r??{}));return o=i.value,[{op:"settings.replace",owner:e,raw:i.next,ifRevision:B(r)}]}),o}s(pe,"mutatePersonSettings");async function Z(e,n){const o=S(e);if(o.kind==="project"){H(h(o.root),t=>G(t,n,{createParent:!1}));return}await pe(o.key,()=>({value:void 0,next:n}))}s(Z,"writeRawAtRoot");async function j(e,n){const o=S(e);return o.kind==="project"?H(h(o.root),t=>{const r=n(E(t)??{});return G(t,r.next,{createParent:!1}),r.value}):await pe(o.key,n)}s(j,"mutateRawAtRoot");const C=globalThis,I=Symbol.for("crtr.broker.config-snapshot");function ko(e){C[I]=e}s(ko,"installBrokerConfigSnapshot");function Ao(){return C[I]}s(Ao,"brokerConfigSnapshot");function An(e){const n=C[I];if(n){if(e!=="user")throw new Error(`broker config snapshot has no ${e} scope`);return n.user}const o=g(e);return o?Sn(o):w()}s(An,"readConfig");function Sn(e){const n=P(e);return n?z(n):w()}s(Sn,"readConfigAtRoot");function jn(e){const n=E(J(e));return{marketplaces:n?.marketplaces??{},plugins:n?.plugins??{},activeCanvas:n?.activeCanvas??null,...n?.last_self_check===void 0?{}:{last_self_check:n.last_self_check}}}s(jn,"readProjectState");function xn(e){const n=g(e);return n?e==="project"?Xe(n)?jn(n):se():bn(S(n).key):se()}s(xn,"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 ge(e){if(e.scope==="user")return"user";if(!fe().some(n=>n.profileId===e.profileId))throw pn(`profile not found: ${e.profileId}`,{received:e.profileId,next:"Run `crtr profile list` to see available profiles."});return`profile:${e.profileId}`}s(ge,"rawSettingsKeyForTarget");function So(e){return q(ge(e))}s(So,"readRawSettingsForTarget");async function jo(e,n,o){const t=ge(e);try{z(n)}catch(u){throw new Rn(u)}if(q(t).revision!==o)throw new le;const i=await W(u=>B(u.settings[t])===o?[{op:"settings.replace",owner:t,raw:n,ifRevision:o}]:(()=>{throw new le})()),c=B(i.settings[t]);return{revision:c,changed:c!==o}}s(jo,"replaceRawSettingsForTarget");async function xo(e,n){const o=k(e);return await j(o,t=>{const r=n(t);return{value:r,next:r}})}s(xo,"updateRawConfigAtomically");async function Ro(e,n){await Z(k(e),n)}s(Ro,"writeConfig");async function Oo(e,n){await Z(k(e),n)}s(Oo,"writeRawConfig");function On(e,n){H(J(e),o=>G(o,n,{createParent:!1}))}s(On,"writeProjectState");async function En(e,n){const o=k(e);if(e==="project"){On(o,n);return}const t=S(o).key;await W(()=>[{op:"state.write",owner:t,state:n}])}s(En,"writeState");async function Eo(e,n){if(e!=="project"){yn(n);return}try{Qe(n)}catch(o){if(o.code!=="EEXIST"||!en(n).isDirectory())throw o}E(h(n))===null&&await Z(n,{schema_version:w().schema_version})}s(Eo,"ensureScopeInitialized");function Pn(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(Pn,"normalizeKeybindings");const Cn=new Set(fn.filter(mn).map(e=>e.id));function In(e){return Array.isArray(e)?[...new Set(e.filter(n=>typeof n=="string"&&ln.has(n)&&Cn.has(n)))]:[]}s(In,"normalizeTmuxPassthrough");function T(e,n=un()){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(T,"mergeModelLadders");function ye(e){return e==="ultra"||e==="strong"||e==="medium"||e==="light"}s(ye,"validStrength");const Y=["ultra","strong","medium","light"];function V(){return Object.create(null)}s(V,"routeRecord");function x(e,n,o){Object.defineProperty(e,n,{value:o,writable:!0,enumerable:!0,configurable:!0})}s(x,"setRouteRecord");function he(e){const n=V();for(const[o,t]of Object.entries(e??{}))x(n,o,t);return n}s(he,"copyRouteRecord");function R(e,n){ce({level:"warn",event:"config.model_routes.invalid",fields:{route_id:e,rule:n}})}s(R,"routeInvalid");function $(e,n={}){const o=he(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 f of["family","credentialSource","providerId"]){const a=i[f];if(a!==void 0){if(typeof a!="string"||a.trim()===""){d=`${f} must be a non-empty string`;break}u[f]=a}}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 f={...c?.models};for(const[a,l]of Object.entries(i.models)){if(!ye(a)){d=`models.${a} is not a model strength`;break}if(typeof l!="string"||l.trim()===""){d=`models.${a} must be a non-empty provider/id ref`;break}const m=l.indexOf("/");if(m<=0||m===l.length-1){d=`models.${a} must be provider/id`;break}f[a]=l}u.models=f}if(d===void 0&&u.models!==void 0&&u.providerId!==void 0){for(const[f,a]of Object.entries(u.models))if(a.slice(0,a.indexOf("/"))!==u.providerId){d=`models.${f} provider prefix must equal providerId`;break}}if(d!==void 0){R(t,d);continue}x(o,t,u)}return o}s($,"mergeModelRoutePatches");function be(e){const n=V(),o=V();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=Y.filter(c=>r.models[c]===void 0);if(i.length>0){R(t,`incomplete route; missing strengths: ${i.join(", ")}`),x(o,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models});continue}x(n,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models})}return{complete:n,legacy:o}}s(be,"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=Y.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 Y){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=>ye(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 $n(e){return e!==""&&e.replace(/\*/g,"")===""}s($n,"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($n(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 _e=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,Mn=new Set(["crtr","crtrd"]);function F(e,n){if(n==="bin"&&Mn.has(e))return`${n}.${e} is reserved by crouter \u2014 pick another bare command name`;if(!_e.test(e))return`${n}.${e} is not a safe bare command name (expected ${_e.source})`}s(F,"bareCommandNameIssue");function Nn(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(Nn,"binDeclarationIssues");function Ln(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(Ln,"normalizeBin");const ve=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function Fn(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(ve.test(o)||r.push(`action name must match ${ve.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(Fn,"humanActionDeclarationIssues");function Dn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n=new Set(Fn(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(Dn,"normalizeHumanActions");function Po(e){return Nn(e).map(n=>n.message)}s(Po,"invalidPluginBinReasons");function Co(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(Co,"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 we(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(we,"normalizeKindFields");function b(e,n=dn()){const o={...n};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){const i=we(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(b,"mergeKinds");let D;function To(e){D=e}s(To,"setAppPluginKinds");function ke(e,n,o){if(e==="user"&&D!==void 0){let i=o;for(const c of Object.keys(D).sort((u,d)=>u.localeCompare(d)))i=b(D[c],i);return i}if(n===null)return o;const t=de(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=b(i.manifest.kinds,r);return r}s(ke,"layerPluginKinds");function $o(){const e=[{origin:"config.json",components:An("user").page_components}],n=g("user");if(n!==null){const o=de("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:me(t.manifest.page_components,r)})}}return kn(e)}s($o,"resolvePageComponents");const X=new Set(["whenToUse","model","orchestratorModel","tools","extensions","availableTo"]);function Mo(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))X.has(c)||n.push(`kinds.${t}.${c} is not a KindConfig field (expected one of: ${[...X].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`);we(r)===null&&n.push(`kinds.${t} has no valid KindConfig field \u2014 an entry must set at least one of: ${[...X].join(", ")} (whenToUse is required when the kind exists in no lower layer)`)}return n}s(Mo,"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 Kn(e,n=an()){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(Kn,"mergeRemoteCanvas");function Ae(e,n){return e===!0?"notify":e===!1?!1:e==="notify"||e==="apply"?e:n}s(Ae,"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 Gn(e,n){return{unattendedParkMs:O((e!==null&&typeof e=="object"?e:{}).unattendedParkMs,n.unattendedParkMs)}}s(Gn,"mergeLifecycle");function qn(e){if(!Array.isArray(e))return[...ie];const n=e.filter(o=>typeof o=="string"&&o.trim()!=="").map(o=>o.trim());return n.length>0?n:[...ie]}s(qn,"normalizeWhipMessages");function Bn(e,n){if(e===null||typeof e!="object")return{...n};const o=e.tcp;return{tcp:typeof o=="string"?o:n.tcp}}s(Bn,"mergeApi");let Se=!1;function z(e){!Se&&e.ledger!==void 0&&(Se=!0,ce({level:"warn",event:"config.ledger.ignored",fields:{variables:Object.values(gn)}}));const n=w(),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:Ae(i?.crtr,n.auto_update.crtr),content:Ae(i?.content,n.auto_update.content),interval_hours:u},f=e.max_panes_per_window,a=O(f,n.max_panes_per_window),l=typeof e.auto_open_child_viewers=="boolean"?e.auto_open_child_viewers:n.auto_open_child_viewers,m=Un(e.brokerThresholds,n.brokerThresholds),y=Gn(e.lifecycle,n.lifecycle),K=typeof e.completion_bell=="boolean"?e.completion_bell:n.completion_bell,p=e.working_gerunds===void 0?[...n.working_gerunds]:wn(e.working_gerunds),xe=typeof e.whip_mode=="boolean"?e.whip_mode:n.whip_mode,Re=me(e.page_components),Oe=typeof e.page_surface=="boolean"?e.page_surface:n.page_surface,Ee=qn(e.whip_messages),Pe=cn.includes(e.whip_message_mode)?e.whip_message_mode:n.whip_message_mode,Ce=sn.includes(e.mouse_mode_default)?e.mouse_mode_default:n.mouse_mode_default,Ie=O(e.live_cycles,n.live_cycles),Te=rn.includes(e.condensed_history)?e.condensed_history:n.condensed_history,$e=typeof e.bash_tool_purpose=="boolean"?e.bash_tool_purpose:n.bash_tool_purpose,Me=typeof e.fold_finished_tools=="boolean"?e.fold_finished_tools:n.fold_finished_tools,Ne=typeof e.summarize_tool_calls=="boolean"?e.summarize_tool_calls:n.summarize_tool_calls,Le=typeof e.detailed_tool_recaps=="boolean"?e.detailed_tool_recaps:n.detailed_tool_recaps,Fe=Number.isInteger(e.document_port)&&e.document_port>=1&&e.document_port<=65535?e.document_port:n.document_port,De=typeof e.consolidate_replies=="boolean"?e.consolidate_replies:n.consolidate_replies,ze=Pn(e.keybindings),Ke=In(e.tmux_passthrough),Ue=T(e.modelLadders),Ge=L(e.providerOptions,n.providerOptions),U=be($(e.modelRoutes)),Q=U.complete,ee=M(e.modelRouting,void 0),qe=b(e.kinds),Be=Kn(e.remoteCanvas),We=Bn(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 ne=e.store,v=e.runtime;if(v?.person_uids!==void 0&&(!Array.isArray(v.person_uids)||!v.person_uids.every(oe=>Number.isSafeInteger(oe)&&oe>=0)))throw new Error("runtime.person_uids must be an array of nonnegative integer uids");const He=N(e.spawnEnv,n.spawnEnv?.allow),Je=Ln(e.bin),Ze=Dn(e.humanActions);return{bin:Je,humanActions:Ze,schema_version:o,marketplaces:t,plugins:r,auto_update:d,max_panes_per_window:a,auto_open_child_viewers:l,brokerThresholds:m,lifecycle:y,completion_bell:K,working_gerunds:p,whip_mode:xe,page_components:Re,page_surface:Oe,whip_messages:Ee,whip_message_mode:Pe,mouse_mode_default:Ce,live_cycles:Ie,condensed_history:Te,bash_tool_purpose:$e,fold_finished_tools:Me,summarize_tool_calls:Ne,detailed_tool_recaps:Le,document_port:Fe,consolidate_replies:De,keybindings:ze,tmux_passthrough:Ke,modelLadders:Ue,providerOptions:Ge,...Object.keys(Q).length>0?{modelRoutes:Q}:{},...Object.keys(U.legacy).length>0?{legacyModelRoutes:U.legacy}:{},...ee!==void 0?{modelRouting:ee}:{},kinds:qe,remoteCanvas:Be,api:We,..._?{paths:_}:{},...ne?{store:ne}:{},...v?{runtime:v}:{},spawnEnv:He}}s(z,"mergeConfig");function Wn(e){return P(e)}s(Wn,"readRawConfigAtRoot");function Hn(e){const n=g(e);return n?Wn(n):null}s(Hn,"readRawScopeConfig");function Jn(e){if(e===null||e==="")return null;try{return P(ae(e))}catch{return null}}s(Jn,"readRawProfileConfig");function Zn(e,n){return[...hn(e,n)].reverse().map(r=>({root:r,raw:P(r)}))}s(Zn,"readRawProjectScopeConfigs");function je(e=process.cwd(),n=Ve()||null){const o=C[I];if(o)return o.launch;const t=w(),r=Hn("user"),i=Jn(n),c=Zn(e,n);let u=b(r?.kinds,ke("user",g("user"),t.kinds)),d=T(r?.modelLadders,t.modelLadders),f=L(r?.providerOptions,t.providerOptions),a=$(r?.modelRoutes),l=M(r?.modelRouting,void 0),m=N(r?.spawnEnv,t.spawnEnv?.allow);i!==null&&(u=b(i.kinds,u),d=T(i.modelLadders,d),f=L(i.providerOptions,f),a=$(i.modelRoutes,a),l=M(i.modelRouting,l),m=N(i.spawnEnv,m.allow));for(const{root:K,raw:p}of c)u=b(p?.kinds,ke("project",K,u)),p!==null&&(d=T(p.modelLadders,d),f=L(p.providerOptions,f),a=$(p.modelRoutes,a),l=M(p.modelRouting,l),m=N(p.spawnEnv,m.allow));const y=be(a);return{kinds:u,modelLadders:d,providerOptions:f,...Object.keys(y.complete).length>0?{modelRoutes:y.complete}:{},...Object.keys(y.legacy).length>0?{legacyModelRoutes:y.legacy}:{},...l!==void 0?{modelRouting:l}:{},spawnEnv:m}}s(je,"readMergedLaunchConfig");function No(e){return je().kinds[e]}s(No,"resolveKindConfig");function Lo(e){const n=je().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(Lo,"subKindsAvailableTo");function Yn(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(Yn,"sparseModelLaddersToPersist");async function Vn(e,n){return await j(e,o=>{const t=z(o),r=z(o);n(r);const i={...o};for(const u of vn)u!=="modelLadders"&&!nn(r[u],t[u])&&(i[u]=r[u]);const c=Yn(o.modelLadders,t.modelLadders,r.modelLadders);return c!==void 0?i.modelLadders=c:delete i.modelLadders,{value:r,next:i}})}s(Vn,"mutateConfigAtRoot");async function Fo(e,n){await j(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(Fo,"persistModelLadders");async function Do(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");await j(e,t=>{const r=he(t.modelRoutes??{});o===void 0?delete r[n]:x(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(Do,"persistModelRoute");async function Xn(e,n){return await Vn(e,n)}s(Xn,"updateConfigAtRoot");async function zo(e,n){return await Xn(k(e),n)}s(zo,"updateConfig");async function Ko(e){const n=e.orchestrator===!0?"orchestratorModel":"model";return await j(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:h(e.root),field:n}}s(Ko,"persistDefaultKindModel");async function Uo(e,n){const o=xn(e);return n(o),await En(e,o),o}s(Uo,"updateState");export{Rn as InvalidRawSettingsError,Nn as binDeclarationIssues,Ao as brokerConfigSnapshot,vo as configPath,Eo as ensureScopeInitialized,Fn as humanActionDeclarationIssues,ko as installBrokerConfigSnapshot,Po as invalidPluginBinReasons,Mo as invalidPluginKindsReasons,Io as invalidPluginRequiresReasons,Ln as normalizeBin,Dn as normalizeHumanActions,Co as normalizeRequires,In as normalizeTmuxPassthrough,Ko as persistDefaultKindModel,Fo as persistModelLadders,Do as persistModelRoute,An as readConfig,Sn as readConfigAtRoot,je as readMergedLaunchConfig,Wn as readRawConfigAtRoot,Hn as readRawScopeConfig,So as readRawSettingsForTarget,xn as readState,jo as replaceRawSettingsForTarget,No as resolveKindConfig,$o as resolvePageComponents,To as setAppPluginKinds,wo as statePath,Lo as subKindsAvailableTo,zo as updateConfig,Xn as updateConfigAtRoot,xo as updateRawConfigAtomically,Uo as updateState,Ro as writeConfig,Oo as writeRawConfig,En as writeState};
@@ -1,11 +1,11 @@
1
- var F=Object.defineProperty;var p=(t,e)=>F(t,"name",{value:e,configurable:!0});import{isSafeNodeId as H}from"../canvas/paths.js";import{OBJECT_TYPE as x}from"../permissions/builtin.js";import{ceilingFrag as W,resolveCeiling as v}from"../permissions/ceiling.js";import{compilePermission as Y}from"../permissions/compile.js";import{permits as k,readerUser as q,resolveReader as A}from"../permissions/evaluator.js";import{loadDefinitions as B}from"../permissions/schemas.js";import{and as P,or as b,raw as I}from"../permissions/sql.js";import{getById as J}from"./objects.js";import{flatMapAsync as O}from"../async-array.js";async function g(t,e){const o=await q(t,e),s=o===null?void 0:await t.prepare(`SELECT o.* FROM objects o JOIN identities i ON i.object = o.id AND i.scheme = 'user'
2
- WHERE o.type = 'person' AND o.home_machine IS NULL AND regexp_replace(i.external_id, '^user:', '') = ?`).get(o),a=await t.prepare(`SELECT a.* FROM objects a WHERE a.type = 'app' AND a.deleted_at IS NULL AND EXISTS (
1
+ var x=Object.defineProperty;var p=(t,e)=>x(t,"name",{value:e,configurable:!0});import{isSafeNodeId as F}from"../canvas/paths.js";import{OBJECT_TYPE as H}from"../permissions/builtin.js";import{ceilingFrag as W,readablePackageApps as v,resolveCeiling as Y}from"../permissions/ceiling.js";import{compilePermission as k}from"../permissions/compile.js";import{permits as P,readerUser as b,resolveReader as y}from"../permissions/evaluator.js";import{loadDefinitions as q}from"../permissions/schemas.js";import{and as B,or as J,raw as g}from"../permissions/sql.js";import{getById as V}from"./objects.js";import{flatMapAsync as O}from"../async-array.js";async function I(t,e){const o=await b(t,e),i=o===null?void 0:await t.prepare(`SELECT o.* FROM objects o JOIN identities i ON i.object = o.id AND i.scheme = 'user'
2
+ WHERE o.type = 'person' AND o.home_machine IS NULL AND regexp_replace(i.external_id, '^user:', '') = ?`).get(o),a=await v(t,e,o),c=await t.prepare(`SELECT a.* FROM objects a WHERE a.type = 'app' AND a.deleted_at IS NULL AND ${a===null?`EXISTS (
3
3
  SELECT 1 FROM objects o JOIN documents d ON d.object = o.id
4
- WHERE o.owner = a.id AND o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL)
5
- ORDER BY a.name = 'crtr', a.name`).all(),c=e.class==="node"?[e.node,...e.repos,...e.projectRepos,e.profile,e.app]:e.class==="app"?[e.app]:e.class==="object"?[e.owner,...e.repos,e.app]:[],r=new Set;return[...c,s,...a].filter(i=>!!i&&!r.has(i.id)&&!!r.add(i.id))}p(g,"viewOf");async function V(t,e,o){const s=await k(t,e,"read",o),a=await v(t,e.reader,"read"),c=Y(await B(t),e.subjects,x,"read",I("gr.id")),r={sql:`EXISTS (WITH RECURSIVE up(id, depth) AS (
4
+ WHERE o.owner = a.id AND o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL)`:"a.id = ANY(?::text[])"}
5
+ ORDER BY a.name = 'crtr', a.name`).all(...a===null?[]:[a]),r=e.class==="node"?[e.node,...e.repos,...e.projectRepos,e.profile,e.app]:e.class==="app"?[e.app]:e.class==="object"?[e.owner,...e.repos,e.app]:[],s=new Set;return[...r,i,...c].filter(l=>!!l&&!s.has(l.id)&&!!s.add(l.id))}p(I,"viewOf");async function X(t,e,o){const i=await P(t,e,"read",o),a=await Y(t,e.reader,e.userId,"read"),c=k(await q(t),e.subjects,H,"read",g("gr.id")),r={sql:`EXISTS (WITH RECURSIVE up(id, depth) AS (
6
6
  SELECT ${o}.id, 0
7
7
  UNION ALL SELECT p.owner, up.depth + 1 FROM up JOIN objects p ON p.id = up.id WHERE p.owner IS NOT NULL AND up.depth < 12)
8
- SELECT 1 FROM up gr WHERE ${c.sql})`,params:c.params},i=P([I(`${o}.deleted_at IS NOT NULL AND ${o}.schema_type IS NULL`),...a===null?[]:[W(a,o)],r]);return b([s,i])}p(V,"readableOrDeleted");const S=new WeakMap;function D(t,e){let o=S.get(e);return o===void 0&&(o=V(t,e,"o"),o.catch(()=>S.delete(e)),S.set(e,o)),o}p(D,"readableFrag");async function X(t,e,o){const s=await D(t,e);return(await t.prepareUncached(`SELECT ${s.sql} AS ok FROM objects o WHERE o.id = ?`).get(...s.params,o))?.ok===!0}p(X,"canReadOrDeleted");async function E(t,e,o,s,a,c=!1){const r=await D(t,e);return await t.prepareUncached(`SELECT o.* FROM objects o WHERE (o.type = 'document' OR strpos(o.type, '.') > 0) AND o.owner = ? AND o.name = ? AND o.home_machine IS ${c?"NOT NULL":"NULL"}
9
- AND o.deleted_at IS ${a?"NOT NULL":"NULL"} AND ${r.sql}${a?" ORDER BY o.deleted_at DESC":""}`).all(o.id,s,...r.params)}p(E,"namedObjects");const re=["person","app","repo","profile"],T=/^(person|app|repo|profile|node):([^/]+)\/(.+)$/,h=p(t=>t.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim(),"unwrapRef");function j(t){const e=T.exec(t);if(e)return{type:e[1],handle:e[2],rest:e[3]};const o=t.indexOf("/");return o>0?{handle:t.slice(0,o),rest:t.slice(o+1)}:null}p(j,"splitOwnerRef");async function U(t,e,o){return o==="node"?await t.prepare("SELECT * FROM objects WHERE type = 'node' AND id = ?").all(e):o?await t.prepare("SELECT * FROM objects WHERE type = ? AND (name = ? OR id = ?) AND home_machine IS NULL").all(o,e,e):await t.prepare(`SELECT * FROM objects WHERE (type = 'node' AND id = ?)
10
- OR (type IN ('person','app','repo','profile') AND (name = ? OR id = ?) AND home_machine IS NULL)`).all(e,e,e)}p(U,"ownersByHandle");const _=/^([^:/]+):reports\/(.+?)(?:\.md)?$/;async function ie(t,e,o){const s=[...new Set(o.filter(n=>h(n)===n&&!_.test(n)))],a=new Map;if(s.length===0||e.view.length===0)return a;const c=new Set((await t.prepare("SELECT id FROM objects WHERE id = ANY(?::text[])").all(s)).map(n=>n.id)),r=s.filter(n=>!c.has(n));for(const n of r)a.set(n,null);const i=await D(t,e.ev),w=await t.prepareUncached(`SELECT o.id, o.owner, o.name FROM objects o WHERE (o.type = 'document' OR strpos(o.type, '.') > 0)
11
- AND o.owner = ANY(?::text[]) AND o.name = ANY(?::text[]) AND o.home_machine IS NULL AND o.deleted_at IS NULL AND ${i.sql}`).all(e.view.map(n=>n.id),r,...i.params),m=new Map(e.view.map((n,d)=>[n.id,d])),l=new Map;for(const n of w){const d=m.get(n.owner);d<(l.get(n.name)??1/0)&&(l.set(n.name,d),a.set(n.name,n.id))}return a}p(ie,"viewResolutions");async function se(t,e,o,s){let a=h(o);const c=_.exec(a);c&&!T.test(a)&&(a=`${c[1]}/reports/${c[2]}`);const r=s?.ev??await A(t,e),i=await J(t,a);if(i&&await X(t,r,i.id))return i.deleted_at?{deleted:i}:{object:i};const w=s?.view??await g(t,e);for(const l of w){const n=(await E(t,r,l,a,!1))[0];if(n)return{object:n}}const m=j(a);if(m){const{type:l,handle:n,rest:d}=m,C=l!==void 0,N=await U(t,n,l),R=p(f=>{const u=w.findIndex($=>$.id===f.id);return u<0?1/0:u},"rank"),M=N.filter(f=>R(f)<1/0).sort((f,u)=>R(f)-R(u));for(const f of M){const u=(await E(t,r,f,d,!1))[0];if(u)return{object:u}}const L=await O(N,async f=>await E(t,r,f,d,!1));if(L.length===1)return{object:L[0]};if(L.length>1)return{ambiguous:L};const y=(await O(N,async f=>await E(t,r,f,d,!0)))[0];return y?{deleted:y}:!C&&N.length===0&&H(n)?{remote:a}:{missing:!0}}for(const l of w){const n=(await E(t,r,l,a,!0))[0];if(n)return{deleted:n}}return{missing:!0}}p(se,"resolveName");async function ce(t,e,o){const s=h(o),a=await A(t,e),c=p(async(w,m)=>await O(w,async l=>await E(t,a,l,m,!1,!0)),"found"),r=await c(await g(t,e),s);if(r.length)return r;const i=j(s);return i?await c(await U(t,i.handle,i.type),i.rest):[]}p(ce,"stubsNamed");export{re as HANDLE_TYPES,X as canReadOrDeleted,U as ownersByHandle,V as readableOrDeleted,se as resolveName,ce as stubsNamed,h as unwrapRef,g as viewOf,ie as viewResolutions};
8
+ SELECT 1 FROM up gr WHERE ${c.sql})`,params:c.params},s=B([g(`${o}.deleted_at IS NOT NULL AND ${o}.schema_type IS NULL`),...a===null?[]:[W(a,o)],r]);return J([i,s])}p(X,"readableOrDeleted");const S=new WeakMap;function A(t,e){let o=S.get(e);return o===void 0&&(o=X(t,e,"o"),o.catch(()=>S.delete(e)),S.set(e,o)),o}p(A,"readableFrag");async function z(t,e,o){const i=await A(t,e);return(await t.prepareUncached(`SELECT ${i.sql} AS ok FROM objects o WHERE o.id = ?`).get(...i.params,o))?.ok===!0}p(z,"canReadOrDeleted");async function E(t,e,o,i,a,c=!1){const r=await A(t,e);return await t.prepareUncached(`SELECT o.* FROM objects o WHERE (o.type = 'document' OR strpos(o.type, '.') > 0) AND o.owner = ? AND o.name = ? AND o.home_machine IS ${c?"NOT NULL":"NULL"}
9
+ AND o.deleted_at IS ${a?"NOT NULL":"NULL"} AND ${r.sql}${a?" ORDER BY o.deleted_at DESC":""}`).all(o.id,i,...r.params)}p(E,"namedObjects");const ie=["person","app","repo","profile"],T=/^(person|app|repo|profile|node):([^/]+)\/(.+)$/,D=p(t=>t.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim(),"unwrapRef");function j(t){const e=T.exec(t);if(e)return{type:e[1],handle:e[2],rest:e[3]};const o=t.indexOf("/");return o>0?{handle:t.slice(0,o),rest:t.slice(o+1)}:null}p(j,"splitOwnerRef");async function U(t,e,o){return o==="node"?await t.prepare("SELECT * FROM objects WHERE type = 'node' AND id = ?").all(e):o?await t.prepare("SELECT * FROM objects WHERE type = ? AND (name = ? OR id = ?) AND home_machine IS NULL").all(o,e,e):await t.prepare(`SELECT * FROM objects WHERE (type = 'node' AND id = ?)
10
+ OR (type IN ('person','app','repo','profile') AND (name = ? OR id = ?) AND home_machine IS NULL)`).all(e,e,e)}p(U,"ownersByHandle");const _=/^([^:/]+):reports\/(.+?)(?:\.md)?$/;async function se(t,e,o){const i=[...new Set(o.filter(n=>D(n)===n&&!_.test(n)))],a=new Map;if(i.length===0||e.view.length===0)return a;const c=new Set((await t.prepare("SELECT id FROM objects WHERE id = ANY(?::text[])").all(i)).map(n=>n.id)),r=i.filter(n=>!c.has(n));for(const n of r)a.set(n,null);const s=await A(t,e.ev),l=await t.prepareUncached(`SELECT o.id, o.owner, o.name FROM objects o WHERE (o.type = 'document' OR strpos(o.type, '.') > 0)
11
+ AND o.owner = ANY(?::text[]) AND o.name = ANY(?::text[]) AND o.home_machine IS NULL AND o.deleted_at IS NULL AND ${s.sql}`).all(e.view.map(n=>n.id),r,...s.params),m=new Map(e.view.map((n,u)=>[n.id,u])),f=new Map;for(const n of l){const u=m.get(n.owner);u<(f.get(n.name)??1/0)&&(f.set(n.name,u),a.set(n.name,n.id))}return a}p(se,"viewResolutions");async function ce(t,e,o,i){let a=D(o);const c=_.exec(a);c&&!T.test(a)&&(a=`${c[1]}/reports/${c[2]}`);const r=i?.ev??await y(t,e),s=await V(t,a);if(s&&await z(t,r,s.id))return s.deleted_at?{deleted:s}:{object:s};const l=i?.view??await I(t,e);for(const f of l){const n=(await E(t,r,f,a,!1))[0];if(n)return{object:n}}const m=j(a);if(m){const{type:f,handle:n,rest:u}=m,C=f!==void 0,N=await U(t,n,f),R=p(d=>{const w=l.findIndex($=>$.id===d.id);return w<0?1/0:w},"rank"),M=N.filter(d=>R(d)<1/0).sort((d,w)=>R(d)-R(w));for(const d of M){const w=(await E(t,r,d,u,!1))[0];if(w)return{object:w}}const L=await O(N,async d=>await E(t,r,d,u,!1));if(L.length===1)return{object:L[0]};if(L.length>1)return{ambiguous:L};const h=(await O(N,async d=>await E(t,r,d,u,!0)))[0];return h?{deleted:h}:!C&&N.length===0&&F(n)?{remote:a}:{missing:!0}}for(const f of l){const n=(await E(t,r,f,a,!0))[0];if(n)return{deleted:n}}return{missing:!0}}p(ce,"resolveName");async function pe(t,e,o){const i=D(o),a=await y(t,e),c=p(async(l,m)=>await O(l,async f=>await E(t,a,f,m,!1,!0)),"found"),r=await c(await I(t,e),i);if(r.length)return r;const s=j(i);return s?await c(await U(t,s.handle,s.type),s.rest):[]}p(pe,"stubsNamed");export{ie as HANDLE_TYPES,z as canReadOrDeleted,U as ownersByHandle,X as readableOrDeleted,ce as resolveName,pe as stubsNamed,D as unwrapRef,I as viewOf,se as viewResolutions};
@@ -1,14 +1,8 @@
1
- import { type QuarantinedPlugin } from '../plugin-quarantine.js';
2
1
  import { type PackageFile } from './package-files.js';
3
2
  import type { ReposFn } from './reader.js';
4
3
  import type { ObjectRow } from './types.js';
4
+ import type { QuarantinedPlugin } from '../plugin-quarantine.js';
5
5
  import type { CanvasDb } from '../canvas/db.js';
6
- export interface PackageSource {
7
- plugin: string;
8
- dir: string;
9
- }
10
- /** The builtin corpus (app `crtr`), then every enabled user-scope plugin's `memory/` folder. */
11
- export declare function packageSources(): PackageSource[];
12
6
  export interface PackageReconcileReport {
13
7
  created: number;
14
8
  updated: number;
@@ -25,17 +19,14 @@ export interface PackageReconcileReport {
25
19
  /** plugin directories with an unreadable manifest or command file, moved aside before reconciling */
26
20
  quarantined: QuarantinedPlugin[];
27
21
  }
28
- /** Converge every package's documents onto its files. Run inside the caller's transaction.
29
- * Idempotent: a second run over unchanged files changes no document, revision or rule (it re-derives
30
- * each document's link edges, which land the same). A changed body replaces the
31
- * document's single revision and publishes `edited`; a vanished file (or plugin) marks its
32
- * document deleted and publishes `deleted`. */
33
- export declare function reconcilePackageDocuments(db: CanvasDb, explicitSources?: readonly PackageSource[]): Promise<PackageReconcileReport>;
34
22
  /** Record a package document's `links-to` edges the way a runtime document gets them, so delivery
35
- * attaches the link previews. Each `[[name]]` resolves in its own app's view and links only when it
36
- * names another shipped document (builtin or plugin): a package never links to a user's or a
23
+ * attaches the link previews. Each `[[name]]` links only when it names a shipped document of any
24
+ * published package (builtin, provider or plugin): a package never links to a user's or a
37
25
  * runtime document that happens to share a bare name. A name that resolves to nothing stays text.
38
26
  * Edges are rewritten only when they differ, so an unchanged pass writes nothing. */
39
27
  export declare function deriveLinks(db: CanvasDb, owner: ObjectRow, doc: ObjectRow, body: string): Promise<void>;
28
+ /** Re-derive the links of every live shipped document of every app. A package published before the one its documents name has no edge to it yet;
29
+ * publishing the missing one calls this, so the edges do not depend on which package arrived first. */
30
+ export declare function relinkAllShipped(db: CanvasDb): Promise<void>;
40
31
  export declare function convergeFile(db: CanvasDb, owner: ObjectRow, file: PackageFile, report: PackageReconcileReport, reposFn: ReposFn): Promise<string>;
41
32
  export declare function deleteMissing(db: CanvasDb, owner: ObjectRow, keep: ReadonlySet<string>, report: PackageReconcileReport, reposFn: ReposFn): Promise<void>;
@@ -1,6 +1,8 @@
1
- var S=Object.defineProperty;var u=(e,a)=>S(e,"name",{value:a,configurable:!0});import{randomUUID as _}from"node:crypto";import{CrtrError as g}from"../errors.js";import{emitEvent as O}from"../events/emit.js";import{quarantineBrokenPlugins as I}from"../plugin-quarantine.js";import{listInstalledPlugins as D}from"../resolver.js";import{builtinMemoryRoot as T,pluginMemoryDir as R}from"../scope.js";import{documentFields as L}from"../../migrations/004-canvas-documents/fields.js";import{put as A}from"./bodies.js";import{applyDocumentFields as k,deleteDocument as j,documentRecord as F,heads as x,importDocument as C,linksIn as M,moveDocument as U}from"./documents.js";import{deleteShippedReader as H,writeShippedReader as W}from"../permissions/facts.js";import{setLinks as J}from"./edges.js";import{publish as b}from"./events.js";import{resolveName as P}from"./names.js";import{packageFiles as q}from"./package-files.js";import{ensureOwner as B,getById as y,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:T()},...D("user").filter(e=>e.enabled).map(e=>({plugin:e.name,dir:R(e)}))]}u(G,"packageSources");async function Ee(e,a){const n={created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:[]};if(a===void 0){n.quarantined=I();for(const t of n.quarantined)O({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 l=a??G(),m=u(async t=>await $(e,t),"reposFn"),o=new Set,r=[],f=new Set;for(const t of l){let s;try{if(f.has(t.plugin))throw new g("conflict",`plugin "${t.plugin}" is already provided by another package`);f.add(t.plugin),s=await B(e,"app","app",`app:${t.plugin}`,t.plugin)}catch(p){if(!(p instanceof g)||p.code!=="conflict")throw p;n.conflicts.push({plugin:t.plugin,message:p.message});continue}o.add(s.id);const{files:c,invalid:w}=q(t.dir);n.invalid.push(...w);const E=new Set;for(const p of c){const d=await X(e,s,p,n,m);E.add(d),r.push({owner:s,id:d,body:p.body})}await v(e,s,E,n,m)}const i=await e.prepare(`SELECT DISTINCT o.owner AS id FROM objects o JOIN documents d ON d.object = o.id
2
- WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{id:t}of i)o.has(t)||await v(e,await y(e,t),new Set,n,m);for(const{owner:t,id:s,body:c}of r)await Q(e,t,await y(e,s),c);return n}u(Ee,"reconcilePackageDocuments");async function Q(e,a,n,l){if(n.deleted_at!==null)return;const m={class:"app",app:a,scopes:[]},o=[];for(const i of M(l)){const t=await P(e,m,i);"object"in t&&t.object.id!==n.id&&await V(e,t.object.id)&&o.push({written_as:i,dst:t.object})}const r=u(i=>JSON.stringify(i.map(t=>[t.dst,t.written_as]).sort()),"key"),f=await e.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ?").all(n.id);r(f)!==r(o.map(i=>({dst:i.dst.id,written_as:i.written_as})))&&await J(e,n,o)}u(Q,"deriveLinks");async function V(e,a){return await e.prepare("SELECT 1 FROM documents WHERE object = ? AND source_path IS NOT NULL").get(a)!==void 0}u(V,"shippedDocument");async function X(e,a,n,l,m){const o=L(n.frontmatter,n.body,{missingPrivacy:"public"}),r={preview:o.preview.trim()===""?n.name:o.preview,summary:o.summary,whyItExists:o.whyItExists,unlisted:o.unlisted,rules:o.rules,lintIgnore:Array.isArray(o.lintIgnore)?o.lintIgnore.filter(d=>typeof d=="string"):null,extensions:o.extensions!==null&&typeof o.extensions=="object"&&!Array.isArray(o.extensions)?o.extensions:null},f=await A(e,n.body);let i=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
3
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(a.id,n.name);if(i?await e.prepare("UPDATE documents SET source_path = ? WHERE object = ? AND source_path IS DISTINCT FROM ?").run(n.path,i.id,n.path):i=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
4
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path = ?`).get(a.id,n.path),!i){const d=await C(e,{owner:a,name:n.name,kind:o.kind,privacy:"public",...r,revisions:[{body_hash:f,author:a,rationale:null,created_at:new Date().toISOString()}]});return await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(n.path,d.id),await W(e,d.id),l.created+=1,d.id}const t=i.name!==n.name||i.kind!==o.kind;i.name!==n.name&&(i=await U(e,null,i,{name:n.name})),i.kind!==o.kind&&(await K(e,i,o.kind),i=await y(e,i.id));const s=await F(e,i),[c]=await x(e,i),w=c?.body_hash!==f,E=i.preview!==r.preview||s.summary!==r.summary||s.why_it_exists!==r.whyItExists||s.unlisted!==r.unlisted||JSON.stringify(s.lint_ignore)!==JSON.stringify(r.lintIgnore)||JSON.stringify(s.extensions)!==JSON.stringify(r.extensions)||JSON.stringify((await z(e,i)).map(N))!==JSON.stringify(r.rules.map(N));if(!w&&!E&&!t)return i.id;const p=await k(e,i,r);if(w&&c){const d=_(),h=c.number+1;await e.prepare(`INSERT INTO revisions (rev_id, document, number, body_hash, author, rationale, verbatim, created_at)
5
- SELECT ?, document, ?, ?, author, rationale, verbatim, ? FROM revisions WHERE rev_id = ?`).run(d,h,f,new Date().toISOString(),c.rev_id),await e.prepare("INSERT INTO revision_parents (child, parent) SELECT ?, parent FROM revision_parents WHERE child = ?").run(d,c.rev_id),await e.prepare("DELETE FROM revisions WHERE rev_id = ?").run(c.rev_id),await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(d,h,p.id),await b(e,{event:"edited",object:p,actor:a,pointer:d,attrs:{revision:h,from_revision:c.number,rationale:"package file changed"}},m)}return l.updated+=1,p.id}u(X,"convergeFile");function N(e){return[e.on,e.deliver,e.match??null,e.matchFrontmatter??null,e.if??null]}u(N,"ruleKey");async function v(e,a,n,l,m){const o=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
6
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(a.id);for(const r of o)n.has(r.id)||(await j(e,null,r,m),await H(e,r.id),l.deleted+=1)}u(v,"deleteMissing");export{X as convergeFile,v as deleteMissing,Q as deriveLinks,G as packageSources,Ee as reconcilePackageDocuments};
1
+ var S=Object.defineProperty;var p=(e,a)=>S(e,"name",{value:a,configurable:!0});import{randomUUID as I}from"node:crypto";import{documentFields as _}from"../../migrations/004-canvas-documents/fields.js";import{get as D,put as L}from"./bodies.js";import{applyDocumentFields as R,deleteDocument as v,documentRecord as g,heads as h,importDocument as A,linksIn as T,moveDocument as x}from"./documents.js";import{deleteShippedReader as j,writeShippedReader as k}from"../permissions/facts.js";import{setLinks as U}from"./edges.js";import{publish as F}from"./events.js";import{getById as l,setObjectKind as J}from"./objects.js";import{rulesOf as C}from"./rules.js";async function H(e,a,i){const d=/^(?:app:)?([^/:]+)\/(.+)$/.exec(i),s=[{app:null,doc:i}];d!==null&&s.push({app:d[1],doc:d[2]});for(const{app:t,doc:o}of s){const r=await e.prepare(`SELECT o.id FROM objects o JOIN documents d ON d.object = o.id AND d.source_path IS NOT NULL JOIN objects a ON a.id = o.owner AND a.type = 'app'
2
+ WHERE o.type = 'document' AND o.deleted_at IS NULL AND o.home_machine IS NULL AND o.name = ? AND (?::text IS NULL OR a.name = ?)
3
+ ORDER BY (o.owner = ?) DESC, a.name = 'crtr', a.name LIMIT 1`).get(o,t,t,a.id);if(r!==void 0)return await l(e,r.id)??void 0}}p(H,"shippedNamed");async function M(e,a,i,d){if(i.deleted_at!==null)return;const s=[];for(const r of T(d)){const n=await H(e,a,r);n!==void 0&&n.id!==i.id&&s.push({written_as:r,dst:n})}const t=p(r=>JSON.stringify(r.map(n=>[n.dst,n.written_as]).sort()),"key"),o=await e.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.id);t(o)!==t(s.map(r=>({dst:r.dst.id,written_as:r.written_as})))&&await U(e,i,s)}p(M,"deriveLinks");async function Q(e){const a=await e.prepare(`SELECT o.id, o.owner FROM objects o JOIN documents d ON d.object = o.id
4
+ WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL ORDER BY o.id`).all();for(const{id:i,owner:d}of a){const s=await l(e,i),[t]=await h(e,s);t!==void 0&&await M(e,await l(e,d),s,await D(e,t.body_hash))}}p(Q,"relinkAllShipped");async function V(e,a,i,d,s){const t=_(i.frontmatter,i.body,{missingPrivacy:"public"}),o={preview:t.preview.trim()===""?i.name:t.preview,summary:t.summary,whyItExists:t.whyItExists,unlisted:t.unlisted,rules:t.rules,lintIgnore:Array.isArray(t.lintIgnore)?t.lintIgnore.filter(c=>typeof c=="string"):null,extensions:t.extensions!==null&&typeof t.extensions=="object"&&!Array.isArray(t.extensions)?t.extensions:null},r=await L(e,i.body);let n=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
5
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(a.id,i.name);if(n?await e.prepare("UPDATE documents SET source_path = ? WHERE object = ? AND source_path IS DISTINCT FROM ?").run(i.path,n.id,i.path):n=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
6
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path = ?`).get(a.id,i.path),!n){const c=await A(e,{owner:a,name:i.name,kind:t.kind,privacy:"public",...o,revisions:[{body_hash:r,author:a,rationale:null,created_at:new Date().toISOString()}]});return await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,c.id),await k(e,c.id),d.created+=1,c.id}const f=n.name!==i.name||n.kind!==t.kind;n.name!==i.name&&(n=await x(e,null,n,{name:i.name})),n.kind!==t.kind&&(await J(e,n,t.kind),n=await l(e,n.id));const m=await g(e,n),[u]=await h(e,n),w=u?.body_hash!==r,O=n.preview!==o.preview||m.summary!==o.summary||m.why_it_exists!==o.whyItExists||m.unlisted!==o.unlisted||JSON.stringify(m.lint_ignore)!==JSON.stringify(o.lintIgnore)||JSON.stringify(m.extensions)!==JSON.stringify(o.extensions)||JSON.stringify((await C(e,n)).map(y))!==JSON.stringify(o.rules.map(y));if(!w&&!O&&!f)return n.id;const N=await R(e,n,o);if(w&&u){const c=I(),E=u.number+1;await e.prepare(`INSERT INTO revisions (rev_id, document, number, body_hash, author, rationale, verbatim, created_at)
7
+ SELECT ?, document, ?, ?, author, rationale, verbatim, ? FROM revisions WHERE rev_id = ?`).run(c,E,r,new Date().toISOString(),u.rev_id),await e.prepare("INSERT INTO revision_parents (child, parent) SELECT ?, parent FROM revision_parents WHERE child = ?").run(c,u.rev_id),await e.prepare("DELETE FROM revisions WHERE rev_id = ?").run(u.rev_id),await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(c,E,N.id),await F(e,{event:"edited",object:N,actor:a,pointer:c,attrs:{revision:E,from_revision:u.number,rationale:"package file changed"}},s)}return d.updated+=1,N.id}p(V,"convergeFile");function y(e){return[e.on,e.deliver,e.match??null,e.matchFrontmatter??null,e.if??null]}p(y,"ruleKey");async function X(e,a,i,d,s){const t=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
8
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(a.id);for(const o of t)i.has(o.id)||(await v(e,null,o,s),await j(e,o.id),d.deleted+=1)}p(X,"deleteMissing");export{V as convergeFile,X as deleteMissing,M as deriveLinks,Q as relinkAllShipped};
@@ -1,3 +1,4 @@
1
+ import type { KindConfig, ObjectKindDeclaration } from '../../types.js';
1
2
  import { type PackageReconcileReport } from './package-docs.js';
2
3
  import { type PackageFile } from './package-files.js';
3
4
  import type { CanvasDb } from '../canvas/db.js';
@@ -7,8 +8,14 @@ export interface PackageSnapshot {
7
8
  origin: 'builtin' | 'provider' | 'plugin';
8
9
  /** crtr release (builtin), bundle generation (provider), manifest version (plugin) */
9
10
  version: string;
10
- /** when this process obtained the bytes; null for the builtin package */
11
- fetchedAt: string | null;
11
+ /** the directory's revision for a directory package; null for builtin and for a Mac's local package */
12
+ revision: number | null;
13
+ /** normalized marketplace URL of a plugin package; null otherwise */
14
+ source: string | null;
15
+ /** the manifest's launch `kinds` (persona kinds); null when it declares none */
16
+ launchKinds: Record<string, Partial<KindConfig>> | null;
17
+ /** the manifest's custom-object kinds, keyed by kind name; null when it declares none */
18
+ objectKinds: Record<string, ObjectKindDeclaration> | null;
12
19
  digest: string;
13
20
  /** files with `path` already package-relative, prefixed by the app: `<app>/<relative path>` */
14
21
  files: PackageFile[];
@@ -20,11 +27,34 @@ export interface PackageSnapshot {
20
27
  export interface PublishReport extends PackageReconcileReport {
21
28
  /** the row's release is newer than this snapshot: nothing was written */
22
29
  superseded: boolean;
30
+ /** what declaring the snapshot's object kinds did; empty when the bytes were already published */
31
+ kinds: {
32
+ applied: Array<{
33
+ kind: string;
34
+ outcome: 'inserted' | 'updated' | 'unchanged';
35
+ }>;
36
+ refused: Array<{
37
+ kind: string;
38
+ message: string;
39
+ }>;
40
+ };
23
41
  }
24
42
  /** The package folder `dir` as a snapshot of app `app`. Throws when the folder or any directory in it cannot be read. The digest covers the
25
43
  * path and raw bytes of every markdown file, invalid ones included, so removing or changing an invalid file changes it. */
26
- export declare function readPackageSnapshot(dir: string, meta: Pick<PackageSnapshot, 'app' | 'origin' | 'version' | 'fetchedAt'>): PackageSnapshot;
44
+ export declare function readPackageSnapshot(dir: string, meta: Pick<PackageSnapshot, 'app' | 'origin' | 'version'> & Partial<Pick<PackageSnapshot, 'revision' | 'source' | 'launchKinds' | 'objectKinds'>>): PackageSnapshot;
45
+ /** JSON with every object's keys sorted at every depth. */
46
+ export declare function canonicalJson(value: unknown): string;
27
47
  /** App `crtr` as this process's release ships it. */
28
48
  export declare function readBuiltinSnapshot(): PackageSnapshot;
29
- /** Publish one snapshot inside the caller's write transaction. Serialized per app on the publication row. Returns what changed. */
30
- export declare function publish(db: CanvasDb, snapshot: PackageSnapshot, publishedBy: string): Promise<PublishReport>;
49
+ /** Publish one snapshot inside the caller's write transaction. Serialized per app on the publication row. A `capture` is called after the lock is
50
+ * taken, so a Mac reads its disk with no older read able to commit after it. Returns what changed. */
51
+ export declare function publish(db: CanvasDb, input: PackageSnapshot | {
52
+ app: string;
53
+ origin: PackageSnapshot['origin'];
54
+ capture: () => PackageSnapshot;
55
+ }, publishedBy: string): Promise<PublishReport>;
56
+ /** A package folder that does not exist ships nothing: a plugin without a `memory/` folder. */
57
+ export declare function emptyPackageSnapshot(meta: Pick<PackageSnapshot, 'app' | 'origin' | 'version'> & Partial<Pick<PackageSnapshot, 'launchKinds' | 'objectKinds'>>): PackageSnapshot;
58
+ /** Take a package down: soft-delete the app's shipped documents, undeclare its manifest kinds, mark the row withdrawn. Nothing else of the
59
+ * app (its ordinary documents, its kind rows) is touched. The caller decides the package is no longer wanted. */
60
+ export declare function withdraw(db: CanvasDb, app: string): Promise<PublishReport>;
@@ -1,3 +1,3 @@
1
- var g=Object.defineProperty;var p=(i,e)=>g(i,"name",{value:e,configurable:!0});import{createHash as w}from"node:crypto";import{readdirSync as v,readFileSync as y,statSync as h}from"node:fs";import{join as E,relative as u,sep as m}from"node:path";import{compareReleaseVersions as S}from"../../api/runtime-version.js";import{crtrVersion as _}from"../../shared/crtr-version.js";import{emitEvent as N}from"../events/emit.js";import{builtinMemoryRoot as k}from"../scope.js";import{deleteMissing as F,convergeFile as L,deriveLinks as O}from"./package-docs.js";import{packageFiles as T}from"./package-files.js";import{ensureOwner as D,getById as I}from"./objects.js";import{checkoutsForCwd as R}from"./repo-sync/repos.js";function A(i){if(!h(i,{throwIfNoEntry:!1})?.isDirectory())throw new Error(`package folder is missing or not a directory: ${i}`);const e=[],o=[i];for(;o.length>0;){const r=o.pop();for(const t of v(r,{withFileTypes:!0})){const n=E(r,t.name);t.isDirectory()?t.name.startsWith(".")||o.push(n):t.isFile()&&t.name.endsWith(".md")&&t.name!=="SKILL.md"&&e.push([u(i,n).split(m).join("/"),y(n)])}}return e.sort((r,t)=>r[0]<t[0]?-1:1)}p(A,"strictPackageBytes");function b(i,e){const o=A(i),{files:r,invalid:t}=T(i),n=r.map(c=>({...c,path:`${e.app}/${u(i,c.path).split(m).join("/")}`})),a=w("sha256");for(const[c,d]of o)a.update(c).update("\0").update(d).update("\0");return{...e,digest:a.digest("hex"),files:n,invalid:t}}p(b,"readPackageSnapshot");function G(){return b(k(),{app:"crtr",origin:"builtin",version:_(),fetchedAt:null})}p(G,"readBuiltinSnapshot");async function K(i,e,o){const r={created:0,updated:0,deleted:0,conflicts:[],invalid:[...e.invalid],quarantined:[],superseded:!1},t=await D(i,"app","app",`app:${e.app}`,e.app),n=new Date().toISOString();await i.prepare(`INSERT INTO package_publications (app, origin, version, digest, published_at, published_by)
2
- VALUES (?, ?, '', '', ?, ?) ON CONFLICT (app) DO NOTHING`).run(t.id,e.origin,n,o);const a=await i.prepare("SELECT version, digest, withdrawn_at FROM package_publications WHERE app = ? FOR UPDATE").get(t.id);if(S(e.version,a.version)<0)return r.superseded=!0,N({level:"warn",event:"daemon.packages.superseded",fields:{app:e.app,snapshot_version:e.version,published_version:a.version}}),r;if(a.digest===e.digest&&a.withdrawn_at===null){if(a.version===e.version)return r}else{const d=p(async s=>await R(i,s),"reposFn"),l=new Set;for(const s of e.files)l.add(await L(i,t,s,r,d));if(e.invalid.length===0&&await F(i,t,l,r,d),r.created+r.updated+r.deleted>0)for(const s of e.files){const f=await i.prepare("SELECT o.id FROM objects o WHERE o.type = 'document' AND o.owner = ? AND o.name = ? AND o.deleted_at IS NULL").get(t.id,s.name);f!==void 0&&await O(i,t,await I(i,f.id),s.body)}}return await i.prepare(`UPDATE package_publications SET origin = ?, version = ?, fetched_at = ?, digest = ?, published_at = ?, published_by = ?, withdrawn_at = NULL
3
- WHERE app = ?`).run(e.origin,e.version,e.fetchedAt,e.digest,n,o,t.id),r}p(K,"publish");export{K as publish,G as readBuiltinSnapshot,b as readPackageSnapshot};
1
+ var O=Object.defineProperty;var d=(e,i)=>O(e,"name",{value:i,configurable:!0});import{createHash as g}from"node:crypto";import{readdirSync as j,readFileSync as T,statSync as b}from"node:fs";import{join as K,relative as f,sep as m}from"node:path";import{compareReleaseVersions as N}from"../../api/runtime-version.js";import{upsertKind as R}from"./custom.js";import{crtrVersion as F}from"../../shared/crtr-version.js";import{emitEvent as v}from"../events/emit.js";import{builtinMemoryRoot as D}from"../scope.js";import{deleteMissing as E,convergeFile as A,relinkAllShipped as $}from"./package-docs.js";import{packageFiles as I}from"./package-files.js";import{ensureOwner as y}from"./objects.js";import{checkoutsForCwd as k}from"./repo-sync/repos.js";function L(e){if(!b(e,{throwIfNoEntry:!1})?.isDirectory())throw new Error(`package folder is missing or not a directory: ${e}`);const i=[],t=[e];for(;t.length>0;){const r=t.pop();for(const s of j(r,{withFileTypes:!0})){const o=K(r,s.name);s.isDirectory()?s.name.startsWith(".")||t.push(o):s.isFile()&&s.name.endsWith(".md")&&s.name!=="SKILL.md"&&i.push([f(e,o).split(m).join("/"),T(o)])}}return i.sort((r,s)=>r[0]<s[0]?-1:1)}d(L,"strictPackageBytes");function P(e,i){const t=L(e),{files:r,invalid:s}=I(e),o=r.map(n=>({...n,path:`${i.app}/${f(e,n.path).split(m).join("/")}`})),p=g("sha256");for(const[n,c]of t)p.update(n).update("\0").update(c).update("\0");const a=s.map(n=>({...n,path:`${i.app}/${f(e,n.path).split(m).join("/")}`}));return S({revision:null,source:null,launchKinds:null,objectKinds:null,...i},o,a,p)}d(P,"readPackageSnapshot");function l(e){if(Array.isArray(e))return`[${e.map(l).join(",")}]`;if(e!==null&&typeof e=="object"){const i=e;return`{${Object.keys(i).sort().filter(t=>i[t]!==void 0).map(t=>`${JSON.stringify(t)}:${l(i[t])}`).join(",")}}`}return JSON.stringify(e)??"null"}d(l,"canonicalJson");function S(e,i,t,r){return r.update(l({launchKinds:e.launchKinds,objectKinds:e.objectKinds,source:e.source})),{...e,digest:r.digest("hex"),files:i,invalid:t}}d(S,"snapshotOf");function Z(){return P(D(),{app:"crtr",origin:"builtin",version:F()})}d(Z,"readBuiltinSnapshot");function H(e,i){return e.revision!==null?i.revision!==null&&e.revision<Number(i.revision):e.origin==="builtin"?N(e.version,i.version)<0:!1}d(H,"olderThanPublished");async function ee(e,i,t){const{app:r,origin:s}=i,o=await y(e,"app","app",`app:${r}`,r),p=new Date().toISOString();await e.prepare(`INSERT INTO package_publications (app, origin, version, digest, published_at, published_by)
2
+ VALUES (?, ?, '', '', ?, ?) ON CONFLICT (app) DO NOTHING`).run(o.id,s,p,t);const a=await e.prepare("SELECT version, digest, revision, withdrawn_at FROM package_publications WHERE app = ? FOR UPDATE").get(o.id),n="capture"in i?i.capture():i,c={created:0,updated:0,deleted:0,conflicts:[],invalid:[...n.invalid],quarantined:[],superseded:!1,kinds:{applied:[],refused:[]}};if(H(n,a))return c.superseded=!0,v({level:"warn",event:"daemon.packages.superseded",fields:{app:n.app,snapshot_version:n.version,published_version:a.version,snapshot_revision:n.revision??"none",published_revision:a.revision===null?"none":Number(a.revision)}}),c;if(a.digest===n.digest&&a.withdrawn_at===null){if(a.version===n.version&&String(a.revision??"")===String(n.revision??""))return c}else{const h=d(async u=>await k(e,u),"reposFn"),w=new Set;for(const u of n.files)w.add(await A(e,o,u,c,h));n.invalid.length===0&&await E(e,o,w,c,h),c.kinds=await _(e,o,n.objectKinds),c.created+c.updated+c.deleted>0&&await $(e)}return await e.prepare(`UPDATE package_publications SET origin = ?, version = ?, revision = ?, source = ?, launch_kinds_json = ?, digest = ?, published_at = ?, published_by = ?, withdrawn_at = NULL
3
+ WHERE app = ?`).run(n.origin,n.version,n.revision,n.source,n.launchKinds===null?null:l(n.launchKinds),n.digest,p,t,o.id),c}d(ee,"publish");function ne(e){return S({revision:null,source:null,launchKinds:null,objectKinds:null,...e},[],[],g("sha256"))}d(ne,"emptyPackageSnapshot");async function _(e,i,t){const r={applied:[],refused:[]},s=new Set;for(const[p,a]of Object.entries(t??{}))try{const n=await R(e,{name:p,owner:i,description:a.description,events:a.events,read:a.read===void 0?null:{command:a.read.command},source:"manifest"});n.outcome!=="skipped"&&(s.add(p),r.applied.push({kind:p,outcome:n.outcome}))}catch(n){if(n.code!=="kind_owned")throw n;r.refused.push({kind:p,message:n.message}),v({level:"warn",event:"daemon.packages.kind_owned",fields:{app:i.id,kind:p}})}const o=await e.prepare("SELECT name FROM object_kinds WHERE owner = ? AND source = 'manifest' AND declared = 1").all(i.id);for(const{name:p}of o)s.has(p)||await e.prepare("UPDATE object_kinds SET declared = 0 WHERE name = ?").run(p);return r}d(_,"convergeKinds");async function ie(e,i){const t={created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:[],superseded:!1,kinds:{applied:[],refused:[]}},r=await y(e,"app","app",`app:${i}`,i),s=await e.prepare("SELECT withdrawn_at FROM package_publications WHERE app = ? FOR UPDATE").get(r.id);return s===void 0||s.withdrawn_at!==null||(await E(e,r,new Set,t,async o=>await k(e,o)),await _(e,r,null),await e.prepare("UPDATE package_publications SET withdrawn_at = ?, digest = '' WHERE app = ?").run(new Date().toISOString(),r.id)),t}d(ie,"withdraw");export{l as canonicalJson,ne as emptyPackageSnapshot,ee as publish,Z as readBuiltinSnapshot,P as readPackageSnapshot,ie as withdraw};
@@ -1,4 +1,3 @@
1
- import { type KindsReport } from './package-kinds.js';
2
1
  import { type PackageReconcileReport } from './package-docs.js';
3
2
  import type { CanvasDb } from '../canvas/db.js';
4
3
  export interface TokensReport {
@@ -10,6 +9,26 @@ export interface TokensReport {
10
9
  /** plugins whose `crtr_app_` token was removed because they no longer qualify */
11
10
  revoked: string[];
12
11
  }
12
+ export interface KindsReport {
13
+ /** every kind a valid plugin declares that now has its row, and what upserting it did */
14
+ applied: Array<{
15
+ plugin: string;
16
+ kind: string;
17
+ outcome: 'inserted' | 'updated' | 'unchanged';
18
+ }>;
19
+ /** kinds another owner holds; the plugin's other kinds still applied */
20
+ refused: Array<{
21
+ plugin: string;
22
+ kind: string;
23
+ code: 'kind_owned';
24
+ message: string;
25
+ }>;
26
+ /** enabled plugins whose manifest or commands are invalid (no kind update, no token); a provider bundle's kinds reconcile without a token, and plugins whose app handle another owner holds */
27
+ invalid: Array<{
28
+ plugin: string;
29
+ error: string;
30
+ }>;
31
+ }
13
32
  export interface PackagesReport extends PackageReconcileReport {
14
33
  kinds: KindsReport;
15
34
  tokens: TokensReport;
@@ -1 +1,3 @@
1
- var p=Object.defineProperty;var a=(o,t)=>p(o,"name",{value:t,configurable:!0});import{appPlugins as u}from"../plugin-app.js";import{ensurePluginAppToken as f,listScopedApiTokens as g,revokePluginAppTokens as l}from"../secrets.js";import{reconcilePackageKinds as m}from"./package-kinds.js";import{reconcilePackageDocuments as d}from"./package-docs.js";async function A(o){const t=await d(o),i=await m(o),s={granted:[],revoked:[]},r=new Set;for(const e of u()){const n=e.manifest.scopes??[];f(e.name,n),r.add(e.name),s.granted.push({plugin:e.name,scopes:[...n]})}const c=new Set;for(const e of g("user")){if(!e.token.startsWith("crtr_app_")||!e.grantee.startsWith("app:"))continue;const n=e.grantee.slice(4);r.has(n)||c.add(n)}for(const e of c)l(e)&&s.revoked.push(e);return{...t,kinds:i,tokens:s}}a(A,"reconcilePackages");export{A as reconcilePackages};
1
+ var v=Object.defineProperty;var a=(i,t)=>v(i,"name",{value:t,configurable:!0});import{statSync as S}from"node:fs";import{CrtrError as m}from"../errors.js";import{emitEvent as E}from"../events/emit.js";import{invalidAppPlugins as y,appPlugins as _,kindPlugins as N}from"../plugin-app.js";import{quarantineBrokenPlugins as T}from"../plugin-quarantine.js";import{listInstalledPlugins as I}from"../resolver.js";import{pluginMemoryDir as P}from"../scope.js";import{ensurePluginAppToken as D,listScopedApiTokens as L,revokePluginAppTokens as O}from"../secrets.js";import{deleteMissing as A}from"./package-docs.js";import{emptyPackageSnapshot as R,publish as f,readBuiltinSnapshot as j,readPackageSnapshot as x,withdraw as B}from"./package-publish.js";import{getById as C}from"./objects.js";import{checkoutsForCwd as b}from"./repo-sync/repos.js";const g="mac";function q(i){return S(i,{throwIfNoEntry:!1})?.isDirectory()===!0}a(q,"isDirectory");async function X(i){const t={created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:T(),kinds:{applied:[],refused:[],invalid:y().map(e=>({plugin:e.plugin.name,error:e.error}))},tokens:{granted:[],revoked:[]}};for(const e of t.quarantined)E({level:"error",event:"daemon.plugins.quarantined",fields:{plugin:e.name,file:e.file,reason:e.reason,...e.movedTo!==void 0?{moved_to:e.movedTo}:{}}});const k=new Set(N().map(e=>e.name)),r=new Set(["crtr"]),p=a((e,n)=>{t.created+=e.created,t.updated+=e.updated,t.deleted+=e.deleted,t.invalid.push(...e.invalid);for(const o of e.kinds.applied)t.kinds.applied.push({plugin:n,kind:o.kind,outcome:o.outcome});for(const o of e.kinds.refused)t.kinds.refused.push({plugin:n,kind:o.kind,code:"kind_owned",message:o.message})},"add");p(await f(i,{app:"crtr",origin:"builtin",capture:a(()=>j(),"capture")},g),"crtr");for(const e of I("user").filter(n=>n.enabled)){const n=e.manifest.provider===void 0?"plugin":"provider",o=P(e),d=e.manifest.kinds!==void 0&&Object.keys(e.manifest.kinds).length>0?e.manifest.kinds:null,u={app:e.name,origin:n,version:e.manifest.version??"",launchKinds:d,objectKinds:k.has(e.name)?e.manifest.object_kinds??null:null};try{if(r.has(e.name))throw new m("conflict",`plugin "${e.name}" is already provided by another package`);r.add(e.name),p(await f(i,{app:e.name,origin:n,capture:a(()=>q(o)?x(o,u):R(u),"capture")},g),e.name)}catch(s){if(!(s instanceof m)||s.code!=="conflict")throw s;t.conflicts.push({plugin:e.name,message:s.message})}}const h=await i.prepare(`SELECT i.external_id FROM package_publications p JOIN identities i ON i.object = p.app AND i.scheme = 'app'
2
+ WHERE p.withdrawn_at IS NULL ORDER BY i.external_id`).all();for(const{external_id:e}of h){const n=e.replace(/^app:/,"");if(r.has(n))continue;const o=await B(i,n);t.deleted+=o.deleted}const w=await i.prepare(`SELECT DISTINCT o.owner AS id FROM objects o JOIN documents d ON d.object = o.id
3
+ WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{id:e}of w){const n=await C(i,e),o=await i.prepare("SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(e);o!==void 0&&r.has(o.external_id.replace(/^app:/,""))||await A(i,n,new Set,t,async d=>await b(i,d))}const c=new Set;for(const e of _()){const n=e.manifest.scopes??[];D(e.name,n),c.add(e.name),t.tokens.granted.push({plugin:e.name,scopes:[...n]})}const l=new Set;for(const e of L("user")){if(!e.token.startsWith("crtr_app_")||!e.grantee.startsWith("app:"))continue;const n=e.grantee.slice(4);c.has(n)||l.add(n)}for(const e of l)O(e)&&t.tokens.revoked.push(e);return t}a(X,"reconcilePackages");export{X as reconcilePackages};
@@ -1,3 +1,4 @@
1
- var K=Object.defineProperty;var s=(t,n)=>K(t,"name",{value:n,configurable:!0});import{mkdirSync as v,readFileSync as x,writeFileSync as H}from"node:fs";import{join as f}from"node:path";import{openDb as Q,withCanvasWrite as Z}from"../../canvas/db.js";import{hashOf as W,storable as ee}from"../../graph/bodies.js";import{personIdAsync as te}from"../../canvas/person.js";import{CANVAS_SCHEMA_VERSION as ne}from"../../canvas/schema-version.js";import{crtrVersion as re}from"../../../shared/crtr-version.js";import{BODIES_TABLE as C,DUMPED_TABLES as P,RowFileWriter as ae,SEQ_TABLES as se,bundleKeys as oe,encodeRows as ie,rowLine as ce}from"../bundle/layout.js";import{parseFrontmatterGeneric as le}from"../../frontmatter.js";import{inlineMessageBody as he}from"./inline.js";const pe=16*1024*1024,de=8*1024*1024,D=32*1024*1024,q=5e3,M="bodies-pre.json",F=s(t=>`"${t.replace(/"/g,'""')}"`,"q");function ue(t){try{const n=x(t,"utf8");return n.trim()===""?null:n}catch(n){if(n.code==="ENOENT")return null;throw n}}s(ue,"readText");function Ee(t){try{return x(t,"utf8")}catch(n){if(n.code==="ENOENT")return null;throw n}}s(Ee,"readFileOrNull");function J(t,n){return new Error(`revision text ${t} is ${n} bytes; the import carries at most ${D} bytes of text per body`)}s(J,"tooLarge");function fe(t){const n=W(t.text);if(n!==t.hash)throw new Error(`body ${t.hash} does not hash to its key (its text hashes to ${n}); upgrade and restart the daemon so its boot repair rekeys the row, then export again`)}s(fe,"assertCanonical");async function U(t,n,o,r,m){const w=f(n,"rows",C);v(w,{recursive:!0});const _=[];let i=null;const O=s(async()=>{const a=i;i=null,a!==null&&await a.close()},"close"),b=s(async a=>{fe(a);const h=ce(a),c=Buffer.byteLength(h,"utf8");if(i!==null&&i.bytes>0&&i.bytes+c>pe&&await O(),i===null){const p=`${o}-${_.length}`;i=new ae(f(w,`${p}.jsonl.zst`)),_.push(p)}await i.write(h,c)},"add");try{for(let a=0;a<r.length;){const h=await t.prepare("SELECT hash, octet_length(text) AS n FROM bodies WHERE hash = ANY(?::text[]) ORDER BY hash").all(r.slice(a,a+q));let c=[],p=0;const R=s(async()=>{if(c.length===0)return;const d=await t.prepare("SELECT hash, text FROM bodies WHERE hash = ANY(?::text[]) ORDER BY hash").all(c);for(const y of d)await b(y);c=[],p=0},"fetchPage");for(const{hash:d,n:y}of h){const E=Number(y);if(E>D)throw J(d,E);c.length>0&&p+E>de&&await R(),c.push(d),p+=E}await R(),a+=q}for(const a of m){const h=Buffer.byteLength(a.text,"utf8");if(h>D)throw J(a.hash,h);await b(a)}await O()}catch(a){throw i?.destroy(),a}return _}s(U,"writeBodyParts");const V=s(async t=>(await t.prepare("SELECT hash FROM bodies ORDER BY hash").all()).map(n=>n.hash),"allHashes");async function Be(t){const n=await Q(),o=await V(n),r=await U(n,t,"pre",o,[]),m={hashes:o,parts:r,count:o.length,canonical:!0};return H(f(t,M),JSON.stringify(m)),{parts:r,count:o.length}}s(Be,"dumpBodiesPre");function me(t){try{return JSON.parse(x(f(t,M),"utf8"))}catch(n){if(n.code==="ENOENT")return{hashes:[],parts:[],count:0};throw n}}s(me,"readPre");function we(t,n){if(t.count===0&&t.parts.length===0)return;if(t.canonical!==!0)throw new Error(`${M} was written before bodies were checked against their keys; discard the export directory and run the pre-pause dump again`);const o=t.hashes.filter(r=>!n.has(r));if(o.length>0)throw new Error(`the pre-pause dump holds ${o.length} body row(s) the database no longer has (first: ${o[0]}); the boot repair ran after it. Discard the export directory and run the pre-pause dump again`)}s(we,"assertPreCurrent");async function xe(t,n){const o=await te();return v(f(t,"rows"),{recursive:!0}),Z(async r=>{await r.exec("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");const m=new Set((await r.prepareUncached("SELECT table_name FROM information_schema.tables WHERE table_schema = current_schema()").all()).map(e=>e.table_name)),w=P.filter(e=>!m.has(e));if(w.length>0)throw new Error(`the canvas database lacks tables the bundle needs: ${w.join(", ")}`);const _=await r.prepareUncached(`SELECT c.conrelid::regclass::text AS tbl, (SELECT array_agg(a.attname::text ORDER BY k.ord) FROM unnest(c.conkey) WITH ORDINALITY k(attnum, ord)
2
- JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = k.attnum) AS cols FROM pg_constraint c WHERE c.contype = 'p' AND c.connamespace = current_schema()::regnamespace`).all(),i=new Map(_.map(e=>[e.tbl.replace(/"/g,"").replace(/^.*\./,""),e.cols])),O=await r.prepareUncached("SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = current_schema() ORDER BY table_name, ordinal_position").all(),b=new Set(se),a={};let h=0,c=0;for(const e of P.filter(u=>u!==C)){const u=b.has(e)?["seq"]:i.get(e),g=u!==void 0&&u.length>0?u.map(l=>`t.${F(l)}`):O.filter(l=>l.table_name===e).map(l=>`t.${F(l.column_name)}::text`);if(g.length===0)throw new Error(`table ${e} has no columns to order its dump by`);let N=(await r.prepareUncached(`SELECT row_to_json(t)::text AS j FROM ${F(e)} t ORDER BY ${g.join(", ")}`).all()).map(l=>JSON.parse(l.j));e==="messages"&&(N=N.map(l=>{const Y=l.body_ref,j=he(l,ue);return typeof Y=="string"&&Y.startsWith("/")&&(j===l?c+=1:h+=1),j})),a[e]=N.length,H(f(t,oe.rows("",e).replace(/^imports\/\//,"")),ie(N))}const p=(await r.prepare("SELECT value FROM canvas_meta WHERE key = 'install_id'").get())?.value;if(p===void 0)throw new Error("this canvas database has no install id");const R=await r.prepare("SELECT object FROM environments WHERE kind = 'machine' ORDER BY object LIMIT 1").get(),d={installId:p,kind:n,person:o,release:re(),schemaVersion:ne,counts:a,...R===void 0?{}:{environmentObject:R.object}},y=await r.prepare("SELECT request_id, request_dir, file FROM human_requests ORDER BY request_id").all(),E=await r.prepare("SELECT object, source_path FROM documents WHERE source_path IS NOT NULL ORDER BY object").all(),I={},L=new Map;for(const e of E){const u=Ee(e.source_path);if(u===null)continue;const g=ee(le(u).body),T=W(g);I[e.object]=T,L.set(T,{hash:T,text:g})}const S=me(t),A=new Set(S.hashes),$=await V(r);we(S,new Set($));const B=$.filter(e=>!A.has(e)),k=new Set(B),z=await U(r,t,"pause",B,[...L.values()].filter(e=>!A.has(e.hash)&&!k.has(e.hash))),G=S.count+B.length+[...L.values()].filter(e=>!A.has(e.hash)&&!k.has(e.hash)).length;a[C]=G,d.bodyParts=[...S.parts,...z];const X=await r.prepare(`SELECT n.node_id, r.grantee, n.cwd, w.path AS worktree FROM nodes n JOIN node_rows r ON r.node_id = n.node_id LEFT JOIN worktrees w ON w.node_id = n.node_id AND w.state = 'open'
3
- WHERE n.status IN ('active', 'idle') ORDER BY n.node_id`).all();return d.shippedBodies=I,{source:d,requests:y.map(e=>({id:e.request_id,dir:e.request_dir,file:e.file})),runningNodes:X.map(e=>({node:e.node_id,app:e.grantee,cwd:e.cwd,worktree:e.worktree})),bodiesInlined:h,bodiesMissing:c}})}s(xe,"dumpRows");export{D as BODY_MAX_BYTES,pe as PART_JSON_BYTES,Be as dumpBodiesPre,xe as dumpRows};
1
+ var U=Object.defineProperty;var s=(e,n)=>U(e,"name",{value:n,configurable:!0});import{mkdirSync as I,readFileSync as M,writeFileSync as F}from"node:fs";import{join as f}from"node:path";import{openDb as V,withCanvasWrite as z}from"../../canvas/db.js";import{hashOf as X}from"../../graph/bodies.js";import{personIdAsync as G}from"../../canvas/person.js";import{CANVAS_SCHEMA_VERSION as K}from"../../canvas/schema-version.js";import{crtrVersion as Q}from"../../../shared/crtr-version.js";import{BODIES_TABLE as b,DUMPED_TABLES as $,RowFileWriter as Z,SEQ_TABLES as ee,bundleKeys as te,encodeRows as ne,rowLine as re}from"../bundle/layout.js";import{inlineMessageBody as ae}from"./inline.js";const se=16*1024*1024,oe=8*1024*1024,k=32*1024*1024,v=5e3,T="bodies-pre.json",N=s(e=>`"${e.replace(/"/g,'""')}"`,"q");function ie(e){try{const n=M(e,"utf8");return n.trim()===""?null:n}catch(n){if(n.code==="ENOENT")return null;throw n}}s(ie,"readText");function ce(e,n){return new Error(`revision text ${e} is ${n} bytes; the import carries at most ${k} bytes of text per body`)}s(ce,"tooLarge");function le(e){const n=X(e.text);if(n!==e.hash)throw new Error(`body ${e.hash} does not hash to its key (its text hashes to ${n}); upgrade and restart the daemon so its boot repair rekeys the row, then export again`)}s(le,"assertCanonical");async function Y(e,n,o,r){const E=f(n,"rows",b);I(E,{recursive:!0});const m=[];let i=null;const y=s(async()=>{const a=i;i=null,a!==null&&await a.close()},"close"),S=s(async a=>{le(a);const h=re(a),c=Buffer.byteLength(h,"utf8");if(i!==null&&i.bytes>0&&i.bytes+c>se&&await y(),i===null){const d=`${o}-${m.length}`;i=new Z(f(E,`${d}.jsonl.zst`)),m.push(d)}await i.write(h,c)},"add");try{for(let a=0;a<r.length;){const h=await e.prepare("SELECT hash, octet_length(text) AS n FROM bodies WHERE hash = ANY(?::text[]) ORDER BY hash").all(r.slice(a,a+v));let c=[],d=0;const _=s(async()=>{if(c.length===0)return;const p=await e.prepare("SELECT hash, text FROM bodies WHERE hash = ANY(?::text[]) ORDER BY hash").all(c);for(const u of p)await S(u);c=[],d=0},"fetchPage");for(const{hash:p,n:u}of h){const w=Number(u);if(w>k)throw ce(p,w);c.length>0&&d+w>oe&&await _(),c.push(p),d+=w}await _(),a+=v}await y()}catch(a){throw i?.destroy(),a}return m}s(Y,"writeBodyParts");const j=s(async e=>(await e.prepare("SELECT hash FROM bodies ORDER BY hash").all()).map(n=>n.hash),"allHashes");async function Se(e){const n=await V(),o=await j(n),r=await Y(n,e,"pre",o),E={hashes:o,parts:r,count:o.length,canonical:!0};return F(f(e,T),JSON.stringify(E)),{parts:r,count:o.length}}s(Se,"dumpBodiesPre");function de(e){try{return JSON.parse(M(f(e,T),"utf8"))}catch(n){if(n.code==="ENOENT")return{hashes:[],parts:[],count:0};throw n}}s(de,"readPre");function he(e,n){if(e.count===0&&e.parts.length===0)return;if(e.canonical!==!0)throw new Error(`${T} was written before bodies were checked against their keys; discard the export directory and run the pre-pause dump again`);const o=e.hashes.filter(r=>!n.has(r));if(o.length>0)throw new Error(`the pre-pause dump holds ${o.length} body row(s) the database no longer has (first: ${o[0]}); the boot repair ran after it. Discard the export directory and run the pre-pause dump again`)}s(he,"assertPreCurrent");async function be(e,n){const o=await G();return I(f(e,"rows"),{recursive:!0}),z(async r=>{await r.exec("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");const E=new Set((await r.prepareUncached("SELECT table_name FROM information_schema.tables WHERE table_schema = current_schema()").all()).map(t=>t.table_name)),m=$.filter(t=>!E.has(t));if(m.length>0)throw new Error(`the canvas database lacks tables the bundle needs: ${m.join(", ")}`);const i=await r.prepareUncached(`SELECT c.conrelid::regclass::text AS tbl, (SELECT array_agg(a.attname::text ORDER BY k.ord) FROM unnest(c.conkey) WITH ORDINALITY k(attnum, ord)
2
+ JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = k.attnum) AS cols FROM pg_constraint c WHERE c.contype = 'p' AND c.connamespace = current_schema()::regnamespace`).all(),y=new Map(i.map(t=>[t.tbl.replace(/"/g,"").replace(/^.*\./,""),t.cols])),S=await r.prepareUncached("SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = current_schema() ORDER BY table_name, ordinal_position").all(),a=new Set(ee),h={};let c=0,d=0;for(const t of $.filter(R=>R!==b)){const R=a.has(t)?["seq"]:y.get(t),x=R!==void 0&&R.length>0?R.map(l=>`t.${N(l)}`):S.filter(l=>l.table_name===t).map(l=>`t.${N(l.column_name)}::text`);if(x.length===0)throw new Error(`table ${t} has no columns to order its dump by`);let g=(await r.prepareUncached(`SELECT row_to_json(t)::text AS j FROM ${N(t)} t ORDER BY ${x.join(", ")}`).all()).map(l=>JSON.parse(l.j));t==="messages"&&(g=g.map(l=>{const C=l.body_ref,D=ae(l,ie);return typeof C=="string"&&C.startsWith("/")&&(D===l?d+=1:c+=1),D})),h[t]=g.length,F(f(e,te.rows("",t).replace(/^imports\/\//,"")),ne(g))}const _=(await r.prepare("SELECT value FROM canvas_meta WHERE key = 'install_id'").get())?.value;if(_===void 0)throw new Error("this canvas database has no install id");const p=await r.prepare("SELECT object FROM environments WHERE kind = 'machine' ORDER BY object LIMIT 1").get(),u={installId:_,kind:n,person:o,release:Q(),schemaVersion:K,counts:h,...p===void 0?{}:{environmentObject:p.object}},w=await r.prepare("SELECT request_id, request_dir, file FROM human_requests ORDER BY request_id").all(),H=await r.prepare(`SELECT d.object, r.body_hash FROM documents d JOIN revisions r ON r.document = d.object
3
+ WHERE d.source_path IS NOT NULL AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id) ORDER BY d.object, r.number DESC, r.rev_id`).all(),L={};for(const t of H)L[t.object]??=t.body_hash;const O=de(e),W=new Set(O.hashes),A=await j(r);he(O,new Set(A));const B=A.filter(t=>!W.has(t)),P=await Y(r,e,"pause",B),q=O.count+B.length;h[b]=q,u.bodyParts=[...O.parts,...P];const J=await r.prepare(`SELECT n.node_id, r.grantee, n.cwd, w.path AS worktree FROM nodes n JOIN node_rows r ON r.node_id = n.node_id LEFT JOIN worktrees w ON w.node_id = n.node_id AND w.state = 'open'
4
+ WHERE n.status IN ('active', 'idle') ORDER BY n.node_id`).all();return u.shippedBodies=L,{source:u,requests:w.map(t=>({id:t.request_id,dir:t.request_dir,file:t.file})),runningNodes:J.map(t=>({node:t.node_id,app:t.grantee,cwd:t.cwd,worktree:t.worktree})),bodiesInlined:c,bodiesMissing:d}})}s(be,"dumpRows");export{k as BODY_MAX_BYTES,se as PART_JSON_BYTES,Se as dumpBodiesPre,be as dumpRows};
@@ -1,2 +1,2 @@
1
- var R=Object.defineProperty;var l=(s,o)=>R(s,"name",{value:o,configurable:!0});import{loadSecretsKey as g,open as U,seal as I}from"../../secrets/index.js";import{insertAccount as j,upsertModelCooldown as $}from"../../subscription-state.js";import{validateEnvVarName as C,isReservedEnvName as D}from"../../env-name.js";import{encodeKeys as V}from"./copy.js";const x=["auth","credentials","env","settings","plugins"],O=l((s,o)=>`imports/${s}/stores/${o}.sealed`,"storeKey"),h=l((s,o)=>`import_store|${s}|${o}`,"aadOf");async function B(s,o,i,r){if(r===null||typeof r!="object"||Array.isArray(r))throw new F(`the ${i} store must be a JSON object`);const{sealed:t,keyId:_}=I(JSON.stringify(r),g(),h(o,i));await s.put(O(o,i),Buffer.concat([Buffer.from(`${_}
2
- `),t]))}l(B,"putStore");class F extends Error{static{l(this,"StoreBodyError")}}async function E(s,o,i){const r=await s.get(O(o,i));if(r===null)return null;const t=r.indexOf(10);return JSON.parse(U(r.subarray(t+1),r.subarray(0,t).toString("utf8"),g(),h(o,i)))}l(E,"readStore");const H=l(s=>x.map(o=>O(s,o)),"allStoreKeys");async function G(s){const{db:o,blobs:i,runId:r,person:t}=s,_=l(e=>s.objectMap.get(e)??e,"mapId"),f=l(e=>e.startsWith("profile:")?`profile:${_(e.slice(8))}`:e,"mapOwner"),m={},N=[],v=l((e,n)=>{(m[e]??=[]).push(n)},"record"),p=l(async(e,n,a,c)=>{await o.prepareUncached(`${n} ON CONFLICT DO NOTHING RETURNING 1 AS ok`).get(...a)!==void 0&&v(e,c)},"insert"),y=g(),k=new Date().toISOString(),d=await E(i,r,"credentials");if(d!==null){for(const e of d.accounts)if(await o.prepare("SELECT 1 FROM model_accounts WHERE person = ? AND provider_id = ? AND (id = ? OR label_key = ?)").get(t,e.provider,e.id,e.labelKey)===void 0){await j(o,t,e.provider,{providerId:e.provider,id:e.id,label:e.label,labelKey:e.labelKey,position:e.position,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}},{refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{},...e.clientId?{clientId:e.clientId}:{}}),v("model_accounts",[t,e.provider,e.id]);for(const a of e.cooldowns)await $(o,t,e.provider,e.id,a.family,a.until,a.at),v("model_account_cooldowns",[t,e.provider,e.id,a.family])}d.rotation!==void 0&&await p("model_settings","INSERT INTO model_settings (person, default_fallback_strength, revert_when_available) VALUES (?, ?, ?)",[t,d.rotation.defaultFallbackStrength,d.rotation.revertWhenAvailable===null?null:d.rotation.revertWhenAvailable?1:0],[t]),d.openaiDeviceId!==void 0&&/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(d.openaiDeviceId)&&await o.prepare("INSERT INTO model_settings (person, openai_device_id) VALUES (?, ?) ON CONFLICT (person) DO UPDATE SET openai_device_id = COALESCE(model_settings.openai_device_id, EXCLUDED.openai_device_id)").run(t,d.openaiDeviceId.toLowerCase())}const T=await E(i,r,"auth");if(T!==null)for(const[e,n]of Object.entries(T.apiKeys)){const{sealed:a,keyId:c}=I(JSON.stringify(n),y,`model_api_keys|${t}|${e}`);await p("model_api_keys","INSERT INTO model_api_keys (person, provider_id, credential_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?)",[t,e,a,c,k],[t,e])}const b=await E(i,r,"env");if(b!==null)for(const[e,n]of Object.entries(b.profiles)){const a=_(e);for(const[c,u]of Object.entries(n)){if(!C(c).isValid||D(c)||typeof u!="string"||u.includes("\0"))continue;const{sealed:A,keyId:L}=I(u,y,`env_vars|${t}|profile|${a}|${c}`);await p("env_vars","INSERT INTO env_vars (person, owner_kind, owner_id, name, value_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[t,"profile",a,c,A,L,k],[t,"profile",a,c])}}const w=await E(i,r,"settings");if(w!==null){for(const[e,n]of Object.entries(w.settings))await p("scope_settings","INSERT INTO scope_settings (person, owner_key, raw_json) VALUES (?, ?, ?)",[t,f(e),JSON.stringify(n)],[t,f(e)]);for(const[e,n]of Object.entries(w.state))await p("scope_bookkeeping","INSERT INTO scope_bookkeeping (person, owner_key, last_self_check, active_canvas) VALUES (?, ?, ?, ?)",[t,f(e),n.last_self_check??null,n.activeCanvas??null],[t,f(e)]);for(const[e,n]of Object.entries(w.profileProjects)){const a=_(e);if(await o.prepare("SELECT 1 FROM profile_projects WHERE profile_id = ?").get(a)!==void 0)continue;let c=0;for(const u of n)await p("profile_projects","INSERT INTO profile_projects (profile_id, path, position) VALUES (?, ?, ?)",[a,u,c++],[a,u])}for(const[e,n]of Object.entries(w.defaults))await p("profile_defaults","INSERT INTO profile_defaults (person, directory, profile_id) VALUES (?, ?, ?)",[t,e,_(n)],[t,e])}const S=await E(i,r,"plugins");if(S!==null){for(const e of S.marketplaces)await p("scope_marketplaces","INSERT INTO scope_marketplaces (person, owner_key, name, last_updated, url, ref, installed_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[t,f(e.owner),e.name,e.last_updated??null,e.url??null,e.ref??null,e.installed_at??null],[t,f(e.owner),e.name]);for(const e of S.plugins){if(e.source_path!=null&&(e.source_marketplace??null)===null){N.push(`${e.owner}:${e.name}`);continue}await p("scope_plugins","INSERT INTO scope_plugins (person, owner_key, name, last_updated, etag, checked_at, enabled, source_marketplace, version, source_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",[t,f(e.owner),e.name,e.last_updated??null,e.etag??null,e.checked_at??null,e.enabled?1:0,e.source_marketplace??null,e.version??null,e.source_path??null],[t,f(e.owner),e.name])}(m.scope_plugins??[]).length+(m.scope_marketplaces??[]).length>0&&await p("person_settings","INSERT INTO person_settings (person, plugins_revision) VALUES (?, 1)",[t],[t])}for(const[e,n]of Object.entries(m))await o.prepare("INSERT INTO import_manifest (run_id, table_name, inserted_keys) VALUES (?, ?, ?) ON CONFLICT (run_id, table_name) DO UPDATE SET inserted_keys = EXCLUDED.inserted_keys").run(r,e,V(n));return Object.keys(m).length>0&&await o.prepare("SELECT pg_notify('crtr_signal', ?)").get(`settings:${t}`),{inserted:m,unresolvedPlugins:N}}l(G,"writeStores");export{x as STORE_NAMES,F as StoreBodyError,H as allStoreKeys,B as putStore,O as storeKey,G as writeStores};
1
+ var U=Object.defineProperty;var p=(i,r)=>U(i,"name",{value:r,configurable:!0});import{loadSecretsKey as y,open as C,seal as k}from"../../secrets/index.js";import{insertAccount as j,upsertModelCooldown as D}from"../../subscription-state.js";import{validateEnvVarName as V,isReservedEnvName as F}from"../../env-name.js";import{encodeKeys as M}from"./copy.js";import{compareInstallOwners as W}from"../../scoped-state/migrate-app-installs.js";const x=["auth","credentials","env","settings","plugins"],h=p((i,r)=>`imports/${i}/stores/${r}.sealed`,"storeKey"),$=p((i,r)=>`import_store|${i}|${r}`,"aadOf");async function q(i,r,d,s){if(s===null||typeof s!="object"||Array.isArray(s))throw new K(`the ${d} store must be a JSON object`);const{sealed:a,keyId:m}=k(JSON.stringify(s),y(),$(r,d));await i.put(h(r,d),Buffer.concat([Buffer.from(`${m}
2
+ `),a]))}p(q,"putStore");class K extends Error{static{p(this,"StoreBodyError")}}async function I(i,r,d){const s=await i.get(h(r,d));if(s===null)return null;const a=s.indexOf(10);return JSON.parse(C(s.subarray(a+1),s.subarray(0,a).toString("utf8"),y(),$(r,d)))}p(I,"readStore");const z=p(i=>x.map(r=>h(i,r)),"allStoreKeys");async function Q(i){const{db:r,blobs:d,runId:s,person:a}=i,m=p(e=>i.objectMap.get(e)??e,"mapId"),w=p(e=>e.startsWith("profile:")?`profile:${m(e.slice(8))}`:e,"mapOwner"),E={},T=[],N=p((e,n)=>{(E[e]??=[]).push(n)},"record"),f=p(async(e,n,o,l)=>{await r.prepareUncached(`${n} ON CONFLICT DO NOTHING RETURNING 1 AS ok`).get(...o)!==void 0&&N(e,l)},"insert"),b=y(),A=new Date().toISOString(),u=await I(d,s,"credentials");if(u!==null){for(const e of u.accounts)if(await r.prepare("SELECT 1 FROM model_accounts WHERE person = ? AND provider_id = ? AND (id = ? OR label_key = ?)").get(a,e.provider,e.id,e.labelKey)===void 0){await j(r,a,e.provider,{providerId:e.provider,id:e.id,label:e.label,labelKey:e.labelKey,position:e.position,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}},{refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{},...e.clientId?{clientId:e.clientId}:{}}),N("model_accounts",[a,e.provider,e.id]);for(const o of e.cooldowns)await D(r,a,e.provider,e.id,o.family,o.until,o.at),N("model_account_cooldowns",[a,e.provider,e.id,o.family])}u.rotation!==void 0&&await f("model_settings","INSERT INTO model_settings (person, default_fallback_strength, revert_when_available) VALUES (?, ?, ?)",[a,u.rotation.defaultFallbackStrength,u.rotation.revertWhenAvailable===null?null:u.rotation.revertWhenAvailable?1:0],[a]),u.openaiDeviceId!==void 0&&/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(u.openaiDeviceId)&&await r.prepare("INSERT INTO model_settings (person, openai_device_id) VALUES (?, ?) ON CONFLICT (person) DO UPDATE SET openai_device_id = COALESCE(model_settings.openai_device_id, EXCLUDED.openai_device_id)").run(a,u.openaiDeviceId.toLowerCase())}const L=await I(d,s,"auth");if(L!==null)for(const[e,n]of Object.entries(L.apiKeys)){const{sealed:o,keyId:l}=k(JSON.stringify(n),b,`model_api_keys|${a}|${e}`);await f("model_api_keys","INSERT INTO model_api_keys (person, provider_id, credential_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?)",[a,e,o,l,A],[a,e])}const R=await I(d,s,"env");if(R!==null)for(const[e,n]of Object.entries(R.profiles)){const o=m(e);for(const[l,t]of Object.entries(n)){if(!V(l).isValid||F(l)||typeof t!="string"||t.includes("\0"))continue;const{sealed:c,keyId:_}=k(t,b,`env_vars|${a}|profile|${o}|${l}`);await f("env_vars","INSERT INTO env_vars (person, owner_kind, owner_id, name, value_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[a,"profile",o,l,c,_,A],[a,"profile",o,l])}}const S=await I(d,s,"settings");if(S!==null){for(const[e,n]of Object.entries(S.settings))await f("scope_settings","INSERT INTO scope_settings (person, owner_key, raw_json) VALUES (?, ?, ?)",[a,w(e),JSON.stringify(n)],[a,w(e)]);for(const[e,n]of Object.entries(S.state))await f("scope_bookkeeping","INSERT INTO scope_bookkeeping (person, owner_key, last_self_check, active_canvas) VALUES (?, ?, ?, ?)",[a,w(e),n.last_self_check??null,n.activeCanvas??null],[a,w(e)]);for(const[e,n]of Object.entries(S.profileProjects)){const o=m(e);if(await r.prepare("SELECT 1 FROM profile_projects WHERE profile_id = ?").get(o)!==void 0)continue;let l=0;for(const t of n)await f("profile_projects","INSERT INTO profile_projects (profile_id, path, position) VALUES (?, ?, ?)",[o,t,l++],[o,t])}for(const[e,n]of Object.entries(S.defaults))await f("profile_defaults","INSERT INTO profile_defaults (person, directory, profile_id) VALUES (?, ?, ?)",[a,e,m(n)],[a,e])}const O=await I(d,s,"plugins");if(O!==null){const e=p(async t=>{if(t.startsWith("app:"))return t;if(t==="user")return"app:terminal";if(!t.startsWith("profile:"))throw new Error(`unsupported install owner in the bundle: ${t}`);return(await r.prepare("SELECT grantee FROM profiles WHERE id = ?").get(m(t.slice(8))))?.grantee??"app:terminal"},"appOf"),n=new Map;for(const t of new Set([...O.marketplaces,...O.plugins].map(c=>c.owner)))t.startsWith("profile:")&&n.set(t,(await r.prepare("SELECT last_used_at FROM profiles WHERE id = ?").get(m(t.slice(8))))?.last_used_at??"");const o=p(t=>[...t].sort((c,_)=>W(w(c.owner),n.get(c.owner)??"",w(_.owner),n.get(_.owner)??"")),"byProfileFirst"),l=new Map;for(const t of o(O.marketplaces)){const c=await e(t.owner),_=new Map((await r.prepare("SELECT name, url FROM scope_marketplaces WHERE person = ? AND owner_key = ?").all(a,c)).map(v=>[v.name,v.url]));let g=t.name;for(let v=2;_.has(g)&&_.get(g)!==(t.url??null);v+=1)g=`${t.name}-${v}`;l.set(`${t.owner}\0${t.name}`,g),await f("scope_marketplaces","INSERT INTO scope_marketplaces (person, owner_key, name, last_updated, url, ref, installed_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[a,c,g,t.last_updated??null,t.url??null,t.ref??null,t.installed_at??null],[a,c,g])}for(const t of o(O.plugins)){if(t.source_path!=null&&(t.source_marketplace??null)===null){T.push(`${t.owner}:${t.name}`);continue}const c=await e(t.owner),_=t.source_marketplace==null?null:l.get(`${t.owner}\0${t.source_marketplace}`)??t.source_marketplace;await f("scope_plugins","INSERT INTO scope_plugins (person, owner_key, name, last_updated, etag, checked_at, enabled, source_marketplace, version, source_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",[a,c,t.name,t.last_updated??null,t.etag??null,t.checked_at??null,t.enabled?1:0,_,t.version??null,t.source_path??null],[a,c,t.name])}(E.scope_plugins??[]).length+(E.scope_marketplaces??[]).length>0&&await f("person_settings","INSERT INTO person_settings (person, plugins_revision) VALUES (?, 1)",[a],[a])}for(const[e,n]of Object.entries(E))await r.prepare("INSERT INTO import_manifest (run_id, table_name, inserted_keys) VALUES (?, ?, ?) ON CONFLICT (run_id, table_name) DO UPDATE SET inserted_keys = EXCLUDED.inserted_keys").run(s,e,M(n));return Object.keys(E).length>0&&await r.prepare("SELECT pg_notify('crtr_signal', ?)").get(`settings:${a}`),{inserted:E,unresolvedPlugins:T}}p(Q,"writeStores");export{x as STORE_NAMES,K as StoreBodyError,z as allStoreKeys,q as putStore,h as storeKey,Q as writeStores};
@@ -1 +1 @@
1
- var d=Object.defineProperty;var f=(n,e)=>d(n,"name",{value:e,configurable:!0});import{join as l}from"node:path";import{CONFIG_FILE as p}from"../types.js";import{listDirs as g,pathExists as I,readJsonIfExists as b}from"./fs-utils.js";import{readPluginManifest as w}from"./manifest.js";import{awaitBundleSwap as y,bundleSwapInFlightElsewhere as h}from"./plugin-swap-lock.js";import{isCloudRole as a,requestPersonId as v}from"./canvas/person-state.js";import{materializedPluginsDir as k}from"./scoped-state/materialized-plugins.js";import{readCachedSettings as C}from"./scoped-state/person-store.js";import{pluginsDir as E}from"./scope.js";function F(n,e){const r=n==="user"?C("user").settings:b(l(e,p));return r&&r.plugins&&typeof r.plugins=="object"?r.plugins:{}}f(F,"pluginConfigForRoot");function S(n,e,r){const i=Object.keys(e);return i.every(t=>r.has(t))?[]:i.filter(t=>!r.has(t)&&h(n,t))}f(S,"swapsToWaitOut");function G(n,e){const r=n==="user"&&a()?v():void 0;if(n==="user"&&a()&&r===void 0)return[];const i=n!=="user"?l(e,"plugins"):r!==void 0?k(r):E("user");if(i===void 0||!I(i))return[];const t=F(n,e),u=m(n,i,t),s=S(e,t,new Set(u.map(o=>o.name)));if(s.length===0)return u;for(const o of s)y(e,o);return m(n,i,t)}f(G,"listInstalledPluginsInRoot");function m(n,e,r){const i=[];for(const t of g(e)){const u=l(e,t),s=w(u);if(!s)continue;const o=r[t],c=typeof o?.version=="string"?o.version:s.version;i.push({name:t,scope:n,root:u,manifest:s,enabled:typeof o?.enabled=="boolean"?o.enabled:!0,sourceMarketplace:typeof o?.source_marketplace=="string"?o.source_marketplace:void 0,version:c})}return i}f(m,"collectInstalledPlugins");export{G as listInstalledPluginsInRoot};
1
+ var m=Object.defineProperty;var f=(t,e)=>m(t,"name",{value:e,configurable:!0});import{join as l}from"node:path";import{CONFIG_FILE as p}from"../types.js";import{listDirs as g,pathExists as d,readJsonIfExists as b}from"./fs-utils.js";import{readPluginManifest as w}from"./manifest.js";import{awaitBundleSwap as y,bundleSwapInFlightElsewhere as I}from"./plugin-swap-lock.js";import{isCloudRole as h}from"./canvas/person-state.js";import{readCachedSettings as v}from"./scoped-state/person-store.js";import{pluginsDir as k}from"./scope.js";function C(t,e){const n=t==="user"?v("user").settings:b(l(e,p));return n&&n.plugins&&typeof n.plugins=="object"?n.plugins:{}}f(C,"pluginConfigForRoot");function E(t,e,n){const i=Object.keys(e);return i.every(r=>n.has(r))?[]:i.filter(r=>!n.has(r)&&I(t,r))}f(E,"swapsToWaitOut");function G(t,e){if(t==="user"&&h())return[];const n=t!=="user"?l(e,"plugins"):k("user");if(n===void 0||!d(n))return[];const i=C(t,e),r=a(t,n,i),u=E(e,i,new Set(r.map(o=>o.name)));if(u.length===0)return r;for(const o of u)y(e,o);return a(t,n,i)}f(G,"listInstalledPluginsInRoot");function a(t,e,n){const i=[];for(const r of g(e)){const u=l(e,r),o=w(u);if(!o)continue;const s=n[r],c=typeof s?.version=="string"?s.version:o.version;i.push({name:r,scope:t,root:u,manifest:o,enabled:typeof s?.enabled=="boolean"?s.enabled:!0,sourceMarketplace:typeof s?.source_marketplace=="string"?s.source_marketplace:void 0,version:c})}return i}f(a,"collectInstalledPlugins");export{G as listInstalledPluginsInRoot};