@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.
- package/dist/index.mjs +88 -94
- 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
|
|
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({
|
|
617
|
-
|
|
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
|
-
])
|
|
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.
|
|
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.
|
|
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
|
}
|