appduct 0.12.0 → 0.14.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 (75) hide show
  1. package/README.md +43 -37
  2. package/dist/bin.js +102 -37
  3. package/dist/cli/command-options.d.ts +7 -17
  4. package/dist/cli/result-types.d.ts +19 -9
  5. package/dist/cli/routes/daemon/index.js +70 -17
  6. package/dist/cli/routes/daemon/run.js +288 -50
  7. package/dist/cli/routes/daemon/start.js +70 -17
  8. package/dist/cli/routes/daemon/status.js +70 -17
  9. package/dist/cli/routes/daemon/stop.js +70 -17
  10. package/dist/cli/routes/doctor.js +70 -17
  11. package/dist/cli/routes/events/index.d.ts +6 -0
  12. package/dist/cli/routes/events/index.js +900 -0
  13. package/dist/cli/routes/events/ls.d.ts +5 -0
  14. package/dist/cli/routes/events/ls.js +1798 -0
  15. package/dist/cli/routes/events/shared-options.d.ts +12 -0
  16. package/dist/cli/routes/events/shared-options.js +102 -0
  17. package/dist/cli/routes/events/since.d.ts +6 -0
  18. package/dist/cli/routes/events/since.js +2034 -0
  19. package/dist/cli/routes/events/tail.d.ts +6 -0
  20. package/dist/cli/routes/{events.js → events/tail.js} +235 -163
  21. package/dist/cli/routes/init.js +72 -19
  22. package/dist/cli/routes/keygen.js +70 -17
  23. package/dist/cli/routes/mcp.js +289 -166
  24. package/dist/cli/routes/sessions/index.d.ts +7 -0
  25. package/dist/cli/routes/sessions/index.js +900 -0
  26. package/dist/cli/routes/sessions/link.d.ts +4 -0
  27. package/dist/cli/routes/{link.js → sessions/link.js} +71 -18
  28. package/dist/cli/routes/sessions/ls.d.ts +4 -0
  29. package/dist/cli/routes/{ls.js → sessions/ls.js} +71 -18
  30. package/dist/cli/routes/sessions/revoke.d.ts +4 -0
  31. package/dist/cli/routes/{revoke.js → sessions/revoke.js} +72 -19
  32. package/dist/cli/routes/tools/call.d.ts +4 -0
  33. package/dist/cli/routes/{invoke.js → tools/call.js} +73 -20
  34. package/dist/cli/routes/tools/describe.d.ts +6 -0
  35. package/dist/cli/routes/tools/describe.js +1894 -0
  36. package/dist/cli/routes/tools/index.d.ts +6 -0
  37. package/dist/cli/routes/tools/index.js +900 -0
  38. package/dist/cli/routes/tools/ls.d.ts +4 -0
  39. package/dist/cli/routes/{tools.js → tools/ls.js} +98 -74
  40. package/dist/cli/routes/tools/shared-options.d.ts +16 -0
  41. package/dist/cli/routes/tools/shared-options.js +122 -0
  42. package/dist/client/app-client.d.ts +96 -37
  43. package/dist/client/index.d.ts +3 -2
  44. package/dist/client/index.js +178 -138
  45. package/dist/commands/events-ls.d.ts +21 -0
  46. package/dist/commands/events.d.ts +31 -14
  47. package/dist/commands/invoke.d.ts +1 -1
  48. package/dist/commands/link.d.ts +1 -1
  49. package/dist/commands/ls.d.ts +1 -1
  50. package/dist/commands/revoke.d.ts +1 -1
  51. package/dist/commands/tools.d.ts +12 -13
  52. package/dist/daemon/calls.d.ts +3 -1
  53. package/dist/daemon/config.d.ts +1 -1
  54. package/dist/daemon/event-bus.d.ts +42 -0
  55. package/dist/daemon/registry.d.ts +20 -12
  56. package/dist/daemon/rpc-errors.d.ts +4 -1
  57. package/dist/daemon/sessions.d.ts +4 -2
  58. package/dist/errors.d.ts +1 -1
  59. package/dist/events/index.d.ts +60 -0
  60. package/dist/index.js +114 -40
  61. package/dist/link.d.ts +1 -1
  62. package/dist/mcp/app-tools.d.ts +22 -2
  63. package/dist/mcp/connect-tool.d.ts +1 -1
  64. package/dist/mcp/events-tool.d.ts +67 -27
  65. package/dist/native-scheme.d.ts +1 -1
  66. package/dist/output.d.ts +29 -6
  67. package/dist/rpc/client.d.ts +1 -1
  68. package/dist/scheme.d.ts +4 -4
  69. package/package.json +2 -2
  70. package/dist/cli/routes/events.d.ts +0 -3
  71. package/dist/cli/routes/invoke.d.ts +0 -3
  72. package/dist/cli/routes/link.d.ts +0 -3
  73. package/dist/cli/routes/ls.d.ts +0 -3
  74. package/dist/cli/routes/revoke.d.ts +0 -3
  75. package/dist/cli/routes/tools.d.ts +0 -3
