@code-insights/cli 1.0.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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +287 -0
  3. package/dist/commands/connect.d.ts +8 -0
  4. package/dist/commands/connect.d.ts.map +1 -0
  5. package/dist/commands/connect.js +33 -0
  6. package/dist/commands/connect.js.map +1 -0
  7. package/dist/commands/init.d.ts +9 -0
  8. package/dist/commands/init.d.ts.map +1 -0
  9. package/dist/commands/init.js +176 -0
  10. package/dist/commands/init.js.map +1 -0
  11. package/dist/commands/insights.d.ts +13 -0
  12. package/dist/commands/insights.d.ts.map +1 -0
  13. package/dist/commands/insights.js +87 -0
  14. package/dist/commands/insights.js.map +1 -0
  15. package/dist/commands/install-hook.d.ts +9 -0
  16. package/dist/commands/install-hook.d.ts.map +1 -0
  17. package/dist/commands/install-hook.js +98 -0
  18. package/dist/commands/install-hook.js.map +1 -0
  19. package/dist/commands/link.d.ts +8 -0
  20. package/dist/commands/link.d.ts.map +1 -0
  21. package/dist/commands/link.js +39 -0
  22. package/dist/commands/link.js.map +1 -0
  23. package/dist/commands/reset.d.ts +3 -0
  24. package/dist/commands/reset.d.ts.map +1 -0
  25. package/dist/commands/reset.js +95 -0
  26. package/dist/commands/reset.js.map +1 -0
  27. package/dist/commands/status.d.ts +5 -0
  28. package/dist/commands/status.d.ts.map +1 -0
  29. package/dist/commands/status.js +107 -0
  30. package/dist/commands/status.js.map +1 -0
  31. package/dist/commands/sync.d.ts +13 -0
  32. package/dist/commands/sync.d.ts.map +1 -0
  33. package/dist/commands/sync.js +205 -0
  34. package/dist/commands/sync.js.map +1 -0
  35. package/dist/firebase/client.d.ts +35 -0
  36. package/dist/firebase/client.d.ts.map +1 -0
  37. package/dist/firebase/client.js +317 -0
  38. package/dist/firebase/client.js.map +1 -0
  39. package/dist/index.d.ts +3 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +48 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/parser/insights.d.ts +7 -0
  44. package/dist/parser/insights.d.ts.map +1 -0
  45. package/dist/parser/insights.js +271 -0
  46. package/dist/parser/insights.js.map +1 -0
  47. package/dist/parser/jsonl.d.ts +6 -0
  48. package/dist/parser/jsonl.d.ts.map +1 -0
  49. package/dist/parser/jsonl.js +305 -0
  50. package/dist/parser/jsonl.js.map +1 -0
  51. package/dist/parser/titles.d.ts +14 -0
  52. package/dist/parser/titles.d.ts.map +1 -0
  53. package/dist/parser/titles.js +258 -0
  54. package/dist/parser/titles.js.map +1 -0
  55. package/dist/types.d.ts +216 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/types.js +3 -0
  58. package/dist/types.js.map +1 -0
  59. package/dist/utils/config.d.ts +46 -0
  60. package/dist/utils/config.d.ts.map +1 -0
  61. package/dist/utils/config.js +106 -0
  62. package/dist/utils/config.js.map +1 -0
  63. package/dist/utils/device.d.ts +32 -0
  64. package/dist/utils/device.d.ts.map +1 -0
  65. package/dist/utils/device.js +132 -0
  66. package/dist/utils/device.js.map +1 -0
  67. package/dist/utils/firebase-json.d.ts +87 -0
  68. package/dist/utils/firebase-json.d.ts.map +1 -0
  69. package/dist/utils/firebase-json.js +206 -0
  70. package/dist/utils/firebase-json.js.map +1 -0
  71. package/dist/utils/pricing.d.ts +29 -0
  72. package/dist/utils/pricing.d.ts.map +1 -0
  73. package/dist/utils/pricing.js +66 -0
  74. package/dist/utils/pricing.js.map +1 -0
  75. package/package.json +56 -0
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Get or create a persistent device ID.
3
+ * Stored in ~/.code-insights/device-id
4
+ */
5
+ export declare function getDeviceId(): string;
6
+ /**
7
+ * Get device information for session metadata
8
+ */
9
+ export declare function getDeviceInfo(): {
10
+ deviceId: string;
11
+ hostname: string;
12
+ platform: string;
13
+ username: string;
14
+ };
15
+ /**
16
+ * Get git remote URL for a project path.
17
+ * Returns null if not a git repo or no remote configured.
18
+ */
19
+ export declare function getGitRemoteUrl(projectPath: string): string | null;
20
+ /**
21
+ * Generate a stable project ID.
22
+ *
23
+ * Priority:
24
+ * 1. Git remote URL (most stable across devices)
25
+ * 2. Project path hash (fallback for non-git projects)
26
+ */
27
+ export declare function generateStableProjectId(projectPath: string): {
28
+ projectId: string;
29
+ source: 'git-remote' | 'path-hash';
30
+ gitRemoteUrl: string | null;
31
+ };
32
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/utils/device.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,wBAAgB,WAAW,IAAI,MAAM,CA6BpC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAOA;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAclE;AAuCD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,GAAG,WAAW,CAAC;IACnC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CA4BA"}
@@ -0,0 +1,132 @@
1
+ import { execFileSync } from 'child_process';
2
+ import { createHash } from 'crypto';
3
+ import { hostname, platform, userInfo } from 'os';
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
5
+ import { join } from 'path';
6
+ const CONFIG_DIR = join(process.env.HOME || '~', '.code-insights');
7
+ const DEVICE_ID_FILE = join(CONFIG_DIR, 'device-id');
8
+ /**
9
+ * Get or create a persistent device ID.
10
+ * Stored in ~/.code-insights/device-id
11
+ */
12
+ export function getDeviceId() {
13
+ // Check if device ID already exists
14
+ if (existsSync(DEVICE_ID_FILE)) {
15
+ const existingId = readFileSync(DEVICE_ID_FILE, 'utf-8').trim();
16
+ if (existingId) {
17
+ return existingId;
18
+ }
19
+ }
20
+ // Generate new device ID based on machine characteristics
21
+ const machineInfo = [
22
+ hostname(),
23
+ platform(),
24
+ userInfo().username,
25
+ process.arch,
26
+ ].join('-');
27
+ const deviceId = createHash('sha256')
28
+ .update(machineInfo)
29
+ .digest('hex')
30
+ .slice(0, 12);
31
+ // Persist the device ID
32
+ if (!existsSync(CONFIG_DIR)) {
33
+ mkdirSync(CONFIG_DIR, { recursive: true });
34
+ }
35
+ writeFileSync(DEVICE_ID_FILE, deviceId, 'utf-8');
36
+ return deviceId;
37
+ }
38
+ /**
39
+ * Get device information for session metadata
40
+ */
41
+ export function getDeviceInfo() {
42
+ return {
43
+ deviceId: getDeviceId(),
44
+ hostname: hostname(),
45
+ platform: platform(),
46
+ username: userInfo().username,
47
+ };
48
+ }
49
+ /**
50
+ * Get git remote URL for a project path.
51
+ * Returns null if not a git repo or no remote configured.
52
+ */
53
+ export function getGitRemoteUrl(projectPath) {
54
+ try {
55
+ // Use execFileSync for safety - no shell spawning
56
+ const result = execFileSync('git', ['remote', 'get-url', 'origin'], {
57
+ cwd: projectPath,
58
+ encoding: 'utf-8',
59
+ timeout: 5000,
60
+ stdio: ['pipe', 'pipe', 'pipe'], // Capture stderr
61
+ });
62
+ return normalizeGitUrl(result.trim());
63
+ }
64
+ catch {
65
+ // Not a git repo or no origin remote
66
+ return null;
67
+ }
68
+ }
69
+ /**
70
+ * Normalize git URL to a canonical form.
71
+ * Handles SSH, HTTPS, and various formats.
72
+ *
73
+ * Examples:
74
+ * - git@github.com:user/repo.git -> github.com/user/repo
75
+ * - https://github.com/user/repo.git -> github.com/user/repo
76
+ * - ssh://git@github.com/user/repo -> github.com/user/repo
77
+ */
78
+ function normalizeGitUrl(url) {
79
+ let normalized = url;
80
+ // Remove .git suffix
81
+ normalized = normalized.replace(/\.git$/, '');
82
+ // Handle SSH format: git@github.com:user/repo
83
+ const sshMatch = normalized.match(/^git@([^:]+):(.+)$/);
84
+ if (sshMatch) {
85
+ return `${sshMatch[1]}/${sshMatch[2]}`;
86
+ }
87
+ // Handle SSH URL format: ssh://git@github.com/user/repo
88
+ const sshUrlMatch = normalized.match(/^ssh:\/\/git@([^/]+)\/(.+)$/);
89
+ if (sshUrlMatch) {
90
+ return `${sshUrlMatch[1]}/${sshUrlMatch[2]}`;
91
+ }
92
+ // Handle HTTPS format: https://github.com/user/repo
93
+ const httpsMatch = normalized.match(/^https?:\/\/([^/]+)\/(.+)$/);
94
+ if (httpsMatch) {
95
+ return `${httpsMatch[1]}/${httpsMatch[2]}`;
96
+ }
97
+ // Fallback - return as is
98
+ return normalized;
99
+ }
100
+ /**
101
+ * Generate a stable project ID.
102
+ *
103
+ * Priority:
104
+ * 1. Git remote URL (most stable across devices)
105
+ * 2. Project path hash (fallback for non-git projects)
106
+ */
107
+ export function generateStableProjectId(projectPath) {
108
+ const gitRemoteUrl = getGitRemoteUrl(projectPath);
109
+ if (gitRemoteUrl) {
110
+ // Use git remote URL for stable ID
111
+ const projectId = createHash('sha256')
112
+ .update(gitRemoteUrl)
113
+ .digest('hex')
114
+ .slice(0, 16);
115
+ return {
116
+ projectId,
117
+ source: 'git-remote',
118
+ gitRemoteUrl,
119
+ };
120
+ }
121
+ // Fallback to path hash (will differ across devices)
122
+ const projectId = createHash('sha256')
123
+ .update(projectPath)
124
+ .digest('hex')
125
+ .slice(0, 16);
126
+ return {
127
+ projectId,
128
+ source: 'path-hash',
129
+ gitRemoteUrl: null,
130
+ };
131
+ }
132
+ //# sourceMappingURL=device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/utils/device.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import type { FirebaseServiceAccountJson, FirebaseWebConfig } from '../types.js';
2
+ export type FileReadResult<T> = {
3
+ success: true;
4
+ data: T;
5
+ } | {
6
+ success: false;
7
+ error: 'not_found' | 'invalid_json' | 'permission_denied' | 'unknown';
8
+ message: string;
9
+ };
10
+ /**
11
+ * Validate Firebase Service Account JSON structure
12
+ */
13
+ export declare function validateServiceAccountJson(data: unknown): data is FirebaseServiceAccountJson;
14
+ /**
15
+ * Check if data looks like a service account (for cross-type detection)
16
+ */
17
+ export declare function looksLikeServiceAccount(data: unknown): boolean;
18
+ /**
19
+ * Check if data looks like a web config (for cross-type detection)
20
+ */
21
+ export declare function looksLikeWebConfig(data: unknown): boolean;
22
+ /**
23
+ * Validate Firebase Web SDK config structure
24
+ */
25
+ export declare function validateWebConfig(data: unknown): data is FirebaseWebConfig;
26
+ /**
27
+ * Parse Firebase's JavaScript config snippet into a JSON object.
28
+ *
29
+ * Firebase Console gives you JavaScript like:
30
+ * const firebaseConfig = {
31
+ * apiKey: "AIza...",
32
+ * authDomain: "my-project.firebaseapp.com",
33
+ * ...
34
+ * };
35
+ *
36
+ * This function extracts the object and converts it to a parsed object.
37
+ */
38
+ export declare function parseFirebaseJsConfig(content: string): Record<string, unknown> | null;
39
+ /**
40
+ * Read a Firebase config file — accepts JSON or JavaScript format.
41
+ *
42
+ * Tries JSON.parse first. If that fails, tries to extract a JS object
43
+ * literal (handles the raw Firebase Console snippet).
44
+ */
45
+ export declare function readFirebaseConfigFile<T>(filePath: string): FileReadResult<T>;
46
+ /**
47
+ * Resolve file path (expand ~ to home directory)
48
+ */
49
+ export declare function resolveFilePath(filePath: string): string;
50
+ /**
51
+ * Check if file exists
52
+ */
53
+ export declare function fileExists(filePath: string): boolean;
54
+ /**
55
+ * Read and parse a JSON file with detailed error reporting
56
+ */
57
+ export declare function readJsonFileWithError<T>(filePath: string): FileReadResult<T>;
58
+ /**
59
+ * Read and parse a JSON file (simple version, returns null on error)
60
+ */
61
+ export declare function readJsonFile<T>(filePath: string): T | null;
62
+ /**
63
+ * Extract service account config from JSON file
64
+ */
65
+ export declare function extractServiceAccountConfig(json: FirebaseServiceAccountJson): {
66
+ projectId: string;
67
+ clientEmail: string;
68
+ privateKey: string;
69
+ };
70
+ /**
71
+ * Encode web config for URL parameter (base64url encoding per RFC 4648)
72
+ *
73
+ * The web app decodes this with:
74
+ * const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
75
+ * const json = atob(base64);
76
+ * const config = JSON.parse(json);
77
+ */
78
+ export declare function encodeWebConfigForUrl(config: FirebaseWebConfig): string;
79
+ /**
80
+ * Generate the web dashboard URL with embedded config
81
+ *
82
+ * Note: Firebase web API keys are designed for client-side use and are
83
+ * restricted by Firestore security rules, not by key secrecy. However,
84
+ * users should still avoid sharing this URL publicly.
85
+ */
86
+ export declare function generateDashboardUrl(config: FirebaseWebConfig): string;
87
+ //# sourceMappingURL=firebase-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firebase-json.d.ts","sourceRoot":"","sources":["../../src/utils/firebase-json.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEjF,MAAM,MAAM,cAAc,CAAC,CAAC,IACxB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAC1B;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,WAAW,GAAG,cAAc,GAAG,mBAAmB,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/G;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,0BAA0B,CAY5F;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAI9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAIzD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,iBAAiB,CAa1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAkCrF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAmC7E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAoB5E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAG1D;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,0BAA0B;;;;EAM3E;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAOvE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAGtE"}
@@ -0,0 +1,206 @@
1
+ import * as fs from 'fs';
2
+ /**
3
+ * Validate Firebase Service Account JSON structure
4
+ */
5
+ export function validateServiceAccountJson(data) {
6
+ if (!data || typeof data !== 'object')
7
+ return false;
8
+ const obj = data;
9
+ return (obj.type === 'service_account' &&
10
+ typeof obj.project_id === 'string' &&
11
+ typeof obj.private_key === 'string' &&
12
+ typeof obj.client_email === 'string' &&
13
+ obj.private_key.includes('-----BEGIN PRIVATE KEY-----'));
14
+ }
15
+ /**
16
+ * Check if data looks like a service account (for cross-type detection)
17
+ */
18
+ export function looksLikeServiceAccount(data) {
19
+ if (!data || typeof data !== 'object')
20
+ return false;
21
+ const obj = data;
22
+ return obj.type === 'service_account' || typeof obj.private_key === 'string';
23
+ }
24
+ /**
25
+ * Check if data looks like a web config (for cross-type detection)
26
+ */
27
+ export function looksLikeWebConfig(data) {
28
+ if (!data || typeof data !== 'object')
29
+ return false;
30
+ const obj = data;
31
+ return typeof obj.apiKey === 'string' || typeof obj.authDomain === 'string';
32
+ }
33
+ /**
34
+ * Validate Firebase Web SDK config structure
35
+ */
36
+ export function validateWebConfig(data) {
37
+ if (!data || typeof data !== 'object')
38
+ return false;
39
+ const obj = data;
40
+ return (typeof obj.apiKey === 'string' &&
41
+ typeof obj.authDomain === 'string' &&
42
+ typeof obj.projectId === 'string' &&
43
+ typeof obj.storageBucket === 'string' &&
44
+ typeof obj.messagingSenderId === 'string' &&
45
+ typeof obj.appId === 'string');
46
+ }
47
+ /**
48
+ * Parse Firebase's JavaScript config snippet into a JSON object.
49
+ *
50
+ * Firebase Console gives you JavaScript like:
51
+ * const firebaseConfig = {
52
+ * apiKey: "AIza...",
53
+ * authDomain: "my-project.firebaseapp.com",
54
+ * ...
55
+ * };
56
+ *
57
+ * This function extracts the object and converts it to a parsed object.
58
+ */
59
+ export function parseFirebaseJsConfig(content) {
60
+ try {
61
+ // Strip single-line comments
62
+ let cleaned = content.replace(/\/\/.*$/gm, '');
63
+ // Strip multi-line comments
64
+ cleaned = cleaned.replace(/\/\*[\s\S]*?\*\//g, '');
65
+ // Strip import statements
66
+ cleaned = cleaned.replace(/^import\s+.*$/gm, '');
67
+ // Strip lines like: const app = initializeApp(firebaseConfig);
68
+ cleaned = cleaned.replace(/^.*initializeApp.*$/gm, '');
69
+ // Extract the object literal between first { and last }
70
+ const firstBrace = cleaned.indexOf('{');
71
+ const lastBrace = cleaned.lastIndexOf('}');
72
+ if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace) {
73
+ return null;
74
+ }
75
+ let objectStr = cleaned.substring(firstBrace, lastBrace + 1);
76
+ // Quote unquoted keys at start of lines: apiKey: → "apiKey":
77
+ // Uses ^ with multiline to avoid matching colons inside string values
78
+ objectStr = objectStr.replace(/^(\s*)(\w+)\s*:/gm, '$1"$2":');
79
+ // Remove trailing commas before }
80
+ objectStr = objectStr.replace(/,\s*}/g, '}');
81
+ return JSON.parse(objectStr);
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }
87
+ /**
88
+ * Read a Firebase config file — accepts JSON or JavaScript format.
89
+ *
90
+ * Tries JSON.parse first. If that fails, tries to extract a JS object
91
+ * literal (handles the raw Firebase Console snippet).
92
+ */
93
+ export function readFirebaseConfigFile(filePath) {
94
+ const resolvedPath = resolveFilePath(filePath);
95
+ try {
96
+ if (!fs.existsSync(resolvedPath)) {
97
+ return { success: false, error: 'not_found', message: `File not found: ${filePath}` };
98
+ }
99
+ const content = fs.readFileSync(resolvedPath, 'utf-8');
100
+ // Try JSON first
101
+ try {
102
+ const data = JSON.parse(content);
103
+ return { success: true, data };
104
+ }
105
+ catch {
106
+ // Not valid JSON — try parsing as JavaScript
107
+ }
108
+ // Try Firebase JS config format
109
+ const parsed = parseFirebaseJsConfig(content);
110
+ if (parsed) {
111
+ return { success: true, data: parsed };
112
+ }
113
+ return {
114
+ success: false,
115
+ error: 'invalid_json',
116
+ message: 'Could not parse file as JSON or Firebase JavaScript config',
117
+ };
118
+ }
119
+ catch (err) {
120
+ if (err instanceof Error && 'code' in err && err.code === 'EACCES') {
121
+ return { success: false, error: 'permission_denied', message: 'Permission denied reading file' };
122
+ }
123
+ return { success: false, error: 'unknown', message: `Error reading file: ${err instanceof Error ? err.message : 'Unknown error'}` };
124
+ }
125
+ }
126
+ /**
127
+ * Resolve file path (expand ~ to home directory)
128
+ */
129
+ export function resolveFilePath(filePath) {
130
+ return filePath.replace(/^~/, process.env.HOME || '');
131
+ }
132
+ /**
133
+ * Check if file exists
134
+ */
135
+ export function fileExists(filePath) {
136
+ const resolvedPath = resolveFilePath(filePath);
137
+ return fs.existsSync(resolvedPath);
138
+ }
139
+ /**
140
+ * Read and parse a JSON file with detailed error reporting
141
+ */
142
+ export function readJsonFileWithError(filePath) {
143
+ const resolvedPath = resolveFilePath(filePath);
144
+ try {
145
+ if (!fs.existsSync(resolvedPath)) {
146
+ return { success: false, error: 'not_found', message: `File not found: ${filePath}` };
147
+ }
148
+ const content = fs.readFileSync(resolvedPath, 'utf-8');
149
+ const data = JSON.parse(content);
150
+ return { success: true, data };
151
+ }
152
+ catch (err) {
153
+ if (err instanceof SyntaxError) {
154
+ return { success: false, error: 'invalid_json', message: 'File contains invalid JSON' };
155
+ }
156
+ if (err instanceof Error && 'code' in err && err.code === 'EACCES') {
157
+ return { success: false, error: 'permission_denied', message: 'Permission denied reading file' };
158
+ }
159
+ return { success: false, error: 'unknown', message: `Error reading file: ${err instanceof Error ? err.message : 'Unknown error'}` };
160
+ }
161
+ }
162
+ /**
163
+ * Read and parse a JSON file (simple version, returns null on error)
164
+ */
165
+ export function readJsonFile(filePath) {
166
+ const result = readJsonFileWithError(filePath);
167
+ return result.success ? result.data : null;
168
+ }
169
+ /**
170
+ * Extract service account config from JSON file
171
+ */
172
+ export function extractServiceAccountConfig(json) {
173
+ return {
174
+ projectId: json.project_id,
175
+ clientEmail: json.client_email,
176
+ privateKey: json.private_key,
177
+ };
178
+ }
179
+ /**
180
+ * Encode web config for URL parameter (base64url encoding per RFC 4648)
181
+ *
182
+ * The web app decodes this with:
183
+ * const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
184
+ * const json = atob(base64);
185
+ * const config = JSON.parse(json);
186
+ */
187
+ export function encodeWebConfigForUrl(config) {
188
+ const json = JSON.stringify(config);
189
+ return Buffer.from(json, 'utf-8')
190
+ .toString('base64')
191
+ .replace(/\+/g, '-')
192
+ .replace(/\//g, '_')
193
+ .replace(/=+$/, '');
194
+ }
195
+ /**
196
+ * Generate the web dashboard URL with embedded config
197
+ *
198
+ * Note: Firebase web API keys are designed for client-side use and are
199
+ * restricted by Firestore security rules, not by key secrecy. However,
200
+ * users should still avoid sharing this URL publicly.
201
+ */
202
+ export function generateDashboardUrl(config) {
203
+ const encodedConfig = encodeWebConfigForUrl(config);
204
+ return `https://code-insights.app/?config=${encodedConfig}`;
205
+ }
206
+ //# sourceMappingURL=firebase-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firebase-json.js","sourceRoot":"","sources":["../../src/utils/firebase-json.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Model pricing table and cost calculation utilities.
3
+ * Prices are USD per 1M tokens, sourced from Anthropic's pricing page.
4
+ * Last updated: 2026-02-18
5
+ */
6
+ export interface ModelPricing {
7
+ input: number;
8
+ output: number;
9
+ }
10
+ export interface UsageEntry {
11
+ model: string;
12
+ usage: {
13
+ input_tokens?: number;
14
+ output_tokens?: number;
15
+ cache_creation_input_tokens?: number;
16
+ cache_read_input_tokens?: number;
17
+ };
18
+ }
19
+ /**
20
+ * Get pricing for a model, falling back to default if unknown.
21
+ * Tries exact match first, then prefix match (e.g., 'claude-sonnet-4-5-20250929' matches 'claude-sonnet-4-5').
22
+ */
23
+ export declare function getModelPricing(model: string): ModelPricing;
24
+ /**
25
+ * Calculate total estimated cost from usage entries.
26
+ * Returns cost in USD, rounded to 4 decimal places.
27
+ */
28
+ export declare function calculateCost(entries: UsageEntry[]): number;
29
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/utils/pricing.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QACL,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,2BAA2B,CAAC,EAAE,MAAM,CAAC;QACrC,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAClC,CAAC;CACH;AA4BD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAc3D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAiB3D"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Model pricing table and cost calculation utilities.
3
+ * Prices are USD per 1M tokens, sourced from Anthropic's pricing page.
4
+ * Last updated: 2026-02-18
5
+ */
6
+ // Pricing per 1M tokens (USD)
7
+ // Cache read tokens are priced at 10% of input price (Anthropic standard)
8
+ // Cache creation tokens are priced at 25% more than input price
9
+ const MODEL_PRICING = {
10
+ // Claude 4.x family
11
+ 'claude-opus-4-6': { input: 5, output: 25 },
12
+ 'claude-opus-4-5': { input: 5, output: 25 },
13
+ 'claude-opus-4-1': { input: 15, output: 75 },
14
+ 'claude-opus-4': { input: 15, output: 75 },
15
+ 'claude-sonnet-4-6': { input: 3, output: 15 },
16
+ 'claude-sonnet-4-5': { input: 3, output: 15 },
17
+ 'claude-sonnet-4': { input: 3, output: 15 },
18
+ 'claude-haiku-4-5': { input: 1, output: 5 },
19
+ // Claude 3.5 family
20
+ 'claude-3-5-sonnet-20241022': { input: 3, output: 15 },
21
+ 'claude-3-5-haiku-20241022': { input: 0.8, output: 4 },
22
+ 'claude-haiku-3-5': { input: 0.8, output: 4 },
23
+ // Claude 3 family
24
+ 'claude-3-opus-20240229': { input: 15, output: 75 },
25
+ 'claude-3-sonnet-20240229': { input: 3, output: 15 },
26
+ 'claude-3-haiku-20240307': { input: 0.25, output: 1.25 },
27
+ };
28
+ // Default fallback pricing (sonnet-level)
29
+ const DEFAULT_PRICING = { input: 3, output: 15 };
30
+ /**
31
+ * Get pricing for a model, falling back to default if unknown.
32
+ * Tries exact match first, then prefix match (e.g., 'claude-sonnet-4-5-20250929' matches 'claude-sonnet-4-5').
33
+ */
34
+ export function getModelPricing(model) {
35
+ // Exact match
36
+ if (MODEL_PRICING[model]) {
37
+ return MODEL_PRICING[model];
38
+ }
39
+ // Prefix match (model IDs often have date suffixes)
40
+ for (const [key, pricing] of Object.entries(MODEL_PRICING)) {
41
+ if (model.startsWith(key)) {
42
+ return pricing;
43
+ }
44
+ }
45
+ return DEFAULT_PRICING;
46
+ }
47
+ /**
48
+ * Calculate total estimated cost from usage entries.
49
+ * Returns cost in USD, rounded to 4 decimal places.
50
+ */
51
+ export function calculateCost(entries) {
52
+ let totalCost = 0;
53
+ for (const { model, usage } of entries) {
54
+ const pricing = getModelPricing(model);
55
+ const inputTokens = usage.input_tokens ?? 0;
56
+ const outputTokens = usage.output_tokens ?? 0;
57
+ const cacheCreationTokens = usage.cache_creation_input_tokens ?? 0;
58
+ const cacheReadTokens = usage.cache_read_input_tokens ?? 0;
59
+ totalCost += (inputTokens / 1_000_000) * pricing.input;
60
+ totalCost += (outputTokens / 1_000_000) * pricing.output;
61
+ totalCost += (cacheCreationTokens / 1_000_000) * pricing.input * 1.25;
62
+ totalCost += (cacheReadTokens / 1_000_000) * pricing.input * 0.1;
63
+ }
64
+ return Math.round(totalCost * 10000) / 10000;
65
+ }
66
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/utils/pricing.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@code-insights/cli",
3
+ "version": "1.0.0",
4
+ "description": "Sync your AI coding sessions to Firebase for analysis",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "code-insights": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ },
24
+ "keywords": [
25
+ "claude",
26
+ "claude-code",
27
+ "insights",
28
+ "analytics",
29
+ "firestore"
30
+ ],
31
+ "author": "Srikanth Rao M",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/melagiri/code-insights.git",
35
+ "directory": "cli"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/melagiri/code-insights/issues"
39
+ },
40
+ "homepage": "https://github.com/melagiri/code-insights/tree/master/cli#readme",
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "license": "MIT",
45
+ "dependencies": {
46
+ "chalk": "^5.4.1",
47
+ "commander": "^12.1.0",
48
+ "firebase-admin": "^13.4.0",
49
+ "inquirer": "^12.6.1",
50
+ "ora": "^8.2.0"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^22.15.30",
54
+ "typescript": "^5.9.3"
55
+ }
56
+ }