@dyyz1993/create-agent 2.0.0 → 2.0.1
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/package.json +1 -1
- package/src/lib/copy.ts +23 -1
- package/templates/agent/eslint.config.mjs +15 -2
- package/templates/agent/package.json +10 -2
- package/templates/agent/src/__tests__/hybrid-mode.test.ts +126 -0
- package/templates/agent/src/__tests__/server-config-security.test.ts +55 -0
- package/templates/agent/src/bun/index.ts +96 -41
- package/templates/agent/src/bun/three.d.ts +1 -0
- package/templates/agent/src/gateway/__tests__/ws-handler-token.test.ts +118 -0
- package/templates/agent/src/gateway/http-routes.ts +1 -1
- package/templates/agent/src/gateway/ws-handler.ts +84 -56
- package/templates/agent/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
- package/templates/agent/src/mainview/__tests__/setup.ts +32 -29
- package/templates/agent/src/mainview/__tests__/theme-variables.test.ts +36 -0
- package/templates/agent/src/mainview/components/common/ErrorBoundary.tsx +1 -58
- package/templates/agent/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
- package/templates/agent/src/mainview/components/common/ThemeToggle.tsx +1 -0
- package/templates/agent/src/mainview/components/feed/FeedPanel.tsx +232 -210
- package/templates/agent/src/mainview/components/todo/TodoPanel.tsx +149 -122
- package/templates/agent/src/mainview/lib/api-client.ts +182 -172
- package/templates/agent/src/server-config.ts +34 -32
- package/templates/agent/src/server.ts +43 -54
- package/templates/agent/src/shared/handlers/__tests__/chat-concurrency.test.ts +127 -0
- package/templates/agent/src/shared/handlers/__tests__/handler-isolation.test.ts +244 -0
- package/templates/agent/src/shared/handlers/bash.ts +65 -65
- package/templates/agent/src/shared/handlers/chat.ts +189 -155
- package/templates/agent/src/shared/handlers/debug.ts +8 -0
- package/templates/agent/src/shared/handlers/feed.ts +31 -32
- package/templates/agent/src/shared/handlers/index.ts +1 -0
- package/templates/agent/src/shared/handlers/rules.ts +31 -31
- package/templates/agent/src/shared/handlers/todo.ts +31 -31
- package/templates/agent/src/shared/http-routes.ts +250 -0
- package/templates/agent/src/shared/lib/__tests__/web-server.test.ts +190 -0
- package/templates/agent/src/shared/lib/logger.ts +117 -95
- package/templates/agent/src/shared/lib/web-server.ts +128 -0
- package/templates/agent/src/shared/modules/debug.ts +8 -0
- package/templates/agent/src/shared/rpc-schema.ts +16 -3
- package/templates/agent/test-upload.txt +0 -0
- package/templates/agent/tsconfig.ipc.json +10 -0
- package/templates/agent/tsconfig.json +10 -2
- package/templates/chat/eslint.config.mjs +15 -2
- package/templates/chat/package.json +10 -2
- package/templates/chat/src/__tests__/hybrid-mode.test.ts +125 -0
- package/templates/chat/src/__tests__/server-config-security.test.ts +55 -0
- package/templates/chat/src/__tests__/server-config.test.ts +46 -44
- package/templates/chat/src/bun/index.ts +94 -44
- package/templates/chat/src/bun/three.d.ts +1 -0
- package/templates/chat/src/gateway/http-routes.ts +1 -1
- package/templates/chat/src/gateway/ws-handler.ts +84 -56
- package/templates/chat/src/mainview/App.tsx +6 -0
- package/templates/chat/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
- package/templates/chat/src/mainview/__tests__/theme-variables.test.ts +36 -0
- package/templates/chat/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
- package/templates/chat/src/mainview/components/common/ThemeToggle.tsx +1 -0
- package/templates/chat/src/mainview/lib/api-client.ts +183 -156
- package/templates/chat/src/mainview/lib/rpc-cache.ts +84 -0
- package/templates/chat/src/mainview/main.tsx +4 -3
- package/templates/chat/src/server-config.ts +33 -31
- package/templates/chat/src/server.ts +43 -60
- package/templates/chat/src/shared/handlers/chat.ts +196 -162
- package/templates/chat/src/shared/handlers/debug.ts +8 -0
- package/templates/chat/src/shared/handlers/index.ts +1 -0
- package/templates/chat/src/shared/http-routes.ts +250 -0
- package/templates/chat/src/shared/lib/__tests__/logger.test.ts +92 -0
- package/templates/chat/src/shared/lib/logger.ts +108 -95
- package/templates/chat/src/shared/lib/web-server.ts +128 -0
- package/templates/chat/src/shared/modules/debug.ts +8 -0
- package/templates/chat/src/shared/rpc-schema.ts +3 -2
- package/templates/chat/test-upload.txt +0 -0
- package/templates/chat/tsconfig.ipc.json +10 -0
- package/templates/general/eslint.config.mjs +15 -2
- package/templates/general/package.json +10 -2
- package/templates/general/src/__tests__/hybrid-mode.test.ts +125 -0
- package/templates/general/src/__tests__/server-config-security.test.ts +55 -0
- package/templates/general/src/__tests__/server-config.test.ts +46 -44
- package/templates/general/src/bun/index.ts +94 -44
- package/templates/general/src/bun/three.d.ts +1 -0
- package/templates/general/src/gateway/http-routes.ts +1 -1
- package/templates/general/src/gateway/ws-handler.ts +84 -56
- package/templates/general/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
- package/templates/general/src/mainview/__tests__/theme-variables.test.ts +36 -0
- package/templates/general/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
- package/templates/general/src/mainview/components/common/ThemeToggle.tsx +1 -0
- package/templates/general/src/mainview/components/feed/FeedPanel.tsx +241 -219
- package/templates/general/src/mainview/components/layout/AppLayout.tsx +6 -0
- package/templates/general/src/mainview/lib/api-client.ts +183 -156
- package/templates/general/src/mainview/lib/rpc-cache.ts +84 -0
- package/templates/general/src/mainview/main.tsx +4 -3
- package/templates/general/src/server-config.ts +33 -31
- package/templates/general/src/server.ts +43 -60
- package/templates/general/src/shared/handlers/__tests__/chat.test.ts +165 -160
- package/templates/general/src/shared/handlers/chat.ts +189 -155
- package/templates/general/src/shared/handlers/debug.ts +8 -0
- package/templates/general/src/shared/handlers/feed.ts +38 -40
- package/templates/general/src/shared/handlers/index.ts +1 -0
- package/templates/general/src/shared/http-routes.ts +250 -0
- package/templates/general/src/shared/lib/__tests__/logger.test.ts +92 -0
- package/templates/general/src/shared/lib/logger.ts +117 -95
- package/templates/general/src/shared/lib/web-server.ts +128 -0
- package/templates/general/src/shared/modules/debug.ts +8 -0
- package/templates/general/src/shared/rpc-schema.ts +12 -2
- package/templates/general/test-upload.txt +0 -0
- package/templates/general/tsconfig.ipc.json +10 -0
- package/templates/shared/__tests__/cors-security.test.ts +165 -0
- package/templates/shared/__tests__/file-path-security.test.ts +260 -0
- package/templates/shared/__tests__/http-routes.test.ts +174 -0
- package/templates/shared/__tests__/logger.test.ts +186 -0
- package/templates/shared/components/ErrorBoundary.tsx +58 -0
- package/templates/shared/env.d.ts +11 -0
- package/templates/shared/http-routes.ts +230 -0
- package/templates/shared/logger.ts +129 -0
- package/templates/shared/test-upload.txt +0 -0
- package/templates/shared/vite-base.config.ts +92 -0
- package/templates/shared/vitest-base.config.ts +42 -0
- package/templates/agent/eslint-plugin-rpc/index.js +0 -50
- package/templates/agent/eslint-plugin-rpc/package.json +0 -6
- package/templates/agent/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
- package/templates/agent/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
- package/templates/agent/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
- package/templates/agent/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
- package/templates/agent/eslint-plugin-rpc/rules/require-api-client.js +0 -58
- package/templates/agent/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
- package/templates/agent/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
- package/templates/chat/eslint-plugin-rpc/index.js +0 -50
- package/templates/chat/eslint-plugin-rpc/package.json +0 -6
- package/templates/chat/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
- package/templates/chat/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
- package/templates/chat/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
- package/templates/chat/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
- package/templates/chat/eslint-plugin-rpc/rules/require-api-client.js +0 -58
- package/templates/chat/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
- package/templates/chat/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
- package/templates/general/eslint-plugin-rpc/index.js +0 -50
- package/templates/general/eslint-plugin-rpc/package.json +0 -6
- package/templates/general/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
- package/templates/general/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
- package/templates/general/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
- package/templates/general/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
- package/templates/general/eslint-plugin-rpc/rules/require-api-client.js +0 -58
- package/templates/general/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
- package/templates/general/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
|
@@ -9,171 +9,205 @@ import { createLogger } from "../lib/logger";
|
|
|
9
9
|
|
|
10
10
|
const log = createLogger("chat");
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
type Platform = "desktop" | "web";
|
|
13
|
+
|
|
14
|
+
export function getStoragePathFor(platform: Platform): string {
|
|
15
|
+
const dir = join(homedir(), ".pi-agent");
|
|
16
|
+
if (platform === "desktop") {
|
|
17
|
+
return join(dir, "chat-history-desktop.json");
|
|
18
|
+
}
|
|
19
|
+
const sessionId = Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 8);
|
|
20
|
+
return join(dir, `chat-history-web-${sessionId}.json`);
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
type ChatMessage = { id: string; role: "user" | "assistant"; content: string; timestamp: number };
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
25
|
+
function createMessageStore(filePath: string) {
|
|
26
|
+
async function load(): Promise<ChatMessage[]> {
|
|
27
|
+
try {
|
|
28
|
+
if (!existsSync(filePath)) {
|
|
29
|
+
log.info(`No history file at ${filePath}`);
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const raw = await readFile(filePath, "utf-8");
|
|
33
|
+
const msgs = JSON.parse(raw) as ChatMessage[];
|
|
34
|
+
log.info(`Loaded ${msgs.length} messages from ${filePath}`);
|
|
35
|
+
return msgs;
|
|
36
|
+
} catch (err) {
|
|
37
|
+
log.error("Failed to load history", { error: err });
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function save(messages: ChatMessage[]): Promise<void> {
|
|
43
|
+
const dir = dirname(filePath);
|
|
44
|
+
try {
|
|
45
|
+
if (!existsSync(dir)) {
|
|
46
|
+
await mkdir(dir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
await writeFile(filePath, JSON.stringify(messages, null, 2), "utf-8");
|
|
49
|
+
log.info(`Saved ${messages.length} messages to ${filePath}`);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
log.error("Failed to save history", { error: err });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { load, save };
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
type RegisterFn = <K extends keyof RPCMethods & string>(
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
method: K,
|
|
60
|
+
handler: (params: MethodParams<RPCMethods, K>) => Promise<MethodResult<RPCMethods, K>>
|
|
53
61
|
) => void;
|
|
54
62
|
|
|
55
63
|
function generateReply(input: string): string {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
64
|
+
const lower = input.toLowerCase().trim();
|
|
65
|
+
|
|
66
|
+
if (/^(hi|hello|hey|howdy|hola|yo|sup)\b/i.test(lower)) {
|
|
67
|
+
const greetings = [
|
|
68
|
+
"Hey there! How can I help you today?",
|
|
69
|
+
"Hello! Great to see you. What would you like to know?",
|
|
70
|
+
"Hi! I'm your desktop assistant. Ask me anything!",
|
|
71
|
+
];
|
|
72
|
+
return greetings[Math.floor(Math.random() * greetings.length)];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (
|
|
76
|
+
/what('?s| is) the (time|date|day)|current (time|date)|what time|today'?s date/i.test(lower)
|
|
77
|
+
) {
|
|
78
|
+
const now = new Date();
|
|
79
|
+
const date = now.toLocaleDateString("en-US", {
|
|
80
|
+
weekday: "long",
|
|
81
|
+
year: "numeric",
|
|
82
|
+
month: "long",
|
|
83
|
+
day: "numeric",
|
|
84
|
+
});
|
|
85
|
+
const time = now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" });
|
|
86
|
+
return `It's currently **${time}** on **${date}**.`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const mathMatch = lower.match(
|
|
90
|
+
/(?:what(?:'s| is)\s+)?(\d+(?:\.\d+)?)\s*([+\-*/x×÷^])\s*(\d+(?:\.\d+)?)/
|
|
91
|
+
);
|
|
92
|
+
if (mathMatch) {
|
|
93
|
+
const a = parseFloat(mathMatch[1]);
|
|
94
|
+
const op = mathMatch[2];
|
|
95
|
+
const b = parseFloat(mathMatch[3]);
|
|
96
|
+
let result: number;
|
|
97
|
+
switch (op) {
|
|
98
|
+
case "+":
|
|
99
|
+
result = a + b;
|
|
100
|
+
break;
|
|
101
|
+
case "-":
|
|
102
|
+
result = a - b;
|
|
103
|
+
break;
|
|
104
|
+
case "*":
|
|
105
|
+
case "x":
|
|
106
|
+
case "×":
|
|
107
|
+
result = a * b;
|
|
108
|
+
break;
|
|
109
|
+
case "/":
|
|
110
|
+
case "÷":
|
|
111
|
+
result = b !== 0 ? a / b : NaN;
|
|
112
|
+
break;
|
|
113
|
+
case "^":
|
|
114
|
+
result = Math.pow(a, b);
|
|
115
|
+
break;
|
|
116
|
+
default:
|
|
117
|
+
result = NaN;
|
|
118
|
+
}
|
|
119
|
+
if (isNaN(result)) {
|
|
120
|
+
return "Hmm, I couldn't calculate that. Did you try dividing by zero?";
|
|
121
|
+
}
|
|
122
|
+
const niceResult = Number.isInteger(result)
|
|
123
|
+
? result.toString()
|
|
124
|
+
: result.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
|
|
125
|
+
return `That would be **${niceResult}**! Need help with anything else?`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (/file|folder|directory|browse|explorer|open file|read file/i.test(lower)) {
|
|
129
|
+
return (
|
|
130
|
+
"To browse and manage files, use the **File Explorer** panel on the left sidebar. " +
|
|
131
|
+
"You can navigate directories, preview files, and open them for editing. " +
|
|
132
|
+
"Try typing a file path or asking me about a specific directory!"
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (/git|commit|branch|push|pull|merge|status|diff|log/i.test(lower)) {
|
|
137
|
+
return (
|
|
138
|
+
"The **Git Panel** gives you full source control right inside the app. " +
|
|
139
|
+
"You can view changes, stage files, commit, switch branches, push/pull, and see the commit log. " +
|
|
140
|
+
"Look for the source control icon in the sidebar to get started."
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (/^(help|commands|what can you|what do you|capabilities|features)/i.test(lower)) {
|
|
145
|
+
return (
|
|
146
|
+
"Here's what I can help with:\n\n" +
|
|
147
|
+
"- **Greetings** - Say hi and I'll say hi back!\n" +
|
|
148
|
+
'- **Time & Date** - Ask "what time is it?" or "what\'s today\'s date?"\n' +
|
|
149
|
+
'- **Math** - Give me an expression like "12 * 8" or "what is 100 / 4"\n' +
|
|
150
|
+
"- **Files** - Ask about file browsing and I'll explain the File Explorer\n" +
|
|
151
|
+
"- **Git** - Ask about version control and I'll walk you through it\n" +
|
|
152
|
+
"- **Help** - Show this message anytime!\n\n" +
|
|
153
|
+
"This is a demo assistant - try different things to see what sticks!"
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const defaults = [
|
|
158
|
+
"That's an interesting question! I'm a demo assistant, so my knowledge is limited - but try asking about **time**, **math**, **files**, or **git**.",
|
|
159
|
+
"I wish I could help with that! For now I can answer questions about time, do simple math, and explain the app's features. Type **help** to see what I can do.",
|
|
160
|
+
"Hmm, I'm not sure about that one. But I *can* do math, tell you the time, and explain features. Give it a shot!",
|
|
161
|
+
];
|
|
162
|
+
return defaults[Math.floor(Math.random() * defaults.length)];
|
|
132
163
|
}
|
|
133
164
|
|
|
134
|
-
export function register(server: RPCServer,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
165
|
+
export function register(server: RPCServer, options: HandlerOptions): void {
|
|
166
|
+
const storagePath = getStoragePathFor(options.platform);
|
|
167
|
+
const store = createMessageStore(storagePath);
|
|
168
|
+
|
|
169
|
+
const r: RegisterFn = (method, handler) => {
|
|
170
|
+
server.register(method, handler as (params: unknown) => Promise<unknown>);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
r("chat.list", async (params) => {
|
|
174
|
+
const all = await store.load();
|
|
175
|
+
const limit = params.limit ?? 50;
|
|
176
|
+
const messages = all.slice(-limit);
|
|
177
|
+
return {
|
|
178
|
+
messages,
|
|
179
|
+
hasMore: all.length > limit,
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
r("chat.send", async (params) => {
|
|
184
|
+
const all = await store.load();
|
|
185
|
+
|
|
186
|
+
const userMsg: ChatMessage = {
|
|
187
|
+
id: `msg-${Date.now()}-user`,
|
|
188
|
+
role: "user",
|
|
189
|
+
content: params.content,
|
|
190
|
+
timestamp: Date.now(),
|
|
191
|
+
};
|
|
192
|
+
all.push(userMsg);
|
|
193
|
+
|
|
194
|
+
server.emitEvent("chat.message", userMsg, { role: userMsg.role });
|
|
195
|
+
|
|
196
|
+
const thinkDelay = 200 + Math.floor(Math.random() * 300);
|
|
197
|
+
await new Promise((r) => setTimeout(r, thinkDelay));
|
|
198
|
+
|
|
199
|
+
const reply: ChatMessage = {
|
|
200
|
+
id: `msg-${Date.now()}-assistant`,
|
|
201
|
+
role: "assistant",
|
|
202
|
+
content: generateReply(params.content),
|
|
203
|
+
timestamp: Date.now(),
|
|
204
|
+
};
|
|
205
|
+
all.push(reply);
|
|
206
|
+
|
|
207
|
+
server.emitEvent("chat.message", reply, { role: reply.role });
|
|
208
|
+
|
|
209
|
+
await store.save(all);
|
|
210
|
+
|
|
211
|
+
return { ok: true };
|
|
212
|
+
});
|
|
179
213
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RPCServer } from "@dyyz1993/rpc-core";
|
|
2
|
+
import type { HandlerOptions } from "../rpc-schema";
|
|
3
|
+
|
|
4
|
+
export function register(server: RPCServer, _options: HandlerOptions): void {
|
|
5
|
+
server.register("debug.subscriptions", async () => {
|
|
6
|
+
return { subscriptions: server.getActiveSubscriptions() };
|
|
7
|
+
});
|
|
8
|
+
}
|
|
@@ -6,48 +6,47 @@ import { createLogger } from "../lib/logger";
|
|
|
6
6
|
|
|
7
7
|
const log = createLogger("feed");
|
|
8
8
|
|
|
9
|
-
// 内存存储
|
|
10
|
-
const posts: FeedPost[] = [];
|
|
11
|
-
|
|
12
9
|
type RegisterFn = <K extends keyof RPCMethods & string>(
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
method: K,
|
|
11
|
+
handler: (params: MethodParams<RPCMethods, K>) => Promise<MethodResult<RPCMethods, K>>
|
|
15
12
|
) => void;
|
|
16
13
|
|
|
17
14
|
export function register(server: RPCServer, _options: HandlerOptions): void {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
const posts: FeedPost[] = [];
|
|
16
|
+
|
|
17
|
+
const r: RegisterFn = (method, handler) => {
|
|
18
|
+
server.register(method, handler as (params: unknown) => Promise<unknown>);
|
|
19
|
+
};
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
r("feed.post", async (params) => {
|
|
22
|
+
const category = params.category as FeedCategory;
|
|
23
|
+
const author = (params.author as string) || "anonymous";
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
const post: FeedPost = {
|
|
26
|
+
id: `feed-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
27
|
+
content: params.content,
|
|
28
|
+
category,
|
|
29
|
+
author,
|
|
30
|
+
timestamp: Date.now(),
|
|
31
|
+
};
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
posts.push(post);
|
|
34
|
+
log.info(`New post: ${post.id} [${category}] by ${author}`);
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
// 发送事件 — metadata 用于过滤
|
|
37
|
+
server.emitEvent("feed.update", post, { category, author });
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
return { id: post.id };
|
|
40
|
+
});
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
r("feed.list", async (params) => {
|
|
43
|
+
const limit = params.limit ?? 50;
|
|
44
|
+
let filtered = posts;
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
if (params.category) {
|
|
47
|
+
filtered = posts.filter((p) => p.category === params.category);
|
|
48
|
+
}
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
return { posts: filtered.slice(-limit) };
|
|
51
|
+
});
|
|
53
52
|
}
|
|
@@ -4,42 +4,42 @@ import type { RPCMethods, HandlerOptions } from "../rpc-schema";
|
|
|
4
4
|
import type { Rule } from "../modules/rules";
|
|
5
5
|
|
|
6
6
|
type RegisterFn = <K extends keyof RPCMethods & string>(
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
method: K,
|
|
8
|
+
handler: (params: MethodParams<RPCMethods, K>) => Promise<MethodResult<RPCMethods, K>>
|
|
9
9
|
) => void;
|
|
10
10
|
|
|
11
|
-
const rules: Rule[] = [];
|
|
12
|
-
let ruleIdCounter = 1;
|
|
13
|
-
|
|
14
11
|
export function register(server: RPCServer, _options: HandlerOptions): void {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
const rules: Rule[] = [];
|
|
13
|
+
let ruleIdCounter = 1;
|
|
14
|
+
|
|
15
|
+
const r: RegisterFn = (method, handler) => {
|
|
16
|
+
server.register(method, handler as (params: unknown) => Promise<unknown>);
|
|
17
|
+
};
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
r("rules.list", async () => ({ rules }));
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
r("rules.add", async (params) => {
|
|
22
|
+
const rule: Rule = {
|
|
23
|
+
id: `rule-${ruleIdCounter++}`,
|
|
24
|
+
name: params.name,
|
|
25
|
+
pattern: params.pattern,
|
|
26
|
+
enabled: true,
|
|
27
|
+
};
|
|
28
|
+
rules.push(rule);
|
|
29
|
+
return { rule };
|
|
30
|
+
});
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
r("rules.toggle", async (params) => {
|
|
33
|
+
const rule = rules.find((r) => r.id === params.id);
|
|
34
|
+
if (!rule) throw new Error(`Rule ${params.id} not found`);
|
|
35
|
+
rule.enabled = !rule.enabled;
|
|
36
|
+
return { rule };
|
|
37
|
+
});
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
r("rules.remove", async (params) => {
|
|
40
|
+
const idx = rules.findIndex((r) => r.id === params.id);
|
|
41
|
+
if (idx === -1) return { success: false };
|
|
42
|
+
rules.splice(idx, 1);
|
|
43
|
+
return { success: true };
|
|
44
|
+
});
|
|
45
45
|
}
|
|
@@ -4,42 +4,42 @@ import type { RPCMethods, HandlerOptions } from "../rpc-schema";
|
|
|
4
4
|
import type { TodoItem, TodoStatus } from "../modules/todo";
|
|
5
5
|
|
|
6
6
|
type RegisterFn = <K extends keyof RPCMethods & string>(
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
method: K,
|
|
8
|
+
handler: (params: MethodParams<RPCMethods, K>) => Promise<MethodResult<RPCMethods, K>>
|
|
9
9
|
) => void;
|
|
10
10
|
|
|
11
|
-
const items: TodoItem[] = [];
|
|
12
|
-
let todoIdCounter = 1;
|
|
13
|
-
|
|
14
11
|
export function register(server: RPCServer, _options: HandlerOptions): void {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
const items: TodoItem[] = [];
|
|
13
|
+
let todoIdCounter = 1;
|
|
14
|
+
|
|
15
|
+
const r: RegisterFn = (method, handler) => {
|
|
16
|
+
server.register(method, handler as (params: unknown) => Promise<unknown>);
|
|
17
|
+
};
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
r("todo.list", async () => ({ items }));
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
r("todo.add", async (params) => {
|
|
22
|
+
const item: TodoItem = {
|
|
23
|
+
id: `todo-${todoIdCounter++}`,
|
|
24
|
+
content: params.content,
|
|
25
|
+
status: "pending",
|
|
26
|
+
createdAt: Date.now(),
|
|
27
|
+
};
|
|
28
|
+
items.push(item);
|
|
29
|
+
return { item };
|
|
30
|
+
});
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
r("todo.update", async (params) => {
|
|
33
|
+
const item = items.find((i) => i.id === params.id);
|
|
34
|
+
if (!item) throw new Error(`Todo ${params.id} not found`);
|
|
35
|
+
item.status = params.status as TodoStatus;
|
|
36
|
+
return { item };
|
|
37
|
+
});
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
r("todo.remove", async (params) => {
|
|
40
|
+
const idx = items.findIndex((i) => i.id === params.id);
|
|
41
|
+
if (idx === -1) return { success: false };
|
|
42
|
+
items.splice(idx, 1);
|
|
43
|
+
return { success: true };
|
|
44
|
+
});
|
|
45
45
|
}
|