@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
|
@@ -4,9 +4,20 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Domain Studio</title>
|
|
7
|
-
<script
|
|
7
|
+
<script>
|
|
8
|
+
// Paint the theme before the first frame — the app would otherwise flash light.
|
|
9
|
+
try {
|
|
10
|
+
var pref = localStorage.getItem('studio.theme') || 'system'
|
|
11
|
+
if (
|
|
12
|
+
pref === 'dark' ||
|
|
13
|
+
(pref === 'system' && matchMedia('(prefers-color-scheme: dark)').matches)
|
|
14
|
+
)
|
|
15
|
+
document.documentElement.classList.add('dark')
|
|
16
|
+
} catch {}
|
|
17
|
+
</script>
|
|
18
|
+
<script type="module" crossorigin src="/assets/index-C4kNlvwF.js"></script>
|
|
8
19
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Dd_uD5pT.js">
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
20
|
+
<link rel="stylesheet" crossorigin href="/assets/index-5C1uT6ML.css">
|
|
10
21
|
</head>
|
|
11
22
|
<body>
|
|
12
23
|
<div id="root"></div>
|
package/studio/package.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"typecheck": "tsgo --noEmit"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@astrale-os/sdk": "0.5.0-beta.
|
|
24
|
+
"@astrale-os/sdk": "0.5.0-beta.64",
|
|
25
25
|
"@astrale-os/shell": "0.4.2-beta.6",
|
|
26
26
|
"@dagrejs/dagre": "^3.0.0",
|
|
27
27
|
"@radix-ui/react-collapsible": "^1.1.0",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"@radix-ui/react-slot": "^1.1.2",
|
|
35
35
|
"@radix-ui/react-tooltip": "^1.1.8",
|
|
36
36
|
"@tanstack/react-query": "^5.66.0",
|
|
37
|
+
"@tisoap/react-flow-smart-edge": "^5.0.0",
|
|
37
38
|
"@xyflow/react": "^12.4.4",
|
|
38
39
|
"chokidar": "^4.0.3",
|
|
39
40
|
"class-variance-authority": "^0.7.1",
|
|
@@ -35,8 +35,14 @@ test('Ask uses the active harness model and matching per-harness conversation',
|
|
|
35
35
|
roots.push(root)
|
|
36
36
|
mkdirSync(join(root, 'schema'))
|
|
37
37
|
writeFileSync(join(root, 'astrale.config.ts'), 'export default {}\n')
|
|
38
|
-
writeFileSync(join(root, 'application.ts'), 'export default {}\n')
|
|
39
38
|
writeFileSync(join(root, 'schema/index.ts'), 'export const Test = {}\n')
|
|
39
|
+
writeFileSync(
|
|
40
|
+
join(root, 'application.ts'),
|
|
41
|
+
`import { defineApplication } from '@astrale-os/sdk/application'
|
|
42
|
+
import { Test } from './schema/index.js'
|
|
43
|
+
export default defineApplication({ schema: Test, runtime: {} as never })
|
|
44
|
+
`,
|
|
45
|
+
)
|
|
40
46
|
const handle = registerDomain(root)!
|
|
41
47
|
domainIds.push(handle.id)
|
|
42
48
|
|
|
@@ -6,6 +6,7 @@ import type { StudioEvent } from '../../../shared/types'
|
|
|
6
6
|
import type { DomainHandle } from '../../domain'
|
|
7
7
|
import type { HarnessMcpServer } from '../harness/adapter'
|
|
8
8
|
|
|
9
|
+
import { studioCliCommand } from '../../cli'
|
|
9
10
|
import { removeState, statePath, writeJson } from '../../state/store'
|
|
10
11
|
import { openBridgeSession } from './routes'
|
|
11
12
|
|
|
@@ -32,7 +33,6 @@ export function setBridgePort(port: number): void {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
const MCP_SERVER = join(import.meta.dir, 'stdio.ts')
|
|
35
|
-
const BIN = process.env.DOMAIN_STUDIO_BRIDGE_BUN || process.execPath
|
|
36
36
|
|
|
37
37
|
/** Mint the run-scoped MCP grant and its secret-bearing configuration file. */
|
|
38
38
|
export function startBridge(handle: DomainHandle, notify: (event: StudioEvent) => void): Bridge {
|
|
@@ -45,10 +45,22 @@ export function startBridge(handle: DomainHandle, notify: (event: StudioEvent) =
|
|
|
45
45
|
const bridgeConfigPath = statePath(handle.root, bridgeRel)
|
|
46
46
|
chmodSync(bridgeConfigPath, 0o600)
|
|
47
47
|
|
|
48
|
+
let bridgeCommand: string[]
|
|
49
|
+
try {
|
|
50
|
+
bridgeCommand = studioCliCommand(['__studio-bridge', '--config', bridgeConfigPath])
|
|
51
|
+
} catch {
|
|
52
|
+
bridgeCommand = [
|
|
53
|
+
process.env.DOMAIN_STUDIO_BRIDGE_BUN || process.execPath,
|
|
54
|
+
MCP_SERVER,
|
|
55
|
+
'--config',
|
|
56
|
+
bridgeConfigPath,
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
|
|
48
60
|
const server: HarnessMcpServer = {
|
|
49
61
|
name: 'domain-studio',
|
|
50
|
-
command:
|
|
51
|
-
args:
|
|
62
|
+
command: bridgeCommand[0],
|
|
63
|
+
args: bridgeCommand.slice(1),
|
|
52
64
|
required: true,
|
|
53
65
|
approvalMode: 'approve',
|
|
54
66
|
enabledTools: Object.keys(TOOL_ROUTES),
|
|
@@ -8,5 +8,10 @@ test('embedded agents receive the current SDK and frontend skill contracts', ()
|
|
|
8
8
|
expect(prompt).toContain('modular Actions and Workflows')
|
|
9
9
|
expect(prompt).toContain('astrale-frontend-design')
|
|
10
10
|
expect(prompt).toContain('Runtime/Application entries')
|
|
11
|
+
expect(prompt).toContain('"@$ASTRALE_ISSUES_PROJECT_ID::createIssue"')
|
|
12
|
+
expect(prompt).toContain('astrale --ci --no-prompt call')
|
|
13
|
+
expect(prompt).toContain('Issue Node ID')
|
|
14
|
+
expect(prompt).not.toContain('class.Issue:create')
|
|
15
|
+
expect(prompt).not.toContain('initialTags')
|
|
11
16
|
expect(prompt).not.toContain('schema/ runtime/ views/')
|
|
12
17
|
})
|
|
@@ -54,11 +54,26 @@ export function buildSystemPrompt(options: { bridge: boolean }): string {
|
|
|
54
54
|
'working in this domain costs YOU friction — a confusing or missing Astrale API/error, stale',
|
|
55
55
|
'docs or skills, or a bug that forced a workaround — FILE a DX issue as part of the task, per',
|
|
56
56
|
'the **astrale-cli** skill’s “Reporting DX Issues” section: dedup against open issues first, then',
|
|
57
|
-
'`astrale call
|
|
58
|
-
'
|
|
59
|
-
'
|
|
57
|
+
'use `astrale --ci --no-prompt call "@$ASTRALE_ISSUES_PROJECT_ID::createIssue" -i admin',
|
|
58
|
+
' --data \'{"title":"<one line>","description":"<evidence>","priority":2}\' --json`.',
|
|
59
|
+
'ASTRALE_ISSUES_PROJECT_ID must name the destination Project. It is best-effort and non-blocking',
|
|
60
|
+
'— never let filing one derail the user’s task — and mention the returned Issue Node ID and',
|
|
61
|
+
'reference in your reply when you file one.',
|
|
60
62
|
'',
|
|
61
|
-
'
|
|
63
|
+
'WHERE THE USER READS YOU — two channels, and they are not interchangeable:',
|
|
64
|
+
' 1. THE CONVERSATION. Your final message is shown as your reply in the studio chat. Always',
|
|
65
|
+
' end the turn with one, even when you also answered threads — a silent turn reads as a',
|
|
66
|
+
' failure. Keep it to what changed and what it means.',
|
|
67
|
+
' 2. THE THREADS. Comments pinned on the schema are answered IN PLACE, on the thread.',
|
|
68
|
+
'When open threads came with this turn, answer them in the threads AND say so in your final',
|
|
69
|
+
'message (e.g. "answered 2 comments on Invoice and Quote") so the reader knows where to look.',
|
|
70
|
+
'With no open thread, the conversation is the only channel — just answer there.',
|
|
71
|
+
'',
|
|
72
|
+
'CONTEXT DOCUMENTS — whatever the user attached lives under `.domain-studio/context/docs/`',
|
|
73
|
+
'and is listed with its path in each turn. Nothing is pasted into the prompt: open the ones',
|
|
74
|
+
'that matter, ignore the rest.',
|
|
75
|
+
'',
|
|
76
|
+
'REPLY PROTOCOL — how a thread answer must be shaped:',
|
|
62
77
|
'- For EVERY open thread, append exactly one concise {role:"author"} reply: what you changed,',
|
|
63
78
|
' your answer (with `options` when it is a decision), or a clarifying question.',
|
|
64
79
|
'- If you fully addressed a thread, also set "status":"closed" and add a short "closeNote".',
|
|
@@ -3,11 +3,13 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
|
3
3
|
import { tmpdir } from 'node:os'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
|
|
6
|
+
import type { AgentRun } from '../../shared/types'
|
|
6
7
|
import type { AskResult } from './harness/adapter'
|
|
7
8
|
|
|
8
9
|
import { registerDomain, unregisterDomain } from '../domain'
|
|
9
10
|
import { getHarnessById } from './harness/registry'
|
|
10
11
|
import { handleAgentRoute } from './routes'
|
|
12
|
+
import { persistRun } from './run/transcript'
|
|
11
13
|
import { NdjsonChannel } from './stream'
|
|
12
14
|
|
|
13
15
|
const roots: string[] = []
|
|
@@ -26,8 +28,14 @@ function fixture() {
|
|
|
26
28
|
roots.push(root)
|
|
27
29
|
mkdirSync(join(root, 'schema'))
|
|
28
30
|
writeFileSync(join(root, 'astrale.config.ts'), 'export default {}\n')
|
|
29
|
-
writeFileSync(join(root, 'application.ts'), 'export default {}\n')
|
|
30
31
|
writeFileSync(join(root, 'schema/index.ts'), 'export const Test = {}\n')
|
|
32
|
+
writeFileSync(
|
|
33
|
+
join(root, 'application.ts'),
|
|
34
|
+
`import { defineApplication } from '@astrale-os/sdk/application'
|
|
35
|
+
import { Test } from './schema/index.js'
|
|
36
|
+
export default defineApplication({ schema: Test, runtime: {} as never })
|
|
37
|
+
`,
|
|
38
|
+
)
|
|
31
39
|
const handle = registerDomain(root)!
|
|
32
40
|
domainIds.push(handle.id)
|
|
33
41
|
return handle
|
|
@@ -67,6 +75,36 @@ test('owns harness status and prompt routes behind one agent boundary', async ()
|
|
|
67
75
|
})
|
|
68
76
|
})
|
|
69
77
|
|
|
78
|
+
test('serves the bounded persisted conversation history', async () => {
|
|
79
|
+
const handle = fixture()
|
|
80
|
+
const saved = (id: string, createdAt: string): AgentRun => ({
|
|
81
|
+
id,
|
|
82
|
+
domainId: handle.id,
|
|
83
|
+
harness: 'codex',
|
|
84
|
+
status: 'succeeded',
|
|
85
|
+
createdAt,
|
|
86
|
+
summary: id,
|
|
87
|
+
instruction: `Do ${id}`,
|
|
88
|
+
targetCommentIds: [],
|
|
89
|
+
events: [],
|
|
90
|
+
})
|
|
91
|
+
persistRun(handle.root, saved('older', '2026-08-20T01:00:00.000Z'), true)
|
|
92
|
+
persistRun(handle.root, saved('newer', '2026-08-20T02:00:00.000Z'), true)
|
|
93
|
+
|
|
94
|
+
const url = new URL(`http://127.0.0.1/api/domain/${handle.id}/agent/history?limit=1`)
|
|
95
|
+
const response = await handleAgentRoute({
|
|
96
|
+
req: new Request(url),
|
|
97
|
+
url,
|
|
98
|
+
rest: '/agent/history',
|
|
99
|
+
body: {},
|
|
100
|
+
handle,
|
|
101
|
+
notify: () => {},
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
expect(response?.status).toBe(200)
|
|
105
|
+
expect(await response?.json()).toEqual([expect.objectContaining({ id: 'newer' })])
|
|
106
|
+
})
|
|
107
|
+
|
|
70
108
|
test('ignores non-agent routes and rejects unknown agent routes', async () => {
|
|
71
109
|
process.env.DOMAIN_STUDIO_HARNESS = 'mock'
|
|
72
110
|
expect(await route('/context')).toBeNull()
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
setSessionId,
|
|
29
29
|
submitRun,
|
|
30
30
|
} from './run/coordinator'
|
|
31
|
+
import { readRunHistory } from './run/transcript'
|
|
31
32
|
import { readUsage } from './run/usage'
|
|
32
33
|
import { NdjsonChannel } from './stream'
|
|
33
34
|
|
|
@@ -88,6 +89,8 @@ export async function handleAgentRoute(input: AgentRouteInput): Promise<Response
|
|
|
88
89
|
})
|
|
89
90
|
return result.error ? json({ error: result.error }) : json(result.run)
|
|
90
91
|
}
|
|
92
|
+
if (rest === '/agent/history' && req.method === 'GET')
|
|
93
|
+
return json(readRunHistory(id, root, Number(url.searchParams.get('limit')) || undefined))
|
|
91
94
|
if (rest === '/agent/cancel' && req.method === 'POST') return json({ ok: cancelRun(id) })
|
|
92
95
|
if (rest === '/agent/reset' && req.method === 'POST') return json({ ok: resetConversation(id) })
|
|
93
96
|
if (rest === '/agent/session') {
|
|
@@ -58,8 +58,14 @@ function fixture(): DomainHandle {
|
|
|
58
58
|
roots.push(root)
|
|
59
59
|
mkdirSync(join(root, 'schema'))
|
|
60
60
|
writeFileSync(join(root, 'astrale.config.ts'), 'export default {}\n')
|
|
61
|
-
writeFileSync(join(root, 'application.ts'), 'export default {}\n')
|
|
62
61
|
writeFileSync(join(root, 'schema/index.ts'), 'export const Test = {}\n')
|
|
62
|
+
writeFileSync(
|
|
63
|
+
join(root, 'application.ts'),
|
|
64
|
+
`import { defineApplication } from '@astrale-os/sdk/application'
|
|
65
|
+
import { Test } from './schema/index.js'
|
|
66
|
+
export default defineApplication({ schema: Test, runtime: {} as never })
|
|
67
|
+
`,
|
|
68
|
+
)
|
|
63
69
|
const handle = registerDomain(root)!
|
|
64
70
|
domainIds.push(handle.id)
|
|
65
71
|
return handle
|
|
@@ -136,6 +136,7 @@ export async function prepareRun(
|
|
|
136
136
|
: awaiting.length === 1
|
|
137
137
|
? '1 open thread'
|
|
138
138
|
: `${awaiting.length} open threads`,
|
|
139
|
+
...(message ? { instruction: message } : {}),
|
|
139
140
|
targetCommentIds: awaiting.map((comment) => comment.id),
|
|
140
141
|
events: [],
|
|
141
142
|
sessionId: resume,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, expect, test } from 'bun:test'
|
|
2
|
-
import { mkdtempSync, rmSync } from 'node:fs'
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync } from 'node:fs'
|
|
3
3
|
import { tmpdir } from 'node:os'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
|
|
@@ -8,7 +8,7 @@ import type { AgentRun } from '../../../shared/types'
|
|
|
8
8
|
import { asJsonRecord } from '../../json'
|
|
9
9
|
import { readJson, writeJson } from '../../state/store'
|
|
10
10
|
import { currentRun, hydrateRun } from './live-state'
|
|
11
|
-
import { readLastRun } from './transcript'
|
|
11
|
+
import { persistRun, readLastRun, readRunHistory } from './transcript'
|
|
12
12
|
|
|
13
13
|
const roots: string[] = []
|
|
14
14
|
|
|
@@ -45,6 +45,51 @@ test('reconciles a run orphaned by restart and hydrates its terminal snapshot',
|
|
|
45
45
|
status: 'interrupted',
|
|
46
46
|
sessionId: 'thread-1',
|
|
47
47
|
})
|
|
48
|
+
expect(readRunHistory(domainId, root)).toEqual([
|
|
49
|
+
expect.objectContaining({ id: running.id, status: 'interrupted' }),
|
|
50
|
+
])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('reads bounded terminal history oldest first and skips unrelated or unreadable entries', () => {
|
|
54
|
+
const root = mkdtempSync(join(tmpdir(), 'studio-agent-history-'))
|
|
55
|
+
roots.push(root)
|
|
56
|
+
const domainId = `domain-${crypto.randomUUID()}`
|
|
57
|
+
const run = (id: string, createdAt: string, owner = domainId): AgentRun => ({
|
|
58
|
+
id,
|
|
59
|
+
domainId: owner,
|
|
60
|
+
harness: 'codex',
|
|
61
|
+
status: 'succeeded',
|
|
62
|
+
createdAt,
|
|
63
|
+
summary: id,
|
|
64
|
+
instruction: `Do ${id}`,
|
|
65
|
+
targetCommentIds: [],
|
|
66
|
+
events: [],
|
|
67
|
+
prompt: {
|
|
68
|
+
createdAt,
|
|
69
|
+
systemPrompt: 'system',
|
|
70
|
+
turnPrompt: 'the whole handoff markdown',
|
|
71
|
+
firstTurn: true,
|
|
72
|
+
resumed: false,
|
|
73
|
+
mcpTools: [],
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
persistRun(root, run('newer', '2026-08-20T02:00:00.000Z'), true)
|
|
78
|
+
persistRun(root, run('older', '2026-08-20T01:00:00.000Z'), true)
|
|
79
|
+
persistRun(root, run('foreign', '2026-08-20T03:00:00.000Z', 'another-domain'), true)
|
|
80
|
+
mkdirSync(join(root, '.domain-studio/.cache/agent/runs/unreadable.json'))
|
|
81
|
+
|
|
82
|
+
expect(readRunHistory(domainId, root)).toEqual([
|
|
83
|
+
expect.objectContaining({ id: 'older', instruction: 'Do older' }),
|
|
84
|
+
expect.objectContaining({ id: 'newer', instruction: 'Do newer' }),
|
|
85
|
+
])
|
|
86
|
+
expect(readRunHistory(domainId, root, 1)).toEqual([expect.objectContaining({ id: 'newer' })])
|
|
87
|
+
// the frozen prompt is the biggest field of a turn and the chat never shows it
|
|
88
|
+
expect(
|
|
89
|
+
readJson(root, '.cache/agent/runs/newer.json', (v) => v as AgentRun, null)?.prompt,
|
|
90
|
+
).toBeDefined()
|
|
91
|
+
expect(readRunHistory(domainId, root).every((turn) => turn.prompt === undefined)).toBe(true)
|
|
92
|
+
expect(readRunHistory(domainId, root, -10)).toEqual([expect.objectContaining({ id: 'newer' })])
|
|
48
93
|
})
|
|
49
94
|
|
|
50
95
|
test('admits future run fields but rejects malformed persisted run structure', () => {
|
|
@@ -7,10 +7,11 @@ import {
|
|
|
7
7
|
type MergeResult,
|
|
8
8
|
} from '../../../shared/types'
|
|
9
9
|
import { asBoolean, asFiniteNumber, asJsonRecord, asString, asStringArray } from '../../json'
|
|
10
|
-
import { readJson, writeJson } from '../../state/store'
|
|
10
|
+
import { listState, readJson, writeJson } from '../../state/store'
|
|
11
11
|
|
|
12
12
|
const LAST_RUN_FILE = '.cache/agent/last-run.json'
|
|
13
|
-
const
|
|
13
|
+
const RUNS_DIR = '.cache/agent/runs'
|
|
14
|
+
const runFile = (id: string) => `${RUNS_DIR}/${id}.json`
|
|
14
15
|
|
|
15
16
|
const RUN_STATUSES = new Set<AgentRun['status']>([
|
|
16
17
|
'queued',
|
|
@@ -139,6 +140,7 @@ function decodeAgentRun(value: unknown): AgentRun | undefined {
|
|
|
139
140
|
const decoded = decodeAgentEvent(event)
|
|
140
141
|
return decoded ? [decoded] : []
|
|
141
142
|
})
|
|
143
|
+
const instruction = asString(record.instruction)
|
|
142
144
|
const finishedAt = asString(record.finishedAt)
|
|
143
145
|
const sessionId = asString(record.sessionId)
|
|
144
146
|
const resumed = asBoolean(record.resumed)
|
|
@@ -158,6 +160,7 @@ function decodeAgentRun(value: unknown): AgentRun | undefined {
|
|
|
158
160
|
summary,
|
|
159
161
|
targetCommentIds,
|
|
160
162
|
events,
|
|
163
|
+
...(instruction === undefined ? {} : { instruction }),
|
|
161
164
|
...(finishedAt === undefined ? {} : { finishedAt }),
|
|
162
165
|
...(sessionId === undefined ? {} : { sessionId }),
|
|
163
166
|
...(resumed === undefined ? {} : { resumed }),
|
|
@@ -190,7 +193,33 @@ export function readLastRun(domainId: string, root: string): AgentRun | null {
|
|
|
190
193
|
last.finishedAt = last.finishedAt ?? new Date().toISOString()
|
|
191
194
|
last.error =
|
|
192
195
|
'the studio restarted during this turn — your conversation is preserved; submit again to continue'
|
|
193
|
-
persistRun(root, last)
|
|
196
|
+
persistRun(root, last, true)
|
|
194
197
|
}
|
|
195
198
|
return last
|
|
196
199
|
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The conversation so far, oldest first: every transcript this domain kept.
|
|
203
|
+
*
|
|
204
|
+
* Only terminal runs are written to `runs/`, so the ACTIVE one is missing here —
|
|
205
|
+
* callers layer it on top (the live run is already streamed to them).
|
|
206
|
+
*
|
|
207
|
+
* The frozen `prompt` of each turn is dropped: it is the largest field by far (the
|
|
208
|
+
* whole handoff markdown) and nothing reads it here — the activity drawer inspects
|
|
209
|
+
* the CURRENT run, which still carries it.
|
|
210
|
+
*/
|
|
211
|
+
export function readRunHistory(domainId: string, root: string, limit = 40): AgentRun[] {
|
|
212
|
+
const boundedLimit = Number.isSafeInteger(limit) ? Math.min(Math.max(limit, 1), 100) : 40
|
|
213
|
+
const runs: AgentRun[] = []
|
|
214
|
+
for (const file of listState(root, RUNS_DIR)) {
|
|
215
|
+
if (!file.endsWith('.json')) continue
|
|
216
|
+
try {
|
|
217
|
+
const run = readJson(root, `${RUNS_DIR}/${file}`, decodeAgentRun, null)
|
|
218
|
+
if (run && run.domainId === domainId) runs.push(run)
|
|
219
|
+
} catch {
|
|
220
|
+
// One unreadable transcript must not hide the rest of the conversation.
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
runs.sort((a, b) => a.createdAt.localeCompare(b.createdAt))
|
|
224
|
+
return runs.slice(-boundedLimit).map(({ prompt: _prompt, ...turn }) => turn)
|
|
225
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { documentResponseHeaders } from './documents'
|
|
4
|
+
|
|
5
|
+
test('an uploaded document can never execute on the studio origin', () => {
|
|
6
|
+
// the uploader picks the stored MIME type, and this origin drives a local agent
|
|
7
|
+
const html = documentResponseHeaders('text/html;charset=utf-8', 'notes.html')
|
|
8
|
+
|
|
9
|
+
expect(html['content-type']).toBe('application/octet-stream')
|
|
10
|
+
expect(html['content-disposition']).toBe("attachment; filename*=UTF-8''notes.html")
|
|
11
|
+
expect(html['x-content-type-options']).toBe('nosniff')
|
|
12
|
+
expect(documentResponseHeaders('image/svg+xml', 'logo.svg')['content-type']).toBe(
|
|
13
|
+
'application/octet-stream',
|
|
14
|
+
)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('documents that only ever render as data stay inline', () => {
|
|
18
|
+
const markdown = documentResponseHeaders('text/markdown;charset=utf-8', 'pricing.md')
|
|
19
|
+
|
|
20
|
+
expect(markdown['content-type']).toBe('text/markdown;charset=utf-8')
|
|
21
|
+
expect(markdown['content-disposition']).toBe('inline')
|
|
22
|
+
expect(documentResponseHeaders('application/pdf', 'quote.pdf')['content-disposition']).toBe(
|
|
23
|
+
'inline',
|
|
24
|
+
)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('a download name survives spaces and non-ascii', () => {
|
|
28
|
+
expect(documentResponseHeaders('application/zip', 'Décisions tarifaires.zip')).toMatchObject({
|
|
29
|
+
'content-disposition': "attachment; filename*=UTF-8''D%C3%A9cisions%20tarifaires.zip",
|
|
30
|
+
})
|
|
31
|
+
})
|
|
@@ -8,6 +8,40 @@ import {
|
|
|
8
8
|
} from '../state/documents'
|
|
9
9
|
import { json, notFound, type DomainRouteContext } from './http'
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Types safe to render in a tab. Everything else downloads instead.
|
|
13
|
+
*
|
|
14
|
+
* The uploader chooses the stored MIME type, and this origin also serves the API
|
|
15
|
+
* that drives a local agent — so an `text/html` (or SVG) document opened from the
|
|
16
|
+
* studio would run its script with the studio's own privileges.
|
|
17
|
+
*/
|
|
18
|
+
const INLINE_TYPES = new Set([
|
|
19
|
+
'text/plain',
|
|
20
|
+
'text/markdown',
|
|
21
|
+
'text/csv',
|
|
22
|
+
'application/pdf',
|
|
23
|
+
'image/png',
|
|
24
|
+
'image/jpeg',
|
|
25
|
+
'image/gif',
|
|
26
|
+
'image/webp',
|
|
27
|
+
'image/avif',
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
export function documentResponseHeaders(type: string, name: string): Record<string, string> {
|
|
31
|
+
const mime = (type.split(';')[0] ?? '').trim().toLowerCase()
|
|
32
|
+
if (INLINE_TYPES.has(mime))
|
|
33
|
+
return {
|
|
34
|
+
'content-type': type,
|
|
35
|
+
'content-disposition': 'inline',
|
|
36
|
+
'x-content-type-options': 'nosniff',
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
'content-type': 'application/octet-stream',
|
|
40
|
+
'content-disposition': `attachment; filename*=UTF-8''${encodeURIComponent(name)}`,
|
|
41
|
+
'x-content-type-options': 'nosniff',
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
11
45
|
/** Routes that must run before POST bodies are parsed as JSON. */
|
|
12
46
|
export async function handleDocumentTransport(
|
|
13
47
|
req: Request,
|
|
@@ -33,7 +67,7 @@ export async function handleDocumentTransport(
|
|
|
33
67
|
const document = readDocument(root, decodeURIComponent(raw[1]))
|
|
34
68
|
if (!document) return notFound()
|
|
35
69
|
return new Response(Bun.file(document.abs), {
|
|
36
|
-
headers:
|
|
70
|
+
headers: documentResponseHeaders(document.meta.type, document.meta.name),
|
|
37
71
|
})
|
|
38
72
|
}
|
|
39
73
|
|
|
@@ -27,8 +27,14 @@ test('loadout fails closed instead of probing ambient Claude when gateway auth f
|
|
|
27
27
|
roots.push(root)
|
|
28
28
|
mkdirSync(join(root, 'schema'))
|
|
29
29
|
writeFileSync(join(root, 'astrale.config.ts'), 'export default {}\n')
|
|
30
|
-
writeFileSync(join(root, 'application.ts'), 'export default {}\n')
|
|
31
30
|
writeFileSync(join(root, 'schema/index.ts'), 'export const Test = {}\n')
|
|
31
|
+
writeFileSync(
|
|
32
|
+
join(root, 'application.ts'),
|
|
33
|
+
`import { defineApplication } from '@astrale-os/sdk/application'
|
|
34
|
+
import { Test } from './schema/index.js'
|
|
35
|
+
export default defineApplication({ schema: Test, runtime: {} as never })
|
|
36
|
+
`,
|
|
37
|
+
)
|
|
32
38
|
const handle = registerDomain(root)!
|
|
33
39
|
domainIds.push(handle.id)
|
|
34
40
|
|
|
@@ -19,8 +19,14 @@ function fixture() {
|
|
|
19
19
|
roots.push(root)
|
|
20
20
|
mkdirSync(join(root, 'schema'))
|
|
21
21
|
writeFileSync(join(root, 'astrale.config.ts'), 'export default {}\n')
|
|
22
|
-
writeFileSync(join(root, 'application.ts'), 'export default {}\n')
|
|
23
22
|
writeFileSync(join(root, 'schema/index.ts'), 'export const Test = {}\n')
|
|
23
|
+
writeFileSync(
|
|
24
|
+
join(root, 'application.ts'),
|
|
25
|
+
`import { defineApplication } from '@astrale-os/sdk/application'
|
|
26
|
+
import { Test } from './schema/index.js'
|
|
27
|
+
export default defineApplication({ schema: Test, runtime: {} as never })
|
|
28
|
+
`,
|
|
29
|
+
)
|
|
24
30
|
const handle = registerDomain(root)!
|
|
25
31
|
domainIds.push(handle.id)
|
|
26
32
|
return handle
|
package/studio/server/cache.ts
CHANGED
|
@@ -169,13 +169,13 @@ function hashFileIfPresent(hash: ReturnType<typeof createHash>, label: string, f
|
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
function bundleCacheKey(root: string, schemaDirName: string): string {
|
|
172
|
+
function bundleCacheKey(root: string, schemaDirName: string, applicationFile: string): string {
|
|
173
173
|
const hash = createHash('sha256')
|
|
174
174
|
hash.update(`domain-studio-bundle-cache-v${BUNDLE_CACHE_VERSION}\0`)
|
|
175
175
|
hash.update(`schema-dir:${schemaDirName}\0`)
|
|
176
176
|
hash.update(`bun:${Bun.version}\0`)
|
|
177
177
|
|
|
178
|
-
const files = hashAnatomyFiles(root, schemaDirName)
|
|
178
|
+
const files = hashAnatomyFiles(root, schemaDirName, applicationFile)
|
|
179
179
|
for (const [file, digest] of Object.entries(files).sort(([a], [b]) => a.localeCompare(b))) {
|
|
180
180
|
hash.update(`${file}\0${digest}\0`)
|
|
181
181
|
}
|
|
@@ -213,7 +213,7 @@ export async function getBundle(id: string, rebuild = false): Promise<StudioSche
|
|
|
213
213
|
const h = getDomain(id)
|
|
214
214
|
if (!h) return null
|
|
215
215
|
if (!rebuild && bundles.has(id)) return bundles.get(id)!
|
|
216
|
-
const keyBefore = bundleCacheKey(h.root, h.schemaDirName)
|
|
216
|
+
const keyBefore = bundleCacheKey(h.root, h.schemaDirName, h.applicationFile)
|
|
217
217
|
if (!rebuild) {
|
|
218
218
|
const cached = readCachedBundle(h.root, keyBefore)
|
|
219
219
|
if (cached) {
|
|
@@ -224,7 +224,7 @@ export async function getBundle(id: string, rebuild = false): Promise<StudioSche
|
|
|
224
224
|
}
|
|
225
225
|
const b = await buildBundle(h)
|
|
226
226
|
bundles.set(id, b)
|
|
227
|
-
const keyAfter = bundleCacheKey(h.root, h.schemaDirName)
|
|
227
|
+
const keyAfter = bundleCacheKey(h.root, h.schemaDirName, h.applicationFile)
|
|
228
228
|
if (keyAfter === keyBefore) writeCachedBundle(h.root, keyAfter, b)
|
|
229
229
|
return b
|
|
230
230
|
}
|
package/studio/server/detect.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* detect.ts — workspace domain detection. Walks a directory tree (symlink-safe,
|
|
3
3
|
* ignoring node_modules/.git/.astrale/dist/…), registering every confirmed
|
|
4
|
-
* domain. Also handles being pointed
|
|
4
|
+
* domain whose Application selects an authored Schema. Also handles being pointed
|
|
5
|
+
* at a single astrale.config.ts.
|
|
5
6
|
*/
|
|
6
7
|
import { existsSync, lstatSync, readdirSync } from 'node:fs'
|
|
7
8
|
import { dirname, join, resolve } from 'node:path'
|
|
@@ -23,33 +24,29 @@ const IGNORE = new Set([
|
|
|
23
24
|
])
|
|
24
25
|
|
|
25
26
|
/** Resolve a CLI target: a path to astrale.config.ts, a domain dir, or a workspace to scan. */
|
|
26
|
-
export function resolveTarget(target: string
|
|
27
|
+
export function resolveTarget(target: string): DomainHandle[] {
|
|
27
28
|
const abs = resolve(target)
|
|
28
29
|
if (abs.endsWith('astrale.config.ts')) {
|
|
29
|
-
const h = registerDomain(dirname(abs)
|
|
30
|
+
const h = registerDomain(dirname(abs))
|
|
30
31
|
return h ? [h] : []
|
|
31
32
|
}
|
|
32
33
|
// a single domain dir?
|
|
33
|
-
const single = registerDomain(abs
|
|
34
|
+
const single = registerDomain(abs)
|
|
34
35
|
if (single) {
|
|
35
36
|
// still scan beneath for sibling/nested domains (e.g. a workspace whose root is also a domain)
|
|
36
|
-
const nested = scanWorkspace(abs
|
|
37
|
+
const nested = scanWorkspace(abs).filter((d) => d.id !== single.id)
|
|
37
38
|
return dedupe([single, ...nested])
|
|
38
39
|
}
|
|
39
|
-
return scanWorkspace(abs
|
|
40
|
+
return scanWorkspace(abs)
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
export function scanWorkspace(
|
|
43
|
-
workspace: string,
|
|
44
|
-
schemaDirName = 'schema',
|
|
45
|
-
maxDepth = 4,
|
|
46
|
-
): DomainHandle[] {
|
|
43
|
+
export function scanWorkspace(workspace: string, maxDepth = 4): DomainHandle[] {
|
|
47
44
|
const found: DomainHandle[] = []
|
|
48
45
|
const root = resolve(workspace)
|
|
49
46
|
if (!existsSync(root)) return found
|
|
50
47
|
|
|
51
48
|
const walk = (dir: string, depth: number) => {
|
|
52
|
-
const h = registerDomain(dir
|
|
49
|
+
const h = registerDomain(dir)
|
|
53
50
|
if (h) found.push(h)
|
|
54
51
|
if (depth >= maxDepth) return
|
|
55
52
|
let entries: string[]
|