@astrale-os/cli 0.8.0-alpha.0 → 0.8.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/astrale.js +62968 -41240
- package/dist/public/connect-core.js +55030 -0
- package/dist/public/keys/index.js +45631 -0
- package/dist/public/paths/index.js +43990 -0
- package/dist/types/admin/instance/model.d.ts +40 -0
- package/dist/types/connect-core.d.ts +22 -0
- package/dist/types/connection/auth.d.ts +33 -0
- package/dist/types/errors.d.ts +23 -0
- package/dist/types/identity/registry.d.ts +25 -0
- package/dist/types/keys/algorithm.d.ts +6 -0
- package/dist/types/keys/credential.d.ts +21 -0
- package/dist/types/keys/index.d.ts +4 -0
- package/dist/types/keys/pair.d.ts +34 -0
- package/dist/types/lib/admin-target.d.ts +74 -0
- package/dist/types/lib/ca-fetch.d.ts +2 -0
- package/dist/types/lib/config.d.ts +71 -0
- package/dist/types/lib/format.d.ts +1 -0
- package/dist/types/lib/idp-session.d.ts +33 -0
- package/dist/types/lib/idp.d.ts +397 -0
- package/dist/types/lib/instance-target.d.ts +36 -0
- package/dist/types/lib/instance.d.ts +186 -0
- package/dist/types/lib/log.d.ts +30 -0
- package/dist/types/lib/login-flow.d.ts +49 -0
- package/dist/types/lib/meta.d.ts +36 -0
- package/dist/types/lib/output.d.ts +83 -0
- package/dist/types/lib/table.d.ts +18 -0
- package/dist/types/lib/validation.d.ts +11 -0
- package/dist/types/paths/index.d.ts +2 -0
- package/dist/types/state/exchange-credentials.d.ts +25 -0
- package/dist/types/state/files.d.ts +9 -0
- package/dist/types/state/identities.d.ts +42 -0
- package/dist/types/state/index.d.ts +8 -0
- package/dist/types/state/paths.d.ts +32 -0
- package/package.json +30 -8
- package/src/admin/.spec/api.d.ts +3 -0
- package/src/admin/.spec/architecture.md +15 -0
- package/src/admin/.spec/icon.svg +3 -0
- package/src/admin/.spec/layout.ts +6 -0
- package/src/admin/catalog/.spec/api.d.ts +65 -0
- package/src/admin/catalog/.spec/architecture.md +6 -0
- package/src/admin/catalog/.spec/icon.svg +3 -0
- package/src/admin/catalog/.spec/layout.ts +6 -0
- package/src/admin/catalog/__tests__/client.test.ts +171 -0
- package/src/admin/catalog/client.ts +214 -0
- package/src/admin/catalog/index.ts +13 -0
- package/src/admin/catalog/model.ts +42 -0
- package/src/admin/catalog/tsconfig.json +8 -0
- package/src/admin/graph/.spec/api.d.ts +28 -0
- package/src/admin/graph/.spec/architecture.md +5 -0
- package/src/admin/graph/.spec/icon.svg +3 -0
- package/src/admin/graph/.spec/layout.ts +3 -0
- package/src/admin/graph/index.ts +6 -0
- package/src/admin/graph/nodes.ts +56 -0
- package/src/admin/index.ts +2 -0
- package/src/admin/instance/.spec/api.d.ts +66 -0
- package/src/admin/instance/.spec/architecture.md +10 -0
- package/src/admin/instance/.spec/icon.svg +3 -0
- package/src/admin/instance/.spec/layout.ts +6 -0
- package/src/admin/instance/__tests__/client.test.ts +289 -0
- package/src/admin/instance/client.ts +298 -0
- package/src/admin/instance/index.ts +16 -0
- package/src/admin/instance/model.ts +65 -0
- package/src/admin/instance/tsconfig.json +8 -0
- package/src/commands/__tests__/admin-instance.test.ts +3 -36
- package/src/commands/__tests__/call.test.ts +21 -68
- package/src/commands/__tests__/domain-install-owned.test.ts +26 -27
- package/src/commands/__tests__/domain-list.test.ts +2 -6
- package/src/commands/__tests__/logs.test.ts +50 -98
- package/src/commands/__tests__/ls.test.ts +21 -17
- package/src/commands/__tests__/read-commands.test.ts +171 -101
- package/src/commands/__tests__/view.test.ts +100 -51
- package/src/commands/admin/status.ts +3 -3
- package/src/commands/admin/use.ts +29 -6
- package/src/commands/auth/login.ts +1 -1
- package/src/commands/auth/logout.ts +4 -2
- package/src/commands/auth/status.ts +2 -2
- package/src/commands/auth/token.ts +2 -2
- package/src/commands/browser.ts +1 -1
- package/src/commands/call.ts +55 -91
- package/src/commands/describe.ts +52 -147
- package/src/commands/domain/install.ts +81 -50
- package/src/commands/domain/list.ts +4 -8
- package/src/commands/domain/publish.ts +9 -31
- package/src/commands/get.ts +32 -65
- package/src/commands/identity/__tests__/register.test.ts +93 -0
- package/src/commands/identity/create.ts +2 -2
- package/src/commands/identity/delete.ts +2 -2
- package/src/commands/identity/export.ts +7 -33
- package/src/commands/identity/import.ts +21 -70
- package/src/commands/identity/list.ts +2 -2
- package/src/commands/identity/register.ts +148 -117
- package/src/commands/identity/sync.ts +2 -2
- package/src/commands/identity/unsync.ts +2 -2
- package/src/commands/identity/use.ts +2 -2
- package/src/commands/identity/whoami.ts +2 -2
- package/src/commands/idp/add.ts +1 -1
- package/src/commands/idp/list.ts +1 -1
- package/src/commands/idp/refresh.ts +1 -1
- package/src/commands/idp/remove.ts +2 -2
- package/src/commands/idp/show.ts +1 -1
- package/src/commands/instance/active.ts +2 -2
- package/src/commands/instance/bookmark.ts +8 -2
- package/src/commands/instance/create.ts +1 -1
- package/src/commands/instance/delete.ts +4 -10
- package/src/commands/instance/forget.ts +1 -1
- package/src/commands/instance/list.ts +3 -3
- package/src/commands/instance/status.ts +5 -7
- package/src/commands/instance/use.ts +3 -3
- package/src/commands/logs.ts +152 -279
- package/src/commands/ls.ts +78 -204
- package/src/commands/mutate.ts +70 -139
- package/src/commands/query.ts +100 -278
- package/src/commands/session/analyze.ts +1 -1
- package/src/commands/session/list.ts +1 -1
- package/src/commands/setup.ts +1 -1
- package/src/commands/status.ts +1 -1
- package/src/commands/studio.ts +1 -1
- package/src/commands/token.ts +26 -21
- package/src/commands/update.ts +1 -1
- package/src/commands/use.ts +2 -3
- package/src/commands/view-serve.ts +1 -1
- package/src/commands/view.ts +67 -109
- package/src/connect-core.test.ts +38 -0
- package/src/connect-core.ts +42 -0
- package/src/connection/.spec/api.d.ts +111 -0
- package/src/connection/.spec/architecture.md +41 -0
- package/src/connection/.spec/capabilities/connection.ts +13 -0
- package/src/connection/.spec/examples/call.ts +7 -0
- package/src/connection/.spec/flows/command.ts +20 -0
- package/src/connection/.spec/flows/session.ts +61 -0
- package/src/connection/.spec/laws/connection.ts +181 -0
- package/src/connection/.spec/layout.ts +19 -0
- package/src/{kernel → connection}/__tests__/auth.test.ts +26 -2
- package/src/connection/__tests__/ca-fetch.test.ts +53 -0
- package/src/connection/__tests__/call.test.ts +17 -0
- package/src/connection/__tests__/credential.test.ts +109 -0
- package/src/connection/__tests__/errors.test.ts +96 -0
- package/src/connection/__tests__/exchange.test.ts +224 -0
- package/src/connection/__tests__/fixtures/localhost-cert.pem +19 -0
- package/src/connection/__tests__/fixtures/localhost-key.base64 +1 -0
- package/src/connection/__tests__/managed.test.ts +39 -0
- package/src/connection/__tests__/self.test.ts +51 -0
- package/src/connection/__tests__/session.test.ts +140 -0
- package/src/connection/__tests__/target.test.ts +93 -0
- package/src/{kernel → connection}/auth.ts +8 -8
- package/src/connection/call.ts +7 -0
- package/src/{kernel/run.ts → connection/command.ts} +22 -13
- package/src/connection/credential.ts +114 -0
- package/src/{kernel → connection}/errors.ts +32 -9
- package/src/connection/exchange.ts +274 -0
- package/src/connection/index.ts +8 -0
- package/src/connection/self.ts +120 -0
- package/src/connection/session.ts +176 -0
- package/src/connection/target.ts +111 -0
- package/src/connection/tsconfig.json +6 -0
- package/src/graph/.spec/api.d.ts +34 -0
- package/src/graph/.spec/laws/documents.ts +24 -0
- package/src/graph/.spec/laws/projection.ts +13 -0
- package/src/graph/.spec/layout.ts +6 -0
- package/src/graph/__tests__/mutation.test.ts +43 -0
- package/src/graph/__tests__/projection.test.ts +22 -0
- package/src/graph/__tests__/query.test.ts +154 -0
- package/src/graph/index.ts +3 -0
- package/src/graph/mutation.ts +18 -0
- package/src/graph/projection.ts +21 -0
- package/src/graph/query.ts +108 -0
- package/src/graph/tsconfig.json +6 -0
- package/src/identity/.history/ard/bootstrap-root-transfer.md +57 -0
- package/src/identity/.history/ard/domain-mediated-registration.md +36 -0
- package/src/identity/.spec/api.d.ts +124 -0
- package/src/identity/.spec/architecture.md +33 -0
- package/src/identity/.spec/capabilities/identity.ts +7 -0
- package/src/identity/.spec/flows/bootstrap-root.ts +79 -0
- package/src/identity/.spec/flows/domain-mediated-registration.ts +51 -0
- package/src/identity/.spec/flows/import.ts +29 -0
- package/src/identity/.spec/laws/identity.ts +56 -0
- package/src/identity/.spec/layout.ts +13 -0
- package/src/identity/.spec/schemas/identity-export-v1.schema.json +25 -0
- package/src/identity/__tests__/fixtures/registry-journey.ts +53 -0
- package/src/identity/__tests__/registration.test.ts +64 -0
- package/src/identity/__tests__/registry.test.ts +70 -0
- package/src/identity/__tests__/transfer.test.ts +198 -0
- package/src/identity/index.ts +23 -0
- package/src/identity/registration.ts +72 -0
- package/src/identity/registry.ts +172 -0
- package/src/identity/transfer.ts +187 -0
- package/src/identity/tsconfig.json +6 -0
- package/src/index.ts +1 -0
- package/src/keys/.spec/api.d.ts +74 -0
- package/src/keys/.spec/architecture.md +13 -0
- package/src/keys/.spec/laws/keys.ts +43 -0
- package/src/keys/.spec/layout.ts +6 -0
- package/src/keys/__tests__/algorithm.test.ts +39 -0
- package/src/keys/__tests__/keys.test.ts +168 -0
- package/src/keys/algorithm.ts +33 -0
- package/src/keys/credential.ts +112 -0
- package/src/keys/index.ts +14 -0
- package/src/keys/pair.ts +219 -0
- package/src/keys/tsconfig.json +6 -0
- package/src/lib/__tests__/admin-target.test.ts +67 -7
- package/src/lib/__tests__/binary.test.ts +2 -3
- package/src/lib/__tests__/command-dx.test.ts +2 -2
- package/src/lib/__tests__/config.test.ts +3 -2
- package/src/lib/__tests__/idp.test.ts +4 -6
- package/src/lib/__tests__/instance-target.test.ts +7 -4
- package/src/lib/__tests__/local-status.test.ts +1 -1
- package/src/lib/__tests__/log.test.ts +31 -0
- package/src/lib/__tests__/meta.test.ts +55 -0
- package/src/lib/__tests__/sdk-deps.test.ts +1 -1
- package/src/lib/__tests__/self.test.ts +8 -230
- package/src/lib/__tests__/use-target.test.ts +1 -1
- package/src/lib/__tests__/view-open-intent.test.ts +78 -104
- package/src/lib/__tests__/view-session.test.ts +99 -0
- package/src/lib/admin-domain.ts +39 -25
- package/src/lib/admin-instance.ts +38 -43
- package/src/lib/admin-target.ts +67 -13
- package/src/lib/binary.ts +13 -34
- package/src/lib/browser.ts +1 -1
- package/src/{kernel → lib}/ca-fetch.ts +35 -27
- package/src/lib/config.ts +1 -1
- package/src/lib/idp-session.ts +1 -1
- package/src/lib/idp.ts +3 -8
- package/src/lib/instance-target.ts +12 -8
- package/src/lib/instance.ts +34 -2
- package/src/lib/local-status.ts +2 -2
- package/src/lib/log.ts +3 -1
- package/src/lib/login-flow.ts +32 -5
- package/src/lib/meta.ts +15 -11
- package/src/lib/provision-instance.ts +4 -12
- package/src/lib/self.ts +5 -147
- package/src/lib/update.ts +1 -1
- package/src/lib/use-target.ts +1 -1
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/open-intent.ts +7 -37
- package/src/lib/view/port-allocation.ts +1 -1
- package/src/lib/view/resolve.ts +52 -45
- package/src/lib/view/server.ts +16 -33
- package/src/lib/view/session.ts +51 -22
- package/src/paths/.spec/api.d.ts +13 -0
- package/src/paths/.spec/architecture.md +5 -0
- package/src/paths/.spec/layout.ts +3 -0
- package/src/paths/index.ts +13 -0
- package/src/program/.spec/api.d.ts +42 -0
- package/src/program/.spec/architecture.md +18 -0
- package/src/program/.spec/laws/program.ts +29 -0
- package/src/program/.spec/layout.ts +14 -0
- package/src/program/__tests__/program.test.ts +359 -0
- package/src/program/build.ts +190 -0
- package/src/program/command.ts +42 -0
- package/src/program/index.ts +2 -0
- package/src/program/options.ts +40 -0
- package/src/{registry.ts → program/registry.ts} +5 -1
- package/src/program/tsconfig.json +6 -0
- package/src/setup/steps/admin.ts +18 -3
- package/src/setup/steps/instance.ts +1 -1
- package/src/state/.spec/api.d.ts +133 -0
- package/src/state/.spec/architecture.md +31 -0
- package/src/state/.spec/capabilities/state.ts +7 -0
- package/src/state/.spec/flows/identity-update.ts +22 -0
- package/src/state/.spec/laws/state.ts +73 -0
- package/src/state/.spec/layout.ts +14 -0
- package/src/state/__tests__/exchange-credentials.test.ts +188 -0
- package/src/state/__tests__/files.test.ts +113 -0
- package/src/state/__tests__/fixtures/identity-transition.ts +37 -0
- package/src/state/__tests__/identities.test.ts +142 -0
- package/src/state/__tests__/paths.test.ts +27 -0
- package/src/state/exchange-credentials.ts +219 -0
- package/src/state/files.ts +129 -0
- package/src/state/identities.ts +236 -0
- package/src/state/index.ts +29 -0
- package/src/state/paths.ts +61 -0
- package/src/state/tsconfig.json +6 -0
- package/src/telemetry/__tests__/redact.test.ts +3 -3
- package/src/telemetry/__tests__/trigger.test.ts +1 -1
- package/src/telemetry/settings.ts +1 -1
- package/src/telemetry/store.ts +1 -1
- package/src/test-utils.ts +1 -1
- package/studio/client/dist/assets/elk-api-D0cBetPW.js +1 -0
- package/studio/client/dist/assets/elk-worker.min-C9JGDOE-.js +6312 -0
- package/studio/client/dist/assets/index-BQnJ5sgd.css +1 -0
- package/studio/client/dist/assets/index-ChOr3yP0.css +1 -0
- package/studio/client/dist/assets/index-Dspir4w7.js +81 -0
- package/studio/client/dist/assets/index-bVD2KJgz.js +8 -0
- package/studio/client/dist/index.html +2 -2
- package/studio/package.json +10 -1
- package/studio/server/introspect/extractor.ts +1 -1
- package/studio/server/state/views.test.ts +3 -7
- package/studio/server/state/views.ts +23 -30
- package/studio/tsconfig.json +23 -0
- package/tsconfig.json +1 -1
- package/viewer/dist/main.js +65 -39
- package/viewer/tsconfig.json +13 -0
- package/src/command.ts +0 -42
- package/src/commands/__tests__/help-contract.test.ts +0 -164
- package/src/kernel/__tests__/client-owned-lookup.test.ts +0 -51
- package/src/kernel/__tests__/errors.test.ts +0 -43
- package/src/kernel/__tests__/expand.test.ts +0 -123
- package/src/kernel/client.ts +0 -214
- package/src/kernel/expand.ts +0 -192
- package/src/kernel/graph.ts +0 -96
- package/src/kernel/index.ts +0 -29
- package/src/kernel/options.ts +0 -22
- package/src/kernel/types.ts +0 -14
- package/src/lib/__tests__/domain-identity.test.ts +0 -60
- package/src/lib/__tests__/fs-atomic.test.ts +0 -104
- package/src/lib/__tests__/identity.test.ts +0 -79
- package/src/lib/__tests__/keys.test.ts +0 -129
- package/src/lib/domain-identity.ts +0 -49
- package/src/lib/env.ts +0 -49
- package/src/lib/fs-atomic.ts +0 -126
- package/src/lib/identity.ts +0 -256
- package/src/lib/keys.ts +0 -294
- package/src/lib/paths.ts +0 -11
- package/src/program.ts +0 -213
- package/studio/client/dist/assets/index-BaqEuIQJ.js +0 -109
- package/studio/client/dist/assets/index-jRdExahh.css +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { CommandDefinition } from '../../command'
|
|
2
1
|
import type { OwnedInstanceInfo } from '../../lib/admin-instance'
|
|
2
|
+
import type { CommandDefinition } from '../../program/index'
|
|
3
3
|
|
|
4
4
|
import { AstraleError } from '../../errors'
|
|
5
|
-
import {
|
|
5
|
+
import { getDefault, setDefault } from '../../identity/index'
|
|
6
|
+
import { listOwnedInstances } from '../../lib/admin-instance'
|
|
6
7
|
import { ADMIN_TARGET_OPTIONS } from '../../lib/admin-target'
|
|
7
|
-
import { getDefault, setDefault } from '../../lib/identity'
|
|
8
8
|
import {
|
|
9
9
|
getActive,
|
|
10
10
|
readInstances,
|
package/src/commands/logs.ts
CHANGED
|
@@ -1,347 +1,220 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* `astrale logs` — tail the kernel event journal (function.journal) for the target
|
|
5
|
-
* instance. Defaults to the kernel journal syscall
|
|
6
|
-
* `/kernel.astrale.ai/functions/journal`; `--service <name>` switches to the
|
|
7
|
-
* per-instance `services` domain log buffer (the historical behavior).
|
|
8
|
-
*
|
|
9
|
-
* Target the instance with `-i <instance>` (inherited from withKernelOptions).
|
|
10
|
-
*/
|
|
1
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
2
|
+
import { syscalls } from '@astrale-os/sdk/schema/kernel'
|
|
11
3
|
import chalk from 'chalk'
|
|
12
4
|
|
|
13
|
-
import type {
|
|
14
|
-
import type { ClientContext, KernelCommandOpts } from '../kernel'
|
|
5
|
+
import type { ConnectionContext, KernelCommandOpts } from '../connection'
|
|
15
6
|
import type { Column, ListProjection } from '../lib/output'
|
|
7
|
+
import type { CommandDefinition } from '../program/index'
|
|
16
8
|
|
|
17
|
-
import { runKernelCommand,
|
|
18
|
-
import { fatal, withSpinner } from '../lib/log'
|
|
9
|
+
import { createPathCall, runKernelCommand, withClientSession } from '../connection'
|
|
19
10
|
import { isMachine, output, presentList } from '../lib/output'
|
|
20
11
|
|
|
21
|
-
const
|
|
22
|
-
const DEFAULT_SERVICES_ORIGIN = 'services.astrale.ai'
|
|
12
|
+
const JOURNAL_PATH = Path.project(syscalls.journal.ref).raw
|
|
23
13
|
const DEFAULT_LIMIT = 200
|
|
24
|
-
const FOLLOW_INTERVAL_MS =
|
|
25
|
-
// The journal-read syscall journals its own ops; hide them by default so polling
|
|
26
|
-
// doesn't self-pollute the view. `--all` shows them.
|
|
27
|
-
const SELF_READ_PREFIX = 'op:function.journal:'
|
|
28
|
-
|
|
29
|
-
// The published @astrale-os/kernel-core@0.5.0 JournalFilter has no `cursor` yet
|
|
30
|
-
// (added on the kernel branch). Type against the augmented shape so the
|
|
31
|
-
// incremental cursor compiles against the consumer dependency.
|
|
32
|
-
type EventsParams = JournalFilter & { cursor?: number }
|
|
14
|
+
const FOLLOW_INTERVAL_MS = 2_000
|
|
33
15
|
|
|
34
16
|
type LogsOpts = KernelCommandOpts & {
|
|
35
17
|
since?: string
|
|
36
18
|
until?: string
|
|
37
19
|
topic?: string
|
|
20
|
+
topicPrefix?: string
|
|
38
21
|
principal?: string
|
|
39
22
|
limit?: string
|
|
40
23
|
cursor?: string
|
|
41
24
|
follow?: boolean
|
|
42
|
-
all?: boolean
|
|
43
|
-
timing?: boolean
|
|
44
|
-
// service mode
|
|
45
|
-
service?: string
|
|
46
|
-
tail?: string
|
|
47
|
-
servicesOrigin?: string
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Kernel journal page. The function.journal syscall returns a BARE `JournalEntry[]`;
|
|
52
|
-
* the client derives the next cursor from the max `seq`. We still model a
|
|
53
|
-
* `nextCursor` field so the TTY footer / incremental tail share one shape, and
|
|
54
|
-
* so a future paged kernel response degrades gracefully.
|
|
55
|
-
*/
|
|
56
|
-
type EventsPage = {
|
|
57
|
-
entries: JournalEntry[]
|
|
58
|
-
nextCursor?: number | null
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// ── function.journal (default) ───────────────────────────────────
|
|
62
|
-
|
|
63
|
-
/** The highest `seq` across entries, or null when empty. */
|
|
64
|
-
function maxSeq(entries: JournalEntry[]): number | null {
|
|
65
|
-
let max: number | null = null
|
|
66
|
-
for (const e of entries) {
|
|
67
|
-
if (typeof e.seq === 'number' && (max === null || e.seq > max)) max = e.seq
|
|
68
|
-
}
|
|
69
|
-
return max
|
|
70
25
|
}
|
|
71
26
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return { entries, nextCursor: maxSeq(entries) }
|
|
81
|
-
}
|
|
82
|
-
const obj = (raw ?? {}) as { entries?: unknown; nextCursor?: unknown }
|
|
83
|
-
const entries = Array.isArray(obj.entries) ? (obj.entries as JournalEntry[]) : []
|
|
84
|
-
const nextCursor = typeof obj.nextCursor === 'number' ? obj.nextCursor : maxSeq(entries)
|
|
85
|
-
return { entries, nextCursor }
|
|
27
|
+
export interface JournalRecord {
|
|
28
|
+
readonly sequence: number
|
|
29
|
+
readonly timestamp: string
|
|
30
|
+
readonly topic: string
|
|
31
|
+
readonly payload: unknown
|
|
32
|
+
readonly principal?: string
|
|
33
|
+
readonly correlationId?: string
|
|
34
|
+
readonly causationId?: string
|
|
86
35
|
}
|
|
87
36
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (opts.topic) params.topic = opts.topic
|
|
92
|
-
if (opts.principal) params.principal = opts.principal as JournalFilter['principal']
|
|
93
|
-
if (opts.since !== undefined) params.since = parseTimeFlag('--since', opts.since)
|
|
94
|
-
if (opts.until !== undefined) params.until = parseTimeFlag('--until', opts.until)
|
|
95
|
-
params.limit = opts.limit !== undefined ? parsePositiveInt('--limit', opts.limit) : DEFAULT_LIMIT
|
|
96
|
-
if (opts.cursor !== undefined) params.cursor = parsePositiveInt('--cursor', opts.cursor)
|
|
97
|
-
return params
|
|
37
|
+
export interface JournalPage {
|
|
38
|
+
readonly records: readonly JournalRecord[]
|
|
39
|
+
readonly cursor?: string
|
|
98
40
|
}
|
|
99
41
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
42
|
+
export interface JournalInput {
|
|
43
|
+
readonly topics?: { readonly exact?: readonly string[]; readonly prefixes?: readonly string[] }
|
|
44
|
+
readonly principal?: string
|
|
45
|
+
readonly since?: string
|
|
46
|
+
readonly until?: string
|
|
47
|
+
readonly cursor?: string
|
|
48
|
+
readonly limit: number
|
|
106
49
|
}
|
|
107
50
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
51
|
+
/** Map flags to the exact public journal syscall input without legacy glob/sequence lowering. */
|
|
52
|
+
export function buildJournalInput(opts: LogsOpts): JournalInput {
|
|
53
|
+
const exact = nonEmpty(opts.topic)
|
|
54
|
+
const prefix = nonEmpty(opts.topicPrefix)
|
|
55
|
+
return {
|
|
56
|
+
...(exact === undefined && prefix === undefined
|
|
57
|
+
? {}
|
|
58
|
+
: {
|
|
59
|
+
topics: {
|
|
60
|
+
...(exact === undefined ? {} : { exact: [exact] }),
|
|
61
|
+
...(prefix === undefined ? {} : { prefixes: [prefix] }),
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
...(nonEmpty(opts.principal) === undefined ? {} : { principal: opts.principal }),
|
|
65
|
+
...(nonEmpty(opts.since) === undefined ? {} : { since: opts.since }),
|
|
66
|
+
...(nonEmpty(opts.until) === undefined ? {} : { until: opts.until }),
|
|
67
|
+
...(nonEmpty(opts.cursor) === undefined ? {} : { cursor: opts.cursor }),
|
|
68
|
+
limit: opts.limit === undefined ? DEFAULT_LIMIT : positiveInteger('--limit', opts.limit),
|
|
112
69
|
}
|
|
113
|
-
return n
|
|
114
70
|
}
|
|
115
71
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
72
|
+
/** Validate the record fields the CLI presentation consumes and retain the opaque cursor. */
|
|
73
|
+
export function acceptJournalPage(input: unknown): JournalPage {
|
|
74
|
+
if (!isRecord(input) || !Array.isArray(input.records)) {
|
|
75
|
+
throw new TypeError('Kernel journal response must contain a records array')
|
|
76
|
+
}
|
|
77
|
+
const records = input.records.map((record, index) => acceptRecord(record, index))
|
|
78
|
+
if (input.cursor !== undefined && typeof input.cursor !== 'string') {
|
|
79
|
+
throw new TypeError('Kernel journal cursor must be text')
|
|
80
|
+
}
|
|
81
|
+
return Object.freeze({
|
|
82
|
+
records: Object.freeze(records),
|
|
83
|
+
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
84
|
+
})
|
|
123
85
|
}
|
|
124
86
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (ms >= 500) return chalk.red(s)
|
|
130
|
-
if (ms >= 100) return chalk.yellow(s)
|
|
131
|
-
return chalk.green(s)
|
|
87
|
+
async function fetchPage(context: ConnectionContext, opts: LogsOpts): Promise<JournalPage> {
|
|
88
|
+
return acceptJournalPage(
|
|
89
|
+
await context.session.call(createPathCall(JOURNAL_PATH, buildJournalInput(opts))),
|
|
90
|
+
)
|
|
132
91
|
}
|
|
133
92
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
93
|
+
async function runOnce(opts: LogsOpts): Promise<void> {
|
|
94
|
+
await runKernelCommand({
|
|
95
|
+
opts,
|
|
96
|
+
label: 'Kernel journal',
|
|
97
|
+
fn: (context) => fetchPage(context, opts),
|
|
98
|
+
format: (page, format) => {
|
|
99
|
+
if (isMachine(format) || format.format !== undefined) output(page, format)
|
|
100
|
+
else {
|
|
101
|
+
presentList([...page.records], format, journalProjection)
|
|
102
|
+
if (page.cursor) process.stderr.write(` cursor: ${page.cursor}\n`)
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
})
|
|
144
106
|
}
|
|
145
107
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
108
|
+
async function follow(opts: LogsOpts): Promise<void> {
|
|
109
|
+
await withClientSession(opts, async (context) => {
|
|
110
|
+
let cursor = opts.cursor
|
|
111
|
+
for (;;) {
|
|
112
|
+
const page = await fetchPage(context, { ...opts, cursor })
|
|
113
|
+
for (const record of page.records) printRecord(record)
|
|
114
|
+
cursor = page.cursor ?? cursor
|
|
115
|
+
await new Promise((resolve) => setTimeout(resolve, FOLLOW_INTERVAL_MS))
|
|
116
|
+
}
|
|
117
|
+
})
|
|
154
118
|
}
|
|
155
119
|
|
|
156
|
-
function
|
|
120
|
+
function journalProjection(records: JournalRecord[]): ListProjection {
|
|
157
121
|
const columns: Column[] = [
|
|
158
|
-
{ key: '
|
|
159
|
-
{ key: '
|
|
160
|
-
{ key: 'topic', header: 'TOPIC', color:
|
|
161
|
-
{ key: 'latency', header: 'LATENCY', color: LATENCY_COLOR },
|
|
162
|
-
...(showTiming ? [{ key: 'steps', header: 'STEPS', color: chalk.dim } as Column] : []),
|
|
122
|
+
{ key: 'sequence', header: 'SEQ', color: chalk.dim },
|
|
123
|
+
{ key: 'timestamp', header: 'TIME', color: chalk.dim },
|
|
124
|
+
{ key: 'topic', header: 'TOPIC', color: chalk.cyan },
|
|
163
125
|
{ key: 'principal', header: 'PRINCIPAL', color: chalk.dim },
|
|
164
126
|
]
|
|
165
127
|
return {
|
|
166
128
|
columns,
|
|
167
|
-
rows:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
topic:
|
|
171
|
-
|
|
172
|
-
...(showTiming ? { steps: timingOf(e) } : {}),
|
|
173
|
-
principal: String(e.event.metadata.principal),
|
|
129
|
+
rows: records.map((record) => ({
|
|
130
|
+
sequence: String(record.sequence),
|
|
131
|
+
timestamp: record.timestamp,
|
|
132
|
+
topic: record.topic,
|
|
133
|
+
principal: record.principal ?? '',
|
|
174
134
|
})),
|
|
175
|
-
paths:
|
|
135
|
+
paths: records.map((record) => String(record.sequence)),
|
|
176
136
|
}
|
|
177
137
|
}
|
|
178
138
|
|
|
179
|
-
|
|
180
|
-
const raw = await ctx.client.call(JOURNAL_FN_PATH, buildEventsParams(opts))
|
|
181
|
-
const page = normalizePage(raw)
|
|
182
|
-
// Strip the journal's own read ops by default (but keep nextCursor past them).
|
|
183
|
-
const entries = opts.all
|
|
184
|
-
? page.entries
|
|
185
|
-
: page.entries.filter((e) => !e.event.topic.startsWith(SELF_READ_PREFIX))
|
|
186
|
-
return { entries, nextCursor: page.nextCursor }
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function printEventLine(e: JournalEntry, showTiming = false): void {
|
|
190
|
-
const ts = new Date(e.event.metadata.timestamp).toISOString()
|
|
191
|
-
const lat = latencyOf(e)
|
|
192
|
-
const steps = showTiming ? timingOf(e) : ''
|
|
139
|
+
function printRecord(record: JournalRecord): void {
|
|
193
140
|
process.stdout.write(
|
|
194
|
-
`${chalk.dim(String(
|
|
141
|
+
`${chalk.dim(String(record.sequence).padStart(6))} ${chalk.dim(record.timestamp)} ${chalk.cyan(record.topic)} ${chalk.dim(record.principal ?? '')}\n`,
|
|
195
142
|
)
|
|
196
143
|
}
|
|
197
144
|
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
presentList(page.entries, fmtOpts, (entries) => eventsProjection(entries, opts.timing))
|
|
212
|
-
if (typeof page.nextCursor === 'number') {
|
|
213
|
-
process.stdout.write(chalk.dim(` tail: --follow (or --cursor ${page.nextCursor})\n`))
|
|
214
|
-
}
|
|
215
|
-
},
|
|
216
|
-
})
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Client-side polling follow (ctx.client exposes no stream transport). Take the
|
|
221
|
-
* max `seq` from each page and pass it back as `cursor` (exclusive cursor) so
|
|
222
|
-
* the next poll only returns new entries.
|
|
223
|
-
*/
|
|
224
|
-
async function followEvents(opts: LogsOpts): Promise<void> {
|
|
225
|
-
await withKernelClient(opts, async (ctx) => {
|
|
226
|
-
let cursor = opts.cursor !== undefined ? parsePositiveInt('--cursor', opts.cursor) : undefined
|
|
227
|
-
for (;;) {
|
|
228
|
-
const page = await fetchEventsPage(ctx, { ...opts, cursor: cursor?.toString() })
|
|
229
|
-
for (const e of page.entries) printEventLine(e, opts.timing)
|
|
230
|
-
if (typeof page.nextCursor === 'number') cursor = page.nextCursor
|
|
231
|
-
await new Promise((resolve) => setTimeout(resolve, FOLLOW_INTERVAL_MS))
|
|
145
|
+
function acceptRecord(input: unknown, index: number): JournalRecord {
|
|
146
|
+
if (
|
|
147
|
+
!isRecord(input) ||
|
|
148
|
+
!Number.isSafeInteger(input.sequence) ||
|
|
149
|
+
typeof input.timestamp !== 'string' ||
|
|
150
|
+
typeof input.topic !== 'string'
|
|
151
|
+
) {
|
|
152
|
+
throw new TypeError(`Kernel journal record ${index} is invalid`)
|
|
153
|
+
}
|
|
154
|
+
for (const key of ['principal', 'correlationId', 'causationId'] as const) {
|
|
155
|
+
if (input[key] !== undefined && typeof input[key] !== 'string') {
|
|
156
|
+
throw new TypeError(`Kernel journal record ${index}.${key} must be text`)
|
|
232
157
|
}
|
|
158
|
+
}
|
|
159
|
+
return Object.freeze({
|
|
160
|
+
sequence: input.sequence as number,
|
|
161
|
+
timestamp: input.timestamp,
|
|
162
|
+
topic: input.topic,
|
|
163
|
+
payload: input.payload,
|
|
164
|
+
...(input.principal === undefined ? {} : { principal: input.principal as string }),
|
|
165
|
+
...(input.correlationId === undefined ? {} : { correlationId: input.correlationId as string }),
|
|
166
|
+
...(input.causationId === undefined ? {} : { causationId: input.causationId as string }),
|
|
233
167
|
})
|
|
234
168
|
}
|
|
235
169
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
170
|
+
function positiveInteger(flag: string, raw: string): number {
|
|
171
|
+
if (!/^\d+$/.test(raw)) throw new TypeError(`${flag} must be a positive integer`)
|
|
172
|
+
const value = Number.parseInt(raw, 10)
|
|
173
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
174
|
+
throw new TypeError(`${flag} must be a positive integer`)
|
|
175
|
+
}
|
|
176
|
+
return value
|
|
241
177
|
}
|
|
242
178
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
access: chalk.cyan,
|
|
247
|
-
debug: chalk.dim,
|
|
179
|
+
function nonEmpty(input: string | undefined): string | undefined {
|
|
180
|
+
const value = input?.trim()
|
|
181
|
+
return value ? value : undefined
|
|
248
182
|
}
|
|
249
183
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
let host = ref.trim()
|
|
253
|
-
if (host.includes('://')) {
|
|
254
|
-
try {
|
|
255
|
-
host = new URL(host).hostname
|
|
256
|
-
} catch {
|
|
257
|
-
// keep the raw value — the call will fail loud with the name
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return host.includes('.') ? (host.split('.')[0] ?? host) : host
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
async function runService(opts: LogsOpts): Promise<void> {
|
|
264
|
-
const name = parseServiceName(opts.service as string)
|
|
265
|
-
const origin = opts.servicesOrigin ?? DEFAULT_SERVICES_ORIGIN
|
|
266
|
-
const tail = opts.tail !== undefined ? Number(opts.tail) : undefined
|
|
267
|
-
if (tail !== undefined && (!Number.isInteger(tail) || tail <= 0)) {
|
|
268
|
-
throw new Error(`--tail needs a positive integer, got "${opts.tail}"`)
|
|
269
|
-
}
|
|
270
|
-
const result = await withSpinner(`Fetching logs for ${name}`, !isMachine(opts), () =>
|
|
271
|
-
withKernelClient(
|
|
272
|
-
opts,
|
|
273
|
-
async (ctx) =>
|
|
274
|
-
(await ctx.client.call(
|
|
275
|
-
`/${origin}/services/${name}::logs`,
|
|
276
|
-
tail !== undefined ? { tail } : {},
|
|
277
|
-
)) as ServiceLogs,
|
|
278
|
-
),
|
|
279
|
-
)
|
|
280
|
-
if (isMachine(opts)) {
|
|
281
|
-
output(result, opts)
|
|
282
|
-
return
|
|
283
|
-
}
|
|
284
|
-
if (result.lines.length === 0) {
|
|
285
|
-
console.log(chalk.dim(`no log lines captured yet for ${result.name}`))
|
|
286
|
-
return
|
|
287
|
-
}
|
|
288
|
-
for (const entry of result.lines) {
|
|
289
|
-
const ts = new Date(entry.ts).toISOString()
|
|
290
|
-
const paint = LEVEL_COLOR[entry.level] ?? ((s: string) => s)
|
|
291
|
-
console.log(`${chalk.dim(ts)} ${paint(entry.level.padEnd(6))} ${entry.line}`)
|
|
292
|
-
}
|
|
184
|
+
function isRecord(input: unknown): input is Record<string, unknown> {
|
|
185
|
+
return input !== null && typeof input === 'object' && !Array.isArray(input)
|
|
293
186
|
}
|
|
294
187
|
|
|
295
188
|
export default {
|
|
296
189
|
name: 'logs',
|
|
297
|
-
description: '
|
|
298
|
-
options: [
|
|
299
|
-
{ flags: '--since <t>', description: 'Events at/after this time (epoch-ms or ISO-8601)' },
|
|
300
|
-
{ flags: '--until <t>', description: 'Events at/before this time (epoch-ms or ISO-8601)' },
|
|
301
|
-
{ flags: '--topic <glob>', description: 'Topic glob (e.g. op:*:failed, graph:node:**)' },
|
|
302
|
-
{ flags: '--principal <id>', description: 'Filter by the triggering identity' },
|
|
303
|
-
{ flags: '--limit <n>', description: `Max entries (default ${DEFAULT_LIMIT})` },
|
|
304
|
-
{ flags: '--cursor <n>', description: 'Start after this journal sequence number' },
|
|
305
|
-
{ flags: '--follow', description: 'Poll for new events (Ctrl-C to stop)' },
|
|
306
|
-
{ flags: '--all', description: 'Include the journal-read syscall ops (hidden by default)' },
|
|
307
|
-
{ flags: '--timing', description: 'Show the per-step dispatch breakdown (auth/authz/exec/…)' },
|
|
308
|
-
{ flags: '--service <name>', description: 'Tail a deployed service log buffer instead' },
|
|
309
|
-
{ flags: '--tail <n>', description: '[--service] lines to return (default 200, max 500)' },
|
|
310
|
-
{
|
|
311
|
-
flags: '--services-origin <origin>',
|
|
312
|
-
description: `[--service] services-domain origin (default ${DEFAULT_SERVICES_ORIGIN})`,
|
|
313
|
-
},
|
|
314
|
-
],
|
|
190
|
+
description: 'Read or follow the authorized Kernel journal',
|
|
315
191
|
afterHelpText: `
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
with the per-step dispatch breakdown (auth/in/authz/resolve/inv/exec/out/fx,
|
|
322
|
-
non-zero only). --follow polls for new entries (client-side, tailing by
|
|
323
|
-
sequence number). The journal-read syscall's own ops are hidden unless --all.
|
|
192
|
+
Behavior:
|
|
193
|
+
Calls the public Kernel journal syscall and emits its { records, cursor }
|
|
194
|
+
page. Topic selection is exact or prefix-based; cursors and timestamps are
|
|
195
|
+
opaque strings owned by the journal backend. --follow reuses one Client Session
|
|
196
|
+
and advances only with the returned cursor.
|
|
324
197
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
domain installed on the target instance.
|
|
198
|
+
Historical event-glob lowering and the application-specific services-domain
|
|
199
|
+
log buffer are not part of the Kernel V2 journal contract.
|
|
328
200
|
|
|
329
201
|
Examples:
|
|
330
|
-
astrale logs -i staging
|
|
331
|
-
astrale logs
|
|
332
|
-
astrale logs
|
|
333
|
-
astrale logs -i staging --since 2026-06-20T00:00:00Z --follow
|
|
334
|
-
astrale logs --service my-notes -i staging --tail 50
|
|
202
|
+
$ astrale logs -i staging --limit 50
|
|
203
|
+
$ astrale logs --topic op:function.failed
|
|
204
|
+
$ astrale logs --topic-prefix op:function. --follow
|
|
335
205
|
`,
|
|
206
|
+
options: [
|
|
207
|
+
{ flags: '--since <timestamp>', description: 'Inclusive journal timestamp lower bound' },
|
|
208
|
+
{ flags: '--until <timestamp>', description: 'Inclusive journal timestamp upper bound' },
|
|
209
|
+
{ flags: '--topic <topic>', description: 'Match one exact topic' },
|
|
210
|
+
{ flags: '--topic-prefix <prefix>', description: 'Match one topic prefix' },
|
|
211
|
+
{ flags: '--principal <id>', description: 'Filter by triggering identity ID' },
|
|
212
|
+
{ flags: '--limit <n>', description: `Maximum records (default: ${DEFAULT_LIMIT})` },
|
|
213
|
+
{ flags: '--cursor <token>', description: 'Resume from an opaque journal cursor' },
|
|
214
|
+
{ flags: '--follow', description: 'Poll using returned cursors until interrupted' },
|
|
215
|
+
],
|
|
336
216
|
action: async (opts: LogsOpts) => {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
await runService(opts)
|
|
340
|
-
} else {
|
|
341
|
-
await runEvents(opts)
|
|
342
|
-
}
|
|
343
|
-
} catch (e) {
|
|
344
|
-
fatal(e)
|
|
345
|
-
}
|
|
217
|
+
if (opts.follow) await follow(opts)
|
|
218
|
+
else await runOnce(opts)
|
|
346
219
|
},
|
|
347
220
|
} satisfies CommandDefinition
|