@openspecui/server 1.0.0 → 1.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.
Files changed (2) hide show
  1. package/dist/index.mjs +88 -94
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -5,75 +5,17 @@ import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
5
5
  import { applyWSSHandler } from "@trpc/server/adapters/ws";
6
6
  import { WebSocketServer } from "ws";
7
7
  import { ApplyInstructionsSchema, ArtifactInstructionsSchema, ChangeStatusSchema, CliExecutor, ConfigManager, OpenSpecAdapter, OpenSpecWatcher, OpsxKernel, PtyClientMessageSchema, ReactiveContext, SchemaDetailSchema, SchemaInfoSchema, SchemaResolutionSchema, TemplatesSchema, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, initWatcherPool, isWatcherPoolInitialized, reactiveReadDir, reactiveReadFile, reactiveStat, sniffGlobalCli } from "@openspecui/core";
8
- import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
9
- import { dirname, join, matchesGlob, relative, resolve, sep } from "node:path";
10
8
  import { initTRPC } from "@trpc/server";
11
9
  import { observable } from "@trpc/server/observable";
10
+ import { mkdir, rm, writeFile } from "node:fs/promises";
11
+ import { dirname, join, matchesGlob, relative, resolve, sep } from "node:path";
12
12
  import { z } from "zod";
13
- import YAML, { parse } from "yaml";
13
+ import { parse } from "yaml";
14
14
  import { EventEmitter } from "node:events";
15
15
  import { createServer as createServer$1 } from "node:net";
16
16
  import * as pty from "@lydell/node-pty";
17
17
  import { EventEmitter as EventEmitter$1 } from "events";
18
18
 
19
- //#region src/reactive-subscription.ts
20
- /**
21
- * 创建响应式订阅
22
- *
23
- * 自动追踪 task 中的文件依赖,当依赖变更时自动重新执行并推送新数据。
24
- *
25
- * @param task 要执行的异步任务,内部的文件读取会被自动追踪
26
- * @returns tRPC observable
27
- *
28
- * @example
29
- * ```typescript
30
- * // 在 router 中使用
31
- * subscribe: publicProcedure.subscription(({ ctx }) => {
32
- * return createReactiveSubscription(() => ctx.adapter.listSpecsWithMeta())
33
- * })
34
- * ```
35
- */
36
- function createReactiveSubscription(task) {
37
- return observable((emit) => {
38
- const context = new ReactiveContext();
39
- const controller = new AbortController();
40
- (async () => {
41
- try {
42
- for await (const data of context.stream(task, controller.signal)) emit.next(data);
43
- } catch (err) {
44
- if (!controller.signal.aborted) emit.error(err);
45
- }
46
- })();
47
- return () => {
48
- controller.abort();
49
- };
50
- });
51
- }
52
- /**
53
- * 创建带输入参数的响应式订阅
54
- *
55
- * @param task 接收输入参数的异步任务
56
- * @returns 返回一个函数,接收输入参数并返回 tRPC observable
57
- *
58
- * @example
59
- * ```typescript
60
- * // 在 router 中使用
61
- * subscribeOne: publicProcedure
62
- * .input(z.object({ id: z.string() }))
63
- * .subscription(({ ctx, input }) => {
64
- * return createReactiveSubscriptionWithInput(
65
- * (id: string) => ctx.adapter.readSpec(id)
66
- * )(input.id)
67
- * })
68
- * ```
69
- */
70
- function createReactiveSubscriptionWithInput(task) {
71
- return (input) => {
72
- return createReactiveSubscription(() => task(input));
73
- };
74
- }
75
-
76
- //#endregion
77
19
  //#region src/cli-stream-observable.ts
78
20
  /**
79
21
  * 创建安全的 CLI 流式 observable
@@ -234,6 +176,64 @@ var ReactiveKV = class {
234
176
  /** Singleton instance shared across the server lifetime */
235
177
  const reactiveKV = new ReactiveKV();
236
178
 
179
+ //#endregion
180
+ //#region src/reactive-subscription.ts
181
+ /**
182
+ * 创建响应式订阅
183
+ *
184
+ * 自动追踪 task 中的文件依赖,当依赖变更时自动重新执行并推送新数据。
185
+ *
186
+ * @param task 要执行的异步任务,内部的文件读取会被自动追踪
187
+ * @returns tRPC observable
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * // 在 router 中使用
192
+ * subscribe: publicProcedure.subscription(({ ctx }) => {
193
+ * return createReactiveSubscription(() => ctx.adapter.listSpecsWithMeta())
194
+ * })
195
+ * ```
196
+ */
197
+ function createReactiveSubscription(task) {
198
+ return observable((emit) => {
199
+ const context = new ReactiveContext();
200
+ const controller = new AbortController();
201
+ (async () => {
202
+ try {
203
+ for await (const data of context.stream(task, controller.signal)) emit.next(data);
204
+ } catch (err) {
205
+ if (!controller.signal.aborted) emit.error(err);
206
+ }
207
+ })();
208
+ return () => {
209
+ controller.abort();
210
+ };
211
+ });
212
+ }
213
+ /**
214
+ * 创建带输入参数的响应式订阅
215
+ *
216
+ * @param task 接收输入参数的异步任务
217
+ * @returns 返回一个函数,接收输入参数并返回 tRPC observable
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * // 在 router 中使用
222
+ * subscribeOne: publicProcedure
223
+ * .input(z.object({ id: z.string() }))
224
+ * .subscription(({ ctx, input }) => {
225
+ * return createReactiveSubscriptionWithInput(
226
+ * (id: string) => ctx.adapter.readSpec(id)
227
+ * )(input.id)
228
+ * })
229
+ * ```
230
+ */
231
+ function createReactiveSubscriptionWithInput(task) {
232
+ return (input) => {
233
+ return createReactiveSubscription(() => task(input));
234
+ };
235
+ }
236
+
237
237
  //#endregion
