@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,192 +0,0 @@
1
- import type { KernelCommandOpts } from './types'
2
-
3
- /** CLI bridge for @self resolution and stale-registration error hints. */
4
- import { readConfig } from '../lib/config'
5
- import { getDefault, getIdentity, setRegistration } from '../lib/identity'
6
- import { decodeTokenClaims, readIdpSession } from '../lib/idp'
7
- import { resolveInstanceTarget } from '../lib/instance-target'
8
- import { fileExists, keypairPaths } from '../lib/keys'
9
- import { KEYS_DIR } from '../lib/paths'
10
- import {
11
- containsSelfRef,
12
- expandSelfReferences,
13
- resolveSelfNodeId,
14
- selfRefusalError,
15
- type SelfResolverContext,
16
- type SelfResolution,
17
- } from '../lib/self'
18
- import { lookupImplicitOwnedInstance, withKernelClient } from './client'
19
-
20
- /** Metadata attached to errors so the NotFoundError path can hint at stale `@self` expansions. */
21
- export type SelfExpansionMeta = {
22
- original: string
23
- expanded: string
24
- selfId: string
25
- identity?: string
26
- slug?: string
27
- }
28
-
29
- /** Build the same target/signing context `withKernelClient` will use. */
30
- export async function buildSelfContext(opts: KernelCommandOpts): Promise<SelfResolverContext> {
31
- const config = await readConfig()
32
- // Mirror withKernelClient's slug logic: --url without -i ⇒ no slug.
33
- let slug: string | undefined
34
- let defaultIdentity: string | undefined
35
- if (opts.url && !opts.instance) {
36
- slug = undefined
37
- } else {
38
- const resolved = await resolveInstanceTarget(
39
- opts.instance ? { source: 'name', name: opts.instance } : { source: 'active' },
40
- {
41
- config,
42
- admin: {},
43
- managed: (instanceSlug) => lookupImplicitOwnedInstance(instanceSlug, opts),
44
- },
45
- )
46
- slug = resolved.name
47
- defaultIdentity = resolved.defaultIdentity
48
- }
49
-
50
- // Mirror resolveCredential's instance-signed branch: when targeting a
51
- // child for which the CLI generated a dedicated keypair, the call signs
52
- // as the instance itself, not as a user identity.
53
- let instanceSigned = false
54
- if (!opts.creds && !opts.as && !defaultIdentity && slug && slug !== 'manager') {
55
- const { privatePath } = keypairPaths(slug, KEYS_DIR)
56
- instanceSigned = await fileExists(privatePath)
57
- }
58
-
59
- // Identity lookup failures should surface as real CLI errors, not @self refusals.
60
- let identity: SelfResolverContext['identity']
61
- if (!opts.creds) {
62
- const identityName = opts.as ?? defaultIdentity
63
- if (identityName) {
64
- const i = await getIdentity(identityName)
65
- identity = { ...i, name: identityName }
66
- } else {
67
- identity = await getDefault()
68
- }
69
- }
70
-
71
- let idpSubject: string | undefined
72
- if (identity && (identity.source ?? 'key') === 'idp') {
73
- instanceSigned = false
74
- const session = await readIdpSession(identity.name)
75
- const claims = decodeTokenClaims(session?.id_token ?? session?.access_token)
76
- if (typeof claims?.sub === 'string' && claims.sub.trim().length > 0) {
77
- idpSubject = claims.sub
78
- }
79
- }
80
-
81
- return { identity, instanceSlug: slug, credsJwt: opts.creds, instanceSigned, idpSubject }
82
- }
83
-
84
- /**
85
- * Run `fn`; if it throws a `NotFoundError`, stamp expansion metadata onto the
86
- * error so `formatKernelError` can append the stale-registration hint. The
87
- * error is re-thrown either way.
88
- */
89
- export async function withSelfHint<T>(
90
- fn: () => Promise<T>,
91
- meta: SelfExpansionMeta | undefined,
92
- ): Promise<T> {
93
- if (!meta) return fn()
94
- try {
95
- return await fn()
96
- } catch (err) {
97
- if (err instanceof Error && err.name === 'NotFoundError') {
98
- ;(err as Error & { expandedFromSelf?: SelfExpansionMeta }).expandedFromSelf = meta
99
- }
100
- throw err
101
- }
102
- }
103
-
104
- /** Convenience: resolve the nodeId once, or throw the typed refusal. */
105
- export function resolveOrThrow(selfCtx: SelfResolverContext): string {
106
- const r: SelfResolution = resolveSelfNodeId(selfCtx)
107
- if ('reason' in r) throw selfRefusalError(r)
108
- return r.id
109
- }
110
-
111
- export type ResolveSelfIdLazyDeps = {
112
- whoami?: (opts: KernelCommandOpts) => Promise<{ id?: unknown; kernelUrl: string }>
113
- setRegistration?: typeof setRegistration
114
- now?: () => Date
115
- }
116
-
117
- /**
118
- * Resolve @self for IdP identities through one whoami refresh, then cache the
119
- * current node id. Non-IdP refusals stay typed and local.
120
- */
121
- export async function resolveSelfIdLazy(
122
- selfCtx: SelfResolverContext,
123
- opts: KernelCommandOpts,
124
- deps: ResolveSelfIdLazyDeps = {},
125
- ): Promise<string> {
126
- const r: SelfResolution = resolveSelfNodeId(selfCtx)
127
- const cachedId = 'reason' in r ? undefined : r.id
128
- const isIdp = (selfCtx.identity?.source ?? 'key') === 'idp'
129
-
130
- if (isIdp && selfCtx.instanceSlug && selfCtx.identity) {
131
- let lookup: { id?: unknown; kernelUrl: string }
132
- try {
133
- lookup = await (deps.whoami ?? whoamiSelfId)(opts)
134
- } catch {
135
- if (cachedId) return cachedId
136
- if ('reason' in r) throw selfRefusalError(r)
137
- throw selfRefusalError({ reason: 'idp-no-sub', identityName: selfCtx.identity.name })
138
- }
139
-
140
- const resolvedId =
141
- typeof lookup.id === 'string' && lookup.id.trim().length > 0 ? lookup.id : undefined
142
- if (!resolvedId) {
143
- if (cachedId) return cachedId
144
- if ('reason' in r) throw selfRefusalError(r)
145
- throw selfRefusalError({ reason: 'idp-no-sub', identityName: selfCtx.identity.name })
146
- }
147
-
148
- const cached = selfCtx.identity.registrations?.[selfCtx.instanceSlug]
149
- if (cached?.sub !== resolvedId || cached?.iss !== lookup.kernelUrl) {
150
- await (deps.setRegistration ?? setRegistration)(selfCtx.identity.name, selfCtx.instanceSlug, {
151
- iss: lookup.kernelUrl,
152
- sub: resolvedId,
153
- registeredAt: (deps.now ?? (() => new Date()))().toISOString(),
154
- })
155
- }
156
- return resolvedId
157
- }
158
-
159
- if (!('reason' in r)) return r.id
160
- throw selfRefusalError(r)
161
- }
162
-
163
- async function whoamiSelfId(opts: KernelCommandOpts): Promise<{ id?: unknown; kernelUrl: string }> {
164
- let kernelUrl = ''
165
- const me = await withKernelClient(opts, (ctx) => {
166
- kernelUrl = ctx.url
167
- return ctx.client.as(ctx.credential).auth.whoami()
168
- })
169
- return { id: me.id, kernelUrl }
170
- }
171
-
172
- /** Expand @self in path-like commands and return metadata for NotFound hints. */
173
- export async function expandSelfInPath(
174
- path: string,
175
- opts: KernelCommandOpts,
176
- ): Promise<{ path: string; meta: SelfExpansionMeta | undefined }> {
177
- if (!containsSelfRef(path)) return { path, meta: undefined }
178
- const selfCtx = await buildSelfContext(opts)
179
- const id = await resolveSelfIdLazy(selfCtx, opts)
180
- const expanded = expandSelfReferences(path, id)
181
- if (expanded === path) return { path, meta: undefined }
182
- return {
183
- path: expanded,
184
- meta: {
185
- original: path,
186
- expanded,
187
- selfId: id,
188
- identity: selfCtx.identity?.name,
189
- slug: selfCtx.instanceSlug,
190
- },
191
- }
192
- }
@@ -1,96 +0,0 @@
1
- import type {
2
- GetResultWire,
3
- GraphApi,
4
- GraphNodeWire,
5
- MutationResultWire,
6
- PatchInput,
7
- QueryASTInput,
8
- } from '@astrale-os/kernel-client/graph'
9
-
10
- import { createGraph, rawOf } from '@astrale-os/kernel-client/graph'
11
-
12
- import type { ClientContext } from './client'
13
-
14
- export type {
15
- GetResultWire,
16
- GraphApi,
17
- GraphNodeWire,
18
- MutationResultWire,
19
- PatchInput,
20
- QueryASTInput,
21
- }
22
-
23
- /** A node row as it crosses the graph doors — the shared item shape for `get`/`ls`/`describe`. */
24
- export type GraphNode = GraphNodeWire
25
-
26
- /**
27
- * Bind the graph sugar (`function.get` / `function.mutate` + the read/write
28
- * helpers) over a CLI client session. The sugar is pure over a call capability;
29
- * we hand it `ClientSession.call` — the same surface every command already uses.
30
- */
31
- export function bindGraph(ctx: ClientContext): GraphApi {
32
- return createGraph((method, params) => ctx.client.call(method, params))
33
- }
34
-
35
- /**
36
- * Split a `function.get` page into addressed root and descendants. Root matches
37
- * path/id first, then falls back to the shallowest node in the page.
38
- */
39
- export function splitRoot(
40
- nodes: readonly GraphNode[],
41
- rootPath: string,
42
- ): { root: GraphNode | undefined; children: GraphNode[] } {
43
- const raw = rawOf(rootPath)
44
- const idForm = rootPath.startsWith('@') ? rootPath.slice(1) : undefined
45
-
46
- let idx = nodes.findIndex(
47
- (n) => rawOf(n.path) === raw || (idForm !== undefined && n.id === idForm),
48
- )
49
- if (idx < 0 && nodes.length > 0) {
50
- let min = Number.POSITIVE_INFINITY
51
- nodes.forEach((n, i) => {
52
- const depth = rawOf(n.path).split('/').length
53
- if (depth < min) {
54
- min = depth
55
- idx = i
56
- }
57
- })
58
- }
59
-
60
- const root = idx >= 0 ? nodes[idx] : undefined
61
- const children = idx >= 0 ? nodes.filter((_, i) => i !== idx) : [...nodes]
62
- return { root, children }
63
- }
64
-
65
- /** The children cursor of a single-root `function.get` result, if the page overflowed. */
66
- export function childrenCursor(result: GetResultWire): string | undefined {
67
- const next = result.next
68
- if (!next) return undefined
69
- return Object.values(next)[0]?.children
70
- }
71
-
72
- /**
73
- * Strip a fully-qualified prop key to its leaf name. Node props travel under
74
- * qualified keys (`<domain>:class.X.property.name` / `<domain>:interface.I.property.name`);
75
- * this recovers `name`.
76
- */
77
- export function unqualifyKey(key: string): string {
78
- const dot = key.lastIndexOf('.property.')
79
- if (dot >= 0) return key.slice(dot + '.property.'.length)
80
- const slash = key.lastIndexOf('/')
81
- return slash >= 0 ? key.slice(slash + 1) : key
82
- }
83
-
84
- /** Read a node prop by its unqualified leaf name (exact key wins, else a qualified match). */
85
- export function nodeProp(
86
- node: { props: Record<string, unknown> } | null | undefined,
87
- name: string,
88
- ): unknown {
89
- const props = node?.props
90
- if (!props) return undefined
91
- if (name in props) return props[name]
92
- for (const [k, v] of Object.entries(props)) {
93
- if (unqualifyKey(k) === name) return v
94
- }
95
- return undefined
96
- }
@@ -1,29 +0,0 @@
1
- export {
2
- withKernelClient,
3
- withAdminKernelClient,
4
- resolveKernelTarget,
5
- type ClientContext,
6
- type ResolvedKernelTarget,
7
- } from './client'
8
- export { resolveCredential } from './auth'
9
- export { formatKernelError } from './errors'
10
- export {
11
- buildSelfContext,
12
- expandSelfInPath,
13
- withSelfHint,
14
- resolveOrThrow,
15
- resolveSelfIdLazy,
16
- type SelfExpansionMeta,
17
- } from './expand'
18
- export { runKernelCommand } from './run'
19
- export { bindGraph, splitRoot, childrenCursor, nodeProp, unqualifyKey } from './graph'
20
- export type {
21
- GetResultWire,
22
- GraphApi,
23
- GraphNode,
24
- GraphNodeWire,
25
- MutationResultWire,
26
- PatchInput,
27
- QueryASTInput,
28
- } from './graph'
29
- export type { KernelCommandOpts, CallCommandOpts } from './types'
@@ -1,22 +0,0 @@
1
- import type { CommandOption } from '../command'
2
-
3
- /**
4
- * Flags consumed by `withKernelClient` (and therefore valid on any command
5
- * that talks to a kernel). Spread these into a command's `options` to expose
6
- * the same `--url / --instance / --timeout / --as / --creds / --debug`
7
- * surface as `astrale call`.
8
- */
9
- export const KERNEL_PASSTHROUGH_OPTIONS: CommandOption[] = [
10
- {
11
- flags: '--url <url>',
12
- description: 'Target a kernel URL directly (overrides instance resolution)',
13
- },
14
- {
15
- flags: '-i, --instance <name>',
16
- description: 'Target a specific instance (overrides active)',
17
- },
18
- { flags: '--timeout <ms>', description: 'Request timeout in ms (default: 30000)' },
19
- { flags: '--as <identity>', description: 'Call as a specific identity' },
20
- { flags: '--creds <token>', description: 'Use a pre-signed credential (e.g. delegation token)' },
21
- { flags: '--debug', description: 'Print full error diagnostics on failure' },
22
- ]
@@ -1,14 +0,0 @@
1
- import type { OutputOpts } from '../lib/output'
2
-
3
- export type KernelCommandOpts = OutputOpts & {
4
- url?: string
5
- instance?: string
6
- timeout?: string
7
- as?: string
8
- creds?: string
9
- debug?: boolean
10
- }
11
-
12
- export type CallCommandOpts = KernelCommandOpts & {
13
- data?: string
14
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * `inferAlg` regression tests.
3
- *
4
- * (The historical `collectFunctionSubs` adversarial suite — 15 cases that
5
- * pinned the raw-spec edge-walker against both wire forms / both member
6
- * namespaces — was removed when the CLI converged onto the kernel's
7
- * canonical resolver. Subs derivation now runs the IDENTICAL
8
- * `Graph.fromWire` → `deserializeDomainFromGraph` → `collectFunctionSubs`
9
- * pipeline the kernel uses at install validation, so drift is impossible
10
- * by construction. The contract is pinned by
11
- * `kernel/core/__tests__/domain/resolve-callables-agreement.test.ts`.)
12
- */
13
-
14
- import { describe, expect, test } from 'bun:test'
15
-
16
- import { AstraleError } from '../../errors'
17
- import { inferAlg } from '../domain-identity'
18
-
19
- describe('inferAlg', () => {
20
- // Adversarial: older CLI releases generated ES256 keys without
21
- // stamping `alg`. Every fresh-machine `astrale domain install` then
22
- // crashed with "alg is required when jwk.alg is not present" because the
23
- // CLI read `privateJwk.alg as string` directly and handed `undefined` to
24
- // jose's importJWK. The fix sets `alg` at keygen time AND falls back to
25
- // inferring from `crv`/`kty` for already-issued keys (META_TRACE #34).
26
-
27
- test('returns explicit alg when present', () => {
28
- expect(inferAlg({ alg: 'ES256' })).toBe('ES256')
29
- expect(inferAlg({ alg: 'EdDSA' })).toBe('EdDSA')
30
- })
31
-
32
- test('infers ES256 from P-256 EC keys (crv + kty) when alg is missing', () => {
33
- expect(inferAlg({ kty: 'EC', crv: 'P-256' })).toBe('ES256')
34
- })
35
-
36
- test('infers EdDSA from Ed25519 OKP keys (crv + kty) when alg is missing', () => {
37
- expect(inferAlg({ kty: 'OKP', crv: 'Ed25519' })).toBe('EdDSA')
38
- })
39
-
40
- test('explicit alg wins over inferable shape (no second-guessing)', () => {
41
- // If a file says ES256 but has Ed25519 components, that's a broken
42
- // file — don't silently override. assertKeyPairConsistent will catch
43
- // the mismatch downstream.
44
- expect(inferAlg({ alg: 'ES256', kty: 'OKP', crv: 'Ed25519' })).toBe('ES256')
45
- })
46
-
47
- test('throws AstraleError with hint when no alg/crv/kty resolves', () => {
48
- expect(() => inferAlg({})).toThrow(AstraleError)
49
- expect(() => inferAlg({ kty: 'EC' })).toThrow(AstraleError)
50
- expect(() => inferAlg({ kty: 'EC', crv: 'P-384' })).toThrow(AstraleError)
51
- })
52
-
53
- test('error message includes keyPath when provided', () => {
54
- expect(() => inferAlg({}, '/path/to/broken.jwk')).toThrow(/\/path\/to\/broken\.jwk/)
55
- })
56
-
57
- test('treats empty-string alg as missing', () => {
58
- expect(inferAlg({ alg: '', kty: 'EC', crv: 'P-256' })).toBe('ES256')
59
- })
60
- })
@@ -1,104 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
2
- import { mkdtemp, readdir, readFile, rm, stat, utimes, writeFile } from 'node:fs/promises'
3
- import { tmpdir } from 'node:os'
4
- import { join } from 'node:path'
5
-
6
- import { atomicWrite, withFileLock } from '../fs-atomic'
7
-
8
- let tmp: string
9
-
10
- beforeEach(async () => {
11
- tmp = await mkdtemp(join(tmpdir(), 'astrale-fs-atomic-'))
12
- })
13
-
14
- afterEach(async () => {
15
- await rm(tmp, { recursive: true, force: true })
16
- })
17
-
18
- describe('atomicWrite', () => {
19
- test('writes the content with mode 0600 and leaves no tmp files behind', async () => {
20
- const path = join(tmp, 'out.json')
21
-
22
- await atomicWrite(path, '{"a":1}')
23
-
24
- expect(await readFile(path, 'utf-8')).toBe('{"a":1}')
25
- expect(((await stat(path)).mode & 0o777).toString(8)).toBe('600')
26
- expect(await readdir(tmp)).toEqual(['out.json'])
27
- })
28
- })
29
-
30
- describe('withFileLock', () => {
31
- test('serializes concurrent read-modify-write cycles', async () => {
32
- const lockPath = join(tmp, 'counter.lock')
33
- const counterPath = join(tmp, 'counter')
34
- await writeFile(counterPath, '0')
35
-
36
- const bump = () =>
37
- withFileLock(
38
- lockPath,
39
- async () => {
40
- const value = Number(await readFile(counterPath, 'utf-8'))
41
- await new Promise((r) => setTimeout(r, 2))
42
- await writeFile(counterPath, String(value + 1))
43
- },
44
- { pollIntervalMs: 2 },
45
- )
46
- await Promise.all(Array.from({ length: 20 }, bump))
47
-
48
- expect(await readFile(counterPath, 'utf-8')).toBe('20')
49
- })
50
-
51
- test('takes over a lock whose mtime is stale', async () => {
52
- const lockPath = join(tmp, 'stale.lock')
53
- await writeFile(lockPath, JSON.stringify({ pid: process.pid }))
54
- const past = new Date(Date.now() - 60_000)
55
- await utimes(lockPath, past, past)
56
-
57
- const ran = await withFileLock(lockPath, async () => 'ran', { pollIntervalMs: 5 })
58
-
59
- expect(ran).toBe('ran')
60
- })
61
-
62
- test('takes over a fresh lock held by a dead pid', async () => {
63
- const lockPath = join(tmp, 'dead.lock')
64
- await writeFile(lockPath, JSON.stringify({ pid: 99999999 }))
65
-
66
- const ran = await withFileLock(lockPath, async () => 'ran', { pollIntervalMs: 5 })
67
-
68
- expect(ran).toBe('ran')
69
- })
70
-
71
- test('releases the lock when fn throws', async () => {
72
- const lockPath = join(tmp, 'throw.lock')
73
-
74
- await expect(
75
- withFileLock(lockPath, async () => {
76
- throw new Error('boom')
77
- }),
78
- ).rejects.toThrow('boom')
79
-
80
- const ran = await withFileLock(lockPath, async () => 'ran', { timeoutMs: 200 })
81
- expect(ran).toBe('ran')
82
- })
83
-
84
- test('creates a missing parent directory', async () => {
85
- const lockPath = join(tmp, 'nested', 'deep', 'a.lock')
86
-
87
- const ran = await withFileLock(lockPath, async () => 'ran')
88
-
89
- expect(ran).toBe('ran')
90
- })
91
-
92
- test('times out while a live holder keeps the lock', async () => {
93
- const lockPath = join(tmp, 'held.lock')
94
- await writeFile(lockPath, JSON.stringify({ pid: process.pid }))
95
-
96
- await expect(
97
- withFileLock(lockPath, async () => 'ran', {
98
- pollIntervalMs: 20,
99
- staleAfterMs: 60_000,
100
- timeoutMs: 150,
101
- }),
102
- ).rejects.toThrow('Timed out')
103
- })
104
- })
@@ -1,79 +0,0 @@
1
- import { describe, expect, test } from 'bun:test'
2
-
3
- import { IdentityStoreSchema } from '../identity'
4
-
5
- describe('IdentityStoreSchema', () => {
6
- test('parses valid store', () => {
7
- const result = IdentityStoreSchema.parse({
8
- default: 'manager',
9
- identities: {
10
- manager: { subject: 'manager', createdAt: '2024-01-01T00:00:00Z' },
11
- },
12
- })
13
- expect(result.default).toBe('manager')
14
- expect(result.identities.manager.subject).toBe('manager')
15
- })
16
-
17
- test('parses multiple identities', () => {
18
- const result = IdentityStoreSchema.parse({
19
- default: 'admin',
20
- identities: {
21
- admin: {
22
- subject: 'admin',
23
- createdAt: '2024-01-01T00:00:00Z',
24
- issuer: 'https://admin.example.com',
25
- },
26
- user: { subject: 'user-principal', createdAt: '2024-06-15T12:00:00Z' },
27
- },
28
- })
29
- expect(Object.keys(result.identities)).toHaveLength(2)
30
- expect(result.identities.admin.issuer).toBe('https://admin.example.com')
31
- expect(result.identities.user.subject).toBe('user-principal')
32
- })
33
-
34
- test('parses IdP-backed identities without key metadata', () => {
35
- const result = IdentityStoreSchema.parse({
36
- default: 'alice',
37
- identities: {
38
- alice: {
39
- source: 'idp',
40
- subject: 'user_123',
41
- idp: 'workos',
42
- issuer: 'https://example.authkit.app',
43
- audience: 'https://api.example.com',
44
- createdAt: '2024-01-01T00:00:00Z',
45
- claims: { email: 'alice@example.com' },
46
- },
47
- },
48
- })
49
-
50
- expect(result.identities.alice.source).toBe('idp')
51
- expect(result.identities.alice.kid).toBeUndefined()
52
- expect(result.identities.alice.idp).toBe('workos')
53
- })
54
-
55
- test('rejects missing default field', () => {
56
- expect(() =>
57
- IdentityStoreSchema.parse({
58
- identities: { manager: { subject: 'manager', createdAt: '2024-01-01' } },
59
- }),
60
- ).toThrow()
61
- })
62
-
63
- test('rejects identity missing subject', () => {
64
- expect(() =>
65
- IdentityStoreSchema.parse({
66
- default: 'manager',
67
- identities: { manager: { createdAt: '2024-01-01' } },
68
- }),
69
- ).toThrow()
70
- })
71
-
72
- test('accepts empty identities record', () => {
73
- const result = IdentityStoreSchema.parse({
74
- default: 'nobody',
75
- identities: {},
76
- })
77
- expect(Object.keys(result.identities)).toHaveLength(0)
78
- })
79
- })