@astrale-os/cli 0.8.1-alpha.0 → 0.8.1-alpha.3

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 (314) hide show
  1. package/dist/astrale.js +63133 -41267
  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-describe.test.ts +67 -0
  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__/install-direct.test.ts +20 -0
  70. package/src/commands/__tests__/instance-list-admin.test.ts +26 -0
  71. package/src/commands/__tests__/logs.test.ts +71 -96
  72. package/src/commands/__tests__/ls.test.ts +21 -17
  73. package/src/commands/__tests__/read-commands.test.ts +171 -101
  74. package/src/commands/__tests__/view.test.ts +100 -51
  75. package/src/commands/admin/status.ts +3 -3
  76. package/src/commands/admin/use.ts +29 -6
  77. package/src/commands/auth/login.ts +1 -1
  78. package/src/commands/auth/logout.ts +4 -2
  79. package/src/commands/auth/status.ts +2 -2
  80. package/src/commands/auth/token.ts +2 -2
  81. package/src/commands/browser.ts +1 -1
  82. package/src/commands/call-describe.ts +131 -0
  83. package/src/commands/call.ts +69 -109
  84. package/src/commands/describe.ts +52 -147
  85. package/src/commands/domain/install.ts +106 -49
  86. package/src/commands/domain/list.ts +4 -8
  87. package/src/commands/domain/publish.ts +9 -31
  88. package/src/commands/get.ts +33 -66
  89. package/src/commands/identity/__tests__/register.test.ts +93 -0
  90. package/src/commands/identity/create.ts +2 -2
  91. package/src/commands/identity/delete.ts +2 -2
  92. package/src/commands/identity/export.ts +7 -33
  93. package/src/commands/identity/import.ts +21 -70
  94. package/src/commands/identity/list.ts +2 -2
  95. package/src/commands/identity/register.ts +148 -117
  96. package/src/commands/identity/sync.ts +2 -2
  97. package/src/commands/identity/unsync.ts +2 -2
  98. package/src/commands/identity/use.ts +2 -2
  99. package/src/commands/identity/whoami.ts +2 -2
  100. package/src/commands/idp/add.ts +1 -1
  101. package/src/commands/idp/list.ts +1 -1
  102. package/src/commands/idp/refresh.ts +1 -1
  103. package/src/commands/idp/remove.ts +2 -2
  104. package/src/commands/idp/show.ts +1 -1
  105. package/src/commands/instance/active.ts +2 -2
  106. package/src/commands/instance/bookmark.ts +8 -2
  107. package/src/commands/instance/create.ts +1 -1
  108. package/src/commands/instance/delete.ts +4 -10
  109. package/src/commands/instance/forget.ts +1 -1
  110. package/src/commands/instance/list.ts +39 -6
  111. package/src/commands/instance/status.ts +5 -7
  112. package/src/commands/instance/use.ts +3 -3
  113. package/src/commands/logs.ts +171 -278
  114. package/src/commands/ls.ts +78 -204
  115. package/src/commands/mutate.ts +70 -139
  116. package/src/commands/query.ts +100 -278
  117. package/src/commands/session/analyze.ts +1 -1
  118. package/src/commands/session/list.ts +1 -1
  119. package/src/commands/setup.ts +1 -1
  120. package/src/commands/status.ts +1 -1
  121. package/src/commands/studio.ts +1 -1
  122. package/src/commands/token.ts +26 -21
  123. package/src/commands/update.ts +1 -1
  124. package/src/commands/use.ts +2 -3
  125. package/src/commands/view-serve.ts +1 -1
  126. package/src/commands/view.ts +67 -109
  127. package/src/connect-core.test.ts +38 -0
  128. package/src/connect-core.ts +42 -0
  129. package/src/connection/.spec/api.d.ts +111 -0
  130. package/src/connection/.spec/architecture.md +41 -0
  131. package/src/connection/.spec/capabilities/connection.ts +13 -0
  132. package/src/connection/.spec/examples/call.ts +7 -0
  133. package/src/connection/.spec/flows/command.ts +20 -0
  134. package/src/connection/.spec/flows/session.ts +61 -0
  135. package/src/connection/.spec/laws/connection.ts +181 -0
  136. package/src/connection/.spec/layout.ts +19 -0
  137. package/src/{kernel → connection}/__tests__/auth.test.ts +26 -2
  138. package/src/connection/__tests__/ca-fetch.test.ts +53 -0
  139. package/src/connection/__tests__/call.test.ts +17 -0
  140. package/src/connection/__tests__/credential.test.ts +109 -0
  141. package/src/connection/__tests__/errors.test.ts +96 -0
  142. package/src/connection/__tests__/exchange.test.ts +224 -0
  143. package/src/connection/__tests__/fixtures/localhost-cert.pem +19 -0
  144. package/src/connection/__tests__/fixtures/localhost-key.base64 +1 -0
  145. package/src/connection/__tests__/managed.test.ts +39 -0
  146. package/src/connection/__tests__/self.test.ts +51 -0
  147. package/src/connection/__tests__/session.test.ts +140 -0
  148. package/src/connection/__tests__/target.test.ts +93 -0
  149. package/src/{kernel → connection}/auth.ts +8 -8
  150. package/src/connection/call.ts +7 -0
  151. package/src/{kernel/run.ts → connection/command.ts} +22 -13
  152. package/src/connection/credential.ts +114 -0
  153. package/src/{kernel → connection}/errors.ts +32 -9
  154. package/src/connection/exchange.ts +274 -0
  155. package/src/connection/index.ts +8 -0
  156. package/src/connection/self.ts +120 -0
  157. package/src/connection/session.ts +176 -0
  158. package/src/connection/target.ts +111 -0
  159. package/src/connection/tsconfig.json +6 -0
  160. package/src/graph/.spec/api.d.ts +34 -0
  161. package/src/graph/.spec/laws/documents.ts +24 -0
  162. package/src/graph/.spec/laws/projection.ts +13 -0
  163. package/src/graph/.spec/layout.ts +6 -0
  164. package/src/graph/__tests__/mutation.test.ts +45 -0
  165. package/src/graph/__tests__/projection.test.ts +22 -0
  166. package/src/graph/__tests__/query.test.ts +154 -0
  167. package/src/graph/index.ts +3 -0
  168. package/src/graph/mutation.ts +33 -0
  169. package/src/graph/projection.ts +21 -0
  170. package/src/graph/query.ts +108 -0
  171. package/src/graph/tsconfig.json +6 -0
  172. package/src/identity/.history/ard/bootstrap-root-transfer.md +57 -0
  173. package/src/identity/.history/ard/domain-mediated-registration.md +36 -0
  174. package/src/identity/.spec/api.d.ts +124 -0
  175. package/src/identity/.spec/architecture.md +33 -0
  176. package/src/identity/.spec/capabilities/identity.ts +7 -0
  177. package/src/identity/.spec/flows/bootstrap-root.ts +79 -0
  178. package/src/identity/.spec/flows/domain-mediated-registration.ts +51 -0
  179. package/src/identity/.spec/flows/import.ts +29 -0
  180. package/src/identity/.spec/laws/identity.ts +56 -0
  181. package/src/identity/.spec/layout.ts +13 -0
  182. package/src/identity/.spec/schemas/identity-export-v1.schema.json +25 -0
  183. package/src/identity/__tests__/fixtures/registry-journey.ts +53 -0
  184. package/src/identity/__tests__/registration.test.ts +64 -0
  185. package/src/identity/__tests__/registry.test.ts +70 -0
  186. package/src/identity/__tests__/transfer.test.ts +198 -0
  187. package/src/identity/index.ts +23 -0
  188. package/src/identity/registration.ts +72 -0
  189. package/src/identity/registry.ts +172 -0
  190. package/src/identity/transfer.ts +187 -0
  191. package/src/identity/tsconfig.json +6 -0
  192. package/src/index.ts +1 -0
  193. package/src/keys/.spec/api.d.ts +74 -0
  194. package/src/keys/.spec/architecture.md +13 -0
  195. package/src/keys/.spec/laws/keys.ts +43 -0
  196. package/src/keys/.spec/layout.ts +6 -0
  197. package/src/keys/__tests__/algorithm.test.ts +39 -0
  198. package/src/keys/__tests__/keys.test.ts +168 -0
  199. package/src/keys/algorithm.ts +33 -0
  200. package/src/keys/credential.ts +112 -0
  201. package/src/keys/index.ts +14 -0
  202. package/src/keys/pair.ts +219 -0
  203. package/src/keys/tsconfig.json +6 -0
  204. package/src/lib/__tests__/admin-target.test.ts +67 -7
  205. package/src/lib/__tests__/binary.test.ts +2 -3
  206. package/src/lib/__tests__/command-dx.test.ts +2 -2
  207. package/src/lib/__tests__/config.test.ts +3 -2
  208. package/src/lib/__tests__/idp.test.ts +4 -6
  209. package/src/lib/__tests__/instance-target.test.ts +7 -4
  210. package/src/lib/__tests__/local-status.test.ts +1 -1
  211. package/src/lib/__tests__/log.test.ts +31 -0
  212. package/src/lib/__tests__/meta.test.ts +55 -0
  213. package/src/lib/__tests__/sdk-deps.test.ts +1 -1
  214. package/src/lib/__tests__/self.test.ts +8 -230
  215. package/src/lib/__tests__/use-target.test.ts +1 -1
  216. package/src/lib/__tests__/view-open-intent.test.ts +78 -104
  217. package/src/lib/__tests__/view-session.test.ts +99 -0
  218. package/src/lib/admin-domain.ts +39 -25
  219. package/src/lib/admin-instance.ts +38 -43
  220. package/src/lib/admin-target.ts +67 -13
  221. package/src/lib/binary.ts +13 -34
  222. package/src/lib/browser.ts +1 -1
  223. package/src/{kernel → lib}/ca-fetch.ts +35 -27
  224. package/src/lib/config.ts +1 -1
  225. package/src/lib/idp-session.ts +1 -1
  226. package/src/lib/idp.ts +3 -8
  227. package/src/lib/instance-target.ts +12 -8
  228. package/src/lib/instance.ts +34 -2
  229. package/src/lib/local-status.ts +2 -2
  230. package/src/lib/log.ts +3 -1
  231. package/src/lib/login-flow.ts +32 -5
  232. package/src/lib/meta.ts +15 -11
  233. package/src/lib/provision-instance.ts +4 -12
  234. package/src/lib/self.ts +5 -147
  235. package/src/lib/update.ts +1 -1
  236. package/src/lib/use-target.ts +1 -1
  237. package/src/lib/validation.ts +2 -2
  238. package/src/lib/view/open-intent.ts +7 -37
  239. package/src/lib/view/port-allocation.ts +1 -1
  240. package/src/lib/view/resolve.ts +52 -45
  241. package/src/lib/view/server.ts +16 -33
  242. package/src/lib/view/session.ts +51 -22
  243. package/src/paths/.spec/api.d.ts +13 -0
  244. package/src/paths/.spec/architecture.md +5 -0
  245. package/src/paths/.spec/layout.ts +3 -0
  246. package/src/paths/index.ts +13 -0
  247. package/src/program/.spec/api.d.ts +42 -0
  248. package/src/program/.spec/architecture.md +18 -0
  249. package/src/program/.spec/laws/program.ts +29 -0
  250. package/src/program/.spec/layout.ts +14 -0
  251. package/src/program/__tests__/program.test.ts +359 -0
  252. package/src/program/build.ts +190 -0
  253. package/src/program/command.ts +42 -0
  254. package/src/program/index.ts +2 -0
  255. package/src/program/options.ts +40 -0
  256. package/src/{registry.ts → program/registry.ts} +5 -1
  257. package/src/program/tsconfig.json +6 -0
  258. package/src/setup/steps/admin.ts +18 -3
  259. package/src/setup/steps/instance.ts +1 -1
  260. package/src/state/.spec/api.d.ts +133 -0
  261. package/src/state/.spec/architecture.md +31 -0
  262. package/src/state/.spec/capabilities/state.ts +7 -0
  263. package/src/state/.spec/flows/identity-update.ts +22 -0
  264. package/src/state/.spec/laws/state.ts +73 -0
  265. package/src/state/.spec/layout.ts +14 -0
  266. package/src/state/__tests__/exchange-credentials.test.ts +188 -0
  267. package/src/state/__tests__/files.test.ts +113 -0
  268. package/src/state/__tests__/fixtures/identity-transition.ts +37 -0
  269. package/src/state/__tests__/identities.test.ts +142 -0
  270. package/src/state/__tests__/paths.test.ts +27 -0
  271. package/src/state/exchange-credentials.ts +219 -0
  272. package/src/state/files.ts +129 -0
  273. package/src/state/identities.ts +236 -0
  274. package/src/state/index.ts +29 -0
  275. package/src/state/paths.ts +61 -0
  276. package/src/state/tsconfig.json +6 -0
  277. package/src/telemetry/__tests__/redact.test.ts +3 -3
  278. package/src/telemetry/__tests__/trigger.test.ts +1 -1
  279. package/src/telemetry/settings.ts +1 -1
  280. package/src/telemetry/store.ts +1 -1
  281. package/src/test-utils.ts +1 -1
  282. package/studio/client/dist/assets/{elk-api-DVw5WDTH.js → elk-api-D0cBetPW.js} +1 -1
  283. package/studio/client/dist/assets/index-Dspir4w7.js +81 -0
  284. package/studio/client/dist/assets/{index-Dl709Ra1.js → index-bVD2KJgz.js} +4 -4
  285. package/studio/client/dist/index.html +1 -1
  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/tsconfig.json +1 -1
  291. package/viewer/dist/main.js +65 -39
  292. package/src/command.ts +0 -42
  293. package/src/commands/__tests__/help-contract.test.ts +0 -164
  294. package/src/kernel/__tests__/client-owned-lookup.test.ts +0 -51
  295. package/src/kernel/__tests__/errors.test.ts +0 -43
  296. package/src/kernel/__tests__/expand.test.ts +0 -123
  297. package/src/kernel/client.ts +0 -214
  298. package/src/kernel/expand.ts +0 -192
  299. package/src/kernel/graph.ts +0 -96
  300. package/src/kernel/index.ts +0 -29
  301. package/src/kernel/options.ts +0 -22
  302. package/src/kernel/types.ts +0 -14
  303. package/src/lib/__tests__/domain-identity.test.ts +0 -60
  304. package/src/lib/__tests__/fs-atomic.test.ts +0 -104
  305. package/src/lib/__tests__/identity.test.ts +0 -79
  306. package/src/lib/__tests__/keys.test.ts +0 -129
  307. package/src/lib/domain-identity.ts +0 -49
  308. package/src/lib/env.ts +0 -49
  309. package/src/lib/fs-atomic.ts +0 -126
  310. package/src/lib/identity.ts +0 -256
  311. package/src/lib/keys.ts +0 -294
  312. package/src/lib/paths.ts +0 -11
  313. package/src/program.ts +0 -213
  314. package/studio/client/dist/assets/index-CTbkDp3z.js +0 -81
