@openspecui/core 1.1.0 → 1.2.0

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.
package/dist/index.d.mts CHANGED
@@ -17,13 +17,13 @@ declare const ChangeFileSchema: z.ZodObject<{
17
17
  /** Optional byte size for files */
18
18
  size: z.ZodOptional<z.ZodNumber>;
19
19
  }, "strip", z.ZodTypeAny, {
20
- type: "file" | "directory";
21
20
  path: string;
21
+ type: "file" | "directory";
22
22
  content?: string | undefined;
23
23
  size?: number | undefined;
24
24
  }, {
25
- type: "file" | "directory";
26
25
  path: string;
26
+ type: "file" | "directory";
27
27
  content?: string | undefined;
28
28
  size?: number | undefined;
29
29
  }>;
@@ -1673,14 +1673,14 @@ declare const ArtifactStatusSchema: z.ZodObject<{
1673
1673
  missingDeps: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
1674
1674
  relativePath: z.ZodOptional<z.ZodString>;
1675
1675
  }, "strip", z.ZodTypeAny, {
1676
- status: "done" | "ready" | "blocked";
1677
1676
  id: string;
1677
+ status: "done" | "ready" | "blocked";
1678
1678
  outputPath: string;
1679
1679
  missingDeps?: string[] | undefined;
1680
1680
  relativePath?: string | undefined;
1681
1681
  }, {
1682
- status: "done" | "ready" | "blocked";
1683
1682
  id: string;
1683
+ status: "done" | "ready" | "blocked";
1684
1684
  outputPath: string;
1685
1685
  missingDeps?: string[] | undefined;
1686
1686
  relativePath?: string | undefined;
@@ -1698,14 +1698,14 @@ declare const ChangeStatusSchema: z.ZodObject<{
1698
1698
  missingDeps: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
1699
1699
  relativePath: z.ZodOptional<z.ZodString>;
1700
1700
  }, "strip", z.ZodTypeAny, {
1701
- status: "done" | "ready" | "blocked";
1702
1701
  id: string;
1702
+ status: "done" | "ready" | "blocked";
1703
1703
  outputPath: string;
1704
1704
  missingDeps?: string[] | undefined;
1705
1705
  relativePath?: string | undefined;
1706
1706
  }, {
1707
- status: "done" | "ready" | "blocked";
1708
1707
  id: string;
1708
+ status: "done" | "ready" | "blocked";
1709
1709
  outputPath: string;
1710
1710
  missingDeps?: string[] | undefined;
1711
1711
  relativePath?: string | undefined;
@@ -1716,8 +1716,8 @@ declare const ChangeStatusSchema: z.ZodObject<{
1716
1716
  isComplete: boolean;
1717
1717
  applyRequires: string[];
1718
1718
  artifacts: {
1719
- status: "done" | "ready" | "blocked";
1720
1719
  id: string;
1720
+ status: "done" | "ready" | "blocked";
1721
1721
  outputPath: string;
1722
1722
  missingDeps?: string[] | undefined;
1723
1723
  relativePath?: string | undefined;
@@ -1728,8 +1728,8 @@ declare const ChangeStatusSchema: z.ZodObject<{
1728
1728
  isComplete: boolean;
1729
1729
  applyRequires: string[];
1730
1730
  artifacts: {
1731
- status: "done" | "ready" | "blocked";
1732
1731
  id: string;
1732
+ status: "done" | "ready" | "blocked";
1733
1733
  outputPath: string;
1734
1734
  missingDeps?: string[] | undefined;
1735
1735
  relativePath?: string | undefined;
@@ -1742,13 +1742,13 @@ declare const DependencyInfoSchema: z.ZodObject<{
1742
1742
  path: z.ZodString;
1743
1743
  description: z.ZodString;
1744
1744
  }, "strip", z.ZodTypeAny, {
1745
- path: string;
1746
1745
  id: string;
1746
+ path: string;
1747
1747
  description: string;
1748
1748
  done: boolean;
1749
1749
  }, {
1750
- path: string;
1751
1750
  id: string;
1751
+ path: string;
1752
1752
  description: string;
1753
1753
  done: boolean;
1754
1754
  }>;
@@ -1856,13 +1856,13 @@ declare const ArtifactInstructionsSchema: z.ZodObject<{
1856
1856
  path: z.ZodString;
1857
1857
  description: z.ZodString;
1858
1858
  }, "strip", z.ZodTypeAny, {
1859
- path: string;
1860
1859
  id: string;
1860
+ path: string;
1861
1861
  description: string;
1862
1862
  done: boolean;
1863
1863
  }, {
1864
- path: string;
1865
1864
  id: string;
1865
+ path: string;
1866
1866
  description: string;
1867
1867
  done: boolean;
1868
1868
  }>, "many">;
@@ -1876,8 +1876,8 @@ declare const ArtifactInstructionsSchema: z.ZodObject<{
1876
1876
  artifactId: string;
1877
1877
  template: string;
1878
1878
  dependencies: {
1879
- path: string;
1880
1879
  id: string;
1880
+ path: string;
1881
1881
  description: string;
1882
1882
  done: boolean;
1883
1883
  }[];
@@ -1894,8 +1894,8 @@ declare const ArtifactInstructionsSchema: z.ZodObject<{
1894
1894
  artifactId: string;
1895
1895
  template: string;
1896
1896
  dependencies: {
1897
- path: string;
1898
1897
  id: string;
1898
+ path: string;
1899
1899
  description: string;
1900
1900
  done: boolean;
1901
1901
  }[];
@@ -1937,16 +1937,16 @@ declare const SchemaResolutionSchema: z.ZodObject<{
1937
1937
  source: "project" | "user" | "package";
1938
1938
  }>, "many">;
1939
1939
  }, "strip", z.ZodTypeAny, {
1940
- path: string;
1941
1940
  name: string;
1941
+ path: string;
1942
1942
  source: "project" | "user" | "package";
1943
1943
  shadows: {
1944
1944
  path: string;
1945
1945
  source: "project" | "user" | "package";
1946
1946
  }[];
1947
1947
  }, {
1948
- path: string;
1949
1948
  name: string;
1949
+ path: string;
1950
1950
  source: "project" | "user" | "package";
1951
1951
  shadows: {
1952
1952
  path: string;
@@ -2157,6 +2157,8 @@ declare class OpsxKernel {
2157
2157
  private readonly projectDir;
2158
2158
  private readonly cliExecutor;
2159
2159
  private readonly controller;
2160
+ private warmupPromise;
2161
+ private readonly _streamReady;
2160
2162
  private _statusList;
2161
2163
  private _schemas;
2162
2164
  private _changeIds;
@@ -2176,6 +2178,8 @@ declare class OpsxKernel {
2176
2178
  private _entityControllers;
2177
2179
  constructor(projectDir: string, cliExecutor: CliExecutor);
2178
2180
  warmup(): Promise<void>;
2181
+ waitForWarmup(): Promise<void>;
2182
+ private runWarmup;
2179
2183
  dispose(): void;
2180
2184
  getStatusList(): ChangeStatus[];
2181
2185
  getSchemas(): SchemaInfo[];
@@ -2187,18 +2191,22 @@ declare class OpsxKernel {
2187
2191
  getInstructions(changeId: string, artifact: string, schema?: string): ArtifactInstructions;
2188
2192
  getApplyInstructions(changeId: string, schema?: string): ApplyInstructions;
2189
2193
  getSchemaResolution(name: string): SchemaResolution;
2194
+ peekSchemaResolution(name: string): SchemaResolution | null;
2190
2195
  getSchemaDetail(name: string): SchemaDetail;
2196
+ peekSchemaDetail(name: string): SchemaDetail | null;
2191
2197
  getSchemaFiles(name: string): ChangeFile[];
2192
2198
  getSchemaYaml(name: string): string | null;
2193
2199
  getChangeMetadata(changeId: string): string | null;
2194
2200
  getArtifactOutput(changeId: string, outputPath: string): string | null;
2195
2201
  getGlobArtifactFiles(changeId: string, outputPath: string): GlobArtifactFile[];
2196
2202
  private startStream;
2203
+ private startStreamOnce;
2197
2204
  private warmupSchema;
2198
2205
  private warmupChange;
2199
2206
  private watchSchemaChanges;
2200
2207
  private watchChangeIdChanges;
2201
2208
  private teardownEntity;
2209
+ private clearStreamReadyByPrefix;
2202
2210
  private fetchSchemas;
2203
2211
  private fetchChangeIds;
2204
2212
  private fetchProjectConfig;
@@ -2214,22 +2222,22 @@ declare class OpsxKernel {
2214
2222
  private fetchTemplateContents;
2215
2223
  private fetchChangeMetadata;
2216
2224
  private fetchArtifactOutput;
2217
- /**
2218
- * Ensure a per-change status stream exists. If not yet warmed up,
2219
- * creates the state and starts a stream lazily.
2220
- */
2221
- ensureStatus(changeId: string, schema?: string): void;
2222
- ensureInstructions(changeId: string, artifact: string, schema?: string): void;
2223
- ensureApplyInstructions(changeId: string, schema?: string): void;
2224
- ensureArtifactOutput(changeId: string, outputPath: string): void;
2225
- ensureGlobArtifactFiles(changeId: string, outputPath: string): void;
2226
- ensureSchemaResolution(name: string): void;
2227
- ensureSchemaDetail(name: string): void;
2228
- ensureSchemaFiles(name: string): void;
2229
- ensureSchemaYaml(name: string): void;
2230
- ensureTemplates(schema?: string): void;
2231
- ensureTemplateContents(schema?: string): void;
2232
- ensureChangeMetadata(changeId: string): void;
2225
+ ensureSchemas(): Promise<void>;
2226
+ ensureChangeIds(): Promise<void>;
2227
+ ensureProjectConfig(): Promise<void>;
2228
+ ensureStatusList(): Promise<void>;
2229
+ ensureStatus(changeId: string, schema?: string): Promise<void>;
2230
+ ensureInstructions(changeId: string, artifact: string, schema?: string): Promise<void>;
2231
+ ensureApplyInstructions(changeId: string, schema?: string): Promise<void>;
2232
+ ensureArtifactOutput(changeId: string, outputPath: string): Promise<void>;
2233
+ ensureGlobArtifactFiles(changeId: string, outputPath: string): Promise<void>;
2234
+ ensureSchemaResolution(name: string): Promise<void>;
2235
+ ensureSchemaDetail(name: string): Promise<void>;
2236
+ ensureSchemaFiles(name: string): Promise<void>;
2237
+ ensureSchemaYaml(name: string): Promise<void>;
2238
+ ensureTemplates(schema?: string): Promise<void>;
2239
+ ensureTemplateContents(schema?: string): Promise<void>;
2240
+ ensureChangeMetadata(changeId: string): Promise<void>;
2233
2241
  private combineSignals;
2234
2242
  }
2235
2243
  //#endregion
@@ -2243,22 +2251,28 @@ declare const PtySessionInfoSchema: z.ZodObject<{
2243
2251
  platform: z.ZodEnum<["windows", "macos", "common"]>;
2244
2252
  isExited: z.ZodBoolean;
2245
2253
  exitCode: z.ZodNullable<z.ZodNumber>;
2254
+ closeTip: z.ZodOptional<z.ZodString>;
2255
+ closeCallbackUrl: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
2246
2256
  }, "strip", z.ZodTypeAny, {
2257
+ id: string;
2247
2258
  command: string;
2248
2259
  args: string[];
2249
2260
  platform: "windows" | "macos" | "common";
2250
2261
  exitCode: number | null;
2251
2262
  title: string;
2252
- id: string;
2253
2263
  isExited: boolean;
2264
+ closeTip?: string | undefined;
2265
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2254
2266
  }, {
2267
+ id: string;
2255
2268
  command: string;
2256
2269
  args: string[];
2257
2270
  platform: "windows" | "macos" | "common";
2258
2271
  exitCode: number | null;
2259
2272
  title: string;
2260
- id: string;
2261
2273
  isExited: boolean;
2274
+ closeTip?: string | undefined;
2275
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2262
2276
  }>;
2263
2277
  declare const PtyCreateMessageSchema: z.ZodObject<{
2264
2278
  type: z.ZodLiteral<"create">;
@@ -2267,20 +2281,26 @@ declare const PtyCreateMessageSchema: z.ZodObject<{
2267
2281
  rows: z.ZodOptional<z.ZodNumber>;
2268
2282
  command: z.ZodOptional<z.ZodString>;
2269
2283
  args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
2284
+ closeTip: z.ZodOptional<z.ZodString>;
2285
+ closeCallbackUrl: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
2270
2286
  }, "strip", z.ZodTypeAny, {
2271
2287
  type: "create";
2272
2288
  requestId: string;
2273
- cols?: number | undefined;
2274
- rows?: number | undefined;
2275
2289
  command?: string | undefined;
2276
2290
  args?: string[] | undefined;
2291
+ cols?: number | undefined;
2292
+ rows?: number | undefined;
2293
+ closeTip?: string | undefined;
2294
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2277
2295
  }, {
2278
2296
  type: "create";
2279
2297
  requestId: string;
2280
- cols?: number | undefined;
2281
- rows?: number | undefined;
2282
2298
  command?: string | undefined;
2283
2299
  args?: string[] | undefined;
2300
+ cols?: number | undefined;
2301
+ rows?: number | undefined;
2302
+ closeTip?: string | undefined;
2303
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2284
2304
  }>;
2285
2305
  declare const PtyInputMessageSchema: z.ZodObject<{
2286
2306
  type: z.ZodLiteral<"input">;
@@ -2351,20 +2371,26 @@ declare const PtyClientMessageSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObje
2351
2371
  rows: z.ZodOptional<z.ZodNumber>;
2352
2372
  command: z.ZodOptional<z.ZodString>;
2353
2373
  args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
2374
+ closeTip: z.ZodOptional<z.ZodString>;
2375
+ closeCallbackUrl: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
2354
2376
  }, "strip", z.ZodTypeAny, {
2355
2377
  type: "create";
2356
2378
  requestId: string;
2357
- cols?: number | undefined;
2358
- rows?: number | undefined;
2359
2379
  command?: string | undefined;
2360
2380
  args?: string[] | undefined;
2381
+ cols?: number | undefined;
2382
+ rows?: number | undefined;
2383
+ closeTip?: string | undefined;
2384
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2361
2385
  }, {
2362
2386
  type: "create";
2363
2387
  requestId: string;
2364
- cols?: number | undefined;
2365
- rows?: number | undefined;
2366
2388
  command?: string | undefined;
2367
2389
  args?: string[] | undefined;
2390
+ cols?: number | undefined;
2391
+ rows?: number | undefined;
2392
+ closeTip?: string | undefined;
2393
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2368
2394
  }>, z.ZodObject<{
2369
2395
  type: z.ZodLiteral<"input">;
2370
2396
  sessionId: z.ZodString;
@@ -2501,44 +2527,54 @@ declare const PtyListResponseSchema: z.ZodObject<{
2501
2527
  platform: z.ZodEnum<["windows", "macos", "common"]>;
2502
2528
  isExited: z.ZodBoolean;
2503
2529
  exitCode: z.ZodNullable<z.ZodNumber>;
2530
+ closeTip: z.ZodOptional<z.ZodString>;
2531
+ closeCallbackUrl: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
2504
2532
  }, "strip", z.ZodTypeAny, {
2533
+ id: string;
2505
2534
  command: string;
2506
2535
  args: string[];
2507
2536
  platform: "windows" | "macos" | "common";
2508
2537
  exitCode: number | null;
2509
2538
  title: string;
2510
- id: string;
2511
2539
  isExited: boolean;
2540
+ closeTip?: string | undefined;
2541
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2512
2542
  }, {
2543
+ id: string;
2513
2544
  command: string;
2514
2545
  args: string[];
2515
2546
  platform: "windows" | "macos" | "common";
2516
2547
  exitCode: number | null;
2517
2548
  title: string;
2518
- id: string;
2519
2549
  isExited: boolean;
2550
+ closeTip?: string | undefined;
2551
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2520
2552
  }>, "many">;
2521
2553
  }, "strip", z.ZodTypeAny, {
2522
2554
  type: "list";
2523
2555
  sessions: {
2556
+ id: string;
2524
2557
  command: string;
2525
2558
  args: string[];
2526
2559
  platform: "windows" | "macos" | "common";
2527
2560
  exitCode: number | null;
2528
2561
  title: string;
2529
- id: string;
2530
2562
  isExited: boolean;
2563
+ closeTip?: string | undefined;
2564
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2531
2565
  }[];
2532
2566
  }, {
2533
2567
  type: "list";
2534
2568
  sessions: {
2569
+ id: string;
2535
2570
  command: string;
2536
2571
  args: string[];
2537
2572
  platform: "windows" | "macos" | "common";
2538
2573
  exitCode: number | null;
2539
2574
  title: string;
2540
- id: string;
2541
2575
  isExited: boolean;
2576
+ closeTip?: string | undefined;
2577
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2542
2578
  }[];
2543
2579
  }>;
2544
2580
  declare const PtyErrorCodeSchema: z.ZodEnum<["INVALID_JSON", "INVALID_MESSAGE", "SESSION_NOT_FOUND", "PTY_CREATE_FAILED"]>;
@@ -2548,14 +2584,14 @@ declare const PtyErrorResponseSchema: z.ZodObject<{
2548
2584
  message: z.ZodString;
2549
2585
  sessionId: z.ZodOptional<z.ZodString>;
2550
2586
  }, "strip", z.ZodTypeAny, {
2551
- type: "error";
2552
2587
  code: "INVALID_JSON" | "INVALID_MESSAGE" | "SESSION_NOT_FOUND" | "PTY_CREATE_FAILED";
2553
2588
  message: string;
2589
+ type: "error";
2554
2590
  sessionId?: string | undefined;
2555
2591
  }, {
2556
- type: "error";
2557
2592
  code: "INVALID_JSON" | "INVALID_MESSAGE" | "SESSION_NOT_FOUND" | "PTY_CREATE_FAILED";
2558
2593
  message: string;
2594
+ type: "error";
2559
2595
  sessionId?: string | undefined;
2560
2596
  }>;
2561
2597
  declare const PtyServerMessageSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
@@ -2631,44 +2667,54 @@ declare const PtyServerMessageSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObje
2631
2667
  platform: z.ZodEnum<["windows", "macos", "common"]>;
2632
2668
  isExited: z.ZodBoolean;
2633
2669
  exitCode: z.ZodNullable<z.ZodNumber>;
2670
+ closeTip: z.ZodOptional<z.ZodString>;
2671
+ closeCallbackUrl: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
2634
2672
  }, "strip", z.ZodTypeAny, {
2673
+ id: string;
2635
2674
  command: string;
2636
2675
  args: string[];
2637
2676
  platform: "windows" | "macos" | "common";
2638
2677
  exitCode: number | null;
2639
2678
  title: string;
2640
- id: string;
2641
2679
  isExited: boolean;
2680
+ closeTip?: string | undefined;
2681
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2642
2682
  }, {
2683
+ id: string;
2643
2684
  command: string;
2644
2685
  args: string[];
2645
2686
  platform: "windows" | "macos" | "common";
2646
2687
  exitCode: number | null;
2647
2688
  title: string;
2648
- id: string;
2649
2689
  isExited: boolean;
2690
+ closeTip?: string | undefined;
2691
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2650
2692
  }>, "many">;
2651
2693
  }, "strip", z.ZodTypeAny, {
2652
2694
  type: "list";
2653
2695
  sessions: {
2696
+ id: string;
2654
2697
  command: string;
2655
2698
  args: string[];
2656
2699
  platform: "windows" | "macos" | "common";
2657
2700
  exitCode: number | null;
2658
2701
  title: string;
2659
- id: string;
2660
2702
  isExited: boolean;
2703
+ closeTip?: string | undefined;
2704
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2661
2705
  }[];
2662
2706
  }, {
2663
2707
  type: "list";
2664
2708
  sessions: {
2709
+ id: string;
2665
2710
  command: string;
2666
2711
  args: string[];
2667
2712
  platform: "windows" | "macos" | "common";
2668
2713
  exitCode: number | null;
2669
2714
  title: string;
2670
- id: string;
2671
2715
  isExited: boolean;
2716
+ closeTip?: string | undefined;
2717
+ closeCallbackUrl?: string | Record<string, string> | undefined;
2672
2718
  }[];
2673
2719
  }>, z.ZodObject<{
2674
2720
  type: z.ZodLiteral<"error">;
@@ -2676,14 +2722,14 @@ declare const PtyServerMessageSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObje
2676
2722
  message: z.ZodString;
2677
2723
  sessionId: z.ZodOptional<z.ZodString>;
2678
2724
  }, "strip", z.ZodTypeAny, {
2679
- type: "error";
2680
2725
  code: "INVALID_JSON" | "INVALID_MESSAGE" | "SESSION_NOT_FOUND" | "PTY_CREATE_FAILED";
2681
2726
  message: string;
2727
+ type: "error";
2682
2728
  sessionId?: string | undefined;
2683
2729
  }, {
2684
- type: "error";
2685
2730
  code: "INVALID_JSON" | "INVALID_MESSAGE" | "SESSION_NOT_FOUND" | "PTY_CREATE_FAILED";
2686
2731
  message: string;
2732
+ type: "error";
2687
2733
  sessionId?: string | undefined;
2688
2734
  }>]>;
2689
2735
  type PtyClientMessage = z.infer<typeof PtyClientMessageSchema>;
package/dist/index.mjs CHANGED
@@ -3251,6 +3251,8 @@ var OpsxKernel = class {
3251
3251
  projectDir;
3252
3252
  cliExecutor;
3253
3253
  controller = new AbortController();
3254
+ warmupPromise = null;
3255
+ _streamReady = /* @__PURE__ */ new Map();
3254
3256
  _statusList = new ReactiveState([]);
3255
3257
  _schemas = new ReactiveState([]);
3256
3258
  _changeIds = new ReactiveState([]);
@@ -3273,17 +3275,28 @@ var OpsxKernel = class {
3273
3275
  this.cliExecutor = cliExecutor;
3274
3276
  }
3275
3277
  async warmup() {
3278
+ if (this.warmupPromise) return this.warmupPromise;
3279
+ this.warmupPromise = this.runWarmup().catch((error) => {
3280
+ this.warmupPromise = null;
3281
+ throw error;
3282
+ });
3283
+ return this.warmupPromise;
3284
+ }
3285
+ async waitForWarmup() {
3286
+ await this.warmup();
3287
+ }
3288
+ async runWarmup() {
3276
3289
  const signal = this.controller.signal;
3277
3290
  await Promise.all([
3278
- this.startStream(this._schemas, () => this.fetchSchemas(), signal),
3279
- this.startStream(this._changeIds, () => this.fetchChangeIds(), signal),
3280
- this.startStream(this._projectConfig, () => this.fetchProjectConfig(), signal)
3291
+ this.startStreamOnce("global:schemas", this._schemas, () => this.fetchSchemas(), signal),
3292
+ this.startStreamOnce("global:change-ids", this._changeIds, () => this.fetchChangeIds(), signal),
3293
+ this.startStreamOnce("global:project-config", this._projectConfig, () => this.fetchProjectConfig(), signal)
3281
3294
  ]);
3282
3295
  const schemas = this._schemas.get();
3283
3296
  await Promise.all(schemas.map((s) => this.warmupSchema(s.name, signal)));
3284
3297
  const changeIds = this._changeIds.get();
3285
3298
  await Promise.all(changeIds.map((id) => this.warmupChange(id, signal)));
3286
- await this.startStream(this._statusList, () => this.fetchStatusList(), signal);
3299
+ await this.startStreamOnce("global:status-list", this._statusList, () => this.fetchStatusList(), signal);
3287
3300
  this.watchSchemaChanges(signal);
3288
3301
  this.watchChangeIdChanges(signal);
3289
3302
  }
@@ -3291,6 +3304,8 @@ var OpsxKernel = class {
3291
3304
  this.controller.abort();
3292
3305
  for (const ctrl of this._entityControllers.values()) ctrl.abort();
3293
3306
  this._entityControllers.clear();
3307
+ this._streamReady.clear();
3308
+ this.warmupPromise = null;
3294
3309
  }
3295
3310
  getStatusList() {
3296
3311
  return this._statusList.get();
@@ -3337,11 +3352,21 @@ var OpsxKernel = class {
3337
3352
  if (!state) throw new Error(`Schema resolution not found for "${name}"`);
3338
3353
  return state.get();
3339
3354
  }
3355
+ peekSchemaResolution(name) {
3356
+ const state = this._schemaResolutions.get(name);
3357
+ if (!state) return null;
3358
+ return state.get() ?? null;
3359
+ }
3340
3360
  getSchemaDetail(name) {
3341
3361
  const state = this._schemaDetails.get(name);
3342
3362
  if (!state) throw new Error(`Schema detail not found for "${name}"`);
3343
3363
  return state.get();
3344
3364
  }
3365
+ peekSchemaDetail(name) {
3366
+ const state = this._schemaDetails.get(name);
3367
+ if (!state) return null;
3368
+ return state.get() ?? null;
3369
+ }
3345
3370
  getSchemaFiles(name) {
3346
3371
  const state = this._schemaFiles.get(name);
3347
3372
  if (!state) throw new Error(`Schema files not found for "${name}"`);
@@ -3370,7 +3395,7 @@ var OpsxKernel = class {
3370
3395
  return state.get();
3371
3396
  }
3372
3397
  startStream(state, task, signal) {
3373
- return new Promise((resolve$1) => {
3398
+ return new Promise((resolve$1, reject) => {
3374
3399
  const context = new ReactiveContext();
3375
3400
  let first = true;
3376
3401
  (async () => {
@@ -3382,12 +3407,28 @@ var OpsxKernel = class {
3382
3407
  resolve$1();
3383
3408
  }
3384
3409
  }
3385
- } catch {}
3410
+ } catch (error) {
3411
+ if (first && !signal.aborted) {
3412
+ reject(error);
3413
+ return;
3414
+ }
3415
+ }
3386
3416
  if (first) resolve$1();
3387
3417
  })();
3388
3418
  });
3389
3419
  }
3420
+ startStreamOnce(key, state, task, signal) {
3421
+ const existing = this._streamReady.get(key);
3422
+ if (existing) return existing;
3423
+ const ready = this.startStream(state, task, signal).catch((error) => {
3424
+ this._streamReady.delete(key);
3425
+ throw error;
3426
+ });
3427
+ this._streamReady.set(key, ready);
3428
+ return ready;
3429
+ }
3390
3430
  async warmupSchema(name, parentSignal) {
3431
+ if (this._entityControllers.has(`schema:${name}`)) return;
3391
3432
  const entityCtrl = new AbortController();
3392
3433
  this._entityControllers.set(`schema:${name}`, entityCtrl);
3393
3434
  const signal = this.combineSignals(parentSignal, entityCtrl.signal);
@@ -3400,17 +3441,18 @@ var OpsxKernel = class {
3400
3441
  if (!this._templates.has("")) this._templates.set("", new ReactiveState({}));
3401
3442
  if (!this._templateContents.has("")) this._templateContents.set("", new ReactiveState({}));
3402
3443
  await Promise.all([
3403
- this.startStream(this._schemaResolutions.get(name), () => this.fetchSchemaResolution(name), signal),
3404
- this.startStream(this._schemaDetails.get(name), () => this.fetchSchemaDetail(name), signal),
3405
- this.startStream(this._schemaFiles.get(name), () => this.fetchSchemaFiles(name), signal),
3406
- this.startStream(this._schemaYamls.get(name), () => this.fetchSchemaYaml(name), signal),
3407
- this.startStream(this._templates.get(name), () => this.fetchTemplates(name), signal),
3408
- this.startStream(this._templateContents.get(name), () => this.fetchTemplateContents(name), signal),
3409
- this.startStream(this._templates.get(""), () => this.fetchTemplates(void 0), signal),
3410
- this.startStream(this._templateContents.get(""), () => this.fetchTemplateContents(void 0), signal)
3444
+ this.startStreamOnce(`schema:${name}:resolution`, this._schemaResolutions.get(name), () => this.fetchSchemaResolution(name), signal),
3445
+ this.startStreamOnce(`schema:${name}:detail`, this._schemaDetails.get(name), () => this.fetchSchemaDetail(name), signal),
3446
+ this.startStreamOnce(`schema:${name}:files`, this._schemaFiles.get(name), () => this.fetchSchemaFiles(name), signal),
3447
+ this.startStreamOnce(`schema:${name}:yaml`, this._schemaYamls.get(name), () => this.fetchSchemaYaml(name), signal),
3448
+ this.startStreamOnce(`schema:${name}:templates`, this._templates.get(name), () => this.fetchTemplates(name), signal),
3449
+ this.startStreamOnce(`schema:${name}:template-contents`, this._templateContents.get(name), () => this.fetchTemplateContents(name), signal),
3450
+ this.startStreamOnce("schema::templates", this._templates.get(""), () => this.fetchTemplates(void 0), signal),
3451
+ this.startStreamOnce("schema::template-contents", this._templateContents.get(""), () => this.fetchTemplateContents(void 0), signal)
3411
3452
  ]);
3412
3453
  }
3413
3454
  async warmupChange(changeId, parentSignal) {
3455
+ if (this._entityControllers.has(`change:${changeId}`)) return;
3414
3456
  const entityCtrl = new AbortController();
3415
3457
  this._entityControllers.set(`change:${changeId}`, entityCtrl);
3416
3458
  const signal = this.combineSignals(parentSignal, entityCtrl.signal);
@@ -3420,22 +3462,22 @@ var OpsxKernel = class {
3420
3462
  const applyKey = `${changeId}:`;
3421
3463
  if (!this._applyInstructions.has(applyKey)) this._applyInstructions.set(applyKey, new ReactiveState(null));
3422
3464
  await Promise.all([
3423
- this.startStream(this._statuses.get(statusKey), () => this.fetchStatus(changeId, void 0), signal),
3424
- this.startStream(this._changeMetadata.get(changeId), () => this.fetchChangeMetadata(changeId), signal),
3425
- this.startStream(this._applyInstructions.get(applyKey), () => this.fetchApplyInstructions(changeId, void 0), signal)
3465
+ this.startStreamOnce(`change:${changeId}:status:`, this._statuses.get(statusKey), () => this.fetchStatus(changeId, void 0), signal),
3466
+ this.startStreamOnce(`change:${changeId}:metadata`, this._changeMetadata.get(changeId), () => this.fetchChangeMetadata(changeId), signal),
3467
+ this.startStreamOnce(`change:${changeId}:apply:`, this._applyInstructions.get(applyKey), () => this.fetchApplyInstructions(changeId, void 0), signal)
3426
3468
  ]);
3427
3469
  const status = this._statuses.get(statusKey)?.get();
3428
3470
  if (status?.artifacts) await Promise.all(status.artifacts.map(async (artifact) => {
3429
3471
  const instrKey = `${changeId}:${artifact.id}:`;
3430
3472
  if (!this._instructions.has(instrKey)) this._instructions.set(instrKey, new ReactiveState(null));
3431
- await this.startStream(this._instructions.get(instrKey), () => this.fetchInstructions(changeId, artifact.id, void 0), signal);
3473
+ await this.startStreamOnce(`change:${changeId}:instructions:${artifact.id}:`, this._instructions.get(instrKey), () => this.fetchInstructions(changeId, artifact.id, void 0), signal);
3432
3474
  const outputKey = `${changeId}:${artifact.outputPath}`;
3433
3475
  if (!this._artifactOutputs.has(outputKey)) this._artifactOutputs.set(outputKey, new ReactiveState(null));
3434
- await this.startStream(this._artifactOutputs.get(outputKey), () => this.fetchArtifactOutput(changeId, artifact.outputPath), signal);
3476
+ await this.startStreamOnce(`change:${changeId}:output:${artifact.outputPath}`, this._artifactOutputs.get(outputKey), () => this.fetchArtifactOutput(changeId, artifact.outputPath), signal);
3435
3477
  if (artifact.outputPath.includes("*") || artifact.outputPath.includes("?") || artifact.outputPath.includes("[")) {
3436
3478
  const globKey = `${changeId}:${artifact.outputPath}`;
3437
3479
  if (!this._globArtifactFiles.has(globKey)) this._globArtifactFiles.set(globKey, new ReactiveState([]));
3438
- await this.startStream(this._globArtifactFiles.get(globKey), () => readGlobArtifactFiles(this.projectDir, changeId, artifact.outputPath), signal);
3480
+ await this.startStreamOnce(`change:${changeId}:glob:${artifact.outputPath}`, this._globArtifactFiles.get(globKey), () => readGlobArtifactFiles(this.projectDir, changeId, artifact.outputPath), signal);
3439
3481
  }
3440
3482
  }));
3441
3483
  }
@@ -3446,7 +3488,7 @@ var OpsxKernel = class {
3446
3488
  try {
3447
3489
  for await (const schemas of context.stream(() => Promise.resolve(this._schemas.get()), signal)) {
3448
3490
  const newNames = new Set(schemas.map((s) => s.name));
3449
- for (const name of newNames) if (!prevNames.has(name)) this.warmupSchema(name, signal);
3491
+ for (const name of newNames) if (!prevNames.has(name)) this.warmupSchema(name, signal).catch(() => {});
3450
3492
  for (const name of prevNames) if (!newNames.has(name)) {
3451
3493
  this.teardownEntity(`schema:${name}`);
3452
3494
  this._schemaResolutions.delete(name);
@@ -3455,6 +3497,7 @@ var OpsxKernel = class {
3455
3497
  this._schemaYamls.delete(name);
3456
3498
  this._templates.delete(name);
3457
3499
  this._templateContents.delete(name);
3500
+ this.clearStreamReadyByPrefix(`schema:${name}:`);
3458
3501
  }
3459
3502
  prevNames = newNames;
3460
3503
  }
@@ -3468,15 +3511,33 @@ var OpsxKernel = class {
3468
3511
  try {
3469
3512
  for await (const ids of context.stream(() => Promise.resolve(this._changeIds.get()), signal)) {
3470
3513
  const newIds = new Set(ids);
3471
- for (const id of newIds) if (!prevIds.has(id)) this.warmupChange(id, signal);
3514
+ for (const id of newIds) if (!prevIds.has(id)) this.warmupChange(id, signal).catch(() => {});
3472
3515
  for (const id of prevIds) if (!newIds.has(id)) {
3473
3516
  this.teardownEntity(`change:${id}`);
3474
- for (const key of this._statuses.keys()) if (key.startsWith(`${id}:`)) this._statuses.delete(key);
3475
- for (const key of this._instructions.keys()) if (key.startsWith(`${id}:`)) this._instructions.delete(key);
3476
- for (const key of this._applyInstructions.keys()) if (key.startsWith(`${id}:`)) this._applyInstructions.delete(key);
3517
+ for (const key of this._statuses.keys()) if (key.startsWith(`${id}:`)) {
3518
+ this._statuses.delete(key);
3519
+ this._streamReady.delete(`change:${id}:status:${key.slice(id.length + 1)}`);
3520
+ }
3521
+ for (const key of this._instructions.keys()) if (key.startsWith(`${id}:`)) {
3522
+ this._instructions.delete(key);
3523
+ const suffix = key.slice(id.length + 1);
3524
+ this._streamReady.delete(`change:${id}:instructions:${suffix}`);
3525
+ }
3526
+ for (const key of this._applyInstructions.keys()) if (key.startsWith(`${id}:`)) {
3527
+ this._applyInstructions.delete(key);
3528
+ this._streamReady.delete(`change:${id}:apply:${key.slice(id.length + 1)}`);
3529
+ }
3477
3530
  this._changeMetadata.delete(id);
3478
- for (const key of this._artifactOutputs.keys()) if (key.startsWith(`${id}:`)) this._artifactOutputs.delete(key);
3479
- for (const key of this._globArtifactFiles.keys()) if (key.startsWith(`${id}:`)) this._globArtifactFiles.delete(key);
3531
+ this._streamReady.delete(`change:${id}:metadata`);
3532
+ for (const key of this._artifactOutputs.keys()) if (key.startsWith(`${id}:`)) {
3533
+ this._artifactOutputs.delete(key);
3534
+ this._streamReady.delete(`change:${id}:output:${key.slice(id.length + 1)}`);
3535
+ }
3536
+ for (const key of this._globArtifactFiles.keys()) if (key.startsWith(`${id}:`)) {
3537
+ this._globArtifactFiles.delete(key);
3538
+ this._streamReady.delete(`change:${id}:glob:${key.slice(id.length + 1)}`);
3539
+ }
3540
+ this.clearStreamReadyByPrefix(`change:${id}:`);
3480
3541
  }
3481
3542
  prevIds = newIds;
3482
3543
  }
@@ -3490,6 +3551,9 @@ var OpsxKernel = class {
3490
3551
  this._entityControllers.delete(key);
3491
3552
  }
3492
3553
  }
3554
+ clearStreamReadyByPrefix(prefix) {
3555
+ for (const key of this._streamReady.keys()) if (key.startsWith(prefix)) this._streamReady.delete(key);
3556
+ }
3493
3557
  async fetchSchemas() {
3494
3558
  await touchOpsxProjectDeps(this.projectDir);
3495
3559
  const result = await this.cliExecutor.schemas();
@@ -3524,13 +3588,10 @@ var OpsxKernel = class {
3524
3588
  return status;
3525
3589
  }
3526
3590
  async fetchStatusList() {
3591
+ await this.ensureChangeIds();
3527
3592
  const changeIds = this._changeIds.get();
3528
- return Promise.all(changeIds.map((id) => {
3529
- const key = `${id}:`;
3530
- const state = this._statuses.get(key);
3531
- if (state) return Promise.resolve(state.get());
3532
- return this.fetchStatus(id);
3533
- }));
3593
+ await Promise.all(changeIds.map((id) => this.ensureStatus(id)));
3594
+ return changeIds.map((id) => this.getStatus(id));
3534
3595
  }
3535
3596
  async fetchInstructions(changeId, artifact, schema) {
3536
3597
  await touchOpsxProjectDeps(this.projectDir);
@@ -3570,18 +3631,21 @@ var OpsxKernel = class {
3570
3631
  }
3571
3632
  async fetchSchemaDetail(name) {
3572
3633
  await touchOpsxProjectDeps(this.projectDir);
3573
- const schemaPath = join$1((await this.fetchSchemaResolution(name)).path, "schema.yaml");
3634
+ await this.ensureSchemaResolution(name);
3635
+ const schemaPath = join$1(this.getSchemaResolution(name).path, "schema.yaml");
3574
3636
  const content = await reactiveReadFile(schemaPath);
3575
3637
  if (!content) throw new Error(`schema.yaml not found at ${schemaPath}`);
3576
3638
  return parseSchemaYamlInline(content);
3577
3639
  }
3578
3640
  async fetchSchemaFiles(name) {
3579
3641
  await touchOpsxProjectDeps(this.projectDir);
3580
- return readEntriesUnderRoot((await this.fetchSchemaResolution(name)).path);
3642
+ await this.ensureSchemaResolution(name);
3643
+ return readEntriesUnderRoot(this.getSchemaResolution(name).path);
3581
3644
  }
3582
3645
  async fetchSchemaYaml(name) {
3583
3646
  await touchOpsxProjectDeps(this.projectDir);
3584
- return reactiveReadFile(join$1((await this.fetchSchemaResolution(name)).path, "schema.yaml"));
3647
+ await this.ensureSchemaResolution(name);
3648
+ return reactiveReadFile(join$1(this.getSchemaResolution(name).path, "schema.yaml"));
3585
3649
  }
3586
3650
  async fetchTemplates(schema) {
3587
3651
  await touchOpsxProjectDeps(this.projectDir);
@@ -3590,7 +3654,8 @@ var OpsxKernel = class {
3590
3654
  return parseCliJson(result.stdout, TemplatesSchema, "openspec templates");
3591
3655
  }
3592
3656
  async fetchTemplateContents(schema) {
3593
- const templates = await this.fetchTemplates(schema);
3657
+ await this.ensureTemplates(schema);
3658
+ const templates = this.getTemplates(schema);
3594
3659
  const entries = await Promise.all(Object.entries(templates).map(async ([artifactId, info]) => {
3595
3660
  return [artifactId, {
3596
3661
  content: await reactiveReadFile(info.path),
@@ -3606,88 +3671,72 @@ var OpsxKernel = class {
3606
3671
  async fetchArtifactOutput(changeId, outputPath) {
3607
3672
  return reactiveReadFile(join$1(this.projectDir, "openspec", "changes", changeId, outputPath));
3608
3673
  }
3609
- /**
3610
- * Ensure a per-change status stream exists. If not yet warmed up,
3611
- * creates the state and starts a stream lazily.
3612
- */
3613
- ensureStatus(changeId, schema) {
3674
+ async ensureSchemas() {
3675
+ await this.startStreamOnce("global:schemas", this._schemas, () => this.fetchSchemas(), this.controller.signal);
3676
+ }
3677
+ async ensureChangeIds() {
3678
+ await this.startStreamOnce("global:change-ids", this._changeIds, () => this.fetchChangeIds(), this.controller.signal);
3679
+ }
3680
+ async ensureProjectConfig() {
3681
+ await this.startStreamOnce("global:project-config", this._projectConfig, () => this.fetchProjectConfig(), this.controller.signal);
3682
+ }
3683
+ async ensureStatusList() {
3684
+ await this.startStreamOnce("global:status-list", this._statusList, () => this.fetchStatusList(), this.controller.signal);
3685
+ }
3686
+ async ensureStatus(changeId, schema) {
3614
3687
  const key = `${changeId}:${schema ?? ""}`;
3615
- if (!this._statuses.has(key)) {
3616
- this._statuses.set(key, new ReactiveState(null));
3617
- this.startStream(this._statuses.get(key), () => this.fetchStatus(changeId, schema), this.controller.signal);
3618
- }
3688
+ if (!this._statuses.has(key)) this._statuses.set(key, new ReactiveState(null));
3689
+ await this.startStreamOnce(`change:${changeId}:status:${schema ?? ""}`, this._statuses.get(key), () => this.fetchStatus(changeId, schema), this.controller.signal);
3619
3690
  }
3620
- ensureInstructions(changeId, artifact, schema) {
3691
+ async ensureInstructions(changeId, artifact, schema) {
3621
3692
  const key = `${changeId}:${artifact}:${schema ?? ""}`;
3622
- if (!this._instructions.has(key)) {
3623
- this._instructions.set(key, new ReactiveState(null));
3624
- this.startStream(this._instructions.get(key), () => this.fetchInstructions(changeId, artifact, schema), this.controller.signal);
3625
- }
3693
+ if (!this._instructions.has(key)) this._instructions.set(key, new ReactiveState(null));
3694
+ await this.startStreamOnce(`change:${changeId}:instructions:${artifact}:${schema ?? ""}`, this._instructions.get(key), () => this.fetchInstructions(changeId, artifact, schema), this.controller.signal);
3626
3695
  }
3627
- ensureApplyInstructions(changeId, schema) {
3696
+ async ensureApplyInstructions(changeId, schema) {
3628
3697
  const key = `${changeId}:${schema ?? ""}`;
3629
- if (!this._applyInstructions.has(key)) {
3630
- this._applyInstructions.set(key, new ReactiveState(null));
3631
- this.startStream(this._applyInstructions.get(key), () => this.fetchApplyInstructions(changeId, schema), this.controller.signal);
3632
- }
3698
+ if (!this._applyInstructions.has(key)) this._applyInstructions.set(key, new ReactiveState(null));
3699
+ await this.startStreamOnce(`change:${changeId}:apply:${schema ?? ""}`, this._applyInstructions.get(key), () => this.fetchApplyInstructions(changeId, schema), this.controller.signal);
3633
3700
  }
3634
- ensureArtifactOutput(changeId, outputPath) {
3701
+ async ensureArtifactOutput(changeId, outputPath) {
3635
3702
  const key = `${changeId}:${outputPath}`;
3636
- if (!this._artifactOutputs.has(key)) {
3637
- this._artifactOutputs.set(key, new ReactiveState(null));
3638
- this.startStream(this._artifactOutputs.get(key), () => this.fetchArtifactOutput(changeId, outputPath), this.controller.signal);
3639
- }
3703
+ if (!this._artifactOutputs.has(key)) this._artifactOutputs.set(key, new ReactiveState(null));
3704
+ await this.startStreamOnce(`change:${changeId}:output:${outputPath}`, this._artifactOutputs.get(key), () => this.fetchArtifactOutput(changeId, outputPath), this.controller.signal);
3640
3705
  }
3641
- ensureGlobArtifactFiles(changeId, outputPath) {
3706
+ async ensureGlobArtifactFiles(changeId, outputPath) {
3642
3707
  const key = `${changeId}:${outputPath}`;
3643
- if (!this._globArtifactFiles.has(key)) {
3644
- this._globArtifactFiles.set(key, new ReactiveState([]));
3645
- this.startStream(this._globArtifactFiles.get(key), () => readGlobArtifactFiles(this.projectDir, changeId, outputPath), this.controller.signal);
3646
- }
3708
+ if (!this._globArtifactFiles.has(key)) this._globArtifactFiles.set(key, new ReactiveState([]));
3709
+ await this.startStreamOnce(`change:${changeId}:glob:${outputPath}`, this._globArtifactFiles.get(key), () => readGlobArtifactFiles(this.projectDir, changeId, outputPath), this.controller.signal);
3647
3710
  }
3648
- ensureSchemaResolution(name) {
3649
- if (!this._schemaResolutions.has(name)) {
3650
- this._schemaResolutions.set(name, new ReactiveState(null));
3651
- this.startStream(this._schemaResolutions.get(name), () => this.fetchSchemaResolution(name), this.controller.signal);
3652
- }
3711
+ async ensureSchemaResolution(name) {
3712
+ if (!this._schemaResolutions.has(name)) this._schemaResolutions.set(name, new ReactiveState(null));
3713
+ await this.startStreamOnce(`schema:${name}:resolution`, this._schemaResolutions.get(name), () => this.fetchSchemaResolution(name), this.controller.signal);
3653
3714
  }
3654
- ensureSchemaDetail(name) {
3655
- if (!this._schemaDetails.has(name)) {
3656
- this._schemaDetails.set(name, new ReactiveState(null));
3657
- this.startStream(this._schemaDetails.get(name), () => this.fetchSchemaDetail(name), this.controller.signal);
3658
- }
3715
+ async ensureSchemaDetail(name) {
3716
+ if (!this._schemaDetails.has(name)) this._schemaDetails.set(name, new ReactiveState(null));
3717
+ await this.startStreamOnce(`schema:${name}:detail`, this._schemaDetails.get(name), () => this.fetchSchemaDetail(name), this.controller.signal);
3659
3718
  }
3660
- ensureSchemaFiles(name) {
3661
- if (!this._schemaFiles.has(name)) {
3662
- this._schemaFiles.set(name, new ReactiveState([]));
3663
- this.startStream(this._schemaFiles.get(name), () => this.fetchSchemaFiles(name), this.controller.signal);
3664
- }
3719
+ async ensureSchemaFiles(name) {
3720
+ if (!this._schemaFiles.has(name)) this._schemaFiles.set(name, new ReactiveState([]));
3721
+ await this.startStreamOnce(`schema:${name}:files`, this._schemaFiles.get(name), () => this.fetchSchemaFiles(name), this.controller.signal);
3665
3722
  }
3666
- ensureSchemaYaml(name) {
3667
- if (!this._schemaYamls.has(name)) {
3668
- this._schemaYamls.set(name, new ReactiveState(null));
3669
- this.startStream(this._schemaYamls.get(name), () => this.fetchSchemaYaml(name), this.controller.signal);
3670
- }
3723
+ async ensureSchemaYaml(name) {
3724
+ if (!this._schemaYamls.has(name)) this._schemaYamls.set(name, new ReactiveState(null));
3725
+ await this.startStreamOnce(`schema:${name}:yaml`, this._schemaYamls.get(name), () => this.fetchSchemaYaml(name), this.controller.signal);
3671
3726
  }
3672
- ensureTemplates(schema) {
3727
+ async ensureTemplates(schema) {
3673
3728
  const key = schema ?? "";
3674
- if (!this._templates.has(key)) {
3675
- this._templates.set(key, new ReactiveState({}));
3676
- this.startStream(this._templates.get(key), () => this.fetchTemplates(schema), this.controller.signal);
3677
- }
3729
+ if (!this._templates.has(key)) this._templates.set(key, new ReactiveState({}));
3730
+ await this.startStreamOnce(`schema:${key}:templates`, this._templates.get(key), () => this.fetchTemplates(schema), this.controller.signal);
3678
3731
  }
3679
- ensureTemplateContents(schema) {
3732
+ async ensureTemplateContents(schema) {
3680
3733
  const key = schema ?? "";
3681
- if (!this._templateContents.has(key)) {
3682
- this._templateContents.set(key, new ReactiveState({}));
3683
- this.startStream(this._templateContents.get(key), () => this.fetchTemplateContents(schema), this.controller.signal);
3684
- }
3734
+ if (!this._templateContents.has(key)) this._templateContents.set(key, new ReactiveState({}));
3735
+ await this.startStreamOnce(`schema:${key}:template-contents`, this._templateContents.get(key), () => this.fetchTemplateContents(schema), this.controller.signal);
3685
3736
  }
3686
- ensureChangeMetadata(changeId) {
3687
- if (!this._changeMetadata.has(changeId)) {
3688
- this._changeMetadata.set(changeId, new ReactiveState(null));
3689
- this.startStream(this._changeMetadata.get(changeId), () => this.fetchChangeMetadata(changeId), this.controller.signal);
3690
- }
3737
+ async ensureChangeMetadata(changeId) {
3738
+ if (!this._changeMetadata.has(changeId)) this._changeMetadata.set(changeId, new ReactiveState(null));
3739
+ await this.startStreamOnce(`change:${changeId}:metadata`, this._changeMetadata.get(changeId), () => this.fetchChangeMetadata(changeId), this.controller.signal);
3691
3740
  }
3692
3741
  combineSignals(a, b) {
3693
3742
  const ctrl = new AbortController();
@@ -3754,6 +3803,7 @@ const PtyPlatformSchema = z.enum([
3754
3803
  "macos",
3755
3804
  "common"
3756
3805
  ]);
3806
+ const CloseCallbackUrlSchema = z.union([z.string(), z.record(z.string())]);
3757
3807
  const PtySessionInfoSchema = z.object({
3758
3808
  id: z.string().min(1),
3759
3809
  title: z.string(),
@@ -3761,7 +3811,9 @@ const PtySessionInfoSchema = z.object({
3761
3811
  args: z.array(z.string()),
3762
3812
  platform: PtyPlatformSchema,
3763
3813
  isExited: z.boolean(),
3764
- exitCode: z.number().int().nullable()
3814
+ exitCode: z.number().int().nullable(),
3815
+ closeTip: z.string().optional(),
3816
+ closeCallbackUrl: CloseCallbackUrlSchema.optional()
3765
3817
  });
3766
3818
  const PtyCreateMessageSchema = z.object({
3767
3819
  type: z.literal("create"),
@@ -3769,7 +3821,9 @@ const PtyCreateMessageSchema = z.object({
3769
3821
  cols: PositiveInt.optional(),
3770
3822
  rows: PositiveInt.optional(),
3771
3823
  command: z.string().min(1).optional(),
3772
- args: z.array(z.string()).optional()
3824
+ args: z.array(z.string()).optional(),
3825
+ closeTip: z.string().optional(),
3826
+ closeCallbackUrl: CloseCallbackUrlSchema.optional()
3773
3827
  });
3774
3828
  const PtyInputMessageSchema = z.object({
3775
3829
  type: z.literal("input"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openspecui/core",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Core OpenSpec adapter and parser",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",