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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -0
  3. package/bin/iris.mjs +267 -0
  4. package/package.json +61 -0
  5. package/template/LICENSE +21 -0
  6. package/template/README.md +49 -0
  7. package/template/eslint.config.mjs +18 -0
  8. package/template/next.config.ts +7 -0
  9. package/template/package-lock.json +9193 -0
  10. package/template/package.json +46 -0
  11. package/template/postcss.config.mjs +7 -0
  12. package/template/public/file.svg +1 -0
  13. package/template/public/globe.svg +1 -0
  14. package/template/public/next.svg +1 -0
  15. package/template/public/vercel.svg +1 -0
  16. package/template/public/window.svg +1 -0
  17. package/template/src/app/api/chat/route.ts +2445 -0
  18. package/template/src/app/api/connections/models/route.ts +255 -0
  19. package/template/src/app/api/connections/test/route.ts +124 -0
  20. package/template/src/app/api/local-sync/route.ts +74 -0
  21. package/template/src/app/api/tool-approval/route.ts +47 -0
  22. package/template/src/app/favicon.ico +0 -0
  23. package/template/src/app/globals.css +808 -0
  24. package/template/src/app/layout.tsx +74 -0
  25. package/template/src/app/page.tsx +444 -0
  26. package/template/src/components/ChatView.tsx +1537 -0
  27. package/template/src/components/Composer.tsx +160 -0
  28. package/template/src/components/MapView.tsx +244 -0
  29. package/template/src/components/MessageCard.tsx +955 -0
  30. package/template/src/components/SearchModal.tsx +72 -0
  31. package/template/src/components/SettingsModal.tsx +1257 -0
  32. package/template/src/components/Sidebar.tsx +153 -0
  33. package/template/src/components/TopBar.tsx +164 -0
  34. package/template/src/lib/connections.ts +275 -0
  35. package/template/src/lib/data.ts +324 -0
  36. package/template/src/lib/db.ts +49 -0
  37. package/template/src/lib/hooks.ts +76 -0
  38. package/template/src/lib/local-sync.ts +192 -0
  39. package/template/src/lib/memory.ts +695 -0
  40. package/template/src/lib/model-presets.ts +251 -0
  41. package/template/src/lib/store.ts +36 -0
  42. package/template/src/lib/tooling/approvals.ts +78 -0
  43. package/template/src/lib/tooling/providers/anthropic.ts +155 -0
  44. package/template/src/lib/tooling/providers/ollama.ts +73 -0
  45. package/template/src/lib/tooling/providers/openai.ts +267 -0
  46. package/template/src/lib/tooling/providers/openai_compatible.ts +16 -0
  47. package/template/src/lib/tooling/providers/types.ts +44 -0
  48. package/template/src/lib/tooling/registry.ts +103 -0
  49. package/template/src/lib/tooling/runtime.ts +189 -0
  50. package/template/src/lib/tooling/safety.ts +165 -0
  51. package/template/src/lib/tooling/tools/apps.ts +108 -0
  52. package/template/src/lib/tooling/tools/apps_plus.ts +153 -0
  53. package/template/src/lib/tooling/tools/communication.ts +883 -0
  54. package/template/src/lib/tooling/tools/files.ts +395 -0
  55. package/template/src/lib/tooling/tools/music.ts +988 -0
  56. package/template/src/lib/tooling/tools/notes.ts +461 -0
  57. package/template/src/lib/tooling/tools/notes_plus.ts +294 -0
  58. package/template/src/lib/tooling/tools/numbers.ts +175 -0
  59. package/template/src/lib/tooling/tools/schedule.ts +579 -0
  60. package/template/src/lib/tooling/tools/system.ts +142 -0
  61. package/template/src/lib/tooling/tools/web.ts +212 -0
  62. package/template/src/lib/tooling/tools/workflow.ts +218 -0
  63. package/template/src/lib/tooling/types.ts +27 -0
  64. package/template/src/lib/types.ts +309 -0
  65. package/template/src/lib/utils.ts +108 -0
  66. package/template/tsconfig.json +34 -0
