@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,251 +1,125 @@
1
- import { rawOf } from '@astrale-os/kernel-client/graph'
1
+ import type { Node } from '@astrale-os/sdk/graph/node'
2
+ import type { QueryDirection, QueryResult } from '@astrale-os/sdk/query'
3
+
4
+ import { ClassPath } from '@astrale-os/sdk/graph/class'
2
5
  import chalk from 'chalk'
3
6
 
4
- import type { CommandDefinition } from '../command'
5
- import type { GraphNode, KernelCommandOpts, SelfExpansionMeta } from '../kernel'
7
+ import type { KernelCommandOpts } from '../connection'
6
8
  import type { ListProjection } from '../lib/output'
9
+ import type { CommandDefinition } from '../program/index'
7
10
 
8
- import {
9
- bindGraph,
10
- childrenCursor,
11
- expandSelfInPath,
12
- formatKernelError,
13
- runKernelCommand,
14
- splitRoot,
15
- withKernelClient,
16
- withSelfHint,
17
- } from '../kernel'
18
- import { spinner } from '../lib/log'
19
- import { isMachine, output, presentList } from '../lib/output'
11
+ import { expandSelfInPath, runKernelCommand, withSelfHint } from '../connection'
12
+ import { nodeProperty, prepareQuery } from '../graph/index'
13
+ import { log } from '../lib/log'
14
+ import { isMachine, presentList } from '../lib/output'
20
15
 
21
16
  type LsOpts = KernelCommandOpts & {
17
+ edge?: string
18
+ direction?: QueryDirection
19
+ limit?: string
20
+ cursor?: string
22
21
  long?: boolean
23
22
  quiet?: boolean
24
- recursive?: boolean
25
23
  count?: boolean
26
- filter?: string
27
- }
28
-
29
- /** A child node row — the shared `function.get` node shape. */
30
- type Item = GraphNode
31
-
32
- // ── Display projection ──────────────────────────────────────
33
-
34
- /** `/example.astrale.ai` → `example.astrale.ai`; `/` stays `/`. */
35
- export function basename(path?: string): string {
36
- if (!path || path === '/') return path ?? ''
37
- return path.slice(path.lastIndexOf('/') + 1)
38
- }
39
-
40
- /** `/:kernel.astrale.ai:class.Domain` → `Domain`; falls back to the most specific label. */
41
- export function classNameOf(item: { class?: string; __labels?: string[] }): string {
42
- const tail = item.class?.split(/[/:.]/).pop()
43
- return tail || item.__labels?.[item.__labels.length - 1] || '?'
44
24
  }
45
25
 
