@astrale-os/cli 0.4.0-alpha.13 → 0.6.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 +24396 -9632
- package/package.json +24 -22
- package/src/command.ts +2 -0
- package/src/commands/__tests__/admin-instance.test.ts +3 -2
- package/src/commands/__tests__/domain-list.test.ts +6 -2
- 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/__tests__/view.test.ts +100 -0
- package/src/commands/call.ts +27 -44
- package/src/commands/describe.ts +57 -58
- package/src/commands/domain/install.ts +9 -9
- package/src/commands/domain/list.ts +2 -2
- package/src/commands/domain/publish.ts +3 -3
- package/src/commands/get.ts +48 -23
- package/src/commands/identity/register.ts +27 -33
- package/src/commands/instance/active.ts +2 -2
- package/src/commands/instance/create.ts +1 -1
- package/src/commands/instance/delete.ts +3 -3
- package/src/commands/instance/list.ts +8 -3
- package/src/commands/instance/status.ts +4 -3
- package/src/commands/instance/use.ts +2 -2
- 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 +307 -20
- package/src/commands/session/analyze.ts +50 -0
- package/src/commands/session/list.ts +36 -0
- package/src/commands/token.ts +4 -7
- package/src/commands/view-serve.ts +26 -0
- package/src/commands/view.ts +614 -0
- package/src/connect-core.test.ts +42 -0
- package/src/connect-core.ts +53 -0
- package/src/kernel/__tests__/auth.test.ts +1 -0
- package/src/kernel/__tests__/expand.test.ts +123 -0
- package/src/kernel/client.ts +33 -37
- package/src/kernel/expand.ts +55 -61
- package/src/kernel/graph.ts +96 -0
- package/src/kernel/index.ts +18 -3
- package/src/kernel/options.ts +1 -1
- package/src/kernel/run.ts +0 -13
- package/src/lib/__tests__/instance-target.test.ts +34 -0
- package/src/lib/__tests__/table.test.ts +1 -1
- package/src/lib/__tests__/view-open-intent.test.ts +308 -0
- package/src/lib/__tests__/view-snapshot.test.ts +77 -0
- package/src/lib/admin-domain.ts +8 -4
- package/src/lib/admin-instance.ts +5 -1
- package/src/lib/config.ts +1 -0
- package/src/lib/domain-identity.ts +1 -1
- package/src/lib/instance-target.ts +5 -0
- package/src/lib/instance.ts +11 -0
- package/src/lib/log.ts +12 -2
- package/src/lib/login-flow.ts +41 -4
- package/src/lib/provision-instance.ts +2 -2
- package/src/lib/self.ts +1 -3
- package/src/lib/view/open-intent.ts +97 -0
- package/src/lib/view/resolve.ts +104 -0
- package/src/lib/view/server.ts +294 -0
- package/src/lib/view/session.ts +123 -0
- package/src/lib/view/snapshot.ts +101 -0
- package/src/program.ts +16 -3
- package/src/registry.ts +1 -1
- package/src/setup/render.ts +1 -3
- package/src/setup/steps/instance.ts +2 -2
- package/src/telemetry/__tests__/gate.test.ts +63 -0
- package/src/telemetry/__tests__/recorder.test.ts +110 -0
- package/src/telemetry/__tests__/redact.test.ts +79 -0
- package/src/telemetry/__tests__/session.test.ts +166 -0
- package/src/telemetry/__tests__/trigger.test.ts +49 -0
- package/src/telemetry/adapters/__tests__/claude-code.test.ts +89 -0
- package/src/telemetry/adapters/__tests__/codex.test.ts +138 -0
- package/src/telemetry/adapters/__tests__/index.test.ts +88 -0
- package/src/telemetry/adapters/claude-code.ts +90 -0
- package/src/telemetry/adapters/codex.ts +160 -0
- package/src/telemetry/adapters/index.ts +45 -0
- package/src/telemetry/adapters/types.ts +21 -0
- package/src/telemetry/analyze.ts +227 -0
- package/src/telemetry/gate.ts +79 -0
- package/src/telemetry/recorder.ts +57 -0
- package/src/telemetry/redact.ts +53 -0
- package/src/telemetry/session.ts +88 -0
- package/src/telemetry/settings.ts +26 -0
- package/src/telemetry/store.ts +91 -0
- package/src/telemetry/trigger.ts +119 -0
- package/src/telemetry/types.ts +64 -0
- package/studio/client/dist/assets/index-CyN5G8IA.js +109 -0
- package/studio/client/dist/assets/index-DKKMHBBC.css +1 -0
- package/studio/client/dist/index.html +2 -2
- package/studio/server/agent/ask.ts +7 -2
- package/studio/server/agent/claude.ts +15 -3
- package/studio/server/agent/runner.ts +4 -1
- package/studio/server/agent/session-id.ts +13 -0
- package/studio/server/api.ts +50 -32
- package/studio/server/cache.ts +17 -6
- package/studio/server/client-package.test.ts +147 -0
- package/studio/server/client-package.ts +242 -0
- package/studio/server/index.ts +13 -0
- package/studio/server/introspect/anatomy-extras.test.ts +91 -0
- package/studio/server/introspect/anatomy-extras.ts +48 -49
- package/studio/server/introspect/anatomy.ts +7 -7
- package/studio/server/introspect/overlay-tsmorph.test.ts +104 -0
- package/studio/server/introspect/overlay-tsmorph.ts +230 -72
- package/studio/server/state/harness-gateway.ts +12 -3
- package/studio/server/state/harness-token.ts +0 -0
- package/studio/server/state/views.test.ts +90 -0
- package/studio/server/state/views.ts +396 -99
- package/studio/server/state/visibility.ts +10 -6
- package/studio/server/view-dev-server.test.ts +111 -0
- package/studio/server/view-dev-server.ts +372 -0
- package/studio/shared/types.ts +57 -10
- package/viewer/dist/index.html +93 -0
- package/viewer/dist/main.js +71 -0
- 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/call.ts
CHANGED
|
@@ -3,7 +3,14 @@ import { K } from '@astrale-os/kernel-core'
|
|
|
3
3
|
import type { CommandDefinition } from '../command'
|
|
4
4
|
import type { CallCommandOpts, ClientContext, SelfExpansionMeta } from '../kernel'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
bindGraph,
|
|
8
|
+
buildSelfContext,
|
|
9
|
+
nodeProp,
|
|
10
|
+
resolveSelfIdLazy,
|
|
11
|
+
runKernelCommand,
|
|
12
|
+
withSelfHint,
|
|
13
|
+
} from '../kernel'
|
|
7
14
|
import { presentBinary, type BinaryLike } from '../lib/binary'
|
|
8
15
|
import { log } from '../lib/log'
|
|
9
16
|
import { output, present } from '../lib/output'
|
|
@@ -19,12 +26,8 @@ export async function callCommand(
|
|
|
19
26
|
rawParams: string[],
|
|
20
27
|
opts: CallOpts,
|
|
21
28
|
): Promise<void> {
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// missing a cached registration (persisted, so it's local afterwards).
|
|
25
|
-
// Throws a typed SelfRefusalError (manager, instance-signed, …) which we
|
|
26
|
-
// surface as a fatal CLI error. Runs BEFORE `--describe` so users get the
|
|
27
|
-
// typed refusal instead of a generic NotFoundError from the kernel.
|
|
29
|
+
// Expand @self before describe/execute so refusals are typed and stale IdP
|
|
30
|
+
// registrations can refresh through whoami.
|
|
28
31
|
let expandedPath = path
|
|
29
32
|
let expandedRaw = rawParams
|
|
30
33
|
let selfMeta: SelfExpansionMeta | undefined
|
|
@@ -35,9 +38,7 @@ export async function callCommand(
|
|
|
35
38
|
const selfId = await resolveSelfIdLazy(selfCtx, opts)
|
|
36
39
|
expandedPath = expandSelfReferences(path, selfId)
|
|
37
40
|
expandedRaw = rawParams.map((p) => expandSelfReferences(p, selfId))
|
|
38
|
-
//
|
|
39
|
-
// hint in `formatKernelError` is just as useful when `@self` lived in
|
|
40
|
-
// a param (`node=@self`) as when it was in the path head.
|
|
41
|
+
// Stale-registration hints apply when @self appears in either path or params.
|
|
41
42
|
const rawMutated = expandedRaw.some((p, i) => p !== rawParams[i])
|
|
42
43
|
if (expandedPath !== path || rawMutated) {
|
|
43
44
|
selfMeta = {
|
|
@@ -80,14 +81,7 @@ export async function callCommand(
|
|
|
80
81
|
opts,
|
|
81
82
|
label: expandedPath,
|
|
82
83
|
fn: async (ctx): Promise<CallResult> => {
|
|
83
|
-
//
|
|
84
|
-
// the call to a redirect carrying the worker's URL + `iss`; the session
|
|
85
|
-
// follows it, minting a worker-scoped delegation for that `iss` (the
|
|
86
|
-
// delegation cache wired in `client.ts`). No client-side binding lookup.
|
|
87
|
-
//
|
|
88
|
-
// The one thing the reactive path can't discover after dispatch is a
|
|
89
|
-
// binary output mode (a JSON decode would corrupt the bytes), so detect
|
|
90
|
-
// it up front and route to the binary transport — which also auto-follows.
|
|
84
|
+
// Binary outputs must be routed before dispatch; JSON decoding would corrupt bytes.
|
|
91
85
|
if (await isBinaryOutput(ctx, expandedPath)) {
|
|
92
86
|
const response = await withSelfHint(() => ctx.client.binary(expandedPath, params), selfMeta)
|
|
93
87
|
return { kind: 'binary', response }
|
|
@@ -106,26 +100,15 @@ export async function callCommand(
|
|
|
106
100
|
}
|
|
107
101
|
|
|
108
102
|
/**
|
|
109
|
-
* Best-effort
|
|
110
|
-
*
|
|
111
|
-
* decode and corrupt the bytes), and the client can't discover that after
|
|
112
|
-
* dispatch. For a static path the path IS the Function node — read `output`
|
|
113
|
-
* off it via `::get`. For an instance-method path (`<node>::method`) the
|
|
114
|
-
* Function node isn't addressable that way: resolve the instance's class
|
|
115
|
-
* first, then probe the class's method node (`<classPath>:method`). An
|
|
116
|
-
* interface-hosted instance method still escapes the probe (its Function node
|
|
117
|
-
* hangs off the interface, not the class) — that and any other failure returns
|
|
118
|
-
* false and falls through to the value path, letting the kernel surface the
|
|
119
|
-
* real error. `::get` is a kernel syscall (same origin) so it neither
|
|
120
|
-
* redirects nor triggers delegation.
|
|
103
|
+
* Best-effort binary preflight. Static paths read the Function node directly;
|
|
104
|
+
* instance paths probe the class method node and fall back to value transport on miss.
|
|
121
105
|
*/
|
|
122
106
|
async function isBinaryOutput(ctx: ClientContext, path: string): Promise<boolean> {
|
|
123
107
|
try {
|
|
124
108
|
const target = path.includes('::') ? await instanceMethodNodePath(ctx, path) : path
|
|
125
109
|
if (!target) return false
|
|
126
|
-
const node =
|
|
127
|
-
|
|
128
|
-
} | null
|
|
110
|
+
const node = await bindGraph(ctx).get(target)
|
|
111
|
+
// Props travel under fully-qualified keys; `output.key` IS that key.
|
|
129
112
|
return node?.props?.[K.$.i('Function').output.key] === 'binary'
|
|
130
113
|
} catch {
|
|
131
114
|
return false
|
|
@@ -141,22 +124,23 @@ async function instanceMethodNodePath(
|
|
|
141
124
|
const source = path.slice(0, sep)
|
|
142
125
|
const method = path.slice(sep + 2)
|
|
143
126
|
if (!source || !method) return undefined
|
|
144
|
-
const node =
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
return node?.class ? `${node.class}:${method}` : undefined
|
|
127
|
+
const node = await bindGraph(ctx).get(source)
|
|
128
|
+
// ClassPath + method name forms the class-owned Function MethodPath.
|
|
129
|
+
return node?.class ? `${node.class.raw}:${method}` : undefined
|
|
148
130
|
}
|
|
149
131
|
|
|
150
132
|
async function describeOperation(path: string, opts: CallOpts): Promise<void> {
|
|
151
|
-
await runKernelCommand
|
|
133
|
+
await runKernelCommand({
|
|
152
134
|
opts,
|
|
153
135
|
label: `Schema for ${path}`,
|
|
154
|
-
|
|
136
|
+
// The Function node carries the schemas as props (function.get depth:0).
|
|
137
|
+
fn: async (ctx) => await bindGraph(ctx).get(path),
|
|
155
138
|
format: (node, fmtOpts) => {
|
|
156
|
-
const
|
|
139
|
+
const input = nodeProp(node, 'inputSchema')
|
|
140
|
+
const outputSchema = nodeProp(node, 'outputSchema')
|
|
157
141
|
const schema: Record<string, unknown> = {}
|
|
158
|
-
if (
|
|
159
|
-
if (
|
|
142
|
+
if (input) schema.input = tryParseJson(input)
|
|
143
|
+
if (outputSchema) schema.output = tryParseJson(outputSchema)
|
|
160
144
|
output(Object.keys(schema).length > 0 ? schema : node, fmtOpts)
|
|
161
145
|
},
|
|
162
146
|
})
|
|
@@ -277,8 +261,7 @@ Examples:
|
|
|
277
261
|
$ astrale call /:host.astrale.ai:class.KernelInstance:list
|
|
278
262
|
$ astrale call /:blog.acme.com:class.Author:list limit=10
|
|
279
263
|
$ astrale call '@self::deactivate'
|
|
280
|
-
$ astrale call /:
|
|
281
|
-
-d "$(cat spec.json)"
|
|
264
|
+
$ astrale call /:shell.astrale.ai:class.Application:available --json
|
|
282
265
|
`,
|
|
283
266
|
arguments: [
|
|
284
267
|
{
|
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
|
|
@@ -7,8 +7,8 @@ import type { KernelCommandOpts } from '../../kernel'
|
|
|
7
7
|
import { AstraleError } from '../../errors'
|
|
8
8
|
import { runKernelCommand } from '../../kernel'
|
|
9
9
|
import { withAdminKernelClient } from '../../kernel/client'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { adminDomainMethod, type DomainInfo } from '../../lib/admin-domain'
|
|
11
|
+
import { adminInstanceMethod, type InstanceInfo } from '../../lib/admin-instance'
|
|
12
12
|
import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
|
|
13
13
|
import { getActive } from '../../lib/instance'
|
|
14
14
|
import { fatal, log, withSpinner } from '../../lib/log'
|
|
@@ -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
|
|
@@ -125,7 +125,7 @@ async function installViaAdmin(target: string | undefined, opts: InstallOpts): P
|
|
|
125
125
|
|
|
126
126
|
try {
|
|
127
127
|
await withAdminKernelClient(adminOpts, async (ctx) => {
|
|
128
|
-
const instances = (await ctx.client.call(
|
|
128
|
+
const instances = (await ctx.client.call(adminInstanceMethod('list'), {})) as InstanceInfo[]
|
|
129
129
|
|
|
130
130
|
const ref = await resolveDomainRef(ctx, target, interactive)
|
|
131
131
|
const slug = await resolveTargetSlug(opts, target, interactive, instances)
|
|
@@ -145,7 +145,7 @@ async function installViaAdmin(target: string | undefined, opts: InstallOpts): P
|
|
|
145
145
|
`Installing ${label} on ${match.slug}`,
|
|
146
146
|
!isMachine(opts),
|
|
147
147
|
() =>
|
|
148
|
-
ctx.client.call(
|
|
148
|
+
ctx.client.call(adminDomainMethod('install'), {
|
|
149
149
|
instanceId: match.slug,
|
|
150
150
|
...ref,
|
|
151
151
|
}) as Promise<DomainInstallResult>,
|
|
@@ -190,7 +190,7 @@ async function resolveDomainRef(
|
|
|
190
190
|
if (target) return domainRefFromTarget(target)
|
|
191
191
|
if (!interactive) throw new AstraleError('MISSING_ARG', 'No domain given.')
|
|
192
192
|
|
|
193
|
-
const catalog = (await ctx.client.call(
|
|
193
|
+
const catalog = (await ctx.client.call(adminDomainMethod('list'), {})) as DomainInfo[]
|
|
194
194
|
const installable = catalog.filter((d) => d.url)
|
|
195
195
|
if (installable.length === 0) {
|
|
196
196
|
throw new AstraleError(
|
|
@@ -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,
|
|
@@ -5,7 +5,7 @@ import type { KernelCommandOpts } from '../../kernel'
|
|
|
5
5
|
import type { ListProjection, RawOutputOpts } from '../../lib/output'
|
|
6
6
|
|
|
7
7
|
import { withAdminKernelClient } from '../../kernel/client'
|
|
8
|
-
import {
|
|
8
|
+
import { adminDomainMethod, type DomainInfo } from '../../lib/admin-domain'
|
|
9
9
|
import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
|
|
10
10
|
import { fatal, withSpinner } from '../../lib/log'
|
|
11
11
|
import { isMachine, presentList } from '../../lib/output'
|
|
@@ -102,7 +102,7 @@ Examples:
|
|
|
102
102
|
async (): Promise<DomainRow[]> => {
|
|
103
103
|
const list = await withAdminKernelClient(
|
|
104
104
|
opts,
|
|
105
|
-
async (ctx) => (await ctx.client.call(
|
|
105
|
+
async (ctx) => (await ctx.client.call(adminDomainMethod('list'), {})) as DomainInfo[],
|
|
106
106
|
)
|
|
107
107
|
const filtered = opts.defaultOnly ? list.filter((d) => d.installByDefault) : list
|
|
108
108
|
filtered.sort(byDefaultThenName)
|
|
@@ -4,7 +4,7 @@ import type { CommandDefinition } from '../../command'
|
|
|
4
4
|
import type { KernelCommandOpts } from '../../kernel'
|
|
5
5
|
|
|
6
6
|
import { withAdminKernelClient } from '../../kernel/client'
|
|
7
|
-
import {
|
|
7
|
+
import { adminDomainMethod, type DomainInfo } from '../../lib/admin-domain'
|
|
8
8
|
import { ADMIN_TARGET_OPTIONS, type AdminTargetCommandOpts } from '../../lib/admin-target'
|
|
9
9
|
import { fatal, withSpinner } from '../../lib/log'
|
|
10
10
|
import { isMachine, output } from '../../lib/output'
|
|
@@ -115,7 +115,7 @@ Examples:
|
|
|
115
115
|
// silently bumping `updatedAt`. `info` throws when absent → treat as
|
|
116
116
|
// a fresh publish.
|
|
117
117
|
const existing = (await ctx.client
|
|
118
|
-
.call(
|
|
118
|
+
.call(adminDomainMethod('info'), { origin })
|
|
119
119
|
.catch(() => null)) as DomainInfo | null
|
|
120
120
|
if (
|
|
121
121
|
existing &&
|
|
@@ -127,7 +127,7 @@ Examples:
|
|
|
127
127
|
) {
|
|
128
128
|
return { entry: existing, changed: false as const, isNew: false }
|
|
129
129
|
}
|
|
130
|
-
const entry = (await ctx.client.call(
|
|
130
|
+
const entry = (await ctx.client.call(adminDomainMethod('publish'), {
|
|
131
131
|
origin,
|
|
132
132
|
name,
|
|
133
133
|
url: publicUrl,
|
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
|