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

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 (343) hide show
  1. package/README.md +80 -6
  2. package/dist/astrale.js +55471 -58674
  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 +118 -10
  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 +35 -32
  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 +89 -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 +12 -5
  138. package/src/graph/.spec/layout.ts +9 -1
  139. package/src/graph/__tests__/mutation.test.ts +2 -2
  140. package/src/graph/__tests__/query.test.ts +25 -6
  141. package/src/graph/class.ts +30 -0
  142. package/src/graph/index.ts +1 -0
  143. package/src/graph/query.ts +21 -32
  144. package/src/identity/.spec/api.d.ts +2 -2
  145. package/src/identity/__tests__/fixtures/registry-journey.ts +13 -1
  146. package/src/identity/__tests__/registration.test.ts +1 -1
  147. package/src/identity/__tests__/registry.test.ts +4 -0
  148. package/src/identity/registration.ts +3 -3
  149. package/src/identity/registry.ts +2 -0
  150. package/src/keys/__tests__/keys.test.ts +4 -0
  151. package/src/keys/credential.ts +1 -0
  152. package/src/lib/__tests__/admin-target.test.ts +12 -0
  153. package/src/lib/__tests__/binary.test.ts +16 -1
  154. package/src/lib/__tests__/browser-retention.test.ts +212 -0
  155. package/src/lib/__tests__/command-dx.test.ts +1 -1
  156. package/src/lib/__tests__/config.test.ts +27 -0
  157. package/src/lib/__tests__/domain-publication.test.ts +10 -16
  158. package/src/lib/__tests__/idp.test.ts +34 -4
  159. package/src/lib/__tests__/instance-candidates.test.ts +1 -3
  160. package/src/lib/__tests__/instance-target.test.ts +7 -39
  161. package/src/lib/__tests__/log-errors.test.ts +64 -0
  162. package/src/lib/__tests__/skills.test.ts +809 -0
  163. package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
  164. package/src/lib/__tests__/update.test.ts +264 -11
  165. package/src/lib/__tests__/view-assets.test.ts +25 -0
  166. package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
  167. package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
  168. package/src/lib/__tests__/view-open-intent.test.ts +0 -2
  169. package/src/lib/__tests__/view-server.test.ts +26 -3
  170. package/src/lib/__tests__/view-session.test.ts +2 -1
  171. package/src/lib/admin-instance.ts +14 -8
  172. package/src/lib/admin-target.ts +21 -2
  173. package/src/lib/binary.ts +22 -5
  174. package/src/lib/browser-retention.ts +210 -0
  175. package/src/lib/command-dx.ts +9 -9
  176. package/src/lib/config.ts +12 -1
  177. package/src/lib/domain-publication.ts +9 -7
  178. package/src/lib/failure-debug.ts +26 -0
  179. package/src/lib/idp.ts +34 -3
  180. package/src/lib/instance-candidates.ts +2 -2
  181. package/src/lib/instance-target.ts +3 -14
  182. package/src/lib/instance.ts +16 -11
  183. package/src/lib/log.ts +16 -9
  184. package/src/lib/output.ts +1 -1
  185. package/src/lib/proc.ts +7 -2
  186. package/src/lib/provision-instance.ts +34 -57
  187. package/src/lib/skills/lock.ts +117 -0
  188. package/src/lib/skills/sync.ts +814 -0
  189. package/src/lib/skills.ts +1 -52
  190. package/src/lib/update.ts +298 -45
  191. package/src/lib/validation.ts +2 -2
  192. package/src/lib/view/assets.ts +11 -2
  193. package/src/lib/view/external-open-intent.ts +41 -0
  194. package/src/lib/view/external-open-origins.ts +37 -0
  195. package/src/lib/view/host-capabilities.ts +17 -0
  196. package/src/lib/view/resolve.ts +1 -1
  197. package/src/lib/view/server.ts +18 -3
  198. package/src/lib/view/session.ts +2 -0
  199. package/src/program/.spec/api.d.ts +1 -0
  200. package/src/program/__tests__/program.test.ts +163 -6
  201. package/src/program/argv.ts +14 -0
  202. package/src/program/build.ts +26 -4
  203. package/src/program/command.ts +1 -0
  204. package/src/program/index.ts +1 -0
  205. package/src/program/registry.ts +2 -1
  206. package/src/setup/__tests__/instance-step.test.ts +27 -10
  207. package/src/setup/steps/instance.ts +22 -15
  208. package/src/setup/steps/skills.ts +40 -38
  209. package/src/state/.spec/api.d.ts +21 -2
  210. package/src/state/.spec/architecture.md +18 -4
  211. package/src/state/.spec/layout.ts +1 -0
  212. package/src/state/__tests__/exchange-credentials.test.ts +88 -42
  213. package/src/state/__tests__/files.test.ts +24 -1
  214. package/src/state/__tests__/fixtures/session-route-process.ts +93 -0
  215. package/src/state/__tests__/paths.test.ts +1 -0
  216. package/src/state/__tests__/session-routes.test.ts +138 -0
  217. package/src/state/exchange-credentials.ts +22 -9
  218. package/src/state/files.ts +41 -0
  219. package/src/state/index.ts +3 -1
  220. package/src/state/paths.ts +3 -0
  221. package/src/state/session-routes.ts +34 -0
  222. package/src/telemetry/__tests__/analyze-log.test.ts +38 -0
  223. package/src/telemetry/__tests__/recorder.test.ts +1 -0
  224. package/src/telemetry/__tests__/retention.test.ts +267 -0
  225. package/src/telemetry/__tests__/settings.test.ts +102 -0
  226. package/src/telemetry/__tests__/store-scan.test.ts +128 -0
  227. package/src/telemetry/__tests__/trigger.test.ts +33 -4
  228. package/src/telemetry/analyze.ts +24 -2
  229. package/src/telemetry/recorder.ts +15 -4
  230. package/src/telemetry/retention.ts +176 -0
  231. package/src/telemetry/session.ts +18 -12
  232. package/src/telemetry/settings.ts +64 -11
  233. package/src/telemetry/store.ts +92 -9
  234. package/src/telemetry/trigger.ts +16 -28
  235. package/src/ui/.spec/api.d.ts +14 -0
  236. package/src/ui/.spec/architecture.md +14 -0
  237. package/src/ui/.spec/laws.ts +50 -0
  238. package/src/ui/.spec/layout.ts +16 -0
  239. package/src/ui/__tests__/ui.test.ts +1742 -0
  240. package/src/ui/index.ts +13 -0
  241. package/src/ui/lock.ts +89 -0
  242. package/src/ui/model.ts +83 -0
  243. package/src/ui/operations.ts +1044 -0
  244. package/src/ui/project.ts +333 -0
  245. package/src/ui/release.ts +398 -0
  246. package/src/ui/runner.ts +18 -0
  247. package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
  248. package/studio/client/dist/assets/index-BFVFs_8x.js +81 -0
  249. package/studio/client/dist/assets/index-DuB9iUdu.css +2 -0
  250. package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
  251. package/studio/client/dist/assets/schema-studio--a0kX3QU.css +1 -0
  252. package/studio/client/dist/assets/schema-studio-DH6oEWME.js +8 -0
  253. package/studio/client/dist/index.html +4 -3
  254. package/studio/package.json +8 -14
  255. package/studio/server/agent/ask.test.ts +1 -1
  256. package/studio/server/agent/bridge/grant.test.ts +1 -1
  257. package/studio/server/agent/bridge/routes.test.ts +1 -1
  258. package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
  259. package/studio/server/agent/harness/gateway/token.test.ts +1 -1
  260. package/studio/server/agent/harness/gateway/token.ts +3 -3
  261. package/studio/server/agent/prompts/anchors.test.ts +41 -32
  262. package/studio/server/agent/prompts/anchors.ts +23 -51
  263. package/studio/server/agent/prompts/system.test.ts +4 -4
  264. package/studio/server/agent/prompts/system.ts +5 -5
  265. package/studio/server/agent/routes.test.ts +1 -1
  266. package/studio/server/agent/run/coordinator.test.ts +1 -1
  267. package/studio/server/agent/run/preparation.ts +1 -1
  268. package/studio/server/api/canvas.ts +0 -1
  269. package/studio/server/api/context.ts +1 -1
  270. package/studio/server/api/deployment.ts +1 -1
  271. package/studio/server/api/views.ts +2 -2
  272. package/studio/server/api/workspace.ts +1 -1
  273. package/studio/server/api-agent-loadout.test.ts +1 -1
  274. package/studio/server/api.test.ts +1 -1
  275. package/studio/server/cache.test.ts +24 -18
  276. package/studio/server/cache.ts +10 -54
  277. package/studio/server/cli-consumers.test.ts +31 -3
  278. package/studio/server/domain.test.ts +50 -68
  279. package/studio/server/domain.ts +82 -63
  280. package/studio/server/handoff/copy.ts +2 -2
  281. package/studio/server/index.ts +1 -1
  282. package/studio/server/instances/probe.test.ts +1 -1
  283. package/studio/server/introspect/anatomy/views/routes.ts +46 -37
  284. package/studio/server/introspect/anatomy/views.ts +10 -16
  285. package/studio/server/introspect/anatomy-extras.test.ts +29 -56
  286. package/studio/server/introspect/anatomy.test.ts +22 -14
  287. package/studio/server/introspect/anatomy.ts +6 -9
  288. package/studio/server/introspect/bundle.ts +0 -3
  289. package/studio/server/introspect/canonical-schema.test.ts +155 -424
  290. package/studio/server/introspect/canonical-schema.ts +248 -601
  291. package/studio/server/introspect/core.ts +14 -22
  292. package/studio/server/introspect/diff.test.ts +21 -9
  293. package/studio/server/introspect/diff.ts +19 -64
  294. package/studio/server/introspect/extractor.ts +23 -122
  295. package/studio/server/introspect/overlay-tsmorph.test.ts +116 -241
  296. package/studio/server/introspect/overlay-tsmorph.ts +0 -1
  297. package/studio/server/introspect/overlay.ts +3 -51
  298. package/studio/server/introspect/revision.test.ts +24 -28
  299. package/studio/server/introspect/revision.ts +11 -22
  300. package/studio/server/introspect/runtime.test.ts +77 -195
  301. package/studio/server/introspect/runtime.ts +5 -67
  302. package/studio/server/introspect/schema-ir-json.test.ts +81 -0
  303. package/studio/server/introspect/schema-ir-json.ts +39 -69
  304. package/studio/server/introspect/schema-refs.test.ts +43 -88
  305. package/studio/server/introspect/schema-refs.ts +14 -49
  306. package/studio/server/introspect/source-overlay/handlers.ts +116 -636
  307. package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
  308. package/studio/server/introspect/source-overlay/spans.ts +15 -45
  309. package/studio/server/lifecycle.ts +4 -1
  310. package/studio/server/state/baseline.test.ts +14 -9
  311. package/studio/server/state/baseline.ts +3 -5
  312. package/studio/server/state/documents.test.ts +69 -0
  313. package/studio/server/state/documents.ts +57 -10
  314. package/studio/server/state/visibility.ts +1 -5
  315. package/studio/server/views/model.ts +3 -3
  316. package/studio/server/views/target.test.ts +18 -76
  317. package/studio/server/views/target.ts +24 -50
  318. package/studio/server/watch.test.ts +11 -11
  319. package/studio/server/watch.ts +8 -4
  320. package/studio/server/workspace/updates.ts +23 -1
  321. package/studio/server/workspace-watch.ts +2 -2
  322. package/studio/shared/contracts/runtime.ts +4 -0
  323. package/studio/shared/contracts/schema.ts +44 -131
  324. package/studio/shared/contracts/surface.test.ts +15 -17
  325. package/studio/shared/contracts/workspace.ts +5 -11
  326. package/studio/shared/schema/identity.test.ts +20 -44
  327. package/studio/shared/schema/identity.ts +9 -24
  328. package/studio/tsconfig.json +2 -1
  329. package/tsconfig.json +1 -1
  330. package/viewer/dist/index.html +21 -0
  331. package/viewer/dist/main.js +41 -62
  332. package/.check-workspace.cjs +0 -40
  333. package/src/admin/binding.ts +0 -168
  334. package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
  335. package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
  336. package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
  337. package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
  338. package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
  339. package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
  340. package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
  341. package/studio/server/introspect/core-extractor.ts +0 -139
  342. package/studio/server/introspect/overlay.test.ts +0 -72
  343. package/studio/server/introspect/source-overlay/annotations.ts +0 -14
