spiracha 1.1.2 → 1.3.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/AGENTS.md +57 -14
- package/README.md +122 -65
- package/apps/ui/AGENTS.md +18 -8
- package/apps/ui/README.md +30 -12
- package/apps/ui/dist/client/assets/{analytics-CqWZmyV6.js → analytics-B_hYz65v.js} +1 -1
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-qiyygB7e.js +1 -0
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-z1SQC2Kg.js +1 -0
- package/apps/ui/dist/client/assets/antigravity-keychain-panel-dYuRWtCf.js +1 -0
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-CliqUr7o.js +1 -0
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-CnoBzyX6.js +1 -0
- package/apps/ui/dist/client/assets/antigravity.index-CakfZz_E.js +1 -0
- package/apps/ui/dist/client/assets/antigravity.index-DY7M1KhG.js +1 -0
- package/apps/ui/dist/client/assets/badge-aHE9ETVe.js +1 -0
- package/apps/ui/dist/client/assets/checkbox-DN3XnJaA.js +1 -0
- package/apps/ui/dist/client/assets/cursor-threads._composerId-BMQyx8qG.js +1 -0
- package/apps/ui/dist/client/assets/cursor-threads._composerId-BTlaA-tV.js +1 -0
- package/apps/ui/dist/client/assets/cursor._workspaceKey-CrgrfevV.js +1 -0
- package/apps/ui/dist/client/assets/cursor._workspaceKey-bYS2syGL.js +1 -0
- package/apps/ui/dist/client/assets/cursor.index-CTqZMPYU.js +1 -0
- package/apps/ui/dist/client/assets/cursor.index-Clsz4E_e.js +2 -0
- package/apps/ui/dist/client/assets/{data-table-DnPYMPCD.js → data-table-Cj-v-uyB.js} +2 -2
- package/apps/ui/dist/client/assets/delete-confirm-dialog-DTpzBiNK.js +11 -0
- package/apps/ui/dist/client/assets/dist-BNAn99Pu.js +1 -0
- package/apps/ui/dist/client/assets/download-P3Rp23Ad.js +1 -0
- package/apps/ui/dist/client/assets/dropdown-menu-3qB5j9nt.js +1 -0
- package/apps/ui/dist/client/assets/es2015-Dwm_turD.js +41 -0
- package/apps/ui/dist/client/assets/export-dialog-CazdrASq.js +1 -0
- package/apps/ui/dist/client/assets/formatters-BdnWuM1z.js +1 -0
- package/apps/ui/dist/client/assets/index-BVFnfS78.js +22 -0
- package/apps/ui/dist/client/assets/json-panel-DLkS30sQ.js +1 -0
- package/apps/ui/dist/client/assets/metadata-section-jnIkB7dB.js +1 -0
- package/apps/ui/dist/client/assets/{metric-card-9jwBF7rG.js → metric-card-CBZuWLzQ.js} +1 -1
- package/apps/ui/dist/client/assets/page-header-CnD21cPn.js +1 -0
- package/apps/ui/dist/client/assets/projects._project-BLszwvYL.js +1 -0
- package/apps/ui/dist/client/assets/projects._project-DvLxYbvk.js +1 -0
- package/apps/ui/dist/client/assets/projects.index-COn8woBR.js +1 -0
- package/apps/ui/dist/client/assets/projects.index-DYs98skV.js +3 -0
- package/apps/ui/dist/client/assets/refresh-ccw-BDrYXjtD.js +1 -0
- package/apps/ui/dist/client/assets/reload-error-panel-DLAg0AW2.js +1 -0
- package/apps/ui/dist/client/assets/routes-BtF5-coe.js +1 -0
- package/apps/ui/dist/client/assets/scroll-text-CqaFm9by.js +1 -0
- package/apps/ui/dist/client/assets/select-DbnpwqL6.js +1 -0
- package/apps/ui/dist/client/assets/settings-CGX3VleN.js +1 -0
- package/apps/ui/dist/client/assets/styles-Ch0r3kMZ.css +1 -0
- package/apps/ui/dist/client/assets/text-document-panel-DPleOmmq.js +1 -0
- package/apps/ui/dist/client/assets/text-filter-7M6wRo-t.js +2 -0
- package/apps/ui/dist/client/assets/threads._threadId-D5w76IB-.js +7 -0
- package/apps/ui/dist/client/assets/{threads._threadId-DT75NiBa.js → threads._threadId-Dx85sI9P.js} +1 -1
- package/apps/ui/dist/client/assets/useMutation-MZ3Hr9h9.js +1 -0
- package/apps/ui/dist/client/assets/useQuery-Cb4V0AT0.js +1 -0
- package/apps/ui/dist/client/icon.svg +28 -0
- package/apps/ui/dist/client/manifest.json +6 -16
- package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CBbkUXw6.js +227 -0
- package/apps/ui/dist/server/assets/{analytics-BMxW_bZL.js → analytics-CBNOYZwJ.js} +2 -2
- package/apps/ui/dist/server/assets/antigravity-conversation-state-HgzS302O.js +16 -0
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-B9Rm4EXh.js +212 -0
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BIdYNy68.js +20 -0
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-D426O-64.js +11 -0
- package/apps/ui/dist/server/assets/antigravity-db-D9gW1D8G.js +576 -0
- package/apps/ui/dist/server/assets/antigravity-keychain-DOiuHDwK.js +126 -0
- package/apps/ui/dist/server/assets/antigravity-keychain-panel-DcLyBBwd.js +55 -0
- package/apps/ui/dist/server/assets/antigravity-queries-CgQhlQ7J.js +37 -0
- package/apps/ui/dist/server/assets/antigravity-server-DFUx4Khk.js +114 -0
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-3m_MzNFA.js +11 -0
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-D42ixtzp.js +210 -0
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-DnSlSC-C.js +28 -0
- package/apps/ui/dist/server/assets/antigravity.index-DZVT-cac.js +104 -0
- package/apps/ui/dist/server/assets/antigravity.index-DudTB3Tq.js +11 -0
- package/apps/ui/dist/server/assets/badge-EvdhKK_Z.js +26 -0
- package/apps/ui/dist/server/assets/{codex-queries-CAF6HYiG.js → codex-queries-eOJGfHQj.js} +6 -18
- package/apps/ui/dist/server/assets/{codex-server-C01sv0JJ.js → codex-server-nrETIF--.js} +166 -226
- package/apps/ui/dist/server/assets/createServerRpc-BtXIw2iP.js +12 -0
- package/apps/ui/dist/server/assets/createSsrRpc-COf5Zuye.js +16 -0
- package/apps/ui/dist/server/assets/cursor-db-B7agkAvM.js +643 -0
- package/apps/ui/dist/server/assets/cursor-exporter-types-CI3goo-c.js +34 -0
- package/apps/ui/dist/server/assets/cursor-queries-BMhuJeUO.js +65 -0
- package/apps/ui/dist/server/assets/cursor-recovery-9bJLs7vG.js +361 -0
- package/apps/ui/dist/server/assets/cursor-server-BgylIFgn.js +184 -0
- package/apps/ui/dist/server/assets/cursor-threads._composerId-BB0Y_Mao.js +11 -0
- package/apps/ui/dist/server/assets/cursor-threads._composerId-BsxFKzoJ.js +218 -0
- package/apps/ui/dist/server/assets/cursor-threads._composerId-DXffY_CK.js +18 -0
- package/apps/ui/dist/server/assets/cursor-transcript-2iL3KFSK.js +125 -0
- package/apps/ui/dist/server/assets/cursor._workspaceKey-BP2J1x_x.js +28 -0
- package/apps/ui/dist/server/assets/cursor._workspaceKey-BQd0e-Pd.js +399 -0
- package/apps/ui/dist/server/assets/cursor._workspaceKey-nmg3YIOQ.js +11 -0
- package/apps/ui/dist/server/assets/cursor.index-CQVxtCm8.js +189 -0
- package/apps/ui/dist/server/assets/cursor.index-CcsX7DG0.js +11 -0
- package/apps/ui/dist/server/assets/{delete-confirm-dialog-CWqcTXTF.js → delete-confirm-dialog-PCD7S0_M.js} +5 -4
- package/apps/ui/dist/server/assets/download-DMmiy1xf.js +92 -0
- package/apps/ui/dist/server/assets/{input-B4tEzctc.js → dropdown-menu-Dy_9t6TN.js} +1 -11
- package/apps/ui/dist/server/assets/{download-C5rkk_Bo.js → export-dialog-DaPlOGFT.js} +8 -99
- package/apps/ui/dist/server/assets/json-panel-RYsxWFae.js +16 -0
- package/apps/ui/dist/server/assets/{loading-panel-DbLdvjtR.js → loading-panel-BGFnWseS.js} +1 -1
- package/apps/ui/dist/server/assets/metadata-section-D6Lbc7D6.js +54 -0
- package/apps/ui/dist/server/assets/page-header-VNSaM3xd.js +29 -0
- package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +12 -0
- package/apps/ui/dist/server/assets/{projects._project-CJ7l0ynC.js → projects._project-DUN3iWfg.js} +4 -4
- package/apps/ui/dist/server/assets/{projects._project-CcJLp_A8.js → projects._project-Dim9Y0kD.js} +54 -26
- package/apps/ui/dist/server/assets/projects.index-BLXOx5eL.js +12 -0
- package/apps/ui/dist/server/assets/{projects.index-srtogpuF.js → projects.index-DjSQK5dm.js} +23 -27
- package/apps/ui/dist/server/assets/{projects.index-CaplpeMy.js → reload-error-panel-BJMxY3U1.js} +5 -6
- package/apps/ui/dist/server/assets/{router-C_w-haH6.js → router-DrDgc-LD.js} +131 -44
- package/apps/ui/dist/server/assets/{routes-CPe-ppmC.js → routes-B-GlEe2C.js} +54 -39
- package/apps/ui/dist/server/assets/{routes-BhbxvJE7.js → routes-CNHAUMwo.js} +2 -2
- package/apps/ui/dist/server/assets/{settings-MvWDgc1u.js → settings-OayxIYQQ.js} +1 -1
- package/apps/ui/dist/server/assets/shared-CPRNYIql.js +134 -0
- package/apps/ui/dist/server/assets/text-document-panel-D8JbQWAn.js +23 -0
- package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +36 -0
- package/apps/ui/dist/server/assets/{threads._threadId-Ba7vv6-K.js → threads._threadId-CJzm4KrZ.js} +3 -3
- package/apps/ui/dist/server/assets/{threads._threadId-euyNckhj.js → threads._threadId-DODTYddm.js} +69 -76
- package/apps/ui/dist/server/server.js +83 -36
- package/bin/codex-chats-claude.js +5 -0
- package/bin/codex-chats.js +5 -0
- package/bin/spiracha.js +5 -0
- package/package.json +26 -13
- package/src/export-cursor.ts +244 -0
- package/src/lib/antigravity-db.ts +936 -0
- package/src/lib/antigravity-exporter-types.ts +70 -0
- package/src/lib/antigravity-keychain.ts +203 -0
- package/src/lib/codex-browser-db.ts +7 -1
- package/src/lib/codex-browser-export.ts +2 -2
- package/src/lib/codex-browser-types.ts +22 -1
- package/src/lib/codex-exporter-cli.ts +9 -9
- package/src/lib/codex-exporter-transcript.ts +16 -190
- package/src/lib/codex-exporter-types.ts +1 -1
- package/src/lib/codex-exporter.ts +0 -1
- package/src/lib/codex-thread-recovery.ts +202 -0
- package/src/lib/cursor-db.ts +1096 -0
- package/src/lib/cursor-exporter-types.ts +190 -0
- package/src/lib/cursor-exporter.ts +266 -0
- package/src/lib/cursor-recovery.ts +543 -0
- package/src/lib/cursor-transcript.ts +183 -0
- package/src/lib/interactive-cli.ts +2 -2
- package/src/mcp-server.ts +2 -2
- package/src/spiracha.ts +16 -3
- package/src/ui-cli.ts +2 -2
- package/apps/ui/dist/client/assets/checkbox-DXM4lkJq.js +0 -1
- package/apps/ui/dist/client/assets/delete-confirm-dialog-CcZaRX33.js +0 -11
- package/apps/ui/dist/client/assets/download-DOwxk-cG.js +0 -1
- package/apps/ui/dist/client/assets/es2015-Bm0kEzx2.js +0 -41
- package/apps/ui/dist/client/assets/formatters-C12LmYaa.js +0 -1
- package/apps/ui/dist/client/assets/index-DdJ7ahIt.js +0 -22
- package/apps/ui/dist/client/assets/input-CEsI7EpI.js +0 -1
- package/apps/ui/dist/client/assets/page-header-Dr_h1CVv.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-uyNGnpjH.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-zoM8d2nH.js +0 -1
- package/apps/ui/dist/client/assets/projects.index-D1CWVN-O.js +0 -1
- package/apps/ui/dist/client/assets/projects.index-DukMuny6.js +0 -1
- package/apps/ui/dist/client/assets/routes-Gr2Wwh83.js +0 -1
- package/apps/ui/dist/client/assets/select-CFim44gT.js +0 -1
- package/apps/ui/dist/client/assets/settings-DqhyDxo2.js +0 -1
- package/apps/ui/dist/client/assets/styles-CMrP9Jb4.css +0 -1
- package/apps/ui/dist/client/assets/threads._threadId-Df5VXIuZ.js +0 -7
- package/apps/ui/dist/client/favicon.ico +0 -0
- package/apps/ui/dist/client/logo192.png +0 -0
- package/apps/ui/dist/client/logo512.png +0 -0
- package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-C0V305Nt.js +0 -99
- package/apps/ui/dist/server/assets/page-header-CxdZM86z.js +0 -25
- package/apps/ui/dist/server/assets/projects._project-CLSohrBp.js +0 -26
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_ANTIGRAVITY_IDE_DIR = path.join(os.homedir(), '.gemini', 'antigravity-ide');
|
|
5
|
+
export const DEFAULT_ANTIGRAVITY_DIR = path.join(os.homedir(), '.gemini', 'antigravity');
|
|
6
|
+
|
|
7
|
+
export const resolveAntigravityRoots = (): string[] => {
|
|
8
|
+
const configured = process.env.SPIRACHA_ANTIGRAVITY_DIRS?.trim() || process.env.SPIRACHA_ANTIGRAVITY_DIR?.trim();
|
|
9
|
+
if (configured) {
|
|
10
|
+
return configured
|
|
11
|
+
.split(path.delimiter)
|
|
12
|
+
.map((entry) => entry.trim())
|
|
13
|
+
.filter(Boolean);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return [DEFAULT_ANTIGRAVITY_IDE_DIR, DEFAULT_ANTIGRAVITY_DIR];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const getAntigravityConversationDir = (root: string): string => path.join(root, 'conversations');
|
|
20
|
+
|
|
21
|
+
export const getAntigravityBrainDir = (root: string): string => path.join(root, 'brain');
|
|
22
|
+
|
|
23
|
+
export const getAntigravitySummaryIndexPath = (root: string): string => path.join(root, 'agyhub_summaries_proto.pb');
|
|
24
|
+
|
|
25
|
+
export type AntigravityArtifact = {
|
|
26
|
+
artifactType: string | null;
|
|
27
|
+
bytes: number;
|
|
28
|
+
name: string;
|
|
29
|
+
path: string;
|
|
30
|
+
sourceRoot: string;
|
|
31
|
+
summary: string | null;
|
|
32
|
+
updatedAtMs: number | null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type AntigravityTranscriptSource = 'overview' | 'safe-storage' | 'transcript';
|
|
36
|
+
|
|
37
|
+
export type AntigravityConversation = {
|
|
38
|
+
artifactBytes: number;
|
|
39
|
+
artifactCount: number;
|
|
40
|
+
artifacts: AntigravityArtifact[];
|
|
41
|
+
conversationBytes: number;
|
|
42
|
+
conversationId: string;
|
|
43
|
+
conversationMtimeMs: number | null;
|
|
44
|
+
conversationPath: string | null;
|
|
45
|
+
createdAtMs: number | null;
|
|
46
|
+
indexedItemCount: number | null;
|
|
47
|
+
lastUpdatedAtMs: number | null;
|
|
48
|
+
sourceRoot: string | null;
|
|
49
|
+
summaryPath: string | null;
|
|
50
|
+
title: string;
|
|
51
|
+
transcriptBytes: number;
|
|
52
|
+
transcriptEntryCount: number;
|
|
53
|
+
transcriptPath: string | null;
|
|
54
|
+
transcriptSource: AntigravityTranscriptSource | null;
|
|
55
|
+
workspaceFolder: string | null;
|
|
56
|
+
workspaceKey: string;
|
|
57
|
+
workspaceLabel: string;
|
|
58
|
+
workspaceUri: string | null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type AntigravityWorkspaceGroup = {
|
|
62
|
+
artifactCount: number;
|
|
63
|
+
conversationBytes: number;
|
|
64
|
+
conversationCount: number;
|
|
65
|
+
key: string;
|
|
66
|
+
label: string;
|
|
67
|
+
lastActiveMs: number;
|
|
68
|
+
transcriptCount: number;
|
|
69
|
+
uri: string | null;
|
|
70
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { createDecipheriv, pbkdf2Sync } from 'node:crypto';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
|
|
5
|
+
export const ANTIGRAVITY_KEYCHAIN_SERVICE = 'Antigravity Safe Storage';
|
|
6
|
+
export const ANTIGRAVITY_KEYCHAIN_ACCOUNT = 'Antigravity Key';
|
|
7
|
+
|
|
8
|
+
export type AntigravityDecryptionState = {
|
|
9
|
+
canRequestAccess: boolean;
|
|
10
|
+
error: string | null;
|
|
11
|
+
isUnlocked: boolean;
|
|
12
|
+
keychainAccount: string;
|
|
13
|
+
keychainService: string;
|
|
14
|
+
platform: NodeJS.Platform;
|
|
15
|
+
provider: 'keychain' | 'unsupported';
|
|
16
|
+
status: 'error' | 'locked' | 'unlocked' | 'unsupported';
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const execFileAsync = promisify(execFile);
|
|
20
|
+
const SAFE_STORAGE_SALT = 'saltysalt';
|
|
21
|
+
const SAFE_STORAGE_ITERATIONS = 1003;
|
|
22
|
+
const SAFE_STORAGE_KEY_LENGTH = 16;
|
|
23
|
+
const SAFE_STORAGE_IV = Buffer.alloc(16, 0x20);
|
|
24
|
+
|
|
25
|
+
let cachedKeychainSecret: string | null = null;
|
|
26
|
+
let lastKeychainError: string | null = null;
|
|
27
|
+
|
|
28
|
+
export const deriveAntigravitySafeStorageKey = (keychainSecret: string | Buffer): Buffer => {
|
|
29
|
+
return pbkdf2Sync(keychainSecret, SAFE_STORAGE_SALT, SAFE_STORAGE_ITERATIONS, SAFE_STORAGE_KEY_LENGTH, 'sha1');
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const parseBufferJson = (value: unknown): Buffer | null => {
|
|
33
|
+
if (!value || typeof value !== 'object') {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const data = (value as { data?: unknown }).data;
|
|
38
|
+
if (!Array.isArray(data) || data.some((entry) => typeof entry !== 'number')) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return Buffer.from(data);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const normalizeEncryptedPayload = (payload: Buffer | Uint8Array | string): Buffer | null => {
|
|
46
|
+
if (payload instanceof Buffer) {
|
|
47
|
+
return payload;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (payload instanceof Uint8Array) {
|
|
51
|
+
return Buffer.from(payload);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const trimmed = payload.trim();
|
|
55
|
+
if (trimmed.startsWith('{')) {
|
|
56
|
+
try {
|
|
57
|
+
return parseBufferJson(JSON.parse(trimmed));
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return Buffer.from(payload, 'binary');
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const hasSafeStoragePrefix = (payload: Buffer): boolean => {
|
|
67
|
+
const prefix = payload.subarray(0, 3).toString('ascii');
|
|
68
|
+
return prefix === 'v10' || prefix === 'v11';
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const isReadableUtf8 = (value: string): boolean => {
|
|
72
|
+
if (value.includes('\uFFFD')) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const printable = [...value].filter((char) => {
|
|
77
|
+
const code = char.charCodeAt(0);
|
|
78
|
+
return code === 9 || code === 10 || code === 13 || code >= 32;
|
|
79
|
+
}).length;
|
|
80
|
+
|
|
81
|
+
return value.length === 0 || printable / value.length > 0.95;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const decryptWithKey = (encrypted: Buffer, key: Buffer): string | null => {
|
|
85
|
+
const ciphertext = hasSafeStoragePrefix(encrypted) ? encrypted.subarray(3) : encrypted;
|
|
86
|
+
if (ciphertext.length === 0 || ciphertext.length % 16 !== 0) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const decipher = createDecipheriv('aes-128-cbc', key, SAFE_STORAGE_IV);
|
|
92
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8');
|
|
93
|
+
return isReadableUtf8(decrypted) ? decrypted : null;
|
|
94
|
+
} catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const decryptAntigravitySafeStoragePayload = (
|
|
100
|
+
payload: Buffer | Uint8Array | string,
|
|
101
|
+
keychainSecret: string,
|
|
102
|
+
): string | null => {
|
|
103
|
+
const encrypted = normalizeEncryptedPayload(payload);
|
|
104
|
+
if (!encrypted) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const keyAttempts = [deriveAntigravitySafeStorageKey(keychainSecret)];
|
|
109
|
+
if (/^[A-Za-z0-9+/]+={0,2}$/u.test(keychainSecret)) {
|
|
110
|
+
keyAttempts.push(deriveAntigravitySafeStorageKey(Buffer.from(keychainSecret, 'base64')));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const key of keyAttempts) {
|
|
114
|
+
const decrypted = decryptWithKey(encrypted, key);
|
|
115
|
+
if (decrypted !== null) {
|
|
116
|
+
return decrypted;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return null;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const getAntigravityDecryptionState = ({
|
|
124
|
+
cachedSecret = cachedKeychainSecret,
|
|
125
|
+
lastError = lastKeychainError,
|
|
126
|
+
platform = process.platform,
|
|
127
|
+
}: {
|
|
128
|
+
cachedSecret?: string | null;
|
|
129
|
+
lastError?: string | null;
|
|
130
|
+
platform?: NodeJS.Platform;
|
|
131
|
+
} = {}): AntigravityDecryptionState => {
|
|
132
|
+
if (platform !== 'darwin') {
|
|
133
|
+
return {
|
|
134
|
+
canRequestAccess: false,
|
|
135
|
+
error: null,
|
|
136
|
+
isUnlocked: false,
|
|
137
|
+
keychainAccount: ANTIGRAVITY_KEYCHAIN_ACCOUNT,
|
|
138
|
+
keychainService: ANTIGRAVITY_KEYCHAIN_SERVICE,
|
|
139
|
+
platform,
|
|
140
|
+
provider: 'unsupported',
|
|
141
|
+
status: 'unsupported',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (cachedSecret) {
|
|
146
|
+
return {
|
|
147
|
+
canRequestAccess: true,
|
|
148
|
+
error: null,
|
|
149
|
+
isUnlocked: true,
|
|
150
|
+
keychainAccount: ANTIGRAVITY_KEYCHAIN_ACCOUNT,
|
|
151
|
+
keychainService: ANTIGRAVITY_KEYCHAIN_SERVICE,
|
|
152
|
+
platform,
|
|
153
|
+
provider: 'keychain',
|
|
154
|
+
status: 'unlocked',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
canRequestAccess: true,
|
|
160
|
+
error: lastError,
|
|
161
|
+
isUnlocked: false,
|
|
162
|
+
keychainAccount: ANTIGRAVITY_KEYCHAIN_ACCOUNT,
|
|
163
|
+
keychainService: ANTIGRAVITY_KEYCHAIN_SERVICE,
|
|
164
|
+
platform,
|
|
165
|
+
provider: 'keychain',
|
|
166
|
+
status: lastError ? 'error' : 'locked',
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const getCachedAntigravityKeychainSecret = (): string | null => cachedKeychainSecret;
|
|
171
|
+
|
|
172
|
+
export const readAntigravityKeychainSecret = async (): Promise<string> => {
|
|
173
|
+
if (process.platform !== 'darwin') {
|
|
174
|
+
throw new Error('Antigravity Keychain access is only available on macOS.');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const { stdout } = await execFileAsync('security', [
|
|
178
|
+
'find-generic-password',
|
|
179
|
+
'-s',
|
|
180
|
+
ANTIGRAVITY_KEYCHAIN_SERVICE,
|
|
181
|
+
'-a',
|
|
182
|
+
ANTIGRAVITY_KEYCHAIN_ACCOUNT,
|
|
183
|
+
'-w',
|
|
184
|
+
]);
|
|
185
|
+
const secret = stdout.trim();
|
|
186
|
+
if (!secret) {
|
|
187
|
+
throw new Error(`No secret was returned for ${ANTIGRAVITY_KEYCHAIN_SERVICE}.`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return secret;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export const unlockAntigravityDecryption = async (): Promise<AntigravityDecryptionState> => {
|
|
194
|
+
try {
|
|
195
|
+
cachedKeychainSecret = await readAntigravityKeychainSecret();
|
|
196
|
+
lastKeychainError = null;
|
|
197
|
+
} catch (error) {
|
|
198
|
+
cachedKeychainSecret = null;
|
|
199
|
+
lastKeychainError = error instanceof Error ? error.message : String(error);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return getAntigravityDecryptionState();
|
|
203
|
+
};
|
|
@@ -491,7 +491,13 @@ export const getCodexDashboardSummary = (dbPath: string): DashboardSummary => {
|
|
|
491
491
|
return {
|
|
492
492
|
activeThreads: threads.filter((thread) => !thread.archived).length,
|
|
493
493
|
archivedThreads: threads.filter((thread) => Boolean(thread.archived)).length,
|
|
494
|
-
recentThreads: threads
|
|
494
|
+
recentThreads: threads
|
|
495
|
+
.slice(0, 5)
|
|
496
|
+
.filter((thread) => Boolean(getPortablePathBasename(thread.cwd)))
|
|
497
|
+
.map((thread) => ({
|
|
498
|
+
project: getPortablePathBasename(thread.cwd),
|
|
499
|
+
thread: compactThreadListRow(thread),
|
|
500
|
+
})),
|
|
495
501
|
threadsWithRelations,
|
|
496
502
|
topProjectsByThreadCount: [...projects]
|
|
497
503
|
.sort((left, right) => {
|
|
@@ -11,9 +11,9 @@ import { buildUiExportDownloadUrl, ensureUiExportDir } from './ui-export-files';
|
|
|
11
11
|
type RenderCodexThreadDownloadInput = {
|
|
12
12
|
dbPath: string;
|
|
13
13
|
includeCommentary: boolean;
|
|
14
|
+
includeMetadata: boolean;
|
|
14
15
|
includeTools: boolean;
|
|
15
16
|
largeExportThresholdBytes?: number;
|
|
16
|
-
optimized: boolean;
|
|
17
17
|
outputFormat: ExportFormat;
|
|
18
18
|
pathDisplaySettings?: Pick<PathDisplaySettings, 'convertToProjectRoot' | 'redactUsername'>;
|
|
19
19
|
publicExportDir?: string;
|
|
@@ -101,9 +101,9 @@ const toDownloadOptions = (input: RenderCodexThreadDownloadInput): CodexCliOptio
|
|
|
101
101
|
dbPath: input.dbPath,
|
|
102
102
|
flat: false,
|
|
103
103
|
includeCommentary: input.includeCommentary,
|
|
104
|
+
includeMetadata: input.includeMetadata,
|
|
104
105
|
includeTools: input.includeTools,
|
|
105
106
|
inputDir: '',
|
|
106
|
-
optimized: input.optimized,
|
|
107
107
|
outputDir: '',
|
|
108
108
|
outputFormat: input.outputFormat,
|
|
109
109
|
projectFilter: null,
|
|
@@ -170,10 +170,15 @@ export type ThreadBrowseData = {
|
|
|
170
170
|
thread: ThreadRow;
|
|
171
171
|
};
|
|
172
172
|
|
|
173
|
+
export type DashboardRecentThread = {
|
|
174
|
+
project: string;
|
|
175
|
+
thread: ThreadRow;
|
|
176
|
+
};
|
|
177
|
+
|
|
173
178
|
export type DashboardSummary = {
|
|
174
179
|
activeThreads: number;
|
|
175
180
|
archivedThreads: number;
|
|
176
|
-
recentThreads:
|
|
181
|
+
recentThreads: DashboardRecentThread[];
|
|
177
182
|
threadsWithRelations: number;
|
|
178
183
|
topProjectsByThreadCount: ProjectSummary[];
|
|
179
184
|
topProjectsByTokens: ProjectSummary[];
|
|
@@ -191,6 +196,22 @@ export type DeleteProjectResult = DeleteThreadsResult & {
|
|
|
191
196
|
projectName: string;
|
|
192
197
|
};
|
|
193
198
|
|
|
199
|
+
export type RecoverProjectThreadsResult = {
|
|
200
|
+
backups: {
|
|
201
|
+
globalState: string;
|
|
202
|
+
sessionIndex: string;
|
|
203
|
+
stateDb: string;
|
|
204
|
+
};
|
|
205
|
+
projectName: string;
|
|
206
|
+
projectRootsAdded: number;
|
|
207
|
+
resolvedCwds: string[];
|
|
208
|
+
rolloutFilesTouched: number;
|
|
209
|
+
savedRootsAdded: number;
|
|
210
|
+
sessionIndexRowsUpdated: number;
|
|
211
|
+
threadDbRowsUpdated: number;
|
|
212
|
+
topLevelThreadsFound: number;
|
|
213
|
+
};
|
|
214
|
+
|
|
194
215
|
export type ToolUsageSummary = {
|
|
195
216
|
count: number;
|
|
196
217
|
name: string;
|
|
@@ -10,7 +10,7 @@ export const parseCodexCliArgs = (argv: string[]): CodexCliOptions => {
|
|
|
10
10
|
let projectFilter: string | null = null;
|
|
11
11
|
let threadIds: string[] = [];
|
|
12
12
|
let outputProvided = false;
|
|
13
|
-
let
|
|
13
|
+
let includeMetadata = true;
|
|
14
14
|
let includeCommentary = true;
|
|
15
15
|
let includeTools = false;
|
|
16
16
|
let outputFormat: ExportFormat = 'md';
|
|
@@ -22,9 +22,9 @@ export const parseCodexCliArgs = (argv: string[]): CodexCliOptions => {
|
|
|
22
22
|
dbPath,
|
|
23
23
|
flat,
|
|
24
24
|
includeCommentary,
|
|
25
|
+
includeMetadata,
|
|
25
26
|
includeTools,
|
|
26
27
|
inputDir,
|
|
27
|
-
optimized,
|
|
28
28
|
outputDir,
|
|
29
29
|
outputFormat,
|
|
30
30
|
outputProvided,
|
|
@@ -37,9 +37,9 @@ export const parseCodexCliArgs = (argv: string[]): CodexCliOptions => {
|
|
|
37
37
|
dbPath,
|
|
38
38
|
flat,
|
|
39
39
|
includeCommentary,
|
|
40
|
+
includeMetadata,
|
|
40
41
|
includeTools,
|
|
41
42
|
inputDir,
|
|
42
|
-
optimized,
|
|
43
43
|
outputDir,
|
|
44
44
|
outputFormat,
|
|
45
45
|
outputProvided,
|
|
@@ -58,9 +58,9 @@ export const parseCodexCliArgs = (argv: string[]): CodexCliOptions => {
|
|
|
58
58
|
dbPath,
|
|
59
59
|
flat,
|
|
60
60
|
includeCommentary,
|
|
61
|
+
includeMetadata,
|
|
61
62
|
includeTools,
|
|
62
63
|
inputDir,
|
|
63
|
-
optimized,
|
|
64
64
|
outputDir: outputDir ?? DEFAULT_OUTPUT_DIR,
|
|
65
65
|
outputFormat,
|
|
66
66
|
projectFilter,
|
|
@@ -73,9 +73,9 @@ type CodexCliState = {
|
|
|
73
73
|
dbPath: string;
|
|
74
74
|
flat: boolean;
|
|
75
75
|
includeCommentary: boolean;
|
|
76
|
+
includeMetadata: boolean;
|
|
76
77
|
includeTools: boolean;
|
|
77
78
|
inputDir: string;
|
|
78
|
-
optimized: boolean;
|
|
79
79
|
outputDir: string | null;
|
|
80
80
|
outputFormat: ExportFormat;
|
|
81
81
|
outputProvided: boolean;
|
|
@@ -142,12 +142,12 @@ const applyCodexCliArg = (argv: string[], index: number, state: CodexCliState):
|
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
if (arg === '--
|
|
145
|
+
if (arg === '--no-metadata') {
|
|
146
146
|
return {
|
|
147
147
|
index,
|
|
148
148
|
state: {
|
|
149
149
|
...state,
|
|
150
|
-
|
|
150
|
+
includeMetadata: false,
|
|
151
151
|
},
|
|
152
152
|
};
|
|
153
153
|
}
|
|
@@ -220,7 +220,7 @@ export const getCodexHelpText = (): string => {
|
|
|
220
220
|
'Usage:',
|
|
221
221
|
' codex-chats',
|
|
222
222
|
' codex-chats --interactive',
|
|
223
|
-
' codex-chats [--db FILE] [--input DIR] [--output DIR] [--cwd DIR] [--project NAME] [--
|
|
223
|
+
' codex-chats [--db FILE] [--input DIR] [--output DIR] [--cwd DIR] [--project NAME] [--no-metadata] [--tools] [--flat] [--output-format md|txt] [codex://threads/<thread-id> ...]',
|
|
224
224
|
'',
|
|
225
225
|
'Options:',
|
|
226
226
|
` --db Thread database path (default: ${DEFAULT_DB_PATH})`,
|
|
@@ -230,7 +230,7 @@ export const getCodexHelpText = (): string => {
|
|
|
230
230
|
' --project Only export chats whose cwd basename matches this project name',
|
|
231
231
|
' codex://threads/<id>',
|
|
232
232
|
' Only export the exact threads referenced by these Codex deeplinks',
|
|
233
|
-
' --
|
|
233
|
+
' --no-metadata Omit the chat metadata section from the export header',
|
|
234
234
|
' --tools Include tool-call logs such as exec_command invocations',
|
|
235
235
|
' --flat Write all exports into one folder instead of nested subfolders',
|
|
236
236
|
' --output-format Output file format: md or txt (default: md)',
|