appduct 0.13.0 → 0.15.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.
Files changed (62) hide show
  1. package/README.md +55 -24
  2. package/dist/artifact-inspect.d.ts +38 -39
  3. package/dist/bin.js +106 -7
  4. package/dist/cli/result-types.d.ts +35 -2
  5. package/dist/cli/routes/daemon/index.js +90 -2
  6. package/dist/cli/routes/daemon/run.js +1088 -517
  7. package/dist/cli/routes/daemon/start.js +98 -2
  8. package/dist/cli/routes/daemon/status.js +99 -2
  9. package/dist/cli/routes/daemon/stop.js +98 -2
  10. package/dist/cli/routes/doctor.js +98 -3
  11. package/dist/cli/routes/events/index.d.ts +0 -3
  12. package/dist/cli/routes/events/index.js +92 -3
  13. package/dist/cli/routes/events/ls.d.ts +5 -0
  14. package/dist/cli/routes/events/ls.js +1837 -0
  15. package/dist/cli/routes/events/since.js +98 -2
  16. package/dist/cli/routes/events/tail.js +98 -2
  17. package/dist/cli/routes/init.js +265 -108
  18. package/dist/cli/routes/keygen.js +90 -2
  19. package/dist/cli/routes/mcp.js +243 -25
  20. package/dist/cli/routes/sessions/index.js +90 -2
  21. package/dist/cli/routes/sessions/link.js +199 -22
  22. package/dist/cli/routes/sessions/ls.js +98 -2
  23. package/dist/cli/routes/sessions/revoke.js +98 -2
  24. package/dist/cli/routes/tools/call.js +98 -2
  25. package/dist/cli/routes/tools/describe.js +98 -2
  26. package/dist/cli/routes/tools/index.js +90 -2
  27. package/dist/cli/routes/tools/ls.js +98 -2
  28. package/dist/client/attach-page.d.ts +39 -0
  29. package/dist/client/bootstrap.d.ts +17 -3
  30. package/dist/client/index.d.ts +2 -1
  31. package/dist/client/index.js +286 -112
  32. package/dist/commands/doctor.d.ts +3 -4
  33. package/dist/commands/events-ls.d.ts +21 -0
  34. package/dist/commands/init.d.ts +8 -2
  35. package/dist/commands/link.d.ts +10 -1
  36. package/dist/daemon/calls.d.ts +3 -1
  37. package/dist/daemon/config.d.ts +6 -0
  38. package/dist/daemon/daemon.d.ts +6 -1
  39. package/dist/daemon/event-bus.d.ts +10 -0
  40. package/dist/daemon/links.d.ts +9 -5
  41. package/dist/daemon/listener.d.ts +28 -1
  42. package/dist/daemon/registry.d.ts +20 -12
  43. package/dist/daemon/rpc-errors.d.ts +6 -0
  44. package/dist/daemon/sessions.d.ts +14 -8
  45. package/dist/daemon/tls.d.ts +0 -3
  46. package/dist/devtools-relay/attach-tab.d.ts +33 -0
  47. package/dist/devtools-relay/index.d.ts +8 -0
  48. package/dist/devtools-relay/memory-daemon-sockets.d.ts +20 -0
  49. package/dist/devtools-relay/memory-devtools-browser.d.ts +10 -0
  50. package/dist/devtools-relay/memory-page-channel.d.ts +16 -0
  51. package/dist/devtools-relay/node-daemon-socket.d.ts +3 -0
  52. package/dist/devtools-relay/node-devtools-browser.d.ts +3 -0
  53. package/dist/devtools-relay/ports.d.ts +48 -0
  54. package/dist/devtools-relay/relay.d.ts +16 -0
  55. package/dist/index.js +106 -7
  56. package/dist/link.d.ts +35 -1
  57. package/dist/mcp/app-tools.d.ts +19 -0
  58. package/dist/mcp/connect-tool.d.ts +15 -2
  59. package/dist/mcp/events-tool.d.ts +39 -0
  60. package/dist/native-scheme.d.ts +11 -7
  61. package/dist/scheme.d.ts +5 -1
  62. package/package.json +15 -5
@@ -24,6 +24,7 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
24
24
  "daemonLogMaxBytes",
25
25
  "eventsLogMaxBytes",
26
26
  "policy",
27
+ "webOrigins",
27
28
  "advertisedIp",
28
29
  "scheme",
29
30
  "restartDaemonOnVersionMismatch"
