@astrale-os/cli 1.0.0-beta.4 → 1.0.0-beta.6

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 (173) hide show
  1. package/dist/astrale.js +55649 -59388
  2. package/dist/public/connect-core.js +1972 -26831
  3. package/dist/public/keys/index.js +1754 -40686
  4. package/dist/public/paths/index.js +859 -39511
  5. package/dist/types/admin/instance/model.d.ts +3 -4
  6. package/dist/types/errors.d.ts +1 -1
  7. package/dist/types/lib/idp.d.ts +1 -0
  8. package/dist/types/lib/instance-target.d.ts +1 -1
  9. package/dist/types/lib/log.d.ts +2 -2
  10. package/dist/types/lib/update.d.ts +30 -0
  11. package/package.json +4 -6
  12. package/src/__tests__/fixtures/publication.ts +20 -0
  13. package/src/admin/__tests__/binding.test.ts +14 -21
  14. package/src/admin/__tests__/fixture.ts +101 -0
  15. package/src/admin/binding.ts +86 -6
  16. package/src/admin/catalog/.spec/api.d.ts +2 -2
  17. package/src/admin/catalog/__tests__/client.test.ts +20 -49
  18. package/src/admin/catalog/client.ts +35 -26
  19. package/src/admin/catalog/model.ts +3 -4
  20. package/src/admin/graph/.spec/api.d.ts +4 -10
  21. package/src/admin/graph/nodes.ts +1 -1
  22. package/src/admin/instance/.spec/api.d.ts +2 -2
  23. package/src/admin/instance/__tests__/client.test.ts +58 -83
  24. package/src/admin/instance/client.ts +49 -39
  25. package/src/admin/instance/model.ts +9 -8
  26. package/src/commands/__tests__/call-describe.test.ts +46 -33
  27. package/src/commands/__tests__/domain-install-operation.test.ts +3 -14
  28. package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
  29. package/src/commands/__tests__/domain-list.test.ts +5 -25
  30. package/src/commands/__tests__/install-identity-override.test.ts +3 -14
  31. package/src/commands/__tests__/read-commands.test.ts +35 -10
  32. package/src/commands/__tests__/update.test.ts +25 -3
  33. package/src/commands/call-describe.ts +59 -67
  34. package/src/commands/call.ts +41 -54
  35. package/src/commands/domain/install.ts +2 -2
  36. package/src/commands/domain/uninstall.ts +2 -2
  37. package/src/commands/get.ts +13 -14
  38. package/src/commands/identity/__tests__/register.test.ts +3 -3
  39. package/src/commands/identity/register.ts +5 -4
  40. package/src/commands/instance/delete.ts +5 -3
  41. package/src/commands/instance/status.ts +1 -9
  42. package/src/commands/introspect.ts +4 -11
  43. package/src/commands/logs.ts +24 -25
  44. package/src/commands/mutate.ts +6 -5
  45. package/src/commands/query.ts +27 -30
  46. package/src/commands/token.ts +8 -7
  47. package/src/commands/update.ts +31 -11
  48. package/src/commands/view.ts +4 -3
  49. package/src/connection/.spec/api.d.ts +7 -8
  50. package/src/connection/.spec/architecture.md +5 -4
  51. package/src/connection/.spec/flows/session.ts +2 -2
  52. package/src/connection/.spec/laws/connection.ts +6 -2
  53. package/src/connection/.spec/layout.ts +1 -0
  54. package/src/connection/__tests__/credential.test.ts +1 -1
  55. package/src/connection/__tests__/errors.test.ts +27 -20
  56. package/src/connection/__tests__/exchange.test.ts +30 -1
  57. package/src/connection/__tests__/failure-fixtures.ts +39 -0
  58. package/src/connection/__tests__/failure-safety.test.ts +75 -0
  59. package/src/connection/__tests__/self.test.ts +31 -11
  60. package/src/connection/__tests__/session.test.ts +3 -3
  61. package/src/connection/call.ts +1 -1
  62. package/src/connection/credential.ts +2 -2
  63. package/src/connection/errors.ts +1 -355
  64. package/src/connection/exchange.ts +47 -23
  65. package/src/connection/failure/classify.ts +110 -0
  66. package/src/connection/failure/debug.ts +26 -0
  67. package/src/connection/failure/index.ts +18 -0
  68. package/src/connection/failure/model.ts +28 -0
  69. package/src/connection/failure/render.ts +116 -0
  70. package/src/connection/reasons.ts +16 -0
  71. package/src/connection/self.ts +39 -21
  72. package/src/connection/session.ts +8 -8
  73. package/src/errors.ts +3 -2
  74. package/src/graph/.spec/api.d.ts +12 -5
  75. package/src/graph/.spec/layout.ts +9 -1
  76. package/src/graph/__tests__/mutation.test.ts +2 -2
  77. package/src/graph/__tests__/query.test.ts +25 -6
  78. package/src/graph/class.ts +30 -0
  79. package/src/graph/index.ts +1 -0
  80. package/src/graph/query.ts +21 -32
  81. package/src/identity/.spec/api.d.ts +2 -2
  82. package/src/identity/__tests__/registration.test.ts +1 -1
  83. package/src/identity/registration.ts +3 -3
  84. package/src/lib/__tests__/domain-publication.test.ts +4 -15
  85. package/src/lib/__tests__/idp.test.ts +25 -0
  86. package/src/lib/__tests__/instance-target.test.ts +6 -39
  87. package/src/lib/__tests__/log-errors.test.ts +53 -0
  88. package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
  89. package/src/lib/__tests__/update.test.ts +101 -8
  90. package/src/lib/__tests__/view-open-intent.test.ts +0 -2
  91. package/src/lib/__tests__/view-server.test.ts +1 -1
  92. package/src/lib/__tests__/view-session.test.ts +1 -1
  93. package/src/lib/command-dx.ts +1 -4
  94. package/src/lib/domain-publication.ts +1 -1
  95. package/src/lib/idp.ts +29 -1
  96. package/src/lib/instance-target.ts +3 -14
  97. package/src/lib/log.ts +11 -6
  98. package/src/lib/update.ts +120 -26
  99. package/src/lib/validation.ts +2 -2
  100. package/src/lib/view/resolve.ts +1 -1
  101. package/src/program/__tests__/program.test.ts +1 -1
  102. package/studio/client/dist/assets/{elk-api-lwhFo7vB.js → elk-api-Di4OXGNH.js} +1 -1
  103. package/studio/client/dist/assets/index-BLm6J11H.js +81 -0
  104. package/studio/client/dist/assets/index-BiGR8Oo9.css +1 -0
  105. package/studio/client/dist/assets/index-DTjvkOUX.js +8 -0
  106. package/studio/client/dist/index.html +2 -2
  107. package/studio/package.json +2 -9
  108. package/studio/server/agent/ask.test.ts +1 -1
  109. package/studio/server/agent/bridge/grant.test.ts +1 -1
  110. package/studio/server/agent/bridge/routes.test.ts +1 -1
  111. package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
  112. package/studio/server/agent/prompts/anchors.test.ts +32 -36
  113. package/studio/server/agent/prompts/anchors.ts +20 -49
  114. package/studio/server/agent/prompts/system.test.ts +3 -2
  115. package/studio/server/agent/prompts/system.ts +3 -3
  116. package/studio/server/agent/routes.test.ts +1 -1
  117. package/studio/server/agent/run/coordinator.test.ts +1 -1
  118. package/studio/server/api/canvas.ts +0 -1
  119. package/studio/server/api-agent-loadout.test.ts +1 -1
  120. package/studio/server/api.test.ts +1 -1
  121. package/studio/server/cache.test.ts +23 -12
  122. package/studio/server/cache.ts +12 -15
  123. package/studio/server/domain.test.ts +50 -68
  124. package/studio/server/domain.ts +82 -63
  125. package/studio/server/handoff/copy.ts +1 -1
  126. package/studio/server/index.ts +1 -1
  127. package/studio/server/instances/probe.test.ts +1 -1
  128. package/studio/server/introspect/anatomy/views/routes.ts +46 -37
  129. package/studio/server/introspect/anatomy/views.ts +9 -14
  130. package/studio/server/introspect/anatomy-extras.test.ts +26 -42
  131. package/studio/server/introspect/anatomy.test.ts +22 -14
  132. package/studio/server/introspect/anatomy.ts +6 -9
  133. package/studio/server/introspect/bundle.ts +0 -3
  134. package/studio/server/introspect/canonical-schema.test.ts +94 -389
  135. package/studio/server/introspect/canonical-schema.ts +227 -457
  136. package/studio/server/introspect/core-extractor.ts +18 -127
  137. package/studio/server/introspect/core.ts +1 -2
  138. package/studio/server/introspect/diff.test.ts +11 -7
  139. package/studio/server/introspect/diff.ts +18 -55
  140. package/studio/server/introspect/extractor.ts +22 -117
  141. package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
  142. package/studio/server/introspect/overlay.test.ts +26 -54
  143. package/studio/server/introspect/overlay.ts +10 -37
  144. package/studio/server/introspect/revision.test.ts +1 -1
  145. package/studio/server/introspect/revision.ts +1 -1
  146. package/studio/server/introspect/runtime.test.ts +65 -183
  147. package/studio/server/introspect/runtime.ts +8 -25
  148. package/studio/server/introspect/schema-ir-json.test.ts +70 -0
  149. package/studio/server/introspect/schema-ir-json.ts +38 -68
  150. package/studio/server/introspect/schema-refs.test.ts +43 -88
  151. package/studio/server/introspect/schema-refs.ts +14 -49
  152. package/studio/server/introspect/source-overlay/handlers.ts +116 -636
  153. package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
  154. package/studio/server/introspect/source-overlay/spans.ts +15 -45
  155. package/studio/server/state/baseline.test.ts +14 -9
  156. package/studio/server/state/baseline.ts +3 -5
  157. package/studio/server/state/visibility.ts +1 -5
  158. package/studio/server/views/model.ts +3 -3
  159. package/studio/server/views/target.test.ts +17 -74
  160. package/studio/server/views/target.ts +24 -50
  161. package/studio/server/watch.test.ts +11 -11
  162. package/studio/server/watch.ts +8 -4
  163. package/studio/server/workspace-watch.ts +2 -2
  164. package/studio/shared/contracts/schema.ts +38 -107
  165. package/studio/shared/contracts/surface.test.ts +13 -15
  166. package/studio/shared/contracts/workspace.ts +2 -11
  167. package/studio/shared/schema/identity.test.ts +20 -44
  168. package/studio/shared/schema/identity.ts +9 -23
  169. package/viewer/dist/main.js +40 -61
  170. package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
  171. package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
  172. package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
  173. package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
