@astrale-os/cli 0.8.1-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 (310) hide show
  1. package/dist/astrale.js +62959 -41238
  2. package/dist/public/connect-core.js +55030 -0
  3. package/dist/public/keys/index.js +45631 -0
  4. package/dist/public/paths/index.js +43990 -0
  5. package/dist/types/admin/instance/model.d.ts +40 -0
  6. package/dist/types/connect-core.d.ts +22 -0
  7. package/dist/types/connection/auth.d.ts +33 -0
  8. package/dist/types/errors.d.ts +23 -0
  9. package/dist/types/identity/registry.d.ts +25 -0
  10. package/dist/types/keys/algorithm.d.ts +6 -0
  11. package/dist/types/keys/credential.d.ts +21 -0
  12. package/dist/types/keys/index.d.ts +4 -0
  13. package/dist/types/keys/pair.d.ts +34 -0
  14. package/dist/types/lib/admin-target.d.ts +74 -0
  15. package/dist/types/lib/ca-fetch.d.ts +2 -0
  16. package/dist/types/lib/config.d.ts +71 -0
  17. package/dist/types/lib/format.d.ts +1 -0
  18. package/dist/types/lib/idp-session.d.ts +33 -0
  19. package/dist/types/lib/idp.d.ts +397 -0
  20. package/dist/types/lib/instance-target.d.ts +36 -0
  21. package/dist/types/lib/instance.d.ts +186 -0
  22. package/dist/types/lib/log.d.ts +30 -0
  23. package/dist/types/lib/login-flow.d.ts +49 -0
  24. package/dist/types/lib/meta.d.ts +36 -0
  25. package/dist/types/lib/output.d.ts +83 -0
  26. package/dist/types/lib/table.d.ts +18 -0
  27. package/dist/types/lib/validation.d.ts +11 -0
  28. package/dist/types/paths/index.d.ts +2 -0
  29. package/dist/types/state/exchange-credentials.d.ts +25 -0
  30. package/dist/types/state/files.d.ts +9 -0
  31. package/dist/types/state/identities.d.ts +42 -0
  32. package/dist/types/state/index.d.ts +8 -0
  33. package/dist/types/state/paths.d.ts +32 -0
  34. package/package.json +22 -7
  35. package/src/admin/.spec/api.d.ts +3 -0
  36. package/src/admin/.spec/architecture.md +15 -0
  37. package/src/admin/.spec/icon.svg +3 -0
  38. package/src/admin/.spec/layout.ts +6 -0
  39. package/src/admin/catalog/.spec/api.d.ts +65 -0
  40. package/src/admin/catalog/.spec/architecture.md +6 -0
  41. package/src/admin/catalog/.spec/icon.svg +3 -0
  42. package/src/admin/catalog/.spec/layout.ts +6 -0
  43. package/src/admin/catalog/__tests__/client.test.ts +171 -0
  44. package/src/admin/catalog/client.ts +214 -0
  45. package/src/admin/catalog/index.ts +13 -0
  46. package/src/admin/catalog/model.ts +42 -0
  47. package/src/admin/catalog/tsconfig.json +8 -0
  48. package/src/admin/graph/.spec/api.d.ts +28 -0
  49. package/src/admin/graph/.spec/architecture.md +5 -0
  50. package/src/admin/graph/.spec/icon.svg +3 -0
  51. package/src/admin/graph/.spec/layout.ts +3 -0
  52. package/src/admin/graph/index.ts +6 -0
  53. package/src/admin/graph/nodes.ts +56 -0
  54. package/src/admin/index.ts +2 -0
  55. package/src/admin/instance/.spec/api.d.ts +66 -0
  56. package/src/admin/instance/.spec/architecture.md +10 -0
  57. package/src/admin/instance/.spec/icon.svg +3 -0
  58. package/src/admin/instance/.spec/layout.ts +6 -0
  59. package/src/admin/instance/__tests__/client.test.ts +289 -0
  60. package/src/admin/instance/client.ts +298 -0
  61. package/src/admin/instance/index.ts +16 -0
  62. package/src/admin/instance/model.ts +65 -0
  63. package/src/admin/instance/tsconfig.json +8 -0
  64. package/src/commands/__tests__/admin-instance.test.ts +3 -36
  65. package/src/commands/__tests__/call.test.ts +21 -68
  66. package/src/commands/__tests__/domain-install-owned.test.ts +26 -27
  67. package/src/commands/__tests__/domain-list.test.ts +2 -6
  68. package/src/commands/__tests__/logs.test.ts +50 -98
  69. package/src/commands/__tests__/ls.test.ts +21 -17
  70. package/src/commands/__tests__/read-commands.test.ts +171 -101
  71. package/src/commands/__tests__/view.test.ts +100 -51
  72. package/src/commands/admin/status.ts +3 -3
  73. package/src/commands/admin/use.ts +29 -6
  74. package/src/commands/auth/login.ts +1 -1
  75. package/src/commands/auth/logout.ts +4 -2
  76. package/src/commands/auth/status.ts +2 -2
  77. package/src/commands/auth/token.ts +2 -2
  78. package/src/commands/browser.ts +1 -1
  79. package/src/commands/call.ts +55 -91
  80. package/src/commands/describe.ts +52 -147
  81. package/src/commands/domain/install.ts +81 -50
  82. package/src/commands/domain/list.ts +4 -8
  83. package/src/commands/domain/publish.ts +9 -31
  84. package/src/commands/get.ts +32 -65
  85. package/src/commands/identity/__tests__/register.test.ts +93 -0
  86. package/src/commands/identity/create.ts +2 -2
  87. package/src/commands/identity/delete.ts +2 -2
  88. package/src/commands/identity/export.ts +7 -33
  89. package/src/commands/identity/import.ts +21 -70
  90. package/src/commands/identity/list.ts +2 -2
  91. package/src/commands/identity/register.ts +148 -117
  92. package/src/commands/identity/sync.ts +2 -2
  93. package/src/commands/identity/unsync.ts +2 -2
  94. package/src/commands/identity/use.ts +2 -2
  95. package/src/commands/identity/whoami.ts +2 -2
  96. package/src/commands/idp/add.ts +1 -1
  97. package/src/commands/idp/list.ts +1 -1
  98. package/src/commands/idp/refresh.ts +1 -1
  99. package/src/commands/idp/remove.ts +2 -2
  100. package/src/commands/idp/show.ts +1 -1
  101. package/src/commands/instance/active.ts +2 -2
  102. package/src/commands/instance/bookmark.ts +8 -2
  103. package/src/commands/instance/create.ts +1 -1
  104. package/src/commands/instance/delete.ts +4 -10
  105. package/src/commands/instance/forget.ts +1 -1
  106. package/src/commands/instance/list.ts +3 -3
  107. package/src/commands/instance/status.ts +5 -7
  108. package/src/commands/instance/use.ts +3 -3
  109. package/src/commands/logs.ts +152 -279
  110. package/src/commands/ls.ts +78 -204
  111. package/src/commands/mutate.ts +70 -139
  112. package/src/commands/query.ts +100 -278
  113. package/src/commands/session/analyze.ts +1 -1
  114. package/src/commands/session/list.ts +1 -1
  115. package/src/commands/setup.ts +1 -1
  116. package/src/commands/status.ts +1 -1
  117. package/src/commands/studio.ts +1 -1
  118. package/src/commands/token.ts +26 -21
  119. package/src/commands/update.ts +1 -1
  120. package/src/commands/use.ts +2 -3
  121. package/src/commands/view-serve.ts +1 -1
  122. package/src/commands/view.ts +67 -109
  123. package/src/connect-core.test.ts +38 -0
  124. package/src/connect-core.ts +42 -0
  125. package/src/connection/.spec/api.d.ts +111 -0
  126. package/src/connection/.spec/architecture.md +41 -0
  127. package/src/connection/.spec/capabilities/connection.ts +13 -0
  128. package/src/connection/.spec/examples/call.ts +7 -0
  129. package/src/connection/.spec/flows/command.ts +20 -0
  130. package/src/connection/.spec/flows/session.ts +61 -0
  131. package/src/connection/.spec/laws/connection.ts +181 -0
  132. package/src/connection/.spec/layout.ts +19 -0
  133. package/src/{kernel → connection}/__tests__/auth.test.ts +26 -2
  134. package/src/connection/__tests__/ca-fetch.test.ts +53 -0
  135. package/src/connection/__tests__/call.test.ts +17 -0
  136. package/src/connection/__tests__/credential.test.ts +109 -0
  137. package/src/connection/__tests__/errors.test.ts +96 -0
  138. package/src/connection/__tests__/exchange.test.ts +224 -0
  139. package/src/connection/__tests__/fixtures/localhost-cert.pem +19 -0
  140. package/src/connection/__tests__/fixtures/localhost-key.base64 +1 -0
  141. package/src/connection/__tests__/managed.test.ts +39 -0
  142. package/src/connection/__tests__/self.test.ts +51 -0
  143. package/src/connection/__tests__/session.test.ts +140 -0
  144. package/src/connection/__tests__/target.test.ts +93 -0
  145. package/src/{kernel → connection}/auth.ts +8 -8
  146. package/src/connection/call.ts +7 -0
  147. package/src/{kernel/run.ts → connection/command.ts} +22 -13
  148. package/src/connection/credential.ts +114 -0
  149. package/src/{kernel → connection}/errors.ts +32 -9
  150. package/src/connection/exchange.ts +274 -0
  151. package/src/connection/index.ts +8 -0
  152. package/src/connection/self.ts +120 -0
  153. package/src/connection/session.ts +176 -0
  154. package/src/connection/target.ts +111 -0
  155. package/src/connection/tsconfig.json +6 -0
  156. package/src/graph/.spec/api.d.ts +34 -0
  157. package/src/graph/.spec/laws/documents.ts +24 -0
  158. package/src/graph/.spec/laws/projection.ts +13 -0
  159. package/src/graph/.spec/layout.ts +6 -0
  160. package/src/graph/__tests__/mutation.test.ts +43 -0
  161. package/src/graph/__tests__/projection.test.ts +22 -0
  162. package/src/graph/__tests__/query.test.ts +154 -0
  163. package/src/graph/index.ts +3 -0
  164. package/src/graph/mutation.ts +18 -0
  165. package/src/graph/projection.ts +21 -0
  166. package/src/graph/query.ts +108 -0
  167. package/src/graph/tsconfig.json +6 -0
  168. package/src/identity/.history/ard/bootstrap-root-transfer.md +57 -0
  169. package/src/identity/.history/ard/domain-mediated-registration.md +36 -0
  170. package/src/identity/.spec/api.d.ts +124 -0
  171. package/src/identity/.spec/architecture.md +33 -0
  172. package/src/identity/.spec/capabilities/identity.ts +7 -0
  173. package/src/identity/.spec/flows/bootstrap-root.ts +79 -0
  174. package/src/identity/.spec/flows/domain-mediated-registration.ts +51 -0
  175. package/src/identity/.spec/flows/import.ts +29 -0
  176. package/src/identity/.spec/laws/identity.ts +56 -0
  177. package/src/identity/.spec/layout.ts +13 -0
  178. package/src/identity/.spec/schemas/identity-export-v1.schema.json +25 -0
  179. package/src/identity/__tests__/fixtures/registry-journey.ts +53 -0
  180. package/src/identity/__tests__/registration.test.ts +64 -0
  181. package/src/identity/__tests__/registry.test.ts +70 -0
  182. package/src/identity/__tests__/transfer.test.ts +198 -0
  183. package/src/identity/index.ts +23 -0
  184. package/src/identity/registration.ts +72 -0
  185. package/src/identity/registry.ts +172 -0
  186. package/src/identity/transfer.ts +187 -0
  187. package/src/identity/tsconfig.json +6 -0
  188. package/src/index.ts +1 -0
  189. package/src/keys/.spec/api.d.ts +74 -0
  190. package/src/keys/.spec/architecture.md +13 -0
  191. package/src/keys/.spec/laws/keys.ts +43 -0
  192. package/src/keys/.spec/layout.ts +6 -0
  193. package/src/keys/__tests__/algorithm.test.ts +39 -0
  194. package/src/keys/__tests__/keys.test.ts +168 -0
  195. package/src/keys/algorithm.ts +33 -0
  196. package/src/keys/credential.ts +112 -0
  197. package/src/keys/index.ts +14 -0
  198. package/src/keys/pair.ts +219 -0
  199. package/src/keys/tsconfig.json +6 -0
  200. package/src/lib/__tests__/admin-target.test.ts +67 -7
  201. package/src/lib/__tests__/binary.test.ts +2 -3
  202. package/src/lib/__tests__/command-dx.test.ts +2 -2
  203. package/src/lib/__tests__/config.test.ts +3 -2
  204. package/src/lib/__tests__/idp.test.ts +4 -6
  205. package/src/lib/__tests__/instance-target.test.ts +7 -4
  206. package/src/lib/__tests__/local-status.test.ts +1 -1
  207. package/src/lib/__tests__/log.test.ts +31 -0
  208. package/src/lib/__tests__/meta.test.ts +55 -0
  209. package/src/lib/__tests__/sdk-deps.test.ts +1 -1
  210. package/src/lib/__tests__/self.test.ts +8 -230
  211. package/src/lib/__tests__/use-target.test.ts +1 -1
  212. package/src/lib/__tests__/view-open-intent.test.ts +78 -104
  213. package/src/lib/__tests__/view-session.test.ts +99 -0
  214. package/src/lib/admin-domain.ts +39 -25
  215. package/src/lib/admin-instance.ts +38 -43
  216. package/src/lib/admin-target.ts +67 -13
  217. package/src/lib/binary.ts +13 -34
  218. package/src/lib/browser.ts +1 -1
  219. package/src/{kernel → lib}/ca-fetch.ts +35 -27
  220. package/src/lib/config.ts +1 -1
  221. package/src/lib/idp-session.ts +1 -1
  222. package/src/lib/idp.ts +3 -8
  223. package/src/lib/instance-target.ts +12 -8
  224. package/src/lib/instance.ts +34 -2
  225. package/src/lib/local-status.ts +2 -2
  226. package/src/lib/log.ts +3 -1
  227. package/src/lib/login-flow.ts +32 -5
  228. package/src/lib/meta.ts +15 -11
  229. package/src/lib/provision-instance.ts +4 -12
  230. package/src/lib/self.ts +5 -147
  231. package/src/lib/update.ts +1 -1
  232. package/src/lib/use-target.ts +1 -1
  233. package/src/lib/validation.ts +2 -2
  234. package/src/lib/view/open-intent.ts +7 -37
  235. package/src/lib/view/port-allocation.ts +1 -1
  236. package/src/lib/view/resolve.ts +52 -45
  237. package/src/lib/view/server.ts +16 -33
  238. package/src/lib/view/session.ts +51 -22
  239. package/src/paths/.spec/api.d.ts +13 -0
  240. package/src/paths/.spec/architecture.md +5 -0
  241. package/src/paths/.spec/layout.ts +3 -0
  242. package/src/paths/index.ts +13 -0
  243. package/src/program/.spec/api.d.ts +42 -0
  244. package/src/program/.spec/architecture.md +18 -0
  245. package/src/program/.spec/laws/program.ts +29 -0
  246. package/src/program/.spec/layout.ts +14 -0
  247. package/src/program/__tests__/program.test.ts +359 -0
  248. package/src/program/build.ts +190 -0
  249. package/src/program/command.ts +42 -0
  250. package/src/program/index.ts +2 -0
  251. package/src/program/options.ts +40 -0
  252. package/src/{registry.ts → program/registry.ts} +5 -1
  253. package/src/program/tsconfig.json +6 -0
  254. package/src/setup/steps/admin.ts +18 -3
  255. package/src/setup/steps/instance.ts +1 -1
  256. package/src/state/.spec/api.d.ts +133 -0
  257. package/src/state/.spec/architecture.md +31 -0
  258. package/src/state/.spec/capabilities/state.ts +7 -0
  259. package/src/state/.spec/flows/identity-update.ts +22 -0
  260. package/src/state/.spec/laws/state.ts +73 -0
  261. package/src/state/.spec/layout.ts +14 -0
  262. package/src/state/__tests__/exchange-credentials.test.ts +188 -0
  263. package/src/state/__tests__/files.test.ts +113 -0
  264. package/src/state/__tests__/fixtures/identity-transition.ts +37 -0
  265. package/src/state/__tests__/identities.test.ts +142 -0
  266. package/src/state/__tests__/paths.test.ts +27 -0
  267. package/src/state/exchange-credentials.ts +219 -0
  268. package/src/state/files.ts +129 -0
  269. package/src/state/identities.ts +236 -0
  270. package/src/state/index.ts +29 -0
  271. package/src/state/paths.ts +61 -0
  272. package/src/state/tsconfig.json +6 -0
  273. package/src/telemetry/__tests__/redact.test.ts +3 -3
  274. package/src/telemetry/__tests__/trigger.test.ts +1 -1
  275. package/src/telemetry/settings.ts +1 -1
  276. package/src/telemetry/store.ts +1 -1
  277. package/src/test-utils.ts +1 -1
  278. package/studio/client/dist/assets/{elk-api-DVw5WDTH.js → elk-api-D0cBetPW.js} +1 -1
  279. package/studio/client/dist/assets/index-Dspir4w7.js +81 -0
  280. package/studio/client/dist/assets/{index-Dl709Ra1.js → index-bVD2KJgz.js} +4 -4
  281. package/studio/client/dist/index.html +1 -1
  282. package/studio/package.json +10 -1
  283. package/studio/server/introspect/extractor.ts +1 -1
  284. package/studio/server/state/views.test.ts +3 -7
  285. package/studio/server/state/views.ts +23 -30
  286. package/tsconfig.json +1 -1
  287. package/viewer/dist/main.js +65 -39
  288. package/src/command.ts +0 -42
  289. package/src/commands/__tests__/help-contract.test.ts +0 -164
  290. package/src/kernel/__tests__/client-owned-lookup.test.ts +0 -51
  291. package/src/kernel/__tests__/errors.test.ts +0 -43
  292. package/src/kernel/__tests__/expand.test.ts +0 -123
  293. package/src/kernel/client.ts +0 -214
  294. package/src/kernel/expand.ts +0 -192
  295. package/src/kernel/graph.ts +0 -96
  296. package/src/kernel/index.ts +0 -29
  297. package/src/kernel/options.ts +0 -22
  298. package/src/kernel/types.ts +0 -14
  299. package/src/lib/__tests__/domain-identity.test.ts +0 -60
  300. package/src/lib/__tests__/fs-atomic.test.ts +0 -104
  301. package/src/lib/__tests__/identity.test.ts +0 -79
  302. package/src/lib/__tests__/keys.test.ts +0 -129
  303. package/src/lib/domain-identity.ts +0 -49
  304. package/src/lib/env.ts +0 -49
  305. package/src/lib/fs-atomic.ts +0 -126
  306. package/src/lib/identity.ts +0 -256
  307. package/src/lib/keys.ts +0 -294
  308. package/src/lib/paths.ts +0 -11
  309. package/src/program.ts +0 -213
  310. package/studio/client/dist/assets/index-CTbkDp3z.js +0 -81
