codiedev 0.9.25 → 0.9.28
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 +32 -4
- package/assets/opencode-plugin.mjs +54 -0
- package/assets/tui-brand-plugin.tsx +63 -0
- package/dist/backup/cache.js +2 -2
- package/dist/backup/run.js +5 -0
- package/dist/captureExclusions.d.ts +30 -0
- package/dist/captureExclusions.js +118 -0
- package/dist/captureLog.js +6 -3
- package/dist/cli.js +38 -0
- package/dist/codie.d.ts +2 -0
- package/dist/codie.js +233 -0
- package/dist/codieLauncher.d.ts +41 -0
- package/dist/codieLauncher.js +352 -0
- package/dist/codieRuntime.d.ts +19 -0
- package/dist/codieRuntime.js +91 -0
- package/dist/codieRuntimeInstall.d.ts +27 -0
- package/dist/codieRuntimeInstall.js +145 -0
- package/dist/commands/capture.d.ts +14 -0
- package/dist/commands/capture.js +148 -0
- package/dist/configDir.d.ts +1 -0
- package/dist/configDir.js +54 -0
- package/dist/connect.js +27 -2
- package/dist/hook.js +77 -43
- package/dist/upgradeNudge.js +6 -5
- package/dist/utils.d.ts +30 -3
- package/dist/utils.js +313 -22
- package/package.json +13 -5
package/dist/hook.js
CHANGED
|
@@ -45,36 +45,7 @@ const utils_1 = require("./utils");
|
|
|
45
45
|
const limitEvent_1 = require("./limitEvent");
|
|
46
46
|
const version_1 = require("./version");
|
|
47
47
|
const captureLog_1 = require("./captureLog");
|
|
48
|
-
|
|
49
|
-
return new Promise((resolve, reject) => {
|
|
50
|
-
const parsed = new URL(url);
|
|
51
|
-
const options = {
|
|
52
|
-
hostname: parsed.hostname,
|
|
53
|
-
port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
|
|
54
|
-
path: parsed.pathname + parsed.search,
|
|
55
|
-
method: "GET",
|
|
56
|
-
headers: {
|
|
57
|
-
Authorization: `Bearer ${bearerToken}`,
|
|
58
|
-
"X-Codiedev-Cli-Version": version_1.CLI_VERSION,
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
const lib = parsed.protocol === "https:" ? https : http;
|
|
62
|
-
const req = lib.request(options, (res) => {
|
|
63
|
-
let body = "";
|
|
64
|
-
res.on("data", (c) => (body += c));
|
|
65
|
-
res.on("end", () => {
|
|
66
|
-
try {
|
|
67
|
-
resolve(JSON.parse(body));
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
reject(new Error("bad json"));
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
req.on("error", reject);
|
|
75
|
-
req.end();
|
|
76
|
-
});
|
|
77
|
-
}
|
|
48
|
+
const captureExclusions_1 = require("./captureExclusions");
|
|
78
49
|
// Pure builder for the discovery additionalContext. Returns null when there are
|
|
79
50
|
// no matches. The suggested `codiedev pull` command carries the EXACT top key
|
|
80
51
|
// (e.g. "pr-push.md") rather than a "<key>" placeholder — otherwise agents
|
|
@@ -105,7 +76,10 @@ async function runDiscover(prompt) {
|
|
|
105
76
|
if (!config)
|
|
106
77
|
process.exit(0);
|
|
107
78
|
try {
|
|
108
|
-
|
|
79
|
+
// POST body, never a query param: raw prompts in GET query strings land
|
|
80
|
+
// in server/proxy access logs (SIG-16). The server keeps accepting legacy
|
|
81
|
+
// GETs from older CLIs during the transition.
|
|
82
|
+
const res = await postJsonForResponse(`${config.backendUrl}/api/cli/skill-discovery`, { q: prompt }, config.token);
|
|
109
83
|
const skills = res?.skills ?? [];
|
|
110
84
|
const additionalContext = buildDiscoveryContext(skills);
|
|
111
85
|
if (additionalContext) {
|
|
@@ -176,7 +150,7 @@ async function postSessionSkip(config, sessionId, reason) {
|
|
|
176
150
|
// so the backend can later compute captured ÷ started. Deliberately tiny — no
|
|
177
151
|
// transcript read, no git — so it never slows session startup. Best-effort +
|
|
178
152
|
// silent (SessionStart hook output is ignored by Claude Code).
|
|
179
|
-
async function runSessionStart(hookInput) {
|
|
153
|
+
async function runSessionStart(hookInput, source = "claude-code") {
|
|
180
154
|
const config = (0, utils_1.readConfig)();
|
|
181
155
|
if (!config)
|
|
182
156
|
process.exit(0);
|
|
@@ -185,7 +159,7 @@ async function runSessionStart(hookInput) {
|
|
|
185
159
|
await postJson(`${config.backendUrl}/api/cli/sessionStart`, {
|
|
186
160
|
sessionId: hookInput.session_id,
|
|
187
161
|
startedAt: Date.now(),
|
|
188
|
-
source
|
|
162
|
+
source,
|
|
189
163
|
}, config.token, PING_TIMEOUT_MS);
|
|
190
164
|
}
|
|
191
165
|
catch {
|
|
@@ -339,6 +313,10 @@ function resolveMode(arg) {
|
|
|
339
313
|
return "codex";
|
|
340
314
|
if (arg === "capture-cursor")
|
|
341
315
|
return "cursor";
|
|
316
|
+
if (arg === "capture-opencode" || arg === "capture-opencode-final")
|
|
317
|
+
return "opencode";
|
|
318
|
+
if (arg === "capture-desktop" || arg === "capture-desktop-final")
|
|
319
|
+
return "desktop";
|
|
342
320
|
return "claude-code";
|
|
343
321
|
}
|
|
344
322
|
async function main() {
|
|
@@ -368,6 +346,12 @@ async function main() {
|
|
|
368
346
|
// Write per-session upload state so the throttle can defer later
|
|
369
347
|
// non-final uploads within the same window.
|
|
370
348
|
writeSessionState: utils_1.writeSessionUploadState,
|
|
349
|
+
// Re-read local config in the uploader so adding an exclusion after a
|
|
350
|
+
// job was queued still prevents its transcript from leaving the device.
|
|
351
|
+
shouldSkipJob: (job) => {
|
|
352
|
+
const config = (0, utils_1.readConfig)();
|
|
353
|
+
return (0, captureExclusions_1.shouldSkipCaptureUploadJob)(job, config);
|
|
354
|
+
},
|
|
371
355
|
};
|
|
372
356
|
}
|
|
373
357
|
// Detached uploader child (spawned by the capture path below). The job —
|
|
@@ -441,6 +425,13 @@ async function main() {
|
|
|
441
425
|
catch {
|
|
442
426
|
process.exit(0);
|
|
443
427
|
}
|
|
428
|
+
// Local privacy boundary: every stdin-driven hook mode passes through this
|
|
429
|
+
// check BEFORE prompt discovery, limit-event tails, SessionStart pings,
|
|
430
|
+
// backup, self-heal, transcript reads, or detached dispatch. Ignored means
|
|
431
|
+
// no Codiedev request at all for this session.
|
|
432
|
+
if ((0, captureExclusions_1.shouldIgnoreCaptureInput)((0, utils_1.readConfig)(), hookInput)) {
|
|
433
|
+
process.exit(0);
|
|
434
|
+
}
|
|
444
435
|
// UserPromptSubmit discovery path — separate from session capture.
|
|
445
436
|
if (process.argv[2] === "discover") {
|
|
446
437
|
await runDiscover(hookInput.prompt);
|
|
@@ -452,8 +443,8 @@ async function main() {
|
|
|
452
443
|
return;
|
|
453
444
|
}
|
|
454
445
|
// SessionStart: post the capture-coverage denominator ping. Tiny + fast.
|
|
455
|
-
if (process.argv[2] === "session-start") {
|
|
456
|
-
await runSessionStart(hookInput);
|
|
446
|
+
if (process.argv[2] === "session-start" || process.argv[2] === "session-start-opencode") {
|
|
447
|
+
await runSessionStart(hookInput, process.argv[2] === "session-start-opencode" ? "opencode" : "claude-code");
|
|
457
448
|
return;
|
|
458
449
|
}
|
|
459
450
|
// Self-heal: a user who upgraded the CLI but hasn't re-run `connect` gets
|
|
@@ -563,6 +554,17 @@ async function runCaptureFinish(input) {
|
|
|
563
554
|
const config = (0, utils_1.readConfig)();
|
|
564
555
|
if (!config)
|
|
565
556
|
return;
|
|
557
|
+
// Re-check in the detached child: the user may have added an exclusion
|
|
558
|
+
// after the parent hook dispatched this job but before it started.
|
|
559
|
+
if ((0, captureExclusions_1.shouldIgnoreCaptureInput)(config, input)) {
|
|
560
|
+
(0, captureLog_1.appendCaptureLog)({
|
|
561
|
+
stage: "capture-finish",
|
|
562
|
+
sessionId: input.session_id,
|
|
563
|
+
outcome: "skip",
|
|
564
|
+
detail: "ignored-path",
|
|
565
|
+
});
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
566
568
|
const { session_id, transcript_path, cwd, workspace_roots } = input;
|
|
567
569
|
if (!transcript_path) {
|
|
568
570
|
await postSessionSkip(config, session_id, "no-transcript-path");
|
|
@@ -585,9 +587,15 @@ async function runCaptureFinish(input) {
|
|
|
585
587
|
}
|
|
586
588
|
async function runCaptureFinishInner(input, config, session_id, transcript_path, cwd, workspace_roots) {
|
|
587
589
|
const mode = resolveMode(input.mode);
|
|
590
|
+
// Desktop transcripts (opencode SDK {info,parts}[] dumps) are byte-identical
|
|
591
|
+
// to opencode's, so every parser below EXCEPT parseUsage treats desktop
|
|
592
|
+
// exactly as opencode via this mapping. parseUsage is the one exception —
|
|
593
|
+
// see the rawTranscriptContent note below.
|
|
594
|
+
const parserMode = mode === "desktop" ? "opencode" : mode;
|
|
588
595
|
const workspaceRoot = workspace_roots && workspace_roots.length > 0
|
|
589
596
|
? workspace_roots[0]
|
|
590
597
|
: cwd || process.cwd();
|
|
598
|
+
const scopePaths = (0, captureExclusions_1.captureScopePaths)({ cwd, workspace_roots }, workspaceRoot);
|
|
591
599
|
let transcriptContent;
|
|
592
600
|
try {
|
|
593
601
|
transcriptContent = fs.readFileSync(transcript_path, "utf8");
|
|
@@ -606,10 +614,19 @@ async function runCaptureFinishInner(input, config, session_id, transcript_path,
|
|
|
606
614
|
await postSessionSkip(config, session_id, reason);
|
|
607
615
|
return;
|
|
608
616
|
}
|
|
617
|
+
// Snapshot the RAW transcript before normalization below flattens it to
|
|
618
|
+
// Claude Code's newline-JSONL shape — that flattening drops info.cost /
|
|
619
|
+
// info.providerID, which desktop's parseUsage (below) needs and opencode's
|
|
620
|
+
// doesn't. Every other mode leaves transcriptContent untouched here, so this
|
|
621
|
+
// is a no-op for them.
|
|
622
|
+
const rawTranscriptContent = transcriptContent;
|
|
623
|
+
if (parserMode === "opencode") {
|
|
624
|
+
transcriptContent = (0, utils_1.normalizeOpenCodeTranscript)(transcriptContent);
|
|
625
|
+
}
|
|
609
626
|
// Resolve repo URL. Codex embeds it in session_meta; CC and Cursor both
|
|
610
627
|
// need the git remote of the workspace.
|
|
611
628
|
let remoteUrl = null;
|
|
612
|
-
if (
|
|
629
|
+
if (parserMode === "codex") {
|
|
613
630
|
remoteUrl = (0, utils_1.extractCodexRepoUrl)(transcriptContent);
|
|
614
631
|
}
|
|
615
632
|
// The worktrees the session actually wrote to — resolved up front because
|
|
@@ -630,9 +647,9 @@ async function runCaptureFinishInner(input, config, session_id, transcript_path,
|
|
|
630
647
|
// Best-effort local match — backend re-resolves from `remoteUrl` against the
|
|
631
648
|
// company's current repo list, so a stale/empty local cache doesn't matter.
|
|
632
649
|
const matchedRepo = remoteUrl ? (0, utils_1.matchRepo)(remoteUrl, config.repos) : null;
|
|
633
|
-
const stats =
|
|
650
|
+
const stats = parserMode === "codex"
|
|
634
651
|
? (0, utils_1.parseCodexStats)(transcriptContent)
|
|
635
|
-
:
|
|
652
|
+
: parserMode === "cursor"
|
|
636
653
|
? { messageCount: 0, toolCallCount: 0 }
|
|
637
654
|
: (0, utils_1.parseClaudeCodeStats)(transcriptContent);
|
|
638
655
|
const transcriptBase64 = Buffer.from(transcriptContent, "utf8").toString("base64");
|
|
@@ -662,10 +679,13 @@ async function runCaptureFinishInner(input, config, session_id, transcript_path,
|
|
|
662
679
|
const headShas = [...new Set([...keys.headShas, ...authoredShas])];
|
|
663
680
|
const headSha = keys.headSha ?? headShas[0] ?? null;
|
|
664
681
|
// Cost/velocity/steering signals parsed straight from the transcript we're
|
|
665
|
-
// already holding — no extra hooks, no extra git/process calls.
|
|
666
|
-
|
|
682
|
+
// already holding — no extra hooks, no extra git/process calls. parseUsage
|
|
683
|
+
// keeps the literal mode (it has its own desktop parser) and reads the RAW
|
|
684
|
+
// pre-normalization content for desktop; parseSteering routes through
|
|
685
|
+
// parserMode like the parsers above.
|
|
686
|
+
const usage = (0, utils_1.parseUsage)(mode === "desktop" ? rawTranscriptContent : transcriptContent, mode);
|
|
667
687
|
const sessionStartedAt = (0, utils_1.parseSessionStart)(transcriptContent);
|
|
668
|
-
const steering = (0, utils_1.parseSteering)(transcriptContent,
|
|
688
|
+
const steering = (0, utils_1.parseSteering)(transcriptContent, parserMode);
|
|
669
689
|
const payload = (0, utils_1.buildCapturePayload)({
|
|
670
690
|
sessionId: session_id,
|
|
671
691
|
transcriptBase64,
|
|
@@ -683,6 +703,8 @@ async function runCaptureFinishInner(input, config, session_id, transcript_path,
|
|
|
683
703
|
inputTokens: usage.inputTokens,
|
|
684
704
|
outputTokens: usage.outputTokens,
|
|
685
705
|
cacheTokens: usage.cacheTokens,
|
|
706
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
707
|
+
cacheCreationTokens: usage.cacheCreationTokens,
|
|
686
708
|
model: usage.model,
|
|
687
709
|
sessionStartedAt,
|
|
688
710
|
interruptCount: steering.interrupts,
|
|
@@ -690,6 +712,8 @@ async function runCaptureFinishInner(input, config, session_id, transcript_path,
|
|
|
690
712
|
// Carry the final flag into the HTTP body — the server uses it to decide
|
|
691
713
|
// whether to close the session lease (true) or renew it (false/absent).
|
|
692
714
|
final: input.final,
|
|
715
|
+
// Desktop-only measured spend (undefined for every other source).
|
|
716
|
+
costUsd: usage.costUsd,
|
|
693
717
|
});
|
|
694
718
|
// Throttle decision: final captures go immediately; non-final (per-turn Stop)
|
|
695
719
|
// are deferred if we uploaded this session recently, to avoid per-turn churn.
|
|
@@ -718,7 +742,12 @@ async function runCaptureFinishInner(input, config, session_id, transcript_path,
|
|
|
718
742
|
const hookScriptPath = process.argv[1];
|
|
719
743
|
const tmpFile = path.join(os.tmpdir(), `codiedev-capture-${session_id ?? process.pid}.json`);
|
|
720
744
|
if (decision.mode === "immediate") {
|
|
721
|
-
(0, utils_1.dispatchCaptureDetached)({
|
|
745
|
+
(0, utils_1.dispatchCaptureDetached)({
|
|
746
|
+
url: `${config.backendUrl}/api/captureSession`,
|
|
747
|
+
token: config.token,
|
|
748
|
+
body: payload,
|
|
749
|
+
scopePaths,
|
|
750
|
+
}, hookScriptPath, { spawn: child_process_1.spawn, writeFileSync: fs.writeFileSync, tmpFile, openSync: fs.openSync, closeSync: fs.closeSync });
|
|
722
751
|
// Retry any earlier capture jobs that failed to upload (transient network
|
|
723
752
|
// blips), excluding the one we just dispatched. Best-effort, detached.
|
|
724
753
|
// Skip the sweep on deferred path — the deferred child will handle uploading.
|
|
@@ -754,7 +783,12 @@ async function runCaptureFinishInner(input, config, session_id, transcript_path,
|
|
|
754
783
|
// Deferred: write the job file (newer turns overwrite = supersede), then
|
|
755
784
|
// spawn the deferred-upload child which will sleep, claim, and upload.
|
|
756
785
|
// Same stdio/fd-hygiene contract as dispatchCaptureDetached.
|
|
757
|
-
(0, utils_1.dispatchDeferredUpload)({
|
|
786
|
+
(0, utils_1.dispatchDeferredUpload)({
|
|
787
|
+
url: `${config.backendUrl}/api/captureSession`,
|
|
788
|
+
token: config.token,
|
|
789
|
+
body: payload,
|
|
790
|
+
scopePaths,
|
|
791
|
+
}, hookScriptPath, { spawn: child_process_1.spawn, writeFileSync: fs.writeFileSync, tmpFile, openSync: fs.openSync, closeSync: fs.closeSync }, decision.delayMs);
|
|
758
792
|
}
|
|
759
793
|
}
|
|
760
794
|
catch (err) {
|
package/dist/upgradeNudge.js
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
// __tests__/versionCheck.test.ts; this module does the IO (registry fetch +
|
|
5
5
|
// disk cache) and is exercised by manual smoke.
|
|
6
6
|
//
|
|
7
|
-
// Cache lives at
|
|
8
|
-
//
|
|
9
|
-
//
|
|
7
|
+
// Cache lives at <codiedevStateDir()>/.update-cache.json (~/.codiedev by
|
|
8
|
+
// default; ~/.codiedev-desktop under CODIEDEV_CONFIG_DIR). 24h TTL — long
|
|
9
|
+
// enough that the registry isn't hit on every command, short enough that
|
|
10
|
+
// newly-published versions surface the day after release.
|
|
10
11
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
12
|
if (k2 === undefined) k2 = k;
|
|
12
13
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -44,10 +45,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
44
45
|
exports.maybePrintUpgradeNudge = maybePrintUpgradeNudge;
|
|
45
46
|
const fs = __importStar(require("fs"));
|
|
46
47
|
const path = __importStar(require("path"));
|
|
47
|
-
const os = __importStar(require("os"));
|
|
48
48
|
const version_1 = require("./version");
|
|
49
49
|
const versionCheck_1 = require("./versionCheck");
|
|
50
|
-
const
|
|
50
|
+
const configDir_1 = require("./configDir");
|
|
51
|
+
const CACHE_PATH = path.join((0, configDir_1.codiedevStateDir)(), ".update-cache.json");
|
|
51
52
|
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
52
53
|
const REGISTRY_URL = "https://registry.npmjs.org/codiedev/latest";
|
|
53
54
|
const FETCH_TIMEOUT_MS = 1500;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import { spawn as nodeSpawn } from "child_process";
|
|
3
3
|
import type { CaptureLogEntry } from "./captureLog";
|
|
4
|
+
import { codiedevStateDir } from "./configDir";
|
|
5
|
+
export { codiedevStateDir };
|
|
4
6
|
export interface CodiedevConfig {
|
|
5
7
|
token: string;
|
|
6
8
|
companyId: string;
|
|
@@ -12,9 +14,15 @@ export interface CodiedevConfig {
|
|
|
12
14
|
}>;
|
|
13
15
|
lastSynced: string;
|
|
14
16
|
backendUrl: string;
|
|
17
|
+
deviceId?: string;
|
|
18
|
+
deviceName?: string;
|
|
19
|
+
captureIgnoredPaths?: string[];
|
|
15
20
|
}
|
|
21
|
+
export declare const CONFIG_PATH: string;
|
|
16
22
|
export declare function readConfig(): CodiedevConfig | null;
|
|
17
23
|
export declare function writeConfig(config: CodiedevConfig): void;
|
|
24
|
+
/** Exported for permission-focused tests; normal callers use writeConfig. */
|
|
25
|
+
export declare function writeConfigAtPath(config: CodiedevConfig, configDir: string, configPath: string): void;
|
|
18
26
|
export declare function parseGitRemoteFromConfig(configText: string): string | null;
|
|
19
27
|
export declare function readGitRemoteFromConfig(startDir: string): string | null;
|
|
20
28
|
export declare function getGitRemoteUrl(cwd: string): string | null;
|
|
@@ -59,11 +67,14 @@ export interface CapturePayloadInput {
|
|
|
59
67
|
inputTokens?: number;
|
|
60
68
|
outputTokens?: number;
|
|
61
69
|
cacheTokens?: number;
|
|
70
|
+
cacheReadTokens?: number;
|
|
71
|
+
cacheCreationTokens?: number;
|
|
62
72
|
model?: string | null;
|
|
63
73
|
sessionStartedAt?: number | null;
|
|
64
74
|
interruptCount?: number;
|
|
65
75
|
rejectionCount?: number;
|
|
66
76
|
final?: boolean;
|
|
77
|
+
costUsd?: number;
|
|
67
78
|
}
|
|
68
79
|
export declare function buildCapturePayload(input: CapturePayloadInput): Record<string, unknown>;
|
|
69
80
|
export declare function hashToken(token: string): string;
|
|
@@ -72,6 +83,11 @@ export declare function resetStableNodeCacheForTests(): void;
|
|
|
72
83
|
export declare function claudeCodeInstalled(): boolean;
|
|
73
84
|
export declare function codexInstalled(): boolean;
|
|
74
85
|
export declare function cursorInstalled(): boolean;
|
|
86
|
+
export declare function openCodeInstalled(): boolean;
|
|
87
|
+
/** Install a dormant global OpenCode adapter. Installing it unconditionally
|
|
88
|
+
* keeps `codiedev connect` as the only setup step even when Codie/OpenCode is
|
|
89
|
+
* installed later. OpenCode loads this file from its documented plugin dir. */
|
|
90
|
+
export declare function installOpenCodeCapturePlugin(): void;
|
|
75
91
|
/**
|
|
76
92
|
* VS Code with the GitHub Copilot Chat extension installed. The extension
|
|
77
93
|
* directory pattern is `~/.vscode/extensions/github.copilot-chat-*`. We treat
|
|
@@ -153,16 +169,26 @@ export interface SteeringStats {
|
|
|
153
169
|
}
|
|
154
170
|
export declare function parseClaudeSteering(content: string): SteeringStats;
|
|
155
171
|
export declare function parseCodexSteering(content: string): SteeringStats;
|
|
156
|
-
export declare function parseSteering(content: string, mode: "claude-code" | "codex" | "cursor"): SteeringStats;
|
|
172
|
+
export declare function parseSteering(content: string, mode: "claude-code" | "codex" | "cursor" | "opencode"): SteeringStats;
|
|
157
173
|
export interface UsageStats {
|
|
158
174
|
inputTokens: number;
|
|
159
175
|
outputTokens: number;
|
|
176
|
+
/** Legacy fold of read + creation — kept for older servers. */
|
|
160
177
|
cacheTokens: number;
|
|
178
|
+
cacheReadTokens: number;
|
|
179
|
+
cacheCreationTokens: number;
|
|
161
180
|
model: string | null;
|
|
181
|
+
/** Engine-reported spend (opencode SDK `info.cost`). Desktop mode only. */
|
|
182
|
+
costUsd?: number;
|
|
162
183
|
}
|
|
163
|
-
export declare function parseUsage(content: string, mode: "claude-code" | "codex" | "cursor"): UsageStats;
|
|
184
|
+
export declare function parseUsage(content: string, mode: "claude-code" | "codex" | "cursor" | "opencode" | "desktop"): UsageStats;
|
|
164
185
|
export declare function parseSessionStart(content: string): number | null;
|
|
165
186
|
export declare function parseClaudeCodeStats(content: string): ParsedStats;
|
|
187
|
+
/** Convert OpenCode's SDK message array (`{info, parts}[]`) into the same
|
|
188
|
+
* newline-delimited shape used by Claude Code. The rest of capture—privacy
|
|
189
|
+
* exclusions, git keys, token parsing, scrubbing, storage, and merge joins—can
|
|
190
|
+
* therefore stay one shared pipeline instead of becoming a parallel product. */
|
|
191
|
+
export declare function normalizeOpenCodeTranscript(content: string): string;
|
|
166
192
|
export declare function parseCodexStats(content: string): ParsedStats;
|
|
167
193
|
export declare function extractCodexRepoUrl(content: string): string | null;
|
|
168
194
|
export interface CaptureUploadJob {
|
|
@@ -171,6 +197,7 @@ export interface CaptureUploadJob {
|
|
|
171
197
|
body: Record<string, unknown>;
|
|
172
198
|
attempts?: number;
|
|
173
199
|
queuedAt?: number;
|
|
200
|
+
scopePaths?: string[];
|
|
174
201
|
}
|
|
175
202
|
export declare const MAX_UPLOAD_ATTEMPTS = 5;
|
|
176
203
|
export declare const UPLOAD_TTL_MS: number;
|
|
@@ -262,6 +289,7 @@ interface UploadDeps {
|
|
|
262
289
|
recordSuccess?: (job: CaptureUploadJob) => void;
|
|
263
290
|
log?: (entry: CaptureLogEntry) => void;
|
|
264
291
|
writeSessionState?: (sessionId: string, state: SessionUploadState) => void;
|
|
292
|
+
shouldSkipJob?: (job: CaptureUploadJob) => boolean;
|
|
265
293
|
}
|
|
266
294
|
export interface LastCaptureRecord {
|
|
267
295
|
sessionId: string | undefined;
|
|
@@ -292,4 +320,3 @@ interface DeferredUploadDeps extends UploadDeps {
|
|
|
292
320
|
}
|
|
293
321
|
export declare function runDeferredUpload(jobFile: string, delayMs: number, deps: DeferredUploadDeps): Promise<void>;
|
|
294
322
|
export declare function transcriptReadSkipReason(err: unknown): string;
|
|
295
|
-
export {};
|