@astrale-os/cli 0.8.1-alpha.2 → 0.8.1-alpha.4
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 +1 -1
- package/dist/astrale.js +206 -160
- package/package.json +1 -1
- package/src/commands/__tests__/call-describe.test.ts +67 -0
- package/src/commands/__tests__/install-direct.test.ts +20 -0
- package/src/commands/__tests__/instance-list-admin.test.ts +26 -0
- package/src/commands/__tests__/logs.test.ts +23 -0
- package/src/commands/__tests__/read-commands.test.ts +0 -12
- package/src/commands/call-describe.ts +131 -0
- package/src/commands/call.ts +17 -21
- package/src/commands/domain/install.ts +39 -13
- package/src/commands/domain/list.ts +1 -1
- package/src/commands/get.ts +3 -3
- package/src/commands/instance/list.ts +36 -3
- package/src/commands/logs.ts +29 -9
- package/src/graph/__tests__/mutation.test.ts +3 -1
- package/src/graph/mutation.ts +15 -0
- package/src/lib/__tests__/command-dx.test.ts +13 -1
- package/src/lib/command-dx.ts +12 -0
- package/src/program/__tests__/program.test.ts +3 -3
- package/src/program/build.ts +1 -3
- package/src/telemetry/__tests__/gate.test.ts +1 -1
- package/src/commands/__tests__/ls.test.ts +0 -29
- package/src/commands/ls.ts +0 -125
package/README.md
CHANGED
|
@@ -110,7 +110,7 @@ Main command groups:
|
|
|
110
110
|
|
|
111
111
|
| Group | What it covers |
|
|
112
112
|
|-------|----------------|
|
|
113
|
-
| Kernel | `
|
|
113
|
+
| Kernel | `get`, `call`, `query`, `describe`, `token` |
|
|
114
114
|
| Context | `status`, `whoami`, `use` |
|
|
115
115
|
| Management | `admin`, `instance`, `identity`, `auth`, `idp`, `update` |
|
|
116
116
|
| Agent | `browser` |
|
package/dist/astrale.js
CHANGED
|
@@ -2576,7 +2576,7 @@ var package_default;
|
|
|
2576
2576
|
var init_package = __esm(() => {
|
|
2577
2577
|
package_default = {
|
|
2578
2578
|
name: "@astrale-os/cli",
|
|
2579
|
-
version: "0.8.1-alpha.
|
|
2579
|
+
version: "0.8.1-alpha.4",
|
|
2580
2580
|
description: "Astrale CLI — connect to existing Astrale kernels",
|
|
2581
2581
|
keywords: [
|
|
2582
2582
|
"astrale",
|
|
@@ -90137,6 +90137,9 @@ var init_mutation2 = __esm(() => {
|
|
|
90137
90137
|
|
|
90138
90138
|
// src/graph/mutation.ts
|
|
90139
90139
|
function prepareMutation(input) {
|
|
90140
|
+
if (isLegacyPatchData(input)) {
|
|
90141
|
+
throw new TypeError("Legacy PatchData { nodes, edges } is not Mutation V3. Author { preconditions, operations } or a canonical astrale.graph.mutation/v3 document.");
|
|
90142
|
+
}
|
|
90140
90143
|
if (isCanonicalCandidate(input))
|
|
90141
90144
|
return MutationAST.decode(input);
|
|
90142
90145
|
return MutationAST.create(input);
|
|
@@ -90144,6 +90147,9 @@ function prepareMutation(input) {
|
|
|
90144
90147
|
function isCanonicalCandidate(input) {
|
|
90145
90148
|
return input !== null && typeof input === "object" && !Array.isArray(input) && (Object.hasOwn(input, "format") || Object.hasOwn(input, "version"));
|
|
90146
90149
|
}
|
|
90150
|
+
function isLegacyPatchData(input) {
|
|
90151
|
+
return input !== null && typeof input === "object" && !Array.isArray(input) && !Object.hasOwn(input, "operations") && (Object.hasOwn(input, "nodes") || Object.hasOwn(input, "edges"));
|
|
90152
|
+
}
|
|
90147
90153
|
var init_mutation3 = __esm(() => {
|
|
90148
90154
|
init_mutation2();
|
|
90149
90155
|
});
|
|
@@ -90308,6 +90314,99 @@ var init_binary4 = __esm(() => {
|
|
|
90308
90314
|
init_output();
|
|
90309
90315
|
});
|
|
90310
90316
|
|
|
90317
|
+
// src/commands/call-describe.ts
|
|
90318
|
+
function describeCallableFromSchema(path5, schema2) {
|
|
90319
|
+
if (path5.ast.anchor.kind !== "domain")
|
|
90320
|
+
return;
|
|
90321
|
+
const origin = path5.ast.anchor.origin;
|
|
90322
|
+
const last = path5.ast.steps.at(-1);
|
|
90323
|
+
const document = asRecord2(schema2);
|
|
90324
|
+
if (document === undefined || last === undefined)
|
|
90325
|
+
return;
|
|
90326
|
+
if (last.kind === "method") {
|
|
90327
|
+
const owner = path5.ast.steps.at(-2);
|
|
90328
|
+
if (owner?.kind === "projection" && (owner.projection.kind === "class" || owner.projection.kind === "interface")) {
|
|
90329
|
+
const bag = asRecord2(owner.projection.kind === "class" ? document.classes : document.interfaces);
|
|
90330
|
+
const definition3 = asRecord2(bag?.[owner.projection.name]);
|
|
90331
|
+
const method = asRecord2(asRecord2(definition3?.methods)?.[last.name]);
|
|
90332
|
+
if (method !== undefined) {
|
|
90333
|
+
return Object.freeze({
|
|
90334
|
+
path: path5.raw,
|
|
90335
|
+
origin,
|
|
90336
|
+
class: owner.projection.name,
|
|
90337
|
+
method: last.name,
|
|
90338
|
+
dispatch: last.dispatch,
|
|
90339
|
+
...callableFields(method)
|
|
90340
|
+
});
|
|
90341
|
+
}
|
|
90342
|
+
}
|
|
90343
|
+
const matches3 = findNamedMethods(document, origin, path5.raw, last.name, last.dispatch);
|
|
90344
|
+
if (matches3.length === 1)
|
|
90345
|
+
return matches3[0];
|
|
90346
|
+
if (matches3.length > 1) {
|
|
90347
|
+
return Object.freeze({
|
|
90348
|
+
path: path5.raw,
|
|
90349
|
+
origin,
|
|
90350
|
+
method: last.name,
|
|
90351
|
+
dispatch: last.dispatch,
|
|
90352
|
+
candidates: Object.freeze(matches3)
|
|
90353
|
+
});
|
|
90354
|
+
}
|
|
90355
|
+
return;
|
|
90356
|
+
}
|
|
90357
|
+
if (last.kind === "projection" && last.projection.kind === "function") {
|
|
90358
|
+
const fn2 = asRecord2(asRecord2(document.functions)?.[last.projection.name]);
|
|
90359
|
+
if (fn2 === undefined)
|
|
90360
|
+
return;
|
|
90361
|
+
return Object.freeze({
|
|
90362
|
+
path: path5.raw,
|
|
90363
|
+
origin,
|
|
90364
|
+
function: last.projection.name,
|
|
90365
|
+
...callableFields(fn2)
|
|
90366
|
+
});
|
|
90367
|
+
}
|
|
90368
|
+
return;
|
|
90369
|
+
}
|
|
90370
|
+
function missingCallableDescription(path5) {
|
|
90371
|
+
return new AstraleError("CALL_DESCRIBE_UNAVAILABLE", `No callable schema is installed for ${path5}.`, "Use a Domain-rooted Path such as /:host.astrale.ai:class.Manager:createInstance. Method Paths are not Function nodes.");
|
|
90372
|
+
}
|
|
90373
|
+
function findNamedMethods(schema2, origin, path5, method, dispatch) {
|
|
90374
|
+
const matches3 = [];
|
|
90375
|
+
for (const bag of [schema2.classes, schema2.interfaces]) {
|
|
90376
|
+
const definitions2 = asRecord2(bag);
|
|
90377
|
+
if (definitions2 === undefined)
|
|
90378
|
+
continue;
|
|
90379
|
+
for (const [className, definition3] of Object.entries(definitions2)) {
|
|
90380
|
+
const methodDef = asRecord2(asRecord2(asRecord2(definition3)?.methods)?.[method]);
|
|
90381
|
+
if (methodDef === undefined)
|
|
90382
|
+
continue;
|
|
90383
|
+
matches3.push(Object.freeze({
|
|
90384
|
+
path: path5,
|
|
90385
|
+
origin,
|
|
90386
|
+
class: className,
|
|
90387
|
+
method,
|
|
90388
|
+
dispatch,
|
|
90389
|
+
...callableFields(methodDef)
|
|
90390
|
+
}));
|
|
90391
|
+
}
|
|
90392
|
+
}
|
|
90393
|
+
return matches3;
|
|
90394
|
+
}
|
|
90395
|
+
function callableFields(value2) {
|
|
90396
|
+
return {
|
|
90397
|
+
...typeof value2.description === "string" ? { description: value2.description } : {},
|
|
90398
|
+
...value2.auth === undefined ? {} : { auth: value2.auth },
|
|
90399
|
+
...value2.input === undefined ? {} : { input: value2.input },
|
|
90400
|
+
...value2.output === undefined ? {} : { output: value2.output }
|
|
90401
|
+
};
|
|
90402
|
+
}
|
|
90403
|
+
function asRecord2(input) {
|
|
90404
|
+
return input !== null && typeof input === "object" && !Array.isArray(input) ? input : undefined;
|
|
90405
|
+
}
|
|
90406
|
+
var init_call_describe = __esm(() => {
|
|
90407
|
+
init_errors2();
|
|
90408
|
+
});
|
|
90409
|
+
|
|
90311
90410
|
// src/commands/call.ts
|
|
90312
90411
|
var exports_call = {};
|
|
90313
90412
|
__export(exports_call, {
|
|
@@ -90374,28 +90473,20 @@ async function describeOperation(path5, opts) {
|
|
|
90374
90473
|
await runKernelCommand({
|
|
90375
90474
|
opts,
|
|
90376
90475
|
label: `Schema for ${path5}`,
|
|
90377
|
-
fn: async ({ graph }) =>
|
|
90378
|
-
|
|
90379
|
-
|
|
90380
|
-
|
|
90381
|
-
|
|
90382
|
-
|
|
90383
|
-
|
|
90384
|
-
if (
|
|
90385
|
-
|
|
90386
|
-
|
|
90387
|
-
}
|
|
90476
|
+
fn: async ({ graph }) => {
|
|
90477
|
+
const parsed = Path.parse(path5);
|
|
90478
|
+
if (parsed.ast.anchor.kind !== "domain") {
|
|
90479
|
+
throw missingCallableDescription(path5);
|
|
90480
|
+
}
|
|
90481
|
+
const domain3 = await graph.getOrThrow(Path.parse(`/:${parsed.ast.anchor.origin}`));
|
|
90482
|
+
const described = describeCallableFromSchema(parsed, nodeProperty(domain3, "schema"));
|
|
90483
|
+
if (described === undefined)
|
|
90484
|
+
throw missingCallableDescription(path5);
|
|
90485
|
+
return described;
|
|
90486
|
+
},
|
|
90487
|
+
format: (schema2, fmtOpts) => output(schema2, fmtOpts)
|
|
90388
90488
|
});
|
|
90389
90489
|
}
|
|
90390
|
-
function tryParseJson(value2) {
|
|
90391
|
-
if (typeof value2 !== "string")
|
|
90392
|
-
return value2;
|
|
90393
|
-
try {
|
|
90394
|
-
return JSON.parse(value2);
|
|
90395
|
-
} catch {
|
|
90396
|
-
return value2;
|
|
90397
|
-
}
|
|
90398
|
-
}
|
|
90399
90490
|
async function parseParams(rawParams, dataFlag) {
|
|
90400
90491
|
if (dataFlag) {
|
|
90401
90492
|
if (rawParams.length > 0) {
|
|
@@ -90471,6 +90562,7 @@ var init_call2 = __esm(() => {
|
|
|
90471
90562
|
init_binary4();
|
|
90472
90563
|
init_log();
|
|
90473
90564
|
init_output();
|
|
90565
|
+
init_call_describe();
|
|
90474
90566
|
PARAM_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
90475
90567
|
call_default = {
|
|
90476
90568
|
name: "call",
|
|
@@ -90490,16 +90582,19 @@ Self-reference:
|
|
|
90490
90582
|
(e.g. via 'astrale get @self --json'). Resolution authenticates to
|
|
90491
90583
|
the selected Kernel and never trusts a local registration or JWT sub.
|
|
90492
90584
|
|
|
90585
|
+
--describe reads the callable's input/output from the installed Domain
|
|
90586
|
+
schema. Method Paths are not Function nodes.
|
|
90587
|
+
|
|
90493
90588
|
Examples:
|
|
90494
|
-
$ astrale call /:host.astrale.ai:class.
|
|
90589
|
+
$ astrale call /:host.astrale.ai:class.Manager:createInstance --describe
|
|
90495
90590
|
$ astrale call /:blog.acme.com:class.Author:list limit=10
|
|
90496
90591
|
$ astrale call '@self::deactivate'
|
|
90497
|
-
$ astrale call /:
|
|
90592
|
+
$ astrale call /:kernel.astrale.ai:function.journal --data '{"limit":5}' --json
|
|
90498
90593
|
`,
|
|
90499
90594
|
arguments: [
|
|
90500
90595
|
{
|
|
90501
90596
|
name: "path",
|
|
90502
|
-
description: "Operation path (e.g., /:host.astrale.ai:class.
|
|
90597
|
+
description: "Operation path (e.g., /:host.astrale.ai:class.Manager:createInstance or /node::method)"
|
|
90503
90598
|
},
|
|
90504
90599
|
{ name: "params...", description: "Params as key=value pairs", required: false }
|
|
90505
90600
|
],
|
|
@@ -90597,7 +90692,7 @@ async function getCommand(target2, opts) {
|
|
|
90597
90692
|
try {
|
|
90598
90693
|
({ path: path5, meta: meta3 } = await expandSelfInPath(target2, opts));
|
|
90599
90694
|
} catch (error52) {
|
|
90600
|
-
|
|
90695
|
+
await formatKernelError(error52, isMachine(opts), undefined, opts.debug);
|
|
90601
90696
|
process.exit(1);
|
|
90602
90697
|
}
|
|
90603
90698
|
await runKernelCommand({
|
|
@@ -90611,7 +90706,7 @@ var get_default;
|
|
|
90611
90706
|
var init_get = __esm(() => {
|
|
90612
90707
|
init_path5();
|
|
90613
90708
|
init_connection2();
|
|
90614
|
-
|
|
90709
|
+
init_errors12();
|
|
90615
90710
|
init_output();
|
|
90616
90711
|
get_default = {
|
|
90617
90712
|
name: "get",
|
|
@@ -90757,115 +90852,6 @@ var init_class3 = __esm(() => {
|
|
|
90757
90852
|
init_class();
|
|
90758
90853
|
});
|
|
90759
90854
|
|
|
90760
|
-
// src/commands/ls.ts
|
|
90761
|
-
var exports_ls = {};
|
|
90762
|
-
__export(exports_ls, {
|
|
90763
|
-
lsCommand: () => lsCommand,
|
|
90764
|
-
listProjection: () => listProjection,
|
|
90765
|
-
displayName: () => displayName,
|
|
90766
|
-
default: () => ls_default
|
|
90767
|
-
});
|
|
90768
|
-
async function lsCommand(source2, opts) {
|
|
90769
|
-
if (opts.edge === undefined) {
|
|
90770
|
-
log.error("ls requires --edge <class>; Kernel V2 has no universal child relation");
|
|
90771
|
-
process.exit(1);
|
|
90772
|
-
}
|
|
90773
|
-
let path5;
|
|
90774
|
-
let meta3;
|
|
90775
|
-
let prepared;
|
|
90776
|
-
try {
|
|
90777
|
-
({ path: path5, meta: meta3 } = await expandSelfInPath(source2, opts));
|
|
90778
|
-
prepared = prepareQuery({
|
|
90779
|
-
sources: [path5],
|
|
90780
|
-
edge: opts.edge,
|
|
90781
|
-
direction: opts.direction,
|
|
90782
|
-
limit: opts.limit,
|
|
90783
|
-
cursor: opts.cursor
|
|
90784
|
-
});
|
|
90785
|
-
} catch (error52) {
|
|
90786
|
-
log.error(error52 instanceof Error ? error52.message : "Invalid edge neighborhood");
|
|
90787
|
-
process.exit(1);
|
|
90788
|
-
}
|
|
90789
|
-
await runKernelCommand({
|
|
90790
|
-
opts,
|
|
90791
|
-
label: `Neighbors of ${path5}`,
|
|
90792
|
-
fn: ({ graph }) => withSelfHint(() => graph.query(prepared.ast, { page: prepared.page }), meta3),
|
|
90793
|
-
format: (response2, format3) => {
|
|
90794
|
-
const graph = completeGraph(response2.result);
|
|
90795
|
-
presentList([...graph.nodes], { ...format3, long: opts.long, quiet: opts.quiet, count: opts.count }, listProjection);
|
|
90796
|
-
if (response2.page.next && !isMachine(format3) && !opts.quiet && !opts.count) {
|
|
90797
|
-
process.stderr.write(` cursor: ${response2.page.next}
|
|
90798
|
-
`);
|
|
90799
|
-
}
|
|
90800
|
-
}
|
|
90801
|
-
});
|
|
90802
|
-
}
|
|
90803
|
-
function completeGraph(result) {
|
|
90804
|
-
if (result.kind !== "graph")
|
|
90805
|
-
throw new TypeError("ls expected one complete graph Query result");
|
|
90806
|
-
return result.graph;
|
|
90807
|
-
}
|
|
90808
|
-
function listProjection(nodes) {
|
|
90809
|
-
return {
|
|
90810
|
-
columns: [
|
|
90811
|
-
{ key: "name", header: "NAME", color: source_default.cyan },
|
|
90812
|
-
{ key: "class", header: "CLASS", color: source_default.dim },
|
|
90813
|
-
{ key: "id", header: "ID", color: source_default.dim }
|
|
90814
|
-
],
|
|
90815
|
-
rows: nodes.map((node4) => ({
|
|
90816
|
-
name: displayName(node4),
|
|
90817
|
-
class: ClassPath.name(node4.class),
|
|
90818
|
-
id: node4.id
|
|
90819
|
-
})),
|
|
90820
|
-
paths: nodes.map((node4) => `@${node4.id}`)
|
|
90821
|
-
};
|
|
90822
|
-
}
|
|
90823
|
-
function displayName(node4) {
|
|
90824
|
-
const value2 = nodeProperty(node4, "name") ?? nodeProperty(node4, "title") ?? nodeProperty(node4, "slug");
|
|
90825
|
-
return typeof value2 === "string" && value2.length > 0 ? value2 : `@${node4.id}`;
|
|
90826
|
-
}
|
|
90827
|
-
var ls_default;
|
|
90828
|
-
var init_ls = __esm(() => {
|
|
90829
|
-
init_class3();
|
|
90830
|
-
init_source();
|
|
90831
|
-
init_connection2();
|
|
90832
|
-
init_graph6();
|
|
90833
|
-
init_log();
|
|
90834
|
-
init_output();
|
|
90835
|
-
ls_default = {
|
|
90836
|
-
name: "ls",
|
|
90837
|
-
description: "List one finite exact edge neighborhood",
|
|
90838
|
-
afterHelpText: `
|
|
90839
|
-
Behavior:
|
|
90840
|
-
Lists Nodes reached from one source through one exact Edge Class. Kernel V2
|
|
90841
|
-
has no universal parent/child relation, so --edge is required and recursive
|
|
90842
|
-
tree walking is intentionally absent. Direction defaults to outgoing and the
|
|
90843
|
-
finite default limit is 100. -q emits one @id per line.
|
|
90844
|
-
|
|
90845
|
-
Examples:
|
|
90846
|
-
$ astrale ls @note --edge /:notes.example.dev:class.references
|
|
90847
|
-
$ astrale ls @note --edge /:notes.example.dev:class.references --direction incoming --limit 25
|
|
90848
|
-
`,
|
|
90849
|
-
arguments: [{ name: "source", description: "Canonical source Path or @id" }],
|
|
90850
|
-
options: [
|
|
90851
|
-
{ flags: "--edge <class>", description: "Exact Edge Class to traverse" },
|
|
90852
|
-
{
|
|
90853
|
-
flags: "--direction <direction>",
|
|
90854
|
-
description: "Traversal direction (default: outgoing)",
|
|
90855
|
-
choices: ["outgoing", "incoming", "incident"]
|
|
90856
|
-
},
|
|
90857
|
-
{ flags: "--limit <n>", description: "Finite selected Node limit (default: 100)" },
|
|
90858
|
-
{ flags: "--cursor <token>", description: "Resume one live query scope" },
|
|
90859
|
-
{ flags: "-l, --long", description: "Print complete canonical Nodes" },
|
|
90860
|
-
{ flags: "-q, --quiet", description: "Print one @id per line" },
|
|
90861
|
-
{ flags: "--count", description: "Print only the number of returned Nodes" }
|
|
90862
|
-
],
|
|
90863
|
-
action: async (source2, opts) => {
|
|
90864
|
-
await lsCommand(source2, opts);
|
|
90865
|
-
}
|
|
90866
|
-
};
|
|
90867
|
-
});
|
|
90868
|
-
|
|
90869
90855
|
// src/commands/describe.ts
|
|
90870
90856
|
var exports_describe = {};
|
|
90871
90857
|
__export(exports_describe, {
|
|
@@ -91173,24 +91159,37 @@ function printRecord(record10) {
|
|
|
91173
91159
|
`);
|
|
91174
91160
|
}
|
|
91175
91161
|
function acceptRecord2(input, index) {
|
|
91176
|
-
if (!isRecord8(input) || !Number.isSafeInteger(input.sequence) || typeof input.
|
|
91162
|
+
if (!isRecord8(input) || !Number.isSafeInteger(input.sequence) || typeof input.topic !== "string") {
|
|
91177
91163
|
throw new TypeError(`Kernel journal record ${index} is invalid`);
|
|
91178
91164
|
}
|
|
91179
|
-
|
|
91180
|
-
|
|
91181
|
-
|
|
91182
|
-
}
|
|
91165
|
+
const occurredAt = optionalText2(input.occurredAt, index, "occurredAt");
|
|
91166
|
+
const timestamp = optionalText2(input.timestamp, index, "timestamp") ?? occurredAt;
|
|
91167
|
+
if (timestamp === undefined) {
|
|
91168
|
+
throw new TypeError(`Kernel journal record ${index} is missing occurredAt/timestamp`);
|
|
91183
91169
|
}
|
|
91170
|
+
const correlation = isRecord8(input.correlation) ? input.correlation : undefined;
|
|
91171
|
+
const correlationId = optionalText2(input.correlationId, index, "correlationId") ?? optionalText2(correlation?.invocationId, index, "correlation.invocationId");
|
|
91172
|
+
const principal = optionalText2(input.principal, index, "principal");
|
|
91184
91173
|
return Object.freeze({
|
|
91185
91174
|
sequence: input.sequence,
|
|
91186
|
-
timestamp
|
|
91175
|
+
timestamp,
|
|
91187
91176
|
topic: input.topic,
|
|
91188
91177
|
payload: input.payload,
|
|
91189
|
-
...
|
|
91190
|
-
...input.
|
|
91191
|
-
...
|
|
91178
|
+
...occurredAt === undefined ? {} : { occurredAt },
|
|
91179
|
+
...optionalText2(input.committedAt, index, "committedAt") === undefined ? {} : { committedAt: input.committedAt },
|
|
91180
|
+
...principal === undefined ? {} : { principal },
|
|
91181
|
+
...correlationId === undefined ? {} : { correlationId },
|
|
91182
|
+
...optionalText2(input.causationId, index, "causationId") === undefined ? {} : { causationId: input.causationId }
|
|
91192
91183
|
});
|
|
91193
91184
|
}
|
|
91185
|
+
function optionalText2(input, index, field) {
|
|
91186
|
+
if (input === undefined)
|
|
91187
|
+
return;
|
|
91188
|
+
if (typeof input !== "string") {
|
|
91189
|
+
throw new TypeError(`Kernel journal record ${index}.${field} must be text`);
|
|
91190
|
+
}
|
|
91191
|
+
return input;
|
|
91192
|
+
}
|
|
91194
91193
|
function positiveInteger3(flag, raw2) {
|
|
91195
91194
|
if (!/^\d+$/.test(raw2))
|
|
91196
91195
|
throw new TypeError(`${flag} must be a positive integer`);
|
|
@@ -92742,7 +92741,8 @@ Examples:
|
|
|
92742
92741
|
var exports_list = {};
|
|
92743
92742
|
__export(exports_list, {
|
|
92744
92743
|
default: () => list_default,
|
|
92745
|
-
buildInstanceRows: () => buildInstanceRows
|
|
92744
|
+
buildInstanceRows: () => buildInstanceRows,
|
|
92745
|
+
adminInventoryUnavailable: () => adminInventoryUnavailable
|
|
92746
92746
|
});
|
|
92747
92747
|
function buildInstanceRows(managed, bookmarks, show) {
|
|
92748
92748
|
const rows = [];
|
|
@@ -92786,9 +92786,19 @@ function buildInstanceRows(managed, bookmarks, show) {
|
|
|
92786
92786
|
}
|
|
92787
92787
|
return rows;
|
|
92788
92788
|
}
|
|
92789
|
-
|
|
92789
|
+
function adminInventoryUnavailable(cause) {
|
|
92790
|
+
const code = cause instanceof AstraleError ? cause.code : undefined;
|
|
92791
|
+
if (code !== undefined && ADMIN_INVENTORY_CODES.has(code)) {
|
|
92792
|
+
return new AstraleError("ADMIN_INVENTORY_UNAVAILABLE", "Managed instance listing needs the Admin Domain and an IdP-backed identity. Admin is not deployed in this environment.", "Use `astrale instance list --bookmarked` for local kernel bookmarks. Key-backed identities cannot mint an Admin Domain token.");
|
|
92793
|
+
}
|
|
92794
|
+
if (cause instanceof AstraleError)
|
|
92795
|
+
return cause;
|
|
92796
|
+
return new AstraleError("ADMIN_INVENTORY_UNAVAILABLE", cause instanceof Error ? cause.message : String(cause), "Use `astrale instance list --bookmarked` for local kernel bookmarks.");
|
|
92797
|
+
}
|
|
92798
|
+
var COLUMNS, list_default, ADMIN_INVENTORY_CODES;
|
|
92790
92799
|
var init_list = __esm(() => {
|
|
92791
92800
|
init_source();
|
|
92801
|
+
init_errors2();
|
|
92792
92802
|
init_admin_instance();
|
|
92793
92803
|
init_admin_instance();
|
|
92794
92804
|
init_admin_target();
|
|
@@ -92823,7 +92833,11 @@ var init_list = __esm(() => {
|
|
|
92823
92833
|
}));
|
|
92824
92834
|
let managed = [];
|
|
92825
92835
|
if (!opts.bookmarked) {
|
|
92826
|
-
|
|
92836
|
+
try {
|
|
92837
|
+
managed = await withSpinner("Fetching instances", !isMachine(opts), () => listOwnedInstances(opts));
|
|
92838
|
+
} catch (error52) {
|
|
92839
|
+
throw adminInventoryUnavailable(error52);
|
|
92840
|
+
}
|
|
92827
92841
|
}
|
|
92828
92842
|
if (isMachine(opts)) {
|
|
92829
92843
|
output({
|
|
@@ -92848,6 +92862,15 @@ var init_list = __esm(() => {
|
|
|
92848
92862
|
}
|
|
92849
92863
|
}
|
|
92850
92864
|
};
|
|
92865
|
+
ADMIN_INVENTORY_CODES = new Set([
|
|
92866
|
+
"TOKEN_EXCHANGE_SOURCE_INVALID",
|
|
92867
|
+
"TOKEN_EXCHANGE_SOURCE_EXPIRED",
|
|
92868
|
+
"TOKEN_EXCHANGE_UNSUPPORTED",
|
|
92869
|
+
"TOKEN_EXCHANGE_DISCOVERY_FAILED",
|
|
92870
|
+
"TOKEN_EXCHANGE_PROTOCOL_ERROR",
|
|
92871
|
+
"TOKEN_EXCHANGE_INSECURE",
|
|
92872
|
+
"ADMIN_DOMAIN_ISSUER_MISSING"
|
|
92873
|
+
]);
|
|
92851
92874
|
});
|
|
92852
92875
|
|
|
92853
92876
|
// src/commands/instance/bookmark.ts
|
|
@@ -93437,7 +93460,7 @@ Behavior:
|
|
|
93437
93460
|
Reads the admin catalog — every domain that has been \`publish\`ed
|
|
93438
93461
|
(origin → published worker URL). Listing only shows what is INSTALLABLE;
|
|
93439
93462
|
what is actually mounted where lives on each instance's own graph
|
|
93440
|
-
(\`astrale
|
|
93463
|
+
(\`astrale query\` against that instance).
|
|
93441
93464
|
|
|
93442
93465
|
Default output is a NAME/ORIGIN/URL/DEFAULT table on a TTY, JSON when piped
|
|
93443
93466
|
or with --json/--raw (agent-friendly — full DomainInfo objects). -q prints
|
|
@@ -93591,8 +93614,23 @@ __export(exports_install, {
|
|
|
93591
93614
|
isIdentityOverride: () => isIdentityOverride,
|
|
93592
93615
|
installViaAdmin: () => installViaAdmin,
|
|
93593
93616
|
domainRefFromTarget: () => domainRefFromTarget,
|
|
93617
|
+
directInstallCallInput: () => directInstallCallInput,
|
|
93594
93618
|
default: () => install_default
|
|
93595
93619
|
});
|
|
93620
|
+
function directInstallCallInput(url3, token, operation2 = crypto.randomUUID()) {
|
|
93621
|
+
return Object.freeze({
|
|
93622
|
+
operation: operation2,
|
|
93623
|
+
domains: [
|
|
93624
|
+
Object.freeze({
|
|
93625
|
+
source: Object.freeze({
|
|
93626
|
+
kind: "remote",
|
|
93627
|
+
url: url3,
|
|
93628
|
+
...token === undefined ? {} : { token }
|
|
93629
|
+
})
|
|
93630
|
+
})
|
|
93631
|
+
]
|
|
93632
|
+
});
|
|
93633
|
+
}
|
|
93596
93634
|
async function installViaAdmin(target2, opts, dependencies = {}) {
|
|
93597
93635
|
const admin = { ...defaultAdminInstallDependencies, ...dependencies };
|
|
93598
93636
|
const interactive = !!process.stdin.isTTY && !(opts.ci || opts.noPrompt || process.env.CI);
|
|
@@ -93710,20 +93748,18 @@ async function installDirect(target2, opts) {
|
|
|
93710
93748
|
await runKernelCommand({
|
|
93711
93749
|
opts,
|
|
93712
93750
|
label: `Installing domain from ${url3}`,
|
|
93713
|
-
fn: async ({ session }) => await session.call(createPathCall(Path.project(exports_syscalls.install.ref).raw,
|
|
93714
|
-
domains: [{ url: url3, ...opts.token ? { token: opts.token } : {} }]
|
|
93715
|
-
})),
|
|
93751
|
+
fn: async ({ session }) => await session.call(createPathCall(Path.project(exports_syscalls.install.ref).raw, directInstallCallInput(url3, opts.token))),
|
|
93716
93752
|
format: (result, fmtOpts, isRaw) => {
|
|
93717
93753
|
if (isRaw) {
|
|
93718
93754
|
output(result, fmtOpts);
|
|
93719
93755
|
return;
|
|
93720
93756
|
}
|
|
93721
|
-
const installed = result[0];
|
|
93722
|
-
if (
|
|
93723
|
-
throw new Error("Kernel install returned no
|
|
93724
|
-
|
|
93725
|
-
|
|
93726
|
-
|
|
93757
|
+
const installed = result.transitions[0]?.intent;
|
|
93758
|
+
if (installed === undefined) {
|
|
93759
|
+
throw new Error("Kernel install returned no committed Domain transition.");
|
|
93760
|
+
}
|
|
93761
|
+
const revision = installed.target?.schemaRevision ?? result.operation;
|
|
93762
|
+
log.success(`Domain installed: ${installed.origin}@${revision}`);
|
|
93727
93763
|
if (isIdentityOverride(installed.origin, host) && installed.origin !== consentedOrigin) {
|
|
93728
93764
|
log.warn(`Installed origin "${installed.origin}" differs from the serving host "${host}" ` + `and was not confirmed before install (the worker's /meta did not declare it). ` + `Every ${installed.origin}/* call on this instance now routes to ${host}.`);
|
|
93729
93765
|
}
|
|
@@ -95759,9 +95795,21 @@ function renderCommanderError(program2, error, argv = process.argv.slice(2)) {
|
|
|
95759
95795
|
].join(`
|
|
95760
95796
|
`);
|
|
95761
95797
|
}
|
|
95798
|
+
var RETIRED_COMMANDS = {
|
|
95799
|
+
ls: "astrale query <source> --edge <class>"
|
|
95800
|
+
};
|
|
95762
95801
|
function renderUnknownCommand(tokens, catalog) {
|
|
95763
95802
|
const command = tokens.join(" ");
|
|
95764
95803
|
const first = tokens[0];
|
|
95804
|
+
const retired = first === undefined ? undefined : RETIRED_COMMANDS[first];
|
|
95805
|
+
if (retired !== undefined) {
|
|
95806
|
+
return [
|
|
95807
|
+
`Unknown command: ${source_default.bold(`astrale ${command}`)}`,
|
|
95808
|
+
"",
|
|
95809
|
+
`\`astrale ${first}\` was removed. Use ${source_default.bold(retired)} instead.`
|
|
95810
|
+
].join(`
|
|
95811
|
+
`);
|
|
95812
|
+
}
|
|
95765
95813
|
const namespaceMatches = catalog.filter((entry) => entry.path.at(-1) === first);
|
|
95766
95814
|
if (namespaceMatches.length > 0) {
|
|
95767
95815
|
return [
|
|
@@ -95961,7 +96009,6 @@ async function buildProgram() {
|
|
|
95961
96009
|
registerCommand(program3, withKernelOptions((await Promise.resolve().then(() => (init_token(), exports_token))).default));
|
|
95962
96010
|
registerCommand(program3, withKernelOptions((await Promise.resolve().then(() => (init_get(), exports_get))).default));
|
|
95963
96011
|
registerCommand(program3, withKernelOptions((await Promise.resolve().then(() => (init_mutate4(), exports_mutate))).default));
|
|
95964
|
-
registerCommand(program3, withKernelOptions((await Promise.resolve().then(() => (init_ls(), exports_ls))).default));
|
|
95965
96012
|
registerCommand(program3, withKernelOptions((await Promise.resolve().then(() => (init_describe(), exports_describe))).default));
|
|
95966
96013
|
registerCommand(program3, withKernelOptions((await Promise.resolve().then(() => (init_query7(), exports_query))).default));
|
|
95967
96014
|
registerCommand(program3, withKernelOptions((await Promise.resolve().then(() => (init_logs(), exports_logs))).default));
|
|
@@ -96049,7 +96096,7 @@ async function buildProgram() {
|
|
|
96049
96096
|
program3.addHelpText("after", `
|
|
96050
96097
|
Command groups:
|
|
96051
96098
|
Getting started setup (sign in, pick an instance, equip your workspace)
|
|
96052
|
-
Kernel
|
|
96099
|
+
Kernel get, mutate, call, query, describe, token
|
|
96053
96100
|
Management admin, instance, domain, identity, auth, idp, update
|
|
96054
96101
|
Agent browser (drive the GUI via agent-browser)
|
|
96055
96102
|
Studio studio (launch the local Domain Studio GUI for a workspace)
|
|
@@ -96063,7 +96110,6 @@ Path syntax:
|
|
|
96063
96110
|
@nodeId::method Instance method on a node by UID
|
|
96064
96111
|
|
|
96065
96112
|
Examples:
|
|
96066
|
-
$ astrale ls @note --edge /:notes.example.dev:class.references
|
|
96067
96113
|
$ astrale studio
|
|
96068
96114
|
$ astrale admin status
|
|
96069
96115
|
$ astrale update --check
|
package/package.json
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
2
|
+
import { describe, expect, test } from 'bun:test'
|
|
3
|
+
|
|
4
|
+
import { describeCallableFromSchema } from '../call-describe'
|
|
5
|
+
|
|
6
|
+
const schema = {
|
|
7
|
+
classes: {
|
|
8
|
+
Manager: {
|
|
9
|
+
methods: {
|
|
10
|
+
createInstance: {
|
|
11
|
+
auth: 'authorized',
|
|
12
|
+
description: 'Create one child Instance.',
|
|
13
|
+
input: { type: 'object', required: ['operationId', 'slug'] },
|
|
14
|
+
output: { mode: 'value' },
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
functions: {
|
|
20
|
+
journal: {
|
|
21
|
+
description: 'Read the authorized Kernel journal.',
|
|
22
|
+
input: { type: 'object' },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('describeCallableFromSchema', () => {
|
|
28
|
+
test('reads a static Class method from the Domain schema', () => {
|
|
29
|
+
const described = describeCallableFromSchema(
|
|
30
|
+
Path.parse('/:host.astrale.ai:class.Manager:createInstance'),
|
|
31
|
+
schema,
|
|
32
|
+
)
|
|
33
|
+
expect(described).toMatchObject({
|
|
34
|
+
origin: 'host.astrale.ai',
|
|
35
|
+
class: 'Manager',
|
|
36
|
+
method: 'createInstance',
|
|
37
|
+
dispatch: 'static',
|
|
38
|
+
description: 'Create one child Instance.',
|
|
39
|
+
auth: 'authorized',
|
|
40
|
+
})
|
|
41
|
+
expect(described?.input).toMatchObject({ required: ['operationId', 'slug'] })
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('finds an instance method when the receiver Path is not a Class projection', () => {
|
|
45
|
+
expect(
|
|
46
|
+
describeCallableFromSchema(
|
|
47
|
+
Path.parse('/:host.astrale.ai:core.manager::createInstance'),
|
|
48
|
+
schema,
|
|
49
|
+
),
|
|
50
|
+
).toMatchObject({
|
|
51
|
+
origin: 'host.astrale.ai',
|
|
52
|
+
class: 'Manager',
|
|
53
|
+
method: 'createInstance',
|
|
54
|
+
dispatch: 'instance',
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('reads a standalone Function from the Domain schema', () => {
|
|
59
|
+
expect(
|
|
60
|
+
describeCallableFromSchema(Path.parse('/:kernel.astrale.ai:function.journal'), schema),
|
|
61
|
+
).toMatchObject({
|
|
62
|
+
origin: 'kernel.astrale.ai',
|
|
63
|
+
function: 'journal',
|
|
64
|
+
description: 'Read the authorized Kernel journal.',
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { directInstallCallInput } from '../domain/install'
|
|
4
|
+
|
|
5
|
+
describe('directInstallCallInput', () => {
|
|
6
|
+
test('sends the current remote install syscall, not a legacy url list', () => {
|
|
7
|
+
expect(directInstallCallInput('https://tasks.example.test', 'secret', 'op-1')).toEqual({
|
|
8
|
+
operation: 'op-1',
|
|
9
|
+
domains: [
|
|
10
|
+
{
|
|
11
|
+
source: {
|
|
12
|
+
kind: 'remote',
|
|
13
|
+
url: 'https://tasks.example.test',
|
|
14
|
+
token: 'secret',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { AstraleError } from '../../errors'
|
|
4
|
+
import { adminInventoryUnavailable } from '../instance/list'
|
|
5
|
+
|
|
6
|
+
describe('adminInventoryUnavailable', () => {
|
|
7
|
+
test('turns key-backed Admin exchange failures into a bookmark-only instruction', () => {
|
|
8
|
+
const error = adminInventoryUnavailable(
|
|
9
|
+
new AstraleError(
|
|
10
|
+
'TOKEN_EXCHANGE_SOURCE_INVALID',
|
|
11
|
+
'The source identity credential has no valid expiration.',
|
|
12
|
+
),
|
|
13
|
+
)
|
|
14
|
+
expect(error.code).toBe('ADMIN_INVENTORY_UNAVAILABLE')
|
|
15
|
+
expect(error.message).toContain('Admin is not deployed')
|
|
16
|
+
expect(error.hint).toContain('instance list --bookmarked')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('turns a missing Admin Domain issuer into the same instruction', () => {
|
|
20
|
+
const error = adminInventoryUnavailable(
|
|
21
|
+
new AstraleError('ADMIN_DOMAIN_ISSUER_MISSING', 'no Domain issuer'),
|
|
22
|
+
)
|
|
23
|
+
expect(error.code).toBe('ADMIN_INVENTORY_UNAVAILABLE')
|
|
24
|
+
expect(error.hint).toContain('Key-backed identities cannot mint an Admin Domain token')
|
|
25
|
+
})
|
|
26
|
+
})
|