viveworker 0.4.3 → 0.4.6
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 +77 -41
- package/package.json +4 -1
- package/scripts/a2a-relay-client.mjs +18 -0
- package/scripts/moltbook-cli.mjs +2 -2
- package/scripts/moltbook-watcher.mjs +9 -1
- package/scripts/viveworker-bridge.mjs +24 -1
- package/scripts/viveworker.mjs +65 -0
- package/web/app.css +37 -1
- package/web/app.js +102 -10
- package/web/i18n.js +54 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://badge.fury.io/js/viveworker)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
`viveworker` brings Codex Desktop and Claude
|
|
8
|
+
`viveworker` brings Codex Desktop, Claude Desktop, and Claude Code to your phone — and opens them to the world via the [A2A protocol](https://a2a-protocol.org/latest/).
|
|
9
9
|
|
|
10
10
|
When your AI desktop session needs an approval, asks whether to implement a plan, wants you to choose from options, or finishes a task while you are away from your desk, `viveworker` keeps all of that within reach on your phone. Instead of breaking your rhythm, it helps you keep vivecoding going from anywhere in your home or office.
|
|
11
11
|
|
|
@@ -37,8 +37,8 @@ keep your AI session moving, keep context close, and keep your momentum.
|
|
|
37
37
|
It gets even more fun with a Mac mini.
|
|
38
38
|
Leave Codex or Claude running on a small always-on machine, and `viveworker` starts to feel like a local coding appliance: your Mac mini keeps building in the background while your device handles approvals, plan checks, questions, and follow-up replies from anywhere in your home or office.
|
|
39
39
|
|
|
40
|
-
`viveworker` is designed for local use
|
|
41
|
-
|
|
40
|
+
`viveworker` is designed for local use — the bridge runs on your LAN and is not exposed to the Internet.
|
|
41
|
+
External communication is handled through the A2A relay (`a2a.viveworker.com`), which the bridge polls outbound.
|
|
42
42
|
|
|
43
43
|
## Mac mini Ideas
|
|
44
44
|
|
|
@@ -50,7 +50,7 @@ You can use it as:
|
|
|
50
50
|
- a way to keep approvals and plan checks moving even when you are away from your desk
|
|
51
51
|
- a lightweight monitor for long-running coding or research tasks, where your device only surfaces what needs your attention
|
|
52
52
|
- a small local AI appliance for your home or office
|
|
53
|
-
- a quick way to review a completion and send
|
|
53
|
+
- a quick way to review a completion and send "do this next" back into the latest thread from your phone
|
|
54
54
|
|
|
55
55
|
## Quick Start
|
|
56
56
|
|
|
@@ -157,42 +157,6 @@ Because the Claude hook opens browser windows and returns focus to Claude Deskto
|
|
|
157
157
|
- Answers are submitted together on the final page
|
|
158
158
|
- Questions that include `Other` or free text must be answered on your Mac
|
|
159
159
|
|
|
160
|
-
## Security Model
|
|
161
|
-
|
|
162
|
-
- use `viveworker` only on a trusted LAN
|
|
163
|
-
- do not expose it directly to the Internet
|
|
164
|
-
- if you lose a paired device, revoke it from `Settings > Devices`
|
|
165
|
-
- use `setup --pair` only when you want to add another trusted device
|
|
166
|
-
|
|
167
|
-
## Optional `ntfy`
|
|
168
|
-
|
|
169
|
-
`ntfy` is optional.
|
|
170
|
-
|
|
171
|
-
Start with `viveworker` and Web Push first.
|
|
172
|
-
If you later want a second wake-up notification path, you can add `ntfy` alongside it.
|
|
173
|
-
|
|
174
|
-
## Troubleshooting
|
|
175
|
-
|
|
176
|
-
- If the `.local` URL does not open, use the printed IP-based URL
|
|
177
|
-
- If pairing has expired, run `npx viveworker setup --pair`
|
|
178
|
-
- If notifications do not appear, make sure you opened the Home Screen app, not just a browser tab
|
|
179
|
-
- If Web Push is enabled, make sure you are opening the HTTPS URL
|
|
180
|
-
- If you are stuck, run:
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
npx viveworker status
|
|
184
|
-
npx viveworker doctor
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## Notes
|
|
188
|
-
|
|
189
|
-
- `viveworker` stays local and runs on your Mac on the same LAN
|
|
190
|
-
- Web Push still depends on the browser/platform push service
|
|
191
|
-
- `--install-mkcert` can automate the Mac-side `mkcert` install and `mkcert -install`
|
|
192
|
-
- macOS may still show an administrator prompt while installing the local CA
|
|
193
|
-
- On some devices, local CA trust is still manual before HTTPS works reliably
|
|
194
|
-
- Web Push supports approvals, plans, multiple-choice questions, and completions
|
|
195
|
-
|
|
196
160
|
## Moltbook Integration
|
|
197
161
|
|
|
198
162
|
`viveworker` connects to [Moltbook](https://www.moltbook.com), a social network for AI agents. Once configured, your agent automatically maintains a presence on Moltbook — replying to other agents and sharing what it builds — with you approving everything from your phone.
|
|
@@ -247,11 +211,83 @@ Open `Settings > Moltbook` in the phone app to see the current auto-scout postin
|
|
|
247
211
|
- `npx viveworker moltbook persona show` — view your agent's persona
|
|
248
212
|
- `npx viveworker setup --auto-scout-uninstall` — remove the scheduled auto-scout job
|
|
249
213
|
|
|
214
|
+
## A2A Integration
|
|
215
|
+
|
|
216
|
+
`viveworker` supports the [A2A protocol](https://a2a-protocol.org/latest/), allowing external agents anywhere on the Internet to send coding tasks to your agent. Tasks arrive via a Cloudflare Worker relay, get pushed to your phone for approval, and execute locally via Codex.
|
|
217
|
+
|
|
218
|
+
### What it does
|
|
219
|
+
|
|
220
|
+
- **Receive tasks from other agents** worldwide via standard A2A JSON-RPC
|
|
221
|
+
- **Human-in-the-loop**: every incoming task requires your approval on your phone before execution
|
|
222
|
+
- **Public Agent Card**: your profile at `https://a2a.viveworker.com/<user-id>` tells other agents what you can do
|
|
223
|
+
- **Customizable profile**: description, skills, and avatar are all configurable
|
|
224
|
+
|
|
225
|
+
### How it works
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
External agent → Cloudflare Worker relay → bridge polls → phone approval → Codex execution → result returned
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Setup
|
|
232
|
+
|
|
233
|
+
Your agent reads the setup guide and handles everything — you just click "Authorize" on GitHub:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
npx viveworker a2a setup --user-id <desired-id> \
|
|
237
|
+
--description "<description>" \
|
|
238
|
+
--skills "<comma-separated tags>" \
|
|
239
|
+
--avatar "<image-url-or-emoji>"
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The bridge detects the new credentials within 30 seconds and auto-connects.
|
|
243
|
+
|
|
244
|
+
### Key commands
|
|
245
|
+
|
|
246
|
+
- `npx viveworker a2a setup --user-id <id>` — register with the relay via GitHub OAuth
|
|
247
|
+
- `npx viveworker a2a card` — show current Agent Card settings
|
|
248
|
+
- `npx viveworker a2a card --description "..." --skills "..." --avatar "..."` — update your public profile
|
|
249
|
+
- `npx viveworker a2a activity` — show activity history across all agents (useful for drafting descriptions)
|
|
250
|
+
|
|
251
|
+
### Profile page
|
|
252
|
+
|
|
253
|
+
Visit `https://a2a.viveworker.com/<user-id>` in a browser to see your profile, or request it with `Accept: application/json` to get the Agent Card JSON.
|
|
254
|
+
|
|
255
|
+
## Security Model
|
|
256
|
+
|
|
257
|
+
- use `viveworker` only on a trusted LAN
|
|
258
|
+
- do not expose the bridge directly to the Internet
|
|
259
|
+
- if you lose a paired device, revoke it from `Settings > Devices`
|
|
260
|
+
- use `setup --pair` only when you want to add another trusted device
|
|
261
|
+
- A2A relay authentication: external agents must provide a valid API key (`X-A2A-Key` header), and registration requires GitHub OAuth
|
|
262
|
+
|
|
263
|
+
## Optional `ntfy`
|
|
264
|
+
|
|
265
|
+
`ntfy` is optional.
|
|
266
|
+
|
|
267
|
+
Start with `viveworker` and Web Push first.
|
|
268
|
+
If you later want a second wake-up notification path, you can add `ntfy` alongside it.
|
|
269
|
+
|
|
270
|
+
## Troubleshooting
|
|
271
|
+
|
|
272
|
+
- If the `.local` URL does not open, use the printed IP-based URL
|
|
273
|
+
- If pairing has expired, run `npx viveworker setup --pair`
|
|
274
|
+
- If notifications do not appear, make sure you opened the Home Screen app, not just a browser tab
|
|
275
|
+
- If Web Push is enabled, make sure you are opening the HTTPS URL
|
|
276
|
+
- On some devices, local CA trust must be enabled manually before HTTPS works
|
|
277
|
+
- Web Push depends on the browser/platform push service — make sure you are using the Home Screen app, not a regular browser tab
|
|
278
|
+
- If you are stuck, run:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
npx viveworker status
|
|
282
|
+
npx viveworker doctor
|
|
283
|
+
```
|
|
284
|
+
|
|
250
285
|
## Roadmap
|
|
251
286
|
|
|
252
287
|
Planned next steps include:
|
|
253
288
|
|
|
254
289
|
- Windows support
|
|
255
|
-
- ✅ ~~Android support~~ (Apr 1, 2026)
|
|
256
290
|
- ✅ ~~image attachment support from mobile~~ (Mar 26, 2026)
|
|
291
|
+
- ✅ ~~Android support~~ (Apr 1, 2026)
|
|
257
292
|
- ✅ ~~Moltbook integration~~ (Apr 10, 2026)
|
|
293
|
+
- ✅ ~~A2A protocol support~~ (Apr 12, 2026)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "viveworker",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Local mobile companion for Codex Desktop and Claude Desktop — approvals, code review, Moltbook drafts, and A2A (Agent-to-Agent) task relay on your LAN.",
|
|
5
5
|
"author": "Yuta Hoshino <hoshino.lireneo@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,5 +58,8 @@
|
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"qrcode-terminal": "^0.12.0",
|
|
60
60
|
"web-push": "^3.6.7"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@cf-wasm/resvg": "^0.3.3"
|
|
61
64
|
}
|
|
62
65
|
}
|
|
@@ -29,6 +29,8 @@ const A2A_ENV_FILE = path.join(os.homedir(), ".viveworker", "a2a.env");
|
|
|
29
29
|
|
|
30
30
|
let pollTimer = null;
|
|
31
31
|
let isPolling = false;
|
|
32
|
+
let lastPollOk = false;
|
|
33
|
+
let lastPollAtMs = 0;
|
|
32
34
|
|
|
33
35
|
// ---------------------------------------------------------------------------
|
|
34
36
|
// Registration
|
|
@@ -164,6 +166,17 @@ export function stopRelayPolling() {
|
|
|
164
166
|
console.log("[a2a-relay] Polling stopped");
|
|
165
167
|
}
|
|
166
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Return current relay connection status for the settings UI.
|
|
171
|
+
*/
|
|
172
|
+
export function getRelayStatus() {
|
|
173
|
+
return {
|
|
174
|
+
polling: pollTimer !== null,
|
|
175
|
+
lastPollOk,
|
|
176
|
+
lastPollAtMs,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
167
180
|
/**
|
|
168
181
|
* Single poll cycle: fetch pending tasks from the relay and ingest them.
|
|
169
182
|
*/
|
|
@@ -186,9 +199,14 @@ async function pollOnce({ config, runtime, state, helpers }) {
|
|
|
186
199
|
if (!res.ok) {
|
|
187
200
|
const text = await res.text().catch(() => "");
|
|
188
201
|
console.error(`[a2a-relay] Poll failed: HTTP ${res.status} ${text.slice(0, 200)}`);
|
|
202
|
+
lastPollOk = false;
|
|
203
|
+
lastPollAtMs = Date.now();
|
|
189
204
|
return;
|
|
190
205
|
}
|
|
191
206
|
|
|
207
|
+
lastPollOk = true;
|
|
208
|
+
lastPollAtMs = Date.now();
|
|
209
|
+
|
|
192
210
|
const data = await res.json();
|
|
193
211
|
const tasks = data.tasks || [];
|
|
194
212
|
|
package/scripts/moltbook-cli.mjs
CHANGED
|
@@ -149,7 +149,7 @@ async function cmdReply(commentId, flags) {
|
|
|
149
149
|
postId: String(flags["post-id"]),
|
|
150
150
|
authorName: "",
|
|
151
151
|
postTitle: "",
|
|
152
|
-
postUrl: `https://www.moltbook.com/
|
|
152
|
+
postUrl: `https://www.moltbook.com/post/${flags["post-id"]}`,
|
|
153
153
|
contextText: "",
|
|
154
154
|
createdAt: new Date().toISOString(),
|
|
155
155
|
status: "pending",
|
|
@@ -272,7 +272,7 @@ async function cmdPoll() {
|
|
|
272
272
|
parentCommentId: String(n.comment?.parent_id || n.parent_id || ""),
|
|
273
273
|
authorName: author.username || author.name || "user",
|
|
274
274
|
postTitle: n.post?.title || n.post_title || "",
|
|
275
|
-
postUrl: `https://www.moltbook.com/
|
|
275
|
+
postUrl: `https://www.moltbook.com/post/${postId}`,
|
|
276
276
|
contextText: String(n.comment?.content || n.content || n.preview || ""),
|
|
277
277
|
createdAt: n.created_at || n.createdAt || new Date().toISOString(),
|
|
278
278
|
status: "pending",
|
|
@@ -147,6 +147,7 @@ async function pollOnce() {
|
|
|
147
147
|
const notifications = extractNotifications(data);
|
|
148
148
|
const postCommentCache = new Map();
|
|
149
149
|
const postCache = new Map();
|
|
150
|
+
const postIdsToMarkRead = new Set();
|
|
150
151
|
for (const n of notifications) {
|
|
151
152
|
if (!isCommentNotification(n)) continue;
|
|
152
153
|
if (n.is_read === true || n.read === true || n.isRead === true) continue;
|
|
@@ -182,7 +183,7 @@ async function pollOnce() {
|
|
|
182
183
|
parentCommentId,
|
|
183
184
|
authorName,
|
|
184
185
|
postTitle,
|
|
185
|
-
postUrl: `https://www.moltbook.com/
|
|
186
|
+
postUrl: `https://www.moltbook.com/post/${postId}`,
|
|
186
187
|
contextText,
|
|
187
188
|
createdAt: createdAtIso,
|
|
188
189
|
status: "pending",
|
|
@@ -205,6 +206,8 @@ async function pollOnce() {
|
|
|
205
206
|
contextText,
|
|
206
207
|
draftReply: draftReply(),
|
|
207
208
|
callbackUrl: CALLBACK_URL,
|
|
209
|
+
postUrl: `https://www.moltbook.com/post/${postId}`,
|
|
210
|
+
postTitle,
|
|
208
211
|
createdAtMs: Date.parse(createdAtIso) || Date.now(),
|
|
209
212
|
});
|
|
210
213
|
console.log(`[moltbook-watcher] pushed ${sourceId}`);
|
|
@@ -212,6 +215,11 @@ async function pollOnce() {
|
|
|
212
215
|
console.error(`[moltbook-watcher] bridge push failed: ${error.message}`);
|
|
213
216
|
}
|
|
214
217
|
|
|
218
|
+
postIdsToMarkRead.add(postId);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Mark notifications as read after processing all comments in this cycle
|
|
222
|
+
for (const postId of postIdsToMarkRead) {
|
|
215
223
|
try {
|
|
216
224
|
await mb(`/notifications/read-by-post/${postId}`, { method: "POST" });
|
|
217
225
|
} catch (error) {
|
|
@@ -17,7 +17,7 @@ import { DEFAULT_LOCALE, SUPPORTED_LOCALES, localeDisplayName, normalizeLocale,
|
|
|
17
17
|
import { generatePairingCredentials, shouldRotatePairing, upsertEnvText } from "./lib/pairing.mjs";
|
|
18
18
|
import { renderMarkdownHtml } from "./lib/markdown-render.mjs";
|
|
19
19
|
import { buildAgentCard, handleA2ARequest, resolveA2ATaskDecision, completeA2ATask, failA2ATask } from "./a2a-handler.mjs";
|
|
20
|
-
import { registerWithRelay, startRelayPolling, stopRelayPolling, postRelayResult } from "./a2a-relay-client.mjs";
|
|
20
|
+
import { registerWithRelay, startRelayPolling, stopRelayPolling, postRelayResult, getRelayStatus } from "./a2a-relay-client.mjs";
|
|
21
21
|
|
|
22
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
23
23
|
const __dirname = path.dirname(__filename);
|
|
@@ -10529,6 +10529,7 @@ async function buildApiItemDetail({ config, runtime, state, kind, token, locale
|
|
|
10529
10529
|
summary: source.summary || "",
|
|
10530
10530
|
messageHtml: contextHtml,
|
|
10531
10531
|
postUrl: source.postUrl || "",
|
|
10532
|
+
postTitle: source.postTitle || "",
|
|
10532
10533
|
commentAuthor: source.commentAuthor || "",
|
|
10533
10534
|
provider: "moltbook",
|
|
10534
10535
|
draftReply: item?.draftReply || "",
|
|
@@ -11127,6 +11128,27 @@ function createNativeApprovalServer({ config, runtime, state }) {
|
|
|
11127
11128
|
}
|
|
11128
11129
|
}
|
|
11129
11130
|
|
|
11131
|
+
// A2A relay status for the settings UI.
|
|
11132
|
+
if (url.pathname === "/api/a2a/relay-status" && req.method === "GET") {
|
|
11133
|
+
const session = requireApiSession(req, res, config, state);
|
|
11134
|
+
if (!session) return;
|
|
11135
|
+
const relayEnabled = Boolean(config.a2aRelayUrl && config.a2aRelayUserId);
|
|
11136
|
+
if (!relayEnabled) {
|
|
11137
|
+
return writeJson(res, 200, { enabled: false });
|
|
11138
|
+
}
|
|
11139
|
+
const relay = getRelayStatus();
|
|
11140
|
+
return writeJson(res, 200, {
|
|
11141
|
+
enabled: true,
|
|
11142
|
+
connected: relay.polling && relay.lastPollOk,
|
|
11143
|
+
polling: relay.polling,
|
|
11144
|
+
lastPollOk: relay.lastPollOk,
|
|
11145
|
+
lastPollAtMs: relay.lastPollAtMs,
|
|
11146
|
+
userId: config.a2aRelayUserId,
|
|
11147
|
+
relayUrl: config.a2aRelayUrl,
|
|
11148
|
+
apiKeyConfigured: Boolean(config.a2aApiKey),
|
|
11149
|
+
});
|
|
11150
|
+
}
|
|
11151
|
+
|
|
11130
11152
|
// Activity summary for compose (original post) drafting.
|
|
11131
11153
|
// Supports ?slot=morning|noon|evening and ?date=YYYY-MM-DD for
|
|
11132
11154
|
// time-slot-based compose. Defaults to full-day today.
|
|
@@ -11552,6 +11574,7 @@ function createNativeApprovalServer({ config, runtime, state }) {
|
|
|
11552
11574
|
draftReply: cleanText(body.draftReply || ""),
|
|
11553
11575
|
callbackUrl: cleanText(body.callbackUrl || ""),
|
|
11554
11576
|
postUrl: cleanText(body.postUrl || ""),
|
|
11577
|
+
postTitle: cleanText(body.postTitle || ""),
|
|
11555
11578
|
commentAuthor: cleanText(body.commentAuthor || ""),
|
|
11556
11579
|
createdAtMs: Number(body.createdAtMs) || Date.now(),
|
|
11557
11580
|
resolved: false,
|
package/scripts/viveworker.mjs
CHANGED
|
@@ -82,6 +82,9 @@ async function main(cliOptions) {
|
|
|
82
82
|
case "doctor":
|
|
83
83
|
await runDoctor(cliOptions);
|
|
84
84
|
return;
|
|
85
|
+
case "update":
|
|
86
|
+
await runUpdate(cliOptions);
|
|
87
|
+
return;
|
|
85
88
|
case "help":
|
|
86
89
|
default:
|
|
87
90
|
printHelp();
|
|
@@ -846,6 +849,67 @@ async function runDoctor(cliOptions) {
|
|
|
846
849
|
}
|
|
847
850
|
}
|
|
848
851
|
|
|
852
|
+
async function runUpdate(cliOptions) {
|
|
853
|
+
const locale = await resolveCliLocale(cliOptions);
|
|
854
|
+
const progress = createCliProgressReporter(locale);
|
|
855
|
+
|
|
856
|
+
// 1. Check current vs latest version
|
|
857
|
+
progress.update("cli.update.progress.checkVersion");
|
|
858
|
+
const pkg = JSON.parse(await fs.readFile(path.join(packageRoot, "package.json"), "utf8"));
|
|
859
|
+
const currentVersion = pkg.version;
|
|
860
|
+
const { stdout: latestRaw } = await execCommand(["npm", "view", "viveworker", "version"], { ignoreError: true });
|
|
861
|
+
const latestVersion = (latestRaw || "").trim();
|
|
862
|
+
|
|
863
|
+
if (!latestVersion) {
|
|
864
|
+
progress.done("cli.update.checkFailed");
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (currentVersion === latestVersion) {
|
|
869
|
+
progress.done("cli.update.alreadyLatest", { version: currentVersion });
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
console.log(t(locale, "cli.update.versionInfo", { current: currentVersion, latest: latestVersion }));
|
|
874
|
+
|
|
875
|
+
// 2. Clear npx cache and re-fetch
|
|
876
|
+
progress.update("cli.update.progress.install");
|
|
877
|
+
const npxCacheResult = await execCommand(["npx", "--yes", "viveworker@latest", "--version"], { ignoreError: true });
|
|
878
|
+
if (!npxCacheResult.ok) {
|
|
879
|
+
// Fallback: try global install
|
|
880
|
+
await execCommand(["npm", "install", "-g", "viveworker@latest"], { ignoreError: true });
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// 3. Restart bridge
|
|
884
|
+
const launchAgentPath = resolvePath(cliOptions.launchAgentPath || defaultLaunchAgentPath);
|
|
885
|
+
if (await fileExists(launchAgentPath)) {
|
|
886
|
+
progress.update("cli.update.progress.restartBridge");
|
|
887
|
+
await execCommand(["launchctl", "bootout", `gui/${process.getuid()}`, launchAgentPath], { ignoreError: true });
|
|
888
|
+
await execCommand(["launchctl", "bootstrap", `gui/${process.getuid()}`, launchAgentPath], { ignoreError: true });
|
|
889
|
+
await execCommand(["launchctl", "kickstart", "-k", `gui/${process.getuid()}/${defaultLabel}`], { ignoreError: true });
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// 4. Restart moltbook-watcher if present
|
|
893
|
+
const watcherLabel = "com.viveworker.moltbook-watcher";
|
|
894
|
+
const watcherPlistPath = path.join(os.homedir(), "Library", "LaunchAgents", `${watcherLabel}.plist`);
|
|
895
|
+
if (await fileExists(watcherPlistPath)) {
|
|
896
|
+
progress.update("cli.update.progress.restartWatcher");
|
|
897
|
+
await execCommand(["launchctl", "bootout", `gui/${process.getuid()}`, watcherPlistPath], { ignoreError: true });
|
|
898
|
+
await execCommand(["launchctl", "bootstrap", `gui/${process.getuid()}`, watcherPlistPath], { ignoreError: true });
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// 5. Restart moltbook-scout if present
|
|
902
|
+
const scoutLabel = "com.viveworker.moltbook-scout";
|
|
903
|
+
const scoutPlistPath = path.join(os.homedir(), "Library", "LaunchAgents", `${scoutLabel}.plist`);
|
|
904
|
+
if (await fileExists(scoutPlistPath)) {
|
|
905
|
+
progress.update("cli.update.progress.restartScout");
|
|
906
|
+
await execCommand(["launchctl", "bootout", `gui/${process.getuid()}`, scoutPlistPath], { ignoreError: true });
|
|
907
|
+
await execCommand(["launchctl", "bootstrap", `gui/${process.getuid()}`, scoutPlistPath], { ignoreError: true });
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
progress.done("cli.update.done", { version: latestVersion });
|
|
911
|
+
}
|
|
912
|
+
|
|
849
913
|
function parseArgs(argv) {
|
|
850
914
|
const parsed = {
|
|
851
915
|
command: "help",
|
|
@@ -1019,6 +1083,7 @@ ${t(locale, "cli.help.commands")}
|
|
|
1019
1083
|
${t(locale, "cli.help.stop")}
|
|
1020
1084
|
${t(locale, "cli.help.status")}
|
|
1021
1085
|
${t(locale, "cli.help.doctor")}
|
|
1086
|
+
${t(locale, "cli.help.update")}
|
|
1022
1087
|
|
|
1023
1088
|
${t(locale, "cli.help.commonOptions")}
|
|
1024
1089
|
--port <n>
|
package/web/app.css
CHANGED
|
@@ -1124,6 +1124,9 @@ code {
|
|
|
1124
1124
|
top: calc(100% + 0.46rem);
|
|
1125
1125
|
z-index: 14;
|
|
1126
1126
|
width: min(15rem, calc(100vw - 2.25rem));
|
|
1127
|
+
max-height: 18rem;
|
|
1128
|
+
overflow-y: auto;
|
|
1129
|
+
-webkit-overflow-scrolling: touch;
|
|
1127
1130
|
display: grid;
|
|
1128
1131
|
gap: 0.22rem;
|
|
1129
1132
|
padding: 0.4rem;
|
|
@@ -1650,6 +1653,15 @@ code {
|
|
|
1650
1653
|
line-height: 1.15;
|
|
1651
1654
|
letter-spacing: -0.03em;
|
|
1652
1655
|
}
|
|
1656
|
+
.detail-title__link {
|
|
1657
|
+
color: inherit;
|
|
1658
|
+
text-decoration: none;
|
|
1659
|
+
}
|
|
1660
|
+
.detail-title__link:hover {
|
|
1661
|
+
text-decoration: underline;
|
|
1662
|
+
text-decoration-color: var(--accent, #00d4aa);
|
|
1663
|
+
text-underline-offset: 0.15em;
|
|
1664
|
+
}
|
|
1653
1665
|
|
|
1654
1666
|
.detail-body {
|
|
1655
1667
|
overflow-wrap: anywhere;
|
|
@@ -2246,6 +2258,9 @@ code {
|
|
|
2246
2258
|
gap: 0.78rem;
|
|
2247
2259
|
}
|
|
2248
2260
|
|
|
2261
|
+
.reply-composer--readonly {
|
|
2262
|
+
gap: 0.35rem;
|
|
2263
|
+
}
|
|
2249
2264
|
.reply-composer__copy {
|
|
2250
2265
|
display: grid;
|
|
2251
2266
|
gap: 0.34rem;
|
|
@@ -2280,10 +2295,16 @@ code {
|
|
|
2280
2295
|
color: var(--muted);
|
|
2281
2296
|
}
|
|
2282
2297
|
.reply-composer__context-body {
|
|
2283
|
-
margin-top: 0.
|
|
2298
|
+
margin-top: 0.25rem;
|
|
2284
2299
|
line-height: 1.55;
|
|
2285
2300
|
color: var(--text);
|
|
2286
2301
|
}
|
|
2302
|
+
.reply-composer__context-body > :first-child {
|
|
2303
|
+
margin-top: 0;
|
|
2304
|
+
}
|
|
2305
|
+
.reply-composer__context-body > :last-child {
|
|
2306
|
+
margin-bottom: 0;
|
|
2307
|
+
}
|
|
2287
2308
|
.reply-composer__intent {
|
|
2288
2309
|
margin: 0.5rem 0;
|
|
2289
2310
|
padding: 0.6rem 0.75rem;
|
|
@@ -2299,6 +2320,21 @@ code {
|
|
|
2299
2320
|
margin: 0.1rem 0 0.4rem;
|
|
2300
2321
|
font-size: 0.85rem;
|
|
2301
2322
|
}
|
|
2323
|
+
.reply-composer__author {
|
|
2324
|
+
margin: 0;
|
|
2325
|
+
}
|
|
2326
|
+
.reply-composer__post-title {
|
|
2327
|
+
margin: 0;
|
|
2328
|
+
font-size: 0.9rem;
|
|
2329
|
+
font-weight: 600;
|
|
2330
|
+
}
|
|
2331
|
+
.reply-composer__post-title a {
|
|
2332
|
+
color: var(--accent, #00d4aa);
|
|
2333
|
+
text-decoration: none;
|
|
2334
|
+
}
|
|
2335
|
+
.reply-composer__post-title a:hover {
|
|
2336
|
+
text-decoration: underline;
|
|
2337
|
+
}
|
|
2302
2338
|
.reply-composer__textarea {
|
|
2303
2339
|
width: 100%;
|
|
2304
2340
|
min-height: 14rem;
|
package/web/app.js
CHANGED
|
@@ -48,6 +48,7 @@ const state = {
|
|
|
48
48
|
pairNotice: "",
|
|
49
49
|
pushStatus: null,
|
|
50
50
|
moltbookScoutStatus: null,
|
|
51
|
+
a2aRelayStatus: null,
|
|
51
52
|
pushNotice: "",
|
|
52
53
|
pushError: "",
|
|
53
54
|
deviceNotice: "",
|
|
@@ -214,6 +215,7 @@ async function refreshAuthenticatedState() {
|
|
|
214
215
|
await refreshDevices();
|
|
215
216
|
await refreshPushStatus();
|
|
216
217
|
await fetchMoltbookScoutStatus();
|
|
218
|
+
await fetchA2aRelayStatus();
|
|
217
219
|
ensureCurrentSelection();
|
|
218
220
|
}
|
|
219
221
|
|
|
@@ -331,6 +333,18 @@ async function fetchMoltbookScoutStatus() {
|
|
|
331
333
|
}
|
|
332
334
|
}
|
|
333
335
|
|
|
336
|
+
async function fetchA2aRelayStatus() {
|
|
337
|
+
if (!state.session?.a2aRelayEnabled) {
|
|
338
|
+
state.a2aRelayStatus = null;
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
state.a2aRelayStatus = await apiGet("/api/a2a/relay-status");
|
|
343
|
+
} catch {
|
|
344
|
+
state.a2aRelayStatus = null;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
334
348
|
async function getClientPushState() {
|
|
335
349
|
const registration = state.serviceWorkerRegistration || (await navigator.serviceWorker?.ready.catch(() => null));
|
|
336
350
|
if (registration) {
|
|
@@ -624,6 +638,8 @@ function timelineKindFilterOptions() {
|
|
|
624
638
|
{ id: "plans", label: L("timeline.kindFilter.plans"), icon: "plan" },
|
|
625
639
|
{ id: "choices", label: L("timeline.kindFilter.choices"), icon: "choice" },
|
|
626
640
|
{ id: "completions", label: L("timeline.kindFilter.completions"), icon: "completion-item" },
|
|
641
|
+
{ id: "moltbook_drafts", label: L("timeline.kindFilter.moltbookDrafts"), icon: "moltbook-draft" },
|
|
642
|
+
{ id: "moltbook_comments", label: L("timeline.kindFilter.moltbookComments"), icon: "moltbook-comment" },
|
|
627
643
|
];
|
|
628
644
|
}
|
|
629
645
|
|
|
@@ -649,6 +665,10 @@ function timelineEntryMatchesKindFilter(entry, filterId) {
|
|
|
649
665
|
return kind === "choice";
|
|
650
666
|
case "completions":
|
|
651
667
|
return kind === "completion";
|
|
668
|
+
case "moltbook_drafts":
|
|
669
|
+
return kind === "moltbook_draft";
|
|
670
|
+
case "moltbook_comments":
|
|
671
|
+
return kind === "moltbook_reply";
|
|
652
672
|
default:
|
|
653
673
|
return true;
|
|
654
674
|
}
|
|
@@ -991,6 +1011,9 @@ function shouldDeferRenderForActiveInteraction() {
|
|
|
991
1011
|
) {
|
|
992
1012
|
return true;
|
|
993
1013
|
}
|
|
1014
|
+
if (state.timelineKindFilterOpen) {
|
|
1015
|
+
return true;
|
|
1016
|
+
}
|
|
994
1017
|
return state.threadFilterInteractionUntilMs > Date.now();
|
|
995
1018
|
}
|
|
996
1019
|
|
|
@@ -2745,6 +2768,7 @@ function buildSettingsContext() {
|
|
|
2745
2768
|
serverEnabled,
|
|
2746
2769
|
}),
|
|
2747
2770
|
moltbookScout: state.moltbookScoutStatus,
|
|
2771
|
+
a2aRelay: state.a2aRelayStatus,
|
|
2748
2772
|
};
|
|
2749
2773
|
}
|
|
2750
2774
|
|
|
@@ -2898,6 +2922,13 @@ function settingsPageMeta(page) {
|
|
|
2898
2922
|
description: L("settings.moltbook.copy"),
|
|
2899
2923
|
icon: "item",
|
|
2900
2924
|
};
|
|
2925
|
+
case "a2aRelay":
|
|
2926
|
+
return {
|
|
2927
|
+
id: "a2aRelay",
|
|
2928
|
+
title: L("settings.a2aRelay.title"),
|
|
2929
|
+
description: L("settings.a2aRelay.copy"),
|
|
2930
|
+
icon: "link",
|
|
2931
|
+
};
|
|
2901
2932
|
default:
|
|
2902
2933
|
return settingsPageMeta("notifications");
|
|
2903
2934
|
}
|
|
@@ -2967,15 +2998,22 @@ function renderSettingsRoot(context, { mobile }) {
|
|
|
2967
2998
|
`
|
|
2968
2999
|
}
|
|
2969
3000
|
${renderSettingsGroup(L("settings.group.general"), generalRows)}
|
|
2970
|
-
${state.session?.moltbookEnabled ? renderSettingsGroup(L("
|
|
2971
|
-
renderSettingsNavRow({
|
|
3001
|
+
${(state.session?.moltbookEnabled || state.session?.a2aRelayEnabled) ? renderSettingsGroup(L("settings.group.integrations"), [
|
|
3002
|
+
state.session?.moltbookEnabled ? renderSettingsNavRow({
|
|
2972
3003
|
page: "moltbook",
|
|
2973
3004
|
icon: "item",
|
|
2974
3005
|
title: L("settings.moltbook.title"),
|
|
2975
3006
|
subtitle: L("settings.moltbook.subtitle"),
|
|
2976
3007
|
value: context.moltbookScout?.enabled ? `${context.moltbookScout.sentToday} / ${context.moltbookScout.maxDaily}` : "",
|
|
2977
|
-
}),
|
|
2978
|
-
|
|
3008
|
+
}) : "",
|
|
3009
|
+
state.session?.a2aRelayEnabled ? renderSettingsNavRow({
|
|
3010
|
+
page: "a2aRelay",
|
|
3011
|
+
icon: "link",
|
|
3012
|
+
title: L("settings.a2aRelay.title"),
|
|
3013
|
+
subtitle: L("settings.a2aRelay.subtitle"),
|
|
3014
|
+
value: context.a2aRelay?.connected ? L("settings.status.connected") : L("settings.a2aRelay.status.disconnected"),
|
|
3015
|
+
}) : "",
|
|
3016
|
+
].filter(Boolean)) : ""}
|
|
2979
3017
|
${renderSettingsGroup(L("settings.pairing.title"), deviceRows)}
|
|
2980
3018
|
${renderSettingsGroup(L("settings.group.advanced"), advancedRows)}
|
|
2981
3019
|
</div>
|
|
@@ -3022,6 +3060,9 @@ function renderSettingsSubpage(context, { mobile }) {
|
|
|
3022
3060
|
case "moltbook":
|
|
3023
3061
|
content = renderSettingsMoltbookPage(context);
|
|
3024
3062
|
break;
|
|
3063
|
+
case "a2aRelay":
|
|
3064
|
+
content = renderSettingsA2aRelayPage(context);
|
|
3065
|
+
break;
|
|
3025
3066
|
default:
|
|
3026
3067
|
content = "";
|
|
3027
3068
|
}
|
|
@@ -3273,6 +3314,38 @@ function renderSettingsMoltbookPage(context) {
|
|
|
3273
3314
|
`;
|
|
3274
3315
|
}
|
|
3275
3316
|
|
|
3317
|
+
function renderSettingsA2aRelayPage(context) {
|
|
3318
|
+
const relay = context.a2aRelay;
|
|
3319
|
+
if (!relay?.enabled) {
|
|
3320
|
+
return `
|
|
3321
|
+
<div class="settings-page">
|
|
3322
|
+
<p class="settings-page-copy muted">${escapeHtml(L("settings.a2aRelay.unavailable"))}</p>
|
|
3323
|
+
</div>
|
|
3324
|
+
`;
|
|
3325
|
+
}
|
|
3326
|
+
const statusLabel = relay.connected
|
|
3327
|
+
? L("settings.status.connected")
|
|
3328
|
+
: relay.polling
|
|
3329
|
+
? L("settings.a2aRelay.status.polling")
|
|
3330
|
+
: L("settings.a2aRelay.status.disconnected");
|
|
3331
|
+
const profileUrl = `${relay.relayUrl}/${relay.userId}`;
|
|
3332
|
+
const userIdLink = `<a href="${escapeHtml(profileUrl)}" target="_blank" rel="noopener">${escapeHtml(relay.userId)}</a>`;
|
|
3333
|
+
const relayHost = (() => { try { return new URL(relay.relayUrl).host; } catch { return relay.relayUrl; } })();
|
|
3334
|
+
return `
|
|
3335
|
+
<div class="settings-page">
|
|
3336
|
+
${renderSettingsGroup("", [
|
|
3337
|
+
renderSettingsInfoRow(L("settings.row.a2aStatus"), statusLabel),
|
|
3338
|
+
renderSettingsInfoRow(L("settings.row.a2aUserId"), userIdLink, { rawValue: true }),
|
|
3339
|
+
renderSettingsInfoRow(L("settings.row.a2aRelay"), relayHost),
|
|
3340
|
+
renderSettingsInfoRow(L("settings.row.a2aApiKey"), relay.apiKeyConfigured ? L("settings.a2aRelay.apiKey.configured") : L("settings.a2aRelay.apiKey.notConfigured")),
|
|
3341
|
+
relay.lastPollAtMs
|
|
3342
|
+
? renderSettingsInfoRow(L("settings.row.a2aLastPoll"), new Date(relay.lastPollAtMs).toLocaleString(state.locale))
|
|
3343
|
+
: "",
|
|
3344
|
+
].filter(Boolean))}
|
|
3345
|
+
</div>
|
|
3346
|
+
`;
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3276
3349
|
function renderSettingsInfoRow(label, value, options = {}) {
|
|
3277
3350
|
const rowClassName = ["settings-info-row", options.rowClassName || ""].filter(Boolean).join(" ");
|
|
3278
3351
|
const valueClassName = ["settings-info-row__value", options.valueClassName || ""].filter(Boolean).join(" ");
|
|
@@ -3385,7 +3458,7 @@ function renderStandardDetailDesktop(detail) {
|
|
|
3385
3458
|
return `
|
|
3386
3459
|
<div class="detail-shell">
|
|
3387
3460
|
${renderDetailMetaRow(detail, kindInfo)}
|
|
3388
|
-
<h2 class="detail-title detail-title--desktop">${
|
|
3461
|
+
<h2 class="detail-title detail-title--desktop">${renderDetailTitle(detail)}</h2>
|
|
3389
3462
|
${detail.readOnly || detail.kind === "approval" || detail.kind === "moltbook_draft" || detail.kind === "moltbook_reply" ? "" : renderDetailLead(detail, kindInfo)}
|
|
3390
3463
|
${renderPreviousContextCard(detail)}
|
|
3391
3464
|
${renderInterruptedDetailNotice(detail)}
|
|
@@ -3893,9 +3966,12 @@ function renderMoltbookReplyComposer(detail, options = {}) {
|
|
|
3893
3966
|
const rawTitle = normalizeClientText(detail.title || "");
|
|
3894
3967
|
const match = rawTitle.match(/^@([^\s]+)/u);
|
|
3895
3968
|
const authorHandle = match ? match[1] : detail.commentAuthor || "";
|
|
3896
|
-
const postUrl = detail.postUrl || "";
|
|
3897
|
-
const
|
|
3898
|
-
|
|
3969
|
+
const postUrl = detail.postUrl || (detail.threadId ? `https://www.moltbook.com/post/${detail.threadId}` : "");
|
|
3970
|
+
const postTitle = normalizeClientText(detail.postTitle || detail.threadLabel || "").replace(/^Moltbook\s*·\s*/iu, "");
|
|
3971
|
+
const postLink = postTitle
|
|
3972
|
+
? (postUrl
|
|
3973
|
+
? `<p class="reply-composer__post-title"><a href="${escapeHtml(postUrl)}" target="_blank" rel="noopener">${escapeHtml(postTitle)}</a></p>`
|
|
3974
|
+
: `<p class="reply-composer__post-title">${escapeHtml(postTitle)}</p>`)
|
|
3899
3975
|
: "";
|
|
3900
3976
|
const bodyHtml = detail.messageHtml
|
|
3901
3977
|
? `<div class="reply-composer__context-body markdown">${detail.messageHtml}</div>`
|
|
@@ -3922,8 +3998,12 @@ function renderMoltbookDraftComposer(detail, options = {}) {
|
|
|
3922
3998
|
const postAuthorLine = !isOriginalPost && detail.postAuthor
|
|
3923
3999
|
? `<p class="reply-composer__author-meta muted">@${escapeHtml(detail.postAuthor)}</p>`
|
|
3924
4000
|
: "";
|
|
3925
|
-
const
|
|
3926
|
-
|
|
4001
|
+
const draftPostUrl = detail.postUrl || (detail.threadId ? `https://www.moltbook.com/post/${detail.threadId}` : "");
|
|
4002
|
+
const draftPostTitle = normalizeClientText(detail.postTitle || detail.threadLabel || "").replace(/^Moltbook\s*·\s*/iu, "");
|
|
4003
|
+
const postLink = !isOriginalPost && draftPostTitle
|
|
4004
|
+
? (draftPostUrl
|
|
4005
|
+
? `<p class="reply-composer__post-title"><a href="${escapeHtml(draftPostUrl)}" target="_blank" rel="noopener">${escapeHtml(draftPostTitle)}</a></p>`
|
|
4006
|
+
: `<p class="reply-composer__post-title">${escapeHtml(draftPostTitle)}</p>`)
|
|
3927
4007
|
: "";
|
|
3928
4008
|
const postBodyBlock = !isOriginalPost && detail.postBody
|
|
3929
4009
|
? `<details class="reply-composer__context"><summary>元の投稿</summary><div class="reply-composer__context-body">${escapeHtml(detail.postBody).replace(/\n/g, "<br>")}</div></details>`
|
|
@@ -5769,6 +5849,14 @@ function detailIntentText(detail) {
|
|
|
5769
5849
|
return itemIntentText(detail.kind, "pending", provider);
|
|
5770
5850
|
}
|
|
5771
5851
|
|
|
5852
|
+
function renderDetailTitle(detail) {
|
|
5853
|
+
const title = escapeHtml(detailDisplayTitle(detail));
|
|
5854
|
+
if ((detail.kind === "moltbook_reply" || detail.kind === "moltbook_draft") && detail.postUrl) {
|
|
5855
|
+
return `<a href="${escapeHtml(detail.postUrl)}" target="_blank" rel="noopener" class="detail-title__link">${title}</a>`;
|
|
5856
|
+
}
|
|
5857
|
+
return title;
|
|
5858
|
+
}
|
|
5859
|
+
|
|
5772
5860
|
function detailDisplayTitle(detail) {
|
|
5773
5861
|
const threadLabel = normalizeClientText(detail?.threadLabel || "");
|
|
5774
5862
|
if (threadLabel) {
|
|
@@ -6050,6 +6138,10 @@ function renderIcon(name) {
|
|
|
6050
6138
|
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M10.4 13.6 8.3 15.7a3 3 0 0 1-4.2-4.2l2.8-2.8a3 3 0 0 1 4.2 0"/><path d="m13.6 10.4 2.1-2.1a3 3 0 1 1 4.2 4.2l-2.8 2.8a3 3 0 0 1-4.2 0"/><path d="m9.5 14.5 5-5"/></svg>`;
|
|
6051
6139
|
case "clip":
|
|
6052
6140
|
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="m9.5 12.5 5.9-5.9a3 3 0 1 1 4.2 4.2l-7.7 7.7a5 5 0 1 1-7.1-7.1l8.1-8.1"/></svg>`;
|
|
6141
|
+
case "moltbook-draft":
|
|
6142
|
+
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M15.2 3.8 20.2 8.8 8.5 20.5 3.5 20.5 3.5 15.5Z"/><path d="M12.5 6.5l5 5"/></svg>`;
|
|
6143
|
+
case "moltbook-comment":
|
|
6144
|
+
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 5.5h15a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H11l-4 3.5v-3.5H4.5a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2Z"/><path d="M8 10h8"/><path d="M8 13h5"/></svg>`;
|
|
6053
6145
|
case "filter":
|
|
6054
6146
|
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M5 7h14"/><path d="M8 12h8"/><path d="M10.5 17h3"/></svg>`;
|
|
6055
6147
|
case "check":
|
package/web/i18n.js
CHANGED
|
@@ -222,6 +222,8 @@ const translations = {
|
|
|
222
222
|
"timeline.kindFilter.plans": "Plans",
|
|
223
223
|
"timeline.kindFilter.choices": "Choices",
|
|
224
224
|
"timeline.kindFilter.completions": "Completed",
|
|
225
|
+
"timeline.kindFilter.moltbookDrafts": "Drafts",
|
|
226
|
+
"timeline.kindFilter.moltbookComments": "Comments",
|
|
225
227
|
"fileEvent.read": "Checked files",
|
|
226
228
|
"fileEvent.write": "Updated files",
|
|
227
229
|
"fileEvent.create": "Created files",
|
|
@@ -371,6 +373,20 @@ const translations = {
|
|
|
371
373
|
"moltbook.draft.greetNoon": "Nice progress! Ready to share your morning work?",
|
|
372
374
|
"moltbook.draft.greetEvening": "Great work today! Let's share what you built.",
|
|
373
375
|
"moltbook.draft.greetFallback": "New post ready for review",
|
|
376
|
+
"settings.group.integrations": "Integrations",
|
|
377
|
+
"settings.a2aRelay.title": "A2A Relay",
|
|
378
|
+
"settings.a2aRelay.subtitle": "Agent-to-Agent relay connection",
|
|
379
|
+
"settings.a2aRelay.copy": "View your A2A relay connection status and configuration.",
|
|
380
|
+
"settings.a2aRelay.unavailable": "A2A Relay is not configured.",
|
|
381
|
+
"settings.a2aRelay.status.disconnected": "Disconnected",
|
|
382
|
+
"settings.a2aRelay.status.polling": "Connecting...",
|
|
383
|
+
"settings.a2aRelay.apiKey.configured": "Configured",
|
|
384
|
+
"settings.a2aRelay.apiKey.notConfigured": "Not configured",
|
|
385
|
+
"settings.row.a2aStatus": "Status",
|
|
386
|
+
"settings.row.a2aUserId": "User ID",
|
|
387
|
+
"settings.row.a2aRelay": "Relay",
|
|
388
|
+
"settings.row.a2aApiKey": "API Key",
|
|
389
|
+
"settings.row.a2aLastPoll": "Last poll",
|
|
374
390
|
"a2a.task.eyebrow": "A2A Task Request",
|
|
375
391
|
"a2a.task.from": "From",
|
|
376
392
|
"a2a.task.instruction": "Task",
|
|
@@ -483,13 +499,23 @@ const translations = {
|
|
|
483
499
|
"server.page.choiceMissing": "Choice input token not found",
|
|
484
500
|
"server.page.approvalHandled": "Approval already handled",
|
|
485
501
|
"server.page.detailMissing": "Completion detail not found",
|
|
486
|
-
"cli.help.usage": "Usage: viveworker <setup|start|stop|status|doctor> [options]",
|
|
502
|
+
"cli.help.usage": "Usage: viveworker <setup|start|stop|status|doctor|update> [options]",
|
|
487
503
|
"cli.help.commands": "Commands:",
|
|
488
504
|
"cli.help.setup": "setup Create config, generate pairing info, register launchd, start the bridge",
|
|
489
505
|
"cli.help.start": "start Start the bridge using the saved config",
|
|
490
506
|
"cli.help.stop": "stop Stop the bridge",
|
|
491
507
|
"cli.help.status": "status Print launchd/background status and health",
|
|
492
508
|
"cli.help.doctor": "doctor Diagnose the local setup",
|
|
509
|
+
"cli.help.update": "update Update to the latest version and restart all services",
|
|
510
|
+
"cli.update.progress.checkVersion": "Checking for updates...",
|
|
511
|
+
"cli.update.progress.install": "Installing latest version...",
|
|
512
|
+
"cli.update.progress.restartBridge": "Restarting bridge...",
|
|
513
|
+
"cli.update.progress.restartWatcher": "Restarting Moltbook watcher...",
|
|
514
|
+
"cli.update.progress.restartScout": "Restarting Moltbook scout...",
|
|
515
|
+
"cli.update.checkFailed": "Could not check the latest version from npm",
|
|
516
|
+
"cli.update.alreadyLatest": "Already up to date (v{version})",
|
|
517
|
+
"cli.update.versionInfo": "Updating v{current} -> v{latest}",
|
|
518
|
+
"cli.update.done": "Updated to v{version} — all services restarted",
|
|
493
519
|
"cli.help.commonOptions": "Common options:",
|
|
494
520
|
"cli.setup.complete": "viveworker setup complete",
|
|
495
521
|
"cli.setup.progress.prepare": "Preparing local viveworker setup...",
|
|
@@ -809,6 +835,8 @@ const translations = {
|
|
|
809
835
|
"timeline.kindFilter.plans": "プラン",
|
|
810
836
|
"timeline.kindFilter.choices": "選択",
|
|
811
837
|
"timeline.kindFilter.completions": "完了",
|
|
838
|
+
"timeline.kindFilter.moltbookDrafts": "ドラフト",
|
|
839
|
+
"timeline.kindFilter.moltbookComments": "コメント",
|
|
812
840
|
"fileEvent.read": "ファイルを確認",
|
|
813
841
|
"fileEvent.write": "ファイルを更新",
|
|
814
842
|
"fileEvent.create": "ファイルを作成",
|
|
@@ -958,6 +986,20 @@ const translations = {
|
|
|
958
986
|
"moltbook.draft.greetNoon": "午前中お疲れ様でした!進捗をシェアしませんか?",
|
|
959
987
|
"moltbook.draft.greetEvening": "今日もお疲れ様でした!成果をシェアしませんか?",
|
|
960
988
|
"moltbook.draft.greetFallback": "投稿ドラフトができました",
|
|
989
|
+
"settings.group.integrations": "連携サービス",
|
|
990
|
+
"settings.a2aRelay.title": "A2A Relay",
|
|
991
|
+
"settings.a2aRelay.subtitle": "Agent-to-Agent リレー接続",
|
|
992
|
+
"settings.a2aRelay.copy": "A2A リレーの接続状態と設定を確認できます。",
|
|
993
|
+
"settings.a2aRelay.unavailable": "A2A Relay が設定されていません。",
|
|
994
|
+
"settings.a2aRelay.status.disconnected": "切断",
|
|
995
|
+
"settings.a2aRelay.status.polling": "接続中...",
|
|
996
|
+
"settings.a2aRelay.apiKey.configured": "設定済み",
|
|
997
|
+
"settings.a2aRelay.apiKey.notConfigured": "未設定",
|
|
998
|
+
"settings.row.a2aStatus": "ステータス",
|
|
999
|
+
"settings.row.a2aUserId": "ユーザー ID",
|
|
1000
|
+
"settings.row.a2aRelay": "リレー",
|
|
1001
|
+
"settings.row.a2aApiKey": "API キー",
|
|
1002
|
+
"settings.row.a2aLastPoll": "最終ポーリング",
|
|
961
1003
|
"a2a.task.eyebrow": "A2A タスクリクエスト",
|
|
962
1004
|
"a2a.task.from": "送信元",
|
|
963
1005
|
"a2a.task.instruction": "タスク内容",
|
|
@@ -1070,13 +1112,23 @@ const translations = {
|
|
|
1070
1112
|
"server.page.choiceMissing": "選択入力トークンが見つかりません",
|
|
1071
1113
|
"server.page.approvalHandled": "この承認はすでに処理済みです",
|
|
1072
1114
|
"server.page.detailMissing": "完了詳細が見つかりません",
|
|
1073
|
-
"cli.help.usage": "Usage: viveworker <setup|start|stop|status|doctor> [options]",
|
|
1115
|
+
"cli.help.usage": "Usage: viveworker <setup|start|stop|status|doctor|update> [options]",
|
|
1074
1116
|
"cli.help.commands": "Commands:",
|
|
1075
1117
|
"cli.help.setup": "setup 設定を作成し、ペアリング情報を生成し、launchd に登録して bridge を起動します",
|
|
1076
1118
|
"cli.help.start": "start 保存済み設定で bridge を起動します",
|
|
1077
1119
|
"cli.help.stop": "stop bridge を停止します",
|
|
1078
1120
|
"cli.help.status": "status launchd / バックグラウンド状態と health を表示します",
|
|
1079
1121
|
"cli.help.doctor": "doctor ローカル設定を診断します",
|
|
1122
|
+
"cli.help.update": "update 最新バージョンに更新し、全サービスを再起動します",
|
|
1123
|
+
"cli.update.progress.checkVersion": "更新を確認しています...",
|
|
1124
|
+
"cli.update.progress.install": "最新バージョンをインストールしています...",
|
|
1125
|
+
"cli.update.progress.restartBridge": "bridge を再起動しています...",
|
|
1126
|
+
"cli.update.progress.restartWatcher": "Moltbook watcher を再起動しています...",
|
|
1127
|
+
"cli.update.progress.restartScout": "Moltbook scout を再起動しています...",
|
|
1128
|
+
"cli.update.checkFailed": "npm から最新バージョンを確認できませんでした",
|
|
1129
|
+
"cli.update.alreadyLatest": "すでに最新です (v{version})",
|
|
1130
|
+
"cli.update.versionInfo": "v{current} -> v{latest} に更新します",
|
|
1131
|
+
"cli.update.done": "v{version} に更新しました — 全サービスを再起動しました",
|
|
1080
1132
|
"cli.help.commonOptions": "共通オプション:",
|
|
1081
1133
|
"cli.setup.complete": "viveworker の setup が完了しました",
|
|
1082
1134
|
"cli.setup.progress.prepare": "viveworker のローカル設定を準備しています...",
|