@astrale-os/cli 1.0.0-beta.3 → 1.0.0-beta.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/README.md +84 -6
  2. package/dist/astrale.js +57586 -60753
  3. package/dist/public/connect-core.js +2158 -30706
  4. package/dist/public/keys/index.js +1016 -29541
  5. package/dist/public/paths/index.js +370 -28654
  6. package/dist/types/admin/contract.d.ts +26 -0
  7. package/dist/types/admin/instance/client.d.ts +20 -0
  8. package/dist/types/admin/instance/index.d.ts +2 -0
  9. package/dist/types/admin/instance/model.d.ts +6 -10
  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 +11 -36
  28. package/dist/types/lib/config.d.ts +13 -59
  29. package/dist/types/lib/failure-debug.d.ts +1 -0
  30. package/dist/types/lib/idp.d.ts +25 -173
  31. package/dist/types/lib/instance-target.d.ts +1 -1
  32. package/dist/types/lib/instance.d.ts +30 -88
  33. package/dist/types/lib/log.d.ts +5 -3
  34. package/dist/types/lib/output.d.ts +1 -1
  35. package/dist/types/lib/proc.d.ts +43 -0
  36. package/dist/types/lib/self.d.ts +9 -0
  37. package/dist/types/lib/update.d.ts +109 -0
  38. package/dist/types/lib/validation.d.ts +4 -1
  39. package/dist/types/state/exchange-credentials.d.ts +6 -2
  40. package/dist/types/state/files.d.ts +2 -0
  41. package/dist/types/state/index.d.ts +3 -2
  42. package/dist/types/state/paths.d.ts +2 -0
  43. package/dist/types/state/session-routes.d.ts +10 -0
  44. package/package.json +16 -19
  45. package/src/__tests__/fixtures/publication.ts +20 -0
  46. package/src/admin/.spec/architecture.md +12 -5
  47. package/src/admin/__tests__/fixture.ts +25 -0
  48. package/src/admin/catalog/.spec/api.d.ts +1 -3
  49. package/src/admin/catalog/.spec/architecture.md +5 -4
  50. package/src/admin/catalog/__tests__/client.test.ts +143 -69
  51. package/src/admin/catalog/client.ts +40 -54
  52. package/src/admin/catalog/model.ts +3 -4
  53. package/src/admin/contract.ts +47 -0
  54. package/src/admin/graph/.spec/api.d.ts +4 -10
  55. package/src/admin/graph/nodes.ts +1 -1
  56. package/src/admin/instance/.spec/api.d.ts +5 -9
  57. package/src/admin/instance/.spec/architecture.md +7 -7
  58. package/src/admin/instance/__tests__/client.test.ts +157 -175
  59. package/src/admin/instance/client.ts +39 -128
  60. package/src/admin/instance/index.ts +1 -2
  61. package/src/admin/instance/model.ts +12 -18
  62. package/src/commands/__tests__/admin-instance.test.ts +11 -8
  63. package/src/commands/__tests__/call-describe.test.ts +46 -33
  64. package/src/commands/__tests__/call.test.ts +34 -0
  65. package/src/commands/__tests__/domain-install-operation.test.ts +30 -22
  66. package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
  67. package/src/commands/__tests__/domain-list.test.ts +5 -25
  68. package/src/commands/__tests__/install-direct.test.ts +115 -8
  69. package/src/commands/__tests__/install-identity-override.test.ts +6 -17
  70. package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
  71. package/src/commands/__tests__/instance-status.test.ts +139 -0
  72. package/src/commands/__tests__/introspect-parse.test.ts +80 -1
  73. package/src/commands/__tests__/logs.test.ts +250 -2
  74. package/src/commands/__tests__/read-commands.test.ts +119 -11
  75. package/src/commands/__tests__/token-ttl.test.ts +49 -4
  76. package/src/commands/__tests__/update.test.ts +25 -3
  77. package/src/commands/__tests__/view.test.ts +50 -2
  78. package/src/commands/auth/logout.ts +2 -1
  79. package/src/commands/browser.ts +29 -0
  80. package/src/commands/call-describe.ts +60 -68
  81. package/src/commands/call.ts +71 -59
  82. package/src/commands/domain/install.ts +73 -46
  83. package/src/commands/domain/list.ts +1 -1
  84. package/src/commands/domain/publish.ts +14 -11
  85. package/src/commands/domain/uninstall.ts +2 -2
  86. package/src/commands/get.ts +15 -16
  87. package/src/commands/identity/__tests__/register.test.ts +3 -3
  88. package/src/commands/identity/register.ts +8 -8
  89. package/src/commands/instance/create.ts +8 -16
  90. package/src/commands/instance/delete.ts +12 -9
  91. package/src/commands/instance/list.ts +2 -2
  92. package/src/commands/instance/status.ts +66 -20
  93. package/src/commands/instance/use.ts +16 -12
  94. package/src/commands/introspect.ts +53 -41
  95. package/src/commands/logs.ts +123 -36
  96. package/src/commands/mutate.ts +6 -5
  97. package/src/commands/query.ts +39 -36
  98. package/src/commands/session/analyze.ts +26 -4
  99. package/src/commands/token.ts +42 -27
  100. package/src/commands/ui/__tests__/commands.test.ts +152 -0
  101. package/src/commands/ui/add.ts +51 -0
  102. package/src/commands/ui/doctor.ts +21 -0
  103. package/src/commands/ui/init.ts +38 -0
  104. package/src/commands/ui/list.ts +26 -0
  105. package/src/commands/ui/preset-apply.ts +19 -0
  106. package/src/commands/ui/preset-list.ts +12 -0
  107. package/src/commands/ui/shared.ts +25 -0
  108. package/src/commands/update.ts +90 -48
  109. package/src/commands/view.ts +82 -37
  110. package/src/connection/.spec/api.d.ts +7 -8
  111. package/src/connection/.spec/architecture.md +14 -5
  112. package/src/connection/.spec/flows/session.ts +2 -2
  113. package/src/connection/.spec/laws/connection.ts +6 -2
  114. package/src/connection/.spec/layout.ts +1 -0
  115. package/src/connection/__tests__/auth.test.ts +1 -0
  116. package/src/connection/__tests__/credential.test.ts +21 -1
  117. package/src/connection/__tests__/errors.test.ts +27 -20
  118. package/src/connection/__tests__/exchange.test.ts +64 -11
  119. package/src/connection/__tests__/failure-fixtures.ts +39 -0
  120. package/src/connection/__tests__/failure-safety.test.ts +75 -0
  121. package/src/connection/__tests__/self.test.ts +31 -11
  122. package/src/connection/__tests__/session.test.ts +8 -4
  123. package/src/connection/__tests__/target.test.ts +2 -0
  124. package/src/connection/auth.ts +13 -0
  125. package/src/connection/call.ts +1 -1
  126. package/src/connection/credential.ts +2 -2
  127. package/src/connection/errors.ts +1 -355
  128. package/src/connection/exchange.ts +110 -61
  129. package/src/connection/failure/classify.ts +110 -0
  130. package/src/connection/failure/index.ts +18 -0
  131. package/src/connection/failure/model.ts +28 -0
  132. package/src/connection/failure/render.ts +116 -0
  133. package/src/connection/reasons.ts +16 -0
  134. package/src/connection/self.ts +39 -21
  135. package/src/connection/session.ts +27 -9
  136. package/src/errors.ts +3 -2
  137. package/src/graph/.spec/api.d.ts +13 -6
  138. package/src/graph/.spec/laws/documents.ts +7 -7
  139. package/src/graph/.spec/layout.ts +9 -1
  140. package/src/graph/__tests__/mutation.test.ts +2 -2
  141. package/src/graph/__tests__/query.test.ts +35 -9
  142. package/src/graph/class.ts +30 -0
  143. package/src/graph/index.ts +1 -0
  144. package/src/graph/query.ts +27 -40
  145. package/src/identity/.spec/api.d.ts +2 -2
  146. package/src/identity/__tests__/fixtures/registry-journey.ts +13 -1
  147. package/src/identity/__tests__/registration.test.ts +1 -1
  148. package/src/identity/__tests__/registry.test.ts +4 -0
  149. package/src/identity/registration.ts +3 -3
  150. package/src/identity/registry.ts +2 -0
  151. package/src/keys/__tests__/keys.test.ts +4 -0
  152. package/src/keys/credential.ts +1 -0
  153. package/src/lib/__tests__/admin-target.test.ts +12 -0
  154. package/src/lib/__tests__/binary.test.ts +16 -1
  155. package/src/lib/__tests__/browser-retention.test.ts +212 -0
  156. package/src/lib/__tests__/command-dx.test.ts +1 -1
  157. package/src/lib/__tests__/config.test.ts +27 -0
  158. package/src/lib/__tests__/domain-publication.test.ts +10 -16
  159. package/src/lib/__tests__/idp.test.ts +34 -4
  160. package/src/lib/__tests__/instance-candidates.test.ts +1 -3
  161. package/src/lib/__tests__/instance-target.test.ts +7 -39
  162. package/src/lib/__tests__/log-errors.test.ts +64 -0
  163. package/src/lib/__tests__/skills.test.ts +834 -0
  164. package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
  165. package/src/lib/__tests__/update.test.ts +264 -11
  166. package/src/lib/__tests__/view-assets.test.ts +25 -0
  167. package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
  168. package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
  169. package/src/lib/__tests__/view-open-intent.test.ts +0 -2
  170. package/src/lib/__tests__/view-server.test.ts +26 -3
  171. package/src/lib/__tests__/view-session.test.ts +2 -1
  172. package/src/lib/admin-instance.ts +14 -8
  173. package/src/lib/admin-target.ts +21 -2
  174. package/src/lib/binary.ts +22 -5
  175. package/src/lib/browser-retention.ts +210 -0
  176. package/src/lib/command-dx.ts +9 -9
  177. package/src/lib/config.ts +12 -1
  178. package/src/lib/domain-publication.ts +9 -7
  179. package/src/lib/failure-debug.ts +26 -0
  180. package/src/lib/idp.ts +34 -3
  181. package/src/lib/instance-candidates.ts +2 -2
  182. package/src/lib/instance-target.ts +3 -14
  183. package/src/lib/instance.ts +16 -11
  184. package/src/lib/log.ts +16 -9
  185. package/src/lib/output.ts +1 -1
  186. package/src/lib/proc.ts +7 -2
  187. package/src/lib/provision-instance.ts +34 -57
  188. package/src/lib/skills/lock.ts +117 -0
  189. package/src/lib/skills/sync.ts +878 -0
  190. package/src/lib/skills.ts +1 -52
  191. package/src/lib/update.ts +298 -45
  192. package/src/lib/validation.ts +2 -2
  193. package/src/lib/view/assets.ts +11 -2
  194. package/src/lib/view/external-open-intent.ts +41 -0
  195. package/src/lib/view/external-open-origins.ts +37 -0
  196. package/src/lib/view/host-capabilities.ts +17 -0
  197. package/src/lib/view/resolve.ts +1 -1
  198. package/src/lib/view/server.ts +18 -3
  199. package/src/lib/view/session.ts +2 -0
  200. package/src/program/.spec/api.d.ts +1 -0
  201. package/src/program/__tests__/program.test.ts +201 -7
  202. package/src/program/argv.ts +14 -0
  203. package/src/program/build.ts +26 -4
  204. package/src/program/command.ts +1 -0
  205. package/src/program/index.ts +1 -0
  206. package/src/program/registry.ts +2 -1
  207. package/src/setup/__tests__/instance-step.test.ts +27 -10
  208. package/src/setup/steps/instance.ts +22 -15
  209. package/src/setup/steps/skills.ts +40 -38
  210. package/src/state/.spec/api.d.ts +21 -2
  211. package/src/state/.spec/architecture.md +18 -4
  212. package/src/state/.spec/layout.ts +1 -0
  213. package/src/state/__tests__/exchange-credentials.test.ts +88 -42
  214. package/src/state/__tests__/files.test.ts +24 -1
  215. package/src/state/__tests__/fixtures/session-route-process.ts +93 -0
  216. package/src/state/__tests__/paths.test.ts +1 -0
  217. package/src/state/__tests__/session-routes.test.ts +138 -0
  218. package/src/state/exchange-credentials.ts +22 -9
  219. package/src/state/files.ts +41 -0
  220. package/src/state/index.ts +3 -1
  221. package/src/state/paths.ts +3 -0
  222. package/src/state/session-routes.ts +34 -0
  223. package/src/telemetry/__tests__/analyze-log.test.ts +38 -0
  224. package/src/telemetry/__tests__/recorder.test.ts +1 -0
  225. package/src/telemetry/__tests__/retention.test.ts +267 -0
  226. package/src/telemetry/__tests__/settings.test.ts +102 -0
  227. package/src/telemetry/__tests__/store-scan.test.ts +128 -0
  228. package/src/telemetry/__tests__/trigger.test.ts +33 -4
  229. package/src/telemetry/analyze.ts +24 -2
  230. package/src/telemetry/recorder.ts +15 -4
  231. package/src/telemetry/retention.ts +176 -0
  232. package/src/telemetry/session.ts +18 -12
  233. package/src/telemetry/settings.ts +64 -11
  234. package/src/telemetry/store.ts +92 -9
  235. package/src/telemetry/trigger.ts +16 -28
  236. package/src/ui/.spec/api.d.ts +14 -0
  237. package/src/ui/.spec/architecture.md +14 -0
  238. package/src/ui/.spec/laws.ts +50 -0
  239. package/src/ui/.spec/layout.ts +16 -0
  240. package/src/ui/__tests__/ui.test.ts +1742 -0
  241. package/src/ui/index.ts +13 -0
  242. package/src/ui/lock.ts +89 -0
  243. package/src/ui/model.ts +83 -0
  244. package/src/ui/operations.ts +1044 -0
  245. package/src/ui/project.ts +333 -0
  246. package/src/ui/release.ts +398 -0
  247. package/src/ui/runner.ts +18 -0
  248. package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
  249. package/studio/client/dist/assets/index-BFVFs_8x.js +81 -0
  250. package/studio/client/dist/assets/index-DuB9iUdu.css +2 -0
  251. package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
  252. package/studio/client/dist/assets/schema-studio--a0kX3QU.css +1 -0
  253. package/studio/client/dist/assets/schema-studio-DH6oEWME.js +8 -0
  254. package/studio/client/dist/index.html +4 -3
  255. package/studio/package.json +8 -14
  256. package/studio/server/agent/ask.test.ts +1 -1
  257. package/studio/server/agent/bridge/grant.test.ts +1 -1
  258. package/studio/server/agent/bridge/routes.test.ts +1 -1
  259. package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
  260. package/studio/server/agent/harness/gateway/token.test.ts +1 -1
  261. package/studio/server/agent/harness/gateway/token.ts +3 -3
  262. package/studio/server/agent/prompts/anchors.test.ts +41 -32
  263. package/studio/server/agent/prompts/anchors.ts +23 -51
  264. package/studio/server/agent/prompts/system.test.ts +4 -4
  265. package/studio/server/agent/prompts/system.ts +5 -5
  266. package/studio/server/agent/routes.test.ts +1 -1
  267. package/studio/server/agent/run/coordinator.test.ts +1 -1
  268. package/studio/server/agent/run/preparation.ts +1 -1
  269. package/studio/server/api/canvas.ts +0 -1
  270. package/studio/server/api/context.ts +1 -1
  271. package/studio/server/api/deployment.ts +1 -1
  272. package/studio/server/api/views.ts +2 -2
  273. package/studio/server/api/workspace.ts +1 -1
  274. package/studio/server/api-agent-loadout.test.ts +1 -1
  275. package/studio/server/api.test.ts +1 -1
  276. package/studio/server/cache.test.ts +24 -18
  277. package/studio/server/cache.ts +10 -54
  278. package/studio/server/cli-consumers.test.ts +32 -4
  279. package/studio/server/cli.test.ts +2 -2
  280. package/studio/server/domain.test.ts +50 -68
  281. package/studio/server/domain.ts +82 -63
  282. package/studio/server/handoff/copy.ts +2 -2
  283. package/studio/server/index.ts +1 -1
  284. package/studio/server/instances/probe.test.ts +1 -1
  285. package/studio/server/introspect/anatomy/views/routes.ts +46 -37
  286. package/studio/server/introspect/anatomy/views.ts +10 -16
  287. package/studio/server/introspect/anatomy-extras.test.ts +29 -56
  288. package/studio/server/introspect/anatomy.test.ts +22 -14
  289. package/studio/server/introspect/anatomy.ts +6 -9
  290. package/studio/server/introspect/bundle.ts +0 -3
  291. package/studio/server/introspect/canonical-schema.test.ts +155 -424
  292. package/studio/server/introspect/canonical-schema.ts +248 -601
  293. package/studio/server/introspect/core.ts +14 -22
  294. package/studio/server/introspect/diff.test.ts +21 -9
  295. package/studio/server/introspect/diff.ts +19 -64
  296. package/studio/server/introspect/extractor.ts +23 -122
  297. package/studio/server/introspect/overlay-tsmorph.test.ts +116 -241
  298. package/studio/server/introspect/overlay-tsmorph.ts +0 -1
  299. package/studio/server/introspect/overlay.ts +3 -51
  300. package/studio/server/introspect/revision.test.ts +24 -28
  301. package/studio/server/introspect/revision.ts +11 -22
  302. package/studio/server/introspect/runtime.test.ts +77 -195
  303. package/studio/server/introspect/runtime.ts +5 -67
  304. package/studio/server/introspect/schema-ir-json.test.ts +81 -0
  305. package/studio/server/introspect/schema-ir-json.ts +39 -69
  306. package/studio/server/introspect/schema-refs.test.ts +43 -88
  307. package/studio/server/introspect/schema-refs.ts +14 -49
  308. package/studio/server/introspect/source-overlay/handlers.ts +116 -636
  309. package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
  310. package/studio/server/introspect/source-overlay/spans.ts +15 -45
  311. package/studio/server/lifecycle.ts +4 -1
  312. package/studio/server/state/baseline.test.ts +14 -9
  313. package/studio/server/state/baseline.ts +3 -5
  314. package/studio/server/state/documents.test.ts +69 -0
  315. package/studio/server/state/documents.ts +57 -10
  316. package/studio/server/state/visibility.ts +1 -5
  317. package/studio/server/views/model.ts +3 -3
  318. package/studio/server/views/target.test.ts +18 -76
  319. package/studio/server/views/target.ts +25 -51
  320. package/studio/server/watch.test.ts +11 -11
  321. package/studio/server/watch.ts +8 -4
  322. package/studio/server/workspace/updates.ts +23 -1
  323. package/studio/server/workspace-watch.ts +2 -2
  324. package/studio/shared/contracts/runtime.ts +4 -0
  325. package/studio/shared/contracts/schema.ts +44 -131
  326. package/studio/shared/contracts/surface.test.ts +15 -17
  327. package/studio/shared/contracts/workspace.ts +5 -11
  328. package/studio/shared/schema/identity.test.ts +20 -44
  329. package/studio/shared/schema/identity.ts +9 -24
  330. package/studio/tsconfig.json +2 -1
  331. package/tsconfig.json +1 -1
  332. package/viewer/dist/index.html +21 -0
  333. package/viewer/dist/main.js +41 -62
  334. package/.check-workspace.cjs +0 -40
  335. package/src/admin/binding.ts +0 -168
  336. package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
  337. package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
  338. package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
  339. package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
  340. package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
  341. package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
  342. package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
  343. package/studio/server/introspect/core-extractor.ts +0 -139
  344. package/studio/server/introspect/overlay.test.ts +0 -72
  345. package/studio/server/introspect/source-overlay/annotations.ts +0 -14
