@xnetjs/cli 0.2.1 → 0.3.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/dist/agent-local-XRQV3OKU.js +6 -0
- package/dist/chunk-NOYKKZDQ.js +90 -0
- package/dist/cli.js +236 -110
- package/package.json +9 -9
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// src/utils/agent-local.ts
|
|
2
|
+
import { getSigningPublicKeyFromPrivate } from "@xnetjs/crypto";
|
|
3
|
+
import { SQLiteNodeStorageAdapter, builtInSchemas } from "@xnetjs/data";
|
|
4
|
+
import { createDID } from "@xnetjs/identity";
|
|
5
|
+
import { createXNetClient } from "@xnetjs/runtime";
|
|
6
|
+
var toNodeData = (node) => ({
|
|
7
|
+
id: node.id,
|
|
8
|
+
schemaId: node.schemaId,
|
|
9
|
+
properties: node.properties,
|
|
10
|
+
deleted: node.deleted ?? false,
|
|
11
|
+
createdAt: node.createdAt ?? 0,
|
|
12
|
+
updatedAt: node.updatedAt ?? 0
|
|
13
|
+
});
|
|
14
|
+
async function resolveStorage(db) {
|
|
15
|
+
if (db) {
|
|
16
|
+
const { createElectronSQLiteAdapter } = await import("@xnetjs/sqlite/electron");
|
|
17
|
+
const adapter2 = await createElectronSQLiteAdapter({
|
|
18
|
+
path: db,
|
|
19
|
+
busyTimeout: 5e3,
|
|
20
|
+
foreignKeys: true,
|
|
21
|
+
walMode: true
|
|
22
|
+
});
|
|
23
|
+
return new SQLiteNodeStorageAdapter(adapter2);
|
|
24
|
+
}
|
|
25
|
+
const { createMemorySQLiteAdapter } = await import("@xnetjs/sqlite/memory");
|
|
26
|
+
const adapter = await createMemorySQLiteAdapter();
|
|
27
|
+
return new SQLiteNodeStorageAdapter(adapter);
|
|
28
|
+
}
|
|
29
|
+
function builtInSchemaRegistry() {
|
|
30
|
+
const iris = Object.keys(builtInSchemas).filter((iri) => iri.includes("@"));
|
|
31
|
+
return {
|
|
32
|
+
getAllIRIs: () => iris,
|
|
33
|
+
get: async (iri) => {
|
|
34
|
+
const loader = builtInSchemas[iri];
|
|
35
|
+
if (!loader) return null;
|
|
36
|
+
const schema = await loader();
|
|
37
|
+
return {
|
|
38
|
+
iri,
|
|
39
|
+
name: schema.schema.name,
|
|
40
|
+
properties: schema.schema.properties
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async function createLocalAgentBackend(options) {
|
|
46
|
+
const agentDID = createDID(getSigningPublicKeyFromPrivate(options.agentKey));
|
|
47
|
+
const nodeStorage = await resolveStorage(options.db);
|
|
48
|
+
const client = await createXNetClient({
|
|
49
|
+
nodeStorage,
|
|
50
|
+
authorDID: agentDID,
|
|
51
|
+
signingKey: options.agentKey
|
|
52
|
+
});
|
|
53
|
+
const store = {
|
|
54
|
+
get: async (id) => {
|
|
55
|
+
const node = await client.store.get(id);
|
|
56
|
+
return node ? toNodeData(node) : null;
|
|
57
|
+
},
|
|
58
|
+
list: async (opts) => {
|
|
59
|
+
const nodes = await client.store.list({
|
|
60
|
+
...opts?.schemaId ? { schemaId: opts.schemaId } : {},
|
|
61
|
+
...opts?.limit !== void 0 ? { limit: opts.limit } : {},
|
|
62
|
+
...opts?.offset !== void 0 ? { offset: opts.offset } : {}
|
|
63
|
+
});
|
|
64
|
+
return nodes.map(toNodeData);
|
|
65
|
+
},
|
|
66
|
+
create: async (opts) => {
|
|
67
|
+
const node = await client.store.create({
|
|
68
|
+
...opts.id ? { id: opts.id } : {},
|
|
69
|
+
schemaId: opts.schemaId,
|
|
70
|
+
properties: opts.properties
|
|
71
|
+
});
|
|
72
|
+
return toNodeData(node);
|
|
73
|
+
},
|
|
74
|
+
update: async (id, opts) => {
|
|
75
|
+
const node = await client.store.update(id, { properties: opts.properties });
|
|
76
|
+
return toNodeData(node);
|
|
77
|
+
},
|
|
78
|
+
delete: async (id) => {
|
|
79
|
+
await client.store.delete(id);
|
|
80
|
+
},
|
|
81
|
+
subscribe: (listener) => client.store.subscribe((event) => {
|
|
82
|
+
listener({ change: { type: event.change.type }, node: null, isRemote: false });
|
|
83
|
+
})
|
|
84
|
+
};
|
|
85
|
+
return { store, schemas: builtInSchemaRegistry(), client, agentDID };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
createLocalAgentBackend
|
|
90
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,9 @@ import {
|
|
|
4
4
|
generateLensCode,
|
|
5
5
|
generateLensSnippet
|
|
6
6
|
} from "./chunk-IUOECMVU.js";
|
|
7
|
+
import {
|
|
8
|
+
createLocalAgentBackend
|
|
9
|
+
} from "./chunk-NOYKKZDQ.js";
|
|
7
10
|
|
|
8
11
|
// src/cli.ts
|
|
9
12
|
import { program } from "commander";
|
|
@@ -724,11 +727,82 @@ function registerConnectorCommand(program2) {
|
|
|
724
727
|
}
|
|
725
728
|
|
|
726
729
|
// src/commands/data.ts
|
|
727
|
-
import { generateSigningKeyPair, getSigningPublicKeyFromPrivate } from "@xnetjs/crypto";
|
|
728
|
-
import {
|
|
730
|
+
import { generateSigningKeyPair, getSigningPublicKeyFromPrivate, sign } from "@xnetjs/crypto";
|
|
731
|
+
import {
|
|
732
|
+
applyBundle,
|
|
733
|
+
defineSchema,
|
|
734
|
+
SQLiteNodeStorageAdapter,
|
|
735
|
+
text,
|
|
736
|
+
verifyBundle,
|
|
737
|
+
writeBundle
|
|
738
|
+
} from "@xnetjs/data";
|
|
729
739
|
import { createDID } from "@xnetjs/identity";
|
|
730
740
|
import { createXNetClient } from "@xnetjs/runtime";
|
|
731
741
|
import chalk from "chalk";
|
|
742
|
+
import { resolve as resolve5 } from "path";
|
|
743
|
+
|
|
744
|
+
// src/utils/fs-bundle.ts
|
|
745
|
+
import { createReadStream } from "fs";
|
|
746
|
+
import { mkdir as mkdir2, readdir, readFile as readFile2, stat, writeFile } from "fs/promises";
|
|
747
|
+
import { dirname as dirname3, join as join5, relative, sep } from "path";
|
|
748
|
+
import { createInterface } from "readline";
|
|
749
|
+
var FsBundleSink = class {
|
|
750
|
+
constructor(root) {
|
|
751
|
+
this.root = root;
|
|
752
|
+
}
|
|
753
|
+
async writeEntry(path, data) {
|
|
754
|
+
const target = join5(this.root, path);
|
|
755
|
+
await mkdir2(dirname3(target), { recursive: true });
|
|
756
|
+
await writeFile(target, data);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
var FsBundleSource = class {
|
|
760
|
+
constructor(root) {
|
|
761
|
+
this.root = root;
|
|
762
|
+
}
|
|
763
|
+
async readEntry(path) {
|
|
764
|
+
try {
|
|
765
|
+
return new Uint8Array(await readFile2(join5(this.root, path)));
|
|
766
|
+
} catch {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async *readLines(path) {
|
|
771
|
+
const target = join5(this.root, path);
|
|
772
|
+
try {
|
|
773
|
+
await stat(target);
|
|
774
|
+
} catch {
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
const rl = createInterface({ input: createReadStream(target), crlfDelay: Infinity });
|
|
778
|
+
for await (const line of rl) {
|
|
779
|
+
if (line.length > 0) yield line;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
async listEntries(prefix) {
|
|
783
|
+
const results = [];
|
|
784
|
+
const walk = async (dir) => {
|
|
785
|
+
let entries;
|
|
786
|
+
try {
|
|
787
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
788
|
+
} catch {
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
for (const entry of entries) {
|
|
792
|
+
const full = join5(dir, entry.name);
|
|
793
|
+
if (entry.isDirectory()) await walk(full);
|
|
794
|
+
else {
|
|
795
|
+
const rel = relative(this.root, full).split(sep).join("/");
|
|
796
|
+
if (rel.startsWith(prefix)) results.push(rel);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
await walk(this.root);
|
|
801
|
+
return results.sort();
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
// src/commands/data.ts
|
|
732
806
|
var NoteSchema = defineSchema({
|
|
733
807
|
name: "Note",
|
|
734
808
|
namespace: "xnet://cli/",
|
|
@@ -773,6 +847,35 @@ async function runCreateNote(client, input) {
|
|
|
773
847
|
async function runListNotes(client) {
|
|
774
848
|
return client.fetch(NoteSchema);
|
|
775
849
|
}
|
|
850
|
+
function parseScope(opts) {
|
|
851
|
+
if (opts.space) return { kind: "space", spaceId: opts.space };
|
|
852
|
+
if (opts.schema?.length) return { kind: "schemas", schemaIds: opts.schema };
|
|
853
|
+
if (opts.node?.length) return { kind: "nodes", nodeIds: opts.node };
|
|
854
|
+
return { kind: "full" };
|
|
855
|
+
}
|
|
856
|
+
function printVerifyReport(report) {
|
|
857
|
+
const m = report.manifest;
|
|
858
|
+
if (m) {
|
|
859
|
+
console.log(` format: ${m.formatVersion} (change protocol v${m.protocolVersion.change})`);
|
|
860
|
+
console.log(` owner: ${m.ownerDid}`);
|
|
861
|
+
console.log(` scope: ${JSON.stringify(m.scope)}`);
|
|
862
|
+
console.log(
|
|
863
|
+
` contents: ${m.counts.changes} changes, ${m.counts.blobs} blobs, ${m.counts.yjsDocs} yjs docs`
|
|
864
|
+
);
|
|
865
|
+
if (m.prerequisites) {
|
|
866
|
+
console.log(
|
|
867
|
+
` requires: base bundle through lamport ${m.prerequisites.lamport} (incremental)`
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
for (const issue of report.issues) {
|
|
872
|
+
const paint = issue.severity === "error" ? chalk.red : chalk.yellow;
|
|
873
|
+
console.log(paint(` ${issue.severity}: [${issue.code}] ${issue.detail}`));
|
|
874
|
+
}
|
|
875
|
+
console.log(
|
|
876
|
+
report.ok ? chalk.green("\u2713 bundle verifies clean") : chalk.red("\u2717 bundle failed verification")
|
|
877
|
+
);
|
|
878
|
+
}
|
|
776
879
|
function registerDataCommand(program2) {
|
|
777
880
|
const data = program2.command("data").description("Read and write live nodes (runtime client)");
|
|
778
881
|
data.command("add").description("Create a Note node").requiredOption("--title <title>", "Note title").option("--body <body>", "Note body", "").option("--db <path>", "SQLite file path (default: in-memory)").option("--key <hex>", "Ed25519 signing key (hex); falls back to $XNET_SIGNING_KEY").action(async (opts) => {
|
|
@@ -801,11 +904,123 @@ function registerDataCommand(program2) {
|
|
|
801
904
|
await client.destroy();
|
|
802
905
|
}
|
|
803
906
|
});
|
|
907
|
+
data.command("export").description("Export the change log as an .xnetpack bundle directory (exploration 0344)").requiredOption("--out <dir>", "Bundle output directory").option("--db <path>", "SQLite file path (default: in-memory)").option("--key <hex>", "Ed25519 signing key (hex); falls back to $XNET_SIGNING_KEY").option("--space <id>", "Export one space (the space node plus its members)").option("--schema <iri...>", "Export nodes of the given schema IRI(s)").option("--node <id...>", "Export the given node id(s)").option("--since-lamport <n>", "Incremental: export changes after this lamport time").action(
|
|
908
|
+
async (opts) => {
|
|
909
|
+
const { signingKey, ephemeral } = resolveSigningKey(opts.key);
|
|
910
|
+
if (ephemeral) {
|
|
911
|
+
console.log(
|
|
912
|
+
chalk.yellow(
|
|
913
|
+
"\u26A0 no signing key provided \u2014 manifest will be signed by an ephemeral identity"
|
|
914
|
+
)
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
const client = await buildDataClient({
|
|
918
|
+
db: opts.db,
|
|
919
|
+
key: Buffer.from(signingKey).toString("hex")
|
|
920
|
+
});
|
|
921
|
+
try {
|
|
922
|
+
const scope = parseScope(opts);
|
|
923
|
+
const since = opts.sinceLamport ? { lamport: Number(opts.sinceLamport), heads: [], changeCount: 0 } : void 0;
|
|
924
|
+
const manifest = await writeBundle(client.store, scope, new FsBundleSink(opts.out), {
|
|
925
|
+
ownerDid: client.authorDID,
|
|
926
|
+
manifestSigner: (bytes) => sign(bytes, signingKey),
|
|
927
|
+
since
|
|
928
|
+
});
|
|
929
|
+
console.log(chalk.green(`\u2713 exported ${manifest.counts.changes} changes to ${opts.out}`));
|
|
930
|
+
console.log(chalk.dim(` owner: ${manifest.ownerDid}`));
|
|
931
|
+
console.log(chalk.dim(` frontier lamport: ${manifest.frontier.lamport}`));
|
|
932
|
+
} finally {
|
|
933
|
+
await client.destroy();
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
);
|
|
937
|
+
data.command("import").description("Verify and import an .xnetpack bundle directory").requiredOption("--in <dir>", "Bundle directory to import").option("--db <path>", "SQLite file path (default: in-memory)").option("--key <hex>", "Ed25519 signing key (hex); falls back to $XNET_SIGNING_KEY").option("--dry-run", "Verify and report only \u2014 write nothing").option("--allow-foreign-owner", "Import a bundle owned by another identity's DID").option("--allow-unsigned", "Import a bundle whose manifest is unsigned").action(
|
|
938
|
+
async (opts) => {
|
|
939
|
+
const source = new FsBundleSource(opts.in);
|
|
940
|
+
if (opts.dryRun) {
|
|
941
|
+
const report = await verifyBundle(source);
|
|
942
|
+
printVerifyReport(report);
|
|
943
|
+
if (!report.ok) process.exitCode = 1;
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
const client = await buildDataClient({ db: opts.db, key: opts.key });
|
|
947
|
+
try {
|
|
948
|
+
const result = await applyBundle(client.store, source, {
|
|
949
|
+
importerDid: client.authorDID,
|
|
950
|
+
allowForeignOwner: opts.allowForeignOwner,
|
|
951
|
+
allowUnsigned: opts.allowUnsigned
|
|
952
|
+
});
|
|
953
|
+
console.log(
|
|
954
|
+
chalk.green(
|
|
955
|
+
`\u2713 applied ${result.applied} change(s), ${result.duplicates} duplicate(s) skipped`
|
|
956
|
+
)
|
|
957
|
+
);
|
|
958
|
+
if (result.quarantined.length > 0) {
|
|
959
|
+
console.log(chalk.yellow(`\u26A0 ${result.quarantined.length} record(s) quarantined:`));
|
|
960
|
+
for (const q of result.quarantined.slice(0, 20)) {
|
|
961
|
+
console.log(chalk.yellow(` [${q.kind}] ${q.subject}: ${q.reason}`));
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
} finally {
|
|
965
|
+
await client.destroy();
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
);
|
|
969
|
+
data.command("export-folder").description(
|
|
970
|
+
"Export the workspace as a human-readable folder: Pages/*.md, Databases/*.rows.jsonl,\nCanvases/*.canvas (Tier 2 \u2014 a projection in xNet's markdown dialect: wikilinks and\nschema JSON travel, history/signatures/comments do not; use `export` for the lossless bundle)"
|
|
971
|
+
).requiredOption("--out <dir>", "Folder to write the projection into").option("--db <path>", "SQLite file path (default: in-memory)").option("--key <hex>", "Ed25519 signing key (hex); falls back to $XNET_SIGNING_KEY").action(async (opts) => {
|
|
972
|
+
const { signingKey } = resolveSigningKey(opts.key);
|
|
973
|
+
const [{ createLocalAgentBackend: createLocalAgentBackend2 }, plugins] = await Promise.all([
|
|
974
|
+
import("./agent-local-XRQV3OKU.js"),
|
|
975
|
+
import("@xnetjs/plugins/node")
|
|
976
|
+
]);
|
|
977
|
+
const backend = await createLocalAgentBackend2({ db: opts.db, agentKey: signingKey });
|
|
978
|
+
try {
|
|
979
|
+
const aiSurface = plugins.createAiSurfaceService({
|
|
980
|
+
store: backend.store,
|
|
981
|
+
schemas: backend.schemas
|
|
982
|
+
});
|
|
983
|
+
const exporter = plugins.createAiWorkspaceExporter({ ...backend, aiSurface });
|
|
984
|
+
const result = await exporter.exportWorkspace({ rootDir: resolve5(opts.out) });
|
|
985
|
+
console.log(
|
|
986
|
+
chalk.green(`\u2713 exported ${result.manifestEntries.length} file(s) to ${opts.out}`)
|
|
987
|
+
);
|
|
988
|
+
console.log(
|
|
989
|
+
chalk.dim(
|
|
990
|
+
" note: this is a readable projection (markdown + JSONL); history, signatures,\n and comments do not survive \u2014 use `xnet data export` for the lossless bundle"
|
|
991
|
+
)
|
|
992
|
+
);
|
|
993
|
+
} finally {
|
|
994
|
+
await backend.client.destroy();
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
data.command("snapshot").description(
|
|
998
|
+
"Write a defragmented SQLite snapshot of the database (VACUUM INTO \u2014 Tier 2:\nmaterialized state for any SQLite tool; use 'export' for the lossless bundle)"
|
|
999
|
+
).requiredOption("--db <path>", "SQLite file path to snapshot").requiredOption("--sqlite <out>", "Output .sqlite file path").action(async (opts) => {
|
|
1000
|
+
const { createElectronSQLiteAdapter } = await import("@xnetjs/sqlite/electron");
|
|
1001
|
+
const adapter = await createElectronSQLiteAdapter({
|
|
1002
|
+
path: opts.db,
|
|
1003
|
+
busyTimeout: 5e3,
|
|
1004
|
+
foreignKeys: true,
|
|
1005
|
+
walMode: true
|
|
1006
|
+
});
|
|
1007
|
+
try {
|
|
1008
|
+
await adapter.exec(`VACUUM INTO '${opts.sqlite.replace(/'/g, "''")}'`);
|
|
1009
|
+
console.log(chalk.green(`\u2713 snapshot written to ${opts.sqlite}`));
|
|
1010
|
+
console.log(
|
|
1011
|
+
chalk.dim(
|
|
1012
|
+
" note: a snapshot is materialized state (readable by any SQLite tool); it is not\n a signed bundle \u2014 use `xnet data export` for the lossless, verifiable artifact"
|
|
1013
|
+
)
|
|
1014
|
+
);
|
|
1015
|
+
} finally {
|
|
1016
|
+
await adapter.close();
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
804
1019
|
}
|
|
805
1020
|
|
|
806
1021
|
// src/commands/doctor.ts
|
|
807
1022
|
import { writeFileSync as writeFileSync3, readFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
808
|
-
import { resolve as
|
|
1023
|
+
import { resolve as resolve6, join as join6 } from "path";
|
|
809
1024
|
import {
|
|
810
1025
|
verifyIntegrity,
|
|
811
1026
|
quickIntegrityCheck,
|
|
@@ -835,15 +1050,15 @@ async function getChalk() {
|
|
|
835
1050
|
}
|
|
836
1051
|
function findDataDir(providedDir) {
|
|
837
1052
|
if (providedDir) {
|
|
838
|
-
const resolved =
|
|
1053
|
+
const resolved = resolve6(process.cwd(), providedDir);
|
|
839
1054
|
if (existsSync(resolved)) {
|
|
840
1055
|
return resolved;
|
|
841
1056
|
}
|
|
842
1057
|
throw new Error(`Data directory not found: ${resolved}`);
|
|
843
1058
|
}
|
|
844
|
-
const commonPaths = [".xnet/data", "data", ".data",
|
|
1059
|
+
const commonPaths = [".xnet/data", "data", ".data", resolve6(process.env.HOME ?? "", ".xnet/data")];
|
|
845
1060
|
for (const path of commonPaths) {
|
|
846
|
-
const resolved =
|
|
1061
|
+
const resolved = resolve6(process.cwd(), path);
|
|
847
1062
|
if (existsSync(resolved)) {
|
|
848
1063
|
return resolved;
|
|
849
1064
|
}
|
|
@@ -854,7 +1069,7 @@ function loadChangesFromDir(dataDir) {
|
|
|
854
1069
|
const changes = [];
|
|
855
1070
|
const patterns = ["changes.json", "changes/*.json", "*.changes.json"];
|
|
856
1071
|
for (const pattern of patterns) {
|
|
857
|
-
const changesFile =
|
|
1072
|
+
const changesFile = join6(dataDir, pattern.split("/")[0]);
|
|
858
1073
|
if (existsSync(changesFile) && statSync(changesFile).isFile()) {
|
|
859
1074
|
try {
|
|
860
1075
|
const content = readFileSync(changesFile, "utf-8");
|
|
@@ -868,12 +1083,12 @@ function loadChangesFromDir(dataDir) {
|
|
|
868
1083
|
}
|
|
869
1084
|
}
|
|
870
1085
|
}
|
|
871
|
-
const changesDir =
|
|
1086
|
+
const changesDir = join6(dataDir, "changes");
|
|
872
1087
|
if (existsSync(changesDir) && statSync(changesDir).isDirectory()) {
|
|
873
1088
|
const files = readdirSync(changesDir).filter((f) => f.endsWith(".json"));
|
|
874
1089
|
for (const file of files) {
|
|
875
1090
|
try {
|
|
876
|
-
const content = readFileSync(
|
|
1091
|
+
const content = readFileSync(join6(changesDir, file), "utf-8");
|
|
877
1092
|
const change = JSON.parse(content);
|
|
878
1093
|
changes.push(change);
|
|
879
1094
|
} catch {
|
|
@@ -1114,7 +1329,7 @@ async function exportCommand(options) {
|
|
|
1114
1329
|
dataDir = "";
|
|
1115
1330
|
changes = generateDemoChanges();
|
|
1116
1331
|
}
|
|
1117
|
-
const outputPath =
|
|
1332
|
+
const outputPath = resolve6(process.cwd(), options.output);
|
|
1118
1333
|
console.log(chalk2.gray(`Source: ${dataDir || "demo data"}`));
|
|
1119
1334
|
console.log(chalk2.gray(`Output: ${outputPath}`));
|
|
1120
1335
|
console.log();
|
|
@@ -1153,7 +1368,7 @@ async function importCommand(options) {
|
|
|
1153
1368
|
const chalk2 = await getChalk();
|
|
1154
1369
|
console.log(chalk2.bold("\nxNet Data Import\n"));
|
|
1155
1370
|
try {
|
|
1156
|
-
const inputPath =
|
|
1371
|
+
const inputPath = resolve6(process.cwd(), options.input);
|
|
1157
1372
|
if (!existsSync(inputPath)) {
|
|
1158
1373
|
console.error(chalk2.red(`Error: Input file not found: ${inputPath}`));
|
|
1159
1374
|
process.exit(1);
|
|
@@ -1236,29 +1451,29 @@ import { agentPassportId } from "@xnetjs/data";
|
|
|
1236
1451
|
import { createDID as createDID2, mintAgentPassport } from "@xnetjs/identity";
|
|
1237
1452
|
|
|
1238
1453
|
// src/utils/agent-passport-file.ts
|
|
1239
|
-
import { mkdir as
|
|
1454
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile2, chmod, readdir as readdir2 } from "fs/promises";
|
|
1240
1455
|
import { homedir } from "os";
|
|
1241
|
-
import { join as
|
|
1456
|
+
import { join as join7 } from "path";
|
|
1242
1457
|
function agentPassportDir() {
|
|
1243
|
-
return process.env.XNET_AGENT_DIR ??
|
|
1458
|
+
return process.env.XNET_AGENT_DIR ?? join7(homedir(), ".xnet", "agents");
|
|
1244
1459
|
}
|
|
1245
1460
|
var passportPath = (name) => {
|
|
1246
1461
|
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
1247
1462
|
throw new Error(`Invalid agent name: ${name} (use letters, digits, - and _)`);
|
|
1248
1463
|
}
|
|
1249
|
-
return
|
|
1464
|
+
return join7(agentPassportDir(), `${name}.json`);
|
|
1250
1465
|
};
|
|
1251
1466
|
async function saveAgentPassportFile(file) {
|
|
1252
1467
|
const dir = agentPassportDir();
|
|
1253
|
-
await
|
|
1468
|
+
await mkdir3(dir, { recursive: true, mode: 448 });
|
|
1254
1469
|
const path = passportPath(file.name);
|
|
1255
|
-
await
|
|
1470
|
+
await writeFile2(path, JSON.stringify(file, null, 2) + "\n", { mode: 384 });
|
|
1256
1471
|
await chmod(path, 384);
|
|
1257
1472
|
return path;
|
|
1258
1473
|
}
|
|
1259
1474
|
async function loadAgentPassportFile(name) {
|
|
1260
1475
|
try {
|
|
1261
|
-
const raw = await
|
|
1476
|
+
const raw = await readFile3(passportPath(name), "utf8");
|
|
1262
1477
|
return JSON.parse(raw);
|
|
1263
1478
|
} catch (err) {
|
|
1264
1479
|
if (err.code === "ENOENT") return null;
|
|
@@ -1267,7 +1482,7 @@ async function loadAgentPassportFile(name) {
|
|
|
1267
1482
|
}
|
|
1268
1483
|
async function listAgentPassportNames() {
|
|
1269
1484
|
try {
|
|
1270
|
-
const entries = await
|
|
1485
|
+
const entries = await readdir2(agentPassportDir());
|
|
1271
1486
|
return entries.filter((e) => e.endsWith(".json")).map((e) => e.slice(0, -5));
|
|
1272
1487
|
} catch (err) {
|
|
1273
1488
|
if (err.code === "ENOENT") return [];
|
|
@@ -1419,95 +1634,6 @@ import {
|
|
|
1419
1634
|
createMCPServer,
|
|
1420
1635
|
createMcpHttpServer
|
|
1421
1636
|
} from "@xnetjs/plugins/node";
|
|
1422
|
-
|
|
1423
|
-
// src/utils/agent-local.ts
|
|
1424
|
-
import { getSigningPublicKeyFromPrivate as getSigningPublicKeyFromPrivate3 } from "@xnetjs/crypto";
|
|
1425
|
-
import { SQLiteNodeStorageAdapter as SQLiteNodeStorageAdapter2, builtInSchemas } from "@xnetjs/data";
|
|
1426
|
-
import { createDID as createDID3 } from "@xnetjs/identity";
|
|
1427
|
-
import { createXNetClient as createXNetClient2 } from "@xnetjs/runtime";
|
|
1428
|
-
var toNodeData = (node) => ({
|
|
1429
|
-
id: node.id,
|
|
1430
|
-
schemaId: node.schemaId,
|
|
1431
|
-
properties: node.properties,
|
|
1432
|
-
deleted: node.deleted ?? false,
|
|
1433
|
-
createdAt: node.createdAt ?? 0,
|
|
1434
|
-
updatedAt: node.updatedAt ?? 0
|
|
1435
|
-
});
|
|
1436
|
-
async function resolveStorage2(db) {
|
|
1437
|
-
if (db) {
|
|
1438
|
-
const { createElectronSQLiteAdapter } = await import("@xnetjs/sqlite/electron");
|
|
1439
|
-
const adapter2 = await createElectronSQLiteAdapter({
|
|
1440
|
-
path: db,
|
|
1441
|
-
busyTimeout: 5e3,
|
|
1442
|
-
foreignKeys: true,
|
|
1443
|
-
walMode: true
|
|
1444
|
-
});
|
|
1445
|
-
return new SQLiteNodeStorageAdapter2(adapter2);
|
|
1446
|
-
}
|
|
1447
|
-
const { createMemorySQLiteAdapter } = await import("@xnetjs/sqlite/memory");
|
|
1448
|
-
const adapter = await createMemorySQLiteAdapter();
|
|
1449
|
-
return new SQLiteNodeStorageAdapter2(adapter);
|
|
1450
|
-
}
|
|
1451
|
-
function builtInSchemaRegistry() {
|
|
1452
|
-
const iris = Object.keys(builtInSchemas).filter((iri) => iri.includes("@"));
|
|
1453
|
-
return {
|
|
1454
|
-
getAllIRIs: () => iris,
|
|
1455
|
-
get: async (iri) => {
|
|
1456
|
-
const loader = builtInSchemas[iri];
|
|
1457
|
-
if (!loader) return null;
|
|
1458
|
-
const schema = await loader();
|
|
1459
|
-
return {
|
|
1460
|
-
iri,
|
|
1461
|
-
name: schema.schema.name,
|
|
1462
|
-
properties: schema.schema.properties
|
|
1463
|
-
};
|
|
1464
|
-
}
|
|
1465
|
-
};
|
|
1466
|
-
}
|
|
1467
|
-
async function createLocalAgentBackend(options) {
|
|
1468
|
-
const agentDID = createDID3(getSigningPublicKeyFromPrivate3(options.agentKey));
|
|
1469
|
-
const nodeStorage = await resolveStorage2(options.db);
|
|
1470
|
-
const client = await createXNetClient2({
|
|
1471
|
-
nodeStorage,
|
|
1472
|
-
authorDID: agentDID,
|
|
1473
|
-
signingKey: options.agentKey
|
|
1474
|
-
});
|
|
1475
|
-
const store = {
|
|
1476
|
-
get: async (id) => {
|
|
1477
|
-
const node = await client.store.get(id);
|
|
1478
|
-
return node ? toNodeData(node) : null;
|
|
1479
|
-
},
|
|
1480
|
-
list: async (opts) => {
|
|
1481
|
-
const nodes = await client.store.list({
|
|
1482
|
-
...opts?.schemaId ? { schemaId: opts.schemaId } : {},
|
|
1483
|
-
...opts?.limit !== void 0 ? { limit: opts.limit } : {},
|
|
1484
|
-
...opts?.offset !== void 0 ? { offset: opts.offset } : {}
|
|
1485
|
-
});
|
|
1486
|
-
return nodes.map(toNodeData);
|
|
1487
|
-
},
|
|
1488
|
-
create: async (opts) => {
|
|
1489
|
-
const node = await client.store.create({
|
|
1490
|
-
...opts.id ? { id: opts.id } : {},
|
|
1491
|
-
schemaId: opts.schemaId,
|
|
1492
|
-
properties: opts.properties
|
|
1493
|
-
});
|
|
1494
|
-
return toNodeData(node);
|
|
1495
|
-
},
|
|
1496
|
-
update: async (id, opts) => {
|
|
1497
|
-
const node = await client.store.update(id, { properties: opts.properties });
|
|
1498
|
-
return toNodeData(node);
|
|
1499
|
-
},
|
|
1500
|
-
delete: async (id) => {
|
|
1501
|
-
await client.store.delete(id);
|
|
1502
|
-
},
|
|
1503
|
-
subscribe: (listener) => client.store.subscribe((event) => {
|
|
1504
|
-
listener({ change: { type: event.change.type }, node: null, isRemote: false });
|
|
1505
|
-
})
|
|
1506
|
-
};
|
|
1507
|
-
return { store, schemas: builtInSchemaRegistry(), client, agentDID };
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
// src/commands/mcp.ts
|
|
1511
1637
|
var defaultBackendFactory = (options) => createRemoteAgentBackend(options);
|
|
1512
1638
|
function buildMcpServer(backend, agent) {
|
|
1513
1639
|
return createMCPServer({
|
|
@@ -1626,7 +1752,7 @@ function parseIntOption3(value) {
|
|
|
1626
1752
|
|
|
1627
1753
|
// src/commands/migrate.ts
|
|
1628
1754
|
import { writeFileSync as writeFileSync4, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
1629
|
-
import { resolve as
|
|
1755
|
+
import { resolve as resolve7 } from "path";
|
|
1630
1756
|
async function getChalk2() {
|
|
1631
1757
|
try {
|
|
1632
1758
|
const chalk2 = await import("chalk");
|
|
@@ -1811,7 +1937,7 @@ async function generateCommand(options) {
|
|
|
1811
1937
|
includeTodos: true
|
|
1812
1938
|
});
|
|
1813
1939
|
if (options.output) {
|
|
1814
|
-
const outputPath =
|
|
1940
|
+
const outputPath = resolve7(process.cwd(), options.output);
|
|
1815
1941
|
if (existsSync2(outputPath) && !options.force) {
|
|
1816
1942
|
console.error(chalk2.red(`Error: File already exists: ${outputPath}`));
|
|
1817
1943
|
console.error(chalk2.gray("Use --force to overwrite."));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnetjs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "xNet CLI - Schema migrations, diagnostics, and development tools",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"chalk": "^5.3.0",
|
|
33
33
|
"commander": "^12.0.0",
|
|
34
|
-
"@xnetjs/core": "2.
|
|
35
|
-
"@xnetjs/crypto": "2.
|
|
36
|
-
"@xnetjs/data": "2.
|
|
34
|
+
"@xnetjs/core": "2.4.0",
|
|
35
|
+
"@xnetjs/crypto": "2.4.0",
|
|
36
|
+
"@xnetjs/data": "2.4.0",
|
|
37
37
|
"@xnetjs/devkit": "1.0.0",
|
|
38
|
-
"@xnetjs/identity": "2.
|
|
39
|
-
"@xnetjs/plugins": "2.
|
|
40
|
-
"@xnetjs/runtime": "0.5.
|
|
41
|
-
"@xnetjs/sqlite": "2.
|
|
42
|
-
"@xnetjs/sync": "2.
|
|
38
|
+
"@xnetjs/identity": "2.4.0",
|
|
39
|
+
"@xnetjs/plugins": "2.4.0",
|
|
40
|
+
"@xnetjs/runtime": "0.5.4",
|
|
41
|
+
"@xnetjs/sqlite": "2.4.0",
|
|
42
|
+
"@xnetjs/sync": "2.4.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"tsup": "^8.0.0",
|