| 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 | 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 | 51 | Optional: |
| 52 | ||
| 52 | 53 | - `ELECTRON_RENDERER_PORT=5180 bun run dev` if `5173` is already in use. |
| 53 | 54 | |
| 54 | 55 | ## Provider architecture |
apps/desktop/package.json+1-3| ... | ... | @@ -4,14 +4,13 @@ |
| 4 | 4 | "private": true, |
| 5 | 5 | "main": "dist-electron/main.js", |
| 6 | 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 | 8 | "dev:bundle": "tsup --watch", |
| 9 | 9 | "dev:electron": "bun run scripts/dev-electron.mjs", |
| 10 | 10 | "build": "tsup", |
| 11 | 11 | "start": "electron dist-electron/main.js", |
| 12 | 12 | "postinstall": "electron-rebuild", |
| 13 | 13 | "typecheck": "tsc --noEmit", |
| 14 | "lint": "biome check src/", | |
| 15 | 14 | "test": "vitest run", |
| 16 | 15 | "smoke-test": "node scripts/smoke-test.mjs" |
| 17 | 16 | }, |
| ... | ... | @@ -23,7 +22,6 @@ |
| 23 | 22 | "devDependencies": { |
| 24 | 23 | "@electron/rebuild": "^3.7.0", |
| 25 | 24 | "@types/node": "^22.10.2", |
| 26 | "concurrently": "^9.1.2", | |
| 27 | 25 | "electronmon": "^2.0.2", |
| 28 | 26 | "tsup": "^8.3.5", |
| 29 | 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 | 6 | const devServerUrl = `http://localhost:${port}`; |
| 7 | 7 | |
| 8 | 8 | await waitOn({ |
| 9 | resources: [ | |
| 10 | `tcp:${port}`, | |
| 11 | "file:dist-electron/main.js", | |
| 12 | "file:dist-electron/preload.js", | |
| 13 | ], | |
| 9 | resources: [`tcp:${port}`, "file:dist-electron/main.js", "file:dist-electron/preload.js"], | |
| 14 | 10 | }); |
| 15 | 11 | |
| 16 | const command = | |
| 17 | process.platform === "win32" ? "electronmon.cmd" : "electronmon"; | |
| 12 | const command = process.platform === "win32" ? "electronmon.cmd" : "electronmon"; | |
| 18 | 13 | const child = spawn(command, ["dist-electron/main.js"], { |
| 19 | 14 | stdio: "inherit", |
| 20 | 15 | env: { |
apps/desktop/src/codexAppServerManager.test.ts+3-9| ... | ... | @@ -1,9 +1,6 @@ |
| 1 | 1 | import { describe, expect, it } from "vitest"; |
| 2 | 2 | |
| 3 | import { | |
| 4 | classifyCodexStderrLine, | |
| 5 | normalizeCodexModelSlug, | |
| 6 | } from "./codexAppServerManager"; | |
| 3 | import { classifyCodexStderrLine, normalizeCodexModelSlug } from "./codexAppServerManager"; | |
| 7 | 4 | |
| 8 | 5 | describe("classifyCodexStderrLine", () => { |
| 9 | 6 | it("ignores empty lines", () => { |
| ... | ... | @@ -23,8 +20,7 @@ describe("classifyCodexStderrLine", () => { |
| 23 | 20 | }); |
| 24 | 21 | |
| 25 | 22 | it("keeps unknown structured errors", () => { |
| 26 | const line = | |
| 27 | "2026-02-08T04:24:20.085687Z ERROR codex_core::runtime: unrecoverable failure"; | |
| 23 | const line = "2026-02-08T04:24:20.085687Z ERROR codex_core::runtime: unrecoverable failure"; | |
| 28 | 24 | expect(classifyCodexStderrLine(line)).toEqual({ |
| 29 | 25 | message: line, |
| 30 | 26 | }); |
| ... | ... | @@ -45,9 +41,7 @@ describe("normalizeCodexModelSlug", () => { |
| 45 | 41 | }); |
| 46 | 42 | |
| 47 | 43 | it("prefers codex id when model differs", () => { |
| 48 | expect(normalizeCodexModelSlug("gpt-5.3", "gpt-5.3-codex")).toBe( | |
| 49 | "gpt-5.3-codex", | |
| 50 | ); | |
| 44 | expect(normalizeCodexModelSlug("gpt-5.3", "gpt-5.3-codex")).toBe("gpt-5.3-codex"); | |
| 51 | 45 | }); |
| 52 | 46 | |
| 53 | 47 | it("keeps non-aliased models as-is", () => { |
apps/desktop/src/codexAppServerManager.ts+24-89| ... | ... | @@ -91,9 +91,7 @@ export function normalizeCodexModelSlug( |
| 91 | 91 | return normalized; |
| 92 | 92 | } |
| 93 | 93 | |
| 94 | export function classifyCodexStderrLine( | |
| 95 | rawLine: string, | |
| 96 | ): { message: string } | null { | |
| 94 | export function classifyCodexStderrLine(rawLine: string): { message: string } | null { | |
| 97 | 95 | const line = rawLine.replaceAll(ANSI_ESCAPE_REGEX, "").trim(); |
| 98 | 96 | if (!line) { |
| 99 | 97 | return null; |
| ... | ... | @@ -106,9 +104,7 @@ export function classifyCodexStderrLine( |
| 106 | 104 | return null; |
| 107 | 105 | } |
| 108 | 106 | |
| 109 | const isBenignError = BENIGN_ERROR_LOG_SNIPPETS.some((snippet) => | |
| 110 | line.includes(snippet), | |
| 111 | ); | |
| 107 | const isBenignError = BENIGN_ERROR_LOG_SNIPPETS.some((snippet) => line.includes(snippet)); | |
| 112 | 108 | if (isBenignError) { |
| 113 | 109 | return null; |
| 114 | 110 | } |
| ... | ... | @@ -124,9 +120,7 @@ export interface CodexAppServerManagerEvents { |
| 124 | 120 | export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEvents> { |
| 125 | 121 | private readonly sessions = new Map<string, CodexSessionContext>(); |
| 126 | 122 | |
| 127 | async startSession( | |
| 128 | input: ProviderSessionStartInput, | |
| 129 | ): Promise<ProviderSession> { | |
| 123 | async startSession(input: ProviderSessionStartInput): Promise<ProviderSession> { | |
| 130 | 124 | const sessionId = randomUUID(); |
| 131 | 125 | const now = new Date().toISOString(); |
| 132 | 126 | |
| ... | ... | @@ -160,11 +154,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 160 | 154 | this.sessions.set(sessionId, context); |
| 161 | 155 | this.attachProcessListeners(context); |
| 162 | 156 | |
| 163 | this.emitLifecycleEvent( | |
| 164 | context, | |
| 165 | "session/connecting", | |
| 166 | "Starting codex app-server", | |
| 167 | ); | |
| 157 | this.emitLifecycleEvent(context, "session/connecting", "Starting codex app-server"); | |
| 168 | 158 | |
| 169 | 159 | try { |
| 170 | 160 | await this.sendRequest(context, "initialize", { |
| ... | ... | @@ -188,10 +178,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 188 | 178 | experimentalRawEvents: false, |
| 189 | 179 | }); |
| 190 | 180 | |
| 191 | const threadId = this.readString( | |
| 192 | this.readObject(threadStart)?.thread, | |
| 193 | "id", | |
| 194 | ); | |
| 181 | const threadId = this.readString(this.readObject(threadStart)?.thread, "id"); | |
| 195 | 182 | if (!threadId) { |
| 196 | 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 | 187 | status: "ready", |
| 201 | 188 | threadId, |
| 202 | 189 | }); |
| 203 | this.emitLifecycleEvent( | |
| 204 | context, | |
| 205 | "session/ready", | |
| 206 | `Connected to thread ${threadId}`, | |
| 207 | ); | |
| 190 | this.emitLifecycleEvent(context, "session/ready", `Connected to thread ${threadId}`); | |
| 208 | 191 | return { ...context.session }; |
| 209 | 192 | } catch (error) { |
| 210 | const message = | |
| 211 | error instanceof Error | |
| 212 | ? error.message | |
| 213 | : "Failed to start Codex session."; | |
| 193 | const message = error instanceof Error ? error.message : "Failed to start Codex session."; | |
| 214 | 194 | this.updateSession(context, { |
| 215 | 195 | status: "error", |
| 216 | 196 | lastError: message, |
| 217 | 197 | }); |
| 218 | 198 | this.emitErrorEvent(context, "session/startFailed", message); |
| 219 | 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( | |
| 225 | input: ProviderSendTurnInput, | |
| 226 | ): Promise<ProviderTurnStartResult> { | |
| 204 | async sendTurn(input: ProviderSendTurnInput): Promise<ProviderTurnStartResult> { | |
| 227 | 205 | const context = this.requireSession(input.sessionId); |
| 228 | 206 | if (!context.session.threadId) { |
| 229 | 207 | throw new Error("Session is missing a thread id."); |
| ... | ... | @@ -252,11 +230,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 252 | 230 | turnStartParams.effort = input.effort; |
| 253 | 231 | } |
| 254 | 232 | |
| 255 | const response = await this.sendRequest( | |
| 256 | context, | |
| 257 | "turn/start", | |
| 258 | turnStartParams, | |
| 259 | ); | |
| 233 | const response = await this.sendRequest(context, "turn/start", turnStartParams); | |
| 260 | 234 | |
| 261 | 235 | const turn = this.readObject(this.readObject(response), "turn"); |
| 262 | 236 | const turnId = this.readString(turn, "id"); |
| ... | ... | @@ -498,10 +472,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 498 | 472 | }); |
| 499 | 473 | |
| 500 | 474 | if (notification.method === "thread/started") { |
| 501 | const threadId = this.readString( | |
| 502 | this.readObject(notification.params)?.thread, | |
| 503 | "id", | |
| 504 | ); | |
| 475 | const threadId = this.readString(this.readObject(notification.params)?.thread, "id"); | |
| 505 | 476 | if (threadId) { |
| 506 | 477 | this.updateSession(context, { threadId }); |
| 507 | 478 | } |
| ... | ... | @@ -509,10 +480,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 509 | 480 | } |
| 510 | 481 | |
| 511 | 482 | if (notification.method === "turn/started") { |
| 512 | const turnId = this.readString( | |
| 513 | this.readObject(notification.params)?.turn, | |
| 514 | "id", | |
| 515 | ); | |
| 483 | const turnId = this.readString(this.readObject(notification.params)?.turn, "id"); | |
| 516 | 484 | this.updateSession(context, { |
| 517 | 485 | status: "running", |
| 518 | 486 | activeTurnId: turnId, |
| ... | ... | @@ -523,10 +491,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 523 | 491 | if (notification.method === "turn/completed") { |
| 524 | 492 | const turn = this.readObject(notification.params, "turn"); |
| 525 | 493 | const status = this.readString(turn, "status"); |
| 526 | const errorMessage = this.readString( | |
| 527 | this.readObject(turn, "error"), | |
| 528 | "message", | |
| 529 | ); | |
| 494 | const errorMessage = this.readString(this.readObject(turn, "error"), "message"); | |
| 530 | 495 | this.updateSession(context, { |
| 531 | 496 | status: status === "failed" ? "error" : "ready", |
| 532 | 497 | activeTurnId: undefined, |
| ... | ... | @@ -536,10 +501,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 536 | 501 | } |
| 537 | 502 | |
| 538 | 503 | if (notification.method === "error") { |
| 539 | const message = this.readString( | |
| 540 | this.readObject(notification.params)?.error, | |
| 541 | "message", | |
| 542 | ); | |
| 504 | const message = this.readString(this.readObject(notification.params)?.error, "message"); | |
| 543 | 505 | const willRetry = this.readBoolean(notification.params, "willRetry"); |
| 544 | 506 | |
| 545 | 507 | this.updateSession(context, { |
| ... | ... | @@ -549,10 +511,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 549 | 511 | } |
| 550 | 512 | } |
| 551 | 513 | |
| 552 | private handleServerRequest( | |
| 553 | context: CodexSessionContext, | |
| 554 | request: JsonRpcRequest, | |
| 555 | ): void { | |
| 514 | private handleServerRequest(context: CodexSessionContext, request: JsonRpcRequest): void { | |
| 556 | 515 | const route = this.readRouteFields(request.params); |
| 557 | 516 | const requestKind = this.requestKindForMethod(request.method); |
| 558 | 517 | let requestId: string | undefined; |
| ... | ... | @@ -609,10 +568,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 609 | 568 | }); |
| 610 | 569 | } |
| 611 | 570 | |
| 612 | private handleResponse( | |
| 613 | context: CodexSessionContext, | |
| 614 | response: JsonRpcResponse, | |
| 615 | ): void { | |
| 571 | private handleResponse(context: CodexSessionContext, response: JsonRpcResponse): void { | |
| 616 | 572 | const key = String(response.id); |
| 617 | 573 | const pending = context.pending.get(key); |
| 618 | 574 | if (!pending) { |
| ... | ... | @@ -623,11 +579,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 623 | 579 | context.pending.delete(key); |
| 624 | 580 | |
| 625 | 581 | if (response.error?.message) { |
| 626 | pending.reject( | |
| 627 | new Error( | |
| 628 | `${pending.method} failed: ${String(response.error.message)}`, | |
| 629 | ), | |
| 630 | ); | |
| 582 | pending.reject(new Error(`${pending.method} failed: ${String(response.error.message)}`)); | |
| 631 | 583 | return; |
| 632 | 584 | } |
| 633 | 585 | |
| ... | ... | @@ -674,11 +626,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 674 | 626 | context.child.stdin.write(`${encoded}\n`); |
| 675 | 627 | } |
| 676 | 628 | |
| 677 | private emitLifecycleEvent( | |
| 678 | context: CodexSessionContext, | |
| 679 | method: string, | |
| 680 | message: string, | |
| 681 | ): void { | |
| 629 | private emitLifecycleEvent(context: CodexSessionContext, method: string, message: string): void { | |
| 682 | 630 | this.emitEvent({ |
| 683 | 631 | id: randomUUID(), |
| 684 | 632 | kind: "session", |
| ... | ... | @@ -690,11 +638,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 690 | 638 | }); |
| 691 | 639 | } |
| 692 | 640 | |
| 693 | private emitErrorEvent( | |
| 694 | context: CodexSessionContext, | |
| 695 | method: string, | |
| 696 | message: string, | |
| 697 | ): void { | |
| 641 | private emitErrorEvent(context: CodexSessionContext, method: string, message: string): void { | |
| 698 | 642 | this.emitEvent({ |
| 699 | 643 | id: randomUUID(), |
| 700 | 644 | kind: "error", |
| ... | ... | @@ -710,10 +654,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 710 | 654 | this.emit("event", event); |
| 711 | 655 | } |
| 712 | 656 | |
| 713 | private updateSession( | |
| 714 | context: CodexSessionContext, | |
| 715 | updates: Partial<ProviderSession>, | |
| 716 | ): void { | |
| 657 | private updateSession(context: CodexSessionContext, updates: Partial<ProviderSession>): void { | |
| 717 | 658 | context.session = { |
| 718 | 659 | ...context.session, |
| 719 | 660 | ...updates, |
| ... | ... | @@ -762,8 +703,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 762 | 703 | } |
| 763 | 704 | |
| 764 | 705 | const candidate = value as Record<string, unknown>; |
| 765 | const hasId = | |
| 766 | typeof candidate.id === "string" || typeof candidate.id === "number"; | |
| 706 | const hasId = typeof candidate.id === "string" || typeof candidate.id === "number"; | |
| 767 | 707 | const hasMethod = typeof candidate.method === "string"; |
| 768 | 708 | return hasId && !hasMethod; |
| 769 | 709 | } |
| ... | ... | @@ -783,11 +723,9 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 783 | 723 | this.readString(params, "threadId") ?? |
| 784 | 724 | this.readString(this.readObject(params, "thread"), "id"); |
| 785 | 725 | const turnId = |
| 786 | this.readString(params, "turnId") ?? | |
| 787 | this.readString(this.readObject(params, "turn"), "id"); | |
| 726 | this.readString(params, "turnId") ?? this.readString(this.readObject(params, "turn"), "id"); | |
| 788 | 727 | const itemId = |
| 789 | this.readString(params, "itemId") ?? | |
| 790 | this.readString(this.readObject(params, "item"), "id"); | |
| 728 | this.readString(params, "itemId") ?? this.readString(this.readObject(params, "item"), "id"); | |
| 791 | 729 | |
| 792 | 730 | if (threadId) { |
| 793 | 731 | route.threadId = threadId; |
| ... | ... | @@ -804,10 +742,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 804 | 742 | return route; |
| 805 | 743 | } |
| 806 | 744 | |
| 807 | private readObject( | |
| 808 | value: unknown, | |
| 809 | key?: string, | |
| 810 | ): Record<string, unknown> | undefined { | |
| 745 | private readObject(value: unknown, key?: string): Record<string, unknown> | undefined { | |
| 811 | 746 | const target = |
| 812 | 747 | key === undefined |
| 813 | 748 | ? value |
apps/desktop/src/ipcHelpers.test.ts+3-5| ... | ... | @@ -55,11 +55,9 @@ describe("withParsedPayload", () => { |
| 55 | 55 | |
| 56 | 56 | describe("withParsedArgs", () => { |
| 57 | 57 | it("parses tuple arguments before invoking handler", () => { |
| 58 | const handler = vi.fn( | |
| 59 | (_event: unknown, sessionId: string, data: string) => { | |
| 60 | return `${sessionId}:${data}`; | |
| 61 | }, | |
| 62 | ); | |
| 58 | const handler = vi.fn((_event: unknown, sessionId: string, data: string) => { | |
| 59 | return `${sessionId}:${data}`; | |
| 60 | }); | |
| 63 | 61 | const wrapped = withParsedArgs( |
| 64 | 62 | { |
| 65 | 63 | parse(args: unknown[]): [string, string] { |
apps/desktop/src/main.ts+12-26| ... | ... | @@ -105,8 +105,7 @@ function registerIpcHandlers(): void { |
| 105 | 105 | ); |
| 106 | 106 | |
| 107 | 107 | ipcMain.handle(IPC_CHANNELS.dialogPickFolder, async () => { |
| 108 | const owner = | |
| 109 | BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]; | |
| 108 | const owner = BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]; | |
| 110 | 109 | const result = owner |
| 111 | 110 | ? await dialog.showOpenDialog(owner, { |
| 112 | 111 | properties: ["openDirectory", "createDirectory"], |
| ... | ... | @@ -175,12 +174,9 @@ function registerIpcHandlers(): void { |
| 175 | 174 | // Provider handlers |
| 176 | 175 | ipcMain.handle( |
| 177 | 176 | IPC_CHANNELS.providerSessionStart, |
| 178 | withParsedPayload( | |
| 179 | providerSessionStartInputSchema, | |
| 180 | async (_event, payload) => { | |
| 181 | return providerManager.startSession(payload); | |
| 182 | }, | |
| 183 | ), | |
| 177 | withParsedPayload(providerSessionStartInputSchema, async (_event, payload) => { | |
| 178 | return providerManager.startSession(payload); | |
| 179 | }), | |
| 184 | 180 | ); |
| 185 | 181 | |
| 186 | 182 | ipcMain.handle( |
| ... | ... | @@ -192,12 +188,9 @@ function registerIpcHandlers(): void { |
| 192 | 188 | |
| 193 | 189 | ipcMain.handle( |
| 194 | 190 | IPC_CHANNELS.providerTurnInterrupt, |
| 195 | withParsedPayload( | |
| 196 | providerInterruptTurnInputSchema, | |
| 197 | async (_event, payload) => { | |
| 198 | await providerManager.interruptTurn(payload); | |
| 199 | }, | |
| 200 | ), | |
| 191 | withParsedPayload(providerInterruptTurnInputSchema, async (_event, payload) => { | |
| 192 | await providerManager.interruptTurn(payload); | |
| 193 | }), | |
| 201 | 194 | ); |
| 202 | 195 | |
| 203 | 196 | ipcMain.handle( |
| ... | ... | @@ -212,12 +205,9 @@ function registerIpcHandlers(): void { |
| 212 | 205 | |
| 213 | 206 | ipcMain.handle( |
| 214 | 207 | IPC_CHANNELS.providerSessionStop, |
| 215 | withParsedPayload( | |
| 216 | providerStopSessionInputSchema, | |
| 217 | async (_event, payload) => { | |
| 218 | providerManager.stopSession(payload); | |
| 219 | }, | |
| 220 | ), | |
| 208 | withParsedPayload(providerStopSessionInputSchema, async (_event, payload) => { | |
| 209 | providerManager.stopSession(payload); | |
| 210 | }), | |
| 221 | 211 | ); |
| 222 | 212 | |
| 223 | 213 | ipcMain.handle(IPC_CHANNELS.providerSessionList, async () => { |
| ... | ... | @@ -225,18 +215,14 @@ function registerIpcHandlers(): void { |
| 225 | 215 | }); |
| 226 | 216 | } |
| 227 | 217 | |
| 228 | async function runTerminalCommand( | |
| 229 | input: TerminalCommandInput, | |
| 230 | ): Promise<TerminalCommandResult> { | |
| 218 | async function runTerminalCommand(input: TerminalCommandInput): Promise<TerminalCommandResult> { | |
| 231 | 219 | const shellPath = |
| 232 | 220 | process.platform === "win32" |
| 233 | 221 | ? (process.env.ComSpec ?? "cmd.exe") |
| 234 | 222 | : (process.env.SHELL ?? "/bin/sh"); |
| 235 | 223 | |
| 236 | 224 | const args = |
| 237 | process.platform === "win32" | |
| 238 | ? ["/d", "/s", "/c", input.command] | |
| 239 | : ["-lc", input.command]; | |
| 225 | process.platform === "win32" ? ["/d", "/s", "/c", input.command] : ["-lc", input.command]; | |
| 240 | 226 | |
| 241 | 227 | return new Promise((resolve, reject) => { |
| 242 | 228 | const child = spawn(shellPath, args, { |
apps/desktop/src/preload.ts+8-16| ... | ... | @@ -18,14 +18,12 @@ const nativeApi: NativeApi = { |
| 18 | 18 | agent: { |
| 19 | 19 | spawn: (config) => ipcRenderer.invoke(IPC_CHANNELS.agentSpawn, config), |
| 20 | 20 | kill: (sessionId) => ipcRenderer.invoke(IPC_CHANNELS.agentKill, sessionId), |
| 21 | write: (sessionId, data) => | |
| 22 | ipcRenderer.invoke(IPC_CHANNELS.agentWrite, sessionId, data), | |
| 21 | write: (sessionId, data) => ipcRenderer.invoke(IPC_CHANNELS.agentWrite, sessionId, data), | |
| 23 | 22 | onOutput: (callback) => { |
| 24 | 23 | const listener = (_event: Electron.IpcRendererEvent, chunk: unknown) => |
| 25 | 24 | callback(chunk as Parameters<typeof callback>[0]); |
| 26 | 25 | ipcRenderer.on(IPC_CHANNELS.agentOutput, listener); |
| 27 | return () => | |
| 28 | ipcRenderer.removeListener(IPC_CHANNELS.agentOutput, listener); | |
| 26 | return () => ipcRenderer.removeListener(IPC_CHANNELS.agentOutput, listener); | |
| 29 | 27 | }, |
| 30 | 28 | onExit: (callback) => { |
| 31 | 29 | const listener = (_event: Electron.IpcRendererEvent, exit: unknown) => |
| ... | ... | @@ -35,23 +33,17 @@ const nativeApi: NativeApi = { |
| 35 | 33 | }, |
| 36 | 34 | }, |
| 37 | 35 | providers: { |
| 38 | startSession: (input) => | |
| 39 | ipcRenderer.invoke(IPC_CHANNELS.providerSessionStart, input), | |
| 40 | sendTurn: (input) => | |
| 41 | ipcRenderer.invoke(IPC_CHANNELS.providerTurnStart, input), | |
| 42 | interruptTurn: (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), | |
| 36 | startSession: (input) => ipcRenderer.invoke(IPC_CHANNELS.providerSessionStart, input), | |
| 37 | sendTurn: (input) => ipcRenderer.invoke(IPC_CHANNELS.providerTurnStart, input), | |
| 38 | interruptTurn: (input) => ipcRenderer.invoke(IPC_CHANNELS.providerTurnInterrupt, input), | |
| 39 | respondToRequest: (input) => ipcRenderer.invoke(IPC_CHANNELS.providerRequestRespond, input), | |
| 40 | stopSession: (input) => ipcRenderer.invoke(IPC_CHANNELS.providerSessionStop, input), | |
| 48 | 41 | listSessions: () => ipcRenderer.invoke(IPC_CHANNELS.providerSessionList), |
| 49 | 42 | onEvent: (callback) => { |
| 50 | 43 | const listener = (_event: Electron.IpcRendererEvent, payload: unknown) => |
| 51 | 44 | callback(payload as Parameters<typeof callback>[0]); |
| 52 | 45 | ipcRenderer.on(IPC_CHANNELS.providerEvent, listener); |
| 53 | return () => | |
| 54 | ipcRenderer.removeListener(IPC_CHANNELS.providerEvent, listener); | |
| 46 | return () => ipcRenderer.removeListener(IPC_CHANNELS.providerEvent, listener); | |
| 55 | 47 | }, |
| 56 | 48 | }, |
| 57 | 49 | shell: { |
apps/desktop/src/processManager.ts+1-4| ... | ... | @@ -68,7 +68,6 @@ export class ProcessManager extends EventEmitter<ProcessManagerEvents> { |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | 70 | private spawnPty(sessionId: string, config: AgentConfig): string { |
| 71 | // eslint-disable-next-line @typescript-eslint/no-var-requires | |
| 72 | 71 | const pty = require("node-pty") as typeof import("node-pty"); |
| 73 | 72 | |
| 74 | 73 | const ptyProcess = pty.spawn(config.command, config.args, { |
| ... | ... | @@ -76,9 +75,7 @@ export class ProcessManager extends EventEmitter<ProcessManagerEvents> { |
| 76 | 75 | cols: 120, |
| 77 | 76 | rows: 30, |
| 78 | 77 | cwd: config.cwd ?? process.cwd(), |
| 79 | env: (config.env | |
| 80 | ? { ...process.env, ...config.env } | |
| 81 | : process.env) as Record<string, string>, | |
| 78 | env: (config.env ? { ...process.env, ...config.env } : process.env) as Record<string, string>, | |
| 82 | 79 | }); |
| 83 | 80 | |
| 84 | 81 | this.ptySessions.set(sessionId, ptyProcess); |
apps/desktop/src/todoStore.ts+1-5| ... | ... | @@ -82,11 +82,7 @@ export class TodoStore { |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | 84 | private async persist(): Promise<void> { |
| 85 | await fs.writeFile( | |
| 86 | this.filePath, | |
| 87 | JSON.stringify(this.todos, null, 2), | |
| 88 | "utf8", | |
| 89 | ); | |
| 85 | await fs.writeFile(this.filePath, JSON.stringify(this.todos, null, 2), "utf8"); | |
| 90 | 86 | } |
| 91 | 87 | |
| 92 | 88 | private async runExclusive<T>(operation: () => Promise<T>): Promise<T> { |
apps/desktop/tsconfig.json+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | "compilerOptions": { |
| 4 | 4 | "composite": true, |
| 5 | 5 | "types": ["node", "electron"], |
| 6 | "lib": ["ES2022", "DOM"] | |
| 6 | "lib": ["ES2023", "DOM"] | |
| 7 | 7 | }, |
| 8 | 8 | "include": ["src", "tsup.config.ts"] |
| 9 | 9 | } |
apps/renderer/package.json+1-2| ... | ... | @@ -8,15 +8,14 @@ |
| 8 | 8 | "build": "vite build", |
| 9 | 9 | "preview": "vite preview", |
| 10 | 10 | "typecheck": "tsc --noEmit", |
| 11 | "lint": "biome check src/", | |
| 12 | 11 | "test": "vitest run --passWithNoTests" |
| 13 | 12 | }, |
| 14 | 13 | "dependencies": { |
| 15 | 14 | "@acme/contracts": "workspace:*", |
| 16 | 15 | "highlight.js": "^11.11.1", |
| 17 | "react-markdown": "^10.1.0", | |
| 18 | 16 | "react": "^19.0.0", |
| 19 | 17 | "react-dom": "^19.0.0", |
| 18 | "react-markdown": "^10.1.0", | |
| 20 | 19 | "rehype-highlight": "^7.0.2", |
| 21 | 20 | "remark-gfm": "^4.0.1", |
| 22 | 21 | "zod": "^3.24.1" |
apps/renderer/src/components/ChatMarkdown.tsx+1-3| ... | ... | @@ -18,9 +18,7 @@ export default function ChatMarkdown({ text }: ChatMarkdownProps) { |
| 18 | 18 | <div className="chat-markdown text-sm leading-relaxed text-[#d0d0d0]"> |
| 19 | 19 | <ReactMarkdown |
| 20 | 20 | remarkPlugins={[remarkGfm]} |
| 21 | rehypePlugins={[ | |
| 22 | [rehypeHighlight, { detect: true, ignoreMissing: true }], | |
| 23 | ]} | |
| 21 | rehypePlugins={[[rehypeHighlight, { detect: true, ignoreMissing: true }]]} | |
| 24 | 22 | components={markdownComponents} |
| 25 | 23 | > |
| 26 | 24 | {text} |
apps/renderer/src/components/ChatView.tsx+20-50| ... | ... | @@ -144,9 +144,7 @@ export default function ChatView() { |
| 144 | 144 | const editorMenuRef = useRef<HTMLDivElement>(null); |
| 145 | 145 | |
| 146 | 146 | const activeThread = state.threads.find((t) => t.id === state.activeThreadId); |
| 147 | const activeProject = state.projects.find( | |
| 148 | (p) => p.id === activeThread?.projectId, | |
| 149 | ); | |
| 147 | const activeProject = state.projects.find((p) => p.id === activeThread?.projectId); | |
| 150 | 148 | const selectedModel = resolveModelSlug( |
| 151 | 149 | activeThread?.model ?? activeProject?.model ?? DEFAULT_MODEL, |
| 152 | 150 | ); |
| ... | ... | @@ -164,7 +162,7 @@ export default function ChatView() { |
| 164 | 162 | ); |
| 165 | 163 | const assistantCompletionByItemId = useMemo(() => { |
| 166 | 164 | const map = new Map<string, string>(); |
| 167 | const ordered = [...(activeThread?.events ?? [])].reverse(); | |
| 165 | const ordered = [...(activeThread?.events ?? [])].toReversed(); | |
| 168 | 166 | for (const event of ordered) { |
| 169 | 167 | if (event.method !== "item/completed") continue; |
| 170 | 168 | if (!event.itemId) continue; |
| ... | ... | @@ -264,18 +262,15 @@ export default function ChatView() { |
| 264 | 262 | // Auto-scroll on new messages |
| 265 | 263 | const messageCount = activeThread?.messages.length ?? 0; |
| 266 | 264 | const workLogCount = workLogEntries.length; |
| 267 | // biome-ignore lint/correctness/useExhaustiveDependencies: trigger on message count change | |
| 268 | 265 | useEffect(() => { |
| 269 | 266 | messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); |
| 270 | 267 | }, [messageCount]); |
| 271 | // biome-ignore lint/correctness/useExhaustiveDependencies: auto-scroll while active work-log events stream in | |
| 272 | 268 | useEffect(() => { |
| 273 | 269 | if (phase !== "running") return; |
| 274 | 270 | messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); |
| 275 | 271 | }, [phase, workLogCount]); |
| 276 | 272 | |
| 277 | 273 | // Auto-resize textarea |
| 278 | // biome-ignore lint/correctness/useExhaustiveDependencies: trigger on prompt change | |
| 279 | 274 | useEffect(() => { |
| 280 | 275 | const ta = textareaRef.current; |
| 281 | 276 | if (!ta) return; |
| ... | ... | @@ -298,10 +293,7 @@ export default function ChatView() { |
| 298 | 293 | |
| 299 | 294 | const handleClickOutside = (event: MouseEvent) => { |
| 300 | 295 | if (!modelMenuRef.current) return; |
| 301 | if ( | |
| 302 | event.target instanceof Node && | |
| 303 | !modelMenuRef.current.contains(event.target) | |
| 304 | ) { | |
| 296 | if (event.target instanceof Node && !modelMenuRef.current.contains(event.target)) { | |
| 305 | 297 | setIsModelMenuOpen(false); |
| 306 | 298 | } |
| 307 | 299 | }; |
| ... | ... | @@ -394,8 +386,7 @@ export default function ChatView() { |
| 394 | 386 | |
| 395 | 387 | // Auto-title from first message |
| 396 | 388 | if (activeThread.messages.length === 0) { |
| 397 | const title = | |
| 398 | trimmed.length > 50 ? `${trimmed.slice(0, 50)}...` : trimmed; | |
| 389 | const title = trimmed.length > 50 ? `${trimmed.slice(0, 50)}...` : trimmed; | |
| 399 | 390 | dispatch({ |
| 400 | 391 | type: "SET_THREAD_TITLE", |
| 401 | 392 | threadId: activeThread.id, |
| ... | ... | @@ -501,9 +492,7 @@ export default function ChatView() { |
| 501 | 492 | <div className="drag-region h-[52px] shrink-0" /> |
| 502 | 493 | <div className="flex flex-1 items-center justify-center"> |
| 503 | 494 | <div className="text-center"> |
| 504 | <p className="text-sm"> | |
| 505 | Select a thread or create a new one to get started. | |
| 506 | </p> | |
| 495 | <p className="text-sm">Select a thread or create a new one to get started.</p> | |
| 507 | 496 | </div> |
| 508 | 497 | </div> |
| 509 | 498 | </div> |
| ... | ... | @@ -513,7 +502,7 @@ export default function ChatView() { |
| 513 | 502 | return ( |
| 514 | 503 | <div className="flex flex-1 flex-col bg-[#0c0c0c]"> |
| 515 | 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 | 506 | <div className="flex items-center gap-3"> |
| 518 | 507 | <h2 className="text-sm font-medium text-[#e0e0e0]"> |
| 519 | 508 | {activeThread.title} |
| ... | ... | @@ -654,9 +643,7 @@ export default function ChatView() { |
| 654 | 643 | <div className="flex-1 overflow-y-auto px-5 py-4"> |
| 655 | 644 | {activeThread.messages.length === 0 && !isWorking ? ( |
| 656 | 645 | <div className="flex h-full items-center justify-center"> |
| 657 | <p className="text-sm text-[#a0a0a0]/30"> | |
| 658 | Send a message to start the conversation. | |
| 659 | </p> | |
| 646 | <p className="text-sm text-[#a0a0a0]/30">Send a message to start the conversation.</p> | |
| 660 | 647 | </div> |
| 661 | 648 | ) : ( |
| 662 | 649 | <div className="mx-auto max-w-3xl space-y-4"> |
| ... | ... | @@ -770,7 +757,7 @@ export default function ChatView() { |
| 770 | 757 | {/* Input bar */} |
| 771 | 758 | <div className="px-5 pb-4 pt-2"> |
| 772 | 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 | 761 | {/* Textarea area */} |
| 775 | 762 | <div className="px-4 pt-4 pb-2"> |
| 776 | 763 | <textarea |
| ... | ... | @@ -781,9 +768,7 @@ export default function ChatView() { |
| 781 | 768 | onChange={(e) => setPrompt(e.target.value)} |
| 782 | 769 | onKeyDown={onKeyDown} |
| 783 | 770 | placeholder={ |
| 784 | phase === "disconnected" | |
| 785 | ? "Ask for follow-up changes" | |
| 786 | : "Ask anything..." | |
| 771 | phase === "disconnected" ? "Ask for follow-up changes" : "Ask anything..." | |
| 787 | 772 | } |
| 788 | 773 | disabled={isSending || isConnecting} |
| 789 | 774 | /> |
| ... | ... | @@ -796,12 +781,10 @@ export default function ChatView() { |
| 796 | 781 | <div className="relative" ref={modelMenuRef}> |
| 797 | 782 | <button |
| 798 | 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 | 785 | onClick={() => setIsModelMenuOpen((open) => !open)} |
| 801 | 786 | > |
| 802 | <span className="max-w-[180px] truncate"> | |
| 803 | {selectedModel} | |
| 804 | </span> | |
| 787 | <span className="max-w-[180px] truncate">{selectedModel}</span> | |
| 805 | 788 | <svg |
| 806 | 789 | width="10" |
| 807 | 790 | height="10" |
| ... | ... | @@ -820,10 +803,8 @@ export default function ChatView() { |
| 820 | 803 | </svg> |
| 821 | 804 | </button> |
| 822 | 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"> | |
| 824 | <p className="px-2 py-1 text-[11px] text-[#a0a0a0]/70"> | |
| 825 | Select model | |
| 826 | </p> | |
| 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"> | |
| 807 | <p className="px-2 py-1 text-[11px] text-[#a0a0a0]/70">Select model</p> | |
| 827 | 808 | <div className="max-h-72 overflow-y-auto"> |
| 828 | 809 | {modelOptions.map((model) => { |
| 829 | 810 | const isSelected = model === selectedModel; |
| ... | ... | @@ -833,8 +814,8 @@ export default function ChatView() { |
| 833 | 814 | type="button" |
| 834 | 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 | 816 | isSelected |
| 836 | ? "bg-white/[0.08] text-white" | |
| 837 | : "text-[#d4d4d4] hover:bg-white/[0.05]" | |
| 817 | ? "bg-white/8 text-white" | |
| 818 | : "text-[#d4d4d4] hover:bg-white/5" | |
| 838 | 819 | }`} |
| 839 | 820 | onClick={() => onModelSelect(model)} |
| 840 | 821 | > |
| ... | ... | @@ -855,17 +836,14 @@ export default function ChatView() { |
| 855 | 836 | </div> |
| 856 | 837 | |
| 857 | 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 | 841 | {/* Reasoning effort */} |
| 861 | 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 | 844 | htmlFor="reasoning-effort" |
| 864 | 845 | > |
| 865 | <span> | |
| 866 | {selectedEffort.charAt(0).toUpperCase() + | |
| 867 | selectedEffort.slice(1)} | |
| 868 | </span> | |
| 846 | <span>{selectedEffort.charAt(0).toUpperCase() + selectedEffort.slice(1)}</span> | |
| 869 | 847 | <select |
| 870 | 848 | id="reasoning-effort" |
| 871 | 849 | className="absolute opacity-0 w-0 h-0" |
| ... | ... | @@ -873,11 +851,7 @@ export default function ChatView() { |
| 873 | 851 | onChange={(event) => setSelectedEffort(event.target.value)} |
| 874 | 852 | > |
| 875 | 853 | {REASONING_OPTIONS.map((effort) => ( |
| 876 | <option | |
| 877 | key={effort} | |
| 878 | value={effort} | |
| 879 | className="bg-[#1b1b1d]" | |
| 880 | > | |
| 854 | <option key={effort} value={effort} className="bg-[#1b1b1d]"> | |
| 881 | 855 | {effort} |
| 882 | 856 | {effort === DEFAULT_REASONING ? " (default)" : ""} |
| 883 | 857 | </option> |
| ... | ... | @@ -1004,11 +978,7 @@ export default function ChatView() { |
| 1004 | 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 | 979 | disabled={isSending || isConnecting || !prompt.trim()} |
| 1006 | 980 | aria-label={ |
| 1007 | isConnecting | |
| 1008 | ? "Connecting" | |
| 1009 | : isSending | |
| 1010 | ? "Sending" | |
| 1011 | : "Send message" | |
| 981 | isConnecting ? "Connecting" : isSending ? "Sending" : "Send message" | |
| 1012 | 982 | } |
| 1013 | 983 | > |
| 1014 | 984 | {isConnecting || isSending ? ( |
apps/renderer/src/components/DiffPanel.tsx+4-12| ... | ... | @@ -7,9 +7,7 @@ export default function DiffPanel() { |
| 7 | 7 | <aside className="flex h-full w-[400px] shrink-0 flex-col border-l border-white/[0.08] bg-[#141414]"> |
| 8 | 8 | {/* Header */} |
| 9 | 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]"> | |
| 11 | Uncommitted changes | |
| 12 | </h3> | |
| 10 | <h3 className="text-xs font-medium text-[#e0e0e0]">Uncommitted changes</h3> | |
| 13 | 11 | <button |
| 14 | 12 | type="button" |
| 15 | 13 | className="text-[#a0a0a0]/40 transition-colors duration-150 hover:text-[#a0a0a0]/70" |
| ... | ... | @@ -27,15 +25,11 @@ export default function DiffPanel() { |
| 27 | 25 | <span className="rounded bg-emerald-500/20 px-1.5 py-0.5 text-[9px] font-medium text-emerald-400"> |
| 28 | 26 | M |
| 29 | 27 | </span> |
| 30 | <span className="font-mono text-xs text-[#a0a0a0]/60"> | |
| 31 | src/App.tsx | |
| 32 | </span> | |
| 28 | <span className="font-mono text-xs text-[#a0a0a0]/60">src/App.tsx</span> | |
| 33 | 29 | </div> |
| 34 | 30 | <div className="space-y-0.5 font-mono text-[11px]"> |
| 35 | 31 | <div className="text-red-400/60">- const oldValue = "hello";</div> |
| 36 | <div className="text-emerald-400/60"> | |
| 37 | + const newValue = "world"; | |
| 38 | </div> | |
| 32 | <div className="text-emerald-400/60">+ const newValue = "world";</div> | |
| 39 | 33 | </div> |
| 40 | 34 | </div> |
| 41 | 35 | |
| ... | ... | @@ -44,9 +38,7 @@ export default function DiffPanel() { |
| 44 | 38 | <span className="rounded bg-blue-500/20 px-1.5 py-0.5 text-[9px] font-medium text-blue-400"> |
| 45 | 39 | A |
| 46 | 40 | </span> |
| 47 | <span className="font-mono text-xs text-[#a0a0a0]/60"> | |
| 48 | src/utils.ts | |
| 49 | </span> | |
| 41 | <span className="font-mono text-xs text-[#a0a0a0]/60">src/utils.ts</span> | |
| 50 | 42 | </div> |
| 51 | 43 | </div> |
| 52 | 44 | </div> |
apps/renderer/src/components/Sidebar.tsx+14-22| ... | ... | @@ -120,7 +120,7 @@ export default function Sidebar() { |
| 120 | 120 | }, [handleNewThread, state.activeThreadId, state.projects, state.threads]); |
| 121 | 121 | |
| 122 | 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 | 124 | {/* Drag region / traffic light space */} |
| 125 | 125 | <div className="drag-region h-[52px] shrink-0" /> |
| 126 | 126 | {/* Branding */} |
| ... | ... | @@ -128,16 +128,14 @@ export default function Sidebar() { |
| 128 | 128 | <div className="flex h-7 w-7 items-center justify-center rounded-lg bg-white text-xs font-bold text-[#0c0c0c]"> |
| 129 | 129 | CT |
| 130 | 130 | </div> |
| 131 | <span className="text-sm font-semibold tracking-tight text-[#e0e0e0]"> | |
| 132 | CodeThing | |
| 133 | </span> | |
| 131 | <span className="text-sm font-semibold tracking-tight text-[#e0e0e0]">CodeThing</span> | |
| 134 | 132 | </div> |
| 135 | 133 | |
| 136 | 134 | {/* New thread (global) */} |
| 137 | 135 | <div className="px-3 pb-3"> |
| 138 | 136 | <button |
| 139 | 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 | 139 | onClick={() => { |
| 142 | 140 | if (state.projects.length === 0) { |
| 143 | 141 | setAddingProject(true); |
| ... | ... | @@ -169,10 +167,8 @@ export default function Sidebar() { |
| 169 | 167 | {/* Project header */} |
| 170 | 168 | <button |
| 171 | 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]" | |
| 173 | onClick={() => | |
| 174 | dispatch({ type: "TOGGLE_PROJECT", projectId: project.id }) | |
| 175 | } | |
| 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" | |
| 171 | onClick={() => dispatch({ type: "TOGGLE_PROJECT", projectId: project.id })} | |
| 176 | 172 | > |
| 177 | 173 | <span className="text-[10px] text-[#a0a0a0]/50"> |
| 178 | 174 | {project.expanded ? "â–¼" : "â–¶"} |
| ... | ... | @@ -180,14 +176,12 @@ export default function Sidebar() { |
| 180 | 176 | <span className="flex-1 truncate text-xs font-medium text-[#e0e0e0]/90"> |
| 181 | 177 | {project.name} |
| 182 | 178 | </span> |
| 183 | <span className="text-[10px] text-[#a0a0a0]/40"> | |
| 184 | {threads.length} | |
| 185 | </span> | |
| 179 | <span className="text-[10px] text-[#a0a0a0]/40">{threads.length}</span> | |
| 186 | 180 | </button> |
| 187 | 181 | |
| 188 | 182 | {/* Threads */} |
| 189 | 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 | 185 | {threads.map((thread) => { |
| 192 | 186 | const isActive = state.activeThreadId === thread.id; |
| 193 | 187 | const threadStatus = threadStatusLabel( |
| ... | ... | @@ -198,9 +192,7 @@ export default function Sidebar() { |
| 198 | 192 | key={thread.id} |
| 199 | 193 | type="button" |
| 200 | 194 | className={`flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors duration-150 ${ |
| 201 | isActive | |
| 202 | ? "bg-white/10 text-white" | |
| 203 | : "text-[#a0a0a0]/70 hover:bg-white/[0.04]" | |
| 195 | isActive ? "bg-white/10 text-white" : "text-[#a0a0a0]/70 hover:bg-white/4" | |
| 204 | 196 | }`} |
| 205 | 197 | onClick={() => |
| 206 | 198 | dispatch({ |
| ... | ... | @@ -254,12 +246,12 @@ export default function Sidebar() { |
| 254 | 246 | |
| 255 | 247 | {/* Add project form */} |
| 256 | 248 | {addingProject ? ( |
| 257 | <div className="border-t border-white/[0.08] p-3"> | |
| 249 | <div className="border-t border-white/8 p-3"> | |
| 258 | 250 | <p className="mb-2 text-[10px] font-medium uppercase tracking-wider text-[#a0a0a0]/50"> |
| 259 | 251 | Add project |
| 260 | 252 | </p> |
| 261 | 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 | 255 | placeholder="/path/to/project" |
| 264 | 256 | value={newCwd} |
| 265 | 257 | onChange={(e) => setNewCwd(e.target.value)} |
| ... | ... | @@ -271,7 +263,7 @@ export default function Sidebar() { |
| 271 | 263 | {api && ( |
| 272 | 264 | <button |
| 273 | 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 | 267 | onClick={() => void handlePickFolder()} |
| 276 | 268 | disabled={isPickingFolder} |
| 277 | 269 | > |
| ... | ... | @@ -288,7 +280,7 @@ export default function Sidebar() { |
| 288 | 280 | </button> |
| 289 | 281 | <button |
| 290 | 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 | 284 | onClick={() => setAddingProject(false)} |
| 293 | 285 | > |
| 294 | 286 | Cancel |
| ... | ... | @@ -296,10 +288,10 @@ export default function Sidebar() { |
| 296 | 288 | </div> |
| 297 | 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 | 292 | <button |
| 301 | 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 | 295 | onClick={() => setAddingProject(true)} |
| 304 | 296 | > |
| 305 | 297 | + Add project |
apps/renderer/src/index.css+3-4| ... | ... | @@ -5,8 +5,8 @@ |
| 5 | 5 | } |
| 6 | 6 | |
| 7 | 7 | body { |
| 8 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, | |
| 9 | "Helvetica Neue", Arial, sans-serif; | |
| 8 | font-family: | |
| 9 | -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; | |
| 10 | 10 | margin: 0; |
| 11 | 11 | padding: 0; |
| 12 | 12 | min-height: 100vh; |
| ... | ... | @@ -29,8 +29,7 @@ pre, |
| 29 | 29 | code, |
| 30 | 30 | textarea, |
| 31 | 31 | input { |
| 32 | font-family: "SF Mono", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, | |
| 33 | monospace; | |
| 32 | font-family: "SF Mono", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; | |
| 34 | 33 | } |
| 35 | 34 | |
| 36 | 35 | /* Window drag region (frameless titlebar) */ |
apps/renderer/src/model-logic.test.ts+1-5| ... | ... | @@ -1,9 +1,5 @@ |
| 1 | 1 | import { describe, expect, it } from "vitest"; |
| 2 | import { | |
| 3 | DEFAULT_MODEL, | |
| 4 | normalizeModelSlug, | |
| 5 | resolveModelSlug, | |
| 6 | } from "./model-logic"; | |
| 2 | import { DEFAULT_MODEL, normalizeModelSlug, resolveModelSlug } from "./model-logic"; | |
| 7 | 3 | |
| 8 | 4 | describe("normalizeModelSlug", () => { |
| 9 | 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 | 69 | runtimeMode: RuntimeMode; |
| 70 | 70 | } |
| 71 | 71 | |
| 72 | function maybeMigrateLegacyModel( | |
| 73 | model: string, | |
| 74 | isLegacyPayload: boolean, | |
| 75 | ): string { | |
| 72 | function maybeMigrateLegacyModel(model: string, isLegacyPayload: boolean): string { | |
| 76 | 73 | if (!isLegacyPayload) { |
| 77 | 74 | return model; |
| 78 | 75 | } |
| ... | ... | @@ -86,9 +83,7 @@ function hydrateProject( |
| 86 | 83 | ): Project { |
| 87 | 84 | return { |
| 88 | 85 | ...project, |
| 89 | model: resolveModelSlug( | |
| 90 | maybeMigrateLegacyModel(project.model, isLegacyPayload), | |
| 91 | ), | |
| 86 | model: resolveModelSlug(maybeMigrateLegacyModel(project.model, isLegacyPayload)), | |
| 92 | 87 | }; |
| 93 | 88 | } |
| 94 | 89 | |
| ... | ... | @@ -100,9 +95,7 @@ function hydrateThread( |
| 100 | 95 | id: thread.id, |
| 101 | 96 | projectId: thread.projectId, |
| 102 | 97 | title: thread.title, |
| 103 | model: resolveModelSlug( | |
| 104 | maybeMigrateLegacyModel(thread.model, isLegacyPayload), | |
| 105 | ), | |
| 98 | model: resolveModelSlug(maybeMigrateLegacyModel(thread.model, isLegacyPayload)), | |
| 106 | 99 | session: null, |
| 107 | 100 | messages: thread.messages.map((message) => ({ |
| 108 | 101 | ...message, |
| ... | ... | @@ -139,7 +132,7 @@ export function hydratePersistedState( |
| 139 | 132 | .filter((thread) => projectIds.has(thread.projectId)); |
| 140 | 133 | const hasActiveThread = Boolean( |
| 141 | 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 | 138 | return { |
apps/renderer/src/session-logic.test.ts+2-7| ... | ... | @@ -22,9 +22,7 @@ function makeEvent(overrides: Partial<ProviderEvent>): ProviderEvent { |
| 22 | 22 | }; |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | function makeSession( | |
| 26 | overrides: Partial<ProviderSession> = {}, | |
| 27 | ): ProviderSession { | |
| 25 | function makeSession(overrides: Partial<ProviderSession> = {}): ProviderSession { | |
| 28 | 26 | return { |
| 29 | 27 | sessionId: "sess-1", |
| 30 | 28 | provider: "codex", |
| ... | ... | @@ -99,10 +97,7 @@ describe("deriveTimelineEntries", () => { |
| 99 | 97 | |
| 100 | 98 | const timeline = deriveTimelineEntries(messages, workEntries); |
| 101 | 99 | |
| 102 | expect(timeline.map((entry) => entry.id)).toEqual([ | |
| 103 | "work:w-1", | |
| 104 | "message:m-1", | |
| 105 | ]); | |
| 100 | expect(timeline.map((entry) => entry.id)).toEqual(["work:w-1", "message:m-1"]); | |
| 106 | 101 | }); |
| 107 | 102 | }); |
| 108 | 103 |
apps/renderer/src/session-logic.ts+5-17| ... | ... | @@ -1,8 +1,4 @@ |
| 1 | import type { | |
| 2 | ProviderEvent, | |
| 3 | ProviderKind, | |
| 4 | ProviderSession, | |
| 5 | } from "@acme/contracts"; | |
| 1 | import type { ProviderEvent, ProviderKind, ProviderSession } from "@acme/contracts"; | |
| 6 | 2 | import type { ChatMessage, SessionPhase } from "./types"; |
| 7 | 3 | |
| 8 | 4 | export const PROVIDER_OPTIONS: Array<{ |
| ... | ... | @@ -49,10 +45,7 @@ export function formatDuration(durationMs: number): string { |
| 49 | 45 | return `${minutes}m ${seconds}s`; |
| 50 | 46 | } |
| 51 | 47 | |
| 52 | export function formatElapsed( | |
| 53 | startIso: string, | |
| 54 | endIso: string | undefined, | |
| 55 | ): string | null { | |
| 48 | export function formatElapsed(startIso: string, endIso: string | undefined): string | null { | |
| 56 | 49 | if (!endIso) return null; |
| 57 | 50 | |
| 58 | 51 | const startedAt = Date.parse(startIso); |
| ... | ... | @@ -304,7 +297,7 @@ export function deriveWorkLogEntries( |
| 304 | 297 | events: ProviderEvent[], |
| 305 | 298 | turnId: string | undefined, |
| 306 | 299 | ): WorkLogEntry[] { |
| 307 | const ordered = [...events].reverse(); | |
| 300 | const ordered = [...events].toReversed(); | |
| 308 | 301 | const entries: WorkLogEntry[] = []; |
| 309 | 302 | const turnStartedAtIso = turnId |
| 310 | 303 | ? ordered.find((event) => { |
| ... | ... | @@ -312,9 +305,7 @@ export function deriveWorkLogEntries( |
| 312 | 305 | return eventTurnId(event) === turnId; |
| 313 | 306 | })?.createdAt |
| 314 | 307 | : undefined; |
| 315 | const turnStartedAt = turnStartedAtIso | |
| 316 | ? Date.parse(turnStartedAtIso) | |
| 317 | : Number.NaN; | |
| 308 | const turnStartedAt = turnStartedAtIso ? Date.parse(turnStartedAtIso) : Number.NaN; | |
| 318 | 309 | |
| 319 | 310 | for (const event of ordered) { |
| 320 | 311 | if (turnId) { |
| ... | ... | @@ -428,10 +419,7 @@ export function derivePhase(session: ProviderSession | null): SessionPhase { |
| 428 | 419 | return "ready"; |
| 429 | 420 | } |
| 430 | 421 | |
| 431 | export function evolveSession( | |
| 432 | previous: ProviderSession, | |
| 433 | event: ProviderEvent, | |
| 434 | ): ProviderSession { | |
| 422 | export function evolveSession(previous: ProviderSession, event: ProviderEvent): ProviderSession { | |
| 435 | 423 | const payload = asObject(event.payload); |
| 436 | 424 | |
| 437 | 425 | if (event.method === "thread/started") { |
apps/renderer/src/store.ts+2-9| ... | ... | @@ -116,10 +116,7 @@ function updateThread( |
| 116 | 116 | return threads.map((t) => (t.id === threadId ? updater(t) : t)); |
| 117 | 117 | } |
| 118 | 118 | |
| 119 | function findThreadBySessionId( | |
| 120 | threads: Thread[], | |
| 121 | sessionId: string, | |
| 122 | ): Thread | undefined { | |
| 119 | function findThreadBySessionId(threads: Thread[], sessionId: string): Thread | undefined { | |
| 123 | 120 | return threads.find((t) => t.session?.sessionId === sessionId); |
| 124 | 121 | } |
| 125 | 122 | |
| ... | ... | @@ -325,11 +322,7 @@ export function StoreProvider({ children }: { children: ReactNode }) { |
| 325 | 322 | persistState(state); |
| 326 | 323 | }, [state]); |
| 327 | 324 | |
| 328 | return createElement( | |
| 329 | StoreContext.Provider, | |
| 330 | { value: { state, dispatch } }, | |
| 331 | children, | |
| 332 | ); | |
| 325 | return createElement(StoreContext.Provider, { value: { state, dispatch } }, children); | |
| 333 | 326 | } |
| 334 | 327 | |
| 335 | 328 | export function useStore() { |
apps/renderer/tsconfig.json+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | "compilerOptions": { |
| 4 | 4 | "composite": true, |
| 5 | 5 | "jsx": "react-jsx", |
| 6 | "lib": ["ES2022", "DOM", "DOM.Iterable"], | |
| 6 | "lib": ["ES2023", "DOM", "DOM.Iterable"], | |
| 7 | 7 | "types": ["vite/client"] |
| 8 | 8 | }, |
| 9 | 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 | 5 | "": { |
| 6 | 6 | "name": "ct-round-5", |
| 7 | 7 | "devDependencies": { |
| 8 | "@biomejs/biome": "^1.9.0", | |
| 8 | "oxfmt": "^0.28.0", | |
| 9 | "oxlint": "^1.43.0", | |
| 9 | 10 | "turbo": "^2.3.3", |
| 10 | 11 | "typescript": "^5.7.3", |
| 11 | 12 | "vitest": "^3.0.0", |
| ... | ... | @@ -22,7 +23,6 @@ |
| 22 | 23 | "devDependencies": { |
| 23 | 24 | "@electron/rebuild": "^3.7.0", |
| 24 | 25 | "@types/node": "^22.10.2", |
| 25 | "concurrently": "^9.1.2", | |
| 26 | 26 | "electronmon": "^2.0.2", |
| 27 | 27 | "tsup": "^8.3.5", |
| 28 | 28 | "typescript": "^5.7.3", |
| ... | ... | @@ -110,24 +110,6 @@ |
| 110 | 110 | |
| 111 | 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 | 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 | 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 | 198 | |
| 217 | 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 | 233 | "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], |
| 220 | 234 | |
| 221 | 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 | 484 | |
| 471 | 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 | 487 | "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], |
| 476 | 488 | |
| 477 | 489 | "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], |
| ... | ... | @@ -904,6 +916,10 @@ |
| 904 | 916 | |
| 905 | 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 | 923 | "p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="], |
| 908 | 924 | |
| 909 | 925 | "p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="], |
| ... | ... | @@ -1014,8 +1030,6 @@ |
| 1014 | 1030 | |
| 1015 | 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 | 1033 | "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], |
| 1020 | 1034 | |
| 1021 | 1035 | "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], |
| ... | ... | @@ -1058,7 +1072,7 @@ |
| 1058 | 1072 | |
| 1059 | 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 | 1077 | "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], |
| 1064 | 1078 | |
| ... | ... | @@ -1076,7 +1090,7 @@ |
| 1076 | 1090 | |
| 1077 | 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 | 1095 | "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], |
| 1082 | 1096 | |
| ... | ... | @@ -1210,8 +1224,6 @@ |
| 1210 | 1224 | |
| 1211 | 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 | 1227 | "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], |
| 1216 | 1228 | |
| 1217 | 1229 | "electron/@types/node": ["@types/node@20.19.33", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw=="], |
| ... | ... | @@ -1246,14 +1258,14 @@ |
| 1246 | 1258 | |
| 1247 | 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 | 1263 | "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], |
| 1250 | 1264 | |
| 1251 | 1265 | "@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], |
| 1252 | 1266 | |
| 1253 | 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 | 1269 | "rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], |
| 1258 | 1270 | |
| 1259 | 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 | 1 | { |
| 2 | 2 | "name": "ct-round-5", |
| 3 | 3 | "private": true, |
| 4 | "packageManager": "bun@1.1.38", | |
| 5 | "workspaces": ["apps/*", "packages/*"], | |
| 4 | "workspaces": [ | |
| 5 | "apps/*", | |
| 6 | "packages/*" | |
| 7 | ], | |
| 6 | 8 | "scripts": { |
| 7 | 9 | "dev": "bun run build:contracts && turbo run dev --parallel", |
| 8 | 10 | "build": "turbo run build", |
| 9 | 11 | "typecheck": "turbo run typecheck", |
| 10 | "lint": "turbo run lint", | |
| 12 | "lint": "oxlint --report-unused-disable-directives", | |
| 11 | 13 | "test": "turbo run test", |
| 12 | "format": "biome format --write .", | |
| 13 | "check": "biome check .", | |
| 14 | "fmt": "oxfmt", | |
| 14 | 15 | "smoke-test": "turbo run smoke-test", |
| 15 | 16 | "build:contracts": "bun run --cwd packages/contracts build" |
| 16 | 17 | }, |
| 17 | 18 | "devDependencies": { |
| 18 | "@biomejs/biome": "^1.9.0", | |
| 19 | "oxfmt": "^0.28.0", | |
| 20 | "oxlint": "^1.43.0", | |
| 19 | 21 | "turbo": "^2.3.3", |
| 20 | 22 | "typescript": "^5.7.3", |
| 21 | 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 | 2 | "name": "@acme/contracts", |
| 3 | 3 | "version": "0.0.0", |
| 4 | 4 | "private": true, |
| 5 | "files": [ | |
| 6 | "dist" | |
| 7 | ], | |
| 5 | 8 | "type": "module", |
| 6 | 9 | "main": "./dist/index.cjs", |
| 7 | 10 | "module": "./dist/index.js", |
| ... | ... | @@ -13,12 +16,10 @@ |
| 13 | 16 | "require": "./dist/index.cjs" |
| 14 | 17 | } |
| 15 | 18 | }, |
| 16 | "files": ["dist"], | |
| 17 | 19 | "scripts": { |
| 18 | 20 | "dev": "tsup src/index.ts --format esm,cjs --dts --watch --clean", |
| 19 | 21 | "build": "tsup src/index.ts --format esm,cjs --dts --clean", |
| 20 | 22 | "typecheck": "tsc --noEmit", |
| 21 | "lint": "biome check src/", | |
| 22 | 23 | "test": "vitest run" |
| 23 | 24 | }, |
| 24 | 25 | "dependencies": { |
packages/contracts/src/ipc.ts+2-6| ... | ... | @@ -62,12 +62,8 @@ export interface NativeApi { |
| 62 | 62 | onExit: (callback: (exit: AgentExit) => void) => () => void; |
| 63 | 63 | }; |
| 64 | 64 | providers: { |
| 65 | startSession: ( | |
| 66 | input: ProviderSessionStartInput, | |
| 67 | ) => Promise<ProviderSession>; | |
| 68 | sendTurn: ( | |
| 69 | input: ProviderSendTurnInput, | |
| 70 | ) => Promise<ProviderTurnStartResult>; | |
| 65 | startSession: (input: ProviderSessionStartInput) => Promise<ProviderSession>; | |
| 66 | sendTurn: (input: ProviderSendTurnInput) => Promise<ProviderTurnStartResult>; | |
| 71 | 67 | interruptTurn: (input: ProviderInterruptTurnInput) => Promise<void>; |
| 72 | 68 | respondToRequest: (input: ProviderRespondToRequestInput) => Promise<void>; |
| 73 | 69 | stopSession: (input: ProviderStopSessionInput) => Promise<void>; |
packages/contracts/src/model.test.ts+1-6| ... | ... | @@ -1,11 +1,6 @@ |
| 1 | 1 | import { describe, expect, it } from "vitest"; |
| 2 | 2 | |
| 3 | import { | |
| 4 | DEFAULT_MODEL, | |
| 5 | MODEL_OPTIONS, | |
| 6 | normalizeModelSlug, | |
| 7 | resolveModelSlug, | |
| 8 | } from "./model"; | |
| 3 | import { DEFAULT_MODEL, MODEL_OPTIONS, normalizeModelSlug, resolveModelSlug } from "./model"; | |
| 9 | 4 | |
| 10 | 5 | describe("normalizeModelSlug", () => { |
| 11 | 6 | it("maps known aliases to canonical slugs", () => { |
packages/contracts/src/model.ts+2-8| ... | ... | @@ -1,8 +1,4 @@ |
| 1 | export const MODEL_OPTIONS = [ | |
| 2 | "gpt-5.3-codex", | |
| 3 | "gpt-5.2-codex", | |
| 4 | "gpt-5.2", | |
| 5 | ] as const; | |
| 1 | export const MODEL_OPTIONS = ["gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.2"] as const; | |
| 6 | 2 | |
| 7 | 3 | export const DEFAULT_MODEL = "gpt-5.3-codex"; |
| 8 | 4 | |
| ... | ... | @@ -11,9 +7,7 @@ export const MODEL_SLUG_ALIASES: Record<string, string> = { |
| 11 | 7 | "gpt-5.3": "gpt-5.3-codex", |
| 12 | 8 | }; |
| 13 | 9 | |
| 14 | export function normalizeModelSlug( | |
| 15 | model: string | null | undefined, | |
| 16 | ): string | null { | |
| 10 | export function normalizeModelSlug(model: string | null | undefined): string | null { | |
| 17 | 11 | if (typeof model !== "string") { |
| 18 | 12 | return null; |
| 19 | 13 | } |
packages/contracts/src/provider.ts+2-6| ... | ... | @@ -105,9 +105,7 @@ export const providerEventSchema = z.object({ |
| 105 | 105 | }); |
| 106 | 106 | |
| 107 | 107 | export type ProviderKind = z.infer<typeof providerKindSchema>; |
| 108 | export type ProviderApprovalPolicy = z.infer< | |
| 109 | typeof providerApprovalPolicySchema | |
| 110 | >; | |
| 108 | export type ProviderApprovalPolicy = z.infer<typeof providerApprovalPolicySchema>; | |
| 111 | 109 | export type ProviderSandboxMode = z.infer<typeof providerSandboxModeSchema>; |
| 112 | 110 | export type ProviderRequestKind = z.infer<typeof providerRequestKindSchema>; |
| 113 | 111 | export type ProviderApprovalDecision = z.infer< |
| ... | ... | @@ -115,9 +113,7 @@ export type ProviderApprovalDecision = z.infer< |
| 115 | 113 | >; |
| 116 | 114 | export type ProviderSessionStatus = z.infer<typeof providerSessionStatusSchema>; |
| 117 | 115 | export type ProviderSession = z.infer<typeof providerSessionSchema>; |
| 118 | export type ProviderSessionStartInput = z.input< | |
| 119 | typeof providerSessionStartInputSchema | |
| 120 | >; | |
| 116 | export type ProviderSessionStartInput = z.input<typeof providerSessionStartInputSchema>; | |
| 121 | 117 | export type ProviderSendTurnInput = z.input<typeof providerSendTurnInputSchema>; |
| 122 | 118 | export type ProviderTurnStartResult = z.infer< |
| 123 | 119 | typeof providerTurnStartResultSchema |
tsconfig.base.json+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "compilerOptions": { |
| 3 | "target": "ES2022", | |
| 3 | "target": "ES2023", | |
| 4 | 4 | "module": "ESNext", |
| 5 | 5 | "moduleResolution": "Bundler", |
| 6 | 6 | "strict": true, |
turbo.json-4| ... | ... | @@ -14,10 +14,6 @@ |
| 14 | 14 | "dependsOn": ["^typecheck"], |
| 15 | 15 | "outputs": [] |
| 16 | 16 | }, |
| 17 | "lint": { | |
| 18 | "dependsOn": ["^lint"], | |
| 19 | "outputs": [] | |
| 20 | }, | |
| 21 | 17 | "test": { |
| 22 | 18 | "dependsOn": ["^build"], |
| 23 | 19 | "outputs": [] |