@pixelbyte-software/pixcode 1.34.0 → 1.35.1

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.
Files changed (247) hide show
  1. package/LICENSE +718 -718
  2. package/README.de.md +248 -248
  3. package/README.ja.md +240 -240
  4. package/README.ko.md +240 -240
  5. package/README.md +303 -303
  6. package/README.ru.md +248 -248
  7. package/README.tr.md +250 -250
  8. package/README.zh-CN.md +240 -240
  9. package/dist/api-docs.html +548 -395
  10. package/dist/assets/index-B8w57E1r.css +32 -0
  11. package/dist/assets/index-CBdsvGSR.js +854 -0
  12. package/dist/clear-cache.html +85 -85
  13. package/dist/convert-icons.md +52 -52
  14. package/dist/favicon.svg +8 -8
  15. package/dist/generate-icons.js +48 -48
  16. package/dist/icons/codex-white.svg +3 -3
  17. package/dist/icons/codex.svg +3 -3
  18. package/dist/icons/cursor-white.svg +11 -11
  19. package/dist/icons/icon-128x128.svg +9 -9
  20. package/dist/icons/icon-144x144.svg +9 -9
  21. package/dist/icons/icon-152x152.svg +9 -9
  22. package/dist/icons/icon-192x192.svg +9 -9
  23. package/dist/icons/icon-384x384.svg +9 -9
  24. package/dist/icons/icon-512x512.svg +9 -9
  25. package/dist/icons/icon-72x72.svg +9 -9
  26. package/dist/icons/icon-96x96.svg +9 -9
  27. package/dist/icons/icon-template.svg +9 -9
  28. package/dist/icons/qwen-logo.svg +14 -14
  29. package/dist/index.html +59 -59
  30. package/dist/logo.svg +12 -12
  31. package/dist/manifest.json +60 -60
  32. package/dist/openapi.yaml +1693 -1311
  33. package/dist/sw.js +124 -124
  34. package/dist-server/server/claude-sdk.js +38 -7
  35. package/dist-server/server/claude-sdk.js.map +1 -1
  36. package/dist-server/server/cli.js +107 -112
  37. package/dist-server/server/cli.js.map +1 -1
  38. package/dist-server/server/daemon/manager.js +33 -33
  39. package/dist-server/server/daemon-manager.js +159 -112
  40. package/dist-server/server/daemon-manager.js.map +1 -1
  41. package/dist-server/server/database/json-store.js +8 -5
  42. package/dist-server/server/database/json-store.js.map +1 -1
  43. package/dist-server/server/index.js +31 -10
  44. package/dist-server/server/index.js.map +1 -1
  45. package/dist-server/server/modules/orchestration/a2a/adapter-registry.js +45 -19
  46. package/dist-server/server/modules/orchestration/a2a/adapter-registry.js.map +1 -1
  47. package/dist-server/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.js.map +1 -1
  48. package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js +1 -0
  49. package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js.map +1 -1
  50. package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js +202 -0
  51. package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js.map +1 -0
  52. package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js +205 -0
  53. package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js.map +1 -0
  54. package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js +205 -0
  55. package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js.map +1 -0
  56. package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js +205 -0
  57. package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js.map +1 -0
  58. package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js +205 -0
  59. package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js.map +1 -0
  60. package/dist-server/server/modules/orchestration/a2a/routes.js +298 -34
  61. package/dist-server/server/modules/orchestration/a2a/routes.js.map +1 -1
  62. package/dist-server/server/modules/orchestration/a2a/task-store.js +144 -0
  63. package/dist-server/server/modules/orchestration/a2a/task-store.js.map +1 -0
  64. package/dist-server/server/modules/orchestration/a2a/validator.js +16 -0
  65. package/dist-server/server/modules/orchestration/a2a/validator.js.map +1 -1
  66. package/dist-server/server/modules/orchestration/index.js +14 -0
  67. package/dist-server/server/modules/orchestration/index.js.map +1 -1
  68. package/dist-server/server/modules/orchestration/preview/port-watcher.js +90 -0
  69. package/dist-server/server/modules/orchestration/preview/port-watcher.js.map +1 -0
  70. package/dist-server/server/modules/orchestration/preview/preview-proxy.js +58 -0
  71. package/dist-server/server/modules/orchestration/preview/preview-proxy.js.map +1 -0
  72. package/dist-server/server/modules/orchestration/preview/types.js +2 -0
  73. package/dist-server/server/modules/orchestration/preview/types.js.map +1 -0
  74. package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js +37 -0
  75. package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js.map +1 -0
  76. package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js +68 -0
  77. package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js.map +1 -0
  78. package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js +128 -0
  79. package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js.map +1 -0
  80. package/dist-server/server/modules/orchestration/tasks/orchestration-task.types.js +2 -0
  81. package/dist-server/server/modules/orchestration/tasks/orchestration-task.types.js.map +1 -0
  82. package/dist-server/server/modules/orchestration/workflows/built-in-workflows.js +126 -0
  83. package/dist-server/server/modules/orchestration/workflows/built-in-workflows.js.map +1 -0
  84. package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +1047 -0
  85. package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -0
  86. package/dist-server/server/modules/orchestration/workflows/workflow-store.js +76 -0
  87. package/dist-server/server/modules/orchestration/workflows/workflow-store.js.map +1 -0
  88. package/dist-server/server/modules/orchestration/workflows/workflow.routes.js +151 -0
  89. package/dist-server/server/modules/orchestration/workflows/workflow.routes.js.map +1 -0
  90. package/dist-server/server/modules/orchestration/workflows/workflow.types.js +2 -0
  91. package/dist-server/server/modules/orchestration/workflows/workflow.types.js.map +1 -0
  92. package/dist-server/server/modules/orchestration/workflows/workspace-target.js +98 -0
  93. package/dist-server/server/modules/orchestration/workflows/workspace-target.js.map +1 -0
  94. package/dist-server/server/modules/orchestration/workspace/docker-workspace.js +122 -0
  95. package/dist-server/server/modules/orchestration/workspace/docker-workspace.js.map +1 -0
  96. package/dist-server/server/modules/orchestration/workspace/path-safety.js +48 -0
  97. package/dist-server/server/modules/orchestration/workspace/path-safety.js.map +1 -0
  98. package/dist-server/server/modules/orchestration/workspace/types.js +11 -0
  99. package/dist-server/server/modules/orchestration/workspace/types.js.map +1 -0
  100. package/dist-server/server/modules/orchestration/workspace/workspace-manager.js +80 -0
  101. package/dist-server/server/modules/orchestration/workspace/workspace-manager.js.map +1 -0
  102. package/dist-server/server/modules/orchestration/workspace/worktree-workspace.js +96 -0
  103. package/dist-server/server/modules/orchestration/workspace/worktree-workspace.js.map +1 -0
  104. package/dist-server/server/modules/providers/index.js +3 -0
  105. package/dist-server/server/modules/providers/index.js.map +1 -0
  106. package/dist-server/server/openai-codex.js +35 -4
  107. package/dist-server/server/openai-codex.js.map +1 -1
  108. package/dist-server/server/routes/commands.js +25 -25
  109. package/dist-server/server/routes/git.js +17 -17
  110. package/dist-server/server/routes/taskmaster.js +525 -508
  111. package/dist-server/server/routes/taskmaster.js.map +1 -1
  112. package/package.json +180 -178
  113. package/scripts/fix-node-pty.js +67 -67
  114. package/scripts/smoke/a2a-roundtrip.mjs +86 -17
  115. package/scripts/smoke/orchestration-api.mjs +172 -0
  116. package/scripts/smoke/orchestration-live-run.mjs +176 -0
  117. package/server/claude-sdk.js +898 -857
  118. package/server/cli.js +935 -940
  119. package/server/constants/config.js +4 -4
  120. package/server/cursor-cli.js +342 -342
  121. package/server/daemon/manager.js +564 -564
  122. package/server/daemon-manager.js +959 -920
  123. package/server/database/db.js +794 -794
  124. package/server/database/json-store.js +197 -194
  125. package/server/gemini-cli.js +535 -535
  126. package/server/gemini-response-handler.js +79 -79
  127. package/server/index.js +3135 -3104
  128. package/server/load-env.js +34 -34
  129. package/server/middleware/auth.js +173 -173
  130. package/server/modules/orchestration/a2a/adapter-registry.ts +72 -22
  131. package/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.ts +9 -3
  132. package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +1 -0
  133. package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +244 -0
  134. package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +249 -0
  135. package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +248 -0
  136. package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +248 -0
  137. package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +248 -0
  138. package/server/modules/orchestration/a2a/routes.ts +349 -36
  139. package/server/modules/orchestration/a2a/task-store.ts +178 -0
  140. package/server/modules/orchestration/a2a/types.ts +14 -0
  141. package/server/modules/orchestration/a2a/validator.ts +25 -2
  142. package/server/modules/orchestration/index.ts +40 -0
  143. package/server/modules/orchestration/preview/port-watcher.ts +112 -0
  144. package/server/modules/orchestration/preview/preview-proxy.ts +60 -0
  145. package/server/modules/orchestration/preview/types.ts +19 -0
  146. package/server/modules/orchestration/tasks/orchestration-task-store.ts +45 -0
  147. package/server/modules/orchestration/tasks/orchestration-task.routes.ts +73 -0
  148. package/server/modules/orchestration/tasks/orchestration-task.service.ts +145 -0
  149. package/server/modules/orchestration/tasks/orchestration-task.types.ts +29 -0
  150. package/server/modules/orchestration/workflows/built-in-workflows.ts +127 -0
  151. package/server/modules/orchestration/workflows/workflow-runner.ts +1206 -0
  152. package/server/modules/orchestration/workflows/workflow-store.ts +97 -0
  153. package/server/modules/orchestration/workflows/workflow.routes.ts +169 -0
  154. package/server/modules/orchestration/workflows/workflow.types.ts +70 -0
  155. package/server/modules/orchestration/workflows/workspace-target.ts +120 -0
  156. package/server/modules/orchestration/workspace/docker-workspace.ts +135 -0
  157. package/server/modules/orchestration/workspace/path-safety.ts +55 -0
  158. package/server/modules/orchestration/workspace/types.ts +52 -0
  159. package/server/modules/orchestration/workspace/workspace-manager.ts +97 -0
  160. package/server/modules/orchestration/workspace/worktree-workspace.ts +125 -0
  161. package/server/modules/providers/index.ts +2 -0
  162. package/server/modules/providers/list/claude/claude-auth.provider.ts +145 -145
  163. package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
  164. package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -306
  165. package/server/modules/providers/list/claude/claude.provider.ts +15 -15
  166. package/server/modules/providers/list/codex/codex-auth.provider.ts +115 -115
  167. package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
  168. package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -319
  169. package/server/modules/providers/list/codex/codex.provider.ts +15 -15
  170. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -143
  171. package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
  172. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -421
  173. package/server/modules/providers/list/cursor/cursor.provider.ts +15 -15
  174. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +163 -163
  175. package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -110
  176. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -227
  177. package/server/modules/providers/list/gemini/gemini.provider.ts +15 -15
  178. package/server/modules/providers/list/opencode/opencode-auth.provider.ts +130 -130
  179. package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +126 -126
  180. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +232 -232
  181. package/server/modules/providers/list/opencode/opencode.provider.ts +29 -29
  182. package/server/modules/providers/list/qwen/qwen-auth.provider.ts +145 -145
  183. package/server/modules/providers/list/qwen/qwen-mcp.provider.ts +114 -114
  184. package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +265 -265
  185. package/server/modules/providers/list/qwen/qwen.provider.ts +21 -21
  186. package/server/modules/providers/provider.registry.ts +40 -40
  187. package/server/modules/providers/provider.routes.ts +819 -819
  188. package/server/modules/providers/services/mcp.service.ts +86 -86
  189. package/server/modules/providers/services/provider-auth.service.ts +26 -26
  190. package/server/modules/providers/services/sessions.service.ts +45 -45
  191. package/server/modules/providers/shared/base/abstract.provider.ts +20 -20
  192. package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
  193. package/server/modules/providers/shared/provider-configs.ts +142 -142
  194. package/server/modules/providers/tests/mcp.test.ts +293 -293
  195. package/server/openai-codex.js +462 -426
  196. package/server/opencode-cli.js +459 -459
  197. package/server/opencode-response-handler.js +107 -107
  198. package/server/projects.js +3105 -3105
  199. package/server/qwen-code-cli.js +395 -395
  200. package/server/qwen-response-handler.js +73 -73
  201. package/server/routes/agent.js +1365 -1365
  202. package/server/routes/auth.js +138 -138
  203. package/server/routes/codex.js +19 -19
  204. package/server/routes/commands.js +554 -554
  205. package/server/routes/cursor.js +52 -52
  206. package/server/routes/gemini.js +24 -24
  207. package/server/routes/git.js +1488 -1488
  208. package/server/routes/mcp-utils.js +31 -31
  209. package/server/routes/messages.js +61 -61
  210. package/server/routes/network.js +120 -120
  211. package/server/routes/plugins.js +318 -318
  212. package/server/routes/projects.js +915 -915
  213. package/server/routes/qwen.js +27 -27
  214. package/server/routes/settings.js +286 -286
  215. package/server/routes/taskmaster.js +1496 -1471
  216. package/server/routes/telegram.js +125 -125
  217. package/server/routes/user.js +123 -123
  218. package/server/services/external-access.js +171 -171
  219. package/server/services/install-jobs.js +571 -571
  220. package/server/services/notification-orchestrator.js +242 -242
  221. package/server/services/provider-credentials.js +189 -189
  222. package/server/services/provider-models.js +381 -381
  223. package/server/services/telegram/bot.js +279 -279
  224. package/server/services/telegram/telegram-http-client.js +130 -130
  225. package/server/services/telegram/translations.js +170 -170
  226. package/server/services/vapid-keys.js +36 -36
  227. package/server/sessionManager.js +225 -225
  228. package/server/shared/interfaces.ts +54 -54
  229. package/server/shared/types.ts +172 -172
  230. package/server/shared/utils.ts +193 -193
  231. package/server/tsconfig.json +36 -36
  232. package/server/utils/colors.js +21 -21
  233. package/server/utils/commandParser.js +303 -303
  234. package/server/utils/frontmatter.js +18 -18
  235. package/server/utils/gitConfig.js +34 -34
  236. package/server/utils/mcp-detector.js +147 -147
  237. package/server/utils/plugin-loader.js +457 -457
  238. package/server/utils/plugin-process-manager.js +184 -184
  239. package/server/utils/port-access.js +209 -209
  240. package/server/utils/runtime-paths.js +37 -37
  241. package/server/utils/taskmaster-websocket.js +128 -128
  242. package/server/utils/url-detection.js +71 -71
  243. package/server/vite-daemon.js +78 -78
  244. package/shared/modelConstants.js +162 -162
  245. package/shared/networkHosts.js +22 -22
  246. package/dist/assets/index-B1ghfb4w.css +0 -32
  247. package/dist/assets/index-BvClqlMf.js +0 -852