@@ -24,17 +24,16 @@ export interface DomainInstallReceipt {
24
24
  readonly installedRevision?: string;
25
25
  readonly error?: string;
26
26
  }
27
- export declare class AdminInstanceNotFoundError extends Error {
27
+ export declare class AdminInstanceNotFoundError extends AstraleError {
28
28
  readonly identifier: string;
29
- readonly name = "NotFoundError";
30
29
  constructor(identifier: string);
31
30
  }
32
- export declare class AdminHostNotFoundError extends Error {
31
+ export declare class AdminHostNotFoundError extends AstraleError {
33
32
  readonly identifier: string;
34
- readonly name = "NotFoundError";
35
33
  constructor(identifier: string);
36
34
  }
37
35
  /** Match an owner-scoped Instance by slug, canonical Node Path, or bare Node id. */
38
36
  export declare function findOwnedInstance(instances: readonly OwnedInstanceInfo[], identifier: string): OwnedInstanceInfo | undefined;
39
37
  /** Human-readable location of an Instance (state, region, and Host Path). */
40
38
  export declare function formatInstanceLocation(info: InstanceInfo): string;
39
+ import { AstraleError } from '../../errors';
@@ -1,7 +1,7 @@
1
1
  export declare class AstraleError extends Error {
2
2
  code: string;
3
3
  hint?: string;
4
- constructor(code: string, message: string, hint?: string);
4
+ constructor(code: string, message: string, hint?: string, options?: ErrorOptions);
5
5
  }
6
6
  export declare class AuthError extends AstraleError {
7
7
  constructor(message: string, hint?: string);
@@ -322,6 +322,7 @@ export declare function fetchOidcMetadata(issuer: string): Promise<OidcMetadata>
322
322
  export declare function workosAuthKitMetadata(apiHost?: string, clientId?: string): OidcMetadata;
323
323
  export declare function builtinIdpConfig(name: string, clientIdOverride?: string, env?: NodeJS.ProcessEnv): IdpConfig | null;
324
324
  export declare function workosClientIdFromEnv(env?: NodeJS.ProcessEnv): string | undefined;
325
+ export declare function legacyBuiltinWorkosReplacement(existing: IdpConfig, clientIdOverride?: string, env?: NodeJS.ProcessEnv): IdpConfig | undefined;
325
326
  export declare function postForm(url: string, params: URLSearchParams): Promise<TokenResponse>;
326
327
  export declare function postJson(url: string, bodyInput: Record<string, string>): Promise<TokenResponse>;
327
328
  export declare function tokenExpiresAt(token: TokenResponse): string | undefined;
@@ -1,5 +1,5 @@
1
- import type { InstanceInfo } from '../admin/instance/model';
2
1
  import type { AstraleConfig } from './config';
2
+ import { type InstanceInfo } from '../admin/instance/model';
3
3
  import { type AdminTargetCommandOpts, type ResolvedAdminTarget } from './admin-target';
4
4
  import { type InstanceStore } from './instance';
5
5
  export type InstanceTargetRequest = {
@@ -11,8 +11,8 @@ export declare const log: {
11
11
  /** Report an error with hint (when present) and exit. `--json` / `--ci` / a
12
12
  * non-TTY stdout always get one structured JSON line on stderr. */
13
13
  export declare function fatal(e: unknown, opts?: MachineOpts): never;
14
- /** Admit expected invalid input and exit through {@link fatal}. */
15
- export declare function failClosed(error: unknown, opts?: MachineOpts): never;
14
+ /** Project only a caller-authored admission failure into the CLI input family. */
15
+ export declare function failInput(error: unknown, opts?: MachineOpts): never;
16
16
  /** Shortcut for stub commands that aren't wired in v1 (§15). */
17
17
  export declare function fatalNotImplemented(feature: string, hint?: string): never;
18
18
  /**
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { AstraleError } from '../errors';
2
3
  export declare const DEFAULT_UPDATE_CHANNEL = "beta";
3
4
  export declare const InstallMetadataSchema: z.ZodObject<{
4
5
  method: z.ZodLiteral<"script">;
@@ -73,8 +74,26 @@ export type UpdateRequest = {
73
74
  currentVersion: string;
74
75
  platform?: Platform;
75
76
  installPath?: string;
77
+ execution?: UpdateExecution;
76
78
  };
79
+ export type UpdateExecution = {
80
+ kind: 'standalone';
81
+ executable: string;
82
+ } | {
83
+ kind: 'package-managed';
84
+ executable: string;
85
+ };
86
+ declare const admittedScriptInstall: unique symbol;
87
+ export type AdmittedScriptInstall = Readonly<{
88
+ metadata: InstallMetadata;
89
+ executable: string;
90
+ [admittedScriptInstall]: true;
91
+ }>;
77
92
  export type UpdateResult = {
93
+ status: 'managed';
94
+ currentVersion: string;
95
+ executable: string;
96
+ } | {
78
97
  status: 'up-to-date';
79
98
  currentVersion: string;
80
99
  latestVersion: string;
@@ -93,9 +112,20 @@ export type UpdateResult = {
93
112
  };
94
113
  export declare function detectPlatform(): Platform;
95
114
  export declare function platformKey(platform: Platform): string;
115
+ export declare function classifyUpdateExecution(input: {
116
+ bunVersion?: string;
117
+ executable: string;
118
+ entry?: string;
119
+ }): UpdateExecution;
120
+ export declare function detectUpdateExecution(): UpdateExecution;
121
+ export declare function packageManagedUpdateError(executable: string): AstraleError;
96
122
  export declare function readInstallMetadata(path?: string): Promise<InstallMetadata>;
123
+ export declare function admitScriptInstall(meta: InstallMetadata, execution: Extract<UpdateExecution, {
124
+ kind: 'standalone';
125
+ }>): Promise<AdmittedScriptInstall>;
97
126
  export declare function writeInstallMetadata(meta: InstallMetadata, path?: string): Promise<void>;
98
127
  export declare function releaseBase(meta: InstallMetadata, req: Pick<UpdateRequest, 'channel' | 'version'>): string;
99
128
  export declare function fetchManifest(base: string): Promise<UpdateManifest>;
100
129
  export declare function shouldUpdate(currentVersion: string, manifestVersion: string): boolean;
101
130
  export declare function updateAstrale(req: UpdateRequest): Promise<UpdateResult>;
131
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/cli",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.6",
4
4
  "description": "Astrale CLI — connect to existing Astrale kernels",
5
5
  "keywords": [
6
6
  "astrale",
@@ -62,11 +62,9 @@
62
62
  "zod": "^3.25.0"
63
63
  },
64
64
  "devDependencies": {
65
- "@astrale-os/kernel-client": ">=0.6.0-beta.4 <1.0.0",
66
- "@astrale-os/kernel-protocol": ">=0.5.0-beta.3 <1.0.0",
67
65
  "@astrale-os/ox": ">=0.1.3 <1.0.0",
68
- "@astrale-os/sdk": ">=0.5.0-beta.9 <1.0.0",
69
- "@astrale-os/shell": ">=0.3.8-beta.2 <1.0.0",
66
+ "@astrale-os/sdk": ">=0.5.0-beta.17 <1.0.0",
67
+ "@astrale-os/shell": ">=0.4.0 <1.0.0",
70
68
  "@astrale/commitlint-config": "npm:@jsr/astrale__commitlint-config@~2.0.1",
71
69
  "@commitlint/cli": "~20.3.1",
72
70
  "@commitlint/config-conventional": "~20.3.1",
@@ -108,7 +106,7 @@
108
106
  "lint:fix": "oxlint --fix .",
109
107
  "format": "pnpm exec oxfmt --write .",
110
108
  "format:check": "pnpm exec oxfmt --check .",
111
- "test": "bun test src studio/client/src studio/server studio/shared && node --test scripts/*.test.mjs",
109
+ "test": "node scripts/qualification/source-boundary.mjs --target && bun test src studio/client/src studio/server studio/shared && node --test scripts/*.test.mjs",
112
110
  "test:studio": "pnpm --dir studio test",
113
111
  "test:watch": "bun test --watch src"
114
112
  }
@@ -0,0 +1,20 @@
1
+ import type { schema } from '@astrale-os/sdk/schema'
2
+
3
+ import { defineApplication } from '@astrale-os/sdk/application'
4
+ import { compile } from '@astrale-os/sdk/deployment/build'
5
+ import { addressing, assemble } from '@astrale-os/sdk/deployment/release'
6
+ import { defineRuntime } from '@astrale-os/sdk/runtime'
7
+
8
+ /** Build a canonical empty-runtime Release for cross-boundary CLI tests. */
9
+ export function releaseFor<const Schema extends schema.DomainSchema>(
10
+ source: Schema,
11
+ issuer: string,
12
+ ) {
13
+ const runtime = defineRuntime<Schema>()({
14
+ integrations: {},
15
+ initialize: () => ({ providers: {} }),
16
+ actions: [],
17
+ workflows: [],
18
+ })
19
+ return assemble(compile(defineApplication({ schema: source, runtime })), addressing(issuer))
20
+ }
@@ -1,38 +1,31 @@
1
- import type { ClientSession } from '@astrale-os/kernel-client/session'
1
+ import type { ClientSession } from '@astrale-os/sdk/client/session'
2
2
 
3
- import { defineDomain } from '@astrale-os/sdk'
4
- import { issuer } from '@astrale-os/sdk/auth'
5
- import { createDeployment } from '@astrale-os/sdk/deployment'
6
- import { defineSchema } from '@astrale-os/sdk/schema/v1'
3
+ import { bundle, defineSchema, schema as schemaApi } from '@astrale-os/sdk/schema'
7
4
  import { expect, mock, test } from 'bun:test'
8
5
 
6
+ import { releaseFor } from '../../__tests__/fixtures/publication'
9
7
  import { bindAdmin } from '../binding'
10
8
 
11
9
  const schema = defineSchema('admin.astrale.ai', {})
12
- const deployment = createDeployment({
13
- definition: defineDomain({
14
- schema,
15
- handlers: { functions: {}, classes: {}, interfaces: {} },
16
- }),
17
- issuer: issuer.accept('https://admin.beta.astrale.ai'),
18
- bundleHref: 'https://admin.beta.astrale.ai/domain.bundle.json',
19
- bindings: { callables: [] },
20
- })
10
+ const release = releaseFor(schema, 'https://admin.beta.astrale.ai')
21
11
 
22
12
  test('binds the installed Admin Domain instead of the source Kernel publication', async () => {
23
- const installed = mock(async () => ({
13
+ const installation = mock(async () => ({
24
14
  state: 'ready' as const,
25
15
  target: 'sha256:admin-target' as const,
26
- source: { kind: 'remote' as const, publication: deployment.publication },
27
- bundle: deployment.bundle,
16
+ source: { kind: 'remote' as const, publication: release.publication },
17
+ bundle: bundle.create(schema),
28
18
  readiness: 'sha256:admin-readiness' as const,
29
19
  capabilities: { requested: [], materialized: [] },
30
20
  }))
31
- const session = { installed } as unknown as ClientSession
21
+ const session = {
22
+ installation,
23
+ bind: (domain: unknown) => ({ domain, graph: {} }),
24
+ } as unknown as ClientSession
32
25
 
33
26
  const binding = await bindAdmin(session)
34
27
 
35
- expect(installed).toHaveBeenCalledWith('admin.astrale.ai')
36
- expect(binding.$.origin).toBe('admin.astrale.ai')
37
- expect(String(binding.$.publication?.identity.issuer)).toBe('https://admin.beta.astrale.ai')
28
+ expect(installation).toHaveBeenCalledWith('admin.astrale.ai')
29
+ expect(binding.domain.origin).toBe('admin.astrale.ai')
30
+ expect(binding.domain.revision).toBe(schemaApi.revision(schema))
38
31
  })
@@ -0,0 +1,101 @@
1
+ import type { ClientSession } from '@astrale-os/sdk/client/session'
2
+ import type { CallableReference } from '@astrale-os/sdk/client/session'
3
+
4
+ import {
5
+ callableAddress,
6
+ core,
7
+ defineSchema,
8
+ edgeClass,
9
+ method,
10
+ nodeClass,
11
+ property,
12
+ schema,
13
+ } from '@astrale-os/sdk/schema'
14
+ import { mock } from 'bun:test'
15
+
16
+ import type { AdminBinding } from '../binding'
17
+
18
+ const operation = () =>
19
+ method({
20
+ auth: 'authenticated',
21
+ input: { type: 'object', additionalProperties: true },
22
+ output: { type: 'object', additionalProperties: true },
23
+ })
24
+
25
+ const Domain = nodeClass({
26
+ properties: {
27
+ origin: property({ type: 'string' }, { required: true }),
28
+ name: property({ type: 'string' }, { required: true }),
29
+ discoveryUrl: property({ type: 'string' }, { required: true }),
30
+ description: property({ type: 'string' }),
31
+ createdAt: property({ type: 'string' }, { required: true }),
32
+ updatedAt: property({ type: 'string' }, { required: true }),
33
+ },
34
+ methods: { configureDefault: operation() },
35
+ })
36
+ const Fleet = nodeClass({
37
+ methods: {
38
+ publishDomain: operation(),
39
+ listInstances: operation(),
40
+ createInstance: operation(),
41
+ },
42
+ })
43
+ const Host = nodeClass({
44
+ properties: {
45
+ id: property({ type: 'string' }, { required: true }),
46
+ state: property({ type: 'string' }, { required: true }),
47
+ },
48
+ methods: { createInstance: operation() },
49
+ })
50
+ const Instance = nodeClass({
51
+ methods: {
52
+ status: operation(),
53
+ delete: operation(),
54
+ installDomain: operation(),
55
+ },
56
+ })
57
+ const fleet_installs_domain_by_default = edgeClass.directed({
58
+ source: { as: 'fleet', accepts: [Fleet], outgoing: '0..*' },
59
+ target: { as: 'domain', accepts: [Domain], incoming: '0..*' },
60
+ })
61
+ const fleet_reserves_admin_host = edgeClass.directed({
62
+ source: { as: 'fleet', accepts: [Fleet], outgoing: '0..1' },
63
+ target: { as: 'host', accepts: [Host], incoming: '0..1' },
64
+ })
65
+
66
+ export const AdminTestSchema = defineSchema('admin.astrale.ai', {
67
+ classes: {
68
+ Domain,
69
+ Fleet,
70
+ Host,
71
+ Instance,
72
+ fleet_installs_domain_by_default,
73
+ fleet_reserves_admin_host,
74
+ },
75
+ core: { nodes: { fleet: core.node(() => Fleet, {}) }, edges: [] },
76
+ })
77
+
78
+ export const AdminTestDomain = schema.resolve(AdminTestSchema)
79
+
80
+ export function adminBinding(): AdminBinding {
81
+ return { domain: AdminTestDomain, graph: {} } as unknown as AdminBinding
82
+ }
83
+
84
+ export function adminSession(
85
+ implementation?: (
86
+ method: { readonly owner: string; readonly name: string },
87
+ receiver: unknown,
88
+ input: unknown,
89
+ ) => unknown,
90
+ ) {
91
+ const invoke = mock(async (reference: CallableReference, input: unknown) => {
92
+ const address = callableAddress(reference.callable)
93
+ if (address.kind !== 'method') throw new Error('Expected an Admin Method.')
94
+ return implementation?.(
95
+ { owner: address.owner.name, name: address.name },
96
+ reference.target,
97
+ input,
98
+ )
99
+ })
100
+ return { invoke, session: { invoke } as unknown as ClientSession }
101
+ }
@@ -1,7 +1,16 @@
1
- import type { DomainBinding } from '@astrale-os/kernel-client/domain'
2
- import type { ClientSession } from '@astrale-os/kernel-client/session'
1
+ import type { Input } from '@astrale-os/sdk/client'
2
+ import type { ClientSession } from '@astrale-os/sdk/client/session'
3
+ import type {
4
+ ResolvedClass,
5
+ ResolvedCoreDefinition,
6
+ ResolvedMethod,
7
+ ResolvedProperty,
8
+ } from '@astrale-os/sdk/schema'
9
+ import type { DomainBinding } from '@astrale-os/shell'
3
10
 
4
- import { bind } from '@astrale-os/kernel-client/domain'
11
+ import { reference } from '@astrale-os/sdk/client/session'
12
+ import { Path } from '@astrale-os/sdk/graph/path'
13
+ import { bindDomain } from '@astrale-os/shell'
5
14
 
6
15
  const ADMIN_ORIGIN = 'admin.astrale.ai'
7
16
 
@@ -9,10 +18,81 @@ export type AdminBinding = DomainBinding
9
18
 
10
19
  /** Bind the exact Admin revision installed on this source Kernel. */
11
20
  export async function bindAdmin(session: ClientSession): Promise<AdminBinding> {
12
- const installed = await session.installed(ADMIN_ORIGIN)
13
- const binding = bind(session, installed)
14
- if (binding.$.publication?.origin !== ADMIN_ORIGIN || binding.$.origin !== ADMIN_ORIGIN) {
21
+ const installed = await session.installation(ADMIN_ORIGIN)
22
+ return requireAdminBinding(await bindDomain(session, installed.bundle.root))
23
+ }
24
+
25
+ /** Admit an injected dynamic binding as the Admin Domain. */
26
+ export function requireAdminBinding(binding: AdminBinding): AdminBinding {
27
+ if (binding.domain.origin !== ADMIN_ORIGIN) {
15
28
  throw new TypeError('Configured Admin target does not serve the Admin Domain.')
16
29
  }
17
30
  return binding
18
31
  }
32
+
33
+ /** Resolve one required Admin Class without rebuilding its schema contract. */
34
+ export function requireAdminClass(
35
+ binding: AdminBinding,
36
+ name: string,
37
+ kind: 'node',
38
+ ): ResolvedClass<'node'>
39
+ export function requireAdminClass(
40
+ binding: AdminBinding,
41
+ name: string,
42
+ kind: 'edge',
43
+ ): ResolvedClass<'edge'>
44
+ export function requireAdminClass(
45
+ binding: AdminBinding,
46
+ name: string,
47
+ kind: 'node' | 'edge',
48
+ ): ResolvedClass<'node'> | ResolvedClass<'edge'> {
49
+ const selected = binding.domain.classes[name]
50
+ if (selected?.kind !== kind) throw new TypeError(`Admin ${name} is not a ${kind} Class.`)
51
+ return selected
52
+ }
53
+
54
+ /** Resolve one required Admin Core Node as its canonical projection Path. */
55
+ export function requireAdminCore(binding: AdminBinding, name: string): Path {
56
+ const selected = binding.domain.core.nodes[name] as ResolvedCoreDefinition | undefined
57
+ if (selected === undefined) throw new TypeError(`Admin has no Core Node ${name}.`)
58
+ return Path.project(selected.ref)
59
+ }
60
+
61
+ /** Resolve one effective Property by unambiguous member name. */
62
+ export function requireAdminProperty(
63
+ owner: ResolvedClass,
64
+ name: string,
65
+ ): ResolvedProperty<unknown> {
66
+ const selected = [...owner.properties].find((property) => property.name === name)
67
+ if (selected === undefined) {
68
+ throw new TypeError(`Admin ${owner.ref.name}.${name} Property is absent.`)
69
+ }
70
+ return selected
71
+ }
72
+
73
+ /** Invoke one resolved executable instance Method through Kernel Client. */
74
+ export async function invokeAdminMethod(
75
+ session: ClientSession,
76
+ binding: AdminBinding,
77
+ owner: ResolvedClass<'node'>,
78
+ name: string,
79
+ receiver: Path,
80
+ input: Input,
81
+ ): Promise<unknown> {
82
+ const method = [...owner.methods].find((candidate) => candidate.name === name)
83
+ if (method === undefined || !isExecutableInstanceMethod(method)) {
84
+ throw new TypeError(`Admin ${owner.ref.name}.${name} is not an executable instance Method.`)
85
+ }
86
+ return session.invoke(reference(binding.domain, method)(receiver), input)
87
+ }
88
+
89
+ type ExecutableInstanceMethod = ResolvedMethod<
90
+ unknown,
91
+ unknown,
92
+ false,
93
+ Exclude<ResolvedMethod['inheritance'], 'abstract'>
94
+ >
95
+
96
+ function isExecutableInstanceMethod(method: ResolvedMethod): method is ExecutableInstanceMethod {
97
+ return method.static === false && method.inheritance !== 'abstract'
98
+ }
@@ -1,5 +1,5 @@
1
- import type { DomainBinding } from '@astrale-os/kernel-client/domain'
2
- import type { ClientSession } from '@astrale-os/kernel-client/session'
1
+ import type { ClientSession } from '@astrale-os/sdk/client/session'
2
+ import type { DomainBinding } from '@astrale-os/shell'
3
3
 
4
4
  import type { AdminGraphApi } from '../../graph/.spec/api.js'
5
5
 
@@ -1,41 +1,25 @@
1
1
  import type { Node } from '@astrale-os/sdk/graph/node'
2
2
  import type { QueryAST } from '@astrale-os/sdk/query'
3
3
 
4
- import { ClassPath } from '@astrale-os/sdk/graph/class'
5
4
  import { NodeId } from '@astrale-os/sdk/graph/node'
6
- import { Path } from '@astrale-os/sdk/graph/path'
7
5
  import { describe, expect, mock, test } from 'bun:test'
8
6
 
9
- import type { AdminBinding } from '../../binding'
10
7
  import type { AdminGraphApi } from '../../graph'
11
8
 
9
+ import { AdminTestDomain, adminBinding, adminSession } from '../../__tests__/fixture'
12
10
  import { connectAdminCatalog } from '../client'
13
11
 
14
- function definition(name: string) {
15
- return {
16
- name,
17
- $: {
18
- method(method: string) {
19
- return { owner: name, name: method }
20
- },
21
- property(property: string) {
22
- return { key: property }
23
- },
24
- },
25
- }
26
- }
27
-
28
12
  function domainNode(id: string, origin: string): Node {
29
13
  return {
30
14
  id: NodeId(id),
31
- class: ClassPath.from('admin.astrale.ai', 'Domain'),
32
- props: {
15
+ class: AdminTestDomain.classes.Domain.key,
16
+ props: AdminTestDomain.classes.Domain.properties.from({
33
17
  origin,
34
18
  name: origin.split('.')[0]!,
35
19
  discoveryUrl: `https://${origin}`,
36
20
  createdAt: '2026-08-12T00:00:00.000Z',
37
21
  updatedAt: '2026-08-12T00:00:00.000Z',
38
- } as never,
22
+ }),
39
23
  }
40
24
  }
41
25
 
@@ -44,24 +28,16 @@ function fixture(input: {
44
28
  defaults?: readonly Node[]
45
29
  invoke?: (method: { owner: string; name: string }, receiver: unknown, value: unknown) => unknown
46
30
  }) {
47
- const Domain = definition('Domain')
48
- const Fleet = definition('Fleet')
49
- const invoke = mock(async (method: unknown, receiver: unknown, value: unknown) =>
50
- input.invoke?.(method as { owner: string; name: string }, receiver, value),
51
- )
52
- const binding = {
53
- $: {
54
- publication: { origin: 'admin.astrale.ai' },
55
- origin: 'admin.astrale.ai',
56
- class(name: string) {
57
- if (name === 'Domain') return Domain
58
- if (name === 'Fleet') return Fleet
59
- throw new Error(`Unexpected class ${name}`)
60
- },
61
- core: { nodes: { fleet: { path: Path.id(NodeId('fleet')) } } },
62
- invoke,
63
- },
64
- } as unknown as AdminBinding
31
+ const calls: Array<{
32
+ method: { owner: string; name: string }
33
+ receiver: string
34
+ value: unknown
35
+ }> = []
36
+ const remote = adminSession((method, receiver, value) => {
37
+ calls.push({ method, receiver: String(receiver), value })
38
+ return input.invoke?.(method, receiver, value)
39
+ })
40
+ const binding = adminBinding()
65
41
  const query = mock(async (_ast: QueryAST) => ({
66
42
  result: {
67
43
  kind: 'nodes' as const,
@@ -79,10 +55,11 @@ function fixture(input: {
79
55
  }))
80
56
  const graph = { query, neighbors } as unknown as AdminGraphApi
81
57
  return {
82
- invoke,
58
+ invoke: remote.invoke,
59
+ calls,
83
60
  connect: () =>
84
61
  connectAdminCatalog(
85
- { session: {} as never, graph },
62
+ { session: remote.session, graph },
86
63
  {
87
64
  bind: async () => binding,
88
65
  operationId: (kind) => `cli.domain.${kind}:test`,
@@ -130,16 +107,10 @@ describe('V2 Admin Domain catalog adapter', () => {
130
107
  installByDefault: true,
131
108
  }),
132
109
  ).resolves.toMatchObject({ changed: true, isNew: true, entry: { installByDefault: true } })
133
- expect(
134
- contract.invoke.mock.calls.map(([method, receiver, value]) => ({
135
- method,
136
- receiver: String(receiver),
137
- value,
138
- })),
139
- ).toEqual([
110
+ expect(contract.calls).toEqual([
140
111
  {
141
112
  method: { owner: 'Fleet', name: 'publishDomain' },
142
- receiver: '@fleet',
113
+ receiver: '/:admin.astrale.ai:core.fleet::publishDomain',
143
114
  value: {
144
115
  operationId: 'cli.domain.publish:test',
145
116
  origin: 'crm.acme.dev',
@@ -149,7 +120,7 @@ describe('V2 Admin Domain catalog adapter', () => {
149
120
  },
150
121
  {
151
122
  method: { owner: 'Domain', name: 'configureDefault' },
152
- receiver: '@crm-domain',
123
+ receiver: '@crm-domain::configureDefault',
153
124
  value: {
154
125
  operationId: 'cli.domain.configure-default:test',
155
126
  enabled: true,