iris-chatbot 0.2.4
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/LICENSE +21 -0
- package/README.md +49 -0
- package/bin/iris.mjs +267 -0
- package/package.json +61 -0
- package/template/LICENSE +21 -0
- package/template/README.md +49 -0
- package/template/eslint.config.mjs +18 -0
- package/template/next.config.ts +7 -0
- package/template/package-lock.json +9193 -0
- package/template/package.json +46 -0
- package/template/postcss.config.mjs +7 -0
- package/template/public/file.svg +1 -0
- package/template/public/globe.svg +1 -0
- package/template/public/next.svg +1 -0
- package/template/public/vercel.svg +1 -0
- package/template/public/window.svg +1 -0
- package/template/src/app/api/chat/route.ts +2445 -0
- package/template/src/app/api/connections/models/route.ts +255 -0
- package/template/src/app/api/connections/test/route.ts +124 -0
- package/template/src/app/api/local-sync/route.ts +74 -0
- package/template/src/app/api/tool-approval/route.ts +47 -0
- package/template/src/app/favicon.ico +0 -0
- package/template/src/app/globals.css +808 -0
- package/template/src/app/layout.tsx +74 -0
- package/template/src/app/page.tsx +444 -0
- package/template/src/components/ChatView.tsx +1537 -0
- package/template/src/components/Composer.tsx +160 -0
- package/template/src/components/MapView.tsx +244 -0
- package/template/src/components/MessageCard.tsx +955 -0
- package/template/src/components/SearchModal.tsx +72 -0
- package/template/src/components/SettingsModal.tsx +1257 -0
- package/template/src/components/Sidebar.tsx +153 -0
- package/template/src/components/TopBar.tsx +164 -0
- package/template/src/lib/connections.ts +275 -0
- package/template/src/lib/data.ts +324 -0
- package/template/src/lib/db.ts +49 -0
- package/template/src/lib/hooks.ts +76 -0
- package/template/src/lib/local-sync.ts +192 -0
- package/template/src/lib/memory.ts +695 -0
- package/template/src/lib/model-presets.ts +251 -0
- package/template/src/lib/store.ts +36 -0
- package/template/src/lib/tooling/approvals.ts +78 -0
- package/template/src/lib/tooling/providers/anthropic.ts +155 -0
- package/template/src/lib/tooling/providers/ollama.ts +73 -0
- package/template/src/lib/tooling/providers/openai.ts +267 -0
- package/template/src/lib/tooling/providers/openai_compatible.ts +16 -0
- package/template/src/lib/tooling/providers/types.ts +44 -0
- package/template/src/lib/tooling/registry.ts +103 -0
- package/template/src/lib/tooling/runtime.ts +189 -0
- package/template/src/lib/tooling/safety.ts +165 -0
- package/template/src/lib/tooling/tools/apps.ts +108 -0
- package/template/src/lib/tooling/tools/apps_plus.ts +153 -0
- package/template/src/lib/tooling/tools/communication.ts +883 -0
- package/template/src/lib/tooling/tools/files.ts +395 -0
- package/template/src/lib/tooling/tools/music.ts +988 -0
- package/template/src/lib/tooling/tools/notes.ts +461 -0
- package/template/src/lib/tooling/tools/notes_plus.ts +294 -0
- package/template/src/lib/tooling/tools/numbers.ts +175 -0
- package/template/src/lib/tooling/tools/schedule.ts +579 -0
- package/template/src/lib/tooling/tools/system.ts +142 -0
- package/template/src/lib/tooling/tools/web.ts +212 -0
- package/template/src/lib/tooling/tools/workflow.ts +218 -0
- package/template/src/lib/tooling/types.ts +27 -0
- package/template/src/lib/types.ts +309 -0
- package/template/src/lib/utils.ts +108 -0
- package/template/tsconfig.json +34 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import nodeFs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { assertAllowedPath, ensureMacOS } from "../safety";
|
|
5
|
+
import { runCommandSafe } from "../runtime";
|
|
6
|
+
import type { ToolDefinition, ToolExecutionContext } from "../types";
|
|
7
|
+
|
|
8
|
+
type FileListInput = {
|
|
9
|
+
path: string;
|
|
10
|
+
recursive?: boolean;
|
|
11
|
+
pattern?: string;
|
|
12
|
+
limit?: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type FileWriteInput = {
|
|
16
|
+
source?: string;
|
|
17
|
+
destination?: string;
|
|
18
|
+
path?: string;
|
|
19
|
+
overwrite?: boolean;
|
|
20
|
+
recursive?: boolean;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const APPLESCRIPT_TIMEOUT_MS = 20_000;
|
|
24
|
+
|
|
25
|
+
function asObject(input: unknown): Record<string, unknown> {
|
|
26
|
+
if (!input || typeof input !== "object") {
|
|
27
|
+
throw new Error("Tool input must be an object.");
|
|
28
|
+
}
|
|
29
|
+
return input as Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function asString(input: unknown, field: string): string {
|
|
33
|
+
if (typeof input !== "string" || !input.trim()) {
|
|
34
|
+
throw new Error(`Missing required string field: ${field}`);
|
|
35
|
+
}
|
|
36
|
+
return input.trim();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function asOptionalBoolean(input: unknown, defaultValue: boolean): boolean {
|
|
40
|
+
if (typeof input === "boolean") {
|
|
41
|
+
return input;
|
|
42
|
+
}
|
|
43
|
+
return defaultValue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function wildcardToRegex(pattern: string): RegExp {
|
|
47
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
48
|
+
return new RegExp(`^${escaped.replace(/\*/g, ".*")}$`, "i");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function listDirectory(params: {
|
|
52
|
+
rootPath: string;
|
|
53
|
+
recursive: boolean;
|
|
54
|
+
pattern?: string;
|
|
55
|
+
limit: number;
|
|
56
|
+
signal?: AbortSignal;
|
|
57
|
+
}) {
|
|
58
|
+
const matcher = params.pattern ? wildcardToRegex(params.pattern) : null;
|
|
59
|
+
const output: Array<Record<string, unknown>> = [];
|
|
60
|
+
const queue: string[] = [params.rootPath];
|
|
61
|
+
let totalCount = 0;
|
|
62
|
+
|
|
63
|
+
while (queue.length > 0) {
|
|
64
|
+
if (params.signal?.aborted) {
|
|
65
|
+
throw new Error("Aborted.");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const current = queue.shift() as string;
|
|
69
|
+
const entries = await fs.readdir(current, { withFileTypes: true });
|
|
70
|
+
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const fullPath = path.join(current, entry.name);
|
|
73
|
+
if (matcher && !matcher.test(entry.name)) {
|
|
74
|
+
if (!(params.recursive && entry.isDirectory())) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (entry.isDirectory()) {
|
|
80
|
+
totalCount += 1;
|
|
81
|
+
if (output.length < params.limit) {
|
|
82
|
+
output.push({
|
|
83
|
+
path: fullPath,
|
|
84
|
+
name: entry.name,
|
|
85
|
+
type: "directory",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (params.recursive) {
|
|
89
|
+
queue.push(fullPath);
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const stat = await fs.stat(fullPath);
|
|
95
|
+
totalCount += 1;
|
|
96
|
+
if (output.length < params.limit) {
|
|
97
|
+
output.push({
|
|
98
|
+
path: fullPath,
|
|
99
|
+
name: entry.name,
|
|
100
|
+
type: "file",
|
|
101
|
+
size: stat.size,
|
|
102
|
+
modifiedAt: stat.mtimeMs,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
entries: output,
|
|
110
|
+
totalCount,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function runAppleScript(
|
|
115
|
+
script: string,
|
|
116
|
+
args: string[],
|
|
117
|
+
signal?: AbortSignal,
|
|
118
|
+
): Promise<string> {
|
|
119
|
+
const { stdout } = await runCommandSafe({
|
|
120
|
+
command: "osascript",
|
|
121
|
+
args: ["-e", script, ...args.map((arg) => String(arg))],
|
|
122
|
+
signal,
|
|
123
|
+
timeoutMs: APPLESCRIPT_TIMEOUT_MS,
|
|
124
|
+
});
|
|
125
|
+
return stdout;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function runFileList(input: unknown, context: ToolExecutionContext) {
|
|
129
|
+
const payload = asObject(input) as FileListInput;
|
|
130
|
+
const targetPath = asString(payload.path, "path");
|
|
131
|
+
const rootPath = await assertAllowedPath({
|
|
132
|
+
candidate: targetPath,
|
|
133
|
+
localTools: context.localTools,
|
|
134
|
+
mode: "read",
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const recursive = asOptionalBoolean(payload.recursive, false);
|
|
138
|
+
const pattern = typeof payload.pattern === "string" && payload.pattern.trim()
|
|
139
|
+
? payload.pattern.trim()
|
|
140
|
+
: undefined;
|
|
141
|
+
const limit =
|
|
142
|
+
typeof payload.limit === "number" && Number.isFinite(payload.limit)
|
|
143
|
+
? Math.max(1, Math.min(1000, Math.floor(payload.limit)))
|
|
144
|
+
: 200;
|
|
145
|
+
|
|
146
|
+
const { entries, totalCount } = await listDirectory({
|
|
147
|
+
rootPath,
|
|
148
|
+
recursive,
|
|
149
|
+
pattern,
|
|
150
|
+
limit,
|
|
151
|
+
signal: context.signal,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
rootPath,
|
|
156
|
+
recursive,
|
|
157
|
+
pattern,
|
|
158
|
+
count: totalCount,
|
|
159
|
+
returnedCount: entries.length,
|
|
160
|
+
limit,
|
|
161
|
+
truncated: totalCount > entries.length,
|
|
162
|
+
entries,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function runFileMove(input: unknown, context: ToolExecutionContext) {
|
|
167
|
+
const payload = asObject(input) as FileWriteInput;
|
|
168
|
+
const source = asString(payload.source, "source");
|
|
169
|
+
const destination = asString(payload.destination, "destination");
|
|
170
|
+
const overwrite = asOptionalBoolean(payload.overwrite, false);
|
|
171
|
+
|
|
172
|
+
const resolvedSource = await assertAllowedPath({
|
|
173
|
+
candidate: source,
|
|
174
|
+
localTools: context.localTools,
|
|
175
|
+
mode: "read",
|
|
176
|
+
});
|
|
177
|
+
const resolvedDestination = await assertAllowedPath({
|
|
178
|
+
candidate: destination,
|
|
179
|
+
localTools: context.localTools,
|
|
180
|
+
mode: "write",
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
if (!overwrite) {
|
|
184
|
+
try {
|
|
185
|
+
await fs.access(resolvedDestination);
|
|
186
|
+
throw new Error(`Destination already exists: ${resolvedDestination}`);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (context.localTools.dryRun) {
|
|
195
|
+
return {
|
|
196
|
+
dryRun: true,
|
|
197
|
+
action: "file_move",
|
|
198
|
+
source: resolvedSource,
|
|
199
|
+
destination: resolvedDestination,
|
|
200
|
+
overwrite,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
await fs.mkdir(path.dirname(resolvedDestination), { recursive: true });
|
|
205
|
+
await fs.rename(resolvedSource, resolvedDestination);
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
moved: true,
|
|
209
|
+
source: resolvedSource,
|
|
210
|
+
destination: resolvedDestination,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function runFileCopy(input: unknown, context: ToolExecutionContext) {
|
|
215
|
+
const payload = asObject(input) as FileWriteInput;
|
|
216
|
+
const source = asString(payload.source, "source");
|
|
217
|
+
const destination = asString(payload.destination, "destination");
|
|
218
|
+
const overwrite = asOptionalBoolean(payload.overwrite, false);
|
|
219
|
+
|
|
220
|
+
const resolvedSource = await assertAllowedPath({
|
|
221
|
+
candidate: source,
|
|
222
|
+
localTools: context.localTools,
|
|
223
|
+
mode: "read",
|
|
224
|
+
});
|
|
225
|
+
const resolvedDestination = await assertAllowedPath({
|
|
226
|
+
candidate: destination,
|
|
227
|
+
localTools: context.localTools,
|
|
228
|
+
mode: "write",
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const copyFlag = overwrite ? 0 : nodeFs.constants.COPYFILE_EXCL;
|
|
232
|
+
|
|
233
|
+
if (context.localTools.dryRun) {
|
|
234
|
+
return {
|
|
235
|
+
dryRun: true,
|
|
236
|
+
action: "file_copy",
|
|
237
|
+
source: resolvedSource,
|
|
238
|
+
destination: resolvedDestination,
|
|
239
|
+
overwrite,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
await fs.mkdir(path.dirname(resolvedDestination), { recursive: true });
|
|
244
|
+
await fs.copyFile(resolvedSource, resolvedDestination, copyFlag);
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
copied: true,
|
|
248
|
+
source: resolvedSource,
|
|
249
|
+
destination: resolvedDestination,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function runFileMkdir(input: unknown, context: ToolExecutionContext) {
|
|
254
|
+
const payload = asObject(input) as FileWriteInput;
|
|
255
|
+
const target = asString(payload.path, "path");
|
|
256
|
+
const recursive = asOptionalBoolean(payload.recursive, true);
|
|
257
|
+
|
|
258
|
+
const resolvedTarget = await assertAllowedPath({
|
|
259
|
+
candidate: target,
|
|
260
|
+
localTools: context.localTools,
|
|
261
|
+
mode: "write",
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
if (context.localTools.dryRun) {
|
|
265
|
+
return {
|
|
266
|
+
dryRun: true,
|
|
267
|
+
action: "file_mkdir",
|
|
268
|
+
path: resolvedTarget,
|
|
269
|
+
recursive,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
await fs.mkdir(resolvedTarget, { recursive });
|
|
274
|
+
return {
|
|
275
|
+
created: true,
|
|
276
|
+
path: resolvedTarget,
|
|
277
|
+
recursive,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function runFileDeleteToTrash(input: unknown, context: ToolExecutionContext) {
|
|
282
|
+
const payload = asObject(input) as FileWriteInput;
|
|
283
|
+
const target = asString(payload.path, "path");
|
|
284
|
+
|
|
285
|
+
const resolvedTarget = await assertAllowedPath({
|
|
286
|
+
candidate: target,
|
|
287
|
+
localTools: context.localTools,
|
|
288
|
+
mode: "read",
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (context.localTools.dryRun) {
|
|
292
|
+
return {
|
|
293
|
+
dryRun: true,
|
|
294
|
+
action: "file_delete_to_trash",
|
|
295
|
+
path: resolvedTarget,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
ensureMacOS("Moving files to Trash");
|
|
300
|
+
const script =
|
|
301
|
+
'on run argv\n' +
|
|
302
|
+
'set targetPath to item 1 of argv\n' +
|
|
303
|
+
'tell application "Finder"\n' +
|
|
304
|
+
'delete POSIX file targetPath\n' +
|
|
305
|
+
'end tell\n' +
|
|
306
|
+
'return "ok"\n' +
|
|
307
|
+
'end run';
|
|
308
|
+
|
|
309
|
+
await runAppleScript(script, [resolvedTarget], context.signal);
|
|
310
|
+
return {
|
|
311
|
+
trashed: true,
|
|
312
|
+
path: resolvedTarget,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export const fileTools: ToolDefinition[] = [
|
|
317
|
+
{
|
|
318
|
+
name: "file_list",
|
|
319
|
+
description: "List files/folders under an allowed path. Supports optional recursion and wildcard name pattern.",
|
|
320
|
+
inputSchema: {
|
|
321
|
+
type: "object",
|
|
322
|
+
required: ["path"],
|
|
323
|
+
properties: {
|
|
324
|
+
path: { type: "string" },
|
|
325
|
+
recursive: { type: "boolean" },
|
|
326
|
+
pattern: { type: "string" },
|
|
327
|
+
limit: { type: "number" },
|
|
328
|
+
},
|
|
329
|
+
additionalProperties: false,
|
|
330
|
+
},
|
|
331
|
+
risk: "read",
|
|
332
|
+
execute: runFileList,
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: "file_move",
|
|
336
|
+
description: "Move a file/folder from source to destination.",
|
|
337
|
+
inputSchema: {
|
|
338
|
+
type: "object",
|
|
339
|
+
required: ["source", "destination"],
|
|
340
|
+
properties: {
|
|
341
|
+
source: { type: "string" },
|
|
342
|
+
destination: { type: "string" },
|
|
343
|
+
overwrite: { type: "boolean" },
|
|
344
|
+
},
|
|
345
|
+
additionalProperties: false,
|
|
346
|
+
},
|
|
347
|
+
risk: "write",
|
|
348
|
+
execute: runFileMove,
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: "file_copy",
|
|
352
|
+
description: "Copy a file from source to destination.",
|
|
353
|
+
inputSchema: {
|
|
354
|
+
type: "object",
|
|
355
|
+
required: ["source", "destination"],
|
|
356
|
+
properties: {
|
|
357
|
+
source: { type: "string" },
|
|
358
|
+
destination: { type: "string" },
|
|
359
|
+
overwrite: { type: "boolean" },
|
|
360
|
+
},
|
|
361
|
+
additionalProperties: false,
|
|
362
|
+
},
|
|
363
|
+
risk: "write",
|
|
364
|
+
execute: runFileCopy,
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: "file_mkdir",
|
|
368
|
+
description: "Create a folder path.",
|
|
369
|
+
inputSchema: {
|
|
370
|
+
type: "object",
|
|
371
|
+
required: ["path"],
|
|
372
|
+
properties: {
|
|
373
|
+
path: { type: "string" },
|
|
374
|
+
recursive: { type: "boolean" },
|
|
375
|
+
},
|
|
376
|
+
additionalProperties: false,
|
|
377
|
+
},
|
|
378
|
+
risk: "write",
|
|
379
|
+
execute: runFileMkdir,
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: "file_delete_to_trash",
|
|
383
|
+
description: "Move a file/folder to Trash (macOS).",
|
|
384
|
+
inputSchema: {
|
|
385
|
+
type: "object",
|
|
386
|
+
required: ["path"],
|
|
387
|
+
properties: {
|
|
388
|
+
path: { type: "string" },
|
|
389
|
+
},
|
|
390
|
+
additionalProperties: false,
|
|
391
|
+
},
|
|
392
|
+
risk: "destructive",
|
|
393
|
+
execute: runFileDeleteToTrash,
|
|
394
|
+
},
|
|
395
|
+
];
|