@north-light/cli 0.3.535 → 0.3.537
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 +3 -0
- 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/recovery.d.ts +2 -1
- package/dist/clients/attach/viewer.js +406 -406
- package/dist/commands/api-client.js +3 -3
- package/dist/core/canvas/canvas.d.ts +10 -0
- package/dist/core/canvas/canvas.js +20 -20
- package/dist/core/canvas/crons.d.ts +4 -2
- package/dist/core/canvas/crons.js +1 -1
- package/dist/core/canvas/paths.d.ts +5 -0
- package/dist/core/canvas/paths.js +1 -1
- package/dist/core/canvas/recovery.d.ts +7 -0
- package/dist/core/canvas/recovery.js +2 -2
- package/dist/core/feed/feed.js +3 -3
- package/dist/core/feed/messages.d.ts +6 -0
- package/dist/core/feed/messages.js +5 -5
- package/dist/core/graph/kinds/node.js +5 -5
- package/dist/core/model-auth/flows.js +1 -1
- package/dist/core/providers/mint.d.ts +2 -0
- package/dist/core/providers/mint.js +1 -1
- package/dist/core/runtime/broker/cli-relay.js +1 -1
- package/dist/core/runtime/broker/fault-retry.d.ts +12 -0
- package/dist/core/runtime/broker/fault-retry.js +1 -1
- package/dist/core/runtime/broker/run-turn-end.d.ts +3 -2
- package/dist/core/runtime/broker/run-turn-end.js +1 -1
- package/dist/core/runtime/lifecycle.d.ts +4 -0
- package/dist/core/runtime/lifecycle.js +2 -2
- package/dist/core/runtime/nodes.js +1 -1
- package/dist/core/runtime/revive.d.ts +1 -1
- package/dist/core/runtime/revive.js +1 -1
- package/dist/core/worktree-mutation-async.d.ts +10 -0
- package/dist/core/worktree-mutation-async.js +5 -5
- package/dist/core/worktree-sweep.js +6 -6
- package/dist/core/worktree.d.ts +1 -0
- package/dist/core/worktree.js +1 -1
- package/dist/daemon/api/handlers/broker-recovery.js +1 -1
- package/dist/daemon/api/handlers/messages.js +2 -2
- package/dist/daemon/api/vendor-signin.js +20 -20
- package/dist/daemon/messaging/node-message.js +1 -1
- package/dist/daemon/peer/forward.js +1 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { type ManagedWorktree } from './canvas/index.js';
|
|
2
2
|
import { type AbandonManagedWorktreeResult, type AutoDroppedWorktree } from './worktree.js';
|
|
3
|
+
/** Run `admit` — the step that persists a node whose cwd is `cwd` — under the
|
|
4
|
+
* repository lock the cleanup sweep removes checkouts under, when `cwd` is in
|
|
5
|
+
* another node's managed checkout. The repository comes from that owner's
|
|
6
|
+
* recorded managed worktree, not from the checkout on disk, so the lock is
|
|
7
|
+
* still taken after the sweep has removed the checkout. The sweep decides to
|
|
8
|
+
* remove a checkout and removes it inside that lock, and a node admitted inside
|
|
9
|
+
* the same lock is either seen by that decision or finds the checkout already
|
|
10
|
+
* gone and is refused here, instead of being persisted with a cwd that no
|
|
11
|
+
* longer exists. */
|
|
12
|
+
export declare function admitIntoManagedCheckout<T>(cwd: string, nodeId: string, admit: () => Promise<T>): Promise<T>;
|
|
3
13
|
export declare function rollbackManagedWorktreeAsync(wt: ManagedWorktree): Promise<void>;
|
|
4
14
|
export declare function createManagedWorktreeAsync(cwd: string, nodeId: string, baseRef?: string): Promise<ManagedWorktree>;
|
|
5
15
|
export interface AbsentWorktreeConclusion {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var
|
|
2
|
-
stderr: ${n.stderr.trim()||"<empty>"}`);return h(e,["rev-parse","--verify",
|
|
3
|
-
`).map(l=>l.trim()).includes(s)))return;
|
|
1
|
+
var O=Object.defineProperty;var o=(e,a)=>O(e,"name",{value:a,configurable:!0});import{existsSync as p,mkdirSync as B,readFileSync as q,realpathSync as I,rmSync as N,writeFileSync as j}from"node:fs";import{basename as G,dirname as K,isAbsolute as z,join as x,relative as J,resolve as Q,sep as E}from"node:path";import{abandonWorktreeAndRepoint as U,getNode as m,readWorktree as V}from"./canvas/index.js";import{isPidAlive as X}from"./canvas/pid.js";import{InputError as Y}from"./io.js";import{CONFIG_FILE as Z,CRTR_DIR_NAME as S,STATE_FILE as ee}from"../types.js";import{withExclusiveDirectoryLockAsync as W}from"./exclusive-lock.js";import{gitAsync as i}from"./git.js";import{WORKTREE_LOCK_WAIT_MS as te,canonicalPath as D,checkoutPathForBranchInList as re,isManagedWorktreeBase as ae,managedWorktreesRoot as ne,managedWorktreePath as oe,persistClosedWorktreeState as k,worktreeLockPathForCommonDir as M,WorktreeError as c}from"./worktree.js";import{proveBranchContained as ie}from"./worktree-containment.js";import{refDeleteTransactionStdin as se}from"./worktree-landing.js";import{deleteTrashAsync as ce,removeWorktreeCheckoutAsync as he}from"./worktree-remove.js";const C=o(()=>Math.min(te,25e3),"lockWaitMs");function v(e){return(e.stderr.trim()||e.stdout.trim()).trim()}o(v,"detailOf");async function h(e,a,r,n){const t=await i(a,e);if(t.status!==0)throw new c(r,`git ${a.join(" ")} failed`,n,v(t));return t.stdout.trim()}o(h,"runGit");async function L(e){return I(Q(e,await h(e,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.")))}o(L,"commonGitDir");async function _(e,a){const r=await L(e);return W(M(r),a,{timeoutMs:C(),timeoutError:o(()=>new c("worktree_lock_timeout",`timed out waiting for the managed-worktree lock in ${r}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(_,"withRepositoryLock");function ue(e,a){const r=D(ne()),n=J(r,D(e));if(n===""||n===".."||n.startsWith(`..${E}`)||z(n))return null;const t=n.split(E)[0];return t===a?null:x(r,t)}o(ue,"foreignManagedCheckout");async function Me(e,a,r){const n=ue(e,a);if(n===null)return r();const t=await V(G(n));return t===null?r():_(t.repo_root,async()=>{if(!p(e))throw new Y({error:"cwd_missing",message:`cwd is not an existing directory: ${e}`,received:e,field:"cwd",next:"Pass an existing directory; the managed checkout it was in has been cleaned up."});return r()})}o(Me,"admitIntoManagedCheckout");async function $(e,a){const r=`refs/heads/${a}`,n=await i(["show-ref","--verify","--quiet",r],e);if(n.status===1&&n.executionFailed!==!0)throw new c("missing_base_branch",`managed worktree base '${a}' does not name an existing local branch`,"The base must be a local branch that landing can fast-forward \u2014 freshen your local branch from origin first (rebase-not-merge), then base on it.");if(n.status!==0)throw new c("base_branch_inspection_failed",`could not inspect local base branch '${a}' (git show-ref exited with status ${n.status})`,"Inspect the repository and its Git metadata, then retry.",`status: ${n.status}
|
|
2
|
+
stderr: ${n.stderr.trim()||"<empty>"}`);return h(e,["rev-parse","--verify",r],"base_branch_resolve_failed","Inspect the local base branch, then retry.")}o($,"resolveLandableBase");function le(e,a){try{const r=x(a,"info","exclude");B(K(r),{recursive:!0});let n="";try{n=q(r,"utf8")}catch{}const t=[`/${S}/${Z}`,`/${S}/${ee}`];if(t.every(s=>n.split(`
|
|
3
|
+
`).map(l=>l.trim()).includes(s)))return;j(r,`${n}${n!==""&&!n.endsWith(`
|
|
4
4
|
`)?`
|
|
5
5
|
`:""}# crtr: exclude crtr's own untracked project-scope bookkeeping (auto-added at managed-worktree creation)
|
|
6
6
|
${t.filter(s=>!n.split(`
|
|
7
7
|
`).map(l=>l.trim()).includes(s)).join(`
|
|
8
8
|
`)}
|
|
9
|
-
`)}catch{}}o(
|
|
10
|
-
`).some(n=>n.trim()===`branch refs/heads/${e.branch}`)){if((await i(["worktree","remove","--force",e.path],e.repo_root)).status!==0)return}else if(p(e.path))try{
|
|
9
|
+
`)}catch{}}o(le,"seedExclude");async function Ce(e){await _(e.repo_root,async()=>{const a=await i(["rev-parse","--verify",`refs/heads/${e.branch}`],e.repo_root);if(a.status!==0||a.stdout.trim()!==e.base_sha)return;const r=await i(["worktree","list","--porcelain"],e.repo_root);if(r.status===0){if(r.stdout.split(`
|
|
10
|
+
`).some(n=>n.trim()===`branch refs/heads/${e.branch}`)){if((await i(["worktree","remove","--force",e.path],e.repo_root)).status!==0)return}else if(p(e.path))try{N(e.path,{recursive:!0,force:!0})}catch{return}await i(["update-ref","-d",`refs/heads/${e.branch}`,e.base_sha],e.repo_root)}})}o(Ce,"rollbackManagedWorktreeAsync");async function Le(e,a,r="main"){const n=await h(e,["rev-parse","--show-toplevel"],"not_git_repo","Pass --cwd <real repo> pointing inside a git repository.");if(ae(n))throw new c("managed_worktree_base",`cwd ${n} is a managed worktree and cannot be used as a managed worktree base`,"Pass `--cwd` pointing at the repository\u2019s primary checkout and keep `--worktree` set to the desired existing local base branch, including the parent branch.");const t=await L(n);return W(M(t),async()=>{const s=await $(n,r),l=oe(a),u=`crtr/${a}`;if(p(l))throw new c("worktree_path_exists",`managed worktree path already exists: ${l}`,"Remove the stale path or choose a fresh node id.");if((await i(["show-ref","--verify","--quiet",`refs/heads/${u}`],n)).status===0)throw new c("worktree_branch_exists",`managed worktree branch already exists: ${u}`,"Remove the stale branch or choose a fresh node id.");const b={state:"open",path:l,branch:u,repo_root:n,base_ref:r,base_sha:s,created:new Date().toISOString()};await h(n,["branch",u,s],"worktree_branch_create_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.");const f=await i(["worktree","add",l,u],n);if(f.status!==0)throw await i(["update-ref","-d",`refs/heads/${u}`,s],n),new c("worktree_add_failed","git worktree add failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.",v(f));return le(n,t),b},{timeoutMs:C(),timeoutError:o(()=>new c("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(Le,"createManagedWorktreeAsync");async function P(e){if(e.state!=="open"||!p(e.path))return null;const a=await i(["rev-parse","--abbrev-ref","HEAD"],e.path),r=await i(["status","--porcelain"],e.path),n=await i(["rev-parse","HEAD"],e.path);if(a.status!==0||a.stdout.trim()!==e.branch||r.status!==0||r.stdout.trim()!==""||n.status!==0)return null;const t=await i(["rev-parse","--git-path","rebase-merge"],e.path);return t.status!==0||p(t.stdout.trim())?null:n.stdout.trim()}o(P,"cleanTip");async function F(e,a){try{const r=await $(e.path,e.base_ref);return(await i(["merge-base","--is-ancestor",a,r],e.path)).status===0}catch(r){if(r instanceof c&&r.code==="missing_base_branch"&&a===e.base_sha)return!0;throw r}}o(F,"containedInLocalBase");function de(e){return{node_id:"",branch:e.branch,worktree_path:e.path,worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${e.path} remains intact.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${e.branch} remains checked out at ${e.path}.`}}o(de,"deferred");async function R(e){if(e.state!=="open"||p(e.path))return null;const a=`refs/heads/${e.branch}`,r=await i(["show-ref","--verify","--quiet",a],e.repo_root);if(r.status===1&&r.executionFailed!==!0)return{branchSha:null};if(r.status!==0)return null;const n=await i(["rev-parse","--verify",a],e.repo_root);if(n.status!==0)return null;const t=n.stdout.trim(),s=await i(["worktree","list","--porcelain"],e.repo_root);if(s.status!==0||re(s.stdout,e.branch)!==null)return null;const l=await ie(e.repo_root,e,t);return l===null?null:{branchSha:t,containment:l}}o(R,"proveAbsentPathManagedWorktree");async function pe(e,a,r){return p(a.path)?null:r.branchSha===null?(await k(e,a,"complete"),{node_id:e,branch:a.branch,worktree_path:a.path,worktree_removed:!0,branch_deleted:!0}):r.containment===void 0?null:(await k(e,a,"complete"),{node_id:e,branch:a.branch,worktree_path:a.path,worktree_removed:!0,branch_deleted:!1,branch_delete_error:`local branch ${a.branch} is delivered but retained because its checkout status can change outside crtr's repository lock.`})}o(pe,"autoDropAbsentPathManagedWorktreeLocked");async function fe(e){const a=(await m(e))?.managed_worktree;return a==null||a.state!=="open"||p(a.path)||await R(a)===null?null:_(a.repo_root,async()=>{const r=(await m(e))?.managed_worktree;if(r==null||r.state!=="open")return null;const n=await R(r);if(n===null)return null;const t=await pe(e,r,n);return t===null?null:{dropped:t,landedSha:n.branchSha??r.base_sha}})}o(fe,"concludeAbsentManagedWorktreeAsync");async function Pe(e){const a=(await m(e))?.managed_worktree;if(a==null||a.state!=="open")return null;if(!p(a.path))return(await fe(e))?.dropped??null;const r=await P(a);return r===null||!await F(a,r)?null:_(a.path,async()=>{const n=(await m(e))?.managed_worktree;if(n==null||n.state!=="open")return null;const t=await P(n);return t===null||t!==r||!await F(n,t)?null:(await k(e,n,"pending"),{...de(n),node_id:e})})}o(Pe,"autoDropCleanManagedWorktreeAsync");async function T(e){for(const a of["rebase-merge","rebase-apply"]){const r=await i(["rev-parse","--git-path",a],e);if(r.status!==0||p(r.stdout.trim()))return!0}return!1}o(T,"rebaseInProgress");async function y(e,a,r,n,t,s=a.branch){await U(e,{cwd:a.repo_root,abandoned:{branch:s,tip_sha:r,at:n,by:t}})}o(y,"persistAbandoned");async function Fe(e,a){const r=(await m(e))?.managed_worktree;if(r==null||r.state!=="open")throw new c("no_managed_worktree",`node ${e} has no open managed worktree`,"Nothing to abandon for this node.");const n=[];try{return await _(r.path,()=>we(e,a,n))}finally{await ce(r.path,n[0])}}o(Fe,"abandonManagedWorktreeAsync");async function g(e,a){const{result:r,trash:n}=await he(e.repo_root,e.path);if(n!==void 0&&a.push(n),r.status!==0)throw new c("worktree_remove_failed",`git worktree remove ${e.path} failed`,"Inspect the worktree manually, then retry.",v(r))}o(g,"removeCheckout");async function we(e,a,r){const n=await m(e),t=n?.managed_worktree;if(n===null||t===void 0||t===null||t.state!=="open")throw new c("no_managed_worktree",`node ${e} has no open managed worktree`,"Nothing to abandon for this node.");if(n.status==="active"||n.status==="idle"||X(n.pi_pid))throw new c("node_running",`node ${e} is still running`,"Abandon requires a stopped node with no live broker process. From the running owner, use `crtr node worktree close` to land locally, or make the checkout clean, push its current tip to origin, and rerun `crtr push final`.");if(!p(t.path))throw new c("worktree_missing",`managed worktree path is missing: ${t.path}`,"Inspect the branch and worktree registration, then retry.");const s=await h(t.path,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry."),l=await i(["show-ref","--verify","--quiet",`refs/heads/${t.branch}`],t.path);if(s!==t.branch){if(l.status===0||s==="HEAD")throw new c("wrong_branch",`managed worktree is not on its recorded branch (expected ${t.branch}, found ${s==="HEAD"?"detached HEAD":s})`,`Inspect ${t.path}, then rerun \`crtr node worktree abandon\`.`);if(await T(t.path)||await h(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")!=="")throw new c("dirty_worktree","managed worktree has uncommitted changes",`Commit or stash them in ${t.path}, then rerun \`crtr node worktree abandon\`.`);const d=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the worktree manually, then retry.");await g(t,r);const w=new Date().toISOString();return await y(e,t,d,w,a,s),{node_id:e,branch:s,tip_sha:d,worktree_path:t.path,branch_deleted:!1}}const u=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the branch manually, then retry.");let b=null;try{const d=await $(t.path,t.base_ref);(await i(["merge-base","--is-ancestor",u,d],t.path)).status===0&&(b=d)}catch(d){if(!(d instanceof c)||d.code!=="missing_base_branch")throw d}const f=new Date().toISOString(),H=await h(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.");if(b!==null&&u===t.base_sha){const d=await h(t.repo_root,["rev-parse",`${u}^{tree}`],"abandon_tree_failed","Inspect the branch manually, then retry."),w=await h(t.repo_root,["-c","user.name=crtr-abandon","-c","user.email=crtr-abandon@localhost","commit-tree",d,"-p",u,"-m","crtr-abandon","-m",`Crtr-Abandon: ${e} ${f}`],"abandon_commit_failed","Inspect the branch manually, then retry.");return await h(t.repo_root,["update-ref",`refs/heads/${t.branch}`,w,u],"abandon_marker_failed","The branch changed while abandon was starting; inspect the worktree, then retry."),await g(t,r),(await i(["update-ref","--stdin"],t.repo_root,se(t.branch,w,`refs/heads/${t.base_ref}`,b))).status===0?(await k(e,t,"complete"),{node_id:e,branch:t.branch,tip_sha:w,worktree_path:t.path,branch_deleted:!0}):(await y(e,t,w,f,a),{node_id:e,branch:t.branch,tip_sha:w,worktree_path:t.path,branch_deleted:!1})}if(!await T(t.path)&&H==="")return await g(t,r),await y(e,t,u,f,a),{node_id:e,branch:t.branch,tip_sha:u,worktree_path:t.path,branch_deleted:!1};await h(t.path,["add","--all"],"abandon_stage_failed","Inspect the worktree manually, then retry."),await h(t.path,["-c","user.name=crtr-abandon","-c","user.email=crtr-abandon@localhost","commit","--allow-empty","-m","crtr-abandon","-m",`Crtr-Abandon: ${e} ${f}`],"abandon_commit_failed","Inspect the worktree manually, then retry.");const A=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the branch manually, then retry.");return await g(t,r),await y(e,t,A,f,a),{node_id:e,branch:t.branch,tip_sha:A,worktree_path:t.path,branch_deleted:!1}}o(we,"abandonLocked");export{Fe as abandonManagedWorktreeAsync,Me as admitIntoManagedCheckout,Pe as autoDropCleanManagedWorktreeAsync,fe as concludeAbsentManagedWorktreeAsync,Le as createManagedWorktreeAsync,Ce as rollbackManagedWorktreeAsync};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var q=Object.defineProperty;var
|
|
2
|
-
`))if(e.startsWith("worktree "))return e.slice(9).trim();return null}
|
|
3
|
-
`))if(s.startsWith("worktree "))a=s.slice(9).trim();else if(a!==null&&s===`branch refs/heads/${r}`&&
|
|
4
|
-
${
|
|
5
|
-
`).map(p=>p.trim()).filter(p=>p!==""),
|
|
6
|
-
${k}`}}
|
|
1
|
+
var q=Object.defineProperty;var c=(t,e)=>q(t,"name",{value:e,configurable:!0});import{createHash as z}from"node:crypto";import{existsSync as w,realpathSync as Q}from"node:fs";import{isAbsolute as V,relative as j,resolve as v,sep as G}from"node:path";import{completeCleanup as J,getNode as E,recordSweepAttempt as X,resumableNodeCwds as Y}from"./canvas/index.js";import{withExclusiveDirectoryLockAsync as Z}from"./exclusive-lock.js";import{gitAsync as d}from"./git.js";import{proveBranchContained as D,upstreamTip as I}from"./worktree-containment.js";import{parseStatusPorcelain as tt,planLandIntoCheckout as et,refDeleteTransactionStdin as at}from"./worktree-landing.js";import{shellQuote as rt}from"../shared/shell-quote.js";import{deleteTrashAsync as st,removeWorktreeCheckoutAsync as B}from"./worktree-remove.js";import{WORKTREE_LOCK_WAIT_MS as nt,canonicalPath as L,checkoutPathForBranchInList as M,samePath as C,worktreeLockPathForCommonDir as it}from"./worktree.js";const A=[2,5,15,30,60],ot=A.length;function S(t){return t==null||t.state==="abandoned"?!1:t.state==="open"||t.cleanup!=="complete"}c(S,"isReconcilable");function Ht(t,e){const r=t.sweep?.next_attempt;if(r===void 0)return!0;const a=Date.parse(r);return Number.isNaN(a)||a<=e}c(Ht,"isDueForSweep");async function m(t,e){const r=await d(["rev-parse","--verify","--quiet",e],t);return r.status===0?r.stdout.trim():null}c(m,"revParseRef");function h(t){return(t.stderr.trim()||t.stdout.trim()).trim()}c(h,"detailOf");async function F(t){const e=await d(["rev-parse","--git-common-dir"],t);if(e.status!==0)return null;try{return Q(v(t,e.stdout.trim()))}catch{return null}}c(F,"commonGitDirAsync");async function lt(t){for(const e of["rebase-merge","rebase-apply"]){const r=await d(["rev-parse","--git-path",e],t);if(r.status!==0)return null;if(w(v(t,r.stdout.trim())))return!0}return!1}c(lt,"isRebaseInProgressAsync");function ut(t){for(const e of t.split(`
|
|
2
|
+
`))if(e.startsWith("worktree "))return e.slice(9).trim();return null}c(ut,"mainWorktreePath");function ct(t,e,r){let a=null;for(const s of t.split(`
|
|
3
|
+
`))if(s.startsWith("worktree "))a=s.slice(9).trim();else if(a!==null&&s===`branch refs/heads/${r}`&&C(a,e))return a;return null}c(ct,"registeredCheckoutPath");async function dt(t){const e=w(t.path),r=e?t.path:t.repo_root,a=await m(r,`refs/heads/${t.base_ref}`),s=await I(r,t.base_ref),n=await m(r,`refs/heads/${t.branch}`);if(!e)return{base:a,upstream:s?.sha??null,branch:n,checkout:"absent"};const i=await d(["rev-parse","HEAD"],t.path),o=await d(["status","--porcelain"],t.path),l=i.status!==0||o.status!==0?null:z("sha256").update(`${i.stdout.trim()}
|
|
4
|
+
${o.stdout}`).digest("hex");return{base:a,upstream:s?.sha??null,branch:n,checkout:l}}c(dt,"observe");function ft(t,e){return t.checkout===null||e.checkout===null?!1:t.base===e.base&&t.upstream===e.upstream&&t.branch===e.branch&&t.checkout===e.checkout}c(ft,"sameObservation");async function ht(t,e,r,a,s,n){if(r.status!=="refused"&&r.status!=="failed"||!await n.stillEligible())return;const i=(e.sweep?.attempts??0)+1,o=A[Math.min(i,A.length)-1]??A[0],l=(await E(t))?.managed_worktree;l==null||!S(l)||await X(t,{reason:r.reason,...r.detail===void 0?{}:{detail:r.detail},observed:a,attempts:i,lastAttemptAt:new Date(s).toISOString(),nextAttemptAt:new Date(s+o*6e4).toISOString(),quarantined:i>=ot})}c(ht,"recordUnfinished");async function pt(t,e,r,a,s){const n=await d(["status","--porcelain"],t);if(n.status!==0)return{ok:!1,detail:h(n)};const i=await d(["diff","--name-only",r,a],t);if(i.status!==0)return{ok:!1,detail:h(i)};const o=i.stdout.split(`
|
|
5
|
+
`).map(p=>p.trim()).filter(p=>p!==""),l=et(tt(n.stdout),o);if(l.kind==="refuse")return{ok:!1,detail:`untracked files in ${t} would be overwritten by the incoming commits: ${l.collisions.join(", ")}`};if(!await s.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const u=c(async()=>{const p=await d(["merge","--ff-only",a],t);return p.status===0?{ok:!0}:{ok:!1,detail:h(p)}},"merge");if(l.kind==="direct")return u();const b=await m(t,"refs/stash"),g=await d(["stash","push","--message",`crtr landing ${e}`],t);if(g.status!==0)return{ok:!1,detail:h(g)};const f=await m(t,"refs/stash");if(f===null||f===b)return u();const k=`local changes from ${t} are retained in stash ${f}; recover them with \`git -C ${rt(t)} stash apply ${f}\` and resolve any conflict.`,_=await u();return _.ok?(await d(["stash","apply",f],t)).status===0?(await U(t,f),{ok:!0}):(await d(["reset","--hard",a],t),{ok:!0,stash:`the shared base checkout was reset to landed commit ${a} because local changes conflicted with it. ${k}`}):(await d(["stash","apply",f],t)).status===0?(await U(t,f),_):{ok:!1,detail:`${_.detail}
|
|
6
|
+
${k}`}}c(pt,"landIntoBaseCheckoutAsync");async function U(t,e){await m(t,"refs/stash")===e&&await d(["stash","drop"],t)}c(U,"dropStashIfStillTip");async function mt(t,e,r){const a=await m(t.path,`refs/heads/${t.base_ref}`);if(a===null)return{ok:!1,detail:`local base branch '${t.base_ref}' does not resolve`};if(!await r.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const s=await d(["rebase",a],t.path);if(s.status!==0)return await d(["rebase","--abort"],t.path),{ok:!1,detail:h(s)};const n=await m(t.path,"HEAD");if(n===null)return{ok:!1,detail:"could not read the rebased worktree tip"};if((await d(["merge-base","--is-ancestor",a,n],t.path)).status!==0)return{ok:!1,detail:`the rebased branch is not a fast-forward of '${t.base_ref}'`};const i=M(e,t.base_ref);if(i===null){if(!await r.stillEligible())return{ok:!1,detail:"node is no longer terminal",ineligible:!0};const u=await d(["update-ref",`refs/heads/${t.base_ref}`,n,a],t.path);return u.status===0?{ok:!0,landedSha:n}:{ok:!1,detail:h(u)}}const o=await d(["rev-parse","HEAD"],i);if(o.status!==0||o.stdout.trim()!==a)return{ok:!1,detail:`base checkout ${i} is not at ${a}`};const l=await pt(i,t.branch,a,n,r);return l.ok?{ok:!0,landedSha:n,...l.stash===void 0?{}:{stash:l.stash}}:l}c(mt,"finishBlockedLand");const $={status:"skipped",reason:"node_no_longer_terminal"},x={status:"skipped",reason:"checkout_in_use"};function bt(t,e){const r=j(t,e);return r===""||r!==".."&&!r.startsWith(`..${G}`)&&!V(r)}c(bt,"isWithinDirectory");async function P(t,e){const r=[v(e),L(e)];return(await Y(t)).some(({cwd:a})=>{const s=[v(a),L(a)];return r.some(n=>s.some(i=>bt(n,i)))})}c(P,"checkoutHasDependents");async function Nt(t,e,r=Date.now()){const a=(await E(t))?.managed_worktree;if(!S(a)||a==null)return{status:"skipped",reason:"already_reconciled"};if(w(a.path)&&await P(t,a.path))return x;const s=w(a.path)?a.path:a.repo_root;if(!w(s))return await T(t,a,{status:"refused",reason:"repo_unavailable",detail:`neither the recorded checkout nor repository root exists, so ${a.branch} cannot be proved absent`},{base:null,upstream:null,branch:null,checkout:null},r,e);const n=await F(s);if(n===null)return await T(t,a,{status:"failed",reason:"git_dir_unreadable",detail:`could not resolve the git common dir from ${s}`},{base:null,upstream:null,branch:null,checkout:null},r,e);let i={base:null,upstream:null,branch:null,checkout:null},o;const l=[];try{o=await Z(it(n),async()=>{if(!await e.stillEligible())return $;const u=(await E(t))?.managed_worktree;return!S(u)||u==null?{status:"skipped",reason:"already_reconciled"}:(i=await dt(u),u.sweep!==void 0&&ft(u.sweep.observed,i)?{status:"refused",reason:u.sweep.reason,...u.sweep.detail===void 0?{}:{detail:u.sweep.detail},unchanged:!0}:w(u.path)?await _t(t,u,e,l):await kt(t,u,i,e))},{timeoutMs:nt,timeoutError:c(()=>new Error(`timed out waiting for the managed-worktree lock on ${n}`),"timeoutError")})}catch(u){o={status:"failed",reason:"lock_unavailable",detail:u instanceof Error?u.message:String(u)}}return await st(a.path,l[0]),await T(t,a,o,i,r,e)}c(Nt,"reconcileManagedWorktree");async function T(t,e,r,a,s,n){return await ht(t,e,r,a,s,n),r}c(T,"finish");async function H(t,e,r){if(!await r.stillEligible())return $;const a=(await E(t))?.managed_worktree;return a==null||a.path!==e.path||a.branch!==e.branch?{status:"skipped",reason:"record_changed"}:(await J(t),{status:"complete"})}c(H,"persistComplete");async function kt(t,e,r,a){const s=e.repo_root;if(r.branch===null){const l=await d(["show-ref","--verify","--quiet",`refs/heads/${e.branch}`],s);return l.status!==1?{status:"failed",reason:"branch_inspection_failed",detail:h(l)}:await H(t,e,a)}const n=await d(["worktree","list","--porcelain"],s);if(n.status!==0)return{status:"failed",reason:"worktree_list_failed",detail:h(n)};const i=M(n.stdout,e.branch);if(i!==null)return{status:"refused",reason:"branch_checked_out_elsewhere",detail:`${e.branch} is checked out at ${i}`};const o=await D(s,e,r.branch);return o===null?{status:"refused",reason:"unlanded_work",detail:`${e.branch} carries content no base or upstream ref contains; its checkout is already gone, so the branch is the only copy`}:K(t,e,s,r.branch,o,a)}c(kt,"concludeAbsentCheckout");async function _t(t,e,r,a){const s=await d(["worktree","list","--porcelain"],e.path);if(s.status!==0)return{status:"failed",reason:"worktree_list_failed",detail:h(s)};const n=e.disposal,i=n?.observed_branch??e.branch,o=ct(s.stdout,e.path,i);if(o===null)return{status:"refused",reason:"not_registered",detail:`${e.path} is not registered to refs/heads/${i}; crtr will not act on a path it cannot prove it owns`};const l=ut(s.stdout);if(l===null||C(l,o)||!w(l))return{status:"refused",reason:"repo_unavailable",detail:`the repository's main checkout is unavailable, so ${e.path} cannot be removed from outside itself`};const u=await lt(e.path);if(u===null)return{status:"failed",reason:"git_path_failed",detail:`could not inspect in-progress operations in ${e.path}`};if(u)return{status:"refused",reason:"rebase_in_progress",detail:`${e.path} is mid-rebase`};const b=await d(["rev-parse","--abbrev-ref","HEAD"],e.path);if(b.status!==0)return{status:"failed",reason:"branch_check_failed",detail:h(b)};if(b.stdout.trim()!==i)return{status:"refused",reason:"wrong_branch",detail:`expected ${i}, found ${b.stdout.trim()==="HEAD"?"detached HEAD":b.stdout.trim()}`};const g=await d(["status","--porcelain"],e.path);if(g.status!==0)return{status:"failed",reason:"status_failed",detail:h(g)};if(g.stdout.trim()!=="")return{status:"refused",reason:"dirty_worktree",detail:`${e.path} has uncommitted changes`};let f=await m(e.path,"HEAD");if(f===null)return{status:"failed",reason:"head_unreadable",detail:`could not read HEAD in ${e.path}`};if(n!==void 0){if(await F(e.path)!==n.common_dir||f!==n.observed_sha)return{status:"refused",reason:"disposal_authorization_stale",detail:`${e.path} changed since \`close\` authorized its disposal; rerun \`crtr node worktree close\` from that node to re-authorize, or ask the user with \`crtr human send\``};if(!await r.stillEligible())return $;if(await P(t,o))return x;const{result:W,trash:R}=await B(l,o);return R!==void 0&&a.push(R),W.status!==0?{status:"refused",reason:"worktree_remove_failed",detail:h(W)}:await H(t,e,r)}let k=await D(e.path,e,f),_;if(k===null&&e.land_intent!==void 0){const y=await mt(e,s.stdout,r);if(!y.ok)return y.ineligible===!0?$:{status:"refused",reason:"land_blocked",detail:y.detail};f=y.landedSha,_=y.stash,k=await D(e.path,e,f)}if(k===null)return{status:"refused",reason:"unlanded_work",detail:`${e.branch} carries commits its base does not contain, and this node never asked to land them`};if(!await r.stillEligible())return $;if(await P(t,o))return x;const{result:p,trash:N}=await B(l,o);if(N!==void 0&&a.push(N),p.status!==0)return{status:"refused",reason:"worktree_remove_failed",detail:h(p)};const O=await K(t,e,l,f,k,r);return O.status==="complete"&&_!==void 0?{status:"complete",stash:_}:O}c(_t,"reconcilePresentCheckout");async function K(t,e,r,a,s,n){if(!await n.stillEligible())return $;const i=at(e.branch,a,s.containingRef,s.containingSha),o=await d(["update-ref","--stdin"],r,i);return o.status!==0?{status:"refused",reason:"branch_delete_failed",detail:h(o)}:await H(t,e,n)}c(K,"deleteBranchAndComplete");export{Ht as isDueForSweep,S as isReconcilable,Nt as reconcileManagedWorktree};
|
package/dist/core/worktree.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export declare function checkoutPathForBranchInList(porcelain: string, branch: s
|
|
|
30
30
|
* failures fall back to `resolve`, which is the pre-canonical comparison — no
|
|
31
31
|
* worse than the string compare it replaces. */
|
|
32
32
|
export declare function samePath(a: string, b: string): boolean;
|
|
33
|
+
export declare function canonicalPath(path: string): string;
|
|
33
34
|
export declare const WORKTREE_LOCK_WAIT_MS = 30000;
|
|
34
35
|
/** The lock directory naming one repository's common Git dir. Every process
|
|
35
36
|
* that mutates a managed worktree — the synchronous commands here and the
|
package/dist/core/worktree.js
CHANGED
|
@@ -15,4 +15,4 @@ stderr: ${a}${s}`)}return _(e,["rev-parse","--verify",t],"base_branch_resolve_fa
|
|
|
15
15
|
`;re(r,t+u)}catch{}}o(We,"seedManagedWorktreeExclude");function Qe(e,r,t="main"){const n=we(e);if(ye(n))throw new f("managed_worktree_base",`cwd ${n} is a managed worktree and cannot be used as a managed worktree base`,"Pass `--cwd` pointing at the repository\u2019s primary checkout and keep `--worktree` set to the desired existing local base branch, including the parent branch.");return B(n,()=>{const a=R(n,t),s=ge(r),u=`crtr/${r}`;if(k(s))throw new f("worktree_path_exists",`managed worktree path already exists: ${s}`,"Remove the stale path or choose a fresh node id.");if(i(["show-ref","--verify","--quiet",`refs/heads/${u}`],n).status===0)throw new f("worktree_branch_exists",`managed worktree branch already exists: ${u}`,"Remove the stale branch or choose a fresh node id.");const h={state:"open",path:s,branch:u,repo_root:n,base_ref:t,base_sha:a,created:new Date().toISOString()};_(n,["branch",u,a],"worktree_branch_create_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.");try{return _(n,["worktree","add",s,u],"worktree_add_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree."),We(n),h}catch(m){throw K(h,!0),m}})}o(Qe,"createManagedWorktree");function Ae(e){return _(e,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")}o(Ae,"statusPorcelain");function U(e){return _(e,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")}o(U,"currentBranch");function X(e,r){return _(e,["rev-parse","--git-path",r],"git_path_failed","Inspect the worktree manually, then retry.")}o(X,"gitPath");function M(e){return k(X(e,"rebase-merge"))||k(X(e,"rebase-apply"))}o(M,"isRebaseInProgress");async function $(e,r,t="pending"){const n=await v(e);await ue(e,{cwd:r.repo_root,cleanup:t,closedAt:new Date().toISOString(),unbindSession:n?.pi_session_id!=null||n?.pi_session_file!=null})}o($,"persistClosedWorktreeState");async function Re(e,r){const t=(await v(e))?.managed_worktree;t==null||t.state!=="open"||await le(e,{at:new Date().toISOString(),blocked:r})}o(Re,"recordBlockedLandIntent");function Me(e,r,t,n){const a=i(["status","--porcelain"],e);if(a.status!==0)return{ok:!1,detail:(a.stderr.trim()||a.stdout.trim()).trim()};const s=i(["diff","--name-only",t,n],e);if(s.status!==0)return{ok:!1,detail:(s.stderr.trim()||s.stdout.trim()).trim()};const u=s.stdout.split(`
|
|
16
16
|
`).map(g=>g.trim()).filter(g=>g!==""),h=ke(be(a.stdout),u);if(h.kind==="refuse")return{ok:!1,detail:`untracked files in ${e} would be overwritten by the incoming commits: ${h.collisions.join(", ")}`};const m=o(()=>{const g=i(["merge","--ff-only",n],e);return g.status===0?{ok:!0}:{ok:!1,detail:(g.stderr.trim()||g.stdout.trim()).trim()}},"merge");if(h.kind==="direct")return m();const d=i(["rev-parse","--verify","--quiet","refs/stash"],e),l=d.status===0?d.stdout.trim():null,b=i(["stash","push","--message",`crtr landing ${r}`],e);if(b.status!==0)return{ok:!1,detail:(b.stderr.trim()||b.stdout.trim()).trim()};const c=i(["rev-parse","--verify","--quiet","refs/stash"],e),p=c.status===0?c.stdout.trim():null;if(p===null||p===l)return m();const y=`local changes from ${e} are retained in stash ${p}; recover them with \`git -C ${w(e)} stash apply ${p}\` and resolve any conflict.`,W=m();return W.ok?i(["stash","apply",p],e).status===0?(z(e,p),{ok:!0}):(i(["reset","--hard",n],e),{ok:!0,stash:`the shared base checkout was reset to landed commit ${n} because local changes conflicted with it. ${y}`}):i(["stash","apply",p],e).status===0?(z(e,p),W):{ok:!1,detail:`${W.detail}
|
|
17
17
|
${y}`}}o(Me,"landIntoBaseCheckout");function z(e,r){const t=i(["rev-parse","--verify","--quiet","refs/stash"],e);t.status===0&&t.stdout.trim()===r&&i(["stash","drop"],e)}o(z,"dropStashIfStillTip");function L(e){const r=`git -C ${w(e.repo_root)} worktree remove ${w(e.path)}`;return{worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${e.path} remains intact. After its node exits, run \`${r}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${e.branch} remains checked out at ${e.path}; delete it only after that checkout is removed and its landed commit is still contained in ${e.base_ref}.`}}o(L,"deferredCleanupResult");async function Le(e,r,t){if(D(r.repo_root,r.path,t)!==!0)throw new f("wrong_branch",`managed worktree's recorded branch ${r.branch} no longer exists, and its checkout at ${r.path} is not registered to ${t} either`,`Inspect ${r.path} and its git worktree registration, then ask the user with \`crtr human send\` if you are unsure how to proceed.`);const n=_(r.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.");if(Ee(r.path,r.base_ref,n)===null)throw new f("branch_moved_unlanded",`managed worktree's recorded branch ${r.branch} no longer exists; its checkout is now on ${t} at ${n}, which carries content local base branch '${r.base_ref}' does not yet contain`,`Land or push ${t} through its own workflow (merge its PR, or fast-forward '${r.base_ref}' to include it) before closing, or ask the user with \`crtr human send\` if you are unsure how ${t} should be delivered.`);const s=S(r.repo_root),u=D(r.repo_root,r.path,t)===!0,h=U(r.path)===t,m=_(r.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")===n;if(!u||!h||!m)throw new f("wrong_branch",`managed worktree at ${r.path} changed while closing`,`Inspect ${r.path}, then rerun \`crtr node worktree close\`.`);const d={observed_branch:t,observed_sha:n,common_dir:s,authorized:new Date().toISOString()};await $(e,{...r,disposal:d},"pending");const l=`git -C ${w(r.repo_root)} worktree remove ${w(r.path)}`;return{node_id:e,branch:t,worktree_path:r.path,landed_sha:n,worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${r.path} remains intact. After its node exits, run \`${l}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`${t} is not a crtr-managed branch and will never be deleted automatically; it is preserved once the checkout above is removed.`}}o(Le,"closeBranchMovedWorktree");async function Te(e,r){const t=(await v(e))?.managed_worktree;if(t==null)return{message:`node ${e} has no managed worktree`,next:`Nothing to ${r} for this node.`};if(t.state==="abandoned"){const n=t.abandoned?.branch??t.branch;return{message:`node ${e}'s managed worktree was already abandoned`,next:`Nothing to ${r} for this node; its work was retained on branch ${n}.`}}return t.state==="closed"?t.cleanup==="complete"?{message:`node ${e}'s managed worktree is already closed and its cleanup is complete`,next:`Nothing to ${r} for this node.`}:{message:`node ${e}'s managed worktree is already closed; this is not lost ownership \u2014 its checkout at ${t.path} is scheduled for automatic cleanup once that is safe`,next:`Nothing to ${r} for this node. The daemon sweep finishes removing ${t.path} on its own; inspect it directly only if it still needs manual attention.`}:{message:`node ${e} has no open managed worktree`,next:`Nothing to ${r} for this node.`}}o(Te,"noManagedWorktreeMessage");async function Q(e){if(await v(e)===null)throw new f("unknown_node",`unknown node: ${e}`,"Run this from a live node, or pass a real node id once that command supports it.");const t=await x(e);if(t===null){const{message:c,next:p}=await Te(e,"close");throw new f("no_managed_worktree",c,p)}if(!k(t.path)){const c=T(t);if(c===null)throw new f("worktree_missing",`managed worktree path is missing: ${t.path}`,J(t));if(await V(e,t,c)===null)throw new f("worktree_missing",`managed worktree path is missing: ${t.path}`,J(t));return{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:c.branchSha??t.base_sha,worktree_removed:!0,branch_deleted:!0}}if(M(t.path))throw new f("rebase_in_progress","this managed worktree is mid-rebase",`Resolve conflicts in ${t.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`.`);if(Ae(t.path)!=="")throw new f("dirty_worktree","managed worktree has uncommitted changes",`Commit the work in ${t.path} (or ask the user with \`crtr human send\` if you do not know whether to keep it), then rerun \`crtr node worktree close\`.`);const a=U(t.path);if(a!==t.branch){const c=a==="HEAD"?"detached HEAD":a,p=$e(t.path,t.branch);if(p||a==="HEAD")throw new f("wrong_branch",`managed worktree is not on its recorded branch (expected ${t.branch}, found ${c})`,p?`Check out ${t.branch} in ${t.path} before closing, or ask the user with \`crtr human send\` if you are unsure why it changed, then rerun \`crtr node worktree close\`.`:`The recorded branch ${t.branch} no longer exists, and ${t.path} is in detached HEAD, so there is no branch to preserve. Check out or create a branch there before closing, or ask the user with \`crtr human send\` if you are unsure how to proceed.`);return await Le(e,t,a)}let s;try{s=R(t.path,t.base_ref)}catch(c){const p=_(t.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`.");if(!(c instanceof f)||c.code!=="missing_base_branch"||p!==t.base_sha)throw c;return await $(e,t,"pending"),{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:t.base_sha,...L(t)}}const u=i(["rebase",s],t.path);if(u.status!==0){const c=(u.stderr.trim()||u.stdout.trim()).trim();throw new f("rebase_failed",`rebase onto local base branch '${t.base_ref}' did not complete`,`Resolve conflicts in ${t.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`. If the blocker is not mechanical, ask the user with \`crtr human send\`.`,c)}const h=_(t.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`."),m=o(async(c,p,y)=>(await Re(e,c),new f("land_base_failed",c,p,y)),"blocked"),d=i(["merge-base","--is-ancestor",s,h],t.path);if(d.status!==0)throw await m(`rebased worktree is not a fast-forward of local base branch '${t.base_ref}'`,"Ask the user with `crtr human send` how to land this managed worktree.",(d.stderr.trim()||d.stdout.trim()).trim());const l=ve(t.path,t.base_ref);if(l!==null){const c=i(["rev-parse","HEAD"],l),p=c.stdout.trim();if(c.status!==0||p!==s){const y=c.status!==0?(c.stderr.trim()||p).trim():`expected ${s}, found ${p}`;throw await m(`local base branch '${t.base_ref}' changed while this worktree was closing`,`Reconcile the base checkout at ${l}, then rerun \`crtr node worktree close\`. If you are unsure, ask the user with \`crtr human send\`.`,y)}}let b;if(l!==null){const c=Me(l,t.branch,s,h);if(!c.ok)throw await m(`could not fast-forward local base branch '${t.base_ref}' to this worktree`,`Local base branch '${t.base_ref}' is checked out at ${l} in a state that blocks a fast-forward. Reconcile it there, then rerun \`crtr node worktree close\`. If you are unsure, ask the user with \`crtr human send\`.`,c.detail);b=c.stash}else{const c=i(["update-ref",`refs/heads/${t.base_ref}`,h,s],t.path);if(c.status!==0)throw await m(`could not fast-forward local base branch '${t.base_ref}' to this worktree`,"Ask the user with `crtr human send` how to land this managed worktree.",(c.stderr.trim()||c.stdout.trim()).trim())}return await $(e,t,"pending"),{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:h,...L(t),...b===void 0?{}:{base_checkout_stash:b}}}o(Q,"closeManagedWorktreeLocked");async function Je(e){const r=await x(e);return r===null?await Q(e):await j(k(r.path)?r.path:r.repo_root,async()=>await Q(e))}o(Je,"closeManagedWorktree");function Pe(e,r,t,n){const a=_e(e.branch,r,t,n),s=i(["update-ref","--stdin"],e.repo_root,a),u=s.status===0;if(u)return{branchDeleted:u};const h=(s.stderr.trim()||s.stdout.trim()).trim();return{branchDeleted:u,branchDeleteError:`local branch ${e.branch} could not be deleted automatically (${h}); run \`git -C ${w(e.repo_root)} branch -D ${w(e.branch)}\` after confirming it is merged.`}}o(Pe,"bestEffortDeleteBranchWithContainmentProof");function T(e){if(e.state!=="open"||k(e.path))return null;const r=`refs/heads/${e.branch}`,t=i(["show-ref","--verify","--quiet",r],e.repo_root);if(t.status===1)return{branchSha:null};if(t.status!==0)return null;const n=i(["rev-parse","--verify",r],e.repo_root);if(n.status!==0)return null;const a=n.stdout.trim(),s=i(["worktree","list","--porcelain"],e.repo_root);if(s.status!==0||I(s.stdout,e.branch)!==null)return null;const u=[],h=`refs/heads/${e.base_ref}`,m=i(["show-ref","--verify","--quiet",h],e.repo_root);if(m.status===0){const l=i(["rev-parse","--verify",h],e.repo_root);if(l.status!==0)return null;u.push({ref:h,sha:l.stdout.trim()})}else if(m.status!==1)return null;const d=i(["rev-parse","--symbolic-full-name","--verify",`${e.base_ref}@{upstream}`],e.repo_root);if(d.status===0){const l=d.stdout.trim(),b=i(["rev-parse","--verify",l],e.repo_root);l.startsWith("refs/")&&b.status===0&&u.push({ref:l,sha:b.stdout.trim()})}else{const l=`refs/remotes/origin/${e.base_ref}`,b=i(["show-ref","--verify","--quiet",l],e.repo_root);if(b.status===0){const c=i(["rev-parse","--verify",l],e.repo_root);c.status===0&&u.push({ref:l,sha:c.stdout.trim()})}else if(b.status!==1)return null}for(const l of u)if(i(["merge-base","--is-ancestor",a,l.sha],e.repo_root).status===0)return{branchSha:a,containingRef:l.ref,containingSha:l.sha};return null}o(T,"proveAbsentPathManagedWorktree");function J(e){return`The recorded checkout path is gone, but crtr cannot prove ${e.branch} is already delivered. Push or land ${e.branch}, then rerun \`crtr push final\`; if you cannot establish what happened to the branch, ask the user with \`crtr human send\`.`}o(J,"missingWorktreeRecoveryHint");function P(e){if(e.state!=="open"||!k(e.path)||M(e.path))return null;const r=i(["rev-parse","--abbrev-ref","HEAD"],e.path);if(r.status!==0||r.stdout.trim()!==e.branch)return null;const t=i(["status","--porcelain"],e.path);if(t.status!==0||t.stdout.trim()!=="")return null;const n=i(["rev-parse","HEAD"],e.path);if(n.status!==0)return null;const a=n.stdout.trim();let s;try{s=R(e.path,e.base_ref)}catch(h){if(h instanceof f&&h.code==="missing_base_branch"&&a===e.base_sha)return{branchSha:a,baseSha:e.base_sha};throw h}return i(["merge-base","--is-ancestor",a,s],e.path).status!==0?null:{branchSha:a,baseSha:s}}o(P,"proveCleanEmptyManagedWorktree");async function V(e,r,t){if(k(r.path))return null;if(t.branchSha===null)return await $(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0};if(t.containingRef===void 0||t.containingSha===void 0)return null;const{branchDeleted:n}=Pe(r,t.branchSha,t.containingRef,t.containingSha);return n?(await $(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0}):null}o(V,"autoDropAbsentPathManagedWorktreeLocked");async function He(e,r,t){const n=P(r);return n===null||n.branchSha!==t.branchSha?null:(await $(e,r,"pending"),{node_id:e,branch:r.branch,worktree_path:r.path,...L(r)})}o(He,"autoDropCleanManagedWorktreeLocked");function Y(e){if(e.state!=="open"||!k(e.path)||M(e.path))return null;const r=i(["status","--porcelain"],e.path);if(r.status!==0||r.stdout.trim()!=="")return null;const t=i(["rev-parse","HEAD"],e.path);return t.status===0?t.stdout.trim():null}o(Y,"cleanCurrentCheckoutTip");async function Ve(e){const r=Y(e);if(r===null)return!1;const t=process.env.GIT_SSH_COMMAND??process.env.GIT_SSH??"ssh",n=await he(["ls-remote","--exit-code","--heads","origin"],e.path,void 0,{timeoutMs:1e4,env:{...process.env,GIT_TERMINAL_PROMPT:"0",GIT_SSH_COMMAND:`${t} -o BatchMode=yes`}});return n.status!==0||!n.stdout.split(`
|
|
18
|
-
`).some(s=>{const[u,h]=s.split(" ",2);return u===r&&h?.startsWith("refs/heads/")===!0})?!1:Y(e)===r}o(Ve,"branchIsPushedAtCurrentTip");async function Ye(e){const r=await x(e);if(r===null)return null;const t=!k(r.path);return(t?T(r)===null:P(r)===null)?null:await j(t?r.repo_root:r.path,async()=>{const n=await x(e);if(n===null)return null;if(!k(n.path)){const s=T(n);return s===null?null:await V(e,n,s)}const a=P(n);return a===null?null:await He(e,n,a)})}o(Ye,"autoDropCleanManagedWorktree");export{q as WORKTREE_LOCK_WAIT_MS,f as WorktreeError,Ye as autoDropCleanManagedWorktree,He as autoDropCleanManagedWorktreeLocked,Ve as branchIsPushedAtCurrentTip,I as checkoutPathForBranchInList,Je as closeManagedWorktree,Qe as createManagedWorktree,Xe as hasOpenManagedWorktree,ye as isManagedWorktreeBase,M as isRebaseInProgress,ge as managedWorktreePath,F as managedWorktreesRoot,J as missingWorktreeRecoveryHint,x as openManagedWorktreeForNode,$ as persistClosedWorktreeState,P as proveCleanEmptyManagedWorktree,ze as rollbackManagedWorktree,xe as samePath,G as worktreeLockPathForCommonDir};
|
|
18
|
+
`).some(s=>{const[u,h]=s.split(" ",2);return u===r&&h?.startsWith("refs/heads/")===!0})?!1:Y(e)===r}o(Ve,"branchIsPushedAtCurrentTip");async function Ye(e){const r=await x(e);if(r===null)return null;const t=!k(r.path);return(t?T(r)===null:P(r)===null)?null:await j(t?r.repo_root:r.path,async()=>{const n=await x(e);if(n===null)return null;if(!k(n.path)){const s=T(n);return s===null?null:await V(e,n,s)}const a=P(n);return a===null?null:await He(e,n,a)})}o(Ye,"autoDropCleanManagedWorktree");export{q as WORKTREE_LOCK_WAIT_MS,f as WorktreeError,Ye as autoDropCleanManagedWorktree,He as autoDropCleanManagedWorktreeLocked,Ve as branchIsPushedAtCurrentTip,E as canonicalPath,I as checkoutPathForBranchInList,Je as closeManagedWorktree,Qe as createManagedWorktree,Xe as hasOpenManagedWorktree,ye as isManagedWorktreeBase,M as isRebaseInProgress,ge as managedWorktreePath,F as managedWorktreesRoot,J as missingWorktreeRecoveryHint,x as openManagedWorktreeForNode,$ as persistClosedWorktreeState,P as proveCleanEmptyManagedWorktree,ze as rollbackManagedWorktree,xe as samePath,G as worktreeLockPathForCommonDir};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var k=Object.defineProperty;var o=(
|
|
1
|
+
var k=Object.defineProperty;var o=(t,e)=>k(t,"name",{value:e,configurable:!0});import{ApiError as E}from"../../../api/errors.js";import{getNode as I}from"../../../core/canvas/canvas.js";import{liveExecutionForNode as S}from"../../../core/canvas/executions.js";import{admitEpisode as T,clearEpisode as q,clearFault as m,closeTurn as R,invalidateEpisode as P,openTurn as h,parseProviderError as F,readEpisode as A,readFault as _,recordFault as v,recordPendingEpisode as N,recordTransportPoisonVerdict as j,recordTurnProgress as O,reopenEpisode as L,settleRejectedEpisode as D}from"../../../core/canvas/recovery.js";import{notFound as B,usage as n}from"../../../core/errors.js";import{hasPresentedViewer as $}from"../viewer-presence.js";import{ownerOnly as b}from"../router.js";const y=new Set(["pi\u2192provider","viewer\u2194broker","relay\u2194broker","viewer\u2194crtrd","daemon\u2192node","crtr\u2192pi"]),C=new Set(["rate-limit","overloaded","connection","auth","protocol","context-overflow","other","wedged","model-not-found"]),K=new Set(["auto","manual","fatal"]),U=new Set(["sdk","daemon","client"]);function d(t,e){if(typeof t!="object"||t===null||Array.isArray(t))throw n(`${e} requires a JSON object`);return t}o(d,"objectBody");async function a(t){if(await I(t)===null)throw B(`unknown node: ${t}`,{received:t})}o(a,"requireNode");async function u(t,e){if(typeof e!="string"||e==="")throw n("expected_execution_id must be a non-empty string");const r=await S(t);if(r===null||r.executionId!==e)throw new E(409,"stale_execution","broker execution is no longer current")}o(u,"requireCurrentExecution");function g(t){if(typeof t!="object"||t===null||Array.isArray(t))throw n("retry must be an object");const e=t;if(!K.has(e.disposition))throw n("retry.disposition must be auto, manual, or fatal");if(e.by!==void 0&&!U.has(e.by))throw n("retry.by must be sdk, daemon, or client");for(const r of["attempt","max"])if(e[r]!==void 0&&(!Number.isSafeInteger(e[r])||e[r]<0))throw n(`retry.${r} must be a non-negative safe integer`);if(e.nextAt!==void 0&&typeof e.nextAt!="string")throw n("retry.nextAt must be a string");return e}o(g,"parseRetry");function c(t){const e=d(t,"fault input");if(!y.has(e.link))throw n("fault link is invalid");if(typeof e.op!="string"||e.op==="")throw n("fault op must be a non-empty string");if(!C.has(e.kind))throw n("fault kind is invalid");if(typeof e.message!="string")throw n("fault message must be a string");if(e.operation_id!==void 0&&(typeof e.operation_id!="string"||e.operation_id===""))throw n("operation_id must be a non-empty string");if(e.since!==void 0&&typeof e.since!="string")throw n("since must be a string");if(e.anchor_entry_id!==void 0&&(typeof e.anchor_entry_id!="string"||e.anchor_entry_id===""))throw n("anchor_entry_id must be a non-empty string");const r=e.provider_error===void 0?void 0:F(e.provider_error);if(e.provider_error!==void 0&&r===void 0)throw n("provider_error is invalid");return{...e,...r===void 0?{}:{provider_error:r},retry:g(e.retry)}}o(c,"parseFaultInput");function p(t){return{link:t.link,op:t.op,kind:t.kind,retry:t.retry,message:t.message,...t.operation_id===void 0?{}:{operation_id:t.operation_id},...t.since===void 0?{}:{since:t.since},...t.anchor_entry_id===void 0?{}:{anchorEntryId:t.anchor_entry_id},...t.provider_error===void 0?{}:{providerError:t.provider_error}}}o(p,"faultInput");function l(t){return{fault_id:t.faultId,link:t.link,op:t.op,kind:t.kind,retry:t.retry,message:t.message,since:t.since,operation_id:t.operation_id,...t.anchorEntryId===void 0?{}:{anchor_entry_id:t.anchorEntryId},...t.providerError===void 0?{}:{provider_error:t.providerError}}}o(l,"faultDto");async function x(t){const e=await _(t),r=await A(t);return{fault:e===null?null:l(e),episode:r===null?null:{state:r.state,session_id:r.sessionId,fault:l(r.fault)}}}o(x,"recoveryState");function V(t){const e=d(t,"broker turn"),r=e.event;if(r!=="started"&&r!=="progress"&&r!=="closed"&&r!=="interrupted"&&r!=="exit-diagnostic")throw n("event is invalid");if(typeof e.expected_execution_id!="string"||e.expected_execution_id==="")throw n("expected_execution_id must be a non-empty string");if(r==="closed"){if(e.end_reason!=="settled"&&e.end_reason!=="teardown-aborted")throw n("closed turn requires end_reason settled or teardown-aborted")}else if(e.end_reason!==void 0)throw n("end_reason is valid only for closed turns");if(r==="exit-diagnostic"){const i=d(e.exit_diagnostic,"exit_diagnostic"),s=d(i.transport_poison,"exit_diagnostic.transport_poison");if(i.schema!=="crtr.execution-exit-diagnostic/v1"||!Number.isSafeInteger(s.consecutive_passes)||s.consecutive_passes<1||!Array.isArray(s.routes)||s.routes.length===0||!s.routes.every(f=>typeof f=="string"&&f!==""))throw n("exit_diagnostic is invalid")}else if(e.exit_diagnostic!==void 0)throw n("exit_diagnostic is valid only for exit-diagnostic turns");return e}o(V,"parseTurn");async function Y(t){const e=t.params.id;await a(e);const r=V(t.body);await u(e,r.expected_execution_id);let i;switch(r.event){case"started":i=await h(r.expected_execution_id,{reason:"agent-start"});break;case"interrupted":i=await h(r.expected_execution_id,{reason:"prompt-unsettled"});break;case"progress":i=await O(r.expected_execution_id);break;case"closed":i=await R(r.expected_execution_id,{endReason:r.end_reason,viewerPresent:$(e)});break;case"exit-diagnostic":{const w=r.exit_diagnostic.transport_poison;i=await j(r.expected_execution_id,{consecutivePasses:w.consecutive_passes,routes:w.routes});break}}const s=(await _(e))?.link;return{status:200,body:{applied:i,fault_link:s==="daemon\u2192node"||s==="pi\u2192provider"?s:null}}}o(Y,"handleTurn");function G(t){const e=d(t,"provider retry");if(typeof e.expected_execution_id!="string"||e.expected_execution_id==="")throw n("expected_execution_id must be a non-empty string");if(!["record-pending","admit","reopen","invalidate","clear","settle-rejected"].includes(e.op))throw n("provider retry op is invalid");if(typeof e.session_id!="string"||e.session_id==="")throw n("session_id must be a non-empty string");if(e.op==="record-pending"&&(e.input=c(e.input)),e.op==="settle-rejected"){if(typeof e.fault_id!="string"||e.fault_id==="")throw n("settle-rejected requires fault_id");if(typeof e.reopen!="boolean")throw n("settle-rejected requires reopen");e.input=c(e.input)}if(e.op==="admit"){if(typeof e.fault_id!="string"||e.fault_id==="")throw n("admit requires fault_id");e.retry=g(e.retry)}return e}o(G,"parseProviderRetry");async function J(t){const e=t.params.id;await a(e);const r=G(t.body);await u(e,r.expected_execution_id);let i;switch(r.op){case"record-pending":i=await N(e,{sessionId:r.session_id,input:p(r.input)})!==null;break;case"admit":i=await T(e,{sessionId:r.session_id,faultId:r.fault_id,retry:r.retry});break;case"settle-rejected":i=await D(e,{sessionId:r.session_id,faultId:r.fault_id,fault:p(r.input),reopen:r.reopen});break;case"reopen":i=await L(e,{sessionId:r.session_id});break;case"invalidate":i=await P(e,{sessionId:r.session_id});break;case"clear":i=await q(e,{sessionId:r.session_id});break}return{status:200,body:{applied:i,episode:(await x(e)).episode}}}o(J,"handleProviderRetry");function W(t){const e=d(t,"broker fault");if(typeof e.expected_execution_id!="string"||e.expected_execution_id==="")throw n("expected_execution_id must be a non-empty string");if(e.op!=="record"&&e.op!=="clear")throw n("fault op must be record or clear");if(e.op==="record"&&(e.input=c(e.input)),e.op==="clear"&&e.link!==void 0&&!y.has(e.link))throw n("fault link is invalid");if(e.preserve_episode!==void 0&&typeof e.preserve_episode!="boolean")throw n("preserve_episode must be boolean");return e}o(W,"parseBrokerFault");async function z(t){const e=t.params.id;await a(e);const r=W(t.body);await u(e,r.expected_execution_id);const i=r.op==="record"?await v(e,p(r.input)):(await m(e,{...r.link===void 0?{}:{link:r.link},...r.preserve_episode===void 0?{}:{preserveEpisode:r.preserve_episode}}),await _(e));return{status:200,body:{fault:i===null?null:l(i)}}}o(z,"handleBrokerFault");async function H(t){const e=t.params.id;return await a(e),await u(e,t.query.get("expected_execution_id")),{status:200,body:await x(e)}}o(H,"handleRecovery");async function M(t){const e=t.params.id;await a(e);const r=await v(e,p(c(t.body)));return{status:200,body:{fault:r===null?null:l(r)}}}o(M,"handleNodeFaultRecord");async function Q(t){const e=t.params.id;await a(e);const r=t.query.get("link");if(r!==null&&!y.has(r))throw n("fault link is invalid");const i=t.query.get("preserve_episode");if(i!==null&&i!=="true"&&i!=="false")throw n("preserve_episode must be true or false");return{status:200,body:{cleared:await m(e,{...r===null?{}:{link:r},...i===null?{}:{preserveEpisode:i==="true"}})}}}o(Q,"handleNodeFaultClear");const se=b([{method:"POST",pattern:"/v1/broker/:id/turn",handler:Y},{method:"POST",pattern:"/v1/broker/:id/provider-retry",handler:J},{method:"POST",pattern:"/v1/broker/:id/fault",handler:z},{method:"GET",pattern:"/v1/broker/:id/recovery",handler:H}]),de=b([{method:"POST",pattern:"/v1/nodes/:id/fault",handler:M},{method:"DELETE",pattern:"/v1/nodes/:id/fault",handler:Q}]);export{se as brokerRecoveryRoutes,de as nodeFaultRoutes,u as requireCurrentExecution};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var ce=Object.defineProperty;var u=(t,e)=>ce(t,"name",{value:e,configurable:!0});import{randomBytes as ue}from"node:crypto";import{claimNodeMail as le,acknowledgeNodeMail as fe}from"../../../core/canvas/deliveries.js";import{nodeMailEntry as me}from"../../../core/feed/inbox.js";import{enqueueNodeMail as
|
|
2
|
-
`)[0].slice(0,120)}u(
|
|
1
|
+
var ce=Object.defineProperty;var u=(t,e)=>ce(t,"name",{value:e,configurable:!0});import{randomBytes as ue}from"node:crypto";import{claimNodeMail as le,acknowledgeNodeMail as fe}from"../../../core/canvas/deliveries.js";import{nodeMailEntry as me}from"../../../core/feed/inbox.js";import{enqueueNodeMail as D,cancelPendingNodeMail as pe}from"../../../core/feed/messages.js";import{reviveNode as L}from"../../../core/runtime/revive.js";import{BrokerUnreachableError as U}from"../../../core/runtime/broker-request.js";import{isBrokerLive as R}from"../../../core/runtime/model-swap.js";import{deliverLive as ye,interruptLive as he}from"../../../core/runtime/interactive-deliver.js";import{isProfilePaused as we}from"../../../core/profiles/manifest.js";import{getNode as O,withFreshTerminalGuard as be}from"../../../core/canvas/canvas.js";import{getById as G,nodeObject as F,ownerForGrantee as ge}from"../../../core/graph/objects.js";import{recordActedOn as ve}from"../../../core/graph/edges.js";import{recordWatch as H}from"../../../core/graph/watches.js";import{publish as V}from"../../../core/graph/events.js";import{readerFor as K}from"../reader.js";import{canRead as _e}from"../../../core/graph/access.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{getPendingStructuredRequestForNode as Q}from"../../../core/canvas/structured-requests.js";import{consumeDeadlineOnWake as X}from"../../../core/feed/messages.js";import{assertNotFinalized as M,assertFinalizedForReopen as ke,commitReopenResident as P}from"../../../core/runtime/reopen.js";import{admitLegacyStructuredOutput as Se}from"../../structured-requests/sidecar.js";import{assertOutputSchemaAccepted as $e}from"../../../core/runtime/structured-output.js";import{appendSituationalContext as E,formatSituationalProse as Ne}from"../../../core/runtime/situational-context.js";import{formatDataCard as Y}from"../../../shared/generated-context.js";import{hasNoNaturalCycle as je}from"../../../core/runtime/revive-all.js";import{recordPendingParkActivity as Ae}from"../../park-pending.js";import{wantsExecution as qe}from"../../reconcilers/node-lifecycle/wants-execution.js";import{hasFreshGroundState as Oe}from"../../../core/runtime/revive.js";import{CrtrError as Ce,notFound as C,usage as c}from"../../../core/errors.js";import{emitEvent as xe}from"../../../core/events/emit.js";import{assertScope as Re}from"../../../core/scopes.js";import{InputError as I}from"../../../core/io.js";import{ApiError as Fe}from"../../../api/index.js";import{describeObject as Me,stubOf as Pe}from"../../../core/peer/stubs.js";import{machineNameOf as Z}from"../../../core/peer/names.js";import{callerNode as Ee,callerStub as Ie,forwardVerb as ee,relayError as te,remoteStub as Te,unknownError as re,withMachine as ne}from"../../peer/forward.js";import{ownerOnly as Be}from"../router.js";const ze=["critical","urgent","normal","deferred"];function oe(t,e){if(typeof t!="object"||t===null||Array.isArray(t))throw c(`${e} must be an object { kind, facts?, body? }`);const a=t,n=a.kind;if(typeof n!="string"||n.trim()==="")throw c(`${e}.kind is required`);if(!n.includes(":"))throw c(`${e}.kind must be namespaced (contain ':'): ${n}`);const r={kind:n},o=a.facts;if(o!==void 0){if(typeof o!="object"||o===null||Array.isArray(o))throw c(`${e}.facts must be an object of string or number values`);const d={};for(const[l,m]of Object.entries(o)){if(typeof m!="string"&&typeof m!="number")throw c(`${e}.facts.${l} must be a string or number`);d[l]=m}r.facts=d}const s=a.body;if(s!==void 0){if(typeof s!="string")throw c(`${e}.body must be a string`);r.body=s}return r}u(oe,"parseCardRequest");function ae(t,e){try{return Y(t.kind,t.facts??{},t.body??"")}catch(a){throw c(`${e}: ${a instanceof Error?a.message:String(a)}`)}}u(ae,"renderCard");function We(t){if(typeof t!="object"||t===null)throw c("a message requires a JSON body { body, tier?, fresh? }");const e=t,a=e.body,n=e.fresh===!0,r=e.tier,o=e.situational_context,s=e.output_schema,d=typeof a=="string"&&a.trim()!=="",l=typeof o=="string"&&o.trim()!=="",m=typeof s=="string"&&s.trim()!=="",v=e.situational_card,S=e.context_cards,b=v===void 0?void 0:oe(v,"situational_card");if(S!==void 0&&!Array.isArray(S))throw c("context_cards must be an array of { kind, facts?, body? }");const p=S?.map((N,$)=>oe(N,`context_cards[${$}]`));if(b!==void 0&&typeof o=="string"&&o!=="")throw c("situational_card and situational_context are mutually exclusive");const w=b!==void 0||p!==void 0&&p.length>0;if(!n&&!d&&!l&&!m&&!w)throw c("a message requires a non-empty body (or fresh: true, situational_context, a runtime card, or output_schema)");if(n&&p!==void 0&&p.length>0)throw c("context_cards cannot ride a fresh revive (no inbox entry carries them)");if(r!==void 0&&!ze.includes(r))throw c(`invalid tier: ${String(r)} (expected critical|urgent|normal|deferred)`);const f={body:typeof a=="string"?a:""};r!==void 0&&(f.tier=r),n&&(f.fresh=!0),e.reopen===!0&&(f.reopen=!0),typeof o=="string"&&(f.situational_context=o),b!==void 0&&(f.situational_card=b),p!==void 0&&(f.context_cards=p),typeof s=="string"&&(f.output_schema=s),typeof e.from=="string"&&(f.from=e.from);const y=e.delivery;if(y!==void 0&&y!=="interactive")throw c(`invalid delivery: ${String(y)} (expected interactive)`);if(y==="interactive"){if(n||f.situational_context!==void 0||f.output_schema!==void 0)throw c("interactive delivery supports a body, runtime cards, and reopen only (no fresh, situational_context, or output_schema)");if(!d)throw c("interactive delivery requires a non-empty body");if(f.tier==="deferred")throw c("interactive delivery cannot be deferred");f.delivery="interactive"}return f}u(We,"parseSendBody");function Je(t){let e;try{e=JSON.parse(t)}catch{throw c("output_schema must be a JSON schema string")}if(typeof e!="object"||e===null||Array.isArray(e))throw c("output_schema must be a JSON schema object");return e}u(Je,"parseSchema");function De(t){return t.split(`
|
|
2
|
+
`)[0].slice(0,120)}u(De,"label");function ie(t,e){return e===null?"":Y("reopen-final-report",{report:`${t}/${e}`},"This node previously completed work. Read this final report before acting on the new instruction.")}u(ie,"previousFinalReportCard");async function Le(t,e,a){if(a!==null&&a!=="human"&&(await G(t,a))?.type==="node"){const o=await F(t,a);return{actor:o,senderNode:o}}if(a==="human")return{actor:await ge(t,"user:local"),senderNode:null};const n=await K(t,e);return{actor:n.class==="person"?n.person:n.class==="app"?n.app:n.node,senderNode:n.class==="node"?n.node:null}}u(Le,"messageActor");async function se(t,e,a,n,r,o){const s=await F(t,a),{actor:d,senderNode:l}=await Le(t,e,n),m=r===null?{}:{pointer:r};await ve(t,d,s),l!==null&&l.id!==s.id&&await H(t,l,s,"message",{endsOn:o&&r!==null?`result:${r}`:"final"}),await V(t,{event:"messaged",object:s,actor:d,attrs:{},...m}),l!==null&&await V(t,{event:"sent",object:l,actor:l,attrs:{to:s.id},...m})}u(se,"recordMessage");function Ue(t,e){return new I({error:"deferred_no_natural_cycle",message:`${t} is ${e} with no natural next cycle ahead of it \u2014 a deferred message would never be delivered.`,next:"Use --tier normal, urgent, or critical to wake it now, or --reopen (with a body or --fresh) to re-task it first."})}u(Ue,"deferredNoNaturalCycle");async function Ge(t){if(!await Oe(t))throw new Fe(422,"unprocessable",`node ${t} has no goal/roadmap on disk \u2014 a fresh revive would resume amnesiac.`)}u(Ge,"assertRecoverableForFresh");function He(t,e){return t instanceof I?{error:t.payload.error,message:t.payload.message,next:t.payload.next,message_id:e}:t instanceof Ce?{error:t.code,message:t.message,message_id:e}:{error:"wake_failed",message:"the target could not be woken; see the daemon log",message_id:e}}u(He,"wakeFailure");async function Ve(t,e){const a=typeof t.body=="object"&&t.body!==null&&!Array.isArray(t.body)?{...t.body}:t.body;typeof a=="object"&&a!==null&&delete a.from;const n=await ee(t,e,{method:"POST",path:`/v1/nodes/${encodeURIComponent(e)}/messages`,body:a});if(n.kind==="local")return null;if(n.kind==="unknown")throw re(e,n.unreachable);if(n.status>=400)throw te(n.status,n.body);return await k(async r=>{const o=await Ee(r,t);if(o===null)return;const s=n.body?.object,d=await Te(r,e,"node",n.machine.id,s);await H(r,o,d,"message",{endsOn:"final"})}),{status:n.status,body:ne(n.body,n.machine)}}u(Ve,"forwardMessage");async function Ke(t){const e=await Qe(t);if(t.principal.class!=="forwarded"||typeof e.body!="object"||e.body===null)return e;const a=await k(async n=>{const r=await F(n,t.params.id);return await _e(n,await K(n,t),r)?await Me(n,r):{id:r.id,type:r.type,kind:null,name:r.id,owner:null}});return{...e,body:{...e.body,object:a}}}u(Ke,"handleMessage");async function Qe(t){const e=t.params.id,a=await Ve(t,e);if(a!==null)return a;const n=await O(e);if(n===null)throw C(`unknown node: ${e}`,{received:e});const r=We(t.body),o=r.tier??"normal",s=we(n.profile_id),d=t.principal.class==="forwarded"?await k(async i=>(await Ie(i,t))?.id??null):r.from??null;if(d!==null){const i=await O(d);i!==null&&Re(i,"crtr:act")}const l=d===null?null:await k(async i=>{const h=await G(i,d);return h===null?null:(await Pe(i,h))?.home_machine??null}),m=l===null?void 0:`${d} on ${Z(l)}`,v=t.principal.class==="broker"?t.principal.node:null,S=l===null?{}:{from_machine:Z(l)},b=r.body.trim()!=="",p=r.situational_card!==void 0?ae(r.situational_card,"situational_card"):r.situational_context!==void 0?Ne(r.situational_context.trim()):"",w=p!=="",f=(r.context_cards??[]).map((i,h)=>ae(i,`context_cards[${h}]`)),y=r.output_schema!==void 0&&r.output_schema.trim()!==""?Je(r.output_schema):null;y!==null&&await $e(y);let N=!1;const $=u(async i=>{if(!(y===null||N)){if(await Q(e)!==null)throw new I({error:"structured_request_conflict",message:`node ${e} already has a pending structured request`,next:"Wait for the current request to settle before requesting another structured result."});await Se(e,"oneoff",y,i),N=!0}},"ensureSchema");if(r.fresh){const i=r.reopen?await ke(e):null;r.reopen||await M(e),await Ge(e),await $(),w&&E(e,p),r.reopen&&await P(e,i,v);const h=await L(e,{resume:!1,capacity:"refuse"});return{status:201,body:{node_id:e,delivered:!1,revived:h.launch!==void 0}}}r.delivery==="interactive"&&!r.reopen&&await M(e);let T=!1,_="";if(r.delivery==="interactive"&&r.reopen&&(_=ie(e,await P(e,void 0,v)),T=!0),r.delivery==="interactive"&&await $(),r.delivery==="interactive"&&!s&&!n.run_id&&R(n))try{return w&&E(e,p),await ye(e,r.body,{cards:[..._===""?[]:[_],...w?[p]:[],...f],origin:d==="human"?{type:"human"}:{type:"runtime",kind:"inbox",entries:[{...d===null?{}:{from:d},...m===void 0?{}:{fromName:m},kind:"message",tier:o}]}}),await k(async h=>await se(h,t,e,d,null,!1)),await X(e),{status:201,body:{node_id:e,delivered:!0,revived:!1,delivered_via:"engine"}}}catch(i){if(!(i instanceof U))throw i}const B=u(async()=>{r.reopen||await M(e);const i=ue(16).toString("hex"),h=u(async q=>await se(q,t,e,d,i,y!==null),"withinEnqueue");await $(i),w&&E(e,p),r.reopen&&!T&&(_=ie(e,await P(e,void 0,v)));const x=[..._===""?[]:[_],...f],J=x.length>0?{cards:x}:{};if(b||y!==null){const q=b?r.body:`A one-off structured-output request has been granted. Submit a result matching the requested schema with the \`submit_result\` tool (\`request_id: "${(await Q(e))?.request_id??"<request-id>"}"\`) before you stop; after it is accepted, continue normally.`;return await D(e,{senderNodeId:d,...m===void 0?{}:{senderName:m},urgency:o,wake:1,kind:"message",messageId:i,withinEnqueue:h,label:De(q),bodyJson:{body:q,...S,...w?{situational:!0}:{},...J}})}return await D(e,{senderNodeId:d,...m===void 0?{}:{senderName:m},urgency:o,wake:1,kind:"message",messageId:i,withinEnqueue:h,label:w?"(ambient context updated)":"(context delivered)",bodyJson:{...w?{situational:!0}:{},situationalOnly:!0,...J}})},"deliverImmediate"),g=await u(async()=>{if(o==="deferred"){const i=await be(e,je,B);if(i.kind==="missing")throw C(`unknown node: ${e}`,{received:e});if(i.kind==="terminal")throw Ue(e,i.status);return i.result}return await B()},"deliver")();g.urgency!=="deferred"&&await k(async()=>Ae(e));const j=await O(e);if(j===null)throw C(`unknown node: ${e}`,{received:e});n.frozen_at!==null&&await X(e);let z=!1,W=!1,A;if((r.reopen||j.lifecycle==="terminal"||!await qe(j))&&g.urgency!=="deferred"&&!s&&!R(j))try{const i=await L(e,{resume:!0,capacity:"freeze"});z=i.launch!==void 0,W=i.outcome==="frozen"}catch(i){A=He(i,g.messageId),xe({level:"error",event:"message.wake_failed",node_id:e,error:i,fields:{message_id:g.messageId,error_code:A.error}})}return{status:201,body:{node_id:e,delivered:!0,revived:z,...W?{not_revived_reason:"capacity_frozen"}:{},...A===void 0?{}:{wake_failed:A},delivered_at:g.createdAt,...g.urgency===o?{}:{delivered_tier:g.urgency},...r.delivery==="interactive"?{delivered_via:"inbox"}:{}}}}u(Qe,"handleMessageHere");async function Xe(t){const e=t.params.id,a=await ee(t,e,{method:"POST",path:`/v1/nodes/${encodeURIComponent(e)}/interrupt`,body:t.body});if(a.kind==="unknown")throw re(e,a.unreachable);if(a.kind==="remote"){if(a.status>=400)throw te(a.status,a.body);return{status:a.status,body:ne(a.body,a.machine)}}const n=await O(e);if(n===null)throw C(`unknown node: ${e}`,{received:e});const r=await pe(e,{senderNodeId:"human",kind:"message"});let o=!1;if(R(n))try{o=await he(e)!=="idle"}catch(d){if(!(d instanceof U))throw d}return{status:200,body:{node_id:e,outcome:o?"aborted_turn":r.length>0?"canceled_pending":"idle",aborted_turn:o,canceled_pending:r.length}}}u(Xe,"handleInterrupt");function de(t){if(typeof t.body!="object"||t.body===null||Array.isArray(t.body))throw c("mail requires an object body");const e=t.body;if(typeof e.execution_id!="string"||e.execution_id.trim()==="")throw c("execution_id is required");return e}u(de,"mailBody");async function Ye(t){const e=de(t),a=e.channel;if(a!=="wake"&&a!=="passive"&&a!=="deferred_run")throw c("channel must be wake, passive, or deferred_run");const n=await le(t.params.id,e.execution_id,a);return{status:200,body:{action:n.action,deliveries:n.deliveries.map(o=>({delivery_id:o.deliveryId,attempts:o.attempts,handoff:o.handoff,entry:{...me(o),seq:o.message.seq}}))}}}u(Ye,"handleMailClaim");async function Ze(t){const e=de(t);if(!Array.isArray(e.deliveries))throw c("deliveries must be an array");const a=e.deliveries.map(o=>{if(typeof o!="object"||o===null||Array.isArray(o))throw c("each delivery must be an object");const s=o;if(typeof s.delivery_id!="string"||s.delivery_id==="")throw c("delivery_id is required");if(!Number.isSafeInteger(s.attempts)||s.attempts<1)throw c("attempts must be a positive integer");if(s.disposition!=="acknowledged"&&s.disposition!=="released")throw c("disposition must be acknowledged or released");return{delivery_id:s.delivery_id,attempts:s.attempts,disposition:s.disposition}}),n=await fe(t.params.id,e.execution_id,a.map(o=>({deliveryId:o.delivery_id,attempts:o.attempts,disposition:o.disposition})));return{status:200,body:{action:n.action,results:n.results.map(o=>({delivery_id:o.deliveryId,outcome:o.outcome}))}}}u(Ze,"handleMailAcknowledge");const Wt=Be([{method:"POST",pattern:"/v1/nodes/:id/mail/claim",handler:Ye},{method:"POST",pattern:"/v1/nodes/:id/mail/acknowledge",handler:Ze}]),Jt=[{method:"POST",pattern:"/v1/nodes/:id/messages",scope:"crtr:act",handler:Ke},{method:"POST",pattern:"/v1/nodes/:id/interrupt",scope:"crtr:act",handler:Xe}];export{Wt as brokerMailRoutes,Ke as handleMessage,Jt as messageRoutes};
|