@north-light/cli 0.3.504 → 0.3.506
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 +5 -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/canvas.d.ts +40 -0
- package/dist/api/routes.d.ts +2 -0
- package/dist/api/routes.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +3 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +3 -2
- package/dist/clients/attach/viewer.js +575 -575
- package/dist/clients/dashboard/app.d.ts +7 -0
- package/dist/clients/dashboard/app.js +2 -0
- package/dist/clients/dashboard/model.d.ts +39 -0
- package/dist/clients/dashboard/model.js +1 -0
- package/dist/clients/dashboard/render.d.ts +29 -0
- package/dist/clients/dashboard/render.js +3 -0
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/surface/node/focus.js +1 -1
- package/dist/commands/surface-dashboard.d.ts +2 -0
- package/dist/commands/surface-dashboard.js +3 -0
- package/dist/commands/surface.js +1 -1
- package/dist/core/canvas/browse/app.d.ts +3 -0
- package/dist/core/canvas/browse/app.js +3 -3
- package/dist/core/canvas/browse/model.d.ts +5 -1
- package/dist/core/canvas/browse/model.js +1 -1
- package/dist/core/canvas/browse/render.d.ts +5 -0
- package/dist/core/canvas/browse/render.js +3 -3
- package/dist/core/canvas/migrations.js +1 -1
- package/dist/core/canvas/recovery.d.ts +5 -0
- package/dist/core/canvas/recovery.js +2 -2
- package/dist/core/human/requests.d.ts +3 -0
- package/dist/core/human/requests.js +17 -17
- package/dist/core/keybindings/catalog.d.ts +4 -3
- package/dist/core/keybindings/catalog.js +1 -1
- package/dist/core/llm/run.js +2 -2
- package/dist/core/runtime/broker/model-adapter.d.ts +1 -2
- package/dist/core/runtime/focus-viewer.d.ts +6 -0
- package/dist/core/runtime/focus-viewer.js +1 -1
- package/dist/core/runtime/placement-tmux.d.ts +1 -1
- package/dist/core/runtime/placement-tmux.js +1 -1
- package/dist/core/runtime/tmux-bindings.js +3 -3
- package/dist/core/runtime/tmux-driver.d.ts +6 -0
- package/dist/core/runtime/tmux-driver.js +5 -5
- package/dist/core/subscription-state.d.ts +2 -0
- package/dist/core/subscription-state.js +4 -4
- package/dist/daemon/api/bridge.js +3 -3
- package/dist/daemon/api/handlers/broker-recovery.js +1 -1
- package/dist/daemon/api/handlers/canvas.js +6 -6
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/viewer-presence.d.ts +3 -0
- package/dist/daemon/api/viewer-presence.js +1 -1
- package/docs/sdk/release-notes.md +8 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
package/dist/core/llm/run.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var ge=Object.defineProperty;var u=(e,t)=>ge(e,"name",{value:t,configurable:!0});import{randomUUID as be}from"node:crypto";import{streamSimple as ve}from"@earendil-works/pi-ai/compat";import{expandModelCandidates as re,parseModelSpec as se,probeRouteAvailability as we}from"../model-routes.js";import{readMergedLaunchConfig as ie}from"../config.js";import{openModelRegistry as xe}from"../runtime/model-registry.js";import{MAX_RATE_LIMIT_COOLDOWN_MS as ae,coolingUntilFor as de,isManagedProvider as H,markSubscriptionRateLimited as ke,markSubscriptionSuccess as je,readSubscriptionPool as Me,retryAfterMsFromHeaders as K,retryAfterMsFromError as qe,refreshSubscriptionCredential as Ae,subscriptionNeedsSuccessWrite as $e}from"../subscription-state.js";import{classifyRateLimit as Ie,describeRateLimitScope as le,rateLimitHeaders as Re}from"../rate-limit-scope.js";import{RunError as s}from"../runs/operations.js";import{recognizeProviderError as ce}from"../provider-error-recognition.js";import{isSafetyRefusal as Se}from"./refusal.js";import{assertUnderCap as Ce,recordModelUsage as ue}from"../usage/ledger.js";import{outboundToken as Le}from"../providers/mint.js";import{discardFile as Oe}from"../providers/token-cache.js";import{ProviderError as Fe}from"../providers/errors.js";import{appCandidates as Ne}from"./app-rungs.js";function De(e){return typeof e=="string"||e===null||Array.isArray(e)&&e.every(t=>t&&typeof t=="object"&&(t.type==="text"&&typeof t.text=="string"||t.type==="image_url"&&typeof t.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(t.image_url.url)))}u(De,"validContent");function fe(e){return Array.isArray(e)?e.map(t=>{if(t.type==="text")return t;const[,n,r]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(t.image_url.url);return{type:"image",mimeType:n,data:r}}):e??""}u(fe,"imageContent");function st(e){if(!e||typeof e!="object"||Array.isArray(e))throw new s(400,"invalid_request","body must be an object");const t=e;if(!Array.isArray(t.messages)||!t.messages.every(n=>n&&typeof n=="object"&&["system","user","assistant","tool"].includes(n.role)&&De(n.content)))throw new s(400,"invalid_request","messages must be chat messages","messages");if(t.messages.some(n=>["system","assistant"].includes(n.role)&&typeof n.content!="string"&&n.content!==null))throw new s(400,"invalid_request","system and assistant content must be text","messages");if(t.model!==void 0&&typeof t.model!="string")throw new s(400,"invalid_request","model must be a string","model");if(t.model_policy!==void 0&&!["exact","substitute"].includes(t.model_policy))throw new s(400,"invalid_request","invalid model_policy","model_policy");if(t.payer!==void 0&&!["user","app"].includes(t.payer))throw new s(400,"invalid_request","invalid payer","payer");if(t.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(t.reasoning_effort))throw new s(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(t.stream!==void 0&&typeof t.stream!="boolean")throw new s(400,"invalid_request","stream must be boolean","stream");if(t.tools!==void 0&&(!Array.isArray(t.tools)||!t.tools.every(n=>n?.type==="function"&&typeof n.function?.name=="string"&&n.function.name.length>0&&(n.function.description===void 0||typeof n.function.description=="string")&&(n.function.parameters===void 0||n.function.parameters!==null&&typeof n.function.parameters=="object"&&!Array.isArray(n.function.parameters)&&n.function.parameters.type==="object"))))throw new s(400,"invalid_request","invalid tools","tools");if(t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&t.tool_choice!=="required"&&(typeof t.tool_choice!="object"||t.tool_choice===null||t.tool_choice.type!=="function"||typeof t.tool_choice.function?.name!="string"))throw new s(400,"invalid_request","invalid tool_choice","tool_choice");if(t.messages.some(n=>n.role==="assistant"&&n.tool_calls!==void 0&&(!Array.isArray(n.tool_calls)||!n.tool_calls.every(r=>r&&typeof r.id=="string"&&typeof r.function?.name=="string"&&typeof r.function.arguments=="string"&&Ee(r.function.arguments)))))throw new s(400,"invalid_request","invalid assistant tool_calls","messages");if(t.stop!==void 0&&!(typeof t.stop=="string"||Array.isArray(t.stop)&&t.stop.every(n=>typeof n=="string")))throw new s(400,"invalid_request","invalid stop","stop");if(t.seed!==void 0&&!Number.isSafeInteger(t.seed))throw new s(400,"invalid_request","invalid seed","seed");for(const[n,r]of[["temperature",t.temperature],["top_p",t.top_p],["max_tokens",t.max_tokens]])if(r!==void 0&&(typeof r!="number"||!Number.isFinite(r)||n==="temperature"&&(r<0||r>2)||n==="top_p"&&(r<0||r>1)||n==="max_tokens"&&(!Number.isSafeInteger(r)||r<1)))throw new s(400,"invalid_request",`invalid ${n}`,n);if(t.response_format!==void 0){const n=t.response_format;if(!n||typeof n!="object"||Array.isArray(n)||!["text","json_object","json_schema"].includes(n.type)||n.type==="json_schema"&&(typeof n.json_schema?.name!="string"||!n.json_schema.name||!n.json_schema.schema||typeof n.json_schema.schema!="object"||Array.isArray(n.json_schema.schema)))throw new s(400,"invalid_request","invalid response_format","response_format")}if(t.metadata!==void 0&&Buffer.byteLength(JSON.stringify(t.metadata)??"")>4096)throw new s(400,"invalid_request","metadata exceeds 4 KB","metadata");return t}u(st,"parseLlmRequest");function Ee(e){try{const t=JSON.parse(e);return t!==null&&typeof t=="object"&&!Array.isArray(t)}catch{return!1}}u(Ee,"isJsonObject");function Pe(e){const t=Date.now(),n=e.messages.map(i=>i.role==="system"?{role:"system",content:i.content??"",timestamp:t}:i.role==="tool"?{role:"toolResult",toolCallId:i.tool_call_id??"",toolName:"",content:typeof i.content=="string"||i.content===null?[{type:"text",text:i.content??""}]:fe(i.content),isError:!1,timestamp:t}:i.role==="assistant"?{role:"assistant",content:[...i.content?[{type:"text",text:i.content}]:[],...(i.tool_calls??[]).map(g=>({type:"toolCall",id:g.id,name:g.function.name,arguments:JSON.parse(g.function.arguments)}))],api:"openai-completions",provider:"openai",model:e.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:t}:{role:"user",content:fe(i.content),timestamp:t}),r=n.findIndex(i=>i.role!=="system"),w=n.slice(0,r<0?n.length:r).map(i=>i.content),y=w.join(`
|
|
2
2
|
|
|
3
|
-
`),l=e.tools?.map(({function:i})=>({name:i.name,description:i.description??"",parameters:i.parameters??{type:"object",properties:{}}}));return{...y?{systemPrompt:y}:{},messages:n.slice(w.length),...l?{tools:l}:{}}}u(Pe,"contextOf");function fe(e,t,n,r){if(!e){const y=ie(n,r),l=Object.values({...y.modelRoutes??{},...y.legacyModelRoutes??{}}),i=l.length?[y.modelLadders.defaultProvider,...y.modelRouting?.familyOrder??[],...l.map(g=>g.family)].find(g=>l.some(F=>F.family===g)):y.modelLadders.defaultProvider??"anthropic";return{kind:"logical",family:i,strength:"strong",spec:`${i}/strong`}}const w=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(e);return w?{kind:"logical",family:w[1],strength:w[2],spec:e}:{kind:t==="exact"?"exact":"concrete",spec:e}}u(fe,"requestFor");function Ee(e,t){const{input:n,output:r,cacheRead:w,cacheWrite:y}=e.cost;return n===0&&r===0&&w===0&&y===0?null:Number.isFinite(t)&&t>=0?t:null}u(Ee,"usdEquivalent");function Te(e,t){return e.compat?.allowedFallbackModels?.find(r=>r.provider===e.provider&&r.model===t)}u(Te,"allowedFallback");function st(e){return e?{payer:e.payer??"user",sub:e.sub,grantee:e.grantee,inference_endpoint:e.inference_endpoint??null}:void 0}u(st,"llmCallerOf");function Ue(e){return e instanceof Oe?e.code==="server_error"?new s(503,"upstream_model_unavailable",e.message):new s(404,"model_unavailable",e.message):e}u(Ue,"mintFailure");async function it(e,t,n,r,w,y){const l=y?.payer==="app"?y:void 0;if(e.payer==="app"&&!l)throw new s(403,"payer_not_app","this grant is not app-paid");if(e.payer==="user"&&l)throw new s(400,"invalid_request","this grant is app-paid; payer cannot be user","payer");if(l&&!l.inference_endpoint)throw new s(404,"model_unavailable",`${l.grantee} is app-paid but its grant carries no inference endpoint`);const i=l?.inference_endpoint??void 0;l||Se(w);const g=await we(),F=u(c=>c.routeId!=="concrete"&&c.routeId!=="exact","configured"),Y=u(c=>{const p=g.find(c.providerId,c.modelId);return p!==void 0&&(!e.reasoning_effort||p.reasoning)&&(e.seed===void 0&&e.response_format===void 0||p.api==="openai-completions")&&(e.stop===void 0||["openai-completions","anthropic-messages"].includes(p.api))},"matchesFeatures"),me=u(()=>{const c=fe(e.model,e.model_policy,r?.cwd,r?.profileId),p=re(c.kind==="exact"?{kind:"concrete",spec:c.spec}:c,r?.cwd,r?.profileId).filter(F);let a;if(c.kind==="logical")a=e.model_policy==="exact"?p.filter(m=>m.family===c.family&&m.strength===c.strength).slice(0,1):p;else{const m=se(c.spec),M=p.filter(j=>j.providerId===m?.providerId&&j.modelId===m.modelId&&(e.model_policy!=="exact"||j.thinkingLevel===m.thinkingLevel)&&Y(j));if(e.model_policy==="exact")a=M.slice(0,1);else if(M.length)a=M;else{const j=ie(r?.cwd,r?.profileId),x=[...Object.values(j.modelRoutes??{}).flatMap(v=>Object.entries(v.models)),...[j.modelLadders.anthropic,j.modelLadders.openai].flatMap(v=>Object.entries(v))].find(([,v])=>v===c.spec)?.[0],f=fe(void 0,"substitute",r?.cwd,r?.profileId);a=x&&f.kind==="logical"?re({...f,strength:x,spec:c.spec},r?.cwd,r?.profileId).filter(v=>F(v)&&v.strength===x):[]}}return a},"ownerCandidates");let R;if(i?R=Fe(i,e.model,e.model_policy==="exact").flatMap((c,p)=>{const a=se(c.model);return a?[{routeId:"app",credentialSource:"app",family:"app",strength:c.strength,...a,rank:p}]:[]}):R=me(),R=R.filter(Y),R.length===0)throw e.model_policy==="exact"?new s(400,"substitution_refused","no eligible model route is configured"):new s(404,"model_unavailable","no eligible model route is configured");const ee=`chatcmpl-${ge()}`;let I=1/0,b,z;for(const c of R){if(!l&&!ve(c,g).authenticated)continue;const p=g.find(c.providerId,c.modelId);if(!p)continue;const a=i?{...p,baseUrl:`${i.base_url}/${p.provider}`}:p,m=c.providerId,M=!l&&H(m)?je(m).filter(x=>!x.authFailure):[],j=M.filter(x=>ae(x,a.id)<=Date.now());if(M.length&&!j.length){const x=Math.min(...M.map(f=>ae(f,a.id)));x<I&&(I=x,z=`${a.provider}/${a.id}`);continue}const te=l?[void 0,void 0]:M.length?j:[void 0];for(const[x,f]of te.entries()){const v=Date.now();let oe=f?.access;if(f&&f.expires<=Date.now()&&H(m)){const o=g.getProvider(m)?.auth.oauth;if(!o)throw new s(503,"upstream_model_unavailable",`no OAuth refresh for ${m}`,void 0,{served_model:`${a.provider}/${a.id}`});oe=(await Ae(m,f.label,async(k,ne)=>{const W=await o.refresh({type:"oauth",refresh:k,access:"",expires:0,...ne?{clientId:ne}:{}},t??new AbortController().signal),X=W.clientId;return{refresh:W.refresh,access:W.access,expires:W.expires,...typeof X=="string"&&X?{clientId:X}:{}}})).access}const pe=Pe(e);let N;if(l&&i)try{N=await Ce({audience:i.base_url,sub:l.sub,grantee:l.grantee,initiating_app:l.grantee,scope:["crtr:llm"]},{signal:t})}catch(o){throw Ue(o)}const S=f||l?void 0:await g.getApiKeyAndHeaders(a);if(S&&!S.ok)continue;const h=`${a.provider}/${a.id}`,C=e.model??h;let A=!1,O,q=3e4,$;const B=[],Z=[],D=[];let d,P;const E=u((o,_=null)=>({id:ee,object:"chat.completion.chunk",created:Math.floor(v/1e3),model:h,served_model:h,requested_model:C,substituted:C!==h&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(C),choices:[{index:0,delta:o,finish_reason:_}]}),"chunk"),T=u(o=>{B.push(o),n?.(o)},"emit"),he=be(a,pe,{apiKey:N?.access_token??oe??(S?.ok?S.apiKey:void 0),...S?.ok?{headers:S.headers}:{},signal:t,reasoning:e.reasoning_effort??c.thinkingLevel,...e.temperature!==void 0?{temperature:e.temperature}:{},...e.max_tokens!==void 0?{maxTokens:e.max_tokens}:{},...e.top_p!==void 0?{samplingParams:{top_p:e.top_p}}:{},...e.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:u(o=>{if(!o||typeof o!="object")return;const _=e.tool_choice,k=_==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof _=="object"&&_!==null?a.api==="anthropic-messages"?{type:"tool",name:_.function.name}:_:void 0;return{...o,...k?{tool_choice:k}:{},...e.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof e.stop=="string"?[e.stop]:e.stop}:{},...e.seed!==void 0?{seed:e.seed}:{},...e.response_format!==void 0?{response_format:e.response_format}:{}}},"onPayload"),fetch:u(async(o,_)=>{const k=await fetch(o,_);return O=k.status,k.status===429&&(A=!0,$=k.headers,q=K(k.headers)??15e3),k},"fetch"),onResponse:u(o=>{o.status===429&&(A=!0,$=o.headers,q=K(o.headers)??15e3)},"onResponse")});try{for await(const o of he)o.type==="text_delta"&&(Z.push(o.delta),T(E({content:o.delta}))),o.type==="thinking_delta"&&(D.push(o.delta),T(E({reasoning_content:o.delta}))),o.type==="done"&&(d=o.message),o.type==="error"&&(d=o.error)}catch(o){const _=qe(o);$===void 0&&o&&typeof o=="object"&&"headers"in o&&Object.keys(Re(o.headers)).length&&($=o.headers);const k=le({status:o&&typeof o=="object"&&"status"in o?o.status:void 0,message:o instanceof Error?o.message:String(o)});if(l)P=o;else{if(!A&&k.kind!=="rate-limit")throw B.length?new s(503,"upstream_model_unavailable",o instanceof Error?o.message:String(o),void 0,{served_model:h}):o;A=!0,q=_??q}}if(l){if(O===401){if(N&&Le(N.key),x===0)continue;throw new s(404,"model_unavailable",`${i.base_url} refuses this runtime's tokens`,void 0,{served_model:h})}if(A&&K($)===void 0)throw new s(503,"upstream_model_unavailable",`${h} was rate-limited without a retry-after`,void 0,{served_model:h});if(!A&&(P!==void 0||O!==void 0&&O>=500))throw new s(503,"upstream_model_unavailable",P instanceof Error?P.message:d?.errorMessage??"inference endpoint failed",void 0,{served_model:h})}if(!A&&d?.stopReason==="error"&&le({message:d.errorMessage}).kind==="rate-limit"&&(A=!0,q=15e3),A){const o=$e($,a.id);if(K($)===void 0&&o.resetAt!==void 0&&(q=Math.max(q,o.resetAt-Date.now())),b={provider:m,model:a.id,...f?{account:f.label}:{},observation:o},console.error(`[llm] 429 from ${h}${f?` account "${f.label}"`:""}: scope=${de(o.scope)} retry_after_ms=${q} headers=${JSON.stringify(o.headers)}`),f&&H(m)&&await xe(m,f.label,q,v,Date.now(),o.scope),Date.now()+q<I&&(I=Date.now()+q,z=h),Z.length||D.length)throw new s(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:h});if(l)break;continue}const U=Ie(d);if(O===400&&e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(d?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(d?.errorMessage??""))throw new s(400,"invalid_request",d.errorMessage,"tool_choice",{served_model:h});if(!d||d.stopReason==="error"&&!U||d.stopReason==="aborted")throw new s(503,"upstream_model_unavailable",d?.errorMessage??"model request failed",void 0,{served_model:h});f&&H(m)&&Me(f,a.id)&&await ke(m,f.label,v,a.id);const G=d.content.filter(o=>o.type==="toolCall").map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:JSON.stringify(o.arguments)}})),Q=U?"content_filter":d.stopReason==="toolUse"?"tool_calls":d.stopReason==="length"?"length":"stop",V=d.responseModel?Te(a,d.responseModel):void 0,L=V?`${a.provider}/${V.model}`:h,J={prompt_tokens:d.usage.input,completion_tokens:d.usage.output,total_tokens:d.usage.totalTokens,cache_read_tokens:d.usage.cacheRead,cache_write_tokens:d.usage.cacheWrite,usd_equivalent:Ee(V??a,d.usage.cost.total),served_model:L};l?ce(l.grantee,"app",L,d.usage,J.usd_equivalent):w&&ce(w.grantee,"user",L,d.usage,J.usd_equivalent);const _e={id:ee,object:"chat.completion",created:Math.floor(v/1e3),model:L,served_model:L,requested_model:C,substituted:C!==L&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(C),choices:[{index:0,message:{role:"assistant",content:Z.join("")||null,...D.length?{reasoning_content:D.join("")}:{},...G.length?{tool_calls:G}:{},...U?{refusal:d.errorMessage??"Content filtered"}:{}},finish_reason:Q}],usage:J,...e.metadata!==void 0?{metadata:e.metadata}:{}};for(const[o,_]of G.entries())T(E({tool_calls:[{index:o,..._}]}));return T({...E({},Q),...U?{choices:[{index:0,delta:{},finish_reason:Q,message:{refusal:d.errorMessage??"Content filtered"}}]}:{},usage:J}),{completion:_e,chunks:B}}}throw I!==1/0?new s(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((I-Date.now())/1e3)),served_model:b?`${b.provider}/${b.model}`:z,...b?{rate_limit:{provider:b.provider,model:b.model,...b.account?{account:b.account}:{},scope:de(b.observation.scope),...b.observation.claim?{claim:b.observation.claim}:{},headers:b.observation.headers}}:{}},void 0,!0):e.model_policy==="exact"?new s(400,"substitution_refused","the requested model is unavailable"):new s(404,"model_unavailable","no usable model route is available")}u(it,"runLlm");export{st as llmCallerOf,rt as parseLlmRequest,it as runLlm};
|
|
3
|
+
`),l=e.tools?.map(({function:i})=>({name:i.name,description:i.description??"",parameters:i.parameters??{type:"object",properties:{}}}));return{...y?{systemPrompt:y}:{},messages:n.slice(w.length),...l?{tools:l}:{}}}u(Pe,"contextOf");function me(e,t,n,r){if(!e){const y=ie(n,r),l=Object.values({...y.modelRoutes??{},...y.legacyModelRoutes??{}}),i=l.length?[y.modelLadders.defaultProvider,...y.modelRouting?.familyOrder??[],...l.map(g=>g.family)].find(g=>l.some(F=>F.family===g)):y.modelLadders.defaultProvider??"anthropic";return{kind:"logical",family:i,strength:"strong",spec:`${i}/strong`}}const w=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(e);return w?{kind:"logical",family:w[1],strength:w[2],spec:e}:{kind:t==="exact"?"exact":"concrete",spec:e}}u(me,"requestFor");function Te(e,t){const{input:n,output:r,cacheRead:w,cacheWrite:y}=e.cost;return n===0&&r===0&&w===0&&y===0?null:Number.isFinite(t)&&t>=0?t:null}u(Te,"usdEquivalent");function Ue(e,t){return e.compat?.allowedFallbackModels?.find(r=>r.provider===e.provider&&r.model===t)}u(Ue,"allowedFallback");function it(e){return e?{payer:e.payer??"user",sub:e.sub,grantee:e.grantee,inference_endpoint:e.inference_endpoint??null}:void 0}u(it,"llmCallerOf");function Je(e){return e instanceof Fe?e.code==="server_error"?new s(503,"upstream_model_unavailable",e.message):new s(404,"model_unavailable",e.message):e}u(Je,"mintFailure");async function at(e,t,n,r,w,y){const l=y?.payer==="app"?y:void 0;if(e.payer==="app"&&!l)throw new s(403,"payer_not_app","this grant is not app-paid");if(e.payer==="user"&&l)throw new s(400,"invalid_request","this grant is app-paid; payer cannot be user","payer");if(l&&!l.inference_endpoint)throw new s(404,"model_unavailable",`${l.grantee} is app-paid but its grant carries no inference endpoint`);const i=l?.inference_endpoint??void 0;l||Ce(w);const g=await xe(),F=u(c=>c.routeId!=="concrete"&&c.routeId!=="exact","configured"),Y=u(c=>{const p=g.find(c.providerId,c.modelId);return p!==void 0&&(!e.reasoning_effort||p.reasoning)&&(e.seed===void 0&&e.response_format===void 0||p.api==="openai-completions")&&(e.stop===void 0||["openai-completions","anthropic-messages"].includes(p.api))},"matchesFeatures"),pe=u(()=>{const c=me(e.model,e.model_policy,r?.cwd,r?.profileId),p=re(c.kind==="exact"?{kind:"concrete",spec:c.spec}:c,r?.cwd,r?.profileId).filter(F);let a;if(c.kind==="logical")a=e.model_policy==="exact"?p.filter(m=>m.family===c.family&&m.strength===c.strength).slice(0,1):p;else{const m=se(c.spec),A=p.filter(j=>j.providerId===m?.providerId&&j.modelId===m.modelId&&(e.model_policy!=="exact"||j.thinkingLevel===m.thinkingLevel)&&Y(j));if(e.model_policy==="exact")a=A.slice(0,1);else if(A.length)a=A;else{const j=ie(r?.cwd,r?.profileId),x=[...Object.values(j.modelRoutes??{}).flatMap(v=>Object.entries(v.models)),...[j.modelLadders.anthropic,j.modelLadders.openai].flatMap(v=>Object.entries(v))].find(([,v])=>v===c.spec)?.[0],f=me(void 0,"substitute",r?.cwd,r?.profileId);a=x&&f.kind==="logical"?re({...f,strength:x,spec:c.spec},r?.cwd,r?.profileId).filter(v=>F(v)&&v.strength===x):[]}}return a},"ownerCandidates");let I;if(i?I=Ne(i,e.model,e.model_policy==="exact").flatMap((c,p)=>{const a=se(c.model);return a?[{routeId:"app",credentialSource:"app",family:"app",strength:c.strength,...a,rank:p}]:[]}):I=pe(),I=I.filter(Y),I.length===0)throw e.model_policy==="exact"?new s(400,"substitution_refused","no eligible model route is configured"):new s(404,"model_unavailable","no eligible model route is configured");const ee=`chatcmpl-${be()}`;let R=1/0,b,z;for(const c of I){if(!l&&!we(c,g).authenticated)continue;const p=g.find(c.providerId,c.modelId);if(!p)continue;const a=i?{...p,baseUrl:`${i.base_url}/${p.provider}`}:p,m=c.providerId,A=!l&&H(m)?Me(m).filter(x=>!x.authFailure):[],j=A.filter(x=>de(x,a.id)<=Date.now());if(A.length&&!j.length){const x=Math.min(...A.map(f=>de(f,a.id)));x<R&&(R=x,z=`${a.provider}/${a.id}`);continue}const te=l?[void 0,void 0]:A.length?j:[void 0];for(const[x,f]of te.entries()){const v=Date.now();let oe=f?.access;if(f&&f.expires<=Date.now()&&H(m)){const o=g.getProvider(m)?.auth.oauth;if(!o)throw new s(503,"upstream_model_unavailable",`no OAuth refresh for ${m}`,void 0,{served_model:`${a.provider}/${a.id}`});oe=(await Ae(m,f.label,async(k,ne)=>{const W=await o.refresh({type:"oauth",refresh:k,access:"",expires:0,...ne?{clientId:ne}:{}},t??new AbortController().signal),V=W.clientId;return{refresh:W.refresh,access:W.access,expires:W.expires,...typeof V=="string"&&V?{clientId:V}:{}}})).access}const he=Pe(e);let N;if(l&&i)try{N=await Le({audience:i.base_url,sub:l.sub,grantee:l.grantee,initiating_app:l.grantee,scope:["crtr:llm"]},{signal:t})}catch(o){throw Je(o)}const S=f||l?void 0:await g.getApiKeyAndHeaders(a);if(S&&!S.ok)continue;const h=`${a.provider}/${a.id}`,C=e.model??h;let q=!1,O,M=3e4,$;const B=[],X=[],D=[];let d,E;const P=u((o,_=null)=>({id:ee,object:"chat.completion.chunk",created:Math.floor(v/1e3),model:h,served_model:h,requested_model:C,substituted:C!==h&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(C),choices:[{index:0,delta:o,finish_reason:_}]}),"chunk"),T=u(o=>{B.push(o),n?.(o)},"emit"),_e=ve(a,he,{apiKey:N?.access_token??oe??(S?.ok?S.apiKey:void 0),...S?.ok?{headers:S.headers}:{},signal:t,reasoning:e.reasoning_effort??c.thinkingLevel,...e.temperature!==void 0?{temperature:e.temperature}:{},...e.max_tokens!==void 0?{maxTokens:e.max_tokens}:{},...e.top_p!==void 0?{samplingParams:{top_p:e.top_p}}:{},...e.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:u(o=>{if(!o||typeof o!="object")return;const _=e.tool_choice,k=_==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof _=="object"&&_!==null?a.api==="anthropic-messages"?{type:"tool",name:_.function.name}:_:void 0;return{...o,...k?{tool_choice:k}:{},...e.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof e.stop=="string"?[e.stop]:e.stop}:{},...e.seed!==void 0?{seed:e.seed}:{},...e.response_format!==void 0?{response_format:e.response_format}:{}}},"onPayload"),fetch:u(async(o,_)=>{const k=await fetch(o,_);return O=k.status,k.status===429&&(q=!0,$=k.headers,M=K(k.headers)??15e3),k},"fetch"),onResponse:u(o=>{o.status===429&&(q=!0,$=o.headers,M=K(o.headers)??15e3)},"onResponse")});try{for await(const o of _e)o.type==="text_delta"&&(X.push(o.delta),T(P({content:o.delta}))),o.type==="thinking_delta"&&(D.push(o.delta),T(P({reasoning_content:o.delta}))),o.type==="done"&&(d=o.message),o.type==="error"&&(d=o.error)}catch(o){const _=qe(o);$===void 0&&o&&typeof o=="object"&&"headers"in o&&Object.keys(Re(o.headers)).length&&($=o.headers);const k=ce({status:o&&typeof o=="object"&&"status"in o?o.status:void 0,message:o instanceof Error?o.message:String(o)});if(l)E=o;else{if(!q&&k.kind!=="rate-limit")throw B.length?new s(503,"upstream_model_unavailable",o instanceof Error?o.message:String(o),void 0,{served_model:h}):o;q=!0,M=_??M}}if(l){if(O===401){if(N&&Oe(N.key),x===0)continue;throw new s(404,"model_unavailable",`${i.base_url} refuses this runtime's tokens`,void 0,{served_model:h})}if(q&&K($)===void 0)throw new s(503,"upstream_model_unavailable",`${h} was rate-limited without a retry-after`,void 0,{served_model:h});if(!q&&(E!==void 0||O!==void 0&&O>=500))throw new s(503,"upstream_model_unavailable",E instanceof Error?E.message:d?.errorMessage??"inference endpoint failed",void 0,{served_model:h})}if(!q&&d?.stopReason==="error"&&ce({message:d.errorMessage}).kind==="rate-limit"&&(q=!0,M=15e3),q){const o=Ie($,a.id);if(K($)===void 0&&o.resetAt!==void 0&&(M=Math.max(M,o.resetAt-Date.now())),b={provider:m,model:a.id,...f?{account:f.label}:{},observation:o},console.error(`[llm] 429 from ${h}${f?` account "${f.label}"`:""}: scope=${le(o.scope)} retry_after_ms=${M} headers=${JSON.stringify(o.headers)}`),f&&H(m)&&await ke(m,f.label,M,v,Date.now(),o.scope),Date.now()+Math.min(M,ae)<R&&(R=Date.now()+Math.min(M,ae),z=h),X.length||D.length)throw new s(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:h});if(l)break;continue}const U=Se(d);if(O===400&&e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(d?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(d?.errorMessage??""))throw new s(400,"invalid_request",d.errorMessage,"tool_choice",{served_model:h});if(!d||d.stopReason==="error"&&!U||d.stopReason==="aborted")throw new s(503,"upstream_model_unavailable",d?.errorMessage??"model request failed",void 0,{served_model:h});f&&H(m)&&$e(f,a.id)&&await je(m,f.label,v,a.id);const Z=d.content.filter(o=>o.type==="toolCall").map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:JSON.stringify(o.arguments)}})),G=U?"content_filter":d.stopReason==="toolUse"?"tool_calls":d.stopReason==="length"?"length":"stop",Q=d.responseModel?Ue(a,d.responseModel):void 0,L=Q?`${a.provider}/${Q.model}`:h,J={prompt_tokens:d.usage.input,completion_tokens:d.usage.output,total_tokens:d.usage.totalTokens,cache_read_tokens:d.usage.cacheRead,cache_write_tokens:d.usage.cacheWrite,usd_equivalent:Te(Q??a,d.usage.cost.total),served_model:L};l?ue(l.grantee,"app",L,d.usage,J.usd_equivalent):w&&ue(w.grantee,"user",L,d.usage,J.usd_equivalent);const ye={id:ee,object:"chat.completion",created:Math.floor(v/1e3),model:L,served_model:L,requested_model:C,substituted:C!==L&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(C),choices:[{index:0,message:{role:"assistant",content:X.join("")||null,...D.length?{reasoning_content:D.join("")}:{},...Z.length?{tool_calls:Z}:{},...U?{refusal:d.errorMessage??"Content filtered"}:{}},finish_reason:G}],usage:J,...e.metadata!==void 0?{metadata:e.metadata}:{}};for(const[o,_]of Z.entries())T(P({tool_calls:[{index:o,..._}]}));return T({...P({},G),...U?{choices:[{index:0,delta:{},finish_reason:G,message:{refusal:d.errorMessage??"Content filtered"}}]}:{},usage:J}),{completion:ye,chunks:B}}}throw R!==1/0?new s(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((R-Date.now())/1e3)),served_model:b?`${b.provider}/${b.model}`:z,...b?{rate_limit:{provider:b.provider,model:b.model,...b.account?{account:b.account}:{},scope:le(b.observation.scope),...b.observation.claim?{claim:b.observation.claim}:{},headers:b.observation.headers}}:{}},void 0,!0):e.model_policy==="exact"?new s(400,"substitution_refused","the requested model is unavailable"):new s(404,"model_unavailable","no usable model route is available")}u(at,"runLlm");export{it as llmCallerOf,st as parseLlmRequest,at as runLlm};
|
|
@@ -20,6 +20,5 @@ export declare function refusalRetryAt(message: {
|
|
|
20
20
|
* too. */
|
|
21
21
|
export declare function wireHistory(messages: Context['messages']): Context['messages'];
|
|
22
22
|
export declare function requestBody(model: Model<any>, context: Context, options: ModelsSimpleStreamOptions, intentOverride?: string): Record<string, unknown>;
|
|
23
|
-
/** One pi stream, backed exclusively by the authenticated daemon llm route. */
|
|
24
|
-
/** `intent` replaces the node's own `CRTR_MODEL_INTENT` for one stream: a background one-shot of an app-paid node asks for `app/light`, not the node's rung. */
|
|
23
|
+
/** One pi stream, backed exclusively by the authenticated daemon llm route. `intent` replaces the node's own `CRTR_MODEL_INTENT` for one stream: a background one-shot of an app-paid node asks for `app/light`, not the node's rung. */
|
|
25
24
|
export declare function streamBrokerModel(model: Model<any>, context: Context, options?: ModelsSimpleStreamOptions, intent?: string): AssistantMessageEventStream;
|
|
@@ -10,6 +10,10 @@ export interface FocusViewerOpts {
|
|
|
10
10
|
pane?: string;
|
|
11
11
|
newPane?: boolean;
|
|
12
12
|
inPlace?: boolean;
|
|
13
|
+
/** With `inPlace`: leave this node's viewer in another pane open instead of closing it. */
|
|
14
|
+
keepOtherViewers?: boolean;
|
|
15
|
+
/** With `inPlace`: the inbox ticket the new viewer opens before showing the conversation. */
|
|
16
|
+
openTicket?: string;
|
|
13
17
|
}
|
|
14
18
|
export interface FocusViewerResult {
|
|
15
19
|
focused: boolean;
|
|
@@ -36,4 +40,6 @@ export declare function focusNode(client: CrtrClient, node: Pick<NodeDetailDTO,
|
|
|
36
40
|
pane: string;
|
|
37
41
|
newPane?: boolean;
|
|
38
42
|
inPlace?: boolean;
|
|
43
|
+
keepOtherViewers?: boolean;
|
|
44
|
+
openTicket?: string;
|
|
39
45
|
}): Promise<FocusNodeResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var A=Object.defineProperty;var o=(t,e)=>A(t,"name",{value:e,configurable:!0});import{envHomeOverride as E}from"../../shared/env.js";import{isFinishedWork as O,isLiveStatus as R}from"../canvas/node-order.js";import{InputError as C}from"../io.js";import{closePane as m,currentTmux as x,displayPopupAsync as y,getPaneOption as P,listLivePanes as H,paneExists as h,paneLocation as l,renameWindow as g,respawnPaneSync as V,selectLayout as W,selectPane as v,selectWindow as $,setPaneOption as L,shellQuote as N,splitWindow as S,switchClient as K,windowOfPane as q}from"./placement-tmux.js";function z(){const t=E();return t!==void 0?{CRTR_HOME:t}:{}}o(z,"viewerEnv");function T(){return{...z(),CRTR_ATTACH_WAKE_PARKED:"0"}}o(T,"focusViewerEnv");function d(t){const e=H();if(e!==null){for(const n of e)if(P(n,"@crtr_node")===t)return n}}o(d,"viewerPaneOf");function _(t,e,n,i,a){const s=S(t,{cwd:n,env:T(),command:`crtr surface attach to ${e}`});if(s===null)return{focused:!1,session:null,inPlace:!1,revived:a};const c=l(s);return c!==null&&W(c.window,"even-horizontal"),v(s),{focused:!0,session:c?.session??i,inPlace:!1,revived:a}}o(_,"openBesideCaller");async function M(t,e,n){const i=n.pane??process.env.TMUX_PANE??x()?.pane;if(i===void 0||i==="")return{focused:!1,session:null,inPlace:!1,revived:!1};const s=(await t.ensureAttach(e,{resume:!0,revive:!0,wakeParked:!1})).revived,c=l(i)?.session??null;if(n.inPlace===!0){const r=P(i,"@crtr_node"),u=d(e);n.keepOtherViewers!==!0&&u!==void 0&&u!==i&&h(u)&&m(u);try{L(i,"@crtr_node","")}catch{}const w=q(i),k={...T(),...r!==void 0&&r!==""?{CRTR_ATTACH_ROSTER:r}:{},...n.openTicket!==void 0?{CRTR_ATTACH_OPEN_TICKET:n.openTicket}:{}},p=V({pane:i,cwd:n.cwd,env:k,command:`crtr surface attach to ${e}`});return p&&w!==null&&g(w,n.name),{focused:p,session:c,inPlace:!0,revived:s}}if(n.newPane===!0){const r=d(e);return r!==void 0&&h(r)&&m(r),_(i,e,n.cwd,c,s)}const f=d(e);if(f!==void 0){const r=l(f);if(r!==null)return K(r.session),$(r.session,r.window),v(f),{focused:!0,session:r.session,inPlace:!0,revived:s}}return _(i,e,n.cwd,c,s)}o(M,"focusViewer");async function U(t,e,n){const i=e.node_id;if(e.kind==="human")throw new C({error:"not_focusable",message:`node ${i} is a human-ask (kind:human), not a conversation \u2014 it has no pi session to focus.`,next:`The pending question is queued for the user; see it with \`crtr human list\`, or retract it with \`crtr human cancel ${i}\`.`});return!R(e.status)&&O({lifecycle:e.lifecycle,finalized:e.final_report!=null})?{focused:await y(n.pane,e.cwd,`crtr node inspect transcript ${N(i)}`),session:null,revived:!1,inPlace:!1}:M(t,i,{cwd:e.cwd,name:e.name,pane:n.pane,...n.newPane===!0?{newPane:!0}:{},...n.inPlace===!0?{inPlace:!0}:{},...n.keepOtherViewers===!0?{keepOtherViewers:!0}:{},...n.openTicket!==void 0?{openTicket:n.openTicket}:{}})}o(U,"focusNode");export{U as focusNode,M as focusViewer,z as viewerEnv,d as viewerPaneOf};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { paneExists, paneLocation, paneOfNode, paneCurrentPath, paneSize, paneRunning, ensureSession, openNodeWindow, splitWindow, closePane, currentTmux, switchClient, selectWindow, selectLayout, selectPane, getPaneOption, setPaneOption, breakPane, inTmux, piCommand, shellQuote, renameWindow, displayPopup, displayPopupAsync, windowOfPane, respawnPaneSync, respawnPaneInBackground, respawnPaneInPlace, runShellInBackground, paneStartCommand, panePid, windowAlive, listLivePanes, VIEWER_STAMP_OPTION, stampViewerPaneAsync, liveViewerPanes, sendAttachControl, displayClientMessage, displayMessage, windowIndex, sessionExists, tmuxAvailable, tmuxInteractive, } from './tmux-driver.js';
|
|
1
|
+
export { paneExists, paneLocation, paneOfNode, paneCurrentPath, paneSize, paneRunning, ensureSession, openNodeWindow, splitWindow, closePane, currentTmux, switchClient, selectWindow, selectLayout, selectPane, getPaneOption, setPaneOption, breakPane, inTmux, piCommand, shellQuote, renameWindow, displayPopup, displayPopupAsync, windowOfPane, respawnPaneSync, respawnPaneInBackground, respawnPaneInPlace, runShellInBackground, paneStartCommand, swapPanes, paneCurrentCommand, closeWindow, panePid, windowAlive, listLivePanes, VIEWER_STAMP_OPTION, stampViewerPaneAsync, liveViewerPanes, sendAttachControl, displayClientMessage, displayMessage, windowIndex, sessionExists, tmuxAvailable, tmuxInteractive, } from './tmux-driver.js';
|
|
2
2
|
export { installTmuxBindings } from './tmux-bindings.js';
|
|
3
3
|
export { BROKER_VIEW_SOCKET_WAIT_MS, waitForBrokerViewSocket } from './view-socket.js';
|
|
4
4
|
/** Env for a `crtr surface attach` VIEWER pane (the focus split, the spawn viewer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var u=Object.defineProperty;var s=(e,n)=>u(e,"name",{value:n,configurable:!0});import{envHomeOverride as w}from"../../shared/env.js";import{paneLocation as m,ensureSession as P,openNodeWindow as T,splitWindow as f,selectLayout as _}from"./tmux-driver.js";import{BROKER_VIEW_SOCKET_WAIT_MS as
|
|
1
|
+
var u=Object.defineProperty;var s=(e,n)=>u(e,"name",{value:n,configurable:!0});import{envHomeOverride as w}from"../../shared/env.js";import{paneLocation as m,ensureSession as P,openNodeWindow as T,splitWindow as f,selectLayout as _}from"./tmux-driver.js";import{BROKER_VIEW_SOCKET_WAIT_MS as C}from"./view-socket.js";import{paneExists as v,paneLocation as y,paneOfNode as g,paneCurrentPath as h,paneSize as B,paneRunning as M,ensureSession as V,openNodeWindow as k,splitWindow as H,closePane as L,currentTmux as N,switchClient as b,selectWindow as K,selectLayout as z,selectPane as F,getPaneOption as Q,setPaneOption as $,breakPane as j,inTmux as q,piCommand as D,shellQuote as G,renameWindow as J,displayPopup as U,displayPopupAsync as X,windowOfPane as Y,respawnPaneSync as Z,respawnPaneInBackground as ee,respawnPaneInPlace as ne,runShellInBackground as oe,paneStartCommand as ie,swapPanes as ae,paneCurrentCommand as te,closeWindow as re,panePid as se,windowAlive as le,listLivePanes as pe,VIEWER_STAMP_OPTION as ce,stampViewerPaneAsync as de,liveViewerPanes as ue,sendAttachControl as we,displayClientMessage as me,displayMessage as Pe,windowIndex as Te,sessionExists as fe,tmuxAvailable as _e,tmuxInteractive as Ce}from"./tmux-driver.js";import{installTmuxBindings as Se}from"./tmux-bindings.js";import{BROKER_VIEW_SOCKET_WAIT_MS as Ae,waitForBrokerViewSocket as xe}from"./view-socket.js";function I(){const e=w();return e!==void 0?{CRTR_HOME:e}:{}}s(I,"viewerSplitEnv");function E(e,n,o={}){const l=`crtr surface attach to ${e}`,p={...I(),...o.waitForBroker===!0?{CRTR_ATTACH_WAIT_MS:String(C)}:{},...o.initialWhip===!0?{CRTR_ATTACH_INITIAL_WHIP:"1"}:{}},i=o.cwd??process.cwd();if(o.besidePane!==void 0){const t=f(o.besidePane,{cwd:i,env:p,command:l});if(t===null)return null;const c=m(t),d=c?.session??n,r=c?.window??null;return r!==null&&_(r,"even-horizontal"),{pane:t,session:d,window:r}}P(n,i);const a=T({session:n,name:o.name??e,cwd:i,env:p,command:l});return a===null?null:{pane:a.pane,session:n,window:a.window}}s(E,"openViewerPane");export{Ae as BROKER_VIEW_SOCKET_WAIT_MS,ce as VIEWER_STAMP_OPTION,j as breakPane,L as closePane,re as closeWindow,N as currentTmux,me as displayClientMessage,Pe as displayMessage,U as displayPopup,X as displayPopupAsync,V as ensureSession,Q as getPaneOption,q as inTmux,Se as installTmuxBindings,pe as listLivePanes,ue as liveViewerPanes,k as openNodeWindow,E as openViewerPane,te as paneCurrentCommand,h as paneCurrentPath,v as paneExists,y as paneLocation,g as paneOfNode,se as panePid,M as paneRunning,B as paneSize,ie as paneStartCommand,D as piCommand,J as renameWindow,ee as respawnPaneInBackground,ne as respawnPaneInPlace,Z as respawnPaneSync,oe as runShellInBackground,z as selectLayout,F as selectPane,K as selectWindow,we as sendAttachControl,fe as sessionExists,$ as setPaneOption,G as shellQuote,H as splitWindow,de as stampViewerPaneAsync,ae as swapPanes,b as switchClient,_e as tmuxAvailable,Ce as tmuxInteractive,I as viewerSplitEnv,xe as waitForBrokerViewSocket,le as windowAlive,Te as windowIndex,Y as windowOfPane};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var Q=Object.defineProperty;var u=(e,n)=>Q(e,"name",{value:n,configurable:!0});import{mkdtempSync as Y,rmSync as Z,writeFileSync as V}from"node:fs";import{tmpdir as O}from"node:os";import{join as M}from"node:path";import{ATTACH_CONTROL_BINDINGS as U,BINDING_CATALOG as _,BINDING_IDS as ee,encodeAttachControlInput as ne,formatGesture as te,isAttachPaneBinding as re,resolveUserKeybindingSettings as q}from"../keybindings/index.js";import{normalizeTmuxPassthrough as ie}from"../config.js";import{withExclusiveDirectoryLock as oe}from"../exclusive-lock.js";import{surfaceTmuxStyleArgs as se}from"./surface-bg.js";import{runTmux as f,shellQuote as h}from"./tmux-driver.js";const A="@crtr_keybindings_manifest",p="root",ue={"crtr.tmux.menu.promote":{description:"promote to orchestrator",action:{kind:"keys",keys:"/promote"}},"crtr.tmux.menu.resume":{description:"find node",action:{kind:"keys",keys:"/resume"}},"crtr.tmux.menu.fork":{description:"fork into idle root",action:{kind:"direct",run:"node fork --pane {pane}"}},"crtr.tmux.menu.demote-mode":{description:"demote to base node",action:{kind:"command",run:"node config --mode base --pane {pane}"}},"crtr.tmux.menu.demote":{description:"demote to terminal (finish when done)",action:{kind:"command",run:"node lifecycle demote --pane {pane}"}},"crtr.tmux.menu.make-resident":{description:"make resident (interactable)",action:{kind:"command",run:"node config --lifecycle resident --pane {pane}"}},"crtr.tmux.menu.detach":{description:"demote + close viewer",action:{kind:"command",run:"node lifecycle demote --pane {pane} --detach"}},"crtr.tmux.menu.close-subtree":{description:"close agent + subtree",action:{kind:"command",run:"node lifecycle close --pane {pane}"}},"crtr.tmux.menu.background-bash":{description:"background running bash",action:{kind:"direct",run:"node bash background --pane {pane}"}},"crtr.tmux.menu.file-review":{description:"review a file",action:{kind:"raw-keys",keys:"M-S-r"}},"crtr.tmux.menu.metadata":{description:"metadata",action:{kind:"keys",keys:"/node-metadata"}},"crtr.tmux.menu.fold-tools":{description:"fold finished tool calls",action:{kind:"keys",keys:"/fold-tools"}},"crtr.tmux.menu.show-working":{description:"show/hide working",action:{kind:"keys",keys:"/show-working"}},"crtr.tmux.menu.context":{description:"context + reports",action:{kind:"keys",keys:"/context"}},"crtr.tmux.menu.providers":{description:"settings",action:{kind:"popup",run:"sys settings",size:{w:"90%",h:"85%"}}},"crtr.tmux.menu.provider-options":{description:"provider options",action:{kind:"popup",run:"sys settings --page provider-options",size:{w:"90%",h:"85%"}}},"crtr.tmux.menu.plugins":{description:"plugins",action:{kind:"popup",run:"pkg browse",size:{w:"90%",h:"85%"}}},"crtr.tmux.menu.analytics":{description:"analytics",action:{kind:"popup",run:"canvas analytics --pane {pane}",size:{w:"90%",h:"85%"}}},"crtr.tmux.menu.context-admin":{description:"context surface",action:{kind:"popup",run:"sys context admin",size:{w:"98%",h:"96%"}}},"crtr.tmux.menu.graph":{description:"canvas graph",action:{kind:"keys",keys:"/graph"}},"crtr.tmux.menu.focus-manager":{description:"go to manager",action:{kind:"command",run:"surface node focus {manager}"}},"crtr.tmux.menu.copy-id":{description:"copy node id",action:{kind:"command",run:"surface node id --pane {pane}"}},"crtr.tmux.menu.reload-viewer":{description:"reload viewer",action:{kind:"direct",run:"surface reload --pane {pane}"}},"crtr.tmux.menu.recap":{description:"generate recap",action:{kind:"keys",keys:"/recap"}},"crtr.tmux.menu.pin":{description:"pin conversation",action:{kind:"direct",run:"surface node pin --pane {pane}"}},"crtr.tmux.menu.issue.idea":{description:"idea",action:{kind:"popup",run:"canvas issue new --type idea"}},"crtr.tmux.menu.issue.bug":{description:"bug",action:{kind:"popup",run:"canvas issue new --type bug"}}};function b(e){return e.replace(/#/g,"##")}u(b,"escapeTmuxFormats");function ce(e){return e.map(h).join(" ")}u(ce,"tmuxCommand");const ae={w:"60%",h:"50%"};function G(e,n=!1,t=ae){const r=`crtr canvas chord --pane '#{pane_id}' --run ${h(b(e))}${n?" --interactive":""}`;return n?{argv:["display-popup","-E","-w",t.w,"-h",t.h,"-d","#{pane_current_path}",r],menu:`display-popup -E -w ${t.w} -h ${t.h} -d "#{pane_current_path}" ${h(r)}`}:{argv:["run-shell",`${r} >/dev/null 2>&1`],menu:`run-shell ${h(`${r} >/dev/null 2>&1`)}`}}u(G,"chordCommand");function de(e){const t=`crtr ${b(e).replace("{pane}","#{pane_id}")} >/dev/null 2>&1`;return{argv:["run-shell","-b",t],menu:`run-shell -b ${h(t)}`}}u(de,"directCommand");function j(e){switch(e.kind){case"disabled":return null;case"separator":return null;case"dismiss":return{argv:["refresh-client"],menu:"refresh-client"};case"keys":{const n=b(e.keys);return{argv:["send-keys",n,"Enter"],menu:`send-keys -t '#{pane_id}' ${h(n)} Enter`}}case"raw-keys":{const n=b(e.keys);return{argv:["send-keys",n],menu:`send-keys -t '#{pane_id}' ${h(n)}`}}case"literal-input":{const n=b(e.input);return{argv:["send-keys","-l",n],menu:`send-keys -l -t '#{pane_id}' ${h(n)}`}}case"command":return G(e.run);case"direct":return de(e.run);case"popup":return G(e.run,!0,e.size);case"submenu":{const n=N(e.title,e.items);return{argv:n,menu:ce(n)}}}}u(j,"menuActionCommand");function me(e){return j(e)?.menu??""}u(me,"renderMenuAction");function pe(e){return e.replace(/#\[[^\]]*\]/g,"").length}u(pe,"menuItemWidth");function N(e,n){const t=b(e),r=n.reduce((o,c)=>Math.max(o,pe(c.description)),0),s=["display-menu","-T",`#[align=centre]${t}`,...se()];s.push("-x",`#{e|-:#{pane_right},${Math.max(t.length,r)+7}}`,"-y","#{e|+:#{pane_top},3}","--");for(const o of n){if(o.action.kind==="separator"){s.push("");continue}const c=o.action.kind==="disabled"?o.description:b(o.description);s.push(c,o.selector,me(o.action))}return s.push("","-esc / q to close","",""),s}u(N,"buildMenuArgs");const le={enter:"Enter",escape:"Escape",tab:"Tab",backspace:"BSpace",delete:"DC",space:"Space",up:"Up",down:"Down",left:"Left",right:"Right",pageup:"PPage",pagedown:"NPage",home:"Home",end:"End"};function z(e){if(e==="+")return{key:"+",modifiers:[]};if(e.endsWith("++"))return{key:"+",modifiers:e.slice(0,-2).split("+")};const n=e.split("+");return{key:n.pop(),modifiers:n}}u(z,"normalizedStrokeParts");function fe(e){return v(e)[0]}u(fe,"tmuxKey");function v(e){let{key:n,modifiers:t}=z(e);const r=new Set(t),s=u((o,c)=>[r.has("ctrl")?"C-":"",r.has("alt")?"M-":"",c?"S-":"",le[o]??o].join(""),"name");if(r.has("shift")&&n.length===1&&/[a-z]/.test(n)){const o=n.toUpperCase();return!r.has("ctrl")&&!r.has("alt")?[s(o,!1)]:[s(o,!0),s(o,!1),s(n,!0)]}return[s(n,r.has("shift"))]}u(v,"tmuxKeyNames");function ge(e){const{key:n,modifiers:t}=z(e);return t.includes("shift")&&/[a-z]/.test(n)?n.toUpperCase():n==="space"?" ":n}u(ge,"menuSelector");function T(e,n,t){const r=[];for(const s of e){const o=n.get(s);if(o!==void 0)for(const c of t.gestures(s))r.push({description:o.description,selector:ge(c),action:o.action})}return r}u(T,"boundMenuItems");function F(e){const n=new Map;for(const[c,l]of Object.entries(ue))l!==void 0&&n.set(c,l);for(const{menuId:c,actionId:l}of U){const x=_.find(w=>w.id===l);if(x===void 0)throw new Error(`missing attach binding definition ${l}`);n.set(c,{description:x.name.toLowerCase(),action:{kind:"literal-input",input:ne(l)}})}const t=T(U.map(({menuId:c})=>c),n,e);t.length>0&&n.set("crtr.tmux.menu.attach",{description:"attach viewer \u203A",action:{kind:"submenu",title:" attach viewer ",items:t}});const r=T(["crtr.tmux.menu.metadata","crtr.tmux.menu.context"],n,e);n.set("crtr.tmux.menu.show",{description:"inspect \u203A",action:{kind:"submenu",title:" inspect node ",items:r}});const s=T(["crtr.tmux.menu.fold-tools","crtr.tmux.menu.show-working"],n,e);n.set("crtr.tmux.menu.view-settings",{description:"display \u203A",action:{kind:"submenu",title:" display ",items:s}});const o=T(["crtr.tmux.menu.issue.idea","crtr.tmux.menu.issue.bug"],n,e);return n.set("crtr.tmux.menu.issues",{description:"create \u203A",action:{kind:"submenu",title:" create ",items:o}}),n}u(F,"resolvedMenuActions");function K(e,n){const t=u(c=>T(c,n,e),"boundItems"),r=[],s=u((c,l)=>{l.length!==0&&(r.length>0&&r.push({description:"",selector:"",action:{kind:"separator"}}),r.push({description:`-#[bold]${c}`,selector:"",action:{kind:"disabled"}},...l))},"addGroup");s("Explore",t(["crtr.tmux.menu.resume","crtr.tmux.menu.graph","crtr.tmux.menu.focus-manager","crtr.tmux.menu.show","crtr.tmux.menu.view-settings"])),s("Current node",t(["crtr.tmux.menu.attach","crtr.tmux.menu.background-bash","crtr.tmux.menu.file-review","crtr.tmux.menu.pin","crtr.tmux.menu.copy-id","crtr.tmux.menu.reload-viewer","crtr.tmux.menu.recap"])),s("Lifecycle",t(["crtr.tmux.menu.fork","crtr.tmux.menu.promote","crtr.tmux.menu.demote-mode","crtr.tmux.menu.demote","crtr.tmux.menu.make-resident","crtr.tmux.menu.detach","crtr.tmux.menu.close-subtree"])),s("System",t(["crtr.tmux.menu.providers","crtr.tmux.menu.provider-options","crtr.tmux.menu.plugins","crtr.tmux.menu.analytics","crtr.tmux.menu.context-admin","crtr.tmux.menu.issues"]));const o=e.gestures("crtr.tmux.menu.open");if(o.length>0&&r.length>0){r.push({description:"",selector:"",action:{kind:"separator"}});for(const c of o)r.push({description:`${te(c)} to close`,selector:fe(c),action:{kind:"dismiss"}})}return r}u(K,"menuItems");function Pe(e){const n=e??q().bindings;return N(" crtr ",K(n,F(n)))}u(Pe,"crouterMenuTmuxArgs");function S(e,n){let t=n;for(;t<e.length&&/\s/.test(e[t]);)t+=1;if(t>=e.length)return null;let r=null,s="";for(;t<e.length;){const o=e[t];if(r===null&&/\s/.test(o))break;if(r===null&&(o==="'"||o==='"')){r=o,t+=1;continue}if(r===o){r=null,t+=1;continue}if(o==="\\"&&r!=="'"&&t+1<e.length){s+=e[t+1],t+=2;continue}s+=o,t+=1}return{value:s,end:t}}u(S,"readTmuxToken");function B(e){const n=S(e,0);if(n?.value!=="bind-key")return null;let t=n.end,r=!1;for(;;){const s=S(e,t);if(s===null)return null;if(t=s.end,s.value==="-r"){r=!0;continue}if(s.value!=="-T")continue;const o=S(e,t);if(o===null)return null;const c=S(e,o.end);return c===null?null:{table:o.value,key:c.value,command:e.slice(c.end).trimStart(),repeat:r}}}u(B,"parseTmuxBinding");function $(e,n){const t=f(["list-keys","-T",e]);return t.ok?t.stdout.split(`
|
|
1
|
+
var Q=Object.defineProperty;var u=(e,n)=>Q(e,"name",{value:n,configurable:!0});import{mkdtempSync as Y,rmSync as Z,writeFileSync as V}from"node:fs";import{tmpdir as O}from"node:os";import{join as M}from"node:path";import{ATTACH_CONTROL_BINDINGS as U,BINDING_CATALOG as _,BINDING_IDS as ee,encodeAttachControlInput as ne,formatGesture as te,isAttachPaneBinding as re,resolveUserKeybindingSettings as q}from"../keybindings/index.js";import{normalizeTmuxPassthrough as ie}from"../config.js";import{withExclusiveDirectoryLock as oe}from"../exclusive-lock.js";import{surfaceTmuxStyleArgs as se}from"./surface-bg.js";import{runTmux as f,shellQuote as k}from"./tmux-driver.js";const A="@crtr_keybindings_manifest",p="root",ue={"crtr.tmux.menu.promote":{description:"promote to orchestrator",action:{kind:"keys",keys:"/promote"}},"crtr.tmux.menu.resume":{description:"find node",action:{kind:"keys",keys:"/resume"}},"crtr.tmux.menu.dashboard":{description:"dashboard",action:{kind:"direct",run:"surface dashboard --pane {pane}"}},"crtr.tmux.menu.fork":{description:"fork into idle root",action:{kind:"direct",run:"node fork --pane {pane}"}},"crtr.tmux.menu.demote-mode":{description:"demote to base node",action:{kind:"command",run:"node config --mode base --pane {pane}"}},"crtr.tmux.menu.demote":{description:"demote to terminal (finish when done)",action:{kind:"command",run:"node lifecycle demote --pane {pane}"}},"crtr.tmux.menu.make-resident":{description:"make resident (interactable)",action:{kind:"command",run:"node config --lifecycle resident --pane {pane}"}},"crtr.tmux.menu.detach":{description:"demote + close viewer",action:{kind:"command",run:"node lifecycle demote --pane {pane} --detach"}},"crtr.tmux.menu.close-subtree":{description:"close agent + subtree",action:{kind:"command",run:"node lifecycle close --pane {pane}"}},"crtr.tmux.menu.background-bash":{description:"background running bash",action:{kind:"direct",run:"node bash background --pane {pane}"}},"crtr.tmux.menu.file-review":{description:"review a file",action:{kind:"raw-keys",keys:"M-S-r"}},"crtr.tmux.menu.metadata":{description:"metadata",action:{kind:"keys",keys:"/node-metadata"}},"crtr.tmux.menu.fold-tools":{description:"fold finished tool calls",action:{kind:"keys",keys:"/fold-tools"}},"crtr.tmux.menu.show-working":{description:"show/hide working",action:{kind:"keys",keys:"/show-working"}},"crtr.tmux.menu.context":{description:"context + reports",action:{kind:"keys",keys:"/context"}},"crtr.tmux.menu.providers":{description:"settings",action:{kind:"popup",run:"sys settings",size:{w:"90%",h:"85%"}}},"crtr.tmux.menu.provider-options":{description:"provider options",action:{kind:"popup",run:"sys settings --page provider-options",size:{w:"90%",h:"85%"}}},"crtr.tmux.menu.plugins":{description:"plugins",action:{kind:"popup",run:"pkg browse",size:{w:"90%",h:"85%"}}},"crtr.tmux.menu.analytics":{description:"analytics",action:{kind:"popup",run:"canvas analytics --pane {pane}",size:{w:"90%",h:"85%"}}},"crtr.tmux.menu.context-admin":{description:"context surface",action:{kind:"popup",run:"sys context admin",size:{w:"98%",h:"96%"}}},"crtr.tmux.menu.graph":{description:"canvas graph",action:{kind:"keys",keys:"/graph"}},"crtr.tmux.menu.focus-manager":{description:"go to manager",action:{kind:"command",run:"surface node focus {manager}"}},"crtr.tmux.menu.copy-id":{description:"copy node id",action:{kind:"command",run:"surface node id --pane {pane}"}},"crtr.tmux.menu.reload-viewer":{description:"reload viewer",action:{kind:"direct",run:"surface reload --pane {pane}"}},"crtr.tmux.menu.recap":{description:"generate recap",action:{kind:"keys",keys:"/recap"}},"crtr.tmux.menu.pin":{description:"pin conversation",action:{kind:"direct",run:"surface node pin --pane {pane}"}},"crtr.tmux.menu.issue.idea":{description:"idea",action:{kind:"popup",run:"canvas issue new --type idea"}},"crtr.tmux.menu.issue.bug":{description:"bug",action:{kind:"popup",run:"canvas issue new --type bug"}}};function b(e){return e.replace(/#/g,"##")}u(b,"escapeTmuxFormats");function ce(e){return e.map(k).join(" ")}u(ce,"tmuxCommand");const ae={w:"60%",h:"50%"};function G(e,n=!1,t=ae){const r=`crtr canvas chord --pane '#{pane_id}' --run ${k(b(e))}${n?" --interactive":""}`;return n?{argv:["display-popup","-E","-w",t.w,"-h",t.h,"-d","#{pane_current_path}",r],menu:`display-popup -E -w ${t.w} -h ${t.h} -d "#{pane_current_path}" ${k(r)}`}:{argv:["run-shell",`${r} >/dev/null 2>&1`],menu:`run-shell ${k(`${r} >/dev/null 2>&1`)}`}}u(G,"chordCommand");function de(e){const t=`crtr ${b(e).replace("{pane}","#{pane_id}")} >/dev/null 2>&1`;return{argv:["run-shell","-b",t],menu:`run-shell -b ${k(t)}`}}u(de,"directCommand");function j(e){switch(e.kind){case"disabled":return null;case"separator":return null;case"dismiss":return{argv:["refresh-client"],menu:"refresh-client"};case"keys":{const n=b(e.keys);return{argv:["send-keys",n,"Enter"],menu:`send-keys -t '#{pane_id}' ${k(n)} Enter`}}case"raw-keys":{const n=b(e.keys);return{argv:["send-keys",n],menu:`send-keys -t '#{pane_id}' ${k(n)}`}}case"literal-input":{const n=b(e.input);return{argv:["send-keys","-l",n],menu:`send-keys -l -t '#{pane_id}' ${k(n)}`}}case"command":return G(e.run);case"direct":return de(e.run);case"popup":return G(e.run,!0,e.size);case"submenu":{const n=N(e.title,e.items);return{argv:n,menu:ce(n)}}}}u(j,"menuActionCommand");function me(e){return j(e)?.menu??""}u(me,"renderMenuAction");function pe(e){return e.replace(/#\[[^\]]*\]/g,"").length}u(pe,"menuItemWidth");function N(e,n){const t=b(e),r=n.reduce((o,c)=>Math.max(o,pe(c.description)),0),s=["display-menu","-T",`#[align=centre]${t}`,...se()];s.push("-x",`#{e|-:#{pane_right},${Math.max(t.length,r)+7}}`,"-y","#{e|+:#{pane_top},3}","--");for(const o of n){if(o.action.kind==="separator"){s.push("");continue}const c=o.action.kind==="disabled"?o.description:b(o.description);s.push(c,o.selector,me(o.action))}return s.push("","-esc / q to close","",""),s}u(N,"buildMenuArgs");const le={enter:"Enter",escape:"Escape",tab:"Tab",backspace:"BSpace",delete:"DC",space:"Space",up:"Up",down:"Down",left:"Left",right:"Right",pageup:"PPage",pagedown:"NPage",home:"Home",end:"End"};function z(e){if(e==="+")return{key:"+",modifiers:[]};if(e.endsWith("++"))return{key:"+",modifiers:e.slice(0,-2).split("+")};const n=e.split("+");return{key:n.pop(),modifiers:n}}u(z,"normalizedStrokeParts");function fe(e){return v(e)[0]}u(fe,"tmuxKey");function v(e){let{key:n,modifiers:t}=z(e);const r=new Set(t),s=u((o,c)=>[r.has("ctrl")?"C-":"",r.has("alt")?"M-":"",c?"S-":"",le[o]??o].join(""),"name");if(r.has("shift")&&n.length===1&&/[a-z]/.test(n)){const o=n.toUpperCase();return!r.has("ctrl")&&!r.has("alt")?[s(o,!1)]:[s(o,!0),s(o,!1),s(n,!0)]}return[s(n,r.has("shift"))]}u(v,"tmuxKeyNames");function ge(e){const{key:n,modifiers:t}=z(e);return t.includes("shift")&&/[a-z]/.test(n)?n.toUpperCase():n==="space"?" ":n}u(ge,"menuSelector");function T(e,n,t){const r=[];for(const s of e){const o=n.get(s);if(o!==void 0)for(const c of t.gestures(s))r.push({description:o.description,selector:ge(c),action:o.action})}return r}u(T,"boundMenuItems");function F(e){const n=new Map;for(const[c,l]of Object.entries(ue))l!==void 0&&n.set(c,l);for(const{menuId:c,actionId:l}of U){const x=_.find(w=>w.id===l);if(x===void 0)throw new Error(`missing attach binding definition ${l}`);n.set(c,{description:x.name.toLowerCase(),action:{kind:"literal-input",input:ne(l)}})}const t=T(U.map(({menuId:c})=>c),n,e);t.length>0&&n.set("crtr.tmux.menu.attach",{description:"attach viewer \u203A",action:{kind:"submenu",title:" attach viewer ",items:t}});const r=T(["crtr.tmux.menu.metadata","crtr.tmux.menu.context"],n,e);n.set("crtr.tmux.menu.show",{description:"inspect \u203A",action:{kind:"submenu",title:" inspect node ",items:r}});const s=T(["crtr.tmux.menu.fold-tools","crtr.tmux.menu.show-working"],n,e);n.set("crtr.tmux.menu.view-settings",{description:"display \u203A",action:{kind:"submenu",title:" display ",items:s}});const o=T(["crtr.tmux.menu.issue.idea","crtr.tmux.menu.issue.bug"],n,e);return n.set("crtr.tmux.menu.issues",{description:"create \u203A",action:{kind:"submenu",title:" create ",items:o}}),n}u(F,"resolvedMenuActions");function K(e,n){const t=u(c=>T(c,n,e),"boundItems"),r=[],s=u((c,l)=>{l.length!==0&&(r.length>0&&r.push({description:"",selector:"",action:{kind:"separator"}}),r.push({description:`-#[bold]${c}`,selector:"",action:{kind:"disabled"}},...l))},"addGroup");s("Explore",t(["crtr.tmux.menu.dashboard","crtr.tmux.menu.resume","crtr.tmux.menu.graph","crtr.tmux.menu.focus-manager","crtr.tmux.menu.show","crtr.tmux.menu.view-settings"])),s("Current node",t(["crtr.tmux.menu.attach","crtr.tmux.menu.background-bash","crtr.tmux.menu.file-review","crtr.tmux.menu.pin","crtr.tmux.menu.copy-id","crtr.tmux.menu.reload-viewer","crtr.tmux.menu.recap"])),s("Lifecycle",t(["crtr.tmux.menu.fork","crtr.tmux.menu.promote","crtr.tmux.menu.demote-mode","crtr.tmux.menu.demote","crtr.tmux.menu.make-resident","crtr.tmux.menu.detach","crtr.tmux.menu.close-subtree"])),s("System",t(["crtr.tmux.menu.providers","crtr.tmux.menu.provider-options","crtr.tmux.menu.plugins","crtr.tmux.menu.analytics","crtr.tmux.menu.context-admin","crtr.tmux.menu.issues"]));const o=e.gestures("crtr.tmux.menu.open");if(o.length>0&&r.length>0){r.push({description:"",selector:"",action:{kind:"separator"}});for(const c of o)r.push({description:`${te(c)} to close`,selector:fe(c),action:{kind:"dismiss"}})}return r}u(K,"menuItems");function Pe(e){const n=e??q().bindings;return N(" crtr ",K(n,F(n)))}u(Pe,"crouterMenuTmuxArgs");function S(e,n){let t=n;for(;t<e.length&&/\s/.test(e[t]);)t+=1;if(t>=e.length)return null;let r=null,s="";for(;t<e.length;){const o=e[t];if(r===null&&/\s/.test(o))break;if(r===null&&(o==="'"||o==='"')){r=o,t+=1;continue}if(r===o){r=null,t+=1;continue}if(o==="\\"&&r!=="'"&&t+1<e.length){s+=e[t+1],t+=2;continue}s+=o,t+=1}return{value:s,end:t}}u(S,"readTmuxToken");function B(e){const n=S(e,0);if(n?.value!=="bind-key")return null;let t=n.end,r=!1;for(;;){const s=S(e,t);if(s===null)return null;if(t=s.end,s.value==="-r"){r=!0;continue}if(s.value!=="-T")continue;const o=S(e,t);if(o===null)return null;const c=S(e,o.end);return c===null?null:{table:o.value,key:c.value,command:e.slice(c.end).trimStart(),repeat:r}}}u(B,"parseTmuxBinding");function $(e,n){const t=f(["list-keys","-T",e]);return t.ok?t.stdout.split(`
|
|
2
2
|
`).find(r=>{const s=B(r);return s?.table===e&&s.key===n})??null:null}u($,"bindingAt");function Le(e){const n=f(["list-keys","-T",p]);if(!n.ok)return[];const t=new Set(n.stdout.split(`
|
|
3
|
-
`).flatMap(r=>{const s=B(r);return s?.table===p?[s.key]:[]}));return e.filter(r=>!r.includes(" > ")&&v(r).some(s=>t.has(s)))}u(Le,"occupiedTmuxRootGestures");function R(e){return typeof e=="object"&&e!==null&&typeof e.table=="string"&&typeof e.key=="string"}u(R,"isInstalledPair");function
|
|
4
|
-
`,"utf8"),f(["source-file",t])}finally{Z(n,{recursive:!0,force:!0})}}u(be,"sourceBindingLine");function X(e){return e.original===null?f(["unbind-key","-T",p,C(e.key)]):be(e.original)}u(X,"restoreWrappedBinding");function we(){return["run-shell","-b",'crtr surface inbox --quiet --tmux-socket "#{socket_path}" --tmux-client "#{client_name}" --target-pane "#{pane_id}"']}u(we,"inboxToggleTmuxCommand");function ve(e){switch(e){case"crtr.tmux.node.next":return["run-shell","crtr surface node cycle --dir next --pane '#{pane_id}' >/dev/null 2>&1"];case"crtr.tmux.node.previous":return["run-shell","crtr surface node cycle --dir prev --pane '#{pane_id}' >/dev/null 2>&1"];case"crtr.tmux.inbox.toggle":return we();default:throw new Error(`no tmux command for ${e}`)}}u(ve,"actionCommand");function $e(){const e=process.env.TMUX?.split(",")[0]?.trim();if(e!==void 0&&e!=="")return`${e}.crtr-keybindings.lock`;const n=f(["display-message","-p","#{socket_path}"]);if(n.ok&&n.stdout!=="")return`${n.stdout}.crtr-keybindings.lock`;const t=typeof process.getuid=="function"?process.getuid():"user";return M(O(),`crtr-tmux-keybindings-${t}.lock`)}u($e,"tmuxBindingLockPath");function De(e,n=[]){return oe($e(),()=>Ie(e,n))}u(De,"installTmuxBindings");function Ie(e,n=[]){const t=e===void 0?q():void 0,r=e??t.bindings,s=e===void 0?t.tmuxPassthrough:ie(n),o=[],c=[],l=
|
|
3
|
+
`).flatMap(r=>{const s=B(r);return s?.table===p?[s.key]:[]}));return e.filter(r=>!r.includes(" > ")&&v(r).some(s=>t.has(s)))}u(Le,"occupiedTmuxRootGestures");function R(e){return typeof e=="object"&&e!==null&&typeof e.table=="string"&&typeof e.key=="string"}u(R,"isInstalledPair");function he(e){if(typeof e!="object"||e===null)return!1;const n=e;return typeof n.bindingId=="string"&&ee.has(n.bindingId)&&typeof n.gesture=="string"&&typeof n.key=="string"&&(typeof n.original=="string"||n.original===null)&&typeof n.wrapper=="string"&&(n.state==="owned"||n.state==="relinquished")}u(he,"isWrappedRootBinding");function ke(){const e=f(["show-options","-s","-q",A]);if(!e.ok)return null;if(e.stdout==="")return{pairs:[],wrappers:[]};const n=f(["show-options","-s","-qv",A]);if(!n.ok)return null;try{const t=JSON.parse(n.stdout);if(Array.isArray(t))return t.every(R)?{pairs:t,wrappers:[]}:null;if(typeof t!="object"||t===null)return null;const r=t;return!Array.isArray(r.pairs)||!r.pairs.every(R)||!Array.isArray(r.wrappers)||!r.wrappers.every(he)?null:{pairs:r.pairs,wrappers:r.wrappers}}catch{return null}}u(ke,"readManifest");function W(e){return f(["set-option","-s",A,JSON.stringify(e)])}u(W,"writeManifest");function C(e){return e.replace(/\\/g,"\\\\").replace(/;/g,"\\;")}u(C,"tmuxKeyArgument");function xe(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/;/g,"\\;")}"`}u(xe,"tmuxCommandToken");function ye(e){let n=null,t="";for(let r=0;r<e.length;r+=1){const s=e[r];if(n===null&&(s==="'"||s==='"')){n=s,t+=s;continue}if(n===s){n=null,t+=s;continue}if(s==="\\"&&r+1<e.length){const o=e[r+1];n===null&&o===";"?t+=";":t+=s+o,r+=1;continue}t+=s}return t}u(ye,"bindingCommandForBranch");function be(e){const n=Y(M(O(),"crtr-tmux-binding-")),t=M(n,"binding.conf");try{return V(t,`${e}
|
|
4
|
+
`,"utf8"),f(["source-file",t])}finally{Z(n,{recursive:!0,force:!0})}}u(be,"sourceBindingLine");function X(e){return e.original===null?f(["unbind-key","-T",p,C(e.key)]):be(e.original)}u(X,"restoreWrappedBinding");function we(){return["run-shell","-b",'crtr surface inbox --quiet --tmux-socket "#{socket_path}" --tmux-client "#{client_name}" --target-pane "#{pane_id}"']}u(we,"inboxToggleTmuxCommand");function ve(e){switch(e){case"crtr.tmux.node.next":return["run-shell","crtr surface node cycle --dir next --pane '#{pane_id}' >/dev/null 2>&1"];case"crtr.tmux.node.previous":return["run-shell","crtr surface node cycle --dir prev --pane '#{pane_id}' >/dev/null 2>&1"];case"crtr.tmux.inbox.toggle":return we();default:throw new Error(`no tmux command for ${e}`)}}u(ve,"actionCommand");function $e(){const e=process.env.TMUX?.split(",")[0]?.trim();if(e!==void 0&&e!=="")return`${e}.crtr-keybindings.lock`;const n=f(["display-message","-p","#{socket_path}"]);if(n.ok&&n.stdout!=="")return`${n.stdout}.crtr-keybindings.lock`;const t=typeof process.getuid=="function"?process.getuid():"user";return M(O(),`crtr-tmux-keybindings-${t}.lock`)}u($e,"tmuxBindingLockPath");function De(e,n=[]){return oe($e(),()=>Ie(e,n))}u(De,"installTmuxBindings");function Ie(e,n=[]){const t=e===void 0?q():void 0,r=e??t.bindings,s=e===void 0?t.tmuxPassthrough:ie(n),o=[],c=[],l=ke();if(l===null)return o.push({code:"tmux-error",message:`tmux is unavailable or ${A} is malformed`}),{ok:!1,installed:c,diagnostics:o};const x=new Map;for(const i of s){const a=_.find(d=>d.id===i);if(!(a===void 0||!re(a))){for(const d of r.gestures(i))if(!d.includes(" > "))for(const m of v(d))x.has(m)||x.set(m,{bindingId:i,gesture:d})}}const w=new Set,I=[];for(const i of l.wrappers){const a=x.get(i.key),d=$(p,i.key);if(d!==null&&d===i.original)continue;if(i.state==="relinquished"||d!==i.wrapper){w.add(i.key),a!==void 0&&I.push({...i,...a,state:"relinquished"});continue}const m=X(i);m.ok||(w.add(i.key),I.push(i),o.push({code:"tmux-error",bindingId:i.bindingId,gesture:i.gesture,message:`could not restore tmux binding ${p}:${i.key}: ${m.stderr}`}))}for(const i of l.pairs){if(i.table===p&&w.has(i.key))continue;const a=f(["unbind-key","-T",i.table,C(i.key)]);!a.ok&&$(i.table,i.key)!==null&&o.push({code:"tmux-error",message:`could not remove crouter binding ${i.table}:${i.key}: ${a.stderr}`})}if(o.some(i=>i.code==="tmux-error")){const i=W({pairs:l.pairs,wrappers:I});return i.ok||o.push({code:"tmux-error",message:`could not save crouter tmux ownership manifest: ${i.stderr}`}),{ok:!1,installed:c,diagnostics:o}}const E=u((i,a,d,m,h)=>{if($(i,a)!==null)return o.push({code:"occupied",bindingId:m,gesture:h,message:`${i}:${a} is already bound by tmux; ${m} was not installed`}),!1;const g=f(["bind-key","-T",i,C(a),...d]);return g.ok?(c.push({table:i,key:a}),!0):(o.push({code:"tmux-error",bindingId:m,gesture:h,message:`could not install ${m}: ${g.stderr}`}),!1)},"install"),P=F(r),H=N(" crtr ",K(r,P));for(const i of r.gestures("crtr.tmux.menu.open"))for(const a of v(i))E(p,a,H,"crtr.tmux.menu.open",i);for(const i of _){if(i.pair?.field!=="menu")continue;const a=_.find(y=>y.id===i.pair?.pairedId);if(a?.pair?.field!=="global")continue;const d=i.id,m=a.id,h=P.get(d),g=h===void 0?null:j(h.action);if(g===null)throw new Error(`no direct tmux action for paired menu binding ${d}`);for(const y of r.gestures(m))for(const J of v(y))E(p,J,g.argv,m,y)}for(const i of["crtr.tmux.node.next","crtr.tmux.node.previous","crtr.tmux.inbox.toggle"])for(const a of r.gestures(i))for(const d of v(a))E(p,d,ve(i),i,a);const L=[];for(const[i,a]of x){if(w.has(i))continue;const d=$(p,i);if(d===null)continue;const m=B(d);if(m===null||m.command===""){o.push({code:"tmux-error",bindingId:a.bindingId,gesture:a.gesture,message:`could not parse occupied tmux binding ${p}:${i}`});continue}const h=f(["bind-key",...m.repeat?["-r"]:[],"-T",p,C(i),"if-shell","-F","#{!=:#{@crtr_attach},}",`send-keys ${xe(i)}`,ye(m.command)]);if(!h.ok){o.push({code:"tmux-error",bindingId:a.bindingId,gesture:a.gesture,message:`could not wrap ${p}:${i}: ${h.stderr}`});continue}const g=$(p,i);if(g===null||!g.includes("@crtr_attach")){o.push({code:"tmux-error",bindingId:a.bindingId,gesture:a.gesture,message:`could not fingerprint wrapped tmux binding ${p}:${i}`});continue}const y={...a,key:i,original:d,wrapper:g,state:"owned"};I.push(y),L.push(y)}const D=W({pairs:c,wrappers:I});if(!D.ok){o.push({code:"tmux-error",message:`could not save crouter tmux ownership manifest: ${D.stderr}`});for(const i of L)$(p,i.key)===i.wrapper&&X(i)}return{ok:o.every(i=>i.code!=="tmux-error"),installed:c,diagnostics:o}}u(Ie,"reconcileTmuxBindings");export{Pe as crouterMenuTmuxArgs,we as inboxToggleTmuxCommand,De as installTmuxBindings,Le as occupiedTmuxRootGestures};
|
|
@@ -260,6 +260,12 @@ export declare function runShellInBackground(command: string): boolean;
|
|
|
260
260
|
* — the exact command `respawn-pane` with no argument re-runs. undefined when
|
|
261
261
|
* tmux is unreachable or the pane is gone. */
|
|
262
262
|
export declare function paneStartCommand(pane: string): string | undefined;
|
|
263
|
+
/** Swap two panes' places on screen (`swap-pane -s a -t b`). The panes keep their ids and
|
|
264
|
+
* processes; only their positions (and windows) exchange. Returns tmux's error text on
|
|
265
|
+
* failure, null on success. */
|
|
266
|
+
export declare function swapPanes(source: string, target: string): string | null;
|
|
267
|
+
/** The foreground command name of a pane (`#{pane_current_command}`). */
|
|
268
|
+
export declare function paneCurrentCommand(pane: string): string | undefined;
|
|
263
269
|
/** The pane's root process pid (`#{pane_pid}`) — the process tmux launched in
|
|
264
270
|
* the pane (the viewer itself, or the login shell hosting it). undefined when
|
|
265
271
|
* tmux is unreachable or the pane is gone. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var O=Object.defineProperty;var r=(n,t)=>O(n,"name",{value:t,configurable:!0});import{execFile as w,spawnSync as
|
|
2
|
-
`)){const s=e.indexOf(" ");if(!(s<0)&&e.slice(s+1)===n){const u=e.slice(0,s);if(u!=="")return u}}return null}r(nn,"paneOfNode");function T(){if(!M())return null;const n=o(["display-message","-p","#{session_name} #{window_id} #{pane_id}"]);if(!n.ok)return null;const[t,e,s]=n.stdout.split(" ");return{session:t,window:e,pane:s}}r(T,"currentTmux");function tn(){const n=
|
|
1
|
+
var O=Object.defineProperty;var r=(n,t)=>O(n,"name",{value:t,configurable:!0});import{execFile as w,spawnSync as d}from"node:child_process";import{encodeAttachControlInput as C}from"../keybindings/index.js";import{listProcessTable as E}from"../canvas/pid.js";import{bundledPiCommandForShell as $}from"./pi-cli.js";import{shellQuote as l}from"../../shared/shell-quote.js";import{envPiBinaryOverride as N}from"../../shared/env.js";function o(n){const t=d("tmux",n,{encoding:"utf8"});return{ok:t.status===0,stdout:(t.stdout??"").trim(),stderr:(t.stderr??"").trim()}}r(o,"tmux");function p(n,t){return o(n===void 0||n===""?t:["-S",n,...t])}r(p,"tmuxOnSocket");function M(){return process.env.TMUX!==void 0&&process.env.TMUX!==""}r(M,"inTmux");function Y(){return d("tmux",["list-sessions"],{encoding:"utf8",stdio:"ignore"}).status===0}r(Y,"tmuxSessionsReachable");function Z(){return d("tmux",["list-clients","-F","#{client_name}"],{encoding:"utf8"}).status===0}r(Z,"tmuxClientsReachable");function nn(n){if(n==="")return null;const t=o(["list-panes","-a","-F","#{pane_id} #{@crtr_node}"]);if(!t.ok)return null;for(const e of t.stdout.split(`
|
|
2
|
+
`)){const s=e.indexOf(" ");if(!(s<0)&&e.slice(s+1)===n){const u=e.slice(0,s);if(u!=="")return u}}return null}r(nn,"paneOfNode");function T(){if(!M())return null;const n=o(["display-message","-p","#{session_name} #{window_id} #{pane_id}"]);if(!n.ok)return null;const[t,e,s]=n.stdout.split(" ");return{session:t,window:e,pane:s}}r(T,"currentTmux");function tn(){const n=d("tmux",["-V"],{stdio:"ignore"});return n.error===void 0&&n.status===0}r(tn,"tmuxAvailable");function en(n){return d("tmux",n,{stdio:"inherit"}).status??1}r(en,"tmuxInteractive");function F(n){return o(["has-session","-t",n]).ok}r(F,"sessionExists");function A(){const n=o(["show-options","-s","terminal-overrides"]).stdout;/Enbp=\\+E/.test(n)||o(["set-option","-sga","terminal-overrides","*:Enbp=\\E[?2004h:Dsbp=\\E[?2004l"])}r(A,"ensureBracketedPaste");function R(){A(),o(["set-option","-s","focus-events","on"])}r(R,"ensureServerOptions");function rn(n,t){F(n)||o(["new-session","-d","-s",n,"-c",t]),R()}r(rn,"ensureSession");function x(n){const t=[];for(const[e,s]of Object.entries(n))t.push("-e",`${e}=${s}`);return t}r(x,"envFlags");function on(n){const t=o(["new-window","-d","-a","-P","-F","#{window_id} #{pane_id}","-t",`${n.session}:`,"-n",n.name,"-c",n.cwd,...x(n.env),n.command]);if(!t.ok)return null;const[e,s]=t.stdout.split(" ");return e===void 0||e===""||s===void 0||s===""?null:{window:e,pane:s}}r(on,"openNodeWindow");function sn(n,t){const e=o(["split-window","-d",...t.vertical===!0?[]:["-h"],...t.before===!0?["-b"]:[],...t.size!==void 0?["-l",String(t.size)]:[],"-P","-F","#{pane_id}","-t",n,"-c",t.cwd,...x(t.env),t.command]);return e.ok&&e.stdout!==""?e.stdout:null}r(sn,"splitWindow");function un(n,t){return!(T()?.session===n)&&!o(["switch-client","-t",n]).ok?!1:o(["select-window","-t",t]).ok}r(un,"focusWindow");function an(n){return o(["kill-window","-t",n]).ok}r(an,"closeWindow");function cn(n,t){return o(["rename-window","-t",n,t]).ok}r(cn,"renameWindow");function dn(n){const t=o(["break-pane","-d","-P","-F","#{window_id} #{pane_id}","-s",n]);if(!t.ok)return null;const[e,s]=t.stdout.split(" ");return e===void 0||e===""||s===void 0||s===""?null:{window:e,pane:s}}r(dn,"breakPane");function pn(n){return o(["kill-pane","-t",n]).ok}r(pn,"closePane");function ln(n,t,e){return o(h(n,t,e)).ok}r(ln,"displayPopup");function fn(n,t,e){return new Promise(s=>{try{w("tmux",h(n,t,e),i=>s(i===null)).stdin?.end()}catch{s(!1)}})}r(fn,"displayPopupAsync");function h(n,t,e){return["display-popup","-E","-t",n,"-d",t,"-w","90%","-h","90%",e]}r(h,"displayPopupArgs");function mn(n,t){const e=o(["display-message","-p","-t",`${n}:${t}`,"#{pane_id}"]);return e.ok&&e.stdout!==""?e.stdout:null}r(mn,"paneOfWindow");function wn(n){const t=o(["display-message","-p","-t",n,"#{window_id}"]);return t.ok&&t.stdout!==""?t.stdout:null}r(wn,"windowOfPane");function xn(n){const t=o(["display-message","-p","-t",n,"#{session_name} #{window_id}"]);if(!t.ok)return null;const e=/^(.*) (@\d+)$/.exec(t.stdout);return e===null||e[1]===""?null:{session:e[1],window:e[2]}}r(xn,"paneLocation");function kn(n){const t=o(["display-message","-p","-t",n,"#{window_index}"]);return!t.ok||!/^\d+$/.test(t.stdout)?null:Number.parseInt(t.stdout,10)}r(kn,"windowIndex");function gn(n){const t=o(["display-message","-p","-t",n,"#{pane_id}"]);return t.ok&&t.stdout===n}r(gn,"paneExists");function hn(n){const t=o(["display-message","-p","-t",n,"#{pane_id} #{pane_dead}"]);return t.ok&&t.stdout===`${n} 0`}r(hn,"paneRunning");function _n(){const n=o(["list-panes","-a","-F","#{pane_id}"]);return n.ok?new Set(n.stdout.split(`
|
|
3
3
|
`).filter(t=>t!=="")):null}r(_n,"listLivePanes");const V=2e3;async function Pn(){const n=await new Promise(t=>{try{w("tmux",["list-panes","-a","-F","#{pane_id}"],{encoding:"utf8",timeout:V,maxBuffer:4194304},(e,s)=>t(e?null:s))}catch{t(null)}});return n===null?null:new Set(n.trim().split(`
|
|
4
|
-
`).filter(t=>t!==""))}r(Pn,"listLivePanesAsync");function yn(n){const t=o(["display-message","-p","-t",n,"#{pane_current_path}"]);return t.ok&&t.stdout!==""?t.stdout:null}r(yn,"paneCurrentPath");function bn(n){const t=o(["display-message","-p","-t",n,"#{pane_width} #{pane_height}"]);if(!t.ok)return null;const[e,s]=t.stdout.split(" "),u=Number.parseInt(e,10),i=Number.parseInt(s,10);return Number.isInteger(u)&&u>0&&Number.isInteger(i)&&i>0?{cols:u,rows:i}:null}r(bn,"paneSize");function vn(n,t,e=4e3){const s=r(u=>{const i=["display-message",...u?["-C"]:[],"-d",String(e)];return t!==void 0&&t!==""&&i.push("-t",t),i.push(n),i},"args");return o(s(!0)).ok||o(s(!1)).ok}r(vn,"displayMessage");function Sn(n,t,e){return o(["set-option","-p","-t",n,t,e]).ok}r(Sn,"setPaneOption");function In(n,t){const e=o(["show-options","-p","-t",n,"-q","-v",t]);return e.ok?e.stdout:void 0}r(In,"getPaneOption");const W="@crtr_viewer_at";let _=0,P=Promise.resolve();function On(n){if(n==="")return;const t=Math.max(Date.now(),_+1);_=t,P=P.then(()=>new Promise(e=>{try{w("tmux",["set-option","-p","-t",n,W,String(t)],()=>e())}catch{e()}}))}r(On,"stampViewerPaneAsync");function B(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}r(B,"escapeRegex");function k(n,t){const e=n.replace(/^"|"$/g,"");return new RegExp(`(?:^|\\s)surface\\s+attach\\s+to\\s+${B(t)}(?:\\s|$)`).test(e)}r(k,"looksLikeViewerCommand");function L(){const n
|
|
5
|
-
`)){const[i,c,
|
|
6
|
-
`).filter(e=>e!=="")}r(U,"listWindowIds");function
|
|
4
|
+
`).filter(t=>t!==""))}r(Pn,"listLivePanesAsync");function yn(n){const t=o(["display-message","-p","-t",n,"#{pane_current_path}"]);return t.ok&&t.stdout!==""?t.stdout:null}r(yn,"paneCurrentPath");function bn(n){const t=o(["display-message","-p","-t",n,"#{pane_width} #{pane_height}"]);if(!t.ok)return null;const[e,s]=t.stdout.split(" "),u=Number.parseInt(e,10),i=Number.parseInt(s,10);return Number.isInteger(u)&&u>0&&Number.isInteger(i)&&i>0?{cols:u,rows:i}:null}r(bn,"paneSize");function vn(n,t,e=4e3){const s=r(u=>{const i=["display-message",...u?["-C"]:[],"-d",String(e)];return t!==void 0&&t!==""&&i.push("-t",t),i.push(n),i},"args");return o(s(!0)).ok||o(s(!1)).ok}r(vn,"displayMessage");function Sn(n,t,e){return o(["set-option","-p","-t",n,t,e]).ok}r(Sn,"setPaneOption");function In(n,t){const e=o(["show-options","-p","-t",n,"-q","-v",t]);return e.ok?e.stdout:void 0}r(In,"getPaneOption");const W="@crtr_viewer_at";let _=0,P=Promise.resolve();function On(n){if(n==="")return;const t=Math.max(Date.now(),_+1);_=t,P=P.then(()=>new Promise(e=>{try{w("tmux",["set-option","-p","-t",n,W,String(t)],()=>e())}catch{e()}}))}r(On,"stampViewerPaneAsync");function B(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}r(B,"escapeRegex");function k(n,t){const e=n.replace(/^"|"$/g,"");return new RegExp(`(?:^|\\s)surface\\s+attach\\s+to\\s+${B(t)}(?:\\s|$)`).test(e)}r(k,"looksLikeViewerCommand");function L(){const n=E();if(n===null)return null;const t=new Map,e=new Map;for(const{pid:s,ppid:u,command:i}of n){e.set(s,i);const a=t.get(u)??[];a.push(s),t.set(u,a)}return{childrenOf:t,commandOf:e}}r(L,"readProcessTree");function j(n,t,e){const s=[t];for(;s.length>0;){const u=s.pop();if(k(n.commandOf.get(u)??"",e))return!0;const i=n.childrenOf.get(u);i!==void 0&&s.push(...i)}return!1}r(j,"processTreeHasViewer");function Cn(n){const t=p(n,["list-panes","-a","-F","#{pane_id} #{session_name} #{@crtr_node} #{@crtr_viewer_at}"]);if(!t.ok||t.stdout==="")return[];const e=[];let s;for(const u of t.stdout.split(`
|
|
5
|
+
`)){const[i,a,c,b]=u.split(" ");if(i===void 0||i===""||a===void 0||a===""||c===void 0||c==="")continue;const g=p(n,["display-message","-p","-t",i,"#{pane_current_command} #{pane_start_command} #{pane_pid}"]);if(!g.ok)continue;const[v="",S="",I]=g.stdout.split(" ");if(!(k(v,c)||k(S,c))){s??=L();const m=Number.parseInt(I,10);if(s===null||!Number.isInteger(m)||m<=0||!j(s,m,c))continue}const f=Number.parseInt(b??"",10);e.push({pane:i,session:a,nodeId:c,stampMs:Number.isSafeInteger(f)&&f>=0?f:0})}return e}r(Cn,"liveViewerPanes");function En(n,t,e){return p(n,["send-keys","-l","-t",t,C(e)]).ok}r(En,"sendAttachControl");function $n(n,t,e){const s=r(u=>{const i=["display-message",...u?["-C"]:[]];return t!==void 0&&t!==""&&i.push("-c",t),i.push(e),i},"args");return p(n,s(!0)).ok||p(n,s(!1)).ok}r($n,"displayClientMessage");function y(n){return["respawn-pane","-k","-c",n.cwd,...x(n.env),"-t",n.pane,n.command]}r(y,"respawnPaneArgs");function Nn(n){return o(y(n)).ok}r(Nn,"respawnPaneSync");function Mn(n){return z(`tmux ${y(n).map(l).join(" ")} >/dev/null 2>&1`)}r(Mn,"respawnPaneInBackground");function z(n){return o(["run-shell","-b",n]).ok}r(z,"runShellInBackground");function Tn(n){const t=o(["display-message","-p","-t",n,"#{pane_start_command}"]);return t.ok?t.stdout:void 0}r(Tn,"paneStartCommand");function Fn(n,t){const e=o(["swap-pane","-s",n,"-t",t]);return e.ok?null:e.stderr===""?"tmux swap-pane failed":e.stderr}r(Fn,"swapPanes");function An(n){const t=o(["display-message","-p","-t",n,"#{pane_current_command}"]);return t.ok&&t.stdout!==""?t.stdout:void 0}r(An,"paneCurrentCommand");function Rn(n){const t=o(["display-message","-p","-t",n,"#{pane_pid}"]);if(!t.ok)return;const e=Number.parseInt(t.stdout,10);return Number.isInteger(e)&&e>0?e:void 0}r(Rn,"panePid");function Vn(n){return o(["respawn-pane","-k","-t",n]).ok}r(Vn,"respawnPaneInPlace");function Wn(n,t=N()??$(l)){return[t,...n.map(l)].join(" ")}r(Wn,"piCommand");function U(n){const t=o(["list-windows","-t",n,"-F","#{window_id}"]);return!t.ok||t.stdout===""?[]:t.stdout.split(`
|
|
6
|
+
`).filter(e=>e!=="")}r(U,"listWindowIds");function Bn(n,t){return n==null||t==null?!1:U(n).includes(t)}r(Bn,"windowAlive");function Ln(n,t){return o(["select-window","-t",`${n}:${t}`]).ok}r(Ln,"selectWindow");function jn(n,t){return o(["select-layout","-t",n,t]).ok}r(jn,"selectLayout");function zn(n){return o(["select-pane","-t",n]).ok}r(zn,"selectPane");function Un(n){return o(["switch-client","-t",n]).ok}r(Un,"switchClient");function qn(n,t){return o(["send-keys","-t",n,t,"Enter"]).ok}r(qn,"sendKeysEnter");export{W as VIEWER_STAMP_OPTION,dn as breakPane,pn as closePane,an as closeWindow,T as currentTmux,$n as displayClientMessage,vn as displayMessage,ln as displayPopup,fn as displayPopupAsync,rn as ensureSession,un as focusWindow,In as getPaneOption,M as inTmux,_n as listLivePanes,Pn as listLivePanesAsync,U as listWindowIds,Cn as liveViewerPanes,on as openNodeWindow,An as paneCurrentCommand,yn as paneCurrentPath,gn as paneExists,xn as paneLocation,nn as paneOfNode,mn as paneOfWindow,Rn as panePid,hn as paneRunning,bn as paneSize,Tn as paneStartCommand,Wn as piCommand,cn as renameWindow,Mn as respawnPaneInBackground,Vn as respawnPaneInPlace,Nn as respawnPaneSync,z as runShellInBackground,o as runTmux,jn as selectLayout,zn as selectPane,Ln as selectWindow,En as sendAttachControl,qn as sendKeysEnter,F as sessionExists,Sn as setPaneOption,l as shellQuote,sn as splitWindow,On as stampViewerPaneAsync,Fn as swapPanes,Un as switchClient,tn as tmuxAvailable,Z as tmuxClientsReachable,en as tmuxInteractive,Y as tmuxSessionsReachable,Bn as windowAlive,kn as windowIndex,wn as windowOfPane};
|
|
@@ -3,6 +3,8 @@ export declare const ANTHROPIC_PROVIDER_ID = "anthropic";
|
|
|
3
3
|
export declare const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
|
|
4
4
|
/** pi's built-in `openai` provider signed in with a ChatGPT account (dynamic OAuth client). Distinct from Codex: its own pool, credential file and sign-in. */
|
|
5
5
|
export declare const OPENAI_CHATGPT_PROVIDER_ID = "openai";
|
|
6
|
+
/** Longest a recorded 429 keeps an account or model family cooling, whatever the provider's Retry-After or window reset says: after this the next real call probes the provider, and a success clears the row. */
|
|
7
|
+
export declare const MAX_RATE_LIMIT_COOLDOWN_MS: number;
|
|
6
8
|
export declare const INVALID_REFRESH_TOKEN_BACKOFF_MS: number;
|
|
7
9
|
export type ManagedProviderId = typeof ANTHROPIC_PROVIDER_ID | typeof OPENAI_CODEX_PROVIDER_ID | typeof OPENAI_CHATGPT_PROVIDER_ID;
|
|
8
10
|
export declare const MANAGED_PROVIDER_IDS: readonly ManagedProviderId[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var rt=Object.defineProperty;var o=(t,n)=>rt(t,"name",{value:n,configurable:!0});import{prepared as g}from"./sqlite-statements.js";import{createHash as ot,randomUUID as
|
|
2
|
-
`)}o(v,"managedCredentialFile");function
|
|
3
|
-
`,{mode:384}),n}o(
|
|
4
|
-
`))}})}o(E,"locked");function M(t,n,e){g(t,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)").run(n,e.id,e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null)}o(M,"insert");function x(t,n){g(t,"UPDATE managed_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE provider_id=? AND id=?").run(n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,n.providerId,n.id)}o(x,"update");function at(t,n,e,a,i,r){g(t,"INSERT INTO managed_account_model_cooldowns(provider_id,account_id,family,rate_limited_until,last_rate_limited_at) VALUES(?,?,?,?,?) ON CONFLICT(provider_id,account_id,family) DO UPDATE SET rate_limited_until=excluded.rate_limited_until,last_rate_limited_at=excluded.last_rate_limited_at").run(n,e,a,i,r)}o(at,"upsertModelCooldown");function C(t,n,e){e===void 0?g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(n):g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=?").run(n,e)}o(C,"deleteModelCooldowns");function w(t,n){return V(t,n).map(({entry:e})=>e)}o(w,"publicPool");function $(t,n){try{n()}catch(e){throw new Error(`managed ${b(t)} credential file was published, but its SQL membership update failed; SQL membership remains authoritative: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}o($,"writeMembershipAfterCredentialPublish");async function Yt(t,n){return E(t,async(e,a,i)=>{const r=n.map(u=>N(t,u)),s=r.map((u,l)=>({entry:u,id:a[l]?.row.id??U()})),c={};for(const{entry:u,id:l}of s)c[l]=I(u);return i({version:1,accounts:c}),$(t,()=>A(h(),u=>{const l=T(u,t);g(u,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),g(u,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(t),s.forEach(({entry:m,id:d},y)=>{M(u,t,{providerId:t,id:d,label:m.label,labelKey:f(m.label),position:y,lastAttemptAt:m.lastAttemptAt,lastRateLimitedAt:m.lastRateLimitedAt,rateLimitedUntil:m.rateLimitedUntil,...m.authFailure?{authFailure:m.authFailure}:{}});for(const[F,k]of Object.entries(m.modelCooldowns??{}))at(u,t,d,F,k,l.get(d)?.find(p=>p.family===F)?.lastRateLimitedAt??m.lastRateLimitedAt)})})),r})}o(Yt,"writeSubscriptionPool");function O(t,n){const e=n.trim().toLowerCase(),a=Number.parseInt(e,10);return Number.isFinite(a)&&String(a)===e&&a>=1&&a<=t.length?a-1:t.findIndex(({entry:i})=>f(i.label)===e)}o(O,"resolve");async function Rt(t,n){const e=N(t,n);return E(t,async(a,i,r)=>{if(i.some(({entry:l})=>f(l.label)===f(e.label)))throw new R(`${b(t)} subscription already exists: ${e.label}`);const s=_(e),c=i.find(({entry:l})=>_(l)===s);if(s&&c)throw new R(`This ${b(t)} account is already added as \u201C${c.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const u=U();return a.accounts[u]=I(e),r(a),$(t,()=>A(h(),l=>M(l,t,{providerId:t,id:u,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}}))),w(t,a)})}o(Rt,"addSubscription");async function Zt(t,n){const e=N(t,n);return D(t).find(i=>f(i.label)===f(e.label))?It(t,e):Rt(t,e)}o(Zt,"upsertSubscription");async function It(t,n){const e=N(t,n);return E(t,async(a,i,r)=>{const s=i.findIndex(({entry:d})=>f(d.label)===f(e.label)),c=i[s],u=i.find(({entry:d},y)=>y!==s&&_(d)===_(e));if(_(e)&&u)throw new R(`This ${b(t)} account is already added as \u201C${u.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);if(!c){const d=U();return a.accounts[d]=I(e),r(a),$(t,()=>A(h(),y=>M(y,t,{providerId:t,id:d,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil}))),w(t,a)}const l=c.row,m=e.lastRateLimitedAt>=l.lastRateLimitedAt;return a.accounts[l.id]=I(e),r(a),$(t,()=>A(h(),d=>{x(d,{...l,label:e.label,labelKey:f(e.label),lastAttemptAt:Math.max(l.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:l.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:l.rateLimitedUntil}),tt(c.entry,e)&&C(d,t,l.id)})),w(t,a)})}o(It,"upsertSubscriptionWithUniqueAccount");async function vt(t,n,e){return E(t,async(a,i)=>{const r=O(i,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=e.trim();if(!s)throw new Error(`${b(t)} subscription label cannot be empty`);if(i.some((c,u)=>u!==r&&f(c.entry.label)===f(s)))throw new R(`${b(t)} subscription already exists: ${s}`);return A(h(),c=>x(c,{...i[r].row,label:s,labelKey:f(s)})),w(t,a)})}o(vt,"renameSubscription");async function B(t,n){return E(t,async(e,a,i)=>{const r=O(a,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=a[r].row;return A(h(),c=>{g(c,"DELETE FROM managed_accounts WHERE provider_id=? AND id=?").run(t,s.id),C(c,t,s.id)}),delete e.accounts[s.id],i(e),w(t,e)})}o(B,"remove");async function te(t,n){return B(t,n)}o(te,"removeManagedAccount");async function ee(t,n){return B(t,n)}o(ee,"removeSubscription");async function $t(t){return E(t,async(n,e,a)=>{A(h(),i=>{g(i,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),C(i,t)});for(const{row:i}of e)delete n.accounts[i.id];return a(n),e.length})}o($t,"removeAllManagedAccounts");async function ne(t,n){return n===void 0?(await $t(t),[]):B(t,n)}o(ne,"removeManagedProvider");async function ae(t,n){return E(t,async(e,a)=>{const i=O(a,n);if(i<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return A(h(),r=>{[a[i],...a.filter((c,u)=>u!==i)].forEach((c,u)=>x(r,{...c.row,position:u}))}),w(t,e)})}o(ae,"promoteSubscription");async function it(t,n,e){return A(h(),a=>{const i=P(a,t,n);if(!i)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const r=e(i);r&&x(a,r)}),D(t)}o(it,"mutateHealth");async function ie(t,n,e=Date.now()){return it(t,n,a=>({...a,lastAttemptAt:Math.max(a.lastAttemptAt,e)}))}o(ie,"markSubscriptionAttempt");async function re(t,n,e,a,i=Date.now()){return E(t,async(r,s)=>{const c=s.find(({entry:u})=>f(u.label)===f(n));if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return c.entry.refresh!==e.refresh||c.entry.access!==e.access||A(h(),u=>x(u,{...c.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(c.row.lastAttemptAt,i),rateLimitedUntil:Math.max(c.row.rateLimitedUntil,i+Math.max(0,a))})),w(t,r)})}o(re,"markSubscriptionAuthenticationFailed");async function oe(t,n,e,a=Date.now(),i=Date.now(),r={kind:"account"}){return r.kind==="account"?it(t,n,s=>i<s.lastRateLimitedAt?null:{...s,lastAttemptAt:Math.max(s.lastAttemptAt,a),lastRateLimitedAt:i,rateLimitedUntil:i+Math.max(0,e)}):(A(h(),s=>{const c=P(s,t,n);if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);x(s,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,a)});const u=T(s,t).get(c.id)?.find(l=>l.family===r.family);u&&i<u.lastRateLimitedAt||at(s,t,c.id,r.family,i+Math.max(0,e),i)}),D(t))}o(oe,"markSubscriptionRateLimited");async function se(t,n,e=Date.now(),a){const i=j(a);return A(h(),r=>{const s=P(r,t,n);if(!s)throw new Error(`Unknown ${b(t)} subscription: ${n}`);if(e>=s.lastRateLimitedAt&&x(r,{...s,authFailure:void 0,lastAttemptAt:Math.max(s.lastAttemptAt,e),rateLimitedUntil:0}),i===void 0)return;const c=T(r,t).get(s.id)?.find(u=>u.family===i);c&&e>=c.lastRateLimitedAt&&g(r,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=? AND family=?").run(t,s.id,i)}),D(t)}o(se,"markSubscriptionSuccess");async function ce(t,n,e){return E(t,async(a,i,r)=>{const s=O(i,n);if(s<0)throw new Error(`${b(t)} subscription "${n}" no longer exists`);const c=i[s];if(c.entry.expires>Date.now())return c.entry;const u=await e(c.entry.refresh,c.entry.clientId),l=et(nt(t,c.entry.label,u),u.clientId??c.entry.clientId);return a.accounts[c.row.id]={...a.accounts[c.row.id],...l},r(a),{...c.entry,...l}})}o(ce,"refreshSubscriptionCredential");function Ft(t){const n=t,e=[n?.errorMessage,n?.message,t instanceof Error?t.message:""].filter(i=>typeof i=="string").join(" ");return(typeof n?.status=="number"?n.status:Number(e.match(/\bstatus[=:]\s*(\d+)/i)?.[1]))===400&&/invalid_grant|refresh token .*invalid|refresh token .*not found/i.test(e)}o(Ft,"isInvalidRefreshTokenError");async function le(t,n,e){return E(t,async(a,i,r)=>{const s=new Set;let c;for(;s.size<i.length;){const u=n(i.filter(({entry:m})=>!s.has(m.label)).map(({entry:m})=>m));if(!u)break;const l=i.find(({entry:m})=>m.label===u.label);s.add(l.entry.label);try{const m=await e(l.entry);if(m){const d=et(nt(t,l.entry.label,m),m.clientId??l.entry.clientId);return a.accounts[l.row.id]={...a.accounts[l.row.id],...d},r(a),{...l.entry,...d}}return l.entry}catch(m){if(!Ft(m))throw m;c=m;const d=Date.now();A(h(),y=>x(y,{...l.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(l.row.lastAttemptAt,d),rateLimitedUntil:Math.max(l.row.rateLimitedUntil,d+At)}))}}if(c)throw c})}o(le,"modifyElectedSubscription");async function St(t,n){const e=n.accountId?.trim(),a=n.email?.trim();if(t!==H)return{...e?{accountId:e}:{},...a?{email:a}:{}};try{const i=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${n.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!i.ok)return e?{accountId:e}:{};const r=await i.json(),s=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,c=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...s?{accountId:s}:{},...c?{email:c}:{}}}catch{return e?{accountId:e}:{}}}o(St,"enrichManagedLoginIdentity");async function ue(t,n,e){const a=await St(t,e);return E(t,async(i,r,s)=>{const c=e.autoLabel?void 0:n?.trim(),u=_({refresh:e.refresh,accountId:a.accountId}),l=(c?r.find(({entry:p})=>f(p.label)===f(c)):void 0)??r.find(({entry:p})=>_(p)===u);let m=l?.entry.label??c;if(!m&&e.autoLabel){const p=a.email||n?.trim()||`Account ${r.length+1}`;m=p;for(let S=2;r.some(({entry:L})=>f(L.label)===f(m));S++)m=`${p} (${S})`}if(!m)throw new Error(`${b(t)} subscription label cannot be empty`);const d=N(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...a.accountId?{accountId:a.accountId}:{},...e.clientId?{clientId:e.clientId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!l){if(r.some(({entry:L})=>f(L.label)===f(d.label)))throw new R(`${b(t)} subscription already exists: ${d.label}`);const p=r.find(({entry:L})=>_(L)===_(d));if(_(d)&&p)throw new R(`This ${b(t)} account is already added as \u201C${p.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const S=U();return i.accounts[S]=I(d),s(i),$(t,()=>A(h(),L=>M(L,t,{providerId:t,id:S,label:d.label,labelKey:f(d.label),position:r.length,lastAttemptAt:d.lastAttemptAt,lastRateLimitedAt:d.lastRateLimitedAt,rateLimitedUntil:d.rateLimitedUntil}))),w(t,i)}const y=l.row,F=r.find(({entry:p})=>p.label!==l.entry.label&&_(p)===_(d));if(_(d)&&F)throw new R(`This ${b(t)} account is already added as \u201C${F.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);i.accounts[y.id]=I(d),s(i);const k=d.lastRateLimitedAt>=y.lastRateLimitedAt;return $(t,()=>A(h(),p=>{x(p,{...y,authFailure:void 0,lastAttemptAt:Math.max(y.lastAttemptAt,d.lastAttemptAt),lastRateLimitedAt:k?d.lastRateLimitedAt:y.lastRateLimitedAt,rateLimitedUntil:k?0:y.rateLimitedUntil}),tt(l.entry,d)&&C(p,t,y.id)})),w(t,i)})}o(ue,"commitManagedLogin");function Ut(t,n=Date.now()){if(!t?.trim())return;const e=Number(t.trim());if(Number.isFinite(e))return Math.max(0,Math.round(e*1e3));const a=Date.parse(t);return Number.isFinite(a)?Math.max(0,a-n):void 0}o(Ut,"parseRetryAfterHeader");function Nt(t){if(!t)return;const n=o(a=>t instanceof Headers?t.get(a)??void 0:Object.entries(t).find(([i])=>i.toLowerCase()===a)?.[1],"get"),e=n("retry-after-ms");return e!==void 0&&e.trim()!==""&&Number.isFinite(Number(e))?Math.max(0,Number(e)):Ut(n("retry-after"))}o(Nt,"retryAfterMsFromHeaders");function de(t){if(!t||typeof t!="object"||!("headers"in t))return;const n=t.headers;if(!(!(n instanceof Headers)&&(!n||typeof n!="object"||Array.isArray(n))))return Nt(n)}o(de,"retryAfterMsFromError");function Dt(){const t=ut();if(t)return t.rotation;const n=g(ft(ht(h())),"SELECT default_fallback_strength,revert_when_available FROM rotation_settings WHERE singleton=1").get();return{defaultFallbackStrength:X(n?.default_fallback_strength)??J,revertWhenAvailable:n?.revert_when_available===void 0||n.revert_when_available===null?!0:!!n.revert_when_available}}o(Dt,"readRotationConfig");function me(t){const n=Dt(),e={defaultFallbackStrength:X(t.defaultFallbackStrength)??n.defaultFallbackStrength??J,revertWhenAvailable:t.revertWhenAvailable??n.revertWhenAvailable??!0};return A(h(),a=>g(a,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?) ON CONFLICT(singleton) DO UPDATE SET default_fallback_strength=excluded.default_fallback_strength,revert_when_available=excluded.revert_when_available").run(e.defaultFallbackStrength,e.revertWhenAvailable?1:0)),e}o(me,"writeRotationConfig");function fe(t,n=Date.now(),e){const a=D(t),i=a.filter(s=>s.authFailure===void 0&&Et(s,e)<=n),r=b(t);return i.length===0?`${r} pool exhausted`:`${r} ${i[0]?.label??t} (${i.length}/${a.length} ready)`}o(fe,"formatStatusLine");export{H as ANTHROPIC_PROVIDER_ID,R as DuplicateSubscriptionError,At as INVALID_REFRESH_TOKEN_BACKOFF_MS,qt as MANAGED_PROVIDER_IDS,G as OPENAI_CHATGPT_PROVIDER_ID,z as OPENAI_CODEX_PROVIDER_ID,wt as activeCooldowns,Rt as addSubscription,ue as commitManagedLogin,Et as coolingUntilFor,St as enrichManagedLoginIdentity,zt as formatCooldowns,fe as formatStatusLine,b as getProviderLabel,Ft as isInvalidRefreshTokenError,Jt as isManagedProvider,ie as markSubscriptionAttempt,re as markSubscriptionAuthenticationFailed,oe as markSubscriptionRateLimited,se as markSubscriptionSuccess,le as modifyElectedSubscription,Qt as openAIDeviceId,Ut as parseRetryAfterHeader,ae as promoteSubscription,Dt as readRotationConfig,D as readSubscriptionPool,ce as refreshSubscriptionCredential,$t as removeAllManagedAccounts,te as removeManagedAccount,ne as removeManagedProvider,ee as removeSubscription,vt as renameSubscription,Xt as resolveProviderCandidates,de as retryAfterMsFromError,Nt as retryAfterMsFromHeaders,Gt as subscriptionNeedsSuccessWrite,Zt as upsertSubscription,It as upsertSubscriptionWithUniqueAccount,me as writeRotationConfig,Yt as writeSubscriptionPool};
|
|
1
|
+
var rt=Object.defineProperty;var o=(t,n)=>rt(t,"name",{value:n,configurable:!0});import{prepared as g}from"./sqlite-statements.js";import{createHash as ot,randomUUID as N}from"node:crypto";import{mkdirSync as st,readFileSync as ct,writeFileSync as lt}from"node:fs";import{join as q}from"node:path";import{brokerConfigSnapshot as ut}from"./config.js";import{LockedJsonFile as dt}from"./locked-file.js";import{secretsDir as mt}from"./layout.js";import{openScopedStore as ft}from"./scoped-state/db.js";import{providerStore as ht}from"./scoped-state/paths.js";import{providerModelCooldowns as T,providerModelCooldownsFor as bt,providerRowByReference as P,providerRows as pt,providerWrite as A}from"./scoped-state/providers.js";import{modelCooldownFamily as j}from"./rate-limit-scope.js";const W="anthropic",z="openai-codex",G="openai",At=600*1e3,yt=720*60*60*1e3,zt=[W,z,G],J="strong",Q={anthropic:{label:"Claude",credentials:"anthropic-credentials.json"},"openai-codex":{label:"OpenAI Codex",credentials:"openai-codex-credentials.json"},openai:{label:"ChatGPT",credentials:"openai-credentials.json"}};function I(t){return{refresh:t.refresh,access:t.access,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{}}}o(I,"stored");function H(t){return typeof t=="string"&&t.trim()?t.trim():void 0}o(H,"optionalString");function h(){return mt()}o(h,"agentDir");function K(t){return q(h(),Q[t].credentials)}o(K,"credentialFile");function X(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}o(X,"normalizeStrength");function f(t){return t.toLowerCase()}o(f,"key");function _t(t){const n=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",a=typeof t.access=="string"?t.access.trim():"",i=Number(t.expires);if(!n||!e||!a||!Number.isFinite(i))return;const r=H(t.accountId),s=H(t.clientId);return{label:n,refresh:e,access:a,expires:i,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...r?{accountId:r}:{},...s?{clientId:s}:{},...Y(t.modelCooldowns)?{modelCooldowns:Y(t.modelCooldowns)}:{}}}o(_t,"credential");function Y(t){if(!t||typeof t!="object"||Array.isArray(t))return;const n=Object.entries(t).filter(([e,a])=>e.trim()!==""&&Number.isFinite(Number(a))).map(([e,a])=>[e,Number(a)]);return n.length?Object.fromEntries(n):void 0}o(Y,"modelCooldowns");function U(t,n){if(!n.label?.trim())throw new Error(`${b(t)} subscription label cannot be empty`);const e=_t(n);if(!e)throw new Error(`Refusing to persist incomplete ${b(t)} subscription "${n.label.trim()}"; pool rows require both OAuth tokens.`);return e}o(U,"assertCredential");function Z(t,n){if(!n||n.trim()==="")return{version:1,accounts:{}};let e;try{e=JSON.parse(n)}catch(a){throw new Error(`managed credential file ${t} is corrupt: ${a instanceof Error?a.message:String(a)}`)}if(typeof e!="object"||e===null||Array.isArray(e)||e.version!==1||typeof e.accounts!="object"||e.accounts===null||Array.isArray(e.accounts))throw new Error(`managed credential file ${t} is corrupt`);return e}o(Z,"parseCredentials");function gt(t,n,e=[]){const a=n.accounts[t.id];if(!a||typeof a.refresh!="string"||typeof a.access!="string"||!Number.isFinite(a.expires))throw new Error(`managed ${t.providerId} account ${t.id} has no credential record`);return{label:t.label,refresh:a.refresh,access:a.access,expires:a.expires,rateLimitedUntil:t.rateLimitedUntil,lastAttemptAt:t.lastAttemptAt,lastRateLimitedAt:t.lastRateLimitedAt,...t.authFailure?{authFailure:t.authFailure}:{},...a.accountId?{accountId:a.accountId}:{},...a.clientId?{clientId:a.clientId}:{},...e.length?{modelCooldowns:Object.fromEntries(e.map(i=>[i.family,i.rateLimitedUntil]))}:{}}}o(gt,"toPublic");function V(t,n){const e=bt(h(),t);return pt(h(),t).map(a=>({row:a,entry:gt(a,n,e.get(a.id))}))}o(V,"rowsWithCredentials");function v(t){return new dt(K(t),!0,`{"version":1,"accounts":{}}
|
|
2
|
+
`)}o(v,"managedCredentialFile");function Et(t){return Z(K(t),v(t).readUnlocked())}o(Et,"readFile");function tt(t,n){const e=t.accountId?.trim(),a=n.accountId?.trim();return!!(e&&a&&e!==a)}o(tt,"differentAccount");function _(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${ot("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}o(_,"identity");function et(t,n){const e=H(n);return e?{...t,clientId:e}:t}o(et,"withClientId");function nt(t,n,e){const a=e.refresh?.trim()??"",i=e.access?.trim()??"",r=Number(e.expires);if(!a||!i||!Number.isFinite(r))throw new Error(`${b(t)} subscription "${n}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:a,access:i,expires:r}}o(nt,"assertTokens");function wt(t,n){const e=j(n);return Math.max(t.rateLimitedUntil||0,e===void 0?0:t.modelCooldowns?.[e]??0)}o(wt,"coolingUntilFor");function xt(t,n=Date.now()){return[...(t.rateLimitedUntil||0)>n?[{scope:"account",until:t.rateLimitedUntil}]:[],...Object.entries(t.modelCooldowns??{}).filter(([,e])=>e>n).map(([e,a])=>({scope:e,until:a}))]}o(xt,"activeCooldowns");function Gt(t,n=Date.now()){const e=xt(t,n).map(({scope:a,until:i})=>`${a==="account"?"":`${a} `}cooldown ${Lt(i-n)}`);return e.length?e.join(", "):void 0}o(Gt,"formatCooldowns");function Lt(t){const n=Math.max(1,Math.ceil(t/6e4));if(n<60)return`${n}m`;const e=Math.floor(n/60);return e<24?`${e}h${n%60?` ${n%60}m`:""}`:`${Math.floor(e/24)}d${e%24?` ${e%24}h`:""}`}o(Lt,"formatWait");function Rt(t,n){const e=j(n);return e!==void 0&&t.modelCooldowns?.[e]!==void 0}o(Rt,"hasModelCooldownRow");function Jt(t,n){return!!(t.rateLimitedUntil||t.authFailure)||Rt(t,n)}o(Jt,"subscriptionNeedsSuccessWrite");function Qt(t){return t===W||t===z||t===G}o(Qt,"isManagedProvider");function Xt(){const t=q(h(),"openai-device-id");try{const e=ct(t,"utf8").trim();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return e.toLowerCase()}catch{}const n=N();return st(h(),{recursive:!0,mode:448}),lt(t,`${n}
|
|
3
|
+
`,{mode:384}),n}o(Xt,"openAIDeviceId");function b(t){return Q[t].label}o(b,"getProviderLabel");function Yt(t,n=Date.now(),e={}){const a=[],i=[];for(let r=0;r<t.length;r++){const s=t[r],c=s.registered?s.automaticFallback===!1?"non-managed-fallback":s.authenticated?(s.coolingUntil??0)>n?"cooling":void 0:"unauthenticated":"unregistered";if(!c)a.push(s);else if(i.push({candidate:s,reason:c}),e.pinnedToFirst&&r===0&&c!=="cooling")break}return{candidates:a,rejected:i}}o(Yt,"resolveProviderCandidates");class R extends Error{static{o(this,"DuplicateSubscriptionError")}constructor(n){super(n),this.name="DuplicateSubscriptionError"}}function D(t){return V(t,Et(t)).map(({entry:n})=>n)}o(D,"readSubscriptionPool");async function E(t,n){const e=K(t);return v(t).withLockAsync(async(a,i,r)=>{const s=Z(e,a);return{result:await n(s,V(t,s),c=>r(`${JSON.stringify(c)}
|
|
4
|
+
`))}})}o(E,"locked");function k(t,n,e){g(t,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)").run(n,e.id,e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null)}o(k,"insert");function x(t,n){g(t,"UPDATE managed_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE provider_id=? AND id=?").run(n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,n.providerId,n.id)}o(x,"update");function at(t,n,e,a,i,r){g(t,"INSERT INTO managed_account_model_cooldowns(provider_id,account_id,family,rate_limited_until,last_rate_limited_at) VALUES(?,?,?,?,?) ON CONFLICT(provider_id,account_id,family) DO UPDATE SET rate_limited_until=excluded.rate_limited_until,last_rate_limited_at=excluded.last_rate_limited_at").run(n,e,a,i,r)}o(at,"upsertModelCooldown");function C(t,n,e){e===void 0?g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(n):g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=?").run(n,e)}o(C,"deleteModelCooldowns");function w(t,n){return V(t,n).map(({entry:e})=>e)}o(w,"publicPool");function $(t,n){try{n()}catch(e){throw new Error(`managed ${b(t)} credential file was published, but its SQL membership update failed; SQL membership remains authoritative: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}o($,"writeMembershipAfterCredentialPublish");async function Zt(t,n){return E(t,async(e,a,i)=>{const r=n.map(u=>U(t,u)),s=r.map((u,l)=>({entry:u,id:a[l]?.row.id??N()})),c={};for(const{entry:u,id:l}of s)c[l]=I(u);return i({version:1,accounts:c}),$(t,()=>A(h(),u=>{const l=T(u,t);g(u,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),g(u,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(t),s.forEach(({entry:m,id:d},y)=>{k(u,t,{providerId:t,id:d,label:m.label,labelKey:f(m.label),position:y,lastAttemptAt:m.lastAttemptAt,lastRateLimitedAt:m.lastRateLimitedAt,rateLimitedUntil:m.rateLimitedUntil,...m.authFailure?{authFailure:m.authFailure}:{}});for(const[F,M]of Object.entries(m.modelCooldowns??{}))at(u,t,d,F,M,l.get(d)?.find(p=>p.family===F)?.lastRateLimitedAt??m.lastRateLimitedAt)})})),r})}o(Zt,"writeSubscriptionPool");function O(t,n){const e=n.trim().toLowerCase(),a=Number.parseInt(e,10);return Number.isFinite(a)&&String(a)===e&&a>=1&&a<=t.length?a-1:t.findIndex(({entry:i})=>f(i.label)===e)}o(O,"resolve");async function It(t,n){const e=U(t,n);return E(t,async(a,i,r)=>{if(i.some(({entry:l})=>f(l.label)===f(e.label)))throw new R(`${b(t)} subscription already exists: ${e.label}`);const s=_(e),c=i.find(({entry:l})=>_(l)===s);if(s&&c)throw new R(`This ${b(t)} account is already added as \u201C${c.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const u=N();return a.accounts[u]=I(e),r(a),$(t,()=>A(h(),l=>k(l,t,{providerId:t,id:u,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}}))),w(t,a)})}o(It,"addSubscription");async function vt(t,n){const e=U(t,n);return D(t).find(i=>f(i.label)===f(e.label))?$t(t,e):It(t,e)}o(vt,"upsertSubscription");async function $t(t,n){const e=U(t,n);return E(t,async(a,i,r)=>{const s=i.findIndex(({entry:d})=>f(d.label)===f(e.label)),c=i[s],u=i.find(({entry:d},y)=>y!==s&&_(d)===_(e));if(_(e)&&u)throw new R(`This ${b(t)} account is already added as \u201C${u.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);if(!c){const d=N();return a.accounts[d]=I(e),r(a),$(t,()=>A(h(),y=>k(y,t,{providerId:t,id:d,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil}))),w(t,a)}const l=c.row,m=e.lastRateLimitedAt>=l.lastRateLimitedAt;return a.accounts[l.id]=I(e),r(a),$(t,()=>A(h(),d=>{x(d,{...l,label:e.label,labelKey:f(e.label),lastAttemptAt:Math.max(l.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:l.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:l.rateLimitedUntil}),tt(c.entry,e)&&C(d,t,l.id)})),w(t,a)})}o($t,"upsertSubscriptionWithUniqueAccount");async function te(t,n,e){return E(t,async(a,i)=>{const r=O(i,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=e.trim();if(!s)throw new Error(`${b(t)} subscription label cannot be empty`);if(i.some((c,u)=>u!==r&&f(c.entry.label)===f(s)))throw new R(`${b(t)} subscription already exists: ${s}`);return A(h(),c=>x(c,{...i[r].row,label:s,labelKey:f(s)})),w(t,a)})}o(te,"renameSubscription");async function B(t,n){return E(t,async(e,a,i)=>{const r=O(a,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=a[r].row;return A(h(),c=>{g(c,"DELETE FROM managed_accounts WHERE provider_id=? AND id=?").run(t,s.id),C(c,t,s.id)}),delete e.accounts[s.id],i(e),w(t,e)})}o(B,"remove");async function ee(t,n){return B(t,n)}o(ee,"removeManagedAccount");async function ne(t,n){return B(t,n)}o(ne,"removeSubscription");async function Ft(t){return E(t,async(n,e,a)=>{A(h(),i=>{g(i,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),C(i,t)});for(const{row:i}of e)delete n.accounts[i.id];return a(n),e.length})}o(Ft,"removeAllManagedAccounts");async function ae(t,n){return n===void 0?(await Ft(t),[]):B(t,n)}o(ae,"removeManagedProvider");async function ie(t,n){return E(t,async(e,a)=>{const i=O(a,n);if(i<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return A(h(),r=>{[a[i],...a.filter((c,u)=>u!==i)].forEach((c,u)=>x(r,{...c.row,position:u}))}),w(t,e)})}o(ie,"promoteSubscription");async function it(t,n,e){return A(h(),a=>{const i=P(a,t,n);if(!i)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const r=e(i);r&&x(a,r)}),D(t)}o(it,"mutateHealth");async function re(t,n,e=Date.now()){return it(t,n,a=>({...a,lastAttemptAt:Math.max(a.lastAttemptAt,e)}))}o(re,"markSubscriptionAttempt");async function oe(t,n,e,a,i=Date.now()){return E(t,async(r,s)=>{const c=s.find(({entry:u})=>f(u.label)===f(n));if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return c.entry.refresh!==e.refresh||c.entry.access!==e.access||A(h(),u=>x(u,{...c.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(c.row.lastAttemptAt,i),rateLimitedUntil:Math.max(c.row.rateLimitedUntil,i+Math.max(0,a))})),w(t,r)})}o(oe,"markSubscriptionAuthenticationFailed");async function se(t,n,e,a=Date.now(),i=Date.now(),r={kind:"account"}){return e=Math.min(Math.max(0,e),At),r.kind==="account"?it(t,n,s=>i<s.lastRateLimitedAt?null:{...s,lastAttemptAt:Math.max(s.lastAttemptAt,a),lastRateLimitedAt:i,rateLimitedUntil:i+e}):(A(h(),s=>{const c=P(s,t,n);if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);x(s,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,a)});const u=T(s,t).get(c.id)?.find(l=>l.family===r.family);u&&i<u.lastRateLimitedAt||at(s,t,c.id,r.family,i+e,i)}),D(t))}o(se,"markSubscriptionRateLimited");async function ce(t,n,e=Date.now(),a){const i=j(a);return A(h(),r=>{const s=P(r,t,n);if(!s)throw new Error(`Unknown ${b(t)} subscription: ${n}`);if(e>=s.lastRateLimitedAt&&x(r,{...s,authFailure:void 0,lastAttemptAt:Math.max(s.lastAttemptAt,e),rateLimitedUntil:0}),i===void 0)return;const c=T(r,t).get(s.id)?.find(u=>u.family===i);c&&e>=c.lastRateLimitedAt&&g(r,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=? AND family=?").run(t,s.id,i)}),D(t)}o(ce,"markSubscriptionSuccess");async function le(t,n,e){return E(t,async(a,i,r)=>{const s=O(i,n);if(s<0)throw new Error(`${b(t)} subscription "${n}" no longer exists`);const c=i[s];if(c.entry.expires>Date.now())return c.entry;const u=await e(c.entry.refresh,c.entry.clientId),l=et(nt(t,c.entry.label,u),u.clientId??c.entry.clientId);return a.accounts[c.row.id]={...a.accounts[c.row.id],...l},r(a),{...c.entry,...l}})}o(le,"refreshSubscriptionCredential");function St(t){const n=t,e=[n?.errorMessage,n?.message,t instanceof Error?t.message:""].filter(i=>typeof i=="string").join(" ");return(typeof n?.status=="number"?n.status:Number(e.match(/\bstatus[=:]\s*(\d+)/i)?.[1]))===400&&/invalid_grant|refresh token .*invalid|refresh token .*not found/i.test(e)}o(St,"isInvalidRefreshTokenError");async function ue(t,n,e){return E(t,async(a,i,r)=>{const s=new Set;let c;for(;s.size<i.length;){const u=n(i.filter(({entry:m})=>!s.has(m.label)).map(({entry:m})=>m));if(!u)break;const l=i.find(({entry:m})=>m.label===u.label);s.add(l.entry.label);try{const m=await e(l.entry);if(m){const d=et(nt(t,l.entry.label,m),m.clientId??l.entry.clientId);return a.accounts[l.row.id]={...a.accounts[l.row.id],...d},r(a),{...l.entry,...d}}return l.entry}catch(m){if(!St(m))throw m;c=m;const d=Date.now();A(h(),y=>x(y,{...l.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(l.row.lastAttemptAt,d),rateLimitedUntil:Math.max(l.row.rateLimitedUntil,d+yt)}))}}if(c)throw c})}o(ue,"modifyElectedSubscription");async function Nt(t,n){const e=n.accountId?.trim(),a=n.email?.trim();if(t!==W)return{...e?{accountId:e}:{},...a?{email:a}:{}};try{const i=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${n.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!i.ok)return e?{accountId:e}:{};const r=await i.json(),s=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,c=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...s?{accountId:s}:{},...c?{email:c}:{}}}catch{return e?{accountId:e}:{}}}o(Nt,"enrichManagedLoginIdentity");async function de(t,n,e){const a=await Nt(t,e);return E(t,async(i,r,s)=>{const c=e.autoLabel?void 0:n?.trim(),u=_({refresh:e.refresh,accountId:a.accountId}),l=(c?r.find(({entry:p})=>f(p.label)===f(c)):void 0)??r.find(({entry:p})=>_(p)===u);let m=l?.entry.label??c;if(!m&&e.autoLabel){const p=a.email||n?.trim()||`Account ${r.length+1}`;m=p;for(let S=2;r.some(({entry:L})=>f(L.label)===f(m));S++)m=`${p} (${S})`}if(!m)throw new Error(`${b(t)} subscription label cannot be empty`);const d=U(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...a.accountId?{accountId:a.accountId}:{},...e.clientId?{clientId:e.clientId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!l){if(r.some(({entry:L})=>f(L.label)===f(d.label)))throw new R(`${b(t)} subscription already exists: ${d.label}`);const p=r.find(({entry:L})=>_(L)===_(d));if(_(d)&&p)throw new R(`This ${b(t)} account is already added as \u201C${p.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const S=N();return i.accounts[S]=I(d),s(i),$(t,()=>A(h(),L=>k(L,t,{providerId:t,id:S,label:d.label,labelKey:f(d.label),position:r.length,lastAttemptAt:d.lastAttemptAt,lastRateLimitedAt:d.lastRateLimitedAt,rateLimitedUntil:d.rateLimitedUntil}))),w(t,i)}const y=l.row,F=r.find(({entry:p})=>p.label!==l.entry.label&&_(p)===_(d));if(_(d)&&F)throw new R(`This ${b(t)} account is already added as \u201C${F.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);i.accounts[y.id]=I(d),s(i);const M=d.lastRateLimitedAt>=y.lastRateLimitedAt;return $(t,()=>A(h(),p=>{x(p,{...y,authFailure:void 0,lastAttemptAt:Math.max(y.lastAttemptAt,d.lastAttemptAt),lastRateLimitedAt:M?d.lastRateLimitedAt:y.lastRateLimitedAt,rateLimitedUntil:M?0:y.rateLimitedUntil}),tt(l.entry,d)&&C(p,t,y.id)})),w(t,i)})}o(de,"commitManagedLogin");function Ut(t,n=Date.now()){if(!t?.trim())return;const e=Number(t.trim());if(Number.isFinite(e))return Math.max(0,Math.round(e*1e3));const a=Date.parse(t);return Number.isFinite(a)?Math.max(0,a-n):void 0}o(Ut,"parseRetryAfterHeader");function Dt(t){if(!t)return;const n=o(a=>t instanceof Headers?t.get(a)??void 0:Object.entries(t).find(([i])=>i.toLowerCase()===a)?.[1],"get"),e=n("retry-after-ms");return e!==void 0&&e.trim()!==""&&Number.isFinite(Number(e))?Math.max(0,Number(e)):Ut(n("retry-after"))}o(Dt,"retryAfterMsFromHeaders");function me(t){if(!t||typeof t!="object"||!("headers"in t))return;const n=t.headers;if(!(!(n instanceof Headers)&&(!n||typeof n!="object"||Array.isArray(n))))return Dt(n)}o(me,"retryAfterMsFromError");function Mt(){const t=ut();if(t)return t.rotation;const n=g(ft(ht(h())),"SELECT default_fallback_strength,revert_when_available FROM rotation_settings WHERE singleton=1").get();return{defaultFallbackStrength:X(n?.default_fallback_strength)??J,revertWhenAvailable:n?.revert_when_available===void 0||n.revert_when_available===null?!0:!!n.revert_when_available}}o(Mt,"readRotationConfig");function fe(t){const n=Mt(),e={defaultFallbackStrength:X(t.defaultFallbackStrength)??n.defaultFallbackStrength??J,revertWhenAvailable:t.revertWhenAvailable??n.revertWhenAvailable??!0};return A(h(),a=>g(a,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?) ON CONFLICT(singleton) DO UPDATE SET default_fallback_strength=excluded.default_fallback_strength,revert_when_available=excluded.revert_when_available").run(e.defaultFallbackStrength,e.revertWhenAvailable?1:0)),e}o(fe,"writeRotationConfig");function he(t,n=Date.now(),e){const a=D(t),i=a.filter(s=>s.authFailure===void 0&&wt(s,e)<=n),r=b(t);return i.length===0?`${r} pool exhausted`:`${r} ${i[0]?.label??t} (${i.length}/${a.length} ready)`}o(he,"formatStatusLine");export{W as ANTHROPIC_PROVIDER_ID,R as DuplicateSubscriptionError,yt as INVALID_REFRESH_TOKEN_BACKOFF_MS,zt as MANAGED_PROVIDER_IDS,At as MAX_RATE_LIMIT_COOLDOWN_MS,G as OPENAI_CHATGPT_PROVIDER_ID,z as OPENAI_CODEX_PROVIDER_ID,xt as activeCooldowns,It as addSubscription,de as commitManagedLogin,wt as coolingUntilFor,Nt as enrichManagedLoginIdentity,Gt as formatCooldowns,he as formatStatusLine,b as getProviderLabel,St as isInvalidRefreshTokenError,Qt as isManagedProvider,re as markSubscriptionAttempt,oe as markSubscriptionAuthenticationFailed,se as markSubscriptionRateLimited,ce as markSubscriptionSuccess,ue as modifyElectedSubscription,Xt as openAIDeviceId,Ut as parseRetryAfterHeader,ie as promoteSubscription,Mt as readRotationConfig,D as readSubscriptionPool,le as refreshSubscriptionCredential,Ft as removeAllManagedAccounts,ee as removeManagedAccount,ae as removeManagedProvider,ne as removeSubscription,te as renameSubscription,Yt as resolveProviderCandidates,me as retryAfterMsFromError,Dt as retryAfterMsFromHeaders,Jt as subscriptionNeedsSuccessWrite,vt as upsertSubscription,$t as upsertSubscriptionWithUniqueAccount,fe as writeRotationConfig,Zt as writeSubscriptionPool};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var
|
|
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 o=!0,n=!1,a=Date.now();const f=s(()=>{o=!0},"acknowledge"),m=s(()=>{n||(n=!0,clearInterval(h),r.off("pong",f))},"stop");r.on("pong",f);const h=setInterval(()=>{if(Date.now()-a>i){o=!0;return}if(!o){m(),r.terminate();return}o=!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,o,n){const a=JSON.stringify({error:{code:o,message:n}}),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
|
|
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),o=new x(T);let n=!1,a=!1,f,m=!1,h=!1,d=!1;i.on("connect",()=>{m=!0,!n&&d&&_(e,r),v(e,{link:"relay\u2194broker"})});const p=s((t,l)=>{if(!n){if(n=!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=o.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=>{n||t||(v(e,{link:"relay\u2194broker"}),p(1e3,"broker socket closed"))}),r.on("close",(t,l)=>{const c=l.toString("utf8");if(n){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(n||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(
|
|
8
|
+
`;try{i.write(y)}catch{}})}s(z,"bridgeConnection");async function ne(r,e,i,o){e.on("error",()=>{});try{if(!A(o)){u(e,400,"Bad Request","invalid_node_id",`invalid node id: ${o}`);return}if(S(o)===null){u(e,404,"Not Found","node_not_found",`no node ${o}`);return}if(j(r)){if(!await F(o,250)){u(e,409,"Conflict","node_dormant",`node ${o} is dormant and revive was opted out (?revive=0)`);return}}else{let n;try{const a=await O(o,{resume:!0,capacity:"refuse"},q(r));if(a.parked){u(e,409,"Conflict","node_dormant",`node ${o} is parked; attach with ?wakeParked=1 to wake it`);return}n=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(o,n)){u(e,409,"Conflict","broker_not_ready",`broker view socket for ${o} did not become listenable after revive`);return}}if(e.destroyed)return;H.handleUpgrade(r,e,i,n=>z(n,o))}catch(n){const a=n instanceof Error?n.message:String(n);if(D(n)){u(e,400,"Bad Request","invalid_request",a);return}u(e,500,"Internal Server Error","internal",`attach upgrade failed: ${a}`)}}s(ne,"handleAttachUpgrade");export{M as REMOTE_ATTACH_HEARTBEAT_MS,J as REMOTE_ATTACH_QUIET_MS,ne as handleAttachUpgrade,E as isCleanViewerClose,u as refuseUpgrade,W as startRemoteAttachHeartbeat};
|