@@ -1049,6 +1049,7 @@ var formatJson = (value, flags) => {
1049
1049
  import pc from "picocolors";
1050
1050
  import {
1051
1051
  formatAgentWebSocketUrl,
1052
+ renderEventSignature,
1052
1053
  renderToolSignature,
1053
1054
  summarizeToolDescription
1054
1055
  } from "@appduct/shared";
@@ -1144,6 +1145,13 @@ var formatDevice = (device) => {
1144
1145
  const label = parts.length > 0 ? parts.join(" ") : "unknown device";
1145
1146
  return device.os ? `${label} (${device.os})` : label;
1146
1147
  };
1148
+ var SUSPEND_REASON_LABELS = {
1149
+ app_backgrounded: "app in background",
1150
+ connection_lost: "connection lost"
1151
+ };
1152
+ var formatSessionState = (session) => {
1153
+ return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
1154
+ };
1147
1155
  var renderLsData = (colors, data, now) => {
1148
1156
  if (data.length === 0) {
1149
1157
  return [colors.dim("Sessions"), " No Appduct sessions are registered."];
@@ -1152,7 +1160,7 @@ var renderLsData = (colors, data, now) => {
1152
1160
  const headers = ["Alias", "State", "Device", "Tools", "Age"];
1153
1161
  const rows = data.map((session) => [
1154
1162
  session.alias,
1155
- session.state,
1163
+ formatSessionState(session),
1156
1164
  formatDevice(session.device),
1157
1165
  String(session.toolCount),
1158
1166
  formatAge(session.claimedAt ?? session.createdAt, nowIso)
@@ -1166,9 +1174,6 @@ var renderLsData = (colors, data, now) => {
1166
1174
  ...rows.map((row) => ` ${row.map((cell, index) => cell.padEnd(widths[index])).join(" ")}`)
1167
1175
  ];
1168
1176
  };
1169
- var isToolsListing = (data) => {
1170
- return typeof data === "object" && data !== null && Array.isArray(data.tools);
1171
- };
1172
1177
  var isToolGroupsListing = (data) => {
1173
1178
  return typeof data === "object" && data !== null && Array.isArray(data.groups) && !Array.isArray(data.tools);
1174
1179
  };
@@ -1186,7 +1191,7 @@ var renderEmptyToolsLine = (data) => {
1186
1191
  }
1187
1192
  if (data.group !== void 0) {
1188
1193
  const match = data.filter === void 0 ? "" : ` match ${JSON.stringify(data.filter)}`;
1189
- return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools --groups\` to see the session's groups.`;
1194
+ return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools ls --groups\` to see the session's groups.`;
1190
1195
  }
1191
1196
  return data.filter === void 0 ? " No tools registered." : ` No tools match ${JSON.stringify(data.filter)}.`;
1192
1197
  };
@@ -1268,7 +1273,7 @@ var renderToolSummaryTable = (colors, data) => {
1268
1273
  ...grouped ? renderGroupedToolLines(colors, data.tools) : data.tools.flatMap((tool) => renderToolSummaryLines(tool, " ")),
1269
1274
  ...renderTruncationLine(data),
1270
1275
  "",
1271
- "Run `appduct tools <name>` for a tool's full schema."
1276
+ "Run `appduct tools describe <name>` for a tool's full schema."
1272
1277
  ];
1273
1278
  };
1274
1279
  var renderToolDetail = (colors, tool, flags) => {
@@ -1320,18 +1325,62 @@ var renderToolGroups = (colors, data) => {
1320
1325
  colors.green("Groups"),
1321
1326
  ...data.groups.map((entry) => ` ${label(entry).padEnd(width)} ${String(entry.total).padStart(countWidth)}`),
1322
1327
  "",
1323
- `${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.")
1328
+ `${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.")
1324
1329
  ];
1325
1330
  };
1326
- var renderToolsData = (colors, data, flags, full) => {
1331
+ var renderToolsListData = (colors, data, flags, full) => {
1327
1332
  if (isToolGroupsListing(data)) {
1328
1333
  return renderToolGroups(colors, data);
1329
1334
  }
1330
- if (!isToolsListing(data)) {
1331
- return renderToolDetail(colors, data, flags);
1332
- }
1333
1335
  return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
1334
1336
  };
1337
+ var renderEventsTruncationLine = (data) => {
1338
+ const offset = data.offset ?? 0;
1339
+ if (data.events.length >= data.total) {
1340
+ return [];
1341
+ }
1342
+ return [
1343
+ "",
1344
+ `Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
1345
+ ];
1346
+ };
1347
+ var renderEventsListData = (colors, data, flags) => {
1348
+ const [only] = data.events;
1349
+ if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
1350
+ return renderFields(
1351
+ colors.green(`Event: ${only.name}`),
1352
+ [
1353
+ ["Signature", renderEventSignature(only)],
1354
+ ["Description", only.description],
1355
+ ["Payload schema", only.payload_schema]
1356
+ ],
1357
+ flags
1358
+ );
1359
+ }
1360
+ if (data.events.length === 0 && data.total > 0) {
1361
+ return [
1362
+ colors.green("Events"),
1363
+ ` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
1364
+ ];
1365
+ }
1366
+ if (data.events.length === 0) {
1367
+ return [
1368
+ colors.green("Events"),
1369
+ data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
1370
+ ];
1371
+ }
1372
+ const lines = data.events.flatMap((event) => [
1373
+ ` ${renderEventSignature(event)}`,
1374
+ ` ${summarizeToolDescription(event.description)}`
1375
+ ]);
1376
+ return [
1377
+ colors.green("Events"),
1378
+ ...lines,
1379
+ ...renderEventsTruncationLine(data),
1380
+ "",
1381
+ "Run `appduct events ls --name <name>` for an event's full payload schema."
1382
+ ];
1383
+ };
1335
1384
  var renderInvokeData = (colors, data, flags) => {
1336
1385
  return [colors.green("Result"), formatScalar(data, flags)];
1337
1386
  };
@@ -1515,15 +1564,19 @@ var renderSuccessData = (colors, command, data, options) => {
1515
1564
  return renderInitData(colors, data, flags);
1516
1565
  case "keygen":
1517
1566
  return renderKeygenData(colors, data, flags);
1518
- case "link":
1567
+ case "sessions link":
1519
1568
  return renderLinkData(colors, data, flags, options.qr);
1520
- case "ls":
1569
+ case "sessions ls":
1521
1570
  return renderLsData(colors, data, options.now ?? /* @__PURE__ */ new Date());
1522
- case "tools":
1523
- return renderToolsData(colors, data, flags, options.full);
1524
- case "invoke":
1571
+ case "tools ls":
1572
+ return renderToolsListData(colors, data, flags, options.full);
1573
+ case "tools describe":
1574
+ return renderToolDetail(colors, data, flags);
1575
+ case "events ls":
1576
+ return renderEventsListData(colors, data, flags);
1577
+ case "tools call":
1525
1578
  return renderInvokeData(colors, data, flags);
1526
- case "revoke":
1579
+ case "sessions revoke":
1527
1580
  return renderRevokeData(colors, data);
1528
1581
  case "daemon run":
1529
1582
  return renderDaemonRunData(colors, data, flags);
@@ -1078,6 +1078,7 @@ var formatJson = (value, flags) => {
1078
1078
  import pc from "picocolors";
1079
1079
  import {
1080
1080
  formatAgentWebSocketUrl,
1081
+ renderEventSignature,
1081
1082
  renderToolSignature,
1082
1083
  summarizeToolDescription
1083
1084
  } from "@appduct/shared";
@@ -1173,6 +1174,13 @@ var formatDevice = (device) => {
1173
1174
  const label = parts.length > 0 ? parts.join(" ") : "unknown device";
1174
1175
  return device.os ? `${label} (${device.os})` : label;
1175
1176
  };
1177
+ var SUSPEND_REASON_LABELS = {
1178
+ app_backgrounded: "app in background",
1179
+ connection_lost: "connection lost"
1180
+ };
1181
+ var formatSessionState = (session) => {
1182
+ return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
1183
+ };
1176
1184
  var renderLsData = (colors, data, now) => {
1177
1185
  if (data.length === 0) {
1178
1186
  return [colors.dim("Sessions"), " No Appduct sessions are registered."];
@@ -1181,7 +1189,7 @@ var renderLsData = (colors, data, now) => {
1181
1189
  const headers = ["Alias", "State", "Device", "Tools", "Age"];
1182
1190
  const rows = data.map((session) => [
1183
1191
  session.alias,
1184
- session.state,
1192
+ formatSessionState(session),
1185
1193
  formatDevice(session.device),
1186
1194
  String(session.toolCount),
1187
1195
  formatAge(session.claimedAt ?? session.createdAt, nowIso)
@@ -1195,9 +1203,6 @@ var renderLsData = (colors, data, now) => {
1195
1203
  ...rows.map((row) => ` ${row.map((cell, index) => cell.padEnd(widths[index])).join(" ")}`)
1196
1204
  ];
1197
1205
  };
1198
- var isToolsListing = (data) => {
1199
- return typeof data === "object" && data !== null && Array.isArray(data.tools);
1200
- };
1201
1206
  var isToolGroupsListing = (data) => {
1202
1207
  return typeof data === "object" && data !== null && Array.isArray(data.groups) && !Array.isArray(data.tools);
1203
1208
  };
@@ -1215,7 +1220,7 @@ var renderEmptyToolsLine = (data) => {
1215
1220
  }
1216
1221
  if (data.group !== void 0) {
1217
1222
  const match = data.filter === void 0 ? "" : ` match ${JSON.stringify(data.filter)}`;
1218
- return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools --groups\` to see the session's groups.`;
1223
+ return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools ls --groups\` to see the session's groups.`;
1219
1224
  }
1220
1225
  return data.filter === void 0 ? " No tools registered." : ` No tools match ${JSON.stringify(data.filter)}.`;
1221
1226
  };
@@ -1297,7 +1302,7 @@ var renderToolSummaryTable = (colors, data) => {
1297
1302
  ...grouped ? renderGroupedToolLines(colors, data.tools) : data.tools.flatMap((tool) => renderToolSummaryLines(tool, " ")),
1298
1303
  ...renderTruncationLine(data),
1299
1304
  "",
1300
- "Run `appduct tools <name>` for a tool's full schema."
1305
+ "Run `appduct tools describe <name>` for a tool's full schema."
1301
1306
  ];
1302
1307
  };
1303
1308
  var renderToolDetail = (colors, tool, flags) => {
@@ -1349,18 +1354,62 @@ var renderToolGroups = (colors, data) => {
1349
1354
  colors.green("Groups"),
1350
1355
  ...data.groups.map((entry) => ` ${label(entry).padEnd(width)} ${String(entry.total).padStart(countWidth)}`),
1351
1356
  "",
1352
- `${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.")
1357
+ `${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.")
1353
1358
  ];
1354
1359
  };
1355
- var renderToolsData = (colors, data, flags, full) => {
1360
+ var renderToolsListData = (colors, data, flags, full) => {
1356
1361
  if (isToolGroupsListing(data)) {
1357
1362
  return renderToolGroups(colors, data);
1358
1363
  }
1359
- if (!isToolsListing(data)) {
1360
- return renderToolDetail(colors, data, flags);
1361
- }
1362
1364
  return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
1363
1365
  };
1366
+ var renderEventsTruncationLine = (data) => {
1367
+ const offset = data.offset ?? 0;
1368
+ if (data.events.length >= data.total) {
1369
+ return [];
1370
+ }
1371
+ return [
1372
+ "",
1373
+ `Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
1374
+ ];
1375
+ };
1376
+ var renderEventsListData = (colors, data, flags) => {
1377
+ const [only] = data.events;
1378
+ if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
1379
+ return renderFields(
1380
+ colors.green(`Event: ${only.name}`),
1381
+ [
1382
+ ["Signature", renderEventSignature(only)],
1383
+ ["Description", only.description],
1384
+ ["Payload schema", only.payload_schema]
1385
+ ],
1386
+ flags
1387
+ );
1388
+ }
1389
+ if (data.events.length === 0 && data.total > 0) {
1390
+ return [
1391
+ colors.green("Events"),
1392
+ ` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
1393
+ ];
1394
+ }
1395
+ if (data.events.length === 0) {
1396
+ return [
1397
+ colors.green("Events"),
1398
+ data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
1399
+ ];
1400
+ }
1401
+ const lines = data.events.flatMap((event) => [
1402
+ ` ${renderEventSignature(event)}`,
1403
+ ` ${summarizeToolDescription(event.description)}`
1404
+ ]);
1405
+ return [
1406
+ colors.green("Events"),
1407
+ ...lines,
1408
+ ...renderEventsTruncationLine(data),
1409
+ "",
1410
+ "Run `appduct events ls --name <name>` for an event's full payload schema."
1411
+ ];
1412
+ };
1364
1413
  var renderInvokeData = (colors, data, flags) => {
1365
1414
  return [colors.green("Result"), formatScalar(data, flags)];
1366
1415
  };
@@ -1544,15 +1593,19 @@ var renderSuccessData = (colors, command, data, options) => {
1544
1593
  return renderInitData(colors, data, flags);
1545
1594
  case "keygen":
1546
1595
  return renderKeygenData(colors, data, flags);
1547
- case "link":
1596
+ case "sessions link":
1548
1597
  return renderLinkData(colors, data, flags, options.qr);
1549
- case "ls":
1598
+ case "sessions ls":
1550
1599
  return renderLsData(colors, data, options.now ?? /* @__PURE__ */ new Date());
1551
- case "tools":
1552
- return renderToolsData(colors, data, flags, options.full);
1553
- case "invoke":
1600
+ case "tools ls":
1601
+ return renderToolsListData(colors, data, flags, options.full);
1602
+ case "tools describe":
1603
+ return renderToolDetail(colors, data, flags);
1604
+ case "events ls":
1605
+ return renderEventsListData(colors, data, flags);
1606
+ case "tools call":
1554
1607
  return renderInvokeData(colors, data, flags);
1555
- case "revoke":
1608
+ case "sessions revoke":
1556
1609
  return renderRevokeData(colors, data);
1557
1610
  case "daemon run":
1558
1611
  return renderDaemonRunData(colors, data, flags);
@@ -1048,6 +1048,7 @@ var formatJson = (value, flags) => {
1048
1048
  import pc from "picocolors";
1049
1049
  import {
1050
1050
  formatAgentWebSocketUrl,
1051
+ renderEventSignature,
1051
1052
  renderToolSignature,
1052
1053
  summarizeToolDescription
1053
1054
  } from "@appduct/shared";
@@ -1143,6 +1144,13 @@ var formatDevice = (device) => {
1143
1144
  const label = parts.length > 0 ? parts.join(" ") : "unknown device";
1144
1145
  return device.os ? `${label} (${device.os})` : label;
1145
1146
  };
1147
+ var SUSPEND_REASON_LABELS = {
1148
+ app_backgrounded: "app in background",
1149
+ connection_lost: "connection lost"
1150
+ };
1151
+ var formatSessionState = (session) => {
1152
+ return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
1153
+ };
1146
1154
  var renderLsData = (colors, data, now) => {
1147
1155
  if (data.length === 0) {
1148
1156
  return [colors.dim("Sessions"), " No Appduct sessions are registered."];
@@ -1151,7 +1159,7 @@ var renderLsData = (colors, data, now) => {
1151
1159
  const headers = ["Alias", "State", "Device", "Tools", "Age"];
1152
1160
  const rows = data.map((session) => [
1153
1161
  session.alias,
1154
- session.state,
1162
+ formatSessionState(session),
1155
1163
  formatDevice(session.device),
1156
1164
  String(session.toolCount),
1157
1165
  formatAge(session.claimedAt ?? session.createdAt, nowIso)
@@ -1165,9 +1173,6 @@ var renderLsData = (colors, data, now) => {
1165
1173
  ...rows.map((row) => ` ${row.map((cell, index) => cell.padEnd(widths[index])).join(" ")}`)
1166
1174
  ];
1167
1175
  };
1168
- var isToolsListing = (data) => {
1169
- return typeof data === "object" && data !== null && Array.isArray(data.tools);
1170
- };
1171
1176
  var isToolGroupsListing = (data) => {
1172
1177
  return typeof data === "object" && data !== null && Array.isArray(data.groups) && !Array.isArray(data.tools);
1173
1178
  };
@@ -1185,7 +1190,7 @@ var renderEmptyToolsLine = (data) => {
1185
1190
  }
1186
1191
  if (data.group !== void 0) {
1187
1192
  const match = data.filter === void 0 ? "" : ` match ${JSON.stringify(data.filter)}`;
1188
- return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools --groups\` to see the session's groups.`;
1193
+ return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools ls --groups\` to see the session's groups.`;
1189
1194
  }
1190
1195
  return data.filter === void 0 ? " No tools registered." : ` No tools match ${JSON.stringify(data.filter)}.`;
1191
1196
  };
@@ -1267,7 +1272,7 @@ var renderToolSummaryTable = (colors, data) => {
1267
1272
  ...grouped ? renderGroupedToolLines(colors, data.tools) : data.tools.flatMap((tool) => renderToolSummaryLines(tool, " ")),
1268
1273
  ...renderTruncationLine(data),
1269
1274
  "",
1270
- "Run `appduct tools <name>` for a tool's full schema."
1275
+ "Run `appduct tools describe <name>` for a tool's full schema."
1271
1276
  ];
1272
1277
  };
1273
1278
  var renderToolDetail = (colors, tool, flags) => {
@@ -1319,18 +1324,62 @@ var renderToolGroups = (colors, data) => {
1319
1324
  colors.green("Groups"),
1320
1325
  ...data.groups.map((entry) => ` ${label(entry).padEnd(width)} ${String(entry.total).padStart(countWidth)}`),
1321
1326
  "",
1322
- `${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.")
1327
+ `${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.")
1323
1328
  ];
1324
1329
  };
1325
- var renderToolsData = (colors, data, flags, full) => {
1330
+ var renderToolsListData = (colors, data, flags, full) => {
1326
1331
  if (isToolGroupsListing(data)) {
1327
1332
  return renderToolGroups(colors, data);
1328
1333
  }
1329
- if (!isToolsListing(data)) {
1330
- return renderToolDetail(colors, data, flags);
1331
- }
1332
1334
  return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
1333
1335
  };
1336
+ var renderEventsTruncationLine = (data) => {
1337
+ const offset = data.offset ?? 0;
1338
+ if (data.events.length >= data.total) {
1339
+ return [];
1340
+ }
1341
+ return [
1342
+ "",
1343
+ `Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
1344
+ ];
1345
+ };
1346
+ var renderEventsListData = (colors, data, flags) => {
1347
+ const [only] = data.events;
1348
+ if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
1349
+ return renderFields(
1350
+ colors.green(`Event: ${only.name}`),
1351
+ [
1352
+ ["Signature", renderEventSignature(only)],
1353
+ ["Description", only.description],
1354
+ ["Payload schema", only.payload_schema]
1355
+ ],
1356
+ flags
1357
+ );
1358
+ }
1359
+ if (data.events.length === 0 && data.total > 0) {
1360
+ return [
1361
+ colors.green("Events"),
1362
+ ` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
1363
+ ];
1364
+ }
1365
+ if (data.events.length === 0) {
1366
+ return [
1367
+ colors.green("Events"),
1368
+ data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
1369
+ ];
1370
+ }
1371
+ const lines = data.events.flatMap((event) => [
1372
+ ` ${renderEventSignature(event)}`,
1373
+ ` ${summarizeToolDescription(event.description)}`
1374
+ ]);
1375
+ return [
1376
+ colors.green("Events"),
1377
+ ...lines,
1378
+ ...renderEventsTruncationLine(data),
1379
+ "",
1380
+ "Run `appduct events ls --name <name>` for an event's full payload schema."
1381
+ ];
1382
+ };
1334
1383
  var renderInvokeData = (colors, data, flags) => {
1335
1384
  return [colors.green("Result"), formatScalar(data, flags)];
1336
1385
  };
@@ -1514,15 +1563,19 @@ var renderSuccessData = (colors, command, data, options) => {
1514
1563
  return renderInitData(colors, data, flags);
1515
1564
  case "keygen":
1516
1565
  return renderKeygenData(colors, data, flags);
1517
- case "link":
1566
+ case "sessions link":
1518
1567
  return renderLinkData(colors, data, flags, options.qr);
1519
- case "ls":
1568
+ case "sessions ls":
1520
1569
  return renderLsData(colors, data, options.now ?? /* @__PURE__ */ new Date());
1521
- case "tools":
1522
- return renderToolsData(colors, data, flags, options.full);
1523
- case "invoke":
1570
+ case "tools ls":
1571
+ return renderToolsListData(colors, data, flags, options.full);
1572
+ case "tools describe":
1573
+ return renderToolDetail(colors, data, flags);
1574
+ case "events ls":
1575
+ return renderEventsListData(colors, data, flags);
1576
+ case "tools call":
1524
1577
  return renderInvokeData(colors, data, flags);
1525
- case "revoke":
1578
+ case "sessions revoke":
1526
1579
  return renderRevokeData(colors, data);
1527
1580
  case "daemon run":
1528
1581
  return renderDaemonRunData(colors, data, flags);
@@ -458,6 +458,7 @@ var formatJson = (value, flags) => {
458
458
  import pc from "picocolors";
459
459
  import {
460
460
  formatAgentWebSocketUrl,
461
+ renderEventSignature,
461
462
  renderToolSignature,
462
463
  summarizeToolDescription
463
464
  } from "@appduct/shared";
@@ -553,6 +554,13 @@ var formatDevice = (device) => {
553
554
  const label = parts.length > 0 ? parts.join(" ") : "unknown device";
554
555
  return device.os ? `${label} (${device.os})` : label;
555
556
  };
557
+ var SUSPEND_REASON_LABELS = {
558
+ app_backgrounded: "app in background",
559
+ connection_lost: "connection lost"
560
+ };
561
+ var formatSessionState = (session) => {
562
+ return session.suspendReason ? `${session.state} (${SUSPEND_REASON_LABELS[session.suspendReason]})` : session.state;
563
+ };
556
564
  var renderLsData = (colors, data, now) => {
557
565
  if (data.length === 0) {
558
566
  return [colors.dim("Sessions"), " No Appduct sessions are registered."];
@@ -561,7 +569,7 @@ var renderLsData = (colors, data, now) => {
561
569
  const headers = ["Alias", "State", "Device", "Tools", "Age"];
562
570
  const rows = data.map((session) => [
563
571
  session.alias,
564
- session.state,
572
+ formatSessionState(session),
565
573
  formatDevice(session.device),
566
574
  String(session.toolCount),
567
575
  formatAge(session.claimedAt ?? session.createdAt, nowIso)
@@ -575,9 +583,6 @@ var renderLsData = (colors, data, now) => {
575
583
  ...rows.map((row) => ` ${row.map((cell, index) => cell.padEnd(widths[index])).join(" ")}`)
576
584
  ];
577
585
  };
578
- var isToolsListing = (data) => {
579
- return typeof data === "object" && data !== null && Array.isArray(data.tools);
580
- };
581
586
  var isToolGroupsListing = (data) => {
582
587
  return typeof data === "object" && data !== null && Array.isArray(data.groups) && !Array.isArray(data.tools);
583
588
  };
@@ -595,7 +600,7 @@ var renderEmptyToolsLine = (data) => {
595
600
  }
596
601
  if (data.group !== void 0) {
597
602
  const match = data.filter === void 0 ? "" : ` match ${JSON.stringify(data.filter)}`;
598
- return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools --groups\` to see the session's groups.`;
603
+ return ` No tools in group ${JSON.stringify(data.group)}${match}. Run \`appduct tools ls --groups\` to see the session's groups.`;
599
604
  }
600
605
  return data.filter === void 0 ? " No tools registered." : ` No tools match ${JSON.stringify(data.filter)}.`;
601
606
  };
@@ -677,7 +682,7 @@ var renderToolSummaryTable = (colors, data) => {
677
682
  ...grouped ? renderGroupedToolLines(colors, data.tools) : data.tools.flatMap((tool) => renderToolSummaryLines(tool, " ")),
678
683
  ...renderTruncationLine(data),
679
684
  "",
680
- "Run `appduct tools <name>` for a tool's full schema."
685
+ "Run `appduct tools describe <name>` for a tool's full schema."
681
686
  ];
682
687
  };
683
688
  var renderToolDetail = (colors, tool, flags) => {
@@ -729,18 +734,62 @@ var renderToolGroups = (colors, data) => {
729
734
  colors.green("Groups"),
730
735
  ...data.groups.map((entry) => ` ${label(entry).padEnd(width)} ${String(entry.total).padStart(countWidth)}`),
731
736
  "",
732
- `${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.")
737
+ `${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.")
733
738
  ];
734
739
  };
735
- var renderToolsData = (colors, data, flags, full) => {
740
+ var renderToolsListData = (colors, data, flags, full) => {
736
741
  if (isToolGroupsListing(data)) {
737
742
  return renderToolGroups(colors, data);
738
743
  }
739
- if (!isToolsListing(data)) {
740
- return renderToolDetail(colors, data, flags);
741
- }
742
744
  return full ? renderToolsFullListing(colors, data, flags) : renderToolSummaryTable(colors, data);
743
745
  };
746
+ var renderEventsTruncationLine = (data) => {
747
+ const offset = data.offset ?? 0;
748
+ if (data.events.length >= data.total) {
749
+ return [];
750
+ }
751
+ return [
752
+ "",
753
+ `Showing ${data.events.length} of ${data.total} events (offset ${offset}). Narrow with --name <glob> or page with --offset <n>.`
754
+ ];
755
+ };
756
+ var renderEventsListData = (colors, data, flags) => {
757
+ const [only] = data.events;
758
+ if (data.name !== void 0 && !data.name.includes("*") && only !== void 0) {
759
+ return renderFields(
760
+ colors.green(`Event: ${only.name}`),
761
+ [
762
+ ["Signature", renderEventSignature(only)],
763
+ ["Description", only.description],
764
+ ["Payload schema", only.payload_schema]
765
+ ],
766
+ flags
767
+ );
768
+ }
769
+ if (data.events.length === 0 && data.total > 0) {
770
+ return [
771
+ colors.green("Events"),
772
+ ` No events at offset ${data.offset ?? 0}; ${data.total} matching event${data.total === 1 ? "" : "s"} in total.`
773
+ ];
774
+ }
775
+ if (data.events.length === 0) {
776
+ return [
777
+ colors.green("Events"),
778
+ data.name === void 0 ? " No events declared." : ` No events match ${JSON.stringify(data.name)}.`
779
+ ];
780
+ }
781
+ const lines = data.events.flatMap((event) => [
782
+ ` ${renderEventSignature(event)}`,
783
+ ` ${summarizeToolDescription(event.description)}`
784
+ ]);
785
+ return [
786
+ colors.green("Events"),
787
+ ...lines,
788
+ ...renderEventsTruncationLine(data),
789
+ "",
790
+ "Run `appduct events ls --name <name>` for an event's full payload schema."
791
+ ];
792
+ };
744
793
  var renderInvokeData = (colors, data, flags) => {
745
794
  return [colors.green("Result"), formatScalar(data, flags)];
746
795
  };
@@ -924,15 +973,19 @@ var renderSuccessData = (colors, command, data, options) => {
924
973
  return renderInitData(colors, data, flags);
925
974
  case "keygen":
926
975
  return renderKeygenData(colors, data, flags);
927
- case "link":
976
+ case "sessions link":
928
977
  return renderLinkData(colors, data, flags, options.qr);
929
- case "ls":
978
+ case "sessions ls":
930
979
  return renderLsData(colors, data, options.now ?? /* @__PURE__ */ new Date());
931
- case "tools":
932
- return renderToolsData(colors, data, flags, options.full);
933
- case "invoke":
980
+ case "tools ls":
981
+ return renderToolsListData(colors, data, flags, options.full);
982
+ case "tools describe":
983
+ return renderToolDetail(colors, data, flags);
984
+ case "events ls":
985
+ return renderEventsListData(colors, data, flags);
986
+ case "tools call":
934
987
  return renderInvokeData(colors, data, flags);
935
- case "revoke":
988
+ case "sessions revoke":
936
989
  return renderRevokeData(colors, data);
937
990
  case "daemon run":
938
991
  return renderDaemonRunData(colors, data, flags);
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Route for `appduct events` — itself a router over the events verbs, so `events tail` loads only
3
+ * `tail.ts` (issue #96). cac matches only the noun (`cli/create-cli.ts`), so the verb arrives here
4
+ * as the first positional rather than as a cac command of its own.
5
+ */
6
+ export declare const route: import("../../router.js").Route;