@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
@@ -1,4 +1,9 @@
1
- import type { ClientContext } from '../../kernel'
1
+ import type { ResolvedView as SessionResolvedView } from '@astrale-os/kernel-client/session'
2
+ import type { ResolvedView } from '@astrale-os/shell'
3
+
4
+ import { Path } from '@astrale-os/sdk/graph/path'
5
+
6
+ import type { ConnectionContext } from '../../connection'
2
7
 
3
8
  import { AstraleError } from '../../errors'
4
9
 
@@ -9,7 +14,6 @@ import { AstraleError } from '../../errors'
9
14
  * it via `view_for`.
10
15
  */
11
16
 
12
- const VIEW_RESOLVE_PATH = '/:kernel.astrale.ai:class.View:resolve'
13
17
  const VIEW_PATH_RE = /^\/:[^\s/:@]+:view\.[a-z][a-z0-9-]*$/
14
18
 
15
19
  export type ViewSpec = { kind: 'view' | 'target'; path: string }
@@ -23,31 +27,62 @@ export function parseViewSpec(spec: string): ViewSpec {
23
27
  )
24
28
  }
25
29
 
26
- /** Wire shape of one `View:resolve` entry. */
27
- export type ViewCandidate = {
30
+ /** The owning Domain principal used when an explicit ViewPath has no target override. */
31
+ export function viewOwnerTarget(viewPath: string): string {
32
+ const parsed = Path.parse(viewPath)
33
+ if (parsed.ast.anchor.kind !== 'domain') {
34
+ throw new AstraleError('INVALID_ARGUMENT', `ViewPath has no Domain owner: ${viewPath}`)
35
+ }
36
+ return `/:${parsed.ast.anchor.origin}`
37
+ }
38
+
39
+ /** One exact Shell selection plus stable presentation fields for CLI output. */
40
+ export type ViewCandidate = ResolvedView & {
28
41
  id: string
29
42
  path: string
30
43
  url: string
31
44
  name?: string
32
- handshake?: 'shell' | 'none'
45
+ handshake: 'shell' | 'none'
33
46
  origin: 'self' | 'class'
47
+ issuer: string
48
+ etag: string
49
+ revision: string
34
50
  }
35
51
 
36
52
  export async function resolveViewCandidates(
37
- ctx: ClientContext,
53
+ ctx: ConnectionContext,
38
54
  nodePath: string,
39
55
  ): Promise<ViewCandidate[]> {
40
- const result = await ctx.client.call(VIEW_RESOLVE_PATH, { node: nodePath })
41
- if (!Array.isArray(result)) {
42
- throw new AstraleError('UNEXPECTED_RESULT', `View:resolve returned a non-array for ${nodePath}`)
43
- }
44
- return result as ViewCandidate[]
56
+ const target = Path.parse(nodePath).raw
57
+ const catalog = await ctx.session.viewsFor(target)
58
+ return catalog.views.map((route) => toCandidate(target, route))
59
+ }
60
+
61
+ function toCandidate(target: ResolvedView['target'], route: SessionResolvedView): ViewCandidate {
62
+ const key = String(route.key)
63
+ return Object.freeze({
64
+ target,
65
+ route,
66
+ id: key,
67
+ path: `/:${key}`,
68
+ url: route.href,
69
+ name: key.slice(key.lastIndexOf(':view.') + ':view.'.length),
70
+ handshake: route.handshake,
71
+ origin: route.declaration.target.kind === 'domain' ? 'self' : 'class',
72
+ issuer: route.issuer,
73
+ etag: route.etag,
74
+ revision: route.revision,
75
+ })
76
+ }
77
+
78
+ /** Strip presentation aliases before crossing the Shell mount boundary. */
79
+ export function selectedView(candidate: ViewCandidate): ResolvedView {
80
+ return Object.freeze({ target: candidate.target, route: candidate.route })
45
81
  }
46
82
 
47
83
  /** The slug tail of a candidate: `view.dashboard` → `dashboard`. */
48
84
  export function candidateSlug(candidate: ViewCandidate): string {
49
- const fromPath = candidate.path?.split('/').pop() ?? ''
50
- return candidate.name ?? fromPath
85
+ return candidate.name ?? candidate.id.slice(candidate.id.lastIndexOf(':view.') + ':view.'.length)
51
86
  }
52
87
 
53
88
  export function pickCandidate(
@@ -56,7 +91,10 @@ export function pickCandidate(
56
91
  slug?: string,
57
92
  ): ViewCandidate | 'ambiguous' {
58
93
  if (slug) {
59
- const match = candidates.find((c) => candidateSlug(c) === slug || c.path.endsWith(`/${slug}`))
94
+ const match = candidates.find(
95
+ (candidate) =>
96
+ candidate.id === slug || candidate.path === slug || candidateSlug(candidate) === slug,
97
+ )
60
98
  if (!match) {
61
99
  throw new AstraleError(
62
100
  'VIEW_NOT_FOUND',
@@ -71,34 +109,3 @@ export function pickCandidate(
71
109
  if (candidates.length === 1) return candidates[0]
72
110
  return 'ambiguous'
73
111
  }
74
-
75
- /**
76
- * Apply a `--view-url` override: an origin-only value swaps the origin and
77
- * keeps the resolved path, a value with a path replaces the URL wholesale.
78
- */
79
- export function applyViewUrlOverride(resolvedUrl: string, override: string): string {
80
- const parsed = parseUrl(override)
81
- if (parsed.pathname !== '/') return parsed.toString()
82
- const original = parseUrl(resolvedUrl)
83
- return new URL(original.pathname + original.search + original.hash, parsed.origin).toString()
84
- }
85
-
86
- /**
87
- * The kernel addresses locally-served workers as `host.docker.internal`
88
- * (reachable from its container); the host browser reaches the same worker on
89
- * loopback. Rewrite so the iframe loads without an /etc/hosts entry.
90
- */
91
- export function rewriteLocalViewUrl(url: string): string {
92
- const parsed = parseUrl(url)
93
- if (parsed.hostname !== 'host.docker.internal') return url
94
- parsed.hostname = '127.0.0.1'
95
- return parsed.toString()
96
- }
97
-
98
- function parseUrl(raw: string): URL {
99
- try {
100
- return new URL(raw)
101
- } catch {
102
- throw new AstraleError('INVALID_URL', `Not a valid URL: ${raw}`)
103
- }
104
- }
@@ -7,8 +7,8 @@ import { Readable } from 'node:stream'
7
7
 
8
8
  import type { ViewServeConfig } from './session'
9
9
 
10
- import { withKernelClient } from '../../kernel'
11
- import { fetchWithCaFile } from '../../kernel/ca-fetch'
10
+ import { withClientSession } from '../../connection'
11
+ import { fetchWithCaFile } from '../ca-fetch'
12
12
  import { viewerDistDir } from './assets'
13
13
  import { removeSessionFiles } from './session'
14
14
 
@@ -29,7 +29,7 @@ const IDLE_SWEEP_MS = 60_000
29
29
 
30
30
  export type PageStatus = { state: string; error?: string; at: string }
31
31
 
32
- type TokenGrant = { token: string; expiresAt: number; kind: 'minted' | 'raw' }
32
+ type TokenGrant = { token: string; expiresAt: number; kind: 'minted' }
33
33
 
34
34
  export function startViewServer(config: ViewServeConfig): Server {
35
35
  const { session, proxy } = config
@@ -40,31 +40,15 @@ export function startViewServer(config: ViewServeConfig): Server {
40
40
  let grant: TokenGrant | null = null
41
41
  let lastActivity = Date.now()
42
42
 
43
- /**
44
- * Prefer a kernel-minted TTL-bound identity credential (what the GUI hands
45
- * its iframes); fall back to the raw CLI credential so the session works
46
- * anywhere the CLI itself can call.
47
- */
43
+ /** Mint one TTL-bound credential; raw CLI credentials never enter the browser session. */
48
44
  async function freshGrant(): Promise<TokenGrant> {
49
45
  if (grant && grant.expiresAt - Date.now() > TOKEN_REFRESH_MARGIN_MS) return grant
50
- grant = await withKernelClient(config.kernel, async (ctx) => {
51
- try {
52
- const token = await ctx.client.as(ctx.credential).auth.mint()
53
- return {
54
- token,
55
- expiresAt: jwtExpiry(token) ?? Date.now() + FALLBACK_TOKEN_TTL_MS,
56
- kind: 'minted' as const,
57
- }
58
- } catch (error) {
59
- console.log(
60
- `mint failed (${error instanceof Error ? error.message : String(error)}) — falling back to the raw CLI credential`,
61
- )
62
- const token = ctx.credential
63
- return {
64
- token,
65
- expiresAt: jwtExpiry(token) ?? Date.now() + FALLBACK_TOKEN_TTL_MS,
66
- kind: 'raw' as const,
67
- }
46
+ grant = await withClientSession(config.kernel, async ({ auth, target }) => {
47
+ const token = await auth.mint({ audience: target.kernelIssuer, ttlSeconds: 3_600 })
48
+ return {
49
+ token,
50
+ expiresAt: jwtExpiry(token) ?? Date.now() + FALLBACK_TOKEN_TTL_MS,
51
+ kind: 'minted' as const,
68
52
  }
69
53
  })
70
54
  return grant
@@ -108,14 +92,9 @@ export function startViewServer(config: ViewServeConfig): Server {
108
92
  }
109
93
  if (sub === '/config.json' && req.method === 'GET') {
110
94
  json(res, 200, {
111
- viewUrl: session.view.url,
112
- viewPath: session.view.path ?? null,
113
- viewName: session.view.name ?? null,
114
- functionId: session.view.functionId,
115
- handshake: session.view.handshake,
116
- targetNodeId: session.target?.id ?? null,
117
- targetPath: session.target?.path ?? null,
95
+ view: session.view,
118
96
  kernelUrl: proxy.direct ? proxy.kernelUrl : `${base}/k`,
97
+ kernelIssuer: proxy.issuer,
119
98
  identity: session.identity ?? null,
120
99
  instance: session.instance ?? null,
121
100
  sessionId: session.id,
@@ -123,6 +102,10 @@ export function startViewServer(config: ViewServeConfig): Server {
123
102
  return
124
103
  }
125
104
  if (sub === '/token' && req.method === 'POST') {
105
+ if (session.view.route.handshake !== 'shell') {
106
+ json(res, 403, { error: 'plain views have no Astrale credential privilege' })
107
+ return
108
+ }
126
109
  const fresh = await freshGrant()
127
110
  json(res, 200, { token: fresh.token, expiresAt: fresh.expiresAt, kind: fresh.kind })
128
111
  return
@@ -1,7 +1,10 @@
1
- import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises'
1
+ import type { ResolvedView } from '@astrale-os/shell'
2
+
3
+ import { closeSync, fchmodSync, openSync } from 'node:fs'
4
+ import { chmod, mkdir, readdir, readFile, rm } from 'node:fs/promises'
2
5
  import { join } from 'node:path'
3
6
 
4
- import { paths } from '../env'
7
+ import { atomicWrite, paths } from '../../state/index'
5
8
 
6
9
  /**
7
10
  * View-session records: one detached server process per open view, tracked as
@@ -18,15 +21,8 @@ export type ViewSessionRecord = {
18
21
  nonce: string
19
22
  /** Host page URL, nonce-scoped: `http://127.0.0.1:<port>/s/<nonce>/`. */
20
23
  pageUrl: string
21
- view: {
22
- url: string
23
- functionId: string
24
- handshake: 'shell' | 'none'
25
- /** ViewPath or resolved view node path, when the view came from the graph. */
26
- path?: string
27
- name?: string
28
- }
29
- target?: { id?: string; path?: string }
24
+ /** Exact verified View placement passed to Shell without split aliases. */
25
+ view: ResolvedView
30
26
  instance?: string
31
27
  identity?: string
32
28
  createdAt: string
@@ -44,24 +40,57 @@ export type ViewServeConfig = {
44
40
  * loopback proxy). Otherwise the child gets the nonce-scoped proxy, which
45
41
  * handles CORS and self-signed local CAs.
46
42
  */
47
- proxy: { kernelUrl: string; caFile?: string; direct: boolean }
43
+ proxy: { kernelUrl: string; issuer: string; caFile?: string; direct: boolean }
48
44
  idleMs: number
49
45
  }
50
46
 
51
- export const recordPath = (id: string): string => join(VIEW_DIR, `${id}.json`)
52
- export const logPath = (id: string): string => join(VIEW_DIR, `${id}.log`)
53
- export const configPath = (id: string): string => join(VIEW_DIR, `${id}.config.json`)
47
+ export const recordPath = (id: string, directory = VIEW_DIR): string =>
48
+ join(directory, `${id}.json`)
49
+ export const logPath = (id: string, directory = VIEW_DIR): string => join(directory, `${id}.log`)
50
+ export const configPath = (id: string, directory = VIEW_DIR): string =>
51
+ join(directory, `${id}.config.json`)
52
+
53
+ /** Create or repair the credential-bearing session directory as owner-only. */
54
+ export async function ensureViewDirectory(directory = VIEW_DIR): Promise<void> {
55
+ await mkdir(directory, { recursive: true, mode: 0o700 })
56
+ await chmod(directory, 0o700)
57
+ }
58
+
59
+ export async function saveRecord(record: ViewSessionRecord, directory = VIEW_DIR): Promise<void> {
60
+ await ensureViewDirectory(directory)
61
+ await atomicWrite(recordPath(record.id, directory), `${JSON.stringify(record, null, 2)}\n`)
62
+ }
63
+
64
+ /** Atomically publish the detached process config; it may contain raw `--creds`. */
65
+ export async function saveServeConfig(
66
+ config: ViewServeConfig,
67
+ directory = VIEW_DIR,
68
+ ): Promise<void> {
69
+ await ensureViewDirectory(directory)
70
+ await atomicWrite(
71
+ configPath(config.session.id, directory),
72
+ `${JSON.stringify(config, null, 2)}\n`,
73
+ )
74
+ }
54
75
 
55
- export async function saveRecord(record: ViewSessionRecord): Promise<void> {
56
- await mkdir(VIEW_DIR, { recursive: true })
57
- await writeFile(recordPath(record.id), `${JSON.stringify(record, null, 2)}\n`)
76
+ /** Open or repair a session log as owner-only and return its descriptor. */
77
+ export async function openSessionLog(id: string, directory = VIEW_DIR): Promise<number> {
78
+ await ensureViewDirectory(directory)
79
+ const descriptor = openSync(logPath(id, directory), 'a', 0o600)
80
+ try {
81
+ fchmodSync(descriptor, 0o600)
82
+ return descriptor
83
+ } catch (error) {
84
+ closeSync(descriptor)
85
+ throw error
86
+ }
58
87
  }
59
88
 
60
- export async function removeSessionFiles(id: string): Promise<void> {
89
+ export async function removeSessionFiles(id: string, directory = VIEW_DIR): Promise<void> {
61
90
  await Promise.all([
62
- rm(recordPath(id), { force: true }),
63
- rm(configPath(id), { force: true }),
64
- rm(logPath(id), { force: true }),
91
+ rm(recordPath(id, directory), { force: true }),
92
+ rm(configPath(id, directory), { force: true }),
93
+ rm(logPath(id, directory), { force: true }),
65
94
  ])
66
95
  }
67
96
 
@@ -0,0 +1,13 @@
1
+ export {
2
+ ASTRALE_HOME,
3
+ CONFIG_PATH,
4
+ DATA_DIR,
5
+ IDENTITIES_PATH,
6
+ IDPS_PATH,
7
+ IDP_SESSIONS_DIR,
8
+ INSTALL_PATH,
9
+ INSTANCES_PATH,
10
+ KEYS_DIR,
11
+ createPaths,
12
+ } from '../../state/.spec/api.js'
13
+ export type { PathEnvironment, Paths } from '../../state/.spec/api.js'
@@ -0,0 +1,5 @@
1
+ # CLI paths facade
2
+
3
+ This independently consumed Node facade exposes only the CLI state coordinates retained by
4
+ `@astrale-os/cli/paths`. The State module remains their semantic owner; this facade adds no mutable
5
+ configuration or filesystem behavior.
@@ -0,0 +1,3 @@
1
+ import { defineLayout } from '@astrale-os/spec/authoring'
2
+
3
+ export default defineLayout({ entries: ['index.ts'], exact: true })
@@ -0,0 +1,13 @@
1
+ export {
2
+ ASTRALE_HOME,
3
+ CONFIG_PATH,
4
+ DATA_DIR,
5
+ IDENTITIES_PATH,
6
+ IDPS_PATH,
7
+ IDP_SESSIONS_DIR,
8
+ INSTALL_PATH,
9
+ INSTANCES_PATH,
10
+ KEYS_DIR,
11
+ createPaths,
12
+ } from '../state/index'
13
+ export type { PathEnvironment, Paths } from '../state/index'
@@ -0,0 +1,42 @@
1
+ import type { Command } from 'commander'
2
+
3
+ /** One positional argument in a CLI command definition. */
4
+ export interface CommandArgument {
5
+ readonly name: string
6
+ readonly description: string
7
+ readonly required?: boolean
8
+ }
9
+
10
+ /** One Commander-compatible option in a CLI command definition. */
11
+ export interface CommandOption {
12
+ readonly flags: string
13
+ readonly description: string
14
+ readonly default?: string
15
+ readonly choices?: string[]
16
+ }
17
+
18
+ /** Internal authoring shape implemented by each command module. */
19
+ export interface CommandDefinition {
20
+ readonly name: string
21
+ readonly description: string
22
+ readonly summary?: string
23
+ readonly hidden?: boolean
24
+ readonly aliases?: string[]
25
+ readonly arguments?: CommandArgument[]
26
+ readonly options?: CommandOption[]
27
+ readonly afterHelpText?: string
28
+ /** Opaque Commander callback; each command module owns its concrete argument tuple. */
29
+ readonly action: (...args: never[]) => Promise<void>
30
+ }
31
+
32
+ /** Internal composition shape for one group of commands. */
33
+ export interface CommandGroup {
34
+ readonly name: string
35
+ readonly description: string
36
+ readonly summary?: string
37
+ readonly commands: CommandDefinition[]
38
+ readonly subgroups?: CommandGroup[]
39
+ }
40
+
41
+ /** Build a fresh, complete Commander tree without parsing process arguments. */
42
+ export function buildProgram(): Promise<Command>
@@ -0,0 +1,18 @@
1
+ # CLI program architecture
2
+
3
+ The program module owns Commander composition, not command effects or process lifecycle. It loads
4
+ the existing command definitions only when `buildProgram` runs, attaches the single shared Kernel
5
+ option set, and returns a fresh unparsed tree. Command modules own their actions; the binary owns
6
+ argv, telemetry, signal, error, and exit handling.
7
+
8
+ ```mermaid
9
+ flowchart LR
10
+ B[Binary entrypoint] --> P[buildProgram]
11
+ D[Command definitions] --> P
12
+ O[Shared Kernel options] --> P
13
+ P --> C[Fresh Commander tree]
14
+ C --> B
15
+ ```
16
+
17
+ The module is private to the CLI package. The exhaustive user-facing grammar remains the generated
18
+ Commander inventory rather than a duplicate declaration tree.
@@ -0,0 +1,29 @@
1
+ import { defineLaw } from '@astrale-os/spec/authoring'
2
+
3
+ export const CLI_PROGRAM_LEDGERED_SURFACE = defineLaw({
4
+ id: 'CLI-PROGRAM-LEDGERED-SURFACE',
5
+ statement:
6
+ 'Program composition exposes one exact root and nested command surface; metadata, options, aliases, visibility, Commander behavior, rendered help, and package-derived version change only through a deliberate compatibility-ledger entry.',
7
+ tests: [
8
+ {
9
+ file: '__tests__/program.test.ts',
10
+ id: 'TEST-CLI-PROGRAM-MATCHES-LEDGERED-SURFACE',
11
+ },
12
+ {
13
+ file: '__tests__/program.test.ts',
14
+ id: 'TEST-CLI-PROGRAM-VERSION-SINGLE-SOURCE',
15
+ },
16
+ ],
17
+ })
18
+
19
+ export const CLI_PROGRAM_FRESH_ROOT = defineLaw({
20
+ id: 'CLI-PROGRAM-FRESH-ROOT',
21
+ statement:
22
+ 'Each build produces an independent unparsed Commander root; mutation by one consumer cannot alter a later build.',
23
+ tests: [
24
+ {
25
+ file: '__tests__/program.test.ts',
26
+ id: 'TEST-CLI-PROGRAM-BUILDS-ISOLATED-ROOTS',
27
+ },
28
+ ],
29
+ })
@@ -0,0 +1,14 @@
1
+ import { defineLayout } from '@astrale-os/spec/authoring'
2
+
3
+ export default defineLayout({
4
+ entries: [
5
+ '__tests__/',
6
+ 'build.ts',
7
+ 'command.ts',
8
+ 'index.ts',
9
+ 'options.ts',
10
+ 'registry.ts',
11
+ 'tsconfig.json',
12
+ ],
13
+ exact: true,
14
+ })