@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
package/src/commands/view.ts
DELETED
|
@@ -1,658 +0,0 @@
|
|
|
1
|
-
import type { ResolvedView } from '@astrale-os/shell'
|
|
2
|
-
|
|
3
|
-
import chalk from 'chalk'
|
|
4
|
-
import { randomBytes } from 'node:crypto'
|
|
5
|
-
import { closeSync, existsSync, statSync } from 'node:fs'
|
|
6
|
-
import { readdir, readFile } from 'node:fs/promises'
|
|
7
|
-
import { dirname, join } from 'node:path'
|
|
8
|
-
|
|
9
|
-
import type { KernelCommandOpts } from '../connection'
|
|
10
|
-
import type { ViewServeConfig, ViewSessionRecord } from '../lib/view/session'
|
|
11
|
-
import type { CommandDefinition } from '../program/index'
|
|
12
|
-
|
|
13
|
-
import { expandSelfInPath, withClientSession } from '../connection'
|
|
14
|
-
import { AstraleError } from '../errors'
|
|
15
|
-
import { readIdentities } from '../identity/index'
|
|
16
|
-
import { ab, AGENT_BROWSER_REPO, BROWSER_DIR, findAgentBrowser } from '../lib/browser'
|
|
17
|
-
import { readInstances } from '../lib/instance'
|
|
18
|
-
import { fatal, log } from '../lib/log'
|
|
19
|
-
import { isMachine, output, type RawOutputOpts } from '../lib/output'
|
|
20
|
-
import { findFreePort } from '../lib/port'
|
|
21
|
-
import { run, spawnHandle } from '../lib/proc'
|
|
22
|
-
import { admitExternalOpenOrigins } from '../lib/view/external-open-origins'
|
|
23
|
-
import { withViewPortAllocationLock } from '../lib/view/port-allocation'
|
|
24
|
-
import {
|
|
25
|
-
candidateSlug,
|
|
26
|
-
parseViewSpec,
|
|
27
|
-
pickCandidate,
|
|
28
|
-
resolveViewCandidates,
|
|
29
|
-
selectedView,
|
|
30
|
-
type ViewCandidate,
|
|
31
|
-
viewOwnerTarget,
|
|
32
|
-
} from '../lib/view/resolve'
|
|
33
|
-
import { ensureViewerAssets } from '../lib/view/server'
|
|
34
|
-
import {
|
|
35
|
-
closeSession,
|
|
36
|
-
configPath,
|
|
37
|
-
listSessions,
|
|
38
|
-
logPath,
|
|
39
|
-
openSessionLog,
|
|
40
|
-
saveRecord,
|
|
41
|
-
saveServeConfig,
|
|
42
|
-
} from '../lib/view/session'
|
|
43
|
-
import { snapshotText, waitForSettledSnapshot } from '../lib/view/snapshot'
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* `astrale view` — open ONE view in a local browser shell, authenticated as
|
|
47
|
-
* the CLI identity, driveable by agent-browser (default) or a real browser.
|
|
48
|
-
* Design + protocol details: VIEW_CLI_SPEC.md at the workspace root.
|
|
49
|
-
*/
|
|
50
|
-
|
|
51
|
-
type ViewOpts = KernelCommandOpts &
|
|
52
|
-
RawOutputOpts & {
|
|
53
|
-
target?: string
|
|
54
|
-
view?: string
|
|
55
|
-
list?: boolean
|
|
56
|
-
viewUrl?: string
|
|
57
|
-
handshake?: 'shell' | 'none'
|
|
58
|
-
headed?: boolean
|
|
59
|
-
browser?: boolean
|
|
60
|
-
open?: boolean
|
|
61
|
-
snapshot?: boolean
|
|
62
|
-
screenshot?: string
|
|
63
|
-
sessions?: boolean
|
|
64
|
-
close?: string | boolean
|
|
65
|
-
all?: boolean
|
|
66
|
-
allowExternalOrigin?: string[]
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const VIEW_PORT_BASE = 4419
|
|
70
|
-
const VIEW_PORT_SPAN = 20
|
|
71
|
-
const IDLE_MS = 30 * 60_000
|
|
72
|
-
const READY_TIMEOUT_MS = 8000
|
|
73
|
-
const STATE_TIMEOUT_MS = 25_000
|
|
74
|
-
const POLL_MS = 250
|
|
75
|
-
/** Dedicated agent-browser profile for view sessions (no cookies involved). */
|
|
76
|
-
const VIEW_PROFILE = `${BROWSER_DIR}/_view`
|
|
77
|
-
|
|
78
|
-
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
79
|
-
|
|
80
|
-
export async function resolveSession(
|
|
81
|
-
spec: string,
|
|
82
|
-
opts: ViewOpts,
|
|
83
|
-
): Promise<{ view?: ResolvedView; candidates: ViewCandidate[] }> {
|
|
84
|
-
rejectUnrepresentableOverrides(opts)
|
|
85
|
-
const parsed = parseViewSpec(spec)
|
|
86
|
-
if (parsed.kind === 'target' && opts.target) {
|
|
87
|
-
fatal(new Error('Pass the target either as the positional or as --target, not both'))
|
|
88
|
-
}
|
|
89
|
-
if (parsed.kind === 'view' && opts.view) {
|
|
90
|
-
fatal(new Error('An explicit ViewPath cannot be combined with --view <slug>'))
|
|
91
|
-
}
|
|
92
|
-
const targetInput =
|
|
93
|
-
parsed.kind === 'target' ? parsed.path : (opts.target ?? viewOwnerTarget(parsed.path))
|
|
94
|
-
const { target, candidates } = await withClientSession(opts, async (context) => {
|
|
95
|
-
const { path: target } = await expandSelfInPath(targetInput, context)
|
|
96
|
-
return { target, candidates: await resolveViewCandidates(context, target) }
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
if (opts.list) {
|
|
100
|
-
return { candidates }
|
|
101
|
-
}
|
|
102
|
-
const selector = parsed.kind === 'view' ? parsed.path : opts.view
|
|
103
|
-
const picked = await chooseCandidate(candidates, target, selector, opts)
|
|
104
|
-
return {
|
|
105
|
-
view: selectedView(picked),
|
|
106
|
-
candidates,
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Retain the frozen command flags while failing closed: neither legacy flag
|
|
112
|
-
* has a truthful representation in V2's verified View placement contract.
|
|
113
|
-
*/
|
|
114
|
-
export function rejectUnrepresentableOverrides(
|
|
115
|
-
opts: Pick<ViewOpts, 'handshake' | 'viewUrl'>,
|
|
116
|
-
): void {
|
|
117
|
-
if (opts.viewUrl === undefined && opts.handshake === undefined) return
|
|
118
|
-
throw new AstraleError(
|
|
119
|
-
'UNSUPPORTED_VIEW_OVERRIDE',
|
|
120
|
-
'--view-url and --handshake cannot override a V2 View: the Shell mounts one verified View placement with complete provenance.',
|
|
121
|
-
'Pass a ViewPath or target and use the placement published by its Domain.',
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async function chooseCandidate(
|
|
126
|
-
candidates: ViewCandidate[],
|
|
127
|
-
anchor: string,
|
|
128
|
-
selector: string | undefined,
|
|
129
|
-
opts: ViewOpts,
|
|
130
|
-
): Promise<ViewCandidate> {
|
|
131
|
-
const picked = pickCandidate(candidates, anchor, selector)
|
|
132
|
-
if (picked !== 'ambiguous') return picked
|
|
133
|
-
if (process.stdin.isTTY && !isMachine(opts)) {
|
|
134
|
-
const { select } = await import('@inquirer/prompts')
|
|
135
|
-
return select({
|
|
136
|
-
message: `${anchor} has ${candidates.length} views — open which?`,
|
|
137
|
-
choices: candidates.map((c) => ({
|
|
138
|
-
name: `${candidateSlug(c)} ${chalk.dim(c.url)}`,
|
|
139
|
-
value: c,
|
|
140
|
-
})),
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
throw new AstraleError(
|
|
144
|
-
'AMBIGUOUS_VIEW',
|
|
145
|
-
`${anchor} resolves ${candidates.length} views — pick one with --view <slug>: ${candidates.map(candidateSlug).join(', ')}`,
|
|
146
|
-
)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* The session server must run under NODE when possible: an orphaned Bun
|
|
151
|
-
* process on macOS cannot open TLS sockets at all (its TLS init needs the
|
|
152
|
-
* user session's trust services), so token mints and the kernel proxy would
|
|
153
|
-
* die once the CLI exits. The published CLI entry is node-runnable; a dev
|
|
154
|
-
* checkout builds `dist/astrale.js` on demand (Bun is present there).
|
|
155
|
-
*/
|
|
156
|
-
interface ServeRuntimeEnvironment {
|
|
157
|
-
readonly entry: string | undefined
|
|
158
|
-
readonly executable: string
|
|
159
|
-
readonly exists: typeof existsSync
|
|
160
|
-
readonly find: typeof findOnPath
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export async function resolveServeRuntime(
|
|
164
|
-
environment: Partial<ServeRuntimeEnvironment> = {},
|
|
165
|
-
): Promise<{ file: string; args: string[] }> {
|
|
166
|
-
const entry = environment.entry ?? process.argv[1]
|
|
167
|
-
const executable = environment.executable ?? process.execPath
|
|
168
|
-
const exists = environment.exists ?? existsSync
|
|
169
|
-
const find = environment.find ?? findOnPath
|
|
170
|
-
const node = await find('node')
|
|
171
|
-
if (node && entry?.endsWith('.js') && exists(entry)) return { file: node, args: [entry] }
|
|
172
|
-
if (node && entry?.endsWith('.ts')) {
|
|
173
|
-
const dist = join(dirname(entry), '..', 'dist', 'astrale.js')
|
|
174
|
-
await ensureDevDist(entry, dist)
|
|
175
|
-
if (exists(dist)) return { file: node, args: [dist] }
|
|
176
|
-
}
|
|
177
|
-
return directServeRuntime(executable, entry, entry !== undefined && exists(entry))
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** Reinvoke a compiled executable without its virtual Bun filesystem entry. */
|
|
181
|
-
export function directServeRuntime(
|
|
182
|
-
executable: string,
|
|
183
|
-
entry: string | undefined,
|
|
184
|
-
entryExists = entry !== undefined && existsSync(entry),
|
|
185
|
-
): { file: string; args: string[] } {
|
|
186
|
-
return {
|
|
187
|
-
file: executable,
|
|
188
|
-
args: entry && !entry.startsWith('/$bunfs/') && entryExists ? [entry] : [],
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export function viewServeInvocation(
|
|
193
|
-
runtime: { file: string; args: string[] },
|
|
194
|
-
config: string,
|
|
195
|
-
): { file: string; args: string[] } {
|
|
196
|
-
return { file: runtime.file, args: [...runtime.args, '__view-serve', '--config', config] }
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
async function findOnPath(name: string): Promise<string | null> {
|
|
200
|
-
const lookup =
|
|
201
|
-
process.platform === 'win32' ? run('where', [name]) : run('sh', ['-c', `command -v ${name}`])
|
|
202
|
-
const res = await lookup.catch(() => null)
|
|
203
|
-
if (!res || res.code !== 0) return null
|
|
204
|
-
return res.stdout.split(/\r?\n/)[0]?.trim() || null
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/** Dev checkout: (re)build the node-runnable CLI bundle when missing or stale. */
|
|
208
|
-
async function ensureDevDist(entry: string, dist: string): Promise<void> {
|
|
209
|
-
if (!(await devDistIsStale(entry, dist))) return
|
|
210
|
-
const projectDir = join(dirname(entry), '..')
|
|
211
|
-
const buildScript = join(projectDir, 'scripts', 'build.ts')
|
|
212
|
-
const bun = await findOnPath('bun')
|
|
213
|
-
if (!bun || !existsSync(buildScript)) return
|
|
214
|
-
|
|
215
|
-
// Build output goes to stderr so --json stdout remains valid.
|
|
216
|
-
console.error('(dev) dist/astrale.js is stale — running the official CLI build…')
|
|
217
|
-
const built = await run(bun, [buildScript], { cwd: projectDir })
|
|
218
|
-
if (built.stdout) process.stderr.write(built.stdout)
|
|
219
|
-
if (built.stderr) process.stderr.write(built.stderr)
|
|
220
|
-
if (built.code !== 0) throw new Error(`Official CLI build failed with exit code ${built.code}.`)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export async function devDistIsStale(entry: string, dist: string): Promise<boolean> {
|
|
224
|
-
if (!existsSync(dist)) return true
|
|
225
|
-
const projectDir = join(dirname(entry), '..')
|
|
226
|
-
const builtAt = statSync(dist).mtimeMs
|
|
227
|
-
const directories = [join(projectDir, 'src'), join(projectDir, 'bin'), join(projectDir, 'vendor')]
|
|
228
|
-
const files = [
|
|
229
|
-
join(projectDir, 'scripts', 'build.ts'),
|
|
230
|
-
join(projectDir, 'package.json'),
|
|
231
|
-
join(projectDir, 'pnpm-lock.yaml'),
|
|
232
|
-
]
|
|
233
|
-
for (const directory of directories) {
|
|
234
|
-
if (existsSync(directory) && (await newerThan(directory, builtAt))) return true
|
|
235
|
-
}
|
|
236
|
-
return files.some((file) => existsSync(file) && statSync(file).mtimeMs > builtAt)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
async function newerThan(dir: string, mtimeMs: number): Promise<boolean> {
|
|
240
|
-
const entries = await readdir(dir, { withFileTypes: true, recursive: true })
|
|
241
|
-
for (const item of entries) {
|
|
242
|
-
if (!item.isFile()) continue
|
|
243
|
-
if (statSync(join(item.parentPath, item.name)).mtimeMs > mtimeMs) return true
|
|
244
|
-
}
|
|
245
|
-
return false
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/** Spawn the detached session server (the CLI re-invoking itself) and wait for it. */
|
|
249
|
-
async function startSession(view: ResolvedView, opts: ViewOpts): Promise<ViewSessionRecord> {
|
|
250
|
-
await ensureViewerAssets()
|
|
251
|
-
const kernelTarget = await withClientSession(
|
|
252
|
-
opts,
|
|
253
|
-
async ({ target: connectionTarget }) => connectionTarget,
|
|
254
|
-
)
|
|
255
|
-
const [instances, identities, runtime] = await Promise.all([
|
|
256
|
-
readInstances(),
|
|
257
|
-
readIdentities(),
|
|
258
|
-
resolveServeRuntime(),
|
|
259
|
-
])
|
|
260
|
-
return withViewPortAllocationLock(() =>
|
|
261
|
-
startSessionLocked(view, opts, kernelTarget, instances.active, identities.default, runtime),
|
|
262
|
-
)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export function createViewServeConfig(
|
|
266
|
-
record: ViewSessionRecord,
|
|
267
|
-
opts: Pick<ViewOpts, 'allowExternalOrigin' | 'as' | 'creds' | 'instance' | 'timeout' | 'url'>,
|
|
268
|
-
kernelTarget: { url: string; kernelIssuer: string; caFile?: string },
|
|
269
|
-
): ViewServeConfig {
|
|
270
|
-
return {
|
|
271
|
-
session: record,
|
|
272
|
-
kernel: {
|
|
273
|
-
url: opts.url,
|
|
274
|
-
instance: opts.instance,
|
|
275
|
-
as: opts.as,
|
|
276
|
-
creds: opts.creds,
|
|
277
|
-
timeout: opts.timeout,
|
|
278
|
-
},
|
|
279
|
-
proxy: {
|
|
280
|
-
kernelUrl: kernelTarget.url,
|
|
281
|
-
issuer: kernelTarget.kernelIssuer,
|
|
282
|
-
caFile: kernelTarget.caFile,
|
|
283
|
-
direct: isPublicHttps(kernelTarget.url) && !kernelTarget.caFile,
|
|
284
|
-
},
|
|
285
|
-
externalOrigins: admitExternalOpenOrigins(opts.allowExternalOrigin),
|
|
286
|
-
idleMs: IDLE_MS,
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Called under the cross-process port-allocation lock. Keep the lock until the
|
|
292
|
-
* detached child answers its readiness probe: only then is the selected port
|
|
293
|
-
* durably owned and safe for the next CLI process to scan.
|
|
294
|
-
*/
|
|
295
|
-
async function startSessionLocked(
|
|
296
|
-
view: ResolvedView,
|
|
297
|
-
opts: ViewOpts,
|
|
298
|
-
kernelTarget: { url: string; kernelIssuer: string; caFile?: string },
|
|
299
|
-
activeInstance: string | undefined,
|
|
300
|
-
defaultIdentity: string | undefined,
|
|
301
|
-
runtime: { file: string; args: string[] },
|
|
302
|
-
): Promise<ViewSessionRecord> {
|
|
303
|
-
const port = await findFreePort(VIEW_PORT_BASE, VIEW_PORT_SPAN)
|
|
304
|
-
if (port === null) {
|
|
305
|
-
throw new Error(
|
|
306
|
-
`No free port in ${VIEW_PORT_BASE}-${VIEW_PORT_BASE + VIEW_PORT_SPAN - 1} — close sessions with \`astrale view --close --all\``,
|
|
307
|
-
)
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const id = `v-${randomBytes(3).toString('hex')}`
|
|
311
|
-
const nonce = randomBytes(12).toString('hex')
|
|
312
|
-
const record: ViewSessionRecord = {
|
|
313
|
-
id,
|
|
314
|
-
pid: 0,
|
|
315
|
-
port,
|
|
316
|
-
nonce,
|
|
317
|
-
pageUrl: `http://127.0.0.1:${port}/s/${nonce}/`,
|
|
318
|
-
view,
|
|
319
|
-
instance: opts.instance ?? (opts.url ? opts.url : activeInstance),
|
|
320
|
-
identity: opts.creds ? '(pre-signed creds)' : (opts.as ?? defaultIdentity),
|
|
321
|
-
createdAt: new Date().toISOString(),
|
|
322
|
-
}
|
|
323
|
-
const serveConfig = createViewServeConfig(record, opts, kernelTarget)
|
|
324
|
-
|
|
325
|
-
await saveServeConfig(serveConfig)
|
|
326
|
-
const logFd = await openSessionLog(id)
|
|
327
|
-
const invocation = viewServeInvocation(runtime, configPath(id))
|
|
328
|
-
const child = spawnHandle(invocation.file, invocation.args, {
|
|
329
|
-
detached: true,
|
|
330
|
-
stdio: ['ignore', logFd, logFd],
|
|
331
|
-
})
|
|
332
|
-
child.unref()
|
|
333
|
-
closeSync(logFd)
|
|
334
|
-
if (!child.pid) throw new Error('Failed to spawn the view session server')
|
|
335
|
-
const live = { ...record, pid: child.pid }
|
|
336
|
-
await saveRecord(live)
|
|
337
|
-
|
|
338
|
-
const deadline = Date.now() + READY_TIMEOUT_MS
|
|
339
|
-
while (Date.now() < deadline) {
|
|
340
|
-
try {
|
|
341
|
-
const res = await fetch(`${live.pageUrl}state`)
|
|
342
|
-
if (res.ok) return live
|
|
343
|
-
} catch {
|
|
344
|
-
// not up yet
|
|
345
|
-
}
|
|
346
|
-
if (child.exitCode !== null) break
|
|
347
|
-
await sleep(POLL_MS)
|
|
348
|
-
}
|
|
349
|
-
const tail = await readFile(logPath(id), 'utf8').catch(() => '')
|
|
350
|
-
await closeSession(live)
|
|
351
|
-
throw new Error(
|
|
352
|
-
`View session server did not come up.${tail ? `\n--- server log ---\n${tail.slice(-2000)}` : ''}`,
|
|
353
|
-
)
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
type PageState = { state: string; error?: string }
|
|
357
|
-
|
|
358
|
-
async function waitForPageState(record: ViewSessionRecord): Promise<PageState> {
|
|
359
|
-
const deadline = Date.now() + STATE_TIMEOUT_MS
|
|
360
|
-
let last: PageState = { state: 'waiting' }
|
|
361
|
-
while (Date.now() < deadline) {
|
|
362
|
-
try {
|
|
363
|
-
last = (await (await fetch(`${record.pageUrl}state`)).json()) as PageState
|
|
364
|
-
if (last.state === 'connected' || last.state === 'plain' || last.state === 'failed') {
|
|
365
|
-
return last
|
|
366
|
-
}
|
|
367
|
-
} catch {
|
|
368
|
-
// transient
|
|
369
|
-
}
|
|
370
|
-
await sleep(POLL_MS)
|
|
371
|
-
}
|
|
372
|
-
return last
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Public https kernels are dialed directly by the view iframe (the router
|
|
377
|
-
* serves CORS; Chrome's local-network-access rules forbid a public view
|
|
378
|
-
* origin fetching our loopback proxy). Local/self-signed kernels go through
|
|
379
|
-
* the proxy.
|
|
380
|
-
*/
|
|
381
|
-
function isPublicHttps(url: string): boolean {
|
|
382
|
-
try {
|
|
383
|
-
const parsed = new URL(url)
|
|
384
|
-
return (
|
|
385
|
-
parsed.protocol === 'https:' &&
|
|
386
|
-
!['localhost', '127.0.0.1', '::1', 'host.docker.internal'].includes(parsed.hostname)
|
|
387
|
-
)
|
|
388
|
-
} catch {
|
|
389
|
-
return false
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function openSystemBrowser(url: string): void {
|
|
394
|
-
const argv =
|
|
395
|
-
process.platform === 'darwin'
|
|
396
|
-
? ['open', url]
|
|
397
|
-
: process.platform === 'win32'
|
|
398
|
-
? ['cmd', '/c', 'start', '', url]
|
|
399
|
-
: ['xdg-open', url]
|
|
400
|
-
void run(argv[0], argv.slice(1)).catch(() => {})
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
function driveHint(headed: boolean): string {
|
|
404
|
-
return `agent-browser --profile ${VIEW_PROFILE}${headed ? ' --headed' : ''}`
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
function describeState(state: PageState): string {
|
|
408
|
-
switch (state.state) {
|
|
409
|
-
case 'connected':
|
|
410
|
-
return 'connected (shell handshake, kernel calls live)'
|
|
411
|
-
case 'plain':
|
|
412
|
-
return 'mounted (plain iframe, no shell handshake)'
|
|
413
|
-
case 'failed':
|
|
414
|
-
return `failed — ${state.error ?? 'unknown error'}`
|
|
415
|
-
case 'mounting':
|
|
416
|
-
return 'still mounting (check again with a snapshot)'
|
|
417
|
-
default:
|
|
418
|
-
return 'page not loaded yet'
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
async function reportOpened(
|
|
423
|
-
record: ViewSessionRecord,
|
|
424
|
-
state: PageState | null,
|
|
425
|
-
mode: 'agent' | 'system' | 'none',
|
|
426
|
-
opts: ViewOpts,
|
|
427
|
-
): Promise<void> {
|
|
428
|
-
if (isMachine(opts)) {
|
|
429
|
-
output(
|
|
430
|
-
{
|
|
431
|
-
session: record,
|
|
432
|
-
state: state?.state ?? 'unopened',
|
|
433
|
-
error: state?.error,
|
|
434
|
-
driver: mode === 'agent' ? { profile: VIEW_PROFILE, headed: !!opts.headed } : mode,
|
|
435
|
-
},
|
|
436
|
-
opts,
|
|
437
|
-
)
|
|
438
|
-
return
|
|
439
|
-
}
|
|
440
|
-
const label = `/:${record.view.route.key}`
|
|
441
|
-
log.success(`View session ${chalk.bold(record.id)} — ${chalk.bold(label)}`)
|
|
442
|
-
log.dim(` target ${record.view.target}`)
|
|
443
|
-
log.dim(
|
|
444
|
-
` identity ${record.identity ?? '(default)'} instance ${record.instance ?? '(active)'}`,
|
|
445
|
-
)
|
|
446
|
-
log.dim(` page ${record.pageUrl}`)
|
|
447
|
-
if (state) log.dim(` state ${describeState(state)}`)
|
|
448
|
-
console.log('')
|
|
449
|
-
if (mode === 'agent') {
|
|
450
|
-
console.log(chalk.bold('Drive it:'))
|
|
451
|
-
console.log(` ${driveHint(!!opts.headed)} snapshot`)
|
|
452
|
-
console.log(` ${driveHint(!!opts.headed)} click @e3`)
|
|
453
|
-
} else if (mode === 'none') {
|
|
454
|
-
console.log(chalk.bold('Open it:'))
|
|
455
|
-
console.log(` ${record.pageUrl}`)
|
|
456
|
-
}
|
|
457
|
-
console.log(`${chalk.bold('Close:')} astrale view --close ${record.id}`)
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
export async function runSnapshotExtras(
|
|
461
|
-
opts: Pick<ViewOpts, 'headed' | 'screenshot' | 'snapshot'>,
|
|
462
|
-
): Promise<void> {
|
|
463
|
-
const target = { profile: VIEW_PROFILE, headed: !!opts.headed }
|
|
464
|
-
const settled =
|
|
465
|
-
opts.screenshot || opts.snapshot
|
|
466
|
-
? await waitForSettledSnapshot(() => ab(['snapshot'], target))
|
|
467
|
-
: null
|
|
468
|
-
|
|
469
|
-
if (opts.screenshot) {
|
|
470
|
-
const shot = await ab(['screenshot', opts.screenshot], target)
|
|
471
|
-
if (!shot.ok) log.warn(`screenshot failed: ${shot.error ?? 'unknown error'}`)
|
|
472
|
-
else log.dim(` screenshot → ${opts.screenshot}`)
|
|
473
|
-
}
|
|
474
|
-
if (opts.snapshot && settled) {
|
|
475
|
-
if (!settled.ok) {
|
|
476
|
-
log.warn(`snapshot failed: ${settled.error ?? 'unknown error'}`)
|
|
477
|
-
return
|
|
478
|
-
}
|
|
479
|
-
console.log(snapshotText(settled) ?? JSON.stringify(settled.data, null, 2))
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
async function closeCommand(opts: ViewOpts): Promise<void> {
|
|
484
|
-
const sessions = await listSessions()
|
|
485
|
-
let targets: ViewSessionRecord[]
|
|
486
|
-
if (opts.all) targets = sessions
|
|
487
|
-
else if (typeof opts.close === 'string') {
|
|
488
|
-
const match = sessions.find((s) => s.id === opts.close)
|
|
489
|
-
if (!match)
|
|
490
|
-
return fatal(new Error(`No view session "${opts.close}" — see \`astrale view --sessions\``))
|
|
491
|
-
targets = [match]
|
|
492
|
-
} else if (sessions.length <= 1) targets = sessions
|
|
493
|
-
else {
|
|
494
|
-
return fatal(
|
|
495
|
-
new Error(
|
|
496
|
-
`${sessions.length} sessions open — pass --close <id> or --close --all:\n${sessions
|
|
497
|
-
.map((s) => ` ${s.id} /:${s.view.route.key}`)
|
|
498
|
-
.join('\n')}`,
|
|
499
|
-
),
|
|
500
|
-
)
|
|
501
|
-
}
|
|
502
|
-
for (const session of targets) await closeSession(session)
|
|
503
|
-
if (isMachine(opts)) output({ closed: targets.map((s) => s.id) }, opts)
|
|
504
|
-
else if (targets.length === 0) log.dim('No view sessions.')
|
|
505
|
-
else log.success(`Closed ${targets.map((s) => s.id).join(', ')}`)
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
async function sessionsCommand(opts: ViewOpts): Promise<void> {
|
|
509
|
-
const sessions = await listSessions()
|
|
510
|
-
if (isMachine(opts)) {
|
|
511
|
-
output(sessions, opts)
|
|
512
|
-
return
|
|
513
|
-
}
|
|
514
|
-
if (sessions.length === 0) {
|
|
515
|
-
log.dim('No view sessions.')
|
|
516
|
-
return
|
|
517
|
-
}
|
|
518
|
-
for (const s of sessions) {
|
|
519
|
-
console.log(
|
|
520
|
-
`${chalk.bold(s.id)} /:${s.view.route.key} target ${s.view.target} ${chalk.dim(s.pageUrl)}`,
|
|
521
|
-
)
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
export default {
|
|
526
|
-
name: 'view',
|
|
527
|
-
description: 'Open one view in a local browser shell your agent can drive',
|
|
528
|
-
arguments: [
|
|
529
|
-
{
|
|
530
|
-
name: 'spec',
|
|
531
|
-
description: 'ViewPath (/:origin:view.slug) or target node (/path or @id)',
|
|
532
|
-
required: false,
|
|
533
|
-
},
|
|
534
|
-
],
|
|
535
|
-
options: [
|
|
536
|
-
{
|
|
537
|
-
flags: '--target <path>',
|
|
538
|
-
description:
|
|
539
|
-
'Target node to open the view on (optional — some views are standalone); @self works',
|
|
540
|
-
},
|
|
541
|
-
{ flags: '--view <slug>', description: 'Pick a view when the target resolves several' },
|
|
542
|
-
{ flags: '--list', description: 'Resolve and print the candidate views; do not open' },
|
|
543
|
-
{
|
|
544
|
-
flags: '--view-url <url>',
|
|
545
|
-
description: 'Override the view frontend URL (origin swaps origin; full URL replaces)',
|
|
546
|
-
},
|
|
547
|
-
{
|
|
548
|
-
flags: '--handshake <mode>',
|
|
549
|
-
description: 'Override the mount mode (needed with a bare --view-url)',
|
|
550
|
-
choices: ['shell', 'none'],
|
|
551
|
-
},
|
|
552
|
-
{ flags: '--headed', description: 'Visible agent-browser window' },
|
|
553
|
-
{ flags: '--browser', description: 'Open in the system default browser instead' },
|
|
554
|
-
{ flags: '--no-open', description: 'Start the session and print the URL only' },
|
|
555
|
-
{ flags: '--snapshot', description: 'Print an accessibility snapshot once the view is up' },
|
|
556
|
-
{ flags: '--screenshot <file>', description: 'Save a screenshot once the view is up' },
|
|
557
|
-
{ flags: '--sessions', description: 'List active view sessions' },
|
|
558
|
-
{
|
|
559
|
-
flags: '--close [id]',
|
|
560
|
-
description: 'Close a view session (bare: the only open one; with --all: every session)',
|
|
561
|
-
},
|
|
562
|
-
{ flags: '--all', description: 'With --close: close every session' },
|
|
563
|
-
{
|
|
564
|
-
flags: '--allow-external-origin <origin...>',
|
|
565
|
-
description: 'Grant this View exact HTTPS origins it may open in a new browser context',
|
|
566
|
-
},
|
|
567
|
-
],
|
|
568
|
-
afterHelpText: `
|
|
569
|
-
What it does:
|
|
570
|
-
Renders ONE view — no GUI, no cookies, no WorkOS. It resolves the view on the
|
|
571
|
-
kernel, starts a loopback session server that supplies the shell handshake (real
|
|
572
|
-
handshake via @astrale-os/shell, token minted from YOUR CLI identity, kernel
|
|
573
|
-
calls proxied), and opens the page headless in agent-browser. Driving stays
|
|
574
|
-
agent-browser's job; auth follows --as/--creds/-i like any kernel command.
|
|
575
|
-
|
|
576
|
-
A session stays up ~30 min idle (heartbeat while the page is open). The view
|
|
577
|
-
gets exactly what the GUI would hand it: one target-bound resolved placement,
|
|
578
|
-
an audience-bound credential for shell mounts, and the kernel endpoint.
|
|
579
|
-
|
|
580
|
-
Examples:
|
|
581
|
-
$ astrale view @customer
|
|
582
|
-
$ astrale view /:crm.example.dev:view.dashboard
|
|
583
|
-
$ astrale view /:agents.astrale.ai:view.agent --target @f00d1234 --as alice
|
|
584
|
-
$ astrale view @customer --snapshot
|
|
585
|
-
$ astrale view /:integrations.astrale.ai:view.application --allow-external-origin https://connect.nango.dev https://connect.composio.dev
|
|
586
|
-
$ astrale view --list
|
|
587
|
-
$ astrale view --sessions ; astrale view --close --all
|
|
588
|
-
`,
|
|
589
|
-
action: async (spec: string | undefined, opts: ViewOpts) => {
|
|
590
|
-
if (opts.close !== undefined) return closeCommand(opts)
|
|
591
|
-
if (opts.sessions) return sessionsCommand(opts)
|
|
592
|
-
if (opts.list && !spec) return sessionsCommand(opts)
|
|
593
|
-
|
|
594
|
-
rejectUnrepresentableOverrides(opts)
|
|
595
|
-
if (!spec) return fatal(new Error('Nothing to open — pass a ViewPath or target node.'))
|
|
596
|
-
const wantsAgentBrowser = !opts.browser && opts.open !== false
|
|
597
|
-
if ((opts.snapshot || opts.screenshot) && !wantsAgentBrowser) {
|
|
598
|
-
return fatal(
|
|
599
|
-
new Error('--snapshot/--screenshot need the agent-browser page (drop --browser/--no-open)'),
|
|
600
|
-
)
|
|
601
|
-
}
|
|
602
|
-
if (wantsAgentBrowser && !(await findAgentBrowser())) {
|
|
603
|
-
log.error('agent-browser is not installed — it is the engine `astrale view` drives.')
|
|
604
|
-
log.dim(' npm install -g agent-browser && agent-browser install')
|
|
605
|
-
log.dim(` npx skills add ${AGENT_BROWSER_REPO}`)
|
|
606
|
-
log.dim(' (or use --browser / --no-open)')
|
|
607
|
-
process.exit(1)
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
const { view, candidates } = await resolveSession(spec, opts)
|
|
611
|
-
if (opts.list) {
|
|
612
|
-
if (isMachine(opts)) output(candidates, opts)
|
|
613
|
-
else if (candidates.length === 0) log.dim('No views resolve here.')
|
|
614
|
-
else {
|
|
615
|
-
for (const c of candidates) {
|
|
616
|
-
console.log(
|
|
617
|
-
`${chalk.bold(candidateSlug(c))} ${c.handshake} ${c.origin} ${chalk.dim(c.url)} ${c.path}`,
|
|
618
|
-
)
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
return
|
|
622
|
-
}
|
|
623
|
-
if (!view) throw new Error('View resolution completed without a selected view')
|
|
624
|
-
|
|
625
|
-
const record = await startSession(view, opts)
|
|
626
|
-
|
|
627
|
-
let mode: 'agent' | 'system' | 'none' = 'none'
|
|
628
|
-
if (wantsAgentBrowser) {
|
|
629
|
-
mode = 'agent'
|
|
630
|
-
const opened = await ab(['open', record.pageUrl], {
|
|
631
|
-
profile: VIEW_PROFILE,
|
|
632
|
-
headed: !!opts.headed,
|
|
633
|
-
})
|
|
634
|
-
if (!opened.ok) {
|
|
635
|
-
await closeSession(record)
|
|
636
|
-
return fatal(
|
|
637
|
-
new Error(`agent-browser could not open the page: ${opened.error ?? 'unknown error'}`),
|
|
638
|
-
)
|
|
639
|
-
}
|
|
640
|
-
} else if (opts.browser) {
|
|
641
|
-
mode = 'system'
|
|
642
|
-
openSystemBrowser(record.pageUrl)
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
const state = mode === 'none' ? null : await waitForPageState(record)
|
|
646
|
-
if (state?.state === 'failed') {
|
|
647
|
-
await reportOpened(record, state, mode, opts)
|
|
648
|
-
if (opts.debug) {
|
|
649
|
-
const tail = await readFile(logPath(record.id), 'utf8').catch(() => '')
|
|
650
|
-
if (tail) console.error(`--- server log ---\n${tail.slice(-3000)}`)
|
|
651
|
-
}
|
|
652
|
-
await closeSession(record)
|
|
653
|
-
process.exit(1)
|
|
654
|
-
}
|
|
655
|
-
await reportOpened(record, state, mode, opts)
|
|
656
|
-
if (mode === 'agent') await runSnapshotExtras(opts)
|
|
657
|
-
},
|
|
658
|
-
} satisfies CommandDefinition
|
package/src/connect-core.test.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
|
|
3
|
-
import * as compatibility from './connect-core'
|
|
4
|
-
|
|
5
|
-
describe('connect-core compatibility barrel', () => {
|
|
6
|
-
test('retains the functions borrowed by connect-host', () => {
|
|
7
|
-
for (const value of [
|
|
8
|
-
compatibility.readIdentities,
|
|
9
|
-
compatibility.getDefault,
|
|
10
|
-
compatibility.getIdentity,
|
|
11
|
-
compatibility.signAs,
|
|
12
|
-
compatibility.listIdentityKeys,
|
|
13
|
-
compatibility.readInstances,
|
|
14
|
-
compatibility.resetInstancesMemo,
|
|
15
|
-
compatibility.resolveInstance,
|
|
16
|
-
compatibility.normalizeInstanceKernelUrl,
|
|
17
|
-
compatibility.orgIdForAudience,
|
|
18
|
-
compatibility.resolveInstanceTarget,
|
|
19
|
-
compatibility.readConfig,
|
|
20
|
-
compatibility.resolveCredential,
|
|
21
|
-
compatibility.fetchWithCaFile,
|
|
22
|
-
compatibility.createPaths,
|
|
23
|
-
compatibility.loginViaIdp,
|
|
24
|
-
compatibility.resolveIdpName,
|
|
25
|
-
compatibility.readIdpSession,
|
|
26
|
-
compatibility.isSessionExpired,
|
|
27
|
-
]) {
|
|
28
|
-
expect(value).toBeTypeOf('function')
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
test('retains typed errors and the captured paths singleton', () => {
|
|
33
|
-
expect(compatibility.AuthError).toBeTypeOf('function')
|
|
34
|
-
expect(compatibility.IdpRefreshTransientError).toBeTypeOf('function')
|
|
35
|
-
expect(compatibility.IdpOrgMembershipError).toBeTypeOf('function')
|
|
36
|
-
expect(compatibility.paths.home).toBeTypeOf('string')
|
|
37
|
-
})
|
|
38
|
-
})
|