@@ -0,0 +1,139 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test'
2
+ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+
6
+ import type { InstanceInfo } from '../../lib/admin-instance'
7
+ import type { ResolvedInstance } from '../../lib/instance'
8
+
9
+ import { resolveStatus, type StatusDependencies } from '../instance/status'
10
+
11
+ const cliRoot = join(import.meta.dir, '../../..')
12
+
13
+ let home: string
14
+ let server: ReturnType<typeof Bun.serve>
15
+
16
+ beforeEach(async () => {
17
+ home = await mkdtemp(join(tmpdir(), 'astrale-instance-status-'))
18
+ server = Bun.serve({
19
+ port: 0,
20
+ fetch(request) {
21
+ const url = new URL(request.url)
22
+ const issuer = `http://127.0.0.1:${server.port}`
23
+ if (url.pathname === '/.well-known/openid-configuration') {
24
+ return Response.json({ issuer, jwks_uri: `${issuer}/.well-known/jwks.json` })
25
+ }
26
+ if (url.pathname === '/.well-known/jwks.json') {
27
+ return Response.json({ keys: [{ kid: 'status-test' }] })
28
+ }
29
+ return new Response('Not found', { status: 404 })
30
+ },
31
+ })
32
+ })
33
+
34
+ afterEach(async () => {
35
+ server.stop(true)
36
+ await rm(home, { recursive: true, force: true })
37
+ })
38
+
39
+ describe('instance status', () => {
40
+ test('keeps managed lifecycle authoritative when the slug is also bookmarked', async () => {
41
+ const managed: InstanceInfo = {
42
+ id: '@managed',
43
+ slug: 'shared',
44
+ url: 'https://managed.example.com',
45
+ state: 'provisioning',
46
+ phase: 'bootstrap',
47
+ }
48
+ const statusOwnedInstance: StatusDependencies['statusOwnedInstance'] = mock(async () => managed)
49
+ const resolveInstance: StatusDependencies['resolveInstance'] = mock(async () => {
50
+ throw new Error('bookmark resolution must not run')
51
+ })
52
+ const probeBookmark: StatusDependencies['probeBookmark'] = mock(async () => {
53
+ throw new Error('bookmark probe must not run')
54
+ })
55
+
56
+ await expect(
57
+ resolveStatus('shared', {}, { statusOwnedInstance, resolveInstance, probeBookmark }),
58
+ ).resolves.toEqual(managed)
59
+ expect(statusOwnedInstance).toHaveBeenCalledWith({}, 'shared')
60
+ expect(resolveInstance).not.toHaveBeenCalled()
61
+ expect(probeBookmark).not.toHaveBeenCalled()
62
+ })
63
+
64
+ test('probes only an explicitly selected bookmark and rejects mixed source flags', async () => {
65
+ const bookmark: ResolvedInstance = {
66
+ name: 'local',
67
+ kind: 'bookmark',
68
+ url: 'https://local.example.com',
69
+ issuer: 'https://issuer.example.com',
70
+ }
71
+ const statusOwnedInstance: StatusDependencies['statusOwnedInstance'] = mock(async () => {
72
+ throw new Error('managed status must not run')
73
+ })
74
+ const resolveInstance: StatusDependencies['resolveInstance'] = mock(async () => bookmark)
75
+ const probeBookmark: StatusDependencies['probeBookmark'] = mock(async () => undefined)
76
+ const dependencies = { statusOwnedInstance, resolveInstance, probeBookmark }
77
+
78
+ await expect(resolveStatus('local', { bookmarked: true }, dependencies)).resolves.toEqual({
79
+ slug: 'local',
80
+ url: bookmark.url,
81
+ issuer: 'https://issuer.example.com',
82
+ kind: 'bookmark',
83
+ state: 'ready',
84
+ })
85
+ expect(resolveInstance).toHaveBeenCalledWith('local', undefined, { persist: false })
86
+ expect(probeBookmark).toHaveBeenCalledWith(bookmark)
87
+ expect(statusOwnedInstance).not.toHaveBeenCalled()
88
+
89
+ await expect(
90
+ resolveStatus('local', { bookmarked: true, admin: 'admin' }, dependencies),
91
+ ).rejects.toMatchObject({ code: 'INVALID_FLAG' })
92
+ })
93
+
94
+ test('probes an explicit local bookmark without rewriting its registry', async () => {
95
+ const issuer = `http://127.0.0.1:${server.port}`
96
+ const registry = `${JSON.stringify({
97
+ active: 'local',
98
+ instances: {
99
+ local: {
100
+ url: issuer,
101
+ issuer,
102
+ mode: 'remote',
103
+ },
104
+ },
105
+ })}\n`
106
+ const path = join(home, 'instances.json')
107
+ await writeFile(path, registry)
108
+
109
+ const process = Bun.spawn({
110
+ cmd: [
111
+ 'bun',
112
+ join(cliRoot, 'bin/astrale.ts'),
113
+ 'instance',
114
+ 'status',
115
+ 'local',
116
+ '--bookmarked',
117
+ '--json',
118
+ ],
119
+ env: { ...Bun.env, ASTRALE_HOME: home },
120
+ stdout: 'pipe',
121
+ stderr: 'pipe',
122
+ })
123
+ const [stdout, stderr, exitCode] = await Promise.all([
124
+ new Response(process.stdout).text(),
125
+ new Response(process.stderr).text(),
126
+ process.exited,
127
+ ])
128
+
129
+ expect(exitCode, stderr).toBe(0)
130
+ expect(JSON.parse(stdout)).toEqual({
131
+ slug: 'local',
132
+ url: issuer,
133
+ issuer,
134
+ kind: 'bookmark',
135
+ state: 'ready',
136
+ })
137
+ expect(await readFile(path, 'utf8')).toBe(registry)
138
+ })
139
+ })
@@ -1,6 +1,44 @@
1
+ import type { DomainBundle, DomainInfo } from '@astrale-os/sdk/client/schema'
2
+
3
+ import { bundle, defineSchema, func, method, nodeClass, schema } from '@astrale-os/sdk/schema'
1
4
  import { describe, expect, test } from 'bun:test'
