@vforsh/argus 0.1.23 → 0.2.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/dist/argus.js CHANGED
@@ -2130,7 +2130,7 @@ var {
2130
2130
  // package.json
2131
2131
  var package_default = {
2132
2132
  name: "@vforsh/argus",
2133
- version: "0.1.23",
2133
+ version: "0.2.0",
2134
2134
  repository: {
2135
2135
  type: "git",
2136
2136
  url: "git+https://github.com/vforsh/argus.git",
@@ -2156,9 +2156,9 @@ var package_default = {
2156
2156
  access: "public"
2157
2157
  },
2158
2158
  dependencies: {
2159
- "@vforsh/argus-core": "^0.1.11",
2160
- "@vforsh/argus-plugin-api": "^0.1.4",
2161
- "@vforsh/argus-watcher": "^0.1.16",
2159
+ "@vforsh/argus-core": "^0.2.0",
2160
+ "@vforsh/argus-plugin-api": "^0.2.0",
2161
+ "@vforsh/argus-watcher": "^0.2.0",
2162
2162
  commander: "14.0.2",
2163
2163
  esbuild: "0.25.12",
2164
2164
  prettier: "3.7.4"
@@ -2448,7 +2448,7 @@ var optionalPoint = (value, key) => {
2448
2448
  // ../argus-core/dist/protocol/http/auth.js
2449
2449
  var AUTH_STATE_METADATA_SCHEMA_VERSION = 1;
2450
2450
  // ../argus-core/dist/protocol/version.js
2451
- var ARGUS_PROTOCOL_VERSION = 1;
2451
+ var ARGUS_PROTOCOL_VERSION = 2;
2452
2452
  // ../argus-core/dist/registry/registry.js
2453
2453
  import fs2 from "node:fs/promises";
2454
2454
  import path3 from "node:path";
@@ -6093,12 +6093,279 @@ var buildStatus = (ctx) => {
6093
6093
  };
6094
6094
  };
6095
6095
 
6096
+ // ../argus-watcher/dist/buffer/LogBuffer.js
6097
+ import { randomUUID } from "node:crypto";
6098
+
6099
+ class LogEpochError extends Error {
6100
+ code;
6101
+ constructor(code, message) {
6102
+ super(message);
6103
+ this.name = "LogEpochError";
6104
+ this.code = code;
6105
+ }
6106
+ }
6107
+ var EPOCH_PREFIX = "argus-log-epoch-v1.";
6108
+
6109
+ class LogBuffer {
6110
+ maxSize;
6111
+ streamId = randomUUID();
6112
+ events = [];
6113
+ nextId = 1;
6114
+ waiters = [];
6115
+ epochWaiters = [];
6116
+ constructor(maxSize) {
6117
+ this.maxSize = maxSize;
6118
+ }
6119
+ add(event) {
6120
+ const entry = {
6121
+ ...event,
6122
+ id: this.nextId++
6123
+ };
6124
+ this.events.push(entry);
6125
+ this.trim();
6126
+ this.flushWaiters();
6127
+ this.flushEpochWaiters();
6128
+ return entry;
6129
+ }
6130
+ listAfter(after, filters, limit) {
6131
+ return this.listAfterPosition(after, filters, limit);
6132
+ }
6133
+ getCursor() {
6134
+ return this.beginLogEpoch();
6135
+ }
6136
+ beginLogEpoch() {
6137
+ return encodeEpoch({ v: 1, s: this.streamId, p: this.currentPosition() });
6138
+ }
6139
+ getEpoch() {
6140
+ return this.beginLogEpoch();
6141
+ }
6142
+ getEpochAt(position) {
6143
+ return encodeEpoch({ v: 1, s: this.streamId, p: position });
6144
+ }
6145
+ listAfterEpoch(epoch, filters, limit) {
6146
+ const position = this.resolveEpoch(epoch);
6147
+ const events = this.listAfterPosition(position, filters, limit);
6148
+ return {
6149
+ events,
6150
+ nextCursor: events.length > 0 ? this.getEpochAt(events[events.length - 1]?.id ?? position) : epoch
6151
+ };
6152
+ }
6153
+ waitForAfter(after, filters, limit, timeoutMs) {
6154
+ const immediate = this.listAfter(after, filters, limit);
6155
+ if (immediate.length > 0) {
6156
+ return Promise.resolve(immediate);
6157
+ }
6158
+ return new Promise((resolve) => {
6159
+ const timer = setTimeout(() => {
6160
+ this.waiters = this.waiters.filter((waiter) => waiter.timer !== timer);
6161
+ resolve([]);
6162
+ }, timeoutMs);
6163
+ this.waiters.push({ after, filters, limit, resolve, timer });
6164
+ });
6165
+ }
6166
+ async waitForAfterEpoch(epoch, filters, limit, timeoutMs) {
6167
+ const position = this.resolveEpoch(epoch);
6168
+ const immediate = this.listAfterPosition(position, filters, limit);
6169
+ if (immediate.length > 0) {
6170
+ return { events: immediate, nextCursor: this.getEpochAt(immediate[immediate.length - 1]?.id ?? position) };
6171
+ }
6172
+ return new Promise((resolve, reject) => {
6173
+ const timer = setTimeout(() => {
6174
+ this.epochWaiters = this.epochWaiters.filter((waiter) => waiter.timer !== timer);
6175
+ resolve({ events: [], nextCursor: epoch });
6176
+ }, timeoutMs);
6177
+ this.epochWaiters.push({ position, filters, limit, resolve, reject, timer });
6178
+ });
6179
+ }
6180
+ getStats() {
6181
+ if (this.events.length === 0) {
6182
+ return { size: this.maxSize, count: 0, minId: null, maxId: null };
6183
+ }
6184
+ return {
6185
+ size: this.maxSize,
6186
+ count: this.events.length,
6187
+ minId: this.events[0]?.id ?? null,
6188
+ maxId: this.events[this.events.length - 1]?.id ?? null
6189
+ };
6190
+ }
6191
+ trim() {
6192
+ if (this.maxSize <= 0) {
6193
+ this.events = [];
6194
+ return;
6195
+ }
6196
+ if (this.events.length <= this.maxSize) {
6197
+ return;
6198
+ }
6199
+ this.events = this.events.slice(this.events.length - this.maxSize);
6200
+ }
6201
+ flushWaiters() {
6202
+ if (this.waiters.length === 0) {
6203
+ return;
6204
+ }
6205
+ const remaining = [];
6206
+ for (const waiter of this.waiters) {
6207
+ const events = this.listAfter(waiter.after, waiter.filters, waiter.limit);
6208
+ if (events.length > 0) {
6209
+ clearTimeout(waiter.timer);
6210
+ waiter.resolve(events);
6211
+ continue;
6212
+ }
6213
+ remaining.push(waiter);
6214
+ }
6215
+ this.waiters = remaining;
6216
+ }
6217
+ flushEpochWaiters() {
6218
+ if (this.epochWaiters.length === 0) {
6219
+ return;
6220
+ }
6221
+ const remaining = [];
6222
+ for (const waiter of this.epochWaiters) {
6223
+ try {
6224
+ this.validatePosition(waiter.position);
6225
+ } catch (error) {
6226
+ clearTimeout(waiter.timer);
6227
+ waiter.reject(error);
6228
+ continue;
6229
+ }
6230
+ const events = this.listAfterPosition(waiter.position, waiter.filters, waiter.limit);
6231
+ if (events.length > 0) {
6232
+ clearTimeout(waiter.timer);
6233
+ waiter.resolve({
6234
+ events,
6235
+ nextCursor: this.getEpochAt(events[events.length - 1]?.id ?? waiter.position)
6236
+ });
6237
+ continue;
6238
+ }
6239
+ remaining.push(waiter);
6240
+ }
6241
+ this.epochWaiters = remaining;
6242
+ }
6243
+ listAfterPosition(position, filters, limit) {
6244
+ return this.events.filter((event) => event.id > position && matchesFilters(event, filters)).slice(0, limit);
6245
+ }
6246
+ resolveEpoch(epoch) {
6247
+ const payload = decodeEpoch(epoch);
6248
+ if (!payload) {
6249
+ throw new LogEpochError("invalid", "Invalid log epoch. Capture a new epoch from the watcher.");
6250
+ }
6251
+ if (payload.s !== this.streamId) {
6252
+ throw new LogEpochError("mismatch", "Log epoch belongs to a different watcher session. Capture a new epoch.");
6253
+ }
6254
+ if (payload.p > this.currentPosition()) {
6255
+ throw new LogEpochError("future", "Log epoch points past the current log stream.");
6256
+ }
6257
+ this.validatePosition(payload.p);
6258
+ return payload.p;
6259
+ }
6260
+ validatePosition(position) {
6261
+ if (this.events.length > 0 && position < (this.events[0]?.id ?? position + 1) - 1) {
6262
+ throw new LogEpochError("evicted", "Log epoch is stale because the ring buffer evicted entries. Capture a new epoch.");
6263
+ }
6264
+ if (this.maxSize === 0 && this.currentPosition() > position) {
6265
+ throw new LogEpochError("evicted", "Log epoch is stale because the ring buffer evicted entries. Capture a new epoch.");
6266
+ }
6267
+ }
6268
+ currentPosition() {
6269
+ return this.nextId - 1;
6270
+ }
6271
+ }
6272
+ var encodeEpoch = (payload) => `${EPOCH_PREFIX}${Buffer.from(JSON.stringify(payload)).toString("base64url")}`;
6273
+ var decodeEpoch = (epoch) => {
6274
+ if (typeof epoch !== "string" || !epoch.startsWith(EPOCH_PREFIX)) {
6275
+ return null;
6276
+ }
6277
+ try {
6278
+ const parsed = JSON.parse(Buffer.from(epoch.slice(EPOCH_PREFIX.length), "base64url").toString("utf8"));
6279
+ if (parsed.v !== 1 || typeof parsed.s !== "string" || !parsed.s || typeof parsed.p !== "number" || !Number.isSafeInteger(parsed.p) || parsed.p < 0) {
6280
+ return null;
6281
+ }
6282
+ return parsed;
6283
+ } catch {
6284
+ return null;
6285
+ }
6286
+ };
6287
+ var matchesFilters = (event, filters) => {
6288
+ if (filters.sinceTs && event.ts < filters.sinceTs) {
6289
+ return false;
6290
+ }
6291
+ if (filters.levels && filters.levels.length > 0 && !filters.levels.includes(event.level)) {
6292
+ return false;
6293
+ }
6294
+ if (filters.source && !event.source.toLowerCase().includes(filters.source.toLowerCase())) {
6295
+ return false;
6296
+ }
6297
+ if (filters.match && filters.match.length > 0) {
6298
+ for (const pattern of filters.match) {
6299
+ if (pattern.test(event.text)) {
6300
+ return true;
6301
+ }
6302
+ }
6303
+ return false;
6304
+ }
6305
+ return true;
6306
+ };
6307
+
6308
+ // ../argus-watcher/dist/http/routes/logEpochQuery.js
6309
+ var parseLogPosition = (url) => {
6310
+ const after = normalizeQueryValue2(url.searchParams.get("after") ?? null);
6311
+ const sinceEpoch = normalizeQueryValue2(url.searchParams.get("sinceEpoch"));
6312
+ if (after && sinceEpoch) {
6313
+ return { error: "Use either after or sinceEpoch, not both." };
6314
+ }
6315
+ if (sinceEpoch) {
6316
+ return { kind: "epoch", epoch: sinceEpoch };
6317
+ }
6318
+ if (after) {
6319
+ return { kind: "epoch", epoch: after };
6320
+ }
6321
+ return { kind: "all" };
6322
+ };
6323
+ var respondLogEpochError = (res, error) => {
6324
+ const status = error.code === "invalid" || error.code === "future" ? 400 : 409;
6325
+ respondJson(res, {
6326
+ ok: false,
6327
+ error: {
6328
+ message: error.message,
6329
+ code: `log_epoch_${error.code}`
6330
+ }
6331
+ }, status);
6332
+ };
6333
+ var listLogsFromPosition = (buffer, position, filters, limit) => {
6334
+ if (position.kind === "epoch") {
6335
+ return buffer.listAfterEpoch(position.epoch, filters, limit);
6336
+ }
6337
+ return createQueryResult(buffer, position, buffer.listAfter(0, filters, limit));
6338
+ };
6339
+ var waitForLogsFromPosition = async (buffer, position, filters, limit, timeoutMs) => {
6340
+ if (position.kind === "epoch") {
6341
+ return buffer.waitForAfterEpoch(position.epoch, filters, limit, timeoutMs);
6342
+ }
6343
+ const events = await buffer.waitForAfter(0, filters, limit, timeoutMs);
6344
+ return createQueryResult(buffer, position, events);
6345
+ };
6346
+ var createQueryResult = (buffer, position, events) => ({
6347
+ events,
6348
+ nextCursor: resolveNextCursor(buffer, position, events)
6349
+ });
6350
+ var resolveNextCursor = (buffer, position, events) => {
6351
+ if (events.length > 0) {
6352
+ return buffer.getEpochAt(events[events.length - 1]?.id ?? 0);
6353
+ }
6354
+ if (position.kind === "epoch") {
6355
+ return position.epoch;
6356
+ }
6357
+ return buffer.getEpoch();
6358
+ };
6359
+
6096
6360
  // ../argus-watcher/dist/http/routes/getLogs.js
6097
6361
  var route2 = defineJsonRoute({
6098
6362
  method: "GET",
6099
6363
  path: "/logs",
6100
6364
  handle: ({ res, url, ctx }) => {
6101
- const after = clampNumber(url.searchParams.get("after"), 0);
6365
+ const position = parseLogPosition(url);
6366
+ if ("error" in position) {
6367
+ return respondLogEpochError(res, new LogEpochError("invalid", position.error));
6368
+ }
6102
6369
  const limit = clampNumber(url.searchParams.get("limit"), 500, 1, 5000);
6103
6370
  const levels = parseLevels(url.searchParams.get("levels"));
6104
6371
  const match = url.searchParams.getAll("match");
@@ -6116,10 +6383,25 @@ var route2 = defineJsonRoute({
6116
6383
  if (compiledMatch.error) {
6117
6384
  return respondInvalidMatch(res, compiledMatch.error);
6118
6385
  }
6119
- emitRequest(ctx, res, "logs", { after, limit, levels, match: matchPatterns.patterns, matchCase, source, sinceTs });
6120
- const events = ctx.buffer.listAfter(after, { levels, match: compiledMatch.match, source, sinceTs }, limit);
6121
- const nextAfter = events.length > 0 ? events[events.length - 1]?.id ?? after : after;
6122
- return { ok: true, events, nextAfter };
6386
+ emitRequest(ctx, res, "logs", {
6387
+ after: position.kind === "epoch" && url.searchParams.has("after") ? position.epoch : undefined,
6388
+ sinceEpoch: position.kind === "epoch" ? position.epoch : undefined,
6389
+ limit,
6390
+ levels,
6391
+ match: matchPatterns.patterns,
6392
+ matchCase,
6393
+ source,
6394
+ sinceTs
6395
+ });
6396
+ try {
6397
+ const result = listLogsFromPosition(ctx.buffer, position, { levels, match: compiledMatch.match, source, sinceTs }, limit);
6398
+ return { ok: true, ...result };
6399
+ } catch (error) {
6400
+ if (error instanceof LogEpochError) {
6401
+ return respondLogEpochError(res, error);
6402
+ }
6403
+ throw error;
6404
+ }
6123
6405
  }
6124
6406
  });
6125
6407
 
@@ -6130,12 +6412,22 @@ var route3 = defineJsonRoute({
6130
6412
  handle: ({ ctx }) => ({ ok: true, cursor: ctx.buffer.getCursor() })
6131
6413
  });
6132
6414
 
6133
- // ../argus-watcher/dist/http/routes/getTail.js
6415
+ // ../argus-watcher/dist/http/routes/getLogEpoch.js
6134
6416
  var route4 = defineJsonRoute({
6417
+ method: "GET",
6418
+ path: "/logs/epoch",
6419
+ handle: ({ ctx }) => ({ ok: true, epoch: ctx.buffer.beginLogEpoch() })
6420
+ });
6421
+
6422
+ // ../argus-watcher/dist/http/routes/getTail.js
6423
+ var route5 = defineJsonRoute({
6135
6424
  method: "GET",
6136
6425
  path: "/tail",
6137
6426
  handle: async ({ res, url, ctx }) => {
6138
- const after = clampNumber(url.searchParams.get("after"), 0);
6427
+ const position = parseLogPosition(url);
6428
+ if ("error" in position) {
6429
+ return respondLogEpochError(res, new LogEpochError("invalid", position.error));
6430
+ }
6139
6431
  const limit = clampNumber(url.searchParams.get("limit"), 500, 1, 5000);
6140
6432
  const timeoutMs = clampNumber(url.searchParams.get("timeoutMs"), 25000, 1000, 120000);
6141
6433
  const levels = parseLevels(url.searchParams.get("levels"));
@@ -6153,10 +6445,25 @@ var route4 = defineJsonRoute({
6153
6445
  if (compiledMatch.error) {
6154
6446
  return respondInvalidMatch(res, compiledMatch.error);
6155
6447
  }
6156
- emitRequest(ctx, res, "tail", { after, limit, levels, match: matchPatterns.patterns, matchCase, source, timeoutMs });
6157
- const events = await ctx.buffer.waitForAfter(after, { levels, match: compiledMatch.match, source }, limit, timeoutMs);
6158
- const nextAfter = events.length > 0 ? events[events.length - 1]?.id ?? after : after;
6159
- return { ok: true, events, nextAfter, timedOut: events.length === 0 };
6448
+ emitRequest(ctx, res, "tail", {
6449
+ after: position.kind === "epoch" ? position.epoch : undefined,
6450
+ sinceEpoch: position.kind === "epoch" ? position.epoch : undefined,
6451
+ limit,
6452
+ levels,
6453
+ match: matchPatterns.patterns,
6454
+ matchCase,
6455
+ source,
6456
+ timeoutMs
6457
+ });
6458
+ try {
6459
+ const result = await waitForLogsFromPosition(ctx.buffer, position, { levels, match: compiledMatch.match, source }, limit, timeoutMs);
6460
+ return { ok: true, ...result, timedOut: result.events.length === 0 };
6461
+ } catch (error) {
6462
+ if (error instanceof LogEpochError) {
6463
+ return respondLogEpochError(res, error);
6464
+ }
6465
+ throw error;
6466
+ }
6160
6467
  }
6161
6468
  });
6162
6469
 
@@ -6471,7 +6778,7 @@ var hasTruthyFlag = (searchParams, key) => {
6471
6778
  var optionalClampNumber = (value, min) => value == null ? undefined : clampNumber(value, undefined, min);
6472
6779
 
6473
6780
  // ../argus-watcher/dist/http/routes/getNet.js
6474
- var route5 = defineJsonRoute({
6781
+ var route6 = defineJsonRoute({
6475
6782
  method: "GET",
6476
6783
  path: "/net",
6477
6784
  handle: ({ res, url, ctx }) => {
@@ -6490,7 +6797,7 @@ var route5 = defineJsonRoute({
6490
6797
  });
6491
6798
 
6492
6799
  // ../argus-watcher/dist/http/routes/getNetRequests.js
6493
- var route6 = defineJsonRoute({
6800
+ var route7 = defineJsonRoute({
6494
6801
  method: "GET",
6495
6802
  path: "/net/requests",
6496
6803
  handle: ({ res, url, ctx }) => {
@@ -6544,7 +6851,7 @@ var parsePositiveInt2 = (value) => {
6544
6851
  };
6545
6852
 
6546
6853
  // ../argus-watcher/dist/http/routes/getNetRequest.js
6547
- var route7 = defineJsonRoute({
6854
+ var route8 = defineJsonRoute({
6548
6855
  method: "GET",
6549
6856
  path: "/net/request",
6550
6857
  handle: ({ res, url, ctx }) => {
@@ -6628,7 +6935,7 @@ var createNetBodyError = (message, code) => {
6628
6935
  var capitalizePart = (part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`;
6629
6936
 
6630
6937
  // ../argus-watcher/dist/http/routes/getNetRequestBody.js
6631
- var route8 = defineJsonRoute({
6938
+ var route9 = defineJsonRoute({
6632
6939
  method: "GET",
6633
6940
  path: "/net/request/body",
6634
6941
  handle: async ({ res, url, ctx }) => {
@@ -6698,7 +7005,7 @@ var resolveBodySessionId = (ctx, frameId, storedSessionId) => {
6698
7005
  };
6699
7006
 
6700
7007
  // ../argus-watcher/dist/http/routes/getNetTail.js
6701
- var route9 = defineJsonRoute({
7008
+ var route10 = defineJsonRoute({
6702
7009
  method: "GET",
6703
7010
  path: "/net/tail",
6704
7011
  handle: async ({ res, url, ctx }) => {
@@ -6734,7 +7041,7 @@ var pollNetBuffer = async (buffer, after, filters, limit, timeoutMs) => {
6734
7041
  var delay2 = (timeoutMs) => new Promise((resolve) => setTimeout(resolve, timeoutMs));
6735
7042
 
6736
7043
  // ../argus-watcher/dist/http/routes/getNetWebSockets.js
6737
- var route10 = defineJsonRoute({
7044
+ var route11 = defineJsonRoute({
6738
7045
  method: "GET",
6739
7046
  path: "/net/ws",
6740
7047
  handle: ({ res, url, ctx }) => {
@@ -6753,7 +7060,7 @@ var route10 = defineJsonRoute({
6753
7060
  });
6754
7061
 
6755
7062
  // ../argus-watcher/dist/http/routes/getNetWebSocketConnection.js
6756
- var route11 = defineJsonRoute({
7063
+ var route12 = defineJsonRoute({
6757
7064
  method: "GET",
6758
7065
  path: "/net/ws/connection",
6759
7066
  handle: ({ res, url, ctx }) => {
@@ -6775,7 +7082,7 @@ var route11 = defineJsonRoute({
6775
7082
  });
6776
7083
 
6777
7084
  // ../argus-watcher/dist/http/routes/getNetSse.js
6778
- var route12 = defineJsonRoute({
7085
+ var route13 = defineJsonRoute({
6779
7086
  method: "GET",
6780
7087
  path: "/net/sse",
6781
7088
  handle: ({ res, url, ctx }) => {
@@ -6794,7 +7101,7 @@ var route12 = defineJsonRoute({
6794
7101
  });
6795
7102
 
6796
7103
  // ../argus-watcher/dist/http/routes/postNetClear.js
6797
- var route13 = defineJsonRoute({
7104
+ var route14 = defineJsonRoute({
6798
7105
  method: "POST",
6799
7106
  path: "/net/clear",
6800
7107
  handle: ({ res, ctx }) => {
@@ -7279,7 +7586,7 @@ var normalizeStorageEntries = (entries) => entries.filter((entry) => typeof entr
7279
7586
  }));
7280
7587
 
7281
7588
  // ../argus-watcher/dist/http/routes/getAuthCookies.js
7282
- var route14 = defineJsonRoute({
7589
+ var route15 = defineJsonRoute({
7283
7590
  method: "GET",
7284
7591
  path: "/auth/cookies",
7285
7592
  handle: ({ res, url, ctx }) => {
@@ -7294,7 +7601,7 @@ var route14 = defineJsonRoute({
7294
7601
  });
7295
7602
 
7296
7603
  // ../argus-watcher/dist/http/routes/getAuthState.js
7297
- var route15 = defineJsonRoute({
7604
+ var route16 = defineJsonRoute({
7298
7605
  method: "GET",
7299
7606
  path: "/auth/state",
7300
7607
  handle: ({ res, url, ctx }) => {
@@ -7484,7 +7791,7 @@ var applyAuthStateToSession = async (input) => hydrateAuthState({
7484
7791
  });
7485
7792
 
7486
7793
  // ../argus-watcher/dist/http/routes/postAuthStateLoad.js
7487
- var route16 = defineJsonRoute({
7794
+ var route17 = defineJsonRoute({
7488
7795
  method: "POST",
7489
7796
  path: "/auth/state/load",
7490
7797
  parseBody: true,
@@ -7603,12 +7910,12 @@ var expandRemoteObjectViaGetProperties = async (record2, cdp) => {
7603
7910
  };
7604
7911
 
7605
7912
  // ../argus-watcher/dist/cdp/scenarioBridge.js
7606
- import { randomUUID } from "node:crypto";
7913
+ import { randomUUID as randomUUID2 } from "node:crypto";
7607
7914
  var SCENARIO_ACTION_TIMEOUT_MS = 30000;
7608
7915
  var DEFAULT_LOG_LIMIT = 500;
7609
7916
  var MAX_LOG_LIMIT = 5000;
7610
7917
  var installScenarioBridge = async (session, expression, args, services, timeoutMs) => {
7611
- const token = randomUUID();
7918
+ const token = randomUUID2();
7612
7919
  const suffix = token.replaceAll("-", "");
7613
7920
  const bindingName = `__argusScenarioSend_${suffix}`;
7614
7921
  const responseName = `__argusScenarioReceive_${suffix}`;
@@ -7728,17 +8035,11 @@ var parseCheckpointName = (value) => {
7728
8035
  var readScenarioLogs = (buffer, value) => {
7729
8036
  const payload = requireRecord(value, "logs.read payload");
7730
8037
  const cursor = payload.cursor;
7731
- if (typeof cursor !== "number" || !Number.isInteger(cursor) || cursor < 0) {
7732
- throw new Error("log cursor must be a non-negative integer");
8038
+ if (typeof cursor !== "string" || !cursor.trim()) {
8039
+ throw new Error("log cursor must be a non-empty opaque cursor");
7733
8040
  }
7734
8041
  const { filters, limit } = parseLogOptions(payload.options);
7735
- const endCursor = buffer.getCursor();
7736
- const events = buffer.listAfter(cursor, filters, limit);
7737
- let nextCursor = endCursor;
7738
- if (events.length === limit) {
7739
- nextCursor = events[events.length - 1]?.id ?? cursor;
7740
- }
7741
- return { events, nextCursor };
8042
+ return buffer.listAfterEpoch(cursor, filters, limit);
7742
8043
  };
7743
8044
  var parseLogOptions = (value) => {
7744
8045
  if (value == null)
@@ -8021,7 +8322,7 @@ var shouldMaterializeByValue = (record2) => {
8021
8322
  };
8022
8323
 
8023
8324
  // ../argus-watcher/dist/http/routes/postEval.js
8024
- var route17 = defineJsonRoute({
8325
+ var route18 = defineJsonRoute({
8025
8326
  method: "POST",
8026
8327
  path: "/eval",
8027
8328
  parseBody: true,
@@ -8060,7 +8361,7 @@ var normalizeEvalArgs = (args) => {
8060
8361
  };
8061
8362
 
8062
8363
  // ../argus-watcher/dist/http/routes/postTraceStart.js
8063
- var route18 = defineJsonRoute({
8364
+ var route19 = defineJsonRoute({
8064
8365
  method: "POST",
8065
8366
  path: "/trace/start",
8066
8367
  parseBody: true,
@@ -8069,7 +8370,7 @@ var route18 = defineJsonRoute({
8069
8370
  });
8070
8371
 
8071
8372
  // ../argus-watcher/dist/http/routes/postTraceStop.js
8072
- var route19 = defineJsonRoute({
8373
+ var route20 = defineJsonRoute({
8073
8374
  method: "POST",
8074
8375
  path: "/trace/stop",
8075
8376
  parseBody: true,
@@ -8165,7 +8466,7 @@ var validateClip = (clip) => {
8165
8466
  };
8166
8467
 
8167
8468
  // ../argus-watcher/dist/http/routes/postScreenshot.js
8168
- var route20 = defineJsonRoute({
8469
+ var route21 = defineJsonRoute({
8169
8470
  method: "POST",
8170
8471
  path: "/screenshot",
8171
8472
  parseBody: true,
@@ -8677,7 +8978,7 @@ var countNodes = (nodes) => {
8677
8978
  };
8678
8979
 
8679
8980
  // ../argus-watcher/dist/http/routes/postSnapshot.js
8680
- var route21 = defineJsonRoute({
8981
+ var route22 = defineJsonRoute({
8681
8982
  method: "POST",
8682
8983
  path: "/snapshot",
8683
8984
  parseBody: true,
@@ -8854,7 +9155,7 @@ var locateRoutes = [
8854
9155
  ];
8855
9156
 
8856
9157
  // ../argus-watcher/dist/http/routes/postCodeList.js
8857
- var route22 = defineJsonRoute({
9158
+ var route23 = defineJsonRoute({
8858
9159
  method: "POST",
8859
9160
  path: "/code/list",
8860
9161
  parseBody: true,
@@ -8869,7 +9170,7 @@ var route22 = defineJsonRoute({
8869
9170
  });
8870
9171
 
8871
9172
  // ../argus-watcher/dist/http/routes/postCodeRead.js
8872
- var route23 = defineJsonRoute({
9173
+ var route24 = defineJsonRoute({
8873
9174
  method: "POST",
8874
9175
  path: "/code/read",
8875
9176
  parseBody: true,
@@ -8894,7 +9195,7 @@ var route23 = defineJsonRoute({
8894
9195
  });
8895
9196
 
8896
9197
  // ../argus-watcher/dist/http/routes/postCodeGrep.js
8897
- var route24 = defineJsonRoute({
9198
+ var route25 = defineJsonRoute({
8898
9199
  method: "POST",
8899
9200
  path: "/code/grep",
8900
9201
  parseBody: true,
@@ -8915,7 +9216,7 @@ var route24 = defineJsonRoute({
8915
9216
  });
8916
9217
 
8917
9218
  // ../argus-watcher/dist/http/routes/postCodeEdit.js
8918
- var route25 = defineJsonRoute({
9219
+ var route26 = defineJsonRoute({
8919
9220
  method: "POST",
8920
9221
  path: "/code/edit",
8921
9222
  parseBody: true,
@@ -9272,7 +9573,7 @@ var fillResolvedNodes = async (session, handles, value) => {
9272
9573
  return handles.length;
9273
9574
  };
9274
9575
  // ../argus-watcher/dist/http/routes/postDomTree.js
9275
- var route26 = defineJsonRoute({
9576
+ var route27 = defineJsonRoute({
9276
9577
  method: "POST",
9277
9578
  path: "/dom/tree",
9278
9579
  parseBody: true,
@@ -9295,7 +9596,7 @@ var route26 = defineJsonRoute({
9295
9596
  });
9296
9597
 
9297
9598
  // ../argus-watcher/dist/http/routes/postDomInfo.js
9298
- var route27 = defineJsonRoute({
9599
+ var route28 = defineJsonRoute({
9299
9600
  method: "POST",
9300
9601
  path: "/dom/info",
9301
9602
  parseBody: true,
@@ -9580,7 +9881,7 @@ var defineDomTargetRoute = (input) => defineJsonRoute({
9580
9881
  });
9581
9882
 
9582
9883
  // ../argus-watcher/dist/http/routes/postDomHover.js
9583
- var route28 = defineDomTargetRoute({
9884
+ var route29 = defineDomTargetRoute({
9584
9885
  path: "/dom/hover",
9585
9886
  endpoint: "dom/hover",
9586
9887
  action: "hover",
@@ -9591,7 +9892,7 @@ var route28 = defineDomTargetRoute({
9591
9892
  });
9592
9893
 
9593
9894
  // ../argus-watcher/dist/http/routes/postDomClick.js
9594
- var route29 = defineJsonRoute({
9895
+ var route30 = defineJsonRoute({
9595
9896
  method: "POST",
9596
9897
  path: "/dom/click",
9597
9898
  bodySchema: domClickRequestSchema,
@@ -9639,7 +9940,7 @@ var route29 = defineJsonRoute({
9639
9940
  });
9640
9941
 
9641
9942
  // ../argus-watcher/dist/http/routes/postDomDrag.js
9642
- var route30 = defineJsonRoute({
9943
+ var route31 = defineJsonRoute({
9643
9944
  method: "POST",
9644
9945
  path: "/dom/drag",
9645
9946
  bodySchema: domDragRequestSchema,
@@ -9856,7 +10157,7 @@ var dispatchKeydown = async (session, options) => {
9856
10157
  };
9857
10158
 
9858
10159
  // ../argus-watcher/dist/http/routes/postDomKeydown.js
9859
- var route31 = defineJsonRoute({
10160
+ var route32 = defineJsonRoute({
9860
10161
  method: "POST",
9861
10162
  path: "/dom/keydown",
9862
10163
  parseBody: true,
@@ -9906,7 +10207,7 @@ var route31 = defineJsonRoute({
9906
10207
 
9907
10208
  // ../argus-watcher/dist/http/routes/postDomAdd.js
9908
10209
  var validPositions = ["beforebegin", "afterbegin", "beforeend", "afterend"];
9909
- var route32 = defineJsonRoute({
10210
+ var route33 = defineJsonRoute({
9910
10211
  method: "POST",
9911
10212
  path: "/dom/add",
9912
10213
  parseBody: true,
@@ -9995,7 +10296,7 @@ var selectDomAddNodeIds = (allNodeIds, options) => {
9995
10296
  };
9996
10297
 
9997
10298
  // ../argus-watcher/dist/http/routes/postDomRemove.js
9998
- var route33 = defineJsonRoute({
10299
+ var route34 = defineJsonRoute({
9999
10300
  method: "POST",
10000
10301
  path: "/dom/remove",
10001
10302
  parseBody: true,
@@ -10017,7 +10318,7 @@ var route33 = defineJsonRoute({
10017
10318
 
10018
10319
  // ../argus-watcher/dist/http/routes/postDomModify.js
10019
10320
  var validTypes = ["attr", "class", "style", "text", "html"];
10020
- var route34 = defineJsonRoute({
10321
+ var route35 = defineJsonRoute({
10021
10322
  method: "POST",
10022
10323
  path: "/dom/modify",
10023
10324
  parseBody: true,
@@ -10048,7 +10349,7 @@ var route34 = defineJsonRoute({
10048
10349
  });
10049
10350
 
10050
10351
  // ../argus-watcher/dist/http/routes/postDomSetFile.js
10051
- var route35 = defineJsonRoute({
10352
+ var route36 = defineJsonRoute({
10052
10353
  method: "POST",
10053
10354
  path: "/dom/set-file",
10054
10355
  parseBody: true,
@@ -10086,7 +10387,7 @@ var route35 = defineJsonRoute({
10086
10387
  });
10087
10388
 
10088
10389
  // ../argus-watcher/dist/http/routes/postDomFocus.js
10089
- var route36 = defineDomTargetRoute({
10390
+ var route37 = defineDomTargetRoute({
10090
10391
  path: "/dom/focus",
10091
10392
  endpoint: "dom/focus",
10092
10393
  action: "focus",
@@ -10097,7 +10398,7 @@ var route36 = defineDomTargetRoute({
10097
10398
  });
10098
10399
 
10099
10400
  // ../argus-watcher/dist/http/routes/postDomFill.js
10100
- var route37 = defineDomTargetRoute({
10401
+ var route38 = defineDomTargetRoute({
10101
10402
  path: "/dom/fill",
10102
10403
  endpoint: "dom/fill",
10103
10404
  action: "fill",
@@ -10118,7 +10419,7 @@ var route37 = defineDomTargetRoute({
10118
10419
  });
10119
10420
 
10120
10421
  // ../argus-watcher/dist/http/routes/postDomScroll.js
10121
- var route38 = defineJsonRoute({
10422
+ var route39 = defineJsonRoute({
10122
10423
  method: "POST",
10123
10424
  path: "/dom/scroll",
10124
10425
  parseBody: true,
@@ -10177,7 +10478,7 @@ var getViewportSize = async (session) => {
10177
10478
  };
10178
10479
 
10179
10480
  // ../argus-watcher/dist/http/routes/postDomScrollTo.js
10180
- var route39 = defineJsonRoute({
10481
+ var route40 = defineJsonRoute({
10181
10482
  method: "POST",
10182
10483
  path: "/dom/scroll-to",
10183
10484
  parseBody: true,
@@ -10224,7 +10525,7 @@ var route39 = defineJsonRoute({
10224
10525
  });
10225
10526
 
10226
10527
  // ../argus-watcher/dist/http/routes/getEmulation.js
10227
- var route40 = defineJsonRoute({
10528
+ var route41 = defineJsonRoute({
10228
10529
  method: "GET",
10229
10530
  path: "/emulation",
10230
10531
  endpoint: "emulation",
@@ -10233,7 +10534,7 @@ var route40 = defineJsonRoute({
10233
10534
 
10234
10535
  // ../argus-watcher/dist/http/routes/postEmulation.js
10235
10536
  var validActions = ["set", "clear"];
10236
- var route41 = defineJsonRoute({
10537
+ var route42 = defineJsonRoute({
10237
10538
  method: "POST",
10238
10539
  path: "/emulation",
10239
10540
  parseBody: true,
@@ -10292,7 +10593,7 @@ function validateEmulationRequest(payload) {
10292
10593
  }
10293
10594
 
10294
10595
  // ../argus-watcher/dist/http/routes/getThrottle.js
10295
- var route42 = defineJsonRoute({
10596
+ var route43 = defineJsonRoute({
10296
10597
  method: "GET",
10297
10598
  path: "/throttle",
10298
10599
  endpoint: "throttle",
@@ -10301,7 +10602,7 @@ var route42 = defineJsonRoute({
10301
10602
 
10302
10603
  // ../argus-watcher/dist/http/routes/postThrottle.js
10303
10604
  var validActions2 = ["set", "clear"];
10304
- var route43 = defineJsonRoute({
10605
+ var route44 = defineJsonRoute({
10305
10606
  method: "POST",
10306
10607
  path: "/throttle",
10307
10608
  parseBody: true,
@@ -10329,7 +10630,7 @@ var route43 = defineJsonRoute({
10329
10630
  });
10330
10631
 
10331
10632
  // ../argus-watcher/dist/http/routes/getDialog.js
10332
- var route44 = defineJsonRoute({
10633
+ var route45 = defineJsonRoute({
10333
10634
  method: "GET",
10334
10635
  path: "/dialog",
10335
10636
  endpoint: "dialog/status",
@@ -10337,7 +10638,7 @@ var route44 = defineJsonRoute({
10337
10638
  });
10338
10639
 
10339
10640
  // ../argus-watcher/dist/http/routes/postDialog.js
10340
- var route45 = defineJsonRoute({
10641
+ var route46 = defineJsonRoute({
10341
10642
  method: "POST",
10342
10643
  path: "/dialog",
10343
10644
  parseBody: true,
@@ -10376,7 +10677,7 @@ var route45 = defineJsonRoute({
10376
10677
  });
10377
10678
 
10378
10679
  // ../argus-watcher/dist/http/routes/postVisibility.js
10379
- var route46 = defineJsonRoute({
10680
+ var route47 = defineJsonRoute({
10380
10681
  method: "POST",
10381
10682
  path: "/visibility",
10382
10683
  parseBody: true,
@@ -10493,7 +10794,7 @@ function validateStoragePayload(payload) {
10493
10794
  }
10494
10795
 
10495
10796
  // ../argus-watcher/dist/http/routes/postReload.js
10496
- var route47 = defineJsonRoute({
10797
+ var route48 = defineJsonRoute({
10497
10798
  method: "POST",
10498
10799
  path: "/reload",
10499
10800
  parseBody: true,
@@ -10507,7 +10808,7 @@ var route47 = defineJsonRoute({
10507
10808
  });
10508
10809
 
10509
10810
  // ../argus-watcher/dist/http/routes/postShutdown.js
10510
- var route48 = defineJsonRoute({
10811
+ var route49 = defineJsonRoute({
10511
10812
  method: "POST",
10512
10813
  path: "/shutdown",
10513
10814
  endpoint: "shutdown",
@@ -10522,7 +10823,7 @@ var route48 = defineJsonRoute({
10522
10823
  });
10523
10824
 
10524
10825
  // ../argus-watcher/dist/http/routes/getExtensionTabs.js
10525
- var route49 = defineJsonRoute({
10826
+ var route50 = defineJsonRoute({
10526
10827
  method: "GET",
10527
10828
  path: "/tabs",
10528
10829
  extensionOnly: true,
@@ -10543,7 +10844,7 @@ var route49 = defineJsonRoute({
10543
10844
  });
10544
10845
 
10545
10846
  // ../argus-watcher/dist/http/routes/getExtensionDiagnostics.js
10546
- var route50 = defineJsonRoute({
10847
+ var route51 = defineJsonRoute({
10547
10848
  method: "GET",
10548
10849
  path: "/extension/diagnostics",
10549
10850
  extensionOnly: true,
@@ -10557,7 +10858,7 @@ var route50 = defineJsonRoute({
10557
10858
  });
10558
10859
 
10559
10860
  // ../argus-watcher/dist/http/routes/getTargets.js
10560
- var route51 = defineJsonRoute({
10861
+ var route52 = defineJsonRoute({
10561
10862
  method: "GET",
10562
10863
  path: "/targets",
10563
10864
  extensionOnly: true,
@@ -10572,7 +10873,7 @@ var route51 = defineJsonRoute({
10572
10873
  });
10573
10874
 
10574
10875
  // ../argus-watcher/dist/http/routes/postAttach.js
10575
- var route52 = defineJsonRoute({
10876
+ var route53 = defineJsonRoute({
10576
10877
  method: "POST",
10577
10878
  path: "/attach",
10578
10879
  parseBody: true,
@@ -10595,7 +10896,7 @@ var route52 = defineJsonRoute({
10595
10896
  });
10596
10897
 
10597
10898
  // ../argus-watcher/dist/http/routes/postDetach.js
10598
- var route53 = defineJsonRoute({
10899
+ var route54 = defineJsonRoute({
10599
10900
  method: "POST",
10600
10901
  path: "/detach",
10601
10902
  parseBody: true,
@@ -10621,8 +10922,8 @@ var route53 = defineJsonRoute({
10621
10922
  var watcherRoutes = [
10622
10923
  route,
10623
10924
  route3,
10624
- route2,
10625
10925
  route4,
10926
+ route2,
10626
10927
  route5,
10627
10928
  route6,
10628
10929
  route7,
@@ -10632,22 +10933,22 @@ var watcherRoutes = [
10632
10933
  route11,
10633
10934
  route12,
10634
10935
  route13,
10635
- ...netMockRoutes,
10636
10936
  route14,
10937
+ ...netMockRoutes,
10938
+ route15,
10637
10939
  cookieGetRoute,
10638
10940
  cookieSetRoute,
10639
10941
  cookieDeleteRoute,
10640
10942
  cookieClearRoute,
10641
- route15,
10642
10943
  route16,
10643
10944
  route17,
10644
10945
  route18,
10645
10946
  route19,
10646
- ...recordRoutes,
10647
10947
  route20,
10948
+ ...recordRoutes,
10648
10949
  route21,
10649
- ...locateRoutes,
10650
10950
  route22,
10951
+ ...locateRoutes,
10651
10952
  route23,
10652
10953
  route24,
10653
10954
  route25,
@@ -10672,18 +10973,19 @@ var watcherRoutes = [
10672
10973
  route44,
10673
10974
  route45,
10674
10975
  route46,
10675
- ...storageRoutes,
10676
10976
  route47,
10977
+ ...storageRoutes,
10677
10978
  route48,
10678
10979
  route49,
10679
10980
  route50,
10680
10981
  route51,
10681
10982
  route52,
10682
- route53
10983
+ route53,
10984
+ route54
10683
10985
  ];
10684
10986
 
10685
10987
  // ../argus-watcher/dist/http/router.js
10686
- var routes = new Map(watcherRoutes.map((route54) => [`${route54.method} ${route54.path}`, route54]));
10988
+ var routes = new Map(watcherRoutes.map((route55) => [`${route55.method} ${route55.path}`, route55]));
10687
10989
  var dispatch = (req, res, url, ctx) => {
10688
10990
  const entry = routes.get(`${req.method} ${url.pathname}`);
10689
10991
  if (!entry || entry.extensionOnly && !ctx.sourceHandle) {
@@ -11347,100 +11649,6 @@ var formatWatcherError = (error) => {
11347
11649
  import os3 from "node:os";
11348
11650
  import path7 from "node:path";
11349
11651
 
11350
- // ../argus-watcher/dist/buffer/LogBuffer.js
11351
- class LogBuffer {
11352
- maxSize;
11353
- events = [];
11354
- nextId = 1;
11355
- waiters = [];
11356
- constructor(maxSize) {
11357
- this.maxSize = maxSize;
11358
- }
11359
- add(event) {
11360
- const entry = {
11361
- ...event,
11362
- id: this.nextId++
11363
- };
11364
- this.events.push(entry);
11365
- this.trim();
11366
- this.flushWaiters();
11367
- return entry;
11368
- }
11369
- listAfter(after, filters, limit) {
11370
- const filtered = this.events.filter((event) => event.id > after && matchesFilters(event, filters));
11371
- return filtered.slice(0, limit);
11372
- }
11373
- getCursor() {
11374
- return this.nextId - 1;
11375
- }
11376
- waitForAfter(after, filters, limit, timeoutMs) {
11377
- const immediate = this.listAfter(after, filters, limit);
11378
- if (immediate.length > 0) {
11379
- return Promise.resolve(immediate);
11380
- }
11381
- return new Promise((resolve) => {
11382
- const timer = setTimeout(() => {
11383
- this.waiters = this.waiters.filter((waiter) => waiter.timer !== timer);
11384
- resolve([]);
11385
- }, timeoutMs);
11386
- this.waiters.push({ after, filters, limit, resolve, timer });
11387
- });
11388
- }
11389
- getStats() {
11390
- if (this.events.length === 0) {
11391
- return { size: this.maxSize, count: 0, minId: null, maxId: null };
11392
- }
11393
- return {
11394
- size: this.maxSize,
11395
- count: this.events.length,
11396
- minId: this.events[0]?.id ?? null,
11397
- maxId: this.events[this.events.length - 1]?.id ?? null
11398
- };
11399
- }
11400
- trim() {
11401
- if (this.events.length <= this.maxSize) {
11402
- return;
11403
- }
11404
- this.events = this.events.slice(this.events.length - this.maxSize);
11405
- }
11406
- flushWaiters() {
11407
- if (this.waiters.length === 0) {
11408
- return;
11409
- }
11410
- const remaining = [];
11411
- for (const waiter of this.waiters) {
11412
- const events = this.listAfter(waiter.after, waiter.filters, waiter.limit);
11413
- if (events.length > 0) {
11414
- clearTimeout(waiter.timer);
11415
- waiter.resolve(events);
11416
- continue;
11417
- }
11418
- remaining.push(waiter);
11419
- }
11420
- this.waiters = remaining;
11421
- }
11422
- }
11423
- var matchesFilters = (event, filters) => {
11424
- if (filters.sinceTs && event.ts < filters.sinceTs) {
11425
- return false;
11426
- }
11427
- if (filters.levels && filters.levels.length > 0 && !filters.levels.includes(event.level)) {
11428
- return false;
11429
- }
11430
- if (filters.source && !event.source.toLowerCase().includes(filters.source.toLowerCase())) {
11431
- return false;
11432
- }
11433
- if (filters.match && filters.match.length > 0) {
11434
- for (const pattern of filters.match) {
11435
- if (pattern.test(event.text)) {
11436
- return true;
11437
- }
11438
- }
11439
- return false;
11440
- }
11441
- return true;
11442
- };
11443
-
11444
11652
  // ../argus-watcher/dist/buffer/NetBuffer.js
11445
11653
  class NetBuffer {
11446
11654
  maxSize;
@@ -20030,9 +20238,11 @@ var previewLogEvent = (event) => ({
20030
20238
 
20031
20239
  // dist/watchers/queryParams.js
20032
20240
  var appendAfterLimitParams = (params, options) => {
20033
- const after = parseNumber(options.after);
20034
- if (after != null) {
20035
- params.set("after", String(after));
20241
+ if (options.after?.trim()) {
20242
+ params.set("after", options.after.trim());
20243
+ }
20244
+ if (options.sinceEpoch?.trim()) {
20245
+ params.set("sinceEpoch", options.sinceEpoch.trim());
20036
20246
  }
20037
20247
  const limit = parseNumber(options.limit);
20038
20248
  if (limit != null) {
@@ -20171,7 +20381,11 @@ var appendRepeatedParams = (params, key, values) => {
20171
20381
  // dist/commands/logs.js
20172
20382
  var runLogCursor = defineWatcherCommand({
20173
20383
  build: () => ({ path: "/logs/cursor", timeoutMs: 5000 }),
20174
- formatHuman: (response, { output }) => output.writeHuman(String(response.cursor))
20384
+ formatHuman: (response, { output }) => output.writeHuman(response.cursor)
20385
+ });
20386
+ var runLogEpoch = defineWatcherCommand({
20387
+ build: () => ({ path: "/logs/epoch", timeoutMs: 5000 }),
20388
+ formatHuman: (response, { output }) => output.writeHuman(response.epoch)
20175
20389
  });
20176
20390
  var runLogs = defineWatcherCommand({
20177
20391
  isJson: (options) => options.jsonFull === true,
@@ -20214,7 +20428,14 @@ var runTail = async (id, options) => {
20214
20428
  if (!resolved)
20215
20429
  return;
20216
20430
  const { watcher } = resolved;
20217
- let after = parseNumber(options.after) ?? 0;
20431
+ const afterEpoch = options.after?.trim() || undefined;
20432
+ const sinceEpoch = options.sinceEpoch?.trim() || undefined;
20433
+ if (afterEpoch && sinceEpoch) {
20434
+ output.writeWarn("Use either --after or --since-epoch, not both.");
20435
+ process.exitCode = 2;
20436
+ return;
20437
+ }
20438
+ let position = afterEpoch ?? sinceEpoch;
20218
20439
  const timeoutMs = parseNumber(options.timeout) ?? 25000;
20219
20440
  const limit = parseNumber(options.limit);
20220
20441
  let running = true;
@@ -20225,7 +20446,9 @@ var runTail = async (id, options) => {
20225
20446
  process.on("SIGTERM", stop);
20226
20447
  while (running) {
20227
20448
  const params = new URLSearchParams;
20228
- params.set("after", String(after));
20449
+ if (position) {
20450
+ params.set(sinceEpoch ? "sinceEpoch" : "after", position);
20451
+ }
20229
20452
  params.set("timeoutMs", String(timeoutMs));
20230
20453
  if (limit != null) {
20231
20454
  params.set("limit", String(limit));
@@ -20245,25 +20468,23 @@ var runTail = async (id, options) => {
20245
20468
  process.exitCode = 1;
20246
20469
  return;
20247
20470
  }
20248
- if (response.events.length > 0) {
20249
- for (const event of response.events) {
20250
- if (options.jsonFull) {
20251
- output.writeJsonLine({ watcher: watcher.id, event });
20252
- continue;
20253
- }
20254
- if (options.json) {
20255
- const previewEvent = previewLogEvent(event);
20256
- output.writeJsonLine({ watcher: watcher.id, event: previewEvent });
20257
- continue;
20258
- }
20259
- output.writeHuman(formatLogEvent(event, {
20260
- includeTimestamps: watcher.includeTimestamps
20261
- }));
20262
- }
20471
+ for (const event of response.events) {
20472
+ writeTailEvent(event, watcher, options, output);
20263
20473
  }
20264
- after = response.nextAfter;
20474
+ position = response.nextCursor;
20265
20475
  }
20266
20476
  };
20477
+ var writeTailEvent = (event, watcher, options, output) => {
20478
+ if (options.jsonFull) {
20479
+ output.writeJsonLine({ watcher: watcher.id, event });
20480
+ return;
20481
+ }
20482
+ if (options.json) {
20483
+ output.writeJsonLine({ watcher: watcher.id, event: previewLogEvent(event) });
20484
+ return;
20485
+ }
20486
+ output.writeHuman(formatLogEvent(event, { includeTimestamps: watcher.includeTimestamps }));
20487
+ };
20267
20488
 
20268
20489
  // dist/cli/register/logsCommands.js
20269
20490
  var sharedFilterOptions = [
@@ -20290,7 +20511,8 @@ var logsCommands = [
20290
20511
  options: [
20291
20512
  ...sharedFilterOptions,
20292
20513
  { flags: "--since <duration>", description: "Filter by time window (e.g. 10m, 2h, 30s)" },
20293
- { flags: "--after <id>", description: "Only return events after this id" },
20514
+ { flags: "--after <cursor>", description: "Only return events after this opaque cursor" },
20515
+ { flags: "--since-epoch <epoch>", description: "Only return events after this opaque epoch" },
20294
20516
  { flags: "--limit <count>", description: "Maximum number of events" },
20295
20517
  { flags: "--json", description: "Output bounded JSON preview for automation" },
20296
20518
  { flags: "--json-full", description: "Output full JSON (can be very large)" }
@@ -20302,6 +20524,16 @@ var logsCommands = [
20302
20524
  await runLogs(id, options);
20303
20525
  },
20304
20526
  subcommands: [
20527
+ {
20528
+ name: "epoch",
20529
+ description: "Return an opaque watcher-session log epoch without downloading events",
20530
+ arguments: [{ flags: "[id]", description: "Watcher id to query" }],
20531
+ options: [{ flags: "--json", description: "Output one JSON document for automation" }],
20532
+ examples: ["argus logs epoch app", "argus logs epoch app --json"],
20533
+ action: async (id, options, command) => {
20534
+ await runLogEpoch(id, command.optsWithGlobals?.() ?? options);
20535
+ }
20536
+ },
20305
20537
  {
20306
20538
  name: "cursor",
20307
20539
  description: "Return the current log cursor without downloading events",
@@ -20318,7 +20550,8 @@ var logsCommands = [
20318
20550
  arguments: [{ flags: "[id]", description: "Watcher id to follow" }],
20319
20551
  options: [
20320
20552
  ...sharedFilterOptions,
20321
- { flags: "--after <id>", description: "Start after this event id" },
20553
+ { flags: "--after <cursor>", description: "Start after this opaque cursor" },
20554
+ { flags: "--since-epoch <epoch>", description: "Start after this opaque epoch" },
20322
20555
  { flags: "--limit <count>", description: "Maximum number of events per poll" },
20323
20556
  { flags: "--timeout <ms>", description: "Long-poll timeout in milliseconds" },
20324
20557
  { flags: "--json", description: "Output bounded newline-delimited JSON events" },