@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
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import type { ClientSession } from '@astrale-os/
|
|
2
|
-
import type { Node } from '@astrale-os/sdk/graph/node'
|
|
1
|
+
import type { ClientSession } from '@astrale-os/sdk/client/session'
|
|
3
2
|
|
|
4
3
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
5
|
-
import { Query } from '@astrale-os/sdk/query'
|
|
6
4
|
|
|
7
|
-
import {
|
|
8
|
-
import { readAllNodes, type AdminGraphApi } from '../graph'
|
|
5
|
+
import { AdminContract, callAdminMethod } from '../contract'
|
|
9
6
|
import {
|
|
10
|
-
AdminHostNotFoundError,
|
|
11
7
|
AdminInstanceNotFoundError,
|
|
12
8
|
findOwnedInstance,
|
|
13
9
|
type DomainInstallReceipt,
|
|
@@ -16,62 +12,38 @@ import {
|
|
|
16
12
|
type OwnedInstanceInfo,
|
|
17
13
|
} from './model'
|
|
18
14
|
|
|
19
|
-
const ADMIN_ORIGIN = 'admin.astrale.ai'
|
|
20
|
-
const PAGE_SIZE = 256
|
|
21
|
-
const MAXIMUM_INSTANCES = 10_000
|
|
22
|
-
const MAXIMUM_PAGES = Math.ceil(MAXIMUM_INSTANCES / PAGE_SIZE) + 1
|
|
23
|
-
|
|
24
|
-
const HostRef = Object.freeze({ origin: ADMIN_ORIGIN, kind: 'class' as const, name: 'Host' })
|
|
25
|
-
const fleetReservesAdminHost = Object.freeze({
|
|
26
|
-
origin: ADMIN_ORIGIN,
|
|
27
|
-
kind: 'class' as const,
|
|
28
|
-
name: 'fleet_reserves_admin_host',
|
|
29
|
-
})
|
|
30
|
-
|
|
31
15
|
export interface AdminInstanceContext {
|
|
32
16
|
readonly session: ClientSession
|
|
33
|
-
readonly graph: AdminGraphApi
|
|
34
17
|
}
|
|
35
18
|
|
|
36
19
|
export interface AdminInstanceApi {
|
|
37
20
|
list(): Promise<OwnedInstanceInfo[]>
|
|
38
|
-
create(slug: string
|
|
21
|
+
create(slug: string): Promise<InstanceInfo>
|
|
39
22
|
status(identifier: string): Promise<InstanceInfo>
|
|
40
23
|
delete(identifier: string): Promise<InstanceInfo>
|
|
41
24
|
installDomain(identifier: string, domain: string): Promise<DomainInstallReceipt>
|
|
42
25
|
}
|
|
43
26
|
|
|
44
27
|
export interface AdminInstanceDependencies {
|
|
45
|
-
readonly bind?: (session: ClientSession) => Promise<AdminBinding>
|
|
46
28
|
readonly operationId?: (kind: 'create' | 'status' | 'delete' | 'install-domain') => string
|
|
47
29
|
}
|
|
48
30
|
|
|
49
31
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
32
|
+
* Connect the public Instance journey through stable Admin call paths. Routine
|
|
33
|
+
* operations perform no schema discovery. No Host lifecycle method is present.
|
|
52
34
|
*/
|
|
53
35
|
export async function connectAdminInstances(
|
|
54
36
|
context: AdminInstanceContext,
|
|
55
37
|
dependencies: AdminInstanceDependencies = {},
|
|
56
38
|
): Promise<AdminInstanceApi> {
|
|
57
|
-
const binding = await (dependencies.bind ?? bindAdmin)(context.session)
|
|
58
|
-
if (binding.$.publication?.origin !== ADMIN_ORIGIN || binding.$.origin !== ADMIN_ORIGIN) {
|
|
59
|
-
throw new TypeError('Configured Admin target does not serve the Admin Domain.')
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const Instance = binding.$.class('Instance')
|
|
63
|
-
const Host = binding.$.class('Host')
|
|
64
|
-
const Fleet = binding.$.class('Fleet')
|
|
65
|
-
const fleet = binding.$.core.nodes.fleet?.path
|
|
66
|
-
if (fleet === undefined) throw new TypeError('Admin Domain has no singleton Fleet receiver.')
|
|
67
|
-
|
|
68
39
|
const operationId = dependencies.operationId ?? defaultOperationId
|
|
69
40
|
|
|
70
41
|
const list = async (): Promise<OwnedInstanceInfo[]> => {
|
|
71
|
-
const result: unknown = await
|
|
72
|
-
|
|
73
|
-
fleet,
|
|
74
|
-
|
|
42
|
+
const result: unknown = await callAdminMethod(
|
|
43
|
+
context.session,
|
|
44
|
+
AdminContract.fleet,
|
|
45
|
+
'listInstances',
|
|
46
|
+
{},
|
|
75
47
|
)
|
|
76
48
|
if (!Array.isArray(result)) throw new TypeError('Admin Instance inventory is invalid.')
|
|
77
49
|
return result.map((value) => instanceFromSummary(value) as OwnedInstanceInfo)
|
|
@@ -88,121 +60,55 @@ export async function connectAdminInstances(
|
|
|
88
60
|
identifier: string,
|
|
89
61
|
): Promise<InstanceInfo> => {
|
|
90
62
|
const instance = await requireInstance(identifier)
|
|
91
|
-
const output = await
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
{ operationId: operationId(method) } as never,
|
|
95
|
-
)
|
|
63
|
+
const output = await callAdminMethod(context.session, Path.parse(instance.id), method, {
|
|
64
|
+
operationId: operationId(method),
|
|
65
|
+
})
|
|
96
66
|
return instanceFromSummary(output)
|
|
97
67
|
}
|
|
98
68
|
|
|
99
69
|
return Object.freeze({
|
|
100
70
|
list,
|
|
101
|
-
async create(slug: string
|
|
71
|
+
async create(slug: string) {
|
|
102
72
|
const input = Object.freeze({
|
|
103
73
|
operationId: operationId('create'),
|
|
104
74
|
slug,
|
|
105
75
|
})
|
|
106
|
-
if (hostId !== undefined) {
|
|
107
|
-
const selected = await hostInventory(context.graph, binding, Host, fleet)
|
|
108
|
-
const host = selected.hosts.find(
|
|
109
|
-
(item) => item.id === hostId && item.state === 'ready' && item.path !== selected.reserved,
|
|
110
|
-
)
|
|
111
|
-
if (host === undefined) throw new AdminHostNotFoundError(hostId)
|
|
112
|
-
return instanceFromSummary(
|
|
113
|
-
await binding.$.invoke(
|
|
114
|
-
Host.$.method('createInstance') as never,
|
|
115
|
-
Path.parse(host.path),
|
|
116
|
-
input as never,
|
|
117
|
-
),
|
|
118
|
-
)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Default placement belongs to Admin. Avoid a redundant Host graph read:
|
|
122
|
-
// it requires broader graph authority than this Fleet operation and can
|
|
123
|
-
// deny an otherwise authorized caller before the server selects a Host.
|
|
124
76
|
return instanceFromSummary(
|
|
125
|
-
await
|
|
77
|
+
await callAdminMethod(context.session, AdminContract.fleet, 'createInstance', input),
|
|
126
78
|
)
|
|
127
79
|
},
|
|
128
80
|
status: (identifier: string) => invokeInstance('status', identifier),
|
|
129
81
|
delete: (identifier: string) => invokeInstance('delete', identifier),
|
|
130
82
|
async installDomain(identifier: string, domain: string): Promise<DomainInstallReceipt> {
|
|
131
83
|
const instance = await requireInstance(identifier)
|
|
132
|
-
const output = await
|
|
133
|
-
|
|
84
|
+
const output = await callAdminMethod(
|
|
85
|
+
context.session,
|
|
134
86
|
Path.parse(instance.id),
|
|
87
|
+
'installDomain',
|
|
135
88
|
{
|
|
136
89
|
operationId: operationId('install-domain'),
|
|
137
90
|
domain: Path.parse(domain).raw,
|
|
138
|
-
}
|
|
91
|
+
},
|
|
139
92
|
)
|
|
140
93
|
return domainInstallReceipt(output)
|
|
141
94
|
},
|
|
142
95
|
})
|
|
143
96
|
}
|
|
144
97
|
|
|
145
|
-
interface HostInventoryItem {
|
|
146
|
-
readonly id: string
|
|
147
|
-
readonly nodeId: string
|
|
148
|
-
readonly path: string
|
|
149
|
-
readonly state: string
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async function hostInventory(
|
|
153
|
-
graph: AdminGraphApi,
|
|
154
|
-
binding: AdminBinding,
|
|
155
|
-
Host: ReturnType<AdminBinding['$']['class']>,
|
|
156
|
-
fleet: Path,
|
|
157
|
-
): Promise<{ readonly hosts: readonly HostInventoryItem[]; readonly reserved?: string }> {
|
|
158
|
-
const [nodes, reservedPage] = await Promise.all([
|
|
159
|
-
readAllNodes(
|
|
160
|
-
graph,
|
|
161
|
-
Query.from({ kind: 'node', definitions: [HostRef] }).select({
|
|
162
|
-
kind: 'nodes',
|
|
163
|
-
projection: { kind: 'value' },
|
|
164
|
-
}),
|
|
165
|
-
{
|
|
166
|
-
label: 'Admin Host inventory',
|
|
167
|
-
maximum: MAXIMUM_INSTANCES,
|
|
168
|
-
maximumPages: MAXIMUM_PAGES,
|
|
169
|
-
},
|
|
170
|
-
),
|
|
171
|
-
graph.neighbors(fleet, fleetReservesAdminHost, {
|
|
172
|
-
direction: 'outgoing',
|
|
173
|
-
page: { size: 1 },
|
|
174
|
-
}),
|
|
175
|
-
])
|
|
176
|
-
const reserved = reservedPage.first === null ? undefined : Path.id(reservedPage.first.id).raw
|
|
177
|
-
return Object.freeze({
|
|
178
|
-
hosts: Object.freeze(
|
|
179
|
-
nodes.map((node) =>
|
|
180
|
-
Object.freeze({
|
|
181
|
-
id: requiredStringProperty(Host, node, 'id'),
|
|
182
|
-
nodeId: String(node.id),
|
|
183
|
-
path: Path.id(node.id).raw,
|
|
184
|
-
state: requiredStringProperty(Host, node, 'state'),
|
|
185
|
-
}),
|
|
186
|
-
),
|
|
187
|
-
),
|
|
188
|
-
...(reserved === undefined ? {} : { reserved }),
|
|
189
|
-
})
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
type DynamicDefinition = ReturnType<AdminBinding['$']['class']>
|
|
193
|
-
|
|
194
98
|
function instanceFromSummary(input: unknown): InstanceInfo {
|
|
195
99
|
const value = record(input, 'Admin Instance summary')
|
|
196
100
|
const failure = value.failure === undefined ? undefined : record(value.failure, 'Admin failure')
|
|
197
101
|
return Object.freeze({
|
|
198
|
-
id:
|
|
102
|
+
id: requiredNodePath(value.id, 'Admin Instance id'),
|
|
199
103
|
slug: requiredString(value.slug, 'Admin Instance slug'),
|
|
200
104
|
url: optionalStringValue(value.url) ?? '',
|
|
201
|
-
state: instanceState(value.state),
|
|
202
105
|
...(value.hostId === undefined
|
|
203
106
|
? {}
|
|
204
|
-
: { hostId:
|
|
205
|
-
...(value.region === undefined
|
|
107
|
+
: { hostId: requiredNodePath(value.hostId, 'Admin Host id') }),
|
|
108
|
+
...(value.region === undefined
|
|
109
|
+
? {}
|
|
110
|
+
: { region: requiredString(value.region, 'Admin Host region') }),
|
|
111
|
+
state: instanceState(value.state),
|
|
206
112
|
...(value.phase === undefined
|
|
207
113
|
? {}
|
|
208
114
|
: { phase: requiredString(value.phase, 'Admin Instance phase') }),
|
|
@@ -212,6 +118,9 @@ function instanceFromSummary(input: unknown): InstanceInfo {
|
|
|
212
118
|
...(value.createdAt === undefined
|
|
213
119
|
? {}
|
|
214
120
|
: { createdAt: requiredString(value.createdAt, 'Admin Instance creation time') }),
|
|
121
|
+
...(value.updatedAt === undefined
|
|
122
|
+
? {}
|
|
123
|
+
: { updatedAt: requiredString(value.updatedAt, 'Admin Instance update time') }),
|
|
215
124
|
...(value.organizationId === undefined
|
|
216
125
|
? {}
|
|
217
126
|
: { organizationId: requiredString(value.organizationId, 'Admin organization id') }),
|
|
@@ -223,8 +132,8 @@ function domainInstallReceipt(input: unknown): DomainInstallReceipt {
|
|
|
223
132
|
const failure = value.failure === undefined ? undefined : record(value.failure, 'Admin failure')
|
|
224
133
|
if (typeof value.ok !== 'boolean') throw new TypeError('Admin Domain install outcome is invalid.')
|
|
225
134
|
return Object.freeze({
|
|
226
|
-
domain:
|
|
227
|
-
instance:
|
|
135
|
+
domain: requiredNodePath(value.domain, 'Admin Domain reference'),
|
|
136
|
+
instance: requiredNodePath(value.instance, 'Admin Instance reference'),
|
|
228
137
|
origin: requiredString(value.origin, 'Installed Domain origin'),
|
|
229
138
|
ok: value.ok,
|
|
230
139
|
...(value.installedRevision === undefined
|
|
@@ -238,13 +147,6 @@ function domainInstallReceipt(input: unknown): DomainInstallReceipt {
|
|
|
238
147
|
})
|
|
239
148
|
}
|
|
240
149
|
|
|
241
|
-
function requiredStringProperty(definition: DynamicDefinition, node: Node, name: string): string {
|
|
242
|
-
return requiredString(
|
|
243
|
-
node.props[definition.$.property(name).key],
|
|
244
|
-
`Admin ${definition.name}.${name}`,
|
|
245
|
-
)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
150
|
function instanceState(input: unknown): InstanceState {
|
|
249
151
|
if (
|
|
250
152
|
input === 'provisioning' ||
|
|
@@ -263,6 +165,15 @@ function requiredString(input: unknown, label: string): string {
|
|
|
263
165
|
return input
|
|
264
166
|
}
|
|
265
167
|
|
|
168
|
+
function requiredNodePath(input: unknown, label: string): string {
|
|
169
|
+
const value = requiredString(input, label)
|
|
170
|
+
try {
|
|
171
|
+
return Path.parse(value).raw
|
|
172
|
+
} catch {
|
|
173
|
+
throw new TypeError(`${label} is invalid.`)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
266
177
|
function optionalStringValue(input: unknown): string | undefined {
|
|
267
178
|
if (input === undefined) return undefined
|
|
268
179
|
return requiredString(input, 'Admin string value')
|
|
@@ -5,10 +5,9 @@ export {
|
|
|
5
5
|
type AdminInstanceDependencies,
|
|
6
6
|
} from './client'
|
|
7
7
|
export {
|
|
8
|
-
AdminHostNotFoundError,
|
|
9
8
|
AdminInstanceNotFoundError,
|
|
10
9
|
findOwnedInstance,
|
|
11
|
-
|
|
10
|
+
formatInstanceState,
|
|
12
11
|
type DomainInstallReceipt,
|
|
13
12
|
type InstanceInfo,
|
|
14
13
|
type InstanceState,
|
|
@@ -7,10 +7,11 @@ export interface InstanceInfo {
|
|
|
7
7
|
readonly url: string
|
|
8
8
|
readonly hostId?: string
|
|
9
9
|
readonly region?: string
|
|
10
|
-
readonly state
|
|
10
|
+
readonly state: InstanceState
|
|
11
11
|
readonly phase?: string
|
|
12
12
|
readonly error?: string | null
|
|
13
13
|
readonly createdAt?: string
|
|
14
|
+
readonly updatedAt?: string
|
|
14
15
|
readonly organizationId?: string
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -28,19 +29,13 @@ export interface DomainInstallReceipt {
|
|
|
28
29
|
readonly error?: string
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
export class AdminInstanceNotFoundError extends
|
|
32
|
-
readonly name = 'NotFoundError'
|
|
33
|
-
|
|
34
|
-
constructor(readonly identifier: string) {
|
|
35
|
-
super(`No owned Admin Instance matches ${JSON.stringify(identifier)}.`)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class AdminHostNotFoundError extends Error {
|
|
40
|
-
readonly name = 'NotFoundError'
|
|
41
|
-
|
|
32
|
+
export class AdminInstanceNotFoundError extends AstraleError {
|
|
42
33
|
constructor(readonly identifier: string) {
|
|
43
|
-
super(
|
|
34
|
+
super(
|
|
35
|
+
'INSTANCE_NOT_FOUND',
|
|
36
|
+
`No owned Admin Instance matches ${JSON.stringify(identifier)}.`,
|
|
37
|
+
'Run `astrale instance list` to see your instances.',
|
|
38
|
+
)
|
|
44
39
|
}
|
|
45
40
|
}
|
|
46
41
|
|
|
@@ -57,9 +52,8 @@ export function findOwnedInstance(
|
|
|
57
52
|
)
|
|
58
53
|
}
|
|
59
54
|
|
|
60
|
-
/** Human-readable
|
|
61
|
-
export function
|
|
62
|
-
return
|
|
63
|
-
.filter(Boolean)
|
|
64
|
-
.join(' · ')
|
|
55
|
+
/** Human-readable non-ready lifecycle state for one Instance. */
|
|
56
|
+
export function formatInstanceState(info: InstanceInfo): string {
|
|
57
|
+
return info.state === 'ready' ? '' : `${info.state}${info.phase ? ` (${info.phase})` : ''}`
|
|
65
58
|
}
|
|
59
|
+
import { AstraleError } from '../../errors'
|
|
@@ -4,7 +4,10 @@ import type { IdentityStore } from '../../identity/index'
|
|
|
4
4
|
|
|
5
5
|
import { AuthError } from '../../errors'
|
|
6
6
|
import { findOwnedInstance, type OwnedInstanceInfo } from '../../lib/admin-instance'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
assertInstanceCreateIdentity,
|
|
9
|
+
selectInstanceCreateIdentity,
|
|
10
|
+
} from '../../lib/provision-instance'
|
|
8
11
|
|
|
9
12
|
describe('admin-backed instance commands', () => {
|
|
10
13
|
test('findOwnedInstance matches owner inventory by slug or stable node id', () => {
|
|
@@ -14,8 +17,6 @@ describe('admin-backed instance commands', () => {
|
|
|
14
17
|
url: 'https://demo.eu.astrale.ai',
|
|
15
18
|
state: 'failed',
|
|
16
19
|
organizationId: 'org_123',
|
|
17
|
-
hostId: 'host-1',
|
|
18
|
-
region: 'eu',
|
|
19
20
|
phase: 'installing:default-domains',
|
|
20
21
|
error: 'postInstall failed',
|
|
21
22
|
createdAt: '2026-07-16T00:00:00.000Z',
|
|
@@ -39,8 +40,8 @@ describe('admin-backed instance commands', () => {
|
|
|
39
40
|
},
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
expect(() =>
|
|
43
|
-
expect(() =>
|
|
43
|
+
expect(() => assertInstanceCreateIdentity(store)).toThrow(AuthError)
|
|
44
|
+
expect(() => assertInstanceCreateIdentity(store)).toThrow('WorkOS login required')
|
|
44
45
|
})
|
|
45
46
|
|
|
46
47
|
test('instance create preflight accepts IdP-backed identities', () => {
|
|
@@ -58,7 +59,8 @@ describe('admin-backed instance commands', () => {
|
|
|
58
59
|
},
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
expect(() =>
|
|
62
|
+
expect(() => assertInstanceCreateIdentity(store)).not.toThrow()
|
|
63
|
+
expect(selectInstanceCreateIdentity(store)).toBe('bryan')
|
|
62
64
|
})
|
|
63
65
|
|
|
64
66
|
test('--as selection must also be IdP-backed', () => {
|
|
@@ -82,7 +84,8 @@ describe('admin-backed instance commands', () => {
|
|
|
82
84
|
},
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
expect(() =>
|
|
86
|
-
expect(() =>
|
|
87
|
+
expect(() => assertInstanceCreateIdentity(store, { as: 'local' })).toThrow(AuthError)
|
|
88
|
+
expect(() => assertInstanceCreateIdentity(store, { as: 'workos' })).not.toThrow()
|
|
89
|
+
expect(selectInstanceCreateIdentity(store, { as: 'workos' })).toBe('workos')
|
|
87
90
|
})
|
|
88
91
|
})
|
|
@@ -1,34 +1,47 @@
|
|
|
1
1
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
2
|
+
import { bundle, defineSchema, func, method, nodeClass } from '@astrale-os/sdk/schema'
|
|
2
3
|
import { describe, expect, test } from 'bun:test'
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
+
import { describeCallableFromBundle } from '../call-describe'
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
output: { mode: 'value' },
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
},
|
|
7
|
+
const createInstance = method({
|
|
8
|
+
auth: 'authenticated',
|
|
9
|
+
description: 'Create one child Instance.',
|
|
10
|
+
input: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: { operationId: { type: 'string' }, slug: { type: 'string' } },
|
|
13
|
+
required: ['operationId', 'slug'],
|
|
14
|
+
additionalProperties: false,
|
|
18
15
|
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
output: { type: 'object', additionalProperties: true },
|
|
17
|
+
static: true,
|
|
18
|
+
})
|
|
19
|
+
const inspectInstance = method({
|
|
20
|
+
auth: 'authenticated',
|
|
21
|
+
description: 'Inspect one child Instance.',
|
|
22
|
+
input: { type: 'object', properties: {}, additionalProperties: false },
|
|
23
|
+
output: { type: 'object', additionalProperties: true },
|
|
24
|
+
})
|
|
25
|
+
const journal = func({
|
|
26
|
+
auth: 'authenticated',
|
|
27
|
+
description: 'Read the authorized Kernel journal.',
|
|
28
|
+
input: { type: 'object', properties: {}, additionalProperties: false },
|
|
29
|
+
output: { type: 'object', additionalProperties: true },
|
|
30
|
+
})
|
|
31
|
+
const source = defineSchema('host.astrale.ai', {
|
|
32
|
+
classes: {
|
|
33
|
+
Manager: nodeClass({ methods: { createInstance } }),
|
|
34
|
+
Instance: nodeClass({ methods: { inspectInstance } }),
|
|
24
35
|
},
|
|
25
|
-
}
|
|
36
|
+
functions: { journal },
|
|
37
|
+
})
|
|
38
|
+
const installed = bundle.create(source)
|
|
26
39
|
|
|
27
|
-
describe('
|
|
28
|
-
test('reads a static Class method from the Domain
|
|
29
|
-
const described =
|
|
40
|
+
describe('describeCallableFromBundle', () => {
|
|
41
|
+
test('reads a static Class method from the resolved Domain', () => {
|
|
42
|
+
const described = describeCallableFromBundle(
|
|
30
43
|
Path.parse('/:host.astrale.ai:class.Manager:createInstance'),
|
|
31
|
-
|
|
44
|
+
installed,
|
|
32
45
|
)
|
|
33
46
|
expect(described).toMatchObject({
|
|
34
47
|
origin: 'host.astrale.ai',
|
|
@@ -36,30 +49,30 @@ describe('describeCallableFromSchema', () => {
|
|
|
36
49
|
method: 'createInstance',
|
|
37
50
|
dispatch: 'static',
|
|
38
51
|
description: 'Create one child Instance.',
|
|
39
|
-
auth: '
|
|
52
|
+
auth: 'authenticated',
|
|
40
53
|
})
|
|
41
54
|
expect(described?.input).toMatchObject({ required: ['operationId', 'slug'] })
|
|
42
55
|
})
|
|
43
56
|
|
|
44
|
-
test('finds
|
|
57
|
+
test('finds one instance method when the receiver Path is not a Class projection', () => {
|
|
45
58
|
expect(
|
|
46
|
-
|
|
47
|
-
Path.parse('/:host.astrale.ai:core.manager::
|
|
48
|
-
|
|
59
|
+
describeCallableFromBundle(
|
|
60
|
+
Path.parse('/:host.astrale.ai:core.manager::inspectInstance'),
|
|
61
|
+
installed,
|
|
49
62
|
),
|
|
50
63
|
).toMatchObject({
|
|
51
64
|
origin: 'host.astrale.ai',
|
|
52
|
-
class: '
|
|
53
|
-
method: '
|
|
65
|
+
class: 'Instance',
|
|
66
|
+
method: 'inspectInstance',
|
|
54
67
|
dispatch: 'instance',
|
|
55
68
|
})
|
|
56
69
|
})
|
|
57
70
|
|
|
58
|
-
test('reads a standalone Function from the Domain
|
|
71
|
+
test('reads a standalone Function from the resolved Domain', () => {
|
|
59
72
|
expect(
|
|
60
|
-
|
|
73
|
+
describeCallableFromBundle(Path.parse('/:host.astrale.ai:function.journal'), installed),
|
|
61
74
|
).toMatchObject({
|
|
62
|
-
origin: '
|
|
75
|
+
origin: 'host.astrale.ai',
|
|
63
76
|
function: 'journal',
|
|
64
77
|
description: 'Read the authorized Kernel journal.',
|
|
65
78
|
})
|
|
@@ -26,4 +26,38 @@ describe('call command result lifetime', () => {
|
|
|
26
26
|
if (materialized.kind !== 'stream') throw new Error('expected a materialized stream')
|
|
27
27
|
expect(Object.isFrozen(materialized.values)).toBe(true)
|
|
28
28
|
})
|
|
29
|
+
|
|
30
|
+
test('drains streaming binary bytes before the command session closes', async () => {
|
|
31
|
+
let sessionOpen = true
|
|
32
|
+
async function* body() {
|
|
33
|
+
for (const chunk of [new Uint8Array([1, 2]), new Uint8Array([3])]) {
|
|
34
|
+
if (!sessionOpen) throw new Error('session closed before binary consumption')
|
|
35
|
+
yield chunk
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const materialized = await materializeCallResult({
|
|
40
|
+
kind: 'binary',
|
|
41
|
+
invocation: { source: 'https://kernel.test' as never, id: 'test-binary' },
|
|
42
|
+
value: {
|
|
43
|
+
body: body(),
|
|
44
|
+
mediaType: 'application/octet-stream',
|
|
45
|
+
status: 206,
|
|
46
|
+
headers: { 'content-range': 'bytes 0-2/3' },
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
sessionOpen = false
|
|
50
|
+
|
|
51
|
+
expect(materialized).toMatchObject({
|
|
52
|
+
kind: 'binary',
|
|
53
|
+
value: {
|
|
54
|
+
mediaType: 'application/octet-stream',
|
|
55
|
+
status: 206,
|
|
56
|
+
headers: { 'content-range': 'bytes 0-2/3' },
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
if (materialized.kind !== 'binary') throw new Error('expected materialized binary')
|
|
60
|
+
expect([...materialized.value.body]).toEqual([1, 2, 3])
|
|
61
|
+
expect(Object.isFrozen(materialized.value)).toBe(true)
|
|
62
|
+
})
|
|
29
63
|
})
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { issuer } from '@astrale-os/sdk/auth'
|
|
3
|
-
import { createDeployment } from '@astrale-os/sdk/deployment'
|
|
4
|
-
import { defineSchema } from '@astrale-os/sdk/schema/v1'
|
|
1
|
+
import { defineSchema } from '@astrale-os/sdk/schema'
|
|
5
2
|
import { afterEach, describe, expect, mock, test } from 'bun:test'
|
|
6
3
|
|
|
4
|
+
import { releaseFor } from '../../__tests__/fixtures/publication'
|
|
7
5
|
import { installDirect } from '../domain/install'
|
|
8
6
|
|
|
9
7
|
const GENERATED = '4a4c9a18-50f6-4d84-a7b7-2d83e3e45dc8'
|
|
@@ -12,16 +10,7 @@ const RETRY = '139137b5-af47-47ce-92b2-b64a2b0c63d7'
|
|
|
12
10
|
const originalFetch = globalThis.fetch
|
|
13
11
|
|
|
14
12
|
const schema = defineSchema('crm.test', {})
|
|
15
|
-
const
|
|
16
|
-
schema,
|
|
17
|
-
handlers: { functions: {}, classes: {}, interfaces: {} },
|
|
18
|
-
})
|
|
19
|
-
const deployed = createDeployment({
|
|
20
|
-
definition,
|
|
21
|
-
issuer: issuer.accept('https://crm.test'),
|
|
22
|
-
bundleHref: 'https://crm.test/domain.bundle.json',
|
|
23
|
-
bindings: { callables: [] },
|
|
24
|
-
}).publication
|
|
13
|
+
const deployed = releaseFor(schema, 'https://crm.test').publication
|
|
25
14
|
|
|
26
15
|
function publicationResponse(): Response {
|
|
27
16
|
return Response.json(deployed)
|
|
@@ -49,9 +38,7 @@ describe('direct domain install operation identity', () => {
|
|
|
49
38
|
recovery = input.recovery
|
|
50
39
|
requests.push(
|
|
51
40
|
await input.fn({
|
|
52
|
-
session: {
|
|
53
|
-
call: async (call: { input: unknown }) => call.input,
|
|
54
|
-
},
|
|
41
|
+
session: { schema: { install: async (input: unknown) => input } },
|
|
55
42
|
} as never),
|
|
56
43
|
)
|
|
57
44
|
},
|
|
@@ -62,7 +49,7 @@ describe('direct domain install operation identity', () => {
|
|
|
62
49
|
expect(requests).toEqual([
|
|
63
50
|
{
|
|
64
51
|
operation: GENERATED,
|
|
65
|
-
domains: [{
|
|
52
|
+
domains: [{ publication: { url: 'https://crm.test' } }],
|
|
66
53
|
},
|
|
67
54
|
])
|
|
68
55
|
expect(recovery).toEqual({
|
|
@@ -89,9 +76,7 @@ describe('direct domain install operation identity', () => {
|
|
|
89
76
|
runKernelCommand: async (input) => {
|
|
90
77
|
requests.push(
|
|
91
78
|
await input.fn({
|
|
92
|
-
session: {
|
|
93
|
-
call: async (call: { input: unknown }) => call.input,
|
|
94
|
-
},
|
|
79
|
+
session: { schema: { install: async (input: unknown) => input } },
|
|
95
80
|
} as never),
|
|
96
81
|
)
|
|
97
82
|
},
|
|
@@ -103,7 +88,7 @@ describe('direct domain install operation identity', () => {
|
|
|
103
88
|
expect(requests).toEqual([
|
|
104
89
|
{
|
|
105
90
|
operation: RETRY,
|
|
106
|
-
domains: [{
|
|
91
|
+
domains: [{ publication: { url: 'https://crm.test' } }],
|
|
107
92
|
},
|
|
108
93
|
])
|
|
109
94
|
})
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { issuer } from '@astrale-os/sdk/auth'
|
|
3
|
-
import { createDeployment } from '@astrale-os/sdk/deployment'
|
|
4
|
-
import { defineSchema } from '@astrale-os/sdk/schema/v1'
|
|
1
|
+
import { defineSchema, schema as schemaApi } from '@astrale-os/sdk/schema'
|
|
5
2
|
import { afterAll, describe, expect, test } from 'bun:test'
|
|
6
3
|
|
|
7
4
|
import type { DomainInfo } from '../../lib/admin-domain'
|
|
8
5
|
|
|
6
|
+
import { releaseFor } from '../../__tests__/fixtures/publication'
|
|
9
7
|
import { byDefaultThenName, domainProjection, probe, type DomainRow } from '../domain/list'
|
|
10
8
|
|
|
11
9
|
const strip = (s: string): string => s.replace(/\[[0-9;]*m/g, '')
|
|
@@ -85,16 +83,7 @@ describe('domain list — canonical Publication check', () => {
|
|
|
85
83
|
|
|
86
84
|
test('reports the admitted schema revision in machine data', async () => {
|
|
87
85
|
const schema = defineSchema('catalog-check.example.dev', {})
|
|
88
|
-
const
|
|
89
|
-
schema,
|
|
90
|
-
handlers: { functions: {}, classes: {}, interfaces: {} },
|
|
91
|
-
})
|
|
92
|
-
const deployed = createDeployment({
|
|
93
|
-
definition,
|
|
94
|
-
issuer: issuer.accept('https://catalog-check.example.dev'),
|
|
95
|
-
bundleHref: 'https://catalog-check.example.dev/domain.bundle.json',
|
|
96
|
-
bindings: { callables: [] },
|
|
97
|
-
}).publication
|
|
86
|
+
const deployed = releaseFor(schema, 'https://catalog-check.example.dev').publication
|
|
98
87
|
const server = Bun.serve({
|
|
99
88
|
port: 0,
|
|
100
89
|
fetch(request) {
|
|
@@ -114,23 +103,14 @@ describe('domain list — canonical Publication check', () => {
|
|
|
114
103
|
),
|
|
115
104
|
).resolves.toMatchObject({
|
|
116
105
|
reachable: true,
|
|
117
|
-
schemaRevision:
|
|
106
|
+
schemaRevision: schemaApi.revision(schema),
|
|
118
107
|
checkError: null,
|
|
119
108
|
})
|
|
120
109
|
})
|
|
121
110
|
|
|
122
111
|
test('rejects a Publication whose origin differs from the catalog entry', async () => {
|
|
123
112
|
const schema = defineSchema('deployed.example.dev', {})
|
|
124
|
-
const
|
|
125
|
-
schema,
|
|
126
|
-
handlers: { functions: {}, classes: {}, interfaces: {} },
|
|
127
|
-
})
|
|
128
|
-
const deployed = createDeployment({
|
|
129
|
-
definition,
|
|
130
|
-
issuer: issuer.accept('https://deployed.example.dev'),
|
|
131
|
-
bundleHref: 'https://deployed.example.dev/domain.bundle.json',
|
|
132
|
-
bindings: { callables: [] },
|
|
133
|
-
}).publication
|
|
113
|
+
const deployed = releaseFor(schema, 'https://deployed.example.dev').publication
|
|
134
114
|
const server = Bun.serve({
|
|
135
115
|
port: 0,
|
|
136
116
|
fetch: () => Response.json(deployed),
|