@agentproto/apps 0.4.0 → 0.5.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/README.md +15 -0
- package/dist/bin/sync.d.ts +23 -0
- package/dist/bin/sync.d.ts.map +1 -0
- package/dist/bin/sync.mjs +944 -0
- package/dist/bin/sync.mjs.map +1 -0
- package/dist/code-team/index.d.ts.map +1 -1
- package/dist/code-team.mjs +4 -0
- package/dist/code-team.mjs.map +1 -1
- package/dist/content-team/index.d.ts.map +1 -1
- package/dist/content-team.mjs +4 -0
- package/dist/content-team.mjs.map +1 -1
- package/dist/index.mjs +607 -2
- package/dist/index.mjs.map +1 -1
- package/dist/mail-triage/index.d.ts.map +1 -1
- package/dist/mail-triage/ui.d.ts +15 -0
- package/dist/mail-triage/ui.d.ts.map +1 -0
- package/dist/mail-triage.mjs +348 -1
- package/dist/mail-triage.mjs.map +1 -1
- package/dist/media-viewer/index.d.ts.map +1 -1
- package/dist/media-viewer/ui.d.ts +14 -0
- package/dist/media-viewer/ui.d.ts.map +1 -0
- package/dist/media-viewer.mjs +251 -1
- package/dist/media-viewer.mjs.map +1 -1
- package/package.json +7 -4
package/dist/index.mjs
CHANGED
|
@@ -76,6 +76,10 @@ var deliverChange = defineWorkflow({
|
|
|
76
76
|
|
|
77
77
|
// src/code-team/index.ts
|
|
78
78
|
var codeTeam = defineApp({
|
|
79
|
+
id: "@agentproto/code-team",
|
|
80
|
+
name: "Code Team",
|
|
81
|
+
version: "0.1.0",
|
|
82
|
+
description: "Implement, review, and fix code changes.",
|
|
79
83
|
agents: [implementer, reviewer, fixer],
|
|
80
84
|
workflows: [deliverChange]
|
|
81
85
|
});
|
|
@@ -160,6 +164,10 @@ var produceCover = defineWorkflow({
|
|
|
160
164
|
|
|
161
165
|
// src/content-team/index.ts
|
|
162
166
|
var contentTeam = defineApp({
|
|
167
|
+
id: "@agentproto/content-team",
|
|
168
|
+
name: "Content Team",
|
|
169
|
+
version: "0.1.0",
|
|
170
|
+
description: "Research, draft, edit, and illustrate content.",
|
|
163
171
|
agents: [researcher, writer, editor, illustrator],
|
|
164
172
|
workflows: [produceContent, produceCover]
|
|
165
173
|
});
|
|
@@ -205,10 +213,357 @@ var triageInbox = defineWorkflow({
|
|
|
205
213
|
]
|
|
206
214
|
});
|
|
207
215
|
|
|
216
|
+
// src/mail-triage/ui.ts
|
|
217
|
+
var MAIL_TRIAGE_TOOLS = [
|
|
218
|
+
"imported:agentpush/mailbox_list",
|
|
219
|
+
"imported:agentpush/mailbox_search",
|
|
220
|
+
"imported:agentpush/mailbox_triage_plan",
|
|
221
|
+
"imported:agentpush/mailbox_triage_apply",
|
|
222
|
+
"app_run",
|
|
223
|
+
"app_status",
|
|
224
|
+
"agent_output"
|
|
225
|
+
];
|
|
226
|
+
var APP_ID = "@agentproto/mail-triage";
|
|
227
|
+
var AGENT_ID = "@agentproto/triager";
|
|
228
|
+
var CATEGORIES = ["urgent", "needs-reply", "newsletter", "notification"];
|
|
229
|
+
var MAIL_TRIAGE_HTML = `<!DOCTYPE html>
|
|
230
|
+
<html lang="en">
|
|
231
|
+
<head>
|
|
232
|
+
<meta charset="UTF-8">
|
|
233
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
234
|
+
<title>Mail Triage</title>
|
|
235
|
+
<style>
|
|
236
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
237
|
+
:root{
|
|
238
|
+
--bg:#0d1117;--bg2:#161b22;--bg3:#21262d;--border:#30363d;
|
|
239
|
+
--text:#e6edf3;--text2:#8b949e;
|
|
240
|
+
--green:#3fb950;--yellow:#d29922;--red:#f85149;--blue:#58a6ff;--purple:#bc8cff;
|
|
241
|
+
}
|
|
242
|
+
html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-size:13px;background:var(--bg);color:var(--text)}
|
|
243
|
+
#app{display:flex;flex-direction:column;height:100%;overflow-y:auto}
|
|
244
|
+
#toolbar{padding:10px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;background:var(--bg2);flex-shrink:0}
|
|
245
|
+
#toolbar .title{font-size:13px;font-weight:600;flex:1}
|
|
246
|
+
select,input[type=text]{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:5px 8px;border-radius:4px;font-size:12px;font-family:inherit}
|
|
247
|
+
.abtn{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:5px 10px;border-radius:4px;cursor:pointer;font-size:12px;font-family:inherit}
|
|
248
|
+
.abtn:hover{background:var(--border)}
|
|
249
|
+
.abtn:disabled{opacity:.4;cursor:default}
|
|
250
|
+
.abtn.primary{background:var(--blue);border-color:var(--blue);color:#04101c}
|
|
251
|
+
.abtn.primary:hover{opacity:.9}
|
|
252
|
+
#content{padding:14px;display:flex;flex-direction:column;gap:16px}
|
|
253
|
+
section{background:var(--bg2);border:1px solid var(--border);border-radius:6px;padding:12px}
|
|
254
|
+
section h3{font-size:11px;color:var(--text2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:10px}
|
|
255
|
+
#summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px}
|
|
256
|
+
.tile{background:var(--bg3);border:1px solid var(--border);border-radius:6px;padding:10px;text-align:center}
|
|
257
|
+
.tile .count{font-size:22px;font-weight:700}
|
|
258
|
+
.tile .label{font-size:11px;color:var(--text2);text-transform:capitalize}
|
|
259
|
+
.tile.urgent .count{color:var(--red)}
|
|
260
|
+
.tile.needs-reply .count{color:var(--yellow)}
|
|
261
|
+
.tile.newsletter .count{color:var(--blue)}
|
|
262
|
+
.tile.notification .count{color:var(--purple)}
|
|
263
|
+
#plan-list,#search-results,#log{max-height:260px;overflow-y:auto;font-size:12px}
|
|
264
|
+
.plan-item{display:flex;justify-content:space-between;gap:10px;padding:6px 0;border-bottom:1px solid var(--border)}
|
|
265
|
+
.plan-item .subj{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
266
|
+
.plan-item .tag{color:var(--text2);font-size:11px;white-space:nowrap}
|
|
267
|
+
#log{font-family:Menlo,Monaco,monospace;white-space:pre-wrap;word-break:break-word;background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:8px}
|
|
268
|
+
.searchbar{display:flex;gap:8px;margin-bottom:8px}
|
|
269
|
+
.searchbar input{flex:1}
|
|
270
|
+
.actions{display:flex;gap:8px;margin-top:10px}
|
|
271
|
+
#status{padding:6px 14px;font-size:11px;color:var(--text2);border-top:1px solid var(--border);background:var(--bg2);flex-shrink:0}
|
|
272
|
+
.empty{color:var(--text2);padding:8px 0}
|
|
273
|
+
</style>
|
|
274
|
+
</head>
|
|
275
|
+
<body>
|
|
276
|
+
<div id="app">
|
|
277
|
+
<div id="toolbar">
|
|
278
|
+
<span class="title">Mail Triage</span>
|
|
279
|
+
<select id="mailbox-select"></select>
|
|
280
|
+
<button class="abtn" id="refresh-btn">Refresh</button>
|
|
281
|
+
</div>
|
|
282
|
+
<div id="content">
|
|
283
|
+
<section>
|
|
284
|
+
<h3>Inbox summary</h3>
|
|
285
|
+
<div id="summary"></div>
|
|
286
|
+
</section>
|
|
287
|
+
|
|
288
|
+
<section>
|
|
289
|
+
<h3>Search</h3>
|
|
290
|
+
<div class="searchbar">
|
|
291
|
+
<input type="text" id="search-input" placeholder="e.g. is:unread">
|
|
292
|
+
<button class="abtn" id="search-btn">Search</button>
|
|
293
|
+
</div>
|
|
294
|
+
<div id="search-results"><div class="empty">No search run yet.</div></div>
|
|
295
|
+
</section>
|
|
296
|
+
|
|
297
|
+
<section>
|
|
298
|
+
<h3>Triage plan</h3>
|
|
299
|
+
<div class="actions">
|
|
300
|
+
<button class="abtn primary" id="triage-btn">Triage</button>
|
|
301
|
+
<button class="abtn" id="apply-btn" disabled>Apply</button>
|
|
302
|
+
</div>
|
|
303
|
+
<div id="plan-list" style="margin-top:10px"><div class="empty">No plan yet \u2014 click Triage.</div></div>
|
|
304
|
+
</section>
|
|
305
|
+
|
|
306
|
+
<section>
|
|
307
|
+
<h3>Triager agent</h3>
|
|
308
|
+
<div class="actions">
|
|
309
|
+
<button class="abtn" id="run-agent-btn">Run Triager Agent</button>
|
|
310
|
+
</div>
|
|
311
|
+
<div id="log" style="margin-top:10px"><div class="empty">Agent has not run yet.</div></div>
|
|
312
|
+
</section>
|
|
313
|
+
</div>
|
|
314
|
+
<div id="status">Connecting…</div>
|
|
315
|
+
</div>
|
|
316
|
+
<script>
|
|
317
|
+
var APP_ID = ${JSON.stringify(APP_ID)};
|
|
318
|
+
var AGENT_ID = ${JSON.stringify(AGENT_ID)};
|
|
319
|
+
var CATEGORIES = ${JSON.stringify(CATEGORIES)};
|
|
320
|
+
var callTool = null;
|
|
321
|
+
var currentPlan = null;
|
|
322
|
+
var pollHandle = null;
|
|
323
|
+
|
|
324
|
+
function setStatus(msg) {
|
|
325
|
+
document.getElementById("status").textContent = msg;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// See media-viewer/ui.ts for why this loop exists: app_tool_call wraps its
|
|
329
|
+
// dispatch result in at least one more {content:[{type:"text",text:...}]}
|
|
330
|
+
// shell than the underlying tool returned.
|
|
331
|
+
function unwrapText(result) {
|
|
332
|
+
var cur = result;
|
|
333
|
+
for (var i = 0; i < 4; i++) {
|
|
334
|
+
if (typeof cur === "string") {
|
|
335
|
+
try {
|
|
336
|
+
cur = JSON.parse(cur);
|
|
337
|
+
continue;
|
|
338
|
+
} catch (e) {
|
|
339
|
+
return cur;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (cur && Array.isArray(cur.content) && cur.content[0] && typeof cur.content[0].text === "string") {
|
|
343
|
+
cur = cur.content[0].text;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
return cur;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function callApp(tool, args) {
|
|
352
|
+
return callTool("app_tool_call", { appId: APP_ID, tool: tool, args: args || {} }).then(unwrapText);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function asList(value, keys) {
|
|
356
|
+
if (Array.isArray(value)) return value;
|
|
357
|
+
if (value && typeof value === "object") {
|
|
358
|
+
for (var i = 0; i < keys.length; i++) {
|
|
359
|
+
if (Array.isArray(value[keys[i]])) return value[keys[i]];
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function renderSummary(counts) {
|
|
366
|
+
var el = document.getElementById("summary");
|
|
367
|
+
el.innerHTML = "";
|
|
368
|
+
CATEGORIES.forEach(function (cat) {
|
|
369
|
+
var tile = document.createElement("div");
|
|
370
|
+
tile.className = "tile " + cat;
|
|
371
|
+
var count = counts && counts[cat] !== undefined ? counts[cat] : "\u2013";
|
|
372
|
+
tile.innerHTML = '<div class="count"></div><div class="label"></div>';
|
|
373
|
+
tile.querySelector(".count").textContent = String(count);
|
|
374
|
+
tile.querySelector(".label").textContent = cat.replace("-", " ");
|
|
375
|
+
el.appendChild(tile);
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function loadMailboxes() {
|
|
380
|
+
setStatus("Loading mailboxes\\u2026");
|
|
381
|
+
return callApp("imported:agentpush/mailbox_list", {})
|
|
382
|
+
.then(function (result) {
|
|
383
|
+
var mailboxes = asList(result, ["mailboxes", "items"]);
|
|
384
|
+
var select = document.getElementById("mailbox-select");
|
|
385
|
+
select.innerHTML = "";
|
|
386
|
+
if (mailboxes.length === 0) {
|
|
387
|
+
var opt = document.createElement("option");
|
|
388
|
+
opt.textContent = "inbox";
|
|
389
|
+
opt.value = "inbox";
|
|
390
|
+
select.appendChild(opt);
|
|
391
|
+
} else {
|
|
392
|
+
mailboxes.forEach(function (mb) {
|
|
393
|
+
var name = typeof mb === "string" ? mb : mb.name || mb.id || JSON.stringify(mb);
|
|
394
|
+
var opt = document.createElement("option");
|
|
395
|
+
opt.value = name;
|
|
396
|
+
opt.textContent = name;
|
|
397
|
+
select.appendChild(opt);
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
setStatus("Ready");
|
|
401
|
+
})
|
|
402
|
+
.catch(function (err) {
|
|
403
|
+
setStatus("Error loading mailboxes: " + err.message);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function renderSearchResults(items) {
|
|
408
|
+
var el = document.getElementById("search-results");
|
|
409
|
+
el.innerHTML = "";
|
|
410
|
+
if (items.length === 0) {
|
|
411
|
+
el.innerHTML = '<div class="empty">No results.</div>';
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
items.forEach(function (item) {
|
|
415
|
+
var row = document.createElement("div");
|
|
416
|
+
row.className = "plan-item";
|
|
417
|
+
var subj = typeof item === "string" ? item : item.subject || item.title || JSON.stringify(item);
|
|
418
|
+
var from = item && item.from ? item.from : "";
|
|
419
|
+
row.innerHTML = '<span class="subj"></span><span class="tag"></span>';
|
|
420
|
+
row.querySelector(".subj").textContent = subj;
|
|
421
|
+
row.querySelector(".tag").textContent = from;
|
|
422
|
+
el.appendChild(row);
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function renderPlan(plan) {
|
|
427
|
+
var items = asList(plan, ["actions", "items", "plan"]);
|
|
428
|
+
var el = document.getElementById("plan-list");
|
|
429
|
+
el.innerHTML = "";
|
|
430
|
+
if (items.length === 0) {
|
|
431
|
+
el.innerHTML = '<div class="empty">Plan is empty.</div>';
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
var counts = {};
|
|
435
|
+
items.forEach(function (item) {
|
|
436
|
+
var cat = (item && (item.category || item.tag)) || "notification";
|
|
437
|
+
counts[cat] = (counts[cat] || 0) + 1;
|
|
438
|
+
var row = document.createElement("div");
|
|
439
|
+
row.className = "plan-item";
|
|
440
|
+
var subj = (item && (item.subject || item.title)) || JSON.stringify(item);
|
|
441
|
+
var action = (item && (item.action || item.category)) || "";
|
|
442
|
+
row.innerHTML = '<span class="subj"></span><span class="tag"></span>';
|
|
443
|
+
row.querySelector(".subj").textContent = subj;
|
|
444
|
+
row.querySelector(".tag").textContent = action;
|
|
445
|
+
el.appendChild(row);
|
|
446
|
+
});
|
|
447
|
+
renderSummary(counts);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
document.getElementById("refresh-btn").addEventListener("click", loadMailboxes);
|
|
451
|
+
|
|
452
|
+
document.getElementById("search-btn").addEventListener("click", function () {
|
|
453
|
+
var query = document.getElementById("search-input").value;
|
|
454
|
+
var mailbox = document.getElementById("mailbox-select").value;
|
|
455
|
+
setStatus("Searching\\u2026");
|
|
456
|
+
callApp("imported:agentpush/mailbox_search", { mailbox: mailbox, query: query })
|
|
457
|
+
.then(function (result) {
|
|
458
|
+
renderSearchResults(asList(result, ["messages", "items", "results"]));
|
|
459
|
+
setStatus("Ready");
|
|
460
|
+
})
|
|
461
|
+
.catch(function (err) {
|
|
462
|
+
setStatus("Search failed: " + err.message);
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
document.getElementById("triage-btn").addEventListener("click", function () {
|
|
467
|
+
var mailbox = document.getElementById("mailbox-select").value;
|
|
468
|
+
setStatus("Building triage plan\\u2026");
|
|
469
|
+
document.getElementById("apply-btn").disabled = true;
|
|
470
|
+
callApp("imported:agentpush/mailbox_triage_plan", { mailbox: mailbox })
|
|
471
|
+
.then(function (plan) {
|
|
472
|
+
currentPlan = plan;
|
|
473
|
+
renderPlan(plan);
|
|
474
|
+
document.getElementById("apply-btn").disabled = false;
|
|
475
|
+
setStatus("Plan ready \\u2014 review, then Apply.");
|
|
476
|
+
})
|
|
477
|
+
.catch(function (err) {
|
|
478
|
+
setStatus("Triage plan failed: " + err.message);
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
document.getElementById("apply-btn").addEventListener("click", function () {
|
|
483
|
+
if (!currentPlan) return;
|
|
484
|
+
setStatus("Applying triage plan\\u2026");
|
|
485
|
+
callApp("imported:agentpush/mailbox_triage_apply", { plan: currentPlan })
|
|
486
|
+
.then(function () {
|
|
487
|
+
setStatus("Plan applied.");
|
|
488
|
+
document.getElementById("apply-btn").disabled = true;
|
|
489
|
+
})
|
|
490
|
+
.catch(function (err) {
|
|
491
|
+
setStatus("Apply failed: " + err.message);
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
function appendLog(text) {
|
|
496
|
+
var el = document.getElementById("log");
|
|
497
|
+
if (el.querySelector(".empty")) el.innerHTML = "";
|
|
498
|
+
el.textContent += (el.textContent ? "\\n" : "") + text;
|
|
499
|
+
el.scrollTop = el.scrollHeight;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function pollAgentRun(appRunId) {
|
|
503
|
+
if (pollHandle) clearTimeout(pollHandle);
|
|
504
|
+
callApp("app_status", { appRunId: appRunId })
|
|
505
|
+
.then(function (status) {
|
|
506
|
+
var sessions = (status && status.sessions) || [];
|
|
507
|
+
var sessionId = sessions[0] && sessions[0].sessionId;
|
|
508
|
+
var tail = sessionId
|
|
509
|
+
? callApp("agent_output", { sessionId: sessionId, lastN: 200, clean: true })
|
|
510
|
+
: Promise.resolve("");
|
|
511
|
+
return tail.then(function (output) {
|
|
512
|
+
if (output) appendLog(typeof output === "string" ? output : JSON.stringify(output));
|
|
513
|
+
var done = status && (status.status === "ended" || status.status === "done" || status.endedAt);
|
|
514
|
+
if (!done) {
|
|
515
|
+
pollHandle = setTimeout(function () { pollAgentRun(appRunId); }, 2000);
|
|
516
|
+
} else {
|
|
517
|
+
setStatus("Agent run finished.");
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
})
|
|
521
|
+
.catch(function (err) {
|
|
522
|
+
setStatus("Agent status failed: " + err.message);
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
document.getElementById("run-agent-btn").addEventListener("click", function () {
|
|
527
|
+
document.getElementById("log").innerHTML = "";
|
|
528
|
+
setStatus("Starting triager agent\\u2026");
|
|
529
|
+
callApp("app_run", { appId: APP_ID, agents: [AGENT_ID], prompt: "Triage my inbox." })
|
|
530
|
+
.then(function (run) {
|
|
531
|
+
appendLog("Started app run " + (run && run.appRunId));
|
|
532
|
+
pollAgentRun(run.appRunId);
|
|
533
|
+
})
|
|
534
|
+
.catch(function (err) {
|
|
535
|
+
setStatus("Run failed: " + err.message);
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
window.McpApp.connect()
|
|
540
|
+
.then(function (bridge) {
|
|
541
|
+
callTool = bridge.callTool;
|
|
542
|
+
return loadMailboxes();
|
|
543
|
+
})
|
|
544
|
+
.catch(function (err) {
|
|
545
|
+
setStatus("Standalone mode \\u2014 no host bridge (" + err.message + ")");
|
|
546
|
+
renderSummary(null);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
renderSummary(null);
|
|
550
|
+
</script>
|
|
551
|
+
</body>
|
|
552
|
+
</html>`;
|
|
553
|
+
|
|
208
554
|
// src/mail-triage/index.ts
|
|
209
555
|
var mailTriage = defineApp({
|
|
556
|
+
id: "@agentproto/mail-triage",
|
|
557
|
+
name: "Mail Triage",
|
|
558
|
+
version: "0.1.0",
|
|
559
|
+
description: "Triage your inbox \u2014 categorize, label, archive.",
|
|
210
560
|
agents: [triager],
|
|
211
|
-
workflows: [triageInbox]
|
|
561
|
+
workflows: [triageInbox],
|
|
562
|
+
ui: {
|
|
563
|
+
html: MAIL_TRIAGE_HTML,
|
|
564
|
+
title: "Mail Triage",
|
|
565
|
+
tools: [...MAIL_TRIAGE_TOOLS]
|
|
566
|
+
}
|
|
212
567
|
});
|
|
213
568
|
var cataloger = {
|
|
214
569
|
agent: defineAgent({
|
|
@@ -274,10 +629,260 @@ var scanMedia = defineWorkflow({
|
|
|
274
629
|
]
|
|
275
630
|
});
|
|
276
631
|
|
|
632
|
+
// src/media-viewer/ui.ts
|
|
633
|
+
var MEDIA_VIEWER_TOOLS = ["directory_list", "file_info", "file_read"];
|
|
634
|
+
var APP_ID2 = "@agentproto/media-viewer";
|
|
635
|
+
var MEDIA_VIEWER_HTML = `<!DOCTYPE html>
|
|
636
|
+
<html lang="en">
|
|
637
|
+
<head>
|
|
638
|
+
<meta charset="UTF-8">
|
|
639
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
640
|
+
<title>Media Viewer</title>
|
|
641
|
+
<style>
|
|
642
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
643
|
+
:root{
|
|
644
|
+
--bg:#0d1117;--bg2:#161b22;--bg3:#21262d;--border:#30363d;
|
|
645
|
+
--text:#e6edf3;--text2:#8b949e;
|
|
646
|
+
--green:#3fb950;--yellow:#d29922;--red:#f85149;--blue:#58a6ff;--purple:#bc8cff;
|
|
647
|
+
}
|
|
648
|
+
html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-size:13px;background:var(--bg);color:var(--text)}
|
|
649
|
+
#app{display:flex;flex-direction:column;height:100%}
|
|
650
|
+
#toolbar{padding:10px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;background:var(--bg2);flex-shrink:0}
|
|
651
|
+
#toolbar .title{font-size:13px;font-weight:600;flex:1}
|
|
652
|
+
#breadcrumb{font-size:12px;color:var(--text2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
653
|
+
.abtn{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:5px 10px;border-radius:4px;cursor:pointer;font-size:12px;font-family:inherit}
|
|
654
|
+
.abtn:hover{background:var(--border)}
|
|
655
|
+
#body{flex:1;display:flex;overflow:hidden}
|
|
656
|
+
#grid-wrap{flex:1;overflow-y:auto;padding:14px}
|
|
657
|
+
#grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px}
|
|
658
|
+
.cell{display:flex;flex-direction:column;align-items:center;gap:6px;padding:10px 6px;border:1px solid var(--border);border-radius:6px;background:var(--bg2);cursor:pointer;text-align:center}
|
|
659
|
+
.cell:hover{background:var(--bg3);border-color:var(--blue)}
|
|
660
|
+
.cell.selected{border-color:var(--blue);background:var(--bg3)}
|
|
661
|
+
.icon{font-size:28px;line-height:1}
|
|
662
|
+
.name{font-size:11px;color:var(--text);word-break:break-word;max-width:100%;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
|
|
663
|
+
#empty{color:var(--text2);padding:24px;text-align:center}
|
|
664
|
+
#details{width:280px;border-left:1px solid var(--border);background:var(--bg2);padding:14px;overflow-y:auto;flex-shrink:0}
|
|
665
|
+
#details h3{font-size:12px;color:var(--text2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:8px}
|
|
666
|
+
#details .row{display:flex;justify-content:space-between;gap:8px;padding:4px 0;border-bottom:1px solid var(--border);font-size:12px}
|
|
667
|
+
#details .row span:first-child{color:var(--text2)}
|
|
668
|
+
#preview{margin-top:10px;padding:8px;background:var(--bg);border:1px solid var(--border);border-radius:4px;font-family:Menlo,Monaco,monospace;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:240px;overflow-y:auto}
|
|
669
|
+
#status{padding:6px 14px;font-size:11px;color:var(--text2);border-top:1px solid var(--border);background:var(--bg2);flex-shrink:0}
|
|
670
|
+
</style>
|
|
671
|
+
</head>
|
|
672
|
+
<body>
|
|
673
|
+
<div id="app">
|
|
674
|
+
<div id="toolbar">
|
|
675
|
+
<button class="abtn" id="up-btn" title="Up one level">↑</button>
|
|
676
|
+
<span class="title">Media Viewer</span>
|
|
677
|
+
<span id="breadcrumb">.</span>
|
|
678
|
+
<button class="abtn" id="refresh-btn">Refresh</button>
|
|
679
|
+
</div>
|
|
680
|
+
<div id="body">
|
|
681
|
+
<div id="grid-wrap">
|
|
682
|
+
<div id="grid"></div>
|
|
683
|
+
<div id="empty" style="display:none">No files in this folder.</div>
|
|
684
|
+
</div>
|
|
685
|
+
<div id="details" style="display:none"></div>
|
|
686
|
+
</div>
|
|
687
|
+
<div id="status">Connecting…</div>
|
|
688
|
+
</div>
|
|
689
|
+
<script>
|
|
690
|
+
var APP_ID = ${JSON.stringify(APP_ID2)};
|
|
691
|
+
var currentPath = ".";
|
|
692
|
+
var entries = [];
|
|
693
|
+
var callTool = null;
|
|
694
|
+
|
|
695
|
+
function setStatus(msg) {
|
|
696
|
+
document.getElementById("status").textContent = msg;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// app_tool_call wraps its dispatch result at least once (its own
|
|
700
|
+
// text-result body is the JSON-stringified inner MCP tool result); peel
|
|
701
|
+
// back through nested {content:[{type:"text",text:...}]} shells until the
|
|
702
|
+
// payload stops looking like one, so this works whichever layer the host's
|
|
703
|
+
// own bridge already unwrapped.
|
|
704
|
+
function unwrapText(result) {
|
|
705
|
+
var cur = result;
|
|
706
|
+
for (var i = 0; i < 4; i++) {
|
|
707
|
+
if (typeof cur === "string") {
|
|
708
|
+
try {
|
|
709
|
+
cur = JSON.parse(cur);
|
|
710
|
+
continue;
|
|
711
|
+
} catch (e) {
|
|
712
|
+
return cur;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (cur && Array.isArray(cur.content) && cur.content[0] && typeof cur.content[0].text === "string") {
|
|
716
|
+
cur = cur.content[0].text;
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
return cur;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function callApp(tool, args) {
|
|
725
|
+
return callTool("app_tool_call", { appId: APP_ID, tool: tool, args: args || {} }).then(unwrapText);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function parseDirectoryListing(raw) {
|
|
729
|
+
var text = typeof raw === "string" ? raw : "";
|
|
730
|
+
return text
|
|
731
|
+
.split("\\n")
|
|
732
|
+
.map(function (line) { return line.trim(); })
|
|
733
|
+
.filter(function (line) { return line.length > 0; })
|
|
734
|
+
.map(function (line) {
|
|
735
|
+
var isDir = line.indexOf("[DIR]") === 0;
|
|
736
|
+
var name = line.replace(/^\\[(FILE|DIR)\\]\\s*/, "");
|
|
737
|
+
return { name: name, isDir: isDir };
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function iconFor(entry) {
|
|
742
|
+
if (entry.isDir) return "\\uD83D\\uDCC1";
|
|
743
|
+
var ext = (entry.name.split(".").pop() || "").toLowerCase();
|
|
744
|
+
if (["jpg", "jpeg", "png", "gif", "webp", "svg", "bmp", "ico", "avif"].indexOf(ext) !== -1) return "\\uD83D\\uDDBC\\uFE0F";
|
|
745
|
+
if (["mp4", "webm", "mov", "avi", "mkv"].indexOf(ext) !== -1) return "\\uD83C\\uDFA5";
|
|
746
|
+
if (["mp3", "wav", "ogg", "flac", "aac", "m4a"].indexOf(ext) !== -1) return "\\uD83C\\uDFB5";
|
|
747
|
+
return "\\uD83D\\uDCC4";
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function joinPath(base, name) {
|
|
751
|
+
if (base === "." || base === "") return name;
|
|
752
|
+
return base.replace(/\\/+$/, "") + "/" + name;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function parentPath(path) {
|
|
756
|
+
if (path === "." || path.indexOf("/") === -1) return ".";
|
|
757
|
+
return path.slice(0, path.lastIndexOf("/")) || ".";
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function renderGrid() {
|
|
761
|
+
document.getElementById("breadcrumb").textContent = currentPath;
|
|
762
|
+
var grid = document.getElementById("grid");
|
|
763
|
+
var empty = document.getElementById("empty");
|
|
764
|
+
grid.innerHTML = "";
|
|
765
|
+
if (entries.length === 0) {
|
|
766
|
+
empty.style.display = "block";
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
empty.style.display = "none";
|
|
770
|
+
entries.forEach(function (entry, idx) {
|
|
771
|
+
var cell = document.createElement("div");
|
|
772
|
+
cell.className = "cell";
|
|
773
|
+
cell.dataset.idx = String(idx);
|
|
774
|
+
cell.innerHTML = '<div class="icon">' + iconFor(entry) + '</div><div class="name"></div>';
|
|
775
|
+
cell.querySelector(".name").textContent = entry.name;
|
|
776
|
+
cell.addEventListener("click", function () { onSelect(entry, cell); });
|
|
777
|
+
grid.appendChild(cell);
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function onSelect(entry, cell) {
|
|
782
|
+
Array.prototype.forEach.call(document.querySelectorAll(".cell"), function (c) {
|
|
783
|
+
c.classList.remove("selected");
|
|
784
|
+
});
|
|
785
|
+
cell.classList.add("selected");
|
|
786
|
+
if (entry.isDir) {
|
|
787
|
+
loadDirectory(joinPath(currentPath, entry.name));
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
showFileDetails(joinPath(currentPath, entry.name));
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function showFileDetails(path) {
|
|
794
|
+
var details = document.getElementById("details");
|
|
795
|
+
details.style.display = "block";
|
|
796
|
+
details.innerHTML = "<h3>Loading…</h3>";
|
|
797
|
+
setStatus("Reading " + path + "\\u2026");
|
|
798
|
+
callApp("file_info", { path: path })
|
|
799
|
+
.then(function (info) {
|
|
800
|
+
var rows = "";
|
|
801
|
+
if (info && typeof info === "object") {
|
|
802
|
+
["name", "type", "size", "modified"].forEach(function (key) {
|
|
803
|
+
if (info[key] !== undefined) {
|
|
804
|
+
rows += '<div class="row"><span>' + key + '</span><span></span></div>';
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
details.innerHTML = "<h3>Details</h3>" + rows + '<div id="preview">Loading preview…</div>';
|
|
809
|
+
var rowEls = details.querySelectorAll(".row span:last-child");
|
|
810
|
+
var keys = ["name", "type", "size", "modified"].filter(function (key) {
|
|
811
|
+
return info && info[key] !== undefined;
|
|
812
|
+
});
|
|
813
|
+
keys.forEach(function (key, i) {
|
|
814
|
+
rowEls[i].textContent = String(info[key]);
|
|
815
|
+
});
|
|
816
|
+
return callApp("file_read", { path: path });
|
|
817
|
+
})
|
|
818
|
+
.then(function (content) {
|
|
819
|
+
var pre = document.getElementById("preview");
|
|
820
|
+
if (!pre) return;
|
|
821
|
+
var text = typeof content === "string" ? content : JSON.stringify(content);
|
|
822
|
+
pre.textContent = text.length > 2000 ? text.slice(0, 2000) + "\\u2026 (truncated)" : (text || "(empty file)");
|
|
823
|
+
setStatus("Ready \\u00b7 " + currentPath);
|
|
824
|
+
})
|
|
825
|
+
.catch(function (err) {
|
|
826
|
+
var pre = document.getElementById("preview");
|
|
827
|
+
if (pre) pre.textContent = "Preview unavailable (" + err.message + ")";
|
|
828
|
+
setStatus("Ready \\u00b7 " + currentPath);
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
function loadDirectory(path) {
|
|
833
|
+
document.getElementById("details").style.display = "none";
|
|
834
|
+
setStatus("Loading " + path + "\\u2026");
|
|
835
|
+
return callApp("directory_list", { path: path })
|
|
836
|
+
.then(function (raw) {
|
|
837
|
+
currentPath = path;
|
|
838
|
+
entries = parseDirectoryListing(raw).sort(function (a, b) {
|
|
839
|
+
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
840
|
+
return a.name.localeCompare(b.name);
|
|
841
|
+
});
|
|
842
|
+
renderGrid();
|
|
843
|
+
setStatus("Ready \\u00b7 " + entries.length + " entries \\u00b7 " + path);
|
|
844
|
+
})
|
|
845
|
+
.catch(function (err) {
|
|
846
|
+
entries = [];
|
|
847
|
+
renderGrid();
|
|
848
|
+
setStatus("Error: " + err.message);
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
document.getElementById("up-btn").addEventListener("click", function () {
|
|
853
|
+
loadDirectory(parentPath(currentPath));
|
|
854
|
+
});
|
|
855
|
+
document.getElementById("refresh-btn").addEventListener("click", function () {
|
|
856
|
+
loadDirectory(currentPath);
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
window.McpApp.connect()
|
|
860
|
+
.then(function (bridge) {
|
|
861
|
+
callTool = bridge.callTool;
|
|
862
|
+
return loadDirectory(".");
|
|
863
|
+
})
|
|
864
|
+
.catch(function (err) {
|
|
865
|
+
setStatus("Standalone mode \\u2014 no host bridge (" + err.message + ")");
|
|
866
|
+
entries = [];
|
|
867
|
+
renderGrid();
|
|
868
|
+
});
|
|
869
|
+
</script>
|
|
870
|
+
</body>
|
|
871
|
+
</html>`;
|
|
872
|
+
|
|
277
873
|
// src/media-viewer/index.ts
|
|
278
874
|
var mediaViewer = defineApp({
|
|
875
|
+
id: "@agentproto/media-viewer",
|
|
876
|
+
name: "Media Viewer",
|
|
877
|
+
version: "0.1.0",
|
|
878
|
+
description: "Scan a folder for images, video, and audio \u2014 gallery view.",
|
|
279
879
|
agents: [cataloger],
|
|
280
|
-
workflows: [scanMedia]
|
|
880
|
+
workflows: [scanMedia],
|
|
881
|
+
ui: {
|
|
882
|
+
html: MEDIA_VIEWER_HTML,
|
|
883
|
+
title: "Media Viewer",
|
|
884
|
+
tools: [...MEDIA_VIEWER_TOOLS]
|
|
885
|
+
}
|
|
281
886
|
});
|
|
282
887
|
|
|
283
888
|
export { codeTeam, contentTeam, mailTriage, mediaViewer };
|