@north-light/cli 0.3.560 → 0.3.562
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 +27 -4
- 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/bash-jobs.d.ts +5 -1
- package/dist/api/dto/broker-ops.d.ts +25 -1
- package/dist/api/dto/charges.d.ts +9 -2
- package/dist/api/dto/delivery.d.ts +26 -2
- package/dist/api/dto/execution-start.d.ts +57 -0
- package/dist/api/dto/execution-start.js +0 -0
- package/dist/api/dto/modelauth.d.ts +5 -0
- package/dist/api/dto/node-records.d.ts +16 -0
- package/dist/api/dto/objects.d.ts +1 -1
- package/dist/api/dto/reports.d.ts +0 -3
- package/dist/api/dto/run-events.d.ts +7 -1
- package/dist/api/dto/settings.d.ts +55 -0
- package/dist/api/dto/settings.js +0 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +1 -1
- package/dist/api/routes.d.ts +11 -0
- package/dist/api/routes.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +4 -0
- package/dist/cli.js +7 -7
- package/dist/clients/attach/chrome/bash-jobs.d.ts +11 -1
- package/dist/clients/attach/chrome/bash-jobs.js +1 -1
- package/dist/clients/attach/chrome/canvas-panels.d.ts +1 -1
- package/dist/clients/attach/chrome/roster.js +6 -4
- package/dist/clients/attach/overlays/auth.js +2 -2
- package/dist/clients/attach/render/chat-view.js +1 -1
- package/dist/clients/attach/viewer.js +550 -567
- package/dist/commands/canvas-config.js +2 -2
- package/dist/commands/canvas-use.js +1 -1
- package/dist/commands/human.js +2 -2
- package/dist/commands/node/bash.js +3 -3
- package/dist/commands/node/create.js +2 -2
- package/dist/commands/person-settings.d.ts +1 -0
- package/dist/commands/person-settings.js +1 -0
- package/dist/commands/pkg/market-manage.d.ts +2 -2
- package/dist/commands/pkg/market-manage.js +1 -1
- package/dist/commands/pkg/plugin-manage.js +13 -13
- package/dist/commands/pkg/shared.js +1 -1
- package/dist/commands/profile/default.js +1 -1
- package/dist/commands/push.js +2 -3
- package/dist/commands/sys/config.js +1 -1
- package/dist/commands/sys/connect.js +1 -1
- package/dist/commands/sys/context/sysprompt.js +2 -2
- package/dist/commands/sys/doctor.js +1 -1
- package/dist/commands/sys/panels/broker-limits-panel.js +1 -1
- package/dist/commands/sys/panels/keybindings-panel.js +1 -1
- package/dist/commands/sys/panels/models-panel.d.ts +8 -2
- package/dist/commands/sys/panels/models-panel.js +1 -1
- package/dist/commands/sys/panels/prefs-panel.d.ts +1 -0
- package/dist/commands/sys/panels/prefs-panel.js +1 -1
- package/dist/commands/sys/panels/profiles-panel.js +1 -1
- package/dist/commands/sys/panels/provider-panel.d.ts +4 -0
- package/dist/commands/sys/panels/provider-panel.js +1 -1
- package/dist/commands/sys/provider-login.js +5 -5
- package/dist/commands/sys/setup-core.js +3 -3
- package/dist/commands/sys/setup-settings-runner.js +1 -1
- package/dist/commands/sys/spend.js +3 -3
- package/dist/commands/sys/update.js +1 -1
- package/dist/core/auth-file.d.ts +11 -19
- package/dist/core/auth-file.js +1 -2
- package/dist/core/auto-update.d.ts +1 -1
- package/dist/core/auto-update.js +4 -4
- package/dist/core/bash-jobs.d.ts +29 -71
- package/dist/core/bash-jobs.js +9 -10
- package/dist/core/blobs/directory.d.ts +3 -0
- package/dist/core/blobs/directory.js +1 -0
- package/dist/core/blobs/fault.d.ts +3 -0
- package/dist/core/blobs/fault.js +1 -0
- package/dist/core/blobs/index.d.ts +21 -0
- package/dist/core/blobs/index.js +1 -0
- package/dist/core/blobs/r2.d.ts +9 -0
- package/dist/core/blobs/r2.js +4 -0
- package/dist/core/bootstrap.d.ts +1 -1
- package/dist/core/bootstrap.js +2 -2
- package/dist/core/canvas/canvas.d.ts +9 -7
- package/dist/core/canvas/canvas.js +8 -8
- package/dist/core/canvas/crons.d.ts +3 -2
- package/dist/core/canvas/crons.js +3 -3
- package/dist/core/canvas/daemon-owner.d.ts +5 -0
- package/dist/core/canvas/daemon-owner.js +1 -1
- package/dist/core/canvas/db.d.ts +10 -9
- package/dist/core/canvas/db.js +4 -4
- package/dist/core/canvas/executions.d.ts +100 -4
- package/dist/core/canvas/executions.js +1 -1
- package/dist/core/canvas/export-sqlite.js +1 -1
- package/dist/core/canvas/focuses.d.ts +4 -0
- package/dist/core/canvas/focuses.js +1 -1
- package/dist/core/canvas/history.js +9 -9
- package/dist/core/canvas/managed-worktree-sweep-candidates.d.ts +2 -1
- package/dist/core/canvas/managed-worktree-sweep-candidates.js +3 -2
- package/dist/core/canvas/node-tokens.d.ts +1 -4
- package/dist/core/canvas/node-tokens.js +3 -3
- package/dist/core/canvas/paths.d.ts +0 -1
- package/dist/core/canvas/paths.js +1 -1
- package/dist/core/canvas/person-state.d.ts +26 -0
- package/dist/core/canvas/person-state.js +1 -0
- package/dist/core/canvas/person.d.ts +3 -10
- package/dist/core/canvas/person.js +7 -7
- package/dist/core/canvas/reconciler-leases.d.ts +12 -0
- package/dist/core/canvas/reconciler-leases.js +8 -0
- package/dist/core/canvas/recovery.d.ts +4 -0
- package/dist/core/canvas/recovery.js +1 -1
- package/dist/core/canvas/schema-pg/003-fleet-removals.sql +1 -1
- package/dist/core/canvas/schema-pg/005-fleet-blobs.sql +13 -0
- package/dist/core/canvas/schema-pg/006-job-rows.sql +20 -0
- package/dist/core/canvas/schema-pg/007-ledger-check.sql +117 -0
- package/dist/core/canvas/schema-version.d.ts +1 -1
- package/dist/core/canvas/schema-version.js +1 -1
- package/dist/core/canvas/sessions.d.ts +8 -11
- package/dist/core/canvas/sessions.js +1 -4
- package/dist/core/canvas/tree.d.ts +4 -2
- package/dist/core/canvas/tree.js +6 -6
- package/dist/core/canvas/types.d.ts +7 -0
- package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
- package/dist/core/command-plugins/bundle.d.ts +1 -1
- package/dist/core/command-plugins/bundle.js +1 -1
- package/dist/core/command-plugins/compose.d.ts +1 -1
- package/dist/core/command-plugins/discovery.d.ts +0 -1
- package/dist/core/command-plugins/discovery.js +1 -1
- package/dist/core/command-plugins/help-addenda.js +1 -1
- package/dist/core/command-plugins/presence.d.ts +2 -0
- package/dist/core/command-plugins/presence.js +1 -1
- package/dist/core/config.d.ts +16 -15
- package/dist/core/config.js +1 -1
- package/dist/core/conversation-store/client-worker.js +1 -1
- package/dist/core/conversation-store/client.js +2 -2
- package/dist/core/conversation-store/index.d.ts +9 -11
- package/dist/core/conversation-store/index.js +1 -1
- package/dist/core/conversation-store/patch.js +1 -1
- package/dist/core/conversation-store/row-store.d.ts +14 -0
- package/dist/core/conversation-store/row-store.js +13 -0
- package/dist/core/directory/join.js +1 -1
- package/dist/core/env-name.d.ts +1 -0
- package/dist/core/env-name.js +1 -1
- package/dist/core/environment-recipe.d.ts +15 -0
- package/dist/core/environment-recipe.js +1 -0
- package/dist/core/environments.js +1 -1
- package/dist/core/events/emit.js +2 -2
- package/dist/core/events/run-event-chunks.d.ts +25 -0
- package/dist/core/events/run-event-chunks.js +5 -0
- package/dist/core/feed/messages.js +5 -5
- package/dist/core/grants/mirror.d.ts +15 -0
- package/dist/core/grants/mirror.js +15 -3
- package/dist/core/grants/on-sync.d.ts +9 -5
- package/dist/core/grants/on-sync.js +1 -1
- package/dist/core/grants/remove.d.ts +1 -6
- package/dist/core/grants/remove.js +1 -1
- package/dist/core/grants/sync.js +1 -1
- package/dist/core/grants/watch.d.ts +15 -9
- package/dist/core/grants/watch.js +1 -1
- package/dist/core/graph/custom.js +8 -8
- package/dist/core/graph/jobs.d.ts +83 -5
- package/dist/core/graph/jobs.js +11 -2
- package/dist/core/graph/kinds/job.js +2 -2
- package/dist/core/graph/names.js +7 -7
- package/dist/core/graph/objects.d.ts +5 -1
- package/dist/core/graph/objects.js +11 -11
- package/dist/core/graph/reader.d.ts +3 -0
- package/dist/core/graph/reader.js +3 -3
- package/dist/core/graph/repo-sync/identity.d.ts +3 -1
- package/dist/core/graph/repo-sync/identity.js +1 -1
- package/dist/core/graph/repo-sync/repos.d.ts +1 -1
- package/dist/core/graph/repo-sync/repos.js +2 -2
- package/dist/core/graph/repo-sync/sync.js +2 -2
- package/dist/core/installed-plugins.js +1 -1
- package/dist/core/keybindings/persistence.d.ts +1 -1
- package/dist/core/keybindings/persistence.js +1 -1
- package/dist/core/layout-migrate/loose-keys.d.ts +1 -1
- package/dist/core/layout-migrate/loose-keys.js +1 -1
- package/dist/core/ledger/account.d.ts +12 -0
- package/dist/core/ledger/account.js +2 -0
- package/dist/core/ledger/charges.d.ts +2 -2
- package/dist/core/ledger/charges.js +1 -1
- package/dist/core/ledger/check.d.ts +5 -1
- package/dist/core/ledger/check.js +1 -7
- package/dist/core/ledger/chunks.d.ts +8 -0
- package/dist/core/ledger/chunks.js +2 -0
- package/dist/core/ledger/index.d.ts +2 -1
- package/dist/core/ledger/index.js +1 -1
- package/dist/core/ledger/infra-allowance.d.ts +9 -0
- package/dist/core/ledger/infra-allowance.js +1 -0
- package/dist/core/ledger/limits.js +3 -3
- package/dist/core/ledger/payer.d.ts +1 -1
- package/dist/core/ledger/payer.js +1 -1
- package/dist/core/ledger/read.d.ts +14 -4
- package/dist/core/ledger/read.js +2 -3
- package/dist/core/ledger/totals.d.ts +5 -16
- package/dist/core/ledger/totals.js +4 -4
- package/dist/core/ledger/types.d.ts +12 -3
- package/dist/core/llm/run.js +2 -2
- package/dist/core/managed-providers.d.ts +72 -0
- package/dist/core/managed-providers.js +1 -0
- package/dist/core/model-auth/api-credential-store.d.ts +10 -0
- package/dist/core/model-auth/api-credential-store.js +1 -0
- package/dist/core/model-auth/daemon-login.d.ts +1 -1
- package/dist/core/model-auth/daemon-login.js +1 -1
- package/dist/core/model-auth/flows.d.ts +5 -4
- package/dist/core/model-auth/flows.js +1 -1
- package/dist/core/model-auth/handoff-jti.d.ts +3 -4
- package/dist/core/model-auth/handoff-jti.js +1 -1
- package/dist/core/model-auth/readiness.js +1 -1
- package/dist/core/model-config.d.ts +1 -1
- package/dist/core/model-config.js +1 -1
- package/dist/core/model-routes-pool.d.ts +14 -0
- package/dist/core/model-routes-pool.js +1 -0
- package/dist/core/model-routes.d.ts +11 -16
- package/dist/core/model-routes.js +1 -1
- package/dist/core/peer/ingest.d.ts +2 -0
- package/dist/core/peer/ingest.js +1 -1
- package/dist/core/peer/stubs.d.ts +8 -0
- package/dist/core/peer/stubs.js +1 -1
- package/dist/core/permissions/evaluator.d.ts +7 -2
- package/dist/core/permissions/evaluator.js +2 -2
- package/dist/core/plugin-env.js +1 -1
- package/dist/core/pool-credential-store.d.ts +2 -5
- package/dist/core/pool-credential-store.js +1 -1
- package/dist/core/profiles/app-env-store.d.ts +5 -5
- package/dist/core/profiles/app-env-store.js +1 -2
- package/dist/core/profiles/default-binding.d.ts +5 -5
- package/dist/core/profiles/default-binding.js +1 -1
- package/dist/core/profiles/env-store.d.ts +10 -21
- package/dist/core/profiles/env-store.js +1 -2
- package/dist/core/profiles/manifest.d.ts +14 -18
- package/dist/core/profiles/manifest.js +1 -1
- package/dist/core/profiles/relocate.js +1 -1
- package/dist/core/profiles/select.js +21 -21
- package/dist/core/provider-credential.d.ts +1 -1
- package/dist/core/provider-credential.js +1 -1
- package/dist/core/provider-management.d.ts +3 -4
- package/dist/core/provider-management.js +1 -1
- package/dist/core/providers/bundles.js +2 -2
- package/dist/core/providers/call.d.ts +3 -34
- package/dist/core/providers/description.d.ts +34 -0
- package/dist/core/providers/description.js +0 -0
- package/dist/core/providers/token-cache.d.ts +14 -14
- package/dist/core/providers/token-cache.js +1 -1
- package/dist/core/review/companion.js +1 -1
- package/dist/core/runs/activity.d.ts +1 -1
- package/dist/core/runs/activity.js +1 -1
- package/dist/core/runs/events.d.ts +7 -5
- package/dist/core/runs/events.js +2 -6
- package/dist/core/runs/operations.d.ts +1 -1
- package/dist/core/runs/operations.js +6 -6
- package/dist/core/runtime/bearings.js +1 -1
- package/dist/core/runtime/broker/attachments-fetch.d.ts +5 -0
- package/dist/core/runtime/broker/attachments-fetch.js +1 -0
- package/dist/core/runtime/broker/bash-job-launch.d.ts +4 -2
- package/dist/core/runtime/broker/bash-job-launch.js +1 -1
- package/dist/core/runtime/broker/boot-state.d.ts +9 -0
- package/dist/core/runtime/broker/boot-state.js +1 -0
- package/dist/core/runtime/broker/cli-relay.d.ts +2 -1
- package/dist/core/runtime/broker/cli-relay.js +1 -1
- package/dist/core/runtime/broker/client-registry.d.ts +3 -3
- package/dist/core/runtime/broker/daemon-ops.d.ts +10 -1
- package/dist/core/runtime/broker/daemon-ops.js +1 -1
- package/dist/core/runtime/broker/fault-retry.d.ts +2 -0
- package/dist/core/runtime/broker/fault-retry.js +1 -1
- package/dist/core/runtime/broker/frame-engine-control.js +1 -1
- package/dist/core/runtime/broker/held-deferred-inbox.d.ts +3 -0
- package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
- package/dist/core/runtime/broker/job-reporter.d.ts +18 -0
- package/dist/core/runtime/broker/job-reporter.js +2 -0
- package/dist/core/runtime/broker/model-adapter.d.ts +3 -0
- package/dist/core/runtime/broker/model-adapter.js +3 -2
- package/dist/core/runtime/broker/signal-stream.js +1 -1
- package/dist/core/runtime/broker/spilled-body.d.ts +5 -0
- package/dist/core/runtime/broker/spilled-body.js +1 -0
- package/dist/core/runtime/broker/summaries-post.d.ts +5 -0
- package/dist/core/runtime/broker/summaries-post.js +1 -0
- package/dist/core/runtime/broker/tool-groups.d.ts +3 -0
- package/dist/core/runtime/broker/tool-groups.js +1 -1
- package/dist/core/runtime/broker/turn-replies.d.ts +1 -0
- package/dist/core/runtime/broker/turn-replies.js +1 -1
- package/dist/core/runtime/broker-persona-guidance.js +2 -2
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/deliver-live.js +1 -1
- package/dist/core/runtime/first-boot-rows.d.ts +8 -0
- package/dist/core/runtime/first-boot-rows.js +2 -0
- package/dist/core/runtime/fleet.d.ts +13 -12
- package/dist/core/runtime/front-door-login.js +1 -1
- package/dist/core/runtime/front-door.js +6 -6
- package/dist/core/runtime/headless-pi.js +1 -1
- package/dist/core/runtime/host.d.ts +14 -3
- package/dist/core/runtime/host.js +3 -3
- package/dist/core/runtime/invocation.d.ts +6 -0
- package/dist/core/runtime/kickoff.d.ts +2 -14
- package/dist/core/runtime/kickoff.js +17 -19
- package/dist/core/runtime/launch-config-worker.js +1 -1
- package/dist/core/runtime/launch.js +2 -2
- package/dist/core/runtime/model-registry.js +1 -1
- package/dist/core/runtime/node-read.d.ts +4 -6
- package/dist/core/runtime/node-read.js +3 -3
- package/dist/core/runtime/nodes.js +1 -1
- package/dist/core/runtime/outcome-document.d.ts +0 -1
- package/dist/core/runtime/outcome-document.js +1 -1
- package/dist/core/runtime/output-result.d.ts +15 -0
- package/dist/core/runtime/output-result.js +5 -0
- package/dist/core/runtime/placement.d.ts +17 -1
- package/dist/core/runtime/placement.js +1 -1
- package/dist/core/runtime/provider-credential-gate.d.ts +6 -2
- package/dist/core/runtime/provider-credential-gate.js +2 -2
- package/dist/core/runtime/recovery-hold.d.ts +21 -2
- package/dist/core/runtime/recovery-hold.js +2 -1
- package/dist/core/runtime/recycle.js +1 -1
- package/dist/core/runtime/reset.js +1 -1
- package/dist/core/runtime/revive.d.ts +2 -2
- package/dist/core/runtime/revive.js +2 -2
- package/dist/core/runtime/runner/blaxel/client.d.ts +81 -0
- package/dist/core/runtime/runner/blaxel/client.js +1 -0
- package/dist/core/runtime/runner/blaxel/image-build.d.ts +74 -0
- package/dist/core/runtime/runner/blaxel/image-build.js +5 -0
- package/dist/core/runtime/runner/blaxel/runner.d.ts +41 -0
- package/dist/core/runtime/runner/blaxel/runner.js +3 -0
- package/dist/core/runtime/runner/index.d.ts +8 -0
- package/dist/core/runtime/runner/index.js +1 -0
- package/dist/core/runtime/runner/process.d.ts +11 -0
- package/dist/core/runtime/runner/process.js +1 -0
- package/dist/core/runtime/runner/types.d.ts +95 -0
- package/dist/core/runtime/runner/types.js +0 -0
- package/dist/core/runtime/situational-context.d.ts +3 -6
- package/dist/core/runtime/situational-context.js +2 -2
- package/dist/core/runtime/situational-live.js +1 -1
- package/dist/core/runtime/spawn-env-rows.d.ts +5 -0
- package/dist/core/runtime/spawn-env-rows.js +1 -0
- package/dist/core/runtime/spawn-env.d.ts +4 -18
- package/dist/core/runtime/spawn-env.js +1 -1
- package/dist/core/runtime/spawn.js +2 -2
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +2 -8
- package/dist/core/runtime/structured-output.d.ts +0 -67
- package/dist/core/runtime/structured-output.js +3 -8
- package/dist/core/runtime/tmux-driver.d.ts +8 -0
- package/dist/core/runtime/tmux-driver.js +6 -6
- package/dist/core/runtime/view-socket.d.ts +20 -0
- package/dist/core/runtime/view-socket.js +3 -1
- package/dist/core/runtime/warm-pool.d.ts +13 -1
- package/dist/core/runtime/warm-pool.js +1 -1
- package/dist/core/scoped-state/legacy-profiles.d.ts +11 -0
- package/dist/core/scoped-state/legacy-profiles.js +1 -0
- package/dist/core/scoped-state/materialized-plugins.d.ts +18 -0
- package/dist/core/scoped-state/materialized-plugins.js +1 -0
- package/dist/core/scoped-state/migrate.d.ts +8 -1
- package/dist/core/scoped-state/migrate.js +3 -3
- package/dist/core/scoped-state/person-rows.d.ts +10 -0
- package/dist/core/scoped-state/person-rows.js +6 -0
- package/dist/core/scoped-state/person-store.d.ts +72 -0
- package/dist/core/scoped-state/person-store.js +1 -0
- package/dist/core/secrets/blaxel-launch.d.ts +24 -0
- package/dist/core/secrets/blaxel-launch.js +4 -0
- package/dist/core/secrets/blaxel-register.d.ts +2 -0
- package/dist/core/secrets/blaxel-register.js +1 -0
- package/dist/core/secrets/env-rows.d.ts +17 -0
- package/dist/core/secrets/env-rows.js +4 -0
- package/dist/core/secrets/environment-owner.d.ts +8 -0
- package/dist/core/secrets/environment-owner.js +1 -0
- package/dist/core/secrets/environment-secrets.d.ts +14 -0
- package/dist/core/secrets/environment-secrets.js +1 -0
- package/dist/core/secrets/index.d.ts +2 -0
- package/dist/core/secrets/index.js +1 -0
- package/dist/core/secrets/key.d.ts +3 -0
- package/dist/core/secrets/key.js +2 -0
- package/dist/core/secrets/seal.d.ts +18 -0
- package/dist/core/secrets/seal.js +1 -0
- package/dist/core/spaces/mounts.d.ts +1 -1
- package/dist/core/spaces/mounts.js +1 -1
- package/dist/core/spaces/process.d.ts +1 -1
- package/dist/core/spaces/stop.d.ts +7 -5
- package/dist/core/spaces/stop.js +2 -2
- package/dist/core/spaces/users.d.ts +2 -1
- package/dist/core/spaces/users.js +1 -1
- package/dist/core/storage/attachments.d.ts +11 -8
- package/dist/core/storage/attachments.js +1 -1
- package/dist/core/storage/tables.d.ts +4 -0
- package/dist/core/storage/tables.js +3 -3
- package/dist/core/subscription-state.d.ts +34 -72
- package/dist/core/subscription-state.js +1 -4
- package/dist/core/substrate/delivery/plan.js +1 -1
- package/dist/core/user-settings.js +1 -1
- package/dist/daemon/api/app-listener.js +10 -10
- package/dist/daemon/api/auth/person-bearer.d.ts +14 -0
- package/dist/daemon/api/auth/person-bearer.js +1 -0
- package/dist/daemon/api/bridge.js +4 -4
- package/dist/daemon/api/document-listener.js +3 -3
- package/dist/daemon/api/environment-of.d.ts +18 -0
- package/dist/daemon/api/environment-of.js +5 -0
- package/dist/daemon/api/handlers/analytics.js +1 -1
- package/dist/daemon/api/handlers/app-profiles.d.ts +2 -2
- package/dist/daemon/api/handlers/app-profiles.js +1 -1
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/api/handlers/bash.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.d.ts +3 -0
- package/dist/daemon/api/handlers/broker-ops.js +1 -1
- package/dist/daemon/api/handlers/broker-recovery.js +1 -1
- package/dist/daemon/api/handlers/broker-signals.d.ts +0 -3
- package/dist/daemon/api/handlers/broker-signals.js +2 -2
- package/dist/daemon/api/handlers/canvas.js +4 -4
- package/dist/daemon/api/handlers/charges.d.ts +1 -1
- package/dist/daemon/api/handlers/charges.js +1 -1
- package/dist/daemon/api/handlers/conversations.d.ts +2 -0
- package/dist/daemon/api/handlers/conversations.js +3 -0
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/daemon.js +1 -1
- package/dist/daemon/api/handlers/delivery.d.ts +1 -1
- package/dist/daemon/api/handlers/delivery.js +1 -1
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/exec.js +1 -1
- package/dist/daemon/api/handlers/execution-start.d.ts +2 -0
- package/dist/daemon/api/handlers/execution-start.js +2 -0
- package/dist/daemon/api/handlers/files.js +1 -1
- package/dist/daemon/api/handlers/git-credential.d.ts +11 -0
- package/dist/daemon/api/handlers/git-credential.js +1 -0
- package/dist/daemon/api/handlers/hook-exec.d.ts +1 -1
- package/dist/daemon/api/handlers/hook-exec.js +1 -1
- package/dist/daemon/api/handlers/human-requests.js +1 -1
- package/dist/daemon/api/handlers/inbox.js +1 -1
- package/dist/daemon/api/handlers/messages.js +1 -1
- package/dist/daemon/api/handlers/model-config.js +1 -1
- package/dist/daemon/api/handlers/modelauth.d.ts +1 -1
- package/dist/daemon/api/handlers/modelauth.js +1 -1
- package/dist/daemon/api/handlers/node-events.d.ts +6 -0
- package/dist/daemon/api/handlers/node-events.js +3 -3
- package/dist/daemon/api/handlers/node-outcomes.js +1 -1
- package/dist/daemon/api/handlers/node-records.js +3 -2
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/handlers/objects.js +3 -3
- package/dist/daemon/api/handlers/profiles.js +1 -1
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/handlers/reports.js +9 -13
- package/dist/daemon/api/handlers/reviews.js +1 -1
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/dist/daemon/api/handlers/settings.d.ts +2 -0
- package/dist/daemon/api/handlers/settings.js +1 -0
- package/dist/daemon/api/handlers/structured-requests.js +1 -1
- package/dist/daemon/api/handlers/vendor-status.d.ts +1 -1
- package/dist/daemon/api/handlers/vendor-status.js +1 -1
- package/dist/daemon/api/handlers/worktree.js +1 -1
- package/dist/daemon/api/map.d.ts +2 -2
- package/dist/daemon/api/map.js +2 -2
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/reader.d.ts +8 -1
- package/dist/daemon/api/reader.js +1 -1
- package/dist/daemon/api/router.d.ts +4 -2
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.d.ts +15 -3
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/api/vendor-signin.d.ts +2 -1
- package/dist/daemon/api/vendor-signin.js +20 -20
- package/dist/daemon/api/viewer-presence.d.ts +20 -6
- package/dist/daemon/api/viewer-presence.js +2 -1
- package/dist/daemon/broker-signals/bus.d.ts +48 -11
- package/dist/daemon/broker-signals/bus.js +1 -1
- package/dist/daemon/cron/armed-context.js +1 -1
- package/dist/daemon/cron/lease-recovery.js +1 -1
- package/dist/daemon/cron/live-runs.d.ts +6 -0
- package/dist/daemon/cron/live-runs.js +1 -1
- package/dist/daemon/cron/run-socket.js +1 -1
- package/dist/daemon/cron-run.js +2 -2
- package/dist/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/crtrd.d.ts +10 -0
- package/dist/daemon/crtrd.js +18 -17
- package/dist/daemon/delivery/run-delivery.d.ts +1 -1
- package/dist/daemon/delivery/run-delivery.js +1 -1
- package/dist/daemon/due-sources.d.ts +1 -1
- package/dist/daemon/due-sources.js +1 -1
- package/dist/daemon/due.js +7 -6
- package/dist/daemon/fleet.d.ts +6 -6
- package/dist/daemon/fleet.js +9 -9
- package/dist/daemon/park-activity.d.ts +2 -1
- package/dist/daemon/park-activity.js +1 -1
- package/dist/daemon/park-pending.d.ts +20 -13
- package/dist/daemon/park-pending.js +3 -1
- package/dist/daemon/profile-delete.js +1 -1
- package/dist/daemon/reconcilers/bash-deadline.d.ts +15 -10
- package/dist/daemon/reconcilers/bash-deadline.js +1 -3
- package/dist/daemon/reconcilers/broker-supervision.d.ts +13 -3
- package/dist/daemon/reconcilers/broker-supervision.js +4 -4
- package/dist/daemon/reconcilers/cron-lane.d.ts +0 -1
- package/dist/daemon/reconcilers/cron-lane.js +1 -1
- package/dist/daemon/reconcilers/fault-retry-backstop.d.ts +2 -9
- package/dist/daemon/reconcilers/fault-retry-backstop.js +1 -1
- package/dist/daemon/reconcilers/grant-effects.d.ts +8 -0
- package/dist/daemon/reconcilers/grant-effects.js +1 -0
- package/dist/daemon/reconcilers/grove-instance-sweep.d.ts +1 -3
- package/dist/daemon/reconcilers/grove-instance-sweep.js +1 -1
- package/dist/daemon/reconcilers/ledger-r2.d.ts +27 -0
- package/dist/daemon/reconcilers/ledger-r2.js +7 -0
- package/dist/daemon/reconcilers/ledger-sandbox.d.ts +55 -0
- package/dist/daemon/reconcilers/ledger-sandbox.js +9 -0
- package/dist/daemon/reconcilers/ledger-storage.d.ts +22 -0
- package/dist/daemon/reconcilers/ledger-storage.js +5 -0
- package/dist/daemon/reconcilers/live-obligation.d.ts +3 -2
- package/dist/daemon/reconcilers/live-obligation.js +1 -1
- package/dist/daemon/reconcilers/managed-worktree-sweep.d.ts +0 -5
- package/dist/daemon/reconcilers/managed-worktree-sweep.js +1 -1
- package/dist/daemon/reconcilers/node-deadline.d.ts +2 -1
- package/dist/daemon/reconcilers/node-deadline.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -2
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +31 -0
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -0
- package/dist/daemon/reconcilers/node-lifecycle/requested-stops.d.ts +5 -0
- package/dist/daemon/reconcilers/node-lifecycle/requested-stops.js +2 -0
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
- package/dist/daemon/reconcilers/pending-review-submit.js +1 -1
- package/dist/daemon/reconcilers/recipe-image.d.ts +49 -0
- package/dist/daemon/reconcilers/recipe-image.js +7 -0
- package/dist/daemon/reconcilers/row-claim.d.ts +19 -0
- package/dist/daemon/reconcilers/row-claim.js +2 -0
- package/dist/daemon/reconcilers/storage-due-work.d.ts +18 -3
- package/dist/daemon/reconcilers/storage-due-work.js +2 -1
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -13
- package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
- package/dist/daemon/reconcilers/structured-request-lane.js +1 -1
- package/dist/daemon/reconcilers/throttled-lane.d.ts +11 -0
- package/dist/daemon/reconcilers/throttled-lane.js +1 -0
- package/dist/daemon/review/finish.js +1 -1
- package/dist/daemon/run-notices.d.ts +3 -0
- package/dist/daemon/run-notices.js +1 -1
- package/dist/daemon/runs/append-section.d.ts +4 -1
- package/dist/daemon/runs/append-section.js +2 -2
- package/dist/daemon/runs/observer.d.ts +3 -2
- package/dist/daemon/runs/observer.js +3 -2
- package/dist/daemon/runs/view-transport.d.ts +19 -0
- package/dist/daemon/runs/view-transport.js +1 -0
- package/dist/daemon/structured-requests/boot.d.ts +2 -1
- package/dist/daemon/structured-requests/boot.js +1 -1
- package/dist/daemon/structured-requests/effects.d.ts +4 -3
- package/dist/daemon/structured-requests/effects.js +1 -1
- package/dist/daemon/structured-requests/notifier.d.ts +2 -0
- package/dist/daemon/structured-requests/notifier.js +1 -1
- package/dist/daemon/structured-requests/sidecar.d.ts +4 -3
- package/dist/daemon/structured-requests/sidecar.js +1 -1
- package/dist/launcher/validate.js +1 -1
- package/dist/migrations/004-canvas-documents/index.js +2 -2
- package/dist/pi-extensions/broker-local.d.ts +3 -3
- package/dist/pi-extensions/broker-local.js +1 -2
- package/dist/pi-extensions/canvas-bash-valve.js +6 -6
- package/dist/pi-extensions/canvas-context-intro.js +2 -2
- package/dist/pi-extensions/canvas-elapsed.js +2 -2
- package/dist/pi-extensions/canvas-goal-capture.js +1 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
- package/dist/pi-extensions/canvas-review-boundary.d.ts +11 -1
- package/dist/pi-extensions/canvas-review-boundary.js +3 -3
- package/dist/pi-extensions/canvas-stophook.js +1 -1
- package/dist/pi-extensions/canvas-structured-output.d.ts +5 -3
- package/dist/pi-extensions/canvas-structured-output.js +6 -11
- package/dist/pi-extensions/canvas-tool-guide.d.ts +9 -2
- package/dist/pi-extensions/canvas-tool-guide.js +6 -6
- package/dist/shared/inbox-entry-body.js +7 -7
- package/dist/types.d.ts +12 -0
- package/docs/sdk/release-notes.md +4 -0
- package/package.json +7 -4
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- package/scripts/git-credential-crtr +33 -0
- package/scripts/sandbox-bootstrap.sh +54 -0
- package/scripts/sandbox-start.mjs +320 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +0 -78
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +0 -1179
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +0 -1406
- package/dist/core/conversation-store/file-store.d.ts +0 -37
- package/dist/core/conversation-store/file-store.js +0 -9
- package/dist/core/conversation-store/listener.d.ts +0 -4
- package/dist/core/conversation-store/listener.js +0 -8
- package/dist/core/scoped-state/profiles.d.ts +0 -27
- package/dist/core/scoped-state/profiles.js +0 -1
- package/dist/core/scoped-state/providers.d.ts +0 -24
- package/dist/core/scoped-state/providers.js +0 -1
- package/dist/core/scoped-state/settings.d.ts +0 -28
- package/dist/core/scoped-state/settings.js +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var
|
|
1
|
+
var p=Object.defineProperty;var o=(r,e)=>p(r,"name",{value:e,configurable:!0});import{randomBytes as N,randomUUID as E}from"node:crypto";import{join as I}from"node:path";import{cancelCronsOnWake as b}from"../canvas/crons.js";import{withCanvasWrite as c,openDb as v}from"../canvas/db.js";import{getRow as k}from"../canvas/canvas.js";import{messagesDir as h}from"../canvas/paths.js";import{settleDeadControllerWait as R,settleMatchingControllerWait as D}from"../canvas/waits.js";import{generateOperationId as S,operationIdContext as T,validateOperationId as A}from"../events/operation-id.js";import{clipBody as O}from"../../shared/inbox-entry-body.js";function u(){let r;do r=N(16).toString("hex");while(r==="00000000000000000000000000000000");return r}o(u,"newId");function x(r,e){const n=JSON.stringify(r);if(n===void 0)throw new TypeError(`${e} must be JSON-serializable`);return n}o(x,"stringify");async function L(r,e){return e!=="normal"&&e!=="deferred"?e:(await k(r))?.lifecycle==="terminal"?"urgent":e}o(L,"effectiveTier");function C(r,e){return I(h(r),`${e.replace(/[^0-9]/g,"")}-${E().slice(0,6)}.md`)}o(C,"overflowBodyPath");function J(r){if(typeof r!="object"||r===null)return"";const e=r.body;return typeof e=="string"?e.trim():""}o(J,"inlineBody");async function y(r,e){const n=e.messageId??u(),t=Number((await r.prepare("UPDATE message_sequence SET seq = seq + 1 WHERE id = 1 RETURNING seq").get()).seq);return await r.prepare(`INSERT INTO messages
|
|
2
2
|
(message_id, seq, operation_id, created_at, sender_node_id, sender_name, kind, label, body_ref, body_json, disposition,
|
|
3
3
|
event, object_id, level, actor_id, cause_message_id, depth)
|
|
4
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(t,
|
|
4
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(n,t,e.operationId,e.createdAt,e.senderNodeId,e.senderName,e.kind,e.label,e.bodyRef,e.bodyJson===void 0?null:x(e.bodyJson,"message body"),e.disposition,e.event?.event??null,e.event?.objectId??null,e.event?.level??null,e.event?.actorId??null,e.causeMessageId??null,e.depth??0),n}o(y,"insertMessage");async function f(r,e,n,t,a,d,s){const i=u();return await r.prepare(`INSERT INTO deliveries
|
|
5
5
|
(delivery_id, message_id, destination_id, urgency, wake, state, idempotency_key, created_at, updated_at)
|
|
6
|
-
VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?)`).run(
|
|
7
|
-
FROM deliveries d JOIN messages m ON m.message_id = d.message_id WHERE d.idempotency_key = ?`).get(e.idempotencyKey);if(
|
|
8
|
-
WHERE w.node_id = ? AND w.kind = 'controller' AND w.target_id = ? AND w.satisfied_at IS NULL`).get(r,e);if(a===void 0||a.controller_status==="active"||a.controller_status==="idle")return null;const d=await
|
|
6
|
+
VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?)`).run(i,e,n,t,a,s??null,d,d),i}o(f,"insertNodeDelivery");function w(r,e){const n=new Date().toISOString(),t=r.operationId===void 0?T.current()??S():A(r.operationId);let a=r.bodyRef??null,d=r.bodyJson;if(a===null&&e!==null&&r.senderNodeId!=="human"){const s=J(d),i=O(s);i.clipped&&(a=C(e,n),d={...d,body:i.text,body_full:s})}return{createdAt:n,operationId:t,bodyRef:a,bodyJson:d}}o(w,"finalizeNodeMail");async function U(r,e){const n=w(e,r);return await c(async t=>{const a=await t.prepare("SELECT run_id FROM nodes WHERE node_id = ?").get(r);if(a?.run_id&&(await t.prepare("SELECT deleting_at FROM nodes WHERE node_id = ?").get(a.run_id))?.deleting_at)throw new Error("run is deleting");if(e.idempotencyKey!==void 0){const l=await t.prepare(`SELECT d.delivery_id, d.message_id, d.urgency, m.created_at
|
|
7
|
+
FROM deliveries d JOIN messages m ON m.message_id = d.message_id WHERE d.idempotency_key = ?`).get(e.idempotencyKey);if(l!==void 0)return{messageId:l.message_id,deliveryId:l.delivery_id,urgency:l.urgency,createdAt:l.created_at}}const d=Date.now(),s=await L(r,e.urgency),i=await y(t,{messageId:e.messageId,operationId:n.operationId,createdAt:n.createdAt,senderNodeId:e.senderNodeId??null,senderName:e.senderName??null,kind:e.kind,label:e.label,bodyRef:n.bodyRef,bodyJson:n.bodyJson,disposition:e.disposition??null}),_=await f(t,i,r,s,e.wake,d,e.idempotencyKey);return await D(r,e.senderNodeId??null,d,t),await e.withinEnqueue?.(t,i),e.wake===1&&s!=="deferred"&&await g(r),{messageId:i,deliveryId:_,urgency:s,createdAt:n.createdAt}})}o(U,"enqueueNodeMail");function z(r){switch(r){case"interrupt":return{urgency:"critical",wake:1};case"steer":return{urgency:"urgent",wake:1};case"after-turn":return{urgency:"normal",wake:1};case"next-turn":return{urgency:"deferred",wake:0}}}o(z,"listenDeliveryTier");async function m(r,e,n){const t={kind:"update",label:n.label??"notice",bodyJson:{body:n.body},urgency:n.tier,wake:n.wake},a=w(t,e),d=Date.now(),s=await y(r,{operationId:a.operationId,createdAt:a.createdAt,senderNodeId:null,senderName:null,kind:t.kind,label:t.label,bodyRef:a.bodyRef,bodyJson:a.bodyJson,disposition:null}),i=await f(r,s,e,n.tier,n.wake,d);return{messageId:s,deliveryId:i,urgency:n.tier,createdAt:a.createdAt}}o(m,"insertNodeNotice");async function g(r){await c(async e=>{for(const n of await b(r))await m(e,r,{body:`Your cron ${n.name}, next scheduled for ${n.fire_at}, was canceled by this wake. Re-create it if you still need it.`,label:"cron canceled",tier:"normal",wake:1})})}o(g,"consumeDeadlineOnWake");async function V(r,e,n){const t=await m(r,e,n);return n.wake===1&&n.tier!=="deferred"&&await g(e),t}o(V,"queueNodeNotice");async function G(r,e=Date.now()){return Number((await(await v()).prepare("UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE delivery_id = ? AND canceled_at IS NULL").run(e,e,r)).changes)===1}o(G,"cancelDelivery");async function Y(r,e={}){return await c(async n=>{const t=["d.destination_id = ?","d.canceled_at IS NULL","d.state IN ('pending', 'handed_off')"],a=[r];e.senderNodeId!==void 0&&(t.push("m.sender_node_id IS NOT DISTINCT FROM ?"),a.push(e.senderNodeId)),e.kind!==void 0&&(t.push("m.kind = ?"),a.push(e.kind));const d=await n.prepareUncached(`SELECT d.delivery_id FROM deliveries d JOIN messages m ON m.message_id = d.message_id WHERE ${t.join(" AND ")}`).all(...a);if(d.length===0)return[];const s=Date.now();return await n.prepareUncached(`UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE delivery_id IN (${d.map(()=>"?").join(",")})`).run(s,s,...d.map(i=>i.delivery_id)),d.map(i=>i.delivery_id)})}o(Y,"cancelPendingNodeMail");async function Q(r,e){return await c(async n=>{const t=Date.now(),a=await n.prepare(`SELECT target.status AS controller_status FROM waits w LEFT JOIN nodes target ON target.node_id = w.target_id
|
|
8
|
+
WHERE w.node_id = ? AND w.kind = 'controller' AND w.target_id = ? AND w.satisfied_at IS NULL`).get(r,e);if(a===void 0||a.controller_status==="active"||a.controller_status==="idle")return null;const d=await U(r,{kind:"urgent",label:"declared controller is no longer live",bodyJson:{body:`Controller ${e} is no longer live; the declared message wait was cleared.`},urgency:"urgent",wake:1});if(!await R(r,e,t,n))throw new Error(`controller wait changed while notifying ${r}`);return d})}o(Q,"enqueueControllerDeathNotice");export{G as cancelDelivery,Y as cancelPendingNodeMail,g as consumeDeadlineOnWake,L as effectiveTier,Q as enqueueControllerDeathNotice,U as enqueueNodeMail,w as finalizeNodeMail,y as insertMessage,z as listenDeliveryTier,u as newId,V as queueNodeNotice};
|
|
@@ -78,6 +78,21 @@ export declare function grantSyncState(grantId: string): Promise<GrantSyncState
|
|
|
78
78
|
export declare function grantSyncStateFor(grantee: string, sub: string): Promise<GrantSyncState | null>;
|
|
79
79
|
/** The mirrored grant `grantId`, whatever its status, or null when the mirror lacks it or the row is unparsable. */
|
|
80
80
|
export declare function grantRecord(grantId: string): Promise<GrantRecord | null>;
|
|
81
|
+
/** How a grant stands for the effects of a change (`grant-effects`): `inactive` is a missing, revoked, removed or expired grant;
|
|
82
|
+
* `lagging` is an active grant whose computed columns do not yet match its revision (it reads as no grant for requests but
|
|
83
|
+
* stops nothing, because the directory's second write completes it). `sub`, `grantee` and `removal_done_at` come from the row
|
|
84
|
+
* even when its computed columns lag; all are null when the row is gone. */
|
|
85
|
+
export interface GrantStanding {
|
|
86
|
+
standing: 'active' | 'inactive' | 'lagging';
|
|
87
|
+
sub: string | null;
|
|
88
|
+
grantee: string | null;
|
|
89
|
+
status: GrantRecord['status'] | null;
|
|
90
|
+
removal_done_at: string | null;
|
|
91
|
+
record: GrantRecord | null;
|
|
92
|
+
}
|
|
93
|
+
export declare function grantStanding(grantId: string): Promise<GrantStanding>;
|
|
94
|
+
/** Ids of the grants in `status` (this runtime's), for a catch-up pass. */
|
|
95
|
+
export declare function grantIdsWhere(where: string): Promise<string[]>;
|
|
81
96
|
/** Who a grant's model calls are charged to: `app` only for a mirrored grant the directory marked app-paid, else the person. */
|
|
82
97
|
export declare function payerOfGrant(grantId: string | null | undefined): Promise<'user' | 'app'>;
|
|
83
98
|
export declare function activeGrant(grantId: string): Promise<ActiveGrant | null>;
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
var
|
|
1
|
+
var T=Object.defineProperty;var r=(e,n)=>T(e,"name",{value:n,configurable:!0});import{createHash as N}from"node:crypto";import{parseScope as O}from"@north-light/identity";import{openDb as d,withCanvasWrite as j}from"../canvas/db.js";import{isUnsandboxedMachine as w}from"../runtime/unsandboxed.js";import{replaceWrittenBy as h,userSubject as A}from"../permissions/facts.js";import{GROUP_TYPE as b}from"../permissions/builtin.js";const R=["not_started","pending","ready","failed","unavailable"],k=["ultra","strong","medium","light"];function D(){const e=process.env.CRTR_RUNTIME;return e?{sql:" AND runtime = ?",args:[e]}:{sql:"",args:[]}}r(D,"runtimeScope");async function u(e,n,t=[]){const s=D();return await(await d()).prepare(`SELECT ${e} FROM grants WHERE ${n}${s.sql}`).all(...t,...s.args)}r(u,"grantRows");async function c(e,n,t=[]){return(await u(e,n,t))[0]}r(c,"grantRow");function l(e){if(!Array.isArray(e))throw new TypeError("grant scopes must be a list");return e.map(n=>{if(!n||typeof n!="object"||typeof n.scope!="string"||!["always","ask"].includes(n.mode))throw new TypeError("invalid grant scope entry");return{scope:n.scope,mode:n.mode}})}r(l,"entries");function C(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{state:n,message:t,next:s,step:i}=e;return typeof n!="string"||!R.includes(n)||t!==void 0&&typeof t!="string"||i!==void 0&&typeof i!="string"||typeof s!="string"||!s?null:{state:n,...t===void 0?{}:{message:t},next:s,...i===void 0?{}:{step:i}}}r(C,"dependencyOf");function m(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const n={};for(const[t,s]of Object.entries(e)){const i=C(s);i&&(n[t]=i)}return n}r(m,"dependenciesOf");function I(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{base_url:n,rungs:t}=e;if(typeof n!="string"||!n||!t||typeof t!="object"||Array.isArray(t))return null;const s={};for(const i of k){const a=t[i];typeof a=="string"&&a&&(s[i]=a)}return Object.keys(s).length?{base_url:n.replace(/\/+$/,""),rungs:s}:null}r(I,"inferenceEndpointOf");function y(e){if(e.computed_rev!==e.rev||e.scopes_json===null||e.resolved_scopes_json===null)throw new TypeError("grant columns are not computed for this revision");return{id:e.id,runtime:e.runtime,sub:e.sub,grantee:e.grantee,rev:e.rev,scopes:l(JSON.parse(e.scopes_json)),resolved_scopes:l(JSON.parse(e.resolved_scopes_json)),spend_cap:e.spend_cap,expires_at:e.expires_at,status:e.status,revoked_at:e.revoked_at,approved_at:e.approved_at,payer:e.payer,dependencies:m(JSON.parse(e.dependencies_json??"{}")),run_notice_url:e.run_notice_url??null,inference_endpoint:e.inference_endpoint_json?I(JSON.parse(e.inference_endpoint_json)):null,synced_at:e.synced_at??""}}r(y,"record");function p(e){if(!e||e.status!=="active")return null;try{return M(y(e))}catch(n){if(n instanceof TypeError||n instanceof SyntaxError)return null;throw n}}r(p,"active");function g(e){return e.grantee!=="app:terminal"&&w()}r(g,"mirrorOnly");async function B(){return(await F()).filter(e=>!g(e))}r(B,"localGrants");async function F(){return(await u("*","true")).flatMap(e=>{try{return[y(e)]}catch(n){if(n instanceof TypeError||n instanceof SyntaxError)return[];throw n}})}r(F,"allGrants");function L(e){return e.expires_at!==null&&!(Date.parse(e.expires_at)>Date.now())}r(L,"grantExpired");function M(e){if(e.status!=="active"||L(e))return null;try{const n=e.resolved_scopes.map(({scope:t})=>O(t));return{...e,resolved:e.resolved_scopes.flatMap(({mode:t},s)=>t==="always"?[n[s]]:[])}}catch(n){if(n instanceof TypeError||n instanceof SyntaxError)return null;throw n}}r(M,"activeRecord");function v(e){return Array.isArray(e)?`[${e.map(v).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().filter(n=>e[n]!==void 0).map(n=>`${JSON.stringify(n)}:${v(e[n])}`).join(",")}}`:JSON.stringify(e)??"null"}r(v,"canonicalJson");function X(e){const n={rev:e.rev,resolved_scopes:e.resolved_scopes,spend_cap:e.spend_cap,payer:e.payer,dependencies:e.dependencies??{},...e.inference_endpoint?{inference_endpoint:e.inference_endpoint}:{}};return N("sha256").update(v(n)).digest("base64url").slice(0,22)}r(X,"grantVersion");async function Y(e){return!!await c("1","id = ?",[e])}r(Y,"hasGrant");async function z(e){return await c("rev, status","id = ?",[e])??null}r(z,"grantSyncState");async function K(e,n){return await c("rev, status","grantee = ? AND sub = ?",[e,n])??null}r(K,"grantSyncStateFor");async function G(e){const n=await c("*","id = ?",[e]);if(!n)return null;try{return y(n)}catch(t){if(t instanceof TypeError||t instanceof SyntaxError)return null;throw t}}r(G,"grantRecord");async function Q(e){const n=await c("*","id = ?",[e]);if(!n)return{standing:"inactive",sub:null,grantee:null,status:null,removal_done_at:null,record:null};const t={sub:n.sub,grantee:n.grantee,status:n.status,removal_done_at:n.removal_done_at??null};if(n.status!=="active")return{...t,standing:"inactive",record:null};if(n.computed_rev!==n.rev)return{...t,standing:"lagging",record:null};const s=p(n);return s===null?{...t,standing:"inactive",record:null}:{...t,standing:"active",record:s}}r(Q,"grantStanding");async function Z(e){return(await u("id",e)).map(n=>n.id)}r(Z,"grantIdsWhere");async function ee(e){return(e?(await G(e))?.payer:null)??"user"}r(ee,"payerOfGrant");async function H(e){return p(await c("*","id = ?",[e]))}r(H,"activeGrant");async function ne(e,n){return p(await c("*","grantee = ? AND sub = ?",[e,n]))}r(ne,"grantFor");async function re(e){if(g({grantee:e}))return null;const n=await u("*","grantee = ? AND status = 'active'",[e]);if(n.length>1)throw new Error(`multiple active grants for ${e}`);return p(n[0])}r(re,"activeGrantForApp");async function te(e){const n=await H(e);return n&&n.run_notice_url&&!g(n)?{url:n.run_notice_url,sub:n.sub,grantee:n.grantee}:null}r(te,"runNoticeTarget");async function se(){const e=await u("*","grantee = 'app:terminal' AND status = 'active'");if(e.length>1)throw new Error("multiple active terminal grants");return p(e[0])}r(se,"terminalGrant");async function ae(){return(await(await d()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value??null}r(ae,"grantsSource");async function oe(e){return(await(await d()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value===e}r(oe,"hasSyncedGrants");async function ie(e){return(await e.prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value!=="local"?0:(await e.prepare("DELETE FROM runtime_state WHERE key = 'grants_source'").run(),Number((await e.prepare("DELETE FROM grants").run()).changes))}r(ie,"dropLocalGrantMirror");function ce(e){if(e===void 0)return[];if(!Array.isArray(e))throw new TypeError("invalid grant sync response");return e.map(n=>{const{team:t,members:s}=n??{};if(typeof t!="string"||!/^team:\S+$/.test(t)||!Array.isArray(s)||s.some(i=>typeof i!="string"||!/^user:[^\s*]+$/.test(i)))throw new TypeError("invalid grant sync response");return{team:t,members:s}})}r(ce,"teamsOf");async function ue(e,n,t,s=[]){const i=new Set;for(const a of e){if(i.has(a.id)||!a.id||!a.runtime||!a.sub||!a.grantee||!Number.isInteger(a.rev)||!["active","revoked","removed"].includes(a.status))throw new TypeError("invalid grant sync response");i.add(a.id),l(a.scopes),l(a.resolved_scopes)}await j(async a=>{let _=0;const f=[...i];_+=Number((await(f.length?a.prepareUncached(`DELETE FROM grants WHERE id NOT IN (${f.map(()=>"?").join(",")})`).run(...f):a.prepareUncached("DELETE FROM grants").run())).changes);const E=a.prepare(`INSERT INTO grants
|
|
2
2
|
(id, runtime, sub, grantee, rev, scopes_json, resolved_scopes_json, spend_cap, expires_at,
|
|
3
|
-
status, revoked_at, approved_at, payer, dependencies_json, run_notice_url, inference_endpoint_json, synced_at)
|
|
4
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
3
|
+
status, revoked_at, approved_at, payer, dependencies_json, run_notice_url, inference_endpoint_json, synced_at, computed_rev)
|
|
4
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5
|
+
ON CONFLICT (id) DO UPDATE SET runtime = excluded.runtime, sub = excluded.sub, grantee = excluded.grantee, rev = excluded.rev,
|
|
6
|
+
scopes_json = excluded.scopes_json, resolved_scopes_json = excluded.resolved_scopes_json, spend_cap = excluded.spend_cap,
|
|
7
|
+
expires_at = excluded.expires_at, status = excluded.status, revoked_at = excluded.revoked_at, approved_at = excluded.approved_at,
|
|
8
|
+
removal_done_at = CASE WHEN excluded.status = 'removed' AND grants.status = 'removed' THEN grants.removal_done_at END,
|
|
9
|
+
payer = excluded.payer, dependencies_json = excluded.dependencies_json, run_notice_url = excluded.run_notice_url,
|
|
10
|
+
inference_endpoint_json = excluded.inference_endpoint_json, synced_at = excluded.synced_at, computed_rev = excluded.computed_rev
|
|
11
|
+
WHERE (grants.runtime, grants.sub, grants.grantee, grants.rev, grants.scopes_json, grants.resolved_scopes_json, grants.spend_cap,
|
|
12
|
+
grants.expires_at, grants.status, grants.revoked_at, grants.approved_at, grants.payer, grants.dependencies_json,
|
|
13
|
+
grants.run_notice_url, grants.inference_endpoint_json, grants.computed_rev)
|
|
14
|
+
IS DISTINCT FROM (excluded.runtime, excluded.sub, excluded.grantee, excluded.rev, excluded.scopes_json, excluded.resolved_scopes_json,
|
|
15
|
+
excluded.spend_cap, excluded.expires_at, excluded.status, excluded.revoked_at, excluded.approved_at, excluded.payer,
|
|
16
|
+
excluded.dependencies_json, excluded.run_notice_url, excluded.inference_endpoint_json, excluded.computed_rev)`);for(const o of e)_+=Number((await E.run(o.id,o.runtime,o.sub,o.grantee,o.rev,JSON.stringify(o.scopes),JSON.stringify(o.resolved_scopes),o.spend_cap,o.expires_at,o.status,o.revoked_at,o.approved_at,o.payer,JSON.stringify(m(o.dependencies)),o.run_notice_url??null,o.inference_endpoint?JSON.stringify(o.inference_endpoint):null,o.synced_at,o.rev)).changes);await h(a,"directory",b,"member",s.flatMap(({team:o,members:x})=>x.map(S=>({resourceId:o,subject:A(S)})))),await a.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_source', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(t),_>0&&await a.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_generation', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(runtime_state.value AS INTEGER) + 1 AS TEXT)").run()})}r(ue,"replaceGrants");async function pe(){const e=await(await d()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_generation'").get();return e?Number(e.value):0}r(pe,"grantsGeneration");export{H as activeGrant,re as activeGrantForApp,M as activeRecord,F as allGrants,m as dependenciesOf,ie as dropLocalGrantMirror,L as grantExpired,ne as grantFor,Z as grantIdsWhere,G as grantRecord,Q as grantStanding,z as grantSyncState,K as grantSyncStateFor,X as grantVersion,pe as grantsGeneration,ae as grantsSource,Y as hasGrant,oe as hasSyncedGrants,I as inferenceEndpointOf,B as localGrants,g as mirrorOnly,ee as payerOfGrant,ue as replaceGrants,te as runNoticeTarget,ce as teamsOf,se as terminalGrant};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { SandboxRecord } from '../../launcher/spec.js';
|
|
2
|
-
import { type GrantRecord } from './mirror.js';
|
|
3
2
|
/** Work that outlives the sync that started it; the daemon keeps ownership of it during shutdown. */
|
|
4
3
|
export type Detach = (work: Promise<void>) => void;
|
|
5
4
|
/** Why a sandbox must stop under the current mirror, or null when every
|
|
@@ -8,6 +7,10 @@ export declare function exceeds(sandbox: Pick<SandboxRecord, 'uid' | 'nodeId' |
|
|
|
8
7
|
app: string;
|
|
9
8
|
status: string;
|
|
10
9
|
}>): Promise<string | null>;
|
|
10
|
+
/** After a confirmed revocation stop: the node waits for its next message,
|
|
11
|
+
* whose broker gets the current mounts (runtime spec §15), and its run reads
|
|
12
|
+
* idle rather than running. */
|
|
13
|
+
export declare function afterRevocationStop(nodeId: string): Promise<void>;
|
|
11
14
|
/** R26 from current state (runtime spec §15 "Ending revoked file access"):
|
|
12
15
|
* compare every live launcher sandbox's launched mounts with what the current
|
|
13
16
|
* mirror grants, and stop each one that holds more. Runs after every sync and
|
|
@@ -16,7 +19,8 @@ export declare function exceeds(sandbox: Pick<SandboxRecord, 'uid' | 'nodeId' |
|
|
|
16
19
|
export declare function sweepSandboxes(): Promise<{
|
|
17
20
|
completion: Promise<void>;
|
|
18
21
|
}>;
|
|
19
|
-
/** R26
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
|
|
22
|
+
/** R26 after a sync replaced the grant mirror (runtime spec §15): stop the launcher sandboxes that now hold more than the mirror
|
|
23
|
+
* grants. Everything else a grant change causes (waking `grant.get` holds, closing streams, discarding tokens, stopping the
|
|
24
|
+
* grant's nodes, the removal cascade, provider bundles) is done by the `grant:<id>` handlers of `daemon/reconcilers/grant-effects.ts`,
|
|
25
|
+
* which the sync's row writes fire. Resolves once the stops have started; their completion continues as detached work. */
|
|
26
|
+
export declare function onGrantSync(detach?: Detach): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var v=Object.defineProperty;var d=(o,e)=>v(o,"name",{value:e,configurable:!0});import{relative as b,sep as g}from"node:path";import{runningJobs as x}from"../graph/jobs.js";import{appSpace as y}from"../layout.js";import{getNode as f,recordSettledTurn as h}from"../canvas/index.js";import{transition as S}from"../runtime/lifecycle.js";import{openDb as l}from"../canvas/db.js";import{emitEvent as m}from"../events/emit.js";import{grantedMounts as E,isGrantedSource as R,mountAllowed as k}from"../spaces/mounts.js";import{permissionsForRun as w}from"../spaces/permissions.js";import{stopNodeSandboxes as I}from"../spaces/stop.js";import{activeGrantForApp as P}from"./mirror.js";import{launcherPresent as A,listSandboxes as C,stopSandboxPid as F}from"./sandboxes.js";import{checkCronFire as M}from"../../daemon/cron/authority.js";import{cronExecutionFor as N}from"../../daemon/cron/live-runs.js";import{cronExecutionsReady as O}from"../../daemon/cron/run-socket.js";import{flatMapAsync as T}from"../async-array.js";import{personId as W}from"../canvas/person.js";async function _(){return new Map((await(await l()).prepare("SELECT uid, app, status FROM app_users").all()).map(o=>[o.uid,{app:o.app,status:o.status}]))}d(_,"appUserOwners");async function D(o,e){e??=await _();const n=e.get(o.uid),p=o.nodeId.startsWith("cron-"),a=p?N(o.nodeId.slice(5)):void 0;if(p&&(a===void 0||!n))return"permission_revoked";if(a!==void 0&&n.status==="active"){const r=await M({grantee:a.grantee,sub:a.sub,grant_id:a.grantId,scopes:a.scopes});if(!r.allowed)return a.refusal??={reason:r.reason,message:r.message},"permission_revoked"}if(!n||n.app==="app:terminal")return null;if(n.status!=="active")return"app_removed";const t=await f(o.nodeId);try{let r;if(a!==void 0)r=await w(a.grantee,a.sub,a.grantId,a.scopes);else if(t&&t.grantee===n.app)r=await w(n.app,t.sub??W(),t.grant_id??null,t.scopes??null);else{const i=await P(n.app);if(!i)return"permission_revoked";r=await w(n.app,i.sub,i.id,null)}const c=E(r),u=y(n.app).root;return o.mounts.every(i=>{const s=b(u,i.source);return s===""||s!==".."&&!s.startsWith(`..${g}`)&&!s.startsWith(g)||!R(n.app,i.source)||k(i,c)})?null:"permission_revoked"}catch{return"permission_revoked"}}d(D,"exceeds");async function G(o){const e=await f(o);if(!e)return;(e.status==="active"||e.status==="idle")&&await S(o,"release");const n=new Date().toISOString();await h(o,n),e.run_id&&e.run_id!==o&&await(await l()).prepare("UPDATE nodes SET last_settled_at = COALESCE(last_settled_at, ?) WHERE node_id = ?").run(n,e.run_id)}d(G,"afterRevocationStop");async function L(o){const e=new Set,n=await f(o);n?.pi_pid!=null&&e.add(n.pi_pid);for(const p of await x(await l(),o))p.pgid!==null&&e.add(p.pgid);return e}d(L,"nodePids");async function U(){if(!A())return{completion:Promise.resolve()};await O();const o=await C(),e=await _(),n=await T(o,async t=>{const r=await D(t,e);return r?[{sandbox:t,reason:r}]:[]}),p=[],a=new Map;for(const t of n){m({level:"info",event:"daemon.grants.sandbox_stop",fields:{sandbox_node:t.sandbox.nodeId,pid:t.sandbox.pid,grantee:e.get(t.sandbox.uid).app,reason:t.reason}});const r=a.get(t.sandbox.nodeId)??[];r.push(t),a.set(t.sandbox.nodeId,r)}for(const[t,r]of a){const c=await f(t),u=c?await L(t):new Set,i=c!==null&&r.some(({sandbox:s})=>u.has(s.pid));p.push((async()=>{if(i){const s=await I(t,"permission_revoked",{holdRecovery:!0,after:d(()=>G(t),"after")});if(!s.confirmed)throw s.error??new Error(`stop of ${t} was not confirmed`)}for(const{sandbox:s}of r)(!i||!u.has(s.pid))&&await F(s.pid)})().catch(s=>{m({level:"error",event:"daemon.grants.sandbox_stop_failed",...c?{node_id:t}:{fields:{sandbox_node:t}},error:s})}))}return{completion:Promise.all(p).then(()=>{})}}d(U,"sweepSandboxes");async function se(o=e=>{}){try{const{completion:e}=await U();o(e)}catch(e){m({level:"error",event:"daemon.grants.sweep_failed",error:e})}}d(se,"onGrantSync");export{G as afterRevocationStop,D as exceeds,se as onGrantSync,U as sweepSandboxes};
|
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
/** Run the removal cascade for every `removed` grant whose app still has an
|
|
2
|
-
* active OS-user row, and once for each app in `transitions` (its grant became
|
|
3
|
-
* `removed` at this sync) even with no row. Serialized; each cascade is
|
|
4
|
-
* idempotent and marks the row `removed` last, so an interrupted one resumes. */
|
|
5
|
-
export declare function runRemovalCascades(transitions?: ReadonlySet<string>): Promise<void>;
|
|
6
1
|
/** Runtime spec §15 "On removal" (design flow 10). Audit and usage rows stay. */
|
|
7
|
-
export declare function removeApp(app: string): Promise<void>;
|
|
2
|
+
export declare function removeApp(app: string, sub?: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var _=Object.defineProperty;var E=(r,o)=>_(r,"name",{value:o,configurable:!0});import{rmSync as v}from"node:fs";import{join as S}from"node:path";import{deleteNodes as h}from"../canvas/canvas.js";import{withCanvasWrite as c,openDb as d}from"../canvas/db.js";import{cancelCron as O}from"../canvas/crons.js";import{emitEvent as p}from"../events/emit.js";import{appSpace as T,appsRoot as w,secretsDir as C}from"../layout.js";import{notifyRunDeleted as D}from"../runs/events.js";import{cancelOpenQuestions as L}from"../runs/questions.js";import{removeTreeBeneath as W}from"../spaces/open-beneath.js";import{markGranteeUploadsDeletePending as F,releaseRunUploads as H,turnSharesOffFor as N}from"../storage/tables.js";import{stopNodeSandboxes as M}from"../spaces/stop.js";import{activeAppUser as $,markAppUserRemoved as y}from"../spaces/users.js";import{launcherRequest as A}from"../../launcher/client.js";import{LaunchRefused as U}from"../../launcher/spec.js";import{localGrants as q}from"./mirror.js";import{launcherPresent as u,listSandboxes as I,stopSandboxPid as P}from"./sandboxes.js";import{filterAsync as B}from"../async-array.js";async function se(r,o){if(r==="app:terminal")throw new Error("app:terminal cannot be removed");const m=T(r),s=o===void 0||!(await q()).some(e=>e.grantee===r&&e.sub!==o&&e.status!=="removed"),i=s?await $(r):null;p({level:"info",event:"daemon.grants.removal_started",fields:{grantee:r,...o===void 0?{}:{sub:o},uid:i?.uid??null}});const t=o===void 0?{sql:"grantee = ?",args:[r]}:{sql:"grantee = ? AND sub = ?",args:[r,o]};for(const{node_id:e}of await(await d()).prepare(`SELECT node_id FROM node_rows WHERE ${t.sql} ORDER BY created DESC`).all(...t.args))await L({node_id:e},"the app was removed");const a=await c(async e=>(await e.prepare(`UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id IN (SELECT node_id FROM node_rows WHERE ${t.sql})`).run(new Date().toISOString(),...t.args),(await e.prepare(`SELECT node_id FROM node_rows WHERE ${t.sql} ORDER BY created DESC`).all(...t.args)).map(n=>n.node_id)));for(const{cron_id:e}of await(await d()).prepare(`SELECT cron_id FROM crons WHERE ${t.sql}`).all(...t.args))await O(e);const l=(await Promise.all(a.map(e=>M(e,"app_removed",{deleting:!0})))).filter(e=>!e.confirmed);if(l.length)throw new Error(`sandbox teardown could not be confirmed for ${l.length} node(s) of ${r}`);if(i&&u())for(const e of await I())e.uid===i.uid&&await P(e.pid);const R=await B(a,async e=>(await(await d()).prepare("SELECT run_id FROM nodes WHERE node_id = ?").get(e))?.run_id===e);await c(async e=>{if(s&&await N(r,e),await H(a,e),s&&await F(r,e),!a.length)return;const n=a.map(()=>"?").join(","),g=await e.prepareUncached(`SELECT message_id FROM messages WHERE sender_node_id IN (${n}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${n})`).all(...a,...a);await e.prepareUncached(`DELETE FROM deliveries WHERE destination_id IN (${n})`).run(...a);for(const{message_id:f}of g)await e.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(f,f)}),await h(a);for(const e of R)D(e);if(s&&(v(S(C(),"env",m.root.slice(w().length+1)),{recursive:!0,force:!0}),W(w(),m.root)),i&&u())try{await A({operation:"user.delete",uid:i.uid})}catch(e){if(!(e instanceof U&&e.refusal==="invalid_uid"))throw e}i&&await y(i.uid),p({level:"info",event:"daemon.grants.removal_completed",fields:{grantee:r,...o===void 0?{}:{sub:o},uid:i?.uid??null,nodes:a.length}})}E(se,"removeApp");export{se as removeApp};
|
package/dist/core/grants/sync.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var b=Object.defineProperty;var n=(e,r)=>b(e,"name",{value:r,configurable:!0});import{createPrivateKey as E,randomUUID as S,sign as j}from"node:crypto";import{readFileSync as x}from"node:fs";import{join as $}from"node:path";import{crtrHome as T}from"../canvas/paths.js";import{isUnsandboxedMachine as g}from"../runtime/unsandboxed.js";import{emitEvent as p}from"../events/emit.js";import{describeOutboundFailure as A,outboundFetch as v,outboundJson as h,outboundRefusal as k}from"../events/outbound.js";import{dependenciesOf as O,inferenceEndpointOf as P,replaceGrants as N,teamsOf as R}from"./mirror.js";import{onGrantSync as G}from"./on-sync.js";import{adoptInstallPerson as J}from"../scoped-state/migrate.js";import{PersonOutsideRequestError as B,PersonUnresolvedError as D,personId as F,resolvePerson as I,retireLocalPerson as M,setPersonId as U}from"../canvas/person.js";const ae=300;function q(e,r){const t=Math.floor(Date.now()/1e3),o=Buffer.from(JSON.stringify({alg:"ES256",typ:"JWT",kid:e.kid})).toString("base64url"),d=Buffer.from(JSON.stringify({iss:e.client_id,sub:e.client_id,aud:r,iat:t,exp:t+300,jti:S()})).toString("base64url"),i=`${o}.${d}`,s=j("sha256",Buffer.from(i),{key:E({key:e.jwk,format:"jwk"}),dsaEncoding:"ieee-p1363"});return`${i}.${s.toString("base64url")}`}n(q,"assertion");function y(){const e=JSON.parse(x($(T(),"secrets","runtime-client.json"),"utf8"));if(!e.jwk||e.alg!=="ES256"||!e.kid||!e.client_id||!e.runtime||e.jwk.kid!==e.kid||e.jwk.kty!=="EC"||e.jwk.crv!=="P-256"||!e.jwk.d)throw new TypeError("invalid runtime client key");return e}n(y,"runtimeClient");function ce(e=y()){return e.runtime.startsWith("runtime:")?e.runtime.slice(8):e.runtime}n(ce,"runtimeId");function C(e){return`This machine was removed from your Northlight account. To use it again, create a join token at ${e.replace(/\/$/,"")}/account/machines, and run \`crtr sys join\` from a plain terminal.`}n(C,"machineRevokedMessage");class w extends Error{static{n(this,"MachineRevokedError")}constructor(r){super(C(r)),this.name="MachineRevokedError"}}async function L(e,r){const t=y(),o=`${e.replace(/\/$/,"")}/token`,d=new URLSearchParams({grant_type:"client_credentials",client_id:t.client_id,client_assertion_type:"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",client_assertion:q(t,o),...r===void 0?{}:{audience:r}}),i=await v("directory_token",o,{method:"POST",body:d,signal:AbortSignal.timeout(1e4)});if(!i.ok)throw i.status===401&&g()&&await W(i)?new w(e):k("directory_token",o,`directory token request failed: ${i.status}`);const s=await h("directory_token",o,i);if(typeof s.access_token!="string")throw new TypeError("directory token response has no access_token");return{access_token:s.access_token,expires_at:z(s.access_token,s.expires_in)}}n(L,"mintDirectoryToken");async function W(e){try{return(await e.json()).error==="invalid_client"}catch{return!1}}n(W,"isInvalidClient");function z(e,r){if(typeof r=="number"&&Number.isFinite(r)&&r>0)return Date.now()+r*1e3;try{const t=JSON.parse(Buffer.from(e.split(".")[1]??"","base64url").toString("utf8"));if(typeof t.exp=="number")return t.exp*1e3}catch{}return Date.now()+3e5}n(z,"tokenExpiry");function H(e,r){if(!e||typeof e!="object")throw new TypeError("invalid grant sync response");const t=e,o=r.runtime.startsWith("runtime:")?r.runtime:`runtime:${r.runtime}`;if(!(t.runtime===o||g()&&typeof t.runtime=="string"&&t.runtime.startsWith("runtime:")&&t.grantee!=="app:terminal")||typeof t.id!="string"||typeof t.sub!="string"||typeof t.grantee!="string"||!Number.isInteger(t.rev)||!Array.isArray(t.scopes)||!Array.isArray(t.resolved_scopes)||!["active","revoked","removed"].includes(t.status??"")||typeof t.synced_at!="string")throw new TypeError("invalid grant sync response");const s=e.revisions?.find(a=>a.rev===t.rev)?.approved_at;if(typeof s!="string")throw new TypeError("grant revision has no approval time");const l=t.payer==="app"?"app":"user";return{...t,spend_cap:t.spend_cap??null,expires_at:t.expires_at??null,revoked_at:t.revoked_at??null,approved_at:s,payer:l,dependencies:O(t.dependencies),inference_endpoint:l==="app"?P(t.inference_endpoint):null,run_notice_url:typeof t.run_notice_url=="string"&&t.run_notice_url!==""?t.run_notice_url:null}}n(H,"responseGrant");async function K(){try{F();return}catch(t){if(t instanceof B)return;if(!(t instanceof D))throw t}const e=await I();if(e===null)return;U(e.id),await J(e.id);const r=await M();p({level:"info",event:"daemon.person.resolved",fields:{person_id:e.id,source:e.source}}),p({level:"info",event:"daemon.person.retired",fields:{person_id:r.person,retired:r.retired,columns:r.columns}})}n(K,"resolveLatePerson");let _;function ue(e){return _?_(e):Promise.resolve()}n(ue,"requestGrantSync");function de(e,r=t=>{}){let t;const o=e.replace(/\/$/,""),d=n(async()=>{const a=y(),c=await L(o),f=await v("grant_sync",`${o}/v1/runtime/grants`,{headers:{Authorization:`Bearer ${c.access_token}`},signal:AbortSignal.timeout(1e4)});if(!f.ok)throw k("grant_sync",`${o}/v1/runtime/grants`,`directory grant sync failed: ${f.status}`);const u=await h("grant_sync",`${o}/v1/runtime/grants`,f);if(!Array.isArray(u.grants)||typeof u.synced_at!="string")throw new TypeError("invalid grant sync response");await N(u.grants.map(m=>H(m,a)),u.synced_at,o,R(u.teams)),p({level:"info",event:"daemon.grants.synced",fields:{grants:u.grants.length,synced_at:u.synced_at}}),await K();try{await G(r)}catch(m){p({level:"error",event:"daemon.grants.on_sync_failed",error:m})}},"run");let i;const s=n(()=>{const a=d().catch(c=>{throw c instanceof w?p({level:"error",event:"daemon.grants.machine_revoked",error:c}):p({level:"error",event:"daemon.grants.sync_failed",error:c,fields:A(c)}),c}).finally(()=>{t===a&&(t=void 0)});return t=a,a},"start"),l=n((a={})=>t?a.fresh?(i??=t.catch(()=>{}).then(()=>(i=void 0,t??s())),i):t:s(),"sync");return _=l,l}n(de,"createGrantSync");export{ae as GRANT_SYNC_INTERVAL_S,w as MachineRevokedError,q as assertion,de as createGrantSync,C as machineRevokedMessage,L as mintDirectoryToken,ue as requestGrantSync,y as runtimeClient,ce as runtimeId};
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
/** A `grant.get` hold on one grant. It is registered before the caller reads the mirror and stays registered until `release`, so a
|
|
2
|
+
* wake that lands at any moment of the hold (including while a mirror read is outstanding) is remembered: the next `changed()`
|
|
3
|
+
* answers at once. */
|
|
4
|
+
export interface GrantHold {
|
|
5
|
+
/** Resolve when the grant was notified since the last `changed()` returned, or after `seconds`. */
|
|
6
|
+
changed(seconds: number): Promise<void>;
|
|
7
|
+
release(): void;
|
|
8
|
+
}
|
|
9
|
+
/** Register a hold on grant `grantId`. The timer of a pending `changed()` is unref'd: a held request never keeps the process alive. */
|
|
10
|
+
export declare function holdGrant(grantId: string): GrantHold;
|
|
11
|
+
/** Wake the long-polls held on grant `grantId`: its row changed. Each woken `grant.get` re-reads the grant and waits again unless
|
|
12
|
+
* its version moved, so a change that leaves the version alone never ends a hold early. Never triggers a sync. */
|
|
13
|
+
export declare function wakeGrantWaiters(grantId: string): void;
|
|
14
|
+
/** Wake every held long-poll (the listen connection restarted: changes may have gone unheard). */
|
|
15
|
+
export declare function wakeAllGrantWaiters(): void;
|
|
10
16
|
/** The app listener is closing: drop every held long-poll and its timer. Its
|
|
11
17
|
* socket is being destroyed, so nothing is answered. */
|
|
12
18
|
export declare function releaseGrantWaiters(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var u=Object.defineProperty;var
|
|
1
|
+
var u=Object.defineProperty;var n=(e,t)=>u(e,"name",{value:t,configurable:!0});const o=new Map;function w(e,t){const r=o.get(e);r&&(r.delete(t),r.size||o.delete(e))}n(w,"release");function k(e){let t=!1,r,s;const a=n(()=>{clearTimeout(s);const f=r;r=void 0,f?.()},"settle"),c={wake:n(()=>{t=!0,a()},"wake"),cancel:n(()=>{clearTimeout(s)},"cancel")};let i=o.get(e);return i||o.set(e,i=new Set),i.add(c),{changed:n(f=>new Promise(l=>{if(t){t=!1,l();return}r=n(()=>{t=!1,l()},"resolveWait"),s=setTimeout(a,f*1e3),s.unref()}),"changed"),release:n(()=>{w(e,c),a()},"release")}}n(k,"holdGrant");function d(e){for(const t of[...o.get(e)??[]])t.wake()}n(d,"wakeGrantWaiters");function m(){for(const e of[...o.keys()])d(e)}n(m,"wakeAllGrantWaiters");function x(){for(const e of[...o.values()])for(const t of[...e])t.cancel();o.clear()}n(x,"releaseGrantWaiters");function W(){let e=0;for(const t of o.values())e+=t.size;return e}n(W,"grantWaiterCount");export{W as grantWaiterCount,k as holdGrant,x as releaseGrantWaiters,m as wakeAllGrantWaiters,d as wakeGrantWaiters};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
var
|
|
2
|
-
VALUES (?, ?, ?, ?)`).run(d.id,o,m,
|
|
1
|
+
var x=Object.defineProperty;var l=(e,t)=>x(e,"name",{value:t,configurable:!0});import{createHash as C,randomBytes as $,timingSafeEqual as P}from"node:crypto";import{CrtrError as j,usage as f}from"../errors.js";import{ExitCode as S}from"../../types.js";import{createObject as J,liveDocumentNamed as q}from"./objects.js";import{invalidateCustomKind as A}from"./kinds/registry.js";import{publish as O,HOP_LIMIT as F,HopLimitError as M}from"./events.js";import{checkoutsForCwd as N}from"./repo-sync/repos.js";import{onQueueSignal as B,signalQueue as R}from"./receivers.js";import{receiverEndpoint as K,isPublicReceiverAddress as V}from"./receiver-url.js";import{isIP as G}from"node:net";import{KIND as k,LOCAL as D,MAX_KIND_EVENTS as Q,RESERVED_KIND_ROOTS as Y,eventCatalogReasons as z}from"../plugin-objects-manifest.js";import{check as L}from"../permissions/evaluator.js";import{userOf as X}from"../permissions/evaluator.js";import{nodePlace as Z}from"../permissions/ceiling.js";import{PermissionError as ee}from"../permissions/errors.js";import{writeActsFor as te}from"../permissions/facts.js";async function ne(e,t,n){return t.class!=="person"&&t.app.id===n.id?!0:await L(e,t,"edit",n.id)}l(ne,"mayActAs");async function ie(e,t,n){return await L(e,t,"read",n.id)}l(ie,"customReachable");const se=["public","private"];function h(e,t){throw new j(e,t,S.GENERAL)}l(h,"conflict");function b(e){return C("sha256").update(e).digest("hex")}l(b,"hash");function H(){return`crtr_pub_${$(32).toString("base64url")}`}l(H,"issueToken");function ae(e){if(e==null)return null;if(e==="queue")return JSON.stringify(e);if(typeof e!="object"||Array.isArray(e))throw f("invalid custom receiver; expected queue, an https url, or argv and absolute cwd");if("url"in e&&typeof e.url=="string")try{const n=K(e.url).hostname.replace(/^\[|\]$/g,"");if(G(n)&&!V(n))throw new Error("non-public IP address");return JSON.stringify({url:e.url})}catch(t){throw f(`invalid custom receiver: ${String(t)}`)}if("argv"in e&&Array.isArray(e.argv)&&e.argv.length>0&&e.argv.every(t=>typeof t=="string")&&typeof e.cwd=="string"&&e.cwd.startsWith("/"))return JSON.stringify(e);throw f("invalid custom receiver; url must be https and argv requires a command and absolute cwd")}l(ae,"receiverJson");async function re(e,t){return t.class==="node"?(await Z(e,t.node.id))?.isolated===!0:t.class==="app"||t.class==="object"?t.place?.isolated===!0:!1}l(re,"inIsolatedRun");async function Ne(e,t,n){const{kind:i,name:a,owner:s,description:o,events:u,receiver:c,rotateToken:w,privacy:p}=n;if(p!==void 0&&!se.includes(p))throw f(`invalid privacy ${JSON.stringify(p)}`);if(await ne(e,t,s)||h("forbidden",`cannot act as ${s.id}`),await re(e,t))throw new ee(403,"isolated_run","a custom object cannot be registered from an isolated run");if(!k.test(i)||Y.includes(i.split(".")[0]))throw f(`invalid or reserved kind ${i}`);if(!a.trim()||a!==a.trim()||a.includes("\0"))throw f(`invalid object name ${JSON.stringify(a)}`);const m=ae(c);if(c!=="queue"&&c!==null&&c!==void 0&&"argv"in c&&t.class!=="person"&&t.class!=="node"&&h("argv_forbidden","argv receiver requires a person or node principal"),u===void 0){const d=await e.prepare("SELECT source FROM object_kinds WHERE name = ?").get(i);if(d?.source!=="manifest"&&d?.source!=="peer")throw f("events is required unless the kind is declared by an installed plugin")}else if(Object.keys(u).length>Q||Object.entries(u).some(([d,_])=>!D.test(d)||!Number.isInteger(_?.level)||_.level<1||_.level>10||_.supersedes!==void 0&&typeof _.supersedes!="boolean"||_.ends_wait!==void 0&&typeof _.ends_wait!="boolean"))throw f("invalid custom event catalog");const r=await q(e,s.id,a);if(r&&!await ie(e,t,r)&&h("forbidden",`cannot act as ${s.id}`),r&&t.class!=="person"&&t.class!=="node"){const d=(await e.prepare("SELECT receiver_json FROM custom_objects WHERE object = ?").get(r.id))?.receiver_json;d&&typeof JSON.parse(d)=="object"&&"argv"in JSON.parse(d)&&h("argv_forbidden","argv receiver requires a person or node principal")}r&&r.type!==i&&h("name_taken",`${s.name}/${a} is already held by ${r.id}`);const g=u===void 0?null:await oe(e,{name:i,owner:s,description:o,events:u,read:null,source:"register"});if(!r){const d=await J(e,{type:i,kind:null,owner:s.id,privacy:p??"public",name:a,preview:""});s.type==="app"&&await te(e,d.id,await X(e,t));const _=H();return await e.prepare(`INSERT INTO custom_objects (object, description, receiver_json, token_hash)
|
|
2
|
+
VALUES (?, ?, ?, ?)`).run(d.id,o,m,b(_)),t.class==="node"&&await e.prepare("UPDATE custom_objects SET registered_by = ? WHERE object = ?").run(t.node.id,d.id),m!==null&&await e.prepare("UPDATE objects SET reception = 2 WHERE id = ?").run(d.id),await O(e,{event:"created",object:d,actor:t.class==="object"?t.object:t.class==="person"?t.person:t.class==="app"?t.app:t.node,attrs:{}},async U=>await N(e,U)),{object:d,token:_,created:!0,changed:[]}}const y=await e.prepare("SELECT description, receiver_json FROM custom_objects WHERE object = ?").get(r.id);y||h("name_taken",`${s.name}/${a} is not a custom object`);const E=[];if(y.description!==o&&E.push("description"),y.receiver_json!==m&&E.push("receiver"),g?.changed.includes("events")&&E.push("events"),g?.changed.includes("description")&&!E.includes("description")&&E.push("description"),p!==void 0&&r.privacy!==p&&E.push("privacy"),w&&E.push("token"),E.length===0)return{object:r,created:!1,changed:E};const v=w?H():void 0,T=E.includes("privacy")?{...r,privacy:p}:r;if(T!==r&&await e.prepare("UPDATE objects SET privacy = ? WHERE id = ?").run(p,r.id),await e.prepare("UPDATE custom_objects SET description = ?, receiver_json = ?, token_hash = COALESCE(?, token_hash) WHERE object = ?").run(o,m,v===void 0?null:b(v),r.id),await e.prepare("UPDATE objects SET reception = ? WHERE id = ?").run(m===null?0:2,r.id),y.receiver_json!==m){const d=Date.now();if(m===null){const _=await e.prepare(`UPDATE deliveries SET canceled_at = ?, updated_at = ?
|
|
3
3
|
WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(d,d,r.id);await e.prepare("UPDATE custom_objects SET dropped = dropped + ? WHERE object = ?").run(_.changes,r.id)}else await e.prepare(`UPDATE deliveries SET next_attempt_at = ?, updated_at = ?
|
|
4
|
-
WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(m==='"queue"'?null:d,d,r.id);
|
|
5
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1)`).run(i
|
|
6
|
-
UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(u,u);if(u!==null&&c===void 0)throw new j("unknown_cause",`no message ${u}`,S.USAGE);if(c&&c.depth>=F)throw new M(c.depth+1);const w=new Date().toISOString();return await e.prepare("UPDATE custom_objects SET summary = ?, state_json = ?, published_at = ? WHERE object = ?").run(
|
|
7
|
-
`)[0],t.id),await O(e,{event:`${t.type}.${
|
|
4
|
+
WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(m==='"queue"'?null:d,d,r.id);R(r.id)}return w&&R(r.id),await O(e,{event:"configured",object:T,actor:t.class==="object"?t.object:t.class==="person"?t.person:t.class==="app"?t.app:t.node,attrs:{changed:E.join(",")}},async d=>await N(e,d)),{object:T,...v===void 0?{}:{token:v},created:!1,changed:E}}l(Ne,"registerCustomObject");async function oe(e,t){const{name:n,owner:i,description:a,events:s,read:o,source:u}=t;if(!k.test(n))throw f(`invalid kind ${n}`);if(z(s,n).length>0)throw f("invalid custom event catalog");await e.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(`kind:${n}`);const c=await e.prepare("SELECT owner, description, events_json, read_json, source, declared FROM object_kinds WHERE name = ?").get(n),w=I(s),p=u==="manifest"&&o!==null?JSON.stringify({command:o.command}):null,m=new Date().toISOString();if(!c)return await e.prepare(`INSERT INTO object_kinds (name, owner, description, events_json, created_at, updated_at, read_json, source, declared)
|
|
5
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1)`).run(n,i.id,a,w,m,m,p,u),A(e,n),{outcome:"inserted",changed:[]};const r=c.owner===i.id;if(u==="peer"){if(c.source!=="peer")return{outcome:"skipped",changed:[]}}else if(c.source!=="peer"&&(r||h("kind_owned",`${n} belongs to another owner`),u==="register"&&c.source==="manifest"))return{outcome:"skipped",changed:[]};const g=[];return I(JSON.parse(c.events_json))!==w&&g.push("events"),c.description!==a&&g.push("description"),(c.read_json??null)!==p&&g.push("read"),g.length===0&&r&&c.source===u&&c.declared===1?{outcome:"unchanged",changed:g}:(await e.prepare("UPDATE object_kinds SET owner = ?, description = ?, events_json = ?, read_json = ?, source = ?, declared = 1, updated_at = ? WHERE name = ?").run(i.id,a,w,p,u,m,n),A(e,n),{outcome:"updated",changed:g})}l(oe,"upsertKind");async function Te(e,t,n,i){const a=await e.prepare("SELECT events_json FROM object_kinds WHERE name = ?").get(t.type);if(!a||t.deleted_at!==null)throw f(`${t.id} is not a live custom object`);const s=JSON.parse(a.events_json);if(!D.test(i.event)||!Object.hasOwn(s,i.event))throw new j("unknown_event",`${t.type} declares ${Object.keys(s).join(", ")}, not ${i.event}`,S.USAGE,{declared:Object.keys(s),event:i.event});if(typeof i.summary!="string")throw f("summary must be text");const o=i.state===void 0?null:JSON.stringify(i.state);if(i.state!==void 0&&o===void 0)throw f("state must be JSON-serializable");if(Buffer.byteLength(JSON.stringify({summary:i.summary,...i.state===void 0?{}:{state:i.state}}))>64*1024)throw new j("body_too_large","custom event summary and state exceed 64 KiB",S.USAGE);if(o!==null&&Buffer.byteLength(o)>64*1024)throw f("state exceeds 64 KiB");const u=i.cause??null,c=u===null?null:await e.prepare(`SELECT depth FROM messages WHERE message_id = ?
|
|
6
|
+
UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(u,u);if(u!==null&&c===void 0)throw new j("unknown_cause",`no message ${u}`,S.USAGE);if(c&&c.depth>=F)throw new M(c.depth+1);const w=new Date().toISOString();return await e.prepare("UPDATE custom_objects SET summary = ?, state_json = ?, published_at = ? WHERE object = ?").run(i.summary,o,w,t.id),await e.prepare("UPDATE objects SET preview = ? WHERE id = ?").run(i.summary.split(`
|
|
7
|
+
`)[0],t.id),await O(e,{event:`${t.type}.${i.event}`,object:t,actor:n,attrs:{},messageBody:{summary:i.summary,...i.state===void 0?{}:{state:i.state}},...i.cause===void 0?{}:{cause:i.cause}},async p=>await N(e,p))}l(Te,"publishCustom");async function Re(e,t,n){if(t.deleted_at!==null)return;await O(e,{event:"deleted",object:t,actor:n,attrs:{}},async a=>await N(e,a));const i=new Date().toISOString();await e.prepare("UPDATE objects SET deleted_at = ?, reception = 0 WHERE id = ?").run(i,t.id),await e.prepare("UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL").run(Date.now(),Date.now(),t.id),R(t.id)}l(Re,"unregisterCustomObject");function I(e){return JSON.stringify(Object.fromEntries(Object.entries(e).sort(([t],[n])=>t.localeCompare(n)).map(([t,n])=>[t,{level:n.level,...n.supersedes===void 0?{}:{supersedes:n.supersedes},...n.ends_wait===void 0?{}:{ends_wait:n.ends_wait}}])))}l(I,"canonicalEvents");async function be(e,t,n=0){return await W(e,t.id,n,"pending")}l(be,"pendingFor");async function Ae(e,t){const n=await e.prepare("SELECT destination_id FROM deliveries WHERE delivery_id = ?").get(t);return n?(await W(e,n.destination_id,-1,"handed_off")).find(i=>i.delivery_id===t)??null:null}l(Ae,"pushLineFor");async function W(e,t,n,i){return(await e.prepare(`SELECT d.delivery_id, m.message_id, m.seq, m.event, m.level, m.object_id, m.actor_id, m.body_json, m.body_ref, m.depth, m.created_at,
|
|
8
8
|
o.type, o.name, owner.id AS owner_id
|
|
9
9
|
FROM deliveries d JOIN messages m ON m.message_id = d.message_id JOIN objects o ON o.id = m.object_id
|
|
10
10
|
LEFT JOIN objects owner ON owner.id = o.owner
|
|
11
|
-
WHERE d.destination_id = ? AND d.state = ? AND d.canceled_at IS NULL AND m.seq > ? ORDER BY m.seq`).all(t,n
|
|
12
|
-
WHERE delivery_id = ? AND destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(
|
|
11
|
+
WHERE d.destination_id = ? AND d.state = ? AND d.canceled_at IS NULL AND m.seq > ? ORDER BY m.seq`).all(t,i,n)).map(s=>{const o=s.body_json?JSON.parse(s.body_json):{};return{delivery_id:s.delivery_id,message_id:s.message_id,seq:s.seq,event:s.event,level:s.level,object:{id:s.object_id,type:s.type,name:s.name,owner:s.owner_id},actor:s.actor_id,attrs:o.push?.attrs??{},...o.summary===void 0?{}:{summary:o.summary},...o.state===void 0?{}:{state:o.state},...o.push?.pointer===void 0?{}:{pointer:o.push.pointer},depth:s.depth,created_at:s.created_at}})}l(W,"queueLines");async function ke(e,t,n){const i=Date.now(),a=[];let s=0;for(const o of n){const u=await e.prepare(`UPDATE deliveries SET state = 'acknowledged', acknowledged_at = ?, updated_at = ?
|
|
12
|
+
WHERE delivery_id = ? AND destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(i,i,o,t.id);Number(u.changes)?s++:a.push(o)}return{acknowledged:s,...a.length?{unknown:a}:{}}}l(ke,"acknowledge");async function De(e,t,n){const i=await e.prepare("SELECT token_hash FROM custom_objects WHERE object = ?").get(t.id),a=Buffer.from(i?.token_hash??"".padEnd(64,"0"),"hex"),s=Buffer.from(b(n),"hex");return n.startsWith("crtr_pub_")&&t.deleted_at===null&&a.length===s.length&&P(a,s)&&i?.token_hash!=null}l(De,"verifyPublishToken");export{ke as acknowledge,ie as customReachable,ne as mayActAs,B as onQueueSignal,be as pendingFor,Te as publishCustom,Ae as pushLineFor,Ne as registerCustomObject,Re as unregisterCustomObject,oe as upsertKind,De as verifyPublishToken};
|
|
@@ -6,8 +6,55 @@ export interface JobRow {
|
|
|
6
6
|
command: string;
|
|
7
7
|
status: JobStatus;
|
|
8
8
|
exit_code: number | null;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/** Blob key of the job's output (`jobs/<node>/<job>/output/<seq>`). */
|
|
10
|
+
output_key: string;
|
|
11
|
+
/** ISO time the node must claim the job by, or null for a job that runs to completion. */
|
|
12
|
+
deadline_at: string | null;
|
|
13
|
+
purpose: string | null;
|
|
14
|
+
quiet_ok: boolean;
|
|
15
|
+
pgid: number | null;
|
|
16
|
+
pgid_identity: string | null;
|
|
17
|
+
activity_last_ms: number | null;
|
|
18
|
+
activity_sampled_ms: number | null;
|
|
19
|
+
activity_files: string | null;
|
|
20
|
+
silence_activity_ms: number | null;
|
|
21
|
+
silence_threshold_ms: number | null;
|
|
22
|
+
exit_reported_at: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** A live job as the node, the roster and the stop guard see it. */
|
|
25
|
+
export interface RunningJob {
|
|
26
|
+
jobId: string;
|
|
27
|
+
command: string;
|
|
28
|
+
purpose: string | null;
|
|
29
|
+
startedAtMs: number;
|
|
30
|
+
outputKey: string;
|
|
31
|
+
deadlineAtMs: number | null;
|
|
32
|
+
pgid: number | null;
|
|
33
|
+
pgidIdentity: string | null;
|
|
34
|
+
quietOk: boolean;
|
|
35
|
+
activity: {
|
|
36
|
+
lastActivityMs: number;
|
|
37
|
+
sampledAtMs: number;
|
|
38
|
+
files: string[];
|
|
39
|
+
} | null;
|
|
40
|
+
silence: {
|
|
41
|
+
lastActivityMs: number;
|
|
42
|
+
thresholdMs: number;
|
|
43
|
+
} | null;
|
|
44
|
+
}
|
|
45
|
+
/** The blob prefix a job's output chunks are written under, one object per chunk (`<prefix>/<seq, 8 digits>`). */
|
|
46
|
+
export declare function jobOutputKey(nodeId: string, jobId: string): string;
|
|
47
|
+
/** What the broker's valve reports about a job it runs: any subset, applied together. */
|
|
48
|
+
export interface JobReport {
|
|
49
|
+
pgid?: number;
|
|
50
|
+
pgidIdentity?: string;
|
|
51
|
+
exitCode?: number;
|
|
52
|
+
vanished?: boolean;
|
|
53
|
+
activity?: {
|
|
54
|
+
lastActivityMs: number;
|
|
55
|
+
sampledAtMs: number;
|
|
56
|
+
files: string[];
|
|
57
|
+
};
|
|
11
58
|
}
|
|
12
59
|
export declare function jobRow(db: CanvasDb, job: ObjectRow): Promise<JobRow | null>;
|
|
13
60
|
/** Create the job object (`id` keeps the job id the runner already uses), its `running` row,
|
|
@@ -15,15 +62,46 @@ export declare function jobRow(db: CanvasDb, job: ObjectRow): Promise<JobRow | n
|
|
|
15
62
|
export declare function startJob(db: CanvasDb, node: ObjectRow, spec: {
|
|
16
63
|
id?: string;
|
|
17
64
|
command: string;
|
|
18
|
-
|
|
19
|
-
|
|
65
|
+
purpose?: string | null;
|
|
66
|
+
deadlineAtMs?: number | null;
|
|
20
67
|
}): Promise<ObjectRow>;
|
|
21
68
|
/** End a running job: `exited` with its exit code, or `killed` with the killer as actor (null
|
|
22
69
|
* when the runtime killed it, e.g. at its deadline; `reason` then names why). The event carries
|
|
23
|
-
* the exit code and
|
|
70
|
+
* the exit code and output key, never output. A job already ended is left as is. */
|
|
24
71
|
export declare function finishJob(db: CanvasDb, job: ObjectRow, end: {
|
|
25
72
|
exitCode: number;
|
|
26
73
|
} | {
|
|
27
74
|
killedBy: ObjectRow | null;
|
|
28
75
|
reason?: string;
|
|
29
76
|
}): Promise<ObjectRow>;
|
|
77
|
+
/** Arm (epoch ms) or drop (null) a running job's cancel deadline. */
|
|
78
|
+
export declare function setJobDeadline(db: CanvasDb, jobId: string, deadlineAtMs: number | null): Promise<void>;
|
|
79
|
+
/** Declare a running job an expected wait: it gets no silence messages. */
|
|
80
|
+
export declare function markJobQuietOk(db: CanvasDb, jobId: string): Promise<void>;
|
|
81
|
+
/** Record the silence message last sent for a job. */
|
|
82
|
+
export declare function recordJobSilence(db: CanvasDb, jobId: string, lastActivityMs: number, thresholdMs: number): Promise<void>;
|
|
83
|
+
/** Apply a broker report to a running job's row: the process identity, the activity sample, and the exit
|
|
84
|
+
* (`exit_reported_at` set; the reconciler's next pass ends the job through `finishJob`). */
|
|
85
|
+
export declare function applyJobReport(db: CanvasDb, jobId: string, report: JobReport): Promise<void>;
|
|
86
|
+
/** Every `running` job a node owns, oldest first. */
|
|
87
|
+
export declare function runningJobs(db: CanvasDb, nodeId: string): Promise<RunningJob[]>;
|
|
88
|
+
/** Every `running` job in the canvas, with its owner. */
|
|
89
|
+
export declare function allRunningJobs(db: CanvasDb): Promise<Array<RunningJob & {
|
|
90
|
+
owner: string;
|
|
91
|
+
}>>;
|
|
92
|
+
/** Every `running` job in the canvas whose cancel deadline has passed, with its owner. */
|
|
93
|
+
export declare function expiredJobs(db: CanvasDb, nowMs: number): Promise<Array<RunningJob & {
|
|
94
|
+
owner: string;
|
|
95
|
+
}>>;
|
|
96
|
+
/** Credit every armed deadline of a node's running jobs for an interval the node was not permitted to run. */
|
|
97
|
+
export declare function shiftJobDeadlines(db: CanvasDb, nodeId: string, byMs: number): Promise<number>;
|
|
98
|
+
/** End every running job whose exit the broker reported: `exited` with its code, or `killed` when the process
|
|
99
|
+
* vanished without one. Returns how many ended. */
|
|
100
|
+
export declare function endReportedJobs(db: CanvasDb): Promise<number>;
|
|
101
|
+
/** True when the node's newest execution runs on a runner other than the process runner (a sandbox): its jobs run
|
|
102
|
+
* there, so their pids, supervisor files and output are not on this host and only the broker's reports describe them. */
|
|
103
|
+
export declare function ownerRunsRemotely(db: CanvasDb, nodeId: string): Promise<boolean>;
|
|
104
|
+
/** End, as killed, every running job whose owner's newest execution is a sandbox execution that has exited: the
|
|
105
|
+
* sandbox the job ran in is gone with its broker, so nothing is left to report an exit. Run after `endReportedJobs`,
|
|
106
|
+
* so an exit the broker did report wins. Returns how many ended. */
|
|
107
|
+
export declare function endJobsOfExitedSandboxes(db: CanvasDb): Promise<number>;
|
package/dist/core/graph/jobs.js
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
var
|
|
2
|
-
`,1)[0].trim()}
|
|
1
|
+
var y=Object.defineProperty;var o=(t,e)=>y(t,"name",{value:e,configurable:!0});import{recordActedOn as d}from"./edges.js";import{publish as p}from"./events.js";import{createObject as b,getById as u}from"./objects.js";import{recordWatch as m}from"./watches.js";function w(t,e){return`jobs/${t}/${e}/output`}o(w,"jobOutputKey");function j(t){return t.split(`
|
|
2
|
+
`,1)[0].trim()}o(j,"firstLine");function E(t,e){return`${j(t)} (${e})`}o(E,"previewOf");async function h(t,e){return await t.prepare("SELECT * FROM jobs WHERE object = ?").get(e.id)??null}o(h,"jobRow");async function I(t,e,n){if(e.type!=="node")throw new Error(`startJob: ${e.id} is a ${e.type}, not a node`);const i=await b(t,{...n.id===void 0?{}:{id:n.id},type:"job",kind:"bash",owner:e.id,privacy:"public",name:j(n.command)||"bash",preview:E(n.command,"running")});return await t.prepare(`INSERT INTO jobs (object, command, status, exit_code, output_key, deadline_at, purpose)
|
|
3
|
+
VALUES (?, ?, 'running', NULL, ?, ?, ?)`).run(i.id,n.command,w(e.id,i.id),n.deadlineAtMs==null?null:new Date(n.deadlineAtMs).toISOString(),n.purpose??null),await d(t,e,i),await m(t,e,i,"job-start"),await p(t,{event:"created",object:i,actor:e,attrs:{}}),i}o(I,"startJob");async function f(t,e,n){const i=await h(t,e);if(i===null)throw new Error(`finishJob: ${e.id} has no jobs row`);if(i.status!=="running")return e;const a="killedBy"in n,l=a?"killed":"exited",s=a?null:n.exitCode;await t.prepare("UPDATE jobs SET status = ?, exit_code = ? WHERE object = ?").run(l,s,e.id);const _=a?"killed":`exited ${s}`;await t.prepare("UPDATE objects SET preview = ? WHERE id = ?").run(E(i.command,_),e.id);const r=await u(t,e.id);return a&&n.killedBy!==null&&await d(t,n.killedBy,r),await p(t,{event:l,object:r,actor:a?n.killedBy:null,attrs:a?{"output-key":i.output_key,...n.reason===void 0?{}:{reason:n.reason}}:{"exit-code":s,"output-key":i.output_key}}),r}o(f,"finishJob");async function x(t,e,n){await t.prepare("UPDATE jobs SET deadline_at = ? WHERE object = ? AND status = 'running'").run(n===null?null:new Date(n).toISOString(),e)}o(x,"setJobDeadline");async function L(t,e){await t.prepare("UPDATE jobs SET quiet_ok = true WHERE object = ?").run(e)}o(L,"markJobQuietOk");async function M(t,e,n,i){await t.prepare("UPDATE jobs SET silence_activity_ms = ?, silence_threshold_ms = ? WHERE object = ?").run(Math.trunc(n),Math.trunc(i),e)}o(M,"recordJobSilence");async function v(t,e,n){const i=[],a=[];n.pgid!==void 0&&(i.push("pgid = ?"),a.push(n.pgid)),n.pgidIdentity!==void 0&&(i.push("pgid_identity = ?"),a.push(n.pgidIdentity)),n.activity!==void 0&&(i.push("activity_last_ms = ?","activity_sampled_ms = ?","activity_files = ?"),a.push(Math.trunc(n.activity.lastActivityMs),Math.trunc(n.activity.sampledAtMs),n.activity.files.join(`
|
|
4
|
+
`))),n.exitCode!==void 0?(i.push("exit_code = ?","exit_reported_at = ?"),a.push(n.exitCode,new Date().toISOString())):n.vanished===!0&&(i.push("exit_reported_at = ?"),a.push(new Date().toISOString())),i.length!==0&&await t.prepare(`UPDATE jobs SET ${i.join(", ")} WHERE object = ? AND status = 'running'`).run(...a,e)}o(v,"applyJobReport");function S(t){const e=Number(t.slice(0,Math.max(0,t.indexOf("-"))));return Number.isFinite(e)&&e>0?e:0}o(S,"startedAtMs");function c(t){return{jobId:t.object,command:t.command,purpose:t.purpose,startedAtMs:S(t.object),outputKey:t.output_key,deadlineAtMs:t.deadline_at===null?null:Date.parse(t.deadline_at),pgid:t.pgid,pgidIdentity:t.pgid_identity,quietOk:t.quiet_ok,activity:t.activity_last_ms===null||t.activity_sampled_ms===null?null:{lastActivityMs:t.activity_last_ms,sampledAtMs:t.activity_sampled_ms,files:(t.activity_files??"").split(`
|
|
5
|
+
`).filter(e=>e!=="")},silence:t.silence_activity_ms===null||t.silence_threshold_ms===null?null:{lastActivityMs:t.silence_activity_ms,thresholdMs:t.silence_threshold_ms}}}o(c,"toRunning");async function N(t,e){return(await t.prepare(`SELECT j.* FROM jobs j JOIN objects o ON o.id = j.object
|
|
6
|
+
WHERE o.owner = ? AND o.deleted_at IS NULL AND j.status = 'running'`).all(e)).map(c).sort((i,a)=>i.startedAtMs-a.startedAtMs)}o(N,"runningJobs");async function T(t){return(await t.prepare(`SELECT j.*, o.owner AS owner FROM jobs j JOIN objects o ON o.id = j.object
|
|
7
|
+
WHERE o.deleted_at IS NULL AND j.status = 'running'`).all()).map(n=>({...c(n),owner:n.owner}))}o(T,"allRunningJobs");async function k(t,e){return(await t.prepare(`SELECT j.*, o.owner AS owner FROM jobs j JOIN objects o ON o.id = j.object
|
|
8
|
+
WHERE o.deleted_at IS NULL AND j.status = 'running' AND j.deadline_at IS NOT NULL AND j.deadline_at <= ?`).all(new Date(e).toISOString())).map(i=>({...c(i),owner:i.owner}))}o(k,"expiredJobs");async function J(t,e,n){if(!Number.isFinite(n)||n<=0)return 0;let i=0;for(const a of await N(t,e))a.deadlineAtMs!==null&&(await x(t,a.jobId,a.deadlineAtMs+n),i+=1);return i}o(J,"shiftJobDeadlines");async function C(t){const e=await t.prepare(`SELECT j.object, j.exit_code FROM jobs j JOIN objects o ON o.id = j.object
|
|
9
|
+
WHERE o.deleted_at IS NULL AND j.status = 'running' AND j.exit_reported_at IS NOT NULL`).all();for(const{object:n,exit_code:i}of e){const a=await u(t,n);await f(t,a,i===null?{killedBy:null,reason:"process disappeared without an exit record"}:{exitCode:i})}return e.length}o(C,"endReportedJobs");async function U(t,e){const n=await t.prepare("SELECT runner FROM executions WHERE node_id = ? ORDER BY started_at DESC, attempt DESC LIMIT 1").get(e);return n!==void 0&&n.runner!=="process"}o(U,"ownerRunsRemotely");async function W(t){const e=await t.prepare(`SELECT j.object FROM jobs j JOIN objects o ON o.id = j.object
|
|
10
|
+
WHERE o.deleted_at IS NULL AND j.status = 'running'
|
|
11
|
+
AND (SELECT e.runner || ':' || e.state FROM executions e WHERE e.node_id = o.owner ORDER BY e.started_at DESC, e.attempt DESC LIMIT 1) IN ('blaxel:exited')`).all();for(const{object:n}of e)await f(t,await u(t,n),{killedBy:null,reason:"the sandbox running the job ended with its broker"});return e.length}o(W,"endJobsOfExitedSandboxes");export{T as allRunningJobs,v as applyJobReport,W as endJobsOfExitedSandboxes,C as endReportedJobs,k as expiredJobs,f as finishJob,w as jobOutputKey,h as jobRow,L as markJobQuietOk,U as ownerRunsRemotely,M as recordJobSilence,N as runningJobs,x as setJobDeadline,J as shiftJobDeadlines,I as startJob};
|