@north-light/cli 0.3.580 → 0.3.582
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +6 -1
- package/dist/api/client.js +3 -3
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/execution-start.d.ts +14 -0
- package/dist/api/dto/health.d.ts +22 -0
- package/dist/api/dto/imports.d.ts +142 -0
- package/dist/api/dto/imports.js +0 -0
- package/dist/api/error-codes.d.ts +5 -0
- package/dist/api/error-codes.js +1 -1
- package/dist/api/errors.js +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -1
- package/dist/api/routes.d.ts +2 -0
- package/dist/api/routes.js +1 -1
- package/dist/build-root.d.ts +11 -4
- package/dist/build-root.js +1 -1
- package/dist/cli.js +7 -7
- package/dist/clients/attach/session/connection.d.ts +3 -1
- package/dist/clients/attach/session/connection.js +1 -1
- package/dist/clients/attach/viewer.js +529 -529
- package/dist/commands/api-client.d.ts +12 -4
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/sys/daemon.js +1 -1
- package/dist/commands/sys/desktop.d.ts +5 -0
- package/dist/commands/sys/desktop.js +1 -0
- package/dist/commands/sys/import/move.d.ts +55 -0
- package/dist/commands/sys/import/move.js +8 -0
- package/dist/commands/sys/import/resume.d.ts +1 -0
- package/dist/commands/sys/import/resume.js +1 -0
- package/dist/commands/sys/import/start.d.ts +4 -0
- package/dist/commands/sys/import/start.js +1 -0
- package/dist/commands/sys/join.d.ts +2 -0
- package/dist/commands/sys/join.js +1 -1
- package/dist/commands/sys/machine/rotate.d.ts +1 -0
- package/dist/commands/sys/machine/rotate.js +1 -0
- package/dist/commands/sys/signin.d.ts +7 -0
- package/dist/commands/sys/signin.js +2 -0
- package/dist/commands/sys.js +1 -1
- package/dist/core/blobs/r2.d.ts +4 -0
- package/dist/core/blobs/r2.js +7 -4
- package/dist/core/canvas/daemon-owner.d.ts +1 -1
- package/dist/core/canvas/executions.d.ts +1 -1
- package/dist/core/canvas/node-tokens.d.ts +2 -0
- package/dist/core/canvas/node-tokens.js +3 -3
- package/dist/core/canvas/pid.d.ts +6 -0
- package/dist/core/canvas/pid.js +5 -5
- package/dist/core/command-plugins/compose.js +1 -1
- package/dist/core/command.d.ts +17 -8
- package/dist/core/command.js +9 -9
- package/dist/core/directory/issuer.d.ts +3 -0
- package/dist/core/directory/issuer.js +1 -1
- package/dist/core/directory/person-credential.d.ts +61 -0
- package/dist/core/directory/person-credential.js +1 -0
- package/dist/core/directory/reach.d.ts +3 -0
- package/dist/core/directory/reach.js +1 -0
- package/dist/core/grants/mirror.d.ts +3 -0
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/graph/bodies-boot-copy.d.ts +16 -2
- package/dist/core/graph/bodies-boot-copy.js +1 -1
- package/dist/core/graph/bodies.d.ts +13 -4
- package/dist/core/graph/bodies.js +1 -1
- package/dist/core/graph/deletion.d.ts +4 -1
- package/dist/core/graph/deletion.js +3 -3
- package/dist/core/graph/documents.d.ts +4 -0
- package/dist/core/graph/documents.js +9 -9
- package/dist/core/graph/reader.js +2 -2
- package/dist/core/graph/repo-sync/exchange.js +2 -2
- package/dist/core/graph/repo-sync/ref-format.d.ts +7 -2
- package/dist/core/graph/repo-sync/ref-format.js +2 -2
- package/dist/core/import/bundle/blob-keys.d.ts +10 -0
- package/dist/core/import/bundle/blob-keys.js +1 -0
- package/dist/core/import/bundle/layout.d.ts +90 -0
- package/dist/core/import/bundle/layout.js +5 -0
- package/dist/core/import/export/archive.d.ts +11 -0
- package/dist/core/import/export/archive.js +1 -0
- package/dist/core/import/export/cloud.d.ts +23 -0
- package/dist/core/import/export/cloud.js +1 -0
- package/dist/core/import/export/dump.d.ts +34 -0
- package/dist/core/import/export/dump.js +3 -0
- package/dist/core/import/export/files.d.ts +22 -0
- package/dist/core/import/export/files.js +1 -0
- package/dist/core/import/export/inline.d.ts +3 -0
- package/dist/core/import/export/inline.js +1 -0
- package/dist/core/import/export/source.d.ts +2 -0
- package/dist/core/import/export/source.js +1 -0
- package/dist/core/import/export/stores.d.ts +9 -0
- package/dist/core/import/export/stores.js +1 -0
- package/dist/core/import/load/bind.d.ts +19 -0
- package/dist/core/import/load/bind.js +15 -0
- package/dist/core/import/load/blobs.d.ts +10 -0
- package/dist/core/import/load/blobs.js +1 -0
- package/dist/core/import/load/catalog.d.ts +32 -0
- package/dist/core/import/load/catalog.js +9 -0
- package/dist/core/import/load/copy.d.ts +26 -0
- package/dist/core/import/load/copy.js +6 -0
- package/dist/core/import/load/path-map.d.ts +7 -0
- package/dist/core/import/load/path-map.js +1 -0
- package/dist/core/import/load/prepare.d.ts +37 -0
- package/dist/core/import/load/prepare.js +10 -0
- package/dist/core/import/load/rewrite.d.ts +39 -0
- package/dist/core/import/load/rewrite.js +13 -0
- package/dist/core/import/load/rollback.d.ts +3 -0
- package/dist/core/import/load/rollback.js +6 -0
- package/dist/core/import/load/run.d.ts +22 -0
- package/dist/core/import/load/run.js +3 -0
- package/dist/core/import/load/stage.d.ts +15 -0
- package/dist/core/import/load/stage.js +1 -0
- package/dist/core/import/load/stores.d.ts +24 -0
- package/dist/core/import/load/stores.js +2 -0
- package/dist/core/import/load/verify.d.ts +4 -0
- package/dist/core/import/load/verify.js +12 -0
- package/dist/core/io.js +4 -4
- package/dist/core/northlight/previews.d.ts +22 -0
- package/dist/core/northlight/previews.js +1 -0
- package/dist/core/providers/call.js +1 -1
- package/dist/core/providers/device.js +1 -1
- package/dist/core/providers/files.d.ts +2 -1
- package/dist/core/providers/files.js +1 -1
- package/dist/core/runtime/broker/cli-relay.js +1 -1
- package/dist/core/runtime/broker/model-adapter.js +3 -3
- package/dist/core/runtime/broker-ownership.d.ts +1 -0
- package/dist/core/runtime/broker-ownership.js +1 -0
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/first-run-offer.d.ts +7 -2
- package/dist/core/runtime/first-run-offer.js +10 -2
- package/dist/core/runtime/front-door.js +4 -4
- package/dist/core/runtime/revive.d.ts +5 -2
- package/dist/core/runtime/revive.js +2 -2
- package/dist/core/runtime/runner/blaxel/runner.js +3 -3
- package/dist/core/runtime/runner/index.d.ts +1 -0
- package/dist/core/runtime/runner/index.js +1 -1
- package/dist/core/runtime/runner/machine/call.d.ts +8 -0
- package/dist/core/runtime/runner/machine/call.js +3 -0
- package/dist/core/runtime/runner/machine/environment.d.ts +1 -0
- package/dist/core/runtime/runner/machine/environment.js +1 -0
- package/dist/core/runtime/runner/machine/executor.d.ts +29 -0
- package/dist/core/runtime/runner/machine/executor.js +1 -0
- package/dist/core/runtime/runner/machine/machine-executor.test.d.ts +1 -0
- package/dist/core/runtime/runner/machine/machine-executor.test.js +4 -0
- package/dist/core/runtime/runner/machine/ops.d.ts +127 -0
- package/dist/core/runtime/runner/machine/ops.js +1 -0
- package/dist/core/runtime/runner/machine/runner.d.ts +12 -0
- package/dist/core/runtime/runner/machine/runner.js +1 -0
- package/dist/core/runtime/runner/types.d.ts +7 -1
- package/dist/core/runtime/warm-pool.d.ts +2 -0
- package/dist/core/runtime/warm-pool.js +1 -1
- package/dist/core/secrets/env-rows.d.ts +1 -0
- package/dist/core/secrets/env-rows.js +1 -1
- package/dist/core/storage/purge.d.ts +6 -0
- package/dist/core/storage/purge.js +4 -0
- package/dist/core/subscription-state.d.ts +1 -0
- package/dist/core/subscription-state.js +1 -1
- package/dist/daemon/api/bridge.js +3 -3
- package/dist/daemon/api/handlers/attach.js +1 -1
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/daemon.js +1 -1
- package/dist/daemon/api/handlers/execution-start.js +2 -2
- package/dist/daemon/api/handlers/files.js +1 -1
- package/dist/daemon/api/handlers/imports.d.ts +2 -0
- package/dist/daemon/api/handlers/imports.js +1 -0
- package/dist/daemon/api/handlers/machine-credential.d.ts +2 -0
- package/dist/daemon/api/handlers/machine-credential.js +1 -0
- package/dist/daemon/api/handlers/profiles.js +1 -1
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/dist/daemon/api/import-auth.d.ts +6 -0
- package/dist/daemon/api/import-auth.js +1 -0
- package/dist/daemon/api/operations.d.ts +2 -2
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/router.d.ts +7 -1
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/boot-migrations.js +1 -1
- package/dist/daemon/broker-signals/bus.d.ts +3 -2
- package/dist/daemon/broker-signals/bus.js +1 -1
- package/dist/daemon/cron/armed-context.d.ts +1 -0
- package/dist/daemon/cron/armed-context.js +1 -1
- package/dist/daemon/cron/closed.d.ts +31 -0
- package/dist/daemon/cron/closed.js +1 -0
- package/dist/daemon/cron/ended.js +2 -2
- package/dist/daemon/cron/lease-recovery.js +1 -1
- package/dist/daemon/cron/machine-exec.d.ts +11 -0
- package/dist/daemon/cron/machine-exec.js +1 -0
- package/dist/daemon/cron/machine-fire.d.ts +26 -0
- package/dist/daemon/cron/machine-fire.js +1 -0
- package/dist/daemon/cron/recurrence.d.ts +14 -0
- package/dist/daemon/cron/recurrence.js +1 -0
- package/dist/daemon/cron/run-token.d.ts +7 -0
- package/dist/daemon/cron/run-token.js +1 -0
- package/dist/daemon/cron-run.js +2 -2
- package/dist/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/crtrd.d.ts +3 -3
- package/dist/daemon/crtrd.js +14 -14
- package/dist/daemon/drain.d.ts +25 -0
- package/dist/daemon/drain.js +9 -0
- package/dist/daemon/due-sources.d.ts +1 -1
- package/dist/daemon/due-sources.js +1 -1
- package/dist/daemon/due.js +8 -8
- package/dist/daemon/fleet.js +5 -5
- package/dist/daemon/import-dump-run.d.ts +1 -0
- package/dist/daemon/import-dump-run.js +5 -0
- package/dist/daemon/machine-daemon.d.ts +10 -0
- package/dist/daemon/machine-daemon.js +1 -0
- package/dist/daemon/machine-link/client.d.ts +29 -0
- package/dist/daemon/machine-link/client.js +1 -0
- package/dist/daemon/machine-link/config.d.ts +45 -0
- package/dist/daemon/machine-link/config.js +3 -0
- package/dist/daemon/machine-link/credential.d.ts +7 -0
- package/dist/daemon/machine-link/credential.js +2 -0
- package/dist/daemon/machine-link/forward.d.ts +16 -0
- package/dist/daemon/machine-link/forward.js +1 -0
- package/dist/daemon/machine-link/frames.d.ts +49 -0
- package/dist/daemon/machine-link/frames.js +1 -0
- package/dist/daemon/machine-link/holder.d.ts +73 -0
- package/dist/daemon/machine-link/holder.js +9 -0
- package/dist/daemon/machine-link/move-notify.d.ts +4 -0
- package/dist/daemon/machine-link/move-notify.js +1 -0
- package/dist/daemon/manage.d.ts +19 -3
- package/dist/daemon/manage.js +3 -3
- package/dist/daemon/reconcilers/cron-lane.js +1 -1
- package/dist/daemon/reconcilers/delivery-lane.js +2 -2
- package/dist/daemon/reconcilers/import-lane.d.ts +9 -0
- package/dist/daemon/reconcilers/import-lane.js +1 -0
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +3 -0
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
- package/dist/daemon/reconcilers/purge-due-work.d.ts +13 -0
- package/dist/daemon/reconcilers/purge-due-work.js +1 -0
- package/dist/daemon/reconcilers/row-claim.d.ts +1 -0
- package/dist/daemon/reconcilers/row-claim.js +2 -2
- package/dist/daemon/runs/observer.js +3 -3
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- package/scripts/sandbox-start.mjs +185 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
var _t=Object.defineProperty;var l=(t,a)=>_t(t,"name",{value:a,configurable:!0});import{createHash as ht,randomUUID as x}from"node:crypto";import{brokerConfigSnapshot as pt}from"./config.js";import{openDb as W,withCanvasWrite as N}from"./canvas/db.js";import{personIdAsync as y}from"./canvas/person.js";import{loadSecretsKey as K,open as X,seal as $}from"./secrets/index.js";import{modelCooldownFamily as wt}from"./rate-limit-scope.js";import{REFRESH_BUSY_PHRASE as At,REFRESH_TIMEOUT_PHRASE as bt}from"./provider-error-recognition.js";import{registerRotationReader as yt}from"./model-routes.js";import{coolingUntilFor as Et,getProviderLabel as h,isInvalidRefreshTokenError as Y,DuplicateSubscriptionError as O,ANTHROPIC_PROVIDER_ID as Lt,MAX_RATE_LIMIT_COOLDOWN_MS as B,INVALID_REFRESH_TOKEN_BACKOFF_MS as Rt}from"./managed-providers.js";export*from"./managed-providers.js";const J="strong";function Q(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}l(Q,"normalizeStrength");function _(t){return t.toLowerCase()}l(_,"key");function j(t){return typeof t=="string"&&t.trim()?t.trim():void 0}l(j,"optionalString");function gt(t){const a=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",n=typeof t.access=="string"?t.access.trim():"",i=Number(t.expires);if(!a||!e||!n||!Number.isFinite(i))return;const r=j(t.accountId),s=j(t.clientId);return{label:a,refresh:e,access:n,expires:i,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...r?{accountId:r}:{},...s?{clientId:s}:{},...Z(t.modelCooldowns)?{modelCooldowns:Z(t.modelCooldowns)}:{}}}l(gt,"credential");function Z(t){if(!t||typeof t!="object"||Array.isArray(t))return;const a=Object.entries(t).filter(([e,n])=>e.trim()!==""&&Number.isFinite(Number(n))).map(([e,n])=>[e,Number(n)]);return a.length?Object.fromEntries(a):void 0}l(Z,"modelCooldowns");function F(t,a){if(!a.label?.trim())throw new Error(`${h(t)} subscription label cannot be empty`);const e=gt(a);if(!e)throw new Error(`Refusing to persist incomplete ${h(t)} subscription "${a.label.trim()}"; pool rows require both OAuth tokens.`);return e}l(F,"assertCredential");function v(t,a){const e=t.accountId?.trim(),n=a.accountId?.trim();return!!(e&&n&&e!==n)}l(v,"differentAccount");function p(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${ht("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}l(p,"identity");function tt(t,a){const e=j(a);return e?{...t,clientId:e}:t}l(tt,"withClientId");function et(t,a,e){const n=e.refresh?.trim()??"",i=e.access?.trim()??"",r=Number(e.expires);if(!n||!i||!Number.isFinite(r))throw new Error(`${h(t)} subscription "${a}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:n,access:i,expires:r}}l(et,"assertTokens");const at=1e4,It="15s",xt="55P03";class nt extends Error{static{l(this,"RefreshBusyError")}constructor(a,e){super(`${h(a)} subscription "${e}" ${At}; treating it as cooling for this call`),this.name="RefreshBusyError"}}class Nt extends Error{static{l(this,"RefreshTimeoutError")}constructor(a){super(`${h(a)} ${bt} ${at} ms`),this.name="RefreshTimeoutError"}}const it="id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure,refresh_sealed,access_sealed,expires,vendor_account_id,client_id,key_id",I=l((t,a,e,n)=>`model_accounts|${t}|${a}|${e}|${n}`,"aadOf");function E(t){return{refresh:t.refresh,access:t.access,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{}}}l(E,"stored");function Ft(t,a){return{providerId:t,id:a.id,label:a.label,labelKey:a.label_key,position:a.position,lastAttemptAt:a.last_attempt_at,lastRateLimitedAt:a.last_rate_limited_at,rateLimitedUntil:a.rate_limited_until,...a.auth_failure?{authFailure:a.auth_failure}:{}}}l(Ft,"rowOf");function rt(t,a,e,n=[]){if(!e.refresh_sealed||!e.access_sealed||e.expires===null||!e.key_id)throw new Error(`managed ${a} account ${e.id} has no credential record`);const i=K(),r=X(e.refresh_sealed,e.key_id,i,I(t,a,e.id,"refresh")),s=X(e.access_sealed,e.key_id,i,I(t,a,e.id,"access")),o=Ft(a,e),c={label:o.label,refresh:r,access:s,expires:e.expires,rateLimitedUntil:o.authFailure||!o.lastRateLimitedAt?o.rateLimitedUntil:Math.min(o.rateLimitedUntil,o.lastRateLimitedAt+B),lastAttemptAt:o.lastAttemptAt,lastRateLimitedAt:o.lastRateLimitedAt,...o.authFailure?{authFailure:o.authFailure}:{},...e.vendor_account_id?{accountId:e.vendor_account_id}:{},...e.client_id?{clientId:e.client_id}:{},...n.length?{modelCooldowns:Object.fromEntries(n.map(d=>[d.family,d.lastRateLimitedAt?Math.min(d.rateLimitedUntil,d.lastRateLimitedAt+B):d.rateLimitedUntil]))}:{}};return{row:o,entry:c}}l(rt,"toPublic");async function S(t,a,e){const n=new Map,i=await t.prepare("SELECT account_id,family,rate_limited_until,last_rate_limited_at FROM model_account_cooldowns WHERE person=? AND provider_id=? ORDER BY family").all(a,e);for(const r of i){const s=n.get(r.account_id)??[];s.push({family:r.family,rateLimitedUntil:r.rate_limited_until,lastRateLimitedAt:r.last_rate_limited_at}),n.set(r.account_id,s)}return n}l(S,"loadCooldowns");async function k(t,a,e,n=!1){const i=await t.prepare(`SELECT ${it} FROM model_accounts WHERE person=? AND provider_id=? ORDER BY position${n?" FOR UPDATE":""}`).all(a,e),r=await S(t,a,e);return i.map(s=>rt(a,e,s,r.get(s.id)))}l(k,"loadPool");async function st(t,a,e,n){const i=n.trim().toLowerCase(),r=Number.parseInt(i,10),s=Number.isFinite(r)&&String(r)===i&&r>=1,o=await t.prepare(`SELECT ${it} FROM model_accounts WHERE person=? AND provider_id=? AND ${s?"position=?":"label_key=?"} FOR UPDATE`).get(a,e,s?r-1:i);if(o)return rt(a,e,o,(await S(t,a,e)).get(o.id))}l(st,"lockedAccount");async function T(t){await t.exec(`SET LOCAL lock_timeout = '${It}'`)}l(T,"setLockTimeout");async function L(t,a){const e=await y();return N(async n=>(await T(n),await n.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(`model_accounts|${e}|${t}`),a({db:n,person:e,rows:await k(n,e,t,!0)})))}l(L,"locked");async function C(t,a,e,n,i){const r=K(),s=$(i.refresh,r,I(a,e,n.id,"refresh")),o=$(i.access,r,I(a,e,n.id,"access"));await t.prepare("INSERT INTO model_accounts(person,provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure,refresh_sealed,access_sealed,expires,vendor_account_id,client_id,key_id) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)").run(a,e,n.id,n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,s.sealed,o.sealed,i.expires,i.accountId??null,i.clientId??null,s.keyId)}l(C,"insertAccount");async function M(t,a,e,n,i){const r=K(),s=$(i.refresh,r,I(a,e,n,"refresh")),o=$(i.access,r,I(a,e,n,"access"));await t.prepare("UPDATE model_accounts SET refresh_sealed=?,access_sealed=?,expires=?,vendor_account_id=?,client_id=?,key_id=? WHERE person=? AND provider_id=? AND id=?").run(s.sealed,o.sealed,i.expires,i.accountId??null,i.clientId??null,s.keyId,a,e,n)}l(M,"setCredential");async function b(t,a,e){await t.prepare("UPDATE model_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE person=? AND provider_id=? AND id=?").run(e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null,a,e.providerId,e.id)}l(b,"updateHealth");async function ot(t,a,e,n,i,r,s){await t.prepare("INSERT INTO model_account_cooldowns(person,provider_id,account_id,family,rate_limited_until,last_rate_limited_at) VALUES(?,?,?,?,?,?) ON CONFLICT(person,provider_id,account_id,family) DO UPDATE SET rate_limited_until=excluded.rate_limited_until,last_rate_limited_at=excluded.last_rate_limited_at").run(a,e,n,i,r,s)}l(ot,"upsertModelCooldown");async function U(t,a,e,n){n===void 0?await t.prepare("DELETE FROM model_account_cooldowns WHERE person=? AND provider_id=?").run(a,e):await t.prepare("DELETE FROM model_account_cooldowns WHERE person=? AND provider_id=? AND account_id=?").run(a,e,n)}l(U,"deleteModelCooldowns");const R=l(async({db:t,person:a},e)=>(await k(t,a,e)).map(({entry:n})=>n),"publicPool"),lt=l(t=>t?.code===xt,"lockWaitExpired");function ct(t,a){let e;return Promise.race([a,new Promise((n,i)=>{e=setTimeout(()=>i(new Nt(t)),at)})]).finally(()=>clearTimeout(e))}l(ct,"withExchangeTimeout");async function V(t){return(await St(t)).map(({entry:a})=>a)}l(V,"readSubscriptionPool");async function St(t){const a=await y();return(await k(await W(),a,t)).map(({row:e,entry:n})=>({id:e.id,entry:n}))}l(St,"readSubscriptionPoolRows");async function qt(t,a){return L(t,async e=>{const{db:n,person:i,rows:r}=e,s=a.map(d=>F(t,d)),o=s.map((d,u)=>({entry:d,id:r[u]?.row.id??x()})),c=await S(n,i,t);await n.prepare("DELETE FROM model_accounts WHERE person=? AND provider_id=?").run(i,t),await U(n,i,t);for(const[d,{entry:u,id:m}]of o.entries()){await C(n,i,t,{providerId:t,id:m,label:u.label,labelKey:_(u.label),position:d,lastAttemptAt:u.lastAttemptAt,lastRateLimitedAt:u.lastRateLimitedAt,rateLimitedUntil:u.rateLimitedUntil,...u.authFailure?{authFailure:u.authFailure}:{}},E(u));for(const[f,A]of Object.entries(u.modelCooldowns??{}))await ot(n,i,t,m,f,A,c.get(m)?.find(D=>D.family===f)?.lastRateLimitedAt??u.lastRateLimitedAt)}return s})}l(qt,"writeSubscriptionPool");function H(t,a){const e=a.trim().toLowerCase(),n=Number.parseInt(e,10);return Number.isFinite(n)&&String(n)===e&&n>=1&&n<=t.length?n-1:t.findIndex(({entry:i})=>_(i.label)===e)}l(H,"resolve");const P=l((t,a)=>new O(`This ${h(t)} account is already added as \u201C${a}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`),"duplicateAccount");async function Tt(t,a){const e=F(t,a);return L(t,async n=>{const{db:i,person:r,rows:s}=n;if(s.some(({entry:d})=>_(d.label)===_(e.label)))throw new O(`${h(t)} subscription already exists: ${e.label}`);const o=p(e),c=s.find(({entry:d})=>p(d)===o);if(o&&c)throw P(t,c.entry.label);return await C(i,r,t,{providerId:t,id:x(),label:e.label,labelKey:_(e.label),position:s.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}},E(e)),R(n,t)})}l(Tt,"addSubscription");async function Xt(t,a){const e=F(t,a);return(await V(t)).find(i=>_(i.label)===_(e.label))?Ut(t,e):Tt(t,e)}l(Xt,"upsertSubscription");async function Ut(t,a){const e=F(t,a);return L(t,async n=>{const{db:i,person:r,rows:s}=n,o=s.findIndex(({entry:f})=>_(f.label)===_(e.label)),c=s[o],d=s.find(({entry:f},A)=>A!==o&&p(f)===p(e));if(p(e)&&d)throw P(t,d.entry.label);if(!c)return await C(i,r,t,{providerId:t,id:x(),label:e.label,labelKey:_(e.label),position:s.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil},E(e)),R(n,t);const u=c.row,m=e.lastRateLimitedAt>=u.lastRateLimitedAt;return await M(i,r,t,u.id,E(e)),await b(i,r,{...u,authFailure:void 0,label:e.label,labelKey:_(e.label),lastAttemptAt:Math.max(u.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:u.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:u.rateLimitedUntil}),v(c.entry,e)&&await U(i,r,t,u.id),R(n,t)})}l(Ut,"upsertSubscriptionWithUniqueAccount");async function Yt(t,a,e){return L(t,async n=>{const{db:i,person:r,rows:s}=n,o=H(s,a);if(o<0)throw new Error(`Unknown ${h(t)} subscription: ${a}`);const c=e.trim();if(!c)throw new Error(`${h(t)} subscription label cannot be empty`);if(s.some((d,u)=>u!==o&&_(d.entry.label)===_(c)))throw new O(`${h(t)} subscription already exists: ${c}`);return await b(i,r,{...s[o].row,label:c,labelKey:_(c)}),R(n,t)})}l(Yt,"renameSubscription");async function G(t,a,e){return L(t,async n=>{const{db:i,person:r,rows:s}=n,o=a(s);if(o<0)throw new Error(`Unknown ${h(t)} subscription: ${e}`);const c=s[o].row;return await i.prepare("DELETE FROM model_accounts WHERE person=? AND provider_id=? AND id=?").run(r,t,c.id),await U(i,r,t,c.id),R(n,t)})}l(G,"remove");async function Dt(t,a){return G(t,e=>H(e,a),a)}l(Dt,"removeManagedAccount");async function Jt(t,a){return G(t,e=>e.findIndex(({row:n})=>n.id===a),a)}l(Jt,"removeManagedAccountById");async function Qt(t,a){return G(t,e=>H(e,a),a)}l(Qt,"removeSubscription");async function $t(t){return L(t,async({db:a,person:e,rows:n})=>(await a.prepare("DELETE FROM model_accounts WHERE person=? AND provider_id=?").run(e,t),await U(a,e,t),n.length))}l($t,"removeAllManagedAccounts");async function Zt(t,a){return a===void 0?(await $t(t),[]):Dt(t,a)}l(Zt,"removeManagedProvider");async function vt(t,a){return L(t,async e=>{const{db:n,person:i,rows:r}=e,s=H(r,a);if(s<0)throw new Error(`Unknown ${h(t)} subscription: ${a}`);const o=[r[s],...r.filter((c,d)=>d!==s)];for(const[c,d]of o.entries())await b(n,i,{...d.row,position:c});return R(e,t)})}l(vt,"promoteSubscription");async function z(t,a,e){const n=await y();return await N(async i=>{await T(i);const r=await st(i,n,t,a);if(!r)throw new Error(`Unknown ${h(t)} subscription: ${a}`);await e(i,n,r.row)}),V(t)}l(z,"healthWrite");async function te(t,a,e=Date.now()){return z(t,a,(n,i,r)=>b(n,i,{...r,lastAttemptAt:Math.max(r.lastAttemptAt,e)}))}l(te,"markSubscriptionAttempt");async function ee(t,a,e,n=Date.now(),i=Date.now(),r={kind:"account"}){return e=Math.min(Math.max(0,e),B),z(t,a,async(s,o,c)=>{if(r.kind==="account"){if(i<c.lastRateLimitedAt)return;await b(s,o,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,n),lastRateLimitedAt:i,rateLimitedUntil:i+e});return}await b(s,o,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,n)});const d=(await S(s,o,t)).get(c.id)?.find(u=>u.family===r.family);d&&i<d.lastRateLimitedAt||await ot(s,o,t,c.id,r.family,i+e,i)})}l(ee,"markSubscriptionRateLimited");async function ae(t,a,e=Date.now(),n){const i=wt(n);return z(t,a,async(r,s,o)=>{if(e>=o.lastRateLimitedAt&&await b(r,s,{...o,authFailure:void 0,lastAttemptAt:Math.max(o.lastAttemptAt,e),rateLimitedUntil:0}),i===void 0)return;const c=(await S(r,s,t)).get(o.id)?.find(d=>d.family===i);c&&e>=c.lastRateLimitedAt&&await r.prepare("DELETE FROM model_account_cooldowns WHERE person=? AND provider_id=? AND account_id=? AND family=?").run(s,t,o.id,i)})}l(ae,"markSubscriptionSuccess");async function ut(t,a,e){const n=Date.now();await b(t,a,{...e,authFailure:"invalid_grant",lastAttemptAt:Math.max(e.lastAttemptAt,n),rateLimitedUntil:Math.max(e.rateLimitedUntil,n+Rt)})}l(ut,"recordInvalidGrant");async function ne(t,a,e){const n=await y();let i;try{i=await N(async r=>{await T(r);const s=await st(r,n,t,a);if(!s)throw new Error(`${h(t)} subscription "${a}" no longer exists`);if(s.entry.expires>Date.now())return{entry:s.entry};if(s.row.authFailure)throw new Error(`${h(t)} subscription "${s.entry.label}" refresh refused with status=400 invalid_grant; it needs a new sign-in`);let o;try{o=await ct(t,e(s.entry.refresh,s.entry.clientId))}catch(d){if(!Y(d))throw d;return await ut(r,n,s.row),{refused:d}}const c=tt(et(t,s.entry.label,o),o.clientId??s.entry.clientId);return await M(r,n,t,s.row.id,E({...s.entry,...c})),{entry:{...s.entry,...c}}})}catch(r){throw lt(r)?new nt(t,a):r}if("refused"in i)throw i.refused;return i.entry}l(ne,"refreshSubscriptionCredential");async function ie(t,a,e){const n=await y();let i;try{i=await N(async r=>{await T(r);const s=await k(r,n,t,!0),o=new Set;let c;for(;o.size<s.length;){const d=a(s.filter(({entry:m})=>!o.has(m.label)).map(({entry:m})=>m));if(!d)break;const u=s.find(({entry:m})=>m.label===d.label);o.add(u.entry.label);try{const m=await ct(t,e(u.entry));if(!m)return{entry:u.entry};const f=tt(et(t,u.entry.label,m),m.clientId??u.entry.clientId);return await M(r,n,t,u.row.id,E({...u.entry,...f})),{entry:{...u.entry,...f}}}catch(m){if(!Y(m))throw m;c=m,await ut(r,n,u.row)}}return c?{refused:c}:{}})}catch(r){throw lt(r)?new nt(t,a([])?.label??t):r}if("refused"in i)throw i.refused;return i.entry}l(ie,"modifyElectedSubscription");async function Ot(t,a){const e=a.accountId?.trim(),n=a.email?.trim();if(t!==Lt)return{...e?{accountId:e}:{},...n?{email:n}:{}};try{const i=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${a.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!i.ok)return e?{accountId:e}:{};const r=await i.json(),s=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,o=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...s?{accountId:s}:{},...o?{email:o}:{}}}catch{return e?{accountId:e}:{}}}l(Ot,"enrichManagedLoginIdentity");async function re(t,a,e){const n=await Ot(t,e);return L(t,async i=>{const{db:r,person:s,rows:o}=i,c=e.autoLabel?void 0:a?.trim(),d=p({refresh:e.refresh,accountId:n.accountId}),u=(c?o.find(({entry:w})=>_(w.label)===_(c)):void 0)??o.find(({entry:w})=>p(w)===d);let m=u?.entry.label??c;if(!m&&e.autoLabel){const w=n.email||a?.trim()||`Account ${o.length+1}`;m=w;for(let g=2;o.some(({entry:ft})=>_(ft.label)===_(m));g++)m=`${w} (${g})`}if(!m)throw new Error(`${h(t)} subscription label cannot be empty`);const f=F(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...n.accountId?{accountId:n.accountId}:{},...e.clientId?{clientId:e.clientId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!u){if(o.some(({entry:g})=>_(g.label)===_(f.label)))throw new O(`${h(t)} subscription already exists: ${f.label}`);const w=o.find(({entry:g})=>p(g)===p(f));if(p(f)&&w)throw P(t,w.entry.label);return await C(r,s,t,{providerId:t,id:x(),label:f.label,labelKey:_(f.label),position:o.length,lastAttemptAt:f.lastAttemptAt,lastRateLimitedAt:f.lastRateLimitedAt,rateLimitedUntil:f.rateLimitedUntil},E(f)),R(i,t)}const A=u.row,D=o.find(({entry:w})=>w.label!==u.entry.label&&p(w)===p(f));if(p(f)&&D)throw P(t,D.entry.label);const q=f.lastRateLimitedAt>=A.lastRateLimitedAt;return await M(r,s,t,A.id,E(f)),await b(r,s,{...A,authFailure:void 0,lastAttemptAt:Math.max(A.lastAttemptAt,f.lastAttemptAt),lastRateLimitedAt:q?f.lastRateLimitedAt:A.lastRateLimitedAt,rateLimitedUntil:q?0:A.rateLimitedUntil}),v(u.entry,f)&&await U(r,s,t,A.id),R(i,t)})}l(re,"commitManagedLogin");const dt=new Map;function mt(t){return{defaultFallbackStrength:Q(t?.default_fallback_strength)??J,revertWhenAvailable:t?.revert_when_available===void 0||t.revert_when_available===null?!0:!!t.revert_when_available}}l(mt,"rotationFromRow");async function kt(){const t=await y(),a=mt(await(await W()).prepare("SELECT default_fallback_strength,revert_when_available FROM model_settings WHERE person=?").get(t));return dt.set(t,a),a}l(kt,"loadRotationConfig"),yt(()=>Ct());async function Ct(){const t=pt();return t?t.rotation:kt()}l(Ct,"readRotationConfig");async function se(t){const a=await y(),e=await N(async n=>{await T(n),await n.prepare("INSERT INTO model_settings(person) VALUES(?) ON CONFLICT(person) DO NOTHING").run(a);const i=mt(await n.prepare("SELECT default_fallback_strength,revert_when_available FROM model_settings WHERE person=? FOR UPDATE").get(a)),r={defaultFallbackStrength:Q(t.defaultFallbackStrength)??i.defaultFallbackStrength??J,revertWhenAvailable:t.revertWhenAvailable??i.revertWhenAvailable??!0};return await n.prepare("UPDATE model_settings SET default_fallback_strength=?,revert_when_available=?,revision=revision+1 WHERE person=?").run(r.defaultFallbackStrength,r.revertWhenAvailable?1:0,a),r});return dt.set(a,e),e}l(se,"writeRotationConfig");async function oe(){const t=await y();return(await(await W()).prepare("INSERT INTO model_settings(person,openai_device_id) VALUES(?,?) ON CONFLICT(person) DO UPDATE SET openai_device_id=COALESCE(model_settings.openai_device_id,EXCLUDED.openai_device_id) RETURNING openai_device_id").get(t,x())).openai_device_id}l(oe,"openAIDeviceId");async function le(t,a=Date.now(),e){const n=await V(t),i=n.filter(s=>s.authFailure===void 0&&Et(s,e)<=a),r=h(t);return i.length===0?`${r} pool exhausted`:`${r} ${i[0]?.label??t} (${i.length}/${n.length} ready)`}l(le,"formatStatusLine");export{at as REFRESH_EXCHANGE_TIMEOUT_MS,nt as RefreshBusyError,Nt as RefreshTimeoutError,Tt as addSubscription,re as commitManagedLogin,Ot as enrichManagedLoginIdentity,le as formatStatusLine,C as insertAccount,kt as loadRotationConfig,te as markSubscriptionAttempt,ee as markSubscriptionRateLimited,ae as markSubscriptionSuccess,ie as modifyElectedSubscription,oe as openAIDeviceId,vt as promoteSubscription,Ct as readRotationConfig,V as readSubscriptionPool,St as readSubscriptionPoolRows,ne as refreshSubscriptionCredential,$t as removeAllManagedAccounts,Dt as removeManagedAccount,Jt as removeManagedAccountById,Zt as removeManagedProvider,Qt as removeSubscription,Yt as renameSubscription,ot as upsertModelCooldown,Xt as upsertSubscription,Ut as upsertSubscriptionWithUniqueAccount,se as writeRotationConfig,qt as writeSubscriptionPool};
|
|
1
|
+
var _t=Object.defineProperty;var l=(t,a)=>_t(t,"name",{value:a,configurable:!0});import{createHash as ht,randomUUID as x}from"node:crypto";import{brokerConfigSnapshot as pt}from"./config.js";import{openDb as W,withCanvasWrite as N}from"./canvas/db.js";import{personIdAsync as y}from"./canvas/person.js";import{loadSecretsKey as K,open as X,seal as $}from"./secrets/index.js";import{modelCooldownFamily as wt}from"./rate-limit-scope.js";import{REFRESH_BUSY_PHRASE as At,REFRESH_TIMEOUT_PHRASE as bt}from"./provider-error-recognition.js";import{registerRotationReader as yt}from"./model-routes.js";import{coolingUntilFor as Et,getProviderLabel as h,isInvalidRefreshTokenError as Y,DuplicateSubscriptionError as O,ANTHROPIC_PROVIDER_ID as Lt,MAX_RATE_LIMIT_COOLDOWN_MS as B,INVALID_REFRESH_TOKEN_BACKOFF_MS as Rt}from"./managed-providers.js";export*from"./managed-providers.js";const J="strong";function Q(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}l(Q,"normalizeStrength");function _(t){return t.toLowerCase()}l(_,"key");function j(t){return typeof t=="string"&&t.trim()?t.trim():void 0}l(j,"optionalString");function gt(t){const a=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",n=typeof t.access=="string"?t.access.trim():"",i=Number(t.expires);if(!a||!e||!n||!Number.isFinite(i))return;const r=j(t.accountId),s=j(t.clientId);return{label:a,refresh:e,access:n,expires:i,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...r?{accountId:r}:{},...s?{clientId:s}:{},...Z(t.modelCooldowns)?{modelCooldowns:Z(t.modelCooldowns)}:{}}}l(gt,"credential");function Z(t){if(!t||typeof t!="object"||Array.isArray(t))return;const a=Object.entries(t).filter(([e,n])=>e.trim()!==""&&Number.isFinite(Number(n))).map(([e,n])=>[e,Number(n)]);return a.length?Object.fromEntries(a):void 0}l(Z,"modelCooldowns");function F(t,a){if(!a.label?.trim())throw new Error(`${h(t)} subscription label cannot be empty`);const e=gt(a);if(!e)throw new Error(`Refusing to persist incomplete ${h(t)} subscription "${a.label.trim()}"; pool rows require both OAuth tokens.`);return e}l(F,"assertCredential");function v(t,a){const e=t.accountId?.trim(),n=a.accountId?.trim();return!!(e&&n&&e!==n)}l(v,"differentAccount");function p(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${ht("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}l(p,"identity");function tt(t,a){const e=j(a);return e?{...t,clientId:e}:t}l(tt,"withClientId");function et(t,a,e){const n=e.refresh?.trim()??"",i=e.access?.trim()??"",r=Number(e.expires);if(!n||!i||!Number.isFinite(r))throw new Error(`${h(t)} subscription "${a}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:n,access:i,expires:r}}l(et,"assertTokens");const at=1e4,It="15s",xt="55P03";class nt extends Error{static{l(this,"RefreshBusyError")}constructor(a,e){super(`${h(a)} subscription "${e}" ${At}; treating it as cooling for this call`),this.name="RefreshBusyError"}}class Nt extends Error{static{l(this,"RefreshTimeoutError")}constructor(a){super(`${h(a)} ${bt} ${at} ms`),this.name="RefreshTimeoutError"}}const it="id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure,refresh_sealed,access_sealed,expires,vendor_account_id,client_id,key_id",I=l((t,a,e,n)=>`model_accounts|${t}|${a}|${e}|${n}`,"aadOf");function E(t){return{refresh:t.refresh,access:t.access,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{}}}l(E,"stored");function Ft(t,a){return{providerId:t,id:a.id,label:a.label,labelKey:a.label_key,position:a.position,lastAttemptAt:a.last_attempt_at,lastRateLimitedAt:a.last_rate_limited_at,rateLimitedUntil:a.rate_limited_until,...a.auth_failure?{authFailure:a.auth_failure}:{}}}l(Ft,"rowOf");function rt(t,a,e,n=[]){if(!e.refresh_sealed||!e.access_sealed||e.expires===null||!e.key_id)throw new Error(`managed ${a} account ${e.id} has no credential record`);const i=K(),r=X(e.refresh_sealed,e.key_id,i,I(t,a,e.id,"refresh")),s=X(e.access_sealed,e.key_id,i,I(t,a,e.id,"access")),o=Ft(a,e),c={label:o.label,refresh:r,access:s,expires:e.expires,rateLimitedUntil:o.authFailure||!o.lastRateLimitedAt?o.rateLimitedUntil:Math.min(o.rateLimitedUntil,o.lastRateLimitedAt+B),lastAttemptAt:o.lastAttemptAt,lastRateLimitedAt:o.lastRateLimitedAt,...o.authFailure?{authFailure:o.authFailure}:{},...e.vendor_account_id?{accountId:e.vendor_account_id}:{},...e.client_id?{clientId:e.client_id}:{},...n.length?{modelCooldowns:Object.fromEntries(n.map(d=>[d.family,d.lastRateLimitedAt?Math.min(d.rateLimitedUntil,d.lastRateLimitedAt+B):d.rateLimitedUntil]))}:{}};return{row:o,entry:c}}l(rt,"toPublic");async function S(t,a,e){const n=new Map,i=await t.prepare("SELECT account_id,family,rate_limited_until,last_rate_limited_at FROM model_account_cooldowns WHERE person=? AND provider_id=? ORDER BY family").all(a,e);for(const r of i){const s=n.get(r.account_id)??[];s.push({family:r.family,rateLimitedUntil:r.rate_limited_until,lastRateLimitedAt:r.last_rate_limited_at}),n.set(r.account_id,s)}return n}l(S,"loadCooldowns");async function k(t,a,e,n=!1){const i=await t.prepare(`SELECT ${it} FROM model_accounts WHERE person=? AND provider_id=? ORDER BY position${n?" FOR UPDATE":""}`).all(a,e),r=await S(t,a,e);return i.map(s=>rt(a,e,s,r.get(s.id)))}l(k,"loadPool");async function st(t,a,e,n){const i=n.trim().toLowerCase(),r=Number.parseInt(i,10),s=Number.isFinite(r)&&String(r)===i&&r>=1,o=await t.prepare(`SELECT ${it} FROM model_accounts WHERE person=? AND provider_id=? AND ${s?"position=?":"label_key=?"} FOR UPDATE`).get(a,e,s?r-1:i);if(o)return rt(a,e,o,(await S(t,a,e)).get(o.id))}l(st,"lockedAccount");async function T(t){await t.exec(`SET LOCAL lock_timeout = '${It}'`)}l(T,"setLockTimeout");async function L(t,a){const e=await y();return N(async n=>(await T(n),await n.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(`model_accounts|${e}|${t}`),a({db:n,person:e,rows:await k(n,e,t,!0)})))}l(L,"locked");async function C(t,a,e,n,i){const r=K(),s=$(i.refresh,r,I(a,e,n.id,"refresh")),o=$(i.access,r,I(a,e,n.id,"access"));await t.prepare("INSERT INTO model_accounts(person,provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure,refresh_sealed,access_sealed,expires,vendor_account_id,client_id,key_id) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)").run(a,e,n.id,n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,s.sealed,o.sealed,i.expires,i.accountId??null,i.clientId??null,s.keyId)}l(C,"insertAccount");async function M(t,a,e,n,i){const r=K(),s=$(i.refresh,r,I(a,e,n,"refresh")),o=$(i.access,r,I(a,e,n,"access"));await t.prepare("UPDATE model_accounts SET refresh_sealed=?,access_sealed=?,expires=?,vendor_account_id=?,client_id=?,key_id=? WHERE person=? AND provider_id=? AND id=?").run(s.sealed,o.sealed,i.expires,i.accountId??null,i.clientId??null,s.keyId,a,e,n)}l(M,"setCredential");async function b(t,a,e){await t.prepare("UPDATE model_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE person=? AND provider_id=? AND id=?").run(e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null,a,e.providerId,e.id)}l(b,"updateHealth");async function ot(t,a,e,n,i,r,s){await t.prepare("INSERT INTO model_account_cooldowns(person,provider_id,account_id,family,rate_limited_until,last_rate_limited_at) VALUES(?,?,?,?,?,?) ON CONFLICT(person,provider_id,account_id,family) DO UPDATE SET rate_limited_until=excluded.rate_limited_until,last_rate_limited_at=excluded.last_rate_limited_at").run(a,e,n,i,r,s)}l(ot,"upsertModelCooldown");async function U(t,a,e,n){n===void 0?await t.prepare("DELETE FROM model_account_cooldowns WHERE person=? AND provider_id=?").run(a,e):await t.prepare("DELETE FROM model_account_cooldowns WHERE person=? AND provider_id=? AND account_id=?").run(a,e,n)}l(U,"deleteModelCooldowns");const R=l(async({db:t,person:a},e)=>(await k(t,a,e)).map(({entry:n})=>n),"publicPool"),lt=l(t=>t?.code===xt,"lockWaitExpired");function ct(t,a){let e;return Promise.race([a,new Promise((n,i)=>{e=setTimeout(()=>i(new Nt(t)),at)})]).finally(()=>clearTimeout(e))}l(ct,"withExchangeTimeout");async function V(t){return(await St(t)).map(({entry:a})=>a)}l(V,"readSubscriptionPool");async function St(t){const a=await y();return(await k(await W(),a,t)).map(({row:e,entry:n})=>({id:e.id,entry:n}))}l(St,"readSubscriptionPoolRows");async function qt(t,a){return L(t,async e=>{const{db:n,person:i,rows:r}=e,s=a.map(d=>F(t,d)),o=s.map((d,u)=>({entry:d,id:r[u]?.row.id??x()})),c=await S(n,i,t);await n.prepare("DELETE FROM model_accounts WHERE person=? AND provider_id=?").run(i,t),await U(n,i,t);for(const[d,{entry:u,id:m}]of o.entries()){await C(n,i,t,{providerId:t,id:m,label:u.label,labelKey:_(u.label),position:d,lastAttemptAt:u.lastAttemptAt,lastRateLimitedAt:u.lastRateLimitedAt,rateLimitedUntil:u.rateLimitedUntil,...u.authFailure?{authFailure:u.authFailure}:{}},E(u));for(const[f,A]of Object.entries(u.modelCooldowns??{}))await ot(n,i,t,m,f,A,c.get(m)?.find(D=>D.family===f)?.lastRateLimitedAt??u.lastRateLimitedAt)}return s})}l(qt,"writeSubscriptionPool");function H(t,a){const e=a.trim().toLowerCase(),n=Number.parseInt(e,10);return Number.isFinite(n)&&String(n)===e&&n>=1&&n<=t.length?n-1:t.findIndex(({entry:i})=>_(i.label)===e)}l(H,"resolve");const P=l((t,a)=>new O(`This ${h(t)} account is already added as \u201C${a}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`),"duplicateAccount");async function Tt(t,a){const e=F(t,a);return L(t,async n=>{const{db:i,person:r,rows:s}=n;if(s.some(({entry:d})=>_(d.label)===_(e.label)))throw new O(`${h(t)} subscription already exists: ${e.label}`);const o=p(e),c=s.find(({entry:d})=>p(d)===o);if(o&&c)throw P(t,c.entry.label);return await C(i,r,t,{providerId:t,id:x(),label:e.label,labelKey:_(e.label),position:s.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}},E(e)),R(n,t)})}l(Tt,"addSubscription");async function Xt(t,a){const e=F(t,a);return(await V(t)).find(i=>_(i.label)===_(e.label))?Ut(t,e):Tt(t,e)}l(Xt,"upsertSubscription");async function Ut(t,a){const e=F(t,a);return L(t,async n=>{const{db:i,person:r,rows:s}=n,o=s.findIndex(({entry:f})=>_(f.label)===_(e.label)),c=s[o],d=s.find(({entry:f},A)=>A!==o&&p(f)===p(e));if(p(e)&&d)throw P(t,d.entry.label);if(!c)return await C(i,r,t,{providerId:t,id:x(),label:e.label,labelKey:_(e.label),position:s.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil},E(e)),R(n,t);const u=c.row,m=e.lastRateLimitedAt>=u.lastRateLimitedAt;return await M(i,r,t,u.id,E(e)),await b(i,r,{...u,authFailure:void 0,label:e.label,labelKey:_(e.label),lastAttemptAt:Math.max(u.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:u.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:u.rateLimitedUntil}),v(c.entry,e)&&await U(i,r,t,u.id),R(n,t)})}l(Ut,"upsertSubscriptionWithUniqueAccount");async function Yt(t,a,e){return L(t,async n=>{const{db:i,person:r,rows:s}=n,o=H(s,a);if(o<0)throw new Error(`Unknown ${h(t)} subscription: ${a}`);const c=e.trim();if(!c)throw new Error(`${h(t)} subscription label cannot be empty`);if(s.some((d,u)=>u!==o&&_(d.entry.label)===_(c)))throw new O(`${h(t)} subscription already exists: ${c}`);return await b(i,r,{...s[o].row,label:c,labelKey:_(c)}),R(n,t)})}l(Yt,"renameSubscription");async function G(t,a,e){return L(t,async n=>{const{db:i,person:r,rows:s}=n,o=a(s);if(o<0)throw new Error(`Unknown ${h(t)} subscription: ${e}`);const c=s[o].row;return await i.prepare("DELETE FROM model_accounts WHERE person=? AND provider_id=? AND id=?").run(r,t,c.id),await U(i,r,t,c.id),R(n,t)})}l(G,"remove");async function Dt(t,a){return G(t,e=>H(e,a),a)}l(Dt,"removeManagedAccount");async function Jt(t,a){return G(t,e=>e.findIndex(({row:n})=>n.id===a),a)}l(Jt,"removeManagedAccountById");async function Qt(t,a){return G(t,e=>H(e,a),a)}l(Qt,"removeSubscription");async function $t(t){return L(t,async({db:a,person:e,rows:n})=>(await a.prepare("DELETE FROM model_accounts WHERE person=? AND provider_id=?").run(e,t),await U(a,e,t),n.length))}l($t,"removeAllManagedAccounts");async function Zt(t,a){return a===void 0?(await $t(t),[]):Dt(t,a)}l(Zt,"removeManagedProvider");async function vt(t,a){return L(t,async e=>{const{db:n,person:i,rows:r}=e,s=H(r,a);if(s<0)throw new Error(`Unknown ${h(t)} subscription: ${a}`);const o=[r[s],...r.filter((c,d)=>d!==s)];for(const[c,d]of o.entries())await b(n,i,{...d.row,position:c});return R(e,t)})}l(vt,"promoteSubscription");async function z(t,a,e){const n=await y();return await N(async i=>{await T(i);const r=await st(i,n,t,a);if(!r)throw new Error(`Unknown ${h(t)} subscription: ${a}`);await e(i,n,r.row)}),V(t)}l(z,"healthWrite");async function te(t,a,e=Date.now()){return z(t,a,(n,i,r)=>b(n,i,{...r,lastAttemptAt:Math.max(r.lastAttemptAt,e)}))}l(te,"markSubscriptionAttempt");async function ee(t,a,e,n=Date.now(),i=Date.now(),r={kind:"account"}){return e=Math.min(Math.max(0,e),B),z(t,a,async(s,o,c)=>{if(r.kind==="account"){if(i<c.lastRateLimitedAt)return;await b(s,o,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,n),lastRateLimitedAt:i,rateLimitedUntil:i+e});return}await b(s,o,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,n)});const d=(await S(s,o,t)).get(c.id)?.find(u=>u.family===r.family);d&&i<d.lastRateLimitedAt||await ot(s,o,t,c.id,r.family,i+e,i)})}l(ee,"markSubscriptionRateLimited");async function ae(t,a,e=Date.now(),n){const i=wt(n);return z(t,a,async(r,s,o)=>{if(e>=o.lastRateLimitedAt&&await b(r,s,{...o,authFailure:void 0,lastAttemptAt:Math.max(o.lastAttemptAt,e),rateLimitedUntil:0}),i===void 0)return;const c=(await S(r,s,t)).get(o.id)?.find(d=>d.family===i);c&&e>=c.lastRateLimitedAt&&await r.prepare("DELETE FROM model_account_cooldowns WHERE person=? AND provider_id=? AND account_id=? AND family=?").run(s,t,o.id,i)})}l(ae,"markSubscriptionSuccess");async function ut(t,a,e){const n=Date.now();await b(t,a,{...e,authFailure:"invalid_grant",lastAttemptAt:Math.max(e.lastAttemptAt,n),rateLimitedUntil:Math.max(e.rateLimitedUntil,n+Rt)})}l(ut,"recordInvalidGrant");async function ne(t,a,e){const n=await y();let i;try{i=await N(async r=>{await T(r);const s=await st(r,n,t,a);if(!s)throw new Error(`${h(t)} subscription "${a}" no longer exists`);if(s.entry.expires>Date.now())return{entry:s.entry};if(s.row.authFailure)throw new Error(`${h(t)} subscription "${s.entry.label}" refresh refused with status=400 invalid_grant; it needs a new sign-in`);let o;try{o=await ct(t,e(s.entry.refresh,s.entry.clientId))}catch(d){if(!Y(d))throw d;return await ut(r,n,s.row),{refused:d}}const c=tt(et(t,s.entry.label,o),o.clientId??s.entry.clientId);return await M(r,n,t,s.row.id,E({...s.entry,...c})),{entry:{...s.entry,...c}}})}catch(r){throw lt(r)?new nt(t,a):r}if("refused"in i)throw i.refused;return i.entry}l(ne,"refreshSubscriptionCredential");async function ie(t,a,e){const n=await y();let i;try{i=await N(async r=>{await T(r);const s=await k(r,n,t,!0),o=new Set;let c;for(;o.size<s.length;){const d=a(s.filter(({entry:m})=>!o.has(m.label)).map(({entry:m})=>m));if(!d)break;const u=s.find(({entry:m})=>m.label===d.label);o.add(u.entry.label);try{const m=await ct(t,e(u.entry));if(!m)return{entry:u.entry};const f=tt(et(t,u.entry.label,m),m.clientId??u.entry.clientId);return await M(r,n,t,u.row.id,E({...u.entry,...f})),{entry:{...u.entry,...f}}}catch(m){if(!Y(m))throw m;c=m,await ut(r,n,u.row)}}return c?{refused:c}:{}})}catch(r){throw lt(r)?new nt(t,a([])?.label??t):r}if("refused"in i)throw i.refused;return i.entry}l(ie,"modifyElectedSubscription");async function Ot(t,a){const e=a.accountId?.trim(),n=a.email?.trim();if(t!==Lt)return{...e?{accountId:e}:{},...n?{email:n}:{}};try{const i=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${a.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!i.ok)return e?{accountId:e}:{};const r=await i.json(),s=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,o=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...s?{accountId:s}:{},...o?{email:o}:{}}}catch{return e?{accountId:e}:{}}}l(Ot,"enrichManagedLoginIdentity");async function re(t,a,e){const n=await Ot(t,e);return L(t,async i=>{const{db:r,person:s,rows:o}=i,c=e.autoLabel?void 0:a?.trim(),d=p({refresh:e.refresh,accountId:n.accountId}),u=(c?o.find(({entry:w})=>_(w.label)===_(c)):void 0)??o.find(({entry:w})=>p(w)===d);let m=u?.entry.label??c;if(!m&&e.autoLabel){const w=n.email||a?.trim()||`Account ${o.length+1}`;m=w;for(let g=2;o.some(({entry:ft})=>_(ft.label)===_(m));g++)m=`${w} (${g})`}if(!m)throw new Error(`${h(t)} subscription label cannot be empty`);const f=F(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...n.accountId?{accountId:n.accountId}:{},...e.clientId?{clientId:e.clientId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!u){if(o.some(({entry:g})=>_(g.label)===_(f.label)))throw new O(`${h(t)} subscription already exists: ${f.label}`);const w=o.find(({entry:g})=>p(g)===p(f));if(p(f)&&w)throw P(t,w.entry.label);return await C(r,s,t,{providerId:t,id:x(),label:f.label,labelKey:_(f.label),position:o.length,lastAttemptAt:f.lastAttemptAt,lastRateLimitedAt:f.lastRateLimitedAt,rateLimitedUntil:f.rateLimitedUntil},E(f)),R(i,t)}const A=u.row,D=o.find(({entry:w})=>w.label!==u.entry.label&&p(w)===p(f));if(p(f)&&D)throw P(t,D.entry.label);const q=f.lastRateLimitedAt>=A.lastRateLimitedAt;return await M(r,s,t,A.id,E(f)),await b(r,s,{...A,authFailure:void 0,lastAttemptAt:Math.max(A.lastAttemptAt,f.lastAttemptAt),lastRateLimitedAt:q?f.lastRateLimitedAt:A.lastRateLimitedAt,rateLimitedUntil:q?0:A.rateLimitedUntil}),v(u.entry,f)&&await U(r,s,t,A.id),R(i,t)})}l(re,"commitManagedLogin");const dt=new Map;function mt(t){return{defaultFallbackStrength:Q(t?.default_fallback_strength)??J,revertWhenAvailable:t?.revert_when_available===void 0||t.revert_when_available===null?!0:!!t.revert_when_available}}l(mt,"rotationFromRow");async function kt(){const t=await y(),a=mt(await(await W()).prepare("SELECT default_fallback_strength,revert_when_available FROM model_settings WHERE person=?").get(t));return dt.set(t,a),a}l(kt,"loadRotationConfig"),yt(()=>Ct());async function Ct(){const t=pt();return t?t.rotation:kt()}l(Ct,"readRotationConfig");async function se(t){const a=await y(),e=await N(async n=>{await T(n),await n.prepare("INSERT INTO model_settings(person) VALUES(?) ON CONFLICT(person) DO NOTHING").run(a);const i=mt(await n.prepare("SELECT default_fallback_strength,revert_when_available FROM model_settings WHERE person=? FOR UPDATE").get(a)),r={defaultFallbackStrength:Q(t.defaultFallbackStrength)??i.defaultFallbackStrength??J,revertWhenAvailable:t.revertWhenAvailable??i.revertWhenAvailable??!0};return await n.prepare("UPDATE model_settings SET default_fallback_strength=?,revert_when_available=?,revision=revision+1 WHERE person=?").run(r.defaultFallbackStrength,r.revertWhenAvailable?1:0,a),r});return dt.set(a,e),e}l(se,"writeRotationConfig");async function oe(){const t=await y();return(await(await W()).prepare("INSERT INTO model_settings(person,openai_device_id) VALUES(?,?) ON CONFLICT(person) DO UPDATE SET openai_device_id=COALESCE(model_settings.openai_device_id,EXCLUDED.openai_device_id) RETURNING openai_device_id").get(t,x())).openai_device_id}l(oe,"openAIDeviceId");async function le(t,a=Date.now(),e){const n=await V(t),i=n.filter(s=>s.authFailure===void 0&&Et(s,e)<=a),r=h(t);return i.length===0?`${r} pool exhausted`:`${r} ${i[0]?.label??t} (${i.length}/${n.length} ready)`}l(le,"formatStatusLine");export{at as REFRESH_EXCHANGE_TIMEOUT_MS,nt as RefreshBusyError,Nt as RefreshTimeoutError,I as aadOf,Tt as addSubscription,re as commitManagedLogin,Ot as enrichManagedLoginIdentity,le as formatStatusLine,C as insertAccount,kt as loadRotationConfig,te as markSubscriptionAttempt,ee as markSubscriptionRateLimited,ae as markSubscriptionSuccess,ie as modifyElectedSubscription,oe as openAIDeviceId,vt as promoteSubscription,Ct as readRotationConfig,V as readSubscriptionPool,St as readSubscriptionPoolRows,ne as refreshSubscriptionCredential,$t as removeAllManagedAccounts,Dt as removeManagedAccount,Jt as removeManagedAccountById,Zt as removeManagedProvider,Qt as removeSubscription,Yt as renameSubscription,ot as upsertModelCooldown,Xt as upsertSubscription,Ut as upsertSubscriptionWithUniqueAccount,se as writeRotationConfig,qt as writeSubscriptionPool};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var j=Object.defineProperty;var
|
|
1
|
+
var j=Object.defineProperty;var i=(r,e)=>j(r,"name",{value:e,configurable:!0});import{WebSocketServer as K}from"ws";import{isSafeNodeId as N,viewSocketPath as Q}from"../../core/canvas/paths.js";import{getNode as T}from"../../core/canvas/index.js";import{requestPersonId as z,runAsPerson as G}from"../../core/canvas/person-state.js";import{BROKER_READ_CAPS as X,CLIENT_READ_CAPS as Y,FrameDecoder as Z,FrameOverflowError as I}from"../../core/runtime/broker-protocol.js";import{emitEvent as E}from"../../core/events/emit.js";import{classify as $}from"../../core/fault-classifier.js";import{clearFault as g,markNodeRead as ee,recordFault as x}from"../../core/canvas/recovery.js";import{decideAttachRevive as re}from"../../core/runtime/revive.js";import{connectViewSocket as te,connectWssView as ne,probeViewSocket as ie,waitForBrokerViewSocket as ae,waitForWssView as P}from"../../core/runtime/view-socket.js";import{liveExecutionForNode as oe}from"../../core/canvas/executions.js";import{runnerForExecution as ce}from"../../core/runtime/runner/index.js";import{isBeneathRefusal as se}from"../../core/spaces/open-beneath.js";import{openMachineView as le,probeMachineView as fe,waitForMachineView as ue}from"../machine-link/forward.js";import{attachViewer as F,detachViewer as me,holdViewerForRedial as de,presentViewer as he}from"./viewer-presence.js";const pe=3e4,we=new K({noServer:!0,maxPayload:X.maxLineBytes}),ye=25e3,ve=6e4;function ke(r,e=ye,s=ve){let n=!0,h=!1,u=Date.now();const l=i(()=>{n=!0},"acknowledge"),a=i(()=>{h||(h=!0,clearInterval(o),r.off("pong",l))},"stop");r.on("pong",l);const o=setInterval(()=>{if(Date.now()-u>s){n=!0;return}if(!n){a(),r.terminate();return}n=!1;try{r.ping()}catch{a(),r.terminate()}},e);return o.unref(),{stop:a,noteActivity:i(()=>{u=Date.now()},"noteActivity")}}i(ke,"startRemoteAttachHeartbeat");function C(r){const e=Buffer.from(r,"utf8");return e.byteLength<=123?r:e.subarray(0,123).toString("utf8")}i(C,"clampReason");function y(r,e,s,n,h){const u=JSON.stringify({error:{code:n,message:h,origin:"runtime"}}),l=`HTTP/1.1 ${e} ${s}\r
|
|
2
2
|
content-type: application/json; charset=utf-8\r
|
|
3
3
|
content-length: ${Buffer.byteLength(u)}\r
|
|
4
4
|
connection: close\r
|
|
5
5
|
\r
|
|
6
|
-
`+u;try{r.end(l)}catch{try{r.destroy()}catch{}}}
|
|
6
|
+
`+u;try{r.end(l)}catch{try{r.destroy()}catch{}}}i(y,"refuseUpgrade");function ge(r){if(new URL(r.url??"/","http://127.0.0.1").searchParams.get("revive")==="0")return!0;const s=r.headers["x-crtr-attach-revive"];return(Array.isArray(s)?s[0]:s)==="0"}i(ge,"reviveOptedOut");function be(r){return new URL(r.url??"/","http://127.0.0.1").searchParams.get("wakeParked")==="1"}i(be,"wakeParkedRequested");function O(r){return r===1e3||r===1001||r===1005}i(O,"isCleanViewerClose");function p(r,e,s){s.catch(n=>E({level:"error",event:e,node_id:r,error:n}))}i(p,"detach");async function D(r){const e=await oe(r);return e===null||e.runner==="process"?{kind:"unix",path:Q(r)}:ce(e.runner).viewerAddress(e)}i(D,"brokerViewAddress");function _e(r,e){return e.kind==="machine"?le(e.environmentId,r):e.kind==="wss"?ne(e.url,e.headers):te(r)}i(_e,"connectBrokerView");function Ee(r,e,s){return e.kind==="machine"?fe(e.environmentId,r,s):e.kind==="wss"?P(e.url,e.headers,void 0,s):ie(r,s)}i(Ee,"probeBrokerView");async function Ae(r,e,s,n){const h=[];let u=i(t=>{h.push(t)},"relayMessage");if(r.on("message",t=>u(t)),!N(e)){try{r.close(1008,C(`invalid node id: ${e}`))}catch{}return}if(await T(e)===null){try{r.close(1011,C(`no node ${e}`))}catch{}return}await g(e,{link:"viewer\u2194crtrd"});const l=_e(e,s),a=new Z(Y);let o=!1,v=!1,d,b=!1,V=!1,A="controller",B=Promise.resolve(),_=!1;const S=[];let R=!1;l.on("connect",()=>{b=!0,!o&&_&&p(e,"daemon.bridge.presence_failed",F(e,r)),p(e,"daemon.bridge.connect_failed",g(e,{link:"relay\u2194broker"}))});const w=i((t,f,c=!1)=>{if(!o){if(o=!0,p(e,"daemon.bridge.presence_failed",c?de(e,r):me(e,r)),d?.stop(),!l.destroyed)try{l.destroy()}catch{}try{r.close(t,C(f))}catch{}}},"teardown");d=ke(r);const M=i(async t=>{d?.noteActivity();let f;try{f=a.push(t)}catch(c){const m=c instanceof I?`broker sent an oversized frame (${c.message})`:`failed to decode a broker frame for ${e}`;w(1009,m);return}S.push(...f),R||await U()},"onData"),U=i(async()=>{R=!0;try{for(;S.length>0&&!o;){const t=S.splice(0);let f;try{f=await n(A)}catch(m){E({level:"error",event:"daemon.bridge.attach_recheck_failed",node_id:e,error:m}),w(1011,"revoked");return}if(o)return;if(!f){w(1008,"revoked");return}let c=!1;for(const m of t)r.readyState===r.OPEN&&(r.send(JSON.stringify(m)),m?.type==="welcome"&&(c=!0));if(_&&c)try{await he(e,r)&&await ee(e)}catch(m){E({level:"warn",event:"daemon.bridge.present_failed",node_id:e,error:m})}}}finally{R=!1}},"drainOutbound");l.on("data",t=>p(e,"daemon.bridge.data_failed",M(t)));const W=i(async t=>{const f=$("relay\u2194broker",t);await x(e,{link:"relay\u2194broker",op:"bridge socket",kind:f.kind,retry:{disposition:"manual"},message:t.message});const c=t.code==="ECONNREFUSED"||t.code==="ENOENT"?`no running broker for ${e}`:`view socket error for ${e}: ${t.message}`;w(1011,c,!0)},"onSocketError");l.on("error",t=>p(e,"daemon.bridge.socket_error_failed",W(t)));const H=i(async t=>{o||t||(await g(e,{link:"relay\u2194broker"}),w(1e3,"broker socket closed",!0))},"onSocketClose");l.on("close",t=>p(e,"daemon.bridge.socket_close_failed",H(t)));const L=i(async(t,f)=>{const c=f.toString("utf8");if(o){O(t)&&await g(e,{link:"viewer\u2194crtrd"});return}if(O(t))await g(e,{link:"viewer\u2194crtrd"});else if(!v){v=!0;const m=$("viewer\u2194crtrd",{code:t,reason:c});await x(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:m.kind,retry:{disposition:"auto"},message:c||`remote attach closed (${t})`})}w(t,c||`remote attach closed (${t})`)},"onWsClose");r.on("close",(t,f)=>p(e,"daemon.bridge.ws_close_failed",L(t,f)));const J=i(async()=>{if(!v){v=!0;const t=$("viewer\u2194crtrd",{code:1006,reason:"remote attach websocket error"});await x(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:t.kind,retry:{disposition:"auto"},message:"remote attach websocket error"})}w(1011,"remote attach websocket error")},"onWsError");r.on("error",()=>p(e,"daemon.bridge.ws_error_failed",J())),u=i(t=>{if(o||l.destroyed)return;d?.noteActivity();const c=(Array.isArray(t)?Buffer.concat(t):Buffer.isBuffer(t)?t:Buffer.from(new Uint8Array(t))).toString("utf8");if(!V)try{const k=JSON.parse(c);k?.type==="hello"&&(V=!0,A=k.role==="observer"?"observer":"controller",_=k.attends!==!1,_&&b&&p(e,"daemon.bridge.presence_failed",F(e,r)))}catch{}const m=c.endsWith(`
|
|
7
7
|
`)?c:c+`
|
|
8
|
-
`,q=
|
|
8
|
+
`,q=i(()=>{try{l.write(m)}catch{}},"write");B=B.then(async()=>{o||(await n(A)?q():w(1008,"revoked"))}).catch(k=>{E({level:"error",event:"daemon.bridge.attach_recheck_failed",node_id:e,error:k}),w(1011,"revoked")})},"relayMessage");for(const t of h.splice(0))u(t)}i(Ae,"bridgeConnection");async function He(r,e,s,n,h){const u=z(),l=u===void 0?h:a=>G(u,()=>h(a));e.on("error",()=>{});try{if(!N(n)){y(e,400,"Bad Request","invalid_node_id",`invalid node id: ${n}`);return}if(await T(n)===null){y(e,404,"Not Found","node_not_found",`no node ${n}`);return}let a;if(ge(r)){if(a=await D(n),!await Ee(n,a,250)){y(e,409,"Conflict","node_dormant",`node ${n} is dormant and revive was opted out (?revive=0)`);return}}else{let o;try{const d=await re(n,{resume:!0,capacity:"refuse"},be(r));if(d.parked){y(e,409,"Conflict","node_dormant",`node ${n} is parked; attach with ?wakeParked=1 to wake it`);return}o=d.result.launch?.exited}catch(d){const b=d instanceof Error?d.message:String(d);y(e,409,"Conflict","attach_revive_failed",`attach revive failed: ${b}`);return}if(a=await D(n),!(a.kind==="machine"?await ue(a.environmentId,n,o,pe):a.kind==="wss"?await P(a.url,a.headers,o):await ae(n,o))){y(e,409,"Conflict","broker_not_ready",`broker view socket for ${n} did not become listenable after revive`);return}}if(e.destroyed)return;we.handleUpgrade(r,e,s,o=>p(n,"daemon.bridge.connection_failed",Ae(o,n,a,l)))}catch(a){const o=a instanceof Error?a.message:String(a);if(se(a)){y(e,400,"Bad Request","invalid_request",o);return}y(e,500,"Internal Server Error","internal",`attach upgrade failed: ${o}`)}}i(He,"handleAttachUpgrade");export{ye as REMOTE_ATTACH_HEARTBEAT_MS,ve as REMOTE_ATTACH_QUIET_MS,He as handleAttachUpgrade,O as isCleanViewerClose,y as refuseUpgrade,ke as startRemoteAttachHeartbeat};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var l=Object.defineProperty;var i=(r,e)=>l(r,"name",{value:e,configurable:!0});import{getNode as v}from"../../../core/canvas/canvas.js";import{decideAttachRevive as k}from"../../../core/runtime/revive.js";import{viewSocketPath as p}from"../../../core/canvas/paths.js";import{waitForBrokerViewSocket as b}from"../../../core/runtime/placement-tmux.js";import{liveExecutionForNode as y}from"../../../core/canvas/executions.js";import{runnerForExecution as P}from"../../../core/runtime/runner/index.js";import{waitForMachineView as A}from"../../machine-link/forward.js";import{general as N,notFound as w,usage as u}from"../../../core/errors.js";import{mayOnNode as d,ownerOnly as _}from"../router.js";const g=3e4;function x(r){if(r==null)return{resume:!0,revive:!0,wakeParked:!0};if(typeof r!="object")throw u("attach body must be a JSON object { resume?, revive?, wakeParked? }");const e=r.resume,t=r.revive,o=r.wakeParked;if(e!==void 0&&typeof e!="boolean")throw u("resume must be a boolean");if(t!==void 0&&typeof t!="boolean")throw u("revive must be a boolean");if(o!==void 0&&typeof o!="boolean")throw u("wakeParked must be a boolean");return{resume:e??!0,revive:t??!0,wakeParked:o??!0}}i(x,"parseAttachBody");async function E(r,e,t){return e==="controller"?await d(r,"message",t):await d(r,"read",t)?!0:e==="either"&&await d(r,"message",t)}i(E,"mayAttachNode");async function F(r){const e=r.params.id;if(await v(e)===null)throw w(`unknown node: ${e}`,{received:e});if(!await E(r.principal,"either",e))throw w(`unknown node: ${e}`,{received:e});const{resume:t,revive:o,wakeParked:h}=x(r.body);let c=!1,f=!1,m=!1;if(o){const a=await k(e,{resume:t,capacity:"refuse"},h);if(m=a.parked,!a.parked){c=a.result.launch!==void 0,f=a.result.resumed;const n=await y(e);if(n!==null&&n.runner==="machine"){const s=await P(n.runner).viewerAddress(n);if(s.kind!=="machine")throw new Error(`the machine execution for ${e} resolved a ${s.kind} viewer address`);if(!await A(s.environmentId,e,a.result.launch?.exited,g))throw N(`the broker for ${e} on its machine did not accept a viewer after revive`)}else await b(e)}}return{status:200,body:{node_id:e,socket_path:p(e),revived:c,resumed:f,parked:m}}}i(F,"handleAttach");const C=_([{method:"POST",pattern:"/v1/nodes/:id/attach",handler:F}]);export{C as attachRoutes,E as mayAttachNode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var X=Object.defineProperty;var t=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as nn}from"node:crypto";import{relative as en,resolve as G,sep as F}from"node:path";import{ApiError as s}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as k,listCrons as A,listCronRuns as L,pokeHeldCrons as tn,setCronState as Z}from"../../../core/canvas/crons.js";import{getNode as p}from"../../../core/canvas/canvas.js";import{appSpace as an,cronFolder as sn}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn}from"../../../core/grants/mirror.js";import{callerTerminalGrant as fn}from"../../../core/grants/terminal.js";import{assertScope as H}from"../../../core/scopes.js";import{InputError as c}from"../../../core/io.js";import{nextSlotAfter as wn}from"../../../core/wake.js";import{executeCron as _n,markSelfCanceledCronRun as hn}from"../../cron-run.js";import{toCronDTO as v,toCronRunDTO as M}from"../map.js";import{appOf as P,assertCeiling as mn,canReadNode as gn}from"../router.js";import{checkCronBirth as yn,checkCronFire as bn,cronRefusalError as kn}from"../../cron/authority.js";import{endCron as vn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as On}from"../../../core/async-array.js";import{personId as Cn}from"../../../core/canvas/person.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new c({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(E,"requireString");function O(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new c({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(O,"requireEnum");async function In(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new c({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await p(n)===null)throw new c({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new c({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new c({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(In,"parseSinkSpec");const qn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function Y(e,n){if(!qn.test(e)||Number.isNaN(Date.parse(e)))throw new c({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(Y,"requireUtcIso");function Nn(e){try{wn(e,new Date)}catch(n){throw new c({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(Nn,"requireRecur");function jn(e){let n;try{n=JSON.parse(e)}catch{throw new c({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new c({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new c({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(jn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function K(e,n){return n===null||e.scope==="global"||e.profile===n}t(K,"inScope");function J(e){if(e.principal.class==="person"&&e.principal.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");return e.principal.class==="person"?null:P(e.principal)}t(J,"callerApp");async function R(e,n){if(!await gn(e.principal,n.grantee))return!1;const r=e.principal,o=r.class==="broker"?await p(r.node):r.class==="cron"?{run_id:r.origin_run,isolated:r.isolated}:r.class==="forwarded"&&r.actor.kind==="app"?{run_id:r.actor.place.run,isolated:r.actor.place.isolated}:null;return!(o!==null&&(o.isolated||n.isolated)&&o.run_id!==n.origin_run_id)}t(R,"visibleTo");function Tn(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(Tn,"anchorNodeOf");async function An(e,n,r,o,i,d){const u=typeof e.cwd=="string"&&e.cwd.trim()!==""?G(e.cwd):null;if(!cn()){if(u===null)throw new c({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return u}const h=o!==null&&await p(o)!==null?o:void 0,l=t(async q=>await ln(n,q,h,i,void 0,d),"reaches");if(u!==null&&await l(u))return u;const a=an(n).files;if(u!==null&&e.cwd_is_default!==!0)throw new s(400,"invalid_request",`cwd ${u} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${a}`,{cwd:u,default:a},void 0,{param:"cwd"});const f=r===null?null:(await p(r))?.cwd??null;return f!==null&&await l(f)?f:a}t(An,"resolveArmedCwd");async function Pn(e,n,r){const o=e.principal;if(o.class==="person"){if(o.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");const a=await fn();return{sub:a?.sub??Cn(),grant_id:a?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const a=await un(n);if(a===null)throw new s(401,"grant_revoked",`no active grant for ${n}`);const f=e.ceiling??o.scopes;return{sub:a.sub,grant_id:a.id,scopes:f===null?null:[...f],origin_run_id:null}}if(o.class==="cron"){const a=await bn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!a.allowed)throw new s(a.reason==="scope_missing"?403:401,a.reason==="scope_missing"?"scope_missing":"grant_revoked",a.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run,armed_from_exec:o.from_exec}}if(o.class!=="broker")throw new s(401,"unauthorized","this caller cannot arm a cron");const i=r??await p(o.node),d=i?.grant_id??o.grant,u=i?.sub??o.sub,h=d!=null?await dn(d):await pn(n,u);if(h===null)throw new s(401,"grant_revoked","the node grant is not active");const l=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:l===null?null:[...l],origin_run_id:i?.run_id??o.run}}t(Pn,"armPrincipal");async function En(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=Y(E(n,"fire_at"),"fire_at"),d=typeof n.recur=="string"&&n.recur!==""?Nn(n.recur):null,u=typeof n.expires_at=="string"&&n.expires_at!==""?Y(n.expires_at,"expires_at"):null,h=typeof n.env_json=="string"&&n.env_json!==""?jn(n.env_json):null,l=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,a=O(n.on_output,"on_output",["silent","on-failure","always","on-change"],l?.startsWith("node:")?"on-change":"on-failure"),f=O(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=O(n.tier,"tier",["normal","urgent","critical"],"normal"),W=a==="always"||a==="on-change";if(W&&l===null)throw new c({error:"sink_required",message:`on_output "${a}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!W&&l!==null)throw new c({error:"sink_unused",message:`on_output "${a}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=l!==null?await In(l):"{}";if(l?.startsWith("spawn:")&&(mn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const y=await yn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,from_exec:e.principal.from_exec},{kind:l.slice(6)});if(!y.allowed)throw kn(y)}const g=P(e.principal)??"app:terminal",b=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==b)throw new s(403,"permission_error","a broker may only arm a cron as its own node");const m=b===null?null:await p(b);if(m!==null){if(m.grantee!==g)throw new c({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});H(m,"crtr:schedule"),l?.startsWith("spawn:")&&H(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new s(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const x=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(x&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=x||n.isolated===!0,w=await Pn(e,g,m),j=nn(),S=N?sn(g,j):null;if(S!==null&&n.cwd_is_default!==!0){const y=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?G(n.cwd):S);if(y===".."||y.startsWith(`..${F}`)||y.startsWith(F))throw new s(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await An(n,g,b,Tn(n),N,{sub:w.sub,grantId:w.grant_id,scopes:w.scopes}),_=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(_!==null&&await p(_)!==null&&(await p(_)).grantee!==g)throw new c({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(_!==null&&await p(_)===null)throw new c({error:"anchor_not_found",message:`anchor node ${_} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const B=n.cancel_on_wake===!0;if(B&&_===null)throw new c({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new c({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const T=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,z=O(n.scope,"scope",["profile","global"],T!==null?"profile":"global");if(z==="profile"&&T===null)throw new c({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await rn({cron_id:j,name:r,created_by:b,grantee:g,sub:w.sub,grant_id:w.grant_id,scopes:w.scopes,origin_run_id:w.origin_run_id,armed_from_exec:w.armed_from_exec===!0,isolated:N,command:o,fire_at:i,recur:d,tz:typeof n.tz=="string"?n.tz:null,expires_at:u,anchor_node:_,cancel_on_wake:B,cwd:V,env_json:h,profile:T,scope:z,run_timeout_s:$,overlap:f,on_output:a,sink:Q,tier:q}),{status:201,body:v(await k(j),null)}}t(En,"handleArm");async function Un(e){J(e);const n=U(e),r=e.query.get("created_by");if(r==="")throw new s(400,"invalid_request","created_by must be nonempty when present");const o=await $n((n===null?await A({all:!0}):[...await A({profile:n}),...await A({global:!0})].sort((i,d)=>i.fire_at<d.fire_at?-1:i.fire_at>d.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await R(e,i));return{status:200,body:await On(o,async i=>v(i,await D(i)))}}t(Un,"handleList");async function D(e){return(await L(e.cron_id))[0]??null}t(D,"lastRunOf");async function C(e){const n=e.params.cronId,r=await k(n);if(r===null||!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(!K(r,U(e)))throw new s(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(C,"requireCron");function I(e,n){const r=J(e);if(r!==null&&n.grantee!==r)throw new s(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Jn(e){const n=await C(e),r=await L(n.cron_id);return{status:200,body:{cron:v(n,r[0]??null),runs:r.map(M)}}}t(Jn,"handleShow");async function Rn(e){const n=I(e,await C(e));await Z(n.cron_id,"paused");const r=await k(n.cron_id);return{status:200,body:v(r,await D(r))}}t(Rn,"handlePause");async function Dn(e){const n=I(e,await C(e));await Z(n.cron_id,"active");const r=await k(n.cron_id);return{status:200,body:v(r,await D(r))}}t(Dn,"handleResume");async function Wn(e){const n=I(e,await C(e));if(n.run_state==="running")throw new s(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await _n(n,{outOfBand:!0});if(r===null)throw new s(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:M(r)}}t(Wn,"handleRun");async function xn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,J(e)),at:n}}}t(xn,"handlePoke");function Bn(e,n){const r=e.principal;if(r.class==="cron")return r.kind==="exec"?{kind:"canceled",by:r.app}:r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const i=on(n.sink);return i?.kind==="node"&&i.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=P(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function zn(e){const n=e.params.cronId,r=await k(n);if(r!==null&&!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!K(r,U(e)))throw new s(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),i=e.principal.class==="cron"?e.principal.kind==="cron"&&o===e.principal.execution&&n===e.principal.cron:!0,d=o!==null&&i&&hn(n,o);return r!==null&&await vn(r,d?null:Bn(e,r),{preserveRunningProcess:d}),{status:204}}t(zn,"handleCancel");const de=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:En},{method:"GET",pattern:"/v1/crons",handler:Un},{method:"GET",pattern:"/v1/crons/:cronId",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Dn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:xn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Wn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:zn}];export{de as cronRoutes};
|
|
1
|
+
var tn=Object.defineProperty;var t=(e,n)=>tn(e,"name",{value:n,configurable:!0});import{randomUUID as an}from"node:crypto";import{relative as sn,resolve as H,sep as M}from"node:path";import{ApiError as a}from"../../../api/errors.js";import{armCron as ln,parseCronSink as cn,getCron as v,listCrons as A,listCronRuns as Z,pokeHeldCrons as un,setCronState as Y}from"../../../core/canvas/crons.js";import{getNode as f}from"../../../core/canvas/canvas.js";import{appSpace as dn,cronFolder as pn}from"../../../core/layout.js";import{appProcessesSandboxed as fn,cwdIsMounted as wn}from"../../../core/spaces/process.js";import{activeGrant as _n,activeGrantForApp as hn,grantFor as mn}from"../../../core/grants/mirror.js";import{callerTerminalGrant as gn}from"../../../core/grants/terminal.js";import{assertScope as K}from"../../../core/scopes.js";import{InputError as l}from"../../../core/io.js";import{nextSlotAfter as yn}from"../../../core/wake.js";import{executeCron as bn,markSelfCanceledCronRun as kn}from"../../cron-run.js";import{toCronDTO as S,toCronRunDTO as Q}from"../map.js";import{appOf as R,assertCeiling as vn,canReadNode as Sn}from"../router.js";import{checkCronBirth as $n,checkCronFire as On,cronRefusalError as Cn}from"../../cron/authority.js";import{endCron as In}from"../../cron/ended.js";import{requireUsableProfile as qn}from"./profiles.js";import{lookupProfile as Nn}from"../../../core/profiles/manifest.js";import{filterAsync as Tn,mapAsync as jn}from"../../../core/async-array.js";import{personId as Pn}from"../../../core/canvas/person.js";import{openDb as En}from"../../../core/canvas/db.js";function U(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new l({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(U,"requireString");function C(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new l({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(C,"requireEnum");async function An(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new l({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await f(n)===null)throw new l({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new l({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new l({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(An,"parseSinkSpec");const Rn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function V(e,n){if(!Rn.test(e)||Number.isNaN(Date.parse(e)))throw new l({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(V,"requireUtcIso");function Un(e){try{yn(e,new Date)}catch(n){throw new l({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(Un,"requireRecur");function Jn(e){let n;try{n=JSON.parse(e)}catch{throw new l({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new l({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new l({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(Jn,"requireEnvJson");function J(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(J,"callerProfile");function X(e,n){return n===null||e.scope==="global"||e.profile===n}t(X,"inScope");function nn(e){return e.principal.class==="person"?null:R(e.principal)}t(nn,"callerApp");function I(e){return e.principal.class==="person"?e.principal.sub??null:null}t(I,"bearerPerson");async function en(e,n){return(await(await En()).prepare("SELECT person FROM profiles WHERE id = ?").get(e))?.person===n}t(en,"profileBelongsTo");async function W(e,n){const r=I(e);if(r!==null)return n.profile!==null&&await en(n.profile,r);if(!await Sn(e.principal,n.grantee))return!1;const o=e.principal,i=o.class==="broker"?await f(o.node):o.class==="cron"?{run_id:o.origin_run,isolated:o.isolated}:o.class==="forwarded"&&o.actor.kind==="app"?{run_id:o.actor.place.run,isolated:o.actor.place.isolated}:null;return!(i!==null&&(i.isolated||n.isolated)&&i.run_id!==n.origin_run_id)}t(W,"visibleTo");function Wn(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(Wn,"anchorNodeOf");async function Dn(e,n,r,o,i,u){const p=typeof e.cwd=="string"&&e.cwd.trim()!==""?H(e.cwd):null;if(!fn()){if(p===null)throw new l({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return p}const g=o!==null&&await f(o)!==null?o:void 0,c=t(async T=>await wn(n,T,g,i,void 0,u),"reaches");if(p!==null&&await c(p))return p;const s=dn(n).files;if(p!==null&&e.cwd_is_default!==!0)throw new a(400,"invalid_request",`cwd ${p} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${s}`,{cwd:p,default:s},void 0,{param:"cwd"});const w=r===null?null:(await f(r))?.cwd??null;return w!==null&&await c(w)?w:s}t(Dn,"resolveArmedCwd");async function Bn(e,n,r){const o=e.principal;if(o.class==="person"){const s=await gn();return{sub:s?.sub??o.sub??Pn(),grant_id:s?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const s=await hn(n);if(s===null)throw new a(401,"grant_revoked",`no active grant for ${n}`);const w=e.ceiling??o.scopes;return{sub:s.sub,grant_id:s.id,scopes:w===null?null:[...w],origin_run_id:null}}if(o.class==="cron"){const s=await On({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!s.allowed)throw new a(s.reason==="scope_missing"?403:401,s.reason==="scope_missing"?"scope_missing":"grant_revoked",s.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run,armed_from_exec:o.from_exec}}if(o.class!=="broker")throw new a(401,"unauthorized","this caller cannot arm a cron");const i=r??await f(o.node),u=i?.grant_id??o.grant,p=i?.sub??o.sub,g=u!=null?await _n(u):await mn(n,p);if(g===null)throw new a(401,"grant_revoked","the node grant is not active");const c=i?.scopes??o.scopes;return{sub:g.sub,grant_id:g.id,scopes:c===null?null:[...c],origin_run_id:i?.run_id??o.run}}t(Bn,"armPrincipal");async function zn(e){const n=e.body??{},r=U(n,"name"),o=U(n,"command"),i=V(U(n,"fire_at"),"fire_at"),u=typeof n.recur=="string"&&n.recur!==""?Un(n.recur):null,p=typeof n.expires_at=="string"&&n.expires_at!==""?V(n.expires_at,"expires_at"):null,g=typeof n.env_json=="string"&&n.env_json!==""?Jn(n.env_json):null,c=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,s=C(n.on_output,"on_output",["silent","on-failure","always","on-change"],c?.startsWith("node:")?"on-change":"on-failure"),w=C(n.overlap,"overlap",["skip","queue","replace"],"skip"),T=C(n.tier,"tier",["normal","urgent","critical"],"normal"),B=s==="always"||s==="on-change";if(B&&c===null)throw new l({error:"sink_required",message:`on_output "${s}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!B&&c!==null)throw new l({error:"sink_unused",message:`on_output "${s}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const rn=c!==null?await An(c):"{}",z=I(e);if(z!==null)for(const d of[c?.startsWith("node:")?c.slice(5).trim():null,typeof n.created_by=="string"?n.created_by:null,typeof n.anchor_node=="string"?n.anchor_node:null]){const L=d===null||d===""?null:await f(d);if(L!==null&&L.sub!==z)throw new a(404,"not_found","node not found")}if(c?.startsWith("spawn:")&&(vn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const d=await $n({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,from_exec:e.principal.from_exec},{kind:c.slice(6)});if(!d.allowed)throw Cn(d)}const b=R(e.principal)??"app:terminal",k=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==k)throw new a(403,"permission_error","a broker may only arm a cron as its own node");const y=k===null?null:await f(k);if(y!==null){if(y.grantee!==b)throw new l({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});K(y,"crtr:schedule"),c?.startsWith("spawn:")&&K(y,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new a(400,"invalid_request","isolated must be a boolean");if(y?.isolated&&n.isolated===!1)throw new a(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const F=y?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(F&&n.isolated===!1)throw new a(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const j=F||n.isolated===!0,_=await Bn(e,b,y),P=an(),$=j?pn(b,P):null;if($!==null&&n.cwd_is_default!==!0){const d=sn($,typeof n.cwd=="string"&&n.cwd.trim()!==""?H(n.cwd):$);if(d===".."||d.startsWith(`..${M}`)||d.startsWith(M))throw new a(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const on=$??await Dn(n,b,k,Wn(n),j,{sub:_.sub,grantId:_.grant_id,scopes:_.scopes}),h=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(h!==null&&await f(h)!==null&&(await f(h)).grantee!==b)throw new l({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(h!==null&&await f(h)===null)throw new l({error:"anchor_not_found",message:`anchor node ${h} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const G=n.cancel_on_wake===!0;if(G&&h===null)throw new l({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const O=n.run_timeout_s??300;if(typeof O!="number"||!Number.isInteger(O)||O<=0)throw new l({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const E=I(e),m=typeof n.profile!="string"||n.profile===""?null:E===null?(await qn(e,n.profile)).profileId:(()=>{const d=Nn(n.profile);if(d.kind!=="resolved")throw new a(404,"not_found","profile not found");return d.entry.profileId})();if(E!==null&&(m===null||!await en(m,E)))throw new a(m===null?400:404,m===null?"invalid_request":"not_found",m===null?"a person's cron names the profile it belongs to":"profile not found",void 0,void 0,{param:"profile"});const x=C(n.scope,"scope",["profile","global"],m!==null?"profile":"global");if(x==="profile"&&m===null)throw new l({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await ln({cron_id:P,name:r,created_by:k,grantee:b,sub:_.sub,grant_id:_.grant_id,scopes:_.scopes,origin_run_id:_.origin_run_id,armed_from_exec:_.armed_from_exec===!0,isolated:j,command:o,fire_at:i,recur:u,tz:typeof n.tz=="string"?n.tz:null,expires_at:p,anchor_node:h,cancel_on_wake:G,cwd:on,env_json:g,profile:m,scope:x,run_timeout_s:O,overlap:w,on_output:s,sink:rn,tier:T}),{status:201,body:S(await v(P),null)}}t(zn,"handleArm");async function Fn(e){const n=J(e),r=e.query.get("created_by");if(r==="")throw new a(400,"invalid_request","created_by must be nonempty when present");const o=await Tn((n===null?await A({all:!0}):[...await A({profile:n}),...await A({global:!0})].sort((i,u)=>i.fire_at<u.fire_at?-1:i.fire_at>u.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await W(e,i));return{status:200,body:await jn(o,async i=>S(i,await D(i)))}}t(Fn,"handleList");async function D(e){return(await Z(e.cron_id))[0]??null}t(D,"lastRunOf");async function q(e){const n=e.params.cronId,r=await v(n);if(r===null||!await W(e,r))throw new a(404,"cron_not_found",`no cron ${n}`);if(!X(r,J(e)))throw new a(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(q,"requireCron");function N(e,n){const r=nn(e);if(r!==null&&n.grantee!==r)throw new a(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(N,"requireOwnCron");async function Gn(e){const n=await q(e),r=await Z(n.cron_id);return{status:200,body:{cron:S(n,r[0]??null),runs:r.map(Q)}}}t(Gn,"handleShow");async function xn(e){const n=N(e,await q(e));await Y(n.cron_id,"paused");const r=await v(n.cron_id);return{status:200,body:S(r,await D(r))}}t(xn,"handlePause");async function Ln(e){const n=N(e,await q(e));await Y(n.cron_id,"active");const r=await v(n.cron_id);return{status:200,body:S(r,await D(r))}}t(Ln,"handleResume");async function Hn(e){const n=N(e,await q(e));if(n.run_state==="running")throw new a(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await bn(n,{outOfBand:!0});if(r===null)throw new a(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:Q(r)}}t(Hn,"handleRun");async function Mn(e){if(I(e)!==null)throw new a(403,"permission_error","a person's sign-in does not poke the whole control plane's crons");const n=new Date().toISOString();return{status:200,body:{unparked:await un(n,nn(e)),at:n}}}t(Mn,"handlePoke");function Zn(e,n){const r=e.principal;if(r.class==="cron")return r.kind==="exec"?{kind:"canceled",by:r.app}:r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const i=cn(n.sink);return i?.kind==="node"&&i.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=R(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Zn,"cancelNotice");async function Yn(e){const n=e.params.cronId,r=await v(n);if(r!==null&&!await W(e,r))throw new a(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!X(r,J(e)))throw new a(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&N(e,r);const o=e.query.get("run_id"),i=e.principal.class==="cron"?e.principal.kind==="cron"&&o===e.principal.execution&&n===e.principal.cron:!0,u=o!==null&&i&&kn(n,o);return r!==null&&await In(r,u?null:Zn(e,r),{preserveRunningProcess:u}),{status:204}}t(Yn,"handleCancel");const ye=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:zn},{method:"GET",pattern:"/v1/crons",handler:Fn},{method:"GET",pattern:"/v1/crons/:cronId",handler:Gn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:xn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Ln},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:Mn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Hn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:Yn}];export{ye as cronRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var d=Object.defineProperty;var n=(t,e)=>d(t,"name",{value:e,configurable:!0});import{drainState as u,endDrain as m,isDrained as c,settleDrain as l,startDrain as y}from"../../drain.js";import{ApiError as h}from"../../../api/errors.js";import{readMoveRecord as b}from"../../machine-link/config.js";import{toMigrateStateDTO as f}from"../map.js";import{migrateLooseKeys as p}from"../../../core/layout-migrate/loose-keys.js";import{boundDaemonControl as s}from"../../control.js";import{ownerOnly as _}from"../router.js";function v(){const t=s();if(t===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot hand over"};const{graceMs:e}=t.requestHandover();return{status:202,body:{accepted:!0,predecessor_pid:process.pid,predecessor_epoch:t.epoch,grace_ms:e}}}n(v,"handleRestart");async function w(t){if(t.body!==void 0)throw{status:400,code:"invalid_request",message:"admission request must not include a body"};const e=s();if(e===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot admit normal work"};return{status:200,body:await e.admitStartup()}}n(w,"handleAdmit");async function g(t){const e=s();if(e===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot run the canvas import"};const o=t.body;if(o!==void 0&&(typeof o!="object"||o===null||Array.isArray(o)))throw{status:400,code:"invalid_request",message:"migration request body must be an object"};const r=o??{};if(r.dry_run!==void 0&&typeof r.dry_run!="boolean")throw{status:400,code:"invalid_request",message:"dry_run must be a boolean"};const i=await p(r.dry_run===!0),a=await e.repairMigrations({dryRun:r.dry_run===!0});return{status:200,body:{...f(a.result,{resumed:a.resumed,resumeError:a.resumeError}),loose_keys:i}}}n(g,"handleMigrate");async function D(t){const e=t.body??{};if(typeof e!="object"||e===null||Array.isArray(e))throw{status:400,code:"invalid_request",message:"drain request body must be an object"};const o=e.settle_limit_ms;if(o!==void 0&&(typeof o!="number"||!Number.isFinite(o)||o<0))throw{status:400,code:"invalid_request",message:"settle_limit_ms must be a non-negative number"};return await y(),{status:200,body:o===void 0?await u():await l({limitMs:o})}}n(D,"handleDrain");async function q(){if(b()!==void 0)throw new h(409,"conflict","a move to Northlight is unsettled on this computer, so its pause stays: run `crtr sys signin` to settle it");const t=await c();return{status:200,body:{...await m(),was_drained:t}}}n(q,"handleEndDrain");const R=_([{method:"POST",pattern:"/v1/daemon/restart",handler:n(t=>v(),"handler")},{method:"POST",pattern:"/v1/daemon/migrate",handler:g},{method:"POST",pattern:"/v1/daemon/admit",handler:w},{method:"POST",pattern:"/v1/daemon/drain",handler:D},{method:"DELETE",pattern:"/v1/daemon/drain",handler:q}]);export{R as daemonRoutes};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
WHERE person = ? AND enabled = 1 AND owner_key IN (${
|
|
1
|
+
var f=Object.defineProperty;var c=(i,e)=>f(i,"name",{value:e,configurable:!0});import{ApiError as a}from"../../../api/errors.js";import{getNode as w}from"../../../core/canvas/canvas.js";import{openDb as d}from"../../../core/canvas/db.js";import{blobStore as h}from"../../../core/blobs/index.js";import{presignGet as E,releaseSignerFromEnv as b}from"../../../core/secrets/blaxel-launch.js";import{grantedProviderIds as g,isProviderBundleRow as k}from"../../../core/grants/mirror.js";import{profileMetadataEnv as x}from"../../../core/runtime/spawn-env.js";import{localEnvironmentSecrets as y}from"../../../core/secrets/environment-owner.js";import{environmentOfNode as v,recipeOf as R}from"../environment-of.js";import{mintInstallationToken as S}from"./git-credential.js";async function $(i,e){const n=await d(),s=e===null?["user"]:["user",`profile:${e}`],t=await n.prepare(`SELECT owner_key, name, version, source_marketplace, source_path FROM scope_plugins
|
|
2
|
+
WHERE person = ? AND enabled = 1 AND owner_key IN (${s.map(()=>"?").join(", ")}) ORDER BY name`).all(i,...s),r=new Map;for(const o of t)(!r.has(o.name)||o.owner_key!=="user")&&r.set(o.name,o);const u=[],p=await g(i);for(const o of r.values()){if(k(o,p))continue;if(o.source_path!==null){u.push({name:o.name,source:{path:o.source_path},version:o.version});continue}if(o.source_marketplace===null)throw new a(500,"plugin_source_missing",`plugin ${o.name} names neither a marketplace nor a path`);const l=await n.prepare("SELECT url, ref FROM scope_marketplaces WHERE person = ? AND owner_key = ? AND name = ?").get(i,o.owner_key,o.source_marketplace);u.push({name:o.name,source:{marketplace:{url:l?.url??null,ref:l?.ref??null}},version:o.version})}return u}c($,"plugins");const O=900;async function T(i){const e=await d(),n=await e.prepare("SELECT blob_key, unpacked_at FROM import_node_files WHERE node = ?").get(i);if(n===void 0)return null;if(!(await(await h()).list(n.blob_key)).includes(n.blob_key)){if(n.unpacked_at!==null)return null;throw new a(500,"import_files_missing",`the imported archive ${n.blob_key} of ${i} is gone and no sandbox unpacked it`)}const s=/^imports\/([^/]+)\/node-files\//.exec(n.blob_key)?.[1];if(s===void 0)throw new a(500,"import_files_invalid",`import_node_files names ${n.blob_key} for ${i}, which is no imports/<run>/node-files key`);const t=await e.prepare("SELECT source_value, target_value FROM import_id_map WHERE run_id = ? AND kind = 'path' ORDER BY source_value").all(s);return{url:E(b(),n.blob_key,O),path_map:t.map(r=>({from:r.source_value,to:r.target_value}))}}c(T,"importFiles");async function N(i){const{principal:e}=i;if(e.class!=="broker")throw new a(403,"broker_only","this route is called with a node token");const n=i.params.execution_id??"",t=await(await d()).prepare("SELECT node_id, state FROM executions WHERE execution_id = ?").get(n);if(t===void 0||t.node_id!==e.node)throw new a(403,"not_your_execution",`execution ${n} is not an execution of this node`);if(t.state!=="launching"&&t.state!=="running")throw new a(403,"execution_ended",`execution ${n} is ${t.state}`);return e.node}c(N,"ownedExecution");async function D(i){const e=await N(i);return await(await d()).prepare("UPDATE import_node_files SET unpacked_at = ? WHERE node = ? AND unpacked_at IS NULL").run(new Date().toISOString(),e),{status:200,body:{}}}c(D,"handleImportUnpacked");async function L(i){const{principal:e}=i;if(e.class!=="broker")throw new a(403,"broker_only","the start bundle is read with a node token");const n=i.params.execution_id??"",s=await d(),t=await s.prepare("SELECT node_id, state, launch_json, runner FROM executions WHERE execution_id = ?").get(n);if(t===void 0||t.node_id!==e.node)throw new a(403,"not_your_execution",`execution ${n} is not an execution of this node`);if(t.state!=="launching"&&t.state!=="running")throw new a(403,"execution_ended",`execution ${n} is ${t.state}`);if(t.launch_json===null)throw new a(409,"launch_missing",`execution ${n} carries no launch recipe`);const r=await w(e.node);if(r===null)throw new a(404,"node_not_found",`no node ${e.node}`);const u=await s.prepare("SELECT plugins_revision FROM person_settings WHERE person = ?").get(e.sub),p=await v(r.profile_id??null),o=await R(p),l=o?.repositories.map(m=>m.github_full_name)??[],_=l.length===0?null:(await S(e.sub,l)).author;return{status:200,body:{launch:JSON.parse(t.launch_json),node:{id:r.node_id,app:e.app,cwd:r.cwd,profile:r.profile_id??null,isolated:r.isolated===!0,environment_id:p},recipe:o,git_identity:_,plugins_revision:u===void 0?0:Number(u.plugins_revision),plugins:await $(e.sub,r.profile_id??null),import_node_files:await T(r.node_id),...t.runner==="machine"?{machine_env:{...x(r.profile_id??null),...await y(r.profile_id??null,e.app)}}:{}}}}c(L,"handleStart");const Y=[{method:"GET",pattern:"/v1/executions/:execution_id/start",handler:L},{method:"POST",pattern:"/v1/executions/:execution_id/import-unpacked",handler:D}];export{Y as executionStartRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var R=Object.defineProperty;var s=(e,t)=>R(e,"name",{value:t,configurable:!0});import{closeSync as T,constants as q,fstatSync as B,readSync as v}from"node:fs";import{isAbsolute as L,relative as F,resolve as b,sep as I}from"node:path";import{filterAsync as M}from"../../../core/async-array.js";import{appSpace as g}from"../../../core/layout.js";import{ApiError as f}from"../../../api/index.js";import{notFound as E,usage as c}from"../../../core/errors.js";import{InputError as k}from"../../../core/io.js";import{RunError as m}from"../../../core/runs/operations.js";import{isBeneathRefusal as z,listBeneath as N,openBeneath as O,writeFileBeneath as D}from"../../../core/spaces/open-beneath.js";import{fileRoot as P}from"../../../core/spaces/permissions.js";import{openDb as C}from"../../../core/canvas/db.js";import{canReadNode as $,resolvedScopes as S}from"../router.js";const h=512*1024,_=1024*1024,W=1e3;function y(e,t){if(e===null||e==="")throw c(`a file ${t} requires a \`path\` ${t==="write"?"body field":"query param"}`);if(!L(e))throw c(`file ${t} path must be absolute`,{received:e});return e}s(y,"requireAbsolutePath");function A(e){if(e==null)return"utf8";if(e==="utf8"||e==="base64")return e;throw c("encoding must be `utf8` or `base64`",{received:e})}s(A,"parseEncoding");function
|
|
1
|
+
var R=Object.defineProperty;var s=(e,t)=>R(e,"name",{value:t,configurable:!0});import{closeSync as T,constants as q,fstatSync as B,readSync as v}from"node:fs";import{isAbsolute as L,relative as F,resolve as b,sep as I}from"node:path";import{filterAsync as M}from"../../../core/async-array.js";import{appSpace as g}from"../../../core/layout.js";import{ApiError as f}from"../../../api/index.js";import{notFound as E,usage as c}from"../../../core/errors.js";import{InputError as k}from"../../../core/io.js";import{RunError as m}from"../../../core/runs/operations.js";import{isBeneathRefusal as z,listBeneath as N,openBeneath as O,writeFileBeneath as D}from"../../../core/spaces/open-beneath.js";import{fileRoot as P}from"../../../core/spaces/permissions.js";import{openDb as C}from"../../../core/canvas/db.js";import{canReadNode as $,resolvedScopes as S}from"../router.js";const h=512*1024,_=1024*1024,W=1e3;function y(e,t){if(e===null||e==="")throw c(`a file ${t} requires a \`path\` ${t==="write"?"body field":"query param"}`);if(!L(e))throw c(`file ${t} path must be absolute`,{received:e});return e}s(y,"requireAbsolutePath");function A(e){if(e==null)return"utf8";if(e==="utf8"||e==="base64")return e;throw c("encoding must be `utf8` or `base64`",{received:e})}s(A,"parseEncoding");function u(e,t,r){if(e instanceof f||e instanceof k||e instanceof m)throw e;if(z(e))throw new m(400,"invalid_request",`${t} meets a symlink, leaves its permitted root, or is not a regular file`,"path");const n=e.code;throw n==="ENOENT"?E(`no such file: ${t}`,{received:t}):n==="EACCES"||n==="EPERM"?new f(403,"forbidden",`permission denied ${r} ${t}`):new f(500,"internal",`cannot ${r} ${t}: ${e instanceof Error?e.message:String(e)}`)}s(u,"mapFilesystemError");async function Y(e,t,r,n){const i=await(await C()).prepare("SELECT isolated FROM nodes WHERE node_id = ?").get(t),[l,d]=F(g(r).root,b(n)).split(I),a=l==="nodes"&&d?await $(e.principal,r,d):!1;return{isolated:i?.isolated===1,reaches:s(()=>a,"reaches")}}s(Y,"runConfinementFor");async function w(e,t,r){const n=e.principal;if(n.class==="forwarded"||n.class==="machine"||n.class==="import")throw new f(403,"permission_error","this operation is not available to the caller");if(n.class==="person"&&n.sub!==void 0)throw new f(403,"permission_error","files are read by the install's person or a node");const o=n.class==="person"?null:n.class==="app"?{app:n.app,resolved:n.scopes,launched:null}:n.class==="cron"?{app:n.app,resolved:await S(n),launched:n.scopes}:{app:n.app,resolved:await S(n),launched:n.scopes,run:await Y(e,n.node,n.app,t)},i=P(o,t,r);if("root"in i)return i.root;throw"hidden"in i?E(`no such file: ${t}`,{received:t}):new m(403,"scope_missing",i.missing?`missing scope ${i.missing}`:`no permission names ${t}`,void 0,void 0,i.missing??void 0)}s(w,"rootFor");function Z(e,t,r){let n;try{n=O(e,t,q.O_RDONLY)}catch(o){return u(o,t,"reading")}try{const o=B(n);if(o.isDirectory())throw c(`file peek path is a directory: ${t}`,{received:t});const i=Buffer.allocUnsafe(Math.min(o.size,h)+1),l=v(n,i,0,i.length,0),d=l>h,a=i.subarray(0,Math.min(l,h)).toString(r);return{path:t,content:a,truncated:d}}catch(o){return u(o,t,"reading")}finally{T(n)}}s(Z,"readHead");async function j(e){const t=y(e.query.get("path"),"peek");return{status:200,body:Z(await w(e,t,"read"),t,A(e.query.get("encoding")))}}s(j,"handlePeek");function G(e){return e.length%4===0&&/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e)}s(G,"isBase64");function H(e){if(typeof e!="object"||e===null||Array.isArray(e))throw c("file write requires an object body");const t=e,r=y(typeof t.path=="string"?t.path:null,"write");if(typeof t.content!="string")throw c("file write requires a string `content` body field");const n=A(t.encoding);if(n==="base64"&&!G(t.content))throw c("base64 file content must be valid base64");return{path:r,content:t.content,encoding:n}}s(H,"parseWriteBody");function U(e,t,r){if(r.byteLength>_)throw new f(413,"file_too_large",`file content exceeds the ${_} byte limit`);try{return D(e,t,r,{mode:process.platform==="linux"?432:384,atomic:!0}),{path:t,bytes_written:r.byteLength}}catch(n){return u(n,t,"writing")}}s(U,"writeAtomically");async function X(e){const{path:t,content:r,encoding:n}=H(e.body);return{status:200,body:U(await w(e,t,"rw"),t,Buffer.from(r,n))}}s(X,"handleWrite");function K(e){if(e===null)return W;const t=Number(e);if(!Number.isInteger(t)||t<1)throw c("file list limit must be a positive integer",{received:e});return t}s(K,"parseLimit");async function J(e){const t=y(e.query.get("path"),"list"),r=K(e.query.get("limit")),n=await w(e,t,"read");let o;try{o=N(n,t,r+1)}catch(a){return u(a,t,"listing")}const i=e.principal;i.class==="broker"&&b(t)===g(i.app).nodes&&(o=await M(o,async({name:a})=>await $(i,i.app,a)));const l=o.slice(0,r).map(({name:a,stat:p})=>({name:a,type:p.isFile()?"file":p.isDirectory()?"dir":"other",size:p.size,modified:p.mtime.toISOString()}));return{status:200,body:{path:t,entries:l,truncated:o.length>r}}}s(J,"handleList");const fe=[{method:"GET",pattern:"/v1/files/peek",scope:"crtr:act",handler:j},{method:"POST",pattern:"/v1/files/write",scope:"crtr:act",handler:X},{method:"GET",pattern:"/v1/files/list",scope:"crtr:act",handler:J}];export{fe as fileRoutes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var k=Object.defineProperty;var a=(t,n)=>k(t,"name",{value:n,configurable:!0});import{randomBytes as E}from"node:crypto";import{ApiError as o}from"../../../api/errors.js";import{blobStore as w}from"../../../core/blobs/index.js";import{presignPutUrl as $,r2ConfigFromEnv as v}from"../../../core/blobs/r2.js";import{openDb as c}from"../../../core/canvas/db.js";import{bundleKeys as m}from"../../../core/import/bundle/layout.js";import{putStore as O,StoreBodyError as T,STORE_NAMES as R}from"../../../core/import/load/stores.js";import{rollbackRun as D}from"../../../core/import/load/rollback.js";import{crtrVersion as f}from"../../../shared/crtr-version.js";import{importTokenFor as N}from"../import-auth.js";const h=3600;function y(t){if(t.principal.class!=="person"||t.principal.sub===void 0)throw new o(403,"person_only","an import is made by a signed-in person");return t.principal.sub}a(y,"personOf");async function _(t,n=!1){const e=t.params.run_id??"",r=await(await c()).prepare("SELECT * FROM import_runs WHERE run_id = ?").get(e),s=t.principal;if(s.class==="import"){if(s.run===null&&!n)throw new o(403,"permission_error","the service credential starts and reads runs; the import token uploads and loads");if(r===void 0||(s.run===null?r.source_kind!=="hosted":s.run!==r.run_id))throw new o(404,"import_not_found",`no import run ${e}`);if(s.run!==null&&!n&&r.state!=="uploading"&&r.state!=="loading")throw new o(403,"import_token_expired",`import run ${r.run_id} is ${r.state}: its import token no longer works`);return r}if(r===void 0||r.person!==y(t))throw new o(404,"import_not_found",`no import run ${e}`);return r}a(_,"ownRun");function A(t,n,e){const r=t.principal;if(r.class!=="import")return y(t);if(r.run!==null)throw new o(403,"permission_error","an import token reaches only its own run");const s=n.person;if(typeof s!="string"||!/^user:.+/.test(s))throw new o(400,"invalid_body","person (user:<id>) is required");if(e!=="hosted")throw new o(400,"invalid_body","the service credential starts hosted runs only: source_kind must be hosted");return s}a(A,"startPerson");const g=a(t=>{if(t===null||typeof t!="object"||Array.isArray(t))throw new o(400,"invalid_body","the body must be a JSON object");return t},"objectBody");async function j(t){const n=g(t.body),e=A(t,n,n.source_kind),r=n.source_install_id,s=n.source_kind,u=n.release;if(typeof r!="string"||r===""||s!=="machine"&&s!=="hosted"||typeof u!="string")throw new o(400,"invalid_body","source_install_id, source_kind (machine | hosted) and release are required");if(u!==f())throw new o(409,"release_mismatch",`the source runs ${u}, the control plane accepts ${f()}`);const p=await c();let i=await p.prepare("SELECT run_id, state FROM import_runs WHERE source_install_id = ? AND state IN ('uploading', 'loading', 'committed', 'done')").get(r);if(i?.state==="uploading"){const S={step:"uploading",code:"superseded",message:`a new import run was started for install ${r} before this one asked for the load`};await p.prepare("UPDATE import_runs SET state = 'failed', failure_json = ?, finished_at = ? WHERE run_id = ? AND state = 'uploading'").run(JSON.stringify(S),new Date().toISOString(),i.run_id),i=await p.prepare("SELECT run_id, state FROM import_runs WHERE source_install_id = ? AND state IN ('uploading', 'loading', 'committed', 'done')").get(r)}if(i?.state==="done")throw new o(409,"already_imported",`install ${r} was imported by run ${i.run_id}`);if(i!==void 0)throw new o(409,"import_running",`install ${r} has an open run ${i.run_id} (${i.state})`);const d=`imp_${E(8).toString("hex")}`;await p.prepare("INSERT INTO import_runs (run_id, source_install_id, source_kind, person, release, state, created_at) VALUES (?, ?, ?, ?, ?, 'uploading', ?)").run(d,r,s,e,u,new Date().toISOString());const l=t.principal.class==="import"?N(d):null;return{status:201,body:{run_id:d,upload:{rows_prefix:m.prefix(d),url_template:`POST /v1/imports/${d}/upload-urls {"keys":[...]}`},...l===null?{}:{import_token:l}}}}a(j,"handleStart");async function P(t){const n=await _(t);if(n.state!=="uploading")throw new o(409,"import_not_uploading",`import run ${n.run_id} is ${n.state}`);const e=g(t.body).keys;if(!Array.isArray(e)||e.some(i=>typeof i!="string"))throw new o(400,"invalid_body","keys must be a list of blob keys");const r=m.prefix(n.run_id);for(const i of e)if(!(i.startsWith(r)&&!i.startsWith(`${r}stores/`)&&i!==m.manifest(n.run_id))||i.split("/").some(l=>l===""||l==="."||l===".."))throw new o(400,"invalid_key",`${i} is not a key this run may upload`);const s=v();if(s===null)throw new o(501,"upload_not_supported","this control plane keeps blobs in a directory: there is no URL to upload to");const u={};for(const i of e)u[i]=$(s,i,h);return{status:200,body:{urls:u,expires_at:new Date(Date.now()+h*1e3).toISOString()}}}a(P,"handleUploadUrls");async function I(t){const n=await _(t);if(n.state!=="uploading")throw new o(409,"import_not_uploading",`import run ${n.run_id} is ${n.state}`);const e=t.params.name;if(!R.includes(e))throw new o(404,"unknown_store",`no file store ${e}`);try{await O(await w(),n.run_id,e,t.body)}catch(r){throw r instanceof T?new o(400,"invalid_body",r.message):r}return{status:204}}a(I,"handleStore");async function U(t){const n=await _(t),e=await c();if(n.state==="uploading"){if(await(await w()).get(m.source(n.run_id))===null)throw new o(409,"bundle_invalid","source.json is not uploaded");await e.prepare("UPDATE import_runs SET state = 'loading', step = 'queued', loaded_at = ? WHERE run_id = ? AND state = 'uploading'").run(new Date().toISOString(),n.run_id)}else if(n.state!=="loading"&&n.state!=="committed"&&n.state!=="done")throw new o(409,"import_not_uploading",`import run ${n.run_id} is ${n.state}`);return{status:202,body:await b(n.run_id)}}a(U,"handleLoad");async function b(t){const n=await(await c()).prepare("SELECT * FROM import_runs WHERE run_id = ?").get(t),e=n.counts_json===null?{}:JSON.parse(n.counts_json),{unresolved_plugins:r,...s}=e;return{run_id:n.run_id,source_install_id:n.source_install_id,source_kind:n.source_kind,person:n.person,release:n.release,state:n.state,step:n.step,counts:s,verify:n.verify_json===null?null:JSON.parse(n.verify_json),failure:n.failure_json===null?null:JSON.parse(n.failure_json),unresolved_plugins:r??[],created:n.created_at,loaded:n.loaded_at,finished:n.finished_at}}a(b,"runDto");async function L(t){const n=await _(t,!0);return{status:200,body:await b(n.run_id)}}a(L,"handleGet");async function W(t){const n=await _(t),e=await D(n.run_id,await w());if(!e.rolled_back)throw new o(409,"rollback_has_dependents",`later rows depend on import run ${n.run_id}`,e.dependents);return{status:200,body:e}}a(W,"handleRollback");const X=[{method:"POST",pattern:"/v1/imports",handler:j},{method:"POST",pattern:"/v1/imports/:run_id/upload-urls",handler:P},{method:"PUT",pattern:"/v1/imports/:run_id/stores/:name",handler:I},{method:"POST",pattern:"/v1/imports/:run_id/load",handler:U},{method:"GET",pattern:"/v1/imports/:run_id",handler:L},{method:"POST",pattern:"/v1/imports/:run_id/rollback",handler:W}];export{X as importRoutes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var s=Object.defineProperty;var a=(e,t)=>s(e,"name",{value:t,configurable:!0});import{ApiError as i}from"../../../api/errors.js";import{openDb as d}from"../../../core/canvas/db.js";import{hasMachineCredential as c,mintMachineCredential as m}from"../../machine-link/credential.js";async function p(e){if(e.principal.class!=="person")throw new i(403,"person_only","the machine credential is issued to a person");const t=e.params.run_id??"",o=await d(),n=await o.prepare("SELECT source_install_id, source_kind, person, state FROM import_runs WHERE run_id = ?").get(t);if(n===void 0||e.principal.sub!==void 0&&n.person!==e.principal.sub)throw new i(404,"import_not_found",`no import run ${t}`);if(n.source_kind!=="machine")throw new i(409,"not_a_machine_import","only a machine source gets a machine credential");if(n.state!=="done")throw new i(409,"import_not_done",`import run ${t} is ${n.state}`);const r=await o.prepare("SELECT object FROM environments WHERE machine_id = ? AND kind = 'machine'").get(n.source_install_id);if(r===void 0)throw new i(409,"environment_missing",`no machine environment for install ${n.source_install_id}`);if(!(e.query.get("rotate")==="1")&&await c(r.object))throw new i(409,"credential_exists","the machine credential was already issued; rotate it");return{status:200,body:{environment_id:r.object,credential:await m(r.object)}}}a(p,"handleMint");const f=[{method:"POST",pattern:"/v1/imports/:run_id/machine-credential",handler:p}];export{f as machineCredentialRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var N=Object.defineProperty;var o=(t,e)=>N(t,"name",{value:e,configurable:!0});import{openDb as q}from"../../../core/canvas/db.js";import{assertLaunchKind as c}from"../../../core/runtime/launch-target.js";import{listNodesByProfile as v,shiftProfileNodeDeadlines as L}from"../../../core/canvas/canvas.js";import{liveExecutionForNode as S}from"../../../core/canvas/executions.js";import{shiftJobDeadlines as $}from"../../../core/graph/jobs.js";import{boundFleet as F}from"../../../core/runtime/fleet.js";import{recordedPidLiveness as M}from"../../../core/canvas/pid.js";import{withCanvasWrite as w}from"../../../core/canvas/db.js";import{reposForCwd as R,warmCheckoutIdentities as V}from"../../../core/graph/repo-sync/repos.js";import{renameProfileObject as C}from"../../../core/graph/objects.js";import{usage as l}from"../../../core/errors.js";import{ApiError as f}from"../../../api/errors.js";import{scopeAllowed as E}from"../../../core/scopes.js";import{validateEnvVarName as G}from"../../../core/env-name.js";import{listProfileEnvNames as U,removeProfileEnvVar as H,setProfileEnvVar as K}from"../../../core/profiles/env-store.js";import{isDeploymentName as W,listAppEnvNames as B,removeAppEnvVar as J,setAppEnvVar as z}from"../../../core/profiles/app-env-store.js";import{addProfileProject as Q,assertDeliveryLimits as X,setProfileDeliveryLimit as g,assertProfileProjects as h,createProfile as j,ensureRootProfile as Y,listProfiles as m,loadProfileManifest as y,pauseProfile as Z,removeProfileProject as x,renameProfile as ee,resumeProfile as te,setProfileDefaultKind as ae,updateProfileMetadata as ne}from"../../../core/profiles/manifest.js";import{requestProfileDeletion as re}from"../../profile-delete.js";import{brokerSignalBus as ie}from"../../broker-signals/bus.js";import{toProfileDTO as oe}from"../map.js";import{machineEnvironmentId as se}from"../../../core/environments.js";import{actsAsPerson as _,appOf as b,resolvedScopes as de}from"../router.js";const T="root";function d(t){return{...oe(t),environment_id:t.manifest.environment_id??se()}}o(d,"toProfileDTO");function u(t){return b(t.principal)??"app:terminal"}o(u,"callerApp");function A(t,e,n=null){if(m().some(a=>a.manifest.name===e&&a.profileId!==n&&(a.manifest.grantee??"app:terminal")===t))throw new f(400,"invalid_request",`${t} already has a profile named ${e}`)}o(A,"assertNameFree");async function p(t,e){const n=await k(t,e),a=n.manifest.grantee??"app:terminal";if(!_(t.principal)&&a!==b(t.principal))throw new f(403,"permission_error",`profile ${n.profileId} belongs to ${a}; only that app changes it`);return n}o(p,"requireOwnProfile");async function le(t){const e=t.body??{};if(typeof e.name!="string"||e.name.trim()==="")throw l("a profile name is required",{field:"name"});e.projects!==void 0&&h(e.projects);const n=u(t),a=e.name.trim();A(n,a),e.default_kind!==void 0&&await c(e.default_kind,process.cwd(),null);const i=await j(a,e.projects??[],{defaultKind:e.default_kind,metadata:e.metadata,grantee:n});return{status:201,body:d(i)}}o(le,"handleCreate");async function fe(t,e){const n=e.trim();if(n==="")throw l("profile name must not be empty");return await w(async a=>(await C(a,t,n),await ee(t,n)))}o(fe,"renameProfileAndHandle");async function pe(t,e,n){await V(n);const a=await w(async i=>(await R(i,n)).map(r=>r.name));await Q(t,n);for(const i of a)e[i]===void 0&&e[`repo:${i}`]===void 0&&await g(t,i,"preview")}o(pe,"addProjectWithDefaultLimit");async function me(t){const{profileId:e,manifest:n}=await p(t,t.params.name),a=t.body??{};return a.name!==void 0&&A(n.grantee??"app:terminal",a.name.trim(),e),a.default_kind!==void 0&&await c(a.default_kind,process.cwd(),e),a.add_project!==void 0&&h([a.add_project]),a.set_delivery_limit!==void 0&&X({[a.set_delivery_limit.owner]:a.set_delivery_limit.level}),a.name!==void 0&&await fe(e,a.name),a.default_kind!==void 0&&await ae(e,a.default_kind),a.add_project!==void 0&&await pe(e,n.delivery_limits??{},a.add_project.path),a.set_delivery_limit!==void 0&&await g(e,a.set_delivery_limit.owner,a.set_delivery_limit.level),a.remove_project!==void 0&&await x(e,a.remove_project),{status:200,body:d(y(e))}}o(me,"handleUpdate");async function ue(t){const{profileId:e}=await p(t,t.params.name);return{status:200,body:{profile_id:e,names:await U(e)}}}o(ue,"handleEnvList");function P(t){const e=t.params.var,n=G(e);if(!n.isValid)throw l(`invalid env var name: ${n.errors.join("; ")}`,{field:"name",received:e});return e}o(P,"requireEnvVarName");function D(t){const e=P(t);if(W(e)&&!_(t.principal))throw new f(403,"permission_error",`${e} is deployment-wide; only a person changes it`);return e}o(D,"requireSelfEnvVarName");async function ce(t){const{profileId:e}=await p(t,t.params.name),n=P(t),{value:a}=t.body??{};if(typeof a!="string"||a==="")throw l("a non-empty env value is required",{field:"value"});return{status:200,body:{profile_id:e,changed:await K(e,n,a)}}}o(ce,"handleEnvSet");async function ve(t){const{profileId:e}=await p(t,t.params.name),n=P(t);return{status:200,body:{profile_id:e,changed:await H(e,n)}}}o(ve,"handleEnvRemove");async function we(t){const e=t.params.name,n=t.body??{};n.projects!==void 0&&h(n.projects);const a=u(t);if(e===T&&a==="app:terminal")return{status:200,body:d(await Y())};if(e===T)throw new f(403,"permission_error","root belongs to the terminal app");const i=m().find(s=>s.manifest.name===e&&(s.manifest.grantee??"app:terminal")===a);if(i!==void 0)return{status:200,body:d(i)};n.default_kind!==void 0&&await c(n.default_kind,process.cwd(),null);const r=await j(e,n.projects??[],{defaultKind:n.default_kind,metadata:n.metadata,grantee:a});return{status:200,body:d(r)}}o(we,"handleEnsure");async function he(t){const e=await p(t,t.params.name),n=t.body??{};if(typeof n!="object"||n===null||Array.isArray(n))throw l("profile metadata body must be an object",{field:"body"});const a=n,i=a.unset===void 0?[]:a.unset;if(!Array.isArray(i)||i.some(O=>typeof O!="string"))throw l("profile metadata `unset` must be an array of strings",{field:"unset"});const r=a.set===void 0?{}:a.set,s=await ne(e.profileId,r,i);return{status:200,body:d(s)}}o(he,"handleSetMetadata");async function ye(t){const e=y(t.params.name),n=await Z(e.profileId),a=(await v(e.profileId)).filter(r=>r.status==="active"||r.status==="idle");if(a.length===0)return{status:200,body:{profile_id:e.profileId,paused_at:n.manifest.paused_at}};const i=F();for(const r of a){const s=i.get(r.node_id);if(s!==void 0){await i.interruptForProfilePause(r.node_id,s.pid);continue}r.pi_pid!=null&&M(r.pi_pid,r.pi_pid_identity)==="alive"&&await i.interruptForProfilePause(r.node_id,r.pi_pid)}return{status:200,body:{profile_id:e.profileId,paused_at:n.manifest.paused_at}}}o(ye,"handlePause");async function _e(t){const e=y(t.params.name),n=await te(e.profileId);for(const a of await v(e.profileId))(a.status==="active"||a.status==="idle"||await S(a.node_id)!==null)&&ie.markMail(a.node_id);if(n.pausedAt!==null){const a=Date.parse(n.pausedAt);if(!Number.isNaN(a)){const i=Math.max(0,Date.now()-a);await L(e.profileId,i);for(const r of await v(e.profileId))r.status!=="dead"&&await w(s=>$(s,r.node_id,i))}}return{status:200,body:{profile_id:e.profileId,paused_at:n.manifest.paused_at}}}o(_e,"handleResume");async function I(t){const e=t.principal;if(_(e))return m();if(e.class==="person"&&e.sub!==void 0){const a=await q(),i=new Set((await a.prepare("SELECT id FROM profiles WHERE person = ?").all(e.sub)).map(r=>r.id));return m().filter(({profileId:r})=>i.has(r))}const n=await de(e);return m().filter(({profileId:a,manifest:i})=>{const r=i.grantee??"app:terminal";if(r===b(e))return!0;const s=`crtr:memory:rw:${r}:profile:${a}`;return E(n,s)&&(e.class!=="broker"||E(e.scopes,s))})}o(I,"visibleProfiles");async function k(t,e){const n=await I(t),a=n.find(({profileId:r})=>r===e),i=a?[a]:n.filter(({manifest:r})=>r.name===e);if(i.length===0)throw new f(404,"not_found","profile not found");if(i.length>1)throw new f(400,"ambiguous","profile name matches multiple visible profiles; use an exact id");return i[0]}o(k,"requireUsableProfile");async function be(t){return{status:200,body:(await I(t)).map(d)}}o(be,"handleList");async function Pe(t){return{status:200,body:d(await k(t,t.params.name))}}o(Pe,"handleGet");async function Ee(t){return re((await p(t,t.params.name)).profileId,t.body)}o(Ee,"handleDelete");const Be=[{method:"GET",pattern:"/v1/apps/self/env",handler:o(async t=>({status:200,body:{names:await B(u(t))}}),"handler")},{method:"PUT",pattern:"/v1/apps/self/env/:var",handler:o(async t=>{const e=D(t),{value:n}=t.body??{};if(typeof n!="string"||!n)throw l("a non-empty env value is required",{field:"value"});return{status:200,body:{changed:await z(u(t),e,n)}}},"handler")},{method:"DELETE",pattern:"/v1/apps/self/env/:var",handler:o(async t=>({status:200,body:{changed:await J(u(t),D(t))}}),"handler")},{method:"PUT",pattern:"/v1/profiles/:name",handler:we},{method:"POST",pattern:"/v1/profiles/:name/pause",ownerOnly:!0,handler:ye},{method:"POST",pattern:"/v1/profiles/:name/resume",ownerOnly:!0,handler:_e},{method:"PATCH",pattern:"/v1/profiles/:name/metadata",handler:he},{method:"POST",pattern:"/v1/profiles",handler:le},{method:"PATCH",pattern:"/v1/profiles/:name",handler:me},{method:"GET",pattern:"/v1/profiles/:name/env",handler:ue},{method:"PUT",pattern:"/v1/profiles/:name/env/:var",handler:ce},{method:"DELETE",pattern:"/v1/profiles/:name/env/:var",handler:ve},{method:"DELETE",pattern:"/v1/profiles/:name",handler:Ee},{method:"GET",pattern:"/v1/profiles",handler:be},{method:"GET",pattern:"/v1/profiles/:name",handler:Pe}];export{pe as addProjectWithDefaultLimit,Be as profileRoutes,fe as renameProfileAndHandle,k as requireUsableProfile};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var q=Object.defineProperty;var s=(e,r)=>q(e,"name",{value:r,configurable:!0});import{randomUUID as G}from"node:crypto";import{cloudPersonScope as N}from"../../../core/runs/operations.js";import{directoryIssuer as T}from"../../../core/directory/issuer.js";import{activeGrantForApp as F,allGrants as x,grantFor as C,localGrants as j,providerIdsFromGrants as
|
|
1
|
+
var q=Object.defineProperty;var s=(e,r)=>q(e,"name",{value:r,configurable:!0});import{randomUUID as G}from"node:crypto";import{cloudPersonScope as N}from"../../../core/runs/operations.js";import{directoryIssuer as T}from"../../../core/directory/issuer.js";import{activeGrantForApp as F,allGrants as x,grantFor as C,localGrants as j,providerIdsFromGrants as S}from"../../../core/grants/mirror.js";import{getNode as k}from"../../../core/canvas/canvas.js";import{scopeAllowed as B}from"../../../core/scopes.js";import{ProviderError as f,providerErrorBody as D,scopeMissing as M}from"../../../core/providers/errors.js";import{callProvider as U,providerTools as z}from"../../../core/providers/call.js";import{providerInstallState as L}from"../../../core/providers/bundles.js";import{installedProviderPlugins as V,providerRootClaims as Z,resolveProviderRef as W}from"../../../core/providers/roots.js";import{isCloudRole as h}from"../../../core/canvas/person-state.js";import{listAudit as J,writeProviderAudit as X}from"../../../core/providers/audit.js";import{emitEvent as P}from"../../../core/events/emit.js";import{resolvedScopes as _}from"../router.js";const Ae=new Set(["provider.tools","provider.call","audit.list"]);async function H(e){if(e.class==="forwarded"){if(e.actor.kind==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null,origin:{node:null,run:null,machine:e.via}};const r=await C(e.actor.app,e.sub);if(!r)throw new f("grant_revoked",`app ${e.actor.app} has no active grant`);return{class:"app",sub:e.sub,grantee:e.actor.app,client_id:null,resolved:r.resolved,nodeScopes:e.actor.node_scopes,run_id:null,node_id:null,origin:{node:e.actor.node,run:e.actor.place?.run??null,machine:e.via}}}if(e.class==="machine"||e.class==="import")throw new f("permission_error","this operation is not available to the caller");if(e.class==="person"&&e.sub!==void 0){if(!h())throw new f("permission_error","provider calls are made by the install's person, a node or an app");return{class:"person",sub:e.sub,grantee:null,client_id:null,resolved:await _(e),nodeScopes:null,run_id:null,node_id:null}}if(e.class==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null};if(e.class==="app"){const r=await F(e.app);if(!r)throw new f("grant_revoked",`app ${e.app} has no active grant`);return{class:"app",sub:r.sub,grantee:e.app,client_id:null,resolved:r.resolved,nodeScopes:e.scopes,run_id:null,node_id:null}}return e.class==="cron"?{class:"app",sub:e.sub,grantee:e.app,client_id:null,resolved:await _(e),nodeScopes:e.scopes,run_id:e.origin_run,node_id:null}:{class:"broker",sub:e.sub,grantee:e.app,client_id:e.client_id,resolved:await _(e),nodeScopes:e.scopes,run_id:e.run??(await k(e.node))?.run_id??null,node_id:e.node}}s(H,"callerFromRequest");function Ee(e){return{class:"app",sub:e.sub,grantee:e.grantee,client_id:e.client_id,resolved:e.resolved,nodeScopes:null,run_id:null,node_id:null}}s(Ee,"callerFromApp");const K=/^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64})$/;function Q(e){const r=K.exec(e);return r!==null&&r[1]!=="crtr"}s(Q,"isProviderScope");const A=new WeakMap;function Y(e){let r=A.get(e);return r===void 0&&(r=new Set(e),A.set(e,r)),r}s(Y,"heldSet");function E(e,r){return e===null?!0:Q(r)?Y(e).has(r):B(e,r)}s(E,"holds");function R(e,r){return e.class==="person"&&e.resolved===null?!0:E(e.resolved??[],r)&&E(e.nodeScopes,r)}s(R,"callerAllows");function Re(e,r,n){const o=`${r}:${n}`;if(!R(e,o))throw M(o)}s(Re,"requireGroup");function ee(){return!!T()}s(ee,"providerOperationsEnabled");const re=/^[A-Za-z0-9_-]{1,64}$/;function v(e,r){return new f("invalid_request",e,{param:r})}s(v,"invalid");function I(e){if(typeof e!="string"||!re.test(e)||e==="crtr")throw v("provider must be a provider id","provider");return e}s(I,"parseProvider");function oe(e){if(!e||typeof e!="object"||Array.isArray(e))throw new f("invalid_request","a JSON object is required");const r=e,n=I(r.provider),o=r.tool;if(typeof o!="string"||!o.trim())throw v("tool must be a non-empty string","tool");const i=r.arguments??{};if(!i||typeof i!="object"||Array.isArray(i))throw v("arguments must be an object","arguments");const d=r.version;if(d!==void 0&&(typeof d!="number"||!Number.isInteger(d)||d<1))throw v("version must be an integer >= 1","version");const a=r.approval;if(a!==void 0&&(typeof a!="string"||!a))throw v("approval must be a question id","approval");const l=r.machine;if(l!==void 0&&(typeof l!="string"||!l.trim()))throw v("machine must be a machine id","machine");return{provider:n,tool:o.trim(),arguments:i,...d===void 0?{}:{version:d},...a===void 0?{}:{approval:a},...l===void 0?{}:{machine:l.trim()}}}s(oe,"parseCallInput");async function te(e,r=new URLSearchParams){const n=s((u,p,b)=>{const m=r.get(u);if(m===null)return p;if(!/^\d+$/.test(m)||Number(m)>b||Number(m)<1)throw v(`invalid ${u}`,u);return Number(m)},"numeric"),o=r.has("after")?n("after",0,Number.MAX_SAFE_INTEGER):void 0,i=n("limit",100,1e3),d=Object.fromEntries(["operation","outcome","run_id","since","until"].filter(u=>r.has(u)).map(u=>[u,r.get(u)])),a=N(e.sub),l=a===void 0?{}:{sub:a};return{status:200,body:await J(e.class==="person"?null:e.grantee,{after:o,limit:i,...l,...d})}}s(te,"auditList");async function ne(e,r,n){let o=r;const i=performance.now(),d=V(),{skipped:a}=Z(d),l=h()?await x():await j(),u=h()?new Set(S(l.filter(t=>t.sub===e.sub))):null,p=s(t=>!a.has(t)&&(u===null||u.has(t)),"shown"),b=new Set(d.filter(t=>t.enabled&&t.manifest.provider?.id===t.name&&p(t.name)).map(t=>t.name)),m=S(l).filter(p),g=[...new Set([...b,...m])].sort();if(o!==void 0){const t=W(o);if(t===null)throw new f("not_found",`provider "${o}" is not available`);o=t}if(o!==void 0&&!g.includes(o))throw new f("not_found",`provider "${o}" is not available`);const $=o===void 0?g:[o],O=e.class==="person"?l.flatMap(t=>t.status==="active"&&(!h()||t.sub===e.sub)?t.resolved_scopes.map(({scope:c})=>c):[]):e.resolved??[],w=await Promise.all($.map(async t=>{if(b.has(t))return z(e,t);if(!O.some(y=>y.startsWith(`${t}:`)&&R(e,y)))return null;const c=L(t);return c===void 0?null:{provider:t,display_name:t,status:c.status,...c.error?{error:c.error}:{},groups:[],commands:[]}})).then(t=>t.filter(c=>c!==null));if(P({level:"debug",event:"api.provider.tools",duration_ms:performance.now()-i,fields:{caller:e.class,provider:o??null,providers:w.length}}),o!==void 0){if(w.length===0)throw new f("not_found",`provider "${o}" is not available`);return{status:200,body:w[0]}}return{status:200,body:{providers:w}}}s(ne,"providerTools");async function se(e,r,n){return U(e,r,n)}s(se,"providerCall");async function ie(e,r,n,o){if(!ee())throw new f("not_found","provider operations need a directory-connected runtime");if(e==="audit.list")return await te(r,n.query);if(e==="provider.tools")return ne(r,n.params.provider===void 0?void 0:I(n.params.provider),o);if(e==="provider.call"){let i;try{i=oe(n.body)}catch(d){const a=n.body&&typeof n.body=="object"&&!Array.isArray(n.body)?n.body:{};throw await X({request_id:o.requestId,caller:r,provider:typeof a.provider=="string"?a.provider:"",tool:typeof a.tool=="string"?a.tool:"",outcome:`refused:${d instanceof f?d.code:"invalid_request"}`}),d}return se(r,i,o)}throw new f("not_found","unknown operation")}s(ie,"handleProviderOperation");function de(e){const r=new AbortController,n=s(()=>{e.writableFinished||r.abort()},"abort");return e.on("close",n),{signal:r.signal,release:s(()=>e.off("close",n),"release")}}s(de,"abortOnClose");function ae(e,r,n,o){const i=D(e,r,n,o??void 0);return i.status>=500&&!(e instanceof f)&&P({event:"api.provider.failed",level:"error",error:e instanceof Error?e:new Error(String(e)),fields:{request_id:r}}),i}s(ae,"providerFailure");const Ie=[{method:"GET",pattern:"/v1/audit",name:"audit.list"},{method:"GET",pattern:"/v1/providers/tools",name:"provider.tools"},{method:"GET",pattern:"/v1/providers/:provider/tools",name:"provider.tools"},{method:"POST",pattern:"/v1/provider/call",name:"provider.call"}].map(({method:e,pattern:r,name:n})=>({method:e,pattern:r,handler:s(async o=>{const i=G(),{signal:d,release:a}=de(o.res);let l=null;try{return l=await H(o.principal),{...await ie(n,l,{params:o.params,body:o.body,query:new URL(o.req.url??"/","http://runtime.local").searchParams},{requestId:i,signal:d,...o.causedBy===void 0?{}:{causedBy:o.causedBy}}),headers:{"Request-Id":i}}}catch(u){return ae(u,i,!1,l?.run_id)}finally{a()}},"handler")}));export{Ae as PROVIDER_OPERATIONS,de as abortOnClose,R as callerAllows,Ee as callerFromApp,H as callerFromRequest,ie as handleProviderOperation,oe as parseCallInput,I as parseProvider,ae as providerFailure,ee as providerOperationsEnabled,Ie as providerRoutes,Re as requireGroup};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var S=Object.defineProperty;var c=(a,t)=>S(a,"name",{value:t,configurable:!0});import{callerGrant as g}from"../principal.js";import{grantVersion as b,payerOfGrant as O}from"../../../core/grants/mirror.js";import{holdGrant as $}from"../../../core/grants/watch.js";import{RunError as n,idempotencyKeyOf as v,cancelRun as N,cloudPersonScope as A,deleteRun as j,interruptRun as x,listRuns as B,messageRun as J,renameRun as z,requestedScopes as D,startRun as E,traceRun as G,waitRun as K}from"../../../core/runs/operations.js";import{canReadRun as y,requireRun as w,runCallerApp as M}from"../router.js";import{waitSeconds as q}from"../long-poll-wait.js";import{outputSchemaProblem as P}from"../../../core/runtime/structured-output.js";function m(a){if(!a||typeof a!="object"||Array.isArray(a))throw new n(400,"invalid_request","a JSON object is required");return a}c(m,"object");function k(a,t,i,u,l){if(a===null)return l;if(!/^(0|[1-9]\d*)$/.test(a)||!Number.isSafeInteger(Number(a))||Number(a)<i||Number(a)>u)throw new n(400,"invalid_request",`invalid ${t}`,t);return Number(a)}c(k,"integer");const T=256*1024;async function Y(a,t,i,u,l,_,h){switch(a.name){case"grant.get":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=q("grant.get",u),s=u.get("since");if(s!==null&&!/^[A-Za-z0-9_-]{1,64}$/.test(s))throw new n(400,"invalid_request","invalid since","since");const d=e&&s!==null?$(t.grant):void 0;let o;try{if(o=await g(t),d){const f=Date.now()+e*1e3;for(;b(o)===s&&Date.now()<f;)await d.changed(Math.max(0,(f-Date.now())/1e3)),o=await g(t)}}finally{d?.release()}return{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:await O(t.grant),dependencies:o.dependencies??{},version:b(o)}}}case"run.start":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=m(l),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 d=["payer","model_policy","kind"];for(const r of d)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 o=e.metadata===void 0?{}:m(e.metadata);if(Buffer.byteLength(JSON.stringify(o))>4096)throw new n(400,"invalid_request","metadata exceeds 4 KB","metadata");let f;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 P(r);if(p!==null)throw new n(400,"invalid_request",`output_schema is not usable: ${p}`,"output_schema");f=r}const R=D(t.resolved,e.scopes,t.grantee);return{status:201,body:await E(t,{prompt:e.prompt,scopes:R,metadata:o,profile:e.profile,isolated:e.isolated,cwd:e.cwd,...e.model===void 0?{}:{model:e.model},...e.deadline===void 0?{}:{deadline:e.deadline},...f===void 0?{}:{output_schema:f},...e.message===void 0?{}:{message:e.message},...h===void 0?{}:{causedBy:h}},v(_))}}case"run.list":{const e=u.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=u.get("app");if(s!==null&&!/^(?:app:)?[a-z0-9][a-z0-9-]*$/.test(s))throw new n(400,"invalid_request","invalid app","app");const d=s===null?M(t):s.startsWith("app:")?s:`app:${s}`;if(d!==null&&!await y(t,d))throw new n(404,"not_found","run not found");const o=A(t.sub);return{status:200,body:await B(d,{status:e,...o===void 0?{}:{sub:o},after:u.get("after")??void 0,limit:k(u.get("limit"),"limit",1,100,50),..."class"in t&&(t.class==="broker"||t.class==="cron")&&d!==null?{visible:c(async f=>await y(t,d,f),"visible")}:{}})}}case"run.get":{const e=q("run.get",u),s=await w(t,i),d=await K(i,s.grantee,e);return await w(t,i),{status:200,body:d}}case"run.message":{const e=m(l);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");if(e.steer!==void 0&&typeof e.steer!="boolean")throw new n(400,"invalid_request","steer must be a boolean","steer");if(e.start_turn===!1&&e.steer===!0)throw new n(400,"invalid_request","steer requires start_turn","steer");return{status:202,body:await J(await w(t,i,!0),e.message,void 0,e.start_turn,v(_),e.steer)}}case"run.interrupt":{const e=m(l===void 0?{}:l);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 x(await w(t,i,!0),e.children)}}case"run.cancel":{const e=m(l===void 0?{}:l);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 N(await w(t,i,!0),{keepSchedules:e.keep_schedules})}}case"run.rename":{const e=m(l);for(const s of Object.keys(e))if(s!=="name")throw new n(400,"invalid_request",`unknown field ${s}`,s);return{status:200,body:await z(await w(t,i,!0),e.name)}}case"run.delete":{const{uploads_pending:e}=await j(await w(t,i,!0,!0));return{status:200,body:{run_id:i,uploads_pending:e}}}case"trace.get":{const e=u.get("format")??void 0;if(e&&e!=="entries")throw new n(400,"invalid_request","unknown trace format","format");const s=await w(t,i);return{status:200,body:await G(i,s.grantee,e)}}default:throw new n(404,"not_found","unknown operation")}}c(Y,"handleRunOperation");export{Y as handleRunOperation,k as integer};
|
|
1
|
+
var S=Object.defineProperty;var c=(a,t)=>S(a,"name",{value:t,configurable:!0});import{callerGrant as g}from"../principal.js";import{grantVersion as b,payerOfGrant as O}from"../../../core/grants/mirror.js";import{holdGrant as $}from"../../../core/grants/watch.js";import{RunError as n,idempotencyKeyOf as v,cancelRun as N,cloudPersonScope as A,deleteRun as j,interruptRun as x,listRuns as B,messageRun as J,renameRun as z,requestedScopes as D,startRun as E,traceRun as G,waitRun as K}from"../../../core/runs/operations.js";import{canReadRun as y,requireRun as w,runCallerApp as M}from"../router.js";import{waitSeconds as q}from"../long-poll-wait.js";import{outputSchemaProblem as P}from"../../../core/runtime/structured-output.js";function m(a){if(!a||typeof a!="object"||Array.isArray(a))throw new n(400,"invalid_request","a JSON object is required");return a}c(m,"object");function k(a,t,i,u,l){if(a===null)return l;if(!/^(0|[1-9]\d*)$/.test(a)||!Number.isSafeInteger(Number(a))||Number(a)<i||Number(a)>u)throw new n(400,"invalid_request",`invalid ${t}`,t);return Number(a)}c(k,"integer");const T=256*1024;async function Y(a,t,i,u,l,_,h){switch(a.name){case"grant.get":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=q("grant.get",u),s=u.get("since");if(s!==null&&!/^[A-Za-z0-9_-]{1,64}$/.test(s))throw new n(400,"invalid_request","invalid since","since");const d=e&&s!==null?$(t.grant):void 0;let o;try{if(o=await g(t),d){const f=Date.now()+e*1e3;for(;b(o)===s&&Date.now()<f;)await d.changed(Math.max(0,(f-Date.now())/1e3)),o=await g(t)}}finally{d?.release()}return{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:await O(t.grant),dependencies:o.dependencies??{},version:b(o)}}}case"run.start":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=m(l),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 d=["payer","model_policy","kind"];for(const r of d)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 o=e.metadata===void 0?{}:m(e.metadata);if(Buffer.byteLength(JSON.stringify(o))>4096)throw new n(400,"invalid_request","metadata exceeds 4 KB","metadata");let f;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 P(r);if(p!==null)throw new n(400,"invalid_request",`output_schema is not usable: ${p}`,"output_schema");f=r}const R=D(t.resolved,e.scopes,t.grantee);return{status:201,body:await E(t,{prompt:e.prompt,scopes:R,metadata:o,profile:e.profile,isolated:e.isolated,cwd:e.cwd,...e.model===void 0?{}:{model:e.model},...e.deadline===void 0?{}:{deadline:e.deadline},...f===void 0?{}:{output_schema:f},...e.message===void 0?{}:{message:e.message},...h===void 0?{}:{causedBy:h}},v(_))}}case"run.list":{const e=u.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=u.get("app");if(s!==null&&!/^(?:app:)?[a-z0-9][a-z0-9-]*$/.test(s))throw new n(400,"invalid_request","invalid app","app");const d=s===null?M(t):s.startsWith("app:")?s:`app:${s}`;if(d!==null&&!await y(t,d))throw new n(404,"not_found","run not found");const o=A("sub"in t?t.sub:void 0);return{status:200,body:await B(d,{status:e,...o===void 0?{}:{sub:o},after:u.get("after")??void 0,limit:k(u.get("limit"),"limit",1,100,50),..."class"in t&&(t.class==="broker"||t.class==="cron")&&d!==null?{visible:c(async f=>await y(t,d,f),"visible")}:{}})}}case"run.get":{const e=q("run.get",u),s=await w(t,i),d=await K(i,s.grantee,e);return await w(t,i),{status:200,body:d}}case"run.message":{const e=m(l);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");if(e.steer!==void 0&&typeof e.steer!="boolean")throw new n(400,"invalid_request","steer must be a boolean","steer");if(e.start_turn===!1&&e.steer===!0)throw new n(400,"invalid_request","steer requires start_turn","steer");return{status:202,body:await J(await w(t,i,!0),e.message,void 0,e.start_turn,v(_),e.steer)}}case"run.interrupt":{const e=m(l===void 0?{}:l);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 x(await w(t,i,!0),e.children)}}case"run.cancel":{const e=m(l===void 0?{}:l);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 N(await w(t,i,!0),{keepSchedules:e.keep_schedules})}}case"run.rename":{const e=m(l);for(const s of Object.keys(e))if(s!=="name")throw new n(400,"invalid_request",`unknown field ${s}`,s);return{status:200,body:await z(await w(t,i,!0),e.name)}}case"run.delete":{const{uploads_pending:e}=await j(await w(t,i,!0,!0));return{status:200,body:{run_id:i,uploads_pending:e}}}case"trace.get":{const e=u.get("format")??void 0;if(e&&e!=="entries")throw new n(400,"invalid_request","unknown trace format","format");const s=await w(t,i);return{status:200,body:await G(i,s.grantee,e)}}default:throw new n(404,"not_found","unknown operation")}}c(Y,"handleRunOperation");export{Y as handleRunOperation,k as integer};
|