@astrale-os/cli 1.0.0-beta.32 → 1.0.0-beta.43
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/CHANGELOG.md +928 -0
- package/README.md +53 -43
- package/THIRD-PARTY-NOTICES.md +37 -1
- package/dist/astrale.js +349631 -83669
- package/dist/public/connect-core.js +16 -47
- package/dist/public/keys/index.js +1 -1
- package/dist/types/admin/contract.d.ts +11 -0
- package/dist/types/admin/graph/index.d.ts +1 -0
- package/dist/types/admin/graph/nodes.d.ts +12 -0
- package/dist/types/admin/instance/client.d.ts +2 -0
- package/dist/types/connection/lifetime.d.ts +1 -1
- package/dist/types/errors.d.ts +3 -0
- package/dist/types/lib/proc.d.ts +1 -0
- package/dist/types/lib/update.d.ts +15 -7
- package/package.json +8 -6
- package/studio/client/dist/assets/index-5C1uT6ML.css +2 -0
- package/studio/client/dist/assets/index-C4kNlvwF.js +80 -0
- package/studio/client/dist/assets/schema-studio-BR9d0faB.js +8 -0
- package/studio/client/dist/index.html +13 -2
- package/studio/package.json +2 -1
- package/studio/server/agent/ask.test.ts +7 -1
- package/studio/server/agent/bridge/grant.ts +15 -3
- package/studio/server/agent/prompts/system.test.ts +5 -0
- package/studio/server/agent/prompts/system.ts +19 -4
- package/studio/server/agent/routes.test.ts +39 -1
- package/studio/server/agent/routes.ts +3 -0
- package/studio/server/agent/run/coordinator.test.ts +7 -1
- package/studio/server/agent/run/preparation.ts +1 -0
- package/studio/server/agent/run/transcript.test.ts +47 -2
- package/studio/server/agent/run/transcript.ts +32 -3
- package/studio/server/api/documents.test.ts +31 -0
- package/studio/server/api/documents.ts +35 -1
- package/studio/server/api-agent-loadout.test.ts +7 -1
- package/studio/server/api.test.ts +7 -1
- package/studio/server/cache.ts +4 -4
- package/studio/server/detect.ts +9 -12
- package/studio/server/domain.test.ts +83 -3
- package/studio/server/domain.ts +205 -27
- package/studio/server/handoff/service.ts +1 -1
- package/studio/server/index.ts +5 -8
- package/studio/server/introspect/extractor.ts +111 -17
- package/studio/server/introspect/overlay-tsmorph.test.ts +9 -9
- package/studio/server/introspect/overlay.ts +1 -1
- package/studio/server/introspect/runtime.ts +9 -1
- package/studio/server/introspect/source-overlay/spans.ts +2 -5
- package/studio/server/lifecycle.ts +1 -1
- package/studio/server/state/baseline.test.ts +14 -0
- package/studio/server/state/baseline.ts +16 -2
- package/studio/server/watch.test.ts +2 -1
- package/studio/server/watch.ts +5 -2
- package/studio/server/workspace/create.ts +9 -8
- package/studio/server/workspace-state.ts +4 -6
- package/studio/server/workspace-watch.ts +33 -22
- package/studio/shared/contracts/agent.ts +3 -0
- package/viewer/dist/main.js +12 -12
- package/src/__tests__/fixtures/publication.ts +0 -20
- package/src/admin/.spec/api.d.ts +0 -3
- package/src/admin/.spec/architecture.md +0 -22
- package/src/admin/.spec/icon.svg +0 -3
- package/src/admin/.spec/layout.ts +0 -6
- package/src/admin/__tests__/fixture.ts +0 -25
- package/src/admin/catalog/.spec/api.d.ts +0 -63
- package/src/admin/catalog/.spec/architecture.md +0 -7
- package/src/admin/catalog/.spec/icon.svg +0 -3
- package/src/admin/catalog/.spec/layout.ts +0 -6
- package/src/admin/catalog/__tests__/client.test.ts +0 -245
- package/src/admin/catalog/client.ts +0 -195
- package/src/admin/catalog/index.ts +0 -13
- package/src/admin/catalog/model.ts +0 -41
- package/src/admin/catalog/tsconfig.json +0 -8
- package/src/admin/contract.ts +0 -47
- package/src/admin/graph/.spec/api.d.ts +0 -22
- package/src/admin/graph/.spec/architecture.md +0 -5
- package/src/admin/graph/.spec/icon.svg +0 -3
- package/src/admin/graph/.spec/layout.ts +0 -3
- package/src/admin/graph/index.ts +0 -6
- package/src/admin/graph/nodes.ts +0 -56
- package/src/admin/index.ts +0 -2
- package/src/admin/instance/.spec/api.d.ts +0 -62
- package/src/admin/instance/.spec/architecture.md +0 -10
- package/src/admin/instance/.spec/icon.svg +0 -3
- package/src/admin/instance/.spec/layout.ts +0 -6
- package/src/admin/instance/__tests__/client.test.ts +0 -273
- package/src/admin/instance/client.ts +0 -191
- package/src/admin/instance/index.ts +0 -15
- package/src/admin/instance/model.ts +0 -59
- package/src/admin/instance/tsconfig.json +0 -8
- package/src/commands/__tests__/admin-instance.test.ts +0 -91
- package/src/commands/__tests__/auth-login.test.ts +0 -231
- package/src/commands/__tests__/auth-token.test.ts +0 -223
- package/src/commands/__tests__/call-describe.test.ts +0 -80
- package/src/commands/__tests__/call.test.ts +0 -63
- package/src/commands/__tests__/domain-install-operation.test.ts +0 -145
- package/src/commands/__tests__/domain-install-owned.test.ts +0 -191
- package/src/commands/__tests__/domain-list.test.ts +0 -133
- package/src/commands/__tests__/domain-uninstall.test.ts +0 -53
- package/src/commands/__tests__/install-direct.test.ts +0 -128
- package/src/commands/__tests__/install-identity-override.test.ts +0 -76
- package/src/commands/__tests__/instance-bookmark.test.ts +0 -166
- package/src/commands/__tests__/instance-list-admin.test.ts +0 -26
- package/src/commands/__tests__/instance-list-rows.test.ts +0 -64
- package/src/commands/__tests__/instance-status.test.ts +0 -139
- package/src/commands/__tests__/instance-use.test.ts +0 -67
- package/src/commands/__tests__/introspect-parse.test.ts +0 -100
- package/src/commands/__tests__/logs.test.ts +0 -345
- package/src/commands/__tests__/read-commands.test.ts +0 -377
- package/src/commands/__tests__/setup-plan.test.ts +0 -61
- package/src/commands/__tests__/studio-descriptor.test.ts +0 -22
- package/src/commands/__tests__/token-ttl.test.ts +0 -66
- package/src/commands/__tests__/update.test.ts +0 -95
- package/src/commands/__tests__/view-build.test.ts +0 -58
- package/src/commands/__tests__/view.test.ts +0 -197
- package/src/commands/admin/status.ts +0 -61
- package/src/commands/admin/use.ts +0 -100
- package/src/commands/auth/login.ts +0 -82
- package/src/commands/auth/logout.ts +0 -53
- package/src/commands/auth/status.ts +0 -86
- package/src/commands/auth/token.ts +0 -174
- package/src/commands/browser.ts +0 -236
- package/src/commands/call-describe.ts +0 -123
- package/src/commands/call.ts +0 -230
- package/src/commands/domain/install.ts +0 -575
- package/src/commands/domain/list.ts +0 -155
- package/src/commands/domain/publish.ts +0 -136
- package/src/commands/domain/uninstall.ts +0 -128
- package/src/commands/get.ts +0 -69
- package/src/commands/identity/__tests__/register.test.ts +0 -93
- package/src/commands/identity/create.ts +0 -35
- package/src/commands/identity/delete.ts +0 -24
- package/src/commands/identity/export.ts +0 -46
- package/src/commands/identity/import.ts +0 -61
- package/src/commands/identity/list.ts +0 -56
- package/src/commands/identity/register.ts +0 -195
- package/src/commands/identity/sync.ts +0 -38
- package/src/commands/identity/unsync.ts +0 -34
- package/src/commands/identity/use.ts +0 -24
- package/src/commands/identity/whoami.ts +0 -34
- package/src/commands/idp/add.ts +0 -150
- package/src/commands/idp/list.ts +0 -57
- package/src/commands/idp/refresh.ts +0 -38
- package/src/commands/idp/remove.ts +0 -36
- package/src/commands/idp/show.ts +0 -29
- package/src/commands/instance/active.ts +0 -73
- package/src/commands/instance/bookmark.ts +0 -101
- package/src/commands/instance/create.ts +0 -61
- package/src/commands/instance/delete.ts +0 -69
- package/src/commands/instance/forget.ts +0 -26
- package/src/commands/instance/list.ts +0 -197
- package/src/commands/instance/status.ts +0 -92
- package/src/commands/instance/use.ts +0 -263
- package/src/commands/introspect.ts +0 -129
- package/src/commands/logs.ts +0 -371
- package/src/commands/mutate.ts +0 -122
- package/src/commands/query.ts +0 -142
- package/src/commands/session/analyze.ts +0 -72
- package/src/commands/session/list.ts +0 -36
- package/src/commands/setup.ts +0 -54
- package/src/commands/status.ts +0 -60
- package/src/commands/studio.ts +0 -434
- package/src/commands/token.ts +0 -121
- package/src/commands/ui/__tests__/commands.test.ts +0 -152
- package/src/commands/ui/add.ts +0 -51
- package/src/commands/ui/doctor.ts +0 -21
- package/src/commands/ui/init.ts +0 -38
- package/src/commands/ui/list.ts +0 -26
- package/src/commands/ui/preset-apply.ts +0 -19
- package/src/commands/ui/preset-list.ts +0 -12
- package/src/commands/ui/shared.ts +0 -25
- package/src/commands/update.ts +0 -356
- package/src/commands/use.ts +0 -86
- package/src/commands/view-serve.ts +0 -26
- package/src/commands/view.ts +0 -658
- package/src/connect-core.test.ts +0 -38
- package/src/connect-core.ts +0 -42
- package/src/connection/.spec/api.d.ts +0 -110
- package/src/connection/.spec/architecture.md +0 -58
- package/src/connection/.spec/capabilities/connection.ts +0 -13
- package/src/connection/.spec/examples/call.ts +0 -7
- package/src/connection/.spec/flows/command.ts +0 -20
- package/src/connection/.spec/flows/session.ts +0 -62
- package/src/connection/.spec/laws/connection.ts +0 -213
- package/src/connection/.spec/layout.ts +0 -22
- package/src/connection/__tests__/auth.test.ts +0 -129
- package/src/connection/__tests__/ca-fetch.test.ts +0 -60
- package/src/connection/__tests__/call.test.ts +0 -17
- package/src/connection/__tests__/credential.test.ts +0 -254
- package/src/connection/__tests__/errors.test.ts +0 -577
- package/src/connection/__tests__/exchange.test.ts +0 -404
- package/src/connection/__tests__/failure-fixtures.ts +0 -39
- package/src/connection/__tests__/failure-safety.test.ts +0 -75
- package/src/connection/__tests__/fixtures/localhost-cert.pem +0 -19
- package/src/connection/__tests__/fixtures/localhost-key.base64 +0 -1
- package/src/connection/__tests__/managed.test.ts +0 -39
- package/src/connection/__tests__/reasons.test.ts +0 -78
- package/src/connection/__tests__/self.test.ts +0 -71
- package/src/connection/__tests__/session.test.ts +0 -144
- package/src/connection/__tests__/target.test.ts +0 -95
- package/src/connection/auth.ts +0 -265
- package/src/connection/call.ts +0 -7
- package/src/connection/command.ts +0 -67
- package/src/connection/credential.ts +0 -138
- package/src/connection/errors.ts +0 -1
- package/src/connection/exchange.ts +0 -394
- package/src/connection/failure/classify.ts +0 -110
- package/src/connection/failure/index.ts +0 -18
- package/src/connection/failure/model.ts +0 -28
- package/src/connection/failure/render.ts +0 -116
- package/src/connection/index.ts +0 -8
- package/src/connection/lifetime.ts +0 -25
- package/src/connection/reasons.ts +0 -209
- package/src/connection/self.ts +0 -138
- package/src/connection/session.ts +0 -203
- package/src/connection/target.ts +0 -112
- package/src/connection/tsconfig.json +0 -6
- package/src/errors.ts +0 -66
- package/src/graph/.spec/api.d.ts +0 -41
- package/src/graph/.spec/laws/documents.ts +0 -24
- package/src/graph/.spec/laws/projection.ts +0 -13
- package/src/graph/.spec/layout.ts +0 -14
- package/src/graph/__tests__/mutation.test.ts +0 -51
- package/src/graph/__tests__/projection.test.ts +0 -22
- package/src/graph/__tests__/query.test.ts +0 -180
- package/src/graph/class.ts +0 -30
- package/src/graph/index.ts +0 -4
- package/src/graph/mutation.ts +0 -46
- package/src/graph/projection.ts +0 -21
- package/src/graph/query.ts +0 -95
- package/src/graph/tsconfig.json +0 -6
- package/src/identity/.history/ard/bootstrap-root-transfer.md +0 -57
- package/src/identity/.history/ard/domain-mediated-registration.md +0 -36
- package/src/identity/.spec/api.d.ts +0 -124
- package/src/identity/.spec/architecture.md +0 -33
- package/src/identity/.spec/capabilities/identity.ts +0 -7
- package/src/identity/.spec/flows/bootstrap-root.ts +0 -79
- package/src/identity/.spec/flows/domain-mediated-registration.ts +0 -51
- package/src/identity/.spec/flows/import.ts +0 -29
- package/src/identity/.spec/laws/identity.ts +0 -56
- package/src/identity/.spec/layout.ts +0 -13
- package/src/identity/__tests__/fixtures/registry-journey.ts +0 -65
- package/src/identity/__tests__/registration.test.ts +0 -64
- package/src/identity/__tests__/registry.test.ts +0 -74
- package/src/identity/__tests__/transfer.test.ts +0 -198
- package/src/identity/index.ts +0 -23
- package/src/identity/registration.ts +0 -72
- package/src/identity/registry.ts +0 -181
- package/src/identity/transfer.ts +0 -187
- package/src/identity/tsconfig.json +0 -6
- package/src/index.ts +0 -1
- package/src/keys/.spec/api.d.ts +0 -74
- package/src/keys/.spec/architecture.md +0 -13
- package/src/keys/.spec/laws/keys.ts +0 -43
- package/src/keys/.spec/layout.ts +0 -6
- package/src/keys/__tests__/algorithm.test.ts +0 -39
- package/src/keys/__tests__/keys.test.ts +0 -172
- package/src/keys/algorithm.ts +0 -33
- package/src/keys/credential.ts +0 -113
- package/src/keys/index.ts +0 -14
- package/src/keys/pair.ts +0 -219
- package/src/keys/tsconfig.json +0 -6
- package/src/lib/__tests__/admin-target.test.ts +0 -184
- package/src/lib/__tests__/binary.test.ts +0 -70
- package/src/lib/__tests__/browser-retention.test.ts +0 -212
- package/src/lib/__tests__/command-dx.test.ts +0 -102
- package/src/lib/__tests__/concurrency.test.ts +0 -62
- package/src/lib/__tests__/config.test.ts +0 -81
- package/src/lib/__tests__/design.test.ts +0 -99
- package/src/lib/__tests__/domain-publication.test.ts +0 -138
- package/src/lib/__tests__/format.test.ts +0 -22
- package/src/lib/__tests__/idp-session.driver.ts +0 -60
- package/src/lib/__tests__/idp-session.test.ts +0 -404
- package/src/lib/__tests__/idp.test.ts +0 -415
- package/src/lib/__tests__/instance-candidates.test.ts +0 -146
- package/src/lib/__tests__/instance-target.test.ts +0 -213
- package/src/lib/__tests__/instance.test.ts +0 -192
- package/src/lib/__tests__/local-status.test.ts +0 -202
- package/src/lib/__tests__/log-errors.test.ts +0 -64
- package/src/lib/__tests__/log.test.ts +0 -31
- package/src/lib/__tests__/meta.test.ts +0 -55
- package/src/lib/__tests__/output.test.ts +0 -162
- package/src/lib/__tests__/panel.test.ts +0 -40
- package/src/lib/__tests__/port.test.ts +0 -44
- package/src/lib/__tests__/prompt.test.ts +0 -25
- package/src/lib/__tests__/sdk-deps.test.ts +0 -68
- package/src/lib/__tests__/self.test.ts +0 -50
- package/src/lib/__tests__/skills.test.ts +0 -834
- package/src/lib/__tests__/studio-server-deps.test.ts +0 -111
- package/src/lib/__tests__/table.test.ts +0 -53
- package/src/lib/__tests__/update.test.ts +0 -511
- package/src/lib/__tests__/use-target.test.ts +0 -56
- package/src/lib/__tests__/validation.test.ts +0 -34
- package/src/lib/__tests__/view-assets.test.ts +0 -171
- package/src/lib/__tests__/view-external-open-intent.test.ts +0 -150
- package/src/lib/__tests__/view-external-open-origins.test.ts +0 -27
- package/src/lib/__tests__/view-open-intent.test.ts +0 -280
- package/src/lib/__tests__/view-port-allocation.test.ts +0 -82
- package/src/lib/__tests__/view-server.test.ts +0 -91
- package/src/lib/__tests__/view-session.test.ts +0 -100
- package/src/lib/__tests__/view-snapshot.test.ts +0 -77
- package/src/lib/admin-domain.ts +0 -43
- package/src/lib/admin-instance.ts +0 -56
- package/src/lib/admin-target.ts +0 -290
- package/src/lib/binary.ts +0 -127
- package/src/lib/browser-retention.ts +0 -210
- package/src/lib/browser.ts +0 -150
- package/src/lib/ca-fetch.ts +0 -133
- package/src/lib/command-dx.ts +0 -191
- package/src/lib/concurrency.ts +0 -31
- package/src/lib/config.ts +0 -57
- package/src/lib/credential-lifetime.ts +0 -24
- package/src/lib/domain-publication.ts +0 -104
- package/src/lib/failure-debug.ts +0 -26
- package/src/lib/format.ts +0 -4
- package/src/lib/idp-session.ts +0 -156
- package/src/lib/idp.ts +0 -926
- package/src/lib/instance-candidates.ts +0 -49
- package/src/lib/instance-target.ts +0 -197
- package/src/lib/instance.ts +0 -468
- package/src/lib/invocation.ts +0 -11
- package/src/lib/local-status.ts +0 -152
- package/src/lib/log.ts +0 -143
- package/src/lib/login-flow.ts +0 -201
- package/src/lib/meta.ts +0 -90
- package/src/lib/output.ts +0 -238
- package/src/lib/panel.ts +0 -61
- package/src/lib/port.ts +0 -41
- package/src/lib/proc.ts +0 -87
- package/src/lib/prompt.ts +0 -136
- package/src/lib/provision-instance.ts +0 -139
- package/src/lib/sdk-deps.ts +0 -104
- package/src/lib/self.ts +0 -22
- package/src/lib/skills/lock.ts +0 -117
- package/src/lib/skills/sync.ts +0 -878
- package/src/lib/skills.ts +0 -120
- package/src/lib/table.ts +0 -62
- package/src/lib/update.ts +0 -568
- package/src/lib/use-target.ts +0 -24
- package/src/lib/validation.ts +0 -59
- package/src/lib/view/assets.ts +0 -87
- package/src/lib/view/external-open-intent.ts +0 -41
- package/src/lib/view/external-open-origins.ts +0 -37
- package/src/lib/view/host-capabilities.ts +0 -17
- package/src/lib/view/open-intent.ts +0 -67
- package/src/lib/view/port-allocation.ts +0 -19
- package/src/lib/view/resolve.ts +0 -111
- package/src/lib/view/server.ts +0 -259
- package/src/lib/view/session.ts +0 -154
- package/src/lib/view/snapshot.ts +0 -101
- package/src/paths/.spec/api.d.ts +0 -13
- package/src/paths/.spec/architecture.md +0 -5
- package/src/paths/.spec/layout.ts +0 -3
- package/src/paths/index.ts +0 -13
- package/src/program/.spec/api.d.ts +0 -43
- package/src/program/.spec/architecture.md +0 -18
- package/src/program/.spec/laws/program.ts +0 -29
- package/src/program/.spec/layout.ts +0 -14
- package/src/program/__tests__/program.test.ts +0 -556
- package/src/program/argv.ts +0 -14
- package/src/program/build.ts +0 -205
- package/src/program/command.ts +0 -43
- package/src/program/index.ts +0 -3
- package/src/program/options.ts +0 -41
- package/src/program/registry.ts +0 -64
- package/src/program/tsconfig.json +0 -6
- package/src/setup/__tests__/instance-step.test.ts +0 -256
- package/src/setup/__tests__/util.test.ts +0 -29
- package/src/setup/engine.ts +0 -83
- package/src/setup/render.ts +0 -107
- package/src/setup/steps/admin.ts +0 -93
- package/src/setup/steps/agent-browser.ts +0 -81
- package/src/setup/steps/auth.ts +0 -54
- package/src/setup/steps/domain.ts +0 -68
- package/src/setup/steps/index.ts +0 -18
- package/src/setup/steps/instance.ts +0 -206
- package/src/setup/steps/skills-bridge.ts +0 -70
- package/src/setup/steps/skills.ts +0 -61
- package/src/setup/types.ts +0 -61
- package/src/setup/util.ts +0 -34
- package/src/state/.spec/api.d.ts +0 -152
- package/src/state/.spec/architecture.md +0 -45
- package/src/state/.spec/capabilities/state.ts +0 -7
- package/src/state/.spec/flows/identity-update.ts +0 -22
- package/src/state/.spec/laws/state.ts +0 -73
- package/src/state/.spec/layout.ts +0 -15
- package/src/state/__tests__/exchange-credentials.test.ts +0 -341
- package/src/state/__tests__/files.test.ts +0 -136
- package/src/state/__tests__/fixtures/identity-transition.ts +0 -37
- package/src/state/__tests__/fixtures/session-route-process.ts +0 -93
- package/src/state/__tests__/identities.test.ts +0 -142
- package/src/state/__tests__/paths.test.ts +0 -28
- package/src/state/__tests__/session-routes.test.ts +0 -138
- package/src/state/exchange-credentials.ts +0 -250
- package/src/state/files.ts +0 -170
- package/src/state/identities.ts +0 -229
- package/src/state/index.ts +0 -31
- package/src/state/paths.ts +0 -64
- package/src/state/session-routes.ts +0 -34
- package/src/state/tsconfig.json +0 -6
- package/src/telemetry/__tests__/analyze-log.test.ts +0 -38
- package/src/telemetry/__tests__/gate.test.ts +0 -63
- package/src/telemetry/__tests__/recorder.test.ts +0 -111
- package/src/telemetry/__tests__/redact.test.ts +0 -79
- package/src/telemetry/__tests__/retention.test.ts +0 -267
- package/src/telemetry/__tests__/session.test.ts +0 -166
- package/src/telemetry/__tests__/settings.test.ts +0 -102
- package/src/telemetry/__tests__/store-scan.test.ts +0 -128
- package/src/telemetry/__tests__/trigger.test.ts +0 -78
- package/src/telemetry/adapters/__tests__/claude-code.test.ts +0 -89
- package/src/telemetry/adapters/__tests__/codex.test.ts +0 -138
- package/src/telemetry/adapters/__tests__/index.test.ts +0 -88
- package/src/telemetry/adapters/claude-code.ts +0 -90
- package/src/telemetry/adapters/codex.ts +0 -160
- package/src/telemetry/adapters/index.ts +0 -45
- package/src/telemetry/adapters/types.ts +0 -21
- package/src/telemetry/analyze.ts +0 -249
- package/src/telemetry/gate.ts +0 -79
- package/src/telemetry/recorder.ts +0 -68
- package/src/telemetry/redact.ts +0 -53
- package/src/telemetry/retention.ts +0 -176
- package/src/telemetry/session.ts +0 -94
- package/src/telemetry/settings.ts +0 -79
- package/src/telemetry/store.ts +0 -174
- package/src/telemetry/trigger.ts +0 -107
- package/src/telemetry/types.ts +0 -64
- package/src/test-utils.ts +0 -18
- package/src/ui/.spec/api.d.ts +0 -14
- package/src/ui/.spec/architecture.md +0 -14
- package/src/ui/.spec/laws.ts +0 -50
- package/src/ui/.spec/layout.ts +0 -16
- package/src/ui/__tests__/ui.test.ts +0 -1742
- package/src/ui/index.ts +0 -13
- package/src/ui/lock.ts +0 -89
- package/src/ui/model.ts +0 -83
- package/src/ui/operations.ts +0 -1044
- package/src/ui/project.ts +0 -333
- package/src/ui/release.ts +0 -398
- package/src/ui/runner.ts +0 -18
- package/studio/client/dist/assets/index-BFVFs_8x.js +0 -81
- package/studio/client/dist/assets/index-DuB9iUdu.css +0 -2
- package/studio/client/dist/assets/schema-studio-DH6oEWME.js +0 -8
- package/studio/tsconfig.json +0 -24
- package/viewer/tsconfig.json +0 -13
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
|
|
2
|
-
import { mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises'
|
|
3
|
-
import { tmpdir } from 'node:os'
|
|
4
|
-
import { join } from 'node:path'
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
IDENTITY_STORE_VERSION,
|
|
8
|
-
IdentityStateError,
|
|
9
|
-
readIdentityStore,
|
|
10
|
-
updateIdentityStore,
|
|
11
|
-
type IdentityStore,
|
|
12
|
-
} from '../identities'
|
|
13
|
-
|
|
14
|
-
let directory: string
|
|
15
|
-
let path: string
|
|
16
|
-
|
|
17
|
-
const legacy = JSON.stringify(
|
|
18
|
-
{
|
|
19
|
-
default: 'manager',
|
|
20
|
-
identities: {
|
|
21
|
-
manager: {
|
|
22
|
-
subject: 'manager',
|
|
23
|
-
createdAt: '2024-01-01T00:00:00.000Z',
|
|
24
|
-
source: 'key',
|
|
25
|
-
mode: 'local',
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
null,
|
|
30
|
-
2,
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
beforeEach(async () => {
|
|
34
|
-
directory = await mkdtemp(join(tmpdir(), 'astrale-identity-state-'))
|
|
35
|
-
path = join(directory, 'identities.json')
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
afterEach(async () => {
|
|
39
|
-
await rm(directory, { recursive: true, force: true })
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
describe('identity state', () => {
|
|
43
|
-
/** @evidence TEST-CLI-STATE-IDENTITY-READ-SAFE */
|
|
44
|
-
test('reads missing, legacy, and current state without writing', async () => {
|
|
45
|
-
const missing = await readIdentityStore({
|
|
46
|
-
path,
|
|
47
|
-
now: () => new Date('2025-01-01T00:00:00.000Z'),
|
|
48
|
-
})
|
|
49
|
-
expect(missing).toEqual({ default: '', identities: {} })
|
|
50
|
-
await expect(readFile(path, 'utf-8')).rejects.toThrow()
|
|
51
|
-
|
|
52
|
-
await writeFile(path, legacy)
|
|
53
|
-
expect((await readIdentityStore({ path })).default).toBe('manager')
|
|
54
|
-
expect(await readFile(path, 'utf-8')).toBe(legacy)
|
|
55
|
-
await expect(readFile(`${path}.v0.bak`, 'utf-8')).rejects.toThrow()
|
|
56
|
-
|
|
57
|
-
const current = `${JSON.stringify({ version: IDENTITY_STORE_VERSION, ...missing })}\n`
|
|
58
|
-
await writeFile(path, current)
|
|
59
|
-
expect((await readIdentityStore({ path })).identities).toEqual({})
|
|
60
|
-
expect(await readFile(path, 'utf-8')).toBe(current)
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
/** @evidence TEST-CLI-STATE-IDENTITY-FAILS-CLOSED */
|
|
64
|
-
test('rejects malformed and unsupported state without replacing it', async () => {
|
|
65
|
-
for (const raw of ['{', JSON.stringify({ version: 2, default: 'manager', identities: {} })]) {
|
|
66
|
-
await writeFile(path, raw)
|
|
67
|
-
const error = await readIdentityStore({ path }).catch((caught) => caught)
|
|
68
|
-
expect(error).toBeInstanceOf(IdentityStateError)
|
|
69
|
-
expect((error as IdentityStateError).code).toBe(
|
|
70
|
-
raw === '{' ? 'IDENTITY_STATE_INVALID' : 'IDENTITY_STATE_VERSION_UNSUPPORTED',
|
|
71
|
-
)
|
|
72
|
-
expect(await readFile(path, 'utf-8')).toBe(raw)
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
/** @evidence TEST-CLI-STATE-IDENTITY-MIGRATES */
|
|
77
|
-
test('backs up exact legacy bytes before the first V1 mutation', async () => {
|
|
78
|
-
await writeFile(path, legacy)
|
|
79
|
-
|
|
80
|
-
await updateIdentityStore(
|
|
81
|
-
(current) => ({ next: { ...current, default: 'alice' }, value: undefined }),
|
|
82
|
-
{ path },
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
expect(await readFile(`${path}.v0.bak`, 'utf-8')).toBe(legacy)
|
|
86
|
-
expect((await stat(`${path}.v0.bak`)).mode & 0o777).toBe(0o600)
|
|
87
|
-
const written = JSON.parse(await readFile(path, 'utf-8')) as {
|
|
88
|
-
version: number
|
|
89
|
-
default: string
|
|
90
|
-
}
|
|
91
|
-
expect(written).toMatchObject({ version: 1, default: 'alice' })
|
|
92
|
-
expect((await stat(path)).mode & 0o777).toBe(0o600)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
/** @evidence TEST-CLI-STATE-IDENTITY-CONCURRENT */
|
|
96
|
-
test('retains concurrent selection and registration from separate processes', async () => {
|
|
97
|
-
const alice = {
|
|
98
|
-
subject: 'alice',
|
|
99
|
-
createdAt: '2025-01-01T00:00:00.000Z',
|
|
100
|
-
source: 'key' as const,
|
|
101
|
-
mode: 'local' as const,
|
|
102
|
-
}
|
|
103
|
-
const bob = { ...alice, subject: 'bob' }
|
|
104
|
-
await writeFile(
|
|
105
|
-
path,
|
|
106
|
-
JSON.stringify({
|
|
107
|
-
version: IDENTITY_STORE_VERSION,
|
|
108
|
-
default: 'manager',
|
|
109
|
-
identities: {
|
|
110
|
-
manager: { ...alice, subject: 'manager' },
|
|
111
|
-
alice,
|
|
112
|
-
bob,
|
|
113
|
-
},
|
|
114
|
-
}),
|
|
115
|
-
)
|
|
116
|
-
const fixture = join(import.meta.dir, 'fixtures', 'identity-transition.ts')
|
|
117
|
-
const commands = [
|
|
118
|
-
['default', 'alice'],
|
|
119
|
-
...Array.from({ length: 8 }, (_, index) => ['registration', 'bob', `instance-${index}`]),
|
|
120
|
-
]
|
|
121
|
-
const processes = commands.map((args) =>
|
|
122
|
-
Bun.spawn({
|
|
123
|
-
cmd: ['bun', fixture, ...args],
|
|
124
|
-
env: { ...process.env, ASTRALE_HOME: directory },
|
|
125
|
-
stdout: 'pipe',
|
|
126
|
-
stderr: 'pipe',
|
|
127
|
-
}),
|
|
128
|
-
)
|
|
129
|
-
const outcomes = await Promise.all(
|
|
130
|
-
processes.map(async (process) => ({
|
|
131
|
-
code: await process.exited,
|
|
132
|
-
stderr: await new Response(process.stderr).text(),
|
|
133
|
-
})),
|
|
134
|
-
)
|
|
135
|
-
expect(outcomes).toEqual(outcomes.map(() => ({ code: 0, stderr: '' })))
|
|
136
|
-
|
|
137
|
-
const store: IdentityStore = await readIdentityStore({ path })
|
|
138
|
-
expect(store.default).toBe('alice')
|
|
139
|
-
expect(Object.keys(store.identities.bob.registrations ?? {})).toHaveLength(8)
|
|
140
|
-
await expect(readFile(`${path}.lock`, 'utf-8')).rejects.toThrow()
|
|
141
|
-
})
|
|
142
|
-
})
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
import { join } from 'node:path'
|
|
3
|
-
|
|
4
|
-
import { createPaths } from '../paths'
|
|
5
|
-
|
|
6
|
-
describe('state paths', () => {
|
|
7
|
-
/** @evidence TEST-CLI-STATE-PATHS-CAPTURED */
|
|
8
|
-
test('captures explicit and environment coordinates once', () => {
|
|
9
|
-
const environment: Record<string, string | undefined> = {
|
|
10
|
-
ASTRALE_HOME: '/environment/home',
|
|
11
|
-
ASTRALE_KEYS_DIR: '/environment/keys',
|
|
12
|
-
ASTRALE_DATA_DIR: '/environment/data',
|
|
13
|
-
}
|
|
14
|
-
const explicit = createPaths('/explicit/home', environment)
|
|
15
|
-
const inherited = createPaths(undefined, environment)
|
|
16
|
-
|
|
17
|
-
environment.ASTRALE_HOME = '/changed/home'
|
|
18
|
-
environment.ASTRALE_KEYS_DIR = '/changed/keys'
|
|
19
|
-
|
|
20
|
-
expect(explicit.home).toBe('/explicit/home')
|
|
21
|
-
expect(explicit.keys).toBe('/environment/keys')
|
|
22
|
-
expect(explicit.config).toBe(join('/explicit/home', 'config.json'))
|
|
23
|
-
expect(explicit.sessionRoutes).toBe(join('/explicit/home', 'session', 'routes.json'))
|
|
24
|
-
expect(inherited.home).toBe('/environment/home')
|
|
25
|
-
expect(inherited.keys).toBe('/environment/keys')
|
|
26
|
-
expect(Object.isFrozen(explicit)).toBe(true)
|
|
27
|
-
})
|
|
28
|
-
})
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import type { SessionRouteArtifact } from '@astrale-os/sdk/client/session'
|
|
2
|
-
|
|
3
|
-
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
|
|
4
|
-
import { access, mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises'
|
|
5
|
-
import { tmpdir } from 'node:os'
|
|
6
|
-
import { join } from 'node:path'
|
|
7
|
-
|
|
8
|
-
import { FileSessionRouteStore } from '../session-routes'
|
|
9
|
-
|
|
10
|
-
let directory: string
|
|
11
|
-
let path: string
|
|
12
|
-
|
|
13
|
-
beforeEach(async () => {
|
|
14
|
-
directory = await mkdtemp(join(tmpdir(), 'astrale-session-routes-'))
|
|
15
|
-
path = join(directory, 'private', 'routes.json')
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
afterEach(async () => {
|
|
19
|
-
await rm(directory, { recursive: true, force: true })
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
describe('session route file store', () => {
|
|
23
|
-
test('round-trips the Kernel-owned artifact under owner-private filesystem modes', async () => {
|
|
24
|
-
const artifact: SessionRouteArtifact = { version: 1, entries: {} }
|
|
25
|
-
const store = new FileSessionRouteStore(path)
|
|
26
|
-
|
|
27
|
-
store.write(artifact)
|
|
28
|
-
|
|
29
|
-
expect(store.read()).toEqual(artifact)
|
|
30
|
-
expect((await stat(path)).mode & 0o777).toBe(0o600)
|
|
31
|
-
expect((await stat(join(directory, 'private'))).mode & 0o777).toBe(0o700)
|
|
32
|
-
expect(await readFile(path, 'utf8')).toBe('{"version":1,"entries":{}}\n')
|
|
33
|
-
|
|
34
|
-
store.clear()
|
|
35
|
-
await expect(access(path)).rejects.toThrow()
|
|
36
|
-
expect(() => store.clear()).not.toThrow()
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
test('leaves malformed representation recovery to Kernel Client as a cold miss', async () => {
|
|
40
|
-
await mkdir(join(directory, 'private'))
|
|
41
|
-
await writeFile(path, '{invalid')
|
|
42
|
-
const fixture = join(import.meta.dir, 'fixtures', 'session-route-process.ts')
|
|
43
|
-
|
|
44
|
-
expect(await runFixture(fixture, path)).toEqual({
|
|
45
|
-
value: 'done',
|
|
46
|
-
sourceAttempts: 1,
|
|
47
|
-
destinationAttempts: 1,
|
|
48
|
-
})
|
|
49
|
-
expect(JSON.parse(await readFile(path, 'utf8'))).toMatchObject({ version: 1 })
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
test('reuses Kernel-admitted routing state in a separate operating-system process', async () => {
|
|
53
|
-
const routePath = join(directory, 'private', 'routes.json')
|
|
54
|
-
const fixture = join(import.meta.dir, 'fixtures', 'session-route-process.ts')
|
|
55
|
-
|
|
56
|
-
const first = await runFixture(fixture, routePath)
|
|
57
|
-
const second = await runFixture(fixture, routePath)
|
|
58
|
-
|
|
59
|
-
expect(first).toEqual({ value: 'done', sourceAttempts: 1, destinationAttempts: 1 })
|
|
60
|
-
expect(second).toEqual({ value: 'done', sourceAttempts: 0, destinationAttempts: 1 })
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
test('publishes only complete owner-private artifacts under concurrent process writers', async () => {
|
|
64
|
-
const routePath = join(directory, 'private', 'routes.json')
|
|
65
|
-
const fixture = join(import.meta.dir, 'fixtures', 'session-route-process.ts')
|
|
66
|
-
|
|
67
|
-
const results = await Promise.all(
|
|
68
|
-
Array.from({ length: 8 }, () => runFixture(fixture, routePath)),
|
|
69
|
-
)
|
|
70
|
-
expect(results).toHaveLength(8)
|
|
71
|
-
expect(results.every((result) => result.destinationAttempts === 1)).toBe(true)
|
|
72
|
-
expect(JSON.parse(await readFile(routePath, 'utf8'))).toMatchObject({ version: 1 })
|
|
73
|
-
expect((await stat(routePath)).mode & 0o777).toBe(0o600)
|
|
74
|
-
expect(await runFixture(fixture, routePath)).toEqual({
|
|
75
|
-
value: 'done',
|
|
76
|
-
sourceAttempts: 0,
|
|
77
|
-
destinationAttempts: 1,
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
test('auth logout clears every persisted bearer cache through the real CLI command', async () => {
|
|
82
|
-
const routePath = join(directory, 'session', 'routes.json')
|
|
83
|
-
const exchangePath = join(directory, 'exchange', 'credentials.json')
|
|
84
|
-
await mkdir(join(directory, 'session'), { recursive: true })
|
|
85
|
-
await mkdir(join(directory, 'exchange'), { recursive: true })
|
|
86
|
-
await writeFile(routePath, '{"version":1,"entries":{"confidential":{}}}\n')
|
|
87
|
-
await writeFile(
|
|
88
|
-
exchangePath,
|
|
89
|
-
'{"version":2,"entries":{"confidential":{"credential":"bearer"}}}\n',
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
const child = Bun.spawn(
|
|
93
|
-
[
|
|
94
|
-
process.execPath,
|
|
95
|
-
join(import.meta.dir, '../../..', 'bin', 'astrale.ts'),
|
|
96
|
-
'auth',
|
|
97
|
-
'logout',
|
|
98
|
-
'--all',
|
|
99
|
-
'--json',
|
|
100
|
-
],
|
|
101
|
-
{
|
|
102
|
-
env: { ...process.env, ASTRALE_HOME: directory },
|
|
103
|
-
stdout: 'pipe',
|
|
104
|
-
stderr: 'pipe',
|
|
105
|
-
},
|
|
106
|
-
)
|
|
107
|
-
const [exitCode, stdout, stderr] = await Promise.all([
|
|
108
|
-
child.exited,
|
|
109
|
-
new Response(child.stdout).text(),
|
|
110
|
-
new Response(child.stderr).text(),
|
|
111
|
-
])
|
|
112
|
-
|
|
113
|
-
expect(exitCode, stderr).toBe(0)
|
|
114
|
-
expect(JSON.parse(stdout)).toEqual({ cleared: [] })
|
|
115
|
-
await expect(access(routePath)).rejects.toThrow()
|
|
116
|
-
expect(JSON.parse(await readFile(exchangePath, 'utf8'))).toEqual({ version: 2, entries: {} })
|
|
117
|
-
})
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
interface FixtureResult {
|
|
121
|
-
readonly value: string
|
|
122
|
-
readonly sourceAttempts: number
|
|
123
|
-
readonly destinationAttempts: number
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async function runFixture(fixture: string, routePath: string): Promise<FixtureResult> {
|
|
127
|
-
const child = Bun.spawn([process.execPath, fixture, routePath], {
|
|
128
|
-
stdout: 'pipe',
|
|
129
|
-
stderr: 'pipe',
|
|
130
|
-
})
|
|
131
|
-
const [exitCode, stdout, stderr] = await Promise.all([
|
|
132
|
-
child.exited,
|
|
133
|
-
new Response(child.stdout).text(),
|
|
134
|
-
new Response(child.stderr).text(),
|
|
135
|
-
])
|
|
136
|
-
expect(exitCode, stderr).toBe(0)
|
|
137
|
-
return JSON.parse(stdout) as FixtureResult
|
|
138
|
-
}
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import { credential, grant } from '@astrale-os/sdk/auth'
|
|
2
|
-
import { chmod, mkdir, readFile } from 'node:fs/promises'
|
|
3
|
-
import { dirname } from 'node:path'
|
|
4
|
-
|
|
5
|
-
import { credentialLifetimeCovers } from '../lib/credential-lifetime'
|
|
6
|
-
import { atomicWrite, withFileLock } from './files'
|
|
7
|
-
import { EXCHANGE_CREDENTIALS_PATH } from './paths'
|
|
8
|
-
|
|
9
|
-
const VERSION = 2
|
|
10
|
-
const MINIMUM_REMAINING_SECONDS = 30
|
|
11
|
-
|
|
12
|
-
export namespace exchange {
|
|
13
|
-
export interface Artifact {
|
|
14
|
-
readonly version: 2
|
|
15
|
-
readonly entries: Record<string, Entry>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface Key {
|
|
19
|
-
readonly kernelIssuer: string
|
|
20
|
-
readonly domainIssuer: string
|
|
21
|
-
readonly sourceIssuer: string
|
|
22
|
-
readonly sourceSubject: string
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface Entry {
|
|
26
|
-
readonly credential: string
|
|
27
|
-
readonly expiresAt: number
|
|
28
|
-
readonly user: string
|
|
29
|
-
readonly sourceIssuer: string
|
|
30
|
-
readonly sourceSubject: string
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export class ExchangeCredentialCache {
|
|
35
|
-
private readonly refreshing = new Map<string, Promise<string>>()
|
|
36
|
-
|
|
37
|
-
constructor(private readonly path = EXCHANGE_CREDENTIALS_PATH) {}
|
|
38
|
-
|
|
39
|
-
getOrRefresh(
|
|
40
|
-
key: exchange.Key,
|
|
41
|
-
minimumRemainingSeconds: number,
|
|
42
|
-
refresh: () => Promise<exchange.Entry>,
|
|
43
|
-
now = () => Math.floor(Date.now() / 1_000),
|
|
44
|
-
): Promise<string> {
|
|
45
|
-
if (!Number.isSafeInteger(minimumRemainingSeconds) || minimumRemainingSeconds < 1) {
|
|
46
|
-
throw new TypeError('Exchange credential minimum lifetime must be a positive safe integer.')
|
|
47
|
-
}
|
|
48
|
-
const encoded = encodeKey(key)
|
|
49
|
-
const pendingKey = `${encoded}\0${minimumRemainingSeconds}`
|
|
50
|
-
const current = this.refreshing.get(pendingKey)
|
|
51
|
-
if (current !== undefined) return current
|
|
52
|
-
const pending = this.getOrRefreshOnce(
|
|
53
|
-
key,
|
|
54
|
-
encoded,
|
|
55
|
-
minimumRemainingSeconds,
|
|
56
|
-
refresh,
|
|
57
|
-
now,
|
|
58
|
-
).finally(() => {
|
|
59
|
-
this.refreshing.delete(pendingKey)
|
|
60
|
-
})
|
|
61
|
-
this.refreshing.set(pendingKey, pending)
|
|
62
|
-
return pending
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async deleteKernel(kernelIssuer: string): Promise<void> {
|
|
66
|
-
await this.transition((store) => {
|
|
67
|
-
for (const encoded of Object.keys(store.entries)) {
|
|
68
|
-
const key = decodeKey(encoded)
|
|
69
|
-
if (key === undefined || key.kernelIssuer === kernelIssuer) delete store.entries[encoded]
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async clear(): Promise<void> {
|
|
75
|
-
await this.transition((store) => {
|
|
76
|
-
for (const encoded of Object.keys(store.entries)) delete store.entries[encoded]
|
|
77
|
-
})
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
private async getOrRefreshOnce(
|
|
81
|
-
key: exchange.Key,
|
|
82
|
-
encoded: string,
|
|
83
|
-
minimumRemainingSeconds: number,
|
|
84
|
-
refresh: () => Promise<exchange.Entry>,
|
|
85
|
-
now: () => number,
|
|
86
|
-
): Promise<string> {
|
|
87
|
-
return withFileLock(`${this.path}.lock`, async () => {
|
|
88
|
-
await ensurePrivateDirectory(this.path)
|
|
89
|
-
const store = await readStore(this.path)
|
|
90
|
-
const changed = scrub(store, now())
|
|
91
|
-
const cached = store.entries[encoded]
|
|
92
|
-
if (cached !== undefined && validEntry(key, cached, now(), minimumRemainingSeconds)) {
|
|
93
|
-
if (changed) await writeStore(this.path, store)
|
|
94
|
-
return cached.credential
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const next = await refresh()
|
|
98
|
-
if (!validEntry(key, next, now(), minimumRemainingSeconds)) {
|
|
99
|
-
throw new Error(
|
|
100
|
-
'Token exchange returned a credential inconsistent with its cache key or required lifetime.',
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
store.entries[encoded] = Object.freeze({ ...next })
|
|
104
|
-
await writeStore(this.path, store)
|
|
105
|
-
return next.credential
|
|
106
|
-
})
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private async transition(change: (store: exchange.Artifact) => void): Promise<void> {
|
|
110
|
-
await withFileLock(`${this.path}.lock`, async () => {
|
|
111
|
-
await ensurePrivateDirectory(this.path)
|
|
112
|
-
const store = await readStore(this.path)
|
|
113
|
-
change(store)
|
|
114
|
-
await writeStore(this.path, store)
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async function readStore(path: string): Promise<exchange.Artifact> {
|
|
120
|
-
try {
|
|
121
|
-
const input = JSON.parse(await readFile(path, 'utf8')) as unknown
|
|
122
|
-
if (input === null || typeof input !== 'object' || Array.isArray(input)) return emptyStore()
|
|
123
|
-
const value = input as Record<string, unknown>
|
|
124
|
-
if (
|
|
125
|
-
value.version !== VERSION ||
|
|
126
|
-
value.entries === null ||
|
|
127
|
-
typeof value.entries !== 'object' ||
|
|
128
|
-
Array.isArray(value.entries)
|
|
129
|
-
) {
|
|
130
|
-
return emptyStore()
|
|
131
|
-
}
|
|
132
|
-
return { version: VERSION, entries: { ...(value.entries as Record<string, exchange.Entry>) } }
|
|
133
|
-
} catch {
|
|
134
|
-
return emptyStore()
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function emptyStore(): exchange.Artifact {
|
|
139
|
-
return { version: VERSION, entries: {} }
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async function writeStore(path: string, store: exchange.Artifact): Promise<void> {
|
|
143
|
-
await atomicWrite(path, `${JSON.stringify(store, null, 2)}\n`)
|
|
144
|
-
await chmod(path, 0o600)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async function ensurePrivateDirectory(path: string): Promise<void> {
|
|
148
|
-
const directory = dirname(path)
|
|
149
|
-
await mkdir(directory, { recursive: true, mode: 0o700 })
|
|
150
|
-
await chmod(directory, 0o700)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function validEntry(
|
|
154
|
-
key: exchange.Key,
|
|
155
|
-
entry: exchange.Entry,
|
|
156
|
-
now: number,
|
|
157
|
-
minimumRemaining = MINIMUM_REMAINING_SECONDS,
|
|
158
|
-
): boolean {
|
|
159
|
-
if (
|
|
160
|
-
entry === null ||
|
|
161
|
-
typeof entry !== 'object' ||
|
|
162
|
-
Reflect.ownKeys(entry).length !== 5 ||
|
|
163
|
-
typeof entry.credential !== 'string' ||
|
|
164
|
-
entry.credential.length === 0 ||
|
|
165
|
-
!Number.isSafeInteger(entry.expiresAt) ||
|
|
166
|
-
entry.expiresAt - now < minimumRemaining ||
|
|
167
|
-
typeof entry.user !== 'string' ||
|
|
168
|
-
entry.user.length === 0 ||
|
|
169
|
-
entry.sourceIssuer !== key.sourceIssuer ||
|
|
170
|
-
entry.sourceSubject !== key.sourceSubject
|
|
171
|
-
) {
|
|
172
|
-
return false
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
const inspected = credential.inspect(entry.credential)
|
|
176
|
-
const carried = grant.acceptUnresolved(inspected.claims.grant).expr
|
|
177
|
-
if (
|
|
178
|
-
carried.kind !== 'identity' ||
|
|
179
|
-
!('credential' in carried) ||
|
|
180
|
-
typeof carried.credential !== 'string'
|
|
181
|
-
) {
|
|
182
|
-
return false
|
|
183
|
-
}
|
|
184
|
-
const proof = credential.inspect(carried.credential)
|
|
185
|
-
const issued = proof.claims.delegation
|
|
186
|
-
if (
|
|
187
|
-
issued === null ||
|
|
188
|
-
typeof issued !== 'object' ||
|
|
189
|
-
Array.isArray(issued) ||
|
|
190
|
-
Reflect.ownKeys(issued).length !== 2 ||
|
|
191
|
-
!Object.hasOwn(issued, 'v') ||
|
|
192
|
-
!Object.hasOwn(issued, 'expr') ||
|
|
193
|
-
Reflect.get(issued, 'v') !== 1
|
|
194
|
-
) {
|
|
195
|
-
return false
|
|
196
|
-
}
|
|
197
|
-
grant.accept({ expr: Reflect.get(issued, 'expr') })
|
|
198
|
-
return (
|
|
199
|
-
inspected.iss === key.domainIssuer &&
|
|
200
|
-
inspected.aud === key.kernelIssuer &&
|
|
201
|
-
inspected.claims.exp === entry.expiresAt &&
|
|
202
|
-
!Object.hasOwn(inspected.claims, 'delegation') &&
|
|
203
|
-
proof.iss === key.kernelIssuer &&
|
|
204
|
-
proof.sub === entry.user &&
|
|
205
|
-
proof.aud === key.kernelIssuer &&
|
|
206
|
-
typeof proof.claims.exp === 'number' &&
|
|
207
|
-
Number.isSafeInteger(proof.claims.exp) &&
|
|
208
|
-
credentialLifetimeCovers(proof.claims.exp, minimumRemaining, now)
|
|
209
|
-
)
|
|
210
|
-
} catch {
|
|
211
|
-
return false
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function encodeKey(key: exchange.Key): string {
|
|
216
|
-
return JSON.stringify([key.kernelIssuer, key.domainIssuer, key.sourceIssuer, key.sourceSubject])
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function decodeKey(input: string): exchange.Key | undefined {
|
|
220
|
-
try {
|
|
221
|
-
const value = JSON.parse(input) as unknown
|
|
222
|
-
if (
|
|
223
|
-
!Array.isArray(value) ||
|
|
224
|
-
value.length !== 4 ||
|
|
225
|
-
value.some((part) => typeof part !== 'string' || part.length === 0)
|
|
226
|
-
) {
|
|
227
|
-
return undefined
|
|
228
|
-
}
|
|
229
|
-
return {
|
|
230
|
-
kernelIssuer: value[0]!,
|
|
231
|
-
domainIssuer: value[1]!,
|
|
232
|
-
sourceIssuer: value[2]!,
|
|
233
|
-
sourceSubject: value[3]!,
|
|
234
|
-
}
|
|
235
|
-
} catch {
|
|
236
|
-
return undefined
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function scrub(store: Pick<exchange.Artifact, 'entries'>, now: number): boolean {
|
|
241
|
-
let changed = false
|
|
242
|
-
for (const [encoded, entry] of Object.entries(store.entries)) {
|
|
243
|
-
const key = decodeKey(encoded)
|
|
244
|
-
if (key === undefined || !validEntry(key, entry, now, 1)) {
|
|
245
|
-
delete store.entries[encoded]
|
|
246
|
-
changed = true
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return changed
|
|
250
|
-
}
|