pi-cost 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +13 -1
- package/extensions/cost.ts +53 -83
- package/lib/app.js +113 -0
- package/lib/args.js +17 -0
- package/lib/cli.js +593 -0
- package/lib/cost-dir.js +62 -0
- package/lib/costs.js +799 -0
- package/lib/net-guard.js +178 -0
- package/lib/pi-parsers-cost.js +234 -66
- package/lib/pi-pricing.js +35 -8
- package/lib/themes.js +90 -0
- package/package.json +16 -7
- package/public/app.js +2232 -448
- package/public/fonts/OFL-IBM-Plex.txt +93 -0
- package/public/fonts/OFL-Playfair-Display.txt +93 -0
- package/public/fonts/fonts.css +244 -0
- package/public/fonts/ibm-plex-mono-cyrillic-400.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-cyrillic-500.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-cyrillic-600.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-cyrillic-ext-400.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-cyrillic-ext-500.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-cyrillic-ext-600.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-latin-400.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-latin-500.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-latin-600.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-latin-ext-400.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-latin-ext-500.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-latin-ext-600.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-vietnamese-400.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-vietnamese-500.woff2 +0 -0
- package/public/fonts/ibm-plex-mono-vietnamese-600.woff2 +0 -0
- package/public/fonts/playfair-display-cyrillic-400.woff2 +0 -0
- package/public/fonts/playfair-display-latin-400.woff2 +0 -0
- package/public/fonts/playfair-display-latin-ext-400.woff2 +0 -0
- package/public/fonts/playfair-display-vietnamese-400.woff2 +0 -0
- package/public/index.html +68 -46
- package/public/style.css +319 -8
- package/public/sw.js +2 -2
- package/server.js +58 -659
- package/themes/ayu-dark.json +21 -0
- package/themes/ayu-light.json +21 -0
- package/themes/catppuccin-dark.json +21 -0
- package/themes/catppuccin-light.json +21 -0
- package/themes/dracula-dark.json +21 -0
- package/themes/dracula-light.json +21 -0
- package/themes/everforest-dark.json +21 -0
- package/themes/everforest-light.json +21 -0
- package/themes/github-dark.json +21 -0
- package/themes/github-light.json +21 -0
- package/themes/gruvbox-dark.json +21 -0
- package/themes/gruvbox-light.json +21 -0
- package/themes/kanagawa-dark.json +21 -0
- package/themes/kanagawa-light.json +21 -0
- package/themes/monokai-dark.json +21 -0
- package/themes/monokai-light.json +21 -0
- package/themes/night-owl-dark.json +21 -0
- package/themes/night-owl-light.json +21 -0
- package/themes/nord-dark.json +21 -0
- package/themes/nord-light.json +21 -0
- package/themes/one-dark-dark.json +21 -0
- package/themes/one-dark-light.json +21 -0
- package/themes/rose-pine-dark.json +21 -0
- package/themes/rose-pine-light.json +21 -0
- package/themes/solarized-dark.json +21 -0
- package/themes/solarized-light.json +21 -0
- package/themes/synthwave-dark.json +21 -0
- package/themes/synthwave-light.json +21 -0
- package/themes/tokyo-night-dark.json +21 -0
- package/themes/tokyo-night-light.json +21 -0
- package/themes/vitesse-dark.json +21 -0
- package/themes/vitesse-light.json +21 -0
package/lib/themes.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fsp = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { readCostSettings } = require('./cost-dir');
|
|
6
|
+
|
|
7
|
+
const BUILTIN_THEME_DIR = path.join(__dirname, '..', 'themes');
|
|
8
|
+
|
|
9
|
+
// The same 14 keys the pi-kanban embed handshake sends in `pi-companion:theme`.
|
|
10
|
+
const REQUIRED_COLOR_KEYS = [
|
|
11
|
+
'bgDeep', 'bgSurface', 'bgElevated', 'bgHover',
|
|
12
|
+
'border',
|
|
13
|
+
'textPrimary', 'textSecondary', 'textTertiary', 'textMuted',
|
|
14
|
+
'accent', 'accentText',
|
|
15
|
+
'success', 'warning', 'error',
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// Precedence: env > <costDir>/settings.json > defaults.
|
|
19
|
+
function themeConfig(costDir, env = process.env) {
|
|
20
|
+
const fromSettings = readCostSettings(costDir).themes || {};
|
|
21
|
+
return {
|
|
22
|
+
dir: env.COST_THEME_DIR || fromSettings.dir || path.join(costDir, 'themes'),
|
|
23
|
+
lightId: env.COST_LIGHT_THEME || fromSettings.light || 'pi-light',
|
|
24
|
+
darkId: env.COST_DARK_THEME || fromSettings.dark || 'pi-dark',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function loadThemesFromDir(dir, builtin, into) {
|
|
29
|
+
let entries;
|
|
30
|
+
try { entries = await fsp.readdir(dir); }
|
|
31
|
+
catch (e) {
|
|
32
|
+
if (e.code !== 'ENOENT') console.warn(`themes: cannot read ${dir}: ${e.message}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await Promise.all(entries.filter((f) => f.toLowerCase().endsWith('.json')).map(async (f) => {
|
|
36
|
+
const full = path.join(dir, f);
|
|
37
|
+
const id = f.replace(/\.json$/i, '');
|
|
38
|
+
try {
|
|
39
|
+
const obj = JSON.parse(await fsp.readFile(full, 'utf8'));
|
|
40
|
+
if (obj.mode !== 'light' && obj.mode !== 'dark') {
|
|
41
|
+
console.warn(`themes: skip ${full}: invalid mode`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const colors = obj.colors || {};
|
|
45
|
+
const missing = REQUIRED_COLOR_KEYS.filter((k) => typeof colors[k] !== 'string' || !colors[k].trim());
|
|
46
|
+
if (missing.length) {
|
|
47
|
+
console.warn(`themes: skip ${full}: missing colors ${missing.join(',')}`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const str = (v) => (typeof v === 'string' && v.trim() ? v : null);
|
|
51
|
+
const name = str(obj.name) || id;
|
|
52
|
+
into.set(id, { id, name, displayName: str(obj.displayName) || name, mode: obj.mode, colors, builtin });
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.warn(`themes: skip ${full}: ${e.message}`);
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createThemeStore(costDir) {
|
|
60
|
+
const config = themeConfig(costDir);
|
|
61
|
+
const themes = new Map();
|
|
62
|
+
const ready = (async () => {
|
|
63
|
+
await loadThemesFromDir(BUILTIN_THEME_DIR, true, themes);
|
|
64
|
+
await loadThemesFromDir(config.dir, false, themes);
|
|
65
|
+
})();
|
|
66
|
+
|
|
67
|
+
function active(mode) {
|
|
68
|
+
const id = mode === 'light' ? config.lightId : config.darkId;
|
|
69
|
+
return themes.get(id) || themes.get(mode === 'light' ? 'pi-light' : 'pi-dark') || null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// A family is a `<id>-light` / `<id>-dark` pair; the theme menu offers families, not single themes.
|
|
73
|
+
function families() {
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const light of themes.values()) {
|
|
76
|
+
if (light.mode !== 'light' || !light.id.endsWith('-light')) continue;
|
|
77
|
+
const id = light.id.slice(0, -'-light'.length);
|
|
78
|
+
if (!id) continue;
|
|
79
|
+
const dark = themes.get(`${id}-dark`);
|
|
80
|
+
if (dark?.mode !== 'dark') continue;
|
|
81
|
+
out.push({ id, displayName: light.displayName.replace(/[\s_-]+light$/i, ''), light, dark });
|
|
82
|
+
}
|
|
83
|
+
const rank = (f) => (f.id === 'pi' ? 0 : f.id === 'classic' ? 1 : 2);
|
|
84
|
+
return out.sort((a, b) => rank(a) - rank(b) || a.displayName.localeCompare(b.displayName));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { config, themes, ready, active, families };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = { REQUIRED_COLOR_KEYS, createThemeStore };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cost",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Cost dashboard for the pi coding agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
"url": "https://github.com/NikiforovAll/pi-cost/issues"
|
|
21
21
|
},
|
|
22
22
|
"homepage": "https://nikiforovall.github.io/pi-cost/",
|
|
23
|
-
"bin": {
|
|
23
|
+
"bin": {
|
|
24
|
+
"pi-cost": "./server.js"
|
|
25
|
+
},
|
|
24
26
|
"main": "server.js",
|
|
25
27
|
"files": [
|
|
26
28
|
"server.js",
|
|
@@ -28,18 +30,21 @@
|
|
|
28
30
|
"public/",
|
|
29
31
|
"extensions/",
|
|
30
32
|
"themes/",
|
|
31
|
-
"README.md"
|
|
33
|
+
"README.md",
|
|
34
|
+
"CHANGELOG.md"
|
|
32
35
|
],
|
|
33
36
|
"scripts": {
|
|
34
37
|
"start": "node server.js",
|
|
35
38
|
"dev": "node server.js --open",
|
|
39
|
+
"test": "node --test",
|
|
36
40
|
"docs:dev": "vitepress dev docs",
|
|
37
41
|
"docs:build": "vitepress build docs",
|
|
38
42
|
"docs:preview": "vitepress preview docs"
|
|
39
43
|
},
|
|
40
44
|
"dependencies": {
|
|
41
|
-
"
|
|
42
|
-
"
|
|
45
|
+
"chart.js": "^4.6.1",
|
|
46
|
+
"express": "^5.3.0",
|
|
47
|
+
"open": "^11.0.4"
|
|
43
48
|
},
|
|
44
49
|
"devDependencies": {
|
|
45
50
|
"vitepress": "^1.6.4"
|
|
@@ -48,10 +53,14 @@
|
|
|
48
53
|
"@earendil-works/pi-coding-agent": "*"
|
|
49
54
|
},
|
|
50
55
|
"peerDependenciesMeta": {
|
|
51
|
-
"@earendil-works/pi-coding-agent": {
|
|
56
|
+
"@earendil-works/pi-coding-agent": {
|
|
57
|
+
"optional": true
|
|
58
|
+
}
|
|
52
59
|
},
|
|
53
60
|
"pi": {
|
|
54
|
-
"extensions": [
|
|
61
|
+
"extensions": [
|
|
62
|
+
"./extensions"
|
|
63
|
+
],
|
|
55
64
|
"image": "https://raw.githubusercontent.com/NikiforovAll/pi-cost/main/assets/overview.png",
|
|
56
65
|
"video": "https://raw.githubusercontent.com/NikiforovAll/pi-cost/main/assets/demo.mp4"
|
|
57
66
|
},
|