@dmrlzz/davecode 0.3.0 → 0.5.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/README.md +5 -4
- package/bin/davecode.js +0 -0
- package/dist/{ChatApp-FO5SABXB.js → ChatApp-CJBICQGA.js} +289 -137
- package/dist/ChatApp-CJBICQGA.js.map +1 -0
- package/dist/{RunnerView-DLVR7C22.js → RunnerView-A7JSPUW5.js} +5 -5
- package/dist/account-QWDPJWAA.js +23 -0
- package/dist/accounts-IQMR72FC.js +37 -0
- package/dist/{budget-RBHCEDPP.js → budget-XANOUVWO.js} +6 -6
- package/dist/chat-KDDRNC5V.js +359 -0
- package/dist/chat-KDDRNC5V.js.map +1 -0
- package/dist/{accounts-MTMGYKEP.js → chunk-44AFJL2X.js} +24 -17
- package/dist/chunk-44AFJL2X.js.map +1 -0
- package/dist/{chunk-BOLFK3BH.js → chunk-5LKOJBUL.js} +2 -2
- package/dist/{chunk-23KTE7IK.js → chunk-AZKACMME.js} +2 -2
- package/dist/{chunk-PIIJ3RSI.js → chunk-FYPMA55O.js} +1269 -1265
- package/dist/chunk-FYPMA55O.js.map +1 -0
- package/dist/{chunk-OYEYNDOO.js → chunk-GE22VBCY.js} +3 -3
- package/dist/{chunk-C7FDRA3U.js → chunk-HSBJDV3O.js} +660 -139
- package/dist/chunk-HSBJDV3O.js.map +1 -0
- package/dist/chunk-JLATEHS7.js +20 -0
- package/dist/chunk-JLATEHS7.js.map +1 -0
- package/dist/{chunk-VVCXGQMY.js → chunk-MC7RIPL7.js} +17 -8
- package/dist/chunk-MC7RIPL7.js.map +1 -0
- package/dist/{chunk-MSLLH5XP.js → chunk-PGAJIGUF.js} +4 -4
- package/dist/{chunk-OXRMGR72.js → chunk-PGPXE6LF.js} +2 -2
- package/dist/chunk-QA3O6RA2.js +81 -0
- package/dist/chunk-QA3O6RA2.js.map +1 -0
- package/dist/chunk-W7GPBEBZ.js +144 -0
- package/dist/chunk-W7GPBEBZ.js.map +1 -0
- package/dist/{chunk-IVZHKUCD.js → chunk-WQ7MEWDF.js} +3 -3
- package/dist/{config-FRMY5OHG.js → config-YTUPIYZT.js} +5 -5
- package/dist/dashboard/assets/Accounts-cq8RLMoI.js +1 -0
- package/dist/dashboard/assets/Badge-Z4RBdO9j.js +55 -0
- package/dist/dashboard/assets/Callout-w8BTYh4k.js +1 -0
- package/dist/dashboard/assets/Field-Cmpgg_1z.js +1 -0
- package/dist/dashboard/assets/Goals-Bw02to72.js +1 -0
- package/dist/dashboard/assets/Library-DK7mlpRb.js +1 -0
- package/dist/dashboard/assets/{Markdown-ClRsddnB.js → Markdown-DCZ4Awcc.js} +8 -8
- package/dist/dashboard/assets/Panes-_KTf8pbZ.js +8 -0
- package/dist/dashboard/assets/Panes-kHJ-D0s7.css +1 -0
- package/dist/dashboard/assets/RoutesPage-QV0oPRW9.js +4 -0
- package/dist/dashboard/assets/Runner-CkHBLQl0.js +1 -0
- package/dist/dashboard/assets/Settings-BLz2gTBR.js +1 -0
- package/dist/dashboard/assets/Switch-DPYlLseH.js +1 -0
- package/dist/dashboard/assets/Tasks-BiUQSTpt.js +2 -0
- package/dist/dashboard/assets/Traffic-BVfBuC70.js +1 -0
- package/dist/dashboard/assets/Workspace-CJTgiiU5.js +1 -0
- package/dist/dashboard/assets/arrow-up-DjhzZprp.js +1 -0
- package/dist/dashboard/assets/cn-BwE4cei0.js +1 -0
- package/dist/dashboard/assets/forms-DVIUAxAc.js +1 -0
- package/dist/dashboard/assets/index-BDBa5cb0.js +104 -0
- package/dist/dashboard/assets/index-BEmBgsbP.css +2 -0
- package/dist/dashboard/assets/task-errors-Daqwi0ue.js +1 -0
- package/dist/dashboard/assets/trash-BTuDcSaQ.js +1 -0
- package/dist/dashboard/favicon.svg +1 -7
- package/dist/dashboard/index.html +4 -4
- package/dist/{dist-ZPXEVBEC.js → dist-37VQSHHA.js} +24 -2
- package/dist/dist-37VQSHHA.js.map +1 -0
- package/dist/{doctor-7RUD4D3A.js → doctor-PYUQRRCB.js} +5 -5
- package/dist/{goal-JFRNPBSD.js → goal-SXZUDUCL.js} +5 -5
- package/dist/index.js +48 -30
- package/dist/index.js.map +1 -1
- package/dist/{init-NFYNO3A3.js → init-Z2AFQEEP.js} +6 -6
- package/dist/{prompts-L37J72YW.js → prompts-TZ7UC42M.js} +9 -5
- package/dist/prompts-TZ7UC42M.js.map +1 -0
- package/dist/{run-ARUPXFFB.js → run-AOS7L3RT.js} +10 -10
- package/dist/runtime.js +18 -0
- package/dist/runtime.js.map +1 -0
- package/dist/{start-CFPOVIAR.js → start-CEPXKZUE.js} +5 -5
- package/dist/{status-E4EACVE2.js → status-WNJ3GGKU.js} +6 -6
- package/dist/status-WNJ3GGKU.js.map +1 -0
- package/dist/{tasks-ZIK6BVEX.js → tasks-TFUTUMWB.js} +6 -6
- package/package.json +21 -15
- package/dist/ChatApp-FO5SABXB.js.map +0 -1
- package/dist/accounts-MTMGYKEP.js.map +0 -1
- package/dist/chat-ZDXX3N6X.js +0 -95
- package/dist/chat-ZDXX3N6X.js.map +0 -1
- package/dist/chunk-C7FDRA3U.js.map +0 -1
- package/dist/chunk-DYQ56FBT.js +0 -47
- package/dist/chunk-DYQ56FBT.js.map +0 -1
- package/dist/chunk-PIIJ3RSI.js.map +0 -1
- package/dist/chunk-VVCXGQMY.js.map +0 -1
- package/dist/dashboard/assets/Accounts-BR3ZfVk4.js +0 -1
- package/dist/dashboard/assets/Badge-B-qzNRrZ.js +0 -55
- package/dist/dashboard/assets/Callout-B9g_wPDi.js +0 -1
- package/dist/dashboard/assets/Field-BfabPEcY.js +0 -1
- package/dist/dashboard/assets/Goals-BsV-6m94.js +0 -1
- package/dist/dashboard/assets/Library-CEA9P1m-.js +0 -1
- package/dist/dashboard/assets/Panel-B9tSgX6l.js +0 -1
- package/dist/dashboard/assets/RoutesPage-Dh-BJ3ir.js +0 -4
- package/dist/dashboard/assets/Runner-C_e9l5l2.js +0 -1
- package/dist/dashboard/assets/Settings-C6Yc1Gjg.js +0 -1
- package/dist/dashboard/assets/Switch-CUpOJjUT.js +0 -1
- package/dist/dashboard/assets/Tasks-CTku0rR3.js +0 -2
- package/dist/dashboard/assets/Traffic-CE6wZSMr.js +0 -1
- package/dist/dashboard/assets/Workspace-CxFxvkA6.js +0 -1
- package/dist/dashboard/assets/arrow-up-DL3CuFOO.js +0 -1
- package/dist/dashboard/assets/forms-D6UIYgC5.js +0 -1
- package/dist/dashboard/assets/index-1iH716Xy.css +0 -2
- package/dist/dashboard/assets/index-BMWr9EKo.js +0 -104
- package/dist/dashboard/assets/task-errors-CE4oEr8H.js +0 -1
- package/dist/dashboard/assets/trash-CWqeZ7eg.js +0 -1
- package/dist/prompts-L37J72YW.js.map +0 -1
- /package/dist/{RunnerView-DLVR7C22.js.map → RunnerView-A7JSPUW5.js.map} +0 -0
- /package/dist/{dist-ZPXEVBEC.js.map → account-QWDPJWAA.js.map} +0 -0
- /package/dist/{status-E4EACVE2.js.map → accounts-IQMR72FC.js.map} +0 -0
- /package/dist/{budget-RBHCEDPP.js.map → budget-XANOUVWO.js.map} +0 -0
- /package/dist/{chunk-BOLFK3BH.js.map → chunk-5LKOJBUL.js.map} +0 -0
- /package/dist/{chunk-23KTE7IK.js.map → chunk-AZKACMME.js.map} +0 -0
- /package/dist/{chunk-OYEYNDOO.js.map → chunk-GE22VBCY.js.map} +0 -0
- /package/dist/{chunk-MSLLH5XP.js.map → chunk-PGAJIGUF.js.map} +0 -0
- /package/dist/{chunk-OXRMGR72.js.map → chunk-PGPXE6LF.js.map} +0 -0
- /package/dist/{chunk-IVZHKUCD.js.map → chunk-WQ7MEWDF.js.map} +0 -0
- /package/dist/{config-FRMY5OHG.js.map → config-YTUPIYZT.js.map} +0 -0
- /package/dist/{doctor-7RUD4D3A.js.map → doctor-PYUQRRCB.js.map} +0 -0
- /package/dist/{goal-JFRNPBSD.js.map → goal-SXZUDUCL.js.map} +0 -0
- /package/dist/{init-NFYNO3A3.js.map → init-Z2AFQEEP.js.map} +0 -0
- /package/dist/{run-ARUPXFFB.js.map → run-AOS7L3RT.js.map} +0 -0
- /package/dist/{start-CFPOVIAR.js.map → start-CEPXKZUE.js.map} +0 -0
- /package/dist/{tasks-ZIK6BVEX.js.map → tasks-TFUTUMWB.js.map} +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
brandLogo
|
|
3
|
+
} from "./chunk-HSBJDV3O.js";
|
|
4
|
+
|
|
5
|
+
// src/ui/banner.ts
|
|
6
|
+
function brandBanner(theme, text, columns = 80, minColumns = 56) {
|
|
7
|
+
if (columns < minColumns) return text;
|
|
8
|
+
const art = brandLogo(theme.unicode);
|
|
9
|
+
const width = Math.max(...art.map((line) => line.length)) + 4;
|
|
10
|
+
const start = Math.max(0, Math.floor((art.length - text.length) / 2));
|
|
11
|
+
return art.map((line, i) => {
|
|
12
|
+
const beside = text[i - start];
|
|
13
|
+
return beside ? `${theme.accent(line.padEnd(width))}${beside}` : theme.accent(line);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
brandBanner
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=chunk-JLATEHS7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/banner.ts"],"sourcesContent":["import { brandLogo } from '@davecode/core';\nimport type { Theme } from './theme';\n\n/**\n * The bird with a few lines of text beside it, vertically centred. Terminals narrower than\n * `minColumns` get the text alone.\n */\nexport function brandBanner(theme: Theme, text: string[], columns = 80, minColumns = 56): string[] {\n if (columns < minColumns) return text;\n const art = brandLogo(theme.unicode);\n const width = Math.max(...art.map((line) => line.length)) + 4;\n const start = Math.max(0, Math.floor((art.length - text.length) / 2));\n return art.map((line, i) => {\n const beside = text[i - start];\n return beside ? `${theme.accent(line.padEnd(width))}${beside}` : theme.accent(line);\n });\n}\n"],"mappings":";;;;;AAOO,SAAS,YAAY,OAAc,MAAgB,UAAU,IAAI,aAAa,IAAc;AACjG,MAAI,UAAU,WAAY,QAAO;AACjC,QAAM,MAAM,UAAU,MAAM,OAAO;AACnC,QAAM,QAAQ,KAAK,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,KAAK,MAAM,CAAC,IAAI;AAC5D,QAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,OAAO,IAAI,SAAS,KAAK,UAAU,CAAC,CAAC;AACpE,SAAO,IAAI,IAAI,CAAC,MAAM,MAAM;AAC1B,UAAM,SAAS,KAAK,IAAI,KAAK;AAC7B,WAAO,SAAS,GAAG,MAAM,OAAO,KAAK,OAAO,KAAK,CAAC,CAAC,GAAG,MAAM,KAAK,MAAM,OAAO,IAAI;AAAA,EACpF,CAAC;AACH;","names":[]}
|
|
@@ -56,14 +56,18 @@ var ASCII_GLYPHS = {
|
|
|
56
56
|
barEmpty: ".",
|
|
57
57
|
rule: "-"
|
|
58
58
|
};
|
|
59
|
-
var ACCENT_256 = 80;
|
|
60
|
-
var ACCENT_HEX = "#5fd7d7";
|
|
61
59
|
function sgr(open, close) {
|
|
62
60
|
return (text) => text === "" ? text : `\x1B[${open}m${text}\x1B[${close}m`;
|
|
63
61
|
}
|
|
64
62
|
var identity = (text) => text;
|
|
63
|
+
var THEME_MODES = ["dark", "light", "auto"];
|
|
64
|
+
var EMPHASIS = {
|
|
65
|
+
dark: { open: "1;97", ink: "whiteBright" },
|
|
66
|
+
light: { open: "1;30", ink: "black" },
|
|
67
|
+
auto: { open: "1", ink: void 0 }
|
|
68
|
+
};
|
|
65
69
|
function createTheme(options) {
|
|
66
|
-
const { color, unicode } = options;
|
|
70
|
+
const { color, unicode, mode = "auto" } = options;
|
|
67
71
|
const glyph = unicode ? UNICODE_GLYPHS : ASCII_GLYPHS;
|
|
68
72
|
if (!color) {
|
|
69
73
|
return {
|
|
@@ -83,25 +87,30 @@ function createTheme(options) {
|
|
|
83
87
|
color,
|
|
84
88
|
unicode,
|
|
85
89
|
glyph,
|
|
86
|
-
|
|
90
|
+
// Monochrome by design: emphasis is bold black or white, never a hue. Colour is reserved
|
|
91
|
+
// for state (ok, warn, error).
|
|
92
|
+
accent: sgr(EMPHASIS[mode].open, "22;39"),
|
|
87
93
|
ok: sgr("32", "39"),
|
|
88
94
|
warn: sgr("33", "39"),
|
|
89
95
|
error: sgr("31", "39"),
|
|
90
96
|
dim: sgr("2", "22"),
|
|
91
97
|
bold: sgr("1", "22"),
|
|
92
|
-
ink: { accent:
|
|
98
|
+
ink: { accent: EMPHASIS[mode].ink, ok: "green", warn: "yellow", error: "red", dim: true }
|
|
93
99
|
};
|
|
94
100
|
}
|
|
95
|
-
function themeFor(env, stream, colorFlag, platform = process.platform) {
|
|
101
|
+
function themeFor(env, stream, colorFlag, platform = process.platform, mode = "auto") {
|
|
96
102
|
return createTheme({
|
|
97
103
|
color: detectColor(env, stream, colorFlag),
|
|
98
|
-
unicode: detectUnicode(env, platform)
|
|
104
|
+
unicode: detectUnicode(env, platform),
|
|
105
|
+
mode
|
|
99
106
|
});
|
|
100
107
|
}
|
|
101
108
|
var PLAIN_THEME = createTheme({ color: false, unicode: true });
|
|
102
109
|
|
|
103
110
|
export {
|
|
111
|
+
THEME_MODES,
|
|
112
|
+
createTheme,
|
|
104
113
|
themeFor,
|
|
105
114
|
PLAIN_THEME
|
|
106
115
|
};
|
|
107
|
-
//# sourceMappingURL=chunk-
|
|
116
|
+
//# sourceMappingURL=chunk-MC7RIPL7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/theme.ts"],"sourcesContent":["/**\n * Terminal styling: one accent colour plus semantic colours, with plain-text fallbacks for\n * `NO_COLOR`, `--no-color`, non-TTY output and terminals without Unicode glyphs.\n */\n\nexport interface ColorStream {\n isTTY?: boolean;\n}\n\n/**\n * Colour is on for TTYs unless `NO_COLOR` is set (any value) or `--no-color` was passed.\n * `FORCE_COLOR` (anything but `0`/`false`) turns it on even when piped.\n */\nexport function detectColor(\n env: NodeJS.ProcessEnv,\n stream: ColorStream,\n flag: boolean | undefined = undefined,\n): boolean {\n if (flag === false) return false;\n if (env.NO_COLOR !== undefined && env.NO_COLOR !== '') return false;\n const force = env.FORCE_COLOR;\n if (force !== undefined) return !['0', 'false'].includes(force.trim().toLowerCase());\n if (env.TERM === 'dumb') return false;\n return stream.isTTY === true;\n}\n\n/**\n * Heuristic from `is-unicode-supported`: every non-Windows terminal except the Linux console,\n * and the Windows terminals known to render box-drawing and braille glyphs.\n */\nexport function detectUnicode(\n env: NodeJS.ProcessEnv,\n platform: NodeJS.Platform = process.platform,\n): boolean {\n if (env.DAVECODE_ASCII === '1') return false;\n if (platform !== 'win32') return env.TERM !== 'linux';\n return (\n Boolean(env.WT_SESSION) ||\n Boolean(env.TERMINUS_SUBLIME) ||\n env.ConEmuTask === '{cmd::Cmder}' ||\n env.TERM_PROGRAM === 'vscode' ||\n env.TERM_PROGRAM === 'Terminus-Sublime' ||\n env.TERM === 'xterm-256color' ||\n env.TERM === 'alacritty' ||\n env.TERMINAL_EMULATOR === 'JetBrains-JediTerm' ||\n Boolean(env.CI)\n );\n}\n\nexport interface Glyphs {\n ok: string;\n fail: string;\n warn: string;\n info: string;\n bullet: string;\n arrow: string;\n dot: string;\n ellipsis: string;\n prompt: string;\n /** Task statuses. */\n pending: string;\n running: string;\n success: string;\n failed: string;\n blocked: string;\n /** Tree connectors (`tee` = has siblings below, `elbow` = last child). */\n tee: string;\n elbow: string;\n pipe: string;\n /** Quota bars. */\n barFull: string;\n barEmpty: string;\n /** Horizontal rule. */\n rule: string;\n}\n\nconst UNICODE_GLYPHS: Glyphs = {\n ok: '✓',\n fail: '✗',\n warn: '!',\n info: 'i',\n bullet: '•',\n arrow: '→',\n dot: '●',\n ellipsis: '…',\n prompt: '›',\n pending: '○',\n running: '◐',\n success: '●',\n failed: '✗',\n blocked: '◌',\n tee: '├─',\n elbow: '└─',\n pipe: '│ ',\n barFull: '█',\n barEmpty: '░',\n rule: '─',\n};\n\nconst ASCII_GLYPHS: Glyphs = {\n ok: 'ok',\n fail: 'x',\n warn: '!',\n info: 'i',\n bullet: '*',\n arrow: '->',\n dot: '*',\n ellipsis: '...',\n prompt: '>',\n pending: 'o',\n running: '~',\n success: '*',\n failed: 'x',\n blocked: '.',\n tee: '|-',\n elbow: '`-',\n pipe: '| ',\n barFull: '#',\n barEmpty: '.',\n rule: '-',\n};\n\ntype Style = (text: string) => string;\n\nexport interface Theme {\n color: boolean;\n unicode: boolean;\n glyph: Glyphs;\n /** Emphasis for headings, ids and the active element (bold, not a hue). */\n accent: Style;\n ok: Style;\n warn: Style;\n error: Style;\n /** Secondary information. */\n dim: Style;\n bold: Style;\n /** Ink `<Text color>` values (undefined when colour is off). */\n ink: {\n accent: string | undefined;\n ok: string | undefined;\n warn: string | undefined;\n error: string | undefined;\n dim: boolean;\n };\n}\n\nfunction sgr(open: string, close: string): Style {\n return (text) => (text === '' ? text : `\\u001b[${open}m${text}\\u001b[${close}m`);\n}\n\nconst identity: Style = (text) => text;\n\n/**\n * `dark` and `light` pin emphasis to bright white or black; `auto` uses the terminal's own\n * foreground, which reads on any background.\n */\nexport type ThemeMode = 'dark' | 'light' | 'auto';\nexport const THEME_MODES: readonly ThemeMode[] = ['dark', 'light', 'auto'];\n\nconst EMPHASIS: Record<ThemeMode, { open: string; ink: string | undefined }> = {\n dark: { open: '1;97', ink: 'whiteBright' },\n light: { open: '1;30', ink: 'black' },\n auto: { open: '1', ink: undefined },\n};\n\nexport function createTheme(options: {\n color: boolean;\n unicode: boolean;\n mode?: ThemeMode;\n}): Theme {\n const { color, unicode, mode = 'auto' } = options;\n const glyph = unicode ? UNICODE_GLYPHS : ASCII_GLYPHS;\n if (!color) {\n return {\n color,\n unicode,\n glyph,\n accent: identity,\n ok: identity,\n warn: identity,\n error: identity,\n dim: identity,\n bold: identity,\n ink: { accent: undefined, ok: undefined, warn: undefined, error: undefined, dim: false },\n };\n }\n return {\n color,\n unicode,\n glyph,\n // Monochrome by design: emphasis is bold black or white, never a hue. Colour is reserved\n // for state (ok, warn, error).\n accent: sgr(EMPHASIS[mode].open, '22;39'),\n ok: sgr('32', '39'),\n warn: sgr('33', '39'),\n error: sgr('31', '39'),\n dim: sgr('2', '22'),\n bold: sgr('1', '22'),\n ink: { accent: EMPHASIS[mode].ink, ok: 'green', warn: 'yellow', error: 'red', dim: true },\n };\n}\n\n/** Theme for a stream given the environment and the `--color/--no-color` flag. */\nexport function themeFor(\n env: NodeJS.ProcessEnv,\n stream: ColorStream,\n colorFlag?: boolean,\n platform: NodeJS.Platform = process.platform,\n mode: ThemeMode = 'auto',\n): Theme {\n return createTheme({\n color: detectColor(env, stream, colorFlag),\n unicode: detectUnicode(env, platform),\n mode,\n });\n}\n\n/** A theme with no styling, for tests and `--json`. */\nexport const PLAIN_THEME: Theme = createTheme({ color: false, unicode: true });\n"],"mappings":"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","names":[]}
|
|
@@ -9,12 +9,12 @@ import {
|
|
|
9
9
|
} from "./chunk-SF5ZEAP4.js";
|
|
10
10
|
import {
|
|
11
11
|
printJson
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-QA3O6RA2.js";
|
|
13
13
|
import {
|
|
14
14
|
detectProjectRoot,
|
|
15
15
|
findGateway,
|
|
16
16
|
targetFor
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-FYPMA55O.js";
|
|
18
18
|
import {
|
|
19
19
|
EXIT
|
|
20
20
|
} from "./chunk-7XVXEO7U.js";
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
QUOTA_WINDOWS,
|
|
23
23
|
createEngine,
|
|
24
24
|
loadConfig
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-HSBJDV3O.js";
|
|
26
26
|
|
|
27
27
|
// src/commands/status.ts
|
|
28
28
|
function emptyUsage(accountId) {
|
|
@@ -210,4 +210,4 @@ export {
|
|
|
210
210
|
renderStatus,
|
|
211
211
|
statusCommand
|
|
212
212
|
};
|
|
213
|
-
//# sourceMappingURL=chunk-
|
|
213
|
+
//# sourceMappingURL=chunk-PGAJIGUF.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PLAIN_THEME
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MC7RIPL7.js";
|
|
4
4
|
|
|
5
5
|
// src/tui/theme-context.tsx
|
|
6
6
|
import { createContext, useContext } from "react";
|
|
@@ -17,4 +17,4 @@ export {
|
|
|
17
17
|
ThemeProvider,
|
|
18
18
|
useTheme
|
|
19
19
|
};
|
|
20
|
-
//# sourceMappingURL=chunk-
|
|
20
|
+
//# sourceMappingURL=chunk-PGPXE6LF.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
THEME_MODES,
|
|
3
|
+
themeFor
|
|
4
|
+
} from "./chunk-MC7RIPL7.js";
|
|
5
|
+
|
|
6
|
+
// src/context.ts
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join as join2, resolve } from "path";
|
|
9
|
+
|
|
10
|
+
// src/lib/preferences.ts
|
|
11
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
12
|
+
import { dirname, join } from "path";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
var schema = z.object({
|
|
15
|
+
theme: z.enum(THEME_MODES).optional(),
|
|
16
|
+
onboarded: z.boolean().optional()
|
|
17
|
+
});
|
|
18
|
+
function preferencesPath(home) {
|
|
19
|
+
return join(home, "preferences.json");
|
|
20
|
+
}
|
|
21
|
+
function readPreferences(home) {
|
|
22
|
+
try {
|
|
23
|
+
const parsed = schema.safeParse(JSON.parse(readFileSync(preferencesPath(home), "utf8")));
|
|
24
|
+
return parsed.success ? parsed.data : {};
|
|
25
|
+
} catch {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function writePreferences(home, update) {
|
|
30
|
+
const next = { ...readPreferences(home), ...update };
|
|
31
|
+
const path = preferencesPath(home);
|
|
32
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
33
|
+
writeFileSync(path, `${JSON.stringify(next, null, 2)}
|
|
34
|
+
`);
|
|
35
|
+
return next;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/context.ts
|
|
39
|
+
function resolveHome(env, flag, cwd = process.cwd()) {
|
|
40
|
+
if (flag) return resolve(cwd, flag);
|
|
41
|
+
return env.DAVECODE_HOME ? resolve(cwd, env.DAVECODE_HOME) : join2(homedir(), ".davecode");
|
|
42
|
+
}
|
|
43
|
+
function createContext(options, io, base) {
|
|
44
|
+
const home = resolveHome(base.env, options.home, base.cwd);
|
|
45
|
+
const env = { ...base.env, DAVECODE_HOME: home };
|
|
46
|
+
const json = options.json === true;
|
|
47
|
+
const mode = readPreferences(home).theme;
|
|
48
|
+
const theme = themeFor(env, io.stdout, json ? false : options.color, process.platform, mode);
|
|
49
|
+
const errTheme = themeFor(env, io.stderr, options.color, process.platform, mode);
|
|
50
|
+
return {
|
|
51
|
+
env,
|
|
52
|
+
home,
|
|
53
|
+
cwd: base.cwd,
|
|
54
|
+
json,
|
|
55
|
+
theme,
|
|
56
|
+
errTheme,
|
|
57
|
+
io,
|
|
58
|
+
interactive: io.stdin.isTTY === true && io.stdout.isTTY === true && !json,
|
|
59
|
+
columns: io.stdout.columns && io.stdout.columns > 0 ? io.stdout.columns : 80,
|
|
60
|
+
out: (line = "") => {
|
|
61
|
+
io.stdout.write(`${line}
|
|
62
|
+
`);
|
|
63
|
+
},
|
|
64
|
+
err: (line = "") => {
|
|
65
|
+
io.stderr.write(`${line}
|
|
66
|
+
`);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function printJson(ctx, value) {
|
|
71
|
+
ctx.io.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
72
|
+
`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
readPreferences,
|
|
77
|
+
writePreferences,
|
|
78
|
+
createContext,
|
|
79
|
+
printJson
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=chunk-QA3O6RA2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/lib/preferences.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport { readPreferences } from './lib/preferences';\nimport { type Theme, themeFor } from './ui/theme';\n\n/** Minimal writable surface the CLI prints to (process streams in production, buffers in tests). */\nexport interface OutStream {\n write(chunk: string): unknown;\n isTTY?: boolean;\n columns?: number;\n}\n\nexport interface InStream {\n isTTY?: boolean;\n}\n\nexport interface CliIO {\n stdout: OutStream;\n stderr: OutStream;\n stdin: InStream;\n}\n\n/** Options shared by every command (`--home`, `--json`, `--no-color`). */\nexport interface GlobalOptions {\n home?: string;\n json?: boolean;\n color?: boolean;\n}\n\n/** Everything a command needs; built once per invocation from the global flags. */\nexport interface CliContext {\n /** Effective environment (`DAVECODE_HOME` reflects `--home`). */\n env: NodeJS.ProcessEnv;\n /** Resolved DaveCode home. */\n home: string;\n cwd: string;\n json: boolean;\n theme: Theme;\n /** Theme for stderr (it can be a TTY while stdout is piped). */\n errTheme: Theme;\n io: CliIO;\n /** Both stdin and stdout are terminals and `--json` is off. */\n interactive: boolean;\n /** Terminal width (80 when unknown). */\n columns: number;\n /** Print a line to stdout. */\n out(line?: string): void;\n /** Print a line to stderr. */\n err(line?: string): void;\n}\n\nexport function resolveHome(env: NodeJS.ProcessEnv, flag?: string, cwd = process.cwd()): string {\n if (flag) return resolve(cwd, flag);\n return env.DAVECODE_HOME ? resolve(cwd, env.DAVECODE_HOME) : join(homedir(), '.davecode');\n}\n\nexport function createContext(\n options: GlobalOptions,\n io: CliIO,\n base: { env: NodeJS.ProcessEnv; cwd: string },\n): CliContext {\n const home = resolveHome(base.env, options.home, base.cwd);\n const env = { ...base.env, DAVECODE_HOME: home };\n const json = options.json === true;\n const mode = readPreferences(home).theme;\n const theme = themeFor(env, io.stdout, json ? false : options.color, process.platform, mode);\n const errTheme = themeFor(env, io.stderr, options.color, process.platform, mode);\n return {\n env,\n home,\n cwd: base.cwd,\n json,\n theme,\n errTheme,\n io,\n interactive: io.stdin.isTTY === true && io.stdout.isTTY === true && !json,\n columns: io.stdout.columns && io.stdout.columns > 0 ? io.stdout.columns : 80,\n out: (line = '') => {\n io.stdout.write(`${line}\\n`);\n },\n err: (line = '') => {\n io.stderr.write(`${line}\\n`);\n },\n };\n}\n\n/** Print a value as pretty JSON on stdout (the `--json` contract). */\nexport function printJson(ctx: CliContext, value: unknown): void {\n ctx.io.stdout.write(`${JSON.stringify(value, null, 2)}\\n`);\n}\n","import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { z } from 'zod';\nimport { THEME_MODES, type ThemeMode } from '../ui/theme';\n\n/** Per-user terminal preferences chosen during first-run setup. */\nexport interface Preferences {\n theme?: ThemeMode;\n /** First-run setup finished (it is not shown again). */\n onboarded?: boolean;\n}\n\nconst schema = z.object({\n theme: z.enum(THEME_MODES as [ThemeMode, ...ThemeMode[]]).optional(),\n onboarded: z.boolean().optional(),\n});\n\nexport function preferencesPath(home: string): string {\n return join(home, 'preferences.json');\n}\n\n/** Missing or unreadable preferences behave like a first run. */\nexport function readPreferences(home: string): Preferences {\n try {\n const parsed = schema.safeParse(JSON.parse(readFileSync(preferencesPath(home), 'utf8')));\n return parsed.success ? parsed.data : {};\n } catch {\n return {};\n }\n}\n\nexport function writePreferences(home: string, update: Preferences): Preferences {\n const next = { ...readPreferences(home), ...update };\n const path = preferencesPath(home);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, `${JSON.stringify(next, null, 2)}\\n`);\n return next;\n}\n"],"mappings":";;;;;;AAAA,SAAS,eAAe;AACxB,SAAS,QAAAA,OAAM,eAAe;;;ACD9B,SAAS,WAAW,cAAc,qBAAqB;AACvD,SAAS,SAAS,YAAY;AAC9B,SAAS,SAAS;AAUlB,IAAM,SAAS,EAAE,OAAO;AAAA,EACtB,OAAO,EAAE,KAAK,WAA0C,EAAE,SAAS;AAAA,EACnE,WAAW,EAAE,QAAQ,EAAE,SAAS;AAClC,CAAC;AAEM,SAAS,gBAAgB,MAAsB;AACpD,SAAO,KAAK,MAAM,kBAAkB;AACtC;AAGO,SAAS,gBAAgB,MAA2B;AACzD,MAAI;AACF,UAAM,SAAS,OAAO,UAAU,KAAK,MAAM,aAAa,gBAAgB,IAAI,GAAG,MAAM,CAAC,CAAC;AACvF,WAAO,OAAO,UAAU,OAAO,OAAO,CAAC;AAAA,EACzC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,SAAS,iBAAiB,MAAc,QAAkC;AAC/E,QAAM,OAAO,EAAE,GAAG,gBAAgB,IAAI,GAAG,GAAG,OAAO;AACnD,QAAM,OAAO,gBAAgB,IAAI;AACjC,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,CAAI;AACxD,SAAO;AACT;;;ADcO,SAAS,YAAY,KAAwB,MAAe,MAAM,QAAQ,IAAI,GAAW;AAC9F,MAAI,KAAM,QAAO,QAAQ,KAAK,IAAI;AAClC,SAAO,IAAI,gBAAgB,QAAQ,KAAK,IAAI,aAAa,IAAIC,MAAK,QAAQ,GAAG,WAAW;AAC1F;AAEO,SAAS,cACd,SACA,IACA,MACY;AACZ,QAAM,OAAO,YAAY,KAAK,KAAK,QAAQ,MAAM,KAAK,GAAG;AACzD,QAAM,MAAM,EAAE,GAAG,KAAK,KAAK,eAAe,KAAK;AAC/C,QAAM,OAAO,QAAQ,SAAS;AAC9B,QAAM,OAAO,gBAAgB,IAAI,EAAE;AACnC,QAAM,QAAQ,SAAS,KAAK,GAAG,QAAQ,OAAO,QAAQ,QAAQ,OAAO,QAAQ,UAAU,IAAI;AAC3F,QAAM,WAAW,SAAS,KAAK,GAAG,QAAQ,QAAQ,OAAO,QAAQ,UAAU,IAAI;AAC/E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,KAAK,KAAK;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,GAAG,MAAM,UAAU,QAAQ,GAAG,OAAO,UAAU,QAAQ,CAAC;AAAA,IACrE,SAAS,GAAG,OAAO,WAAW,GAAG,OAAO,UAAU,IAAI,GAAG,OAAO,UAAU;AAAA,IAC1E,KAAK,CAAC,OAAO,OAAO;AAClB,SAAG,OAAO,MAAM,GAAG,IAAI;AAAA,CAAI;AAAA,IAC7B;AAAA,IACA,KAAK,CAAC,OAAO,OAAO;AAClB,SAAG,OAAO,MAAM,GAAG,IAAI;AAAA,CAAI;AAAA,IAC7B;AAAA,EACF;AACF;AAGO,SAAS,UAAU,KAAiB,OAAsB;AAC/D,MAAI,GAAG,OAAO,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,CAAI;AAC3D;","names":["join","join"]}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CliError,
|
|
3
|
+
EXIT
|
|
4
|
+
} from "./chunk-7XVXEO7U.js";
|
|
5
|
+
import {
|
|
6
|
+
AccountClient,
|
|
7
|
+
AccountStore,
|
|
8
|
+
accountService,
|
|
9
|
+
startAccountLogin
|
|
10
|
+
} from "./chunk-HSBJDV3O.js";
|
|
11
|
+
|
|
12
|
+
// src/lib/browser.ts
|
|
13
|
+
import { spawn } from "child_process";
|
|
14
|
+
var detached = (command, args) => new Promise((resolve) => {
|
|
15
|
+
const child = spawn(command, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
16
|
+
child.once("error", () => resolve(false));
|
|
17
|
+
child.once("spawn", () => {
|
|
18
|
+
child.unref();
|
|
19
|
+
resolve(true);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
async function openBrowser(url, platform = process.platform, launch = detached) {
|
|
23
|
+
const parsed = new URL(url);
|
|
24
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return false;
|
|
25
|
+
const target = parsed.toString();
|
|
26
|
+
if (platform === "win32") return launch("rundll32", ["url.dll,FileProtocolHandler", target]);
|
|
27
|
+
if (platform === "darwin") return launch("open", [target]);
|
|
28
|
+
return launch("xdg-open", [target]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/commands/account.ts
|
|
32
|
+
function serviceFor(ctx, deps) {
|
|
33
|
+
return deps.service ?? accountService(ctx.env);
|
|
34
|
+
}
|
|
35
|
+
function clientFor(service, deps) {
|
|
36
|
+
return new AccountClient(service, deps.fetch ? { fetch: deps.fetch } : {});
|
|
37
|
+
}
|
|
38
|
+
function describeUser(session) {
|
|
39
|
+
return session.user.email ?? session.user.name ?? session.user.id;
|
|
40
|
+
}
|
|
41
|
+
async function currentAccount(ctx, deps = {}) {
|
|
42
|
+
const service = serviceFor(ctx, deps);
|
|
43
|
+
if (!service) return void 0;
|
|
44
|
+
return new AccountStore(ctx.home, ctx.env).current(clientFor(service, deps));
|
|
45
|
+
}
|
|
46
|
+
function accountsEnabled(ctx, deps = {}) {
|
|
47
|
+
return serviceFor(ctx, deps) !== void 0;
|
|
48
|
+
}
|
|
49
|
+
async function signIn(ctx, deps = {}, announce) {
|
|
50
|
+
const service = serviceFor(ctx, deps);
|
|
51
|
+
if (!service) {
|
|
52
|
+
throw new CliError("DaveCode accounts are not available in this build");
|
|
53
|
+
}
|
|
54
|
+
const { theme } = ctx;
|
|
55
|
+
const login = await startAccountLogin({ client: clientFor(service, deps) });
|
|
56
|
+
const opened = await (deps.open ?? openBrowser)(login.url).catch(() => false);
|
|
57
|
+
if (announce) announce(login.url, opened);
|
|
58
|
+
else {
|
|
59
|
+
ctx.out(
|
|
60
|
+
opened ? `${theme.accent(theme.glyph.arrow)} Opened your browser to sign in.` : `${theme.accent(theme.glyph.arrow)} Open this link to sign in:`
|
|
61
|
+
);
|
|
62
|
+
ctx.out(theme.dim(` ${login.url}`));
|
|
63
|
+
ctx.out(theme.dim(" Waiting for you to finish in the browser. Ctrl+C cancels."));
|
|
64
|
+
}
|
|
65
|
+
const cancel = () => void login.close();
|
|
66
|
+
process.once("SIGINT", cancel);
|
|
67
|
+
try {
|
|
68
|
+
const session = await login.session;
|
|
69
|
+
new AccountStore(ctx.home, ctx.env).save(session);
|
|
70
|
+
return session;
|
|
71
|
+
} finally {
|
|
72
|
+
process.off("SIGINT", cancel);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function loginCommand(ctx, deps = {}) {
|
|
76
|
+
const session = await signIn(ctx, deps);
|
|
77
|
+
ctx.out(
|
|
78
|
+
`${ctx.theme.ok(ctx.theme.glyph.ok)} Signed in as ${ctx.theme.bold(describeUser(session))}`
|
|
79
|
+
);
|
|
80
|
+
return EXIT.ok;
|
|
81
|
+
}
|
|
82
|
+
async function logoutCommand(ctx, deps = {}) {
|
|
83
|
+
const store = new AccountStore(ctx.home, ctx.env);
|
|
84
|
+
const session = store.load();
|
|
85
|
+
const service = serviceFor(ctx, deps);
|
|
86
|
+
if (session && service) await clientFor(service, deps).signOut(session.accessToken);
|
|
87
|
+
store.clear();
|
|
88
|
+
ctx.out(session ? "Signed out." : "You were not signed in.");
|
|
89
|
+
return EXIT.ok;
|
|
90
|
+
}
|
|
91
|
+
async function accountCommand(ctx, deps = {}) {
|
|
92
|
+
const session = await currentAccount(ctx, deps);
|
|
93
|
+
if (ctx.json) {
|
|
94
|
+
ctx.out(JSON.stringify({ signedIn: Boolean(session), user: session?.user ?? null }));
|
|
95
|
+
return session ? EXIT.ok : EXIT.failure;
|
|
96
|
+
}
|
|
97
|
+
if (!session) {
|
|
98
|
+
ctx.out(`Not signed in. Run ${ctx.theme.accent("davecode login")}.`);
|
|
99
|
+
return EXIT.failure;
|
|
100
|
+
}
|
|
101
|
+
const via = session.user.provider ? ctx.theme.dim(` (${session.user.provider})`) : "";
|
|
102
|
+
ctx.out(`Signed in as ${ctx.theme.bold(describeUser(session))}${via}`);
|
|
103
|
+
return EXIT.ok;
|
|
104
|
+
}
|
|
105
|
+
function chatAccountActions(ctx, deps = {}) {
|
|
106
|
+
if (!accountsEnabled(ctx, deps)) return void 0;
|
|
107
|
+
return {
|
|
108
|
+
status: async () => {
|
|
109
|
+
const session = await currentAccount(ctx, deps);
|
|
110
|
+
return session ? `Signed in as ${describeUser(session)}` : "Not signed in. Type /login.";
|
|
111
|
+
},
|
|
112
|
+
login: async (notify) => {
|
|
113
|
+
const session = await signIn(
|
|
114
|
+
ctx,
|
|
115
|
+
deps,
|
|
116
|
+
(url, opened) => notify(
|
|
117
|
+
`${opened ? "Opened your browser to sign in." : "Open this link to sign in:"}
|
|
118
|
+
${url}`
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
return `Signed in as ${describeUser(session)}`;
|
|
122
|
+
},
|
|
123
|
+
logout: async () => {
|
|
124
|
+
const store = new AccountStore(ctx.home, ctx.env);
|
|
125
|
+
const session = store.load();
|
|
126
|
+
const service = serviceFor(ctx, deps);
|
|
127
|
+
if (session && service) await clientFor(service, deps).signOut(session.accessToken);
|
|
128
|
+
store.clear();
|
|
129
|
+
return session ? "Signed out. Type /login to sign in again." : "You were not signed in.";
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export {
|
|
135
|
+
describeUser,
|
|
136
|
+
currentAccount,
|
|
137
|
+
accountsEnabled,
|
|
138
|
+
signIn,
|
|
139
|
+
loginCommand,
|
|
140
|
+
logoutCommand,
|
|
141
|
+
accountCommand,
|
|
142
|
+
chatAccountActions
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=chunk-W7GPBEBZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/browser.ts","../src/commands/account.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\n\nexport type Launcher = (command: string, args: string[]) => Promise<boolean>;\n\nconst detached: Launcher = (command, args) =>\n new Promise((resolve) => {\n const child = spawn(command, args, { detached: true, stdio: 'ignore', windowsHide: true });\n child.once('error', () => resolve(false));\n child.once('spawn', () => {\n child.unref();\n resolve(true);\n });\n });\n\n/**\n * Open an http(s) URL in the default browser. Returns false when nothing could be launched, so\n * the caller can print the URL instead.\n */\nexport async function openBrowser(\n url: string,\n platform: NodeJS.Platform = process.platform,\n launch: Launcher = detached,\n): Promise<boolean> {\n const parsed = new URL(url);\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return false;\n const target = parsed.toString();\n // No shell is involved, so the URL is never interpreted as a command.\n if (platform === 'win32') return launch('rundll32', ['url.dll,FileProtocolHandler', target]);\n if (platform === 'darwin') return launch('open', [target]);\n return launch('xdg-open', [target]);\n}\n","import {\n AccountClient,\n type AccountServiceConfig,\n type AccountSession,\n AccountStore,\n accountService,\n startAccountLogin,\n} from '@davecode/core';\nimport type { CliContext } from '../context';\nimport { CliError, EXIT } from '../errors';\nimport { openBrowser } from '../lib/browser';\n\nexport interface AccountDeps {\n service?: AccountServiceConfig;\n fetch?: typeof fetch;\n open?: (url: string) => Promise<boolean>;\n}\n\nfunction serviceFor(ctx: CliContext, deps: AccountDeps): AccountServiceConfig | undefined {\n return deps.service ?? accountService(ctx.env);\n}\n\nfunction clientFor(service: AccountServiceConfig, deps: AccountDeps): AccountClient {\n return new AccountClient(service, deps.fetch ? { fetch: deps.fetch } : {});\n}\n\nexport function describeUser(session: AccountSession): string {\n return session.user.email ?? session.user.name ?? session.user.id;\n}\n\n/** The signed-in session (refreshed when needed), or undefined. */\nexport async function currentAccount(\n ctx: CliContext,\n deps: AccountDeps = {},\n): Promise<AccountSession | undefined> {\n const service = serviceFor(ctx, deps);\n if (!service) return undefined;\n return new AccountStore(ctx.home, ctx.env).current(clientFor(service, deps));\n}\n\n/** Whether this build signs in to DaveCode accounts at all (it needs a configured service). */\nexport function accountsEnabled(ctx: CliContext, deps: AccountDeps = {}): boolean {\n return serviceFor(ctx, deps) !== undefined;\n}\n\n/**\n * Open the browser sign-in page and wait for it to finish. Prints the URL too, for machines\n * where no browser can be launched (SSH, containers).\n */\nexport async function signIn(\n ctx: CliContext,\n deps: AccountDeps = {},\n announce?: (url: string, opened: boolean) => void,\n): Promise<AccountSession> {\n const service = serviceFor(ctx, deps);\n if (!service) {\n throw new CliError('DaveCode accounts are not available in this build');\n }\n const { theme } = ctx;\n const login = await startAccountLogin({ client: clientFor(service, deps) });\n const opened = await (deps.open ?? openBrowser)(login.url).catch(() => false);\n if (announce) announce(login.url, opened);\n else {\n ctx.out(\n opened\n ? `${theme.accent(theme.glyph.arrow)} Opened your browser to sign in.`\n : `${theme.accent(theme.glyph.arrow)} Open this link to sign in:`,\n );\n ctx.out(theme.dim(` ${login.url}`));\n ctx.out(theme.dim(' Waiting for you to finish in the browser. Ctrl+C cancels.'));\n }\n const cancel = () => void login.close();\n process.once('SIGINT', cancel);\n try {\n const session = await login.session;\n new AccountStore(ctx.home, ctx.env).save(session);\n return session;\n } finally {\n process.off('SIGINT', cancel);\n }\n}\n\nexport async function loginCommand(ctx: CliContext, deps: AccountDeps = {}): Promise<number> {\n const session = await signIn(ctx, deps);\n ctx.out(\n `${ctx.theme.ok(ctx.theme.glyph.ok)} Signed in as ${ctx.theme.bold(describeUser(session))}`,\n );\n return EXIT.ok;\n}\n\nexport async function logoutCommand(ctx: CliContext, deps: AccountDeps = {}): Promise<number> {\n const store = new AccountStore(ctx.home, ctx.env);\n const session = store.load();\n const service = serviceFor(ctx, deps);\n if (session && service) await clientFor(service, deps).signOut(session.accessToken);\n store.clear();\n ctx.out(session ? 'Signed out.' : 'You were not signed in.');\n return EXIT.ok;\n}\n\nexport async function accountCommand(ctx: CliContext, deps: AccountDeps = {}): Promise<number> {\n const session = await currentAccount(ctx, deps);\n if (ctx.json) {\n ctx.out(JSON.stringify({ signedIn: Boolean(session), user: session?.user ?? null }));\n return session ? EXIT.ok : EXIT.failure;\n }\n if (!session) {\n ctx.out(`Not signed in. Run ${ctx.theme.accent('davecode login')}.`);\n return EXIT.failure;\n }\n const via = session.user.provider ? ctx.theme.dim(` (${session.user.provider})`) : '';\n ctx.out(`Signed in as ${ctx.theme.bold(describeUser(session))}${via}`);\n return EXIT.ok;\n}\n\n/** Account actions for the chat's /login, /logout and /account (they return text, never print). */\nexport interface ChatAccountActions {\n status(): Promise<string>;\n login(notify: (text: string) => void): Promise<string>;\n logout(): Promise<string>;\n}\n\nexport function chatAccountActions(\n ctx: CliContext,\n deps: AccountDeps = {},\n): ChatAccountActions | undefined {\n if (!accountsEnabled(ctx, deps)) return undefined;\n return {\n status: async () => {\n const session = await currentAccount(ctx, deps);\n return session ? `Signed in as ${describeUser(session)}` : 'Not signed in. Type /login.';\n },\n login: async (notify) => {\n const session = await signIn(ctx, deps, (url, opened) =>\n notify(\n `${opened ? 'Opened your browser to sign in.' : 'Open this link to sign in:'}\\n${url}`,\n ),\n );\n return `Signed in as ${describeUser(session)}`;\n },\n logout: async () => {\n const store = new AccountStore(ctx.home, ctx.env);\n const session = store.load();\n const service = serviceFor(ctx, deps);\n if (session && service) await clientFor(service, deps).signOut(session.accessToken);\n store.clear();\n return session ? 'Signed out. Type /login to sign in again.' : 'You were not signed in.';\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
accountCreateSchema,
|
|
3
3
|
accountPatchSchema,
|
|
4
4
|
findGateway
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-FYPMA55O.js";
|
|
6
6
|
import {
|
|
7
7
|
CliError
|
|
8
8
|
} from "./chunk-7XVXEO7U.js";
|
|
9
9
|
import {
|
|
10
10
|
createEngine
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-HSBJDV3O.js";
|
|
12
12
|
|
|
13
13
|
// src/lib/accounts-backend.ts
|
|
14
14
|
import { z } from "zod";
|
|
@@ -177,4 +177,4 @@ export {
|
|
|
177
177
|
openAccounts,
|
|
178
178
|
resolveAccount
|
|
179
179
|
};
|
|
180
|
-
//# sourceMappingURL=chunk-
|
|
180
|
+
//# sourceMappingURL=chunk-WQ7MEWDF.js.map
|
|
@@ -3,11 +3,10 @@ import {
|
|
|
3
3
|
} from "./chunk-SF5ZEAP4.js";
|
|
4
4
|
import {
|
|
5
5
|
printJson
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-VVCXGQMY.js";
|
|
6
|
+
} from "./chunk-QA3O6RA2.js";
|
|
8
7
|
import {
|
|
9
8
|
detectProjectRoot
|
|
10
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-FYPMA55O.js";
|
|
11
10
|
import {
|
|
12
11
|
CliError,
|
|
13
12
|
EXIT
|
|
@@ -16,7 +15,8 @@ import {
|
|
|
16
15
|
globalPaths,
|
|
17
16
|
loadConfig,
|
|
18
17
|
projectPaths
|
|
19
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-HSBJDV3O.js";
|
|
19
|
+
import "./chunk-MC7RIPL7.js";
|
|
20
20
|
|
|
21
21
|
// src/commands/config.ts
|
|
22
22
|
import { existsSync } from "fs";
|
|
@@ -155,4 +155,4 @@ export {
|
|
|
155
155
|
configPath,
|
|
156
156
|
configShow
|
|
157
157
|
};
|
|
158
|
-
//# sourceMappingURL=config-
|
|
158
|
+
//# sourceMappingURL=config-YTUPIYZT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{i as e,n as t,o as n,r,t as i}from"./cn-BwE4cei0.js";import{E as a,F as o,R as s,_ as c,d as l,ft as u,h as d,ot as f,p,pt as m,st as h,t as g,u as _,v,x as y,y as b}from"./Badge-Z4RBdO9j.js";import{n as x,t as S}from"./Switch-DPYlLseH.js";import{a as C,c as w,i as ee,l as T,n as te,o as ne,r as re,s as ie,t as ae}from"./forms-DVIUAxAc.js";import{t as E}from"./Callout-w8BTYh4k.js";import{t as D}from"./trash-BTuDcSaQ.js";import{C as O,I as k,J as A,O as j,R as M,_ as N,a as P,d as F,f as I,g as L,i as R,j as z,k as B,m as V,n as H,o as U,p as W,r as G,s as K,t as q,u as J,w as Y,z as oe}from"./index-BDBa5cb0.js";import{n as X,t as Z}from"./Field-Cmpgg_1z.js";var se={name:`log-in`,size:24,node:[[`path`,{d:`m10 17 5-5-5-5`,key:`1bsop3`}],[`path`,{d:`M15 12H3`,key:`6jk70r`}],[`path`,{d:`M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4`,key:`u53s6r`}]]};se.node;var ce=t(se),le={name:`shield-alert`,size:24,node:[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M12 16h.01`,key:`1drbdi`}]]};le.node;var ue=t(le),Q=n(e(),1),$=r(),de=3e5,fe={connected:{tone:`ok`,label:`Connected`},not_connected:{tone:`warn`,label:`Not connected`},unknown:{tone:`neutral`,label:`Unknown`}};function pe({status:e}){let{tone:t,label:n}=fe[e];return(0,$.jsx)(g,{tone:t,dot:!0,children:n})}function me({account:e}){let[t,n]=(0,Q.useState)(null),r=v(e.id,t!==null),i=c(),a=r.data,o=a?.status===`connected`;return(0,Q.useEffect)(()=>{if(!t)return;if(o){n(null),O({tone:`ok`,title:`Subscription connected`,description:e.label});return}let r=window.setTimeout(()=>n(null),de);return()=>window.clearTimeout(r)},[t,o,e.label]),(0,$.jsxs)(`section`,{"aria-label":`Connection`,className:`flex flex-col gap-2.5 rounded-md border border-line bg-raised p-3`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(`p`,{className:`text-[12px] font-medium text-fg-2`,children:`Connection`}),a?(0,$.jsx)(pe,{status:a.status}):r.isError?(0,$.jsx)(g,{tone:`err`,children:`Check failed`}):(0,$.jsx)(g,{children:`Checking…`}),(0,$.jsx)(Y,{variant:`ghost`,size:`sm`,icon:M,iconOnly:!0,"aria-label":`Check again`,className:`ml-auto`,loading:r.isFetching&&!t,onClick:()=>r.refetch()})]}),a?.detail&&(0,$.jsx)(`p`,{className:`text-2xs leading-5 text-muted`,children:a.detail}),r.isError&&(0,$.jsx)(`p`,{className:`text-2xs leading-5 text-err`,children:u(r.error)}),a?.method===`cli-login`&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,$.jsx)(Y,{variant:o?`secondary`:`primary`,size:`sm`,icon:ce,loading:i.isPending,onClick:async()=>{try{n(await i.mutateAsync(e.id))}catch(e){O({tone:`err`,title:`Could not start the sign-in`,description:u(e)})}},children:o?`Sign in again`:`Connect subscription`}),(0,$.jsx)(`span`,{className:`text-2xs text-muted`,children:`Opens the vendor’s official sign-in. DaveCode never sees your password.`})]}),t&&(0,$.jsxs)(E,{tone:`info`,title:t.launched?`Waiting for the sign-in…`:`Run the sign-in on the gateway machine`,children:[(0,$.jsx)(`p`,{children:t.message}),!t.launched&&(0,$.jsx)(W,{command:t.command,className:`mt-2`})]}),!t&&!o&&a.loginCommand&&(0,$.jsx)(W,{command:a.loginCommand})]})]})}function he({account:e,usage:t,onClose:n}){return(0,$.jsx)(I,{open:!!e,onClose:n,title:e?.label??`Account`,description:e&&(0,$.jsx)(`span`,{className:`font-mono`,children:e.id}),width:`w-[min(560px,100vw)]`,children:e&&(0,$.jsx)(ge,{account:e,usage:t,onDone:n},e.id)})}function ge({account:e,usage:t,onDone:n}){let r=o(),i=y(),a=h(e.provider),s=Array.isArray(e.config.models)?e.config.models.join(`, `):``,[c,d]=(0,Q.useState)(e.label),[f,p]=(0,Q.useState)(String(e.priority)),[m,g]=(0,Q.useState)(String(e.weight)),[_,v]=(0,Q.useState)(C(e.limits)),[b,x]=(0,Q.useState)(T(e.subscription)),[S,k]=(0,Q.useState)({}),[A,j]=(0,Q.useState)(typeof e.config.baseUrl==`string`?e.config.baseUrl:``),[M,I]=(0,Q.useState)(s),[R,z]=(0,Q.useState)(``),[B,V]=(0,Q.useState)({}),[H,W]=(0,Q.useState)({}),[G,K]=(0,Q.useState)(null),[q,J]=(0,Q.useState)(!1);return(0,$.jsxs)(`form`,{onSubmit:async t=>{t.preventDefault(),K(null);let i={};c.trim()||(i.label=`Label cannot be empty.`);let a=Number(f);(!Number.isInteger(a)||a<0)&&(i.priority=`Whole number, 0 or more.`);let o=Number(m);(!Number.isFinite(o)||o<=0)&&(i.weight=`Number above 0.`),e.provider===`openai-compatible`&&!ee(A.trim())&&(i.baseUrl=`Enter an http(s) URL.`);let s=ne(_),l=w(b);if(V(i),W(s.errors),k(l.errors),Object.values(i).some(Boolean)||Object.keys(s.errors).length||Object.keys(l.errors).length)return;let d={};if(c.trim()!==e.label&&(d.label=c.trim()),a!==e.priority&&(d.priority=a),o!==e.weight&&(d.weight=o),JSON.stringify(s.limits)!==JSON.stringify(e.limits)&&(d.limits=s.limits),JSON.stringify(l.subscription??null)!==JSON.stringify(e.subscription??null)&&(d.subscription=l.subscription??null),e.provider===`openai-compatible`){let t=ie(M),n={...e.config,baseUrl:A.trim(),models:t.length?t:void 0};JSON.stringify(n)!==JSON.stringify(e.config)&&(d.config=n)}if(R.trim()&&(d.secret=R.trim()),Object.keys(d).length===0)n();else try{await r.mutateAsync({id:e.id,patch:d}),z(``),O({tone:`ok`,title:`Account updated`,description:e.label}),n()}catch(e){z(``),K(u(e))}},noValidate:!0,className:`flex flex-col gap-5`,children:[(0,$.jsxs)(`section`,{"aria-label":`Summary`,className:`flex flex-col gap-3 rounded-md border border-line bg-raised p-3`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[(0,$.jsx)(N,{provider:e.provider,size:`lg`}),(0,$.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,$.jsx)(`p`,{className:`text-[13px] font-medium text-fg`,children:a.label}),(0,$.jsxs)(`p`,{className:`font-mono text-2xs text-muted`,children:[e.provider,` · updated `,l(Date.parse(e.updatedAt))]})]}),(0,$.jsx)(L,{account:e})]}),e.lastError&&e.status!==`active`&&(0,$.jsx)(`p`,{className:`rounded border border-line bg-panel px-2 py-1.5 font-mono text-2xs text-fg-2`,children:e.lastError}),(0,$.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,$.jsx)(U,{label:`1m`,usage:t?.windows[`1m`]}),(0,$.jsx)(U,{label:`5h`,usage:t?.windows[`5h`]}),(0,$.jsx)(U,{label:`24h`,usage:t?.windows[`24h`]}),(0,$.jsx)(P,{className:`pl-9`})]})]}),(0,$.jsx)(me,{account:e}),(0,$.jsx)(Z,{label:`Label`,required:!0,error:B.label,children:({id:e,describedBy:t,invalid:n})=>(0,$.jsx)(X,{id:e,value:c,onChange:e=>d(e.target.value),"aria-describedby":t,"aria-invalid":n||void 0})}),(0,$.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,$.jsx)(Z,{label:`Priority`,error:B.priority,hint:`Lower is tried first.`,children:({id:e,describedBy:t,invalid:n})=>(0,$.jsx)(X,{id:e,inputMode:`numeric`,value:f,onChange:e=>p(e.target.value),"aria-describedby":t,"aria-invalid":n||void 0,className:`tnum font-mono`})}),(0,$.jsx)(Z,{label:`Weight`,error:B.weight,hint:`Share among equal priorities.`,children:({id:e,describedBy:t,invalid:n})=>(0,$.jsx)(X,{id:e,inputMode:`decimal`,value:m,onChange:e=>g(e.target.value),"aria-describedby":t,"aria-invalid":n||void 0,className:`tnum font-mono`})})]}),e.provider===`openai-compatible`&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(Z,{label:`Base URL`,required:!0,error:B.baseUrl,children:({id:e,describedBy:t,invalid:n})=>(0,$.jsx)(X,{id:e,type:`url`,value:A,onChange:e=>j(e.target.value),"aria-describedby":t,"aria-invalid":n||void 0,className:`font-mono`})}),(0,$.jsx)(Z,{label:`Models`,hint:`Comma-separated allow-list.`,children:({id:e,describedBy:t})=>(0,$.jsx)(X,{id:e,value:M,onChange:e=>I(e.target.value),"aria-describedby":t,className:`font-mono`})})]}),(a.auth===`api-key`||a.auth===`optional-key`)&&(0,$.jsx)(Z,{label:`Replace ${(a.secretLabel??`secret`).replace(` (optional)`,``)}`,hint:e.hasSecret===!1?`No secret stored yet. Write-only: it is encrypted at rest and never displayed.`:`A secret is stored. Write-only: leave empty to keep it; it is never displayed.`,children:({id:e,describedBy:t})=>(0,$.jsx)(te,{id:e,value:R,onChange:z,placeholder:`••••••••••••`,describedBy:t})}),(0,$.jsxs)(`fieldset`,{className:`flex flex-col gap-2`,children:[(0,$.jsx)(`legend`,{className:`mb-2 text-[12px] font-medium text-fg-2`,children:`Quota limits`}),(0,$.jsx)(ae,{value:_,onChange:v,errors:H})]}),(0,$.jsxs)(`fieldset`,{className:`flex flex-col gap-2`,children:[(0,$.jsx)(`legend`,{className:`mb-1 text-[12px] font-medium text-fg-2`,children:`Subscription`}),(0,$.jsx)(`p`,{className:`mb-1 text-2xs text-muted`,children:`What you pay for this account. Entered by hand; DaveCode never reads billing pages. Agents see the plan, renewal and remaining budget, never secrets.`}),(0,$.jsx)(re,{value:b,onChange:x,errors:S})]}),Object.keys(e.config).length>0&&e.provider!==`openai-compatible`&&(0,$.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,$.jsx)(`p`,{className:`text-[12px] font-medium text-fg-2`,children:`Provider config`}),(0,$.jsx)(`pre`,{className:`overflow-x-auto rounded-md border border-line bg-raised p-2.5 font-mono text-2xs leading-5 text-fg-2`,children:JSON.stringify(e.config,null,2)})]}),G&&(0,$.jsx)(E,{tone:`err`,title:`Request failed`,children:G}),(0,$.jsxs)(`div`,{className:`sticky -bottom-4 -mx-5 -mb-4 flex items-center gap-2 border-t border-line bg-panel px-5 py-3`,children:[(0,$.jsx)(Y,{variant:`danger`,icon:D,onClick:()=>J(!0),children:`Delete`}),(0,$.jsxs)(`div`,{className:`ml-auto flex gap-2`,children:[(0,$.jsx)(Y,{variant:`ghost`,onClick:n,children:`Cancel`}),(0,$.jsx)(Y,{type:`submit`,variant:`primary`,loading:r.isPending,children:`Save changes`})]})]}),(0,$.jsx)(F,{open:q,onClose:()=>J(!1),title:`Delete ${e.label}?`,description:`The account and its encrypted secret are removed from the gateway. Requests already served stay in the usage history.`,footer:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(Y,{variant:`ghost`,onClick:()=>J(!1),children:`Keep account`}),(0,$.jsx)(Y,{variant:`danger`,icon:D,loading:i.isPending,onClick:async()=>{try{await i.mutateAsync(e.id),O({tone:`ok`,title:`Account removed`,description:e.label}),J(!1),n()}catch(e){J(!1),K(u(e))}},children:`Delete account`})]}),children:(0,$.jsx)(`p`,{className:`font-mono text-[12px] text-muted`,children:e.id})})]})}var _e={"claude-cli":{binary:`claude`,env:`CLAUDE_CONFIG_DIR`},"codex-cli":{binary:`codex`,env:`CODEX_HOME`},"gemini-cli":{binary:`gemini`,env:`GEMINI_CLI_HOME`}};function ve({open:e,onClose:t}){return(0,$.jsx)(I,{open:e,onClose:t,title:`Add account`,description:`Secrets are encrypted at rest with AES-256-GCM and are never shown again after saving.`,width:`w-[min(560px,100vw)]`,children:e&&(0,$.jsx)(ye,{onDone:t})})}function ye({onDone:e}){let t=a(),n=p(),r=b(),o=t.data?.experimental.geminiWeb??!1,s=t.data?.experimental.multiAccountRotation??!1,[c,l]=(0,Q.useState)(`anthropic`),[d,g]=(0,Q.useState)(``),[_,v]=(0,Q.useState)(``),[y,x]=(0,Q.useState)(``),[S,w]=(0,Q.useState)(``),[T,re]=(0,Q.useState)(``),[D,k]=(0,Q.useState)(`100`),[A,j]=(0,Q.useState)(`1`),[M,P]=(0,Q.useState)(C()),[F,I]=(0,Q.useState)(!1),[L,R]=(0,Q.useState)({}),[z,B]=(0,Q.useState)(null),V=h(c),H=(n.data??[]).filter(e=>e.provider===c),U=V.subscription&&H.length>0&&!s,G=c===`gemini-web`&&!o;return(0,$.jsxs)(`form`,{onSubmit:async t=>{t.preventDefault(),B(null);let n={};d.trim()||(n.label=`Give the account a name you will recognise in logs.`),V.auth===`api-key`&&!_.trim()&&(n.secret=`${V.secretLabel??`Secret`} is required.`),c===`openai-compatible`&&!ee(y.trim())&&(n.baseUrl=`Enter an http(s) URL ending in the /v1 base, e.g. http://127.0.0.1:11434/v1`);let i=Number(D);(!Number.isInteger(i)||i<0)&&(n.priority=`Whole number, 0 or more.`);let a=Number(A);(!Number.isFinite(a)||a<=0)&&(n.weight=`Number above 0.`),c===`gemini-web`&&!F&&(n.ack=`Confirm that you accept the risk.`);let o=ne(M);if(Object.keys(o.errors).length&&(n.limits=o.errors),R(n),Object.keys(n).length||G)return;let s={};if(c===`openai-compatible`){s.baseUrl=y.trim();let e=ie(S);e.length&&(s.models=e)}V.auth===`cli`&&T.trim()&&(s.binaryPath=T.trim());let l={provider:c,label:d.trim(),priority:i,weight:a,...Object.keys(o.limits).length&&{limits:o.limits},...Object.keys(s).length&&{config:s},..._.trim()&&{secret:_.trim()}};try{let t=await r.mutateAsync(l);v(``),O({tone:`ok`,title:`Account added`,description:`${t.label} (${t.id})`}),e()}catch(e){v(``),B(m(e)&&e.code===`experimental_disabled`?`The gateway refused this provider: experimental.geminiWeb is off in config.json.`:u(e))}},noValidate:!0,className:`flex flex-col gap-5`,children:[(0,$.jsxs)(`fieldset`,{children:[(0,$.jsx)(`legend`,{className:`mb-2 text-[12px] font-medium text-fg-2`,children:`Provider`}),(0,$.jsx)(`div`,{className:`grid grid-cols-2 gap-2`,children:f.map(e=>{let t=h(e),n=e===`gemini-web`&&!o,r=c===e;return(0,$.jsxs)(`label`,{className:i(`relative flex cursor-pointer items-start gap-2.5 rounded-md border p-2.5 transition-colors duration-150`,`has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-accent`,r?`border-accent/60 bg-accent-soft`:`border-line hover:border-line-strong hover:bg-hover`,n&&!r&&`opacity-60`),children:[(0,$.jsx)(`input`,{type:`radio`,name:`provider`,value:e,checked:r,onChange:()=>{l(e),R({}),B(null)},className:`sr-only`}),(0,$.jsx)(N,{provider:e}),(0,$.jsxs)(`span`,{className:`min-w-0`,children:[(0,$.jsxs)(`span`,{className:`flex items-center gap-1.5 text-[13px] font-medium text-fg`,children:[t.label,t.experimental&&(0,$.jsx)(`span`,{className:`rounded border border-warn/30 px-1 text-2xs font-medium text-warn`,children:n?`off`:`experimental`})]}),(0,$.jsx)(`span`,{className:`block font-mono text-2xs text-muted`,children:e})]})]},e)})}),(0,$.jsx)(`p`,{className:`mt-2 text-[12px] text-muted`,children:V.blurb})]}),c===`gemini-web`&&(0,$.jsxs)(E,{tone:`warn`,title:`Terms of Service risk`,children:[(0,$.jsx)(`p`,{children:`This provider automates a consumer Gemini web session through Chromium. That likely violates Google's Terms of Service and the account can be rate-limited or suspended.`}),o?(0,$.jsxs)(`label`,{className:`mt-2 flex items-start gap-2 text-fg`,children:[(0,$.jsx)(`input`,{type:`checkbox`,checked:F,onChange:e=>I(e.target.checked),className:`mt-0.5 accent-accent`,"aria-describedby":L.ack?`ack-error`:void 0}),`I understand the risk and want to add this account anyway.`]}):(0,$.jsxs)(`p`,{className:`mt-1.5`,children:[`It is disabled on this gateway. To opt in, set`,` `,(0,$.jsxs)(`code`,{className:`font-mono text-fg`,children:[`"experimental": `,`{ "geminiWeb": true }`]}),` `,`in `,(0,$.jsx)(`code`,{className:`font-mono text-fg`,children:`~/.davecode/config.json`}),` and restart.`]}),L.ack&&(0,$.jsx)(`p`,{id:`ack-error`,role:`alert`,className:`mt-1 text-err`,children:L.ack})]}),U&&(0,$.jsxs)(E,{tone:`info`,title:`Multi-account rotation is off`,children:[`You already have a `,V.label,` account. Without`,` `,(0,$.jsx)(`code`,{className:`font-mono text-fg`,children:`experimental.multiAccountRotation`}),` the router uses only one subscription login per provider; the others act as manual fallbacks.`]}),!G&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(Z,{label:`Label`,required:!0,error:L.label,hint:`Shown in the dashboard, logs and events.`,children:({id:e,describedBy:t,invalid:n})=>(0,$.jsx)(X,{id:e,value:d,placeholder:c===`openai-compatible`?`OpenRouter`:`${V.label} — work`,onChange:e=>g(e.target.value),"aria-describedby":t,"aria-invalid":n||void 0,"aria-required":!0})}),c===`openai-compatible`&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(Z,{label:`Base URL`,required:!0,error:L.baseUrl,hint:`The OpenAI-compatible root, including /v1.`,children:({id:e,describedBy:t,invalid:n})=>(0,$.jsx)(X,{id:e,type:`url`,value:y,placeholder:`http://127.0.0.1:11434/v1`,onChange:e=>x(e.target.value),"aria-describedby":t,"aria-invalid":n||void 0,className:`font-mono`})}),(0,$.jsx)(Z,{label:`Models`,hint:`Comma-separated allow-list. Leave empty to discover via /v1/models.`,children:({id:e,describedBy:t})=>(0,$.jsx)(X,{id:e,value:S,placeholder:`Provider model IDs`,onChange:e=>w(e.target.value),"aria-describedby":t,className:`font-mono`})})]}),V.auth===`cli`?(0,$.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,$.jsx)(Z,{label:`CLI binary`,hint:`Optional path; defaults to the binary on your PATH.`,children:({id:e,describedBy:t})=>(0,$.jsx)(X,{id:e,value:T,placeholder:_e[c]?.binary??`claude`,onChange:e=>re(e.target.value),"aria-describedby":t,className:`font-mono`})}),(0,$.jsxs)(`p`,{className:`text-[12px] text-muted`,children:[`No secret is needed here. After adding, open the account and press`,` `,(0,$.jsx)(`span`,{className:`text-fg-2`,children:`Connect subscription`}),`: it runs the vendor’s official sign-in inside the account’s own sandbox (`,_e[c]?.env,`). DaveCode never sees your password. From a terminal:`]}),(0,$.jsx)(W,{command:`davecode add-account --provider ${c}`})]}):V.auth===`browser`?null:(0,$.jsx)(Z,{label:V.secretLabel??`Secret`,required:V.auth===`api-key`,error:L.secret,hint:`Write-only. Encrypted at rest; no endpoint ever returns it.`,children:({id:e,describedBy:t,invalid:n})=>(0,$.jsx)(te,{id:e,value:_,onChange:v,placeholder:V.secretPlaceholder,describedBy:t,invalid:n})}),(0,$.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,$.jsx)(Z,{label:`Priority`,error:L.priority,hint:`Lower is tried first.`,children:({id:e,describedBy:t,invalid:n})=>(0,$.jsx)(X,{id:e,inputMode:`numeric`,value:D,onChange:e=>k(e.target.value),"aria-describedby":t,"aria-invalid":n||void 0,className:`tnum font-mono`})}),(0,$.jsx)(Z,{label:`Weight`,error:L.weight,hint:`Share among equal priorities.`,children:({id:e,describedBy:t,invalid:n})=>(0,$.jsx)(X,{id:e,inputMode:`decimal`,value:A,onChange:e=>j(e.target.value),"aria-describedby":t,"aria-invalid":n||void 0,className:`tnum font-mono`})})]}),(0,$.jsxs)(`details`,{className:`group rounded-md border border-line`,children:[(0,$.jsxs)(`summary`,{className:`flex items-center justify-between px-3 py-2 text-[12px] font-medium text-fg-2 select-none hover:text-fg`,children:[`Quota limits`,(0,$.jsx)(`span`,{className:`text-2xs font-normal text-muted group-open:hidden`,children:`optional · unlimited by default`})]}),(0,$.jsx)(`div`,{className:`border-t border-line p-3`,children:(0,$.jsx)(ae,{value:M,onChange:P,errors:L.limits??{}})})]})]}),z&&(0,$.jsx)(E,{tone:`err`,title:`Could not add the account`,children:z}),(0,$.jsxs)(`div`,{className:`sticky -bottom-4 -mx-5 -mb-4 flex items-center justify-end gap-2 border-t border-line bg-panel px-5 py-3`,children:[(0,$.jsx)(Y,{variant:`ghost`,onClick:e,children:`Cancel`}),(0,$.jsx)(Y,{type:`submit`,variant:`primary`,loading:r.isPending,disabled:G,icon:G?ue:void 0,children:G?`Disabled by config`:`Add account`})]})]})}function be(e){if(!e)return;let t=e.price?`${e.price.amount} ${e.price.currency}/${e.price.period===`month`?`mo`:`yr`}`:void 0;return[e.plan,t].filter(Boolean).join(` · `)||void 0}function xe({days:e}){return e===void 0?(0,$.jsx)(`span`,{className:`text-faint`,children:`—`}):e<0?(0,$.jsxs)(g,{tone:`err`,children:[`overdue `,-e,`d`]}):(0,$.jsx)(g,{tone:e<=3?`warn`:`neutral`,mono:!0,children:e===0?`today`:`in ${e}d`})}function Se({budget:e}){if(e.remaining===null)return(0,$.jsx)(`span`,{className:`text-2xs text-faint`,children:`no limit known`});let t=e.remaining,n=t<=.1?`bg-err`:t<=.15?`bg-warn`:`bg-meter`;return(0,$.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,$.jsx)(`div`,{role:`meter`,"aria-label":`${e.label} remaining budget`,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":Math.round(t*100),className:`relative h-1.5 min-w-16 flex-1 rounded-full bg-track`,children:(0,$.jsx)(`div`,{className:i(`absolute inset-y-0 left-0 rounded-full transition-[width] duration-500 ease-snappy`,n),style:{width:`${Math.min(100,t*100)}%`}})}),(0,$.jsx)(`span`,{className:`tnum w-9 shrink-0 text-right font-mono text-2xs text-fg-2`,children:_(t)}),(0,$.jsx)(`span`,{className:`w-14 shrink-0 font-mono text-2xs text-muted`,children:e.tightestWindow??`provider`})]})}function Ce(){let e=d(),[t,n]=(0,Q.useState)(!1),r=e.data?.accounts??[],a=r.filter(e=>e.enabled),o=a.filter(e=>e.available).length;return(0,$.jsxs)(G,{"aria-labelledby":`budget-title`,children:[(0,$.jsx)(R,{id:`budget-title`,title:`Budget`,meta:e.data?`${o} of ${a.length} enabled accounts available · plans are entered by hand`:void 0,actions:(0,$.jsx)(Y,{size:`sm`,variant:`ghost`,icon:A,"aria-expanded":t,onClick:()=>n(e=>!e),children:`What agents see`})}),e.isPending?(0,$.jsx)(J,{rows:3}):e.isError?(0,$.jsx)(`p`,{className:`px-3.5 py-3 text-[12px] text-err`,children:u(e.error)}):(0,$.jsxs)($.Fragment,{children:[t&&(0,$.jsxs)(`div`,{className:`relative border-b border-line bg-raised px-3.5 py-3`,children:[(0,$.jsx)(V,{value:e.data.context,label:`Copy agent budget context`,className:`absolute top-2 right-2`}),(0,$.jsx)(`pre`,{className:`overflow-x-auto pr-8 font-mono text-2xs leading-5 whitespace-pre-wrap text-fg-2`,children:e.data.context||`No enabled accounts: agents get no budget context.`})]}),(0,$.jsx)(`ul`,{className:`divide-y divide-line`,children:r.map(e=>(0,$.jsx)(`li`,{children:(0,$.jsxs)(`button`,{type:`button`,onClick:()=>j(`account`,e.accountId),className:i(`grid w-full grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)_auto] items-center gap-x-4 gap-y-1.5 px-3.5 py-2.5 text-left transition-colors duration-150 hover:bg-hover/60 md:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)_5.5rem_minmax(0,1.2fr)_minmax(0,1fr)]`,!e.enabled&&`text-muted`),children:[(0,$.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2`,children:[(0,$.jsx)(N,{provider:e.provider}),(0,$.jsx)(`span`,{className:`truncate text-[13px] font-medium`,children:e.label})]}),(0,$.jsx)(`span`,{className:`truncate text-[12px] text-fg-2`,children:be(e.subscription)??(0,$.jsx)(`span`,{className:`text-faint`,children:`no plan`})}),(0,$.jsx)(`span`,{className:`justify-self-end md:justify-self-start`,children:(0,$.jsx)(xe,{days:e.renewsInDays})}),(0,$.jsx)(`span`,{className:`col-span-2 md:col-span-1`,children:(0,$.jsx)(Se,{budget:e})}),(0,$.jsx)(`span`,{className:`truncate text-[12px] md:text-right`,children:e.available?(0,$.jsx)(g,{tone:`ok`,dot:!0,children:`available`}):(0,$.jsx)(`span`,{className:`text-warn`,title:e.unavailableReason,children:e.unavailableReason??`unavailable`})})]})},e.accountId))})]})]})}function we(){let e=p(),t=s(),n=o(),{params:r}=B(),[a,c]=(0,Q.useState)(``),[l,d]=(0,Q.useState)(`all`),f=r.get(`account`),m=r.get(`new`)===`1`,g=(0,Q.useMemo)(()=>new Map((t.data??[]).map(e=>[e.accountId,e])),[t.data]),_=(0,Q.useMemo)(()=>[...e.data??[]].sort((e,t)=>e.priority-t.priority||e.label.localeCompare(t.label)),[e.data]),v=(0,Q.useMemo)(()=>{let e={all:_.length,active:0,cooldown:0,disabled:0,error:0};for(let t of _)e[t.status]+=1;return e},[_]),y=a.trim().toLowerCase(),b=_.filter(e=>(l===`all`||e.status===l)&&(!y||e.label.toLowerCase().includes(y)||e.id.toLowerCase().includes(y)||e.provider.includes(y))),C=_.find(e=>e.id===f),w=(e,t)=>{n.mutate({id:e.id,patch:{enabled:t}},{onError:n=>O({tone:`err`,title:`Could not ${t?`enable`:`disable`} ${e.label}`,description:u(n)})})};return(0,$.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,$.jsx)(H,{title:`Accounts`,description:`Provider accounts the router balances across. Lower priority numbers are tried first.`,actions:(0,$.jsx)(Y,{variant:`primary`,icon:oe,onClick:()=>j(`new`,`1`),children:`Add account`})}),(0,$.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,$.jsxs)(`div`,{className:`relative w-full sm:w-64`,children:[(0,$.jsx)(k,{"aria-hidden":!0,className:`pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-muted`,strokeWidth:1.75}),(0,$.jsx)(X,{type:`search`,"aria-label":`Filter accounts`,placeholder:`Filter by label, id or provider`,value:a,onChange:e=>c(e.target.value),className:`pl-8`,"data-filter-input":!0})]}),(0,$.jsx)(q,{label:`Status`,value:l,onChange:d,options:[{value:`all`,label:`All`,count:v.all},{value:`active`,label:`Active`,count:v.active},{value:`cooldown`,label:`Cooldown`,count:v.cooldown},{value:`disabled`,label:`Disabled`,count:v.disabled},{value:`error`,label:`Error`,count:v.error}]}),(0,$.jsx)(P,{className:`ml-auto hidden lg:flex`})]}),e.isError&&(0,$.jsx)(E,{tone:`err`,title:`Could not load accounts`,children:u(e.error)}),(0,$.jsx)(G,{children:e.isPending?(0,$.jsx)(J,{rows:5}):_.length===0?(0,$.jsx)(K,{icon:z,title:`No accounts yet`,description:`Add an API key, a local OpenAI-compatible server or a CLI login. The router starts using it immediately.`,command:`davecode add-account`,action:(0,$.jsx)(Y,{variant:`primary`,icon:oe,onClick:()=>j(`new`,`1`),children:`Add account`})}):b.length===0?(0,$.jsx)(K,{icon:k,title:`No matching accounts`,description:`Try a different filter.`}):(0,$.jsx)(`div`,{className:`overflow-x-auto`,children:(0,$.jsxs)(`table`,{className:`w-full min-w-[880px] text-[13px]`,children:[(0,$.jsx)(`caption`,{className:`sr-only`,children:`Accounts`}),(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{className:`border-b border-line text-left text-[12px] text-muted`,children:[(0,$.jsx)(`th`,{scope:`col`,className:`w-[32%] px-3.5 py-2 font-medium`,children:`Account`}),(0,$.jsx)(`th`,{scope:`col`,className:`px-3 py-2 font-medium`,children:`Status`}),(0,$.jsx)(`th`,{scope:`col`,className:`w-20 px-3 py-2 text-right font-medium`,children:`Priority`}),(0,$.jsx)(`th`,{scope:`col`,className:`w-20 px-3 py-2 text-right font-medium`,children:`Weight`}),(0,$.jsx)(`th`,{scope:`col`,className:`w-[30%] px-3 py-2 font-medium`,children:`Quota`}),(0,$.jsx)(`th`,{scope:`col`,className:`w-20 px-3 py-2 font-medium`,children:`Enabled`}),(0,$.jsx)(`th`,{scope:`col`,className:`w-10 px-2 py-2`,children:(0,$.jsx)(`span`,{className:`sr-only`,children:`Details`})})]})}),(0,$.jsx)(`tbody`,{children:b.map(e=>{let t=g.get(e.id),n=h(e.provider);return(0,$.jsxs)(`tr`,{onClick:()=>j(`account`,e.id),className:i(`group cursor-pointer border-b border-line transition-colors duration-150 last:border-b-0 hover:bg-hover/60`,f===e.id&&`bg-hover/60`,!e.enabled&&`text-muted`),children:[(0,$.jsx)(`td`,{className:`max-w-0 px-3.5 py-2.5`,children:(0,$.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2.5`,children:[(0,$.jsx)(N,{provider:e.provider}),(0,$.jsxs)(`div`,{className:`min-w-0`,children:[(0,$.jsx)(`button`,{type:`button`,onClick:t=>{t.stopPropagation(),j(`account`,e.id)},className:`block max-w-full truncate text-left font-medium text-fg hover:underline focus-visible:underline`,children:e.label}),(0,$.jsxs)(`p`,{className:`truncate font-mono text-2xs text-muted`,children:[e.id,` · `,n.label,n.experimental&&(0,$.jsx)(`span`,{className:`text-warn`,children:` · experimental`})]})]})]})}),(0,$.jsx)(`td`,{className:`px-3 py-2.5`,children:(0,$.jsx)(L,{account:e})}),(0,$.jsx)(`td`,{className:`tnum px-3 py-2.5 text-right font-mono`,children:e.priority}),(0,$.jsx)(`td`,{className:`tnum px-3 py-2.5 text-right font-mono`,children:e.weight}),(0,$.jsx)(`td`,{className:`px-3 py-2`,children:(0,$.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,$.jsx)(U,{label:`1m`,usage:t?.windows[`1m`]}),(0,$.jsx)(U,{label:`5h`,usage:t?.windows[`5h`]}),(0,$.jsx)(U,{label:`24h`,usage:t?.windows[`24h`]})]})}),(0,$.jsx)(`td`,{className:`px-3 py-2.5`,children:(0,$.jsx)(S,{checked:e.enabled,onChange:t=>w(e,t),label:`${e.enabled?`Disable`:`Enable`} ${e.label}`})}),(0,$.jsx)(`td`,{className:`px-2 py-2.5 text-muted`,children:(0,$.jsx)(x,{"aria-hidden":!0,className:`size-4 transition-transform duration-150 group-hover:translate-x-0.5`,strokeWidth:1.75})})]},e.id)})})]})})}),_.length>0&&(0,$.jsx)(Ce,{}),(0,$.jsx)(ve,{open:m,onClose:()=>j(`new`,null)}),(0,$.jsx)(he,{account:C,usage:C?g.get(C.id):void 0,onClose:()=>j(`account`,null)})]})}export{we as Accounts};
|