@openspecui/server 7.0.1 → 7.0.2

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.
@@ -2,9 +2,9 @@ import m, { createRequire } from "node:module";
2
2
  import a from "node:path";
3
3
  import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import se, { writeSync } from "node:fs";
5
+ import Ht from "node:crypto";
5
6
  import o from "node:os";
6
7
  import p from "node:net";
7
- import Ht from "node:crypto";
8
8
  import { inspect } from "node:util";
9
9
  import { MessageChannel } from "node:worker_threads";
10
10
  import { createRequire as createRequire$1 } from "module";
package/dist/index.mjs CHANGED
@@ -1,12 +1,13 @@
1
1
  import { a as resolveCt2ModelDownloadPlanFromRepositoryFiles } from "./src-Dh_UAz5C.mjs";
2
2
  import { t as resolveGgufModelDownloadPlanFromRepositoryFiles } from "./src-CZfiVL_-.mjs";
3
- import { AI_TOOLS, ActiveRootMutationSchema, ActiveRootRevisionSchema, AgentDeliveryPolicyUpdateSchema, BatchTranslateInputSchema, CliExecutor, CliProjectionCommandError, CliProjectionNoticeSchema, CodeEditorThemeSchema, ConfigManager, CustomSoundHashSchema, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, DashboardGitSnapshotSchema, DashboardSummaryInvalidationSchema, DashboardSummaryProjectionStateSchema, DashboardTrendsProjectionSchema, DocumentTranslationConfigUpdateSchema, EnvironmentDefaultStoreUpdateSchema, EnvironmentGlobalConfigValueSchema, EnvironmentGlobalFileProjectionDataSchema, EnvironmentGlobalFileProjectionStateSchema, EnvironmentGlobalProjectionDataSchema, EnvironmentGlobalProjectionStateSchema, GitConfigSchema, GlobalSettingsManager, LocalModelAssetStateSchema, LocalModelLifecycleFileStateSchema, LocalModelLifecycleGroupStateSchema, LocalModelProfileManifestSchema, MarkdownParser, NotificationPublishInputSchema, NotificationSettingsSchema, OPENSPECUI_DOCUMENT_HOOK_VERSION, OPENSPECUI_WORKFLOW_HOOK_VERSION, OPSX_ARTIFACT_INPUT_ACTIONS, OPSX_CHANGE_INPUT_ACTIONS, OPSX_COMMAND_CAPABLE_WORKFLOWS, OPSX_CORE_PROFILE_WORKFLOWS, OPSX_TEXT_INPUT_ACTIONS, OpenSpecAdapter, OpenSpecDataHomeObserver, OpenSpecUIGlobalSettingsUpdateSchema, OpenSpecWatcher, OpsxConfigSchema, OpsxKernel, OwnedSpecIdentitySchema, PINNED_AGENT_GENERATOR_VERSION, PlanningCliProjectionDataSchema, PlanningCliProjectionSelectorSchema, PlanningCliProjectionStateSchema, ProjectBindingUpdateSchema, ProjectWatcher, PtyClientMessageSchema, RUNTIME_INVALIDATION_FACETS, ReactiveContext, ReactiveObservationEnvironment, ReactiveState, RuntimeInvalidationIndex, RuntimeRootInvalidationRegistry, ServiceTranslationEngineIdSchema, SpecIdentitySchema, StoreDoctorResultSchema, StoreListResultSchema, StoreMutationLifecycleEventSchema, StoreMutationStartResponseSchema, TRANSLATION_ENGINE_MANIFESTS, TerminalConfigSchema, TerminalControlParser, TerminalRendererEngineSchema, TranslationCacheReadInputSchema, TranslationCacheWriteInputSchema, TranslationEngineIdSchema, TranslationEngineLifecycleStatusSchema, acquireWatcher, applyAgentDeliveryPolicy, buildBackendHealthPayload, buildLocalDownloadPlanFromRepositoryFiles, buildRuntimePackageInstallCommand, checkLocalDirectionalModelLanguagePair, classifyStoreCliResult, clearCache, compareAndWriteAtomicPhysicalReactiveFile, computeEnvUri, constantTimeEqual, createCleanCliEnv, createPhysicalReactiveDirectory, createToolInitStateProjection, createTranslationEngineLifecycleStatus, detectRuntimePackageManager, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getExternalCodexCommandObservationRoot, getManagedLocalTranslationEngineManifest, getOpsxEntityRootRelativePath, getRootContextCliSelector, getToolInitStates, getTranslationEngineLifecycleMessage, getWatcherRuntimeStatus, inferFileMime, inferFilePreviewKind, inspectActiveRootOfficialConfig, inspectEnvironmentDefaultStore, inspectProjectBinding, isDirectionalManagedLocalTranslationEngineId, isManagedLocalTranslationEngineId, isTerminalMutationStatus, isTranslationEngineDependencyReady, isTranslationEngineRuntimeReady, loadOpenSpecAgentCommandContents, normalizeAgentDeliveryPolicy, normalizeBatchTranslationError, parseCliCommandResult, parseOpsxEntityMetadata, parseOpsxSchemaDetail, patchActiveRootOfficialFields, reactiveReadFile, reactiveStat, removePhysicalReactivePath, requireCanonicalOpenSpecEntityId, requireCanonicalOpenSpecSpecId, requireOpenSpecEntityRelativePath, resolveOpenSpecDataScope, resolveRootContext, resolveRuntimePackageInstallStrategy, resolveTerminalShellDefaults, runPhysicalReactivePathMutation, selectLocalDownloadGroup, shouldShowTranslationEngineInstallGate, sniffGlobalCli, subscribeWatcherRuntimeStatus, terminalNotificationEventToPublishInput, toCliProjectionCommandEvidence, toCliProjectionFailure, toCliProjectionNotice, toStoreFeatureResult, toggleMarkdownTask, updateProjectBindingContent, updateReactiveFileCache, validateActiveRootRawYaml, writePhysicalReactiveFile } from "@openspecui/core";
3
+ import { AI_TOOLS, ActiveRootMutationSchema, ActiveRootRevisionSchema, AgentDeliveryPolicyUpdateSchema, BatchTranslateInputSchema, CliExecutor, CliProjectionCommandError, CliProjectionNoticeSchema, CodeEditorThemeSchema, ConfigManager, CustomSoundHashSchema, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, DashboardGitSnapshotSchema, DashboardSummaryInvalidationSchema, DashboardSummaryProjectionStateSchema, DashboardTrendsProjectionSchema, DocumentTranslationConfigUpdateSchema, EnvironmentDefaultStoreUpdateSchema, EnvironmentGlobalConfigValueSchema, EnvironmentGlobalFileProjectionDataSchema, EnvironmentGlobalFileProjectionStateSchema, EnvironmentGlobalProjectionDataSchema, EnvironmentGlobalProjectionStateSchema, GitConfigSchema, GlobalSettingsManager, LocalModelAssetStateSchema, LocalModelLifecycleFileStateSchema, LocalModelLifecycleGroupStateSchema, LocalModelProfileManifestSchema, MarkdownParser, NotificationPublishInputSchema, NotificationSettingsSchema, OPENSPECUI_DOCUMENT_HOOK_VERSION, OPENSPECUI_WORKFLOW_HOOK_VERSION, OPSX_ARTIFACT_INPUT_ACTIONS, OPSX_CHANGE_INPUT_ACTIONS, OPSX_COMMAND_CAPABLE_WORKFLOWS, OPSX_CORE_PROFILE_WORKFLOWS, OPSX_TEXT_INPUT_ACTIONS, OpenSpecAdapter, OpenSpecDataHomeObserver, OpenSpecUIGlobalSettingsUpdateSchema, OpenSpecWatcher, OpsxConfigSchema, OpsxKernel, OwnedSpecIdentitySchema, PINNED_AGENT_GENERATOR_VERSION, PlanningCliProjectionDataSchema, PlanningCliProjectionSelectorSchema, PlanningCliProjectionStateSchema, ProjectBindingUpdateSchema, ProjectWatcher, PtyClientMessageSchema, RUNTIME_INVALIDATION_FACETS, ReactiveContext, ReactiveObservationEnvironment, ReactiveState, RuntimeInvalidationIndex, RuntimeRootInvalidationRegistry, ServiceTranslationEngineIdSchema, SpecIdentitySchema, StoreDoctorResultSchema, StoreListResultSchema, StoreMutationLifecycleEventSchema, StoreMutationStartResponseSchema, TRANSLATION_ENGINE_MANIFESTS, TerminalConfigSchema, TerminalControlParser, TerminalRendererEngineSchema, TranslationCacheReadInputSchema, TranslationCacheWriteInputSchema, TranslationEngineIdSchema, TranslationEngineLifecycleStatusSchema, acquireWatcher, applyAgentDeliveryPolicy, buildBackendHealthPayload, buildLocalDownloadPlanFromRepositoryFiles, buildRuntimePackageInstallCommand, checkLocalDirectionalModelLanguagePair, classifyStoreCliResult, clearCache, compareAndWriteAtomicPhysicalReactiveFile, computeEnvUri, constantTimeEqual, createCleanCliEnv, createPhysicalReactiveDirectory, createToolInitStateProjection, createTranslationEngineLifecycleStatus, detectRuntimePackageManager, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getExternalCodexCommandObservationRoot, getManagedLocalTranslationEngineManifest, getOpsxEntityRootRelativePath, getRootContextCliSelector, getToolInitStates, getTranslationEngineLifecycleMessage, getWatcherRuntimeStatus, inferFileMime, inferFilePreviewKind, inspectActiveRootOfficialConfig, inspectEnvironmentDefaultStore, inspectProjectBinding, isDirectionalManagedLocalTranslationEngineId, isManagedLocalTranslationEngineId, isTerminalMutationStatus, isTranslationEngineDependencyReady, isTranslationEngineRuntimeReady, loadOpenSpecAgentCommandContents, normalizeAgentDeliveryPolicy, normalizeBatchTranslationError, parseCliCommandResult, parseOpsxEntityMetadata, parseOpsxSchemaDetail, patchActiveRootOfficialFields, reactiveReadFile, reactiveStat, removePhysicalReactivePath, replaceFileAtomically, requireCanonicalOpenSpecEntityId, requireCanonicalOpenSpecSpecId, requireOpenSpecEntityRelativePath, resolveCommandInvocation, resolveOpenSpecDataScope, resolveRootContext, resolveRuntimePackageInstallStrategy, resolveTerminalShellDefaults, runPhysicalReactivePathMutation, selectLocalDownloadGroup, shouldShowTranslationEngineInstallGate, sniffGlobalCli, spawnSafe, subscribeWatcherRuntimeStatus, terminalNotificationEventToPublishInput, terminateChildProcessTree, toCliProjectionCommandEvidence, toCliProjectionFailure, toCliProjectionNotice, toStoreFeatureResult, toggleMarkdownTask, updateProjectBindingContent, updateReactiveFileCache, validateActiveRootRawYaml, writePhysicalReactiveFile } from "@openspecui/core";
4
4
  import { basename, dirname, extname, isAbsolute, join, matchesGlob, relative, resolve, sep } from "node:path";
