@astrale-os/cli 1.0.0-beta.0 → 1.0.0-beta.10

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 (322) hide show
  1. package/README.md +23 -12
  2. package/dist/astrale.js +51494 -56307
  3. package/dist/public/connect-core.js +2028 -26846
  4. package/dist/public/keys/index.js +1755 -40694
  5. package/dist/public/paths/index.js +859 -39518
  6. package/dist/types/admin/binding.d.ts +19 -0
  7. package/dist/types/admin/instance/client.d.ts +22 -0
  8. package/dist/types/admin/instance/index.d.ts +2 -0
  9. package/dist/types/admin/instance/model.d.ts +5 -12
  10. package/dist/types/connection/auth.d.ts +3 -0
  11. package/dist/types/connection/call.d.ts +3 -0
  12. package/dist/types/connection/command.d.ts +27 -0
  13. package/dist/types/connection/credential.d.ts +14 -0
  14. package/dist/types/connection/errors.d.ts +1 -0
  15. package/dist/types/connection/exchange.d.ts +9 -0
  16. package/dist/types/connection/failure/classify.d.ts +2 -0
  17. package/dist/types/connection/failure/index.d.ts +5 -0
  18. package/dist/types/connection/failure/model.d.ts +25 -0
  19. package/dist/types/connection/failure/render.d.ts +3 -0
  20. package/dist/types/connection/index.d.ts +8 -0
  21. package/dist/types/connection/reasons.d.ts +37 -0
  22. package/dist/types/connection/self.d.ts +37 -0
  23. package/dist/types/connection/session.d.ts +31 -0
  24. package/dist/types/connection/target.d.ts +38 -0
  25. package/dist/types/errors.d.ts +1 -1
  26. package/dist/types/lib/admin-instance.d.ts +17 -0
  27. package/dist/types/lib/admin-target.d.ts +6 -2
  28. package/dist/types/lib/failure-debug.d.ts +1 -0
  29. package/dist/types/lib/idp.d.ts +7 -0
  30. package/dist/types/lib/instance-target.d.ts +1 -1
  31. package/dist/types/lib/instance.d.ts +10 -2
  32. package/dist/types/lib/log.d.ts +5 -3
  33. package/dist/types/lib/meta.d.ts +2 -2
  34. package/dist/types/lib/proc.d.ts +43 -0
  35. package/dist/types/lib/self.d.ts +9 -0
  36. package/dist/types/lib/update.d.ts +131 -0
  37. package/package.json +8 -10
  38. package/src/__tests__/fixtures/publication.ts +20 -0
  39. package/src/admin/__tests__/binding.test.ts +31 -0
  40. package/src/admin/__tests__/fixture.ts +101 -0
  41. package/src/admin/binding.ts +98 -0
  42. package/src/admin/catalog/.spec/api.d.ts +2 -2
  43. package/src/admin/catalog/__tests__/client.test.ts +20 -49
  44. package/src/admin/catalog/client.ts +36 -32
  45. package/src/admin/catalog/model.ts +3 -4
  46. package/src/admin/graph/.spec/api.d.ts +4 -10
  47. package/src/admin/graph/nodes.ts +1 -1
  48. package/src/admin/instance/.spec/api.d.ts +2 -2
  49. package/src/admin/instance/__tests__/client.test.ts +40 -163
  50. package/src/admin/instance/client.ts +35 -137
  51. package/src/admin/instance/index.ts +1 -2
  52. package/src/admin/instance/model.ts +11 -20
  53. package/src/commands/__tests__/admin-instance.test.ts +11 -8
  54. package/src/commands/__tests__/auth-login.test.ts +54 -1
  55. package/src/commands/__tests__/call-describe.test.ts +46 -33
  56. package/src/commands/__tests__/call.test.ts +5 -1
  57. package/src/commands/__tests__/domain-install-operation.test.ts +12 -7
  58. package/src/commands/__tests__/domain-install-owned.test.ts +6 -2
  59. package/src/commands/__tests__/domain-list.test.ts +63 -4
  60. package/src/commands/__tests__/install-identity-override.test.ts +19 -19
  61. package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
  62. package/src/commands/__tests__/instance-status.test.ts +139 -0
  63. package/src/commands/__tests__/read-commands.test.ts +35 -10
  64. package/src/commands/__tests__/studio-descriptor.test.ts +22 -0
  65. package/src/commands/__tests__/update.test.ts +95 -0
  66. package/src/commands/call-describe.ts +60 -68
  67. package/src/commands/call.ts +43 -56
  68. package/src/commands/domain/install.ts +13 -17
  69. package/src/commands/domain/list.ts +18 -13
  70. package/src/commands/domain/publish.ts +14 -11
  71. package/src/commands/domain/uninstall.ts +2 -2
  72. package/src/commands/get.ts +14 -15
  73. package/src/commands/identity/__tests__/register.test.ts +3 -3
  74. package/src/commands/identity/register.ts +5 -4
  75. package/src/commands/instance/create.ts +8 -16
  76. package/src/commands/instance/delete.ts +12 -9
  77. package/src/commands/instance/list.ts +2 -2
  78. package/src/commands/instance/status.ts +66 -20
  79. package/src/commands/instance/use.ts +16 -12
  80. package/src/commands/introspect.ts +9 -16
  81. package/src/commands/logs.ts +24 -25
  82. package/src/commands/mutate.ts +6 -5
  83. package/src/commands/query.ts +27 -30
  84. package/src/commands/studio.ts +20 -0
  85. package/src/commands/token.ts +8 -7
  86. package/src/commands/update.ts +68 -22
  87. package/src/commands/view.ts +7 -6
  88. package/src/connection/.spec/api.d.ts +7 -8
  89. package/src/connection/.spec/architecture.md +5 -4
  90. package/src/connection/.spec/flows/session.ts +2 -2
  91. package/src/connection/.spec/laws/connection.ts +6 -2
  92. package/src/connection/.spec/layout.ts +1 -0
  93. package/src/connection/__tests__/credential.test.ts +20 -1
  94. package/src/connection/__tests__/errors.test.ts +43 -32
  95. package/src/connection/__tests__/exchange.test.ts +36 -6
  96. package/src/connection/__tests__/failure-fixtures.ts +39 -0
  97. package/src/connection/__tests__/failure-safety.test.ts +75 -0
  98. package/src/connection/__tests__/self.test.ts +31 -11
  99. package/src/connection/__tests__/session.test.ts +3 -3
  100. package/src/connection/__tests__/target.test.ts +1 -0
  101. package/src/connection/auth.ts +13 -0
  102. package/src/connection/call.ts +1 -1
  103. package/src/connection/credential.ts +2 -2
  104. package/src/connection/errors.ts +1 -355
  105. package/src/connection/exchange.ts +52 -27
  106. package/src/connection/failure/classify.ts +110 -0
  107. package/src/connection/failure/index.ts +18 -0
  108. package/src/connection/failure/model.ts +28 -0
  109. package/src/connection/failure/render.ts +116 -0
  110. package/src/connection/reasons.ts +16 -0
  111. package/src/connection/self.ts +39 -21
  112. package/src/connection/session.ts +19 -8
  113. package/src/errors.ts +3 -2
  114. package/src/graph/.spec/api.d.ts +12 -5
  115. package/src/graph/.spec/layout.ts +9 -1
  116. package/src/graph/__tests__/mutation.test.ts +2 -2
  117. package/src/graph/__tests__/query.test.ts +25 -6
  118. package/src/graph/class.ts +30 -0
  119. package/src/graph/index.ts +1 -0
  120. package/src/graph/query.ts +21 -32
  121. package/src/identity/.spec/api.d.ts +2 -2
  122. package/src/identity/__tests__/registration.test.ts +1 -1
  123. package/src/identity/registration.ts +3 -3
  124. package/src/keys/__tests__/keys.test.ts +4 -0
  125. package/src/keys/credential.ts +1 -0
  126. package/src/lib/__tests__/admin-target.test.ts +12 -0
  127. package/src/lib/__tests__/command-dx.test.ts +1 -1
  128. package/src/lib/__tests__/domain-publication.test.ts +138 -0
  129. package/src/lib/__tests__/idp-session.test.ts +22 -0
  130. package/src/lib/__tests__/idp.test.ts +36 -4
  131. package/src/lib/__tests__/instance-candidates.test.ts +1 -3
  132. package/src/lib/__tests__/instance-target.test.ts +7 -39
  133. package/src/lib/__tests__/instance.test.ts +6 -0
  134. package/src/lib/__tests__/log-errors.test.ts +64 -0
  135. package/src/lib/__tests__/studio-server-deps.test.ts +53 -16
  136. package/src/lib/__tests__/update.test.ts +113 -8
  137. package/src/lib/__tests__/view-open-intent.test.ts +0 -2
  138. package/src/lib/__tests__/view-server.test.ts +1 -1
  139. package/src/lib/__tests__/view-session.test.ts +1 -1
  140. package/src/lib/admin-instance.ts +14 -8
  141. package/src/lib/admin-target.ts +21 -2
  142. package/src/lib/command-dx.ts +9 -9
  143. package/src/lib/domain-publication.ts +104 -0
  144. package/src/lib/failure-debug.ts +26 -0
  145. package/src/lib/idp.ts +39 -4
  146. package/src/lib/instance-candidates.ts +2 -2
  147. package/src/lib/instance-target.ts +3 -14
  148. package/src/lib/instance.ts +22 -12
  149. package/src/lib/log.ts +16 -9
  150. package/src/lib/login-flow.ts +13 -3
  151. package/src/lib/meta.ts +2 -2
  152. package/src/lib/provision-instance.ts +34 -57
  153. package/src/lib/update.ts +124 -30
  154. package/src/lib/validation.ts +2 -2
  155. package/src/lib/view/resolve.ts +1 -1
  156. package/src/program/__tests__/program.test.ts +20 -4
  157. package/src/program/build.ts +2 -2
  158. package/src/setup/__tests__/instance-step.test.ts +27 -10
  159. package/src/setup/steps/instance.ts +22 -15
  160. package/studio/client/dist/assets/{elk-api-D2xgMJvi.js → elk-api-ByzoDZWH.js} +1 -1
  161. package/studio/client/dist/assets/index-B0RCgUOT.js +8 -0
  162. package/studio/client/dist/assets/index-BiGR8Oo9.css +1 -0
  163. package/studio/client/dist/assets/index-N8J8EKlB.js +81 -0
  164. package/studio/client/dist/index.html +2 -2
  165. package/studio/package.json +5 -10
  166. package/studio/server/agent/ask.test.ts +1 -1
  167. package/studio/server/agent/bridge/grant.test.ts +1 -1
  168. package/studio/server/agent/bridge/routes.test.ts +1 -1
  169. package/studio/server/agent/bridge/stdio.ts +8 -1
  170. package/studio/server/agent/conversation.test.ts +10 -5
  171. package/studio/server/agent/conversation.ts +56 -8
  172. package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
  173. package/studio/server/agent/harness/codex/loadout.ts +18 -21
  174. package/studio/server/agent/harness/gateway/config.ts +20 -18
  175. package/studio/server/agent/harness/selection.ts +9 -1
  176. package/studio/server/agent/prompts/anchors.test.ts +32 -36
  177. package/studio/server/agent/prompts/anchors.ts +20 -49
  178. package/studio/server/agent/prompts/system.test.ts +3 -2
  179. package/studio/server/agent/prompts/system.ts +6 -5
  180. package/studio/server/agent/prompts/turn.ts +13 -4
  181. package/studio/server/agent/routes.test.ts +1 -1
  182. package/studio/server/agent/run/completion.ts +2 -2
  183. package/studio/server/agent/run/coordinator.test.ts +1 -1
  184. package/studio/server/agent/run/preparation.ts +6 -5
  185. package/studio/server/agent/run/transcript.test.ts +35 -1
  186. package/studio/server/agent/run/transcript.ts +169 -3
  187. package/studio/server/agent/run/usage.test.ts +24 -0
  188. package/studio/server/agent/run/usage.ts +26 -1
  189. package/studio/server/api/canvas.ts +43 -0
  190. package/studio/server/api/comments.ts +85 -0
  191. package/studio/server/api/context.ts +61 -0
  192. package/studio/server/api/deployment.ts +39 -0
  193. package/studio/server/api/documents.ts +55 -0
  194. package/studio/server/api/domain.ts +59 -0
  195. package/studio/server/api/http.ts +45 -0
  196. package/studio/server/api/project.ts +57 -0
  197. package/studio/server/api/schema.ts +12 -0
  198. package/studio/server/api/views.ts +57 -0
  199. package/studio/server/api/workspace.ts +60 -0
  200. package/studio/server/api-agent-loadout.test.ts +1 -1
  201. package/studio/server/api.test.ts +147 -0
  202. package/studio/server/api.ts +20 -422
  203. package/studio/server/cache.test.ts +122 -0
  204. package/studio/server/cache.ts +162 -11
  205. package/studio/server/cli-consumers.test.ts +494 -0
  206. package/studio/server/cli.test.ts +88 -0
  207. package/studio/server/cli.ts +240 -0
  208. package/studio/server/client-package.ts +25 -1
  209. package/studio/server/domain.test.ts +50 -40
  210. package/studio/server/domain.ts +83 -52
  211. package/studio/server/environment/dotenv-preview.test.ts +39 -0
  212. package/studio/server/environment/dotenv-preview.ts +27 -0
  213. package/studio/server/environment/files.test.ts +53 -0
  214. package/studio/server/{state/env.ts → environment/files.ts} +7 -48
  215. package/studio/server/{state → handoff}/copy.ts +21 -6
  216. package/studio/server/handoff/service.test.ts +22 -0
  217. package/studio/server/{state/handoff.ts → handoff/service.ts} +21 -14
  218. package/studio/server/index.ts +1 -1
  219. package/studio/server/instances/active.ts +72 -0
  220. package/studio/server/instances/deploy-record.test.ts +31 -0
  221. package/studio/server/instances/deploy-record.ts +37 -0
  222. package/studio/server/instances/deploy.ts +56 -0
  223. package/studio/server/{state/instance.test.ts → instances/probe.test.ts} +25 -21
  224. package/studio/server/instances/probe.ts +67 -0
  225. package/studio/server/instances/status.test.ts +12 -0
  226. package/studio/server/instances/status.ts +49 -0
  227. package/studio/server/introspect/anatomy/client-tree.ts +57 -0
  228. package/studio/server/introspect/anatomy/env-fields.ts +66 -0
  229. package/studio/server/introspect/anatomy/schema-definition.ts +53 -0
  230. package/studio/server/introspect/anatomy/source.ts +181 -0
  231. package/studio/server/introspect/anatomy/views/routes.ts +114 -0
  232. package/studio/server/introspect/anatomy/views.ts +53 -0
  233. package/studio/server/introspect/anatomy-extras.test.ts +106 -35
  234. package/studio/server/introspect/anatomy-extras.ts +7 -726
  235. package/studio/server/introspect/anatomy.test.ts +22 -14
  236. package/studio/server/introspect/anatomy.ts +52 -28
  237. package/studio/server/introspect/bundle.ts +15 -9
  238. package/studio/server/introspect/canonical-schema.test.ts +93 -309
  239. package/studio/server/introspect/canonical-schema.ts +257 -425
  240. package/studio/server/introspect/config-preview.test.ts +32 -0
  241. package/studio/server/introspect/config-preview.ts +119 -0
  242. package/studio/server/introspect/core-extractor.ts +18 -127
  243. package/studio/server/introspect/core.ts +1 -2
  244. package/studio/server/introspect/diff.test.ts +18 -18
  245. package/studio/server/introspect/diff.ts +44 -90
  246. package/studio/server/introspect/extractor.ts +23 -116
  247. package/studio/server/introspect/hash.ts +5 -2
  248. package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
  249. package/studio/server/introspect/overlay-tsmorph.ts +7 -1305
  250. package/studio/server/introspect/overlay.test.ts +26 -54
  251. package/studio/server/introspect/overlay.ts +10 -37
  252. package/studio/server/introspect/revision.test.ts +54 -0
  253. package/studio/server/introspect/revision.ts +49 -0
  254. package/studio/server/introspect/runtime.test.ts +83 -143
  255. package/studio/server/introspect/runtime.ts +40 -11
  256. package/studio/server/introspect/schema-ir-json.test.ts +70 -0
  257. package/studio/server/introspect/schema-ir-json.ts +151 -0
  258. package/studio/server/introspect/schema-refs.test.ts +43 -88
  259. package/studio/server/introspect/schema-refs.ts +14 -49
  260. package/studio/server/introspect/source-overlay/annotations.ts +14 -0
  261. package/studio/server/introspect/source-overlay/handlers.ts +161 -0
  262. package/studio/server/introspect/source-overlay/kernel-calls.ts +46 -0
  263. package/studio/server/introspect/source-overlay/project.ts +248 -0
  264. package/studio/server/introspect/source-overlay/spans.ts +330 -0
  265. package/studio/server/json.ts +46 -0
  266. package/studio/server/lifecycle.ts +5 -1
  267. package/studio/server/sse.test.ts +26 -0
  268. package/studio/server/sse.ts +4 -1
  269. package/studio/server/state/baseline.test.ts +223 -7
  270. package/studio/server/state/baseline.ts +148 -34
  271. package/studio/server/state/comments.test.ts +66 -1
  272. package/studio/server/state/comments.ts +173 -10
  273. package/studio/server/state/context.ts +34 -2
  274. package/studio/server/state/documents.ts +42 -1
  275. package/studio/server/state/integrations.ts +27 -1
  276. package/studio/server/state/layout.test.ts +34 -0
  277. package/studio/server/state/layout.ts +44 -17
  278. package/studio/server/state/settings.ts +29 -22
  279. package/studio/server/state/store.test.ts +122 -0
  280. package/studio/server/state/store.ts +66 -17
  281. package/studio/server/state/visibility.ts +22 -11
  282. package/studio/server/views/model.test.ts +57 -0
  283. package/studio/server/views/model.ts +66 -0
  284. package/studio/server/views/runtime.ts +40 -0
  285. package/studio/server/views/selection-repository.test.ts +31 -0
  286. package/studio/server/views/selection-repository.ts +60 -0
  287. package/studio/server/views/session.test.ts +73 -0
  288. package/studio/server/views/session.ts +166 -0
  289. package/studio/server/views/target.test.ts +78 -0
  290. package/studio/server/views/target.ts +222 -0
  291. package/studio/server/watch.test.ts +11 -11
  292. package/studio/server/watch.ts +9 -5
  293. package/studio/server/workspace/catalog.test.ts +17 -0
  294. package/studio/server/workspace/catalog.ts +43 -0
  295. package/studio/server/{state → workspace}/create.test.ts +1 -1
  296. package/studio/server/{state → workspace}/create.ts +1 -1
  297. package/studio/server/{state → workspace}/git.ts +1 -1
  298. package/studio/server/workspace/updates.ts +83 -0
  299. package/studio/server/workspace-state.ts +1 -1
  300. package/studio/server/workspace-watch.ts +2 -2
  301. package/studio/shared/contracts/README.md +16 -0
  302. package/studio/shared/contracts/agent.ts +270 -0
  303. package/studio/shared/contracts/runtime.ts +50 -0
  304. package/studio/shared/contracts/schema.ts +393 -0
  305. package/studio/shared/contracts/surface.test.ts +317 -0
  306. package/studio/shared/contracts/workspace.ts +265 -0
  307. package/studio/shared/layout.test.ts +116 -0
  308. package/studio/shared/schema/identity.test.ts +34 -0
  309. package/studio/shared/schema/identity.ts +68 -0
  310. package/studio/shared/types.ts +11 -1099
  311. package/studio/tsconfig.json +1 -1
  312. package/viewer/dist/main.js +40 -61
  313. package/.check-workspace.cjs +0 -40
  314. package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
  315. package/studio/client/dist/assets/index-BMdnsIJA.css +0 -1
  316. package/studio/client/dist/assets/index-D-vRV8w7.js +0 -8
  317. package/studio/client/dist/assets/index-LGSWRrk8.js +0 -81
  318. package/studio/server/state/catalog.ts +0 -117
  319. package/studio/server/state/instance.ts +0 -280
  320. package/studio/server/state/updates.ts +0 -63
  321. package/studio/server/state/views.test.ts +0 -286
  322. package/studio/server/state/views.ts +0 -535
