@astrale-os/cli 1.0.0-beta.1 → 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 +51471 -56290
  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 +39 -164
  50. package/src/admin/instance/client.ts +35 -124
  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 +48 -19
  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 +38 -14
  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,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,15 +1,12 @@
1
1
  import type { CommandDefinition } from '../../program/index'
2
2
 
3
+ import { formatKernelError } from '../../connection/errors'
3
4
  import { ADMIN_TARGET_OPTIONS } from '../../lib/admin-target'
4
- import { fatal } from '../../lib/log'
5
5
  import { isMachine, output } from '../../lib/output'
6
6
  import { promptText } from '../../lib/prompt'
7
7
  import { provisionInstance, type ProvisionOpts } from '../../lib/provision-instance'
8
8
  import { validateSlug } from '../../lib/validation'
9
9
 
10
- // Re-exported for tests + `astrale setup`, which share the provisioning saga.
11
- export { assertAlphaCreateIdentity, parseEligibleHostIds } from '../../lib/provision-instance'
12
-
13
10
  /** inquirer `validate` for a slug: true when valid, else the human message. */
14
11
  function slugError(value: string): true | string {
15
12
  try {
@@ -22,12 +19,12 @@ function slugError(value: string): true | string {
22
19
 
23
20
  export default {
24
21
  name: 'create',
25
- description: 'Provision an alpha instance through the admin kernel (Instance.alphaCreate)',
22
+ description: 'Provision an instance through the Admin control plane',
26
23
  afterHelpText: `
27
24
  Behavior:
28
- Calls Instance.alphaCreate on the configured admin kernel. The caller must be
29
- logged in with WorkOS. When --host-id is omitted, the admin kernel chooses the
30
- caller's single eligible host. The new instance becomes the active instance.
25
+ Requests a new Instance from the configured Admin Domain. The caller must be
26
+ logged in with WorkOS. Admin owns infrastructure placement. The new instance
27
+ becomes the active instance.
31
28
 
32
29
  Run with no slug in a terminal and it prompts for one (validated live). With
33
30
  no TTY — or --ci / --no-prompt — the slug argument is required up front, so
@@ -38,13 +35,7 @@ Examples:
38
35
  $ astrale instance create demo
39
36
  `,
40
37
  arguments: [{ name: 'id', description: 'Instance slug', required: false }],
41
- options: [
42
- ...ADMIN_TARGET_OPTIONS,
43
- {
44
- flags: '--host-id <id>',
45
- description: 'Advanced: host node id to provision on when multiple hosts are available',
46
- },
47
- ],
38
+ options: [...ADMIN_TARGET_OPTIONS],
48
39
  action: async (id: string | undefined, opts: ProvisionOpts) => {
49
40
  try {
50
41
  // Interactive (TTY only): prompt for the slug when omitted, with live
@@ -63,7 +54,8 @@ Examples:
63
54
  return
64
55
  }
65
56
  } catch (e) {
66
- fatal(e, opts)
57
+ await formatKernelError(e, isMachine(opts), undefined, opts.debug)
58
+ process.exit(1)
67
59
  }
68
60
  },
69
61
  } satisfies CommandDefinition
@@ -1,10 +1,12 @@
1
1
  import type { KernelCommandOpts } from '../../connection'
2
2
  import type { CommandDefinition } from '../../program/index'
3
3
 
4
+ import { AdminInstanceNotFoundError } from '../../admin/instance'
5
+ import { formatKernelError } from '../../connection/errors'
4
6
  import { deleteOwnedInstance } from '../../lib/admin-instance'
5
7
  import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
6
8
  import { clearActive, readInstances, removeInstance, resolveInstanceKey } from '../../lib/instance'
7
- import { fatal, log, withSpinner } from '../../lib/log'
9
+ import { log, withSpinner } from '../../lib/log'
8
10
  import { isMachine, output } from '../../lib/output'
9
11
 
10
12
  type DeleteOpts = KernelCommandOpts &
@@ -18,10 +20,9 @@ export default {
18
20
  afterHelpText: `
19
21
  Behavior:
20
22
  Calls Instance.delete on the configured admin kernel: best-effort decommission
21
- of the instance's routing-registry entry + WorkOS org (frees the slug), then
22
- removes the Instance record. The host kernel itself is NOT deregistered (the
23
- deprovisioning saga is a follow-up). A same-name local bookmark is removed
24
- after the delete succeeds unless --keep-bookmark is passed.
23
+ of the instance's route and tenant reservation, then removes the Instance
24
+ projection. A same-name local bookmark is removed after the delete succeeds
25
+ unless --keep-bookmark is passed.
25
26
  `,
26
27
  arguments: [{ name: 'id', description: 'Instance slug', required: true }],
27
28
  options: [
@@ -53,14 +54,16 @@ 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)
62
64
  }
63
- fatal(e, opts)
65
+ await formatKernelError(e, isMachine(opts), undefined, opts.debug)
66
+ process.exit(1)
64
67
  }
65
68
  },
