rudel 0.2.1 → 0.2.3
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/cli.js +352 -132
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1887,7 +1887,7 @@ async function run(app, inputs, context) {
|
|
|
1887
1887
|
// package.json
|
|
1888
1888
|
var package_default = {
|
|
1889
1889
|
name: "rudel",
|
|
1890
|
-
version: "0.2.
|
|
1890
|
+
version: "0.2.3",
|
|
1891
1891
|
type: "module",
|
|
1892
1892
|
description: "CLI for the Coding Agent Analytics Platform rudel.ai",
|
|
1893
1893
|
license: "MIT",
|
|
@@ -1951,9 +1951,9 @@ var package_default = {
|
|
|
1951
1951
|
};
|
|
1952
1952
|
|
|
1953
1953
|
// ../../packages/agent-adapters/src/adapters/claude-code/index.ts
|
|
1954
|
-
import { readdir as readdir2, readFile as readFile2, stat } from "node:fs/promises";
|
|
1954
|
+
import { readdir as readdir2, readFile as readFile2, realpath, stat } from "node:fs/promises";
|
|
1955
1955
|
import { homedir as homedir2 } from "node:os";
|
|
1956
|
-
import { dirname as dirname2, join as join3 } from "node:path";
|
|
1956
|
+
import { dirname as dirname2, isAbsolute, join as join3, relative, sep } from "node:path";
|
|
1957
1957
|
|
|
1958
1958
|
// ../../node_modules/zod/v3/external.js
|
|
1959
1959
|
var exports_external = {};
|
|
@@ -6070,12 +6070,31 @@ async function ingestRudelCodexSessions(ingestor, rows, options) {
|
|
|
6070
6070
|
const data = options?.validate ? rows.map((row) => RudelCodexSessionsRowSchema.parse(row)) : rows;
|
|
6071
6071
|
await ingestor.insert({ table: "rudel.codex_sessions", values: data });
|
|
6072
6072
|
}
|
|
6073
|
+
// ../../packages/agent-adapters/src/errors.ts
|
|
6074
|
+
var SOURCE_DISPLAY_NAMES = {
|
|
6075
|
+
claude_code: "Claude Code",
|
|
6076
|
+
codex: "Codex"
|
|
6077
|
+
};
|
|
6078
|
+
function getMissingTranscriptTimestampMessage(source) {
|
|
6079
|
+
return `${SOURCE_DISPLAY_NAMES[source]} transcript contains no valid timestamp`;
|
|
6080
|
+
}
|
|
6081
|
+
function isMissingTranscriptTimestampMessage(source, message) {
|
|
6082
|
+
return message === getMissingTranscriptTimestampMessage(source);
|
|
6083
|
+
}
|
|
6084
|
+
|
|
6085
|
+
class MissingTranscriptTimestampError extends Error {
|
|
6086
|
+
constructor(source) {
|
|
6087
|
+
super(getMissingTranscriptTimestampMessage(source));
|
|
6088
|
+
this.name = "MissingTranscriptTimestampError";
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
|
|
6073
6092
|
// ../../packages/agent-adapters/src/utils.ts
|
|
6074
6093
|
import { readdir, readFile } from "node:fs/promises";
|
|
6075
6094
|
import { homedir } from "node:os";
|
|
6076
6095
|
import { join } from "node:path";
|
|
6077
6096
|
function toClickHouseDateTime(isoString) {
|
|
6078
|
-
return isoString.replace("T", " ").replace("Z", "")
|
|
6097
|
+
return new Date(isoString).toISOString().replace("T", " ").replace("Z", "");
|
|
6079
6098
|
}
|
|
6080
6099
|
async function readFileWithRetry(filePath, maxRetries = 5) {
|
|
6081
6100
|
const delayMs = 500;
|
|
@@ -6210,6 +6229,7 @@ function removeHook() {
|
|
|
6210
6229
|
|
|
6211
6230
|
// ../../packages/agent-adapters/src/adapters/claude-code/index.ts
|
|
6212
6231
|
var SESSIONS_BASE_DIR = join3(homedir2(), ".claude", "projects");
|
|
6232
|
+
var SAFE_BASENAME_PATTERN = /^[A-Za-z0-9_-]{1,200}$/;
|
|
6213
6233
|
function encodeProjectPath(projectPath) {
|
|
6214
6234
|
return projectPath.replace(/\//g, "-");
|
|
6215
6235
|
}
|
|
@@ -6254,8 +6274,11 @@ function extractAgentIds(sessionContent) {
|
|
|
6254
6274
|
continue;
|
|
6255
6275
|
try {
|
|
6256
6276
|
const entry = JSON.parse(line);
|
|
6257
|
-
if (entry.toolUseResult
|
|
6258
|
-
|
|
6277
|
+
if (!isRecord(entry) || !isRecord(entry.toolUseResult))
|
|
6278
|
+
continue;
|
|
6279
|
+
const agentId = entry.toolUseResult.agentId;
|
|
6280
|
+
if (isSafeBasename(agentId)) {
|
|
6281
|
+
agentIds.add(agentId);
|
|
6259
6282
|
}
|
|
6260
6283
|
} catch {}
|
|
6261
6284
|
}
|
|
@@ -6263,12 +6286,19 @@ function extractAgentIds(sessionContent) {
|
|
|
6263
6286
|
}
|
|
6264
6287
|
async function readSubagentFiles(sessionDir, agentIds, sessionId) {
|
|
6265
6288
|
const subagents = [];
|
|
6289
|
+
const subagentDirs = await resolveSubagentDirectories(sessionDir, sessionId);
|
|
6266
6290
|
for (const agentId of agentIds) {
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6291
|
+
if (!isSafeBasename(agentId))
|
|
6292
|
+
continue;
|
|
6293
|
+
for (const subagentDir of subagentDirs) {
|
|
6294
|
+
let agentPath;
|
|
6295
|
+
try {
|
|
6296
|
+
agentPath = await realpath(join3(subagentDir, `agent-${agentId}.jsonl`));
|
|
6297
|
+
} catch {
|
|
6298
|
+
continue;
|
|
6299
|
+
}
|
|
6300
|
+
if (!isContainedPath(subagentDir, agentPath))
|
|
6301
|
+
continue;
|
|
6272
6302
|
try {
|
|
6273
6303
|
const content = await readFile2(agentPath, "utf-8");
|
|
6274
6304
|
subagents.push({ agentId, content });
|
|
@@ -6343,6 +6373,9 @@ class ClaudeCodeAdapter {
|
|
|
6343
6373
|
}
|
|
6344
6374
|
async buildUploadRequest(session, context) {
|
|
6345
6375
|
const content = await readFileWithRetry(session.transcriptPath);
|
|
6376
|
+
if (!this.extractTimestamps(content)) {
|
|
6377
|
+
throw new MissingTranscriptTimestampError(this.source);
|
|
6378
|
+
}
|
|
6346
6379
|
const agentIds = extractAgentIds(content);
|
|
6347
6380
|
const sessionDir = dirname2(session.transcriptPath);
|
|
6348
6381
|
const subagents = agentIds.length > 0 ? await readSubagentFiles(sessionDir, agentIds, session.sessionId) : [];
|
|
@@ -6365,6 +6398,8 @@ class ClaudeCodeAdapter {
|
|
|
6365
6398
|
extractTimestamps(content) {
|
|
6366
6399
|
let min = null;
|
|
6367
6400
|
let max = null;
|
|
6401
|
+
let minTime = Number.POSITIVE_INFINITY;
|
|
6402
|
+
let maxTime = Number.NEGATIVE_INFINITY;
|
|
6368
6403
|
for (const line of content.split(`
|
|
6369
6404
|
`)) {
|
|
6370
6405
|
if (!line)
|
|
@@ -6375,12 +6410,23 @@ class ClaudeCodeAdapter {
|
|
|
6375
6410
|
} catch {
|
|
6376
6411
|
continue;
|
|
6377
6412
|
}
|
|
6378
|
-
if ((parsed
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6413
|
+
if (!isRecord(parsed))
|
|
6414
|
+
continue;
|
|
6415
|
+
if (parsed.type !== "user" && parsed.type !== "assistant")
|
|
6416
|
+
continue;
|
|
6417
|
+
if (typeof parsed.timestamp !== "string")
|
|
6418
|
+
continue;
|
|
6419
|
+
const timestampTime = Date.parse(parsed.timestamp);
|
|
6420
|
+
if (!Number.isFinite(timestampTime))
|
|
6421
|
+
continue;
|
|
6422
|
+
const timestamp = new Date(timestampTime).toISOString();
|
|
6423
|
+
if (timestampTime < minTime) {
|
|
6424
|
+
min = timestamp;
|
|
6425
|
+
minTime = timestampTime;
|
|
6426
|
+
}
|
|
6427
|
+
if (timestampTime > maxTime) {
|
|
6428
|
+
max = timestamp;
|
|
6429
|
+
maxTime = timestampTime;
|
|
6384
6430
|
}
|
|
6385
6431
|
}
|
|
6386
6432
|
if (!min || !max)
|
|
@@ -6399,10 +6445,13 @@ class ClaudeCodeAdapter {
|
|
|
6399
6445
|
subagents[sub.agentId] = sub.content;
|
|
6400
6446
|
}
|
|
6401
6447
|
}
|
|
6402
|
-
const timestamps = this.extractTimestamps(input.content);
|
|
6448
|
+
const timestamps = context.timestamps ?? this.extractTimestamps(input.content);
|
|
6449
|
+
if (!timestamps) {
|
|
6450
|
+
throw new MissingTranscriptTimestampError(this.source);
|
|
6451
|
+
}
|
|
6403
6452
|
return {
|
|
6404
|
-
session_date:
|
|
6405
|
-
last_interaction_date:
|
|
6453
|
+
session_date: toClickHouseDateTime(timestamps.sessionDate),
|
|
6454
|
+
last_interaction_date: toClickHouseDateTime(timestamps.lastInteractionDate),
|
|
6406
6455
|
session_id: input.sessionId,
|
|
6407
6456
|
organization_id: context.organizationId,
|
|
6408
6457
|
project_path: input.projectPath,
|
|
@@ -6451,6 +6500,35 @@ class ClaudeCodeAdapter {
|
|
|
6451
6500
|
}
|
|
6452
6501
|
}
|
|
6453
6502
|
var claudeCodeAdapter = new ClaudeCodeAdapter;
|
|
6503
|
+
function isRecord(value) {
|
|
6504
|
+
return typeof value === "object" && value !== null;
|
|
6505
|
+
}
|
|
6506
|
+
function isSafeBasename(value) {
|
|
6507
|
+
return typeof value === "string" && SAFE_BASENAME_PATTERN.test(value);
|
|
6508
|
+
}
|
|
6509
|
+
async function resolveSubagentDirectories(sessionDir, sessionId) {
|
|
6510
|
+
let canonicalSessionDir;
|
|
6511
|
+
try {
|
|
6512
|
+
canonicalSessionDir = await realpath(sessionDir);
|
|
6513
|
+
} catch {
|
|
6514
|
+
return [];
|
|
6515
|
+
}
|
|
6516
|
+
const directories = [canonicalSessionDir];
|
|
6517
|
+
if (!isSafeBasename(sessionId)) {
|
|
6518
|
+
return directories;
|
|
6519
|
+
}
|
|
6520
|
+
try {
|
|
6521
|
+
const nestedDir = await realpath(join3(canonicalSessionDir, sessionId, "subagents"));
|
|
6522
|
+
if (isContainedPath(canonicalSessionDir, nestedDir)) {
|
|
6523
|
+
directories.push(nestedDir);
|
|
6524
|
+
}
|
|
6525
|
+
} catch {}
|
|
6526
|
+
return directories;
|
|
6527
|
+
}
|
|
6528
|
+
function isContainedPath(parentPath, candidatePath) {
|
|
6529
|
+
const pathFromParent = relative(parentPath, candidatePath);
|
|
6530
|
+
return pathFromParent !== "" && pathFromParent !== ".." && !pathFromParent.startsWith(`..${sep}`) && !isAbsolute(pathFromParent);
|
|
6531
|
+
}
|
|
6454
6532
|
// ../../packages/agent-adapters/src/adapters/codex/index.ts
|
|
6455
6533
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
6456
6534
|
import { homedir as homedir4 } from "node:os";
|
|
@@ -6598,7 +6676,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
|
|
|
6598
6676
|
ptr++;
|
|
6599
6677
|
return banComments || c !== "#" ? ptr : skipVoid(str, skipComment(str, ptr), banNewLines);
|
|
6600
6678
|
}
|
|
6601
|
-
function skipUntil(str, ptr,
|
|
6679
|
+
function skipUntil(str, ptr, sep2, end, banNewLines = false) {
|
|
6602
6680
|
if (!end) {
|
|
6603
6681
|
ptr = indexOfNewline(str, ptr);
|
|
6604
6682
|
return ptr < 0 ? str.length : ptr;
|
|
@@ -6607,7 +6685,7 @@ function skipUntil(str, ptr, sep, end, banNewLines = false) {
|
|
|
6607
6685
|
let c = str[i];
|
|
6608
6686
|
if (c === "#") {
|
|
6609
6687
|
i = indexOfNewline(str, i);
|
|
6610
|
-
} else if (c ===
|
|
6688
|
+
} else if (c === sep2) {
|
|
6611
6689
|
return i + 1;
|
|
6612
6690
|
} else if (c === end || banNewLines && (c === `
|
|
6613
6691
|
` || c === "\r" && str[i + 1] === `
|
|
@@ -7680,6 +7758,9 @@ class CodexAdapter {
|
|
|
7680
7758
|
}
|
|
7681
7759
|
async buildUploadRequest(session, context) {
|
|
7682
7760
|
const content = await readFile3(session.transcriptPath, "utf-8");
|
|
7761
|
+
if (!this.extractTimestamps(content)) {
|
|
7762
|
+
throw new MissingTranscriptTimestampError(this.source);
|
|
7763
|
+
}
|
|
7683
7764
|
return {
|
|
7684
7765
|
source: this.source,
|
|
7685
7766
|
sessionId: session.sessionId,
|
|
@@ -7698,6 +7779,8 @@ class CodexAdapter {
|
|
|
7698
7779
|
extractTimestamps(content) {
|
|
7699
7780
|
let min = null;
|
|
7700
7781
|
let max = null;
|
|
7782
|
+
let minTime = Number.POSITIVE_INFINITY;
|
|
7783
|
+
let maxTime = Number.NEGATIVE_INFINITY;
|
|
7701
7784
|
for (const line of content.split(`
|
|
7702
7785
|
`)) {
|
|
7703
7786
|
if (!line)
|
|
@@ -7708,12 +7791,19 @@ class CodexAdapter {
|
|
|
7708
7791
|
} catch {
|
|
7709
7792
|
continue;
|
|
7710
7793
|
}
|
|
7711
|
-
if (parsed.timestamp)
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7794
|
+
if (!isRecord2(parsed) || typeof parsed.timestamp !== "string")
|
|
7795
|
+
continue;
|
|
7796
|
+
const timestampTime = Date.parse(parsed.timestamp);
|
|
7797
|
+
if (!Number.isFinite(timestampTime))
|
|
7798
|
+
continue;
|
|
7799
|
+
const timestamp = new Date(timestampTime).toISOString();
|
|
7800
|
+
if (timestampTime < minTime) {
|
|
7801
|
+
min = timestamp;
|
|
7802
|
+
minTime = timestampTime;
|
|
7803
|
+
}
|
|
7804
|
+
if (timestampTime > maxTime) {
|
|
7805
|
+
max = timestamp;
|
|
7806
|
+
maxTime = timestampTime;
|
|
7717
7807
|
}
|
|
7718
7808
|
}
|
|
7719
7809
|
if (!min || !max)
|
|
@@ -7726,10 +7816,13 @@ class CodexAdapter {
|
|
|
7726
7816
|
}
|
|
7727
7817
|
buildRow(input, context) {
|
|
7728
7818
|
const now = context.ingestedAt.toISOString().replace("Z", "");
|
|
7729
|
-
const timestamps = this.extractTimestamps(input.content);
|
|
7819
|
+
const timestamps = context.timestamps ?? this.extractTimestamps(input.content);
|
|
7820
|
+
if (!timestamps) {
|
|
7821
|
+
throw new MissingTranscriptTimestampError(this.source);
|
|
7822
|
+
}
|
|
7730
7823
|
return {
|
|
7731
|
-
session_date:
|
|
7732
|
-
last_interaction_date:
|
|
7824
|
+
session_date: toClickHouseDateTime(timestamps.sessionDate),
|
|
7825
|
+
last_interaction_date: toClickHouseDateTime(timestamps.lastInteractionDate),
|
|
7733
7826
|
session_id: input.sessionId,
|
|
7734
7827
|
organization_id: context.organizationId,
|
|
7735
7828
|
project_path: input.projectPath,
|
|
@@ -7747,6 +7840,9 @@ class CodexAdapter {
|
|
|
7747
7840
|
}
|
|
7748
7841
|
}
|
|
7749
7842
|
var codexAdapter = new CodexAdapter;
|
|
7843
|
+
function isRecord2(value) {
|
|
7844
|
+
return typeof value === "object" && value !== null;
|
|
7845
|
+
}
|
|
7750
7846
|
// ../../packages/agent-adapters/src/registry.ts
|
|
7751
7847
|
import { existsSync as existsSync3 } from "node:fs";
|
|
7752
7848
|
var adapters = new Map;
|
|
@@ -11021,7 +11117,6 @@ var SessionAnalyticsSchema = exports_external.object({
|
|
|
11021
11117
|
skills: exports_external.array(exports_external.string()),
|
|
11022
11118
|
slash_commands: exports_external.array(exports_external.string()),
|
|
11023
11119
|
has_commit: exports_external.boolean(),
|
|
11024
|
-
session_archetype: exports_external.string(),
|
|
11025
11120
|
model_used: exports_external.string(),
|
|
11026
11121
|
used_plan_mode: exports_external.boolean(),
|
|
11027
11122
|
source: SourceSchema.optional()
|
|
@@ -11044,6 +11139,8 @@ var SessionAnalyticsSummaryComparisonSchema = exports_external.object({
|
|
|
11044
11139
|
})
|
|
11045
11140
|
});
|
|
11046
11141
|
var SessionListInputSchema = DaysInputSchema.extend({
|
|
11142
|
+
startDate: exports_external.string().date().optional(),
|
|
11143
|
+
endDate: exports_external.string().date().optional(),
|
|
11047
11144
|
userId: exports_external.string().max(MAX_ID_FILTER_LENGTH).optional(),
|
|
11048
11145
|
projectPath: exports_external.string().max(MAX_PATH_FILTER_LENGTH).optional(),
|
|
11049
11146
|
repository: exports_external.string().max(MAX_PATH_FILTER_LENGTH).optional(),
|
|
@@ -11057,7 +11154,6 @@ var VALID_DIMENSIONS = [
|
|
|
11057
11154
|
"user_id",
|
|
11058
11155
|
"project_path",
|
|
11059
11156
|
"repository",
|
|
11060
|
-
"session_archetype",
|
|
11061
11157
|
"model_used",
|
|
11062
11158
|
"has_commit",
|
|
11063
11159
|
"used_plan_mode",
|
|
@@ -11111,7 +11207,6 @@ var SessionDetailSchema = exports_external.object({
|
|
|
11111
11207
|
success_score: exports_external.number().optional(),
|
|
11112
11208
|
duration_min: exports_external.number().optional(),
|
|
11113
11209
|
total_interactions: exports_external.number().optional(),
|
|
11114
|
-
session_archetype: exports_external.string().optional(),
|
|
11115
11210
|
model_used: exports_external.string().optional(),
|
|
11116
11211
|
source: SourceSchema.optional()
|
|
11117
11212
|
});
|
|
@@ -11367,74 +11462,90 @@ var WrappedDecimalClaimEntitlementSchema = exports_external.object({
|
|
|
11367
11462
|
});
|
|
11368
11463
|
|
|
11369
11464
|
// ../../packages/api-routes/src/schemas/wrapped-share.ts
|
|
11465
|
+
var WRAPPED_SHARE_RESOURCE_LIMITS = {
|
|
11466
|
+
backMetricCount: 20,
|
|
11467
|
+
classNameLength: 512,
|
|
11468
|
+
imageUrlLength: 2048,
|
|
11469
|
+
snapshotBytes: 64 * 1024,
|
|
11470
|
+
socialImageBytes: 1024 * 1024,
|
|
11471
|
+
statItemCount: 8,
|
|
11472
|
+
textLength: 256
|
|
11473
|
+
};
|
|
11474
|
+
var WRAPPED_SHARE_SOCIAL_IMAGE_DATA_URL_PREFIX = "data:image/png;base64,";
|
|
11475
|
+
var WRAPPED_SHARE_SOCIAL_IMAGE_DATA_URL_MAX_LENGTH = WRAPPED_SHARE_SOCIAL_IMAGE_DATA_URL_PREFIX.length + Math.ceil(WRAPPED_SHARE_RESOURCE_LIMITS.socialImageBytes / 3) * 4;
|
|
11476
|
+
var WrappedShareTextSchema = exports_external.string().max(WRAPPED_SHARE_RESOURCE_LIMITS.textLength);
|
|
11477
|
+
var WrappedShareRequiredTextSchema = WrappedShareTextSchema.min(1);
|
|
11370
11478
|
var WrappedShareThemeSchema = exports_external.enum(["dark", "light", "muted"]);
|
|
11371
11479
|
var WrappedShareVariantSchema = exports_external.enum(["normal", "decimal"]);
|
|
11372
11480
|
var WrappedShareLayoutModeSchema = exports_external.enum(["front", "front_back"]);
|
|
11373
11481
|
var WrappedShareIdSchema = exports_external.string().trim().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/u);
|
|
11374
11482
|
var WrappedShareHeaderMetricSchema = exports_external.object({
|
|
11375
|
-
label:
|
|
11376
|
-
title:
|
|
11377
|
-
value:
|
|
11378
|
-
});
|
|
11483
|
+
label: WrappedShareTextSchema.optional(),
|
|
11484
|
+
title: WrappedShareTextSchema.optional(),
|
|
11485
|
+
value: WrappedShareRequiredTextSchema
|
|
11486
|
+
}).strict();
|
|
11379
11487
|
var WrappedShareStatItemIconSchema = exports_external.enum(["claude", "codex"]);
|
|
11380
11488
|
var WrappedShareStatItemSchema = exports_external.object({
|
|
11381
11489
|
icon: WrappedShareStatItemIconSchema.optional(),
|
|
11382
|
-
key:
|
|
11383
|
-
label:
|
|
11384
|
-
title:
|
|
11385
|
-
value:
|
|
11386
|
-
});
|
|
11490
|
+
key: WrappedShareRequiredTextSchema.regex(/^[A-Za-z0-9][A-Za-z0-9_-]*$/u),
|
|
11491
|
+
label: WrappedShareTextSchema.optional(),
|
|
11492
|
+
title: WrappedShareTextSchema.optional(),
|
|
11493
|
+
value: WrappedShareRequiredTextSchema
|
|
11494
|
+
}).strict();
|
|
11387
11495
|
var WrappedShareBackMetricSchema = exports_external.object({
|
|
11388
|
-
label:
|
|
11496
|
+
label: WrappedShareTextSchema,
|
|
11389
11497
|
slot: exports_external.enum(["body", "footer"]).optional(),
|
|
11390
|
-
value:
|
|
11391
|
-
});
|
|
11498
|
+
value: WrappedShareRequiredTextSchema
|
|
11499
|
+
}).strict();
|
|
11392
11500
|
var WrappedShareRevealMetricsSchema = exports_external.object({
|
|
11393
11501
|
avgSessionMin: exports_external.number().nonnegative().nullable(),
|
|
11394
11502
|
commitRate: exports_external.number().min(0).max(100).nullable(),
|
|
11395
11503
|
daysSinceFirst: exports_external.number().nonnegative(),
|
|
11396
11504
|
distinctProjectCount: exports_external.number().nonnegative(),
|
|
11397
11505
|
longestSessionMin: exports_external.number().nonnegative().nullable()
|
|
11398
|
-
});
|
|
11506
|
+
}).strict();
|
|
11399
11507
|
var WrappedShareAppearanceSchema = exports_external.object({
|
|
11400
11508
|
layoutMode: WrappedShareLayoutModeSchema,
|
|
11401
11509
|
showArchetypeLabel: exports_external.boolean()
|
|
11510
|
+
}).strict();
|
|
11511
|
+
var WrappedShareSocialImageDataUrlSchema = exports_external.string().max(WRAPPED_SHARE_SOCIAL_IMAGE_DATA_URL_MAX_LENGTH).regex(/^data:image\/png;base64,[A-Za-z0-9+/]+={0,2}$/u).refine(isWrappedShareSocialImageWithinByteLimit, {
|
|
11512
|
+
message: `Wrapped share social image must be at most ${WRAPPED_SHARE_RESOURCE_LIMITS.socialImageBytes} bytes`
|
|
11402
11513
|
});
|
|
11403
|
-
var WrappedShareSocialImageDataUrlSchema = exports_external.string().max(7000000).regex(/^data:image\/png;base64,[A-Za-z0-9+/]+={0,2}$/u);
|
|
11404
11514
|
var WrappedShareRowSchema = exports_external.object({
|
|
11405
11515
|
activeDays: exports_external.number().nonnegative(),
|
|
11406
11516
|
cost: exports_external.number().nonnegative(),
|
|
11407
|
-
displayName:
|
|
11408
|
-
favoriteModel:
|
|
11517
|
+
displayName: WrappedShareRequiredTextSchema,
|
|
11518
|
+
favoriteModel: WrappedShareTextSchema.nullable(),
|
|
11409
11519
|
hasActivity: exports_external.boolean(),
|
|
11410
|
-
imageUrl: exports_external.string().nullable(),
|
|
11520
|
+
imageUrl: exports_external.string().max(WRAPPED_SHARE_RESOURCE_LIMITS.imageUrlLength).nullable(),
|
|
11411
11521
|
inputTokens: exports_external.number().nonnegative(),
|
|
11412
|
-
lastActiveDate:
|
|
11522
|
+
lastActiveDate: WrappedShareTextSchema.nullable(),
|
|
11413
11523
|
outputTokens: exports_external.number().nonnegative(),
|
|
11414
|
-
role:
|
|
11524
|
+
role: WrappedShareRequiredTextSchema,
|
|
11415
11525
|
totalSessions: exports_external.number().nonnegative(),
|
|
11416
11526
|
totalTokens: exports_external.number().nonnegative()
|
|
11417
|
-
});
|
|
11418
|
-
var
|
|
11527
|
+
}).strict();
|
|
11528
|
+
var WrappedShareSnapshotObjectSchema = exports_external.object({
|
|
11419
11529
|
appearance: WrappedShareAppearanceSchema.optional(),
|
|
11420
|
-
archetypeLabel:
|
|
11421
|
-
backMetrics: exports_external.array(WrappedShareBackMetricSchema).optional(),
|
|
11530
|
+
archetypeLabel: WrappedShareRequiredTextSchema,
|
|
11531
|
+
backMetrics: exports_external.array(WrappedShareBackMetricSchema).max(WRAPPED_SHARE_RESOURCE_LIMITS.backMetricCount).optional(),
|
|
11422
11532
|
headerLeftMetric: WrappedShareHeaderMetricSchema.optional(),
|
|
11423
11533
|
headerRightMetric: WrappedShareHeaderMetricSchema.optional(),
|
|
11424
11534
|
revealMetrics: WrappedShareRevealMetricsSchema.optional(),
|
|
11425
11535
|
row: WrappedShareRowSchema,
|
|
11426
|
-
shellClassName: exports_external.string().min(1),
|
|
11427
|
-
|
|
11428
|
-
statItems: exports_external.array(WrappedShareStatItemSchema),
|
|
11536
|
+
shellClassName: exports_external.string().min(1).max(WRAPPED_SHARE_RESOURCE_LIMITS.classNameLength),
|
|
11537
|
+
statItems: exports_external.array(WrappedShareStatItemSchema).max(WRAPPED_SHARE_RESOURCE_LIMITS.statItemCount),
|
|
11429
11538
|
theme: WrappedShareThemeSchema
|
|
11539
|
+
}).strict();
|
|
11540
|
+
var WrappedShareSnapshotSchema = WrappedShareSnapshotObjectSchema.refine(isWrappedShareSnapshotWithinByteLimit, {
|
|
11541
|
+
message: `Wrapped share snapshot must be at most ${WRAPPED_SHARE_RESOURCE_LIMITS.snapshotBytes} bytes`
|
|
11430
11542
|
});
|
|
11431
|
-
var PublicWrappedShareSnapshotSchema = WrappedShareSnapshotSchema
|
|
11432
|
-
socialImageDataUrl: true
|
|
11433
|
-
});
|
|
11543
|
+
var PublicWrappedShareSnapshotSchema = WrappedShareSnapshotSchema;
|
|
11434
11544
|
var CreateWrappedShareInputSchema = exports_external.object({
|
|
11545
|
+
socialImageDataUrl: WrappedShareSocialImageDataUrlSchema.optional(),
|
|
11435
11546
|
snapshot: WrappedShareSnapshotSchema,
|
|
11436
11547
|
variant: WrappedShareVariantSchema.default("normal")
|
|
11437
|
-
});
|
|
11548
|
+
}).strict();
|
|
11438
11549
|
var GetPublicWrappedShareInputSchema = exports_external.object({
|
|
11439
11550
|
shareId: WrappedShareIdSchema
|
|
11440
11551
|
});
|
|
@@ -11447,6 +11558,22 @@ var WrappedShareRecordSchema = exports_external.object({
|
|
|
11447
11558
|
var PublicWrappedShareSchema = WrappedShareRecordSchema.extend({
|
|
11448
11559
|
snapshot: PublicWrappedShareSnapshotSchema
|
|
11449
11560
|
});
|
|
11561
|
+
function getWrappedShareSnapshotByteLength(snapshot) {
|
|
11562
|
+
const snapshotJson = JSON.stringify(snapshot);
|
|
11563
|
+
if (snapshotJson === undefined) {
|
|
11564
|
+
return 0;
|
|
11565
|
+
}
|
|
11566
|
+
return new TextEncoder().encode(snapshotJson).byteLength;
|
|
11567
|
+
}
|
|
11568
|
+
function isWrappedShareSnapshotWithinByteLimit(snapshot) {
|
|
11569
|
+
return getWrappedShareSnapshotByteLength(snapshot) <= WRAPPED_SHARE_RESOURCE_LIMITS.snapshotBytes;
|
|
11570
|
+
}
|
|
11571
|
+
function isWrappedShareSocialImageWithinByteLimit(dataUrl) {
|
|
11572
|
+
const base64 = dataUrl.slice(WRAPPED_SHARE_SOCIAL_IMAGE_DATA_URL_PREFIX.length);
|
|
11573
|
+
const paddingLength = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
|
|
11574
|
+
const decodedBytes = base64.length * 3 / 4 - paddingLength;
|
|
11575
|
+
return Number.isInteger(decodedBytes) && decodedBytes <= WRAPPED_SHARE_RESOURCE_LIMITS.socialImageBytes;
|
|
11576
|
+
}
|
|
11450
11577
|
|
|
11451
11578
|
// ../../packages/api-routes/src/schemas/wrapped-resume.ts
|
|
11452
11579
|
var CreateWrappedResumeInputSchema = exports_external.object({
|
|
@@ -11639,6 +11766,10 @@ var CliUserSchema = exports_external.object({
|
|
|
11639
11766
|
var CliSetupStatusSchema = exports_external.object({
|
|
11640
11767
|
hasCliLogin: exports_external.boolean()
|
|
11641
11768
|
});
|
|
11769
|
+
var ChatwootIdentitySchema = exports_external.object({
|
|
11770
|
+
identifier: exports_external.string(),
|
|
11771
|
+
identifier_hash: exports_external.string().regex(/^[a-f0-9]{64}$/u)
|
|
11772
|
+
});
|
|
11642
11773
|
var OrganizationSchema = exports_external.object({
|
|
11643
11774
|
id: exports_external.string(),
|
|
11644
11775
|
name: exports_external.string(),
|
|
@@ -11646,6 +11777,7 @@ var OrganizationSchema = exports_external.object({
|
|
|
11646
11777
|
logo: exports_external.string().nullable()
|
|
11647
11778
|
});
|
|
11648
11779
|
var TeamInviteLinkSchema = exports_external.object({
|
|
11780
|
+
expires_at: exports_external.string().datetime(),
|
|
11649
11781
|
invite_url: exports_external.string().url(),
|
|
11650
11782
|
organization_id: exports_external.string(),
|
|
11651
11783
|
organization_name: exports_external.string()
|
|
@@ -11732,6 +11864,9 @@ var contract = {
|
|
|
11732
11864
|
revokeToken: oc.output(exports_external.object({ success: exports_external.literal(true) })),
|
|
11733
11865
|
setupStatus: oc.output(CliSetupStatusSchema)
|
|
11734
11866
|
},
|
|
11867
|
+
chatwoot: {
|
|
11868
|
+
identity: oc.output(ChatwootIdentitySchema.nullable())
|
|
11869
|
+
},
|
|
11735
11870
|
listMyOrganizations: oc.output(exports_external.array(OrganizationSchema)),
|
|
11736
11871
|
ingestSession: oc.input(IngestSessionInputSchema).output(IngestSessionOutputSchema).errors({
|
|
11737
11872
|
[REDACTION_BUDGET_EXCEEDED_CODE]: {
|
|
@@ -11758,7 +11893,8 @@ var contract = {
|
|
|
11758
11893
|
getOrganizationSessionCount: oc.input(exports_external.object({ organizationId: exports_external.string(), userId: exports_external.string().optional() })).output(exports_external.object({ count: exports_external.number() })),
|
|
11759
11894
|
deleteOrganization: oc.input(exports_external.object({ organizationId: exports_external.string() })).output(exports_external.object({ success: exports_external.literal(true) })),
|
|
11760
11895
|
teamInviteLink: {
|
|
11761
|
-
|
|
11896
|
+
create: oc.input(exports_external.object({ organizationId: exports_external.string() })).output(TeamInviteLinkSchema),
|
|
11897
|
+
revoke: oc.input(exports_external.object({ organizationId: exports_external.string() })).output(exports_external.object({ success: exports_external.literal(true) })),
|
|
11762
11898
|
accept: oc.input(exports_external.object({ token: exports_external.string().min(1) })).output(TeamInviteAcceptResultSchema)
|
|
11763
11899
|
},
|
|
11764
11900
|
wrappedShare: {
|
|
@@ -11848,43 +11984,77 @@ var contract = {
|
|
|
11848
11984
|
};
|
|
11849
11985
|
|
|
11850
11986
|
// src/lib/credentials.ts
|
|
11987
|
+
import { randomUUID } from "node:crypto";
|
|
11851
11988
|
import {
|
|
11989
|
+
chmodSync,
|
|
11852
11990
|
existsSync as existsSync6,
|
|
11853
11991
|
mkdirSync as mkdirSync2,
|
|
11854
11992
|
readFileSync as readFileSync5,
|
|
11993
|
+
renameSync,
|
|
11855
11994
|
rmSync,
|
|
11995
|
+
statSync,
|
|
11856
11996
|
writeFileSync as writeFileSync3
|
|
11857
11997
|
} from "node:fs";
|
|
11858
11998
|
import { homedir as homedir7 } from "node:os";
|
|
11859
11999
|
import { join as join9 } from "node:path";
|
|
12000
|
+
var PRIVATE_DIRECTORY_MODE2 = 448;
|
|
12001
|
+
var PRIVATE_FILE_MODE2 = 384;
|
|
11860
12002
|
function getConfigDir() {
|
|
11861
12003
|
return process.env.RUDEL_CONFIG_DIR ?? join9(homedir7(), ".rudel");
|
|
11862
12004
|
}
|
|
11863
|
-
function getCredentialsPath() {
|
|
11864
|
-
return join9(getConfigDir(), "credentials.json");
|
|
11865
|
-
}
|
|
11866
12005
|
function saveCredentials(credentials) {
|
|
11867
12006
|
const dir = getConfigDir();
|
|
11868
|
-
|
|
11869
|
-
|
|
12007
|
+
const path = getCredentialsPath(dir);
|
|
12008
|
+
const content = JSON.stringify(credentials, null, 2);
|
|
12009
|
+
mkdirSync2(dir, { recursive: true, mode: PRIVATE_DIRECTORY_MODE2 });
|
|
12010
|
+
enforcePrivateMode(dir, PRIVATE_DIRECTORY_MODE2);
|
|
12011
|
+
if (existsSync6(path)) {
|
|
12012
|
+
enforcePrivateMode(path, PRIVATE_FILE_MODE2);
|
|
11870
12013
|
}
|
|
11871
|
-
|
|
11872
|
-
mode: 384
|
|
11873
|
-
});
|
|
12014
|
+
replaceCredentialsFile(path, content);
|
|
11874
12015
|
}
|
|
11875
12016
|
function loadCredentials() {
|
|
11876
|
-
const
|
|
12017
|
+
const dir = getConfigDir();
|
|
12018
|
+
const path = getCredentialsPath(dir);
|
|
11877
12019
|
if (!existsSync6(path))
|
|
11878
12020
|
return null;
|
|
12021
|
+
enforcePrivateMode(dir, PRIVATE_DIRECTORY_MODE2);
|
|
12022
|
+
enforcePrivateMode(path, PRIVATE_FILE_MODE2);
|
|
11879
12023
|
const content = readFileSync5(path, "utf-8");
|
|
11880
12024
|
return JSON.parse(content);
|
|
11881
12025
|
}
|
|
11882
12026
|
function clearCredentials() {
|
|
11883
|
-
const path = getCredentialsPath();
|
|
12027
|
+
const path = getCredentialsPath(getConfigDir());
|
|
11884
12028
|
if (existsSync6(path)) {
|
|
11885
12029
|
rmSync(path);
|
|
11886
12030
|
}
|
|
11887
12031
|
}
|
|
12032
|
+
function getCredentialsPath(configDir) {
|
|
12033
|
+
return join9(configDir, "credentials.json");
|
|
12034
|
+
}
|
|
12035
|
+
function replaceCredentialsFile(path, content) {
|
|
12036
|
+
const temporaryPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
12037
|
+
try {
|
|
12038
|
+
writeFileSync3(temporaryPath, content, {
|
|
12039
|
+
encoding: "utf8",
|
|
12040
|
+
flag: "wx",
|
|
12041
|
+
mode: PRIVATE_FILE_MODE2
|
|
12042
|
+
});
|
|
12043
|
+
enforcePrivateMode(temporaryPath, PRIVATE_FILE_MODE2);
|
|
12044
|
+
renameSync(temporaryPath, path);
|
|
12045
|
+
enforcePrivateMode(path, PRIVATE_FILE_MODE2);
|
|
12046
|
+
} finally {
|
|
12047
|
+
rmSync(temporaryPath, { force: true });
|
|
12048
|
+
}
|
|
12049
|
+
}
|
|
12050
|
+
function enforcePrivateMode(path, mode) {
|
|
12051
|
+
if (process.platform === "win32")
|
|
12052
|
+
return;
|
|
12053
|
+
chmodSync(path, mode);
|
|
12054
|
+
if ((statSync(path).mode & 511) !== mode) {
|
|
12055
|
+
throw new Error("Unable to establish private credential permissions");
|
|
12056
|
+
}
|
|
12057
|
+
}
|
|
11888
12058
|
|
|
11889
12059
|
// src/lib/insecure-url-opt-in.ts
|
|
11890
12060
|
var TRUTHY_ENV_VALUES = ["1", "true", "yes", "on"];
|
|
@@ -13966,14 +14136,17 @@ async function batchUpload(options) {
|
|
|
13966
14136
|
let succeeded = 0;
|
|
13967
14137
|
let failed = 0;
|
|
13968
14138
|
let skipped = 0;
|
|
14139
|
+
let deferred = 0;
|
|
13969
14140
|
let completed = 0;
|
|
13970
14141
|
let rateLimited = false;
|
|
13971
14142
|
const errors2 = [];
|
|
14143
|
+
const skippedItems = [];
|
|
14144
|
+
const skippedSessionIds = new Set;
|
|
13972
14145
|
let redacted = {};
|
|
13973
14146
|
let redactedBytes = 0;
|
|
13974
14147
|
await pMap(items, async (item) => {
|
|
13975
14148
|
if (rateLimited) {
|
|
13976
|
-
|
|
14149
|
+
deferred++;
|
|
13977
14150
|
const error = "Skipped — rate limit reached. Run `rudel upload --retry` to upload remaining sessions.";
|
|
13978
14151
|
await recordFailedUpload({
|
|
13979
14152
|
sessionId: item.sessionId,
|
|
@@ -13997,6 +14170,13 @@ async function batchUpload(options) {
|
|
|
13997
14170
|
redacted = mergeRedactionCounts(redacted, result.redacted ?? {});
|
|
13998
14171
|
redactedBytes += result.redactedBytes ?? 0;
|
|
13999
14172
|
await removeFailedUpload(item.sessionId);
|
|
14173
|
+
} else if (result.retryable === false) {
|
|
14174
|
+
skipped++;
|
|
14175
|
+
skippedItems.push({
|
|
14176
|
+
label: item.label,
|
|
14177
|
+
reason: result.error ?? "Upload cannot be retried"
|
|
14178
|
+
});
|
|
14179
|
+
skippedSessionIds.add(item.sessionId);
|
|
14000
14180
|
} else {
|
|
14001
14181
|
failed++;
|
|
14002
14182
|
const error = result.error ?? "Unknown error";
|
|
@@ -14014,30 +14194,48 @@ async function batchUpload(options) {
|
|
|
14014
14194
|
});
|
|
14015
14195
|
}
|
|
14016
14196
|
} catch (err) {
|
|
14017
|
-
failed++;
|
|
14018
14197
|
const error = err instanceof Error ? err.message : String(err);
|
|
14019
|
-
|
|
14020
|
-
|
|
14021
|
-
|
|
14022
|
-
|
|
14023
|
-
|
|
14024
|
-
|
|
14025
|
-
|
|
14026
|
-
|
|
14027
|
-
|
|
14198
|
+
if (err instanceof MissingTranscriptTimestampError) {
|
|
14199
|
+
skipped++;
|
|
14200
|
+
skippedItems.push({ label: item.label, reason: error });
|
|
14201
|
+
skippedSessionIds.add(item.sessionId);
|
|
14202
|
+
} else {
|
|
14203
|
+
failed++;
|
|
14204
|
+
errors2.push({ label: item.label, error });
|
|
14205
|
+
await recordFailedUpload({
|
|
14206
|
+
sessionId: item.sessionId,
|
|
14207
|
+
transcriptPath: item.transcriptPath,
|
|
14208
|
+
projectPath: item.projectPath,
|
|
14209
|
+
source: item.source,
|
|
14210
|
+
organizationId: item.organizationId,
|
|
14211
|
+
error
|
|
14212
|
+
});
|
|
14213
|
+
}
|
|
14028
14214
|
} finally {
|
|
14029
14215
|
completed++;
|
|
14030
14216
|
onItemComplete?.(completed, total);
|
|
14031
14217
|
}
|
|
14032
14218
|
}, { concurrency, stopOnError: false });
|
|
14033
|
-
|
|
14219
|
+
for (const sessionId of skippedSessionIds) {
|
|
14220
|
+
await removeFailedUpload(sessionId);
|
|
14221
|
+
}
|
|
14222
|
+
if (rateLimited && deferred > 0) {
|
|
14034
14223
|
errors2.push({
|
|
14035
14224
|
label: "Rate limit",
|
|
14036
|
-
error: `${
|
|
14225
|
+
error: `${deferred} session(s) skipped. Run \`rudel upload --retry\` later to upload them.`
|
|
14037
14226
|
});
|
|
14038
|
-
failed +=
|
|
14227
|
+
failed += deferred;
|
|
14039
14228
|
}
|
|
14040
|
-
return {
|
|
14229
|
+
return {
|
|
14230
|
+
succeeded,
|
|
14231
|
+
failed,
|
|
14232
|
+
skipped,
|
|
14233
|
+
total,
|
|
14234
|
+
errors: errors2,
|
|
14235
|
+
skippedItems,
|
|
14236
|
+
redacted,
|
|
14237
|
+
redactedBytes
|
|
14238
|
+
};
|
|
14041
14239
|
}
|
|
14042
14240
|
|
|
14043
14241
|
// src/lib/upload-endpoint.ts
|
|
@@ -14156,13 +14354,13 @@ function formatServerUploadError(error) {
|
|
|
14156
14354
|
return `Rudel server error (${status}). This is not an auth problem. Retry later with: rudel upload --retry; if it repeats, share this status with the Rudel team.`;
|
|
14157
14355
|
}
|
|
14158
14356
|
function getPayloadTooLargeDetail(error) {
|
|
14159
|
-
const data =
|
|
14357
|
+
const data = isRecord3(error.data) ? error.data : null;
|
|
14160
14358
|
const bodyValue = data?.body;
|
|
14161
|
-
const body =
|
|
14359
|
+
const body = isRecord3(bodyValue) ? bodyValue : null;
|
|
14162
14360
|
return getStringField(body, "error") ?? getStringField(data, "error");
|
|
14163
14361
|
}
|
|
14164
14362
|
function getErrorData(error) {
|
|
14165
|
-
const data =
|
|
14363
|
+
const data = isRecord3(error.data) ? error.data : null;
|
|
14166
14364
|
return {
|
|
14167
14365
|
authMessage: getStringField(data, "authMessage"),
|
|
14168
14366
|
actualBytes: getNumberField(data, "actualBytes"),
|
|
@@ -14175,7 +14373,7 @@ function getErrorData(error) {
|
|
|
14175
14373
|
};
|
|
14176
14374
|
}
|
|
14177
14375
|
function getRedactionBudgetErrorData(error) {
|
|
14178
|
-
const data =
|
|
14376
|
+
const data = isRecord3(error.data) ? error.data : null;
|
|
14179
14377
|
const inputBytes = getNumberField(data, "inputBytes");
|
|
14180
14378
|
const redactedBytes = getNumberField(data, "redactedBytes");
|
|
14181
14379
|
const ruleIdsValue = data?.ruleIds;
|
|
@@ -14192,7 +14390,7 @@ function getNumberField(record, key) {
|
|
|
14192
14390
|
const value3 = record?.[key];
|
|
14193
14391
|
return typeof value3 === "number" && Number.isFinite(value3) ? value3 : null;
|
|
14194
14392
|
}
|
|
14195
|
-
function
|
|
14393
|
+
function isRecord3(value3) {
|
|
14196
14394
|
return typeof value3 === "object" && value3 !== null;
|
|
14197
14395
|
}
|
|
14198
14396
|
function formatWait(milliseconds) {
|
|
@@ -14280,6 +14478,14 @@ async function uploadSession(request, config) {
|
|
|
14280
14478
|
redactedBytes: filteredText.redactedBytes + (response.redactedBytes ?? 0)
|
|
14281
14479
|
};
|
|
14282
14480
|
} catch (error) {
|
|
14481
|
+
if (error instanceof ORPCError2 && error.status === 400 && isMissingTranscriptTimestampMessage(request.source, error.message)) {
|
|
14482
|
+
return {
|
|
14483
|
+
success: false,
|
|
14484
|
+
error: error.message,
|
|
14485
|
+
attempts: attempt,
|
|
14486
|
+
retryable: false
|
|
14487
|
+
};
|
|
14488
|
+
}
|
|
14283
14489
|
const errorMessage = formatUploadError(error);
|
|
14284
14490
|
if (isRateLimited(error) || isApiKeyRateLimited(error)) {
|
|
14285
14491
|
return {
|
|
@@ -14328,13 +14534,13 @@ function formatRedactionBudgetError(anomaly) {
|
|
|
14328
14534
|
return `Redaction safety check stopped upload: known-pattern redaction would replace ${formatBytes(anomaly.redactedBytes)} of ${formatBytes(anomaly.inputBytes)} (${ratio}%), above the 20% transcript budget (${rules}). The unfiltered transcript was not uploaded.`;
|
|
14329
14535
|
}
|
|
14330
14536
|
function isIngestSessionResponse(value3) {
|
|
14331
|
-
if (!
|
|
14537
|
+
if (!isRecord3(value3) || value3.success !== true) {
|
|
14332
14538
|
return false;
|
|
14333
14539
|
}
|
|
14334
14540
|
if (typeof value3.sessionId !== "string") {
|
|
14335
14541
|
return false;
|
|
14336
14542
|
}
|
|
14337
|
-
if (value3.redacted !== undefined && !
|
|
14543
|
+
if (value3.redacted !== undefined && !isRecord3(value3.redacted)) {
|
|
14338
14544
|
return false;
|
|
14339
14545
|
}
|
|
14340
14546
|
return value3.redactedBytes === undefined || typeof value3.redactedBytes === "number";
|
|
@@ -14382,7 +14588,7 @@ async function runBatchUpload(options) {
|
|
|
14382
14588
|
bar.message(`Retrying ${itemLabel} (${attempt}/${maxAttempts}) after ${error}`);
|
|
14383
14589
|
}
|
|
14384
14590
|
});
|
|
14385
|
-
bar.stop(summary.failed > 0 ? `Completed with ${summary.failed} error(s)` : "Upload complete");
|
|
14591
|
+
bar.stop(summary.failed > 0 ? `Completed with ${summary.failed} error(s)` : summary.skipped > 0 ? `Completed with ${summary.skipped} skipped` : "Upload complete");
|
|
14386
14592
|
return summary;
|
|
14387
14593
|
}
|
|
14388
14594
|
function renderBatchSummary(summary, options) {
|
|
@@ -14405,6 +14611,15 @@ function renderBatchSummary(summary, options) {
|
|
|
14405
14611
|
lines.push(` ...and ${summary.errors.length - maxErrors} more`);
|
|
14406
14612
|
}
|
|
14407
14613
|
}
|
|
14614
|
+
if (summary.skipped > 0) {
|
|
14615
|
+
lines.push(`${prefix}${summary.skipped} session(s) skipped`);
|
|
14616
|
+
for (const item of summary.skippedItems.slice(0, maxErrors)) {
|
|
14617
|
+
lines.push(` ${item.label}: ${item.reason}`);
|
|
14618
|
+
}
|
|
14619
|
+
if (summary.skippedItems.length > maxErrors) {
|
|
14620
|
+
lines.push(` ...and ${summary.skippedItems.length - maxErrors} more`);
|
|
14621
|
+
}
|
|
14622
|
+
}
|
|
14408
14623
|
if (showRetryHint && summary.failed > 0) {
|
|
14409
14624
|
lines.push("");
|
|
14410
14625
|
lines.push("Run `rudel upload --retry` to retry failed uploads.");
|
|
@@ -14416,13 +14631,13 @@ function renderBatchSummary(summary, options) {
|
|
|
14416
14631
|
}
|
|
14417
14632
|
|
|
14418
14633
|
// src/lib/product-analytics.ts
|
|
14419
|
-
import { randomUUID } from "node:crypto";
|
|
14634
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
14420
14635
|
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
14421
14636
|
import { homedir as homedir8 } from "node:os";
|
|
14422
14637
|
import { join as join11 } from "node:path";
|
|
14423
14638
|
|
|
14424
14639
|
// ../../node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
14425
|
-
import { dirname as dirname4, posix, sep } from "path";
|
|
14640
|
+
import { dirname as dirname4, posix, sep as sep2 } from "path";
|
|
14426
14641
|
function createModulerModifier() {
|
|
14427
14642
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
14428
14643
|
return async (frames) => {
|
|
@@ -14431,7 +14646,7 @@ function createModulerModifier() {
|
|
|
14431
14646
|
return frames;
|
|
14432
14647
|
};
|
|
14433
14648
|
}
|
|
14434
|
-
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname4(process.argv[1]) : process.cwd(), isWindows =
|
|
14649
|
+
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname4(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
|
|
14435
14650
|
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
14436
14651
|
return (filename) => {
|
|
14437
14652
|
if (!filename)
|
|
@@ -18938,7 +19153,7 @@ function getOrCreateCliInstallationId() {
|
|
|
18938
19153
|
if (typeof state.cli_installation_id === "string") {
|
|
18939
19154
|
return state.cli_installation_id;
|
|
18940
19155
|
}
|
|
18941
|
-
const cliInstallationId =
|
|
19156
|
+
const cliInstallationId = randomUUID2();
|
|
18942
19157
|
writeAnalyticsState({
|
|
18943
19158
|
...state,
|
|
18944
19159
|
cli_installation_id: cliInstallationId
|
|
@@ -19048,7 +19263,7 @@ var CliProductAnalyticsEvents = PRODUCT_ANALYTICS_EVENTS;
|
|
|
19048
19263
|
|
|
19049
19264
|
// src/lib/project-config.ts
|
|
19050
19265
|
import {
|
|
19051
|
-
chmodSync,
|
|
19266
|
+
chmodSync as chmodSync2,
|
|
19052
19267
|
existsSync as existsSync9,
|
|
19053
19268
|
mkdirSync as mkdirSync4,
|
|
19054
19269
|
readFileSync as readFileSync8,
|
|
@@ -19066,18 +19281,18 @@ function loadProjectsConfig() {
|
|
|
19066
19281
|
const path = getProjectsConfigPath();
|
|
19067
19282
|
if (!existsSync9(path))
|
|
19068
19283
|
return { projects: {} };
|
|
19069
|
-
|
|
19070
|
-
|
|
19284
|
+
chmodSync2(getConfigDir3(), 448);
|
|
19285
|
+
chmodSync2(path, 384);
|
|
19071
19286
|
const content = readFileSync8(path, "utf-8");
|
|
19072
19287
|
return JSON.parse(content);
|
|
19073
19288
|
}
|
|
19074
19289
|
function saveProjectsConfig(config) {
|
|
19075
19290
|
const dir = getConfigDir3();
|
|
19076
19291
|
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
19077
|
-
|
|
19292
|
+
chmodSync2(dir, 448);
|
|
19078
19293
|
const path = getProjectsConfigPath();
|
|
19079
19294
|
writeFileSync5(path, JSON.stringify(config, null, 2), { mode: 384 });
|
|
19080
|
-
|
|
19295
|
+
chmodSync2(path, 384);
|
|
19081
19296
|
}
|
|
19082
19297
|
async function getProjectKey(cwd) {
|
|
19083
19298
|
try {
|
|
@@ -19298,25 +19513,18 @@ async function runEnable() {
|
|
|
19298
19513
|
if (!session) {
|
|
19299
19514
|
return { success: false, error: "Session not found" };
|
|
19300
19515
|
}
|
|
19301
|
-
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
|
|
19307
|
-
|
|
19308
|
-
|
|
19309
|
-
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
|
|
19313
|
-
});
|
|
19314
|
-
} catch (error) {
|
|
19315
|
-
return {
|
|
19316
|
-
success: false,
|
|
19317
|
-
error: error instanceof Error ? error.message : String(error)
|
|
19318
|
-
};
|
|
19319
|
-
}
|
|
19516
|
+
const request = await adapter.buildUploadRequest(session, {
|
|
19517
|
+
gitInfo,
|
|
19518
|
+
organizationId: selectedOrgId,
|
|
19519
|
+
uploadMode: "manual"
|
|
19520
|
+
});
|
|
19521
|
+
return uploadSession(request, {
|
|
19522
|
+
endpoint,
|
|
19523
|
+
token: credentials.token,
|
|
19524
|
+
allowInsecureEndpoint: allowPlaintextEndpoint,
|
|
19525
|
+
authType: credentials.authType,
|
|
19526
|
+
onRetry
|
|
19527
|
+
});
|
|
19320
19528
|
}
|
|
19321
19529
|
});
|
|
19322
19530
|
renderBatchSummary(summary, { context: adapter.name });
|
|
@@ -20940,6 +21148,10 @@ async function reportHookUploadFailure(logger2, result, failure) {
|
|
|
20940
21148
|
sessionId: failure.sessionId,
|
|
20941
21149
|
error: uploadError
|
|
20942
21150
|
});
|
|
21151
|
+
if (result.retryable === false) {
|
|
21152
|
+
await removeFailedUpload(failure.sessionId);
|
|
21153
|
+
return;
|
|
21154
|
+
}
|
|
20943
21155
|
if (result.endpointRejected) {
|
|
20944
21156
|
process.stderr.write(`Rudel hook upload refused for session ${failure.sessionId}: ${uploadError}
|
|
20945
21157
|
`);
|
|
@@ -22153,12 +22365,20 @@ async function runSingleUpload(flags, session, allowPlaintextEndpoint) {
|
|
|
22153
22365
|
transcriptPath: sessionInfo.transcriptPath,
|
|
22154
22366
|
projectPath: sessionInfo.projectPath
|
|
22155
22367
|
};
|
|
22156
|
-
|
|
22157
|
-
|
|
22158
|
-
|
|
22159
|
-
|
|
22160
|
-
|
|
22161
|
-
|
|
22368
|
+
let request;
|
|
22369
|
+
try {
|
|
22370
|
+
request = await claudeCodeAdapter.buildUploadRequest(sessionFile, {
|
|
22371
|
+
tag: flags.tag,
|
|
22372
|
+
gitInfo,
|
|
22373
|
+
organizationId,
|
|
22374
|
+
uploadMode: "manual"
|
|
22375
|
+
});
|
|
22376
|
+
} catch (error) {
|
|
22377
|
+
if (error instanceof MissingTranscriptTimestampError) {
|
|
22378
|
+
return new Error("This transcript has no timestamped user/assistant messages, so it cannot be uploaded.");
|
|
22379
|
+
}
|
|
22380
|
+
throw error;
|
|
22381
|
+
}
|
|
22162
22382
|
write(`Transcript: ${request.content.length} bytes`);
|
|
22163
22383
|
if (request.subagents && request.subagents.length > 0) {
|
|
22164
22384
|
write(`Subagents: ${request.subagents.length} file(s)`);
|