@astrale-os/cli 0.4.0-alpha.13 → 0.5.0-alpha.0
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/LICENSE +202 -0
- package/README.md +2 -2
- package/THIRD-PARTY-NOTICES.md +27 -0
- package/dist/astrale.js +8687 -6398
- package/package.json +19 -21
- package/src/commands/__tests__/help-contract.test.ts +27 -14
- package/src/commands/__tests__/install-identity-override.test.ts +2 -2
- package/src/commands/__tests__/ls.test.ts +1 -1
- package/src/commands/__tests__/read-commands.test.ts +201 -0
- package/src/commands/call.ts +27 -44
- package/src/commands/describe.ts +57 -58
- package/src/commands/domain/install.ts +4 -4
- package/src/commands/get.ts +48 -23
- package/src/commands/identity/register.ts +27 -33
- package/src/commands/logs.ts +8 -8
- package/src/commands/ls.ts +77 -55
- package/src/commands/mutate.ts +191 -0
- package/src/commands/query.ts +288 -20
- package/src/commands/token.ts +4 -7
- package/src/kernel/__tests__/expand.test.ts +123 -0
- package/src/kernel/client.ts +3 -13
- package/src/kernel/expand.ts +52 -59
- package/src/kernel/graph.ts +96 -0
- package/src/kernel/index.ts +11 -2
- package/src/kernel/run.ts +0 -13
- package/src/lib/__tests__/table.test.ts +1 -1
- package/src/lib/admin-domain.ts +3 -3
- package/src/lib/domain-identity.ts +1 -1
- package/src/lib/self.ts +1 -3
- package/src/program.ts +5 -3
- package/src/setup/render.ts +1 -3
- package/studio/client/dist/assets/index-BcejyJpa.css +1 -0
- package/studio/client/dist/assets/index-Cqz3Oy_B.js +179 -0
- package/studio/client/dist/index.html +2 -2
- package/studio/server/agent/ask.ts +5 -1
- package/studio/server/agent/claude.ts +15 -3
- package/studio/server/api.ts +7 -7
- package/studio/server/introspect/overlay-tsmorph.ts +108 -50
- package/studio/server/state/harness-gateway.ts +12 -3
- package/studio/server/state/harness-token.ts +0 -0
- package/studio/server/state/visibility.ts +5 -1
- package/src/kernel/__tests__/remote-routing.test.ts +0 -70
- package/src/kernel/remote-routing.ts +0 -88
- package/studio/client/dist/assets/index-DOwzZAEK.css +0 -1
- package/studio/client/dist/assets/index-wtU0Zxhy.js +0 -183
- package/studio/tsconfig.json +0 -23
package/src/commands/describe.ts
CHANGED
|
@@ -2,20 +2,21 @@ import { ClassPath } from '@astrale-os/kernel-core/domain'
|
|
|
2
2
|
import chalk from 'chalk'
|
|
3
3
|
|
|
4
4
|
import type { CommandDefinition } from '../command'
|
|
5
|
-
import type { KernelCommandOpts } from '../kernel'
|
|
6
|
-
|
|
7
|
-
import {
|
|
5
|
+
import type { GraphNode, KernelCommandOpts } from '../kernel'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
bindGraph,
|
|
9
|
+
expandSelfInPath,
|
|
10
|
+
nodeProp,
|
|
11
|
+
runKernelCommand,
|
|
12
|
+
splitRoot,
|
|
13
|
+
unqualifyKey,
|
|
14
|
+
withSelfHint,
|
|
15
|
+
} from '../kernel'
|
|
8
16
|
import { log } from '../lib/log'
|
|
9
17
|
import { output } from '../lib/output'
|
|
10
18
|
|
|
11
|
-
type
|
|
12
|
-
id?: string
|
|
13
|
-
slug?: string
|
|
14
|
-
class?: string
|
|
15
|
-
properties?: Record<string, unknown>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
type DescribeResult = { node: NodeItem; children: NodeItem[] }
|
|
19
|
+
type DescribeResult = { node: GraphNode | undefined; children: GraphNode[] }
|
|
19
20
|
|
|
20
21
|
// Commander turns `--no-schema` into `schema: false`.
|
|
21
22
|
type DescribeOpts = KernelCommandOpts & { schema?: boolean }
|
|
@@ -32,27 +33,18 @@ export async function describeCommand(path: string, opts: DescribeOpts): Promise
|
|
|
32
33
|
await runKernelCommand<DescribeResult>({
|
|
33
34
|
opts,
|
|
34
35
|
label: expandedPath,
|
|
36
|
+
// One function.get depth:1 = the node AND its children (was ::get +
|
|
37
|
+
// ::listChildren). Function-class children are the node's operations.
|
|
35
38
|
fn: async (ctx) => {
|
|
36
|
-
const
|
|
37
|
-
() => ctx.
|
|
39
|
+
const result = await withSelfHint(
|
|
40
|
+
() => bindGraph(ctx).query((q) => q.from(expandedPath).children()),
|
|
38
41
|
meta,
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const result = await withSelfHint(
|
|
44
|
-
() => ctx.client.call(`${expandedPath}::listChildren`, {}),
|
|
45
|
-
meta,
|
|
46
|
-
)
|
|
47
|
-
children = extractItems<NodeItem>(result)
|
|
48
|
-
} catch {
|
|
49
|
-
// Node may have no children (leaf node)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return { node, children }
|
|
42
|
+
)
|
|
43
|
+
const { root, children } = splitRoot(result.wire.nodes, expandedPath)
|
|
44
|
+
return { node: root, children }
|
|
53
45
|
},
|
|
54
46
|
format: (result, fmtOpts, isRaw) => {
|
|
55
|
-
const shown = opts.schema === false ?
|
|
47
|
+
const shown = opts.schema === false ? stripSchema(result) : result
|
|
56
48
|
if (isRaw) {
|
|
57
49
|
output(shown, fmtOpts)
|
|
58
50
|
return
|
|
@@ -62,23 +54,21 @@ export async function describeCommand(path: string, opts: DescribeOpts): Promise
|
|
|
62
54
|
})
|
|
63
55
|
}
|
|
64
56
|
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return { ...result, node: { ...result.node, properties: rest } }
|
|
57
|
+
function stripSchema(result: DescribeResult): DescribeResult {
|
|
58
|
+
if (!result.node) return result
|
|
59
|
+
const entries = Object.entries(result.node.props).filter(([k]) => unqualifyKey(k) !== 'schema')
|
|
60
|
+
return { ...result, node: { ...result.node, props: Object.fromEntries(entries) } }
|
|
70
61
|
}
|
|
71
62
|
|
|
72
63
|
// ── Pretty-print ────────────────────────────────────────────
|
|
73
64
|
|
|
74
65
|
function printDescribe({ node, children }: DescribeResult, path: string): void {
|
|
75
66
|
const kind = classNameOf(node) ?? 'Node'
|
|
76
|
-
const
|
|
77
|
-
console.log(` ${chalk.bold.cyan(
|
|
67
|
+
const name = basename(path) || path
|
|
68
|
+
console.log(` ${chalk.bold.cyan(name)} ${chalk.dim(`(${kind})`)}`)
|
|
78
69
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
70
|
+
const description = nodeProp(node, 'description')
|
|
71
|
+
if (description) console.log(` ${chalk.dim(String(description))}`)
|
|
82
72
|
console.log('')
|
|
83
73
|
|
|
84
74
|
const operations = children.filter(isFunction)
|
|
@@ -86,12 +76,13 @@ function printDescribe({ node, children }: DescribeResult, path: string): void {
|
|
|
86
76
|
|
|
87
77
|
if (operations.length > 0) {
|
|
88
78
|
console.log(` ${chalk.bold('Operations:')}`)
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
}
|
|
79
|
+
const names = operations.map((o) => basename(o.path))
|
|
80
|
+
const slugW = Math.max(4, ...names.map((n) => n.length))
|
|
81
|
+
operations.forEach((op, i) => {
|
|
82
|
+
const opName = names[i].padEnd(slugW)
|
|
83
|
+
const schema = formatInputSchema(op)
|
|
84
|
+
console.log(` ${chalk.green(opName)} ${chalk.dim(schema)}`)
|
|
85
|
+
})
|
|
95
86
|
console.log('')
|
|
96
87
|
}
|
|
97
88
|
|
|
@@ -99,7 +90,7 @@ function printDescribe({ node, children }: DescribeResult, path: string): void {
|
|
|
99
90
|
console.log(` ${chalk.bold('Children:')}`)
|
|
100
91
|
for (const child of otherChildren) {
|
|
101
92
|
const childKind = classNameOf(child) ?? '?'
|
|
102
|
-
console.log(` ${chalk.cyan(child.
|
|
93
|
+
console.log(` ${chalk.cyan(basename(child.path) || '?')} ${chalk.dim(childKind)}`)
|
|
103
94
|
}
|
|
104
95
|
console.log('')
|
|
105
96
|
}
|
|
@@ -109,18 +100,26 @@ function printDescribe({ node, children }: DescribeResult, path: string): void {
|
|
|
109
100
|
}
|
|
110
101
|
}
|
|
111
102
|
|
|
103
|
+
/** `/a/b/c` → `c`; `/` → `/`. */
|
|
104
|
+
function basename(path?: string): string {
|
|
105
|
+
if (!path || path === '/') return path ?? ''
|
|
106
|
+
return path.slice(path.lastIndexOf('/') + 1)
|
|
107
|
+
}
|
|
108
|
+
|
|
112
109
|
/** Short class name from the contract field `class` (a serialized ClassPath). */
|
|
113
|
-
function classNameOf(item:
|
|
114
|
-
return item
|
|
110
|
+
function classNameOf(item: GraphNode | undefined): string | undefined {
|
|
111
|
+
return item?.class ? (ClassPath.tryParse(item.class)?.className ?? undefined) : undefined
|
|
115
112
|
}
|
|
116
113
|
|
|
117
|
-
function isFunction(item:
|
|
114
|
+
function isFunction(item: GraphNode): boolean {
|
|
118
115
|
return classNameOf(item) === 'Function'
|
|
119
116
|
}
|
|
120
117
|
|
|
121
|
-
function printProperties(node:
|
|
122
|
-
|
|
123
|
-
const entries = Object.entries(props)
|
|
118
|
+
function printProperties(node: GraphNode | undefined): void {
|
|
119
|
+
if (!node) return
|
|
120
|
+
const entries = Object.entries(node.props)
|
|
121
|
+
.map(([k, v]) => [unqualifyKey(k), v] as const)
|
|
122
|
+
.filter(([k]) => k !== '__labels' && k !== 'id')
|
|
124
123
|
if (entries.length === 0) return
|
|
125
124
|
|
|
126
125
|
console.log(` ${chalk.bold('Properties:')}`)
|
|
@@ -130,9 +129,8 @@ function printProperties(node: NodeItem): void {
|
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
131
|
|
|
133
|
-
function formatInputSchema(
|
|
134
|
-
|
|
135
|
-
const raw = properties.inputSchema
|
|
132
|
+
function formatInputSchema(node: GraphNode): string {
|
|
133
|
+
const raw = nodeProp(node, 'inputSchema')
|
|
136
134
|
if (!raw) return '{}'
|
|
137
135
|
|
|
138
136
|
try {
|
|
@@ -160,9 +158,10 @@ export default {
|
|
|
160
158
|
description: 'Describe a node: its kind, operations, children, and schemas',
|
|
161
159
|
afterHelpText: `
|
|
162
160
|
Behavior:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
161
|
+
One function.get depth:1 — the node plus its children (Function-class
|
|
162
|
+
children are shown as Operations). Raw dump: full properties + children.
|
|
163
|
+
For Domain nodes the properties include a multi-kB serialized 'schema' —
|
|
164
|
+
use --no-schema, and pipe to jq rather than reading by eye.
|
|
166
165
|
|
|
167
166
|
Examples:
|
|
168
167
|
$ astrale describe /kernel.astrale.ai
|
|
@@ -177,6 +176,6 @@ Examples:
|
|
|
177
176
|
},
|
|
178
177
|
],
|
|
179
178
|
action: async (path, opts) => {
|
|
180
|
-
await describeCommand(path as string, opts as
|
|
179
|
+
await describeCommand(path as string, opts as DescribeOpts)
|
|
181
180
|
},
|
|
182
181
|
} satisfies CommandDefinition
|
|
@@ -26,7 +26,7 @@ type DomainInstallResult = {
|
|
|
26
26
|
error?: string | null
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
/** Mirrors the kernel's `
|
|
29
|
+
/** Mirrors the kernel's `Domain.install` return. */
|
|
30
30
|
type DirectInstallResult = { domainId: string; origin: string }
|
|
31
31
|
|
|
32
32
|
type InstallOpts = KernelCommandOpts &
|
|
@@ -50,7 +50,7 @@ Behavior:
|
|
|
50
50
|
interactively. The target instance is the active one, or -i <slug>; it must be
|
|
51
51
|
admin-managed (otherwise the command fails loudly and points you at --direct).
|
|
52
52
|
|
|
53
|
-
--direct: installs a url STRAIGHT onto the instance kernel (
|
|
53
|
+
--direct: installs a url STRAIGHT onto the instance kernel (Domain.install),
|
|
54
54
|
bypassing the admin/catalog. Works on any instance you can authenticate to
|
|
55
55
|
(managed, bookmarked, or local), using your own authority. This is the only
|
|
56
56
|
mode that runs the identity-override consent gate: when the domain's declared
|
|
@@ -260,7 +260,7 @@ async function activeSlug(): Promise<string | undefined> {
|
|
|
260
260
|
// ── Direct path (--direct) ────────────────────────────────────────────────────
|
|
261
261
|
|
|
262
262
|
/**
|
|
263
|
-
* Install a url straight onto the instance kernel (`
|
|
263
|
+
* Install a url straight onto the instance kernel (`Domain.install`),
|
|
264
264
|
* bypassing the admin/catalog — the classic path, with the §5 identity-override
|
|
265
265
|
* consent gate. Works on any instance the caller can authenticate to.
|
|
266
266
|
*/
|
|
@@ -290,7 +290,7 @@ async function installDirect(target: string | undefined, opts: InstallOpts): Pro
|
|
|
290
290
|
opts,
|
|
291
291
|
label: `Installing domain from ${url}`,
|
|
292
292
|
fn: async (ctx) =>
|
|
293
|
-
(await ctx.client.call(K.
|
|
293
|
+
(await ctx.client.call(K.Domain.install.path.method.raw, {
|
|
294
294
|
url,
|
|
295
295
|
...(opts.token ? { token: opts.token } : {}),
|
|
296
296
|
})) as DirectInstallResult,
|
package/src/commands/get.ts
CHANGED
|
@@ -1,29 +1,46 @@
|
|
|
1
1
|
import type { CommandDefinition } from '../command'
|
|
2
2
|
import type { KernelCommandOpts } from '../kernel'
|
|
3
3
|
|
|
4
|
-
import { expandSelfInPath, runKernelCommand, withSelfHint } from '../kernel'
|
|
4
|
+
import { bindGraph, expandSelfInPath, runKernelCommand, withSelfHint } from '../kernel'
|
|
5
5
|
import { log } from '../lib/log'
|
|
6
6
|
import { output } from '../lib/output'
|
|
7
7
|
|
|
8
|
-
type GetOpts = KernelCommandOpts & {
|
|
8
|
+
type GetOpts = KernelCommandOpts & {
|
|
9
|
+
long?: boolean
|
|
10
|
+
}
|
|
9
11
|
|
|
10
12
|
const INTERNAL_KEYS = new Set(['__labels', 'classId'])
|
|
11
13
|
|
|
12
|
-
export async function getCommand(
|
|
13
|
-
|
|
14
|
+
export async function getCommand(pathArg: string, opts: GetOpts): Promise<void> {
|
|
15
|
+
if (!pathArg) {
|
|
16
|
+
log.error('Usage: astrale get <path>')
|
|
17
|
+
process.exit(1)
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let path: string
|
|
14
22
|
let meta
|
|
15
23
|
try {
|
|
16
|
-
;({ path
|
|
24
|
+
;({ path, meta } = await expandSelfInPath(pathArg, opts))
|
|
17
25
|
} catch (e) {
|
|
18
26
|
log.error(e instanceof Error ? e.message : 'Invalid @self expansion')
|
|
19
27
|
process.exit(1)
|
|
28
|
+
return
|
|
20
29
|
}
|
|
30
|
+
|
|
21
31
|
await runKernelCommand({
|
|
22
32
|
opts,
|
|
23
|
-
label:
|
|
24
|
-
fn: (ctx) =>
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
label: 'Node ' + path,
|
|
34
|
+
fn: async (ctx) => {
|
|
35
|
+
const node = await withSelfHint(() => bindGraph(ctx).get(path), meta)
|
|
36
|
+
if (node === null) {
|
|
37
|
+
log.error('node "' + path + '" not found or not visible')
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
return node
|
|
41
|
+
},
|
|
42
|
+
format: (node, fmtOpts) => {
|
|
43
|
+
output(opts.long ? node : cleanNode(node), fmtOpts)
|
|
27
44
|
},
|
|
28
45
|
})
|
|
29
46
|
}
|
|
@@ -40,21 +57,29 @@ function cleanNode(data: unknown): unknown {
|
|
|
40
57
|
|
|
41
58
|
export default {
|
|
42
59
|
name: 'get',
|
|
43
|
-
description: 'Get
|
|
44
|
-
afterHelpText:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
description: 'Get one node by path or id',
|
|
61
|
+
afterHelpText: [
|
|
62
|
+
'',
|
|
63
|
+
'Behavior:',
|
|
64
|
+
' Point read through the kernel-client GraphApi.get shorthand. Accepts exactly',
|
|
65
|
+
' one node path (tree path /domain/class.Name, or an id @nodeId). @self is',
|
|
66
|
+
' expanded before dispatch. Missing and masked nodes share one opaque error:',
|
|
67
|
+
' node "<path>" not found or not visible.',
|
|
68
|
+
'',
|
|
69
|
+
' Output is the historical flat wire node projection { id, class, path, props }.',
|
|
70
|
+
' Add -l to keep the internal fields (__labels, classId). Richer reads -',
|
|
71
|
+
' multiple roots, child expansion, edge expansion, cursors, or full GraphData -',
|
|
72
|
+
' live on astrale query.',
|
|
73
|
+
'',
|
|
74
|
+
'Examples:',
|
|
75
|
+
' $ astrale get /kernel.astrale.ai/class.Root',
|
|
76
|
+
' $ astrale get @abc123 -l',
|
|
77
|
+
' $ astrale get @self --json',
|
|
78
|
+
'',
|
|
79
|
+
].join('\n'),
|
|
53
80
|
arguments: [{ name: 'path', description: 'Node path (/domain/Class) or ID (@nodeId)' }],
|
|
54
|
-
options: [
|
|
55
|
-
{ flags: '-l, --long', description: 'Include all internal fields (__labels, classId)' },
|
|
56
|
-
],
|
|
81
|
+
options: [{ flags: '-l, --long', description: 'Include internal fields (__labels, classId)' }],
|
|
57
82
|
action: async (path, opts) => {
|
|
58
|
-
await getCommand(path as string, opts as
|
|
83
|
+
await getCommand(path as string, opts as GetOpts)
|
|
59
84
|
},
|
|
60
85
|
} satisfies CommandDefinition
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { K } from '@astrale-os/kernel-core'
|
|
2
1
|
import { importJWK, SignJWT, type JWK } from 'jose'
|
|
3
2
|
import { readFile } from 'node:fs/promises'
|
|
4
3
|
|
|
5
4
|
import type { CommandDefinition } from '../../command'
|
|
6
|
-
import type { KernelCommandOpts } from '../../kernel'
|
|
5
|
+
import type { ClientContext, KernelCommandOpts } from '../../kernel'
|
|
7
6
|
|
|
8
|
-
import { runKernelCommand } from '../../kernel'
|
|
7
|
+
import { bindGraph, runKernelCommand } from '../../kernel'
|
|
9
8
|
import { KERNEL_PASSTHROUGH_OPTIONS } from '../../kernel/options'
|
|
10
9
|
import { getIdentity, setRegistration } from '../../lib/identity'
|
|
11
10
|
import { getActive } from '../../lib/instance'
|
|
@@ -13,7 +12,6 @@ import { fileExists, keypairPaths } from '../../lib/keys'
|
|
|
13
12
|
import { fatal, log } from '../../lib/log'
|
|
14
13
|
import { output } from '../../lib/output'
|
|
15
14
|
|
|
16
|
-
type CreateNodeResult = { id: string; path: string }
|
|
17
15
|
type RegisterIdentityResult = { iss: string; sub: string }
|
|
18
16
|
|
|
19
17
|
type RegisterOpts = KernelCommandOpts & {
|
|
@@ -27,37 +25,32 @@ async function readJwk(path: string): Promise<JWK> {
|
|
|
27
25
|
return JSON.parse(raw) as JWK
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
type ChildNode = { class?: string; path?: string }
|
|
31
|
-
|
|
32
28
|
/**
|
|
33
29
|
* Discover the identity-bearer class on the target instance: find the
|
|
34
|
-
* installed
|
|
35
|
-
*
|
|
30
|
+
* installed domain that owns a `User` class (workspace on managed instances)
|
|
31
|
+
* and address it semantically.
|
|
36
32
|
* The bearer class must exist before a non-root identity can be registered;
|
|
37
33
|
* failing here names the real problem instead of surfacing as a confusing
|
|
38
|
-
* permission error on a hardcoded
|
|
34
|
+
* permission error on a hardcoded origin.
|
|
39
35
|
*/
|
|
40
|
-
async function resolveUserClassPath(ctx: {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const domains =
|
|
45
|
-
.filter((
|
|
46
|
-
.map((
|
|
36
|
+
async function resolveUserClassPath(ctx: ClientContext): Promise<string> {
|
|
37
|
+
const graph = bindGraph(ctx)
|
|
38
|
+
// Root's direct children are the installed Domain nodes.
|
|
39
|
+
const result = await graph.children('/')
|
|
40
|
+
const domains = result.nodes
|
|
41
|
+
.filter((n) => n.class.className === 'Domain')
|
|
42
|
+
.map((n) => n.path.raw.replace(/^\//, ''))
|
|
47
43
|
.filter(Boolean)
|
|
48
44
|
for (const origin of domains) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
await ctx.client.call(`/${origin}/class.User::get`, {})
|
|
45
|
+
// The class materializes as a `class.User` Folder under the domain mount;
|
|
46
|
+
// a resolving node means the domain declares it (null when absent).
|
|
47
|
+
if (await graph.get(`/${origin}/class.User`)) {
|
|
53
48
|
return `/:${origin}:class.User`
|
|
54
|
-
} catch {
|
|
55
|
-
// This domain has no User class — try the next one.
|
|
56
49
|
}
|
|
57
50
|
}
|
|
58
51
|
throw new Error(
|
|
59
52
|
'No installed domain declares a `User` class on this instance — registering a ' +
|
|
60
|
-
'non-root identity needs an identity-bearer class (install
|
|
53
|
+
'non-root identity needs an identity-bearer class (install workspace or another ' +
|
|
61
54
|
'domain, or pass --class <classPath> explicitly).',
|
|
62
55
|
)
|
|
63
56
|
}
|
|
@@ -85,7 +78,7 @@ export default {
|
|
|
85
78
|
{
|
|
86
79
|
flags: '--class <classPath>',
|
|
87
80
|
description:
|
|
88
|
-
'Class path of the identity node to create (default:
|
|
81
|
+
'Class path of the identity node to create (default: the installed domain that declares class.User)',
|
|
89
82
|
},
|
|
90
83
|
{
|
|
91
84
|
flags: '--path <nodePath>',
|
|
@@ -127,23 +120,24 @@ export default {
|
|
|
127
120
|
|
|
128
121
|
const classPath = opts.class ?? (await resolveUserClassPath(ctx))
|
|
129
122
|
|
|
130
|
-
// A bare `astrale identity create <name>` carries no profile, but
|
|
131
|
-
// bearer class may require one
|
|
132
|
-
// firstName/lastName) — default both to the identity name so a dev
|
|
123
|
+
// A bare `astrale identity create <name>` carries no profile, but a
|
|
124
|
+
// User bearer class may require one. Default both names so a dev
|
|
133
125
|
// registration works out of the box; `--props` overrides.
|
|
134
126
|
const extraProps = opts.props ? (JSON.parse(opts.props) as Record<string, unknown>) : {}
|
|
135
127
|
const userDefaults = classPath.endsWith(':class.User')
|
|
136
128
|
? { firstName: name, lastName: name }
|
|
137
129
|
: {}
|
|
138
130
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
131
|
+
// One-arm create through function.mutate; the minted node id comes
|
|
132
|
+
// back in createdNodes (keyed by the `at` path).
|
|
133
|
+
const nodeId = await bindGraph(ctx).createNode(classPath, nodePath, {
|
|
134
|
+
'Statused.status': 'creating',
|
|
135
|
+
...userDefaults,
|
|
136
|
+
...extraProps,
|
|
137
|
+
})
|
|
144
138
|
|
|
145
139
|
const bootstrap = await mintBootstrapJwt(privateJwk)
|
|
146
|
-
const result = (await ctx.client.call(`@${
|
|
140
|
+
const result = (await ctx.client.call(`@${nodeId}::registerIdentity`, {
|
|
147
141
|
signingKey: {
|
|
148
142
|
publicKey: { jwk: publicJwk },
|
|
149
143
|
credential: bootstrap,
|
package/src/commands/logs.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { JournalEntry, JournalFilter } from '@astrale-os/kernel-core'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* `astrale logs` — tail the kernel event journal (
|
|
4
|
+
* `astrale logs` — tail the kernel event journal (function.journal) for the target
|
|
5
5
|
* instance. Defaults to the kernel journal syscall
|
|
6
|
-
* `/kernel.astrale.ai/
|
|
6
|
+
* `/kernel.astrale.ai/functions/journal`; `--service <name>` switches to the
|
|
7
7
|
* per-instance `services` domain log buffer (the historical behavior).
|
|
8
8
|
*
|
|
9
9
|
* Target the instance with `-i <instance>` (inherited from withKernelOptions).
|
|
@@ -18,13 +18,13 @@ import { runKernelCommand, withKernelClient } from '../kernel'
|
|
|
18
18
|
import { fatal, withSpinner } from '../lib/log'
|
|
19
19
|
import { isMachine, output, presentList } from '../lib/output'
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const JOURNAL_FN_PATH = '/kernel.astrale.ai/functions/journal'
|
|
22
22
|
const DEFAULT_SERVICES_ORIGIN = 'services.astrale.ai'
|
|
23
23
|
const DEFAULT_LIMIT = 200
|
|
24
24
|
const FOLLOW_INTERVAL_MS = 2000
|
|
25
25
|
// The journal-read syscall journals its own ops; hide them by default so polling
|
|
26
26
|
// doesn't self-pollute the view. `--all` shows them.
|
|
27
|
-
const SELF_READ_PREFIX = 'op:
|
|
27
|
+
const SELF_READ_PREFIX = 'op:function.journal:'
|
|
28
28
|
|
|
29
29
|
// The published @astrale-os/kernel-core@0.5.0 JournalFilter has no `cursor` yet
|
|
30
30
|
// (added on the kernel branch). Type against the augmented shape so the
|
|
@@ -48,7 +48,7 @@ type LogsOpts = KernelCommandOpts & {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
* Kernel journal page. The
|
|
51
|
+
* Kernel journal page. The function.journal syscall returns a BARE `JournalEntry[]`;
|
|
52
52
|
* the client derives the next cursor from the max `seq`. We still model a
|
|
53
53
|
* `nextCursor` field so the TTY footer / incremental tail share one shape, and
|
|
54
54
|
* so a future paged kernel response degrades gracefully.
|
|
@@ -58,7 +58,7 @@ type EventsPage = {
|
|
|
58
58
|
nextCursor?: number | null
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// ──
|
|
61
|
+
// ── function.journal (default) ───────────────────────────────────
|
|
62
62
|
|
|
63
63
|
/** The highest `seq` across entries, or null when empty. */
|
|
64
64
|
function maxSeq(entries: JournalEntry[]): number | null {
|
|
@@ -177,7 +177,7 @@ function eventsProjection(entries: JournalEntry[], showTiming = false): ListProj
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
async function fetchEventsPage(ctx: ClientContext, opts: LogsOpts): Promise<EventsPage> {
|
|
180
|
-
const raw = await ctx.client.call(
|
|
180
|
+
const raw = await ctx.client.call(JOURNAL_FN_PATH, buildEventsParams(opts))
|
|
181
181
|
const page = normalizePage(raw)
|
|
182
182
|
// Strip the journal's own read ops by default (but keep nextCursor past them).
|
|
183
183
|
const entries = opts.all
|
|
@@ -313,7 +313,7 @@ export default {
|
|
|
313
313
|
},
|
|
314
314
|
],
|
|
315
315
|
afterHelpText: `
|
|
316
|
-
Default: tails the kernel event journal via ${
|
|
316
|
+
Default: tails the kernel event journal via ${JOURNAL_FN_PATH} on the target
|
|
317
317
|
instance (-i <instance>). Topics use ':'-segmented globs ('*' one segment,
|
|
318
318
|
'**' zero-or-more). Machine output (--json / pipe) emits the JournalEntry[]
|
|
319
319
|
array; a TTY shows a SEQ/TIME/TOPIC/LATENCY/PRINCIPAL table (LATENCY is the
|