@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
@@ -6,6 +6,12 @@ type GraphContext = {
6
6
  getOrThrow: typeof getOrThrowMock
7
7
  mutate: typeof mutateMock
8
8
  }
9
+ session: {
10
+ schema: {
11
+ inspect: typeof inspectMock
12
+ bundle: typeof bundleMock
13
+ }
14
+ }
9
15
  }
10
16
 
11
17
  type RunOpts = {
@@ -36,6 +42,10 @@ let mutations: unknown[] = []
36
42
  let queryResult: unknown
37
43
  let getResult: unknown
38
44
  let mutationResult: unknown
45
+ let inspectResult: unknown
46
+ let bundleResult: unknown
47
+ let selfFailure: unknown
48
+ let selfContexts: unknown[] = []
39
49
 
40
50
  const queryMock = mock(async (ast: unknown, options?: unknown) => {
41
51
  queryCalls.push({ ast, options })
@@ -49,7 +59,11 @@ const mutateMock = mock(async (mutation: unknown) => {
49
59
  mutations.push(mutation)
50
60
  return mutationResult
51
61
  })
52
- const expandSelfInPathMock = mock(async (path: string) => {
62
+ const inspectMock = mock(async (_origin: string) => inspectResult)
63
+ const bundleMock = mock(async (_origin: string) => bundleResult)
64
+ const expandSelfInPathMock = mock(async (path: string, context: unknown) => {
65
+ selfContexts.push(context)
66
+ if (selfFailure !== undefined) throw selfFailure
53
67
  if (path === '@self') {
54
68
  return {
55
69
  path: '@expanded-self',
@@ -59,10 +73,12 @@ const expandSelfInPathMock = mock(async (path: string) => {
59
73
  return { path, meta: undefined }
60
74
  })
61
75
  const withSelfHintMock = mock(async (action: () => Promise<unknown>) => action())
76
+ const commandContext = {
77
+ graph: { query: queryMock, getOrThrow: getOrThrowMock, mutate: mutateMock },
78
+ session: { schema: { inspect: inspectMock, bundle: bundleMock } },
79
+ }
62
80
  const runKernelCommandMock = mock(async (run: RunOpts) => {
63
- const result = await run.fn({
64
- graph: { query: queryMock, getOrThrow: getOrThrowMock, mutate: mutateMock },
65
- })
81
+ const result = await run.fn(commandContext)
66
82
  await run.format?.(result, run.opts, true)
67
83
  })
68
84
 
@@ -88,9 +104,22 @@ beforeEach(() => {
88
104
  }
89
105
  getResult = undefined
90
106
  mutationResult = { createdNodes: {} }
107
+ inspectResult = {
108
+ origin: 'notes.example.dev',
109
+ revision: 'notes-v1',
110
+ generation: 'sha256:generation',
111
+ }
112
+ bundleResult = {
113
+ domain: inspectResult,
114
+ bundle: { origin: 'notes.example.dev', revision: 'notes-v1' },
115
+ }
116
+ selfFailure = undefined
117
+ selfContexts = []
91
118
  queryMock.mockClear()
92
119
  getOrThrowMock.mockClear()
93
120
  mutateMock.mockClear()
121
+ inspectMock.mockClear()
122
+ bundleMock.mockClear()
94
123
  expandSelfInPathMock.mockClear()
95
124
  withSelfHintMock.mockClear()
96
125
  runKernelCommandMock.mockClear()
@@ -161,12 +190,12 @@ describe('query command', () => {
161
190
  ])
162
191
  })
163
192
 
164
- test('authors a Definition source through the same canonical Query V6 call', async () => {
193
+ test('authors a Class source through the same canonical Query V6 call', async () => {
165
194
  const { queryCommand } = await import('../query')
166
195
 
167
196
  await queryCommand([], {
168
197
  json: true,
169
- definition: '/:issues.astrale.ai:class.Issue',
198
+ class: '/:issues.astrale.ai:class.Issue',
170
199
  limit: '20',
171
200
  })
172
201
 
@@ -179,8 +208,8 @@ describe('query command', () => {
179
208
  kind: 'node',
180
209
  terms: [
181
210
  {
182
- kind: 'definition',
183
- definition: { origin: 'issues.astrale.ai', kind: 'class', name: 'Issue' },
211
+ kind: 'class',
212
+ class: { origin: 'issues.astrale.ai', kind: 'class', name: 'Issue' },
184
213
  },
185
214
  ],
186
215
  binding: 'n0',
@@ -193,6 +222,44 @@ describe('query command', () => {
193
222
  ])
194
223
  })
195
224
 
225
+ test('retains the opaque continuation in machine output without wrapping the graph result', async () => {
226
+ const { queryCommand } = await import('../query')
227
+ const result = {
228
+ kind: 'graph' as const,
229
+ graph: {
230
+ nodes: [{ id: 'issue-1', class: 'issues.astrale.ai:class.Issue', props: {} }],
231
+ edges: [],
232
+ },
233
+ selection: { kind: 'node' as const, ids: ['issue-1'] },
234
+ }
235
+ queryResult = {
236
+ result,
237
+ page: { next: 'opaque-next-page' },
238
+ }
239
+
240
+ await queryCommand([], {
241
+ json: true,
242
+ class: '/:issues.astrale.ai:class.Issue',
243
+ limit: '1',
244
+ })
245
+
246
+ expect(JSON.parse(stdout)).toEqual({
247
+ ...result,
248
+ page: { next: 'opaque-next-page' },
249
+ })
250
+ })
251
+
252
+ test('resolves @self inside the command session and never relabels transport failure as input', async () => {
253
+ const { queryCommand } = await import('../query')
254
+ await queryCommand(['@self'], { json: true, limit: '2' })
255
+ expect(selfContexts).toEqual([commandContext])
256
+
257
+ const aggregate = new AggregateError([], '')
258
+ selfFailure = aggregate
259
+ await expect(queryCommand(['@self'], { json: true, limit: '2' })).rejects.toBe(aggregate)
260
+ expect(errors.join('\n')).not.toContain('INVALID_INPUT')
261
+ })
262
+
196
263
  /** @evidence TEST-CLI-QUERY-REJECTS-V1-BEFORE-CONNECTION */
197
264
  test('rejects a legacy AST before opening the command connection', async () => {
198
265
  const { queryCommand } = await import('../query')
@@ -215,7 +282,7 @@ describe('get command', () => {
215
282
  const { getCommand } = await import('../get')
216
283
  getResult = {
217
284
  id: 'note-1',
218
- class: '/:notes.example.dev:class.Note',
285
+ class: 'notes.example.dev:class.Note',
219
286
  props: { 'notes.example.dev:class.Note.property.title': 'Hello' },
220
287
  }
221
288
 
@@ -226,6 +293,47 @@ describe('get command', () => {
226
293
  expect(stdout).not.toContain('__labels')
227
294
  expect(stdout).not.toContain('classId')
228
295
  })
296
+
297
+ test('resolves @self through the same point-read command context', async () => {
298
+ const { getCommand } = await import('../get')
299
+ await getCommand('@self', { json: true })
300
+ expect(selfContexts).toEqual([commandContext])
301
+ expect(getTargets).toEqual(['@expanded-self'])
302
+ })
303
+ })
304
+
305
+ describe('introspect command', () => {
306
+ test('uses the split SDK Schema API without recreating the retired introspection request', async () => {
307
+ const { introspectCommand } = await import('../introspect')
308
+
309
+ await introspectCommand(
310
+ 'notes.example.dev',
311
+ { json: true },
312
+ {
313
+ runKernelCommand: runKernelCommandMock as never,
314
+ },
315
+ )
316
+
317
+ expect(inspectMock).toHaveBeenCalledWith('notes.example.dev')
318
+ expect(bundleMock).not.toHaveBeenCalled()
319
+ expect(JSON.parse(stdout)).toEqual(inspectResult)
320
+ })
321
+
322
+ test('requests the immutable bundle only when explicitly selected', async () => {
323
+ const { introspectCommand } = await import('../introspect')
324
+
325
+ await introspectCommand(
326
+ 'notes.example.dev',
327
+ { json: true, bundle: true },
328
+ {
329
+ runKernelCommand: runKernelCommandMock as never,
330
+ },
331
+ )
332
+
333
+ expect(bundleMock).toHaveBeenCalledWith('notes.example.dev')
334
+ expect(inspectMock).not.toHaveBeenCalled()
335
+ expect(JSON.parse(stdout)).toEqual(bundleResult)
336
+ })
229
337
  })
230
338
 
231
339
  describe('mutate command', () => {
@@ -242,7 +350,7 @@ describe('mutate command', () => {
242
350
  {
243
351
  op: 'node.create',
244
352
  as: 'note',
245
- class: '/:notes.example.dev:class.Note',
353
+ class: 'notes.example.dev:class.Note',
246
354
  props: {},
247
355
  },
248
356
  ],
@@ -258,7 +366,7 @@ describe('mutate command', () => {
258
366
  {
259
367
  op: 'node.create',
260
368
  as: 'note',
261
- class: '/:notes.example.dev:class.Note',
369
+ class: 'notes.example.dev:class.Note',
262
370
  props: {},
263
371
  },
264
372
  ],
@@ -1,11 +1,13 @@
1
- import { describe, expect, test } from 'bun:test'
1
+ import type { AuthApi, Identity, IssuerId, MintedCredential } from '@astrale-os/sdk/auth'
2
+
3
+ import { describe, expect, mock, test } from 'bun:test'
2
4
 
3
5
  import { AstraleError } from '../../errors'
4
- import { parseTtl } from '../token'
6
+ import { issueToken, parseTtl } from '../token'
5
7
 
6
8
  describe('parseTtl', () => {
7
- test('defaults to 3600 seconds', () => {
8
- expect(parseTtl(undefined)).toBe(3600)
9
+ test('defaults below the five-minute local source-credential lifetime', () => {
10
+ expect(parseTtl(undefined)).toBe(240)
9
11
  })
10
12
 
11
13
  test('rejects non-positive and non-integer values', () => {
@@ -19,3 +21,46 @@ describe('parseTtl', () => {
19
21
  expect(parseTtl('90')).toBe(90)
20
22
  })
21
23
  })
24
+
25
+ describe('issueToken', () => {
26
+ const kernel = 'https://kernel.test' as IssuerId
27
+ const identity = {
28
+ id: 'caller' as Identity['id'],
29
+ issuer: 'https://issuer.test' as Identity['issuer'],
30
+ subject: 'caller' as Identity['subject'],
31
+ frozen: false,
32
+ requiredClaims: [],
33
+ } satisfies Identity
34
+
35
+ test('mints a reusable top-level credential for the Kernel audience', async () => {
36
+ const mint = mock(async () => 'kernel-token' as MintedCredential)
37
+ const delegate = mock(async () => 'delegated-token' as MintedCredential)
38
+ const whoami = mock(async () => identity)
39
+ const auth = { mint, delegate, whoami } satisfies Pick<AuthApi, 'delegate' | 'mint' | 'whoami'>
40
+
41
+ await expect(issueToken(auth, kernel, kernel, 90)).resolves.toBe(
42
+ 'kernel-token' as MintedCredential,
43
+ )
44
+ expect(mint).toHaveBeenCalledWith({ ttlSeconds: 90 })
45
+ expect(whoami).not.toHaveBeenCalled()
46
+ expect(delegate).not.toHaveBeenCalled()
47
+ })
48
+
49
+ test('delegates the selected identity only for an external audience', async () => {
50
+ const mint = mock(async () => 'kernel-token' as MintedCredential)
51
+ const delegate = mock(async () => 'delegated-token' as MintedCredential)
52
+ const whoami = mock(async () => identity)
53
+ const auth = { mint, delegate, whoami } satisfies Pick<AuthApi, 'delegate' | 'mint' | 'whoami'>
54
+
55
+ await expect(issueToken(auth, kernel, 'https://service.test' as IssuerId, 120)).resolves.toBe(
56
+ 'delegated-token' as MintedCredential,
57
+ )
58
+ expect(mint).not.toHaveBeenCalled()
59
+ expect(whoami).toHaveBeenCalledTimes(1)
60
+ expect(delegate).toHaveBeenCalledWith(identity.id, {
61
+ audience: 'https://service.test',
62
+ ttlSeconds: 120,
63
+ attenuation: { kind: 'identity', self: true },
64
+ })
65
+ })
66
+ })
@@ -35,9 +35,11 @@ describe('CLI update staleness', () => {
35
35
  const result = await cliStale(
36
36
  { channel: 'canary' },
37
37
  {
38
- update: async () => {
39
- throw new Error('package managed')
40
- },
38
+ update: async () => ({
39
+ status: 'managed',
40
+ currentVersion: '1.0.0-beta.0',
41
+ executable: '/opt/homebrew/bin/node',
42
+ }),
41
43
  fetchPackageVersion: async ({ channel }) => {
42
44
  expect(channel).toBe('canary')
43
45
  return '1.0.0-canary.0'
@@ -52,6 +54,26 @@ describe('CLI update staleness', () => {
52
54
  })
53
55
  })
54
56
 
57
+ test('does not misclassify a script update failure as package-managed', async () => {
58
+ const fetchPackageVersion = mock(async () => '9.9.9')
59
+ const result = await cliStale(
60
+ {},
61
+ {
62
+ update: async () => {
63
+ throw new Error('release endpoint unavailable')
64
+ },
65
+ fetchPackageVersion,
66
+ },
67
+ )
68
+
69
+ expect(result).toMatchObject({
70
+ stale: false,
71
+ managed: false,
72
+ error: 'release endpoint unavailable',
73
+ })
74
+ expect(fetchPackageVersion).not.toHaveBeenCalled()
75
+ })
76
+
55
77
  test('maps the default to beta and the stable channel to npm latest', async () => {
56
78
  const originalFetch = globalThis.fetch
57
79
  const fetchMock = mock(async (input: RequestInfo | URL) => {
@@ -52,7 +52,6 @@ const resolved = [
52
52
  kind: 'definition' as const,
53
53
  definitions: [{ origin: 'ai-gateway.astrale.ai', kind: 'class' as const, name: 'Model' }],
54
54
  },
55
- auth: 'required' as const,
56
55
  },
57
56
  }),
58
57
  route({
@@ -62,7 +61,7 @@ const resolved = [
62
61
  issuer: 'https://ai-gateway.astrale.ai',
63
62
  etag: `sha256:${'c'.repeat(64)}`,
64
63
  revision: `sha256:${'d'.repeat(64)}`,
65
- declaration: { target: { kind: 'domain' as const }, auth: 'public' as const },
64
+ declaration: { target: { kind: 'domain' as const } },
66
65
  }),
67
66
  ]
68
67
 
@@ -147,3 +146,52 @@ describe('view capture timing', () => {
147
146
  expect(commands.length).toBeGreaterThan(2)
148
147
  })
149
148
  })
149
+
150
+ describe('view session runtime', () => {
151
+ test('builds one serve config with the admitted operator origin grant', async () => {
152
+ const { createViewServeConfig } = await import('../view')
153
+ const record = {
154
+ id: 'v-proof',
155
+ pid: 0,
156
+ port: 4419,
157
+ nonce: 'proof',
158
+ pageUrl: 'http://127.0.0.1:4419/s/proof/',
159
+ view: { target: Path.parse('/:ai-gateway.astrale.ai').raw, route: resolved[0] },
160
+ createdAt: '2026-08-26T00:00:00.000Z',
161
+ }
162
+
163
+ const config = createViewServeConfig(
164
+ record,
165
+ { allowExternalOrigin: ['https://connect.nango.dev/'] },
166
+ { url: 'https://kernel.test', kernelIssuer: 'https://kernel.test' },
167
+ )
168
+
169
+ expect(config.session).toBe(record)
170
+ expect(config.externalOrigins).toEqual(['https://connect.nango.dev'])
171
+ expect(config.proxy).toEqual({
172
+ kernelUrl: 'https://kernel.test',
173
+ issuer: 'https://kernel.test',
174
+ caFile: undefined,
175
+ direct: true,
176
+ })
177
+ })
178
+
179
+ test('spawns the detached server from a compiled executable without its virtual Bun entry', async () => {
180
+ const { resolveServeRuntime, viewServeInvocation } = await import('../view')
181
+
182
+ const runtime = await resolveServeRuntime({
183
+ executable: '/opt/astrale/bin/astrale',
184
+ entry: '/$bunfs/root/astrale',
185
+ exists: () => true,
186
+ find: async () => '/usr/bin/node',
187
+ })
188
+ expect(runtime).toEqual({
189
+ file: '/opt/astrale/bin/astrale',
190
+ args: [],
191
+ })
192
+ expect(viewServeInvocation(runtime, '/tmp/view.config.json')).toEqual({
193
+ file: '/opt/astrale/bin/astrale',
194
+ args: ['__view-serve', '--config', '/tmp/view.config.json'],
195
+ })
196
+ })
197
+ })
@@ -4,7 +4,7 @@ import { getDefault, readIdentities } from '../../identity/index'
4
4
  import { deleteIdpSession, listIdpSessions } from '../../lib/idp'
5
5
  import { log } from '../../lib/log'
6
6
  import { output, RAW_OUTPUT_OPTIONS } from '../../lib/output'
7
- import { ExchangeCredentialCache } from '../../state/index'
7
+ import { ExchangeCredentialCache, SESSION_ROUTE_STORE } from '../../state/index'
8
8
 
9
9
  export default {
10
10
  name: 'logout',
@@ -30,6 +30,7 @@ export default {
30
30
 
31
31
  for (const name of names) await deleteIdpSession(name)
32
32
  await new ExchangeCredentialCache().clear()
33
+ SESSION_ROUTE_STORE.clear()
33
34
 
34
35
  if (opts.raw || opts.json) {
35
36
  output({ cleared: names }, opts)
@@ -13,6 +13,7 @@ import {
13
13
  profileDirFor,
14
14
  saveSession,
15
15
  } from '../lib/browser'
16
+ import { sweepBrowserProfiles } from '../lib/browser-retention'
16
17
  import { readLocalStatus } from '../lib/local-status'
17
18
  import { fatal, log } from '../lib/log'
18
19
  import { isMachine, output, RAW_OUTPUT_OPTIONS, type RawOutputOpts } from '../lib/output'
@@ -25,6 +26,29 @@ type BrowserOpts = RawOutputOpts & {
25
26
  check?: boolean
26
27
  }
27
28
 
29
+ /**
30
+ * Opportunistic profile retention, the same `git gc --auto` shape the session
31
+ * store uses: no daemon, no cron, it just rides on the command that created the
32
+ * mess. Silent when there is nothing to do, and never fatal — losing a sweep is
33
+ * strictly better than losing the browser command.
34
+ */
35
+ async function reportSweep(machine: boolean): Promise<void> {
36
+ try {
37
+ const swept = await sweepBrowserProfiles()
38
+ if (machine || swept.bytesFreed === 0) return
39
+ const freed = `${(swept.bytesFreed / 1024 / 1024).toFixed(0)} MB`
40
+ const what = [
41
+ swept.removed.length > 0 ? `${swept.removed.length} dormant profile(s) removed` : null,
42
+ swept.purged.length > 0 ? `${swept.purged.length} cache(s) trimmed` : null,
43
+ ]
44
+ .filter(Boolean)
45
+ .join(', ')
46
+ log.dim(`retention: ${what} — ${freed} freed`)
47
+ } catch {
48
+ /* retention must never break the browser command */
49
+ }
50
+ }
51
+
28
52
  const LOGIN_TIMEOUT_MS = 180_000
29
53
  const POLL_INTERVAL_MS = 2500
30
54
 
@@ -125,6 +149,11 @@ Examples:
125
149
  const host = new URL(gui).host
126
150
  await requireAgentBrowser(machine, opts)
127
151
 
152
+ // Retention runs BEFORE anything launches, so no profile we touch can be in
153
+ // use by a browser this command started. Profiles held by someone else's
154
+ // live browser are skipped by the sweep itself.
155
+ await reportSweep(machine)
156
+
128
157
  const usingCdp = !!opts.cdp
129
158
  const profile = usingCdp ? null : (opts.profile ?? profileDirFor(host))
130
159
  const target = usingCdp ? { cdp: opts.cdp } : { profile: profile! }
@@ -1,8 +1,9 @@
1
1
  import type { Path } from '@astrale-os/sdk/graph/path'
2
+ import type { ResolvedFunction, ResolvedMethod } from '@astrale-os/sdk/schema'
2
3
 
3
- import { AstraleError } from '../errors'
4
+ import { bundle, schema } from '@astrale-os/sdk/schema'
4
5
 
5
- type JsonRecord = Record<string, unknown>
6
+ import { AstraleError } from '../errors'
6
7
 
7
8
  export interface CallableDescription {
8
9
  readonly path: string
@@ -18,40 +19,40 @@ export interface CallableDescription {
18
19
  readonly candidates?: readonly CallableDescription[]
19
20
  }
20
21
 
21
- /** Resolve one callable's input/output from an installed Domain schema document. */
22
- export function describeCallableFromSchema(
22
+ /** Resolve one callable from an admitted installed Domain bundle. */
23
+ export function describeCallableFromBundle(
23
24
  path: Path,
24
- schema: unknown,
25
+ input: unknown,
25
26
  ): CallableDescription | undefined {
26
27
  if (path.ast.anchor.kind !== 'domain') return undefined
27
28
  const origin = path.ast.anchor.origin
28
29
  const last = path.ast.steps.at(-1)
29
- const document = asRecord(schema)
30
- if (document === undefined || last === undefined) return undefined
30
+ if (last === undefined) return undefined
31
+ const domain = schema.resolve(bundle.accept(input).root)
32
+ if (domain.origin !== origin) return undefined
31
33
 
32
34
  if (last.kind === 'method') {
33
- const owner = path.ast.steps.at(-2)
34
- if (
35
- owner?.kind === 'projection' &&
36
- (owner.projection.kind === 'class' || owner.projection.kind === 'interface')
37
- ) {
38
- const bag = asRecord(
39
- owner.projection.kind === 'class' ? document.classes : document.interfaces,
35
+ const ownerStep = path.ast.steps.at(-2)
36
+ if (ownerStep?.kind === 'projection' && ownerStep.projection.kind === 'class') {
37
+ const owner = domain.classes[ownerStep.projection.name]
38
+ if (owner?.kind !== 'node') return undefined
39
+ const method = namedMethod(
40
+ last.dispatch === 'static' ? owner.static.methods : owner.methods,
41
+ last.name,
42
+ last.dispatch,
40
43
  )
41
- const definition = asRecord(bag?.[owner.projection.name])
42
- const method = asRecord(asRecord(definition?.methods)?.[last.name])
43
- if (method !== undefined) {
44
- return Object.freeze({
45
- path: path.raw,
46
- origin,
47
- class: owner.projection.name,
48
- method: last.name,
49
- dispatch: last.dispatch,
50
- ...callableFields(method),
51
- })
52
- }
44
+ return method === undefined
45
+ ? undefined
46
+ : description(path, origin, owner.ref.name, method, last.dispatch)
53
47
  }
54
- const matches = findNamedMethods(document, origin, path.raw, last.name, last.dispatch)
48
+
49
+ const matches = Object.values(domain.classes).flatMap((owner) => {
50
+ if (owner.kind !== 'node') return []
51
+ const method = namedMethod(owner.methods, last.name, last.dispatch)
52
+ return method === undefined
53
+ ? []
54
+ : [description(path, origin, owner.ref.name, method, last.dispatch)]
55
+ })
55
56
  if (matches.length === 1) return matches[0]
56
57
  if (matches.length > 1) {
57
58
  return Object.freeze({
@@ -66,13 +67,13 @@ export function describeCallableFromSchema(
66
67
  }
67
68
 
68
69
  if (last.kind === 'projection' && last.projection.kind === 'function') {
69
- const fn = asRecord(asRecord(document.functions)?.[last.projection.name])
70
- if (fn === undefined) return undefined
70
+ const callable = domain.functions[last.projection.name]
71
+ if (callable === undefined) return undefined
71
72
  return Object.freeze({
72
73
  path: path.raw,
73
74
  origin,
74
- function: last.projection.name,
75
- ...callableFields(fn),
75
+ function: callable.ref.name,
76
+ ...callableFields(callable),
76
77
  })
77
78
  }
78
79
  return undefined
@@ -82,50 +83,41 @@ export function missingCallableDescription(path: string): AstraleError {
82
83
  return new AstraleError(
83
84
  'CALL_DESCRIBE_UNAVAILABLE',
84
85
  `No callable schema is installed for ${path}.`,
85
- 'Use a Domain-rooted Path such as /:host.astrale.ai:class.Manager:createInstance. Method Paths are not Function nodes.',
86
+ 'Use a Domain-rooted Path such as /:kernel.astrale.ai:class.Identity:whois. Method Paths are not Function nodes.',
86
87
  )
87
88
  }
88
89
 
89
- function findNamedMethods(
90
- schema: JsonRecord,
90
+ function namedMethod(
91
+ methods: Iterable<ResolvedMethod>,
92
+ name: string,
93
+ dispatch: 'static' | 'instance',
94
+ ): ResolvedMethod | undefined {
95
+ const expectedStatic = dispatch === 'static'
96
+ return [...methods].find((method) => method.name === name && method.static === expectedStatic)
97
+ }
98
+
99
+ function description(
100
+ path: Path,
91
101
  origin: string,
92
- path: string,
93
- method: string,
102
+ className: string,
103
+ callable: ResolvedMethod,
94
104
  dispatch: 'static' | 'instance',
95
- ): CallableDescription[] {
96
- const matches: CallableDescription[] = []
97
- for (const bag of [schema.classes, schema.interfaces]) {
98
- const definitions = asRecord(bag)
99
- if (definitions === undefined) continue
100
- for (const [className, definition] of Object.entries(definitions)) {
101
- const methodDef = asRecord(asRecord(asRecord(definition)?.methods)?.[method])
102
- if (methodDef === undefined) continue
103
- matches.push(
104
- Object.freeze({
105
- path,
106
- origin,
107
- class: className,
108
- method,
109
- dispatch,
110
- ...callableFields(methodDef),
111
- }),
112
- )
113
- }
114
- }
115
- return matches
105
+ ): CallableDescription {
106
+ return Object.freeze({
107
+ path: path.raw,
108
+ origin,
109
+ class: className,
110
+ method: callable.name,
111
+ dispatch,
112
+ ...callableFields(callable),
113
+ })
116
114
  }
117
115
 
118
- function callableFields(value: JsonRecord): Partial<CallableDescription> {
116
+ function callableFields(callable: ResolvedFunction | ResolvedMethod): Partial<CallableDescription> {
119
117
  return {
120
- ...(typeof value.description === 'string' ? { description: value.description } : {}),
121
- ...(value.auth === undefined ? {} : { auth: value.auth }),
122
- ...(value.input === undefined ? {} : { input: value.input }),
123
- ...(value.output === undefined ? {} : { output: value.output }),
118
+ ...(callable.description === undefined ? {} : { description: callable.description }),
119
+ auth: callable.auth,
120
+ input: callable.input,
121
+ output: callable.output,
124
122
  }
125
123
  }
126
-
127
- function asRecord(input: unknown): JsonRecord | undefined {
128
- return input !== null && typeof input === 'object' && !Array.isArray(input)
129
- ? (input as JsonRecord)
130
- : undefined
131
- }