@astrale-os/cli 0.8.1-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/dist/astrale.js +62959 -41238
- 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.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-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
package/src/command.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export type CommandArgument = {
|
|
2
|
-
name: string
|
|
3
|
-
description: string
|
|
4
|
-
required?: boolean
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export type CommandOption = {
|
|
8
|
-
flags: string
|
|
9
|
-
description: string
|
|
10
|
-
default?: string
|
|
11
|
-
choices?: string[]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type CommandDefinition = {
|
|
15
|
-
name: string
|
|
16
|
-
description: string
|
|
17
|
-
/** One-line summary shown in the parent's command list (Commander `.summary()`). */
|
|
18
|
-
summary?: string
|
|
19
|
-
/** Hide from help listings (internal entries, e.g. `__view-serve`). */
|
|
20
|
-
hidden?: boolean
|
|
21
|
-
aliases?: string[]
|
|
22
|
-
arguments?: CommandArgument[]
|
|
23
|
-
options?: CommandOption[]
|
|
24
|
-
/**
|
|
25
|
-
* Free-form prose appended to `<cmd> --help` (Commander `addHelpText('after')`).
|
|
26
|
-
* Use for command-local behavior the flag list cannot express + canonical
|
|
27
|
-
* examples. Pre-wrap lines to ~78 cols — Commander prints it verbatim.
|
|
28
|
-
*/
|
|
29
|
-
afterHelpText?: string
|
|
30
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Commander passes typed args at runtime
|
|
31
|
-
action: (...args: any[]) => Promise<void>
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export type CommandGroup = {
|
|
35
|
-
name: string
|
|
36
|
-
description: string
|
|
37
|
-
/** One-line summary shown in the program's command list (Commander `.summary()`). */
|
|
38
|
-
summary?: string
|
|
39
|
-
commands: CommandDefinition[]
|
|
40
|
-
/** Nested subgroups (one extra level of `astrale foo bar baz` nesting). */
|
|
41
|
-
subgroups?: CommandGroup[]
|
|
42
|
-
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import type { Command } from 'commander'
|
|
2
|
-
|
|
3
|
-
import { describe, expect, test } from 'bun:test'
|
|
4
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
5
|
-
import { join } from 'node:path'
|
|
6
|
-
|
|
7
|
-
import { buildProgram } from '../../program'
|
|
8
|
-
|
|
9
|
-
// Help output is the public CLI contract: version, spec anchors, and skill mirror stay in sync.
|
|
10
|
-
|
|
11
|
-
const cliRoot = join(import.meta.dir, '../../..')
|
|
12
|
-
|
|
13
|
-
/** Every command in the tree (root + groups + nested subgroups), depth-first. */
|
|
14
|
-
function allCommands(cmd: Command): Command[] {
|
|
15
|
-
return [cmd, ...cmd.commands.flatMap(allCommands)]
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
describe('help contract — version is single-sourced', () => {
|
|
19
|
-
test('program version === package.json === release-please manifest', async () => {
|
|
20
|
-
const program = await buildProgram()
|
|
21
|
-
const pkg = JSON.parse(readFileSync(join(cliRoot, 'package.json'), 'utf8')) as {
|
|
22
|
-
version: string
|
|
23
|
-
}
|
|
24
|
-
const manifest = JSON.parse(
|
|
25
|
-
readFileSync(join(cliRoot, '.release-please-manifest.json'), 'utf8'),
|
|
26
|
-
) as Record<string, string>
|
|
27
|
-
|
|
28
|
-
// Program version must follow both package.json and release-please metadata.
|
|
29
|
-
expect(program.version()).toBe(pkg.version)
|
|
30
|
-
expect(manifest['.']).toBe(pkg.version)
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
describe('help contract — no internal SPEC anchors leak to users', () => {
|
|
35
|
-
test('no rendered --help text contains a § section anchor', async () => {
|
|
36
|
-
const program = await buildProgram()
|
|
37
|
-
const offenders = allCommands(program)
|
|
38
|
-
.filter((c) => c.helpInformation().includes('§'))
|
|
39
|
-
.map((c) => c.name() || '<root>')
|
|
40
|
-
|
|
41
|
-
expect(offenders).toEqual([])
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
describe('help contract — IdP/auth surface is registered', () => {
|
|
46
|
-
test('idp group and auth commands are visible in --help tree', async () => {
|
|
47
|
-
const program = await buildProgram()
|
|
48
|
-
const names = allCommands(program).map((command) => command.name())
|
|
49
|
-
|
|
50
|
-
expect(names).toContain('idp')
|
|
51
|
-
expect(names).toContain('add')
|
|
52
|
-
expect(names).toContain('login')
|
|
53
|
-
expect(names).toContain('token')
|
|
54
|
-
expect(names).toContain('update')
|
|
55
|
-
expect(program.helpInformation()).toContain('idp')
|
|
56
|
-
expect(program.helpInformation()).toContain('update')
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
describe('help contract — admin target surface is registered', () => {
|
|
61
|
-
test('admin group and admin-target flags are visible', async () => {
|
|
62
|
-
const program = await buildProgram()
|
|
63
|
-
const names = allCommands(program).map((command) => command.name())
|
|
64
|
-
const instanceCreate = allCommands(program).find((command) => command.name() === 'create')
|
|
65
|
-
|
|
66
|
-
expect(names).toContain('admin')
|
|
67
|
-
expect(names).toContain('status')
|
|
68
|
-
expect(names).toContain('use')
|
|
69
|
-
expect(program.helpInformation()).toContain('admin')
|
|
70
|
-
expect(instanceCreate?.helpInformation()).toContain('--admin <name>')
|
|
71
|
-
expect(instanceCreate?.helpInformation()).toContain('--admin-url <url>')
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test('instance create is the alphaCreate flow, not legacy Instance.init DX', async () => {
|
|
75
|
-
const program = await buildProgram()
|
|
76
|
-
const instanceCreate = allCommands(program).find((command) => command.name() === 'create')
|
|
77
|
-
const help = instanceCreate?.helpInformation() ?? ''
|
|
78
|
-
|
|
79
|
-
expect(help).toContain('Instance.alphaCreate')
|
|
80
|
-
expect(help).not.toContain('--no-use')
|
|
81
|
-
expect(help).not.toContain('Instance.init requires --host-id')
|
|
82
|
-
})
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
describe('help contract — connect-only command surface', () => {
|
|
86
|
-
test('runtime management commands are not registered', async () => {
|
|
87
|
-
const program = await buildProgram()
|
|
88
|
-
const names = allCommands(program).map((command) => command.name())
|
|
89
|
-
|
|
90
|
-
// `logs` and `domain` have managed/admin meanings, so only retired local-runtime verbs are absent.
|
|
91
|
-
for (const removed of [
|
|
92
|
-
'init',
|
|
93
|
-
'start',
|
|
94
|
-
'stop',
|
|
95
|
-
'restart',
|
|
96
|
-
'reset',
|
|
97
|
-
'bootstrap',
|
|
98
|
-
'tunnel',
|
|
99
|
-
'graph',
|
|
100
|
-
'server',
|
|
101
|
-
'env',
|
|
102
|
-
]) {
|
|
103
|
-
expect(names).not.toContain(removed)
|
|
104
|
-
}
|
|
105
|
-
})
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
describe('help contract — read command split', () => {
|
|
109
|
-
test('get is point-read only and query owns structured read flags', async () => {
|
|
110
|
-
const program = await buildProgram()
|
|
111
|
-
const get = allCommands(program).find((command) => command.name() === 'get')
|
|
112
|
-
const query = allCommands(program).find((command) => command.name() === 'query')
|
|
113
|
-
const getHelp = get?.helpInformation() ?? ''
|
|
114
|
-
const queryHelp = query?.helpInformation() ?? ''
|
|
115
|
-
|
|
116
|
-
expect(getHelp).toContain('Usage: astrale get [options] <path>')
|
|
117
|
-
expect(getHelp).toContain('-l, --long')
|
|
118
|
-
expect(getHelp).not.toContain('--depth')
|
|
119
|
-
expect(getHelp).not.toContain('--children')
|
|
120
|
-
expect(getHelp).not.toContain('--edges')
|
|
121
|
-
expect(getHelp).not.toContain('--graph')
|
|
122
|
-
|
|
123
|
-
expect(queryHelp).toContain('Usage: astrale query [options] [paths...]')
|
|
124
|
-
expect(queryHelp).toContain('--depth <n>')
|
|
125
|
-
expect(queryHelp).toContain('--children <json>')
|
|
126
|
-
expect(queryHelp).toContain('--edges <json>')
|
|
127
|
-
expect(queryHelp).toContain('--ast <json>')
|
|
128
|
-
expect(queryHelp).toContain('--cypher <query>')
|
|
129
|
-
})
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
describe('help contract — payload sources', () => {
|
|
133
|
-
test('call excludes --file while mutate supports it', async () => {
|
|
134
|
-
const program = await buildProgram()
|
|
135
|
-
const call = allCommands(program).find((command) => command.name() === 'call')
|
|
136
|
-
const mutate = allCommands(program).find((command) => command.name() === 'mutate')
|
|
137
|
-
const callHelp = call?.helpInformation() ?? ''
|
|
138
|
-
const mutateHelp = mutate?.helpInformation() ?? ''
|
|
139
|
-
|
|
140
|
-
expect(callHelp).toContain('--data <json>')
|
|
141
|
-
expect(callHelp).not.toContain('--file <path>')
|
|
142
|
-
expect(mutateHelp).toContain('--data <json>')
|
|
143
|
-
expect(mutateHelp).toContain('--file <path>')
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
describe('help contract — skill is single-source, not duplicated', () => {
|
|
148
|
-
const canonical = join(cliRoot, 'skills/astrale-cli/SKILL.md')
|
|
149
|
-
// Workspace mirror lives in the superrepo, outside this submodule. Absent
|
|
150
|
-
// when the CLI repo is tested standalone — only assert parity when present.
|
|
151
|
-
const mirror = join(cliRoot, '../.agents/skills/astrale-cli/SKILL.md')
|
|
152
|
-
|
|
153
|
-
test('canonical skill file exists and is non-empty', () => {
|
|
154
|
-
expect(existsSync(canonical)).toBe(true)
|
|
155
|
-
expect(readFileSync(canonical, 'utf8').length).toBeGreaterThan(0)
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
test.skipIf(!existsSync(mirror))(
|
|
159
|
-
'workspace mirror is byte-identical to the canonical skill',
|
|
160
|
-
() => {
|
|
161
|
-
expect(readFileSync(mirror, 'utf8')).toBe(readFileSync(canonical, 'utf8'))
|
|
162
|
-
},
|
|
163
|
-
)
|
|
164
|
-
})
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from 'bun:test'
|
|
2
|
-
|
|
3
|
-
import type { OwnedInstanceInfo } from '../../lib/admin-instance'
|
|
4
|
-
import type { AdminTargetCommandOpts } from '../../lib/admin-target'
|
|
5
|
-
import type { KernelCommandOpts } from '../types'
|
|
6
|
-
|
|
7
|
-
import { lookupImplicitOwnedInstance } from '../client'
|
|
8
|
-
|
|
9
|
-
describe('implicit managed target owner discovery', () => {
|
|
10
|
-
test('keeps target credentials out of the admin inventory lookup', async () => {
|
|
11
|
-
const owned: OwnedInstanceInfo = {
|
|
12
|
-
id: 'owned-id',
|
|
13
|
-
slug: 'owned',
|
|
14
|
-
url: 'https://owned.eu.astrale.ai',
|
|
15
|
-
state: 'ready',
|
|
16
|
-
}
|
|
17
|
-
let captured:
|
|
18
|
-
| {
|
|
19
|
-
slug: string
|
|
20
|
-
opts: KernelCommandOpts & AdminTargetCommandOpts
|
|
21
|
-
}
|
|
22
|
-
| undefined
|
|
23
|
-
const lookupOwned = mock(
|
|
24
|
-
async (slug: string, opts: KernelCommandOpts & AdminTargetCommandOpts) => {
|
|
25
|
-
captured = { slug, opts }
|
|
26
|
-
return owned
|
|
27
|
-
},
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
await lookupImplicitOwnedInstance(
|
|
31
|
-
'owned',
|
|
32
|
-
{
|
|
33
|
-
as: 'target-identity',
|
|
34
|
-
creds: 'target-delegation',
|
|
35
|
-
timeout: '45000',
|
|
36
|
-
debug: true,
|
|
37
|
-
},
|
|
38
|
-
{ lookupOwned },
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
expect(captured).toEqual({
|
|
42
|
-
slug: 'owned',
|
|
43
|
-
opts: {
|
|
44
|
-
timeout: '45000',
|
|
45
|
-
debug: true,
|
|
46
|
-
},
|
|
47
|
-
})
|
|
48
|
-
expect(captured?.opts).not.toHaveProperty('as')
|
|
49
|
-
expect(captured?.opts).not.toHaveProperty('creds')
|
|
50
|
-
})
|
|
51
|
-
})
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
|
|
3
|
-
// We test the stripMethodSuffix behavior indirectly through the module's exports.
|
|
4
|
-
// Since stripMethodSuffix is private, we test the pattern directly.
|
|
5
|
-
const stripMethodSuffix = (msg: string): string => {
|
|
6
|
-
return msg.replace(/(\/[^"\s:]+)::([a-zA-Z]\w*)/g, '$1')
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
describe('stripMethodSuffix', () => {
|
|
10
|
-
test('strips ::listChildren from path', () => {
|
|
11
|
-
expect(stripMethodSuffix('Path not found: "/nonexistent::listChildren"')).toBe(
|
|
12
|
-
'Path not found: "/nonexistent"',
|
|
13
|
-
)
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
test('strips ::get from path', () => {
|
|
17
|
-
expect(stripMethodSuffix('Path not found: "/some/path::get"')).toBe(
|
|
18
|
-
'Path not found: "/some/path"',
|
|
19
|
-
)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
test('strips any method name', () => {
|
|
23
|
-
expect(stripMethodSuffix('"/kernel.astrale.ai/Root::describe"')).toBe(
|
|
24
|
-
'"/kernel.astrale.ai/Root"',
|
|
25
|
-
)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
test('does not strip from non-path strings', () => {
|
|
29
|
-
expect(stripMethodSuffix('regular::text')).toBe('regular::text')
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
test('handles multiple paths in one message', () => {
|
|
33
|
-
expect(stripMethodSuffix('"/a/b::get" and "/c/d::list"')).toBe('"/a/b" and "/c/d"')
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
test('preserves messages without method suffixes', () => {
|
|
37
|
-
expect(stripMethodSuffix('No path found')).toBe('No path found')
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
test('does not strip single colon (not a method dispatch)', () => {
|
|
41
|
-
expect(stripMethodSuffix('"/a/b:notMethod"')).toBe('"/a/b:notMethod"')
|
|
42
|
-
})
|
|
43
|
-
})
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
|
|
3
|
-
import type { SelfResolverContext } from '../../lib/self'
|
|
4
|
-
|
|
5
|
-
import { resolveSelfIdLazy } from '../expand'
|
|
6
|
-
|
|
7
|
-
describe('resolveSelfIdLazy', () => {
|
|
8
|
-
test('refreshes a cached IdP @self registration via whoami', async () => {
|
|
9
|
-
const writes: unknown[][] = []
|
|
10
|
-
const ctx: SelfResolverContext = {
|
|
11
|
-
identity: {
|
|
12
|
-
name: 'bryan',
|
|
13
|
-
subject: 'user_01KC9MW1M6S5J6V9ERSRJ8RDYF',
|
|
14
|
-
createdAt: '2026-06-25T08:00:00.000Z',
|
|
15
|
-
source: 'idp',
|
|
16
|
-
registrations: {
|
|
17
|
-
bryan: {
|
|
18
|
-
iss: 'https://old.example',
|
|
19
|
-
sub: '4ad8e4ce-5cf7-4c2e-ab29-5549023dc8bf',
|
|
20
|
-
registeredAt: '2026-06-25T08:00:00.000Z',
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
instanceSlug: 'bryan',
|
|
25
|
-
instanceSigned: false,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const id = await resolveSelfIdLazy(
|
|
29
|
-
ctx,
|
|
30
|
-
{},
|
|
31
|
-
{
|
|
32
|
-
whoami: async () => ({
|
|
33
|
-
id: 'f011538e-9edc-4c29-92ce-9b81b6c1b6c7',
|
|
34
|
-
kernelUrl: 'https://bryan.example',
|
|
35
|
-
}),
|
|
36
|
-
setRegistration: async (...args) => {
|
|
37
|
-
writes.push(args)
|
|
38
|
-
},
|
|
39
|
-
now: () => new Date('2026-06-25T08:15:00.000Z'),
|
|
40
|
-
},
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
expect(id).toBe('f011538e-9edc-4c29-92ce-9b81b6c1b6c7')
|
|
44
|
-
expect(writes).toEqual([
|
|
45
|
-
[
|
|
46
|
-
'bryan',
|
|
47
|
-
'bryan',
|
|
48
|
-
{
|
|
49
|
-
iss: 'https://bryan.example',
|
|
50
|
-
sub: 'f011538e-9edc-4c29-92ce-9b81b6c1b6c7',
|
|
51
|
-
registeredAt: '2026-06-25T08:15:00.000Z',
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
])
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
test('keeps the cached IdP id when whoami is temporarily unavailable', async () => {
|
|
58
|
-
const ctx: SelfResolverContext = {
|
|
59
|
-
identity: {
|
|
60
|
-
name: 'bryan',
|
|
61
|
-
subject: 'user_01KC9MW1M6S5J6V9ERSRJ8RDYF',
|
|
62
|
-
createdAt: '2026-06-25T08:00:00.000Z',
|
|
63
|
-
source: 'idp',
|
|
64
|
-
registrations: {
|
|
65
|
-
bryan: {
|
|
66
|
-
iss: 'https://bryan.example',
|
|
67
|
-
sub: 'cached-id',
|
|
68
|
-
registeredAt: '2026-06-25T08:00:00.000Z',
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
instanceSlug: 'bryan',
|
|
73
|
-
instanceSigned: false,
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
await expect(
|
|
77
|
-
resolveSelfIdLazy(
|
|
78
|
-
ctx,
|
|
79
|
-
{},
|
|
80
|
-
{
|
|
81
|
-
whoami: async () => {
|
|
82
|
-
throw new Error('network down')
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
),
|
|
86
|
-
).resolves.toBe('cached-id')
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
test('does not run whoami for key-backed registrations', async () => {
|
|
90
|
-
const ctx: SelfResolverContext = {
|
|
91
|
-
identity: {
|
|
92
|
-
name: 'alice',
|
|
93
|
-
subject: 'alice',
|
|
94
|
-
createdAt: '2026-06-25T08:00:00.000Z',
|
|
95
|
-
source: 'key',
|
|
96
|
-
registrations: {
|
|
97
|
-
bryan: {
|
|
98
|
-
iss: 'https://bryan.example',
|
|
99
|
-
sub: 'key-node-id',
|
|
100
|
-
registeredAt: '2026-06-25T08:00:00.000Z',
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
instanceSlug: 'bryan',
|
|
105
|
-
instanceSigned: false,
|
|
106
|
-
}
|
|
107
|
-
let called = false
|
|
108
|
-
|
|
109
|
-
const id = await resolveSelfIdLazy(
|
|
110
|
-
ctx,
|
|
111
|
-
{},
|
|
112
|
-
{
|
|
113
|
-
whoami: async () => {
|
|
114
|
-
called = true
|
|
115
|
-
return { id: 'should-not-be-used', kernelUrl: 'https://bryan.example' }
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
expect(id).toBe('key-node-id')
|
|
121
|
-
expect(called).toBe(false)
|
|
122
|
-
})
|
|
123
|
-
})
|
package/src/kernel/client.ts
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
import { KernelClient, type FnMap } from '@astrale-os/kernel-client'
|
|
2
|
-
import { ClientSession, delegationMintVia } from '@astrale-os/kernel-client/session'
|
|
3
|
-
|
|
4
|
-
import type { AdminTargetCommandOpts } from '../lib/admin-target'
|
|
5
|
-
import type { KernelCommandOpts } from './types'
|
|
6
|
-
|
|
7
|
-
import { AstraleError } from '../errors'
|
|
8
|
-
import {
|
|
9
|
-
callOwnedInstances,
|
|
10
|
-
findOwnedInstance,
|
|
11
|
-
type InstanceInfo,
|
|
12
|
-
type OwnedInstanceInfo,
|
|
13
|
-
} from '../lib/admin-instance'
|
|
14
|
-
import { readConfig } from '../lib/config'
|
|
15
|
-
import { resolveInstanceTarget, type ResolvedInstanceTarget } from '../lib/instance-target'
|
|
16
|
-
import { resolveCredential } from './auth'
|
|
17
|
-
import { fetchWithCaFile } from './ca-fetch'
|
|
18
|
-
|
|
19
|
-
const DEFAULT_TIMEOUT_MS = 30_000
|
|
20
|
-
|
|
21
|
-
export type ClientContext = {
|
|
22
|
-
/** High-level call surface — bound to `credential` via ClientSession.identity. */
|
|
23
|
-
client: ClientSession<FnMap>
|
|
24
|
-
credential: string
|
|
25
|
-
url: string
|
|
26
|
-
config: Awaited<ReturnType<typeof readConfig>>
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export type ResolvedKernelTarget = {
|
|
30
|
-
url: string
|
|
31
|
-
audience: string
|
|
32
|
-
slug?: string
|
|
33
|
-
defaultIdentity?: string
|
|
34
|
-
caFile?: string
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Resolve the kernel a command targets (`--url` / `-i` / active instance). */
|
|
38
|
-
export async function resolveKernelTarget(
|
|
39
|
-
opts: KernelCommandOpts,
|
|
40
|
-
config: Awaited<ReturnType<typeof readConfig>>,
|
|
41
|
-
): Promise<ResolvedKernelTarget> {
|
|
42
|
-
// Ad-hoc `--url` — unknown kernel. Stamp the URL itself as audience,
|
|
43
|
-
// no slug for per-instance signing.
|
|
44
|
-
if (opts.url && !opts.instance) {
|
|
45
|
-
const resolved = await resolveInstanceTarget({ source: 'url', url: opts.url }, { config })
|
|
46
|
-
return resolvedToKernelTarget(resolved)
|
|
47
|
-
}
|
|
48
|
-
const resolved = await resolveInstanceTarget(
|
|
49
|
-
opts.instance ? { source: 'name', name: opts.instance } : { source: 'active' },
|
|
50
|
-
{
|
|
51
|
-
config,
|
|
52
|
-
admin: {},
|
|
53
|
-
managed: (slug) => lookupImplicitOwnedInstance(slug, opts),
|
|
54
|
-
},
|
|
55
|
-
)
|
|
56
|
-
return resolvedToKernelTarget(resolved, opts.url)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Connect to a kernel instance, run `fn`, then disconnect.
|
|
61
|
-
* The new client is lazy: construction does no I/O. We only need to
|
|
62
|
-
* release sockets on the way out.
|
|
63
|
-
*/
|
|
64
|
-
export async function withKernelClient<T>(
|
|
65
|
-
opts: KernelCommandOpts,
|
|
66
|
-
fn: (ctx: ClientContext) => Promise<T>,
|
|
67
|
-
): Promise<T> {
|
|
68
|
-
const config = await readConfig()
|
|
69
|
-
const target = await resolveKernelTarget(opts, config)
|
|
70
|
-
return withResolvedKernelClient(opts, config, target, fn)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export async function listOwnedInstances(
|
|
74
|
-
opts: KernelCommandOpts & AdminTargetCommandOpts,
|
|
75
|
-
): Promise<OwnedInstanceInfo[]> {
|
|
76
|
-
return await withAdminKernelClient(opts, async (ctx) => callOwnedInstances(ctx.client))
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export async function lookupOwnedInstance(
|
|
80
|
-
slug: string,
|
|
81
|
-
opts: KernelCommandOpts & AdminTargetCommandOpts,
|
|
82
|
-
): Promise<OwnedInstanceInfo> {
|
|
83
|
-
const instance = findOwnedInstance(await listOwnedInstances(opts), slug)
|
|
84
|
-
if (instance) return instance
|
|
85
|
-
throw new AstraleError('INSTANCE_NOT_FOUND', `No owned instance matches "${slug}".`)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export type ImplicitOwnedInstanceLookupDependencies = {
|
|
89
|
-
lookupOwned: (
|
|
90
|
-
slug: string,
|
|
91
|
-
opts: KernelCommandOpts & AdminTargetCommandOpts,
|
|
92
|
-
) => Promise<OwnedInstanceInfo>
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Resolve an implicit managed target through the caller's owner inventory.
|
|
97
|
-
*
|
|
98
|
-
* Target `--as` / `--creds` belong to the eventual child-kernel call. They
|
|
99
|
-
* must not authenticate the admin discovery request: leaving them out lets
|
|
100
|
-
* `withAdminKernelClient` use the admin bookmark's default WorkOS identity.
|
|
101
|
-
*/
|
|
102
|
-
export async function lookupImplicitOwnedInstance(
|
|
103
|
-
slug: string,
|
|
104
|
-
targetOpts: KernelCommandOpts,
|
|
105
|
-
deps: ImplicitOwnedInstanceLookupDependencies = {
|
|
106
|
-
lookupOwned: lookupOwnedInstance,
|
|
107
|
-
},
|
|
108
|
-
): Promise<InstanceInfo> {
|
|
109
|
-
return await deps.lookupOwned(slug, {
|
|
110
|
-
timeout: targetOpts.timeout,
|
|
111
|
-
debug: targetOpts.debug,
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function resolvedToKernelTarget(
|
|
116
|
-
target: ResolvedInstanceTarget,
|
|
117
|
-
urlOverride?: string,
|
|
118
|
-
): ResolvedKernelTarget {
|
|
119
|
-
return {
|
|
120
|
-
url: urlOverride ?? target.url,
|
|
121
|
-
audience: target.issuer,
|
|
122
|
-
slug: target.name,
|
|
123
|
-
defaultIdentity: target.defaultIdentity,
|
|
124
|
-
caFile: target.caFile,
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export async function withAdminKernelClient<T>(
|
|
129
|
-
opts: KernelCommandOpts & AdminTargetCommandOpts,
|
|
130
|
-
fn: (ctx: ClientContext) => Promise<T>,
|
|
131
|
-
): Promise<T> {
|
|
132
|
-
const config = await readConfig()
|
|
133
|
-
const target = await resolveInstanceTarget({ source: 'admin' }, { config, admin: opts })
|
|
134
|
-
return withResolvedKernelClient(
|
|
135
|
-
opts,
|
|
136
|
-
config,
|
|
137
|
-
{
|
|
138
|
-
url: target.url,
|
|
139
|
-
audience: target.issuer,
|
|
140
|
-
slug: target.name,
|
|
141
|
-
defaultIdentity: target.defaultIdentity,
|
|
142
|
-
caFile: target.caFile,
|
|
143
|
-
},
|
|
144
|
-
fn,
|
|
145
|
-
)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async function withResolvedKernelClient<T>(
|
|
149
|
-
opts: KernelCommandOpts,
|
|
150
|
-
config: Awaited<ReturnType<typeof readConfig>>,
|
|
151
|
-
target: ResolvedKernelTarget,
|
|
152
|
-
fn: (ctx: ClientContext) => Promise<T>,
|
|
153
|
-
): Promise<T> {
|
|
154
|
-
const credential = await resolveCredential(
|
|
155
|
-
{ ...opts, defaultIdentity: target.defaultIdentity },
|
|
156
|
-
config,
|
|
157
|
-
target.audience,
|
|
158
|
-
target.slug,
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
// CLI is short-lived and one-shot per command. Skip the WS upgrade
|
|
162
|
-
// (saves up to 5s on hangs) and disable HTTP retries (saves ~7s of
|
|
163
|
-
// exponential backoff on ECONNREFUSED / 5xx). The user can re-run.
|
|
164
|
-
const requestTimeout = resolveTimeoutMs(opts.timeout)
|
|
165
|
-
const fetchImpl = target.caFile ? fetchWithCaFile(target.caFile) : undefined
|
|
166
|
-
const client: ClientSession<FnMap> = new ClientSession<FnMap>({
|
|
167
|
-
default: target.url,
|
|
168
|
-
identity: credential,
|
|
169
|
-
delegation: {
|
|
170
|
-
// Resolve lazily: the session must exist before the cache mints.
|
|
171
|
-
mint: delegationMintVia(() => client, credential),
|
|
172
|
-
ttl: 3600,
|
|
173
|
-
},
|
|
174
|
-
pool: {
|
|
175
|
-
clientFactory: (u) =>
|
|
176
|
-
new KernelClient<FnMap>({
|
|
177
|
-
url: u,
|
|
178
|
-
requestTimeout,
|
|
179
|
-
defaultTransport: 'http',
|
|
180
|
-
retry: { maxAttempts: 1 },
|
|
181
|
-
...(fetchImpl ? { fetch: fetchImpl } : {}),
|
|
182
|
-
}),
|
|
183
|
-
},
|
|
184
|
-
})
|
|
185
|
-
await client.ready()
|
|
186
|
-
|
|
187
|
-
try {
|
|
188
|
-
return await fn({ client, credential, url: target.url, config })
|
|
189
|
-
} catch (error) {
|
|
190
|
-
// Attach url so formatKernelError can display it in connection errors
|
|
191
|
-
if (error instanceof Error) (error as Error & { url?: string }).url = target.url
|
|
192
|
-
throw error
|
|
193
|
-
} finally {
|
|
194
|
-
client.disconnect()
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function resolveTimeoutMs(raw: string | undefined): number {
|
|
199
|
-
if (raw === undefined) return DEFAULT_TIMEOUT_MS
|
|
200
|
-
if (!/^\d+$/.test(raw)) {
|
|
201
|
-
throw new AstraleError(
|
|
202
|
-
'INVALID_FLAG',
|
|
203
|
-
`Invalid --timeout value "${raw}" — expected a positive integer (milliseconds)`,
|
|
204
|
-
)
|
|
205
|
-
}
|
|
206
|
-
const n = Number.parseInt(raw, 10)
|
|
207
|
-
if (!Number.isFinite(n) || n <= 0) {
|
|
208
|
-
throw new AstraleError(
|
|
209
|
-
'INVALID_FLAG',
|
|
210
|
-
`Invalid --timeout value "${raw}" — must be a positive integer`,
|
|
211
|
-
)
|
|
212
|
-
}
|
|
213
|
-
return n
|
|
214
|
-
}
|