@@ -1,9 +1,10 @@
1
- import type { CommandDefinition } from '../../command'
1
+ import type { CommandDefinition } from '../../program/index'
2
2
 
3
- import { getDefault, readIdentities } from '../../lib/identity'
3
+ import { getDefault, readIdentities } from '../../identity/index'
4
4
  import { deleteIdpSession, listIdpSessions } from '../../lib/idp'
5
5
  import { log } from '../../lib/log'
6
6
  import { output, RAW_OUTPUT_OPTIONS } from '../../lib/output'
7
+ import { ExchangeCredentialCache } from '../../state/index'
7
8
 
8
9
  export default {
9
10
  name: 'logout',
@@ -28,6 +29,7 @@ export default {
28
29
  }
29
30
 
30
31
  for (const name of names) await deleteIdpSession(name)
32
+ await new ExchangeCredentialCache().clear()
31
33
 
32
34
  if (opts.raw || opts.json) {
33
35
  output({ cleared: names }, opts)
@@ -1,9 +1,9 @@
1
1
  import chalk from 'chalk'
2
2
 
3
- import type { CommandDefinition } from '../../command'
4
3
  import type { ListOpts, ListProjection } from '../../lib/output'
4
+ import type { CommandDefinition } from '../../program/index'
5
5
 
6
- import { readIdentities } from '../../lib/identity'
6
+ import { readIdentities } from '../../identity/index'
7
7
  import { isSessionExpired, listIdpSessions } from '../../lib/idp'
8
8
  import { log } from '../../lib/log'
9
9
  import { isMachine, presentList, RAW_OUTPUT_OPTIONS } from '../../lib/output'
@@ -1,6 +1,6 @@
1
- import type { CommandDefinition } from '../../command'
1
+ import type { CommandDefinition } from '../../program/index'
2
2
 
3
- import { getDefault, getIdentity, readIdentities } from '../../lib/identity'
3
+ import { getDefault, getIdentity, readIdentities } from '../../identity/index'
4
4
  import { isSessionExpired, readIdpSession, type IdpSession } from '../../lib/idp'
5
5
  import { ensureFreshSession } from '../../lib/idp-session'
6
6
  import { log } from '../../lib/log'
@@ -1,7 +1,7 @@
1
1
  import chalk from 'chalk'
2
2
  import { existsSync } from 'node:fs'
3
3
 
4
- import type { CommandDefinition } from '../command'
4
+ import type { CommandDefinition } from '../program/index'
5
5
 
6
6
  import {
7
7
  ab,
@@ -0,0 +1,131 @@
1
+ import type { Path } from '@astrale-os/sdk/graph/path'
2
+
3
+ import { AstraleError } from '../errors'
4
+
5
+ type JsonRecord = Record<string, unknown>
6
+
7
+ export interface CallableDescription {
8
+ readonly path: string
9
+ readonly origin: string
10
+ readonly method?: string
11
+ readonly function?: string
12
+ readonly class?: string
13
+ readonly dispatch?: 'static' | 'instance'
14
+ readonly description?: string
15
+ readonly auth?: unknown
16
+ readonly input?: unknown
17
+ readonly output?: unknown
18
+ readonly candidates?: readonly CallableDescription[]
19
+ }
20
+
21
+ /** Resolve one callable's input/output from an installed Domain schema document. */
22
+ export function describeCallableFromSchema(
23
+ path: Path,
24
+ schema: unknown,
25
+ ): CallableDescription | undefined {
26
+ if (path.ast.anchor.kind !== 'domain') return undefined
27
+ const origin = path.ast.anchor.origin
28
+ const last = path.ast.steps.at(-1)
29
+ const document = asRecord(schema)
30
+ if (document === undefined || last === undefined) return undefined
31
+
32
+ if (last.kind === 'method') {
33
+ const owner = path.ast.steps.at(-2)
34
+ if (
35
+ owner?.kind === 'projection' &&
36
+ (owner.projection.kind === 'class' || owner.projection.kind === 'interface')
37
+ ) {
38
+ const bag = asRecord(
39
+ owner.projection.kind === 'class' ? document.classes : document.interfaces,
40
+ )
41
+ const definition = asRecord(bag?.[owner.projection.name])
42
+ const method = asRecord(asRecord(definition?.methods)?.[last.name])
43
+ if (method !== undefined) {
44
+ return Object.freeze({
45
+ path: path.raw,
46
+ origin,
47
+ class: owner.projection.name,
48
+ method: last.name,
49
+ dispatch: last.dispatch,
50
+ ...callableFields(method),
51
+ })
52
+ }
53
+ }
54
+ const matches = findNamedMethods(document, origin, path.raw, last.name, last.dispatch)
55
+ if (matches.length === 1) return matches[0]
56
+ if (matches.length > 1) {
57
+ return Object.freeze({
58
+ path: path.raw,
59
+ origin,
60
+ method: last.name,
61
+ dispatch: last.dispatch,
62
+ candidates: Object.freeze(matches),
63
+ })
64
+ }
65
+ return undefined
66
+ }
67
+
68
+ if (last.kind === 'projection' && last.projection.kind === 'function') {
69
+ const fn = asRecord(asRecord(document.functions)?.[last.projection.name])
70
+ if (fn === undefined) return undefined
71
+ return Object.freeze({
72
+ path: path.raw,
73
+ origin,
74
+ function: last.projection.name,
75
+ ...callableFields(fn),
76
+ })
77
+ }
78
+ return undefined
79
+ }
80
+
81
+ export function missingCallableDescription(path: string): AstraleError {
82
+ return new AstraleError(
83
+ 'CALL_DESCRIBE_UNAVAILABLE',
84
+ `No callable schema is installed for ${path}.`,
85
+ 'Use a Domain-rooted Path such as /:host.astrale.ai:class.Manager:createInstance. Method Paths are not Function nodes.',
86
+ )
87
+ }
88
+
89
+ function findNamedMethods(
90
+ schema: JsonRecord,
91
+ origin: string,
92
+ path: string,
93
+ method: string,
94
+ dispatch: 'static' | 'instance',
95
+ ): CallableDescription[] {
96
+ const matches: CallableDescription[] = []
97
+ for (const bag of [schema.classes, schema.interfaces]) {
98
+ const definitions = asRecord(bag)
99
+ if (definitions === undefined) continue
100
+ for (const [className, definition] of Object.entries(definitions)) {
101
+ const methodDef = asRecord(asRecord(asRecord(definition)?.methods)?.[method])
102
+ if (methodDef === undefined) continue
103
+ matches.push(
104
+ Object.freeze({
105
+ path,
106
+ origin,
107
+ class: className,
108
+ method,
109
+ dispatch,
110
+ ...callableFields(methodDef),
111
+ }),
112
+ )
113
+ }
114
+ }
115
+ return matches
116
+ }
117
+
118
+ function callableFields(value: JsonRecord): Partial<CallableDescription> {
119
+ return {
120
+ ...(typeof value.description === 'string' ? { description: value.description } : {}),
121
+ ...(value.auth === undefined ? {} : { auth: value.auth }),
122
+ ...(value.input === undefined ? {} : { input: value.input }),
123
+ ...(value.output === undefined ? {} : { output: value.output }),
124
+ }
125
+ }
126
+
127
+ function asRecord(input: unknown): JsonRecord | undefined {
128
+ return input !== null && typeof input === 'object' && !Array.isArray(input)
129
+ ? (input as JsonRecord)
130
+ : undefined
131
+ }
@@ -1,59 +1,41 @@
1
- import { K } from '@astrale-os/kernel-core'
1
+ import { Path } from '@astrale-os/sdk/graph/path'
2
2
 
3
- import type { CommandDefinition } from '../command'
4
- import type { CallCommandOpts, ClientContext, SelfExpansionMeta } from '../kernel'
3
+ import type { ConnectionContext, KernelCommandOpts } from '../connection'
4
+ import type { CommandDefinition } from '../program/index'
5
5
 
6
- import {
7
- bindGraph,
8
- buildSelfContext,
9
- nodeProp,
10
- resolveSelfIdLazy,
11
- runKernelCommand,
12
- withSelfHint,
13
- } from '../kernel'
14
- import { presentBinary, type BinaryLike } from '../lib/binary'
6
+ import { createPathCall, expandSelfInCall, runKernelCommand, withSelfHint } from '../connection'
7
+ import { nodeProperty } from '../graph/index'
8
+ import { presentBinary } from '../lib/binary'
15
9
  import { log } from '../lib/log'
16
10
  import { output, present } from '../lib/output'
17
- import { containsSelfRef, expandSelfReferences } from '../lib/self'
11
+ import { describeCallableFromSchema, missingCallableDescription } from './call-describe'
18
12
 
19
- type CallOpts = CallCommandOpts & { describe?: boolean; dryRun?: boolean; output?: string }
13
+ type CallOpts = KernelCommandOpts & {
14
+ data?: string
15
+ describe?: boolean
16
+ dryRun?: boolean
17
+ output?: string
18
+ }
20
19
 
21
- /** A call resolves to either a JSON value or a binary response. */
22
- type CallResult = { kind: 'binary'; response: BinaryLike } | { kind: 'value'; value: unknown }
20
+ type CallResult = Awaited<ReturnType<ConnectionContext['session']['dispatch']>>
21
+ type MaterializedCallResult =
22
+ | Exclude<CallResult, { readonly kind: 'stream' }>
23
+ | { readonly kind: 'stream'; readonly values: readonly unknown[] }
23
24
 
24
25
  export async function callCommand(
25
26
  path: string,
26
27
  rawParams: string[],
27
28
  opts: CallOpts,
28
29
  ): Promise<void> {
29
- // Expand @self before describe/execute so refusals are typed and stale IdP
30
- // registrations can refresh through whoami.
31
- let expandedPath = path
32
- let expandedRaw = rawParams
33
- let selfMeta: SelfExpansionMeta | undefined
34
- const inputsHaveSelf = containsSelfRef(path) || rawParams.some(containsSelfRef)
35
- if (inputsHaveSelf) {
36
- try {
37
- const selfCtx = await buildSelfContext(opts)
38
- const selfId = await resolveSelfIdLazy(selfCtx, opts)
39
- expandedPath = expandSelfReferences(path, selfId)
40
- expandedRaw = rawParams.map((p) => expandSelfReferences(p, selfId))
41
- // Stale-registration hints apply when @self appears in either path or params.
42
- const rawMutated = expandedRaw.some((p, i) => p !== rawParams[i])
43
- if (expandedPath !== path || rawMutated) {
44
- selfMeta = {
45
- original: path,
46
- expanded: expandedPath,
47
- selfId,
48
- identity: selfCtx.identity?.name,
49
- slug: selfCtx.instanceSlug,
50
- }
51
- }
52
- } catch (e) {
53
- log.error(e instanceof Error ? e.message : 'Invalid @self expansion')
54
- process.exit(1)
55
- }
30
+ // Expand @self before describe/execute through authenticated Identity.whoami.
31
+ let expanded: Awaited<ReturnType<typeof expandSelfInCall>>
32
+ try {
33
+ expanded = await expandSelfInCall(path, rawParams, opts)
34
+ } catch (error) {
35
+ log.error(error instanceof Error ? error.message : 'Invalid @self expansion')
36
+ process.exit(1)
56
37
  }
38
+ const expandedPath = expanded.path
57
39
 
58
40
  // ── Describe mode: show schema without executing ────────
59
41
  // Runs AFTER expansion so `astrale call @self::m --describe` works.
@@ -64,7 +46,7 @@ export async function callCommand(
64
46
  // ── Parse params ────────────────────────────────────────
65
47
  let params: Record<string, unknown>
66
48
  try {
67
- params = await parseParams(expandedRaw, opts.data)
49
+ params = await parseParams([...expanded.parameters], opts.data)
68
50
  } catch (e) {
69
51
  log.error(e instanceof Error ? e.message : 'Invalid params')
70
52
  process.exit(1)
@@ -77,84 +59,59 @@ export async function callCommand(
77
59
  }
78
60
 
79
61
  // ── Execute ────────────────────────────────────────────
80
- await runKernelCommand<CallResult>({
62
+ await runKernelCommand<MaterializedCallResult>({
81
63
  opts,
82
64
  label: expandedPath,
83
- fn: async (ctx): Promise<CallResult> => {
84
- // Binary outputs must be routed before dispatch; JSON decoding would corrupt bytes.
85
- if (await isBinaryOutput(ctx, expandedPath)) {
86
- const response = await withSelfHint(() => ctx.client.binary(expandedPath, params), selfMeta)
87
- return { kind: 'binary', response }
88
- }
89
- const value = await withSelfHint(() => ctx.client.call(expandedPath, params), selfMeta)
90
- return { kind: 'value', value }
91
- },
65
+ fn: (ctx) =>
66
+ withSelfHint(
67
+ async () =>
68
+ materializeCallResult(await ctx.session.dispatch(createPathCall(expandedPath, params))),
69
+ expanded.meta,
70
+ ),
92
71
  format: async (result, fmtOpts) => {
93
- if (result.kind === 'binary') {
94
- await presentBinary(result.response, fmtOpts, { outFile: opts.output })
95
- return
72
+ switch (result.kind) {
73
+ case 'value':
74
+ present(result.value, fmtOpts)
75
+ return
76
+ case 'binary':
77
+ await presentBinary(result.value, fmtOpts, { outFile: opts.output })
78
+ return
79
+ case 'stream':
80
+ output(result.values, fmtOpts)
81
+ return
82
+ case 'redirect':
83
+ throw new Error('Client Session returned an unresolved redirect.')
96
84
  }
97
- present(result.value, fmtOpts)
98
85
  },
99
86
  })
100
87
  }
101
88
 
102
- /**
103
- * Best-effort binary preflight. Static paths read the Function node directly;
104
- * instance paths probe the class method node and fall back to value transport on miss.
105
- */
106
- async function isBinaryOutput(ctx: ClientContext, path: string): Promise<boolean> {
107
- try {
108
- const target = path.includes('::') ? await instanceMethodNodePath(ctx, path) : path
109
- if (!target) return false
110
- const node = await bindGraph(ctx).get(target)
111
- // Props travel under fully-qualified keys; `output.key` IS that key.
112
- return node?.props?.[K.$.i('Function').output.key] === 'binary'
113
- } catch {
114
- return false
115
- }
116
- }
117
-
118
- /** `<node>::method` → the method's Function-node MethodPath, via the node's class. */
119
- async function instanceMethodNodePath(
120
- ctx: ClientContext,
121
- path: string,
122
- ): Promise<string | undefined> {
123
- const sep = path.lastIndexOf('::')
124
- const source = path.slice(0, sep)
125
- const method = path.slice(sep + 2)
126
- if (!source || !method) return undefined
127
- const node = await bindGraph(ctx).get(source)
128
- // ClassPath + method name forms the class-owned Function MethodPath.
129
- return node?.class ? `${node.class.raw}:${method}` : undefined
89
+ /** Drain a session-backed stream before the command-scoped Client Session closes. */
90
+ export async function materializeCallResult(result: CallResult): Promise<MaterializedCallResult> {
91
+ if (result.kind !== 'stream') return result
92
+ const values: unknown[] = []
93
+ for await (const value of result.stream) values.push(value)
94
+ return Object.freeze({ kind: 'stream', values: Object.freeze(values) })
130
95
  }
131
96
 
132
97
  async function describeOperation(path: string, opts: CallOpts): Promise<void> {
133
98
  await runKernelCommand({
134
99
  opts,
135
100
  label: `Schema for ${path}`,
136
- // The Function node carries the schemas as props (function.get depth:0).
137
- fn: async (ctx) => await bindGraph(ctx).get(path),
138
- format: (node, fmtOpts) => {
139
- const input = nodeProp(node, 'inputSchema')
140
- const outputSchema = nodeProp(node, 'outputSchema')
141
- const schema: Record<string, unknown> = {}
142
- if (input) schema.input = tryParseJson(input)
143
- if (outputSchema) schema.output = tryParseJson(outputSchema)
144
- output(Object.keys(schema).length > 0 ? schema : node, fmtOpts)
101
+ fn: async ({ graph }) => {
102
+ const parsed = Path.parse(path)
103
+ if (parsed.ast.anchor.kind !== 'domain') {
104
+ throw missingCallableDescription(path)
105
+ }
106
+ const domain = await graph.getOrThrow(Path.parse(`/:${parsed.ast.anchor.origin}`))
107
+ const described = describeCallableFromSchema(parsed, nodeProperty(domain, 'schema'))
108
+ if (described === undefined) throw missingCallableDescription(path)
109
+ return described
145
110
  },
111
+ format: (schema, fmtOpts) => output(schema, fmtOpts),
146
112
  })
147
113
  }
148
114
 
149
- function tryParseJson(value: unknown): unknown {
150
- if (typeof value !== 'string') return value
151
- try {
152
- return JSON.parse(value)
153
- } catch {
154
- return value
155
- }
156
- }
157
-
158
115
  // ── Param parsing ───────────────────────────────────────────
159
116
 
160
117
  export async function parseParams(
@@ -254,20 +211,23 @@ Self-reference:
254
211
  @self expands to your nodeId on the active instance (path head or
255
212
  bare param value, e.g. node=@self). --data and stdin payloads are
256
213
  sent verbatim — pre-resolve manually to a literal @<nodeId> there
257
- (e.g. via 'astrale describe @self -q', or shell-substituted from the
258
- registration record in ~/.astrale/identities.json).
214
+ (e.g. via 'astrale get @self --json'). Resolution authenticates to
215
+ the selected Kernel and never trusts a local registration or JWT sub.
216
+
217
+ --describe reads the callable's input/output from the installed Domain
218
+ schema. Method Paths are not Function nodes.
259
219
 
260
220
  Examples:
261
- $ astrale call /:host.astrale.ai:class.KernelInstance:list
221
+ $ astrale call /:host.astrale.ai:class.Manager:createInstance --describe
262
222
  $ astrale call /:blog.acme.com:class.Author:list limit=10
263
223
  $ astrale call '@self::deactivate'
264
- $ astrale call /:shell.astrale.ai:function.search-domains --json
224
+ $ astrale call /:kernel.astrale.ai:function.journal --data '{"limit":5}' --json
265
225
  `,
266
226
  arguments: [
267
227
  {
268
228
  name: 'path',
269
229
  description:
270
- 'Operation path (e.g., /:host.astrale.ai:class.KernelInstance:list or /node::method)',
230
+ 'Operation path (e.g., /:host.astrale.ai:class.Manager:createInstance or /node::method)',
271
231
  },
272
232
  { name: 'params...', description: 'Params as key=value pairs', required: false },
273
233
  ],
@@ -1,181 +1,86 @@
1
- import { ClassPath } from '@astrale-os/kernel-core/domain'
1
+ import type { Node } from '@astrale-os/sdk/graph/node'
2
+
3
+ import { ClassPath } from '@astrale-os/sdk/graph/class'
4
+ import { Path } from '@astrale-os/sdk/graph/path'
2
5
  import chalk from 'chalk'
3
6
 
4
- import type { CommandDefinition } from '../command'
5
- import type { GraphNode, KernelCommandOpts } from '../kernel'
7
+ import type { KernelCommandOpts } from '../connection'
8
+ import type { CommandDefinition } from '../program/index'
6
9
 
7
- import {
8
- bindGraph,
9
- expandSelfInPath,
10
- nodeProp,
11
- runKernelCommand,
12
- splitRoot,
13
- unqualifyKey,
14
- withSelfHint,
15
- } from '../kernel'
10
+ import { expandSelfInPath, runKernelCommand, withSelfHint } from '../connection'
11
+ import { unqualifyProperty } from '../graph/index'
16
12
  import { log } from '../lib/log'
17
13
  import { output } from '../lib/output'
18
14
 
19
- type DescribeResult = { node: GraphNode | undefined; children: GraphNode[] }
20
-
21
- // Commander turns `--no-schema` into `schema: false`.
22
15
  type DescribeOpts = KernelCommandOpts & { schema?: boolean }
16
+ type DescribedNode = Omit<Node, 'props'> & { readonly props: Readonly<Record<string, unknown>> }
23
17
 
24
- export async function describeCommand(path: string, opts: DescribeOpts): Promise<void> {
25
- let expandedPath: string
18
+ /** Describe only facts present on the canonical Node; no hierarchy is inferred from its Path. */
19
+ export async function describeCommand(target: string, opts: DescribeOpts): Promise<void> {
20
+ let path: string
26
21
  let meta
27
22
  try {
28
- ;({ path: expandedPath, meta } = await expandSelfInPath(path, opts))
29
- } catch (e) {
30
- log.error(e instanceof Error ? e.message : 'Invalid @self expansion')
23
+ ;({ path, meta } = await expandSelfInPath(target, opts))
24
+ } catch (error) {
25
+ log.error(error instanceof Error ? error.message : 'Invalid target')
31
26
  process.exit(1)
32
27
  }
33
- await runKernelCommand<DescribeResult>({
28
+
29
+ await runKernelCommand({
34
30
  opts,
35
- label: expandedPath,
36
- // One function.get depth:1 = the node AND its children (was ::get +
37
- // ::listChildren). Function-class children are the node's operations.
38
- fn: async (ctx) => {
39
- const result = await withSelfHint(
40
- () => bindGraph(ctx).query((q) => q.from(expandedPath).children()),
41
- meta,
42
- )
43
- const { root, children } = splitRoot(result.wire.nodes, expandedPath)
44
- return { node: root, children }
45
- },
46
- format: (result, fmtOpts, isRaw) => {
47
- const shown = opts.schema === false ? stripSchema(result) : result
48
- if (isRaw) {
49
- output(shown, fmtOpts)
50
- return
51
- }
52
- printDescribe(shown, expandedPath)
31
+ label: `Describe ${path}`,
32
+ fn: ({ graph }) => withSelfHint(() => graph.getOrThrow(Path.parse(path)), meta),
33
+ format: (node, format, machine) => {
34
+ const shown = opts.schema === false ? withoutSchema(node) : node
35
+ if (machine || format.format !== undefined) output(shown, format)
36
+ else printDescription(path, shown)
53
37
  },
54
38
  })
55
39
  }
56
40
 
57
- function stripSchema(result: DescribeResult): DescribeResult {
58
- if (!result.node) return result
59
- const entries = Object.entries(result.node.props).filter(([k]) => unqualifyKey(k) !== 'schema')
60
- return { ...result, node: { ...result.node, props: Object.fromEntries(entries) } }
61
- }
62
-
63
- // ── Pretty-print ────────────────────────────────────────────
64
-
65
- function printDescribe({ node, children }: DescribeResult, path: string): void {
66
- const kind = classNameOf(node) ?? 'Node'
67
- const name = basename(path) || path
68
- console.log(` ${chalk.bold.cyan(name)} ${chalk.dim(`(${kind})`)}`)
69
-
70
- const description = nodeProp(node, 'description')
71
- if (description) console.log(` ${chalk.dim(String(description))}`)
72
- console.log('')
73
-
74
- const operations = children.filter(isFunction)
75
- const otherChildren = children.filter((c) => !isFunction(c))
76
-
77
- if (operations.length > 0) {
78
- console.log(` ${chalk.bold('Operations:')}`)
79
- const names = operations.map((o) => basename(o.path))
80
- const slugW = Math.max(4, ...names.map((n) => n.length))
81
- operations.forEach((op, i) => {
82
- const opName = names[i].padEnd(slugW)
83
- const schema = formatInputSchema(op)
84
- console.log(` ${chalk.green(opName)} ${chalk.dim(schema)}`)
85
- })
86
- console.log('')
87
- }
88
-
89
- if (otherChildren.length > 0) {
90
- console.log(` ${chalk.bold('Children:')}`)
91
- for (const child of otherChildren) {
92
- const childKind = classNameOf(child) ?? '?'
93
- console.log(` ${chalk.cyan(basename(child.path) || '?')} ${chalk.dim(childKind)}`)
94
- }
95
- console.log('')
41
+ function withoutSchema(node: Node): DescribedNode {
42
+ return {
43
+ ...node,
44
+ props: Object.fromEntries(
45
+ Object.entries(node.props).filter(([key]) => unqualifyProperty(key) !== 'schema'),
46
+ ),
96
47
  }
97
-
98
- if (children.length === 0) {
99
- printProperties(node)
100
- }
101
- }
102
-
103
- /** `/a/b/c` → `c`; `/` → `/`. */
104
- function basename(path?: string): string {
105
- if (!path || path === '/') return path ?? ''
106
- return path.slice(path.lastIndexOf('/') + 1)
107
48
  }
108
49
 
109
- /** Short class name from the contract field `class` (a serialized ClassPath). */
110
- function classNameOf(item: GraphNode | undefined): string | undefined {
111
- return item?.class ? (ClassPath.tryParse(item.class)?.className ?? undefined) : undefined
112
- }
113
-
114
- function isFunction(item: GraphNode): boolean {
115
- return classNameOf(item) === 'Function'
116
- }
117
-
118
- function printProperties(node: GraphNode | undefined): void {
119
- if (!node) return
120
- const entries = Object.entries(node.props)
121
- .map(([k, v]) => [unqualifyKey(k), v] as const)
122
- .filter(([k]) => k !== '__labels' && k !== 'id')
123
- if (entries.length === 0) return
124
-
125
- console.log(` ${chalk.bold('Properties:')}`)
126
- for (const [k, v] of entries) {
127
- const val = typeof v === 'string' && v.length > 80 ? v.slice(0, 80) + '...' : String(v)
128
- console.log(` ${chalk.cyan(k)}: ${chalk.dim(val)}`)
129
- }
130
- }
131
-
132
- function formatInputSchema(node: GraphNode): string {
133
- const raw = nodeProp(node, 'inputSchema')
134
- if (!raw) return '{}'
135
-
136
- try {
137
- const schema = typeof raw === 'string' ? JSON.parse(raw) : raw
138
- if (typeof schema !== 'object' || schema === null) return '{}'
139
-
140
- const props = (schema as { properties?: Record<string, { type?: string }> }).properties
141
- if (!props || Object.keys(props).length === 0) return '{}'
142
-
143
- const required = new Set((schema as { required?: string[] }).required ?? [])
144
-
145
- const fields = Object.entries(props).map(([name, def]) => {
146
- const type = def?.type ?? '?'
147
- return required.has(name) ? `${name}: ${type}` : `${name}?: ${type}`
148
- })
149
-
150
- return `{ ${fields.join(', ')} }`
151
- } catch {
152
- return '{...}'
50
+ function printDescription(target: string, node: DescribedNode): void {
51
+ console.log(` ${chalk.bold.cyan(target)}`)
52
+ console.log(` ${chalk.dim('id')} ${node.id}`)
53
+ console.log(
54
+ ` ${chalk.dim('class')} ${ClassPath.name(node.class)} ${chalk.dim(`(${node.class})`)}`,
55
+ )
56
+
57
+ const properties = Object.entries(node.props)
58
+ if (properties.length === 0) return
59
+ console.log(`\n ${chalk.bold('Properties:')}`)
60
+ for (const [key, value] of properties) {
61
+ const rendered = typeof value === 'string' ? value : JSON.stringify(value)
62
+ console.log(` ${chalk.cyan(unqualifyProperty(key))}: ${chalk.dim(rendered)}`)
153
63
  }
154
64
  }
155
65
 
156
66
  export default {
157
67
  name: 'describe',
158
- description: 'Describe a node: its kind, operations, children, and schemas',
68
+ description: 'Describe the canonical facts of one node',
159
69
  afterHelpText: `
160
70
  Behavior:
161
- One function.get depth:1 the node plus its children (Function-class
162
- children are shown as Operations). Raw dump: full properties + children.
163
- For Domain nodes the properties include a multi-kB serialized 'schema' —
164
- use --no-schema, and pipe to jq rather than reading by eye.
71
+ Reads one canonical Node and presents its ID, Class, and properties.
72
+ It does not infer operations or children from path layout. --no-schema
73
+ omits schema-valued properties from Domain-sized results.
165
74
 
166
75
  Examples:
167
- $ astrale describe /kernel.astrale.ai
168
- $ astrale describe /host.astrale.ai --no-schema | jq .
76
+ $ astrale describe /:kernel.astrale.ai:class.Domain
77
+ $ astrale describe @self --no-schema
169
78
  `,
170
- arguments: [{ name: 'path', description: 'Node path (/domain/Class) or ID (@nodeId)' }],
79
+ arguments: [{ name: 'target', description: 'Canonical Node Path or @id' }],
171
80
  options: [
172
- {
173
- flags: '--no-schema',
174
- description:
175
- 'Omit the serialized `schema` property (useful for Domain nodes, where it is multi-kB)',
176
- },
81
+ { flags: '--no-schema', description: 'Omit properties whose qualified leaf is schema' },
177
82
  ],
178
- action: async (path, opts) => {
179
- await describeCommand(path as string, opts as DescribeOpts)
83
+ action: async (target, opts) => {
84
+ await describeCommand(target as string, opts as DescribeOpts)
180
85
  },
181
86
  } satisfies CommandDefinition