@astrale-os/cli 1.0.0-beta.12 → 1.0.0-beta.13
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.
- package/dist/astrale.js +15432 -14745
- package/dist/types/admin/contract.d.ts +26 -0
- package/dist/types/admin/instance/client.d.ts +2 -4
- package/dist/types/admin/instance/model.d.ts +3 -0
- package/package.json +1 -1
- package/src/admin/.spec/architecture.md +12 -5
- package/src/admin/__tests__/fixture.ts +19 -95
- package/src/admin/catalog/.spec/api.d.ts +0 -2
- package/src/admin/catalog/.spec/architecture.md +5 -4
- package/src/admin/catalog/__tests__/client.test.ts +136 -33
- package/src/admin/catalog/client.ts +38 -61
- package/src/admin/contract.ts +46 -0
- package/src/admin/instance/.spec/api.d.ts +4 -8
- package/src/admin/instance/.spec/architecture.md +7 -7
- package/src/admin/instance/__tests__/client.test.ts +138 -31
- package/src/admin/instance/client.ts +32 -37
- package/src/admin/instance/model.ts +3 -0
- package/src/commands/__tests__/call.test.ts +34 -0
- package/src/commands/__tests__/read-commands.test.ts +27 -0
- package/src/commands/__tests__/token-ttl.test.ts +49 -4
- package/src/commands/call.ts +28 -3
- package/src/commands/query.ts +8 -2
- package/src/commands/token.ts +34 -20
- package/src/commands/ui/__tests__/commands.test.ts +129 -0
- package/src/commands/ui/add.ts +51 -0
- package/src/commands/ui/doctor.ts +13 -0
- package/src/commands/ui/init.ts +38 -0
- package/src/commands/ui/list.ts +26 -0
- package/src/commands/ui/preset-apply.ts +19 -0
- package/src/commands/ui/preset-list.ts +12 -0
- package/src/commands/ui/shared.ts +25 -0
- package/src/lib/__tests__/binary.test.ts +16 -1
- package/src/lib/binary.ts +22 -5
- package/src/lib/proc.ts +7 -2
- package/src/program/.spec/api.d.ts +1 -0
- package/src/program/__tests__/program.test.ts +32 -2
- package/src/program/build.ts +23 -1
- package/src/program/command.ts +1 -0
- package/src/program/registry.ts +2 -1
- package/src/ui/.spec/api.d.ts +14 -0
- package/src/ui/.spec/architecture.md +10 -0
- package/src/ui/.spec/laws.ts +36 -0
- package/src/ui/.spec/layout.ts +16 -0
- package/src/ui/__tests__/ui.test.ts +542 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/lock.ts +87 -0
- package/src/ui/model.ts +83 -0
- package/src/ui/operations.ts +539 -0
- package/src/ui/project.ts +146 -0
- package/src/ui/release.ts +267 -0
- package/src/ui/runner.ts +18 -0
- package/studio/server/agent/harness/gateway/token.test.ts +1 -1
- package/studio/server/agent/harness/gateway/token.ts +3 -3
- package/dist/types/admin/binding.d.ts +0 -19
- package/src/admin/__tests__/binding.test.ts +0 -31
- package/src/admin/binding.ts +0 -98
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Input } from '@astrale-os/sdk/client';
|
|
2
|
+
import type { ClientSession } from '@astrale-os/sdk/client/session';
|
|
3
|
+
import type { ClassRef } from '@astrale-os/sdk/schema';
|
|
4
|
+
import { Path } from '@astrale-os/sdk/graph/path';
|
|
5
|
+
export declare const AdminContract: Readonly<{
|
|
6
|
+
origin: "admin.astrale.ai";
|
|
7
|
+
fleet: Path;
|
|
8
|
+
classes: Readonly<{
|
|
9
|
+
Domain: ClassRef;
|
|
10
|
+
}>;
|
|
11
|
+
edges: Readonly<{
|
|
12
|
+
fleetInstallsDomainByDefault: ClassRef;
|
|
13
|
+
}>;
|
|
14
|
+
properties: Readonly<{
|
|
15
|
+
domain: Readonly<{
|
|
16
|
+
origin: `${string}:class.${string}.property.origin`;
|
|
17
|
+
name: "kernel.astrale.ai:class.Named.property.name";
|
|
18
|
+
discoveryUrl: `${string}:class.${string}.property.discoveryUrl`;
|
|
19
|
+
description: "kernel.astrale.ai:class.Descriptable.property.description";
|
|
20
|
+
createdAt: "kernel.astrale.ai:class.Timestamped.property.createdAt";
|
|
21
|
+
updatedAt: "kernel.astrale.ai:class.Timestamped.property.updatedAt";
|
|
22
|
+
}>;
|
|
23
|
+
}>;
|
|
24
|
+
}>;
|
|
25
|
+
/** Invoke one stable Admin instance Method without schema discovery or reflection. */
|
|
26
|
+
export declare function callAdminMethod(session: ClientSession, receiver: Path, method: string, input: Input): Promise<unknown>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ClientSession } from '@astrale-os/sdk/client/session';
|
|
2
|
-
import { type AdminBinding } from '../binding';
|
|
3
2
|
import { type DomainInstallReceipt, type InstanceInfo, type OwnedInstanceInfo } from './model';
|
|
4
3
|
export interface AdminInstanceContext {
|
|
5
4
|
readonly session: ClientSession;
|
|
@@ -12,11 +11,10 @@ export interface AdminInstanceApi {
|
|
|
12
11
|
installDomain(identifier: string, domain: string): Promise<DomainInstallReceipt>;
|
|
13
12
|
}
|
|
14
13
|
export interface AdminInstanceDependencies {
|
|
15
|
-
readonly bind?: (session: ClientSession) => Promise<AdminBinding>;
|
|
16
14
|
readonly operationId?: (kind: 'create' | 'status' | 'delete' | 'install-domain') => string;
|
|
17
15
|
}
|
|
18
16
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* Connect the public Instance journey through stable Admin call paths. Routine
|
|
18
|
+
* operations perform no schema discovery. No Host lifecycle method is present.
|
|
21
19
|
*/
|
|
22
20
|
export declare function connectAdminInstances(context: AdminInstanceContext, dependencies?: AdminInstanceDependencies): Promise<AdminInstanceApi>;
|
|
@@ -4,10 +4,13 @@ export interface InstanceInfo {
|
|
|
4
4
|
readonly id: string;
|
|
5
5
|
readonly slug: string;
|
|
6
6
|
readonly url: string;
|
|
7
|
+
readonly hostId?: string;
|
|
8
|
+
readonly region?: string;
|
|
7
9
|
readonly state: InstanceState;
|
|
8
10
|
readonly phase?: string;
|
|
9
11
|
readonly error?: string | null;
|
|
10
12
|
readonly createdAt?: string;
|
|
13
|
+
readonly updatedAt?: string;
|
|
11
14
|
readonly organizationId?: string;
|
|
12
15
|
}
|
|
13
16
|
/** An Instance returned through the caller-scoped Admin graph inventory. */
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# CLI Admin adapters
|
|
2
2
|
|
|
3
|
-
This private aggregate translates the frozen CLI command journey into the
|
|
4
|
-
`admin.astrale.ai` contract.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Host directly, or persist remote graph truth.
|
|
3
|
+
This private aggregate translates the frozen CLI command journey into the stable public
|
|
4
|
+
`admin.astrale.ai` contract. Routine commands use canonical Admin Class, Property, Core receiver,
|
|
5
|
+
and Method paths through Kernel Client. They do not download, inspect, bind, or rebuild the Admin
|
|
6
|
+
schema. Strict local decoders admit every untrusted Admin result. The adapter does not import Admin
|
|
7
|
+
implementation code, call Host directly, or persist remote graph truth.
|
|
8
8
|
|
|
9
9
|
`instance` owns create/list/status/delete/install projection and explicitly excludes the reserved
|
|
10
10
|
Admin Host from consumer placement. `catalog` owns Domain inventory, publication, and default-set
|
|
@@ -13,3 +13,10 @@ configuration. `graph` is their bounded cursor helper and is not re-exported by
|
|
|
13
13
|
Commander grammar, help text, output shapes, bookmark behavior, and current interactive host
|
|
14
14
|
selection remain owned by the existing command surface; these adapters change only its remote
|
|
15
15
|
semantic owner.
|
|
16
|
+
|
|
17
|
+
## Performance contract
|
|
18
|
+
|
|
19
|
+
`instance list` performs one Admin method call after connection setup and no schema-discovery call.
|
|
20
|
+
Against the production beta Admin endpoint, the complete command must finish in less than 1 second
|
|
21
|
+
with a warm route/cache and less than 2 seconds from a cold CLI process. Unit tests prove the
|
|
22
|
+
single-call/no-reflection shape; retained live evidence proves the wall-clock bounds.
|
|
@@ -1,101 +1,25 @@
|
|
|
1
|
+
import type { Call } from '@astrale-os/sdk/client'
|
|
1
2
|
import type { ClientSession } from '@astrale-os/sdk/client/session'
|
|
2
|
-
import type { CallableReference } from '@astrale-os/sdk/client/session'
|
|
3
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
4
|
import { mock } from 'bun:test'
|
|
15
5
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
)
|
|
6
|
+
export function adminSession(implementation?: (target: string, input: unknown) => unknown): {
|
|
7
|
+
readonly call: ReturnType<typeof mock>
|
|
8
|
+
readonly reflection: ReturnType<typeof mock>
|
|
9
|
+
readonly session: ClientSession
|
|
10
|
+
} {
|
|
11
|
+
const call = mock(async (request: Call) =>
|
|
12
|
+
implementation?.(String(request.target), request.input),
|
|
13
|
+
)
|
|
14
|
+
const reflection = mock(() => {
|
|
15
|
+
throw new Error('Routine Admin commands must not perform schema discovery or reflection.')
|
|
99
16
|
})
|
|
100
|
-
|
|
17
|
+
const session = {
|
|
18
|
+
call,
|
|
19
|
+
installation: reflection,
|
|
20
|
+
snapshot: reflection,
|
|
21
|
+
bind: reflection,
|
|
22
|
+
invoke: reflection,
|
|
23
|
+
} as unknown as ClientSession
|
|
24
|
+
return { call, reflection, session }
|
|
101
25
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ClientSession } from '@astrale-os/sdk/client/session'
|
|
2
|
-
import type { DomainBinding } from '@astrale-os/shell'
|
|
3
2
|
|
|
4
3
|
import type { AdminGraphApi } from '../../graph/.spec/api.js'
|
|
5
4
|
|
|
@@ -55,7 +54,6 @@ export class AdminDomainNotFoundError extends Error {
|
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
export interface AdminCatalogDependencies {
|
|
58
|
-
readonly bind?: (session: ClientSession) => Promise<DomainBinding>
|
|
59
57
|
readonly operationId?: (kind: 'publish' | 'configure-default') => string
|
|
60
58
|
}
|
|
61
59
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# CLI Admin catalog adapter
|
|
2
2
|
|
|
3
|
-
The adapter reads policy-visible `Domain` Nodes through bounded Graph pages
|
|
4
|
-
membership from the Fleet relation.
|
|
5
|
-
|
|
6
|
-
no-op, and an
|
|
3
|
+
The adapter reads policy-visible `Domain` Nodes through bounded Graph pages using stable Class and
|
|
4
|
+
Property keys, and derives default membership from the stable Fleet relation. It performs no schema
|
|
5
|
+
discovery or reflection. Publication invokes `Fleet.publishDomain`; a requested default change is a
|
|
6
|
+
separate explicit `Domain.configureDefault` call. Unchanged publication input is a no-op, and an
|
|
7
|
+
omitted description preserves the current value.
|
|
@@ -2,23 +2,35 @@ import type { Node } from '@astrale-os/sdk/graph/node'
|
|
|
2
2
|
import type { QueryAST } from '@astrale-os/sdk/query'
|
|
3
3
|
|
|
4
4
|
import { NodeId } from '@astrale-os/sdk/graph/node'
|
|
5
|
+
import { normalizeProperties } from '@astrale-os/sdk/graph/properties'
|
|
6
|
+
import { PropertyKey } from '@astrale-os/sdk/schema'
|
|
5
7
|
import { describe, expect, mock, test } from 'bun:test'
|
|
6
8
|
|
|
7
9
|
import type { AdminGraphApi } from '../../graph'
|
|
8
10
|
|
|
9
|
-
import {
|
|
11
|
+
import { adminSession } from '../../__tests__/fixture'
|
|
10
12
|
import { connectAdminCatalog } from '../client'
|
|
11
13
|
|
|
14
|
+
const domainProperties = Object.freeze({
|
|
15
|
+
origin: PropertyKey('admin.astrale.ai:class.Domain.property.origin'),
|
|
16
|
+
name: PropertyKey('kernel.astrale.ai:class.Named.property.name'),
|
|
17
|
+
discoveryUrl: PropertyKey('admin.astrale.ai:class.Domain.property.discoveryUrl'),
|
|
18
|
+
description: PropertyKey('kernel.astrale.ai:class.Descriptable.property.description'),
|
|
19
|
+
createdAt: PropertyKey('kernel.astrale.ai:class.Timestamped.property.createdAt'),
|
|
20
|
+
updatedAt: PropertyKey('kernel.astrale.ai:class.Timestamped.property.updatedAt'),
|
|
21
|
+
})
|
|
22
|
+
|
|
12
23
|
function domainNode(id: string, origin: string): Node {
|
|
13
24
|
return {
|
|
14
25
|
id: NodeId(id),
|
|
15
|
-
class:
|
|
16
|
-
props:
|
|
17
|
-
origin,
|
|
18
|
-
name: origin.split('.')[0]!,
|
|
19
|
-
discoveryUrl: `https://${origin}`,
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
class: 'admin.astrale.ai:Domain' as Node['class'],
|
|
27
|
+
props: normalizeProperties({
|
|
28
|
+
[domainProperties.origin]: origin,
|
|
29
|
+
[domainProperties.name]: origin.split('.')[0]!,
|
|
30
|
+
[domainProperties.discoveryUrl]: `https://${origin}`,
|
|
31
|
+
[domainProperties.description]: `${origin} description`,
|
|
32
|
+
[domainProperties.createdAt]: '2026-08-12T00:00:00.000Z',
|
|
33
|
+
[domainProperties.updatedAt]: '2026-08-12T00:00:00.000Z',
|
|
22
34
|
}),
|
|
23
35
|
}
|
|
24
36
|
}
|
|
@@ -26,18 +38,16 @@ function domainNode(id: string, origin: string): Node {
|
|
|
26
38
|
function fixture(input: {
|
|
27
39
|
domains?: readonly Node[]
|
|
28
40
|
defaults?: readonly Node[]
|
|
29
|
-
invoke?: (
|
|
41
|
+
invoke?: (target: string, value: unknown) => unknown
|
|
30
42
|
}) {
|
|
31
43
|
const calls: Array<{
|
|
32
|
-
|
|
33
|
-
receiver: string
|
|
44
|
+
target: string
|
|
34
45
|
value: unknown
|
|
35
46
|
}> = []
|
|
36
|
-
const remote = adminSession((
|
|
37
|
-
calls.push({
|
|
38
|
-
return input.invoke?.(
|
|
47
|
+
const remote = adminSession((target, value) => {
|
|
48
|
+
calls.push({ target, value })
|
|
49
|
+
return input.invoke?.(target, value)
|
|
39
50
|
})
|
|
40
|
-
const binding = adminBinding()
|
|
41
51
|
const query = mock(async (_ast: QueryAST) => ({
|
|
42
52
|
result: {
|
|
43
53
|
kind: 'nodes' as const,
|
|
@@ -46,22 +56,30 @@ function fixture(input: {
|
|
|
46
56
|
page: {},
|
|
47
57
|
}))
|
|
48
58
|
const defaultNodes = input.defaults ?? []
|
|
49
|
-
const neighbors = mock(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
const neighbors = mock(
|
|
60
|
+
async (
|
|
61
|
+
_source: Parameters<AdminGraphApi['neighbors']>[0],
|
|
62
|
+
_via: Parameters<AdminGraphApi['neighbors']>[1],
|
|
63
|
+
_options: Parameters<AdminGraphApi['neighbors']>[2],
|
|
64
|
+
) => ({
|
|
65
|
+
nodes: defaultNodes,
|
|
66
|
+
first: defaultNodes[0] ?? null,
|
|
67
|
+
graph: { nodes: defaultNodes, edges: [] },
|
|
68
|
+
cursor: null,
|
|
69
|
+
collect: async () => ({ nodes: defaultNodes, cursor: null }),
|
|
70
|
+
}),
|
|
71
|
+
)
|
|
56
72
|
const graph = { query, neighbors } as unknown as AdminGraphApi
|
|
57
73
|
return {
|
|
58
|
-
|
|
74
|
+
call: remote.call,
|
|
75
|
+
reflection: remote.reflection,
|
|
76
|
+
query,
|
|
77
|
+
neighbors,
|
|
59
78
|
calls,
|
|
60
79
|
connect: () =>
|
|
61
80
|
connectAdminCatalog(
|
|
62
81
|
{ session: remote.session, graph },
|
|
63
82
|
{
|
|
64
|
-
bind: async () => binding,
|
|
65
83
|
operationId: (kind) => `cli.domain.${kind}:test`,
|
|
66
84
|
},
|
|
67
85
|
),
|
|
@@ -83,16 +101,57 @@ describe('V2 Admin Domain catalog adapter', () => {
|
|
|
83
101
|
test('lists Domain nodes through GraphApi and joins the Fleet default relation', async () => {
|
|
84
102
|
const crm = domainNode('crm-domain', 'crm.acme.dev')
|
|
85
103
|
const notes = domainNode('notes-domain', 'notes.acme.dev')
|
|
86
|
-
const
|
|
104
|
+
const contract = fixture({ domains: [crm, notes], defaults: [crm] })
|
|
105
|
+
const api = await contract.connect()
|
|
87
106
|
|
|
88
107
|
await expect(api.list()).resolves.toEqual([
|
|
89
|
-
|
|
108
|
+
{
|
|
90
109
|
id: '@crm-domain',
|
|
91
110
|
origin: 'crm.acme.dev',
|
|
111
|
+
name: 'crm',
|
|
112
|
+
url: 'https://crm.acme.dev',
|
|
113
|
+
description: 'crm.acme.dev description',
|
|
92
114
|
installByDefault: true,
|
|
93
|
-
|
|
94
|
-
|
|
115
|
+
createdAt: '2026-08-12T00:00:00.000Z',
|
|
116
|
+
updatedAt: '2026-08-12T00:00:00.000Z',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: '@notes-domain',
|
|
120
|
+
origin: 'notes.acme.dev',
|
|
121
|
+
name: 'notes',
|
|
122
|
+
url: 'https://notes.acme.dev',
|
|
123
|
+
description: 'notes.acme.dev description',
|
|
124
|
+
createdAt: '2026-08-12T00:00:00.000Z',
|
|
125
|
+
updatedAt: '2026-08-12T00:00:00.000Z',
|
|
126
|
+
},
|
|
95
127
|
])
|
|
128
|
+
expect(contract.query).toHaveBeenCalledWith(
|
|
129
|
+
{
|
|
130
|
+
format: 'astrale.graph.query',
|
|
131
|
+
version: 'v6',
|
|
132
|
+
source: {
|
|
133
|
+
kind: 'node',
|
|
134
|
+
terms: [
|
|
135
|
+
{
|
|
136
|
+
kind: 'class',
|
|
137
|
+
class: { origin: 'admin.astrale.ai', kind: 'class', name: 'Domain' },
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
binding: 'n0',
|
|
141
|
+
},
|
|
142
|
+
steps: [],
|
|
143
|
+
select: { kind: 'nodes', binding: 'n0', projection: { kind: 'value' } },
|
|
144
|
+
},
|
|
145
|
+
{ page: { size: 256 } },
|
|
146
|
+
)
|
|
147
|
+
const [source, edge, options] = contract.neighbors.mock.calls[0]!
|
|
148
|
+
expect(String(source)).toBe('/:admin.astrale.ai:core.fleet')
|
|
149
|
+
expect(edge).toEqual({
|
|
150
|
+
origin: 'admin.astrale.ai',
|
|
151
|
+
kind: 'class',
|
|
152
|
+
name: 'fleet_installs_domain_by_default',
|
|
153
|
+
})
|
|
154
|
+
expect(options).toEqual({ direction: 'outgoing', page: { size: 256 } })
|
|
96
155
|
})
|
|
97
156
|
|
|
98
157
|
test('publishes through Fleet then configures the Domain default explicitly', async () => {
|
|
@@ -109,8 +168,7 @@ describe('V2 Admin Domain catalog adapter', () => {
|
|
|
109
168
|
).resolves.toMatchObject({ changed: true, isNew: true, entry: { installByDefault: true } })
|
|
110
169
|
expect(contract.calls).toEqual([
|
|
111
170
|
{
|
|
112
|
-
|
|
113
|
-
receiver: '/:admin.astrale.ai:core.fleet::publishDomain',
|
|
171
|
+
target: '/:admin.astrale.ai:core.fleet::publishDomain',
|
|
114
172
|
value: {
|
|
115
173
|
operationId: 'cli.domain.publish:test',
|
|
116
174
|
origin: 'crm.acme.dev',
|
|
@@ -119,8 +177,7 @@ describe('V2 Admin Domain catalog adapter', () => {
|
|
|
119
177
|
},
|
|
120
178
|
},
|
|
121
179
|
{
|
|
122
|
-
|
|
123
|
-
receiver: '@crm-domain::configureDefault',
|
|
180
|
+
target: '@crm-domain::configureDefault',
|
|
124
181
|
value: {
|
|
125
182
|
operationId: 'cli.domain.configure-default:test',
|
|
126
183
|
enabled: true,
|
|
@@ -137,6 +194,52 @@ describe('V2 Admin Domain catalog adapter', () => {
|
|
|
137
194
|
await expect(
|
|
138
195
|
api.publish({ origin: 'crm.acme.dev', name: 'crm', url: 'https://crm.acme.dev' }),
|
|
139
196
|
).resolves.toMatchObject({ changed: false, isNew: false })
|
|
140
|
-
expect(contract.
|
|
197
|
+
expect(contract.call).not.toHaveBeenCalled()
|
|
198
|
+
expect(contract.reflection).not.toHaveBeenCalled()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('connects without schema discovery or graph I/O', async () => {
|
|
202
|
+
const contract = fixture({})
|
|
203
|
+
|
|
204
|
+
await contract.connect()
|
|
205
|
+
|
|
206
|
+
expect(contract.call).not.toHaveBeenCalled()
|
|
207
|
+
expect(contract.reflection).not.toHaveBeenCalled()
|
|
208
|
+
expect(contract.query).not.toHaveBeenCalled()
|
|
209
|
+
expect(contract.neighbors).not.toHaveBeenCalled()
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
test('rejects malformed graph records and publication summaries', async () => {
|
|
213
|
+
const invalidNode = {
|
|
214
|
+
id: NodeId('invalid-domain'),
|
|
215
|
+
class: 'admin.astrale.ai:Domain' as Node['class'],
|
|
216
|
+
props: normalizeProperties({
|
|
217
|
+
[domainProperties.origin]: 'invalid.example.dev',
|
|
218
|
+
[domainProperties.name]: 'invalid',
|
|
219
|
+
[domainProperties.discoveryUrl]: 'https://invalid.example.dev',
|
|
220
|
+
[domainProperties.createdAt]: '2026-08-12T00:00:00.000Z',
|
|
221
|
+
}),
|
|
222
|
+
}
|
|
223
|
+
await expect((await fixture({ domains: [invalidNode] }).connect()).list()).rejects.toThrow(
|
|
224
|
+
'Admin Domain.updatedAt is invalid.',
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
const malformed = fixture({ invoke: () => ({ id: '@domain-only' }) })
|
|
228
|
+
await expect(
|
|
229
|
+
(await malformed.connect()).publish({
|
|
230
|
+
origin: 'crm.acme.dev',
|
|
231
|
+
name: 'crm',
|
|
232
|
+
url: 'https://crm.acme.dev',
|
|
233
|
+
}),
|
|
234
|
+
).rejects.toThrow('Admin Domain origin is invalid.')
|
|
235
|
+
|
|
236
|
+
const malformedPath = fixture({ invoke: () => ({ ...summary('crm.acme.dev'), id: 'bad' }) })
|
|
237
|
+
await expect(
|
|
238
|
+
(await malformedPath.connect()).publish({
|
|
239
|
+
origin: 'crm.acme.dev',
|
|
240
|
+
name: 'crm',
|
|
241
|
+
url: 'https://crm.acme.dev',
|
|
242
|
+
}),
|
|
243
|
+
).rejects.toThrow('Admin Domain id is invalid.')
|
|
141
244
|
})
|
|
142
245
|
})
|