@@ -84,6 +85,7 @@ var defaultConfig = (paths) => {
84
85
  daemonLogMaxBytes: DEFAULT_DAEMON_LOG_MAX_BYTES,
85
86
  eventsLogMaxBytes: 10 * 1024 * 1024,
86
87
  restartDaemonOnVersionMismatch: false,
88
+ webOrigins: [],
87
89
  policy: {
88
90
  default: "allow",
89
91
  destructive: "allow"
@@ -154,6 +156,12 @@ var loadConfig = async (paths, options = {}) => {
154
156
  "restartDaemonOnVersionMismatch"
155
157
  );
156
158
  }
159
+ if (parsed.webOrigins !== void 0) {
160
+ if (!Array.isArray(parsed.webOrigins)) {
161
+ throw configError("webOrigins", "must be an array of origin strings.");
162
+ }
163
+ config.webOrigins = parsed.webOrigins.map((origin) => requireNonEmptyString(origin, "webOrigins"));
164
+ }
157
165
  if (parsed.policy !== void 0) {
158
166
  if (typeof parsed.policy !== "object" || parsed.policy === null || Array.isArray(parsed.policy)) {
159
167
  throw configError("policy", "must be an object.");
@@ -1079,6 +1087,7 @@ var handleEventsCommand = async (options, context) => {
1079
1087
  import pc from "picocolors";
1080
1088
  import {
1081
1089
  formatAgentWebSocketUrl,
1090
+ renderEventSignature,
1082
1091
  renderToolSignature,
1083
1092
  summarizeToolDescription
1084
1093
  } from "@appduct/shared";
@@ -1179,6 +1188,13 @@ var formatDevice = (device) => {
1179
1188
  const label = parts.length > 0 ? parts.join(" ") : "unknown device";
1180
1189
  return device.os ? `${label} (${device.os})` : label;
1181
1190
  };
1191
+ var SUSPEND_REASON_LABELS = {
1192
+ app_backgrounded: "app in background",
1193
+ connection_lost: "connection lost"
1194
+ };
1195
+ var formatSessionState = (session) => {
1196
+ return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
1197
+ };
1182
1198
  var renderLsData = (colors, data, now) => {
1183
1199
  if (data.length === 0) {
1184
1200
  return [colors.dim("Sessions"), " No Appduct sessions are registered."];
@@ -1187,7 +1203,7 @@ var renderLsData = (colors, data, now) => {
1187
1203
  const headers = ["Alias", "State", "Device", "Tools", "Age"];
1188
1204
  const rows = data.map((session) => [
1189
1205
  session.alias,
1190
- session.state,
1206
+ formatSessionState(session),
1191
1207
  formatDevice(session.device),
1192
1208
  String(session.toolCount),
1193
1209
  formatAge(session.claimedAt ?? session.createdAt, nowIso)
@@ -1361,10 +1377,87 @@ var renderToolsListData = (colors, data, flags, full) => {
1361
1377
  }
1362
1378
  return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
1363
1379
  };
1380
+ var renderEventsTruncationLine = (data) => {
1381
+ const offset = data.offset ?? 0;
1382
+ if (data.events.length >= data.total) {
1383
+ return [];
1384
+ }
1385
+ return [
1386
+ "",
1387
+ `Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
1388
+ ];
1389
+ };
1390
+ var renderEventsListData = (colors, data, flags) => {
1391
+ const [only] = data.events;
1392
+ if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
1393
+ return renderFields(
1394
+ colors.green(`Event: ${only.name}`),
1395
+ [
1396
+ ["Signature", renderEventSignature(only)],
1397
+ ["Description", only.description],
1398
+ ["Payload schema", only.payload_schema]
1399
+ ],
1400
+ flags
1401
+ );
1402
+ }
1403
+ if (data.events.length === 0 && data.total > 0) {
1404
+ return [
1405
+ colors.green("Events"),
1406
+ ` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
1407
+ ];
1408
+ }
1409
+ if (data.events.length === 0) {
1410
+ return [
1411
+ colors.green("Events"),
1412
+ data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
1413
+ ];
1414
+ }
1415
+ const lines = data.events.flatMap((event) => [
1416
+ ` ${renderEventSignature(event)}`,
1417
+ ` ${summarizeToolDescription(event.description)}`
1418
+ ]);
1419
+ return [
1420
+ colors.green("Events"),
1421
+ ...lines,
1422
+ ...renderEventsTruncationLine(data),
1423
+ "",
1424
+ "Run `appduct events ls --name <name>` for an event's full payload schema."
1425
+ ];
1426
+ };
1364
1427
  var renderInvokeData = (colors, data, flags) => {
1365
1428
  return [colors.green("Result"), formatScalar(data, flags)];
1366
1429
  };
1367
1430
  var renderLinkData = (colors, data, flags, qr) => {
1431
+ if ("attached" in data) {
1432
+ return [
1433
+ colors.green("Page Attached"),
1434
+ ...renderFields(
1435
+ "Page",
1436
+ [
1437
+ ["Session", data.sessionId],
1438
+ ["Tab", data.targetId],
1439
+ ["URL", data.url],
1440
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1441
+ ],
1442
+ flags
1443
+ )
1444
+ ];
1445
+ }
1446
+ if ("script" in data) {
1447
+ return [
1448
+ colors.green("Link Created"),
1449
+ ...renderFields(
1450
+ "Link",
1451
+ [
1452
+ ["Session", data.sessionId],
1453
+ ["URL", data.url],
1454
+ ["Script", data.script],
1455
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1456
+ ],
1457
+ flags
1458
+ )
1459
+ ];
1460
+ }
1368
1461
  const lines = [
1369
1462
  colors.green("Link Created"),
1370
1463
  ...renderFields(
@@ -1485,6 +1578,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
1485
1578
  ["PID", data.daemon.pid],
1486
1579
  ["Started at", data.daemon.started_at],
1487
1580
  ["WSS port", data.daemon.wss_port],
1581
+ ["Web port", data.daemon.web_port],
1488
1582
  ["Pinned keys", data.daemon.pinned_keys.length],
1489
1583
  ["Sessions", data.daemon.session_count]
1490
1584
  ],
@@ -1552,6 +1646,8 @@ var renderSuccessData = (colors, command, data, options) => {
1552
1646
  return renderToolsListData(colors, data, flags, options.full);
1553
1647
  case "tools describe":
1554
1648
  return renderToolDetail(colors, data, flags);
1649
+ case "events ls":
1650
+ return renderEventsListData(colors, data, flags);
1555
1651
  case "tools call":
1556
1652
  return renderInvokeData(colors, data, flags);
1557
1653
  case "sessions revoke":
@@ -1648,7 +1744,7 @@ var EXIT_CODE_BY_ERROR_TYPE = {
1648
1744
  // EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
1649
1745
  // missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
1650
1746
  // code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
1651
- // assertion failure (docs/tasks/08-appduct-doctor.md).
1747
+ // assertion failure.
1652
1748
  inspection_error: 66,
1653
1749
  connection_error: 70,
1654
1750
  session_error: 71,
@@ -24,6 +24,7 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
24
24
  "daemonLogMaxBytes",
25
25
  "eventsLogMaxBytes",
26
26
  "policy",
27
+ "webOrigins",
27
28
  "advertisedIp",
28
29
  "scheme",
29
30
  "restartDaemonOnVersionMismatch"
@@ -84,6 +85,7 @@ var defaultConfig = (paths) => {
84
85
  daemonLogMaxBytes: DEFAULT_DAEMON_LOG_MAX_BYTES,
85
86
  eventsLogMaxBytes: 10 * 1024 * 1024,
86
87
  restartDaemonOnVersionMismatch: false,
88
+ webOrigins: [],
87
89
  policy: {
88
90
  default: "allow",
89
91
  destructive: "allow"
@@ -154,6 +156,12 @@ var loadConfig = async (paths, options = {}) => {
154
156
  "restartDaemonOnVersionMismatch"
155
157
  );
156
158
  }
159
+ if (parsed.webOrigins !== void 0) {
160
+ if (!Array.isArray(parsed.webOrigins)) {
161
+ throw configError("webOrigins", "must be an array of origin strings.");
162
+ }
163
+ config.webOrigins = parsed.webOrigins.map((origin) => requireNonEmptyString(origin, "webOrigins"));
164
+ }
157
165
  if (parsed.policy !== void 0) {
158
166
  if (typeof parsed.policy !== "object" || parsed.policy === null || Array.isArray(parsed.policy)) {
159
167
  throw configError("policy", "must be an object.");
@@ -1079,6 +1087,7 @@ var handleEventsCommand = async (options, context) => {
1079
1087
  import pc from "picocolors";
1080
1088
  import {
1081
1089
  formatAgentWebSocketUrl,
1090
+ renderEventSignature,
1082
1091
  renderToolSignature,
1083
1092
  summarizeToolDescription
1084
1093
  } from "@appduct/shared";
@@ -1179,6 +1188,13 @@ var formatDevice = (device) => {
1179
1188
  const label = parts.length > 0 ? parts.join(" ") : "unknown device";
1180
1189
  return device.os ? `${label} (${device.os})` : label;
1181
1190
  };
1191
+ var SUSPEND_REASON_LABELS = {
1192
+ app_backgrounded: "app in background",
1193
+ connection_lost: "connection lost"
1194
+ };
1195
+ var formatSessionState = (session) => {
1196
+ return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
1197
+ };
1182
1198
  var renderLsData = (colors, data, now) => {
1183
1199
  if (data.length === 0) {
1184
1200
  return [colors.dim("Sessions"), " No Appduct sessions are registered."];
@@ -1187,7 +1203,7 @@ var renderLsData = (colors, data, now) => {
1187
1203
  const headers = ["Alias", "State", "Device", "Tools", "Age"];
1188
1204
  const rows = data.map((session) => [
1189
1205
  session.alias,
1190
- session.state,
1206
+ formatSessionState(session),
1191
1207
  formatDevice(session.device),
1192
1208
  String(session.toolCount),
1193
1209
  formatAge(session.claimedAt ?? session.createdAt, nowIso)
@@ -1361,10 +1377,87 @@ var renderToolsListData = (colors, data, flags, full) => {
1361
1377
  }
1362
1378
  return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
1363
1379
  };
1380
+ var renderEventsTruncationLine = (data) => {
1381
+ const offset = data.offset ?? 0;
1382
+ if (data.events.length >= data.total) {
1383
+ return [];
1384
+ }
1385
+ return [
1386
+ "",
1387
+ `Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
1388
+ ];
1389
+ };
1390
+ var renderEventsListData = (colors, data, flags) => {
1391
+ const [only] = data.events;
1392
+ if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
1393
+ return renderFields(
1394
+ colors.green(`Event: ${only.name}`),
1395
+ [
1396
+ ["Signature", renderEventSignature(only)],
1397
+ ["Description", only.description],
1398
+ ["Payload schema", only.payload_schema]
1399
+ ],
1400
+ flags
1401
+ );
1402
+ }
1403
+ if (data.events.length === 0 && data.total > 0) {
1404
+ return [
1405
+ colors.green("Events"),
1406
+ ` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
1407
+ ];
1408
+ }
1409
+ if (data.events.length === 0) {
1410
+ return [
1411
+ colors.green("Events"),
1412
+ data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
1413
+ ];
1414
+ }
1415
+ const lines = data.events.flatMap((event) => [
1416
+ ` ${renderEventSignature(event)}`,
1417
+ ` ${summarizeToolDescription(event.description)}`
1418
+ ]);
1419
+ return [
1420
+ colors.green("Events"),
1421
+ ...lines,
1422
+ ...renderEventsTruncationLine(data),
1423
+ "",
1424
+ "Run `appduct events ls --name <name>` for an event's full payload schema."
1425
+ ];
1426
+ };
1364
1427
  var renderInvokeData = (colors, data, flags) => {
1365
1428
  return [colors.green("Result"), formatScalar(data, flags)];
1366
1429
  };
1367
1430
  var renderLinkData = (colors, data, flags, qr) => {
1431
+ if ("attached" in data) {
1432
+ return [
1433
+ colors.green("Page Attached"),
1434
+ ...renderFields(
1435
+ "Page",
1436
+ [
1437
+ ["Session", data.sessionId],
1438
+ ["Tab", data.targetId],
1439
+ ["URL", data.url],
1440
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1441
+ ],
1442
+ flags
1443
+ )
1444
+ ];
1445
+ }
1446
+ if ("script" in data) {
1447
+ return [
1448
+ colors.green("Link Created"),
1449
+ ...renderFields(
1450
+ "Link",
1451
+ [
1452
+ ["Session", data.sessionId],
1453
+ ["URL", data.url],
1454
+ ["Script", data.script],
1455
+ ["Expires", new Date(data.expiresAt * 1e3).toISOString()]
1456
+ ],
1457
+ flags
1458
+ )
1459
+ ];
1460
+ }
1368
1461
  const lines = [
1369
1462
  colors.green("Link Created"),
1370
1463
  ...renderFields(
@@ -1485,6 +1578,7 @@ var renderDaemonStatusData = (colors, data, flags) => {
1485
1578
  ["PID", data.daemon.pid],
1486
1579
  ["Started at", data.daemon.started_at],
1487
1580
  ["WSS port", data.daemon.wss_port],
1581
+ ["Web port", data.daemon.web_port],
1488
1582
  ["Pinned keys", data.daemon.pinned_keys.length],
1489
1583
  ["Sessions", data.daemon.session_count]
1490
1584
  ],
@@ -1552,6 +1646,8 @@ var renderSuccessData = (colors, command, data, options) => {
1552
1646
  return renderToolsListData(colors, data, flags, options.full);
1553
1647
  case "tools describe":
1554
1648
  return renderToolDetail(colors, data, flags);
1649
+ case "events ls":
1650
+ return renderEventsListData(colors, data, flags);
1555
1651
  case "tools call":
1556
1652
  return renderInvokeData(colors, data, flags);
1557
1653
  case "sessions revoke":
@@ -1632,7 +1728,7 @@ var EXIT_CODE_BY_ERROR_TYPE = {
1632
1728
  // EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
1633
1729
  // missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
1634
1730
  // code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
1635
- // assertion failure (docs/tasks/08-appduct-doctor.md).
1731
+ // assertion failure.
1636
1732
  inspection_error: 66,
1637
1733
  connection_error: 70,
1638
1734
  session_error: 71,