@astrale-os/cli 1.0.0-beta.4 → 1.0.0-beta.6

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.
Files changed (173) hide show
  1. package/dist/astrale.js +55649 -59388
  2. package/dist/public/connect-core.js +1972 -26831
  3. package/dist/public/keys/index.js +1754 -40686
  4. package/dist/public/paths/index.js +859 -39511
  5. package/dist/types/admin/instance/model.d.ts +3 -4
  6. package/dist/types/errors.d.ts +1 -1
  7. package/dist/types/lib/idp.d.ts +1 -0
  8. package/dist/types/lib/instance-target.d.ts +1 -1
  9. package/dist/types/lib/log.d.ts +2 -2
  10. package/dist/types/lib/update.d.ts +30 -0
  11. package/package.json +4 -6
  12. package/src/__tests__/fixtures/publication.ts +20 -0
  13. package/src/admin/__tests__/binding.test.ts +14 -21
  14. package/src/admin/__tests__/fixture.ts +101 -0
  15. package/src/admin/binding.ts +86 -6
  16. package/src/admin/catalog/.spec/api.d.ts +2 -2
  17. package/src/admin/catalog/__tests__/client.test.ts +20 -49
  18. package/src/admin/catalog/client.ts +35 -26
  19. package/src/admin/catalog/model.ts +3 -4
  20. package/src/admin/graph/.spec/api.d.ts +4 -10
  21. package/src/admin/graph/nodes.ts +1 -1
  22. package/src/admin/instance/.spec/api.d.ts +2 -2
  23. package/src/admin/instance/__tests__/client.test.ts +58 -83
  24. package/src/admin/instance/client.ts +49 -39
  25. package/src/admin/instance/model.ts +9 -8
  26. package/src/commands/__tests__/call-describe.test.ts +46 -33
  27. package/src/commands/__tests__/domain-install-operation.test.ts +3 -14
  28. package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
  29. package/src/commands/__tests__/domain-list.test.ts +5 -25
  30. package/src/commands/__tests__/install-identity-override.test.ts +3 -14
  31. package/src/commands/__tests__/read-commands.test.ts +35 -10
  32. package/src/commands/__tests__/update.test.ts +25 -3
  33. package/src/commands/call-describe.ts +59 -67
  34. package/src/commands/call.ts +41 -54
  35. package/src/commands/domain/install.ts +2 -2
  36. package/src/commands/domain/uninstall.ts +2 -2
  37. package/src/commands/get.ts +13 -14
  38. package/src/commands/identity/__tests__/register.test.ts +3 -3
  39. package/src/commands/identity/register.ts +5 -4
  40. package/src/commands/instance/delete.ts +5 -3
  41. package/src/commands/instance/status.ts +1 -9
  42. package/src/commands/introspect.ts +4 -11
  43. package/src/commands/logs.ts +24 -25
  44. package/src/commands/mutate.ts +6 -5
  45. package/src/commands/query.ts +27 -30
  46. package/src/commands/token.ts +8 -7
  47. package/src/commands/update.ts +31 -11
  48. package/src/commands/view.ts +4 -3
  49. package/src/connection/.spec/api.d.ts +7 -8
  50. package/src/connection/.spec/architecture.md +5 -4
  51. package/src/connection/.spec/flows/session.ts +2 -2
  52. package/src/connection/.spec/laws/connection.ts +6 -2
  53. package/src/connection/.spec/layout.ts +1 -0
  54. package/src/connection/__tests__/credential.test.ts +1 -1
  55. package/src/connection/__tests__/errors.test.ts +27 -20
  56. package/src/connection/__tests__/exchange.test.ts +30 -1
  57. package/src/connection/__tests__/failure-fixtures.ts +39 -0
  58. package/src/connection/__tests__/failure-safety.test.ts +75 -0
  59. package/src/connection/__tests__/self.test.ts +31 -11
  60. package/src/connection/__tests__/session.test.ts +3 -3
  61. package/src/connection/call.ts +1 -1
  62. package/src/connection/credential.ts +2 -2
  63. package/src/connection/errors.ts +1 -355
  64. package/src/connection/exchange.ts +47 -23
  65. package/src/connection/failure/classify.ts +110 -0
  66. package/src/connection/failure/debug.ts +26 -0
  67. package/src/connection/failure/index.ts +18 -0
  68. package/src/connection/failure/model.ts +28 -0
  69. package/src/connection/failure/render.ts +116 -0
  70. package/src/connection/reasons.ts +16 -0
  71. package/src/connection/self.ts +39 -21
  72. package/src/connection/session.ts +8 -8
  73. package/src/errors.ts +3 -2
  74. package/src/graph/.spec/api.d.ts +12 -5
  75. package/src/graph/.spec/layout.ts +9 -1
  76. package/src/graph/__tests__/mutation.test.ts +2 -2
  77. package/src/graph/__tests__/query.test.ts +25 -6
  78. package/src/graph/class.ts +30 -0
  79. package/src/graph/index.ts +1 -0
  80. package/src/graph/query.ts +21 -32
  81. package/src/identity/.spec/api.d.ts +2 -2
  82. package/src/identity/__tests__/registration.test.ts +1 -1
  83. package/src/identity/registration.ts +3 -3
  84. package/src/lib/__tests__/domain-publication.test.ts +4 -15
  85. package/src/lib/__tests__/idp.test.ts +25 -0
  86. package/src/lib/__tests__/instance-target.test.ts +6 -39
  87. package/src/lib/__tests__/log-errors.test.ts +53 -0
  88. package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
  89. package/src/lib/__tests__/update.test.ts +101 -8
  90. package/src/lib/__tests__/view-open-intent.test.ts +0 -2
  91. package/src/lib/__tests__/view-server.test.ts +1 -1
  92. package/src/lib/__tests__/view-session.test.ts +1 -1
  93. package/src/lib/command-dx.ts +1 -4
  94. package/src/lib/domain-publication.ts +1 -1
  95. package/src/lib/idp.ts +29 -1
  96. package/src/lib/instance-target.ts +3 -14
  97. package/src/lib/log.ts +11 -6
  98. package/src/lib/update.ts +120 -26
  99. package/src/lib/validation.ts +2 -2
  100. package/src/lib/view/resolve.ts +1 -1
  101. package/src/program/__tests__/program.test.ts +1 -1
  102. package/studio/client/dist/assets/{elk-api-lwhFo7vB.js → elk-api-Di4OXGNH.js} +1 -1
  103. package/studio/client/dist/assets/index-BLm6J11H.js +81 -0
  104. package/studio/client/dist/assets/index-BiGR8Oo9.css +1 -0
  105. package/studio/client/dist/assets/index-DTjvkOUX.js +8 -0
  106. package/studio/client/dist/index.html +2 -2
  107. package/studio/package.json +2 -9
  108. package/studio/server/agent/ask.test.ts +1 -1
  109. package/studio/server/agent/bridge/grant.test.ts +1 -1
  110. package/studio/server/agent/bridge/routes.test.ts +1 -1
  111. package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
  112. package/studio/server/agent/prompts/anchors.test.ts +32 -36
  113. package/studio/server/agent/prompts/anchors.ts +20 -49
  114. package/studio/server/agent/prompts/system.test.ts +3 -2
  115. package/studio/server/agent/prompts/system.ts +3 -3
  116. package/studio/server/agent/routes.test.ts +1 -1
  117. package/studio/server/agent/run/coordinator.test.ts +1 -1
  118. package/studio/server/api/canvas.ts +0 -1
  119. package/studio/server/api-agent-loadout.test.ts +1 -1
  120. package/studio/server/api.test.ts +1 -1
  121. package/studio/server/cache.test.ts +23 -12
  122. package/studio/server/cache.ts +12 -15
  123. package/studio/server/domain.test.ts +50 -68
  124. package/studio/server/domain.ts +82 -63
  125. package/studio/server/handoff/copy.ts +1 -1
  126. package/studio/server/index.ts +1 -1
  127. package/studio/server/instances/probe.test.ts +1 -1
  128. package/studio/server/introspect/anatomy/views/routes.ts +46 -37
  129. package/studio/server/introspect/anatomy/views.ts +9 -14
  130. package/studio/server/introspect/anatomy-extras.test.ts +26 -42
  131. package/studio/server/introspect/anatomy.test.ts +22 -14
  132. package/studio/server/introspect/anatomy.ts +6 -9
  133. package/studio/server/introspect/bundle.ts +0 -3
  134. package/studio/server/introspect/canonical-schema.test.ts +94 -389
  135. package/studio/server/introspect/canonical-schema.ts +227 -457
  136. package/studio/server/introspect/core-extractor.ts +18 -127
  137. package/studio/server/introspect/core.ts +1 -2
  138. package/studio/server/introspect/diff.test.ts +11 -7
  139. package/studio/server/introspect/diff.ts +18 -55
  140. package/studio/server/introspect/extractor.ts +22 -117
  141. package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
  142. package/studio/server/introspect/overlay.test.ts +26 -54
  143. package/studio/server/introspect/overlay.ts +10 -37
  144. package/studio/server/introspect/revision.test.ts +1 -1
  145. package/studio/server/introspect/revision.ts +1 -1
  146. package/studio/server/introspect/runtime.test.ts +65 -183
  147. package/studio/server/introspect/runtime.ts +8 -25
  148. package/studio/server/introspect/schema-ir-json.test.ts +70 -0
  149. package/studio/server/introspect/schema-ir-json.ts +38 -68
  150. package/studio/server/introspect/schema-refs.test.ts +43 -88
  151. package/studio/server/introspect/schema-refs.ts +14 -49
  152. package/studio/server/introspect/source-overlay/handlers.ts +116 -636
  153. package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
  154. package/studio/server/introspect/source-overlay/spans.ts +15 -45
  155. package/studio/server/state/baseline.test.ts +14 -9
  156. package/studio/server/state/baseline.ts +3 -5
  157. package/studio/server/state/visibility.ts +1 -5
  158. package/studio/server/views/model.ts +3 -3
  159. package/studio/server/views/target.test.ts +17 -74
  160. package/studio/server/views/target.ts +24 -50
  161. package/studio/server/watch.test.ts +11 -11
  162. package/studio/server/watch.ts +8 -4
  163. package/studio/server/workspace-watch.ts +2 -2
  164. package/studio/shared/contracts/schema.ts +38 -107
  165. package/studio/shared/contracts/surface.test.ts +13 -15
  166. package/studio/shared/contracts/workspace.ts +2 -11
  167. package/studio/shared/schema/identity.test.ts +20 -44
  168. package/studio/shared/schema/identity.ts +9 -23
  169. package/viewer/dist/main.js +40 -61
  170. package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
  171. package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
  172. package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
  173. package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
