@xnetjs/cli 0.1.7 → 0.2.1
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/cli.js +325 -7
- package/package.json +9 -9
package/dist/cli.js
CHANGED
|
@@ -1230,20 +1230,337 @@ function registerDoctorCommand(program2) {
|
|
|
1230
1230
|
});
|
|
1231
1231
|
}
|
|
1232
1232
|
|
|
1233
|
+
// src/commands/enroll.ts
|
|
1234
|
+
import { getSigningPublicKeyFromPrivate as getSigningPublicKeyFromPrivate2 } from "@xnetjs/crypto";
|
|
1235
|
+
import { agentPassportId } from "@xnetjs/data";
|
|
1236
|
+
import { createDID as createDID2, mintAgentPassport } from "@xnetjs/identity";
|
|
1237
|
+
|
|
1238
|
+
// src/utils/agent-passport-file.ts
|
|
1239
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile, chmod, readdir } from "fs/promises";
|
|
1240
|
+
import { homedir } from "os";
|
|
1241
|
+
import { join as join6 } from "path";
|
|
1242
|
+
function agentPassportDir() {
|
|
1243
|
+
return process.env.XNET_AGENT_DIR ?? join6(homedir(), ".xnet", "agents");
|
|
1244
|
+
}
|
|
1245
|
+
var passportPath = (name) => {
|
|
1246
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
1247
|
+
throw new Error(`Invalid agent name: ${name} (use letters, digits, - and _)`);
|
|
1248
|
+
}
|
|
1249
|
+
return join6(agentPassportDir(), `${name}.json`);
|
|
1250
|
+
};
|
|
1251
|
+
async function saveAgentPassportFile(file) {
|
|
1252
|
+
const dir = agentPassportDir();
|
|
1253
|
+
await mkdir2(dir, { recursive: true, mode: 448 });
|
|
1254
|
+
const path = passportPath(file.name);
|
|
1255
|
+
await writeFile(path, JSON.stringify(file, null, 2) + "\n", { mode: 384 });
|
|
1256
|
+
await chmod(path, 384);
|
|
1257
|
+
return path;
|
|
1258
|
+
}
|
|
1259
|
+
async function loadAgentPassportFile(name) {
|
|
1260
|
+
try {
|
|
1261
|
+
const raw = await readFile2(passportPath(name), "utf8");
|
|
1262
|
+
return JSON.parse(raw);
|
|
1263
|
+
} catch (err) {
|
|
1264
|
+
if (err.code === "ENOENT") return null;
|
|
1265
|
+
throw err;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
async function listAgentPassportNames() {
|
|
1269
|
+
try {
|
|
1270
|
+
const entries = await readdir(agentPassportDir());
|
|
1271
|
+
return entries.filter((e) => e.endsWith(".json")).map((e) => e.slice(0, -5));
|
|
1272
|
+
} catch (err) {
|
|
1273
|
+
if (err.code === "ENOENT") return [];
|
|
1274
|
+
throw err;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
function bytesToHex(bytes) {
|
|
1278
|
+
return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1279
|
+
}
|
|
1280
|
+
function hexToBytes2(hex) {
|
|
1281
|
+
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
1282
|
+
return new Uint8Array(Buffer.from(clean, "hex"));
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
// src/commands/enroll.ts
|
|
1286
|
+
var AGENT_PASSPORT_SCHEMA_IRI = "xnet://xnet.fyi/AgentPassport@1.0.0";
|
|
1287
|
+
var RUNTIMES = ["openclaw", "hermes", "claude-code", "other"];
|
|
1288
|
+
var openClawStdioSnippet = (name) => JSON.stringify(
|
|
1289
|
+
{
|
|
1290
|
+
mcp: {
|
|
1291
|
+
servers: {
|
|
1292
|
+
xnet: {
|
|
1293
|
+
command: "xnet",
|
|
1294
|
+
args: ["mcp", "serve", "--agent", name],
|
|
1295
|
+
transport: "stdio"
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
},
|
|
1300
|
+
null,
|
|
1301
|
+
2
|
|
1302
|
+
);
|
|
1303
|
+
var hermesStdioSnippet = (name) => JSON.stringify(
|
|
1304
|
+
{
|
|
1305
|
+
mcpServers: {
|
|
1306
|
+
xnet: { command: "xnet", args: ["mcp", "serve", "--agent", name] }
|
|
1307
|
+
}
|
|
1308
|
+
},
|
|
1309
|
+
null,
|
|
1310
|
+
2
|
|
1311
|
+
);
|
|
1312
|
+
async function runEnroll(name, options) {
|
|
1313
|
+
const keyHex = options.key ?? process.env.XNET_SIGNING_KEY;
|
|
1314
|
+
if (!keyHex) {
|
|
1315
|
+
throw new Error(
|
|
1316
|
+
"Operator signing key required: pass --key <hex> or set $XNET_SIGNING_KEY (an ephemeral operator would break the trust chain)"
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
if (options.space.length === 0) {
|
|
1320
|
+
throw new Error("At least one --space <id> is required (passports are always scoped)");
|
|
1321
|
+
}
|
|
1322
|
+
const operatorKey = hexToBytes2(keyHex);
|
|
1323
|
+
const operatorDID = createDID2(getSigningPublicKeyFromPrivate2(operatorKey));
|
|
1324
|
+
const capabilities = options.space.flatMap(
|
|
1325
|
+
(space) => options.can.map((can) => ({ with: `xnet://space/${space}`, can }))
|
|
1326
|
+
);
|
|
1327
|
+
const grant = mintAgentPassport({
|
|
1328
|
+
operatorDID,
|
|
1329
|
+
operatorKey,
|
|
1330
|
+
capabilities,
|
|
1331
|
+
ttlSeconds: options.ttlDays * 24 * 3600
|
|
1332
|
+
});
|
|
1333
|
+
const passport = {
|
|
1334
|
+
name,
|
|
1335
|
+
runtime: options.runtime,
|
|
1336
|
+
agentDID: grant.agentDID,
|
|
1337
|
+
operatorDID,
|
|
1338
|
+
agentKeyHex: bytesToHex(grant.agentKey),
|
|
1339
|
+
ucan: grant.ucan,
|
|
1340
|
+
expiresAt: grant.expiresAt,
|
|
1341
|
+
capabilities,
|
|
1342
|
+
createdAt: Date.now()
|
|
1343
|
+
};
|
|
1344
|
+
const path = await saveAgentPassportFile(passport);
|
|
1345
|
+
let nodeCreated = false;
|
|
1346
|
+
if (options.node) {
|
|
1347
|
+
try {
|
|
1348
|
+
const backend = await createRemoteAgentBackend(
|
|
1349
|
+
options.apiUrl ? { apiUrl: options.apiUrl } : {}
|
|
1350
|
+
);
|
|
1351
|
+
await backend.store.create({
|
|
1352
|
+
id: agentPassportId(grant.agentDID),
|
|
1353
|
+
schemaId: AGENT_PASSPORT_SCHEMA_IRI,
|
|
1354
|
+
properties: {
|
|
1355
|
+
...options.auditSpace ? { space: options.auditSpace } : {},
|
|
1356
|
+
agentDID: grant.agentDID,
|
|
1357
|
+
operatorDID,
|
|
1358
|
+
displayName: name,
|
|
1359
|
+
runtime: options.runtime,
|
|
1360
|
+
ucan: grant.ucan,
|
|
1361
|
+
expiresAt: grant.expiresAt,
|
|
1362
|
+
status: "active"
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
nodeCreated = true;
|
|
1366
|
+
} catch {
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return {
|
|
1370
|
+
passport,
|
|
1371
|
+
path,
|
|
1372
|
+
nodeCreated,
|
|
1373
|
+
snippets: { openclaw: openClawStdioSnippet(name), hermes: hermesStdioSnippet(name) }
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
function registerAgentEnrollCommand(program2) {
|
|
1377
|
+
const agent = program2.command("agent").description("Enroll and manage external agent passports (exploration 0337)");
|
|
1378
|
+
agent.command("enroll <name>").description("Mint a scoped Agent Passport (own DID + operator-delegated UCAN)").option("--runtime <runtime>", `Agent runtime: ${RUNTIMES.join("|")}`, "other").option("--space <id...>", "Space ids the agent may write (repeatable)", []).option("--can <action...>", "Delegated actions", ["node/create", "node/update"]).option("--ttl-days <n>", "Passport lifetime in days (rotate weekly)", parseFloatOption, 7).option("--key <hex>", "Operator Ed25519 signing key (hex); falls back to $XNET_SIGNING_KEY").option("--audit-space <id>", "Space to home the AgentPassport node in").option("--api-url <url>", "xNet local API URL (default http://127.0.0.1:31415)").option("--no-node", "Skip recording the AgentPassport node in the workspace").action(async (name, options) => {
|
|
1379
|
+
if (!RUNTIMES.includes(options.runtime)) {
|
|
1380
|
+
throw new Error(`Unknown runtime: ${options.runtime} (use ${RUNTIMES.join("|")})`);
|
|
1381
|
+
}
|
|
1382
|
+
const result = await runEnroll(name, options);
|
|
1383
|
+
console.log(`Agent passport minted: ${result.passport.agentDID}`);
|
|
1384
|
+
console.log(` runtime: ${result.passport.runtime}`);
|
|
1385
|
+
console.log(` expires: ${new Date(result.passport.expiresAt).toISOString()}`);
|
|
1386
|
+
console.log(` saved: ${result.path} (0600 \u2014 contains the agent's private key)`);
|
|
1387
|
+
console.log(
|
|
1388
|
+
` workspace node: ${result.nodeCreated ? "recorded" : "skipped (API unreachable or --no-node)"}`
|
|
1389
|
+
);
|
|
1390
|
+
console.log("\nCapabilities:");
|
|
1391
|
+
for (const cap of result.passport.capabilities) {
|
|
1392
|
+
console.log(` ${cap.can} ${cap.with}`);
|
|
1393
|
+
}
|
|
1394
|
+
console.log("\nOpenClaw (~/.openclaw/openclaw.json):\n" + result.snippets.openclaw);
|
|
1395
|
+
console.log("\nHermes Agent:\n" + result.snippets.hermes);
|
|
1396
|
+
console.log(
|
|
1397
|
+
`
|
|
1398
|
+
Hub: add the operator DID to trustedDids so self-issued tokens are rejected:
|
|
1399
|
+
trustedDids: ["${result.passport.operatorDID}"]`
|
|
1400
|
+
);
|
|
1401
|
+
});
|
|
1402
|
+
agent.command("list").description("List enrolled agent passports").action(async () => {
|
|
1403
|
+
const names = await listAgentPassportNames();
|
|
1404
|
+
if (names.length === 0) {
|
|
1405
|
+
console.log("No agent passports enrolled (xnet agent enroll <name> --space <id>)");
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
for (const name of names) console.log(name);
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
function parseFloatOption(value) {
|
|
1412
|
+
const parsed = Number.parseFloat(value);
|
|
1413
|
+
if (!Number.isFinite(parsed) || parsed <= 0) throw new Error(`Invalid number: ${value}`);
|
|
1414
|
+
return parsed;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1233
1417
|
// src/commands/mcp.ts
|
|
1234
1418
|
import {
|
|
1235
1419
|
createMCPServer,
|
|
1236
1420
|
createMcpHttpServer
|
|
1237
1421
|
} 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
|
|
1238
1511
|
var defaultBackendFactory = (options) => createRemoteAgentBackend(options);
|
|
1239
|
-
function buildMcpServer(backend) {
|
|
1240
|
-
return createMCPServer({
|
|
1512
|
+
function buildMcpServer(backend, agent) {
|
|
1513
|
+
return createMCPServer({
|
|
1514
|
+
store: backend.store,
|
|
1515
|
+
schemas: backend.schemas,
|
|
1516
|
+
...agent ? {
|
|
1517
|
+
agentAudit: {
|
|
1518
|
+
agentDID: agent.passport.agentDID,
|
|
1519
|
+
sessionKey: `${agent.passport.runtime}:${agent.passport.name}`,
|
|
1520
|
+
channel: "other",
|
|
1521
|
+
...agent.auditSpaceId ? { spaceId: agent.auditSpaceId } : {}
|
|
1522
|
+
}
|
|
1523
|
+
} : {}
|
|
1524
|
+
});
|
|
1241
1525
|
}
|
|
1242
1526
|
async function startMcpServe(backendFactory, options) {
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1527
|
+
let agent;
|
|
1528
|
+
let backend;
|
|
1529
|
+
if (options.agent) {
|
|
1530
|
+
const passport = await loadAgentPassportFile(options.agent);
|
|
1531
|
+
if (!passport) {
|
|
1532
|
+
throw new Error(
|
|
1533
|
+
`No passport for agent "${options.agent}" (run: xnet agent enroll ${options.agent} --space <id>)`
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
if (passport.expiresAt <= Date.now()) {
|
|
1537
|
+
throw new Error(
|
|
1538
|
+
`Passport for "${options.agent}" expired ${new Date(passport.expiresAt).toISOString()} \u2014 re-enroll to rotate`
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
agent = {
|
|
1542
|
+
passport,
|
|
1543
|
+
...options.auditSpace ? { auditSpaceId: options.auditSpace } : {}
|
|
1544
|
+
};
|
|
1545
|
+
if (options.db) {
|
|
1546
|
+
backend = await createLocalAgentBackend({
|
|
1547
|
+
db: options.db,
|
|
1548
|
+
agentKey: hexToBytes2(passport.agentKeyHex)
|
|
1549
|
+
});
|
|
1550
|
+
} else {
|
|
1551
|
+
console.error(
|
|
1552
|
+
"warning: --agent without --db serves over the local API; writes are signed by the app identity, not the agent DID"
|
|
1553
|
+
);
|
|
1554
|
+
backend = await backendFactory({
|
|
1555
|
+
...options.apiUrl ? { apiUrl: options.apiUrl } : {}
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
} else {
|
|
1559
|
+
backend = await backendFactory({
|
|
1560
|
+
...options.apiUrl ? { apiUrl: options.apiUrl } : {}
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
const server = buildMcpServer(backend, agent);
|
|
1247
1564
|
if (options.http) {
|
|
1248
1565
|
const http = createMcpHttpServer({
|
|
1249
1566
|
server,
|
|
@@ -1285,7 +1602,7 @@ function registerMcpCommand(program2, backendFactory = defaultBackendFactory) {
|
|
|
1285
1602
|
mcp.command("serve").description("Start an MCP server (stdio by default; --http for browser/OpenClaw clients)").option("--http", "Serve over hardened loopback HTTP instead of stdio").option("--host <host>", "Loopback host for --http (default 127.0.0.1)").option("--port <n>", "Port for --http (default 31416)", parseIntOption3).option(
|
|
1286
1603
|
"--allow-origin <origin...>",
|
|
1287
1604
|
"Browser origins permitted for --http (e.g. https://user.github.io)"
|
|
1288
|
-
).option("--pairing-token <token>", "Shared secret for --http (generated if omitted)").option("--api-url <url>", "xNet local API URL (default http://127.0.0.1:31415)").action(async (options) => {
|
|
1605
|
+
).option("--pairing-token <token>", "Shared secret for --http (generated if omitted)").option("--api-url <url>", "xNet local API URL (default http://127.0.0.1:31415)").option("--agent <name>", "Serve as an enrolled agent passport (exploration 0337)").option("--db <path>", "With --agent: agent-signed local SQLite store").option("--audit-space <id>", "With --agent: Space to home audit records in").action(async (options) => {
|
|
1289
1606
|
const handle = await startMcpServe(backendFactory, options);
|
|
1290
1607
|
if (handle.mode === "http" && handle.http) {
|
|
1291
1608
|
console.error(`xNet MCP server listening on ${handle.http.url}${handle.http.path}`);
|
|
@@ -1760,6 +2077,7 @@ registerMigrateCommand(program);
|
|
|
1760
2077
|
registerSchemaCommand(program);
|
|
1761
2078
|
registerDoctorCommand(program);
|
|
1762
2079
|
registerAgentCommands(program);
|
|
2080
|
+
registerAgentEnrollCommand(program);
|
|
1763
2081
|
registerMcpCommand(program);
|
|
1764
2082
|
registerBridgeCommand(program);
|
|
1765
2083
|
registerCodeCommand(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnetjs/cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
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.2.0",
|
|
35
|
+
"@xnetjs/crypto": "2.2.0",
|
|
36
|
+
"@xnetjs/data": "2.2.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.2.0",
|
|
39
|
+
"@xnetjs/plugins": "2.2.0",
|
|
40
|
+
"@xnetjs/runtime": "0.5.2",
|
|
41
|
+
"@xnetjs/sqlite": "2.2.0",
|
|
42
|
+
"@xnetjs/sync": "2.2.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"tsup": "^8.0.0",
|