@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
|
@@ -36,6 +36,8 @@ let mutations: unknown[] = []
|
|
|
36
36
|
let queryResult: unknown
|
|
37
37
|
let getResult: unknown
|
|
38
38
|
let mutationResult: unknown
|
|
39
|
+
let selfFailure: unknown
|
|
40
|
+
let selfContexts: unknown[] = []
|
|
39
41
|
|
|
40
42
|
const queryMock = mock(async (ast: unknown, options?: unknown) => {
|
|
41
43
|
queryCalls.push({ ast, options })
|
|
@@ -49,7 +51,9 @@ const mutateMock = mock(async (mutation: unknown) => {
|
|
|
49
51
|
mutations.push(mutation)
|
|
50
52
|
return mutationResult
|
|
51
53
|
})
|
|
52
|
-
const expandSelfInPathMock = mock(async (path: string) => {
|
|
54
|
+
const expandSelfInPathMock = mock(async (path: string, context: unknown) => {
|
|
55
|
+
selfContexts.push(context)
|
|
56
|
+
if (selfFailure !== undefined) throw selfFailure
|
|
53
57
|
if (path === '@self') {
|
|
54
58
|
return {
|
|
55
59
|
path: '@expanded-self',
|
|
@@ -59,10 +63,11 @@ const expandSelfInPathMock = mock(async (path: string) => {
|
|
|
59
63
|
return { path, meta: undefined }
|
|
60
64
|
})
|
|
61
65
|
const withSelfHintMock = mock(async (action: () => Promise<unknown>) => action())
|
|
66
|
+
const commandContext = {
|
|
67
|
+
graph: { query: queryMock, getOrThrow: getOrThrowMock, mutate: mutateMock },
|
|
68
|
+
}
|
|
62
69
|
const runKernelCommandMock = mock(async (run: RunOpts) => {
|
|
63
|
-
const result = await run.fn(
|
|
64
|
-
graph: { query: queryMock, getOrThrow: getOrThrowMock, mutate: mutateMock },
|
|
65
|
-
})
|
|
70
|
+
const result = await run.fn(commandContext)
|
|
66
71
|
await run.format?.(result, run.opts, true)
|
|
67
72
|
})
|
|
68
73
|
|
|
@@ -88,6 +93,8 @@ beforeEach(() => {
|
|
|
88
93
|
}
|
|
89
94
|
getResult = undefined
|
|
90
95
|
mutationResult = { createdNodes: {} }
|
|
96
|
+
selfFailure = undefined
|
|
97
|
+
selfContexts = []
|
|
91
98
|
queryMock.mockClear()
|
|
92
99
|
getOrThrowMock.mockClear()
|
|
93
100
|
mutateMock.mockClear()
|
|
@@ -161,7 +168,7 @@ describe('query command', () => {
|
|
|
161
168
|
])
|
|
162
169
|
})
|
|
163
170
|
|
|
164
|
-
test('authors a
|
|
171
|
+
test('authors a Class source through the same canonical Query V6 call', async () => {
|
|
165
172
|
const { queryCommand } = await import('../query')
|
|
166
173
|
|
|
167
174
|
await queryCommand([], {
|
|
@@ -179,8 +186,8 @@ describe('query command', () => {
|
|
|
179
186
|
kind: 'node',
|
|
180
187
|
terms: [
|
|
181
188
|
{
|
|
182
|
-
kind: '
|
|
183
|
-
|
|
189
|
+
kind: 'class',
|
|
190
|
+
class: { origin: 'issues.astrale.ai', kind: 'class', name: 'Issue' },
|
|
184
191
|
},
|
|
185
192
|
],
|
|
186
193
|
binding: 'n0',
|
|
@@ -193,6 +200,17 @@ describe('query command', () => {
|
|
|
193
200
|
])
|
|
194
201
|
})
|
|
195
202
|
|
|
203
|
+
test('resolves @self inside the command session and never relabels transport failure as input', async () => {
|
|
204
|
+
const { queryCommand } = await import('../query')
|
|
205
|
+
await queryCommand(['@self'], { json: true, limit: '2' })
|
|
206
|
+
expect(selfContexts).toEqual([commandContext])
|
|
207
|
+
|
|
208
|
+
const aggregate = new AggregateError([], '')
|
|
209
|
+
selfFailure = aggregate
|
|
210
|
+
await expect(queryCommand(['@self'], { json: true, limit: '2' })).rejects.toBe(aggregate)
|
|
211
|
+
expect(errors.join('\n')).not.toContain('INVALID_INPUT')
|
|
212
|
+
})
|
|
213
|
+
|
|
196
214
|
/** @evidence TEST-CLI-QUERY-REJECTS-V1-BEFORE-CONNECTION */
|
|
197
215
|
test('rejects a legacy AST before opening the command connection', async () => {
|
|
198
216
|
const { queryCommand } = await import('../query')
|
|
@@ -215,7 +233,7 @@ describe('get command', () => {
|
|
|
215
233
|
const { getCommand } = await import('../get')
|
|
216
234
|
getResult = {
|
|
217
235
|
id: 'note-1',
|
|
218
|
-
class: '
|
|
236
|
+
class: 'notes.example.dev:class.Note',
|
|
219
237
|
props: { 'notes.example.dev:class.Note.property.title': 'Hello' },
|
|
220
238
|
}
|
|
221
239
|
|
|
@@ -226,6 +244,13 @@ describe('get command', () => {
|
|
|
226
244
|
expect(stdout).not.toContain('__labels')
|
|
227
245
|
expect(stdout).not.toContain('classId')
|
|
228
246
|
})
|
|
247
|
+
|
|
248
|
+
test('resolves @self through the same point-read command context', async () => {
|
|
249
|
+
const { getCommand } = await import('../get')
|
|
250
|
+
await getCommand('@self', { json: true })
|
|
251
|
+
expect(selfContexts).toEqual([commandContext])
|
|
252
|
+
expect(getTargets).toEqual(['@expanded-self'])
|
|
253
|
+
})
|
|
229
254
|
})
|
|
230
255
|
|
|
231
256
|
describe('mutate command', () => {
|
|
@@ -242,7 +267,7 @@ describe('mutate command', () => {
|
|
|
242
267
|
{
|
|
243
268
|
op: 'node.create',
|
|
244
269
|
as: 'note',
|
|
245
|
-
class: '
|
|
270
|
+
class: 'notes.example.dev:class.Note',
|
|
246
271
|
props: {},
|
|
247
272
|
},
|
|
248
273
|
],
|
|
@@ -258,7 +283,7 @@ describe('mutate command', () => {
|
|
|
258
283
|
{
|
|
259
284
|
op: 'node.create',
|
|
260
285
|
as: 'note',
|
|
261
|
-
class: '
|
|
286
|
+
class: 'notes.example.dev:class.Note',
|
|
262
287
|
props: {},
|
|
263
288
|
},
|
|
264
289
|
],
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test'
|
|
2
|
+
import { realpathSync } from 'node:fs'
|
|
3
|
+
|
|
4
|
+
import { encodeStudioCliDescriptor } from '../studio'
|
|
5
|
+
|
|
6
|
+
test('Studio descriptor preserves the exact runtime and script entrypoint', () => {
|
|
7
|
+
expect(JSON.parse(encodeStudioCliDescriptor(process.execPath, import.meta.path))).toEqual({
|
|
8
|
+
version: 1,
|
|
9
|
+
executable: process.execPath,
|
|
10
|
+
args: [realpathSync(import.meta.path)],
|
|
11
|
+
})
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('Studio descriptor reinvokes a compiled executable without its virtual bunfs entry', () => {
|
|
15
|
+
expect(JSON.parse(encodeStudioCliDescriptor('/opt/bin/astrale', '/$bunfs/root/astrale'))).toEqual(
|
|
16
|
+
{
|
|
17
|
+
version: 1,
|
|
18
|
+
executable: '/opt/bin/astrale',
|
|
19
|
+
args: [],
|
|
20
|
+
},
|
|
21
|
+
)
|
|
22
|
+
})
|
|
@@ -6,14 +6,17 @@ describe('CLI update staleness', () => {
|
|
|
6
6
|
test('trusts the release manifest for a script install without consulting npm latest', async () => {
|
|
7
7
|
const fetchPackageVersion = mock(async () => '0.8.1-alpha.7')
|
|
8
8
|
const result = await cliStale(
|
|
9
|
-
{
|
|
9
|
+
{},
|
|
10
10
|
{
|
|
11
|
-
update: async () =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
update: async ({ channel }) => {
|
|
12
|
+
expect(channel).toBe('beta')
|
|
13
|
+
return {
|
|
14
|
+
status: 'up-to-date',
|
|
15
|
+
currentVersion: '1.0.0-beta.0',
|
|
16
|
+
latestVersion: '1.0.0-beta.0',
|
|
17
|
+
channel: 'beta',
|
|
18
|
+
}
|
|
19
|
+
},
|
|
17
20
|
fetchPackageVersion,
|
|
18
21
|
},
|
|
19
22
|
)
|
|
@@ -28,37 +31,63 @@ describe('CLI update staleness', () => {
|
|
|
28
31
|
expect(fetchPackageVersion).not.toHaveBeenCalled()
|
|
29
32
|
})
|
|
30
33
|
|
|
31
|
-
test('uses
|
|
34
|
+
test('uses an explicit npm dist-tag only for package-managed installs', async () => {
|
|
32
35
|
const result = await cliStale(
|
|
33
|
-
{ channel: '
|
|
36
|
+
{ channel: 'canary' },
|
|
34
37
|
{
|
|
35
|
-
update: async () => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
update: async () => ({
|
|
39
|
+
status: 'managed',
|
|
40
|
+
currentVersion: '1.0.0-beta.0',
|
|
41
|
+
executable: '/opt/homebrew/bin/node',
|
|
42
|
+
}),
|
|
38
43
|
fetchPackageVersion: async ({ channel }) => {
|
|
39
|
-
expect(channel).toBe('
|
|
40
|
-
return '1.0.0-
|
|
44
|
+
expect(channel).toBe('canary')
|
|
45
|
+
return '1.0.0-canary.0'
|
|
41
46
|
},
|
|
42
47
|
},
|
|
43
48
|
)
|
|
44
49
|
|
|
45
50
|
expect(result).toMatchObject({
|
|
46
51
|
managed: true,
|
|
47
|
-
latest: '1.0.0-
|
|
52
|
+
latest: '1.0.0-canary.0',
|
|
48
53
|
channel: 'npm',
|
|
49
54
|
})
|
|
50
55
|
})
|
|
51
56
|
|
|
52
|
-
test('
|
|
57
|
+
test('does not misclassify a script update failure as package-managed', async () => {
|
|
58
|
+
const fetchPackageVersion = mock(async () => '9.9.9')
|
|
59
|
+
const result = await cliStale(
|
|
60
|
+
{},
|
|
61
|
+
{
|
|
62
|
+
update: async () => {
|
|
63
|
+
throw new Error('release endpoint unavailable')
|
|
64
|
+
},
|
|
65
|
+
fetchPackageVersion,
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
expect(result).toMatchObject({
|
|
70
|
+
stale: false,
|
|
71
|
+
managed: false,
|
|
72
|
+
error: 'release endpoint unavailable',
|
|
73
|
+
})
|
|
74
|
+
expect(fetchPackageVersion).not.toHaveBeenCalled()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('maps the default to beta and the stable channel to npm latest', async () => {
|
|
53
78
|
const originalFetch = globalThis.fetch
|
|
54
79
|
const fetchMock = mock(async (input: RequestInfo | URL) => {
|
|
55
|
-
|
|
56
|
-
return Response.json({ version: '1.0.0' })
|
|
80
|
+
const target = String(input).split('/').at(-1)
|
|
81
|
+
return Response.json({ version: target === 'beta' ? '1.0.0-beta.0' : '1.0.0' })
|
|
57
82
|
})
|
|
58
83
|
globalThis.fetch = fetchMock as unknown as typeof fetch
|
|
59
84
|
try {
|
|
85
|
+
expect(await fetchNpmTargetVersion({})).toBe('1.0.0-beta.0')
|
|
60
86
|
expect(await fetchNpmTargetVersion({ channel: 'stable' })).toBe('1.0.0')
|
|
61
|
-
expect(fetchMock).
|
|
87
|
+
expect(fetchMock.mock.calls.map(([input]) => String(input))).toEqual([
|
|
88
|
+
'https://registry.npmjs.org/@astrale-os/cli/beta',
|
|
89
|
+
'https://registry.npmjs.org/@astrale-os/cli/latest',
|
|
90
|
+
])
|
|
62
91
|
} finally {
|
|
63
92
|
globalThis.fetch = originalFetch
|
|
64
93
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Path } from '@astrale-os/sdk/graph/path'
|
|
2
|
+
import type { ResolvedFunction, ResolvedMethod } from '@astrale-os/sdk/schema'
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import { bundle, schema } from '@astrale-os/sdk/schema'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
import { AstraleError } from '../errors'
|
|
6
7
|
|
|
7
8
|
export interface CallableDescription {
|
|
8
9
|
readonly path: string
|
|
@@ -18,40 +19,40 @@ export interface CallableDescription {
|
|
|
18
19
|
readonly candidates?: readonly CallableDescription[]
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
/** Resolve one callable
|
|
22
|
-
export function
|
|
22
|
+
/** Resolve one callable from an admitted installed Domain bundle. */
|
|
23
|
+
export function describeCallableFromBundle(
|
|
23
24
|
path: Path,
|
|
24
|
-
|
|
25
|
+
input: unknown,
|
|
25
26
|
): CallableDescription | undefined {
|
|
26
27
|
if (path.ast.anchor.kind !== 'domain') return undefined
|
|
27
28
|
const origin = path.ast.anchor.origin
|
|
28
29
|
const last = path.ast.steps.at(-1)
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
if (last === undefined) return undefined
|
|
31
|
+
const domain = schema.resolve(bundle.accept(input).root)
|
|
32
|
+
if (domain.origin !== origin) return undefined
|
|
31
33
|
|
|
32
34
|
if (last.kind === 'method') {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
owner
|
|
36
|
-
(owner
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
const ownerStep = path.ast.steps.at(-2)
|
|
36
|
+
if (ownerStep?.kind === 'projection' && ownerStep.projection.kind === 'class') {
|
|
37
|
+
const owner = domain.classes[ownerStep.projection.name]
|
|
38
|
+
if (owner?.kind !== 'node') return undefined
|
|
39
|
+
const method = namedMethod(
|
|
40
|
+
last.dispatch === 'static' ? owner.static.methods : owner.methods,
|
|
41
|
+
last.name,
|
|
42
|
+
last.dispatch,
|
|
40
43
|
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return Object.freeze({
|
|
45
|
-
path: path.raw,
|
|
46
|
-
origin,
|
|
47
|
-
class: owner.projection.name,
|
|
48
|
-
method: last.name,
|
|
49
|
-
dispatch: last.dispatch,
|
|
50
|
-
...callableFields(method),
|
|
51
|
-
})
|
|
52
|
-
}
|
|
44
|
+
return method === undefined
|
|
45
|
+
? undefined
|
|
46
|
+
: description(path, origin, owner.ref.name, method, last.dispatch)
|
|
53
47
|
}
|
|
54
|
-
|
|
48
|
+
|
|
49
|
+
const matches = Object.values(domain.classes).flatMap((owner) => {
|
|
50
|
+
if (owner.kind !== 'node') return []
|
|
51
|
+
const method = namedMethod(owner.methods, last.name, last.dispatch)
|
|
52
|
+
return method === undefined
|
|
53
|
+
? []
|
|
54
|
+
: [description(path, origin, owner.ref.name, method, last.dispatch)]
|
|
55
|
+
})
|
|
55
56
|
if (matches.length === 1) return matches[0]
|
|
56
57
|
if (matches.length > 1) {
|
|
57
58
|
return Object.freeze({
|
|
@@ -66,13 +67,13 @@ export function describeCallableFromSchema(
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
if (last.kind === 'projection' && last.projection.kind === 'function') {
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
70
|
+
const callable = domain.functions[last.projection.name]
|
|
71
|
+
if (callable === undefined) return undefined
|
|
71
72
|
return Object.freeze({
|
|
72
73
|
path: path.raw,
|
|
73
74
|
origin,
|
|
74
|
-
function:
|
|
75
|
-
...callableFields(
|
|
75
|
+
function: callable.ref.name,
|
|
76
|
+
...callableFields(callable),
|
|
76
77
|
})
|
|
77
78
|
}
|
|
78
79
|
return undefined
|
|
@@ -82,50 +83,41 @@ export function missingCallableDescription(path: string): AstraleError {
|
|
|
82
83
|
return new AstraleError(
|
|
83
84
|
'CALL_DESCRIBE_UNAVAILABLE',
|
|
84
85
|
`No callable schema is installed for ${path}.`,
|
|
85
|
-
'Use a Domain-rooted Path such as /:
|
|
86
|
+
'Use a Domain-rooted Path such as /:kernel.astrale.ai:class.Identity:whois. Method Paths are not Function nodes.',
|
|
86
87
|
)
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
function
|
|
90
|
-
|
|
90
|
+
function namedMethod(
|
|
91
|
+
methods: Iterable<ResolvedMethod>,
|
|
92
|
+
name: string,
|
|
93
|
+
dispatch: 'static' | 'instance',
|
|
94
|
+
): ResolvedMethod | undefined {
|
|
95
|
+
const expectedStatic = dispatch === 'static'
|
|
96
|
+
return [...methods].find((method) => method.name === name && method.static === expectedStatic)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function description(
|
|
100
|
+
path: Path,
|
|
91
101
|
origin: string,
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
className: string,
|
|
103
|
+
callable: ResolvedMethod,
|
|
94
104
|
dispatch: 'static' | 'instance',
|
|
95
|
-
): CallableDescription
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Object.freeze({
|
|
105
|
-
path,
|
|
106
|
-
origin,
|
|
107
|
-
class: className,
|
|
108
|
-
method,
|
|
109
|
-
dispatch,
|
|
110
|
-
...callableFields(methodDef),
|
|
111
|
-
}),
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return matches
|
|
105
|
+
): CallableDescription {
|
|
106
|
+
return Object.freeze({
|
|
107
|
+
path: path.raw,
|
|
108
|
+
origin,
|
|
109
|
+
class: className,
|
|
110
|
+
method: callable.name,
|
|
111
|
+
dispatch,
|
|
112
|
+
...callableFields(callable),
|
|
113
|
+
})
|
|
116
114
|
}
|
|
117
115
|
|
|
118
|
-
function callableFields(
|
|
116
|
+
function callableFields(callable: ResolvedFunction | ResolvedMethod): Partial<CallableDescription> {
|
|
119
117
|
return {
|
|
120
|
-
...(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
...(callable.description === undefined ? {} : { description: callable.description }),
|
|
119
|
+
auth: callable.auth,
|
|
120
|
+
input: callable.input,
|
|
121
|
+
output: callable.output,
|
|
124
122
|
}
|
|
125
123
|
}
|
|
126
|
-
|
|
127
|
-
function asRecord(input: unknown): JsonRecord | undefined {
|
|
128
|
-
return input !== null && typeof input === 'object' && !Array.isArray(input)
|
|
129
|
-
? (input as JsonRecord)
|
|
130
|
-
: undefined
|
|
131
|
-
}
|
package/src/commands/call.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { CommandDefinition } from '../program/index'
|
|
|
5
5
|
|
|
6
6
|
import { createPathCall, expandSelfInCall, runKernelCommand, withSelfHint } from '../connection'
|
|
7
7
|
import { presentBinary } from '../lib/binary'
|
|
8
|
-
import {
|
|
8
|
+
import { failInput, log } from '../lib/log'
|
|
9
9
|
import { output, present } from '../lib/output'
|
|
10
10
|
|
|
11
11
|
type CallOpts = KernelCommandOpts & {
|
|
@@ -24,60 +24,47 @@ export async function callCommand(
|
|
|
24
24
|
rawParams: string[],
|
|
25
25
|
opts: CallOpts,
|
|
26
26
|
): Promise<void> {
|
|
27
|
-
// Expand @self before describe/execute through authenticated Identity.whoami.
|
|
28
|
-
let expanded: Awaited<ReturnType<typeof expandSelfInCall>>
|
|
29
|
-
try {
|
|
30
|
-
expanded = await expandSelfInCall(path, rawParams, opts)
|
|
31
|
-
} catch (error) {
|
|
32
|
-
failClosed(error, opts)
|
|
33
|
-
}
|
|
34
|
-
const expandedPath = expanded.path
|
|
35
|
-
try {
|
|
36
|
-
Path.parse(expandedPath)
|
|
37
|
-
} catch (error) {
|
|
38
|
-
failClosed(error, opts)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ── Parse params ────────────────────────────────────────
|
|
42
27
|
let params: Record<string, unknown>
|
|
43
28
|
try {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// ── Dry-run: show what would be sent ─────────────────────
|
|
50
|
-
if (opts.dryRun) {
|
|
51
|
-
output({ method: expandedPath, params }, opts)
|
|
52
|
-
return
|
|
29
|
+
Path.parse(path)
|
|
30
|
+
params = await parseParams(rawParams, opts.data)
|
|
31
|
+
} catch (error) {
|
|
32
|
+
failInput(error, opts)
|
|
53
33
|
}
|
|
54
34
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
35
|
+
await runKernelCommand<MaterializedCallResult | { readonly kind: 'dry'; readonly call: unknown }>(
|
|
36
|
+
{
|
|
37
|
+
opts,
|
|
38
|
+
label: path,
|
|
39
|
+
fn: async (ctx) => {
|
|
40
|
+
const expanded = await expandSelfInCall(path, params, ctx)
|
|
41
|
+
const request = createPathCall(expanded.path, expanded.parameters)
|
|
42
|
+
if (opts.dryRun) return { kind: 'dry', call: request }
|
|
43
|
+
return withSelfHint(
|
|
44
|
+
async () => materializeCallResult(await ctx.session.dispatch(request)),
|
|
45
|
+
expanded.meta,
|
|
46
|
+
)
|
|
47
|
+
},
|
|
48
|
+
format: async (result, fmtOpts) => {
|
|
49
|
+
switch (result.kind) {
|
|
50
|
+
case 'dry':
|
|
51
|
+
output(result.call, fmtOpts)
|
|
52
|
+
return
|
|
53
|
+
case 'value':
|
|
54
|
+
present(result.value, fmtOpts)
|
|
55
|
+
return
|
|
56
|
+
case 'binary':
|
|
57
|
+
await presentBinary(result.value, fmtOpts, { outFile: opts.output })
|
|
58
|
+
return
|
|
59
|
+
case 'stream':
|
|
60
|
+
output(result.values, fmtOpts)
|
|
61
|
+
return
|
|
62
|
+
case 'redirect':
|
|
63
|
+
throw new Error('Client Session returned an unresolved redirect.')
|
|
64
|
+
}
|
|
65
|
+
},
|
|
79
66
|
},
|
|
80
|
-
|
|
67
|
+
)
|
|
81
68
|
}
|
|
82
69
|
|
|
83
70
|
/** Drain a session-backed stream before the command-scoped Client Session closes. */
|
|
@@ -101,7 +88,7 @@ export async function parseParams(
|
|
|
101
88
|
try {
|
|
102
89
|
return JSON.parse(dataFlag)
|
|
103
90
|
} catch {
|
|
104
|
-
throw new
|
|
91
|
+
throw new TypeError(`Invalid JSON in --data: ${dataFlag}`)
|
|
105
92
|
}
|
|
106
93
|
}
|
|
107
94
|
|
|
@@ -114,7 +101,7 @@ export async function parseParams(
|
|
|
114
101
|
try {
|
|
115
102
|
return JSON.parse(stdin)
|
|
116
103
|
} catch {
|
|
117
|
-
throw new
|
|
104
|
+
throw new TypeError('Invalid JSON from stdin')
|
|
118
105
|
}
|
|
119
106
|
}
|
|
120
107
|
|
|
@@ -142,7 +129,7 @@ export function parseKeyValue(pairs: string[]): Record<string, unknown> {
|
|
|
142
129
|
for (const pair of pairs) {
|
|
143
130
|
const eqIdx = pair.indexOf('=')
|
|
144
131
|
if (eqIdx === -1) {
|
|
145
|
-
throw new
|
|
132
|
+
throw new TypeError(`Invalid param "${pair}" — expected key=value format`)
|
|
146
133
|
}
|
|
147
134
|
const key = pair.slice(0, eqIdx)
|
|
148
135
|
const raw = pair.slice(eqIdx + 1)
|
|
@@ -150,7 +137,7 @@ export function parseKeyValue(pairs: string[]): Record<string, unknown> {
|
|
|
150
137
|
const hint = key.endsWith(':')
|
|
151
138
|
? ` (looks like httpie's "key:=value" syntax — Astrale CLI doesn't support it; use --data '{"${key.slice(0, -1)}":<value>}' for nested values)`
|
|
152
139
|
: ` (keys must be identifier-shaped: letters, digits, underscore, hyphen)`
|
|
153
|
-
throw new
|
|
140
|
+
throw new TypeError(`Invalid param key "${key}" in "${pair}"${hint}`)
|
|
154
141
|
}
|
|
155
142
|
result[key] = coerceValue(raw)
|
|
156
143
|
}
|
|
@@ -194,7 +181,7 @@ Self-reference:
|
|
|
194
181
|
Callable input/output lives on astrale introspect <path>.
|
|
195
182
|
|
|
196
183
|
Examples:
|
|
197
|
-
$ astrale introspect /:
|
|
184
|
+
$ astrale introspect /:kernel.astrale.ai:class.Identity:whois
|
|
198
185
|
$ astrale call /:blog.acme.com:class.Author:list limit=10
|
|
199
186
|
$ astrale call '@self::deactivate'
|
|
200
187
|
$ astrale call /:kernel.astrale.ai:function.journal --data '{"limit":5}' --json
|
|
@@ -203,7 +190,7 @@ Examples:
|
|
|
203
190
|
{
|
|
204
191
|
name: 'path',
|
|
205
192
|
description:
|
|
206
|
-
'Operation path (e.g., /:
|
|
193
|
+
'Operation path (e.g., /:kernel.astrale.ai:class.Identity:whois or /node::method)',
|
|
207
194
|
},
|
|
208
195
|
{ name: 'params...', description: 'Params as key=value pairs', required: false },
|
|
209
196
|
],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
2
|
-
import {
|
|
2
|
+
import { K } from '@astrale-os/sdk/schema'
|
|
3
3
|
import chalk from 'chalk'
|
|
4
4
|
|
|
5
5
|
import type { KernelCommandOpts } from '../../connection'
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '../../lib/admin-domain'
|
|
16
16
|
import { listOwnedInstancesInContext, type OwnedInstanceInfo } from '../../lib/admin-instance'
|
|
17
17
|
import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
|
|
18
|
+
import { fetchDomainPublication } from '../../lib/domain-publication'
|
|
18
19
|
import { getActive } from '../../lib/instance'
|
|
19
20
|
import { fatal, log, withSpinner } from '../../lib/log'
|
|
20
21
|
import { isMachine, output } from '../../lib/output'
|
|
@@ -413,7 +414,7 @@ export async function installDirect(
|
|
|
413
414
|
fn: async ({ session }) =>
|
|
414
415
|
(await session.call(
|
|
415
416
|
createPathCall(
|
|
416
|
-
Path.project(
|
|
417
|
+
Path.project(K.functions.install.ref).raw,
|
|
417
418
|
directInstallCallInput(url, operation, opts.token),
|
|
418
419
|
),
|
|
419
420
|
)) as DirectInstallResult,
|
|
@@ -429,11 +430,11 @@ export async function installDirect(
|
|
|
429
430
|
log.dim(` operation: ${result.operation}`)
|
|
430
431
|
// Belt-and-braces: the kernel-confirmed origin is authoritative. If it
|
|
431
432
|
// aliases the host and the pre-install gate never consented to THAT
|
|
432
|
-
// origin (lying or
|
|
433
|
+
// origin (lying or unavailable Publication), say so loudly after the fact.
|
|
433
434
|
if (isIdentityOverride(installed.origin, host) && installed.origin !== consentedOrigin) {
|
|
434
435
|
log.warn(
|
|
435
436
|
`Installed origin "${installed.origin}" differs from the serving host "${host}" ` +
|
|
436
|
-
`and was not confirmed before install (the worker
|
|
437
|
+
`and was not confirmed before install (the worker Publication was unavailable). ` +
|
|
437
438
|
`Every ${installed.origin}/* call on this instance now routes to ${host}.`,
|
|
438
439
|
)
|
|
439
440
|
}
|
|
@@ -478,10 +479,10 @@ export function isIdentityOverride(origin: string, host: string): boolean {
|
|
|
478
479
|
* Claiming an origin that differs from the serving host is an explicit actAs
|
|
479
480
|
* and needs typed consent (or `--allow-identity-override` in scripts).
|
|
480
481
|
*
|
|
481
|
-
* The pre-install check reads the worker's
|
|
482
|
+
* The pre-install check reads the worker's canonical Publication origin,
|
|
482
483
|
* so it is consent UX, not enforcement — a hostile worker can lie here, and
|
|
483
484
|
* the kernel anchors the cryptographic identity (`iss`) on the real URL
|
|
484
|
-
* regardless. When
|
|
485
|
+
* regardless. When the Publication is unreachable or invalid, the gate
|
|
485
486
|
* degrades to a warning and the kernel-confirmed origin is re-checked after
|
|
486
487
|
* install (see `format` above).
|
|
487
488
|
*
|
|
@@ -496,7 +497,8 @@ async function ensureIdentityOverrideConsent(
|
|
|
496
497
|
const origin = await probeDeclaredOrigin(url)
|
|
497
498
|
if (origin === undefined) {
|
|
498
499
|
log.warn(
|
|
499
|
-
`Could not read a declared origin from
|
|
500
|
+
`Could not read a declared origin from ` +
|
|
501
|
+
`${new URL('/.well-known/astrale/domain.json', url).href} — ` +
|
|
500
502
|
`skipping the pre-install identity check (the installed origin is verified after install).`,
|
|
501
503
|
)
|
|
502
504
|
return undefined
|
|
@@ -534,19 +536,13 @@ async function ensureIdentityOverrideConsent(
|
|
|
534
536
|
return origin
|
|
535
537
|
}
|
|
536
538
|
|
|
537
|
-
/** Best-effort read of the worker's
|
|
539
|
+
/** Best-effort read of the worker's origin from its admitted canonical Publication. */
|
|
538
540
|
export async function probeDeclaredOrigin(url: string): Promise<string | undefined> {
|
|
539
541
|
try {
|
|
540
|
-
|
|
541
|
-
if (!res.ok) return undefined
|
|
542
|
-
// SDK workers serve `origin`; `domainName` is the pre-Kernel-V2 name, kept
|
|
543
|
-
// as a fallback for workers deployed before the rename.
|
|
544
|
-
const body = (await res.json()) as { origin?: unknown; domainName?: unknown }
|
|
545
|
-
const declared = body.origin ?? body.domainName
|
|
546
|
-
return typeof declared === 'string' && declared.length > 0 ? declared : undefined
|
|
542
|
+
return (await fetchDomainPublication(url, AbortSignal.timeout(10_000))).origin
|
|
547
543
|
} catch {
|
|
548
|
-
//
|
|
549
|
-
// itself will surface a dead worker with its own error.
|
|
544
|
+
// An unreachable or invalid Publication is not fatal here: the caller warns
|
|
545
|
+
// and the install itself will surface a dead worker with its own error.
|
|
550
546
|
return undefined
|
|
551
547
|
}
|
|
552
548
|
}
|