| ... | ... | @@ -6,10 +6,16 @@ import * as NodePath from "node:path"; |
| 6 | 6 | |
| 7 | 7 | import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"; |
| 8 | 8 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 9 | | import { EnvironmentOrchestrationHttpApi } from "@t3tools/contracts"; |
| 9 | import { |
| 10 | CommandId, |
| 11 | EnvironmentOrchestrationHttpApi, |
| 12 | ProviderInstanceId, |
| 13 | ThreadId, |
| 14 | } from "@t3tools/contracts"; |
| 10 | 15 | import * as NetService from "@t3tools/shared/Net"; |
| 11 | 16 | import { assert, it } from "@effect/vitest"; |
| 12 | 17 | import * as Effect from "effect/Effect"; |
| 18 | import * as DateTime from "effect/DateTime"; |
| 13 | 19 | import * as Layer from "effect/Layer"; |
| 14 | 20 | import * as HttpRouter from "effect/unstable/http/HttpRouter"; |
| 15 | 21 | import * as HttpServer from "effect/unstable/http/HttpServer"; |
| ... | ... | @@ -22,6 +28,7 @@ import { Command } from "effect/unstable/cli"; |
| 22 | 28 | import { cli, makeCli } from "./bin.ts"; |
| 23 | 29 | import * as ServerConfig from "./config.ts"; |
| 24 | 30 | import * as ProjectionSnapshotQuery from "./orchestration/Services/ProjectionSnapshotQuery.ts"; |
| 31 | import * as OrchestrationEngine from "./orchestration/Services/OrchestrationEngine.ts"; |
| 25 | 32 | import { OrchestrationLayerLive } from "./orchestration/runtimeLayer.ts"; |
| 26 | 33 | import { orchestrationHttpApiLayer } from "./orchestration/http.ts"; |
| 27 | 34 | import { layerConfig as SqlitePersistenceLayerLive } from "./persistence/Layers/Sqlite.ts"; |
| ... | ... | @@ -460,6 +467,63 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { |
| 460 | 467 | }), |
| 461 | 468 | ); |
| 462 | 469 | |
| 470 | it.effect("force removes projects that still contain threads", () => |
| 471 | Effect.gen(function* () { |
| 472 | const baseDir = NodeFS.mkdtempSync( |
| 473 | NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-force-remove-test-"), |
| 474 | ); |
| 475 | const workspaceRoot = NodeFS.mkdtempSync( |
| 476 | NodePath.join(NodeOS.tmpdir(), "t3-cli-projects-force-remove-workspace-"), |
| 477 | ); |
| 478 | |
| 479 | yield* runCliWithRuntime(["project", "add", workspaceRoot, "--base-dir", baseDir]); |
| 480 | const afterAdd = yield* readPersistedSnapshot(baseDir); |
| 481 | const project = afterAdd.projects.find( |
| 482 | (candidate) => candidate.workspaceRoot === workspaceRoot && candidate.deletedAt === null, |
| 483 | ); |
| 484 | assert.isTrue(project !== undefined); |
| 485 | |
| 486 | const config = yield* makeCliTestServerConfig(baseDir); |
| 487 | yield* Effect.gen(function* () { |
| 488 | const engine = yield* OrchestrationEngine.OrchestrationEngineService; |
| 489 | yield* engine.dispatch({ |
| 490 | type: "thread.create", |
| 491 | commandId: CommandId.make("cmd-cli-force-remove-thread"), |
| 492 | threadId: ThreadId.make("thread-cli-force-remove"), |
| 493 | projectId: project!.id, |
| 494 | title: "Thread", |
| 495 | modelSelection: { |
| 496 | instanceId: ProviderInstanceId.make("codex"), |
| 497 | model: "gpt-5-codex", |
| 498 | }, |
| 499 | interactionMode: "default", |
| 500 | runtimeMode: "approval-required", |
| 501 | branch: null, |
| 502 | worktreePath: null, |
| 503 | createdAt: DateTime.formatIso(yield* DateTime.now), |
| 504 | }); |
| 505 | }).pipe(Effect.provide(makeProjectPersistenceLayer(config))); |
| 506 | |
| 507 | yield* runCliWithRuntime([ |
| 508 | "project", |
| 509 | "remove", |
| 510 | project!.id, |
| 511 | "--force", |
| 512 | "--base-dir", |
| 513 | baseDir, |
| 514 | ]); |
| 515 | const afterRemove = yield* readPersistedSnapshot(baseDir); |
| 516 | assert.isTrue( |
| 517 | (afterRemove.projects.find((candidate) => candidate.id === project!.id)?.deletedAt ?? |
| 518 | null) !== null, |
| 519 | ); |
| 520 | assert.isTrue( |
| 521 | (afterRemove.threads.find((thread) => thread.id === "thread-cli-force-remove")?.deletedAt ?? |
| 522 | null) !== null, |
| 523 | ); |
| 524 | }), |
| 525 | ); |
| 526 | |
| 463 | 527 | it.effect("routes project commands through a running server when runtime state is present", () => |
| 464 | 528 | Effect.gen(function* () { |
| 465 | 529 | const baseDir = NodeFS.mkdtempSync( |