@astrale-os/cli 0.8.0-alpha.0 → 0.8.1-alpha.2
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 +4 -1
- package/dist/astrale.js +62968 -41240
- 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 +30 -8
- 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.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__/logs.test.ts +50 -98
- 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.ts +55 -91
- package/src/commands/describe.ts +52 -147
- package/src/commands/domain/install.ts +81 -50
- package/src/commands/domain/list.ts +4 -8
- package/src/commands/domain/publish.ts +9 -31
- package/src/commands/get.ts +32 -65
- 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 +3 -3
- package/src/commands/instance/status.ts +5 -7
- package/src/commands/instance/use.ts +3 -3
- package/src/commands/logs.ts +152 -279
- 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 +43 -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 +18 -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-D0cBetPW.js +1 -0
- package/studio/client/dist/assets/elk-worker.min-C9JGDOE-.js +6312 -0
- package/studio/client/dist/assets/index-BQnJ5sgd.css +1 -0
- package/studio/client/dist/assets/index-ChOr3yP0.css +1 -0
- package/studio/client/dist/assets/index-Dspir4w7.js +81 -0
- package/studio/client/dist/assets/index-bVD2KJgz.js +8 -0
- package/studio/client/dist/index.html +2 -2
- 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/studio/tsconfig.json +23 -0
- package/tsconfig.json +1 -1
- package/viewer/dist/main.js +65 -39
- package/viewer/tsconfig.json +13 -0
- 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-BaqEuIQJ.js +0 -109
- package/studio/client/dist/assets/index-jRdExahh.css +0 -1
|
@@ -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,12 @@ 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
|
-
type
|
|
21
|
-
name: string
|
|
23
|
+
/** Exact output of the public Kernel install syscall for one requested root. */
|
|
24
|
+
type DirectInstallResult = readonly {
|
|
22
25
|
origin: string
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
error?: string | null
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Mirrors the kernel's `Domain.install` return. */
|
|
30
|
-
type DirectInstallResult = { domainId: string; origin: string }
|
|
26
|
+
revision: string
|
|
27
|
+
etag?: string
|
|
28
|
+
}[]
|
|
31
29
|
|
|
32
30
|
type InstallOpts = KernelCommandOpts &
|
|
33
31
|
AdminTargetCommandOpts & {
|
|
@@ -50,8 +48,8 @@ Behavior:
|
|
|
50
48
|
interactively. The target instance is the active one, or -i <slug>; it must be
|
|
51
49
|
admin-managed (otherwise the command fails loudly and points you at --direct).
|
|
52
50
|
|
|
53
|
-
--direct: installs a url
|
|
54
|
-
bypassing the admin
|
|
51
|
+
--direct: installs a url through the public Kernel install syscall,
|
|
52
|
+
bypassing the admin catalog. Works on any instance you can authenticate to
|
|
55
53
|
(managed, bookmarked, or local), using your own authority. This is the only
|
|
56
54
|
mode that runs the identity-override consent gate: when the domain's declared
|
|
57
55
|
origin differs from its serving host, it requires explicit consent (an
|
|
@@ -108,7 +106,12 @@ Examples:
|
|
|
108
106
|
* an admin-kernel override). The admin kernel is chosen via --admin/--admin-url
|
|
109
107
|
* or config, exactly like `domain publish`.
|
|
110
108
|
*/
|
|
111
|
-
async function installViaAdmin(
|
|
109
|
+
export async function installViaAdmin(
|
|
110
|
+
target: string | undefined,
|
|
111
|
+
opts: InstallOpts,
|
|
112
|
+
dependencies: Partial<AdminInstallDependencies> = {},
|
|
113
|
+
): Promise<void> {
|
|
114
|
+
const admin = { ...defaultAdminInstallDependencies, ...dependencies }
|
|
112
115
|
const interactive = !!process.stdin.isTTY && !(opts.ci || opts.noPrompt || process.env.CI)
|
|
113
116
|
if (!target && !interactive) {
|
|
114
117
|
fatal(
|
|
@@ -121,13 +124,17 @@ async function installViaAdmin(target: string | undefined, opts: InstallOpts): P
|
|
|
121
124
|
}
|
|
122
125
|
// Strip the install-target selector so the admin client resolves only the
|
|
123
126
|
// admin kernel (via --admin/--admin-url/config), not the instance under `-i`.
|
|
124
|
-
const adminOpts
|
|
127
|
+
const adminOpts = {
|
|
128
|
+
admin: opts.admin,
|
|
129
|
+
adminUrl: opts.adminUrl,
|
|
130
|
+
timeout: opts.timeout,
|
|
131
|
+
as: opts.as,
|
|
132
|
+
creds: opts.creds,
|
|
133
|
+
}
|
|
125
134
|
|
|
126
135
|
try {
|
|
127
|
-
await
|
|
128
|
-
const instances = await
|
|
129
|
-
|
|
130
|
-
const ref = await resolveDomainRef(ctx, target, interactive)
|
|
136
|
+
await withAdminClientSession(adminOpts, async (ctx) => {
|
|
137
|
+
const instances = await admin.listInstances(ctx)
|
|
131
138
|
const slug = await resolveTargetSlug(opts, target, interactive, instances)
|
|
132
139
|
|
|
133
140
|
const match = instances.find((i) => i.slug === slug)
|
|
@@ -141,15 +148,14 @@ async function installViaAdmin(target: string | undefined, opts: InstallOpts): P
|
|
|
141
148
|
}
|
|
142
149
|
assertInstallTargetReady(match)
|
|
143
150
|
|
|
144
|
-
const
|
|
151
|
+
const domains = await admin.listDomains(ctx)
|
|
152
|
+
const domain = await resolveDomain(domains, target, interactive)
|
|
153
|
+
|
|
154
|
+
const label = domain.origin
|
|
145
155
|
const result = await withSpinner(
|
|
146
156
|
`Installing ${label} on ${match.slug}`,
|
|
147
157
|
!isMachine(opts),
|
|
148
|
-
() =>
|
|
149
|
-
ctx.client.call(adminDomainMethod('install'), {
|
|
150
|
-
instanceId: match.slug,
|
|
151
|
-
...ref,
|
|
152
|
-
}) as Promise<DomainInstallResult>,
|
|
158
|
+
() => admin.install(ctx, match, domain),
|
|
153
159
|
{ success: (r) => `Installed ${r.origin} on ${match.slug}` },
|
|
154
160
|
)
|
|
155
161
|
|
|
@@ -193,15 +199,25 @@ export function domainRefFromTarget(target: string): { origin?: string; url?: st
|
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
/** Resolve the domain to install: the positional `target`, or an interactive pick. */
|
|
196
|
-
async function
|
|
197
|
-
|
|
202
|
+
async function resolveDomain(
|
|
203
|
+
catalog: readonly DomainInfo[],
|
|
198
204
|
target: string | undefined,
|
|
199
205
|
interactive: boolean,
|
|
200
|
-
): Promise<
|
|
201
|
-
if (target)
|
|
206
|
+
): Promise<DomainInfo> {
|
|
207
|
+
if (target) {
|
|
208
|
+
const ref = domainRefFromTarget(target)
|
|
209
|
+
const found = catalog.find((domain) =>
|
|
210
|
+
ref.origin === undefined ? domain.url === ref.url : domain.origin === ref.origin,
|
|
211
|
+
)
|
|
212
|
+
if (found !== undefined) return found
|
|
213
|
+
throw new AstraleError(
|
|
214
|
+
'DOMAIN_NOT_FOUND',
|
|
215
|
+
`No published domain matches "${target}".`,
|
|
216
|
+
'Run `astrale domain list` to see the Admin catalog.',
|
|
217
|
+
)
|
|
218
|
+
}
|
|
202
219
|
if (!interactive) throw new AstraleError('MISSING_ARG', 'No domain given.')
|
|
203
220
|
|
|
204
|
-
const catalog = (await ctx.client.call(adminDomainMethod('list'), {})) as DomainInfo[]
|
|
205
221
|
const installable = catalog.filter((d) => d.url)
|
|
206
222
|
if (installable.length === 0) {
|
|
207
223
|
throw new AstraleError(
|
|
@@ -218,9 +234,21 @@ async function resolveDomainRef(
|
|
|
218
234
|
})),
|
|
219
235
|
)
|
|
220
236
|
if (!origin) throw new AstraleError('CANCELLED', 'No domain selected.')
|
|
221
|
-
return
|
|
237
|
+
return catalog.find((domain) => domain.origin === origin)!
|
|
222
238
|
}
|
|
223
239
|
|
|
240
|
+
interface AdminInstallDependencies {
|
|
241
|
+
readonly listInstances: typeof listOwnedInstancesInContext
|
|
242
|
+
readonly listDomains: typeof listAdminDomainsInContext
|
|
243
|
+
readonly install: typeof installAdminDomainInContext
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const defaultAdminInstallDependencies: AdminInstallDependencies = Object.freeze({
|
|
247
|
+
listInstances: listOwnedInstancesInContext,
|
|
248
|
+
listDomains: listAdminDomainsInContext,
|
|
249
|
+
install: installAdminDomainInContext,
|
|
250
|
+
})
|
|
251
|
+
|
|
224
252
|
/**
|
|
225
253
|
* Resolve the target instance slug: `-i`, else the active instance. When run
|
|
226
254
|
* bare (no positional) in a TTY, prompt for it with the active instance
|
|
@@ -271,8 +299,8 @@ async function activeSlug(): Promise<string | undefined> {
|
|
|
271
299
|
// ── Direct path (--direct) ────────────────────────────────────────────────────
|
|
272
300
|
|
|
273
301
|
/**
|
|
274
|
-
* Install a url
|
|
275
|
-
* bypassing the admin
|
|
302
|
+
* Install a url through the public Kernel install syscall,
|
|
303
|
+
* bypassing the admin catalog, with the identity-override
|
|
276
304
|
* consent gate. Works on any instance the caller can authenticate to.
|
|
277
305
|
*/
|
|
278
306
|
async function installDirect(target: string | undefined, opts: InstallOpts): Promise<void> {
|
|
@@ -300,26 +328,29 @@ async function installDirect(target: string | undefined, opts: InstallOpts): Pro
|
|
|
300
328
|
await runKernelCommand<DirectInstallResult>({
|
|
301
329
|
opts,
|
|
302
330
|
label: `Installing domain from ${url}`,
|
|
303
|
-
fn: async (
|
|
304
|
-
(await
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
331
|
+
fn: async ({ session }) =>
|
|
332
|
+
(await session.call(
|
|
333
|
+
createPathCall(Path.project(syscalls.install.ref).raw, {
|
|
334
|
+
domains: [{ url, ...(opts.token ? { token: opts.token } : {}) }],
|
|
335
|
+
}),
|
|
336
|
+
)) as DirectInstallResult,
|
|
308
337
|
format: (result, fmtOpts, isRaw) => {
|
|
309
338
|
if (isRaw) {
|
|
310
339
|
output(result, fmtOpts)
|
|
311
340
|
return
|
|
312
341
|
}
|
|
313
|
-
|
|
314
|
-
|
|
342
|
+
const installed = result[0]
|
|
343
|
+
if (!installed) throw new Error('Kernel install returned no installed Domain receipt.')
|
|
344
|
+
log.success(`Domain installed: ${installed.origin}@${installed.revision}`)
|
|
345
|
+
if (installed.etag) log.dim(` publication: ${installed.etag}`)
|
|
315
346
|
// Belt-and-braces: the kernel-confirmed origin is authoritative. If it
|
|
316
347
|
// aliases the host and the pre-install gate never consented to THAT
|
|
317
348
|
// origin (lying or absent `/meta`), say so loudly after the fact.
|
|
318
|
-
if (isIdentityOverride(
|
|
349
|
+
if (isIdentityOverride(installed.origin, host) && installed.origin !== consentedOrigin) {
|
|
319
350
|
log.warn(
|
|
320
|
-
`Installed origin "${
|
|
351
|
+
`Installed origin "${installed.origin}" differs from the serving host "${host}" ` +
|
|
321
352
|
`and was not confirmed before install (the worker's /meta did not declare it). ` +
|
|
322
|
-
`Every ${
|
|
353
|
+
`Every ${installed.origin}/* call on this instance now routes to ${host}.`,
|
|
323
354
|
)
|
|
324
355
|
}
|
|
325
356
|
},
|
|
@@ -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 {
|
|
3
|
+
import type { KernelCommandOpts } from '../connection'
|
|
4
|
+
import type { CommandDefinition } from '../program/index'
|
|
5
|
+
|
|
6
|
+
import { expandSelfInPath, runKernelCommand, withSelfHint } from '../connection'
|
|
5
7
|
import { log } from '../lib/log'
|
|
6
8
|
import { output } from '../lib/output'
|
|
7
9
|
|
|
8
|
-
type GetOpts = KernelCommandOpts
|
|
9
|
-
long?: boolean
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const INTERNAL_KEYS = new Set(['__labels', 'classId'])
|
|
13
|
-
|
|
14
|
-
export async function getCommand(pathArg: string, opts: GetOpts): Promise<void> {
|
|
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
|
-
log.error(
|
|
17
|
+
;({ path, meta } = await expandSelfInPath(target, opts))
|
|
18
|
+
} catch (error) {
|
|
19
|
+
log.error(error instanceof Error ? error.message : 'Invalid target')
|
|
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) {
|