@astrale-os/cli 0.8.1-alpha.0 → 0.8.1-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/astrale.js +63133 -41267
- package/dist/public/connect-core.js +55030 -0
- package/dist/public/keys/index.js +45631 -0
- package/dist/public/paths/index.js +43990 -0
- package/dist/types/admin/instance/model.d.ts +40 -0
- package/dist/types/connect-core.d.ts +22 -0
- package/dist/types/connection/auth.d.ts +33 -0
- package/dist/types/errors.d.ts +23 -0
- package/dist/types/identity/registry.d.ts +25 -0
- package/dist/types/keys/algorithm.d.ts +6 -0
- package/dist/types/keys/credential.d.ts +21 -0
- package/dist/types/keys/index.d.ts +4 -0
- package/dist/types/keys/pair.d.ts +34 -0
- package/dist/types/lib/admin-target.d.ts +74 -0
- package/dist/types/lib/ca-fetch.d.ts +2 -0
- package/dist/types/lib/config.d.ts +71 -0
- package/dist/types/lib/format.d.ts +1 -0
- package/dist/types/lib/idp-session.d.ts +33 -0
- package/dist/types/lib/idp.d.ts +397 -0
- package/dist/types/lib/instance-target.d.ts +36 -0
- package/dist/types/lib/instance.d.ts +186 -0
- package/dist/types/lib/log.d.ts +30 -0
- package/dist/types/lib/login-flow.d.ts +49 -0
- package/dist/types/lib/meta.d.ts +36 -0
- package/dist/types/lib/output.d.ts +83 -0
- package/dist/types/lib/table.d.ts +18 -0
- package/dist/types/lib/validation.d.ts +11 -0
- package/dist/types/paths/index.d.ts +2 -0
- package/dist/types/state/exchange-credentials.d.ts +25 -0
- package/dist/types/state/files.d.ts +9 -0
- package/dist/types/state/identities.d.ts +42 -0
- package/dist/types/state/index.d.ts +8 -0
- package/dist/types/state/paths.d.ts +32 -0
- package/package.json +22 -7
- package/src/admin/.spec/api.d.ts +3 -0
- package/src/admin/.spec/architecture.md +15 -0
- package/src/admin/.spec/icon.svg +3 -0
- package/src/admin/.spec/layout.ts +6 -0
- package/src/admin/catalog/.spec/api.d.ts +65 -0
- package/src/admin/catalog/.spec/architecture.md +6 -0
- package/src/admin/catalog/.spec/icon.svg +3 -0
- package/src/admin/catalog/.spec/layout.ts +6 -0
- package/src/admin/catalog/__tests__/client.test.ts +171 -0
- package/src/admin/catalog/client.ts +214 -0
- package/src/admin/catalog/index.ts +13 -0
- package/src/admin/catalog/model.ts +42 -0
- package/src/admin/catalog/tsconfig.json +8 -0
- package/src/admin/graph/.spec/api.d.ts +28 -0
- package/src/admin/graph/.spec/architecture.md +5 -0
- package/src/admin/graph/.spec/icon.svg +3 -0
- package/src/admin/graph/.spec/layout.ts +3 -0
- package/src/admin/graph/index.ts +6 -0
- package/src/admin/graph/nodes.ts +56 -0
- package/src/admin/index.ts +2 -0
- package/src/admin/instance/.spec/api.d.ts +66 -0
- package/src/admin/instance/.spec/architecture.md +10 -0
- package/src/admin/instance/.spec/icon.svg +3 -0
- package/src/admin/instance/.spec/layout.ts +6 -0
- package/src/admin/instance/__tests__/client.test.ts +289 -0
- package/src/admin/instance/client.ts +298 -0
- package/src/admin/instance/index.ts +16 -0
- package/src/admin/instance/model.ts +65 -0
- package/src/admin/instance/tsconfig.json +8 -0
- package/src/commands/__tests__/admin-instance.test.ts +3 -36
- package/src/commands/__tests__/call-describe.test.ts +67 -0
- package/src/commands/__tests__/call.test.ts +21 -68
- package/src/commands/__tests__/domain-install-owned.test.ts +26 -27
- package/src/commands/__tests__/domain-list.test.ts +2 -6
- package/src/commands/__tests__/install-direct.test.ts +20 -0
- package/src/commands/__tests__/instance-list-admin.test.ts +26 -0
- package/src/commands/__tests__/logs.test.ts +71 -96
- package/src/commands/__tests__/ls.test.ts +21 -17
- package/src/commands/__tests__/read-commands.test.ts +171 -101
- package/src/commands/__tests__/view.test.ts +100 -51
- package/src/commands/admin/status.ts +3 -3
- package/src/commands/admin/use.ts +29 -6
- package/src/commands/auth/login.ts +1 -1
- package/src/commands/auth/logout.ts +4 -2
- package/src/commands/auth/status.ts +2 -2
- package/src/commands/auth/token.ts +2 -2
- package/src/commands/browser.ts +1 -1
- package/src/commands/call-describe.ts +131 -0
- package/src/commands/call.ts +69 -109
- package/src/commands/describe.ts +52 -147
- package/src/commands/domain/install.ts +106 -49
- package/src/commands/domain/list.ts +4 -8
- package/src/commands/domain/publish.ts +9 -31
- package/src/commands/get.ts +33 -66
- package/src/commands/identity/__tests__/register.test.ts +93 -0
- package/src/commands/identity/create.ts +2 -2
- package/src/commands/identity/delete.ts +2 -2
- package/src/commands/identity/export.ts +7 -33
- package/src/commands/identity/import.ts +21 -70
- package/src/commands/identity/list.ts +2 -2
- package/src/commands/identity/register.ts +148 -117
- package/src/commands/identity/sync.ts +2 -2
- package/src/commands/identity/unsync.ts +2 -2
- package/src/commands/identity/use.ts +2 -2
- package/src/commands/identity/whoami.ts +2 -2
- package/src/commands/idp/add.ts +1 -1
- package/src/commands/idp/list.ts +1 -1
- package/src/commands/idp/refresh.ts +1 -1
- package/src/commands/idp/remove.ts +2 -2
- package/src/commands/idp/show.ts +1 -1
- package/src/commands/instance/active.ts +2 -2
- package/src/commands/instance/bookmark.ts +8 -2
- package/src/commands/instance/create.ts +1 -1
- package/src/commands/instance/delete.ts +4 -10
- package/src/commands/instance/forget.ts +1 -1
- package/src/commands/instance/list.ts +39 -6
- package/src/commands/instance/status.ts +5 -7
- package/src/commands/instance/use.ts +3 -3
- package/src/commands/logs.ts +171 -278
- package/src/commands/ls.ts +78 -204
- package/src/commands/mutate.ts +70 -139
- package/src/commands/query.ts +100 -278
- package/src/commands/session/analyze.ts +1 -1
- package/src/commands/session/list.ts +1 -1
- package/src/commands/setup.ts +1 -1
- package/src/commands/status.ts +1 -1
- package/src/commands/studio.ts +1 -1
- package/src/commands/token.ts +26 -21
- package/src/commands/update.ts +1 -1
- package/src/commands/use.ts +2 -3
- package/src/commands/view-serve.ts +1 -1
- package/src/commands/view.ts +67 -109
- package/src/connect-core.test.ts +38 -0
- package/src/connect-core.ts +42 -0
- package/src/connection/.spec/api.d.ts +111 -0
- package/src/connection/.spec/architecture.md +41 -0
- package/src/connection/.spec/capabilities/connection.ts +13 -0
- package/src/connection/.spec/examples/call.ts +7 -0
- package/src/connection/.spec/flows/command.ts +20 -0
- package/src/connection/.spec/flows/session.ts +61 -0
- package/src/connection/.spec/laws/connection.ts +181 -0
- package/src/connection/.spec/layout.ts +19 -0
- package/src/{kernel → connection}/__tests__/auth.test.ts +26 -2
- package/src/connection/__tests__/ca-fetch.test.ts +53 -0
- package/src/connection/__tests__/call.test.ts +17 -0
- package/src/connection/__tests__/credential.test.ts +109 -0
- package/src/connection/__tests__/errors.test.ts +96 -0
- package/src/connection/__tests__/exchange.test.ts +224 -0
- package/src/connection/__tests__/fixtures/localhost-cert.pem +19 -0
- package/src/connection/__tests__/fixtures/localhost-key.base64 +1 -0
- package/src/connection/__tests__/managed.test.ts +39 -0
- package/src/connection/__tests__/self.test.ts +51 -0
- package/src/connection/__tests__/session.test.ts +140 -0
- package/src/connection/__tests__/target.test.ts +93 -0
- package/src/{kernel → connection}/auth.ts +8 -8
- package/src/connection/call.ts +7 -0
- package/src/{kernel/run.ts → connection/command.ts} +22 -13
- package/src/connection/credential.ts +114 -0
- package/src/{kernel → connection}/errors.ts +32 -9
- package/src/connection/exchange.ts +274 -0
- package/src/connection/index.ts +8 -0
- package/src/connection/self.ts +120 -0
- package/src/connection/session.ts +176 -0
- package/src/connection/target.ts +111 -0
- package/src/connection/tsconfig.json +6 -0
- package/src/graph/.spec/api.d.ts +34 -0
- package/src/graph/.spec/laws/documents.ts +24 -0
- package/src/graph/.spec/laws/projection.ts +13 -0
- package/src/graph/.spec/layout.ts +6 -0
- package/src/graph/__tests__/mutation.test.ts +45 -0
- package/src/graph/__tests__/projection.test.ts +22 -0
- package/src/graph/__tests__/query.test.ts +154 -0
- package/src/graph/index.ts +3 -0
- package/src/graph/mutation.ts +33 -0
- package/src/graph/projection.ts +21 -0
- package/src/graph/query.ts +108 -0
- package/src/graph/tsconfig.json +6 -0
- package/src/identity/.history/ard/bootstrap-root-transfer.md +57 -0
- package/src/identity/.history/ard/domain-mediated-registration.md +36 -0
- package/src/identity/.spec/api.d.ts +124 -0
- package/src/identity/.spec/architecture.md +33 -0
- package/src/identity/.spec/capabilities/identity.ts +7 -0
- package/src/identity/.spec/flows/bootstrap-root.ts +79 -0
- package/src/identity/.spec/flows/domain-mediated-registration.ts +51 -0
- package/src/identity/.spec/flows/import.ts +29 -0
- package/src/identity/.spec/laws/identity.ts +56 -0
- package/src/identity/.spec/layout.ts +13 -0
- package/src/identity/.spec/schemas/identity-export-v1.schema.json +25 -0
- package/src/identity/__tests__/fixtures/registry-journey.ts +53 -0
- package/src/identity/__tests__/registration.test.ts +64 -0
- package/src/identity/__tests__/registry.test.ts +70 -0
- package/src/identity/__tests__/transfer.test.ts +198 -0
- package/src/identity/index.ts +23 -0
- package/src/identity/registration.ts +72 -0
- package/src/identity/registry.ts +172 -0
- package/src/identity/transfer.ts +187 -0
- package/src/identity/tsconfig.json +6 -0
- package/src/index.ts +1 -0
- package/src/keys/.spec/api.d.ts +74 -0
- package/src/keys/.spec/architecture.md +13 -0
- package/src/keys/.spec/laws/keys.ts +43 -0
- package/src/keys/.spec/layout.ts +6 -0
- package/src/keys/__tests__/algorithm.test.ts +39 -0
- package/src/keys/__tests__/keys.test.ts +168 -0
- package/src/keys/algorithm.ts +33 -0
- package/src/keys/credential.ts +112 -0
- package/src/keys/index.ts +14 -0
- package/src/keys/pair.ts +219 -0
- package/src/keys/tsconfig.json +6 -0
- package/src/lib/__tests__/admin-target.test.ts +67 -7
- package/src/lib/__tests__/binary.test.ts +2 -3
- package/src/lib/__tests__/command-dx.test.ts +2 -2
- package/src/lib/__tests__/config.test.ts +3 -2
- package/src/lib/__tests__/idp.test.ts +4 -6
- package/src/lib/__tests__/instance-target.test.ts +7 -4
- package/src/lib/__tests__/local-status.test.ts +1 -1
- package/src/lib/__tests__/log.test.ts +31 -0
- package/src/lib/__tests__/meta.test.ts +55 -0
- package/src/lib/__tests__/sdk-deps.test.ts +1 -1
- package/src/lib/__tests__/self.test.ts +8 -230
- package/src/lib/__tests__/use-target.test.ts +1 -1
- package/src/lib/__tests__/view-open-intent.test.ts +78 -104
- package/src/lib/__tests__/view-session.test.ts +99 -0
- package/src/lib/admin-domain.ts +39 -25
- package/src/lib/admin-instance.ts +38 -43
- package/src/lib/admin-target.ts +67 -13
- package/src/lib/binary.ts +13 -34
- package/src/lib/browser.ts +1 -1
- package/src/{kernel → lib}/ca-fetch.ts +35 -27
- package/src/lib/config.ts +1 -1
- package/src/lib/idp-session.ts +1 -1
- package/src/lib/idp.ts +3 -8
- package/src/lib/instance-target.ts +12 -8
- package/src/lib/instance.ts +34 -2
- package/src/lib/local-status.ts +2 -2
- package/src/lib/log.ts +3 -1
- package/src/lib/login-flow.ts +32 -5
- package/src/lib/meta.ts +15 -11
- package/src/lib/provision-instance.ts +4 -12
- package/src/lib/self.ts +5 -147
- package/src/lib/update.ts +1 -1
- package/src/lib/use-target.ts +1 -1
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/open-intent.ts +7 -37
- package/src/lib/view/port-allocation.ts +1 -1
- package/src/lib/view/resolve.ts +52 -45
- package/src/lib/view/server.ts +16 -33
- package/src/lib/view/session.ts +51 -22
- package/src/paths/.spec/api.d.ts +13 -0
- package/src/paths/.spec/architecture.md +5 -0
- package/src/paths/.spec/layout.ts +3 -0
- package/src/paths/index.ts +13 -0
- package/src/program/.spec/api.d.ts +42 -0
- package/src/program/.spec/architecture.md +18 -0
- package/src/program/.spec/laws/program.ts +29 -0
- package/src/program/.spec/layout.ts +14 -0
- package/src/program/__tests__/program.test.ts +359 -0
- package/src/program/build.ts +190 -0
- package/src/program/command.ts +42 -0
- package/src/program/index.ts +2 -0
- package/src/program/options.ts +40 -0
- package/src/{registry.ts → program/registry.ts} +5 -1
- package/src/program/tsconfig.json +6 -0
- package/src/setup/steps/admin.ts +18 -3
- package/src/setup/steps/instance.ts +1 -1
- package/src/state/.spec/api.d.ts +133 -0
- package/src/state/.spec/architecture.md +31 -0
- package/src/state/.spec/capabilities/state.ts +7 -0
- package/src/state/.spec/flows/identity-update.ts +22 -0
- package/src/state/.spec/laws/state.ts +73 -0
- package/src/state/.spec/layout.ts +14 -0
- package/src/state/__tests__/exchange-credentials.test.ts +188 -0
- package/src/state/__tests__/files.test.ts +113 -0
- package/src/state/__tests__/fixtures/identity-transition.ts +37 -0
- package/src/state/__tests__/identities.test.ts +142 -0
- package/src/state/__tests__/paths.test.ts +27 -0
- package/src/state/exchange-credentials.ts +219 -0
- package/src/state/files.ts +129 -0
- package/src/state/identities.ts +236 -0
- package/src/state/index.ts +29 -0
- package/src/state/paths.ts +61 -0
- package/src/state/tsconfig.json +6 -0
- package/src/telemetry/__tests__/redact.test.ts +3 -3
- package/src/telemetry/__tests__/trigger.test.ts +1 -1
- package/src/telemetry/settings.ts +1 -1
- package/src/telemetry/store.ts +1 -1
- package/src/test-utils.ts +1 -1
- package/studio/client/dist/assets/{elk-api-DVw5WDTH.js → elk-api-D0cBetPW.js} +1 -1
- package/studio/client/dist/assets/index-Dspir4w7.js +81 -0
- package/studio/client/dist/assets/{index-Dl709Ra1.js → index-bVD2KJgz.js} +4 -4
- package/studio/client/dist/index.html +1 -1
- package/studio/package.json +10 -1
- package/studio/server/introspect/extractor.ts +1 -1
- package/studio/server/state/views.test.ts +3 -7
- package/studio/server/state/views.ts +23 -30
- package/tsconfig.json +1 -1
- package/viewer/dist/main.js +65 -39
- package/src/command.ts +0 -42
- package/src/commands/__tests__/help-contract.test.ts +0 -164
- package/src/kernel/__tests__/client-owned-lookup.test.ts +0 -51
- package/src/kernel/__tests__/errors.test.ts +0 -43
- package/src/kernel/__tests__/expand.test.ts +0 -123
- package/src/kernel/client.ts +0 -214
- package/src/kernel/expand.ts +0 -192
- package/src/kernel/graph.ts +0 -96
- package/src/kernel/index.ts +0 -29
- package/src/kernel/options.ts +0 -22
- package/src/kernel/types.ts +0 -14
- package/src/lib/__tests__/domain-identity.test.ts +0 -60
- package/src/lib/__tests__/fs-atomic.test.ts +0 -104
- package/src/lib/__tests__/identity.test.ts +0 -79
- package/src/lib/__tests__/keys.test.ts +0 -129
- package/src/lib/domain-identity.ts +0 -49
- package/src/lib/env.ts +0 -49
- package/src/lib/fs-atomic.ts +0 -126
- package/src/lib/identity.ts +0 -256
- package/src/lib/keys.ts +0 -294
- package/src/lib/paths.ts +0 -11
- package/src/program.ts +0 -213
- package/studio/client/dist/assets/index-CTbkDp3z.js +0 -81
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
2
|
+
import { syscalls } from '@astrale-os/sdk/schema/kernel'
|
|
2
3
|
import chalk from 'chalk'
|
|
3
4
|
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
5
|
+
import type { KernelCommandOpts } from '../../connection'
|
|
6
|
+
import type { CommandDefinition } from '../../program/index'
|
|
6
7
|
|
|
8
|
+
import { createPathCall, runKernelCommand, withAdminClientSession } from '../../connection'
|
|
7
9
|
import { AstraleError } from '../../errors'
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
installAdminDomainInContext,
|
|
12
|
+
listAdminDomainsInContext,
|
|
13
|
+
type DomainInfo,
|
|
14
|
+
} from '../../lib/admin-domain'
|
|
15
|
+
import { listOwnedInstancesInContext, type OwnedInstanceInfo } from '../../lib/admin-instance'
|
|
12
16
|
import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
|
|
13
17
|
import { getActive } from '../../lib/instance'
|
|
14
18
|
import { fatal, log, withSpinner } from '../../lib/log'
|
|
@@ -16,18 +20,35 @@ import { isMachine, output } from '../../lib/output'
|
|
|
16
20
|
import { confirmWithInput, promptText, selectFrom } from '../../lib/prompt'
|
|
17
21
|
import { isHttpUrl } from '../../lib/validation'
|
|
18
22
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
/** Public Kernel install syscall input for one remote URL. */
|
|
24
|
+
export function directInstallCallInput(
|
|
25
|
+
url: string,
|
|
26
|
+
token?: string,
|
|
27
|
+
operation: string = crypto.randomUUID(),
|
|
28
|
+
) {
|
|
29
|
+
return Object.freeze({
|
|
30
|
+
operation,
|
|
31
|
+
domains: [
|
|
32
|
+
Object.freeze({
|
|
33
|
+
source: Object.freeze({
|
|
34
|
+
kind: 'remote' as const,
|
|
35
|
+
url,
|
|
36
|
+
...(token === undefined ? {} : { token }),
|
|
37
|
+
}),
|
|
38
|
+
}),
|
|
39
|
+
],
|
|
40
|
+
})
|
|
27
41
|
}
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
type DirectInstallResult = {
|
|
44
|
+
readonly operation: string
|
|
45
|
+
readonly transitions: readonly {
|
|
46
|
+
readonly intent: {
|
|
47
|
+
readonly origin: string
|
|
48
|
+
readonly target?: { readonly schemaRevision?: string } | null
|
|
49
|
+
}
|
|
50
|
+
}[]
|
|
51
|
+
}
|
|
31
52
|
|
|
32
53
|
type InstallOpts = KernelCommandOpts &
|
|
33
54
|
AdminTargetCommandOpts & {
|
|
@@ -50,8 +71,8 @@ Behavior:
|
|
|
50
71
|
interactively. The target instance is the active one, or -i <slug>; it must be
|
|
51
72
|
admin-managed (otherwise the command fails loudly and points you at --direct).
|
|
52
73
|
|
|
53
|
-
--direct: installs a url
|
|
54
|
-
bypassing the admin
|
|
74
|
+
--direct: installs a url through the public Kernel install syscall,
|
|
75
|
+
bypassing the admin catalog. Works on any instance you can authenticate to
|
|
55
76
|
(managed, bookmarked, or local), using your own authority. This is the only
|
|
56
77
|
mode that runs the identity-override consent gate: when the domain's declared
|
|
57
78
|
origin differs from its serving host, it requires explicit consent (an
|
|
@@ -108,7 +129,12 @@ Examples:
|
|
|
108
129
|
* an admin-kernel override). The admin kernel is chosen via --admin/--admin-url
|
|
109
130
|
* or config, exactly like `domain publish`.
|
|
110
131
|
*/
|
|
111
|
-
async function installViaAdmin(
|
|
132
|
+
export async function installViaAdmin(
|
|
133
|
+
target: string | undefined,
|
|
134
|
+
opts: InstallOpts,
|
|
135
|
+
dependencies: Partial<AdminInstallDependencies> = {},
|
|
136
|
+
): Promise<void> {
|
|
137
|
+
const admin = { ...defaultAdminInstallDependencies, ...dependencies }
|
|
112
138
|
const interactive = !!process.stdin.isTTY && !(opts.ci || opts.noPrompt || process.env.CI)
|
|
113
139
|
if (!target && !interactive) {
|
|
114
140
|
fatal(
|
|
@@ -121,13 +147,17 @@ async function installViaAdmin(target: string | undefined, opts: InstallOpts): P
|
|
|
121
147
|
}
|
|
122
148
|
// Strip the install-target selector so the admin client resolves only the
|
|
123
149
|
// admin kernel (via --admin/--admin-url/config), not the instance under `-i`.
|
|
124
|
-
const adminOpts
|
|
150
|
+
const adminOpts = {
|
|
151
|
+
admin: opts.admin,
|
|
152
|
+
adminUrl: opts.adminUrl,
|
|
153
|
+
timeout: opts.timeout,
|
|
154
|
+
as: opts.as,
|
|
155
|
+
creds: opts.creds,
|
|
156
|
+
}
|
|
125
157
|
|
|
126
158
|
try {
|
|
127
|
-
await
|
|
128
|
-
const instances = await
|
|
129
|
-
|
|
130
|
-
const ref = await resolveDomainRef(ctx, target, interactive)
|
|
159
|
+
await withAdminClientSession(adminOpts, async (ctx) => {
|
|
160
|
+
const instances = await admin.listInstances(ctx)
|
|
131
161
|
const slug = await resolveTargetSlug(opts, target, interactive, instances)
|
|
132
162
|
|
|
133
163
|
const match = instances.find((i) => i.slug === slug)
|
|
@@ -141,15 +171,14 @@ async function installViaAdmin(target: string | undefined, opts: InstallOpts): P
|
|
|
141
171
|
}
|
|
142
172
|
assertInstallTargetReady(match)
|
|
143
173
|
|
|
144
|
-
const
|
|
174
|
+
const domains = await admin.listDomains(ctx)
|
|
175
|
+
const domain = await resolveDomain(domains, target, interactive)
|
|
176
|
+
|
|
177
|
+
const label = domain.origin
|
|
145
178
|
const result = await withSpinner(
|
|
146
179
|
`Installing ${label} on ${match.slug}`,
|
|
147
180
|
!isMachine(opts),
|
|
148
|
-
() =>
|
|
149
|
-
ctx.client.call(adminDomainMethod('install'), {
|
|
150
|
-
instanceId: match.slug,
|
|
151
|
-
...ref,
|
|
152
|
-
}) as Promise<DomainInstallResult>,
|
|
181
|
+
() => admin.install(ctx, match, domain),
|
|
153
182
|
{ success: (r) => `Installed ${r.origin} on ${match.slug}` },
|
|
154
183
|
)
|
|
155
184
|
|
|
@@ -193,15 +222,25 @@ export function domainRefFromTarget(target: string): { origin?: string; url?: st
|
|
|
193
222
|
}
|
|
194
223
|
|
|
195
224
|
/** Resolve the domain to install: the positional `target`, or an interactive pick. */
|
|
196
|
-
async function
|
|
197
|
-
|
|
225
|
+
async function resolveDomain(
|
|
226
|
+
catalog: readonly DomainInfo[],
|
|
198
227
|
target: string | undefined,
|
|
199
228
|
interactive: boolean,
|
|
200
|
-
): Promise<
|
|
201
|
-
if (target)
|
|
229
|
+
): Promise<DomainInfo> {
|
|
230
|
+
if (target) {
|
|
231
|
+
const ref = domainRefFromTarget(target)
|
|
232
|
+
const found = catalog.find((domain) =>
|
|
233
|
+
ref.origin === undefined ? domain.url === ref.url : domain.origin === ref.origin,
|
|
234
|
+
)
|
|
235
|
+
if (found !== undefined) return found
|
|
236
|
+
throw new AstraleError(
|
|
237
|
+
'DOMAIN_NOT_FOUND',
|
|
238
|
+
`No published domain matches "${target}".`,
|
|
239
|
+
'Run `astrale domain list` to see the Admin catalog.',
|
|
240
|
+
)
|
|
241
|
+
}
|
|
202
242
|
if (!interactive) throw new AstraleError('MISSING_ARG', 'No domain given.')
|
|
203
243
|
|
|
204
|
-
const catalog = (await ctx.client.call(adminDomainMethod('list'), {})) as DomainInfo[]
|
|
205
244
|
const installable = catalog.filter((d) => d.url)
|
|
206
245
|
if (installable.length === 0) {
|
|
207
246
|
throw new AstraleError(
|
|
@@ -218,9 +257,21 @@ async function resolveDomainRef(
|
|
|
218
257
|
})),
|
|
219
258
|
)
|
|
220
259
|
if (!origin) throw new AstraleError('CANCELLED', 'No domain selected.')
|
|
221
|
-
return
|
|
260
|
+
return catalog.find((domain) => domain.origin === origin)!
|
|
222
261
|
}
|
|
223
262
|
|
|
263
|
+
interface AdminInstallDependencies {
|
|
264
|
+
readonly listInstances: typeof listOwnedInstancesInContext
|
|
265
|
+
readonly listDomains: typeof listAdminDomainsInContext
|
|
266
|
+
readonly install: typeof installAdminDomainInContext
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const defaultAdminInstallDependencies: AdminInstallDependencies = Object.freeze({
|
|
270
|
+
listInstances: listOwnedInstancesInContext,
|
|
271
|
+
listDomains: listAdminDomainsInContext,
|
|
272
|
+
install: installAdminDomainInContext,
|
|
273
|
+
})
|
|
274
|
+
|
|
224
275
|
/**
|
|
225
276
|
* Resolve the target instance slug: `-i`, else the active instance. When run
|
|
226
277
|
* bare (no positional) in a TTY, prompt for it with the active instance
|
|
@@ -271,8 +322,8 @@ async function activeSlug(): Promise<string | undefined> {
|
|
|
271
322
|
// ── Direct path (--direct) ────────────────────────────────────────────────────
|
|
272
323
|
|
|
273
324
|
/**
|
|
274
|
-
* Install a url
|
|
275
|
-
* bypassing the admin
|
|
325
|
+
* Install a url through the public Kernel install syscall,
|
|
326
|
+
* bypassing the admin catalog, with the identity-override
|
|
276
327
|
* consent gate. Works on any instance the caller can authenticate to.
|
|
277
328
|
*/
|
|
278
329
|
async function installDirect(target: string | undefined, opts: InstallOpts): Promise<void> {
|
|
@@ -300,26 +351,32 @@ async function installDirect(target: string | undefined, opts: InstallOpts): Pro
|
|
|
300
351
|
await runKernelCommand<DirectInstallResult>({
|
|
301
352
|
opts,
|
|
302
353
|
label: `Installing domain from ${url}`,
|
|
303
|
-
fn: async (
|
|
304
|
-
(await
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
354
|
+
fn: async ({ session }) =>
|
|
355
|
+
(await session.call(
|
|
356
|
+
createPathCall(
|
|
357
|
+
Path.project(syscalls.install.ref).raw,
|
|
358
|
+
directInstallCallInput(url, opts.token),
|
|
359
|
+
),
|
|
360
|
+
)) as DirectInstallResult,
|
|
308
361
|
format: (result, fmtOpts, isRaw) => {
|
|
309
362
|
if (isRaw) {
|
|
310
363
|
output(result, fmtOpts)
|
|
311
364
|
return
|
|
312
365
|
}
|
|
313
|
-
|
|
314
|
-
|
|
366
|
+
const installed = result.transitions[0]?.intent
|
|
367
|
+
if (installed === undefined) {
|
|
368
|
+
throw new Error('Kernel install returned no committed Domain transition.')
|
|
369
|
+
}
|
|
370
|
+
const revision = installed.target?.schemaRevision ?? result.operation
|
|
371
|
+
log.success(`Domain installed: ${installed.origin}@${revision}`)
|
|
315
372
|
// Belt-and-braces: the kernel-confirmed origin is authoritative. If it
|
|
316
373
|
// aliases the host and the pre-install gate never consented to THAT
|
|
317
374
|
// origin (lying or absent `/meta`), say so loudly after the fact.
|
|
318
|
-
if (isIdentityOverride(
|
|
375
|
+
if (isIdentityOverride(installed.origin, host) && installed.origin !== consentedOrigin) {
|
|
319
376
|
log.warn(
|
|
320
|
-
`Installed origin "${
|
|
377
|
+
`Installed origin "${installed.origin}" differs from the serving host "${host}" ` +
|
|
321
378
|
`and was not confirmed before install (the worker's /meta did not declare it). ` +
|
|
322
|
-
`Every ${
|
|
379
|
+
`Every ${installed.origin}/* call on this instance now routes to ${host}.`,
|
|
323
380
|
)
|
|
324
381
|
}
|
|
325
382
|
},
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
|
|
3
|
-
import type {
|
|
4
|
-
import type { KernelCommandOpts } from '../../kernel'
|
|
3
|
+
import type { KernelCommandOpts } from '../../connection'
|
|
5
4
|
import type { ListProjection, RawOutputOpts } from '../../lib/output'
|
|
5
|
+
import type { CommandDefinition } from '../../program/index'
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import { adminDomainMethod, type DomainInfo } from '../../lib/admin-domain'
|
|
7
|
+
import { listAdminDomains, type DomainInfo } from '../../lib/admin-domain'
|
|
9
8
|
import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
|
|
10
9
|
import { fatal, withSpinner } from '../../lib/log'
|
|
11
10
|
import { isMachine, presentList } from '../../lib/output'
|
|
@@ -100,10 +99,7 @@ Examples:
|
|
|
100
99
|
'Fetching domains',
|
|
101
100
|
!isMachine(opts),
|
|
102
101
|
async (): Promise<DomainRow[]> => {
|
|
103
|
-
const list = await
|
|
104
|
-
opts,
|
|
105
|
-
async (ctx) => (await ctx.client.call(adminDomainMethod('list'), {})) as DomainInfo[],
|
|
106
|
-
)
|
|
102
|
+
const list = await listAdminDomains(opts)
|
|
107
103
|
const filtered = opts.defaultOnly ? list.filter((d) => d.installByDefault) : list
|
|
108
104
|
filtered.sort(byDefaultThenName)
|
|
109
105
|
if (!opts.check) return filtered as DomainRow[]
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
3
|
+
import type { KernelCommandOpts } from '../../connection'
|
|
4
|
+
import type { CommandDefinition } from '../../program/index'
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { adminDomainMethod, type DomainInfo } from '../../lib/admin-domain'
|
|
6
|
+
import { publishAdminDomain } from '../../lib/admin-domain'
|
|
8
7
|
import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
|
|
9
8
|
import { fatal, withSpinner } from '../../lib/log'
|
|
10
9
|
import { isMachine, output } from '../../lib/output'
|
|
@@ -108,33 +107,12 @@ Examples:
|
|
|
108
107
|
`Publishing ${name} → ${publicUrl}`,
|
|
109
108
|
!isMachine(opts),
|
|
110
109
|
() =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const existing = (await ctx.client
|
|
118
|
-
.call(adminDomainMethod('info'), { origin })
|
|
119
|
-
.catch(() => null)) as DomainInfo | null
|
|
120
|
-
if (
|
|
121
|
-
existing &&
|
|
122
|
-
existing.name === name &&
|
|
123
|
-
existing.url === publicUrl &&
|
|
124
|
-
(opts.description === undefined || existing.description === opts.description) &&
|
|
125
|
-
(opts.installByDefault === undefined ||
|
|
126
|
-
(existing.installByDefault ?? false) === opts.installByDefault)
|
|
127
|
-
) {
|
|
128
|
-
return { entry: existing, changed: false as const, isNew: false }
|
|
129
|
-
}
|
|
130
|
-
const entry = (await ctx.client.call(adminDomainMethod('publish'), {
|
|
131
|
-
origin,
|
|
132
|
-
name,
|
|
133
|
-
url: publicUrl,
|
|
134
|
-
...(opts.description ? { description: opts.description } : {}),
|
|
135
|
-
...(opts.installByDefault ? { installByDefault: true } : {}),
|
|
136
|
-
})) as DomainInfo
|
|
137
|
-
return { entry, changed: true as const, isNew: !existing }
|
|
110
|
+
publishAdminDomain(opts, {
|
|
111
|
+
origin,
|
|
112
|
+
name,
|
|
113
|
+
url: publicUrl,
|
|
114
|
+
...(opts.description ? { description: opts.description } : {}),
|
|
115
|
+
...(opts.installByDefault ? { installByDefault: true } : {}),
|
|
138
116
|
}),
|
|
139
117
|
{
|
|
140
118
|
success: ({ entry, changed, isNew }) =>
|
package/src/commands/get.ts
CHANGED
|
@@ -1,85 +1,52 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { KernelCommandOpts } from '../kernel'
|
|
1
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { output } from '../lib/output'
|
|
3
|
+
import type { KernelCommandOpts } from '../connection'
|
|
4
|
+
import type { CommandDefinition } from '../program/index'
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const INTERNAL_KEYS = new Set(['__labels', 'classId'])
|
|
6
|
+
import { expandSelfInPath, runKernelCommand, withSelfHint } from '../connection'
|
|
7
|
+
import { formatKernelError } from '../connection/errors'
|
|
8
|
+
import { isMachine, output } from '../lib/output'
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
if (!pathArg) {
|
|
16
|
-
log.error('Usage: astrale get <path>')
|
|
17
|
-
process.exit(1)
|
|
18
|
-
return
|
|
19
|
-
}
|
|
10
|
+
type GetOpts = KernelCommandOpts
|
|
20
11
|
|
|
12
|
+
/** Read one exact canonical Node. The caller's Path is not fabricated into the Node value. */
|
|
13
|
+
export async function getCommand(target: string, opts: GetOpts): Promise<void> {
|
|
21
14
|
let path: string
|
|
22
15
|
let meta
|
|
23
16
|
try {
|
|
24
|
-
;({ path, meta } = await expandSelfInPath(
|
|
25
|
-
} catch (
|
|
26
|
-
|
|
17
|
+
;({ path, meta } = await expandSelfInPath(target, opts))
|
|
18
|
+
} catch (error) {
|
|
19
|
+
await formatKernelError(error, isMachine(opts), undefined, opts.debug)
|
|
27
20
|
process.exit(1)
|
|
28
|
-
return
|
|
29
21
|
}
|
|
30
22
|
|
|
31
23
|
await runKernelCommand({
|
|
32
24
|
opts,
|
|
33
|
-
label:
|
|
34
|
-
fn:
|
|
35
|
-
|
|
36
|
-
if (node === null) {
|
|
37
|
-
log.error('node "' + path + '" not found or not visible')
|
|
38
|
-
process.exit(1)
|
|
39
|
-
}
|
|
40
|
-
return node
|
|
41
|
-
},
|
|
42
|
-
format: (node, fmtOpts) => {
|
|
43
|
-
output(opts.long ? node : cleanNode(node), fmtOpts)
|
|
44
|
-
},
|
|
25
|
+
label: `Node ${path}`,
|
|
26
|
+
fn: ({ graph }) => withSelfHint(() => graph.getOrThrow(Path.parse(path)), meta),
|
|
27
|
+
format: (node, format) => output(node, format),
|
|
45
28
|
})
|
|
46
29
|
}
|
|
47
30
|
|
|
48
|
-
function cleanNode(data: unknown): unknown {
|
|
49
|
-
if (!data || typeof data !== 'object') return data
|
|
50
|
-
const result: Record<string, unknown> = {}
|
|
51
|
-
for (const [k, v] of Object.entries(data as Record<string, unknown>)) {
|
|
52
|
-
if (INTERNAL_KEYS.has(k)) continue
|
|
53
|
-
result[k] = v
|
|
54
|
-
}
|
|
55
|
-
return result
|
|
56
|
-
}
|
|
57
|
-
|
|
58
31
|
export default {
|
|
59
32
|
name: 'get',
|
|
60
|
-
description: 'Get one node by
|
|
61
|
-
afterHelpText:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
'',
|
|
79
|
-
].join('\n'),
|
|
80
|
-
arguments: [{ name: 'path', description: 'Node path (/domain/Class) or ID (@nodeId)' }],
|
|
81
|
-
options: [{ flags: '-l, --long', description: 'Include internal fields (__labels, classId)' }],
|
|
82
|
-
action: async (path, opts) => {
|
|
83
|
-
await getCommand(path as string, opts as GetOpts)
|
|
33
|
+
description: 'Get one canonical node by Path or ID',
|
|
34
|
+
afterHelpText: `
|
|
35
|
+
Behavior:
|
|
36
|
+
Resolves one exact Kernel V2 Path and prints the canonical Node
|
|
37
|
+
{ id, class, props }. Missing and authorization-masked nodes remain
|
|
38
|
+
intentionally indistinguishable. @self is expanded before dispatch.
|
|
39
|
+
|
|
40
|
+
A Node does not contain a synthetic path, labels, or backend class ID.
|
|
41
|
+
Use astrale query for graph-shaped reads.
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
$ astrale get /:notes.example.dev:class.Note
|
|
45
|
+
$ astrale get @abc123 --json
|
|
46
|
+
$ astrale get @self
|
|
47
|
+
`,
|
|
48
|
+
arguments: [{ name: 'target', description: 'Canonical Node Path or @id' }],
|
|
49
|
+
action: async (target, opts) => {
|
|
50
|
+
await getCommand(target as string, opts as GetOpts)
|
|
84
51
|
},
|
|
85
52
|
} satisfies CommandDefinition
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { issuer, jwk, provision } from '@astrale-os/sdk/auth'
|
|
2
|
+
import { ClassPath } from '@astrale-os/sdk/graph/class'
|
|
3
|
+
import { normalizeProperties } from '@astrale-os/sdk/graph/properties'
|
|
4
|
+
import { expect, mock, test } from 'bun:test'
|
|
5
|
+
import { exportJWK, generateKeyPair, jwtVerify } from 'jose'
|
|
6
|
+
|
|
7
|
+
import { formatIdentityRegistration, prepareIdentityProvision } from '../register'
|
|
8
|
+
|
|
9
|
+
/** @evidence TEST-CLI-IDENTITY-REGISTER-JSON-EXACT */
|
|
10
|
+
test('emits exactly one structured value for machine registration output', () => {
|
|
11
|
+
const writes: string[] = []
|
|
12
|
+
const logs: string[] = []
|
|
13
|
+
const originalWrite = process.stdout.write
|
|
14
|
+
const originalLog = console.log
|
|
15
|
+
process.stdout.write = mock((chunk: string | Uint8Array) => {
|
|
16
|
+
writes.push(typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk))
|
|
17
|
+
return true
|
|
18
|
+
}) as typeof process.stdout.write
|
|
19
|
+
console.log = mock((...values: unknown[]) => logs.push(values.map(String).join(' ')))
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
formatIdentityRegistration(
|
|
23
|
+
{ iss: 'https://identity.example', sub: 'self', nodeId: 'operator-node' },
|
|
24
|
+
{ json: true },
|
|
25
|
+
true,
|
|
26
|
+
)
|
|
27
|
+
} finally {
|
|
28
|
+
process.stdout.write = originalWrite
|
|
29
|
+
console.log = originalLog
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
expect(JSON.parse(writes.join(''))).toEqual({
|
|
33
|
+
iss: 'https://identity.example',
|
|
34
|
+
sub: 'self',
|
|
35
|
+
nodeId: 'operator-node',
|
|
36
|
+
})
|
|
37
|
+
expect(logs).toEqual([])
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('builds one exact Mutation V3 identity birth bound to a self proof', async () => {
|
|
41
|
+
const { privateKey, publicKey } = await generateKeyPair('ES256', { extractable: true })
|
|
42
|
+
const privateJwk = { ...(await exportJWK(privateKey)), alg: 'ES256', kid: 'alice-key' }
|
|
43
|
+
const publicJwk = jwk.acceptPublic({
|
|
44
|
+
...(await exportJWK(publicKey)),
|
|
45
|
+
alg: 'ES256',
|
|
46
|
+
kid: 'alice-key',
|
|
47
|
+
})
|
|
48
|
+
const kernelIssuer = issuer.accept('https://kernel.example')
|
|
49
|
+
const classPath = ClassPath.parse('/:accounts.example:class.User')
|
|
50
|
+
const properties = normalizeProperties({
|
|
51
|
+
'accounts.example:class.User.property.name': 'Alice',
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const prepared = await prepareIdentityProvision({
|
|
55
|
+
name: 'alice',
|
|
56
|
+
classPath,
|
|
57
|
+
properties,
|
|
58
|
+
privateKey: privateJwk,
|
|
59
|
+
publicKey: publicJwk,
|
|
60
|
+
kernelIssuer,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
expect(String(prepared.binding)).toBe('identity')
|
|
64
|
+
expect(prepared.request.idempotencyKey).toBe('identity-register:alice')
|
|
65
|
+
expect(JSON.parse(JSON.stringify(prepared.request.mutation))).toEqual({
|
|
66
|
+
format: 'astrale.graph.mutation',
|
|
67
|
+
version: 'v3',
|
|
68
|
+
preconditions: [],
|
|
69
|
+
operations: [
|
|
70
|
+
{
|
|
71
|
+
op: 'node.create',
|
|
72
|
+
as: 'identity',
|
|
73
|
+
class: '/:accounts.example:class.User',
|
|
74
|
+
props: { 'accounts.example:class.User.property.name': 'Alice' },
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const credentials = prepared.request.identities[prepared.binding]?.credentials
|
|
80
|
+
expect(credentials?.publicKey).toEqual(publicJwk)
|
|
81
|
+
expect(typeof credentials?.proof).toBe('string')
|
|
82
|
+
if (typeof credentials?.proof !== 'string') throw new TypeError('Expected compact JWT proof')
|
|
83
|
+
|
|
84
|
+
const expectedFingerprint = await provision.fingerprint(prepared.request)
|
|
85
|
+
const expectedIssuer = await provision.selfIssuer(kernelIssuer, publicJwk)
|
|
86
|
+
const verified = await jwtVerify(credentials.proof, publicKey, {
|
|
87
|
+
algorithms: ['ES256'],
|
|
88
|
+
issuer: expectedIssuer,
|
|
89
|
+
subject: 'self',
|
|
90
|
+
audience: kernelIssuer,
|
|
91
|
+
})
|
|
92
|
+
expect(verified.payload.provision).toBe(expectedFingerprint)
|
|
93
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { CommandDefinition } from '../../
|
|
1
|
+
import type { CommandDefinition } from '../../program/index'
|
|
2
2
|
|
|
3
|
-
import { createIdentity } from '../../
|
|
3
|
+
import { createIdentity } from '../../identity/index'
|
|
4
4
|
import { fatal, log } from '../../lib/log'
|
|
5
5
|
|
|
6
6
|
export default {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { CommandDefinition } from '../../
|
|
1
|
+
import type { CommandDefinition } from '../../program/index'
|
|
2
2
|
|
|
3
|
-
import { deleteIdentity } from '../../
|
|
3
|
+
import { deleteIdentity } from '../../identity/index'
|
|
4
4
|
import { fatal, log } from '../../lib/log'
|
|
5
5
|
|
|
6
6
|
export default {
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { chmod, readFile, writeFile } from 'node:fs/promises'
|
|
1
|
+
import type { CommandDefinition } from '../../program/index'
|
|
3
2
|
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
import { getIdentity } from '../../lib/identity'
|
|
7
|
-
import { keypairPaths } from '../../lib/keys'
|
|
3
|
+
import { encodeIdentityExport, exportIdentity, writeIdentityExport } from '../../identity/index'
|
|
8
4
|
import { fatal, log } from '../../lib/log'
|
|
9
5
|
import { readPassphrase } from '../../lib/prompt'
|
|
10
6
|
|
|
@@ -25,33 +21,11 @@ export default {
|
|
|
25
21
|
],
|
|
26
22
|
action: async (name: string, path: string, opts: { encrypt?: boolean }) => {
|
|
27
23
|
try {
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
])
|
|
34
|
-
const envelope = {
|
|
35
|
-
version: 1,
|
|
36
|
-
subject: identity.subject,
|
|
37
|
-
mode: identity.mode ?? 'local',
|
|
38
|
-
kid: identity.kid,
|
|
39
|
-
issuer: identity.issuer,
|
|
40
|
-
privateJwk,
|
|
41
|
-
publicJwk,
|
|
42
|
-
}
|
|
43
|
-
const plain = JSON.stringify(envelope, null, 2)
|
|
44
|
-
|
|
45
|
-
if (opts.encrypt) {
|
|
46
|
-
const passphrase = await readPassphrase('Passphrase (min 8 chars): ', { minLength: 8 })
|
|
47
|
-
const enc = await new CompactEncrypt(new TextEncoder().encode(plain))
|
|
48
|
-
.setProtectedHeader({ alg: 'PBES2-HS256+A128KW', enc: 'A256GCM' })
|
|
49
|
-
.encrypt(new TextEncoder().encode(passphrase))
|
|
50
|
-
await writeFile(path, enc)
|
|
51
|
-
} else {
|
|
52
|
-
await writeFile(path, plain)
|
|
53
|
-
}
|
|
54
|
-
await chmod(path, 0o600)
|
|
24
|
+
const envelope = await exportIdentity(name)
|
|
25
|
+
const passphrase = opts.encrypt
|
|
26
|
+
? await readPassphrase('Passphrase (min 8 chars): ', { minLength: 8 })
|
|
27
|
+
: undefined
|
|
28
|
+
await writeIdentityExport(path, await encodeIdentityExport(envelope, passphrase))
|
|
55
29
|
|
|
56
30
|
log.success(`Exported identity "${name}" → ${path}${opts.encrypt ? ' (encrypted)' : ''}`)
|
|
57
31
|
if (!opts.encrypt) {
|