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.
- package/README.md +43 -38
- package/dist/bin.js +50 -36
- package/dist/cli/command-options.d.ts +7 -17
- package/dist/cli/result-types.d.ts +11 -8
- package/dist/cli/routes/daemon/index.js +12 -16
- package/dist/cli/routes/daemon/run.js +216 -46
- package/dist/cli/routes/daemon/start.js +18 -16
- package/dist/cli/routes/daemon/status.js +18 -16
- package/dist/cli/routes/daemon/stop.js +18 -16
- package/dist/cli/routes/doctor.js +12 -16
- package/dist/cli/routes/events/index.d.ts +9 -0
- package/dist/cli/routes/events/index.js +842 -0
- package/dist/cli/routes/events/shared-options.d.ts +12 -0
- package/dist/cli/routes/events/shared-options.js +102 -0
- package/dist/cli/routes/events/since.d.ts +6 -0
- package/dist/cli/routes/events/since.js +1977 -0
- package/dist/cli/routes/events/tail.d.ts +6 -0
- package/dist/cli/routes/{events.js → events/tail.js} +184 -162
- package/dist/cli/routes/init.js +14 -18
- package/dist/cli/routes/keygen.js +13 -16
- package/dist/cli/routes/mcp.js +193 -175
- package/dist/cli/routes/sessions/index.d.ts +7 -0
- package/dist/cli/routes/sessions/index.js +843 -0
- package/dist/cli/routes/sessions/link.d.ts +4 -0
- package/dist/cli/routes/{link.js → sessions/link.js} +19 -17
- package/dist/cli/routes/sessions/ls.d.ts +4 -0
- package/dist/cli/routes/{ls.js → sessions/ls.js} +19 -17
- package/dist/cli/routes/sessions/revoke.d.ts +4 -0
- package/dist/cli/routes/{revoke.js → sessions/revoke.js} +20 -18
- package/dist/cli/routes/tools/call.d.ts +4 -0
- package/dist/cli/routes/{invoke.js → tools/call.js} +21 -19
- package/dist/cli/routes/tools/describe.d.ts +6 -0
- package/dist/cli/routes/tools/describe.js +1837 -0
- package/dist/cli/routes/tools/index.d.ts +6 -0
- package/dist/cli/routes/tools/index.js +843 -0
- package/dist/cli/routes/tools/ls.d.ts +4 -0
- package/dist/cli/routes/{tools.js → tools/ls.js} +46 -73
- package/dist/cli/routes/tools/shared-options.d.ts +16 -0
- package/dist/cli/routes/tools/shared-options.js +122 -0
- package/dist/client/app-client.d.ts +96 -37
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.js +183 -139
- package/dist/commands/events.d.ts +31 -13
- package/dist/commands/invoke.d.ts +1 -1
- package/dist/commands/link.d.ts +1 -1
- package/dist/commands/ls.d.ts +1 -1
- package/dist/commands/revoke.d.ts +1 -1
- package/dist/commands/tools.d.ts +12 -13
- package/dist/daemon/config.d.ts +5 -2
- package/dist/daemon/daemon.d.ts +3 -0
- package/dist/daemon/event-bus.d.ts +36 -4
- package/dist/daemon/events-log.d.ts +31 -0
- package/dist/daemon/memory-append-only-file.d.ts +17 -0
- package/dist/daemon/node-append-only-file.d.ts +9 -0
- package/dist/daemon/rpc-errors.d.ts +1 -1
- package/dist/daemon/state-dir.d.ts +2 -0
- package/dist/errors.d.ts +1 -1
- package/dist/events/index.d.ts +60 -0
- package/dist/index.js +62 -39
- package/dist/link.d.ts +1 -1
- package/dist/mcp/app-tools.d.ts +3 -2
- package/dist/mcp/connect-tool.d.ts +1 -1
- package/dist/mcp/events-tool.d.ts +28 -34
- package/dist/native-scheme.d.ts +1 -1
- package/dist/output.d.ts +29 -6
- package/dist/rpc/client.d.ts +1 -1
- package/dist/scheme.d.ts +4 -4
- package/package.json +2 -2
- package/dist/cli/routes/events.d.ts +0 -3
- package/dist/cli/routes/invoke.d.ts +0 -3
- package/dist/cli/routes/link.d.ts +0 -3
- package/dist/cli/routes/ls.d.ts +0 -3
- package/dist/cli/routes/revoke.d.ts +0 -3
- 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
|
|
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
|
|
1666
|
-
case "
|
|
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
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
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
|
-
|
|
1743
|
-
|
|
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
|
|
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
|
|
1753
|
-
throw usageError(`"${flagName}" must be a
|
|
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 {
|
|
1873
|
-
const follow = Boolean(options.follow);
|
|
1903
|
+
const { stateDir, env } = context;
|
|
1874
1904
|
return executeHostedCommand(
|
|
1875
1905
|
commandName(context),
|
|
1876
|
-
//
|
|
1877
|
-
//
|
|
1878
|
-
//
|
|
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
|
|
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,
|
|
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
|
}
|
package/dist/cli/routes/init.js
CHANGED
|
@@ -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
|
|
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
|
|
1336
|
-
case "
|
|
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
|
|
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
|
|
784
|
-
case "
|
|
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);
|