238
238
  //#region src/router.ts
239
239
  const t = initTRPC.context().create();
@@ -613,20 +613,40 @@ const configRouter = router({
613
613
  return getDefaultCliCommandString();
614
614
  }),
615
615
  update: publicProcedure.input(z.object({
616
- cli: z.object({ command: z.string() }).optional(),
617
- ui: z.object({ theme: z.enum([
616
+ cli: z.object({
617
+ command: z.string().nullable().optional(),
618
+ args: z.array(z.string()).nullable().optional()
619
+ }).optional(),
620
+ theme: z.enum([
618
621
  "light",
619
622
  "dark",
620
623
  "system"
621
- ]) }).optional()
624
+ ]).optional(),
625
+ terminal: z.object({
626
+ fontSize: z.number().min(8).max(32).optional(),
627
+ fontFamily: z.string().optional(),
628
+ cursorBlink: z.boolean().optional(),
629
+ cursorStyle: z.enum([
630
+ "block",
631
+ "underline",
632
+ "bar"
633
+ ]).optional(),
634
+ scrollback: z.number().min(0).max(1e5).optional()
635
+ }).optional()
622
636
  })).mutation(async ({ ctx, input }) => {
637
+ const hasCliCommand = input.cli !== void 0 && Object.prototype.hasOwnProperty.call(input.cli, "command");
638
+ const hasCliArgs = input.cli !== void 0 && Object.prototype.hasOwnProperty.call(input.cli, "args");
639
+ if (hasCliCommand && !hasCliArgs) {
640
+ await ctx.configManager.setCliCommand(input.cli?.command ?? "");
641
+ if (input.theme !== void 0 || input.terminal !== void 0) await ctx.configManager.writeConfig({
642
+ theme: input.theme,
643
+ terminal: input.terminal
644
+ });
645
+ return { success: true };
646
+ }
623
647
  await ctx.configManager.writeConfig(input);
624
648
  return { success: true };
625
649
  }),
626
- setCliCommand: publicProcedure.input(z.object({ command: z.string() })).mutation(async ({ ctx, input }) => {
627
- await ctx.configManager.setCliCommand(input.command);
628
- return { success: true };
629
- }),
630
650
  subscribe: publicProcedure.subscription(({ ctx }) => {
631
651
  return createReactiveSubscription(() => ctx.configManager.readConfig());
632
652
  })
@@ -972,32 +992,6 @@ const opsxRouter = router({
972
992
  await writeFile(join(openspecDir, "config.yaml"), input.content, "utf-8");
973
993
  return { success: true };
974
994
  }),
975
- updateProjectConfigUi: publicProcedure.input(z.object({
976
- "font-size": z.number().min(8).max(32).optional(),
977
- "font-families": z.array(z.string()).optional(),
978
- "cursor-blink": z.boolean().optional(),
979
- "cursor-style": z.enum([
980
- "block",
981
- "underline",
982
- "bar"
983
- ]).optional(),
984
- scrollback: z.number().min(0).max(1e5).optional()
985
- })).mutation(async ({ ctx, input }) => {
986
- const configPath = join(ctx.projectDir, "openspec", "config.yaml");
987
- let doc;
988
- try {
989
- const content = await readFile(configPath, "utf-8");
990
- doc = YAML.parseDocument(content);
991
- } catch {
992
- doc = new YAML.Document({});
993
- }
994
- if (!doc.has("ui")) doc.set("ui", doc.createNode({}));
995
- const uiNode = doc.get("ui", true);
996
- for (const [key, value] of Object.entries(input)) if (value !== void 0) uiNode.set(key, value);
997
- await mkdir(join(ctx.projectDir, "openspec"), { recursive: true });
998
- await writeFile(configPath, doc.toString(), "utf-8");
999
- return { success: true };
1000
- }),
1001
995
  listChanges: publicProcedure.query(async ({ ctx }) => {
1002
996
  return reactiveReadDir(join(ctx.projectDir, "openspec", "changes"), {
1003
997
  directoriesOnly: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openspecui/server",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.mjs",
6
6
  "exports": {
@@ -20,7 +20,7 @@
20
20
  "yaml": "^2.8.0",
21
21
  "yargs": "^18.0.0",
22
22
  "zod": "^3.24.1",
23
- "@openspecui/core": "1.0.0"
23
+ "@openspecui/core": "1.0.2"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^22.10.2",
@@ -34,7 +34,7 @@
34
34
  "scripts": {
35
35
  "build": "tsdown src/index.ts --format esm --no-dts",
36
36
  "typecheck": "tsc --noEmit",
37
- "dev": "tsx watch src/standalone.ts",
37
+ "dev": "tsx watch --include '../core/dist/**' src/standalone.ts",
38
38
  "test": "vitest run",
39
39
  "test:watch": "vitest"
40
40
  }