@turns/dev 0.0.23 → 0.0.24
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 +7 -0
- package/dist/harness-registry.js +1 -1
- package/dist/index.js +7 -6
- package/dist/logging.js +14 -0
- package/dist/path-resolution.js +2 -1
- package/dist/queue-worker.js +11 -10
- package/dist/upload-sessions.js +29 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,13 @@ On macOS, credentials and daemon state live under
|
|
|
69
69
|
LaunchAgent `~/Library/LaunchAgents/dev.turns.cli.plist`. Its output is written
|
|
70
70
|
to `~/Library/Application Support/turns/daemon.log`.
|
|
71
71
|
|
|
72
|
+
Daemon runtime logs (including reconnects, upload progress, and errors) prefix
|
|
73
|
+
each line with a UTC ISO 8601 timestamp, for example
|
|
74
|
+
`[2026-10-19T09:12:34.567Z] Reconnected to wss://turns.dev/api/socket ...`.
|
|
75
|
+
Foreground uploads use the same timestamped runtime logs. Multiline messages
|
|
76
|
+
share one timestamp and retain their indentation; stdout and stderr remain
|
|
77
|
+
separate. Help, version, and service-management output keep their plain format.
|
|
78
|
+
|
|
72
79
|
The credential file contains a seven-day access token and a signed refresh
|
|
73
80
|
token. The CLI refreshes the access token at least once every 24 hours. A new
|
|
74
81
|
machine opens a one-time turns.dev URL, waits on an authentication WebSocket,
|
package/dist/harness-registry.js
CHANGED
|
@@ -372,7 +372,7 @@ export function processInvocation({ harness, sessionId, transcriptPath, cwd, pro
|
|
|
372
372
|
sessionId,
|
|
373
373
|
...(!createSession ? ['--allow-workspace-switch'] : []),
|
|
374
374
|
...(model && model !== 'auto' ? ['--model', model] : []),
|
|
375
|
-
...(thinkingLevel ? ['--reasoning-effort', thinkingLevel
|
|
375
|
+
...(thinkingLevel ? ['--reasoning-effort', thinkingLevel] : []),
|
|
376
376
|
prompt,
|
|
377
377
|
],
|
|
378
378
|
};
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
import { applicationPaths, authenticate, CREDENTIAL_REFRESH_INTERVAL_MS, credentialsNeedRefresh, exchangeLegacyToken, normalizeOrigin, readCredentials, recoverRejectedCredentials, refreshCredentials, removeCredentials, uploadSocketUrl, writeCredentials, } from "./auth.js";
|
|
7
7
|
import { daemonStatus, installDaemon, uninstallDaemon } from "./daemon.js";
|
|
8
8
|
import { normalizeAllowedDirectories } from "./directory-policy.js";
|
|
9
|
+
import { logError, logInfo } from "./logging.js";
|
|
9
10
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const CLI_DIRECTORY = path.basename(HERE) === 'dist' ? path.dirname(HERE) : HERE;
|
|
11
12
|
const pkg = JSON.parse(fs.readFileSync(path.join(CLI_DIRECTORY, 'package.json'), 'utf8'));
|
|
@@ -50,11 +51,11 @@ export async function migrateLegacyCredentials(paths, origin, { legacyTokenFile
|
|
|
50
51
|
const credentials = await exchangeLegacyToken(normalizeOrigin(origin || 'https://turns.dev'), legacyToken);
|
|
51
52
|
writeCredentials(credentials, paths);
|
|
52
53
|
fs.unlinkSync(legacyTokenFile);
|
|
53
|
-
|
|
54
|
+
logInfo(`Upgraded the existing uploader credential for @${credentials.user.login}.\n`);
|
|
54
55
|
return credentials;
|
|
55
56
|
}
|
|
56
57
|
catch (error) {
|
|
57
|
-
|
|
58
|
+
logError(`Could not upgrade the existing uploader credential: ${error.message}\n`);
|
|
58
59
|
return null;
|
|
59
60
|
}
|
|
60
61
|
}
|
|
@@ -166,7 +167,7 @@ function credentialProvider(initial, { paths, interactive }) {
|
|
|
166
167
|
};
|
|
167
168
|
const timer = setInterval(() => {
|
|
168
169
|
refreshIfDue().catch((error) => {
|
|
169
|
-
|
|
170
|
+
logError(`Turns credential refresh failed: ${error.message}; retrying later.\n`);
|
|
170
171
|
});
|
|
171
172
|
}, Math.min(CREDENTIAL_REFRESH_INTERVAL_MS, 60 * 60_000));
|
|
172
173
|
timer.unref?.();
|
|
@@ -282,7 +283,7 @@ async function main(rawArguments = process.argv.slice(2)) {
|
|
|
282
283
|
credentials = await authenticate({ origin: parsed.origin, paths, interactive: false });
|
|
283
284
|
}
|
|
284
285
|
if (allowedDirectories.length) {
|
|
285
|
-
|
|
286
|
+
logInfo(`Daemon directory allowlist:\n${allowedDirectories.map((value) => ` ${value}`).join('\n')}\n`);
|
|
286
287
|
}
|
|
287
288
|
await runUpload(credentials, {
|
|
288
289
|
paths,
|
|
@@ -312,7 +313,7 @@ async function main(rawArguments = process.argv.slice(2)) {
|
|
|
312
313
|
const selected = scopedWatchOptions(command === 'upload' ? argv[1] || process.cwd() : process.cwd(), {
|
|
313
314
|
once: parsed.once,
|
|
314
315
|
});
|
|
315
|
-
|
|
316
|
+
logInfo(selected.watch
|
|
316
317
|
? `Watching sessions in ${selected.directory} as @${credentials.user.login}. Press Ctrl-C to stop.\n`
|
|
317
318
|
: `Uploading sessions in ${selected.directory} as @${credentials.user.login}.\n`);
|
|
318
319
|
await runUpload(credentials, {
|
|
@@ -330,7 +331,7 @@ try {
|
|
|
330
331
|
catch { }
|
|
331
332
|
if (invokedAsMain) {
|
|
332
333
|
main().catch((error) => {
|
|
333
|
-
|
|
334
|
+
logError(`turns: ${error?.message || error}\n`);
|
|
334
335
|
process.exitCode = 1;
|
|
335
336
|
});
|
|
336
337
|
}
|
package/dist/logging.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
/** Callers supply complete log messages, including their trailing newline. */
|
|
3
|
+
export function formatLogMessage(message, now = new Date()) {
|
|
4
|
+
const prefix = `[${now.toISOString()}] `;
|
|
5
|
+
// Timestamp every physical line, but do not add a dangling prefix after the
|
|
6
|
+
// final newline. Keep indentation and use one time for the whole message.
|
|
7
|
+
return message.replace(/^(?=[\s\S])/gmu, prefix);
|
|
8
|
+
}
|
|
9
|
+
export function logInfo(message) {
|
|
10
|
+
return process.stdout.write(formatLogMessage(message));
|
|
11
|
+
}
|
|
12
|
+
export function logError(message) {
|
|
13
|
+
return process.stderr.write(formatLogMessage(message));
|
|
14
|
+
}
|
package/dist/path-resolution.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { Transform } from 'node:stream';
|
|
5
|
+
import { logError } from "./logging.js";
|
|
5
6
|
/**
|
|
6
7
|
* Turning the paths an agent writes into things a reader can open.
|
|
7
8
|
*
|
|
@@ -526,7 +527,7 @@ export function createPathResolver({ options, machine, baseUrl, appendResolution
|
|
|
526
527
|
}
|
|
527
528
|
const chain = (chains.get(sessionId) || Promise.resolve()).then(() => resolveBatch(sessionId, workingDirectory, fresh).catch((error) => {
|
|
528
529
|
const message = error?.message || String(error);
|
|
529
|
-
|
|
530
|
+
logError(`Path resolution failed: ${redactor ? redactor.redactText(message) : message}\n`);
|
|
530
531
|
}));
|
|
531
532
|
chains.set(sessionId, chain);
|
|
532
533
|
return chain;
|
package/dist/queue-worker.js
CHANGED
|
@@ -4,6 +4,7 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { Readable } from 'node:stream';
|
|
6
6
|
import { pipeline } from 'node:stream/promises';
|
|
7
|
+
import { logError } from "./logging.js";
|
|
7
8
|
import { anchorId, connectExistingAgentAnchors, launchAgentAnchor, waitForAgentTurn } from "./agent-anchor-client.js";
|
|
8
9
|
import { createCodexUpdateManager } from "./codex-update.js";
|
|
9
10
|
import { isAsyncQuestionId } from "./codex-async-question-recovery.js";
|
|
@@ -384,7 +385,7 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
384
385
|
catch (error) {
|
|
385
386
|
const message = String(error?.message || error);
|
|
386
387
|
state.failures = (state.failures || 0) + 1;
|
|
387
|
-
|
|
388
|
+
logError(`Runtime event append failed for ${sessionId}: ${message}\n`);
|
|
388
389
|
// Socket failures are retried inside request(). Keep valid anchor
|
|
389
390
|
// events across other transient Worker/R2 failures too. Only discard
|
|
390
391
|
// a batch when the session is gone or the internally constructed row
|
|
@@ -399,7 +400,7 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
399
400
|
state.failures = 0;
|
|
400
401
|
}
|
|
401
402
|
else if (state.failures >= MAX_RUNTIME_FLUSH_FAILURES) {
|
|
402
|
-
|
|
403
|
+
logError(`Dropping ${events.length} undeliverable runtime events for ${sessionId} after ` +
|
|
403
404
|
`${state.failures} attempts: ${message}\n`);
|
|
404
405
|
state.events.splice(0, events.length);
|
|
405
406
|
state.failures = 0;
|
|
@@ -504,7 +505,7 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
504
505
|
if (state === 'idle')
|
|
505
506
|
await flushRuntime(sessionId);
|
|
506
507
|
await reportHarness(sessionId, state, entry);
|
|
507
|
-
})().catch((error) =>
|
|
508
|
+
})().catch((error) => logError(`Harness status upload failed: ${error?.message || error}\n`));
|
|
508
509
|
}
|
|
509
510
|
if (message.type === 'live_event') {
|
|
510
511
|
const turnMessageId = String(message.messageId || entry.snapshot.currentTurn?.messageId || '');
|
|
@@ -633,7 +634,7 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
633
634
|
// Dispatch deliberately does not wait for that publish: the queue preserves
|
|
634
635
|
// order on its own, and a frame the Worker will not accept must not be able
|
|
635
636
|
// to hold a claimed prompt back from the agent.
|
|
636
|
-
void enqueueRuntime(sessionId, queuePromptBoundary(recovery?.entry || anchors.get(sessionId), claim), true).catch((error) =>
|
|
637
|
+
void enqueueRuntime(sessionId, queuePromptBoundary(recovery?.entry || anchors.get(sessionId), claim), true).catch((error) => logError(`Queue prompt boundary publish failed for ${sessionId}: ${error?.message || error}\n`));
|
|
637
638
|
let descriptor;
|
|
638
639
|
let agentSessionId = '';
|
|
639
640
|
let creating = false;
|
|
@@ -717,7 +718,7 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
717
718
|
void reportHarness(sessionId, 'stopped', entry).catch(() => { });
|
|
718
719
|
return;
|
|
719
720
|
}
|
|
720
|
-
void renewClaim().catch((error) =>
|
|
721
|
+
void renewClaim().catch((error) => logError(`Queue lease renewal failed: ${error?.message || error}\n`));
|
|
721
722
|
// Never renew lifecycle state from an old in-memory snapshot. The
|
|
722
723
|
// anchor may have disconnected since the claim began, which would
|
|
723
724
|
// otherwise resurrect a canonical stopped edge as running.
|
|
@@ -820,7 +821,7 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
820
821
|
queueMicrotask(() => void tryClaim(sessionId));
|
|
821
822
|
}
|
|
822
823
|
catch (error) {
|
|
823
|
-
|
|
824
|
+
logError(`Queue dispatch failed for ${sessionId}: ${error?.message || error}\n`);
|
|
824
825
|
const held = claims.get(sessionId);
|
|
825
826
|
stopClaimRenewal(held);
|
|
826
827
|
if (held?.entry && !held.entry.connection.closed) {
|
|
@@ -895,7 +896,7 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
895
896
|
await reportHarness(sessionId, 'stopped', held?.entry || entry).catch(() => { });
|
|
896
897
|
}
|
|
897
898
|
});
|
|
898
|
-
|
|
899
|
+
logError(`Anchor recovery failed for ${sessionId}: ${error?.message || error}\n`);
|
|
899
900
|
});
|
|
900
901
|
}
|
|
901
902
|
};
|
|
@@ -1334,7 +1335,7 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
1334
1335
|
// as their identity: a pane already showing them recognizes the
|
|
1335
1336
|
// refresh instead of being offered its own save as a new version.
|
|
1336
1337
|
contentEtag: String(edit.afterHash || ''),
|
|
1337
|
-
}).catch((error) =>
|
|
1338
|
+
}).catch((error) => logError(`Post-edit refresh failed for ${applied.path}: ${error?.message || error}\n`));
|
|
1338
1339
|
}
|
|
1339
1340
|
replyRpc.notify({
|
|
1340
1341
|
type: 'session.file.edit.result',
|
|
@@ -1420,7 +1421,7 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
1420
1421
|
void tryClaim(sessionId);
|
|
1421
1422
|
}
|
|
1422
1423
|
catch (error) {
|
|
1423
|
-
|
|
1424
|
+
logError(`Queue registration failed: ${error?.message || error}\n`);
|
|
1424
1425
|
}
|
|
1425
1426
|
finally {
|
|
1426
1427
|
registering.delete(control);
|
|
@@ -1439,7 +1440,7 @@ export async function watchQueuedPrompts({ rpc, options, machine, getDescriptors
|
|
|
1439
1440
|
void tryClaim(sessionId);
|
|
1440
1441
|
}
|
|
1441
1442
|
catch (error) {
|
|
1442
|
-
|
|
1443
|
+
logError(`Machine heartbeat failed: ${error?.message || error}\n`);
|
|
1443
1444
|
}
|
|
1444
1445
|
}
|
|
1445
1446
|
}
|
package/dist/upload-sessions.js
CHANGED
|
@@ -6,6 +6,7 @@ import path from 'node:path';
|
|
|
6
6
|
import process from 'node:process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import WebSocket from 'ws';
|
|
9
|
+
import { logError, logInfo } from "./logging.js";
|
|
9
10
|
import { streamDeepseekTrajectoryLines } from "./deepseek-trajectories.js";
|
|
10
11
|
import { normalizeAllowedDirectories, workingDirectoryIsAllowed } from "./directory-policy.js";
|
|
11
12
|
import { discoverGitAssociation } from "./git-association.js";
|
|
@@ -131,7 +132,7 @@ function reportingRedactor(tokens) {
|
|
|
131
132
|
return;
|
|
132
133
|
const rules = Object.fromEntries([...counts].sort((left, right) => right[1] - left[1]));
|
|
133
134
|
counts.clear();
|
|
134
|
-
|
|
135
|
+
logError(`Secret filtering applied (v${REDACTION_VERSION}): ${JSON.stringify(rules)}\n`);
|
|
135
136
|
};
|
|
136
137
|
const timer = setInterval(report, 60_000);
|
|
137
138
|
timer.unref?.();
|
|
@@ -690,7 +691,7 @@ export async function connectUploader(options, machine, reconnect = false) {
|
|
|
690
691
|
});
|
|
691
692
|
try {
|
|
692
693
|
const ready = await rpc.ready;
|
|
693
|
-
|
|
694
|
+
logInfo(`${reconnect ? 'Reconnected' : 'Connected'} to ${options.url} as @${ready.user.login} ` +
|
|
694
695
|
`from ${os.hostname()} (${machine})\n`);
|
|
695
696
|
return rpc;
|
|
696
697
|
}
|
|
@@ -700,14 +701,14 @@ export async function connectUploader(options, machine, reconnect = false) {
|
|
|
700
701
|
authenticationRecoveries += 1;
|
|
701
702
|
const credentials = await options.credentialProvider.recover();
|
|
702
703
|
options.token = credentials.accessToken;
|
|
703
|
-
|
|
704
|
+
logError('Uploader authentication was rejected; refreshed credentials.\n');
|
|
704
705
|
attempt -= 1;
|
|
705
706
|
continue;
|
|
706
707
|
}
|
|
707
708
|
if (!isSocketError(error) || attempt === MAX_SOCKET_RETRIES)
|
|
708
709
|
throw error;
|
|
709
710
|
const delay = reconnectDelay(attempt);
|
|
710
|
-
|
|
711
|
+
logError(` WebSocket connection failed: ${error.message}; retrying in ` +
|
|
711
712
|
`${(delay / 1000).toFixed(1)}s (${attempt + 1}/${MAX_SOCKET_RETRIES})\n`);
|
|
712
713
|
await wait(delay);
|
|
713
714
|
}
|
|
@@ -756,7 +757,7 @@ export class SharedUploaderConnection {
|
|
|
756
757
|
this.reconnectTimer = setTimeout(() => {
|
|
757
758
|
this.reconnectTimer = null;
|
|
758
759
|
void this.current().catch((error) => {
|
|
759
|
-
|
|
760
|
+
logError(`Shared uploader reconnect failed: ${error?.message || error}\n`);
|
|
760
761
|
this.scheduleReconnect();
|
|
761
762
|
});
|
|
762
763
|
}, 1_000);
|
|
@@ -921,7 +922,7 @@ async function uploadFile(rpc, machine, root, capture, redactor = DEFAULT_REDACT
|
|
|
921
922
|
}
|
|
922
923
|
nextRow = Number(acknowledgement.nextRow);
|
|
923
924
|
sent += batch.rows.length;
|
|
924
|
-
|
|
925
|
+
logInfo(` ${capture.relative}: ${nextRow}/${rows.length} rows · revision ${acknowledgement.revision}${acknowledgement.archived ? ' · archived' : ''}\n`);
|
|
925
926
|
}
|
|
926
927
|
return { rows: rows.length, sent, skipped: false };
|
|
927
928
|
}
|
|
@@ -1372,7 +1373,7 @@ async function reportExternalManagement(rpc, machine, descriptors, previous = ne
|
|
|
1372
1373
|
managedKeys = externallyManagedNativeDescriptorKeys(descriptors);
|
|
1373
1374
|
}
|
|
1374
1375
|
catch (error) {
|
|
1375
|
-
|
|
1376
|
+
logError(`External ownership scan failed: ${error?.message || error}\n`);
|
|
1376
1377
|
return { rpc, managed: null };
|
|
1377
1378
|
}
|
|
1378
1379
|
const observedAt = new Date().toISOString();
|
|
@@ -1391,7 +1392,7 @@ async function reportExternalManagement(rpc, machine, descriptors, previous = ne
|
|
|
1391
1392
|
});
|
|
1392
1393
|
}
|
|
1393
1394
|
catch (error) {
|
|
1394
|
-
|
|
1395
|
+
logError(`External ownership upload failed: ${error?.message || error}\n`);
|
|
1395
1396
|
return { managed: null, previous };
|
|
1396
1397
|
}
|
|
1397
1398
|
}
|
|
@@ -1408,11 +1409,11 @@ async function watchExternalManagement(rpc, machine, getDescriptors) {
|
|
|
1408
1409
|
previous = reported.previous;
|
|
1409
1410
|
if (reported.managed !== null && reported.managed !== lastManagedCount) {
|
|
1410
1411
|
lastManagedCount = reported.managed;
|
|
1411
|
-
|
|
1412
|
+
logInfo(`External ownership: ${reported.managed} managed session${reported.managed === 1 ? '' : 's'}\n`);
|
|
1412
1413
|
}
|
|
1413
1414
|
}
|
|
1414
1415
|
catch (error) {
|
|
1415
|
-
|
|
1416
|
+
logError(`External ownership watcher failed: ${error?.message || error}\n`);
|
|
1416
1417
|
}
|
|
1417
1418
|
await wait(NATIVE_WATCH_INTERVAL_MS);
|
|
1418
1419
|
}
|
|
@@ -1456,9 +1457,9 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1456
1457
|
const byHarness = new Map();
|
|
1457
1458
|
for (const session of initialSessions)
|
|
1458
1459
|
byHarness.set(session.harness, (byHarness.get(session.harness) || 0) + 1);
|
|
1459
|
-
|
|
1460
|
+
logInfo(`Native inventory: ${initialSessions.length} sessions across ${byHarness.size} harnesses\n`);
|
|
1460
1461
|
for (const [harness, count] of [...byHarness].sort())
|
|
1461
|
-
|
|
1462
|
+
logInfo(` ${harness}: ${count}\n`);
|
|
1462
1463
|
if (options.dryRun)
|
|
1463
1464
|
return;
|
|
1464
1465
|
const state = createNativeUploadState(options.nativeStateFile, machine);
|
|
@@ -1586,7 +1587,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1586
1587
|
stableSessionId: stableNativeSessionId,
|
|
1587
1588
|
redactor,
|
|
1588
1589
|
}).catch((error) => {
|
|
1589
|
-
|
|
1590
|
+
logError(`Queue worker stopped unexpectedly: ${error?.stack || error}\n`);
|
|
1590
1591
|
});
|
|
1591
1592
|
}
|
|
1592
1593
|
const totals = { current: 0, uploaded: 0, rows: 0, empty: 0, failed: 0 };
|
|
@@ -1623,7 +1624,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1623
1624
|
const message = error?.message || String(error);
|
|
1624
1625
|
if (message !== uploadProgressError) {
|
|
1625
1626
|
uploadProgressError = message;
|
|
1626
|
-
|
|
1627
|
+
logError(`Upload progress report failed: ${message}\n`);
|
|
1627
1628
|
}
|
|
1628
1629
|
}
|
|
1629
1630
|
};
|
|
@@ -1685,7 +1686,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1685
1686
|
cursor = {};
|
|
1686
1687
|
cursors.set(sessionId, cursor);
|
|
1687
1688
|
seen.delete(sessionId);
|
|
1688
|
-
|
|
1689
|
+
logInfo(` ${descriptor.harness} ${descriptor.sourcePath}: reset for ${redactionUpgrade ? 'secret-filtered' : 'full'} replay\n`);
|
|
1689
1690
|
}
|
|
1690
1691
|
result = await uploadNativeSession(rpc, machine, descriptor, cursor, (progress) => {
|
|
1691
1692
|
// Persist an explicitly incomplete fingerprint while a long replay
|
|
@@ -1693,7 +1694,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1693
1694
|
// but the empty fingerprint still schedules the unfinished tail.
|
|
1694
1695
|
state.set(sessionId, '', cursor, REDACTION_VERSION);
|
|
1695
1696
|
scheduleUploadProgress();
|
|
1696
|
-
|
|
1697
|
+
logInfo(` ${index + 1}/${changed.length} ${descriptor.harness}: ` +
|
|
1697
1698
|
`${progress.nextRow}/${progress.total} rows` +
|
|
1698
1699
|
`${progress.archived ? ' · archived' : ''}\n`);
|
|
1699
1700
|
}, redactor, {
|
|
@@ -1717,7 +1718,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1717
1718
|
failure = error;
|
|
1718
1719
|
break;
|
|
1719
1720
|
}
|
|
1720
|
-
|
|
1721
|
+
logError(` ${index + 1}/${changed.length} ${descriptor.harness}: ${error.message}; ` +
|
|
1721
1722
|
`reconnecting (${attempt + 1}/${MAX_SOCKET_RETRIES})\n`);
|
|
1722
1723
|
rpc.close();
|
|
1723
1724
|
await wait(reconnectDelay(attempt));
|
|
@@ -1734,7 +1735,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1734
1735
|
failures.delete(sessionId);
|
|
1735
1736
|
state.tombstone(sessionId);
|
|
1736
1737
|
totals.current += 1;
|
|
1737
|
-
|
|
1738
|
+
logInfo(` ${index + 1}/${changed.length} ${descriptor.harness}: deleted on turns.dev\n`);
|
|
1738
1739
|
scheduleUploadProgress();
|
|
1739
1740
|
continue;
|
|
1740
1741
|
}
|
|
@@ -1759,7 +1760,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1759
1760
|
totals.rows += result.sent;
|
|
1760
1761
|
}
|
|
1761
1762
|
if (!result.skipped || (index + 1) % 50 === 0 || index + 1 === changed.length) {
|
|
1762
|
-
|
|
1763
|
+
logInfo(` ${index + 1}/${changed.length} ${descriptor.harness} ${descriptor.sourcePath}: ` +
|
|
1763
1764
|
`${result.empty ? 'no conversation rows' : result.skipped ? `current (${result.rows} rows)` : `${result.sent} rows uploaded`}\n`);
|
|
1764
1765
|
}
|
|
1765
1766
|
scheduleUploadProgress();
|
|
@@ -1774,7 +1775,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1774
1775
|
attempts,
|
|
1775
1776
|
retryAt: Date.now() + nativeFailureRetryDelay(attempts),
|
|
1776
1777
|
});
|
|
1777
|
-
|
|
1778
|
+
logError(` ${index + 1}/${changed.length} ${descriptor.harness} ${descriptor.sourcePath}: ${error?.message || error}\n`);
|
|
1778
1779
|
scheduleUploadProgress();
|
|
1779
1780
|
}
|
|
1780
1781
|
}
|
|
@@ -1800,7 +1801,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1800
1801
|
rememberInventory(sessions);
|
|
1801
1802
|
const unsettled = changedSessions();
|
|
1802
1803
|
if (unsettled.length) {
|
|
1803
|
-
|
|
1804
|
+
logError(`${unsettled.length} native sessions kept changing; run \`turns\` without --once to follow them continuously.\n`);
|
|
1804
1805
|
}
|
|
1805
1806
|
break;
|
|
1806
1807
|
}
|
|
@@ -1816,9 +1817,9 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1816
1817
|
await reportUploadProgress(true);
|
|
1817
1818
|
rpc.close();
|
|
1818
1819
|
totals.failed = failures.size;
|
|
1819
|
-
|
|
1820
|
+
logInfo(`Native upload complete: ${JSON.stringify(totals)}\n`);
|
|
1820
1821
|
if (failures.size) {
|
|
1821
|
-
|
|
1822
|
+
logError(`${JSON.stringify({ failures: [...failures.values()] }, null, 2)}\n`);
|
|
1822
1823
|
process.exitCode = 1;
|
|
1823
1824
|
}
|
|
1824
1825
|
return;
|
|
@@ -1962,7 +1963,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1962
1963
|
shutdown();
|
|
1963
1964
|
process.exit(0);
|
|
1964
1965
|
});
|
|
1965
|
-
|
|
1966
|
+
logInfo(`Watching ${initialSessions.length} native sessions via ${nativeMonitor.count} directories; ` +
|
|
1966
1967
|
`polling known files every ${NATIVE_POLL_INTERVAL_MS / 1000}s and auditing every ` +
|
|
1967
1968
|
`${NATIVE_DISCOVERY_INTERVAL_MS / 1000}s…\n`);
|
|
1968
1969
|
let historicalSessionId = '';
|
|
@@ -1979,7 +1980,7 @@ async function uploadNative(options, machine, redactor = DEFAULT_REDACTOR) {
|
|
|
1979
1980
|
scheduleUploadProgress();
|
|
1980
1981
|
}
|
|
1981
1982
|
catch (error) {
|
|
1982
|
-
|
|
1983
|
+
logError(`Native inventory refresh failed: ${error?.message || error}\n`);
|
|
1983
1984
|
}
|
|
1984
1985
|
}
|
|
1985
1986
|
const changed = changedSessions();
|
|
@@ -2064,7 +2065,7 @@ export async function runUploader(argv = process.argv.slice(2), { credentialProv
|
|
|
2064
2065
|
const result = await uploadFile(rpc, machine, options.runs, capture, filtering.redactor);
|
|
2065
2066
|
seen.set(capture.file, fingerprint);
|
|
2066
2067
|
if (result.skipped)
|
|
2067
|
-
|
|
2068
|
+
logInfo(` ${capture.relative}: current (${result.rows} rows)\n`);
|
|
2068
2069
|
}
|
|
2069
2070
|
};
|
|
2070
2071
|
await sweep();
|
|
@@ -2072,7 +2073,7 @@ export async function runUploader(argv = process.argv.slice(2), { credentialProv
|
|
|
2072
2073
|
rpc.close();
|
|
2073
2074
|
return;
|
|
2074
2075
|
}
|
|
2075
|
-
|
|
2076
|
+
logInfo('Watching for appended capture rows…\n');
|
|
2076
2077
|
for (;;) {
|
|
2077
2078
|
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
2078
2079
|
await sweep();
|
|
@@ -2085,7 +2086,7 @@ export async function runUploader(argv = process.argv.slice(2), { credentialProv
|
|
|
2085
2086
|
}
|
|
2086
2087
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
2087
2088
|
runUploader().catch((error) => {
|
|
2088
|
-
|
|
2089
|
+
logError(`${error?.stack || error}\n`);
|
|
2089
2090
|
process.exitCode = 1;
|
|
2090
2091
|
});
|
|
2091
2092
|
}
|