@astrale-os/cli 1.0.0-beta.1 → 1.0.0-beta.11

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 (329) hide show
  1. package/README.md +23 -12
  2. package/dist/astrale.js +48048 -56798
  3. package/dist/public/connect-core.js +2011 -30765
  4. package/dist/public/keys/index.js +884 -29483
  5. package/dist/public/paths/index.js +13101 -41459
  6. package/dist/types/admin/binding.d.ts +19 -0
  7. package/dist/types/admin/instance/client.d.ts +22 -0
  8. package/dist/types/admin/instance/index.d.ts +2 -0
  9. package/dist/types/admin/instance/model.d.ts +5 -12
  10. package/dist/types/connection/auth.d.ts +3 -0
  11. package/dist/types/connection/call.d.ts +3 -0
  12. package/dist/types/connection/command.d.ts +27 -0
  13. package/dist/types/connection/credential.d.ts +14 -0
  14. package/dist/types/connection/errors.d.ts +1 -0
  15. package/dist/types/connection/exchange.d.ts +9 -0
  16. package/dist/types/connection/failure/classify.d.ts +2 -0
  17. package/dist/types/connection/failure/index.d.ts +5 -0
  18. package/dist/types/connection/failure/model.d.ts +25 -0
  19. package/dist/types/connection/failure/render.d.ts +3 -0
  20. package/dist/types/connection/index.d.ts +8 -0
  21. package/dist/types/connection/reasons.d.ts +37 -0
  22. package/dist/types/connection/self.d.ts +37 -0
  23. package/dist/types/connection/session.d.ts +31 -0
  24. package/dist/types/connection/target.d.ts +38 -0
  25. package/dist/types/errors.d.ts +1 -1
  26. package/dist/types/lib/admin-instance.d.ts +17 -0
  27. package/dist/types/lib/admin-target.d.ts +11 -36
  28. package/dist/types/lib/config.d.ts +7 -59
  29. package/dist/types/lib/failure-debug.d.ts +1 -0
  30. package/dist/types/lib/idp.d.ts +26 -168
  31. package/dist/types/lib/instance-target.d.ts +1 -1
  32. package/dist/types/lib/instance.d.ts +30 -88
  33. package/dist/types/lib/log.d.ts +5 -3
  34. package/dist/types/lib/meta.d.ts +2 -2
  35. package/dist/types/lib/proc.d.ts +43 -0
  36. package/dist/types/lib/self.d.ts +9 -0
  37. package/dist/types/lib/update.d.ts +91 -0
  38. package/dist/types/lib/validation.d.ts +4 -1
  39. package/package.json +16 -19
  40. package/src/__tests__/fixtures/publication.ts +20 -0
  41. package/src/admin/__tests__/binding.test.ts +31 -0
  42. package/src/admin/__tests__/fixture.ts +101 -0
  43. package/src/admin/binding.ts +98 -0
  44. package/src/admin/catalog/.spec/api.d.ts +2 -2
  45. package/src/admin/catalog/__tests__/client.test.ts +20 -49
  46. package/src/admin/catalog/client.ts +36 -32
  47. package/src/admin/catalog/model.ts +3 -4
  48. package/src/admin/graph/.spec/api.d.ts +4 -10
  49. package/src/admin/graph/nodes.ts +1 -1
  50. package/src/admin/instance/.spec/api.d.ts +2 -2
  51. package/src/admin/instance/__tests__/client.test.ts +39 -164
  52. package/src/admin/instance/client.ts +35 -124
  53. package/src/admin/instance/index.ts +1 -2
  54. package/src/admin/instance/model.ts +11 -20
  55. package/src/commands/__tests__/admin-instance.test.ts +11 -8
  56. package/src/commands/__tests__/auth-login.test.ts +54 -1
  57. package/src/commands/__tests__/call-describe.test.ts +46 -33
  58. package/src/commands/__tests__/call.test.ts +5 -1
  59. package/src/commands/__tests__/domain-install-operation.test.ts +12 -7
  60. package/src/commands/__tests__/domain-install-owned.test.ts +6 -2
  61. package/src/commands/__tests__/domain-list.test.ts +63 -4
  62. package/src/commands/__tests__/install-identity-override.test.ts +19 -19
  63. package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
  64. package/src/commands/__tests__/instance-status.test.ts +139 -0
  65. package/src/commands/__tests__/read-commands.test.ts +35 -10
  66. package/src/commands/__tests__/studio-descriptor.test.ts +22 -0
  67. package/src/commands/__tests__/update.test.ts +48 -19
  68. package/src/commands/call-describe.ts +60 -68
  69. package/src/commands/call.ts +43 -56
  70. package/src/commands/domain/install.ts +13 -17
  71. package/src/commands/domain/list.ts +18 -13
  72. package/src/commands/domain/publish.ts +14 -11
  73. package/src/commands/domain/uninstall.ts +2 -2
  74. package/src/commands/get.ts +14 -15
  75. package/src/commands/identity/__tests__/register.test.ts +3 -3
  76. package/src/commands/identity/register.ts +5 -4
  77. package/src/commands/instance/create.ts +8 -16
  78. package/src/commands/instance/delete.ts +12 -9
  79. package/src/commands/instance/list.ts +2 -2
  80. package/src/commands/instance/status.ts +66 -20
  81. package/src/commands/instance/use.ts +16 -12
  82. package/src/commands/introspect.ts +9 -16
  83. package/src/commands/logs.ts +24 -25
  84. package/src/commands/mutate.ts +6 -5
  85. package/src/commands/query.ts +27 -30
  86. package/src/commands/studio.ts +20 -0
  87. package/src/commands/token.ts +8 -7
  88. package/src/commands/update.ts +38 -14
  89. package/src/commands/view.ts +7 -6
  90. package/src/connection/.spec/api.d.ts +7 -8
  91. package/src/connection/.spec/architecture.md +5 -4
  92. package/src/connection/.spec/flows/session.ts +2 -2
  93. package/src/connection/.spec/laws/connection.ts +6 -2
  94. package/src/connection/.spec/layout.ts +1 -0
  95. package/src/connection/__tests__/credential.test.ts +20 -1
  96. package/src/connection/__tests__/errors.test.ts +43 -32
  97. package/src/connection/__tests__/exchange.test.ts +36 -6
  98. package/src/connection/__tests__/failure-fixtures.ts +39 -0
  99. package/src/connection/__tests__/failure-safety.test.ts +75 -0
  100. package/src/connection/__tests__/self.test.ts +31 -11
  101. package/src/connection/__tests__/session.test.ts +3 -3
  102. package/src/connection/__tests__/target.test.ts +1 -0
  103. package/src/connection/auth.ts +13 -0
  104. package/src/connection/call.ts +1 -1
  105. package/src/connection/credential.ts +2 -2
  106. package/src/connection/errors.ts +1 -355
  107. package/src/connection/exchange.ts +52 -27
  108. package/src/connection/failure/classify.ts +110 -0
  109. package/src/connection/failure/index.ts +18 -0
  110. package/src/connection/failure/model.ts +28 -0
  111. package/src/connection/failure/render.ts +116 -0
  112. package/src/connection/reasons.ts +16 -0
  113. package/src/connection/self.ts +39 -21
  114. package/src/connection/session.ts +19 -8
  115. package/src/errors.ts +3 -2
  116. package/src/graph/.spec/api.d.ts +12 -5
  117. package/src/graph/.spec/layout.ts +9 -1
  118. package/src/graph/__tests__/mutation.test.ts +2 -2
  119. package/src/graph/__tests__/query.test.ts +25 -6
  120. package/src/graph/class.ts +30 -0
  121. package/src/graph/index.ts +1 -0
  122. package/src/graph/query.ts +21 -32
  123. package/src/identity/.spec/api.d.ts +2 -2
  124. package/src/identity/__tests__/registration.test.ts +1 -1
  125. package/src/identity/registration.ts +3 -3
  126. package/src/keys/__tests__/keys.test.ts +4 -0
  127. package/src/keys/credential.ts +1 -0
  128. package/src/lib/__tests__/admin-target.test.ts +12 -0
  129. package/src/lib/__tests__/command-dx.test.ts +1 -1
  130. package/src/lib/__tests__/domain-publication.test.ts +138 -0
  131. package/src/lib/__tests__/idp-session.test.ts +22 -0
  132. package/src/lib/__tests__/idp.test.ts +36 -4
  133. package/src/lib/__tests__/instance-candidates.test.ts +1 -3
  134. package/src/lib/__tests__/instance-target.test.ts +7 -39
  135. package/src/lib/__tests__/instance.test.ts +6 -0
  136. package/src/lib/__tests__/log-errors.test.ts +64 -0
  137. package/src/lib/__tests__/studio-server-deps.test.ts +53 -16
  138. package/src/lib/__tests__/update.test.ts +113 -8
  139. package/src/lib/__tests__/view-open-intent.test.ts +0 -2
  140. package/src/lib/__tests__/view-server.test.ts +18 -3
  141. package/src/lib/__tests__/view-session.test.ts +1 -1
  142. package/src/lib/admin-instance.ts +14 -8
  143. package/src/lib/admin-target.ts +21 -2
  144. package/src/lib/command-dx.ts +9 -9
  145. package/src/lib/domain-publication.ts +104 -0
  146. package/src/lib/failure-debug.ts +26 -0
  147. package/src/lib/idp.ts +39 -4
  148. package/src/lib/instance-candidates.ts +2 -2
  149. package/src/lib/instance-target.ts +3 -14
  150. package/src/lib/instance.ts +22 -12
  151. package/src/lib/log.ts +16 -9
  152. package/src/lib/login-flow.ts +13 -3
  153. package/src/lib/meta.ts +2 -2
  154. package/src/lib/provision-instance.ts +34 -57
  155. package/src/lib/update.ts +124 -30
  156. package/src/lib/validation.ts +2 -2
  157. package/src/lib/view/resolve.ts +1 -1
  158. package/src/lib/view/server.ts +17 -3
  159. package/src/program/__tests__/program.test.ts +20 -4
  160. package/src/program/build.ts +2 -2
  161. package/src/setup/__tests__/instance-step.test.ts +27 -10
  162. package/src/setup/steps/instance.ts +22 -15
  163. package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
  164. package/studio/client/dist/assets/index-7YHdeCxp.js +81 -0
  165. package/studio/client/dist/assets/index-C0FAnwNw.css +2 -0
  166. package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
  167. package/studio/client/dist/assets/schema-studio-BilUNb6Z.css +1 -0
  168. package/studio/client/dist/assets/schema-studio-CABpTfvH.js +8 -0
  169. package/studio/client/dist/index.html +3 -2
  170. package/studio/package.json +10 -14
  171. package/studio/server/agent/ask.test.ts +1 -1
  172. package/studio/server/agent/bridge/grant.test.ts +1 -1
  173. package/studio/server/agent/bridge/routes.test.ts +1 -1
  174. package/studio/server/agent/bridge/stdio.ts +8 -1
  175. package/studio/server/agent/conversation.test.ts +10 -5
  176. package/studio/server/agent/conversation.ts +56 -8
  177. package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
  178. package/studio/server/agent/harness/codex/loadout.ts +18 -21
  179. package/studio/server/agent/harness/gateway/config.ts +20 -18
  180. package/studio/server/agent/harness/selection.ts +9 -1
  181. package/studio/server/agent/prompts/anchors.test.ts +32 -36
  182. package/studio/server/agent/prompts/anchors.ts +20 -49
  183. package/studio/server/agent/prompts/system.test.ts +3 -2
  184. package/studio/server/agent/prompts/system.ts +6 -5
  185. package/studio/server/agent/prompts/turn.ts +13 -4
  186. package/studio/server/agent/routes.test.ts +1 -1
  187. package/studio/server/agent/run/completion.ts +2 -2
  188. package/studio/server/agent/run/coordinator.test.ts +1 -1
  189. package/studio/server/agent/run/preparation.ts +6 -5
  190. package/studio/server/agent/run/transcript.test.ts +35 -1
  191. package/studio/server/agent/run/transcript.ts +169 -3
  192. package/studio/server/agent/run/usage.test.ts +24 -0
  193. package/studio/server/agent/run/usage.ts +26 -1
  194. package/studio/server/api/canvas.ts +43 -0
  195. package/studio/server/api/comments.ts +85 -0
  196. package/studio/server/api/context.ts +61 -0
  197. package/studio/server/api/deployment.ts +39 -0
  198. package/studio/server/api/documents.ts +55 -0
  199. package/studio/server/api/domain.ts +59 -0
  200. package/studio/server/api/http.ts +45 -0
  201. package/studio/server/api/project.ts +57 -0
  202. package/studio/server/api/schema.ts +12 -0
  203. package/studio/server/api/views.ts +57 -0
  204. package/studio/server/api/workspace.ts +60 -0
  205. package/studio/server/api-agent-loadout.test.ts +1 -1
  206. package/studio/server/api.test.ts +147 -0
  207. package/studio/server/api.ts +20 -422
  208. package/studio/server/cache.test.ts +122 -0
  209. package/studio/server/cache.ts +162 -11
  210. package/studio/server/cli-consumers.test.ts +494 -0
  211. package/studio/server/cli.test.ts +88 -0
  212. package/studio/server/cli.ts +240 -0
  213. package/studio/server/client-package.ts +25 -1
  214. package/studio/server/domain.test.ts +50 -40
  215. package/studio/server/domain.ts +83 -52
  216. package/studio/server/environment/dotenv-preview.test.ts +39 -0
  217. package/studio/server/environment/dotenv-preview.ts +27 -0
  218. package/studio/server/environment/files.test.ts +53 -0
  219. package/studio/server/{state/env.ts → environment/files.ts} +7 -48
  220. package/studio/server/{state → handoff}/copy.ts +21 -6
  221. package/studio/server/handoff/service.test.ts +22 -0
  222. package/studio/server/{state/handoff.ts → handoff/service.ts} +21 -14
  223. package/studio/server/index.ts +1 -1
  224. package/studio/server/instances/active.ts +72 -0
  225. package/studio/server/instances/deploy-record.test.ts +31 -0
  226. package/studio/server/instances/deploy-record.ts +37 -0
  227. package/studio/server/instances/deploy.ts +56 -0
  228. package/studio/server/{state/instance.test.ts → instances/probe.test.ts} +25 -21
  229. package/studio/server/instances/probe.ts +67 -0
  230. package/studio/server/instances/status.test.ts +12 -0
  231. package/studio/server/instances/status.ts +49 -0
  232. package/studio/server/introspect/anatomy/client-tree.ts +57 -0
  233. package/studio/server/introspect/anatomy/env-fields.ts +66 -0
  234. package/studio/server/introspect/anatomy/schema-definition.ts +53 -0
  235. package/studio/server/introspect/anatomy/source.ts +181 -0
  236. package/studio/server/introspect/anatomy/views/routes.ts +114 -0
  237. package/studio/server/introspect/anatomy/views.ts +53 -0
  238. package/studio/server/introspect/anatomy-extras.test.ts +106 -35
  239. package/studio/server/introspect/anatomy-extras.ts +7 -726
  240. package/studio/server/introspect/anatomy.test.ts +22 -14
  241. package/studio/server/introspect/anatomy.ts +52 -28
  242. package/studio/server/introspect/bundle.ts +15 -9
  243. package/studio/server/introspect/canonical-schema.test.ts +93 -309
  244. package/studio/server/introspect/canonical-schema.ts +257 -425
  245. package/studio/server/introspect/config-preview.test.ts +32 -0
  246. package/studio/server/introspect/config-preview.ts +119 -0
  247. package/studio/server/introspect/core-extractor.ts +18 -127
  248. package/studio/server/introspect/core.ts +1 -2
  249. package/studio/server/introspect/diff.test.ts +18 -18
  250. package/studio/server/introspect/diff.ts +44 -90
  251. package/studio/server/introspect/extractor.ts +23 -116
  252. package/studio/server/introspect/hash.ts +5 -2
  253. package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
  254. package/studio/server/introspect/overlay-tsmorph.ts +7 -1305
  255. package/studio/server/introspect/overlay.test.ts +26 -54
  256. package/studio/server/introspect/overlay.ts +10 -37
  257. package/studio/server/introspect/revision.test.ts +54 -0
  258. package/studio/server/introspect/revision.ts +49 -0
  259. package/studio/server/introspect/runtime.test.ts +83 -143
  260. package/studio/server/introspect/runtime.ts +40 -11
  261. package/studio/server/introspect/schema-ir-json.test.ts +70 -0
  262. package/studio/server/introspect/schema-ir-json.ts +151 -0
  263. package/studio/server/introspect/schema-refs.test.ts +43 -88
  264. package/studio/server/introspect/schema-refs.ts +14 -49
  265. package/studio/server/introspect/source-overlay/annotations.ts +14 -0
  266. package/studio/server/introspect/source-overlay/handlers.ts +161 -0
  267. package/studio/server/introspect/source-overlay/kernel-calls.ts +46 -0
  268. package/studio/server/introspect/source-overlay/project.ts +248 -0
  269. package/studio/server/introspect/source-overlay/spans.ts +330 -0
  270. package/studio/server/json.ts +46 -0
  271. package/studio/server/lifecycle.ts +5 -1
  272. package/studio/server/sse.test.ts +26 -0
  273. package/studio/server/sse.ts +4 -1
  274. package/studio/server/state/baseline.test.ts +223 -7
  275. package/studio/server/state/baseline.ts +148 -34
  276. package/studio/server/state/comments.test.ts +66 -1
  277. package/studio/server/state/comments.ts +173 -10
  278. package/studio/server/state/context.ts +34 -2
  279. package/studio/server/state/documents.ts +42 -1
  280. package/studio/server/state/integrations.ts +27 -1
  281. package/studio/server/state/layout.test.ts +34 -0
  282. package/studio/server/state/layout.ts +44 -17
  283. package/studio/server/state/settings.ts +29 -22
  284. package/studio/server/state/store.test.ts +122 -0
  285. package/studio/server/state/store.ts +66 -17
  286. package/studio/server/state/visibility.ts +22 -11
  287. package/studio/server/views/model.test.ts +57 -0
  288. package/studio/server/views/model.ts +66 -0
  289. package/studio/server/views/runtime.ts +40 -0
  290. package/studio/server/views/selection-repository.test.ts +31 -0
  291. package/studio/server/views/selection-repository.ts +60 -0
  292. package/studio/server/views/session.test.ts +73 -0
  293. package/studio/server/views/session.ts +166 -0
  294. package/studio/server/views/target.test.ts +78 -0
  295. package/studio/server/views/target.ts +222 -0
  296. package/studio/server/watch.test.ts +11 -11
  297. package/studio/server/watch.ts +9 -5
  298. package/studio/server/workspace/catalog.test.ts +17 -0
  299. package/studio/server/workspace/catalog.ts +43 -0
  300. package/studio/server/{state → workspace}/create.test.ts +1 -1
  301. package/studio/server/{state → workspace}/create.ts +1 -1
  302. package/studio/server/{state → workspace}/git.ts +1 -1
  303. package/studio/server/workspace/updates.ts +83 -0
  304. package/studio/server/workspace-state.ts +1 -1
  305. package/studio/server/workspace-watch.ts +2 -2
  306. package/studio/shared/contracts/README.md +16 -0
  307. package/studio/shared/contracts/agent.ts +270 -0
  308. package/studio/shared/contracts/runtime.ts +50 -0
  309. package/studio/shared/contracts/schema.ts +393 -0
  310. package/studio/shared/contracts/surface.test.ts +317 -0
  311. package/studio/shared/contracts/workspace.ts +265 -0
  312. package/studio/shared/layout.test.ts +116 -0
  313. package/studio/shared/schema/identity.test.ts +34 -0
  314. package/studio/shared/schema/identity.ts +68 -0
  315. package/studio/shared/types.ts +11 -1099
  316. package/studio/tsconfig.json +1 -1
  317. package/viewer/dist/main.js +40 -61
  318. package/.check-workspace.cjs +0 -40
  319. package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
  320. package/studio/client/dist/assets/elk-api-D2xgMJvi.js +0 -1
  321. package/studio/client/dist/assets/index-BMdnsIJA.css +0 -1
  322. package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
  323. package/studio/client/dist/assets/index-D-vRV8w7.js +0 -8
  324. package/studio/client/dist/assets/index-LGSWRrk8.js +0 -81
  325. package/studio/server/state/catalog.ts +0 -117
  326. package/studio/server/state/instance.ts +0 -280
  327. package/studio/server/state/updates.ts +0 -63
  328. package/studio/server/state/views.test.ts +0 -286
  329. package/studio/server/state/views.ts +0 -535
