@pixelbyte-software/pixcode 1.38.2 → 1.38.3
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/assets/index-BzgMq98S.css +32 -0
- package/dist/assets/{index-BJiAbLzU.js → index-Cc0uLPZw.js} +140 -140
- package/dist/index.html +2 -2
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +76 -1
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -1
- package/dist-server/server/modules/orchestration/workflows/workspace-target.js +2 -0
- package/dist-server/server/modules/orchestration/workflows/workspace-target.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +2 -1
- package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +5 -2
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/qwen/qwen-auth.provider.js +1 -1
- package/dist-server/server/modules/providers/list/qwen/qwen-auth.provider.js.map +1 -1
- package/dist-server/server/routes/taskmaster.js +31 -52
- package/dist-server/server/routes/taskmaster.js.map +1 -1
- package/dist-server/server/services/install-jobs.js +152 -17
- package/dist-server/server/services/install-jobs.js.map +1 -1
- package/package.json +1 -1
- package/scripts/smoke/chat-session-provider-pools.mjs +35 -0
- package/scripts/smoke/desktop-tray-icon.mjs +33 -0
- package/scripts/smoke/mac-desktop-runtime.mjs +43 -0
- package/scripts/smoke/multi-project-ui.mjs +45 -0
- package/scripts/smoke/orchestration-permission-fallback.mjs +34 -0
- package/server/modules/orchestration/workflows/workflow-runner.ts +105 -1
- package/server/modules/orchestration/workflows/workspace-target.ts +2 -0
- package/server/modules/providers/list/codex/codex-auth.provider.ts +2 -1
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +6 -2
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +1 -1
- package/server/modules/providers/list/qwen/qwen-auth.provider.ts +1 -1
- package/server/routes/taskmaster.js +36 -57
- package/server/services/install-jobs.js +159 -16
- package/dist/assets/index-BzL2G4Sw.css +0 -32
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const projectsState = readFileSync('src/hooks/useProjectsState.ts', 'utf8');
|
|
5
|
+
|
|
6
|
+
function assert(condition, message) {
|
|
7
|
+
if (!condition) {
|
|
8
|
+
console.error(`chat-session-provider-pools smoke failed: ${message}`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const getProjectSessionsBody = projectsState.match(/const getProjectSessions = \(project: Project\): ProjectSession\[\] => \{[\s\S]*?\n\};/)?.[0] ?? '';
|
|
14
|
+
|
|
15
|
+
assert(
|
|
16
|
+
getProjectSessionsBody.includes('project.qwenSessions') && getProjectSessionsBody.includes('project.opencodeSessions'),
|
|
17
|
+
'selected-session refresh logic should include qwen/opencode pools',
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
assert(
|
|
21
|
+
projectsState.includes('serialize(nextProject.qwenSessions)') && projectsState.includes('serialize(nextProject.opencodeSessions)'),
|
|
22
|
+
'project change detection should include qwen/opencode sessions',
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
assert(
|
|
26
|
+
projectsState.includes("sessionId.startsWith('codex-')") && projectsState.includes("sessionId.startsWith('gemini_')"),
|
|
27
|
+
'fallback session provider inference should cover codex and gemini ids, not only qwen/opencode',
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
assert(
|
|
31
|
+
projectsState.includes("localStorage.setItem('selected-provider', session.__provider)"),
|
|
32
|
+
'selecting an existing session should persist that session provider before navigation',
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
console.log('chat-session-provider-pools smoke passed');
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const main = readFileSync('desktop/electron/main.cjs', 'utf8');
|
|
5
|
+
|
|
6
|
+
function assert(condition, message) {
|
|
7
|
+
if (!condition) {
|
|
8
|
+
console.error(`desktop-tray-icon smoke failed: ${message}`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
assert(
|
|
14
|
+
main.includes('function normalizeTrayIcon'),
|
|
15
|
+
'tray icons should be normalized before creating the Electron Tray',
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
assert(
|
|
19
|
+
main.includes("process.platform === 'darwin'") && main.includes('width: 18') && main.includes('height: 18'),
|
|
20
|
+
'macOS menu bar icon should be resized to 18x18 instead of using the 1024 app icon',
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
assert(
|
|
24
|
+
main.includes('setTemplateImage(true)'),
|
|
25
|
+
'macOS menu bar icon should be marked as a template image',
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
assert(
|
|
29
|
+
main.includes('return normalizeTrayIcon(nativeImage.createFromPath(p))'),
|
|
30
|
+
'resolveTrayIcon should return the normalized image',
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
console.log('desktop-tray-icon smoke passed');
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const installJobs = readFileSync('server/services/install-jobs.js', 'utf8');
|
|
5
|
+
const taskmaster = readFileSync('server/routes/taskmaster.js', 'utf8');
|
|
6
|
+
const codexAuth = readFileSync('server/modules/providers/list/codex/codex-auth.provider.ts', 'utf8');
|
|
7
|
+
const cursorAuth = readFileSync('server/modules/providers/list/cursor/cursor-auth.provider.ts', 'utf8');
|
|
8
|
+
const geminiAuth = readFileSync('server/modules/providers/list/gemini/gemini-auth.provider.ts', 'utf8');
|
|
9
|
+
const qwenAuth = readFileSync('server/modules/providers/list/qwen/qwen-auth.provider.ts', 'utf8');
|
|
10
|
+
|
|
11
|
+
function assert(condition, message) {
|
|
12
|
+
if (!condition) {
|
|
13
|
+
console.error(`mac-desktop-runtime smoke failed: ${message}`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
assert(
|
|
19
|
+
installJobs.includes('function collectUserShellPath'),
|
|
20
|
+
'install-jobs should hydrate PATH from the user shell for macOS GUI launches',
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
assert(
|
|
24
|
+
installJobs.includes("'.nvm'") && installJobs.includes("'.volta'") && installJobs.includes("'.asdf'"),
|
|
25
|
+
'install-jobs should search common user Node manager bin directories',
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
assert(
|
|
29
|
+
installJobs.includes("findExecutableOnPath('npm'") && installJobs.includes('buildCliSpawnEnv'),
|
|
30
|
+
'Task/CLI installers should resolve npm from the augmented runtime PATH before spawning',
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
assert(
|
|
34
|
+
taskmaster.includes('findExecutableOnPath') && !taskmaster.includes("spawn('which'"),
|
|
35
|
+
'TaskMaster status should use Pixcode executable resolution, not bare which from a minimal GUI PATH',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
assert(codexAuth.includes('CODEX_CLI_PATH'), 'Codex auth should honor CODEX_CLI_PATH');
|
|
39
|
+
assert(cursorAuth.includes('CURSOR_CLI_PATH'), 'Cursor auth should honor CURSOR_CLI_PATH');
|
|
40
|
+
assert(geminiAuth.includes('GEMINI_CLI_PATH'), 'Gemini auth should honor GEMINI_CLI_PATH');
|
|
41
|
+
assert(qwenAuth.includes('QWEN_CLI_PATH'), 'Qwen auth should honor QWEN_CLI_PATH');
|
|
42
|
+
|
|
43
|
+
console.log('mac-desktop-runtime smoke passed');
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const chatComposer = readFileSync('src/components/chat/view/subcomponents/ChatComposer.tsx', 'utf8');
|
|
5
|
+
const workerSlotsControl = readFileSync('src/components/chat/view/subcomponents/WorkerSlotsControl.tsx', 'utf8');
|
|
6
|
+
const mainContent = readFileSync('src/components/main-content/view/MainContent.tsx', 'utf8');
|
|
7
|
+
|
|
8
|
+
function assert(condition, message) {
|
|
9
|
+
if (!condition) {
|
|
10
|
+
console.error(`multi-project-ui smoke failed: ${message}`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
assert(
|
|
16
|
+
chatComposer.includes('worker-slot-composer-rail'),
|
|
17
|
+
'chat composer should render the multi-project launcher in a dedicated rail next to submit',
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
assert(
|
|
21
|
+
/<WorkerSlotsControl[\s\S]*align="right"/.test(chatComposer),
|
|
22
|
+
'chat composer should right-align the worker slot popover near the submit button',
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
assert(
|
|
26
|
+
workerSlotsControl.includes('MAX_WORKER_SLOTS') && workerSlotsControl.includes('workerSlotsFull'),
|
|
27
|
+
'worker slots control should keep the four-slot limit visible in the UI',
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
assert(
|
|
31
|
+
workerSlotsControl.includes('translate-y-0') && workerSlotsControl.includes('opacity-100'),
|
|
32
|
+
'worker slots popover should animate into view instead of appearing abruptly',
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
assert(
|
|
36
|
+
mainContent.includes('x: 28') && mainContent.includes("transformOrigin: 'right center'"),
|
|
37
|
+
'right side panels should animate from the right edge toward the left',
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
assert(
|
|
41
|
+
mainContent.includes('transition-[width,opacity,transform]'),
|
|
42
|
+
'split panes should animate width, opacity, and transform changes',
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
console.log('multi-project-ui smoke passed');
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const runner = readFileSync('server/modules/orchestration/workflows/workflow-runner.ts', 'utf8');
|
|
5
|
+
const workspaceTarget = readFileSync('server/modules/orchestration/workflows/workspace-target.ts', 'utf8');
|
|
6
|
+
|
|
7
|
+
function assert(condition, message) {
|
|
8
|
+
if (!condition) {
|
|
9
|
+
console.error(`orchestration-permission-fallback smoke failed: ${message}`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
assert(
|
|
15
|
+
runner.includes('isExternalDirectoryPermissionError'),
|
|
16
|
+
'workflow runner should classify external_directory permission failures',
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
assert(
|
|
20
|
+
runner.includes('buildPermissionFallbackOutput') && runner.includes('buildFallbackFinalReport'),
|
|
21
|
+
'workflow runner should synthesize user-facing outputs instead of failing the whole run',
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
assert(
|
|
25
|
+
runner.includes('resolveNodePermissionMode') && runner.includes('bypassPermissions'),
|
|
26
|
+
'host workspace orchestration should avoid default auto-reject permission mode for selected project paths',
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
assert(
|
|
30
|
+
workspaceTarget.includes('Do not access parent directories') && workspaceTarget.includes('If a tool reports a permission denial'),
|
|
31
|
+
'workspace prompt should tell agents to stay inside the selected workspace and report permission denials',
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
console.log('orchestration-permission-fallback smoke passed');
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
WorkflowRun,
|
|
8
8
|
} from '@/modules/orchestration/workflows/workflow.types.js';
|
|
9
9
|
import {
|
|
10
|
+
type ResolvedWorkspaceTarget,
|
|
10
11
|
resolveWorkflowWorkspace,
|
|
11
12
|
workspaceContextPrompt,
|
|
12
13
|
workspaceTargetMetadata,
|
|
@@ -362,6 +363,92 @@ function compactOutputForContext(text: string): string {
|
|
|
362
363
|
].join('');
|
|
363
364
|
}
|
|
364
365
|
|
|
366
|
+
function isExternalDirectoryPermissionError(value: unknown): boolean {
|
|
367
|
+
const text = String(value ?? '').toLocaleLowerCase('en');
|
|
368
|
+
return (
|
|
369
|
+
text.includes('external_directory') ||
|
|
370
|
+
/permission requested:.*auto-rejecting/u.test(text) ||
|
|
371
|
+
/auto-rejecting.*permission/u.test(text) ||
|
|
372
|
+
/outside (the )?(workspace|working directory)/u.test(text) ||
|
|
373
|
+
/permission.*external/u.test(text)
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function isFinalReportNode(node: WorkflowNode): boolean {
|
|
378
|
+
return node.id === 'final_report' || node.stage === 'final_report' || node.stage === 'report';
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function workspaceNeedsHostPermissionBypass(target: ResolvedWorkspaceTarget): boolean {
|
|
382
|
+
return (target.kind === 'selected_project' || target.kind === 'custom') && target.projectPath !== target.appRoot;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function resolveNodePermissionMode(node: WorkflowNode, target: ResolvedWorkspaceTarget): string | undefined {
|
|
386
|
+
if (node.permissionMode && node.permissionMode !== 'default') {
|
|
387
|
+
return node.permissionMode;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (workspaceNeedsHostPermissionBypass(target)) {
|
|
391
|
+
return 'bypassPermissions';
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return node.permissionMode;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function buildPermissionFallbackOutput(
|
|
398
|
+
node: WorkflowNode,
|
|
399
|
+
reason: string,
|
|
400
|
+
target: ResolvedWorkspaceTarget,
|
|
401
|
+
): string {
|
|
402
|
+
return [
|
|
403
|
+
'Bu adım çalışma alanı izin sınırına takıldı.',
|
|
404
|
+
'',
|
|
405
|
+
`Ajan: ${node.agentLabel || node.id}`,
|
|
406
|
+
`Hedef çalışma alanı: ${target.projectPath}`,
|
|
407
|
+
`Hata: ${reason}`,
|
|
408
|
+
'',
|
|
409
|
+
'Pixcode bu adımı workflow dışına taşırmadan devam ettirdi. Ajan aynı dış dizin yoluna tekrar tekrar erişmek yerine mevcut bağlamla ilerlemeli.',
|
|
410
|
+
].join('\n');
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function buildFallbackFinalReport(
|
|
414
|
+
outputs: Map<string, string>,
|
|
415
|
+
reason: string,
|
|
416
|
+
target: ResolvedWorkspaceTarget,
|
|
417
|
+
): string {
|
|
418
|
+
const completedOutputs = [...outputs.entries()]
|
|
419
|
+
.map(([nodeId, output]) => [`## ${nodeId}`, output || '(çıktı yok)'].join('\n'))
|
|
420
|
+
.join('\n\n');
|
|
421
|
+
|
|
422
|
+
return [
|
|
423
|
+
'Final rapor aracı çalışma alanı izin sınırına takıldı, bu yüzden Pixcode tamamlanan ajan çıktılarından güvenli bir özet üretti.',
|
|
424
|
+
'',
|
|
425
|
+
`Hedef çalışma alanı: ${target.projectPath}`,
|
|
426
|
+
`İzin hatası: ${reason}`,
|
|
427
|
+
'',
|
|
428
|
+
completedOutputs || 'Bu turda final rapora aktarılabilecek tamamlanmış ajan çıktısı yok.',
|
|
429
|
+
].join('\n');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function completeNodeWithPermissionFallback(
|
|
433
|
+
nodeRun: WorkflowNodeRun,
|
|
434
|
+
node: WorkflowNode,
|
|
435
|
+
outputs: Map<string, string>,
|
|
436
|
+
completed: Set<string>,
|
|
437
|
+
reason: string,
|
|
438
|
+
target: ResolvedWorkspaceTarget,
|
|
439
|
+
): void {
|
|
440
|
+
const outputText = isFinalReportNode(node)
|
|
441
|
+
? buildFallbackFinalReport(outputs, reason, target)
|
|
442
|
+
: buildPermissionFallbackOutput(node, reason, target);
|
|
443
|
+
|
|
444
|
+
nodeRun.status = 'completed';
|
|
445
|
+
nodeRun.error = reason;
|
|
446
|
+
nodeRun.outputText = outputText;
|
|
447
|
+
nodeRun.finishedAt = nodeRun.finishedAt ?? Date.now();
|
|
448
|
+
outputs.set(node.id, compactOutputForContext(outputText));
|
|
449
|
+
completed.add(node.id);
|
|
450
|
+
}
|
|
451
|
+
|
|
365
452
|
function expandAgentTeamWorkflow(workflow: Workflow, metadata?: Record<string, unknown>): Workflow {
|
|
366
453
|
const agents = readAgentAssignments(metadata);
|
|
367
454
|
if (agents.length === 0) {
|
|
@@ -1316,6 +1403,7 @@ class WorkflowRunner {
|
|
|
1316
1403
|
|
|
1317
1404
|
nodeRun.status = 'running';
|
|
1318
1405
|
nodeRun.startedAt = Date.now();
|
|
1406
|
+
nodeRun.permissionMode = resolveNodePermissionMode(node, resolveWorkflowWorkspace(run.metadata));
|
|
1319
1407
|
workflowStore.setRun(run);
|
|
1320
1408
|
|
|
1321
1409
|
const inputContext = node.inputs.map((input) => outputs.get(input)).filter(Boolean).join('\n\n');
|
|
@@ -1328,6 +1416,7 @@ class WorkflowRunner {
|
|
|
1328
1416
|
const isolation = readIsolation(settings.isolation) ?? node.isolation ?? 'host';
|
|
1329
1417
|
const keepAfterCompletion = readBoolean(settings.keepWorkspace) ?? true;
|
|
1330
1418
|
const baseRef = readString(settings.baseRef) ?? 'HEAD';
|
|
1419
|
+
const effectivePermissionMode = resolveNodePermissionMode(node, workspaceTarget);
|
|
1331
1420
|
let body: { id?: string; error?: { message?: string } };
|
|
1332
1421
|
try {
|
|
1333
1422
|
const submit = await fetch(`${localA2ABaseUrl()}/tasks`, {
|
|
@@ -1348,7 +1437,7 @@ class WorkflowRunner {
|
|
|
1348
1437
|
agentLabel: node.agentLabel,
|
|
1349
1438
|
assignment: node.assignment,
|
|
1350
1439
|
model: node.model,
|
|
1351
|
-
permissionMode:
|
|
1440
|
+
permissionMode: effectivePermissionMode,
|
|
1352
1441
|
toolsSettings: node.toolsSettings,
|
|
1353
1442
|
projectPath,
|
|
1354
1443
|
workspaceTarget: workspaceTargetMetadata(workspaceTarget),
|
|
@@ -1370,6 +1459,11 @@ class WorkflowRunner {
|
|
|
1370
1459
|
nodeRun.status = 'failed';
|
|
1371
1460
|
nodeRun.error = error instanceof Error ? error.message : String(error);
|
|
1372
1461
|
workflowStore.setRun(run);
|
|
1462
|
+
if (isExternalDirectoryPermissionError(nodeRun.error)) {
|
|
1463
|
+
completeNodeWithPermissionFallback(nodeRun, node, outputs, completed, nodeRun.error, workspaceTarget);
|
|
1464
|
+
workflowStore.setRun(run);
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1373
1467
|
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, nodeRun.error)) {
|
|
1374
1468
|
return;
|
|
1375
1469
|
}
|
|
@@ -1417,6 +1511,11 @@ class WorkflowRunner {
|
|
|
1417
1511
|
outputs.set(node.id, compactOutputForContext(nodeRun.outputText));
|
|
1418
1512
|
}
|
|
1419
1513
|
workflowStore.setRun(run);
|
|
1514
|
+
if (isExternalDirectoryPermissionError(nodeRun.error)) {
|
|
1515
|
+
completeNodeWithPermissionFallback(nodeRun, node, outputs, completed, nodeRun.error, workspaceTarget);
|
|
1516
|
+
workflowStore.setRun(run);
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1420
1519
|
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, nodeRun.error)) {
|
|
1421
1520
|
return;
|
|
1422
1521
|
}
|
|
@@ -1452,6 +1551,11 @@ class WorkflowRunner {
|
|
|
1452
1551
|
nodeRun.status = 'failed';
|
|
1453
1552
|
nodeRun.error = result.error ?? `A2A task ended with ${result.state}`;
|
|
1454
1553
|
workflowStore.setRun(run);
|
|
1554
|
+
if (isExternalDirectoryPermissionError(`${nodeRun.error}\n${nodeRun.outputText ?? ''}`)) {
|
|
1555
|
+
completeNodeWithPermissionFallback(nodeRun, node, outputs, completed, nodeRun.error, workspaceTarget);
|
|
1556
|
+
workflowStore.setRun(run);
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1455
1559
|
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, nodeRun.error)) {
|
|
1456
1560
|
return;
|
|
1457
1561
|
}
|
|
@@ -114,7 +114,9 @@ export function workspaceContextPrompt(target: ResolvedWorkspaceTarget): string
|
|
|
114
114
|
'Rules:',
|
|
115
115
|
'- Treat the working directory above as the authoritative project/application for this task.',
|
|
116
116
|
'- Do not guess another repository path unless the user explicitly asks for it.',
|
|
117
|
+
'- Do not access parent directories, sibling projects, or broad external globs outside the working directory unless the user explicitly asks.',
|
|
117
118
|
'- If the user asks to analyze or modify Pixcode/orchestration itself, the target workspace must be the Pixcode app root.',
|
|
118
119
|
'- Before running package-manager commands, inspect the package files in the working directory.',
|
|
120
|
+
'- If a tool reports a permission denial, summarize the limitation and continue from the provided context instead of retrying the same external path.',
|
|
119
121
|
].filter(Boolean).join('\n');
|
|
120
122
|
}
|
|
@@ -24,8 +24,9 @@ export class CodexProviderAuth implements IProviderAuth {
|
|
|
24
24
|
* Checks whether Codex is available to the server runtime.
|
|
25
25
|
*/
|
|
26
26
|
private checkInstalled(): boolean {
|
|
27
|
+
const cliPath = process.env.CODEX_CLI_PATH || 'codex';
|
|
27
28
|
try {
|
|
28
|
-
const result = spawn.sync(
|
|
29
|
+
const result = spawn.sync(cliPath, ['--version'], { stdio: 'ignore', timeout: 5000 });
|
|
29
30
|
return !result.error && result.status === 0;
|
|
30
31
|
} catch {
|
|
31
32
|
return false;
|
|
@@ -11,12 +11,16 @@ type CursorLoginStatus = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export class CursorProviderAuth implements IProviderAuth {
|
|
14
|
+
private cliPath(): string {
|
|
15
|
+
return process.env.CURSOR_CLI_PATH || 'cursor-agent';
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
/**
|
|
15
19
|
* Checks whether the cursor-agent CLI is available on this host.
|
|
16
20
|
*/
|
|
17
21
|
private checkInstalled(): boolean {
|
|
18
22
|
try {
|
|
19
|
-
const result = spawn.sync(
|
|
23
|
+
const result = spawn.sync(this.cliPath(), ['--version'], { stdio: 'ignore', timeout: 5000 });
|
|
20
24
|
return !result.error && result.status === 0;
|
|
21
25
|
} catch {
|
|
22
26
|
return false;
|
|
@@ -74,7 +78,7 @@ export class CursorProviderAuth implements IProviderAuth {
|
|
|
74
78
|
}, 5000);
|
|
75
79
|
|
|
76
80
|
try {
|
|
77
|
-
childProcess = spawn(
|
|
81
|
+
childProcess = spawn(this.cliPath(), ['status']);
|
|
78
82
|
} catch {
|
|
79
83
|
clearTimeout(timeout);
|
|
80
84
|
processCompleted = true;
|
|
@@ -24,7 +24,7 @@ export class GeminiProviderAuth implements IProviderAuth {
|
|
|
24
24
|
* Checks whether the Gemini CLI is available on this host.
|
|
25
25
|
*/
|
|
26
26
|
private checkInstalled(): boolean {
|
|
27
|
-
const cliPath = process.env.GEMINI_PATH || 'gemini';
|
|
27
|
+
const cliPath = process.env.GEMINI_CLI_PATH || process.env.GEMINI_PATH || 'gemini';
|
|
28
28
|
try {
|
|
29
29
|
const result = spawn.sync(cliPath, ['--version'], { stdio: 'ignore', timeout: 5000 });
|
|
30
30
|
return !result.error && result.status === 0;
|
|
@@ -32,7 +32,7 @@ type QwenCredentialsStatus = {
|
|
|
32
32
|
*/
|
|
33
33
|
export class QwenProviderAuth implements IProviderAuth {
|
|
34
34
|
private checkInstalled(): boolean {
|
|
35
|
-
const cliPath = process.env.QWEN_PATH || 'qwen';
|
|
35
|
+
const cliPath = process.env.QWEN_CLI_PATH || process.env.QWEN_PATH || 'qwen';
|
|
36
36
|
try {
|
|
37
37
|
const result = spawn.sync(cliPath, ['--version'], { stdio: 'ignore', timeout: 5000 });
|
|
38
38
|
return !result.error && result.status === 0;
|
|
@@ -13,13 +13,16 @@ import path from 'path';
|
|
|
13
13
|
import { spawn } from 'child_process';
|
|
14
14
|
|
|
15
15
|
import express from 'express';
|
|
16
|
+
import crossSpawn from 'cross-spawn';
|
|
16
17
|
|
|
17
18
|
import { orchestrationTaskService } from '@/modules/orchestration/tasks/orchestration-task.service.js';
|
|
18
19
|
|
|
19
20
|
import { extractProjectDirectory } from '../projects.js';
|
|
20
21
|
import {
|
|
21
22
|
cancelInstallJob,
|
|
23
|
+
buildCliSpawnEnv,
|
|
22
24
|
createInstallJob,
|
|
25
|
+
findExecutableOnPath,
|
|
23
26
|
getInstallJob,
|
|
24
27
|
snapshotDonePayload
|
|
25
28
|
} from '../services/install-jobs.js';
|
|
@@ -34,70 +37,46 @@ const router = express.Router();
|
|
|
34
37
|
*/
|
|
35
38
|
async function checkTaskMasterInstallation() {
|
|
36
39
|
return new Promise((resolve) => {
|
|
37
|
-
|
|
38
|
-
const
|
|
40
|
+
const env = buildCliSpawnEnv();
|
|
41
|
+
const taskMasterPath = findExecutableOnPath('task-master', env);
|
|
42
|
+
|
|
43
|
+
if (!taskMasterPath) {
|
|
44
|
+
resolve({
|
|
45
|
+
isInstalled: false,
|
|
46
|
+
installPath: null,
|
|
47
|
+
version: null,
|
|
48
|
+
reason: 'TaskMaster CLI not found in PATH'
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const versionChild = crossSpawn(taskMasterPath, ['--version'], {
|
|
39
54
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
40
|
-
|
|
55
|
+
env,
|
|
56
|
+
windowsHide: true,
|
|
41
57
|
});
|
|
42
|
-
|
|
43
|
-
let output = '';
|
|
44
|
-
let errorOutput = '';
|
|
45
|
-
|
|
46
|
-
child.stdout.on('data', (data) => {
|
|
47
|
-
output += data.toString();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
child.stderr.on('data', (data) => {
|
|
51
|
-
errorOutput += data.toString();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
child.on('close', (code) => {
|
|
55
|
-
if (code === 0 && output.trim()) {
|
|
56
|
-
// TaskMaster is installed, get version
|
|
57
|
-
const versionChild = spawn('task-master', ['--version'], {
|
|
58
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
59
|
-
shell: true
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
let versionOutput = '';
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
versionOutput += data.toString();
|
|
66
|
-
});
|
|
59
|
+
let versionOutput = '';
|
|
67
60
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
installPath: output.trim(),
|
|
72
|
-
version: versionCode === 0 ? versionOutput.trim() : 'unknown',
|
|
73
|
-
reason: null
|
|
74
|
-
});
|
|
75
|
-
});
|
|
61
|
+
versionChild.stdout?.on('data', (data) => {
|
|
62
|
+
versionOutput += data.toString();
|
|
63
|
+
});
|
|
76
64
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
});
|
|
85
|
-
} else {
|
|
86
|
-
resolve({
|
|
87
|
-
isInstalled: false,
|
|
88
|
-
installPath: null,
|
|
89
|
-
version: null,
|
|
90
|
-
reason: 'TaskMaster CLI not found in PATH'
|
|
91
|
-
});
|
|
92
|
-
}
|
|
65
|
+
versionChild.on('close', (versionCode) => {
|
|
66
|
+
resolve({
|
|
67
|
+
isInstalled: true,
|
|
68
|
+
installPath: taskMasterPath,
|
|
69
|
+
version: versionCode === 0 ? versionOutput.trim() : 'unknown',
|
|
70
|
+
reason: null
|
|
71
|
+
});
|
|
93
72
|
});
|
|
94
|
-
|
|
95
|
-
|
|
73
|
+
|
|
74
|
+
versionChild.on('error', () => {
|
|
96
75
|
resolve({
|
|
97
|
-
isInstalled:
|
|
98
|
-
installPath:
|
|
99
|
-
version:
|
|
100
|
-
reason:
|
|
76
|
+
isInstalled: true,
|
|
77
|
+
installPath: taskMasterPath,
|
|
78
|
+
version: 'unknown',
|
|
79
|
+
reason: null
|
|
101
80
|
});
|
|
102
81
|
});
|
|
103
82
|
});
|