appduct 0.12.0 → 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 +41 -36
- package/dist/bin.js +44 -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 +136 -28
- package/dist/cli/routes/daemon/start.js +12 -16
- package/dist/cli/routes/daemon/status.js +12 -16
- package/dist/cli/routes/daemon/stop.js +12 -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} +177 -162
- package/dist/cli/routes/init.js +14 -18
- package/dist/cli/routes/keygen.js +12 -16
- package/dist/cli/routes/mcp.js +181 -165
- 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} +13 -17
- package/dist/cli/routes/sessions/ls.d.ts +4 -0
- package/dist/cli/routes/{ls.js → sessions/ls.js} +13 -17
- package/dist/cli/routes/sessions/revoke.d.ts +4 -0
- package/dist/cli/routes/{revoke.js → sessions/revoke.js} +14 -18
- package/dist/cli/routes/tools/call.d.ts +4 -0
- package/dist/cli/routes/{invoke.js → tools/call.js} +15 -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} +40 -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 +178 -138
- package/dist/commands/events.d.ts +31 -14
- 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 +1 -1
- package/dist/daemon/event-bus.d.ts +32 -0
- package/dist/daemon/rpc-errors.d.ts +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/events/index.d.ts +60 -0
- package/dist/index.js +56 -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 -27
- 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
package/dist/client/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/client/connect.ts
|
|
2
|
-
import { RPC_METHODS as
|
|
2
|
+
import { RPC_METHODS as RPC_METHODS4 } from "@appduct/shared";
|
|
3
3
|
|
|
4
4
|
// src/daemon/state-dir.ts
|
|
5
5
|
import { homedir } from "node:os";
|
|
@@ -1029,9 +1029,109 @@ var openDaemonStream = async (options) => {
|
|
|
1029
1029
|
import {
|
|
1030
1030
|
clampToolTimeoutMs,
|
|
1031
1031
|
MAX_TOOL_TIMEOUT_MS,
|
|
1032
|
-
RPC_METHODS as
|
|
1032
|
+
RPC_METHODS as RPC_METHODS3,
|
|
1033
|
+
toAppEvent as toAppEvent2
|
|
1033
1034
|
} from "@appduct/shared";
|
|
1034
1035
|
|
|
1036
|
+
// src/events/index.ts
|
|
1037
|
+
import {
|
|
1038
|
+
RPC_METHODS as RPC_METHODS2,
|
|
1039
|
+
toAppEvent
|
|
1040
|
+
} from "@appduct/shared";
|
|
1041
|
+
var WaitForAppEventTimeoutError = class extends Error {
|
|
1042
|
+
constructor(eventName, timeoutMs) {
|
|
1043
|
+
super(`Timed out after ${timeoutMs}ms waiting for event "${eventName}".`);
|
|
1044
|
+
this.eventName = eventName;
|
|
1045
|
+
this.timeoutMs = timeoutMs;
|
|
1046
|
+
this.name = "WaitForAppEventTimeoutError";
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
var WaitForAppEventConnectionClosedError = class extends Error {
|
|
1050
|
+
constructor(eventName) {
|
|
1051
|
+
super(`The connection to the Appduct daemon closed while waiting for event "${eventName}".`);
|
|
1052
|
+
this.eventName = eventName;
|
|
1053
|
+
this.name = "WaitForAppEventConnectionClosedError";
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
var waitForAppEvent = async (stream, options) => {
|
|
1057
|
+
const { sessionId, name, since, timeoutMs, payloadMaxBytes } = options;
|
|
1058
|
+
let liveHandler = null;
|
|
1059
|
+
const earlyEvents = [];
|
|
1060
|
+
const unsubscribeNotification = stream.onNotification((payload) => {
|
|
1061
|
+
const event = payload;
|
|
1062
|
+
if (liveHandler) {
|
|
1063
|
+
liveHandler(event);
|
|
1064
|
+
} else {
|
|
1065
|
+
earlyEvents.push(event);
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
let unsubscribeClose = () => {
|
|
1069
|
+
};
|
|
1070
|
+
try {
|
|
1071
|
+
await stream.call(RPC_METHODS2.eventsSubscribe, {
|
|
1072
|
+
sessionSelector: sessionId,
|
|
1073
|
+
kinds: ["app_event"],
|
|
1074
|
+
name,
|
|
1075
|
+
payloadMaxBytes
|
|
1076
|
+
});
|
|
1077
|
+
const sinceResult = await stream.call(RPC_METHODS2.eventsSince, {
|
|
1078
|
+
selector: sessionId,
|
|
1079
|
+
since,
|
|
1080
|
+
name,
|
|
1081
|
+
payloadMaxBytes
|
|
1082
|
+
});
|
|
1083
|
+
const seenSeqs = /* @__PURE__ */ new Set();
|
|
1084
|
+
const backlog = [];
|
|
1085
|
+
for (const event of [...sinceResult.events, ...earlyEvents]) {
|
|
1086
|
+
if (event.sessionId === sessionId && !seenSeqs.has(event.seq)) {
|
|
1087
|
+
seenSeqs.add(event.seq);
|
|
1088
|
+
backlog.push(event);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
backlog.sort((a, b) => a.seq - b.seq);
|
|
1092
|
+
for (const event of backlog) {
|
|
1093
|
+
const appEvent = toAppEvent(event, sessionId);
|
|
1094
|
+
if (appEvent) {
|
|
1095
|
+
return { event: appEvent, dropped: sinceResult.dropped };
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
const highestConsideredSeq = backlog.length > 0 ? backlog[backlog.length - 1].seq : sinceResult.cursor;
|
|
1099
|
+
return await new Promise((resolve2, reject) => {
|
|
1100
|
+
let settled = false;
|
|
1101
|
+
const settle = (fn) => {
|
|
1102
|
+
if (settled) {
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
settled = true;
|
|
1106
|
+
clearTimeout(timer);
|
|
1107
|
+
liveHandler = null;
|
|
1108
|
+
fn();
|
|
1109
|
+
};
|
|
1110
|
+
const timer = setTimeout(() => {
|
|
1111
|
+
settle(() => reject(new WaitForAppEventTimeoutError(name, timeoutMs)));
|
|
1112
|
+
}, timeoutMs);
|
|
1113
|
+
unsubscribeClose = stream.onClose(() => {
|
|
1114
|
+
settle(() => reject(new WaitForAppEventConnectionClosedError(name)));
|
|
1115
|
+
});
|
|
1116
|
+
liveHandler = (event) => {
|
|
1117
|
+
if (settled) {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
if (event.sessionId === sessionId && event.seq <= highestConsideredSeq) {
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
const appEvent = toAppEvent(event, sessionId);
|
|
1124
|
+
if (appEvent) {
|
|
1125
|
+
settle(() => resolve2({ event: appEvent, dropped: sinceResult.dropped }));
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
});
|
|
1129
|
+
} finally {
|
|
1130
|
+
unsubscribeNotification();
|
|
1131
|
+
unsubscribeClose();
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1035
1135
|
// src/errors.ts
|
|
1036
1136
|
var AppductCliError = class extends Error {
|
|
1037
1137
|
type;
|
|
@@ -1103,12 +1203,14 @@ var transportTimeoutForToolCall = (timeoutMs) => {
|
|
|
1103
1203
|
const clamped = timeoutMs === void 0 || !Number.isFinite(timeoutMs) ? MAX_TOOL_TIMEOUT_MS : clampToolTimeoutMs(timeoutMs);
|
|
1104
1204
|
return clamped + CALL_TRANSPORT_TIMEOUT_SLACK_MS;
|
|
1105
1205
|
};
|
|
1106
|
-
var makeAppClient = (stream, sessionId) => {
|
|
1206
|
+
var makeAppClient = (stream, sessionId, openStream) => {
|
|
1207
|
+
let closed = false;
|
|
1208
|
+
const openWaitStreams = /* @__PURE__ */ new Set();
|
|
1107
1209
|
const client = {
|
|
1108
1210
|
sessionId,
|
|
1109
1211
|
tools: async () => {
|
|
1110
1212
|
try {
|
|
1111
|
-
const result = await stream.call(
|
|
1213
|
+
const result = await stream.call(RPC_METHODS3.toolsList, {
|
|
1112
1214
|
selector: sessionId
|
|
1113
1215
|
});
|
|
1114
1216
|
const entries = Array.isArray(result) ? result : result.tools;
|
|
@@ -1120,7 +1222,7 @@ var makeAppClient = (stream, sessionId) => {
|
|
|
1120
1222
|
call: async (name, args, options) => {
|
|
1121
1223
|
try {
|
|
1122
1224
|
const result = await stream.call(
|
|
1123
|
-
|
|
1225
|
+
RPC_METHODS3.toolsCall,
|
|
1124
1226
|
{
|
|
1125
1227
|
selector: sessionId,
|
|
1126
1228
|
name,
|
|
@@ -1135,151 +1237,82 @@ var makeAppClient = (stream, sessionId) => {
|
|
|
1135
1237
|
throw toAppductError(error);
|
|
1136
1238
|
}
|
|
1137
1239
|
},
|
|
1240
|
+
// A single implementation backs both of the public type's overloaded signatures — `makeAppClient`
|
|
1241
|
+
// returns `client as unknown as AppClient<TTools>` below, so nothing here has to structurally
|
|
1242
|
+
// satisfy either signature on its own; the overloads are strictly a compile-time view for callers.
|
|
1138
1243
|
events: async (options = {}) => {
|
|
1139
1244
|
try {
|
|
1140
|
-
const result = await stream.call(
|
|
1245
|
+
const result = await stream.call(RPC_METHODS3.eventsSince, {
|
|
1141
1246
|
selector: sessionId,
|
|
1142
1247
|
since: options.since,
|
|
1143
|
-
limit: options.limit
|
|
1248
|
+
limit: options.limit,
|
|
1249
|
+
payloadMaxBytes: options.payloadMaxBytes
|
|
1144
1250
|
});
|
|
1145
1251
|
return {
|
|
1146
|
-
events: result.events.map((event) =>
|
|
1147
|
-
cursor: result.cursor
|
|
1252
|
+
events: result.events.map((event) => toAppEvent2(event, sessionId)).filter((event) => event !== void 0),
|
|
1253
|
+
cursor: result.cursor,
|
|
1254
|
+
dropped: result.dropped,
|
|
1255
|
+
remaining: result.remaining
|
|
1148
1256
|
};
|
|
1149
1257
|
} catch (error) {
|
|
1150
1258
|
throw toAppductError(error);
|
|
1151
1259
|
}
|
|
1152
1260
|
},
|
|
1153
|
-
waitForEvent: (name, options = {}) => {
|
|
1261
|
+
waitForEvent: async (name, options = {}) => {
|
|
1262
|
+
if (closed) {
|
|
1263
|
+
throw new AppductError(
|
|
1264
|
+
"connection_error",
|
|
1265
|
+
`Cannot wait for event "${name}": this AppClient (session "${sessionId}") is closed.`
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1154
1268
|
const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_FOR_EVENT_TIMEOUT_MS;
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1269
|
+
let waitStream;
|
|
1270
|
+
try {
|
|
1271
|
+
waitStream = await openStream();
|
|
1272
|
+
} catch (error) {
|
|
1273
|
+
throw toAppductError(error);
|
|
1274
|
+
}
|
|
1275
|
+
if (closed) {
|
|
1276
|
+
waitStream.close();
|
|
1277
|
+
throw new AppductError(
|
|
1278
|
+
"connection_error",
|
|
1279
|
+
`Cannot wait for event "${name}": this AppClient (session "${sessionId}") is closed.`
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
openWaitStreams.add(waitStream);
|
|
1283
|
+
try {
|
|
1284
|
+
const { event } = await waitForAppEvent(waitStream, {
|
|
1285
|
+
sessionId,
|
|
1286
|
+
name,
|
|
1287
|
+
since: options.since,
|
|
1288
|
+
timeoutMs,
|
|
1289
|
+
payloadMaxBytes: options.payloadMaxBytes
|
|
1290
|
+
});
|
|
1291
|
+
return event;
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
if (error instanceof WaitForAppEventTimeoutError) {
|
|
1294
|
+
throw new AppductError("timeout", error.message);
|
|
1159
1295
|
}
|
|
1160
|
-
if (
|
|
1161
|
-
|
|
1296
|
+
if (error instanceof WaitForAppEventConnectionClosedError) {
|
|
1297
|
+
throw new AppductError("connection_error", error.message);
|
|
1162
1298
|
}
|
|
1163
|
-
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
if (settled) {
|
|
1169
|
-
return;
|
|
1170
|
-
}
|
|
1171
|
-
settled = true;
|
|
1172
|
-
clearTimeout(timer);
|
|
1173
|
-
unsubscribeClose();
|
|
1174
|
-
liveHandler = null;
|
|
1175
|
-
fn();
|
|
1176
|
-
};
|
|
1177
|
-
const timer = setTimeout(() => {
|
|
1178
|
-
settle(
|
|
1179
|
-
() => reject(new AppductError("timeout", `Timed out after ${timeoutMs}ms waiting for event "${name}".`))
|
|
1180
|
-
);
|
|
1181
|
-
}, timeoutMs);
|
|
1182
|
-
let liveHandler = null;
|
|
1183
|
-
const earlyEvents = [];
|
|
1184
|
-
const unsubscribeNotification = stream.onNotification((payload) => {
|
|
1185
|
-
const event = payload;
|
|
1186
|
-
if (liveHandler) {
|
|
1187
|
-
liveHandler(event);
|
|
1188
|
-
} else {
|
|
1189
|
-
earlyEvents.push(event);
|
|
1190
|
-
}
|
|
1191
|
-
});
|
|
1192
|
-
let unsubscribeClose = () => {
|
|
1193
|
-
};
|
|
1194
|
-
const fail = (error) => {
|
|
1195
|
-
settle(() => reject(toAppductError(error)));
|
|
1196
|
-
unsubscribeNotification();
|
|
1197
|
-
};
|
|
1198
|
-
(async () => {
|
|
1199
|
-
await stream.call(RPC_METHODS2.eventsSubscribe, { sessionSelector: sessionId, kinds: ["app_event"] });
|
|
1200
|
-
const sinceResult = await stream.call(RPC_METHODS2.eventsSince, {
|
|
1201
|
-
selector: sessionId,
|
|
1202
|
-
since: options.since
|
|
1203
|
-
});
|
|
1204
|
-
const seenSeqs = /* @__PURE__ */ new Set();
|
|
1205
|
-
const backlog = [];
|
|
1206
|
-
for (const event of [...sinceResult.events, ...earlyEvents]) {
|
|
1207
|
-
if (event.sessionId === sessionId && !seenSeqs.has(event.seq)) {
|
|
1208
|
-
seenSeqs.add(event.seq);
|
|
1209
|
-
backlog.push(event);
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
backlog.sort((a, b) => a.seq - b.seq);
|
|
1213
|
-
for (const event of backlog) {
|
|
1214
|
-
try {
|
|
1215
|
-
const matched = toMatch(event);
|
|
1216
|
-
if (matched) {
|
|
1217
|
-
settle(() => resolve2(matched));
|
|
1218
|
-
unsubscribeNotification();
|
|
1219
|
-
return;
|
|
1220
|
-
}
|
|
1221
|
-
} catch (error) {
|
|
1222
|
-
fail(error);
|
|
1223
|
-
return;
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
const highestConsideredSeq = backlog.length > 0 ? backlog[backlog.length - 1].seq : sinceResult.cursor;
|
|
1227
|
-
unsubscribeClose = stream.onClose(() => {
|
|
1228
|
-
settle(
|
|
1229
|
-
() => reject(
|
|
1230
|
-
new AppductError(
|
|
1231
|
-
"connection_error",
|
|
1232
|
-
`The connection to the Appduct daemon closed while waiting for event "${name}".`
|
|
1233
|
-
)
|
|
1234
|
-
)
|
|
1235
|
-
);
|
|
1236
|
-
unsubscribeNotification();
|
|
1237
|
-
});
|
|
1238
|
-
liveHandler = (event) => {
|
|
1239
|
-
if (settled) {
|
|
1240
|
-
return;
|
|
1241
|
-
}
|
|
1242
|
-
if (event.sessionId === sessionId && event.seq <= highestConsideredSeq) {
|
|
1243
|
-
return;
|
|
1244
|
-
}
|
|
1245
|
-
try {
|
|
1246
|
-
const matched = toMatch(event);
|
|
1247
|
-
if (matched) {
|
|
1248
|
-
settle(() => resolve2(matched));
|
|
1249
|
-
unsubscribeNotification();
|
|
1250
|
-
}
|
|
1251
|
-
} catch (error) {
|
|
1252
|
-
fail(error);
|
|
1253
|
-
}
|
|
1254
|
-
};
|
|
1255
|
-
})().catch((error) => {
|
|
1256
|
-
fail(error);
|
|
1257
|
-
});
|
|
1258
|
-
});
|
|
1299
|
+
throw toAppductError(error);
|
|
1300
|
+
} finally {
|
|
1301
|
+
openWaitStreams.delete(waitStream);
|
|
1302
|
+
waitStream.close();
|
|
1303
|
+
}
|
|
1259
1304
|
},
|
|
1260
1305
|
close: () => {
|
|
1306
|
+
closed = true;
|
|
1307
|
+
for (const waitStream of openWaitStreams) {
|
|
1308
|
+
waitStream.close();
|
|
1309
|
+
}
|
|
1310
|
+
openWaitStreams.clear();
|
|
1261
1311
|
stream.close();
|
|
1262
1312
|
}
|
|
1263
1313
|
};
|
|
1264
1314
|
return client;
|
|
1265
1315
|
};
|
|
1266
|
-
var appEventFrom = (event, sessionId) => {
|
|
1267
|
-
if (event.kind !== "app_event" || event.sessionId !== sessionId) {
|
|
1268
|
-
return void 0;
|
|
1269
|
-
}
|
|
1270
|
-
const data = event.data;
|
|
1271
|
-
if (typeof data.name !== "string") {
|
|
1272
|
-
return void 0;
|
|
1273
|
-
}
|
|
1274
|
-
return {
|
|
1275
|
-
name: data.name,
|
|
1276
|
-
payload: data.payload,
|
|
1277
|
-
ts: event.ts,
|
|
1278
|
-
sessionId: event.sessionId,
|
|
1279
|
-
alias: event.alias,
|
|
1280
|
-
seq: event.seq
|
|
1281
|
-
};
|
|
1282
|
-
};
|
|
1283
1316
|
|
|
1284
1317
|
// src/client/connect.ts
|
|
1285
1318
|
var connect3 = async (options = {}) => {
|
|
@@ -1296,10 +1329,16 @@ var connect3 = async (options = {}) => {
|
|
|
1296
1329
|
throw toAppductError(error);
|
|
1297
1330
|
}
|
|
1298
1331
|
try {
|
|
1299
|
-
const detail = await stream.call(
|
|
1332
|
+
const detail = await stream.call(RPC_METHODS4.sessionsDescribe, {
|
|
1300
1333
|
selector: options.selector
|
|
1301
1334
|
});
|
|
1302
|
-
|
|
1335
|
+
const openStream = () => openDaemonStream({
|
|
1336
|
+
stateDir,
|
|
1337
|
+
spawn: options.spawn,
|
|
1338
|
+
autoSpawn: options.autoSpawn,
|
|
1339
|
+
requestTimeoutMs: options.requestTimeoutMs
|
|
1340
|
+
});
|
|
1341
|
+
return makeAppClient(stream, detail.sessionId, openStream);
|
|
1303
1342
|
} catch (error) {
|
|
1304
1343
|
stream.close();
|
|
1305
1344
|
throw toAppductError(error);
|
|
@@ -1307,7 +1346,7 @@ var connect3 = async (options = {}) => {
|
|
|
1307
1346
|
};
|
|
1308
1347
|
|
|
1309
1348
|
// src/client/bootstrap.ts
|
|
1310
|
-
import { RPC_METHODS as
|
|
1349
|
+
import { RPC_METHODS as RPC_METHODS6 } from "@appduct/shared";
|
|
1311
1350
|
|
|
1312
1351
|
// src/cli/open-target.ts
|
|
1313
1352
|
import { execFile } from "node:child_process";
|
|
@@ -1604,7 +1643,7 @@ var deliverToOpenTarget = async (options) => {
|
|
|
1604
1643
|
};
|
|
1605
1644
|
|
|
1606
1645
|
// src/link.ts
|
|
1607
|
-
import { RPC_METHODS as
|
|
1646
|
+
import { RPC_METHODS as RPC_METHODS5 } from "@appduct/shared";
|
|
1608
1647
|
|
|
1609
1648
|
// src/scheme.ts
|
|
1610
1649
|
import { existsSync as existsSync2 } from "node:fs";
|
|
@@ -2252,7 +2291,7 @@ var mintLink = async (options) => {
|
|
|
2252
2291
|
throw usageError(invalidAppIdMessage(appId));
|
|
2253
2292
|
}
|
|
2254
2293
|
const result = await callDaemon(
|
|
2255
|
-
|
|
2294
|
+
RPC_METHODS5.linkCreate,
|
|
2256
2295
|
{
|
|
2257
2296
|
ttlSeconds: options.ttlSeconds,
|
|
2258
2297
|
addressOverride: options.target && usesLoopbackAddress(options.target) ? OPEN_TARGET_ADDRESS_OVERRIDE : void 0
|
|
@@ -2325,10 +2364,11 @@ var waitForSession = async (sessionId, options = {}) => {
|
|
|
2325
2364
|
} catch (error) {
|
|
2326
2365
|
throw toAppductError(error);
|
|
2327
2366
|
}
|
|
2367
|
+
const openStream = () => openDaemonStream({ stateDir, spawn: options.spawn, autoSpawn: options.autoSpawn });
|
|
2328
2368
|
try {
|
|
2329
2369
|
try {
|
|
2330
|
-
await stream.call(
|
|
2331
|
-
return makeAppClient(stream, sessionId);
|
|
2370
|
+
await stream.call(RPC_METHODS6.sessionsDescribe, { selector: sessionId });
|
|
2371
|
+
return makeAppClient(stream, sessionId, openStream);
|
|
2332
2372
|
} catch (error) {
|
|
2333
2373
|
if (!(error instanceof DaemonRpcError) || error.data?.type !== "unknown_session") {
|
|
2334
2374
|
throw error;
|
|
@@ -2375,11 +2415,11 @@ var waitForSession = async (sessionId, options = {}) => {
|
|
|
2375
2415
|
settle(resolve2);
|
|
2376
2416
|
}
|
|
2377
2417
|
});
|
|
2378
|
-
stream.call(
|
|
2418
|
+
stream.call(RPC_METHODS6.eventsSubscribe, { sessionSelector: sessionId, kinds: ["session_claimed"] }).catch((error) => {
|
|
2379
2419
|
settle(() => reject(toAppductError(error)));
|
|
2380
2420
|
});
|
|
2381
2421
|
});
|
|
2382
|
-
return makeAppClient(stream, sessionId);
|
|
2422
|
+
return makeAppClient(stream, sessionId, openStream);
|
|
2383
2423
|
} catch (error) {
|
|
2384
2424
|
stream.close();
|
|
2385
2425
|
throw toAppductError(error);
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `appduct events` (ARCHITECTURE.md §10): subscribes to `events.subscribe` for `app_event`
|
|
3
|
-
* over a persistent connection and streams the app's own events until interrupted (Ctrl-C) or
|
|
4
|
-
* connection drops. Appduct's lifecycle and tool-call kinds are not printed (issue #98). This
|
|
5
|
-
* the one command whose output isn't a single rendered `CliResult` — each event is written as
|
|
6
|
-
* arrives (NDJSON under `--json`, a human line otherwise) — so it plugs into
|
|
7
|
-
* with a live reporter that suppresses the default one-shot bootstrap
|
|
2
|
+
* `appduct events tail` (ARCHITECTURE.md §10): subscribes to `events.subscribe` for `app_event`
|
|
3
|
+
* only over a persistent connection and streams the app's own events until interrupted (Ctrl-C) or
|
|
4
|
+
* the connection drops. Appduct's lifecycle and tool-call kinds are not printed (issue #98). This
|
|
5
|
+
* is the one command whose output isn't a single rendered `CliResult` — each event is written as
|
|
6
|
+
* it arrives (NDJSON under `--json`, a human line otherwise) — so it plugs into
|
|
7
|
+
* `executeHostedCommand` with a live reporter that suppresses the default one-shot bootstrap
|
|
8
|
+
* render (`cli/runner.ts`).
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
* of a live subscription
|
|
11
|
-
* is written through `onEvent` exactly like a live
|
|
12
|
-
* "hosted command" here just completes immediately
|
|
10
|
+
* `appduct events since <cursor>` (issue #6) is a one-shot mode: a single `events.since` pull
|
|
11
|
+
* instead of a live subscription, selected here by `since !== undefined`. It falls out of the same
|
|
12
|
+
* `EventsHostedResult` shape (each retained event is written through `onEvent` exactly like a live
|
|
13
|
+
* one) so the CLI plumbing needs no forking — the "hosted command" here just completes immediately
|
|
14
|
+
* instead of running until interrupted.
|
|
13
15
|
*/
|
|
14
16
|
import { type EventNotification } from "@appduct/shared";
|
|
15
17
|
import type { CliResult } from "../cli/result-types.js";
|
|
@@ -19,15 +21,30 @@ export type EventsCommandOptions = {
|
|
|
19
21
|
/** Exclusive lower bound on `EventNotification.seq`; switches this command to a one-shot
|
|
20
22
|
* `events.since` pull instead of a live `events.subscribe` stream. */
|
|
21
23
|
since?: number;
|
|
24
|
+
/** Whole-name, case-sensitive glob (issue #115), forwarded to `events.subscribe`/`events.since`.
|
|
25
|
+
* No default — everything is delivered unfiltered unless asked. */
|
|
26
|
+
name?: string;
|
|
27
|
+
/** Caps an `app_event`'s payload to this many UTF-8 bytes of its JSON (issue #115), forwarded to
|
|
28
|
+
* `events.subscribe`/`events.since`. No default — nothing is truncated unless asked. */
|
|
29
|
+
payloadMaxBytes?: number;
|
|
30
|
+
};
|
|
31
|
+
/** `events since` mode's resulting cursor, plus how far behind or ahead of the full picture it
|
|
32
|
+
* is (issue #115): `dropped` events fell off the retention buffer before this pull, `remaining`
|
|
33
|
+
* matching events are still waiting beyond this page. */
|
|
34
|
+
export type EventsSinceCursor = {
|
|
35
|
+
cursor: number;
|
|
36
|
+
dropped: number;
|
|
37
|
+
remaining: number;
|
|
22
38
|
};
|
|
23
39
|
export type EventsCommandContext = {
|
|
24
40
|
stateDir: string;
|
|
25
41
|
spawn?: SpawnFn;
|
|
26
42
|
onEvent: (event: EventNotification) => void;
|
|
27
|
-
/**
|
|
28
|
-
* doesn't have to reconstruct it by maxing `seq` over the
|
|
29
|
-
* response is empty — the whole point of a cursor is
|
|
30
|
-
|
|
43
|
+
/** `events since` mode only: called once with the pull's resulting cursor (plus `dropped`/
|
|
44
|
+
* `remaining`), so a scripted caller doesn't have to reconstruct it by maxing `seq` over the
|
|
45
|
+
* printed lines (impossible when the response is empty — the whole point of a cursor is
|
|
46
|
+
* knowing where to resume from either way). */
|
|
47
|
+
onCursor?: (cursor: EventsSinceCursor) => void;
|
|
31
48
|
};
|
|
32
49
|
export type EventsHostedResult = {
|
|
33
50
|
result: CliResult<never>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `appduct
|
|
2
|
+
* `appduct tools call` (ARCHITECTURE.md §10): `tools.call` round trip. Failures propagate as-is
|
|
3
3
|
* (`DaemonRpcError`, preserving `error.data.type` verbatim) — `errors.ts`'s `toCliError` is what
|
|
4
4
|
* renders `error.type` as the app's wire error type end-to-end, not this module's job.
|
|
5
5
|
*/
|
package/dist/commands/link.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `appduct link` (ARCHITECTURE.md §10, §8): CLI-flag validation (`--open`/`--device`'s
|
|
2
|
+
* `appduct sessions link` (ARCHITECTURE.md §10, §8): CLI-flag validation (`--open`/`--device`'s
|
|
3
3
|
* CLI-specific error wording) around the shared `mintLink` core (`../link.ts`), which both this
|
|
4
4
|
* command and `appduct/client`'s `link()` use so the deep-link shape can't drift between them.
|
|
5
5
|
*/
|
package/dist/commands/ls.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** `appduct ls` (ARCHITECTURE.md §10): `sessions.list` passthrough. */
|
|
1
|
+
/** `appduct sessions ls` (ARCHITECTURE.md §10): `sessions.list` passthrough. */
|
|
2
2
|
import type { CliResult, LsCommandData } from "../cli/result-types.js";
|
|
3
3
|
import { type SpawnFn } from "../rpc/client.js";
|
|
4
4
|
export type LsCommandContext = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** `appduct revoke` (ARCHITECTURE.md §10): revokes a session, optionally by selector. */
|
|
1
|
+
/** `appduct sessions revoke` (ARCHITECTURE.md §10): revokes a session, optionally by selector. */
|
|
2
2
|
import type { CliResult, RevokeCommandData } from "../cli/result-types.js";
|
|
3
3
|
import { type SpawnFn } from "../rpc/client.js";
|
|
4
4
|
export type RevokeCommandOptions = {
|
package/dist/commands/tools.d.ts
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `appduct tools` (ARCHITECTURE.md §10): `
|
|
3
|
-
* <text>] [--limit <n>] [--offset <n>]` lists tools for a
|
|
4
|
-
* lists only the session's groups with their tool counts;
|
|
5
|
-
* tool's full schema/annotations.
|
|
2
|
+
* `appduct tools ls`/`appduct tools describe` (ARCHITECTURE.md §10, issue #96): `ls [selector]
|
|
3
|
+
* [--full] [--group <name>] [--filter <text>] [--limit <n>] [--offset <n>]` lists tools for a
|
|
4
|
+
* session; `ls [selector] --groups` lists only the session's groups with their tool counts;
|
|
5
|
+
* `describe [selector] <name>` shows one tool's full schema/annotations.
|
|
6
6
|
*
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* treating it as a selector and list that session's tools instead.
|
|
7
|
+
* The two verbs (`cli/routes/tools/ls.ts` and `cli/routes/tools/describe.ts`) split `[selector]`
|
|
8
|
+
* from `<name>` unambiguously before this handler ever runs — `describe`'s `<name>` is always the
|
|
9
|
+
* last positional, required, never a candidate selector — so there is no probing here: a listing
|
|
10
|
+
* request (`options.name === undefined`) always lists, a lookup (`options.name !== undefined`)
|
|
11
|
+
* always looks up, on the given selector or the implicit session if none was given.
|
|
13
12
|
*
|
|
14
|
-
* `--group`/`--filter`/`--limit`/`--offset` only ever reach the daemon on a *listing* request: the
|
|
15
|
-
* path
|
|
16
|
-
*
|
|
13
|
+
* `--group`/`--filter`/`--limit`/`--offset` only ever reach the daemon on a *listing* request: the
|
|
14
|
+
* detail path always asks for the whole, unpaged registry, so a name lookup can never miss a tool
|
|
15
|
+
* that paging would have left off a page.
|
|
17
16
|
*/
|
|
18
17
|
import type { CliResult, ToolsCommandData } from "../cli/result-types.js";
|
|
19
18
|
import { type SpawnFn } from "../rpc/client.js";
|
package/dist/daemon/config.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export type AppductConfig = {
|
|
|
57
57
|
/** Operator override for advertised-address detection (daemon/address.ts); undefined = auto-detect. */
|
|
58
58
|
advertisedIp?: string;
|
|
59
59
|
/**
|
|
60
|
-
* Deep-link URI scheme used to compose `appduct link`'s output (ARCHITECTURE.md §10: "taken
|
|
60
|
+
* Deep-link URI scheme used to compose `appduct sessions link`'s output (ARCHITECTURE.md §10: "taken
|
|
61
61
|
* from the flag, else `config.json`, else the CLI errors with a clear message"). Not part of the
|
|
62
62
|
* `config.json` shape enumerated in ARCHITECTURE.md §3 (which only covers daemon-side settings),
|
|
63
63
|
* but `config.json` is explicitly "all fields optional" there and this is the natural home for a
|
|
@@ -21,13 +21,45 @@ export type EventBusEmitInput = Omit<EventNotification, "ts" | "seq"> & {
|
|
|
21
21
|
ts?: number;
|
|
22
22
|
};
|
|
23
23
|
export type EventBusListener = (event: EventNotification) => void;
|
|
24
|
+
export type ProjectAppEventOptions = {
|
|
25
|
+
/** Whole-name, case-sensitive glob (issue #112): `*` matches any run of characters, a pattern
|
|
26
|
+
* with no `*` is an exact name. */
|
|
27
|
+
name?: string;
|
|
28
|
+
/** Caps the payload's JSON to this many UTF-8 bytes (issue #113); see {@link truncateToUtf8Bytes}. */
|
|
29
|
+
payloadMaxBytes?: number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Whether `event` survives `options.name`'s glob, and truncates its payload to `options
|
|
33
|
+
* .payloadMaxBytes` when given — the one implementation `since()`'s drain and `daemon.ts`'s
|
|
34
|
+
* `events.subscribe` fan-out both call, so the two can't drift apart (issue #112, #113). Returns
|
|
35
|
+
* `event` (unchanged, or with its payload replaced by a preview) when it should be kept,
|
|
36
|
+
* `undefined` when it should be dropped.
|
|
37
|
+
*
|
|
38
|
+
* Only an `app_event` carries a `name`/`payload` to filter or truncate, so every other kind passes
|
|
39
|
+
* through unfiltered and untouched regardless of either option — the `kinds` filter
|
|
40
|
+
* (`events.subscribe`) is what narrows those.
|
|
41
|
+
*/
|
|
42
|
+
export declare const projectAppEvent: (event: EventNotification, options?: ProjectAppEventOptions) => EventNotification | undefined;
|
|
24
43
|
export type EventsSinceQuery = {
|
|
25
44
|
since?: number;
|
|
26
45
|
limit?: number;
|
|
46
|
+
/** Whole-name, case-sensitive glob (issue #112): `*` matches any run of characters, a pattern
|
|
47
|
+
* with no `*` is an exact name. See {@link projectAppEvent}. */
|
|
48
|
+
name?: string;
|
|
49
|
+
/** Caps a returned `app_event`'s payload to this many UTF-8 bytes of its JSON (issue #113);
|
|
50
|
+
* applied to the page actually returned, after `name`/`limit`, via {@link projectAppEvent}. */
|
|
51
|
+
payloadMaxBytes?: number;
|
|
27
52
|
};
|
|
28
53
|
export type EventsSinceQueryResult = {
|
|
29
54
|
events: EventNotification[];
|
|
30
55
|
cursor: number;
|
|
56
|
+
/** How many app events after `since` were evicted before this call could return them (issue
|
|
57
|
+
* #113): `max(0, oldest.seq - since - 1)`, `oldest` being the buffer's current oldest retained
|
|
58
|
+
* entry. `0` once nothing has fallen off, and `since` defaults to `0` when omitted. */
|
|
59
|
+
dropped: number;
|
|
60
|
+
/** How many events still match `since`/`name` after the returned (possibly `limit`-truncated)
|
|
61
|
+
* page. `0` on the last page. */
|
|
62
|
+
remaining: number;
|
|
31
63
|
};
|
|
32
64
|
export type EventBus = {
|
|
33
65
|
emit: (event: EventBusEmitInput) => void;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The error the daemon's RPC handlers throw to answer a request with a typed wire error
|
|
3
3
|
* (ARCHITECTURE.md §5). It lives apart from `rpc-server.ts` so the modules that *raise* it — the
|
|
4
4
|
* session engine, the call engine — don't drag the socket server itself into every bundle that
|
|
5
|
-
* needs, say, a call-timeout constant from `calls.ts` (the CLI's `
|
|
5
|
+
* needs, say, a call-timeout constant from `calls.ts` (the CLI's `tools call` route does).
|
|
6
6
|
*/
|
|
7
7
|
import type { ErrorType } from "@appduct/shared";
|
|
8
8
|
export declare class RpcApplicationError extends Error {
|
package/dist/errors.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare const isAppductCliError: (value: unknown) => value is AppductCliE
|
|
|
18
18
|
* Converts any error thrown from a command handler into the CLI's rendered `CliError` shape.
|
|
19
19
|
* RPC/app-originated failures (`DaemonRpcError` with a preserved `data.type`) keep that type
|
|
20
20
|
* **verbatim** in `type` — never re-wrapped under a generic bucket (ARCHITECTURE.md §5) — so e.g.
|
|
21
|
-
* `
|
|
21
|
+
* `tools call --json` on a failing tool call renders `error.type === "tool_execution_error"`, not
|
|
22
22
|
* `"tool_error"`. `getExitCodeForError` still buckets that same error into a sysexits class.
|
|
23
23
|
*/
|
|
24
24
|
export declare const toCliError: (error: unknown) => CliError;
|