@@ -0,0 +1,97 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ import { DockerWorkspace } from '@/modules/orchestration/workspace/docker-workspace.js';
5
+ import { safeJoin } from '@/modules/orchestration/workspace/path-safety.js';
6
+ import type {
7
+ ExecResult,
8
+ WorkspaceHandle,
9
+ WorkspaceKind,
10
+ WorkspaceRequest,
11
+ } from '@/modules/orchestration/workspace/types.js';
12
+ import { WorktreeWorkspace } from '@/modules/orchestration/workspace/worktree-workspace.js';
13
+
14
+ class HostWorkspace implements WorkspaceHandle {
15
+ readonly kind = 'host' as const;
16
+ readonly id: string;
17
+ readonly path: string;
18
+ readonly baseRef: string;
19
+ readonly metadata: Record<string, unknown>;
20
+
21
+ constructor(request: WorkspaceRequest) {
22
+ this.id = `host_${request.taskId}`;
23
+ this.path = request.projectPath;
24
+ this.baseRef = request.baseRef ?? 'HEAD';
25
+ this.metadata = { ...request.metadata, keepAfterCompletion: true };
26
+ }
27
+
28
+ async exec(command: string, args: string[] = []): Promise<ExecResult> {
29
+ const { execFile } = await import('node:child_process');
30
+ const { promisify } = await import('node:util');
31
+ const execFileAsync = promisify(execFile);
32
+ try {
33
+ const { stdout, stderr } = await execFileAsync(command, args, {
34
+ cwd: this.path,
35
+ maxBuffer: 20 * 1024 * 1024,
36
+ });
37
+ return { stdout: String(stdout), stderr: String(stderr), exitCode: 0 };
38
+ } catch (error) {
39
+ const err = error as Error & { stdout?: string; stderr?: string; code?: number };
40
+ return {
41
+ stdout: String(err.stdout ?? ''),
42
+ stderr: String(err.stderr ?? err.message),
43
+ exitCode: typeof err.code === 'number' ? err.code : 1,
44
+ };
45
+ }
46
+ }
47
+
48
+ async readFile(relativePath: string): Promise<string> {
49
+ return fs.promises.readFile(safeJoin(this.path, relativePath), 'utf8');
50
+ }
51
+
52
+ async writeFile(relativePath: string, content: string): Promise<void> {
53
+ const target = safeJoin(this.path, relativePath);
54
+ await fs.promises.mkdir(path.dirname(target), { recursive: true });
55
+ await fs.promises.writeFile(target, content, 'utf8');
56
+ }
57
+
58
+ async diff(): Promise<string> {
59
+ const result = await this.exec('git', ['diff', `${this.baseRef}...HEAD`]);
60
+ return result.exitCode === 0 ? result.stdout : result.stderr || result.stdout;
61
+ }
62
+
63
+ destroy(): Promise<void> {
64
+ return Promise.resolve();
65
+ }
66
+ }
67
+
68
+ function readKind(value: unknown): WorkspaceKind {
69
+ return value === 'host' || value === 'docker' || value === 'worktree' ? value : 'worktree';
70
+ }
71
+
72
+ class WorkspaceManager {
73
+ async create(request: WorkspaceRequest): Promise<WorkspaceHandle> {
74
+ const kind = readKind(request.kind);
75
+ if (kind === 'host') {
76
+ return new HostWorkspace(request);
77
+ }
78
+ if (kind === 'docker') {
79
+ return new DockerWorkspace(request);
80
+ }
81
+ return WorktreeWorkspace.create(request);
82
+ }
83
+
84
+ recover(request: WorkspaceRequest): WorkspaceHandle {
85
+ return new HostWorkspace({
86
+ ...request,
87
+ kind: 'host',
88
+ metadata: {
89
+ ...request.metadata,
90
+ recovered: true,
91
+ },
92
+ });
93
+ }
94
+ }
95
+
96
+ export const workspaceManager = new WorkspaceManager();
97
+ export type { WorkspaceManager };
@@ -0,0 +1,125 @@
1
+ import { execFile } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { promisify } from 'node:util';
5
+
6
+ import { assertSafeId, safeJoin, safeWorkspacePath } from '@/modules/orchestration/workspace/path-safety.js';
7
+ import type {
8
+ ExecResult,
9
+ WorkspaceHandle,
10
+ WorkspaceRequest,
11
+ } from '@/modules/orchestration/workspace/types.js';
12
+ import { WorkspaceError } from '@/modules/orchestration/workspace/types.js';
13
+
14
+ const execFileAsync = promisify(execFile);
15
+
16
+ async function run(
17
+ command: string,
18
+ args: string[],
19
+ cwd: string,
20
+ ): Promise<ExecResult> {
21
+ try {
22
+ const { stdout, stderr } = await execFileAsync(command, args, {
23
+ cwd,
24
+ maxBuffer: 20 * 1024 * 1024,
25
+ });
26
+ return {
27
+ stdout: String(stdout),
28
+ stderr: String(stderr),
29
+ exitCode: 0,
30
+ };
31
+ } catch (error) {
32
+ const err = error as Error & {
33
+ stdout?: string | Buffer;
34
+ stderr?: string | Buffer;
35
+ code?: number;
36
+ };
37
+ return {
38
+ stdout: String(err.stdout ?? ''),
39
+ stderr: String(err.stderr ?? err.message),
40
+ exitCode: typeof err.code === 'number' ? err.code : 1,
41
+ };
42
+ }
43
+ }
44
+
45
+ export class WorktreeWorkspace implements WorkspaceHandle {
46
+ readonly kind = 'worktree' as const;
47
+ readonly id: string;
48
+ readonly path: string;
49
+ readonly baseRef: string;
50
+ readonly branchName: string;
51
+ readonly metadata: Record<string, unknown>;
52
+ private readonly projectPath: string;
53
+
54
+ private constructor(request: WorkspaceRequest, workspaceId: string, branchName: string) {
55
+ this.id = workspaceId;
56
+ this.path = safeWorkspacePath(workspaceId);
57
+ this.baseRef = request.baseRef ?? 'HEAD';
58
+ this.branchName = branchName;
59
+ this.projectPath = request.projectPath;
60
+ this.metadata = {
61
+ ...request.metadata,
62
+ keepAfterCompletion: request.keepAfterCompletion === true,
63
+ };
64
+ }
65
+
66
+ static async create(request: WorkspaceRequest): Promise<WorktreeWorkspace> {
67
+ assertSafeId(request.taskId, 'task id');
68
+ const workspaceId = `ws_${request.taskId}`;
69
+ const branchName = `pixcode/task_${request.taskId}`;
70
+ const workspace = new WorktreeWorkspace(request, workspaceId, branchName);
71
+
72
+ const existingBranch = await run('git', ['rev-parse', '--verify', branchName], request.projectPath);
73
+ if (existingBranch.exitCode === 0) {
74
+ throw new WorkspaceError('WORKSPACE_EXISTS', `Workspace branch already exists: ${branchName}`, {
75
+ branchName,
76
+ });
77
+ }
78
+
79
+ const result = await run(
80
+ 'git',
81
+ ['worktree', 'add', workspace.path, '-b', branchName, workspace.baseRef],
82
+ request.projectPath,
83
+ );
84
+ if (result.exitCode !== 0) {
85
+ throw new WorkspaceError('WORKSPACE_CREATE_FAILED', result.stderr || result.stdout, {
86
+ stdout: result.stdout,
87
+ stderr: result.stderr,
88
+ baseRef: workspace.baseRef,
89
+ });
90
+ }
91
+
92
+ return workspace;
93
+ }
94
+
95
+ exec(command: string, args: string[] = []): Promise<ExecResult> {
96
+ return run(command, args, this.path);
97
+ }
98
+
99
+ async readFile(relativePath: string): Promise<string> {
100
+ return fs.readFile(safeJoin(this.path, relativePath), 'utf8');
101
+ }
102
+
103
+ async writeFile(relativePath: string, content: string): Promise<void> {
104
+ const target = safeJoin(this.path, relativePath);
105
+ await fs.mkdir(path.dirname(target), { recursive: true });
106
+ await fs.writeFile(target, content, 'utf8');
107
+ }
108
+
109
+ async diff(): Promise<string> {
110
+ const result = await run('git', ['diff', `${this.baseRef}...HEAD`], this.path);
111
+ if (result.exitCode !== 0) {
112
+ return result.stderr || result.stdout;
113
+ }
114
+ return result.stdout;
115
+ }
116
+
117
+ async destroy(): Promise<void> {
118
+ const result = await run('git', ['worktree', 'remove', '--force', this.path], this.projectPath);
119
+ if (result.exitCode !== 0) {
120
+ throw new WorkspaceError('WORKSPACE_DESTROY_FAILED', result.stderr || result.stdout, {
121
+ workspacePath: this.path,
122
+ });
123
+ }
124
+ }
125
+ }
@@ -0,0 +1,2 @@
1
+ export { providerRegistry } from './provider.registry.js';
2
+ export { CodexSessionsProvider } from './list/codex/codex-sessions.provider.js';
@@ -1,145 +1,145 @@
1
- import { readFile } from 'node:fs/promises';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
-
5
- import spawn from 'cross-spawn';
6
-
7
- import type { IProviderAuth } from '@/shared/interfaces.js';
8
- import type { ProviderAuthStatus } from '@/shared/types.js';
9
- import { readObjectRecord, readOptionalString } from '@/shared/utils.js';
10
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
11
- // @ts-ignore — plain-JS module
12
- import { getProviderCredentials } from '@/services/provider-credentials.js';
13
-
14
- type ClaudeCredentialsStatus = {
15
- authenticated: boolean;
16
- email: string | null;
17
- method: string | null;
18
- error?: string;
19
- };
20
-
21
- export class ClaudeProviderAuth implements IProviderAuth {
22
- /**
23
- * Checks whether the Claude Code CLI is available on this host.
24
- *
25
- * NOTE: `cross-spawn.sync` does NOT throw on ENOENT — it returns a result
26
- * object with `error` populated. The try/catch alone was always returning
27
- * true and every provider appeared "installed". We now require both
28
- * `!result.error` and a numeric exit status (0 for `--version`) before
29
- * trusting the install.
30
- */
31
- private checkInstalled(): boolean {
32
- const cliPath = process.env.CLAUDE_CLI_PATH || 'claude';
33
- try {
34
- const result = spawn.sync(cliPath, ['--version'], { stdio: 'ignore', timeout: 5000 });
35
- return !result.error && result.status === 0;
36
- } catch {
37
- return false;
38
- }
39
- }
40
-
41
- /**
42
- * Returns Claude installation and credential status using Claude Code's auth priority.
43
- */
44
- async getStatus(): Promise<ProviderAuthStatus> {
45
- const installed = this.checkInstalled();
46
-
47
- if (!installed) {
48
- return {
49
- installed,
50
- provider: 'claude',
51
- authenticated: false,
52
- email: null,
53
- method: null,
54
- error: 'Claude Code CLI is not installed',
55
- };
56
- }
57
-
58
- const credentials = await this.checkCredentials();
59
-
60
- return {
61
- installed,
62
- provider: 'claude',
63
- authenticated: credentials.authenticated,
64
- email: credentials.authenticated ? credentials.email || 'Authenticated' : credentials.email,
65
- method: credentials.method,
66
- error: credentials.authenticated ? undefined : credentials.error || 'Not authenticated',
67
- };
68
- }
69
-
70
- /**
71
- * Reads Claude settings env values that the CLI can use even when the server process env is empty.
72
- */
73
- private async loadSettingsEnv(): Promise<Record<string, unknown>> {
74
- try {
75
- const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
76
- const content = await readFile(settingsPath, 'utf8');
77
- const settings = readObjectRecord(JSON.parse(content));
78
- return readObjectRecord(settings?.env) ?? {};
79
- } catch {
80
- return {};
81
- }
82
- }
83
-
84
- /**
85
- * Checks Claude credentials in the same priority order used by Claude Code.
86
- */
87
- private async checkCredentials(): Promise<ClaudeCredentialsStatus> {
88
- // Pixcode-UI-saved credentials win. Users who paste a key into our
89
- // Settings > Agents form expect authenticated status immediately,
90
- // regardless of env var timing.
91
- try {
92
- const creds = await getProviderCredentials('claude');
93
- if (creds?.apiKey) {
94
- const label = creds.baseUrl
95
- ? `API Key · ${(() => { try { return new URL(creds.baseUrl).host; } catch { return creds.baseUrl; } })()}`
96
- : 'API Key Auth';
97
- return { authenticated: true, email: label, method: 'pixcode_store' };
98
- }
99
- } catch { /* fall through */ }
100
-
101
- if (process.env.ANTHROPIC_API_KEY?.trim()) {
102
- return { authenticated: true, email: 'API Key Auth', method: 'api_key' };
103
- }
104
-
105
- const settingsEnv = await this.loadSettingsEnv();
106
- if (readOptionalString(settingsEnv.ANTHROPIC_API_KEY)) {
107
- return { authenticated: true, email: 'API Key Auth', method: 'api_key' };
108
- }
109
-
110
- if (readOptionalString(settingsEnv.ANTHROPIC_AUTH_TOKEN)) {
111
- return { authenticated: true, email: 'Configured via settings.json', method: 'api_key' };
112
- }
113
-
114
- try {
115
- const credPath = path.join(os.homedir(), '.claude', '.credentials.json');
116
- const content = await readFile(credPath, 'utf8');
117
- const creds = readObjectRecord(JSON.parse(content)) ?? {};
118
- const oauth = readObjectRecord(creds.claudeAiOauth);
119
- const accessToken = readOptionalString(oauth?.accessToken);
120
-
121
- if (accessToken) {
122
- const expiresAt = typeof oauth?.expiresAt === 'number' ? oauth.expiresAt : undefined;
123
- const email = readOptionalString(creds.email) ?? readOptionalString(creds.user) ?? null;
124
- if (!expiresAt || Date.now() < expiresAt) {
125
- return {
126
- authenticated: true,
127
- email,
128
- method: 'credentials_file',
129
- };
130
- }
131
-
132
- return {
133
- authenticated: false,
134
- email,
135
- method: 'credentials_file',
136
- error: 'OAuth token has expired. Please re-authenticate with claude login',
137
- };
138
- }
139
-
140
- return { authenticated: false, email: null, method: null };
141
- } catch {
142
- return { authenticated: false, email: null, method: null };
143
- }
144
- }
145
- }
1
+ import { readFile } from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+
5
+ import spawn from 'cross-spawn';
6
+
7
+ import type { IProviderAuth } from '@/shared/interfaces.js';
8
+ import type { ProviderAuthStatus } from '@/shared/types.js';
9
+ import { readObjectRecord, readOptionalString } from '@/shared/utils.js';
10
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
11
+ // @ts-ignore — plain-JS module
12
+ import { getProviderCredentials } from '@/services/provider-credentials.js';
13
+
14
+ type ClaudeCredentialsStatus = {
15
+ authenticated: boolean;
16
+ email: string | null;
17
+ method: string | null;
18
+ error?: string;
19
+ };
20
+
21
+ export class ClaudeProviderAuth implements IProviderAuth {
22
+ /**
23
+ * Checks whether the Claude Code CLI is available on this host.
24
+ *
25
+ * NOTE: `cross-spawn.sync` does NOT throw on ENOENT — it returns a result
26
+ * object with `error` populated. The try/catch alone was always returning
27
+ * true and every provider appeared "installed". We now require both
28
+ * `!result.error` and a numeric exit status (0 for `--version`) before
29
+ * trusting the install.
30
+ */
31
+ private checkInstalled(): boolean {
32
+ const cliPath = process.env.CLAUDE_CLI_PATH || 'claude';
33
+ try {
34
+ const result = spawn.sync(cliPath, ['--version'], { stdio: 'ignore', timeout: 5000 });
35
+ return !result.error && result.status === 0;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Returns Claude installation and credential status using Claude Code's auth priority.
43
+ */
44
+ async getStatus(): Promise<ProviderAuthStatus> {
45
+ const installed = this.checkInstalled();
46
+
47
+ if (!installed) {
48
+ return {
49
+ installed,
50
+ provider: 'claude',
51
+ authenticated: false,
52
+ email: null,
53
+ method: null,
54
+ error: 'Claude Code CLI is not installed',
55
+ };
56
+ }
57
+
58
+ const credentials = await this.checkCredentials();
59
+
60
+ return {
61
+ installed,
62
+ provider: 'claude',
63
+ authenticated: credentials.authenticated,
64
+ email: credentials.authenticated ? credentials.email || 'Authenticated' : credentials.email,
65
+ method: credentials.method,
66
+ error: credentials.authenticated ? undefined : credentials.error || 'Not authenticated',
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Reads Claude settings env values that the CLI can use even when the server process env is empty.
72
+ */
73
+ private async loadSettingsEnv(): Promise<Record<string, unknown>> {
74
+ try {
75
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
76
+ const content = await readFile(settingsPath, 'utf8');
77
+ const settings = readObjectRecord(JSON.parse(content));
78
+ return readObjectRecord(settings?.env) ?? {};
79
+ } catch {
80
+ return {};
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Checks Claude credentials in the same priority order used by Claude Code.
86
+ */
87
+ private async checkCredentials(): Promise<ClaudeCredentialsStatus> {
88
+ // Pixcode-UI-saved credentials win. Users who paste a key into our
89
+ // Settings > Agents form expect authenticated status immediately,
90
+ // regardless of env var timing.
91
+ try {
92
+ const creds = await getProviderCredentials('claude');
93
+ if (creds?.apiKey) {
94
+ const label = creds.baseUrl
95
+ ? `API Key · ${(() => { try { return new URL(creds.baseUrl).host; } catch { return creds.baseUrl; } })()}`
96
+ : 'API Key Auth';
97
+ return { authenticated: true, email: label, method: 'pixcode_store' };
98
+ }
99
+ } catch { /* fall through */ }
100
+
101
+ if (process.env.ANTHROPIC_API_KEY?.trim()) {
102
+ return { authenticated: true, email: 'API Key Auth', method: 'api_key' };
103
+ }
104
+
105
+ const settingsEnv = await this.loadSettingsEnv();
106
+ if (readOptionalString(settingsEnv.ANTHROPIC_API_KEY)) {
107
+ return { authenticated: true, email: 'API Key Auth', method: 'api_key' };
108
+ }
109
+
110
+ if (readOptionalString(settingsEnv.ANTHROPIC_AUTH_TOKEN)) {
111
+ return { authenticated: true, email: 'Configured via settings.json', method: 'api_key' };
112
+ }
113
+
114
+ try {
115
+ const credPath = path.join(os.homedir(), '.claude', '.credentials.json');
116
+ const content = await readFile(credPath, 'utf8');
117
+ const creds = readObjectRecord(JSON.parse(content)) ?? {};
118
+ const oauth = readObjectRecord(creds.claudeAiOauth);
119
+ const accessToken = readOptionalString(oauth?.accessToken);
120
+
121
+ if (accessToken) {
122
+ const expiresAt = typeof oauth?.expiresAt === 'number' ? oauth.expiresAt : undefined;
123
+ const email = readOptionalString(creds.email) ?? readOptionalString(creds.user) ?? null;
124
+ if (!expiresAt || Date.now() < expiresAt) {
125
+ return {
126
+ authenticated: true,
127
+ email,
128
+ method: 'credentials_file',
129
+ };
130
+ }
131
+
132
+ return {
133
+ authenticated: false,
134
+ email,
135
+ method: 'credentials_file',
136
+ error: 'OAuth token has expired. Please re-authenticate with claude login',
137
+ };
138
+ }
139
+
140
+ return { authenticated: false, email: null, method: null };
141
+ } catch {
142
+ return { authenticated: false, email: null, method: null };
143
+ }
144
+ }
145
+ }