pantheon-opencode 1.3.4 → 1.3.5-beta.52.c75b04d
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 +919 -0
- package/README.md +42 -3
- package/bin/pantheon-init.mjs +17 -0
- package/opencode.json +3 -2
- package/package.json +2 -1
- package/scripts/build-agents-md.mjs +195 -0
- package/scripts/cost.mjs +17 -0
- package/scripts/install/opencode.mjs +91 -22
- package/scripts/release-beta-version.mjs +80 -0
- package/src/pantheon/cost-command.ts +57 -18
- package/src/plugins/tui/package.json +1 -1
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* - missing/unreadable db → FRIENDLY error string, never a crash (tools
|
|
11
11
|
* return errors as TEXT).
|
|
12
12
|
*
|
|
13
|
-
* dbPath resolution: explicit option > env PANTHEON_COST_DB >
|
|
14
|
-
*
|
|
13
|
+
* dbPath resolution: explicit option > env PANTHEON_COST_DB > the version-aware
|
|
14
|
+
* default selected by PANTHEON_OPENCODE_VERSION (v1 or v2). An unset version
|
|
15
|
+
* deliberately preserves V1 behavior. The tool is wired in
|
|
15
16
|
* plugin.ts alongside the delegation toolset (usable by zeus and any agent
|
|
16
17
|
* with tool access — no routing change needed).
|
|
17
18
|
*
|
|
@@ -41,6 +42,8 @@ export interface CostCommandOptions {
|
|
|
41
42
|
dbPath?: string
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
export type OpenCodeVersion = 'v1' | 'v2'
|
|
46
|
+
|
|
44
47
|
/** One cost tool: description + zod args shape + execute (delegation.ts shape). */
|
|
45
48
|
export interface CostTool<Args extends z.ZodRawShape = typeof costArgs> {
|
|
46
49
|
description: string
|
|
@@ -62,15 +65,32 @@ const costArgs = {
|
|
|
62
65
|
.describe('Number of days of delegation history to include (default 7).'),
|
|
63
66
|
} satisfies z.ZodRawShape
|
|
64
67
|
|
|
65
|
-
/**
|
|
66
|
-
function
|
|
67
|
-
const candidates: string[] = []
|
|
68
|
-
const env = process.env.PANTHEON_COST_DB
|
|
69
|
-
if (env !== undefined && env !== '') candidates.push(env)
|
|
68
|
+
/** Return the safe, isolated state DB name for an OpenCode version. */
|
|
69
|
+
function defaultDbPath(version: OpenCodeVersion | undefined): string {
|
|
70
70
|
const xdg = process.env.XDG_DATA_HOME
|
|
71
71
|
const dataHome = xdg !== undefined && xdg !== '' ? xdg : join(homedir(), '.local', 'share')
|
|
72
|
-
|
|
73
|
-
return
|
|
72
|
+
const filename = version === 'v2' ? 'opencode-v2.db' : 'opencode.db'
|
|
73
|
+
return join(dataHome, 'opencode', filename)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolve the database without probing another version's database.
|
|
78
|
+
*
|
|
79
|
+
* Explicit paths always win. A version selector changes exactly one default
|
|
80
|
+
* filename; it never falls back to V1, which prevents a V2 report from
|
|
81
|
+
* silently reading V1 history (or vice versa).
|
|
82
|
+
*/
|
|
83
|
+
export function resolveCostDbPath(options?: CostCommandOptions): string {
|
|
84
|
+
const explicit = options?.dbPath || process.env.PANTHEON_COST_DB
|
|
85
|
+
if (explicit) return explicit
|
|
86
|
+
|
|
87
|
+
const configuredVersion = process.env.PANTHEON_OPENCODE_VERSION
|
|
88
|
+
if (configuredVersion !== undefined && configuredVersion !== 'v1' && configuredVersion !== 'v2') {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`invalid PANTHEON_OPENCODE_VERSION "${configuredVersion}"; expected "v1" or "v2"`,
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
return defaultDbPath(configuredVersion as OpenCodeVersion | undefined)
|
|
74
94
|
}
|
|
75
95
|
|
|
76
96
|
/**
|
|
@@ -79,11 +99,28 @@ function dbCandidates(): string[] {
|
|
|
79
99
|
* to the real opencode.db).
|
|
80
100
|
*/
|
|
81
101
|
function findExistingDb(override?: string): string | undefined {
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
const candidate =
|
|
103
|
+
override === undefined ? resolveCostDbPath() : resolveCostDbPath({ dbPath: override })
|
|
104
|
+
return existsSync(candidate) ? candidate : undefined
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function assertCompatibleSchema(db: {
|
|
108
|
+
prepare(sql: string): { get(): unknown; all(...args: unknown[]): unknown[] }
|
|
109
|
+
}): void {
|
|
110
|
+
const row = db
|
|
111
|
+
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'message'")
|
|
112
|
+
.get()
|
|
113
|
+
if (row === undefined) {
|
|
114
|
+
throw new Error('incompatible opencode.db schema: required "message" table is missing')
|
|
115
|
+
}
|
|
116
|
+
const columns = db.prepare('PRAGMA table_info(message)').all() as Array<{ name?: unknown }>
|
|
117
|
+
const names = new Set(columns.map((column) => column.name))
|
|
118
|
+
const missing = ['data', 'time_created'].filter((name) => !names.has(name))
|
|
119
|
+
if (missing.length > 0) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`incompatible opencode.db schema: message table is missing ${missing.join(', ')}; select the matching V1/V2 database`,
|
|
122
|
+
)
|
|
85
123
|
}
|
|
86
|
-
return undefined
|
|
87
124
|
}
|
|
88
125
|
|
|
89
126
|
/** Aggregate cost + tokens by agent via node:sqlite (read-only). */
|
|
@@ -91,6 +128,7 @@ async function queryWithNodeSqlite(dbPath: string, days: number): Promise<CostRo
|
|
|
91
128
|
const sqlite = await import('node:sqlite')
|
|
92
129
|
const db = new sqlite.DatabaseSync(dbPath, { readOnly: true })
|
|
93
130
|
try {
|
|
131
|
+
assertCompatibleSchema(db)
|
|
94
132
|
const since = Date.now() - days * 86_400_000
|
|
95
133
|
const rows = db.prepare('SELECT data FROM message WHERE time_created >= ?').all(since)
|
|
96
134
|
return aggregateMessages(rows.map((row) => String(row.data)))
|
|
@@ -180,16 +218,17 @@ export function createCostCommand(options?: CostCommandOptions): CostCommand {
|
|
|
180
218
|
try {
|
|
181
219
|
const dbPath = findExistingDb(options?.dbPath)
|
|
182
220
|
if (dbPath === undefined) {
|
|
183
|
-
const expected =
|
|
184
|
-
options?.dbPath ?? process.env.PANTHEON_COST_DB ?? '~/.local/share/opencode/opencode.db'
|
|
221
|
+
const expected = resolveCostDbPath(options)
|
|
185
222
|
return (
|
|
186
223
|
`pantheon_cost failed: opencode.db not found (expected at ${expected}). ` +
|
|
187
224
|
`No cost history is available until opencode has stored session data.`
|
|
188
225
|
)
|
|
189
226
|
}
|
|
190
|
-
const rows = await queryWithNodeSqlite(dbPath, days).catch(async () =>
|
|
191
|
-
|
|
192
|
-
|
|
227
|
+
const rows = await queryWithNodeSqlite(dbPath, days).catch(async (err: unknown) => {
|
|
228
|
+
if (err instanceof Error && err.message.startsWith('incompatible opencode.db schema'))
|
|
229
|
+
throw err
|
|
230
|
+
return queryWithScript(scriptPath, dbPath, days)
|
|
231
|
+
})
|
|
193
232
|
return renderMarkdown(rows, days)
|
|
194
233
|
} catch (err: unknown) {
|
|
195
234
|
const reason = err instanceof Error ? err.message : String(err)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pantheon-tui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.5-beta.52.c75b04d",
|
|
5
5
|
"description": "Pantheon TUI plugin — sidebar (version, preset, sessions, real-time delegations) + AI subscription usage gauges incl. OpenCode Go/Zen dollar limits (vendored, MIT)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|