@@ -1,11 +1,15 @@
1
- import type { AuthApi } from '@astrale-os/kernel-client/auth'
2
- import type { GraphApi } from '@astrale-os/kernel-client/graph'
3
- import type { ClientSessionOptions, SessionAuth } from '@astrale-os/kernel-client/session'
4
-
5
- import { call } from '@astrale-os/kernel-client'
6
- import { createAuth } from '@astrale-os/kernel-client/auth'
7
- import { createGraph } from '@astrale-os/kernel-client/graph'
8
- import { ClientSession } from '@astrale-os/kernel-client/session'
1
+ import type { AuthApi } from '@astrale-os/sdk/auth'
2
+ import type { GraphApi } from '@astrale-os/sdk/client'
3
+ import type {
4
+ ClientSessionOptions,
5
+ SessionAuth,
6
+ SessionRouteStore,
7
+ } from '@astrale-os/sdk/client/session'
8
+
9
+ import { createAuth } from '@astrale-os/sdk/auth'
10
+ import { call } from '@astrale-os/sdk/client'
11
+ import { createGraph } from '@astrale-os/sdk/client'
12
+ import { ClientSession } from '@astrale-os/sdk/client/session'
9
13
 
10
14
  import type { AstraleConfig } from '../lib/config'
11
15
  import type { AdminConnectionOptions, ConnectionOptions, ConnectionTarget } from './target'
