@astrale-os/cli 1.0.0-beta.2 → 1.0.0-beta.20
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/README.md +16 -1
- package/dist/astrale.js +51105 -58057
- package/dist/public/connect-core.js +2085 -30702
- package/dist/public/keys/index.js +884 -29476
- package/dist/public/paths/index.js +13101 -41452
- package/dist/types/admin/contract.d.ts +26 -0
- package/dist/types/admin/instance/client.d.ts +20 -0
- package/dist/types/admin/instance/index.d.ts +2 -0
- package/dist/types/admin/instance/model.d.ts +6 -10
- package/dist/types/connection/auth.d.ts +3 -0
- package/dist/types/connection/call.d.ts +3 -0
- package/dist/types/connection/command.d.ts +27 -0
- package/dist/types/connection/credential.d.ts +14 -0
- package/dist/types/connection/errors.d.ts +1 -0
- package/dist/types/connection/exchange.d.ts +9 -0
- package/dist/types/connection/failure/classify.d.ts +2 -0
- package/dist/types/connection/failure/index.d.ts +5 -0
- package/dist/types/connection/failure/model.d.ts +25 -0
- package/dist/types/connection/failure/render.d.ts +3 -0
- package/dist/types/connection/index.d.ts +8 -0
- package/dist/types/connection/reasons.d.ts +37 -0
- package/dist/types/connection/self.d.ts +37 -0
- package/dist/types/connection/session.d.ts +31 -0
- package/dist/types/connection/target.d.ts +38 -0
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/lib/admin-instance.d.ts +17 -0
- package/dist/types/lib/admin-target.d.ts +11 -36
- package/dist/types/lib/config.d.ts +7 -59
- package/dist/types/lib/failure-debug.d.ts +1 -0
- package/dist/types/lib/idp.d.ts +25 -173
- package/dist/types/lib/instance-target.d.ts +1 -1
- package/dist/types/lib/instance.d.ts +30 -88
- package/dist/types/lib/log.d.ts +5 -3
- package/dist/types/lib/output.d.ts +1 -1
- package/dist/types/lib/proc.d.ts +43 -0
- package/dist/types/lib/self.d.ts +9 -0
- package/dist/types/lib/update.d.ts +109 -0
- package/dist/types/lib/validation.d.ts +4 -1
- package/package.json +15 -20
- package/src/__tests__/fixtures/publication.ts +20 -0
- package/src/admin/.spec/architecture.md +12 -5
- package/src/admin/__tests__/fixture.ts +25 -0
- package/src/admin/catalog/.spec/api.d.ts +1 -3
- package/src/admin/catalog/.spec/architecture.md +5 -4
- package/src/admin/catalog/__tests__/client.test.ts +143 -69
- package/src/admin/catalog/client.ts +40 -54
- package/src/admin/catalog/model.ts +3 -4
- package/src/admin/contract.ts +46 -0
- package/src/admin/graph/.spec/api.d.ts +4 -10
- package/src/admin/graph/nodes.ts +1 -1
- package/src/admin/instance/.spec/api.d.ts +5 -9
- package/src/admin/instance/.spec/architecture.md +7 -7
- package/src/admin/instance/__tests__/client.test.ts +157 -175
- package/src/admin/instance/client.ts +39 -128
- package/src/admin/instance/index.ts +1 -2
- package/src/admin/instance/model.ts +12 -18
- package/src/commands/__tests__/admin-instance.test.ts +11 -8
- package/src/commands/__tests__/call-describe.test.ts +46 -33
- package/src/commands/__tests__/call.test.ts +34 -0
- package/src/commands/__tests__/domain-install-operation.test.ts +7 -22
- package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
- package/src/commands/__tests__/domain-list.test.ts +5 -25
- package/src/commands/__tests__/install-direct.test.ts +115 -8
- package/src/commands/__tests__/install-identity-override.test.ts +3 -14
- package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
- package/src/commands/__tests__/instance-status.test.ts +139 -0
- package/src/commands/__tests__/introspect-parse.test.ts +80 -1
- package/src/commands/__tests__/logs.test.ts +250 -2
- package/src/commands/__tests__/read-commands.test.ts +62 -10
- package/src/commands/__tests__/token-ttl.test.ts +49 -4
- package/src/commands/__tests__/update.test.ts +25 -3
- package/src/commands/__tests__/view.test.ts +49 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +71 -59
- package/src/commands/domain/install.ts +54 -35
- package/src/commands/domain/list.ts +1 -1
- package/src/commands/domain/publish.ts +14 -11
- package/src/commands/domain/uninstall.ts +2 -2
- package/src/commands/get.ts +14 -15
- package/src/commands/identity/__tests__/register.test.ts +3 -3
- package/src/commands/identity/register.ts +5 -4
- package/src/commands/instance/create.ts +8 -16
- package/src/commands/instance/delete.ts +12 -9
- package/src/commands/instance/list.ts +2 -2
- package/src/commands/instance/status.ts +66 -20
- package/src/commands/instance/use.ts +16 -12
- package/src/commands/introspect.ts +48 -35
- package/src/commands/logs.ts +123 -33
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +35 -32
- package/src/commands/token.ts +42 -27
- package/src/commands/ui/__tests__/commands.test.ts +152 -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/commands/update.ts +31 -11
- package/src/commands/view.ts +82 -37
- package/src/connection/.spec/api.d.ts +7 -8
- package/src/connection/.spec/architecture.md +5 -4
- package/src/connection/.spec/flows/session.ts +2 -2
- package/src/connection/.spec/laws/connection.ts +6 -2
- package/src/connection/.spec/layout.ts +1 -0
- package/src/connection/__tests__/credential.test.ts +20 -1
- package/src/connection/__tests__/errors.test.ts +27 -20
- package/src/connection/__tests__/exchange.test.ts +30 -1
- package/src/connection/__tests__/failure-fixtures.ts +39 -0
- package/src/connection/__tests__/failure-safety.test.ts +75 -0
- package/src/connection/__tests__/self.test.ts +31 -11
- package/src/connection/__tests__/session.test.ts +3 -3
- package/src/connection/__tests__/target.test.ts +1 -0
- package/src/connection/auth.ts +13 -0
- package/src/connection/call.ts +1 -1
- package/src/connection/credential.ts +2 -2
- package/src/connection/errors.ts +1 -355
- package/src/connection/exchange.ts +47 -23
- package/src/connection/failure/classify.ts +110 -0
- package/src/connection/failure/index.ts +18 -0
- package/src/connection/failure/model.ts +28 -0
- package/src/connection/failure/render.ts +116 -0
- package/src/connection/reasons.ts +16 -0
- package/src/connection/self.ts +39 -21
- package/src/connection/session.ts +19 -8
- package/src/errors.ts +3 -2
- package/src/graph/.spec/api.d.ts +12 -5
- package/src/graph/.spec/layout.ts +9 -1
- package/src/graph/__tests__/mutation.test.ts +2 -2
- package/src/graph/__tests__/query.test.ts +25 -6
- package/src/graph/class.ts +30 -0
- package/src/graph/index.ts +1 -0
- package/src/graph/query.ts +21 -32
- package/src/identity/.spec/api.d.ts +2 -2
- package/src/identity/__tests__/registration.test.ts +1 -1
- package/src/identity/registration.ts +3 -3
- package/src/keys/__tests__/keys.test.ts +4 -0
- package/src/keys/credential.ts +1 -0
- package/src/lib/__tests__/admin-target.test.ts +12 -0
- package/src/lib/__tests__/binary.test.ts +16 -1
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/domain-publication.test.ts +10 -16
- package/src/lib/__tests__/idp.test.ts +34 -4
- package/src/lib/__tests__/instance-candidates.test.ts +1 -3
- package/src/lib/__tests__/instance-target.test.ts +7 -39
- package/src/lib/__tests__/log-errors.test.ts +64 -0
- package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
- package/src/lib/__tests__/update.test.ts +264 -11
- package/src/lib/__tests__/view-assets.test.ts +14 -0
- package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
- package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
- package/src/lib/__tests__/view-open-intent.test.ts +0 -2
- package/src/lib/__tests__/view-server.test.ts +26 -3
- package/src/lib/__tests__/view-session.test.ts +2 -1
- package/src/lib/admin-instance.ts +14 -8
- package/src/lib/admin-target.ts +21 -2
- package/src/lib/binary.ts +22 -5
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/domain-publication.ts +9 -7
- package/src/lib/failure-debug.ts +26 -0
- package/src/lib/idp.ts +34 -3
- package/src/lib/instance-candidates.ts +2 -2
- package/src/lib/instance-target.ts +3 -14
- package/src/lib/instance.ts +16 -11
- package/src/lib/log.ts +16 -9
- package/src/lib/output.ts +1 -1
- package/src/lib/proc.ts +7 -2
- package/src/lib/provision-instance.ts +34 -57
- package/src/lib/update.ts +298 -45
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/assets.ts +11 -2
- package/src/lib/view/external-open-intent.ts +41 -0
- package/src/lib/view/external-open-origins.ts +37 -0
- package/src/lib/view/host-capabilities.ts +17 -0
- package/src/lib/view/resolve.ts +1 -1
- package/src/lib/view/server.ts +18 -3
- package/src/lib/view/session.ts +2 -0
- package/src/program/.spec/api.d.ts +1 -0
- package/src/program/__tests__/program.test.ts +163 -6
- package/src/program/argv.ts +14 -0
- package/src/program/build.ts +26 -4
- package/src/program/command.ts +1 -0
- package/src/program/index.ts +1 -0
- package/src/program/registry.ts +2 -1
- package/src/setup/__tests__/instance-step.test.ts +27 -10
- package/src/setup/steps/instance.ts +22 -15
- package/src/telemetry/__tests__/recorder.test.ts +1 -0
- package/src/telemetry/recorder.ts +9 -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 +981 -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 +710 -0
- package/src/ui/project.ts +219 -0
- package/src/ui/release.ts +321 -0
- package/src/ui/runner.ts +18 -0
- package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
- package/studio/client/dist/assets/index-7YHdeCxp.js +81 -0
- package/studio/client/dist/assets/index-C0FAnwNw.css +2 -0
- package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
- package/studio/client/dist/assets/schema-studio-BilUNb6Z.css +1 -0
- package/studio/client/dist/assets/schema-studio-CABpTfvH.js +8 -0
- package/studio/client/dist/index.html +3 -2
- package/studio/package.json +7 -13
- package/studio/server/agent/ask.test.ts +1 -1
- package/studio/server/agent/bridge/grant.test.ts +1 -1
- package/studio/server/agent/bridge/routes.test.ts +1 -1
- package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
- package/studio/server/agent/harness/gateway/token.test.ts +1 -1
- package/studio/server/agent/harness/gateway/token.ts +3 -3
- package/studio/server/agent/prompts/anchors.test.ts +32 -36
- package/studio/server/agent/prompts/anchors.ts +20 -49
- package/studio/server/agent/prompts/system.test.ts +3 -2
- package/studio/server/agent/prompts/system.ts +3 -3
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/api/canvas.ts +0 -1
- package/studio/server/api-agent-loadout.test.ts +1 -1
- package/studio/server/api.test.ts +1 -1
- package/studio/server/cache.test.ts +23 -12
- package/studio/server/cache.ts +12 -15
- package/studio/server/domain.test.ts +50 -68
- package/studio/server/domain.ts +82 -63
- package/studio/server/handoff/copy.ts +1 -1
- package/studio/server/index.ts +1 -1
- package/studio/server/instances/probe.test.ts +1 -1
- package/studio/server/introspect/anatomy/views/routes.ts +46 -37
- package/studio/server/introspect/anatomy/views.ts +9 -14
- package/studio/server/introspect/anatomy-extras.test.ts +26 -42
- package/studio/server/introspect/anatomy.test.ts +22 -14
- package/studio/server/introspect/anatomy.ts +6 -9
- package/studio/server/introspect/bundle.ts +0 -3
- package/studio/server/introspect/canonical-schema.test.ts +94 -389
- package/studio/server/introspect/canonical-schema.ts +227 -457
- package/studio/server/introspect/core-extractor.ts +18 -127
- package/studio/server/introspect/core.ts +1 -2
- package/studio/server/introspect/diff.test.ts +11 -7
- package/studio/server/introspect/diff.ts +18 -55
- package/studio/server/introspect/extractor.ts +22 -117
- package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
- package/studio/server/introspect/overlay.test.ts +26 -54
- package/studio/server/introspect/overlay.ts +10 -37
- package/studio/server/introspect/revision.test.ts +1 -1
- package/studio/server/introspect/revision.ts +1 -1
- package/studio/server/introspect/runtime.test.ts +65 -183
- package/studio/server/introspect/runtime.ts +8 -25
- package/studio/server/introspect/schema-ir-json.test.ts +70 -0
- package/studio/server/introspect/schema-ir-json.ts +38 -68
- package/studio/server/introspect/schema-refs.test.ts +43 -88
- package/studio/server/introspect/schema-refs.ts +14 -49
- package/studio/server/introspect/source-overlay/handlers.ts +116 -636
- package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
- package/studio/server/introspect/source-overlay/spans.ts +15 -45
- package/studio/server/state/baseline.test.ts +14 -9
- package/studio/server/state/baseline.ts +3 -5
- package/studio/server/state/visibility.ts +1 -5
- package/studio/server/views/model.ts +3 -3
- package/studio/server/views/target.test.ts +17 -74
- package/studio/server/views/target.ts +24 -50
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +8 -4
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/schema.ts +38 -107
- package/studio/shared/contracts/surface.test.ts +13 -15
- package/studio/shared/contracts/workspace.ts +2 -11
- package/studio/shared/schema/identity.test.ts +20 -44
- package/studio/shared/schema/identity.ts +9 -23
- package/viewer/dist/main.js +30 -51
- package/.check-workspace.cjs +0 -40
- package/src/admin/binding.ts +0 -168
- package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
- package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
- package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
- package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
- package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
- package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
- package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
|
@@ -33,10 +33,9 @@ export interface InstallDomainResult {
|
|
|
33
33
|
readonly error?: string | null
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export class AdminDomainNotFoundError extends
|
|
37
|
-
readonly name = 'NotFoundError'
|
|
38
|
-
|
|
36
|
+
export class AdminDomainNotFoundError extends AstraleError {
|
|
39
37
|
constructor(readonly identifier: string) {
|
|
40
|
-
super(`No visible Admin Domain matches ${JSON.stringify(identifier)}.`)
|
|
38
|
+
super('DOMAIN_NOT_FOUND', `No visible Admin Domain matches ${JSON.stringify(identifier)}.`)
|
|
41
39
|
}
|
|
42
40
|
}
|
|
41
|
+
import { AstraleError } from '../../errors'
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
|
|
5
|
+
import { call } from '@astrale-os/sdk/client'
|
|
6
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
7
|
+
import { K, PropertyKey } from '@astrale-os/sdk/schema'
|
|
8
|
+
|
|
9
|
+
const origin = 'admin.astrale.ai'
|
|
10
|
+
|
|
11
|
+
function classRef(name: string): ClassRef {
|
|
12
|
+
return Object.freeze({ origin, kind: 'class', name }) as ClassRef
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Domain = classRef('Domain')
|
|
16
|
+
|
|
17
|
+
export const AdminContract = Object.freeze({
|
|
18
|
+
origin,
|
|
19
|
+
fleet: Path.parse('/:admin.astrale.ai:core.fleet'),
|
|
20
|
+
classes: Object.freeze({
|
|
21
|
+
Domain,
|
|
22
|
+
}),
|
|
23
|
+
edges: Object.freeze({
|
|
24
|
+
fleetInstallsDomainByDefault: classRef('fleet_installs_domain_by_default'),
|
|
25
|
+
}),
|
|
26
|
+
properties: Object.freeze({
|
|
27
|
+
domain: Object.freeze({
|
|
28
|
+
origin: PropertyKey.of(Domain, 'origin'),
|
|
29
|
+
name: K.classes.Named.properties.name.key,
|
|
30
|
+
discoveryUrl: PropertyKey.of(Domain, 'discoveryUrl'),
|
|
31
|
+
description: K.classes.Descriptable.properties.description.key,
|
|
32
|
+
createdAt: K.classes.Timestamped.properties.createdAt.key,
|
|
33
|
+
updatedAt: K.classes.Timestamped.properties.updatedAt.key,
|
|
34
|
+
}),
|
|
35
|
+
}),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/** Invoke one stable Admin instance Method without schema discovery or reflection. */
|
|
39
|
+
export function callAdminMethod(
|
|
40
|
+
session: ClientSession,
|
|
41
|
+
receiver: Path,
|
|
42
|
+
method: string,
|
|
43
|
+
input: Input,
|
|
44
|
+
): Promise<unknown> {
|
|
45
|
+
return session.call(call(Path.instanceMethod(receiver, method), input))
|
|
46
|
+
}
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { ClassPath } from '@astrale-os/sdk/graph/class'
|
|
1
|
+
import type { GraphApi } from '@astrale-os/sdk/client'
|
|
3
2
|
import type { Node } from '@astrale-os/sdk/graph/node'
|
|
4
|
-
import type {
|
|
5
|
-
import type { QueryAST, QueryResult } from '@astrale-os/sdk/query'
|
|
3
|
+
import type { QueryAST } from '@astrale-os/sdk/query'
|
|
6
4
|
|
|
7
5
|
/** Smallest Kernel graph capability needed by bounded Admin graph reads. */
|
|
8
|
-
export
|
|
9
|
-
query(ast: QueryAST, options?: GraphQueryOptions): Promise<QueryResult>
|
|
10
|
-
}
|
|
6
|
+
export type AdminGraphQueryApi = Pick<GraphApi, 'query'>
|
|
11
7
|
|
|
12
8
|
/** Caller-scoped graph reads shared by the Admin catalog and Instance journeys. */
|
|
13
|
-
export
|
|
14
|
-
neighbors(source: PathLike, edge: ClassPath, options: NeighborsOptions): Promise<NodePage>
|
|
15
|
-
}
|
|
9
|
+
export type AdminGraphApi = Pick<GraphApi, 'query' | 'neighbors'>
|
|
16
10
|
|
|
17
11
|
export interface ReadAllNodesOptions {
|
|
18
12
|
readonly label: string
|
package/src/admin/graph/nodes.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { ClientSession } from '@astrale-os/kernel-client/session'
|
|
3
|
-
|
|
4
|
-
import type { AdminGraphApi } from '../../graph/.spec/api.js'
|
|
1
|
+
import type { ClientSession } from '@astrale-os/sdk/client/session'
|
|
5
2
|
|
|
6
3
|
export type InstanceState = 'provisioning' | 'ready' | 'deleting' | 'failed' | 'deleted'
|
|
7
4
|
|
|
@@ -11,10 +8,11 @@ export interface InstanceInfo {
|
|
|
11
8
|
readonly url: string
|
|
12
9
|
readonly hostId?: string
|
|
13
10
|
readonly region?: string
|
|
14
|
-
readonly state
|
|
11
|
+
readonly state: InstanceState
|
|
15
12
|
readonly phase?: string
|
|
16
13
|
readonly error?: string | null
|
|
17
14
|
readonly createdAt?: string
|
|
15
|
+
readonly updatedAt?: string
|
|
18
16
|
readonly organizationId?: string
|
|
19
17
|
}
|
|
20
18
|
|
|
@@ -33,12 +31,11 @@ export interface DomainInstallReceipt {
|
|
|
33
31
|
|
|
34
32
|
export interface AdminInstanceContext {
|
|
35
33
|
readonly session: ClientSession
|
|
36
|
-
readonly graph: AdminGraphApi
|
|
37
34
|
}
|
|
38
35
|
|
|
39
36
|
export interface AdminInstanceApi {
|
|
40
37
|
list(): Promise<OwnedInstanceInfo[]>
|
|
41
|
-
create(slug: string
|
|
38
|
+
create(slug: string): Promise<InstanceInfo>
|
|
42
39
|
status(identifier: string): Promise<InstanceInfo>
|
|
43
40
|
delete(identifier: string): Promise<InstanceInfo>
|
|
44
41
|
installDomain(identifier: string, domain: string): Promise<DomainInstallReceipt>
|
|
@@ -51,7 +48,6 @@ export class AdminInstanceNotFoundError extends Error {
|
|
|
51
48
|
}
|
|
52
49
|
|
|
53
50
|
export interface AdminInstanceDependencies {
|
|
54
|
-
readonly bind?: (session: ClientSession) => Promise<DomainBinding>
|
|
55
51
|
readonly operationId?: (kind: 'create' | 'status' | 'delete' | 'install-domain') => string
|
|
56
52
|
}
|
|
57
53
|
|
|
@@ -63,4 +59,4 @@ export function findOwnedInstance(
|
|
|
63
59
|
instances: readonly OwnedInstanceInfo[],
|
|
64
60
|
identifier: string,
|
|
65
61
|
): OwnedInstanceInfo | undefined
|
|
66
|
-
export function
|
|
62
|
+
export function formatInstanceState(info: InstanceInfo): string
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# CLI Admin Instance adapter
|
|
2
2
|
|
|
3
|
-
The adapter
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The adapter calls the stable singleton Fleet receiver directly. Listing and automatic creation are
|
|
4
|
+
one Admin Method call each; connection performs no schema discovery, introspection, or graph read.
|
|
5
|
+
Every returned summary is decoded locally before it reaches a command.
|
|
6
6
|
|
|
7
|
-
Automatic creation invokes `Fleet.createInstance
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Automatic creation invokes `Fleet.createInstance`. Status, deletion, and Domain installation first
|
|
8
|
+
resolve a caller-visible Instance through Fleet and then invoke that exact Instance receiver. The
|
|
9
|
+
adapter creates internal operation IDs but preserves the existing CLI projection and
|
|
10
|
+
selection-required experience.
|
|
@@ -1,135 +1,46 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { QueryAST } from '@astrale-os/sdk/query'
|
|
3
|
-
|
|
4
|
-
import { ClassPath } from '@astrale-os/sdk/graph/class'
|
|
5
|
-
import { NodeId } from '@astrale-os/sdk/graph/node'
|
|
6
|
-
import { Path } from '@astrale-os/sdk/graph/path'
|
|
7
|
-
import { describe, expect, mock, test } from 'bun:test'
|
|
8
|
-
|
|
9
|
-
import type { AdminBinding } from '../../binding'
|
|
10
|
-
import type { AdminGraphApi } from '../../graph'
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
11
2
|
|
|
3
|
+
import { adminSession } from '../../__tests__/fixture'
|
|
12
4
|
import { connectAdminInstances } from '../client'
|
|
13
5
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
methods.push(value)
|
|
23
|
-
return value
|
|
24
|
-
},
|
|
25
|
-
property(property: string) {
|
|
26
|
-
return { key: property }
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function node(id: string, props: Record<string, unknown>): Node {
|
|
33
|
-
return {
|
|
34
|
-
id: NodeId(id),
|
|
35
|
-
class: ClassPath.from('admin.astrale.ai', 'Instance'),
|
|
36
|
-
props: props as never,
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function graphResult(nodes: readonly Node[], cursor?: string) {
|
|
41
|
-
return {
|
|
42
|
-
result: {
|
|
43
|
-
kind: 'nodes' as const,
|
|
44
|
-
nodes: nodes.map((value) => ({ kind: 'value' as const, value })),
|
|
45
|
-
},
|
|
46
|
-
page: cursor === undefined ? {} : { next: cursor },
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function sourceName(ast: QueryAST): string | undefined {
|
|
51
|
-
const term = ast.source.terms[0]
|
|
52
|
-
return term?.kind === 'definition' ? term.definition.name : undefined
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function fixture(input: {
|
|
56
|
-
instances?: readonly Node[]
|
|
57
|
-
hosts?: readonly Node[]
|
|
58
|
-
reserved?: Node | null
|
|
59
|
-
invoke?: (method: { owner: string; name: string }, receiver: unknown, input: unknown) => unknown
|
|
60
|
-
}) {
|
|
61
|
-
methods.length = 0
|
|
62
|
-
const Instance = definition('Instance')
|
|
63
|
-
const Host = definition('Host')
|
|
64
|
-
const Fleet = definition('Fleet')
|
|
65
|
-
const invoke = mock(async (method: unknown, receiver: unknown, value: unknown) =>
|
|
66
|
-
input.invoke?.(method as { owner: string; name: string }, receiver, value),
|
|
67
|
-
)
|
|
68
|
-
const binding = {
|
|
69
|
-
$: {
|
|
70
|
-
publication: { origin: 'admin.astrale.ai' },
|
|
71
|
-
origin: 'admin.astrale.ai',
|
|
72
|
-
class(name: string) {
|
|
73
|
-
if (name === 'Instance') return Instance
|
|
74
|
-
if (name === 'Host') return Host
|
|
75
|
-
if (name === 'Fleet') return Fleet
|
|
76
|
-
throw new Error(`Unexpected class ${name}`)
|
|
77
|
-
},
|
|
78
|
-
core: { nodes: { fleet: { path: Path.id(NodeId('fleet')) } } },
|
|
79
|
-
invoke,
|
|
80
|
-
},
|
|
81
|
-
} as unknown as AdminBinding
|
|
82
|
-
const query = mock(async (ast: QueryAST) => {
|
|
83
|
-
const name = sourceName(ast)
|
|
84
|
-
if (name === 'Instance') return graphResult(input.instances ?? [])
|
|
85
|
-
if (name === 'Host') return graphResult(input.hosts ?? [])
|
|
86
|
-
throw new Error(`Unexpected graph query ${String(name)}`)
|
|
87
|
-
})
|
|
88
|
-
const neighbors = mock(async (source: unknown, edge: { name?: string }) => {
|
|
89
|
-
if (String(source) === '@fleet') return page(input.reserved ?? null)
|
|
90
|
-
if (edge.name === 'instance_runs_on_host') return page(input.hosts?.[0] ?? null)
|
|
91
|
-
throw new Error(`Unexpected neighbor query ${String(source)} ${String(edge)}`)
|
|
6
|
+
function fixture(input: { invoke?: (target: string, input: unknown) => unknown }) {
|
|
7
|
+
const calls: Array<{
|
|
8
|
+
target: string
|
|
9
|
+
value: unknown
|
|
10
|
+
}> = []
|
|
11
|
+
const remote = adminSession((target, value) => {
|
|
12
|
+
calls.push({ target, value })
|
|
13
|
+
return input.invoke?.(target, value)
|
|
92
14
|
})
|
|
93
|
-
const graph = { query, neighbors } as unknown as AdminGraphApi
|
|
94
15
|
return {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
16
|
+
call: remote.call,
|
|
17
|
+
reflection: remote.reflection,
|
|
18
|
+
calls,
|
|
98
19
|
connect: () =>
|
|
99
20
|
connectAdminInstances(
|
|
100
|
-
{ session:
|
|
21
|
+
{ session: remote.session },
|
|
101
22
|
{
|
|
102
|
-
bind: async () => binding,
|
|
103
23
|
operationId: (kind) => `cli.instance.${kind}:test`,
|
|
104
24
|
},
|
|
105
25
|
),
|
|
106
26
|
}
|
|
107
27
|
}
|
|
108
28
|
|
|
109
|
-
function page(item: Node | null) {
|
|
110
|
-
return {
|
|
111
|
-
nodes: item === null ? [] : [item],
|
|
112
|
-
first: item,
|
|
113
|
-
graph: { nodes: item === null ? [] : [item], edges: [] },
|
|
114
|
-
cursor: null,
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
29
|
describe('V2 Admin Instance adapter', () => {
|
|
119
30
|
test('lists caller-visible Instances through the Admin-owned Fleet operation', async () => {
|
|
120
31
|
const contract = fixture({
|
|
121
|
-
invoke: (
|
|
122
|
-
|
|
32
|
+
invoke: (target) =>
|
|
33
|
+
target.endsWith('::listInstances')
|
|
123
34
|
? [
|
|
124
35
|
{
|
|
125
36
|
id: '@instance-node',
|
|
126
37
|
slug: 'demo',
|
|
127
38
|
url: 'https://demo.eu.astrale.ai',
|
|
39
|
+
hostId: '@host-node',
|
|
40
|
+
region: 'fr-par',
|
|
128
41
|
state: 'ready',
|
|
129
42
|
createdAt: '2026-08-12T00:00:00.000Z',
|
|
130
43
|
updatedAt: '2026-08-12T00:00:00.000Z',
|
|
131
|
-
hostId: '@host-node',
|
|
132
|
-
region: 'fr-par',
|
|
133
44
|
},
|
|
134
45
|
]
|
|
135
46
|
: undefined,
|
|
@@ -140,17 +51,20 @@ describe('V2 Admin Instance adapter', () => {
|
|
|
140
51
|
id: '@instance-node',
|
|
141
52
|
slug: 'demo',
|
|
142
53
|
url: 'https://demo.eu.astrale.ai',
|
|
143
|
-
state: 'ready',
|
|
144
|
-
createdAt: '2026-08-12T00:00:00.000Z',
|
|
145
54
|
hostId: '@host-node',
|
|
146
55
|
region: 'fr-par',
|
|
56
|
+
state: 'ready',
|
|
57
|
+
createdAt: '2026-08-12T00:00:00.000Z',
|
|
58
|
+
updatedAt: '2026-08-12T00:00:00.000Z',
|
|
147
59
|
},
|
|
148
60
|
])
|
|
149
|
-
expect(contract.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
61
|
+
expect(contract.calls).toEqual([
|
|
62
|
+
{
|
|
63
|
+
target: '/:admin.astrale.ai:core.fleet::listInstances',
|
|
64
|
+
value: {},
|
|
65
|
+
},
|
|
66
|
+
])
|
|
67
|
+
expect(contract.reflection).not.toHaveBeenCalled()
|
|
154
68
|
})
|
|
155
69
|
|
|
156
70
|
test('delegates default placement to Fleet without reading Host inventory', async () => {
|
|
@@ -162,10 +76,7 @@ describe('V2 Admin Instance adapter', () => {
|
|
|
162
76
|
createdAt: '2026-08-12T00:00:00.000Z',
|
|
163
77
|
updatedAt: '2026-08-12T00:00:00.000Z',
|
|
164
78
|
}
|
|
165
|
-
const contract = fixture({
|
|
166
|
-
hosts: [node('host-node', { id: 'host-paris', state: 'ready' })],
|
|
167
|
-
invoke: () => created,
|
|
168
|
-
})
|
|
79
|
+
const contract = fixture({ invoke: () => created })
|
|
169
80
|
|
|
170
81
|
await expect((await contract.connect()).create('demo')).resolves.toMatchObject({
|
|
171
82
|
id: created.id,
|
|
@@ -174,49 +85,18 @@ describe('V2 Admin Instance adapter', () => {
|
|
|
174
85
|
state: created.state,
|
|
175
86
|
createdAt: created.createdAt,
|
|
176
87
|
})
|
|
177
|
-
expect(contract.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
expect(contract.graph.neighbors).not.toHaveBeenCalled()
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
test('resolves --host-id to an exact Host receiver and rejects the reserved Admin Host', async () => {
|
|
187
|
-
const reserved = node('admin-host', { id: 'admin', state: 'ready' })
|
|
188
|
-
const consumer = node('consumer-host', { id: 'consumer-paris', state: 'ready' })
|
|
189
|
-
const contract = fixture({
|
|
190
|
-
hosts: [reserved, consumer],
|
|
191
|
-
reserved,
|
|
192
|
-
invoke: () => ({
|
|
193
|
-
id: '@instance-node',
|
|
194
|
-
slug: 'demo',
|
|
195
|
-
url: 'https://demo.eu.astrale.ai',
|
|
196
|
-
state: 'ready',
|
|
197
|
-
}),
|
|
198
|
-
})
|
|
199
|
-
const api = await contract.connect()
|
|
200
|
-
|
|
201
|
-
await expect(api.create('demo', 'admin')).rejects.toMatchObject({ name: 'NotFoundError' })
|
|
202
|
-
await expect(api.create('demo', 'consumer-paris')).resolves.toMatchObject({ slug: 'demo' })
|
|
203
|
-
expect(contract.invoke).toHaveBeenCalledWith(
|
|
204
|
-
expect.objectContaining({ owner: 'Host', name: 'createInstance' }),
|
|
205
|
-
Path.id(NodeId('consumer-host')),
|
|
206
|
-
{ operationId: 'cli.instance.create:test', slug: 'demo' },
|
|
207
|
-
)
|
|
88
|
+
expect(contract.calls).toEqual([
|
|
89
|
+
{
|
|
90
|
+
target: '/:admin.astrale.ai:core.fleet::createInstance',
|
|
91
|
+
value: { operationId: 'cli.instance.create:test', slug: 'demo' },
|
|
92
|
+
},
|
|
93
|
+
])
|
|
208
94
|
})
|
|
209
95
|
|
|
210
96
|
test('refreshes and deletes through the resolved Instance receiver', async () => {
|
|
211
|
-
const instance = node('instance-node', {
|
|
212
|
-
slug: 'demo',
|
|
213
|
-
url: 'https://demo.eu.astrale.ai',
|
|
214
|
-
state: 'ready',
|
|
215
|
-
})
|
|
216
97
|
const contract = fixture({
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
method.name === 'listInstances'
|
|
98
|
+
invoke: (target) =>
|
|
99
|
+
target.endsWith('::listInstances')
|
|
220
100
|
? [
|
|
221
101
|
{
|
|
222
102
|
id: '@instance-node',
|
|
@@ -231,31 +111,34 @@ describe('V2 Admin Instance adapter', () => {
|
|
|
231
111
|
id: '@instance-node',
|
|
232
112
|
slug: 'demo',
|
|
233
113
|
url: 'https://demo.eu.astrale.ai',
|
|
234
|
-
state:
|
|
114
|
+
state: target.endsWith('::delete') ? 'deleted' : 'ready',
|
|
115
|
+
createdAt: '2026-08-12T00:00:00.000Z',
|
|
116
|
+
updatedAt: '2026-08-12T00:00:00.000Z',
|
|
235
117
|
},
|
|
236
118
|
})
|
|
237
119
|
const api = await contract.connect()
|
|
238
120
|
|
|
239
121
|
await expect(api.status('demo')).resolves.toMatchObject({ state: 'ready' })
|
|
240
122
|
await expect(api.delete('@instance-node')).resolves.toMatchObject({ state: 'deleted' })
|
|
241
|
-
expect(contract.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
123
|
+
expect(contract.calls).toEqual([
|
|
124
|
+
{ target: '/:admin.astrale.ai:core.fleet::listInstances', value: {} },
|
|
125
|
+
{
|
|
126
|
+
target: '@instance-node::status',
|
|
127
|
+
value: { operationId: 'cli.instance.status:test' },
|
|
128
|
+
},
|
|
129
|
+
{ target: '/:admin.astrale.ai:core.fleet::listInstances', value: {} },
|
|
130
|
+
{
|
|
131
|
+
target: '@instance-node::delete',
|
|
132
|
+
value: { operationId: 'cli.instance.delete:test' },
|
|
133
|
+
},
|
|
246
134
|
])
|
|
135
|
+
expect(contract.reflection).not.toHaveBeenCalled()
|
|
247
136
|
})
|
|
248
137
|
|
|
249
138
|
test('installs a resolved catalog Domain through Instance.installDomain', async () => {
|
|
250
|
-
const instance = node('instance-node', {
|
|
251
|
-
slug: 'demo',
|
|
252
|
-
url: 'https://demo.eu.astrale.ai',
|
|
253
|
-
state: 'ready',
|
|
254
|
-
})
|
|
255
139
|
const contract = fixture({
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
method.name === 'listInstances'
|
|
140
|
+
invoke: (target) =>
|
|
141
|
+
target.endsWith('::listInstances')
|
|
259
142
|
? [
|
|
260
143
|
{
|
|
261
144
|
id: '@instance-node',
|
|
@@ -282,10 +165,109 @@ describe('V2 Admin Instance adapter', () => {
|
|
|
282
165
|
origin: 'crm.acme.dev',
|
|
283
166
|
ok: true,
|
|
284
167
|
})
|
|
285
|
-
expect(contract.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
168
|
+
expect(contract.calls.at(-1)).toEqual({
|
|
169
|
+
target: '@instance-node::installDomain',
|
|
170
|
+
value: { operationId: 'cli.instance.install-domain:test', domain: '@crm-domain' },
|
|
171
|
+
})
|
|
172
|
+
expect(contract.reflection).not.toHaveBeenCalled()
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test('connects without network I/O and list performs exactly one call', async () => {
|
|
176
|
+
const contract = fixture({ invoke: () => [] })
|
|
177
|
+
|
|
178
|
+
const api = await contract.connect()
|
|
179
|
+
expect(contract.call).not.toHaveBeenCalled()
|
|
180
|
+
expect(contract.reflection).not.toHaveBeenCalled()
|
|
181
|
+
|
|
182
|
+
await expect(api.list()).resolves.toEqual([])
|
|
183
|
+
expect(contract.call).toHaveBeenCalledTimes(1)
|
|
184
|
+
expect(contract.reflection).not.toHaveBeenCalled()
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
test('rejects malformed inventory, lifecycle, Method, and install outputs', async () => {
|
|
188
|
+
await expect((await fixture({ invoke: () => ({}) }).connect()).list()).rejects.toThrow(
|
|
189
|
+
'Admin Instance inventory is invalid.',
|
|
190
|
+
)
|
|
191
|
+
await expect(
|
|
192
|
+
(
|
|
193
|
+
await fixture({
|
|
194
|
+
invoke: () => [{ id: '@instance-node', slug: 'demo', state: 'mystery' }],
|
|
195
|
+
}).connect()
|
|
196
|
+
).list(),
|
|
197
|
+
).rejects.toThrow('Admin Instance state is invalid.')
|
|
198
|
+
await expect(
|
|
199
|
+
(
|
|
200
|
+
await fixture({
|
|
201
|
+
invoke: () => [{ id: 'not-a-path', slug: 'demo', state: 'ready' }],
|
|
202
|
+
}).connect()
|
|
203
|
+
).list(),
|
|
204
|
+
).rejects.toThrow('Admin Instance id is invalid.')
|
|
205
|
+
await expect(
|
|
206
|
+
(
|
|
207
|
+
await fixture({
|
|
208
|
+
invoke: () => [
|
|
209
|
+
{ id: '@instance-node', slug: 'demo', hostId: 'not-a-path', state: 'ready' },
|
|
210
|
+
],
|
|
211
|
+
}).connect()
|
|
212
|
+
).list(),
|
|
213
|
+
).rejects.toThrow('Admin Host id is invalid.')
|
|
214
|
+
|
|
215
|
+
const malformedStatus = fixture({
|
|
216
|
+
invoke: (target) =>
|
|
217
|
+
target.endsWith('::listInstances')
|
|
218
|
+
? [
|
|
219
|
+
{
|
|
220
|
+
id: '@instance-node',
|
|
221
|
+
slug: 'demo',
|
|
222
|
+
state: 'ready',
|
|
223
|
+
createdAt: '2026-08-12T00:00:00.000Z',
|
|
224
|
+
updatedAt: '2026-08-12T00:00:00.000Z',
|
|
225
|
+
},
|
|
226
|
+
]
|
|
227
|
+
: {
|
|
228
|
+
id: '@instance-node',
|
|
229
|
+
slug: 'demo',
|
|
230
|
+
state: 'ready',
|
|
231
|
+
failure: {},
|
|
232
|
+
createdAt: '2026-08-12T00:00:00.000Z',
|
|
233
|
+
updatedAt: '2026-08-12T00:00:00.000Z',
|
|
234
|
+
},
|
|
235
|
+
})
|
|
236
|
+
await expect((await malformedStatus.connect()).status('demo')).rejects.toThrow(
|
|
237
|
+
'Admin failure message is invalid.',
|
|
289
238
|
)
|
|
239
|
+
|
|
240
|
+
const malformedInstall = fixture({
|
|
241
|
+
invoke: (target) =>
|
|
242
|
+
target.endsWith('::listInstances')
|
|
243
|
+
? [
|
|
244
|
+
{
|
|
245
|
+
id: '@instance-node',
|
|
246
|
+
slug: 'demo',
|
|
247
|
+
state: 'ready',
|
|
248
|
+
createdAt: '2026-08-12T00:00:00.000Z',
|
|
249
|
+
updatedAt: '2026-08-12T00:00:00.000Z',
|
|
250
|
+
},
|
|
251
|
+
]
|
|
252
|
+
: {
|
|
253
|
+
domain: '@crm-domain',
|
|
254
|
+
instance: '@instance-node',
|
|
255
|
+
origin: 'crm.acme.dev',
|
|
256
|
+
ok: 'yes',
|
|
257
|
+
},
|
|
258
|
+
})
|
|
259
|
+
await expect(
|
|
260
|
+
(await malformedInstall.connect()).installDomain('demo', '@crm-domain'),
|
|
261
|
+
).rejects.toThrow('Admin Domain install outcome is invalid.')
|
|
262
|
+
|
|
263
|
+
const malformedInstallPath = fixture({
|
|
264
|
+
invoke: (target) =>
|
|
265
|
+
target.endsWith('::listInstances')
|
|
266
|
+
? [{ id: '@instance-node', slug: 'demo', state: 'ready' }]
|
|
267
|
+
: { domain: 'not-a-path', instance: '@instance-node', origin: 'crm.acme.dev', ok: true },
|
|
268
|
+
})
|
|
269
|
+
await expect(
|
|
270
|
+
(await malformedInstallPath.connect()).installDomain('demo', '@crm-domain'),
|
|
271
|
+
).rejects.toThrow('Admin Domain reference is invalid.')
|
|
290
272
|
})
|
|
291
273
|
})
|