@astrale-os/cli 1.0.0-beta.2 → 1.0.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -1
- package/dist/astrale.js +51105 -58057
- package/dist/public/connect-core.js +2085 -30702
- package/dist/public/keys/index.js +884 -29476
- package/dist/public/paths/index.js +13101 -41452
- package/dist/types/admin/contract.d.ts +26 -0
- package/dist/types/admin/instance/client.d.ts +20 -0
- package/dist/types/admin/instance/index.d.ts +2 -0
- package/dist/types/admin/instance/model.d.ts +6 -10
- package/dist/types/connection/auth.d.ts +3 -0
- package/dist/types/connection/call.d.ts +3 -0
- package/dist/types/connection/command.d.ts +27 -0
- package/dist/types/connection/credential.d.ts +14 -0
- package/dist/types/connection/errors.d.ts +1 -0
- package/dist/types/connection/exchange.d.ts +9 -0
- package/dist/types/connection/failure/classify.d.ts +2 -0
- package/dist/types/connection/failure/index.d.ts +5 -0
- package/dist/types/connection/failure/model.d.ts +25 -0
- package/dist/types/connection/failure/render.d.ts +3 -0
- package/dist/types/connection/index.d.ts +8 -0
- package/dist/types/connection/reasons.d.ts +37 -0
- package/dist/types/connection/self.d.ts +37 -0
- package/dist/types/connection/session.d.ts +31 -0
- package/dist/types/connection/target.d.ts +38 -0
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/lib/admin-instance.d.ts +17 -0
- package/dist/types/lib/admin-target.d.ts +11 -36
- package/dist/types/lib/config.d.ts +7 -59
- package/dist/types/lib/failure-debug.d.ts +1 -0
- package/dist/types/lib/idp.d.ts +25 -173
- package/dist/types/lib/instance-target.d.ts +1 -1
- package/dist/types/lib/instance.d.ts +30 -88
- package/dist/types/lib/log.d.ts +5 -3
- package/dist/types/lib/output.d.ts +1 -1
- package/dist/types/lib/proc.d.ts +43 -0
- package/dist/types/lib/self.d.ts +9 -0
- package/dist/types/lib/update.d.ts +109 -0
- package/dist/types/lib/validation.d.ts +4 -1
- package/package.json +15 -20
- package/src/__tests__/fixtures/publication.ts +20 -0
- package/src/admin/.spec/architecture.md +12 -5
- package/src/admin/__tests__/fixture.ts +25 -0
- package/src/admin/catalog/.spec/api.d.ts +1 -3
- package/src/admin/catalog/.spec/architecture.md +5 -4
- package/src/admin/catalog/__tests__/client.test.ts +143 -69
- package/src/admin/catalog/client.ts +40 -54
- package/src/admin/catalog/model.ts +3 -4
- package/src/admin/contract.ts +46 -0
- package/src/admin/graph/.spec/api.d.ts +4 -10
- package/src/admin/graph/nodes.ts +1 -1
- package/src/admin/instance/.spec/api.d.ts +5 -9
- package/src/admin/instance/.spec/architecture.md +7 -7
- package/src/admin/instance/__tests__/client.test.ts +157 -175
- package/src/admin/instance/client.ts +39 -128
- package/src/admin/instance/index.ts +1 -2
- package/src/admin/instance/model.ts +12 -18
- package/src/commands/__tests__/admin-instance.test.ts +11 -8
- package/src/commands/__tests__/call-describe.test.ts +46 -33
- package/src/commands/__tests__/call.test.ts +34 -0
- package/src/commands/__tests__/domain-install-operation.test.ts +7 -22
- package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
- package/src/commands/__tests__/domain-list.test.ts +5 -25
- package/src/commands/__tests__/install-direct.test.ts +115 -8
- package/src/commands/__tests__/install-identity-override.test.ts +3 -14
- package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
- package/src/commands/__tests__/instance-status.test.ts +139 -0
- package/src/commands/__tests__/introspect-parse.test.ts +80 -1
- package/src/commands/__tests__/logs.test.ts +250 -2
- package/src/commands/__tests__/read-commands.test.ts +62 -10
- package/src/commands/__tests__/token-ttl.test.ts +49 -4
- package/src/commands/__tests__/update.test.ts +25 -3
- package/src/commands/__tests__/view.test.ts +49 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +71 -59
- package/src/commands/domain/install.ts +54 -35
- package/src/commands/domain/list.ts +1 -1
- package/src/commands/domain/publish.ts +14 -11
- package/src/commands/domain/uninstall.ts +2 -2
- package/src/commands/get.ts +14 -15
- package/src/commands/identity/__tests__/register.test.ts +3 -3
- package/src/commands/identity/register.ts +5 -4
- package/src/commands/instance/create.ts +8 -16
- package/src/commands/instance/delete.ts +12 -9
- package/src/commands/instance/list.ts +2 -2
- package/src/commands/instance/status.ts +66 -20
- package/src/commands/instance/use.ts +16 -12
- package/src/commands/introspect.ts +48 -35
- package/src/commands/logs.ts +123 -33
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +35 -32
- package/src/commands/token.ts +42 -27
- package/src/commands/ui/__tests__/commands.test.ts +152 -0
- package/src/commands/ui/add.ts +51 -0
- package/src/commands/ui/doctor.ts +13 -0
- package/src/commands/ui/init.ts +38 -0
- package/src/commands/ui/list.ts +26 -0
- package/src/commands/ui/preset-apply.ts +19 -0
- package/src/commands/ui/preset-list.ts +12 -0
- package/src/commands/ui/shared.ts +25 -0
- package/src/commands/update.ts +31 -11
- package/src/commands/view.ts +82 -37
- package/src/connection/.spec/api.d.ts +7 -8
- package/src/connection/.spec/architecture.md +5 -4
- package/src/connection/.spec/flows/session.ts +2 -2
- package/src/connection/.spec/laws/connection.ts +6 -2
- package/src/connection/.spec/layout.ts +1 -0
- package/src/connection/__tests__/credential.test.ts +20 -1
- package/src/connection/__tests__/errors.test.ts +27 -20
- package/src/connection/__tests__/exchange.test.ts +30 -1
- package/src/connection/__tests__/failure-fixtures.ts +39 -0
- package/src/connection/__tests__/failure-safety.test.ts +75 -0
- package/src/connection/__tests__/self.test.ts +31 -11
- package/src/connection/__tests__/session.test.ts +3 -3
- package/src/connection/__tests__/target.test.ts +1 -0
- package/src/connection/auth.ts +13 -0
- package/src/connection/call.ts +1 -1
- package/src/connection/credential.ts +2 -2
- package/src/connection/errors.ts +1 -355
- package/src/connection/exchange.ts +47 -23
- package/src/connection/failure/classify.ts +110 -0
- package/src/connection/failure/index.ts +18 -0
- package/src/connection/failure/model.ts +28 -0
- package/src/connection/failure/render.ts +116 -0
- package/src/connection/reasons.ts +16 -0
- package/src/connection/self.ts +39 -21
- package/src/connection/session.ts +19 -8
- package/src/errors.ts +3 -2
- package/src/graph/.spec/api.d.ts +12 -5
- package/src/graph/.spec/layout.ts +9 -1
- package/src/graph/__tests__/mutation.test.ts +2 -2
- package/src/graph/__tests__/query.test.ts +25 -6
- package/src/graph/class.ts +30 -0
- package/src/graph/index.ts +1 -0
- package/src/graph/query.ts +21 -32
- package/src/identity/.spec/api.d.ts +2 -2
- package/src/identity/__tests__/registration.test.ts +1 -1
- package/src/identity/registration.ts +3 -3
- package/src/keys/__tests__/keys.test.ts +4 -0
- package/src/keys/credential.ts +1 -0
- package/src/lib/__tests__/admin-target.test.ts +12 -0
- package/src/lib/__tests__/binary.test.ts +16 -1
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/domain-publication.test.ts +10 -16
- package/src/lib/__tests__/idp.test.ts +34 -4
- package/src/lib/__tests__/instance-candidates.test.ts +1 -3
- package/src/lib/__tests__/instance-target.test.ts +7 -39
- package/src/lib/__tests__/log-errors.test.ts +64 -0
- package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
- package/src/lib/__tests__/update.test.ts +264 -11
- package/src/lib/__tests__/view-assets.test.ts +14 -0
- package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
- package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
- package/src/lib/__tests__/view-open-intent.test.ts +0 -2
- package/src/lib/__tests__/view-server.test.ts +26 -3
- package/src/lib/__tests__/view-session.test.ts +2 -1
- package/src/lib/admin-instance.ts +14 -8
- package/src/lib/admin-target.ts +21 -2
- package/src/lib/binary.ts +22 -5
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/domain-publication.ts +9 -7
- package/src/lib/failure-debug.ts +26 -0
- package/src/lib/idp.ts +34 -3
- package/src/lib/instance-candidates.ts +2 -2
- package/src/lib/instance-target.ts +3 -14
- package/src/lib/instance.ts +16 -11
- package/src/lib/log.ts +16 -9
- package/src/lib/output.ts +1 -1
- package/src/lib/proc.ts +7 -2
- package/src/lib/provision-instance.ts +34 -57
- package/src/lib/update.ts +298 -45
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/assets.ts +11 -2
- package/src/lib/view/external-open-intent.ts +41 -0
- package/src/lib/view/external-open-origins.ts +37 -0
- package/src/lib/view/host-capabilities.ts +17 -0
- package/src/lib/view/resolve.ts +1 -1
- package/src/lib/view/server.ts +18 -3
- package/src/lib/view/session.ts +2 -0
- package/src/program/.spec/api.d.ts +1 -0
- package/src/program/__tests__/program.test.ts +163 -6
- package/src/program/argv.ts +14 -0
- package/src/program/build.ts +26 -4
- package/src/program/command.ts +1 -0
- package/src/program/index.ts +1 -0
- package/src/program/registry.ts +2 -1
- package/src/setup/__tests__/instance-step.test.ts +27 -10
- package/src/setup/steps/instance.ts +22 -15
- package/src/telemetry/__tests__/recorder.test.ts +1 -0
- package/src/telemetry/recorder.ts +9 -1
- package/src/ui/.spec/api.d.ts +14 -0
- package/src/ui/.spec/architecture.md +10 -0
- package/src/ui/.spec/laws.ts +36 -0
- package/src/ui/.spec/layout.ts +16 -0
- package/src/ui/__tests__/ui.test.ts +981 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/lock.ts +87 -0
- package/src/ui/model.ts +83 -0
- package/src/ui/operations.ts +710 -0
- package/src/ui/project.ts +219 -0
- package/src/ui/release.ts +321 -0
- package/src/ui/runner.ts +18 -0
- package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
- package/studio/client/dist/assets/index-7YHdeCxp.js +81 -0
- package/studio/client/dist/assets/index-C0FAnwNw.css +2 -0
- package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
- package/studio/client/dist/assets/schema-studio-BilUNb6Z.css +1 -0
- package/studio/client/dist/assets/schema-studio-CABpTfvH.js +8 -0
- package/studio/client/dist/index.html +3 -2
- package/studio/package.json +7 -13
- package/studio/server/agent/ask.test.ts +1 -1
- package/studio/server/agent/bridge/grant.test.ts +1 -1
- package/studio/server/agent/bridge/routes.test.ts +1 -1
- package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
- package/studio/server/agent/harness/gateway/token.test.ts +1 -1
- package/studio/server/agent/harness/gateway/token.ts +3 -3
- package/studio/server/agent/prompts/anchors.test.ts +32 -36
- package/studio/server/agent/prompts/anchors.ts +20 -49
- package/studio/server/agent/prompts/system.test.ts +3 -2
- package/studio/server/agent/prompts/system.ts +3 -3
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/api/canvas.ts +0 -1
- package/studio/server/api-agent-loadout.test.ts +1 -1
- package/studio/server/api.test.ts +1 -1
- package/studio/server/cache.test.ts +23 -12
- package/studio/server/cache.ts +12 -15
- package/studio/server/domain.test.ts +50 -68
- package/studio/server/domain.ts +82 -63
- package/studio/server/handoff/copy.ts +1 -1
- package/studio/server/index.ts +1 -1
- package/studio/server/instances/probe.test.ts +1 -1
- package/studio/server/introspect/anatomy/views/routes.ts +46 -37
- package/studio/server/introspect/anatomy/views.ts +9 -14
- package/studio/server/introspect/anatomy-extras.test.ts +26 -42
- package/studio/server/introspect/anatomy.test.ts +22 -14
- package/studio/server/introspect/anatomy.ts +6 -9
- package/studio/server/introspect/bundle.ts +0 -3
- package/studio/server/introspect/canonical-schema.test.ts +94 -389
- package/studio/server/introspect/canonical-schema.ts +227 -457
- package/studio/server/introspect/core-extractor.ts +18 -127
- package/studio/server/introspect/core.ts +1 -2
- package/studio/server/introspect/diff.test.ts +11 -7
- package/studio/server/introspect/diff.ts +18 -55
- package/studio/server/introspect/extractor.ts +22 -117
- package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
- package/studio/server/introspect/overlay.test.ts +26 -54
- package/studio/server/introspect/overlay.ts +10 -37
- package/studio/server/introspect/revision.test.ts +1 -1
- package/studio/server/introspect/revision.ts +1 -1
- package/studio/server/introspect/runtime.test.ts +65 -183
- package/studio/server/introspect/runtime.ts +8 -25
- package/studio/server/introspect/schema-ir-json.test.ts +70 -0
- package/studio/server/introspect/schema-ir-json.ts +38 -68
- package/studio/server/introspect/schema-refs.test.ts +43 -88
- package/studio/server/introspect/schema-refs.ts +14 -49
- package/studio/server/introspect/source-overlay/handlers.ts +116 -636
- package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
- package/studio/server/introspect/source-overlay/spans.ts +15 -45
- package/studio/server/state/baseline.test.ts +14 -9
- package/studio/server/state/baseline.ts +3 -5
- package/studio/server/state/visibility.ts +1 -5
- package/studio/server/views/model.ts +3 -3
- package/studio/server/views/target.test.ts +17 -74
- package/studio/server/views/target.ts +24 -50
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +8 -4
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/schema.ts +38 -107
- package/studio/shared/contracts/surface.test.ts +13 -15
- package/studio/shared/contracts/workspace.ts +2 -11
- package/studio/shared/schema/identity.test.ts +20 -44
- package/studio/shared/schema/identity.ts +9 -23
- package/viewer/dist/main.js +30 -51
- package/.check-workspace.cjs +0 -40
- package/src/admin/binding.ts +0 -168
- package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
- package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
- package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
- package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
- package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
- package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
- package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { KernelCommandOpts } from '../../connection'
|
|
2
2
|
import type { CommandDefinition } from '../../program/index'
|
|
3
3
|
|
|
4
|
+
import { AdminInstanceNotFoundError } from '../../admin/instance'
|
|
5
|
+
import { formatKernelError } from '../../connection/errors'
|
|
4
6
|
import { deleteOwnedInstance } from '../../lib/admin-instance'
|
|
5
7
|
import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
|
|
6
8
|
import { clearActive, readInstances, removeInstance, resolveInstanceKey } from '../../lib/instance'
|
|
7
|
-
import {
|
|
9
|
+
import { log, withSpinner } from '../../lib/log'
|
|
8
10
|
import { isMachine, output } from '../../lib/output'
|
|
9
11
|
|
|
10
12
|
type DeleteOpts = KernelCommandOpts &
|
|
@@ -18,10 +20,9 @@ export default {
|
|
|
18
20
|
afterHelpText: `
|
|
19
21
|
Behavior:
|
|
20
22
|
Calls Instance.delete on the configured admin kernel: best-effort decommission
|
|
21
|
-
of the instance's
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
after the delete succeeds unless --keep-bookmark is passed.
|
|
23
|
+
of the instance's route and tenant reservation, then removes the Instance
|
|
24
|
+
projection. A same-name local bookmark is removed after the delete succeeds
|
|
25
|
+
unless --keep-bookmark is passed.
|
|
25
26
|
`,
|
|
26
27
|
arguments: [{ name: 'id', description: 'Instance slug', required: true }],
|
|
27
28
|
options: [
|
|
@@ -53,14 +54,16 @@ Behavior:
|
|
|
53
54
|
} catch (e) {
|
|
54
55
|
// Bookmark-only id → no admin Instance → kernel NOT_FOUND. Point to
|
|
55
56
|
// `forget` (local drop) instead of surfacing the raw path error.
|
|
56
|
-
|
|
57
|
-
e instanceof
|
|
58
|
-
|
|
57
|
+
if (
|
|
58
|
+
e instanceof AdminInstanceNotFoundError &&
|
|
59
|
+
resolveInstanceKey(await readInstances(), id)
|
|
60
|
+
) {
|
|
59
61
|
log.error(`No admin-managed instance "${id}".`)
|
|
60
62
|
log.dim(` hint: it's a local bookmark — drop it with: astrale instance forget ${id}`)
|
|
61
63
|
process.exit(1)
|
|
62
64
|
}
|
|
63
|
-
|
|
65
|
+
await formatKernelError(e, isMachine(opts), undefined, opts.debug)
|
|
66
|
+
process.exit(1)
|
|
64
67
|
}
|
|
65
68
|
},
|
|
66
69
|
} satisfies CommandDefinition
|
|
@@ -7,7 +7,7 @@ import type { CommandDefinition } from '../../program/index'
|
|
|
7
7
|
import { AstraleError } from '../../errors'
|
|
8
8
|
import { listOwnedInstances } from '../../lib/admin-instance'
|
|
9
9
|
import {
|
|
10
|
-
|
|
10
|
+
formatInstanceState,
|
|
11
11
|
type InstanceInfo,
|
|
12
12
|
type OwnedInstanceInfo,
|
|
13
13
|
} from '../../lib/admin-instance'
|
|
@@ -136,7 +136,7 @@ export function buildInstanceRows(
|
|
|
136
136
|
name: twin?.active ? `${item.slug} ${chalk.green('*')}` : item.slug,
|
|
137
137
|
kind: 'managed',
|
|
138
138
|
url: item.url ?? '',
|
|
139
|
-
extra: [
|
|
139
|
+
extra: [formatInstanceState(item), twin ? formatBookmarkConnection(twin) : '']
|
|
140
140
|
.filter(Boolean)
|
|
141
141
|
.join(' · '),
|
|
142
142
|
})
|
|
@@ -3,44 +3,90 @@ import chalk from 'chalk'
|
|
|
3
3
|
import type { KernelCommandOpts } from '../../connection'
|
|
4
4
|
import type { CommandDefinition } from '../../program/index'
|
|
5
5
|
|
|
6
|
+
import { formatKernelError } from '../../connection/errors'
|
|
6
7
|
import { AstraleError } from '../../errors'
|
|
7
|
-
import { statusOwnedInstance } from '../../lib/admin-instance'
|
|
8
|
+
import { statusOwnedInstance, type InstanceInfo } from '../../lib/admin-instance'
|
|
8
9
|
import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
|
|
9
|
-
import {
|
|
10
|
+
import { resolveInstance } from '../../lib/instance'
|
|
11
|
+
import { log, withSpinner } from '../../lib/log'
|
|
10
12
|
import { isMachine, output, type RawOutputOpts } from '../../lib/output'
|
|
13
|
+
import { probeBookmark } from './use'
|
|
11
14
|
|
|
12
|
-
type StatusOpts = KernelCommandOpts &
|
|
15
|
+
type StatusOpts = KernelCommandOpts &
|
|
16
|
+
AdminTargetCommandOpts &
|
|
17
|
+
RawOutputOpts & { bookmarked?: boolean }
|
|
18
|
+
|
|
19
|
+
interface BookmarkStatus {
|
|
20
|
+
readonly slug: string
|
|
21
|
+
readonly url: string
|
|
22
|
+
readonly issuer: string
|
|
23
|
+
readonly kind: 'bookmark'
|
|
24
|
+
readonly state: 'ready'
|
|
25
|
+
}
|
|
13
26
|
|
|
14
27
|
export default {
|
|
15
28
|
name: 'status',
|
|
16
|
-
description: 'Show
|
|
29
|
+
description: 'Show managed instance status or probe a bookmark',
|
|
17
30
|
arguments: [{ name: 'id', description: 'Instance slug', required: true }],
|
|
18
|
-
options: [
|
|
31
|
+
options: [
|
|
32
|
+
...ADMIN_TARGET_OPTIONS,
|
|
33
|
+
{ flags: '--bookmarked', description: 'Probe a locally bookmarked kernel connection' },
|
|
34
|
+
],
|
|
19
35
|
action: async (id: string, opts: StatusOpts) => {
|
|
20
36
|
try {
|
|
21
37
|
const result = await withSpinner(`Fetching instance ${id}`, !isMachine(opts), () =>
|
|
22
|
-
|
|
23
|
-
if (!(error instanceof Error) || error.name !== 'NotFoundError') throw error
|
|
24
|
-
throw new AstraleError(
|
|
25
|
-
'INSTANCE_NOT_FOUND',
|
|
26
|
-
`No owned instance matches "${id}".`,
|
|
27
|
-
'Run `astrale instance list` to see your instances.',
|
|
28
|
-
)
|
|
29
|
-
}),
|
|
38
|
+
resolveStatus(id, opts),
|
|
30
39
|
)
|
|
31
40
|
if (isMachine(opts)) {
|
|
32
41
|
output(result, opts)
|
|
33
42
|
return
|
|
34
43
|
}
|
|
35
44
|
console.log(`${chalk.bold(result.slug)} ${chalk.dim(result.url)}`)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (result.
|
|
39
|
-
if (result
|
|
40
|
-
|
|
41
|
-
if (result.createdAt) log.dim(` created: ${result.createdAt}`)
|
|
45
|
+
const phase = 'phase' in result ? result.phase : undefined
|
|
46
|
+
log.dim(` state: ${result.state}${phase ? ` (${phase})` : ''}`)
|
|
47
|
+
if ('error' in result && result.error) log.dim(` error: ${result.error}`)
|
|
48
|
+
if ('organizationId' in result && result.organizationId)
|
|
49
|
+
log.dim(` organization: ${result.organizationId}`)
|
|
50
|
+
if ('createdAt' in result && result.createdAt) log.dim(` created: ${result.createdAt}`)
|
|
42
51
|
} catch (e) {
|
|
43
|
-
|
|
52
|
+
await formatKernelError(e, isMachine(opts), undefined, opts.debug)
|
|
53
|
+
process.exit(1)
|
|
44
54
|
}
|
|
45
55
|
},
|
|
46
56
|
} satisfies CommandDefinition
|
|
57
|
+
|
|
58
|
+
export interface StatusDependencies {
|
|
59
|
+
readonly statusOwnedInstance: typeof statusOwnedInstance
|
|
60
|
+
readonly resolveInstance: typeof resolveInstance
|
|
61
|
+
readonly probeBookmark: typeof probeBookmark
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const defaultStatusDependencies: StatusDependencies = Object.freeze({
|
|
65
|
+
statusOwnedInstance,
|
|
66
|
+
resolveInstance,
|
|
67
|
+
probeBookmark,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
export async function resolveStatus(
|
|
71
|
+
id: string,
|
|
72
|
+
opts: StatusOpts,
|
|
73
|
+
dependencies: StatusDependencies = defaultStatusDependencies,
|
|
74
|
+
): Promise<InstanceInfo | BookmarkStatus> {
|
|
75
|
+
if (!opts.bookmarked) return dependencies.statusOwnedInstance(opts, id)
|
|
76
|
+
if (opts.admin !== undefined || opts.adminUrl !== undefined || opts.domainIssuer !== undefined) {
|
|
77
|
+
throw new AstraleError(
|
|
78
|
+
'INVALID_FLAG',
|
|
79
|
+
'--bookmarked cannot be combined with Admin target options.',
|
|
80
|
+
'Remove --bookmarked to inspect managed lifecycle, or remove the Admin options to probe a local bookmark.',
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
const bookmark = await dependencies.resolveInstance(id, undefined, { persist: false })
|
|
84
|
+
await dependencies.probeBookmark(bookmark)
|
|
85
|
+
return Object.freeze({
|
|
86
|
+
slug: bookmark.name,
|
|
87
|
+
url: bookmark.url,
|
|
88
|
+
issuer: bookmark.issuer ?? bookmark.url,
|
|
89
|
+
kind: 'bookmark',
|
|
90
|
+
state: 'ready',
|
|
91
|
+
})
|
|
92
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { CommandDefinition } from '../../program/index'
|
|
|
3
3
|
|
|
4
4
|
import { AstraleError } from '../../errors'
|
|
5
5
|
import { getDefault, setDefault } from '../../identity/index'
|
|
6
|
-
import {
|
|
6
|
+
import { listOwnedInstancesWithIdentity } from '../../lib/admin-instance'
|
|
7
7
|
import { ADMIN_TARGET_OPTIONS } from '../../lib/admin-target'
|
|
8
8
|
import { fetchWithCaFile } from '../../lib/ca-fetch'
|
|
9
9
|
import {
|
|
@@ -143,7 +143,7 @@ const defaultBookmarkProbeDependencies: BookmarkProbeDependencies = Object.freez
|
|
|
143
143
|
|
|
144
144
|
async function resolveUseTarget(name: string, opts: UseOpts): Promise<ResolvedInstance> {
|
|
145
145
|
const [store, managed] = await Promise.all([readInstances(), fetchManagedInstances(name, opts)])
|
|
146
|
-
const candidates = collectInstanceCandidates(name, store, managed)
|
|
146
|
+
const candidates = collectInstanceCandidates(name, store, managed.instances)
|
|
147
147
|
|
|
148
148
|
if (candidates.length === 0) {
|
|
149
149
|
throw new AstraleError(
|
|
@@ -168,12 +168,13 @@ async function resolveUseTarget(name: string, opts: UseOpts): Promise<ResolvedIn
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
assertManagedReady(chosen.info)
|
|
171
|
-
const { repointedFrom } = await upsertManagedBookmark(
|
|
172
|
-
chosen.key,
|
|
173
|
-
chosen.info.slug,
|
|
174
|
-
chosen.url,
|
|
175
|
-
chosen.info.organizationId,
|
|
176
|
-
|
|
171
|
+
const { repointedFrom } = await upsertManagedBookmark({
|
|
172
|
+
key: chosen.key,
|
|
173
|
+
slug: chosen.info.slug,
|
|
174
|
+
url: chosen.url,
|
|
175
|
+
...(chosen.info.organizationId ? { organizationId: chosen.info.organizationId } : {}),
|
|
176
|
+
...(managed.identity ? { defaultIdentity: managed.identity } : {}),
|
|
177
|
+
})
|
|
177
178
|
if (repointedFrom) {
|
|
178
179
|
log.warn(`Bookmark "${chosen.key}" repointed: ${repointedFrom} → ${chosen.url}`)
|
|
179
180
|
}
|
|
@@ -185,16 +186,19 @@ async function resolveUseTarget(name: string, opts: UseOpts): Promise<ResolvedIn
|
|
|
185
186
|
* Best-effort: an unreachable or unauthenticated admin kernel degrades to
|
|
186
187
|
* bookmark-only resolution.
|
|
187
188
|
*/
|
|
188
|
-
async function fetchManagedInstances(
|
|
189
|
+
async function fetchManagedInstances(
|
|
190
|
+
name: string,
|
|
191
|
+
opts: UseOpts,
|
|
192
|
+
): Promise<{ readonly instances: OwnedInstanceInfo[]; readonly identity?: string }> {
|
|
189
193
|
try {
|
|
190
194
|
validateSlug(name)
|
|
191
195
|
} catch {
|
|
192
|
-
return []
|
|
196
|
+
return { instances: [] }
|
|
193
197
|
}
|
|
194
198
|
try {
|
|
195
|
-
return await
|
|
199
|
+
return await listOwnedInstancesWithIdentity(opts)
|
|
196
200
|
} catch {
|
|
197
|
-
return []
|
|
201
|
+
return { instances: [] }
|
|
198
202
|
}
|
|
199
203
|
}
|
|
200
204
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { DomainBundle, DomainInfo, SchemaApi } from '@astrale-os/sdk/client/schema'
|
|
2
|
+
|
|
1
3
|
import { Path } from '@astrale-os/sdk/graph/path'
|
|
2
4
|
|
|
3
5
|
import type { KernelCommandOpts } from '../connection'
|
|
@@ -5,46 +7,41 @@ import type { CommandDefinition } from '../program/index'
|
|
|
5
7
|
|
|
6
8
|
import { runKernelCommand } from '../connection'
|
|
7
9
|
import { AstraleError } from '../errors'
|
|
8
|
-
import {
|
|
10
|
+
import { failInput } from '../lib/log'
|
|
9
11
|
import { output } from '../lib/output'
|
|
10
|
-
import {
|
|
12
|
+
import { describeCallableFromBundle, missingCallableDescription } from './call-describe'
|
|
11
13
|
|
|
12
14
|
type IntrospectOpts = KernelCommandOpts & { bundle?: boolean }
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
interface IntrospectDependencies {
|
|
17
|
+
readonly runKernelCommand: typeof runKernelCommand
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const defaultIntrospectDependencies = Object.freeze({ runKernelCommand })
|
|
21
|
+
|
|
22
|
+
export async function introspectCommand(
|
|
23
|
+
target: string,
|
|
24
|
+
opts: IntrospectOpts,
|
|
25
|
+
dependencies: Partial<IntrospectDependencies> = {},
|
|
26
|
+
): Promise<void> {
|
|
27
|
+
const introspect = { ...defaultIntrospectDependencies, ...dependencies }
|
|
15
28
|
let origin: string
|
|
16
29
|
let path: Path
|
|
17
30
|
try {
|
|
18
31
|
;({ origin, path } = parseIntrospectTarget(target))
|
|
19
32
|
} catch (error) {
|
|
20
|
-
|
|
33
|
+
failInput(error, opts)
|
|
21
34
|
}
|
|
22
35
|
const wantsCallable = isCallablePath(path)
|
|
23
36
|
|
|
24
|
-
await runKernelCommand({
|
|
37
|
+
await introspect.runKernelCommand({
|
|
25
38
|
opts,
|
|
26
39
|
label: `Introspect ${origin}`,
|
|
27
40
|
fn: async ({ session }) => {
|
|
28
41
|
const includeBundle = wantsCallable || opts.bundle === true
|
|
29
|
-
const result = await session.schema
|
|
30
|
-
from: { kind: 'installation', origin },
|
|
31
|
-
select: {
|
|
32
|
-
state: true,
|
|
33
|
-
target: true,
|
|
34
|
-
source: true,
|
|
35
|
-
readiness: true,
|
|
36
|
-
capabilities: true,
|
|
37
|
-
...(includeBundle ? { bundle: true as const } : {}),
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
|
-
if (result === null) {
|
|
41
|
-
throw new AstraleError(
|
|
42
|
-
'DOMAIN_NOT_INSTALLED',
|
|
43
|
-
`Domain ${origin} is not installed on this Kernel.`,
|
|
44
|
-
)
|
|
45
|
-
}
|
|
42
|
+
const result = await readInstalledDomain(session.schema, origin, includeBundle)
|
|
46
43
|
if (wantsCallable) {
|
|
47
|
-
const described =
|
|
44
|
+
const described = describeCallableFromBundle(path, (result as DomainBundle).bundle)
|
|
48
45
|
if (described === undefined) throw missingCallableDescription(path.raw)
|
|
49
46
|
return described
|
|
50
47
|
}
|
|
@@ -54,12 +51,35 @@ export async function introspectCommand(target: string, opts: IntrospectOpts): P
|
|
|
54
51
|
})
|
|
55
52
|
}
|
|
56
53
|
|
|
54
|
+
export function readInstalledDomain(
|
|
55
|
+
schema: Pick<SchemaApi, 'inspect' | 'bundle'>,
|
|
56
|
+
origin: string,
|
|
57
|
+
includeBundle: true,
|
|
58
|
+
): Promise<DomainBundle>
|
|
59
|
+
export function readInstalledDomain(
|
|
60
|
+
schema: Pick<SchemaApi, 'inspect' | 'bundle'>,
|
|
61
|
+
origin: string,
|
|
62
|
+
includeBundle: false,
|
|
63
|
+
): Promise<DomainInfo>
|
|
64
|
+
export function readInstalledDomain(
|
|
65
|
+
schema: Pick<SchemaApi, 'inspect' | 'bundle'>,
|
|
66
|
+
origin: string,
|
|
67
|
+
includeBundle: boolean,
|
|
68
|
+
): Promise<DomainInfo | DomainBundle>
|
|
69
|
+
export function readInstalledDomain(
|
|
70
|
+
schema: Pick<SchemaApi, 'inspect' | 'bundle'>,
|
|
71
|
+
origin: string,
|
|
72
|
+
includeBundle: boolean,
|
|
73
|
+
): Promise<DomainInfo | DomainBundle> {
|
|
74
|
+
return includeBundle ? schema.bundle(origin) : schema.inspect(origin)
|
|
75
|
+
}
|
|
76
|
+
|
|
57
77
|
export function parseIntrospectTarget(target: string): { origin: string; path: Path } {
|
|
58
78
|
if (target.startsWith('@')) {
|
|
59
79
|
throw new AstraleError(
|
|
60
80
|
'NOT_A_DOMAIN',
|
|
61
81
|
'introspect requires a Domain origin or Path, not an @id.',
|
|
62
|
-
'Example: astrale introspect
|
|
82
|
+
'Example: astrale introspect kernel.astrale.ai or /:kernel.astrale.ai:class.Identity:whois',
|
|
63
83
|
)
|
|
64
84
|
}
|
|
65
85
|
const raw = target.startsWith('/') ? target : `/:${target}`
|
|
@@ -85,28 +105,21 @@ function isCallablePath(path: Path): boolean {
|
|
|
85
105
|
return last.kind === 'projection' && last.projection.kind === 'function'
|
|
86
106
|
}
|
|
87
107
|
|
|
88
|
-
function bundleRoot(bundle: unknown): unknown {
|
|
89
|
-
if (bundle !== null && typeof bundle === 'object' && 'root' in bundle) {
|
|
90
|
-
return (bundle as { root: unknown }).root
|
|
91
|
-
}
|
|
92
|
-
return bundle
|
|
93
|
-
}
|
|
94
|
-
|
|
95
108
|
export default {
|
|
96
109
|
name: 'introspect',
|
|
97
110
|
description: 'Read installed Domain schema from the Kernel Schema syscall',
|
|
98
111
|
afterHelpText: `
|
|
99
112
|
Behavior:
|
|
100
113
|
Calls the public Kernel introspect syscall for one installed Domain.
|
|
101
|
-
A bare origin (
|
|
114
|
+
A bare origin (kernel.astrale.ai or /:kernel.astrale.ai) prints installation
|
|
102
115
|
state, target, source, readiness, and capabilities. --bundle includes the
|
|
103
116
|
schema bundle. A method or Function Path projects that callable's
|
|
104
117
|
input/output from the installed bundle.
|
|
105
118
|
|
|
106
119
|
Examples:
|
|
107
|
-
$ astrale introspect
|
|
108
|
-
$ astrale introspect /:
|
|
109
|
-
$ astrale introspect /:
|
|
120
|
+
$ astrale introspect kernel.astrale.ai
|
|
121
|
+
$ astrale introspect /:kernel.astrale.ai --bundle
|
|
122
|
+
$ astrale introspect /:kernel.astrale.ai:class.Identity:whois
|
|
110
123
|
$ astrale introspect /:kernel.astrale.ai:function.journal
|
|
111
124
|
`,
|
|
112
125
|
arguments: [{ name: 'target', description: 'Domain origin or canonical Path' }],
|
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,7 +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.
|
|
344
|
+
and advances only with the returned cursor. Machine follow output is NDJSON:
|
|
345
|
+
one complete admitted record per line; YAML follow is unsupported.
|
|
256
346
|
|
|
257
347
|
Historical event-glob lowering and the application-specific services-domain
|
|
258
348
|
log buffer are not part of the Kernel V2 journal contract.
|
|
@@ -274,11 +364,11 @@ Examples:
|
|
|
274
364
|
],
|
|
275
365
|
action: async (opts: LogsOpts) => {
|
|
276
366
|
try {
|
|
277
|
-
|
|
278
|
-
if (prepared.follow) await follow(prepared)
|
|
279
|
-
else await runOnce(prepared)
|
|
367
|
+
validateLogsOpts(opts)
|
|
280
368
|
} catch (error) {
|
|
281
|
-
|
|
369
|
+
failInput(error, opts)
|
|
282
370
|
}
|
|
371
|
+
if (opts.follow) await followLogs(opts)
|
|
372
|
+
else await runOnce(opts)
|
|
283
373
|
},
|
|
284
374
|
} 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
|
}
|