appduct 0.11.1 → 0.13.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 (74) hide show
  1. package/README.md +43 -38
  2. package/dist/bin.js +50 -36
  3. package/dist/cli/command-options.d.ts +7 -17
  4. package/dist/cli/result-types.d.ts +11 -8
  5. package/dist/cli/routes/daemon/index.js +12 -16
  6. package/dist/cli/routes/daemon/run.js +216 -46
  7. package/dist/cli/routes/daemon/start.js +18 -16
  8. package/dist/cli/routes/daemon/status.js +18 -16
  9. package/dist/cli/routes/daemon/stop.js +18 -16
  10. package/dist/cli/routes/doctor.js +12 -16
  11. package/dist/cli/routes/events/index.d.ts +9 -0
  12. package/dist/cli/routes/events/index.js +842 -0
  13. package/dist/cli/routes/events/shared-options.d.ts +12 -0
  14. package/dist/cli/routes/events/shared-options.js +102 -0
  15. package/dist/cli/routes/events/since.d.ts +6 -0
  16. package/dist/cli/routes/events/since.js +1977 -0
  17. package/dist/cli/routes/events/tail.d.ts +6 -0
  18. package/dist/cli/routes/{events.js → events/tail.js} +184 -162
  19. package/dist/cli/routes/init.js +14 -18
  20. package/dist/cli/routes/keygen.js +13 -16
  21. package/dist/cli/routes/mcp.js +193 -175
  22. package/dist/cli/routes/sessions/index.d.ts +7 -0
  23. package/dist/cli/routes/sessions/index.js +843 -0
  24. package/dist/cli/routes/sessions/link.d.ts +4 -0
  25. package/dist/cli/routes/{link.js → sessions/link.js} +19 -17
  26. package/dist/cli/routes/sessions/ls.d.ts +4 -0
  27. package/dist/cli/routes/{ls.js → sessions/ls.js} +19 -17
  28. package/dist/cli/routes/sessions/revoke.d.ts +4 -0
  29. package/dist/cli/routes/{revoke.js → sessions/revoke.js} +20 -18
  30. package/dist/cli/routes/tools/call.d.ts +4 -0
  31. package/dist/cli/routes/{invoke.js → tools/call.js} +21 -19
  32. package/dist/cli/routes/tools/describe.d.ts +6 -0
  33. package/dist/cli/routes/tools/describe.js +1837 -0
  34. package/dist/cli/routes/tools/index.d.ts +6 -0
  35. package/dist/cli/routes/tools/index.js +843 -0
  36. package/dist/cli/routes/tools/ls.d.ts +4 -0
  37. package/dist/cli/routes/{tools.js → tools/ls.js} +46 -73
  38. package/dist/cli/routes/tools/shared-options.d.ts +16 -0
  39. package/dist/cli/routes/tools/shared-options.js +122 -0
  40. package/dist/client/app-client.d.ts +96 -37
  41. package/dist/client/index.d.ts +3 -2
  42. package/dist/client/index.js +183 -139
  43. package/dist/commands/events.d.ts +31 -13
  44. package/dist/commands/invoke.d.ts +1 -1
  45. package/dist/commands/link.d.ts +1 -1
  46. package/dist/commands/ls.d.ts +1 -1
  47. package/dist/commands/revoke.d.ts +1 -1
  48. package/dist/commands/tools.d.ts +12 -13
  49. package/dist/daemon/config.d.ts +5 -2
  50. package/dist/daemon/daemon.d.ts +3 -0
  51. package/dist/daemon/event-bus.d.ts +36 -4
  52. package/dist/daemon/events-log.d.ts +31 -0
  53. package/dist/daemon/memory-append-only-file.d.ts +17 -0
  54. package/dist/daemon/node-append-only-file.d.ts +9 -0
  55. package/dist/daemon/rpc-errors.d.ts +1 -1
  56. package/dist/daemon/state-dir.d.ts +2 -0
  57. package/dist/errors.d.ts +1 -1
  58. package/dist/events/index.d.ts +60 -0
  59. package/dist/index.js +62 -39
  60. package/dist/link.d.ts +1 -1
  61. package/dist/mcp/app-tools.d.ts +3 -2
  62. package/dist/mcp/connect-tool.d.ts +1 -1
  63. package/dist/mcp/events-tool.d.ts +28 -34
  64. package/dist/native-scheme.d.ts +1 -1
  65. package/dist/output.d.ts +29 -6
  66. package/dist/rpc/client.d.ts +1 -1
  67. package/dist/scheme.d.ts +4 -4
  68. package/package.json +2 -2
  69. package/dist/cli/routes/events.d.ts +0 -3
  70. package/dist/cli/routes/invoke.d.ts +0 -3
  71. package/dist/cli/routes/link.d.ts +0 -3
  72. package/dist/cli/routes/ls.d.ts +0 -3
  73. package/dist/cli/routes/revoke.d.ts +0 -3
  74. package/dist/cli/routes/tools.d.ts +0 -3
