agent-sin 0.1.11 → 0.1.15
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/CHANGELOG.md +79 -0
- package/README.md +2 -1
- package/builtin-skills/_shared/_todo_lib.py +290 -0
- package/builtin-skills/even-g2-setup/main.ts +896 -0
- package/builtin-skills/even-g2-setup/skill.yaml +133 -0
- package/builtin-skills/memo-delete/main.py +28 -107
- package/builtin-skills/memo-delete/skill.yaml +10 -21
- package/builtin-skills/memo-index/main.py +96 -64
- package/builtin-skills/memo-index/skill.yaml +4 -10
- package/builtin-skills/memo-list/main.py +179 -0
- package/builtin-skills/memo-list/skill.yaml +51 -0
- package/builtin-skills/memo-save/main.py +191 -25
- package/builtin-skills/memo-save/skill.yaml +29 -5
- package/builtin-skills/memo-search/main.py +38 -18
- package/builtin-skills/memo-vector-search/main.py +11 -6
- package/builtin-skills/nightly-topic-knowledge/_feedback_lib.py +391 -0
- package/builtin-skills/nightly-topic-knowledge/_topics_lib.py +415 -0
- package/builtin-skills/nightly-topic-knowledge/main.py +403 -0
- package/builtin-skills/nightly-topic-knowledge/skill.yaml +88 -0
- package/builtin-skills/schedule-add/main.py +26 -0
- package/builtin-skills/service-restart/main.ts +249 -0
- package/builtin-skills/service-restart/skill.yaml +49 -0
- package/builtin-skills/todo-add/main.py +3 -1
- package/builtin-skills/todo-delete/main.py +3 -1
- package/builtin-skills/todo-done/main.py +3 -1
- package/builtin-skills/todo-list/main.py +4 -1
- package/builtin-skills/todo-tick/main.py +3 -1
- package/builtin-skills/topic-knowledge-read/main.py +118 -0
- package/builtin-skills/topic-knowledge-read/skill.yaml +49 -0
- package/dist/builder/build-action-classifier.d.ts +18 -0
- package/dist/builder/build-action-classifier.js +82 -1
- package/dist/builder/build-flow.d.ts +33 -4
- package/dist/builder/build-flow.js +251 -89
- package/dist/builder/builder-session.d.ts +1 -1
- package/dist/builder/builder-session.js +112 -7
- package/dist/builder/conversation-router.d.ts +4 -2
- package/dist/builder/conversation-router.js +19 -2
- package/dist/cli/index.js +323 -20
- package/dist/core/ai-provider.d.ts +1 -0
- package/dist/core/ai-provider.js +8 -3
- package/dist/core/chat-engine.d.ts +10 -3
- package/dist/core/chat-engine.js +1563 -197
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +82 -0
- package/dist/core/daily-memory-promotion.d.ts +7 -0
- package/dist/core/daily-memory-promotion.js +568 -14
- package/dist/core/image-attachments.d.ts +31 -0
- package/dist/core/image-attachments.js +237 -0
- package/dist/core/logger.d.ts +2 -1
- package/dist/core/logger.js +77 -1
- package/dist/core/memo-migration.d.ts +3 -0
- package/dist/core/memo-migration.js +422 -0
- package/dist/core/native-modules.d.ts +24 -0
- package/dist/core/native-modules.js +99 -0
- package/dist/core/notifier.d.ts +8 -3
- package/dist/core/notifier.js +191 -17
- package/dist/core/obsidian-vault.d.ts +19 -0
- package/dist/core/obsidian-vault.js +477 -0
- package/dist/core/operating-model.d.ts +2 -0
- package/dist/core/operating-model.js +15 -0
- package/dist/core/output-writer.d.ts +3 -2
- package/dist/core/output-writer.js +108 -7
- package/dist/core/profile-memory.js +22 -1
- package/dist/core/runtime.d.ts +2 -0
- package/dist/core/runtime.js +9 -1
- package/dist/core/secrets.d.ts +4 -0
- package/dist/core/secrets.js +34 -0
- package/dist/core/skill-history.d.ts +44 -0
- package/dist/core/skill-history.js +329 -0
- package/dist/core/skill-registry.d.ts +5 -0
- package/dist/core/skill-registry.js +11 -0
- package/dist/discord/bot.d.ts +13 -0
- package/dist/discord/bot.js +542 -10
- package/dist/even-g2/gateway.d.ts +15 -0
- package/dist/even-g2/gateway.js +868 -0
- package/dist/runtimes/codex-app-server.d.ts +5 -1
- package/dist/runtimes/codex-app-server.js +147 -8
- package/dist/runtimes/python-runner.js +82 -0
- package/dist/runtimes/typescript-runner.js +13 -1
- package/dist/skills-sdk/types.d.ts +19 -4
- package/dist/telegram/bot.d.ts +1 -0
- package/dist/telegram/bot.js +122 -31
- package/package.json +3 -1
- package/templates/even-g2-agent/README.md +83 -0
- package/templates/even-g2-agent/app.json +20 -0
- package/templates/even-g2-agent/index.html +31 -0
- package/templates/even-g2-agent/package-lock.json +1836 -0
- package/templates/even-g2-agent/package.json +22 -0
- package/templates/even-g2-agent/scripts/qr-auto.mjs +182 -0
- package/templates/even-g2-agent/src/embedded-config.ts +4 -0
- package/templates/even-g2-agent/src/main.ts +539 -0
- package/templates/even-g2-agent/src/style.css +70 -0
- package/templates/even-g2-agent/tsconfig.json +11 -0
- package/templates/skill-python/main.py +20 -2
- package/templates/skill-python/skill.yaml +9 -0
- package/templates/skill-typescript/main.ts +40 -5
- package/templates/skill-typescript/skill.yaml +9 -0
package/dist/telegram/bot.js
CHANGED
|
@@ -2,10 +2,12 @@ import path from "node:path";
|
|
|
2
2
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { loadConfig } from "../core/config.js";
|
|
4
4
|
import { appendEventLog } from "../core/logger.js";
|
|
5
|
+
import { stripInternalControlBlocks, } from "../core/chat-engine.js";
|
|
5
6
|
import { createIntentRuntime, renderBuildFooter, shouldShowBuildFooter, } from "../builder/build-flow.js";
|
|
6
7
|
import { routeConversationMessage, } from "../builder/conversation-router.js";
|
|
7
8
|
import { cleanProgressText, formatBuildProgress, progressIntervalMs } from "../builder/progress-format.js";
|
|
8
9
|
import { chunkText, cleanAttachmentText, formatAttachmentLabel, formatBytes, guessImageMimeType, indentAttachmentContent, isImageLikeFile, isTextLikeFile, } from "../core/message-utils.js";
|
|
10
|
+
import { collectLocalFileAttachments, collectLocalImageAttachments, } from "../core/image-attachments.js";
|
|
9
11
|
import { isEmptyIntentRuntime, loadIntentRuntimeMap, saveIntentRuntimeMap, } from "../builder/intent-runtime-store.js";
|
|
10
12
|
import { inferLocaleFromText, l, lLines, withLocale } from "../core/i18n.js";
|
|
11
13
|
import { consumeUpdateBanner, scheduleUpdateCheck } from "../core/update-notifier.js";
|
|
@@ -350,20 +352,20 @@ async function handleTelegramMessage(state, message) {
|
|
|
350
352
|
draft.update(l("Thinking", "考えています"), { force: true });
|
|
351
353
|
const prevMode = intentRuntime.mode;
|
|
352
354
|
try {
|
|
353
|
-
const
|
|
355
|
+
const routed = await routeTelegramMessage(state, userText, history, intentRuntime, chatKey, chatId, threadId, message.message_id, draft, userMessage.images);
|
|
354
356
|
typing.stop();
|
|
355
357
|
trimHistory(history);
|
|
356
358
|
void saveTelegramHistories(state);
|
|
357
359
|
void saveTelegramIntentRuntimes(state);
|
|
358
360
|
const isBuildEntry = prevMode !== "build" && intentRuntime.mode === "build";
|
|
359
|
-
const decorated = withTelegramModeBadge(intentRuntime, lines, { userText, isBuildEntry });
|
|
361
|
+
const decorated = withTelegramModeBadge(intentRuntime, routed.lines, { userText, isBuildEntry });
|
|
360
362
|
scheduleUpdateCheck(state.config.workspace);
|
|
361
363
|
const banner = await consumeUpdateBanner(state.config.workspace);
|
|
362
364
|
const finalLines = banner ? [banner, "", ...decorated] : decorated;
|
|
363
365
|
const reply = finalLines.filter((line) => line !== undefined && line !== null).join("\n").trim();
|
|
364
366
|
draft.update(l("Sending reply", "応答を送信しています"), { force: true });
|
|
365
367
|
await draft.finish();
|
|
366
|
-
await
|
|
368
|
+
await sendTelegramMessageWithLocalAttachments(state, chatId, reply || l("(no response)", "(応答なし)"), sendOptions, routed.localAttachmentPaths);
|
|
367
369
|
}
|
|
368
370
|
catch (error) {
|
|
369
371
|
typing.stop();
|
|
@@ -397,7 +399,8 @@ async function refreshTelegramStateConfig(state) {
|
|
|
397
399
|
}
|
|
398
400
|
}
|
|
399
401
|
async function routeTelegramMessage(state, text, history, intentRuntime, chatKey, chatId, threadId, replyToMessageId, draft, images = []) {
|
|
400
|
-
|
|
402
|
+
const localAttachmentPaths = [];
|
|
403
|
+
const lines = await routeConversationMessage({
|
|
401
404
|
config: state.config,
|
|
402
405
|
text,
|
|
403
406
|
history,
|
|
@@ -407,7 +410,11 @@ async function routeTelegramMessage(state, text, history, intentRuntime, chatKey
|
|
|
407
410
|
createBuildProgress: () => createTelegramBuildProgressReporter(state, chatKey, chatId, threadId, replyToMessageId, draft),
|
|
408
411
|
onChatProgress: (event) => draft.onChatProgress(event),
|
|
409
412
|
onAiProgress: (event) => draft.onAiProgress(event),
|
|
413
|
+
onLocalAttachments: (paths) => {
|
|
414
|
+
localAttachmentPaths.push(...paths);
|
|
415
|
+
},
|
|
410
416
|
});
|
|
417
|
+
return { lines, localAttachmentPaths };
|
|
411
418
|
}
|
|
412
419
|
function createTelegramBuildProgressReporter(state, chatKey, chatId, threadId, replyToMessageId, draft) {
|
|
413
420
|
const minIntervalMs = telegramProgressIntervalMs();
|
|
@@ -527,31 +534,9 @@ function helpText() {
|
|
|
527
534
|
return lLines([
|
|
528
535
|
"Welcome to the Agent-Sin Telegram bot.",
|
|
529
536
|
"It responds in DMs, mentions, and replies to the bot. Registered skills are called automatically when useful.",
|
|
530
|
-
"",
|
|
531
|
-
"Commands:",
|
|
532
|
-
" /help Show this help",
|
|
533
|
-
" /reset Reset this chat history",
|
|
534
|
-
" /progress quiet|detail Toggle progress messages",
|
|
535
|
-
" /back Return from build/edit mode to chat mode",
|
|
536
|
-
" /build [skill-id] [request] Create a new skill",
|
|
537
|
-
' /build chat <id> "message" Revise through conversation',
|
|
538
|
-
" /build list List drafts",
|
|
539
|
-
" /build test <id> Test a draft",
|
|
540
|
-
" /build status <id> Show status",
|
|
541
537
|
], [
|
|
542
538
|
"Agent-Sin Telegram bot へようこそ。",
|
|
543
539
|
"DM、メンション、bot への返信に反応します。登録済みスキルも自動で呼び出されます。",
|
|
544
|
-
"",
|
|
545
|
-
"コマンド:",
|
|
546
|
-
" /help この使い方を表示",
|
|
547
|
-
" /reset このチャットの会話履歴をリセット",
|
|
548
|
-
" /progress quiet|detail 進捗通知の量を切り替え",
|
|
549
|
-
" /back build / edit モードから chat モードに戻る",
|
|
550
|
-
" /build [skill-id] [要望] 新規スキルを作成",
|
|
551
|
-
' /build chat <id> "メッセージ" 会話しながら修正',
|
|
552
|
-
" /build list 作成中の一覧",
|
|
553
|
-
" /build test <id> 動作確認",
|
|
554
|
-
" /build status <id> 状態を見る",
|
|
555
540
|
]).join("\n");
|
|
556
541
|
}
|
|
557
542
|
async function formatTelegramUserMessageForChat(state, message) {
|
|
@@ -733,7 +718,7 @@ async function persistTelegramAttachmentBuffer(state, attachment, buffer, mimeTy
|
|
|
733
718
|
const HH = String(now.getHours()).padStart(2, "0");
|
|
734
719
|
const mm = String(now.getMinutes()).padStart(2, "0");
|
|
735
720
|
const ss = String(now.getSeconds()).padStart(2, "0");
|
|
736
|
-
const dir = path.join(state.config.
|
|
721
|
+
const dir = path.join(state.config.memory_dir, "attachments", yyyy, MM);
|
|
737
722
|
await mkdir(dir, { recursive: true });
|
|
738
723
|
const ext = pickAttachmentExtension(attachment.filename, mimeType);
|
|
739
724
|
const random = Math.random().toString(36).slice(2, 8);
|
|
@@ -924,6 +909,12 @@ export function formatTelegramDraftProgress(event) {
|
|
|
924
909
|
return null;
|
|
925
910
|
}
|
|
926
911
|
}
|
|
912
|
+
export function formatTelegramChatDraftProgress(event) {
|
|
913
|
+
if (event.kind === "message") {
|
|
914
|
+
return l("Preparing reply", "応答を整理しています");
|
|
915
|
+
}
|
|
916
|
+
return formatTelegramDraftProgress(event);
|
|
917
|
+
}
|
|
927
918
|
function createTelegramDraftStreamer(state, message) {
|
|
928
919
|
if (!shouldUseTelegramDraftStream(message)) {
|
|
929
920
|
return noopTelegramDraftStreamer();
|
|
@@ -990,9 +981,9 @@ function createTelegramDraftStreamer(state, message) {
|
|
|
990
981
|
}
|
|
991
982
|
},
|
|
992
983
|
onAiProgress(event) {
|
|
993
|
-
const text =
|
|
984
|
+
const text = formatTelegramChatDraftProgress(event);
|
|
994
985
|
if (text)
|
|
995
|
-
enqueue(text
|
|
986
|
+
enqueue(text);
|
|
996
987
|
},
|
|
997
988
|
onBuildProgress(event) {
|
|
998
989
|
const text = formatTelegramDraftProgress(event);
|
|
@@ -1024,7 +1015,7 @@ function telegramDraftIntervalMs() {
|
|
|
1024
1015
|
return 1500;
|
|
1025
1016
|
}
|
|
1026
1017
|
function cleanDraftText(text) {
|
|
1027
|
-
const cleaned = text
|
|
1018
|
+
const cleaned = stripInternalControlBlocks(text)
|
|
1028
1019
|
.replace(/```/g, "")
|
|
1029
1020
|
.replace(/\r\n/g, "\n")
|
|
1030
1021
|
.replace(/\r/g, "\n")
|
|
@@ -1058,7 +1049,11 @@ function startTypingKeepalive(state, chatId, threadId) {
|
|
|
1058
1049
|
};
|
|
1059
1050
|
}
|
|
1060
1051
|
async function sendTelegramMessage(state, chatId, content, options = {}) {
|
|
1061
|
-
const
|
|
1052
|
+
const sanitized = stripInternalControlBlocks(content);
|
|
1053
|
+
if (!sanitized.trim()) {
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
const chunks = chunkTelegramMessage(sanitized);
|
|
1062
1057
|
for (let index = 0; index < chunks.length; index += 1) {
|
|
1063
1058
|
const chunk = chunks[index];
|
|
1064
1059
|
const payload = telegramSendPayload(chatId, chunk, index === 0 ? options : { threadId: options.threadId });
|
|
@@ -1088,6 +1083,60 @@ async function sendTelegramMessage(state, chatId, content, options = {}) {
|
|
|
1088
1083
|
}
|
|
1089
1084
|
}
|
|
1090
1085
|
}
|
|
1086
|
+
async function sendTelegramMessageWithLocalAttachments(state, chatId, content, options = {}, localAttachmentPaths = []) {
|
|
1087
|
+
const sanitized = stripInternalControlBlocks(content);
|
|
1088
|
+
const explicitAttachments = await collectLocalFileAttachments({
|
|
1089
|
+
paths: localAttachmentPaths,
|
|
1090
|
+
cwd: state.config.workspace,
|
|
1091
|
+
allowedRoots: [state.config.workspace, state.config.notes_dir],
|
|
1092
|
+
});
|
|
1093
|
+
const inlineImages = await collectLocalImageAttachments({
|
|
1094
|
+
text: sanitized,
|
|
1095
|
+
cwd: state.config.workspace,
|
|
1096
|
+
allowedRoots: [state.config.workspace, state.config.notes_dir],
|
|
1097
|
+
});
|
|
1098
|
+
const attachments = dedupeLocalAttachments([...explicitAttachments, ...inlineImages]);
|
|
1099
|
+
if (sanitized.trim()) {
|
|
1100
|
+
await sendTelegramMessage(state, chatId, sanitized, options);
|
|
1101
|
+
}
|
|
1102
|
+
for (let index = 0; index < attachments.length; index += 1) {
|
|
1103
|
+
await sendTelegramFileAttachment(state, chatId, attachments[index], index === 0 && !sanitized.trim() ? options : { threadId: options.threadId });
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
async function sendTelegramFileAttachment(state, chatId, attachment, options = {}) {
|
|
1107
|
+
const method = shouldSendTelegramAsPhoto(attachment) ? "sendPhoto" : "sendDocument";
|
|
1108
|
+
const fileField = method === "sendPhoto" ? "photo" : "document";
|
|
1109
|
+
const payload = telegramFilePayload(chatId, options);
|
|
1110
|
+
try {
|
|
1111
|
+
const buffer = await readFile(attachment.path);
|
|
1112
|
+
await telegramMultipartApi(state, method, payload, fileField, attachment.filename, attachment.mimeType, buffer);
|
|
1113
|
+
}
|
|
1114
|
+
catch (error) {
|
|
1115
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1116
|
+
console.error(`agent-sin telegram: file send error: ${message}`);
|
|
1117
|
+
await appendEventLog(state.config, {
|
|
1118
|
+
level: "error",
|
|
1119
|
+
source: "telegram",
|
|
1120
|
+
event: "file_send_error",
|
|
1121
|
+
message,
|
|
1122
|
+
details: { chat_id: chatId, path: attachment.path },
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
function shouldSendTelegramAsPhoto(attachment) {
|
|
1127
|
+
return attachment.isImage && ["image/png", "image/jpeg", "image/webp"].includes(attachment.mimeType.toLowerCase());
|
|
1128
|
+
}
|
|
1129
|
+
function dedupeLocalAttachments(attachments) {
|
|
1130
|
+
const seen = new Set();
|
|
1131
|
+
const out = [];
|
|
1132
|
+
for (const attachment of attachments) {
|
|
1133
|
+
if (seen.has(attachment.path))
|
|
1134
|
+
continue;
|
|
1135
|
+
seen.add(attachment.path);
|
|
1136
|
+
out.push(attachment);
|
|
1137
|
+
}
|
|
1138
|
+
return out;
|
|
1139
|
+
}
|
|
1091
1140
|
async function sendTelegramChatAction(state, chatId, threadId) {
|
|
1092
1141
|
try {
|
|
1093
1142
|
const payload = {
|
|
@@ -1103,6 +1152,21 @@ async function sendTelegramChatAction(state, chatId, threadId) {
|
|
|
1103
1152
|
// typing indicator is a hint; ignore failures
|
|
1104
1153
|
}
|
|
1105
1154
|
}
|
|
1155
|
+
function telegramFilePayload(chatId, options = {}) {
|
|
1156
|
+
const payload = {
|
|
1157
|
+
chat_id: chatId,
|
|
1158
|
+
};
|
|
1159
|
+
if (options.threadId) {
|
|
1160
|
+
payload.message_thread_id = options.threadId;
|
|
1161
|
+
}
|
|
1162
|
+
if (options.replyToMessageId && process.env.AGENT_SIN_TELEGRAM_REPLY_TO_MESSAGE !== "0") {
|
|
1163
|
+
payload.reply_parameters = {
|
|
1164
|
+
message_id: options.replyToMessageId,
|
|
1165
|
+
allow_sending_without_reply: true,
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
return payload;
|
|
1169
|
+
}
|
|
1106
1170
|
async function telegramApi(state, method, payload) {
|
|
1107
1171
|
const response = await fetch(`${TELEGRAM_API_BASE}/bot${state.token}/${method}`, {
|
|
1108
1172
|
method: "POST",
|
|
@@ -1119,6 +1183,33 @@ async function telegramApi(state, method, payload) {
|
|
|
1119
1183
|
}
|
|
1120
1184
|
return data.result;
|
|
1121
1185
|
}
|
|
1186
|
+
async function telegramMultipartApi(state, method, payload, fileField, filename, mimeType, buffer) {
|
|
1187
|
+
const form = new FormData();
|
|
1188
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
1189
|
+
if (value === undefined || value === null)
|
|
1190
|
+
continue;
|
|
1191
|
+
form.append(key, typeof value === "object" ? JSON.stringify(value) : String(value));
|
|
1192
|
+
}
|
|
1193
|
+
form.append(fileField, blobFromBuffer(buffer, mimeType), filename);
|
|
1194
|
+
const response = await fetch(`${TELEGRAM_API_BASE}/bot${state.token}/${method}`, {
|
|
1195
|
+
method: "POST",
|
|
1196
|
+
body: form,
|
|
1197
|
+
});
|
|
1198
|
+
if (!response.ok) {
|
|
1199
|
+
const detail = await response.text().catch(() => "");
|
|
1200
|
+
throw new Error(`HTTP ${response.status} ${detail.slice(0, 200)}`);
|
|
1201
|
+
}
|
|
1202
|
+
const data = (await response.json());
|
|
1203
|
+
if (!data.ok) {
|
|
1204
|
+
throw new Error(data.description || "Telegram API error");
|
|
1205
|
+
}
|
|
1206
|
+
return data.result;
|
|
1207
|
+
}
|
|
1208
|
+
function blobFromBuffer(buffer, type) {
|
|
1209
|
+
const bytes = new Uint8Array(buffer.length);
|
|
1210
|
+
bytes.set(buffer);
|
|
1211
|
+
return new Blob([bytes.buffer], { type });
|
|
1212
|
+
}
|
|
1122
1213
|
async function loadTelegramOffset(filePath) {
|
|
1123
1214
|
try {
|
|
1124
1215
|
const raw = await readFile(filePath, "utf8");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-sin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Program Skill-first personal AI agent OS CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,12 +44,14 @@
|
|
|
44
44
|
"release:check": "npm test && npm run pack:check"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"better-sqlite3": "^12.10.0",
|
|
47
48
|
"nodemailer": "^8.0.7",
|
|
48
49
|
"typescript": "^5.9.3",
|
|
49
50
|
"ws": "^8.20.0",
|
|
50
51
|
"yaml": "^2.7.0"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
54
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
53
55
|
"@types/node": "^22.10.0",
|
|
54
56
|
"@types/nodemailer": "^8.0.0",
|
|
55
57
|
"@types/ws": "^8.18.1"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Agent-Sin Even G2 Template
|
|
2
|
+
|
|
3
|
+
This is a minimal Even Hub app for the Agent-Sin G2 gateway.
|
|
4
|
+
|
|
5
|
+
> 👉 **End-to-end setup guide for using this on your own G2 glasses:**
|
|
6
|
+
> [English](../../docs/even-g2-setup.md) · [日本語](../../docs/even-g2-setup.ja.md)
|
|
7
|
+
> The Local Flow below is the minimal developer-facing reference.
|
|
8
|
+
|
|
9
|
+
## Local Flow
|
|
10
|
+
|
|
11
|
+
1. Start the gateway:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
agent-sin g2 setup --history-channel discord --discord-thread auto
|
|
15
|
+
agent-sin service restart
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Use `--history-channel telegram` or `--history-channel none` if you do not
|
|
19
|
+
want Discord history.
|
|
20
|
+
|
|
21
|
+
2. Edit `app.json` to add your own gateway and dev-server origins to
|
|
22
|
+
`permissions[0].whitelist`. The committed file only lists `127.0.0.1` so
|
|
23
|
+
personal IPs and Tailscale hostnames never leak into git. Add what you need
|
|
24
|
+
locally, for example:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
"whitelist": [
|
|
28
|
+
"http://127.0.0.1:8765",
|
|
29
|
+
"http://127.0.0.1:5173",
|
|
30
|
+
"http://192.168.1.23:5173",
|
|
31
|
+
"http://192.168.1.23:8765"
|
|
32
|
+
]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For Tailscale, keep the phone on Tailscale and use the private URL instead
|
|
36
|
+
of any public Funnel URL:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
"whitelist": ["http://your-device.tailnet-name.ts.net:8765"]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Do not commit your edited `app.json` with personal hosts. Revert it to the
|
|
43
|
+
`127.0.0.1`-only baseline before staging, or keep your edits in a working
|
|
44
|
+
copy you do not push.
|
|
45
|
+
|
|
46
|
+
3. Install and run the app:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install
|
|
50
|
+
npm run dev
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
In another terminal:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm run qr:auto
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The QR URL includes the app URL, gateway URL, and token. The token is placed
|
|
60
|
+
in the URL hash so it is not sent to the Vite dev server.
|
|
61
|
+
|
|
62
|
+
Override detection when needed:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run qr:auto -- --host 100.x.y.z
|
|
66
|
+
npm run qr:auto -- --app http://100.x.y.z:5173 --server http://100.x.y.z:8765
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
4. Open it from the glasses menu.
|
|
70
|
+
|
|
71
|
+
## Controls
|
|
72
|
+
|
|
73
|
+
- `Talk`: start voice capture
|
|
74
|
+
- `Talk` again: stop and send
|
|
75
|
+
- `Inbox`: show latest unread gateway notification
|
|
76
|
+
- `Exit`: close the glasses page
|
|
77
|
+
|
|
78
|
+
Voice transcription uses the gateway's `OPENAI_API_KEY` and defaults to
|
|
79
|
+
`gpt-4o-mini-transcribe`. Text and agent replies are mirrored to configured
|
|
80
|
+
Discord / Telegram notification targets only when `AGENT_SIN_G2_HISTORY_CHANNEL`
|
|
81
|
+
is set to `telegram`, `discord`, or `auto`. With
|
|
82
|
+
`AGENT_SIN_G2_DISCORD_THREAD=auto`, the gateway creates one public Discord
|
|
83
|
+
thread in the configured Discord channel and keeps G2 history there.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"package_id": "com.example.agentsin",
|
|
3
|
+
"edition": "202601",
|
|
4
|
+
"name": "Agent-Sin",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"min_app_version": "2.0.0",
|
|
7
|
+
"min_sdk_version": "0.0.10",
|
|
8
|
+
"entrypoint": "index.html",
|
|
9
|
+
"permissions": [
|
|
10
|
+
{
|
|
11
|
+
"name": "network",
|
|
12
|
+
"desc": "Connects to the user's Agent-Sin gateway for private agent messages.",
|
|
13
|
+
"whitelist": [
|
|
14
|
+
"http://127.0.0.1:8765",
|
|
15
|
+
"http://127.0.0.1:5173"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"supported_languages": ["ja", "en"]
|
|
20
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="ja">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Agent-Sin</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<main id="app">
|
|
10
|
+
<h1>Agent-Sin</h1>
|
|
11
|
+
<form id="settings">
|
|
12
|
+
<label>
|
|
13
|
+
Server URL
|
|
14
|
+
<input id="serverUrl" placeholder="http://192.168.1.10:8765" />
|
|
15
|
+
</label>
|
|
16
|
+
<label>
|
|
17
|
+
Token
|
|
18
|
+
<input id="token" placeholder="AGENT_SIN_G2_TOKEN" />
|
|
19
|
+
</label>
|
|
20
|
+
<button type="submit">Save</button>
|
|
21
|
+
</form>
|
|
22
|
+
<p id="status">Loading...</p>
|
|
23
|
+
<form id="textForm">
|
|
24
|
+
<input id="textInput" placeholder="Test message" />
|
|
25
|
+
<button type="submit">Send</button>
|
|
26
|
+
</form>
|
|
27
|
+
<pre id="log"></pre>
|
|
28
|
+
</main>
|
|
29
|
+
<script type="module" src="/src/main.ts"></script>
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|