@adapt-toolkit/a2adapt 0.8.1 → 0.9.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 +140 -6
- package/dist/index.js +57 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import * as fs2 from "node:fs";
|
|
|
13
13
|
// src/config.ts
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
15
|
import { homedir } from "node:os";
|
|
16
|
-
import { resolve, join, dirname } from "node:path";
|
|
16
|
+
import { resolve, join, dirname, basename } from "node:path";
|
|
17
17
|
var DEFAULT_CONFIG = {
|
|
18
18
|
brokerUrl: "ws://a2adapt.adaptframework.solutions/broker",
|
|
19
19
|
port: 3030,
|
|
@@ -70,6 +70,29 @@ function writeConfig(cfg) {
|
|
|
70
70
|
}
|
|
71
71
|
return path;
|
|
72
72
|
}
|
|
73
|
+
var IDENTITY_FILENAME = ".a2adapt-identity";
|
|
74
|
+
function buildIdentityFile(opts) {
|
|
75
|
+
if (!opts.name.trim()) throw new Error("identity name must not be empty");
|
|
76
|
+
const obj = { identity: opts.name.trim() };
|
|
77
|
+
if (opts.force) obj.force = true;
|
|
78
|
+
obj.expose_local = opts.exposeLocal ?? true;
|
|
79
|
+
obj.local_auto_accept = opts.localAutoAccept ?? true;
|
|
80
|
+
return obj;
|
|
81
|
+
}
|
|
82
|
+
function resolveIdentityFilePath(target) {
|
|
83
|
+
const abs = resolve(target);
|
|
84
|
+
return basename(abs) === IDENTITY_FILENAME ? abs : join(abs, IDENTITY_FILENAME);
|
|
85
|
+
}
|
|
86
|
+
function writeIdentityFile(target, opts, overwrite = false) {
|
|
87
|
+
const obj = buildIdentityFile(opts);
|
|
88
|
+
const path = resolveIdentityFilePath(target);
|
|
89
|
+
if (!overwrite && fs.existsSync(path)) {
|
|
90
|
+
throw new Error(`${path} already exists \u2014 pass overwrite to replace it`);
|
|
91
|
+
}
|
|
92
|
+
fs.mkdirSync(dirname(path), { recursive: true });
|
|
93
|
+
fs.writeFileSync(path, JSON.stringify(obj, null, 2) + "\n");
|
|
94
|
+
return path;
|
|
95
|
+
}
|
|
73
96
|
|
|
74
97
|
// src/cli.ts
|
|
75
98
|
var CONFIG = loadConfig();
|
|
@@ -274,18 +297,120 @@ daemon is running (pid ${pid}); restart now to apply? [y/N]: `)).trim().toLowerC
|
|
|
274
297
|
const r = spawnSync(process.execPath, [SELF, "start"], { stdio: "inherit" });
|
|
275
298
|
if (r.status !== 0) process.exit(r.status ?? 1);
|
|
276
299
|
}
|
|
277
|
-
function
|
|
300
|
+
function flagPair(argv, on, off) {
|
|
301
|
+
if (argv.includes(off)) return { value: false, set: true };
|
|
302
|
+
if (argv.includes(on)) return { value: true, set: true };
|
|
303
|
+
return { value: void 0, set: false };
|
|
304
|
+
}
|
|
305
|
+
function flagValue(argv, name) {
|
|
306
|
+
const i = argv.indexOf(name);
|
|
307
|
+
return i >= 0 && i + 1 < argv.length ? argv[i + 1] : void 0;
|
|
308
|
+
}
|
|
309
|
+
async function cmdDefineLocalIdentityFile(argv) {
|
|
310
|
+
const name = flagValue(argv, "--name");
|
|
311
|
+
const force = flagPair(argv, "--force-bind", "--no-force-bind");
|
|
312
|
+
const localBook = flagPair(argv, "--local-book", "--no-local-book");
|
|
313
|
+
const autoAccept = flagPair(argv, "--auto-accept-local", "--no-auto-accept-local");
|
|
314
|
+
const overwrite = argv.includes("--overwrite");
|
|
315
|
+
const print = argv.includes("--print");
|
|
316
|
+
const target = flagValue(argv, "--path") ?? flagValue(argv, "--dir") ?? process.cwd();
|
|
317
|
+
const nonInteractive = name !== void 0 || force.set || localBook.set || autoAccept.set || print;
|
|
318
|
+
let opts;
|
|
319
|
+
if (nonInteractive) {
|
|
320
|
+
if (!name || !name.trim()) {
|
|
321
|
+
err("define-local-identity-file: --name is required in non-interactive mode.");
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
opts = {
|
|
325
|
+
name: name.trim(),
|
|
326
|
+
force: force.value ?? false,
|
|
327
|
+
exposeLocal: localBook.value ?? true,
|
|
328
|
+
localAutoAccept: autoAccept.value ?? true
|
|
329
|
+
};
|
|
330
|
+
} else {
|
|
331
|
+
opts = await runIdentitySurvey();
|
|
332
|
+
}
|
|
333
|
+
if (print) {
|
|
334
|
+
out(JSON.stringify(buildIdentityFile(opts), null, 2));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const path = resolveIdentityFilePath(target);
|
|
338
|
+
if (!overwrite && fs2.existsSync(path)) {
|
|
339
|
+
if (nonInteractive) {
|
|
340
|
+
err(`define-local-identity-file: ${path} already exists \u2014 pass --overwrite to replace it.`);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
344
|
+
let ok = false;
|
|
345
|
+
try {
|
|
346
|
+
const ans = (await rl.question(`
|
|
347
|
+
${path} already exists \u2014 overwrite? [y/N]: `)).trim().toLowerCase();
|
|
348
|
+
ok = ans === "y" || ans === "yes";
|
|
349
|
+
} finally {
|
|
350
|
+
rl.close();
|
|
351
|
+
}
|
|
352
|
+
if (!ok) {
|
|
353
|
+
out("aborted \u2014 nothing written.");
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const written = writeIdentityFile(target, opts, true);
|
|
358
|
+
out("");
|
|
359
|
+
out(`wrote ${written}:`);
|
|
360
|
+
out(JSON.stringify(buildIdentityFile(opts), null, 2));
|
|
361
|
+
}
|
|
362
|
+
async function runIdentitySurvey() {
|
|
363
|
+
out(`a2adapt-mcp define-local-identity-file \u2014 interactive`);
|
|
364
|
+
out(`Answer the prompts; the result is written to ${join2(process.cwd(), IDENTITY_FILENAME)}.`);
|
|
365
|
+
out("");
|
|
366
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
367
|
+
try {
|
|
368
|
+
const askYesNo = async (label, def) => {
|
|
369
|
+
const hint = def ? "Y/n" : "y/N";
|
|
370
|
+
const ans = (await rl.question(` ${label} [${hint}]: `)).trim().toLowerCase();
|
|
371
|
+
if (ans === "") return def;
|
|
372
|
+
return ans === "y" || ans === "yes";
|
|
373
|
+
};
|
|
374
|
+
let name = "";
|
|
375
|
+
while (!name) {
|
|
376
|
+
name = (await rl.question(" Identity name: ")).trim();
|
|
377
|
+
if (!name) out(" (name is required)");
|
|
378
|
+
}
|
|
379
|
+
const force = await askYesNo("Force-bind (pin pre-authorizes evicting another session)?", false);
|
|
380
|
+
const exposeLocal = await askYesNo("Add to the host-local contact book?", true);
|
|
381
|
+
const localAutoAccept = await askYesNo("Auto-accept local invites/introductions?", true);
|
|
382
|
+
return { name, force, exposeLocal, localAutoAccept };
|
|
383
|
+
} finally {
|
|
384
|
+
rl.close();
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async function resolveWatchDir() {
|
|
388
|
+
try {
|
|
389
|
+
const ctrl = new AbortController();
|
|
390
|
+
const t = setTimeout(() => ctrl.abort(), 1500);
|
|
391
|
+
const resp = await fetch(`http://127.0.0.1:${PORT}/state-dir`, { signal: ctrl.signal });
|
|
392
|
+
clearTimeout(t);
|
|
393
|
+
if (resp.ok) {
|
|
394
|
+
const body = await resp.json();
|
|
395
|
+
if (typeof body.stateDir === "string" && body.stateDir) return resolve2(body.stateDir);
|
|
396
|
+
}
|
|
397
|
+
} catch {
|
|
398
|
+
}
|
|
399
|
+
return STATE_DIR;
|
|
400
|
+
}
|
|
401
|
+
async function cmdWatch(which) {
|
|
402
|
+
const watchDir = await resolveWatchDir();
|
|
278
403
|
const offsets = /* @__PURE__ */ new Map();
|
|
279
404
|
const scan = (initial) => {
|
|
280
405
|
let names;
|
|
281
406
|
try {
|
|
282
|
-
names = fs2.readdirSync(
|
|
407
|
+
names = fs2.readdirSync(watchDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
283
408
|
} catch {
|
|
284
409
|
return;
|
|
285
410
|
}
|
|
286
411
|
for (const name of names) {
|
|
287
412
|
if (which && name !== which) continue;
|
|
288
|
-
const logPath = join2(
|
|
413
|
+
const logPath = join2(watchDir, name, "notifications.log");
|
|
289
414
|
let size;
|
|
290
415
|
try {
|
|
291
416
|
size = fs2.statSync(logPath).size;
|
|
@@ -334,7 +459,7 @@ function cmdWatch(which) {
|
|
|
334
459
|
}
|
|
335
460
|
};
|
|
336
461
|
err(
|
|
337
|
-
`a2adapt-mcp watch: watching ${which ? `identity "${which}"` : "all identities"} under ${
|
|
462
|
+
`a2adapt-mcp watch: watching ${which ? `identity "${which}"` : "all identities"} under ${watchDir} (Ctrl-C to stop)`
|
|
338
463
|
);
|
|
339
464
|
scan(true);
|
|
340
465
|
const timer = setInterval(() => scan(false), 1e3);
|
|
@@ -482,6 +607,12 @@ function usage() {
|
|
|
482
607
|
out(" serve run in the foreground (used by start; handy for debugging)");
|
|
483
608
|
out(" watch [identity] stream one line per new inbound message (wake source for a Monitor)");
|
|
484
609
|
out("");
|
|
610
|
+
out(" define-local-identity-file write a .a2adapt-identity workspace pin");
|
|
611
|
+
out(" interactive (default): 4-question survey, writes to CWD");
|
|
612
|
+
out(" scripted: --name <s> [--force-bind] [--local-book] [--auto-accept-local]");
|
|
613
|
+
out(" negate with --no-force-bind / --no-local-book / --no-auto-accept-local");
|
|
614
|
+
out(" --dir <path> | --path <file> (default CWD) \xB7 --overwrite \xB7 --print");
|
|
615
|
+
out("");
|
|
485
616
|
out(" install-service install + start a boot-persistent service (systemd/launchd)");
|
|
486
617
|
out(" uninstall-service stop + remove that service");
|
|
487
618
|
out("");
|
|
@@ -531,8 +662,11 @@ async function main() {
|
|
|
531
662
|
case "setup":
|
|
532
663
|
await cmdSetup();
|
|
533
664
|
break;
|
|
665
|
+
case "define-local-identity-file":
|
|
666
|
+
await cmdDefineLocalIdentityFile(process.argv.slice(3));
|
|
667
|
+
break;
|
|
534
668
|
case "watch":
|
|
535
|
-
cmdWatch(process.argv[3]);
|
|
669
|
+
await cmdWatch(process.argv[3]);
|
|
536
670
|
break;
|
|
537
671
|
case "install-service":
|
|
538
672
|
await cmdInstallService();
|
package/dist/index.js
CHANGED
|
@@ -22427,7 +22427,7 @@ var StreamableHTTPServerTransport = class {
|
|
|
22427
22427
|
};
|
|
22428
22428
|
|
|
22429
22429
|
// src/index.ts
|
|
22430
|
-
import { resolve as resolve2, join as join2, dirname as dirname2 } from "node:path";
|
|
22430
|
+
import { resolve as resolve2, join as join2, dirname as dirname2, isAbsolute } from "node:path";
|
|
22431
22431
|
import { fileURLToPath } from "node:url";
|
|
22432
22432
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
22433
22433
|
import { createServer as createHttpServer } from "node:http";
|
|
@@ -22440,7 +22440,7 @@ import { object_to_adapt_value } from "@adapt-toolkit/sdk/wrapper";
|
|
|
22440
22440
|
// src/config.ts
|
|
22441
22441
|
import * as fs from "node:fs";
|
|
22442
22442
|
import { homedir } from "node:os";
|
|
22443
|
-
import { resolve, join, dirname } from "node:path";
|
|
22443
|
+
import { resolve, join, dirname, basename } from "node:path";
|
|
22444
22444
|
var DEFAULT_CONFIG = {
|
|
22445
22445
|
brokerUrl: "ws://a2adapt.adaptframework.solutions/broker",
|
|
22446
22446
|
port: 3030,
|
|
@@ -22487,9 +22487,32 @@ function loadConfig() {
|
|
|
22487
22487
|
gcIntervalMs: envInt("A2ADAPT_GC_INTERVAL_MS") ?? file.gcIntervalMs ?? DEFAULT_CONFIG.gcIntervalMs
|
|
22488
22488
|
};
|
|
22489
22489
|
}
|
|
22490
|
+
var IDENTITY_FILENAME = ".a2adapt-identity";
|
|
22491
|
+
function buildIdentityFile(opts) {
|
|
22492
|
+
if (!opts.name.trim()) throw new Error("identity name must not be empty");
|
|
22493
|
+
const obj = { identity: opts.name.trim() };
|
|
22494
|
+
if (opts.force) obj.force = true;
|
|
22495
|
+
obj.expose_local = opts.exposeLocal ?? true;
|
|
22496
|
+
obj.local_auto_accept = opts.localAutoAccept ?? true;
|
|
22497
|
+
return obj;
|
|
22498
|
+
}
|
|
22499
|
+
function resolveIdentityFilePath(target) {
|
|
22500
|
+
const abs = resolve(target);
|
|
22501
|
+
return basename(abs) === IDENTITY_FILENAME ? abs : join(abs, IDENTITY_FILENAME);
|
|
22502
|
+
}
|
|
22503
|
+
function writeIdentityFile(target, opts, overwrite = false) {
|
|
22504
|
+
const obj = buildIdentityFile(opts);
|
|
22505
|
+
const path = resolveIdentityFilePath(target);
|
|
22506
|
+
if (!overwrite && fs.existsSync(path)) {
|
|
22507
|
+
throw new Error(`${path} already exists \u2014 pass overwrite to replace it`);
|
|
22508
|
+
}
|
|
22509
|
+
fs.mkdirSync(dirname(path), { recursive: true });
|
|
22510
|
+
fs.writeFileSync(path, JSON.stringify(obj, null, 2) + "\n");
|
|
22511
|
+
return path;
|
|
22512
|
+
}
|
|
22490
22513
|
|
|
22491
22514
|
// src/index.ts
|
|
22492
|
-
var VERSION = true ? "0.
|
|
22515
|
+
var VERSION = true ? "0.9.1" : "0.0.0-dev";
|
|
22493
22516
|
var CONFIG = loadConfig();
|
|
22494
22517
|
var STATE_DIR = CONFIG.stateDir;
|
|
22495
22518
|
var BROKER_URL = CONFIG.brokerUrl;
|
|
@@ -23049,6 +23072,32 @@ function createMcpServer(getSessionId) {
|
|
|
23049
23072
|
}
|
|
23050
23073
|
}
|
|
23051
23074
|
);
|
|
23075
|
+
server.tool(
|
|
23076
|
+
"define_local_identity_file",
|
|
23077
|
+
"Write a `.a2adapt-identity` workspace-pin file that ties a directory to an identity, so a future Claude Code session here auto-binds it (and the SessionStart hook arms the right Monitor). Use this instead of hand-writing the file. Because this daemon is shared and its CWD is not the user's project, you MUST pass an absolute `path` (the target directory, or the full path ending in .a2adapt-identity). Refuses to overwrite unless overwrite=true.",
|
|
23078
|
+
{
|
|
23079
|
+
name: external_exports.string().min(1).describe("Identity name the workspace belongs to."),
|
|
23080
|
+
path: external_exports.string().min(1).describe("Absolute target: a directory (file is created inside it) or a full path ending in .a2adapt-identity."),
|
|
23081
|
+
force: external_exports.boolean().default(false).describe("Pin pre-authorizes force-binding (evicting another session) \u2014 no user prompt at bind time."),
|
|
23082
|
+
expose_local: external_exports.boolean().default(true).describe("Publish this identity in the host-local contact book."),
|
|
23083
|
+
local_auto_accept: external_exports.boolean().default(true).describe("Auto-accept local contact-book introductions (false = they queue for approval)."),
|
|
23084
|
+
overwrite: external_exports.boolean().default(false).describe("Replace an existing .a2adapt-identity file.")
|
|
23085
|
+
},
|
|
23086
|
+
async ({ name, path, force, expose_local, local_auto_accept, overwrite }) => {
|
|
23087
|
+
if (!isAbsolute(path)) {
|
|
23088
|
+
return textResult(`define_local_identity_file failed: path must be absolute (got "${path}").`, true);
|
|
23089
|
+
}
|
|
23090
|
+
const opts = { name, force, exposeLocal: expose_local, localAutoAccept: local_auto_accept };
|
|
23091
|
+
try {
|
|
23092
|
+
const written = writeIdentityFile(path, opts, overwrite);
|
|
23093
|
+
const json = JSON.stringify(buildIdentityFile(opts), null, 2);
|
|
23094
|
+
return textResult(`Wrote ${written}:
|
|
23095
|
+
${json}`);
|
|
23096
|
+
} catch (err) {
|
|
23097
|
+
return textResult(`define_local_identity_file failed: ${String(err)}`, true);
|
|
23098
|
+
}
|
|
23099
|
+
}
|
|
23100
|
+
);
|
|
23052
23101
|
server.tool(
|
|
23053
23102
|
"choose_identity",
|
|
23054
23103
|
"Bind an existing identity to this session so the messaging tools act as it. Binding is exclusive: if the identity is already in use by another session, this is declined unless force=true, which evicts the other session. Never pass force=true on your own initiative \u2014 ask the user and get an explicit confirmation first.",
|
|
@@ -23482,6 +23531,11 @@ async function main() {
|
|
|
23482
23531
|
const transports = {};
|
|
23483
23532
|
const httpServer = createHttpServer(async (req, res) => {
|
|
23484
23533
|
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
23534
|
+
if (req.method === "GET" && url.pathname === "/state-dir") {
|
|
23535
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
23536
|
+
res.end(JSON.stringify({ stateDir: STATE_DIR, version: VERSION }));
|
|
23537
|
+
return;
|
|
23538
|
+
}
|
|
23485
23539
|
if (url.pathname !== "/mcp") {
|
|
23486
23540
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
23487
23541
|
res.end("Not found");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adapt-toolkit/a2adapt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "MCP server daemon for a2adapt — one native ADAPT wrapper hosting N self-sovereign identities, exposing secure agent-to-agent messaging tools over HTTP (Streamable HTTP). Run `a2adapt-mcp start`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|