@@ -0,0 +1,6 @@
1
+ /** Route for `appduct events tail` — loaded by `routes/events/index.ts`'s router only when it
2
+ * runs (issue #96; replaces the removed `appduct events` streaming form). `--follow` is accepted
3
+ * (declared at the `events` noun level in `create-cli.ts`) but never read here: the default
4
+ * behavior already follows, so the flag exists only for script readability. */
5
+ import type { Route } from "../../router.js";
6
+ export declare const route: Route;
@@ -22,6 +22,7 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
22
22
  "eventBufferSize",
23
23
  "auditRetentionDays",
24
24
  "daemonLogMaxBytes",
25
+ "eventsLogMaxBytes",
25
26
  "policy",
26
27
  "advertisedIp",
27
28
  "scheme",
@@ -81,6 +82,7 @@ var defaultConfig = (paths) => {
81
82
  eventBufferSize: 256,
82
83
  auditRetentionDays: DEFAULT_AUDIT_RETENTION_DAYS,
83
84
  daemonLogMaxBytes: DEFAULT_DAEMON_LOG_MAX_BYTES,
85
+ eventsLogMaxBytes: 10 * 1024 * 1024,
84
86
  restartDaemonOnVersionMismatch: false,
85
87
  policy: {
86
88
  default: "allow",
@@ -143,6 +145,9 @@ var loadConfig = async (paths, options = {}) => {
143
145
  if (parsed.daemonLogMaxBytes !== void 0) {
144
146
  config.daemonLogMaxBytes = requirePositiveInteger(parsed.daemonLogMaxBytes, "daemonLogMaxBytes");
145
147
  }
148
+ if (parsed.eventsLogMaxBytes !== void 0) {
149
+ config.eventsLogMaxBytes = requirePositiveInteger(parsed.eventsLogMaxBytes, "eventsLogMaxBytes");
150
+ }
146
151
  if (parsed.restartDaemonOnVersionMismatch !== void 0) {
147
152
  config.restartDaemonOnVersionMismatch = requireBoolean(
148
153
  parsed.restartDaemonOnVersionMismatch,
@@ -243,6 +248,7 @@ var getStateDirPaths = (stateDir) => {
243
248
  socketPath: join(stateDir, "daemon.sock"),
244
249
  pidFilePath: join(stateDir, "daemon.pid"),
245
250
  logFilePath: join(stateDir, "daemon.log"),
251
+ eventsLogPath: join(stateDir, "events.log"),
246
252
  keyPath: join(stateDir, "key.pem"),
247
253
  configPath: join(stateDir, "config.json"),
248
254
  auditDir: join(stateDir, "audit"),
@@ -1009,12 +1015,14 @@ var handleEventsSinceCommand = async (options, context) => {
1009
1015
  try {
1010
1016
  const since = await stream.call(RPC_METHODS2.eventsSince, {
1011
1017
  selector: options.selector,
1012
- since: options.since
1018
+ since: options.since,
1019
+ name: options.name,
1020
+ payloadMaxBytes: options.payloadMaxBytes
1013
1021
  });
1014
1022
  for (const event of since.events) {
1015
1023
  context.onEvent(event);
1016
1024
  }
1017
- context.onCursor?.(since.cursor);
1025
+ context.onCursor?.({ cursor: since.cursor, dropped: since.dropped, remaining: since.remaining });
1018
1026
  } finally {
1019
1027
  stream.close();
1020
1028
  }
@@ -1028,7 +1036,10 @@ var handleEventsCommand = async (options, context) => {
1028
1036
  const stream = await openDaemonStream({ stateDir: context.stateDir, spawn: context.spawn });
1029
1037
  try {
1030
1038
  await stream.call(RPC_METHODS2.eventsSubscribe, {
1031
- sessionSelector: options.selector
1039
+ sessionSelector: options.selector,
1040
+ kinds: ["app_event"],
1041
+ name: options.name,
1042
+ payloadMaxBytes: options.payloadMaxBytes
1032
1043
  });
1033
1044
  } catch (error) {
1034
1045
  stream.close();
@@ -1064,124 +1075,6 @@ var handleEventsCommand = async (options, context) => {
1064
1075
  };
1065
1076
  };
1066
1077
 
1067
- // src/errors.ts
1068
- var EXIT_CODE_BY_ERROR_TYPE = {
1069
- usage_error: 64,
1070
- validation_error: 65,
1071
- // EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
1072
- // missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
1073
- // code here so a release pipeline can't mistake "we couldn't tell" for either a clean pass or an
1074
- // assertion failure (docs/tasks/08-appduct-doctor.md).
1075
- inspection_error: 66,
1076
- connection_error: 70,
1077
- session_error: 71,
1078
- tool_error: 72,
1079
- permission_error: 77,
1080
- internal_error: 1,
1081
- // `doctor --assert-present`/`--assert-absent`: the artifact was inspected successfully and the
1082
- // assertion did not hold. Distinct from `inspection_error` (66) — this means the answer is known
1083
- // and it's "no", not "couldn't tell" — and distinct from `internal_error` (1) so a caller can't
1084
- // conflate a normal assertion failure with an unexpected crash.
1085
- assertion_error: 3
1086
- };
1087
- var RPC_ERROR_EXIT_CLASS = {
1088
- no_session: "session_error",
1089
- ambiguous_session: "session_error",
1090
- unknown_session: "session_error",
1091
- session_not_active: "session_error",
1092
- session_suspended: "session_error",
1093
- tool_not_found: "tool_error",
1094
- tool_input_validation_error: "tool_error",
1095
- tool_output_validation_error: "tool_error",
1096
- tool_execution_error: "tool_error",
1097
- tool_serialization_error: "tool_error",
1098
- tool_timeout: "tool_error",
1099
- tool_cancelled: "tool_error",
1100
- policy_denied: "permission_error",
1101
- invalid_request: "validation_error"
1102
- };
1103
- var AppductCliError = class extends Error {
1104
- type;
1105
- details;
1106
- constructor(type, message, details) {
1107
- super(message);
1108
- this.name = "AppductCliError";
1109
- this.type = type;
1110
- this.details = details;
1111
- }
1112
- };
1113
- var usageError = (message, details) => {
1114
- return new AppductCliError("usage_error", message, details);
1115
- };
1116
- var isAppductCliError = (value) => {
1117
- return value instanceof AppductCliError;
1118
- };
1119
- var rpcErrorClass = (error) => {
1120
- const type = error.data?.type;
1121
- return type ? RPC_ERROR_EXIT_CLASS[type] : void 0;
1122
- };
1123
- var toCliError = (error) => {
1124
- if (isAppductCliError(error)) {
1125
- return {
1126
- type: error.type,
1127
- message: error.message,
1128
- details: error.details
1129
- };
1130
- }
1131
- if (error instanceof DaemonVersionMismatchError) {
1132
- return {
1133
- type: "connection_error",
1134
- message: error.message,
1135
- details: {
1136
- daemon_version: error.daemonVersion,
1137
- client_version: error.clientVersion,
1138
- session_count: error.sessionCount,
1139
- pending_link_count: error.pendingLinkCount
1140
- }
1141
- };
1142
- }
1143
- if (error instanceof DaemonRpcError && error.data?.type) {
1144
- return {
1145
- type: error.data.type,
1146
- message: error.message,
1147
- details: error.data.details
1148
- };
1149
- }
1150
- if (isDaemonUnreachableError(error)) {
1151
- return {
1152
- type: "connection_error",
1153
- message: error instanceof Error ? error.message : "The Appduct daemon is unreachable."
1154
- };
1155
- }
1156
- if (error instanceof Error) {
1157
- return {
1158
- type: "internal_error",
1159
- message: error.message
1160
- };
1161
- }
1162
- return {
1163
- type: "internal_error",
1164
- message: "An unexpected error occurred.",
1165
- details: error
1166
- };
1167
- };
1168
- var getExitCodeForError = (error) => {
1169
- if (isAppductCliError(error)) {
1170
- return EXIT_CODE_BY_ERROR_TYPE[error.type];
1171
- }
1172
- if (error instanceof DaemonVersionMismatchError) {
1173
- return EXIT_CODE_BY_ERROR_TYPE.connection_error;
1174
- }
1175
- if (error instanceof DaemonRpcError) {
1176
- const cliErrorClass = rpcErrorClass(error);
1177
- return EXIT_CODE_BY_ERROR_TYPE[cliErrorClass ?? "internal_error"];
1178
- }
1179
- if (isDaemonUnreachableError(error)) {
1180
- return EXIT_CODE_BY_ERROR_TYPE.connection_error;
1181
- }
1182
- return EXIT_CODE_BY_ERROR_TYPE.internal_error;
1183
- };
1184
-
1185
1078
  // src/output.ts
1186
1079
  import pc from "picocolors";
1187
1080
  import {
@@ -1308,9 +1201,6 @@ var renderLsData = (colors, data, now) => {
1308
1201
  ...rows.map((row) => ` ${row.map((cell, index) => cell.padEnd(widths[index])).join(" ")}`)
1309
1202
  ];
1310
1203
  };
1311
- var isToolsListing = (data) => {
1312
- return typeof data === "object" && data !== null && Array.isArray(data.tools);
1313
- };
1314
1204
  var isToolGroupsListing = (data) => {
1315
1205
  return typeof data === "object" && data !== null && Array.isArray(data.groups) && !Array.isArray(data.tools);
1316
1206
  };
@@ -1328,7 +1218,7 @@ var renderEmptyToolsLine = (data) => {
1328
1218
  }
1329
1219
  if (data.group !== void 0) {
1330
1220
  const match = data.filter === void 0 ? "" : ` match ${JSON.stringify(data.filter)}`;
1331
- return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools --groups\` to see the session's groups.`;
1221
+ return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools ls --groups\` to see the session's groups.`;
1332
1222
  }
1333
1223
  return data.filter === void 0 ? " No tools registered." : ` No tools match ${JSON.stringify(data.filter)}.`;
1334
1224
  };
@@ -1410,7 +1300,7 @@ var renderToolSummaryTable = (colors, data) => {
1410
1300
  ...grouped ? renderGroupedToolLines(colors, data.tools) : data.tools.flatMap((tool) => renderToolSummaryLines(tool, " ")),
1411
1301
  ...renderTruncationLine(data),
1412
1302
  "",
1413
- "Run `appduct tools <name>` for a tool's full schema."
1303
+ "Run `appduct tools describe <name>` for a tool's full schema."
1414
1304
  ];
1415
1305
  };
1416
1306
  var renderToolDetail = (colors, tool, flags) => {
@@ -1462,16 +1352,13 @@ var renderToolGroups = (colors, data) => {
1462
1352
  colors.green("Groups"),
1463
1353
  ...data.groups.map((entry) => ` ${label(entry).padEnd(width)} ${String(entry.total).padStart(countWidth)}`),
1464
1354
  "",
1465
- `${data.total} tool${data.total === 1 ? "" : "s"} in total. ` + (hasAnyGroup(data.groups) ? "Run `appduct tools --group <name>` to list one group's tools." : "No tool declares a group.")
1355
+ `${data.total} tool${data.total === 1 ? "" : "s"} in total. ` + (hasAnyGroup(data.groups) ? "Run `appduct tools ls --group <name>` to list one group's tools." : "No tool declares a group.")
1466
1356
  ];
1467
1357
  };
1468
- var renderToolsData = (colors, data, flags, full) => {
1358
+ var renderToolsListData = (colors, data, flags, full) => {
1469
1359
  if (isToolGroupsListing(data)) {
1470
1360
  return renderToolGroups(colors, data);
1471
1361
  }
1472
- if (!isToolsListing(data)) {
1473
- return renderToolDetail(colors, data, flags);
1474
- }
1475
1362
  return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
1476
1363
  };
1477
1364
  var renderInvokeData = (colors, data, flags) => {
@@ -1657,15 +1544,17 @@ var renderSuccessData = (colors, command, data, options) => {
1657
1544
  return renderInitData(colors, data, flags);
1658
1545
  case "keygen":
1659
1546
  return renderKeygenData(colors, data, flags);
1660
- case "link":
1547
+ case "sessions link":
1661
1548
  return renderLinkData(colors, data, flags, options.qr);
1662
- case "ls":
1549
+ case "sessions ls":
1663
1550
  return renderLsData(colors, data, options.now ?? /* @__PURE__ */ new Date());
1664
- case "tools":
1665
- return renderToolsData(colors, data, flags, options.full);
1666
- case "invoke":
1551
+ case "tools ls":
1552
+ return renderToolsListData(colors, data, flags, options.full);
1553
+ case "tools describe":
1554
+ return renderToolDetail(colors, data, flags);
1555
+ case "tools call":
1667
1556
  return renderInvokeData(colors, data, flags);
1668
- case "revoke":
1557
+ case "sessions revoke":
1669
1558
  return renderRevokeData(colors, data);
1670
1559
  case "daemon run":
1671
1560
  return renderDaemonRunData(colors, data, flags);
@@ -1735,25 +1624,158 @@ var renderEventLine = (event, flags) => {
1735
1624
  const dataSuffix = event.data === void 0 ? "" : ` ${formatJson(event.data, flags)}`;
1736
1625
  return `${colors.dim(timestamp)} ${colors.green(event.kind)}${target ? ` ${target}` : ""}${dataSuffix}`;
1737
1626
  };
1738
- var renderEventsCursorLine = (cursor, flags) => {
1739
- if (flags.json) {
1740
- return JSON.stringify({ cursor });
1627
+
1628
+ // src/errors.ts
1629
+ var EXIT_CODE_BY_ERROR_TYPE = {
1630
+ usage_error: 64,
1631
+ validation_error: 65,
1632
+ // EX_NOINPUT (66): "an input file did not exist or was not readable" — `doctor` throws this for a
1633
+ // missing external tool or an unreadable/corrupt artifact, deliberately distinct from every other
1634
+ // 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).
1636
+ inspection_error: 66,
1637
+ connection_error: 70,
1638
+ session_error: 71,
1639
+ tool_error: 72,
1640
+ permission_error: 77,
1641
+ internal_error: 1,
1642
+ // `doctor --assert-present`/`--assert-absent`: the artifact was inspected successfully and the
1643
+ // assertion did not hold. Distinct from `inspection_error` (66) — this means the answer is known
1644
+ // and it's "no", not "couldn't tell" — and distinct from `internal_error` (1) so a caller can't
1645
+ // conflate a normal assertion failure with an unexpected crash.
1646
+ assertion_error: 3
1647
+ };
1648
+ var RPC_ERROR_EXIT_CLASS = {
1649
+ no_session: "session_error",
1650
+ ambiguous_session: "session_error",
1651
+ unknown_session: "session_error",
1652
+ session_not_active: "session_error",
1653
+ session_suspended: "session_error",
1654
+ tool_not_found: "tool_error",
1655
+ tool_input_validation_error: "tool_error",
1656
+ tool_output_validation_error: "tool_error",
1657
+ tool_execution_error: "tool_error",
1658
+ tool_serialization_error: "tool_error",
1659
+ tool_timeout: "tool_error",
1660
+ tool_cancelled: "tool_error",
1661
+ policy_denied: "permission_error",
1662
+ invalid_request: "validation_error"
1663
+ };
1664
+ var AppductCliError = class extends Error {
1665
+ type;
1666
+ details;
1667
+ constructor(type, message, details) {
1668
+ super(message);
1669
+ this.name = "AppductCliError";
1670
+ this.type = type;
1671
+ this.details = details;
1741
1672
  }
1742
- const colors = pc.createColors(flags.color);
1743
- return colors.dim(`cursor: ${cursor} (pass --since ${cursor} to resume from here)`);
1673
+ };
1674
+ var usageError = (message, details) => {
1675
+ return new AppductCliError("usage_error", message, details);
1676
+ };
1677
+ var isAppductCliError = (value) => {
1678
+ return value instanceof AppductCliError;
1679
+ };
1680
+ var rpcErrorClass = (error) => {
1681
+ const type = error.data?.type;
1682
+ return type ? RPC_ERROR_EXIT_CLASS[type] : void 0;
1683
+ };
1684
+ var toCliError = (error) => {
1685
+ if (isAppductCliError(error)) {
1686
+ return {
1687
+ type: error.type,
1688
+ message: error.message,
1689
+ details: error.details
1690
+ };
1691
+ }
1692
+ if (error instanceof DaemonVersionMismatchError) {
1693
+ return {
1694
+ type: "connection_error",
1695
+ message: error.message,
1696
+ details: {
1697
+ daemon_version: error.daemonVersion,
1698
+ client_version: error.clientVersion,
1699
+ session_count: error.sessionCount,
1700
+ pending_link_count: error.pendingLinkCount
1701
+ }
1702
+ };
1703
+ }
1704
+ if (error instanceof DaemonRpcError && error.data?.type) {
1705
+ return {
1706
+ type: error.data.type,
1707
+ message: error.message,
1708
+ details: error.data.details
1709
+ };
1710
+ }
1711
+ if (isDaemonUnreachableError(error)) {
1712
+ return {
1713
+ type: "connection_error",
1714
+ message: error instanceof Error ? error.message : "The Appduct daemon is unreachable."
1715
+ };
1716
+ }
1717
+ if (error instanceof Error) {
1718
+ return {
1719
+ type: "internal_error",
1720
+ message: error.message
1721
+ };
1722
+ }
1723
+ return {
1724
+ type: "internal_error",
1725
+ message: "An unexpected error occurred.",
1726
+ details: error
1727
+ };
1728
+ };
1729
+ var getExitCodeForError = (error) => {
1730
+ if (isAppductCliError(error)) {
1731
+ return EXIT_CODE_BY_ERROR_TYPE[error.type];
1732
+ }
1733
+ if (error instanceof DaemonVersionMismatchError) {
1734
+ return EXIT_CODE_BY_ERROR_TYPE.connection_error;
1735
+ }
1736
+ if (error instanceof DaemonRpcError) {
1737
+ const cliErrorClass = rpcErrorClass(error);
1738
+ return EXIT_CODE_BY_ERROR_TYPE[cliErrorClass ?? "internal_error"];
1739
+ }
1740
+ if (isDaemonUnreachableError(error)) {
1741
+ return EXIT_CODE_BY_ERROR_TYPE.connection_error;
1742
+ }
1743
+ return EXIT_CODE_BY_ERROR_TYPE.internal_error;
1744
1744
  };
1745
1745
 
1746
1746
  // src/cli/command-options.ts
1747
- var parseNonNegativeIntegerOption = (value, flagName) => {
1747
+ var parsePositiveIntegerOption = (value, flagName) => {
1748
1748
  if (value === void 0) {
1749
1749
  return void 0;
1750
1750
  }
1751
1751
  const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
1752
- if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < 0) {
1753
- throw usageError(`"${flagName}" must be a non-negative integer.`);
1752
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed <= 0) {
1753
+ throw usageError(`"${flagName}" must be a positive integer.`);
1754
1754
  }
1755
1755
  return parsed;
1756
1756
  };
1757
+ var readTextOption = (argv, value, flagName) => {
1758
+ if (value === void 0) {
1759
+ return void 0;
1760
+ }
1761
+ if (typeof value === "boolean") {
1762
+ throw usageError(`"${flagName}" requires a value.`);
1763
+ }
1764
+ let raw;
1765
+ for (let index = 0; index < argv.length; index += 1) {
1766
+ const token = argv[index];
1767
+ if (token === "--") {
1768
+ break;
1769
+ }
1770
+ if (token === flagName && index + 1 < argv.length) {
1771
+ raw = argv[index + 1];
1772
+ index += 1;
1773
+ } else if (token.startsWith(`${flagName}=`)) {
1774
+ raw = token.slice(flagName.length + 1);
1775
+ }
1776
+ }
1777
+ return raw ?? String(Array.isArray(value) ? value.at(-1) : value);
1778
+ };
1757
1779
  var splitOptionalSelector = (args, commandUsage) => {
1758
1780
  if (args.length > 1) {
1759
1781
  throw usageError(`Usage: ${commandUsage} (too many arguments).`);
@@ -1867,33 +1889,33 @@ var guarded = (context) => {
1867
1889
  };
1868
1890
  };
1869
1891
 
1870
- // src/cli/routes/events.ts
1892
+ // src/cli/routes/events/shared-options.ts
1893
+ var parseEventsFilterFlags = (context) => {
1894
+ const { options } = context;
1895
+ return {
1896
+ name: readTextOption(context.argv, options.name, "--name"),
1897
+ payloadMaxBytes: parsePositiveIntegerOption(options.payloadMaxBytes, "--payload-max-bytes")
1898
+ };
1899
+ };
1900
+
1901
+ // src/cli/routes/events/tail.ts
1871
1902
  var route = async (context) => {
1872
- const { options, stateDir, env } = context;
1873
- const follow = Boolean(options.follow);
1903
+ const { stateDir, env } = context;
1874
1904
  return executeHostedCommand(
1875
1905
  commandName(context),
1876
- // All argument parsing happens inside the handler (not the route body) so
1877
- // `executeHostedCommand`'s own try/catch renders a bad argument as a normal usage_error
1878
- // instead of an uncaught rejection, and before the version check so a typo never waits on
1879
- // the daemon.
1906
+ // Argument parsing runs inside the handler (not the route body) so `executeHostedCommand`'s
1907
+ // own try/catch renders a bad argument as a normal usage_error instead of an uncaught
1908
+ // rejection, and before the version check so a typo never waits on the daemon.
1880
1909
  () => {
1881
- const { selector } = splitOptionalSelector(context.args, "events [selector]");
1882
- const since = parseNonNegativeIntegerOption(options.since, "--since");
1883
- if (since !== void 0 && follow) {
1884
- throw usageError('"--since" is a one-shot pull and cannot be combined with "--follow".');
1885
- }
1910
+ const { selector } = splitOptionalSelector(context.args, "events tail [selector]");
1911
+ const { name, payloadMaxBytes } = parseEventsFilterFlags(context);
1886
1912
  return guarded(context)(
1887
1913
  () => handleEventsCommand(
1888
- { selector, since },
1914
+ { selector, name, payloadMaxBytes },
1889
1915
  {
1890
1916
  stateDir,
1891
1917
  onEvent: (event) => {
1892
1918
  env.stdout.write(`${renderEventLine(event, env.flags)}
1893
- `);
1894
- },
1895
- onCursor: (cursor) => {
1896
- env.stdout.write(`${renderEventsCursorLine(cursor, env.flags)}
1897
1919
  `);
1898
1920
  }
1899
1921
  }
@@ -841,10 +841,10 @@ Run \`appduct init --scheme <scheme>\` with the scheme your app registers for de
841
841
  nextSteps: [
842
842
  'Add `import "@appduct/react-native/auto";` to your app entry (index.js / App.tsx) \u2014 it is what starts the in-app agent endpoint.',
843
843
  `Add the Appduct MCP server entry to your agent's MCP config. "--scheme ${scheme}" keeps that entry self-contained; ${SCHEME_ENV_VAR} and this ${PROJECT_CONFIG_RELATIVE_PATH} work too.`,
844
- "With the app running, pair a device: `appduct link --open ios-sim` (or `--open android`).",
844
+ "With the app running, pair a device: `appduct sessions link --open ios-sim` (or `--open android`).",
845
845
  ...origin === void 0 ? [] : [`Scheme "${scheme}" was read from ${origin}.`],
846
846
  ...iosAppId === void 0 && androidAppId === void 0 ? [
847
- "Delivering to a physical iPhone (`--open ios-device`) or an Android device/emulator (`--open android`) needs the installed app's id: run `appduct init --ios-app-id <id> --android-app-id <id>`, or pass --app-id on `appduct link`."
847
+ "Delivering to a physical iPhone (`--open ios-device`) or an Android device/emulator (`--open android`) needs the installed app's id: run `appduct init --ios-app-id <id> --android-app-id <id>`, or pass --app-id on `appduct sessions link`."
848
848
  ] : [],
849
849
  `This file is safe to commit \u2014 it holds only "scheme" and "appId". Do not point --state-dir at this directory: the state dir holds the daemon's private key and audit log, which must never be committed.`
850
850
  ]
@@ -978,9 +978,6 @@ var renderLsData = (colors, data, now) => {
978
978
  ...rows.map((row) => ` ${row.map((cell, index) => cell.padEnd(widths[index])).join(" ")}`)
979
979
  ];
980
980
  };
981
- var isToolsListing = (data) => {
982
- return typeof data === "object" && data !== null && Array.isArray(data.tools);
983
- };
984
981
  var isToolGroupsListing = (data) => {
985
982
  return typeof data === "object" && data !== null && Array.isArray(data.groups) && !Array.isArray(data.tools);
986
983
  };
@@ -998,7 +995,7 @@ var renderEmptyToolsLine = (data) => {
998
995
  }
999
996
  if (data.group !== void 0) {
1000
997
  const match = data.filter === void 0 ? "" : ` match ${JSON.stringify(data.filter)}`;
1001
- return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools --groups\` to see the session's groups.`;
998
+ return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools ls --groups\` to see the session's groups.`;
1002
999
  }
1003
1000
  return data.filter === void 0 ? " No tools registered." : ` No tools match ${JSON.stringify(data.filter)}.`;
1004
1001
  };
@@ -1080,7 +1077,7 @@ var renderToolSummaryTable = (colors, data) => {
1080
1077
  ...grouped ? renderGroupedToolLines(colors, data.tools) : data.tools.flatMap((tool) => renderToolSummaryLines(tool, " ")),
1081
1078
  ...renderTruncationLine(data),
1082
1079
  "",
1083
- "Run `appduct tools <name>` for a tool's full schema."
1080
+ "Run `appduct tools describe <name>` for a tool's full schema."
1084
1081
  ];
1085
1082
  };
1086
1083
  var renderToolDetail = (colors, tool, flags) => {
@@ -1132,16 +1129,13 @@ var renderToolGroups = (colors, data) => {
1132
1129
  colors.green("Groups"),
1133
1130
  ...data.groups.map((entry) => ` ${label(entry).padEnd(width)} ${String(entry.total).padStart(countWidth)}`),
1134
1131
  "",
1135
- `${data.total} tool${data.total === 1 ? "" : "s"} in total. ` + (hasAnyGroup(data.groups) ? "Run `appduct tools --group <name>` to list one group's tools." : "No tool declares a group.")
1132
+ `${data.total} tool${data.total === 1 ? "" : "s"} in total. ` + (hasAnyGroup(data.groups) ? "Run `appduct tools ls --group <name>` to list one group's tools." : "No tool declares a group.")
1136
1133
  ];
1137
1134
  };
1138
- var renderToolsData = (colors, data, flags, full) => {
1135
+ var renderToolsListData = (colors, data, flags, full) => {
1139
1136
  if (isToolGroupsListing(data)) {
1140
1137
  return renderToolGroups(colors, data);
1141
1138
  }
1142
- if (!isToolsListing(data)) {
1143
- return renderToolDetail(colors, data, flags);
1144
- }
1145
1139
  return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
1146
1140
  };
1147
1141
  var renderInvokeData = (colors, data, flags) => {
@@ -1327,15 +1321,17 @@ var renderSuccessData = (colors, command, data, options) => {
1327
1321
  return renderInitData(colors, data, flags);
1328
1322
  case "keygen":
1329
1323
  return renderKeygenData(colors, data, flags);
1330
- case "link":
1324
+ case "sessions link":
1331
1325
  return renderLinkData(colors, data, flags, options.qr);
1332
- case "ls":
1326
+ case "sessions ls":
1333
1327
  return renderLsData(colors, data, options.now ?? /* @__PURE__ */ new Date());
1334
- case "tools":
1335
- return renderToolsData(colors, data, flags, options.full);
1336
- case "invoke":
1328
+ case "tools ls":
1329
+ return renderToolsListData(colors, data, flags, options.full);
1330
+ case "tools describe":
1331
+ return renderToolDetail(colors, data, flags);
1332
+ case "tools call":
1337
1333
  return renderInvokeData(colors, data, flags);
1338
- case "revoke":
1334
+ case "sessions revoke":
1339
1335
  return renderRevokeData(colors, data);
1340
1336
  case "daemon run":
1341
1337
  return renderDaemonRunData(colors, data, flags);
@@ -9,6 +9,7 @@ var getStateDirPaths = (stateDir) => {
9
9
  socketPath: join(stateDir, "daemon.sock"),
10
10
  pidFilePath: join(stateDir, "daemon.pid"),
11
11
  logFilePath: join(stateDir, "daemon.log"),
12
+ eventsLogPath: join(stateDir, "events.log"),
12
13
  keyPath: join(stateDir, "key.pem"),
13
14
  configPath: join(stateDir, "config.json"),
14
15
  auditDir: join(stateDir, "audit"),
@@ -426,9 +427,6 @@ var renderLsData = (colors, data, now) => {
426
427
  ...rows.map((row) => ` ${row.map((cell, index) => cell.padEnd(widths[index])).join(" ")}`)
427
428
  ];
428
429
  };
429
- var isToolsListing = (data) => {
430
- return typeof data === "object" && data !== null && Array.isArray(data.tools);
431
- };
432
430
  var isToolGroupsListing = (data) => {
433
431
  return typeof data === "object" && data !== null && Array.isArray(data.groups) && !Array.isArray(data.tools);
434
432
  };
@@ -446,7 +444,7 @@ var renderEmptyToolsLine = (data) => {
446
444
  }
447
445
  if (data.group !== void 0) {
448
446
  const match = data.filter === void 0 ? "" : ` match ${JSON.stringify(data.filter)}`;
449
- return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools --groups\` to see the session's groups.`;
447
+ return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools ls --groups\` to see the session's groups.`;
450
448
  }
451
449
  return data.filter === void 0 ? " No tools registered." : ` No tools match ${JSON.stringify(data.filter)}.`;
452
450
  };
@@ -528,7 +526,7 @@ var renderToolSummaryTable = (colors, data) => {
528
526
  ...grouped ? renderGroupedToolLines(colors, data.tools) : data.tools.flatMap((tool) => renderToolSummaryLines(tool, " ")),
529
527
  ...renderTruncationLine(data),
530
528
  "",
531
- "Run `appduct tools <name>` for a tool's full schema."
529
+ "Run `appduct tools describe <name>` for a tool's full schema."
532
530
  ];
533
531
  };
534
532
  var renderToolDetail = (colors, tool, flags) => {
@@ -580,16 +578,13 @@ var renderToolGroups = (colors, data) => {
580
578
  colors.green("Groups"),
581
579
  ...data.groups.map((entry) => ` ${label(entry).padEnd(width)} ${String(entry.total).padStart(countWidth)}`),
582
580
  "",
583
- `${data.total} tool${data.total === 1 ? "" : "s"} in total. ` + (hasAnyGroup(data.groups) ? "Run `appduct tools --group <name>` to list one group's tools." : "No tool declares a group.")
581
+ `${data.total} tool${data.total === 1 ? "" : "s"} in total. ` + (hasAnyGroup(data.groups) ? "Run `appduct tools ls --group <name>` to list one group's tools." : "No tool declares a group.")
584
582
  ];
585
583
  };
586
- var renderToolsData = (colors, data, flags, full) => {
584
+ var renderToolsListData = (colors, data, flags, full) => {
587
585
  if (isToolGroupsListing(data)) {
588
586
  return renderToolGroups(colors, data);
589
587
  }
590
- if (!isToolsListing(data)) {
591
- return renderToolDetail(colors, data, flags);
592
- }
593
588
  return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
594
589
  };
595
590
  var renderInvokeData = (colors, data, flags) => {
@@ -775,15 +770,17 @@ var renderSuccessData = (colors, command, data, options) => {
775
770
  return renderInitData(colors, data, flags);
776
771
  case "keygen":
777
772
  return renderKeygenData(colors, data, flags);
778
- case "link":
773
+ case "sessions link":
779
774
  return renderLinkData(colors, data, flags, options.qr);
780
- case "ls":
775
+ case "sessions ls":
781
776
  return renderLsData(colors, data, options.now ?? /* @__PURE__ */ new Date());
782
- case "tools":
783
- return renderToolsData(colors, data, flags, options.full);
784
- case "invoke":
777
+ case "tools ls":
778
+ return renderToolsListData(colors, data, flags, options.full);
779
+ case "tools describe":
780
+ return renderToolDetail(colors, data, flags);
781
+ case "tools call":
785
782
  return renderInvokeData(colors, data, flags);
786
- case "revoke":
783
+ case "sessions revoke":
787
784
  return renderRevokeData(colors, data);
788
785
  case "daemon run":
789
786
  return renderDaemonRunData(colors, data, flags);