@astrale-os/cli 1.0.0-beta.2 → 1.0.0-beta.20
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 +16 -1
- package/dist/astrale.js +51105 -58057
- package/dist/public/connect-core.js +2085 -30702
- package/dist/public/keys/index.js +884 -29476
- package/dist/public/paths/index.js +13101 -41452
- package/dist/types/admin/contract.d.ts +26 -0
- package/dist/types/admin/instance/client.d.ts +20 -0
- package/dist/types/admin/instance/index.d.ts +2 -0
- package/dist/types/admin/instance/model.d.ts +6 -10
- 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 +25 -173
- 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/output.d.ts +1 -1
- 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 +109 -0
- package/dist/types/lib/validation.d.ts +4 -1
- package/package.json +15 -20
- package/src/__tests__/fixtures/publication.ts +20 -0
- package/src/admin/.spec/architecture.md +12 -5
- package/src/admin/__tests__/fixture.ts +25 -0
- package/src/admin/catalog/.spec/api.d.ts +1 -3
- package/src/admin/catalog/.spec/architecture.md +5 -4
- package/src/admin/catalog/__tests__/client.test.ts +143 -69
- package/src/admin/catalog/client.ts +40 -54
- package/src/admin/catalog/model.ts +3 -4
- package/src/admin/contract.ts +46 -0
- 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 +5 -9
- package/src/admin/instance/.spec/architecture.md +7 -7
- package/src/admin/instance/__tests__/client.test.ts +157 -175
- package/src/admin/instance/client.ts +39 -128
- package/src/admin/instance/index.ts +1 -2
- package/src/admin/instance/model.ts +12 -18
- package/src/commands/__tests__/admin-instance.test.ts +11 -8
- package/src/commands/__tests__/call-describe.test.ts +46 -33
- package/src/commands/__tests__/call.test.ts +34 -0
- package/src/commands/__tests__/domain-install-operation.test.ts +7 -22
- package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
- package/src/commands/__tests__/domain-list.test.ts +5 -25
- package/src/commands/__tests__/install-direct.test.ts +115 -8
- package/src/commands/__tests__/install-identity-override.test.ts +3 -14
- 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__/introspect-parse.test.ts +80 -1
- package/src/commands/__tests__/logs.test.ts +250 -2
- package/src/commands/__tests__/read-commands.test.ts +62 -10
- package/src/commands/__tests__/token-ttl.test.ts +49 -4
- package/src/commands/__tests__/update.test.ts +25 -3
- package/src/commands/__tests__/view.test.ts +49 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +71 -59
- package/src/commands/domain/install.ts +54 -35
- package/src/commands/domain/list.ts +1 -1
- 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 +48 -35
- package/src/commands/logs.ts +123 -33
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +35 -32
- package/src/commands/token.ts +42 -27
- package/src/commands/ui/__tests__/commands.test.ts +152 -0
- package/src/commands/ui/add.ts +51 -0
- package/src/commands/ui/doctor.ts +13 -0
- package/src/commands/ui/init.ts +38 -0
- package/src/commands/ui/list.ts +26 -0
- package/src/commands/ui/preset-apply.ts +19 -0
- package/src/commands/ui/preset-list.ts +12 -0
- package/src/commands/ui/shared.ts +25 -0
- package/src/commands/update.ts +31 -11
- package/src/commands/view.ts +82 -37
- 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 +27 -20
- package/src/connection/__tests__/exchange.test.ts +30 -1
- 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 +47 -23
- 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__/binary.test.ts +16 -1
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/domain-publication.test.ts +10 -16
- package/src/lib/__tests__/idp.test.ts +34 -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__/log-errors.test.ts +64 -0
- package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
- package/src/lib/__tests__/update.test.ts +264 -11
- package/src/lib/__tests__/view-assets.test.ts +14 -0
- package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
- package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
- package/src/lib/__tests__/view-open-intent.test.ts +0 -2
- package/src/lib/__tests__/view-server.test.ts +26 -3
- package/src/lib/__tests__/view-session.test.ts +2 -1
- package/src/lib/admin-instance.ts +14 -8
- package/src/lib/admin-target.ts +21 -2
- package/src/lib/binary.ts +22 -5
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/domain-publication.ts +9 -7
- package/src/lib/failure-debug.ts +26 -0
- package/src/lib/idp.ts +34 -3
- package/src/lib/instance-candidates.ts +2 -2
- package/src/lib/instance-target.ts +3 -14
- package/src/lib/instance.ts +16 -11
- package/src/lib/log.ts +16 -9
- package/src/lib/output.ts +1 -1
- package/src/lib/proc.ts +7 -2
- package/src/lib/provision-instance.ts +34 -57
- package/src/lib/update.ts +298 -45
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/assets.ts +11 -2
- package/src/lib/view/external-open-intent.ts +41 -0
- package/src/lib/view/external-open-origins.ts +37 -0
- package/src/lib/view/host-capabilities.ts +17 -0
- package/src/lib/view/resolve.ts +1 -1
- package/src/lib/view/server.ts +18 -3
- package/src/lib/view/session.ts +2 -0
- package/src/program/.spec/api.d.ts +1 -0
- package/src/program/__tests__/program.test.ts +163 -6
- package/src/program/argv.ts +14 -0
- package/src/program/build.ts +26 -4
- package/src/program/command.ts +1 -0
- package/src/program/index.ts +1 -0
- package/src/program/registry.ts +2 -1
- package/src/setup/__tests__/instance-step.test.ts +27 -10
- package/src/setup/steps/instance.ts +22 -15
- package/src/telemetry/__tests__/recorder.test.ts +1 -0
- package/src/telemetry/recorder.ts +9 -1
- package/src/ui/.spec/api.d.ts +14 -0
- package/src/ui/.spec/architecture.md +10 -0
- package/src/ui/.spec/laws.ts +36 -0
- package/src/ui/.spec/layout.ts +16 -0
- package/src/ui/__tests__/ui.test.ts +981 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/lock.ts +87 -0
- package/src/ui/model.ts +83 -0
- package/src/ui/operations.ts +710 -0
- package/src/ui/project.ts +219 -0
- package/src/ui/release.ts +321 -0
- package/src/ui/runner.ts +18 -0
- 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 +7 -13
- 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/harness/claude/adapter.test.ts +15 -5
- package/studio/server/agent/harness/gateway/token.test.ts +1 -1
- package/studio/server/agent/harness/gateway/token.ts +3 -3
- 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 +3 -3
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/api/canvas.ts +0 -1
- package/studio/server/api-agent-loadout.test.ts +1 -1
- package/studio/server/api.test.ts +1 -1
- package/studio/server/cache.test.ts +23 -12
- package/studio/server/cache.ts +12 -15
- package/studio/server/domain.test.ts +50 -68
- package/studio/server/domain.ts +82 -63
- package/studio/server/handoff/copy.ts +1 -1
- package/studio/server/index.ts +1 -1
- package/studio/server/instances/probe.test.ts +1 -1
- package/studio/server/introspect/anatomy/views/routes.ts +46 -37
- package/studio/server/introspect/anatomy/views.ts +9 -14
- package/studio/server/introspect/anatomy-extras.test.ts +26 -42
- package/studio/server/introspect/anatomy.test.ts +22 -14
- package/studio/server/introspect/anatomy.ts +6 -9
- package/studio/server/introspect/bundle.ts +0 -3
- package/studio/server/introspect/canonical-schema.test.ts +94 -389
- package/studio/server/introspect/canonical-schema.ts +227 -457
- package/studio/server/introspect/core-extractor.ts +18 -127
- package/studio/server/introspect/core.ts +1 -2
- package/studio/server/introspect/diff.test.ts +11 -7
- package/studio/server/introspect/diff.ts +18 -55
- package/studio/server/introspect/extractor.ts +22 -117
- package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
- package/studio/server/introspect/overlay.test.ts +26 -54
- package/studio/server/introspect/overlay.ts +10 -37
- package/studio/server/introspect/revision.test.ts +1 -1
- package/studio/server/introspect/revision.ts +1 -1
- package/studio/server/introspect/runtime.test.ts +65 -183
- package/studio/server/introspect/runtime.ts +8 -25
- package/studio/server/introspect/schema-ir-json.test.ts +70 -0
- package/studio/server/introspect/schema-ir-json.ts +38 -68
- 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/handlers.ts +116 -636
- package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
- package/studio/server/introspect/source-overlay/spans.ts +15 -45
- package/studio/server/state/baseline.test.ts +14 -9
- package/studio/server/state/baseline.ts +3 -5
- package/studio/server/state/visibility.ts +1 -5
- package/studio/server/views/model.ts +3 -3
- package/studio/server/views/target.test.ts +17 -74
- package/studio/server/views/target.ts +24 -50
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +8 -4
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/schema.ts +38 -107
- package/studio/shared/contracts/surface.test.ts +13 -15
- package/studio/shared/contracts/workspace.ts +2 -11
- package/studio/shared/schema/identity.test.ts +20 -44
- package/studio/shared/schema/identity.ts +9 -23
- package/viewer/dist/main.js +30 -51
- package/.check-workspace.cjs +0 -40
- package/src/admin/binding.ts +0 -168
- package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
- package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
- package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
- package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
- package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
- package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
- package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
|
@@ -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,44 @@ describe('query command', () => {
|
|
|
193
200
|
])
|
|
194
201
|
})
|
|
195
202
|
|
|
203
|
+
test('retains the opaque continuation in machine output without wrapping the graph result', async () => {
|
|
204
|
+
const { queryCommand } = await import('../query')
|
|
205
|
+
const result = {
|
|
206
|
+
kind: 'graph' as const,
|
|
207
|
+
graph: {
|
|
208
|
+
nodes: [{ id: 'issue-1', class: 'issues.astrale.ai:class.Issue', props: {} }],
|
|
209
|
+
edges: [],
|
|
210
|
+
},
|
|
211
|
+
selection: { kind: 'node' as const, ids: ['issue-1'] },
|
|
212
|
+
}
|
|
213
|
+
queryResult = {
|
|
214
|
+
result,
|
|
215
|
+
page: { next: 'opaque-next-page' },
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
await queryCommand([], {
|
|
219
|
+
json: true,
|
|
220
|
+
definition: '/:issues.astrale.ai:class.Issue',
|
|
221
|
+
limit: '1',
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
expect(JSON.parse(stdout)).toEqual({
|
|
225
|
+
...result,
|
|
226
|
+
page: { next: 'opaque-next-page' },
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
test('resolves @self inside the command session and never relabels transport failure as input', async () => {
|
|
231
|
+
const { queryCommand } = await import('../query')
|
|
232
|
+
await queryCommand(['@self'], { json: true, limit: '2' })
|
|
233
|
+
expect(selfContexts).toEqual([commandContext])
|
|
234
|
+
|
|
235
|
+
const aggregate = new AggregateError([], '')
|
|
236
|
+
selfFailure = aggregate
|
|
237
|
+
await expect(queryCommand(['@self'], { json: true, limit: '2' })).rejects.toBe(aggregate)
|
|
238
|
+
expect(errors.join('\n')).not.toContain('INVALID_INPUT')
|
|
239
|
+
})
|
|
240
|
+
|
|
196
241
|
/** @evidence TEST-CLI-QUERY-REJECTS-V1-BEFORE-CONNECTION */
|
|
197
242
|
test('rejects a legacy AST before opening the command connection', async () => {
|
|
198
243
|
const { queryCommand } = await import('../query')
|
|
@@ -215,7 +260,7 @@ describe('get command', () => {
|
|
|
215
260
|
const { getCommand } = await import('../get')
|
|
216
261
|
getResult = {
|
|
217
262
|
id: 'note-1',
|
|
218
|
-
class: '
|
|
263
|
+
class: 'notes.example.dev:class.Note',
|
|
219
264
|
props: { 'notes.example.dev:class.Note.property.title': 'Hello' },
|
|
220
265
|
}
|
|
221
266
|
|
|
@@ -226,6 +271,13 @@ describe('get command', () => {
|
|
|
226
271
|
expect(stdout).not.toContain('__labels')
|
|
227
272
|
expect(stdout).not.toContain('classId')
|
|
228
273
|
})
|
|
274
|
+
|
|
275
|
+
test('resolves @self through the same point-read command context', async () => {
|
|
276
|
+
const { getCommand } = await import('../get')
|
|
277
|
+
await getCommand('@self', { json: true })
|
|
278
|
+
expect(selfContexts).toEqual([commandContext])
|
|
279
|
+
expect(getTargets).toEqual(['@expanded-self'])
|
|
280
|
+
})
|
|
229
281
|
})
|
|
230
282
|
|
|
231
283
|
describe('mutate command', () => {
|
|
@@ -242,7 +294,7 @@ describe('mutate command', () => {
|
|
|
242
294
|
{
|
|
243
295
|
op: 'node.create',
|
|
244
296
|
as: 'note',
|
|
245
|
-
class: '
|
|
297
|
+
class: 'notes.example.dev:class.Note',
|
|
246
298
|
props: {},
|
|
247
299
|
},
|
|
248
300
|
],
|
|
@@ -258,7 +310,7 @@ describe('mutate command', () => {
|
|
|
258
310
|
{
|
|
259
311
|
op: 'node.create',
|
|
260
312
|
as: 'note',
|
|
261
|
-
class: '
|
|
313
|
+
class: 'notes.example.dev:class.Note',
|
|
262
314
|
props: {},
|
|
263
315
|
},
|
|
264
316
|
],
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AuthApi, Identity, IssuerId, MintedCredential } from '@astrale-os/sdk/auth'
|
|
2
|
+
|
|
3
|
+
import { describe, expect, mock, test } from 'bun:test'
|
|
2
4
|
|
|
3
5
|
import { AstraleError } from '../../errors'
|
|
4
|
-
import { parseTtl } from '../token'
|
|
6
|
+
import { issueToken, parseTtl } from '../token'
|
|
5
7
|
|
|
6
8
|
describe('parseTtl', () => {
|
|
7
|
-
test('defaults
|
|
8
|
-
expect(parseTtl(undefined)).toBe(
|
|
9
|
+
test('defaults below the five-minute local source-credential lifetime', () => {
|
|
10
|
+
expect(parseTtl(undefined)).toBe(240)
|
|
9
11
|
})
|
|
10
12
|
|
|
11
13
|
test('rejects non-positive and non-integer values', () => {
|
|
@@ -19,3 +21,46 @@ describe('parseTtl', () => {
|
|
|
19
21
|
expect(parseTtl('90')).toBe(90)
|
|
20
22
|
})
|
|
21
23
|
})
|
|
24
|
+
|
|
25
|
+
describe('issueToken', () => {
|
|
26
|
+
const kernel = 'https://kernel.test' as IssuerId
|
|
27
|
+
const identity = {
|
|
28
|
+
id: 'caller' as Identity['id'],
|
|
29
|
+
issuer: 'https://issuer.test' as Identity['issuer'],
|
|
30
|
+
subject: 'caller' as Identity['subject'],
|
|
31
|
+
frozen: false,
|
|
32
|
+
requiredClaims: [],
|
|
33
|
+
} satisfies Identity
|
|
34
|
+
|
|
35
|
+
test('mints a reusable top-level credential for the Kernel audience', async () => {
|
|
36
|
+
const mint = mock(async () => 'kernel-token' as MintedCredential)
|
|
37
|
+
const delegate = mock(async () => 'delegated-token' as MintedCredential)
|
|
38
|
+
const whoami = mock(async () => identity)
|
|
39
|
+
const auth = { mint, delegate, whoami } satisfies Pick<AuthApi, 'delegate' | 'mint' | 'whoami'>
|
|
40
|
+
|
|
41
|
+
await expect(issueToken(auth, kernel, kernel, 90)).resolves.toBe(
|
|
42
|
+
'kernel-token' as MintedCredential,
|
|
43
|
+
)
|
|
44
|
+
expect(mint).toHaveBeenCalledWith({ ttlSeconds: 90 })
|
|
45
|
+
expect(whoami).not.toHaveBeenCalled()
|
|
46
|
+
expect(delegate).not.toHaveBeenCalled()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('delegates the selected identity only for an external audience', async () => {
|
|
50
|
+
const mint = mock(async () => 'kernel-token' as MintedCredential)
|
|
51
|
+
const delegate = mock(async () => 'delegated-token' as MintedCredential)
|
|
52
|
+
const whoami = mock(async () => identity)
|
|
53
|
+
const auth = { mint, delegate, whoami } satisfies Pick<AuthApi, 'delegate' | 'mint' | 'whoami'>
|
|
54
|
+
|
|
55
|
+
await expect(issueToken(auth, kernel, 'https://service.test' as IssuerId, 120)).resolves.toBe(
|
|
56
|
+
'delegated-token' as MintedCredential,
|
|
57
|
+
)
|
|
58
|
+
expect(mint).not.toHaveBeenCalled()
|
|
59
|
+
expect(whoami).toHaveBeenCalledTimes(1)
|
|
60
|
+
expect(delegate).toHaveBeenCalledWith(identity.id, {
|
|
61
|
+
audience: 'https://service.test',
|
|
62
|
+
ttlSeconds: 120,
|
|
63
|
+
attenuation: { kind: 'identity', self: true },
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
})
|
|
@@ -35,9 +35,11 @@ describe('CLI update staleness', () => {
|
|
|
35
35
|
const result = await cliStale(
|
|
36
36
|
{ channel: 'canary' },
|
|
37
37
|
{
|
|
38
|
-
update: async () => {
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
update: async () => ({
|
|
39
|
+
status: 'managed',
|
|
40
|
+
currentVersion: '1.0.0-beta.0',
|
|
41
|
+
executable: '/opt/homebrew/bin/node',
|
|
42
|
+
}),
|
|
41
43
|
fetchPackageVersion: async ({ channel }) => {
|
|
42
44
|
expect(channel).toBe('canary')
|
|
43
45
|
return '1.0.0-canary.0'
|
|
@@ -52,6 +54,26 @@ describe('CLI update staleness', () => {
|
|
|
52
54
|
})
|
|
53
55
|
})
|
|
54
56
|
|
|
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
|
+
|
|
55
77
|
test('maps the default to beta and the stable channel to npm latest', async () => {
|
|
56
78
|
const originalFetch = globalThis.fetch
|
|
57
79
|
const fetchMock = mock(async (input: RequestInfo | URL) => {
|
|
@@ -147,3 +147,52 @@ describe('view capture timing', () => {
|
|
|
147
147
|
expect(commands.length).toBeGreaterThan(2)
|
|
148
148
|
})
|
|
149
149
|
})
|
|
150
|
+
|
|
151
|
+
describe('view session runtime', () => {
|
|
152
|
+
test('builds one serve config with the admitted operator origin grant', async () => {
|
|
153
|
+
const { createViewServeConfig } = await import('../view')
|
|
154
|
+
const record = {
|
|
155
|
+
id: 'v-proof',
|
|
156
|
+
pid: 0,
|
|
157
|
+
port: 4419,
|
|
158
|
+
nonce: 'proof',
|
|
159
|
+
pageUrl: 'http://127.0.0.1:4419/s/proof/',
|
|
160
|
+
view: { target: Path.parse('/:ai-gateway.astrale.ai').raw, route: resolved[0] },
|
|
161
|
+
createdAt: '2026-08-26T00:00:00.000Z',
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const config = createViewServeConfig(
|
|
165
|
+
record,
|
|
166
|
+
{ allowExternalOrigin: ['https://connect.nango.dev/'] },
|
|
167
|
+
{ url: 'https://kernel.test', kernelIssuer: 'https://kernel.test' },
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
expect(config.session).toBe(record)
|
|
171
|
+
expect(config.externalOrigins).toEqual(['https://connect.nango.dev'])
|
|
172
|
+
expect(config.proxy).toEqual({
|
|
173
|
+
kernelUrl: 'https://kernel.test',
|
|
174
|
+
issuer: 'https://kernel.test',
|
|
175
|
+
caFile: undefined,
|
|
176
|
+
direct: true,
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test('spawns the detached server from a compiled executable without its virtual Bun entry', async () => {
|
|
181
|
+
const { resolveServeRuntime, viewServeInvocation } = await import('../view')
|
|
182
|
+
|
|
183
|
+
const runtime = await resolveServeRuntime({
|
|
184
|
+
executable: '/opt/astrale/bin/astrale',
|
|
185
|
+
entry: '/$bunfs/root/astrale',
|
|
186
|
+
exists: () => true,
|
|
187
|
+
find: async () => '/usr/bin/node',
|
|
188
|
+
})
|
|
189
|
+
expect(runtime).toEqual({
|
|
190
|
+
file: '/opt/astrale/bin/astrale',
|
|
191
|
+
args: [],
|
|
192
|
+
})
|
|
193
|
+
expect(viewServeInvocation(runtime, '/tmp/view.config.json')).toEqual({
|
|
194
|
+
file: '/opt/astrale/bin/astrale',
|
|
195
|
+
args: ['__view-serve', '--config', '/tmp/view.config.json'],
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
})
|
|
@@ -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
|
-
}
|