@astrale-os/cli 0.8.0-alpha.0 → 0.8.1-alpha.2

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 (317) hide show
  1. package/README.md +4 -1
  2. package/dist/astrale.js +62968 -41240
  3. package/dist/public/connect-core.js +55030 -0
  4. package/dist/public/keys/index.js +45631 -0
  5. package/dist/public/paths/index.js +43990 -0
  6. package/dist/types/admin/instance/model.d.ts +40 -0
  7. package/dist/types/connect-core.d.ts +22 -0
  8. package/dist/types/connection/auth.d.ts +33 -0
  9. package/dist/types/errors.d.ts +23 -0
  10. package/dist/types/identity/registry.d.ts +25 -0
  11. package/dist/types/keys/algorithm.d.ts +6 -0
  12. package/dist/types/keys/credential.d.ts +21 -0
  13. package/dist/types/keys/index.d.ts +4 -0
  14. package/dist/types/keys/pair.d.ts +34 -0
  15. package/dist/types/lib/admin-target.d.ts +74 -0
  16. package/dist/types/lib/ca-fetch.d.ts +2 -0
  17. package/dist/types/lib/config.d.ts +71 -0
  18. package/dist/types/lib/format.d.ts +1 -0
  19. package/dist/types/lib/idp-session.d.ts +33 -0
  20. package/dist/types/lib/idp.d.ts +397 -0
  21. package/dist/types/lib/instance-target.d.ts +36 -0
  22. package/dist/types/lib/instance.d.ts +186 -0
  23. package/dist/types/lib/log.d.ts +30 -0
  24. package/dist/types/lib/login-flow.d.ts +49 -0
  25. package/dist/types/lib/meta.d.ts +36 -0
  26. package/dist/types/lib/output.d.ts +83 -0
  27. package/dist/types/lib/table.d.ts +18 -0
  28. package/dist/types/lib/validation.d.ts +11 -0
  29. package/dist/types/paths/index.d.ts +2 -0
  30. package/dist/types/state/exchange-credentials.d.ts +25 -0
  31. package/dist/types/state/files.d.ts +9 -0
  32. package/dist/types/state/identities.d.ts +42 -0
  33. package/dist/types/state/index.d.ts +8 -0
  34. package/dist/types/state/paths.d.ts +32 -0
  35. package/package.json +30 -8
  36. package/src/admin/.spec/api.d.ts +3 -0
  37. package/src/admin/.spec/architecture.md +15 -0
  38. package/src/admin/.spec/icon.svg +3 -0
  39. package/src/admin/.spec/layout.ts +6 -0
  40. package/src/admin/catalog/.spec/api.d.ts +65 -0
  41. package/src/admin/catalog/.spec/architecture.md +6 -0
  42. package/src/admin/catalog/.spec/icon.svg +3 -0
  43. package/src/admin/catalog/.spec/layout.ts +6 -0
  44. package/src/admin/catalog/__tests__/client.test.ts +171 -0
  45. package/src/admin/catalog/client.ts +214 -0
  46. package/src/admin/catalog/index.ts +13 -0
  47. package/src/admin/catalog/model.ts +42 -0
  48. package/src/admin/catalog/tsconfig.json +8 -0
  49. package/src/admin/graph/.spec/api.d.ts +28 -0
  50. package/src/admin/graph/.spec/architecture.md +5 -0
  51. package/src/admin/graph/.spec/icon.svg +3 -0
  52. package/src/admin/graph/.spec/layout.ts +3 -0
  53. package/src/admin/graph/index.ts +6 -0
  54. package/src/admin/graph/nodes.ts +56 -0
  55. package/src/admin/index.ts +2 -0
  56. package/src/admin/instance/.spec/api.d.ts +66 -0
  57. package/src/admin/instance/.spec/architecture.md +10 -0
  58. package/src/admin/instance/.spec/icon.svg +3 -0
  59. package/src/admin/instance/.spec/layout.ts +6 -0
  60. package/src/admin/instance/__tests__/client.test.ts +289 -0
  61. package/src/admin/instance/client.ts +298 -0
  62. package/src/admin/instance/index.ts +16 -0
  63. package/src/admin/instance/model.ts +65 -0
  64. package/src/admin/instance/tsconfig.json +8 -0
  65. package/src/commands/__tests__/admin-instance.test.ts +3 -36
  66. package/src/commands/__tests__/call.test.ts +21 -68
  67. package/src/commands/__tests__/domain-install-owned.test.ts +26 -27
  68. package/src/commands/__tests__/domain-list.test.ts +2 -6
  69. package/src/commands/__tests__/logs.test.ts +50 -98
  70. package/src/commands/__tests__/ls.test.ts +21 -17
  71. package/src/commands/__tests__/read-commands.test.ts +171 -101
  72. package/src/commands/__tests__/view.test.ts +100 -51
  73. package/src/commands/admin/status.ts +3 -3
  74. package/src/commands/admin/use.ts +29 -6
  75. package/src/commands/auth/login.ts +1 -1
  76. package/src/commands/auth/logout.ts +4 -2
  77. package/src/commands/auth/status.ts +2 -2
  78. package/src/commands/auth/token.ts +2 -2
  79. package/src/commands/browser.ts +1 -1
  80. package/src/commands/call.ts +55 -91
  81. package/src/commands/describe.ts +52 -147
  82. package/src/commands/domain/install.ts +81 -50
  83. package/src/commands/domain/list.ts +4 -8
  84. package/src/commands/domain/publish.ts +9 -31
  85. package/src/commands/get.ts +32 -65
  86. package/src/commands/identity/__tests__/register.test.ts +93 -0
  87. package/src/commands/identity/create.ts +2 -2
  88. package/src/commands/identity/delete.ts +2 -2
  89. package/src/commands/identity/export.ts +7 -33
  90. package/src/commands/identity/import.ts +21 -70
  91. package/src/commands/identity/list.ts +2 -2
  92. package/src/commands/identity/register.ts +148 -117
  93. package/src/commands/identity/sync.ts +2 -2
  94. package/src/commands/identity/unsync.ts +2 -2
  95. package/src/commands/identity/use.ts +2 -2
  96. package/src/commands/identity/whoami.ts +2 -2
  97. package/src/commands/idp/add.ts +1 -1
  98. package/src/commands/idp/list.ts +1 -1
  99. package/src/commands/idp/refresh.ts +1 -1
  100. package/src/commands/idp/remove.ts +2 -2
  101. package/src/commands/idp/show.ts +1 -1
  102. package/src/commands/instance/active.ts +2 -2
  103. package/src/commands/instance/bookmark.ts +8 -2
  104. package/src/commands/instance/create.ts +1 -1
  105. package/src/commands/instance/delete.ts +4 -10
  106. package/src/commands/instance/forget.ts +1 -1
  107. package/src/commands/instance/list.ts +3 -3
  108. package/src/commands/instance/status.ts +5 -7
  109. package/src/commands/instance/use.ts +3 -3
  110. package/src/commands/logs.ts +152 -279
  111. package/src/commands/ls.ts +78 -204
  112. package/src/commands/mutate.ts +70 -139
  113. package/src/commands/query.ts +100 -278
  114. package/src/commands/session/analyze.ts +1 -1
  115. package/src/commands/session/list.ts +1 -1
  116. package/src/commands/setup.ts +1 -1
  117. package/src/commands/status.ts +1 -1
  118. package/src/commands/studio.ts +1 -1
  119. package/src/commands/token.ts +26 -21
  120. package/src/commands/update.ts +1 -1
  121. package/src/commands/use.ts +2 -3
  122. package/src/commands/view-serve.ts +1 -1
  123. package/src/commands/view.ts +67 -109
  124. package/src/connect-core.test.ts +38 -0
  125. package/src/connect-core.ts +42 -0
  126. package/src/connection/.spec/api.d.ts +111 -0
  127. package/src/connection/.spec/architecture.md +41 -0
  128. package/src/connection/.spec/capabilities/connection.ts +13 -0
  129. package/src/connection/.spec/examples/call.ts +7 -0
  130. package/src/connection/.spec/flows/command.ts +20 -0
  131. package/src/connection/.spec/flows/session.ts +61 -0
  132. package/src/connection/.spec/laws/connection.ts +181 -0
  133. package/src/connection/.spec/layout.ts +19 -0
  134. package/src/{kernel → connection}/__tests__/auth.test.ts +26 -2
  135. package/src/connection/__tests__/ca-fetch.test.ts +53 -0
  136. package/src/connection/__tests__/call.test.ts +17 -0
  137. package/src/connection/__tests__/credential.test.ts +109 -0
  138. package/src/connection/__tests__/errors.test.ts +96 -0
  139. package/src/connection/__tests__/exchange.test.ts +224 -0
  140. package/src/connection/__tests__/fixtures/localhost-cert.pem +19 -0
  141. package/src/connection/__tests__/fixtures/localhost-key.base64 +1 -0
  142. package/src/connection/__tests__/managed.test.ts +39 -0
  143. package/src/connection/__tests__/self.test.ts +51 -0
  144. package/src/connection/__tests__/session.test.ts +140 -0
  145. package/src/connection/__tests__/target.test.ts +93 -0
  146. package/src/{kernel → connection}/auth.ts +8 -8
  147. package/src/connection/call.ts +7 -0
  148. package/src/{kernel/run.ts → connection/command.ts} +22 -13
  149. package/src/connection/credential.ts +114 -0
  150. package/src/{kernel → connection}/errors.ts +32 -9
  151. package/src/connection/exchange.ts +274 -0
  152. package/src/connection/index.ts +8 -0
  153. package/src/connection/self.ts +120 -0
  154. package/src/connection/session.ts +176 -0
  155. package/src/connection/target.ts +111 -0
  156. package/src/connection/tsconfig.json +6 -0
  157. package/src/graph/.spec/api.d.ts +34 -0
  158. package/src/graph/.spec/laws/documents.ts +24 -0
  159. package/src/graph/.spec/laws/projection.ts +13 -0
  160. package/src/graph/.spec/layout.ts +6 -0
  161. package/src/graph/__tests__/mutation.test.ts +43 -0
  162. package/src/graph/__tests__/projection.test.ts +22 -0
  163. package/src/graph/__tests__/query.test.ts +154 -0
  164. package/src/graph/index.ts +3 -0
  165. package/src/graph/mutation.ts +18 -0
  166. package/src/graph/projection.ts +21 -0
  167. package/src/graph/query.ts +108 -0
  168. package/src/graph/tsconfig.json +6 -0
  169. package/src/identity/.history/ard/bootstrap-root-transfer.md +57 -0
  170. package/src/identity/.history/ard/domain-mediated-registration.md +36 -0
  171. package/src/identity/.spec/api.d.ts +124 -0
  172. package/src/identity/.spec/architecture.md +33 -0
  173. package/src/identity/.spec/capabilities/identity.ts +7 -0
  174. package/src/identity/.spec/flows/bootstrap-root.ts +79 -0
  175. package/src/identity/.spec/flows/domain-mediated-registration.ts +51 -0
  176. package/src/identity/.spec/flows/import.ts +29 -0
  177. package/src/identity/.spec/laws/identity.ts +56 -0
  178. package/src/identity/.spec/layout.ts +13 -0
  179. package/src/identity/.spec/schemas/identity-export-v1.schema.json +25 -0
  180. package/src/identity/__tests__/fixtures/registry-journey.ts +53 -0
  181. package/src/identity/__tests__/registration.test.ts +64 -0
  182. package/src/identity/__tests__/registry.test.ts +70 -0
  183. package/src/identity/__tests__/transfer.test.ts +198 -0
  184. package/src/identity/index.ts +23 -0
  185. package/src/identity/registration.ts +72 -0
  186. package/src/identity/registry.ts +172 -0
  187. package/src/identity/transfer.ts +187 -0
  188. package/src/identity/tsconfig.json +6 -0
  189. package/src/index.ts +1 -0
  190. package/src/keys/.spec/api.d.ts +74 -0
  191. package/src/keys/.spec/architecture.md +13 -0
  192. package/src/keys/.spec/laws/keys.ts +43 -0
  193. package/src/keys/.spec/layout.ts +6 -0
  194. package/src/keys/__tests__/algorithm.test.ts +39 -0
  195. package/src/keys/__tests__/keys.test.ts +168 -0
  196. package/src/keys/algorithm.ts +33 -0
  197. package/src/keys/credential.ts +112 -0
  198. package/src/keys/index.ts +14 -0
  199. package/src/keys/pair.ts +219 -0
  200. package/src/keys/tsconfig.json +6 -0
  201. package/src/lib/__tests__/admin-target.test.ts +67 -7
  202. package/src/lib/__tests__/binary.test.ts +2 -3
  203. package/src/lib/__tests__/command-dx.test.ts +2 -2
  204. package/src/lib/__tests__/config.test.ts +3 -2
  205. package/src/lib/__tests__/idp.test.ts +4 -6
  206. package/src/lib/__tests__/instance-target.test.ts +7 -4
  207. package/src/lib/__tests__/local-status.test.ts +1 -1
  208. package/src/lib/__tests__/log.test.ts +31 -0
  209. package/src/lib/__tests__/meta.test.ts +55 -0
  210. package/src/lib/__tests__/sdk-deps.test.ts +1 -1
  211. package/src/lib/__tests__/self.test.ts +8 -230
  212. package/src/lib/__tests__/use-target.test.ts +1 -1
  213. package/src/lib/__tests__/view-open-intent.test.ts +78 -104
  214. package/src/lib/__tests__/view-session.test.ts +99 -0
  215. package/src/lib/admin-domain.ts +39 -25
  216. package/src/lib/admin-instance.ts +38 -43
  217. package/src/lib/admin-target.ts +67 -13
  218. package/src/lib/binary.ts +13 -34
  219. package/src/lib/browser.ts +1 -1
  220. package/src/{kernel → lib}/ca-fetch.ts +35 -27
  221. package/src/lib/config.ts +1 -1
  222. package/src/lib/idp-session.ts +1 -1
  223. package/src/lib/idp.ts +3 -8
  224. package/src/lib/instance-target.ts +12 -8
  225. package/src/lib/instance.ts +34 -2
  226. package/src/lib/local-status.ts +2 -2
  227. package/src/lib/log.ts +3 -1
  228. package/src/lib/login-flow.ts +32 -5
  229. package/src/lib/meta.ts +15 -11
  230. package/src/lib/provision-instance.ts +4 -12
  231. package/src/lib/self.ts +5 -147
  232. package/src/lib/update.ts +1 -1
  233. package/src/lib/use-target.ts +1 -1
  234. package/src/lib/validation.ts +2 -2
  235. package/src/lib/view/open-intent.ts +7 -37
  236. package/src/lib/view/port-allocation.ts +1 -1
  237. package/src/lib/view/resolve.ts +52 -45
  238. package/src/lib/view/server.ts +16 -33
  239. package/src/lib/view/session.ts +51 -22
  240. package/src/paths/.spec/api.d.ts +13 -0
  241. package/src/paths/.spec/architecture.md +5 -0
  242. package/src/paths/.spec/layout.ts +3 -0
  243. package/src/paths/index.ts +13 -0
  244. package/src/program/.spec/api.d.ts +42 -0
  245. package/src/program/.spec/architecture.md +18 -0
  246. package/src/program/.spec/laws/program.ts +29 -0
  247. package/src/program/.spec/layout.ts +14 -0
  248. package/src/program/__tests__/program.test.ts +359 -0
  249. package/src/program/build.ts +190 -0
  250. package/src/program/command.ts +42 -0
  251. package/src/program/index.ts +2 -0
  252. package/src/program/options.ts +40 -0
  253. package/src/{registry.ts → program/registry.ts} +5 -1
  254. package/src/program/tsconfig.json +6 -0
  255. package/src/setup/steps/admin.ts +18 -3
  256. package/src/setup/steps/instance.ts +1 -1
  257. package/src/state/.spec/api.d.ts +133 -0
  258. package/src/state/.spec/architecture.md +31 -0
  259. package/src/state/.spec/capabilities/state.ts +7 -0
  260. package/src/state/.spec/flows/identity-update.ts +22 -0
  261. package/src/state/.spec/laws/state.ts +73 -0
  262. package/src/state/.spec/layout.ts +14 -0
  263. package/src/state/__tests__/exchange-credentials.test.ts +188 -0
  264. package/src/state/__tests__/files.test.ts +113 -0
  265. package/src/state/__tests__/fixtures/identity-transition.ts +37 -0
  266. package/src/state/__tests__/identities.test.ts +142 -0
  267. package/src/state/__tests__/paths.test.ts +27 -0
  268. package/src/state/exchange-credentials.ts +219 -0
  269. package/src/state/files.ts +129 -0
  270. package/src/state/identities.ts +236 -0
  271. package/src/state/index.ts +29 -0
  272. package/src/state/paths.ts +61 -0
  273. package/src/state/tsconfig.json +6 -0
  274. package/src/telemetry/__tests__/redact.test.ts +3 -3
  275. package/src/telemetry/__tests__/trigger.test.ts +1 -1
  276. package/src/telemetry/settings.ts +1 -1
  277. package/src/telemetry/store.ts +1 -1
  278. package/src/test-utils.ts +1 -1
  279. package/studio/client/dist/assets/elk-api-D0cBetPW.js +1 -0
  280. package/studio/client/dist/assets/elk-worker.min-C9JGDOE-.js +6312 -0
  281. package/studio/client/dist/assets/index-BQnJ5sgd.css +1 -0
  282. package/studio/client/dist/assets/index-ChOr3yP0.css +1 -0
  283. package/studio/client/dist/assets/index-Dspir4w7.js +81 -0
  284. package/studio/client/dist/assets/index-bVD2KJgz.js +8 -0
  285. package/studio/client/dist/index.html +2 -2
  286. package/studio/package.json +10 -1
  287. package/studio/server/introspect/extractor.ts +1 -1
  288. package/studio/server/state/views.test.ts +3 -7
  289. package/studio/server/state/views.ts +23 -30
  290. package/studio/tsconfig.json +23 -0
  291. package/tsconfig.json +1 -1
  292. package/viewer/dist/main.js +65 -39
  293. package/viewer/tsconfig.json +13 -0
  294. package/src/command.ts +0 -42
  295. package/src/commands/__tests__/help-contract.test.ts +0 -164
  296. package/src/kernel/__tests__/client-owned-lookup.test.ts +0 -51
  297. package/src/kernel/__tests__/errors.test.ts +0 -43
  298. package/src/kernel/__tests__/expand.test.ts +0 -123
  299. package/src/kernel/client.ts +0 -214
  300. package/src/kernel/expand.ts +0 -192
  301. package/src/kernel/graph.ts +0 -96
  302. package/src/kernel/index.ts +0 -29
  303. package/src/kernel/options.ts +0 -22
  304. package/src/kernel/types.ts +0 -14
  305. package/src/lib/__tests__/domain-identity.test.ts +0 -60
  306. package/src/lib/__tests__/fs-atomic.test.ts +0 -104
  307. package/src/lib/__tests__/identity.test.ts +0 -79
  308. package/src/lib/__tests__/keys.test.ts +0 -129
  309. package/src/lib/domain-identity.ts +0 -49
  310. package/src/lib/env.ts +0 -49
  311. package/src/lib/fs-atomic.ts +0 -126
  312. package/src/lib/identity.ts +0 -256
  313. package/src/lib/keys.ts +0 -294
  314. package/src/lib/paths.ts +0 -11
  315. package/src/program.ts +0 -213
  316. package/studio/client/dist/assets/index-BaqEuIQJ.js +0 -109
  317. package/studio/client/dist/assets/index-jRdExahh.css +0 -1
