@kenkaiiii/ggcoder 4.3.223 → 4.3.225
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 +1 -1
- package/dist/cli/auth.d.ts +4 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +344 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/pixel.d.ts +27 -0
- package/dist/cli/pixel.d.ts.map +1 -0
- package/dist/cli/pixel.js +103 -0
- package/dist/cli/pixel.js.map +1 -0
- package/dist/cli/shared.d.ts +13 -0
- package/dist/cli/shared.d.ts.map +1 -0
- package/dist/cli/shared.js +82 -0
- package/dist/cli/shared.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +15 -544
- package/dist/cli.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +2 -2
- package/dist/core/compaction/token-estimator.test.js +1 -1
- package/dist/core/model-registry.d.ts +2 -2
- package/dist/core/model-registry.js +4 -4
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-registry.test.js +1 -1
- package/dist/core/model-registry.test.js.map +1 -1
- package/dist/core/runtime-mode.js +1 -1
- package/dist/core/runtime-mode.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +1 -6
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/system-prompt.js +2 -2
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +2 -1
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/plan-mode.test.js +13 -1
- package/dist/tools/plan-mode.test.js.map +1 -1
- package/dist/tools/read-only-bash.d.ts +13 -0
- package/dist/tools/read-only-bash.d.ts.map +1 -0
- package/dist/tools/read-only-bash.js +155 -0
- package/dist/tools/read-only-bash.js.map +1 -0
- package/dist/tools/read-only-bash.test.d.ts +2 -0
- package/dist/tools/read-only-bash.test.d.ts.map +1 -0
- package/dist/tools/read-only-bash.test.js +47 -0
- package/dist/tools/read-only-bash.test.js.map +1 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +105 -1034
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/Footer.d.ts +2 -2
- package/dist/ui/components/Footer.js +8 -8
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/index.d.ts +3 -0
- package/dist/ui/components/index.d.ts.map +1 -1
- package/dist/ui/components/index.js +3 -0
- package/dist/ui/components/index.js.map +1 -1
- package/dist/ui/hooks/useContextCompaction.d.ts +41 -0
- package/dist/ui/hooks/useContextCompaction.d.ts.map +1 -0
- package/dist/ui/hooks/useContextCompaction.js +149 -0
- package/dist/ui/hooks/useContextCompaction.js.map +1 -0
- package/dist/ui/hooks/useGoalOrchestration.d.ts +81 -0
- package/dist/ui/hooks/useGoalOrchestration.d.ts.map +1 -0
- package/dist/ui/hooks/useGoalOrchestration.js +745 -0
- package/dist/ui/hooks/useGoalOrchestration.js.map +1 -0
- package/dist/ui/hooks/useModeState.d.ts +61 -0
- package/dist/ui/hooks/useModeState.d.ts.map +1 -0
- package/dist/ui/hooks/useModeState.js +86 -0
- package/dist/ui/hooks/useModeState.js.map +1 -0
- package/dist/ui/hooks/usePixelFixFlow.d.ts +57 -0
- package/dist/ui/hooks/usePixelFixFlow.d.ts.map +1 -0
- package/dist/ui/hooks/usePixelFixFlow.js +102 -0
- package/dist/ui/hooks/usePixelFixFlow.js.map +1 -0
- package/dist/ui/hooks/useSessionPersistence.d.ts +34 -0
- package/dist/ui/hooks/useSessionPersistence.d.ts.map +1 -0
- package/dist/ui/hooks/useSessionPersistence.js +67 -0
- package/dist/ui/hooks/useSessionPersistence.js.map +1 -0
- package/dist/ui/hooks/useTranscriptHistory.d.ts +30 -16
- package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -1
- package/dist/ui/hooks/useTranscriptHistory.js +13 -15
- package/dist/ui/hooks/useTranscriptHistory.js.map +1 -1
- package/dist/ui/login.js +1 -1
- package/dist/ui/terminal-history.test.js +60 -0
- package/dist/ui/terminal-history.test.js.map +1 -1
- package/dist/ui/thinking-level-cycle.test.js +23 -3
- package/dist/ui/thinking-level-cycle.test.js.map +1 -1
- package/dist/ui/thinking-level.d.ts.map +1 -1
- package/dist/ui/thinking-level.js +30 -1
- package/dist/ui/thinking-level.js.map +1 -1
- package/dist/ui/transcript/spacing.d.ts +31 -19
- package/dist/ui/transcript/spacing.d.ts.map +1 -1
- package/dist/ui/transcript/spacing.js +16 -3
- package/dist/ui/transcript/spacing.js.map +1 -1
- package/package.json +24 -4
package/dist/cli.js
CHANGED
|
@@ -45,14 +45,11 @@ import { fileURLToPath } from "node:url";
|
|
|
45
45
|
import { parseArgs } from "node:util";
|
|
46
46
|
import fs from "node:fs";
|
|
47
47
|
import readline from "node:readline/promises";
|
|
48
|
-
import { execFile } from "node:child_process";
|
|
49
|
-
import { createRequire } from "node:module";
|
|
50
48
|
import { renderApp } from "./ui/render.js";
|
|
51
49
|
import { runJsonMode } from "./modes/json-mode.js";
|
|
52
50
|
import { runRpcMode } from "./modes/rpc-mode.js";
|
|
53
51
|
import { runServeMode } from "./modes/serve-mode.js";
|
|
54
52
|
import { runAgentHomeMode } from "./modes/agent-home-mode.js";
|
|
55
|
-
import { renderLoginSelector } from "./ui/login.js";
|
|
56
53
|
import { renderSessionSelector } from "./ui/sessions.js";
|
|
57
54
|
import { segmentDisplayText, stripDoneMarkers } from "./utils/plan-steps.js";
|
|
58
55
|
import { formatUserError } from "./utils/error-handler.js";
|
|
@@ -70,62 +67,23 @@ import { createCompactedSessionCheckpoint, formatRestoreInfoText, getRestoredMes
|
|
|
70
67
|
import { setEstimatorModel } from "./core/compaction/token-estimator.js";
|
|
71
68
|
import { getContextWindow, getDefaultModel, getMaxThinkingLevel, getModel, } from "./core/model-registry.js";
|
|
72
69
|
import { MCPClientManager, getMCPServers } from "./core/mcp/index.js";
|
|
70
|
+
import { runPixel } from "./cli/pixel.js";
|
|
71
|
+
import { runLogin, runLogout, runDoctor } from "./cli/auth.js";
|
|
72
|
+
import { CLI_VERSION, LOGO_LINES, clearVisibleScreen, displayName, gradientLine, requireInteractiveTTY, } from "./cli/shared.js";
|
|
73
73
|
import { discoverAgents } from "./core/agents.js";
|
|
74
74
|
import { discoverSkills } from "./core/skills.js";
|
|
75
75
|
import path from "node:path";
|
|
76
|
-
import { loginAnthropic } from "./core/oauth/anthropic.js";
|
|
77
|
-
import { loginOpenAI } from "./core/oauth/openai.js";
|
|
78
|
-
import { loginGemini } from "./core/oauth/gemini.js";
|
|
79
76
|
import chalk from "chalk";
|
|
80
77
|
import { checkAndAutoUpdate } from "./core/auto-update.js";
|
|
81
78
|
import { parseGoalSyntheticEvent } from "./ui/goal-events.js";
|
|
82
79
|
import { routeCliCommandInput } from "./cli/command-routing.js";
|
|
83
|
-
const
|
|
84
|
-
const CLI_VERSION = _require("../package.json").version;
|
|
85
|
-
const THINKING_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
|
|
80
|
+
const THINKING_LEVELS = new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
86
81
|
export function parseThinkingLevel(value) {
|
|
87
82
|
if (value === undefined)
|
|
88
83
|
return undefined;
|
|
89
84
|
if (THINKING_LEVELS.has(value))
|
|
90
85
|
return value;
|
|
91
|
-
throw new Error(`Invalid --thinking value "${value}". Expected low, medium, high, or
|
|
92
|
-
}
|
|
93
|
-
// ── Logo + gradient (mirrors Banner.tsx) ────────────────────────────
|
|
94
|
-
const LOGO_LINES = [
|
|
95
|
-
" \u2584\u2580\u2580\u2580 \u2584\u2580\u2580\u2580",
|
|
96
|
-
" \u2588 \u2580\u2588 \u2588 \u2580\u2588",
|
|
97
|
-
" \u2580\u2584\u2584\u2580 \u2580\u2584\u2584\u2580",
|
|
98
|
-
];
|
|
99
|
-
const GRADIENT = [
|
|
100
|
-
"#60a5fa",
|
|
101
|
-
"#6da1f9",
|
|
102
|
-
"#7a9df7",
|
|
103
|
-
"#8799f5",
|
|
104
|
-
"#9495f3",
|
|
105
|
-
"#a18ff1",
|
|
106
|
-
"#a78bfa",
|
|
107
|
-
"#a18ff1",
|
|
108
|
-
"#9495f3",
|
|
109
|
-
"#8799f5",
|
|
110
|
-
"#7a9df7",
|
|
111
|
-
"#6da1f9",
|
|
112
|
-
];
|
|
113
|
-
function gradientLine(text) {
|
|
114
|
-
let result = "";
|
|
115
|
-
let colorIdx = 0;
|
|
116
|
-
for (const ch of text) {
|
|
117
|
-
if (ch === " ") {
|
|
118
|
-
result += ch;
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
result += chalk.hex(GRADIENT[colorIdx % GRADIENT.length])(ch);
|
|
122
|
-
colorIdx++;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return result;
|
|
126
|
-
}
|
|
127
|
-
function clearVisibleScreen() {
|
|
128
|
-
process.stdout.write("\x1b[2J\x1b[H");
|
|
86
|
+
throw new Error(`Invalid --thinking value "${value}". Expected low, medium, high, xhigh, or max.`);
|
|
129
87
|
}
|
|
130
88
|
function printHelp() {
|
|
131
89
|
// Clear the visible viewport for a clean look without erasing scrollback.
|
|
@@ -178,7 +136,7 @@ function printHelp() {
|
|
|
178
136
|
["--model <name>", "Model to use (e.g. claude-sonnet-4-6, gpt-5.5)"],
|
|
179
137
|
["--max-turns <n>", "Maximum agent turns per prompt"],
|
|
180
138
|
["--system-prompt <text>", "Override the system prompt"],
|
|
181
|
-
["--thinking <level>", "Enable thinking level (low, medium, high, xhigh)"],
|
|
139
|
+
["--thinking <level>", "Enable thinking level (low, medium, high, xhigh, max)"],
|
|
182
140
|
["--resume <id>", "Resume a session by id"],
|
|
183
141
|
["--json", "JSON output mode (for sub-agents)"],
|
|
184
142
|
["--rpc", "JSON-RPC mode (for IDE integrations)"],
|
|
@@ -227,7 +185,7 @@ function createCliSubcommandHandlers() {
|
|
|
227
185
|
});
|
|
228
186
|
};
|
|
229
187
|
return {
|
|
230
|
-
pixel: () => runWithStandardErrorHandling(runPixel, true),
|
|
188
|
+
pixel: () => runWithStandardErrorHandling(() => runPixel({ runInkTUI }), true),
|
|
231
189
|
login: () => runWithStandardErrorHandling(runLogin),
|
|
232
190
|
logout: () => runWithStandardErrorHandling(runLogout),
|
|
233
191
|
sessions: () => runWithStandardErrorHandling(runSessions),
|
|
@@ -288,7 +246,7 @@ function main() {
|
|
|
288
246
|
if (values.json) {
|
|
289
247
|
const message = positionals[0] ?? "";
|
|
290
248
|
const jsonProvider = (values.provider ?? "anthropic");
|
|
291
|
-
const jsonModel = values.model ?? "claude-opus-4-
|
|
249
|
+
const jsonModel = values.model ?? "claude-opus-4-8";
|
|
292
250
|
const maxTurns = values["max-turns"] ? parseInt(values["max-turns"], 10) : undefined;
|
|
293
251
|
const systemPrompt = values["system-prompt"];
|
|
294
252
|
const promptCacheKey = values["prompt-cache-key"];
|
|
@@ -312,7 +270,7 @@ function main() {
|
|
|
312
270
|
// RPC mode — headless JSON-over-stdio for IDE integrations
|
|
313
271
|
if (values.rpc) {
|
|
314
272
|
const rpcProvider = (values.provider ?? "anthropic");
|
|
315
|
-
const rpcModel = values.model ?? "claude-opus-4-
|
|
273
|
+
const rpcModel = values.model ?? "claude-opus-4-8";
|
|
316
274
|
const systemPrompt = values["system-prompt"];
|
|
317
275
|
const cwd = process.cwd();
|
|
318
276
|
runRpcMode({
|
|
@@ -345,7 +303,7 @@ function main() {
|
|
|
345
303
|
return "deepseek-v4-pro";
|
|
346
304
|
if (p === "openrouter")
|
|
347
305
|
return "qwen/qwen3.6-plus";
|
|
348
|
-
return "claude-opus-4-
|
|
306
|
+
return "claude-opus-4-8";
|
|
349
307
|
}
|
|
350
308
|
const model = saved.model ?? getHardcodedDefault(provider);
|
|
351
309
|
const thinkingLevel = saved.thinkingEnabled
|
|
@@ -370,18 +328,6 @@ function main() {
|
|
|
370
328
|
});
|
|
371
329
|
}
|
|
372
330
|
// ── Ink TUI ───────────────────────────────────────────────
|
|
373
|
-
/**
|
|
374
|
-
* Bail with a friendly message if stdin isn't a TTY. Ink's raw-mode crash is
|
|
375
|
-
* cryptic; this catches the common case (piped stdin, API shells, CI).
|
|
376
|
-
*/
|
|
377
|
-
function requireInteractiveTTY() {
|
|
378
|
-
if (process.stdin.isTTY)
|
|
379
|
-
return;
|
|
380
|
-
process.stderr.write(chalk.red("ggcoder needs an interactive terminal — your stdin isn't a TTY.\n") +
|
|
381
|
-
chalk.hex("#6b7280")("Run ggcoder directly in your terminal (not piped or through an API shell). " +
|
|
382
|
-
'For headless use try "ggcoder --json \'<prompt>\'" or "ggcoder --rpc".\n'));
|
|
383
|
-
process.exit(1);
|
|
384
|
-
}
|
|
385
331
|
async function runInkTUI(opts) {
|
|
386
332
|
requireInteractiveTTY();
|
|
387
333
|
const { cwd } = opts;
|
|
@@ -652,341 +598,6 @@ async function runInkTUI(opts) {
|
|
|
652
598
|
});
|
|
653
599
|
closeLogger();
|
|
654
600
|
}
|
|
655
|
-
// ── Login ──────────────────────────────────────────────────
|
|
656
|
-
async function runLogin() {
|
|
657
|
-
requireInteractiveTTY();
|
|
658
|
-
clearVisibleScreen();
|
|
659
|
-
const paths = await ensureAppDirs();
|
|
660
|
-
initLogger(paths.logFile, { version: CLI_VERSION });
|
|
661
|
-
log("INFO", "auth", "Login flow started");
|
|
662
|
-
const authStorage = new AuthStorage();
|
|
663
|
-
await authStorage.load();
|
|
664
|
-
// Phase 1: Ink-based provider selector
|
|
665
|
-
const provider = await renderLoginSelector(CLI_VERSION);
|
|
666
|
-
if (!provider) {
|
|
667
|
-
console.log(chalk.hex("#6b7280")("Login cancelled."));
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
console.log(chalk.hex("#60a5fa").bold("\nLogging in to ") +
|
|
671
|
-
chalk.hex("#a78bfa")(displayName(provider)) +
|
|
672
|
-
chalk.hex("#60a5fa").bold("...\n"));
|
|
673
|
-
// Phase 2: OAuth flow (readline needed for Anthropic code paste)
|
|
674
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
675
|
-
try {
|
|
676
|
-
const callbacks = {
|
|
677
|
-
onOpenUrl: (url) => {
|
|
678
|
-
console.log(chalk.hex("#60a5fa").bold("Opening browser..."));
|
|
679
|
-
openBrowser(url);
|
|
680
|
-
console.log(chalk.hex("#6b7280")("\nIf the browser didn't open, visit:\n") +
|
|
681
|
-
chalk.hex("#6b7280")(url) +
|
|
682
|
-
"\n");
|
|
683
|
-
},
|
|
684
|
-
onPromptCode: async (message) => {
|
|
685
|
-
return rl.question(message + " ");
|
|
686
|
-
},
|
|
687
|
-
onStatus: (message) => {
|
|
688
|
-
console.log(chalk.hex("#6b7280")(message));
|
|
689
|
-
},
|
|
690
|
-
};
|
|
691
|
-
let creds;
|
|
692
|
-
if (provider === "glm" ||
|
|
693
|
-
provider === "moonshot" ||
|
|
694
|
-
provider === "xiaomi" ||
|
|
695
|
-
provider === "minimax" ||
|
|
696
|
-
provider === "deepseek" ||
|
|
697
|
-
provider === "openrouter") {
|
|
698
|
-
const keyLabel = provider === "glm"
|
|
699
|
-
? "Z.AI"
|
|
700
|
-
: provider === "xiaomi"
|
|
701
|
-
? "Xiaomi MiMo"
|
|
702
|
-
: provider === "minimax"
|
|
703
|
-
? "MiniMax"
|
|
704
|
-
: provider === "deepseek"
|
|
705
|
-
? "DeepSeek"
|
|
706
|
-
: provider === "openrouter"
|
|
707
|
-
? "OpenRouter"
|
|
708
|
-
: "Moonshot";
|
|
709
|
-
const apiKey = await rl.question(chalk.hex("#60a5fa")(`Paste your ${keyLabel} API key: `));
|
|
710
|
-
if (!apiKey.trim()) {
|
|
711
|
-
console.log(chalk.hex("#ef4444")("No API key provided. Login cancelled."));
|
|
712
|
-
return;
|
|
713
|
-
}
|
|
714
|
-
creds = {
|
|
715
|
-
accessToken: apiKey.trim(),
|
|
716
|
-
refreshToken: "",
|
|
717
|
-
expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000 * 100, // ~100 years
|
|
718
|
-
...(provider === "xiaomi" ? { baseUrl: "https://token-plan-sgp.xiaomimimo.com/v1" } : {}),
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
else {
|
|
722
|
-
creds =
|
|
723
|
-
provider === "anthropic"
|
|
724
|
-
? await loginAnthropic(callbacks)
|
|
725
|
-
: provider === "gemini"
|
|
726
|
-
? await loginGemini(callbacks)
|
|
727
|
-
: await loginOpenAI(callbacks);
|
|
728
|
-
}
|
|
729
|
-
await authStorage.setCredentials(provider, creds);
|
|
730
|
-
log("INFO", "auth", `Login succeeded for ${displayName(provider)}`);
|
|
731
|
-
console.log(chalk.hex("#4ade80")(`\n✓ Logged in to ${displayName(provider)} successfully!`));
|
|
732
|
-
}
|
|
733
|
-
finally {
|
|
734
|
-
rl.close();
|
|
735
|
-
closeLogger();
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
// ── Doctor ─────────────────────────────────────────────────
|
|
739
|
-
async function runDoctor() {
|
|
740
|
-
clearVisibleScreen();
|
|
741
|
-
const os = await import("node:os");
|
|
742
|
-
const fsP = await import("node:fs/promises");
|
|
743
|
-
const dim = chalk.hex("#6b7280");
|
|
744
|
-
const primary = chalk.hex("#60a5fa");
|
|
745
|
-
const accent = chalk.hex("#a78bfa");
|
|
746
|
-
const good = chalk.hex("#4ade80");
|
|
747
|
-
const warn = chalk.hex("#fbbf24");
|
|
748
|
-
const bad = chalk.hex("#ef4444");
|
|
749
|
-
// ── Banner ──────────────────────────────────────────────────
|
|
750
|
-
const LOGO = LOGO_LINES;
|
|
751
|
-
const GAP = " ";
|
|
752
|
-
console.log();
|
|
753
|
-
console.log(` ${gradientLine(LOGO[0])}${GAP}` +
|
|
754
|
-
primary.bold("GG Coder") +
|
|
755
|
-
dim(` v${CLI_VERSION}`) +
|
|
756
|
-
dim(" · By ") +
|
|
757
|
-
chalk.white.bold("Ken Kai"));
|
|
758
|
-
console.log(` ${gradientLine(LOGO[1])}${GAP}` + accent("Doctor"));
|
|
759
|
-
console.log(` ${gradientLine(LOGO[2])}${GAP}` + dim("Diagnose & Fix"));
|
|
760
|
-
console.log();
|
|
761
|
-
const home = os.homedir();
|
|
762
|
-
const ggDir = path.join(home, ".gg");
|
|
763
|
-
const authFile = path.join(ggDir, "auth.json");
|
|
764
|
-
const lockFile = authFile + ".lock";
|
|
765
|
-
const myUid = process.getuid();
|
|
766
|
-
let fixed = 0;
|
|
767
|
-
// ── Environment ─────────────────────────────────────────────
|
|
768
|
-
console.log(accent(" Environment\n"));
|
|
769
|
-
console.log(dim(` Home: ${home}`));
|
|
770
|
-
console.log(dim(` $HOME: ${process.env.HOME ?? "(not set)"}`));
|
|
771
|
-
console.log(dim(` Node.js: ${process.version}`));
|
|
772
|
-
console.log(dim(` Platform: ${process.platform} ${process.arch}`));
|
|
773
|
-
console.log(dim(` UID: ${myUid} EUID: ${process.geteuid()}`));
|
|
774
|
-
if (process.env.HOME && process.env.HOME !== home) {
|
|
775
|
-
console.log(warn("\n ⚠ $HOME differs from os.homedir() — this can cause auth mismatches"));
|
|
776
|
-
}
|
|
777
|
-
if (myUid !== process.geteuid()) {
|
|
778
|
-
console.log(warn(" ⚠ uid ≠ euid — running with elevated privileges (sudo?)"));
|
|
779
|
-
console.log(dim(" Running ggcoder with sudo can cause ownership issues."));
|
|
780
|
-
console.log(dim(" Use without sudo, or fix after: sudo chown -R $(whoami) ~/.gg"));
|
|
781
|
-
}
|
|
782
|
-
console.log();
|
|
783
|
-
// ── Config Directory ────────────────────────────────────────
|
|
784
|
-
console.log(accent(" Config Directory\n"));
|
|
785
|
-
try {
|
|
786
|
-
const stat = await fsP.stat(ggDir);
|
|
787
|
-
const mode = stat.mode & 0o777;
|
|
788
|
-
console.log(dim(` Path: ${ggDir}`));
|
|
789
|
-
console.log(dim(` Mode: 0o${mode.toString(8)} UID: ${stat.uid}`));
|
|
790
|
-
// Fix ownership
|
|
791
|
-
if (stat.uid !== myUid) {
|
|
792
|
-
console.log(warn(` ⚠ Owned by uid ${stat.uid}, expected ${myUid}`));
|
|
793
|
-
try {
|
|
794
|
-
await fsP.chown(ggDir, myUid, process.getgid());
|
|
795
|
-
console.log(good(" ✓ Fixed directory ownership"));
|
|
796
|
-
fixed++;
|
|
797
|
-
}
|
|
798
|
-
catch {
|
|
799
|
-
console.log(bad(` ✗ Cannot fix — try: sudo chown -R $(whoami) ${ggDir}`));
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
// Fix permissions (should be 0o700)
|
|
803
|
-
if (mode !== 0o700) {
|
|
804
|
-
try {
|
|
805
|
-
await fsP.chmod(ggDir, 0o700);
|
|
806
|
-
console.log(good(" ✓ Fixed directory permissions → 0o700"));
|
|
807
|
-
fixed++;
|
|
808
|
-
}
|
|
809
|
-
catch {
|
|
810
|
-
console.log(bad(` ✗ Cannot fix — try: chmod 700 ${ggDir}`));
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
catch {
|
|
815
|
-
console.log(warn(` ${ggDir} missing — creating...`));
|
|
816
|
-
try {
|
|
817
|
-
await fsP.mkdir(ggDir, { recursive: true, mode: 0o700 });
|
|
818
|
-
console.log(good(` ✓ Created ${ggDir}`));
|
|
819
|
-
fixed++;
|
|
820
|
-
}
|
|
821
|
-
catch (mkErr) {
|
|
822
|
-
console.log(bad(` ✗ Cannot create: ${mkErr instanceof Error ? mkErr.message : String(mkErr)}`));
|
|
823
|
-
console.log();
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
console.log();
|
|
828
|
-
// ── Lock File ───────────────────────────────────────────────
|
|
829
|
-
try {
|
|
830
|
-
const lockStat = await fsP.stat(lockFile);
|
|
831
|
-
const ageMs = Date.now() - lockStat.mtimeMs;
|
|
832
|
-
console.log(accent(" Lock File\n"));
|
|
833
|
-
console.log(warn(` ⚠ Stale lock found (age: ${Math.round(ageMs / 1000)}s)`));
|
|
834
|
-
await fsP.unlink(lockFile);
|
|
835
|
-
console.log(good(" ✓ Removed"));
|
|
836
|
-
fixed++;
|
|
837
|
-
console.log();
|
|
838
|
-
}
|
|
839
|
-
catch {
|
|
840
|
-
// No lock file — good, skip section entirely
|
|
841
|
-
}
|
|
842
|
-
// ── Auth File ───────────────────────────────────────────────
|
|
843
|
-
console.log(accent(" Auth File\n"));
|
|
844
|
-
let authData = null;
|
|
845
|
-
let authNeedsRewrite = false;
|
|
846
|
-
try {
|
|
847
|
-
const stat = await fsP.stat(authFile);
|
|
848
|
-
const mode = stat.mode & 0o777;
|
|
849
|
-
console.log(dim(` Path: ${authFile}`));
|
|
850
|
-
console.log(dim(` Size: ${stat.size} bytes Mode: 0o${mode.toString(8)} UID: ${stat.uid}`));
|
|
851
|
-
// Fix ownership
|
|
852
|
-
if (stat.uid !== myUid) {
|
|
853
|
-
console.log(warn(` ⚠ Owned by uid ${stat.uid}, expected ${myUid}`));
|
|
854
|
-
try {
|
|
855
|
-
await fsP.chown(authFile, myUid, process.getgid());
|
|
856
|
-
console.log(good(" ✓ Fixed file ownership"));
|
|
857
|
-
fixed++;
|
|
858
|
-
}
|
|
859
|
-
catch {
|
|
860
|
-
console.log(bad(` ✗ Cannot fix — try: sudo chown $(whoami) ${authFile}`));
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
// Fix permissions (should be 0o600)
|
|
864
|
-
if (mode !== 0o600) {
|
|
865
|
-
try {
|
|
866
|
-
await fsP.chmod(authFile, 0o600);
|
|
867
|
-
console.log(good(" ✓ Fixed file permissions → 0o600"));
|
|
868
|
-
fixed++;
|
|
869
|
-
}
|
|
870
|
-
catch {
|
|
871
|
-
console.log(bad(` ✗ Cannot fix — try: chmod 600 ${authFile}`));
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
// Try to read and parse
|
|
875
|
-
try {
|
|
876
|
-
const content = await fsP.readFile(authFile, "utf-8");
|
|
877
|
-
try {
|
|
878
|
-
authData = JSON.parse(content);
|
|
879
|
-
}
|
|
880
|
-
catch {
|
|
881
|
-
console.log(bad(" ✗ Invalid JSON — backing up and resetting"));
|
|
882
|
-
const backupName = `auth.json.corrupt.${Date.now()}`;
|
|
883
|
-
await fsP.copyFile(authFile, path.join(ggDir, backupName));
|
|
884
|
-
await fsP.writeFile(authFile, "{}", { encoding: "utf-8", mode: 0o600 });
|
|
885
|
-
console.log(good(` ✓ Corrupt file backed up as ${backupName}`));
|
|
886
|
-
console.log(dim(' Run "ggcoder login" to re-authenticate'));
|
|
887
|
-
authData = {};
|
|
888
|
-
fixed++;
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
catch (readErr) {
|
|
892
|
-
const code = readErr.code;
|
|
893
|
-
if (code === "EACCES") {
|
|
894
|
-
console.log(bad(" ✗ Permission denied reading auth.json"));
|
|
895
|
-
console.log(dim(` Try: sudo chown $(whoami) ${authFile} && chmod 600 ${authFile}`));
|
|
896
|
-
}
|
|
897
|
-
else {
|
|
898
|
-
console.log(bad(` ✗ Read error: ${readErr instanceof Error ? readErr.message : String(readErr)}`));
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
catch {
|
|
903
|
-
console.log(dim(` Path: ${authFile}`));
|
|
904
|
-
console.log(warn(' Not found — run "ggcoder login" to authenticate'));
|
|
905
|
-
}
|
|
906
|
-
console.log();
|
|
907
|
-
// ── Credentials ─────────────────────────────────────────────
|
|
908
|
-
if (authData && Object.keys(authData).length > 0) {
|
|
909
|
-
console.log(accent(" Credentials\n"));
|
|
910
|
-
for (const p of Object.keys(authData)) {
|
|
911
|
-
const cred = authData[p];
|
|
912
|
-
if (!cred || typeof cred !== "object") {
|
|
913
|
-
console.log(bad(` ✗ ${p}: invalid entry — removing`));
|
|
914
|
-
delete authData[p];
|
|
915
|
-
authNeedsRewrite = true;
|
|
916
|
-
fixed++;
|
|
917
|
-
continue;
|
|
918
|
-
}
|
|
919
|
-
if (!cred.accessToken || typeof cred.accessToken !== "string") {
|
|
920
|
-
console.log(bad(` ✗ ${p}: missing accessToken — removing`));
|
|
921
|
-
delete authData[p];
|
|
922
|
-
authNeedsRewrite = true;
|
|
923
|
-
fixed++;
|
|
924
|
-
continue;
|
|
925
|
-
}
|
|
926
|
-
const token = String(cred.accessToken);
|
|
927
|
-
const masked = token.slice(0, 8) + "..." + token.slice(-4);
|
|
928
|
-
const expires = typeof cred.expiresAt === "number" ? new Date(cred.expiresAt).toISOString() : "unknown";
|
|
929
|
-
const expired = typeof cred.expiresAt === "number" && Date.now() > cred.expiresAt;
|
|
930
|
-
if (expired) {
|
|
931
|
-
console.log(warn(` ⚠ ${p}: ${masked} expired ${expires}`));
|
|
932
|
-
}
|
|
933
|
-
else {
|
|
934
|
-
console.log(good(` ✓ ${p}: ${masked} expires ${expires}`));
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
if (authNeedsRewrite) {
|
|
938
|
-
try {
|
|
939
|
-
await fsP.writeFile(authFile, JSON.stringify(authData, null, 2), {
|
|
940
|
-
encoding: "utf-8",
|
|
941
|
-
mode: 0o600,
|
|
942
|
-
});
|
|
943
|
-
console.log(good(" ✓ Cleaned up auth.json"));
|
|
944
|
-
}
|
|
945
|
-
catch {
|
|
946
|
-
console.log(bad(" ✗ Failed to write cleaned auth.json"));
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
console.log();
|
|
950
|
-
}
|
|
951
|
-
// ── Temp Files ──────────────────────────────────────────────
|
|
952
|
-
try {
|
|
953
|
-
const entries = await fsP.readdir(ggDir);
|
|
954
|
-
const tmpFiles = entries.filter((e) => e.startsWith("auth.json.") && e.endsWith(".tmp"));
|
|
955
|
-
if (tmpFiles.length > 0) {
|
|
956
|
-
console.log(accent(" Temp Files\n"));
|
|
957
|
-
console.log(warn(` ⚠ ${tmpFiles.length} orphaned temp file(s) from interrupted writes`));
|
|
958
|
-
for (const tmp of tmpFiles) {
|
|
959
|
-
await fsP.unlink(path.join(ggDir, tmp)).catch(() => { });
|
|
960
|
-
}
|
|
961
|
-
console.log(good(` ✓ Removed ${tmpFiles.length} file(s)`));
|
|
962
|
-
fixed++;
|
|
963
|
-
console.log();
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
catch {
|
|
967
|
-
// Can't read directory — already flagged above
|
|
968
|
-
}
|
|
969
|
-
// ── Summary ─────────────────────────────────────────────────
|
|
970
|
-
if (fixed > 0) {
|
|
971
|
-
console.log(good(` ✓ Fixed ${fixed} issue${fixed > 1 ? "s" : ""}.`));
|
|
972
|
-
}
|
|
973
|
-
else {
|
|
974
|
-
console.log(good(" ✓ Everything looks good."));
|
|
975
|
-
}
|
|
976
|
-
console.log();
|
|
977
|
-
}
|
|
978
|
-
// ── Logout ─────────────────────────────────────────────────
|
|
979
|
-
async function runLogout() {
|
|
980
|
-
const paths = await ensureAppDirs();
|
|
981
|
-
initLogger(paths.logFile, { version: CLI_VERSION });
|
|
982
|
-
log("INFO", "auth", "Logout requested");
|
|
983
|
-
const authStorage = new AuthStorage();
|
|
984
|
-
await authStorage.load();
|
|
985
|
-
await authStorage.clearAll();
|
|
986
|
-
log("INFO", "auth", "Logout succeeded");
|
|
987
|
-
closeLogger();
|
|
988
|
-
console.log(chalk.green("Logged out successfully."));
|
|
989
|
-
}
|
|
990
601
|
// ── Sessions ──────────────────────────────────────────────
|
|
991
602
|
async function runSessions() {
|
|
992
603
|
requireInteractiveTTY();
|
|
@@ -1016,7 +627,7 @@ async function runSessions() {
|
|
|
1016
627
|
return "MiniMax-M2.7";
|
|
1017
628
|
if (p === "deepseek")
|
|
1018
629
|
return "deepseek-v4-pro";
|
|
1019
|
-
return "claude-opus-4-
|
|
630
|
+
return "claude-opus-4-8";
|
|
1020
631
|
}
|
|
1021
632
|
const model = saved2.model ?? getDefault(provider);
|
|
1022
633
|
const thinkingLevel = saved2.thinkingEnabled
|
|
@@ -1248,32 +859,16 @@ async function runAgentHomeLogin() {
|
|
|
1248
859
|
log("INFO", "agent-home", "Agent Home login started");
|
|
1249
860
|
const existing = await loadAgentHomeConfig();
|
|
1250
861
|
// Banner
|
|
1251
|
-
const LOGO =
|
|
1252
|
-
" \u2584\u2580\u2580\u2580 \u2584\u2580\u2580\u2580",
|
|
1253
|
-
" \u2588 \u2580\u2588 \u2588 \u2580\u2588",
|
|
1254
|
-
" \u2580\u2584\u2584\u2580 \u2580\u2584\u2584\u2580",
|
|
1255
|
-
];
|
|
1256
|
-
function gradientTextLocal(text) {
|
|
1257
|
-
let colorIdx = 0;
|
|
1258
|
-
return text
|
|
1259
|
-
.split("")
|
|
1260
|
-
.map((ch) => {
|
|
1261
|
-
if (ch === " ")
|
|
1262
|
-
return ch;
|
|
1263
|
-
const color = GRADIENT[colorIdx++ % GRADIENT.length];
|
|
1264
|
-
return chalk.hex(color)(ch);
|
|
1265
|
-
})
|
|
1266
|
-
.join("");
|
|
1267
|
-
}
|
|
862
|
+
const LOGO = LOGO_LINES;
|
|
1268
863
|
const GAP = " ";
|
|
1269
864
|
console.log();
|
|
1270
|
-
console.log(` ${
|
|
865
|
+
console.log(` ${gradientLine(LOGO[0])}${GAP}` +
|
|
1271
866
|
chalk.hex("#60a5fa").bold("GG Coder") +
|
|
1272
867
|
chalk.hex("#6b7280")(` v${CLI_VERSION}`) +
|
|
1273
868
|
chalk.hex("#6b7280")(" \u00b7 By ") +
|
|
1274
869
|
chalk.white.bold("Ken Kai"));
|
|
1275
|
-
console.log(` ${
|
|
1276
|
-
console.log(` ${
|
|
870
|
+
console.log(` ${gradientLine(LOGO[1])}${GAP}` + chalk.hex("#a78bfa")("Agent Home Setup"));
|
|
871
|
+
console.log(` ${gradientLine(LOGO[2])}${GAP}` + chalk.hex("#6b7280")("Remote Control via iOS"));
|
|
1277
872
|
console.log();
|
|
1278
873
|
if (existing) {
|
|
1279
874
|
console.log(chalk.hex("#6b7280")(" Current config:\n") +
|
|
@@ -1355,105 +950,6 @@ async function runAgentHome() {
|
|
|
1355
950
|
});
|
|
1356
951
|
}
|
|
1357
952
|
// ── Pixel ──────────────────────────────────────────────────
|
|
1358
|
-
async function runPixel() {
|
|
1359
|
-
const sub = process.argv[3];
|
|
1360
|
-
const rest = process.argv.slice(4);
|
|
1361
|
-
if (sub === "install") {
|
|
1362
|
-
const { runPixelInstall } = await import("./core/pixel.js");
|
|
1363
|
-
const opts = parsePixelInstallArgs(rest);
|
|
1364
|
-
await runPixelInstall(opts);
|
|
1365
|
-
return;
|
|
1366
|
-
}
|
|
1367
|
-
if (sub === "fix") {
|
|
1368
|
-
const errorId = rest[0];
|
|
1369
|
-
if (!errorId) {
|
|
1370
|
-
process.stderr.write("Usage: ggcoder pixel fix <error_id>\n");
|
|
1371
|
-
process.exit(1);
|
|
1372
|
-
}
|
|
1373
|
-
const { fixError } = await import("./core/pixel-fix.js");
|
|
1374
|
-
const result = await fixError(errorId);
|
|
1375
|
-
if (result.outcome === "awaiting_review") {
|
|
1376
|
-
console.log(chalk.hex("#4ade80")(`✓ ${result.reason}`));
|
|
1377
|
-
}
|
|
1378
|
-
else {
|
|
1379
|
-
console.log(chalk.hex("#ef4444")(`✗ ${result.reason}`));
|
|
1380
|
-
process.exit(1);
|
|
1381
|
-
}
|
|
1382
|
-
return;
|
|
1383
|
-
}
|
|
1384
|
-
if (sub === "run") {
|
|
1385
|
-
const { runQueue } = await import("./core/pixel-fix.js");
|
|
1386
|
-
const result = await runQueue();
|
|
1387
|
-
console.log(chalk.bold(`${result.fixed} fixed · ${result.failed} failed · ${result.total} total`));
|
|
1388
|
-
if (result.failed > 0)
|
|
1389
|
-
process.exit(1);
|
|
1390
|
-
return;
|
|
1391
|
-
}
|
|
1392
|
-
if (sub === "--help" || sub === "-h") {
|
|
1393
|
-
printPixelHelp();
|
|
1394
|
-
return;
|
|
1395
|
-
}
|
|
1396
|
-
if (sub === "list") {
|
|
1397
|
-
const { listAllErrors } = await import("./core/pixel.js");
|
|
1398
|
-
await listAllErrors();
|
|
1399
|
-
return;
|
|
1400
|
-
}
|
|
1401
|
-
if (sub) {
|
|
1402
|
-
process.stderr.write(`Unknown pixel subcommand: ${sub}\n`);
|
|
1403
|
-
printPixelHelp();
|
|
1404
|
-
process.exit(1);
|
|
1405
|
-
}
|
|
1406
|
-
// No subcommand → launch the Ink TUI with the pixel overlay open. The fix
|
|
1407
|
-
// flow runs through the same agent loop, streaming live in the chat instead
|
|
1408
|
-
// of spawning a subprocess.
|
|
1409
|
-
// Non-TTY (CI, piped) → fall back to text list.
|
|
1410
|
-
if (!process.stdin.isTTY) {
|
|
1411
|
-
const { listAllErrors } = await import("./core/pixel.js");
|
|
1412
|
-
await listAllErrors();
|
|
1413
|
-
return;
|
|
1414
|
-
}
|
|
1415
|
-
const saved = loadSavedSettings();
|
|
1416
|
-
const provider = saved.provider ?? "anthropic";
|
|
1417
|
-
const model = saved.model ?? defaultModelFor(provider);
|
|
1418
|
-
await runInkTUI({
|
|
1419
|
-
provider,
|
|
1420
|
-
model,
|
|
1421
|
-
cwd: process.cwd(),
|
|
1422
|
-
thinkingLevel: saved.thinkingEnabled ? (saved.thinkingLevel ?? "medium") : undefined,
|
|
1423
|
-
theme: saved.theme,
|
|
1424
|
-
initialOverlay: "pixel",
|
|
1425
|
-
});
|
|
1426
|
-
}
|
|
1427
|
-
function defaultModelFor(p) {
|
|
1428
|
-
return getDefaultModel(p).id;
|
|
1429
|
-
}
|
|
1430
|
-
function parsePixelInstallArgs(args) {
|
|
1431
|
-
const out = { skipPackageInstall: false };
|
|
1432
|
-
for (let i = 0; i < args.length; i++) {
|
|
1433
|
-
const a = args[i];
|
|
1434
|
-
if (a === "--ingest-url")
|
|
1435
|
-
out.ingestUrl = args[++i];
|
|
1436
|
-
else if (a === "--name")
|
|
1437
|
-
out.name = args[++i];
|
|
1438
|
-
else if (a === "--skip-install")
|
|
1439
|
-
out.skipPackageInstall = true;
|
|
1440
|
-
}
|
|
1441
|
-
return out;
|
|
1442
|
-
}
|
|
1443
|
-
function printPixelHelp() {
|
|
1444
|
-
console.log(`ggcoder pixel — error tracking + auto-fix queue
|
|
1445
|
-
|
|
1446
|
-
Usage:
|
|
1447
|
-
ggcoder pixel List open errors across every registered project
|
|
1448
|
-
ggcoder pixel install Register the current project and wire up the SDK
|
|
1449
|
-
ggcoder pixel fix <error_id> Fix one specific error end-to-end
|
|
1450
|
-
ggcoder pixel run Auto-fix every open error across all projects
|
|
1451
|
-
|
|
1452
|
-
ggcoder pixel install --name <name> Override the project name
|
|
1453
|
-
ggcoder pixel install --ingest-url <url> Use a custom backend URL
|
|
1454
|
-
ggcoder pixel install --skip-install Don't run the package manager
|
|
1455
|
-
`);
|
|
1456
|
-
}
|
|
1457
953
|
// ── Helpers ────────────────────────────────────────────────
|
|
1458
954
|
/**
|
|
1459
955
|
* Pick the provider/model to start with. If the preferred provider isn't
|
|
@@ -1497,25 +993,6 @@ async function resolveActiveProvider(authStorage, preferred, savedModel) {
|
|
|
1497
993
|
const provider = loggedInProviders[0];
|
|
1498
994
|
return { provider, model: getDefaultModel(provider).id, loggedInProviders };
|
|
1499
995
|
}
|
|
1500
|
-
function displayName(provider) {
|
|
1501
|
-
if (provider === "anthropic")
|
|
1502
|
-
return "Anthropic";
|
|
1503
|
-
if (provider === "xiaomi")
|
|
1504
|
-
return "Xiaomi (MiMo)";
|
|
1505
|
-
if (provider === "gemini")
|
|
1506
|
-
return "Gemini";
|
|
1507
|
-
if (provider === "glm")
|
|
1508
|
-
return "Z.AI (GLM)";
|
|
1509
|
-
if (provider === "moonshot")
|
|
1510
|
-
return "Moonshot";
|
|
1511
|
-
if (provider === "minimax")
|
|
1512
|
-
return "MiniMax";
|
|
1513
|
-
if (provider === "deepseek")
|
|
1514
|
-
return "DeepSeek";
|
|
1515
|
-
if (provider === "openrouter")
|
|
1516
|
-
return "OpenRouter";
|
|
1517
|
-
return "OpenAI";
|
|
1518
|
-
}
|
|
1519
996
|
function extractText(content) {
|
|
1520
997
|
if (typeof content === "string")
|
|
1521
998
|
return content;
|
|
@@ -1743,12 +1220,6 @@ export function messagesToHistoryItems(msgs) {
|
|
|
1743
1220
|
});
|
|
1744
1221
|
return items;
|
|
1745
1222
|
}
|
|
1746
|
-
function openBrowser(url) {
|
|
1747
|
-
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1748
|
-
execFile(cmd, [url], () => {
|
|
1749
|
-
// Ignore errors — user can copy URL manually
|
|
1750
|
-
});
|
|
1751
|
-
}
|
|
1752
1223
|
if (process.argv[1] &&
|
|
1753
1224
|
fileURLToPath(import.meta.url) === fs.realpathSync(path.resolve(process.argv[1]))) {
|
|
1754
1225
|
main();
|