@north-light/cli 0.3.481 → 0.3.483
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/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/grants.d.ts +6 -4
- package/dist/builtin-memory/crouter-sdk/release-notes.md +16 -0
- package/dist/builtin-memory/northlight-apps/concepts/providers.md +4 -3
- package/dist/builtin-memory/northlight-apps/guides/grant-changes.md +11 -9
- package/dist/clients/attach/viewer.js +1 -1
- package/dist/core/command-plugins/compose.js +1 -1
- package/dist/core/command-plugins/transport/http-plan.d.ts +5 -0
- package/dist/core/command-plugins/transport/http-plan.js +1 -1
- package/dist/core/grants/mirror.d.ts +1 -1
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/runs/operations.d.ts +5 -0
- package/dist/core/runs/operations.js +5 -5
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/docs/apps/concepts/providers.md +4 -3
- package/docs/apps/guides/grant-changes.md +11 -9
- package/docs/sdk/release-notes.md +16 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
package/dist/api/contract.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The contract level this build serves and requires. Written by `scripts/contract.mjs release`; never edit by hand. */
|
|
2
|
-
export declare const CONTRACT_LEVEL = "1.
|
|
2
|
+
export declare const CONTRACT_LEVEL = "1.9";
|
|
3
3
|
/** Request header naming the one contract level the caller needs, e.g. `1.3`. Optional: absent means no check. */
|
|
4
4
|
export declare const REQUIRED_CONTRACT_HEADER = "Crouter-Required-Contract";
|
|
5
5
|
/** Response header listing the contract levels the server serves, comma-separated, one per major, e.g. `1.3`. */
|
package/dist/api/contract.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.
|
|
1
|
+
var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.9",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
|
package/dist/api/dto/grants.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
/** A provider's setup state for the account (directory spec, "Provider setup state"). */
|
|
2
|
-
export type SetupState = 'not_started' | 'pending' | 'ready' | 'failed';
|
|
1
|
+
/** A provider's setup state for the account (directory spec, "Provider setup state"). `unavailable` means the provider does not offer this setup to the account; it is not `failed`, and there is nothing for the person to do. */
|
|
2
|
+
export type SetupState = 'not_started' | 'pending' | 'ready' | 'failed' | 'unavailable';
|
|
3
3
|
/** One provider the grantee requires, as the directory's grant sync carried it. */
|
|
4
4
|
export interface ProviderDependency {
|
|
5
5
|
state: SetupState;
|
|
6
|
-
/** The provider's own sentence for `pending` and `
|
|
6
|
+
/** The provider's own sentence for `pending`, `failed` and `unavailable` (its `message_pending` / `message_failed` / `message_unavailable`); absent for `not_started` and `ready`. */
|
|
7
7
|
message?: string;
|
|
8
|
+
/** Where in its journey the provider is, when it reports one (for example `researching`, or `downloaded` on `device`). The values belong to the provider and may grow, so treat an unknown one as no step. Absent when the provider reports none. Changes `version` like any other field. */
|
|
9
|
+
step?: string;
|
|
8
10
|
/** Absolute URL of the directory's setup page for this provider and this app (`<issuer>/connect/setup/<provider>?client_id=<grantee>`); the app may append `return_to` (its own registered redirect origin) so Continue brings the person back. */
|
|
9
11
|
next: string;
|
|
10
12
|
}
|
|
@@ -21,7 +23,7 @@ export interface GrantGetDTO {
|
|
|
21
23
|
}[];
|
|
22
24
|
spend_cap: number | null;
|
|
23
25
|
payer: 'user';
|
|
24
|
-
/** `{ <
|
|
26
|
+
/** `{ <setup id>: ProviderDependency }` (a setup id is a provider id such as `context`, or a dotted level such as `context.deep`) as the directory's grant sync carried it; `{}` when the grantee has none. The daemon performs no `requires` check: an app follows this with the SDK grant watch (`oauth.watchGrants`, or `grant.get` with `wait`/`since`) and refuses its own operation with `provider_setup_pending`. When an entry's `state` is not `ready`, show its `message` (the provider's own sentence, when it has one) and send the person to its `next` (the directory's setup page; not for `unavailable`, where there is nothing for them to do) — never copy the provider's prose into the app. */
|
|
25
27
|
dependencies: Record<string, ProviderDependency>;
|
|
26
28
|
/** Opaque; changes exactly when `rev`, `resolved_scopes`, `spend_cap`, `payer` or `dependencies` changes (never on a periodic sync alone or a new token). Pass it back as `since` with `wait` to hold the read until it changes. */
|
|
27
29
|
version: string;
|
|
@@ -9,6 +9,22 @@ when-and-why-to-read: When you need Release notes, this knowledge should be read
|
|
|
9
9
|
|
|
10
10
|
Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
|
|
11
11
|
|
|
12
|
+
## 0.3.483
|
|
13
|
+
|
|
14
|
+
### `unavailable`, `step` and `setupLink` on `grant.get` dependencies (additive for the SDK; a type change for exhaustive switches)
|
|
15
|
+
|
|
16
|
+
- `SetupState` gains `unavailable`: the provider does not offer this setup to the person's account. It is not `failed`. If you `switch` over `state` exhaustively, add a case; show `message` and do not link `next`.
|
|
17
|
+
- `ProviderDependency` gains an optional `step: string` (for example `researching`, or `downloaded` on `device`). Its values belong to the provider and may grow: ignore one you do not know. A change to `step` alone changes `grant.get`'s `version`, so `oauth.watchGrants` delivers it.
|
|
18
|
+
- `dependencies` keys may be dotted setup ids such as `context.deep`.
|
|
19
|
+
- New `setupLink(dependency, {returnTo})` returns `dependency.next` with `return_to` set and URL-encoded, keeping the query `next` already has (such as `client_id`). Use it instead of building the link yourself.
|
|
20
|
+
- A runtime older than this release drops a dependency whose state is `unavailable` and silently loses `step`, so an app that reads either needs a runtime on this release.
|
|
21
|
+
|
|
22
|
+
## 0.3.482
|
|
23
|
+
|
|
24
|
+
### `runs.start` accepts `deadline` (additive, no app change)
|
|
25
|
+
|
|
26
|
+
`RunStartParams.deadline` bounds a run's wall-clock time, measured from start: one or more `<int>[smhd]` parts (`10m`, `1h30m`), at most `24h`. When it passes, the run settles with `outcome.reason` `deadline_exceeded` and its agent is stopped. A malformed, zero or over-24h value is refused with 400 `invalid_request` and `param: 'deadline'`, and no run is created. `deadline` takes part in the idempotency body, so the same key with another deadline is a 409 `idempotency_conflict`. `model` (already accepted) and `deadline` work on `isolated` runs. `payer`, `model_policy` and `kind` are still refused.
|
|
27
|
+
|
|
12
28
|
## 0.3.481
|
|
13
29
|
|
|
14
30
|
### `npx @north-light/sdk login`, `logout`, `whoami`, `register` and `apps` (additive, no app change)
|
|
@@ -52,11 +52,11 @@ A call is refused when the scope is missing from the grant. The refusal is an `A
|
|
|
52
52
|
|
|
53
53
|
Some apps do not work until a provider is set up for the person. Name it under **What your app requires** on the **What it asks for** tab. The person is then taken through that provider's setup the first time they sign in. Context, for example, asks for a profile URL, and its state stays `pending` for a few minutes afterwards.
|
|
54
54
|
|
|
55
|
-
Read the state on each request that depends on it. `runtime.grant.get()` returns `dependencies`, one entry per
|
|
55
|
+
Read the state on each request that depends on it. `runtime.grant.get()` returns `dependencies`, one entry per setup id the app requires or lists as optional, plus any the directory implies (such as `device` for `context.deep`), keyed by setup id (or `{}` when it has none):
|
|
56
56
|
|
|
57
57
|
```ts
|
|
58
58
|
const {dependencies} = await runtime.grant.get();
|
|
59
|
-
const context = dependencies['context']; // {state, message?, next} or undefined
|
|
59
|
+
const context = dependencies['context']; // {state, message?, next, step?} or undefined
|
|
60
60
|
if (context?.state !== 'ready') {
|
|
61
61
|
// Show context.message and link context.next; never hard-code either.
|
|
62
62
|
}
|
|
@@ -68,8 +68,9 @@ if (context?.state !== 'ready') {
|
|
|
68
68
|
| `pending` | The provider is working. |
|
|
69
69
|
| `ready` | Calls that depend on it work. |
|
|
70
70
|
| `failed` | Setup failed. `message` says why. |
|
|
71
|
+
| `unavailable` | The provider does not offer this to the person's account. `message` says so; there is nothing for them to do. |
|
|
71
72
|
|
|
72
|
-
`message` is the provider's own sentence, present for `pending` and `
|
|
73
|
+
`message` is the provider's own sentence, present for `pending`, `failed` and `unavailable`. `step` is present when the provider reports where things stand (for example `researching`); its values may grow, so ignore one you do not know. `next` is the absolute URL of the setup page for this provider and your app; `setupLink(dependency, {returnTo})` from `@north-light/sdk` adds a `return_to` with your app's registered origin so the person returns to you, and keeps the query `next` already carries. A key can contain a dot (`context.deep`). An entry is missing when the runtime has not synced the grant since the app began requiring the provider: treat that as `not_started`. The runtime does not block your calls for you, so refuse the dependent operation yourself. To act the moment a state turns `ready`, follow it with `oauth.watchGrants`; see [[northlight-apps/guides/grant-changes]].
|
|
73
74
|
|
|
74
75
|
## Checked and not checked
|
|
75
76
|
|
|
@@ -41,31 +41,33 @@ await watcher.stop();
|
|
|
41
41
|
|
|
42
42
|
## Wait on a provider your app requires
|
|
43
43
|
|
|
44
|
-
An app whose registration `requires` a provider (the console's **What your app requires**) needs that provider set up for the person before the operations that depend on it work. The runtime does not check this for you. `grant.get()` returns `dependencies`: one entry per
|
|
44
|
+
An app whose registration `requires` a provider (the console's **What your app requires**) needs that provider set up for the person before the operations that depend on it work. The runtime does not check this for you. `grant.get()` returns `dependencies`: one entry per setup id the app requires or lists as optional (plus any the directory implies, such as `device` for `context.deep`), keyed by setup id, or `{}` when it has none. Read it on each request that needs the provider; do not store it.
|
|
45
45
|
|
|
46
46
|
```ts
|
|
47
|
-
import type
|
|
47
|
+
import {setupLink, type ProviderDependency} from '@north-light/sdk';
|
|
48
48
|
|
|
49
|
-
// ProviderDependency is { state: 'not_started' | 'pending' | 'ready' | 'failed'; message?: string; next: string }.
|
|
49
|
+
// ProviderDependency is { state: 'not_started' | 'pending' | 'ready' | 'failed' | 'unavailable'; message?: string; next: string; step?: string }.
|
|
50
50
|
const {dependencies} = await runtime.grant.get();
|
|
51
51
|
const context: ProviderDependency | undefined = dependencies['context'];
|
|
52
52
|
if (context?.state !== 'ready') {
|
|
53
53
|
// Show the provider's own sentence and link its setup page; never hard-code either.
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Nothing to link when the provider does not offer this to the person (`unavailable`).
|
|
55
|
+
const next = context && context.state !== 'unavailable' ? setupLink(context, {returnTo: 'https://app.example.com'}) : undefined; // your registered redirect origin
|
|
56
56
|
throw new AppError('provider_setup_pending', context?.message ?? 'Finish setting up Context, then try again.', {
|
|
57
57
|
status: 403,
|
|
58
58
|
retryable: false,
|
|
59
|
-
details: {provider: 'context', state: context?.state ?? 'not_started', message: context?.message, next
|
|
59
|
+
details: {provider: 'context', state: context?.state ?? 'not_started', message: context?.message, next},
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
`AppError` stands for your app's own typed error ([[northlight-apps/errors]]).
|
|
65
65
|
|
|
66
|
-
- `state` is `not_started` (the person has not done their part), `pending` (the provider is working), `ready` or `
|
|
67
|
-
- `message` is the provider's own sentence for `pending` and `
|
|
68
|
-
- `
|
|
66
|
+
- `state` is `not_started` (the person has not done their part), `pending` (the provider is working), `ready`, `failed`, or `unavailable` (the provider does not offer this to the person's account; there is nothing for them to do, so do not link `next`).
|
|
67
|
+
- `message` is the provider's own sentence for `pending`, `failed` and `unavailable`. It is absent for `not_started` and `ready`, so keep a short fallback.
|
|
68
|
+
- `step` is present when the provider reports where the person or the work is, for example `researching`. Its values belong to the provider and may grow: show a known one in your own words and ignore an unknown one. A change to `step` alone changes the grant's `version`, so `watchGrants` delivers it.
|
|
69
|
+
- `next` is the absolute URL of the directory's setup page for this provider and your app. Use `setupLink(dependency, {returnTo})` to add `return_to` (your app's registered redirect origin) so the person comes back to you. It keeps the query `next` already has, such as `client_id`, and encodes `returnTo`; do not build the URL yourself.
|
|
70
|
+
- A key can contain a dot. `context.deep` is a level of the `context` provider; read it as `dependencies['context.deep']`.
|
|
69
71
|
- An entry is missing when the runtime has not synced the grant since the app began requiring the provider. Treat it as `not_started`.
|
|
70
72
|
|
|
71
73
|
Refuse the dependent operation with `provider_setup_pending` (403), carrying `state`, `message` and `next` in `details`. To act when the state turns `ready`, use `watchGrants` above instead of polling.
|
|
@@ -1729,7 +1729,7 @@ ${u}`,"1").then(l=>{let d=a.options[Number.parseInt(l,10)-1]?.id;if(d===void 0)t
|
|
|
1729
1729
|
`));return}s(E),i.login(p,"oauth",_).then(()=>{m||(r(`Logged in to ${f.name}`),n(),y())}).catch(C).finally(()=>s(void 0))}else w.showPrompt(`Enter API key for ${f.name}:`,"sk-...").then(async I=>{if(!m){if(I.trim()){if(!E())return;await f_n(p,{type:"api_key",key:I.trim()},o),r(`API key saved for ${f.name}`),n()}else r("No key entered \u2014 login cancelled");y()}}).catch(()=>y())},d=new iS("login",a,l,()=>c());return{component:d,focus:d}}}async function W_n(t,e,n){let{runtime:r,authPath:i}=await F_n(t),o=await U_n(r);return({close:s})=>{let a=u=>{let l=o.find(d=>d.id===u);(async()=>{try{RS(u)?await T_n(u):await zJe(u,i),s(),n(`Logged out of ${l?.name??u}`),e()}catch(d){s(),n(`Logout error: ${d instanceof Error?d.message:String(d)}`)}})()},c=new iS("logout",o,a,()=>s());return{component:c,focus:c}}}var H_n=h(()=>{"use strict";js();VJe();D_n();Vi();O_n();Khe();B_n();uc()});function j_n(t,e){return qxn({nodeId:t.nodeId,profileId:t.meta.profile_id??void 0,remote:t.remote,role:t.role,isBrokerOffline:e})}function q_n(t,e,n,r){let{nodeId:i,socket:o,tui:s,editor:a,containers:c}=t,{setNotice:u}=r,l=()=>o.send({type:"reload_auth"}),d=n.permits("auth")?{openLoginPicker:()=>{let f=r.agentDir()??GR();p.openAuthPicker(()=>Q_n(s,f,l,m=>u(m)))},openLogoutPicker:()=>{let f=r.agentDir()??GR();p.openAuthPicker(()=>W_n(f,l,m=>u(m)))}}:{},p=new qhe(s,a,e,{onCommand:f=>o.send(f),onDialogResponse:f=>o.send(f),onNotice:f=>u(f),onGraph:r.toggleGraph,onInbox:r.toggleInbox,onHelp:r.toggleHelp,onNodeMetadata:r.openNodeMetadata,onQuit:r.quit,onCopy:()=>r.copyTarget("last-assistant"),onCopyTarget:r.copyTarget,onToggleMouse:()=>{let f=r.toggleMouse();u(f?"Mouse: wheel scrolling on, drag to select and copy":"Mouse: off \u2014 tmux drag-select and copy-mode restored")},onColor:r.setChipColor,onPromptSent:r.onPromptSent,frecency:r.frecency,knownRefNames:()=>a.getResolvedRefNames(),onToggleToolsExpand:()=>{let f=r.toggleToolsExpanded();u(f.kind==="summaries"?f.shown?"Tool calls: summarized":"Tool calls: shown":f.expanded?"Tool output: expanded":"Tool output: collapsed")},onFoldTools:()=>u(r.toggleFoldTools()?"Finished tool calls: folded to one line":"Finished tool calls: shown in full"),onShowWorking:()=>{let f=r.toggleShowWorking();u(f===null?"Consolidated replies are off (crtr sys config set consolidate_replies --value true, then re-attach)":f?"Working: shown":"Working: hidden behind each reply")},onToggleThinking:()=>u(r.toggleThinking()?"Thinking blocks: hidden":"Thinking blocks: visible"),onRequest:f=>{let m=n.refusal("engine-drive");return m===void 0?o.request(f):Promise.reject(new Error(m))},onMountPicker:f=>{c.pickerPanel.addChild(f),s.requestRender()},onUnmountPicker:()=>{c.pickerPanel.clear(),s.requestRender()},isDormant:r.isDormant,onDormantSubmit:n.permits("wake-dormant")?f=>{(async()=>{try{let m=await Vt().sendMessage(i,{body:f,from:"human"});u(m.revived?"\u21BB waking node\u2026":"\u2709 delivered \u2014 node reads on next wake")}catch(m){u(`Couldn't wake node: ${m instanceof Error?m.message:String(m)}`)}})()}:void 0,...d},n);return p}var G_n=h(()=>{"use strict";p_n();mhe();uc();H_n();Yz()});async function L6(t,e){await Vt().respondHumanRequest(t,{responses:e})}async function z_n(t,e){await Vt().dismissHumanRequest(t,e)}function V_n(t){if(t instanceof ln)return t.status===503||t.code==="daemon_unavailable";let e=t?.code;return e==="ECONNREFUSED"||e==="ENOENT"}function wu(t){return V_n(t)?"could not reach crtrd \u2014 nothing was submitted":t instanceof ln||t instanceof Error?t.message:String(t)}function B6(t){return V_n(t)?"unreachable":t instanceof ln&&t.status===409&&t.code==="already_settled"?"already_settled":"other"}var iU=h(()=>{"use strict";ud();uc()});var K_n=h(()=>{"use strict";Rr()});function Y_n(t){let e=t?.diagnostics;return Array.isArray(e)&&e.some(n=>n?.type===Shi)}var Shi,J_n=h(()=>{"use strict";Shi="crtr:managed-provider-cooling"});function rXe(t){return typeof t=="string"?t:""}function hf(t,e,n={}){return{kind:t,matchedOn:e,...n}}function Zg(t,e){return e.matchedOn==="text"?{...t,textKind:e.kind,textMatch:e.match,textMatches:e.textMatches}:t}function X_n(t){let e=Chi.flatMap(([r,i])=>{let o=t.match(i);return o?[{kind:r,match:o[0]}]:[]}),n=e[0];return n===void 0?hf("other","none"):{...hf(n.kind,"text",{match:n.match}),textMatches:e}}function Z_n(t){let e=t.retryAfterMs,n=X_n(t.message??""),r=rXe(t.code)||rXe(t.errno);if(/^(ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOENT|ENOTFOUND|EAI_AGAIN|EPIPE|EHOSTUNREACH|ENETUNREACH)$/i.test(r))return Zg(hf("connection","errno",{retryAfterMs:e}),n);if(/^ELOCKED$/i.test(r))return Zg(hf("overloaded","errno",{retryAfterMs:e}),n);let i=typeof t.status=="number"?t.status:void 0;if(i!==void 0){if(i===401||i===403)return Zg(hf("auth","status",{status:i,retryAfterMs:e}),n);if(i===429)return Zg(hf("rate-limit","status",{status:i,retryAfterMs:e}),n);if(i===500||i===502||i===503||i===504)return Zg(hf("transient-http","status",{status:i,retryAfterMs:e}),n);if(i>=500&&i<=599)return Zg(hf("overloaded","status",{status:i,retryAfterMs:e}),n);if(i>=400&&i<=499)return Zg(hf("protocol","status",{status:i,retryAfterMs:e}),n)}let o=typeof t.wsCloseCode=="number"?t.wsCloseCode:void 0;if(o===1008)return Zg(hf("protocol","websocket-close",{retryAfterMs:e}),n);if(o===1006)return Zg(hf("connection","websocket-close",{retryAfterMs:e}),n);if(o===1011){let s=rXe(t.wsCloseReason),a=X_n(s);return/no running broker/i.test(s)||a.kind==="connection"||a.kind==="connection-only"?Zg(hf("connection","websocket-close",{retryAfterMs:e}),n):Zg(hf("other","websocket-close",{retryAfterMs:e}),n)}return e===void 0?n:{...n,retryAfterMs:e}}var Chi,evn=h(()=>{"use strict";Chi=[["rate-limit",/rate.?limit|\b429\b|too many requests|usage[ _]limit|usage_not_included|usagelimiterror|insufficient_quota|quota exceeded/i],["protocol",/\b413\b|payload too large|entity too large|request too large|entity\.too\.large|body exceeds/i],["auth",/invalid_grant|refresh token|unauthori[sz]ed|invalid.?api.?key|authentication failed|\b401\b|\b403\b/i],["context-overflow",/context overflow recovery failed/i],["protocol",/websocket.*(?:closed.*\b(?:1002|1003|1007|1008|1009|1010)\b|protocol error|policy violation|unsupported data|invalid (?:frame|payload)|message too big|mandatory extension)/i],["overloaded-only",/lock file is already being held/i],["overloaded",/overloaded|temporarily unavailable/i],["service-unavailable",/service.?unavailable/i],["overloaded-only",/internal server error|api.?error|\b5\d\d\b|capacity|server.{0,3}busy/i],["connection",/connection|econnreset|etimedout|enotfound|econnrefused|network|fetch failed|socket hang|timed? out|timeout|websocket\s+(?:was\s+)?closed|websocket stream closed before response\.completed|websocket\s+error|websocket.*interrupt/i],["socket",/socket/i],["connection-only",/eai_again|epipe|ehostunreach|enetunreach|\bterminated\b|other side closed|premature close/i]]});function Jx(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}var oU=h(()=>{"use strict"});function La(t){return typeof t=="string"&&t.trim()!==""?t.trim():void 0}function Jhe(t){return typeof t=="number"&&Number.isFinite(t)?t:void 0}function Xhe(t){return typeof t=="string"||typeof t=="number"?t:void 0}function sU(t){return Jx(t)?t:void 0}function Thi(t){let e=La(t.errorMessage)??La(t.message)??La(t.reason)??La(t.statusText),n={...e===void 0?{}:{message:e},...Jhe(t.status)===void 0?{}:{status:Jhe(t.status)},...Xhe(t.code)===void 0?{}:{code:Xhe(t.code)},...La(t.errno)===void 0?{}:{errno:La(t.errno)},...La(t.body)===void 0?{}:{body:La(t.body)}};return Object.keys(n).length===0?void 0:n}function Zhe(t){let e=sU(t),n=sU(e?.headers);if(e===void 0||n===void 0)return;let r={};for(let[i,o]of Object.entries(n)){if(Object.keys(r).length>=khi)break;typeof o=="string"&&(r[i.toLowerCase()]=o.slice(0,Rhi))}return{...La(e.scope)===void 0?{}:{scope:La(e.scope)},...La(e.claim)===void 0?{}:{claim:La(e.claim)},...La(e.account)===void 0?{}:{account:La(e.account)},...La(e.model)===void 0?{}:{model:La(e.model)},headers:r}}function Dhi(t){let e=sU(t)?.diagnostics;if(Array.isArray(e))for(let n of e){let r=sU(n);if(r?.type!==Ihi)continue;let i=sU(r.details);if(i===void 0)continue;let o=Array.isArray(i.causes)?i.causes.flatMap(s=>{let a=sU(s),c=a===void 0?void 0:Thi(a);return c===void 0?[]:[c]}):void 0;return{...Jhe(i.status)===void 0?{}:{status:Jhe(i.status)},...Xhe(i.code)===void 0?{}:{code:Xhe(i.code)},...La(i.errno)===void 0?{}:{errno:La(i.errno)},...La(i.body)===void 0?{}:{body:La(i.body)},...o===void 0||o.length===0?{}:{causes:o},...Zhe(i.ratelimit)===void 0?{}:{ratelimit:Zhe(i.ratelimit)}}}}function tvn(t){return Dhi(t)}var Ihi,khi,Rhi,iXe=h(()=>{"use strict";oU();Ihi="crtr:provider-failure";khi=48,Rhi=256});function Mhi(t){switch(t.kind){case"rate-limit":return"rate-limit";case"auth":return"auth";case"protocol":return"protocol";case"context-overflow":return"context-overflow";case"overloaded":case"overloaded-only":case"transient-http":return"overloaded";case"connection":case"connection-only":return"connection";case"socket":case"service-unavailable":case"other":return"other"}}function Ohi(t,e){return e==="auth"||e==="protocol"||e==="context-overflow"?"fatal":t==="pi\u2192provider"?e==="connection"||e==="rate-limit"||e==="overloaded"?"auto":"fatal":"manual"}function Nhi(t){if(typeof t=="string")return t;if(!Jx(t))return"";let e=[t.message,t.errorMessage,t.reason,t.body,t.statusText];for(let r of e)if(typeof r=="string"&&r!=="")return r;let n=t.error;if(Jx(n)){let r=n.message;if(typeof r=="string"&&r!=="")return r;let i=n.reason;if(typeof i=="string"&&i!=="")return i}return""}function ege(t,e){if(t==="pi\u2192provider"&&Y_n(e))return{kind:"rate-limit",disposition:"auto"};let n=Jx(e)?e:{},r=tvn(e),i=r?.causes??[],o=Z_n({code:n.code??r?.code??i.find(a=>a.code!==void 0)?.code,errno:n.errno??r?.errno??i.find(a=>a.errno!==void 0)?.errno,status:n.status??r?.status,wsCloseCode:n.code,wsCloseReason:n.reason,message:[Nhi(e),r?.body,...i.flatMap(a=>[a.message,a.body])].filter(a=>typeof a=="string"&&a!=="").join(" ")}),s=Mhi(o);return{kind:s,disposition:Ohi(t,s)}}var tge=h(()=>{"use strict";J_n();evn();iXe();oU()});function nvn(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function Lhi(t){if(!nvn(t)||typeof t.provider!="string"||t.provider===""||typeof t.model!="string"||t.model==="")return;let e=typeof t.status=="number"&&Number.isFinite(t.status)?t.status:void 0,n=typeof t.code=="string"||typeof t.code=="number"?t.code:void 0,r=typeof t.errno=="string"?t.errno:void 0,i=typeof t.body=="string"?t.body:void 0,o=Zhe(t.ratelimit),s=Array.isArray(t.causes)?t.causes.flatMap(a=>{if(!nvn(a))return[];let c={...typeof a.message=="string"?{message:a.message}:{},...typeof a.status=="number"&&Number.isFinite(a.status)?{status:a.status}:{},...typeof a.code=="string"||typeof a.code=="number"?{code:a.code}:{},...typeof a.errno=="string"?{errno:a.errno}:{},...typeof a.body=="string"?{body:a.body}:{}};return Object.keys(c).length===0?[]:[c]}):void 0;return{provider:t.provider,model:t.model,...e===void 0?{}:{status:e},...n===void 0?{}:{code:n},...r===void 0?{}:{errno:r},...i===void 0?{}:{body:i},...s===void 0||s.length===0?{}:{causes:s},...o===void 0?{}:{ratelimit:o}}}function rvn(t){let e=t.provider_error_json,n=typeof e=="string"?Lhi(JSON.parse(e)):void 0;if(typeof e=="string"&&n===void 0)throw new Error(`invalid provider error on stored fault ${t.fault_id}`);let r={disposition:t.retry_disposition,...t.retry_by===null?{}:{by:t.retry_by},...t.retry_attempt===null?{}:{attempt:t.retry_attempt},...t.retry_max===null?{}:{max:t.retry_max},...t.retry_next_at===null?{}:{nextAt:t.retry_next_at}};return{faultId:t.fault_id,link:t.link,op:t.op,kind:t.kind,message:t.message,since:t.since,operation_id:t.operation_id,retry:r,...t.anchor_entry_id===null?{}:{anchorEntryId:t.anchor_entry_id},...n===void 0?{}:{providerError:n}}}var J6s,Phi,X6s,oXe=h(()=>{"use strict";Ht();di();Rr();tge();iXe();Of();LQ();J6s=5*6e4,Phi=["pi\u2192provider","viewer\u2194broker","relay\u2194broker","viewer\u2194crtrd","daemon\u2192node","crtr\u2192pi"],X6s=[...Phi,"broker\u2194crtrd"]});var ivn=h(()=>{"use strict"});var Bhi,lVs,$6=h(()=>{"use strict";Ht();di();Rr();ivn();Of();Bhi=`SELECT d.delivery_id FROM deliveries d JOIN messages m ON m.message_id = d.message_id
|
|
1730
1730
|
WHERE d.destination_id = ? AND d.wake = ? AND d.state = 'pending' AND d.canceled_at IS NULL
|
|
1731
1731
|
AND NOT (d.wake = 0 AND m.kind = 'message' AND coalesce(json_extract(m.body_json, '$.start_turn'), 1) = 0)`,lVs=`UPDATE deliveries SET state = 'handed_off', attempts = attempts + 1, claim_owner = ?, claimed_at = ?, updated_at = ?
|
|
1732
|
-
WHERE delivery_id IN (${Bhi})`});var aU,F6=h(()=>{"use strict";_o();A0();aU=["crtr:llm","crtr:act","crtr:schedule","crtr:memory:rw:user","crtr:memory:manage:user","crtr:files:rw:user:."]});var nge=h(()=>{"use strict";ud();Vo()});function ovn(t){if(!Array.isArray(t))throw new TypeError("grant scopes must be a list");return t.map(e=>{if(!e||typeof e!="object"||typeof e.scope!="string"||!["always","ask"].includes(e.mode))throw new TypeError("invalid grant scope entry");return{scope:e.scope,mode:e.mode}})}function Fhi(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let{state:e,message:n,next:r}=t;return typeof e!="string"||!$hi.includes(e)||n!==void 0&&typeof n!="string"||typeof r!="string"||!r?null:{state:e,...n===void 0?{}:{message:n},next:r}}function Uhi(t){if(!t||typeof t!="object"||Array.isArray(t))return{};let e={};for(let[n,r]of Object.entries(t)){let i=Fhi(r);i&&(e[n]=i)}return e}function Qhi(t){return{id:t.id,runtime:t.runtime,sub:t.sub,grantee:t.grantee,rev:t.rev,scopes:ovn(JSON.parse(t.scopes_json)),resolved_scopes:ovn(JSON.parse(t.resolved_scopes_json)),spend_cap:t.spend_cap,expires_at:t.expires_at,status:t.status,revoked_at:t.revoked_at,approved_at:t.approved_at,payer:t.payer,dependencies:Uhi(JSON.parse(t.dependencies_json)),run_notice_url:t.run_notice_url??null,synced_at:t.synced_at}}function svn(t){if(!t||t.status!=="active")return null;try{return Whi(Qhi(t))}catch(e){if(e instanceof TypeError||e instanceof SyntaxError)return null;throw e}}function Whi(t){if(t.status!=="active")return null;try{let e=t.resolved_scopes.map(({scope:n})=>zV(n));return{...t,resolved:t.resolved_scopes.flatMap(({mode:n},r)=>n==="always"?[e[r]]:[])}}catch(e){if(e instanceof TypeError||e instanceof SyntaxError)return null;throw e}}function U6(t){return svn(ce(ir(),"SELECT * FROM grants WHERE id = ?").get(t))}function rge(t,e){return svn(ce(ir(),"SELECT * FROM grants WHERE grantee = ? AND sub = ?").get(t,e))}var $hi,cU=h(()=>{"use strict";Ht();hb();di();nge();$hi=["not_started","pending","ready","failed"]});var avn=h(()=>{"use strict";Xx()});var cvn=h(()=>{"use strict"});var uvn=h(()=>{"use strict";js();Xx()});var aXe=h(()=>{"use strict"});var lvn=h(()=>{"use strict";js();Xx();aXe();Of()});var Xx=h(()=>{"use strict";Vi();avn();cvn();uvn();lvn();aXe()});var uXe=h(()=>{"use strict"});function ige(t,e,n){if(n.length===0)return;let r=ce(t,"INSERT INTO memory_reads (node_id, name, scope, at, type) VALUES (?, ?, ?, ?, ?)");for(let i of n)r.run(e,i.name,i.scope,i.at,i.type)}var XVs,lXe=h(()=>{"use strict";Ht();di();XVs=720*60*60*1e3});function TD(t,e=ir()){let n=ce(e,`SELECT n.run_id, n.isolated, r.isolated AS root_isolated FROM nodes n
|
|
1732
|
+
WHERE delivery_id IN (${Bhi})`});var aU,F6=h(()=>{"use strict";_o();A0();aU=["crtr:llm","crtr:act","crtr:schedule","crtr:memory:rw:user","crtr:memory:manage:user","crtr:files:rw:user:."]});var nge=h(()=>{"use strict";ud();Vo()});function ovn(t){if(!Array.isArray(t))throw new TypeError("grant scopes must be a list");return t.map(e=>{if(!e||typeof e!="object"||typeof e.scope!="string"||!["always","ask"].includes(e.mode))throw new TypeError("invalid grant scope entry");return{scope:e.scope,mode:e.mode}})}function Fhi(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let{state:e,message:n,next:r,step:i}=t;return typeof e!="string"||!$hi.includes(e)||n!==void 0&&typeof n!="string"||i!==void 0&&typeof i!="string"||typeof r!="string"||!r?null:{state:e,...n===void 0?{}:{message:n},next:r,...i===void 0?{}:{step:i}}}function Uhi(t){if(!t||typeof t!="object"||Array.isArray(t))return{};let e={};for(let[n,r]of Object.entries(t)){let i=Fhi(r);i&&(e[n]=i)}return e}function Qhi(t){return{id:t.id,runtime:t.runtime,sub:t.sub,grantee:t.grantee,rev:t.rev,scopes:ovn(JSON.parse(t.scopes_json)),resolved_scopes:ovn(JSON.parse(t.resolved_scopes_json)),spend_cap:t.spend_cap,expires_at:t.expires_at,status:t.status,revoked_at:t.revoked_at,approved_at:t.approved_at,payer:t.payer,dependencies:Uhi(JSON.parse(t.dependencies_json)),run_notice_url:t.run_notice_url??null,synced_at:t.synced_at}}function svn(t){if(!t||t.status!=="active")return null;try{return Whi(Qhi(t))}catch(e){if(e instanceof TypeError||e instanceof SyntaxError)return null;throw e}}function Whi(t){if(t.status!=="active")return null;try{let e=t.resolved_scopes.map(({scope:n})=>zV(n));return{...t,resolved:t.resolved_scopes.flatMap(({mode:n},r)=>n==="always"?[e[r]]:[])}}catch(e){if(e instanceof TypeError||e instanceof SyntaxError)return null;throw e}}function U6(t){return svn(ce(ir(),"SELECT * FROM grants WHERE id = ?").get(t))}function rge(t,e){return svn(ce(ir(),"SELECT * FROM grants WHERE grantee = ? AND sub = ?").get(t,e))}var $hi,cU=h(()=>{"use strict";Ht();hb();di();nge();$hi=["not_started","pending","ready","failed","unavailable"]});var avn=h(()=>{"use strict";Xx()});var cvn=h(()=>{"use strict"});var uvn=h(()=>{"use strict";js();Xx()});var aXe=h(()=>{"use strict"});var lvn=h(()=>{"use strict";js();Xx();aXe();Of()});var Xx=h(()=>{"use strict";Vi();avn();cvn();uvn();lvn();aXe()});var uXe=h(()=>{"use strict"});function ige(t,e,n){if(n.length===0)return;let r=ce(t,"INSERT INTO memory_reads (node_id, name, scope, at, type) VALUES (?, ?, ?, ?, ?)");for(let i of n)r.run(e,i.name,i.scope,i.at,i.type)}var XVs,lXe=h(()=>{"use strict";Ht();di();XVs=720*60*60*1e3});function TD(t,e=ir()){let n=ce(e,`SELECT n.run_id, n.isolated, r.isolated AS root_isolated FROM nodes n
|
|
1733
1733
|
LEFT JOIN nodes r ON r.node_id = n.run_id WHERE n.node_id = ?`).get(t);if(!n){let r=ce(e,"SELECT run_id FROM deleted_isolated_nodes WHERE node_id = ?").get(t);return r?{run:r.run_id,isolated:!0}:null}return{run:n.run_id??t,isolated:n.isolated===1||n.root_isolated===1}}function pvn(t,e=ir()){let n=ce(e,"SELECT registered_by FROM custom_objects WHERE object = ?").get(t);return n?.registered_by?TD(n.registered_by,e)??{run:n.registered_by,isolated:!1}:null}function fvn(t,e){return t.run===e.run||!t.isolated&&!e.isolated}var oge=h(()=>{"use strict";di()});function sge(t,e,n,r,i,o){if(e.class==="person"||r.length===0||i!==null&&i.type!=="document")return null;let s=e.launched;if(s==null||o==="read"&&i?.kind==="report"&&r[0].type==="node"&&qhi(t,e,r[0].id))return null;let a=age(t,mvn(t,n),o,r);return s.some(c=>qc(c,a))?null:a}function qhi(t,e,n){let r=e.class==="node"?TD(e.node.id,t):e.class==="person"?null:e.place??null,i=TD(n,t);return r!==null&&i!==null&&r.run===i.run}function age(t,e,n,r){let i=r[0];return i.type!=="profile"?`crtr:memory:${n}:${e}`:`crtr:memory:${n}:${e}:profile:${mvn(t,i)}`}var mvn,cge=h(()=>{"use strict";Ht();hb();oge();mvn=(t,e)=>ce(t,"SELECT external_id FROM identities WHERE object = ?").get(e.rowid).external_id});function gvn(t,e,n){if(e.class==="person"||n.length===0||n[0].type!=="node"||e.class==="app"&&e.place===void 0)return!0;let r=hvn.get(e);if(r===void 0&&(r=e.class==="app"?e.place:e.class==="node"?TD(e.node.id,t):e.place??pvn(e.object.rowid,t),hvn.set(e,r)),r===null)return!0;let i=TD(n[0].id,t);return i===null?!r.isolated:fvn(r,i)}var yvn,hvn,Avn=h(()=>{"use strict";hb();Q6();cge();oge();yvn={identity:Zx,sync:"runtime",canRead(t,e,n,r,i){if(e.class==="person"||r.length===0)return!0;if(e.app.rowid===n.rowid)return gvn(t,e,r)&&sge(t,e,n,r,i,"read")===null;let o=Zx(t,n).external_id,s=a=>e.scopes!==null&&e.scopes.some(c=>qc(c,a));return r[0].type==="node"?s(`crtr:objects:read:${o}`)||i.type==="document"&&i.kind==="report"&&s(`crtr:conversations:read:${o}`):s(age(t,o,"read",r))},canChange(t,e,n,r,i){if(e.class==="person")return!0;if(e.app.rowid===n.rowid)return r.length===0||gvn(t,e,r)&&sge(t,e,n,r,i,"rw")===null;if(r.length===0||r[0].type==="node")return!1;let o=age(t,Zx(t,n).external_id,"rw",r);return e.scopes!==null&&e.scopes.some(s=>qc(s,o))}},hvn=new WeakMap});var wvn,xvn=h(()=>{"use strict";Ht();hb();Q6();wvn={identity:Zx,sync:"runtime",canRead(t,e,n,r,i){return e.class==="person"||e.class==="object"&&e.owner.rowid===n.rowid?!0:e.scopes===null?!1:e.app.rowid===n.rowid||r.length===0?!0:r[0].type!=="document"?!1:e.scopes.some(o=>qc(o,"crtr:memory:manage:user"))?!0:i.privacy==="public"},canChange(t,e,n,r,i){if(e.class==="person"||e.class==="object"&&e.owner.rowid===n.rowid||e.app.rowid===n.rowid||e.scopes?.some(c=>qc(c,"crtr:memory:manage:user")))return!0;if(r.length===0)return e.scopes?.some(c=>qc(c,"crtr:memory:rw:user"))??!1;if(r[0].type!=="document")return!1;let o=`crtr:memory:rw:user:${i.name}`;if(e.scopes===null||!e.scopes.some(c=>qc(c,o)))return!1;let s=ce(t,`SELECT author.type, author.owner AS app FROM revisions r JOIN objects author ON author.rowid = r.author
|
|
1734
1734
|
WHERE r.document = ? ORDER BY r.number, r.created_at LIMIT 1`).get(r[0].rowid);return s?s.type==="person"||s.app===e.app.rowid?!0:ce(t,`SELECT g.resolved_scopes_json FROM grants g JOIN identities i ON i.external_id = g.grantee
|
|
1735
1735
|
WHERE i.object = ? AND i.scheme = 'app'`).all(s.app).some(({resolved_scopes_json:c})=>JSON.parse(c).some(({scope:u,mode:l})=>l==="always"&&qc(u,"crtr:memory:manage:user"))):!1}}});import{join as dXe,relative as uge,resolve as Ghi,sep as lge}from"node:path";function uU(t,e){let n=uge(t,e);return n===""||n!==".."&&!n.startsWith(`..${lge}`)&&!n.startsWith(lge)}function pXe(t,e,n){let r=Ghi(e);if(t===null||process.platform!=="linux"&&t.app==="app:terminal"){if(process.platform!=="linux")return{root:"/"};let p=[C9(),Sh()].find(f=>uU(f,r));return p?{root:p}:{missing:null}}let i=qa(t.app);if(t.run&&uU(i.root,r)){let[p,f]=uge(i.root,r).split(lge);if(p==="nodes"&&f)return t.run.reaches(f)?{root:dXe(i.nodes,f)}:{hidden:!0};if(t.run.isolated)return{hidden:!0}}for(let p of[i.files,i.nodes])if(uU(p,r))return{root:p};let o=p=>t.resolved.some(f=>qc(f,p))&&(t.launched===null||t.launched.some(f=>qc(f,p))),s=(p,f)=>{let m=uge(f,r)||".",g=x=>x.flatMap(E=>{let _=/^crtr:files:(read|rw):(user|app:[A-Za-z0-9_-]{1,64}):(.+)$/.exec(E);return!_||_[2]!==p||n==="rw"&&_[1]!=="rw"?[]:_[3]==="."||_[3]===m||m.startsWith(`${_[3]}/`)?[dXe(f,_[3]==="."?"":_[3])]:[]}),y=g(t.resolved),w=t.launched===null?y:g(t.launched);return!y.length||!w.length?{missing:`crtr:files:${n}:${p}:${m}`}:{root:[...y,...w].reduce((x,E)=>E.length>x.length?E:x)}};if(uU(XM(),r))return s("user",XM());let[a,c,u,l]=uge(Sh(),r).split(lge);if(!uU(Sh(),r)||!a||!/^[A-Za-z0-9_-]{1,64}$/.test(a))return{missing:null};let d=qa(`app:${a}`);return c==="files"?s(`app:${a}`,d.files):c!=="nodes"||n!=="read"?{missing:null}:o(`crtr:objects:read:app:${a}`)?{root:d.nodes}:u&&l==="attachments"&&o(`crtr:conversations:read:app:${a}`)?{root:dXe(d.nodes,u,l)}:{missing:`crtr:objects:read:app:${a}`}}var dge=h(()=>{"use strict";hb();Vi()});function fXe(t,e,n,r){if(e.class==="object")return e.repos.some(s=>s.rowid===n.rowid);if(e.class!=="node")return!1;let i=[...e.checkouts,...r==="read"?e.projectCheckouts:[]].filter(s=>s.repo.rowid===n.rowid);if(i.length===0)return!1;let o={app:Zx(t,e.app).external_id,resolved:e.scopes??[],launched:null};return i.some(s=>"root"in pXe(o,s.root,r))}var bvn,mXe=h(()=>{"use strict";dge();Q6();bvn={identity:Zx,sync:"repo-ref",canRead(t,e,n,r){return e.class==="person"||r.length===0?!0:fXe(t,e,n,"read")},canChange(t,e,n,r,i){return i.privacy!=="public"?!1:e.class==="person"?!0:fXe(t,e,n,"rw")}}});function Zx(t,e){let n=ce(t,"SELECT object, scheme, external_id FROM identities WHERE object = ?").get(e.rowid);if(!n)throw new Error(`${e.type} ${e.id} has no identity`);return n}var Evn,Q6=h(()=>{"use strict";Ht();Avn();xvn();mXe();Evn={person:wvn,app:yvn,repo:bvn}});function pge(t,e){return ce(t,`WITH RECURSIVE chain(rowid, depth) AS (
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var k=Object.defineProperty;var u=(n,e)=>k(n,"name",{value:e,configurable:!0});import{resolve as x}from"node:path";import{envNodeId as C}from"../../shared/env.js";import{cliClient as
|
|
1
|
+
var k=Object.defineProperty;var u=(n,e)=>k(n,"name",{value:e,configurable:!0});import{resolve as x}from"node:path";import{envNodeId as C}from"../../shared/env.js";import{cliClient as l}from"../../commands/api-client.js";import{apiErrorToCliError as j}from"../io.js";import{ApiError as L}from"../../api/index.js";import{defineBranch as h,defineLeaf as T}from"../command.js";import{resolveCommandRegistry as U}from"../command-manifests/registry.js";import{executeExternalLeaf as M}from"./transport/exec-invoke.js";import{invokeHttpLeaf as A}from"./transport/http-invoke.js";import{readPathParam as S}from"./transport/http-plan.js";async function N(n){const e=await l().request("GET",`/v1/providers/${encodeURIComponent(n)}/tools`);return new Set(e.groups.flatMap(r=>r.leaves.map(a=>a.tool)))}u(N,"allowedProviderTools");function m(n,e,r){return n.children.some(a=>a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))}u(m,"hasAllowedLeaf");function P(n,e,r){return n.children.filter(a=>a.tier!=="hidden"&&(a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))).length}u(P,"visibleChildCount");function _(n,e,r){return async()=>{if(!C())return n.help;const a=await N(e.id);return{...n.help,listing:n.help.listing?.flatMap(t=>{const i=n.children.find(s=>s.name===t.name);if(i?.kind==="leaf")return a.has([...r,i.name].join(" "))?[t]:[];if(i?.kind!=="branch")return[];const o=[...r,i.name];if(!m(i,o,a))return[];const p=P(i,o,a);return[{...t,...t.subCount!==void 0?{subCount:p}:{}}]})}}}u(_,"providerHelp");const $="Runs on one of your devices. Add `--machine <machine_id>` to pick which; without it the call goes to your only device, and with several the answer is `machine_required` listing each one's machine_id. The `runtime:` prefix on the id is optional.";function g(n,e,r,a=!1){return{outputKind:"object",...a?{machineSelector:!0}:{},run:u(async(t,i)=>{const o={...t};for(const p of r.params){if(!("type"in p))continue;const s=p.name.replace(/-([a-z])/g,(c,w)=>w.toUpperCase()),d=o[s];p.type==="file"&&typeof d=="string"?o[s]=x(d):p.type==="path"&&d!==void 0&&(o[s]=S(`provider command \`${n} ${e}\``,p,d))}try{return await l({timeoutMs:0}).request("POST","/v1/provider/call",{provider:n,tool:e,arguments:o,...i?.machine===void 0?{}:{machine:i.machine}},{timeout:0})}catch(p){throw p instanceof L?j(p,void 0,{providerCall:!0}):p}},"run")}}u(g,"providerLeafAdapter");function v(n,e){return/^[A-Za-z0-9_-]{1,64}$/.test(n)&&n!=="crtr"&&!e.has(n)}u(v,"validRootName");function F(n,e){const r=[],a=[];for(const t of n){const{provider:i,commands:o}=t;if(!Array.isArray(o)||t.status!=="available"&&!v(i,e))continue;if(t.status!=="available"){const s=t.error??"bundle installation pending",d=`Unavailable (${t.status}): ${s}`,c=h({name:i,rootEntry:{concept:t.display_name,desc:d,useWhen:d},help:{name:i,summary:d},children:[]});c.unavailable=d,a.push(c);continue}const p=u(s=>s.kind==="branch"?{...s,children:s.children.map(p)}:{...s,rest:{method:"GET",path:"/",params:{}}},"helpNode");for(const s of o)s.kind!=="branch"||!Array.isArray(s.children)||!v(s.name,e)||r.push({contributor:{name:i,scope:"user"},manifestName:s.name,node:p(s),adaptLeaf:u((d,c)=>g(i,c.slice(1).join(" "),d,t.transport==="machine"),"adaptLeaf")})}return[...b(U(r,e)),...a]}u(F,"composeSandboxProviderSubtrees");function y(n,e){return(r,a)=>{const t=n.transport,i=e(a);if(n.provider!==void 0)return g(n.provider.id,i.slice(1).join(" "),r,t.kind==="machine");if(t.kind==="exec"){const p=t.executable;if(p===void 0)throw new Error(`exec plugin "${n.plugin.name}" has an executable leaf without transport.executable`);return{outputKind:"object",run:u(async s=>M({plugin:n.plugin,executable:p,commandPath:i,output:r.output},s),"run")}}if(t.kind==="machine")throw new Error(`machine plugin "${n.plugin.name}" is not a capability provider`);const o=r;return{outputKind:o.rest.streaming===!0?"jsonl":"object",run:u((p,s)=>A({registration:{name:n.plugin.name,endpoint:t.endpoint,...t.authEnv!==void 0?{authEnv:t.authEnv}:{}},...n.manifest?.baseUrl!==void 0?{baseUrl:n.manifest.baseUrl}:{},...n.manifest?.timeouts!==void 0?{timeouts:n.manifest.timeouts}:{},params:o.params,rest:o.rest,output:o.output,commandPath:i},p,s),"run")}}}u(y,"adaptLeaf");function Z(n){return{contributions:n.map(e=>({contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,manifestName:e.manifestName,adaptLeaf:y(e,r=>[e.manifestName,...r.slice(1)]),...e.provider!==void 0?{provider:e.provider}:{}})),coreMounts:[],issues:[]}}u(Z,"adaptPluginContributions");function J(n){return n.map(e=>({parent:e.parent,plugin:e.plugin.name,contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,...e.provider!==void 0?{provider:e.provider}:{},adaptLeaf:y({...e,manifestName:e.node.name},r=>e.provider===void 0?[...r]:[e.provider.id,...r.slice(e.parent.length)])}))}u(J,"adaptPluginCoreMounts");function b(n){return n.contributions.map(e=>f(e,e.node,[e.node.name]))}u(b,"composeExternalSubtrees");function Q(n,e){return n.filter(r=>r.parent.join("\0")===e.join("\0")).map(r=>r.node.kind==="branch"?f(r,r.node,[...e,r.node.name],r.provider===void 0?1:e.length):E(r,r.node,[...e,r.node.name]))}u(Q,"composeCoreBranchMounts");function f(n,e,r,a=1){const t=e.rootEntry===void 0?void 0:{concept:e.rootEntry.concept,desc:e.rootEntry.description,useWhen:e.rootEntry.whenToUse},i=h({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...t!==void 0?{rootEntry:t}:{},help:{name:r.join(" "),summary:e.summary,...e.model!==void 0?{model:e.model}:{},...r.length===1&&n.extensionIssue!==void 0?{extensionIssue:n.extensionIssue}:{}},...e.passthrough!==void 0?{passthrough:e.passthrough}:{},children:e.children.map(o=>o.kind==="branch"?f(n,o,[...r,o.name],a):E(n,o,[...r,o.name]))});return n.provider!==void 0&&(i.effectiveHelp=_(i,n.provider,r.slice(a))),i}u(f,"buildBranch");function E(n,e,r){const a=n.adaptLeaf(e,r);return T({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...a.machineSelector===!0?{machineSelector:!0}:{},help:{name:r.join(" "),summary:e.summary,...a.machineSelector===!0?{guide:$}:{},params:e.params,output:e.output,outputKind:a.outputKind,effects:[...e.effects]},run:a.run})}u(E,"buildLeaf");export{Z as adaptPluginContributions,J as adaptPluginCoreMounts,Q as composeCoreBranchMounts,b as composeExternalSubtrees,F as composeSandboxProviderSubtrees};
|
|
@@ -40,6 +40,11 @@ export interface RequestPlan {
|
|
|
40
40
|
body?: string;
|
|
41
41
|
}
|
|
42
42
|
export declare function buildRequestPlan(spec: HttpInvokerSpec, input: Record<string, unknown>, context: LeafRunContext | undefined, options: RequestPlanOptions): RequestPlan;
|
|
43
|
+
/** Read the local file a `type:"path"` param names (relative paths resolve
|
|
44
|
+
* against the caller's cwd) and return its content per the param's `encoding`.
|
|
45
|
+
* Shared by the HTTP-plugin plan and the provider-leaf adapter, so every
|
|
46
|
+
* transport ships file content and none ships the path string. */
|
|
47
|
+
export declare function readPathParam(commandLabel: string, param: InputParam, value: unknown): unknown;
|
|
43
48
|
/** Map a unary 2xx body to the validated result object, or throw
|
|
44
49
|
* cli_protocol_error (GENERAL) for bad JSON / a non-object / an output mismatch. */
|
|
45
50
|
export declare function mapUnary2xx(spec: HttpInvokerSpec, body: string): Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var N=Object.defineProperty;var f=(e,t)=>N(e,"name",{value:t,configurable:!0});import{readFileSync as k,statSync as R}from"node:fs";import{resolve as j}from"node:path";import{validateDeclaredResult as C}from"../../../api/command-manifest/result.js";import{CrtrError as g}from"../../errors.js";import{envelopeClaimsStale as z,staleErrorDetails as U}from"../../manifest-stale.js";import{ExitCode as
|
|
1
|
+
var N=Object.defineProperty;var f=(e,t)=>N(e,"name",{value:t,configurable:!0});import{readFileSync as k,statSync as R}from"node:fs";import{resolve as j}from"node:path";import{validateDeclaredResult as C}from"../../../api/command-manifest/result.js";import{CrtrError as g}from"../../errors.js";import{envelopeClaimsStale as z,staleErrorDetails as U}from"../../manifest-stale.js";import{ExitCode as h}from"../../../types.js";import{isRecord as x}from"../../../shared/predicates.js";const P=/^[a-z][a-z0-9]*(_[a-z0-9]+)*$/,q=new Set(["internal","unknown_path","command_collision","cli_protocol_error"]),T=new Set(["authorization","crtr-request-id","crtr-run-id","crtr-approval","crtr-upload-id","crtr-upload-link","accept","content-type"]);function Y(e,t,n,r){const i=new Map;for(const o of e.params)i.set(o.name,o);const c=Object.entries(e.rest.params),u=e.baseUrl??new URL(e.registration.endpoint).origin;let y=e.rest.path;for(const[o,a]of c){if(a.in!=="path")continue;const s=t[w(o)];y=y.split(`{${o}}`).join(M(s))}const b=new URL(y,u);for(const[o,a]of c){if(a.in!=="query")continue;const s=w(o);$(i.get(o),s,t,n)&&b.searchParams.append(a.as??o,String(t[s]))}const m=G(e,c,i,t,n,r.fileMode),p=Object.keys(m).length>0?JSON.stringify(m):void 0,l={},d=f((o,a)=>{const s=Object.keys(l).find(S=>S.toLowerCase()===o.toLowerCase());s!==void 0&&delete l[s],l[o]=a},"setHeader");d("Accept","application/json"),d("Crtr-Request-Id",r.requestId),r.authorization!==null&&d("Authorization",r.authorization);for(const[o,a]of c){if(a.in!=="header")continue;const s=w(o);if(!$(i.get(o),s,t,n))continue;const S=a.as;if(r.fileMode==="pass-through"&&T.has(S.toLowerCase()))throw new g("invalid_type",`${v(e)}: header "${S}" is reserved for the runtime`,h.USAGE,{field:o,next:"Remove the runtime-owned header mapping from the manifest."});const _=String(t[s]);if(/[\r\n]/.test(_))throw new g("invalid_type",`${v(e)}: header "${S}" value must not contain a newline`,h.USAGE,{field:o,received:"(newline in header value)",next:"Remove newline characters from the value."});d(S,_)}p!==void 0&&d("Content-Type","application/json");for(const[o,a]of Object.entries(r.extraHeaders)){if(r.fileMode==="pass-through"&&/[\r\n]/.test(a))throw new g("invalid_type",`${v(e)}: header "${o}" value must not contain a newline`,h.USAGE);d(o,a)}if(r.fileMode==="pass-through"){if(d("Accept","application/json"),p!==void 0)d("Content-Type","application/json");else{const o=Object.keys(l).find(a=>a.toLowerCase()==="content-type");o!==void 0&&delete l[o]}if(d("Crtr-Request-Id",r.requestId),r.authorization!==null)d("Authorization",r.authorization);else{const o=Object.keys(l).find(a=>a.toLowerCase()==="authorization");o!==void 0&&delete l[o]}}return{url:b,method:e.rest.method,headers:l,...p!==void 0?{body:p}:{}}}f(Y,"buildRequestPlan");function G(e,t,n,r,i,c){const u=new Map;for(const[m,p]of t){if(p.in!=="body")continue;const l=p.as??m,d=u.get(l);d!==void 0?d.push(m):u.set(l,[m])}const y={};for(const[m,p]of u){const l=p.map(s=>({name:s,param:n.get(s)})),d=l.find(s=>s.param.kind!=="stdin"),o=l.find(s=>s.param.kind==="stdin");let a;d!==void 0&&$(d.param,w(d.name),r,i)?a={param:d.param,value:r[w(d.name)]}:o!==void 0&&$(o.param,w(o.name),r,i)&&(a={param:o.param,value:r[w(o.name)]}),a!==void 0&&(y[m]=I(e,a.param,a.value,c))}const b={...e.rest.body};return e.rest.bodyRoot!==void 0?b[e.rest.bodyRoot]=y:Object.assign(b,y),b}f(G,"assembleBody");function I(e,t,n,r){return r==="pass-through"||t.kind==="context-file"?n:(t.kind==="positional"||t.kind==="flag")&&t.type==="path"?A(v(e),t,n):n}f(I,"materializeBodyValue");function A(e,t,n){if(Array.isArray(n))return n.map(u=>A(e,t,u));const r=String(n),i=j(process.cwd(),r);let c;try{c=R(i)}catch{throw O(e,t.name,r,"no file exists at that path","an existing local file path")}if(!c.isFile())throw O(e,t.name,r,"the path is not a regular file","a regular local file (not a directory, socket, or device)");try{return"encoding"in t&&t.encoding==="base64"?k(i).toString("base64"):k(i,"utf8")}catch{throw O(e,t.name,r,"the file could not be read","a readable local file path")}}f(A,"readPathParam");function O(e,t,n,r,i){return new g("invalid_type",`${e}: cannot read file for "${t}" \u2014 ${r}: ${n}`,h.USAGE,{field:t,received:n,next:`Pass ${i}, relative to the current directory or absolute.`})}f(O,"fileError");function $(e,t,n,r){return r!==void 0?r.providedParams.has(t):Object.prototype.hasOwnProperty.call(n,t)&&n[t]!==void 0}f($,"isPresent");function M(e){const t=String(e);return t==="."||t===".."?t.replace(/\./g,"%2E"):encodeURIComponent(t)}f(M,"encodePathSegmentValue");function w(e){return e.replace(/-([a-z])/g,(t,n)=>n.toUpperCase())}f(w,"paramKey");function Z(e,t){let n;try{n=JSON.parse(t)}catch{throw E(e,"response body was not valid JSON","a JSON object matching the declared output")}if(!x(n))throw E(e,`response body was not a JSON object (got ${H(n)})`,"a JSON object matching the declared output");const r=C(e.output,n);if(r!==null){const c=e.output.find(u=>u.name===r.field)?.type??"unknown";throw r.receivedType!==void 0?E(e,`output field "${r.field}" has the wrong type`,`${r.field}: ${c}`):E(e,`response is missing required output field "${r.field}"`,`a "${r.field}" field of type ${c}`)}return n}f(Z,"mapUnary2xx");function ee(e,t,n){const r=J(t),i=D(n);if(i!==null){const c=P.test(i.code)&&!q.has(i.code);throw new g(c?i.code:"cli_protocol_error",i.message,r,{...i.received!==void 0?{received:i.received}:{},...i.field!==void 0?{field:i.field}:{},...i.manifestStale?U(e.registration.name):{},http_status:t,next:i.next??"Inspect the backend response, then retry if appropriate."})}throw new g("cli_http_error",`${v(e)}: request failed with HTTP ${t}`,r,{http_status:t,next:"Inspect the backend response, then retry if appropriate."})}f(ee,"throwNon2xx");function D(e){let t;try{t=JSON.parse(e)}catch{return null}if(!x(t)||!x(t.error))return null;const n=t.error;return typeof n.code!="string"||typeof n.message!="string"?null:{code:n.code,message:n.message,...typeof n.field=="string"?{field:n.field}:{},...n.received!==void 0?{received:n.received}:{},...typeof n.next=="string"?{next:n.next}:{},manifestStale:z(n),...n.question!==void 0?{question:n.question}:{}}}f(D,"parseHttpError");function J(e){return e===400||e===401||e===403||e===422?h.USAGE:e===404?h.NOT_FOUND:e===409?h.AMBIGUOUS:h.GENERAL}f(J,"deriveExit");function E(e,t,n){return new g("cli_protocol_error",`${v(e)}: ${t}. Expected ${n}.`,h.GENERAL,{received:t,next:`Run \`crtr pkg plugin update ${e.registration.name}\` or inspect the backend response.`})}f(E,"protocolError");function v(e){return`HTTP-transport plugin "${e.registration.name}" command \`${e.commandPath.join(" ")}\``}f(v,"label");function H(e){return e===null?"null":Array.isArray(e)?"array":typeof e}f(H,"typeName");export{Y as buildRequestPlan,Z as mapUnary2xx,D as parseHttpError,A as readPathParam,ee as throwNon2xx};
|
|
@@ -20,7 +20,7 @@ export interface GrantRecord {
|
|
|
20
20
|
revoked_at: string | null;
|
|
21
21
|
approved_at: string | null;
|
|
22
22
|
payer: 'user' | 'app' | null;
|
|
23
|
-
/** The directory's setup entry (`{ state, message?, next }`) per
|
|
23
|
+
/** The directory's setup entry (`{ state, message?, next, step? }`) per setup id the grantee requires. Absent (an older directory) reads as `{}`. */
|
|
24
24
|
dependencies?: GrantDependencies;
|
|
25
25
|
/** The grantee's registered run-notice endpoint (https), or null: runs of this grant send no notice. */
|
|
26
26
|
run_notice_url?: string | null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var y=Object.defineProperty;var t=(e,r)=>y(e,"name",{value:r,configurable:!0});import{createHash as _}from"node:crypto";import{prepared as
|
|
1
|
+
var y=Object.defineProperty;var t=(e,r)=>y(e,"name",{value:r,configurable:!0});import{createHash as _}from"node:crypto";import{prepared as s}from"../sqlite-statements.js";import{parseScope as S}from"@north-light/identity";import{openDb as i,withCanvasWrite as v}from"../canvas/db.js";import{isUnsandboxedMachine as T}from"../runtime/unsandboxed.js";const m=["not_started","pending","ready","failed","unavailable"];function c(e){if(!Array.isArray(e))throw new TypeError("grant scopes must be a list");return e.map(r=>{if(!r||typeof r!="object"||typeof r.scope!="string"||!["always","ask"].includes(r.mode))throw new TypeError("invalid grant scope entry");return{scope:r.scope,mode:r.mode}})}t(c,"entries");function O(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{state:r,message:a,next:u,step:n}=e;return typeof r!="string"||!m.includes(r)||a!==void 0&&typeof a!="string"||n!==void 0&&typeof n!="string"||typeof u!="string"||!u?null:{state:r,...a===void 0?{}:{message:a},next:u,...n===void 0?{}:{step:n}}}t(O,"dependencyOf");function E(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const r={};for(const[a,u]of Object.entries(e)){const n=O(u);n&&(r[a]=n)}return r}t(E,"dependenciesOf");function l(e){return{id:e.id,runtime:e.runtime,sub:e.sub,grantee:e.grantee,rev:e.rev,scopes:c(JSON.parse(e.scopes_json)),resolved_scopes:c(JSON.parse(e.resolved_scopes_json)),spend_cap:e.spend_cap,expires_at:e.expires_at,status:e.status,revoked_at:e.revoked_at,approved_at:e.approved_at,payer:e.payer,dependencies:E(JSON.parse(e.dependencies_json)),run_notice_url:e.run_notice_url??null,synced_at:e.synced_at}}t(l,"record");function p(e){if(!e||e.status!=="active")return null;try{return x(l(e))}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return null;throw r}}t(p,"active");function d(e){return e.grantee!=="app:terminal"&&T()}t(d,"mirrorOnly");function M(){return R().filter(e=>!d(e))}t(M,"localGrants");function R(){return s(i(),"SELECT * FROM grants").all().flatMap(e=>{try{return[l(e)]}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return[];throw r}})}t(R,"allGrants");function x(e){if(e.status!=="active")return null;try{const r=e.resolved_scopes.map(({scope:a})=>S(a));return{...e,resolved:e.resolved_scopes.flatMap(({mode:a},u)=>a==="always"?[r[u]]:[])}}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return null;throw r}}t(x,"activeRecord");function f(e){return Array.isArray(e)?`[${e.map(f).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().filter(r=>e[r]!==void 0).map(r=>`${JSON.stringify(r)}:${f(e[r])}`).join(",")}}`:JSON.stringify(e)??"null"}t(f,"canonicalJson");function b(e){const r={rev:e.rev,resolved_scopes:e.resolved_scopes,spend_cap:e.spend_cap,payer:e.payer,dependencies:e.dependencies??{}};return _("sha256").update(f(r)).digest("base64url").slice(0,22)}t(b,"grantVersion");function k(e){return!!s(i(),"SELECT 1 FROM grants WHERE id = ?").get(e)}t(k,"hasGrant");function H(e){return s(i(),"SELECT rev, status FROM grants WHERE id = ?").get(e)??null}t(H,"grantSyncState");function W(e,r){return s(i(),"SELECT rev, status FROM grants WHERE grantee = ? AND sub = ?").get(e,r)??null}t(W,"grantSyncStateFor");function j(e){const r=s(i(),"SELECT * FROM grants WHERE id = ?").get(e);if(!r)return null;try{return l(r)}catch(a){if(a instanceof TypeError||a instanceof SyntaxError)return null;throw a}}t(j,"grantRecord");function D(e){return p(s(i(),"SELECT * FROM grants WHERE id = ?").get(e))}t(D,"activeGrant");function G(e,r){return p(s(i(),"SELECT * FROM grants WHERE grantee = ? AND sub = ?").get(e,r))}t(G,"grantFor");function I(e){if(d({grantee:e}))return null;const r=s(i(),"SELECT * FROM grants WHERE grantee = ? AND status = 'active'").all(e);if(r.length>1)throw new Error(`multiple active grants for ${e}`);return p(r[0])}t(I,"activeGrantForApp");function J(e){const r=s(i(),"SELECT run_notice_url AS url, sub, grantee FROM grants WHERE id = ? AND status = 'active'").get(e);return r&&r.url&&!d(r)?{url:r.url,sub:r.sub,grantee:r.grantee}:null}t(J,"runNoticeTarget");function U(){const e=s(i(),"SELECT * FROM grants WHERE grantee = 'app:terminal' AND status = 'active'").all();if(e.length>1)throw new Error("multiple active terminal grants");return p(e[0])}t(U,"terminalGrant");function w(){return s(i(),"SELECT value FROM runtime_state WHERE key = 'grants_source'").get()?.value??null}t(w,"grantsSource");function $(e){return s(i(),"SELECT value FROM runtime_state WHERE key = 'grants_source'").get()?.value===e}t($,"hasSyncedGrants");function V(e){return s(e,"SELECT value FROM runtime_state WHERE key = 'grants_source'").get()?.value!=="local"?0:(s(e,"DELETE FROM runtime_state WHERE key = 'grants_source'").run(),Number(s(e,"DELETE FROM grants").run().changes))}t(V,"dropLocalGrantMirror");function P(e,r,a){const u=new Set;for(const n of e){if(u.has(n.id)||!n.id||!n.runtime||!n.sub||!n.grantee||!Number.isInteger(n.rev)||!["active","revoked","removed"].includes(n.status))throw new TypeError("invalid grant sync response");u.add(n.id),c(n.scopes),c(n.resolved_scopes)}v(n=>{n.exec("DELETE FROM grants");const g=s(n,`INSERT INTO grants
|
|
2
2
|
(id, runtime, sub, grantee, rev, scopes_json, resolved_scopes_json, spend_cap, expires_at,
|
|
3
3
|
status, revoked_at, approved_at, payer, dependencies_json, run_notice_url, synced_at)
|
|
4
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);for(const o of e)g.run(o.id,o.runtime,o.sub,o.grantee,o.rev,JSON.stringify(o.scopes),JSON.stringify(o.resolved_scopes),o.spend_cap,o.expires_at,o.status,o.revoked_at,o.approved_at,o.payer,JSON.stringify(
|
|
4
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);for(const o of e)g.run(o.id,o.runtime,o.sub,o.grantee,o.rev,JSON.stringify(o.scopes),JSON.stringify(o.resolved_scopes),o.spend_cap,o.expires_at,o.status,o.revoked_at,o.approved_at,o.payer,JSON.stringify(E(o.dependencies)),o.run_notice_url??null,o.synced_at);s(n,"INSERT INTO runtime_state (key, value) VALUES ('grants_source', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(a),s(n,"INSERT INTO runtime_state (key, value) VALUES ('grants_generation', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(value AS INTEGER) + 1 AS TEXT)").run()})}t(P,"replaceGrants");function X(){const e=s(i(),"SELECT value FROM runtime_state WHERE key = 'grants_generation'").get();return e?Number(e.value):0}t(X,"grantsGeneration");export{D as activeGrant,I as activeGrantForApp,x as activeRecord,R as allGrants,E as dependenciesOf,V as dropLocalGrantMirror,G as grantFor,j as grantRecord,H as grantSyncState,W as grantSyncStateFor,b as grantVersion,X as grantsGeneration,w as grantsSource,k as hasGrant,$ as hasSyncedGrants,M as localGrants,d as mirrorOnly,P as replaceGrants,J as runNoticeTarget,U as terminalGrant};
|
|
@@ -34,6 +34,10 @@ export declare const IDEMPOTENCY_WINDOW_MS = 86400000;
|
|
|
34
34
|
export declare function idempotencyKeyOf(header: unknown): string | undefined;
|
|
35
35
|
/** Delete keys past their window; returns how many were removed. */
|
|
36
36
|
export declare function pruneExpiredIdempotencyKeys(now?: number): number;
|
|
37
|
+
/** Longest `run.start` deadline an app may set: 24 hours. The grammar is `crtr node new --deadline`'s (parts of `<int>[smhd]`, summed). */
|
|
38
|
+
export declare const RUN_DEADLINE_MAX_MS: number;
|
|
39
|
+
/** The milliseconds of a `run.start` `deadline` duration; refuses a malformed, zero, or over-24h value as `invalid_request` on `deadline`. */
|
|
40
|
+
export declare function parseRunDeadlineMs(raw: string): number;
|
|
37
41
|
export declare function startRun(principal: AppPrincipal, input: {
|
|
38
42
|
prompt: string;
|
|
39
43
|
scopes: string[] | null;
|
|
@@ -42,6 +46,7 @@ export declare function startRun(principal: AppPrincipal, input: {
|
|
|
42
46
|
isolated?: boolean;
|
|
43
47
|
cwd?: string;
|
|
44
48
|
model?: string;
|
|
49
|
+
deadline?: string;
|
|
45
50
|
output_schema?: Record<string, unknown>;
|
|
46
51
|
message?: string;
|
|
47
52
|
}, idempotencyKey?: string, resolveAttachments?: AttachmentResolver): Promise<Record<string, unknown>>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
var
|
|
1
|
+
var Ne=Object.defineProperty;var m=(e,t)=>Ne(e,"name",{value:t,configurable:!0});import{prepared as w}from"../sqlite-statements.js";import{coveredByAny as ve}from"../../shared/env.js";import{createHash as J}from"node:crypto";import{ApiError as be}from"../../api/errors.js";import{CrtrError as Oe}from"../errors.js";import{isAbsolute as Y,relative as De,resolve as Me,sep as Ie,join as Te}from"node:path";import{SessionManager as Ae}from"@earendil-works/pi-coding-agent";import{covers as W}from"@north-light/identity";import{openDb as G,withCanvasWrite as L}from"../canvas/db.js";import{deleteNodeWithOwned as Le}from"../graph/deletion.js";import{getById as Ce}from"../graph/objects.js";import{repairRunConversations as X}from"../canvas/sessions.js";import{getNode as N,nodeFiles as xe,removeNodeFiles as ke,updateNode as We}from"../canvas/canvas.js";import{fullName as Fe}from"../canvas/labels.js";import{renameWindow as He}from"../runtime/placement.js";import{liveExecutionForNode as z}from"../canvas/executions.js";import{conversationStore as Q,isConversationNotFound as qe}from"../conversation-store/index.js";import{enqueueNodeMail as Ue}from"../feed/messages.js";import{newestReportBody as Pe}from"../feed/reports.js";import{ensureAppProfile as Be,loadExactProfileManifest as $e}from"../profiles/manifest.js";import{stopNodeSandboxes as V}from"../spaces/stop.js";import{BrokerUnreachableError as je}from"../runtime/broker-request.js";import{interruptLive as Je}from"../runtime/interactive-deliver.js";import{isBrokerLive as Z}from"../runtime/model-swap.js";import{newNodeId as Ye}from"../runtime/nodes.js";import{writeGoal as Ge}from"../runtime/kickoff.js";import{reviveNode as F}from"../runtime/revive.js";import{spawnChildPrepared as Xe}from"../runtime/spawn.js";import{isPortableModelToken as ze}from"../runtime/launch.js";import{parseDurationMs as Qe}from"../wake.js";import{expandModelCandidates as Ve,parseModelSpec as Ze}from"../model-routes.js";import{isolatedBrokerLauncherAvailable as Ke}from"../runtime/host.js";import{resolveRunKind as et}from"../runtime/launch-target.js";import{cycleAwareMessages as tt}from"../runtime/session-cycles.js";import{originFor as K}from"../runtime/stamp/protocol.js";import{readOutputRequest as nt,readOutputResult as ee}from"../runtime/structured-output.js";import{admitLegacyStructuredOutput as te}from"../../daemon/structured-requests/sidecar.js";import{runSchemaRepromptPending as st}from"../runtime/stop-guard.js";import{toRunOutcomeDTO as rt}from"../../daemon/api/map.js";import{openAssistantMessageStartSequence as ot}from"../../daemon/runs/observer.js";import{closeNode as ne}from"../runtime/close.js";import{parseScope as it}from"@north-light/identity";import{appendRunEvent as se,subscribeRunEvents as dt}from"./events.js";import{runActivity as at}from"./activity.js";import{cancelOpenQuestions as re,openQuestionIds as oe}from"./questions.js";import{appSpace as H}from"../layout.js";import{ensureAppSpace as ct}from"../spaces/space.js";import{assertUploadsUsable as ie,resolveStorageUrls as ut}from"../storage/attachments.js";import{storageClient as mt,storageConfig as lt}from"../storage/client.js";import{deleteUploadRow as ft,insertUploadUses as de,releaseRunUploads as _t}from"../storage/tables.js";import{abortProviderCalls as ae}from"../providers/inflight.js";const ce=m((e,t,r)=>ut(t,e,r),"defaultResolver"),pt=3e3;function wn(e){return Number(w(e,"UPDATE idempotency_keys SET grantee = 'app:terminal' WHERE grantee = 'user:local'").run().changes)}m(wn,"rewriteLocalIdempotencyGrantee");class S extends Error{static{m(this,"RunError")}status;code;param;details;scope;retryable;constructor(t,r,s,i,c,d,o=t===503){super(s),this.status=t,this.code=r,this.param=i,this.details=c,this.scope=d,this.retryable=o}}const R=m((e,t)=>new S(400,"invalid_request",e,t),"invalid"),q=m(()=>new S(404,"not_found","run not found"),"missing"),U=m(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"usage"),O=m(()=>G(),"db");function C(e,t,r=!1){const s=N(e);if(!s||s.parent!==null||s.run_id!==e||s.grantee!==t||!r&&s.deleting_at)throw q();return s}m(C,"ownedRun");const P=["waiting_on_user","idle","settled"];function I(e){if(e.status==="canceled"||e.status==="dead"||e.status==="done"&&e.intent!=="parked")return"settled";const t=w(O(),`SELECT 1 FROM node_rows n WHERE n.run_id = ? AND n.status = 'active' AND
|
|
2
2
|
(n.last_settled_at IS NULL OR EXISTS (
|
|
3
3
|
SELECT 1 FROM run_events e WHERE e.run_id = ? AND e.node_id = n.node_id AND e.type = 'node.turn.started'
|
|
4
4
|
AND e.sequence_number > COALESCE((SELECT MAX(c.sequence_number) FROM run_events c
|
|
5
5
|
WHERE c.run_id = e.run_id AND c.node_id = e.node_id AND c.type = 'node.turn.completed'), 0)
|
|
6
|
-
)) LIMIT 1`).get(e.node_id,e.node_id),r=w(
|
|
6
|
+
)) LIMIT 1`).get(e.node_id,e.node_id),r=w(O(),"SELECT 1 FROM deliveries WHERE destination_id IN (SELECT node_id FROM nodes WHERE run_id = ?) AND state IN ('armed', 'pending') AND wake = 1 LIMIT 1").get(e.node_id);return t||r||!e.last_settled_at||st(e)?"running":oe(e.node_id).length?"waiting_on_user":"idle"}m(I,"runStatus");function hn(e){const t=N(e);return t!==null&&I(t)==="running"}m(hn,"runIsRunning");function A(e){X(e.node_id),e=N(e.node_id)??e;const t=I(e),r=t==="settled"?[]:oe(e.node_id),s=Pe(e.node_id)||void 0,i=t==="settled"?rt(e):null,c=i?.kind==="result"?ee(e.node_id,e.grantee):void 0;return{run_id:e.node_id,name:e.name,status:t,...i?{outcome:i}:{},...c===void 0?{}:{result:c},...s!==void 0?{last_report:s}:{},pending_question_ids:r,...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(U())),payer:"user",scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:at(e.node_id,t)}}m(A,"runObject");const B=864e5,ue=255;function yn(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>ue)throw R(`Idempotency-Key must be 1-${ue} characters`);return e}}m(yn,"idempotencyKeyOf");function x(e,t,r,s=G()){const i=w(s,"SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!i||Date.now()-Date.parse(i.created_at)>=B)){if(i.body_hash!==r)throw new S(409,"idempotency_conflict","key used with a different request");return{run_id:i.run_id,response:JSON.parse(i.response_json)}}}m(x,"findIdempotent");function me(e,t,r,s,i,c,d){w(e,"DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,r,new Date(Date.now()-B).toISOString()),w(e,"INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,r,s,i,JSON.stringify(c),d)}m(me,"recordIdempotent");function Rn(e=Date.now()){return L(t=>Number(w(t,"DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-B).toISOString()).changes))}m(Rn,"pruneExpiredIdempotencyKeys");function le(e,t,r,s,i){const c=s.text;let d=0;return{messageId:Ue(t,{kind:"message",label:c.slice(0,120),senderNodeId:"human",senderName:r,bodyJson:i?{body:c}:{body:c,start_turn:!1},urgency:i?"normal":"deferred",wake:i?1:0,withinEnqueue:m((a,_)=>{s.uses.length&&(ie(r,s.uses,a),de(s.uses.map(({upload_id:y,path:E})=>({upload_id:y,path:E,run_id:t})),a)),d=se(a,t,t,"node.message.accepted",{message_id:_,start_turn:i}).sequence_number},"withinEnqueue")}).messageId,sequence:d}}m(le,"acceptRunMessage");class fe extends Error{static{m(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}const gt=1440*60*1e3;function _e(e){const t=typeof e=="string"?Qe(e):null;if(t===null||!Number.isSafeInteger(t))throw R(`deadline '${String(e)}' is not a duration; use one or more <int>[smhd] parts such as 10m or 1h30m`,"deadline");if(t<=0)throw R("deadline must be greater than zero","deadline");if(t>gt)throw R("deadline must be at most 24h","deadline");return t}m(_e,"parseRunDeadlineMs");async function Sn(e,t,r,s=ce){if(t.isolated&&!Ke())throw new S(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");t.deadline!==void 0&&_e(t.deadline);const i=J("sha256").update(JSON.stringify(t)).digest("hex");if(r){const n=x(e.grantee,r,i);if(n){const u=C(n.run_id,e.grantee);return!u.pi_pid&&I(u)==="running"&&(t.output_schema!==void 0&&!u.outcome_at&&nt(u.node_id).state==="absent"&&ee(u.node_id,u.grantee)===void 0&&te(u.node_id,"terminal",t.output_schema),await F(u.node_id,{resume:!0,capacity:"freeze"})),n.response}}await ct(e.grantee);const c=t.profile===void 0?Be(e.grantee):$e(t.profile),d=c.manifest.grantee??"app:terminal";if(d!==e.grantee){const n=`crtr:memory:rw:${d}:profile:${c.profileId}`;if(!e.resolved.some(u=>W(u,n))||t.scopes!==null&&!t.scopes.some(u=>W(u,n)))throw new S(403,"scope_missing",`missing scope ${n}`,"profile",void 0,n)}if(t.model!==void 0){const n=t.model;if(ze(n)){if(n.includes("/")){const u=n.lastIndexOf("/"),h=n.slice(0,u),g=n.slice(u+1);if(["ultra","strong","medium","light"].includes(g)&&Ve({kind:"logical",family:h,strength:g,spec:n},H(e.grantee).files,c.profileId).every(T=>T.family!==h))throw R(`model '${n}' names a model family that is not configured in this daemon`,"model")}}else if(Ze(n)===void 0)throw R(`model '${n}' is not a strength (ultra, strong, medium, light), a family/strength, or a provider/model id`,"model")}const o=Ye(),a=Te(H(e.grantee).nodes,o);if(t.isolated&&t.cwd!==void 0){const n=De(a,Me(t.cwd));if(!Y(t.cwd)||n===".."||n.startsWith(`..${Ie}`)||Y(n))throw R("cwd must be inside the isolated root node folder","cwd")}const _=await s(o,e.grantee,t.prompt),y=_.text;let E;try{E=t.message===void 0?void 0:await s(o,e.grantee,t.message)}catch(n){throw _.cleanup(),n}let p;try{await Xe({kind:et(c),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??a:H(e.grantee).files,profile:c.profileId,prompt:y,nodeId:o,isolated:t.isolated??!1,...t.model===void 0?{}:{model:t.model},...t.deadline===void 0?{}:{deadlineAt:new Date(Date.now()+_e(t.deadline)).toISOString()},...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:o,event_cursor:1,usage_json:JSON.stringify(U())},withinCreate:m((n,u)=>{if(r){const g=x(e.grantee,r,i,n);if(g)throw new fe(g)}const h=_.uses;if(h.length&&(ie(e.grantee,h,n),de(h.map(({upload_id:g,path:T})=>({upload_id:g,path:T,run_id:o})),n)),Ge(o,y),p={run_id:o,node_id:o,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:"user",status:"running",created_at:u.created,metadata:t.metadata,sequence_number:0},se(n,o,o,"run.started",{scopes:t.scopes,payer:"user",usage:U()}),E){const g=le(n,o,e.grantee,E,!1);p.message={run_id:o,message_id:g.messageId,sequence_number:g.sequence}}r&&me(n,e.grantee,r,i,o,p,u.created)},"withinCreate")},t.output_schema===void 0?void 0:n=>{te(n,"terminal",t.output_schema)})}catch(n){if(N(o)||(_.cleanup(),E?.cleanup()),n instanceof fe)return n.winner.response;if(t.model!==void 0&&n instanceof Oe&&n.code==="usage")throw R(n.message,"model");if(n instanceof be&&(n.code==="isolated_run"||n.code==="invalid_request")){const u=n.details&&typeof n.details=="object"&&"param"in n.details&&typeof n.details.param=="string"?n.details.param:void 0;throw new S(n.status,n.code,n.message,u)}throw n}if(!p)throw new Error("run creation did not commit");return p}m(Sn,"startRun");function Nn(e,t){let r;if(t.after)try{const o=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!o||typeof o!="object"||!("activity"in o)||typeof o.activity!="string"||!("id"in o)||typeof o.id!="string")throw new Error("invalid cursor");r={activity:o.activity,id:o.id}}catch{throw R("invalid list cursor","after")}const i=O().prepare(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
|
|
7
7
|
WHERE run_id = node_id AND deleting_at IS NULL ${e===null?"":"AND grantee = ?"}
|
|
8
|
-
ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e]).filter(({node_id:o})=>!t.visible||t.visible(o)).filter(({activity:o,node_id:a})=>!r||o<r.activity||o===r.activity&&a<r.id).map(o=>({root:
|
|
8
|
+
ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e]).filter(({node_id:o})=>!t.visible||t.visible(o)).filter(({activity:o,node_id:a})=>!r||o<r.activity||o===r.activity&&a<r.id).map(o=>({root:N(o.node_id),activity:o.activity})).filter(({root:o})=>!t.status||I(o)===t.status),c=i.slice(0,t.limit),d=c.at(-1);return{runs:c.map(({root:o})=>A(o)),next:i.length>t.limit&&d?Buffer.from(JSON.stringify({activity:d.activity,id:d.root.node_id})).toString("base64url"):null}}m(Nn,"listRuns");async function vn(e,t,r){const s=C(e,t),i=m(()=>A(C(e,t)),"read");return!r||P.includes(I(s))?i():new Promise((c,d)=>{const o=m(()=>{clearTimeout(y),clearInterval(_),a();try{c(i())}catch(E){d(E)}},"finish"),a=dt(e,()=>{try{P.includes(String(i().status))&&o()}catch(E){clearTimeout(y),clearInterval(_),a(),d(E)}}),_=setInterval(()=>{P.includes(String(i().status))&&o()},250),y=setTimeout(o,r*1e3)})}m(vn,"waitRun");async function bn(e,t,r=ce,s=!0,i){const c=e.status==="canceled"||e.outcome_reason==="closed",d=e.grantee??"app:terminal",o=J("sha256").update(JSON.stringify(["run.message",e.node_id,t,s])).digest("hex"),a=m(async p=>{const n=N(e.node_id),u=w(O(),"SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(p.message_id));return n&&!c&&s&&!n.pi_pid&&u&&await F(e.node_id,{resume:!0,capacity:"freeze"}),p},"replay");if(i){const p=x(d,i,o);if(p)return a(p.response)}if(c)throw new S(409,"run_settled","canceled run cannot receive a message");const _=await r(e.node_id,d,t),y=s&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;let E;try{E=L(p=>{if(i){const h=x(d,i,o,p);if(h)return{replayed:h.response}}y?.(e.node_id);const n=le(p,e.node_id,d,_,s),u={run_id:e.node_id,message_id:n.messageId,sequence_number:n.sequence};return i&&me(p,d,i,o,e.node_id,u,new Date().toISOString()),{result:u}})}catch(p){throw _.cleanup(),p}return"replayed"in E?(_.cleanup(),a(E.replayed)):(s&&!e.pi_pid&&await F(e.node_id,{resume:!0,capacity:"freeze"}),E.result)}m(bn,"messageRun");async function Et(e){const t=m(()=>new S(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let r=0;r<2;r++){const s=N(e);if(!s)throw q();if(!Z(s)){if(s.streaming)throw t();return}const i=z(e)?.executionId;try{await Je(e)==="turn"&&ae({nodes:[e]});return}catch(c){if(!(c instanceof je))throw c;const d=N(e);if(!d)throw q();if(!d.streaming)return;if(r===0&&Z(d)&&z(e)?.executionId!==i)continue;throw t()}}throw t()}m(Et,"interruptNode");async function On(e,t="stop"){const r=t==="stop"?w(O(),"SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id).map(({node_id:s})=>s):[e.node_id];return await Promise.all(r.map(s=>Et(s))),A(N(e.node_id)??e)}m(On,"interruptRun");async function Dn(e,t={}){const r=w(O(),"SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id).map(d=>d.node_id);ae({run:e.node_id});const s=[];if(I(e)!=="settled")s.push(...ne(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"}).closed);else for(const d of r){if(d===e.node_id)continue;const o=N(d);o&&o.status!=="done"&&o.status!=="canceled"&&o.status!=="dead"&&s.push(...ne(d,{preserveEmpty:!0,stopReason:"run_canceled"}).closed)}if(await re({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...s])].map(d=>V(d,"run_canceled")))).some(d=>!d.confirmed))throw new S(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const c=N(e.node_id);return A(c??e)}m(Dn,"cancelRun");const pe=200;function Mn(e,t){if(typeof t!="string"||!t.trim())throw R("name is required","name");const r=t.trim();if(r.length>pe)throw R(`name exceeds ${pe} characters`,"name");const s=We(e.node_id,{name:r});return s.window&&He(s.window,Fe(s)),A(s)}m(Mn,"renameRun");async function In(e,t={config:lt,client:mt}){await re({run_id:e.node_id},"the run was deleted");const r=L(a=>(w(a,"UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),w(a,"SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id).map(_=>_.node_id)));if((await Promise.all(r.map(a=>V(a,"run_deleted",{deleting:!0})))).some(a=>!a.confirmed))throw new S(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const i=r.map(a=>{const _=xe(a);if(_.app===null)throw new Error(`run node missing during deletion: ${a}`);return _}),c=w(O(),"SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:a}of c)try{Q().delete(a)}catch(_){if(!qe(_))throw _}const d=L(a=>{for(const p of r){const n=Ce(a,p);n!==null&&n.deleted_at===null&&Le(a,n)}const _=_t([e.node_id],a);w(a,"DELETE FROM run_events WHERE run_id = ?").run(e.node_id),w(a,"DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const y=r.map(()=>"?").join(","),E=a.prepare(`SELECT message_id FROM messages WHERE sender_node_id IN (${y}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${y})`).all(...r,...r);a.prepare(`DELETE FROM deliveries WHERE destination_id IN (${y})`).run(...r);for(const{message_id:p}of E)w(a,"DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(p,p);return _});for(const a of i)ke(a);const{notifyRunDeleted:o}=await import("./events.js");return o(e.node_id),{uploads_pending:await wt(d,t)}}m(In,"deleteRun");async function wt(e,t){if(!e.length||!t.config())return e.length;let r;try{r=t.client()}catch{return e.length}const s=new Set,i=e.map(d=>r.deleteUpload(d).then(()=>{ft(d),s.add(d)},()=>{}));let c;return await Promise.race([Promise.all(i),new Promise(d=>{c=setTimeout(d,t.budgetMs??pt),c.unref?.()})]),clearTimeout(c),e.length-s.size}m(wt,"deleteReleasedUploads");function Tn(e,t,r){C(e,t),X(e);const s=O();s.exec("BEGIN");try{const c=w(s,"SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e).map(n=>({node_id:n.node_id,parent_node_id:n.parent,conversations:w(s,"SELECT DISTINCT session_id FROM sessions WHERE node_id = ? ORDER BY created_at").all(n.node_id).map(({session_id:u})=>{const{header:h,entries:g}=Q().read(u);if(r==="entries")return{conversation_id:u,header:h,entries:g};const T=Ae.inMemory(h.cwd,void 0,[h,...g]),ge=tt(T)??[],$=new Map(g.map((f,v)=>[f.id,v])),Ee=g.filter(f=>f.type==="custom"&&f.customType==="crtr-system-prompt").map(f=>({id:f.id,message:{role:"custom",customType:"crtr-system-prompt",data:"data"in f?f.data:void 0,timestamp:f.timestamp}})),j=g.filter(f=>f.type==="custom_message"&&f.customType==="app_message").map(f=>({id:f.id,message:{role:"custom",customType:"app_message",content:"content"in f?f.content:"",timestamp:f.timestamp}})),we=n.node_id===e?g.find(f=>{if(f.type!=="message"||f.message.role!=="user")return!1;const v=K(g,f.message.timestamp);return v?.type==="runtime"&&v.kind==="kickoff"})?.id:void 0,he=[...ge.filter(({id:f})=>!j.some(v=>v.id===f)),...Ee,...j].sort((f,v)=>($.get(f.id)??Number.MAX_SAFE_INTEGER)-($.get(v.id)??Number.MAX_SAFE_INTEGER)).map(({id:f,message:v})=>{const l=v,D=l.usage,k=l.role==="user"?K(g,Number(l.timestamp)):null,ye=k?.type==="human"||f===we,M=l.role==="toolResult"?"tool_result":l.role==="bashExecution"?"bash_execution":l.role==="compactionSummary"?"compaction_summary":l.role==="branchSummary"?"branch_summary":l.role==="custom"&&l.customType==="crtr-system-prompt"?"system":l.role==="user"?ye?"user":"custom":l.role,Re=M==="custom"?l.role==="custom"?l.customType:k?.type==="runtime"?k.kind:"unknown":void 0,Se=M==="system"&&l.customType==="crtr-system-prompt"?[{type:"text",text:l.data?.text??""}]:typeof l.content=="string"?[{type:"text",text:l.content}]:Array.isArray(l.content)?l.content.map(b=>b.type==="toolCall"?{type:"tool_call",tool_call_id:b.id,name:b.name,arguments:b.arguments}:b.type==="image"?{type:"image",mime_type:b.mimeType}:b.type==="thinking"?{type:"thinking",text:b.thinking}:b):[];return{entry_id:f,role:M,...M==="custom"?{custom_type:Re}:{},time:typeof l.timestamp=="string"?l.timestamp:new Date(Number(l.timestamp??Date.now())).toISOString(),content:Se,...M==="assistant"?{model:l.model,provider:l.provider,stop_reason:l.stopReason,...l.errorMessage?{error_message:l.errorMessage}:{},...D?{usage:{input_tokens:D.input??0,output_tokens:D.output??0,cache_read_tokens:D.cacheRead??0,cache_write_tokens:D.cacheWrite??0,cost_usd:D.cost?.total??0}}:{}}:{},...M==="tool_result"?{tool_call_id:l.toolCallId,tool_name:l.toolName,is_error:l.isError}:{}}})??[];return{conversation_id:u,messages:he}})})),o=w(s,"SELECT event_cursor FROM nodes WHERE node_id = ?").get(e).event_cursor-1,a=new Map(c.map(n=>[n.node_id,ot(n.node_id)])),_=w(s,`SELECT node_id, sequence_number FROM run_events
|
|
9
9
|
WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.done', 'node.output_text.done', 'usage')
|
|
10
|
-
ORDER BY sequence_number`).all(e,o);for(const n of
|
|
10
|
+
ORDER BY sequence_number`).all(e,o);for(const n of _){const u=a.get(n.node_id);u!=null&&n.sequence_number>=u&&a.set(n.node_id,null)}const y=w(s,`SELECT node_id, sequence_number, type, body_json FROM run_events
|
|
11
11
|
WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.delta', 'node.output_text.delta')
|
|
12
|
-
ORDER BY sequence_number`).all(e,o),E=new Map;for(const n of y){const u=a.get(n.node_id);if(u==null||n.sequence_number<u)continue;const h=JSON.parse(n.body_json),g=E.get(n.node_id)??{thinking:"",text:""};n.type==="node.thinking.delta"?g.thinking+=h.delta:g.text+=h.delta,E.set(n.node_id,g)}const
|
|
12
|
+
ORDER BY sequence_number`).all(e,o),E=new Map;for(const n of y){const u=a.get(n.node_id);if(u==null||n.sequence_number<u)continue;const h=JSON.parse(n.body_json),g=E.get(n.node_id)??{thinking:"",text:""};n.type==="node.thinking.delta"?g.thinking+=h.delta:g.text+=h.delta,E.set(n.node_id,g)}const p=c.map(n=>({...n,in_progress_message:a.get(n.node_id)==null?null:E.get(n.node_id)??{thinking:"",text:""}}));return s.exec("COMMIT"),{run_id:e,latest_sequence:o,nodes:p}}catch(i){throw s.exec("ROLLBACK"),i}}m(Tn,"traceRun");function An(e,t,r){if(t===void 0){if(!e.some(s=>W(s,"crtr:llm")))throw new S(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return null}if(!Array.isArray(t)||t.some(s=>typeof s!="string"))throw R("scopes must be a list of scope strings","scopes");for(const s of t){try{it(s)}catch{throw R(`invalid scope ${s}`,"scopes")}if(/^crtr:files:(?:read|rw):app:/.test(s)&&s.split(":")[4]===r.slice(4))throw R(`own-app files cannot narrow a run: ${s}`,"scopes");if(!(/^crtr:memory:(?:read|rw):app:/.test(s)&&s.split(":")[4]===r.slice(4))&&!ve(e,s))throw new S(403,"scope_missing",`missing scope ${s}`,void 0,void 0,s)}if(!t.includes("crtr:llm"))throw new S(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return t}m(An,"requestedScopes");export{B as IDEMPOTENCY_WINDOW_MS,gt as RUN_DEADLINE_MAX_MS,pt as RUN_DELETE_STORAGE_BUDGET_MS,pe as RUN_NAME_MAX,S as RunError,Dn as cancelRun,In as deleteRun,yn as idempotencyKeyOf,On as interruptRun,Nn as listRuns,bn as messageRun,C as ownedRun,_e as parseRunDeadlineMs,Rn as pruneExpiredIdempotencyKeys,Mn as renameRun,An as requestedScopes,wn as rewriteLocalIdempotencyGrantee,hn as runIsRunning,A as runObject,Sn as startRun,Tn as traceRun,vn as waitRun};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var R=Object.defineProperty;var m=(a,t)=>R(a,"name",{value:t,configurable:!0});import{callerGrant as h}from"../principal.js";import{grantVersion as g}from"../../../core/grants/mirror.js";import{waitGrantChange as S}from"../../../core/grants/watch.js";import{RunError as n,idempotencyKeyOf as b,cancelRun as $,deleteRun as N,interruptRun as O,listRuns as A,messageRun as j,renameRun as x,requestedScopes as B,startRun as J,traceRun as z,waitRun as C}from"../../../core/runs/operations.js";import{canReadRun as v,requireRun as
|
|
1
|
+
var R=Object.defineProperty;var m=(a,t)=>R(a,"name",{value:t,configurable:!0});import{callerGrant as h}from"../principal.js";import{grantVersion as g}from"../../../core/grants/mirror.js";import{waitGrantChange as S}from"../../../core/grants/watch.js";import{RunError as n,idempotencyKeyOf as b,cancelRun as $,deleteRun as N,interruptRun as O,listRuns as A,messageRun as j,renameRun as x,requestedScopes as B,startRun as J,traceRun as z,waitRun as C}from"../../../core/runs/operations.js";import{canReadRun as v,requireRun as l,runCallerApp as E}from"../router.js";import{waitSeconds as y}from"../long-poll-wait.js";import{outputSchemaProblem as K}from"../../../core/runtime/structured-output.js";function f(a){if(!a||typeof a!="object"||Array.isArray(a))throw new n(400,"invalid_request","a JSON object is required");return a}m(f,"object");function q(a,t,i,d,u){if(a===null)return u;if(!/^(0|[1-9]\d*)$/.test(a)||!Number.isSafeInteger(Number(a))||Number(a)<i||Number(a)>d)throw new n(400,"invalid_request",`invalid ${t}`,t);return Number(a)}m(q,"integer");const T=256*1024;async function W(a,t,i,d,u,_){switch(a.name){case"grant.get":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=y("grant.get",d),s=d.get("since");if(s!==null&&!/^[A-Za-z0-9_-]{1,64}$/.test(s))throw new n(400,"invalid_request","invalid since","since");let o=h(t);return e&&s!==null&&g(o)===s&&(await S(t.grant,e),o=h(t)),{status:200,body:{sub:t.sub,grantee:t.grantee,client_id:t.client_id,grant:t.grant,rev:o.rev,resolved_scopes:o.resolved_scopes,spend_cap:o.spend_cap,exp:t.exp,payer:"user",dependencies:o.dependencies??{},version:g(o)}}}case"run.start":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=f(u),s=["mode","parent","fork_from","node_id","env","tools","system_prompt"];for(const r of s)if(e[r]!==void 0)throw new n(403,"owner_only",`${r} is owner-only`,r);const o=["payer","model_policy","kind"];for(const r of o)if(e[r]!==void 0)throw new n(400,"invalid_request",`${r} is not available`,r);for(const r of Object.keys(e))if(!["prompt","scopes","metadata","profile","isolated","cwd","worktree","output_schema","message","model","deadline"].includes(r))throw new n(400,"invalid_request",`unknown field ${r}`,r);if(e.model!==void 0&&(typeof e.model!="string"||!e.model.trim()))throw new n(400,"invalid_request","model must be a non-empty string","model");if(e.deadline!==void 0&&(typeof e.deadline!="string"||!e.deadline.trim()))throw new n(400,"invalid_request","deadline must be a duration string such as 10m","deadline");if(e.profile!==void 0&&(typeof e.profile!="string"||!e.profile))throw new n(400,"invalid_request","profile must be a profile id","profile");if(e.isolated!==void 0&&typeof e.isolated!="boolean")throw new n(400,"invalid_request","isolated must be a boolean","isolated");if(e.cwd!==void 0&&(e.isolated!==!0||typeof e.cwd!="string"||!e.cwd))throw new n(400,"invalid_request","cwd is available only for an isolated run and must be an absolute path inside its root folder","cwd");if(e.worktree!==void 0&&e.isolated!==!0)throw new n(400,"invalid_request","worktree is not available","worktree");if(e.isolated===!0&&e.worktree!==void 0)throw new n(400,"invalid_request","worktree is unavailable for an isolated run","worktree");if(typeof e.prompt!="string"||!e.prompt.trim())throw new n(400,"invalid_request","prompt is required","prompt");if(e.message!==void 0&&(typeof e.message!="string"||!e.message.trim()))throw new n(400,"invalid_request","message must be a non-empty string","message");const w=e.metadata===void 0?{}:f(e.metadata);if(Buffer.byteLength(JSON.stringify(w))>4096)throw new n(400,"invalid_request","metadata exceeds 4 KB","metadata");let c;if(e.output_schema!==void 0){const r=e.output_schema;if(!r||typeof r!="object"||Array.isArray(r))throw new n(400,"invalid_request","output_schema must be a JSON Schema object","output_schema");if(Buffer.byteLength(JSON.stringify(r))>T)throw new n(400,"invalid_request","output_schema exceeds 256 KB","output_schema");const p=await K(r);if(p!==null)throw new n(400,"invalid_request",`output_schema is not usable: ${p}`,"output_schema");c=r}const k=B(t.resolved,e.scopes,t.grantee);return{status:201,body:await J(t,{prompt:e.prompt,scopes:k,metadata:w,profile:e.profile,isolated:e.isolated,cwd:e.cwd,...e.model===void 0?{}:{model:e.model},...e.deadline===void 0?{}:{deadline:e.deadline},...c===void 0?{}:{output_schema:c},...e.message===void 0?{}:{message:e.message}},b(_))}}case"run.list":{const e=d.get("status")??void 0;if(e&&!["running","waiting_on_user","idle","settled"].includes(e))throw new n(400,"invalid_request","invalid status","status");const s=d.get("app");if(s!==null&&!/^(?:app:)?[a-z0-9][a-z0-9-]*$/.test(s))throw new n(400,"invalid_request","invalid app","app");const o=s===null?E(t):s.startsWith("app:")?s:`app:${s}`;if(o!==null&&!v(t,o))throw new n(404,"not_found","run not found");return{status:200,body:A(o,{status:e,after:d.get("after")??void 0,limit:q(d.get("limit"),"limit",1,100,50),..."class"in t&&(t.class==="broker"||t.class==="cron")&&o!==null?{visible:m(w=>v(t,o,w),"visible")}:{}})}}case"run.get":{const e=y("run.get",d),s=l(t,i),o=await C(i,s.grantee,e);return l(t,i),{status:200,body:o}}case"run.message":{const e=f(u);if(typeof e.message!="string"||!e.message.trim())throw new n(400,"invalid_request","message is required","message");if(e.start_turn!==void 0&&typeof e.start_turn!="boolean")throw new n(400,"invalid_request","start_turn must be a boolean","start_turn");return{status:202,body:await j(l(t,i,!0),e.message,void 0,e.start_turn,b(_))}}case"run.interrupt":{const e=f(u===void 0?{}:u);if(e.children!==void 0&&e.children!=="stop"&&e.children!=="continue")throw new n(400,"invalid_request","invalid children","children");return{status:200,body:await O(l(t,i,!0),e.children)}}case"run.cancel":{const e=f(u===void 0?{}:u);if(e.keep_schedules!==void 0&&typeof e.keep_schedules!="boolean")throw new n(400,"invalid_request","invalid keep_schedules","keep_schedules");return{status:200,body:await $(l(t,i,!0),{keepSchedules:e.keep_schedules})}}case"run.rename":{const e=f(u);for(const s of Object.keys(e))if(s!=="name")throw new n(400,"invalid_request",`unknown field ${s}`,s);return{status:200,body:x(l(t,i,!0),e.name)}}case"run.delete":{const{uploads_pending:e}=await N(l(t,i,!0,!0));return{status:200,body:{run_id:i,uploads_pending:e}}}case"trace.get":{const e=d.get("format")??void 0;if(e&&e!=="entries")throw new n(400,"invalid_request","unknown trace format","format");const s=l(t,i);return{status:200,body:z(i,s.grantee,e)}}default:throw new n(404,"not_found","unknown operation")}}m(W,"handleRunOperation");export{W as handleRunOperation,q as integer};
|
|
@@ -35,11 +35,11 @@ A call is refused when the scope is missing from the grant. The refusal is an `A
|
|
|
35
35
|
|
|
36
36
|
Some apps do not work until a provider is set up for the person. Name it under **What your app requires** on the **What it asks for** tab. The person is then taken through that provider's setup the first time they sign in. Context, for example, asks for a profile URL, and its state stays `pending` for a few minutes afterwards.
|
|
37
37
|
|
|
38
|
-
Read the state on each request that depends on it. `runtime.grant.get()` returns `dependencies`, one entry per
|
|
38
|
+
Read the state on each request that depends on it. `runtime.grant.get()` returns `dependencies`, one entry per setup id the app requires or lists as optional, plus any the directory implies (such as `device` for `context.deep`), keyed by setup id (or `{}` when it has none):
|
|
39
39
|
|
|
40
40
|
```ts
|
|
41
41
|
const {dependencies} = await runtime.grant.get();
|
|
42
|
-
const context = dependencies['context']; // {state, message?, next} or undefined
|
|
42
|
+
const context = dependencies['context']; // {state, message?, next, step?} or undefined
|
|
43
43
|
if (context?.state !== 'ready') {
|
|
44
44
|
// Show context.message and link context.next; never hard-code either.
|
|
45
45
|
}
|
|
@@ -51,8 +51,9 @@ if (context?.state !== 'ready') {
|
|
|
51
51
|
| `pending` | The provider is working. |
|
|
52
52
|
| `ready` | Calls that depend on it work. |
|
|
53
53
|
| `failed` | Setup failed. `message` says why. |
|
|
54
|
+
| `unavailable` | The provider does not offer this to the person's account. `message` says so; there is nothing for them to do. |
|
|
54
55
|
|
|
55
|
-
`message` is the provider's own sentence, present for `pending` and `
|
|
56
|
+
`message` is the provider's own sentence, present for `pending`, `failed` and `unavailable`. `step` is present when the provider reports where things stand (for example `researching`); its values may grow, so ignore one you do not know. `next` is the absolute URL of the setup page for this provider and your app; `setupLink(dependency, {returnTo})` from `@north-light/sdk` adds a `return_to` with your app's registered origin so the person returns to you, and keeps the query `next` already carries. A key can contain a dot (`context.deep`). An entry is missing when the runtime has not synced the grant since the app began requiring the provider: treat that as `not_started`. The runtime does not block your calls for you, so refuse the dependent operation yourself. To act the moment a state turns `ready`, follow it with `oauth.watchGrants`; see [Grant changes](/docs/guides/grant-changes).
|
|
56
57
|
|
|
57
58
|
## Checked and not checked
|
|
58
59
|
|
|
@@ -39,31 +39,33 @@ await watcher.stop();
|
|
|
39
39
|
|
|
40
40
|
## Wait on a provider your app requires
|
|
41
41
|
|
|
42
|
-
An app whose registration `requires` a provider (the console's **What your app requires**) needs that provider set up for the person before the operations that depend on it work. The runtime does not check this for you. `grant.get()` returns `dependencies`: one entry per
|
|
42
|
+
An app whose registration `requires` a provider (the console's **What your app requires**) needs that provider set up for the person before the operations that depend on it work. The runtime does not check this for you. `grant.get()` returns `dependencies`: one entry per setup id the app requires or lists as optional (plus any the directory implies, such as `device` for `context.deep`), keyed by setup id, or `{}` when it has none. Read it on each request that needs the provider; do not store it.
|
|
43
43
|
|
|
44
44
|
```ts
|
|
45
|
-
import type
|
|
45
|
+
import {setupLink, type ProviderDependency} from '@north-light/sdk';
|
|
46
46
|
|
|
47
|
-
// ProviderDependency is { state: 'not_started' | 'pending' | 'ready' | 'failed'; message?: string; next: string }.
|
|
47
|
+
// ProviderDependency is { state: 'not_started' | 'pending' | 'ready' | 'failed' | 'unavailable'; message?: string; next: string; step?: string }.
|
|
48
48
|
const {dependencies} = await runtime.grant.get();
|
|
49
49
|
const context: ProviderDependency | undefined = dependencies['context'];
|
|
50
50
|
if (context?.state !== 'ready') {
|
|
51
51
|
// Show the provider's own sentence and link its setup page; never hard-code either.
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
// Nothing to link when the provider does not offer this to the person (`unavailable`).
|
|
53
|
+
const next = context && context.state !== 'unavailable' ? setupLink(context, {returnTo: 'https://app.example.com'}) : undefined; // your registered redirect origin
|
|
54
54
|
throw new AppError('provider_setup_pending', context?.message ?? 'Finish setting up Context, then try again.', {
|
|
55
55
|
status: 403,
|
|
56
56
|
retryable: false,
|
|
57
|
-
details: {provider: 'context', state: context?.state ?? 'not_started', message: context?.message, next
|
|
57
|
+
details: {provider: 'context', state: context?.state ?? 'not_started', message: context?.message, next},
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
`AppError` stands for your app's own typed error ([Errors](/docs/errors)).
|
|
63
63
|
|
|
64
|
-
- `state` is `not_started` (the person has not done their part), `pending` (the provider is working), `ready` or `
|
|
65
|
-
- `message` is the provider's own sentence for `pending` and `
|
|
66
|
-
- `
|
|
64
|
+
- `state` is `not_started` (the person has not done their part), `pending` (the provider is working), `ready`, `failed`, or `unavailable` (the provider does not offer this to the person's account; there is nothing for them to do, so do not link `next`).
|
|
65
|
+
- `message` is the provider's own sentence for `pending`, `failed` and `unavailable`. It is absent for `not_started` and `ready`, so keep a short fallback.
|
|
66
|
+
- `step` is present when the provider reports where the person or the work is, for example `researching`. Its values belong to the provider and may grow: show a known one in your own words and ignore an unknown one. A change to `step` alone changes the grant's `version`, so `watchGrants` delivers it.
|
|
67
|
+
- `next` is the absolute URL of the directory's setup page for this provider and your app. Use `setupLink(dependency, {returnTo})` to add `return_to` (your app's registered redirect origin) so the person comes back to you. It keeps the query `next` already has, such as `client_id`, and encodes `returnTo`; do not build the URL yourself.
|
|
68
|
+
- A key can contain a dot. `context.deep` is a level of the `context` provider; read it as `dependencies['context.deep']`.
|
|
67
69
|
- An entry is missing when the runtime has not synced the grant since the app began requiring the provider. Treat it as `not_started`.
|
|
68
70
|
|
|
69
71
|
Refuse the dependent operation with `provider_setup_pending` (403), carrying `state`, `message` and `next` in `details`. To act when the state turns `ready`, use `watchGrants` above instead of polling.
|
|
@@ -7,6 +7,22 @@ description: Behavior changes in @north-light/sdk that an app must act on, newes
|
|
|
7
7
|
|
|
8
8
|
Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
|
|
9
9
|
|
|
10
|
+
## 0.3.483
|
|
11
|
+
|
|
12
|
+
### `unavailable`, `step` and `setupLink` on `grant.get` dependencies (additive for the SDK; a type change for exhaustive switches)
|
|
13
|
+
|
|
14
|
+
- `SetupState` gains `unavailable`: the provider does not offer this setup to the person's account. It is not `failed`. If you `switch` over `state` exhaustively, add a case; show `message` and do not link `next`.
|
|
15
|
+
- `ProviderDependency` gains an optional `step: string` (for example `researching`, or `downloaded` on `device`). Its values belong to the provider and may grow: ignore one you do not know. A change to `step` alone changes `grant.get`'s `version`, so `oauth.watchGrants` delivers it.
|
|
16
|
+
- `dependencies` keys may be dotted setup ids such as `context.deep`.
|
|
17
|
+
- New `setupLink(dependency, {returnTo})` returns `dependency.next` with `return_to` set and URL-encoded, keeping the query `next` already has (such as `client_id`). Use it instead of building the link yourself.
|
|
18
|
+
- A runtime older than this release drops a dependency whose state is `unavailable` and silently loses `step`, so an app that reads either needs a runtime on this release.
|
|
19
|
+
|
|
20
|
+
## 0.3.482
|
|
21
|
+
|
|
22
|
+
### `runs.start` accepts `deadline` (additive, no app change)
|
|
23
|
+
|
|
24
|
+
`RunStartParams.deadline` bounds a run's wall-clock time, measured from start: one or more `<int>[smhd]` parts (`10m`, `1h30m`), at most `24h`. When it passes, the run settles with `outcome.reason` `deadline_exceeded` and its agent is stopped. A malformed, zero or over-24h value is refused with 400 `invalid_request` and `param: 'deadline'`, and no run is created. `deadline` takes part in the idempotency body, so the same key with another deadline is a 409 `idempotency_conflict`. `model` (already accepted) and `deadline` work on `isolated` runs. `payer`, `model_policy` and `kind` are still refused.
|
|
25
|
+
|
|
10
26
|
## 0.3.481
|
|
11
27
|
|
|
12
28
|
### `npx @north-light/sdk login`, `logout`, `whoami`, `register` and `apps` (additive, no app change)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.483",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
},
|
|
138
138
|
"license": "UNLICENSED",
|
|
139
139
|
"dependencies": {
|
|
140
|
-
"@north-light/identity": "0.3.
|
|
140
|
+
"@north-light/identity": "0.3.483",
|
|
141
141
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
142
142
|
"@earendil-works/pi-ai": "1.1.0",
|
|
143
143
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.483",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/cli",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.483",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"workspaces": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@earendil-works/pi-ai": "1.1.0",
|
|
19
19
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
|
20
20
|
"@earendil-works/pi-tui": "1.1.0",
|
|
21
|
-
"@north-light/identity": "0.3.
|
|
21
|
+
"@north-light/identity": "0.3.483",
|
|
22
22
|
"cron-parser": "^5.6.0",
|
|
23
23
|
"esbuild": "^0.27.7",
|
|
24
24
|
"jose": "^6.2.12",
|
|
@@ -12116,17 +12116,17 @@
|
|
|
12116
12116
|
},
|
|
12117
12117
|
"packages/crouter-api": {
|
|
12118
12118
|
"name": "@north-light/api",
|
|
12119
|
-
"version": "0.3.
|
|
12119
|
+
"version": "0.3.483",
|
|
12120
12120
|
"license": "UNLICENSED"
|
|
12121
12121
|
},
|
|
12122
12122
|
"packages/crouter-env-docker": {
|
|
12123
12123
|
"name": "@north-light/env-docker",
|
|
12124
|
-
"version": "0.3.
|
|
12124
|
+
"version": "0.3.483",
|
|
12125
12125
|
"license": "UNLICENSED"
|
|
12126
12126
|
},
|
|
12127
12127
|
"packages/crouter-identity": {
|
|
12128
12128
|
"name": "@north-light/identity",
|
|
12129
|
-
"version": "0.3.
|
|
12129
|
+
"version": "0.3.483",
|
|
12130
12130
|
"license": "UNLICENSED",
|
|
12131
12131
|
"dependencies": {
|
|
12132
12132
|
"jose": "^6.2.1"
|
|
@@ -12134,20 +12134,20 @@
|
|
|
12134
12134
|
},
|
|
12135
12135
|
"packages/crouter-plugin": {
|
|
12136
12136
|
"name": "@north-light/plugin",
|
|
12137
|
-
"version": "0.3.
|
|
12137
|
+
"version": "0.3.483",
|
|
12138
12138
|
"license": "UNLICENSED",
|
|
12139
12139
|
"dependencies": {
|
|
12140
|
-
"@north-light/api": "0.3.
|
|
12141
|
-
"@north-light/identity": "0.3.
|
|
12140
|
+
"@north-light/api": "0.3.483",
|
|
12141
|
+
"@north-light/identity": "0.3.483"
|
|
12142
12142
|
}
|
|
12143
12143
|
},
|
|
12144
12144
|
"packages/crouter-sdk": {
|
|
12145
12145
|
"name": "@north-light/sdk",
|
|
12146
|
-
"version": "0.3.
|
|
12146
|
+
"version": "0.3.483",
|
|
12147
12147
|
"license": "UNLICENSED",
|
|
12148
12148
|
"dependencies": {
|
|
12149
|
-
"@north-light/api": "0.3.
|
|
12150
|
-
"@north-light/identity": "0.3.
|
|
12149
|
+
"@north-light/api": "0.3.483",
|
|
12150
|
+
"@north-light/identity": "0.3.483",
|
|
12151
12151
|
"jose": "^6.2.1"
|
|
12152
12152
|
},
|
|
12153
12153
|
"bin": {
|