@astrale-os/cli 1.0.0-beta.1 → 1.0.0-beta.11
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/README.md +23 -12
- package/dist/astrale.js +48048 -56798
- package/dist/public/connect-core.js +2011 -30765
- package/dist/public/keys/index.js +884 -29483
- package/dist/public/paths/index.js +13101 -41459
- package/dist/types/admin/binding.d.ts +19 -0
- package/dist/types/admin/instance/client.d.ts +22 -0
- package/dist/types/admin/instance/index.d.ts +2 -0
- package/dist/types/admin/instance/model.d.ts +5 -12
- package/dist/types/connection/auth.d.ts +3 -0
- package/dist/types/connection/call.d.ts +3 -0
- package/dist/types/connection/command.d.ts +27 -0
- package/dist/types/connection/credential.d.ts +14 -0
- package/dist/types/connection/errors.d.ts +1 -0
- package/dist/types/connection/exchange.d.ts +9 -0
- package/dist/types/connection/failure/classify.d.ts +2 -0
- package/dist/types/connection/failure/index.d.ts +5 -0
- package/dist/types/connection/failure/model.d.ts +25 -0
- package/dist/types/connection/failure/render.d.ts +3 -0
- package/dist/types/connection/index.d.ts +8 -0
- package/dist/types/connection/reasons.d.ts +37 -0
- package/dist/types/connection/self.d.ts +37 -0
- package/dist/types/connection/session.d.ts +31 -0
- package/dist/types/connection/target.d.ts +38 -0
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/lib/admin-instance.d.ts +17 -0
- package/dist/types/lib/admin-target.d.ts +11 -36
- package/dist/types/lib/config.d.ts +7 -59
- package/dist/types/lib/failure-debug.d.ts +1 -0
- package/dist/types/lib/idp.d.ts +26 -168
- package/dist/types/lib/instance-target.d.ts +1 -1
- package/dist/types/lib/instance.d.ts +30 -88
- package/dist/types/lib/log.d.ts +5 -3
- package/dist/types/lib/meta.d.ts +2 -2
- package/dist/types/lib/proc.d.ts +43 -0
- package/dist/types/lib/self.d.ts +9 -0
- package/dist/types/lib/update.d.ts +91 -0
- package/dist/types/lib/validation.d.ts +4 -1
- package/package.json +16 -19
- package/src/__tests__/fixtures/publication.ts +20 -0
- package/src/admin/__tests__/binding.test.ts +31 -0
- package/src/admin/__tests__/fixture.ts +101 -0
- package/src/admin/binding.ts +98 -0
- package/src/admin/catalog/.spec/api.d.ts +2 -2
- package/src/admin/catalog/__tests__/client.test.ts +20 -49
- package/src/admin/catalog/client.ts +36 -32
- package/src/admin/catalog/model.ts +3 -4
- package/src/admin/graph/.spec/api.d.ts +4 -10
- package/src/admin/graph/nodes.ts +1 -1
- package/src/admin/instance/.spec/api.d.ts +2 -2
- package/src/admin/instance/__tests__/client.test.ts +39 -164
- package/src/admin/instance/client.ts +35 -124
- package/src/admin/instance/index.ts +1 -2
- package/src/admin/instance/model.ts +11 -20
- package/src/commands/__tests__/admin-instance.test.ts +11 -8
- package/src/commands/__tests__/auth-login.test.ts +54 -1
- package/src/commands/__tests__/call-describe.test.ts +46 -33
- package/src/commands/__tests__/call.test.ts +5 -1
- package/src/commands/__tests__/domain-install-operation.test.ts +12 -7
- package/src/commands/__tests__/domain-install-owned.test.ts +6 -2
- package/src/commands/__tests__/domain-list.test.ts +63 -4
- package/src/commands/__tests__/install-identity-override.test.ts +19 -19
- package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
- package/src/commands/__tests__/instance-status.test.ts +139 -0
- package/src/commands/__tests__/read-commands.test.ts +35 -10
- package/src/commands/__tests__/studio-descriptor.test.ts +22 -0
- package/src/commands/__tests__/update.test.ts +48 -19
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +43 -56
- package/src/commands/domain/install.ts +13 -17
- package/src/commands/domain/list.ts +18 -13
- package/src/commands/domain/publish.ts +14 -11
- package/src/commands/domain/uninstall.ts +2 -2
- package/src/commands/get.ts +14 -15
- package/src/commands/identity/__tests__/register.test.ts +3 -3
- package/src/commands/identity/register.ts +5 -4
- package/src/commands/instance/create.ts +8 -16
- package/src/commands/instance/delete.ts +12 -9
- package/src/commands/instance/list.ts +2 -2
- package/src/commands/instance/status.ts +66 -20
- package/src/commands/instance/use.ts +16 -12
- package/src/commands/introspect.ts +9 -16
- package/src/commands/logs.ts +24 -25
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +27 -30
- package/src/commands/studio.ts +20 -0
- package/src/commands/token.ts +8 -7
- package/src/commands/update.ts +38 -14
- package/src/commands/view.ts +7 -6
- package/src/connection/.spec/api.d.ts +7 -8
- package/src/connection/.spec/architecture.md +5 -4
- package/src/connection/.spec/flows/session.ts +2 -2
- package/src/connection/.spec/laws/connection.ts +6 -2
- package/src/connection/.spec/layout.ts +1 -0
- package/src/connection/__tests__/credential.test.ts +20 -1
- package/src/connection/__tests__/errors.test.ts +43 -32
- package/src/connection/__tests__/exchange.test.ts +36 -6
- package/src/connection/__tests__/failure-fixtures.ts +39 -0
- package/src/connection/__tests__/failure-safety.test.ts +75 -0
- package/src/connection/__tests__/self.test.ts +31 -11
- package/src/connection/__tests__/session.test.ts +3 -3
- package/src/connection/__tests__/target.test.ts +1 -0
- package/src/connection/auth.ts +13 -0
- package/src/connection/call.ts +1 -1
- package/src/connection/credential.ts +2 -2
- package/src/connection/errors.ts +1 -355
- package/src/connection/exchange.ts +52 -27
- package/src/connection/failure/classify.ts +110 -0
- package/src/connection/failure/index.ts +18 -0
- package/src/connection/failure/model.ts +28 -0
- package/src/connection/failure/render.ts +116 -0
- package/src/connection/reasons.ts +16 -0
- package/src/connection/self.ts +39 -21
- package/src/connection/session.ts +19 -8
- package/src/errors.ts +3 -2
- package/src/graph/.spec/api.d.ts +12 -5
- package/src/graph/.spec/layout.ts +9 -1
- package/src/graph/__tests__/mutation.test.ts +2 -2
- package/src/graph/__tests__/query.test.ts +25 -6
- package/src/graph/class.ts +30 -0
- package/src/graph/index.ts +1 -0
- package/src/graph/query.ts +21 -32
- package/src/identity/.spec/api.d.ts +2 -2
- package/src/identity/__tests__/registration.test.ts +1 -1
- package/src/identity/registration.ts +3 -3
- package/src/keys/__tests__/keys.test.ts +4 -0
- package/src/keys/credential.ts +1 -0
- package/src/lib/__tests__/admin-target.test.ts +12 -0
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/domain-publication.test.ts +138 -0
- package/src/lib/__tests__/idp-session.test.ts +22 -0
- package/src/lib/__tests__/idp.test.ts +36 -4
- package/src/lib/__tests__/instance-candidates.test.ts +1 -3
- package/src/lib/__tests__/instance-target.test.ts +7 -39
- package/src/lib/__tests__/instance.test.ts +6 -0
- package/src/lib/__tests__/log-errors.test.ts +64 -0
- package/src/lib/__tests__/studio-server-deps.test.ts +53 -16
- package/src/lib/__tests__/update.test.ts +113 -8
- package/src/lib/__tests__/view-open-intent.test.ts +0 -2
- package/src/lib/__tests__/view-server.test.ts +18 -3
- package/src/lib/__tests__/view-session.test.ts +1 -1
- package/src/lib/admin-instance.ts +14 -8
- package/src/lib/admin-target.ts +21 -2
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/domain-publication.ts +104 -0
- package/src/lib/failure-debug.ts +26 -0
- package/src/lib/idp.ts +39 -4
- package/src/lib/instance-candidates.ts +2 -2
- package/src/lib/instance-target.ts +3 -14
- package/src/lib/instance.ts +22 -12
- package/src/lib/log.ts +16 -9
- package/src/lib/login-flow.ts +13 -3
- package/src/lib/meta.ts +2 -2
- package/src/lib/provision-instance.ts +34 -57
- package/src/lib/update.ts +124 -30
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/resolve.ts +1 -1
- package/src/lib/view/server.ts +17 -3
- package/src/program/__tests__/program.test.ts +20 -4
- package/src/program/build.ts +2 -2
- package/src/setup/__tests__/instance-step.test.ts +27 -10
- package/src/setup/steps/instance.ts +22 -15
- package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
- package/studio/client/dist/assets/index-7YHdeCxp.js +81 -0
- package/studio/client/dist/assets/index-C0FAnwNw.css +2 -0
- package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
- package/studio/client/dist/assets/schema-studio-BilUNb6Z.css +1 -0
- package/studio/client/dist/assets/schema-studio-CABpTfvH.js +8 -0
- package/studio/client/dist/index.html +3 -2
- package/studio/package.json +10 -14
- package/studio/server/agent/ask.test.ts +1 -1
- package/studio/server/agent/bridge/grant.test.ts +1 -1
- package/studio/server/agent/bridge/routes.test.ts +1 -1
- package/studio/server/agent/bridge/stdio.ts +8 -1
- package/studio/server/agent/conversation.test.ts +10 -5
- package/studio/server/agent/conversation.ts +56 -8
- package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
- package/studio/server/agent/harness/codex/loadout.ts +18 -21
- package/studio/server/agent/harness/gateway/config.ts +20 -18
- package/studio/server/agent/harness/selection.ts +9 -1
- package/studio/server/agent/prompts/anchors.test.ts +32 -36
- package/studio/server/agent/prompts/anchors.ts +20 -49
- package/studio/server/agent/prompts/system.test.ts +3 -2
- package/studio/server/agent/prompts/system.ts +6 -5
- package/studio/server/agent/prompts/turn.ts +13 -4
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/completion.ts +2 -2
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/agent/run/preparation.ts +6 -5
- package/studio/server/agent/run/transcript.test.ts +35 -1
- package/studio/server/agent/run/transcript.ts +169 -3
- package/studio/server/agent/run/usage.test.ts +24 -0
- package/studio/server/agent/run/usage.ts +26 -1
- package/studio/server/api/canvas.ts +43 -0
- package/studio/server/api/comments.ts +85 -0
- package/studio/server/api/context.ts +61 -0
- package/studio/server/api/deployment.ts +39 -0
- package/studio/server/api/documents.ts +55 -0
- package/studio/server/api/domain.ts +59 -0
- package/studio/server/api/http.ts +45 -0
- package/studio/server/api/project.ts +57 -0
- package/studio/server/api/schema.ts +12 -0
- package/studio/server/api/views.ts +57 -0
- package/studio/server/api/workspace.ts +60 -0
- package/studio/server/api-agent-loadout.test.ts +1 -1
- package/studio/server/api.test.ts +147 -0
- package/studio/server/api.ts +20 -422
- package/studio/server/cache.test.ts +122 -0
- package/studio/server/cache.ts +162 -11
- package/studio/server/cli-consumers.test.ts +494 -0
- package/studio/server/cli.test.ts +88 -0
- package/studio/server/cli.ts +240 -0
- package/studio/server/client-package.ts +25 -1
- package/studio/server/domain.test.ts +50 -40
- package/studio/server/domain.ts +83 -52
- package/studio/server/environment/dotenv-preview.test.ts +39 -0
- package/studio/server/environment/dotenv-preview.ts +27 -0
- package/studio/server/environment/files.test.ts +53 -0
- package/studio/server/{state/env.ts → environment/files.ts} +7 -48
- package/studio/server/{state → handoff}/copy.ts +21 -6
- package/studio/server/handoff/service.test.ts +22 -0
- package/studio/server/{state/handoff.ts → handoff/service.ts} +21 -14
- package/studio/server/index.ts +1 -1
- package/studio/server/instances/active.ts +72 -0
- package/studio/server/instances/deploy-record.test.ts +31 -0
- package/studio/server/instances/deploy-record.ts +37 -0
- package/studio/server/instances/deploy.ts +56 -0
- package/studio/server/{state/instance.test.ts → instances/probe.test.ts} +25 -21
- package/studio/server/instances/probe.ts +67 -0
- package/studio/server/instances/status.test.ts +12 -0
- package/studio/server/instances/status.ts +49 -0
- package/studio/server/introspect/anatomy/client-tree.ts +57 -0
- package/studio/server/introspect/anatomy/env-fields.ts +66 -0
- package/studio/server/introspect/anatomy/schema-definition.ts +53 -0
- package/studio/server/introspect/anatomy/source.ts +181 -0
- package/studio/server/introspect/anatomy/views/routes.ts +114 -0
- package/studio/server/introspect/anatomy/views.ts +53 -0
- package/studio/server/introspect/anatomy-extras.test.ts +106 -35
- package/studio/server/introspect/anatomy-extras.ts +7 -726
- package/studio/server/introspect/anatomy.test.ts +22 -14
- package/studio/server/introspect/anatomy.ts +52 -28
- package/studio/server/introspect/bundle.ts +15 -9
- package/studio/server/introspect/canonical-schema.test.ts +93 -309
- package/studio/server/introspect/canonical-schema.ts +257 -425
- package/studio/server/introspect/config-preview.test.ts +32 -0
- package/studio/server/introspect/config-preview.ts +119 -0
- package/studio/server/introspect/core-extractor.ts +18 -127
- package/studio/server/introspect/core.ts +1 -2
- package/studio/server/introspect/diff.test.ts +18 -18
- package/studio/server/introspect/diff.ts +44 -90
- package/studio/server/introspect/extractor.ts +23 -116
- package/studio/server/introspect/hash.ts +5 -2
- package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
- package/studio/server/introspect/overlay-tsmorph.ts +7 -1305
- package/studio/server/introspect/overlay.test.ts +26 -54
- package/studio/server/introspect/overlay.ts +10 -37
- package/studio/server/introspect/revision.test.ts +54 -0
- package/studio/server/introspect/revision.ts +49 -0
- package/studio/server/introspect/runtime.test.ts +83 -143
- package/studio/server/introspect/runtime.ts +40 -11
- package/studio/server/introspect/schema-ir-json.test.ts +70 -0
- package/studio/server/introspect/schema-ir-json.ts +151 -0
- package/studio/server/introspect/schema-refs.test.ts +43 -88
- package/studio/server/introspect/schema-refs.ts +14 -49
- package/studio/server/introspect/source-overlay/annotations.ts +14 -0
- package/studio/server/introspect/source-overlay/handlers.ts +161 -0
- package/studio/server/introspect/source-overlay/kernel-calls.ts +46 -0
- package/studio/server/introspect/source-overlay/project.ts +248 -0
- package/studio/server/introspect/source-overlay/spans.ts +330 -0
- package/studio/server/json.ts +46 -0
- package/studio/server/lifecycle.ts +5 -1
- package/studio/server/sse.test.ts +26 -0
- package/studio/server/sse.ts +4 -1
- package/studio/server/state/baseline.test.ts +223 -7
- package/studio/server/state/baseline.ts +148 -34
- package/studio/server/state/comments.test.ts +66 -1
- package/studio/server/state/comments.ts +173 -10
- package/studio/server/state/context.ts +34 -2
- package/studio/server/state/documents.ts +42 -1
- package/studio/server/state/integrations.ts +27 -1
- package/studio/server/state/layout.test.ts +34 -0
- package/studio/server/state/layout.ts +44 -17
- package/studio/server/state/settings.ts +29 -22
- package/studio/server/state/store.test.ts +122 -0
- package/studio/server/state/store.ts +66 -17
- package/studio/server/state/visibility.ts +22 -11
- package/studio/server/views/model.test.ts +57 -0
- package/studio/server/views/model.ts +66 -0
- package/studio/server/views/runtime.ts +40 -0
- package/studio/server/views/selection-repository.test.ts +31 -0
- package/studio/server/views/selection-repository.ts +60 -0
- package/studio/server/views/session.test.ts +73 -0
- package/studio/server/views/session.ts +166 -0
- package/studio/server/views/target.test.ts +78 -0
- package/studio/server/views/target.ts +222 -0
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +9 -5
- package/studio/server/workspace/catalog.test.ts +17 -0
- package/studio/server/workspace/catalog.ts +43 -0
- package/studio/server/{state → workspace}/create.test.ts +1 -1
- package/studio/server/{state → workspace}/create.ts +1 -1
- package/studio/server/{state → workspace}/git.ts +1 -1
- package/studio/server/workspace/updates.ts +83 -0
- package/studio/server/workspace-state.ts +1 -1
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/README.md +16 -0
- package/studio/shared/contracts/agent.ts +270 -0
- package/studio/shared/contracts/runtime.ts +50 -0
- package/studio/shared/contracts/schema.ts +393 -0
- package/studio/shared/contracts/surface.test.ts +317 -0
- package/studio/shared/contracts/workspace.ts +265 -0
- package/studio/shared/layout.test.ts +116 -0
- package/studio/shared/schema/identity.test.ts +34 -0
- package/studio/shared/schema/identity.ts +68 -0
- package/studio/shared/types.ts +11 -1099
- package/studio/tsconfig.json +1 -1
- package/viewer/dist/main.js +40 -61
- package/.check-workspace.cjs +0 -40
- package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
- package/studio/client/dist/assets/elk-api-D2xgMJvi.js +0 -1
- package/studio/client/dist/assets/index-BMdnsIJA.css +0 -1
- package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
- package/studio/client/dist/assets/index-D-vRV8w7.js +0 -8
- package/studio/client/dist/assets/index-LGSWRrk8.js +0 -81
- package/studio/server/state/catalog.ts +0 -117
- package/studio/server/state/instance.ts +0 -280
- package/studio/server/state/updates.ts +0 -63
- package/studio/server/state/views.test.ts +0 -286
- package/studio/server/state/views.ts +0 -535
|
@@ -81,11 +81,56 @@ describe('auth login command', () => {
|
|
|
81
81
|
expect(result.exitCode).toBe(0)
|
|
82
82
|
const session = JSON.parse(await readFile(join(tmp, 'idp-sessions', 'alice.json'), 'utf-8'))
|
|
83
83
|
expect(session.audience).toBe('https://kernel.example.com')
|
|
84
|
+
expect(session.clientId).toBe('client_123')
|
|
84
85
|
expect(session.subject).toBe('user_123')
|
|
85
86
|
} finally {
|
|
86
87
|
await server.stop()
|
|
87
88
|
}
|
|
88
89
|
})
|
|
90
|
+
|
|
91
|
+
test('persists an explicit client id and reuses it for a named login', async () => {
|
|
92
|
+
const server = oauthServer({
|
|
93
|
+
accessToken: unsignedJwt({
|
|
94
|
+
iss: 'http://127.0.0.1',
|
|
95
|
+
sub: 'user_123',
|
|
96
|
+
exp: 1893456000,
|
|
97
|
+
}),
|
|
98
|
+
})
|
|
99
|
+
try {
|
|
100
|
+
await writeIdpConfig(server.url)
|
|
101
|
+
|
|
102
|
+
const explicit = await runAuthLogin(
|
|
103
|
+
'--idp',
|
|
104
|
+
'test',
|
|
105
|
+
'--name',
|
|
106
|
+
'alice',
|
|
107
|
+
'--client-id',
|
|
108
|
+
'client_override',
|
|
109
|
+
'--client-credentials',
|
|
110
|
+
'--client-secret-env',
|
|
111
|
+
'TEST_CLIENT_SECRET',
|
|
112
|
+
'--raw',
|
|
113
|
+
)
|
|
114
|
+
const reused = await runAuthLogin(
|
|
115
|
+
'--idp',
|
|
116
|
+
'test',
|
|
117
|
+
'--name',
|
|
118
|
+
'alice',
|
|
119
|
+
'--client-credentials',
|
|
120
|
+
'--client-secret-env',
|
|
121
|
+
'TEST_CLIENT_SECRET',
|
|
122
|
+
'--raw',
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
expect(explicit.exitCode).toBe(0)
|
|
126
|
+
expect(reused.exitCode).toBe(0)
|
|
127
|
+
expect(server.clientIds()).toEqual(['client_override', 'client_override'])
|
|
128
|
+
const session = JSON.parse(await readFile(join(tmp, 'idp-sessions', 'alice.json'), 'utf-8'))
|
|
129
|
+
expect(session.clientId).toBe('client_override')
|
|
130
|
+
} finally {
|
|
131
|
+
await server.stop()
|
|
132
|
+
}
|
|
133
|
+
})
|
|
89
134
|
})
|
|
90
135
|
|
|
91
136
|
async function writeIdpConfig(baseUrl: string): Promise<void> {
|
|
@@ -131,12 +176,19 @@ async function writeIdpConfig(baseUrl: string): Promise<void> {
|
|
|
131
176
|
)
|
|
132
177
|
}
|
|
133
178
|
|
|
134
|
-
function oauthServer(args: { accessToken: string }): {
|
|
179
|
+
function oauthServer(args: { accessToken: string }): {
|
|
180
|
+
url: string
|
|
181
|
+
clientIds: () => Array<string | null>
|
|
182
|
+
stop: () => Promise<void>
|
|
183
|
+
} {
|
|
184
|
+
const clientIds: Array<string | null> = []
|
|
135
185
|
const server = Bun.serve({
|
|
136
186
|
port: 0,
|
|
137
187
|
fetch: async (request) => {
|
|
138
188
|
const url = new URL(request.url)
|
|
139
189
|
if (url.pathname !== '/token') return new Response('not found', { status: 404 })
|
|
190
|
+
const form = new URLSearchParams(await request.text())
|
|
191
|
+
clientIds.push(form.get('client_id'))
|
|
140
192
|
return Response.json({
|
|
141
193
|
access_token: args.accessToken,
|
|
142
194
|
token_type: 'Bearer',
|
|
@@ -146,6 +198,7 @@ function oauthServer(args: { accessToken: string }): { url: string; stop: () =>
|
|
|
146
198
|
})
|
|
147
199
|
return {
|
|
148
200
|
url: `http://${server.hostname}:${server.port}`,
|
|
201
|
+
clientIds: () => clientIds,
|
|
149
202
|
stop: () => server.stop(true),
|
|
150
203
|
}
|
|
151
204
|
}
|
|
@@ -1,34 +1,47 @@
|
|
|
1
1
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
2
|
+
import { bundle, defineSchema, func, method, nodeClass } from '@astrale-os/sdk/schema'
|
|
2
3
|
import { describe, expect, test } from 'bun:test'
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
+
import { describeCallableFromBundle } from '../call-describe'
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
output: { mode: 'value' },
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
},
|
|
7
|
+
const createInstance = method({
|
|
8
|
+
auth: 'authenticated',
|
|
9
|
+
description: 'Create one child Instance.',
|
|
10
|
+
input: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: { operationId: { type: 'string' }, slug: { type: 'string' } },
|
|
13
|
+
required: ['operationId', 'slug'],
|
|
14
|
+
additionalProperties: false,
|
|
18
15
|
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
output: { type: 'object', additionalProperties: true },
|
|
17
|
+
static: true,
|
|
18
|
+
})
|
|
19
|
+
const inspectInstance = method({
|
|
20
|
+
auth: 'authenticated',
|
|
21
|
+
description: 'Inspect one child Instance.',
|
|
22
|
+
input: { type: 'object', properties: {}, additionalProperties: false },
|
|
23
|
+
output: { type: 'object', additionalProperties: true },
|
|
24
|
+
})
|
|
25
|
+
const journal = func({
|
|
26
|
+
auth: 'authenticated',
|
|
27
|
+
description: 'Read the authorized Kernel journal.',
|
|
28
|
+
input: { type: 'object', properties: {}, additionalProperties: false },
|
|
29
|
+
output: { type: 'object', additionalProperties: true },
|
|
30
|
+
})
|
|
31
|
+
const source = defineSchema('host.astrale.ai', {
|
|
32
|
+
classes: {
|
|
33
|
+
Manager: nodeClass({ methods: { createInstance } }),
|
|
34
|
+
Instance: nodeClass({ methods: { inspectInstance } }),
|
|
24
35
|
},
|
|
25
|
-
}
|
|
36
|
+
functions: { journal },
|
|
37
|
+
})
|
|
38
|
+
const installed = bundle.create(source)
|
|
26
39
|
|
|
27
|
-
describe('
|
|
28
|
-
test('reads a static Class method from the Domain
|
|
29
|
-
const described =
|
|
40
|
+
describe('describeCallableFromBundle', () => {
|
|
41
|
+
test('reads a static Class method from the resolved Domain', () => {
|
|
42
|
+
const described = describeCallableFromBundle(
|
|
30
43
|
Path.parse('/:host.astrale.ai:class.Manager:createInstance'),
|
|
31
|
-
|
|
44
|
+
installed,
|
|
32
45
|
)
|
|
33
46
|
expect(described).toMatchObject({
|
|
34
47
|
origin: 'host.astrale.ai',
|
|
@@ -36,30 +49,30 @@ describe('describeCallableFromSchema', () => {
|
|
|
36
49
|
method: 'createInstance',
|
|
37
50
|
dispatch: 'static',
|
|
38
51
|
description: 'Create one child Instance.',
|
|
39
|
-
auth: '
|
|
52
|
+
auth: 'authenticated',
|
|
40
53
|
})
|
|
41
54
|
expect(described?.input).toMatchObject({ required: ['operationId', 'slug'] })
|
|
42
55
|
})
|
|
43
56
|
|
|
44
|
-
test('finds
|
|
57
|
+
test('finds one instance method when the receiver Path is not a Class projection', () => {
|
|
45
58
|
expect(
|
|
46
|
-
|
|
47
|
-
Path.parse('/:host.astrale.ai:core.manager::
|
|
48
|
-
|
|
59
|
+
describeCallableFromBundle(
|
|
60
|
+
Path.parse('/:host.astrale.ai:core.manager::inspectInstance'),
|
|
61
|
+
installed,
|
|
49
62
|
),
|
|
50
63
|
).toMatchObject({
|
|
51
64
|
origin: 'host.astrale.ai',
|
|
52
|
-
class: '
|
|
53
|
-
method: '
|
|
65
|
+
class: 'Instance',
|
|
66
|
+
method: 'inspectInstance',
|
|
54
67
|
dispatch: 'instance',
|
|
55
68
|
})
|
|
56
69
|
})
|
|
57
70
|
|
|
58
|
-
test('reads a standalone Function from the Domain
|
|
71
|
+
test('reads a standalone Function from the resolved Domain', () => {
|
|
59
72
|
expect(
|
|
60
|
-
|
|
73
|
+
describeCallableFromBundle(Path.parse('/:host.astrale.ai:function.journal'), installed),
|
|
61
74
|
).toMatchObject({
|
|
62
|
-
origin: '
|
|
75
|
+
origin: 'host.astrale.ai',
|
|
63
76
|
function: 'journal',
|
|
64
77
|
description: 'Read the authorized Kernel journal.',
|
|
65
78
|
})
|
|
@@ -14,7 +14,11 @@ describe('call command result lifetime', () => {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const sessionStream = Object.assign(stream(), { async cancel() {} })
|
|
17
|
-
const materialized = await materializeCallResult({
|
|
17
|
+
const materialized = await materializeCallResult({
|
|
18
|
+
kind: 'stream',
|
|
19
|
+
invocation: { source: 'https://kernel.test' as never, id: 'test-stream' },
|
|
20
|
+
stream: sessionStream,
|
|
21
|
+
})
|
|
18
22
|
sessionOpen = false
|
|
19
23
|
|
|
20
24
|
expect(materialized).toEqual({ kind: 'stream', values: ['first', 'second'] })
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { defineSchema } from '@astrale-os/sdk/schema'
|
|
1
2
|
import { afterEach, describe, expect, mock, test } from 'bun:test'
|
|
2
3
|
|
|
4
|
+
import { releaseFor } from '../../__tests__/fixtures/publication'
|
|
3
5
|
import { installDirect } from '../domain/install'
|
|
4
6
|
|
|
5
7
|
const GENERATED = '4a4c9a18-50f6-4d84-a7b7-2d83e3e45dc8'
|
|
@@ -7,6 +9,13 @@ const RETRY = '139137b5-af47-47ce-92b2-b64a2b0c63d7'
|
|
|
7
9
|
|
|
8
10
|
const originalFetch = globalThis.fetch
|
|
9
11
|
|
|
12
|
+
const schema = defineSchema('crm.test', {})
|
|
13
|
+
const deployed = releaseFor(schema, 'https://crm.test').publication
|
|
14
|
+
|
|
15
|
+
function publicationResponse(): Response {
|
|
16
|
+
return Response.json(deployed)
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
class ExitError extends Error {}
|
|
11
20
|
|
|
12
21
|
afterEach(() => {
|
|
@@ -15,9 +24,7 @@ afterEach(() => {
|
|
|
15
24
|
|
|
16
25
|
describe('direct domain install operation identity', () => {
|
|
17
26
|
test('generates one operation before transport and exposes it for recovery', async () => {
|
|
18
|
-
globalThis.fetch = mock(async () =>
|
|
19
|
-
Response.json({ domainName: 'crm.test' }),
|
|
20
|
-
) as unknown as typeof fetch
|
|
27
|
+
globalThis.fetch = mock(async () => publicationResponse()) as unknown as typeof fetch
|
|
21
28
|
const create = mock(() => GENERATED)
|
|
22
29
|
const requests: unknown[] = []
|
|
23
30
|
let recovery: unknown
|
|
@@ -54,9 +61,7 @@ describe('direct domain install operation identity', () => {
|
|
|
54
61
|
})
|
|
55
62
|
|
|
56
63
|
test('accepts an explicit operation only as the exact retry identity', async () => {
|
|
57
|
-
globalThis.fetch = mock(async () =>
|
|
58
|
-
Response.json({ domainName: 'crm.test' }),
|
|
59
|
-
) as unknown as typeof fetch
|
|
64
|
+
globalThis.fetch = mock(async () => publicationResponse()) as unknown as typeof fetch
|
|
60
65
|
const create = mock(() => GENERATED)
|
|
61
66
|
const accepted: string[] = []
|
|
62
67
|
const requests: unknown[] = []
|
|
@@ -95,7 +100,7 @@ describe('direct domain install operation identity', () => {
|
|
|
95
100
|
test('rejects a non-UUID operation before metadata or Kernel transport', async () => {
|
|
96
101
|
const originalExit = process.exit
|
|
97
102
|
const originalWrite = process.stderr.write
|
|
98
|
-
const fetch = mock(async () =>
|
|
103
|
+
const fetch = mock(async () => publicationResponse())
|
|
99
104
|
const run = mock(async () => undefined)
|
|
100
105
|
globalThis.fetch = fetch as unknown as typeof globalThis.fetch
|
|
101
106
|
process.exit = (() => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ResponseError } from '@astrale-os/
|
|
1
|
+
import { ResponseError } from '@astrale-os/sdk/client'
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test'
|
|
3
3
|
|
|
4
4
|
import type { OwnedInstanceInfo } from '../../lib/admin-instance'
|
|
@@ -11,6 +11,10 @@ class ExitError extends Error {
|
|
|
11
11
|
|
|
12
12
|
const calls: Array<{ path: string; params: unknown }> = []
|
|
13
13
|
let inventory: OwnedInstanceInfo[] = []
|
|
14
|
+
const TEST_INVOCATION = {
|
|
15
|
+
source: 'https://kernel.test',
|
|
16
|
+
id: 'domain-install-owned',
|
|
17
|
+
} as ConstructorParameters<typeof ResponseError>[2]
|
|
14
18
|
|
|
15
19
|
const hostCall = mock(async (call: { target: string; input: unknown }): Promise<unknown> => {
|
|
16
20
|
const path = call.target
|
|
@@ -137,7 +141,7 @@ describe('admin domain install owner boundary', () => {
|
|
|
137
141
|
createdAt: '2026-08-20T00:00:00.000Z',
|
|
138
142
|
updatedAt: '2026-08-20T00:00:00.000Z',
|
|
139
143
|
}
|
|
140
|
-
const failure = new ResponseError(5001, 'Schema operation is not supported.', {
|
|
144
|
+
const failure = new ResponseError(5001, 'Schema operation is not supported.', TEST_INVOCATION, {
|
|
141
145
|
code: 'SCHEMA_UPGRADE_INCOMPATIBLE',
|
|
142
146
|
details: {
|
|
143
147
|
phase: 'upgrade',
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineSchema, schema as schemaApi } from '@astrale-os/sdk/schema'
|
|
2
|
+
import { afterAll, describe, expect, test } from 'bun:test'
|
|
2
3
|
|
|
3
4
|
import type { DomainInfo } from '../../lib/admin-domain'
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
+
import { releaseFor } from '../../__tests__/fixtures/publication'
|
|
7
|
+
import { byDefaultThenName, domainProjection, probe, type DomainRow } from '../domain/list'
|
|
6
8
|
|
|
7
9
|
const strip = (s: string): string => s.replace(/\[[0-9;]*m/g, '')
|
|
8
10
|
|
|
@@ -68,7 +70,64 @@ describe('domain list — projection', () => {
|
|
|
68
70
|
const live: DomainRow = { ...base, reachable: true, checkError: null }
|
|
69
71
|
expect(strip(domainProjection([live]).rows[0].status)).toBe('● live')
|
|
70
72
|
|
|
71
|
-
const down: DomainRow = { ...base, reachable: false, checkError: '
|
|
72
|
-
expect(strip(domainProjection([down]).rows[0].status)).toBe('○
|
|
73
|
+
const down: DomainRow = { ...base, reachable: false, checkError: 'Publication HTTP 502' }
|
|
74
|
+
expect(strip(domainProjection([down]).rows[0].status)).toBe('○ Publication HTTP 502')
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
describe('domain list — canonical Publication check', () => {
|
|
79
|
+
const servers: { stop(): void }[] = []
|
|
80
|
+
afterAll(() => {
|
|
81
|
+
for (const server of servers) server.stop()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('reports the admitted schema revision in machine data', async () => {
|
|
85
|
+
const schema = defineSchema('catalog-check.example.dev', {})
|
|
86
|
+
const deployed = releaseFor(schema, 'https://catalog-check.example.dev').publication
|
|
87
|
+
const server = Bun.serve({
|
|
88
|
+
port: 0,
|
|
89
|
+
fetch(request) {
|
|
90
|
+
return new URL(request.url).pathname === '/.well-known/astrale/domain.json'
|
|
91
|
+
? Response.json(deployed)
|
|
92
|
+
: new Response('not found', { status: 404 })
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
servers.push(server)
|
|
96
|
+
|
|
97
|
+
await expect(
|
|
98
|
+
probe(
|
|
99
|
+
entry({
|
|
100
|
+
origin: schema.origin,
|
|
101
|
+
url: `http://localhost:${server.port}`,
|
|
102
|
+
}),
|
|
103
|
+
),
|
|
104
|
+
).resolves.toMatchObject({
|
|
105
|
+
reachable: true,
|
|
106
|
+
schemaRevision: schemaApi.revision(schema),
|
|
107
|
+
checkError: null,
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('rejects a Publication whose origin differs from the catalog entry', async () => {
|
|
112
|
+
const schema = defineSchema('deployed.example.dev', {})
|
|
113
|
+
const deployed = releaseFor(schema, 'https://deployed.example.dev').publication
|
|
114
|
+
const server = Bun.serve({
|
|
115
|
+
port: 0,
|
|
116
|
+
fetch: () => Response.json(deployed),
|
|
117
|
+
})
|
|
118
|
+
servers.push(server)
|
|
119
|
+
|
|
120
|
+
await expect(
|
|
121
|
+
probe(
|
|
122
|
+
entry({
|
|
123
|
+
origin: 'catalog.example.dev',
|
|
124
|
+
url: `http://localhost:${server.port}`,
|
|
125
|
+
}),
|
|
126
|
+
),
|
|
127
|
+
).resolves.toMatchObject({
|
|
128
|
+
reachable: false,
|
|
129
|
+
checkError:
|
|
130
|
+
'Domain origin mismatch: deployed=deployed.example.dev expected=catalog.example.dev',
|
|
131
|
+
})
|
|
73
132
|
})
|
|
74
133
|
})
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { defineSchema } from '@astrale-os/sdk/schema'
|
|
1
2
|
import { afterAll, describe, expect, test } from 'bun:test'
|
|
2
3
|
|
|
4
|
+
import { releaseFor } from '../../__tests__/fixtures/publication'
|
|
3
5
|
import { domainRefFromTarget, isIdentityOverride, probeDeclaredOrigin } from '../domain/install'
|
|
4
6
|
|
|
5
7
|
describe('admin-path target classification', () => {
|
|
@@ -29,46 +31,44 @@ describe('identity-override detection', () => {
|
|
|
29
31
|
})
|
|
30
32
|
})
|
|
31
33
|
|
|
32
|
-
describe('declared-origin probe
|
|
34
|
+
describe('declared-origin Publication probe', () => {
|
|
33
35
|
const servers: { stop(): void }[] = []
|
|
34
36
|
afterAll(() => {
|
|
35
37
|
for (const s of servers) s.stop()
|
|
36
38
|
})
|
|
37
39
|
|
|
38
|
-
function
|
|
40
|
+
function servePublication(handler: (req: Request) => Response): string {
|
|
39
41
|
const server = Bun.serve({ port: 0, fetch: handler })
|
|
40
42
|
servers.push(server)
|
|
41
43
|
return `http://localhost:${server.port}`
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
test('reads origin from a well-formed
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
issuer: 'https://crm.acme.dev',
|
|
51
|
-
schemaRevision: 'sha256:abc',
|
|
52
|
-
deploymentVersion: 'v1',
|
|
53
|
-
})
|
|
46
|
+
test('reads origin from a well-formed canonical Publication', async () => {
|
|
47
|
+
const schema = defineSchema('crm.acme.dev', {})
|
|
48
|
+
const deployed = releaseFor(schema, 'https://crm.acme.dev').publication
|
|
49
|
+
const url = servePublication((req) =>
|
|
50
|
+
new URL(req.url).pathname === '/.well-known/astrale/domain.json'
|
|
51
|
+
? Response.json(deployed)
|
|
54
52
|
: new Response('nope', { status: 404 }),
|
|
55
53
|
)
|
|
56
54
|
expect(await probeDeclaredOrigin(url)).toBe('crm.acme.dev')
|
|
57
55
|
})
|
|
58
56
|
|
|
59
|
-
test('
|
|
60
|
-
const legacy =
|
|
61
|
-
|
|
57
|
+
test('does not accept the removed pre-Kernel-V2 domainName shape', async () => {
|
|
58
|
+
const legacy = servePublication(() =>
|
|
59
|
+
Response.json({ iss: 'https://x', domainName: 'crm.acme.dev' }),
|
|
60
|
+
)
|
|
61
|
+
expect(await probeDeclaredOrigin(legacy)).toBeUndefined()
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
-
test('degrades to undefined on
|
|
65
|
-
const noName =
|
|
64
|
+
test('degrades to undefined on invalid Publication, non-200, bad JSON, or dead host', async () => {
|
|
65
|
+
const noName = servePublication(() => Response.json({ iss: 'https://x' }))
|
|
66
66
|
expect(await probeDeclaredOrigin(noName)).toBeUndefined()
|
|
67
67
|
|
|
68
|
-
const error =
|
|
68
|
+
const error = servePublication(() => new Response('boom', { status: 500 }))
|
|
69
69
|
expect(await probeDeclaredOrigin(error)).toBeUndefined()
|
|
70
70
|
|
|
71
|
-
const badJson =
|
|
71
|
+
const badJson = servePublication(() => new Response('<html>', { status: 200 }))
|
|
72
72
|
expect(await probeDeclaredOrigin(badJson)).toBeUndefined()
|
|
73
73
|
|
|
74
74
|
expect(await probeDeclaredOrigin('http://127.0.0.1:1')).toBeUndefined()
|
|
@@ -5,7 +5,7 @@ import type { InstanceInfo } from '../../lib/admin-instance'
|
|
|
5
5
|
import { buildInstanceRows, type Bookmark } from '../instance/list'
|
|
6
6
|
|
|
7
7
|
const managed: InstanceInfo[] = [
|
|
8
|
-
{ id: 'demo', slug: 'demo', url: 'https://demo.eu.astrale.ai',
|
|
8
|
+
{ id: 'demo', slug: 'demo', url: 'https://demo.eu.astrale.ai', state: 'ready' },
|
|
9
9
|
]
|
|
10
10
|
|
|
11
11
|
function bookmark(overrides: Partial<Bookmark> = {}): Bookmark {
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test'
|
|
2
|
+
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
import type { InstanceInfo } from '../../lib/admin-instance'
|
|
7
|
+
import type { ResolvedInstance } from '../../lib/instance'
|
|
8
|
+
|
|
9
|
+
import { resolveStatus, type StatusDependencies } from '../instance/status'
|
|
10
|
+
|
|
11
|
+
const cliRoot = join(import.meta.dir, '../../..')
|
|
12
|
+
|
|
13
|
+
let home: string
|
|
14
|
+
let server: ReturnType<typeof Bun.serve>
|
|
15
|
+
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
home = await mkdtemp(join(tmpdir(), 'astrale-instance-status-'))
|
|
18
|
+
server = Bun.serve({
|
|
19
|
+
port: 0,
|
|
20
|
+
fetch(request) {
|
|
21
|
+
const url = new URL(request.url)
|
|
22
|
+
const issuer = `http://127.0.0.1:${server.port}`
|
|
23
|
+
if (url.pathname === '/.well-known/openid-configuration') {
|
|
24
|
+
return Response.json({ issuer, jwks_uri: `${issuer}/.well-known/jwks.json` })
|
|
25
|
+
}
|
|
26
|
+
if (url.pathname === '/.well-known/jwks.json') {
|
|
27
|
+
return Response.json({ keys: [{ kid: 'status-test' }] })
|
|
28
|
+
}
|
|
29
|
+
return new Response('Not found', { status: 404 })
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
afterEach(async () => {
|
|
35
|
+
server.stop(true)
|
|
36
|
+
await rm(home, { recursive: true, force: true })
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('instance status', () => {
|
|
40
|
+
test('keeps managed lifecycle authoritative when the slug is also bookmarked', async () => {
|
|
41
|
+
const managed: InstanceInfo = {
|
|
42
|
+
id: '@managed',
|
|
43
|
+
slug: 'shared',
|
|
44
|
+
url: 'https://managed.example.com',
|
|
45
|
+
state: 'provisioning',
|
|
46
|
+
phase: 'bootstrap',
|
|
47
|
+
}
|
|
48
|
+
const statusOwnedInstance: StatusDependencies['statusOwnedInstance'] = mock(async () => managed)
|
|
49
|
+
const resolveInstance: StatusDependencies['resolveInstance'] = mock(async () => {
|
|
50
|
+
throw new Error('bookmark resolution must not run')
|
|
51
|
+
})
|
|
52
|
+
const probeBookmark: StatusDependencies['probeBookmark'] = mock(async () => {
|
|
53
|
+
throw new Error('bookmark probe must not run')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
await expect(
|
|
57
|
+
resolveStatus('shared', {}, { statusOwnedInstance, resolveInstance, probeBookmark }),
|
|
58
|
+
).resolves.toEqual(managed)
|
|
59
|
+
expect(statusOwnedInstance).toHaveBeenCalledWith({}, 'shared')
|
|
60
|
+
expect(resolveInstance).not.toHaveBeenCalled()
|
|
61
|
+
expect(probeBookmark).not.toHaveBeenCalled()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('probes only an explicitly selected bookmark and rejects mixed source flags', async () => {
|
|
65
|
+
const bookmark: ResolvedInstance = {
|
|
66
|
+
name: 'local',
|
|
67
|
+
kind: 'bookmark',
|
|
68
|
+
url: 'https://local.example.com',
|
|
69
|
+
issuer: 'https://issuer.example.com',
|
|
70
|
+
}
|
|
71
|
+
const statusOwnedInstance: StatusDependencies['statusOwnedInstance'] = mock(async () => {
|
|
72
|
+
throw new Error('managed status must not run')
|
|
73
|
+
})
|
|
74
|
+
const resolveInstance: StatusDependencies['resolveInstance'] = mock(async () => bookmark)
|
|
75
|
+
const probeBookmark: StatusDependencies['probeBookmark'] = mock(async () => undefined)
|
|
76
|
+
const dependencies = { statusOwnedInstance, resolveInstance, probeBookmark }
|
|
77
|
+
|
|
78
|
+
await expect(resolveStatus('local', { bookmarked: true }, dependencies)).resolves.toEqual({
|
|
79
|
+
slug: 'local',
|
|
80
|
+
url: bookmark.url,
|
|
81
|
+
issuer: 'https://issuer.example.com',
|
|
82
|
+
kind: 'bookmark',
|
|
83
|
+
state: 'ready',
|
|
84
|
+
})
|
|
85
|
+
expect(resolveInstance).toHaveBeenCalledWith('local', undefined, { persist: false })
|
|
86
|
+
expect(probeBookmark).toHaveBeenCalledWith(bookmark)
|
|
87
|
+
expect(statusOwnedInstance).not.toHaveBeenCalled()
|
|
88
|
+
|
|
89
|
+
await expect(
|
|
90
|
+
resolveStatus('local', { bookmarked: true, admin: 'admin' }, dependencies),
|
|
91
|
+
).rejects.toMatchObject({ code: 'INVALID_FLAG' })
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('probes an explicit local bookmark without rewriting its registry', async () => {
|
|
95
|
+
const issuer = `http://127.0.0.1:${server.port}`
|
|
96
|
+
const registry = `${JSON.stringify({
|
|
97
|
+
active: 'local',
|
|
98
|
+
instances: {
|
|
99
|
+
local: {
|
|
100
|
+
url: issuer,
|
|
101
|
+
issuer,
|
|
102
|
+
mode: 'remote',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
})}\n`
|
|
106
|
+
const path = join(home, 'instances.json')
|
|
107
|
+
await writeFile(path, registry)
|
|
108
|
+
|
|
109
|
+
const process = Bun.spawn({
|
|
110
|
+
cmd: [
|
|
111
|
+
'bun',
|
|
112
|
+
join(cliRoot, 'bin/astrale.ts'),
|
|
113
|
+
'instance',
|
|
114
|
+
'status',
|
|
115
|
+
'local',
|
|
116
|
+
'--bookmarked',
|
|
117
|
+
'--json',
|
|
118
|
+
],
|
|
119
|
+
env: { ...Bun.env, ASTRALE_HOME: home },
|
|
120
|
+
stdout: 'pipe',
|
|
121
|
+
stderr: 'pipe',
|
|
122
|
+
})
|
|
123
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
124
|
+
new Response(process.stdout).text(),
|
|
125
|
+
new Response(process.stderr).text(),
|
|
126
|
+
process.exited,
|
|
127
|
+
])
|
|
128
|
+
|
|
129
|
+
expect(exitCode, stderr).toBe(0)
|
|
130
|
+
expect(JSON.parse(stdout)).toEqual({
|
|
131
|
+
slug: 'local',
|
|
132
|
+
url: issuer,
|
|
133
|
+
issuer,
|
|
134
|
+
kind: 'bookmark',
|
|
135
|
+
state: 'ready',
|
|
136
|
+
})
|
|
137
|
+
expect(await readFile(path, 'utf8')).toBe(registry)
|
|
138
|
+
})
|
|
139
|
+
})
|