package/src/command.ts DELETED
@@ -1,42 +0,0 @@
1
- export type CommandArgument = {
2
- name: string
3
- description: string
4
- required?: boolean
5
- }
6
-
7
- export type CommandOption = {
8
- flags: string
9
- description: string
10
- default?: string
11
- choices?: string[]
12
- }
13
-
14
- export type CommandDefinition = {
15
- name: string
16
- description: string
17
- /** One-line summary shown in the parent's command list (Commander `.summary()`). */
18
- summary?: string
19
- /** Hide from help listings (internal entries, e.g. `__view-serve`). */
20
- hidden?: boolean
21
- aliases?: string[]
22
- arguments?: CommandArgument[]
23
- options?: CommandOption[]
24
- /**
25
- * Free-form prose appended to `<cmd> --help` (Commander `addHelpText('after')`).
26
- * Use for command-local behavior the flag list cannot express + canonical
27
- * examples. Pre-wrap lines to ~78 cols — Commander prints it verbatim.
28
- */
29
- afterHelpText?: string
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Commander passes typed args at runtime
31
- action: (...args: any[]) => Promise<void>
32
- }
33
-
34
- export type CommandGroup = {
35
- name: string
36
- description: string
37
- /** One-line summary shown in the program's command list (Commander `.summary()`). */
38
- summary?: string
39
- commands: CommandDefinition[]
40
- /** Nested subgroups (one extra level of `astrale foo bar baz` nesting). */
41
- subgroups?: CommandGroup[]
42
- }
@@ -1,164 +0,0 @@
1
- import type { Command } from 'commander'
2
-
3
- import { describe, expect, test } from 'bun:test'
4
- import { existsSync, readFileSync } from 'node:fs'
5
- import { join } from 'node:path'
6
-
7
- import { buildProgram } from '../../program'
8
-
9
- // Help output is the public CLI contract: version, spec anchors, and skill mirror stay in sync.
10
-
11
- const cliRoot = join(import.meta.dir, '../../..')
12
-
13
- /** Every command in the tree (root + groups + nested subgroups), depth-first. */
14
- function allCommands(cmd: Command): Command[] {
15
- return [cmd, ...cmd.commands.flatMap(allCommands)]
16
- }
17
-
18
- describe('help contract — version is single-sourced', () => {
19
- test('program version === package.json === release-please manifest', async () => {
20
- const program = await buildProgram()
21
- const pkg = JSON.parse(readFileSync(join(cliRoot, 'package.json'), 'utf8')) as {
22
- version: string
23
- }
24
- const manifest = JSON.parse(
25
- readFileSync(join(cliRoot, '.release-please-manifest.json'), 'utf8'),
26
- ) as Record<string, string>
27
-
28
- // Program version must follow both package.json and release-please metadata.
29
- expect(program.version()).toBe(pkg.version)
30
- expect(manifest['.']).toBe(pkg.version)
31
- })
32
- })
33
-
34
- describe('help contract — no internal SPEC anchors leak to users', () => {
35
- test('no rendered --help text contains a § section anchor', async () => {
36
- const program = await buildProgram()
37
- const offenders = allCommands(program)
38
- .filter((c) => c.helpInformation().includes('§'))
39
- .map((c) => c.name() || '<root>')
40
-
41
- expect(offenders).toEqual([])
42
- })
43
- })
44
-
45
- describe('help contract — IdP/auth surface is registered', () => {
46
- test('idp group and auth commands are visible in --help tree', async () => {
47
- const program = await buildProgram()
48
- const names = allCommands(program).map((command) => command.name())
49
-
50
- expect(names).toContain('idp')
51
- expect(names).toContain('add')
52
- expect(names).toContain('login')
53
- expect(names).toContain('token')
54
- expect(names).toContain('update')
55
- expect(program.helpInformation()).toContain('idp')
56
- expect(program.helpInformation()).toContain('update')
57
- })
58
- })
59
-
60
- describe('help contract — admin target surface is registered', () => {
61
- test('admin group and admin-target flags are visible', async () => {
62
- const program = await buildProgram()
63
- const names = allCommands(program).map((command) => command.name())
64
- const instanceCreate = allCommands(program).find((command) => command.name() === 'create')
65
-
66
- expect(names).toContain('admin')
67
- expect(names).toContain('status')
68
- expect(names).toContain('use')
69
- expect(program.helpInformation()).toContain('admin')
70
- expect(instanceCreate?.helpInformation()).toContain('--admin <name>')
71
- expect(instanceCreate?.helpInformation()).toContain('--admin-url <url>')
72
- })
73
-
74
- test('instance create is the alphaCreate flow, not legacy Instance.init DX', async () => {
75
- const program = await buildProgram()
76
- const instanceCreate = allCommands(program).find((command) => command.name() === 'create')
77
- const help = instanceCreate?.helpInformation() ?? ''
78
-
79
- expect(help).toContain('Instance.alphaCreate')
80
- expect(help).not.toContain('--no-use')
81
- expect(help).not.toContain('Instance.init requires --host-id')
82
- })
83
- })
84
-
85
- describe('help contract — connect-only command surface', () => {
86
- test('runtime management commands are not registered', async () => {
87
- const program = await buildProgram()
88
- const names = allCommands(program).map((command) => command.name())
89
-
90
- // `logs` and `domain` have managed/admin meanings, so only retired local-runtime verbs are absent.
91
- for (const removed of [
92
- 'init',
93
- 'start',
94
- 'stop',
95
- 'restart',
96
- 'reset',
97
- 'bootstrap',
98
- 'tunnel',
99
- 'graph',
100
- 'server',
101
- 'env',
102
- ]) {
103
- expect(names).not.toContain(removed)
104
- }
105
- })
106
- })
107
-
108
- describe('help contract — read command split', () => {
109
- test('get is point-read only and query owns structured read flags', async () => {
110
- const program = await buildProgram()
111
- const get = allCommands(program).find((command) => command.name() === 'get')
112
- const query = allCommands(program).find((command) => command.name() === 'query')
113
- const getHelp = get?.helpInformation() ?? ''
114
- const queryHelp = query?.helpInformation() ?? ''
115
-
116
- expect(getHelp).toContain('Usage: astrale get [options] <path>')
117
- expect(getHelp).toContain('-l, --long')
118
- expect(getHelp).not.toContain('--depth')
119
- expect(getHelp).not.toContain('--children')
120
- expect(getHelp).not.toContain('--edges')
121
- expect(getHelp).not.toContain('--graph')
122
-
123
- expect(queryHelp).toContain('Usage: astrale query [options] [paths...]')
124
- expect(queryHelp).toContain('--depth <n>')
125
- expect(queryHelp).toContain('--children <json>')
126
- expect(queryHelp).toContain('--edges <json>')
127
- expect(queryHelp).toContain('--ast <json>')
128
- expect(queryHelp).toContain('--cypher <query>')
129
- })
130
- })
131
-
132
- describe('help contract — payload sources', () => {
133
- test('call excludes --file while mutate supports it', async () => {
134
- const program = await buildProgram()
135
- const call = allCommands(program).find((command) => command.name() === 'call')
136
- const mutate = allCommands(program).find((command) => command.name() === 'mutate')
137
- const callHelp = call?.helpInformation() ?? ''
138
- const mutateHelp = mutate?.helpInformation() ?? ''
139
-
140
- expect(callHelp).toContain('--data <json>')
141
- expect(callHelp).not.toContain('--file <path>')
142
- expect(mutateHelp).toContain('--data <json>')
143
- expect(mutateHelp).toContain('--file <path>')
144
- })
145
- })
146
-
147
- describe('help contract — skill is single-source, not duplicated', () => {
148
- const canonical = join(cliRoot, 'skills/astrale-cli/SKILL.md')
149
- // Workspace mirror lives in the superrepo, outside this submodule. Absent
150
- // when the CLI repo is tested standalone — only assert parity when present.
151
- const mirror = join(cliRoot, '../.agents/skills/astrale-cli/SKILL.md')
152
-
153
- test('canonical skill file exists and is non-empty', () => {
154
- expect(existsSync(canonical)).toBe(true)
155
- expect(readFileSync(canonical, 'utf8').length).toBeGreaterThan(0)
156
- })
157
-
158
- test.skipIf(!existsSync(mirror))(
159
- 'workspace mirror is byte-identical to the canonical skill',
160
- () => {
161
- expect(readFileSync(mirror, 'utf8')).toBe(readFileSync(canonical, 'utf8'))
162
- },
163
- )
164
- })
@@ -1,51 +0,0 @@
1
- import { describe, expect, mock, test } from 'bun:test'
2
-
3
- import type { OwnedInstanceInfo } from '../../lib/admin-instance'
4
- import type { AdminTargetCommandOpts } from '../../lib/admin-target'
5
- import type { KernelCommandOpts } from '../types'
6
-
7
- import { lookupImplicitOwnedInstance } from '../client'
8
-
9
- describe('implicit managed target owner discovery', () => {
10
- test('keeps target credentials out of the admin inventory lookup', async () => {
11
- const owned: OwnedInstanceInfo = {
12
- id: 'owned-id',
13
- slug: 'owned',
14
- url: 'https://owned.eu.astrale.ai',
15
- state: 'ready',
16
- }
17
- let captured:
18
- | {
19
- slug: string
20
- opts: KernelCommandOpts & AdminTargetCommandOpts
21
- }
22
- | undefined
23
- const lookupOwned = mock(
24
- async (slug: string, opts: KernelCommandOpts & AdminTargetCommandOpts) => {
25
- captured = { slug, opts }
26
- return owned
27
- },
28
- )
29
-
30
- await lookupImplicitOwnedInstance(
31
- 'owned',
32
- {
33
- as: 'target-identity',
34
- creds: 'target-delegation',
35
- timeout: '45000',
36
- debug: true,
37
- },
38
- { lookupOwned },
39
- )
40
-
41
- expect(captured).toEqual({
42
- slug: 'owned',
43
- opts: {
44
- timeout: '45000',
45
- debug: true,
46
- },
47
- })
48
- expect(captured?.opts).not.toHaveProperty('as')
49
- expect(captured?.opts).not.toHaveProperty('creds')
50
- })
51
- })
@@ -1,43 +0,0 @@
1
- import { describe, expect, test } from 'bun:test'
2
-
3
- // We test the stripMethodSuffix behavior indirectly through the module's exports.
4
- // Since stripMethodSuffix is private, we test the pattern directly.
5
- const stripMethodSuffix = (msg: string): string => {
6
- return msg.replace(/(\/[^"\s:]+)::([a-zA-Z]\w*)/g, '$1')
7
- }
8
-
9
- describe('stripMethodSuffix', () => {
10
- test('strips ::listChildren from path', () => {
11
- expect(stripMethodSuffix('Path not found: "/nonexistent::listChildren"')).toBe(
12
- 'Path not found: "/nonexistent"',
13
- )
14
- })
15
-
16
- test('strips ::get from path', () => {
17
- expect(stripMethodSuffix('Path not found: "/some/path::get"')).toBe(
18
- 'Path not found: "/some/path"',
19
- )
20
- })
21
-
22
- test('strips any method name', () => {
23
- expect(stripMethodSuffix('"/kernel.astrale.ai/Root::describe"')).toBe(
24
- '"/kernel.astrale.ai/Root"',
25
- )
26
- })
27
-
28
- test('does not strip from non-path strings', () => {
29
- expect(stripMethodSuffix('regular::text')).toBe('regular::text')
30
- })
31
-
32
- test('handles multiple paths in one message', () => {
33
- expect(stripMethodSuffix('"/a/b::get" and "/c/d::list"')).toBe('"/a/b" and "/c/d"')
34
- })
35
-
36
- test('preserves messages without method suffixes', () => {
37
- expect(stripMethodSuffix('No path found')).toBe('No path found')
38
- })
39
-
40
- test('does not strip single colon (not a method dispatch)', () => {
41
- expect(stripMethodSuffix('"/a/b:notMethod"')).toBe('"/a/b:notMethod"')
42
- })
43
- })
@@ -1,123 +0,0 @@
1
- import { describe, expect, test } from 'bun:test'
2
-
3
- import type { SelfResolverContext } from '../../lib/self'
4
-
5
- import { resolveSelfIdLazy } from '../expand'
6
-
7
- describe('resolveSelfIdLazy', () => {
8
- test('refreshes a cached IdP @self registration via whoami', async () => {
9
- const writes: unknown[][] = []
10
- const ctx: SelfResolverContext = {
11
- identity: {
12
- name: 'bryan',
13
- subject: 'user_01KC9MW1M6S5J6V9ERSRJ8RDYF',
14
- createdAt: '2026-06-25T08:00:00.000Z',
15
- source: 'idp',
16
- registrations: {
17
- bryan: {
18
- iss: 'https://old.example',
19
- sub: '4ad8e4ce-5cf7-4c2e-ab29-5549023dc8bf',
20
- registeredAt: '2026-06-25T08:00:00.000Z',
21
- },
22
- },
23
- },
24
- instanceSlug: 'bryan',
25
- instanceSigned: false,
26
- }
27
-
28
- const id = await resolveSelfIdLazy(
29
- ctx,
30
- {},
31
- {
32
- whoami: async () => ({
33
- id: 'f011538e-9edc-4c29-92ce-9b81b6c1b6c7',
34
- kernelUrl: 'https://bryan.example',
35
- }),
36
- setRegistration: async (...args) => {
37
- writes.push(args)
38
- },
39
- now: () => new Date('2026-06-25T08:15:00.000Z'),
40
- },
41
- )
42
-
43
- expect(id).toBe('f011538e-9edc-4c29-92ce-9b81b6c1b6c7')
44
- expect(writes).toEqual([
45
- [
46
- 'bryan',
47
- 'bryan',
48
- {
49
- iss: 'https://bryan.example',
50
- sub: 'f011538e-9edc-4c29-92ce-9b81b6c1b6c7',
51
- registeredAt: '2026-06-25T08:15:00.000Z',
52
- },
53
- ],
54
- ])
55
- })
56
-
57
- test('keeps the cached IdP id when whoami is temporarily unavailable', async () => {
58
- const ctx: SelfResolverContext = {
59
- identity: {
60
- name: 'bryan',
61
- subject: 'user_01KC9MW1M6S5J6V9ERSRJ8RDYF',
62
- createdAt: '2026-06-25T08:00:00.000Z',
63
- source: 'idp',
64
- registrations: {
65
- bryan: {
66
- iss: 'https://bryan.example',
67
- sub: 'cached-id',
68
- registeredAt: '2026-06-25T08:00:00.000Z',
69
- },
70
- },
71
- },
72
- instanceSlug: 'bryan',
73
- instanceSigned: false,
74
- }
75
-
76
- await expect(
77
- resolveSelfIdLazy(
78
- ctx,
79
- {},
80
- {
81
- whoami: async () => {
82
- throw new Error('network down')
83
- },
84
- },
85
- ),
86
- ).resolves.toBe('cached-id')
87
- })
88
-
89
- test('does not run whoami for key-backed registrations', async () => {
90
- const ctx: SelfResolverContext = {
91
- identity: {
92
- name: 'alice',
93
- subject: 'alice',
94
- createdAt: '2026-06-25T08:00:00.000Z',
95
- source: 'key',
96
- registrations: {
97
- bryan: {
98
- iss: 'https://bryan.example',
99
- sub: 'key-node-id',
100
- registeredAt: '2026-06-25T08:00:00.000Z',
101
- },
102
- },
103
- },
104
- instanceSlug: 'bryan',
105
- instanceSigned: false,
106
- }
107
- let called = false
108
-
109
- const id = await resolveSelfIdLazy(
110
- ctx,
111
- {},
112
- {
113
- whoami: async () => {
114
- called = true
115
- return { id: 'should-not-be-used', kernelUrl: 'https://bryan.example' }
116
- },
117
- },
118
- )
119
-
120
- expect(id).toBe('key-node-id')
121
- expect(called).toBe(false)
122
- })
123
- })
@@ -1,214 +0,0 @@
1
- import { KernelClient, type FnMap } from '@astrale-os/kernel-client'
2
- import { ClientSession, delegationMintVia } from '@astrale-os/kernel-client/session'
3
-
4
- import type { AdminTargetCommandOpts } from '../lib/admin-target'
5
- import type { KernelCommandOpts } from './types'
6
-
7
- import { AstraleError } from '../errors'
8
- import {
9
- callOwnedInstances,
10
- findOwnedInstance,
11
- type InstanceInfo,
12
- type OwnedInstanceInfo,
13
- } from '../lib/admin-instance'
14
- import { readConfig } from '../lib/config'
15
- import { resolveInstanceTarget, type ResolvedInstanceTarget } from '../lib/instance-target'
16
- import { resolveCredential } from './auth'
17
- import { fetchWithCaFile } from './ca-fetch'
18
-
19
- const DEFAULT_TIMEOUT_MS = 30_000
20
-
21
- export type ClientContext = {
22
- /** High-level call surface — bound to `credential` via ClientSession.identity. */
23
- client: ClientSession<FnMap>
24
- credential: string
25
- url: string
26
- config: Awaited<ReturnType<typeof readConfig>>
27
- }
28
-
29
- export type ResolvedKernelTarget = {
30
- url: string
31
- audience: string
32
- slug?: string
33
- defaultIdentity?: string
34
- caFile?: string
35
- }
36
-
37
- /** Resolve the kernel a command targets (`--url` / `-i` / active instance). */
38
- export async function resolveKernelTarget(
39
- opts: KernelCommandOpts,
40
- config: Awaited<ReturnType<typeof readConfig>>,
41
- ): Promise<ResolvedKernelTarget> {
42
- // Ad-hoc `--url` — unknown kernel. Stamp the URL itself as audience,
43
- // no slug for per-instance signing.
44
- if (opts.url && !opts.instance) {
45
- const resolved = await resolveInstanceTarget({ source: 'url', url: opts.url }, { config })
46
- return resolvedToKernelTarget(resolved)
47
- }
48
- const resolved = await resolveInstanceTarget(
49
- opts.instance ? { source: 'name', name: opts.instance } : { source: 'active' },
50
- {
51
- config,
52
- admin: {},
53
- managed: (slug) => lookupImplicitOwnedInstance(slug, opts),
54
- },
55
- )
56
- return resolvedToKernelTarget(resolved, opts.url)
57
- }
58
-
59
- /**
60
- * Connect to a kernel instance, run `fn`, then disconnect.
61
- * The new client is lazy: construction does no I/O. We only need to
62
- * release sockets on the way out.
63
- */
64
- export async function withKernelClient<T>(
65
- opts: KernelCommandOpts,
66
- fn: (ctx: ClientContext) => Promise<T>,
67
- ): Promise<T> {
68
- const config = await readConfig()
69
- const target = await resolveKernelTarget(opts, config)
70
- return withResolvedKernelClient(opts, config, target, fn)
71
- }
72
-
73
- export async function listOwnedInstances(
74
- opts: KernelCommandOpts & AdminTargetCommandOpts,
75
- ): Promise<OwnedInstanceInfo[]> {
76
- return await withAdminKernelClient(opts, async (ctx) => callOwnedInstances(ctx.client))
77
- }
78
-
79
- export async function lookupOwnedInstance(
80
- slug: string,
81
- opts: KernelCommandOpts & AdminTargetCommandOpts,
82
- ): Promise<OwnedInstanceInfo> {
83
- const instance = findOwnedInstance(await listOwnedInstances(opts), slug)
84
- if (instance) return instance
85
- throw new AstraleError('INSTANCE_NOT_FOUND', `No owned instance matches "${slug}".`)
86
- }
87
-
88
- export type ImplicitOwnedInstanceLookupDependencies = {
89
- lookupOwned: (
90
- slug: string,
91
- opts: KernelCommandOpts & AdminTargetCommandOpts,
92
- ) => Promise<OwnedInstanceInfo>
93
- }
94
-
95
- /**
96
- * Resolve an implicit managed target through the caller's owner inventory.
97
- *
98
- * Target `--as` / `--creds` belong to the eventual child-kernel call. They
99
- * must not authenticate the admin discovery request: leaving them out lets
100
- * `withAdminKernelClient` use the admin bookmark's default WorkOS identity.
101
- */
102
- export async function lookupImplicitOwnedInstance(
103
- slug: string,
104
- targetOpts: KernelCommandOpts,
105
- deps: ImplicitOwnedInstanceLookupDependencies = {
106
- lookupOwned: lookupOwnedInstance,
107
- },
108
- ): Promise<InstanceInfo> {
109
- return await deps.lookupOwned(slug, {
110
- timeout: targetOpts.timeout,
111
- debug: targetOpts.debug,
112
- })
113
- }
114
-
115
- function resolvedToKernelTarget(
116
- target: ResolvedInstanceTarget,
117
- urlOverride?: string,
118
- ): ResolvedKernelTarget {
119
- return {
120
- url: urlOverride ?? target.url,
121
- audience: target.issuer,
122
- slug: target.name,
123
- defaultIdentity: target.defaultIdentity,
124
- caFile: target.caFile,
125
- }
126
- }
127
-
128
- export async function withAdminKernelClient<T>(
129
- opts: KernelCommandOpts & AdminTargetCommandOpts,
130
- fn: (ctx: ClientContext) => Promise<T>,
131
- ): Promise<T> {
132
- const config = await readConfig()
133
- const target = await resolveInstanceTarget({ source: 'admin' }, { config, admin: opts })
134
- return withResolvedKernelClient(
135
- opts,
136
- config,
137
- {
138
- url: target.url,
139
- audience: target.issuer,
140
- slug: target.name,
141
- defaultIdentity: target.defaultIdentity,
142
- caFile: target.caFile,
143
- },
144
- fn,
145
- )
146
- }
147
-
148
- async function withResolvedKernelClient<T>(
149
- opts: KernelCommandOpts,
150
- config: Awaited<ReturnType<typeof readConfig>>,
151
- target: ResolvedKernelTarget,
152
- fn: (ctx: ClientContext) => Promise<T>,
153
- ): Promise<T> {
154
- const credential = await resolveCredential(
155
- { ...opts, defaultIdentity: target.defaultIdentity },
156
- config,
157
- target.audience,
158
- target.slug,
159
- )
160
-
161
- // CLI is short-lived and one-shot per command. Skip the WS upgrade
162
- // (saves up to 5s on hangs) and disable HTTP retries (saves ~7s of
163
- // exponential backoff on ECONNREFUSED / 5xx). The user can re-run.
164
- const requestTimeout = resolveTimeoutMs(opts.timeout)
165
- const fetchImpl = target.caFile ? fetchWithCaFile(target.caFile) : undefined
166
- const client: ClientSession<FnMap> = new ClientSession<FnMap>({
167
- default: target.url,
168
- identity: credential,
169
- delegation: {
170
- // Resolve lazily: the session must exist before the cache mints.
171
- mint: delegationMintVia(() => client, credential),
172
- ttl: 3600,
173
- },
174
- pool: {
175
- clientFactory: (u) =>
176
- new KernelClient<FnMap>({
177
- url: u,
178
- requestTimeout,
179
- defaultTransport: 'http',
180
- retry: { maxAttempts: 1 },
181
- ...(fetchImpl ? { fetch: fetchImpl } : {}),
182
- }),
183
- },
184
- })
185
- await client.ready()
186
-
187
- try {
188
- return await fn({ client, credential, url: target.url, config })
189
- } catch (error) {
190
- // Attach url so formatKernelError can display it in connection errors
191
- if (error instanceof Error) (error as Error & { url?: string }).url = target.url
192
- throw error
193
- } finally {
194
- client.disconnect()
195
- }
196
- }
197
-
198
- function resolveTimeoutMs(raw: string | undefined): number {
199
- if (raw === undefined) return DEFAULT_TIMEOUT_MS
200
- if (!/^\d+$/.test(raw)) {
201
- throw new AstraleError(
202
- 'INVALID_FLAG',
203
- `Invalid --timeout value "${raw}" — expected a positive integer (milliseconds)`,
204
- )
205
- }
206
- const n = Number.parseInt(raw, 10)
207
- if (!Number.isFinite(n) || n <= 0) {
208
- throw new AstraleError(
209
- 'INVALID_FLAG',
210
- `Invalid --timeout value "${raw}" — must be a positive integer`,
211
- )
212
- }
213
- return n
214
- }