@@ -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 { AstraleError } from '../errors'
4
+ import { bundle, schema } from '@astrale-os/sdk/schema'
4
5
 
5
- type JsonRecord = Record<string, unknown>
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's input/output from an installed Domain schema document. */
22
- export function describeCallableFromSchema(
22
+ /** Resolve one callable from an admitted installed Domain bundle. */
23
+ export function describeCallableFromBundle(
23
24
  path: Path,
24
- schema: unknown,
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
- const document = asRecord(schema)
30
- if (document === undefined || last === undefined) return undefined
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 owner = path.ast.steps.at(-2)
34
- if (
35
- owner?.kind === 'projection' &&
36
- (owner.projection.kind === 'class' || owner.projection.kind === 'interface')
37
- ) {
38
- const bag = asRecord(
39
- owner.projection.kind === 'class' ? document.classes : document.interfaces,
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
- const definition = asRecord(bag?.[owner.projection.name])
42
- const method = asRecord(asRecord(definition?.methods)?.[last.name])
43
- if (method !== undefined) {
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
- const matches = findNamedMethods(document, origin, path.raw, last.name, last.dispatch)
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 fn = asRecord(asRecord(document.functions)?.[last.projection.name])
70
- if (fn === undefined) return undefined
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: last.projection.name,
75
- ...callableFields(fn),
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 /:host.astrale.ai:class.Manager:createInstance. Method Paths are not Function nodes.',
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 findNamedMethods(
90
- schema: JsonRecord,
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
- path: string,
93
- method: string,
102
+ className: string,
103
+ callable: ResolvedMethod,
94
104
  dispatch: 'static' | 'instance',
95
- ): CallableDescription[] {
96
- const matches: CallableDescription[] = []
97
- for (const bag of [schema.classes, schema.interfaces]) {
98
- const definitions = asRecord(bag)
99
- if (definitions === undefined) continue
100
- for (const [className, definition] of Object.entries(definitions)) {
101
- const methodDef = asRecord(asRecord(asRecord(definition)?.methods)?.[method])
102
- if (methodDef === undefined) continue
103
- matches.push(
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(value: JsonRecord): Partial<CallableDescription> {
116
+ function callableFields(callable: ResolvedFunction | ResolvedMethod): Partial<CallableDescription> {
119
117
  return {
120
- ...(typeof value.description === 'string' ? { description: value.description } : {}),
121
- ...(value.auth === undefined ? {} : { auth: value.auth }),
122
- ...(value.input === undefined ? {} : { input: value.input }),
123
- ...(value.output === undefined ? {} : { output: value.output }),
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
- }
@@ -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
  }
@@ -194,7 +181,7 @@ Self-reference:
194
181
  Callable input/output lives on astrale introspect <path>.
195
182
 
196
183
  Examples:
197
- $ astrale introspect /:host.astrale.ai:class.Manager:createInstance
184
+ $ astrale introspect /:kernel.astrale.ai:class.Identity:whois
198
185
  $ astrale call /:blog.acme.com:class.Author:list limit=10
199
186
  $ astrale call '@self::deactivate'
200
187
  $ astrale call /:kernel.astrale.ai:function.journal --data '{"limit":5}' --json
@@ -203,7 +190,7 @@ Examples:
203
190
  {
204
191
  name: 'path',
205
192
  description:
206
- 'Operation path (e.g., /:host.astrale.ai:class.Manager:createInstance or /node::method)',
193
+ 'Operation path (e.g., /:kernel.astrale.ai:class.Identity:whois or /node::method)',
207
194
  },
208
195
  { name: 'params...', description: 'Params as key=value pairs', required: false },
209
196
  ],
@@ -1,5 +1,5 @@
1
1
  import { Path } from '@astrale-os/sdk/graph/path'
2
- import { 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'
@@ -15,6 +15,7 @@ import {
15
15
  } from '../../lib/admin-domain'
16
16
  import { listOwnedInstancesInContext, type OwnedInstanceInfo } from '../../lib/admin-instance'
17
17
  import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
18
+ import { fetchDomainPublication } from '../../lib/domain-publication'
18
19
  import { getActive } from '../../lib/instance'
19
20
  import { fatal, log, withSpinner } from '../../lib/log'
20
21
  import { isMachine, output } from '../../lib/output'
@@ -413,7 +414,7 @@ export async function installDirect(
413
414
  fn: async ({ session }) =>
414
415
  (await session.call(
415
416
  createPathCall(
416
- Path.project(syscalls.install.ref).raw,
417
+ Path.project(K.functions.install.ref).raw,
417
418
  directInstallCallInput(url, operation, opts.token),
418
419
  ),
419
420
  )) as DirectInstallResult,
@@ -429,11 +430,11 @@ export async function installDirect(
429
430
  log.dim(` operation: ${result.operation}`)
430
431
  // Belt-and-braces: the kernel-confirmed origin is authoritative. If it
431
432
  // aliases the host and the pre-install gate never consented to THAT
432
- // origin (lying or absent `/meta`), say so loudly after the fact.
433
+ // origin (lying or unavailable Publication), say so loudly after the fact.
433
434
  if (isIdentityOverride(installed.origin, host) && installed.origin !== consentedOrigin) {
434
435
  log.warn(
435
436
  `Installed origin "${installed.origin}" differs from the serving host "${host}" ` +
436
- `and was not confirmed before install (the worker's /meta did not declare it). ` +
437
+ `and was not confirmed before install (the worker Publication was unavailable). ` +
437
438
  `Every ${installed.origin}/* call on this instance now routes to ${host}.`,
438
439
  )
439
440
  }
@@ -478,10 +479,10 @@ export function isIdentityOverride(origin: string, host: string): boolean {
478
479
  * Claiming an origin that differs from the serving host is an explicit actAs
479
480
  * and needs typed consent (or `--allow-identity-override` in scripts).
480
481
  *
481
- * The pre-install check reads the worker's self-reported `/meta.origin`,
482
+ * The pre-install check reads the worker's canonical Publication origin,
482
483
  * so it is consent UX, not enforcement — a hostile worker can lie here, and
483
484
  * the kernel anchors the cryptographic identity (`iss`) on the real URL
484
- * regardless. When `/meta` is unreachable or silent on the origin, the gate
485
+ * regardless. When the Publication is unreachable or invalid, the gate
485
486
  * degrades to a warning and the kernel-confirmed origin is re-checked after
486
487
  * install (see `format` above).
487
488
  *
@@ -496,7 +497,8 @@ async function ensureIdentityOverrideConsent(
496
497
  const origin = await probeDeclaredOrigin(url)
497
498
  if (origin === undefined) {
498
499
  log.warn(
499
- `Could not read a declared origin from ${new URL('/meta', url).href} — ` +
500
+ `Could not read a declared origin from ` +
501
+ `${new URL('/.well-known/astrale/domain.json', url).href} — ` +
500
502
  `skipping the pre-install identity check (the installed origin is verified after install).`,
501
503
  )
502
504
  return undefined
@@ -534,19 +536,13 @@ async function ensureIdentityOverrideConsent(
534
536
  return origin
535
537
  }
536
538
 
537
- /** Best-effort read of the worker's declared origin from its `/meta` probe. */
539
+ /** Best-effort read of the worker's origin from its admitted canonical Publication. */
538
540
  export async function probeDeclaredOrigin(url: string): Promise<string | undefined> {
539
541
  try {
540
- const res = await fetch(new URL('/meta', url), { signal: AbortSignal.timeout(10_000) })
541
- if (!res.ok) return undefined
542
- // SDK workers serve `origin`; `domainName` is the pre-Kernel-V2 name, kept
543
- // as a fallback for workers deployed before the rename.
544
- const body = (await res.json()) as { origin?: unknown; domainName?: unknown }
545
- const declared = body.origin ?? body.domainName
546
- return typeof declared === 'string' && declared.length > 0 ? declared : undefined
542
+ return (await fetchDomainPublication(url, AbortSignal.timeout(10_000))).origin
547
543
  } catch {
548
- // Unreachable /meta is not fatal here: the caller warns and the install
549
- // itself will surface a dead worker with its own error.
544
+ // An unreachable or invalid Publication is not fatal here: the caller warns
545
+ // and the install itself will surface a dead worker with its own error.
550
546
  return undefined
551
547
  }
552
548
  }
@@ -6,6 +6,7 @@ import type { CommandDefinition } from '../../program/index'
6
6
 
7
7
  import { listAdminDomains, type DomainInfo } from '../../lib/admin-domain'
8
8
  import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
9
+ import { fetchDomainPublication } from '../../lib/domain-publication'
9
10
  import { fatal, withSpinner } from '../../lib/log'
10
11
  import { isMachine, presentList } from '../../lib/output'
11
12
 
@@ -23,7 +24,7 @@ type ListOpts = KernelCommandOpts &
23
24
  /** A catalog entry, optionally enriched with a live `--check` probe. */
24
25
  export type DomainRow = DomainInfo & {
25
26
  reachable?: boolean
26
- schemaHash?: string
27
+ schemaRevision?: string
27
28
  checkError?: string | null
28
29
  }
29
30
 
@@ -72,9 +73,9 @@ Behavior:
72
73
  or with --json/--raw (agent-friendly — full DomainInfo objects). -q prints
73
74
  one install URL per line (pipeable into \`domain install\`); --count prints
74
75
  only the number. --default-only keeps the install-by-default entries (what
75
- every new instance gets via alphaCreate). --check probes each published URL's
76
- /meta and adds a live/unreachable STATUS column (+ reachable/schemaHash in
77
- machine output).
76
+ every new instance receives during Admin-managed provisioning). --check probes each published URL's
77
+ canonical Publication and adds a live/unreachable STATUS column
78
+ (+ reachable/schemaRevision in machine output).
78
79
 
79
80
  The admin kernel is selected like every admin op — the configured default,
80
81
  or --admin <bookmark> / --admin-url <url>.
@@ -87,7 +88,10 @@ Examples:
87
88
  `,
88
89
  options: [
89
90
  ...ADMIN_TARGET_OPTIONS,
90
- { flags: '--check', description: "Probe each domain's URL for reachability + schema hash" },
91
+ {
92
+ flags: '--check',
93
+ description: "Probe each domain's canonical Publication + schema revision",
94
+ },
91
95
  { flags: '--default-only', description: 'Only show install-by-default domains' },
92
96
  { flags: '-q, --quiet', description: 'One install URL per line (unix-pipeable)' },
93
97
  { flags: '--count', description: 'Print only the number of published domains' },
@@ -103,7 +107,7 @@ Examples:
103
107
  const filtered = opts.defaultOnly ? list.filter((d) => d.installByDefault) : list
104
108
  filtered.sort(byDefaultThenName)
105
109
  if (!opts.check) return filtered as DomainRow[]
106
- // Reachability is a direct client-side /meta fetch per entry, in
110
+ // Reachability is a direct client-side Publication fetch per entry, in
107
111
  // parallel — no admin round-trip, and version-independent of the
108
112
  // admin worker (mirrors `domain install`'s probeDeclaredOrigin).
109
113
  return Promise.all(filtered.map(probe))
@@ -129,19 +133,20 @@ export function byDefaultThenName(a: DomainInfo, b: DomainInfo): number {
129
133
 
130
134
  /**
131
135
  * Enrich one entry with a reachability probe: fetch the published worker's
132
- * `/meta` (the same endpoint the admin `DomainEntry.check` hits) and read its
133
- * schema hash. A dead / missing url is itself a result, not a throw.
136
+ * canonical Publication and read its schema revision. A dead or missing URL is
137
+ * itself a result, not a throw.
134
138
  */
135
- async function probe(d: DomainInfo): Promise<DomainRow> {
139
+ export async function probe(d: DomainInfo): Promise<DomainRow> {
136
140
  if (!d.url) return { ...d, reachable: false, checkError: 'no url published' }
137
141
  try {
138
- const res = await fetch(new URL('/meta', d.url), { signal: AbortSignal.timeout(10_000) })
139
- if (!res.ok) return { ...d, reachable: false, checkError: `meta HTTP ${res.status}` }
140
- const meta = (await res.json()) as { schemaHash?: unknown }
142
+ const deployed = await fetchDomainPublication(d.url, AbortSignal.timeout(10_000))
143
+ if (deployed.origin !== d.origin) {
144
+ throw new Error(`Domain origin mismatch: deployed=${deployed.origin} expected=${d.origin}`)
145
+ }
141
146
  return {
142
147
  ...d,
143
148
  reachable: true,
144
- ...(typeof meta.schemaHash === 'string' ? { schemaHash: meta.schemaHash } : {}),
149
+ schemaRevision: deployed.schema.revision,
145
150
  checkError: null,
146
151
  }
147
152
  } catch (err) {
@@ -3,9 +3,11 @@ import chalk from 'chalk'
3
3
  import type { KernelCommandOpts } from '../../connection'
4
4
  import type { CommandDefinition } from '../../program/index'
5
5
 
6
+ import { formatKernelError } from '../../connection/errors'
6
7
  import { publishAdminDomain } from '../../lib/admin-domain'
7
8
  import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
8
- import { fatal, withSpinner } from '../../lib/log'
9
+ import { domainPublicationUrl } from '../../lib/domain-publication'
10
+ import { withSpinner } from '../../lib/log'
9
11
  import { isMachine, output } from '../../lib/output'
10
12
  import { promptText } from '../../lib/prompt'
11
13
  import { isHttpUrl, validateName, validateUrl } from '../../lib/validation'
@@ -48,9 +50,9 @@ Behavior:
48
50
 
49
51
  Publishing only makes the domain INSTALLABLE. Mount it on an instance with
50
52
  \`astrale domain install <url>\` (or rely on the admin's install-by-default
51
- policy). This is usually invoked for you by \`astrale-domain publish\` (which
52
- registers the already-deployed URL it does NOT deploy) or by
53
- \`astrale-domain deploy --publish\` (deploy AND register in one step).
53
+ policy). Deployment and catalog publication are separate owners: run the
54
+ project's \`astrale-domain deploy <environment>\`, then publish its observed
55
+ public URL with this command.
54
56
 
55
57
  Run in a terminal with flags omitted and it PROMPTS for origin / name /
56
58
  public-url (origin defaults to the URL host, name to the origin's first
@@ -68,17 +70,16 @@ Examples:
68
70
  { flags: '--description <text>', description: 'Optional human description for the catalog' },
69
71
  {
70
72
  flags: '--install-by-default',
71
- description: 'Mark the domain for install on every new instance (alphaCreate)',
73
+ description: 'Mark the domain for install on every new instance',
72
74
  },
73
75
  ],
74
76
  // No positional arguments → Commander passes (opts, command); `opts` is first.
75
77
  action: async (opts: PublishOpts) => {
76
78
  try {
77
79
  // Interactive fill (TTY only): a human running this by hand is prompted for
78
- // any missing field. The primary caller `astrale-domain publish` always
79
- // passes every flag, so it never prompts. No TTY / --ci / --no-prompt / CI
80
- // env no prompt: fall straight through to the required-flag error below,
81
- // so a piped / agent / LLM run fails fast instead of hanging on a read.
80
+ // any missing field. Automation passes every flag. No TTY / --ci /
81
+ // --no-prompt / CI env means no prompt: fall straight through to the
82
+ // required-flag error below so a piped or agent run fails fast.
82
83
  const interactive = !!process.stdin.isTTY && !(opts.ci || opts.noPrompt || process.env.CI)
83
84
  let { origin, name, publicUrl } = opts
84
85
  if (interactive) {
@@ -102,6 +103,7 @@ Examples:
102
103
  validateName(origin, 'origin')
103
104
  validateName(name, 'domain')
104
105
  validateUrl(publicUrl)
106
+ const discoveryUrl = domainPublicationUrl(publicUrl).href
105
107
 
106
108
  const result = await withSpinner(
107
109
  `Publishing ${name} → ${publicUrl}`,
@@ -110,7 +112,7 @@ Examples:
110
112
  publishAdminDomain(opts, {
111
113
  origin,
112
114
  name,
113
- url: publicUrl,
115
+ url: discoveryUrl,
114
116
  ...(opts.description ? { description: opts.description } : {}),
115
117
  ...(opts.installByDefault ? { installByDefault: true } : {}),
116
118
  }),
@@ -127,7 +129,8 @@ Examples:
127
129
  return
128
130
  }
129
131
  } catch (e) {
130
- fatal(e)
132
+ await formatKernelError(e, isMachine(opts), undefined, opts.debug)
133
+ process.exit(1)
131
134
  }
132
135
  },
133
136
  } satisfies CommandDefinition
@@ -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
  }
@@ -60,7 +59,7 @@ Examples:
60
59
  $ astrale get /:notes.example.dev:class.Note
61
60
  $ astrale get @abc123 --json
62
61
  $ astrale get @self
63
- $ astrale get /:host.astrale.ai --schema
62
+ $ astrale get /:kernel.astrale.ai --schema
64
63
  `,
65
64
  arguments: [{ name: 'target', description: 'Canonical Node Path or @id' }],
66
65
  options: [{ flags: '--schema', description: 'Include schema-valued properties' }],