@pixelbyte-software/pixcode 1.54.10 → 1.54.11
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 +156 -156
- package/LICENSE +21 -718
- package/README.de.md +169 -169
- package/README.ja.md +167 -167
- package/README.ko.md +167 -167
- package/README.md +435 -418
- 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-DWOW_Jn0.js → index-Bcqs_1vc.js} +114 -114
- package/dist/clear-cache.html +85 -85
- package/dist/convert-icons.md +52 -52
- package/dist/docs.html +308 -308
- 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/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/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 +100 -100
- package/dist-server/server/daemon/manager.js +33 -33
- package/dist-server/server/daemon-manager.js +64 -64
- 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/public-api-manifest.js +51 -51
- package/package.json +224 -224
- 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/smoke/backend-resource-bounds.mjs +152 -152
- 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/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/mobile-ux.mjs +76 -76
- 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 +331 -331
- 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 +298 -298
- package/server/claude-sdk.js +927 -927
- package/server/cli.js +1106 -1106
- 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 +988 -988
- 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 +5671 -5671
- package/server/load-env.js +35 -35
- package/server/middleware/account-lockout.js +112 -112
- package/server/middleware/auth.js +277 -277
- package/server/middleware/rate-limiter.js +87 -87
- 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.test.ts +60 -60
- package/server/modules/orchestration/a2a/adapters/json-event.adapter.ts +101 -101
- 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/auth.middleware.ts +29 -29
- package/server/modules/orchestration/a2a/bus.ts +46 -46
- package/server/modules/orchestration/a2a/routes.ts +586 -586
- package/server/modules/orchestration/a2a/task-dispatcher.ts +345 -345
- 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/index.ts +99 -99
- 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 +81 -81
- package/server/modules/orchestration/tasks/orchestration-task.service.ts +201 -201
- 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 +2062 -2062
- 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-sessions.provider.ts +286 -286
- package/server/modules/providers/list/qwen/qwen-auth.provider.ts +146 -146
- package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +265 -265
- package/server/modules/providers/provider.registry.ts +40 -40
- package/server/modules/providers/provider.routes.ts +982 -982
- 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/tests/mcp.test.ts +293 -293
- package/server/openai-codex.js +468 -468
- package/server/opencode-cli.js +491 -491
- package/server/opencode-response-handler.js +111 -111
- package/server/projects.js +3135 -3135
- package/server/qwen-code-cli.js +410 -410
- package/server/routes/agent.js +1462 -1462
- package/server/routes/auth.js +298 -298
- package/server/routes/codex.js +20 -20
- package/server/routes/commands.js +581 -581
- package/server/routes/cursor.js +61 -61
- package/server/routes/diagnostics.js +44 -44
- package/server/routes/gemini.js +25 -25
- package/server/routes/git.js +1822 -1822
- package/server/routes/live-view.js +434 -434
- package/server/routes/mcp-utils.js +13 -13
- package/server/routes/messages.js +97 -97
- package/server/routes/network.js +143 -143
- package/server/routes/platformization.js +231 -231
- package/server/routes/plugins.js +690 -690
- package/server/routes/production-agent-loop.js +90 -90
- package/server/routes/projects.js +918 -918
- package/server/routes/public-api.js +34 -34
- package/server/routes/qwen.js +27 -27
- package/server/routes/remote.js +56 -56
- package/server/routes/settings.js +321 -321
- package/server/routes/telegram.js +163 -163
- 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 +403 -403
- 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 +1052 -1052
- 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 +181 -181
- package/server/services/qr-login.js +126 -126
- package/server/services/remote-connection.js +127 -127
- package/server/services/runtime-manager.js +323 -323
- package/server/services/startup-update.js +259 -259
- package/server/services/telegram/bot.js +393 -393
- package/server/services/telegram/control-center.js +2453 -2453
- package/server/services/telegram/telegram-gateway.js +314 -314
- package/server/services/telegram/telegram-http-client.js +201 -201
- package/server/services/telegram/translations.js +470 -470
- package/server/services/webhooks.js +216 -216
- package/server/sessionManager.js +225 -225
- package/server/setup-wizard.js +283 -283
- 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 +461 -461
- package/server/utils/plugin-process-manager.js +185 -185
- package/server/utils/port-access.js +227 -227
- package/server/utils/runtime-paths.js +37 -37
- package/server/utils/security-log.js +84 -84
- 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,84 +1,84 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
|
|
5
|
-
const LOG_DIR = path.join(os.homedir(), '.pixcode', 'logs');
|
|
6
|
-
const SECURITY_LOG_PATH = path.join(LOG_DIR, 'security.log');
|
|
7
|
-
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
8
|
-
const MAX_LOG_FILES = 5;
|
|
9
|
-
|
|
10
|
-
function ensureLogDir() {
|
|
11
|
-
try {
|
|
12
|
-
if (!fs.existsSync(LOG_DIR)) {
|
|
13
|
-
fs.mkdirSync(LOG_DIR, { recursive: true, mode: 0o700 });
|
|
14
|
-
}
|
|
15
|
-
} catch {
|
|
16
|
-
// Non-fatal: if we can't create the log dir, we fall back to console
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function sanitizeForLog(value) {
|
|
21
|
-
if (typeof value !== 'string') {
|
|
22
|
-
try {
|
|
23
|
-
value = String(value ?? '');
|
|
24
|
-
} catch {
|
|
25
|
-
return '[unserializable]';
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return value
|
|
29
|
-
.replace(/[\r\n\t]/g, ' ')
|
|
30
|
-
.replace(/\x1b\[[0-9;]*m/g, '')
|
|
31
|
-
.slice(0, 500);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function rotateLogIfNeeded() {
|
|
35
|
-
try {
|
|
36
|
-
if (!fs.existsSync(SECURITY_LOG_PATH)) return;
|
|
37
|
-
const stats = fs.statSync(SECURITY_LOG_PATH);
|
|
38
|
-
if (stats.size < MAX_LOG_SIZE) return;
|
|
39
|
-
for (let i = MAX_LOG_FILES - 1; i >= 1; i--) {
|
|
40
|
-
const oldPath = `${SECURITY_LOG_PATH}.${i}`;
|
|
41
|
-
const newPath = `${SECURITY_LOG_PATH}.${i + 1}`;
|
|
42
|
-
if (fs.existsSync(oldPath)) {
|
|
43
|
-
try { fs.renameSync(oldPath, newPath); } catch { /* noop */ }
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
try { fs.renameSync(SECURITY_LOG_PATH, `${SECURITY_LOG_PATH}.1`); } catch { /* noop */ }
|
|
47
|
-
} catch {
|
|
48
|
-
// Non-fatal
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function securityLog(event, details = {}) {
|
|
53
|
-
const entry = {
|
|
54
|
-
timestamp: new Date().toISOString(),
|
|
55
|
-
event,
|
|
56
|
-
ip: sanitizeForLog(details.ip),
|
|
57
|
-
userId: details.userId != null ? String(details.userId) : null,
|
|
58
|
-
username: details.username ? sanitizeForLog(details.username) : null,
|
|
59
|
-
endpoint: details.endpoint ? sanitizeForLog(details.endpoint) : null,
|
|
60
|
-
method: details.method ? sanitizeForLog(details.method) : null,
|
|
61
|
-
statusCode: details.statusCode || null,
|
|
62
|
-
reason: details.reason ? sanitizeForLog(details.reason) : null,
|
|
63
|
-
userAgent: details.userAgent ? sanitizeForLog(details.userAgent) : null,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const line = JSON.stringify(entry);
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
ensureLogDir();
|
|
70
|
-
rotateLogIfNeeded();
|
|
71
|
-
fs.appendFileSync(SECURITY_LOG_PATH, line + '\n', { mode: 0o600 });
|
|
72
|
-
} catch {
|
|
73
|
-
// Fall back to console if file logging fails
|
|
74
|
-
console.warn('[SECURITY]', line);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function getClientIp(req) {
|
|
79
|
-
const forwarded = req?.headers?.['x-forwarded-for'];
|
|
80
|
-
if (typeof forwarded === 'string' && forwarded.trim()) {
|
|
81
|
-
return forwarded.split(',')[0].trim();
|
|
82
|
-
}
|
|
83
|
-
return req?.socket?.remoteAddress || req?.ip || 'unknown';
|
|
84
|
-
}
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
|
|
5
|
+
const LOG_DIR = path.join(os.homedir(), '.pixcode', 'logs');
|
|
6
|
+
const SECURITY_LOG_PATH = path.join(LOG_DIR, 'security.log');
|
|
7
|
+
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
8
|
+
const MAX_LOG_FILES = 5;
|
|
9
|
+
|
|
10
|
+
function ensureLogDir() {
|
|
11
|
+
try {
|
|
12
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
13
|
+
fs.mkdirSync(LOG_DIR, { recursive: true, mode: 0o700 });
|
|
14
|
+
}
|
|
15
|
+
} catch {
|
|
16
|
+
// Non-fatal: if we can't create the log dir, we fall back to console
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function sanitizeForLog(value) {
|
|
21
|
+
if (typeof value !== 'string') {
|
|
22
|
+
try {
|
|
23
|
+
value = String(value ?? '');
|
|
24
|
+
} catch {
|
|
25
|
+
return '[unserializable]';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return value
|
|
29
|
+
.replace(/[\r\n\t]/g, ' ')
|
|
30
|
+
.replace(/\x1b\[[0-9;]*m/g, '')
|
|
31
|
+
.slice(0, 500);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function rotateLogIfNeeded() {
|
|
35
|
+
try {
|
|
36
|
+
if (!fs.existsSync(SECURITY_LOG_PATH)) return;
|
|
37
|
+
const stats = fs.statSync(SECURITY_LOG_PATH);
|
|
38
|
+
if (stats.size < MAX_LOG_SIZE) return;
|
|
39
|
+
for (let i = MAX_LOG_FILES - 1; i >= 1; i--) {
|
|
40
|
+
const oldPath = `${SECURITY_LOG_PATH}.${i}`;
|
|
41
|
+
const newPath = `${SECURITY_LOG_PATH}.${i + 1}`;
|
|
42
|
+
if (fs.existsSync(oldPath)) {
|
|
43
|
+
try { fs.renameSync(oldPath, newPath); } catch { /* noop */ }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
try { fs.renameSync(SECURITY_LOG_PATH, `${SECURITY_LOG_PATH}.1`); } catch { /* noop */ }
|
|
47
|
+
} catch {
|
|
48
|
+
// Non-fatal
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function securityLog(event, details = {}) {
|
|
53
|
+
const entry = {
|
|
54
|
+
timestamp: new Date().toISOString(),
|
|
55
|
+
event,
|
|
56
|
+
ip: sanitizeForLog(details.ip),
|
|
57
|
+
userId: details.userId != null ? String(details.userId) : null,
|
|
58
|
+
username: details.username ? sanitizeForLog(details.username) : null,
|
|
59
|
+
endpoint: details.endpoint ? sanitizeForLog(details.endpoint) : null,
|
|
60
|
+
method: details.method ? sanitizeForLog(details.method) : null,
|
|
61
|
+
statusCode: details.statusCode || null,
|
|
62
|
+
reason: details.reason ? sanitizeForLog(details.reason) : null,
|
|
63
|
+
userAgent: details.userAgent ? sanitizeForLog(details.userAgent) : null,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const line = JSON.stringify(entry);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
ensureLogDir();
|
|
70
|
+
rotateLogIfNeeded();
|
|
71
|
+
fs.appendFileSync(SECURITY_LOG_PATH, line + '\n', { mode: 0o600 });
|
|
72
|
+
} catch {
|
|
73
|
+
// Fall back to console if file logging fails
|
|
74
|
+
console.warn('[SECURITY]', line);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getClientIp(req) {
|
|
79
|
+
const forwarded = req?.headers?.['x-forwarded-for'];
|
|
80
|
+
if (typeof forwarded === 'string' && forwarded.trim()) {
|
|
81
|
+
return forwarded.split(',')[0].trim();
|
|
82
|
+
}
|
|
83
|
+
return req?.socket?.remoteAddress || req?.ip || 'unknown';
|
|
84
|
+
}
|
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
const ANSI_ESCAPE_SEQUENCE_REGEX = /\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07\x1B\r\n]*(?:\x07|\x1B\\|$)|P[\s\S]*?(?:\x1B\\|$)|\^[\s\S]*?(?:\x1B\\|$)|_[\s\S]*?(?:\x1B\\|$)|[@-Z\\-_])|\x9B[0-?]*[ -/]*[@-~]/g;
|
|
2
|
-
const TRAILING_URL_PUNCTUATION_REGEX = /[)\]}>.,;:!?]+$/;
|
|
3
|
-
|
|
4
|
-
function stripAnsiSequences(value = '') {
|
|
5
|
-
return value.replace(ANSI_ESCAPE_SEQUENCE_REGEX, '');
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function normalizeDetectedUrl(url) {
|
|
9
|
-
if (!url || typeof url !== 'string') return null;
|
|
10
|
-
|
|
11
|
-
const cleaned = url.trim().replace(TRAILING_URL_PUNCTUATION_REGEX, '');
|
|
12
|
-
if (!cleaned) return null;
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
const parsed = new URL(cleaned);
|
|
16
|
-
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
return parsed.toString();
|
|
20
|
-
} catch {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function extractUrlsFromText(value = '') {
|
|
26
|
-
const directMatches = value.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/gi) || [];
|
|
27
|
-
|
|
28
|
-
// Handle wrapped terminal URLs split across lines by terminal width.
|
|
29
|
-
const wrappedMatches = [];
|
|
30
|
-
const continuationRegex = /^[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+$/;
|
|
31
|
-
const lines = value.split(/\r?\n/);
|
|
32
|
-
for (let i = 0; i < lines.length; i++) {
|
|
33
|
-
const line = lines[i].trim();
|
|
34
|
-
const startMatch = line.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/i);
|
|
35
|
-
if (!startMatch) continue;
|
|
36
|
-
|
|
37
|
-
let combined = startMatch[0];
|
|
38
|
-
let j = i + 1;
|
|
39
|
-
while (j < lines.length) {
|
|
40
|
-
const continuation = lines[j].trim();
|
|
41
|
-
if (!continuation) break;
|
|
42
|
-
if (!continuationRegex.test(continuation)) break;
|
|
43
|
-
combined += continuation;
|
|
44
|
-
j++;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
wrappedMatches.push(combined.replace(/\r?\n\s*/g, ''));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return Array.from(new Set([...directMatches, ...wrappedMatches]));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function shouldAutoOpenUrlFromOutput(value = '') {
|
|
54
|
-
const normalized = value.toLowerCase();
|
|
55
|
-
return (
|
|
56
|
-
normalized.includes('browser didn\'t open') ||
|
|
57
|
-
normalized.includes('open this url') ||
|
|
58
|
-
normalized.includes('continue in your browser') ||
|
|
59
|
-
normalized.includes('press enter to open') ||
|
|
60
|
-
normalized.includes('open_url:')
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export {
|
|
65
|
-
ANSI_ESCAPE_SEQUENCE_REGEX,
|
|
66
|
-
TRAILING_URL_PUNCTUATION_REGEX,
|
|
67
|
-
stripAnsiSequences,
|
|
68
|
-
normalizeDetectedUrl,
|
|
69
|
-
extractUrlsFromText,
|
|
70
|
-
shouldAutoOpenUrlFromOutput
|
|
71
|
-
};
|
|
1
|
+
const ANSI_ESCAPE_SEQUENCE_REGEX = /\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07\x1B\r\n]*(?:\x07|\x1B\\|$)|P[\s\S]*?(?:\x1B\\|$)|\^[\s\S]*?(?:\x1B\\|$)|_[\s\S]*?(?:\x1B\\|$)|[@-Z\\-_])|\x9B[0-?]*[ -/]*[@-~]/g;
|
|
2
|
+
const TRAILING_URL_PUNCTUATION_REGEX = /[)\]}>.,;:!?]+$/;
|
|
3
|
+
|
|
4
|
+
function stripAnsiSequences(value = '') {
|
|
5
|
+
return value.replace(ANSI_ESCAPE_SEQUENCE_REGEX, '');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function normalizeDetectedUrl(url) {
|
|
9
|
+
if (!url || typeof url !== 'string') return null;
|
|
10
|
+
|
|
11
|
+
const cleaned = url.trim().replace(TRAILING_URL_PUNCTUATION_REGEX, '');
|
|
12
|
+
if (!cleaned) return null;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const parsed = new URL(cleaned);
|
|
16
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return parsed.toString();
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function extractUrlsFromText(value = '') {
|
|
26
|
+
const directMatches = value.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/gi) || [];
|
|
27
|
+
|
|
28
|
+
// Handle wrapped terminal URLs split across lines by terminal width.
|
|
29
|
+
const wrappedMatches = [];
|
|
30
|
+
const continuationRegex = /^[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+$/;
|
|
31
|
+
const lines = value.split(/\r?\n/);
|
|
32
|
+
for (let i = 0; i < lines.length; i++) {
|
|
33
|
+
const line = lines[i].trim();
|
|
34
|
+
const startMatch = line.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/i);
|
|
35
|
+
if (!startMatch) continue;
|
|
36
|
+
|
|
37
|
+
let combined = startMatch[0];
|
|
38
|
+
let j = i + 1;
|
|
39
|
+
while (j < lines.length) {
|
|
40
|
+
const continuation = lines[j].trim();
|
|
41
|
+
if (!continuation) break;
|
|
42
|
+
if (!continuationRegex.test(continuation)) break;
|
|
43
|
+
combined += continuation;
|
|
44
|
+
j++;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
wrappedMatches.push(combined.replace(/\r?\n\s*/g, ''));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return Array.from(new Set([...directMatches, ...wrappedMatches]));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function shouldAutoOpenUrlFromOutput(value = '') {
|
|
54
|
+
const normalized = value.toLowerCase();
|
|
55
|
+
return (
|
|
56
|
+
normalized.includes('browser didn\'t open') ||
|
|
57
|
+
normalized.includes('open this url') ||
|
|
58
|
+
normalized.includes('continue in your browser') ||
|
|
59
|
+
normalized.includes('press enter to open') ||
|
|
60
|
+
normalized.includes('open_url:')
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
ANSI_ESCAPE_SEQUENCE_REGEX,
|
|
66
|
+
TRAILING_URL_PUNCTUATION_REGEX,
|
|
67
|
+
stripAnsiSequences,
|
|
68
|
+
normalizeDetectedUrl,
|
|
69
|
+
extractUrlsFromText,
|
|
70
|
+
shouldAutoOpenUrlFromOutput
|
|
71
|
+
};
|
package/server/vite-daemon.js
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import process from 'node:process';
|
|
4
|
-
|
|
5
|
-
import { createServer } from 'vite';
|
|
6
|
-
|
|
7
|
-
const DEFAULT_PORT = 5173;
|
|
8
|
-
const DEFAULT_HOST = '0.0.0.0';
|
|
9
|
-
|
|
10
|
-
function parseArgs(argv) {
|
|
11
|
-
const parsed = {
|
|
12
|
-
host: process.env.VITE_HOST || process.env.HOST || DEFAULT_HOST,
|
|
13
|
-
port: Number(process.env.VITE_PORT || DEFAULT_PORT),
|
|
14
|
-
strictPort: true,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
for (let i = 0; i < argv.length; i++) {
|
|
18
|
-
const arg = argv[i];
|
|
19
|
-
if (arg === '--host') {
|
|
20
|
-
parsed.host = argv[++i];
|
|
21
|
-
} else if (arg.startsWith('--host=')) {
|
|
22
|
-
parsed.host = arg.split('=')[1];
|
|
23
|
-
} else if (arg === '--port' || arg === '-p') {
|
|
24
|
-
parsed.port = Number(argv[++i]);
|
|
25
|
-
} else if (arg.startsWith('--port=')) {
|
|
26
|
-
parsed.port = Number(arg.split('=')[1]);
|
|
27
|
-
} else if (arg === '--strictPort') {
|
|
28
|
-
parsed.strictPort = true;
|
|
29
|
-
} else if (arg === '--no-strictPort') {
|
|
30
|
-
parsed.strictPort = false;
|
|
31
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
32
|
-
console.log('Usage: node server/vite-daemon.js [--host 0.0.0.0] [--port 5173] [--strictPort]');
|
|
33
|
-
process.exit(0);
|
|
34
|
-
} else {
|
|
35
|
-
throw new Error(`Unknown argument: ${arg}`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (!Number.isInteger(parsed.port) || parsed.port < 1 || parsed.port > 65535) {
|
|
40
|
-
throw new Error(`Invalid port "${parsed.port}". Expected an integer between 1 and 65535.`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return parsed;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function main() {
|
|
47
|
-
const options = parseArgs(process.argv.slice(2));
|
|
48
|
-
const server = await createServer({
|
|
49
|
-
root: process.cwd(),
|
|
50
|
-
server: {
|
|
51
|
-
host: options.host,
|
|
52
|
-
port: options.port,
|
|
53
|
-
strictPort: options.strictPort,
|
|
54
|
-
},
|
|
55
|
-
clearScreen: false,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
await server.listen();
|
|
59
|
-
server.printUrls();
|
|
60
|
-
|
|
61
|
-
const shutdown = async (signal) => {
|
|
62
|
-
console.log(`[INFO] Frontend daemon received ${signal}, shutting down...`);
|
|
63
|
-
await server.close();
|
|
64
|
-
process.exit(0);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
process.once('SIGTERM', () => {
|
|
68
|
-
void shutdown('SIGTERM');
|
|
69
|
-
});
|
|
70
|
-
process.once('SIGINT', () => {
|
|
71
|
-
void shutdown('SIGINT');
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
main().catch((error) => {
|
|
76
|
-
const message = error?.stack || error?.message || String(error);
|
|
77
|
-
console.error(`[ERROR] Frontend daemon failed to start: ${message}`);
|
|
78
|
-
process.exit(1);
|
|
79
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
|
|
5
|
+
import { createServer } from 'vite';
|
|
6
|
+
|
|
7
|
+
const DEFAULT_PORT = 5173;
|
|
8
|
+
const DEFAULT_HOST = '0.0.0.0';
|
|
9
|
+
|
|
10
|
+
function parseArgs(argv) {
|
|
11
|
+
const parsed = {
|
|
12
|
+
host: process.env.VITE_HOST || process.env.HOST || DEFAULT_HOST,
|
|
13
|
+
port: Number(process.env.VITE_PORT || DEFAULT_PORT),
|
|
14
|
+
strictPort: true,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < argv.length; i++) {
|
|
18
|
+
const arg = argv[i];
|
|
19
|
+
if (arg === '--host') {
|
|
20
|
+
parsed.host = argv[++i];
|
|
21
|
+
} else if (arg.startsWith('--host=')) {
|
|
22
|
+
parsed.host = arg.split('=')[1];
|
|
23
|
+
} else if (arg === '--port' || arg === '-p') {
|
|
24
|
+
parsed.port = Number(argv[++i]);
|
|
25
|
+
} else if (arg.startsWith('--port=')) {
|
|
26
|
+
parsed.port = Number(arg.split('=')[1]);
|
|
27
|
+
} else if (arg === '--strictPort') {
|
|
28
|
+
parsed.strictPort = true;
|
|
29
|
+
} else if (arg === '--no-strictPort') {
|
|
30
|
+
parsed.strictPort = false;
|
|
31
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
32
|
+
console.log('Usage: node server/vite-daemon.js [--host 0.0.0.0] [--port 5173] [--strictPort]');
|
|
33
|
+
process.exit(0);
|
|
34
|
+
} else {
|
|
35
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!Number.isInteger(parsed.port) || parsed.port < 1 || parsed.port > 65535) {
|
|
40
|
+
throw new Error(`Invalid port "${parsed.port}". Expected an integer between 1 and 65535.`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return parsed;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function main() {
|
|
47
|
+
const options = parseArgs(process.argv.slice(2));
|
|
48
|
+
const server = await createServer({
|
|
49
|
+
root: process.cwd(),
|
|
50
|
+
server: {
|
|
51
|
+
host: options.host,
|
|
52
|
+
port: options.port,
|
|
53
|
+
strictPort: options.strictPort,
|
|
54
|
+
},
|
|
55
|
+
clearScreen: false,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await server.listen();
|
|
59
|
+
server.printUrls();
|
|
60
|
+
|
|
61
|
+
const shutdown = async (signal) => {
|
|
62
|
+
console.log(`[INFO] Frontend daemon received ${signal}, shutting down...`);
|
|
63
|
+
await server.close();
|
|
64
|
+
process.exit(0);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
process.once('SIGTERM', () => {
|
|
68
|
+
void shutdown('SIGTERM');
|
|
69
|
+
});
|
|
70
|
+
process.once('SIGINT', () => {
|
|
71
|
+
void shutdown('SIGINT');
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
main().catch((error) => {
|
|
76
|
+
const message = error?.stack || error?.message || String(error);
|
|
77
|
+
console.error(`[ERROR] Frontend daemon failed to start: ${message}`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
});
|