66
69
  } satisfies CommandDefinition
@@ -7,7 +7,7 @@ import type { CommandDefinition } from '../../program/index'
7
7
  import { AstraleError } from '../../errors'
8
8
  import { listOwnedInstances } from '../../lib/admin-instance'
9
9
  import {
10
- formatInstanceLocation,
10
+ formatInstanceState,
11
11
  type InstanceInfo,
12
12
  type OwnedInstanceInfo,
13
13
  } from '../../lib/admin-instance'
@@ -136,7 +136,7 @@ export function buildInstanceRows(
136
136
  name: twin?.active ? `${item.slug} ${chalk.green('*')}` : item.slug,
137
137
  kind: 'managed',
138
138
  url: item.url ?? '',
139
- extra: [formatInstanceLocation(item), twin ? formatBookmarkConnection(twin) : '']
139
+ extra: [formatInstanceState(item), twin ? formatBookmarkConnection(twin) : '']
140
140
  .filter(Boolean)
141
141
  .join(' · '),
142
142
  })
@@ -3,44 +3,90 @@ 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 { AstraleError } from '../../errors'
7
- import { statusOwnedInstance } from '../../lib/admin-instance'
8
+ import { statusOwnedInstance, type InstanceInfo } from '../../lib/admin-instance'
8
9
  import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
9
- import { fatal, log, withSpinner } from '../../lib/log'
10
+ import { resolveInstance } from '../../lib/instance'
11
+ import { log, withSpinner } from '../../lib/log'
10
12
  import { isMachine, output, type RawOutputOpts } from '../../lib/output'
13
+ import { probeBookmark } from './use'
11
14
 
12
- type StatusOpts = KernelCommandOpts & AdminTargetCommandOpts & RawOutputOpts
15
+ type StatusOpts = KernelCommandOpts &
16
+ AdminTargetCommandOpts &
17
+ RawOutputOpts & { bookmarked?: boolean }
18
+
19
+ interface BookmarkStatus {
20
+ readonly slug: string
21
+ readonly url: string
22
+ readonly issuer: string
23
+ readonly kind: 'bookmark'
24
+ readonly state: 'ready'
25
+ }
13
26
 
14
27
  export default {
15
28
  name: 'status',
16
- description: 'Show admin instance status',
29
+ description: 'Show managed instance status or probe a bookmark',
17
30
  arguments: [{ name: 'id', description: 'Instance slug', required: true }],
18
- options: [...ADMIN_TARGET_OPTIONS],
31
+ options: [
32
+ ...ADMIN_TARGET_OPTIONS,
33
+ { flags: '--bookmarked', description: 'Probe a locally bookmarked kernel connection' },
34
+ ],
19
35
  action: async (id: string, opts: StatusOpts) => {
20
36
  try {
21
37
  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
- }),
38
+ resolveStatus(id, opts),
30
39
  )
31
40
  if (isMachine(opts)) {
32
41
  output(result, opts)
33
42
  return
34
43
  }
