@pixelbyte-software/pixcode 1.51.8 โ 1.52.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/CODE_OF_CONDUCT.md +41 -41
- package/CONTRIBUTING.md +155 -155
- package/LICENSE +718 -718
- package/README.de.md +169 -169
- package/README.ja.md +167 -167
- package/README.ko.md +167 -167
- package/README.md +419 -419
- package/README.ru.md +169 -169
- package/README.tr.md +298 -298
- package/README.zh-CN.md +167 -167
- package/SECURITY.md +46 -46
- package/dist/api-automation.html +110 -110
- package/dist/api-docs.html +548 -548
- package/dist/assets/{index--kNEhygZ.js โ index-HFIf2iQq.js} +153 -153
- package/dist/clear-cache.html +85 -85
- package/dist/convert-icons.md +52 -52
- package/dist/docs.html +308 -308
- package/dist/favicon.svg +8 -8
- package/dist/features.html +133 -133
- package/dist/generate-icons.js +48 -48
- package/dist/humans.txt +15 -15
- package/dist/icons/codex-white.svg +3 -3
- package/dist/icons/codex.svg +3 -3
- package/dist/icons/cursor-white.svg +11 -11
- package/dist/icons/icon-128x128.svg +9 -9
- package/dist/icons/icon-144x144.svg +9 -9
- package/dist/icons/icon-152x152.svg +9 -9
- package/dist/icons/icon-192x192.svg +9 -9
- package/dist/icons/icon-384x384.svg +9 -9
- package/dist/icons/icon-512x512.svg +9 -9
- package/dist/icons/icon-72x72.svg +9 -9
- package/dist/icons/icon-96x96.svg +9 -9
- package/dist/icons/icon-template.svg +9 -9
- package/dist/icons/qwen-logo.svg +14 -14
- package/dist/index.html +58 -58
- package/dist/landing.html +268 -268
- package/dist/llms-full.txt +119 -119
- package/dist/llms.txt +53 -53
- package/dist/logo.svg +12 -12
- package/dist/manifest.json +60 -60
- package/dist/openapi.yaml +1696 -1696
- package/dist/orchestration.html +125 -125
- package/dist/robots.txt +4 -4
- package/dist/site.css +692 -692
- package/dist/sitemap.xml +51 -51
- package/dist/sw.js +132 -132
- package/dist-server/server/cli.js +96 -96
- package/dist-server/server/daemon/manager.js +33 -33
- package/dist-server/server/daemon-manager.js +64 -64
- package/dist-server/server/index.js +91 -65
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/routes/commands.js +25 -25
- package/dist-server/server/routes/git.js +17 -17
- package/dist-server/server/routes/live-view.js +46 -46
- package/dist-server/server/services/hermes-gateway.js +5 -5
- package/dist-server/server/services/public-api-manifest.js +51 -51
- package/package.json +222 -222
- package/scripts/fix-node-pty.js +67 -67
- package/scripts/github/create-v1.38-issues.mjs +351 -351
- package/scripts/github/create-vscode-workbench-issues.mjs +121 -121
- package/scripts/hermes/configure-pixcode-mcp.mjs +165 -165
- package/scripts/hermes/pixcode-mcp-server.mjs +1009 -1009
- package/scripts/smoke/changes-panel-layout.mjs +48 -48
- package/scripts/smoke/chat-composer-fixed-layout.mjs +55 -55
- package/scripts/smoke/chat-message-timeline-order.mjs +41 -41
- package/scripts/smoke/chat-realtime-hydration.mjs +44 -44
- package/scripts/smoke/chat-session-provider-pools.mjs +35 -35
- package/scripts/smoke/chat-session-state.mjs +19 -19
- package/scripts/smoke/code-editor-theme.mjs +55 -55
- package/scripts/smoke/code-editor-vscode-engine.mjs +91 -91
- package/scripts/smoke/command-center-agent-writes.mjs +79 -79
- package/scripts/smoke/command-center-non-git.mjs +46 -46
- package/scripts/smoke/context-packet.mjs +43 -43
- package/scripts/smoke/control-room-ux-redesign.mjs +91 -91
- package/scripts/smoke/daemon-entrypoint.mjs +20 -20
- package/scripts/smoke/default-landing-routing.mjs +33 -33
- package/scripts/smoke/desktop-native-notifications.mjs +30 -30
- package/scripts/smoke/desktop-tray-icon.mjs +33 -33
- package/scripts/smoke/discord-release-workflow.mjs +24 -24
- package/scripts/smoke/git-install-update.mjs +255 -255
- package/scripts/smoke/handoff-artifact-protocol.mjs +50 -50
- package/scripts/smoke/hermes-api-install.mjs +56 -56
- package/scripts/smoke/hermes-gateway-persistence.mjs +104 -104
- package/scripts/smoke/hermes-mcp-pixcode-roundtrip.mjs +426 -426
- package/scripts/smoke/hermes-rest-chat-api.mjs +162 -162
- package/scripts/smoke/hermes-rest-chat-live.mjs +45 -45
- package/scripts/smoke/hermes-rest-codex-launch.mjs +209 -209
- package/scripts/smoke/hermes-rest-gateway.mjs +79 -79
- package/scripts/smoke/hermes-rest-live.mjs +42 -42
- package/scripts/smoke/hermes-roundtrip.mjs +167 -167
- package/scripts/smoke/hermes-settings-commands.mjs +349 -349
- package/scripts/smoke/hermes-smoke-launcher-guard.mjs +34 -34
- package/scripts/smoke/live-view-diagnostics.mjs +53 -53
- package/scripts/smoke/live-view-environment.mjs +92 -92
- package/scripts/smoke/live-view-integration.mjs +450 -450
- package/scripts/smoke/mac-desktop-runtime.mjs +37 -37
- package/scripts/smoke/mobile-tunnel-guidance.mjs +29 -29
- package/scripts/smoke/model-registry.mjs +36 -36
- package/scripts/smoke/multi-project-ui.mjs +45 -45
- package/scripts/smoke/multi-worker-slots.mjs +42 -42
- package/scripts/smoke/notification-center.mjs +87 -87
- package/scripts/smoke/notification-inapp-preference.mjs +23 -23
- package/scripts/smoke/notification-taxonomy.mjs +58 -58
- package/scripts/smoke/orchestration-api.mjs +172 -172
- package/scripts/smoke/orchestration-execution-dashboard.mjs +33 -33
- package/scripts/smoke/orchestration-live-run.mjs +176 -176
- package/scripts/smoke/orchestration-mobile-scroll.mjs +29 -29
- package/scripts/smoke/orchestration-model-sync.mjs +30 -30
- package/scripts/smoke/orchestration-permission-fallback.mjs +34 -34
- package/scripts/smoke/orchestration-runtime-guards.mjs +48 -48
- package/scripts/smoke/orchestration-user-facing-output.mjs +25 -25
- package/scripts/smoke/permission-policy.mjs +50 -50
- package/scripts/smoke/pixcode-workbench-1-48.mjs +167 -167
- package/scripts/smoke/provider-models-opencode-live.mjs +66 -66
- package/scripts/smoke/provider-rest-api.mjs +124 -124
- package/scripts/smoke/provider-selection-status.mjs +52 -52
- package/scripts/smoke/run-state-refresh.mjs +52 -52
- package/scripts/smoke/runtime-manager.mjs +99 -99
- package/scripts/smoke/shell-manual-disconnect.mjs +30 -30
- package/scripts/smoke/side-panel-editor-layout.mjs +34 -34
- package/scripts/smoke/static-root-routing.mjs +21 -21
- package/scripts/smoke/strict-handoff-compact.mjs +60 -60
- package/scripts/smoke/taskmaster-config.mjs +24 -24
- package/scripts/smoke/taskmaster-execution-telegram.mjs +3 -3
- package/scripts/smoke/taskmaster-onboarding.mjs +3 -3
- package/scripts/smoke/taskmaster-run-graph.mjs +3 -3
- package/scripts/smoke/telegram-control.mjs +242 -242
- package/scripts/smoke/tunnel-persistence.mjs +56 -56
- package/scripts/smoke/update-issue-progress.mjs +69 -69
- package/scripts/smoke/update-ux.mjs +55 -55
- package/scripts/smoke/v138-completion.mjs +132 -132
- package/scripts/smoke/v138-desktop-release-hardening.mjs +69 -69
- package/scripts/smoke/v138-diagnostics.mjs +63 -63
- package/scripts/smoke/v138-issue-planner.mjs +33 -33
- package/scripts/smoke/v143-remote-control.mjs +76 -76
- package/scripts/smoke/v144-production-loop.mjs +47 -47
- package/scripts/smoke/v145-platformization.mjs +46 -46
- package/scripts/smoke/v146-control-room-ui.mjs +150 -150
- package/scripts/smoke/version-modal-autoshow.mjs +29 -29
- package/scripts/smoke/vscode-workbench-layout.mjs +63 -63
- package/scripts/smoke/vscode-workbench-polish.mjs +461 -461
- package/scripts/smoke/workflow-fallback-replay.mjs +56 -56
- package/scripts/smoke/workflow-templates.mjs +43 -43
- package/scripts/smoke/workflow-trace-timeline.mjs +46 -46
- package/scripts/update-git-install.mjs +283 -283
- package/server/claude-sdk.js +920 -920
- package/server/cli.js +1035 -1035
- package/server/constants/config.js +4 -4
- package/server/cursor-cli.js +344 -344
- package/server/daemon/manager.js +563 -563
- package/server/daemon-manager.js +964 -964
- package/server/database/db.js +895 -895
- package/server/database/json-store.js +197 -197
- package/server/gemini-cli.js +550 -550
- package/server/gemini-response-handler.js +79 -79
- package/server/index.js +111 -81
- package/server/load-env.js +35 -35
- package/server/middleware/auth.js +155 -155
- package/server/modules/orchestration/a2a/adapter-registry.ts +108 -108
- package/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.ts +63 -63
- package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +286 -286
- package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +244 -244
- package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +249 -249
- package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/adapters/json-event.adapter.ts +100 -100
- package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/agent-card.ts +55 -55
- package/server/modules/orchestration/a2a/routes.ts +590 -590
- package/server/modules/orchestration/a2a/task-store.ts +178 -178
- package/server/modules/orchestration/a2a/types.ts +126 -126
- package/server/modules/orchestration/a2a/validator.ts +113 -113
- package/server/modules/orchestration/hermes/hermes.routes.ts +642 -642
- package/server/modules/orchestration/index.ts +101 -101
- package/server/modules/orchestration/preview/port-watcher.ts +112 -112
- package/server/modules/orchestration/preview/preview-proxy.ts +60 -60
- package/server/modules/orchestration/preview/types.ts +19 -19
- package/server/modules/orchestration/security/permission-policy.ts +401 -401
- package/server/modules/orchestration/tasks/orchestration-task-store.ts +41 -41
- package/server/modules/orchestration/tasks/orchestration-task.routes.ts +64 -64
- package/server/modules/orchestration/tasks/orchestration-task.service.ts +209 -209
- package/server/modules/orchestration/tasks/orchestration-task.types.ts +40 -40
- package/server/modules/orchestration/tasks/task-run-graph.ts +155 -155
- package/server/modules/orchestration/workflows/approval-queue.ts +106 -106
- package/server/modules/orchestration/workflows/built-in-workflows.ts +127 -127
- package/server/modules/orchestration/workflows/context-packet.ts +186 -186
- package/server/modules/orchestration/workflows/handoff-artifact.ts +175 -175
- package/server/modules/orchestration/workflows/workflow-fallback-policy.ts +161 -161
- package/server/modules/orchestration/workflows/workflow-replay.ts +254 -254
- package/server/modules/orchestration/workflows/workflow-runner.ts +2070 -2070
- package/server/modules/orchestration/workflows/workflow-store.ts +97 -97
- package/server/modules/orchestration/workflows/workflow-templates.ts +272 -272
- package/server/modules/orchestration/workflows/workflow-trace.ts +424 -424
- package/server/modules/orchestration/workflows/workflow.routes.ts +586 -586
- package/server/modules/orchestration/workflows/workflow.types.ts +111 -111
- package/server/modules/orchestration/workflows/workspace-target.ts +122 -122
- package/server/modules/orchestration/workspace/docker-workspace.ts +136 -136
- package/server/modules/orchestration/workspace/path-safety.ts +55 -55
- package/server/modules/orchestration/workspace/types.ts +52 -52
- package/server/modules/orchestration/workspace/workspace-manager.ts +102 -102
- package/server/modules/orchestration/workspace/worktree-workspace.ts +126 -126
- package/server/modules/providers/index.ts +2 -2
- package/server/modules/providers/list/claude/claude-auth.provider.ts +146 -146
- package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -306
- package/server/modules/providers/list/claude/claude.provider.ts +15 -15
- package/server/modules/providers/list/codex/codex-auth.provider.ts +117 -117
- package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -319
- package/server/modules/providers/list/codex/codex.provider.ts +15 -15
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +147 -147
- package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -421
- package/server/modules/providers/list/cursor/cursor.provider.ts +15 -15
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +173 -173
- package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -110
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -227
- package/server/modules/providers/list/gemini/gemini.provider.ts +15 -15
- package/server/modules/providers/list/opencode/opencode-auth.provider.ts +131 -131
- package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +126 -126
- package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +286 -286
- package/server/modules/providers/list/opencode/opencode.provider.ts +29 -29
- package/server/modules/providers/list/qwen/qwen-auth.provider.ts +146 -146
- package/server/modules/providers/list/qwen/qwen-mcp.provider.ts +114 -114
- package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +265 -265
- package/server/modules/providers/list/qwen/qwen.provider.ts +21 -21
- package/server/modules/providers/provider.registry.ts +40 -40
- package/server/modules/providers/provider.routes.ts +891 -891
- package/server/modules/providers/services/mcp.service.ts +86 -86
- package/server/modules/providers/services/provider-auth.service.ts +26 -26
- package/server/modules/providers/services/sessions.service.ts +45 -45
- package/server/modules/providers/shared/base/abstract.provider.ts +20 -20
- package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
- package/server/modules/providers/shared/provider-configs.ts +142 -142
- package/server/modules/providers/tests/mcp.test.ts +293 -293
- package/server/openai-codex.js +462 -462
- package/server/opencode-cli.js +491 -491
- package/server/opencode-response-handler.js +111 -111
- package/server/projects.js +3008 -3008
- package/server/qwen-code-cli.js +410 -410
- package/server/qwen-response-handler.js +73 -73
- package/server/routes/agent.js +1435 -1435
- package/server/routes/auth.js +142 -142
- package/server/routes/codex.js +20 -20
- package/server/routes/commands.js +538 -538
- package/server/routes/cursor.js +61 -61
- package/server/routes/diagnostics.js +41 -41
- package/server/routes/gemini.js +25 -25
- package/server/routes/git.js +1641 -1641
- package/server/routes/live-view.js +387 -387
- package/server/routes/mcp-utils.js +13 -13
- package/server/routes/messages.js +50 -50
- package/server/routes/network.js +117 -117
- package/server/routes/platformization.js +182 -182
- package/server/routes/plugins.js +320 -320
- package/server/routes/production-agent-loop.js +90 -90
- package/server/routes/projects.js +911 -911
- package/server/routes/public-api.js +34 -34
- package/server/routes/qwen.js +27 -27
- package/server/routes/remote.js +50 -50
- package/server/routes/settings.js +321 -321
- package/server/routes/telegram.js +140 -140
- package/server/routes/user.js +125 -125
- package/server/routes/webhooks.js +63 -63
- package/server/services/control-room.js +102 -102
- package/server/services/diagnostics.js +165 -165
- package/server/services/external-access.js +375 -375
- package/server/services/hermes-gateway.js +1562 -1562
- package/server/services/hermes-install-jobs.js +729 -729
- package/server/services/install-jobs.js +715 -715
- package/server/services/live-view.js +956 -956
- package/server/services/managed-runtimes.js +493 -493
- package/server/services/model-registry.js +144 -144
- package/server/services/notification-orchestrator.js +365 -365
- package/server/services/notification-taxonomy.js +204 -204
- package/server/services/platformization.js +773 -773
- package/server/services/production-agent-loop.js +248 -248
- package/server/services/provider-cli-versions.js +149 -149
- package/server/services/provider-credentials.js +189 -189
- package/server/services/provider-models.js +396 -396
- package/server/services/public-api-manifest.js +190 -190
- package/server/services/remote-connection.js +127 -127
- package/server/services/runtime-manager.js +323 -323
- package/server/services/startup-update.js +234 -234
- package/server/services/telegram/bot.js +331 -331
- package/server/services/telegram/control-center.js +979 -979
- package/server/services/telegram/telegram-http-client.js +151 -151
- package/server/services/telegram/translations.js +340 -340
- package/server/services/vapid-keys.js +36 -36
- package/server/services/webhooks.js +216 -216
- package/server/sessionManager.js +225 -225
- package/server/shared/interfaces.ts +54 -54
- package/server/shared/types.ts +172 -172
- package/server/shared/utils.ts +193 -193
- package/server/tsconfig.json +36 -36
- package/server/utils/colors.js +21 -21
- package/server/utils/commandParser.js +305 -305
- package/server/utils/frontmatter.js +18 -18
- package/server/utils/gitConfig.js +34 -34
- package/server/utils/plugin-loader.js +457 -457
- package/server/utils/plugin-process-manager.js +185 -185
- package/server/utils/port-access.js +209 -209
- package/server/utils/runtime-paths.js +37 -37
- package/server/utils/url-detection.js +71 -71
- package/server/vite-daemon.js +79 -79
- package/shared/modelConstants.js +161 -161
- package/shared/networkHosts.js +22 -22
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
// Gemini Response Handler - JSON Stream processing
|
|
2
|
-
import { sessionsService } from './modules/providers/services/sessions.service.js';
|
|
3
|
-
|
|
4
|
-
class GeminiResponseHandler {
|
|
5
|
-
constructor(ws, options = {}) {
|
|
6
|
-
this.ws = ws;
|
|
7
|
-
this.buffer = '';
|
|
8
|
-
this.onContentFragment = options.onContentFragment || null;
|
|
9
|
-
this.onInit = options.onInit || null;
|
|
10
|
-
this.onToolUse = options.onToolUse || null;
|
|
11
|
-
this.onToolResult = options.onToolResult || null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Process incoming raw data from Gemini stream-json
|
|
15
|
-
processData(data) {
|
|
16
|
-
this.buffer += data;
|
|
17
|
-
|
|
18
|
-
// Split by newline
|
|
19
|
-
const lines = this.buffer.split('\n');
|
|
20
|
-
|
|
21
|
-
// Keep the last incomplete line in the buffer
|
|
22
|
-
this.buffer = lines.pop() || '';
|
|
23
|
-
|
|
24
|
-
for (const line of lines) {
|
|
25
|
-
if (!line.trim()) continue;
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const event = JSON.parse(line);
|
|
29
|
-
this.handleEvent(event);
|
|
30
|
-
} catch (err) {
|
|
31
|
-
// Not a JSON line, probably debug output or CLI warnings
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
handleEvent(event) {
|
|
37
|
-
const sid = typeof this.ws.getSessionId === 'function' ? this.ws.getSessionId() : null;
|
|
38
|
-
|
|
39
|
-
if (event.type === 'init') {
|
|
40
|
-
if (this.onInit) {
|
|
41
|
-
this.onInit(event);
|
|
42
|
-
}
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Invoke per-type callbacks for session tracking
|
|
47
|
-
if (event.type === 'message' && event.role === 'assistant') {
|
|
48
|
-
const content = event.content || '';
|
|
49
|
-
if (this.onContentFragment && content) {
|
|
50
|
-
this.onContentFragment(content);
|
|
51
|
-
}
|
|
52
|
-
} else if (event.type === 'tool_use' && this.onToolUse) {
|
|
53
|
-
this.onToolUse(event);
|
|
54
|
-
} else if (event.type === 'tool_result' && this.onToolResult) {
|
|
55
|
-
this.onToolResult(event);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Normalize via adapter and send all resulting messages
|
|
59
|
-
const normalized = sessionsService.normalizeMessage('gemini', event, sid);
|
|
60
|
-
for (const msg of normalized) {
|
|
61
|
-
this.ws.send(msg);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
forceFlush() {
|
|
66
|
-
if (this.buffer.trim()) {
|
|
67
|
-
try {
|
|
68
|
-
const event = JSON.parse(this.buffer);
|
|
69
|
-
this.handleEvent(event);
|
|
70
|
-
} catch (err) { }
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
destroy() {
|
|
75
|
-
this.buffer = '';
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export default GeminiResponseHandler;
|
|
1
|
+
// Gemini Response Handler - JSON Stream processing
|
|
2
|
+
import { sessionsService } from './modules/providers/services/sessions.service.js';
|
|
3
|
+
|
|
4
|
+
class GeminiResponseHandler {
|
|
5
|
+
constructor(ws, options = {}) {
|
|
6
|
+
this.ws = ws;
|
|
7
|
+
this.buffer = '';
|
|
8
|
+
this.onContentFragment = options.onContentFragment || null;
|
|
9
|
+
this.onInit = options.onInit || null;
|
|
10
|
+
this.onToolUse = options.onToolUse || null;
|
|
11
|
+
this.onToolResult = options.onToolResult || null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Process incoming raw data from Gemini stream-json
|
|
15
|
+
processData(data) {
|
|
16
|
+
this.buffer += data;
|
|
17
|
+
|
|
18
|
+
// Split by newline
|
|
19
|
+
const lines = this.buffer.split('\n');
|
|
20
|
+
|
|
21
|
+
// Keep the last incomplete line in the buffer
|
|
22
|
+
this.buffer = lines.pop() || '';
|
|
23
|
+
|
|
24
|
+
for (const line of lines) {
|
|
25
|
+
if (!line.trim()) continue;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const event = JSON.parse(line);
|
|
29
|
+
this.handleEvent(event);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
// Not a JSON line, probably debug output or CLI warnings
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
handleEvent(event) {
|
|
37
|
+
const sid = typeof this.ws.getSessionId === 'function' ? this.ws.getSessionId() : null;
|
|
38
|
+
|
|
39
|
+
if (event.type === 'init') {
|
|
40
|
+
if (this.onInit) {
|
|
41
|
+
this.onInit(event);
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Invoke per-type callbacks for session tracking
|
|
47
|
+
if (event.type === 'message' && event.role === 'assistant') {
|
|
48
|
+
const content = event.content || '';
|
|
49
|
+
if (this.onContentFragment && content) {
|
|
50
|
+
this.onContentFragment(content);
|
|
51
|
+
}
|
|
52
|
+
} else if (event.type === 'tool_use' && this.onToolUse) {
|
|
53
|
+
this.onToolUse(event);
|
|
54
|
+
} else if (event.type === 'tool_result' && this.onToolResult) {
|
|
55
|
+
this.onToolResult(event);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Normalize via adapter and send all resulting messages
|
|
59
|
+
const normalized = sessionsService.normalizeMessage('gemini', event, sid);
|
|
60
|
+
for (const msg of normalized) {
|
|
61
|
+
this.ws.send(msg);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
forceFlush() {
|
|
66
|
+
if (this.buffer.trim()) {
|
|
67
|
+
try {
|
|
68
|
+
const event = JSON.parse(this.buffer);
|
|
69
|
+
this.handleEvent(event);
|
|
70
|
+
} catch (err) { }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
destroy() {
|
|
75
|
+
this.buffer = '';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default GeminiResponseHandler;
|
package/server/index.js
CHANGED
|
@@ -3283,8 +3283,78 @@ function handleShellConnection(ws, request) {
|
|
|
3283
3283
|
let shellProcess = null;
|
|
3284
3284
|
let ptySessionKey = null;
|
|
3285
3285
|
let urlDetectionBuffer = '';
|
|
3286
|
+
let outputBuffer = '';
|
|
3287
|
+
let outputFlushTimer = null;
|
|
3286
3288
|
const announcedAuthUrls = new Set();
|
|
3287
3289
|
|
|
3290
|
+
function flushOutputBuffer() {
|
|
3291
|
+
if (!outputBuffer || !ptySessionKey) {
|
|
3292
|
+
outputFlushTimer = null;
|
|
3293
|
+
return;
|
|
3294
|
+
}
|
|
3295
|
+
const rawData = outputBuffer;
|
|
3296
|
+
outputBuffer = '';
|
|
3297
|
+
outputFlushTimer = null;
|
|
3298
|
+
|
|
3299
|
+
const session = ptySessionsMap.get(ptySessionKey);
|
|
3300
|
+
if (!session) return;
|
|
3301
|
+
session.updatedAt = Date.now();
|
|
3302
|
+
|
|
3303
|
+
appendPtySessionBuffer(session, rawData);
|
|
3304
|
+
|
|
3305
|
+
if (session.ws && session.ws.readyState === WebSocket.OPEN) {
|
|
3306
|
+
let outputData = rawData;
|
|
3307
|
+
|
|
3308
|
+
const cleanChunk = stripAnsiSequences(rawData);
|
|
3309
|
+
urlDetectionBuffer = `${urlDetectionBuffer}${cleanChunk}`.slice(-SHELL_URL_PARSE_BUFFER_LIMIT);
|
|
3310
|
+
|
|
3311
|
+
outputData = outputData.replace(
|
|
3312
|
+
/OPEN_URL:\s*(https?:\/\/[^\s\x1b\x07]+)/g,
|
|
3313
|
+
'[INFO] Opening in browser: $1'
|
|
3314
|
+
);
|
|
3315
|
+
outputData = hideProviderApprovalChoiceLines(outputData);
|
|
3316
|
+
|
|
3317
|
+
const emitAuthUrl = (detectedUrl, autoOpen = false) => {
|
|
3318
|
+
const normalizedUrl = normalizeDetectedUrl(detectedUrl);
|
|
3319
|
+
if (!normalizedUrl) return;
|
|
3320
|
+
|
|
3321
|
+
const isNewUrl = !announcedAuthUrls.has(normalizedUrl);
|
|
3322
|
+
if (isNewUrl) {
|
|
3323
|
+
announcedAuthUrls.add(normalizedUrl);
|
|
3324
|
+
session.ws.send(JSON.stringify({
|
|
3325
|
+
type: 'auth_url',
|
|
3326
|
+
url: normalizedUrl,
|
|
3327
|
+
autoOpen
|
|
3328
|
+
}));
|
|
3329
|
+
}
|
|
3330
|
+
};
|
|
3331
|
+
|
|
3332
|
+
const normalizedDetectedUrls = extractUrlsFromText(urlDetectionBuffer)
|
|
3333
|
+
.map((url) => normalizeDetectedUrl(url))
|
|
3334
|
+
.filter(Boolean);
|
|
3335
|
+
|
|
3336
|
+
const dedupedDetectedUrls = Array.from(new Set(normalizedDetectedUrls)).filter((url, _, urls) =>
|
|
3337
|
+
!urls.some((otherUrl) => otherUrl !== url && otherUrl.startsWith(url))
|
|
3338
|
+
);
|
|
3339
|
+
|
|
3340
|
+
dedupedDetectedUrls.forEach((url) => emitAuthUrl(url, false));
|
|
3341
|
+
|
|
3342
|
+
if (shouldAutoOpenUrlFromOutput(cleanChunk) && dedupedDetectedUrls.length > 0) {
|
|
3343
|
+
const bestUrl = dedupedDetectedUrls.reduce((longest, current) =>
|
|
3344
|
+
current.length > longest.length ? current : longest
|
|
3345
|
+
);
|
|
3346
|
+
emitAuthUrl(bestUrl, true);
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
if (outputData) {
|
|
3350
|
+
session.ws.send(JSON.stringify({
|
|
3351
|
+
type: 'output',
|
|
3352
|
+
data: outputData
|
|
3353
|
+
}));
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3288
3358
|
ws.on('message', async (message) => {
|
|
3289
3359
|
try {
|
|
3290
3360
|
const data = JSON.parse(message);
|
|
@@ -3353,18 +3423,17 @@ function handleShellConnection(ws, request) {
|
|
|
3353
3423
|
initialCommand.includes('auth login')
|
|
3354
3424
|
);
|
|
3355
3425
|
|
|
3356
|
-
//
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
//
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
const providerSuffix = isPlainShell ? '' : `_${provider}`;
|
|
3426
|
+
// Every UI shell instance gets a unique tabId. This guarantees that
|
|
3427
|
+
// multiple tabs of the same provider (e.g. two OpenCode windows) do
|
|
3428
|
+
// not share or overwrite the same PTY session. If the client does not
|
|
3429
|
+
// send a tabId (legacy callers), generate one on the backend so the
|
|
3430
|
+
// key is still unique for this connection.
|
|
3431
|
+
const tabId = typeof data.tabId === 'string' && data.tabId.trim()
|
|
3432
|
+
? data.tabId.trim()
|
|
3433
|
+
: `tab_${crypto.randomUUID()}`;
|
|
3434
|
+
const providerKey = isPlainShell ? 'plain' : provider;
|
|
3366
3435
|
const ownerUserId = request.user?.id ?? request.user?.userId ?? 'anonymous';
|
|
3367
|
-
ptySessionKey = `${ownerUserId}_${projectPath}_${sessionId || '
|
|
3436
|
+
ptySessionKey = `${ownerUserId}_${projectPath}_${providerKey}_${tabId}_${sessionId || 'new'}`;
|
|
3368
3437
|
|
|
3369
3438
|
// Kill any existing login session before starting fresh
|
|
3370
3439
|
if (isLoginCommand) {
|
|
@@ -3373,16 +3442,12 @@ function handleShellConnection(ws, request) {
|
|
|
3373
3442
|
terminatePtySession(ptySessionKey, oldSession, 'fresh login');
|
|
3374
3443
|
}
|
|
3375
3444
|
} else if (forceNewSession) {
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
const killedSessions = killProviderPtySessions(projectPath, provider, ownerUserId);
|
|
3383
|
-
if (killedSessions > 0) {
|
|
3384
|
-
console.log(`๐งน Fresh ${provider} session requested; terminated ${killedSessions} cached PTY session(s).`);
|
|
3385
|
-
}
|
|
3445
|
+
// Only terminate the PTY that belongs to this exact tabId, not
|
|
3446
|
+
// every session for the same provider. This lets users run
|
|
3447
|
+
// multiple OpenCode/Claude/etc. instances side-by-side.
|
|
3448
|
+
const oldSession = ptySessionsMap.get(ptySessionKey);
|
|
3449
|
+
if (oldSession) {
|
|
3450
|
+
terminatePtySession(ptySessionKey, oldSession, `fresh ${isPlainShell ? 'plain shell' : provider} session`);
|
|
3386
3451
|
}
|
|
3387
3452
|
}
|
|
3388
3453
|
|
|
@@ -3618,6 +3683,7 @@ function handleShellConnection(ws, request) {
|
|
|
3618
3683
|
userId: ownerUserId,
|
|
3619
3684
|
projectPath,
|
|
3620
3685
|
sessionId,
|
|
3686
|
+
tabId,
|
|
3621
3687
|
hermesLaunchId,
|
|
3622
3688
|
provider,
|
|
3623
3689
|
isPlainShell,
|
|
@@ -3635,73 +3701,24 @@ function handleShellConnection(ws, request) {
|
|
|
3635
3701
|
writeTerminalStartupInput(createdSession, terminalStartupInput, 'new provider session', 4500);
|
|
3636
3702
|
}
|
|
3637
3703
|
|
|
3638
|
-
// Handle data output
|
|
3704
|
+
// Handle data output โ batch rapid chunks so high-volume CLI
|
|
3705
|
+
// output does not flood the WebSocket with one JSON frame per
|
|
3706
|
+
// keystroke-sized chunk.
|
|
3639
3707
|
shellProcess.onData((data) => {
|
|
3640
|
-
|
|
3641
|
-
if (!
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
appendPtySessionBuffer(session, data);
|
|
3645
|
-
|
|
3646
|
-
if (session.ws && session.ws.readyState === WebSocket.OPEN) {
|
|
3647
|
-
let outputData = data;
|
|
3648
|
-
|
|
3649
|
-
const cleanChunk = stripAnsiSequences(data);
|
|
3650
|
-
urlDetectionBuffer = `${urlDetectionBuffer}${cleanChunk}`.slice(-SHELL_URL_PARSE_BUFFER_LIMIT);
|
|
3651
|
-
|
|
3652
|
-
outputData = outputData.replace(
|
|
3653
|
-
/OPEN_URL:\s*(https?:\/\/[^\s\x1b\x07]+)/g,
|
|
3654
|
-
'[INFO] Opening in browser: $1'
|
|
3655
|
-
);
|
|
3656
|
-
outputData = hideProviderApprovalChoiceLines(outputData);
|
|
3657
|
-
|
|
3658
|
-
const emitAuthUrl = (detectedUrl, autoOpen = false) => {
|
|
3659
|
-
const normalizedUrl = normalizeDetectedUrl(detectedUrl);
|
|
3660
|
-
if (!normalizedUrl) return;
|
|
3661
|
-
|
|
3662
|
-
const isNewUrl = !announcedAuthUrls.has(normalizedUrl);
|
|
3663
|
-
if (isNewUrl) {
|
|
3664
|
-
announcedAuthUrls.add(normalizedUrl);
|
|
3665
|
-
session.ws.send(JSON.stringify({
|
|
3666
|
-
type: 'auth_url',
|
|
3667
|
-
url: normalizedUrl,
|
|
3668
|
-
autoOpen
|
|
3669
|
-
}));
|
|
3670
|
-
}
|
|
3671
|
-
|
|
3672
|
-
};
|
|
3673
|
-
|
|
3674
|
-
const normalizedDetectedUrls = extractUrlsFromText(urlDetectionBuffer)
|
|
3675
|
-
.map((url) => normalizeDetectedUrl(url))
|
|
3676
|
-
.filter(Boolean);
|
|
3677
|
-
|
|
3678
|
-
// Prefer the most complete URL if shorter prefix variants are also present.
|
|
3679
|
-
const dedupedDetectedUrls = Array.from(new Set(normalizedDetectedUrls)).filter((url, _, urls) =>
|
|
3680
|
-
!urls.some((otherUrl) => otherUrl !== url && otherUrl.startsWith(url))
|
|
3681
|
-
);
|
|
3682
|
-
|
|
3683
|
-
dedupedDetectedUrls.forEach((url) => emitAuthUrl(url, false));
|
|
3684
|
-
|
|
3685
|
-
if (shouldAutoOpenUrlFromOutput(cleanChunk) && dedupedDetectedUrls.length > 0) {
|
|
3686
|
-
const bestUrl = dedupedDetectedUrls.reduce((longest, current) =>
|
|
3687
|
-
current.length > longest.length ? current : longest
|
|
3688
|
-
);
|
|
3689
|
-
emitAuthUrl(bestUrl, true);
|
|
3690
|
-
}
|
|
3691
|
-
|
|
3692
|
-
// Send regular output
|
|
3693
|
-
if (outputData) {
|
|
3694
|
-
session.ws.send(JSON.stringify({
|
|
3695
|
-
type: 'output',
|
|
3696
|
-
data: outputData
|
|
3697
|
-
}));
|
|
3698
|
-
}
|
|
3708
|
+
outputBuffer += data;
|
|
3709
|
+
if (!outputFlushTimer) {
|
|
3710
|
+
outputFlushTimer = setTimeout(flushOutputBuffer, 8);
|
|
3699
3711
|
}
|
|
3700
3712
|
});
|
|
3701
3713
|
|
|
3702
3714
|
// Handle process exit
|
|
3703
3715
|
shellProcess.onExit((exitCode) => {
|
|
3704
3716
|
console.log('๐ Shell process exited with code:', exitCode.exitCode, 'signal:', exitCode.signal);
|
|
3717
|
+
if (outputFlushTimer) {
|
|
3718
|
+
clearTimeout(outputFlushTimer);
|
|
3719
|
+
outputFlushTimer = null;
|
|
3720
|
+
}
|
|
3721
|
+
flushOutputBuffer();
|
|
3705
3722
|
const session = ptySessionsMap.get(ptySessionKey);
|
|
3706
3723
|
if (session?.pty && session.pty !== shellProcess) {
|
|
3707
3724
|
console.log('โฉ๏ธ Ignoring stale PTY exit for replacement session:', ptySessionKey);
|
|
@@ -3794,6 +3811,11 @@ function handleShellConnection(ws, request) {
|
|
|
3794
3811
|
ws.on('close', () => {
|
|
3795
3812
|
console.log('๐ Shell client disconnected');
|
|
3796
3813
|
|
|
3814
|
+
if (outputFlushTimer) {
|
|
3815
|
+
clearTimeout(outputFlushTimer);
|
|
3816
|
+
outputFlushTimer = null;
|
|
3817
|
+
}
|
|
3818
|
+
|
|
3797
3819
|
if (ptySessionKey) {
|
|
3798
3820
|
const session = ptySessionsMap.get(ptySessionKey);
|
|
3799
3821
|
if (session) {
|
|
@@ -3803,6 +3825,14 @@ function handleShellConnection(ws, request) {
|
|
|
3803
3825
|
return;
|
|
3804
3826
|
}
|
|
3805
3827
|
|
|
3828
|
+
// Plain shells are cheap to recreate, so terminate them immediately
|
|
3829
|
+
// instead of keeping a 30-minute zombie PTY around.
|
|
3830
|
+
if (session.isPlainShell) {
|
|
3831
|
+
console.log('๐งน Terminating plain shell PTY on disconnect:', ptySessionKey);
|
|
3832
|
+
terminatePtySession(ptySessionKey, session, 'plain shell disconnect');
|
|
3833
|
+
return;
|
|
3834
|
+
}
|
|
3835
|
+
|
|
3806
3836
|
console.log('โณ PTY session kept alive, will timeout in 30 minutes:', ptySessionKey);
|
|
3807
3837
|
session.ws = null;
|
|
3808
3838
|
|
package/server/load-env.js
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
// Load environment variables from .env before other imports execute.
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
|
|
6
|
-
import { findAppRoot, getModuleDir } from './utils/runtime-paths.js';
|
|
7
|
-
|
|
8
|
-
const __dirname = getModuleDir(import.meta.url);
|
|
9
|
-
// Resolve the repo/app root via the nearest /server folder so this file keeps finding the
|
|
10
|
-
// same top-level .env file from both /server/load-env.js and /dist-server/server/load-env.js.
|
|
11
|
-
const APP_ROOT = findAppRoot(__dirname);
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
const envPath = path.join(APP_ROOT, '.env');
|
|
15
|
-
const envFile = fs.readFileSync(envPath, 'utf8');
|
|
16
|
-
envFile.split('\n').forEach(line => {
|
|
17
|
-
const trimmedLine = line.trim();
|
|
18
|
-
if (trimmedLine && !trimmedLine.startsWith('#')) {
|
|
19
|
-
const [key, ...valueParts] = trimmedLine.split('=');
|
|
20
|
-
if (key && valueParts.length > 0 && !process.env[key]) {
|
|
21
|
-
process.env[key] = valueParts.join('=').trim();
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
} catch (e) {
|
|
26
|
-
console.log('No .env file found or error reading it:', e.message);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Keep the default database in a stable user-level location so rebuilding dist-server
|
|
30
|
-
// never changes where the backend stores auth.db when DATABASE_PATH is not set explicitly.
|
|
31
|
-
const DEFAULT_DATABASE_PATH = path.join(os.homedir(), '.pixcode', 'auth.db');
|
|
32
|
-
|
|
33
|
-
if (!process.env.DATABASE_PATH) {
|
|
34
|
-
process.env.DATABASE_PATH = DEFAULT_DATABASE_PATH;
|
|
35
|
-
}
|
|
1
|
+
// Load environment variables from .env before other imports execute.
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
import { findAppRoot, getModuleDir } from './utils/runtime-paths.js';
|
|
7
|
+
|
|
8
|
+
const __dirname = getModuleDir(import.meta.url);
|
|
9
|
+
// Resolve the repo/app root via the nearest /server folder so this file keeps finding the
|
|
10
|
+
// same top-level .env file from both /server/load-env.js and /dist-server/server/load-env.js.
|
|
11
|
+
const APP_ROOT = findAppRoot(__dirname);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const envPath = path.join(APP_ROOT, '.env');
|
|
15
|
+
const envFile = fs.readFileSync(envPath, 'utf8');
|
|
16
|
+
envFile.split('\n').forEach(line => {
|
|
17
|
+
const trimmedLine = line.trim();
|
|
18
|
+
if (trimmedLine && !trimmedLine.startsWith('#')) {
|
|
19
|
+
const [key, ...valueParts] = trimmedLine.split('=');
|
|
20
|
+
if (key && valueParts.length > 0 && !process.env[key]) {
|
|
21
|
+
process.env[key] = valueParts.join('=').trim();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.log('No .env file found or error reading it:', e.message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Keep the default database in a stable user-level location so rebuilding dist-server
|
|
30
|
+
// never changes where the backend stores auth.db when DATABASE_PATH is not set explicitly.
|
|
31
|
+
const DEFAULT_DATABASE_PATH = path.join(os.homedir(), '.pixcode', 'auth.db');
|
|
32
|
+
|
|
33
|
+
if (!process.env.DATABASE_PATH) {
|
|
34
|
+
process.env.DATABASE_PATH = DEFAULT_DATABASE_PATH;
|
|
35
|
+
}
|