@vkenliu/adit-cloud 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/auth/credentials.d.ts +62 -0
- package/dist/auth/credentials.d.ts.map +1 -0
- package/dist/auth/credentials.js +151 -0
- package/dist/auth/credentials.js.map +1 -0
- package/dist/auth/device-auth.d.ts +40 -0
- package/dist/auth/device-auth.d.ts.map +1 -0
- package/dist/auth/device-auth.js +84 -0
- package/dist/auth/device-auth.js.map +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +80 -0
- package/dist/config.js.map +1 -0
- package/dist/http/client.d.ts +28 -0
- package/dist/http/client.d.ts.map +1 -0
- package/dist/http/client.js +229 -0
- package/dist/http/client.js.map +1 -0
- package/dist/http/errors.d.ts +19 -0
- package/dist/http/errors.d.ts.map +1 -0
- package/dist/http/errors.js +31 -0
- package/dist/http/errors.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/project-link/auto-link.d.ts +30 -0
- package/dist/project-link/auto-link.d.ts.map +1 -0
- package/dist/project-link/auto-link.js +91 -0
- package/dist/project-link/auto-link.js.map +1 -0
- package/dist/project-link/bulk-task.d.ts +15 -0
- package/dist/project-link/bulk-task.d.ts.map +1 -0
- package/dist/project-link/bulk-task.js +107 -0
- package/dist/project-link/bulk-task.js.map +1 -0
- package/dist/project-link/cache.d.ts +22 -0
- package/dist/project-link/cache.d.ts.map +1 -0
- package/dist/project-link/cache.js +82 -0
- package/dist/project-link/cache.js.map +1 -0
- package/dist/project-link/doc-discovery.d.ts +35 -0
- package/dist/project-link/doc-discovery.d.ts.map +1 -0
- package/dist/project-link/doc-discovery.js +260 -0
- package/dist/project-link/doc-discovery.js.map +1 -0
- package/dist/project-link/git-collector.d.ts +88 -0
- package/dist/project-link/git-collector.d.ts.map +1 -0
- package/dist/project-link/git-collector.js +250 -0
- package/dist/project-link/git-collector.js.map +1 -0
- package/dist/project-link/index.d.ts +15 -0
- package/dist/project-link/index.d.ts.map +1 -0
- package/dist/project-link/index.js +20 -0
- package/dist/project-link/index.js.map +1 -0
- package/dist/project-link/intent-command.d.ts +23 -0
- package/dist/project-link/intent-command.d.ts.map +1 -0
- package/dist/project-link/intent-command.js +104 -0
- package/dist/project-link/intent-command.js.map +1 -0
- package/dist/project-link/link-command.d.ts +26 -0
- package/dist/project-link/link-command.d.ts.map +1 -0
- package/dist/project-link/link-command.js +350 -0
- package/dist/project-link/link-command.js.map +1 -0
- package/dist/project-link/qualify.d.ts +25 -0
- package/dist/project-link/qualify.d.ts.map +1 -0
- package/dist/project-link/qualify.js +45 -0
- package/dist/project-link/qualify.js.map +1 -0
- package/dist/project-link/types.d.ts +254 -0
- package/dist/project-link/types.d.ts.map +1 -0
- package/dist/project-link/types.js +8 -0
- package/dist/project-link/types.js.map +1 -0
- package/dist/sync/auto-sync.d.ts +42 -0
- package/dist/sync/auto-sync.d.ts.map +1 -0
- package/dist/sync/auto-sync.js +136 -0
- package/dist/sync/auto-sync.js.map +1 -0
- package/dist/sync/conflicts.d.ts +27 -0
- package/dist/sync/conflicts.d.ts.map +1 -0
- package/dist/sync/conflicts.js +28 -0
- package/dist/sync/conflicts.js.map +1 -0
- package/dist/sync/engine.d.ts +76 -0
- package/dist/sync/engine.d.ts.map +1 -0
- package/dist/sync/engine.js +152 -0
- package/dist/sync/engine.js.map +1 -0
- package/dist/sync/serializer.d.ts +123 -0
- package/dist/sync/serializer.d.ts.map +1 -0
- package/dist/sync/serializer.js +280 -0
- package/dist/sync/serializer.js.map +1 -0
- package/dist/transcript/auto-upload.d.ts +25 -0
- package/dist/transcript/auto-upload.d.ts.map +1 -0
- package/dist/transcript/auto-upload.js +75 -0
- package/dist/transcript/auto-upload.js.map +1 -0
- package/dist/transcript/index.d.ts +4 -0
- package/dist/transcript/index.d.ts.map +1 -0
- package/dist/transcript/index.js +4 -0
- package/dist/transcript/index.js.map +1 -0
- package/dist/transcript/manager.d.ts +63 -0
- package/dist/transcript/manager.d.ts.map +1 -0
- package/dist/transcript/manager.js +143 -0
- package/dist/transcript/manager.js.map +1 -0
- package/dist/transcript/uploader.d.ts +135 -0
- package/dist/transcript/uploader.d.ts.map +1 -0
- package/dist/transcript/uploader.js +235 -0
- package/dist/transcript/uploader.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-upload trigger — fire-and-forget transcript upload after hook events.
|
|
3
|
+
*
|
|
4
|
+
* Called from the hooks system via dynamic import. Fully fail-open:
|
|
5
|
+
* any error is swallowed so it never blocks the AI agent.
|
|
6
|
+
*
|
|
7
|
+
* Designed to be called on every hook event. Internally deduplicates
|
|
8
|
+
* by checking if there's actually new data to upload before doing work.
|
|
9
|
+
*/
|
|
10
|
+
import { loadCloudConfig } from "../config.js";
|
|
11
|
+
import { loadCredentials, isTokenExpired } from "../auth/credentials.js";
|
|
12
|
+
import { CloudClient } from "../http/client.js";
|
|
13
|
+
import { CloudNetworkError, CloudAuthError } from "../http/errors.js";
|
|
14
|
+
import { registerTranscript, processTranscriptUploads } from "./manager.js";
|
|
15
|
+
/**
|
|
16
|
+
* Register a transcript and trigger pending uploads.
|
|
17
|
+
*
|
|
18
|
+
* Call this from hook handlers whenever a transcript_path is seen.
|
|
19
|
+
* The function is idempotent — calling it multiple times with the
|
|
20
|
+
* same path is safe and cheap.
|
|
21
|
+
*
|
|
22
|
+
* @param db - Open database connection
|
|
23
|
+
* @param sessionId - Current session ID
|
|
24
|
+
* @param transcriptPath - Absolute path to the transcript JSONL file
|
|
25
|
+
* @param type - Upload type (default: "transcript")
|
|
26
|
+
*/
|
|
27
|
+
export async function triggerTranscriptUpload(db, sessionId, transcriptPath, type = "transcript") {
|
|
28
|
+
const cloudConfig = loadCloudConfig();
|
|
29
|
+
// 1. Check transcript upload is not explicitly disabled
|
|
30
|
+
if (!cloudConfig.transcriptUpload.enabled)
|
|
31
|
+
return;
|
|
32
|
+
if (process.env.ADIT_CLOUD_ENABLED === "false")
|
|
33
|
+
return;
|
|
34
|
+
// 2. Check credentials exist — credentials are the implicit opt-in
|
|
35
|
+
// (same pattern as auto-sync: stored credentials enable the feature)
|
|
36
|
+
const credentials = loadCredentials();
|
|
37
|
+
if (!credentials)
|
|
38
|
+
return;
|
|
39
|
+
// 3. Resolve server URL: env var takes priority, fall back to credentials
|
|
40
|
+
// (same resolution strategy as triggerAutoSync)
|
|
41
|
+
const serverUrl = cloudConfig.serverUrl ?? credentials.serverUrl;
|
|
42
|
+
if (!serverUrl)
|
|
43
|
+
return;
|
|
44
|
+
// 3a. If env var specifies a different server than credentials, skip
|
|
45
|
+
if (cloudConfig.serverUrl && credentials.serverUrl !== cloudConfig.serverUrl)
|
|
46
|
+
return;
|
|
47
|
+
// 4. Register the transcript (idempotent)
|
|
48
|
+
registerTranscript(db, sessionId, transcriptPath, serverUrl);
|
|
49
|
+
// 5. Process any pending uploads
|
|
50
|
+
if (isTokenExpired(credentials)) {
|
|
51
|
+
// Let CloudClient try to refresh
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const client = new CloudClient(serverUrl, credentials);
|
|
55
|
+
await processTranscriptUploads({
|
|
56
|
+
db,
|
|
57
|
+
client,
|
|
58
|
+
config: cloudConfig.transcriptUpload,
|
|
59
|
+
serverUrl,
|
|
60
|
+
type,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
// Fail silently — this is fire-and-forget
|
|
65
|
+
if (process.env.ADIT_DEBUG) {
|
|
66
|
+
const msg = error instanceof CloudNetworkError
|
|
67
|
+
? `[adit-transcript] upload skipped: server unreachable — ${error.message}`
|
|
68
|
+
: error instanceof CloudAuthError
|
|
69
|
+
? `[adit-transcript] upload skipped: auth failed — ${error.message}`
|
|
70
|
+
: `[adit-transcript] upload failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
71
|
+
process.stderr.write(msg + "\n");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=auto-upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-upload.js","sourceRoot":"","sources":["../../src/transcript/auto-upload.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAG5E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,EAAqB,EACrB,SAAiB,EACjB,cAAsB,EACtB,OAAuB,YAAY;IAEnC,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;IAEtC,wDAAwD;IACxD,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,OAAO;QAAE,OAAO;IAClD,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,OAAO;QAAE,OAAO;IAEvD,mEAAmE;IACnE,wEAAwE;IACxE,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;IACtC,IAAI,CAAC,WAAW;QAAE,OAAO;IAEzB,0EAA0E;IAC1E,mDAAmD;IACnD,MAAM,SAAS,GAAG,WAAW,CAAC,SAAS,IAAI,WAAW,CAAC,SAAS,CAAC;IACjE,IAAI,CAAC,SAAS;QAAE,OAAO;IAEvB,qEAAqE;IACrE,IAAI,WAAW,CAAC,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,WAAW,CAAC,SAAS;QAAE,OAAO;IAErF,0CAA0C;IAC1C,kBAAkB,CAAC,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;IAE7D,iCAAiC;IACjC,IAAI,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,iCAAiC;IACnC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACvD,MAAM,wBAAwB,CAAC;YAC7B,EAAE;YACF,MAAM;YACN,MAAM,EAAE,WAAW,CAAC,gBAAgB;YACpC,SAAS;YACT,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,0CAA0C;QAC1C,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;YAC3B,MAAM,GAAG,GACP,KAAK,YAAY,iBAAiB;gBAChC,CAAC,CAAC,0DAA0D,KAAK,CAAC,OAAO,EAAE;gBAC3E,CAAC,CAAC,KAAK,YAAY,cAAc;oBAC/B,CAAC,CAAC,mDAAmD,KAAK,CAAC,OAAO,EAAE;oBACpE,CAAC,CAAC,oCAAoC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACrG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { registerTranscript, processTranscriptUploads, type TranscriptManagerOptions, type TranscriptProcessResult, } from "./manager.js";
|
|
2
|
+
export { triggerTranscriptUpload, } from "./auto-upload.js";
|
|
3
|
+
export { uploadChunk, uploadFull, readIncrement, compressGzip, getFileSize, checkUploadStatus, lookupUploadByPath, type SyncUploadType, type SyncUploadResponse, type SyncUploadStatus, type UploadChunkParams, } from "./uploader.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transcript/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,WAAW,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { registerTranscript, processTranscriptUploads, } from "./manager.js";
|
|
2
|
+
export { triggerTranscriptUpload, } from "./auto-upload.js";
|
|
3
|
+
export { uploadChunk, uploadFull, readIncrement, compressGzip, getFileSize, checkUploadStatus, lookupUploadByPath, } from "./uploader.js";
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transcript/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,wBAAwB,GAGzB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,WAAW,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,kBAAkB,GAKnB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript upload manager.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the lifecycle of transcript uploads:
|
|
5
|
+
* 1. Discovers transcripts from hook events (via registerTranscript)
|
|
6
|
+
* 2. Periodically checks tracked transcripts for new data
|
|
7
|
+
* 3. Uploads increments with compression
|
|
8
|
+
* 4. Handles server resync requests (full re-upload)
|
|
9
|
+
* 5. Respects max retry limits (3 per file)
|
|
10
|
+
*
|
|
11
|
+
* The manager is designed to be triggered from hook events
|
|
12
|
+
* (fire-and-forget) rather than running as a persistent daemon.
|
|
13
|
+
* Each invocation processes all pending uploads then exits.
|
|
14
|
+
*/
|
|
15
|
+
import type Database from "better-sqlite3";
|
|
16
|
+
import type { CloudClient } from "../http/client.js";
|
|
17
|
+
import type { TranscriptUploadConfig } from "../config.js";
|
|
18
|
+
import { type SyncUploadType } from "./uploader.js";
|
|
19
|
+
export interface TranscriptManagerOptions {
|
|
20
|
+
db: Database.Database;
|
|
21
|
+
client: CloudClient;
|
|
22
|
+
config: TranscriptUploadConfig;
|
|
23
|
+
serverUrl: string;
|
|
24
|
+
/** Upload type discriminator (default: "transcript") */
|
|
25
|
+
type?: SyncUploadType;
|
|
26
|
+
}
|
|
27
|
+
export interface TranscriptProcessResult {
|
|
28
|
+
/** Number of transcripts processed */
|
|
29
|
+
processed: number;
|
|
30
|
+
/** Number of successful uploads */
|
|
31
|
+
uploaded: number;
|
|
32
|
+
/** Number of transcripts skipped (no new data) */
|
|
33
|
+
skipped: number;
|
|
34
|
+
/** Number of failed uploads */
|
|
35
|
+
failed: number;
|
|
36
|
+
/** Number of transcripts that triggered a full resync */
|
|
37
|
+
resynced: number;
|
|
38
|
+
/** Total bytes uploaded (before compression) */
|
|
39
|
+
totalRawBytes: number;
|
|
40
|
+
/** Total bytes uploaded (after compression) */
|
|
41
|
+
totalCompressedBytes: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Register a transcript path for tracking.
|
|
45
|
+
*
|
|
46
|
+
* Called from hook handlers when they see a transcript_path
|
|
47
|
+
* in the hook event input. If the transcript is already tracked,
|
|
48
|
+
* this is a no-op.
|
|
49
|
+
*/
|
|
50
|
+
export declare function registerTranscript(db: Database.Database, sessionId: string, transcriptPath: string, serverUrl: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Process all pending transcript uploads.
|
|
53
|
+
*
|
|
54
|
+
* Iterates through tracked transcripts, checks for new data,
|
|
55
|
+
* and uploads increments. Respects maxConcurrent and maxRetries
|
|
56
|
+
* from config.
|
|
57
|
+
*
|
|
58
|
+
* This function is safe to call concurrently — SQLite's WAL mode
|
|
59
|
+
* and the upsert pattern prevent double-processing. However,
|
|
60
|
+
* the caller should avoid redundant invocations for efficiency.
|
|
61
|
+
*/
|
|
62
|
+
export declare function processTranscriptUploads(opts: TranscriptManagerOptions): Promise<TranscriptProcessResult>;
|
|
63
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/transcript/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAW3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,GAChB,IAAI,CAUN;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,uBAAuB,CAAC,CAsIlC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript upload manager.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the lifecycle of transcript uploads:
|
|
5
|
+
* 1. Discovers transcripts from hook events (via registerTranscript)
|
|
6
|
+
* 2. Periodically checks tracked transcripts for new data
|
|
7
|
+
* 3. Uploads increments with compression
|
|
8
|
+
* 4. Handles server resync requests (full re-upload)
|
|
9
|
+
* 5. Respects max retry limits (3 per file)
|
|
10
|
+
*
|
|
11
|
+
* The manager is designed to be triggered from hook events
|
|
12
|
+
* (fire-and-forget) rather than running as a persistent daemon.
|
|
13
|
+
* Each invocation processes all pending uploads then exits.
|
|
14
|
+
*/
|
|
15
|
+
import { generateId, getTranscriptUpload, getSessionById, upsertTranscriptUpload, listPendingTranscriptUploads, markTranscriptUploaded, markTranscriptUploadFailed, resetTranscriptUpload, } from "@vkenliu/adit-core";
|
|
16
|
+
import { uploadChunk, getFileSize, } from "./uploader.js";
|
|
17
|
+
/**
|
|
18
|
+
* Register a transcript path for tracking.
|
|
19
|
+
*
|
|
20
|
+
* Called from hook handlers when they see a transcript_path
|
|
21
|
+
* in the hook event input. If the transcript is already tracked,
|
|
22
|
+
* this is a no-op.
|
|
23
|
+
*/
|
|
24
|
+
export function registerTranscript(db, sessionId, transcriptPath, serverUrl) {
|
|
25
|
+
const existing = getTranscriptUpload(db, transcriptPath, serverUrl);
|
|
26
|
+
if (existing)
|
|
27
|
+
return; // Already tracked
|
|
28
|
+
upsertTranscriptUpload(db, {
|
|
29
|
+
id: generateId(),
|
|
30
|
+
sessionId,
|
|
31
|
+
transcriptPath,
|
|
32
|
+
serverUrl,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Process all pending transcript uploads.
|
|
37
|
+
*
|
|
38
|
+
* Iterates through tracked transcripts, checks for new data,
|
|
39
|
+
* and uploads increments. Respects maxConcurrent and maxRetries
|
|
40
|
+
* from config.
|
|
41
|
+
*
|
|
42
|
+
* This function is safe to call concurrently — SQLite's WAL mode
|
|
43
|
+
* and the upsert pattern prevent double-processing. However,
|
|
44
|
+
* the caller should avoid redundant invocations for efficiency.
|
|
45
|
+
*/
|
|
46
|
+
export async function processTranscriptUploads(opts) {
|
|
47
|
+
const { db, client, config, serverUrl, type = "transcript", } = opts;
|
|
48
|
+
const result = {
|
|
49
|
+
processed: 0,
|
|
50
|
+
uploaded: 0,
|
|
51
|
+
skipped: 0,
|
|
52
|
+
failed: 0,
|
|
53
|
+
resynced: 0,
|
|
54
|
+
totalRawBytes: 0,
|
|
55
|
+
totalCompressedBytes: 0,
|
|
56
|
+
};
|
|
57
|
+
if (!config.enabled)
|
|
58
|
+
return result;
|
|
59
|
+
// Get all transcripts that need processing
|
|
60
|
+
const pending = listPendingTranscriptUploads(db, serverUrl);
|
|
61
|
+
if (pending.length === 0)
|
|
62
|
+
return result;
|
|
63
|
+
// Process up to maxConcurrent at a time
|
|
64
|
+
// Use sequential processing to be kind to the network
|
|
65
|
+
// and avoid overwhelming the server
|
|
66
|
+
let activeCount = 0;
|
|
67
|
+
for (const upload of pending) {
|
|
68
|
+
if (activeCount >= config.maxConcurrent)
|
|
69
|
+
break;
|
|
70
|
+
result.processed++;
|
|
71
|
+
// Check if the file has new data
|
|
72
|
+
const currentSize = getFileSize(upload.transcriptPath);
|
|
73
|
+
if (currentSize === 0) {
|
|
74
|
+
result.skipped++;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// Skip if no new data and already up to date
|
|
78
|
+
const newDataBytes = currentSize - upload.uploadedBytes;
|
|
79
|
+
if (newDataBytes < config.minIncrementBytes && upload.status === "up_to_date") {
|
|
80
|
+
result.skipped++;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
// Skip if already at current file size
|
|
84
|
+
if (upload.uploadedBytes >= currentSize) {
|
|
85
|
+
result.skipped++;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
activeCount++;
|
|
89
|
+
// Derive cli from the session's platform (falls back to "other"
|
|
90
|
+
// if the session is missing — e.g. orphaned transcript_uploads row)
|
|
91
|
+
const session = getSessionById(db, upload.sessionId);
|
|
92
|
+
const cli = session?.platform ?? "other";
|
|
93
|
+
try {
|
|
94
|
+
const response = await uploadChunk(client, {
|
|
95
|
+
type,
|
|
96
|
+
cli,
|
|
97
|
+
sessionId: upload.sessionId,
|
|
98
|
+
filePath: upload.transcriptPath,
|
|
99
|
+
offsetBytes: upload.uploadedBytes,
|
|
100
|
+
fileSizeBytes: currentSize,
|
|
101
|
+
serverVersion: upload.serverVersion,
|
|
102
|
+
});
|
|
103
|
+
if (response.resyncRequired) {
|
|
104
|
+
// Server needs full re-upload
|
|
105
|
+
resetTranscriptUpload(db, upload.id);
|
|
106
|
+
result.resynced++;
|
|
107
|
+
// Immediately re-upload from offset 0
|
|
108
|
+
const fullResponse = await uploadChunk(client, {
|
|
109
|
+
type,
|
|
110
|
+
cli,
|
|
111
|
+
sessionId: upload.sessionId,
|
|
112
|
+
filePath: upload.transcriptPath,
|
|
113
|
+
offsetBytes: 0,
|
|
114
|
+
fileSizeBytes: currentSize,
|
|
115
|
+
serverVersion: null,
|
|
116
|
+
});
|
|
117
|
+
if (fullResponse.resyncRequired) {
|
|
118
|
+
// Still failing after full re-upload — mark as failed
|
|
119
|
+
markTranscriptUploadFailed(db, upload.id, "Server requested resync but full re-upload also failed");
|
|
120
|
+
result.failed++;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
markTranscriptUploaded(db, upload.id, fullResponse.confirmedOffset, currentSize, fullResponse.serverVersion);
|
|
124
|
+
result.uploaded++;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Normal incremental upload succeeded
|
|
128
|
+
markTranscriptUploaded(db, upload.id, response.confirmedOffset, currentSize, response.serverVersion);
|
|
129
|
+
result.uploaded++;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
134
|
+
markTranscriptUploadFailed(db, upload.id, errorMsg);
|
|
135
|
+
result.failed++;
|
|
136
|
+
if (process.env.ADIT_DEBUG) {
|
|
137
|
+
process.stderr.write(`[adit-transcript] upload failed for ${upload.transcriptPath}: ${errorMsg}\n`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/transcript/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript incremental uploader.
|
|
3
|
+
*
|
|
4
|
+
* Reads a JSONL transcript file, detects new data since the last upload,
|
|
5
|
+
* compresses the increment with gzip, and uploads it to the server.
|
|
6
|
+
*
|
|
7
|
+
* Key design decisions:
|
|
8
|
+
* - Increments are split on newline boundaries to preserve JSONL format
|
|
9
|
+
* - Gzip compression reduces bandwidth for large transcripts
|
|
10
|
+
* - Server returns a version token for conflict detection
|
|
11
|
+
* - Server can signal "resync" to request a full re-upload
|
|
12
|
+
* - Max 3 consecutive failures per file before giving up
|
|
13
|
+
*
|
|
14
|
+
* The upload API is generic (/api/sync/upload) and uses `type` + `cli`
|
|
15
|
+
* fields so the server can handle different file types from different
|
|
16
|
+
* AI coding tools (Claude Code, Cursor, Windsurf, etc.).
|
|
17
|
+
*/
|
|
18
|
+
import type { CloudClient } from "../http/client.js";
|
|
19
|
+
/** Upload type discriminator — extensible for future file types */
|
|
20
|
+
export type SyncUploadType = "transcript" | "plan" | "log";
|
|
21
|
+
/** Response from POST /api/sync/upload */
|
|
22
|
+
export interface SyncUploadResponse {
|
|
23
|
+
/** Server-assigned upload ID (stable across increments) */
|
|
24
|
+
uploadId: string;
|
|
25
|
+
/** Byte offset the server has confirmed receiving up to */
|
|
26
|
+
confirmedOffset: number;
|
|
27
|
+
/** Opaque version token (pass back on next upload) */
|
|
28
|
+
serverVersion: string;
|
|
29
|
+
/** If true, server requests a full re-upload from offset 0 */
|
|
30
|
+
resyncRequired: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Upload status parsed from HEAD response headers.
|
|
34
|
+
*
|
|
35
|
+
* All status is conveyed via standard + custom HTTP headers,
|
|
36
|
+
* with no JSON body — keeping status checks as cheap as possible.
|
|
37
|
+
*/
|
|
38
|
+
export interface SyncUploadStatus {
|
|
39
|
+
uploadId: string;
|
|
40
|
+
confirmedOffset: number;
|
|
41
|
+
serverVersion: string;
|
|
42
|
+
type: string;
|
|
43
|
+
cli: string;
|
|
44
|
+
totalStoredBytes: number;
|
|
45
|
+
lastModified: string | null;
|
|
46
|
+
}
|
|
47
|
+
/** Parameters for uploading a chunk */
|
|
48
|
+
export interface UploadChunkParams {
|
|
49
|
+
/** Upload type (e.g. "transcript") */
|
|
50
|
+
type: SyncUploadType;
|
|
51
|
+
/** CLI identifier (e.g. "claude-code") */
|
|
52
|
+
cli: string;
|
|
53
|
+
/** Session ID for this file */
|
|
54
|
+
sessionId: string;
|
|
55
|
+
/** Absolute path to the file on the client */
|
|
56
|
+
filePath: string;
|
|
57
|
+
/** Byte offset to start reading from */
|
|
58
|
+
offsetBytes: number;
|
|
59
|
+
/** Current total file size */
|
|
60
|
+
fileSizeBytes: number;
|
|
61
|
+
/** Server version token from previous upload (null for first upload) */
|
|
62
|
+
serverVersion: string | null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Read the increment from a transcript file starting at the given
|
|
66
|
+
* byte offset, snapping to the nearest complete line boundary.
|
|
67
|
+
*
|
|
68
|
+
* Returns the raw bytes of complete JSONL lines after the offset.
|
|
69
|
+
* If no complete new lines exist, returns null.
|
|
70
|
+
*/
|
|
71
|
+
export declare function readIncrement(filePath: string, offsetBytes: number): {
|
|
72
|
+
data: Buffer;
|
|
73
|
+
newOffset: number;
|
|
74
|
+
} | null;
|
|
75
|
+
/**
|
|
76
|
+
* Compress a buffer with gzip.
|
|
77
|
+
*/
|
|
78
|
+
export declare function compressGzip(data: Buffer): Promise<Buffer>;
|
|
79
|
+
/**
|
|
80
|
+
* Upload an incremental chunk to the server.
|
|
81
|
+
*
|
|
82
|
+
* Posts to `/api/sync/upload` with `type` and `cli` fields so the server
|
|
83
|
+
* can route to the correct handler regardless of which CLI/file-type
|
|
84
|
+
* produced the data.
|
|
85
|
+
*
|
|
86
|
+
* The chunk is gzip-compressed before sending. The server responds
|
|
87
|
+
* with the confirmed byte offset and a version token.
|
|
88
|
+
*
|
|
89
|
+
* If the server returns a 409 with resyncRequired=true, the caller
|
|
90
|
+
* should reset the upload state and re-upload from offset 0.
|
|
91
|
+
*/
|
|
92
|
+
export declare function uploadChunk(client: CloudClient, params: UploadChunkParams): Promise<SyncUploadResponse>;
|
|
93
|
+
/**
|
|
94
|
+
* Upload the full file from offset 0.
|
|
95
|
+
* Used for initial upload or when server requests a resync.
|
|
96
|
+
*/
|
|
97
|
+
export declare function uploadFull(client: CloudClient, params: Omit<UploadChunkParams, "offsetBytes">): Promise<SyncUploadResponse>;
|
|
98
|
+
/**
|
|
99
|
+
* Check upload status via HEAD /api/sync/upload/{uploadId}.
|
|
100
|
+
*
|
|
101
|
+
* All status is returned in standard HTTP headers — no JSON body.
|
|
102
|
+
* This is the only status check method; there is no GET-by-ID endpoint.
|
|
103
|
+
*
|
|
104
|
+
* Header mapping:
|
|
105
|
+
* X-Upload-Id → uploadId
|
|
106
|
+
* X-Confirmed-Offset → confirmedOffset
|
|
107
|
+
* ETag → serverVersion
|
|
108
|
+
* X-Upload-Type → type
|
|
109
|
+
* X-Upload-Cli → cli
|
|
110
|
+
* Content-Length → totalStoredBytes
|
|
111
|
+
* Last-Modified → lastModified
|
|
112
|
+
*
|
|
113
|
+
* Returns null if the upload is not found (404).
|
|
114
|
+
*/
|
|
115
|
+
export declare function checkUploadStatus(client: CloudClient, uploadId: string): Promise<SyncUploadStatus | null>;
|
|
116
|
+
/**
|
|
117
|
+
* Look up an upload by its original file path.
|
|
118
|
+
*
|
|
119
|
+
* Uses GET /api/sync/upload?lookup=by-path&type=...&cli=...&...
|
|
120
|
+
* The response has no JSON body — status comes from headers only,
|
|
121
|
+
* same as HEAD.
|
|
122
|
+
*
|
|
123
|
+
* Returns null if not found (404).
|
|
124
|
+
*/
|
|
125
|
+
export declare function lookupUploadByPath(client: CloudClient, params: {
|
|
126
|
+
type: SyncUploadType;
|
|
127
|
+
cli: string;
|
|
128
|
+
sessionId: string;
|
|
129
|
+
filePath: string;
|
|
130
|
+
}): Promise<SyncUploadStatus | null>;
|
|
131
|
+
/**
|
|
132
|
+
* Get the current file size, or 0 if the file doesn't exist.
|
|
133
|
+
*/
|
|
134
|
+
export declare function getFileSize(filePath: string): number;
|
|
135
|
+
//# sourceMappingURL=uploader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploader.d.ts","sourceRoot":"","sources":["../../src/transcript/uploader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKrD,mEAAmE;AACnE,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3D,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IACjC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,eAAe,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,uCAAuC;AACvC,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,IAAI,EAAE,cAAc,CAAC;IACrB,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAID;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAkC5C;AAID;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYhE;AAID;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAqD7B;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,aAAa,CAAC,GAC7C,OAAO,CAAC,kBAAkB,CAAC,CAM7B;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAYlC;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE;IACN,IAAI,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,GACA,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoBlC;AAmBD;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGpD"}
|