panopticon-cli 0.4.30 → 0.4.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agents-ZG4JIPO2.js → agents-BDFHF4T3.js} +4 -3
- package/dist/{chunk-42O6XJWZ.js → chunk-2NIAOCIC.js} +14 -14
- package/dist/{chunk-SIAUVHVO.js → chunk-3XAB4IXF.js} +4 -2
- package/dist/{chunk-SIAUVHVO.js.map → chunk-3XAB4IXF.js.map} +1 -1
- package/dist/chunk-ELK6Q7QI.js +545 -0
- package/dist/chunk-ELK6Q7QI.js.map +1 -0
- package/dist/chunk-LYSBSZYV.js +1523 -0
- package/dist/chunk-LYSBSZYV.js.map +1 -0
- package/dist/{chunk-VTMXR7JF.js → chunk-VU4FLXV5.js} +47 -40
- package/dist/{chunk-VTMXR7JF.js.map → chunk-VU4FLXV5.js.map} +1 -1
- package/dist/{chunk-VH27COUW.js → chunk-XP2DXWYP.js} +49 -10
- package/dist/chunk-XP2DXWYP.js.map +1 -0
- package/dist/cli/index.js +52 -29
- package/dist/cli/index.js.map +1 -1
- package/dist/{config-QWTS63TU.js → config-BOAMSKTF.js} +4 -2
- package/dist/dashboard/public/assets/{index-Dhwz2I7n.js → index-ClYqpcAJ.js} +84 -84
- package/dist/dashboard/public/index.html +1 -1
- package/dist/dashboard/server.js +93512 -23114
- package/dist/index.js +16 -14
- package/dist/index.js.map +1 -1
- package/dist/{remote-workspace-FNXLMNBG.js → remote-workspace-2G6V2KNP.js} +7 -5
- package/dist/{remote-workspace-FNXLMNBG.js.map → remote-workspace-2G6V2KNP.js.map} +1 -1
- package/dist/{specialist-context-QVRSHPYN.js → specialist-context-N32QBNNQ.js} +2 -2
- package/dist/{specialist-logs-7TQMHCUN.js → specialist-logs-GF3YV4KL.js} +2 -2
- package/dist/{specialists-UUXB5KFV.js → specialists-JBIW6MP4.js} +2 -2
- package/dist/{traefik-7OLLXUD7.js → traefik-CUJM6K5Z.js} +3 -3
- package/package.json +3 -2
- package/scripts/record-cost-event.js +243 -79
- package/scripts/record-cost-event.ts +128 -68
- package/dist/chunk-J3J32DIR.js +0 -279
- package/dist/chunk-J3J32DIR.js.map +0 -1
- package/dist/chunk-SUMIHS2B.js +0 -1714
- package/dist/chunk-SUMIHS2B.js.map +0 -1
- package/dist/chunk-VH27COUW.js.map +0 -1
- /package/dist/{agents-ZG4JIPO2.js.map → agents-BDFHF4T3.js.map} +0 -0
- /package/dist/{chunk-42O6XJWZ.js.map → chunk-2NIAOCIC.js.map} +0 -0
- /package/dist/{config-QWTS63TU.js.map → config-BOAMSKTF.js.map} +0 -0
- /package/dist/{specialist-context-QVRSHPYN.js.map → specialist-context-N32QBNNQ.js.map} +0 -0
- /package/dist/{specialist-logs-7TQMHCUN.js.map → specialist-logs-GF3YV4KL.js.map} +0 -0
- /package/dist/{specialists-UUXB5KFV.js.map → specialists-JBIW6MP4.js.map} +0 -0
- /package/dist/{traefik-7OLLXUD7.js.map → traefik-CUJM6K5Z.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,25 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
GitHubTracker,
|
|
3
|
-
GitLabTracker,
|
|
4
|
-
IssueNotFoundError,
|
|
5
|
-
LinearTracker,
|
|
6
2
|
LinkManager,
|
|
7
|
-
NotImplementedError,
|
|
8
|
-
TrackerAuthError,
|
|
9
3
|
addAlias,
|
|
10
4
|
cleanOldBackups,
|
|
11
5
|
createBackup,
|
|
12
6
|
createBackupTimestamp,
|
|
13
|
-
createTracker,
|
|
14
|
-
createTrackerFromConfig,
|
|
15
7
|
detectShell,
|
|
16
8
|
executeSync,
|
|
17
9
|
formatIssueRef,
|
|
18
10
|
getAliasInstructions,
|
|
19
|
-
getAllTrackers,
|
|
20
11
|
getLinkManager,
|
|
21
|
-
getPrimaryTracker,
|
|
22
|
-
getSecondaryTracker,
|
|
23
12
|
getShellRcFile,
|
|
24
13
|
hasAlias,
|
|
25
14
|
isPanopticonSymlink,
|
|
@@ -29,17 +18,28 @@ import {
|
|
|
29
18
|
planSync,
|
|
30
19
|
restoreBackup,
|
|
31
20
|
syncHooks
|
|
32
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-ELK6Q7QI.js";
|
|
33
22
|
import {
|
|
23
|
+
GitHubTracker,
|
|
24
|
+
GitLabTracker,
|
|
25
|
+
IssueNotFoundError,
|
|
26
|
+
LinearTracker,
|
|
27
|
+
NotImplementedError,
|
|
34
28
|
PROVIDERS,
|
|
29
|
+
TrackerAuthError,
|
|
30
|
+
createTracker,
|
|
31
|
+
createTrackerFromConfig,
|
|
35
32
|
getAgentCommand,
|
|
33
|
+
getAllTrackers,
|
|
36
34
|
getAvailableModels,
|
|
37
35
|
getClaudeModelFlag,
|
|
38
36
|
getDefaultSettings,
|
|
39
37
|
getDirectProviders,
|
|
38
|
+
getPrimaryTracker,
|
|
40
39
|
getProviderEnv,
|
|
41
40
|
getProviderForModel,
|
|
42
41
|
getRouterProviders,
|
|
42
|
+
getSecondaryTracker,
|
|
43
43
|
init_providers,
|
|
44
44
|
init_settings,
|
|
45
45
|
isAnthropicModel,
|
|
@@ -48,14 +48,15 @@ import {
|
|
|
48
48
|
requiresRouter,
|
|
49
49
|
saveSettings,
|
|
50
50
|
validateSettings
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-LYSBSZYV.js";
|
|
52
52
|
import "./chunk-BBCUK6N2.js";
|
|
53
53
|
import {
|
|
54
54
|
getDashboardApiUrl,
|
|
55
55
|
getDefaultConfig,
|
|
56
|
+
init_config,
|
|
56
57
|
loadConfig,
|
|
57
58
|
saveConfig
|
|
58
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-VU4FLXV5.js";
|
|
59
60
|
import {
|
|
60
61
|
AGENTS_DIR,
|
|
61
62
|
BACKUPS_DIR,
|
|
@@ -97,6 +98,7 @@ import {
|
|
|
97
98
|
// src/index.ts
|
|
98
99
|
init_esm_shims();
|
|
99
100
|
init_paths();
|
|
101
|
+
init_config();
|
|
100
102
|
init_providers();
|
|
101
103
|
init_settings();
|
|
102
104
|
export {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Panopticon CLI - Main exports for library usage\nexport * from './lib/paths.js';\nexport * from './lib/config.js';\nexport * from './lib/shell.js';\nexport * from './lib/backup.js';\nexport * from './lib/sync.js';\nexport * from './lib/tracker/index.js';\nexport * from './lib/providers.js';\nexport * from './lib/settings.js';\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Panopticon CLI - Main exports for library usage\nexport * from './lib/paths.js';\nexport * from './lib/config.js';\nexport * from './lib/shell.js';\nexport * from './lib/backup.js';\nexport * from './lib/sync.js';\nexport * from './lib/tracker/index.js';\nexport * from './lib/providers.js';\nexport * from './lib/settings.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AAKA;AACA;","names":[]}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
saveWorkspaceMetadata
|
|
3
3
|
} from "./chunk-44EOY2ZL.js";
|
|
4
|
-
import {
|
|
5
|
-
loadConfig
|
|
6
|
-
} from "./chunk-VTMXR7JF.js";
|
|
7
4
|
import {
|
|
8
5
|
extractTeamPrefix,
|
|
9
6
|
findProjectByTeam,
|
|
@@ -13,6 +10,10 @@ import {
|
|
|
13
10
|
createExeProvider,
|
|
14
11
|
init_exe_provider
|
|
15
12
|
} from "./chunk-JM6V62LT.js";
|
|
13
|
+
import {
|
|
14
|
+
init_config,
|
|
15
|
+
loadConfig
|
|
16
|
+
} from "./chunk-VU4FLXV5.js";
|
|
16
17
|
import "./chunk-6HXKTOD7.js";
|
|
17
18
|
import {
|
|
18
19
|
init_esm_shims
|
|
@@ -20,10 +21,11 @@ import {
|
|
|
20
21
|
|
|
21
22
|
// src/lib/remote-workspace.ts
|
|
22
23
|
init_esm_shims();
|
|
24
|
+
init_config();
|
|
25
|
+
init_exe_provider();
|
|
23
26
|
import chalk from "chalk";
|
|
24
27
|
import { exec } from "child_process";
|
|
25
28
|
import { promisify } from "util";
|
|
26
|
-
init_exe_provider();
|
|
27
29
|
init_projects();
|
|
28
30
|
var execAsync = promisify(exec);
|
|
29
31
|
async function createRemoteWorkspace(issueId, options = {}) {
|
|
@@ -176,4 +178,4 @@ EOF`);
|
|
|
176
178
|
export {
|
|
177
179
|
createRemoteWorkspace
|
|
178
180
|
};
|
|
179
|
-
//# sourceMappingURL=remote-workspace-
|
|
181
|
+
//# sourceMappingURL=remote-workspace-2G6V2KNP.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/remote-workspace.ts"],"sourcesContent":["/**\n * Remote Workspace Creation\n *\n * Shared module for creating remote workspaces.\n * Used by both workspace.ts (explicit creation) and work/issue.ts (auto-creation).\n */\n\nimport chalk from 'chalk';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { loadConfig } from './config.js';\nimport { createExeProvider } from './remote/exe-provider.js';\nimport { saveWorkspaceMetadata } from './remote/workspace-metadata.js';\nimport type { RemoteWorkspaceMetadata } from './remote/interface.js';\nimport { extractTeamPrefix, findProjectByTeam, resolveProjectFromIssue } from './projects.js';\n\nconst execAsync = promisify(exec);\n\nexport interface CreateRemoteWorkspaceOptions {\n dryRun?: boolean;\n spinner?: { text: string };\n}\n\n/**\n * Create a remote workspace on exe.dev\n */\nexport async function createRemoteWorkspace(\n issueId: string,\n options: CreateRemoteWorkspaceOptions = {}\n): Promise<RemoteWorkspaceMetadata> {\n const config = loadConfig();\n const remoteConfig = config.remote;\n\n if (!remoteConfig?.enabled) {\n throw new Error('Remote workspaces not enabled. Run `pan remote setup`');\n }\n\n const normalizedId = issueId.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n const branchName = `feature/${normalizedId}`;\n const infraVm = remoteConfig.exe?.infra_vm || 'pan-infra';\n const exe = createExeProvider({ infraVm });\n\n // Determine project context\n const teamPrefix = extractTeamPrefix(issueId);\n const projectConfig = teamPrefix ? findProjectByTeam(teamPrefix) : null;\n const projectRoot = projectConfig?.path || process.cwd();\n\n // Determine project identifier for VM name\n let projectId = teamPrefix?.toLowerCase();\n if (!projectId && projectConfig?.linear_team) {\n projectId = projectConfig.linear_team.toLowerCase();\n }\n if (!projectId) {\n try {\n const { stdout } = await execAsync('git remote get-url origin', {\n cwd: projectRoot,\n encoding: 'utf-8',\n });\n const repoMatch = stdout.trim().match(/\\/([^\\/]+?)(\\.git)?$/);\n projectId = repoMatch ? repoMatch[1].toLowerCase().replace(/[^a-z0-9-]/g, '-') : 'proj';\n } catch {\n projectId = 'proj';\n }\n }\n\n // VM names must be valid hostnames (start with letter, alphanumeric + hyphens)\n const vmName = `${projectId}-${normalizedId}-ws`.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\n if (options.dryRun) {\n console.log(chalk.bold('Would create remote workspace:'));\n console.log(` VM: ${chalk.cyan(vmName)}`);\n console.log(` Project: ${chalk.dim(projectId)}`);\n console.log(` Infra: ${chalk.dim(infraVm)}`);\n console.log(` Branch: ${chalk.dim(branchName)}`);\n throw new Error('Dry run - not implemented in this module');\n }\n\n // Get git remote URL\n let repoUrl = '';\n try {\n const { stdout } = await execAsync('git remote get-url origin', {\n cwd: projectRoot,\n encoding: 'utf-8',\n });\n repoUrl = stdout.trim();\n } catch {\n throw new Error('Could not determine git remote URL. Make sure you are in a git repository with a remote origin.');\n }\n\n if (options.spinner) {\n options.spinner.text = 'Creating VM (this may take 1-2 minutes)...';\n }\n\n // Step 1: Create VM\n await exe.createVm(vmName);\n\n // Step 2: Add GitHub host key and clone repository on VM\n if (options.spinner) {\n options.spinner.text = 'Cloning repository on VM...';\n }\n await exe.ssh(vmName, 'mkdir -p ~/.ssh && ssh-keyscan -t ed25519,rsa github.com >> ~/.ssh/known_hosts 2>/dev/null');\n const cloneResult = await exe.ssh(vmName, `git clone ${repoUrl} ~/workspace`);\n if (cloneResult.exitCode !== 0) {\n await exe.deleteVm(vmName);\n throw new Error(`Failed to clone: ${cloneResult.stderr}`);\n }\n\n // Step 3: Create feature branch\n if (options.spinner) {\n options.spinner.text = 'Creating feature branch...';\n }\n const branchResult = await exe.ssh(vmName, `cd ~/workspace && git checkout -b ${branchName}`);\n if (branchResult.exitCode !== 0) {\n await exe.ssh(vmName, `cd ~/workspace && git checkout ${branchName} || git checkout -b ${branchName}`);\n }\n\n // Step 4: Configure environment for shared infra\n const envContent = `\n# Panopticon Remote Workspace\nWORKSPACE_ID=${normalizedId}\nISSUE_ID=${issueId.toUpperCase()}\n\n# Shared Infrastructure\nPOSTGRES_HOST=${infraVm}\nPOSTGRES_PORT=5432\nPOSTGRES_USER=postgres\nPOSTGRES_PASSWORD=\\${PAN_POSTGRES_PASSWORD:-panopticon}\nDATABASE_NAME=myn_${normalizedId.replace(/-/g, '_')}\n\nREDIS_HOST=${infraVm}\nREDIS_PORT=6379\nREDIS_DATABASE=0\n\n# Spring Boot (if applicable)\nSPRING_DATASOURCE_URL=jdbc:postgresql://${infraVm}:5432/myn_${normalizedId.replace(/-/g, '_')}\nSPRING_DATA_REDIS_HOST=${infraVm}\n`;\n\n await exe.ssh(vmName, `cat > ~/workspace/.env.remote << 'EOF'\n${envContent}\nEOF`);\n\n // Step 5: Create database on shared postgres\n const dbName = `myn_${normalizedId.replace(/-/g, '_')}`;\n try {\n await exe.ssh(infraVm, `docker exec pan-postgres psql -U postgres -c \"CREATE DATABASE ${dbName}\" 2>/dev/null || true`);\n } catch {\n // Non-fatal - database might already exist\n }\n\n // Step 6: Install beads CLI globally on remote VM\n if (options.spinner) {\n options.spinner.text = 'Installing beads CLI...';\n }\n const bdInstalled = await exe.installBeads(vmName);\n if (bdInstalled) {\n await exe.initBeads(vmName, '~/workspace');\n }\n\n // Step 6.5: Copy essential skills to remote VM\n if (options.spinner) {\n options.spinner.text = 'Copying skills to remote VM...';\n }\n await exe.copySkillsToVm(vmName);\n\n // Step 7: Start containers if docker compose exists\n let containersStarted = false;\n let frontendUrl = '';\n let apiUrl = '';\n\n const composeCheck = await exe.ssh(vmName, 'ls ~/workspace/docker-compose.yml ~/workspace/.devcontainer/docker-compose.yml 2>/dev/null | head -1');\n\n if (composeCheck.stdout.trim()) {\n if (options.spinner) {\n options.spinner.text = 'Starting containers...';\n }\n const composeDir = composeCheck.stdout.includes('.devcontainer')\n ? '~/workspace/.devcontainer'\n : '~/workspace';\n\n const upResult = await exe.ssh(vmName, `cd ${composeDir} && docker compose up -d 2>&1`);\n containersStarted = upResult.exitCode === 0;\n\n if (containersStarted) {\n if (options.spinner) {\n options.spinner.text = 'Exposing ports...';\n }\n try {\n frontendUrl = await exe.exposePort(vmName, 4173);\n apiUrl = await exe.exposePort(vmName, 7000);\n } catch {\n // Port exposure failed - not critical\n }\n }\n }\n\n // Step 8: Save workspace metadata\n const metadata: RemoteWorkspaceMetadata = {\n id: normalizedId,\n issue: issueId.toUpperCase(),\n provider: 'exe',\n vmName,\n infraVm,\n database: dbName,\n redisDb: 0,\n urls: {\n frontend: frontendUrl || undefined,\n api: apiUrl || undefined,\n },\n created: new Date(),\n location: 'remote',\n };\n\n saveWorkspaceMetadata(metadata);\n\n return metadata;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAOA,OAAO,WAAW;AAGlB,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAE1B;AAGA;AAEA,IAAM,YAAY,UAAU,IAAI;AAUhC,eAAsB,sBACpB,SACA,UAAwC,CAAC,GACP;AAClC,QAAM,SAAS,WAAW;AAC1B,QAAM,eAAe,OAAO;AAE5B,MAAI,CAAC,cAAc,SAAS;AAC1B,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAEA,QAAM,eAAe,QAAQ,YAAY,EAAE,QAAQ,eAAe,GAAG;AACrE,QAAM,aAAa,WAAW,YAAY;AAC1C,QAAM,UAAU,aAAa,KAAK,YAAY;AAC9C,QAAM,MAAM,kBAAkB,EAAE,QAAQ,CAAC;AAGzC,QAAM,aAAa,kBAAkB,OAAO;AAC5C,QAAM,gBAAgB,aAAa,kBAAkB,UAAU,IAAI;AACnE,QAAM,cAAc,eAAe,QAAQ,QAAQ,IAAI;AAGvD,MAAI,YAAY,YAAY,YAAY;AACxC,MAAI,CAAC,aAAa,eAAe,aAAa;AAC5C,gBAAY,cAAc,YAAY,YAAY;AAAA,EACpD;AACA,MAAI,CAAC,WAAW;AACd,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,MAAM,UAAU,6BAA6B;AAAA,QAC9D,KAAK;AAAA,QACL,UAAU;AAAA,MACZ,CAAC;AACD,YAAM,YAAY,OAAO,KAAK,EAAE,MAAM,sBAAsB;AAC5D,kBAAY,YAAY,UAAU,CAAC,EAAE,YAAY,EAAE,QAAQ,eAAe,GAAG,IAAI;AAAA,IACnF,QAAQ;AACN,kBAAY;AAAA,IACd;AAAA,EACF;AAGA,QAAM,SAAS,GAAG,SAAS,IAAI,YAAY,MAAM,YAAY,EAAE,QAAQ,eAAe,GAAG;AAEzF,MAAI,QAAQ,QAAQ;AAClB,YAAQ,IAAI,MAAM,KAAK,gCAAgC,CAAC;AACxD,YAAQ,IAAI,gBAAgB,MAAM,KAAK,MAAM,CAAC,EAAE;AAChD,YAAQ,IAAI,gBAAgB,MAAM,IAAI,SAAS,CAAC,EAAE;AAClD,YAAQ,IAAI,gBAAgB,MAAM,IAAI,OAAO,CAAC,EAAE;AAChD,YAAQ,IAAI,gBAAgB,MAAM,IAAI,UAAU,CAAC,EAAE;AACnD,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAGA,MAAI,UAAU;AACd,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,UAAU,6BAA6B;AAAA,MAC9D,KAAK;AAAA,MACL,UAAU;AAAA,IACZ,CAAC;AACD,cAAU,OAAO,KAAK;AAAA,EACxB,QAAQ;AACN,UAAM,IAAI,MAAM,iGAAiG;AAAA,EACnH;AAEA,MAAI,QAAQ,SAAS;AACnB,YAAQ,QAAQ,OAAO;AAAA,EACzB;AAGA,QAAM,IAAI,SAAS,MAAM;AAGzB,MAAI,QAAQ,SAAS;AACnB,YAAQ,QAAQ,OAAO;AAAA,EACzB;AACA,QAAM,IAAI,IAAI,QAAQ,4FAA4F;AAClH,QAAM,cAAc,MAAM,IAAI,IAAI,QAAQ,aAAa,OAAO,cAAc;AAC5E,MAAI,YAAY,aAAa,GAAG;AAC9B,UAAM,IAAI,SAAS,MAAM;AACzB,UAAM,IAAI,MAAM,oBAAoB,YAAY,MAAM,EAAE;AAAA,EAC1D;AAGA,MAAI,QAAQ,SAAS;AACnB,YAAQ,QAAQ,OAAO;AAAA,EACzB;AACA,QAAM,eAAe,MAAM,IAAI,IAAI,QAAQ,qCAAqC,UAAU,EAAE;AAC5F,MAAI,aAAa,aAAa,GAAG;AAC/B,UAAM,IAAI,IAAI,QAAQ,kCAAkC,UAAU,uBAAuB,UAAU,EAAE;AAAA,EACvG;AAGA,QAAM,aAAa;AAAA;AAAA,eAEN,YAAY;AAAA,WAChB,QAAQ,YAAY,CAAC;AAAA;AAAA;AAAA,gBAGhB,OAAO;AAAA;AAAA;AAAA;AAAA,oBAIH,aAAa,QAAQ,MAAM,GAAG,CAAC;AAAA;AAAA,aAEtC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,0CAKsB,OAAO,aAAa,aAAa,QAAQ,MAAM,GAAG,CAAC;AAAA,yBACpE,OAAO;AAAA;AAG9B,QAAM,IAAI,IAAI,QAAQ;AAAA,EACtB,UAAU;AAAA,IACR;AAGF,QAAM,SAAS,OAAO,aAAa,QAAQ,MAAM,GAAG,CAAC;AACrD,MAAI;AACF,UAAM,IAAI,IAAI,SAAS,iEAAiE,MAAM,uBAAuB;AAAA,EACvH,QAAQ;AAAA,EAER;AAGA,MAAI,QAAQ,SAAS;AACnB,YAAQ,QAAQ,OAAO;AAAA,EACzB;AACA,QAAM,cAAc,MAAM,IAAI,aAAa,MAAM;AACjD,MAAI,aAAa;AACf,UAAM,IAAI,UAAU,QAAQ,aAAa;AAAA,EAC3C;AAGA,MAAI,QAAQ,SAAS;AACnB,YAAQ,QAAQ,OAAO;AAAA,EACzB;AACA,QAAM,IAAI,eAAe,MAAM;AAG/B,MAAI,oBAAoB;AACxB,MAAI,cAAc;AAClB,MAAI,SAAS;AAEb,QAAM,eAAe,MAAM,IAAI,IAAI,QAAQ,sGAAsG;AAEjJ,MAAI,aAAa,OAAO,KAAK,GAAG;AAC9B,QAAI,QAAQ,SAAS;AACnB,cAAQ,QAAQ,OAAO;AAAA,IACzB;AACA,UAAM,aAAa,aAAa,OAAO,SAAS,eAAe,IAC3D,8BACA;AAEJ,UAAM,WAAW,MAAM,IAAI,IAAI,QAAQ,MAAM,UAAU,+BAA+B;AACtF,wBAAoB,SAAS,aAAa;AAE1C,QAAI,mBAAmB;AACrB,UAAI,QAAQ,SAAS;AACnB,gBAAQ,QAAQ,OAAO;AAAA,MACzB;AACA,UAAI;AACF,sBAAc,MAAM,IAAI,WAAW,QAAQ,IAAI;AAC/C,iBAAS,MAAM,IAAI,WAAW,QAAQ,GAAI;AAAA,MAC5C,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAoC;AAAA,IACxC,IAAI;AAAA,IACJ,OAAO,QAAQ,YAAY;AAAA,IAC3B,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,UAAU,eAAe;AAAA,MACzB,KAAK,UAAU;AAAA,IACjB;AAAA,IACA,SAAS,oBAAI,KAAK;AAAA,IAClB,UAAU;AAAA,EACZ;AAEA,wBAAsB,QAAQ;AAE9B,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/remote-workspace.ts"],"sourcesContent":["/**\n * Remote Workspace Creation\n *\n * Shared module for creating remote workspaces.\n * Used by both workspace.ts (explicit creation) and work/issue.ts (auto-creation).\n */\n\nimport chalk from 'chalk';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { loadConfig } from './config.js';\nimport { createExeProvider } from './remote/exe-provider.js';\nimport { saveWorkspaceMetadata } from './remote/workspace-metadata.js';\nimport type { RemoteWorkspaceMetadata } from './remote/interface.js';\nimport { extractTeamPrefix, findProjectByTeam, resolveProjectFromIssue } from './projects.js';\n\nconst execAsync = promisify(exec);\n\nexport interface CreateRemoteWorkspaceOptions {\n dryRun?: boolean;\n spinner?: { text: string };\n}\n\n/**\n * Create a remote workspace on exe.dev\n */\nexport async function createRemoteWorkspace(\n issueId: string,\n options: CreateRemoteWorkspaceOptions = {}\n): Promise<RemoteWorkspaceMetadata> {\n const config = loadConfig();\n const remoteConfig = config.remote;\n\n if (!remoteConfig?.enabled) {\n throw new Error('Remote workspaces not enabled. Run `pan remote setup`');\n }\n\n const normalizedId = issueId.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n const branchName = `feature/${normalizedId}`;\n const infraVm = remoteConfig.exe?.infra_vm || 'pan-infra';\n const exe = createExeProvider({ infraVm });\n\n // Determine project context\n const teamPrefix = extractTeamPrefix(issueId);\n const projectConfig = teamPrefix ? findProjectByTeam(teamPrefix) : null;\n const projectRoot = projectConfig?.path || process.cwd();\n\n // Determine project identifier for VM name\n let projectId = teamPrefix?.toLowerCase();\n if (!projectId && projectConfig?.linear_team) {\n projectId = projectConfig.linear_team.toLowerCase();\n }\n if (!projectId) {\n try {\n const { stdout } = await execAsync('git remote get-url origin', {\n cwd: projectRoot,\n encoding: 'utf-8',\n });\n const repoMatch = stdout.trim().match(/\\/([^\\/]+?)(\\.git)?$/);\n projectId = repoMatch ? repoMatch[1].toLowerCase().replace(/[^a-z0-9-]/g, '-') : 'proj';\n } catch {\n projectId = 'proj';\n }\n }\n\n // VM names must be valid hostnames (start with letter, alphanumeric + hyphens)\n const vmName = `${projectId}-${normalizedId}-ws`.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\n if (options.dryRun) {\n console.log(chalk.bold('Would create remote workspace:'));\n console.log(` VM: ${chalk.cyan(vmName)}`);\n console.log(` Project: ${chalk.dim(projectId)}`);\n console.log(` Infra: ${chalk.dim(infraVm)}`);\n console.log(` Branch: ${chalk.dim(branchName)}`);\n throw new Error('Dry run - not implemented in this module');\n }\n\n // Get git remote URL\n let repoUrl = '';\n try {\n const { stdout } = await execAsync('git remote get-url origin', {\n cwd: projectRoot,\n encoding: 'utf-8',\n });\n repoUrl = stdout.trim();\n } catch {\n throw new Error('Could not determine git remote URL. Make sure you are in a git repository with a remote origin.');\n }\n\n if (options.spinner) {\n options.spinner.text = 'Creating VM (this may take 1-2 minutes)...';\n }\n\n // Step 1: Create VM\n await exe.createVm(vmName);\n\n // Step 2: Add GitHub host key and clone repository on VM\n if (options.spinner) {\n options.spinner.text = 'Cloning repository on VM...';\n }\n await exe.ssh(vmName, 'mkdir -p ~/.ssh && ssh-keyscan -t ed25519,rsa github.com >> ~/.ssh/known_hosts 2>/dev/null');\n const cloneResult = await exe.ssh(vmName, `git clone ${repoUrl} ~/workspace`);\n if (cloneResult.exitCode !== 0) {\n await exe.deleteVm(vmName);\n throw new Error(`Failed to clone: ${cloneResult.stderr}`);\n }\n\n // Step 3: Create feature branch\n if (options.spinner) {\n options.spinner.text = 'Creating feature branch...';\n }\n const branchResult = await exe.ssh(vmName, `cd ~/workspace && git checkout -b ${branchName}`);\n if (branchResult.exitCode !== 0) {\n await exe.ssh(vmName, `cd ~/workspace && git checkout ${branchName} || git checkout -b ${branchName}`);\n }\n\n // Step 4: Configure environment for shared infra\n const envContent = `\n# Panopticon Remote Workspace\nWORKSPACE_ID=${normalizedId}\nISSUE_ID=${issueId.toUpperCase()}\n\n# Shared Infrastructure\nPOSTGRES_HOST=${infraVm}\nPOSTGRES_PORT=5432\nPOSTGRES_USER=postgres\nPOSTGRES_PASSWORD=\\${PAN_POSTGRES_PASSWORD:-panopticon}\nDATABASE_NAME=myn_${normalizedId.replace(/-/g, '_')}\n\nREDIS_HOST=${infraVm}\nREDIS_PORT=6379\nREDIS_DATABASE=0\n\n# Spring Boot (if applicable)\nSPRING_DATASOURCE_URL=jdbc:postgresql://${infraVm}:5432/myn_${normalizedId.replace(/-/g, '_')}\nSPRING_DATA_REDIS_HOST=${infraVm}\n`;\n\n await exe.ssh(vmName, `cat > ~/workspace/.env.remote << 'EOF'\n${envContent}\nEOF`);\n\n // Step 5: Create database on shared postgres\n const dbName = `myn_${normalizedId.replace(/-/g, '_')}`;\n try {\n await exe.ssh(infraVm, `docker exec pan-postgres psql -U postgres -c \"CREATE DATABASE ${dbName}\" 2>/dev/null || true`);\n } catch {\n // Non-fatal - database might already exist\n }\n\n // Step 6: Install beads CLI globally on remote VM\n if (options.spinner) {\n options.spinner.text = 'Installing beads CLI...';\n }\n const bdInstalled = await exe.installBeads(vmName);\n if (bdInstalled) {\n await exe.initBeads(vmName, '~/workspace');\n }\n\n // Step 6.5: Copy essential skills to remote VM\n if (options.spinner) {\n options.spinner.text = 'Copying skills to remote VM...';\n }\n await exe.copySkillsToVm(vmName);\n\n // Step 7: Start containers if docker compose exists\n let containersStarted = false;\n let frontendUrl = '';\n let apiUrl = '';\n\n const composeCheck = await exe.ssh(vmName, 'ls ~/workspace/docker-compose.yml ~/workspace/.devcontainer/docker-compose.yml 2>/dev/null | head -1');\n\n if (composeCheck.stdout.trim()) {\n if (options.spinner) {\n options.spinner.text = 'Starting containers...';\n }\n const composeDir = composeCheck.stdout.includes('.devcontainer')\n ? '~/workspace/.devcontainer'\n : '~/workspace';\n\n const upResult = await exe.ssh(vmName, `cd ${composeDir} && docker compose up -d 2>&1`);\n containersStarted = upResult.exitCode === 0;\n\n if (containersStarted) {\n if (options.spinner) {\n options.spinner.text = 'Exposing ports...';\n }\n try {\n frontendUrl = await exe.exposePort(vmName, 4173);\n apiUrl = await exe.exposePort(vmName, 7000);\n } catch {\n // Port exposure failed - not critical\n }\n }\n }\n\n // Step 8: Save workspace metadata\n const metadata: RemoteWorkspaceMetadata = {\n id: normalizedId,\n issue: issueId.toUpperCase(),\n provider: 'exe',\n vmName,\n infraVm,\n database: dbName,\n redisDb: 0,\n urls: {\n frontend: frontendUrl || undefined,\n api: apiUrl || undefined,\n },\n created: new Date(),\n location: 'remote',\n };\n\n saveWorkspaceMetadata(metadata);\n\n return metadata;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getRecentRunLogs,
|
|
3
3
|
init_specialist_logs
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2NIAOCIC.js";
|
|
5
5
|
import {
|
|
6
6
|
getModelId,
|
|
7
7
|
init_work_type_router
|
|
@@ -253,4 +253,4 @@ export {
|
|
|
253
253
|
regenerateContextDigest,
|
|
254
254
|
scheduleDigestGeneration
|
|
255
255
|
};
|
|
256
|
-
//# sourceMappingURL=specialist-context-
|
|
256
|
+
//# sourceMappingURL=specialist-context-N32QBNNQ.js.map
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
isRunLogActive,
|
|
17
17
|
listRunLogs,
|
|
18
18
|
parseLogMetadata
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-2NIAOCIC.js";
|
|
20
20
|
import "./chunk-VIWUCJ4V.js";
|
|
21
21
|
import "./chunk-BBCUK6N2.js";
|
|
22
22
|
import "./chunk-JY7R7V4G.js";
|
|
@@ -41,4 +41,4 @@ export {
|
|
|
41
41
|
listRunLogs,
|
|
42
42
|
parseLogMetadata
|
|
43
43
|
};
|
|
44
|
-
//# sourceMappingURL=specialist-logs-
|
|
44
|
+
//# sourceMappingURL=specialist-logs-GF3YV4KL.js.map
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
wakeSpecialist,
|
|
56
56
|
wakeSpecialistOrQueue,
|
|
57
57
|
wakeSpecialistWithTask
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-2NIAOCIC.js";
|
|
59
59
|
import "./chunk-VIWUCJ4V.js";
|
|
60
60
|
import "./chunk-BBCUK6N2.js";
|
|
61
61
|
import "./chunk-JY7R7V4G.js";
|
|
@@ -119,4 +119,4 @@ export {
|
|
|
119
119
|
wakeSpecialistOrQueue,
|
|
120
120
|
wakeSpecialistWithTask
|
|
121
121
|
};
|
|
122
|
-
//# sourceMappingURL=specialists-
|
|
122
|
+
//# sourceMappingURL=specialists-JBIW6MP4.js.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cleanupTemplateFiles,
|
|
3
3
|
generatePanopticonTraefikConfig
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-3XAB4IXF.js";
|
|
5
|
+
import "./chunk-VU4FLXV5.js";
|
|
6
6
|
import "./chunk-6HXKTOD7.js";
|
|
7
7
|
import "./chunk-ZHC57RCV.js";
|
|
8
8
|
export {
|
|
9
9
|
cleanupTemplateFiles,
|
|
10
10
|
generatePanopticonTraefikConfig
|
|
11
11
|
};
|
|
12
|
-
//# sourceMappingURL=traefik-
|
|
12
|
+
//# sourceMappingURL=traefik-CUJM6K5Z.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "panopticon-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.32",
|
|
4
4
|
"description": "Multi-agent orchestration for AI coding assistants (Claude Code, Codex, Cursor, Gemini CLI)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -48,8 +48,9 @@
|
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"dev": "tsx watch src/cli/index.ts",
|
|
51
|
-
"build": "npm run build:cli && npm run build:dashboard",
|
|
51
|
+
"build": "npm run build:cli && npm run build:scripts && npm run build:dashboard",
|
|
52
52
|
"build:cli": "tsup",
|
|
53
|
+
"build:scripts": "esbuild scripts/record-cost-event.ts --bundle --platform=node --format=esm --outfile=scripts/record-cost-event.js",
|
|
53
54
|
"build:dashboard": "npm run build:dashboard:frontend && npm run build:dashboard:server",
|
|
54
55
|
"build:dashboard:frontend": "cd src/dashboard/frontend && npm run build",
|
|
55
56
|
"build:dashboard:server": "cd src/dashboard/server && npm run build",
|
|
@@ -1,94 +1,258 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Record a cost event from Claude Code tool usage
|
|
4
|
-
* Called by heartbeat-hook with JSON input on stdin
|
|
5
|
-
*/
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
3
|
+
// scripts/record-cost-event.ts
|
|
4
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, openSync, readSync, fstatSync, closeSync } from "fs";
|
|
5
|
+
import { join as join4 } from "path";
|
|
6
|
+
import { homedir as homedir3 } from "os";
|
|
10
7
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
const input = readFileSync(0, 'utf-8');
|
|
15
|
-
toolInfo = JSON.parse(input);
|
|
16
|
-
} catch (err) {
|
|
17
|
-
// Silent failure - don't break Claude Code execution
|
|
18
|
-
process.exit(0);
|
|
19
|
-
}
|
|
8
|
+
// src/lib/cost.ts
|
|
9
|
+
import { join as join2 } from "path";
|
|
20
10
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
// src/lib/paths.ts
|
|
12
|
+
import { homedir } from "os";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
import { dirname } from "path";
|
|
16
|
+
var PANOPTICON_HOME = process.env.PANOPTICON_HOME || join(homedir(), ".panopticon");
|
|
17
|
+
var CONFIG_DIR = PANOPTICON_HOME;
|
|
18
|
+
var SKILLS_DIR = join(PANOPTICON_HOME, "skills");
|
|
19
|
+
var COMMANDS_DIR = join(PANOPTICON_HOME, "commands");
|
|
20
|
+
var AGENTS_DIR = join(PANOPTICON_HOME, "agents");
|
|
21
|
+
var BIN_DIR = join(PANOPTICON_HOME, "bin");
|
|
22
|
+
var BACKUPS_DIR = join(PANOPTICON_HOME, "backups");
|
|
23
|
+
var COSTS_DIR = join(PANOPTICON_HOME, "costs");
|
|
24
|
+
var HEARTBEATS_DIR = join(PANOPTICON_HOME, "heartbeats");
|
|
25
|
+
var TRAEFIK_DIR = join(PANOPTICON_HOME, "traefik");
|
|
26
|
+
var TRAEFIK_DYNAMIC_DIR = join(TRAEFIK_DIR, "dynamic");
|
|
27
|
+
var TRAEFIK_CERTS_DIR = join(TRAEFIK_DIR, "certs");
|
|
28
|
+
var CERTS_DIR = join(PANOPTICON_HOME, "certs");
|
|
29
|
+
var CONFIG_FILE = join(CONFIG_DIR, "config.toml");
|
|
30
|
+
var SETTINGS_FILE = join(CONFIG_DIR, "settings.json");
|
|
31
|
+
var CLAUDE_DIR = join(homedir(), ".claude");
|
|
32
|
+
var CODEX_DIR = join(homedir(), ".codex");
|
|
33
|
+
var CURSOR_DIR = join(homedir(), ".cursor");
|
|
34
|
+
var GEMINI_DIR = join(homedir(), ".gemini");
|
|
35
|
+
var OPENCODE_DIR = join(homedir(), ".opencode");
|
|
36
|
+
var SYNC_TARGETS = {
|
|
37
|
+
claude: {
|
|
38
|
+
skills: join(CLAUDE_DIR, "skills"),
|
|
39
|
+
commands: join(CLAUDE_DIR, "commands"),
|
|
40
|
+
agents: join(CLAUDE_DIR, "agents")
|
|
41
|
+
},
|
|
42
|
+
codex: {
|
|
43
|
+
skills: join(CODEX_DIR, "skills"),
|
|
44
|
+
commands: join(CODEX_DIR, "commands"),
|
|
45
|
+
agents: join(CODEX_DIR, "agents")
|
|
46
|
+
},
|
|
47
|
+
cursor: {
|
|
48
|
+
skills: join(CURSOR_DIR, "skills"),
|
|
49
|
+
commands: join(CURSOR_DIR, "commands"),
|
|
50
|
+
agents: join(CURSOR_DIR, "agents")
|
|
51
|
+
},
|
|
52
|
+
gemini: {
|
|
53
|
+
skills: join(GEMINI_DIR, "skills"),
|
|
54
|
+
commands: join(GEMINI_DIR, "commands"),
|
|
55
|
+
agents: join(GEMINI_DIR, "agents")
|
|
56
|
+
},
|
|
57
|
+
opencode: {
|
|
58
|
+
skills: join(OPENCODE_DIR, "skills"),
|
|
59
|
+
commands: join(OPENCODE_DIR, "commands"),
|
|
60
|
+
agents: join(OPENCODE_DIR, "agents")
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var TEMPLATES_DIR = join(PANOPTICON_HOME, "templates");
|
|
64
|
+
var CLAUDE_MD_TEMPLATES = join(TEMPLATES_DIR, "claude-md", "sections");
|
|
65
|
+
var currentFile = fileURLToPath(import.meta.url);
|
|
66
|
+
var currentDir = dirname(currentFile);
|
|
67
|
+
var packageRoot;
|
|
68
|
+
if (currentDir.includes("/src/")) {
|
|
69
|
+
packageRoot = dirname(dirname(currentDir));
|
|
70
|
+
} else {
|
|
71
|
+
packageRoot = currentDir.endsWith("/lib") ? dirname(dirname(currentDir)) : dirname(currentDir);
|
|
26
72
|
}
|
|
73
|
+
var SOURCE_TEMPLATES_DIR = join(packageRoot, "templates");
|
|
74
|
+
var SOURCE_TRAEFIK_TEMPLATES = join(SOURCE_TEMPLATES_DIR, "traefik");
|
|
75
|
+
var SOURCE_SCRIPTS_DIR = join(packageRoot, "scripts");
|
|
76
|
+
var SOURCE_SKILLS_DIR = join(packageRoot, "skills");
|
|
77
|
+
var SOURCE_DEV_SKILLS_DIR = join(packageRoot, "dev-skills");
|
|
27
78
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
79
|
+
// src/lib/cost.ts
|
|
80
|
+
var DEFAULT_PRICING = [
|
|
81
|
+
// Anthropic - 4.6 series
|
|
82
|
+
{ provider: "anthropic", model: "claude-opus-4.6", inputPer1k: 5e-3, outputPer1k: 0.025, cacheReadPer1k: 5e-4, cacheWrite5mPer1k: 625e-5, cacheWrite1hPer1k: 0.01, currency: "USD" },
|
|
83
|
+
{ provider: "anthropic", model: "claude-sonnet-4.5", inputPer1k: 3e-3, outputPer1k: 0.015, cacheReadPer1k: 3e-4, cacheWrite5mPer1k: 375e-5, cacheWrite1hPer1k: 6e-3, currency: "USD" },
|
|
84
|
+
{ provider: "anthropic", model: "claude-haiku-4.5", inputPer1k: 1e-3, outputPer1k: 5e-3, cacheReadPer1k: 1e-4, cacheWrite5mPer1k: 125e-5, cacheWrite1hPer1k: 2e-3, currency: "USD" },
|
|
85
|
+
// Anthropic - 4.x series
|
|
86
|
+
{ provider: "anthropic", model: "claude-opus-4-1", inputPer1k: 0.015, outputPer1k: 0.075, cacheReadPer1k: 15e-4, cacheWrite5mPer1k: 0.01875, cacheWrite1hPer1k: 0.03, currency: "USD" },
|
|
87
|
+
{ provider: "anthropic", model: "claude-opus-4", inputPer1k: 0.015, outputPer1k: 0.075, cacheReadPer1k: 15e-4, cacheWrite5mPer1k: 0.01875, cacheWrite1hPer1k: 0.03, currency: "USD" },
|
|
88
|
+
{ provider: "anthropic", model: "claude-sonnet-4", inputPer1k: 3e-3, outputPer1k: 0.015, cacheReadPer1k: 3e-4, cacheWrite5mPer1k: 375e-5, cacheWrite1hPer1k: 6e-3, currency: "USD" },
|
|
89
|
+
// Anthropic - Legacy
|
|
90
|
+
{ provider: "anthropic", model: "claude-haiku-3", inputPer1k: 25e-5, outputPer1k: 125e-5, cacheReadPer1k: 3e-5, cacheWrite5mPer1k: 3e-4, cacheWrite1hPer1k: 5e-4, currency: "USD" },
|
|
91
|
+
// OpenAI
|
|
92
|
+
{ provider: "openai", model: "gpt-4-turbo", inputPer1k: 0.01, outputPer1k: 0.03, currency: "USD" },
|
|
93
|
+
{ provider: "openai", model: "gpt-4o", inputPer1k: 5e-3, outputPer1k: 0.015, currency: "USD" },
|
|
94
|
+
{ provider: "openai", model: "gpt-4o-mini", inputPer1k: 15e-5, outputPer1k: 6e-4, currency: "USD" },
|
|
95
|
+
// Google
|
|
96
|
+
{ provider: "google", model: "gemini-1.5-pro", inputPer1k: 125e-5, outputPer1k: 5e-3, currency: "USD" },
|
|
97
|
+
{ provider: "google", model: "gemini-1.5-flash", inputPer1k: 75e-6, outputPer1k: 3e-4, currency: "USD" },
|
|
98
|
+
// Moonshot AI (Kimi)
|
|
99
|
+
{ provider: "custom", model: "kimi-for-coding", inputPer1k: 6e-4, outputPer1k: 2e-3, cacheReadPer1k: 6e-5, cacheWrite5mPer1k: 75e-5, currency: "USD" },
|
|
100
|
+
{ provider: "custom", model: "kimi-k2.5", inputPer1k: 6e-4, outputPer1k: 2e-3, cacheReadPer1k: 6e-5, cacheWrite5mPer1k: 75e-5, currency: "USD" }
|
|
101
|
+
];
|
|
102
|
+
function calculateCost(usage, pricing) {
|
|
103
|
+
let cost = 0;
|
|
104
|
+
let inputMultiplier = 1;
|
|
105
|
+
let outputMultiplier = 1;
|
|
106
|
+
const totalInputTokens = usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
|
|
107
|
+
if ((pricing.model === "claude-sonnet-4" || pricing.model === "claude-sonnet-4.5") && totalInputTokens > 2e5) {
|
|
108
|
+
inputMultiplier = 2;
|
|
109
|
+
outputMultiplier = 1.5;
|
|
110
|
+
}
|
|
111
|
+
cost += usage.inputTokens / 1e3 * pricing.inputPer1k * inputMultiplier;
|
|
112
|
+
cost += usage.outputTokens / 1e3 * pricing.outputPer1k * outputMultiplier;
|
|
113
|
+
if (usage.cacheReadTokens && pricing.cacheReadPer1k) {
|
|
114
|
+
cost += usage.cacheReadTokens / 1e3 * pricing.cacheReadPer1k;
|
|
115
|
+
}
|
|
116
|
+
if (usage.cacheWriteTokens) {
|
|
117
|
+
const ttl = usage.cacheTTL || "5m";
|
|
118
|
+
const cacheWritePrice = ttl === "1h" ? pricing.cacheWrite1hPer1k : pricing.cacheWrite5mPer1k;
|
|
119
|
+
if (cacheWritePrice) {
|
|
120
|
+
cost += usage.cacheWriteTokens / 1e3 * cacheWritePrice;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return Math.round(cost * 1e6) / 1e6;
|
|
37
124
|
}
|
|
125
|
+
function getPricing(provider, model) {
|
|
126
|
+
let pricing = DEFAULT_PRICING.find(
|
|
127
|
+
(p) => p.provider === provider && p.model === model
|
|
128
|
+
);
|
|
129
|
+
if (!pricing) {
|
|
130
|
+
pricing = DEFAULT_PRICING.find(
|
|
131
|
+
(p) => p.provider === provider && model.startsWith(p.model)
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return pricing || null;
|
|
135
|
+
}
|
|
136
|
+
var BUDGETS_FILE = join2(COSTS_DIR, "budgets.json");
|
|
38
137
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
138
|
+
// src/lib/costs/events.ts
|
|
139
|
+
import { existsSync, mkdirSync, readFileSync, appendFileSync, writeFileSync, renameSync } from "fs";
|
|
140
|
+
import { join as join3 } from "path";
|
|
141
|
+
import { homedir as homedir2 } from "os";
|
|
142
|
+
function getCostsDir() {
|
|
143
|
+
return join3(process.env.HOME || homedir2(), ".panopticon", "costs");
|
|
144
|
+
}
|
|
145
|
+
function getEventsFile() {
|
|
146
|
+
return join3(getCostsDir(), "events.jsonl");
|
|
147
|
+
}
|
|
148
|
+
function ensureEventsFile() {
|
|
149
|
+
const costsDir = getCostsDir();
|
|
150
|
+
const eventsFile = getEventsFile();
|
|
151
|
+
mkdirSync(costsDir, { recursive: true });
|
|
152
|
+
if (!existsSync(eventsFile)) {
|
|
153
|
+
writeFileSync(eventsFile, "", "utf-8");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function appendCostEvent(event2) {
|
|
157
|
+
ensureEventsFile();
|
|
158
|
+
if (!event2.ts || !event2.agentId || !event2.issueId || !event2.model) {
|
|
159
|
+
throw new Error("Missing required event fields: ts, agentId, issueId, model");
|
|
160
|
+
}
|
|
161
|
+
const line = JSON.stringify(event2) + "\n";
|
|
162
|
+
appendFileSync(getEventsFile(), line, "utf-8");
|
|
48
163
|
}
|
|
49
164
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
165
|
+
// scripts/record-cost-event.ts
|
|
166
|
+
var event;
|
|
167
|
+
try {
|
|
168
|
+
const input = readFileSync2(0, "utf-8");
|
|
169
|
+
event = JSON.parse(input);
|
|
170
|
+
} catch {
|
|
54
171
|
process.exit(0);
|
|
55
172
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Record cost event
|
|
173
|
+
var transcriptPath = event?.transcript_path;
|
|
174
|
+
if (!transcriptPath || !existsSync2(transcriptPath)) {
|
|
175
|
+
process.exit(0);
|
|
176
|
+
}
|
|
177
|
+
var sessionId = event?.session_id || "unknown";
|
|
178
|
+
var stateDir = join4(process.env.HOME || homedir3(), ".panopticon", "costs", "state");
|
|
179
|
+
mkdirSync2(stateDir, { recursive: true });
|
|
180
|
+
var stateFile = join4(stateDir, `${sessionId}.offset`);
|
|
181
|
+
var lastOffset = 0;
|
|
182
|
+
if (existsSync2(stateFile)) {
|
|
183
|
+
try {
|
|
184
|
+
lastOffset = parseInt(readFileSync2(stateFile, "utf-8").trim(), 10) || 0;
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
var fd;
|
|
74
189
|
try {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
agentId,
|
|
79
|
-
issueId,
|
|
80
|
-
sessionType,
|
|
81
|
-
provider,
|
|
82
|
-
model,
|
|
83
|
-
input: inputTokens,
|
|
84
|
-
output: outputTokens,
|
|
85
|
-
cacheRead: cacheReadTokens,
|
|
86
|
-
cacheWrite: cacheWriteTokens,
|
|
87
|
-
cost,
|
|
88
|
-
});
|
|
89
|
-
} catch (err) {
|
|
90
|
-
// Silent failure - don't break Claude Code execution
|
|
91
|
-
console.error('Failed to record cost event:', err);
|
|
190
|
+
fd = openSync(transcriptPath, "r");
|
|
191
|
+
} catch {
|
|
192
|
+
process.exit(0);
|
|
92
193
|
}
|
|
93
|
-
|
|
194
|
+
var stat = fstatSync(fd);
|
|
195
|
+
if (stat.size <= lastOffset) {
|
|
196
|
+
closeSync(fd);
|
|
197
|
+
writeFileSync2(stateFile, String(stat.size), "utf-8");
|
|
198
|
+
process.exit(0);
|
|
199
|
+
}
|
|
200
|
+
var bytesToRead = stat.size - lastOffset;
|
|
201
|
+
var buffer = Buffer.alloc(bytesToRead);
|
|
202
|
+
readSync(fd, buffer, 0, bytesToRead, lastOffset);
|
|
203
|
+
closeSync(fd);
|
|
204
|
+
var newContent = buffer.toString("utf-8");
|
|
205
|
+
var lines = newContent.split("\n");
|
|
206
|
+
var agentId = process.env.PANOPTICON_AGENT_ID || "unattributed";
|
|
207
|
+
var issueId = process.env.PANOPTICON_ISSUE_ID || "UNKNOWN";
|
|
208
|
+
var sessionType = process.env.PANOPTICON_SESSION_TYPE || "implementation";
|
|
209
|
+
for (const line of lines) {
|
|
210
|
+
if (!line.trim()) continue;
|
|
211
|
+
try {
|
|
212
|
+
const entry = JSON.parse(line);
|
|
213
|
+
if (entry.type !== "assistant" || !entry.message?.usage) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const usage = entry.message.usage;
|
|
217
|
+
const model = entry.message.model || "claude-sonnet-4";
|
|
218
|
+
const inputTokens = usage.input_tokens || 0;
|
|
219
|
+
const outputTokens = usage.output_tokens || 0;
|
|
220
|
+
const cacheReadTokens = usage.cache_read_input_tokens || 0;
|
|
221
|
+
const cacheWriteTokens = usage.cache_creation_input_tokens || 0;
|
|
222
|
+
if (inputTokens === 0 && outputTokens === 0 && cacheReadTokens === 0 && cacheWriteTokens === 0) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
let provider = "anthropic";
|
|
226
|
+
if (model.includes("gpt")) {
|
|
227
|
+
provider = "openai";
|
|
228
|
+
} else if (model.includes("gemini")) {
|
|
229
|
+
provider = "google";
|
|
230
|
+
}
|
|
231
|
+
const pricing = getPricing(provider, model);
|
|
232
|
+
if (!pricing) continue;
|
|
233
|
+
const cost = calculateCost({
|
|
234
|
+
inputTokens,
|
|
235
|
+
outputTokens,
|
|
236
|
+
cacheReadTokens,
|
|
237
|
+
cacheWriteTokens,
|
|
238
|
+
cacheTTL: "5m"
|
|
239
|
+
}, pricing);
|
|
240
|
+
appendCostEvent({
|
|
241
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
242
|
+
type: "cost",
|
|
243
|
+
agentId,
|
|
244
|
+
issueId,
|
|
245
|
+
sessionType,
|
|
246
|
+
provider,
|
|
247
|
+
model,
|
|
248
|
+
input: inputTokens,
|
|
249
|
+
output: outputTokens,
|
|
250
|
+
cacheRead: cacheReadTokens,
|
|
251
|
+
cacheWrite: cacheWriteTokens,
|
|
252
|
+
cost
|
|
253
|
+
});
|
|
254
|
+
} catch {
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
writeFileSync2(stateFile, String(stat.size), "utf-8");
|
|
94
258
|
process.exit(0);
|