@@ -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 { failClosed, log } from '../lib/log'
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
- params = await parseParams([...expanded.parameters], opts.data)
45
- } catch (e) {
46
- failClosed(e, opts)
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
- // ── Execute ────────────────────────────────────────────
56
- await runKernelCommand<MaterializedCallResult>({
57
- opts,
58
- label: expandedPath,
59
- fn: (ctx) =>
60
- withSelfHint(
61
- async () =>
62
- materializeCallResult(await ctx.session.dispatch(createPathCall(expandedPath, params))),
63
- expanded.meta,
64
- ),
65
- format: async (result, fmtOpts) => {
66
- switch (result.kind) {
67
- case 'value':
68
- present(result.value, fmtOpts)
69
- return
70
- case 'binary':
71
- await presentBinary(result.value, fmtOpts, { outFile: opts.output })
72
- return
73
- case 'stream':
74
- output(result.values, fmtOpts)
75
- return
76
- case 'redirect':
77
- throw new Error('Client Session returned an unresolved redirect.')
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 Error(`Invalid JSON in --data: ${dataFlag}`)
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 Error('Invalid JSON from stdin')
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 Error(`Invalid param "${pair}" — expected key=value format`)
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 Error(`Invalid param key "${key}" in "${pair}"${hint}`)
140
+ throw new TypeError(`Invalid param key "${key}" in "${pair}"${hint}`)
154
141
  }
155
142
  result[key] = coerceValue(raw)
156
143
  }
@@ -1,5 +1,5 @@
1
1
  import { Path } from '@astrale-os/sdk/graph/path'
2
- import { syscalls } from '@astrale-os/sdk/schema/kernel'
2
+ import { K } from '@astrale-os/sdk/schema'
3
3
  import chalk from 'chalk'
4
4
 
5
5
  import type { KernelCommandOpts } from '../../connection'
@@ -414,7 +414,7 @@ export async function installDirect(
414
414
  fn: async ({ session }) =>
415
415
  (await session.call(
416
416
  createPathCall(
417
- Path.project(syscalls.install.ref).raw,
417
+ Path.project(K.functions.install.ref).raw,
418
418
  directInstallCallInput(url, operation, opts.token),
419
419
  ),
420
420
  )) as DirectInstallResult,
@@ -1,5 +1,5 @@
1
1
  import { Path } from '@astrale-os/sdk/graph/path'
2
- import { syscalls } from '@astrale-os/sdk/schema/kernel'
2
+ import { K } from '@astrale-os/sdk/schema'
3
3
  import chalk from 'chalk'
4
4
 
5
5
  import type { KernelCommandOpts } from '../../connection'
@@ -77,7 +77,7 @@ Examples:
77
77
  label: `Uninstalling domain ${origin}`,
78
78
  fn: async ({ session }) =>
79
79
  (await session.call(
80
- createPathCall(Path.project(syscalls.uninstall.ref).raw, uninstallCallInput(origin)),
80
+ createPathCall(Path.project(K.functions.uninstall.ref).raw, uninstallCallInput(origin)),
81
81
  )) as UninstallResult,
82
82
  format: (result, formatOpts, machine) => {
83
83
  if (machine) {
@@ -4,40 +4,39 @@ import type { KernelCommandOpts } from '../connection'
4
4
  import type { CommandDefinition } from '../program/index'
5
5
 
6
6
  import { expandSelfInPath, runKernelCommand, withSelfHint } from '../connection'
7
- import { formatKernelError } from '../connection/errors'
8
7
  import { AstraleError } from '../errors'
9
- import { denoise, isMachine, output } from '../lib/output'
8
+ import { failInput, fatal } from '../lib/log'
9
+ import { denoise, output } from '../lib/output'
10
10
 
11
11
  type GetOpts = KernelCommandOpts & { schema?: boolean }
12
12
 
13
13
  /** Read one exact canonical Node. The caller's Path is not fabricated into the Node value. */
14
14
  export async function getCommand(target: string, opts: GetOpts): Promise<void> {
15
- let path: string
16
- let meta
17
15
  let parsed
18
16
  try {
19
- ;({ path, meta } = await expandSelfInPath(target, opts))
20
- parsed = Path.parse(path)
17
+ parsed = Path.parse(target)
21
18
  } catch (error) {
22
- await formatKernelError(error, isMachine(opts), undefined, opts.debug)
23
- process.exit(1)
19
+ failInput(error, opts)
24
20
  }
25
21
 
26
22
  const last = parsed.ast.steps.at(-1)
27
23
  if (last?.kind === 'method') {
28
24
  const error = new AstraleError(
29
25
  'NOT_A_NODE',
30
- `${path} is a callable Path, not a Node.`,
31
- `Use \`astrale call ${path}\` or \`astrale introspect ${path}\`.`,
26
+ `${target} is a callable Path, not a Node.`,
27
+ `Use \`astrale call ${target}\` or \`astrale introspect ${target}\`.`,
32
28
  )
33
- await formatKernelError(error, isMachine(opts), undefined, opts.debug)
34
- process.exit(1)
29
+ fatal(error, opts)
35
30
  }
36
31
 
37
32
  await runKernelCommand({
38
33
  opts,
39
- label: `Node ${path}`,
40
- fn: ({ graph }) => withSelfHint(() => graph.getOrThrow(parsed), meta),
34
+ label: `Node ${target}`,
35
+ fn: async (context) => {
36
+ const { path, meta } = await expandSelfInPath(target, context)
37
+ const resolved = meta === undefined ? parsed : Path.parse(path)
38
+ return withSelfHint(() => context.graph.getOrThrow(resolved), meta)
39
+ },
41
40
  format: (node, format) => output(opts.schema === true ? node : denoise(node), format),
42
41
  })
43
42
  }
@@ -1,9 +1,9 @@
1
1
  import { issuer, jwk, provision } from '@astrale-os/sdk/auth'
2
- import { ClassPath } from '@astrale-os/sdk/graph/class'
3
2
  import { normalizeProperties } from '@astrale-os/sdk/graph/properties'
4
3
  import { expect, mock, test } from 'bun:test'
5
4
  import { exportJWK, generateKeyPair, jwtVerify } from 'jose'
6
5
 
6
+ import { classKey } from '../../../graph'
7
7
  import { formatIdentityRegistration, prepareIdentityProvision } from '../register'
8
8
 
9
9
  /** @evidence TEST-CLI-IDENTITY-REGISTER-JSON-EXACT */
@@ -46,7 +46,7 @@ test('builds one exact Mutation V3 identity birth bound to a self proof', async
46
46
  kid: 'alice-key',
47
47
  })
48
48
  const kernelIssuer = issuer.accept('https://kernel.example')
49
- const classPath = ClassPath.parse('/:accounts.example:class.User')
49
+ const classPath = classKey('/:accounts.example:class.User', '--class')
50
50
  const properties = normalizeProperties({
51
51
  'accounts.example:class.User.property.name': 'Alice',
52
52
  })
@@ -70,7 +70,7 @@ test('builds one exact Mutation V3 identity birth bound to a self proof', async
70
70
  {
71
71
  op: 'node.create',
72
72
  as: 'identity',
73
- class: '/:accounts.example:class.User',
73
+ class: 'accounts.example:class.User',
74
74
  props: { 'accounts.example:class.User.property.name': 'Alice' },
75
75
  },
76
76
  ],
@@ -1,9 +1,9 @@
1
1
  import type { IssuerId, JsonWebKey, ProvisionRequest } from '@astrale-os/sdk/auth'
2
+ import type { ClassKey as ClassKeyValue } from '@astrale-os/sdk/graph/class'
2
3
  import type { JWK } from 'jose'
3
4
 
4
5
  import { provision, jwk } from '@astrale-os/sdk/auth'
5
- import { ClassPath } from '@astrale-os/sdk/graph/class'
6
- import { LocalBinding } from '@astrale-os/sdk/graph/model'
6
+ import { LocalBinding } from '@astrale-os/sdk/graph'
7
7
  import { normalizeProperties } from '@astrale-os/sdk/graph/properties'
8
8
  import { MutationAST } from '@astrale-os/sdk/mutation'
9
9
  import { importJWK, SignJWT } from 'jose'
@@ -14,6 +14,7 @@ import type { IdentityRegistrationResult } from '../../identity/index'
14
14
  import type { CommandDefinition } from '../../program/index'
15
15
 
16
16
  import { registrationKeyForTarget, runKernelCommand } from '../../connection'
17
+ import { classKey } from '../../graph'
17
18
  import { getIdentity, setRegistration, submitIdentityProvision } from '../../identity/index'
18
19
  import { fileExists, keypairPaths } from '../../keys/index'
19
20
  import { fatal, log } from '../../lib/log'
@@ -83,7 +84,7 @@ Example:
83
84
 
84
85
  const privateKey = await readJwk(privatePath)
85
86
  const publicKey = jwk.acceptPublic(await readJwk(publicPath))
86
- const classPath = ClassPath.parse(opts.class)
87
+ const classPath = classKey(opts.class, '--class')
87
88
  const properties = normalizeProperties(opts.props ? JSON.parse(opts.props) : {})
88
89
 
89
90
  await runKernelCommand<IdentityRegistrationResult>({
@@ -142,7 +143,7 @@ export function formatIdentityRegistration(
142
143
  /** Build and self-prove one exact canonical provision request for the target Kernel. */
143
144
  export async function prepareIdentityProvision(input: {
144
145
  readonly name: string
145
- readonly classPath: ReturnType<typeof ClassPath.parse>
146
+ readonly classPath: ClassKeyValue
146
147
  readonly properties: ReturnType<typeof normalizeProperties>
147
148
  readonly privateKey: JWK
148
149
  readonly publicKey: JsonWebKey
@@ -1,6 +1,7 @@
1
1
  import type { KernelCommandOpts } from '../../connection'
2
2
  import type { CommandDefinition } from '../../program/index'
3
3
 
4
+ import { AdminInstanceNotFoundError } from '../../admin/instance'
4
5
  import { deleteOwnedInstance } from '../../lib/admin-instance'
5
6
  import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
6
7
  import { clearActive, readInstances, removeInstance, resolveInstanceKey } from '../../lib/instance'
@@ -53,9 +54,10 @@ Behavior:
53
54
  } catch (e) {
54
55
  // Bookmark-only id → no admin Instance → kernel NOT_FOUND. Point to
55
56
  // `forget` (local drop) instead of surfacing the raw path error.
56
- const notManaged =
57
- e instanceof Error && (e.name === 'NotFoundError' || e.message.startsWith('Path not found'))
58
- if (notManaged && resolveInstanceKey(await readInstances(), id)) {
57
+ if (
58
+ e instanceof AdminInstanceNotFoundError &&
59
+ resolveInstanceKey(await readInstances(), id)
60
+ ) {
59
61
  log.error(`No admin-managed instance "${id}".`)
60
62
  log.dim(` hint: it's a local bookmark — drop it with: astrale instance forget ${id}`)
61
63
  process.exit(1)
@@ -3,7 +3,6 @@ import chalk from 'chalk'
3
3
  import type { KernelCommandOpts } from '../../connection'
4
4
  import type { CommandDefinition } from '../../program/index'
5
5
 
6
- import { AstraleError } from '../../errors'
7
6
  import { statusOwnedInstance } from '../../lib/admin-instance'
8
7
  import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
9
8
  import { fatal, log, withSpinner } from '../../lib/log'
@@ -19,14 +18,7 @@ export default {
19
18
  action: async (id: string, opts: StatusOpts) => {
20
19
  try {
21
20
  const result = await withSpinner(`Fetching instance ${id}`, !isMachine(opts), () =>
22
- statusOwnedInstance(opts, id).catch((error) => {
23
- if (!(error instanceof Error) || error.name !== 'NotFoundError') throw error
24
- throw new AstraleError(
25
- 'INSTANCE_NOT_FOUND',
26
- `No owned instance matches "${id}".`,
27
- 'Run `astrale instance list` to see your instances.',
28
- )
29
- }),
21
+ statusOwnedInstance(opts, id),
30
22
  )
31
23
  if (isMachine(opts)) {
32
24
  output(result, opts)
@@ -5,9 +5,9 @@ import type { CommandDefinition } from '../program/index'
5
5
 
6
6
  import { runKernelCommand } from '../connection'
7
7
  import { AstraleError } from '../errors'
8
- import { failClosed } from '../lib/log'
8
+ import { failInput } from '../lib/log'
9
9
  import { output } from '../lib/output'
10
- import { describeCallableFromSchema, missingCallableDescription } from './call-describe'
10
+ import { describeCallableFromBundle, missingCallableDescription } from './call-describe'
11
11
 
12
12
  type IntrospectOpts = KernelCommandOpts & { bundle?: boolean }
13
13
 
@@ -17,7 +17,7 @@ export async function introspectCommand(target: string, opts: IntrospectOpts): P
17
17
  try {
18
18
  ;({ origin, path } = parseIntrospectTarget(target))
19
19
  } catch (error) {
20
- failClosed(error, opts)
20
+ failInput(error, opts)
21
21
  }
22
22
  const wantsCallable = isCallablePath(path)
23
23
 
@@ -44,7 +44,7 @@ export async function introspectCommand(target: string, opts: IntrospectOpts): P
44
44
  )
45
45
  }
46
46
  if (wantsCallable) {
47
- const described = describeCallableFromSchema(path, bundleRoot(result.bundle))
47
+ const described = describeCallableFromBundle(path, result.bundle)
48
48
  if (described === undefined) throw missingCallableDescription(path.raw)
49
49
  return described
50
50
  }
@@ -85,13 +85,6 @@ function isCallablePath(path: Path): boolean {
85
85
  return last.kind === 'projection' && last.projection.kind === 'function'
86
86
  }
87
87
 
88
- function bundleRoot(bundle: unknown): unknown {
89
- if (bundle !== null && typeof bundle === 'object' && 'root' in bundle) {
90
- return (bundle as { root: unknown }).root
91
- }
92
- return bundle
93
- }
94
-
95
88
  export default {
96
89
  name: 'introspect',
97
90
  description: 'Read installed Domain schema from the Kernel Schema syscall',
@@ -1,21 +1,16 @@
1
1
  import { Path } from '@astrale-os/sdk/graph/path'
2
- import { syscalls } from '@astrale-os/sdk/schema/kernel'
2
+ import { K } from '@astrale-os/sdk/schema'
3
3
  import chalk from 'chalk'
4
4
 
5
5
  import type { ConnectionContext, KernelCommandOpts } from '../connection'
6
6
  import type { Column, ListProjection } from '../lib/output'
7
7
  import type { CommandDefinition } from '../program/index'
8
8
 
9
- import {
10
- createPathCall,
11
- expandSelfInPath,
12
- runKernelCommand,
13
- withClientSession,
14
- } from '../connection'
15
- import { failClosed } from '../lib/log'
9
+ import { createPathCall, expandSelfInPath, runKernelCommand } from '../connection'
10
+ import { failInput } from '../lib/log'
16
11
  import { isMachine, output, presentList } from '../lib/output'
17
12
 
18
- const JOURNAL_PATH = Path.project(syscalls.journal.ref).raw
13
+ const JOURNAL_PATH = Path.project(K.functions.journal.ref).raw
19
14
  const DEFAULT_LIMIT = 200
20
15
  const FOLLOW_INTERVAL_MS = 2_000
21
16
 
@@ -127,7 +122,7 @@ async function runOnce(opts: LogsOpts): Promise<void> {
127
122
  await runKernelCommand({
128
123
  opts,
129
124
  label: 'Kernel journal',
130
- fn: (context) => fetchPage(context, opts),
125
+ fn: async (context) => fetchPage(context, await resolveLogsOpts(opts, context)),
131
126
  format: (page, format) => {
132
127
  if (isMachine(format) || format.format !== undefined) output(page, format)
133
128
  else {
@@ -139,14 +134,19 @@ async function runOnce(opts: LogsOpts): Promise<void> {
139
134
  }
140
135
 
141
136
  async function follow(opts: LogsOpts): Promise<void> {
142
- await withClientSession(opts, async (context) => {
143
- let cursor = opts.cursor
144
- for (;;) {
145
- const page = await fetchPage(context, { ...opts, cursor })
146
- for (const record of page.records) printRecord(record)
147
- cursor = page.cursor ?? cursor
148
- await new Promise((resolve) => setTimeout(resolve, FOLLOW_INTERVAL_MS))
149
- }
137
+ await runKernelCommand({
138
+ opts,
139
+ label: 'Kernel journal',
140
+ fn: async (context): Promise<never> => {
141
+ const resolved = await resolveLogsOpts(opts, context)
142
+ let cursor = resolved.cursor
143
+ for (;;) {
144
+ const page = await fetchPage(context, { ...resolved, cursor })
145
+ for (const record of page.records) printRecord(record)
146
+ cursor = page.cursor ?? cursor
147
+ await new Promise((resolve) => setTimeout(resolve, FOLLOW_INTERVAL_MS))
148
+ }
149
+ },
150
150
  })
151
151
  }
152
152
 
@@ -227,11 +227,10 @@ function positiveInteger(flag: string, raw: string): number {
227
227
  return value
228
228
  }
229
229
 
230
- async function prepareLogsOpts(opts: LogsOpts): Promise<LogsOpts> {
231
- buildJournalInput(opts)
230
+ async function resolveLogsOpts(opts: LogsOpts, context: ConnectionContext): Promise<LogsOpts> {
232
231
  const principal = nonEmpty(opts.principal)
233
232
  if (principal !== '@self') return opts
234
- const { path } = await expandSelfInPath('@self', opts)
233
+ const { path } = await expandSelfInPath('@self', context)
235
234
  return { ...opts, principal: path.startsWith('@') ? path.slice(1) : path }
236
235
  }
237
236
 
@@ -274,11 +273,11 @@ Examples:
274
273
  ],
275
274
  action: async (opts: LogsOpts) => {
276
275
  try {
277
- const prepared = await prepareLogsOpts(opts)
278
- if (prepared.follow) await follow(prepared)
279
- else await runOnce(prepared)
276
+ buildJournalInput(opts)
280
277
  } catch (error) {
281
- failClosed(error, opts)
278
+ failInput(error, opts)
282
279
  }
280
+ if (opts.follow) await follow(opts)
281
+ else await runOnce(opts)
283
282
  },
284
283
  } satisfies CommandDefinition
@@ -7,8 +7,9 @@ import type { KernelCommandOpts } from '../connection'
7
7
  import type { CommandDefinition } from '../program/index'
8
8
 
9
9
  import { runKernelCommand } from '../connection'
10
+ import { AstraleError } from '../errors'
10
11
  import { prepareMutation } from '../graph/index'
11
- import { failClosed } from '../lib/log'
12
+ import { failInput } from '../lib/log'
12
13
  import { output } from '../lib/output'
13
14
  import { renderTable } from '../lib/table'
14
15
 
@@ -23,7 +24,7 @@ export async function mutateCommand(opts: MutateOpts): Promise<void> {
23
24
  try {
24
25
  mutation = prepareMutation(await readDocument(opts))
25
26
  } catch (error) {
26
- failClosed(error, opts)
27
+ failInput(error, opts)
27
28
  }
28
29
 
29
30
  if (opts.dry) {
@@ -51,9 +52,9 @@ async function readDocument(opts: MutateOpts): Promise<unknown> {
51
52
  try {
52
53
  raw = await readFile(opts.file, 'utf8')
53
54
  } catch (error) {
54
- throw new Error(
55
- `Cannot read --file ${opts.file}: ${error instanceof Error ? error.message : error}`,
56
- )
55
+ throw new AstraleError('FILE_READ_FAILED', `Cannot read --file ${opts.file}.`, undefined, {
56
+ cause: error,
57
+ })
57
58
  }
58
59
  return parseJson(raw, opts.file)
59
60
  }
@@ -6,8 +6,9 @@ import type { KernelCommandOpts, SelfExpansionMeta } from '../connection'
6
6
  import type { CommandDefinition } from '../program/index'
7
7
 
8
8
  import { expandSelfInPath, runKernelCommand, withSelfHint } from '../connection'
9
+ import { AstraleError } from '../errors'
9
10
  import { prepareQuery, type QueryCommandInput } from '../graph/index'
10
- import { failClosed } from '../lib/log'
11
+ import { failInput } from '../lib/log'
11
12
  import { isMachine, output } from '../lib/output'
12
13
 
13
14
  type QueryOpts = KernelCommandOpts & {
@@ -22,46 +23,42 @@ type QueryOpts = KernelCommandOpts & {
22
23
 
23
24
  export async function queryCommand(sources: string[], opts: QueryOpts): Promise<void> {
24
25
  let input: QueryCommandInput
25
- let meta: SelfExpansionMeta | undefined
26
26
  try {
27
27
  const ast = await readAst(opts)
28
- if (ast === undefined) {
29
- const expanded = await Promise.all(sources.map((source) => expandSelfInPath(source, opts)))
30
- input = {
31
- sources: expanded.map(({ path }) => path),
32
- definition: opts.definition,
33
- edge: opts.edge,
34
- direction: opts.direction,
35
- limit: opts.limit,
36
- cursor: opts.cursor,
37
- }
38
- meta = expanded.find((entry) => entry.meta !== undefined)?.meta
39
- } else {
40
- input = {
41
- sources,
42
- ast,
43
- definition: opts.definition,
44
- edge: opts.edge,
45
- direction: opts.direction,
46
- limit: opts.limit,
47
- cursor: opts.cursor,
48
- }
28
+ input = {
29
+ sources,
30
+ ...(ast === undefined ? {} : { ast }),
31
+ definition: opts.definition,
32
+ edge: opts.edge,
33
+ direction: opts.direction,
34
+ limit: opts.limit,
35
+ cursor: opts.cursor,
49
36
  }
50
37
  } catch (error) {
51
- failClosed(error, opts)
38
+ failInput(error, opts)
52
39
  }
53
40
 
54
41
  let prepared
55
42
  try {
56
43
  prepared = prepareQuery(input)
57
44
  } catch (error) {
58
- failClosed(error, opts)
45
+ failInput(error, opts)
59
46
  }
60
47
 
61
48
  await runKernelCommand({
62
49
  opts,
63
50
  label: 'Query',
64
- fn: ({ graph }) => withSelfHint(() => graph.query(prepared.ast, { page: prepared.page }), meta),
51
+ fn: async (context) => {
52
+ const expanded = await Promise.all(sources.map((source) => expandSelfInPath(source, context)))
53
+ const meta: SelfExpansionMeta | undefined = expanded.find(
54
+ (entry) => entry.meta !== undefined,
55
+ )?.meta
56
+ const resolved =
57
+ meta === undefined
58
+ ? prepared
59
+ : prepareQuery({ ...input, sources: expanded.map(({ path }) => path) })
60
+ return withSelfHint(() => context.graph.query(resolved.ast, { page: resolved.page }), meta)
61
+ },
65
62
  format: (response, format) => {
66
63
  output(response.result, format)
67
64
  if (response.page.next && !isMachine(format)) {
@@ -81,9 +78,9 @@ async function readAst(opts: QueryOpts): Promise<unknown | undefined> {
81
78
  try {
82
79
  raw = await readFile(opts.file, 'utf8')
83
80
  } catch (error) {
84
- throw new Error(
85
- `Cannot read --file ${opts.file}: ${error instanceof Error ? error.message : error}`,
86
- )
81
+ throw new AstraleError('FILE_READ_FAILED', `Cannot read --file ${opts.file}.`, undefined, {
82
+ cause: error,
83
+ })
87
84
  }
88
85
  return parseJson(raw, opts.file)
89
86
  }
@@ -122,7 +119,7 @@ Examples:
122
119
  { flags: '-f, --file <path>', description: 'Read a canonical Query V6 document from a file' },
123
120
  {
124
121
  flags: '--definition <path>',
125
- description: 'Select Nodes implementing one exact Class or Interface',
122
+ description: 'Select Nodes satisfying one exact Class',
126
123
  },
127
124
  { flags: '--edge <class>', description: 'Expand one exact Edge Class' },
128
125
  {
@@ -6,7 +6,7 @@ import type { CommandDefinition } from '../program/index'
6
6
  import { runKernelCommand } from '../connection'
7
7
  import { AstraleError } from '../errors'
8
8
  import { decodeJwtExpiration } from '../lib/local-status'
9
- import { failClosed, log } from '../lib/log'
9
+ import { failInput, log } from '../lib/log'
10
10
  import { output } from '../lib/output'
11
11
 
12
12
  /**
@@ -23,7 +23,12 @@ export type TokenOpts = KernelCommandOpts & {
23
23
 
24
24
  export async function tokenCommand(opts: TokenOpts): Promise<void> {
25
25
  const commandOpts: TokenOpts = opts.for && !opts.as ? { ...opts, as: opts.for } : opts
26
- const ttl = parseTtl(commandOpts.ttl)
26
+ let ttl: number
27
+ try {
28
+ ttl = parseTtl(commandOpts.ttl)
29
+ } catch (error) {
30
+ failInput(error, opts)
31
+ }
27
32
  await runKernelCommand<string>({
28
33
  opts: commandOpts,
29
34
  label: 'Minting delegation token',
@@ -97,10 +102,6 @@ Examples:
97
102
  { flags: '--for <identity>', description: 'Mint the token for this identity (alias of --as)' },
98
103
  ],
99
104
  action: async (opts) => {
100
- try {
101
- await tokenCommand(opts as Parameters<typeof tokenCommand>[0])
102
- } catch (error) {
103
- failClosed(error, opts as TokenOpts)
104
- }
105
+ await tokenCommand(opts as Parameters<typeof tokenCommand>[0])
105
106
  },
106
107
  } satisfies CommandDefinition