@@ -21,6 +25,8 @@ import { fetchWithCaFile } from '../lib/ca-fetch'
21
25
  import { readConfig } from '../lib/config'
22
26
  import { log } from '../lib/log'
23
27
  import { isMachine } from '../lib/output'
28
+ import { SESSION_ROUTE_STORE } from '../state/session-routes'
29
+ import { bindCredentialIdentity } from './auth'
24
30
  import { createCliCredential, validateCredentialSelection } from './credential'
25
31
  import { resolveAdminConnectionTarget, resolveConnectionTarget } from './target'
26
32
 
@@ -38,6 +44,8 @@ export interface ConnectionContext {
38
44
  readonly graph: GraphApi
39
45
  readonly auth: AuthApi
40
46
  readonly target: ConnectionTarget
47
+ /** Local identity label selected for this session; absent for raw credentials or anonymous use. */
48
+ readonly identity?: string
41
49
  }
42
50
 
43
51
  interface OwnedConnection {
@@ -63,6 +71,7 @@ export async function withClientSession<Value>(
63
71
  const target = await resolveConnectionTarget(options, config, {
64
72
  managed: (slug) => lookupManagedInstance(slug, options),
65
73
  })
74
+ options = await bindCredentialIdentity(options, target)
66
75
  warnMissingExplicitTarget(options, target)
67
76
  return runResolvedClientSession(target, timeoutMs, options, config, action, openConnection)
68
77
  }
@@ -76,6 +85,7 @@ export async function withAdminClientSession<Value>(
76
85
  const timeoutMs = resolveTimeoutMs(options.timeout)
77
86
  const config = await readConfig()
78
87
  const target = await resolveAdminConnectionTarget(options, config)
88
+ options = await bindCredentialIdentity(options, target)
79
89
  return runResolvedClientSession(target, timeoutMs, options, config, action, openConnection)
80
90
  }
81
91
 
@@ -141,7 +151,13 @@ function openConnection(
141
151
  const graph = createGraph((call, request) => session.call(call, request))
142
152
  const authApi = createAuth((path, input, request) => session.call(call(path, input), request))
143
153
  return {
144
- context: Object.freeze({ session, graph, auth: authApi, target }),
154
+ context: Object.freeze({
155
+ session,
156
+ graph,
157
+ auth: authApi,
158
+ target,
159
+ ...(options.as === undefined ? {} : { identity: options.as }),
160
+ }),
145
161
  close() {
146
162
  session.close()
147
163
  },
@@ -154,11 +170,13 @@ export function createClientSessionOptions(
154
170
  fetch: NonNullable<ClientSessionOptions['fetch']>,
155
171
  auth: SessionAuth | undefined,
156
172
  timeoutMs: number,
173
+ routeStore: SessionRouteStore = SESSION_ROUTE_STORE,
157
174
  ): ClientSessionOptions {
158
175
  return {
159
176
  kernel: target.kernelIssuer,
160
177
  fetch,
161
178
  ...(auth === undefined ? {} : { auth }),
179
+ routeStore,
162
180
  policy: {
163
181
  maximumRouteAgeMs: MAXIMUM_ROUTE_AGE_MS,
164
182
  ...(new URL(target.url).protocol === 'http:' ? { allowInsecureHttp: true } : {}),
package/src/errors.ts CHANGED
@@ -2,8 +2,9 @@ export class AstraleError extends Error {
2
2
  code: string
3
3
  hint?: string
4
4
 
5
- constructor(code: string, message: string, hint?: string) {
6
- super(message)
5
+ constructor(code: string, message: string, hint?: string, options?: ErrorOptions) {
6
+ if (message.trim() === '') throw new TypeError('CLI error message must be non-blank.')
7
+ super(message, options)
7
8
  this.name = 'AstraleError'
8
9
  this.code = code
9
10
  this.hint = hint
@@ -1,10 +1,17 @@
1
+ import type { ClassKey, ClassRef } from '@astrale-os/sdk/graph/class'
1
2
  import type { MutationAST } from '@astrale-os/sdk/mutation'
2
3
  import type { QueryAST, QueryDirection } from '@astrale-os/sdk/query'
3
4
 
4
- /** Untrusted CLI fields used to author or admit one exact Query V5 request. */
5
+ /** Admit one canonical Domain-rooted Class Path into its DSL coordinate. */
6
+ export function classReference(input: string, label: string): ClassRef
7
+
8
+ /** Admit one canonical Domain-rooted Class Path into its exact string identity. */
9
+ export function classKey(input: string, label: string): ClassKey
10
+
11
+ /** Untrusted CLI fields used to author or admit one exact Query V6 request. */
5
12
  export interface QueryCommandInput {
6
13
  readonly sources: readonly string[]
7
- readonly definition?: string
14
+ readonly class?: string
8
15
  readonly ast?: unknown
9
16
  readonly edge?: string
10
17
  readonly direction?: QueryDirection
@@ -12,16 +19,16 @@ export interface QueryCommandInput {
12
19
  readonly cursor?: string
13
20
  }
14
21
 
15
- /** Canonical query plus the caller-bound continuation token kept outside the AST. */
22
+ /** Canonical query plus caller-bound pagination kept outside the AST. */
16
23
  export interface PreparedQuery {
17
24
  readonly ast: QueryAST
18
- readonly cursor?: string
25
+ readonly page: Readonly<{ readonly size: number; readonly after?: string }>
19
26
  }
20
27
 
21
- /** Admit canonical Query V5 or author its supported Path/Definition/one-edge CLI subset. */
28
+ /** Admit canonical Query V6 or author its supported Path/Class/one-edge CLI subset. */
22
29
  export function prepareQuery(input: QueryCommandInput): PreparedQuery
23
30
 
24
- /** Admit canonical Mutation V2 or its exact authoring input; legacy PatchData is rejected. */
31
+ /** Admit canonical Mutation V3 or its exact authoring input; legacy PatchData is rejected. */
25
32
  export function prepareMutation(input: unknown): MutationAST
26
33
 
27
34
  /** Return the user-facing leaf of one exact qualified Property key. */
@@ -1,14 +1,14 @@
1
1
  import { defineLaw } from '@astrale-os/spec/authoring'
2
2
 
3
- export const CLI_GRAPH_QUERY_V5 = defineLaw({
4
- id: 'CLI-GRAPH-QUERY-V5',
3
+ export const CLI_GRAPH_QUERY_V6 = defineLaw({
4
+ id: 'CLI-GRAPH-QUERY-V6',
5
5
  statement:
6
- 'CLI query input becomes exactly one canonical Query V5 document with an explicit finite limit; exact Property ordering and Node or Edge projection profiles are admitted through the canonical AST surface, while legacy versions and unsupported selector combinations fail before a Graph call.',
6
+ 'CLI query input becomes exactly one canonical Query V6 document with an explicit finite limit; --class authors one exact Class source, exact Property ordering and Node or Edge projection profiles are admitted through the canonical AST surface, and legacy versions or unsupported selector combinations fail before a Graph call.',
7
7
  tests: [
8
- { file: '__tests__/query.test.ts', id: 'TEST-CLI-GRAPH-AUTHORS-QUERY-V5' },
9
- { file: '__tests__/query.test.ts', id: 'TEST-CLI-GRAPH-AUTHORS-DEFINITION-QUERY' },
10
- { file: '__tests__/query.test.ts', id: 'TEST-CLI-GRAPH-ADMITS-QUERY-V5-ORDERING' },
11
- { file: '__tests__/query.test.ts', id: 'TEST-CLI-GRAPH-ADMITS-QUERY-V5-PROJECTIONS' },
8
+ { file: '__tests__/query.test.ts', id: 'TEST-CLI-GRAPH-AUTHORS-QUERY-V6' },
9
+ { file: '__tests__/query.test.ts', id: 'TEST-CLI-GRAPH-AUTHORS-CLASS-QUERY' },
10
+ { file: '__tests__/query.test.ts', id: 'TEST-CLI-GRAPH-ADMITS-QUERY-V6-ORDERING' },
11
+ { file: '__tests__/query.test.ts', id: 'TEST-CLI-GRAPH-ADMITS-QUERY-V6-PROJECTIONS' },
12
12
  { file: '__tests__/query.test.ts', id: 'TEST-CLI-GRAPH-REJECTS-LEGACY-QUERY' },
13
13
  ],
14
14
  })
@@ -1,6 +1,14 @@
1
1
  import { defineLayout } from '@astrale-os/spec/authoring'
2
2
 
3
3
  export default defineLayout({
4
- entries: ['__tests__/', 'index.ts', 'mutation.ts', 'projection.ts', 'query.ts', 'tsconfig.json'],
4
+ entries: [
5
+ '__tests__/',
6
+ 'class.ts',
7
+ 'index.ts',
8
+ 'mutation.ts',
9
+ 'projection.ts',
10
+ 'query.ts',
11
+ 'tsconfig.json',
12
+ ],
5
13
  exact: true,
6
14
  })
@@ -14,7 +14,7 @@ describe('prepareMutation', () => {
14
14
  {
15
15
  op: 'node.create',
16
16
  as: 'note',
17
- class: '/:notes.example.dev:class.Note',
17
+ class: 'notes.example.dev:class.Note',
18
18
  props: {},
19
19
  },
20
20
  ],
@@ -29,7 +29,7 @@ describe('prepareMutation', () => {
29
29
  {
30
30
  op: 'node.create',
31
31
  as: 'note',
32
- class: '/:notes.example.dev:class.Note',
32
+ class: 'notes.example.dev:class.Note',
33
33
  props: {},
34
34
  },
35
35
  ],
@@ -37,11 +37,11 @@ describe('prepareQuery', () => {
37
37
  })
38
38
  })
39
39
 
40
- /** @evidence TEST-CLI-GRAPH-AUTHORS-DEFINITION-QUERY */
41
- test('authors an exact Definition source without backend query text', () => {
40
+ /** @evidence TEST-CLI-GRAPH-AUTHORS-CLASS-QUERY */
41
+ test('authors an exact Class source without backend query text', () => {
42
42
  const prepared = prepareQuery({
43
43
  sources: [],
44
- definition: '/:issues.astrale.ai:class.Issue',
44
+ class: '/:issues.astrale.ai:class.Issue',
45
45
  limit: '201',
46
46
  })
47
47
 
@@ -52,8 +52,8 @@ describe('prepareQuery', () => {
52
52
  kind: 'node',
53
53
  terms: [
54
54
  {
55
- kind: 'definition',
56
- definition: { origin: 'issues.astrale.ai', kind: 'class', name: 'Issue' },
55
+ kind: 'class',
56
+ class: { origin: 'issues.astrale.ai', kind: 'class', name: 'Issue' },
57
57
  },
58
58
  ],
59
59
  binding: 'n0',
@@ -64,6 +64,25 @@ describe('prepareQuery', () => {
64
64
  expect(prepared.page).toEqual({ size: 201 })
65
65
  })
66
66
 
67
+ test('unions positional Paths and one Class source in authored order', () => {
68
+ const prepared = prepareQuery({
69
+ sources: ['@note'],
70
+ class: '/:issues.astrale.ai:class.Issue',
71
+ })
72
+
73
+ expect(JSON.parse(JSON.stringify(prepared.ast.source))).toEqual({
74
+ kind: 'node',
75
+ terms: [
76
+ { kind: 'path', path: '@note' },
77
+ {
78
+ kind: 'class',
79
+ class: { origin: 'issues.astrale.ai', kind: 'class', name: 'Issue' },
80
+ },
81
+ ],
82
+ binding: 'n0',
83
+ })
84
+ })
85
+
67
86
  /** @evidence TEST-CLI-GRAPH-ADMITS-QUERY-V6-ORDERING */
68
87
  test('admits one exact Property-ordered Query V6 document', () => {
69
88
  const ast = {
@@ -73,8 +92,8 @@ describe('prepareQuery', () => {
73
92
  kind: 'node',
74
93
  terms: [
75
94
  {
76
- kind: 'definition',
77
- definition: { origin: 'notes.example.dev', kind: 'class', name: 'Note' },
95
+ kind: 'class',
96
+ class: { origin: 'notes.example.dev', kind: 'class', name: 'Note' },
78
97
  },
79
98
  ],
80
99
  binding: 'n0',
@@ -148,7 +167,14 @@ describe('prepareQuery', () => {
148
167
  prepareQuery({ sources: ['/:notes.example.dev:class.Note'], limit: 'all' }),
149
168
  ).toThrow('--limit must be a positive integer')
150
169
  expect(() =>
151
- prepareQuery({ sources: [], definition: '/:notes.example.dev:view.note' }),
152
- ).toThrow('--definition must be one canonical Class or Interface Path')
170
+ prepareQuery({
171
+ sources: [],
172
+ ast: {},
173
+ class: '/:notes.example.dev:class.Note',
174
+ }),
175
+ ).toThrow('--ast/--file cannot be combined with sources, --class, --edge, or --direction')
176
+ expect(() => prepareQuery({ sources: [], class: '/:notes.example.dev:view.note' })).toThrow(
177
+ '--class must be one canonical Class Path',
178
+ )
153
179
  })
154
180
  })
@@ -0,0 +1,30 @@
1
+ import type { ClassKey as ClassKeyValue, ClassRef } from '@astrale-os/sdk/graph/class'
2
+
3
+ import { ClassKey } from '@astrale-os/sdk/graph/class'
4
+ import { Path } from '@astrale-os/sdk/graph/path'
5
+
6
+ /** Admit one canonical Domain-rooted Class Path into its DSL coordinate. */
7
+ export function classReference(input: string, label: string): ClassRef {
8
+ const path = Path.parse(input)
9
+ const step = path.ast.steps[0]
10
+ if (
11
+ path.ast.anchor.kind !== 'domain' ||
12
+ path.ast.steps.length !== 1 ||
13
+ step?.kind !== 'projection' ||
14
+ step.projection.kind !== 'class'
15
+ ) {
16
+ throw new TypeError(`${label} must be one canonical Class Path`)
17
+ }
18
+ return ClassKey.ref(
19
+ ClassKey.of({
20
+ origin: path.ast.anchor.origin,
21
+ kind: 'class',
22
+ name: step.projection.name,
23
+ }),
24
+ )
25
+ }
26
+
27
+ /** Admit one canonical Domain-rooted Class Path into its exact string identity. */
28
+ export function classKey(input: string, label: string): ClassKeyValue {
29
+ return ClassKey.of(classReference(input, label))
30
+ }
@@ -1,3 +1,4 @@
1
+ export { classKey, classReference } from './class'
1
2
  export { prepareMutation } from './mutation'
2
3
  export { nodeProperty, unqualifyProperty } from './projection'
3
4
  export { prepareQuery, type PreparedQuery, type QueryCommandInput } from './query'
@@ -1,15 +1,18 @@
1
1
  import type {
2
+ NodeQueryBuilder,
2
3
  QueryAST as QueryASTValue,
3
- QueryDefinitionRef,
4
4
  QueryDirection,
5
+ QueryNodeInput,
5
6
  } from '@astrale-os/sdk/query'
6
7
 
7
8
  import { Path } from '@astrale-os/sdk/graph/path'
8
9
  import { Query, QueryAST } from '@astrale-os/sdk/query'
9
10
 
11
+ import { classReference } from './class'
12
+
10
13
  export interface QueryCommandInput {
11
14
  readonly sources: readonly string[]
12
- readonly definition?: string
15
+ readonly class?: string
13
16
  readonly ast?: unknown
14
17
  readonly edge?: string
15
18
  readonly direction?: QueryDirection
@@ -24,70 +27,54 @@ export interface PreparedQuery {
24
27
 
25
28
  const DEFAULT_LIMIT = 100
26
29
 
27
- /** Admit canonical Query V6 or author the intentionally small Path/Definition/one-edge subset. */
30
+ /** Admit canonical Query V6 or author the intentionally small Path/Class/one-edge subset. */
28
31
  export function prepareQuery(input: QueryCommandInput): PreparedQuery {
29
32
  const limit = positiveInteger(input.limit, '--limit', DEFAULT_LIMIT)
30
33
  if (input.ast !== undefined) {
31
34
  if (
32
35
  input.sources.length > 0 ||
33
- input.definition !== undefined ||
36
+ input.class !== undefined ||
34
37
  input.edge !== undefined ||
35
38
  input.direction !== undefined
36
39
  ) {
37
40
  throw new TypeError(
38
- '--ast/--file cannot be combined with sources, --definition, --edge, or --direction',
41
+ '--ast/--file cannot be combined with sources, --class, --edge, or --direction',
39
42
  )
40
43
  }
41
44
  return withPage(QueryAST.decode(input.ast), limit, input.cursor)
42
45
  }
43
46
 
44
- if (input.sources.length === 0 && input.definition === undefined) {
45
- throw new TypeError(
46
- 'query requires a Path source, --definition, or a canonical Query V6 document',
47
- )
47
+ if (input.sources.length === 0 && input.class === undefined) {
48
+ throw new TypeError('query requires a Path source, --class, or a canonical Query V6 document')
48
49
  }
49
50
  if (input.direction !== undefined && input.edge === undefined) {
50
51
  throw new TypeError('--direction requires --edge')
51
52
  }
52
53
 
53
54
  const paths = input.sources.map((source) => Path.parse(source))
54
- const definition = input.definition === undefined ? undefined : definitionRef(input.definition)
55
- const query =
56
- paths.length > 0
57
- ? Query.from({
58
- kind: 'node',
59
- paths: paths as [Path, ...Path[]],
60
- ...(definition === undefined ? {} : { definitions: [definition] }),
61
- })
62
- : Query.from({ kind: 'node', definitions: [definition!] })
55
+ const selectedClass =
56
+ input.class === undefined ? undefined : classReference(input.class, '--class')
57
+ const nodes = selectedClass === undefined ? paths : [...paths, selectedClass]
58
+ const query: NodeQueryBuilder<unknown> = Query.from({
59
+ nodes: nodes as [QueryNodeInput, ...QueryNodeInput[]],
60
+ })
63
61
  const ast =
64
62
  input.edge === undefined
65
- ? query.select({ kind: 'graph' })
66
- : query
67
- .expand({
68
- via: [definitionRef(input.edge)],
69
- direction: input.direction ?? 'outgoing',
70
- })
71
- .select({ kind: 'graph' })
63
+ ? query.select({ kind: 'graph', binding: query.node })
64
+ : expanded(query, input.edge, input.direction)
72
65
  return withPage(ast, limit, input.cursor)
73
66
  }
74
67
 
75
- function definitionRef(input: string): QueryDefinitionRef {
76
- const path = Path.parse(input)
77
- const step = path.ast.steps[0]
78
- if (
79
- path.ast.anchor.kind !== 'domain' ||
80
- path.ast.steps.length !== 1 ||
81
- step?.kind !== 'projection' ||
82
- (step.projection.kind !== 'class' && step.projection.kind !== 'interface')
83
- ) {
84
- throw new TypeError('--definition must be one canonical Class or Interface Path')
85
- }
86
- return Object.freeze({
87
- origin: path.ast.anchor.origin,
88
- kind: step.projection.kind,
89
- name: step.projection.name,
68
+ function expanded(
69
+ query: NodeQueryBuilder<unknown>,
70
+ edge: string,
71
+ direction: QueryDirection | undefined,
72
+ ): QueryASTValue {
73
+ const result = query.expand({
74
+ via: [classReference(edge, '--edge')],
75
+ direction: direction ?? 'outgoing',
90
76
  })
77
+ return result.select({ kind: 'graph', binding: result.node })
91
78
  }
92
79
 
93
80
  function positiveInteger(raw: string | undefined, name: string, fallback: number): number {
@@ -1,6 +1,6 @@
1
- import type { Call } from '@astrale-os/kernel-client'
2
1
  import type { ProvisionRequest } from '@astrale-os/sdk/auth'
3
- import type { LocalBinding } from '@astrale-os/sdk/graph/model'
2
+ import type { Call } from '@astrale-os/sdk/client'
3
+ import type { LocalBinding } from '@astrale-os/sdk/graph'
4
4
  import type { JWK } from 'jose'
5
5
 
6
6
  import type {
@@ -1,5 +1,8 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises'
2
+ import { dirname } from 'node:path'
3
+
1
4
  import { fileExists, keypairPaths } from '../../../keys/index'
2
- import { KEYS_DIR } from '../../../state/index'
5
+ import { EXCHANGE_CREDENTIALS_PATH, KEYS_DIR, SESSION_ROUTES_PATH } from '../../../state/index'
3
6
  import {
4
7
  createIdentity,
5
8
  exportIdentity,
@@ -30,6 +33,13 @@ try {
30
33
  } catch (error) {
31
34
  selectedDeletion = error instanceof Error ? error.message : String(error)
32
35
  }
36
+ await mkdir(dirname(SESSION_ROUTES_PATH), { recursive: true })
37
+ await mkdir(dirname(EXCHANGE_CREDENTIALS_PATH), { recursive: true })
38
+ await writeFile(SESSION_ROUTES_PATH, '{"version":1,"entries":{"confidential":{}}}\n')
39
+ await writeFile(
40
+ EXCHANGE_CREDENTIALS_PATH,
41
+ '{"version":2,"entries":{"confidential":{"credential":"bearer"}}}\n',
42
+ )
33
43
  await deleteIdentity('bob')
34
44
  await deleteIdentity('alice-alias')
35
45
  await upsertIdpIdentity('workos', {
@@ -49,5 +59,7 @@ console.log(
49
59
  selectedDeletion,
50
60
  aliceKey: await fileExists(keypairPaths('alice', KEYS_DIR).privatePath),
51
61
  bobKey: await fileExists(keypairPaths('bob', KEYS_DIR).privatePath),
62
+ routeCache: await fileExists(SESSION_ROUTES_PATH),
63
+ exchangeCache: JSON.parse(await readFile(EXCHANGE_CREDENTIALS_PATH, 'utf8')),
52
64
  }),
53
65
  )
@@ -1,6 +1,6 @@
1
1
  import type { ProvisionRequest } from '@astrale-os/sdk/auth'
2
2
 
3
- import { LocalBinding } from '@astrale-os/sdk/graph/model'
3
+ import { LocalBinding } from '@astrale-os/sdk/graph'
4
4
  import { expect, test } from 'bun:test'
5
5
 
6
6
  import { submitIdentityProvision } from '../registration'
@@ -47,6 +47,8 @@ describe('identity registry journey', () => {
47
47
  selectedDeletion: string
48
48
  aliceKey: boolean
49
49
  bobKey: boolean
50
+ routeCache: boolean
51
+ exchangeCache: unknown
50
52
  }
51
53
  expect(result.store.default).toBe('alice')
52
54
  expect(Object.keys(result.store.identities).sort()).toEqual(['alice', 'workos'])
@@ -60,6 +62,8 @@ describe('identity registry journey', () => {
60
62
  expect(result.selectedDeletion).toContain('Cannot delete the default identity')
61
63
  expect(result.aliceKey).toBe(true)
62
64
  expect(result.bobKey).toBe(false)
65
+ expect(result.routeCache).toBe(false)
66
+ expect(result.exchangeCache).toEqual({ version: 2, entries: {} })
63
67
 
64
68
  const persisted = JSON.parse(await readFile(join(root, 'identities.json'), 'utf-8')) as {
65
69
  version?: unknown
@@ -1,9 +1,9 @@
1
- import type { Call } from '@astrale-os/kernel-client'
2
1
  import type { ProvisionRequest } from '@astrale-os/sdk/auth'
3
- import type { LocalBinding } from '@astrale-os/sdk/graph/model'
2
+ import type { Call } from '@astrale-os/sdk/client'
3
+ import type { LocalBinding } from '@astrale-os/sdk/graph'
4
4
 
5
- import { call } from '@astrale-os/kernel-client'
6
5
  import { issuer } from '@astrale-os/sdk/auth'
6
+ import { call } from '@astrale-os/sdk/client'
7
7
  import { NodeId } from '@astrale-os/sdk/graph/node'
8
8
  import { Path } from '@astrale-os/sdk/graph/path'
9
9
 
@@ -10,6 +10,7 @@ import {
10
10
  type IdentityStore,
11
11
  type Registration,
12
12
  ExchangeCredentialCache,
13
+ SESSION_ROUTE_STORE,
13
14
  } from '../state/index'
14
15
 
15
16
  export type { Identity, IdentityStore, Registration } from '../state/index'
@@ -68,6 +69,7 @@ export async function deleteIdentity(name: string): Promise<void> {
68
69
  return { next: { ...store, identities }, value: undefined }
69
70
  })
70
71
  await new ExchangeCredentialCache().clear()
72
+ SESSION_ROUTE_STORE.clear()
71
73
  }
72
74
 
73
75
  function hasAnotherKeyIdentity(store: IdentityStore, name: string, subject: string): boolean {
@@ -62,6 +62,10 @@ describe('DESIGN — per-identity keys', () => {
62
62
  await persistKeypair('alice', { keysDir: tmp })
63
63
  const jwt = await signAs('alice', tmp)
64
64
  expect(jwt.split('.').length).toBe(3)
65
+ const claims = decodeJwt(jwt)
66
+ expect(claims.iat).toBeUndefined()
67
+ expect(claims.exp).toBeNumber()
68
+ expect((claims.exp as number) * 1_000).toBeGreaterThan(Date.now() + 299_000)
65
69
  })
66
70
 
67
71
  /** @evidence TEST-CLI-KEYS-DISTINGUISHES-KERNEL-ROOT-GRANT */
@@ -47,6 +47,7 @@ async function signIdentityCredential(options: {
47
47
  .setIssuer(options.issuer)
48
48
  .setSubject(options.subject)
49
49
  .setAudience(options.audience)
50
+ .setExpirationTime('5m')
50
51
  .sign(privateKey)
51
52
  }
52
53
 
@@ -6,6 +6,7 @@ import {
6
6
  DEFAULT_ADMIN_TARGET_NAME,
7
7
  DEFAULT_ADMIN_DOMAIN_ISSUER,
8
8
  DEFAULT_ADMIN_TARGET_URL,
9
+ defaultAdminTargetForChannel,
9
10
  resolveAdminTargetFromStore,
10
11
  } from '../admin-target'
11
12
  import { DEFAULT_CONFIG, type AstraleConfig } from '../config'
@@ -32,6 +33,17 @@ const instances: InstanceStore = {
32
33
  }
33
34
 
34
35
  describe('resolveAdminTargetFromStore', () => {
36
+ test('derives hosted Admin defaults from the release channel', () => {
37
+ expect(defaultAdminTargetForChannel('beta')).toEqual({
38
+ url: 'https://admin.eu.beta.astrale.ai/api',
39
+ domainIssuer: 'https://admin.beta.astrale.ai',
40
+ })
41
+ expect(defaultAdminTargetForChannel('stable')).toEqual({
42
+ url: 'https://admin.eu.astrale.ai/api',
43
+ domainIssuer: 'https://admin.astrale.ai',
44
+ })
45
+ })
46
+
35
47
  test('uses hosted admin default without active instance coupling', () => {
36
48
  expect(resolveAdminTargetFromStore({}, DEFAULT_CONFIG, instances)).toEqual({
37
49
  name: DEFAULT_ADMIN_TARGET_NAME,
@@ -39,11 +39,26 @@ describe('presentBinary', () => {
39
39
 
40
40
  test('--json inlines text for text-like content types', async () => {
41
41
  await presentBinary(
42
- { mediaType: 'text/plain', body: new TextEncoder().encode('hi') },
42
+ { mediaType: 'text/plain', status: 202, body: new TextEncoder().encode('hi') },
43
43
  { json: true },
44
44
  )
45
45
  const obj = JSON.parse(Buffer.concat(chunks).toString())
46
46
  expect(obj.body).toBe('hi')
47
+ expect(obj.status).toBe(202)
48
+ })
49
+
50
+ test('--raw drains streaming binary chunks in exact order', async () => {
51
+ await presentBinary(
52
+ {
53
+ mediaType: 'application/octet-stream',
54
+ body: (async function* () {
55
+ yield new Uint8Array([1, 2])
56
+ yield new Uint8Array([3, 4])
57
+ })(),
58
+ },
59
+ { raw: true },
60
+ )
61
+ expect(Array.from(Buffer.concat(chunks))).toEqual([1, 2, 3, 4])
47
62
  })
48
63
 
49
64
  test('-o writes raw bytes to a file', async () => {