@astrale-os/cli 1.0.0-beta.3 → 1.0.0-beta.31
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 +84 -6
- package/dist/astrale.js +57586 -60753
- package/dist/public/connect-core.js +2158 -30706
- package/dist/public/keys/index.js +1016 -29541
- package/dist/public/paths/index.js +370 -28654
- package/dist/types/admin/contract.d.ts +26 -0
- package/dist/types/admin/instance/client.d.ts +20 -0
- package/dist/types/admin/instance/index.d.ts +2 -0
- package/dist/types/admin/instance/model.d.ts +6 -10
- package/dist/types/connection/auth.d.ts +3 -0
- package/dist/types/connection/call.d.ts +3 -0
- package/dist/types/connection/command.d.ts +27 -0
- package/dist/types/connection/credential.d.ts +14 -0
- package/dist/types/connection/errors.d.ts +1 -0
- package/dist/types/connection/exchange.d.ts +9 -0
- package/dist/types/connection/failure/classify.d.ts +2 -0
- package/dist/types/connection/failure/index.d.ts +5 -0
- package/dist/types/connection/failure/model.d.ts +25 -0
- package/dist/types/connection/failure/render.d.ts +3 -0
- package/dist/types/connection/index.d.ts +8 -0
- package/dist/types/connection/reasons.d.ts +37 -0
- package/dist/types/connection/self.d.ts +37 -0
- package/dist/types/connection/session.d.ts +31 -0
- package/dist/types/connection/target.d.ts +38 -0
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/lib/admin-instance.d.ts +17 -0
- package/dist/types/lib/admin-target.d.ts +11 -36
- package/dist/types/lib/config.d.ts +13 -59
- package/dist/types/lib/failure-debug.d.ts +1 -0
- package/dist/types/lib/idp.d.ts +25 -173
- package/dist/types/lib/instance-target.d.ts +1 -1
- package/dist/types/lib/instance.d.ts +30 -88
- package/dist/types/lib/log.d.ts +5 -3
- package/dist/types/lib/output.d.ts +1 -1
- package/dist/types/lib/proc.d.ts +43 -0
- package/dist/types/lib/self.d.ts +9 -0
- package/dist/types/lib/update.d.ts +109 -0
- package/dist/types/lib/validation.d.ts +4 -1
- package/dist/types/state/exchange-credentials.d.ts +6 -2
- package/dist/types/state/files.d.ts +2 -0
- package/dist/types/state/index.d.ts +3 -2
- package/dist/types/state/paths.d.ts +2 -0
- package/dist/types/state/session-routes.d.ts +10 -0
- package/package.json +16 -19
- package/src/__tests__/fixtures/publication.ts +20 -0
- package/src/admin/.spec/architecture.md +12 -5
- package/src/admin/__tests__/fixture.ts +25 -0
- package/src/admin/catalog/.spec/api.d.ts +1 -3
- package/src/admin/catalog/.spec/architecture.md +5 -4
- package/src/admin/catalog/__tests__/client.test.ts +143 -69
- package/src/admin/catalog/client.ts +40 -54
- package/src/admin/catalog/model.ts +3 -4
- package/src/admin/contract.ts +47 -0
- package/src/admin/graph/.spec/api.d.ts +4 -10
- package/src/admin/graph/nodes.ts +1 -1
- package/src/admin/instance/.spec/api.d.ts +5 -9
- package/src/admin/instance/.spec/architecture.md +7 -7
- package/src/admin/instance/__tests__/client.test.ts +157 -175
- package/src/admin/instance/client.ts +39 -128
- package/src/admin/instance/index.ts +1 -2
- package/src/admin/instance/model.ts +12 -18
- package/src/commands/__tests__/admin-instance.test.ts +11 -8
- package/src/commands/__tests__/call-describe.test.ts +46 -33
- package/src/commands/__tests__/call.test.ts +34 -0
- package/src/commands/__tests__/domain-install-operation.test.ts +30 -22
- package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
- package/src/commands/__tests__/domain-list.test.ts +5 -25
- package/src/commands/__tests__/install-direct.test.ts +115 -8
- package/src/commands/__tests__/install-identity-override.test.ts +6 -17
- package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
- package/src/commands/__tests__/instance-status.test.ts +139 -0
- package/src/commands/__tests__/introspect-parse.test.ts +80 -1
- package/src/commands/__tests__/logs.test.ts +250 -2
- package/src/commands/__tests__/read-commands.test.ts +119 -11
- package/src/commands/__tests__/token-ttl.test.ts +49 -4
- package/src/commands/__tests__/update.test.ts +25 -3
- package/src/commands/__tests__/view.test.ts +50 -2
- package/src/commands/auth/logout.ts +2 -1
- package/src/commands/browser.ts +29 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +71 -59
- package/src/commands/domain/install.ts +73 -46
- package/src/commands/domain/list.ts +1 -1
- package/src/commands/domain/publish.ts +14 -11
- package/src/commands/domain/uninstall.ts +2 -2
- package/src/commands/get.ts +15 -16
- package/src/commands/identity/__tests__/register.test.ts +3 -3
- package/src/commands/identity/register.ts +8 -8
- package/src/commands/instance/create.ts +8 -16
- package/src/commands/instance/delete.ts +12 -9
- package/src/commands/instance/list.ts +2 -2
- package/src/commands/instance/status.ts +66 -20
- package/src/commands/instance/use.ts +16 -12
- package/src/commands/introspect.ts +53 -41
- package/src/commands/logs.ts +123 -36
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +39 -36
- package/src/commands/session/analyze.ts +26 -4
- package/src/commands/token.ts +42 -27
- package/src/commands/ui/__tests__/commands.test.ts +152 -0
- package/src/commands/ui/add.ts +51 -0
- package/src/commands/ui/doctor.ts +21 -0
- package/src/commands/ui/init.ts +38 -0
- package/src/commands/ui/list.ts +26 -0
- package/src/commands/ui/preset-apply.ts +19 -0
- package/src/commands/ui/preset-list.ts +12 -0
- package/src/commands/ui/shared.ts +25 -0
- package/src/commands/update.ts +90 -48
- package/src/commands/view.ts +82 -37
- package/src/connection/.spec/api.d.ts +7 -8
- package/src/connection/.spec/architecture.md +14 -5
- package/src/connection/.spec/flows/session.ts +2 -2
- package/src/connection/.spec/laws/connection.ts +6 -2
- package/src/connection/.spec/layout.ts +1 -0
- package/src/connection/__tests__/auth.test.ts +1 -0
- package/src/connection/__tests__/credential.test.ts +21 -1
- package/src/connection/__tests__/errors.test.ts +27 -20
- package/src/connection/__tests__/exchange.test.ts +64 -11
- package/src/connection/__tests__/failure-fixtures.ts +39 -0
- package/src/connection/__tests__/failure-safety.test.ts +75 -0
- package/src/connection/__tests__/self.test.ts +31 -11
- package/src/connection/__tests__/session.test.ts +8 -4
- package/src/connection/__tests__/target.test.ts +2 -0
- package/src/connection/auth.ts +13 -0
- package/src/connection/call.ts +1 -1
- package/src/connection/credential.ts +2 -2
- package/src/connection/errors.ts +1 -355
- package/src/connection/exchange.ts +110 -61
- package/src/connection/failure/classify.ts +110 -0
- package/src/connection/failure/index.ts +18 -0
- package/src/connection/failure/model.ts +28 -0
- package/src/connection/failure/render.ts +116 -0
- package/src/connection/reasons.ts +16 -0
- package/src/connection/self.ts +39 -21
- package/src/connection/session.ts +27 -9
- package/src/errors.ts +3 -2
- package/src/graph/.spec/api.d.ts +13 -6
- package/src/graph/.spec/laws/documents.ts +7 -7
- package/src/graph/.spec/layout.ts +9 -1
- package/src/graph/__tests__/mutation.test.ts +2 -2
- package/src/graph/__tests__/query.test.ts +35 -9
- package/src/graph/class.ts +30 -0
- package/src/graph/index.ts +1 -0
- package/src/graph/query.ts +27 -40
- package/src/identity/.spec/api.d.ts +2 -2
- package/src/identity/__tests__/fixtures/registry-journey.ts +13 -1
- package/src/identity/__tests__/registration.test.ts +1 -1
- package/src/identity/__tests__/registry.test.ts +4 -0
- package/src/identity/registration.ts +3 -3
- package/src/identity/registry.ts +2 -0
- package/src/keys/__tests__/keys.test.ts +4 -0
- package/src/keys/credential.ts +1 -0
- package/src/lib/__tests__/admin-target.test.ts +12 -0
- package/src/lib/__tests__/binary.test.ts +16 -1
- package/src/lib/__tests__/browser-retention.test.ts +212 -0
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/config.test.ts +27 -0
- package/src/lib/__tests__/domain-publication.test.ts +10 -16
- package/src/lib/__tests__/idp.test.ts +34 -4
- package/src/lib/__tests__/instance-candidates.test.ts +1 -3
- package/src/lib/__tests__/instance-target.test.ts +7 -39
- package/src/lib/__tests__/log-errors.test.ts +64 -0
- package/src/lib/__tests__/skills.test.ts +834 -0
- package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
- package/src/lib/__tests__/update.test.ts +264 -11
- package/src/lib/__tests__/view-assets.test.ts +25 -0
- package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
- package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
- package/src/lib/__tests__/view-open-intent.test.ts +0 -2
- package/src/lib/__tests__/view-server.test.ts +26 -3
- package/src/lib/__tests__/view-session.test.ts +2 -1
- package/src/lib/admin-instance.ts +14 -8
- package/src/lib/admin-target.ts +21 -2
- package/src/lib/binary.ts +22 -5
- package/src/lib/browser-retention.ts +210 -0
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/config.ts +12 -1
- package/src/lib/domain-publication.ts +9 -7
- package/src/lib/failure-debug.ts +26 -0
- package/src/lib/idp.ts +34 -3
- package/src/lib/instance-candidates.ts +2 -2
- package/src/lib/instance-target.ts +3 -14
- package/src/lib/instance.ts +16 -11
- package/src/lib/log.ts +16 -9
- package/src/lib/output.ts +1 -1
- package/src/lib/proc.ts +7 -2
- package/src/lib/provision-instance.ts +34 -57
- package/src/lib/skills/lock.ts +117 -0
- package/src/lib/skills/sync.ts +878 -0
- package/src/lib/skills.ts +1 -52
- package/src/lib/update.ts +298 -45
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/assets.ts +11 -2
- package/src/lib/view/external-open-intent.ts +41 -0
- package/src/lib/view/external-open-origins.ts +37 -0
- package/src/lib/view/host-capabilities.ts +17 -0
- package/src/lib/view/resolve.ts +1 -1
- package/src/lib/view/server.ts +18 -3
- package/src/lib/view/session.ts +2 -0
- package/src/program/.spec/api.d.ts +1 -0
- package/src/program/__tests__/program.test.ts +201 -7
- package/src/program/argv.ts +14 -0
- package/src/program/build.ts +26 -4
- package/src/program/command.ts +1 -0
- package/src/program/index.ts +1 -0
- package/src/program/registry.ts +2 -1
- package/src/setup/__tests__/instance-step.test.ts +27 -10
- package/src/setup/steps/instance.ts +22 -15
- package/src/setup/steps/skills.ts +40 -38
- package/src/state/.spec/api.d.ts +21 -2
- package/src/state/.spec/architecture.md +18 -4
- package/src/state/.spec/layout.ts +1 -0
- package/src/state/__tests__/exchange-credentials.test.ts +88 -42
- package/src/state/__tests__/files.test.ts +24 -1
- package/src/state/__tests__/fixtures/session-route-process.ts +93 -0
- package/src/state/__tests__/paths.test.ts +1 -0
- package/src/state/__tests__/session-routes.test.ts +138 -0
- package/src/state/exchange-credentials.ts +22 -9
- package/src/state/files.ts +41 -0
- package/src/state/index.ts +3 -1
- package/src/state/paths.ts +3 -0
- package/src/state/session-routes.ts +34 -0
- package/src/telemetry/__tests__/analyze-log.test.ts +38 -0
- package/src/telemetry/__tests__/recorder.test.ts +1 -0
- package/src/telemetry/__tests__/retention.test.ts +267 -0
- package/src/telemetry/__tests__/settings.test.ts +102 -0
- package/src/telemetry/__tests__/store-scan.test.ts +128 -0
- package/src/telemetry/__tests__/trigger.test.ts +33 -4
- package/src/telemetry/analyze.ts +24 -2
- package/src/telemetry/recorder.ts +15 -4
- package/src/telemetry/retention.ts +176 -0
- package/src/telemetry/session.ts +18 -12
- package/src/telemetry/settings.ts +64 -11
- package/src/telemetry/store.ts +92 -9
- package/src/telemetry/trigger.ts +16 -28
- package/src/ui/.spec/api.d.ts +14 -0
- package/src/ui/.spec/architecture.md +14 -0
- package/src/ui/.spec/laws.ts +50 -0
- package/src/ui/.spec/layout.ts +16 -0
- package/src/ui/__tests__/ui.test.ts +1742 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/lock.ts +89 -0
- package/src/ui/model.ts +83 -0
- package/src/ui/operations.ts +1044 -0
- package/src/ui/project.ts +333 -0
- package/src/ui/release.ts +398 -0
- package/src/ui/runner.ts +18 -0
- package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
- package/studio/client/dist/assets/index-BFVFs_8x.js +81 -0
- package/studio/client/dist/assets/index-DuB9iUdu.css +2 -0
- package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
- package/studio/client/dist/assets/schema-studio--a0kX3QU.css +1 -0
- package/studio/client/dist/assets/schema-studio-DH6oEWME.js +8 -0
- package/studio/client/dist/index.html +4 -3
- package/studio/package.json +8 -14
- package/studio/server/agent/ask.test.ts +1 -1
- package/studio/server/agent/bridge/grant.test.ts +1 -1
- package/studio/server/agent/bridge/routes.test.ts +1 -1
- package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
- package/studio/server/agent/harness/gateway/token.test.ts +1 -1
- package/studio/server/agent/harness/gateway/token.ts +3 -3
- package/studio/server/agent/prompts/anchors.test.ts +41 -32
- package/studio/server/agent/prompts/anchors.ts +23 -51
- package/studio/server/agent/prompts/system.test.ts +4 -4
- package/studio/server/agent/prompts/system.ts +5 -5
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/agent/run/preparation.ts +1 -1
- package/studio/server/api/canvas.ts +0 -1
- package/studio/server/api/context.ts +1 -1
- package/studio/server/api/deployment.ts +1 -1
- package/studio/server/api/views.ts +2 -2
- package/studio/server/api/workspace.ts +1 -1
- package/studio/server/api-agent-loadout.test.ts +1 -1
- package/studio/server/api.test.ts +1 -1
- package/studio/server/cache.test.ts +24 -18
- package/studio/server/cache.ts +10 -54
- package/studio/server/cli-consumers.test.ts +32 -4
- package/studio/server/cli.test.ts +2 -2
- package/studio/server/domain.test.ts +50 -68
- package/studio/server/domain.ts +82 -63
- package/studio/server/handoff/copy.ts +2 -2
- package/studio/server/index.ts +1 -1
- package/studio/server/instances/probe.test.ts +1 -1
- package/studio/server/introspect/anatomy/views/routes.ts +46 -37
- package/studio/server/introspect/anatomy/views.ts +10 -16
- package/studio/server/introspect/anatomy-extras.test.ts +29 -56
- package/studio/server/introspect/anatomy.test.ts +22 -14
- package/studio/server/introspect/anatomy.ts +6 -9
- package/studio/server/introspect/bundle.ts +0 -3
- package/studio/server/introspect/canonical-schema.test.ts +155 -424
- package/studio/server/introspect/canonical-schema.ts +248 -601
- package/studio/server/introspect/core.ts +14 -22
- package/studio/server/introspect/diff.test.ts +21 -9
- package/studio/server/introspect/diff.ts +19 -64
- package/studio/server/introspect/extractor.ts +23 -122
- package/studio/server/introspect/overlay-tsmorph.test.ts +116 -241
- package/studio/server/introspect/overlay-tsmorph.ts +0 -1
- package/studio/server/introspect/overlay.ts +3 -51
- package/studio/server/introspect/revision.test.ts +24 -28
- package/studio/server/introspect/revision.ts +11 -22
- package/studio/server/introspect/runtime.test.ts +77 -195
- package/studio/server/introspect/runtime.ts +5 -67
- package/studio/server/introspect/schema-ir-json.test.ts +81 -0
- package/studio/server/introspect/schema-ir-json.ts +39 -69
- package/studio/server/introspect/schema-refs.test.ts +43 -88
- package/studio/server/introspect/schema-refs.ts +14 -49
- package/studio/server/introspect/source-overlay/handlers.ts +116 -636
- package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
- package/studio/server/introspect/source-overlay/spans.ts +15 -45
- package/studio/server/lifecycle.ts +4 -1
- package/studio/server/state/baseline.test.ts +14 -9
- package/studio/server/state/baseline.ts +3 -5
- package/studio/server/state/documents.test.ts +69 -0
- package/studio/server/state/documents.ts +57 -10
- package/studio/server/state/visibility.ts +1 -5
- package/studio/server/views/model.ts +3 -3
- package/studio/server/views/target.test.ts +18 -76
- package/studio/server/views/target.ts +25 -51
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +8 -4
- package/studio/server/workspace/updates.ts +23 -1
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/runtime.ts +4 -0
- package/studio/shared/contracts/schema.ts +44 -131
- package/studio/shared/contracts/surface.test.ts +15 -17
- package/studio/shared/contracts/workspace.ts +5 -11
- package/studio/shared/schema/identity.test.ts +20 -44
- package/studio/shared/schema/identity.ts +9 -24
- package/studio/tsconfig.json +2 -1
- package/tsconfig.json +1 -1
- package/viewer/dist/index.html +21 -0
- package/viewer/dist/main.js +41 -62
- package/.check-workspace.cjs +0 -40
- package/src/admin/binding.ts +0 -168
- package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
- package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
- package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
- package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
- package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
- package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
- package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
- package/studio/server/introspect/core-extractor.ts +0 -139
- package/studio/server/introspect/overlay.test.ts +0 -72
- package/studio/server/introspect/source-overlay/annotations.ts +0 -14
package/src/commands/logs.ts
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
2
|
-
import {
|
|
2
|
+
import { K } from '@astrale-os/sdk/schema'
|
|
3
3
|
import chalk from 'chalk'
|
|
4
4
|
|
|
5
5
|
import type { ConnectionContext, KernelCommandOpts } from '../connection'
|
|
6
6
|
import type { Column, ListProjection } from '../lib/output'
|
|
7
7
|
import type { CommandDefinition } from '../program/index'
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
expandSelfInPath,
|
|
12
|
-
runKernelCommand,
|
|
13
|
-
withClientSession,
|
|
14
|
-
} from '../connection'
|
|
15
|
-
import { failClosed } from '../lib/log'
|
|
9
|
+
import { createPathCall, expandSelfInPath, runKernelCommand } from '../connection'
|
|
10
|
+
import { failInput } from '../lib/log'
|
|
16
11
|
import { isMachine, output, presentList } from '../lib/output'
|
|
17
12
|
|
|
18
|
-
const JOURNAL_PATH = Path.project(
|
|
13
|
+
const JOURNAL_PATH = Path.project(K.functions.journal.ref).raw
|
|
19
14
|
const DEFAULT_LIMIT = 200
|
|
20
15
|
const FOLLOW_INTERVAL_MS = 2_000
|
|
21
16
|
|
|
@@ -38,10 +33,21 @@ export interface JournalRecord {
|
|
|
38
33
|
readonly occurredAt?: string
|
|
39
34
|
readonly committedAt?: string
|
|
40
35
|
readonly principal?: string
|
|
36
|
+
readonly correlation?: JournalCorrelation
|
|
41
37
|
readonly correlationId?: string
|
|
42
38
|
readonly causationId?: string
|
|
43
39
|
}
|
|
44
40
|
|
|
41
|
+
export interface JournalCorrelation {
|
|
42
|
+
readonly operationId?: string
|
|
43
|
+
readonly parentOperationId?: string
|
|
44
|
+
readonly invocationId?: string
|
|
45
|
+
readonly rootInvocationId?: string
|
|
46
|
+
readonly parentInvocationId?: string
|
|
47
|
+
readonly traceId?: string
|
|
48
|
+
readonly spanId?: string
|
|
49
|
+
}
|
|
50
|
+
|
|
45
51
|
export interface JournalPage {
|
|
46
52
|
readonly records: readonly JournalRecord[]
|
|
47
53
|
readonly cursor?: string
|
|
@@ -127,7 +133,7 @@ async function runOnce(opts: LogsOpts): Promise<void> {
|
|
|
127
133
|
await runKernelCommand({
|
|
128
134
|
opts,
|
|
129
135
|
label: 'Kernel journal',
|
|
130
|
-
fn: (context) => fetchPage(context, opts),
|
|
136
|
+
fn: async (context) => fetchPage(context, await resolveLogsOpts(opts, context)),
|
|
131
137
|
format: (page, format) => {
|
|
132
138
|
if (isMachine(format) || format.format !== undefined) output(page, format)
|
|
133
139
|
else {
|
|
@@ -138,15 +144,33 @@ async function runOnce(opts: LogsOpts): Promise<void> {
|
|
|
138
144
|
})
|
|
139
145
|
}
|
|
140
146
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
type FollowDependencies = {
|
|
148
|
+
readonly run: typeof runKernelCommand
|
|
149
|
+
readonly pause: (milliseconds: number) => Promise<void>
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Follow one admitted journal stream. Dependencies are explicit so routing is proven at the command boundary. */
|
|
153
|
+
export async function followLogs(
|
|
154
|
+
opts: LogsOpts,
|
|
155
|
+
dependencies: FollowDependencies = {
|
|
156
|
+
run: runKernelCommand,
|
|
157
|
+
pause: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
|
|
158
|
+
},
|
|
159
|
+
): Promise<void> {
|
|
160
|
+
validateLogsOpts(opts)
|
|
161
|
+
await dependencies.run({
|
|
162
|
+
opts,
|
|
163
|
+
label: 'Kernel journal',
|
|
164
|
+
fn: async (context): Promise<never> => {
|
|
165
|
+
const resolved = await resolveLogsOpts(opts, context)
|
|
166
|
+
let cursor = resolved.cursor
|
|
167
|
+
for (;;) {
|
|
168
|
+
const page = await fetchPage(context, { ...resolved, cursor })
|
|
169
|
+
for (const record of page.records) printRecord(record, opts)
|
|
170
|
+
cursor = page.cursor ?? cursor
|
|
171
|
+
await dependencies.pause(FOLLOW_INTERVAL_MS)
|
|
172
|
+
}
|
|
173
|
+
},
|
|
150
174
|
})
|
|
151
175
|
}
|
|
152
176
|
|
|
@@ -169,12 +193,28 @@ function journalProjection(records: JournalRecord[]): ListProjection {
|
|
|
169
193
|
}
|
|
170
194
|
}
|
|
171
195
|
|
|
172
|
-
function printRecord(record: JournalRecord): void {
|
|
196
|
+
function printRecord(record: JournalRecord, opts: LogsOpts): void {
|
|
197
|
+
if (isMachine(opts) || opts.format === 'json') {
|
|
198
|
+
process.stdout.write(formatFollowRecord(record))
|
|
199
|
+
return
|
|
200
|
+
}
|
|
173
201
|
process.stdout.write(
|
|
174
202
|
`${chalk.dim(String(record.sequence).padStart(6))} ${chalk.dim(record.timestamp)} ${chalk.cyan(record.topic)} ${chalk.dim(record.principal ?? '')}\n`,
|
|
175
203
|
)
|
|
176
204
|
}
|
|
177
205
|
|
|
206
|
+
/** Machine follow is an NDJSON stream: one complete admitted record per line. */
|
|
207
|
+
export function formatFollowRecord(record: JournalRecord): string {
|
|
208
|
+
return `${JSON.stringify(record)}\n`
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function validateLogsOpts(opts: LogsOpts): void {
|
|
212
|
+
buildJournalInput(opts)
|
|
213
|
+
if (opts.follow && opts.format === 'yaml' && !opts.json && !opts.raw) {
|
|
214
|
+
throw new TypeError('--follow does not support YAML; use --json for an NDJSON stream')
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
178
218
|
function acceptRecord(input: unknown, index: number): JournalRecord {
|
|
179
219
|
if (
|
|
180
220
|
!isRecord(input) ||
|
|
@@ -188,10 +228,17 @@ function acceptRecord(input: unknown, index: number): JournalRecord {
|
|
|
188
228
|
if (timestamp === undefined) {
|
|
189
229
|
throw new TypeError(`Kernel journal record ${index} is missing occurredAt/timestamp`)
|
|
190
230
|
}
|
|
191
|
-
const correlation =
|
|
192
|
-
const correlationId
|
|
193
|
-
|
|
194
|
-
|
|
231
|
+
const correlation = acceptCorrelation(input.correlation, index)
|
|
232
|
+
const legacyCorrelationId = optionalIdentifier(input.correlationId, index, 'correlationId')
|
|
233
|
+
const structuredCorrelationId = correlation?.invocationId
|
|
234
|
+
if (
|
|
235
|
+
legacyCorrelationId !== undefined &&
|
|
236
|
+
structuredCorrelationId !== undefined &&
|
|
237
|
+
legacyCorrelationId !== structuredCorrelationId
|
|
238
|
+
) {
|
|
239
|
+
throw new TypeError(`Kernel journal record ${index} has conflicting correlation identifiers`)
|
|
240
|
+
}
|
|
241
|
+
const correlationId = structuredCorrelationId ?? legacyCorrelationId
|
|
195
242
|
const principal = optionalText(input.principal, index, 'principal')
|
|
196
243
|
return Object.freeze({
|
|
197
244
|
sequence: input.sequence as number,
|
|
@@ -203,13 +250,56 @@ function acceptRecord(input: unknown, index: number): JournalRecord {
|
|
|
203
250
|
? {}
|
|
204
251
|
: { committedAt: input.committedAt as string }),
|
|
205
252
|
...(principal === undefined ? {} : { principal }),
|
|
253
|
+
...(correlation === undefined ? {} : { correlation }),
|
|
206
254
|
...(correlationId === undefined ? {} : { correlationId }),
|
|
207
|
-
...(
|
|
255
|
+
...(optionalIdentifier(input.causationId, index, 'causationId') === undefined
|
|
208
256
|
? {}
|
|
209
257
|
: { causationId: input.causationId as string }),
|
|
210
258
|
})
|
|
211
259
|
}
|
|
212
260
|
|
|
261
|
+
const correlationFields = [
|
|
262
|
+
'operationId',
|
|
263
|
+
'parentOperationId',
|
|
264
|
+
'invocationId',
|
|
265
|
+
'rootInvocationId',
|
|
266
|
+
'parentInvocationId',
|
|
267
|
+
'traceId',
|
|
268
|
+
'spanId',
|
|
269
|
+
] as const
|
|
270
|
+
|
|
271
|
+
function acceptCorrelation(input: unknown, index: number): JournalCorrelation | undefined {
|
|
272
|
+
if (input === undefined) return undefined
|
|
273
|
+
if (!isRecord(input)) {
|
|
274
|
+
throw new TypeError(`Kernel journal record ${index}.correlation must be an object`)
|
|
275
|
+
}
|
|
276
|
+
const unknown = Object.keys(input).find(
|
|
277
|
+
(field) => !correlationFields.includes(field as (typeof correlationFields)[number]),
|
|
278
|
+
)
|
|
279
|
+
if (unknown !== undefined) {
|
|
280
|
+
throw new TypeError(`Kernel journal record ${index}.correlation.${unknown} is unsupported`)
|
|
281
|
+
}
|
|
282
|
+
return Object.freeze(
|
|
283
|
+
Object.fromEntries(
|
|
284
|
+
correlationFields.flatMap((field) => {
|
|
285
|
+
const value = optionalIdentifier(input[field], index, `correlation.${field}`)
|
|
286
|
+
return value === undefined ? [] : [[field, value]]
|
|
287
|
+
}),
|
|
288
|
+
),
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function optionalIdentifier(input: unknown, index: number, field: string): string | undefined {
|
|
293
|
+
const value = optionalText(input, index, field)
|
|
294
|
+
if (value === undefined) return undefined
|
|
295
|
+
if (value.trim() === '' || new TextEncoder().encode(value).byteLength > 256) {
|
|
296
|
+
throw new TypeError(
|
|
297
|
+
`Kernel journal record ${index}.${field} must be non-empty and at most 256 UTF-8 bytes`,
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
return value
|
|
301
|
+
}
|
|
302
|
+
|
|
213
303
|
function optionalText(input: unknown, index: number, field: string): string | undefined {
|
|
214
304
|
if (input === undefined) return undefined
|
|
215
305
|
if (typeof input !== 'string') {
|
|
@@ -227,11 +317,10 @@ function positiveInteger(flag: string, raw: string): number {
|
|
|
227
317
|
return value
|
|
228
318
|
}
|
|
229
319
|
|
|
230
|
-
async function
|
|
231
|
-
buildJournalInput(opts)
|
|
320
|
+
async function resolveLogsOpts(opts: LogsOpts, context: ConnectionContext): Promise<LogsOpts> {
|
|
232
321
|
const principal = nonEmpty(opts.principal)
|
|
233
322
|
if (principal !== '@self') return opts
|
|
234
|
-
const { path } = await expandSelfInPath('@self',
|
|
323
|
+
const { path } = await expandSelfInPath('@self', context)
|
|
235
324
|
return { ...opts, principal: path.startsWith('@') ? path.slice(1) : path }
|
|
236
325
|
}
|
|
237
326
|
|
|
@@ -252,10 +341,8 @@ Behavior:
|
|
|
252
341
|
Calls the public Kernel journal syscall and emits its { records, cursor }
|
|
253
342
|
page. Topic selection is exact or prefix-based; cursors and timestamps are
|
|
254
343
|
opaque strings owned by the journal backend. --follow reuses one Client Session
|
|
255
|
-
and advances only with the returned cursor.
|
|
256
|
-
|
|
257
|
-
Historical event-glob lowering and the application-specific services-domain
|
|
258
|
-
log buffer are not part of the Kernel V2 journal contract.
|
|
344
|
+
and advances only with the returned cursor. With --json, follow output is
|
|
345
|
+
NDJSON with one complete admitted record per line; YAML follow is unsupported.
|
|
259
346
|
|
|
260
347
|
Examples:
|
|
261
348
|
$ astrale logs -i staging --limit 50
|
|
@@ -274,11 +361,11 @@ Examples:
|
|
|
274
361
|
],
|
|
275
362
|
action: async (opts: LogsOpts) => {
|
|
276
363
|
try {
|
|
277
|
-
|
|
278
|
-
if (prepared.follow) await follow(prepared)
|
|
279
|
-
else await runOnce(prepared)
|
|
364
|
+
validateLogsOpts(opts)
|
|
280
365
|
} catch (error) {
|
|
281
|
-
|
|
366
|
+
failInput(error, opts)
|
|
282
367
|
}
|
|
368
|
+
if (opts.follow) await followLogs(opts)
|
|
369
|
+
else await runOnce(opts)
|
|
283
370
|
},
|
|
284
371
|
} satisfies CommandDefinition
|
package/src/commands/mutate.ts
CHANGED
|
@@ -7,8 +7,9 @@ import type { KernelCommandOpts } from '../connection'
|
|
|
7
7
|
import type { CommandDefinition } from '../program/index'
|
|
8
8
|
|
|
9
9
|
import { runKernelCommand } from '../connection'
|
|
10
|
+
import { AstraleError } from '../errors'
|
|
10
11
|
import { prepareMutation } from '../graph/index'
|
|
11
|
-
import {
|
|
12
|
+
import { failInput } from '../lib/log'
|
|
12
13
|
import { output } from '../lib/output'
|
|
13
14
|
import { renderTable } from '../lib/table'
|
|
14
15
|
|
|
@@ -23,7 +24,7 @@ export async function mutateCommand(opts: MutateOpts): Promise<void> {
|
|
|
23
24
|
try {
|
|
24
25
|
mutation = prepareMutation(await readDocument(opts))
|
|
25
26
|
} catch (error) {
|
|
26
|
-
|
|
27
|
+
failInput(error, opts)
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
if (opts.dry) {
|
|
@@ -51,9 +52,9 @@ async function readDocument(opts: MutateOpts): Promise<unknown> {
|
|
|
51
52
|
try {
|
|
52
53
|
raw = await readFile(opts.file, 'utf8')
|
|
53
54
|
} catch (error) {
|
|
54
|
-
throw new
|
|
55
|
-
|
|
56
|
-
)
|
|
55
|
+
throw new AstraleError('FILE_READ_FAILED', `Cannot read --file ${opts.file}.`, undefined, {
|
|
56
|
+
cause: error,
|
|
57
|
+
})
|
|
57
58
|
}
|
|
58
59
|
return parseJson(raw, opts.file)
|
|
59
60
|
}
|
package/src/commands/query.ts
CHANGED
|
@@ -6,14 +6,15 @@ import type { KernelCommandOpts, SelfExpansionMeta } from '../connection'
|
|
|
6
6
|
import type { CommandDefinition } from '../program/index'
|
|
7
7
|
|
|
8
8
|
import { expandSelfInPath, runKernelCommand, withSelfHint } from '../connection'
|
|
9
|
+
import { AstraleError } from '../errors'
|
|
9
10
|
import { prepareQuery, type QueryCommandInput } from '../graph/index'
|
|
10
|
-
import {
|
|
11
|
+
import { failInput } from '../lib/log'
|
|
11
12
|
import { isMachine, output } from '../lib/output'
|
|
12
13
|
|
|
13
14
|
type QueryOpts = KernelCommandOpts & {
|
|
14
15
|
ast?: string
|
|
15
16
|
file?: string
|
|
16
|
-
|
|
17
|
+
class?: string
|
|
17
18
|
edge?: string
|
|
18
19
|
direction?: QueryDirection
|
|
19
20
|
limit?: string
|
|
@@ -22,48 +23,49 @@ type QueryOpts = KernelCommandOpts & {
|
|
|
22
23
|
|
|
23
24
|
export async function queryCommand(sources: string[], opts: QueryOpts): Promise<void> {
|
|
24
25
|
let input: QueryCommandInput
|
|
25
|
-
let meta: SelfExpansionMeta | undefined
|
|
26
26
|
try {
|
|
27
27
|
const ast = await readAst(opts)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
cursor: opts.cursor,
|
|
37
|
-
}
|
|
38
|
-
meta = expanded.find((entry) => entry.meta !== undefined)?.meta
|
|
39
|
-
} else {
|
|
40
|
-
input = {
|
|
41
|
-
sources,
|
|
42
|
-
ast,
|
|
43
|
-
definition: opts.definition,
|
|
44
|
-
edge: opts.edge,
|
|
45
|
-
direction: opts.direction,
|
|
46
|
-
limit: opts.limit,
|
|
47
|
-
cursor: opts.cursor,
|
|
48
|
-
}
|
|
28
|
+
input = {
|
|
29
|
+
sources,
|
|
30
|
+
...(ast === undefined ? {} : { ast }),
|
|
31
|
+
class: opts.class,
|
|
32
|
+
edge: opts.edge,
|
|
33
|
+
direction: opts.direction,
|
|
34
|
+
limit: opts.limit,
|
|
35
|
+
cursor: opts.cursor,
|
|
49
36
|
}
|
|
50
37
|
} catch (error) {
|
|
51
|
-
|
|
38
|
+
failInput(error, opts)
|
|
52
39
|
}
|
|
53
40
|
|
|
54
41
|
let prepared
|
|
55
42
|
try {
|
|
56
43
|
prepared = prepareQuery(input)
|
|
57
44
|
} catch (error) {
|
|
58
|
-
|
|
45
|
+
failInput(error, opts)
|
|
59
46
|
}
|
|
60
47
|
|
|
61
48
|
await runKernelCommand({
|
|
62
49
|
opts,
|
|
63
50
|
label: 'Query',
|
|
64
|
-
fn:
|
|
51
|
+
fn: async (context) => {
|
|
52
|
+
const expanded = await Promise.all(sources.map((source) => expandSelfInPath(source, context)))
|
|
53
|
+
const meta: SelfExpansionMeta | undefined = expanded.find(
|
|
54
|
+
(entry) => entry.meta !== undefined,
|
|
55
|
+
)?.meta
|
|
56
|
+
const resolved =
|
|
57
|
+
meta === undefined
|
|
58
|
+
? prepared
|
|
59
|
+
: prepareQuery({ ...input, sources: expanded.map(({ path }) => path) })
|
|
60
|
+
return withSelfHint(() => context.graph.query(resolved.ast, { page: resolved.page }), meta)
|
|
61
|
+
},
|
|
65
62
|
format: (response, format) => {
|
|
66
|
-
output(
|
|
63
|
+
output(
|
|
64
|
+
response.page.next === undefined
|
|
65
|
+
? response.result
|
|
66
|
+
: { ...response.result, page: { next: response.page.next } },
|
|
67
|
+
format,
|
|
68
|
+
)
|
|
67
69
|
if (response.page.next && !isMachine(format)) {
|
|
68
70
|
process.stderr.write(` cursor: ${response.page.next}\n`)
|
|
69
71
|
}
|
|
@@ -81,9 +83,9 @@ async function readAst(opts: QueryOpts): Promise<unknown | undefined> {
|
|
|
81
83
|
try {
|
|
82
84
|
raw = await readFile(opts.file, 'utf8')
|
|
83
85
|
} catch (error) {
|
|
84
|
-
throw new
|
|
85
|
-
|
|
86
|
-
)
|
|
86
|
+
throw new AstraleError('FILE_READ_FAILED', `Cannot read --file ${opts.file}.`, undefined, {
|
|
87
|
+
cause: error,
|
|
88
|
+
})
|
|
87
89
|
}
|
|
88
90
|
return parseJson(raw, opts.file)
|
|
89
91
|
}
|
|
@@ -101,18 +103,19 @@ export default {
|
|
|
101
103
|
description: 'Run one canonical Query V6 graph read',
|
|
102
104
|
afterHelpText: `
|
|
103
105
|
Behavior:
|
|
104
|
-
Positional Paths and --
|
|
106
|
+
Positional Paths and --class author a finite Query V6 read. --edge adds
|
|
105
107
|
one exact Edge-Class expansion; --direction defaults to outgoing. --ast and
|
|
106
108
|
--file accept a complete canonical astrale.graph.query/v6 document, including
|
|
107
109
|
Property ordering and Node or Edge reference/value projections. --cursor resumes
|
|
108
|
-
one caller-bound query scope.
|
|
110
|
+
one caller-bound query scope. Machine output adds page.next only when another
|
|
111
|
+
page exists; pass that opaque value back through --cursor.
|
|
109
112
|
|
|
110
113
|
Legacy depth/children selector JSON and raw Cypher are not portable Kernel
|
|
111
114
|
V2 query contracts and are not accepted.
|
|
112
115
|
|
|
113
116
|
Examples:
|
|
114
117
|
$ astrale query /:notes.example.dev:class.Note --limit 50
|
|
115
|
-
$ astrale query --
|
|
118
|
+
$ astrale query --class /:notes.example.dev:class.Note --limit 50
|
|
116
119
|
$ astrale query @note --edge /:notes.example.dev:class.references --direction outgoing --limit 25
|
|
117
120
|
$ astrale query --file query.v6.json --cursor "$CURSOR"
|
|
118
121
|
`,
|
|
@@ -121,8 +124,8 @@ Examples:
|
|
|
121
124
|
{ flags: '--ast <json>', description: 'Canonical Query V6 JSON document' },
|
|
122
125
|
{ flags: '-f, --file <path>', description: 'Read a canonical Query V6 document from a file' },
|
|
123
126
|
{
|
|
124
|
-
flags: '--
|
|
125
|
-
description: 'Select Nodes
|
|
127
|
+
flags: '--class <path>',
|
|
128
|
+
description: 'Select Nodes satisfying one exact Class',
|
|
126
129
|
},
|
|
127
130
|
{ flags: '--edge <class>', description: 'Expand one exact Edge Class' },
|
|
128
131
|
{
|
|
@@ -4,13 +4,23 @@ import type { CommandDefinition } from '../../program/index'
|
|
|
4
4
|
|
|
5
5
|
import { log } from '../../lib/log'
|
|
6
6
|
import { analyzeSession } from '../../telemetry/analyze'
|
|
7
|
-
import {
|
|
7
|
+
import { sweepStore } from '../../telemetry/retention'
|
|
8
|
+
import { scanSessions } from '../../telemetry/store'
|
|
8
9
|
import { restampLock, releaseLock } from '../../telemetry/trigger'
|
|
9
10
|
|
|
10
11
|
export default {
|
|
11
12
|
name: 'analyze',
|
|
12
13
|
description: 'Analyze a recorded session for DX frictions (dry-run: writes report.md)',
|
|
13
|
-
arguments: [
|
|
14
|
+
arguments: [
|
|
15
|
+
// Optional: the action resolves the most recent closed, unanalyzed session
|
|
16
|
+
// when no id is given, which is how a human is meant to invoke this. The
|
|
17
|
+
// opportunistic trigger always passes an explicit id.
|
|
18
|
+
{
|
|
19
|
+
name: 'id',
|
|
20
|
+
description: 'Session id (default: most recent closed, unanalyzed)',
|
|
21
|
+
required: false,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
14
24
|
options: [
|
|
15
25
|
{ flags: '--file', description: 'File cleared findings as issues on the admin tracker' },
|
|
16
26
|
{ flags: '--model <model>', description: 'Model for the analyzer pass' },
|
|
@@ -22,10 +32,10 @@ export default {
|
|
|
22
32
|
opts: { file?: boolean; model?: string; force?: boolean; auto?: boolean },
|
|
23
33
|
) => {
|
|
24
34
|
if (opts.auto) restampLock()
|
|
35
|
+
let target = id
|
|
25
36
|
try {
|
|
26
|
-
let target = id
|
|
27
37
|
if (!target) {
|
|
28
|
-
const candidate =
|
|
38
|
+
const candidate = scanSessions().find((s) => s.closed && (opts.force || !s.analyzed))
|
|
29
39
|
if (!candidate) {
|
|
30
40
|
if (!opts.auto) log.dim('Nothing to analyze — no closed, unanalyzed session.')
|
|
31
41
|
return
|
|
@@ -45,6 +55,18 @@ export default {
|
|
|
45
55
|
}
|
|
46
56
|
} finally {
|
|
47
57
|
if (opts.auto) releaseLock()
|
|
58
|
+
// The size bound is enforced here, not on the CLI's critical path:
|
|
59
|
+
// measuring every file of every session is only affordable in this
|
|
60
|
+
// process, and this is precisely where the store just grew. The session
|
|
61
|
+
// just analyzed is protected — it is the freshest evidence on disk.
|
|
62
|
+
try {
|
|
63
|
+
const swept = sweepStore({ protect: new Set(target === undefined ? [] : [target]) })
|
|
64
|
+
if (!opts.auto && swept.removed.length > 0) {
|
|
65
|
+
log.dim(`retention: removed ${swept.removed.length} session(s)`)
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
/* retention must never fail the command that triggered it */
|
|
69
|
+
}
|
|
48
70
|
}
|
|
49
71
|
},
|
|
50
72
|
} satisfies CommandDefinition
|
package/src/commands/token.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { issuer } from '@astrale-os/sdk/auth'
|
|
1
|
+
import { issuer, type AuthApi, type IssuerId, type MintedCredential } from '@astrale-os/sdk/auth'
|
|
2
2
|
|
|
3
3
|
import type { KernelCommandOpts } from '../connection'
|
|
4
4
|
import type { CommandDefinition } from '../program/index'
|
|
@@ -6,12 +6,12 @@ import type { CommandDefinition } from '../program/index'
|
|
|
6
6
|
import { runKernelCommand } from '../connection'
|
|
7
7
|
import { AstraleError } from '../errors'
|
|
8
8
|
import { decodeJwtExpiration } from '../lib/local-status'
|
|
9
|
-
import {
|
|
9
|
+
import { failInput, log } from '../lib/log'
|
|
10
10
|
import { output } from '../lib/output'
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* `astrale token` — mint a fresh
|
|
14
|
-
*
|
|
13
|
+
* `astrale token` — mint a fresh audience-bound token for the active instance
|
|
14
|
+
* and selected identity through the bound AuthApi.
|
|
15
15
|
*/
|
|
16
16
|
export type TokenOpts = KernelCommandOpts & {
|
|
17
17
|
audience?: string
|
|
@@ -21,23 +21,25 @@ export type TokenOpts = KernelCommandOpts & {
|
|
|
21
21
|
for?: string
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
const DEFAULT_TOKEN_TTL_SECONDS = 4 * 60
|
|
25
|
+
|
|
24
26
|
export async function tokenCommand(opts: TokenOpts): Promise<void> {
|
|
25
27
|
const commandOpts: TokenOpts = opts.for && !opts.as ? { ...opts, as: opts.for } : opts
|
|
26
|
-
|
|
28
|
+
let ttl: number
|
|
29
|
+
try {
|
|
30
|
+
ttl = parseTtl(commandOpts.ttl)
|
|
31
|
+
} catch (error) {
|
|
32
|
+
failInput(error, opts)
|
|
33
|
+
}
|
|
27
34
|
await runKernelCommand<string>({
|
|
28
35
|
opts: commandOpts,
|
|
29
|
-
label: 'Minting
|
|
36
|
+
label: 'Minting token',
|
|
30
37
|
fn: async (ctx) => {
|
|
31
38
|
const audience =
|
|
32
39
|
commandOpts.audience === undefined
|
|
33
40
|
? ctx.target.kernelIssuer
|
|
34
41
|
: issuer.accept(commandOpts.audience)
|
|
35
|
-
|
|
36
|
-
return ctx.auth.delegate(self.id, {
|
|
37
|
-
audience,
|
|
38
|
-
ttlSeconds: ttl,
|
|
39
|
-
attenuation: { kind: 'identity', self: true },
|
|
40
|
-
})
|
|
42
|
+
return issueToken(ctx.auth, ctx.target.kernelIssuer, audience, ttl)
|
|
41
43
|
},
|
|
42
44
|
format: (token, fmtOpts) => {
|
|
43
45
|
if (fmtOpts.json || fmtOpts.format !== undefined) {
|
|
@@ -45,15 +47,31 @@ export async function tokenCommand(opts: TokenOpts): Promise<void> {
|
|
|
45
47
|
return
|
|
46
48
|
}
|
|
47
49
|
if (!fmtOpts.raw && (process.stdout.isTTY ?? false)) {
|
|
48
|
-
log.dim(' (
|
|
50
|
+
log.dim(' (audience-bound token — ES256, self-identity)')
|
|
49
51
|
}
|
|
50
52
|
process.stdout.write(`${token}\n`)
|
|
51
53
|
},
|
|
52
54
|
})
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
/** Issue either a top-level Kernel credential or an external-audience delegation. */
|
|
58
|
+
export async function issueToken(
|
|
59
|
+
auth: Pick<AuthApi, 'delegate' | 'mint' | 'whoami'>,
|
|
60
|
+
kernel: IssuerId,
|
|
61
|
+
audience: IssuerId,
|
|
62
|
+
ttlSeconds: number,
|
|
63
|
+
): Promise<MintedCredential> {
|
|
64
|
+
if (audience === kernel) return auth.mint({ ttlSeconds })
|
|
65
|
+
const self = await auth.whoami()
|
|
66
|
+
return auth.delegate(self.id, {
|
|
67
|
+
audience,
|
|
68
|
+
ttlSeconds,
|
|
69
|
+
attenuation: { kind: 'identity', self: true },
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
55
73
|
export function parseTtl(raw: string | undefined): number {
|
|
56
|
-
if (raw === undefined) return
|
|
74
|
+
if (raw === undefined) return DEFAULT_TOKEN_TTL_SECONDS
|
|
57
75
|
if (!/^\d+$/.test(raw)) {
|
|
58
76
|
throw new AstraleError(
|
|
59
77
|
'INVALID_FLAG',
|
|
@@ -72,16 +90,17 @@ export function parseTtl(raw: string | undefined): number {
|
|
|
72
90
|
|
|
73
91
|
export default {
|
|
74
92
|
name: 'token',
|
|
75
|
-
description: 'Mint a fresh
|
|
93
|
+
description: 'Mint a fresh audience-bound credential for the active instance + identity',
|
|
76
94
|
afterHelpText: `
|
|
77
95
|
Behavior:
|
|
78
|
-
|
|
79
|
-
The default audience
|
|
80
|
-
|
|
96
|
+
Mints a token for the selected authenticated identity for 240 seconds by default.
|
|
97
|
+
The default Kernel audience produces a top-level Grant credential reusable with
|
|
98
|
+
--creds. A different --audience produces a delegated service envelope. --for is
|
|
99
|
+
an alias of --as.
|
|
81
100
|
|
|
82
|
-
|
|
83
|
-
be
|
|
84
|
-
|
|
101
|
+
Every receiver must admit the exact token audience. A service-audience token can
|
|
102
|
+
be sent as a Bearer token only to that service's authenticated endpoint. The
|
|
103
|
+
requested lifetime cannot exceed the selected source credential's remaining life.
|
|
85
104
|
|
|
86
105
|
Examples:
|
|
87
106
|
$ export TOKEN=$(astrale token --audience shell.astrale.ai --raw)
|
|
@@ -93,14 +112,10 @@ Examples:
|
|
|
93
112
|
flags: '--audience <aud>',
|
|
94
113
|
description: 'Token audience (default: target Kernel issuer)',
|
|
95
114
|
},
|
|
96
|
-
{ flags: '--ttl <sec>', description: 'TTL in seconds (default:
|
|
115
|
+
{ flags: '--ttl <sec>', description: 'TTL in seconds (default: 240)' },
|
|
97
116
|
{ flags: '--for <identity>', description: 'Mint the token for this identity (alias of --as)' },
|
|
98
117
|
],
|
|
99
118
|
action: async (opts) => {
|
|
100
|
-
|
|
101
|
-
await tokenCommand(opts as Parameters<typeof tokenCommand>[0])
|
|
102
|
-
} catch (error) {
|
|
103
|
-
failClosed(error, opts as TokenOpts)
|
|
104
|
-
}
|
|
119
|
+
await tokenCommand(opts as Parameters<typeof tokenCommand>[0])
|
|
105
120
|
},
|
|
106
121
|
} satisfies CommandDefinition
|