35
44
  console.log(`${chalk.bold(result.slug)} ${chalk.dim(result.url)}`)
36
- log.dim(` state: ${result.state}${result.phase ? ` (${result.phase})` : ''}`)
37
- if (result.error) log.dim(` error: ${result.error}`)
38
- if (result.region) log.dim(` region: ${result.region}`)
39
- if (result.hostId) log.dim(` host: ${result.hostId}`)
40
- if (result.organizationId) log.dim(` organization: ${result.organizationId}`)
41
- if (result.createdAt) log.dim(` created: ${result.createdAt}`)
45
+ const phase = 'phase' in result ? result.phase : undefined
46
+ log.dim(` state: ${result.state}${phase ? ` (${phase})` : ''}`)
47
+ if ('error' in result && result.error) log.dim(` error: ${result.error}`)
48
+ if ('organizationId' in result && result.organizationId)
49
+ log.dim(` organization: ${result.organizationId}`)
50
+ if ('createdAt' in result && result.createdAt) log.dim(` created: ${result.createdAt}`)
42
51
  } catch (e) {
43
- fatal(e, opts)
52
+ await formatKernelError(e, isMachine(opts), undefined, opts.debug)
53
+ process.exit(1)
44
54
  }
45
55
  },
46
56
  } satisfies CommandDefinition
57
+
58
+ export interface StatusDependencies {
59
+ readonly statusOwnedInstance: typeof statusOwnedInstance
60
+ readonly resolveInstance: typeof resolveInstance
61
+ readonly probeBookmark: typeof probeBookmark
62
+ }
63
+
64
+ const defaultStatusDependencies: StatusDependencies = Object.freeze({
65
+ statusOwnedInstance,
66
+ resolveInstance,
67
+ probeBookmark,
68
+ })
69
+
70
+ export async function resolveStatus(
71
+ id: string,
72
+ opts: StatusOpts,
73
+ dependencies: StatusDependencies = defaultStatusDependencies,
74
+ ): Promise<InstanceInfo | BookmarkStatus> {
75
+ if (!opts.bookmarked) return dependencies.statusOwnedInstance(opts, id)
76
+ if (opts.admin !== undefined || opts.adminUrl !== undefined || opts.domainIssuer !== undefined) {
77
+ throw new AstraleError(
78
+ 'INVALID_FLAG',
79
+ '--bookmarked cannot be combined with Admin target options.',
80
+ 'Remove --bookmarked to inspect managed lifecycle, or remove the Admin options to probe a local bookmark.',
81
+ )
82
+ }
83
+ const bookmark = await dependencies.resolveInstance(id, undefined, { persist: false })
84
+ await dependencies.probeBookmark(bookmark)
85
+ return Object.freeze({
86
+ slug: bookmark.name,
87
+ url: bookmark.url,
88
+ issuer: bookmark.issuer ?? bookmark.url,
89
+ kind: 'bookmark',
90
+ state: 'ready',
91
+ })
92
+ }
@@ -3,7 +3,7 @@ import type { CommandDefinition } from '../../program/index'
3
3
 
4
4
  import { AstraleError } from '../../errors'
5
5
  import { getDefault, setDefault } from '../../identity/index'
6
- import { listOwnedInstances } from '../../lib/admin-instance'
6
+ import { listOwnedInstancesWithIdentity } from '../../lib/admin-instance'
7
7
  import { ADMIN_TARGET_OPTIONS } from '../../lib/admin-target'
8
8
  import { fetchWithCaFile } from '../../lib/ca-fetch'
