@leg3ndy/otto-bridge 0.4.1 → 0.4.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/executors/native_macos.js +110 -1
- package/dist/types.js +1 -1
- package/package.json +1 -1
|
@@ -109,6 +109,47 @@ function clipText(value, maxLength) {
|
|
|
109
109
|
}
|
|
110
110
|
return `${value.slice(0, maxLength)}...`;
|
|
111
111
|
}
|
|
112
|
+
function escapeHtml(value) {
|
|
113
|
+
return value
|
|
114
|
+
.replace(/&/g, "&")
|
|
115
|
+
.replace(/</g, "<")
|
|
116
|
+
.replace(/>/g, ">");
|
|
117
|
+
}
|
|
118
|
+
function deriveNoteTitle(text, fallback = "Nota Otto") {
|
|
119
|
+
const firstLine = text
|
|
120
|
+
.split(/\r?\n/)
|
|
121
|
+
.map((line) => line.trim().replace(/^#+\s*/, ""))
|
|
122
|
+
.find(Boolean);
|
|
123
|
+
return firstLine ? clipText(firstLine, 80) : fallback;
|
|
124
|
+
}
|
|
125
|
+
function normalizeNoteHeading(value) {
|
|
126
|
+
return value.trim().replace(/^#+\s*/, "");
|
|
127
|
+
}
|
|
128
|
+
function stripDuplicatedTitleFromText(text, title) {
|
|
129
|
+
const lines = text.replace(/\r\n/g, "\n").split("\n");
|
|
130
|
+
if (!lines.length) {
|
|
131
|
+
return text;
|
|
132
|
+
}
|
|
133
|
+
const firstMeaningfulIndex = lines.findIndex((line) => line.trim().length > 0);
|
|
134
|
+
if (firstMeaningfulIndex < 0) {
|
|
135
|
+
return text;
|
|
136
|
+
}
|
|
137
|
+
const firstMeaningfulLine = normalizeNoteHeading(lines[firstMeaningfulIndex]);
|
|
138
|
+
if (firstMeaningfulLine !== normalizeNoteHeading(title)) {
|
|
139
|
+
return text;
|
|
140
|
+
}
|
|
141
|
+
const remaining = lines.slice(firstMeaningfulIndex + 1).join("\n").replace(/^\n+/, "").trim();
|
|
142
|
+
return remaining || text;
|
|
143
|
+
}
|
|
144
|
+
function noteBodyToHtml(text) {
|
|
145
|
+
const normalized = text.replace(/\r\n/g, "\n").trim();
|
|
146
|
+
const blocks = normalized
|
|
147
|
+
.split(/\n{2,}/)
|
|
148
|
+
.map((block) => block.trim())
|
|
149
|
+
.filter(Boolean)
|
|
150
|
+
.map((block) => `<p>${escapeHtml(block).replace(/\n/g, "<br>")}</p>`);
|
|
151
|
+
return blocks.join("");
|
|
152
|
+
}
|
|
112
153
|
function isSafeShellCommand(command) {
|
|
113
154
|
const trimmed = command.trim();
|
|
114
155
|
if (!trimmed) {
|
|
@@ -235,6 +276,18 @@ function parseStructuredActions(job) {
|
|
|
235
276
|
}
|
|
236
277
|
continue;
|
|
237
278
|
}
|
|
279
|
+
if (type === "create_note" || type === "write_note") {
|
|
280
|
+
const text = asString(action.text) || asString(action.body) || asString(action.content);
|
|
281
|
+
if (text) {
|
|
282
|
+
actions.push({
|
|
283
|
+
type: "create_note",
|
|
284
|
+
app: asString(action.app) || "Notes",
|
|
285
|
+
title: asString(action.title) || asString(action.name) || deriveNoteTitle(text),
|
|
286
|
+
text,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
238
291
|
if (type === "type_text" || type === "write_text" || type === "keystroke") {
|
|
239
292
|
const text = asString(action.text) || asString(action.content);
|
|
240
293
|
if (text) {
|
|
@@ -278,6 +331,32 @@ function parseStructuredActions(job) {
|
|
|
278
331
|
}
|
|
279
332
|
return actions;
|
|
280
333
|
}
|
|
334
|
+
function collapseLegacyNotePlan(actions) {
|
|
335
|
+
if (!actions.length) {
|
|
336
|
+
return actions;
|
|
337
|
+
}
|
|
338
|
+
const hasOnlyLegacyNoteActions = actions.every((action) => (action.type === "open_app"
|
|
339
|
+
|| action.type === "focus_app"
|
|
340
|
+
|| action.type === "press_shortcut"
|
|
341
|
+
|| action.type === "type_text"));
|
|
342
|
+
if (!hasOnlyLegacyNoteActions) {
|
|
343
|
+
return actions;
|
|
344
|
+
}
|
|
345
|
+
const opensNotes = actions.some((action) => ((action.type === "open_app" || action.type === "focus_app") && action.app === "Notes"));
|
|
346
|
+
const createsNewNote = actions.some((action) => action.type === "press_shortcut" && action.shortcut.toLowerCase() === "cmd+n");
|
|
347
|
+
const noteText = actions.find((action) => action.type === "type_text");
|
|
348
|
+
if (!opensNotes || !createsNewNote || !noteText) {
|
|
349
|
+
return actions;
|
|
350
|
+
}
|
|
351
|
+
return [
|
|
352
|
+
{
|
|
353
|
+
type: "create_note",
|
|
354
|
+
app: "Notes",
|
|
355
|
+
title: deriveNoteTitle(noteText.text),
|
|
356
|
+
text: noteText.text,
|
|
357
|
+
},
|
|
358
|
+
];
|
|
359
|
+
}
|
|
281
360
|
function deriveActionsFromText(job) {
|
|
282
361
|
const task = extractTaskText(job);
|
|
283
362
|
const detectedApp = detectKnownApp(task);
|
|
@@ -300,7 +379,7 @@ function deriveActionsFromText(job) {
|
|
|
300
379
|
function extractActions(job) {
|
|
301
380
|
const structured = parseStructuredActions(job);
|
|
302
381
|
if (structured.length > 0) {
|
|
303
|
-
return structured;
|
|
382
|
+
return collapseLegacyNotePlan(structured);
|
|
304
383
|
}
|
|
305
384
|
return deriveActionsFromText(job);
|
|
306
385
|
}
|
|
@@ -347,6 +426,12 @@ export class NativeMacOSJobExecutor {
|
|
|
347
426
|
await this.pressShortcut(action.shortcut);
|
|
348
427
|
continue;
|
|
349
428
|
}
|
|
429
|
+
if (action.type === "create_note") {
|
|
430
|
+
await reporter.progress(progressPercent, "Criando nota no Notes");
|
|
431
|
+
const noteTitle = await this.createNote(action.text, action.title);
|
|
432
|
+
completionNotes.push(`Nota criada no Notes: ${noteTitle}`);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
350
435
|
if (action.type === "type_text") {
|
|
351
436
|
await reporter.progress(progressPercent, "Digitando texto no app ativo");
|
|
352
437
|
await this.typeText(action.text);
|
|
@@ -418,6 +503,27 @@ export class NativeMacOSJobExecutor {
|
|
|
418
503
|
}
|
|
419
504
|
await this.runCommand("open", [url]);
|
|
420
505
|
}
|
|
506
|
+
async createNote(text, title) {
|
|
507
|
+
const noteTitle = clipText((title || deriveNoteTitle(text)).trim() || "Nota Otto", 120);
|
|
508
|
+
const noteBodyText = stripDuplicatedTitleFromText(text, noteTitle);
|
|
509
|
+
const noteHtml = noteBodyToHtml(noteBodyText);
|
|
510
|
+
const script = `
|
|
511
|
+
set noteTitle to "${escapeAppleScript(noteTitle)}"
|
|
512
|
+
set noteBody to "${escapeAppleScript(noteHtml)}"
|
|
513
|
+
tell application "Notes"
|
|
514
|
+
activate
|
|
515
|
+
if not (exists default account) then error "Nenhuma conta padrão do Notes foi encontrada."
|
|
516
|
+
tell default account
|
|
517
|
+
tell default folder
|
|
518
|
+
set newNote to make new note with properties {name:noteTitle, body:noteBody}
|
|
519
|
+
show newNote
|
|
520
|
+
end tell
|
|
521
|
+
end tell
|
|
522
|
+
end tell
|
|
523
|
+
`;
|
|
524
|
+
await this.runCommand("osascript", ["-e", script]);
|
|
525
|
+
return noteTitle;
|
|
526
|
+
}
|
|
421
527
|
async focusApp(app) {
|
|
422
528
|
await this.runCommand("osascript", ["-e", `tell application "${escapeAppleScript(app)}" to activate`]);
|
|
423
529
|
}
|
|
@@ -527,6 +633,9 @@ export class NativeMacOSJobExecutor {
|
|
|
527
633
|
if (action.type === "press_shortcut") {
|
|
528
634
|
return `Atalho ${action.shortcut} executado no macOS`;
|
|
529
635
|
}
|
|
636
|
+
if (action.type === "create_note") {
|
|
637
|
+
return `Nota criada no Notes: ${action.title || deriveNoteTitle(action.text)}`;
|
|
638
|
+
}
|
|
530
639
|
if (action.type === "type_text") {
|
|
531
640
|
return "Texto digitado no aplicativo ativo";
|
|
532
641
|
}
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const BRIDGE_CONFIG_VERSION = 1;
|
|
2
|
-
export const BRIDGE_VERSION = "0.4.
|
|
2
|
+
export const BRIDGE_VERSION = "0.4.2";
|
|
3
3
|
export const BRIDGE_PACKAGE_NAME = "@leg3ndy/otto-bridge";
|
|
4
4
|
export const DEFAULT_API_BASE_URL = "http://localhost:8000";
|
|
5
5
|
export const DEFAULT_POLL_INTERVAL_MS = 3000;
|