openspecui 2.3.4 → 2.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/cli.mjs +2 -2
  2. package/dist/index.mjs +1 -1
  3. package/dist/{src-G3aiSI-m.mjs → src-C_h5f1-h.mjs} +342 -26
  4. package/package.json +1 -1
  5. package/web/assets/CanvasRenderer-BxSz1S0N.js +1 -0
  6. package/web/assets/WebGLRenderer-BHhaJ0WJ.js +1 -0
  7. package/web/assets/WebGPURenderer-DRKX39eh.js +1 -0
  8. package/web/assets/browserAll-D4RKeA6K.js +1 -0
  9. package/web/assets/{dist-CyMw4R35.js → dist-7cHS5QWY.js} +1 -1
  10. package/web/assets/dist-BDHG1KDB.js +1 -0
  11. package/web/assets/{dist-C4qVWCwa.js → dist-Bmr28z9J.js} +1 -1
  12. package/web/assets/{dist-Dw21sB44.js → dist-C9Q1D0iI.js} +1 -1
  13. package/web/assets/{dist-BURffNKn.js → dist-CHCyBtgU.js} +1 -1
  14. package/web/assets/{dist-BkfyzqOP.js → dist-CbImOnsL.js} +1 -1
  15. package/web/assets/dist-CdimBSfI.js +1 -0
  16. package/web/assets/{dist-0r-fEysT.js → dist-DmBfd3XF.js} +1 -1
  17. package/web/assets/{dist-D5aqbpvI.js → dist-DoTjw0T7.js} +1 -1
  18. package/web/assets/{dist-C_hvHrdB.js → dist-Pw11Vg0a.js} +1 -1
  19. package/web/assets/dist-TbE9jx_5.js +1 -0
  20. package/web/assets/{dist-EecJWaiB.js → dist-o5BKCN_5.js} +1 -1
  21. package/web/assets/{ghostty-web-Dz4JIhSO.js → ghostty-web-BM-sB-n_.js} +1 -1
  22. package/web/assets/index-Cf-Bih4u.css +1 -0
  23. package/web/assets/{index-CEpXtWXm.js → index-DglxXq77.js} +92 -102
  24. package/web/assets/{init-B8PdfdJA.js → init-CdYLlWcL.js} +1 -1
  25. package/web/assets/trpc-Zloaz5j7.js +1 -0
  26. package/web/assets/webworkerAll-B7pVi8gv.js +1 -0
  27. package/web/index.html +2 -2
  28. package/web/assets/CanvasRenderer-yznXxqwr.js +0 -1
  29. package/web/assets/WebGLRenderer-B23oVGNL.js +0 -1
  30. package/web/assets/WebGPURenderer-Cug1nwkB.js +0 -1
  31. package/web/assets/browserAll-u3VqlriV.js +0 -1
  32. package/web/assets/dist-COUYL3BZ.js +0 -1
  33. package/web/assets/dist-hmhW0TeA.js +0 -1
  34. package/web/assets/dist-zq2gg3Qj.js +0 -1
  35. package/web/assets/index-C5SoNGIA.css +0 -1
  36. package/web/assets/trpc-BYEnVZ4c.js +0 -1
  37. package/web/assets/webworkerAll-BiDEVq-n.js +0 -1
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as SchemaInfoSchema, c as toOpsxDisplayPath, d as CliExecutor, f as ConfigManager, g as __toESM, h as __commonJS, i as SchemaDetailSchema, l as buildHostedLaunchUrl, m as OpenSpecAdapter, o as SchemaResolutionSchema, p as DEFAULT_CONFIG, r as require_dist, s as TemplatesSchema, t as startServer, u as resolveHostedAppBaseUrl } from "./src-G3aiSI-m.mjs";
2
+ import { a as SchemaInfoSchema, c as toOpsxDisplayPath, d as CliExecutor, f as ConfigManager, g as __toESM, h as __commonJS, i as SchemaDetailSchema, l as buildHostedLaunchUrl, m as OpenSpecAdapter, o as SchemaResolutionSchema, p as DEFAULT_CONFIG, r as require_dist, s as TemplatesSchema, t as startServer, u as resolveHostedAppBaseUrl } from "./src-C_h5f1-h.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import { createServer } from "node:net";
5
5
  import { basename, dirname, extname, join, normalize, relative, resolve } from "path";
@@ -4508,7 +4508,7 @@ var yargs_default = Yargs;
4508
4508
  //#endregion
4509
4509
  //#region package.json
4510
4510
  var import_dist = require_dist();
4511
- var version = "2.3.4";
4511
+ var version = "2.3.6";
4512
4512
 
4513
4513
  //#endregion
4514
4514
  //#region src/export.ts
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { n as createServer, t as startServer } from "./src-G3aiSI-m.mjs";
1
+ import { n as createServer, t as startServer } from "./src-C_h5f1-h.mjs";
2
2
 
3
3
  export { createServer, startServer };
@@ -1054,6 +1054,8 @@ var ProjectWatcher = class {
1054
1054
  "project-dir-replaced": 0,
1055
1055
  manual: 0
1056
1056
  };