@@ -0,0 +1,294 @@
1
+ import { runCommandSafe } from "../runtime";
2
+ import { ensureMacOS } from "../safety";
3
+ import type { ToolDefinition, ToolExecutionContext } from "../types";
4
+ import { normalizeNotesBody } from "./notes";
5
+
6
+ type NotesGetByTitleInput = {
7
+ title?: string;
8
+ folder?: string;
9
+ };
10
+
11
+ type NotesReplaceSectionInput = {
12
+ title?: string;
13
+ heading?: string;
14
+ content?: string;
15
+ folder?: string;
16
+ createIfMissing?: boolean;
17
+ };
18
+
19
+ type NotesStructuredSection = {
20
+ heading: string;
21
+ bullets?: string[];
22
+ paragraphs?: string[];
23
+ };
24
+
25
+ type NotesCreateStructuredInput = {
26
+ title?: string;
27
+ folder?: string;
28
+ sections?: NotesStructuredSection[];
29
+ };
30
+
31
+ const APPLESCRIPT_TIMEOUT_MS = 30_000;
32
+
33
+ function asObject(input: unknown): Record<string, unknown> {
34
+ if (!input || typeof input !== "object") {
35
+ throw new Error("Tool input must be an object.");
36
+ }
37
+ return input as Record<string, unknown>;
38
+ }
39
+
40
+ function asString(input: unknown, field: string): string {
41
+ if (typeof input !== "string" || !input.trim()) {
42
+ throw new Error(`Missing required string field: ${field}`);
43
+ }
44
+ return input.trim();
45
+ }
46
+
47
+ async function runAppleScript(script: string, args: string[] = [], signal?: AbortSignal) {
48
+ const { stdout } = await runCommandSafe({
49
+ command: "osascript",
50
+ args: ["-e", script, ...args],
51
+ signal,
52
+ timeoutMs: APPLESCRIPT_TIMEOUT_MS,
53
+ });
54
+ return stdout;
55
+ }
56
+
57
+ function buildStructuredBody(sections: NotesStructuredSection[]): string {
58
+ return sections
59
+ .map((section) => {
60
+ const lines = [`## ${section.heading}`];
61
+ if (Array.isArray(section.paragraphs)) {
62
+ lines.push(...section.paragraphs.filter(Boolean));
63
+ }
64
+ if (Array.isArray(section.bullets)) {
65
+ lines.push(...section.bullets.filter(Boolean).map((bullet) => `- ${bullet}`));
66
+ }
67
+ return lines.join("\n");
68
+ })
69
+ .join("\n\n");
70
+ }
71
+
72
+ async function runNotesGetByTitle(input: unknown) {
73
+ ensureMacOS("Apple Notes automation");
74
+ const payload = asObject(input) as NotesGetByTitleInput;
75
+ const title = asString(payload.title, "title");
76
+ const folder = typeof payload.folder === "string" ? payload.folder.trim() : "";
77
+
78
+ const script =
79
+ 'on run argv\n' +
80
+ 'set targetTitle to item 1 of argv\n' +
81
+ 'set targetFolderName to item 2 of argv\n' +
82
+ 'tell application "Notes"\n' +
83
+ 'repeat with acc in accounts\n' +
84
+ 'repeat with f in folders of acc\n' +
85
+ 'if targetFolderName is "" or name of f is targetFolderName then\n' +
86
+ 'repeat with n in notes of f\n' +
87
+ 'if name of n is targetTitle then\n' +
88
+ 'set noteBody to body of n\n' +
89
+ 'return (name of n as text) & tab & (name of f as text) & tab & noteBody\n' +
90
+ 'end if\n' +
91
+ 'end repeat\n' +
92
+ 'end if\n' +
93
+ 'end repeat\n' +
94
+ 'end repeat\n' +
95
+ 'end tell\n' +
96
+ 'return ""\n' +
97
+ 'end run';
98
+ const output = await runAppleScript(script, [title, folder]);
99
+ if (!output) {
100
+ return { found: false, title, folder: folder || null };
101
+ }
102
+
103
+ const [foundTitle, foundFolder, body] = output.split("\t");
104
+ return {
105
+ found: true,
106
+ title: foundTitle,
107
+ folder: foundFolder,
108
+ bodyPreview: (body ?? "").replace(/\s+/g, " ").slice(0, 600),
109
+ };
110
+ }
111
+
112
+ async function runNotesReplaceSection(input: unknown, context: ToolExecutionContext) {
113
+ ensureMacOS("Apple Notes automation");
114
+ const payload = asObject(input) as NotesReplaceSectionInput;
115
+ const title = asString(payload.title, "title");
116
+ const heading = asString(payload.heading, "heading");
117
+ const content = asString(payload.content, "content");
118
+ const folder = typeof payload.folder === "string" ? payload.folder.trim() : "";
119
+ const createIfMissing = payload.createIfMissing !== false;
120
+ const sectionHtml = normalizeNotesBody(`## ${heading}\n${content}`);
121
+
122
+ if (context.localTools.dryRun) {
123
+ return {
124
+ dryRun: true,
125
+ action: "notes_replace_section",
126
+ title,
127
+ heading,
128
+ folder,
129
+ createIfMissing,
130
+ contentPreview: sectionHtml.slice(0, 200),
131
+ };
132
+ }
133
+
134
+ const script =
135
+ 'on run argv\n' +
136
+ 'set targetTitle to item 1 of argv\n' +
137
+ 'set folderName to item 2 of argv\n' +
138
+ 'set headingText to item 3 of argv\n' +
139
+ 'set newSection to item 4 of argv\n' +
140
+ 'set createMissing to (item 5 of argv) is "true"\n' +
141
+ 'tell application "Notes"\n' +
142
+ 'repeat with acc in accounts\n' +
143
+ 'repeat with f in folders of acc\n' +
144
+ 'if folderName is "" or name of f is folderName then\n' +
145
+ 'repeat with n in notes of f\n' +
146
+ 'if name of n is targetTitle then\n' +
147
+ 'set noteBody to body of n as text\n' +
148
+ 'if noteBody contains headingText then\n' +
149
+ 'set body of n to noteBody & "<br/>" & newSection\n' +
150
+ 'return "updated"\n' +
151
+ 'else if createMissing then\n' +
152
+ 'set body of n to noteBody & "<br/>" & newSection\n' +
153
+ 'return "appended"\n' +
154
+ 'else\n' +
155
+ 'return "heading_missing"\n' +
156
+ 'end if\n' +
157
+ 'end if\n' +
158
+ 'end repeat\n' +
159
+ 'end if\n' +
160
+ 'end repeat\n' +
161
+ 'end repeat\n' +
162
+ 'end tell\n' +
163
+ 'return "not_found"\n' +
164
+ 'end run';
165
+ const status = await runAppleScript(
166
+ script,
167
+ [title, folder, heading, sectionHtml, createIfMissing ? "true" : "false"],
168
+ context.signal,
169
+ );
170
+ return { status, title, heading, folder: folder || null };
171
+ }
172
+
173
+ async function runNotesCreateStructured(input: unknown, context: ToolExecutionContext) {
174
+ const payload = asObject(input) as NotesCreateStructuredInput;
175
+ const title = asString(payload.title, "title");
176
+ const folder = typeof payload.folder === "string" ? payload.folder.trim() : "";
177
+ if (!Array.isArray(payload.sections) || payload.sections.length === 0) {
178
+ throw new Error("Missing required array field: sections");
179
+ }
180
+
181
+ const sections = payload.sections
182
+ .filter((section): section is NotesStructuredSection => {
183
+ return Boolean(section && typeof section.heading === "string" && section.heading.trim());
184
+ })
185
+ .map((section) => ({
186
+ heading: section.heading.trim(),
187
+ bullets: Array.isArray(section.bullets) ? section.bullets.filter(Boolean) : [],
188
+ paragraphs: Array.isArray(section.paragraphs) ? section.paragraphs.filter(Boolean) : [],
189
+ }));
190
+
191
+ const body = buildStructuredBody(sections);
192
+ const formattedBody = normalizeNotesBody(body);
193
+ if (context.localTools.dryRun) {
194
+ return {
195
+ dryRun: true,
196
+ action: "notes_create_structured",
197
+ title,
198
+ folder,
199
+ sectionCount: sections.length,
200
+ bodyPreview: formattedBody.slice(0, 240),
201
+ };
202
+ }
203
+
204
+ // Reuse base notes tool behavior through AppleScript create mode.
205
+ const script =
206
+ 'on run argv\n' +
207
+ 'set noteTitle to item 1 of argv\n' +
208
+ 'set noteBody to item 2 of argv\n' +
209
+ 'set folderName to item 3 of argv\n' +
210
+ 'tell application "Notes"\n' +
211
+ 'if not (exists account 1) then error "No Notes account is available."\n' +
212
+ 'set targetAccount to account 1\n' +
213
+ 'set targetFolder to missing value\n' +
214
+ 'if folderName is not "" then\n' +
215
+ 'repeat with f in folders of targetAccount\n' +
216
+ 'if name of f is folderName then set targetFolder to f\n' +
217
+ 'end repeat\n' +
218
+ 'if targetFolder is missing value then\n' +
219
+ 'set targetFolder to make new folder at targetAccount with properties {name:folderName}\n' +
220
+ 'end if\n' +
221
+ 'else\n' +
222
+ 'set targetFolder to folder "Notes" of targetAccount\n' +
223
+ 'end if\n' +
224
+ 'set newNote to make new note at targetFolder with properties {name:noteTitle, body:noteBody}\n' +
225
+ 'return "created"\n' +
226
+ 'end tell\n' +
227
+ 'end run';
228
+ const status = await runAppleScript(script, [title, formattedBody, folder], context.signal);
229
+ return { status, title, folder: folder || null, sectionCount: sections.length };
230
+ }
231
+
232
+ export const notesPlusTools: ToolDefinition[] = [
233
+ {
234
+ name: "notes_get_by_title",
235
+ description: "Find a note by exact title and optional folder.",
236
+ inputSchema: {
237
+ type: "object",
238
+ required: ["title"],
239
+ properties: {
240
+ title: { type: "string" },
241
+ folder: { type: "string" },
242
+ },
243
+ additionalProperties: false,
244
+ },
245
+ risk: "read",
246
+ execute: runNotesGetByTitle,
247
+ },
248
+ {
249
+ name: "notes_replace_section",
250
+ description: "Replace or append a heading-based section in a note.",
251
+ inputSchema: {
252
+ type: "object",
253
+ required: ["title", "heading", "content"],
254
+ properties: {
255
+ title: { type: "string" },
256
+ heading: { type: "string" },
257
+ content: { type: "string" },
258
+ folder: { type: "string" },
259
+ createIfMissing: { type: "boolean" },
260
+ },
261
+ additionalProperties: false,
262
+ },
263
+ risk: "write",
264
+ execute: runNotesReplaceSection,
265
+ },
266
+ {
267
+ name: "notes_create_structured",
268
+ description: "Create a structured note from headings, bullets, and paragraphs.",
269
+ inputSchema: {
270
+ type: "object",
271
+ required: ["title", "sections"],
272
+ properties: {
273
+ title: { type: "string" },
274
+ folder: { type: "string" },
275
+ sections: {
276
+ type: "array",
277
+ items: {
278
+ type: "object",
279
+ required: ["heading"],
280
+ properties: {
281
+ heading: { type: "string" },
282
+ bullets: { type: "array", items: { type: "string" } },
283
+ paragraphs: { type: "array", items: { type: "string" } },
284
+ },
285
+ additionalProperties: false,
286
+ },
287
+ },
288
+ },
289
+ additionalProperties: false,
290
+ },
291
+ risk: "write",
292
+ execute: runNotesCreateStructured,
293
+ },
294
+ ];
@@ -0,0 +1,175 @@
1
+ import { ensureMacOS } from "../safety";
2
+ import { runCommandSafe } from "../runtime";
3
+ import type { ToolDefinition, ToolExecutionContext } from "../types";
4
+
5
+ type NumbersSetInput = {
6
+ document?: string;
7
+ sheet?: string;
8
+ range?: string;
9
+ values?: string | number | boolean;
10
+ };
11
+
12
+ type NumbersReadInput = {
13
+ document?: string;
14
+ sheet?: string;
15
+ range?: string;
16
+ };
17
+
18
+ const APPLESCRIPT_TIMEOUT_MS = 30_000;
19
+
20
+ function asObject(input: unknown): Record<string, unknown> {
21
+ if (!input || typeof input !== "object") {
22
+ throw new Error("Tool input must be an object.");
23
+ }
24
+ return input as Record<string, unknown>;
25
+ }
26
+
27
+ async function runCommand(command: string, args: string[], signal?: AbortSignal): Promise<string> {
28
+ const { stdout } = await runCommandSafe({
29
+ command,
30
+ args,
31
+ signal,
32
+ timeoutMs: APPLESCRIPT_TIMEOUT_MS,
33
+ });
34
+ return stdout;
35
+ }
36
+
37
+ async function runNumbersSetCells(input: unknown, context: ToolExecutionContext) {
38
+ const payload = asObject(input) as NumbersSetInput;
39
+
40
+ ensureMacOS("Numbers automation");
41
+
42
+ const document = typeof payload.document === "string" ? payload.document.trim() : "";
43
+ const sheet = typeof payload.sheet === "string" && payload.sheet.trim() ? payload.sheet.trim() : "Sheet 1";
44
+ const range = typeof payload.range === "string" && payload.range.trim() ? payload.range.trim() : "A1";
45
+ const value = payload.values ?? "";
46
+
47
+ if (context.localTools.dryRun) {
48
+ return {
49
+ dryRun: true,
50
+ action: "numbers_set_cells",
51
+ document,
52
+ sheet,
53
+ range,
54
+ value,
55
+ };
56
+ }
57
+
58
+ const script =
59
+ 'on run argv\n' +
60
+ 'set docPath to item 1 of argv\n' +
61
+ 'set sheetName to item 2 of argv\n' +
62
+ 'set targetRange to item 3 of argv\n' +
63
+ 'set targetValue to item 4 of argv\n' +
64
+ 'tell application "Numbers"\n' +
65
+ 'activate\n' +
66
+ 'if docPath is "" then\n' +
67
+ 'if (count of documents) = 0 then\n' +
68
+ 'set targetDoc to make new document\n' +
69
+ 'else\n' +
70
+ 'set targetDoc to front document\n' +
71
+ 'end if\n' +
72
+ 'else\n' +
73
+ 'set targetDoc to open POSIX file docPath\n' +
74
+ 'end if\n' +
75
+ 'tell targetDoc\n' +
76
+ 'tell sheet sheetName\n' +
77
+ 'tell table 1\n' +
78
+ 'set value of range targetRange to targetValue\n' +
79
+ 'end tell\n' +
80
+ 'end tell\n' +
81
+ 'save\n' +
82
+ 'end tell\n' +
83
+ 'end tell\n' +
84
+ 'return "ok"\n' +
85
+ 'end run';
86
+
87
+ await runCommand("osascript", ["-e", script, document, sheet, range, String(value)], context.signal);
88
+
89
+ return {
90
+ updated: true,
91
+ document,
92
+ sheet,
93
+ range,
94
+ value,
95
+ };
96
+ }
97
+
98
+ async function runNumbersReadCells(input: unknown, context: ToolExecutionContext) {
99
+ const payload = asObject(input) as NumbersReadInput;
100
+
101
+ ensureMacOS("Numbers automation");
102
+
103
+ const document = typeof payload.document === "string" ? payload.document.trim() : "";
104
+ const sheet = typeof payload.sheet === "string" && payload.sheet.trim() ? payload.sheet.trim() : "Sheet 1";
105
+ const range = typeof payload.range === "string" && payload.range.trim() ? payload.range.trim() : "A1";
106
+
107
+ const script =
108
+ 'on run argv\n' +
109
+ 'set docPath to item 1 of argv\n' +
110
+ 'set sheetName to item 2 of argv\n' +
111
+ 'set targetRange to item 3 of argv\n' +
112
+ 'tell application "Numbers"\n' +
113
+ 'activate\n' +
114
+ 'if docPath is "" then\n' +
115
+ 'if (count of documents) = 0 then error "No Numbers documents are open."\n' +
116
+ 'set targetDoc to front document\n' +
117
+ 'else\n' +
118
+ 'set targetDoc to open POSIX file docPath\n' +
119
+ 'end if\n' +
120
+ 'tell targetDoc\n' +
121
+ 'tell sheet sheetName\n' +
122
+ 'tell table 1\n' +
123
+ 'set readValue to value of range targetRange\n' +
124
+ 'end tell\n' +
125
+ 'end tell\n' +
126
+ 'end tell\n' +
127
+ 'end tell\n' +
128
+ 'return readValue as text\n' +
129
+ 'end run';
130
+
131
+ const value = await runCommand("osascript", ["-e", script, document, sheet, range], context.signal);
132
+
133
+ return {
134
+ document,
135
+ sheet,
136
+ range,
137
+ value,
138
+ };
139
+ }
140
+
141
+ export const numbersTools: ToolDefinition[] = [
142
+ {
143
+ name: "numbers_set_cells",
144
+ description: "Set a value in Numbers sheet range.",
145
+ inputSchema: {
146
+ type: "object",
147
+ properties: {
148
+ document: { type: "string" },
149
+ sheet: { type: "string" },
150
+ range: { type: "string" },
151
+ values: {
152
+ oneOf: [{ type: "string" }, { type: "number" }, { type: "boolean" }],
153
+ },
154
+ },
155
+ additionalProperties: false,
156
+ },
157
+ risk: "write",
158
+ execute: runNumbersSetCells,
159
+ },
160
+ {
161
+ name: "numbers_read_cells",
162
+ description: "Read a value from Numbers sheet range.",
163
+ inputSchema: {
164
+ type: "object",
165
+ properties: {
166
+ document: { type: "string" },
167
+ sheet: { type: "string" },
168
+ range: { type: "string" },
169
+ },
170
+ additionalProperties: false,
171
+ },
172
+ risk: "read",
173
+ execute: runNumbersReadCells,
174
+ },
175
+ ];