9
9
  import {
@@ -143,7 +143,7 @@ const defaultBookmarkProbeDependencies: BookmarkProbeDependencies = Object.freez
143
143
 
144
144
  async function resolveUseTarget(name: string, opts: UseOpts): Promise<ResolvedInstance> {
145
145
  const [store, managed] = await Promise.all([readInstances(), fetchManagedInstances(name, opts)])
146
- const candidates = collectInstanceCandidates(name, store, managed)
146
+ const candidates = collectInstanceCandidates(name, store, managed.instances)
147
147
 
148
148
  if (candidates.length === 0) {
149
149
  throw new AstraleError(
@@ -168,12 +168,13 @@ async function resolveUseTarget(name: string, opts: UseOpts): Promise<ResolvedIn
168
168
  }
169
169
 
170
170
  assertManagedReady(chosen.info)
171
- const { repointedFrom } = await upsertManagedBookmark(
172
- chosen.key,
173
- chosen.info.slug,
174
- chosen.url,
175
- chosen.info.organizationId,
176
- )
171
+ const { repointedFrom } = await upsertManagedBookmark({
172
+ key: chosen.key,
173
+ slug: chosen.info.slug,
174
+ url: chosen.url,
175
+ ...(chosen.info.organizationId ? { organizationId: chosen.info.organizationId } : {}),
176
+ ...(managed.identity ? { defaultIdentity: managed.identity } : {}),
177
+ })
177
178
  if (repointedFrom) {
178
179
  log.warn(`Bookmark "${chosen.key}" repointed: ${repointedFrom} → ${chosen.url}`)
179
180
  }
@@ -185,16 +186,19 @@ async function resolveUseTarget(name: string, opts: UseOpts): Promise<ResolvedIn
185
186
  * Best-effort: an unreachable or unauthenticated admin kernel degrades to
186
187
  * bookmark-only resolution.
187
188
  */
188
- async function fetchManagedInstances(name: string, opts: UseOpts): Promise<OwnedInstanceInfo[]> {
189
+ async function fetchManagedInstances(
190
+ name: string,
191
+ opts: UseOpts,
192
+ ): Promise<{ readonly instances: OwnedInstanceInfo[]; readonly identity?: string }> {
189
193
  try {
190
194
  validateSlug(name)
191
195
  } catch {
192
- return []
196
+ return { instances: [] }
193
197
  }
194
198
  try {
195
- return await listOwnedInstances(opts)
199
+ return await listOwnedInstancesWithIdentity(opts)
196
200
  } catch {
197
- return []
201
+ return { instances: [] }
198
202
  }
199
203
  }
200
204
 
@@ -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
  }
@@ -59,7 +59,7 @@ export function parseIntrospectTarget(target: string): { origin: string; path: P
59
59
  throw new AstraleError(
60
60
  'NOT_A_DOMAIN',
61
61
  'introspect requires a Domain origin or Path, not an @id.',
62
- 'Example: astrale introspect host.astrale.ai or /:host.astrale.ai:class.Manager:createInstance',
62
+ 'Example: astrale introspect kernel.astrale.ai or /:kernel.astrale.ai:class.Identity:whois',
63
63
  )
64
64
  }
65
65
  const raw = target.startsWith('/') ? target : `/:${target}`
@@ -85,28 +85,21 @@ 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',
98
91
  afterHelpText: `
99
92
  Behavior:
100
93
  Calls the public Kernel introspect syscall for one installed Domain.
101
- A bare origin (host.astrale.ai or /:host.astrale.ai) prints installation
94
+ A bare origin (kernel.astrale.ai or /:kernel.astrale.ai) prints installation
102
95
  state, target, source, readiness, and capabilities. --bundle includes the
103
96
  schema bundle. A method or Function Path projects that callable's
104
97
  input/output from the installed bundle.
105
98
 
106
99
  Examples:
107
- $ astrale introspect host.astrale.ai
108
- $ astrale introspect /:host.astrale.ai --bundle
109
- $ astrale introspect /:host.astrale.ai:class.Manager:createInstance
100
+ $ astrale introspect kernel.astrale.ai
101
+ $ astrale introspect /:kernel.astrale.ai --bundle
102
+ $ astrale introspect /:kernel.astrale.ai:class.Identity:whois
110
103
  $ astrale introspect /:kernel.astrale.ai:function.journal
111
104
  `,
112
105
  arguments: [{ name: 'target', description: 'Domain origin or canonical Path' }],
@@ -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
  {