rudel 0.2.2 → 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 +299 -112
- 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;
|
|
@@ -11366,74 +11462,90 @@ var WrappedDecimalClaimEntitlementSchema = exports_external.object({
|
|
|
11366
11462
|
});
|
|
11367
11463
|
|
|
11368
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);
|
|
11369
11478
|
var WrappedShareThemeSchema = exports_external.enum(["dark", "light", "muted"]);
|
|
11370
11479
|
var WrappedShareVariantSchema = exports_external.enum(["normal", "decimal"]);
|
|
11371
11480
|
var WrappedShareLayoutModeSchema = exports_external.enum(["front", "front_back"]);
|
|
11372
11481
|
var WrappedShareIdSchema = exports_external.string().trim().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/u);
|
|
11373
11482
|
var WrappedShareHeaderMetricSchema = exports_external.object({
|
|
11374
|
-
label:
|
|
11375
|
-
title:
|
|
11376
|
-
value:
|
|
11377
|
-
});
|
|
11483
|
+
label: WrappedShareTextSchema.optional(),
|
|
11484
|
+
title: WrappedShareTextSchema.optional(),
|
|
11485
|
+
value: WrappedShareRequiredTextSchema
|
|
11486
|
+
}).strict();
|
|
11378
11487
|
var WrappedShareStatItemIconSchema = exports_external.enum(["claude", "codex"]);
|
|
11379
11488
|
var WrappedShareStatItemSchema = exports_external.object({
|
|
11380
11489
|
icon: WrappedShareStatItemIconSchema.optional(),
|
|
11381
|
-
key:
|
|
11382
|
-
label:
|
|
11383
|
-
title:
|
|
11384
|
-
value:
|
|
11385
|
-
});
|
|
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();
|
|
11386
11495
|
var WrappedShareBackMetricSchema = exports_external.object({
|
|
11387
|
-
label:
|
|
11496
|
+
label: WrappedShareTextSchema,
|
|
11388
11497
|
slot: exports_external.enum(["body", "footer"]).optional(),
|
|
11389
|
-
value:
|
|
11390
|
-
});
|
|
11498
|
+
value: WrappedShareRequiredTextSchema
|
|
11499
|
+
}).strict();
|
|
11391
11500
|
var WrappedShareRevealMetricsSchema = exports_external.object({
|
|
11392
11501
|
avgSessionMin: exports_external.number().nonnegative().nullable(),
|
|
11393
11502
|
commitRate: exports_external.number().min(0).max(100).nullable(),
|
|
11394
11503
|
daysSinceFirst: exports_external.number().nonnegative(),
|
|
11395
11504
|
distinctProjectCount: exports_external.number().nonnegative(),
|
|
11396
11505
|
longestSessionMin: exports_external.number().nonnegative().nullable()
|
|
11397
|
-
});
|
|
11506
|
+
}).strict();
|
|
11398
11507
|
var WrappedShareAppearanceSchema = exports_external.object({
|
|
11399
11508
|
layoutMode: WrappedShareLayoutModeSchema,
|
|
11400
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`
|
|
11401
11513
|
});
|
|
11402
|
-
var WrappedShareSocialImageDataUrlSchema = exports_external.string().max(7000000).regex(/^data:image\/png;base64,[A-Za-z0-9+/]+={0,2}$/u);
|
|
11403
11514
|
var WrappedShareRowSchema = exports_external.object({
|
|
11404
11515
|
activeDays: exports_external.number().nonnegative(),
|
|
11405
11516
|
cost: exports_external.number().nonnegative(),
|
|
11406
|
-
displayName:
|
|
11407
|
-
favoriteModel:
|
|
11517
|
+
displayName: WrappedShareRequiredTextSchema,
|
|
11518
|
+
favoriteModel: WrappedShareTextSchema.nullable(),
|
|
11408
11519
|
hasActivity: exports_external.boolean(),
|
|
11409
|
-
imageUrl: exports_external.string().nullable(),
|
|
11520
|
+
imageUrl: exports_external.string().max(WRAPPED_SHARE_RESOURCE_LIMITS.imageUrlLength).nullable(),
|
|
11410
11521
|
inputTokens: exports_external.number().nonnegative(),
|
|
11411
|
-
lastActiveDate:
|
|
11522
|
+
lastActiveDate: WrappedShareTextSchema.nullable(),
|
|
11412
11523
|
outputTokens: exports_external.number().nonnegative(),
|
|
11413
|
-
role:
|
|
11524
|
+
role: WrappedShareRequiredTextSchema,
|
|
11414
11525
|
totalSessions: exports_external.number().nonnegative(),
|
|
11415
11526
|
totalTokens: exports_external.number().nonnegative()
|
|
11416
|
-
});
|
|
11417
|
-
var
|
|
11527
|
+
}).strict();
|
|
11528
|
+
var WrappedShareSnapshotObjectSchema = exports_external.object({
|
|
11418
11529
|
appearance: WrappedShareAppearanceSchema.optional(),
|
|
11419
|
-
archetypeLabel:
|
|
11420
|
-
backMetrics: exports_external.array(WrappedShareBackMetricSchema).optional(),
|
|
11530
|
+
archetypeLabel: WrappedShareRequiredTextSchema,
|
|
11531
|
+
backMetrics: exports_external.array(WrappedShareBackMetricSchema).max(WRAPPED_SHARE_RESOURCE_LIMITS.backMetricCount).optional(),
|
|
11421
11532
|
headerLeftMetric: WrappedShareHeaderMetricSchema.optional(),
|
|
11422
11533
|
headerRightMetric: WrappedShareHeaderMetricSchema.optional(),
|
|
11423
11534
|
revealMetrics: WrappedShareRevealMetricsSchema.optional(),
|
|
11424
11535
|
row: WrappedShareRowSchema,
|
|
11425
|
-
shellClassName: exports_external.string().min(1),
|
|
11426
|
-
|
|
11427
|
-
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),
|
|
11428
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`
|
|
11429
11542
|
});
|
|
11430
|
-
var PublicWrappedShareSnapshotSchema = WrappedShareSnapshotSchema
|
|
11431
|
-
socialImageDataUrl: true
|
|
11432
|
-
});
|
|
11543
|
+
var PublicWrappedShareSnapshotSchema = WrappedShareSnapshotSchema;
|
|
11433
11544
|
var CreateWrappedShareInputSchema = exports_external.object({
|
|
11545
|
+
socialImageDataUrl: WrappedShareSocialImageDataUrlSchema.optional(),
|
|
11434
11546
|
snapshot: WrappedShareSnapshotSchema,
|
|
11435
11547
|
variant: WrappedShareVariantSchema.default("normal")
|
|
11436
|
-
});
|
|
11548
|
+
}).strict();
|
|
11437
11549
|
var GetPublicWrappedShareInputSchema = exports_external.object({
|
|
11438
11550
|
shareId: WrappedShareIdSchema
|
|
11439
11551
|
});
|
|
@@ -11446,6 +11558,22 @@ var WrappedShareRecordSchema = exports_external.object({
|
|
|
11446
11558
|
var PublicWrappedShareSchema = WrappedShareRecordSchema.extend({
|
|
11447
11559
|
snapshot: PublicWrappedShareSnapshotSchema
|
|
11448
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
|
+
}
|
|
11449
11577
|
|
|
11450
11578
|
// ../../packages/api-routes/src/schemas/wrapped-resume.ts
|
|
11451
11579
|
var CreateWrappedResumeInputSchema = exports_external.object({
|
|
@@ -11638,6 +11766,10 @@ var CliUserSchema = exports_external.object({
|
|
|
11638
11766
|
var CliSetupStatusSchema = exports_external.object({
|
|
11639
11767
|
hasCliLogin: exports_external.boolean()
|
|
11640
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
|
+
});
|
|
11641
11773
|
var OrganizationSchema = exports_external.object({
|
|
11642
11774
|
id: exports_external.string(),
|
|
11643
11775
|
name: exports_external.string(),
|
|
@@ -11645,6 +11777,7 @@ var OrganizationSchema = exports_external.object({
|
|
|
11645
11777
|
logo: exports_external.string().nullable()
|
|
11646
11778
|
});
|
|
11647
11779
|
var TeamInviteLinkSchema = exports_external.object({
|
|
11780
|
+
expires_at: exports_external.string().datetime(),
|
|
11648
11781
|
invite_url: exports_external.string().url(),
|
|
11649
11782
|
organization_id: exports_external.string(),
|
|
11650
11783
|
organization_name: exports_external.string()
|
|
@@ -11731,6 +11864,9 @@ var contract = {
|
|
|
11731
11864
|
revokeToken: oc.output(exports_external.object({ success: exports_external.literal(true) })),
|
|
11732
11865
|
setupStatus: oc.output(CliSetupStatusSchema)
|
|
11733
11866
|
},
|
|
11867
|
+
chatwoot: {
|
|
11868
|
+
identity: oc.output(ChatwootIdentitySchema.nullable())
|
|
11869
|
+
},
|
|
11734
11870
|
listMyOrganizations: oc.output(exports_external.array(OrganizationSchema)),
|
|
11735
11871
|
ingestSession: oc.input(IngestSessionInputSchema).output(IngestSessionOutputSchema).errors({
|
|
11736
11872
|
[REDACTION_BUDGET_EXCEEDED_CODE]: {
|
|
@@ -11757,7 +11893,8 @@ var contract = {
|
|
|
11757
11893
|
getOrganizationSessionCount: oc.input(exports_external.object({ organizationId: exports_external.string(), userId: exports_external.string().optional() })).output(exports_external.object({ count: exports_external.number() })),
|
|
11758
11894
|
deleteOrganization: oc.input(exports_external.object({ organizationId: exports_external.string() })).output(exports_external.object({ success: exports_external.literal(true) })),
|
|
11759
11895
|
teamInviteLink: {
|
|
11760
|
-
|
|
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) })),
|
|
11761
11898
|
accept: oc.input(exports_external.object({ token: exports_external.string().min(1) })).output(TeamInviteAcceptResultSchema)
|
|
11762
11899
|
},
|
|
11763
11900
|
wrappedShare: {
|
|
@@ -13999,14 +14136,17 @@ async function batchUpload(options) {
|
|
|
13999
14136
|
let succeeded = 0;
|
|
14000
14137
|
let failed = 0;
|
|
14001
14138
|
let skipped = 0;
|
|
14139
|
+
let deferred = 0;
|
|
14002
14140
|
let completed = 0;
|
|
14003
14141
|
let rateLimited = false;
|
|
14004
14142
|
const errors2 = [];
|
|
14143
|
+
const skippedItems = [];
|
|
14144
|
+
const skippedSessionIds = new Set;
|
|
14005
14145
|
let redacted = {};
|
|
14006
14146
|
let redactedBytes = 0;
|
|
14007
14147
|
await pMap(items, async (item) => {
|
|
14008
14148
|
if (rateLimited) {
|
|
14009
|
-
|
|
14149
|
+
deferred++;
|
|
14010
14150
|
const error = "Skipped — rate limit reached. Run `rudel upload --retry` to upload remaining sessions.";
|
|
14011
14151
|
await recordFailedUpload({
|
|
14012
14152
|
sessionId: item.sessionId,
|
|
@@ -14030,6 +14170,13 @@ async function batchUpload(options) {
|
|
|
14030
14170
|
redacted = mergeRedactionCounts(redacted, result.redacted ?? {});
|
|
14031
14171
|
redactedBytes += result.redactedBytes ?? 0;
|
|
14032
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);
|
|
14033
14180
|
} else {
|
|
14034
14181
|
failed++;
|
|
14035
14182
|
const error = result.error ?? "Unknown error";
|
|
@@ -14047,30 +14194,48 @@ async function batchUpload(options) {
|
|
|
14047
14194
|
});
|
|
14048
14195
|
}
|
|
14049
14196
|
} catch (err) {
|
|
14050
|
-
failed++;
|
|
14051
14197
|
const error = err instanceof Error ? err.message : String(err);
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14055
|
-
|
|
14056
|
-
|
|
14057
|
-
|
|
14058
|
-
|
|
14059
|
-
|
|
14060
|
-
|
|
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
|
+
}
|
|
14061
14214
|
} finally {
|
|
14062
14215
|
completed++;
|
|
14063
14216
|
onItemComplete?.(completed, total);
|
|
14064
14217
|
}
|
|
14065
14218
|
}, { concurrency, stopOnError: false });
|
|
14066
|
-
|
|
14219
|
+
for (const sessionId of skippedSessionIds) {
|
|
14220
|
+
await removeFailedUpload(sessionId);
|
|
14221
|
+
}
|
|
14222
|
+
if (rateLimited && deferred > 0) {
|
|
14067
14223
|
errors2.push({
|
|
14068
14224
|
label: "Rate limit",
|
|
14069
|
-
error: `${
|
|
14225
|
+
error: `${deferred} session(s) skipped. Run \`rudel upload --retry\` later to upload them.`
|
|
14070
14226
|
});
|
|
14071
|
-
failed +=
|
|
14227
|
+
failed += deferred;
|
|
14072
14228
|
}
|
|
14073
|
-
return {
|
|
14229
|
+
return {
|
|
14230
|
+
succeeded,
|
|
14231
|
+
failed,
|
|
14232
|
+
skipped,
|
|
14233
|
+
total,
|
|
14234
|
+
errors: errors2,
|
|
14235
|
+
skippedItems,
|
|
14236
|
+
redacted,
|
|
14237
|
+
redactedBytes
|
|
14238
|
+
};
|
|
14074
14239
|
}
|
|
14075
14240
|
|
|
14076
14241
|
// src/lib/upload-endpoint.ts
|
|
@@ -14189,13 +14354,13 @@ function formatServerUploadError(error) {
|
|
|
14189
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.`;
|
|
14190
14355
|
}
|
|
14191
14356
|
function getPayloadTooLargeDetail(error) {
|
|
14192
|
-
const data =
|
|
14357
|
+
const data = isRecord3(error.data) ? error.data : null;
|
|
14193
14358
|
const bodyValue = data?.body;
|
|
14194
|
-
const body =
|
|
14359
|
+
const body = isRecord3(bodyValue) ? bodyValue : null;
|
|
14195
14360
|
return getStringField(body, "error") ?? getStringField(data, "error");
|
|
14196
14361
|
}
|
|
14197
14362
|
function getErrorData(error) {
|
|
14198
|
-
const data =
|
|
14363
|
+
const data = isRecord3(error.data) ? error.data : null;
|
|
14199
14364
|
return {
|
|
14200
14365
|
authMessage: getStringField(data, "authMessage"),
|
|
14201
14366
|
actualBytes: getNumberField(data, "actualBytes"),
|
|
@@ -14208,7 +14373,7 @@ function getErrorData(error) {
|
|
|
14208
14373
|
};
|
|
14209
14374
|
}
|
|
14210
14375
|
function getRedactionBudgetErrorData(error) {
|
|
14211
|
-
const data =
|
|
14376
|
+
const data = isRecord3(error.data) ? error.data : null;
|
|
14212
14377
|
const inputBytes = getNumberField(data, "inputBytes");
|
|
14213
14378
|
const redactedBytes = getNumberField(data, "redactedBytes");
|
|
14214
14379
|
const ruleIdsValue = data?.ruleIds;
|
|
@@ -14225,7 +14390,7 @@ function getNumberField(record, key) {
|
|
|
14225
14390
|
const value3 = record?.[key];
|
|
14226
14391
|
return typeof value3 === "number" && Number.isFinite(value3) ? value3 : null;
|
|
14227
14392
|
}
|
|
14228
|
-
function
|
|
14393
|
+
function isRecord3(value3) {
|
|
14229
14394
|
return typeof value3 === "object" && value3 !== null;
|
|
14230
14395
|
}
|
|
14231
14396
|
function formatWait(milliseconds) {
|
|
@@ -14313,6 +14478,14 @@ async function uploadSession(request, config) {
|
|
|
14313
14478
|
redactedBytes: filteredText.redactedBytes + (response.redactedBytes ?? 0)
|
|
14314
14479
|
};
|
|
14315
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
|
+
}
|
|
14316
14489
|
const errorMessage = formatUploadError(error);
|
|
14317
14490
|
if (isRateLimited(error) || isApiKeyRateLimited(error)) {
|
|
14318
14491
|
return {
|
|
@@ -14361,13 +14534,13 @@ function formatRedactionBudgetError(anomaly) {
|
|
|
14361
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.`;
|
|
14362
14535
|
}
|
|
14363
14536
|
function isIngestSessionResponse(value3) {
|
|
14364
|
-
if (!
|
|
14537
|
+
if (!isRecord3(value3) || value3.success !== true) {
|
|
14365
14538
|
return false;
|
|
14366
14539
|
}
|
|
14367
14540
|
if (typeof value3.sessionId !== "string") {
|
|
14368
14541
|
return false;
|
|
14369
14542
|
}
|
|
14370
|
-
if (value3.redacted !== undefined && !
|
|
14543
|
+
if (value3.redacted !== undefined && !isRecord3(value3.redacted)) {
|
|
14371
14544
|
return false;
|
|
14372
14545
|
}
|
|
14373
14546
|
return value3.redactedBytes === undefined || typeof value3.redactedBytes === "number";
|
|
@@ -14415,7 +14588,7 @@ async function runBatchUpload(options) {
|
|
|
14415
14588
|
bar.message(`Retrying ${itemLabel} (${attempt}/${maxAttempts}) after ${error}`);
|
|
14416
14589
|
}
|
|
14417
14590
|
});
|
|
14418
|
-
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");
|
|
14419
14592
|
return summary;
|
|
14420
14593
|
}
|
|
14421
14594
|
function renderBatchSummary(summary, options) {
|
|
@@ -14438,6 +14611,15 @@ function renderBatchSummary(summary, options) {
|
|
|
14438
14611
|
lines.push(` ...and ${summary.errors.length - maxErrors} more`);
|
|
14439
14612
|
}
|
|
14440
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
|
+
}
|
|
14441
14623
|
if (showRetryHint && summary.failed > 0) {
|
|
14442
14624
|
lines.push("");
|
|
14443
14625
|
lines.push("Run `rudel upload --retry` to retry failed uploads.");
|
|
@@ -14455,7 +14637,7 @@ import { homedir as homedir8 } from "node:os";
|
|
|
14455
14637
|
import { join as join11 } from "node:path";
|
|
14456
14638
|
|
|
14457
14639
|
// ../../node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
14458
|
-
import { dirname as dirname4, posix, sep } from "path";
|
|
14640
|
+
import { dirname as dirname4, posix, sep as sep2 } from "path";
|
|
14459
14641
|
function createModulerModifier() {
|
|
14460
14642
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
14461
14643
|
return async (frames) => {
|
|
@@ -14464,7 +14646,7 @@ function createModulerModifier() {
|
|
|
14464
14646
|
return frames;
|
|
14465
14647
|
};
|
|
14466
14648
|
}
|
|
14467
|
-
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 === "\\") {
|
|
14468
14650
|
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
14469
14651
|
return (filename) => {
|
|
14470
14652
|
if (!filename)
|
|
@@ -19331,25 +19513,18 @@ async function runEnable() {
|
|
|
19331
19513
|
if (!session) {
|
|
19332
19514
|
return { success: false, error: "Session not found" };
|
|
19333
19515
|
}
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
19340
|
-
|
|
19341
|
-
|
|
19342
|
-
|
|
19343
|
-
|
|
19344
|
-
|
|
19345
|
-
|
|
19346
|
-
});
|
|
19347
|
-
} catch (error) {
|
|
19348
|
-
return {
|
|
19349
|
-
success: false,
|
|
19350
|
-
error: error instanceof Error ? error.message : String(error)
|
|
19351
|
-
};
|
|
19352
|
-
}
|
|
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
|
+
});
|
|
19353
19528
|
}
|
|
19354
19529
|
});
|
|
19355
19530
|
renderBatchSummary(summary, { context: adapter.name });
|
|
@@ -20973,6 +21148,10 @@ async function reportHookUploadFailure(logger2, result, failure) {
|
|
|
20973
21148
|
sessionId: failure.sessionId,
|
|
20974
21149
|
error: uploadError
|
|
20975
21150
|
});
|
|
21151
|
+
if (result.retryable === false) {
|
|
21152
|
+
await removeFailedUpload(failure.sessionId);
|
|
21153
|
+
return;
|
|
21154
|
+
}
|
|
20976
21155
|
if (result.endpointRejected) {
|
|
20977
21156
|
process.stderr.write(`Rudel hook upload refused for session ${failure.sessionId}: ${uploadError}
|
|
20978
21157
|
`);
|
|
@@ -22186,12 +22365,20 @@ async function runSingleUpload(flags, session, allowPlaintextEndpoint) {
|
|
|
22186
22365
|
transcriptPath: sessionInfo.transcriptPath,
|
|
22187
22366
|
projectPath: sessionInfo.projectPath
|
|
22188
22367
|
};
|
|
22189
|
-
|
|
22190
|
-
|
|
22191
|
-
|
|
22192
|
-
|
|
22193
|
-
|
|
22194
|
-
|
|
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
|
+
}
|
|
22195
22382
|
write(`Transcript: ${request.content.length} bytes`);
|
|
22196
22383
|
if (request.subagents && request.subagents.length > 0) {
|
|
22197
22384
|
write(`Subagents: ${request.subagents.length} file(s)`);
|