1057
+ projectResidency = { state: "active" };
1058
+ runtimeStatusListeners = /* @__PURE__ */ new Set();
1057
1059
  constructor(projectDir, options = {}) {
1058
1060
  this.projectDir = getRealPath$1(projectDir);
1059
1061
  this.debounceMs = options.debounceMs ?? DEBOUNCE_MS$1;
@@ -1083,7 +1085,9 @@ var ProjectWatcher = class {
1083
1085
  this.initialized = true;
1084
1086
  this.generation += 1;
1085
1087
  this.projectDirFingerprint = this.getProjectDirFingerprint();
1088
+ this.projectResidency = { state: "active" };
1086
1089
  this.startPathLivenessMonitor();
1090
+ this.emitRuntimeStatus();
1087
1091
  }
1088
1092
  /**
1089
1093
  * 处理 watcher 错误
@@ -1156,18 +1160,23 @@ var ProjectWatcher = class {
1156
1160
  if (!this.initialized || this.reinitializePending) return;
1157
1161
  const current = this.getProjectDirFingerprint();
1158
1162
  if (current === null) {
1163
+ this.markProjectResidencyEvicted("missing-project-dir");
1159
1164
  console.warn("[ProjectWatcher] Project directory missing, scheduling reinitialize...");
1160
1165
  this.scheduleReinitialize("missing-project-dir");
1161
1166
  return;
1162
1167
  }
1163
1168
  if (this.projectDirFingerprint === null) {
1164
1169
  this.projectDirFingerprint = current;
1170
+ this.markProjectResidencyActive();
1165
1171
  return;
1166
1172
  }
1167
1173
  if (current !== this.projectDirFingerprint) {
1174
+ this.markProjectResidencyEvicted("project-dir-replaced");
1168
1175
  console.warn("[ProjectWatcher] Project directory replaced, scheduling reinitialize...");
1169
1176
  this.scheduleReinitialize("project-dir-replaced");
1177
+ return;
1170
1178
  }
1179
+ this.markProjectResidencyActive();
1171
1180
  }
1172
1181
  /**
1173
1182
  * 处理原始事件
@@ -1267,7 +1276,15 @@ var ProjectWatcher = class {
1267
1276
  generation: this.generation,
1268
1277
  reinitializeCount: this.reinitializeCount,
1269
1278
  lastReinitializeReason: this.lastReinitializeReason,
1270
- reinitializeReasonCounts: { ...this.reinitializeReasonCounts }
1279
+ reinitializeReasonCounts: { ...this.reinitializeReasonCounts },
1280
+ projectResidency: { ...this.projectResidency }
1281
+ };
1282
+ }
1283
+ subscribeRuntimeStatus(listener, options = {}) {
1284
+ this.runtimeStatusListeners.add(listener);
1285
+ if (options.emitCurrent !== false) listener(this.runtimeStatus);
1286
+ return () => {
1287
+ this.runtimeStatusListeners.delete(listener);
1271
1288
  };
1272
1289
  }
1273
1290
  /**
@@ -1277,6 +1294,29 @@ var ProjectWatcher = class {
1277
1294
  this.reinitializeCount += 1;
1278
1295
  this.lastReinitializeReason = reason;
1279
1296
  this.reinitializeReasonCounts[reason] += 1;
1297
+ this.emitRuntimeStatus();
1298
+ }
1299
+ markProjectResidencyActive() {
1300
+ if (this.projectResidency.state === "active") return;
1301
+ this.projectResidency = { state: "active" };
1302
+ this.emitRuntimeStatus();
1303
+ }
1304
+ markProjectResidencyEvicted(reason) {
1305
+ if (this.projectResidency.state === "evicted" && this.projectResidency.reason === reason) return;
1306
+ this.projectResidency = {
1307
+ state: "evicted",
1308
+ reason,
1309
+ detectedAt: Date.now()
1310
+ };
1311
+ this.emitRuntimeStatus();
1312
+ }
1313
+ emitRuntimeStatus() {
1314
+ const status = this.runtimeStatus;
1315
+ for (const listener of this.runtimeStatusListeners) try {
1316
+ listener(status);
1317
+ } catch (error) {
1318
+ console.error("[ProjectWatcher] Runtime status listener failed:", error);
1319
+ }
1280
1320
  }
1281
1321
  /**
1282
1322
  * 重新初始化 watcher
@@ -1292,6 +1332,7 @@ var ProjectWatcher = class {
1292
1332
  this.initialized = false;
1293
1333
  this.initPromise = null;
1294
1334
  this.projectDirFingerprint = null;
1335
+ this.emitRuntimeStatus();
1295
1336
  if (!existsSync(this.projectDir)) {
1296
1337
  console.warn("[ProjectWatcher] Project directory does not exist, waiting for it to be created...");
1297
1338
  this.waitForProjectDir("missing-project-dir");
@@ -1354,6 +1395,8 @@ var ProjectWatcher = class {
1354
1395
  this.initialized = false;
1355
1396
  this.initPromise = null;
1356
1397
  this.projectDirFingerprint = null;
1398
+ this.projectResidency = { state: "active" };
1399
+ this.emitRuntimeStatus();
1357
1400
  }
1358
1401
  };
1359
1402
  /**
@@ -1398,6 +1441,25 @@ const DEBOUNCE_MS = 100;
1398
1441
  const subscriptionCache = /* @__PURE__ */ new Map();
1399
1442
  /** 防抖定时器 */
1400
1443
  const debounceTimers = /* @__PURE__ */ new Map();
1444
+ const watcherRuntimeStatusListeners = /* @__PURE__ */ new Set();
1445
+ let releaseProjectWatcherRuntimeSubscription = null;
1446
+ function emitWatcherRuntimeStatus() {
1447
+ const status = getWatcherRuntimeStatus();
1448
+ for (const listener of watcherRuntimeStatusListeners) listener(status);
1449
+ }
1450
+ function bindProjectWatcherRuntimeStatus() {
1451
+ releaseProjectWatcherRuntimeSubscription?.();
1452
+ releaseProjectWatcherRuntimeSubscription = null;
1453
+ if (!globalProjectWatcher) {
1454
+ emitWatcherRuntimeStatus();
1455
+ return;
1456
+ }
1457
+ const forward = () => {
1458
+ emitWatcherRuntimeStatus();
1459
+ };
1460
+ releaseProjectWatcherRuntimeSubscription = globalProjectWatcher.subscribeRuntimeStatus(forward, { emitCurrent: false });
1461
+ emitWatcherRuntimeStatus();
1462
+ }
1401
1463
  /**
1402
1464
  * 初始化 watcher pool
1403
1465
  *
@@ -1409,9 +1471,14 @@ const debounceTimers = /* @__PURE__ */ new Map();
1409
1471
  async function initWatcherPool(projectDir) {
1410
1472
  const normalizedDir = getRealPath(projectDir);
1411
1473
  if (globalProjectWatcher && globalProjectDir === normalizedDir) return;
1412
- if (globalProjectWatcher) await globalProjectWatcher.close();
1474
+ if (globalProjectWatcher) {
1475
+ releaseProjectWatcherRuntimeSubscription?.();
1476
+ releaseProjectWatcherRuntimeSubscription = null;
1477
+ await globalProjectWatcher.close();
1478
+ }
1413
1479
  globalProjectDir = normalizedDir;
1414
1480
  globalProjectWatcher = getProjectWatcher(normalizedDir);
1481
+ bindProjectWatcherRuntimeStatus();
1415
1482
  await globalProjectWatcher.init();
1416
1483
  }
1417
1484
  /**
@@ -1494,7 +1561,15 @@ function getWatcherRuntimeStatus() {
1494
1561
  generation: runtime.generation,
1495
1562
  reinitializeCount: runtime.reinitializeCount,
1496
1563
  lastReinitializeReason: runtime.lastReinitializeReason,
1497
- reinitializeReasonCounts: runtime.reinitializeReasonCounts
1564
+ reinitializeReasonCounts: runtime.reinitializeReasonCounts,
1565
+ projectResidency: runtime.projectResidency
1566
+ };
1567
+ }
1568
+ function subscribeWatcherRuntimeStatus(listener, options = {}) {
1569
+ watcherRuntimeStatusListeners.add(listener);
1570
+ if (options.emitCurrent !== false) listener(getWatcherRuntimeStatus());
1571
+ return () => {
1572
+ watcherRuntimeStatusListeners.delete(listener);
1498
1573
  };
1499
1574
  }
1500
1575
 
@@ -24132,6 +24207,210 @@ async function loadDashboardOverview(ctx, reason = "dashboard-refresh") {
24132
24207
  };
24133
24208
  }
24134
24209
 
24210
+ //#endregion
24211
+ //#region ../server/src/project-recovery-service.ts
24212
+ function normalizeWorktreeBranchName(defaultBranch) {
24213
+ const normalized = defaultBranch.trim();
24214
+ return /^[^/]+\/(.+)$/.exec(normalized)?.[1] ?? normalized;
24215
+ }
24216
+ function getGitDirArgs(gitDir, args) {
24217
+ return [
24218
+ "--git-dir",
24219
+ gitDir,
24220
+ ...args
24221
+ ];
24222
+ }
24223
+ var ProjectRecoveryService = class {
24224
+ emitter = new EventEmitter$1();
24225
+ current = { state: "idle" };
24226
+ projectDir;
24227
+ gitWorktreeHandoff;
24228
+ runGit;
24229
+ doesPathExist;
24230
+ canonicalPath;
24231
+ cachedGitCommonDir = null;
24232
+ cachedCanonicalProjectDir;
24233
+ primeRepositoryMetadataPromise = null;
24234
+ recoveryEpoch = 0;
24235
+ unsubscribeWatcherRuntime;
24236
+ constructor(options) {
24237
+ this.emitter.setMaxListeners(200);
24238
+ this.projectDir = resolve$1(options.projectDir);
24239
+ this.gitWorktreeHandoff = options.gitWorktreeHandoff;
24240
+ this.runGit = options.runGit ?? defaultRunGit;
24241
+ this.doesPathExist = options.pathExists ?? pathExists;
24242
+ this.canonicalPath = options.canonicalPath ?? canonicalGitPath;
24243
+ this.cachedCanonicalProjectDir = this.projectDir;
24244
+ this.unsubscribeWatcherRuntime = (options.subscribeWatcherRuntime ?? subscribeWatcherRuntimeStatus)((status) => {
24245
+ this.handleWatcherRuntimeStatus(status);
24246
+ }, { emitCurrent: true });
24247
+ this.primeRepositoryMetadata();
24248
+ }
24249
+ getCurrent() {
24250
+ return this.current;
24251
+ }
24252
+ subscribe(listener, options = {}) {
24253
+ this.emitter.on("change", listener);
24254
+ if (options.emitCurrent !== false) listener(this.current);
24255
+ return () => {
24256
+ this.emitter.off("change", listener);
24257
+ };
24258
+ }
24259
+ dispose() {
24260
+ this.unsubscribeWatcherRuntime();
24261
+ this.emitter.removeAllListeners();
24262
+ }
24263
+ setStatus(next) {
24264
+ if (JSON.stringify(this.current) === JSON.stringify(next)) return;
24265
+ this.current = next;
24266
+ this.emitter.emit("change", next);
24267
+ }
24268
+ handleWatcherRuntimeStatus(status) {
24269
+ const residency = status?.projectResidency;
24270
+ if (!residency || residency.state === "active") {
24271
+ this.recoveryEpoch += 1;
24272
+ this.setStatus({ state: "idle" });
24273
+ this.primeRepositoryMetadata();
24274
+ return;
24275
+ }
24276
+ if (this.hasHandledEviction(residency)) return;
24277
+ const epoch = ++this.recoveryEpoch;
24278
+ this.setStatus({
24279
+ state: "evicted",
24280
+ reason: residency.reason,
24281
+ detectedAt: residency.detectedAt
24282
+ });
24283
+ this.resolveRecovery(residency, epoch);
24284
+ }
24285
+ hasHandledEviction(residency) {
24286
+ switch (this.current.state) {
24287
+ case "evicted":
24288
+ case "resolving":
24289
+ case "ready":
24290
+ case "unavailable":
24291
+ case "failed": return this.current.detectedAt === residency.detectedAt;
24292
+ default: return false;
24293
+ }
24294
+ }
24295
+ async resolveRecovery(residency, epoch) {
24296
+ this.setStatus({
24297
+ state: "resolving",
24298
+ reason: residency.reason,
24299
+ detectedAt: residency.detectedAt
24300
+ });
24301
+ if (this.cachedGitCommonDir === null) await this.primeRepositoryMetadata();
24302
+ if (epoch !== this.recoveryEpoch) return;
24303
+ const gitCommonDir = this.cachedGitCommonDir;
24304
+ if (!gitCommonDir || !await this.doesPathExist(gitCommonDir)) {
24305
+ this.setStatus({
24306
+ state: "unavailable",
24307
+ reason: residency.reason,
24308
+ detectedAt: residency.detectedAt,
24309
+ message: "Cached Git metadata is unavailable, so automatic recovery cannot resolve a fallback worktree."
24310
+ });
24311
+ return;
24312
+ }
24313
+ const targetPath = await this.resolveFallbackTargetPath(gitCommonDir);
24314
+ if (epoch !== this.recoveryEpoch) return;
24315
+ if (!targetPath) {
24316
+ this.setStatus({
24317
+ state: "unavailable",
24318
+ reason: residency.reason,
24319
+ detectedAt: residency.detectedAt,
24320
+ message: "No existing default-branch worktree is available for automatic recovery. Restore the worktree manually or reopen the repo from a surviving worktree."
24321
+ });
24322
+ return;
24323
+ }
24324
+ if (!this.gitWorktreeHandoff) {
24325
+ this.setStatus({
24326
+ state: "unavailable",
24327
+ reason: residency.reason,
24328
+ detectedAt: residency.detectedAt,
24329
+ message: "This runtime cannot spawn or reuse sibling worktree servers for automatic recovery."
24330
+ });
24331
+ return;
24332
+ }
24333
+ try {
24334
+ const handoff = await this.gitWorktreeHandoff.ensureWorktreeServer({ targetPath });
24335
+ if (epoch !== this.recoveryEpoch) return;
24336
+ this.setStatus({
24337
+ state: "ready",
24338
+ reason: residency.reason,
24339
+ detectedAt: residency.detectedAt,
24340
+ handoff
24341
+ });
24342
+ } catch (error) {
24343
+ if (epoch !== this.recoveryEpoch) return;
24344
+ this.setStatus({
24345
+ state: "failed",
24346
+ reason: residency.reason,
24347
+ detectedAt: residency.detectedAt,
24348
+ message: error instanceof Error ? error.message : String(error)
24349
+ });
24350
+ }
24351
+ }
24352
+ async primeRepositoryMetadata() {
24353
+ if (this.primeRepositoryMetadataPromise) return this.primeRepositoryMetadataPromise;
24354
+ this.primeRepositoryMetadataPromise = (async () => {
24355
+ this.cachedCanonicalProjectDir = await this.canonicalPath(this.projectDir);
24356
+ this.cachedGitCommonDir = await this.resolveGitCommonDir();
24357
+ })();
24358
+ try {
24359
+ await this.primeRepositoryMetadataPromise;
24360
+ } catch {
24361
+ this.cachedGitCommonDir = null;
24362
+ this.cachedCanonicalProjectDir = this.projectDir;
24363
+ } finally {
24364
+ this.primeRepositoryMetadataPromise = null;
24365
+ }
24366
+ }
24367
+ async resolveGitCommonDir() {
24368
+ const result = await this.runGit(this.projectDir, ["rev-parse", "--git-common-dir"]);
24369
+ const gitCommonDirRaw = result.ok ? result.stdout.trim() : "";
24370
+ if (!gitCommonDirRaw) return null;
24371
+ return resolve$1(this.projectDir, gitCommonDirRaw);
24372
+ }
24373
+ async resolveDefaultBranchName(gitCommonDir) {
24374
+ const cwd = dirname$1(gitCommonDir);
24375
+ const remoteHead = await this.runGit(cwd, getGitDirArgs(gitCommonDir, [
24376
+ "symbolic-ref",
24377
+ "--quiet",
24378
+ "--short",
24379
+ "refs/remotes/origin/HEAD"
24380
+ ]));
24381
+ const remoteRef = remoteHead.stdout.trim();
24382
+ if (remoteHead.ok && remoteRef) return normalizeWorktreeBranchName(remoteRef);
24383
+ const localHead = await this.runGit(cwd, getGitDirArgs(gitCommonDir, [
24384
+ "rev-parse",
24385
+ "--abbrev-ref",
24386
+ "HEAD"
24387
+ ]));
24388
+ const localRef = localHead.stdout.trim();
24389
+ if (localHead.ok && localRef && localRef !== "HEAD") return normalizeWorktreeBranchName(localRef);
24390
+ return "main";
24391
+ }
24392
+ async resolveFallbackTargetPath(gitCommonDir) {
24393
+ const cwd = dirname$1(gitCommonDir);
24394
+ const defaultBranchName = await this.resolveDefaultBranchName(gitCommonDir);
24395
+ const worktreeListResult = await this.runGit(cwd, getGitDirArgs(gitCommonDir, [
24396
+ "worktree",
24397
+ "list",
24398
+ "--porcelain"
24399
+ ]));
24400
+ if (!worktreeListResult.ok) return null;
24401
+ const parsedWorktrees = parseWorktreeList(worktreeListResult.stdout);
24402
+ for (const worktree of parsedWorktrees) {
24403
+ const targetPath = resolve$1(worktree.path);
24404
+ const branchName = worktree.branchRef?.replace(/^refs\/heads\//, "") ?? null;
24405
+ if (worktree.detached || branchName !== defaultBranchName) continue;
24406
+ if (!await this.doesPathExist(targetPath)) continue;
24407
+ if (await this.canonicalPath(targetPath) === this.cachedCanonicalProjectDir) continue;
24408
+ return targetPath;
24409
+ }
24410
+ return null;
24411
+ }
24412
+ };
24413
+
24135
24414
  //#endregion
24136
24415
  //#region ../server/src/pty-manager.ts
24137
24416
  const DEFAULT_SCROLLBACK = 1e3;
@@ -25709,7 +25988,8 @@ function buildSystemStatus(ctx) {
25709
25988
  watcherEnabled: runtime?.initialized ?? false,
25710
25989
  watcherGeneration: runtime?.generation ?? 0,
25711
25990
  watcherReinitializeCount: runtime?.reinitializeCount ?? 0,
25712
- watcherLastReinitializeReason: runtime?.lastReinitializeReason ?? null
25991
+ watcherLastReinitializeReason: runtime?.lastReinitializeReason ?? null,
25992
+ projectRecovery: ctx.projectRecoveryService.getCurrent()
25713
25993
  };
25714
25994
  }
25715
25995
  /**
@@ -26423,13 +26703,24 @@ const systemRouter = router({
26423
26703
  }),
26424
26704
  subscribe: publicProcedure.subscription(({ ctx }) => {
26425
26705
  return observable((emit) => {
26426
- emit.next(buildSystemStatus(ctx));
26427
- const timer = setInterval(() => {
26706
+ const pushStatus = () => {
26428
26707
  emit.next(buildSystemStatus(ctx));
26708
+ };
26709
+ pushStatus();
26710
+ const unsubscribeWatcherRuntime = subscribeWatcherRuntimeStatus(() => {
26711
+ pushStatus();
26712
+ });
26713
+ const unsubscribeProjectRecovery = ctx.projectRecoveryService.subscribe(() => {
26714
+ pushStatus();
26715
+ });
26716
+ const timer = setInterval(() => {
26717
+ pushStatus();
26429
26718
  }, 3e3);
26430
26719
  timer.unref();
26431
26720
  return () => {
26432
26721
  clearInterval(timer);
26722
+ unsubscribeWatcherRuntime();
26723
+ unsubscribeProjectRecovery();
26433
26724
  };
26434
26725
  });
26435
26726
  })
@@ -26821,6 +27112,10 @@ function createServer$2(config) {
26821
27112
  configManager,
26822
27113
  projectDir: config.projectDir
26823
27114
  }, reason), watcher);
27115
+ const projectRecoveryService = new ProjectRecoveryService({
27116
+ projectDir: config.projectDir,
27117
+ gitWorktreeHandoff: config.gitWorktreeHandoff
27118
+ });
26824
27119
  const app = new Hono();
26825
27120
  const corsOrigins = config.corsOrigins ?? ["http://localhost:5173", "http://localhost:3000"];
26826
27121
  app.use("*", cors({
@@ -26848,6 +27143,7 @@ function createServer$2(config) {
26848
27143
  kernel,
26849
27144
  searchService,
26850
27145
  dashboardOverviewService,
27146
+ projectRecoveryService,
26851
27147
  gitWorktreeHandoff: config.gitWorktreeHandoff,
26852
27148
  watcher,
26853
27149
  projectDir: config.projectDir
@@ -26861,6 +27157,7 @@ function createServer$2(config) {
26861
27157
  kernel,
26862
27158
  searchService,
26863
27159
  dashboardOverviewService,
27160
+ projectRecoveryService,
26864
27161
  gitWorktreeHandoff: config.gitWorktreeHandoff,
26865
27162
  watcher,
26866
27163
  projectDir: config.projectDir
@@ -26873,6 +27170,7 @@ function createServer$2(config) {
26873
27170
  kernel,
26874
27171
  searchService,
26875
27172
  dashboardOverviewService,
27173
+ projectRecoveryService,
26876
27174
  watcher,
26877
27175
  createContext,
26878
27176
  port: config.port ?? 3100
@@ -26921,6 +27219,7 @@ async function createWebSocketServer(server, httpServer, config) {
26921
27219
  server.watcher?.stop();
26922
27220
  server.searchService.dispose().catch(() => {});
26923
27221
  server.dashboardOverviewService.dispose();
27222
+ server.projectRecoveryService.dispose();
26924
27223
  }
26925
27224
  };
26926
27225
  }
@@ -26997,38 +27296,55 @@ function resolveLocalCliWorkspace(runtimeDir) {
26997
27296
  function resolvePnpmCommand() {
26998
27297
  return process.platform === "win32" ? "pnpm.cmd" : "pnpm";
26999
27298
  }
27000
- function createWorktreeServerCommand(options) {
27001
- const workspace = resolveLocalCliWorkspace(options.runtimeDir);
27002
- if (workspace) return {
27003
- command: resolvePnpmCommand(),
27004
- args: [
27005
- "--filter",
27006
- "openspecui",
27007
- "run",
27008
- "dev",
27009
- "--dir",
27010
- options.projectDir,
27011
- "--port",
27012
- String(options.port),
27013
- "--no-open"
27014
- ],
27015
- cwd: workspace.repoRoot,
27016
- env: { ...process.env }
27017
- };
27299
+ function createNodeCliCommand(options) {
27018
27300
  return {
27019
27301
  command: process.execPath,
27020
27302
  args: [
27021
- join$1(options.runtimeDir, "cli.mjs"),
27303
+ options.cliEntry,
27022
27304
  "start",
27023
27305
  options.projectDir,
27024
27306
  "--port",
27025
27307
  String(options.port),
27026
27308
  "--no-open"
27027
27309
  ],
27028
- cwd: options.projectDir,
27310
+ cwd: options.cwd,
27029
27311
  env: { ...process.env }
27030
27312
  };
27031
27313
  }
27314
+ function createWorktreeServerCommand(options) {
27315
+ const workspace = resolveLocalCliWorkspace(options.runtimeDir);
27316
+ if (workspace) {
27317
+ const cliDistEntry = join$1(workspace.cliPackageDir, "dist", "cli.mjs");
27318
+ if (existsSync(cliDistEntry)) return createNodeCliCommand({
27319
+ cliEntry: cliDistEntry,
27320
+ projectDir: options.projectDir,
27321
+ port: options.port,
27322
+ cwd: workspace.repoRoot
27323
+ });
27324
+ return {
27325
+ command: resolvePnpmCommand(),
27326
+ args: [
27327
+ "--filter",
27328
+ "openspecui",
27329
+ "run",
27330
+ "dev",
27331
+ "--dir",
27332
+ options.projectDir,
27333
+ "--port",
27334
+ String(options.port),
27335
+ "--no-open"
27336
+ ],
27337
+ cwd: workspace.repoRoot,
27338
+ env: { ...process.env }
27339
+ };
27340
+ }
27341
+ return createNodeCliCommand({
27342
+ cliEntry: join$1(options.runtimeDir, "cli.mjs"),
27343
+ projectDir: options.projectDir,
27344
+ port: options.port,
27345
+ cwd: options.projectDir
27346
+ });
27347
+ }
27032
27348
  async function waitForServerReady(options) {
27033
27349
  let exitMessage = null;
27034
27350
  let startupError = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openspecui",
3
- "version": "2.3.4",
3
+ "version": "2.3.6",
4
4
  "description": "OpenSpec UI - Visual interface for spec-driven development",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -0,0 +1 @@
1
+ import"./Geometry-CNDxiJq_.js";import{r as e}from"./index-DglxXq77.js";export{e as CanvasRenderer};
@@ -0,0 +1 @@
1
+ import"./Geometry-CNDxiJq_.js";import{n as e}from"./index-DglxXq77.js";export{e as WebGLRenderer};
@@ -0,0 +1 @@
1
+ import"./Geometry-CNDxiJq_.js";import{t as e}from"./index-DglxXq77.js";export{e as WebGPURenderer};
@@ -0,0 +1 @@
1
+ import{it as e,x as t}from"./Geometry-CNDxiJq_.js";import{c as n,l as r,o as i,s as a}from"./index-DglxXq77.js";import"./init-CdYLlWcL.js";e.add(r),e.mixin(t,n),e.add(a),e.mixin(t,i);
@@ -1 +1 @@
1
- import{L as e,Lt as t,R as n,i as r,r as i,s as a,u as o}from"./dist-QUHuPJsA.js";import{r as s}from"./dist--FTL3P_Y.js";import{x as c}from"./index-CEpXtWXm.js";var l=i.deserialize({version:14,states:"%pOVOWOOObQPOOOpOSO'#C_OOOO'#Cp'#CpQVOWOOQxQPOOO!TQQOOQ!YQPOOOOOO,58y,58yO!_OSO,58yOOOO-E6n-E6nO!dQQO'#CqQ{QPOOO!iQPOOQ{QPOOO!qQPOOOOOO1G.e1G.eOOQO,59],59]OOQO-E6o-E6oO!yOpO'#CiO#RO`O'#CiQOQPOOO#ZO#tO'#CmO#fO!bO'#CmOOQO,59T,59TO#qOpO,59TO#vO`O,59TOOOO'#Cr'#CrO#{O#tO,59XOOQO,59X,59XOOOO'#Cs'#CsO$WO!bO,59XOOQO1G.o1G.oOOOO-E6p-E6pOOQO1G.s1G.sOOOO-E6q-E6q",stateData:"$g~OjOS~OQROUROkQO~OWTOXUOZUO`VO~OSXOTWO~OXUO[]OlZO~OY^O~O[_O~OT`O~OYaO~OmcOodO~OmfOogO~O^iOnhO~O_jOphO~ObkOqkOrmO~OcnOsnOtmO~OnpO~OppO~ObkOqkOrrO~OcnOsnOtrO~OWX`~",goto:`!^hPPPiPPPPPPPPPmPPPpPPsy!Q!WTROSRe]Re_QSORYSS[T^Rb[QlfRqlQogRso`,nodeNames:`⚠ Content Text Interpolation InterpolationContent }} Entity Attribute VueAttributeName : Identifier @ Is ScriptAttributeValue AttributeScript AttributeScript AttributeName AttributeValue Entity Entity`,maxTerm:36,nodeProps:[[`isolate`,-3,3,13,17,``]],skippedNodes:[0],repeatNodeCount:4,tokenData:"'y~RdXY!aYZ!a]^!apq!ars!rwx!w}!O!|!O!P#t!Q![#y![!]$s!_!`%g!b!c%l!c!}#y#R#S#y#T#j#y#j#k%q#k#o#y%W;'S#y;'S;:j$m<%lO#y~!fSj~XY!aYZ!a]^!apq!a~!wOm~~!|Oo~!b#RX`!b}!O!|!Q![!|![!]!|!c!}!|#R#S!|#T#o!|%W;'S!|;'S;:j#n<%lO!|!b#qP;=`<%l!|~#yOl~%W$QXY#t`!b}!O!|!Q![#y![!]!|!c!}#y#R#S#y#T#o#y%W;'S#y;'S;:j$m<%lO#y%W$pP;=`<%l#y~$zXX~`!b}!O!|!Q![!|![!]!|!c!}!|#R#S!|#T#o!|%W;'S!|;'S;:j#n<%lO!|~%lO[~~%qOZ~%W%xXY#t`!b}!O&e!Q![#y![!]!|!c!}#y#R#S#y#T#o#y%W;'S#y;'S;:j$m<%lO#y!b&jX`!b}!O!|!Q![!|![!]!|!c!}'V#R#S!|#T#o'V%W;'S!|;'S;:j#n<%lO!|!b'^XW!b`!b}!O!|!Q![!|![!]!|!c!}'V#R#S!|#T#o'V%W;'S!|;'S;:j#n<%lO!|",tokenizers:[6,7,new r(`b~RP#q#rU~XP#q#r[~aOT~~`,17,4),new r("!k~RQvwX#o#p!_~^TU~Opmq!]m!^;'Sm;'S;=`!X<%lOm~pUOpmq!]m!]!^!S!^;'Sm;'S;=`!X<%lOm~!XOU~~![P;=`<%lm~!bP#o#p!e~!jOk~~",72,2),new r(`[~RPwxU~ZOp~~`,11,15),new r(`[~RPrsU~ZOn~~`,11,14),new r("!e~RQvwXwx!_~^Tc~Opmq!]m!^;'Sm;'S;=`!X<%lOm~pUOpmq!]m!]!^!S!^;'Sm;'S;=`!X<%lOm~!XOc~~![P;=`<%lm~!dOt~~",66,35),new r("!e~RQrsXvw^~^Or~~cTb~Oprq!]r!^;'Sr;'S;=`!^<%lOr~uUOprq!]r!]!^!X!^;'Sr;'S;=`!^<%lOr~!^Ob~~!aP;=`<%lr~",66,33)],topRules:{Content:[0,1],Attribute:[1,7]},tokenPrec:157}),u=s.parser.configure({top:`SingleExpression`}),d=l.configure({props:[e({Text:n.content,Is:n.definitionOperator,AttributeName:n.attributeName,VueAttributeName:n.keyword,Identifier:n.variableName,"AttributeValue ScriptAttributeValue":n.attributeValue,Entity:n.character,"{{ }}":n.brace,"@ :":n.punctuation})]}),f={parser:u},p=d.configure({wrap:t((e,t)=>e.name==`InterpolationContent`?f:null)}),m=d.configure({wrap:t((e,t)=>e.name==`AttributeScript`?f:null),top:`Attribute`}),h={parser:p},g={parser:m},_=c();function v(e){return e.configure({dialect:`selfClosing`,wrap:t(b)},`vue`)}var y=v(_.language);function b(e,t){switch(e.name){case`Attribute`:return/^(@|:|v-)/.test(t.read(e.from,e.from+2))?g:null;case`Text`:return h}return null}function x(e={}){let t=_;if(e.base){if(e.base.language.name!=`html`||!(e.base.language instanceof a))throw RangeError(`The base option must be the result of calling html(...)`);t=e.base}return new o(t.language==_.language?y:v(t.language),[t.support,t.language.data.of({closeBrackets:{brackets:[`{`,`"`]}})])}export{x as vue};
1
+ import{L as e,Lt as t,R as n,i as r,r as i,s as a,u as o}from"./dist-QUHuPJsA.js";import{r as s}from"./dist--FTL3P_Y.js";import{x as c}from"./index-DglxXq77.js";var l=i.deserialize({version:14,states:"%pOVOWOOObQPOOOpOSO'#C_OOOO'#Cp'#CpQVOWOOQxQPOOO!TQQOOQ!YQPOOOOOO,58y,58yO!_OSO,58yOOOO-E6n-E6nO!dQQO'#CqQ{QPOOO!iQPOOQ{QPOOO!qQPOOOOOO1G.e1G.eOOQO,59],59]OOQO-E6o-E6oO!yOpO'#CiO#RO`O'#CiQOQPOOO#ZO#tO'#CmO#fO!bO'#CmOOQO,59T,59TO#qOpO,59TO#vO`O,59TOOOO'#Cr'#CrO#{O#tO,59XOOQO,59X,59XOOOO'#Cs'#CsO$WO!bO,59XOOQO1G.o1G.oOOOO-E6p-E6pOOQO1G.s1G.sOOOO-E6q-E6q",stateData:"$g~OjOS~OQROUROkQO~OWTOXUOZUO`VO~OSXOTWO~OXUO[]OlZO~OY^O~O[_O~OT`O~OYaO~OmcOodO~OmfOogO~O^iOnhO~O_jOphO~ObkOqkOrmO~OcnOsnOtmO~OnpO~OppO~ObkOqkOrrO~OcnOsnOtrO~OWX`~",goto:`!^hPPPiPPPPPPPPPmPPPpPPsy!Q!WTROSRe]Re_QSORYSS[T^Rb[QlfRqlQogRso`,nodeNames:`⚠ Content Text Interpolation InterpolationContent }} Entity Attribute VueAttributeName : Identifier @ Is ScriptAttributeValue AttributeScript AttributeScript AttributeName AttributeValue Entity Entity`,maxTerm:36,nodeProps:[[`isolate`,-3,3,13,17,``]],skippedNodes:[0],repeatNodeCount:4,tokenData:"'y~RdXY!aYZ!a]^!apq!ars!rwx!w}!O!|!O!P#t!Q![#y![!]$s!_!`%g!b!c%l!c!}#y#R#S#y#T#j#y#j#k%q#k#o#y%W;'S#y;'S;:j$m<%lO#y~!fSj~XY!aYZ!a]^!apq!a~!wOm~~!|Oo~!b#RX`!b}!O!|!Q![!|![!]!|!c!}!|#R#S!|#T#o!|%W;'S!|;'S;:j#n<%lO!|!b#qP;=`<%l!|~#yOl~%W$QXY#t`!b}!O!|!Q![#y![!]!|!c!}#y#R#S#y#T#o#y%W;'S#y;'S;:j$m<%lO#y%W$pP;=`<%l#y~$zXX~`!b}!O!|!Q![!|![!]!|!c!}!|#R#S!|#T#o!|%W;'S!|;'S;:j#n<%lO!|~%lO[~~%qOZ~%W%xXY#t`!b}!O&e!Q![#y![!]!|!c!}#y#R#S#y#T#o#y%W;'S#y;'S;:j$m<%lO#y!b&jX`!b}!O!|!Q![!|![!]!|!c!}'V#R#S!|#T#o'V%W;'S!|;'S;:j#n<%lO!|!b'^XW!b`!b}!O!|!Q![!|![!]!|!c!}'V#R#S!|#T#o'V%W;'S!|;'S;:j#n<%lO!|",tokenizers:[6,7,new r(`b~RP#q#rU~XP#q#r[~aOT~~`,17,4),new r("!k~RQvwX#o#p!_~^TU~Opmq!]m!^;'Sm;'S;=`!X<%lOm~pUOpmq!]m!]!^!S!^;'Sm;'S;=`!X<%lOm~!XOU~~![P;=`<%lm~!bP#o#p!e~!jOk~~",72,2),new r(`[~RPwxU~ZOp~~`,11,15),new r(`[~RPrsU~ZOn~~`,11,14),new r("!e~RQvwXwx!_~^Tc~Opmq!]m!^;'Sm;'S;=`!X<%lOm~pUOpmq!]m!]!^!S!^;'Sm;'S;=`!X<%lOm~!XOc~~![P;=`<%lm~!dOt~~",66,35),new r("!e~RQrsXvw^~^Or~~cTb~Oprq!]r!^;'Sr;'S;=`!^<%lOr~uUOprq!]r!]!^!X!^;'Sr;'S;=`!^<%lOr~!^Ob~~!aP;=`<%lr~",66,33)],topRules:{Content:[0,1],Attribute:[1,7]},tokenPrec:157}),u=s.parser.configure({top:`SingleExpression`}),d=l.configure({props:[e({Text:n.content,Is:n.definitionOperator,AttributeName:n.attributeName,VueAttributeName:n.keyword,Identifier:n.variableName,"AttributeValue ScriptAttributeValue":n.attributeValue,Entity:n.character,"{{ }}":n.brace,"@ :":n.punctuation})]}),f={parser:u},p=d.configure({wrap:t((e,t)=>e.name==`InterpolationContent`?f:null)}),m=d.configure({wrap:t((e,t)=>e.name==`AttributeScript`?f:null),top:`Attribute`}),h={parser:p},g={parser:m},_=c();function v(e){return e.configure({dialect:`selfClosing`,wrap:t(b)},`vue`)}var y=v(_.language);function b(e,t){switch(e.name){case`Attribute`:return/^(@|:|v-)/.test(t.read(e.from,e.from+2))?g:null;case`Text`:return h}return null}function x(e={}){let t=_;if(e.base){if(e.base.language.name!=`html`||!(e.base.language instanceof a))throw RangeError(`The base option must be the result of calling html(...)`);t=e.base}return new o(t.language==_.language?y:v(t.language),[t.support,t.language.data.of({closeBrackets:{brackets:[`{`,`"`]}})])}export{x as vue};
@@ -0,0 +1 @@
1
+ import"./dist-QUHuPJsA.js";import{k as e}from"./index-DglxXq77.js";export{e as json};
@@ -1 +1 @@
1
- import"./dist-QUHuPJsA.js";import"./dist--FTL3P_Y.js";import{x as e}from"./index-CEpXtWXm.js";export{e as html};
1
+ import"./dist-QUHuPJsA.js";import"./dist--FTL3P_Y.js";import{x as e}from"./index-DglxXq77.js";export{e as html};