5
- import { access, copyFile, lstat, mkdir, open, readFile, readlink, realpath, rename, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
5
+ import { access, copyFile, link, lstat, mkdir, open, readFile, readlink, realpath, rename, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
6
6
  import { fileURLToPath, pathToFileURL } from "node:url";
7
7
  import { downloadFile, fileDownloadInfo, listFiles, modelInfo } from "@huggingface/hub";
8
8
  import { observable } from "@trpc/server/observable";
9
9
  import { existsSync, readFileSync, realpathSync, statSync, write, writeFileSync } from "node:fs";
10
+ import { createHash, randomUUID } from "node:crypto";
10
11
  import { freemem, homedir, hostname, platform, totalmem } from "node:os";
11
12
  import { z } from "zod";
12
13
  import { Agent, EnvHttpProxyAgent, setGlobalDispatcher } from "undici";
@@ -20,7 +21,6 @@ import { cors } from "hono/cors";
20
21
  import { WebSocketServer } from "ws";
21
22
  import { Buffer as Buffer$1 } from "node:buffer";
22
23
  import { CustomSoundHashSchema as CustomSoundHashSchema$1, CustomSoundIdSchema, CustomSoundMetadataFileSchema, customHashFromSoundId, soundIdFromCustomHash } from "@openspecui/core/sounds";
23
- import { createHash, randomUUID } from "node:crypto";
24
24
  import { execFile, spawn } from "node:child_process";
25
25
  import { promisify } from "node:util";
26
26
  import { NotificationGroupKeySchema, NotificationPublishInputSchema as NotificationPublishInputSchema$1, getNotificationGroupKey } from "@openspecui/core/notifications";
@@ -502,7 +502,7 @@ var ProjectHookRuntime = class {
502
502
  }
503
503
  async loadFresh() {
504
504
  if (!await pathExists$1(this.hooksPath)) return {};
505
- const { tsImport } = await import("./api-B4zHqR-f.mjs");
505
+ const { tsImport } = await import("./api-C_H23KUr.mjs");
506
506
  return normalizeHooksModule(await tsImport(`${pathToFileURL(this.hooksPath).href}?t=${Date.now()}`, { parentURL: pathToFileURL(this.hooksPath).href }));
507
507
  }
508
508
  };
@@ -524,6 +524,14 @@ function buildHuggingFaceApiBaseUrl(endpoint) {
524
524
 
525
525
  //#endregion
526
526
  //#region src/local-model-asset-store.ts
527
+ /**
528
+ * Orthogonal intents (updated 2026-08-05 Asia/Shanghai):
529
+ * 1. Persist normalized local-model lifecycle state through complete JSON replacements.
530
+ * 2. Survive transient Windows target locks without exposing partial metadata.
531
+ * 3. Retire reactive caches only after the replacement commits.
532
+ *
533
+ * Original request (2026-08-05): Continue the Windows adaptation and fix equivalent failures together.
534
+ */
527
535
  const LocalModelAssetIndexSchema = LocalModelAssetStateSchema.array();
528
536
  var LocalModelAssetStore = class {
529
537
  constructor(options) {
@@ -549,9 +557,13 @@ var LocalModelAssetStore = class {
549
557
  const normalized = LocalModelAssetIndexSchema.parse([...states].sort((left, right) => left.modelId.localeCompare(right.modelId)));
550
558
  const serialized = JSON.stringify(normalized, null, 2);
551
559
  await mkdir(dirname(this.options.indexPath), { recursive: true });
552
- const tempPath = `${this.options.indexPath}.${process.pid}.${Date.now()}.tmp`;
553
- await writeFile(tempPath, `${serialized}\n`, "utf8");
554
- await rename(tempPath, this.options.indexPath);
560
+ const tempPath = `${this.options.indexPath}.${process.pid}.${randomUUID()}.tmp`;
561
+ try {
562
+ await writeFile(tempPath, `${serialized}\n`, "utf8");
563
+ await replaceFileAtomically(tempPath, this.options.indexPath);
564
+ } finally {
565
+ await rm(tempPath, { force: true }).catch(() => void 0);
566
+ }
555
567
  clearCache();
556
568
  }
557
569
  async upsert(state) {
@@ -759,6 +771,14 @@ function getTransformersFileCacheModelPath(cacheDir, modelId) {
759
771
 
760
772
  //#endregion
761
773
  //#region src/local-model-profile-manifest-store.ts
774
+ /**
775
+ * Orthogonal intents (updated 2026-08-05 Asia/Shanghai):
776
+ * 1. Persist normalized local-model profile manifests through complete JSON replacements.
777
+ * 2. Survive transient Windows target locks without exposing partial metadata.
778
+ * 3. Retire reactive caches only after the replacement commits.
779
+ *
780
+ * Original request (2026-08-05): Continue the Windows adaptation and fix equivalent failures together.
781
+ */
762
782
  const LocalModelProfileManifestIndexSchema = z.object({
763
783
  version: z.literal(1).default(1),
764
784
  manifests: z.array(LocalModelProfileManifestSchema).default([])
@@ -786,9 +806,13 @@ var LocalModelProfileManifestStore = class {
786
806
  });
787
807
  const serialized = JSON.stringify(normalized, null, 2);
788
808
  await mkdir(dirname(this.options.manifestPath), { recursive: true });
789
- const tempPath = `${this.options.manifestPath}.${process.pid}.${Date.now()}.tmp`;
790
- await writeFile(tempPath, `${serialized}\n`, "utf8");
791
- await rename(tempPath, this.options.manifestPath);
809
+ const tempPath = `${this.options.manifestPath}.${process.pid}.${randomUUID()}.tmp`;
810
+ try {
811
+ await writeFile(tempPath, `${serialized}\n`, "utf8");
812
+ await replaceFileAtomically(tempPath, this.options.manifestPath);
813
+ } finally {
814
+ await rm(tempPath, { force: true }).catch(() => void 0);
815
+ }
792
816
  clearCache();
793
817
  }
794
818
  async upsert(manifest) {
@@ -1390,6 +1414,16 @@ function isRecord$2(value) {
1390
1414
 
1391
1415
  //#endregion
1392
1416
  //#region src/local-model-asset-service.ts
1417
+ /**
1418
+ * Orthogonal intents (updated 2026-08-09 Asia/Shanghai):
1419
+ * 1. Resolve local translation model catalogs and versioned profile manifests.
1420
+ * 2. Own resumable download, pause, cancellation, retry, progress, and bounded Windows finalization.
1421
+ * 3. Materialize downloaded profiles and platform-appropriate Hugging Face cache pointers.
1422
+ * 4. Preserve selected-group authority while background work settles.
1423
+ * 5. Publish typed lifecycle snapshots and bounded diagnostics.
1424
+ *
1425
+ * Original request (2026-08-04): "Adapt macOS-first development and runtime paths to Windows."
1426
+ */
1393
1427
  const DEFAULT_NETWORK_RETRY_LIMIT$2 = Number.POSITIVE_INFINITY;
1394
1428
  const DEFAULT_NETWORK_RETRY_DELAY_MS$2 = 500;
1395
1429
  const DEFAULT_NETWORK_RETRY_DELAY_MAX_MS$2 = 5e3;
@@ -2742,10 +2776,10 @@ async function streamDownloadToIncompleteFile(input) {
2742
2776
  }
2743
2777
  return true;
2744
2778
  }
2779
+ /** Commit one complete model file without deleting the previous target before replacement succeeds. */
2745
2780
  async function finalizeDownloadedFile$1(input) {
2746
2781
  await mkdir(dirname(input.targetPath), { recursive: true });
2747
- await rm(input.targetPath, { force: true });
2748
- await rename(input.incompletePath, input.targetPath);
2782
+ await replaceFileAtomically(input.incompletePath, input.targetPath);
2749
2783
  }
2750
2784
  async function mirrorDownloadedFileToHubCache(input) {
2751
2785
  await mkdir(dirname(input.cachePaths.blobPath), { recursive: true });
@@ -2753,7 +2787,8 @@ async function mirrorDownloadedFileToHubCache(input) {
2753
2787
  await rm(input.cachePaths.blobPath, { force: true });
2754
2788
  await copyFile(input.targetPath, input.cachePaths.blobPath);
2755
2789
  await unlink(input.cachePaths.pointerPath).catch(() => void 0);
2756
- await symlink(input.cachePaths.blobPath, input.cachePaths.pointerPath);
2790
+ if (process.platform === "win32") await link(input.cachePaths.blobPath, input.cachePaths.pointerPath);
2791
+ else await symlink(input.cachePaths.blobPath, input.cachePaths.pointerPath);
2757
2792
  }
2758
2793
  function getHubCacheFilePaths(input) {
2759
2794
  const repoPath = getHubCacheRepoPath(input.cacheDir, input.modelId);
@@ -6504,7 +6539,10 @@ var EnvironmentGlobalProjectionService = class {
6504
6539
  * 2. Distinguish a valid non-repository path from an identity-command failure.
6505
6540
  * 3. Canonicalize repository paths for binding identity and stale-intent checks.
6506
6541
  * 4. Propagate cooperative cancellation into Git subprocess ownership.
6542
+ * 5. Hide the Git subprocess console window (`windowsHide`) so Git executed by a console-less
6543
+ * Windows daemon never flashes a cmd window.
6507
6544
  *
6545
+ * Original request (2026-08-14): "在Windows平台上,执行命令总是会弹出cmd窗口,这个可否统一隐藏,你先调查一下原因"
6508
6546
  * Original request (2026-07-19): "代码已经提交,开始review。如果有问题,那么可更新change。"
6509
6547
  * Derived requirement (2026-07-19): Checkpoint 6.11 preserves objective Planning Git failures.
6510
6548
  * Original request (2026-07-31): "Code Git Snapshot,它非常慢,有时候甚至要十几秒"
@@ -6521,7 +6559,8 @@ async function defaultRunGit(cwd, args, signal) {
6521
6559
  cwd,
6522
6560
  encoding: "utf8",
6523
6561
  maxBuffer: 8 * 1024 * 1024,
6524
- signal
6562
+ signal,
6563
+ windowsHide: true
6525
6564
  });
6526
6565
  return {
6527
6566
  ok: true,
@@ -9076,7 +9115,10 @@ async function buildDashboardGitSnapshot(options) {
9076
9115
  * 2. Preserve backend-issued Code binding provenance and observable Git task lifecycle.
9077
9116
  * 3. Trigger explicit Git snapshot invalidation through a reactive stamp without broad Dashboard reloads.
9078
9117
  * 4. Propagate Projection Work cancellation into the Git subprocess tree.
9118
+ * 5. Hide Git identity-probe subprocess console windows (`windowsHide`) under the console-less
9119
+ * Windows daemon.
9079
9120
  *
9121
+ * Original request (2026-08-14): "在Windows平台上,执行命令总是会弹出cmd窗口,这个可否统一隐藏,你先调查一下原因"
9080
9122
  * Original request (2026-07-23): "现在页面数据的加载数据非常慢(比如dashboard页面、changes页面都要等待非常久,页面刷新后,似乎后台没有缓存一样,也要加载很久。"
9081
9123
  * Derived requirement (2026-07-25): P4.3 settles a cached refresh stamp before Dashboard Git refresh returns.
9082
9124
  * Original request (2026-07-31): "Code Git Snapshot,它非常慢,有时候甚至要十几秒"
@@ -9127,7 +9169,8 @@ async function resolveGitMetadataDir(projectDir) {
9127
9169
  const { stdout } = await execFileAsync$1("git", ["rev-parse", "--git-dir"], {
9128
9170
  cwd: projectDir,
9129
9171
  maxBuffer: 1024 * 1024,
9130
- encoding: "utf8"
9172
+ encoding: "utf8",
9173
+ windowsHide: true
9131
9174
  });
9132
9175
  const gitDirRaw = stdout.trim();
9133
9176
  if (!gitDirRaw) return null;
@@ -9667,12 +9710,13 @@ function resolveEntityEntryPath(input) {
9667
9710
  //#endregion
9668
9711
  //#region src/file-preview-service.ts
9669
9712
  /**
9670
- * Orthogonal intents (updated 2026-07-16 Asia/Shanghai):
9713
+ * Orthogonal intents (updated 2026-08-04 Asia/Shanghai):
9671
9714
  * 1. Prepare source-bound preview sessions for supported entity files.
9672
- * 2. Serve preview documents and bundled assets with confined path resolution.
9715
+ * 2. Serve preview documents and bundled assets with platform-neutral confined path resolution.
9673
9716
  * 3. Retire every preview session when its owning Planning root is replaced.
9674
9717
  *
9675
9718
  * Original request (2026-07-16): "A -> B 后必须清退 A 的 preview。"
9719
+ * Original request (2026-08-04): "Make pnpm openspecui start and equivalent package scripts work on Windows."
9676
9720
  */
9677
9721
  const PREVIEW_ENTRY_FILE_BY_KIND = {
9678
9722
  image: "image-preview.html",
@@ -9696,6 +9740,10 @@ function toHash(input) {
9696
9740
  function stripLeadingSlash(path) {
9697
9741
  return path.replace(/^\/+/, "");
9698
9742
  }
9743
+ function isPathInsideRoot(rootPath, candidatePath) {
9744
+ const relativePath$1 = relative(rootPath, candidatePath);
9745
+ return relativePath$1 === "" || relativePath$1 !== ".." && !relativePath$1.startsWith(`..${sep}`) && !isAbsolute(relativePath$1);
9746
+ }
9699
9747
  function inferPreviewAssetContentType(path) {
9700
9748
  switch (extname(path).toLowerCase()) {
9701
9749
  case ".html": return "text/html";
@@ -9773,7 +9821,7 @@ var FilePreviewService = class {
9773
9821
  const normalized = stripLeadingSlash(requestPath);
9774
9822
  if (session.previewKind === "html") {
9775
9823
  const absolutePath$1 = resolve(session.directoryPath, normalized);
9776
- if (!absolutePath$1.startsWith(session.directoryPath + "/")) return null;
9824
+ if (!isPathInsideRoot(session.directoryPath, absolutePath$1)) return null;
9777
9825
  if (!existsSync(absolutePath$1) || !statSync(absolutePath$1).isFile()) return null;
9778
9826
  return {
9779
9827
  content: readFileSync(absolutePath$1),
@@ -9783,7 +9831,7 @@ var FilePreviewService = class {
9783
9831
  if (normalized.startsWith("resource/")) {
9784
9832
  const resourcePath = normalized.slice(9);
9785
9833
  const absolutePath$1 = resolve(session.directoryPath, resourcePath);
9786
- if (!absolutePath$1.startsWith(session.directoryPath + "/")) return null;
9834
+ if (!isPathInsideRoot(session.directoryPath, absolutePath$1)) return null;
9787
9835
  if (!existsSync(absolutePath$1) || !statSync(absolutePath$1).isFile()) return null;
9788
9836
  return {
9789
9837
  content: readFileSync(absolutePath$1),
@@ -9793,7 +9841,7 @@ var FilePreviewService = class {
9793
9841
  const assetName = normalized || session.entryFileName;
9794
9842
  if (!assetName) return null;
9795
9843
  const absolutePath = resolve(this.previewAssetsDir, assetName);
9796
- if (!absolutePath.startsWith(resolve(this.previewAssetsDir) + "/")) return null;
9844
+ if (!isPathInsideRoot(this.previewAssetsDir, absolutePath)) return null;
9797
9845
  if (!existsSync(absolutePath) || !statSync(absolutePath).isFile()) return null;
9798
9846
  if (isRewritablePreviewAsset(assetName)) {
9799
9847
  const rewritten = rewritePreviewAssetPaths(readFileSync(absolutePath, "utf8"), hash);
@@ -11828,11 +11876,12 @@ var PtyInputWriter = class {
11828
11876
  //#endregion
11829
11877
  //#region src/pty-manager.ts
11830
11878
  /**
11831
- * Orthogonal intents (updated 2026-07-22 Asia/Shanghai):
11879
+ * Orthogonal intents (updated 2026-08-09 Asia/Shanghai):
11832
11880
  * 1. Own PTY session process, ordered input, buffer, title, and lifecycle state.
11833
11881
  * 2. Spawn each session at an explicitly resolved launch-project or planning-root cwd and retain its immutable Root generation.
11834
11882
  * 3. Preserve the inherited backend environment, including XDG_DATA_HOME, across cwd targets.
11835
11883
  * 4. List and close server-owned sessions for reconnect and teardown.
11884
+ * 5. Resolve Windows Agent launchers to native argv while retaining the requested display command.
11836
11885
  *
11837
11886
  * Original request (2026-07-16): "3.8 Terminal exposes explicit launch-project cwd and planning-root cwd while preserving inherited XDG_DATA_HOME"
11838
11887
  * Owner-reported defect (2026-07-21): Pre-created Agent terminals are absent from Compose Send.
@@ -11919,7 +11968,11 @@ var PtySession = class extends EventEmitter$1 {
11919
11968
  this.rootGeneration = opts.rootGeneration;
11920
11969
  this.maxBufferLines = opts.scrollback ?? DEFAULT_SCROLLBACK;
11921
11970
  this.maxBufferBytes = opts.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;
11922
- this.process = pty.spawn(this.command, this.args, {
11971
+ const invocation = resolveCommandInvocation(this.command, this.args, {
11972
+ cwd: opts.cwd,
11973
+ env: process.env
11974
+ });
11975
+ this.process = pty.spawn(invocation.command, invocation.args, {
11923
11976
  name: "xterm-256color",
11924
11977
  cols: opts.cols ?? 80,
11925
11978
  rows: opts.rows ?? 24,
@@ -17774,10 +17827,12 @@ var TranslationCacheService = class {
17774
17827
  //#endregion
17775
17828
  //#region src/runtime-package-host.ts
17776
17829
  /**
17777
- * Orthogonal intents (updated 2026-07-31 Asia/Shanghai):
17830
+ * Original request (2026-08-04): "The project was developed on macOS and now needs Windows adaptation."
17831
+ * Orthogonal intents (updated 2026-08-08 Asia/Shanghai):
17778
17832
  * 1. Resolve the physical host manifest that owns on-demand runtime installation.
17779
17833
  * 2. Read declared runtime ranges without treating optional peers as install-time dependencies.
17780
17834
  * 3. Normalize legacy on-demand optional dependency records after package-manager installation.
17835
+ * 4. Execute the npm dependency probe through Core's shell-independent Windows-safe spawn owner.
17781
17836
  *
17782
17837
  * Original request (2026-07-31): "这个依赖好像会导致安装的时候仍然会被强制装上去,可能要改成 peerDependencies 会更好"
17783
17838
  */
@@ -17814,7 +17869,7 @@ function readRuntimeHostPackageDependencyRequest(input) {
17814
17869
  return `${input.packageName}@${range}`;
17815
17870
  }
17816
17871
  async function readRuntimeHostPackageDependencyTree(input) {
17817
- const child = spawn("npm", [
17872
+ const started = spawnSafe("npm", [
17818
17873
  "list",
17819
17874
  "--json",
17820
17875
  "--omit=dev",
@@ -17822,9 +17877,10 @@ async function readRuntimeHostPackageDependencyTree(input) {
17822
17877
  ...input.packageNames
17823
17878
  ], {
17824
17879
  cwd: input.runtimeHost.packageDir,
17825
- shell: false,
17826
17880
  env: createCleanCliEnv()
17827
17881
  });
17882
+ if (!started.ok) throw new Error(`Unable to start npm list: ${started.error.message}`);
17883
+ const { child } = started;
17828
17884
  const stdoutChunks = [];
17829
17885
  const stderrChunks = [];
17830
17886
  const exitCode = await new Promise((resolve$1, reject) => {
@@ -18066,6 +18122,17 @@ function buildRuntimeConfig(engineId, profile) {
18066
18122
 
18067
18123
  //#endregion
18068
18124
  //#region src/translation-engine-service.ts
18125
+ /**
18126
+ * Orthogonal intents (updated 2026-08-09 Asia/Shanghai):
18127
+ * 1. Project translation-engine inventory, selection, lifecycle, and immutable settings snapshots.
18128
+ * 2. Install and cancel managed local runtime dependencies through observable, bounded process settlement.
18129
+ * 3. Own local model search, download plans, persisted asset readiness, and profile validation.
18130
+ * 4. Execute browser, OpenAI, and managed-local batch translation with cache-aware events.
18131
+ * 5. Preserve CT2/llama runtime policy, directional language constraints, and resource limits.
18132
+ *
18133
+ * Original request (2026-05-22): "Server provides engine registry, installation sessions, installation logs, selection, and translation."
18134
+ * Original request (2026-08-04): "Make pnpm openspecui start and equivalent package scripts work on Windows."
18135
+ */
18069
18136
  const __dirname$2 = dirname(fileURLToPath(import.meta.url));
18070
18137
  var TranslationEngineService = class {
18071
18138
  projectDir;
@@ -18796,55 +18863,82 @@ async function installManagedLocalRuntime(engineId, input) {
18796
18863
  input.onLifecycle?.(finalLifecycle);
18797
18864
  return finalLifecycle;
18798
18865
  }
18866
+ /** Run one package-manager installation with bounded cross-platform tree cancellation. */
18799
18867
  async function runRuntimeInstallCommand(input) {
18800
- let child;
18801
- try {
18802
- child = spawn(input.command.cmd, input.command.args, {
18803
- cwd: input.cwd,
18804
- shell: false,
18805
- env: createCleanCliEnv()
18806
- });
18807
- } catch (error) {
18808
- return error instanceof Error ? error.message : String(error);
18809
- }
18810
- const abort = () => {
18811
- try {
18812
- child.kill();
18813
- } catch {}
18814
- };
18815
- input.signal?.addEventListener("abort", abort, { once: true });
18816
- try {
18817
- await new Promise((resolve$1, reject) => {
18818
- child.stdout?.on("data", (data) => {
18819
- input.onLog?.({
18820
- stream: "stdout",
18821
- text: data.toString()
18822
- });
18823
- });
18824
- child.stderr?.on("data", (data) => {
18825
- input.onLog?.({
18826
- stream: "stderr",
18827
- text: data.toString()
18828
- });
18829
- });
18830
- child.on("error", (error) => {
18831
- reject(error);
18868
+ if (input.signal?.aborted) return "Translation runtime installation cancelled.";
18869
+ const started = spawnSafe(input.command.cmd, input.command.args, {
18870
+ cwd: input.cwd,
18871
+ shell: false,
18872
+ env: createCleanCliEnv()
18873
+ });
18874
+ if (!started.ok) return started.error.message;
18875
+ const { child } = started;
18876
+ const completion = new Promise((resolve$1) => {
18877
+ child.stdout?.on("data", (data) => {
18878
+ input.onLog?.({
18879
+ stream: "stdout",
18880
+ text: data.toString()
18832
18881
  });
18833
- child.on("close", (exitCode) => {
18834
- if (exitCode === 0) {
18835
- resolve$1();
18836
- return;
18837
- }
18838
- reject(/* @__PURE__ */ new Error(`${input.command.displayCommand} exited with code ${exitCode ?? "unknown"}.`));
18882
+ });
18883
+ child.stderr?.on("data", (data) => {
18884
+ input.onLog?.({
18885
+ stream: "stderr",
18886
+ text: data.toString()
18839
18887
  });
18840
18888
  });
18841
- return null;
18842
- } catch (error) {
18843
- return error instanceof Error ? error.message : String(error);
18889
+ child.once("error", (error) => resolve$1(error.message));
18890
+ child.once("close", (exitCode) => {
18891
+ resolve$1(exitCode === 0 ? null : `${input.command.displayCommand} exited with code ${exitCode ?? "unknown"}.`);
18892
+ });
18893
+ });
18894
+ let requestAbort = null;
18895
+ const abortRequested = new Promise((resolve$1) => {
18896
+ requestAbort = resolve$1;
18897
+ });
18898
+ const abort = () => requestAbort?.();
18899
+ input.signal?.addEventListener("abort", abort, { once: true });
18900
+ try {
18901
+ const outcome = await Promise.race([completion.then((result) => ({
18902
+ kind: "complete",
18903
+ result
18904
+ })), abortRequested.then(() => ({ kind: "abort" }))]);
18905
+ if (outcome.kind === "complete") return outcome.result;
18906
+ return settleRuntimeInstallCancellation(child, completion, input.terminationTimeoutMs ?? 5e3);
18844
18907
  } finally {
18845
18908
  input.signal?.removeEventListener("abort", abort);
18846
18909
  }
18847
18910
  }
18911
+ async function settleRuntimeInstallCancellation(child, completion, timeoutMs) {
18912
+ const failures = [];
18913
+ const terminate = async (signal) => {
18914
+ let timer = null;
18915
+ try {
18916
+ await Promise.race([terminateChildProcessTree(child, signal), new Promise((_resolve, reject) => {
18917
+ timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Timed out requesting ${signal} for runtime installation.`)), timeoutMs);
18918
+ })]);
18919
+ } catch (error) {
18920
+ failures.push(error);
18921
+ } finally {
18922
+ if (timer) clearTimeout(timer);
18923
+ }
18924
+ };
18925
+ const waitForCompletion = async () => {
18926
+ let timer = null;
18927
+ try {
18928
+ return await Promise.race([completion.then(() => true), new Promise((resolve$1) => {
18929
+ timer = setTimeout(() => resolve$1(false), timeoutMs);
18930
+ })]);
18931
+ } finally {
18932
+ if (timer) clearTimeout(timer);
18933
+ }
18934
+ };
18935
+ await terminate("SIGTERM");
18936
+ if (await waitForCompletion()) return "Translation runtime installation cancelled.";
18937
+ await terminate("SIGKILL");
18938
+ if (await waitForCompletion()) return "Translation runtime installation cancelled.";
18939
+ const failureText = failures.map((error) => error instanceof Error ? error.message : String(error)).join(" ");
18940
+ return `Translation runtime installation cancellation did not settle.${failureText ? ` ${failureText}` : ""}`;
18941
+ }
18848
18942
  function getManagedLocalRuntimeSpec(engineId) {
18849
18943
  if (engineId === "local") return {
18850
18944
  packageNames: ["@huggingface/transformers", "onnxruntime-node"],
@@ -18957,7 +19051,9 @@ function readCurrentRuntimeMemory() {
18957
19051
  * 2. Execute translation engines in isolated worker-thread or process hosts.
18958
19052
  * 3. Enforce cancellation and process memory limits while preserving batch evidence.
18959
19053
  * 4. Route worker-thread payloads by an explicit translation protocol kind.
19054
+ * 5. Hide the process-host bootstrap console window (`windowsHide`) under a console-less Windows daemon.
18960
19055
  *
19056
+ * Original request (2026-08-14): "在Windows平台上,执行命令总是会弹出cmd窗口,这个可否统一隐藏,你先调查一下原因"
18961
19057
  * Original request (2026-07-24): "Production worker kinds must not claim each other's payloads."
18962
19058
  */
18963
19059
  const execFileAsync = promisify(execFile);
@@ -19253,7 +19349,8 @@ function createManagedLocalTranslationProcess(input) {
19253
19349
  "inherit",
19254
19350
  "inherit",
19255
19351
  "ipc"
19256
- ]
19352
+ ],
19353
+ windowsHide: true
19257
19354
  });
19258
19355
  }
19259
19356
  function resolveProcessEntryUrl() {
@@ -19410,11 +19507,11 @@ if (!isMainThread) {
19410
19507
  //#endregion
19411
19508
  //#region src/server.ts
19412
19509
  /**
19413
- * Orthogonal intents (updated 2026-08-01 Asia/Shanghai):
19510
+ * Orthogonal intents (updated 2026-08-04 Asia/Shanghai):
19414
19511
  * 1. Bootstrap HTTP/tRPC, WebSocket, PTY, and launch-project runtime services.
19415
19512
  * 2. Delegate planning-root, Store, Environment Global, Agent, and Root Context projection ownership.
19416
19513
  * 3. Host notifications, sound, preview-resource, translation, and telemetry boundaries.
19417
- * 4. Admit dynamic loopback App origins and perform deterministic runtime teardown.
19514
+ * 4. Admit dynamic loopback App origins and await deterministic native-handle teardown.
19418
19515
  * 5. Bridge settled machine-global config changes into Root Context replacement.
19419
19516
  *
19420
19517
  * Original request (2026-07-15): "你先负责后端(内核)的开发。"
@@ -19936,7 +20033,7 @@ async function createWebSocketServer(server, httpServer, config) {
19936
20033
  () => ptyManager.closeAll(),
19937
20034
  () => closeWebSocketServer(ptyWss),
19938
20035
  () => closeWebSocketServer(wss),
19939
- () => server.watcher?.stop()
20036
+ () => server.watcher?.close()
19940
20037
  ]);
19941
20038
  await settleCleanupPhase(failures, [() => server.storeObservationFallback.dispose()]);
19942
20039
  await settleCleanupPhase(failures, [() => server.storeMutationService.dispose()]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openspecui/server",
3
- "version": "7.0.1",
3
+ "version": "7.0.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.mjs",
6
6
  "exports": {
@@ -25,7 +25,7 @@
25
25
  "typecheck:nested-spec-tests": "tsc -p tsconfig.nested-spec-tests.json --noEmit",
26
26
  "typecheck:project-initialization-tests": "tsc -p tsconfig.project-initialization-tests.json --noEmit",
27
27
  "typecheck:default-store-tests": "tsc -p tsconfig.default-store-tests.json --noEmit",
28
- "dev": "NODE_OPTIONS=\"${NODE_OPTIONS:+$NODE_OPTIONS }--conditions=development\" tsx watch --include '../core/dist/**' --include '../search/dist/**' src/standalone.ts",
28
+ "dev": "tsx watch --conditions=development --include '../core/dist/**' --include '../search/dist/**' src/standalone.ts",
29
29
  "test": "vitest run",
30
30
  "test:watch": "vitest"
31
31
  },
@@ -33,8 +33,8 @@
33
33
  "@hono/node-server": "^1.14.1",
34
34
  "@huggingface/hub": "^2.12.0",
35
35
  "@lydell/node-pty": "^1.1.0",
36
- "@openspecui/core": "7.0.1",
37
- "@openspecui/search": "7.0.1",
36
+ "@openspecui/core": "7.0.2",
37
+ "@openspecui/search": "7.0.2",
38
38
  "@opentelemetry/api": "^1.9.1",
39
39
  "@opentelemetry/exporter-trace-otlp-http": "^0.221.0",
40
40
  "@opentelemetry/sdk-node": "^0.221.0",