@pixelbyte-software/pixcode 1.34.0 → 1.35.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/dist/api-docs.html +162 -9
- package/dist/assets/index-B8w57E1r.css +32 -0
- package/dist/assets/index-Djuh0wHV.js +854 -0
- package/dist/favicon.svg +8 -8
- 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/index.html +2 -2
- package/dist/logo.svg +12 -12
- package/dist/openapi.yaml +383 -1
- package/dist-server/server/claude-sdk.js +38 -7
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cli.js +12 -17
- package/dist-server/server/cli.js.map +1 -1
- package/dist-server/server/daemon-manager.js +98 -51
- package/dist-server/server/daemon-manager.js.map +1 -1
- package/dist-server/server/database/json-store.js +8 -5
- package/dist-server/server/database/json-store.js.map +1 -1
- package/dist-server/server/index.js +31 -10
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapter-registry.js +45 -19
- package/dist-server/server/modules/orchestration/a2a/adapter-registry.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js +202 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js +205 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js +205 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js +205 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js +205 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/routes.js +298 -34
- package/dist-server/server/modules/orchestration/a2a/routes.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/task-store.js +144 -0
- package/dist-server/server/modules/orchestration/a2a/task-store.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/validator.js +16 -0
- package/dist-server/server/modules/orchestration/a2a/validator.js.map +1 -1
- package/dist-server/server/modules/orchestration/index.js +14 -0
- package/dist-server/server/modules/orchestration/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/preview/port-watcher.js +90 -0
- package/dist-server/server/modules/orchestration/preview/port-watcher.js.map +1 -0
- package/dist-server/server/modules/orchestration/preview/preview-proxy.js +58 -0
- package/dist-server/server/modules/orchestration/preview/preview-proxy.js.map +1 -0
- package/dist-server/server/modules/orchestration/preview/types.js +2 -0
- package/dist-server/server/modules/orchestration/preview/types.js.map +1 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js +37 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js.map +1 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js +68 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js.map +1 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js +128 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js.map +1 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.types.js +2 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.types.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/built-in-workflows.js +126 -0
- package/dist-server/server/modules/orchestration/workflows/built-in-workflows.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +1047 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-store.js +76 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-store.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow.routes.js +151 -0
- package/dist-server/server/modules/orchestration/workflows/workflow.routes.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow.types.js +2 -0
- package/dist-server/server/modules/orchestration/workflows/workflow.types.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workspace-target.js +98 -0
- package/dist-server/server/modules/orchestration/workflows/workspace-target.js.map +1 -0
- package/dist-server/server/modules/orchestration/workspace/docker-workspace.js +122 -0
- package/dist-server/server/modules/orchestration/workspace/docker-workspace.js.map +1 -0
- package/dist-server/server/modules/orchestration/workspace/path-safety.js +48 -0
- package/dist-server/server/modules/orchestration/workspace/path-safety.js.map +1 -0
- package/dist-server/server/modules/orchestration/workspace/types.js +11 -0
- package/dist-server/server/modules/orchestration/workspace/types.js.map +1 -0
- package/dist-server/server/modules/orchestration/workspace/workspace-manager.js +80 -0
- package/dist-server/server/modules/orchestration/workspace/workspace-manager.js.map +1 -0
- package/dist-server/server/modules/orchestration/workspace/worktree-workspace.js +96 -0
- package/dist-server/server/modules/orchestration/workspace/worktree-workspace.js.map +1 -0
- package/dist-server/server/modules/providers/index.js +3 -0
- package/dist-server/server/modules/providers/index.js.map +1 -0
- package/dist-server/server/openai-codex.js +35 -4
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/routes/taskmaster.js +106 -89
- package/dist-server/server/routes/taskmaster.js.map +1 -1
- package/package.json +3 -1
- package/scripts/smoke/a2a-roundtrip.mjs +167 -98
- package/scripts/smoke/orchestration-api.mjs +172 -0
- package/scripts/smoke/orchestration-live-run.mjs +176 -0
- package/server/claude-sdk.js +48 -7
- package/server/cli.js +12 -17
- package/server/daemon-manager.js +90 -51
- package/server/database/db.js +794 -794
- package/server/database/json-store.js +8 -5
- package/server/index.js +40 -9
- package/server/modules/orchestration/a2a/adapter-registry.ts +108 -58
- package/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.ts +55 -49
- package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +284 -283
- package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +244 -0
- package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +249 -0
- package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +248 -0
- package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +248 -0
- package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +248 -0
- 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 +577 -264
- package/server/modules/orchestration/a2a/task-store.ts +178 -0
- package/server/modules/orchestration/a2a/types.ts +125 -111
- package/server/modules/orchestration/a2a/validator.ts +113 -90
- package/server/modules/orchestration/index.ts +66 -26
- package/server/modules/orchestration/preview/port-watcher.ts +112 -0
- package/server/modules/orchestration/preview/preview-proxy.ts +60 -0
- package/server/modules/orchestration/preview/types.ts +19 -0
- package/server/modules/orchestration/tasks/orchestration-task-store.ts +45 -0
- package/server/modules/orchestration/tasks/orchestration-task.routes.ts +73 -0
- package/server/modules/orchestration/tasks/orchestration-task.service.ts +145 -0
- package/server/modules/orchestration/tasks/orchestration-task.types.ts +29 -0
- package/server/modules/orchestration/workflows/built-in-workflows.ts +127 -0
- package/server/modules/orchestration/workflows/workflow-runner.ts +1206 -0
- package/server/modules/orchestration/workflows/workflow-store.ts +97 -0
- package/server/modules/orchestration/workflows/workflow.routes.ts +169 -0
- package/server/modules/orchestration/workflows/workflow.types.ts +70 -0
- package/server/modules/orchestration/workflows/workspace-target.ts +120 -0
- package/server/modules/orchestration/workspace/docker-workspace.ts +135 -0
- package/server/modules/orchestration/workspace/path-safety.ts +55 -0
- package/server/modules/orchestration/workspace/types.ts +52 -0
- package/server/modules/orchestration/workspace/workspace-manager.ts +97 -0
- package/server/modules/orchestration/workspace/worktree-workspace.ts +125 -0
- package/server/modules/providers/index.ts +2 -0
- package/server/modules/providers/list/opencode/opencode-auth.provider.ts +130 -130
- package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +126 -126
- package/server/modules/providers/list/opencode/opencode.provider.ts +29 -29
- package/server/modules/providers/list/qwen/qwen-auth.provider.ts +145 -145
- package/server/modules/providers/list/qwen/qwen-mcp.provider.ts +114 -114
- package/server/modules/providers/list/qwen/qwen.provider.ts +21 -21
- package/server/modules/providers/shared/provider-configs.ts +142 -142
- package/server/openai-codex.js +40 -4
- package/server/qwen-code-cli.js +395 -395
- package/server/qwen-response-handler.js +73 -73
- package/server/routes/qwen.js +27 -27
- package/server/routes/taskmaster.js +116 -91
- package/server/services/external-access.js +171 -171
- package/server/services/provider-models.js +381 -381
- package/server/services/telegram/telegram-http-client.js +130 -130
- package/server/services/vapid-keys.js +36 -36
- package/server/utils/port-access.js +209 -209
- package/dist/assets/index-B1ghfb4w.css +0 -32
- package/dist/assets/index-BvClqlMf.js +0 -852
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const baseUrl = process.env.PIXCODE_BASE_URL || 'http://127.0.0.1:3001';
|
|
4
|
+
const apiKey = process.env.PIXCODE_API_KEY;
|
|
5
|
+
const goal = process.env.PIXCODE_LIVE_GOAL;
|
|
6
|
+
const timeoutMs = Number.parseInt(process.env.PIXCODE_LIVE_TIMEOUT_MS || '1200000', 10);
|
|
7
|
+
const minAgentOutputs = Number.parseInt(process.env.PIXCODE_LIVE_MIN_AGENT_OUTPUTS || '2', 10);
|
|
8
|
+
const workflowId = process.env.PIXCODE_LIVE_WORKFLOW_ID || 'agent_team';
|
|
9
|
+
|
|
10
|
+
if (!apiKey) {
|
|
11
|
+
console.error('PIXCODE_API_KEY is required.');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!goal) {
|
|
16
|
+
console.error('PIXCODE_LIVE_GOAL is required.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseJsonEnv(name) {
|
|
21
|
+
const raw = process.env[name];
|
|
22
|
+
if (!raw) {
|
|
23
|
+
throw new Error(`${name} is required.`);
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(raw);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw new Error(`${name} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function assert(condition, message) {
|
|
33
|
+
if (!condition) throw new Error(message);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function headers(extra = {}) {
|
|
37
|
+
return {
|
|
38
|
+
authorization: `Bearer ${apiKey}`,
|
|
39
|
+
'content-type': 'application/json',
|
|
40
|
+
...extra,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function request(path, options = {}) {
|
|
45
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
46
|
+
...options,
|
|
47
|
+
headers: headers(options.headers || {}),
|
|
48
|
+
});
|
|
49
|
+
const text = await response.text();
|
|
50
|
+
const body = text ? JSON.parse(text) : null;
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
throw new Error(`${options.method || 'GET'} ${path} failed: ${response.status} ${text}`);
|
|
53
|
+
}
|
|
54
|
+
return body;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function terminal(status) {
|
|
58
|
+
return status === 'completed' || status === 'failed' || status === 'canceled';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function agentTextCount(run) {
|
|
62
|
+
return (run.nodeRuns || []).filter((node) =>
|
|
63
|
+
(node.messages || []).some((message) => message.role !== 'user' && message.text?.trim()) ||
|
|
64
|
+
Boolean(node.outputText?.trim()),
|
|
65
|
+
).length;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function compactNodes(run) {
|
|
69
|
+
return (run.nodeRuns || []).map((node) => ({
|
|
70
|
+
nodeId: node.nodeId,
|
|
71
|
+
label: node.agentLabel,
|
|
72
|
+
status: node.status,
|
|
73
|
+
taskId: node.a2aTaskId,
|
|
74
|
+
messageCount: (node.messages || []).filter((message) => message.role !== 'user').length,
|
|
75
|
+
hasOutput: Boolean(node.outputText?.trim()),
|
|
76
|
+
error: node.error,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function cancelRun(runId) {
|
|
81
|
+
try {
|
|
82
|
+
await request(`/api/orchestration/workflows/runs/${encodeURIComponent(runId)}/cancel`, {
|
|
83
|
+
method: 'POST',
|
|
84
|
+
body: '{}',
|
|
85
|
+
});
|
|
86
|
+
} catch {
|
|
87
|
+
// Best-effort cleanup only.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function main() {
|
|
92
|
+
const agents = parseJsonEnv('PIXCODE_LIVE_AGENTS_JSON');
|
|
93
|
+
assert(Array.isArray(agents) && agents.length > 0, 'PIXCODE_LIVE_AGENTS_JSON must be a non-empty array.');
|
|
94
|
+
|
|
95
|
+
const settings = process.env.PIXCODE_LIVE_SETTINGS_JSON
|
|
96
|
+
? JSON.parse(process.env.PIXCODE_LIVE_SETTINGS_JSON)
|
|
97
|
+
: {};
|
|
98
|
+
const metadata = {
|
|
99
|
+
agents,
|
|
100
|
+
projectPath: process.env.PIXCODE_LIVE_PROJECT_PATH,
|
|
101
|
+
projectId: process.env.PIXCODE_LIVE_PROJECT_ID,
|
|
102
|
+
settings: {
|
|
103
|
+
maxParallelAgents: Number.parseInt(process.env.PIXCODE_LIVE_MAX_PARALLEL || '3', 10),
|
|
104
|
+
isolation: process.env.PIXCODE_LIVE_ISOLATION || 'host',
|
|
105
|
+
keepWorkspace: process.env.PIXCODE_LIVE_KEEP_WORKSPACE !== 'false',
|
|
106
|
+
...settings,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const preview = await request(`/api/orchestration/workflows/${encodeURIComponent(workflowId)}/preview`, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
body: JSON.stringify({ metadata }),
|
|
113
|
+
});
|
|
114
|
+
assert(preview.nodeCount >= agents.length + 1, 'Preview did not expand enough nodes for the requested agents.');
|
|
115
|
+
console.log(JSON.stringify({ event: 'preview', nodeIds: preview.nodes.map((node) => node.id) }));
|
|
116
|
+
|
|
117
|
+
const started = await request(`/api/orchestration/workflows/${encodeURIComponent(workflowId)}/runs`, {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
body: JSON.stringify({ input: goal, metadata }),
|
|
120
|
+
});
|
|
121
|
+
const runId = started.id;
|
|
122
|
+
assert(runId, 'Run id missing from start response.');
|
|
123
|
+
console.log(JSON.stringify({ event: 'started', runId, contextId: started.contextId }));
|
|
124
|
+
|
|
125
|
+
const deadline = Date.now() + timeoutMs;
|
|
126
|
+
let lastSignature = '';
|
|
127
|
+
let latest = started;
|
|
128
|
+
while (!terminal(latest.status) && Date.now() < deadline) {
|
|
129
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
130
|
+
latest = await request(`/api/orchestration/workflows/runs/${encodeURIComponent(runId)}`);
|
|
131
|
+
const nodes = compactNodes(latest);
|
|
132
|
+
const signature = JSON.stringify(nodes.map((node) => [
|
|
133
|
+
node.nodeId,
|
|
134
|
+
node.status,
|
|
135
|
+
node.messageCount,
|
|
136
|
+
node.hasOutput,
|
|
137
|
+
node.error,
|
|
138
|
+
]));
|
|
139
|
+
if (signature !== lastSignature) {
|
|
140
|
+
lastSignature = signature;
|
|
141
|
+
console.log(JSON.stringify({
|
|
142
|
+
event: 'progress',
|
|
143
|
+
status: latest.status,
|
|
144
|
+
agentOutputs: agentTextCount(latest),
|
|
145
|
+
nodes,
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!terminal(latest.status)) {
|
|
151
|
+
await cancelRun(runId);
|
|
152
|
+
throw new Error(`Run ${runId} did not finish before ${timeoutMs}ms; canceled.`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const outputCount = agentTextCount(latest);
|
|
156
|
+
if (outputCount < minAgentOutputs) {
|
|
157
|
+
throw new Error(`Expected at least ${minAgentOutputs} nodes with agent output; got ${outputCount}.`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (latest.status !== 'completed') {
|
|
161
|
+
throw new Error(`Run ended with ${latest.status}: ${JSON.stringify(compactNodes(latest))}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log(JSON.stringify({
|
|
165
|
+
event: 'completed',
|
|
166
|
+
runId,
|
|
167
|
+
contextId: latest.contextId,
|
|
168
|
+
outputCount,
|
|
169
|
+
nodes: compactNodes(latest),
|
|
170
|
+
}, null, 2));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
main().catch((error) => {
|
|
174
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
175
|
+
process.exit(1);
|
|
176
|
+
});
|
package/server/claude-sdk.js
CHANGED
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
* - WebSocket message streaming
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
16
15
|
import crypto from 'crypto';
|
|
17
|
-
import { promises as fs } from 'fs';
|
|
16
|
+
import { existsSync, readFileSync, promises as fs } from 'fs';
|
|
18
17
|
import path from 'path';
|
|
19
18
|
import os from 'os';
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
21
|
+
|
|
21
22
|
import {
|
|
22
23
|
createNotificationEvent,
|
|
23
24
|
notifyRunFailed,
|
|
@@ -140,6 +141,43 @@ function matchesToolPermission(entry, toolName, input) {
|
|
|
140
141
|
return false;
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
function readClaudeSettingsEnv(filePath) {
|
|
145
|
+
try {
|
|
146
|
+
if (!existsSync(filePath)) return {};
|
|
147
|
+
const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
148
|
+
const env = parsed?.env;
|
|
149
|
+
if (!env || typeof env !== 'object') return {};
|
|
150
|
+
|
|
151
|
+
return Object.fromEntries(
|
|
152
|
+
Object.entries(env)
|
|
153
|
+
.filter(([key, value]) =>
|
|
154
|
+
typeof value === 'string' &&
|
|
155
|
+
value.trim() &&
|
|
156
|
+
!key.startsWith('//'),
|
|
157
|
+
),
|
|
158
|
+
);
|
|
159
|
+
} catch {
|
|
160
|
+
return {};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function loadClaudeSettingsEnv(cwd) {
|
|
165
|
+
const files = [
|
|
166
|
+
path.join(os.homedir(), '.claude', 'settings.json'),
|
|
167
|
+
];
|
|
168
|
+
if (cwd) {
|
|
169
|
+
files.push(
|
|
170
|
+
path.join(cwd, '.claude', 'settings.json'),
|
|
171
|
+
path.join(cwd, '.claude', 'settings.local.json'),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return files.reduce((env, filePath) => ({
|
|
176
|
+
...env,
|
|
177
|
+
...readClaudeSettingsEnv(filePath),
|
|
178
|
+
}), {});
|
|
179
|
+
}
|
|
180
|
+
|
|
143
181
|
/**
|
|
144
182
|
* Maps CLI options to SDK-compatible options format
|
|
145
183
|
* @param {Object} options - CLI options
|
|
@@ -154,7 +192,7 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
154
192
|
// Since claude-agent-sdk 0.2.113+, options.env REPLACES process.env in the subprocess
|
|
155
193
|
// instead of overlaying it. Without spreading process.env here, users who rely on
|
|
156
194
|
// ANTHROPIC_BASE_URL, HTTP(S)_PROXY, etc. would silently lose those settings.
|
|
157
|
-
sdkOptions.env = { ...process.env };
|
|
195
|
+
sdkOptions.env = { ...process.env, ...loadClaudeSettingsEnv(cwd) };
|
|
158
196
|
|
|
159
197
|
// Claude Code on Windows hard-requires a POSIX bash (typically from Git
|
|
160
198
|
// for Windows) and reads its path from CLAUDE_CODE_GIT_BASH_PATH. If the
|
|
@@ -228,9 +266,12 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
228
266
|
|
|
229
267
|
sdkOptions.disallowedTools = settings.disallowedTools || [];
|
|
230
268
|
|
|
231
|
-
// Map model
|
|
232
|
-
//
|
|
233
|
-
|
|
269
|
+
// Map model only when Pixcode explicitly passes one. If omitted, let
|
|
270
|
+
// Claude Code load the user's own project/user/local settings, including
|
|
271
|
+
// ~/.claude/settings.json "model".
|
|
272
|
+
if (typeof options.model === 'string' && options.model.trim()) {
|
|
273
|
+
sdkOptions.model = options.model.trim();
|
|
274
|
+
}
|
|
234
275
|
// Model logged at query start below
|
|
235
276
|
|
|
236
277
|
// Map system prompt configuration
|
package/server/cli.js
CHANGED
|
@@ -185,7 +185,7 @@ Examples:
|
|
|
185
185
|
$ pixcode --port 8080 # Start on port 8080
|
|
186
186
|
$ pixcode --no-daemon # Force foreground mode
|
|
187
187
|
$ sudo pixcode daemon install --mode system --port 3001
|
|
188
|
-
$ pixcode daemon install --mode user --port 3001 --
|
|
188
|
+
$ pixcode daemon install --mode user --port 3001 --single-port
|
|
189
189
|
$ pixcode daemon doctor --mode system
|
|
190
190
|
$ pixcode update --restart-daemon
|
|
191
191
|
$ pixcode sandbox ~/my-project # Run in a Docker sandbox
|
|
@@ -696,9 +696,8 @@ function printSystemDaemonActiveNotice(port) {
|
|
|
696
696
|
console.log(`${c.info('[INFO]')} Logs: ${c.bright(logsCommand)}`);
|
|
697
697
|
}
|
|
698
698
|
|
|
699
|
-
function printUserDaemonActiveNotice(port
|
|
699
|
+
function printUserDaemonActiveNotice(port) {
|
|
700
700
|
const effectivePort = Number(port) || 3001;
|
|
701
|
-
const effectiveFrontendPort = Number(frontendPort) || 5173;
|
|
702
701
|
const statusCommand = buildDaemonCliCommand(
|
|
703
702
|
{ subcommand: 'status', mode: 'user' },
|
|
704
703
|
DAEMON_COMMAND_CONTEXT
|
|
@@ -712,8 +711,7 @@ function printUserDaemonActiveNotice(port, frontendPort) {
|
|
|
712
711
|
DAEMON_COMMAND_CONTEXT
|
|
713
712
|
);
|
|
714
713
|
console.log(`${c.ok('[OK]')} User daemon is active for this account.`);
|
|
715
|
-
console.log(`${c.info('[INFO]')}
|
|
716
|
-
console.log(`${c.info('[INFO]')} Frontend: ${c.bright(`http://localhost:${effectiveFrontendPort}`)}`);
|
|
714
|
+
console.log(`${c.info('[INFO]')} UI: ${c.bright(`http://localhost:${effectivePort}`)}`);
|
|
717
715
|
console.log(`${c.info('[INFO]')} Status: ${c.bright(statusCommand)}`);
|
|
718
716
|
console.log(`${c.info('[INFO]')} Stop: ${c.bright(stopCommand)}`);
|
|
719
717
|
console.log(`${c.info('[INFO]')} Logs: ${c.bright(logsCommand)}`);
|
|
@@ -725,7 +723,7 @@ function isSystemPermissionError(error) {
|
|
|
725
723
|
return /(access denied|permission denied|must be root|interactive authentication required|not permitted|failed to connect to bus|operation not permitted|authentication is required|polkit)/i.test(message);
|
|
726
724
|
}
|
|
727
725
|
|
|
728
|
-
function buildAutoInstallArgs(mode, options
|
|
726
|
+
function buildAutoInstallArgs(mode, options) {
|
|
729
727
|
const args = ['install', `--mode=${mode}`];
|
|
730
728
|
if (options.serverPort) {
|
|
731
729
|
args.push('--port', String(options.serverPort));
|
|
@@ -733,9 +731,7 @@ function buildAutoInstallArgs(mode, options, frontendPort) {
|
|
|
733
731
|
if (options.databasePath) {
|
|
734
732
|
args.push('--database-path', String(options.databasePath));
|
|
735
733
|
}
|
|
736
|
-
|
|
737
|
-
args.push('--frontend-port', String(frontendPort));
|
|
738
|
-
}
|
|
734
|
+
args.push('--single-port');
|
|
739
735
|
return args;
|
|
740
736
|
}
|
|
741
737
|
|
|
@@ -748,9 +744,8 @@ async function maybeAutoDaemonStart(options = {}) {
|
|
|
748
744
|
|
|
749
745
|
process.env.PIXCODE_DAEMON_ATTEMPTED = '1';
|
|
750
746
|
const daemonPort = Number(options.serverPort || process.env.SERVER_PORT || process.env.PORT || '3001');
|
|
751
|
-
const
|
|
752
|
-
const
|
|
753
|
-
const userArgs = buildAutoInstallArgs('user', options, frontendPort);
|
|
747
|
+
const systemArgs = buildAutoInstallArgs('system', options);
|
|
748
|
+
const userArgs = buildAutoInstallArgs('user', options);
|
|
754
749
|
|
|
755
750
|
try {
|
|
756
751
|
console.log(`${c.info('[INFO]')} Linux detected. Enforcing system daemon mode for Pixcode...`);
|
|
@@ -773,7 +768,7 @@ async function maybeAutoDaemonStart(options = {}) {
|
|
|
773
768
|
{
|
|
774
769
|
subcommand: 'install',
|
|
775
770
|
mode: 'system',
|
|
776
|
-
extraArgs: ['--port', String(daemonPort), '--
|
|
771
|
+
extraArgs: ['--port', String(daemonPort), '--single-port'],
|
|
777
772
|
},
|
|
778
773
|
DAEMON_COMMAND_CONTEXT
|
|
779
774
|
);
|
|
@@ -793,20 +788,20 @@ async function maybeAutoDaemonStart(options = {}) {
|
|
|
793
788
|
defaultPort: process.env.SERVER_PORT || process.env.PORT || '3001',
|
|
794
789
|
color: c,
|
|
795
790
|
});
|
|
796
|
-
printUserDaemonActiveNotice(daemonPort
|
|
791
|
+
printUserDaemonActiveNotice(daemonPort);
|
|
797
792
|
return true;
|
|
798
793
|
} catch (userError) {
|
|
799
794
|
const userHealthySoon = await waitForPortOpen(daemonPort);
|
|
800
795
|
if (userHealthySoon) {
|
|
801
796
|
console.log(`${c.warn('[WARN]')} User daemon health check was delayed, but port ${daemonPort} is now reachable.`);
|
|
802
|
-
printUserDaemonActiveNotice(daemonPort
|
|
797
|
+
printUserDaemonActiveNotice(daemonPort);
|
|
803
798
|
return true;
|
|
804
799
|
}
|
|
805
800
|
const installSystemCommand = buildDaemonCliCommand(
|
|
806
801
|
{
|
|
807
802
|
subcommand: 'install',
|
|
808
803
|
mode: 'system',
|
|
809
|
-
extraArgs: ['--port', String(daemonPort), '--
|
|
804
|
+
extraArgs: ['--port', String(daemonPort), '--single-port'],
|
|
810
805
|
},
|
|
811
806
|
DAEMON_COMMAND_CONTEXT
|
|
812
807
|
);
|
|
@@ -814,7 +809,7 @@ async function maybeAutoDaemonStart(options = {}) {
|
|
|
814
809
|
{
|
|
815
810
|
subcommand: 'install',
|
|
816
811
|
mode: 'user',
|
|
817
|
-
extraArgs: ['--port', String(daemonPort), '--
|
|
812
|
+
extraArgs: ['--port', String(daemonPort), '--single-port'],
|
|
818
813
|
},
|
|
819
814
|
DAEMON_COMMAND_CONTEXT
|
|
820
815
|
);
|
package/server/daemon-manager.js
CHANGED
|
@@ -187,6 +187,8 @@ function parseDaemonArgs(args) {
|
|
|
187
187
|
parsed.options.frontendPort = args[++i];
|
|
188
188
|
} else if (arg.startsWith('--frontend-port=')) {
|
|
189
189
|
parsed.options.frontendPort = arg.split('=')[1];
|
|
190
|
+
} else if (arg === '--no-frontend' || arg === '--single-port') {
|
|
191
|
+
parsed.options.noFrontend = true;
|
|
190
192
|
} else if (arg === '--mode' || arg === '-m') {
|
|
191
193
|
parsed.options.mode = (args[++i] || '').toLowerCase();
|
|
192
194
|
} else if (arg.startsWith('--mode=')) {
|
|
@@ -512,7 +514,7 @@ function showDaemonHelp(c, context = {}) {
|
|
|
512
514
|
{
|
|
513
515
|
subcommand: 'install',
|
|
514
516
|
mode: 'system',
|
|
515
|
-
extraArgs: ['--port', '3001', '--
|
|
517
|
+
extraArgs: ['--port', '3001', '--single-port'],
|
|
516
518
|
},
|
|
517
519
|
context
|
|
518
520
|
);
|
|
@@ -540,7 +542,9 @@ Subcommands:
|
|
|
540
542
|
|
|
541
543
|
Options:
|
|
542
544
|
-p, --port <port> Set service server port (default: 3001)
|
|
543
|
-
--frontend-port <port>
|
|
545
|
+
--frontend-port <port> Start a separate frontend Vite service on this port (legacy/dev)
|
|
546
|
+
--single-port, --no-frontend
|
|
547
|
+
Serve the built UI from the backend port only (default)
|
|
544
548
|
-m, --mode <mode> Service mode: user | system | auto (default: system)
|
|
545
549
|
--database-path <path> Set service database path
|
|
546
550
|
|
|
@@ -592,6 +596,8 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
592
596
|
const defaultFrontendPort = context.defaultFrontendPort || process.env.VITE_PORT || String(DEFAULT_FRONTEND_PORT);
|
|
593
597
|
const configuredPort = parsed.options.serverPort || defaultPort;
|
|
594
598
|
const configuredFrontendPort = parsed.options.frontendPort || defaultFrontendPort;
|
|
599
|
+
const frontendEnabled = parsed.options.noFrontend !== true &&
|
|
600
|
+
(Boolean(parsed.options.frontendPort) || process.env.PIXCODE_SEPARATE_FRONTEND === '1');
|
|
595
601
|
const databasePath = parsed.options.databasePath || process.env.DATABASE_PATH || '';
|
|
596
602
|
|
|
597
603
|
const portNum = Number(configuredPort);
|
|
@@ -599,7 +605,7 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
599
605
|
throw new Error(`Invalid port "${configuredPort}". Expected an integer between 1 and 65535.`);
|
|
600
606
|
}
|
|
601
607
|
const frontendPortNum = Number(configuredFrontendPort);
|
|
602
|
-
if (!Number.isInteger(frontendPortNum) || frontendPortNum < 1 || frontendPortNum > 65535) {
|
|
608
|
+
if (frontendEnabled && (!Number.isInteger(frontendPortNum) || frontendPortNum < 1 || frontendPortNum > 65535)) {
|
|
603
609
|
throw new Error(`Invalid frontend port "${configuredFrontendPort}". Expected an integer between 1 and 65535.`);
|
|
604
610
|
}
|
|
605
611
|
|
|
@@ -632,9 +638,13 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
632
638
|
const systemUnitInstalled = fs.existsSync(getDaemonServicePath('system'));
|
|
633
639
|
const systemFrontendUnitInstalled = fs.existsSync(getFrontendServicePath('system'));
|
|
634
640
|
const selectedModePort = getPortFromServiceUnit(servicePath) || portNum;
|
|
635
|
-
const selectedModeFrontendPort =
|
|
641
|
+
const selectedModeFrontendPort = frontendEnabled
|
|
642
|
+
? getPortFromServiceUnit(frontendServicePath) || frontendPortNum
|
|
643
|
+
: undefined;
|
|
636
644
|
const portReachable = await isPortReachable(selectedModePort);
|
|
637
|
-
const frontendPortReachable =
|
|
645
|
+
const frontendPortReachable = selectedModeFrontendPort
|
|
646
|
+
? await isPortReachable(selectedModeFrontendPort)
|
|
647
|
+
: false;
|
|
638
648
|
const userState = userBus.ok
|
|
639
649
|
? {
|
|
640
650
|
backend: getServiceState('user', DAEMON_SERVICE_NAME),
|
|
@@ -667,7 +677,10 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
667
677
|
console.log(`${c.info('[INFO]')} user state: backend active=${c.bright(userState.backend.active)} enabled=${c.bright(userState.backend.enabled)} | frontend active=${c.bright(userState.frontend.active)} enabled=${c.bright(userState.frontend.enabled)}`);
|
|
668
678
|
console.log(`${c.info('[INFO]')} system state: backend active=${c.bright(systemState.backend.active)} enabled=${c.bright(systemState.backend.enabled)} | frontend active=${c.bright(systemState.frontend.active)} enabled=${c.bright(systemState.frontend.enabled)}`);
|
|
669
679
|
console.log(`${c.info('[INFO]')} backend port: ${c.bright(String(selectedModePort))} (${portReachable ? c.ok('reachable') : c.warn('not reachable')})`);
|
|
670
|
-
console.log(`${c.info('[INFO]')} frontend
|
|
680
|
+
console.log(`${c.info('[INFO]')} frontend mode: ${frontendEnabled ? c.bright('separate Vite service') : c.bright('single backend port')}`);
|
|
681
|
+
if (frontendEnabled) {
|
|
682
|
+
console.log(`${c.info('[INFO]')} frontend port: ${c.bright(String(selectedModeFrontendPort))} (${frontendPortReachable ? c.ok('reachable') : c.warn('not reachable')})`);
|
|
683
|
+
}
|
|
671
684
|
if (lastErrorLine) {
|
|
672
685
|
console.log(`${c.warn('[WARN]')} Latest error: ${lastErrorLine}`);
|
|
673
686
|
}
|
|
@@ -691,20 +704,17 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
691
704
|
console.log(`SYSTEM_FRONTEND_ENABLED=${systemState.frontend.enabled}`);
|
|
692
705
|
console.log(`BACKEND_PORT=${selectedModePort}`);
|
|
693
706
|
console.log(`BACKEND_PORT_REACHABLE=${portReachable}`);
|
|
694
|
-
console.log(`
|
|
707
|
+
console.log(`FRONTEND_ENABLED=${frontendEnabled}`);
|
|
708
|
+
console.log(`FRONTEND_PORT=${selectedModeFrontendPort ?? ''}`);
|
|
695
709
|
console.log(`FRONTEND_PORT_REACHABLE=${frontendPortReachable}`);
|
|
696
710
|
console.log(`LAST_ERROR_LINE=${JSON.stringify(lastErrorLine || '')}\n`);
|
|
697
711
|
return;
|
|
698
712
|
}
|
|
699
713
|
|
|
700
|
-
const serviceDefs = [
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
{
|
|
705
|
-
servicePath: frontendServicePath,
|
|
706
|
-
},
|
|
707
|
-
];
|
|
714
|
+
const serviceDefs = [{ servicePath }];
|
|
715
|
+
if (frontendEnabled) {
|
|
716
|
+
serviceDefs.push({ servicePath: frontendServicePath });
|
|
717
|
+
}
|
|
708
718
|
|
|
709
719
|
switch (parsed.subcommand) {
|
|
710
720
|
case 'install': {
|
|
@@ -714,7 +724,9 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
714
724
|
|
|
715
725
|
try {
|
|
716
726
|
fs.mkdirSync(path.dirname(servicePath), { recursive: true });
|
|
717
|
-
|
|
727
|
+
if (frontendEnabled) {
|
|
728
|
+
fs.mkdirSync(path.dirname(frontendServicePath), { recursive: true });
|
|
729
|
+
}
|
|
718
730
|
|
|
719
731
|
const backendUnitContent = buildDaemonServiceUnit({
|
|
720
732
|
appRoot,
|
|
@@ -725,20 +737,24 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
725
737
|
});
|
|
726
738
|
fs.writeFileSync(servicePath, backendUnitContent, 'utf8');
|
|
727
739
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
740
|
+
if (frontendEnabled) {
|
|
741
|
+
const frontendUnitContent = buildFrontendDaemonServiceUnit({
|
|
742
|
+
appRoot,
|
|
743
|
+
frontendPort: frontendPortNum,
|
|
744
|
+
nodeExecPath: context.nodeExecPath,
|
|
745
|
+
cliEntry: context.cliEntry,
|
|
746
|
+
});
|
|
747
|
+
fs.writeFileSync(frontendServicePath, frontendUnitContent, 'utf8');
|
|
748
|
+
}
|
|
735
749
|
} catch (fileError) {
|
|
736
750
|
if (mode === 'system' && (fileError.code === 'EACCES' || fileError.code === 'EPERM')) {
|
|
737
751
|
const installHint = buildDaemonCliCommand(
|
|
738
752
|
{
|
|
739
753
|
subcommand: 'install',
|
|
740
754
|
mode: 'system',
|
|
741
|
-
extraArgs:
|
|
755
|
+
extraArgs: frontendEnabled
|
|
756
|
+
? ['--port', String(portNum), '--frontend-port', String(frontendPortNum)]
|
|
757
|
+
: ['--port', String(portNum), '--single-port'],
|
|
742
758
|
},
|
|
743
759
|
daemonCommandContext
|
|
744
760
|
);
|
|
@@ -752,7 +768,12 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
752
768
|
|
|
753
769
|
runSystemctl(mode, ['daemon-reload']);
|
|
754
770
|
runSystemctl(mode, ['enable', '--now', DAEMON_SERVICE_NAME]);
|
|
755
|
-
|
|
771
|
+
if (frontendEnabled) {
|
|
772
|
+
runSystemctl(mode, ['enable', '--now', FRONTEND_DAEMON_SERVICE_NAME]);
|
|
773
|
+
} else {
|
|
774
|
+
runSystemctl(mode, ['stop', FRONTEND_DAEMON_SERVICE_NAME], { allowFailure: true });
|
|
775
|
+
runSystemctl(mode, ['disable', FRONTEND_DAEMON_SERVICE_NAME], { allowFailure: true });
|
|
776
|
+
}
|
|
756
777
|
|
|
757
778
|
if (mode === 'user') {
|
|
758
779
|
const lingerResult = runCommand('loginctl', ['enable-linger', os.userInfo().username]);
|
|
@@ -764,22 +785,32 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
764
785
|
}
|
|
765
786
|
|
|
766
787
|
const installedPort = getPortFromServiceUnit(servicePath) || portNum;
|
|
767
|
-
const installedFrontendPort = getPortFromServiceUnit(frontendServicePath) || frontendPortNum;
|
|
768
788
|
await healthCheckOrThrow(mode, DAEMON_SERVICE_NAME, installedPort, c);
|
|
769
|
-
|
|
789
|
+
const installedFrontendPort = frontendEnabled
|
|
790
|
+
? getPortFromServiceUnit(frontendServicePath) || frontendPortNum
|
|
791
|
+
: undefined;
|
|
792
|
+
if (frontendEnabled && installedFrontendPort) {
|
|
793
|
+
await healthCheckOrThrow(mode, FRONTEND_DAEMON_SERVICE_NAME, installedFrontendPort, c);
|
|
794
|
+
}
|
|
770
795
|
|
|
771
796
|
const backendState = getServiceState(mode, DAEMON_SERVICE_NAME);
|
|
772
|
-
const frontendState =
|
|
797
|
+
const frontendState = frontendEnabled
|
|
798
|
+
? getServiceState(mode, FRONTEND_DAEMON_SERVICE_NAME)
|
|
799
|
+
: { active: 'disabled', enabled: 'disabled' };
|
|
773
800
|
console.log(`\n${c.ok('✔')} Daemon installed and started.`);
|
|
774
801
|
console.log(` Mode: ${c.bright(mode)}`);
|
|
775
802
|
console.log(` Backend Unit: ${c.dim(servicePath)}`);
|
|
776
|
-
console.log(` Frontend
|
|
803
|
+
console.log(` Frontend Mode: ${c.bright(frontendEnabled ? 'separate service' : 'single backend port')}`);
|
|
777
804
|
console.log(` Backend Active: ${c.bright(backendState.active)}`);
|
|
778
805
|
console.log(` Backend Enabled:${c.bright(backendState.enabled)}`);
|
|
779
|
-
console.log(` Frontend Active:${c.bright(frontendState.active)}`);
|
|
780
|
-
console.log(` Frontend Enabled:${c.bright(frontendState.enabled)}`);
|
|
781
806
|
console.log(` Backend URL: ${c.bright(`http://localhost:${installedPort}`)}`);
|
|
782
|
-
|
|
807
|
+
if (frontendEnabled) {
|
|
808
|
+
console.log(` Frontend Unit: ${c.dim(frontendServicePath)}`);
|
|
809
|
+
console.log(` Frontend Active:${c.bright(frontendState.active)}`);
|
|
810
|
+
console.log(` Frontend Enabled:${c.bright(frontendState.enabled)}`);
|
|
811
|
+
console.log(` Frontend URL: ${c.bright(`http://localhost:${installedFrontendPort}`)}`);
|
|
812
|
+
}
|
|
813
|
+
console.log('');
|
|
783
814
|
if (mode === 'system') {
|
|
784
815
|
const statusCommand = buildDaemonCliCommand(
|
|
785
816
|
{ subcommand: 'status', mode: 'system' },
|
|
@@ -793,9 +824,9 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
793
824
|
{ subcommand: 'logs', mode: 'system' },
|
|
794
825
|
daemonCommandContext
|
|
795
826
|
);
|
|
796
|
-
console.log(`${c.ok('[OK]')} System daemon is active
|
|
827
|
+
console.log(`${c.ok('[OK]')} System daemon is active.`);
|
|
797
828
|
console.log(`${c.info('[INFO]')} Backend health: ${c.bright(`http://localhost:${installedPort}/health`)}`);
|
|
798
|
-
console.log(`${c.info('[INFO]')}
|
|
829
|
+
console.log(`${c.info('[INFO]')} UI: ${c.bright(`http://localhost:${installedPort}/`)}`);
|
|
799
830
|
console.log(`${c.info('[INFO]')} Status: ${c.bright(statusCommand)}`);
|
|
800
831
|
console.log(`${c.info('[INFO]')} Stop: ${c.bright(stopCommand)}`);
|
|
801
832
|
console.log(`${c.info('[INFO]')} Logs: ${c.bright(logsCommand)}\n`);
|
|
@@ -813,9 +844,9 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
813
844
|
{ subcommand: 'logs', mode: 'user' },
|
|
814
845
|
daemonCommandContext
|
|
815
846
|
);
|
|
816
|
-
console.log(`${c.ok('[OK]')} User daemon is active
|
|
847
|
+
console.log(`${c.ok('[OK]')} User daemon is active.`);
|
|
817
848
|
console.log(`${c.info('[INFO]')} Backend health: ${c.bright(`http://localhost:${installedPort}/health`)}`);
|
|
818
|
-
console.log(`${c.info('[INFO]')}
|
|
849
|
+
console.log(`${c.info('[INFO]')} UI: ${c.bright(`http://localhost:${installedPort}/`)}`);
|
|
819
850
|
console.log(`${c.info('[INFO]')} Status: ${c.bright(statusCommand)}`);
|
|
820
851
|
console.log(`${c.info('[INFO]')} Stop: ${c.bright(stopCommand)}`);
|
|
821
852
|
console.log(`${c.info('[INFO]')} Logs: ${c.bright(logsCommand)}`);
|
|
@@ -830,36 +861,36 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
830
861
|
|
|
831
862
|
case 'start':
|
|
832
863
|
runSystemctl(mode, ['start', DAEMON_SERVICE_NAME]);
|
|
833
|
-
runSystemctl(mode, ['start', FRONTEND_DAEMON_SERVICE_NAME]);
|
|
864
|
+
if (frontendEnabled) runSystemctl(mode, ['start', FRONTEND_DAEMON_SERVICE_NAME]);
|
|
834
865
|
await healthCheckOrThrow(mode, DAEMON_SERVICE_NAME, effectivePort, c);
|
|
835
|
-
await healthCheckOrThrow(mode, FRONTEND_DAEMON_SERVICE_NAME, effectiveFrontendPort, c);
|
|
836
|
-
console.log(`${c.ok('[OK]')}
|
|
866
|
+
if (frontendEnabled) await healthCheckOrThrow(mode, FRONTEND_DAEMON_SERVICE_NAME, effectiveFrontendPort, c);
|
|
867
|
+
console.log(`${c.ok('[OK]')} Pixcode service started.`);
|
|
837
868
|
break;
|
|
838
869
|
|
|
839
870
|
case 'stop':
|
|
840
871
|
runSystemctl(mode, ['stop', FRONTEND_DAEMON_SERVICE_NAME], { allowFailure: true });
|
|
841
872
|
runSystemctl(mode, ['stop', DAEMON_SERVICE_NAME]);
|
|
842
|
-
console.log(`${c.ok('[OK]')}
|
|
873
|
+
console.log(`${c.ok('[OK]')} Pixcode service stopped (auto-start remains enabled).`);
|
|
843
874
|
break;
|
|
844
875
|
|
|
845
876
|
case 'restart':
|
|
846
877
|
runSystemctl(mode, ['restart', DAEMON_SERVICE_NAME]);
|
|
847
|
-
runSystemctl(mode, ['restart', FRONTEND_DAEMON_SERVICE_NAME]);
|
|
878
|
+
if (frontendEnabled) runSystemctl(mode, ['restart', FRONTEND_DAEMON_SERVICE_NAME]);
|
|
848
879
|
await healthCheckOrThrow(mode, DAEMON_SERVICE_NAME, effectivePort, c);
|
|
849
|
-
await healthCheckOrThrow(mode, FRONTEND_DAEMON_SERVICE_NAME, effectiveFrontendPort, c);
|
|
850
|
-
console.log(`${c.ok('[OK]')}
|
|
880
|
+
if (frontendEnabled) await healthCheckOrThrow(mode, FRONTEND_DAEMON_SERVICE_NAME, effectiveFrontendPort, c);
|
|
881
|
+
console.log(`${c.ok('[OK]')} Pixcode service restarted.`);
|
|
851
882
|
break;
|
|
852
883
|
|
|
853
884
|
case 'enable':
|
|
854
885
|
runSystemctl(mode, ['enable', DAEMON_SERVICE_NAME]);
|
|
855
|
-
runSystemctl(mode, ['enable', FRONTEND_DAEMON_SERVICE_NAME]);
|
|
856
|
-
console.log(`${c.ok('[OK]')}
|
|
886
|
+
if (frontendEnabled) runSystemctl(mode, ['enable', FRONTEND_DAEMON_SERVICE_NAME]);
|
|
887
|
+
console.log(`${c.ok('[OK]')} Pixcode service enabled for auto-start.`);
|
|
857
888
|
break;
|
|
858
889
|
|
|
859
890
|
case 'disable':
|
|
860
891
|
runSystemctl(mode, ['disable', DAEMON_SERVICE_NAME]);
|
|
861
892
|
runSystemctl(mode, ['disable', FRONTEND_DAEMON_SERVICE_NAME]);
|
|
862
|
-
console.log(`${c.ok('[OK]')}
|
|
893
|
+
console.log(`${c.ok('[OK]')} Pixcode service disabled for auto-start.`);
|
|
863
894
|
break;
|
|
864
895
|
|
|
865
896
|
case 'logs': {
|
|
@@ -903,17 +934,25 @@ export async function handleDaemonCommand(args, context = {}) {
|
|
|
903
934
|
const backendUnitExists = fs.existsSync(servicePath);
|
|
904
935
|
const frontendUnitExists = fs.existsSync(frontendServicePath);
|
|
905
936
|
const selectedPort = getPortFromServiceUnit(servicePath) || portNum;
|
|
906
|
-
const selectedFrontendPort =
|
|
937
|
+
const selectedFrontendPort = frontendEnabled
|
|
938
|
+
? getPortFromServiceUnit(frontendServicePath) || frontendPortNum
|
|
939
|
+
: undefined;
|
|
907
940
|
console.log(`\n${c.bright('Pixcode Daemon Status')}\n`);
|
|
908
941
|
console.log(`${c.info('[INFO]')} Mode: ${c.bright(mode)} ${parsed.options.mode === 'auto' ? c.dim('(resolved from auto)') : ''}`);
|
|
909
942
|
console.log(`${c.info('[INFO]')} Backend Unit: ${c.dim(servicePath)} ${backendUnitExists ? c.ok('[OK]') : c.warn('[MISSING]')}`);
|
|
910
943
|
console.log(`${c.info('[INFO]')} Backend Active: ${c.bright(backendState.active)}`);
|
|
911
944
|
console.log(`${c.info('[INFO]')} Backend Enabled:${c.bright(backendState.enabled)}`);
|
|
912
945
|
console.log(`${c.info('[INFO]')} Backend Port: ${c.bright(String(selectedPort))}`);
|
|
913
|
-
console.log(`${c.info('[INFO]')}
|
|
914
|
-
console.log(`${c.info('[INFO]')} Frontend
|
|
915
|
-
|
|
916
|
-
|
|
946
|
+
console.log(`${c.info('[INFO]')} UI URL: ${c.bright(`http://localhost:${selectedPort}/`)}`);
|
|
947
|
+
console.log(`${c.info('[INFO]')} Frontend Mode: ${frontendEnabled ? c.bright('separate Vite service') : c.bright('single backend port')}`);
|
|
948
|
+
if (frontendEnabled) {
|
|
949
|
+
console.log(`${c.info('[INFO]')} Frontend Unit: ${c.dim(frontendServicePath)} ${frontendUnitExists ? c.ok('[OK]') : c.warn('[MISSING]')}`);
|
|
950
|
+
console.log(`${c.info('[INFO]')} Frontend Active:${c.bright(frontendState.active)}`);
|
|
951
|
+
console.log(`${c.info('[INFO]')} Frontend Enabled:${c.bright(frontendState.enabled)}`);
|
|
952
|
+
console.log(`${c.info('[INFO]')} Frontend Port: ${c.bright(String(selectedFrontendPort))}`);
|
|
953
|
+
} else if (frontendUnitExists) {
|
|
954
|
+
console.log(`${c.info('[INFO]')} Legacy Frontend:${c.dim(frontendServicePath)} active=${c.bright(frontendState.active)} enabled=${c.bright(frontendState.enabled)}`);
|
|
955
|
+
}
|
|
917
956
|
console.log('');
|
|
918
957
|
}
|
|
919
958
|
}
|