@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
|
@@ -58,7 +58,7 @@ describe('view session private state', () => {
|
|
|
58
58
|
issuer: issuer('https://example.test'),
|
|
59
59
|
etag: `sha256:${'a'.repeat(64)}`,
|
|
60
60
|
revision: revision('b'),
|
|
61
|
-
declaration: { target: { kind: 'domain' }
|
|
61
|
+
declaration: { target: { kind: 'domain' } },
|
|
62
62
|
},
|
|
63
63
|
},
|
|
64
64
|
createdAt: '2026-08-12T00:00:00.000Z',
|
|
@@ -69,6 +69,7 @@ describe('view session private state', () => {
|
|
|
69
69
|
issuer: 'https://kernel.test',
|
|
70
70
|
direct: true,
|
|
71
71
|
},
|
|
72
|
+
externalOrigins: [],
|
|
72
73
|
idleMs: 60_000,
|
|
73
74
|
} satisfies ViewServeConfig
|
|
74
75
|
|
|
@@ -6,7 +6,7 @@ import { withAdminClientSession } from '../connection'
|
|
|
6
6
|
export {
|
|
7
7
|
AdminInstanceNotFoundError,
|
|
8
8
|
findOwnedInstance,
|
|
9
|
-
|
|
9
|
+
formatInstanceState,
|
|
10
10
|
type InstanceInfo,
|
|
11
11
|
type InstanceState,
|
|
12
12
|
type OwnedInstanceInfo,
|
|
@@ -19,19 +19,25 @@ export function listOwnedInstances(options: AdminConnectionOptions) {
|
|
|
19
19
|
)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/** Read caller-visible inventory together with the exact local identity used for the Admin call. */
|
|
23
|
+
export function listOwnedInstancesWithIdentity(options: AdminConnectionOptions) {
|
|
24
|
+
return withAdminClientSession(options, async (context) =>
|
|
25
|
+
Object.freeze({
|
|
26
|
+
instances: await (await connectAdminInstances(context)).list(),
|
|
27
|
+
...(context.identity === undefined ? {} : { identity: context.identity }),
|
|
28
|
+
}),
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
/** Reuse an already-open Admin session for a caller-visible Instance inventory. */
|
|
23
33
|
export async function listOwnedInstancesInContext(context: ConnectionContext) {
|
|
24
34
|
return (await connectAdminInstances(context)).list()
|
|
25
35
|
}
|
|
26
36
|
|
|
27
|
-
/**
|
|
28
|
-
export function createOwnedInstance(
|
|
29
|
-
options: AdminConnectionOptions,
|
|
30
|
-
slug: string,
|
|
31
|
-
hostId?: string,
|
|
32
|
-
) {
|
|
37
|
+
/** Request one managed Instance; Admin owns infrastructure placement. */
|
|
38
|
+
export function createOwnedInstance(options: AdminConnectionOptions, slug: string) {
|
|
33
39
|
return withAdminClientSession(options, async (context) =>
|
|
34
|
-
(await connectAdminInstances(context)).create(slug
|
|
40
|
+
(await connectAdminInstances(context)).create(slug),
|
|
35
41
|
)
|
|
36
42
|
}
|
|
37
43
|
|
package/src/lib/admin-target.ts
CHANGED
|
@@ -5,11 +5,30 @@ import type { InstanceStore } from './instance'
|
|
|
5
5
|
|
|
6
6
|
import { AstraleError } from '../errors'
|
|
7
7
|
import { readInstances, resolveInstanceKey } from './instance'
|
|
8
|
+
import { DEFAULT_UPDATE_CHANNEL } from './update'
|
|
8
9
|
import { isHttpUrl } from './validation'
|
|
9
10
|
|
|
10
11
|
export const DEFAULT_ADMIN_TARGET_NAME = 'admin'
|
|
11
|
-
|
|
12
|
-
export
|
|
12
|
+
|
|
13
|
+
export function defaultAdminTargetForChannel(channel: string): {
|
|
14
|
+
readonly url: string
|
|
15
|
+
readonly domainIssuer: string
|
|
16
|
+
} {
|
|
17
|
+
return channel === 'stable'
|
|
18
|
+
? {
|
|
19
|
+
url: 'https://admin.eu.astrale.ai/api',
|
|
20
|
+
domainIssuer: 'https://admin.astrale.ai',
|
|
21
|
+
}
|
|
22
|
+
: {
|
|
23
|
+
url: 'https://admin.eu.beta.astrale.ai/api',
|
|
24
|
+
domainIssuer: 'https://admin.beta.astrale.ai',
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const DEFAULT_ADMIN_TARGET = defaultAdminTargetForChannel(DEFAULT_UPDATE_CHANNEL)
|
|
29
|
+
|
|
30
|
+
export const DEFAULT_ADMIN_TARGET_URL = DEFAULT_ADMIN_TARGET.url
|
|
31
|
+
export const DEFAULT_ADMIN_DOMAIN_ISSUER = DEFAULT_ADMIN_TARGET.domainIssuer
|
|
13
32
|
|
|
14
33
|
export const DEFAULT_ADMIN_TARGET_CONFIG = {
|
|
15
34
|
name: DEFAULT_ADMIN_TARGET_NAME,
|
package/src/lib/binary.ts
CHANGED
|
@@ -7,14 +7,31 @@ import { output } from './output'
|
|
|
7
7
|
|
|
8
8
|
/** A binary kernel result: bytes (or a stream of them) plus its content-type. */
|
|
9
9
|
export type BinaryLike = {
|
|
10
|
-
readonly body: Uint8Array
|
|
10
|
+
readonly body: Uint8Array | AsyncIterable<Uint8Array>
|
|
11
11
|
readonly mediaType: string
|
|
12
|
+
readonly status?: number
|
|
12
13
|
readonly headers?: Readonly<Record<string, string>>
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
/** Collapse a (possibly streamed) binary body into a single byte array. */
|
|
16
|
-
export async function readBinaryBody(
|
|
17
|
-
|
|
17
|
+
export async function readBinaryBody(
|
|
18
|
+
body: Uint8Array | AsyncIterable<Uint8Array>,
|
|
19
|
+
): Promise<Uint8Array> {
|
|
20
|
+
if (body instanceof Uint8Array) return new Uint8Array(body)
|
|
21
|
+
const chunks: Uint8Array[] = []
|
|
22
|
+
let size = 0
|
|
23
|
+
for await (const chunk of body) {
|
|
24
|
+
const detached = new Uint8Array(chunk)
|
|
25
|
+
chunks.push(detached)
|
|
26
|
+
size += detached.byteLength
|
|
27
|
+
}
|
|
28
|
+
const bytes = new Uint8Array(size)
|
|
29
|
+
let offset = 0
|
|
30
|
+
for (const chunk of chunks) {
|
|
31
|
+
bytes.set(chunk, offset)
|
|
32
|
+
offset += chunk.byteLength
|
|
33
|
+
}
|
|
34
|
+
return bytes
|
|
18
35
|
}
|
|
19
36
|
|
|
20
37
|
function isTextLike(contentType: string): boolean {
|
|
@@ -43,13 +60,13 @@ function humanSize(bytes: number): string {
|
|
|
43
60
|
function jsonEnvelope(resp: BinaryLike, bytes: Uint8Array): Record<string, unknown> {
|
|
44
61
|
if (isTextLike(resp.mediaType)) {
|
|
45
62
|
return {
|
|
46
|
-
status: 200,
|
|
63
|
+
status: resp.status ?? 200,
|
|
47
64
|
contentType: resp.mediaType,
|
|
48
65
|
body: new TextDecoder().decode(bytes),
|
|
49
66
|
}
|
|
50
67
|
}
|
|
51
68
|
return {
|
|
52
|
-
status: 200,
|
|
69
|
+
status: resp.status ?? 200,
|
|
53
70
|
contentType: resp.mediaType,
|
|
54
71
|
bodyBase64: Buffer.from(bytes).toString('base64'),
|
|
55
72
|
}
|
package/src/lib/command-dx.ts
CHANGED
|
@@ -2,8 +2,6 @@ import type { Command, CommanderError } from 'commander'
|
|
|
2
2
|
|
|
3
3
|
import chalk from 'chalk'
|
|
4
4
|
|
|
5
|
-
import { isMachine } from './output'
|
|
6
|
-
|
|
7
5
|
export type CommandCatalogEntry = {
|
|
8
6
|
path: string[]
|
|
9
7
|
usage: string
|
|
@@ -30,27 +28,27 @@ export function renderCommanderError(
|
|
|
30
28
|
program: Command,
|
|
31
29
|
error: CommanderError,
|
|
32
30
|
argv = process.argv.slice(2),
|
|
31
|
+
machine = argv.some((token) => token === '--ci' || token === '--json' || token === '--raw') ||
|
|
32
|
+
!(process.stdout.isTTY ?? false),
|
|
33
33
|
): string {
|
|
34
34
|
const tokens = stripOptions(argv)
|
|
35
35
|
const catalog = collectCommandCatalog(program)
|
|
36
36
|
const matched = matchRegisteredPrefix(program, tokens)
|
|
37
37
|
|
|
38
38
|
if (matched.path.length === 0 && tokens.length > 0) {
|
|
39
|
-
return maybeMachine(renderUnknownCommand(tokens, catalog))
|
|
39
|
+
return maybeMachine(renderUnknownCommand(tokens, catalog), machine)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
if (error.code === 'commander.missingArgument') {
|
|
43
43
|
const usage = usageFor(matched.path, matched.command)
|
|
44
|
-
const argName = error.message.match(/'([^']+)'/)?.[1]
|
|
45
44
|
return maybeMachine(
|
|
46
45
|
[
|
|
47
|
-
`Missing required argument
|
|
48
|
-
`astrale ${matched.path.join(' ')}`,
|
|
49
|
-
)}`,
|
|
46
|
+
`Missing required argument for ${chalk.bold(`astrale ${matched.path.join(' ')}`)}`,
|
|
50
47
|
'',
|
|
51
48
|
'Usage:',
|
|
52
49
|
` astrale ${usage}`,
|
|
53
50
|
].join('\n'),
|
|
51
|
+
machine,
|
|
54
52
|
)
|
|
55
53
|
}
|
|
56
54
|
|
|
@@ -66,6 +64,7 @@ export function renderCommanderError(
|
|
|
66
64
|
'Usage:',
|
|
67
65
|
` astrale ${usage}`,
|
|
68
66
|
].join('\n'),
|
|
67
|
+
machine,
|
|
69
68
|
)
|
|
70
69
|
}
|
|
71
70
|
|
|
@@ -77,6 +76,7 @@ export function renderCommanderError(
|
|
|
77
76
|
? ['', 'Did you mean:', ...suggestions.map((s) => ` astrale ${s}`)]
|
|
78
77
|
: []),
|
|
79
78
|
].join('\n'),
|
|
79
|
+
machine,
|
|
80
80
|
)
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -143,8 +143,8 @@ function usageFor(path: string[], command: Command): string {
|
|
|
143
143
|
|
|
144
144
|
const ANSI_RE = new RegExp(String.fromCharCode(27) + '\\[[0-9;]*m', 'g')
|
|
145
145
|
|
|
146
|
-
function maybeMachine(text: string): string {
|
|
147
|
-
if (!
|
|
146
|
+
function maybeMachine(text: string, machine: boolean): string {
|
|
147
|
+
if (!machine) return text
|
|
148
148
|
const plain = text.replace(ANSI_RE, '')
|
|
149
149
|
const first = plain.split('\n').find((line) => line.trim().length > 0) ?? plain
|
|
150
150
|
return JSON.stringify({ error: 'USAGE_ERROR', message: first, detail: plain })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { publication } from '@astrale-os/
|
|
1
|
+
import { publication } from '@astrale-os/sdk/publication'
|
|
2
2
|
|
|
3
3
|
const MAXIMUM_PUBLICATION_BYTES = 1024 * 1024
|
|
4
4
|
type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>
|
|
@@ -9,8 +9,7 @@ export async function fetchDomainPublication(
|
|
|
9
9
|
signal?: AbortSignal,
|
|
10
10
|
fetchImpl: FetchLike = globalThis.fetch,
|
|
11
11
|
): Promise<publication.Publication> {
|
|
12
|
-
const
|
|
13
|
-
const url = new URL(publication.PATH, root)
|
|
12
|
+
const url = domainPublicationUrl(input)
|
|
14
13
|
const response = await fetchImpl(url, {
|
|
15
14
|
redirect: 'error',
|
|
16
15
|
...(signal === undefined ? {} : { signal }),
|
|
@@ -81,7 +80,8 @@ async function cancel(body: ReadableStream<Uint8Array> | null): Promise<void> {
|
|
|
81
80
|
if (body !== null) await body.cancel().catch(() => undefined)
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
|
|
83
|
+
/** Canonical discovery document for a Domain origin or exact Publication URL. */
|
|
84
|
+
export function domainPublicationUrl(input: string): URL {
|
|
85
85
|
let url: URL
|
|
86
86
|
try {
|
|
87
87
|
url = new URL(input)
|
|
@@ -94,9 +94,11 @@ function deploymentRoot(input: string): URL {
|
|
|
94
94
|
url.password !== '' ||
|
|
95
95
|
url.search !== '' ||
|
|
96
96
|
url.hash !== '' ||
|
|
97
|
-
!['', '/'].includes(url.pathname)
|
|
97
|
+
!['', '/', publication.PATH].includes(url.pathname)
|
|
98
98
|
) {
|
|
99
|
-
throw new TypeError(
|
|
99
|
+
throw new TypeError(
|
|
100
|
+
`Domain deployment URL must be an HTTP(S) origin or canonical Publication URL: ${input}`,
|
|
101
|
+
)
|
|
100
102
|
}
|
|
101
|
-
return new URL(url.origin)
|
|
103
|
+
return new URL(publication.PATH, url.origin)
|
|
102
104
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
|
|
3
|
+
const MAXIMUM_DEBUG_FAILURES = 32
|
|
4
|
+
|
|
5
|
+
export function printFailureDebug(error: unknown, url: string): void {
|
|
6
|
+
process.stderr.write(`\n${chalk.dim('── debug ─────────────────')}\n`)
|
|
7
|
+
if (url) process.stderr.write(`${chalk.dim(`url: ${url}`)}\n`)
|
|
8
|
+
const seen = new Set<unknown>()
|
|
9
|
+
const pending: Array<{ value: unknown; relation: string }> = [{ value: error, relation: 'error' }]
|
|
10
|
+
while (pending.length > 0 && seen.size < MAXIMUM_DEBUG_FAILURES) {
|
|
11
|
+
const { value, relation } = pending.shift()!
|
|
12
|
+
if (seen.has(value)) continue
|
|
13
|
+
seen.add(value)
|
|
14
|
+
if (!(value instanceof Error)) {
|
|
15
|
+
process.stderr.write(`${chalk.dim(`${relation}: ${String(value)}`)}\n`)
|
|
16
|
+
continue
|
|
17
|
+
}
|
|
18
|
+
process.stderr.write(
|
|
19
|
+
`${chalk.dim(`${relation}: ${value.constructor.name}: ${value.message}`)}\n`,
|
|
20
|
+
)
|
|
21
|
+
if (value.stack) process.stderr.write(`${chalk.dim(value.stack)}\n`)
|
|
22
|
+
if (value.cause !== undefined) pending.push({ value: value.cause, relation: 'caused by' })
|
|
23
|
+
if (value instanceof AggregateError)
|
|
24
|
+
for (const child of value.errors) pending.push({ value: child, relation: 'aggregate' })
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/lib/idp.ts
CHANGED
|
@@ -214,6 +214,8 @@ export type IdpConfig = {
|
|
|
214
214
|
|
|
215
215
|
export const BUILTIN_WORKOS_IDP_NAME = 'workos'
|
|
216
216
|
const DEFAULT_WORKOS_API_HOST = 'https://api.workos.com'
|
|
217
|
+
const DEFAULT_WORKOS_CLIENT_ID = 'client_01KC29HET5F3QAQ8GNTPZ7F320'
|
|
218
|
+
const LEGACY_WORKOS_CLIENT_ID = 'client_01KC29HEGD7B40TV2C4QZ436BG'
|
|
217
219
|
const WORKOS_CLIENT_ID_ENV_NAMES = ['WORKOS_CLIENT_ID', 'VITE_WORKOS_CLIENT_ID'] as const
|
|
218
220
|
|
|
219
221
|
export function idpDir(name: string): string {
|
|
@@ -280,7 +282,17 @@ export async function readIdpConfigOrBuiltin(
|
|
|
280
282
|
validateName(name, 'IdP')
|
|
281
283
|
const store = await readIdpStore()
|
|
282
284
|
if (store.idps[name]) {
|
|
283
|
-
|
|
285
|
+
const existing = await readIdpConfig(name)
|
|
286
|
+
const replacement = opts.persist
|
|
287
|
+
? legacyBuiltinWorkosReplacement(existing, opts.clientId)
|
|
288
|
+
: undefined
|
|
289
|
+
if (!replacement) return existing
|
|
290
|
+
return upsertIdpConfig({
|
|
291
|
+
name: replacement.name,
|
|
292
|
+
metadata: replacement.metadata,
|
|
293
|
+
client: replacement.client,
|
|
294
|
+
builtIn: true,
|
|
295
|
+
})
|
|
284
296
|
}
|
|
285
297
|
|
|
286
298
|
const builtin = builtinIdpConfig(name, opts.clientId)
|
|
@@ -413,11 +425,30 @@ export function builtinIdpConfig(
|
|
|
413
425
|
}
|
|
414
426
|
|
|
415
427
|
export function workosClientIdFromEnv(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
416
|
-
return
|
|
417
|
-
(
|
|
428
|
+
return (
|
|
429
|
+
WORKOS_CLIENT_ID_ENV_NAMES.map((name) => env[name]?.trim()).find(
|
|
430
|
+
(value): value is string => typeof value === 'string' && value.length > 0,
|
|
431
|
+
) ?? DEFAULT_WORKOS_CLIENT_ID
|
|
418
432
|
)
|
|
419
433
|
}
|
|
420
434
|
|
|
435
|
+
export function legacyBuiltinWorkosReplacement(
|
|
436
|
+
existing: IdpConfig,
|
|
437
|
+
clientIdOverride?: string,
|
|
438
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
439
|
+
): IdpConfig | undefined {
|
|
440
|
+
if (
|
|
441
|
+
existing.name !== BUILTIN_WORKOS_IDP_NAME ||
|
|
442
|
+
!existing.entry.builtIn ||
|
|
443
|
+
existing.client.client_id !== LEGACY_WORKOS_CLIENT_ID
|
|
444
|
+
) {
|
|
445
|
+
return undefined
|
|
446
|
+
}
|
|
447
|
+
const replacement = builtinIdpConfig(existing.name, clientIdOverride, env)
|
|
448
|
+
if (!replacement || replacement.client.client_id === LEGACY_WORKOS_CLIENT_ID) return undefined
|
|
449
|
+
return replacement
|
|
450
|
+
}
|
|
451
|
+
|
|
421
452
|
async function fetchOAuthAuthorizationServerMetadata(
|
|
422
453
|
issuer: string,
|
|
423
454
|
): Promise<Partial<OidcMetadata> | undefined> {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { InstanceEntry, InstanceStore } from './instance'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { formatInstanceState, type OwnedInstanceInfo } from './admin-instance'
|
|
4
4
|
import { normalizeInstanceKernelUrl, resolveInstanceKey } from './instance'
|
|
5
5
|
|
|
6
6
|
export type InstanceCandidate =
|
|
@@ -44,6 +44,6 @@ export function describeInstanceCandidate(candidate: InstanceCandidate): string
|
|
|
44
44
|
if (candidate.source === 'bookmark') {
|
|
45
45
|
return `${candidate.key} (bookmark) ${candidate.url}`
|
|
46
46
|
}
|
|
47
|
-
const extra =
|
|
47
|
+
const extra = formatInstanceState(candidate.info)
|
|
48
48
|
return `${candidate.key} (managed) ${candidate.url}${extra ? ` — ${extra}` : ''}`
|
|
49
49
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { InstanceInfo } from '../admin/instance/model'
|
|
2
1
|
import type { AstraleConfig } from './config'
|
|
3
2
|
|
|
3
|
+
import { AdminInstanceNotFoundError, type InstanceInfo } from '../admin/instance/model'
|
|
4
4
|
import { AstraleError } from '../errors'
|
|
5
5
|
import {
|
|
6
6
|
DEFAULT_ADMIN_TARGET_NAME,
|
|
@@ -190,19 +190,8 @@ export function isAdminDiscoveryFailure(error: unknown): boolean {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
export function isManagedInstanceNotFound(error: unknown): boolean {
|
|
193
|
-
if (error instanceof AstraleError && error.code === 'INSTANCE_NOT_FOUND') return true
|
|
194
|
-
if (!(error instanceof Error)) return false
|
|
195
|
-
if (error.name === 'NotFoundError') return true
|
|
196
|
-
// The admin kernel reports a missing instance node as InternalKernelError
|
|
197
|
-
// with a NOT_FOUND-prefixed message. In this lookup that's an instance
|
|
198
|
-
// miss (config problem), not a kernel fault — map it so callers get the
|
|
199
|
-
// typed INSTANCE_NOT_FOUND with remediation instead of a raw kernel error.
|
|
200
|
-
if (error.name === 'InternalKernelError' && /^NOT_FOUND\b/.test(error.message)) return true
|
|
201
|
-
const data = (error as Error & { data?: unknown }).data
|
|
202
193
|
return (
|
|
203
|
-
error
|
|
204
|
-
|
|
205
|
-
typeof data === 'object' &&
|
|
206
|
-
(data as { type?: unknown }).type === 'NotFoundError'
|
|
194
|
+
error instanceof AdminInstanceNotFoundError ||
|
|
195
|
+
(error instanceof AstraleError && error.code === 'INSTANCE_NOT_FOUND')
|
|
207
196
|
)
|
|
208
197
|
}
|
package/src/lib/instance.ts
CHANGED
|
@@ -297,22 +297,26 @@ export async function upsertInstance(
|
|
|
297
297
|
* bookmark was repointed to a different kernel so callers can warn.
|
|
298
298
|
*/
|
|
299
299
|
export async function upsertManagedBookmark(
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
300
|
+
input: Readonly<{
|
|
301
|
+
key: string
|
|
302
|
+
slug: string
|
|
303
|
+
url: string
|
|
304
|
+
organizationId?: string
|
|
305
|
+
defaultIdentity?: string
|
|
306
|
+
}>,
|
|
304
307
|
): Promise<{ entry: InstanceEntry; repointedFrom?: string }> {
|
|
305
308
|
const store = await readInstances()
|
|
306
|
-
const previousUrl = store.instances[key]?.url
|
|
307
|
-
const url = normalizeInstanceKernelUrl(
|
|
308
|
-
const { entry } = await upsertInstance(key, {
|
|
309
|
+
const previousUrl = store.instances[input.key]?.url
|
|
310
|
+
const url = normalizeInstanceKernelUrl(input.url)
|
|
311
|
+
const { entry } = await upsertInstance(input.key, {
|
|
309
312
|
url,
|
|
310
313
|
issuer: url,
|
|
311
|
-
slug,
|
|
312
|
-
name: slug,
|
|
314
|
+
slug: input.slug,
|
|
315
|
+
name: input.slug,
|
|
313
316
|
kind: 'bookmark',
|
|
314
317
|
mode: 'remote',
|
|
315
|
-
...(organizationId ? { organizationId } : {}),
|
|
318
|
+
...(input.organizationId ? { organizationId: input.organizationId } : {}),
|
|
319
|
+
...(input.defaultIdentity ? { defaultIdentity: input.defaultIdentity } : {}),
|
|
316
320
|
})
|
|
317
321
|
return {
|
|
318
322
|
entry,
|
|
@@ -362,8 +366,9 @@ export async function getActive(config?: AstraleConfig): Promise<InstanceEntry &
|
|
|
362
366
|
export async function resolveInstance(
|
|
363
367
|
identifier: string,
|
|
364
368
|
config?: AstraleConfig,
|
|
369
|
+
opts: { persist?: boolean } = {},
|
|
365
370
|
): Promise<ResolvedInstance> {
|
|
366
|
-
const store = await readInstances(config)
|
|
371
|
+
const store = await readInstances(config, opts)
|
|
367
372
|
const key = resolveInstanceKey(store, identifier)
|
|
368
373
|
const entry = key ? store.instances[key] : undefined
|
|
369
374
|
if (!key || !entry?.url) {
|
package/src/lib/log.ts
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from 'chalk'
|
|
|
2
2
|
import ora, { type Ora } from 'ora'
|
|
3
3
|
|
|
4
4
|
import { AstraleError, NotImplementedError } from '../errors'
|
|
5
|
+
import { printFailureDebug } from './failure-debug'
|
|
5
6
|
import { formatElapsed } from './format'
|
|
6
7
|
import { isMachine, type MachineOpts } from './output'
|
|
7
8
|
|
|
@@ -18,28 +19,34 @@ export const log = {
|
|
|
18
19
|
|
|
19
20
|
/** Report an error with hint (when present) and exit. `--json` / `--ci` / a
|
|
20
21
|
* non-TTY stdout always get one structured JSON line on stderr. */
|
|
21
|
-
export function fatal(e: unknown, opts?: MachineOpts): never {
|
|
22
|
+
export function fatal(e: unknown, opts?: MachineOpts & { readonly debug?: boolean }): never {
|
|
22
23
|
// Ctrl-C at an interactive (@inquirer/prompts) prompt — exit quietly with the
|
|
23
24
|
// SIGINT convention, not a red error line.
|
|
24
25
|
if (e instanceof Error && e.name === 'ExitPromptError') process.exit(130)
|
|
25
|
-
const msg =
|
|
26
|
-
|
|
26
|
+
const msg =
|
|
27
|
+
e instanceof AstraleError ? e.message : 'The CLI encountered an unexpected internal failure.'
|
|
28
|
+
const code = e instanceof AstraleError ? e.code : 'UNEXPECTED_ERROR'
|
|
27
29
|
if (isMachine(opts)) {
|
|
28
30
|
const payload: Record<string, unknown> = { error: code, message: msg }
|
|
29
31
|
if (e instanceof AstraleError && e.hint) payload.hint = e.hint
|
|
30
32
|
process.stderr.write(JSON.stringify(payload) + '\n')
|
|
31
|
-
|
|
33
|
+
} else {
|
|
34
|
+
log.error(`${code}: ${msg}`)
|
|
35
|
+
if (e instanceof AstraleError && e.hint) log.dim(` hint: ${e.hint}`)
|
|
32
36
|
}
|
|
33
|
-
|
|
34
|
-
if (e instanceof AstraleError && e.hint) log.dim(` hint: ${e.hint}`)
|
|
37
|
+
if (opts?.debug) printFailureDebug(e, '')
|
|
35
38
|
process.exit(1)
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
/**
|
|
39
|
-
export function
|
|
41
|
+
/** Project only a caller-authored admission failure into the CLI input family. */
|
|
42
|
+
export function failInput(error: unknown, opts?: MachineOpts): never {
|
|
40
43
|
if (error instanceof AstraleError) fatal(error, opts)
|
|
44
|
+
if (!(error instanceof TypeError)) fatal(error, opts)
|
|
41
45
|
fatal(
|
|
42
|
-
new AstraleError(
|
|
46
|
+
new AstraleError(
|
|
47
|
+
'INVALID_INPUT',
|
|
48
|
+
error.message.trim() ? error.message : 'CLI input is invalid.',
|
|
49
|
+
),
|
|
43
50
|
opts,
|
|
44
51
|
)
|
|
45
52
|
}
|
package/src/lib/output.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type RawOutputOpts = Pick<OutputOpts, 'raw' | 'json'>
|
|
|
17
17
|
export type MachineOpts = RawOutputOpts & { readonly ci?: boolean }
|
|
18
18
|
|
|
19
19
|
export const RAW_OUTPUT_OPTIONS = [
|
|
20
|
-
{ flags: '--json', description: '
|
|
20
|
+
{ flags: '--json', description: 'JSON; streaming commands emit one JSON value per line' },
|
|
21
21
|
{ flags: '--raw', description: 'Unwrapped: bare scalar / raw bytes / JSON for objects' },
|
|
22
22
|
] as const
|
|
23
23
|
|
package/src/lib/proc.ts
CHANGED
|
@@ -22,7 +22,11 @@ export function run(
|
|
|
22
22
|
opts: { cwd?: string } = {},
|
|
23
23
|
): Promise<RunResult> {
|
|
24
24
|
return new Promise((resolve, reject) => {
|
|
25
|
-
const child = spawn(file, args, {
|
|
25
|
+
const child = spawn(file, args, {
|
|
26
|
+
cwd: opts.cwd,
|
|
27
|
+
shell: false,
|
|
28
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
29
|
+
})
|
|
26
30
|
let stdout = ''
|
|
27
31
|
let stderr = ''
|
|
28
32
|
child.stdout?.setEncoding('utf8')
|
|
@@ -50,7 +54,7 @@ export function runInherit(
|
|
|
50
54
|
opts: { cwd?: string } = {},
|
|
51
55
|
): Promise<number> {
|
|
52
56
|
return new Promise((resolve, reject) => {
|
|
53
|
-
const child = spawn(file, args, { cwd: opts.cwd, stdio: 'inherit' })
|
|
57
|
+
const child = spawn(file, args, { cwd: opts.cwd, shell: false, stdio: 'inherit' })
|
|
54
58
|
child.on('error', reject)
|
|
55
59
|
child.on('close', (code) => resolve(code ?? -1))
|
|
56
60
|
})
|
|
@@ -73,6 +77,7 @@ export function spawnHandle(
|
|
|
73
77
|
return spawn(file, args, {
|
|
74
78
|
cwd: opts.cwd,
|
|
75
79
|
env: opts.env,
|
|
80
|
+
shell: false,
|
|
76
81
|
// `detached` makes the child its own process-group leader so the caller can
|
|
77
82
|
// tear down the whole tree (the child AND its grandchildren) with a single
|
|
78
83
|
// group signal — `process.kill(-child.pid, …)`.
|