@@ -1,4 +1,4 @@
1
- import type { SessionAuth } from '@astrale-os/kernel-client/session'
1
+ import type { SessionAuth } from '@astrale-os/sdk/client/session'
2
2
 
3
3
  import { issuer, type IssuerId } from '@astrale-os/sdk/auth'
4
4
  import { Path } from '@astrale-os/sdk/graph/path'
@@ -6,6 +6,7 @@ import { describe, expect, test } from 'bun:test'
6
6
 
7
7
  import type { AstraleConfig } from '../../lib/config'
8
8
 
9
+ import { bindCredentialIdentity } from '../auth'
9
10
  import { createCliCredential, createConnectionCredential } from '../credential'
10
11
 
11
12
  const SOURCE = issuer.accept('https://kernel.example')
@@ -20,6 +21,24 @@ const config: AstraleConfig = {
20
21
  }
21
22
 
22
23
  describe('connection credential', () => {
24
+ test('pins the bookmark identity while leaving explicit credentials unnamed', async () => {
25
+ const target = {
26
+ url: `${SOURCE}/invoke`,
27
+ kernelIssuer: SOURCE,
28
+ defaultIdentity: 'bookmark-owner',
29
+ }
30
+
31
+ await expect(bindCredentialIdentity({}, target)).resolves.toMatchObject({
32
+ as: 'bookmark-owner',
33
+ })
34
+ await expect(bindCredentialIdentity({ as: 'explicit-owner' }, target)).resolves.toEqual({
35
+ as: 'explicit-owner',
36
+ })
37
+ await expect(bindCredentialIdentity({ creds: 'opaque' }, target)).resolves.toEqual({
38
+ creds: 'opaque',
39
+ })
40
+ })
41
+
23
42
  /** @evidence TEST-CLI-CONNECTION-RESOLVES-SOURCE-AUTH-BEFORE-ROUTING */
24
43
  test('resolves source authority without destination knowledge', async () => {
25
44
  const audiences: IssuerId[] = []
@@ -1,10 +1,16 @@
1
- import { ResponseError, TransportError } from '@astrale-os/kernel-client'
1
+ import { ResponseError } from '@astrale-os/sdk/client'
2
+ import { Path } from '@astrale-os/sdk/graph/path'
2
3
  import { describe, expect, test } from 'bun:test'
3
4
 
4
5
  import { formatKernelError, functionInputIssues, schemaUpgradeHint } from '../errors'
6
+ import { transportFailure } from './failure-fixtures'
5
7
 
6
8
  const CONFLICT = 4001
7
9
  const VALIDATION = 1003
10
+ const TEST_INVOCATION = {
11
+ source: 'https://kernel.test',
12
+ id: 'connection-errors',
13
+ } as ConstructorParameters<typeof ResponseError>[2]
8
14
 
9
15
  describe('formatKernelError', () => {
10
16
  /** @evidence TEST-CLI-CONNECTION-MAPS-TYPED-TRANSPORT */
@@ -15,24 +21,28 @@ describe('formatKernelError', () => {
15
21
  writes.push(typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk))
16
22
  return true
17
23
  }) as typeof process.stderr.write
18
- const error = new TransportError('Publication discovery request failed.', {
19
- cause: Object.assign(new Error('connect ECONNREFUSED'), { code: 'ECONNREFUSED' }),
20
- phase: 'connect',
21
- delivery: 'unknown',
22
- }) as TransportError & { url?: string }
23
- error.url = 'https://localhost:8443/kernel/host'
24
+ const error = Object.assign(
25
+ transportFailure('Publication discovery request failed.', 'unknown', {
26
+ kind: 'acquisition',
27
+ resource: 'publication',
28
+ }),
29
+ {
30
+ url: 'https://localhost:8443/kernel/host',
31
+ cause: Object.assign(new Error('connect ECONNREFUSED'), { code: 'ECONNREFUSED' }),
32
+ },
33
+ )
24
34
  try {
25
- await formatKernelError(error, true)
35
+ await formatKernelError(error, true, 'https://localhost:8443/kernel/host')
26
36
  } finally {
27
37
  process.stderr.write = original
28
38
  }
29
39
 
30
40
  expect(JSON.parse(writes[0]!)).toMatchObject({
31
- error: 'CONNECTION_ERROR',
41
+ error: 'TRANSPORT_ERROR',
32
42
  message: 'Publication discovery request failed.',
33
43
  url: 'https://localhost:8443/kernel/host',
34
- phase: 'connect',
35
- delivery: 'unknown',
44
+ phase: 'unknown',
45
+ transport: { kind: 'acquisition', resource: 'publication' },
36
46
  })
37
47
  expect(writes[0]).not.toContain('ECONNREFUSED')
38
48
  })
@@ -46,9 +56,8 @@ describe('formatKernelError', () => {
46
56
  }) as typeof process.stderr.write
47
57
  try {
48
58
  await formatKernelError(
49
- new TransportError('Request timed out.', {
50
- cause: new Error('timeout'),
51
- phase: 'timeout',
59
+ transportFailure('Request timed out.', 'timeout', {
60
+ kind: 'invocation',
52
61
  delivery: 'unknown',
53
62
  }),
54
63
  true,
@@ -70,7 +79,7 @@ describe('formatKernelError', () => {
70
79
  expect(JSON.parse(writes[0]!)).toMatchObject({
71
80
  error: 'TIMEOUT',
72
81
  phase: 'timeout',
73
- delivery: 'unknown',
82
+ transport: { kind: 'invocation', delivery: 'unknown' },
74
83
  operation: '4a4c9a18-50f6-4d84-a7b7-2d83e3e45dc8',
75
84
  retry:
76
85
  'astrale domain install https://crm.test --direct --operation 4a4c9a18-50f6-4d84-a7b7-2d83e3e45dc8',
@@ -87,7 +96,7 @@ describe('formatKernelError', () => {
87
96
  }) as typeof process.stderr.write
88
97
  try {
89
98
  await formatKernelError(
90
- new ResponseError(CONFLICT, 'Schema change requires migration.', {
99
+ new ResponseError(CONFLICT, 'Schema change requires migration.', TEST_INVOCATION, {
91
100
  code: 'DATA_MIGRATION_REQUIRED',
92
101
  details: {
93
102
  requirements: [
@@ -138,7 +147,7 @@ describe('formatKernelError', () => {
138
147
  }) as typeof process.stderr.write
139
148
  try {
140
149
  await formatKernelError(
141
- new ResponseError(CONFLICT, 'Schema change requires migration.', {
150
+ new ResponseError(CONFLICT, 'Schema change requires migration.', TEST_INVOCATION, {
142
151
  code: 'DATA_MIGRATION_REQUIRED',
143
152
  details: {
144
153
  requirements: [
@@ -193,7 +202,7 @@ describe('formatKernelError', () => {
193
202
  console.log = (...values: unknown[]) => hints.push(values.map(String).join(' '))
194
203
  try {
195
204
  await formatKernelError(
196
- new ResponseError(CONFLICT, 'Schema change requires migration.', {
205
+ new ResponseError(CONFLICT, 'Schema change requires migration.', TEST_INVOCATION, {
197
206
  code: 'DATA_MIGRATION_REQUIRED',
198
207
  details: {
199
208
  requirements: [
@@ -225,7 +234,7 @@ describe('formatKernelError', () => {
225
234
  )
226
235
  })
227
236
 
228
- test('maps SDK class names to stable CLI error codes', async () => {
237
+ test('maps SDK error identities to stable CLI error codes', async () => {
229
238
  const writes: string[] = []
230
239
  const original = process.stderr.write
231
240
  process.stderr.write = ((chunk: string | Uint8Array) => {
@@ -233,15 +242,17 @@ describe('formatKernelError', () => {
233
242
  return true
234
243
  }) as typeof process.stderr.write
235
244
  try {
236
- const pathError = new Error('Path must have an Id or Domain anchor.')
237
- pathError.name = 'PathError'
238
- await formatKernelError(pathError, true)
245
+ try {
246
+ Path.parse('not-a-path')
247
+ } catch (error) {
248
+ await formatKernelError(error, true)
249
+ }
239
250
  } finally {
240
251
  process.stderr.write = original
241
252
  }
242
253
  expect(JSON.parse(writes[0]!)).toEqual({
243
254
  error: 'PATH_INVALID',
244
- message: 'Path must have an Id or Domain anchor.',
255
+ message: expect.any(String),
245
256
  })
246
257
  })
247
258
 
@@ -254,7 +265,7 @@ describe('formatKernelError', () => {
254
265
  console.log = (...values: unknown[]) => lines.push(values.join(' '))
255
266
  try {
256
267
  await formatKernelError(
257
- new ResponseError(VALIDATION, 'Function input is invalid.', {
268
+ new ResponseError(VALIDATION, 'Function input is invalid.', TEST_INVOCATION, {
258
269
  code: 'FUNCTION_INPUT_INVALID',
259
270
  details: {
260
271
  issues: [
@@ -279,7 +290,7 @@ describe('formatKernelError', () => {
279
290
  false,
280
291
  )
281
292
  await formatKernelError(
282
- new ResponseError(VALIDATION, 'Query input is invalid.', {
293
+ new ResponseError(VALIDATION, 'Query input is invalid.', TEST_INVOCATION, {
283
294
  code: 'QUERY_INPUT_INVALID',
284
295
  details: {
285
296
  phase: 'plan',
@@ -310,7 +321,7 @@ describe('formatKernelError', () => {
310
321
  }) as typeof process.stderr.write
311
322
  try {
312
323
  await formatKernelError(
313
- new ResponseError(5001, 'Schema operation is not supported.', {
324
+ new ResponseError(5001, 'Schema operation is not supported.', TEST_INVOCATION, {
314
325
  code: 'SCHEMA_UPGRADE_INCOMPATIBLE',
315
326
  details: {
316
327
  phase: 'upgrade',
@@ -358,7 +369,7 @@ describe('formatKernelError', () => {
358
369
  console.log = (...values: unknown[]) => details.push(values.map(String).join(' '))
359
370
  try {
360
371
  await formatKernelError(
361
- new ResponseError(5001, 'Schema operation is not supported.', {
372
+ new ResponseError(5001, 'Schema operation is not supported.', TEST_INVOCATION, {
362
373
  code: 'SCHEMA_UPGRADE_INCOMPATIBLE',
363
374
  details: {
364
375
  phase: 'upgrade',
@@ -391,7 +402,7 @@ describe('formatKernelError', () => {
391
402
  }) as typeof process.stderr.write
392
403
  try {
393
404
  await formatKernelError(
394
- new ResponseError(1003, 'Function input is invalid.', {
405
+ new ResponseError(1003, 'Function input is invalid.', TEST_INVOCATION, {
395
406
  code: 'SCHEMA_DOMAIN_ADDRESS_NOT_PUBLIC',
396
407
  details: { phase: 'publication', requiredScope: 'public' },
397
408
  }),
@@ -421,7 +432,7 @@ describe('formatKernelError', () => {
421
432
  console.log = (...values: unknown[]) => hints.push(values.map(String).join(' '))
422
433
  try {
423
434
  await formatKernelError(
424
- new ResponseError(1003, 'Function input is invalid.', {
435
+ new ResponseError(1003, 'Function input is invalid.', TEST_INVOCATION, {
425
436
  code: 'SCHEMA_DOMAIN_ADDRESS_NOT_PUBLIC',
426
437
  details: { phase: 'publication', requiredScope: 'public' },
427
438
  }),
@@ -448,7 +459,7 @@ describe('formatKernelError', () => {
448
459
  }) as typeof process.stderr.write
449
460
  try {
450
461
  await formatKernelError(
451
- new ResponseError(1003, 'Function input is invalid.', {
462
+ new ResponseError(1003, 'Function input is invalid.', TEST_INVOCATION, {
452
463
  code: 'FUNCTION_INPUT_INVALID',
453
464
  details: {
454
465
  issues: [
@@ -495,7 +506,7 @@ describe('formatKernelError', () => {
495
506
  console.log = (...values: unknown[]) => details.push(values.map(String).join(' '))
496
507
  try {
497
508
  await formatKernelError(
498
- new ResponseError(1003, 'Function input is invalid.', {
509
+ new ResponseError(1003, 'Function input is invalid.', TEST_INVOCATION, {
499
510
  code: 'FUNCTION_INPUT_INVALID',
500
511
  details: {
501
512
  issues: [
@@ -530,7 +541,7 @@ describe('formatKernelError', () => {
530
541
  }) as typeof process.stderr.write
531
542
  try {
532
543
  await formatKernelError(
533
- new ResponseError(1003, 'Function input is invalid.', {
544
+ new ResponseError(1003, 'Function input is invalid.', TEST_INVOCATION, {
534
545
  code: 'FUNCTION_INPUT_INVALID',
535
546
  details: { issues: [{ code: 'INVALID_FORMAT', path: '/customer/email' }] },
536
547
  }),
@@ -1,4 +1,4 @@
1
- import type { Fetch } from '@astrale-os/kernel-client'
1
+ import type { Fetch } from '@astrale-os/sdk/client'
2
2
 
3
3
  import { issuer } from '@astrale-os/sdk/auth'
4
4
  import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
@@ -47,7 +47,7 @@ describe('Domain token exchange', () => {
47
47
  observed.push({ url, init, body })
48
48
  const index = observed.filter((entry) => entry.url === INVOCATION).length
49
49
  return invocationResponse(
50
- body.id,
50
+ body.requestId,
51
51
  index === 1
52
52
  ? { id: 'user-1' }
53
53
  : index === 2
@@ -121,7 +121,7 @@ describe('Domain token exchange', () => {
121
121
  if (url === INVOCATION) {
122
122
  const body = JSON.parse(await new Response(init?.body).text()) as Record<string, any>
123
123
  return invocationResponse(
124
- body.id,
124
+ body.requestId,
125
125
  body.call.input && Object.keys(body.call.input).length === 0
126
126
  ? { id: 'user-1' }
127
127
  : 'kernel-destination-envelope',
@@ -190,6 +190,35 @@ describe('Domain token exchange', () => {
190
190
  message: 'Token exchange returned an invalid success response.',
191
191
  })
192
192
  })
193
+
194
+ test('normalizes native issuer Fetch failures without exposing their message', async () => {
195
+ const native = new AggregateError([new Error('private DNS state')], '')
196
+ const resolver = createExchangeCredentialResolver(
197
+ TARGET,
198
+ { resolve: async () => SOURCE_TOKEN },
199
+ async (input, init) => {
200
+ if (String(input) === INVOCATION) {
201
+ const body = JSON.parse(await new Response(init?.body).text()) as Record<string, any>
202
+ return invocationResponse(
203
+ body.requestId,
204
+ body.call.input && Object.keys(body.call.input).length === 0
205
+ ? { id: 'user-1' }
206
+ : 'kernel-destination-envelope',
207
+ new Headers(init?.headers).get('accept')!,
208
+ )
209
+ }
210
+ throw native
211
+ },
212
+ 5_000,
213
+ new ExchangeCredentialCache(join(directory, 'native-failure.json')),
214
+ )
215
+
216
+ await expect(resolver.resolve(KERNEL, new AbortController().signal)).rejects.toMatchObject({
217
+ code: 'TOKEN_EXCHANGE_UNAVAILABLE',
218
+ message: 'Domain issuer discovery could not be reached.',
219
+ cause: native,
220
+ })
221
+ })
193
222
  })
194
223
 
195
224
  function exchangeFetch(
@@ -201,7 +230,7 @@ function exchangeFetch(
201
230
  if (url === INVOCATION) {
202
231
  const body = JSON.parse(await new Response(init?.body).text()) as Record<string, any>
203
232
  return invocationResponse(
204
- body.id,
233
+ body.requestId,
205
234
  body.call.input && Object.keys(body.call.input).length === 0
206
235
  ? { id: 'user-1' }
207
236
  : 'kernel-destination-envelope',
@@ -233,8 +262,9 @@ function configuration(enabled: boolean) {
233
262
  }
234
263
  }
235
264
 
236
- function invocationResponse(id: number, result: unknown, contentType: string): Response {
237
- return new Response(JSON.stringify({ id, result }), {
265
+ function invocationResponse(requestId: unknown, result: unknown, contentType: string): Response {
266
+ const invocation = { source: KERNEL, id: `exchange-${String(requestId)}` }
267
+ return new Response(JSON.stringify({ requestId, invocation, result }), {
238
268
  headers: { 'content-type': contentType, 'cache-control': 'no-store' },
239
269
  })
240
270
  }
@@ -0,0 +1,39 @@
1
+ import { ClientError, TransportError } from '@astrale-os/sdk/client'
2
+
3
+ import type { TransportDiagnosticContext } from '../failure/model'
4
+
5
+ type CompatibleTransportPhase = TransportError['phase'] | 'unknown'
6
+
7
+ export function transportFailure(
8
+ message: string,
9
+ phase: CompatibleTransportPhase,
10
+ context: TransportDiagnosticContext,
11
+ ): TransportError {
12
+ const error = new Error(message) as TransportError & {
13
+ context: TransportDiagnosticContext
14
+ }
15
+ Object.setPrototypeOf(error, TransportError.prototype)
16
+ Object.assign(error, { name: 'TransportError', phase, context })
17
+ return error
18
+ }
19
+
20
+ export function legacyTransportFailure(
21
+ message: string,
22
+ phase: CompatibleTransportPhase,
23
+ delivery: 'not-sent' | 'unknown',
24
+ ): TransportError {
25
+ const error = new Error(message) as TransportError & { delivery: typeof delivery }
26
+ Object.setPrototypeOf(error, TransportError.prototype)
27
+ Object.assign(error, { name: 'TransportError', phase, delivery })
28
+ return error
29
+ }
30
+
31
+ export function sessionFailure(
32
+ message: string,
33
+ failure: 'cancelled' | 'closed' | 'timeout',
34
+ ): ClientError {
35
+ const error = new Error(message) as ClientError & { failure: typeof failure }
36
+ Object.setPrototypeOf(error, ClientError.prototype)
37
+ Object.assign(error, { name: 'SessionError', failure })
38
+ return error
39
+ }
@@ -0,0 +1,75 @@
1
+ import { expect, test } from 'bun:test'
2
+
3
+ import { formatKernelError } from '../errors'
4
+ import { legacyTransportFailure, sessionFailure, transportFailure } from './failure-fixtures'
5
+
6
+ /** @evidence TEST-CLI-CONNECTION-FAILURE-SAFETY */
7
+ test('unknown and empty native failures never become ordinary diagnostics', async () => {
8
+ for (const failure of [new Error('private provider detail'), new Error(''), { secret: true }]) {
9
+ const output = await capture(() => formatKernelError(failure, true))
10
+ expect(JSON.parse(output)).toEqual({
11
+ error: 'UNEXPECTED_ERROR',
12
+ message: 'The CLI encountered an unexpected internal failure.',
13
+ })
14
+ expect(output).not.toContain('private provider detail')
15
+ }
16
+ })
17
+
18
+ test('Aggregate children are available only under explicit debug output', async () => {
19
+ const failure = new AggregateError([new Error('first private'), new Error('second private')], '')
20
+ const ordinary = await capture(() => formatKernelError(failure, true))
21
+ expect(ordinary).not.toContain('first private')
22
+ expect(ordinary).not.toContain('second private')
23
+
24
+ const debug = await capture(() => formatKernelError(failure, true, '', true))
25
+ expect(debug).toContain('aggregate: Error: first private')
26
+ expect(debug).toContain('aggregate: Error: second private')
27
+ })
28
+
29
+ test('Session and acquisition failures remain typed without unsafe recovery advice', async () => {
30
+ const session = await capture(() =>
31
+ formatKernelError(sessionFailure('Session operation timed out.', 'timeout'), true),
32
+ )
33
+ expect(JSON.parse(session)).toEqual({ error: 'TIMEOUT', message: 'Session operation timed out.' })
34
+
35
+ const acquisition = await capture(() =>
36
+ formatKernelError(
37
+ transportFailure('Publication failed.', 'unknown', {
38
+ kind: 'acquisition',
39
+ resource: 'publication',
40
+ }),
41
+ true,
42
+ '',
43
+ false,
44
+ { recovery: { operation: 'must-not-leak', retry: 'must-not-leak' } },
45
+ ),
46
+ )
47
+ expect(JSON.parse(acquisition)).toMatchObject({
48
+ error: 'TRANSPORT_ERROR',
49
+ transport: { kind: 'acquisition', resource: 'publication' },
50
+ })
51
+ expect(acquisition).not.toContain('must-not-leak')
52
+
53
+ const legacy = await capture(() =>
54
+ formatKernelError(legacyTransportFailure('Legacy send failed.', 'send', 'not-sent'), true),
55
+ )
56
+ expect(JSON.parse(legacy)).toMatchObject({
57
+ error: 'TRANSPORT_ERROR',
58
+ transport: { kind: 'invocation', delivery: 'not-sent' },
59
+ })
60
+ })
61
+
62
+ async function capture(action: () => Promise<void>): Promise<string> {
63
+ const writes: string[] = []
64
+ const original = process.stderr.write
65
+ process.stderr.write = ((chunk: string | Uint8Array) => {
66
+ writes.push(typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk))
67
+ return true
68
+ }) as typeof process.stderr.write
69
+ try {
70
+ await action()
71
+ return writes.join('')
72
+ } finally {
73
+ process.stderr.write = original
74
+ }
75
+ }
@@ -2,7 +2,11 @@ import { describe, expect, test } from 'bun:test'
2
2
  import { SignJWT } from 'jose'
3
3
  import { webcrypto } from 'node:crypto'
4
4
 
5
- import { resolveSelfIdAuthenticated } from '../self'
5
+ import type { ConnectionContext } from '../session'
6
+
7
+ import { expandSelfInCall, resolveSelfIdAuthenticated } from '../self'
8
+
9
+ const context = Object.freeze({}) as ConnectionContext
6
10
 
7
11
  describe('resolveSelfIdAuthenticated', () => {
8
12
  /** @evidence TEST-CLI-SELF-USES-AUTHENTICATED-EFFECTIVE-PRINCIPAL */
@@ -14,11 +18,9 @@ describe('resolveSelfIdAuthenticated', () => {
14
18
  .setSubject('manager-principal')
15
19
  .setAudience('https://child.example')
16
20
  .sign(key)
17
- const opts = { creds: credential, url: 'https://manager.example/children/child/invoke' }
18
-
19
- const resolved = await resolveSelfIdAuthenticated(opts, {
21
+ const resolved = await resolveSelfIdAuthenticated(context, {
20
22
  whoami: async (received) => {
21
- expect(received).toBe(opts)
23
+ expect(received).toBe(context)
22
24
  return { id: 'child-kernel-principal', slug: 'child' }
23
25
  },
24
26
  })
@@ -28,17 +30,35 @@ describe('resolveSelfIdAuthenticated', () => {
28
30
 
29
31
  test('resolves an imported root without requiring a local registration', async () => {
30
32
  await expect(
31
- resolveSelfIdAuthenticated(
32
- { as: 'platform-root', url: 'https://manager.example/invoke' },
33
- { whoami: async () => ({ id: 'manager-kernel-principal' }) },
34
- ),
33
+ resolveSelfIdAuthenticated(context, {
34
+ whoami: async () => ({ id: 'manager-kernel-principal' }),
35
+ }),
35
36
  ).resolves.toEqual({ id: 'manager-kernel-principal' })
36
37
  })
37
38
 
38
39
  test('fails closed when authenticated whoami returns no NodeId', async () => {
39
40
  await expect(
40
- resolveSelfIdAuthenticated({}, { whoami: async () => ({ id: ' ' }) }),
41
- ).rejects.toMatchObject({ name: 'SelfResolutionError' })
41
+ resolveSelfIdAuthenticated(context, { whoami: async () => ({ id: ' ' }) }),
42
+ ).rejects.toMatchObject({ code: 'SELF_RESOLUTION_FAILED' })
43
+ })
44
+ })
45
+
46
+ describe('expandSelfInCall', () => {
47
+ test('expands only top-level string values after local parameter admission', async () => {
48
+ const callContext = {
49
+ auth: { whoami: async () => ({ id: 'caller-id' }) },
50
+ target: {},
51
+ } as ConnectionContext
52
+ const expanded = await expandSelfInCall(
53
+ '/:people.example.dev:class.Person:get',
54
+ { owner: '@self', count: 2, nested: { owner: '@self' } },
55
+ callContext,
56
+ )
57
+ expect(expanded.parameters).toEqual({
58
+ owner: '@caller-id',
59
+ count: 2,
60
+ nested: { owner: '@self' },
61
+ })
42
62
  })
43
63
  })
44
64
 
@@ -1,6 +1,6 @@
1
- import type { AuthApi } from '@astrale-os/kernel-client/auth'
2
- import type { GraphApi } from '@astrale-os/kernel-client/graph'
3
- import type { ClientSession } from '@astrale-os/kernel-client/session'
1
+ import type { AuthApi } from '@astrale-os/sdk/auth'
2
+ import type { GraphApi } from '@astrale-os/sdk/client'
3
+ import type { ClientSession } from '@astrale-os/sdk/client/session'
4
4
 
5
5
  import { issuer } from '@astrale-os/sdk/auth'
6
6
  import { describe, expect, test } from 'bun:test'
@@ -68,6 +68,7 @@ describe('connection target', () => {
68
68
  id: 'managed-id',
69
69
  slug,
70
70
  url: 'https://managed.example',
71
+ state: 'ready',
71
72
  }),
72
73
  }),
73
74
  ).toEqual({
@@ -1,4 +1,5 @@
1
1
  import type { AstraleConfig } from '../lib/config'
2
+ import type { ConnectionOptions, ConnectionTarget } from './target'
2
3
 
3
4
  import { AuthError } from '../errors'
4
5
  import { getDefault, getIdentity, type Identity } from '../identity/registry'
@@ -21,6 +22,18 @@ export type KeyIdentityAuthOptions = {
21
22
  audience: string
22
23
  }
23
24
 
25
+ /** Pin the local identity label before a session performs any authenticated effect. */
26
+ export async function bindCredentialIdentity<Options extends ConnectionOptions>(
27
+ options: Options,
28
+ target: ConnectionTarget,
29
+ ): Promise<Options> {
30
+ if (options.anonymous === true || options.creds !== undefined || options.as !== undefined) {
31
+ return options
32
+ }
33
+ const identity = target.defaultIdentity ?? (await getDefault()).name
34
+ return Object.freeze({ ...options, as: identity })
35
+ }
36
+
24
37
  /**
25
38
  * Resolve a signed JWT credential from CLI options.
26
39
  *
@@ -1,4 +1,4 @@
1
- import { call, type Call } from '@astrale-os/kernel-client'
1
+ import { call, type Call } from '@astrale-os/sdk/client'
2
2
  import { Path } from '@astrale-os/sdk/graph/path'
3
3
 
4
4
  /** Convert the CLI's untrusted text/JSON boundary into the one public Call representation. */
@@ -1,5 +1,5 @@
1
- import type { Fetch } from '@astrale-os/kernel-client'
2
- import type { SessionAuth } from '@astrale-os/kernel-client/session'
1
+ import type { Fetch } from '@astrale-os/sdk/client'
2
+ import type { SessionAuth } from '@astrale-os/sdk/client/session'
3
3
 
4
4
  import { credential, type IssuerId } from '@astrale-os/sdk/auth'
5
5