2
5
 
3
- import { parseIntrospectTarget } from '../introspect'
6
+ import { introspectCommand, parseIntrospectTarget } from '../introspect'
7
+
8
+ const source = defineSchema('host.astrale.ai', {
9
+ classes: {
10
+ Manager: nodeClass({
11
+ methods: {
12
+ createInstance: method({
13
+ auth: 'authenticated',
14
+ input: { type: 'object', properties: {}, additionalProperties: false },
15
+ output: { type: 'object', additionalProperties: true },
16
+ static: true,
17
+ }),
18
+ },
19
+ }),
20
+ },
21
+ functions: {
22
+ journal: func({
23
+ auth: 'authenticated',
24
+ input: { type: 'object', properties: {}, additionalProperties: false },
25
+ output: { type: 'object', additionalProperties: true },
26
+ }),
27
+ },
28
+ })
29
+
30
+ const info = {
31
+ origin: source.origin,
32
+ revision: schema.revision(source),
33
+ generation: 'sha256:generation',
34
+ publication: null,
35
+ readiness: 'sha256:readiness',
36
+ capabilities: { requested: {}, materialized: {} },
37
+ bindings: { callables: [], views: [] },
38
+ } satisfies DomainInfo
39
+
40
+ const installed = bundle.create(source)
41
+ const bundled = { domain: info, bundle: installed } satisfies DomainBundle
4
42
 