@@ -1,23 +1,12 @@
1
- import { describe, expect, mock, test } from 'bun:test'
1
+ import { describe, expect, test } from 'bun:test'
2
2
 
3
- import type { IdentityStore } from '../../lib/identity'
3
+ import type { IdentityStore } from '../../identity/index'
4
4
 
5
5
  import { AuthError } from '../../errors'
6
- import {
7
- ADMIN_INSTANCE,
8
- adminInstanceMethod,
9
- callOwnedInstances,
10
- findOwnedInstance,
11
- type OwnedInstanceInfo,
12
- } from '../../lib/admin-instance'
6
+ import { findOwnedInstance, type OwnedInstanceInfo } from '../../lib/admin-instance'
13
7
  import { assertAlphaCreateIdentity } from '../instance/create'
14
8
 
15
9
  describe('admin-backed instance commands', () => {
16
- test('target the merged Instance class', () => {
17
- expect(ADMIN_INSTANCE).toBe('/:admin.astrale.ai:class.Instance')
18
- expect(adminInstanceMethod('list')).toBe('/:admin.astrale.ai:class.Instance:list')
19
- })
20
-
21
10
  test('findOwnedInstance matches owner inventory by slug or stable node id', () => {
22
11
  const owned = {
23
12
  id: 'instance-node',
@@ -37,28 +26,6 @@ describe('admin-backed instance commands', () => {
37
26
  expect(findOwnedInstance([owned], 'other')).toBeUndefined()
38
27
  })
39
28
 
40
- test('owner-scoped discovery calls listMine with an empty input', async () => {
41
- const owned: OwnedInstanceInfo[] = [
42
- {
43
- id: 'instance-node',
44
- slug: 'demo',
45
- url: 'https://demo.eu.astrale.ai',
46
- state: 'ready',
47
- },
48
- ]
49
- const call = mock(async () => owned)
50
-
51
- await expect(callOwnedInstances({ call })).resolves.toEqual([
52
- {
53
- id: 'instance-node',
54
- slug: 'demo',
55
- url: 'https://demo.eu.astrale.ai',
56
- state: 'ready',
57
- },
58
- ])
59
- expect(call).toHaveBeenCalledWith('/:admin.astrale.ai:class.Instance:listMine', {})
60
- })
61
-
62
29
  test('instance create preflight points fresh installs at WorkOS login', () => {
63
30
  const store: IdentityStore = {
64
31
  default: 'manager',
@@ -1,72 +1,25 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
2
 
3
- import { parseKeyValue, coerceValue } from '../call'
4
-
5
- describe('coerceValue', () => {
6
- test('parses JSON objects', () => {
7
- expect(coerceValue('{"a":1}')).toEqual({ a: 1 })
8
- })
9
-
10
- test('parses JSON arrays', () => {
11
- expect(coerceValue('[1,2,3]')).toEqual([1, 2, 3])
12
- })
13
-
14
- test('falls through on invalid JSON object', () => {
15
- expect(coerceValue('{not-json}')).toBe('{not-json}')
16
- })
17
-
18
- test('parses booleans', () => {
19
- expect(coerceValue('true')).toBe(true)
20
- expect(coerceValue('false')).toBe(false)
21
- })
22
-
23
- test('parses null', () => {
24
- expect(coerceValue('null')).toBe(null)
25
- })
26
-
27
- test('parses numbers', () => {
28
- expect(coerceValue('42')).toBe(42)
29
- expect(coerceValue('3.14')).toBe(3.14)
30
- expect(coerceValue('-1')).toBe(-1)
31
- expect(coerceValue('0')).toBe(0)
32
- })
33
-
34
- test('returns strings for non-special values', () => {
35
- expect(coerceValue('hello')).toBe('hello')
36
- expect(coerceValue('')).toBe('')
37
- expect(coerceValue('some text')).toBe('some text')
38
- })
39
-
40
- test('preserves hex, octal, and exotic numeric strings as strings', () => {
41
- expect(coerceValue('0x1f')).toBe('0x1f')
42
- expect(coerceValue('0o77')).toBe('0o77')
43
- expect(coerceValue('0b101')).toBe('0b101')
44
- expect(coerceValue('1e308')).toBe('1e308')
45
- expect(coerceValue('Infinity')).toBe('Infinity')
46
- })
47
- })
48
-
49
- describe('parseKeyValue', () => {
50
- test('parses simple key=value pairs', () => {
51
- expect(parseKeyValue(['name=test', 'count=5'])).toEqual({
52
- name: 'test',
53
- count: 5,
54
- })
55
- })
56
-
57
- test('handles values with = in them', () => {
58
- expect(parseKeyValue(['query=a=b'])).toEqual({ query: 'a=b' })
59
- })
60
-
61
- test('throws on missing =', () => {
62
- expect(() => parseKeyValue(['noequals'])).toThrow('expected key=value')
63
- })
64
-
65
- test('parses JSON values', () => {
66
- expect(parseKeyValue(['data={"x":1}'])).toEqual({ data: { x: 1 } })
67
- })
68
-
69
- test('handles empty value', () => {
70
- expect(parseKeyValue(['key='])).toEqual({ key: '' })
3
+ import { materializeCallResult } from '../call'
4
+
5
+ describe('call command result lifetime', () => {
6
+ /** @evidence TEST-CLI-CALL-DRAINS-STREAM-IN-CONNECTION-ACTION */
7
+ test('drains a session-backed stream before returning it for presentation', async () => {
8
+ let sessionOpen = true
9
+ async function* stream() {
10
+ for (const value of ['first', 'second']) {
11
+ if (!sessionOpen) throw new Error('session closed before stream consumption')
12
+ yield value
13
+ }
14
+ }
15
+
16
+ const sessionStream = Object.assign(stream(), { async cancel() {} })
17
+ const materialized = await materializeCallResult({ kind: 'stream', stream: sessionStream })
18
+ sessionOpen = false
19
+
20
+ expect(materialized).toEqual({ kind: 'stream', values: ['first', 'second'] })
21
+ expect(Object.isFrozen(materialized)).toBe(true)
22
+ if (materialized.kind !== 'stream') throw new Error('expected a materialized stream')
23
+ expect(Object.isFrozen(materialized.values)).toBe(true)
71
24
  })
72
25
  })
@@ -2,9 +2,6 @@ import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test'
2
2
 
3
3
  import type { OwnedInstanceInfo } from '../../lib/admin-instance'
4
4
 
5
- import { adminDomainMethod } from '../../lib/admin-domain'
6
- import { adminInstanceMethod } from '../../lib/admin-instance'
7
-
8
5
  class ExitError extends Error {
9
6
  constructor(readonly code: string | number | null | undefined) {
10
7
  super(`process.exit(${String(code)})`)
@@ -14,21 +11,23 @@ class ExitError extends Error {
14
11
  const calls: Array<{ path: string; params: unknown }> = []
15
12
  let inventory: OwnedInstanceInfo[] = []
16
13
 
17
- const clientCall = mock(async (path: string, params: unknown): Promise<unknown> => {
14
+ const hostCall = mock(async (call: { target: string; input: unknown }): Promise<unknown> => {
15
+ const path = call.target
16
+ const params = call.input
18
17
  calls.push({ path, params })
19
- if (path === adminInstanceMethod('listMine')) return inventory
20
18
  throw new Error(`Unexpected admin call: ${path}`)
21
19
  })
22
20
 
23
- mock.module('../../kernel', () => ({
21
+ mock.module('../../connection', () => ({
22
+ createPathCall: (path: string, input: unknown) => ({
23
+ target: path,
24
+ input,
25
+ }),
24
26
  runKernelCommand: mock(),
25
- }))
26
-
27
- mock.module('../../kernel/client', () => ({
28
- withAdminKernelClient: async (
27
+ withAdminClientSession: async (
29
28
  _opts: unknown,
30
- run: (ctx: { client: { call: typeof clientCall } }) => Promise<unknown>,
31
- ) => run({ client: { call: clientCall } }),
29
+ run: (ctx: { session: { call: typeof hostCall } }) => Promise<unknown>,
30
+ ) => run({ session: { call: hostCall } }),
32
31
  }))
33
32
 
34
33
  let stderr = ''
@@ -39,7 +38,7 @@ beforeEach(() => {
39
38
  calls.length = 0
40
39
  inventory = []
41
40
  stderr = ''
42
- clientCall.mockClear()
41
+ hostCall.mockClear()
43
42
  originalExit = process.exit
44
43
  originalStderrWrite = process.stderr.write.bind(process.stderr)
45
44
  process.exit = ((code?: string | number | null) => {
@@ -57,20 +56,20 @@ afterEach(() => {
57
56
  })
58
57
 
59
58
  async function runInstall(instance: string): Promise<void> {
60
- const command = (await import('../domain/install')).default
61
- const action = command.action as (
62
- target: string | undefined,
63
- opts: Record<string, unknown>,
64
- ) => Promise<void>
65
- await action('crm.acme.dev', {
66
- instance,
67
- json: true,
68
- noPrompt: true,
69
- })
59
+ const { installViaAdmin } = await import('../domain/install')
60
+ await installViaAdmin(
61
+ 'crm.acme.dev',
62
+ {
63
+ instance,
64
+ json: true,
65
+ noPrompt: true,
66
+ },
67
+ { listInstances: async () => inventory },
68
+ )
70
69
  }
71
70
 
72
71
  describe('admin domain install owner boundary', () => {
73
- test('rejects a foreign target after listMine without attempting an install', async () => {
72
+ test('rejects a foreign target after the V2 graph inventory without attempting an install', async () => {
74
73
  inventory = [
75
74
  {
76
75
  id: 'owned-id',
@@ -86,7 +85,7 @@ describe('admin domain install owner boundary', () => {
86
85
  error: 'INSTANCE_NOT_MANAGED',
87
86
  message: 'Instance "foreign" is not admin-managed (managed: owned).',
88
87
  })
89
- expect(calls).toEqual([{ path: adminInstanceMethod('listMine'), params: {} }])
88
+ expect(calls).toEqual([])
90
89
  })
91
90
 
92
91
  test.each([
@@ -116,7 +115,7 @@ describe('admin domain install owner boundary', () => {
116
115
  expect(payload.error).toBe('INSTANCE_NOT_READY')
117
116
  expect(payload.message).toContain(`Instance "owned" is ${status.state}`)
118
117
  expect(payload.hint).toBe(status.error ?? 'Run: astrale instance status owned')
119
- expect(calls).toEqual([{ path: adminInstanceMethod('listMine'), params: {} }])
120
- expect(calls.some((call) => call.path === adminDomainMethod('install'))).toBe(false)
118
+ expect(calls).toEqual([])
119
+ expect(calls).toEqual([])
121
120
  })
122
121
  })
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
2
 
3
- import { ADMIN_DOMAIN, adminDomainMethod, type DomainInfo } from '../../lib/admin-domain'
3
+ import type { DomainInfo } from '../../lib/admin-domain'
4
+
4
5
  import { byDefaultThenName, domainProjection, type DomainRow } from '../domain/list'
5
6
 
6
7
  const strip = (s: string): string => s.replace(/\[[0-9;]*m/g, '')
@@ -17,11 +18,6 @@ function entry(over: Partial<DomainInfo>): DomainInfo {
17
18
  }
18
19
 
19
20
  describe('domain list — ordering', () => {
20
- test('uses the semantic static method path', () => {
21
- expect(ADMIN_DOMAIN).toBe('/:admin.astrale.ai:class.DomainEntry')
22
- expect(adminDomainMethod('list')).toBe('/:admin.astrale.ai:class.DomainEntry:list')
23
- })
24
-
25
21
  test('install-by-default sorts first, then alphabetical by origin', () => {
26
22
  const rows = [
27
23
  entry({ origin: 'zeta.astrale.ai' }),
@@ -1,117 +1,69 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
2
 
3
- import { buildEventsParams, normalizePage, parseServiceName, parseTimeFlag } from '../logs'
3
+ import { acceptJournalPage, buildJournalInput } from '../logs'
4
4
 
5
- describe('parseTimeFlag', () => {
6
- test('passes through epoch-ms', () => {
7
- expect(parseTimeFlag('--since', '1718841600000')).toBe(1718841600000)
8
- expect(parseTimeFlag('--since', '0')).toBe(0)
9
- expect(parseTimeFlag('--since', '-5')).toBe(-5)
10
- })
11
-
12
- test('parses ISO-8601 to epoch ms', () => {
13
- expect(parseTimeFlag('--since', '2026-06-20T00:00:00Z')).toBe(
14
- Date.parse('2026-06-20T00:00:00Z'),
15
- )
16
- })
17
-
18
- test('throws on garbage', () => {
19
- expect(() => parseTimeFlag('--since', 'not-a-time')).toThrow('--since')
20
- })
21
- })
22
-
23
- describe('buildEventsParams', () => {
24
- test('defaults limit when unset, no other fields', () => {
25
- expect(buildEventsParams({})).toEqual({ limit: 200 })
26
- })
27
-
28
- test('maps typed flags into a JournalFilter', () => {
5
+ describe('buildJournalInput', () => {
6
+ /** @evidence TEST-CLI-LOGS-MAPS-EXACT-JOURNAL-INPUT */
7
+ test('maps flags to the current public journal syscall contract', () => {
29
8
  expect(
30
- buildEventsParams({
31
- topic: 'op:*:failed',
32
- principal: 'id_abc',
33
- since: '1000',
34
- until: '2000',
9
+ buildJournalInput({
10
+ topic: 'op:function.failed',
11
+ topicPrefix: 'security.',
12
+ principal: 'caller',
13
+ since: '2026-08-10T00:00:00Z',
14
+ until: '2026-08-11T00:00:00Z',
15
+ cursor: 'opaque-next',
35
16
  limit: '50',
36
- cursor: '7',
37
17
  }),
38
18
  ).toEqual({
39
- topic: 'op:*:failed',
40
- principal: 'id_abc' as never,
41
- since: 1000,
42
- until: 2000,
19
+ topics: { exact: ['op:function.failed'], prefixes: ['security.'] },
20
+ principal: 'caller',
21
+ since: '2026-08-10T00:00:00Z',
22
+ until: '2026-08-11T00:00:00Z',
23
+ cursor: 'opaque-next',
43
24
  limit: 50,
44
- cursor: 7,
45
25
  })
46
26
  })
47
27
 
48
- test('rejects a non-positive limit', () => {
49
- expect(() => buildEventsParams({ limit: '0' })).toThrow('--limit')
50
- expect(() => buildEventsParams({ limit: '-3' })).toThrow('--limit')
51
- expect(() => buildEventsParams({ limit: 'x' })).toThrow('--limit')
52
- })
53
-
54
- test('rejects a non-positive cursor', () => {
55
- expect(() => buildEventsParams({ cursor: '0' })).toThrow('--cursor')
56
- })
57
-
58
- test('omits empty topic/principal', () => {
59
- expect(buildEventsParams({ topic: '', principal: '' })).toEqual({ limit: 200 })
28
+ test('defaults only the finite limit and rejects invalid values', () => {
29
+ expect(buildJournalInput({})).toEqual({ limit: 200 })
30
+ expect(() => buildJournalInput({ limit: '0' })).toThrow('--limit')
31
+ expect(() => buildJournalInput({ limit: 'all' })).toThrow('--limit')
60
32
  })
61
33
  })
62
34
 
63
- describe('normalizePage', () => {
64
- const entry = (seq: number) => ({
65
- seq,
66
- event: {
67
- id: `e${seq}`,
68
- topic: 'op:x',
69
- payload: {},
70
- metadata: { traceId: 't', timestamp: seq, principal: 'p', root: 'r' },
71
- },
72
- })
73
-
74
- test('wraps a bare entry array and derives nextCursor from max seq', () => {
75
- const page = normalizePage([entry(3), entry(7), entry(5)])
76
- expect(page.entries).toHaveLength(3)
77
- expect(page.nextCursor).toBe(7)
78
- })
79
-
80
- test('empty array → empty entries, null cursor', () => {
81
- expect(normalizePage([])).toEqual({ entries: [], nextCursor: null })
82
- })
83
-
84
- test('accepts a paged { entries, nextCursor } shape', () => {
85
- const page = normalizePage({ entries: [entry(1)], nextCursor: 42 })
86
- expect(page.entries).toHaveLength(1)
87
- expect(page.nextCursor).toBe(42)
88
- })
89
-
90
- test('paged shape without nextCursor falls back to max seq', () => {
91
- expect(normalizePage({ entries: [entry(9)] }).nextCursor).toBe(9)
92
- })
93
-
94
- test('non-array, non-page input → empty', () => {
95
- expect(normalizePage(null)).toEqual({ entries: [], nextCursor: null })
96
- expect(normalizePage(undefined)).toEqual({ entries: [], nextCursor: null })
97
- expect(normalizePage('nope')).toEqual({ entries: [], nextCursor: null })
98
- })
99
- })
100
-
101
- describe('parseServiceName', () => {
102
- test('bare name is returned as-is', () => {
103
- expect(parseServiceName('my-notes')).toBe('my-notes')
104
- })
105
-
106
- test('host → first label', () => {
107
- expect(parseServiceName('my-notes.svc.eu.astrale.ai')).toBe('my-notes')
108
- })
35
+ describe('acceptJournalPage', () => {
36
+ /** @evidence TEST-CLI-LOGS-ADMITS-OPAQUE-CURSOR-PAGE */
37
+ test('retains current records and an opaque continuation cursor', () => {
38
+ const page = acceptJournalPage({
39
+ records: [
40
+ {
41
+ sequence: 7,
42
+ timestamp: '2026-08-11T12:00:00.000Z',
43
+ topic: 'op:function.completed',
44
+ payload: { durationMs: 4 },
45
+ principal: 'caller',
46
+ },
47
+ ],
48
+ cursor: 'next-page',
49
+ })
109
50
 
110
- test('full URL → hostname first label', () => {
111
- expect(parseServiceName('https://my-notes.example.dev/path')).toBe('my-notes')
51
+ expect(page).toEqual({
52
+ records: [
53
+ {
54
+ sequence: 7,
55
+ timestamp: '2026-08-11T12:00:00.000Z',
56
+ topic: 'op:function.completed',
57
+ payload: { durationMs: 4 },
58
+ principal: 'caller',
59
+ },
60
+ ],
61
+ cursor: 'next-page',
62
+ })
112
63
  })
113
64
 
114
- test('trims whitespace', () => {
115
- expect(parseServiceName(' my-notes ')).toBe('my-notes')
65
+ test('rejects malformed record and cursor fields instead of formatting them loosely', () => {
66
+ expect(() => acceptJournalPage({ records: [{}] })).toThrow('record 0')
67
+ expect(() => acceptJournalPage({ records: [], cursor: 7 })).toThrow('cursor')
116
68
  })
117
69
  })
@@ -1,25 +1,29 @@
1
+ import type { Node } from '@astrale-os/sdk/graph/node'
2
+
3
+ import { ClassPath } from '@astrale-os/sdk/graph/class'
4
+ import { NodeId } from '@astrale-os/sdk/graph/node'
5
+ import { normalizeProperties } from '@astrale-os/sdk/graph/properties'
1
6
  import { describe, expect, test } from 'bun:test'
2
7
 
3
- import { basename, classNameOf } from '../ls'
8
+ import { displayName, listProjection } from '../ls'
4
9
 
5
- // Regression: the formatter used to read `item.slug` (never returned by the
6
- // kernel), blanking the name column and breaking `-q`/`-R`. The real fields are
7
- // `path` (absolute) and `class` (serialized ClassPath).
8
- describe('ls — display projection (slug-bug regression)', () => {
9
- test('basename derives the display name from the absolute path', () => {
10
- expect(basename('/example.astrale.ai')).toBe('example.astrale.ai')
11
- expect(basename('/kernel.astrale.ai')).toBe('kernel.astrale.ai')
12
- expect(basename('/')).toBe('/')
13
- expect(basename(undefined)).toBe('')
14
- })
10
+ describe('ls display projection', () => {
11
+ const node = {
12
+ id: NodeId('note-1'),
13
+ class: ClassPath.parse('/:notes.example.dev:class.Note'),
14
+ props: normalizeProperties({ 'notes.example.dev:class.Note.property.title': 'Hello' }),
15
+ } satisfies Node
15
16
 
16
- test('classNameOf parses the kind from the serialized class path', () => {
17
- expect(classNameOf({ class: '/:kernel.astrale.ai:class.Domain' })).toBe('Domain')
18
- expect(classNameOf({ class: '/:kernel.astrale.ai:class.Folder' })).toBe('Folder')
17
+ /** @evidence TEST-CLI-LS-PROJECTS-CANONICAL-NODES */
18
+ test('uses canonical properties for display and @id for pipeable output', () => {
19
+ expect(displayName(node)).toBe('Hello')
20
+ expect(listProjection([node])).toMatchObject({
21
+ rows: [{ name: 'Hello', class: 'Note', id: 'note-1' }],
22
+ paths: ['@note-1'],
23
+ })
19
24
  })
20
25
 
21
- test('classNameOf falls back to the most specific label, then ?', () => {
22
- expect(classNameOf({ __labels: ['Node', 'Domain', 'Container'] })).toBe('Container')
23
- expect(classNameOf({})).toBe('?')
26
+ test('falls back to the canonical Node ID when no display property exists', () => {
27
+ expect(displayName({ ...node, props: normalizeProperties({}) })).toBe('@note-1')
24
28
  })
25
29
  })