@north-light/cli 0.3.507 → 0.3.509
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +2 -1
- package/dist/api/client.js +3 -3
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/docs.d.ts +4 -0
- package/dist/api/routes.d.ts +1 -0
- package/dist/api/routes.js +1 -1
- package/dist/clients/attach/assets/mermaid.min.js +3631 -0
- package/dist/clients/attach/render/doc-links.d.ts +2 -5
- package/dist/clients/attach/render/doc-links.js +1 -1
- package/dist/clients/attach/session/document-links.d.ts +6 -18
- package/dist/clients/attach/session/document-links.js +1 -1
- package/dist/clients/attach/session/file-links.d.ts +0 -2
- package/dist/clients/attach/session/file-links.js +1 -1
- package/dist/clients/attach/viewer.js +558 -625
- package/dist/core/config.js +1 -1
- package/dist/core/graph/events.d.ts +1 -0
- package/dist/core/graph/events.js +6 -6
- package/dist/core/graph/names.d.ts +6 -3
- package/dist/core/graph/names.js +3 -3
- package/dist/core/user-settings.d.ts +4 -4
- package/dist/core/user-settings.js +1 -1
- package/dist/daemon/api/bridge.js +3 -3
- package/dist/daemon/api/document-listener.d.ts +7 -0
- package/dist/daemon/api/document-listener.js +11 -0
- package/dist/daemon/api/document-merge.d.ts +2 -0
- package/dist/daemon/api/document-merge.js +1 -0
- package/dist/daemon/api/document-secret.d.ts +2 -0
- package/dist/daemon/api/document-secret.js +1 -0
- package/dist/daemon/api/handlers/docs.d.ts +14 -0
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/document-access.d.ts +2 -0
- package/dist/daemon/api/handlers/document-access.js +1 -0
- package/dist/daemon/api/handlers/objects.js +3 -3
- package/dist/daemon/api/map.js +2 -2
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/api/vendor-signin.d.ts +1 -0
- package/dist/daemon/api/vendor-signin.js +1 -1
- package/dist/daemon/crtrd.js +9 -9
- package/dist/daemon/peer/ingress.js +1 -1
- package/dist/doc-editor/editor.css +1 -0
- package/dist/doc-editor/editor.js +53 -0
- package/dist/types.d.ts +2 -9
- package/dist/types.js +1 -1
- package/package.json +13 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +364 -12
- package/dist/clients/attach/session/doc-html.d.ts +0 -8
- package/dist/clients/attach/session/doc-html.js +0 -66
- package/dist/clients/attach/session/doc-materialize.d.ts +0 -57
- package/dist/clients/attach/session/doc-materialize.js +0 -3
package/dist/core/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var Ue=Object.defineProperty;var s=(e,n)=>Ue(e,"name",{value:n,configurable:!0});import{envProfileId as qe}from"../shared/env.js";import{basename as X,join as Q}from"node:path";import{existsSync as Ge,mkdirSync as Be,statSync as We}from"node:fs";import{isDeepStrictEqual as He}from"node:util";import{CONFIG_FILE as Je,STATE_FILE as Ze,CONDENSED_HISTORY_MODES as Ye,DOC_LINK_OPEN_MODES as Ve,MOUSE_MODE_DEFAULTS as Xe,WHIP_MESSAGE_MODES as Qe,DEFAULT_WHIP_MESSAGES as ee,defaultScopeConfig as k,defaultScopeState as ne,defaultModelLaddersConfig as en,defaultKindsConfig as nn,defaultRemoteCanvasConfig as on}from"../types.js";import{BINDING_CATALOG as tn,BINDING_IDS as rn,isAttachPaneBinding as sn}from"./keybindings/catalog.js";import{notFound as cn}from"./errors.js";import{emitEvent as un}from"./events/emit.js";import{atomicWriteJson as oe,readJsonIfExists as U,ensureDir as dn}from"./fs-utils.js";import{scopeRoot as y,requireScopeRoot as w,findProjectScopeRoots as fn,userScopeRoot as E}from"./scope.js";import{listInstalledPluginsInRoot as te}from"./installed-plugins.js";import{profileDir as q,listProfiles as re}from"./profiles/manifest.js";import{canonicalRoot as S,projectStore as ln,userStore as ie}from"./scoped-state/paths.js";import{convertLegacyScopedStore as an,initializeFreshScopedStore as pn}from"./scoped-state/migrate.js";import{mutateRawSettings as mn,readBookkeeping as gn,readRawSettings as yn,readRawSettingsWithRevision as hn,replaceRawSettings as bn,writeBookkeeping as _n,writeRawSettings as vn}from"./scoped-state/settings.js";import{withExclusiveDirectoryLock as kn}from"./exclusive-lock.js";import{SCOPE_CONFIG_KEYS as wn}from"./user-settings.js";import{normalizeWorkingGerunds as Sn}from"../shared/working-activity.js";import{mergePageComponentLayers as An,normalizePageComponents as se}from"./human/page-catalog.js";function b(e){return Q(e,Je)}s(b,"configPathFor");function ce(e,n){return kn(`${e}.lock`,()=>n(e),{timeoutError:s(()=>new Error(`timed out waiting for config lock: ${e}`),"timeoutError")})}s(ce,"withConfigPathLock");function jn(e){return Q(e,Ze)}s(jn,"statePathFor");function ko(e){const n=y(e);return n?b(n):null}s(ko,"configPath");function wo(e){const n=y(e);return n?jn(n):null}s(wo,"statePath");function g(e){const n=S(e),o=S(E());if(n===o)return{store:ie(o),key:"user",kind:"user",root:o};if(X(n)===".crouter")return an({kind:"project",root:n}),{store:ln(n),key:"project",kind:"project",root:n};for(const t of re()){const r=S(q(t.profileId));if(n===r)return{store:ie(o),key:`profile:${t.profileId}`,kind:"profile",root:r,profileId:t.profileId}}throw new Error(`settings root is not a recognized owner: ${e}`)}s(g,"settingsOwnerForRoot");function I(e){if(X(e)===".crouter"){let n;try{n=S(e)}catch(o){if(o.code==="ENOENT")return null;throw o}if(n!==S(E()))return U(b(e))}return yn(g(e))}s(I,"readRawAtRoot");function G(e,n){const o=g(e);if(o.kind==="project"){ce(b(o.root),t=>oe(t,n,{createParent:!1}));return}vn(o,n)}s(G,"writeRawAtRoot");function A(e,n){const o=g(e);return o.kind==="project"?ce(b(o.root),t=>{const r=n(U(t)??{});return oe(t,r.next,{createParent:!1}),r.value}):mn(o,t=>n(t))}s(A,"mutateRawAtRoot");const P=globalThis,T=Symbol.for("crtr.broker.config-snapshot");function So(e){P[T]=e}s(So,"installBrokerConfigSnapshot");function Ao(){return P[T]}s(Ao,"brokerConfigSnapshot");function Rn(e){const n=P[T];if(n){if(e!=="user")throw new Error(`broker config snapshot has no ${e} scope`);return n.user}const o=y(e);return o?xn(o):k()}s(Rn,"readConfig");function xn(e){const n=I(e);return n?D(n):k()}s(xn,"readConfigAtRoot");function On(e){const n=y(e);if(!n)return ne();try{return gn(g(n))}catch(o){if(e==="project"&&!Ge(n))return ne();throw o}}s(On,"readState");class En extends Error{static{s(this,"InvalidRawSettingsError")}constructor(n){super(`settings cannot be merged: ${n instanceof Error?n.message:String(n)}`),this.name="InvalidRawSettingsError"}}function ue(e){if(e.scope==="user")return g(E());if(!re().some(n=>n.profileId===e.profileId))throw cn(`profile not found: ${e.profileId}`,{received:e.profileId,next:"Run `crtr profile list` to see available profiles."});return g(q(e.profileId))}s(ue,"rawSettingsOwnerForTarget");function jo(e){return hn(ue(e))}s(jo,"readRawSettingsForTarget");function Ro(e,n,o){return bn(ue(e),n,o,t=>{try{D(t)}catch(r){throw new En(r)}})}s(Ro,"replaceRawSettingsForTarget");function xo(e,n){const o=w(e);return A(o,t=>{const r=n(t);return{value:r,next:r}})}s(xo,"updateRawConfigAtomically");function Oo(e,n){G(w(e),n)}s(Oo,"writeConfig");function Eo(e,n){G(w(e),n)}s(Eo,"writeRawConfig");function In(e,n){const o=w(e);_n(g(o),n)}s(In,"writeState");function Io(e,n){if(e==="project")try{Be(n)}catch(t){if(t.code!=="EEXIST"||!We(n).isDirectory())throw t}else dn(n);const o=g(e==="user"?E():n);pn({kind:o.kind==="user"?"user":"project",root:o.store.root}),o.kind==="project"&&U(b(o.root))===null&&G(o.root,{schema_version:k().schema_version})}s(Io,"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 Tn=new Set(tn.filter(sn).map(e=>e.id));function Cn(e){return Array.isArray(e)?[...new Set(e.filter(n=>typeof n=="string"&&rn.has(n)&&Tn.has(n)))]:[]}s(Cn,"normalizeTmuxPassthrough");function C(e,n=en()){const o=n;if(e===null||typeof e!="object")return o;const t=e,r={anthropic:{...o.anthropic},openai:{...o.openai},...o.defaultProvider!==void 0?{defaultProvider:o.defaultProvider}:{}};(t.defaultProvider==="anthropic"||t.defaultProvider==="openai")&&(r.defaultProvider=t.defaultProvider);for(const i of["anthropic","openai"]){const c=t[i];if(!(c===null||typeof c!="object"))for(const[u,d]of Object.entries(c))(u==="ultra"||u==="strong"||u==="medium"||u==="light")&&typeof d=="string"&&(r[i][u]=d)}return r}s(C,"mergeModelLadders");function de(e){return e==="ultra"||e==="strong"||e==="medium"||e==="light"}s(de,"validStrength");const B=["ultra","strong","medium","light"];function W(){return Object.create(null)}s(W,"routeRecord");function j(e,n,o){Object.defineProperty(e,n,{value:o,writable:!0,enumerable:!0,configurable:!0})}s(j,"setRouteRecord");function fe(e){const n=W();for(const[o,t]of Object.entries(e??{}))j(n,o,t);return n}s(fe,"copyRouteRecord");function R(e,n){un({level:"warn",event:"config.model_routes.invalid",fields:{route_id:e,rule:n}})}s(R,"routeInvalid");function $(e,n={}){const o=fe(n);if(e===null||typeof e!="object"||Array.isArray(e))return o;for(const[t,r]of Object.entries(e)){if(t.trim()===""){R(t,"route id must be a non-empty string");continue}if(r===null||typeof r!="object"||Array.isArray(r)){R(t,"route must be an object");continue}const i=r,c=o[t],u={...c};let d;for(const l of["family","credentialSource","providerId"]){const f=i[l];if(f!==void 0){if(typeof f!="string"||f.trim()===""){d=`${l} must be a non-empty string`;break}u[l]=f}}if(d===void 0&&i.models!==void 0)if(i.models===null||typeof i.models!="object"||Array.isArray(i.models))d="models must be an object";else{const l={...c?.models};for(const[f,a]of Object.entries(i.models)){if(!de(f)){d=`models.${f} is not a model strength`;break}if(typeof a!="string"||a.trim()===""){d=`models.${f} must be a non-empty provider/id ref`;break}const p=a.indexOf("/");if(p<=0||p===a.length-1){d=`models.${f} must be provider/id`;break}l[f]=a}u.models=l}if(d===void 0&&u.models!==void 0&&u.providerId!==void 0){for(const[l,f]of Object.entries(u.models))if(f.slice(0,f.indexOf("/"))!==u.providerId){d=`models.${l} provider prefix must equal providerId`;break}}if(d!==void 0){R(t,d);continue}j(o,t,u)}return o}s($,"mergeModelRoutePatches");function le(e){const n=W(),o=W();for(const[t,r]of Object.entries(e)){if(r.family===void 0||r.credentialSource===void 0||r.providerId===void 0||r.models===void 0){R(t,"route must include family, credentialSource, providerId, and models");continue}const i=B.filter(c=>r.models[c]===void 0);if(i.length>0){R(t,`incomplete route; missing strengths: ${i.join(", ")}`),j(o,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models});continue}j(n,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models})}return{complete:n,legacy:o}}s(le,"resolveModelRoutes");function $n(e,n){if(e.trim()==="")throw new Error("model route id must be a non-empty string");for(const t of["family","credentialSource","providerId"])if(typeof n[t]!="string"||n[t].trim()==="")throw new Error(`model route "${e}" ${t} must be a non-empty string`);const o=B.filter(t=>n.models?.[t]===void 0);if(o.length>0)throw new Error(`model route "${e}" is incomplete; missing strengths: ${o.join(", ")}`);for(const t of B){const r=n.models[t];if(typeof r!="string"||r.trim()==="")throw new Error(`model route "${e}" models.${t} must be a non-empty provider/id ref`);const i=r.indexOf("/");if(i<=0||i===r.length-1)throw new Error(`model route "${e}" models.${t} must be provider/id`);if(r.slice(0,i)!==n.providerId)throw new Error(`model route "${e}" models.${t} provider prefix must equal providerId`)}}s($n,"assertCompleteModelRoute");function M(e,n){if(e===void 0||e===null||typeof e!="object"||Array.isArray(e))return n;const o=e,t=s(i=>Array.isArray(i)&&i.every(c=>typeof c=="string"&&c.trim()!=="")?[...i]:void 0,"strings"),r=Array.isArray(o.strengthFallback)&&o.strengthFallback.every(i=>de(i))?[...o.strengthFallback]:void 0;return{...t(o.credentialSourceOrder)!==void 0?{credentialSourceOrder:t(o.credentialSourceOrder)}:{},...t(o.familyOrder)!==void 0?{familyOrder:t(o.familyOrder)}:{},...r!==void 0?{strengthFallback:r}:{}}}s(M,"mergeModelRouting");function Mn(e){return e!==""&&e.replace(/\*/g,"")===""}s(Mn,"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(Mn(r))throw new Error(`spawnEnv.allow: "${r}" matches every env var name (inherit-all) \u2014 this is exactly the leak the spawn-env boundary closes. Name the specific vars/prefixes you actually need instead (e.g. "AWS_*", "GIT_ASKPASS").`);return{allow:[...new Set([...n,...t])]}}s(N,"mergeSpawnEnv");function L(e,n={}){const o={};for(const[t,r]of Object.entries(n))o[t]={...r};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){if(r===null||typeof r!="object")continue;const i={...o[t]},c=r.fastMode;typeof c=="boolean"&&(i.fastMode=c),o[t]=i}return o}s(L,"mergeProviderOptions");const ae=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,Nn=new Set(["crtr","crtrd"]);function F(e,n){if(n==="bin"&&Nn.has(e))return`${n}.${e} is reserved by crouter \u2014 pick another bare command name`;if(!ae.test(e))return`${n}.${e} is not a safe bare command name (expected ${ae.source})`}s(F,"bareCommandNameIssue");function Ln(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(Ln,"binDeclarationIssues");function Fn(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(Fn,"normalizeBin");const pe=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function Dn(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(pe.test(o)||r.push(`action name must match ${pe.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(Dn,"humanActionDeclarationIssues");function zn(e){if(e===null||typeof e!="object"||Array.isArray(e))return{};const n=new Set(Dn(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(zn,"normalizeHumanActions");function Po(e){return Ln(e).map(n=>n.message)}s(Po,"invalidPluginBinReasons");function To(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(To,"normalizeRequires");function Co(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(Co,"invalidPluginRequiresReasons");function me(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(me,"normalizeKindFields");function x(e,n=nn()){const o={...n};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){const i=me(r);if(i===null)continue;const c=o[t];c!==void 0?o[t]={...c,...i}:i.whenToUse!==void 0&&(o[t]=i)}return o}s(x,"mergeKinds");function ge(e,n,o){if(n===null)return o;const t=te(e,n).filter(i=>i.enabled&&i.manifest.kinds!==void 0).sort((i,c)=>i.name.localeCompare(c.name));let r=o;for(const i of t)r=x(i.manifest.kinds,r);return r}s(ge,"layerPluginKinds");function $o(){const e=[{origin:"config.json",components:Rn("user").page_components}],n=y("user");if(n!==null){const o=te("user",n).filter(t=>t.enabled&&t.manifest.page_components!==void 0).sort((t,r)=>t.name.localeCompare(r.name));for(const t of o){const r=`plugin "${t.name}" page_components`;e.push({origin:`plugin "${t.name}"`,components:se(t.manifest.page_components,r)})}}return An(e)}s($o,"resolvePageComponents");const H=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))H.has(c)||n.push(`kinds.${t}.${c} is not a KindConfig field (expected one of: ${[...H].join(", ")})`);i.whenToUse!==void 0&&(typeof i.whenToUse!="string"||i.whenToUse.trim()==="")&&n.push(`kinds.${t}.whenToUse must be a non-empty string`);for(const c of["model","orchestratorModel"])i[c]!==void 0&&(typeof i[c]!="string"||i[c].trim()==="")&&n.push(`kinds.${t}.${c} must be a non-empty string`);for(const c of["tools","extensions","availableTo"])i[c]!==void 0&&!o(i[c])&&n.push(`kinds.${t}.${c} must be an array of strings`);me(r)===null&&n.push(`kinds.${t} has no valid KindConfig field \u2014 an entry must set at least one of: ${[...H].join(", ")} (whenToUse is required when the kind exists in no lower layer)`)}return n}s(Mo,"invalidPluginKindsReasons");function Kn(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(Kn,"normalizeRemoteCanvasTarget");function Un(e,n=on()){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=Kn(i);c!==null&&(o[r]=c)}}return{targets:o}}s(Un,"mergeRemoteCanvas");function ye(e,n){return e===!0?"notify":e===!1?!1:e==="notify"||e==="apply"?e:n}s(ye,"normalizeMode");function O(e,n){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=1?e:n}s(O,"normalizePositiveInteger");function qn(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(qn,"mergeBrokerThresholds");function Gn(e,n){return{unattendedParkMs:O((e!==null&&typeof e=="object"?e:{}).unattendedParkMs,n.unattendedParkMs)}}s(Gn,"mergeLifecycle");function Bn(e){if(!Array.isArray(e))return[...ee];const n=e.filter(o=>typeof o=="string"&&o.trim()!=="").map(o=>o.trim());return n.length>0?n:[...ee]}s(Bn,"normalizeWhipMessages");function Wn(e,n){if(e===null||typeof e!="object")return{...n};const o=e.tcp;return{tcp:typeof o=="string"?o:n.tcp}}s(Wn,"mergeApi");function D(e){const n=k(),o=n.schema_version,t=e.marketplaces===void 0?{}:e.marketplaces,r=e.plugins===void 0?{}:e.plugins,i=e.auto_update,c=i&&typeof i.interval_hours=="number"?i.interval_hours:void 0,u=c!==void 0&&Number.isFinite(c)&&c>=0?c:n.auto_update.interval_hours,d={crtr:ye(i?.crtr,n.auto_update.crtr),content:ye(i?.content,n.auto_update.content),interval_hours:u},l=e.max_panes_per_window,f=O(l,n.max_panes_per_window),a=typeof e.auto_open_child_viewers=="boolean"?e.auto_open_child_viewers:n.auto_open_child_viewers,p=qn(e.brokerThresholds,n.brokerThresholds),h=Gn(e.lifecycle,n.lifecycle),z=typeof e.completion_bell=="boolean"?e.completion_bell:n.completion_bell,m=e.working_gerunds===void 0?[...n.working_gerunds]:Sn(e.working_gerunds),be=typeof e.whip_mode=="boolean"?e.whip_mode:n.whip_mode,_e=se(e.page_components),ve=typeof e.page_surface=="boolean"?e.page_surface:n.page_surface,ke=Bn(e.whip_messages),we=Qe.includes(e.whip_message_mode)?e.whip_message_mode:n.whip_message_mode,Se=Xe.includes(e.mouse_mode_default)?e.mouse_mode_default:n.mouse_mode_default,Ae=O(e.live_cycles,n.live_cycles),je=Ye.includes(e.condensed_history)?e.condensed_history:n.condensed_history,Re=typeof e.bash_tool_purpose=="boolean"?e.bash_tool_purpose:n.bash_tool_purpose,xe=typeof e.fold_finished_tools=="boolean"?e.fold_finished_tools:n.fold_finished_tools,Oe=typeof e.summarize_tool_calls=="boolean"?e.summarize_tool_calls:n.summarize_tool_calls,Ee=typeof e.detailed_tool_recaps=="boolean"?e.detailed_tool_recaps:n.detailed_tool_recaps,Ie=Ve.includes(e.doc_link_open)?e.doc_link_open:n.doc_link_open,Pe=typeof e.consolidate_replies=="boolean"?e.consolidate_replies:n.consolidate_replies,Te=Pn(e.keybindings),Ce=Cn(e.tmux_passthrough),$e=C(e.modelLadders),Me=L(e.providerOptions,n.providerOptions),K=le($(e.modelRoutes)),J=K.complete,Z=M(e.modelRouting,void 0),Ne=x(e.kinds),Le=Un(e.remoteCanvas),Fe=Wn(e.api,n.api),_=e.paths;if(_?.user_files!==void 0&&(typeof _.user_files!="string"||!_.user_files.startsWith("/")))throw new Error("paths.user_files must be an absolute path");const Y=e.store,v=e.runtime;if(v?.person_uids!==void 0&&(!Array.isArray(v.person_uids)||!v.person_uids.every(V=>Number.isSafeInteger(V)&&V>=0)))throw new Error("runtime.person_uids must be an array of nonnegative integer uids");const De=N(e.spawnEnv,n.spawnEnv?.allow),ze=Fn(e.bin),Ke=zn(e.humanActions);return{bin:ze,humanActions:Ke,schema_version:o,marketplaces:t,plugins:r,auto_update:d,max_panes_per_window:f,auto_open_child_viewers:a,brokerThresholds:p,lifecycle:h,completion_bell:z,working_gerunds:m,whip_mode:be,page_components:_e,page_surface:ve,whip_messages:ke,whip_message_mode:we,mouse_mode_default:Se,live_cycles:Ae,condensed_history:je,bash_tool_purpose:Re,fold_finished_tools:xe,summarize_tool_calls:Oe,detailed_tool_recaps:Ee,doc_link_open:Ie,consolidate_replies:Pe,keybindings:Te,tmux_passthrough:Ce,modelLadders:$e,providerOptions:Me,...Object.keys(J).length>0?{modelRoutes:J}:{},...Object.keys(K.legacy).length>0?{legacyModelRoutes:K.legacy}:{},...Z!==void 0?{modelRouting:Z}:{},kinds:Ne,remoteCanvas:Le,api:Fe,..._?{paths:_}:{},...Y?{store:Y}:{},...v?{runtime:v}:{},spawnEnv:De}}s(D,"mergeConfig");function Hn(e){return I(e)}s(Hn,"readRawConfigAtRoot");function Jn(e){const n=y(e);return n?Hn(n):null}s(Jn,"readRawScopeConfig");function Zn(e){if(e===null||e==="")return null;try{return I(q(e))}catch{return null}}s(Zn,"readRawProfileConfig");function Yn(e,n){return[...fn(e,n)].reverse().map(r=>({root:r,raw:I(r)}))}s(Yn,"readRawProjectScopeConfigs");function he(e=process.cwd(),n=qe()||null){const o=P[T];if(o)return o.launch;const t=k(),r=Jn("user"),i=Zn(n),c=Yn(e,n);let u=x(r?.kinds,ge("user",y("user"),t.kinds)),d=C(r?.modelLadders,t.modelLadders),l=L(r?.providerOptions,t.providerOptions),f=$(r?.modelRoutes),a=M(r?.modelRouting,void 0),p=N(r?.spawnEnv,t.spawnEnv?.allow);i!==null&&(u=x(i.kinds,u),d=C(i.modelLadders,d),l=L(i.providerOptions,l),f=$(i.modelRoutes,f),a=M(i.modelRouting,a),p=N(i.spawnEnv,p.allow));for(const{root:z,raw:m}of c)u=x(m?.kinds,ge("project",z,u)),m!==null&&(d=C(m.modelLadders,d),l=L(m.providerOptions,l),f=$(m.modelRoutes,f),a=M(m.modelRouting,a),p=N(m.spawnEnv,p.allow));const h=le(f);return{kinds:u,modelLadders:d,providerOptions:l,...Object.keys(h.complete).length>0?{modelRoutes:h.complete}:{},...Object.keys(h.legacy).length>0?{legacyModelRoutes:h.legacy}:{},...a!==void 0?{modelRouting:a}:{},spawnEnv:p}}s(he,"readMergedLaunchConfig");function No(e){return he().kinds[e]}s(No,"resolveKindConfig");function Lo(e){const n=he().kinds,o=[];for(const[t,r]of Object.entries(n)){const i=t.indexOf("/");if(i===-1)continue;const c=t.slice(0,i),u=r.availableTo??[c];(u.includes("*")||u.includes(e))&&o.push({kind:t,whenToUse:r.whenToUse})}return o.sort((t,r)=>t.kind.localeCompare(r.kind))}s(Lo,"subKindsAvailableTo");function Vn(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(Vn,"sparseModelLaddersToPersist");function Xn(e,n){return A(e,o=>{const t=D(o),r=D(o);n(r);const i={...o};for(const u of wn)u!=="modelLadders"&&!He(r[u],t[u])&&(i[u]=r[u]);const c=Vn(o.modelLadders,t.modelLadders,r.modelLadders);return c!==void 0?i.modelLadders=c:delete i.modelLadders,{value:r,next:i}})}s(Xn,"mutateConfigAtRoot");function Fo(e,n){A(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");function Do(e,n,o){if(o!==void 0)$n(n,o);else if(n.trim()==="")throw new Error("model route id must be a non-empty string");A(e,t=>{const r=fe(t.modelRoutes??{});o===void 0?delete r[n]:j(r,n,{...o,models:{...o.models}});const i={...t};return Object.keys(r).length===0?delete i.modelRoutes:i.modelRoutes=r,{value:void 0,next:i}})}s(Do,"persistModelRoute");function Qn(e,n){return Xn(e,n)}s(Qn,"updateConfigAtRoot");function zo(e,n){return Qn(w(e),n)}s(zo,"updateConfig");function Ko(e){const n=e.orchestrator===!0?"orchestratorModel":"model";return A(e.root,o=>{const t={...o.kinds??{},[e.kind]:{...(o.kinds??{})[e.kind],[n]:e.model}};return{value:void 0,next:{...o,kinds:t}}}),{path:b(e.root),field:n}}s(Ko,"persistDefaultKindModel");function Uo(e,n){const o=On(e);return n(o),In(e,o),o}s(Uo,"updateState");export{En as InvalidRawSettingsError,Ln as binDeclarationIssues,Ao as brokerConfigSnapshot,ko as configPath,Io as ensureScopeInitialized,Dn as humanActionDeclarationIssues,So as installBrokerConfigSnapshot,Po as invalidPluginBinReasons,Mo as invalidPluginKindsReasons,Co as invalidPluginRequiresReasons,Fn as normalizeBin,zn as normalizeHumanActions,To as normalizeRequires,Cn as normalizeTmuxPassthrough,Ko as persistDefaultKindModel,Fo as persistModelLadders,Do as persistModelRoute,Rn as readConfig,xn as readConfigAtRoot,he as readMergedLaunchConfig,Hn as readRawConfigAtRoot,Jn as readRawScopeConfig,jo as readRawSettingsForTarget,On as readState,Ro as replaceRawSettingsForTarget,No as resolveKindConfig,$o as resolvePageComponents,wo as statePath,Lo as subKindsAvailableTo,zo as updateConfig,Qn as updateConfigAtRoot,xo as updateRawConfigAtomically,Uo as updateState,Oo as writeConfig,Eo as writeRawConfig,In as writeState};
|
|
1
|
+
var Ke=Object.defineProperty;var s=(e,n)=>Ke(e,"name",{value:n,configurable:!0});import{envProfileId as qe}from"../shared/env.js";import{basename as X,join as Q}from"node:path";import{existsSync as Ge,mkdirSync as Be,statSync as We}from"node:fs";import{isDeepStrictEqual as He}from"node:util";import{CONFIG_FILE as Je,STATE_FILE as Ze,CONDENSED_HISTORY_MODES as Ye,MOUSE_MODE_DEFAULTS as Ve,WHIP_MESSAGE_MODES as Xe,DEFAULT_WHIP_MESSAGES as ee,defaultScopeConfig as k,defaultScopeState as ne,defaultModelLaddersConfig as Qe,defaultKindsConfig as en,defaultRemoteCanvasConfig as nn}from"../types.js";import{BINDING_CATALOG as on,BINDING_IDS as tn,isAttachPaneBinding as rn}from"./keybindings/catalog.js";import{notFound as sn}from"./errors.js";import{emitEvent as cn}from"./events/emit.js";import{atomicWriteJson as oe,readJsonIfExists as K,ensureDir as un}from"./fs-utils.js";import{scopeRoot as y,requireScopeRoot as w,findProjectScopeRoots as dn,userScopeRoot as E}from"./scope.js";import{listInstalledPluginsInRoot as te}from"./installed-plugins.js";import{profileDir as q,listProfiles as re}from"./profiles/manifest.js";import{canonicalRoot as A,projectStore as fn,userStore as ie}from"./scoped-state/paths.js";import{convertLegacyScopedStore as ln,initializeFreshScopedStore as an}from"./scoped-state/migrate.js";import{mutateRawSettings as mn,readBookkeeping as pn,readRawSettings as gn,readRawSettingsWithRevision as yn,replaceRawSettings as hn,writeBookkeeping as bn,writeRawSettings as _n}from"./scoped-state/settings.js";import{withExclusiveDirectoryLock as vn}from"./exclusive-lock.js";import{SCOPE_CONFIG_KEYS as kn}from"./user-settings.js";import{normalizeWorkingGerunds as wn}from"../shared/working-activity.js";import{mergePageComponentLayers as An,normalizePageComponents as se}from"./human/page-catalog.js";function b(e){return Q(e,Je)}s(b,"configPathFor");function ce(e,n){return vn(`${e}.lock`,()=>n(e),{timeoutError:s(()=>new Error(`timed out waiting for config lock: ${e}`),"timeoutError")})}s(ce,"withConfigPathLock");function Sn(e){return Q(e,Ze)}s(Sn,"statePathFor");function vo(e){const n=y(e);return n?b(n):null}s(vo,"configPath");function ko(e){const n=y(e);return n?Sn(n):null}s(ko,"statePath");function g(e){const n=A(e),o=A(E());if(n===o)return{store:ie(o),key:"user",kind:"user",root:o};if(X(n)===".crouter")return ln({kind:"project",root:n}),{store:fn(n),key:"project",kind:"project",root:n};for(const t of re()){const r=A(q(t.profileId));if(n===r)return{store:ie(o),key:`profile:${t.profileId}`,kind:"profile",root:r,profileId:t.profileId}}throw new Error(`settings root is not a recognized owner: ${e}`)}s(g,"settingsOwnerForRoot");function I(e){if(X(e)===".crouter"){let n;try{n=A(e)}catch(o){if(o.code==="ENOENT")return null;throw o}if(n!==A(E()))return K(b(e))}return gn(g(e))}s(I,"readRawAtRoot");function G(e,n){const o=g(e);if(o.kind==="project"){ce(b(o.root),t=>oe(t,n,{createParent:!1}));return}_n(o,n)}s(G,"writeRawAtRoot");function S(e,n){const o=g(e);return o.kind==="project"?ce(b(o.root),t=>{const r=n(K(t)??{});return oe(t,r.next,{createParent:!1}),r.value}):mn(o,t=>n(t))}s(S,"mutateRawAtRoot");const P=globalThis,T=Symbol.for("crtr.broker.config-snapshot");function wo(e){P[T]=e}s(wo,"installBrokerConfigSnapshot");function Ao(){return P[T]}s(Ao,"brokerConfigSnapshot");function jn(e){const n=P[T];if(n){if(e!=="user")throw new Error(`broker config snapshot has no ${e} scope`);return n.user}const o=y(e);return o?Rn(o):k()}s(jn,"readConfig");function Rn(e){const n=I(e);return n?z(n):k()}s(Rn,"readConfigAtRoot");function xn(e){const n=y(e);if(!n)return ne();try{return pn(g(n))}catch(o){if(e==="project"&&!Ge(n))return ne();throw o}}s(xn,"readState");class On extends Error{static{s(this,"InvalidRawSettingsError")}constructor(n){super(`settings cannot be merged: ${n instanceof Error?n.message:String(n)}`),this.name="InvalidRawSettingsError"}}function ue(e){if(e.scope==="user")return g(E());if(!re().some(n=>n.profileId===e.profileId))throw sn(`profile not found: ${e.profileId}`,{received:e.profileId,next:"Run `crtr profile list` to see available profiles."});return g(q(e.profileId))}s(ue,"rawSettingsOwnerForTarget");function So(e){return yn(ue(e))}s(So,"readRawSettingsForTarget");function jo(e,n,o){return hn(ue(e),n,o,t=>{try{z(t)}catch(r){throw new On(r)}})}s(jo,"replaceRawSettingsForTarget");function Ro(e,n){const o=w(e);return S(o,t=>{const r=n(t);return{value:r,next:r}})}s(Ro,"updateRawConfigAtomically");function xo(e,n){G(w(e),n)}s(xo,"writeConfig");function Oo(e,n){G(w(e),n)}s(Oo,"writeRawConfig");function En(e,n){const o=w(e);bn(g(o),n)}s(En,"writeState");function Eo(e,n){if(e==="project")try{Be(n)}catch(t){if(t.code!=="EEXIST"||!We(n).isDirectory())throw t}else un(n);const o=g(e==="user"?E():n);an({kind:o.kind==="user"?"user":"project",root:o.store.root}),o.kind==="project"&&K(b(o.root))===null&&G(o.root,{schema_version:k().schema_version})}s(Eo,"ensureScopeInitialized");function In(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(In,"normalizeKeybindings");const Pn=new Set(on.filter(rn).map(e=>e.id));function Tn(e){return Array.isArray(e)?[...new Set(e.filter(n=>typeof n=="string"&&tn.has(n)&&Pn.has(n)))]:[]}s(Tn,"normalizeTmuxPassthrough");function C(e,n=Qe()){const o=n;if(e===null||typeof e!="object")return o;const t=e,r={anthropic:{...o.anthropic},openai:{...o.openai},...o.defaultProvider!==void 0?{defaultProvider:o.defaultProvider}:{}};(t.defaultProvider==="anthropic"||t.defaultProvider==="openai")&&(r.defaultProvider=t.defaultProvider);for(const i of["anthropic","openai"]){const c=t[i];if(!(c===null||typeof c!="object"))for(const[u,d]of Object.entries(c))(u==="ultra"||u==="strong"||u==="medium"||u==="light")&&typeof d=="string"&&(r[i][u]=d)}return r}s(C,"mergeModelLadders");function de(e){return e==="ultra"||e==="strong"||e==="medium"||e==="light"}s(de,"validStrength");const B=["ultra","strong","medium","light"];function W(){return Object.create(null)}s(W,"routeRecord");function j(e,n,o){Object.defineProperty(e,n,{value:o,writable:!0,enumerable:!0,configurable:!0})}s(j,"setRouteRecord");function fe(e){const n=W();for(const[o,t]of Object.entries(e??{}))j(n,o,t);return n}s(fe,"copyRouteRecord");function R(e,n){cn({level:"warn",event:"config.model_routes.invalid",fields:{route_id:e,rule:n}})}s(R,"routeInvalid");function $(e,n={}){const o=fe(n);if(e===null||typeof e!="object"||Array.isArray(e))return o;for(const[t,r]of Object.entries(e)){if(t.trim()===""){R(t,"route id must be a non-empty string");continue}if(r===null||typeof r!="object"||Array.isArray(r)){R(t,"route must be an object");continue}const i=r,c=o[t],u={...c};let d;for(const l of["family","credentialSource","providerId"]){const f=i[l];if(f!==void 0){if(typeof f!="string"||f.trim()===""){d=`${l} must be a non-empty string`;break}u[l]=f}}if(d===void 0&&i.models!==void 0)if(i.models===null||typeof i.models!="object"||Array.isArray(i.models))d="models must be an object";else{const l={...c?.models};for(const[f,a]of Object.entries(i.models)){if(!de(f)){d=`models.${f} is not a model strength`;break}if(typeof a!="string"||a.trim()===""){d=`models.${f} must be a non-empty provider/id ref`;break}const m=a.indexOf("/");if(m<=0||m===a.length-1){d=`models.${f} must be provider/id`;break}l[f]=a}u.models=l}if(d===void 0&&u.models!==void 0&&u.providerId!==void 0){for(const[l,f]of Object.entries(u.models))if(f.slice(0,f.indexOf("/"))!==u.providerId){d=`models.${l} provider prefix must equal providerId`;break}}if(d!==void 0){R(t,d);continue}j(o,t,u)}return o}s($,"mergeModelRoutePatches");function le(e){const n=W(),o=W();for(const[t,r]of Object.entries(e)){if(r.family===void 0||r.credentialSource===void 0||r.providerId===void 0||r.models===void 0){R(t,"route must include family, credentialSource, providerId, and models");continue}const i=B.filter(c=>r.models[c]===void 0);if(i.length>0){R(t,`incomplete route; missing strengths: ${i.join(", ")}`),j(o,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models});continue}j(n,t,{family:r.family,credentialSource:r.credentialSource,providerId:r.providerId,models:r.models})}return{complete:n,legacy:o}}s(le,"resolveModelRoutes");function 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=B.filter(t=>n.models?.[t]===void 0);if(o.length>0)throw new Error(`model route "${e}" is incomplete; missing strengths: ${o.join(", ")}`);for(const t of B){const r=n.models[t];if(typeof r!="string"||r.trim()==="")throw new Error(`model route "${e}" models.${t} must be a non-empty provider/id ref`);const i=r.indexOf("/");if(i<=0||i===r.length-1)throw new Error(`model route "${e}" models.${t} must be provider/id`);if(r.slice(0,i)!==n.providerId)throw new Error(`model route "${e}" models.${t} provider prefix must equal providerId`)}}s(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=>de(i))?[...o.strengthFallback]:void 0;return{...t(o.credentialSourceOrder)!==void 0?{credentialSourceOrder:t(o.credentialSourceOrder)}:{},...t(o.familyOrder)!==void 0?{familyOrder:t(o.familyOrder)}:{},...r!==void 0?{strengthFallback:r}:{}}}s(M,"mergeModelRouting");function $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 ae=/^[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(!ae.test(e))return`${n}.${e} is not a safe bare command name (expected ${ae.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 me=/^[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(me.test(o)||r.push(`action name must match ${me.source}`),t===null||typeof t!="object"||Array.isArray(t))r.push("action must be an object with argv and cwd");else{const i=t,c=Object.keys(i).filter(u=>u!=="argv"&&u!=="cwd");c.length>0&&r.push(`action has unsupported field${c.length===1?"":"s"}: ${c.join(", ")}`),(!Array.isArray(i.argv)||i.argv.length===0||!i.argv.every(u=>typeof u=="string"&&u.length>0))&&r.push("argv must be a non-empty array of non-empty strings"),(typeof i.cwd!="string"||i.cwd.length===0)&&r.push("cwd is required and must be a non-empty string")}r.length>0&&n.push({name:o,reason:r.join("; ")})}return n}s(Fn,"humanActionDeclarationIssues");function zn(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(zn,"normalizeHumanActions");function Io(e){return Nn(e).map(n=>n.message)}s(Io,"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 To(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(To,"invalidPluginRequiresReasons");function pe(e){if(e===null||typeof e!="object")return null;const n=e,o={};typeof n.whenToUse=="string"&&n.whenToUse.trim()!==""&&(o.whenToUse=n.whenToUse),typeof n.model=="string"&&n.model.trim()!==""&&(o.model=n.model),typeof n.orchestratorModel=="string"&&n.orchestratorModel.trim()!==""&&(o.orchestratorModel=n.orchestratorModel);const t=s(r=>Array.isArray(r)&&r.every(i=>typeof i=="string"),"isStringArray");return t(n.tools)&&(o.tools=n.tools),t(n.extensions)&&(o.extensions=n.extensions),t(n.availableTo)&&(o.availableTo=n.availableTo),Object.keys(o).length>0?o:null}s(pe,"normalizeKindFields");function x(e,n=en()){const o={...n};if(e!==null&&typeof e=="object")for(const[t,r]of Object.entries(e)){const i=pe(r);if(i===null)continue;const c=o[t];c!==void 0?o[t]={...c,...i}:i.whenToUse!==void 0&&(o[t]=i)}return o}s(x,"mergeKinds");function ge(e,n,o){if(n===null)return o;const t=te(e,n).filter(i=>i.enabled&&i.manifest.kinds!==void 0).sort((i,c)=>i.name.localeCompare(c.name));let r=o;for(const i of t)r=x(i.manifest.kinds,r);return r}s(ge,"layerPluginKinds");function Co(){const e=[{origin:"config.json",components:jn("user").page_components}],n=y("user");if(n!==null){const o=te("user",n).filter(t=>t.enabled&&t.manifest.page_components!==void 0).sort((t,r)=>t.name.localeCompare(r.name));for(const t of o){const r=`plugin "${t.name}" page_components`;e.push({origin:`plugin "${t.name}"`,components:se(t.manifest.page_components,r)})}}return An(e)}s(Co,"resolvePageComponents");const H=new Set(["whenToUse","model","orchestratorModel","tools","extensions","availableTo"]);function $o(e){if(e===null||typeof e!="object"||Array.isArray(e))return["kinds must be a JSON object keyed by kind name"];const n=[],o=s(t=>Array.isArray(t)&&t.every(r=>typeof r=="string"),"isStringArray");for(const[t,r]of Object.entries(e)){if(t.trim()===""){n.push("kinds contains an empty kind name");continue}if(r===null||typeof r!="object"||Array.isArray(r)){n.push(`kinds.${t} must be a JSON object`);continue}const i=r;for(const c of Object.keys(i))H.has(c)||n.push(`kinds.${t}.${c} is not a KindConfig field (expected one of: ${[...H].join(", ")})`);i.whenToUse!==void 0&&(typeof i.whenToUse!="string"||i.whenToUse.trim()==="")&&n.push(`kinds.${t}.whenToUse must be a non-empty string`);for(const c of["model","orchestratorModel"])i[c]!==void 0&&(typeof i[c]!="string"||i[c].trim()==="")&&n.push(`kinds.${t}.${c} must be a non-empty string`);for(const c of["tools","extensions","availableTo"])i[c]!==void 0&&!o(i[c])&&n.push(`kinds.${t}.${c} must be an array of strings`);pe(r)===null&&n.push(`kinds.${t} has no valid KindConfig field \u2014 an entry must set at least one of: ${[...H].join(", ")} (whenToUse is required when the kind exists in no lower layer)`)}return n}s($o,"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 Un(e,n=nn()){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(Un,"mergeRemoteCanvas");function ye(e,n){return e===!0?"notify":e===!1?!1:e==="notify"||e==="apply"?e:n}s(ye,"normalizeMode");function O(e,n){return typeof e=="number"&&Number.isSafeInteger(e)&&e>=1?e:n}s(O,"normalizePositiveInteger");function 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 qn(e,n){return{unattendedParkMs:O((e!==null&&typeof e=="object"?e:{}).unattendedParkMs,n.unattendedParkMs)}}s(qn,"mergeLifecycle");function Gn(e){if(!Array.isArray(e))return[...ee];const n=e.filter(o=>typeof o=="string"&&o.trim()!=="").map(o=>o.trim());return n.length>0?n:[...ee]}s(Gn,"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");function z(e){const n=k(),o=n.schema_version,t=e.marketplaces===void 0?{}:e.marketplaces,r=e.plugins===void 0?{}:e.plugins,i=e.auto_update,c=i&&typeof i.interval_hours=="number"?i.interval_hours:void 0,u=c!==void 0&&Number.isFinite(c)&&c>=0?c:n.auto_update.interval_hours,d={crtr:ye(i?.crtr,n.auto_update.crtr),content:ye(i?.content,n.auto_update.content),interval_hours:u},l=e.max_panes_per_window,f=O(l,n.max_panes_per_window),a=typeof e.auto_open_child_viewers=="boolean"?e.auto_open_child_viewers:n.auto_open_child_viewers,m=Kn(e.brokerThresholds,n.brokerThresholds),h=qn(e.lifecycle,n.lifecycle),D=typeof e.completion_bell=="boolean"?e.completion_bell:n.completion_bell,p=e.working_gerunds===void 0?[...n.working_gerunds]:wn(e.working_gerunds),be=typeof e.whip_mode=="boolean"?e.whip_mode:n.whip_mode,_e=se(e.page_components),ve=typeof e.page_surface=="boolean"?e.page_surface:n.page_surface,ke=Gn(e.whip_messages),we=Xe.includes(e.whip_message_mode)?e.whip_message_mode:n.whip_message_mode,Ae=Ve.includes(e.mouse_mode_default)?e.mouse_mode_default:n.mouse_mode_default,Se=O(e.live_cycles,n.live_cycles),je=Ye.includes(e.condensed_history)?e.condensed_history:n.condensed_history,Re=typeof e.bash_tool_purpose=="boolean"?e.bash_tool_purpose:n.bash_tool_purpose,xe=typeof e.fold_finished_tools=="boolean"?e.fold_finished_tools:n.fold_finished_tools,Oe=typeof e.summarize_tool_calls=="boolean"?e.summarize_tool_calls:n.summarize_tool_calls,Ee=typeof e.detailed_tool_recaps=="boolean"?e.detailed_tool_recaps:n.detailed_tool_recaps,Ie=Number.isInteger(e.document_port)&&e.document_port>=1&&e.document_port<=65535?e.document_port:n.document_port,Pe=typeof e.consolidate_replies=="boolean"?e.consolidate_replies:n.consolidate_replies,Te=In(e.keybindings),Ce=Tn(e.tmux_passthrough),$e=C(e.modelLadders),Me=L(e.providerOptions,n.providerOptions),U=le($(e.modelRoutes)),J=U.complete,Z=M(e.modelRouting,void 0),Ne=x(e.kinds),Le=Un(e.remoteCanvas),Fe=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 Y=e.store,v=e.runtime;if(v?.person_uids!==void 0&&(!Array.isArray(v.person_uids)||!v.person_uids.every(V=>Number.isSafeInteger(V)&&V>=0)))throw new Error("runtime.person_uids must be an array of nonnegative integer uids");const ze=N(e.spawnEnv,n.spawnEnv?.allow),De=Ln(e.bin),Ue=zn(e.humanActions);return{bin:De,humanActions:Ue,schema_version:o,marketplaces:t,plugins:r,auto_update:d,max_panes_per_window:f,auto_open_child_viewers:a,brokerThresholds:m,lifecycle:h,completion_bell:D,working_gerunds:p,whip_mode:be,page_components:_e,page_surface:ve,whip_messages:ke,whip_message_mode:we,mouse_mode_default:Ae,live_cycles:Se,condensed_history:je,bash_tool_purpose:Re,fold_finished_tools:xe,summarize_tool_calls:Oe,detailed_tool_recaps:Ee,document_port:Ie,consolidate_replies:Pe,keybindings:Te,tmux_passthrough:Ce,modelLadders:$e,providerOptions:Me,...Object.keys(J).length>0?{modelRoutes:J}:{},...Object.keys(U.legacy).length>0?{legacyModelRoutes:U.legacy}:{},...Z!==void 0?{modelRouting:Z}:{},kinds:Ne,remoteCanvas:Le,api:Fe,..._?{paths:_}:{},...Y?{store:Y}:{},...v?{runtime:v}:{},spawnEnv:ze}}s(z,"mergeConfig");function Wn(e){return I(e)}s(Wn,"readRawConfigAtRoot");function Hn(e){const n=y(e);return n?Wn(n):null}s(Hn,"readRawScopeConfig");function Jn(e){if(e===null||e==="")return null;try{return I(q(e))}catch{return null}}s(Jn,"readRawProfileConfig");function Zn(e,n){return[...dn(e,n)].reverse().map(r=>({root:r,raw:I(r)}))}s(Zn,"readRawProjectScopeConfigs");function he(e=process.cwd(),n=qe()||null){const o=P[T];if(o)return o.launch;const t=k(),r=Hn("user"),i=Jn(n),c=Zn(e,n);let u=x(r?.kinds,ge("user",y("user"),t.kinds)),d=C(r?.modelLadders,t.modelLadders),l=L(r?.providerOptions,t.providerOptions),f=$(r?.modelRoutes),a=M(r?.modelRouting,void 0),m=N(r?.spawnEnv,t.spawnEnv?.allow);i!==null&&(u=x(i.kinds,u),d=C(i.modelLadders,d),l=L(i.providerOptions,l),f=$(i.modelRoutes,f),a=M(i.modelRouting,a),m=N(i.spawnEnv,m.allow));for(const{root:D,raw:p}of c)u=x(p?.kinds,ge("project",D,u)),p!==null&&(d=C(p.modelLadders,d),l=L(p.providerOptions,l),f=$(p.modelRoutes,f),a=M(p.modelRouting,a),m=N(p.spawnEnv,m.allow));const h=le(f);return{kinds:u,modelLadders:d,providerOptions:l,...Object.keys(h.complete).length>0?{modelRoutes:h.complete}:{},...Object.keys(h.legacy).length>0?{legacyModelRoutes:h.legacy}:{},...a!==void 0?{modelRouting:a}:{},spawnEnv:m}}s(he,"readMergedLaunchConfig");function Mo(e){return he().kinds[e]}s(Mo,"resolveKindConfig");function No(e){const n=he().kinds,o=[];for(const[t,r]of Object.entries(n)){const i=t.indexOf("/");if(i===-1)continue;const c=t.slice(0,i),u=r.availableTo??[c];(u.includes("*")||u.includes(e))&&o.push({kind:t,whenToUse:r.whenToUse})}return o.sort((t,r)=>t.kind.localeCompare(r.kind))}s(No,"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");function Vn(e,n){return S(e,o=>{const t=z(o),r=z(o);n(r);const i={...o};for(const u of kn)u!=="modelLadders"&&!He(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");function Lo(e,n){S(e,o=>{const t=o.modelLadders??{},r={...t};n.defaultProvider!==void 0&&(r.defaultProvider=n.defaultProvider);for(const i of["anthropic","openai"])n[i]!==void 0&&(r[i]={...t[i]??{},...n[i]});return{value:void 0,next:{...o,modelLadders:r}}})}s(Lo,"persistModelLadders");function Fo(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");S(e,t=>{const r=fe(t.modelRoutes??{});o===void 0?delete r[n]:j(r,n,{...o,models:{...o.models}});const i={...t};return Object.keys(r).length===0?delete i.modelRoutes:i.modelRoutes=r,{value:void 0,next:i}})}s(Fo,"persistModelRoute");function Xn(e,n){return Vn(e,n)}s(Xn,"updateConfigAtRoot");function zo(e,n){return Xn(w(e),n)}s(zo,"updateConfig");function Do(e){const n=e.orchestrator===!0?"orchestratorModel":"model";return S(e.root,o=>{const t={...o.kinds??{},[e.kind]:{...(o.kinds??{})[e.kind],[n]:e.model}};return{value:void 0,next:{...o,kinds:t}}}),{path:b(e.root),field:n}}s(Do,"persistDefaultKindModel");function Uo(e,n){const o=xn(e);return n(o),En(e,o),o}s(Uo,"updateState");export{On as InvalidRawSettingsError,Nn as binDeclarationIssues,Ao as brokerConfigSnapshot,vo as configPath,Eo as ensureScopeInitialized,Fn as humanActionDeclarationIssues,wo as installBrokerConfigSnapshot,Io as invalidPluginBinReasons,$o as invalidPluginKindsReasons,To as invalidPluginRequiresReasons,Ln as normalizeBin,zn as normalizeHumanActions,Po as normalizeRequires,Tn as normalizeTmuxPassthrough,Do as persistDefaultKindModel,Lo as persistModelLadders,Fo as persistModelRoute,jn as readConfig,Rn as readConfigAtRoot,he as readMergedLaunchConfig,Wn as readRawConfigAtRoot,Hn as readRawScopeConfig,So as readRawSettingsForTarget,xn as readState,jo as replaceRawSettingsForTarget,Mo as resolveKindConfig,Co as resolvePageComponents,ko as statePath,No as subKindsAvailableTo,zo as updateConfig,Xn as updateConfigAtRoot,Ro as updateRawConfigAtomically,Uo as updateState,xo as writeConfig,Oo as writeRawConfig,En as writeState};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** The event catalog, subject matching, listening-config resolution and `publish`: the one
|
|
2
2
|
* path by which an event on an object reaches its watchers' inboxes. */
|
|
3
3
|
import type { DatabaseSync } from 'node:sqlite';
|
|
4
|
+
export declare function subscribeDocumentEvents(id: string, callback: (event: 'edited' | 'deleted') => void): () => void;
|
|
4
5
|
import { type ReposFn } from './reader.js';
|
|
5
6
|
import { type Subscription } from './watches.js';
|
|
6
7
|
import { CrtrError } from '../errors.js';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
var W=Object.defineProperty;var
|
|
2
|
-
AND name = ? AND rowid != ? LIMIT 1`).get(e.name,e.rowid)?`${e.type}:${e.name}`:e.name}
|
|
3
|
-
`)[0]:`${e.event} ${e.object.type} ${e.object.name}`),...a?.bodyRef===void 0?{}:{bodyRef:a.bodyRef},...a?.disposition===void 0?{}:{disposition:a.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor===null||e.actor===void 0?null:e.actor.home_machine?`${e.actor.name} on ${
|
|
1
|
+
var W=Object.defineProperty;var l=(n,e)=>W(n,"name",{value:e,configurable:!0});import{prepared as h}from"../sqlite-statements.js";import{EventEmitter as $}from"node:events";const v=new $;v.setMaxListeners(0);function pe(n,e){const o=l(s=>e(s),"listener");return v.on(n,o),()=>v.off(n,o)}l(pe,"subscribeDocumentEvents");import{evalCondition as B}from"../predicate.js";import{finalizeNodeMail as J,insertMessage as K}from"../feed/messages.js";import{linkAuthorizes as U,ownerChain as G,readCheck as q}from"./access.js";import{getByRowid as z}from"./objects.js";import{appScopesNow as X,publishLookups as Y,readerForObject as P}from"./reader.js";import{configFromJson as C}from"./watches.js";import{CrtrError as L}from"../errors.js";import{ExitCode as M}from"../../types.js";import{eventSpec as V,kindOf as D}from"./kinds/registry.js";import{machineNameOf as k}from"../peer/names.js";import{RECEIVERS as F}from"./receivers.js";import{DEFAULT_LISTEN as H}from"./types.js";function Q(n,e){if(n.level===void 0||n.level===e.level||e.levels?.includes(n.level))return n.level??e.level;const o=[e.level,...e.levels??[]].filter((s,t,i)=>i.indexOf(s)===t).sort((s,t)=>s-t);throw new L("invalid_level",`${n.object.type} ${n.event} publishes at level ${o.join(", ")}, not ${n.level}`,M.USAGE,{kind:n.object.type,event:n.event,level:n.level,allowed:o})}l(Q,"resolveLevel");function Z(n,e){const o=n.split("."),s=e.split(".");for(let t=0;t<o.length;t++){if(o[t]===">")return t===o.length-1&&s.length>t;if(t>=s.length||o[t]!=="*"&&o[t]!==s[t])return!1}return o.length===s.length}l(Z,"matchSubject");const x={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function ee(n,e){let o=null;for(const s of n){let t=null;for(const i of s)i.threshold<=e&&(!t||i.threshold>t.threshold)&&(t=i);t&&(!o||x[t.delivery]>x[o])&&(o=t.delivery)}return o}l(ee,"resolveDelivery");function Ne(n,e,o){if(e.config)return e.config;const s=h(n,"SELECT listen_json FROM objects WHERE rowid = ?").get(o.rowid);return s?.listen_json?C(s.listen_json):H}l(Ne,"listenConfigFor");function te(n,e){return e.inboxKind??(n.level>=7?"urgent":"update")}l(te,"inboxKind");function ne(n,e){return e.type==="node"?e.id:h(n,`SELECT 1 FROM objects WHERE type IN ('person','app','repo','profile')
|
|
2
|
+
AND name = ? AND rowid != ? LIMIT 1`).get(e.name,e.rowid)?`${e.type}:${e.name}`:e.name}l(ne,"ownerHandleOf");function oe(n,e,o){const s=e.object.owner===null?null:h(n,"SELECT * FROM objects WHERE rowid = ?").get(e.object.owner),t=s?.id??null,i=s?ne(n,s):null;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:t,owner_handle:i,name:e.object.name,...e.object.home_machine?{machine:k(e.object.home_machine),machine_id:e.object.home_machine}:{}},actor:e.actor?.id??null,...e.actor?.home_machine?{actor_machine:k(e.actor.home_machine),actor_machine_id:e.actor.home_machine}:{},attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}l(oe,"pushFields");function se(n,e,o,s){const t=oe(n,e,s),i=e.messageBody!==void 0&&o.carriesBody===!0,f=i?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:t}:{body:e.messageBody,push:t}:{push:t},d=i?e.messageBody:null,a=d?.bodyRef===void 0?null:d;return{kind:te(e,o),label:a?.label??(typeof d?.label=="string"?d.label:e.object.type.includes(".")&&typeof e.messageBody?.summary=="string"?e.messageBody.summary.split(`
|
|
3
|
+
`)[0]:`${e.event} ${e.object.type} ${e.object.name}`),...a?.bodyRef===void 0?{}:{bodyRef:a.bodyRef},...a?.disposition===void 0?{}:{disposition:a.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor===null||e.actor===void 0?null:e.actor.home_machine?`${e.actor.name} on ${k(e.actor.home_machine)}`:e.actor.name,bodyJson:f}}l(se,"messageInput");function ie(n,e,o,s,t){if(!o.supersedes)return[{attrs:e.attrs,destinations:[...s]}];const i=h(n,`SELECT d.delivery_id, d.message_id, d.state, m.body_json FROM deliveries d
|
|
4
4
|
JOIN messages m ON m.message_id = d.message_id
|
|
5
5
|
WHERE d.destination_id = ? AND d.state IN ('pending', 'handed_off')
|
|
6
6
|
AND d.canceled_at IS NULL AND m.event = ? AND m.object_id = ?
|
|
7
|
-
ORDER BY m.seq`),f=h(n,"DELETE FROM deliveries WHERE delivery_id = ?"),
|
|
8
|
-
UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(i,i);if(i!==null&&f===void 0)throw new
|
|
7
|
+
ORDER BY m.seq`),f=h(n,"DELETE FROM deliveries WHERE delivery_id = ?"),d=h(n,"SELECT * FROM objects WHERE id = ?"),a=new Map,_=new Set;for(const m of s){const w=t(m),O=d.get(w),I=O===void 0?void 0:D(n,O.type),j=i.all(w,e.event,e.object.id),b=new Set;for(const c of j)c.state==="handed_off"?I?.supersedeClaimed?.(n,c.delivery_id)&&b.add(c.delivery_id):(f.run(c.delivery_id),_.add(c.message_id));const y=j.filter(c=>c.state==="pending"||b.has(c.delivery_id)).at(-1),R=y?.message_id??null,r=a.get(R)??{prior:y?.body_json?JSON.parse(y.body_json).push?.attrs??null:null,destinations:[]};r.destinations.push(m),a.set(R,r)}const N=h(n,"DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const m of _)N.run(m,m);return[...a.values()].map(m=>({attrs:o.merge?o.merge(m.prior,e):e.attrs,destinations:m.destinations}))}l(ie,"supersede");function re(n,e,o,s,t){const i=z(n,s);if(i===null||i.type!=="app")return!1;const f=h(n,"SELECT 1 FROM peer_stubs WHERE object = ?").get(e.rowid)!==void 0,d=f?null:X(n,i,t);return(f||d!==null)&&U(n,i,d,e,o)}l(re,"linkDelivers");const T=8;class ce extends L{static{l(this,"HopLimitError")}constructor(e){super("hop_limit",`event depth ${e} exceeds ${T}`,M.GENERAL,{depth:e,limit:T})}}function Oe(n,e,o=()=>[]){const s=V(n,e.object.type,e.event),t={...e,level:Q(e,s)};t.object.type==="document"&&(t.event==="edited"||t.event==="deleted")&&queueMicrotask(()=>v.emit(t.object.id,t.event));const i=e.cause??null,f=i===null?null:h(n,`SELECT depth FROM messages WHERE message_id = ?
|
|
8
|
+
UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(i,i);if(i!==null&&f===void 0)throw new L("unknown_cause",`no message ${i}`,M.USAGE,{cause:i});const d=f===null?0:f.depth+1;if(d>T)throw new ce(d);const a=G(n,t.object),_=`w.watcher, o.id AS watcher_id, o.type AS watcher_type, o.reception, s.pattern, s.filter_json,
|
|
9
9
|
COALESCE(s.config_json, o.listen_json) AS config_json, w.link_app`,N=n.prepare(s.durable?`SELECT ${_} FROM watches w JOIN watch_subscriptions s ON s.watch = w.rowid
|
|
10
10
|
JOIN objects o ON o.rowid = w.watcher WHERE w.object = ? AND w.watcher != ? AND o.reception >= 1`:`SELECT ${_} FROM objects o INDEXED BY objects_receiving CROSS JOIN watches w CROSS JOIN watch_subscriptions s
|
|
11
|
-
WHERE o.reception = 2 AND w.watcher = o.rowid AND s.watch = w.rowid AND w.object = ? AND w.watcher != ?`).all(t.object.rowid,t.actor?.rowid??-1);if(N.length===0)return{delivered:0,message_id:null,depth:
|
|
11
|
+
WHERE o.reception = 2 AND w.watcher = o.rowid AND s.watch = w.rowid AND w.object = ? AND w.watcher != ?`).all(t.object.rowid,t.actor?.rowid??-1);if(N.length===0)return{delivered:0,message_id:null,depth:d};const m={event:t.event,level:t.level,id:t.object.id,type:t.object.type,kind:t.object.kind,actor:t.actor?.id??null,...t.attrs},w=new Map;for(const r of N){if(t.onlyWatchers!==void 0&&!t.onlyWatchers.includes(r.watcher_id)||!Z(r.pattern,t.event)||r.filter_json!==null&&!B(JSON.parse(r.filter_json),m))continue;const c=w.get(r.watcher)??{id:r.watcher_id,type:r.watcher_type,reception:r.reception,configs:[],linkApp:r.link_app};c.configs.push(r.config_json?C(r.config_json):H),w.set(r.watcher,c)}const O=(a.length?a[a.length-1]:t.object).type==="repo",I=l(()=>[],"noRepos"),j=q(n,t.object),b=Y(n),y=[];for(const r of w.values()){const c=h(n,"SELECT * FROM objects WHERE id = ?").get(r.id),u=D(n,c.type),S=u.receiverFor?u.receiverFor(n,c):u.receiver;if(S===void 0)continue;const g=ee(r.configs,t.level);if(g===null)continue;const E=P(n,c,O?o:I,b);if(E.scopes===null&&(E.class==="node"||E.owner.type!=="person"))continue;const p=r.linkApp!==null&&!j(E)&&re(n,t.object,c,r.linkApp,b);!p&&!j(E)||y.push({id:r.id,reception:r.reception,delivery:g,receiver:S,...p?{linked:p}:{}})}if(y.length===0)return{delivered:0,message_id:null,depth:d};let R=null;for(const r of ie(n,t,s,y,c=>c.id)){const c=new Map;for(const p of r.destinations)c.set(p.receiver,[...c.get(p.receiver)??[],p]);const u=se(n,t,s,r.attrs),S=[...c].map(([p,A])=>F[p].bodyOwner(A)).find(p=>p!==null)??null,g=J({...u,urgency:"normal"},S),E=K(n,{operationId:g.operationId,createdAt:g.createdAt,senderNodeId:u.senderNodeId,senderName:u.senderName,kind:u.kind,label:u.label,bodyRef:g.bodyRef,bodyJson:g.bodyJson,disposition:u.disposition??null,event:{event:t.event,objectId:t.object.id,level:t.level,actorId:t.actor?.id??null},causeMessageId:i,depth:d});R??=E;for(const[p,A]of c)F[p].enqueue(n,{messageId:E,event:t.event,level:t.level,senderNodeId:u.senderNodeId,elevatesTier:s.elevatesTier===!0,...t.idempotencyKey===void 0?{}:{idempotencyKey:t.idempotencyKey}},A)}return{delivered:y.length,message_id:R,depth:d}}l(Oe,"publish");export{T as HOP_LIMIT,ce as HopLimitError,Ne as listenConfigFor,Z as matchSubject,Oe as publish,ee as resolveDelivery,Q as resolveLevel,pe as subscribeDocumentEvents,ie as supersede};
|
|
@@ -17,6 +17,8 @@ export type Resolution = {
|
|
|
17
17
|
export declare function viewOf(db: DatabaseSync, reader: Reader): ObjectRow[];
|
|
18
18
|
/** The owner types that carry a handle; `<type>:<handle>/<name>` names one exactly. */
|
|
19
19
|
export declare const HANDLE_TYPES: readonly ["person", "app", "repo", "profile"];
|
|
20
|
+
/** A ref without its `[[ ]]` wrapper. */
|
|
21
|
+
export declare const unwrapRef: (raw: string) => string;
|
|
20
22
|
/** Every owner a handle names: a node by id, and each handle-carrying owner type
|
|
21
23
|
* holding that handle (handles are unique per type, not across types). Any owner
|
|
22
24
|
* also answers to its object id, so `<owner object id>/<name>` names a document
|
|
@@ -33,7 +35,8 @@ export interface ResolveContext {
|
|
|
33
35
|
readable: (o: ObjectRow) => boolean;
|
|
34
36
|
}
|
|
35
37
|
export declare function resolveName(db: DatabaseSync, reader: Reader, raw: string, context?: ResolveContext): Resolution;
|
|
36
|
-
/**
|
|
37
|
-
* `resolveName` never answers a stub, so a stub cannot shadow a local
|
|
38
|
-
* `resolveName` found no local object, to learn that the name belongs
|
|
38
|
+
/** Every readable stub of another machine's object that `raw` (`<owner handle>/<name>`, `<type>:<owner handle>/<name>` or a bare name) names:
|
|
39
|
+
* empty when none does, more than one when the name is ambiguous. `resolveName` never answers a stub, so a stub cannot shadow a local
|
|
40
|
+
* object; this is the lookup a forwarding handler makes only after `resolveName` found no local object, to learn that the name belongs
|
|
41
|
+
* to an object another machine homes. */
|
|
39
42
|
export declare function stubsNamed(db: DatabaseSync, reader: Reader, raw: string): ObjectRow[];
|
package/dist/core/graph/names.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
AND deleted_at IS ${
|
|
3
|
-
OR (type IN ('person','app','repo','profile') AND (name = ? OR id = ?) AND home_machine IS NULL)`).all(e,e,e)}
|
|
1
|
+
var M=Object.defineProperty;var i=(t,e)=>M(t,"name",{value:e,configurable:!0});import{prepared as w}from"../sqlite-statements.js";import{isSafeNodeId as _}from"../canvas/paths.js";import{canRead as O,toolApps as $}from"./access.js";import{getById as H}from"./objects.js";function y(t,e){const n=w(t,"SELECT * FROM objects WHERE type = 'person' AND name = 'user' AND home_machine IS NULL").get(),p=$(t,e),o=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]:[],c=new Set;return[...o,n,...p].filter(r=>!!r&&!c.has(r.rowid)&&!!c.add(r.rowid))}i(y,"viewOf");function d(t,e,n,p,o=!1){return t.prepare(`SELECT * FROM objects WHERE (type = 'document' OR instr(type, '.') > 0) AND owner = ? AND name = ? AND home_machine IS ${o?"NOT NULL":"NULL"}
|
|
2
|
+
AND deleted_at IS ${p?"NOT NULL ORDER BY deleted_at DESC":"NULL"}`).all(e.rowid,n)}i(d,"namedObjects");const B=["person","app","repo","profile"],S=/^(person|app|repo|profile|node):([^/]+)\/(.+)$/,A=i(t=>t.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim(),"unwrapRef");function j(t){const e=S.exec(t);if(e)return{type:e[1],handle:e[2],rest:e[3]};const n=t.indexOf("/");return n>0?{handle:t.slice(0,n),rest:t.slice(n+1)}:null}i(j,"splitOwnerRef");function I(t,e,n){return n==="node"?w(t,"SELECT * FROM objects WHERE type = 'node' AND id = ?").all(e):n?w(t,"SELECT * FROM objects WHERE type = ? AND (name = ? OR id = ?) AND home_machine IS NULL").all(n,e,e):w(t,`SELECT * FROM objects WHERE (type = 'node' AND id = ?)
|
|
3
|
+
OR (type IN ('person','app','repo','profile') AND (name = ? OR id = ?) AND home_machine IS NULL)`).all(e,e,e)}i(I,"ownersByHandle");function Y(t,e,n,p){let o=A(n);const c=/^([^:/]+):reports\/(.+?)(?:\.md)?$/.exec(o);c&&!S.test(o)&&(o=`${c[1]}/reports/${c[2]}`);const r=p?.readable??(f=>O(t,e,f)),a=H(t,o);if(a&&r(a))return a.deleted_at?{deleted:a}:{object:a};const N=p?.view??y(t,e);for(const f of N){const l=d(t,f,o,!1).find(r);if(l)return{object:l}}const m=j(o);if(m){const{type:f,handle:l,rest:L}=m,g=f!==void 0,E=I(t,l,f),h=i(s=>{const u=N.findIndex(T=>T.rowid===s.rowid);return u<0?1/0:u},"rank"),x=E.filter(s=>h(s)<1/0).sort((s,u)=>h(s)-h(u));for(const s of x){const u=d(t,s,L,!1).find(r);if(u)return{object:u}}const R=E.flatMap(s=>d(t,s,L,!1)).filter(r);if(R.length===1)return{object:R[0]};if(R.length>1)return{ambiguous:R};const D=E.flatMap(s=>d(t,s,L,!0)).find(r);return D?{deleted:D}:!g&&E.length===0&&_(l)?{remote:o}:{missing:!0}}for(const f of N){const l=d(t,f,o,!0).find(r);if(l)return{deleted:l}}return{missing:!0}}i(Y,"resolveName");function b(t,e,n){const p=A(n),o=i((a,N)=>a.flatMap(m=>d(t,m,N,!1,!0)).filter(m=>O(t,e,m)),"found"),c=o(y(t,e),p);if(c.length)return c;const r=j(p);return r?o(I(t,r.handle,r.type),r.rest):[]}i(b,"stubsNamed");export{B as HANDLE_TYPES,I as ownersByHandle,Y as resolveName,b as stubsNamed,A as unwrapRef,y as viewOf};
|
|
@@ -275,19 +275,19 @@ export declare const CONFIG_SETTINGS_COVERAGE: {
|
|
|
275
275
|
setFromString: (config: ScopeConfig, raw: string) => string | null;
|
|
276
276
|
};
|
|
277
277
|
};
|
|
278
|
-
|
|
278
|
+
document_port: {
|
|
279
279
|
surface: "settings";
|
|
280
280
|
setting: {
|
|
281
|
-
key: "
|
|
281
|
+
key: "document_port";
|
|
282
282
|
label: string;
|
|
283
283
|
description: string;
|
|
284
284
|
valueConstraint: string;
|
|
285
285
|
applies: string;
|
|
286
286
|
group: "display";
|
|
287
287
|
order: number;
|
|
288
|
-
format: (config: ScopeConfig) =>
|
|
288
|
+
format: (config: ScopeConfig) => string;
|
|
289
289
|
cycle: (config: ScopeConfig, delta: number) => void;
|
|
290
|
-
setFromString: (config: ScopeConfig, raw: string) =>
|
|
290
|
+
setFromString: (config: ScopeConfig, raw: string) => "document_port must be an integer from 1 to 65535" | null;
|
|
291
291
|
};
|
|
292
292
|
};
|
|
293
293
|
detailed_tool_recaps: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var w=Object.defineProperty;var n=(e,t)=>w(e,"name",{value:t,configurable:!0});import{CONDENSED_HISTORY_MODES as p,DOC_LINK_OPEN_MODES as u,MOUSE_MODE_DEFAULTS as d,WHIP_MESSAGE_MODES as c}from"../types.js";function a(e,t,o){const r=e.indexOf(t),i=r>=0?r:0;return e[(i+o+e.length)%e.length]}n(a,"cycleChoice");function f(e,t,o){const r=e.indexOf(t);return r>=0?e[(r+o+e.length)%e.length]:o>=0?e.find(i=>i>t)??e[0]:[...e].reverse().find(i=>i<t)??e[e.length-1]}n(f,"cycleNumber");function s(e,t){return e==="true"?!0:e==="false"?!1:`${t} must be true or false`}n(s,"booleanFromString");function _(e,t){if(!/^\d+$/.test(e))return`${t} must be an integer >= 0`;const o=Number(e);return Number.isSafeInteger(o)?o:`${t} must be an integer >= 0`}n(_,"nonNegativeIntegerFromString");function m(e,t){const o=_(e,t);return typeof o=="string"||o<1?`${t} must be an integer >= 1`:o}n(m,"positiveIntegerFromString");const g=[!1,"notify","apply"],b=[0,1,6,12,24,48,72,168],k=[1,2,3,4,5,6,8,10,12],l="openai-codex";function h(e,t){const o=e.providerOptions[t];if(o!==void 0)return o;const r={};return e.providerOptions[t]=r,r}n(h,"providerOptionsFor");const S=[1,2,3,4,5,6],y={schema_version:{surface:"specialized",owner:"internal schema migration"},marketplaces:{surface:"specialized",owner:"Plugins and pkg market commands"},plugins:{surface:"specialized",owner:"Plugins tab and pkg plugin commands"},auto_update:{surface:"settings",settings:{crtr:{key:"auto_update.crtr",label:"crouter updates",description:"Notify about crouter releases, install them automatically, or turn checks off.",valueConstraint:"true|false|notify|apply; true stores notify",applies:"Applies on the next crouter command.",group:"updates",order:70,format:n(e=>e.auto_update.crtr===!1?"off":e.auto_update.crtr,"format"),cycle:n((e,t)=>{e.auto_update.crtr=a(g,e.auto_update.crtr,t)},"cycle"),setFromString:n((e,t)=>{const o=t==="true"?"notify":t;return o!=="notify"&&o!=="apply"&&o!=="false"?"auto_update.crtr must be notify, apply, or false":(e.auto_update.crtr=o==="false"?!1:o,null)},"setFromString")},content:{key:"auto_update.content",label:"Content updates",description:"Notify about plugin and marketplace updates, apply them automatically, or turn checks off.",valueConstraint:"false|notify|apply",applies:"Applies on the next crouter command.",group:"updates",order:80,format:n(e=>e.auto_update.content===!1?"off":e.auto_update.content,"format"),cycle:n((e,t)=>{e.auto_update.content=a(g,e.auto_update.content,t)},"cycle"),setFromString:n((e,t)=>t!=="notify"&&t!=="apply"&&t!=="false"?"auto_update.content must be notify, apply, or false":(e.auto_update.content=t==="false"?!1:t,null),"setFromString")},interval_hours:{key:"auto_update.interval_hours",label:"Update interval",description:"Minimum hours between automatic update checks. Zero checks on every command.",valueConstraint:"integer >= 0",applies:"Applies on the next crouter command.",group:"updates",order:90,format:n(e=>`${e.auto_update.interval_hours}h`,"format"),cycle:n((e,t)=>{e.auto_update.interval_hours=f(b,e.auto_update.interval_hours,t)},"cycle"),setFromString:n((e,t)=>{const o=_(t,"auto_update.interval_hours");return typeof o=="string"?o:(e.auto_update.interval_hours=o,null)},"setFromString")}}},max_panes_per_window:{surface:"settings",setting:{key:"max_panes_per_window",label:"Maximum panes",description:"Maximum human-review panes placed in one tmux window before using another window.",valueConstraint:"integer >= 1",applies:"Applies to the next pane placement.",group:"panes",order:20,format:n(e=>String(e.max_panes_per_window),"format"),cycle:n((e,t)=>{e.max_panes_per_window=f(k,e.max_panes_per_window,t)},"cycle"),setFromString:n((e,t)=>{const o=m(t,"max_panes_per_window");return typeof o=="string"?o:(e.max_panes_per_window=o,null)},"setFromString")}},auto_open_child_viewers:{surface:"settings",setting:{key:"auto_open_child_viewers",label:"Open child panes",description:"Open each managed child beside its watched parent. Off keeps new children running in the background.",valueConstraint:"true|false",applies:"Applies to the next managed child created; independent roots still open their attended viewer.",group:"panes",order:10,format:n(e=>e.auto_open_child_viewers?"on":"off","format"),cycle:n(e=>{e.auto_open_child_viewers=!e.auto_open_child_viewers},"cycle"),setFromString:n((e,t)=>{const o=s(t,"auto_open_child_viewers");return typeof o=="string"?o:(e.auto_open_child_viewers=o,null)},"setFromString")}},brokerThresholds:{surface:"specialized",owner:"Broker limits tab"},lifecycle:{surface:"specialized",owner:"sys config set lifecycle.unattendedParkMs"},completion_bell:{surface:"settings",setting:{key:"completion_bell",label:"Completion bell",description:"Ring a short sound when the node in an attached viewer finishes a turn.",valueConstraint:"true|false",applies:"Applies on the next turn end.",group:"display",order:41,format:n(e=>e.completion_bell?"on":"off","format"),cycle:n(e=>{e.completion_bell=!e.completion_bell},"cycle"),setFromString:n((e,t)=>{const o=s(t,"completion_bell");return typeof o=="string"?o:(e.completion_bell=o,null)},"setFromString")}},working_gerunds:{surface:"specialized",owner:"sys config set working_gerunds"},whip_mode:{surface:"settings",setting:{key:"whip_mode",label:"Whip mode",description:"Play the whip animation when you send a prompt and when a viewer opens for an agent with an initial prompt.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:39,format:n(e=>e.whip_mode?"on":"off","format"),cycle:n(e=>{e.whip_mode=!e.whip_mode},"cycle"),setFromString:n((e,t)=>{const o=s(t,"whip_mode");return typeof o=="string"?o:(e.whip_mode=o,null)},"setFromString")}},page_surface:{surface:"settings",setting:{key:"page_surface",label:"Page human surface",description:"This host reads pages in a browser rather than a terminal. Agents are told the page module runs as written, HTML pages are offered, and document review and live display leave the command tree.",valueConstraint:"true|false",applies:"Applies when human command help is assembled.",group:"display",order:37,format:n(e=>e.page_surface?"on":"off","format"),cycle:n(e=>{e.page_surface=!e.page_surface},"cycle"),setFromString:n((e,t)=>{const o=s(t,"page_surface");return typeof o=="string"?o:(e.page_surface=o,null)},"setFromString")}},whip_messages:{surface:"specialized",owner:"sys config set whip_messages"},whip_message_mode:{surface:"settings",setting:{key:"whip_message_mode",label:"Whip message",description:"What the whip action sends: a random line from whip_messages, or the fixed \u201CGo faster\u201D.",valueConstraint:c.join("|"),applies:"Applies on the next attach.",group:"display",order:40,format:n(e=>e.whip_message_mode==="go-faster"?"go faster":"random","format"),cycle:n((e,t)=>{e.whip_message_mode=a(c,e.whip_message_mode,t)},"cycle"),setFromString:n((e,t)=>c.includes(t)?(e.whip_message_mode=t,null):`whip_message_mode must be one of: ${c.join("|")}`,"setFromString")}},mouse_mode_default:{surface:"settings",setting:{key:"mouse_mode_default",label:"Default mouse mode",description:"Initial mouse wheel scrolling for each attached viewer.",valueConstraint:d.join("|"),applies:"Applies on the next attach.",group:"display",order:38,format:n(e=>e.mouse_mode_default==="tmux"?"follow tmux":e.mouse_mode_default,"format"),cycle:n((e,t)=>{e.mouse_mode_default=a(d,e.mouse_mode_default,t)},"cycle"),setFromString:n((e,t)=>d.includes(t)?(e.mouse_mode_default=t,null):`mouse_mode_default must be one of: ${d.join("|")}`,"setFromString")}},live_cycles:{surface:"settings",setting:{key:"live_cycles",label:"Live cycles",description:"Trailing refresh cycles rendered at full fidelity; older cycles condense to text.",valueConstraint:"integer >= 1",applies:"Applies on the next attach.",group:"display",order:50,format:n(e=>String(e.live_cycles),"format"),cycle:n((e,t)=>{e.live_cycles=f(S,e.live_cycles,t)},"cycle"),setFromString:n((e,t)=>{const o=m(t,"live_cycles");return typeof o=="string"?o:(e.live_cycles=o,null)},"setFromString")}},condensed_history:{surface:"settings",setting:{key:"condensed_history",label:"Condensed history",description:"Messages retained from condensed cycles. Tool calls are never kept.",valueConstraint:p.join("|"),applies:"Applies on the next attach.",group:"display",order:60,format:n(e=>e.condensed_history,"format"),cycle:n((e,t)=>{e.condensed_history=a(p,e.condensed_history,t)},"cycle"),setFromString:n((e,t)=>p.includes(t)?(e.condensed_history=t,null):`condensed_history must be one of: ${p.join("|")}`,"setFromString")}},bash_tool_purpose:{surface:"settings",setting:{key:"bash_tool_purpose",label:"Bash purpose field",description:"Expose the optional purpose label to agents in the bash tool schema. Off removes the field without disabling the bash valve.",valueConstraint:"true|false",applies:"Applies on the next node session.",group:"display",order:25,format:n(e=>e.bash_tool_purpose?"on":"off","format"),cycle:n(e=>{e.bash_tool_purpose=!e.bash_tool_purpose},"cycle"),setFromString:n((e,t)=>{const o=s(t,"bash_tool_purpose");return typeof o=="string"?o:(e.bash_tool_purpose=o,null)},"setFromString")}},fold_finished_tools:{surface:"settings",setting:{key:"fold_finished_tools",label:"Fold finished tools",description:"Fold every settled tool call to its call line. Toggle live with /fold-tools.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:30,format:n(e=>e.fold_finished_tools?"on":"off","format"),cycle:n(e=>{e.fold_finished_tools=!e.fold_finished_tools},"cycle"),setFromString:n((e,t)=>{const o=s(t,"fold_finished_tools");return typeof o=="string"?o:(e.fold_finished_tools=o,null)},"setFromString")}},summarize_tool_calls:{surface:"settings",setting:{key:"summarize_tool_calls",label:"Summarize tool calls",description:"Replace completed folded tool-call groups with a one-line broker summary when available.",valueConstraint:"true|false",applies:"Applies when the next visible message closes a tool-call group.",group:"display",order:35,format:n(e=>e.summarize_tool_calls?"on":"off","format"),cycle:n(e=>{e.summarize_tool_calls=!e.summarize_tool_calls},"cycle"),setFromString:n((e,t)=>{const o=s(t,"summarize_tool_calls");return typeof o=="string"?o:(e.summarize_tool_calls=o,null)},"setFromString")}},doc_link_open:{surface:"settings",setting:{key:"doc_link_open",label:"Document links open in",description:"Where clicking a [[document]] link in a viewer opens it: a read-only browser page with a rendered/raw toggle, or Neovim to edit it and save a new revision (needs iTerm Semantic History).",valueConstraint:u.join("|"),applies:"Applies on the next attach.",group:"display",order:37.5,format:n(e=>e.doc_link_open,"format"),cycle:n((e,t)=>{e.doc_link_open=a(u,e.doc_link_open,t)},"cycle"),setFromString:n((e,t)=>u.includes(t)?(e.doc_link_open=t,null):`doc_link_open must be one of: ${u.join("|")}`,"setFromString")}},detailed_tool_recaps:{surface:"settings",setting:{key:"detailed_tool_recaps",label:"Detailed tool recaps",description:"Show each document read and edited file in folded tool recaps. Off shows a compact count line instead.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:36,format:n(e=>e.detailed_tool_recaps?"on":"off","format"),cycle:n(e=>{e.detailed_tool_recaps=!e.detailed_tool_recaps},"cycle"),setFromString:n((e,t)=>{const o=s(t,"detailed_tool_recaps");return typeof o=="string"?o:(e.detailed_tool_recaps=o,null)},"setFromString")}},consolidate_replies:{surface:"settings",setting:{key:"consolidate_replies",label:"Consolidate replies",description:"Experiment: show one rewritten agent reply per message you type, with the working folded behind it (/show-working).",valueConstraint:"true|false",applies:"Applies on the next attach; the broker reads it per turn.",group:"display",order:36.5,format:n(e=>e.consolidate_replies?"on":"off","format"),cycle:n(e=>{e.consolidate_replies=!e.consolidate_replies},"cycle"),setFromString:n((e,t)=>{const o=s(t,"consolidate_replies");return typeof o=="string"?o:(e.consolidate_replies=o,null)},"setFromString")}},keybindings:{surface:"specialized",owner:"Keybindings tab"},tmux_passthrough:{surface:"specialized",owner:"Keybindings tab"},providerOptions:{surface:"settings",settings:{"openai-codex.fastMode":{key:`providerOptions.${l}.fastMode`,label:"Codex fast mode",description:"Serve Codex turns at the ChatGPT priority tier: about 1.5x faster generation for about 2x the credits. Displayed cost does not reflect the premium.",valueConstraint:"true|false",applies:"Applies on each node's next turn.",group:"providers",order:40,format:n(e=>e.providerOptions[l]?.fastMode?"on":"off","format"),cycle:n(e=>{const t=h(e,l);t.fastMode=!t.fastMode},"cycle"),setFromString:n((e,t)=>{const o=s(t,`providerOptions.${l}.fastMode`);return typeof o=="string"?o:(h(e,l).fastMode=o,null)},"setFromString")}}},modelLadders:{surface:"specialized",owner:"Models tab"},modelRoutes:{surface:"specialized",owner:"Models route declarations"},modelRouting:{surface:"specialized",owner:"Models route declarations"},kinds:{surface:"specialized",owner:"Models tab and kind registry"},page_components:{surface:"specialized",owner:"Page component catalog: product-registered page component kinds"},remoteCanvas:{surface:"specialized",owner:"canvas config commands"},api:{surface:"specialized",owner:"sys connect"},paths:{surface:"specialized",owner:"runtime layout"},store:{surface:"specialized",owner:"daemon conversation store"},runtime:{surface:"specialized",owner:"app-facing listener"},spawnEnv:{surface:"specialized",owner:"spawn environment allow-list"},bin:{surface:"specialized",owner:"bare-binary contributions on every node PATH"},humanActions:{surface:"specialized",owner:"scope-configured human completion actions"}},A=Object.keys(y);function x(){const e=[];for(const t of Object.values(y))t.surface==="settings"&&("setting"in t?e.push(t.setting):e.push(...Object.values(t.settings)));return e.sort((t,o)=>t.order-o.order)}n(x,"collectUserSettings");const v=x();function E(e){return v.find(t=>t.key===e)}n(E,"findUserSetting");function z(e){return v.filter(t=>t.group===e)}n(z,"userSettingsInGroup");export{y as CONFIG_SETTINGS_COVERAGE,A as SCOPE_CONFIG_KEYS,v as USER_SETTINGS,E as findUserSetting,z as userSettingsInGroup};
|
|
1
|
+
var v=Object.defineProperty;var n=(e,t)=>v(e,"name",{value:t,configurable:!0});import{CONDENSED_HISTORY_MODES as u,MOUSE_MODE_DEFAULTS as p,WHIP_MESSAGE_MODES as d}from"../types.js";function i(e,t,o){const s=e.indexOf(t),a=s>=0?s:0;return e[(a+o+e.length)%e.length]}n(i,"cycleChoice");function c(e,t,o){const s=e.indexOf(t);return s>=0?e[(s+o+e.length)%e.length]:o>=0?e.find(a=>a>t)??e[0]:[...e].reverse().find(a=>a<t)??e[e.length-1]}n(c,"cycleNumber");function r(e,t){return e==="true"?!0:e==="false"?!1:`${t} must be true or false`}n(r,"booleanFromString");function f(e,t){if(!/^\d+$/.test(e))return`${t} must be an integer >= 0`;const o=Number(e);return Number.isSafeInteger(o)?o:`${t} must be an integer >= 0`}n(f,"nonNegativeIntegerFromString");function _(e,t){const o=f(e,t);return typeof o=="string"||o<1?`${t} must be an integer >= 1`:o}n(_,"positiveIntegerFromString");const m=[!1,"notify","apply"],w=[0,1,6,12,24,48,72,168],b=[1,2,3,4,5,6,8,10,12],l="openai-codex";function g(e,t){const o=e.providerOptions[t];if(o!==void 0)return o;const s={};return e.providerOptions[t]=s,s}n(g,"providerOptionsFor");const x=[1,2,3,4,5,6],h={schema_version:{surface:"specialized",owner:"internal schema migration"},marketplaces:{surface:"specialized",owner:"Plugins and pkg market commands"},plugins:{surface:"specialized",owner:"Plugins tab and pkg plugin commands"},auto_update:{surface:"settings",settings:{crtr:{key:"auto_update.crtr",label:"crouter updates",description:"Notify about crouter releases, install them automatically, or turn checks off.",valueConstraint:"true|false|notify|apply; true stores notify",applies:"Applies on the next crouter command.",group:"updates",order:70,format:n(e=>e.auto_update.crtr===!1?"off":e.auto_update.crtr,"format"),cycle:n((e,t)=>{e.auto_update.crtr=i(m,e.auto_update.crtr,t)},"cycle"),setFromString:n((e,t)=>{const o=t==="true"?"notify":t;return o!=="notify"&&o!=="apply"&&o!=="false"?"auto_update.crtr must be notify, apply, or false":(e.auto_update.crtr=o==="false"?!1:o,null)},"setFromString")},content:{key:"auto_update.content",label:"Content updates",description:"Notify about plugin and marketplace updates, apply them automatically, or turn checks off.",valueConstraint:"false|notify|apply",applies:"Applies on the next crouter command.",group:"updates",order:80,format:n(e=>e.auto_update.content===!1?"off":e.auto_update.content,"format"),cycle:n((e,t)=>{e.auto_update.content=i(m,e.auto_update.content,t)},"cycle"),setFromString:n((e,t)=>t!=="notify"&&t!=="apply"&&t!=="false"?"auto_update.content must be notify, apply, or false":(e.auto_update.content=t==="false"?!1:t,null),"setFromString")},interval_hours:{key:"auto_update.interval_hours",label:"Update interval",description:"Minimum hours between automatic update checks. Zero checks on every command.",valueConstraint:"integer >= 0",applies:"Applies on the next crouter command.",group:"updates",order:90,format:n(e=>`${e.auto_update.interval_hours}h`,"format"),cycle:n((e,t)=>{e.auto_update.interval_hours=c(w,e.auto_update.interval_hours,t)},"cycle"),setFromString:n((e,t)=>{const o=f(t,"auto_update.interval_hours");return typeof o=="string"?o:(e.auto_update.interval_hours=o,null)},"setFromString")}}},max_panes_per_window:{surface:"settings",setting:{key:"max_panes_per_window",label:"Maximum panes",description:"Maximum human-review panes placed in one tmux window before using another window.",valueConstraint:"integer >= 1",applies:"Applies to the next pane placement.",group:"panes",order:20,format:n(e=>String(e.max_panes_per_window),"format"),cycle:n((e,t)=>{e.max_panes_per_window=c(b,e.max_panes_per_window,t)},"cycle"),setFromString:n((e,t)=>{const o=_(t,"max_panes_per_window");return typeof o=="string"?o:(e.max_panes_per_window=o,null)},"setFromString")}},auto_open_child_viewers:{surface:"settings",setting:{key:"auto_open_child_viewers",label:"Open child panes",description:"Open each managed child beside its watched parent. Off keeps new children running in the background.",valueConstraint:"true|false",applies:"Applies to the next managed child created; independent roots still open their attended viewer.",group:"panes",order:10,format:n(e=>e.auto_open_child_viewers?"on":"off","format"),cycle:n(e=>{e.auto_open_child_viewers=!e.auto_open_child_viewers},"cycle"),setFromString:n((e,t)=>{const o=r(t,"auto_open_child_viewers");return typeof o=="string"?o:(e.auto_open_child_viewers=o,null)},"setFromString")}},brokerThresholds:{surface:"specialized",owner:"Broker limits tab"},lifecycle:{surface:"specialized",owner:"sys config set lifecycle.unattendedParkMs"},completion_bell:{surface:"settings",setting:{key:"completion_bell",label:"Completion bell",description:"Ring a short sound when the node in an attached viewer finishes a turn.",valueConstraint:"true|false",applies:"Applies on the next turn end.",group:"display",order:41,format:n(e=>e.completion_bell?"on":"off","format"),cycle:n(e=>{e.completion_bell=!e.completion_bell},"cycle"),setFromString:n((e,t)=>{const o=r(t,"completion_bell");return typeof o=="string"?o:(e.completion_bell=o,null)},"setFromString")}},working_gerunds:{surface:"specialized",owner:"sys config set working_gerunds"},whip_mode:{surface:"settings",setting:{key:"whip_mode",label:"Whip mode",description:"Play the whip animation when you send a prompt and when a viewer opens for an agent with an initial prompt.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:39,format:n(e=>e.whip_mode?"on":"off","format"),cycle:n(e=>{e.whip_mode=!e.whip_mode},"cycle"),setFromString:n((e,t)=>{const o=r(t,"whip_mode");return typeof o=="string"?o:(e.whip_mode=o,null)},"setFromString")}},page_surface:{surface:"settings",setting:{key:"page_surface",label:"Page human surface",description:"This host reads pages in a browser rather than a terminal. Agents are told the page module runs as written, HTML pages are offered, and document review and live display leave the command tree.",valueConstraint:"true|false",applies:"Applies when human command help is assembled.",group:"display",order:37,format:n(e=>e.page_surface?"on":"off","format"),cycle:n(e=>{e.page_surface=!e.page_surface},"cycle"),setFromString:n((e,t)=>{const o=r(t,"page_surface");return typeof o=="string"?o:(e.page_surface=o,null)},"setFromString")}},whip_messages:{surface:"specialized",owner:"sys config set whip_messages"},whip_message_mode:{surface:"settings",setting:{key:"whip_message_mode",label:"Whip message",description:"What the whip action sends: a random line from whip_messages, or the fixed \u201CGo faster\u201D.",valueConstraint:d.join("|"),applies:"Applies on the next attach.",group:"display",order:40,format:n(e=>e.whip_message_mode==="go-faster"?"go faster":"random","format"),cycle:n((e,t)=>{e.whip_message_mode=i(d,e.whip_message_mode,t)},"cycle"),setFromString:n((e,t)=>d.includes(t)?(e.whip_message_mode=t,null):`whip_message_mode must be one of: ${d.join("|")}`,"setFromString")}},mouse_mode_default:{surface:"settings",setting:{key:"mouse_mode_default",label:"Default mouse mode",description:"Initial mouse wheel scrolling for each attached viewer.",valueConstraint:p.join("|"),applies:"Applies on the next attach.",group:"display",order:38,format:n(e=>e.mouse_mode_default==="tmux"?"follow tmux":e.mouse_mode_default,"format"),cycle:n((e,t)=>{e.mouse_mode_default=i(p,e.mouse_mode_default,t)},"cycle"),setFromString:n((e,t)=>p.includes(t)?(e.mouse_mode_default=t,null):`mouse_mode_default must be one of: ${p.join("|")}`,"setFromString")}},live_cycles:{surface:"settings",setting:{key:"live_cycles",label:"Live cycles",description:"Trailing refresh cycles rendered at full fidelity; older cycles condense to text.",valueConstraint:"integer >= 1",applies:"Applies on the next attach.",group:"display",order:50,format:n(e=>String(e.live_cycles),"format"),cycle:n((e,t)=>{e.live_cycles=c(x,e.live_cycles,t)},"cycle"),setFromString:n((e,t)=>{const o=_(t,"live_cycles");return typeof o=="string"?o:(e.live_cycles=o,null)},"setFromString")}},condensed_history:{surface:"settings",setting:{key:"condensed_history",label:"Condensed history",description:"Messages retained from condensed cycles. Tool calls are never kept.",valueConstraint:u.join("|"),applies:"Applies on the next attach.",group:"display",order:60,format:n(e=>e.condensed_history,"format"),cycle:n((e,t)=>{e.condensed_history=i(u,e.condensed_history,t)},"cycle"),setFromString:n((e,t)=>u.includes(t)?(e.condensed_history=t,null):`condensed_history must be one of: ${u.join("|")}`,"setFromString")}},bash_tool_purpose:{surface:"settings",setting:{key:"bash_tool_purpose",label:"Bash purpose field",description:"Expose the optional purpose label to agents in the bash tool schema. Off removes the field without disabling the bash valve.",valueConstraint:"true|false",applies:"Applies on the next node session.",group:"display",order:25,format:n(e=>e.bash_tool_purpose?"on":"off","format"),cycle:n(e=>{e.bash_tool_purpose=!e.bash_tool_purpose},"cycle"),setFromString:n((e,t)=>{const o=r(t,"bash_tool_purpose");return typeof o=="string"?o:(e.bash_tool_purpose=o,null)},"setFromString")}},fold_finished_tools:{surface:"settings",setting:{key:"fold_finished_tools",label:"Fold finished tools",description:"Fold every settled tool call to its call line. Toggle live with /fold-tools.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:30,format:n(e=>e.fold_finished_tools?"on":"off","format"),cycle:n(e=>{e.fold_finished_tools=!e.fold_finished_tools},"cycle"),setFromString:n((e,t)=>{const o=r(t,"fold_finished_tools");return typeof o=="string"?o:(e.fold_finished_tools=o,null)},"setFromString")}},summarize_tool_calls:{surface:"settings",setting:{key:"summarize_tool_calls",label:"Summarize tool calls",description:"Replace completed folded tool-call groups with a one-line broker summary when available.",valueConstraint:"true|false",applies:"Applies when the next visible message closes a tool-call group.",group:"display",order:35,format:n(e=>e.summarize_tool_calls?"on":"off","format"),cycle:n(e=>{e.summarize_tool_calls=!e.summarize_tool_calls},"cycle"),setFromString:n((e,t)=>{const o=r(t,"summarize_tool_calls");return typeof o=="string"?o:(e.summarize_tool_calls=o,null)},"setFromString")}},document_port:{surface:"settings",setting:{key:"document_port",label:"Document browser port",description:"Fixed localhost port where crtrd serves editable document pages.",valueConstraint:"1-65535",applies:"Applies after daemon handover and on the next attach.",group:"display",order:37.5,format:n(e=>String(e.document_port),"format"),cycle:n((e,t)=>{e.document_port=Math.max(1,Math.min(65535,e.document_port+t))},"cycle"),setFromString:n((e,t)=>{const o=Number(t);return!/^\d+$/.test(t)||!Number.isInteger(o)||o<1||o>65535?"document_port must be an integer from 1 to 65535":(e.document_port=o,null)},"setFromString")}},detailed_tool_recaps:{surface:"settings",setting:{key:"detailed_tool_recaps",label:"Detailed tool recaps",description:"Show each document read and edited file in folded tool recaps. Off shows a compact count line instead.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:36,format:n(e=>e.detailed_tool_recaps?"on":"off","format"),cycle:n(e=>{e.detailed_tool_recaps=!e.detailed_tool_recaps},"cycle"),setFromString:n((e,t)=>{const o=r(t,"detailed_tool_recaps");return typeof o=="string"?o:(e.detailed_tool_recaps=o,null)},"setFromString")}},consolidate_replies:{surface:"settings",setting:{key:"consolidate_replies",label:"Consolidate replies",description:"Experiment: show one rewritten agent reply per message you type, with the working folded behind it (/show-working).",valueConstraint:"true|false",applies:"Applies on the next attach; the broker reads it per turn.",group:"display",order:36.5,format:n(e=>e.consolidate_replies?"on":"off","format"),cycle:n(e=>{e.consolidate_replies=!e.consolidate_replies},"cycle"),setFromString:n((e,t)=>{const o=r(t,"consolidate_replies");return typeof o=="string"?o:(e.consolidate_replies=o,null)},"setFromString")}},keybindings:{surface:"specialized",owner:"Keybindings tab"},tmux_passthrough:{surface:"specialized",owner:"Keybindings tab"},providerOptions:{surface:"settings",settings:{"openai-codex.fastMode":{key:`providerOptions.${l}.fastMode`,label:"Codex fast mode",description:"Serve Codex turns at the ChatGPT priority tier: about 1.5x faster generation for about 2x the credits. Displayed cost does not reflect the premium.",valueConstraint:"true|false",applies:"Applies on each node's next turn.",group:"providers",order:40,format:n(e=>e.providerOptions[l]?.fastMode?"on":"off","format"),cycle:n(e=>{const t=g(e,l);t.fastMode=!t.fastMode},"cycle"),setFromString:n((e,t)=>{const o=r(t,`providerOptions.${l}.fastMode`);return typeof o=="string"?o:(g(e,l).fastMode=o,null)},"setFromString")}}},modelLadders:{surface:"specialized",owner:"Models tab"},modelRoutes:{surface:"specialized",owner:"Models route declarations"},modelRouting:{surface:"specialized",owner:"Models route declarations"},kinds:{surface:"specialized",owner:"Models tab and kind registry"},page_components:{surface:"specialized",owner:"Page component catalog: product-registered page component kinds"},remoteCanvas:{surface:"specialized",owner:"canvas config commands"},api:{surface:"specialized",owner:"sys connect"},paths:{surface:"specialized",owner:"runtime layout"},store:{surface:"specialized",owner:"daemon conversation store"},runtime:{surface:"specialized",owner:"app-facing listener"},spawnEnv:{surface:"specialized",owner:"spawn environment allow-list"},bin:{surface:"specialized",owner:"bare-binary contributions on every node PATH"},humanActions:{surface:"specialized",owner:"scope-configured human completion actions"}},O=Object.keys(h);function S(){const e=[];for(const t of Object.values(h))t.surface==="settings"&&("setting"in t?e.push(t.setting):e.push(...Object.values(t.settings)));return e.sort((t,o)=>t.order-o.order)}n(S,"collectUserSettings");const y=S();function A(e){return y.find(t=>t.key===e)}n(A,"findUserSetting");function z(e){return y.filter(t=>t.group===e)}n(z,"userSettingsInGroup");export{h as CONFIG_SETTINGS_COVERAGE,O as SCOPE_CONFIG_KEYS,y as USER_SETTINGS,A as findUserSetting,z as userSettingsInGroup};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var R=Object.defineProperty;var s=(r,e)=>R(r,"name",{value:e,configurable:!0});import{WebSocketServer as $}from"ws";import{isSafeNodeId as A}from"../../core/canvas/paths.js";import{getNode as S}from"../../core/canvas/index.js";import{BROKER_READ_CAPS as C,CLIENT_READ_CAPS as T,FrameDecoder as x,FrameOverflowError as B}from"../../core/runtime/broker-protocol.js";import{classify as k}from"../../core/fault-classifier.js";import{clearFault as v,markNodeRead as N,recordFault as g}from"../../core/canvas/recovery.js";import{decideAttachRevive as O}from"../../core/runtime/revive.js";import{connectViewSocket as P,probeViewSocket as F,waitForBrokerViewSocket as U}from"../../core/runtime/view-socket.js";import{isBeneathRefusal as D}from"../../core/spaces/open-beneath.js";import{attachViewer as _,detachViewer as V,presentViewer as L}from"./viewer-presence.js";const H=new $({noServer:!0,maxPayload:C.maxLineBytes}),M=25e3,J=6e4;function W(r,e=M,i=J){let
|
|
1
|
+
var R=Object.defineProperty;var s=(r,e)=>R(r,"name",{value:e,configurable:!0});import{WebSocketServer as $}from"ws";import{isSafeNodeId as A}from"../../core/canvas/paths.js";import{getNode as S}from"../../core/canvas/index.js";import{BROKER_READ_CAPS as C,CLIENT_READ_CAPS as T,FrameDecoder as x,FrameOverflowError as B}from"../../core/runtime/broker-protocol.js";import{classify as k}from"../../core/fault-classifier.js";import{clearFault as v,markNodeRead as N,recordFault as g}from"../../core/canvas/recovery.js";import{decideAttachRevive as O}from"../../core/runtime/revive.js";import{connectViewSocket as P,probeViewSocket as F,waitForBrokerViewSocket as U}from"../../core/runtime/view-socket.js";import{isBeneathRefusal as D}from"../../core/spaces/open-beneath.js";import{attachViewer as _,detachViewer as V,presentViewer as L}from"./viewer-presence.js";const H=new $({noServer:!0,maxPayload:C.maxLineBytes}),M=25e3,J=6e4;function W(r,e=M,i=J){let n=!0,o=!1,a=Date.now();const f=s(()=>{n=!0},"acknowledge"),m=s(()=>{o||(o=!0,clearInterval(h),r.off("pong",f))},"stop");r.on("pong",f);const h=setInterval(()=>{if(Date.now()-a>i){n=!0;return}if(!n){m(),r.terminate();return}n=!1;try{r.ping()}catch{m(),r.terminate()}},e);return h.unref(),{stop:m,noteActivity:s(()=>{a=Date.now()},"noteActivity")}}s(W,"startRemoteAttachHeartbeat");function b(r){const e=Buffer.from(r,"utf8");return e.byteLength<=123?r:e.subarray(0,123).toString("utf8")}s(b,"clampReason");function u(r,e,i,n,o){const a=JSON.stringify({error:{code:n,message:o,origin:"runtime"}}),f=`HTTP/1.1 ${e} ${i}\r
|
|
2
2
|
content-type: application/json; charset=utf-8\r
|
|
3
3
|
content-length: ${Buffer.byteLength(a)}\r
|
|
4
4
|
connection: close\r
|
|
5
5
|
\r
|
|
6
|
-
`+a;try{r.end(f)}catch{try{r.destroy()}catch{}}}s(u,"refuseUpgrade");function j(r){if(new URL(r.url??"/","http://127.0.0.1").searchParams.get("revive")==="0")return!0;const i=r.headers["x-crtr-attach-revive"];return(Array.isArray(i)?i[0]:i)==="0"}s(j,"reviveOptedOut");function q(r){return new URL(r.url??"/","http://127.0.0.1").searchParams.get("wakeParked")==="1"}s(q,"wakeParkedRequested");function E(r){return r===1e3||r===1001||r===1005}s(E,"isCleanViewerClose");function z(r,e){if(!A(e)){try{r.close(1008,b(`invalid node id: ${e}`))}catch{}return}if(S(e)===null){try{r.close(1011,b(`no node ${e}`))}catch{}return}v(e,{link:"viewer\u2194crtrd"});const i=P(e),
|
|
6
|
+
`+a;try{r.end(f)}catch{try{r.destroy()}catch{}}}s(u,"refuseUpgrade");function j(r){if(new URL(r.url??"/","http://127.0.0.1").searchParams.get("revive")==="0")return!0;const i=r.headers["x-crtr-attach-revive"];return(Array.isArray(i)?i[0]:i)==="0"}s(j,"reviveOptedOut");function q(r){return new URL(r.url??"/","http://127.0.0.1").searchParams.get("wakeParked")==="1"}s(q,"wakeParkedRequested");function E(r){return r===1e3||r===1001||r===1005}s(E,"isCleanViewerClose");function z(r,e){if(!A(e)){try{r.close(1008,b(`invalid node id: ${e}`))}catch{}return}if(S(e)===null){try{r.close(1011,b(`no node ${e}`))}catch{}return}v(e,{link:"viewer\u2194crtrd"});const i=P(e),n=new x(T);let o=!1,a=!1,f,m=!1,h=!1,d=!1;i.on("connect",()=>{m=!0,!o&&d&&_(e,r),v(e,{link:"relay\u2194broker"})});const p=s((t,l)=>{if(!o){if(o=!0,V(e,r),f?.stop(),!i.destroyed)try{i.destroy()}catch{}try{r.close(t,b(l))}catch{}}},"teardown");f=W(r),i.on("data",t=>{f?.noteActivity();let l;try{l=n.push(t)}catch(c){const y=c instanceof B?`broker sent an oversized frame (${c.message})`:`failed to decode a broker frame for ${e}`;p(1009,y);return}for(const c of l)if(r.readyState===r.OPEN&&(r.send(JSON.stringify(c)),d&&c?.type==="welcome"&&L(e,r)))try{N(e)}catch{}}),i.on("error",t=>{const l=k("relay\u2194broker",t);g(e,{link:"relay\u2194broker",op:"bridge socket",kind:l.kind,retry:{disposition:"manual"},message:t.message});const c=t.code==="ECONNREFUSED"||t.code==="ENOENT"?`no running broker for ${e}`:`view socket error for ${e}: ${t.message}`;p(1011,c)}),i.on("close",t=>{o||t||(v(e,{link:"relay\u2194broker"}),p(1e3,"broker socket closed"))}),r.on("close",(t,l)=>{const c=l.toString("utf8");if(o){E(t)&&v(e,{link:"viewer\u2194crtrd"});return}if(E(t))v(e,{link:"viewer\u2194crtrd"});else if(!a){a=!0;const y=k("viewer\u2194crtrd",{code:t,reason:c});g(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:y.kind,retry:{disposition:"auto"},message:c||`remote attach closed (${t})`})}p(t,c||`remote attach closed (${t})`)}),r.on("error",()=>{if(!a){a=!0;const t=k("viewer\u2194crtrd",{code:1006,reason:"remote attach websocket error"});g(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:t.kind,retry:{disposition:"auto"},message:"remote attach websocket error"})}p(1011,"remote attach websocket error")}),r.on("message",t=>{if(o||i.destroyed)return;f?.noteActivity();const c=(Array.isArray(t)?Buffer.concat(t):Buffer.isBuffer(t)?t:Buffer.from(new Uint8Array(t))).toString("utf8");if(!h)try{const w=JSON.parse(c);w?.type==="hello"&&(h=!0,d=w.attends!==!1,d&&m&&_(e,r))}catch{}const y=c.endsWith(`
|
|
7
7
|
`)?c:c+`
|
|
8
|
-
`;try{i.write(y)}catch{}})}s(z,"bridgeConnection");async function
|
|
8
|
+
`;try{i.write(y)}catch{}})}s(z,"bridgeConnection");async function oe(r,e,i,n){e.on("error",()=>{});try{if(!A(n)){u(e,400,"Bad Request","invalid_node_id",`invalid node id: ${n}`);return}if(S(n)===null){u(e,404,"Not Found","node_not_found",`no node ${n}`);return}if(j(r)){if(!await F(n,250)){u(e,409,"Conflict","node_dormant",`node ${n} is dormant and revive was opted out (?revive=0)`);return}}else{let o;try{const a=await O(n,{resume:!0,capacity:"refuse"},q(r));if(a.parked){u(e,409,"Conflict","node_dormant",`node ${n} is parked; attach with ?wakeParked=1 to wake it`);return}o=a.result.launch?.exited}catch(a){const f=a instanceof Error?a.message:String(a);u(e,409,"Conflict","attach_revive_failed",`attach revive failed: ${f}`);return}if(!await U(n,o)){u(e,409,"Conflict","broker_not_ready",`broker view socket for ${n} did not become listenable after revive`);return}}if(e.destroyed)return;H.handleUpgrade(r,e,i,o=>z(o,n))}catch(o){const a=o instanceof Error?o.message:String(o);if(D(o)){u(e,400,"Bad Request","invalid_request",a);return}u(e,500,"Internal Server Error","internal",`attach upgrade failed: ${a}`)}}s(oe,"handleAttachUpgrade");export{M as REMOTE_ATTACH_HEARTBEAT_MS,J as REMOTE_ATTACH_QUIET_MS,oe as handleAttachUpgrade,E as isCleanViewerClose,u as refuseUpgrade,W as startRemoteAttachHeartbeat};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Why this daemon has no document listener, or undefined while one is bound. `/v1/documents/browser-access` refuses the secret while this is set, so a viewer never sends it to whatever process holds the port. */
|
|
2
|
+
export declare function documentListenerUnavailable(): string | undefined;
|
|
3
|
+
/** Dedicated localhost-only browser listener, independent of /v1 and its TCP bearer configuration. Every route except `/assets/` needs the per-install `secret`, carried by a cookie that `/r/<ref>?t=<secret>` sets. */
|
|
4
|
+
export declare function createDocumentListener(port: number, secret: string): {
|
|
5
|
+
ready: Promise<void>;
|
|
6
|
+
close: () => Promise<void>;
|
|
7
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var L=Object.defineProperty;var h=(n,r)=>L(n,"name",{value:r,configurable:!0});import{createHash as D,timingSafeEqual as A}from"node:crypto";import{createServer as W}from"node:http";import{readFileSync as U}from"node:fs";import{join as T,extname as z}from"node:path";import{fileURLToPath as B}from"node:url";import{withCanvasWrite as g}from"../../core/canvas/db.js";import{ApiError as G}from"../../api/errors.js";import{bodyOf as b}from"../../core/graph/bodies.js";import{canChange as E,isShipped as I}from"../../core/graph/access.js";import{documentRecord as H,editDocument as F,heads as x,withCurrentNames as C}from"../../core/graph/documents.js";import{getById as O}from"../../core/graph/objects.js";import{subscribeDocumentEvents as J}from"../../core/graph/events.js";import{checkoutsForCwd as M}from"../../core/graph/repo-sync/repos.js";import{canonicalName as V,repoChange as X,syncChanged as Z}from"./handlers/docs.js";import{escapeHtml as k}from"./vendor-signin.js";import{readerFor as w,resolveRef as K}from"./reader.js";import{mergeDocumentLines as Q}from"./document-merge.js";import{crtrVersion as Y}from"../../shared/crtr-version.js";const q="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'",S={class:"person"},f=encodeURIComponent(Y()),N=B(new URL("../../../dist/",import.meta.url));function m(n,r,t,s){n.writeHead(r,{"Content-Type":"application/json; charset=utf-8"}),n.end(JSON.stringify({error:{code:t,message:s}}))}h(m,"error");function j(n,r){const t=h(s=>D("sha256").update(s).digest(),"digest");return A(t(n),t(r))}h(j,"sameSecret");function ee(n,r){for(const t of(n??"").split(";")){const s=t.indexOf("=");if(s!==-1&&t.slice(0,s).trim()===r)return t.slice(s+1).trim()}}h(ee,"cookieValue");function P(n,r,t){n.writeHead(r,{"Content-Type":"application/json; charset=utf-8"}),n.end(JSON.stringify(t))}h(P,"json");function R(n){return g(r=>{const t=O(r,n.id);if(!t||t.deleted_at||t.type!=="document")return null;const s=w(r,{principal:S}),l=H(r,t),o=x(r,t),e=V(r,t),u=o.length===1&&E(r,s,t);return{id:t.id,name:e,revision:l.revision,heads_key:l.heads_key,body:o.length===1?C(r,t,b(o[0].body_hash,l.source_path)):"",editable:u,...u?{}:{readonly_reason:o.length!==1?`has ${o.length} heads and needs a merge`:I(r,t)?"shipped by a plugin":"you cannot change this document"}}})}h(R,"snapshot");function y(n){return g(r=>{const t=w(r,{principal:S});try{const s=K(r,t,n);return"deleted"in s||"remote"in s||s.type!=="document"?null:s}catch(s){if(s instanceof G&&s.code==="not_found")return null;throw s}})}h(y,"resolveDocument");async function te(n){const r=[];let t=0;for await(const s of n){if(t+=s.length,t>2e6)throw new Error("request body exceeds 2 MB");r.push(s)}return JSON.parse(Buffer.concat(r).toString("utf8"))}h(te,"requestBody");function ne(n){const r=g(t=>{const s=w(t,{principal:S}),l=O(t,n.id);if(!l||l.type!=="document"||l.deleted_at)return{status:404,value:{error:{code:"not_found",message:"document not found"}}};const o=H(t,l),e=x(t,l);if(e.length!==1||!E(t,s,l))return{status:403,value:{error:{code:"forbidden",message:"document is read-only"}}};const u=C(t,l,b(e[0].body_hash,o.source_path)),d=t.prepare("SELECT body_hash FROM revisions WHERE document = ? AND rev_id = ?").get(l.rowid,n.base_heads_key);if(!d||C(t,l,b(d.body_hash,o.source_path))!==n.base_body)return{status:409,value:{saved:!1,conflict:{revision:o.revision,heads_key:o.heads_key,current_body:u,your_body:n.body}}};const a=n.base_heads_key!==o.heads_key,i=a?Q(n.base_body,n.body,u):n.body;if(i===null)return{status:409,value:{saved:!1,conflict:{revision:o.revision,heads_key:o.heads_key,current_body:u,your_body:n.body}}};const c=F(t,s,l,{body:i},n.rationale?.trim()||"edited by the person in the browser",o.heads_key,{},v=>M(t,v));return"stale"in c?{status:409,value:{saved:!1,conflict:{revision:c.stale.revision,heads_key:c.stale.heads_key,current_body:u,your_body:n.body}}}:{status:200,value:{saved:!0,revision:c.revision,heads_key:c.heads_key,body:i,merged:a},change:X(t,s,[l])}});return r.change&&Z(r.change),r}h(ne,"save");let p="the document listener has not started";function ke(){return p}h(ke,"documentListenerUnavailable");function we(n,r){const t=`crtr_doc_${n}`,s=W((o,e)=>{(async()=>{e.setHeader("Content-Security-Policy",q),e.setHeader("X-Content-Type-Options","nosniff");const u=[`127.0.0.1:${n}`,`localhost:${n}`];if(typeof o.headers.host!="string"||!u.includes(o.headers.host))return m(e,403,"forbidden","invalid Host");if(o.method==="POST"){if(!u.some(a=>o.headers.origin===`http://${a}`))return m(e,403,"forbidden","invalid Origin");if(o.headers["content-type"]!=="application/json")return m(e,415,"unsupported_media_type","Content-Type must be application/json")}const d=new URL(o.url??"/",`http://127.0.0.1:${n}`);if(!d.pathname.startsWith("/assets/")){const a=o.method==="GET"&&d.pathname.startsWith("/r/")?d.searchParams.get("t"):null;if(a!==null&&j(a,r))return d.searchParams.delete("t"),e.writeHead(303,{Location:`${d.pathname}${d.search}`,"Set-Cookie":`${t}=${r}; HttpOnly; SameSite=Strict; Path=/`,"Cache-Control":"no-store"}),e.end();const i=ee(o.headers.cookie,t);if(a!==null||i===void 0||!j(i,r))return d.pathname.startsWith("/r/")?(e.writeHead(401,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end('<!doctype html><html><head><meta charset="utf-8"><title>Not authorized</title><link rel="icon" href="data:,"></head><body style="font-family:system-ui,sans-serif;max-width:40rem;margin:4rem auto;padding:0 1rem"><h1>Not authorized</h1><p>Open this document from its link in the attach viewer.</p></body></html>')):m(e,401,"unauthorized","open the document link from the attach viewer")}if(o.method==="GET"&&d.pathname.startsWith("/r/")){let a;try{a=decodeURIComponent(d.pathname.slice(3))}catch{return m(e,400,"invalid_request","ref is not valid percent-encoding")}const i=y(a);return i?(e.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end(`<!doctype html><html><head><meta charset="utf-8"><title>${k(i.name)}</title><link rel="icon" href="data:,"><link rel="stylesheet" href="/assets/${f}/editor.css"></head><body><div id="app" data-ref="${k(a)}"></div><script type="module" src="/assets/${f}/editor.js"><\/script></body></html>`)):(e.writeHead(404,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end(`<!doctype html><html><head><meta charset="utf-8"><title>Document not found</title><link rel="icon" href="data:,"></head><body style="font-family:system-ui,sans-serif;max-width:40rem;margin:4rem auto;padding:0 1rem"><h1>Document not found</h1><p>No document you can read is named <code>${k(a)}</code>.</p></body></html>`))}if(o.method==="GET"&&d.pathname==="/api/doc"){const a=d.searchParams.get("ref"),i=a?y(a):null,c=i&&R(i);return c?P(e,200,{...c,ref:a}):m(e,404,"not_found","document not found")}if(o.method==="POST"&&d.pathname==="/api/save"){const a=await te(o);if(!a||typeof a!="object"||Array.isArray(a))return m(e,400,"invalid_request","invalid save body");const i=a;if(typeof i.id!="string"||typeof i.base_heads_key!="string"||typeof i.base_body!="string"||typeof i.body!="string"||i.rationale!==void 0&&typeof i.rationale!="string")return m(e,400,"invalid_request","invalid save body");const c=ne(i);return P(e,c.status,c.value)}if(o.method==="GET"&&d.pathname==="/api/events"){const a=d.searchParams.get("id");if(!a||!y(a))return m(e,404,"not_found","document not found");e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-store",Connection:"keep-alive"}),e.write(`: connected
|
|
2
|
+
|
|
3
|
+
`);const i=J(a,v=>{if(v==="deleted"){e.write(`event: deleted
|
|
4
|
+
data: {}
|
|
5
|
+
|
|
6
|
+
`),e.end();return}const $=y(a),_=$&&R($);_&&e.write(`event: edited
|
|
7
|
+
data: ${JSON.stringify({revision:_.revision,heads_key:_.heads_key})}
|
|
8
|
+
|
|
9
|
+
`)}),c=setInterval(()=>e.write(`: keepalive
|
|
10
|
+
|
|
11
|
+
`),3e4);e.on("close",()=>{clearInterval(c),i()});return}if(o.method==="GET"&&d.pathname.startsWith(`/assets/${f}/`)){const a=d.pathname.slice(`/assets/${f}/`.length);if(!/^[a-zA-Z0-9_.-]+$/.test(a))return m(e,404,"not_found","asset not found");const i=a==="mermaid.js"?T(N,"clients/attach/assets/mermaid.min.js"):T(N,"doc-editor",a);try{const c=U(i);return e.writeHead(200,{"Content-Type":z(a)===".css"?"text/css; charset=utf-8":"text/javascript; charset=utf-8","Cache-Control":"public, max-age=31536000, immutable"}),e.end(c)}catch(c){if(c.code==="ENOENT")return m(e,404,"not_found","asset not found");throw c}}return m(e,404,"not_found","route not found")})().catch(u=>{e.headersSent?e.destroy():m(e,500,"server_failure",u instanceof Error?u.message:String(u))})});return{ready:new Promise((o,e)=>{s.once("error",u=>{p=`the document listener could not bind 127.0.0.1:${n}: ${u.message}`,e(u)}),s.listen(n,"127.0.0.1",()=>{s.removeAllListeners("error"),p=void 0,o()})}),close:h(()=>new Promise(o=>{p="the document listener is closed",s.closeAllConnections(),s.close(()=>o())}),"close")}}h(we,"createDocumentListener");export{we as createDocumentListener,ke as documentListenerUnavailable};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var O=Object.defineProperty;var m=(n,e)=>O(n,"name",{value:e,configurable:!0});function M(n){return n.match(/[^\n]*\n|[^\n]+$/g)??[]}m(M,"linesOf");const T=2e3;function K(n,e){let l=0;for(;l<n.length&&l<e.length&&n[l]===e[l];)l++;let c=0;for(;c<n.length-l&&c<e.length-l&&n[n.length-1-c]===e[e.length-1-c];)c++;const d=n.slice(l,n.length-c),h=e.slice(l,e.length-c),f=d.length,a=h.length;if(f===0&&a===0)return[];const s=Math.min(f+a,T),r=s+1,p=new Int32Array(2*s+3),A=[];let w=-1;for(let t=0;t<=s&&w<0;t++){for(let o=-t;o<=t;o+=2){let u=o===-t||o!==t&&p[r+o-1]<p[r+o+1]?p[r+o+1]:p[r+o-1]+1,i=u-o;for(;u<f&&i<a&&d[u]===h[i];)u++,i++;if(p[r+o]=u,u>=f&&i>=a){w=t;break}}A.push(p.slice(r-t,r+t+1))}if(w<0)return null;const D=new Set,k=new Map;let I=f,S=a;for(let t=w;t>0;t--){const o=A[t-1],u=m(j=>o[j+t-1],"at"),i=I-S,y=i===-t||i!==t&&u(i-1)<u(i+1)?i+1:i-1,v=u(y),E=v-y;if(y===i+1){const j=k.get(v)??[];j.unshift(h[E]),k.set(v,j)}else D.add(v);I=v,S=E}const X=[];let g=null;for(let t=0;t<=f;t++){const o=k.get(t);o&&(g??={start:t+l,end:t+l,lines:[]},g.lines.push(...o)),t<f&&D.has(t)?(g??={start:t+l,end:t+l,lines:[]},g.end=t+l+1):g&&(X.push(g),g=null)}return X}m(K,"changes");function L(n,e){return n.start===n.end&&e.start===e.end?n.start===e.start:n.start===n.end?n.start>e.start&&n.start<e.end:e.start===e.end?e.start>n.start&&e.start<n.end:n.start<e.end&&e.start<n.end}m(L,"overlaps");function _(n,e,l){const c=M(n),d=K(c,M(e)),h=K(c,M(l));if(d===null||h===null)return null;for(const s of d)for(const r of h)if(L(s,r)&&!(s.start===r.start&&s.end===r.end&&s.lines.join("")===r.lines.join("")))return null;const f=[...d,...h.filter(s=>!d.some(r=>L(r,s)))];f.sort((s,r)=>r.start-s.start||r.end-s.end);const a=[...c];for(const s of f)a.splice(s.start,s.end-s.start,...s.lines);return a.join("")}m(_,"mergeDocumentLines");export{_ as mergeDocumentLines};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var n=Object.defineProperty;var o=(t,r)=>n(t,"name",{value:r,configurable:!0});import{randomBytes as c}from"node:crypto";import{readFileSync as m,writeFileSync as i}from"node:fs";import{join as d}from"node:path";import{crtrHome as a}from"../../core/canvas/paths.js";const s=/^[A-Za-z0-9_-]{43,}$/;function S(){const t=d(a(),"document-secret");try{i(t,c(32).toString("base64url"),{mode:384,flag:"wx"})}catch(e){if(e.code!=="EEXIST")throw e}const r=m(t,"utf8").trim();if(!s.test(r))throw new Error(`${t} does not hold a document secret; delete it and restart crtrd to mint a new one`);return r}o(S,"documentSecret");export{S as documentSecret};
|
|
@@ -1,2 +1,16 @@
|
|
|
1
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import type { ObjectRow, Reader } from '../../../core/graph/types.js';
|
|
1
3
|
import type { RouteTable } from '../router.js';
|
|
4
|
+
export declare function canonicalName(db: DatabaseSync, o: ObjectRow): string;
|
|
5
|
+
/** A committed change to a repo-owned document starts that repo's sync. */
|
|
6
|
+
export interface RepoChange {
|
|
7
|
+
repos: ObjectRow[];
|
|
8
|
+
dirs: string[];
|
|
9
|
+
}
|
|
10
|
+
/** The repos among `owners` and the directories that may hold a checkout of them (the caller's cwd,
|
|
11
|
+
* a node's own else the request's, and the checkouts its view reaches), captured inside the write
|
|
12
|
+
* transaction and acted on after it commits. */
|
|
13
|
+
export declare function repoChange(db: DatabaseSync, reader: Reader, owners: ObjectRow[], requestCwd?: string): RepoChange;
|
|
14
|
+
/** The change is committed; a sync that cannot start is reported as an event, never as a failed write. */
|
|
15
|
+
export declare function syncChanged(change: RepoChange): void;
|
|
2
16
|
export declare const docRoutes: RouteTable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var re=Object.defineProperty;var u=(t,e)=>re(t,"name",{value:e,configurable:!0});import{prepared as P}from"../../../core/sqlite-statements.js";import{ApiError as _}from"../../../api/errors.js";import{DOC_KINDS as q,DOC_UNSET_FIELDS as H}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as U}from"../../../api/dto/objects.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{CrtrError as ie}from"../../../core/errors.js";import{emitEvent as se}from"../../../core/events/emit.js";import{canChange as V,spaceOf as ae}from"../../../core/graph/access.js";import{get as A}from"../../../core/graph/bodies.js";import{deleteDocument as ce,documentRecord as F,editDocument as de,heads as I,history as ue,linksIn as K,moveDocument as fe,readBase as me,setDocumentRecord as le,withCurrentNames as pe,writeDocument as he}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getByRowid as L}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as we}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as ye}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ve,rulesOf as ge}from"../../../core/graph/rules.js";import{unifiedDiff as _e}from"../../../core/graph/diff.js";import{ownerFromFlag as M,readerFor as E,resolveRef as z}from"../reader.js";function v(t,e,n,o,s={}){return new _(t,e,n,{...s,error:e,message:n,next:o})}u(v,"refuse");const b=u((t,e)=>new _(400,"invalid_request",t,e),"invalid");function R(t){const e=t.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(R,"bodyOf");const W=u(t=>e=>ye(t,e),"reposFnFor");function S(t,e){if(t instanceof ie){const n=t.details;if(t.code==="read_first")throw v(409,"read_first",t.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,n??{});if(t.code==="conflict")throw new _(409,"name_taken",t.message,n);if(t.code==="forbidden")throw new _(403,"forbidden",t.message,n);if(t.code==="scope_missing")throw v(403,"scope_missing",t.message,`Launch the run with \`${String(n?.scope)}\` in its scopes list.`,n??{});if(n?.rule==="repo-public-only")throw v(422,"repo_documents_are_public",t.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw t}u(S,"mapGraphError");function x(t,e,n){const o=z(t,e,decodeURIComponent(n));if("remote"in o)throw v(404,"not_found",G(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new _(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${n} is a ${o.type}, not a document`);return o}u(x,"refDocument");function G(t){return`no object named ${t} on this runtime; it may be on another runtime, or the name may be misspelled`}u(G,"remoteMessage");function be(t,e){const n=e.owner===null?null:L(t,e.owner);return n?n.type==="node"?n.id:n.name:""}u(be,"ownerHandle");function j(t,e){const n=be(t,e);return n?`${n}/${e.name}`:e.name}u(j,"canonicalName");function ke(t,e){const n=e.owner===null?null:L(t,e.owner),o=n?{id:n.id,type:n.type,handle:n.type==="node"?n.id:n.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at}}u(ke,"objectDTO");function Ee(t,e){const n=e.owner===null?null:L(t,e.owner);return n?.type==="repo"?n:null}u(Ee,"repoOwner");function C(t,e,n,o){const s=[...new Map(n.flatMap(f=>{const i=Ee(t,f);return i?[[i.id,i]]:[]})).values()],a=e.class==="node"?e.cwd:o,r=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:s,dirs:[...new Set([...a!==void 0?[a]:[],...r])]}}u(C,"repoChange");function N(t){for(const e of t.repos)try{we(e,t.dirs)}catch(n){se({level:"warn",event:"repo_sync.failed",outcome:"failed",error:n,fields:{repo:e.name,step:"export",reason:n instanceof Error?n.message:String(n)}})}}u(N,"syncChanged");const Y=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function $e(t,e){if(t===void 0)return;if(!Array.isArray(t)){e.push("`rules` must be a list");return}const n=t.map(s=>{if(typeof s!="object"||s===null||Array.isArray(s))return s;const{match_frontmatter:a,...r}=s;return"matchFrontmatter"in r?{...r,match_frontmatter:r.matchFrontmatter}:a===void 0?r:{...r,matchFrontmatter:a}}),o=ve(n);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return n}u($e,"rulesFromDTO");function Z(t,e){const n={},o=u(i=>{const c=t[i];if(c!==void 0){if(typeof c!="string"){e.push(`\`${i}\` must be a string`);return}return c}},"str"),s=o("preview");s!==void 0&&(s.trim()===""||/[\r\n]/.test(s)?e.push("`preview` must be one non-empty line"):n.preview=s);const a=o("summary");a!==void 0&&(n.summary=a);const r=o("why_it_exists");if(r!==void 0&&(n.whyItExists=r),t.privacy!==void 0&&(U.includes(t.privacy)?n.privacy=t.privacy:e.push(`\`privacy\` must be one of ${U.join(", ")}`)),t.unlisted!==void 0&&(typeof t.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):n.unlisted=t.unlisted),t.lint_ignore!==void 0){const i=t.lint_ignore;!Array.isArray(i)||i.some(c=>typeof c!="string")?e.push("`lint_ignore` must be a list of strings"):n.lintIgnore=i}if(t.extensions!==void 0){const i=t.extensions;typeof i!="object"||i===null||Array.isArray(i)||Object.values(i).some(c=>typeof c!="object"||c===null||Array.isArray(c))?e.push("`extensions` must map each plugin to an object of fields"):n.extensions=i}const f=$e(t.rules,e);return f!==void 0&&(n.rules=f),n}u(Z,"fieldsFrom");function J(t){if(t.length>0)throw b(t.join("; "),{errors:t})}u(J,"failOn");function Q(t,e,n,o){const s=K(n).filter(a=>"remote"in X(t,e,a));return{...o.length>0?{unresolved_links:o}:{},...s.length>0?{remote_links:s}:{}}}u(Q,"linkReport");function Oe(t){const e=R(t),n=[],o=e.name;(typeof o!="string"||!Y.test(o))&&n.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&n.push("`body` must be a string"),typeof e.preview!="string"&&n.push("`preview` is required");const s=e.kind??"knowledge";q.includes(s)||n.push(s==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${q.join(", ")}`);const a=Z(e,n);e.cwd!==void 0&&typeof e.cwd!="string"&&n.push("`cwd` must be a string"),J(n);const r=e,{dto:f,change:i}=k(c=>{const h=E(c,t),d=M(c,h,r.owner,r.cwd);if(!V(c,h,d))throw new _(404,"not_found",`no owner ${d.name} you can write to`);try{const{object:p,unresolved:m}=he(c,h,{...a,owner:d,name:r.name,kind:s,preview:a.preview,privacy:a.privacy??"public"},r.body,W(c));return{dto:{id:p.id,name:j(c,p),...Q(c,h,r.body,m)},change:C(c,h,[p],r.cwd)}}catch(p){S(p)}});return N(i),f}u(Oe,"write");function Ae(t,e){let n=t;e.forEach(({old:o,new:s},a)=>{const r=o===""?-1:n.indexOf(o);if(r<0)throw v(422,"replace_no_match",`replace[${a}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:a});if(n.indexOf(o,r+1)>=0)throw v(422,"replace_ambiguous",`replace[${a}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:a});n=n.slice(0,r)+s+n.slice(r+o.length)})}u(Ae,"checkReplacements");function je(t){const e=R(t),n=[],o=e.body!==void 0,s=e.replace!==void 0;o&&s&&n.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&n.push("`body` must be a string");const a=e.replace;s&&(!Array.isArray(a)||a.length===0||a.some(d=>typeof d!="object"||d===null||typeof d.old!="string"||typeof d.new!="string"))&&n.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&n.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&n.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&n.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!s&&n.push("`base` applies only to a `body` or `replace` edit");const r=Z(e,n),f=e.unset??[];if(!Array.isArray(f)||f.some(d=>!H.includes(d)))n.push(`\`unset\` takes only ${H.join(", ")}`);else for(const d of f)d==="summary"&&(r.summary=null),d==="why_it_exists"&&(r.whyItExists=null),d==="unlisted"&&(r.unlisted=!1),d==="lint_ignore"&&(r.lintIgnore=null),d==="extensions"&&(r.extensions=null),d==="rules"&&(r.rules=[]);n.length===0&&!o&&!s&&Object.keys(r).length===0&&n.push("nothing to change: give `body`, `replace`, or a field"),J(n);const i=e,{dto:c,synced:h}=k(d=>{const p=E(d,t),m=x(d,p,t.params.ref),g=j(d,m),O=C(d,p,[m]);try{if(!o&&!s)return le(d,p,m,r),{dto:{id:m.id,name:g,revision:F(d,m).revision},synced:O};const w=i.base??me(d,p,m);if(s&&w!==null){const oe=F(d,m),B=I(d,m);oe.heads_key===w&&B.length===1&&Ae(pe(d,m,A(B[0].body_hash)),i.replace)}const D=i.verbatim===!0,l=o?{body:i.body,verbatim:D}:{replace:i.replace,verbatim:D},y=de(d,p,m,l,i.rationale,w,r,W(d));if("stale"in y)throw v(409,"stale_revision",`${g} changed after your read; it is now at revision ${y.stale.revision}`,`Read it again with \`crtr canvas read ${g}\`, then redo your change on the current text.`,{stale:y.stale});const T=A(I(d,m)[0].body_hash);return{dto:{id:m.id,name:g,revision:y.revision,...Q(d,p,T,y.unresolved)},synced:O}}catch(w){S(w,m)}});return N(h),c}u(je,"edit");function De(t){const e=R(t);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!Y.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");const{dto:n,change:o}=k(s=>{const a=E(s,t),r=x(s,a,t.params.ref),f=e.owner===void 0?void 0:M(s,a,e.owner,e.cwd);try{const i=fe(s,a,r,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}});return{dto:{id:i.id,name:j(s,i)},change:C(s,a,[r,i],e.cwd)}}catch(i){S(i,r)}});return N(o),n}u(De,"move");function Re(t){const{dto:e,change:n}=k(o=>{const s=E(o,t),a=x(o,s,t.params.ref),r=j(o,a),f=C(o,s,[a]);try{ce(o,s,a,W(o))}catch(i){S(i,a)}return{dto:{id:a.id,name:r},change:f}});return N(n),e}u(Re,"remove");function Se(t){const e=t.query.get("diff"),n=e===null?void 0:Number(e);if(n!==void 0&&!(Number.isInteger(n)&&n>0))throw b("`diff` must be a revision number");return k(o=>{const s=E(o,t),a=z(o,s,decodeURIComponent(t.params.ref));if("remote"in a)throw v(404,"not_found",G(a.remote),"Check the name with `crtr canvas search`.");const r="deleted"in a?a.deleted:a;if(r.type!=="document")throw b(`${r.name} is a ${r.type}, not a document`);const f=ue(o,s,r),i={object:ke(o,r),revisions:f.map(c=>({rev_id:c.rev_id,number:c.number,author_id:c.author,rationale:c.rationale,verbatim:c.verbatim,created_at:c.created_at,parents:c.parents}))};if(n!==void 0){const c=f.find(d=>d.number===n);if(!c)throw new _(404,"not_found",`${r.name} has no revision ${n}`);const h=c.parents[0]===void 0?void 0:f.find(d=>d.rev_id===c.parents[0]);i.diff=_e(h?A(h.body_hash):"",A(c.body_hash))}return i})}u(Se,"revisionHistory");const ee=1e3,ne=1e3,te=3e3,Ce=/^When\b.+\bbecause\b.+/,Ne=u(t=>{const e=t.trim();return e===""?0:e.split(/\s+/).length},"words");function Te(t,e){if(t.some(o=>o.on==="boot"&&o.deliver==="content")&&e>ee)return`body is ${e} words, over the ${ee}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const n=[...new Set(t.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return n.length>0&&e>ne?`body is ${e} words, over the ${ne}-word cap for a content rule on ${n.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:t.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>te?`body is ${e} words, over the ${te}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Te,"lengthFinding");function Fe(t){const e=R(t);return k(n=>{const o=E(n,t),s=e.owner===void 0?null:M(n,o,e.owner,e.cwd),a=P(n,"SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),r=[],f=new Map;for(const i of a){if(s&&i.owner!==s.rowid||!V(n,o,i))continue;const c=F(n,i),h=new Set(c.lint_ignore??[]),d=j(n,i),p=u((l,y,T)=>{h.has(l)||r.push({name:d,severity:y,rule:l,message:T})},"add"),m=ge(n,i),g=I(n,i)[0],O=g?A(g.body_hash):"",w=Te(m,Ne(O));w&&p("length","error",w),m.some(l=>l.deliver==="preview")&&!Ce.test(i.preview??"")&&p("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const D=new Set(P(n,"SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.rowid).map(l=>l.written_as));for(const l of K(O))D.has(l)||"remote"in X(n,o,l)||p("links","warning",`[[${l}]] names no object in your view`);if(m.some(l=>l.on==="workspace-open"&&l.deliver==="content")){const l=ae(n,i);l.type==="repo"&&!h.has("workspace-open")&&f.set(l.name,[...f.get(l.name)??[],d])}}for(const[i,c]of f)if(!(c.length<2))for(const h of c)r.push({name:h,severity:"warning",rule:"workspace-open",message:`repo ${i} has ${c.length} documents delivered at content on workspace-open (${c.join(", ")}); keep one and link the rest from it`});return{findings:r}})}u(Fe,"lint");const $=u((t,e=200)=>n=>({status:e,body:t(n)}),"ok"),Qe=[{method:"POST",pattern:"/v1/docs",handler:$(Oe,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:$(je)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:$(De)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:$(Re)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:$(Se)},{method:"POST",pattern:"/v1/docs/lint",handler:$(Fe)}];export{Qe as docRoutes};
|
|
1
|
+
var re=Object.defineProperty;var u=(t,e)=>re(t,"name",{value:e,configurable:!0});import{prepared as P}from"../../../core/sqlite-statements.js";import{ApiError as _}from"../../../api/errors.js";import{DOC_KINDS as q,DOC_UNSET_FIELDS as H}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as U}from"../../../api/dto/objects.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{CrtrError as ie}from"../../../core/errors.js";import{emitEvent as se}from"../../../core/events/emit.js";import{canChange as V,spaceOf as ae}from"../../../core/graph/access.js";import{get as A}from"../../../core/graph/bodies.js";import{deleteDocument as ce,documentRecord as F,editDocument as de,heads as I,history as ue,linksIn as K,moveDocument as fe,readBase as me,setDocumentRecord as le,withCurrentNames as pe,writeDocument as he}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getByRowid as x}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as we}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as ye}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ve,rulesOf as ge}from"../../../core/graph/rules.js";import{unifiedDiff as _e}from"../../../core/graph/diff.js";import{ownerFromFlag as L,readerFor as E,resolveRef as z}from"../reader.js";function v(t,e,n,o,s={}){return new _(t,e,n,{...s,error:e,message:n,next:o})}u(v,"refuse");const b=u((t,e)=>new _(400,"invalid_request",t,e),"invalid");function R(t){const e=t.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(R,"bodyOf");const M=u(t=>e=>ye(t,e),"reposFnFor");function S(t,e){if(t instanceof ie){const n=t.details;if(t.code==="read_first")throw v(409,"read_first",t.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,n??{});if(t.code==="conflict")throw new _(409,"name_taken",t.message,n);if(t.code==="forbidden")throw new _(403,"forbidden",t.message,n);if(t.code==="scope_missing")throw v(403,"scope_missing",t.message,`Launch the run with \`${String(n?.scope)}\` in its scopes list.`,n??{});if(n?.rule==="repo-public-only")throw v(422,"repo_documents_are_public",t.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw t}u(S,"mapGraphError");function W(t,e,n){const o=z(t,e,decodeURIComponent(n));if("remote"in o)throw v(404,"not_found",G(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new _(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${n} is a ${o.type}, not a document`);return o}u(W,"refDocument");function G(t){return`no object named ${t} on this runtime; it may be on another runtime, or the name may be misspelled`}u(G,"remoteMessage");function be(t,e){const n=e.owner===null?null:x(t,e.owner);return n?n.type==="node"?n.id:n.name:""}u(be,"ownerHandle");function j(t,e){const n=be(t,e);return n?`${n}/${e.name}`:e.name}u(j,"canonicalName");function ke(t,e){const n=e.owner===null?null:x(t,e.owner),o=n?{id:n.id,type:n.type,handle:n.type==="node"?n.id:n.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at}}u(ke,"objectDTO");function Ee(t,e){const n=e.owner===null?null:x(t,e.owner);return n?.type==="repo"?n:null}u(Ee,"repoOwner");function C(t,e,n,o){const s=[...new Map(n.flatMap(f=>{const i=Ee(t,f);return i?[[i.id,i]]:[]})).values()],a=e.class==="node"?e.cwd:o,r=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:s,dirs:[...new Set([...a!==void 0?[a]:[],...r])]}}u(C,"repoChange");function N(t){for(const e of t.repos)try{we(e,t.dirs)}catch(n){se({level:"warn",event:"repo_sync.failed",outcome:"failed",error:n,fields:{repo:e.name,step:"export",reason:n instanceof Error?n.message:String(n)}})}}u(N,"syncChanged");const Y=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function $e(t,e){if(t===void 0)return;if(!Array.isArray(t)){e.push("`rules` must be a list");return}const n=t.map(s=>{if(typeof s!="object"||s===null||Array.isArray(s))return s;const{match_frontmatter:a,...r}=s;return"matchFrontmatter"in r?{...r,match_frontmatter:r.matchFrontmatter}:a===void 0?r:{...r,matchFrontmatter:a}}),o=ve(n);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return n}u($e,"rulesFromDTO");function Z(t,e){const n={},o=u(i=>{const c=t[i];if(c!==void 0){if(typeof c!="string"){e.push(`\`${i}\` must be a string`);return}return c}},"str"),s=o("preview");s!==void 0&&(s.trim()===""||/[\r\n]/.test(s)?e.push("`preview` must be one non-empty line"):n.preview=s);const a=o("summary");a!==void 0&&(n.summary=a);const r=o("why_it_exists");if(r!==void 0&&(n.whyItExists=r),t.privacy!==void 0&&(U.includes(t.privacy)?n.privacy=t.privacy:e.push(`\`privacy\` must be one of ${U.join(", ")}`)),t.unlisted!==void 0&&(typeof t.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):n.unlisted=t.unlisted),t.lint_ignore!==void 0){const i=t.lint_ignore;!Array.isArray(i)||i.some(c=>typeof c!="string")?e.push("`lint_ignore` must be a list of strings"):n.lintIgnore=i}if(t.extensions!==void 0){const i=t.extensions;typeof i!="object"||i===null||Array.isArray(i)||Object.values(i).some(c=>typeof c!="object"||c===null||Array.isArray(c))?e.push("`extensions` must map each plugin to an object of fields"):n.extensions=i}const f=$e(t.rules,e);return f!==void 0&&(n.rules=f),n}u(Z,"fieldsFrom");function J(t){if(t.length>0)throw b(t.join("; "),{errors:t})}u(J,"failOn");function Q(t,e,n,o){const s=K(n).filter(a=>"remote"in X(t,e,a));return{...o.length>0?{unresolved_links:o}:{},...s.length>0?{remote_links:s}:{}}}u(Q,"linkReport");function Oe(t){const e=R(t),n=[],o=e.name;(typeof o!="string"||!Y.test(o))&&n.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&n.push("`body` must be a string"),typeof e.preview!="string"&&n.push("`preview` is required");const s=e.kind??"knowledge";q.includes(s)||n.push(s==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${q.join(", ")}`);const a=Z(e,n);e.cwd!==void 0&&typeof e.cwd!="string"&&n.push("`cwd` must be a string"),J(n);const r=e,{dto:f,change:i}=k(c=>{const h=E(c,t),d=L(c,h,r.owner,r.cwd);if(!V(c,h,d))throw new _(404,"not_found",`no owner ${d.name} you can write to`);try{const{object:p,unresolved:m}=he(c,h,{...a,owner:d,name:r.name,kind:s,preview:a.preview,privacy:a.privacy??"public"},r.body,M(c));return{dto:{id:p.id,name:j(c,p),...Q(c,h,r.body,m)},change:C(c,h,[p],r.cwd)}}catch(p){S(p)}});return N(i),f}u(Oe,"write");function Ae(t,e){let n=t;e.forEach(({old:o,new:s},a)=>{const r=o===""?-1:n.indexOf(o);if(r<0)throw v(422,"replace_no_match",`replace[${a}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:a});if(n.indexOf(o,r+1)>=0)throw v(422,"replace_ambiguous",`replace[${a}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:a});n=n.slice(0,r)+s+n.slice(r+o.length)})}u(Ae,"checkReplacements");function je(t){const e=R(t),n=[],o=e.body!==void 0,s=e.replace!==void 0;o&&s&&n.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&n.push("`body` must be a string");const a=e.replace;s&&(!Array.isArray(a)||a.length===0||a.some(d=>typeof d!="object"||d===null||typeof d.old!="string"||typeof d.new!="string"))&&n.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&n.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&n.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&n.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!s&&n.push("`base` applies only to a `body` or `replace` edit");const r=Z(e,n),f=e.unset??[];if(!Array.isArray(f)||f.some(d=>!H.includes(d)))n.push(`\`unset\` takes only ${H.join(", ")}`);else for(const d of f)d==="summary"&&(r.summary=null),d==="why_it_exists"&&(r.whyItExists=null),d==="unlisted"&&(r.unlisted=!1),d==="lint_ignore"&&(r.lintIgnore=null),d==="extensions"&&(r.extensions=null),d==="rules"&&(r.rules=[]);n.length===0&&!o&&!s&&Object.keys(r).length===0&&n.push("nothing to change: give `body`, `replace`, or a field"),J(n);const i=e,{dto:c,synced:h}=k(d=>{const p=E(d,t),m=W(d,p,t.params.ref),g=j(d,m),O=C(d,p,[m]);try{if(!o&&!s)return le(d,p,m,r),{dto:{id:m.id,name:g,revision:F(d,m).revision},synced:O};const w=i.base??me(d,p,m);if(s&&w!==null){const oe=F(d,m),B=I(d,m);oe.heads_key===w&&B.length===1&&Ae(pe(d,m,A(B[0].body_hash)),i.replace)}const D=i.verbatim===!0,l=o?{body:i.body,verbatim:D}:{replace:i.replace,verbatim:D},y=de(d,p,m,l,i.rationale,w,r,M(d));if("stale"in y)throw v(409,"stale_revision",`${g} changed after your read; it is now at revision ${y.stale.revision}`,`Read it again with \`crtr canvas read ${g}\`, then redo your change on the current text.`,{stale:y.stale});const T=A(I(d,m)[0].body_hash);return{dto:{id:m.id,name:g,revision:y.revision,...Q(d,p,T,y.unresolved)},synced:O}}catch(w){S(w,m)}});return N(h),c}u(je,"edit");function De(t){const e=R(t);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!Y.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");const{dto:n,change:o}=k(s=>{const a=E(s,t),r=W(s,a,t.params.ref),f=e.owner===void 0?void 0:L(s,a,e.owner,e.cwd);try{const i=fe(s,a,r,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}});return{dto:{id:i.id,name:j(s,i)},change:C(s,a,[r,i],e.cwd)}}catch(i){S(i,r)}});return N(o),n}u(De,"move");function Re(t){const{dto:e,change:n}=k(o=>{const s=E(o,t),a=W(o,s,t.params.ref),r=j(o,a),f=C(o,s,[a]);try{ce(o,s,a,M(o))}catch(i){S(i,a)}return{dto:{id:a.id,name:r},change:f}});return N(n),e}u(Re,"remove");function Se(t){const e=t.query.get("diff"),n=e===null?void 0:Number(e);if(n!==void 0&&!(Number.isInteger(n)&&n>0))throw b("`diff` must be a revision number");return k(o=>{const s=E(o,t),a=z(o,s,decodeURIComponent(t.params.ref));if("remote"in a)throw v(404,"not_found",G(a.remote),"Check the name with `crtr canvas search`.");const r="deleted"in a?a.deleted:a;if(r.type!=="document")throw b(`${r.name} is a ${r.type}, not a document`);const f=ue(o,s,r),i={object:ke(o,r),revisions:f.map(c=>({rev_id:c.rev_id,number:c.number,author_id:c.author,rationale:c.rationale,verbatim:c.verbatim,created_at:c.created_at,parents:c.parents}))};if(n!==void 0){const c=f.find(d=>d.number===n);if(!c)throw new _(404,"not_found",`${r.name} has no revision ${n}`);const h=c.parents[0]===void 0?void 0:f.find(d=>d.rev_id===c.parents[0]);i.diff=_e(h?A(h.body_hash):"",A(c.body_hash))}return i})}u(Se,"revisionHistory");const ee=1e3,ne=1e3,te=3e3,Ce=/^When\b.+\bbecause\b.+/,Ne=u(t=>{const e=t.trim();return e===""?0:e.split(/\s+/).length},"words");function Te(t,e){if(t.some(o=>o.on==="boot"&&o.deliver==="content")&&e>ee)return`body is ${e} words, over the ${ee}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const n=[...new Set(t.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return n.length>0&&e>ne?`body is ${e} words, over the ${ne}-word cap for a content rule on ${n.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:t.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>te?`body is ${e} words, over the ${te}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Te,"lengthFinding");function Fe(t){const e=R(t);return k(n=>{const o=E(n,t),s=e.owner===void 0?null:L(n,o,e.owner,e.cwd),a=P(n,"SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),r=[],f=new Map;for(const i of a){if(s&&i.owner!==s.rowid||!V(n,o,i))continue;const c=F(n,i),h=new Set(c.lint_ignore??[]),d=j(n,i),p=u((l,y,T)=>{h.has(l)||r.push({name:d,severity:y,rule:l,message:T})},"add"),m=ge(n,i),g=I(n,i)[0],O=g?A(g.body_hash):"",w=Te(m,Ne(O));w&&p("length","error",w),m.some(l=>l.deliver==="preview")&&!Ce.test(i.preview??"")&&p("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const D=new Set(P(n,"SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.rowid).map(l=>l.written_as));for(const l of K(O))D.has(l)||"remote"in X(n,o,l)||p("links","warning",`[[${l}]] names no object in your view`);if(m.some(l=>l.on==="workspace-open"&&l.deliver==="content")){const l=ae(n,i);l.type==="repo"&&!h.has("workspace-open")&&f.set(l.name,[...f.get(l.name)??[],d])}}for(const[i,c]of f)if(!(c.length<2))for(const h of c)r.push({name:h,severity:"warning",rule:"workspace-open",message:`repo ${i} has ${c.length} documents delivered at content on workspace-open (${c.join(", ")}); keep one and link the rest from it`});return{findings:r}})}u(Fe,"lint");const $=u((t,e=200)=>n=>({status:e,body:t(n)}),"ok"),Qe=[{method:"POST",pattern:"/v1/docs",handler:$(Oe,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:$(je)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:$(De)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:$(Re)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:$(Se)},{method:"POST",pattern:"/v1/docs/lint",handler:$(Fe)}];export{j as canonicalName,Qe as docRoutes,C as repoChange,N as syncChanged};
|