@astrale-os/cli 1.0.0-beta.3 → 1.0.0-beta.31
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 +84 -6
- package/dist/astrale.js +57586 -60753
- package/dist/public/connect-core.js +2158 -30706
- package/dist/public/keys/index.js +1016 -29541
- package/dist/public/paths/index.js +370 -28654
- 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 +13 -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/dist/types/state/exchange-credentials.d.ts +6 -2
- package/dist/types/state/files.d.ts +2 -0
- package/dist/types/state/index.d.ts +3 -2
- package/dist/types/state/paths.d.ts +2 -0
- package/dist/types/state/session-routes.d.ts +10 -0
- package/package.json +16 -19
- 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 +47 -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 +30 -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 +6 -17
- 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 +119 -11
- 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 +50 -2
- package/src/commands/auth/logout.ts +2 -1
- package/src/commands/browser.ts +29 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +71 -59
- package/src/commands/domain/install.ts +73 -46
- 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 +15 -16
- package/src/commands/identity/__tests__/register.test.ts +3 -3
- package/src/commands/identity/register.ts +8 -8
- 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 +53 -41
- package/src/commands/logs.ts +123 -36
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +39 -36
- package/src/commands/session/analyze.ts +26 -4
- 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 +21 -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 +90 -48
- package/src/commands/view.ts +82 -37
- package/src/connection/.spec/api.d.ts +7 -8
- package/src/connection/.spec/architecture.md +14 -5
- 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__/auth.test.ts +1 -0
- package/src/connection/__tests__/credential.test.ts +21 -1
- package/src/connection/__tests__/errors.test.ts +27 -20
- package/src/connection/__tests__/exchange.test.ts +64 -11
- 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 +8 -4
- package/src/connection/__tests__/target.test.ts +2 -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 +110 -61
- 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 +27 -9
- package/src/errors.ts +3 -2
- package/src/graph/.spec/api.d.ts +13 -6
- package/src/graph/.spec/laws/documents.ts +7 -7
- package/src/graph/.spec/layout.ts +9 -1
- package/src/graph/__tests__/mutation.test.ts +2 -2
- package/src/graph/__tests__/query.test.ts +35 -9
- package/src/graph/class.ts +30 -0
- package/src/graph/index.ts +1 -0
- package/src/graph/query.ts +27 -40
- package/src/identity/.spec/api.d.ts +2 -2
- package/src/identity/__tests__/fixtures/registry-journey.ts +13 -1
- package/src/identity/__tests__/registration.test.ts +1 -1
- package/src/identity/__tests__/registry.test.ts +4 -0
- package/src/identity/registration.ts +3 -3
- package/src/identity/registry.ts +2 -0
- 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__/browser-retention.test.ts +212 -0
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/config.test.ts +27 -0
- 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__/skills.test.ts +834 -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 +25 -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/browser-retention.ts +210 -0
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/config.ts +12 -1
- 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/skills/lock.ts +117 -0
- package/src/lib/skills/sync.ts +878 -0
- package/src/lib/skills.ts +1 -52
- 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 +201 -7
- 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/setup/steps/skills.ts +40 -38
- package/src/state/.spec/api.d.ts +21 -2
- package/src/state/.spec/architecture.md +18 -4
- package/src/state/.spec/layout.ts +1 -0
- package/src/state/__tests__/exchange-credentials.test.ts +88 -42
- package/src/state/__tests__/files.test.ts +24 -1
- package/src/state/__tests__/fixtures/session-route-process.ts +93 -0
- package/src/state/__tests__/paths.test.ts +1 -0
- package/src/state/__tests__/session-routes.test.ts +138 -0
- package/src/state/exchange-credentials.ts +22 -9
- package/src/state/files.ts +41 -0
- package/src/state/index.ts +3 -1
- package/src/state/paths.ts +3 -0
- package/src/state/session-routes.ts +34 -0
- package/src/telemetry/__tests__/analyze-log.test.ts +38 -0
- package/src/telemetry/__tests__/recorder.test.ts +1 -0
- package/src/telemetry/__tests__/retention.test.ts +267 -0
- package/src/telemetry/__tests__/settings.test.ts +102 -0
- package/src/telemetry/__tests__/store-scan.test.ts +128 -0
- package/src/telemetry/__tests__/trigger.test.ts +33 -4
- package/src/telemetry/analyze.ts +24 -2
- package/src/telemetry/recorder.ts +15 -4
- package/src/telemetry/retention.ts +176 -0
- package/src/telemetry/session.ts +18 -12
- package/src/telemetry/settings.ts +64 -11
- package/src/telemetry/store.ts +92 -9
- package/src/telemetry/trigger.ts +16 -28
- package/src/ui/.spec/api.d.ts +14 -0
- package/src/ui/.spec/architecture.md +14 -0
- package/src/ui/.spec/laws.ts +50 -0
- package/src/ui/.spec/layout.ts +16 -0
- package/src/ui/__tests__/ui.test.ts +1742 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/lock.ts +89 -0
- package/src/ui/model.ts +83 -0
- package/src/ui/operations.ts +1044 -0
- package/src/ui/project.ts +333 -0
- package/src/ui/release.ts +398 -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-BFVFs_8x.js +81 -0
- package/studio/client/dist/assets/index-DuB9iUdu.css +2 -0
- package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
- package/studio/client/dist/assets/schema-studio--a0kX3QU.css +1 -0
- package/studio/client/dist/assets/schema-studio-DH6oEWME.js +8 -0
- package/studio/client/dist/index.html +4 -3
- package/studio/package.json +8 -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/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 +41 -32
- package/studio/server/agent/prompts/anchors.ts +23 -51
- package/studio/server/agent/prompts/system.test.ts +4 -4
- package/studio/server/agent/prompts/system.ts +5 -5
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/agent/run/preparation.ts +1 -1
- package/studio/server/api/canvas.ts +0 -1
- package/studio/server/api/context.ts +1 -1
- package/studio/server/api/deployment.ts +1 -1
- package/studio/server/api/views.ts +2 -2
- package/studio/server/api/workspace.ts +1 -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 +24 -18
- package/studio/server/cache.ts +10 -54
- package/studio/server/cli-consumers.test.ts +32 -4
- package/studio/server/cli.test.ts +2 -2
- package/studio/server/domain.test.ts +50 -68
- package/studio/server/domain.ts +82 -63
- package/studio/server/handoff/copy.ts +2 -2
- 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 +10 -16
- package/studio/server/introspect/anatomy-extras.test.ts +29 -56
- 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 +155 -424
- package/studio/server/introspect/canonical-schema.ts +248 -601
- package/studio/server/introspect/core.ts +14 -22
- package/studio/server/introspect/diff.test.ts +21 -9
- package/studio/server/introspect/diff.ts +19 -64
- package/studio/server/introspect/extractor.ts +23 -122
- package/studio/server/introspect/overlay-tsmorph.test.ts +116 -241
- package/studio/server/introspect/overlay-tsmorph.ts +0 -1
- package/studio/server/introspect/overlay.ts +3 -51
- package/studio/server/introspect/revision.test.ts +24 -28
- package/studio/server/introspect/revision.ts +11 -22
- package/studio/server/introspect/runtime.test.ts +77 -195
- package/studio/server/introspect/runtime.ts +5 -67
- package/studio/server/introspect/schema-ir-json.test.ts +81 -0
- package/studio/server/introspect/schema-ir-json.ts +39 -69
- 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/lifecycle.ts +4 -1
- package/studio/server/state/baseline.test.ts +14 -9
- package/studio/server/state/baseline.ts +3 -5
- package/studio/server/state/documents.test.ts +69 -0
- package/studio/server/state/documents.ts +57 -10
- package/studio/server/state/visibility.ts +1 -5
- package/studio/server/views/model.ts +3 -3
- package/studio/server/views/target.test.ts +18 -76
- package/studio/server/views/target.ts +25 -51
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +8 -4
- package/studio/server/workspace/updates.ts +23 -1
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/runtime.ts +4 -0
- package/studio/shared/contracts/schema.ts +44 -131
- package/studio/shared/contracts/surface.test.ts +15 -17
- package/studio/shared/contracts/workspace.ts +5 -11
- package/studio/shared/schema/identity.test.ts +20 -44
- package/studio/shared/schema/identity.ts +9 -24
- package/studio/tsconfig.json +2 -1
- package/tsconfig.json +1 -1
- package/viewer/dist/index.html +21 -0
- package/viewer/dist/main.js +41 -62
- 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
- package/studio/server/introspect/core-extractor.ts +0 -139
- package/studio/server/introspect/overlay.test.ts +0 -72
- package/studio/server/introspect/source-overlay/annotations.ts +0 -14
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { formatKernelError } from '../errors'
|
|
4
|
+
import { legacyTransportFailure, sessionFailure, transportFailure } from './failure-fixtures'
|
|
5
|
+
|
|
6
|
+
/** @evidence TEST-CLI-CONNECTION-FAILURE-SAFETY */
|
|
7
|
+
test('unknown and empty native failures never become ordinary diagnostics', async () => {
|
|
8
|
+
for (const failure of [new Error('private provider detail'), new Error(''), { secret: true }]) {
|
|
9
|
+
const output = await capture(() => formatKernelError(failure, true))
|
|
10
|
+
expect(JSON.parse(output)).toEqual({
|
|
11
|
+
error: 'UNEXPECTED_ERROR',
|
|
12
|
+
message: 'The CLI encountered an unexpected internal failure.',
|
|
13
|
+
})
|
|
14
|
+
expect(output).not.toContain('private provider detail')
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('Aggregate children are available only under explicit debug output', async () => {
|
|
19
|
+
const failure = new AggregateError([new Error('first private'), new Error('second private')], '')
|
|
20
|
+
const ordinary = await capture(() => formatKernelError(failure, true))
|
|
21
|
+
expect(ordinary).not.toContain('first private')
|
|
22
|
+
expect(ordinary).not.toContain('second private')
|
|
23
|
+
|
|
24
|
+
const debug = await capture(() => formatKernelError(failure, true, '', true))
|
|
25
|
+
expect(debug).toContain('aggregate: Error: first private')
|
|
26
|
+
expect(debug).toContain('aggregate: Error: second private')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('Session and acquisition failures remain typed without unsafe recovery advice', async () => {
|
|
30
|
+
const session = await capture(() =>
|
|
31
|
+
formatKernelError(sessionFailure('Session operation timed out.', 'timeout'), true),
|
|
32
|
+
)
|
|
33
|
+
expect(JSON.parse(session)).toEqual({ error: 'TIMEOUT', message: 'Session operation timed out.' })
|
|
34
|
+
|
|
35
|
+
const acquisition = await capture(() =>
|
|
36
|
+
formatKernelError(
|
|
37
|
+
transportFailure('Publication failed.', 'unknown', {
|
|
38
|
+
kind: 'acquisition',
|
|
39
|
+
resource: 'publication',
|
|
40
|
+
}),
|
|
41
|
+
true,
|
|
42
|
+
'',
|
|
43
|
+
false,
|
|
44
|
+
{ recovery: { operation: 'must-not-leak', retry: 'must-not-leak' } },
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
expect(JSON.parse(acquisition)).toMatchObject({
|
|
48
|
+
error: 'TRANSPORT_ERROR',
|
|
49
|
+
transport: { kind: 'acquisition', resource: 'publication' },
|
|
50
|
+
})
|
|
51
|
+
expect(acquisition).not.toContain('must-not-leak')
|
|
52
|
+
|
|
53
|
+
const legacy = await capture(() =>
|
|
54
|
+
formatKernelError(legacyTransportFailure('Legacy send failed.', 'send', 'not-sent'), true),
|
|
55
|
+
)
|
|
56
|
+
expect(JSON.parse(legacy)).toMatchObject({
|
|
57
|
+
error: 'TRANSPORT_ERROR',
|
|
58
|
+
transport: { kind: 'invocation', delivery: 'not-sent' },
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
async function capture(action: () => Promise<void>): Promise<string> {
|
|
63
|
+
const writes: string[] = []
|
|
64
|
+
const original = process.stderr.write
|
|
65
|
+
process.stderr.write = ((chunk: string | Uint8Array) => {
|
|
66
|
+
writes.push(typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk))
|
|
67
|
+
return true
|
|
68
|
+
}) as typeof process.stderr.write
|
|
69
|
+
try {
|
|
70
|
+
await action()
|
|
71
|
+
return writes.join('')
|
|
72
|
+
} finally {
|
|
73
|
+
process.stderr.write = original
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -2,7 +2,11 @@ import { describe, expect, test } from 'bun:test'
|
|
|
2
2
|
import { SignJWT } from 'jose'
|
|
3
3
|
import { webcrypto } from 'node:crypto'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import type { ConnectionContext } from '../session'
|
|
6
|
+
|
|
7
|
+
import { expandSelfInCall, resolveSelfIdAuthenticated } from '../self'
|
|
8
|
+
|
|
9
|
+
const context = Object.freeze({}) as ConnectionContext
|
|
6
10
|
|
|
7
11
|
describe('resolveSelfIdAuthenticated', () => {
|
|
8
12
|
/** @evidence TEST-CLI-SELF-USES-AUTHENTICATED-EFFECTIVE-PRINCIPAL */
|
|
@@ -14,11 +18,9 @@ describe('resolveSelfIdAuthenticated', () => {
|
|
|
14
18
|
.setSubject('manager-principal')
|
|
15
19
|
.setAudience('https://child.example')
|
|
16
20
|
.sign(key)
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const resolved = await resolveSelfIdAuthenticated(opts, {
|
|
21
|
+
const resolved = await resolveSelfIdAuthenticated(context, {
|
|
20
22
|
whoami: async (received) => {
|
|
21
|
-
expect(received).toBe(
|
|
23
|
+
expect(received).toBe(context)
|
|
22
24
|
return { id: 'child-kernel-principal', slug: 'child' }
|
|
23
25
|
},
|
|
24
26
|
})
|
|
@@ -28,17 +30,35 @@ describe('resolveSelfIdAuthenticated', () => {
|
|
|
28
30
|
|
|
29
31
|
test('resolves an imported root without requiring a local registration', async () => {
|
|
30
32
|
await expect(
|
|
31
|
-
resolveSelfIdAuthenticated(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
),
|
|
33
|
+
resolveSelfIdAuthenticated(context, {
|
|
34
|
+
whoami: async () => ({ id: 'manager-kernel-principal' }),
|
|
35
|
+
}),
|
|
35
36
|
).resolves.toEqual({ id: 'manager-kernel-principal' })
|
|
36
37
|
})
|
|
37
38
|
|
|
38
39
|
test('fails closed when authenticated whoami returns no NodeId', async () => {
|
|
39
40
|
await expect(
|
|
40
|
-
resolveSelfIdAuthenticated(
|
|
41
|
-
).rejects.toMatchObject({
|
|
41
|
+
resolveSelfIdAuthenticated(context, { whoami: async () => ({ id: ' ' }) }),
|
|
42
|
+
).rejects.toMatchObject({ code: 'SELF_RESOLUTION_FAILED' })
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
describe('expandSelfInCall', () => {
|
|
47
|
+
test('expands only top-level string values after local parameter admission', async () => {
|
|
48
|
+
const callContext = {
|
|
49
|
+
auth: { whoami: async () => ({ id: 'caller-id' }) },
|
|
50
|
+
target: {},
|
|
51
|
+
} as ConnectionContext
|
|
52
|
+
const expanded = await expandSelfInCall(
|
|
53
|
+
'/:people.example.dev:class.Person:get',
|
|
54
|
+
{ owner: '@self', count: 2, nested: { owner: '@self' } },
|
|
55
|
+
callContext,
|
|
56
|
+
)
|
|
57
|
+
expect(expanded.parameters).toEqual({
|
|
58
|
+
owner: '@caller-id',
|
|
59
|
+
count: 2,
|
|
60
|
+
nested: { owner: '@self' },
|
|
61
|
+
})
|
|
42
62
|
})
|
|
43
63
|
})
|
|
44
64
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { AuthApi } from '@astrale-os/
|
|
2
|
-
import type { GraphApi } from '@astrale-os/
|
|
3
|
-
import type { ClientSession } from '@astrale-os/
|
|
1
|
+
import type { AuthApi } from '@astrale-os/sdk/auth'
|
|
2
|
+
import type { GraphApi } from '@astrale-os/sdk/client'
|
|
3
|
+
import type { ClientSession } from '@astrale-os/sdk/client/session'
|
|
4
|
+
import type { SessionRouteStore } from '@astrale-os/sdk/client/session'
|
|
4
5
|
|
|
5
6
|
import { issuer } from '@astrale-os/sdk/auth'
|
|
6
7
|
import { describe, expect, test } from 'bun:test'
|
|
@@ -23,6 +24,7 @@ const config: AstraleConfig = {
|
|
|
23
24
|
kernelIssuer: 'https://admin.example',
|
|
24
25
|
},
|
|
25
26
|
telemetry: { enabled: false },
|
|
27
|
+
browser: {},
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
const context: ConnectionContext = Object.freeze({
|
|
@@ -37,11 +39,13 @@ describe('connection session', () => {
|
|
|
37
39
|
/** @evidence TEST-CLI-CONNECTION-PINS-SOURCE-ISSUER */
|
|
38
40
|
test('pins the selected canonical Kernel issuer without a transport escape hatch', async () => {
|
|
39
41
|
const auth = { ttlSeconds: 3_600, resolve: async () => ({ credential: 'credential' }) }
|
|
40
|
-
const
|
|
42
|
+
const routeStore: SessionRouteStore = { read: () => undefined, write: () => undefined }
|
|
43
|
+
const options = createClientSessionOptions(target, globalThis.fetch, auth, 2_500, routeStore)
|
|
41
44
|
|
|
42
45
|
expect(options.kernel).toBe(target.kernelIssuer)
|
|
43
46
|
expect(options).not.toHaveProperty('url')
|
|
44
47
|
expect(options).not.toHaveProperty('sourceIssuer')
|
|
48
|
+
expect(options.routeStore).toBe(routeStore)
|
|
45
49
|
})
|
|
46
50
|
|
|
47
51
|
/** @evidence TEST-CLI-CONNECTION-OMITS-EXPLICIT-ANONYMOUS-CREDENTIAL */
|
|
@@ -15,6 +15,7 @@ const config: AstraleConfig = {
|
|
|
15
15
|
domainIssuer: 'https://admin-domain.example',
|
|
16
16
|
},
|
|
17
17
|
telemetry: { enabled: false },
|
|
18
|
+
browser: {},
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const instances: InstanceStore = {
|
|
@@ -68,6 +69,7 @@ describe('connection target', () => {
|
|
|
68
69
|
id: 'managed-id',
|
|
69
70
|
slug,
|
|
70
71
|
url: 'https://managed.example',
|
|
72
|
+
state: 'ready',
|
|
71
73
|
}),
|
|
72
74
|
}),
|
|
73
75
|
).toEqual({
|
package/src/connection/auth.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AstraleConfig } from '../lib/config'
|
|
2
|
+
import type { ConnectionOptions, ConnectionTarget } from './target'
|
|
2
3
|
|
|
3
4
|
import { AuthError } from '../errors'
|
|
4
5
|
import { getDefault, getIdentity, type Identity } from '../identity/registry'
|
|
@@ -21,6 +22,18 @@ export type KeyIdentityAuthOptions = {
|
|
|
21
22
|
audience: string
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
/** Pin the local identity label before a session performs any authenticated effect. */
|
|
26
|
+
export async function bindCredentialIdentity<Options extends ConnectionOptions>(
|
|
27
|
+
options: Options,
|
|
28
|
+
target: ConnectionTarget,
|
|
29
|
+
): Promise<Options> {
|
|
30
|
+
if (options.anonymous === true || options.creds !== undefined || options.as !== undefined) {
|
|
31
|
+
return options
|
|
32
|
+
}
|
|
33
|
+
const identity = target.defaultIdentity ?? (await getDefault()).name
|
|
34
|
+
return Object.freeze({ ...options, as: identity })
|
|
35
|
+
}
|
|
36
|
+
|
|
24
37
|
/**
|
|
25
38
|
* Resolve a signed JWT credential from CLI options.
|
|
26
39
|
*
|
package/src/connection/call.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Fetch } from '@astrale-os/
|
|
2
|
-
import type { SessionAuth } from '@astrale-os/
|
|
1
|
+
import type { Fetch } from '@astrale-os/sdk/client'
|
|
2
|
+
import type { SessionAuth } from '@astrale-os/sdk/client/session'
|
|
3
3
|
|
|
4
4
|
import { credential, type IssuerId } from '@astrale-os/sdk/auth'
|
|
5
5
|
|
package/src/connection/errors.ts
CHANGED
|
@@ -1,355 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import type { OperationRecovery } from './command'
|
|
4
|
-
|
|
5
|
-
import { AstraleError } from '../errors'
|
|
6
|
-
import { readLocalStatus, type LocalStatus } from '../lib/local-status'
|
|
7
|
-
import { log } from '../lib/log'
|
|
8
|
-
import {
|
|
9
|
-
functionInputIssues,
|
|
10
|
-
queryInputRepair,
|
|
11
|
-
reasonCode,
|
|
12
|
-
schemaUpgradeDetails,
|
|
13
|
-
schemaUpgradeHint,
|
|
14
|
-
type FunctionInputIssue,
|
|
15
|
-
type QueryInputRepair,
|
|
16
|
-
} from './reasons'
|
|
17
|
-
|
|
18
|
-
export { functionInputIssues, schemaUpgradeHint } from './reasons'
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Format and display a kernel client error.
|
|
22
|
-
*
|
|
23
|
-
* Handles CLI-local errors plus the current Kernel Client public error families.
|
|
24
|
-
*
|
|
25
|
-
* When `debug` is true, additional diagnostic information (class name, full
|
|
26
|
-
* error chain, attached url/details) is printed after the user-facing line.
|
|
27
|
-
*/
|
|
28
|
-
export async function formatKernelError(
|
|
29
|
-
error: unknown,
|
|
30
|
-
isRaw: boolean,
|
|
31
|
-
urlArg = '',
|
|
32
|
-
debug = false,
|
|
33
|
-
opts: { recovery?: OperationRecovery } = {},
|
|
34
|
-
): Promise<void> {
|
|
35
|
-
const url =
|
|
36
|
-
urlArg || (error instanceof Error ? ((error as Error & { url?: string }).url ?? '') : '')
|
|
37
|
-
const localContext = await contextForError(error)
|
|
38
|
-
// Handle AstraleError (AuthError, etc.) with structured hints
|
|
39
|
-
if (error instanceof AstraleError) {
|
|
40
|
-
if (isRaw) {
|
|
41
|
-
writeRaw({ error: error.code, message: error.message, hint: error.hint })
|
|
42
|
-
} else {
|
|
43
|
-
log.error(`${chalk.bold(error.code)}: ${error.message}`)
|
|
44
|
-
if (error.hint) log.dim(` ${error.hint}`)
|
|
45
|
-
}
|
|
46
|
-
if (debug) printDebug(error, url)
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (!(error instanceof Error)) {
|
|
51
|
-
if (isRaw) writeRaw({ error: 'UNKNOWN', message: String(error) })
|
|
52
|
-
else log.error(String(error))
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const name = error.name
|
|
57
|
-
|
|
58
|
-
switch (name) {
|
|
59
|
-
case 'TransportError':
|
|
60
|
-
presentTransportError(error, isRaw, url, localContext, opts.recovery)
|
|
61
|
-
break
|
|
62
|
-
|
|
63
|
-
case 'ResponseError': {
|
|
64
|
-
const code = (error as { readonly code?: unknown }).code
|
|
65
|
-
const reason = (error as { readonly reason?: unknown }).reason
|
|
66
|
-
const codeOfReason = reasonCode(reason)
|
|
67
|
-
const inputIssues = functionInputIssues(reason)
|
|
68
|
-
const queryRepair = queryInputRepair(reason)
|
|
69
|
-
const upgrade = schemaUpgradeDetails(reason)
|
|
70
|
-
const removalHint = schemaDataRemovalHint(reason)
|
|
71
|
-
const domainAddressNotPublic = codeOfReason === 'SCHEMA_DOMAIN_ADDRESS_NOT_PUBLIC'
|
|
72
|
-
const displayMessage = domainAddressNotPublic
|
|
73
|
-
? 'Expose the Domain through a public HTTPS URL or public tunnel, then retry.'
|
|
74
|
-
: removalHint !== undefined && !isRaw
|
|
75
|
-
? 'Existing business data still uses schema definitions being removed.'
|
|
76
|
-
: error.message
|
|
77
|
-
const hint =
|
|
78
|
-
codeOfReason === 'FUNCTION_INPUT_INVALID' && (isRaw || inputIssues.length === 0)
|
|
79
|
-
? 'Use `astrale introspect <path>` to see the callable input.'
|
|
80
|
-
: (removalHint ?? (upgrade === undefined ? undefined : schemaUpgradeHint(upgrade)))
|
|
81
|
-
if (isRaw) {
|
|
82
|
-
writeRaw({
|
|
83
|
-
error: 'RESPONSE_ERROR',
|
|
84
|
-
...(code === undefined ? {} : { code }),
|
|
85
|
-
message: displayMessage,
|
|
86
|
-
...(reason === undefined ? {} : { reason }),
|
|
87
|
-
...(hint === undefined ? {} : { hint }),
|
|
88
|
-
})
|
|
89
|
-
} else {
|
|
90
|
-
log.error(
|
|
91
|
-
domainAddressNotPublic
|
|
92
|
-
? `${chalk.bold('SCHEMA_DOMAIN_ADDRESS_NOT_PUBLIC')}: ${displayMessage}`
|
|
93
|
-
: removalHint !== undefined
|
|
94
|
-
? `${chalk.bold('DATA_MIGRATION_REQUIRED')}: ${displayMessage}`
|
|
95
|
-
: `${chalk.bold(code === undefined ? 'RESPONSE_ERROR' : `RESPONSE_ERROR(${String(code)})`)}: ${displayMessage}`,
|
|
96
|
-
)
|
|
97
|
-
if (codeOfReason !== undefined && !domainAddressNotPublic && removalHint === undefined) {
|
|
98
|
-
log.dim(` reason: ${codeOfReason}`)
|
|
99
|
-
}
|
|
100
|
-
presentFunctionInputIssues(inputIssues)
|
|
101
|
-
if (queryRepair !== undefined) presentQueryInputRepair(queryRepair)
|
|
102
|
-
if (upgrade?.issue === 'issuer-changed') {
|
|
103
|
-
log.dim(` installed issuer: ${upgrade.installedIssuer}`)
|
|
104
|
-
log.dim(` replacement issuer: ${upgrade.replacementIssuer}`)
|
|
105
|
-
}
|
|
106
|
-
if (hint !== undefined) log.dim(` ${hint}`)
|
|
107
|
-
}
|
|
108
|
-
break
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
default: {
|
|
112
|
-
const mapped = mapPublicError(error)
|
|
113
|
-
if (isRaw) {
|
|
114
|
-
writeRaw({
|
|
115
|
-
error: mapped.code,
|
|
116
|
-
message: mapped.message,
|
|
117
|
-
...(mapped.hint === undefined ? {} : { hint: mapped.hint }),
|
|
118
|
-
...(mapped.timeoutMs === undefined ? {} : { timeoutMs: mapped.timeoutMs }),
|
|
119
|
-
})
|
|
120
|
-
} else {
|
|
121
|
-
log.error(`${chalk.bold(mapped.code)}: ${mapped.message}`)
|
|
122
|
-
if (mapped.hint) log.dim(` ${mapped.hint}`)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (debug) printDebug(error, url)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function schemaDataRemovalHint(reason: unknown): string | undefined {
|
|
131
|
-
if (reason === null || typeof reason !== 'object') return undefined
|
|
132
|
-
const value = reason as { readonly code?: unknown; readonly details?: unknown }
|
|
133
|
-
if (value.code !== 'DATA_MIGRATION_REQUIRED') return undefined
|
|
134
|
-
if (value.details === null || typeof value.details !== 'object') return undefined
|
|
135
|
-
|
|
136
|
-
const requirements = (value.details as { readonly requirements?: unknown }).requirements
|
|
137
|
-
if (
|
|
138
|
-
!Array.isArray(requirements) ||
|
|
139
|
-
requirements.length === 0 ||
|
|
140
|
-
!requirements.every(
|
|
141
|
-
(requirement) =>
|
|
142
|
-
requirement !== null &&
|
|
143
|
-
typeof requirement === 'object' &&
|
|
144
|
-
(requirement as { readonly operation?: unknown }).operation === 'remove-facts' &&
|
|
145
|
-
(requirement as { readonly reason?: unknown }).reason === 'destructive-change',
|
|
146
|
-
)
|
|
147
|
-
) {
|
|
148
|
-
return undefined
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return 'Delete this data explicitly, then retry. No data was deleted.'
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function mapPublicError(error: Error): {
|
|
155
|
-
code: string
|
|
156
|
-
message: string
|
|
157
|
-
hint?: string
|
|
158
|
-
timeoutMs?: number
|
|
159
|
-
} {
|
|
160
|
-
const name = error.name
|
|
161
|
-
if (name === 'PathError') {
|
|
162
|
-
return { code: 'PATH_INVALID', message: error.message }
|
|
163
|
-
}
|
|
164
|
-
if (name === 'NodeUnavailableError') {
|
|
165
|
-
return {
|
|
166
|
-
code: 'NODE_UNAVAILABLE',
|
|
167
|
-
message: error.message,
|
|
168
|
-
hint: 'If this is a callable Path, use `astrale call` or `astrale introspect`.',
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (name === 'AuthValueError') {
|
|
172
|
-
return { code: 'AUTH_VALUE_INVALID', message: error.message }
|
|
173
|
-
}
|
|
174
|
-
if (name === 'ClientError' && /timed out/i.test(error.message)) {
|
|
175
|
-
const timeoutMs = (error as { timeoutMs?: number }).timeoutMs
|
|
176
|
-
return {
|
|
177
|
-
code: 'TIMEOUT',
|
|
178
|
-
message: error.message,
|
|
179
|
-
hint: 'Try increasing with --timeout',
|
|
180
|
-
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
if (name === 'ClientError' && /Publication discovery returned HTTP/i.test(error.message)) {
|
|
184
|
-
return {
|
|
185
|
-
code: 'KERNEL_DISCOVERY_FAILED',
|
|
186
|
-
message: error.message,
|
|
187
|
-
hint: 'Pass the Kernel issuer URL (no /invoke suffix), e.g. https://host/kernel/host',
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return { code: name && name !== 'Error' ? name : 'UNKNOWN', message: error.message }
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function presentTransportError(
|
|
194
|
-
error: Error,
|
|
195
|
-
isRaw: boolean,
|
|
196
|
-
url: string,
|
|
197
|
-
context: LocalStatus | undefined,
|
|
198
|
-
recovery: OperationRecovery | undefined,
|
|
199
|
-
): void {
|
|
200
|
-
const phase = transportPhase(error)
|
|
201
|
-
const delivery = transportDelivery(error)
|
|
202
|
-
const code =
|
|
203
|
-
phase === 'connect'
|
|
204
|
-
? 'CONNECTION_ERROR'
|
|
205
|
-
: phase === 'timeout'
|
|
206
|
-
? 'TIMEOUT'
|
|
207
|
-
: phase === 'closed'
|
|
208
|
-
? 'DISCONNECTED'
|
|
209
|
-
: 'TRANSPORT_ERROR'
|
|
210
|
-
if (isRaw) {
|
|
211
|
-
writeRaw({
|
|
212
|
-
error: code,
|
|
213
|
-
message: error.message,
|
|
214
|
-
...(url === '' ? {} : { url }),
|
|
215
|
-
...(phase === undefined ? {} : { phase }),
|
|
216
|
-
...(delivery === undefined ? {} : { delivery }),
|
|
217
|
-
...(context === undefined ? {} : { context }),
|
|
218
|
-
...(delivery === 'unknown' && recovery !== undefined ? recovery : {}),
|
|
219
|
-
})
|
|
220
|
-
return
|
|
221
|
-
}
|
|
222
|
-
log.error(`${chalk.bold(code)}: ${error.message}`)
|
|
223
|
-
if (url !== '') log.dim(` target: ${url}`)
|
|
224
|
-
if (phase !== undefined) log.dim(` phase: ${phase}`)
|
|
225
|
-
if (phase === 'connect') log.dim(' Check the target and run `astrale status`.')
|
|
226
|
-
else if (phase === 'timeout') log.dim(' Try increasing `--timeout`.')
|
|
227
|
-
else if (delivery === 'unknown') printOperationRecovery(recovery)
|
|
228
|
-
printLocalContext(context)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function printOperationRecovery(recovery: OperationRecovery | undefined): void {
|
|
232
|
-
if (recovery === undefined) {
|
|
233
|
-
log.dim(' Delivery is unknown; do not automatically retry a mutating call.')
|
|
234
|
-
return
|
|
235
|
-
}
|
|
236
|
-
log.dim(' Delivery is unknown; retry with the same operation id:')
|
|
237
|
-
log.dim(` operation: ${recovery.operation}`)
|
|
238
|
-
log.dim(` ${recovery.retry}`)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function presentFunctionInputIssues(issues: readonly FunctionInputIssue[]): void {
|
|
242
|
-
for (const issue of issues) {
|
|
243
|
-
const location = issue.path === undefined || issue.path === '' ? '<input>' : issue.path
|
|
244
|
-
console.log(chalk.red(` ${location}: ${issue.message} (${chalk.dim(issue.code)})`))
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function presentQueryInputRepair(repair: QueryInputRepair): void {
|
|
249
|
-
if (repair.phase === 'plan') {
|
|
250
|
-
log.dim(` ${repair.path ?? '/'} ${repair.issue}`)
|
|
251
|
-
return
|
|
252
|
-
}
|
|
253
|
-
if (repair.phase === 'limit') {
|
|
254
|
-
log.dim(` ${repair.path ?? '/'} ${repair.limit} limit ${repair.actual}/${repair.maximum}`)
|
|
255
|
-
return
|
|
256
|
-
}
|
|
257
|
-
log.dim(` ${repair.path} ${repair.phase} input`)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function transportPhase(error: Error): string | undefined {
|
|
261
|
-
const phase = (error as Error & { readonly phase?: unknown }).phase
|
|
262
|
-
return phase === 'connect' ||
|
|
263
|
-
phase === 'send' ||
|
|
264
|
-
phase === 'receive' ||
|
|
265
|
-
phase === 'timeout' ||
|
|
266
|
-
phase === 'closed'
|
|
267
|
-
? phase
|
|
268
|
-
: undefined
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function transportDelivery(error: Error): string | undefined {
|
|
272
|
-
const delivery = (error as Error & { readonly delivery?: unknown }).delivery
|
|
273
|
-
return delivery === 'not-sent' || delivery === 'unknown' ? delivery : undefined
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function writeRaw(payload: Record<string, unknown>): void {
|
|
277
|
-
process.stderr.write(JSON.stringify(payload) + '\n')
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
async function contextForError(error: unknown): Promise<LocalStatus | undefined> {
|
|
281
|
-
if (!(error instanceof Error)) return undefined
|
|
282
|
-
if (error.name !== 'TransportError') return undefined
|
|
283
|
-
return readLocalStatus().catch(() => undefined)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function printLocalContext(context: LocalStatus | undefined): void {
|
|
287
|
-
if (!context) return
|
|
288
|
-
process.stderr.write(chalk.dim('\nContext:\n'))
|
|
289
|
-
if ('error' in context.admin) {
|
|
290
|
-
process.stderr.write(chalk.dim(` admin: invalid (${context.admin.error})\n`))
|
|
291
|
-
} else {
|
|
292
|
-
process.stderr.write(chalk.dim(` admin: ${context.admin.name} -> ${context.admin.url}\n`))
|
|
293
|
-
}
|
|
294
|
-
if (context.instance) {
|
|
295
|
-
process.stderr.write(
|
|
296
|
-
chalk.dim(` instance: ${context.instance.active} -> ${context.instance.url}\n`),
|
|
297
|
-
)
|
|
298
|
-
} else {
|
|
299
|
-
process.stderr.write(chalk.dim(' instance: none\n'))
|
|
300
|
-
}
|
|
301
|
-
if (context.identity) {
|
|
302
|
-
const source =
|
|
303
|
-
context.identity.source === 'idp'
|
|
304
|
-
? `idp:${context.identity.idp ?? 'unknown'}`
|
|
305
|
-
: context.identity.source
|
|
306
|
-
process.stderr.write(chalk.dim(` identity: ${context.identity.name} [${source}]\n`))
|
|
307
|
-
if (context.identity.session?.cached) {
|
|
308
|
-
const state = context.identity.session.requiresLogin ? 'login required' : 'ready'
|
|
309
|
-
process.stderr.write(chalk.dim(` session: ${state}\n`))
|
|
310
|
-
} else if (context.identity.source === 'idp') {
|
|
311
|
-
process.stderr.write(chalk.dim(' session: not cached\n'))
|
|
312
|
-
}
|
|
313
|
-
} else {
|
|
314
|
-
process.stderr.write(chalk.dim(' identity: none\n'))
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function printDebug(error: unknown, url: string): void {
|
|
319
|
-
process.stderr.write('\n' + chalk.dim('── debug ─────────────────') + '\n')
|
|
320
|
-
if (url) process.stderr.write(chalk.dim(`url: ${url}`) + '\n')
|
|
321
|
-
if (error instanceof Error) {
|
|
322
|
-
process.stderr.write(chalk.dim(`class: ${error.constructor.name}`) + '\n')
|
|
323
|
-
if (error.stack) process.stderr.write(chalk.dim(error.stack) + '\n')
|
|
324
|
-
// Walk cause chain
|
|
325
|
-
let cause = (error as Error & { cause?: unknown }).cause
|
|
326
|
-
while (cause instanceof Error) {
|
|
327
|
-
process.stderr.write(
|
|
328
|
-
chalk.dim(`caused by: ${cause.constructor.name}: ${cause.message}`) + '\n',
|
|
329
|
-
)
|
|
330
|
-
if (cause.stack) process.stderr.write(chalk.dim(cause.stack) + '\n')
|
|
331
|
-
cause = (cause as Error & { cause?: unknown }).cause
|
|
332
|
-
}
|
|
333
|
-
// Any attached fields (code, details, etc.)
|
|
334
|
-
const extras: Record<string, unknown> = {}
|
|
335
|
-
const bag = error as unknown as Record<string, unknown>
|
|
336
|
-
for (const key of [
|
|
337
|
-
'code',
|
|
338
|
-
'type',
|
|
339
|
-
'reason',
|
|
340
|
-
'details',
|
|
341
|
-
'errors',
|
|
342
|
-
'data',
|
|
343
|
-
'timeoutMs',
|
|
344
|
-
'requestId',
|
|
345
|
-
]) {
|
|
346
|
-
const v = bag[key]
|
|
347
|
-
if (v !== undefined) extras[key] = v
|
|
348
|
-
}
|
|
349
|
-
if (Object.keys(extras).length > 0) {
|
|
350
|
-
process.stderr.write(chalk.dim(`extras: ${JSON.stringify(extras, null, 2)}`) + '\n')
|
|
351
|
-
}
|
|
352
|
-
} else {
|
|
353
|
-
process.stderr.write(chalk.dim(String(error)) + '\n')
|
|
354
|
-
}
|
|
355
|
-
}
|
|
1
|
+
export { formatKernelError, functionInputIssues, schemaUpgradeHint } from './failure'
|