@gxp-dev/tools 2.0.63 → 2.0.65
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 +32 -31
- package/bin/gx-devtools.js +74 -54
- package/bin/lib/cli.js +23 -21
- package/bin/lib/commands/add-dependency.js +366 -325
- package/bin/lib/commands/assets.js +137 -139
- package/bin/lib/commands/build.js +169 -174
- package/bin/lib/commands/datastore.js +181 -183
- package/bin/lib/commands/dev.js +127 -131
- package/bin/lib/commands/extensions.js +147 -149
- package/bin/lib/commands/extract-config.js +73 -67
- package/bin/lib/commands/index.js +12 -12
- package/bin/lib/commands/init.js +342 -240
- package/bin/lib/commands/publish.js +69 -75
- package/bin/lib/commands/socket.js +69 -69
- package/bin/lib/commands/ssl.js +14 -14
- package/bin/lib/constants.js +10 -24
- package/bin/lib/tui/App.tsx +761 -705
- package/bin/lib/tui/components/AIPanel.tsx +191 -171
- package/bin/lib/tui/components/CommandInput.tsx +394 -343
- package/bin/lib/tui/components/GeminiPanel.tsx +175 -151
- package/bin/lib/tui/components/Header.tsx +23 -21
- package/bin/lib/tui/components/LogPanel.tsx +244 -220
- package/bin/lib/tui/components/TabBar.tsx +50 -48
- package/bin/lib/tui/components/WelcomeScreen.tsx +126 -71
- package/bin/lib/tui/index.tsx +37 -39
- package/bin/lib/tui/services/AIService.ts +518 -462
- package/bin/lib/tui/services/ExtensionService.ts +140 -129
- package/bin/lib/tui/services/GeminiService.ts +367 -337
- package/bin/lib/tui/services/ServiceManager.ts +344 -322
- package/bin/lib/tui/services/SocketService.ts +168 -168
- package/bin/lib/tui/services/ViteService.ts +88 -88
- package/bin/lib/tui/services/index.ts +47 -22
- package/bin/lib/utils/ai-scaffold.js +291 -280
- package/bin/lib/utils/extract-config.js +157 -140
- package/bin/lib/utils/files.js +82 -86
- package/bin/lib/utils/index.js +7 -7
- package/bin/lib/utils/paths.js +34 -34
- package/bin/lib/utils/prompts.js +194 -169
- package/bin/lib/utils/ssl.js +79 -81
- package/browser-extensions/README.md +0 -1
- package/browser-extensions/chrome/background.js +244 -237
- package/browser-extensions/chrome/content.js +32 -29
- package/browser-extensions/chrome/devtools.html +7 -7
- package/browser-extensions/chrome/devtools.js +19 -19
- package/browser-extensions/chrome/inspector.js +802 -767
- package/browser-extensions/chrome/manifest.json +71 -63
- package/browser-extensions/chrome/panel.html +674 -636
- package/browser-extensions/chrome/panel.js +722 -712
- package/browser-extensions/chrome/popup.html +586 -543
- package/browser-extensions/chrome/popup.js +282 -244
- package/browser-extensions/chrome/rules.json +1 -1
- package/browser-extensions/chrome/test-chrome.html +216 -136
- package/browser-extensions/chrome/test-mixed-content.html +284 -189
- package/browser-extensions/chrome/test-uri-pattern.html +221 -198
- package/browser-extensions/firefox/README.md +9 -6
- package/browser-extensions/firefox/background.js +221 -218
- package/browser-extensions/firefox/content.js +55 -52
- package/browser-extensions/firefox/debug-errors.html +386 -228
- package/browser-extensions/firefox/debug-https.html +153 -105
- package/browser-extensions/firefox/devtools.html +7 -7
- package/browser-extensions/firefox/devtools.js +23 -20
- package/browser-extensions/firefox/inspector.js +802 -767
- package/browser-extensions/firefox/manifest.json +68 -68
- package/browser-extensions/firefox/panel.html +674 -636
- package/browser-extensions/firefox/panel.js +722 -712
- package/browser-extensions/firefox/popup.html +572 -535
- package/browser-extensions/firefox/popup.js +281 -236
- package/browser-extensions/firefox/test-gramercy.html +170 -125
- package/browser-extensions/firefox/test-imports.html +59 -55
- package/browser-extensions/firefox/test-masking.html +231 -140
- package/browser-extensions/firefox/test-uri-pattern.html +221 -198
- package/dist/tui/App.d.ts +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +154 -150
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/components/AIPanel.d.ts.map +1 -1
- package/dist/tui/components/AIPanel.js +42 -35
- package/dist/tui/components/AIPanel.js.map +1 -1
- package/dist/tui/components/CommandInput.d.ts +1 -1
- package/dist/tui/components/CommandInput.d.ts.map +1 -1
- package/dist/tui/components/CommandInput.js +92 -62
- package/dist/tui/components/CommandInput.js.map +1 -1
- package/dist/tui/components/GeminiPanel.d.ts.map +1 -1
- package/dist/tui/components/GeminiPanel.js +37 -30
- package/dist/tui/components/GeminiPanel.js.map +1 -1
- package/dist/tui/components/Header.d.ts.map +1 -1
- package/dist/tui/components/Header.js +1 -1
- package/dist/tui/components/Header.js.map +1 -1
- package/dist/tui/components/LogPanel.d.ts +1 -1
- package/dist/tui/components/LogPanel.d.ts.map +1 -1
- package/dist/tui/components/LogPanel.js +26 -24
- package/dist/tui/components/LogPanel.js.map +1 -1
- package/dist/tui/components/TabBar.d.ts +2 -2
- package/dist/tui/components/TabBar.d.ts.map +1 -1
- package/dist/tui/components/TabBar.js +11 -11
- package/dist/tui/components/TabBar.js.map +1 -1
- package/dist/tui/components/WelcomeScreen.d.ts.map +1 -1
- package/dist/tui/components/WelcomeScreen.js +6 -6
- package/dist/tui/components/WelcomeScreen.js.map +1 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +8 -8
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/services/AIService.d.ts +2 -2
- package/dist/tui/services/AIService.d.ts.map +1 -1
- package/dist/tui/services/AIService.js +165 -125
- package/dist/tui/services/AIService.js.map +1 -1
- package/dist/tui/services/ExtensionService.d.ts +1 -1
- package/dist/tui/services/ExtensionService.d.ts.map +1 -1
- package/dist/tui/services/ExtensionService.js +33 -26
- package/dist/tui/services/ExtensionService.js.map +1 -1
- package/dist/tui/services/GeminiService.d.ts +1 -1
- package/dist/tui/services/GeminiService.d.ts.map +1 -1
- package/dist/tui/services/GeminiService.js +87 -76
- package/dist/tui/services/GeminiService.js.map +1 -1
- package/dist/tui/services/ServiceManager.d.ts +3 -3
- package/dist/tui/services/ServiceManager.d.ts.map +1 -1
- package/dist/tui/services/ServiceManager.js +72 -58
- package/dist/tui/services/ServiceManager.js.map +1 -1
- package/dist/tui/services/SocketService.d.ts.map +1 -1
- package/dist/tui/services/SocketService.js +32 -32
- package/dist/tui/services/SocketService.js.map +1 -1
- package/dist/tui/services/ViteService.d.ts.map +1 -1
- package/dist/tui/services/ViteService.js +26 -28
- package/dist/tui/services/ViteService.js.map +1 -1
- package/dist/tui/services/index.d.ts +6 -6
- package/dist/tui/services/index.d.ts.map +1 -1
- package/dist/tui/services/index.js +6 -6
- package/dist/tui/services/index.js.map +1 -1
- package/mcp/gxp-api-server.js +83 -81
- package/package.json +109 -93
- package/runtime/PortalContainer.vue +258 -234
- package/runtime/dev-tools/DevToolsModal.vue +153 -155
- package/runtime/dev-tools/LayoutSwitcher.vue +144 -140
- package/runtime/dev-tools/MockDataEditor.vue +456 -433
- package/runtime/dev-tools/SocketSimulator.vue +379 -371
- package/runtime/dev-tools/StoreInspector.vue +517 -455
- package/runtime/dev-tools/index.js +5 -5
- package/runtime/fallback-layouts/PrivateLayout.vue +2 -2
- package/runtime/fallback-layouts/PublicLayout.vue +2 -2
- package/runtime/fallback-layouts/SystemLayout.vue +2 -2
- package/runtime/gxpStringsPlugin.js +159 -134
- package/runtime/index.html +17 -19
- package/runtime/main.js +24 -22
- package/runtime/mock-api/auth-middleware.js +15 -15
- package/runtime/mock-api/image-generator.js +46 -46
- package/runtime/mock-api/index.js +55 -55
- package/runtime/mock-api/response-generator.js +116 -105
- package/runtime/mock-api/route-generator.js +107 -84
- package/runtime/mock-api/socket-triggers.js +94 -93
- package/runtime/mock-api/spec-loader.js +79 -80
- package/runtime/package.json +3 -0
- package/runtime/server.js +68 -68
- package/runtime/stores/gxpPortalConfigStore.js +204 -186
- package/runtime/stores/index.js +2 -2
- package/runtime/vite-inspector-plugin.js +858 -707
- package/runtime/vite-source-tracker-plugin.js +132 -113
- package/runtime/vite.config.js +191 -139
- package/scripts/launch-chrome.js +41 -41
- package/scripts/pack-chrome.js +38 -39
- package/socket-events/AiSessionMessageCreated.json +17 -17
- package/socket-events/SocialStreamPostCreated.json +23 -23
- package/socket-events/SocialStreamPostVariantCompleted.json +22 -22
- package/template/.claude/agents/gxp-developer.md +100 -99
- package/template/.claude/settings.json +7 -7
- package/template/AGENTS.md +30 -23
- package/template/GEMINI.md +20 -20
- package/template/README.md +70 -53
- package/template/app-manifest.json +2 -4
- package/template/configuration.json +10 -10
- package/template/default-styling.css +1 -1
- package/template/index.html +18 -20
- package/template/main.js +24 -22
- package/template/src/DemoPage.vue +415 -362
- package/template/src/Plugin.vue +76 -85
- package/template/src/stores/index.js +3 -3
- package/template/src/stores/test-data.json +164 -172
- package/template/theme-layouts/AdditionalStyling.css +50 -50
- package/template/theme-layouts/PrivateLayout.vue +8 -12
- package/template/theme-layouts/PublicLayout.vue +8 -12
- package/template/theme-layouts/SystemLayout.vue +8 -12
- package/template/vite.extend.js +45 -0
- package/template/vite.config.js +0 -409
package/dist/tui/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { render } from
|
|
4
|
-
import dotenv from
|
|
5
|
-
import path from
|
|
6
|
-
import App from
|
|
7
|
-
import { serviceManager } from
|
|
3
|
+
import { render } from "ink";
|
|
4
|
+
import dotenv from "dotenv";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import App from "./App.js";
|
|
7
|
+
import { serviceManager } from "./services/index.js";
|
|
8
8
|
// Load .env from the current working directory (project directory)
|
|
9
|
-
dotenv.config({ path: path.join(process.cwd(),
|
|
9
|
+
dotenv.config({ path: path.join(process.cwd(), ".env") });
|
|
10
10
|
export function startTUI(options = {}) {
|
|
11
11
|
// Check if stdin supports raw mode (required for Ink input handling)
|
|
12
12
|
// This can fail when:
|
|
@@ -18,7 +18,7 @@ export function startTUI(options = {}) {
|
|
|
18
18
|
// If stdin doesn't support raw mode, we need to handle it gracefully
|
|
19
19
|
// Ink 5.x uses stdin by default and requires raw mode for input
|
|
20
20
|
if (!stdinIsTTY) {
|
|
21
|
-
throw new Error(
|
|
21
|
+
throw new Error("NO_TTY");
|
|
22
22
|
}
|
|
23
23
|
const { waitUntilExit } = render(_jsx(App, { autoStart: options.autoStart, args: options.args }));
|
|
24
24
|
waitUntilExit().then(() => {
|
|
@@ -28,7 +28,7 @@ export function startTUI(options = {}) {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
// Check if run directly (ESM way)
|
|
31
|
-
import { fileURLToPath } from
|
|
31
|
+
import { fileURLToPath } from "url";
|
|
32
32
|
const isMain = process.argv[1] && process.argv[1] === fileURLToPath(import.meta.url);
|
|
33
33
|
if (isMain) {
|
|
34
34
|
startTUI();
|
package/dist/tui/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../bin/lib/tui/index.tsx"],"names":[],"mappings":";;AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../bin/lib/tui/index.tsx"],"names":[],"mappings":";;AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,GAAG,MAAM,UAAU,CAAA;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,mEAAmE;AACnE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;AAOzD,MAAM,UAAU,QAAQ,CAAC,UAAsB,EAAE;IAChD,qEAAqE;IACrE,sBAAsB;IACtB,+BAA+B;IAC/B,qCAAqC;IACrC,4BAA4B;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAA;IAC3B,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,KAAK,IAAI,CAAA;IAEvC,qEAAqE;IACrE,gEAAgE;IAChE,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC1B,CAAC;IAED,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAC/B,KAAC,GAAG,IAAC,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,GAAI,CACzD,CAAA;IAED,aAAa,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;QACzB,iDAAiD;QACjD,cAAc,CAAC,YAAY,EAAE,CAAA;QAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAChB,CAAC,CAAC,CAAA;AACH,CAAC;AAED,kCAAkC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAA;AACnC,MAAM,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACtE,IAAI,MAAM,EAAE,CAAC;IACZ,QAAQ,EAAE,CAAA;AACX,CAAC;AAED,eAAe,QAAQ,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIService.d.ts","sourceRoot":"","sources":["../../../bin/lib/tui/services/AIService.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"AIService.d.ts","sourceRoot":"","sources":["../../../bin/lib/tui/services/AIService.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAGrC,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAA;AAEtD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,UAAU,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAGD,MAAM,WAAW,QAAQ;IACxB,QAAQ,EAAE,UAAU,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,OAAO,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;CACxB;AAsBD,wBAAgB,YAAY,IAAI,QAAQ,CAiBvC;AAGD,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAInD;AAaD,wBAAgB,qBAAqB,IAAI,cAAc,EAAE,CAiFxD;AAGD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAelE;AAGD,qBAAa,SAAU,SAAQ,YAAY;IAC1C,OAAO,CAAC,mBAAmB,CAA+C;IAC1E,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,eAAe,CAAY;IACnC,OAAO,CAAC,YAAY,CAAC,CAAQ;;IAgB7B,WAAW,IAAI,UAAU;IAKzB,WAAW,CAAC,QAAQ,EAAE,UAAU,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IA8BxE,WAAW,IAAI,OAAO;IAOtB,eAAe,IAAI,cAAc,GAAG,SAAS;IAM7C,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IA4B/B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAsBrC,cAAc;YA0Cd,aAAa;YA0Cb,cAAc;YAoBd,iBAAiB;YAmCjB,iBAAiB;YAmCjB,oBAAoB;IA8ElC,iBAAiB,IAAI,IAAI;IAKzB,sBAAsB,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAGlE;AAGD,eAAO,MAAM,SAAS,WAAkB,CAAA"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import { spawn, execSync } from
|
|
4
|
-
import { EventEmitter } from
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { spawn, execSync } from "child_process";
|
|
4
|
+
import { EventEmitter } from "events";
|
|
5
5
|
// Get the gxdev config directory
|
|
6
6
|
function getConfigDir() {
|
|
7
|
-
const home = process.env.HOME || process.env.USERPROFILE ||
|
|
8
|
-
return path.join(home,
|
|
7
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
8
|
+
return path.join(home, ".gxdev");
|
|
9
9
|
}
|
|
10
10
|
// Ensure config directory exists
|
|
11
11
|
function ensureConfigDir() {
|
|
@@ -16,14 +16,14 @@ function ensureConfigDir() {
|
|
|
16
16
|
}
|
|
17
17
|
// Get AI config file path
|
|
18
18
|
function getAIConfigPath() {
|
|
19
|
-
return path.join(getConfigDir(),
|
|
19
|
+
return path.join(getConfigDir(), "ai-config.json");
|
|
20
20
|
}
|
|
21
21
|
// Load AI config
|
|
22
22
|
export function loadAIConfig() {
|
|
23
23
|
try {
|
|
24
24
|
const configPath = getAIConfigPath();
|
|
25
25
|
if (fs.existsSync(configPath)) {
|
|
26
|
-
const content = fs.readFileSync(configPath,
|
|
26
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
27
27
|
return JSON.parse(content);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -31,8 +31,8 @@ export function loadAIConfig() {
|
|
|
31
31
|
// Invalid or missing config file
|
|
32
32
|
}
|
|
33
33
|
return {
|
|
34
|
-
provider:
|
|
35
|
-
systemPrompt:
|
|
34
|
+
provider: "claude", // Default to Claude
|
|
35
|
+
systemPrompt: "You are a helpful assistant for GxP plugin development. Help the user build Vue.js components for the GxP kiosk platform.",
|
|
36
36
|
projectContext: true,
|
|
37
37
|
maxContextTokens: 4000,
|
|
38
38
|
};
|
|
@@ -46,7 +46,7 @@ export function saveAIConfig(config) {
|
|
|
46
46
|
// Check if a command exists
|
|
47
47
|
function commandExists(cmd) {
|
|
48
48
|
try {
|
|
49
|
-
execSync(`which ${cmd}`, { stdio:
|
|
49
|
+
execSync(`which ${cmd}`, { stdio: "pipe" });
|
|
50
50
|
return true;
|
|
51
51
|
}
|
|
52
52
|
catch {
|
|
@@ -57,81 +57,83 @@ function commandExists(cmd) {
|
|
|
57
57
|
export function getAvailableProviders() {
|
|
58
58
|
const providers = [];
|
|
59
59
|
// Check Claude CLI
|
|
60
|
-
if (commandExists(
|
|
60
|
+
if (commandExists("claude")) {
|
|
61
61
|
providers.push({
|
|
62
|
-
id:
|
|
63
|
-
name:
|
|
62
|
+
id: "claude",
|
|
63
|
+
name: "Claude",
|
|
64
64
|
available: true,
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
else {
|
|
68
68
|
providers.push({
|
|
69
|
-
id:
|
|
70
|
-
name:
|
|
69
|
+
id: "claude",
|
|
70
|
+
name: "Claude",
|
|
71
71
|
available: false,
|
|
72
|
-
reason:
|
|
72
|
+
reason: "Install: npm i -g @anthropic-ai/claude-code && claude login",
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
// Check Codex CLI
|
|
76
|
-
if (commandExists(
|
|
76
|
+
if (commandExists("codex")) {
|
|
77
77
|
providers.push({
|
|
78
|
-
id:
|
|
79
|
-
name:
|
|
78
|
+
id: "codex",
|
|
79
|
+
name: "Codex",
|
|
80
80
|
available: true,
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
else {
|
|
84
84
|
providers.push({
|
|
85
|
-
id:
|
|
86
|
-
name:
|
|
85
|
+
id: "codex",
|
|
86
|
+
name: "Codex",
|
|
87
87
|
available: false,
|
|
88
|
-
reason:
|
|
88
|
+
reason: "Install: npm i -g @openai/codex && codex auth",
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
// Check Gemini (CLI, API key, or gcloud)
|
|
92
|
-
if (commandExists(
|
|
92
|
+
if (commandExists("gemini")) {
|
|
93
93
|
providers.push({
|
|
94
|
-
id:
|
|
95
|
-
name:
|
|
94
|
+
id: "gemini",
|
|
95
|
+
name: "Gemini",
|
|
96
96
|
available: true,
|
|
97
|
-
method:
|
|
97
|
+
method: "cli",
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
else if (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) {
|
|
101
101
|
providers.push({
|
|
102
|
-
id:
|
|
103
|
-
name:
|
|
102
|
+
id: "gemini",
|
|
103
|
+
name: "Gemini",
|
|
104
104
|
available: true,
|
|
105
|
-
method:
|
|
105
|
+
method: "api_key",
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
|
-
else if (commandExists(
|
|
108
|
+
else if (commandExists("gcloud")) {
|
|
109
109
|
try {
|
|
110
|
-
const authList = execSync("gcloud auth list --format='value(account)'", {
|
|
110
|
+
const authList = execSync("gcloud auth list --format='value(account)'", {
|
|
111
|
+
stdio: "pipe",
|
|
112
|
+
}).toString();
|
|
111
113
|
if (authList.trim()) {
|
|
112
114
|
providers.push({
|
|
113
|
-
id:
|
|
114
|
-
name:
|
|
115
|
+
id: "gemini",
|
|
116
|
+
name: "Gemini",
|
|
115
117
|
available: true,
|
|
116
|
-
method:
|
|
118
|
+
method: "gcloud",
|
|
117
119
|
});
|
|
118
120
|
}
|
|
119
121
|
}
|
|
120
122
|
catch {
|
|
121
123
|
providers.push({
|
|
122
|
-
id:
|
|
123
|
-
name:
|
|
124
|
+
id: "gemini",
|
|
125
|
+
name: "Gemini",
|
|
124
126
|
available: false,
|
|
125
|
-
reason:
|
|
127
|
+
reason: "Install: npm i -g @google/gemini-cli && gemini",
|
|
126
128
|
});
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
else {
|
|
130
132
|
providers.push({
|
|
131
|
-
id:
|
|
132
|
-
name:
|
|
133
|
+
id: "gemini",
|
|
134
|
+
name: "Gemini",
|
|
133
135
|
available: false,
|
|
134
|
-
reason:
|
|
136
|
+
reason: "Install: npm i -g @google/gemini-cli && gemini",
|
|
135
137
|
});
|
|
136
138
|
}
|
|
137
139
|
return providers;
|
|
@@ -143,11 +145,11 @@ export function getProviderStatus(provider) {
|
|
|
143
145
|
}
|
|
144
146
|
if (provider.method) {
|
|
145
147
|
switch (provider.method) {
|
|
146
|
-
case
|
|
148
|
+
case "cli":
|
|
147
149
|
return `${provider.name} (CLI)`;
|
|
148
|
-
case
|
|
150
|
+
case "api_key":
|
|
149
151
|
return `${provider.name} (API key)`;
|
|
150
|
-
case
|
|
152
|
+
case "gcloud":
|
|
151
153
|
return `${provider.name} (gcloud)`;
|
|
152
154
|
}
|
|
153
155
|
}
|
|
@@ -158,12 +160,12 @@ export class AIService extends EventEmitter {
|
|
|
158
160
|
constructor() {
|
|
159
161
|
super();
|
|
160
162
|
this.conversationHistory = [];
|
|
161
|
-
this.projectContext =
|
|
163
|
+
this.projectContext = "";
|
|
162
164
|
const config = loadAIConfig();
|
|
163
165
|
this.currentProvider = config.provider;
|
|
164
166
|
// Determine gemini method if that's the current provider
|
|
165
167
|
const providers = getAvailableProviders();
|
|
166
|
-
const geminiProvider = providers.find(p => p.id ===
|
|
168
|
+
const geminiProvider = providers.find((p) => p.id === "gemini");
|
|
167
169
|
if (geminiProvider?.available) {
|
|
168
170
|
this.geminiMethod = geminiProvider.method;
|
|
169
171
|
}
|
|
@@ -175,15 +177,18 @@ export class AIService extends EventEmitter {
|
|
|
175
177
|
// Set current provider
|
|
176
178
|
setProvider(provider) {
|
|
177
179
|
const providers = getAvailableProviders();
|
|
178
|
-
const providerInfo = providers.find(p => p.id === provider);
|
|
180
|
+
const providerInfo = providers.find((p) => p.id === provider);
|
|
179
181
|
if (!providerInfo) {
|
|
180
182
|
return { success: false, message: `Unknown provider: ${provider}` };
|
|
181
183
|
}
|
|
182
184
|
if (!providerInfo.available) {
|
|
183
|
-
return {
|
|
185
|
+
return {
|
|
186
|
+
success: false,
|
|
187
|
+
message: `${providerInfo.name} is not available. ${providerInfo.reason || ""}`,
|
|
188
|
+
};
|
|
184
189
|
}
|
|
185
190
|
this.currentProvider = provider;
|
|
186
|
-
if (provider ===
|
|
191
|
+
if (provider === "gemini") {
|
|
187
192
|
this.geminiMethod = providerInfo.method;
|
|
188
193
|
}
|
|
189
194
|
// Save to config
|
|
@@ -196,23 +201,30 @@ export class AIService extends EventEmitter {
|
|
|
196
201
|
// Check if current provider is available
|
|
197
202
|
isAvailable() {
|
|
198
203
|
const providers = getAvailableProviders();
|
|
199
|
-
const current = providers.find(p => p.id === this.currentProvider);
|
|
204
|
+
const current = providers.find((p) => p.id === this.currentProvider);
|
|
200
205
|
return current?.available || false;
|
|
201
206
|
}
|
|
202
207
|
// Get provider info
|
|
203
208
|
getProviderInfo() {
|
|
204
209
|
const providers = getAvailableProviders();
|
|
205
|
-
return providers.find(p => p.id === this.currentProvider);
|
|
210
|
+
return providers.find((p) => p.id === this.currentProvider);
|
|
206
211
|
}
|
|
207
212
|
// Load project context
|
|
208
213
|
loadProjectContext(cwd) {
|
|
209
|
-
const files = [
|
|
214
|
+
const files = [
|
|
215
|
+
"CLAUDE.md",
|
|
216
|
+
"AGENTS.md",
|
|
217
|
+
"GEMINI.md",
|
|
218
|
+
"README.md",
|
|
219
|
+
"package.json",
|
|
220
|
+
"app-manifest.json",
|
|
221
|
+
];
|
|
210
222
|
const contextParts = [];
|
|
211
223
|
for (const file of files) {
|
|
212
224
|
const filePath = path.join(cwd, file);
|
|
213
225
|
if (fs.existsSync(filePath)) {
|
|
214
226
|
try {
|
|
215
|
-
const content = fs.readFileSync(filePath,
|
|
227
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
216
228
|
// Limit each file to 2000 chars
|
|
217
229
|
contextParts.push(`=== ${file} ===\n${content.slice(0, 2000)}`);
|
|
218
230
|
}
|
|
@@ -221,22 +233,22 @@ export class AIService extends EventEmitter {
|
|
|
221
233
|
}
|
|
222
234
|
}
|
|
223
235
|
}
|
|
224
|
-
this.projectContext = contextParts.join(
|
|
236
|
+
this.projectContext = contextParts.join("\n\n");
|
|
225
237
|
}
|
|
226
238
|
// Send message using current provider
|
|
227
239
|
async sendMessage(message) {
|
|
228
240
|
const config = loadAIConfig();
|
|
229
241
|
// Build context
|
|
230
|
-
let systemContext = config.systemPrompt ||
|
|
242
|
+
let systemContext = config.systemPrompt || "";
|
|
231
243
|
if (config.projectContext && this.projectContext) {
|
|
232
|
-
systemContext +=
|
|
244
|
+
systemContext += "\n\nProject Context:\n" + this.projectContext;
|
|
233
245
|
}
|
|
234
246
|
switch (this.currentProvider) {
|
|
235
|
-
case
|
|
247
|
+
case "claude":
|
|
236
248
|
return this.sendWithClaude(message, systemContext);
|
|
237
|
-
case
|
|
249
|
+
case "codex":
|
|
238
250
|
return this.sendWithCodex(message, systemContext);
|
|
239
|
-
case
|
|
251
|
+
case "gemini":
|
|
240
252
|
return this.sendWithGemini(message, systemContext);
|
|
241
253
|
default:
|
|
242
254
|
throw new Error(`Unknown provider: ${this.currentProvider}`);
|
|
@@ -245,29 +257,31 @@ export class AIService extends EventEmitter {
|
|
|
245
257
|
// Send message with Claude CLI
|
|
246
258
|
async sendWithClaude(message, systemContext) {
|
|
247
259
|
return new Promise((resolve, reject) => {
|
|
248
|
-
let output =
|
|
249
|
-
let errorOutput =
|
|
250
|
-
const fullPrompt = systemContext
|
|
251
|
-
|
|
252
|
-
|
|
260
|
+
let output = "";
|
|
261
|
+
let errorOutput = "";
|
|
262
|
+
const fullPrompt = systemContext
|
|
263
|
+
? `${systemContext}\n\nUser: ${message}`
|
|
264
|
+
: message;
|
|
265
|
+
const claude = spawn("claude", ["--print", "-p", fullPrompt], {
|
|
266
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
253
267
|
shell: true,
|
|
254
268
|
});
|
|
255
|
-
claude.stdout.on(
|
|
269
|
+
claude.stdout.on("data", (data) => {
|
|
256
270
|
output += data.toString();
|
|
257
271
|
});
|
|
258
|
-
claude.stderr.on(
|
|
272
|
+
claude.stderr.on("data", (data) => {
|
|
259
273
|
errorOutput += data.toString();
|
|
260
274
|
});
|
|
261
|
-
claude.on(
|
|
275
|
+
claude.on("close", (code) => {
|
|
262
276
|
if (code !== 0) {
|
|
263
|
-
reject(new Error(`Claude error: ${errorOutput ||
|
|
277
|
+
reject(new Error(`Claude error: ${errorOutput || "Unknown error"}`));
|
|
264
278
|
return;
|
|
265
279
|
}
|
|
266
|
-
this.conversationHistory.push({ role:
|
|
267
|
-
this.conversationHistory.push({ role:
|
|
280
|
+
this.conversationHistory.push({ role: "user", content: message });
|
|
281
|
+
this.conversationHistory.push({ role: "assistant", content: output });
|
|
268
282
|
resolve(output.trim());
|
|
269
283
|
});
|
|
270
|
-
claude.on(
|
|
284
|
+
claude.on("error", (err) => {
|
|
271
285
|
reject(new Error(`Failed to run Claude: ${err.message}`));
|
|
272
286
|
});
|
|
273
287
|
});
|
|
@@ -275,72 +289,76 @@ export class AIService extends EventEmitter {
|
|
|
275
289
|
// Send message with Codex CLI
|
|
276
290
|
async sendWithCodex(message, systemContext) {
|
|
277
291
|
return new Promise((resolve, reject) => {
|
|
278
|
-
let output =
|
|
279
|
-
let errorOutput =
|
|
280
|
-
const fullPrompt = systemContext
|
|
281
|
-
|
|
282
|
-
|
|
292
|
+
let output = "";
|
|
293
|
+
let errorOutput = "";
|
|
294
|
+
const fullPrompt = systemContext
|
|
295
|
+
? `${systemContext}\n\nUser: ${message}`
|
|
296
|
+
: message;
|
|
297
|
+
const codex = spawn("codex", ["--quiet", "-p", fullPrompt], {
|
|
298
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
283
299
|
shell: true,
|
|
284
300
|
});
|
|
285
|
-
codex.stdout.on(
|
|
301
|
+
codex.stdout.on("data", (data) => {
|
|
286
302
|
output += data.toString();
|
|
287
303
|
});
|
|
288
|
-
codex.stderr.on(
|
|
304
|
+
codex.stderr.on("data", (data) => {
|
|
289
305
|
errorOutput += data.toString();
|
|
290
306
|
});
|
|
291
|
-
codex.on(
|
|
307
|
+
codex.on("close", (code) => {
|
|
292
308
|
if (code !== 0) {
|
|
293
|
-
reject(new Error(`Codex error: ${errorOutput ||
|
|
309
|
+
reject(new Error(`Codex error: ${errorOutput || "Unknown error"}`));
|
|
294
310
|
return;
|
|
295
311
|
}
|
|
296
|
-
this.conversationHistory.push({ role:
|
|
297
|
-
this.conversationHistory.push({ role:
|
|
312
|
+
this.conversationHistory.push({ role: "user", content: message });
|
|
313
|
+
this.conversationHistory.push({ role: "assistant", content: output });
|
|
298
314
|
resolve(output.trim());
|
|
299
315
|
});
|
|
300
|
-
codex.on(
|
|
316
|
+
codex.on("error", (err) => {
|
|
301
317
|
reject(new Error(`Failed to run Codex: ${err.message}`));
|
|
302
318
|
});
|
|
303
319
|
});
|
|
304
320
|
}
|
|
305
321
|
// Send message with Gemini
|
|
306
322
|
async sendWithGemini(message, systemContext) {
|
|
307
|
-
const fullPrompt = systemContext
|
|
308
|
-
|
|
323
|
+
const fullPrompt = systemContext
|
|
324
|
+
? `${systemContext}\n\nUser: ${message}`
|
|
325
|
+
: message;
|
|
326
|
+
if (this.geminiMethod === "cli") {
|
|
309
327
|
return this.sendWithGeminiCli(fullPrompt);
|
|
310
328
|
}
|
|
311
|
-
else if (this.geminiMethod ===
|
|
329
|
+
else if (this.geminiMethod === "api_key") {
|
|
312
330
|
return this.sendWithGeminiApi(fullPrompt);
|
|
313
331
|
}
|
|
314
|
-
else if (this.geminiMethod ===
|
|
332
|
+
else if (this.geminiMethod === "gcloud") {
|
|
315
333
|
return this.sendWithGeminiGcloud(fullPrompt);
|
|
316
334
|
}
|
|
317
|
-
throw new Error(
|
|
335
|
+
throw new Error("Gemini is not properly configured");
|
|
318
336
|
}
|
|
319
337
|
// Send with Gemini CLI
|
|
320
338
|
async sendWithGeminiCli(prompt) {
|
|
321
339
|
return new Promise((resolve, reject) => {
|
|
322
|
-
let output =
|
|
323
|
-
let errorOutput =
|
|
324
|
-
const gemini = spawn(
|
|
325
|
-
stdio: [
|
|
340
|
+
let output = "";
|
|
341
|
+
let errorOutput = "";
|
|
342
|
+
const gemini = spawn("gemini", ["-p", prompt], {
|
|
343
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
326
344
|
shell: true,
|
|
327
345
|
});
|
|
328
|
-
gemini.stdout.on(
|
|
346
|
+
gemini.stdout.on("data", (data) => {
|
|
329
347
|
output += data.toString();
|
|
330
348
|
});
|
|
331
|
-
gemini.stderr.on(
|
|
349
|
+
gemini.stderr.on("data", (data) => {
|
|
332
350
|
errorOutput += data.toString();
|
|
333
351
|
});
|
|
334
|
-
gemini.on(
|
|
352
|
+
gemini.on("close", (code) => {
|
|
335
353
|
if (code !== 0) {
|
|
336
|
-
reject(new Error(`Gemini error: ${errorOutput ||
|
|
354
|
+
reject(new Error(`Gemini error: ${errorOutput || "Unknown error"}`));
|
|
337
355
|
return;
|
|
338
356
|
}
|
|
339
|
-
this.conversationHistory.push({ role:
|
|
340
|
-
this.conversationHistory.push({ role:
|
|
357
|
+
this.conversationHistory.push({ role: "user", content: prompt });
|
|
358
|
+
this.conversationHistory.push({ role: "assistant", content: output });
|
|
341
359
|
resolve(output.trim());
|
|
342
360
|
});
|
|
343
|
-
gemini.on(
|
|
361
|
+
gemini.on("error", (err) => {
|
|
344
362
|
reject(new Error(`Failed to run Gemini: ${err.message}`));
|
|
345
363
|
});
|
|
346
364
|
});
|
|
@@ -349,13 +367,13 @@ export class AIService extends EventEmitter {
|
|
|
349
367
|
async sendWithGeminiApi(prompt) {
|
|
350
368
|
const apiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
|
|
351
369
|
if (!apiKey) {
|
|
352
|
-
throw new Error(
|
|
370
|
+
throw new Error("GEMINI_API_KEY not set");
|
|
353
371
|
}
|
|
354
372
|
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`, {
|
|
355
|
-
method:
|
|
356
|
-
headers: {
|
|
373
|
+
method: "POST",
|
|
374
|
+
headers: { "Content-Type": "application/json" },
|
|
357
375
|
body: JSON.stringify({
|
|
358
|
-
contents: [{ role:
|
|
376
|
+
contents: [{ role: "user", parts: [{ text: prompt }] }],
|
|
359
377
|
generationConfig: { maxOutputTokens: 2048, temperature: 0.7 },
|
|
360
378
|
}),
|
|
361
379
|
});
|
|
@@ -363,10 +381,11 @@ export class AIService extends EventEmitter {
|
|
|
363
381
|
const errorText = await response.text();
|
|
364
382
|
throw new Error(`Gemini API error: ${response.status} - ${errorText}`);
|
|
365
383
|
}
|
|
366
|
-
const data = await response.json();
|
|
367
|
-
const responseText = data.candidates?.[0]?.content?.parts?.[0]?.text ||
|
|
368
|
-
|
|
369
|
-
this.conversationHistory.push({ role:
|
|
384
|
+
const data = (await response.json());
|
|
385
|
+
const responseText = data.candidates?.[0]?.content?.parts?.[0]?.text ||
|
|
386
|
+
"No response generated.";
|
|
387
|
+
this.conversationHistory.push({ role: "user", content: prompt });
|
|
388
|
+
this.conversationHistory.push({ role: "assistant", content: responseText });
|
|
370
389
|
return responseText;
|
|
371
390
|
}
|
|
372
391
|
// Send with Gemini via gcloud
|
|
@@ -375,38 +394,59 @@ export class AIService extends EventEmitter {
|
|
|
375
394
|
let accessToken;
|
|
376
395
|
let projectId;
|
|
377
396
|
try {
|
|
378
|
-
accessToken = execSync(
|
|
379
|
-
|
|
397
|
+
accessToken = execSync("gcloud auth print-access-token", {
|
|
398
|
+
stdio: "pipe",
|
|
399
|
+
})
|
|
400
|
+
.toString()
|
|
401
|
+
.trim();
|
|
402
|
+
projectId = execSync("gcloud config get-value project", {
|
|
403
|
+
stdio: "pipe",
|
|
404
|
+
})
|
|
405
|
+
.toString()
|
|
406
|
+
.trim();
|
|
380
407
|
}
|
|
381
408
|
catch (error) {
|
|
382
|
-
reject(new Error(
|
|
409
|
+
reject(new Error("Failed to get gcloud credentials"));
|
|
383
410
|
return;
|
|
384
411
|
}
|
|
385
412
|
const requestBody = JSON.stringify({
|
|
386
|
-
contents: [{ role:
|
|
413
|
+
contents: [{ role: "user", parts: [{ text: prompt }] }],
|
|
387
414
|
generationConfig: { maxOutputTokens: 2048, temperature: 0.7 },
|
|
388
415
|
});
|
|
389
|
-
const curl = spawn(
|
|
390
|
-
|
|
416
|
+
const curl = spawn("curl", [
|
|
417
|
+
"-s",
|
|
418
|
+
"-X",
|
|
419
|
+
"POST",
|
|
391
420
|
`https://us-central1-aiplatform.googleapis.com/v1/projects/${projectId}/locations/us-central1/publishers/google/models/gemini-1.5-flash:generateContent`,
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
421
|
+
"-H",
|
|
422
|
+
`Authorization: Bearer ${accessToken}`,
|
|
423
|
+
"-H",
|
|
424
|
+
"Content-Type: application/json",
|
|
425
|
+
"-d",
|
|
426
|
+
requestBody,
|
|
427
|
+
], { stdio: ["pipe", "pipe", "pipe"] });
|
|
428
|
+
let output = "";
|
|
429
|
+
let errorOutput = "";
|
|
430
|
+
curl.stdout.on("data", (data) => {
|
|
431
|
+
output += data.toString();
|
|
432
|
+
});
|
|
433
|
+
curl.stderr.on("data", (data) => {
|
|
434
|
+
errorOutput += data.toString();
|
|
435
|
+
});
|
|
436
|
+
curl.on("close", (code) => {
|
|
401
437
|
if (code !== 0) {
|
|
402
438
|
reject(new Error(`Gemini gcloud error: ${errorOutput}`));
|
|
403
439
|
return;
|
|
404
440
|
}
|
|
405
441
|
try {
|
|
406
442
|
const data = JSON.parse(output);
|
|
407
|
-
const responseText = data.candidates?.[0]?.content?.parts?.[0]?.text ||
|
|
408
|
-
|
|
409
|
-
this.conversationHistory.push({ role:
|
|
443
|
+
const responseText = data.candidates?.[0]?.content?.parts?.[0]?.text ||
|
|
444
|
+
"No response generated.";
|
|
445
|
+
this.conversationHistory.push({ role: "user", content: prompt });
|
|
446
|
+
this.conversationHistory.push({
|
|
447
|
+
role: "assistant",
|
|
448
|
+
content: responseText,
|
|
449
|
+
});
|
|
410
450
|
resolve(responseText);
|
|
411
451
|
}
|
|
412
452
|
catch (parseError) {
|