46
- /** The addressable path of a child (for `-q` / tree descent). */
47
- function itemPath(item: Item): string {
48
- return item.path ?? (item.id ? `@${item.id}` : '')
49
- }
50
-
51
- function lsProjection(items: Item[]): ListProjection {
52
- return {
53
- columns: [
54
- { key: 'name', header: 'NAME', color: chalk.cyan },
55
- { key: 'kind', header: 'KIND', color: chalk.dim },
56
- { key: 'id', header: 'ID', color: chalk.dim },
57
- ],
58
- rows: items.map((i) => ({ name: basename(i.path), kind: classNameOf(i), id: i.id ?? '' })),
59
- paths: items.map(itemPath),
26
+ export async function lsCommand(source: string, opts: LsOpts): Promise<void> {
27
+ if (opts.edge === undefined) {
28
+ log.error('ls requires --edge <class>; Kernel V2 has no universal child relation')
29
+ process.exit(1)
60
30
  }
61
- }
62
31
 
63
- function applyFilter(items: Item[], filter: string | undefined): Item[] {
64
- if (!filter) return items
65
- const f = filter.toLowerCase()
66
- return items.filter((i) => {
67
- const kindMatch = classNameOf(i).toLowerCase() === f
68
- const labelMatch = i.__labels?.some((l) => l.toLowerCase() === f) ?? false
69
- return kindMatch || labelMatch
70
- })
71
- }
72
-
73
- // ── Command ─────────────────────────────────────────────────
74
-
75
- export async function lsCommand(path: string, opts: LsOpts): Promise<void> {
76
- let expandedPath: string
32
+ let path: string
77
33
  let meta
34
+ let prepared
78
35
  try {
79
- ;({ path: expandedPath, meta } = await expandSelfInPath(path, opts))
80
- } catch (e) {
81
- process.stderr.write((e instanceof Error ? e.message : 'Invalid @self expansion') + '\n')
36
+ ;({ path, meta } = await expandSelfInPath(source, opts))
37
+ prepared = prepareQuery({
38
+ sources: [path],
39
+ edge: opts.edge,
40
+ direction: opts.direction,
41
+ limit: opts.limit,
42
+ cursor: opts.cursor,
43
+ })
44
+ } catch (error) {
45
+ log.error(error instanceof Error ? error.message : 'Invalid edge neighborhood')
82
46
  process.exit(1)
83
47
  }
84
48
 
85
- if (opts.recursive) {
86
- return recursiveLs(expandedPath, opts, meta)
87
- }
88
-
89
49
  await runKernelCommand({
90
50
  opts,
91
- label: `Children of ${expandedPath}`,
92
- // Flat listing = ONE function.get depth:1; the root is dropped, the rest are
93
- // the direct children (replaces the removed `::listChildren` syscall).
94
- fn: async (ctx) => {
95
- const result = await withSelfHint(
96
- () => bindGraph(ctx).query((q) => q.from(expandedPath).children()),
97
- meta,
98
- )
99
- return {
100
- children: splitRoot(result.wire.nodes, expandedPath).children,
101
- next: childrenCursor(result.wire),
102
- }
103
- },
104
- format: ({ children, next }, fmtOpts) => {
105
- const items = applyFilter(children, opts.filter)
51
+ label: `Neighbors of ${path}`,
52
+ fn: ({ graph }) => withSelfHint(() => graph.query(prepared.ast, { page: prepared.page }), meta),
53
+ format: (response, format) => {
54
+ const graph = completeGraph(response.result)
106
55
  presentList(
107
- items,
108
- { ...fmtOpts, quiet: opts.quiet, count: opts.count, long: opts.long },
109
- lsProjection,
56
+ [...graph.nodes],
57
+ { ...format, long: opts.long, quiet: opts.quiet, count: opts.count },
58
+ listProjection,
110
59
  )
111
- if (next && !isMachine(fmtOpts) && !opts.quiet && !opts.count) {
112
- process.stdout.write(
113
- chalk.dim(
114
- ' more children truncated - page with `query ' +
115
- expandedPath +
116
- ' --depth 1 --children \'{"cursor":"' +
117
- next +
118
- '"}\'`\n',
119
- ),
120
- )
60
+ if (response.page.next && !isMachine(format) && !opts.quiet && !opts.count) {
61
+ process.stderr.write(` cursor: ${response.page.next}\n`)
121
62
  }
122
63
  },
123
64
  })
124
65
  }
125
66
 
126
- // ── Recursive tree ──────────────────────────────────────────
127
-
128
- const MAX_DEPTH = 5
129
-
130
- type TreeNode = Item & { children: TreeNode[] }
131
-
132
- async function recursiveLs(
133
- path: string,
134
- opts: LsOpts,
135
- meta: SelfExpansionMeta | undefined,
136
- ): Promise<void> {
137
- const machine = isMachine(opts)
138
- const spin = !machine ? spinner(`Listing ${path} recursively...`) : null
139
-
140
- try {
141
- await withKernelClient(opts, async (ctx) => {
142
- // The recursive walk is now ONE function.get to the depth cap; the tree is
143
- // reassembled client-side from the flat node page (was an N+1 buildTree).
144
- const result = await withSelfHint(
145
- () => bindGraph(ctx).query((q) => q.from(path).descend(MAX_DEPTH)),
146
- meta,
147
- )
148
- const tree = buildTree(result.wire.nodes, path)
149
- spin?.succeed(`Tree of ${path}`)
150
- if (!machine) console.log('')
151
-
152
- if (machine || opts.format) {
153
- output(tree, opts)
154
- } else if (opts.quiet) {
155
- printTreeQuiet(tree)
156
- } else {
157
- printTree(tree, '')
158
- }
159
- })
160
- } catch (error) {
161
- if (!machine && spin) spin.fail('Failed')
162
- await formatKernelError(error, machine, undefined, opts.debug, { credential: opts.creds })
163
- process.exit(1)
164
- }
67
+ function completeGraph(result: QueryResult) {
68
+ if (result.kind !== 'graph') throw new TypeError('ls expected one complete graph Query result')
69
+ return result.graph
165
70
  }
166
71
 
167
- /** Parent absolute path of an absolute node path (`/a/b` `/a`; `/a` → `/`). */
168
- function parentPathOf(path: string): string {
169
- const i = path.lastIndexOf('/')
170
- return i <= 0 ? '/' : path.slice(0, i)
171
- }
172
-
173
- /** Reassemble the flat `function.get` node page into a tree rooted at `rootPath`. */
174
- function buildTree(nodes: readonly GraphNode[], rootPath: string): TreeNode[] {
175
- const rootRaw = rawOf(rootPath)
176
- const byPath = new Map<string, TreeNode>()
177
- for (const n of nodes) {
178
- const raw = rawOf(n.path)
179
- if (raw === rootRaw) continue
180
- byPath.set(raw, { ...n, children: [] })
181
- }
182
-
183
- const roots: TreeNode[] = []
184
- for (const [raw, node] of byPath) {
185
- const parent = parentPathOf(raw)
186
- const parentNode = parent === rootRaw ? undefined : byPath.get(parent)
187
- if (parentNode) parentNode.children.push(node)
188
- else roots.push(node)
189
- }
190
- return roots
191
- }
192
-
193
- function printTree(nodes: TreeNode[], prefix: string): void {
194
- for (let i = 0; i < nodes.length; i++) {
195
- const node = nodes[i]
196
- const isLast = i === nodes.length - 1
197
- const connector = isLast ? '└── ' : '├── '
198
- const childPrefix = isLast ? ' ' : '│ '
199
-
200
- const name = basename(node.path) || node.id || '?'
201
- console.log(`${prefix}${connector}${chalk.cyan(name)} ${chalk.dim(classNameOf(node))}`)
202
-
203
- if (node.children.length > 0) {
204
- printTree(node.children, prefix + childPrefix)
205
- }
72
+ export function listProjection(nodes: Node[]): ListProjection {
73
+ return {
74
+ columns: [
75
+ { key: 'name', header: 'NAME', color: chalk.cyan },
76
+ { key: 'class', header: 'CLASS', color: chalk.dim },
77
+ { key: 'id', header: 'ID', color: chalk.dim },
78
+ ],
79
+ rows: nodes.map((node) => ({
80
+ name: displayName(node),
81
+ class: ClassPath.name(node.class),
82
+ id: node.id,
83
+ })),
84
+ paths: nodes.map((node) => `@${node.id}`),
206
85
  }
207
86
  }
208
87
 
209
- function printTreeQuiet(nodes: TreeNode[]): void {
210
- for (const node of nodes) {
211
- process.stdout.write(itemPath(node) + '\n')
212
- if (node.children.length > 0) printTreeQuiet(node.children)
213
- }
88
+ export function displayName(node: Node): string {
89
+ const value =
90
+ nodeProperty(node, 'name') ?? nodeProperty(node, 'title') ?? nodeProperty(node, 'slug')
91
+ return typeof value === 'string' && value.length > 0 ? value : `@${node.id}`
214
92
  }
215
93
 
216
94
  export default {
217
95
  name: 'ls',
218
- description: 'List children of a node',
96
+ description: 'List one finite exact edge neighborhood',
219
97
  afterHelpText: `
220
98
  Behavior:
221
- Default output is a NAME/KIND/ID table on a TTY, JSON when piped. One
222
- function.get depth:1 fetches the direct children; -R fetches the whole
223
- subtree (to depth ${MAX_DEPTH}) in a SINGLE call and renders it as a tree.
224
- --filter matches a child KIND or label (post-filter): Folder, Function,
225
- Domain. At a domain's tree position the children are Folder nodes
226
- (class.X), not Class — so --filter Class returns nothing; use --filter
227
- Folder, or descend into class.<X>. -q prints one absolute path per line
228
- (pipeable).
99
+ Lists Nodes reached from one source through one exact Edge Class. Kernel V2
100
+ has no universal parent/child relation, so --edge is required and recursive
101
+ tree walking is intentionally absent. Direction defaults to outgoing and the
102
+ finite default limit is 100. -q emits one @id per line.
229
103
 
230
104
  Examples:
231
- $ astrale ls /
232
- $ astrale ls /kernel.astrale.ai --filter Folder
233
- $ astrale ls / -q | xargs -I{} astrale describe {}
105
+ $ astrale ls @note --edge /:notes.example.dev:class.references
106
+ $ astrale ls @note --edge /:notes.example.dev:class.references --direction incoming --limit 25
234
107
  `,
235
- arguments: [
236
- { name: 'path', description: 'Node path (/domain/Class) or ID (@nodeId)', required: false },
237
- ],
108
+ arguments: [{ name: 'source', description: 'Canonical source Path or @id' }],
238
109
  options: [
239
- { flags: '-l, --long', description: 'Full node dump (default: compact)' },
240
- { flags: '-q, --quiet', description: 'One path per line (unix-pipeable)' },
241
- { flags: '-R, --recursive', description: 'List recursively (tree view)' },
242
- { flags: '--count', description: 'Print only the number of children' },
110
+ { flags: '--edge <class>', description: 'Exact Edge Class to traverse' },
243
111
  {
244
- flags: '--filter <kind>',
245
- description: 'Filter children by kind or label (e.g., Folder, Function, Domain)',
112
+ flags: '--direction <direction>',
113
+ description: 'Traversal direction (default: outgoing)',
114
+ choices: ['outgoing', 'incoming', 'incident'],
246
115
  },
116
+ { flags: '--limit <n>', description: 'Finite selected Node limit (default: 100)' },
117
+ { flags: '--cursor <token>', description: 'Resume one live query scope' },
118
+ { flags: '-l, --long', description: 'Print complete canonical Nodes' },
119
+ { flags: '-q, --quiet', description: 'Print one @id per line' },
120
+ { flags: '--count', description: 'Print only the number of returned Nodes' },
247
121
  ],
248
- action: async (path, opts) => {
249
- await lsCommand((path as string | undefined) ?? '/', opts as LsOpts)
122
+ action: async (source, opts) => {
123
+ await lsCommand(source as string, opts as LsOpts)
250
124
  },
251
125
  } satisfies CommandDefinition
@@ -1,189 +1,120 @@
1
- import type { PatchInput } from '@astrale-os/kernel-client/graph'
1
+ import type { MutationResult } from '@astrale-os/sdk/mutation'
2
2
 
3
- import { patchDataSchema } from '@astrale-os/kernel-core'
4
3
  import chalk from 'chalk'
5
4
  import { readFile } from 'node:fs/promises'
6
5
 
7
- import type { CommandDefinition } from '../command'
8
- import type { KernelCommandOpts } from '../kernel'
9
- import type { MutationResultWire } from '../kernel'
6
+ import type { KernelCommandOpts } from '../connection'
7
+ import type { CommandDefinition } from '../program/index'
10
8
 
11
- import { bindGraph, runKernelCommand } from '../kernel'
9
+ import { runKernelCommand } from '../connection'
10
+ import { prepareMutation } from '../graph/index'
12
11
  import { log } from '../lib/log'
13
12
  import { output } from '../lib/output'
14
13
  import { renderTable } from '../lib/table'
15
14
 
16
- type MutateOpts = KernelCommandOpts & { data?: string; file?: string; dry?: boolean }
15
+ type MutateOpts = KernelCommandOpts & {
16
+ data?: string
17
+ file?: string
18
+ dry?: boolean
19
+ }
17
20
 
18
21
  export async function mutateCommand(opts: MutateOpts): Promise<void> {
19
- let raw: unknown
22
+ let mutation
20
23
  try {
21
- raw = await readPatch(opts)
22
- } catch (e) {
23
- log.error(e instanceof Error ? e.message : 'Invalid patch')
24
+ mutation = prepareMutation(await readDocument(opts))
25
+ } catch (error) {
26
+ log.error(error instanceof Error ? error.message : 'Invalid Mutation V3 document')
24
27
  process.exit(1)
25
- return
26
28
  }
27
29
 
28
- // --dry: validate the patch locally against the kernel's own schema and print
29
- // the normalized form (every arm defaulted to []). No kernel round-trip.
30
30
  if (opts.dry) {
31
- const parsed = patchDataSchema.safeParse(raw)
32
- if (!parsed.success) {
33
- log.error('Patch failed local validation:')
34
- for (const issue of parsed.error.issues) {
35
- log.dim(` ${issue.path.join('.') || '(root)'}: ${issue.message}`)
36
- }
37
- process.exit(1)
38
- }
39
- output(parsed.data, opts)
31
+ output(mutation, opts)
40
32
  return
41
33
  }
42
34
 
43
- await runKernelCommand<MutationResultWire>({
35
+ await runKernelCommand<MutationResult>({
44
36
  opts,
45
37
  label: 'Mutate',
46
- fn: (ctx) => bindGraph(ctx).mutate(raw as PatchInput),
47
- format: (result, fmtOpts, isRaw) => {
48
- if (isRaw) {
49
- output(result, fmtOpts)
50
- return
51
- }
52
- printResult(result)
38
+ fn: ({ graph }) => graph.mutate(mutation),
39
+ format: (result, format, machine) => {
40
+ if (machine || format.format !== undefined) output(result, format)
41
+ else printResult(result)
53
42
  },
54
43
  })
55
44
  }
56
45
 
57
- /** Patch source ladder (highest wins): --data > --file > stdin. */
58
- async function readPatch(opts: MutateOpts): Promise<unknown> {
59
- if (opts.data) {
46
+ async function readDocument(opts: MutateOpts): Promise<unknown> {
47
+ const authored = [opts.data !== undefined, opts.file !== undefined].filter(Boolean).length
48
+ if (authored > 1) throw new TypeError('--data and --file are mutually exclusive')
49
+ if (opts.data !== undefined) return parseJson(opts.data, '--data')
50
+ if (opts.file !== undefined) {
51
+ let raw: string
60
52
  try {
61
- return JSON.parse(opts.data)
62
- } catch {
63
- throw new Error(`Invalid JSON in --data: ${opts.data}`)
53
+ raw = await readFile(opts.file, 'utf8')
54
+ } catch (error) {
55
+ throw new Error(
56
+ `Cannot read --file ${opts.file}: ${error instanceof Error ? error.message : error}`,
57
+ )
64
58
  }
59
+ return parseJson(raw, opts.file)
65
60
  }
66
- if (opts.file) {
67
- let text: string
68
- try {
69
- text = await readFile(opts.file, 'utf-8')
70
- } catch (e) {
71
- throw new Error(`Cannot read --file ${opts.file}: ${e instanceof Error ? e.message : e}`)
72
- }
73
- try {
74
- return JSON.parse(text)
75
- } catch {
76
- throw new Error(`Invalid JSON in ${opts.file}`)
77
- }
61
+ if (process.stdin.isTTY) {
62
+ throw new TypeError('No mutation provided; use --data, --file, or stdin')
78
63
  }
79
- const stdin = await readStdin()
80
- if (stdin) {
81
- try {
82
- return JSON.parse(stdin)
83
- } catch {
84
- throw new Error('Invalid JSON from stdin')
85
- }
86
- }
87
- throw new Error(
88
- 'No patch provided — pass --data <json>, --file <path>, or pipe a PatchData JSON on stdin',
89
- )
64
+ const chunks: Buffer[] = []
65
+ for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk))
66
+ const raw = Buffer.concat(chunks).toString('utf8').trim()
67
+ if (raw.length === 0) throw new TypeError('No mutation provided on stdin')
68
+ return parseJson(raw, 'stdin')
90
69
  }
91
70
 
92
- async function readStdin(): Promise<string | null> {
93
- if (process.stdin.isTTY) return null
94
- const chunks: Buffer[] = []
95
- for await (const chunk of process.stdin) {
96
- chunks.push(chunk as Buffer)
71
+ function parseJson(raw: string, source: string): unknown {
72
+ try {
73
+ return JSON.parse(raw)
74
+ } catch {
75
+ throw new TypeError(`${source} must contain valid JSON`)
97
76
  }
98
- const text = Buffer.concat(chunks).toString('utf-8').trim()
99
- return text || null
100
77
  }
101
78
 
102
- function printResult(result: MutationResultWire): void {
103
- const nodeRows = Object.entries(result.createdNodes ?? {})
104
- const edgeRows = Object.entries(result.createdEdges ?? {})
105
-
106
- if (nodeRows.length === 0 && edgeRows.length === 0) {
107
- console.log(chalk.dim(' applied — no nodes or edges minted (updates/deletes only)'))
79
+ function printResult(result: MutationResult): void {
80
+ const rows = Object.entries(result.createdNodes).map(([binding, id]) => ({ binding, id }))
81
+ if (rows.length === 0) {
82
+ console.log(chalk.dim(' applied — no Nodes created'))
108
83
  return
109
84
  }
110
-
111
- if (nodeRows.length > 0) {
112
- console.log(` ${chalk.bold('Created nodes:')}`)
113
- console.log(
114
- renderTable(
115
- nodeRows.map(([at, id]) => ({ at, id })),
116
- {
117
- columns: [
118
- { key: 'at', header: 'AT', color: chalk.cyan },
119
- { key: 'id', header: 'ID', color: chalk.dim },
120
- ],
121
- showHeader: true,
122
- },
123
- ),
124
- )
125
- console.log('')
126
- }
127
-
128
- if (edgeRows.length > 0) {
129
- console.log(` ${chalk.bold('Created edges:')}`)
130
- console.log(
131
- renderTable(
132
- edgeRows.map(([tuple, id]) => ({ tuple, id })),
133
- {
134
- columns: [
135
- { key: 'tuple', header: 'CLASS|SOURCE|SLUG|TARGET', color: chalk.cyan },
136
- { key: 'id', header: 'ID', color: chalk.dim },
137
- ],
138
- showHeader: true,
139
- },
140
- ),
141
- )
142
- console.log('')
143
- }
85
+ console.log(` ${chalk.bold('Created nodes:')}`)
86
+ console.log(
87
+ renderTable(rows, {
88
+ columns: [
89
+ { key: 'binding', header: 'BINDING', color: chalk.cyan },
90
+ { key: 'id', header: 'ID', color: chalk.dim },
91
+ ],
92
+ showHeader: true,
93
+ }),
94
+ )
144
95
  }
145
96
 
146
97
  export default {
147
98
  name: 'mutate',
148
- description: 'Apply a batch graph write (create/update/delete nodes & edges) via function.mutate',
99
+ description: 'Apply one atomic Mutation V3 graph transition',
149
100
  afterHelpText: `
150
101
  Behavior:
151
- Sends a PatchData patch through the kernel's function.mutate door — a
152
- single all-or-nothing write. Patch source (highest wins): --data > --file
153
- > stdin. Prints the minted id maps (createdNodes: at→id, createdEdges:
154
- class|source|slug|target id); --json emits the raw MutationResult.
155
-
156
- Authorization is per-arm: a create needs USE on the class and EDIT on the
157
- parent, an update/delete needs EDIT on the target. A denied arm fails the
158
- whole patch. --dry validates the patch locally (kernel patchDataSchema) and
159
- prints the normalized form without touching the kernel.
102
+ Accepts a canonical astrale.graph.mutation/v3 document or its exact
103
+ { preconditions, operations } authoring input. Source is --data, --file,
104
+ or stdin. --dry admits and prints the canonical document without opening a
105
+ Kernel connection. The result contains the real createdNodes binding map.
160
106
 
161
- PatchData shape:
162
- {
163
- "nodes": {
164
- "create": [{ "class": "/:d:class.X", "at": "/d/x", "props": {} }],
165
- "update": [{ "class": "/:d:class.X", "path": "/d/x", "props": {} }],
166
- "delete": [{ "class": "/:d:class.X", "path": "/d/x" }]
167
- },
168
- "edges": {
169
- "create": [{ "class": "/:d:class.e", "source": "/a", "target": "/b", "props": {} }],
170
- "delete": [{ "class": "/:d:class.e", "source": "/a", "target": "/b" }]
171
- }
172
- }
107
+ Legacy PatchData nodes/edges arms and createdEdges IDs are not part of
108
+ Mutation V3 and are rejected rather than approximated.
173
109
 
174
110
  Examples:
175
- $ astrale mutate --data '{"nodes":{"create":[{"class":"/:blog.acme.com:class.Author","at":"/blog.acme.com/authors/ada","props":{}}]}}'
176
- $ astrale mutate --file patch.json
177
- $ echo '{"nodes":{"delete":[{"class":"/:d:class.X","path":"/d/x"}]}}' | astrale mutate
178
- $ astrale mutate --file patch.json --dry
111
+ $ astrale mutate --file mutation.v3.json
112
+ $ astrale mutate --data '{"preconditions":[],"operations":[]}' --dry
179
113
  `,
180
114
  options: [
181
- { flags: '-d, --data <json>', description: 'PatchData as a JSON string' },
182
- { flags: '-f, --file <path>', description: 'Read PatchData JSON from a file' },
183
- {
184
- flags: '--dry',
185
- description: 'Validate locally (no kernel call) and print the normalized patch',
186
- },
115
+ { flags: '-d, --data <json>', description: 'Mutation V3 JSON document' },
116
+ { flags: '-f, --file <path>', description: 'Read a Mutation V3 document from a file' },
117
+ { flags: '--dry', description: 'Admit and print canonical Mutation V3 without dispatch' },
187
118
  ],
188
119
  action: async (opts) => {
189
120
  await mutateCommand(opts as MutateOpts)