5
43
  describe('parseIntrospectTarget', () => {
6
44
  test('accepts a bare origin', () => {
@@ -18,4 +56,45 @@ describe('parseIntrospectTarget', () => {
18
56
  test('rejects an @id', () => {
19
57
  expect(() => parseIntrospectTarget('@abc')).toThrow('not an @id')
20
58
  })
59
+
60
+ test('routes bare, bundle, Method, and Function commands through the current Schema API', async () => {
61
+ const calls: string[] = []
62
+ const schema = {
63
+ inspect: async (origin: string) => {
64
+ calls.push(`inspect:${origin}`)
65
+ return info
66
+ },
67
+ bundle: async (origin: string) => {
68
+ calls.push(`bundle:${origin}`)
69
+ return bundled
70
+ },
71
+ }
72
+ const results: unknown[] = []
73
+ const runKernelCommand = async (input: {
74
+ fn: (context: unknown) => Promise<unknown>
75
+ }): Promise<void> => {
76
+ results.push(await input.fn({ session: { schema } }))
77
+ }
78
+ const dependencies = { runKernelCommand: runKernelCommand as never }
79
+
80
+ await introspectCommand('host.astrale.ai', { json: true }, dependencies)
81
+ await introspectCommand('host.astrale.ai', { bundle: true, json: true }, dependencies)
82
+ await introspectCommand(
83
+ '/:host.astrale.ai:class.Manager:createInstance',
84
+ { json: true },
85
+ dependencies,
86
+ )
87
+ await introspectCommand('/:host.astrale.ai:function.journal', { json: true }, dependencies)
88
+
89
+ expect(results[0]).toBe(info)
90
+ expect(results[1]).toBe(bundled)
91
+ expect(results[2]).toMatchObject({ class: 'Manager', method: 'createInstance' })
92
+ expect(results[3]).toMatchObject({ function: 'journal' })
93
+ expect(calls).toEqual([
94
+ 'inspect:host.astrale.ai',
95
+ 'bundle:host.astrale.ai',
96
+ 'bundle:host.astrale.ai',
97
+ 'bundle:host.astrale.ai',
98
+ ])
99
+ })
21
100
  })
@@ -1,6 +1,11 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
2
 
3
- import { acceptJournalPage, buildJournalInput } from '../logs'
3
+ import logsCommand, {
4
+ acceptJournalPage,
5
+ buildJournalInput,
6
+ followLogs,
7
+ formatFollowRecord,
8
+ } from '../logs'
4
9
 
5
10
  describe('buildJournalInput', () => {
6
11
  /** @evidence TEST-CLI-LOGS-MAPS-EXACT-JOURNAL-INPUT */
@@ -81,7 +86,15 @@ describe('acceptJournalPage', () => {
81
86
  occurredAt: '2026-08-19T16:51:10.049Z',
82
87
  committedAt: '2026-08-19T16:51:10.070Z',
83
88
  payload: { outcome: 'rejected' },
84
- correlation: { invocationId: 'cf862a64-3aa1-4343-ba86-f9b516c4ff95' },
89
+ correlation: {
90
+ operationId: 'operation-child',
91
+ parentOperationId: 'operation-parent',
92
+ invocationId: 'cf862a64-3aa1-4343-ba86-f9b516c4ff95',
93
+ rootInvocationId: 'invocation-root',
94
+ parentInvocationId: 'invocation-parent',
95
+ traceId: 'trace-1',
96
+ spanId: 'span-1',
97
+ },
85
98
  },
86
99
  ],
87
100
  })
@@ -91,7 +104,242 @@ describe('acceptJournalPage', () => {
91
104
  timestamp: '2026-08-19T16:51:10.049Z',
92
105
  occurredAt: '2026-08-19T16:51:10.049Z',
93
106
  committedAt: '2026-08-19T16:51:10.070Z',
107
+ correlation: {
108
+ operationId: 'operation-child',
109
+ parentOperationId: 'operation-parent',
110
+ invocationId: 'cf862a64-3aa1-4343-ba86-f9b516c4ff95',
111
+ rootInvocationId: 'invocation-root',
112
+ parentInvocationId: 'invocation-parent',
113
+ traceId: 'trace-1',
114
+ spanId: 'span-1',
115
+ },
94
116
  correlationId: 'cf862a64-3aa1-4343-ba86-f9b516c4ff95',
95
117
  })
96
118
  })
119
+
120
+ test('rejects malformed or invented structured correlation fields', () => {
121
+ const record = {
122
+ sequence: 1,
123
+ topic: 'function.invoke',
124
+ occurredAt: '2026-08-19T16:51:10.049Z',
125
+ payload: {},
126
+ }
127
+ expect(() => acceptJournalPage({ records: [{ ...record, correlation: 'opaque' }] })).toThrow(
128
+ 'correlation must be an object',
129
+ )
130
+ expect(() =>
131
+ acceptJournalPage({ records: [{ ...record, correlation: { authority: 'forged' } }] }),
132
+ ).toThrow('correlation.authority is unsupported')
133
+ for (const field of [
134
+ 'operationId',
135
+ 'parentOperationId',
136
+ 'invocationId',
137
+ 'rootInvocationId',
138
+ 'parentInvocationId',
139
+ 'traceId',
140
+ 'spanId',
141
+ ]) {
142
+ expect(() =>
143
+ acceptJournalPage({ records: [{ ...record, correlation: { [field]: 7 } }] }),
144
+ ).toThrow(`correlation.${field}`)
145
+ }
146
+ expect(() =>
147
+ acceptJournalPage({ records: [{ ...record, correlation: { invocationId: ' ' } }] }),
148
+ ).toThrow('must be non-empty')
149
+ expect(() =>
150
+ acceptJournalPage({
151
+ records: [{ ...record, correlation: { invocationId: 'x'.repeat(257) } }],
152
+ }),
153
+ ).toThrow('at most 256 UTF-8 bytes')
154
+ expect(
155
+ acceptJournalPage({
156
+ records: [{ ...record, correlation: { invocationId: 'x'.repeat(256) } }],
157
+ }).records[0].correlation?.invocationId,
158
+ ).toHaveLength(256)
159
+ expect(
160
+ acceptJournalPage({
161
+ records: [{ ...record, correlation: { invocationId: 'é'.repeat(128) } }],
162
+ }).records[0].correlation?.invocationId,
163
+ ).toHaveLength(128)
164
+ expect(() =>
165
+ acceptJournalPage({
166
+ records: [{ ...record, correlation: { invocationId: `${'é'.repeat(127)}€` } }],
167
+ }),
168
+ ).toThrow('at most 256 UTF-8 bytes')
169
+ })
170
+
171
+ test('keeps legacy identity compatibility coherent with structured correlation', () => {
172
+ const record = {
173
+ sequence: 1,
174
+ topic: 'function.invoke',
175
+ occurredAt: '2026-08-19T16:51:10.049Z',
176
+ payload: {},
177
+ }
178
+ expect(
179
+ acceptJournalPage({
180
+ records: [{ ...record, correlationId: 'legacy-only', causationId: 'legacy-cause' }],
181
+ }).records[0],
182
+ ).toMatchObject({ correlationId: 'legacy-only', causationId: 'legacy-cause' })
183
+ expect(
184
+ acceptJournalPage({
185
+ records: [
186
+ {
187
+ ...record,
188
+ correlationId: 'same',
189
+ correlation: { invocationId: 'same' },
190
+ },
191
+ ],
192
+ }).records[0],
193
+ ).toMatchObject({ correlationId: 'same', correlation: { invocationId: 'same' } })
194
+ expect(() =>
195
+ acceptJournalPage({
196
+ records: [
197
+ {
198
+ ...record,
199
+ correlationId: 'legacy',
200
+ correlation: { invocationId: 'structured' },
201
+ },
202
+ ],
203
+ }),
204
+ ).toThrow('conflicting correlation identifiers')
205
+ })
206
+
207
+ test('serializes one complete structured record per machine-follow line', () => {
208
+ const record = acceptJournalPage({
209
+ records: [
210
+ {
211
+ sequence: 2,
212
+ topic: 'function.invoke',
213
+ occurredAt: '2026-08-19T16:51:10.049Z',
214
+ payload: { outcome: 'completed' },
215
+ correlation: {
216
+ operationId: 'operation-child',
217
+ parentOperationId: 'operation-parent',
218
+ invocationId: 'invocation-child',
219
+ rootInvocationId: 'invocation-root',
220
+ parentInvocationId: 'invocation-parent',
221
+ traceId: 'trace-1',
222
+ spanId: 'span-1',
223
+ },
224
+ },
225
+ ],
226
+ }).records[0]
227
+ expect(formatFollowRecord(record).endsWith('\n')).toBe(true)
228
+ expect(JSON.parse(formatFollowRecord(record))).toEqual(record)
229
+ })
230
+ })
231
+
232
+ describe('follow output routing', () => {
233
+ const inputRecord = {
234
+ sequence: 2,
235
+ topic: 'function.invoke',
236
+ occurredAt: '2026-08-19T16:51:10.049Z',
237
+ payload: { outcome: 'completed' },
238
+ principal: 'principal-1',
239
+ correlation: {
240
+ invocationId: 'invocation-child',
241
+ rootInvocationId: 'invocation-root',
242
+ parentInvocationId: 'invocation-parent',
243
+ },
244
+ }
245
+ const admittedRecord = acceptJournalPage({ records: [inputRecord] }).records[0]
246
+
247
+ test('routes every effective machine mode through complete NDJSON records', async () => {
248
+ for (const { opts, tty } of [
249
+ { opts: { json: true }, tty: true },
250
+ { opts: { raw: true }, tty: true },
251
+ { opts: { format: 'json' as const }, tty: true },
252
+ { opts: { ci: true }, tty: true },
253
+ { opts: {}, tty: false },
254
+ { opts: { format: 'yaml' as const, json: true }, tty: true },
255
+ { opts: { format: 'yaml' as const, raw: true }, tty: true },
256
+ ]) {
257
+ const stdout = await captureFollowOutput({ ...opts, follow: true }, tty)
258
+ expect(stdout.endsWith('\n')).toBe(true)
259
+ expect(JSON.parse(stdout)).toEqual(admittedRecord)
260
+ }
261
+ })
262
+
263
+ test('keeps an unflagged TTY human-readable', async () => {
264
+ const stdout = await captureFollowOutput({ follow: true }, true)
265
+ expect(stdout).toContain('function.invoke')
266
+ expect(stdout).toContain('principal-1')
267
+ expect(stdout).not.toContain('invocation-child')
268
+ expect(() => JSON.parse(stdout)).toThrow()
269
+ })
270
+
271
+ test('rejects effective YAML before opening a Kernel session with INVALID_INPUT', async () => {
272
+ let runCalls = 0
273
+ await expect(
274
+ followLogs(
275
+ { follow: true, format: 'yaml' },
276
+ {
277
+ run: async () => {
278
+ runCalls += 1
279
+ },
280
+ pause: async () => {},
281
+ },
282
+ ),
283
+ ).rejects.toThrow('--follow does not support YAML')
284
+ expect(runCalls).toBe(0)
285
+
286
+ const originalExit = process.exit
287
+ const originalStderrWrite = process.stderr.write
288
+ let stderr = ''
289
+ process.stderr.write = ((chunk: string | Uint8Array) => {
290
+ stderr += typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8')
291
+ return true
292
+ }) as typeof process.stderr.write
293
+ process.exit = ((code?: string | number | null) => {
294
+ throw new Error(`exit:${String(code)}`)
295
+ }) as typeof process.exit
296
+ try {
297
+ await expect(logsCommand.action({ follow: true, format: 'yaml' })).rejects.toThrow('exit:1')
298
+ expect(JSON.parse(stderr)).toEqual({
299
+ error: 'INVALID_INPUT',
300
+ message: '--follow does not support YAML; use --json for an NDJSON stream',
301
+ })
302
+ } finally {
303
+ process.exit = originalExit
304
+ process.stderr.write = originalStderrWrite
305
+ }
306
+ })
307
+
308
+ async function captureFollowOutput(
309
+ opts: Parameters<typeof followLogs>[0],
310
+ tty: boolean,
311
+ ): Promise<string> {
312
+ const originalWrite = process.stdout.write
313
+ const originalTty = Object.getOwnPropertyDescriptor(process.stdout, 'isTTY')
314
+ let stdout = ''
315
+ let pages = 0
316
+ process.stdout.write = ((chunk: string | Uint8Array) => {
317
+ stdout += typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8')
318
+ return true
319
+ }) as typeof process.stdout.write
320
+ Object.defineProperty(process.stdout, 'isTTY', { configurable: true, value: tty })
321
+ try {
322
+ await expect(
323
+ followLogs(opts, {
324
+ run: async (input) => {
325
+ await input.fn({
326
+ session: {
327
+ call: async () => {
328
+ pages += 1
329
+ if (pages === 1) return { records: [inputRecord] }
330
+ throw new Error('end of controlled stream')
331
+ },
332
+ },
333
+ } as never)
334
+ },
335
+ pause: async () => {},
336
+ }),
337
+ ).rejects.toThrow('end of controlled stream')
338
+ return stdout
339
+ } finally {
340
+ process.stdout.write = originalWrite
341
+ if (originalTty === undefined) delete (process.stdout as { isTTY?: boolean }).isTTY
342
+ else Object.defineProperty(process.stdout, 'isTTY', originalTty)
343
+ }
344
+ }
97
345
  })