| author | |
| committer | |
| log | 70a9a043eecc0da4d11f30416add232b4bf6e0f1 |
| tree | 4ae28914083e29f210d790175b572d26ffdecee3 |
| parent | eef57db955614e346091769bc6d4e80e0068e9b6 |
| signature | Signed by PGP key B5690EEEBB952194 |
* oxc stacak
* kewl
* unused
* unused37 files changed, 217 insertions(+), 407 deletions(-)
.oxfmtrc.json created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | { | ||
| 2 | "$schema": "./node_modules/oxfmt/configuration_schema.json", | ||
| 3 | "ignorePatterns": [ | ||
| 4 | ".plans", | ||
| 5 | "dist", | ||
| 6 | "dist-electron", | ||
| 7 | "node_modules", | ||
| 8 | "bun.lock", | ||
| 9 | "*.tsbuildinfo" | ||
| 10 | ], | ||
| 11 | "experimentalSortPackageJson": {} | ||
| 12 | } | ||
.oxlintrc.json created+13| ... | @@ -0,0 +1,13 @@ | ||
| 1 | { | ||
| 2 | "$schema": "./node_modules/oxlint/configuration_schema.json", | ||
| 3 | "ignorePatterns": ["dist", "dist-electron", "node_modules", "bun.lock", "*.tsbuildinfo"], | ||
| 4 | "plugins": ["eslint", "oxc", "react", "unicorn", "typescript"], | ||
| 5 | "categories": { | ||
| 6 | "correctness": "warn", | ||
| 7 | "suspicious": "warn", | ||
| 8 | "perf": "warn" | ||
| 9 | }, | ||
| 10 | "rules": { | ||
| 11 | "react-in-jsx-scope": "off" | ||
| 12 | } | ||
| 13 | } | ||
.vscode/extensions.json created+3| ... | @@ -0,0 +1,3 @@ | ||
| 1 | { | ||
| 2 | "recommendations": ["oxc.oxc-vscode"] | ||
| 3 | } | ||
.vscode/settings.json created+8| ... | @@ -0,0 +1,8 @@ | ||
| 1 | { | ||
| 2 | "editor.formatOnSave": true, | ||
| 3 | "editor.defaultFormatter": "oxc.oxc-vscode", | ||
| 4 | "editor.codeActionsOnSave": { | ||
| 5 | "source.fixAll.oxc": "always" | ||
| 6 | }, | ||
| 7 | "oxc.unusedDisableDirectives": "warn" | ||
| 8 | } | ||
README.md+1| ... | @@ -49,6 +49,7 @@ Mode changes apply across all threads. Existing live sessions are restarted so o | ... | @@ -49,6 +49,7 @@ Mode changes apply across all threads. Existing live sessions are restarted so o |
| 49 | - `.github/workflows/ci.yml` runs `bun run lint`, `bun run typecheck`, and `bun run test` on pull requests and pushes to `main`. | 49 | - `.github/workflows/ci.yml` runs `bun run lint`, `bun run typecheck`, and `bun run test` on pull requests and pushes to `main`. |
| 50 | 50 | ||
| 51 | Optional: | 51 | Optional: |
| 52 | |||
| 52 | - `ELECTRON_RENDERER_PORT=5180 bun run dev` if `5173` is already in use. | 53 | - `ELECTRON_RENDERER_PORT=5180 bun run dev` if `5173` is already in use. |
| 53 | 54 | ||
| 54 | ## Provider architecture | 55 | ## Provider architecture |
apps/desktop/package.json+1-3| ... | @@ -4,14 +4,13 @@ | ... | @@ -4,14 +4,13 @@ |
| 4 | "private": true, | 4 | "private": true, |
| 5 | "main": "dist-electron/main.js", | 5 | "main": "dist-electron/main.js", |
| 6 | "scripts": { | 6 | "scripts": { |
| 7 | "dev": "concurrently -k -n BUNDLE,ELECTRON \"bun run dev:bundle\" \"bun run dev:electron\"", | 7 | "dev": "bun run --parallel dev:bundle dev:electron", |
| 8 | "dev:bundle": "tsup --watch", | 8 | "dev:bundle": "tsup --watch", |
| 9 | "dev:electron": "bun run scripts/dev-electron.mjs", | 9 | "dev:electron": "bun run scripts/dev-electron.mjs", |
| 10 | "build": "tsup", | 10 | "build": "tsup", |
| 11 | "start": "electron dist-electron/main.js", | 11 | "start": "electron dist-electron/main.js", |
| 12 | "postinstall": "electron-rebuild", | 12 | "postinstall": "electron-rebuild", |
| 13 | "typecheck": "tsc --noEmit", | 13 | "typecheck": "tsc --noEmit", |
| 14 | "lint": "biome check src/", | ||
| 15 | "test": "vitest run", | 14 | "test": "vitest run", |
| 16 | "smoke-test": "node scripts/smoke-test.mjs" | 15 | "smoke-test": "node scripts/smoke-test.mjs" |
| 17 | }, | 16 | }, |
| ... | @@ -23,7 +22,6 @@ | ... | @@ -23,7 +22,6 @@ |
| 23 | "devDependencies": { | 22 | "devDependencies": { |
| 24 | "@electron/rebuild": "^3.7.0", | 23 | "@electron/rebuild": "^3.7.0", |
| 25 | "@types/node": "^22.10.2", | 24 | "@types/node": "^22.10.2", |
| 26 | "concurrently": "^9.1.2", | ||
| 27 | "electronmon": "^2.0.2", | 25 | "electronmon": "^2.0.2", |
| 28 | "tsup": "^8.3.5", | 26 | "tsup": "^8.3.5", |
| 29 | "typescript": "^5.7.3", | 27 | "typescript": "^5.7.3", |
apps/desktop/scripts/dev-electron.mjs+2-7| ... | @@ -6,15 +6,10 @@ const port = Number(process.env.ELECTRON_RENDERER_PORT ?? 5173); | ... | @@ -6,15 +6,10 @@ const port = Number(process.env.ELECTRON_RENDERER_PORT ?? 5173); |
| 6 | const devServerUrl = `http://localhost:${port}`; | 6 | const devServerUrl = `http://localhost:${port}`; |
| 7 | 7 | ||
| 8 | await waitOn({ | 8 | await waitOn({ |
| 9 | resources: [ | 9 | resources: [`tcp:${port}`, "file:dist-electron/main.js", "file:dist-electron/preload.js"], |
| 10 | `tcp:${port}`, | ||
| 11 | "file:dist-electron/main.js", | ||
| 12 | "file:dist-electron/preload.js", | ||
| 13 | ], | ||
| 14 | }); | 10 | }); |
| 15 | 11 | ||
| 16 | const command = | 12 | const command = process.platform === "win32" ? "electronmon.cmd" : "electronmon"; |
| 17 | process.platform === "win32" ? "electronmon.cmd" : "electronmon"; | ||
| 18 | const child = spawn(command, ["dist-electron/main.js"], { | 13 | const child = spawn(command, ["dist-electron/main.js"], { |
| 19 | stdio: "inherit", | 14 | stdio: "inherit", |
| 20 | env: { | 15 | env: { |
apps/desktop/src/codexAppServerManager.test.ts+3-9| ... | @@ -1,9 +1,6 @@ | ... | @@ -1,9 +1,6 @@ |
| 1 | import { describe, expect, it } from "vitest"; | 1 | import { describe, expect, it } from "vitest"; |
| 2 | 2 | ||
| 3 | import { | 3 | import { classifyCodexStderrLine, normalizeCodexModelSlug } from "./codexAppServerManager"; |
| 4 | classifyCodexStderrLine, | ||
| 5 | normalizeCodexModelSlug, | ||
| 6 | } from "./codexAppServerManager"; | ||
| 7 | 4 | ||
| 8 | describe("classifyCodexStderrLine", () => { | 5 | describe("classifyCodexStderrLine", () => { |
| 9 | it("ignores empty lines", () => { | 6 | it("ignores empty lines", () => { |
| ... | @@ -23,8 +20,7 @@ describe("classifyCodexStderrLine", () => { | ... | @@ -23,8 +20,7 @@ describe("classifyCodexStderrLine", () => { |
| 23 | }); | 20 | }); |
| 24 | 21 | ||
| 25 | it("keeps unknown structured errors", () => { | 22 | it("keeps unknown structured errors", () => { |
| 26 | const line = | 23 | const line = "2026-02-08T04:24:20.085687Z ERROR codex_core::runtime: unrecoverable failure"; |
| 27 | "2026-02-08T04:24:20.085687Z ERROR codex_core::runtime: unrecoverable failure"; | ||
| 28 | expect(classifyCodexStderrLine(line)).toEqual({ | 24 | expect(classifyCodexStderrLine(line)).toEqual({ |
| 29 | message: line, | 25 | message: line, |
| 30 | }); | 26 | }); |
| ... | @@ -45,9 +41,7 @@ describe("normalizeCodexModelSlug", () => { | ... | @@ -45,9 +41,7 @@ describe("normalizeCodexModelSlug", () => { |
| 45 | }); | 41 | }); |
| 46 | 42 | ||
| 47 | it("prefers codex id when model differs", () => { | 43 | it("prefers codex id when model differs", () => { |
| 48 | expect(normalizeCodexModelSlug("gpt-5.3", "gpt-5.3-codex")).toBe( | 44 | expect(normalizeCodexModelSlug("gpt-5.3", "gpt-5.3-codex")).toBe("gpt-5.3-codex"); |
| 49 | "gpt-5.3-codex", | ||
| 50 | ); | ||
| 51 | }); | 45 | }); |
| 52 | 46 | ||
| 53 | it("keeps non-aliased models as-is", () => { | 47 | it("keeps non-aliased models as-is", () => { |
apps/desktop/src/codexAppServerManager.ts+24-89| ... | @@ -91,9 +91,7 @@ export function normalizeCodexModelSlug( | ... | @@ -91,9 +91,7 @@ export function normalizeCodexModelSlug( |
| 91 | return normalized; | 91 | return normalized; |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | export function classifyCodexStderrLine( | 94 | export function classifyCodexStderrLine(rawLine: string): { message: string } | null { |
| 95 | rawLine: string, | ||
| 96 | ): { message: string } | null { | ||
| 97 | const line = rawLine.replaceAll(ANSI_ESCAPE_REGEX, "").trim(); | 95 | const line = rawLine.replaceAll(ANSI_ESCAPE_REGEX, "").trim(); |
| 98 | if (!line) { | 96 | if (!line) { |
| 99 | return null; | 97 | return null; |
| ... | @@ -106,9 +104,7 @@ export function classifyCodexStderrLine( | ... | @@ -106,9 +104,7 @@ export function classifyCodexStderrLine( |
| 106 | return null; | 104 | return null; |
| 107 | } | 105 | } |
| 108 | 106 | ||
| 109 | const isBenignError = BENIGN_ERROR_LOG_SNIPPETS.some((snippet) => | 107 | const isBenignError = BENIGN_ERROR_LOG_SNIPPETS.some((snippet) => line.includes(snippet)); |
| 110 | line.includes(snippet), | ||
| 111 | ); | ||
| 112 | if (isBenignError) { | 108 | if (isBenignError) { |
| 113 | return null; | 109 | return null; |
| 114 | } | 110 | } |
| ... | @@ -124,9 +120,7 @@ export interface CodexAppServerManagerEvents { | ... | @@ -124,9 +120,7 @@ export interface CodexAppServerManagerEvents { |
| 124 | export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEvents> { | 120 | export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEvents> { |
| 125 | private readonly sessions = new Map<string, CodexSessionContext>(); | 121 | private readonly sessions = new Map<string, CodexSessionContext>(); |
| 126 | 122 | ||
| 127 | async startSession( | 123 | async startSession(input: ProviderSessionStartInput): Promise<ProviderSession> { |
| 128 | input: ProviderSessionStartInput, | ||
| 129 | ): Promise<ProviderSession> { | ||
| 130 | const sessionId = randomUUID(); | 124 | const sessionId = randomUUID(); |
| 131 | const now = new Date().toISOString(); | 125 | const now = new Date().toISOString(); |
| 132 | 126 | ||
| ... | @@ -160,11 +154,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -160,11 +154,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 160 | this.sessions.set(sessionId, context); | 154 | this.sessions.set(sessionId, context); |
| 161 | this.attachProcessListeners(context); | 155 | this.attachProcessListeners(context); |
| 162 | 156 | ||
| 163 | this.emitLifecycleEvent( | 157 | this.emitLifecycleEvent(context, "session/connecting", "Starting codex app-server"); |
| 164 | context, | ||
| 165 | "session/connecting", | ||
| 166 | "Starting codex app-server", | ||
| 167 | ); | ||
| 168 | 158 | ||
| 169 | try { | 159 | try { |
| 170 | await this.sendRequest(context, "initialize", { | 160 | await this.sendRequest(context, "initialize", { |
| ... | @@ -188,10 +178,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -188,10 +178,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 188 | experimentalRawEvents: false, | 178 | experimentalRawEvents: false, |
| 189 | }); | 179 | }); |
| 190 | 180 | ||
| 191 | const threadId = this.readString( | 181 | const threadId = this.readString(this.readObject(threadStart)?.thread, "id"); |
| 192 | this.readObject(threadStart)?.thread, | ||
| 193 | "id", | ||
| 194 | ); | ||
| 195 | if (!threadId) { | 182 | if (!threadId) { |
| 196 | throw new Error("thread/start response did not include a thread id."); | 183 | throw new Error("thread/start response did not include a thread id."); |
| 197 | } | 184 | } |
| ... | @@ -200,30 +187,21 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -200,30 +187,21 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 200 | status: "ready", | 187 | status: "ready", |
| 201 | threadId, | 188 | threadId, |
| 202 | }); | 189 | }); |
| 203 | this.emitLifecycleEvent( | 190 | this.emitLifecycleEvent(context, "session/ready", `Connected to thread ${threadId}`); |
| 204 | context, | ||
| 205 | "session/ready", | ||
| 206 | `Connected to thread ${threadId}`, | ||
| 207 | ); | ||
| 208 | return { ...context.session }; | 191 | return { ...context.session }; |
| 209 | } catch (error) { | 192 | } catch (error) { |
| 210 | const message = | 193 | const message = error instanceof Error ? error.message : "Failed to start Codex session."; |
| 211 | error instanceof Error | ||
| 212 | ? error.message | ||
| 213 | : "Failed to start Codex session."; | ||
| 214 | this.updateSession(context, { | 194 | this.updateSession(context, { |
| 215 | status: "error", | 195 | status: "error", |
| 216 | lastError: message, | 196 | lastError: message, |
| 217 | }); | 197 | }); |
| 218 | this.emitErrorEvent(context, "session/startFailed", message); | 198 | this.emitErrorEvent(context, "session/startFailed", message); |
| 219 | this.stopSession(sessionId); | 199 | this.stopSession(sessionId); |
| 220 | throw new Error(message); | 200 | throw new Error(message, { cause: error }); |
| 221 | } | 201 | } |
| 222 | } | 202 | } |
| 223 | 203 | ||
| 224 | async sendTurn( | 204 | async sendTurn(input: ProviderSendTurnInput): Promise<ProviderTurnStartResult> { |
| 225 | input: ProviderSendTurnInput, | ||
| 226 | ): Promise<ProviderTurnStartResult> { | ||
| 227 | const context = this.requireSession(input.sessionId); | 205 | const context = this.requireSession(input.sessionId); |
| 228 | if (!context.session.threadId) { | 206 | if (!context.session.threadId) { |
| 229 | throw new Error("Session is missing a thread id."); | 207 | throw new Error("Session is missing a thread id."); |
| ... | @@ -252,11 +230,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -252,11 +230,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 252 | turnStartParams.effort = input.effort; | 230 | turnStartParams.effort = input.effort; |
| 253 | } | 231 | } |
| 254 | 232 | ||
| 255 | const response = await this.sendRequest( | 233 | const response = await this.sendRequest(context, "turn/start", turnStartParams); |
| 256 | context, | ||
| 257 | "turn/start", | ||
| 258 | turnStartParams, | ||
| 259 | ); | ||
| 260 | 234 | ||
| 261 | const turn = this.readObject(this.readObject(response), "turn"); | 235 | const turn = this.readObject(this.readObject(response), "turn"); |
| 262 | const turnId = this.readString(turn, "id"); | 236 | const turnId = this.readString(turn, "id"); |
| ... | @@ -498,10 +472,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -498,10 +472,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 498 | }); | 472 | }); |
| 499 | 473 | ||
| 500 | if (notification.method === "thread/started") { | 474 | if (notification.method === "thread/started") { |
| 501 | const threadId = this.readString( | 475 | const threadId = this.readString(this.readObject(notification.params)?.thread, "id"); |
| 502 | this.readObject(notification.params)?.thread, | ||
| 503 | "id", | ||
| 504 | ); | ||
| 505 | if (threadId) { | 476 | if (threadId) { |
| 506 | this.updateSession(context, { threadId }); | 477 | this.updateSession(context, { threadId }); |
| 507 | } | 478 | } |
| ... | @@ -509,10 +480,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -509,10 +480,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 509 | } | 480 | } |
| 510 | 481 | ||
| 511 | if (notification.method === "turn/started") { | 482 | if (notification.method === "turn/started") { |
| 512 | const turnId = this.readString( | 483 | const turnId = this.readString(this.readObject(notification.params)?.turn, "id"); |
| 513 | this.readObject(notification.params)?.turn, | ||
| 514 | "id", | ||
| 515 | ); | ||
| 516 | this.updateSession(context, { | 484 | this.updateSession(context, { |
| 517 | status: "running", | 485 | status: "running", |
| 518 | activeTurnId: turnId, | 486 | activeTurnId: turnId, |
| ... | @@ -523,10 +491,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -523,10 +491,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 523 | if (notification.method === "turn/completed") { | 491 | if (notification.method === "turn/completed") { |
| 524 | const turn = this.readObject(notification.params, "turn"); | 492 | const turn = this.readObject(notification.params, "turn"); |
| 525 | const status = this.readString(turn, "status"); | 493 | const status = this.readString(turn, "status"); |
| 526 | const errorMessage = this.readString( | 494 | const errorMessage = this.readString(this.readObject(turn, "error"), "message"); |
| 527 | this.readObject(turn, "error"), | ||
| 528 | "message", | ||
| 529 | ); | ||
| 530 | this.updateSession(context, { | 495 | this.updateSession(context, { |
| 531 | status: status === "failed" ? "error" : "ready", | 496 | status: status === "failed" ? "error" : "ready", |
| 532 | activeTurnId: undefined, | 497 | activeTurnId: undefined, |
| ... | @@ -536,10 +501,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -536,10 +501,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 536 | } | 501 | } |
| 537 | 502 | ||
| 538 | if (notification.method === "error") { | 503 | if (notification.method === "error") { |
| 539 | const message = this.readString( | 504 | const message = this.readString(this.readObject(notification.params)?.error, "message"); |
| 540 | this.readObject(notification.params)?.error, | ||
| 541 | "message", | ||
| 542 | ); | ||
| 543 | const willRetry = this.readBoolean(notification.params, "willRetry"); | 505 | const willRetry = this.readBoolean(notification.params, "willRetry"); |
| 544 | 506 | ||
| 545 | this.updateSession(context, { | 507 | this.updateSession(context, { |
| ... | @@ -549,10 +511,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -549,10 +511,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 549 | } | 511 | } |
| 550 | } | 512 | } |
| 551 | 513 | ||
| 552 | private handleServerRequest( | 514 | private handleServerRequest(context: CodexSessionContext, request: JsonRpcRequest): void { |
| 553 | context: CodexSessionContext, | ||
| 554 | request: JsonRpcRequest, | ||
| 555 | ): void { | ||
| 556 | const route = this.readRouteFields(request.params); | 515 | const route = this.readRouteFields(request.params); |
| 557 | const requestKind = this.requestKindForMethod(request.method); | 516 | const requestKind = this.requestKindForMethod(request.method); |
| 558 | let requestId: string | undefined; | 517 | let requestId: string | undefined; |
| ... | @@ -609,10 +568,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -609,10 +568,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 609 | }); | 568 | }); |
| 610 | } | 569 | } |
| 611 | 570 | ||
| 612 | private handleResponse( | 571 | private handleResponse(context: CodexSessionContext, response: JsonRpcResponse): void { |
| 613 | context: CodexSessionContext, | ||
| 614 | response: JsonRpcResponse, | ||
| 615 | ): void { | ||
| 616 | const key = String(response.id); | 572 | const key = String(response.id); |
| 617 | const pending = context.pending.get(key); | 573 | const pending = context.pending.get(key); |
| 618 | if (!pending) { | 574 | if (!pending) { |
| ... | @@ -623,11 +579,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -623,11 +579,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 623 | context.pending.delete(key); | 579 | context.pending.delete(key); |
| 624 | 580 | ||
| 625 | if (response.error?.message) { | 581 | if (response.error?.message) { |
| 626 | pending.reject( | 582 | pending.reject(new Error(`${pending.method} failed: ${String(response.error.message)}`)); |
| 627 | new Error( | ||
| 628 | `${pending.method} failed: ${String(response.error.message)}`, | ||
| 629 | ), | ||
| 630 | ); | ||
| 631 | return; | 583 | return; |
| 632 | } | 584 | } |
| 633 | 585 | ||
| ... | @@ -674,11 +626,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -674,11 +626,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 674 | context.child.stdin.write(`${encoded}\n`); | 626 | context.child.stdin.write(`${encoded}\n`); |
| 675 | } | 627 | } |
| 676 | 628 | ||
| 677 | private emitLifecycleEvent( | 629 | private emitLifecycleEvent(context: CodexSessionContext, method: string, message: string): void { |
| 678 | context: CodexSessionContext, | ||
| 679 | method: string, | ||
| 680 | message: string, | ||
| 681 | ): void { | ||
| 682 | this.emitEvent({ | 630 | this.emitEvent({ |
| 683 | id: randomUUID(), | 631 | id: randomUUID(), |
| 684 | kind: "session", | 632 | kind: "session", |
| ... | @@ -690,11 +638,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -690,11 +638,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 690 | }); | 638 | }); |
| 691 | } | 639 | } |
| 692 | 640 | ||
| 693 | private emitErrorEvent( | 641 | private emitErrorEvent(context: CodexSessionContext, method: string, message: string): void { |
| 694 | context: CodexSessionContext, | ||
| 695 | method: string, | ||
| 696 | message: string, | ||
| 697 | ): void { | ||
| 698 | this.emitEvent({ | 642 | this.emitEvent({ |
| 699 | id: randomUUID(), | 643 | id: randomUUID(), |
| 700 | kind: "error", | 644 | kind: "error", |
| ... | @@ -710,10 +654,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -710,10 +654,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 710 | this.emit("event", event); | 654 | this.emit("event", event); |
| 711 | } | 655 | } |
| 712 | 656 | ||
| 713 | private updateSession( | 657 | private updateSession(context: CodexSessionContext, updates: Partial<ProviderSession>): void { |
| 714 | context: CodexSessionContext, | ||
| 715 | updates: Partial<ProviderSession>, | ||
| 716 | ): void { | ||
| 717 | context.session = { | 658 | context.session = { |
| 718 | ...context.session, | 659 | ...context.session, |
| 719 | ...updates, | 660 | ...updates, |
| ... | @@ -762,8 +703,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -762,8 +703,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 762 | } | 703 | } |
| 763 | 704 | ||
| 764 | const candidate = value as Record<string, unknown>; | 705 | const candidate = value as Record<string, unknown>; |
| 765 | const hasId = | 706 | const hasId = typeof candidate.id === "string" || typeof candidate.id === "number"; |
| 766 | typeof candidate.id === "string" || typeof candidate.id === "number"; | ||
| 767 | const hasMethod = typeof candidate.method === "string"; | 707 | const hasMethod = typeof candidate.method === "string"; |
| 768 | return hasId && !hasMethod; | 708 | return hasId && !hasMethod; |
| 769 | } | 709 | } |
| ... | @@ -783,11 +723,9 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -783,11 +723,9 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 783 | this.readString(params, "threadId") ?? | 723 | this.readString(params, "threadId") ?? |
| 784 | this.readString(this.readObject(params, "thread"), "id"); | 724 | this.readString(this.readObject(params, "thread"), "id"); |
| 785 | const turnId = | 725 | const turnId = |
| 786 | this.readString(params, "turnId") ?? | 726 | this.readString(params, "turnId") ?? this.readString(this.readObject(params, "turn"), "id"); |
| 787 | this.readString(this.readObject(params, "turn"), "id"); | ||
| 788 | const itemId = | 727 | const itemId = |
| 789 | this.readString(params, "itemId") ?? | 728 | this.readString(params, "itemId") ?? this.readString(this.readObject(params, "item"), "id"); |
| 790 | this.readString(this.readObject(params, "item"), "id"); | ||
| 791 | 729 | ||
| 792 | if (threadId) { | 730 | if (threadId) { |
| 793 | route.threadId = threadId; | 731 | route.threadId = threadId; |
| ... | @@ -804,10 +742,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -804,10 +742,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 804 | return route; | 742 | return route; |
| 805 | } | 743 | } |
| 806 | 744 | ||
| 807 | private readObject( | 745 | private readObject(value: unknown, key?: string): Record<string, unknown> | undefined { |
| 808 | value: unknown, | ||
| 809 | key?: string, | ||
| 810 | ): Record<string, unknown> | undefined { | ||
| 811 | const target = | 746 | const target = |
| 812 | key === undefined | 747 | key === undefined |
| 813 | ? value | 748 | ? value |
apps/desktop/src/ipcHelpers.test.ts+3-5| ... | @@ -55,11 +55,9 @@ describe("withParsedPayload", () => { | ... | @@ -55,11 +55,9 @@ describe("withParsedPayload", () => { |
| 55 | 55 | ||
| 56 | describe("withParsedArgs", () => { | 56 | describe("withParsedArgs", () => { |
| 57 | it("parses tuple arguments before invoking handler", () => { | 57 | it("parses tuple arguments before invoking handler", () => { |
| 58 | const handler = vi.fn( | 58 | const handler = vi.fn((_event: unknown, sessionId: string, data: string) => { |
| 59 | (_event: unknown, sessionId: string, data: string) => { | 59 | return `${sessionId}:${data}`; |
| 60 | return `${sessionId}:${data}`; | 60 | }); |
| 61 | }, | ||
| 62 | ); | ||
| 63 | const wrapped = withParsedArgs( | 61 | const wrapped = withParsedArgs( |
| 64 | { | 62 | { |
| 65 | parse(args: unknown[]): [string, string] { | 63 | parse(args: unknown[]): [string, string] { |
apps/desktop/src/main.ts+12-26| ... | @@ -105,8 +105,7 @@ function registerIpcHandlers(): void { | ... | @@ -105,8 +105,7 @@ function registerIpcHandlers(): void { |
| 105 | ); | 105 | ); |
| 106 | 106 | ||
| 107 | ipcMain.handle(IPC_CHANNELS.dialogPickFolder, async () => { | 107 | ipcMain.handle(IPC_CHANNELS.dialogPickFolder, async () => { |
| 108 | const owner = | 108 | const owner = BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]; |
| 109 | BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]; | ||
| 110 | const result = owner | 109 | const result = owner |
| 111 | ? await dialog.showOpenDialog(owner, { | 110 | ? await dialog.showOpenDialog(owner, { |
| 112 | properties: ["openDirectory", "createDirectory"], | 111 | properties: ["openDirectory", "createDirectory"], |
| ... | @@ -175,12 +174,9 @@ function registerIpcHandlers(): void { | ... | @@ -175,12 +174,9 @@ function registerIpcHandlers(): void { |
| 175 | // Provider handlers | 174 | // Provider handlers |
| 176 | ipcMain.handle( | 175 | ipcMain.handle( |
| 177 | IPC_CHANNELS.providerSessionStart, | 176 | IPC_CHANNELS.providerSessionStart, |
| 178 | withParsedPayload( | 177 | withParsedPayload(providerSessionStartInputSchema, async (_event, payload) => { |
| 179 | providerSessionStartInputSchema, | 178 | return providerManager.startSession(payload); |
| 180 | async (_event, payload) => { | 179 | }), |
| 181 | return providerManager.startSession(payload); | ||
| 182 | }, | ||
| 183 | ), | ||
| 184 | ); | 180 | ); |
| 185 | 181 | ||
| 186 | ipcMain.handle( | 182 | ipcMain.handle( |
| ... | @@ -192,12 +188,9 @@ function registerIpcHandlers(): void { | ... | @@ -192,12 +188,9 @@ function registerIpcHandlers(): void { |
| 192 | 188 | ||
| 193 | ipcMain.handle( | 189 | ipcMain.handle( |
| 194 | IPC_CHANNELS.providerTurnInterrupt, | 190 | IPC_CHANNELS.providerTurnInterrupt, |
| 195 | withParsedPayload( | 191 | withParsedPayload(providerInterruptTurnInputSchema, async (_event, payload) => { |
| 196 | providerInterruptTurnInputSchema, | 192 | await providerManager.interruptTurn(payload); |
| 197 | async (_event, payload) => { | 193 | }), |
| 198 | await providerManager.interruptTurn(payload); | ||
| 199 | }, | ||
| 200 | ), | ||
| 201 | ); | 194 | ); |
| 202 | 195 | ||
| 203 | ipcMain.handle( | 196 | ipcMain.handle( |
| ... | @@ -212,12 +205,9 @@ function registerIpcHandlers(): void { | ... | @@ -212,12 +205,9 @@ function registerIpcHandlers(): void { |
| 212 | 205 | ||
| 213 | ipcMain.handle( | 206 | ipcMain.handle( |
| 214 | IPC_CHANNELS.providerSessionStop, | 207 | IPC_CHANNELS.providerSessionStop, |
| 215 | withParsedPayload( | 208 | withParsedPayload(providerStopSessionInputSchema, async (_event, payload) => { |
| 216 | providerStopSessionInputSchema, | 209 | providerManager.stopSession(payload); |
| 217 | async (_event, payload) => { | 210 | }), |
| 218 | providerManager.stopSession(payload); | ||
| 219 | }, | ||
| 220 | ), | ||
| 221 | ); | 211 | ); |
| 222 | 212 | ||
| 223 | ipcMain.handle(IPC_CHANNELS.providerSessionList, async () => { | 213 | ipcMain.handle(IPC_CHANNELS.providerSessionList, async () => { |
| ... | @@ -225,18 +215,14 @@ function registerIpcHandlers(): void { | ... | @@ -225,18 +215,14 @@ function registerIpcHandlers(): void { |
| 225 | }); | 215 | }); |
| 226 | } | 216 | } |
| 227 | 217 | ||
| 228 | async function runTerminalCommand( | 218 | async function runTerminalCommand(input: TerminalCommandInput): Promise<TerminalCommandResult> { |
| 229 | input: TerminalCommandInput, | ||
| 230 | ): Promise<TerminalCommandResult> { | ||
| 231 | const shellPath = | 219 | const shellPath = |
| 232 | process.platform === "win32" | 220 | process.platform === "win32" |
| 233 | ? (process.env.ComSpec ?? "cmd.exe") | 221 | ? (process.env.ComSpec ?? "cmd.exe") |
| 234 | : (process.env.SHELL ?? "/bin/sh"); | 222 | : (process.env.SHELL ?? "/bin/sh"); |
| 235 | 223 | ||
| 236 | const args = | 224 | const args = |
| 237 | process.platform === "win32" | 225 | process.platform === "win32" ? ["/d", "/s", "/c", input.command] : ["-lc", input.command]; |
| 238 | ? ["/d", "/s", "/c", input.command] | ||
| 239 | : ["-lc", input.command]; | ||
| 240 | 226 | ||
| 241 | return new Promise((resolve, reject) => { | 227 | return new Promise((resolve, reject) => { |
| 242 | const child = spawn(shellPath, args, { | 228 | const child = spawn(shellPath, args, { |
apps/desktop/src/preload.ts+8-16| ... | @@ -18,14 +18,12 @@ const nativeApi: NativeApi = { | ... | @@ -18,14 +18,12 @@ const nativeApi: NativeApi = { |
| 18 | agent: { | 18 | agent: { |
| 19 | spawn: (config) => ipcRenderer.invoke(IPC_CHANNELS.agentSpawn, config), | 19 | spawn: (config) => ipcRenderer.invoke(IPC_CHANNELS.agentSpawn, config), |
| 20 | kill: (sessionId) => ipcRenderer.invoke(IPC_CHANNELS.agentKill, sessionId), | 20 | kill: (sessionId) => ipcRenderer.invoke(IPC_CHANNELS.agentKill, sessionId), |
| 21 | write: (sessionId, data) => | 21 | write: (sessionId, data) => ipcRenderer.invoke(IPC_CHANNELS.agentWrite, sessionId, data), |
| 22 | ipcRenderer.invoke(IPC_CHANNELS.agentWrite, sessionId, data), | ||
| 23 | onOutput: (callback) => { | 22 | onOutput: (callback) => { |
| 24 | const listener = (_event: Electron.IpcRendererEvent, chunk: unknown) => | 23 | const listener = (_event: Electron.IpcRendererEvent, chunk: unknown) => |
| 25 | callback(chunk as Parameters<typeof callback>[0]); | 24 | callback(chunk as Parameters<typeof callback>[0]); |
| 26 | ipcRenderer.on(IPC_CHANNELS.agentOutput, listener); | 25 | ipcRenderer.on(IPC_CHANNELS.agentOutput, listener); |
| 27 | return () => | 26 | return () => ipcRenderer.removeListener(IPC_CHANNELS.agentOutput, listener); |
| 28 | ipcRenderer.removeListener(IPC_CHANNELS.agentOutput, listener); | ||
| 29 | }, | 27 | }, |
| 30 | onExit: (callback) => { | 28 | onExit: (callback) => { |
| 31 | const listener = (_event: Electron.IpcRendererEvent, exit: unknown) => | 29 | const listener = (_event: Electron.IpcRendererEvent, exit: unknown) => |
| ... | @@ -35,23 +33,17 @@ const nativeApi: NativeApi = { | ... | @@ -35,23 +33,17 @@ const nativeApi: NativeApi = { |
| 35 | }, | 33 | }, |
| 36 | }, | 34 | }, |
| 37 | providers: { | 35 | providers: { |
| 38 | startSession: (input) => | 36 | startSession: (input) => ipcRenderer.invoke(IPC_CHANNELS.providerSessionStart, input), |
| 39 | ipcRenderer.invoke(IPC_CHANNELS.providerSessionStart, input), | 37 | sendTurn: (input) => ipcRenderer.invoke(IPC_CHANNELS.providerTurnStart, input), |
| 40 | sendTurn: (input) => | 38 | interruptTurn: (input) => ipcRenderer.invoke(IPC_CHANNELS.providerTurnInterrupt, input), |
| 41 | ipcRenderer.invoke(IPC_CHANNELS.providerTurnStart, input), | 39 | respondToRequest: (input) => ipcRenderer.invoke(IPC_CHANNELS.providerRequestRespond, input), |
| 42 | interruptTurn: (input) => | 40 | stopSession: (input) => ipcRenderer.invoke(IPC_CHANNELS.providerSessionStop, input), |
| 43 | ipcRenderer.invoke(IPC_CHANNELS.providerTurnInterrupt, input), | ||
| 44 | respondToRequest: (input) => | ||
| 45 | ipcRenderer.invoke(IPC_CHANNELS.providerRequestRespond, input), | ||
| 46 | stopSession: (input) => | ||
| 47 | ipcRenderer.invoke(IPC_CHANNELS.providerSessionStop, input), | ||
| 48 | listSessions: () => ipcRenderer.invoke(IPC_CHANNELS.providerSessionList), | 41 | listSessions: () => ipcRenderer.invoke(IPC_CHANNELS.providerSessionList), |
| 49 | onEvent: (callback) => { | 42 | onEvent: (callback) => { |
| 50 | const listener = (_event: Electron.IpcRendererEvent, payload: unknown) => | 43 | const listener = (_event: Electron.IpcRendererEvent, payload: unknown) => |
| 51 | callback(payload as Parameters<typeof callback>[0]); | 44 | callback(payload as Parameters<typeof callback>[0]); |
| 52 | ipcRenderer.on(IPC_CHANNELS.providerEvent, listener); | 45 | ipcRenderer.on(IPC_CHANNELS.providerEvent, listener); |
| 53 | return () => | 46 | return () => ipcRenderer.removeListener(IPC_CHANNELS.providerEvent, listener); |
| 54 | ipcRenderer.removeListener(IPC_CHANNELS.providerEvent, listener); | ||
| 55 | }, | 47 | }, |
| 56 | }, | 48 | }, |
| 57 | shell: { | 49 | shell: { |
apps/desktop/src/processManager.ts+1-4| ... | @@ -68,7 +68,6 @@ export class ProcessManager extends EventEmitter<ProcessManagerEvents> { | ... | @@ -68,7 +68,6 @@ export class ProcessManager extends EventEmitter<ProcessManagerEvents> { |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | private spawnPty(sessionId: string, config: AgentConfig): string { | 70 | private spawnPty(sessionId: string, config: AgentConfig): string { |
| 71 | // eslint-disable-next-line @typescript-eslint/no-var-requires | ||
| 72 | const pty = require("node-pty") as typeof import("node-pty"); | 71 | const pty = require("node-pty") as typeof import("node-pty"); |
| 73 | 72 | ||
| 74 | const ptyProcess = pty.spawn(config.command, config.args, { | 73 | const ptyProcess = pty.spawn(config.command, config.args, { |
| ... | @@ -76,9 +75,7 @@ export class ProcessManager extends EventEmitter<ProcessManagerEvents> { | ... | @@ -76,9 +75,7 @@ export class ProcessManager extends EventEmitter<ProcessManagerEvents> { |
| 76 | cols: 120, | 75 | cols: 120, |
| 77 | rows: 30, | 76 | rows: 30, |
| 78 | cwd: config.cwd ?? process.cwd(), | 77 | cwd: config.cwd ?? process.cwd(), |
| 79 | env: (config.env | 78 | env: (config.env ? { ...process.env, ...config.env } : process.env) as Record<string, string>, |
| 80 | ? { ...process.env, ...config.env } | ||
| 81 | : process.env) as Record<string, string>, | ||
| 82 | }); | 79 | }); |
| 83 | 80 | ||
| 84 | this.ptySessions.set(sessionId, ptyProcess); | 81 | this.ptySessions.set(sessionId, ptyProcess); |
apps/desktop/src/todoStore.ts+1-5| ... | @@ -82,11 +82,7 @@ export class TodoStore { | ... | @@ -82,11 +82,7 @@ export class TodoStore { |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | private async persist(): Promise<void> { | 84 | private async persist(): Promise<void> { |
| 85 | await fs.writeFile( | 85 | await fs.writeFile(this.filePath, JSON.stringify(this.todos, null, 2), "utf8"); |
| 86 | this.filePath, | ||
| 87 | JSON.stringify(this.todos, null, 2), | ||
| 88 | "utf8", | ||
| 89 | ); | ||
| 90 | } | 86 | } |
| 91 | 87 | ||
| 92 | private async runExclusive<T>(operation: () => Promise<T>): Promise<T> { | 88 | private async runExclusive<T>(operation: () => Promise<T>): Promise<T> { |
apps/desktop/tsconfig.json+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | "compilerOptions": { | 3 | "compilerOptions": { |
| 4 | "composite": true, | 4 | "composite": true, |
| 5 | "types": ["node", "electron"], | 5 | "types": ["node", "electron"], |
| 6 | "lib": ["ES2022", "DOM"] | 6 | "lib": ["ES2023", "DOM"] |
| 7 | }, | 7 | }, |
| 8 | "include": ["src", "tsup.config.ts"] | 8 | "include": ["src", "tsup.config.ts"] |
| 9 | } | 9 | } |
apps/renderer/package.json+1-2| ... | @@ -8,15 +8,14 @@ | ... | @@ -8,15 +8,14 @@ |
| 8 | "build": "vite build", | 8 | "build": "vite build", |
| 9 | "preview": "vite preview", | 9 | "preview": "vite preview", |
| 10 | "typecheck": "tsc --noEmit", | 10 | "typecheck": "tsc --noEmit", |
| 11 | "lint": "biome check src/", | ||
| 12 | "test": "vitest run --passWithNoTests" | 11 | "test": "vitest run --passWithNoTests" |
| 13 | }, | 12 | }, |
| 14 | "dependencies": { | 13 | "dependencies": { |
| 15 | "@acme/contracts": "workspace:*", | 14 | "@acme/contracts": "workspace:*", |
| 16 | "highlight.js": "^11.11.1", | 15 | "highlight.js": "^11.11.1", |
| 17 | "react-markdown": "^10.1.0", | ||
| 18 | "react": "^19.0.0", | 16 | "react": "^19.0.0", |
| 19 | "react-dom": "^19.0.0", | 17 | "react-dom": "^19.0.0", |
| 18 | "react-markdown": "^10.1.0", | ||
| 20 | "rehype-highlight": "^7.0.2", | 19 | "rehype-highlight": "^7.0.2", |
| 21 | "remark-gfm": "^4.0.1", | 20 | "remark-gfm": "^4.0.1", |
| 22 | "zod": "^3.24.1" | 21 | "zod": "^3.24.1" |
apps/renderer/src/components/ChatMarkdown.tsx+1-3| ... | @@ -18,9 +18,7 @@ export default function ChatMarkdown({ text }: ChatMarkdownProps) { | ... | @@ -18,9 +18,7 @@ export default function ChatMarkdown({ text }: ChatMarkdownProps) { |
| 18 | <div className="chat-markdown text-sm leading-relaxed text-[#d0d0d0]"> | 18 | <div className="chat-markdown text-sm leading-relaxed text-[#d0d0d0]"> |
| 19 | <ReactMarkdown | 19 | <ReactMarkdown |
| 20 | remarkPlugins={[remarkGfm]} | 20 | remarkPlugins={[remarkGfm]} |
| 21 | rehypePlugins={[ | 21 | rehypePlugins={[[rehypeHighlight, { detect: true, ignoreMissing: true }]]} |
| 22 | [rehypeHighlight, { detect: true, ignoreMissing: true }], | ||
| 23 | ]} | ||
| 24 | components={markdownComponents} | 22 | components={markdownComponents} |
| 25 | > | 23 | > |
| 26 | {text} | 24 | {text} |
apps/renderer/src/components/ChatView.tsx+20-50| ... | @@ -144,9 +144,7 @@ export default function ChatView() { | ... | @@ -144,9 +144,7 @@ export default function ChatView() { |
| 144 | const editorMenuRef = useRef<HTMLDivElement>(null); | 144 | const editorMenuRef = useRef<HTMLDivElement>(null); |
| 145 | 145 | ||
| 146 | const activeThread = state.threads.find((t) => t.id === state.activeThreadId); | 146 | const activeThread = state.threads.find((t) => t.id === state.activeThreadId); |
| 147 | const activeProject = state.projects.find( | 147 | const activeProject = state.projects.find((p) => p.id === activeThread?.projectId); |
| 148 | (p) => p.id === activeThread?.projectId, | ||
| 149 | ); | ||
| 150 | const selectedModel = resolveModelSlug( | 148 | const selectedModel = resolveModelSlug( |
| 151 | activeThread?.model ?? activeProject?.model ?? DEFAULT_MODEL, | 149 | activeThread?.model ?? activeProject?.model ?? DEFAULT_MODEL, |
| 152 | ); | 150 | ); |
| ... | @@ -164,7 +162,7 @@ export default function ChatView() { | ... | @@ -164,7 +162,7 @@ export default function ChatView() { |
| 164 | ); | 162 | ); |
| 165 | const assistantCompletionByItemId = useMemo(() => { | 163 | const assistantCompletionByItemId = useMemo(() => { |
| 166 | const map = new Map<string, string>(); | 164 | const map = new Map<string, string>(); |
| 167 | const ordered = [...(activeThread?.events ?? [])].reverse(); | 165 | const ordered = [...(activeThread?.events ?? [])].toReversed(); |
| 168 | for (const event of ordered) { | 166 | for (const event of ordered) { |
| 169 | if (event.method !== "item/completed") continue; | 167 | if (event.method !== "item/completed") continue; |
| 170 | if (!event.itemId) continue; | 168 | if (!event.itemId) continue; |
| ... | @@ -264,18 +262,15 @@ export default function ChatView() { | ... | @@ -264,18 +262,15 @@ export default function ChatView() { |
| 264 | // Auto-scroll on new messages | 262 | // Auto-scroll on new messages |
| 265 | const messageCount = activeThread?.messages.length ?? 0; | 263 | const messageCount = activeThread?.messages.length ?? 0; |
| 266 | const workLogCount = workLogEntries.length; | 264 | const workLogCount = workLogEntries.length; |
| 267 | // biome-ignore lint/correctness/useExhaustiveDependencies: trigger on message count change | ||
| 268 | useEffect(() => { | 265 | useEffect(() => { |
| 269 | messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); | 266 | messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); |
| 270 | }, [messageCount]); | 267 | }, [messageCount]); |
| 271 | // biome-ignore lint/correctness/useExhaustiveDependencies: auto-scroll while active work-log events stream in | ||
| 272 | useEffect(() => { | 268 | useEffect(() => { |
| 273 | if (phase !== "running") return; | 269 | if (phase !== "running") return; |
| 274 | messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); | 270 | messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); |
| 275 | }, [phase, workLogCount]); | 271 | }, [phase, workLogCount]); |
| 276 | 272 | ||
| 277 | // Auto-resize textarea | 273 | // Auto-resize textarea |
| 278 | // biome-ignore lint/correctness/useExhaustiveDependencies: trigger on prompt change | ||
| 279 | useEffect(() => { | 274 | useEffect(() => { |
| 280 | const ta = textareaRef.current; | 275 | const ta = textareaRef.current; |
| 281 | if (!ta) return; | 276 | if (!ta) return; |
| ... | @@ -298,10 +293,7 @@ export default function ChatView() { | ... | @@ -298,10 +293,7 @@ export default function ChatView() { |
| 298 | 293 | ||
| 299 | const handleClickOutside = (event: MouseEvent) => { | 294 | const handleClickOutside = (event: MouseEvent) => { |
| 300 | if (!modelMenuRef.current) return; | 295 | if (!modelMenuRef.current) return; |
| 301 | if ( | 296 | if (event.target instanceof Node && !modelMenuRef.current.contains(event.target)) { |
| 302 | event.target instanceof Node && | ||
| 303 | !modelMenuRef.current.contains(event.target) | ||
| 304 | ) { | ||
| 305 | setIsModelMenuOpen(false); | 297 | setIsModelMenuOpen(false); |
| 306 | } | 298 | } |
| 307 | }; | 299 | }; |
| ... | @@ -394,8 +386,7 @@ export default function ChatView() { | ... | @@ -394,8 +386,7 @@ export default function ChatView() { |
| 394 | 386 | ||
| 395 | // Auto-title from first message | 387 | // Auto-title from first message |
| 396 | if (activeThread.messages.length === 0) { | 388 | if (activeThread.messages.length === 0) { |
| 397 | const title = | 389 | const title = trimmed.length > 50 ? `${trimmed.slice(0, 50)}...` : trimmed; |
| 398 | trimmed.length > 50 ? `${trimmed.slice(0, 50)}...` : trimmed; | ||
| 399 | dispatch({ | 390 | dispatch({ |
| 400 | type: "SET_THREAD_TITLE", | 391 | type: "SET_THREAD_TITLE", |
| 401 | threadId: activeThread.id, | 392 | threadId: activeThread.id, |
| ... | @@ -501,9 +492,7 @@ export default function ChatView() { | ... | @@ -501,9 +492,7 @@ export default function ChatView() { |
| 501 | <div className="drag-region h-[52px] shrink-0" /> | 492 | <div className="drag-region h-[52px] shrink-0" /> |
| 502 | <div className="flex flex-1 items-center justify-center"> | 493 | <div className="flex flex-1 items-center justify-center"> |
| 503 | <div className="text-center"> | 494 | <div className="text-center"> |
| 504 | <p className="text-sm"> | 495 | <p className="text-sm">Select a thread or create a new one to get started.</p> |
| 505 | Select a thread or create a new one to get started. | ||
| 506 | </p> | ||
| 507 | </div> | 496 | </div> |
| 508 | </div> | 497 | </div> |
| 509 | </div> | 498 | </div> |
| ... | @@ -513,7 +502,7 @@ export default function ChatView() { | ... | @@ -513,7 +502,7 @@ export default function ChatView() { |
| 513 | return ( | 502 | return ( |
| 514 | <div className="flex flex-1 flex-col bg-[#0c0c0c]"> | 503 | <div className="flex flex-1 flex-col bg-[#0c0c0c]"> |
| 515 | {/* Top bar */} | 504 | {/* Top bar */} |
| 516 | <header className="drag-region flex items-center justify-between border-b border-white/[0.08] px-5 pt-[28px] pb-3"> | 505 | <header className="drag-region flex items-center justify-between border-b border-white/8 px-5 pt-[28px] pb-3"> |
| 517 | <div className="flex items-center gap-3"> | 506 | <div className="flex items-center gap-3"> |
| 518 | <h2 className="text-sm font-medium text-[#e0e0e0]"> | 507 | <h2 className="text-sm font-medium text-[#e0e0e0]"> |
| 519 | {activeThread.title} | 508 | {activeThread.title} |
| ... | @@ -654,9 +643,7 @@ export default function ChatView() { | ... | @@ -654,9 +643,7 @@ export default function ChatView() { |
| 654 | <div className="flex-1 overflow-y-auto px-5 py-4"> | 643 | <div className="flex-1 overflow-y-auto px-5 py-4"> |
| 655 | {activeThread.messages.length === 0 && !isWorking ? ( | 644 | {activeThread.messages.length === 0 && !isWorking ? ( |
| 656 | <div className="flex h-full items-center justify-center"> | 645 | <div className="flex h-full items-center justify-center"> |
| 657 | <p className="text-sm text-[#a0a0a0]/30"> | 646 | <p className="text-sm text-[#a0a0a0]/30">Send a message to start the conversation.</p> |
| 658 | Send a message to start the conversation. | ||
| 659 | </p> | ||
| 660 | </div> | 647 | </div> |
| 661 | ) : ( | 648 | ) : ( |
| 662 | <div className="mx-auto max-w-3xl space-y-4"> | 649 | <div className="mx-auto max-w-3xl space-y-4"> |
| ... | @@ -770,7 +757,7 @@ export default function ChatView() { | ... | @@ -770,7 +757,7 @@ export default function ChatView() { |
| 770 | {/* Input bar */} | 757 | {/* Input bar */} |
| 771 | <div className="px-5 pb-4 pt-2"> | 758 | <div className="px-5 pb-4 pt-2"> |
| 772 | <form onSubmit={onSend} className="mx-auto max-w-3xl"> | 759 | <form onSubmit={onSend} className="mx-auto max-w-3xl"> |
| 773 | <div className="group rounded-[20px] border border-white/[0.08] bg-[#141416] transition-colors duration-200 focus-within:border-white/[0.16]"> | 760 | <div className="group rounded-[20px] border border-white/8 bg-[#141416] transition-colors duration-200 focus-within:border-white/16"> |
| 774 | {/* Textarea area */} | 761 | {/* Textarea area */} |
| 775 | <div className="px-4 pt-4 pb-2"> | 762 | <div className="px-4 pt-4 pb-2"> |
| 776 | <textarea | 763 | <textarea |
| ... | @@ -781,9 +768,7 @@ export default function ChatView() { | ... | @@ -781,9 +768,7 @@ export default function ChatView() { |
| 781 | onChange={(e) => setPrompt(e.target.value)} | 768 | onChange={(e) => setPrompt(e.target.value)} |
| 782 | onKeyDown={onKeyDown} | 769 | onKeyDown={onKeyDown} |
| 783 | placeholder={ | 770 | placeholder={ |
| 784 | phase === "disconnected" | 771 | phase === "disconnected" ? "Ask for follow-up changes" : "Ask anything..." |
| 785 | ? "Ask for follow-up changes" | ||
| 786 | : "Ask anything..." | ||
| 787 | } | 772 | } |
| 788 | disabled={isSending || isConnecting} | 773 | disabled={isSending || isConnecting} |
| 789 | /> | 774 | /> |
| ... | @@ -796,12 +781,10 @@ export default function ChatView() { | ... | @@ -796,12 +781,10 @@ export default function ChatView() { |
| 796 | <div className="relative" ref={modelMenuRef}> | 781 | <div className="relative" ref={modelMenuRef}> |
| 797 | <button | 782 | <button |
| 798 | type="button" | 783 | type="button" |
| 799 | className="inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-[13px] text-[#a0a0a0]/70 transition-colors duration-150 hover:bg-white/[0.06] hover:text-[#d0d0d0]" | 784 | className="inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-[13px] text-[#a0a0a0]/70 transition-colors duration-150 hover:bg-white/6 hover:text-[#d0d0d0]" |
| 800 | onClick={() => setIsModelMenuOpen((open) => !open)} | 785 | onClick={() => setIsModelMenuOpen((open) => !open)} |
| 801 | > | 786 | > |
| 802 | <span className="max-w-[180px] truncate"> | 787 | <span className="max-w-[180px] truncate">{selectedModel}</span> |
| 803 | {selectedModel} | ||
| 804 | </span> | ||
| 805 | <svg | 788 | <svg |
| 806 | width="10" | 789 | width="10" |
| 807 | height="10" | 790 | height="10" |
| ... | @@ -820,10 +803,8 @@ export default function ChatView() { | ... | @@ -820,10 +803,8 @@ export default function ChatView() { |
| 820 | </svg> | 803 | </svg> |
| 821 | </button> | 804 | </button> |
| 822 | {isModelMenuOpen && ( | 805 | {isModelMenuOpen && ( |
| 823 | <div className="absolute bottom-full left-0 z-20 mb-2 w-[320px] rounded-2xl border border-white/[0.1] bg-[#1b1b1d]/95 p-2 shadow-[0_16px_40px_rgba(0,0,0,0.55)] backdrop-blur"> | 806 | <div className="absolute bottom-full left-0 z-20 mb-2 w-[320px] rounded-2xl border border-white/10 bg-[#1b1b1d]/95 p-2 shadow-[0_16px_40px_rgba(0,0,0,0.55)] backdrop-blur"> |
| 824 | <p className="px-2 py-1 text-[11px] text-[#a0a0a0]/70"> | 807 | <p className="px-2 py-1 text-[11px] text-[#a0a0a0]/70">Select model</p> |
| 825 | Select model | ||
| 826 | </p> | ||
| 827 | <div className="max-h-72 overflow-y-auto"> | 808 | <div className="max-h-72 overflow-y-auto"> |
| 828 | {modelOptions.map((model) => { | 809 | {modelOptions.map((model) => { |
| 829 | const isSelected = model === selectedModel; | 810 | const isSelected = model === selectedModel; |
| ... | @@ -833,8 +814,8 @@ export default function ChatView() { | ... | @@ -833,8 +814,8 @@ export default function ChatView() { |
| 833 | type="button" | 814 | type="button" |
| 834 | className={`mb-0.5 flex w-full items-center justify-between gap-2 rounded-xl px-2 py-2 text-left font-mono text-sm transition-colors duration-150 ${ | 815 | className={`mb-0.5 flex w-full items-center justify-between gap-2 rounded-xl px-2 py-2 text-left font-mono text-sm transition-colors duration-150 ${ |
| 835 | isSelected | 816 | isSelected |
| 836 | ? "bg-white/[0.08] text-white" | 817 | ? "bg-white/8 text-white" |
| 837 | : "text-[#d4d4d4] hover:bg-white/[0.05]" | 818 | : "text-[#d4d4d4] hover:bg-white/5" |
| 838 | }`} | 819 | }`} |
| 839 | onClick={() => onModelSelect(model)} | 820 | onClick={() => onModelSelect(model)} |
| 840 | > | 821 | > |
| ... | @@ -855,17 +836,14 @@ export default function ChatView() { | ... | @@ -855,17 +836,14 @@ export default function ChatView() { |
| 855 | </div> | 836 | </div> |
| 856 | 837 | ||
| 857 | {/* Divider */} | 838 | {/* Divider */} |
| 858 | <div className="mx-0.5 h-4 w-px bg-white/[0.08]" /> | 839 | <div className="mx-0.5 h-4 w-px bg-white/8" /> |
| 859 | 840 | ||
| 860 | {/* Reasoning effort */} | 841 | {/* Reasoning effort */} |
| 861 | <label | 842 | <label |
| 862 | className="inline-flex cursor-pointer items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-[13px] text-[#a0a0a0]/70 transition-colors duration-150 hover:bg-white/[0.06] hover:text-[#d0d0d0]" | 843 | className="inline-flex cursor-pointer items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-[13px] text-[#a0a0a0]/70 transition-colors duration-150 hover:bg-white/6 hover:text-[#d0d0d0]" |
| 863 | htmlFor="reasoning-effort" | 844 | htmlFor="reasoning-effort" |
| 864 | > | 845 | > |
| 865 | <span> | 846 | <span>{selectedEffort.charAt(0).toUpperCase() + selectedEffort.slice(1)}</span> |
| 866 | {selectedEffort.charAt(0).toUpperCase() + | ||
| 867 | selectedEffort.slice(1)} | ||
| 868 | </span> | ||
| 869 | <select | 847 | <select |
| 870 | id="reasoning-effort" | 848 | id="reasoning-effort" |
| 871 | className="absolute opacity-0 w-0 h-0" | 849 | className="absolute opacity-0 w-0 h-0" |
| ... | @@ -873,11 +851,7 @@ export default function ChatView() { | ... | @@ -873,11 +851,7 @@ export default function ChatView() { |
| 873 | onChange={(event) => setSelectedEffort(event.target.value)} | 851 | onChange={(event) => setSelectedEffort(event.target.value)} |
| 874 | > | 852 | > |
| 875 | {REASONING_OPTIONS.map((effort) => ( | 853 | {REASONING_OPTIONS.map((effort) => ( |
| 876 | <option | 854 | <option key={effort} value={effort} className="bg-[#1b1b1d]"> |
| 877 | key={effort} | ||
| 878 | value={effort} | ||
| 879 | className="bg-[#1b1b1d]" | ||
| 880 | > | ||
| 881 | {effort} | 855 | {effort} |
| 882 | {effort === DEFAULT_REASONING ? " (default)" : ""} | 856 | {effort === DEFAULT_REASONING ? " (default)" : ""} |
| 883 | </option> | 857 | </option> |
| ... | @@ -1004,11 +978,7 @@ export default function ChatView() { | ... | @@ -1004,11 +978,7 @@ export default function ChatView() { |
| 1004 | className="flex h-8 w-8 items-center justify-center rounded-full bg-white/90 text-[#0c0c0c] transition-all duration-150 hover:bg-white hover:scale-105 disabled:opacity-30 disabled:hover:scale-100" | 978 | className="flex h-8 w-8 items-center justify-center rounded-full bg-white/90 text-[#0c0c0c] transition-all duration-150 hover:bg-white hover:scale-105 disabled:opacity-30 disabled:hover:scale-100" |
| 1005 | disabled={isSending || isConnecting || !prompt.trim()} | 979 | disabled={isSending || isConnecting || !prompt.trim()} |
| 1006 | aria-label={ | 980 | aria-label={ |
| 1007 | isConnecting | 981 | isConnecting ? "Connecting" : isSending ? "Sending" : "Send message" |
| 1008 | ? "Connecting" | ||
| 1009 | : isSending | ||
| 1010 | ? "Sending" | ||
| 1011 | : "Send message" | ||
| 1012 | } | 982 | } |
| 1013 | > | 983 | > |
| 1014 | {isConnecting || isSending ? ( | 984 | {isConnecting || isSending ? ( |
apps/renderer/src/components/DiffPanel.tsx+4-12| ... | @@ -7,9 +7,7 @@ export default function DiffPanel() { | ... | @@ -7,9 +7,7 @@ export default function DiffPanel() { |
| 7 | <aside className="flex h-full w-[400px] shrink-0 flex-col border-l border-white/[0.08] bg-[#141414]"> | 7 | <aside className="flex h-full w-[400px] shrink-0 flex-col border-l border-white/[0.08] bg-[#141414]"> |
| 8 | {/* Header */} | 8 | {/* Header */} |
| 9 | <div className="drag-region flex items-center justify-between border-b border-white/[0.08] px-4 pt-[28px] pb-3"> | 9 | <div className="drag-region flex items-center justify-between border-b border-white/[0.08] px-4 pt-[28px] pb-3"> |
| 10 | <h3 className="text-xs font-medium text-[#e0e0e0]"> | 10 | <h3 className="text-xs font-medium text-[#e0e0e0]">Uncommitted changes</h3> |
| 11 | Uncommitted changes | ||
| 12 | </h3> | ||
| 13 | <button | 11 | <button |
| 14 | type="button" | 12 | type="button" |
| 15 | className="text-[#a0a0a0]/40 transition-colors duration-150 hover:text-[#a0a0a0]/70" | 13 | className="text-[#a0a0a0]/40 transition-colors duration-150 hover:text-[#a0a0a0]/70" |
| ... | @@ -27,15 +25,11 @@ export default function DiffPanel() { | ... | @@ -27,15 +25,11 @@ export default function DiffPanel() { |
| 27 | <span className="rounded bg-emerald-500/20 px-1.5 py-0.5 text-[9px] font-medium text-emerald-400"> | 25 | <span className="rounded bg-emerald-500/20 px-1.5 py-0.5 text-[9px] font-medium text-emerald-400"> |
| 28 | M | 26 | M |
| 29 | </span> | 27 | </span> |
| 30 | <span className="font-mono text-xs text-[#a0a0a0]/60"> | 28 | <span className="font-mono text-xs text-[#a0a0a0]/60">src/App.tsx</span> |
| 31 | src/App.tsx | ||
| 32 | </span> | ||
| 33 | </div> | 29 | </div> |
| 34 | <div className="space-y-0.5 font-mono text-[11px]"> | 30 | <div className="space-y-0.5 font-mono text-[11px]"> |
| 35 | <div className="text-red-400/60">- const oldValue = "hello";</div> | 31 | <div className="text-red-400/60">- const oldValue = "hello";</div> |
| 36 | <div className="text-emerald-400/60"> | 32 | <div className="text-emerald-400/60">+ const newValue = "world";</div> |
| 37 | + const newValue = "world"; | ||
| 38 | </div> | ||
| 39 | </div> | 33 | </div> |
| 40 | </div> | 34 | </div> |
| 41 | 35 | ||
| ... | @@ -44,9 +38,7 @@ export default function DiffPanel() { | ... | @@ -44,9 +38,7 @@ export default function DiffPanel() { |
| 44 | <span className="rounded bg-blue-500/20 px-1.5 py-0.5 text-[9px] font-medium text-blue-400"> | 38 | <span className="rounded bg-blue-500/20 px-1.5 py-0.5 text-[9px] font-medium text-blue-400"> |
| 45 | A | 39 | A |
| 46 | </span> | 40 | </span> |
| 47 | <span className="font-mono text-xs text-[#a0a0a0]/60"> | 41 | <span className="font-mono text-xs text-[#a0a0a0]/60">src/utils.ts</span> |
| 48 | src/utils.ts | ||
| 49 | </span> | ||
| 50 | </div> | 42 | </div> |
| 51 | </div> | 43 | </div> |
| 52 | </div> | 44 | </div> |
apps/renderer/src/components/Sidebar.tsx+14-22| ... | @@ -120,7 +120,7 @@ export default function Sidebar() { | ... | @@ -120,7 +120,7 @@ export default function Sidebar() { |
| 120 | }, [handleNewThread, state.activeThreadId, state.projects, state.threads]); | 120 | }, [handleNewThread, state.activeThreadId, state.projects, state.threads]); |
| 121 | 121 | ||
| 122 | return ( | 122 | return ( |
| 123 | <aside className="sidebar flex h-full w-[260px] shrink-0 flex-col border-r border-white/[0.08] bg-[#141414]"> | 123 | <aside className="sidebar flex h-full w-[260px] shrink-0 flex-col border-r border-white/8 bg-[#141414]"> |
| 124 | {/* Drag region / traffic light space */} | 124 | {/* Drag region / traffic light space */} |
| 125 | <div className="drag-region h-[52px] shrink-0" /> | 125 | <div className="drag-region h-[52px] shrink-0" /> |
| 126 | {/* Branding */} | 126 | {/* Branding */} |
| ... | @@ -128,16 +128,14 @@ export default function Sidebar() { | ... | @@ -128,16 +128,14 @@ export default function Sidebar() { |
| 128 | <div className="flex h-7 w-7 items-center justify-center rounded-lg bg-white text-xs font-bold text-[#0c0c0c]"> | 128 | <div className="flex h-7 w-7 items-center justify-center rounded-lg bg-white text-xs font-bold text-[#0c0c0c]"> |
| 129 | CT | 129 | CT |
| 130 | </div> | 130 | </div> |
| 131 | <span className="text-sm font-semibold tracking-tight text-[#e0e0e0]"> | 131 | <span className="text-sm font-semibold tracking-tight text-[#e0e0e0]">CodeThing</span> |
| 132 | CodeThing | ||
| 133 | </span> | ||
| 134 | </div> | 132 | </div> |
| 135 | 133 | ||
| 136 | {/* New thread (global) */} | 134 | {/* New thread (global) */} |
| 137 | <div className="px-3 pb-3"> | 135 | <div className="px-3 pb-3"> |
| 138 | <button | 136 | <button |
| 139 | type="button" | 137 | type="button" |
| 140 | className="flex w-full items-center gap-2 rounded-lg border border-white/[0.08] bg-white/[0.04] px-3 py-2 text-xs text-[#a0a0a0]/80 transition-colors duration-150 hover:bg-white/[0.07]" | 138 | className="flex w-full items-center gap-2 rounded-lg border border-white/8 bg-white/4 px-3 py-2 text-xs text-[#a0a0a0]/80 transition-colors duration-150 hover:bg-white/[0.07]" |
| 141 | onClick={() => { | 139 | onClick={() => { |
| 142 | if (state.projects.length === 0) { | 140 | if (state.projects.length === 0) { |
| 143 | setAddingProject(true); | 141 | setAddingProject(true); |
| ... | @@ -169,10 +167,8 @@ export default function Sidebar() { | ... | @@ -169,10 +167,8 @@ export default function Sidebar() { |
| 169 | {/* Project header */} | 167 | {/* Project header */} |
| 170 | <button | 168 | <button |
| 171 | type="button" | 169 | type="button" |
| 172 | className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors duration-150 hover:bg-white/[0.05]" | 170 | className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors duration-150 hover:bg-white/5" |
| 173 | onClick={() => | 171 | onClick={() => dispatch({ type: "TOGGLE_PROJECT", projectId: project.id })} |
| 174 | dispatch({ type: "TOGGLE_PROJECT", projectId: project.id }) | ||
| 175 | } | ||
| 176 | > | 172 | > |
| 177 | <span className="text-[10px] text-[#a0a0a0]/50"> | 173 | <span className="text-[10px] text-[#a0a0a0]/50"> |
| 178 | {project.expanded ? "â–¼" : "â–¶"} | 174 | {project.expanded ? "â–¼" : "â–¶"} |
| ... | @@ -180,14 +176,12 @@ export default function Sidebar() { | ... | @@ -180,14 +176,12 @@ export default function Sidebar() { |
| 180 | <span className="flex-1 truncate text-xs font-medium text-[#e0e0e0]/90"> | 176 | <span className="flex-1 truncate text-xs font-medium text-[#e0e0e0]/90"> |
| 181 | {project.name} | 177 | {project.name} |
| 182 | </span> | 178 | </span> |
| 183 | <span className="text-[10px] text-[#a0a0a0]/40"> | 179 | <span className="text-[10px] text-[#a0a0a0]/40">{threads.length}</span> |
| 184 | {threads.length} | ||
| 185 | </span> | ||
| 186 | </button> | 180 | </button> |
| 187 | 181 | ||
| 188 | {/* Threads */} | 182 | {/* Threads */} |
| 189 | {project.expanded && ( | 183 | {project.expanded && ( |
| 190 | <div className="ml-2 border-l border-white/[0.06] pl-2"> | 184 | <div className="ml-2 border-l border-white/6 pl-2"> |
| 191 | {threads.map((thread) => { | 185 | {threads.map((thread) => { |
| 192 | const isActive = state.activeThreadId === thread.id; | 186 | const isActive = state.activeThreadId === thread.id; |
| 193 | const threadStatus = threadStatusLabel( | 187 | const threadStatus = threadStatusLabel( |
| ... | @@ -198,9 +192,7 @@ export default function Sidebar() { | ... | @@ -198,9 +192,7 @@ export default function Sidebar() { |
| 198 | key={thread.id} | 192 | key={thread.id} |
| 199 | type="button" | 193 | type="button" |
| 200 | className={`flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors duration-150 ${ | 194 | className={`flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors duration-150 ${ |
| 201 | isActive | 195 | isActive ? "bg-white/10 text-white" : "text-[#a0a0a0]/70 hover:bg-white/4" |
| 202 | ? "bg-white/10 text-white" | ||
| 203 | : "text-[#a0a0a0]/70 hover:bg-white/[0.04]" | ||
| 204 | }`} | 196 | }`} |
| 205 | onClick={() => | 197 | onClick={() => |
| 206 | dispatch({ | 198 | dispatch({ |
| ... | @@ -254,12 +246,12 @@ export default function Sidebar() { | ... | @@ -254,12 +246,12 @@ export default function Sidebar() { |
| 254 | 246 | ||
| 255 | {/* Add project form */} | 247 | {/* Add project form */} |
| 256 | {addingProject ? ( | 248 | {addingProject ? ( |
| 257 | <div className="border-t border-white/[0.08] p-3"> | 249 | <div className="border-t border-white/8 p-3"> |
| 258 | <p className="mb-2 text-[10px] font-medium uppercase tracking-wider text-[#a0a0a0]/50"> | 250 | <p className="mb-2 text-[10px] font-medium uppercase tracking-wider text-[#a0a0a0]/50"> |
| 259 | Add project | 251 | Add project |
| 260 | </p> | 252 | </p> |
| 261 | <input | 253 | <input |
| 262 | className="mb-2 w-full rounded-md border border-white/[0.1] bg-white/[0.04] px-2 py-1.5 font-mono text-xs text-[#e0e0e0] placeholder:text-[#a0a0a0]/30 focus:border-white/30 focus:outline-none" | 254 | className="mb-2 w-full rounded-md border border-white/10 bg-white/4 px-2 py-1.5 font-mono text-xs text-[#e0e0e0] placeholder:text-[#a0a0a0]/30 focus:border-white/30 focus:outline-none" |
| 263 | placeholder="/path/to/project" | 255 | placeholder="/path/to/project" |
| 264 | value={newCwd} | 256 | value={newCwd} |
| 265 | onChange={(e) => setNewCwd(e.target.value)} | 257 | onChange={(e) => setNewCwd(e.target.value)} |
| ... | @@ -271,7 +263,7 @@ export default function Sidebar() { | ... | @@ -271,7 +263,7 @@ export default function Sidebar() { |
| 271 | {api && ( | 263 | {api && ( |
| 272 | <button | 264 | <button |
| 273 | type="button" | 265 | type="button" |
| 274 | className="mb-2 flex w-full items-center justify-center rounded-md border border-white/[0.1] px-2 py-1.5 text-xs text-[#a0a0a0]/70 transition-colors duration-150 hover:bg-white/[0.04] disabled:cursor-not-allowed disabled:opacity-60" | 266 | className="mb-2 flex w-full items-center justify-center rounded-md border border-white/10 px-2 py-1.5 text-xs text-[#a0a0a0]/70 transition-colors duration-150 hover:bg-white/4 disabled:cursor-not-allowed disabled:opacity-60" |
| 275 | onClick={() => void handlePickFolder()} | 267 | onClick={() => void handlePickFolder()} |
| 276 | disabled={isPickingFolder} | 268 | disabled={isPickingFolder} |
| 277 | > | 269 | > |
| ... | @@ -288,7 +280,7 @@ export default function Sidebar() { | ... | @@ -288,7 +280,7 @@ export default function Sidebar() { |
| 288 | </button> | 280 | </button> |
| 289 | <button | 281 | <button |
| 290 | type="button" | 282 | type="button" |
| 291 | className="flex-1 rounded-md border border-white/[0.1] px-2 py-1 text-xs text-[#a0a0a0]/60 transition-colors duration-150 hover:bg-white/[0.04]" | 283 | className="flex-1 rounded-md border border-white/10 px-2 py-1 text-xs text-[#a0a0a0]/60 transition-colors duration-150 hover:bg-white/4" |
| 292 | onClick={() => setAddingProject(false)} | 284 | onClick={() => setAddingProject(false)} |
| 293 | > | 285 | > |
| 294 | Cancel | 286 | Cancel |
| ... | @@ -296,10 +288,10 @@ export default function Sidebar() { | ... | @@ -296,10 +288,10 @@ export default function Sidebar() { |
| 296 | </div> | 288 | </div> |
| 297 | </div> | 289 | </div> |
| 298 | ) : ( | 290 | ) : ( |
| 299 | <div className="border-t border-white/[0.08] p-3"> | 291 | <div className="border-t border-white/8 p-3"> |
| 300 | <button | 292 | <button |
| 301 | type="button" | 293 | type="button" |
| 302 | className="flex w-full items-center justify-center gap-1 rounded-md border border-dashed border-white/[0.12] py-2 text-xs text-[#a0a0a0]/50 transition-colors duration-150 hover:border-white/[0.2] hover:text-[#a0a0a0]/70" | 294 | className="flex w-full items-center justify-center gap-1 rounded-md border border-dashed border-white/12 py-2 text-xs text-[#a0a0a0]/50 transition-colors duration-150 hover:border-white/20 hover:text-[#a0a0a0]/70" |
| 303 | onClick={() => setAddingProject(true)} | 295 | onClick={() => setAddingProject(true)} |
| 304 | > | 296 | > |
| 305 | + Add project | 297 | + Add project |
apps/renderer/src/index.css+3-4| ... | @@ -5,8 +5,8 @@ | ... | @@ -5,8 +5,8 @@ |
| 5 | } | 5 | } |
| 6 | 6 | ||
| 7 | body { | 7 | body { |
| 8 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, | 8 | font-family: |
| 9 | "Helvetica Neue", Arial, sans-serif; | 9 | -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; |
| 10 | margin: 0; | 10 | margin: 0; |
| 11 | padding: 0; | 11 | padding: 0; |
| 12 | min-height: 100vh; | 12 | min-height: 100vh; |
| ... | @@ -29,8 +29,7 @@ pre, | ... | @@ -29,8 +29,7 @@ pre, |
| 29 | code, | 29 | code, |
| 30 | textarea, | 30 | textarea, |
| 31 | input { | 31 | input { |
| 32 | font-family: "SF Mono", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, | 32 | font-family: "SF Mono", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; |
| 33 | monospace; | ||
| 34 | } | 33 | } |
| 35 | 34 | ||
| 36 | /* Window drag region (frameless titlebar) */ | 35 | /* Window drag region (frameless titlebar) */ |
apps/renderer/src/model-logic.test.ts+1-5| ... | @@ -1,9 +1,5 @@ | ... | @@ -1,9 +1,5 @@ |
| 1 | import { describe, expect, it } from "vitest"; | 1 | import { describe, expect, it } from "vitest"; |
| 2 | import { | 2 | import { DEFAULT_MODEL, normalizeModelSlug, resolveModelSlug } from "./model-logic"; |
| 3 | DEFAULT_MODEL, | ||
| 4 | normalizeModelSlug, | ||
| 5 | resolveModelSlug, | ||
| 6 | } from "./model-logic"; | ||
| 7 | 3 | ||
| 8 | describe("normalizeModelSlug", () => { | 4 | describe("normalizeModelSlug", () => { |
| 9 | it("maps 5.3 aliases to canonical codex slug", () => { | 5 | it("maps 5.3 aliases to canonical codex slug", () => { |
apps/renderer/src/persistenceSchema.ts+4-11| ... | @@ -69,10 +69,7 @@ export interface PersistedStoreSnapshot { | ... | @@ -69,10 +69,7 @@ export interface PersistedStoreSnapshot { |
| 69 | runtimeMode: RuntimeMode; | 69 | runtimeMode: RuntimeMode; |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | function maybeMigrateLegacyModel( | 72 | function maybeMigrateLegacyModel(model: string, isLegacyPayload: boolean): string { |
| 73 | model: string, | ||
| 74 | isLegacyPayload: boolean, | ||
| 75 | ): string { | ||
| 76 | if (!isLegacyPayload) { | 73 | if (!isLegacyPayload) { |
| 77 | return model; | 74 | return model; |
| 78 | } | 75 | } |
| ... | @@ -86,9 +83,7 @@ function hydrateProject( | ... | @@ -86,9 +83,7 @@ function hydrateProject( |
| 86 | ): Project { | 83 | ): Project { |
| 87 | return { | 84 | return { |
| 88 | ...project, | 85 | ...project, |
| 89 | model: resolveModelSlug( | 86 | model: resolveModelSlug(maybeMigrateLegacyModel(project.model, isLegacyPayload)), |
| 90 | maybeMigrateLegacyModel(project.model, isLegacyPayload), | ||
| 91 | ), | ||
| 92 | }; | 87 | }; |
| 93 | } | 88 | } |
| 94 | 89 | ||
| ... | @@ -100,9 +95,7 @@ function hydrateThread( | ... | @@ -100,9 +95,7 @@ function hydrateThread( |
| 100 | id: thread.id, | 95 | id: thread.id, |
| 101 | projectId: thread.projectId, | 96 | projectId: thread.projectId, |
| 102 | title: thread.title, | 97 | title: thread.title, |
| 103 | model: resolveModelSlug( | 98 | model: resolveModelSlug(maybeMigrateLegacyModel(thread.model, isLegacyPayload)), |
| 104 | maybeMigrateLegacyModel(thread.model, isLegacyPayload), | ||
| 105 | ), | ||
| 106 | session: null, | 99 | session: null, |
| 107 | messages: thread.messages.map((message) => ({ | 100 | messages: thread.messages.map((message) => ({ |
| 108 | ...message, | 101 | ...message, |
| ... | @@ -139,7 +132,7 @@ export function hydratePersistedState( | ... | @@ -139,7 +132,7 @@ export function hydratePersistedState( |
| 139 | .filter((thread) => projectIds.has(thread.projectId)); | 132 | .filter((thread) => projectIds.has(thread.projectId)); |
| 140 | const hasActiveThread = Boolean( | 133 | const hasActiveThread = Boolean( |
| 141 | parsedState.data.activeThreadId && | 134 | parsedState.data.activeThreadId && |
| 142 | threads.some((thread) => thread.id === parsedState.data.activeThreadId), | 135 | threads.some((thread) => thread.id === parsedState.data.activeThreadId), |
| 143 | ); | 136 | ); |
| 144 | 137 | ||
| 145 | return { | 138 | return { |
apps/renderer/src/session-logic.test.ts+2-7| ... | @@ -22,9 +22,7 @@ function makeEvent(overrides: Partial<ProviderEvent>): ProviderEvent { | ... | @@ -22,9 +22,7 @@ function makeEvent(overrides: Partial<ProviderEvent>): ProviderEvent { |
| 22 | }; | 22 | }; |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | function makeSession( | 25 | function makeSession(overrides: Partial<ProviderSession> = {}): ProviderSession { |
| 26 | overrides: Partial<ProviderSession> = {}, | ||
| 27 | ): ProviderSession { | ||
| 28 | return { | 26 | return { |
| 29 | sessionId: "sess-1", | 27 | sessionId: "sess-1", |
| 30 | provider: "codex", | 28 | provider: "codex", |
| ... | @@ -99,10 +97,7 @@ describe("deriveTimelineEntries", () => { | ... | @@ -99,10 +97,7 @@ describe("deriveTimelineEntries", () => { |
| 99 | 97 | ||
| 100 | const timeline = deriveTimelineEntries(messages, workEntries); | 98 | const timeline = deriveTimelineEntries(messages, workEntries); |
| 101 | 99 | ||
| 102 | expect(timeline.map((entry) => entry.id)).toEqual([ | 100 | expect(timeline.map((entry) => entry.id)).toEqual(["work:w-1", "message:m-1"]); |
| 103 | "work:w-1", | ||
| 104 | "message:m-1", | ||
| 105 | ]); | ||
| 106 | }); | 101 | }); |
| 107 | }); | 102 | }); |
| 108 | 103 |
apps/renderer/src/session-logic.ts+5-17| ... | @@ -1,8 +1,4 @@ | ... | @@ -1,8 +1,4 @@ |
| 1 | import type { | 1 | import type { ProviderEvent, ProviderKind, ProviderSession } from "@acme/contracts"; |
| 2 | ProviderEvent, | ||
| 3 | ProviderKind, | ||
| 4 | ProviderSession, | ||
| 5 | } from "@acme/contracts"; | ||
| 6 | import type { ChatMessage, SessionPhase } from "./types"; | 2 | import type { ChatMessage, SessionPhase } from "./types"; |
| 7 | 3 | ||
| 8 | export const PROVIDER_OPTIONS: Array<{ | 4 | export const PROVIDER_OPTIONS: Array<{ |
| ... | @@ -49,10 +45,7 @@ export function formatDuration(durationMs: number): string { | ... | @@ -49,10 +45,7 @@ export function formatDuration(durationMs: number): string { |
| 49 | return `${minutes}m ${seconds}s`; | 45 | return `${minutes}m ${seconds}s`; |
| 50 | } | 46 | } |
| 51 | 47 | ||
| 52 | export function formatElapsed( | 48 | export function formatElapsed(startIso: string, endIso: string | undefined): string | null { |
| 53 | startIso: string, | ||
| 54 | endIso: string | undefined, | ||
| 55 | ): string | null { | ||
| 56 | if (!endIso) return null; | 49 | if (!endIso) return null; |
| 57 | 50 | ||
| 58 | const startedAt = Date.parse(startIso); | 51 | const startedAt = Date.parse(startIso); |
| ... | @@ -304,7 +297,7 @@ export function deriveWorkLogEntries( | ... | @@ -304,7 +297,7 @@ export function deriveWorkLogEntries( |
| 304 | events: ProviderEvent[], | 297 | events: ProviderEvent[], |
| 305 | turnId: string | undefined, | 298 | turnId: string | undefined, |
| 306 | ): WorkLogEntry[] { | 299 | ): WorkLogEntry[] { |
| 307 | const ordered = [...events].reverse(); | 300 | const ordered = [...events].toReversed(); |
| 308 | const entries: WorkLogEntry[] = []; | 301 | const entries: WorkLogEntry[] = []; |
| 309 | const turnStartedAtIso = turnId | 302 | const turnStartedAtIso = turnId |
| 310 | ? ordered.find((event) => { | 303 | ? ordered.find((event) => { |
| ... | @@ -312,9 +305,7 @@ export function deriveWorkLogEntries( | ... | @@ -312,9 +305,7 @@ export function deriveWorkLogEntries( |
| 312 | return eventTurnId(event) === turnId; | 305 | return eventTurnId(event) === turnId; |
| 313 | })?.createdAt | 306 | })?.createdAt |
| 314 | : undefined; | 307 | : undefined; |
| 315 | const turnStartedAt = turnStartedAtIso | 308 | const turnStartedAt = turnStartedAtIso ? Date.parse(turnStartedAtIso) : Number.NaN; |
| 316 | ? Date.parse(turnStartedAtIso) | ||
| 317 | : Number.NaN; | ||
| 318 | 309 | ||
| 319 | for (const event of ordered) { | 310 | for (const event of ordered) { |
| 320 | if (turnId) { | 311 | if (turnId) { |
| ... | @@ -428,10 +419,7 @@ export function derivePhase(session: ProviderSession | null): SessionPhase { | ... | @@ -428,10 +419,7 @@ export function derivePhase(session: ProviderSession | null): SessionPhase { |
| 428 | return "ready"; | 419 | return "ready"; |
| 429 | } | 420 | } |
| 430 | 421 | ||
| 431 | export function evolveSession( | 422 | export function evolveSession(previous: ProviderSession, event: ProviderEvent): ProviderSession { |
| 432 | previous: ProviderSession, | ||
| 433 | event: ProviderEvent, | ||
| 434 | ): ProviderSession { | ||
| 435 | const payload = asObject(event.payload); | 423 | const payload = asObject(event.payload); |
| 436 | 424 | ||
| 437 | if (event.method === "thread/started") { | 425 | if (event.method === "thread/started") { |
apps/renderer/src/store.ts+2-9| ... | @@ -116,10 +116,7 @@ function updateThread( | ... | @@ -116,10 +116,7 @@ function updateThread( |
| 116 | return threads.map((t) => (t.id === threadId ? updater(t) : t)); | 116 | return threads.map((t) => (t.id === threadId ? updater(t) : t)); |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | function findThreadBySessionId( | 119 | function findThreadBySessionId(threads: Thread[], sessionId: string): Thread | undefined { |
| 120 | threads: Thread[], | ||
| 121 | sessionId: string, | ||
| 122 | ): Thread | undefined { | ||
| 123 | return threads.find((t) => t.session?.sessionId === sessionId); | 120 | return threads.find((t) => t.session?.sessionId === sessionId); |
| 124 | } | 121 | } |
| 125 | 122 | ||
| ... | @@ -325,11 +322,7 @@ export function StoreProvider({ children }: { children: ReactNode }) { | ... | @@ -325,11 +322,7 @@ export function StoreProvider({ children }: { children: ReactNode }) { |
| 325 | persistState(state); | 322 | persistState(state); |
| 326 | }, [state]); | 323 | }, [state]); |
| 327 | 324 | ||
| 328 | return createElement( | 325 | return createElement(StoreContext.Provider, { value: { state, dispatch } }, children); |
| 329 | StoreContext.Provider, | ||
| 330 | { value: { state, dispatch } }, | ||
| 331 | children, | ||
| 332 | ); | ||
| 333 | } | 326 | } |
| 334 | 327 | ||
| 335 | export function useStore() { | 328 | export function useStore() { |
apps/renderer/tsconfig.json+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | "compilerOptions": { | 3 | "compilerOptions": { |
| 4 | "composite": true, | 4 | "composite": true, |
| 5 | "jsx": "react-jsx", | 5 | "jsx": "react-jsx", |
| 6 | "lib": ["ES2022", "DOM", "DOM.Iterable"], | 6 | "lib": ["ES2023", "DOM", "DOM.Iterable"], |
| 7 | "types": ["vite/client"] | 7 | "types": ["vite/client"] |
| 8 | }, | 8 | }, |
| 9 | "include": ["src", "vite.config.ts"] | 9 | "include": ["src", "vite.config.ts"] |
biome.json deleted-29| ... | @@ -1,29 +0,0 @@ | ||
| 1 | { | ||
| 2 | "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json", | ||
| 3 | "organizeImports": { | ||
| 4 | "enabled": true | ||
| 5 | }, | ||
| 6 | "linter": { | ||
| 7 | "enabled": true, | ||
| 8 | "rules": { | ||
| 9 | "recommended": true, | ||
| 10 | "correctness": { | ||
| 11 | "noUnusedImports": "warn", | ||
| 12 | "noUnusedVariables": "warn" | ||
| 13 | } | ||
| 14 | } | ||
| 15 | }, | ||
| 16 | "formatter": { | ||
| 17 | "indentStyle": "space", | ||
| 18 | "indentWidth": 2 | ||
| 19 | }, | ||
| 20 | "files": { | ||
| 21 | "ignore": [ | ||
| 22 | "dist", | ||
| 23 | "dist-electron", | ||
| 24 | "node_modules", | ||
| 25 | "bun.lock", | ||
| 26 | "*.tsbuildinfo" | ||
| 27 | ] | ||
| 28 | } | ||
| 29 | } | ||
bun.lock+42-30| ... | @@ -5,7 +5,8 @@ | ... | @@ -5,7 +5,8 @@ |
| 5 | "": { | 5 | "": { |
| 6 | "name": "ct-round-5", | 6 | "name": "ct-round-5", |
| 7 | "devDependencies": { | 7 | "devDependencies": { |
| 8 | "@biomejs/biome": "^1.9.0", | 8 | "oxfmt": "^0.28.0", |
| 9 | "oxlint": "^1.43.0", | ||
| 9 | "turbo": "^2.3.3", | 10 | "turbo": "^2.3.3", |
| 10 | "typescript": "^5.7.3", | 11 | "typescript": "^5.7.3", |
| 11 | "vitest": "^3.0.0", | 12 | "vitest": "^3.0.0", |
| ... | @@ -22,7 +23,6 @@ | ... | @@ -22,7 +23,6 @@ |
| 22 | "devDependencies": { | 23 | "devDependencies": { |
| 23 | "@electron/rebuild": "^3.7.0", | 24 | "@electron/rebuild": "^3.7.0", |
| 24 | "@types/node": "^22.10.2", | 25 | "@types/node": "^22.10.2", |
| 25 | "concurrently": "^9.1.2", | ||
| 26 | "electronmon": "^2.0.2", | 26 | "electronmon": "^2.0.2", |
| 27 | "tsup": "^8.3.5", | 27 | "tsup": "^8.3.5", |
| 28 | "typescript": "^5.7.3", | 28 | "typescript": "^5.7.3", |
| ... | @@ -110,24 +110,6 @@ | ... | @@ -110,24 +110,6 @@ |
| 110 | 110 | ||
| 111 | "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], | 111 | "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], |
| 112 | 112 | ||
| 113 | "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], | ||
| 114 | |||
| 115 | "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], | ||
| 116 | |||
| 117 | "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], | ||
| 118 | |||
| 119 | "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], | ||
| 120 | |||
| 121 | "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], | ||
| 122 | |||
| 123 | "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], | ||
| 124 | |||
| 125 | "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], | ||
| 126 | |||
| 127 | "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], | ||
| 128 | |||
| 129 | "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], | ||
| 130 | |||
| 131 | "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="], | 113 | "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="], |
| 132 | 114 | ||
| 133 | "@electron/node-gyp": ["@electron/node-gyp@github:electron/node-gyp#06b29aa", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^8.1.0", "graceful-fs": "^4.2.6", "make-fetch-happen": "^10.2.1", "nopt": "^6.0.0", "proc-log": "^2.0.1", "semver": "^7.3.5", "tar": "^6.2.1", "which": "^2.0.2" }, "bin": "./bin/node-gyp.js" }, "electron-node-gyp-06b29aa"], | 115 | "@electron/node-gyp": ["@electron/node-gyp@github:electron/node-gyp#06b29aa", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^8.1.0", "graceful-fs": "^4.2.6", "make-fetch-happen": "^10.2.1", "nopt": "^6.0.0", "proc-log": "^2.0.1", "semver": "^7.3.5", "tar": "^6.2.1", "which": "^2.0.2" }, "bin": "./bin/node-gyp.js" }, "electron-node-gyp-06b29aa"], |
| ... | @@ -216,6 +198,38 @@ | ... | @@ -216,6 +198,38 @@ |
| 216 | 198 | ||
| 217 | "@npmcli/move-file": ["@npmcli/move-file@2.0.1", "", { "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" } }, "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ=="], | 199 | "@npmcli/move-file": ["@npmcli/move-file@2.0.1", "", { "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" } }, "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ=="], |
| 218 | 200 | ||
| 201 | "@oxfmt/darwin-arm64": ["@oxfmt/darwin-arm64@0.28.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jmUfF7cNJPw57bEK7sMIqrYRgn4LH428tSgtgLTCtjuGuu1ShREyrkeB7y8HtkXRfhBs4lVY+HMLhqElJvZ6ww=="], | ||
| 202 | |||
| 203 | "@oxfmt/darwin-x64": ["@oxfmt/darwin-x64@0.28.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-S6vlV8S7jbjzJOSjfVg2CimUC0r7/aHDLdUm/3+/B/SU/s1jV7ivqWkMv1/8EB43d1BBwT9JQ60ZMTkBqeXSFA=="], | ||
| 204 | |||
| 205 | "@oxfmt/linux-arm64-gnu": ["@oxfmt/linux-arm64-gnu@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-TfJkMZjePbLiskmxFXVAbGI/OZtD+y+fwS0wyW8O6DWG0ARTf0AipY9zGwGoOdpFuXOJceXvN4SHGLbYNDMY4Q=="], | ||
| 206 | |||
| 207 | "@oxfmt/linux-arm64-musl": ["@oxfmt/linux-arm64-musl@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-7fyQUdW203v4WWGr1T3jwTz4L7KX9y5DeATryQ6fLT6QQp9GEuct8/k0lYhd+ys42iTV/IkJF20e3YkfSOOILg=="], | ||
| 208 | |||
| 209 | "@oxfmt/linux-x64-gnu": ["@oxfmt/linux-x64-gnu@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-sRKqAvEonuz0qr1X1ncUZceOBJerKzkO2gZIZmosvy/JmqyffpIFL3OE2tqacFkeDhrC+dNYQpusO8zsfHo3pw=="], | ||
| 210 | |||
| 211 | "@oxfmt/linux-x64-musl": ["@oxfmt/linux-x64-musl@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-fW6czbXutX/tdQe8j4nSIgkUox9RXqjyxwyWXUDItpoDkoXllq17qbD7GVc0whrEhYQC6hFE1UEAcDypLJoSzw=="], | ||
| 212 | |||
| 213 | "@oxfmt/win32-arm64": ["@oxfmt/win32-arm64@0.28.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-D/HDeQBAQRjTbD9OLV6kRDcStrIfO+JsUODDCdGmhRfNX8LPCx95GpfyybpZfn3wVF8Jq/yjPXV1xLkQ+s7RcA=="], | ||
| 214 | |||
| 215 | "@oxfmt/win32-x64": ["@oxfmt/win32-x64@0.28.0", "", { "os": "win32", "cpu": "x64" }, "sha512-4+S2j4OxOIyo8dz5osm5dZuL0yVmxXvtmNdHB5xyGwAWVvyWNvf7tCaQD7w2fdSsAXQLOvK7KFQrHFe33nJUCA=="], | ||
| 216 | |||
| 217 | "@oxlint/darwin-arm64": ["@oxlint/darwin-arm64@1.43.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C/GhObv/pQZg34NOzB6Mk8x0wc9AKj8fXzJF8ZRKTsBPyHusC6AZ6bba0QG0TUufw1KWuD0j++oebQfWeiFXNw=="], | ||
| 218 | |||
| 219 | "@oxlint/darwin-x64": ["@oxlint/darwin-x64@1.43.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-4NjfUtEEH8ewRQ2KlZGmm6DyrvypMdHwBnQT92vD0dLScNOQzr0V9O8Ua4IWXdeCNl/XMVhAV3h4/3YEYern5A=="], | ||
| 220 | |||
| 221 | "@oxlint/linux-arm64-gnu": ["@oxlint/linux-arm64-gnu@1.43.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-75tf1HvwdZ3ebk83yMbSB+moAEWK98mYqpXiaFAi6Zshie7r+Cx5PLXZFUEqkscenoZ+fcNXakHxfn94V6nf1g=="], | ||
| 222 | |||
| 223 | "@oxlint/linux-arm64-musl": ["@oxlint/linux-arm64-musl@1.43.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-BHV4fb36T2p/7bpA9fiJ5ayt7oJbiYX10nklW5arYp4l9/9yG/FQC5J4G1evzbJ/YbipF9UH0vYBAm5xbqGrvw=="], | ||
| 224 | |||
| 225 | "@oxlint/linux-x64-gnu": ["@oxlint/linux-x64-gnu@1.43.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1l3nvnzWWse1YHibzZ4HQXdF/ibfbKZhp9IguElni3bBqEyPEyurzZ0ikWynDxKGXqZa+UNXTFuU1NRVX1RJ3g=="], | ||
| 226 | |||
| 227 | "@oxlint/linux-x64-musl": ["@oxlint/linux-x64-musl@1.43.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+jNYgLGRFTJxJuaSOZJBwlYo5M0TWRw0+3y5MHOL4ArrIdHyCthg6r4RbVWrsR1qUfUE1VSSHQ2bfbC99RXqMg=="], | ||
| 228 | |||
| 229 | "@oxlint/win32-arm64": ["@oxlint/win32-arm64@1.43.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-dvs1C/HCjCyGTURMagiHprsOvVTT3omDiSzi5Qw0D4QFJ1pEaNlfBhVnOUYgUfS6O7Mcmj4+G+sidRsQcWQ/kA=="], | ||
| 230 | |||
| 231 | "@oxlint/win32-x64": ["@oxlint/win32-x64@1.43.0", "", { "os": "win32", "cpu": "x64" }, "sha512-bSuItSU8mTSDsvmmLTepTdCL2FkJI6dwt9tot/k0EmiYF+ArRzmsl4lXVLssJNRV5lJEc5IViyTrh7oiwrjUqA=="], | ||
| 232 | |||
| 219 | "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], | 233 | "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], |
| 220 | 234 | ||
| 221 | "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], | 235 | "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], |
| ... | @@ -470,8 +484,6 @@ | ... | @@ -470,8 +484,6 @@ |
| 470 | 484 | ||
| 471 | "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], | 485 | "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], |
| 472 | 486 | ||
| 473 | "concurrently": ["concurrently@9.2.1", "", { "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", "shell-quote": "1.8.3", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" }, "bin": { "conc": "dist/bin/concurrently.js", "concurrently": "dist/bin/concurrently.js" } }, "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng=="], | ||
| 474 | |||
| 475 | "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], | 487 | "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], |
| 476 | 488 | ||
| 477 | "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], | 489 | "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], |
| ... | @@ -904,6 +916,10 @@ | ... | @@ -904,6 +916,10 @@ |
| 904 | 916 | ||
| 905 | "ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="], | 917 | "ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="], |
| 906 | 918 | ||
| 919 | "oxfmt": ["oxfmt@0.28.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/darwin-arm64": "0.28.0", "@oxfmt/darwin-x64": "0.28.0", "@oxfmt/linux-arm64-gnu": "0.28.0", "@oxfmt/linux-arm64-musl": "0.28.0", "@oxfmt/linux-x64-gnu": "0.28.0", "@oxfmt/linux-x64-musl": "0.28.0", "@oxfmt/win32-arm64": "0.28.0", "@oxfmt/win32-x64": "0.28.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-3+hhBqPE6Kp22KfJmnstrZbl+KdOVSEu1V0ABaFIg1rYLtrMgrupx9znnHgHLqKxAVHebjTdiCJDk30CXOt6cw=="], | ||
| 920 | |||
| 921 | "oxlint": ["oxlint@1.43.0", "", { "optionalDependencies": { "@oxlint/darwin-arm64": "1.43.0", "@oxlint/darwin-x64": "1.43.0", "@oxlint/linux-arm64-gnu": "1.43.0", "@oxlint/linux-arm64-musl": "1.43.0", "@oxlint/linux-x64-gnu": "1.43.0", "@oxlint/linux-x64-musl": "1.43.0", "@oxlint/win32-arm64": "1.43.0", "@oxlint/win32-x64": "1.43.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.11.2" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-xiqTCsKZch+R61DPCjyqUVP2MhkQlRRYxLRBeBDi+dtQJ90MOgdcjIktvDCgXz0bgtx94EQzHEndsizZjMX2OA=="], | ||
| 922 | |||
| 907 | "p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="], | 923 | "p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="], |
| 908 | 924 | ||
| 909 | "p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="], | 925 | "p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="], |
| ... | @@ -1014,8 +1030,6 @@ | ... | @@ -1014,8 +1030,6 @@ |
| 1014 | 1030 | ||
| 1015 | "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], | 1031 | "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], |
| 1016 | 1032 | ||
| 1017 | "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], | ||
| 1018 | |||
| 1019 | "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], | 1033 | "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], |
| 1020 | 1034 | ||
| 1021 | "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], | 1035 | "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], |
| ... | @@ -1058,7 +1072,7 @@ | ... | @@ -1058,7 +1072,7 @@ |
| 1058 | 1072 | ||
| 1059 | "sumchecker": ["sumchecker@3.0.1", "", { "dependencies": { "debug": "^4.1.0" } }, "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg=="], | 1073 | "sumchecker": ["sumchecker@3.0.1", "", { "dependencies": { "debug": "^4.1.0" } }, "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg=="], |
| 1060 | 1074 | ||
| 1061 | "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], | 1075 | "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], |
| 1062 | 1076 | ||
| 1063 | "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], | 1077 | "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], |
| 1064 | 1078 | ||
| ... | @@ -1076,7 +1090,7 @@ | ... | @@ -1076,7 +1090,7 @@ |
| 1076 | 1090 | ||
| 1077 | "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], | 1091 | "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], |
| 1078 | 1092 | ||
| 1079 | "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], | 1093 | "tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="], |
| 1080 | 1094 | ||
| 1081 | "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], | 1095 | "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], |
| 1082 | 1096 | ||
| ... | @@ -1210,8 +1224,6 @@ | ... | @@ -1210,8 +1224,6 @@ |
| 1210 | 1224 | ||
| 1211 | "cacache/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], | 1225 | "cacache/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], |
| 1212 | 1226 | ||
| 1213 | "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], | ||
| 1214 | |||
| 1215 | "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], | 1227 | "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], |
| 1216 | 1228 | ||
| 1217 | "electron/@types/node": ["@types/node@20.19.33", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw=="], | 1229 | "electron/@types/node": ["@types/node@20.19.33", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw=="], |
| ... | @@ -1246,14 +1258,14 @@ | ... | @@ -1246,14 +1258,14 @@ |
| 1246 | 1258 | ||
| 1247 | "vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], | 1259 | "vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], |
| 1248 | 1260 | ||
| 1261 | "vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], | ||
| 1262 | |||
| 1249 | "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], | 1263 | "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], |
| 1250 | 1264 | ||
| 1251 | "@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], | 1265 | "@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], |
| 1252 | 1266 | ||
| 1253 | "@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], | 1267 | "@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], |
| 1254 | 1268 | ||
| 1255 | "electronmon/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], | ||
| 1256 | |||
| 1257 | "rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], | 1269 | "rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], |
| 1258 | 1270 | ||
| 1259 | "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], | 1271 | "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], |
package.json+13-7| ... | @@ -1,23 +1,29 @@ | ... | @@ -1,23 +1,29 @@ |
| 1 | { | 1 | { |
| 2 | "name": "ct-round-5", | 2 | "name": "ct-round-5", |
| 3 | "private": true, | 3 | "private": true, |
| 4 | "packageManager": "bun@1.1.38", | 4 | "workspaces": [ |
| 5 | "workspaces": ["apps/*", "packages/*"], | 5 | "apps/*", |
| 6 | "packages/*" | ||
| 7 | ], | ||
| 6 | "scripts": { | 8 | "scripts": { |
| 7 | "dev": "bun run build:contracts && turbo run dev --parallel", | 9 | "dev": "bun run build:contracts && turbo run dev --parallel", |
| 8 | "build": "turbo run build", | 10 | "build": "turbo run build", |
| 9 | "typecheck": "turbo run typecheck", | 11 | "typecheck": "turbo run typecheck", |
| 10 | "lint": "turbo run lint", | 12 | "lint": "oxlint --report-unused-disable-directives", |
| 11 | "test": "turbo run test", | 13 | "test": "turbo run test", |
| 12 | "format": "biome format --write .", | 14 | "fmt": "oxfmt", |
| 13 | "check": "biome check .", | ||
| 14 | "smoke-test": "turbo run smoke-test", | 15 | "smoke-test": "turbo run smoke-test", |
| 15 | "build:contracts": "bun run --cwd packages/contracts build" | 16 | "build:contracts": "bun run --cwd packages/contracts build" |
| 16 | }, | 17 | }, |
| 17 | "devDependencies": { | 18 | "devDependencies": { |
| 18 | "@biomejs/biome": "^1.9.0", | 19 | "oxfmt": "^0.28.0", |
| 20 | "oxlint": "^1.43.0", | ||
| 19 | "turbo": "^2.3.3", | 21 | "turbo": "^2.3.3", |
| 20 | "typescript": "^5.7.3", | 22 | "typescript": "^5.7.3", |
| 21 | "vitest": "^3.0.0" | 23 | "vitest": "^3.0.0" |
| 22 | } | 24 | }, |
| 25 | "engines": { | ||
| 26 | "bun": ">=1.3.9" | ||
| 27 | }, | ||
| 28 | "packageManager": "bun@1.3.9" | ||
| 23 | } | 29 | } |
packages/contracts/package.json+3-2| ... | @@ -2,6 +2,9 @@ | ... | @@ -2,6 +2,9 @@ |
| 2 | "name": "@acme/contracts", | 2 | "name": "@acme/contracts", |
| 3 | "version": "0.0.0", | 3 | "version": "0.0.0", |
| 4 | "private": true, | 4 | "private": true, |
| 5 | "files": [ | ||
| 6 | "dist" | ||
| 7 | ], | ||
| 5 | "type": "module", | 8 | "type": "module", |
| 6 | "main": "./dist/index.cjs", | 9 | "main": "./dist/index.cjs", |
| 7 | "module": "./dist/index.js", | 10 | "module": "./dist/index.js", |
| ... | @@ -13,12 +16,10 @@ | ... | @@ -13,12 +16,10 @@ |
| 13 | "require": "./dist/index.cjs" | 16 | "require": "./dist/index.cjs" |
| 14 | } | 17 | } |
| 15 | }, | 18 | }, |
| 16 | "files": ["dist"], | ||
| 17 | "scripts": { | 19 | "scripts": { |
| 18 | "dev": "tsup src/index.ts --format esm,cjs --dts --watch --clean", | 20 | "dev": "tsup src/index.ts --format esm,cjs --dts --watch --clean", |
| 19 | "build": "tsup src/index.ts --format esm,cjs --dts --clean", | 21 | "build": "tsup src/index.ts --format esm,cjs --dts --clean", |
| 20 | "typecheck": "tsc --noEmit", | 22 | "typecheck": "tsc --noEmit", |
| 21 | "lint": "biome check src/", | ||
| 22 | "test": "vitest run" | 23 | "test": "vitest run" |
| 23 | }, | 24 | }, |
| 24 | "dependencies": { | 25 | "dependencies": { |
packages/contracts/src/ipc.ts+2-6| ... | @@ -62,12 +62,8 @@ export interface NativeApi { | ... | @@ -62,12 +62,8 @@ export interface NativeApi { |
| 62 | onExit: (callback: (exit: AgentExit) => void) => () => void; | 62 | onExit: (callback: (exit: AgentExit) => void) => () => void; |
| 63 | }; | 63 | }; |
| 64 | providers: { | 64 | providers: { |
| 65 | startSession: ( | 65 | startSession: (input: ProviderSessionStartInput) => Promise<ProviderSession>; |
| 66 | input: ProviderSessionStartInput, | 66 | sendTurn: (input: ProviderSendTurnInput) => Promise<ProviderTurnStartResult>; |
| 67 | ) => Promise<ProviderSession>; | ||
| 68 | sendTurn: ( | ||
| 69 | input: ProviderSendTurnInput, | ||
| 70 | ) => Promise<ProviderTurnStartResult>; | ||
| 71 | interruptTurn: (input: ProviderInterruptTurnInput) => Promise<void>; | 67 | interruptTurn: (input: ProviderInterruptTurnInput) => Promise<void>; |
| 72 | respondToRequest: (input: ProviderRespondToRequestInput) => Promise<void>; | 68 | respondToRequest: (input: ProviderRespondToRequestInput) => Promise<void>; |
| 73 | stopSession: (input: ProviderStopSessionInput) => Promise<void>; | 69 | stopSession: (input: ProviderStopSessionInput) => Promise<void>; |
packages/contracts/src/model.test.ts+1-6| ... | @@ -1,11 +1,6 @@ | ... | @@ -1,11 +1,6 @@ |
| 1 | import { describe, expect, it } from "vitest"; | 1 | import { describe, expect, it } from "vitest"; |
| 2 | 2 | ||
| 3 | import { | 3 | import { DEFAULT_MODEL, MODEL_OPTIONS, normalizeModelSlug, resolveModelSlug } from "./model"; |
| 4 | DEFAULT_MODEL, | ||
| 5 | MODEL_OPTIONS, | ||
| 6 | normalizeModelSlug, | ||
| 7 | resolveModelSlug, | ||
| 8 | } from "./model"; | ||
| 9 | 4 | ||
| 10 | describe("normalizeModelSlug", () => { | 5 | describe("normalizeModelSlug", () => { |
| 11 | it("maps known aliases to canonical slugs", () => { | 6 | it("maps known aliases to canonical slugs", () => { |
packages/contracts/src/model.ts+2-8| ... | @@ -1,8 +1,4 @@ | ... | @@ -1,8 +1,4 @@ |
| 1 | export const MODEL_OPTIONS = [ | 1 | export const MODEL_OPTIONS = ["gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.2"] as const; |
| 2 | "gpt-5.3-codex", | ||
| 3 | "gpt-5.2-codex", | ||
| 4 | "gpt-5.2", | ||
| 5 | ] as const; | ||
| 6 | 2 | ||
| 7 | export const DEFAULT_MODEL = "gpt-5.3-codex"; | 3 | export const DEFAULT_MODEL = "gpt-5.3-codex"; |
| 8 | 4 | ||
| ... | @@ -11,9 +7,7 @@ export const MODEL_SLUG_ALIASES: Record<string, string> = { | ... | @@ -11,9 +7,7 @@ export const MODEL_SLUG_ALIASES: Record<string, string> = { |
| 11 | "gpt-5.3": "gpt-5.3-codex", | 7 | "gpt-5.3": "gpt-5.3-codex", |
| 12 | }; | 8 | }; |
| 13 | 9 | ||
| 14 | export function normalizeModelSlug( | 10 | export function normalizeModelSlug(model: string | null | undefined): string | null { |
| 15 | model: string | null | undefined, | ||
| 16 | ): string | null { | ||
| 17 | if (typeof model !== "string") { | 11 | if (typeof model !== "string") { |
| 18 | return null; | 12 | return null; |
| 19 | } | 13 | } |
packages/contracts/src/provider.ts+2-6| ... | @@ -105,9 +105,7 @@ export const providerEventSchema = z.object({ | ... | @@ -105,9 +105,7 @@ export const providerEventSchema = z.object({ |
| 105 | }); | 105 | }); |
| 106 | 106 | ||
| 107 | export type ProviderKind = z.infer<typeof providerKindSchema>; | 107 | export type ProviderKind = z.infer<typeof providerKindSchema>; |
| 108 | export type ProviderApprovalPolicy = z.infer< | 108 | export type ProviderApprovalPolicy = z.infer<typeof providerApprovalPolicySchema>; |
| 109 | typeof providerApprovalPolicySchema | ||
| 110 | >; | ||
| 111 | export type ProviderSandboxMode = z.infer<typeof providerSandboxModeSchema>; | 109 | export type ProviderSandboxMode = z.infer<typeof providerSandboxModeSchema>; |
| 112 | export type ProviderRequestKind = z.infer<typeof providerRequestKindSchema>; | 110 | export type ProviderRequestKind = z.infer<typeof providerRequestKindSchema>; |
| 113 | export type ProviderApprovalDecision = z.infer< | 111 | export type ProviderApprovalDecision = z.infer< |
| ... | @@ -115,9 +113,7 @@ export type ProviderApprovalDecision = z.infer< | ... | @@ -115,9 +113,7 @@ export type ProviderApprovalDecision = z.infer< |
| 115 | >; | 113 | >; |
| 116 | export type ProviderSessionStatus = z.infer<typeof providerSessionStatusSchema>; | 114 | export type ProviderSessionStatus = z.infer<typeof providerSessionStatusSchema>; |
| 117 | export type ProviderSession = z.infer<typeof providerSessionSchema>; | 115 | export type ProviderSession = z.infer<typeof providerSessionSchema>; |
| 118 | export type ProviderSessionStartInput = z.input< | 116 | export type ProviderSessionStartInput = z.input<typeof providerSessionStartInputSchema>; |
| 119 | typeof providerSessionStartInputSchema | ||
| 120 | >; | ||
| 121 | export type ProviderSendTurnInput = z.input<typeof providerSendTurnInputSchema>; | 117 | export type ProviderSendTurnInput = z.input<typeof providerSendTurnInputSchema>; |
| 122 | export type ProviderTurnStartResult = z.infer< | 118 | export type ProviderTurnStartResult = z.infer< |
| 123 | typeof providerTurnStartResultSchema | 119 | typeof providerTurnStartResultSchema |
tsconfig.base.json+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | { | 1 | { |
| 2 | "compilerOptions": { | 2 | "compilerOptions": { |
| 3 | "target": "ES2022", | 3 | "target": "ES2023", |
| 4 | "module": "ESNext", | 4 | "module": "ESNext", |
| 5 | "moduleResolution": "Bundler", | 5 | "moduleResolution": "Bundler", |
| 6 | "strict": true, | 6 | "strict": true, |
turbo.json-4| ... | @@ -14,10 +14,6 @@ | ... | @@ -14,10 +14,6 @@ |
| 14 | "dependsOn": ["^typecheck"], | 14 | "dependsOn": ["^typecheck"], |
| 15 | "outputs": [] | 15 | "outputs": [] |
| 16 | }, | 16 | }, |
| 17 | "lint": { | ||
| 18 | "dependsOn": ["^lint"], | ||
| 19 | "outputs": [] | ||
| 20 | }, | ||
| 21 | "test": { | 17 | "test": { |
| 22 | "dependsOn": ["^build"], | 18 | "dependsOn": ["^build"], |
| 23 | "outputs": [] | 19 | "outputs": [] |