sentinelayer-cli 0.10.2 → 0.11.1
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/package.json +1 -1
- package/src/commands/session.js +17 -3
- package/src/session/sync.js +101 -32
package/package.json
CHANGED
package/src/commands/session.js
CHANGED
|
@@ -2474,7 +2474,8 @@ export function registerSessionCommand(program) {
|
|
|
2474
2474
|
const remote = await listSessionsFromApi({
|
|
2475
2475
|
targetPath,
|
|
2476
2476
|
includeArchived,
|
|
2477
|
-
limit,
|
|
2477
|
+
limit: emitJson ? 200 : limit,
|
|
2478
|
+
fetchAll: emitJson,
|
|
2478
2479
|
});
|
|
2479
2480
|
const trimmed = emitJson ? remote.sessions : remote.sessions.slice(0, limit);
|
|
2480
2481
|
const payload = {
|
|
@@ -2485,6 +2486,10 @@ export function registerSessionCommand(program) {
|
|
|
2485
2486
|
ok: remote.ok,
|
|
2486
2487
|
reason: remote.reason || "",
|
|
2487
2488
|
count: remote.count,
|
|
2489
|
+
nextCursor: remote.nextCursor || null,
|
|
2490
|
+
hasMore: Boolean(remote.hasMore),
|
|
2491
|
+
truncated: Boolean(remote.truncated),
|
|
2492
|
+
warnings: Array.isArray(remote.warnings) ? remote.warnings : [],
|
|
2488
2493
|
sessions: trimmed,
|
|
2489
2494
|
};
|
|
2490
2495
|
if (emitJson) {
|
|
@@ -2519,10 +2524,19 @@ export function registerSessionCommand(program) {
|
|
|
2519
2524
|
`${item.sessionId} status=${item.status}${archive} created=${created}${lastActivity}`,
|
|
2520
2525
|
);
|
|
2521
2526
|
}
|
|
2522
|
-
if (remote.count > trimmed.length) {
|
|
2527
|
+
if (remote.count > trimmed.length || remote.hasMore) {
|
|
2523
2528
|
console.log(
|
|
2524
2529
|
pc.gray(
|
|
2525
|
-
|
|
2530
|
+
remote.hasMore
|
|
2531
|
+
? "… more sessions are available (raise --limit or use --json)."
|
|
2532
|
+
: `… ${remote.count - trimmed.length} more (raise --limit or use --json).`,
|
|
2533
|
+
),
|
|
2534
|
+
);
|
|
2535
|
+
}
|
|
2536
|
+
if (remote.truncated) {
|
|
2537
|
+
console.log(
|
|
2538
|
+
pc.yellow(
|
|
2539
|
+
"Remote session listing is truncated by the page cap; JSON output includes nextCursor for resume.",
|
|
2526
2540
|
),
|
|
2527
2541
|
);
|
|
2528
2542
|
}
|
package/src/session/sync.js
CHANGED
|
@@ -1243,21 +1243,27 @@ export async function pollSessionEventsBefore(
|
|
|
1243
1243
|
*
|
|
1244
1244
|
* Mirrors the failure shape of `pollHumanMessages` so callers can render
|
|
1245
1245
|
* a single error path: `{ ok, reason, sessions, count }`. Sessions are
|
|
1246
|
-
* returned in API order (
|
|
1246
|
+
* returned in API order (most-recently-active first per the server's contract); the
|
|
1247
1247
|
* caller is responsible for any further sort or filter.
|
|
1248
1248
|
*
|
|
1249
1249
|
* @param {object} [options]
|
|
1250
1250
|
* @param {string} [options.targetPath]
|
|
1251
1251
|
* @param {boolean} [options.includeArchived]
|
|
1252
1252
|
* @param {number} [options.limit]
|
|
1253
|
+
* @param {string|null} [options.cursor]
|
|
1254
|
+
* @param {boolean} [options.fetchAll]
|
|
1255
|
+
* @param {number} [options.maxPages]
|
|
1253
1256
|
* @param {Function} [options.resolveAuthSession]
|
|
1254
1257
|
* @param {Function} [options.fetchImpl]
|
|
1255
|
-
* @returns {Promise<{ok: boolean, reason: string, sessions: Array<object>, count: number}>}
|
|
1258
|
+
* @returns {Promise<{ok: boolean, reason: string, sessions: Array<object>, count: number, nextCursor: string|null, hasMore: boolean, truncated: boolean, warnings: Array<object>}>}
|
|
1256
1259
|
*/
|
|
1257
1260
|
export async function listSessionsFromApi({
|
|
1258
1261
|
targetPath = process.cwd(),
|
|
1259
1262
|
includeArchived = false,
|
|
1260
1263
|
limit = 50,
|
|
1264
|
+
cursor = null,
|
|
1265
|
+
fetchAll = false,
|
|
1266
|
+
maxPages = 50,
|
|
1261
1267
|
resolveAuthSession = resolveActiveAuthSession,
|
|
1262
1268
|
fetchImpl = fetchWithTimeout,
|
|
1263
1269
|
timeoutMs = DEFAULT_SYNC_TIMEOUT_MS,
|
|
@@ -1270,52 +1276,115 @@ export async function listSessionsFromApi({
|
|
|
1270
1276
|
autoRotate: false,
|
|
1271
1277
|
});
|
|
1272
1278
|
} catch {
|
|
1273
|
-
return { ok: false, reason: "no_session", sessions: [], count: 0 };
|
|
1274
|
-
}
|
|
1275
|
-
if (!session || !session.token) {
|
|
1276
|
-
return { ok: false, reason: "not_authenticated", sessions: [], count: 0 };
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
1280
|
-
const query = new URLSearchParams();
|
|
1281
|
-
if (includeArchived) query.set("include_archived", "true");
|
|
1282
|
-
const normalizedLimit = Math.max(1, Math.min(200, normalizePositiveInteger(limit, 50)));
|
|
1283
|
-
query.set("limit", String(normalizedLimit));
|
|
1284
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions?${query.toString()}`;
|
|
1285
|
-
|
|
1286
|
-
let response;
|
|
1287
|
-
try {
|
|
1288
|
-
response = await fetchImpl(
|
|
1289
|
-
endpoint,
|
|
1290
|
-
{
|
|
1291
|
-
method: "GET",
|
|
1292
|
-
headers: { Authorization: `Bearer ${session.token}` },
|
|
1293
|
-
},
|
|
1294
|
-
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
1295
|
-
);
|
|
1296
|
-
} catch (err) {
|
|
1297
1279
|
return {
|
|
1298
1280
|
ok: false,
|
|
1299
|
-
reason:
|
|
1281
|
+
reason: "no_session",
|
|
1300
1282
|
sessions: [],
|
|
1301
1283
|
count: 0,
|
|
1284
|
+
nextCursor: null,
|
|
1285
|
+
hasMore: false,
|
|
1286
|
+
truncated: false,
|
|
1287
|
+
warnings: [],
|
|
1302
1288
|
};
|
|
1303
1289
|
}
|
|
1304
|
-
if (!
|
|
1290
|
+
if (!session || !session.token) {
|
|
1305
1291
|
return {
|
|
1306
1292
|
ok: false,
|
|
1307
|
-
reason:
|
|
1293
|
+
reason: "not_authenticated",
|
|
1308
1294
|
sessions: [],
|
|
1309
1295
|
count: 0,
|
|
1296
|
+
nextCursor: null,
|
|
1297
|
+
hasMore: false,
|
|
1298
|
+
truncated: false,
|
|
1299
|
+
warnings: [],
|
|
1310
1300
|
};
|
|
1311
1301
|
}
|
|
1312
|
-
|
|
1313
|
-
const
|
|
1302
|
+
|
|
1303
|
+
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
1304
|
+
const normalizedLimit = Math.max(1, Math.min(200, normalizePositiveInteger(limit, 50)));
|
|
1305
|
+
const normalizedMaxPages = Math.max(1, Math.min(100, normalizePositiveInteger(maxPages, 50)));
|
|
1306
|
+
let nextCursor = normalizeString(cursor) || null;
|
|
1307
|
+
let hasMore = false;
|
|
1308
|
+
const sessions = [];
|
|
1309
|
+
const seenSessionIds = new Set();
|
|
1310
|
+
let count = 0;
|
|
1311
|
+
|
|
1312
|
+
for (let page = 0; page < normalizedMaxPages; page += 1) {
|
|
1313
|
+
const query = new URLSearchParams();
|
|
1314
|
+
if (includeArchived) query.set("include_archived", "true");
|
|
1315
|
+
query.set("limit", String(normalizedLimit));
|
|
1316
|
+
if (nextCursor) query.set("cursor", nextCursor);
|
|
1317
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions?${query.toString()}`;
|
|
1318
|
+
|
|
1319
|
+
let response;
|
|
1320
|
+
try {
|
|
1321
|
+
response = await fetchImpl(
|
|
1322
|
+
endpoint,
|
|
1323
|
+
{
|
|
1324
|
+
method: "GET",
|
|
1325
|
+
headers: { Authorization: `Bearer ${session.token}` },
|
|
1326
|
+
},
|
|
1327
|
+
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
1328
|
+
);
|
|
1329
|
+
} catch (err) {
|
|
1330
|
+
return {
|
|
1331
|
+
ok: false,
|
|
1332
|
+
reason: normalizeString(err?.message) || "list_failed",
|
|
1333
|
+
sessions: [],
|
|
1334
|
+
count: 0,
|
|
1335
|
+
nextCursor: null,
|
|
1336
|
+
hasMore: false,
|
|
1337
|
+
truncated: false,
|
|
1338
|
+
warnings: [],
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
if (!response || !response.ok) {
|
|
1342
|
+
return {
|
|
1343
|
+
ok: false,
|
|
1344
|
+
reason: `api_${response ? response.status : "no_response"}`,
|
|
1345
|
+
sessions: [],
|
|
1346
|
+
count: 0,
|
|
1347
|
+
nextCursor: null,
|
|
1348
|
+
hasMore: false,
|
|
1349
|
+
truncated: false,
|
|
1350
|
+
warnings: [],
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
const payload = await response.json().catch(() => ({}));
|
|
1354
|
+
const pageSessions = Array.isArray(payload?.sessions) ? payload.sessions : [];
|
|
1355
|
+
for (const item of pageSessions) {
|
|
1356
|
+
const sessionId = normalizeString(item?.sessionId || item?.id);
|
|
1357
|
+
if (sessionId && seenSessionIds.has(sessionId)) continue;
|
|
1358
|
+
if (sessionId) seenSessionIds.add(sessionId);
|
|
1359
|
+
sessions.push(item);
|
|
1360
|
+
}
|
|
1361
|
+
count = sessions.length;
|
|
1362
|
+
nextCursor = normalizeString(payload?.next_cursor || payload?.nextCursor);
|
|
1363
|
+
hasMore = Boolean(payload?.has_more && nextCursor);
|
|
1364
|
+
if (!fetchAll || !hasMore) break;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
const truncated = Boolean(fetchAll && hasMore && nextCursor);
|
|
1368
|
+
const warnings = truncated
|
|
1369
|
+
? [
|
|
1370
|
+
{
|
|
1371
|
+
code: "SESSION_LIST_MAX_PAGES_REACHED",
|
|
1372
|
+
message: `Session list stopped after ${normalizedMaxPages} pages; output is partial and nextCursor can resume the listing.`,
|
|
1373
|
+
maxPages: normalizedMaxPages,
|
|
1374
|
+
nextCursor,
|
|
1375
|
+
},
|
|
1376
|
+
]
|
|
1377
|
+
: [];
|
|
1378
|
+
|
|
1314
1379
|
return {
|
|
1315
1380
|
ok: true,
|
|
1316
1381
|
reason: "",
|
|
1317
1382
|
sessions,
|
|
1318
|
-
count
|
|
1383
|
+
count,
|
|
1384
|
+
nextCursor: nextCursor || null,
|
|
1385
|
+
hasMore,
|
|
1386
|
+
truncated,
|
|
1387
|
+
warnings,
|
|
1319
1388
|
};
|
|
1320
1389
|
}
|
|
1321
1390
|
|