@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
package/src/lib/keys.ts DELETED
@@ -1,294 +0,0 @@
1
- import { generateKeyPair, exportJWK, importJWK, SignJWT, type JWK } from 'jose'
2
- import { randomUUID } from 'node:crypto'
3
- import { readFile, mkdir, access, unlink } from 'node:fs/promises'
4
- import { join } from 'node:path'
5
-
6
- import { IdentityKeyMissingError } from '../errors'
7
- import { inferAlg } from './domain-identity'
8
- import { atomicWrite } from './fs-atomic'
9
- import { log } from './log'
10
- import { KEYS_DIR } from './paths'
11
-
12
- const LEGACY_MANAGER_PRIVATE = 'manager.private.jwk'
13
- const LEGACY_MANAGER_PUBLIC = 'manager.public.jwk'
14
-
15
- // Default issuer/audience for the host-mode manager's own credential. The
16
- // manager kernel mounts at `/host` (the reserved host slug — SPEC §5.2).
17
- const DEFAULT_MANAGER_ISSUER = 'http://localhost:4400/host'
18
-
19
- type AuthOptions = {
20
- issuer?: string
21
- subject?: string
22
- kid?: string
23
- }
24
-
25
- export type AuthBinding = {
26
- credential: string
27
- publicKey: { jwk: JWK }
28
- }
29
-
30
- export type KeypairPaths = {
31
- privatePath: string
32
- publicPath: string
33
- }
34
-
35
- /**
36
- * Per-identity keypair paths. `manager` stays on the legacy filenames so
37
- * existing installs keep working; other subjects use `<subject>.*.jwk`.
38
- */
39
- export function keypairPaths(subject: string, keysDir: string = KEYS_DIR): KeypairPaths {
40
- if (subject === 'manager') {
41
- return {
42
- privatePath: join(keysDir, LEGACY_MANAGER_PRIVATE),
43
- publicPath: join(keysDir, LEGACY_MANAGER_PUBLIC),
44
- }
45
- }
46
- return {
47
- privatePath: join(keysDir, `${subject}.private.jwk`),
48
- publicPath: join(keysDir, `${subject}.public.jwk`),
49
- }
50
- }
51
-
52
- export async function fileExists(path: string): Promise<boolean> {
53
- try {
54
- await access(path)
55
- return true
56
- } catch {
57
- return false
58
- }
59
- }
60
-
61
- /** List identity names that have a private key on disk. */
62
- export async function listIdentityKeys(keysDir: string = KEYS_DIR): Promise<string[]> {
63
- try {
64
- const { readdir } = await import('node:fs/promises')
65
- const entries = await readdir(keysDir)
66
- const names = new Set<string>()
67
- for (const entry of entries) {
68
- if (entry === LEGACY_MANAGER_PRIVATE) names.add('manager')
69
- else if (entry.endsWith('.private.jwk')) names.add(entry.replace(/\.private\.jwk$/, ''))
70
- }
71
- return Array.from(names).sort()
72
- } catch {
73
- return []
74
- }
75
- }
76
-
77
- /**
78
- * Generate a fresh Ed25519 keypair as plain JWKs (not persisted).
79
- *
80
- * Used by the domain scaffold to stamp worker key files with a real,
81
- * internally-consistent pair — defense against drift between `d` and `x`
82
- * (historical template copies had a mismatched `x`, making every scaffold
83
- * inherit a broken pair). Same crypto as ES256 elsewhere in the CLI but
84
- * EdDSA/Ed25519 matches the worker runtime convention.
85
- */
86
- export async function generateEd25519Jwk(
87
- kid: string,
88
- ): Promise<{ privateJwk: JWK; publicJwk: JWK }> {
89
- const { publicKey, privateKey } = await generateKeyPair('EdDSA', {
90
- crv: 'Ed25519',
91
- extractable: true,
92
- })
93
- const privateJwk = await exportJWK(privateKey)
94
- const publicJwk = await exportJWK(publicKey)
95
- privateJwk.alg = 'EdDSA'
96
- publicJwk.alg = 'EdDSA'
97
- privateJwk.kid = kid
98
- publicJwk.kid = kid
99
- return { privateJwk, publicJwk }
100
- }
101
-
102
- /**
103
- * Generate a new keypair for `subject`, persist atomically with 0o600
104
- * perms, and return the JWKs + kid. Overwrites any existing keys.
105
- */
106
- export async function persistKeypair(
107
- subject: string,
108
- opts?: { keysDir?: string; kid?: string },
109
- ): Promise<{ publicJwk: JWK; privateJwk: JWK; kid: string }> {
110
- const keysDir = opts?.keysDir ?? KEYS_DIR
111
- const { privatePath, publicPath } = keypairPaths(subject, keysDir)
112
- await mkdir(keysDir, { recursive: true })
113
-
114
- const { publicKey, privateKey } = await generateKeyPair('ES256', { extractable: true })
115
- const publicJwk = await exportJWK(publicKey)
116
- const privateJwk = await exportJWK(privateKey)
117
- const kid = opts?.kid ?? `${subject}-key-${randomUUID().slice(0, 8)}`
118
- publicJwk.kid = kid
119
- privateJwk.kid = kid
120
- publicJwk.alg = 'ES256'
121
- privateJwk.alg = 'ES256'
122
-
123
- await atomicWrite(privatePath, JSON.stringify(privateJwk, null, 2))
124
- await atomicWrite(publicPath, JSON.stringify(publicJwk, null, 2))
125
-
126
- return { publicJwk, privateJwk, kid }
127
- }
128
-
129
- /** Remove a subject's keypair files. Idempotent. */
130
- export async function removeKeypair(subject: string, keysDir: string = KEYS_DIR): Promise<void> {
131
- const { privatePath, publicPath } = keypairPaths(subject, keysDir)
132
- for (const p of [privatePath, publicPath]) {
133
- try {
134
- await unlink(p)
135
- } catch {
136
- /* ignore */
137
- }
138
- }
139
- }
140
-
141
- // Legacy signal: warn once per session when an unknown subject falls back
142
- // to the manager key.
143
- const warnedFallback = new Set<string>()
144
-
145
- async function loadSigningMaterial(
146
- subject: string,
147
- keysDir: string,
148
- ): Promise<{ privateJwk: JWK; publicJwk: JWK; kid: string }> {
149
- const { privatePath, publicPath } = keypairPaths(subject, keysDir)
150
-
151
- if (await fileExists(privatePath)) {
152
- const privateJwk = JSON.parse(await readFile(privatePath, 'utf-8')) as JWK
153
- const publicJwk = JSON.parse(await readFile(publicPath, 'utf-8')) as JWK
154
- return {
155
- privateJwk,
156
- publicJwk,
157
- kid: (privateJwk.kid as string | undefined) ?? `${subject}-key`,
158
- }
159
- }
160
-
161
- if (process.env.ASTRALE_STRICT_IDENTITIES === '1') {
162
- throw new IdentityKeyMissingError(subject)
163
- }
164
-
165
- // Manager always uses its own file. Any other subject falls through
166
- // to the manager key with a one-shot warning so the migration window
167
- // stays visible without being noisy.
168
- if (subject === 'manager') throw new IdentityKeyMissingError(subject)
169
-
170
- const { privatePath: mgrPrivate, publicPath: mgrPublic } = keypairPaths('manager', keysDir)
171
- if (!(await fileExists(mgrPrivate))) throw new IdentityKeyMissingError(subject)
172
-
173
- if (!warnedFallback.has(subject)) {
174
- log.warn(
175
- `Legacy shared-key mode for "${subject}" — run \`astrale identity create ${subject}\` to generate a dedicated key.`,
176
- )
177
- warnedFallback.add(subject)
178
- }
179
- const privateJwk = JSON.parse(await readFile(mgrPrivate, 'utf-8')) as JWK
180
- const publicJwk = JSON.parse(await readFile(mgrPublic, 'utf-8')) as JWK
181
- return {
182
- privateJwk,
183
- publicJwk,
184
- kid: (privateJwk.kid as string | undefined) ?? 'manager-key',
185
- }
186
- }
187
-
188
- /**
189
- * Sign a self-identity credential (`grant: identity/self`) from a private
190
- * JWK. Shared by `persistAuth`, `loadAuth`, and `signAs` so the protected
191
- * header and claim set stay identical across all three; the only thing that
192
- * varies is how each caller obtains the key material and which
193
- * subject/audience it stamps.
194
- */
195
- async function signIdentityCredential(args: {
196
- privateJwk: JWK
197
- kid: string
198
- issuer: string
199
- subject: string
200
- audience: string
201
- }): Promise<string> {
202
- const alg = inferAlg(args.privateJwk as Record<string, unknown>)
203
- const privateKey = await importJWK(args.privateJwk, alg)
204
- return new SignJWT({ grant: { v: 1, expr: { kind: 'identity', self: true } } })
205
- .setProtectedHeader({ alg, kid: args.kid })
206
- .setIssuer(args.issuer)
207
- .setSubject(args.subject)
208
- .setAudience(args.audience)
209
- .sign(privateKey)
210
- }
211
-
212
- /**
213
- * Generate a new keypair, persist to disk, and return a signed credential.
214
- * Wraps the manager init path — use `persistKeypair` for bare keypair generation.
215
- */
216
- export async function persistAuth(
217
- keysDir: string = KEYS_DIR,
218
- opts?: AuthOptions,
219
- ): Promise<AuthBinding> {
220
- const issuer = opts?.issuer ?? DEFAULT_MANAGER_ISSUER
221
- const subject = opts?.subject ?? 'manager'
222
- const kid = opts?.kid ?? `${subject}-key`
223
-
224
- const { privateJwk, publicJwk } = await persistKeypair(subject, { keysDir, kid })
225
- const credential = await signIdentityCredential({
226
- privateJwk,
227
- kid,
228
- issuer,
229
- subject,
230
- audience: issuer,
231
- })
232
-
233
- return { credential, publicKey: { jwk: publicJwk } }
234
- }
235
-
236
- /**
237
- * Load existing keys from disk and sign a fresh credential.
238
- */
239
- export async function loadAuth(
240
- keysDir: string = KEYS_DIR,
241
- opts?: AuthOptions,
242
- ): Promise<AuthBinding> {
243
- const issuer = opts?.issuer ?? DEFAULT_MANAGER_ISSUER
244
- const subject = opts?.subject ?? 'manager'
245
-
246
- const { privateJwk, publicJwk, kid } = await loadSigningMaterial(subject, keysDir)
247
- const credential = await signIdentityCredential({
248
- privateJwk,
249
- kid,
250
- issuer,
251
- subject,
252
- audience: issuer,
253
- })
254
-
255
- return { credential, publicKey: { jwk: publicJwk } }
256
- }
257
-
258
- /**
259
- * Load existing keys if present, otherwise generate and persist new ones.
260
- */
261
- export async function resolveAuth(
262
- keysDir: string = KEYS_DIR,
263
- opts?: AuthOptions,
264
- ): Promise<AuthBinding> {
265
- const subject = opts?.subject ?? 'manager'
266
- const { privatePath } = keypairPaths(subject, keysDir)
267
- if (await fileExists(privatePath)) {
268
- return loadAuth(keysDir, opts)
269
- }
270
- return persistAuth(keysDir, opts)
271
- }
272
-
273
- /**
274
- * Sign a JWT as a specific subject. Uses the subject's own keypair when
275
- * present; falls back to the manager key (with a one-shot warning) for
276
- * unknown subjects until `ASTRALE_STRICT_IDENTITIES=1` is set.
277
- */
278
- export async function signAs(
279
- subject: string,
280
- keysDir: string = KEYS_DIR,
281
- opts?: { issuer?: string; audience?: string; subject?: string },
282
- ): Promise<string> {
283
- const issuer = opts?.issuer ?? DEFAULT_MANAGER_ISSUER
284
- const audience = opts?.audience ?? issuer
285
- const { privateJwk, kid } = await loadSigningMaterial(subject, keysDir)
286
-
287
- return signIdentityCredential({
288
- privateJwk,
289
- kid,
290
- issuer,
291
- subject: opts?.subject ?? subject,
292
- audience,
293
- })
294
- }
package/src/lib/paths.ts DELETED
@@ -1,11 +0,0 @@
1
- import { paths } from './env'
2
-
3
- export const ASTRALE_HOME = paths.home
4
- export const KEYS_DIR = paths.keys
5
- export const DATA_DIR = paths.data
6
- export const CONFIG_PATH = paths.config
7
- export const INSTALL_PATH = paths.install
8
- export const IDENTITIES_PATH = paths.identities
9
- export const INSTANCES_PATH = paths.instances
10
- export const IDPS_PATH = paths.idps
11
- export const IDP_SESSIONS_DIR = paths.idpSessionsDir
package/src/program.ts DELETED
@@ -1,213 +0,0 @@
1
- import { Command, Option } from 'commander'
2
-
3
- import type { CommandDefinition } from './command'
4
-
5
- import pkg from '../package.json' with { type: 'json' }
6
- import { KERNEL_PASSTHROUGH_OPTIONS } from './kernel/options'
7
- import { RAW_OUTPUT_OPTIONS } from './lib/output'
8
- import { registerCommand, registerGroup } from './registry'
9
-
10
- /**
11
- * Build the fully-wired Commander program (every command + group registered)
12
- * WITHOUT parsing argv. `bin/astrale.ts` is a thin shim that calls this and
13
- * `.parse()`; tests import it to walk the command tree (the `--help` surface
14
- * is asserted to be the source of truth it claims to be — see
15
- * `commands/__tests__/help-contract.test.ts`).
16
- */
17
- export async function buildProgram(): Promise<Command> {
18
- const program = new Command()
19
-
20
- program
21
- .name('astrale')
22
- .description('Astrale CLI — connect to existing Astrale kernels')
23
- // Single source of truth = package.json (bumped by release-please together
24
- // with .release-please-manifest.json). Never hand-write a version literal.
25
- .version(pkg.version)
26
- .showSuggestionAfterError(true)
27
- .addOption(new Option('--ci', 'Machine mode: no prompts, structured errors on stderr'))
28
- .addOption(new Option('--no-prompt', 'Disable interactive prompts'))
29
- .addOption(
30
- new Option('--offline-ok', 'Tolerate offline state for commands that can operate locally'),
31
- )
32
- .addOption(
33
- new Option('--log-level <level>', 'Log level').choices(['debug', 'info', 'warn', 'error']),
34
- )
35
- .addOption(new Option('--log-format <format>', 'Log output format').choices(['text', 'json']))
36
- .action(async () => {
37
- // Bare `astrale` in an interactive terminal with nothing connected yet →
38
- // launch the guided setup. Otherwise (configured, piped, or CI) show help.
39
- if (process.stdin.isTTY && process.stdout.isTTY) {
40
- const { shouldAutostartSetup } = await import('./setup/engine')
41
- if (await shouldAutostartSetup()) {
42
- await (await import('./commands/setup')).default.action(undefined, {})
43
- return
44
- }
45
- }
46
- program.help()
47
- })
48
-
49
- // Options shared by every kernel-touching command (call / token / get / ls /
50
- // describe / query + `domain install`). Merged onto the command's own
51
- // options at the registration site so the list stays single-sourced — the
52
- // command-definition files only carry their command-specific options.
53
- const kernelOptions = [
54
- {
55
- flags: '--format <type>',
56
- description: 'Output format (default: yaml in TTY, json when piped)',
57
- choices: ['yaml', 'json'],
58
- },
59
- ...RAW_OUTPUT_OPTIONS,
60
- ...KERNEL_PASSTHROUGH_OPTIONS,
61
- ]
62
-
63
- const withKernelOptions = (def: CommandDefinition): CommandDefinition => ({
64
- ...def,
65
- options: [...(def.options ?? []), ...kernelOptions],
66
- })
67
-
68
- // Verbatim alias of `identity whoami` — defer to the command-definition
69
- // module so options stay in sync.
70
- const whoamiMod = await import('./commands/identity/whoami')
71
- registerCommand(program, {
72
- name: 'whoami',
73
- description: 'Show the current default identity (alias for identity whoami)',
74
- options: whoamiMod.default.options,
75
- action: whoamiMod.default.action,
76
- })
77
- registerCommand(program, (await import('./commands/setup')).default)
78
- registerCommand(program, (await import('./commands/use')).default)
79
- registerCommand(program, (await import('./commands/update')).default)
80
-
81
- // ── Graph / kernel ─────────────────────────────────────────────
82
- registerCommand(program, withKernelOptions((await import('./commands/call')).default))
83
- registerCommand(program, withKernelOptions((await import('./commands/token')).default))
84
- registerCommand(program, withKernelOptions((await import('./commands/get')).default))
85
- registerCommand(program, withKernelOptions((await import('./commands/mutate')).default))
86
- registerCommand(program, withKernelOptions((await import('./commands/ls')).default))
87
- registerCommand(program, withKernelOptions((await import('./commands/describe')).default))
88
- registerCommand(program, withKernelOptions((await import('./commands/query')).default))
89
- registerCommand(program, withKernelOptions((await import('./commands/logs')).default))
90
- registerCommand(program, withKernelOptions((await import('./commands/view')).default))
91
- registerCommand(program, (await import('./commands/status')).default)
92
- registerCommand(program, (await import('./commands/browser')).default)
93
- registerCommand(program, (await import('./commands/view-serve')).default)
94
- registerCommand(program, (await import('./commands/studio')).default)
95
-
96
- registerGroup(program, {
97
- name: 'instance',
98
- description: 'Manage admin-provisioned instances and local bookmarks',
99
- commands: [
100
- withKernelOptions((await import('./commands/instance/list')).default),
101
- (await import('./commands/instance/bookmark')).default,
102
- (await import('./commands/instance/forget')).default,
103
- withKernelOptions((await import('./commands/instance/create')).default),
104
- withKernelOptions((await import('./commands/instance/delete')).default),
105
- withKernelOptions((await import('./commands/instance/status')).default),
106
- (await import('./commands/instance/active')).default,
107
- (await import('./commands/instance/use')).default,
108
- ],
109
- })
110
-
111
- registerGroup(program, {
112
- name: 'domain',
113
- description: 'List, publish, and install domains (admin catalog + per-instance install)',
114
- commands: [
115
- withKernelOptions((await import('./commands/domain/list')).default),
116
- withKernelOptions((await import('./commands/domain/publish')).default),
117
- withKernelOptions((await import('./commands/domain/install')).default),
118
- ],
119
- })
120
-
121
- registerGroup(program, {
122
- name: 'admin',
123
- description: 'Configure the admin kernel',
124
- commands: [
125
- (await import('./commands/admin/status')).default,
126
- (await import('./commands/admin/use')).default,
127
- ],
128
- })
129
-
130
- registerGroup(program, {
131
- name: 'identity',
132
- description: 'Manage CLI identities & delegation keypairs',
133
- commands: [
134
- (await import('./commands/identity/create')).default,
135
- (await import('./commands/identity/register')).default,
136
- (await import('./commands/identity/list')).default,
137
- (await import('./commands/identity/use')).default,
138
- (await import('./commands/identity/whoami')).default,
139
- (await import('./commands/identity/delete')).default,
140
- (await import('./commands/identity/sync')).default,
141
- (await import('./commands/identity/unsync')).default,
142
- (await import('./commands/identity/export')).default,
143
- (await import('./commands/identity/import')).default,
144
- ],
145
- })
146
-
147
- registerGroup(program, {
148
- name: 'auth',
149
- description: 'Authenticate with configured identity providers',
150
- commands: [
151
- (await import('./commands/auth/login')).default,
152
- (await import('./commands/auth/token')).default,
153
- (await import('./commands/auth/logout')).default,
154
- (await import('./commands/auth/status')).default,
155
- ],
156
- })
157
-
158
- registerGroup(program, {
159
- name: 'session',
160
- description: 'Locally recorded work sessions: list and DX-analyze them',
161
- commands: [
162
- (await import('./commands/session/list')).default,
163
- (await import('./commands/session/analyze')).default,
164
- ],
165
- })
166
-
167
- registerGroup(program, {
168
- name: 'idp',
169
- description: 'Manage OpenID Connect identity providers',
170
- commands: [
171
- (await import('./commands/idp/add')).default,
172
- (await import('./commands/idp/list')).default,
173
- (await import('./commands/idp/show')).default,
174
- (await import('./commands/idp/refresh')).default,
175
- (await import('./commands/idp/remove')).default,
176
- ],
177
- })
178
-
179
- program.addHelpText(
180
- 'after',
181
- `
182
- Command groups:
183
- Getting started setup (sign in, pick an instance, equip your workspace)
184
- Kernel ls, get, mutate, call, query, describe, token
185
- Management admin, instance, domain, identity, auth, idp, update
186
- Agent browser (drive the GUI via agent-browser)
187
- Studio studio (launch the local Domain Studio GUI for a workspace)
188
-
189
- Path syntax:
190
- /domain Domain node
191
- /domain/class.Name Class node (or /domain/interface.Name)
192
- /domain/class.Name/method Static method — single slash
193
- (interface-hosted static: /domain/interface.Name/method)
194
- <nodePath>::method Instance method dispatch — double colon ::
195
- @nodeId Reference a node by its UID
196
- @nodeId::method Instance method on a node by UID
197
-
198
- Examples:
199
- $ astrale ls /
200
- $ astrale studio
201
- $ astrale admin status
202
- $ astrale update --check
203
- $ astrale instance bookmark staging --url https://kernel.example.com
204
- $ astrale instance create my-app
205
- $ astrale instance status staging
206
- $ astrale token --audience shell.astrale.ai --ttl 3600
207
- $ astrale query / --depth 1
208
- $ astrale query --cypher 'MATCH (n) RETURN n LIMIT 5'
209
- `,
210
- )
211
-
212
- return program
213
- }