@dyyz1993/create-agent 2.0.1 → 2.1.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/package.json +1 -1
- package/src/commands/create.ts +31 -31
- package/src/commands/workspace.ts +112 -105
- package/src/lib/copy.ts +1 -0
- package/templates/agent/electron/main.js +46 -0
- package/templates/agent/electron/preload.js +5 -0
- package/templates/agent/electron-builder.json +40 -0
- package/templates/agent/eslint.config.mjs +2 -0
- package/templates/agent/package.json +60 -2
- package/templates/agent/src/mainview/App.tsx +29 -26
- package/templates/agent/src/mainview/components/chat/ChatPanel.tsx +88 -88
- package/templates/agent/src/mainview/components/file-preview/VirtualizedCodeView.tsx +105 -81
- package/templates/agent/src/mainview/components/search/SearchPanel.tsx +427 -378
- package/templates/agent/src/mainview/components/todo/TodoPanel.tsx +3 -3
- package/templates/agent/src/mainview/hooks/use-input-history.ts +70 -61
- package/templates/agent/src/mainview/lib/api-client.ts +1 -4
- package/templates/agent/src/mainview/main.tsx +4 -10
- package/templates/agent/src/mainview/stores/use-feed-store.ts +107 -107
- package/templates/agent/src/mainview/utils/drop-handler.ts +114 -115
- package/templates/agent/src/server-config.ts +1 -1
- package/templates/agent/src/server.ts +1 -2
- package/templates/agent/src/shared/handlers/chat.ts +5 -5
- package/templates/agent/src/shared/handlers/debug.ts +5 -1
- package/templates/agent/src/shared/handlers/git.ts +286 -243
- package/templates/agent/src/shared/http-routes.ts +1 -1
- package/templates/agent/src/shared/lib/bash-security.ts +43 -43
- package/templates/agent/tsconfig.ipc.json +5 -1
- package/templates/agent/tsconfig.json +3 -1
- package/templates/chat/package.json +3 -0
- package/templates/chat/src/mainview/hooks/use-input-history.ts +70 -61
- package/templates/chat/src/mainview/lib/api-client.ts +2 -5
- package/templates/chat/src/mainview/main.tsx +10 -7
- package/templates/chat/src/server-config.ts +1 -1
- package/templates/chat/src/server.ts +1 -2
- package/templates/chat/src/shared/handlers/chat.ts +5 -5
- package/templates/chat/src/shared/handlers/debug.ts +5 -1
- package/templates/chat/src/shared/http-routes.ts +1 -1
- package/templates/chat/tsconfig.ipc.json +5 -1
- package/templates/chat/tsconfig.json +12 -2
- package/templates/general/package.json +3 -0
- package/templates/general/src/mainview/components/file-preview/VirtualizedCodeView.tsx +101 -81
- package/templates/general/src/mainview/components/search/SearchPanel.tsx +429 -378
- package/templates/general/src/mainview/hooks/use-input-history.ts +70 -61
- package/templates/general/src/mainview/lib/api-client.ts +2 -5
- package/templates/general/src/mainview/main.tsx +10 -7
- package/templates/general/src/mainview/stores/use-feed-store.ts +107 -107
- package/templates/general/src/mainview/utils/drop-handler.ts +114 -115
- package/templates/general/src/server-config.ts +1 -1
- package/templates/general/src/server.ts +1 -2
- package/templates/general/src/shared/handlers/chat.ts +5 -5
- package/templates/general/src/shared/handlers/debug.ts +5 -1
- package/templates/general/src/shared/handlers/git.ts +286 -243
- package/templates/general/src/shared/http-routes.ts +1 -1
- package/templates/general/tsconfig.ipc.json +5 -1
- package/templates/general/tsconfig.json +12 -2
- package/templates/shared/components/ErrorBoundary.tsx +50 -49
- package/templates/shared/http-routes.ts +210 -190
|
@@ -1,150 +1,149 @@
|
|
|
1
1
|
import { apiClient } from "../lib/api-client";
|
|
2
2
|
|
|
3
3
|
export interface DropEntry {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
name: string;
|
|
5
|
+
relativePath: string;
|
|
6
|
+
file?: File;
|
|
7
|
+
isDirectory: boolean;
|
|
8
|
+
children?: DropEntry[];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* 递归读取 webkitGetAsEntry,返回扁平文件列表 + 目录结构
|
|
13
13
|
*/
|
|
14
|
-
function readEntry(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return new Promise((resolve) => {
|
|
27
|
-
const reader = (entry as FileSystemDirectoryEntry).createReader();
|
|
28
|
-
const children: DropEntry[] = [];
|
|
14
|
+
function readEntry(entry: FileSystemEntry, path: string): Promise<DropEntry> {
|
|
15
|
+
if (entry.isFile) {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
(entry as FileSystemFileEntry).file((file) => {
|
|
18
|
+
resolve({ name: entry.name, relativePath: path, file, isDirectory: false });
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
// Directory
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const reader = (entry as FileSystemDirectoryEntry).createReader();
|
|
25
|
+
const children: DropEntry[] = [];
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
27
|
+
const readBatch = () => {
|
|
28
|
+
reader.readEntries(async (entries) => {
|
|
29
|
+
if (entries.length === 0) {
|
|
30
|
+
resolve({ name: entry.name, relativePath: path, isDirectory: true, children });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
for (const e of entries) {
|
|
34
|
+
children.push(await readEntry(e, `${path}/${e.name}`));
|
|
35
|
+
}
|
|
36
|
+
readBatch(); // readEntries may not return all entries in one call
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
readBatch();
|
|
40
|
+
});
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
/**
|
|
47
44
|
* 从 DataTransfer 递归读取所有文件和目录
|
|
48
45
|
*/
|
|
49
46
|
export async function readDropItems(dataTransfer: DataTransfer): Promise<DropEntry[]> {
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
const items = dataTransfer.items;
|
|
48
|
+
if (!items) return [];
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
const entries: DropEntry[] = [];
|
|
51
|
+
const tasks: Promise<void>[] = [];
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
53
|
+
for (let i = 0; i < items.length; i++) {
|
|
54
|
+
const item = items[i]!;
|
|
55
|
+
// Try webkitGetAsEntry (Chrome, Edge, Safari)
|
|
56
|
+
const entry = item.webkitGetAsEntry?.();
|
|
57
|
+
if (entry) {
|
|
58
|
+
tasks.push(
|
|
59
|
+
readEntry(entry, entry.name).then<void>((e) => {
|
|
60
|
+
entries.push(e);
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
await Promise.all(tasks);
|
|
67
|
+
return entries;
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
/**
|
|
72
71
|
* Web 端:递归上传 entries 到目标目录
|
|
73
72
|
*/
|
|
74
73
|
export async function uploadEntriesWeb(entries: DropEntry[], destDir: string): Promise<number> {
|
|
75
|
-
|
|
74
|
+
let count = 0;
|
|
76
75
|
|
|
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
|
-
|
|
76
|
+
async function process(entry: DropEntry, currentDir: string): Promise<void> {
|
|
77
|
+
if (entry.isDirectory) {
|
|
78
|
+
// Create directory
|
|
79
|
+
const dirPath = `${currentDir}/${entry.name}`;
|
|
80
|
+
await apiClient.call("file.createDir", { dirPath: currentDir, name: entry.name });
|
|
81
|
+
count++;
|
|
82
|
+
if (entry.children) {
|
|
83
|
+
for (const child of entry.children) {
|
|
84
|
+
await process(child, dirPath);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} else if (entry.file) {
|
|
88
|
+
// Upload file via HTTP
|
|
89
|
+
const filePath = `${currentDir}/${entry.name}`;
|
|
90
|
+
const arrayBuffer = await entry.file.arrayBuffer();
|
|
91
|
+
const baseUrl = apiClient.getBaseUrl();
|
|
92
|
+
const token = apiClient.getAuthToken();
|
|
93
|
+
const res = await fetch(
|
|
94
|
+
`${baseUrl}/file/upload?path=${encodeURIComponent(filePath)}&token=${token}`,
|
|
95
|
+
{ method: "POST", body: arrayBuffer }
|
|
96
|
+
);
|
|
97
|
+
if (!res.ok) throw new Error(`Upload failed: ${entry.name}`);
|
|
98
|
+
count++;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
102
101
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
for (const entry of entries) {
|
|
103
|
+
await process(entry, destDir);
|
|
104
|
+
}
|
|
105
|
+
return count;
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
/**
|
|
110
109
|
* 桌面端:通过 RPC file.copy 直接复制
|
|
111
110
|
*/
|
|
112
111
|
export async function importFilesDesktop(entries: DropEntry[], destDir: string): Promise<number> {
|
|
113
|
-
|
|
112
|
+
let count = 0;
|
|
114
113
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
114
|
+
async function process(entry: DropEntry, currentDir: string): Promise<void> {
|
|
115
|
+
if (entry.isDirectory) {
|
|
116
|
+
const dirPath = `${currentDir}/${entry.name}`;
|
|
117
|
+
await apiClient.call("file.createDir", { dirPath: currentDir, name: entry.name });
|
|
118
|
+
count++;
|
|
119
|
+
if (entry.children) {
|
|
120
|
+
for (const child of entry.children) {
|
|
121
|
+
await process(child, dirPath);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} else if (entry.file) {
|
|
125
|
+
// Desktop: File object has .path property (Electron/Electrobun)
|
|
126
|
+
const srcPath = (entry.file as File & { path?: string }).path;
|
|
127
|
+
if (srcPath) {
|
|
128
|
+
await apiClient.call("file.copy", { srcPath, destDir: currentDir });
|
|
129
|
+
} else {
|
|
130
|
+
// Fallback: no path available, upload via HTTP
|
|
131
|
+
const filePath = `${currentDir}/${entry.name}`;
|
|
132
|
+
const arrayBuffer = await entry.file.arrayBuffer();
|
|
133
|
+
const baseUrl = apiClient.getBaseUrl();
|
|
134
|
+
const token = apiClient.getAuthToken();
|
|
135
|
+
const res = await fetch(
|
|
136
|
+
`${baseUrl}/file/upload?path=${encodeURIComponent(filePath)}&token=${token}`,
|
|
137
|
+
{ method: "POST", body: arrayBuffer }
|
|
138
|
+
);
|
|
139
|
+
if (!res.ok) throw new Error(`Upload failed: ${entry.name}`);
|
|
140
|
+
}
|
|
141
|
+
count++;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
145
144
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
for (const entry of entries) {
|
|
146
|
+
await process(entry, destDir);
|
|
147
|
+
}
|
|
148
|
+
return count;
|
|
150
149
|
}
|
|
@@ -22,7 +22,7 @@ export function parseEnvInt(
|
|
|
22
22
|
if (value === undefined || value === "") return defaultValue;
|
|
23
23
|
const parsed = parseInt(value, 10);
|
|
24
24
|
if (isNaN(parsed) || parsed < min || parsed > max) {
|
|
25
|
-
|
|
25
|
+
process.stderr.write(`[config] Invalid ${key}: "${value}", using default: ${defaultValue}\n`);
|
|
26
26
|
return defaultValue;
|
|
27
27
|
}
|
|
28
28
|
return parsed;
|
|
@@ -68,8 +68,7 @@ async function start() {
|
|
|
68
68
|
log.info(`WebSocket: ws://localhost:${port}/ws (auth required)`);
|
|
69
69
|
log.info(`Available RPC methods: ${discoverMethodNames().join(", ")}`);
|
|
70
70
|
log.info("File endpoints: GET /file/{path}, GET /info/{path}");
|
|
71
|
-
|
|
72
|
-
console.log("\n" + formatRegistryForOutput() + "\n");
|
|
71
|
+
log.info(formatRegistryForOutput());
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
start().catch((err) => {
|
|
@@ -69,7 +69,7 @@ function generateReply(input: string): string {
|
|
|
69
69
|
"Hello! Great to see you. What would you like to know?",
|
|
70
70
|
"Hi! I'm your desktop assistant. Ask me anything!",
|
|
71
71
|
];
|
|
72
|
-
return greetings[Math.floor(Math.random() * greetings.length)]
|
|
72
|
+
return greetings[Math.floor(Math.random() * greetings.length)]!;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
if (
|
|
@@ -90,9 +90,9 @@ function generateReply(input: string): string {
|
|
|
90
90
|
/(?:what(?:'s| is)\s+)?(\d+(?:\.\d+)?)\s*([+\-*/x×÷^])\s*(\d+(?:\.\d+)?)/
|
|
91
91
|
);
|
|
92
92
|
if (mathMatch) {
|
|
93
|
-
const a = parseFloat(mathMatch[1]);
|
|
94
|
-
const op = mathMatch[2]
|
|
95
|
-
const b = parseFloat(mathMatch[3]);
|
|
93
|
+
const a = parseFloat(mathMatch[1]!);
|
|
94
|
+
const op = mathMatch[2]!;
|
|
95
|
+
const b = parseFloat(mathMatch[3]!);
|
|
96
96
|
let result: number;
|
|
97
97
|
switch (op) {
|
|
98
98
|
case "+":
|
|
@@ -159,7 +159,7 @@ function generateReply(input: string): string {
|
|
|
159
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
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
161
|
];
|
|
162
|
-
return defaults[Math.floor(Math.random() * defaults.length)]
|
|
162
|
+
return defaults[Math.floor(Math.random() * defaults.length)]!;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
export function register(server: RPCServer, options: HandlerOptions): void {
|
|
@@ -3,6 +3,10 @@ import type { HandlerOptions } from "../rpc-schema";
|
|
|
3
3
|
|
|
4
4
|
export function register(server: RPCServer, _options: HandlerOptions): void {
|
|
5
5
|
server.register("debug.subscriptions", async () => {
|
|
6
|
-
return {
|
|
6
|
+
return {
|
|
7
|
+
subscriptions: (
|
|
8
|
+
server as unknown as { getActiveSubscriptions(): unknown }
|
|
9
|
+
).getActiveSubscriptions(),
|
|
10
|
+
};
|
|
7
11
|
});
|
|
8
12
|
}
|