viveworker 0.1.8 → 0.1.10
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 +13 -13
- package/ntfy/server.yml.example +2 -2
- package/package.json +4 -3
- package/scripts/viveworker-bridge.mjs +122 -13
- package/scripts/viveworker.mjs +18 -4
- package/viveworker.env.example +3 -3
- package/web/app.css +1 -0
- package/web/app.js +3 -0
- package/web/i18n.js +89 -75
package/README.md
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
[](https://badge.fury.io/js/viveworker)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
`viveworker` brings Codex Desktop to your
|
|
8
|
+
`viveworker` brings Codex Desktop to your phone.
|
|
9
9
|
|
|
10
10
|
When Codex 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
|
|
|
12
12
|
Think of it as a local companion for Codex on your Mac:
|
|
13
|
-
your Mac keeps building, and your
|
|
13
|
+
your Mac keeps building, and your device keeps you in the loop.
|
|
14
14
|
|
|
15
15
|
## Why It Feels Good
|
|
16
16
|
|
|
@@ -29,13 +29,13 @@ keep Codex moving, keep context close, and keep your momentum.
|
|
|
29
29
|
|
|
30
30
|
`viveworker` works best with:
|
|
31
31
|
|
|
32
|
-
- Mac +
|
|
32
|
+
- Mac + mobile device
|
|
33
33
|
- the same Wi-Fi or LAN
|
|
34
34
|
- a trusted local network
|
|
35
35
|
- the Home Screen web app with Web Push enabled
|
|
36
36
|
|
|
37
37
|
It gets even more fun with a Mac mini.
|
|
38
|
-
Leave Codex 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
|
|
38
|
+
Leave Codex 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
40
|
`viveworker` is designed for local use only.
|
|
41
41
|
It is not intended for Internet exposure.
|
|
@@ -48,7 +48,7 @@ You can use it as:
|
|
|
48
48
|
|
|
49
49
|
- an always-on Codex station that stays running in the background
|
|
50
50
|
- a way to keep approvals and plan checks moving even when you are away from your desk
|
|
51
|
-
- a lightweight monitor for long-running coding or research tasks, where your
|
|
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
53
|
- a quick way to review a completion and send “do this next” back into the latest thread from your phone
|
|
54
54
|
|
|
@@ -79,10 +79,10 @@ npx viveworker setup --port 8820
|
|
|
79
79
|
|
|
80
80
|
1. Run `npx viveworker setup --install-mkcert` on your Mac
|
|
81
81
|
2. If macOS asks, allow the local CA install
|
|
82
|
-
3. On your
|
|
83
|
-
4.
|
|
84
|
-
5. Open the printed pairing URL
|
|
85
|
-
6. Pair your
|
|
82
|
+
3. On your device, open the printed `rootCA.pem` URL
|
|
83
|
+
4. If your device requires local CA trust, install the certificate profile and trust it
|
|
84
|
+
5. Open the printed pairing URL on your device
|
|
85
|
+
6. Pair your device with the code if needed
|
|
86
86
|
7. Add `viveworker` to your Home Screen
|
|
87
87
|
8. Open the Home Screen app
|
|
88
88
|
9. In `Settings`, tap `Enable Notifications`
|
|
@@ -128,7 +128,7 @@ Useful options:
|
|
|
128
128
|
|
|
129
129
|
`--pair` reissues only the short-lived pairing code and pairing URL.
|
|
130
130
|
It does not change the main app URL, port, session secret, TLS, or Web Push settings.
|
|
131
|
-
Use it only when you want to add another trusted
|
|
131
|
+
Use it only when you want to add another trusted device or browser.
|
|
132
132
|
|
|
133
133
|
## Questions and Limits
|
|
134
134
|
|
|
@@ -156,7 +156,7 @@ If you later want a second wake-up notification path, you can add `ntfy` alongsi
|
|
|
156
156
|
|
|
157
157
|
- If the `.local` URL does not open, use the printed IP-based URL
|
|
158
158
|
- If pairing has expired, run `npx viveworker setup --pair`
|
|
159
|
-
- If notifications do not appear, make sure you opened the Home Screen app, not just a
|
|
159
|
+
- If notifications do not appear, make sure you opened the Home Screen app, not just a browser tab
|
|
160
160
|
- If Web Push is enabled, make sure you are opening the HTTPS URL
|
|
161
161
|
- If you are stuck, run:
|
|
162
162
|
|
|
@@ -171,13 +171,13 @@ npx viveworker doctor
|
|
|
171
171
|
- Web Push still depends on the browser/platform push service
|
|
172
172
|
- `--install-mkcert` can automate the Mac-side `mkcert` install and `mkcert -install`
|
|
173
173
|
- macOS may still show an administrator prompt while installing the local CA
|
|
174
|
-
-
|
|
174
|
+
- On some devices, local CA trust is still manual before HTTPS works reliably
|
|
175
175
|
- Web Push supports approvals, plans, multiple-choice questions, and completions
|
|
176
176
|
|
|
177
177
|
## Roadmap
|
|
178
178
|
|
|
179
179
|
Planned next steps include:
|
|
180
180
|
|
|
181
|
-
- Android support
|
|
182
181
|
- Windows support
|
|
182
|
+
- ✅ ~~Android support~~ (Apr 1, 2026)
|
|
183
183
|
- ✅ ~~image attachment support from mobile~~ (Mar 26, 2026)
|
package/ntfy/server.yml.example
CHANGED
|
@@ -8,7 +8,7 @@ base-url: "http://YOUR-MAC-IP-OR-HOSTNAME:8080"
|
|
|
8
8
|
# The container listens on port 80 internally; Docker maps 8080 -> 80.
|
|
9
9
|
listen-http: ":80"
|
|
10
10
|
|
|
11
|
-
# Persistent cache is required so
|
|
11
|
+
# Persistent cache is required so a paired device can fetch message contents after a poll request.
|
|
12
12
|
cache-file: "/var/lib/ntfy/cache.db"
|
|
13
13
|
|
|
14
14
|
# User, ACL, and token database. Created automatically if it does not exist.
|
|
@@ -19,7 +19,7 @@ auth-default-access: "deny-all"
|
|
|
19
19
|
enable-login: true
|
|
20
20
|
enable-signup: false
|
|
21
21
|
|
|
22
|
-
# Needed for near-instant
|
|
22
|
+
# Needed for near-instant mobile delivery on a self-hosted server.
|
|
23
23
|
# Only a poll request is forwarded upstream, not your real message body.
|
|
24
24
|
upstream-base-url: "https://ntfy.sh"
|
|
25
25
|
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "viveworker",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Local
|
|
3
|
+
"version": "0.1.10",
|
|
4
|
+
"description": "Local device companion for Codex Desktop approvals, plan checks, questions, and notifications on your LAN.",
|
|
5
5
|
"author": "Yuta Hoshino <hoshino.lireneo@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"codex",
|
|
9
9
|
"codex-desktop",
|
|
10
10
|
"macos",
|
|
11
|
-
"
|
|
11
|
+
"mobile",
|
|
12
12
|
"ios",
|
|
13
|
+
"android",
|
|
13
14
|
"pwa",
|
|
14
15
|
"web-push",
|
|
15
16
|
"notifications",
|
|
@@ -85,8 +85,16 @@ const state = await loadState(config.stateFile);
|
|
|
85
85
|
const migratedPairedDevicesStateChanged = migratePairedDevicesState({ config, state });
|
|
86
86
|
const restoredPendingPlanStateChanged = restorePendingPlanRequests({ config, runtime, state });
|
|
87
87
|
const restoredPendingUserInputStateChanged = restorePendingUserInputRequests({ config, runtime, state });
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
const initialHistoryItems = normalizeHistoryItems(state.recentHistoryItems ?? [], config.maxHistoryItems);
|
|
89
|
+
const initialTimelineEntries = normalizeTimelineEntries(state.recentTimelineEntries ?? [], config.maxTimelineEntries);
|
|
90
|
+
const normalizedHistoryStateChanged =
|
|
91
|
+
JSON.stringify(initialHistoryItems) !== JSON.stringify(Array.isArray(state.recentHistoryItems) ? state.recentHistoryItems : []);
|
|
92
|
+
const normalizedTimelineStateChanged =
|
|
93
|
+
JSON.stringify(initialTimelineEntries) !== JSON.stringify(Array.isArray(state.recentTimelineEntries) ? state.recentTimelineEntries : []);
|
|
94
|
+
runtime.recentHistoryItems = initialHistoryItems;
|
|
95
|
+
runtime.recentTimelineEntries = initialTimelineEntries;
|
|
96
|
+
state.recentHistoryItems = initialHistoryItems;
|
|
97
|
+
state.recentTimelineEntries = initialTimelineEntries;
|
|
90
98
|
const migratedRecentCodeEventsStateChanged = migrateRecentCodeEventsState({ config, runtime, state });
|
|
91
99
|
const restoredTimelineImagePathsStateChanged = await backfillPersistedTimelineImagePaths({ config, runtime, state });
|
|
92
100
|
runtime.historyFileState.offset = Number(state.historyFileOffset) || 0;
|
|
@@ -1791,7 +1799,11 @@ function normalizeHistoryItem(raw) {
|
|
|
1791
1799
|
|
|
1792
1800
|
const stableId = cleanText(raw.stableId ?? raw.id ?? "");
|
|
1793
1801
|
const kind = cleanText(raw.kind ?? "");
|
|
1794
|
-
const
|
|
1802
|
+
const threadId = cleanText(raw.threadId ?? extractConversationIdFromStableId(stableId) ?? "");
|
|
1803
|
+
const rawThreadLabel = cleanText(raw.threadLabel ?? "");
|
|
1804
|
+
const threadLabel = preferTitleOnlyJsonThreadLabel(rawThreadLabel, threadId, raw.messageText, raw.summary, raw.detailText, raw.message);
|
|
1805
|
+
const title =
|
|
1806
|
+
cleanText(raw.title ?? "") || (threadLabel ? formatTitle(kindTitle(DEFAULT_LOCALE, kind), threadLabel) : kindTitle(DEFAULT_LOCALE, kind));
|
|
1795
1807
|
const messageText = normalizeTimelineMessageText(raw.messageText ?? "");
|
|
1796
1808
|
const summary = normalizeNotificationText(raw.summary ?? "") || formatNotificationBody(messageText, 100) || "";
|
|
1797
1809
|
const createdAtMs = Number(raw.createdAtMs) || Date.now();
|
|
@@ -1805,9 +1817,9 @@ function normalizeHistoryItem(raw) {
|
|
|
1805
1817
|
stableId,
|
|
1806
1818
|
token: cleanText(raw.token ?? "") || historyToken(stableId),
|
|
1807
1819
|
kind,
|
|
1808
|
-
threadId
|
|
1820
|
+
threadId,
|
|
1809
1821
|
title,
|
|
1810
|
-
threadLabel
|
|
1822
|
+
threadLabel,
|
|
1811
1823
|
summary,
|
|
1812
1824
|
messageText,
|
|
1813
1825
|
imagePaths: normalizeTimelineImagePaths(raw.imagePaths ?? raw.localImagePaths ?? []),
|
|
@@ -1922,6 +1934,7 @@ function normalizeTimelineEntry(raw) {
|
|
|
1922
1934
|
return null;
|
|
1923
1935
|
}
|
|
1924
1936
|
|
|
1937
|
+
const threadId = cleanText(raw.threadId ?? extractConversationIdFromStableId(stableId) ?? "");
|
|
1925
1938
|
const messageText = normalizeTimelineMessageText(raw.messageText ?? "");
|
|
1926
1939
|
const fileEventType = normalizeTimelineFileEventType(raw.fileEventType ?? "");
|
|
1927
1940
|
const diffText = normalizeTimelineDiffText(raw.diffText ?? "");
|
|
@@ -1932,7 +1945,14 @@ function normalizeTimelineEntry(raw) {
|
|
|
1932
1945
|
formatNotificationBody(messageText, 180) ||
|
|
1933
1946
|
(kind === "file_event" ? "" : cleanText(raw.title ?? "")) ||
|
|
1934
1947
|
"";
|
|
1935
|
-
const threadLabel =
|
|
1948
|
+
const threadLabel = preferTitleOnlyJsonThreadLabel(
|
|
1949
|
+
cleanText(raw.threadLabel ?? ""),
|
|
1950
|
+
threadId,
|
|
1951
|
+
raw.messageText,
|
|
1952
|
+
raw.summary,
|
|
1953
|
+
raw.detailText,
|
|
1954
|
+
raw.message
|
|
1955
|
+
);
|
|
1936
1956
|
const title =
|
|
1937
1957
|
cleanText(raw.title ?? "") ||
|
|
1938
1958
|
(kind === "file_event" ? fileEventTitle(DEFAULT_LOCALE, fileEventType) : "") ||
|
|
@@ -1944,7 +1964,7 @@ function normalizeTimelineEntry(raw) {
|
|
|
1944
1964
|
stableId,
|
|
1945
1965
|
token: cleanText(raw.token ?? "") || historyToken(stableId),
|
|
1946
1966
|
kind,
|
|
1947
|
-
threadId
|
|
1967
|
+
threadId,
|
|
1948
1968
|
threadLabel,
|
|
1949
1969
|
title,
|
|
1950
1970
|
summary,
|
|
@@ -6820,16 +6840,16 @@ function formatNativeApprovalMessage(kind, params, locale = config?.defaultLocal
|
|
|
6820
6840
|
function formatCommandApprovalMessage(params, locale = config?.defaultLocale || DEFAULT_LOCALE) {
|
|
6821
6841
|
const parts = [];
|
|
6822
6842
|
const reason = truncate(cleanText(params.reason ?? params.justification ?? ""), 220);
|
|
6823
|
-
const command = truncate(cleanText(params.command ?? params.cmd ?? ""),
|
|
6843
|
+
const command = truncate(cleanText(params.command ?? params.cmd ?? ""), 1200);
|
|
6824
6844
|
if (reason) {
|
|
6825
6845
|
parts.push(reason);
|
|
6826
6846
|
} else {
|
|
6827
6847
|
parts.push(t(locale, "server.message.commandApprovalNeeded"));
|
|
6828
6848
|
}
|
|
6829
6849
|
if (command) {
|
|
6830
|
-
parts.push(t(locale, "server.message.
|
|
6850
|
+
parts.push(`${t(locale, "server.message.commandLabel")}\n\`\`\`sh\n${command}\n\`\`\``);
|
|
6831
6851
|
}
|
|
6832
|
-
return
|
|
6852
|
+
return parts.join("\n\n") || t(locale, "server.message.commandApprovalNeeded");
|
|
6833
6853
|
}
|
|
6834
6854
|
|
|
6835
6855
|
function formatFileApprovalMessage(params, locale = config?.defaultLocale || DEFAULT_LOCALE) {
|
|
@@ -8830,6 +8850,7 @@ function buildPendingApprovalDetail(runtime, approval, locale) {
|
|
|
8830
8850
|
const previousContext = buildPreviousApprovalContext(runtime, approval);
|
|
8831
8851
|
return {
|
|
8832
8852
|
kind: "approval",
|
|
8853
|
+
approvalKind: cleanText(approval.kind || ""),
|
|
8833
8854
|
token: approval.token,
|
|
8834
8855
|
title: formatLocalizedTitle(locale, "server.title.approval", approval.threadLabel),
|
|
8835
8856
|
threadLabel: approval.threadLabel || "",
|
|
@@ -12476,6 +12497,22 @@ function extractRolloutMessageText(content) {
|
|
|
12476
12497
|
);
|
|
12477
12498
|
}
|
|
12478
12499
|
|
|
12500
|
+
function extractTitleOnlyJsonTitleFromRolloutContent(content) {
|
|
12501
|
+
if (!Array.isArray(content)) {
|
|
12502
|
+
return "";
|
|
12503
|
+
}
|
|
12504
|
+
for (const entry of content) {
|
|
12505
|
+
if (!isPlainObject(entry) || (entry.type !== "input_text" && entry.type !== "output_text")) {
|
|
12506
|
+
continue;
|
|
12507
|
+
}
|
|
12508
|
+
const title = extractTitleOnlyJsonTitle(entry.text ?? "");
|
|
12509
|
+
if (title) {
|
|
12510
|
+
return title;
|
|
12511
|
+
}
|
|
12512
|
+
}
|
|
12513
|
+
return "";
|
|
12514
|
+
}
|
|
12515
|
+
|
|
12479
12516
|
function rolloutContentHasImages(content) {
|
|
12480
12517
|
if (!Array.isArray(content)) {
|
|
12481
12518
|
return false;
|
|
@@ -12591,6 +12628,22 @@ async function extractRolloutThreadMetadata(filePath) {
|
|
|
12591
12628
|
break;
|
|
12592
12629
|
}
|
|
12593
12630
|
}
|
|
12631
|
+
} else if (!metadata.titleCandidate && entry.payload?.type === "message" && entry.payload?.role === "assistant") {
|
|
12632
|
+
const titleCandidate = truncate(cleanText(extractTitleOnlyJsonTitleFromRolloutContent(entry.payload.content)), 90);
|
|
12633
|
+
if (titleCandidate) {
|
|
12634
|
+
metadata.titleCandidate = titleCandidate;
|
|
12635
|
+
if (metadata.threadId) {
|
|
12636
|
+
break;
|
|
12637
|
+
}
|
|
12638
|
+
}
|
|
12639
|
+
} else if (!metadata.titleCandidate && entry.type === "event_msg" && entry.payload?.type === "task_complete") {
|
|
12640
|
+
const titleCandidate = truncate(cleanText(extractTitleOnlyJsonTitle(entry.payload.last_agent_message ?? "")), 90);
|
|
12641
|
+
if (titleCandidate) {
|
|
12642
|
+
metadata.titleCandidate = titleCandidate;
|
|
12643
|
+
if (metadata.threadId) {
|
|
12644
|
+
break;
|
|
12645
|
+
}
|
|
12646
|
+
}
|
|
12594
12647
|
}
|
|
12595
12648
|
}
|
|
12596
12649
|
} catch {
|
|
@@ -12767,11 +12820,12 @@ function refreshResolvedThreadLabels({ config, runtime, state }) {
|
|
|
12767
12820
|
if (!conversationId) {
|
|
12768
12821
|
return item;
|
|
12769
12822
|
}
|
|
12770
|
-
const
|
|
12823
|
+
const nativeThreadLabel = getNativeThreadLabel({
|
|
12771
12824
|
runtime,
|
|
12772
12825
|
conversationId,
|
|
12773
12826
|
cwd: "",
|
|
12774
12827
|
});
|
|
12828
|
+
const threadLabel = preferTitleOnlyJsonThreadLabel(nativeThreadLabel, conversationId, item.messageText, item.summary);
|
|
12775
12829
|
const title = formatTitle(kindTitle(config.defaultLocale, item.kind), threadLabel);
|
|
12776
12830
|
if (threadLabel === item.threadLabel && title === item.title) {
|
|
12777
12831
|
return item;
|
|
@@ -12801,11 +12855,12 @@ function refreshResolvedThreadLabels({ config, runtime, state }) {
|
|
|
12801
12855
|
if (!threadId) {
|
|
12802
12856
|
return entry;
|
|
12803
12857
|
}
|
|
12804
|
-
const
|
|
12858
|
+
const nativeThreadLabel = getNativeThreadLabel({
|
|
12805
12859
|
runtime,
|
|
12806
12860
|
conversationId: threadId,
|
|
12807
12861
|
cwd: "",
|
|
12808
12862
|
});
|
|
12863
|
+
const threadLabel = preferTitleOnlyJsonThreadLabel(nativeThreadLabel, threadId, entry.messageText, entry.summary);
|
|
12809
12864
|
const title = threadLabel || kindTitle(config.defaultLocale, entry.kind);
|
|
12810
12865
|
if (threadLabel === entry.threadLabel && title === entry.title) {
|
|
12811
12866
|
return entry;
|
|
@@ -12835,11 +12890,12 @@ function refreshResolvedThreadLabels({ config, runtime, state }) {
|
|
|
12835
12890
|
if (!threadId) {
|
|
12836
12891
|
return entry;
|
|
12837
12892
|
}
|
|
12838
|
-
const
|
|
12893
|
+
const nativeThreadLabel = getNativeThreadLabel({
|
|
12839
12894
|
runtime,
|
|
12840
12895
|
conversationId: threadId,
|
|
12841
12896
|
cwd: "",
|
|
12842
12897
|
});
|
|
12898
|
+
const threadLabel = preferTitleOnlyJsonThreadLabel(nativeThreadLabel, threadId, entry.messageText, entry.summary);
|
|
12843
12899
|
const title = threadLabel || kindTitle(config.defaultLocale, entry.kind);
|
|
12844
12900
|
if (threadLabel === entry.threadLabel && title === entry.title) {
|
|
12845
12901
|
return entry;
|
|
@@ -12901,6 +12957,10 @@ function summarizeNotificationText(value) {
|
|
|
12901
12957
|
}
|
|
12902
12958
|
|
|
12903
12959
|
function normalizeLongText(value) {
|
|
12960
|
+
const titleOnlyJsonTitle = extractTitleOnlyJsonTitle(value);
|
|
12961
|
+
if (titleOnlyJsonTitle) {
|
|
12962
|
+
return titleOnlyJsonTitle;
|
|
12963
|
+
}
|
|
12904
12964
|
return String(stripEnvironmentContextBlocks(stripMarkdownLinks(value)) || "")
|
|
12905
12965
|
.replace(/\r\n/gu, "\n")
|
|
12906
12966
|
.replace(/[ \t]+\n/gu, "\n")
|
|
@@ -13012,6 +13072,53 @@ function cleanText(value) {
|
|
|
13012
13072
|
return String(value || "").replace(/\s+/gu, " ").trim();
|
|
13013
13073
|
}
|
|
13014
13074
|
|
|
13075
|
+
function extractTitleOnlyJsonTitle(value) {
|
|
13076
|
+
const rawText = String(value ?? "").trim();
|
|
13077
|
+
if (!rawText) {
|
|
13078
|
+
return "";
|
|
13079
|
+
}
|
|
13080
|
+
const parsed = safeJsonParse(rawText);
|
|
13081
|
+
if (!isPlainObject(parsed) || typeof parsed.title !== "string") {
|
|
13082
|
+
return "";
|
|
13083
|
+
}
|
|
13084
|
+
const meaningfulExtraKeys = Object.entries(parsed).filter(([key, entryValue]) => {
|
|
13085
|
+
if (key === "title") {
|
|
13086
|
+
return false;
|
|
13087
|
+
}
|
|
13088
|
+
if (entryValue == null) {
|
|
13089
|
+
return false;
|
|
13090
|
+
}
|
|
13091
|
+
if (typeof entryValue === "string" && cleanText(entryValue) === "") {
|
|
13092
|
+
return false;
|
|
13093
|
+
}
|
|
13094
|
+
if (Array.isArray(entryValue) && entryValue.length === 0) {
|
|
13095
|
+
return false;
|
|
13096
|
+
}
|
|
13097
|
+
if (isPlainObject(entryValue) && Object.keys(entryValue).length === 0) {
|
|
13098
|
+
return false;
|
|
13099
|
+
}
|
|
13100
|
+
return true;
|
|
13101
|
+
});
|
|
13102
|
+
if (meaningfulExtraKeys.length > 0) {
|
|
13103
|
+
return "";
|
|
13104
|
+
}
|
|
13105
|
+
return cleanText(parsed.title || "");
|
|
13106
|
+
}
|
|
13107
|
+
|
|
13108
|
+
function preferTitleOnlyJsonThreadLabel(rawThreadLabel, conversationId, ...candidates) {
|
|
13109
|
+
const preferredThreadLabel = sanitizeResolvedThreadLabel(rawThreadLabel, conversationId);
|
|
13110
|
+
if (preferredThreadLabel) {
|
|
13111
|
+
return preferredThreadLabel;
|
|
13112
|
+
}
|
|
13113
|
+
for (const candidate of candidates) {
|
|
13114
|
+
const titleOnlyJsonTitle = truncate(cleanText(extractTitleOnlyJsonTitle(candidate)), 90);
|
|
13115
|
+
if (titleOnlyJsonTitle) {
|
|
13116
|
+
return titleOnlyJsonTitle;
|
|
13117
|
+
}
|
|
13118
|
+
}
|
|
13119
|
+
return cleanText(rawThreadLabel || "");
|
|
13120
|
+
}
|
|
13121
|
+
|
|
13015
13122
|
function stripMarkdownLinks(value) {
|
|
13016
13123
|
return String(value || "").replace(/\[([^\]]+)\]\(([^)]+)\)/gu, "$1");
|
|
13017
13124
|
}
|
|
@@ -13226,6 +13333,8 @@ async function main() {
|
|
|
13226
13333
|
}
|
|
13227
13334
|
|
|
13228
13335
|
if (
|
|
13336
|
+
normalizedHistoryStateChanged ||
|
|
13337
|
+
normalizedTimelineStateChanged ||
|
|
13229
13338
|
migratedPairedDevicesStateChanged ||
|
|
13230
13339
|
restoredPendingPlanStateChanged ||
|
|
13231
13340
|
restoredTimelineImagePathsStateChanged ||
|
package/scripts/viveworker.mjs
CHANGED
|
@@ -1336,10 +1336,24 @@ async function installMkcertForMac(progress, locale) {
|
|
|
1336
1336
|
}
|
|
1337
1337
|
|
|
1338
1338
|
progress?.update("cli.setup.progress.installMkcert");
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1339
|
+
try {
|
|
1340
|
+
await execCommand([brewPath, "install", "mkcert"], {
|
|
1341
|
+
streamOutput: true,
|
|
1342
|
+
beforeStreamOutput: () => progress?.clear(),
|
|
1343
|
+
});
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
const rawMessage = String(error?.message || error || "");
|
|
1346
|
+
const permissionIssue =
|
|
1347
|
+
/not writable/iu.test(rawMessage) &&
|
|
1348
|
+
(/homebrew/iu.test(rawMessage) || /\/opt\/homebrew/iu.test(rawMessage) || /\/usr\/local/iu.test(rawMessage));
|
|
1349
|
+
throw new Error(
|
|
1350
|
+
[
|
|
1351
|
+
t(locale, permissionIssue ? "cli.setup.error.mkcertInstallPermission" : "cli.setup.error.mkcertInstallFailed"),
|
|
1352
|
+
t(locale, "cli.setup.error.mkcertInstallNext"),
|
|
1353
|
+
t(locale, "cli.setup.error.mkcertInstallExample"),
|
|
1354
|
+
].join("\n")
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1343
1357
|
const mkcertPath = await findExecutable("mkcert");
|
|
1344
1358
|
if (!mkcertPath) {
|
|
1345
1359
|
throw new Error("mkcert installation finished, but the mkcert executable is still not available.");
|
package/viveworker.env.example
CHANGED
|
@@ -74,15 +74,15 @@ NOTIFY_PLANS=1
|
|
|
74
74
|
NATIVE_APPROVALS=1
|
|
75
75
|
ENABLE_NTFY=0
|
|
76
76
|
|
|
77
|
-
# Native codex.app approval pages for
|
|
77
|
+
# Native codex.app approval pages for mobile devices.
|
|
78
78
|
# Use HTTPS for LAN access whenever possible. Plain HTTP over LAN requires
|
|
79
79
|
# ALLOW_INSECURE_LAN_HTTP=1 and is not recommended.
|
|
80
|
-
# Use a public LAN URL the
|
|
80
|
+
# Use a public LAN URL the device can reach. This can be different from NTFY_PUBLISH_BASE_URL.
|
|
81
81
|
NATIVE_APPROVAL_SERVER_PUBLIC_BASE_URL=
|
|
82
82
|
NATIVE_APPROVAL_SERVER_HOST=127.0.0.1
|
|
83
83
|
NATIVE_APPROVAL_SERVER_PORT=8810
|
|
84
84
|
|
|
85
|
-
# App-server runner settings for interactive
|
|
85
|
+
# App-server runner settings for interactive mobile approvals.
|
|
86
86
|
# If empty, the runner derives the host from NTFY_BASE_URL.
|
|
87
87
|
APPROVAL_SERVER_PUBLIC_BASE_URL=
|
|
88
88
|
APPROVAL_SERVER_HOST=0.0.0.0
|
package/web/app.css
CHANGED
package/web/app.js
CHANGED
|
@@ -3133,6 +3133,9 @@ function renderDetailPlainIntro(detail, options = {}) {
|
|
|
3133
3133
|
if (!["approval", "diff_thread", "file_event"].includes(detail?.kind || "")) {
|
|
3134
3134
|
return "";
|
|
3135
3135
|
}
|
|
3136
|
+
if (detail?.kind === "approval" && normalizeClientText(detail?.approvalKind || "") !== "file") {
|
|
3137
|
+
return "";
|
|
3138
|
+
}
|
|
3136
3139
|
if (!detail?.messageHtml) {
|
|
3137
3140
|
return "";
|
|
3138
3141
|
}
|
package/web/i18n.js
CHANGED
|
@@ -70,7 +70,7 @@ const translations = {
|
|
|
70
70
|
"tab.code.title": "Code",
|
|
71
71
|
"tab.code.label": "Code",
|
|
72
72
|
"tab.code.eyebrow": "Current changes",
|
|
73
|
-
"tab.code.description": "Review the current unstaged file changes from Codex in a mobile-friendly diff view.",
|
|
73
|
+
"tab.code.description": "Review the current unstaged file changes from Codex in a mobile-friendly diff view on your device.",
|
|
74
74
|
"tab.completed.title": "Completed",
|
|
75
75
|
"tab.completed.label": "Completed",
|
|
76
76
|
"tab.completed.eyebrow": "History",
|
|
@@ -80,34 +80,34 @@ const translations = {
|
|
|
80
80
|
"tab.settings.eyebrow": "Device",
|
|
81
81
|
"tab.settings.description": "Pairing, installation, and notification controls.",
|
|
82
82
|
"pair.copy":
|
|
83
|
-
"Pair this
|
|
83
|
+
"Pair this device with your local Codex desktop session and keep approvals, plans, choices, and completions within reach.",
|
|
84
84
|
"pair.codeLabel": "Pairing code",
|
|
85
85
|
"pair.codePlaceholder": "Enter the pairing code",
|
|
86
|
-
"pair.connect": "Connect this
|
|
86
|
+
"pair.connect": "Connect this device",
|
|
87
87
|
"pair.installFromHomeScreen":
|
|
88
88
|
"This pairing link is being kept for the Home Screen app. Add viveworker to your Home Screen from Safari, then open the new icon to finish pairing there.",
|
|
89
89
|
"pair.helperTitle": "Add to Home Screen",
|
|
90
90
|
"pair.helperCopy": "Install viveworker for the best mobile layout and Web Push support.",
|
|
91
91
|
"banner.install.title": "Add viveworker to your Home Screen",
|
|
92
92
|
"banner.install.copy.safari":
|
|
93
|
-
"Install it from Safari for a cleaner
|
|
93
|
+
"Install it from Safari for a cleaner mobile layout and background notifications.",
|
|
94
94
|
"banner.install.copy.other": "Open this page in Safari to install viveworker to your Home Screen.",
|
|
95
95
|
"banner.push.title": "Push notifications can help",
|
|
96
96
|
"banner.push.copy.https": "Open viveworker over HTTPS to turn on push notifications.",
|
|
97
|
-
"banner.push.copy.standalone": "Add viveworker to your Home Screen to receive push notifications on
|
|
97
|
+
"banner.push.copy.standalone": "Add viveworker to your Home Screen to receive push notifications on this device.",
|
|
98
98
|
"banner.push.copy.denied":
|
|
99
|
-
"Notifications are currently blocked for this app. You can re-enable them later from
|
|
99
|
+
"Notifications are currently blocked for this app. You can re-enable them later from your device settings.",
|
|
100
100
|
"banner.push.copy.default":
|
|
101
101
|
"Enable push notifications to catch approvals, plans, questions, and completions even when the app is in the background.",
|
|
102
102
|
"install.guide.title": "Add to Home Screen",
|
|
103
103
|
"install.guide.intro.safari":
|
|
104
|
-
"
|
|
105
|
-
"install.guide.intro.other": "
|
|
104
|
+
"This browser does not let websites trigger this directly, but you can add viveworker in a few taps.",
|
|
105
|
+
"install.guide.intro.other": "If Home Screen installation is unavailable here, try opening this page in Safari or another supported mobile browser.",
|
|
106
106
|
"install.guide.step.openSafari": "Open this same URL in Safari.",
|
|
107
107
|
"install.guide.step.tapShare": "Tap the Share button.",
|
|
108
108
|
"install.guide.step.chooseAdd": "Choose Add to Home Screen.",
|
|
109
109
|
"install.guide.step.tapAdd": "Tap Add, then open the new icon.",
|
|
110
|
-
"shell.subtitle.settings": "Pairing, notifications, and installation for this
|
|
110
|
+
"shell.subtitle.settings": "Pairing, notifications, and installation for this device.",
|
|
111
111
|
"shell.subtitle.detail": "Review the selected item and act without leaving your current flow.",
|
|
112
112
|
"empty.pending":
|
|
113
113
|
"Nothing needs attention right now. New approvals, plans, and choices will appear here first.",
|
|
@@ -147,7 +147,7 @@ const translations = {
|
|
|
147
147
|
"reply.imageAddMore": "Add more images",
|
|
148
148
|
"reply.imageReplace": "Replace image",
|
|
149
149
|
"reply.imageRemove": "Remove",
|
|
150
|
-
"reply.imageHint": "Attach up to {count} images from your
|
|
150
|
+
"reply.imageHint": "Attach up to {count} images from your device and send them together with your message.",
|
|
151
151
|
"reply.imageAttached": "Attached image",
|
|
152
152
|
"reply.send": "Send to Codex",
|
|
153
153
|
"reply.sendConfirm": "Send anyway",
|
|
@@ -225,34 +225,34 @@ const translations = {
|
|
|
225
225
|
"diff.threadSummary": ({ count }) => `Changed ${count} ${count === 1 ? "file" : "files"}`,
|
|
226
226
|
"diff.latestChange": "Latest change",
|
|
227
227
|
"diff.latestChangeFallback": "Current unstaged changes are available in this thread.",
|
|
228
|
-
"settings.intro": "Check pairing, language, install status, and Web Push health for this
|
|
228
|
+
"settings.intro": "Check pairing, language, install status, and Web Push health for this device.",
|
|
229
229
|
"settings.section.overview": "Quick setup",
|
|
230
230
|
"settings.section.notifications": "Notifications",
|
|
231
231
|
"settings.section.install": "Install",
|
|
232
|
-
"settings.section.device": "This
|
|
232
|
+
"settings.section.device": "This device",
|
|
233
233
|
"settings.group.general": "General",
|
|
234
|
-
"settings.group.device": "This
|
|
234
|
+
"settings.group.device": "This device",
|
|
235
235
|
"settings.group.values": "Current settings",
|
|
236
236
|
"settings.group.advanced": "Advanced",
|
|
237
237
|
"settings.group.actions": "Actions",
|
|
238
238
|
"settings.overview.title": "At a glance",
|
|
239
|
-
"settings.overview.copy": "See what is ready, what still needs setup, and what to do next on this
|
|
239
|
+
"settings.overview.copy": "See what is ready, what still needs setup, and what to do next on this device.",
|
|
240
240
|
"settings.summary.notifications": "Notifications",
|
|
241
241
|
"settings.summary.install": "Home Screen",
|
|
242
242
|
"settings.summary.pairing": "Pairing",
|
|
243
243
|
"settings.nextStep.eyebrow": "Next step",
|
|
244
244
|
"settings.nextStep.enableNotifications.title": "Turn on notifications",
|
|
245
|
-
"settings.nextStep.enableNotifications.copy": "Enable Web Push on this
|
|
245
|
+
"settings.nextStep.enableNotifications.copy": "Enable Web Push on this device so approvals, plans, and questions can reach you in the background.",
|
|
246
246
|
"settings.nextStep.serverDisabled.title": "Server setup is still missing",
|
|
247
|
-
"settings.nextStep.serverDisabled.copy": "Web Push is not enabled on this viveworker server yet, so this
|
|
247
|
+
"settings.nextStep.serverDisabled.copy": "Web Push is not enabled on this viveworker server yet, so this device cannot subscribe yet.",
|
|
248
248
|
"settings.nextStep.openHttps.title": "Open the HTTPS version",
|
|
249
|
-
"settings.nextStep.openHttps.copy": "This page must be opened over HTTPS before
|
|
249
|
+
"settings.nextStep.openHttps.copy": "This page must be opened over HTTPS before this device can enable notifications.",
|
|
250
250
|
"settings.nextStep.install.title": "Add viveworker to Home Screen",
|
|
251
|
-
"settings.nextStep.install.copy": "
|
|
251
|
+
"settings.nextStep.install.copy": "Add the app to your Home Screen, then reopen the installed app before enabling notifications.",
|
|
252
252
|
"settings.nextStep.permissionBlocked.title": "Notifications are blocked",
|
|
253
|
-
"settings.nextStep.permissionBlocked.copy": "Re-enable notifications in
|
|
253
|
+
"settings.nextStep.permissionBlocked.copy": "Re-enable notifications in your device settings, then return here to test again.",
|
|
254
254
|
"settings.nextStep.test.title": "Everything is ready",
|
|
255
|
-
"settings.nextStep.test.copy": "Send a test notification to confirm this
|
|
255
|
+
"settings.nextStep.test.copy": "Send a test notification to confirm this device can receive pushes from viveworker.",
|
|
256
256
|
"settings.status.notAvailable": "Not available",
|
|
257
257
|
"settings.status.unsupported": "Not supported",
|
|
258
258
|
"settings.status.needsHttps": "Needs HTTPS",
|
|
@@ -270,7 +270,7 @@ const translations = {
|
|
|
270
270
|
"settings.notifications.serverDisabled": "Web Push is not enabled on the server yet.",
|
|
271
271
|
"settings.notifications.openHttps": "Open viveworker over HTTPS before enabling notifications.",
|
|
272
272
|
"settings.notifications.openHomeScreen":
|
|
273
|
-
"Open the Home Screen app before enabling notifications on
|
|
273
|
+
"Open the installed Home Screen app before enabling notifications on this device.",
|
|
274
274
|
"settings.action.enableNotifications": "Enable Notifications",
|
|
275
275
|
"settings.action.disableNotifications": "Disable Notifications",
|
|
276
276
|
"settings.action.sendTest": "Send Test Notification",
|
|
@@ -279,9 +279,9 @@ const translations = {
|
|
|
279
279
|
"settings.action.removeThisDevice": "Remove this device",
|
|
280
280
|
"settings.install.title": "Install to Home Screen",
|
|
281
281
|
"settings.install.copy":
|
|
282
|
-
"For the best
|
|
282
|
+
"For the best mobile experience, add viveworker to your Home Screen and open the installed app.",
|
|
283
283
|
"settings.device.title": "Devices",
|
|
284
|
-
"settings.device.copy": "Manage trusted
|
|
284
|
+
"settings.device.copy": "Manage trusted devices and browsers that can open this LAN viveworker app.",
|
|
285
285
|
"settings.device.count": ({ count }) => `${count} ${count === 1 ? "device" : "devices"}`,
|
|
286
286
|
"settings.device.section.current": "This device",
|
|
287
287
|
"settings.device.section.other": "Other trusted devices",
|
|
@@ -289,7 +289,7 @@ const translations = {
|
|
|
289
289
|
"settings.device.emptyCurrent": "This device is not listed right now.",
|
|
290
290
|
"settings.device.emptyOther": "No other trusted devices are active.",
|
|
291
291
|
"settings.device.addAnother.title": "Add another device",
|
|
292
|
-
"settings.device.addAnother.heading": "Add a new
|
|
292
|
+
"settings.device.addAnother.heading": "Add a new device or browser",
|
|
293
293
|
"settings.device.addAnother.copy":
|
|
294
294
|
"Run this on your Mac only when you want to add another trusted device. Your main app URL stays the same.",
|
|
295
295
|
"settings.device.addAnother.commandLabel": "Run on your Mac",
|
|
@@ -302,12 +302,12 @@ const translations = {
|
|
|
302
302
|
"settings.device.mode.standalone": "Home Screen app",
|
|
303
303
|
"settings.device.mode.browser": "Browser tab",
|
|
304
304
|
"settings.language.title": "Language",
|
|
305
|
-
"settings.language.copy": "Choose how this
|
|
305
|
+
"settings.language.copy": "Choose how this device should display viveworker.",
|
|
306
306
|
"settings.pairing.title": "Pairing",
|
|
307
|
-
"settings.pairing.copy": "This
|
|
307
|
+
"settings.pairing.copy": "This device is paired with your local Codex desktop session.",
|
|
308
308
|
"settings.pairing.connected": "Paired",
|
|
309
309
|
"settings.technical.title": "Technical details",
|
|
310
|
-
"settings.technical.copy": "Connection and notification diagnostics for this
|
|
310
|
+
"settings.technical.copy": "Connection and notification diagnostics for this device.",
|
|
311
311
|
"settings.technical.copyShort": "Connection and notification diagnostics.",
|
|
312
312
|
"settings.row.deviceId": "Device ID",
|
|
313
313
|
"settings.row.pairedAt": "Paired",
|
|
@@ -334,11 +334,11 @@ const translations = {
|
|
|
334
334
|
"notice.loggedOutKeepTrusted": "Logged out. This device stays trusted, so you can come back without pairing again.",
|
|
335
335
|
"notice.loggedOutDeviceRemoved": "Logged out and removed this device. Pair again before using viveworker here.",
|
|
336
336
|
"error.networkHint":
|
|
337
|
-
"Try reopening the latest HTTPS URL in
|
|
337
|
+
"Try reopening the latest HTTPS URL in your browser, then re-add the Home Screen app if needed.",
|
|
338
338
|
"error.webPushDisabled": "Web Push is not enabled on the server.",
|
|
339
339
|
"error.notificationsRequireHttps": "Notifications require HTTPS.",
|
|
340
340
|
"error.pushUnsupported": "This browser does not support Web Push.",
|
|
341
|
-
"error.openHomeScreen": "
|
|
341
|
+
"error.openHomeScreen": "Open viveworker from the Home Screen before enabling notifications on this device.",
|
|
342
342
|
"error.notificationPermission": ({ status }) => `Notification permission is ${status}.`,
|
|
343
343
|
"error.pushServerNotReady": "The server is not ready for Web Push.",
|
|
344
344
|
"error.serviceWorkerUnavailable": "Service Worker is not available.",
|
|
@@ -383,10 +383,11 @@ const translations = {
|
|
|
383
383
|
"server.action.reject": "Reject",
|
|
384
384
|
"server.action.implement": "Implement",
|
|
385
385
|
"server.message.commandApprovalNeeded": "Command approval needed.",
|
|
386
|
+
"server.message.commandLabel": "Command",
|
|
386
387
|
"server.message.fileApprovalNeeded": "File changes need approval.",
|
|
387
388
|
"server.message.commandPrefix": ({ command }) => `Command: ${command}`,
|
|
388
389
|
"server.message.pathPrefix": ({ path }) => `Path: ${path}`,
|
|
389
|
-
"server.message.approveOnMac": "Approve on Mac only: this
|
|
390
|
+
"server.message.approveOnMac": "Approve on Mac only: this device alert mirrors codex.app.",
|
|
390
391
|
"server.message.approvalNeededInCodex": "Approval needed in Codex.",
|
|
391
392
|
"server.message.extraApprovals": ({ count }) => `Extra approvals: ${count}`,
|
|
392
393
|
"server.message.taskFinished": "Task finished.",
|
|
@@ -404,7 +405,7 @@ const translations = {
|
|
|
404
405
|
"server.confirm.approve": "This approval will be confirmed and Codex will continue.",
|
|
405
406
|
"server.confirm.reject": "This action will be rejected and Codex will be told not to continue.",
|
|
406
407
|
"server.pushTest.title": "viveworker test notification",
|
|
407
|
-
"server.pushTest.body": "Notifications are working on this
|
|
408
|
+
"server.pushTest.body": "Notifications are working on this device.",
|
|
408
409
|
"server.page.notFoundTitle": "Not found",
|
|
409
410
|
"server.page.notFoundBody": "This approval link does not exist.",
|
|
410
411
|
"server.page.notFoundHint": "You can close this page safely.",
|
|
@@ -447,16 +448,16 @@ const translations = {
|
|
|
447
448
|
"cli.setup.webPushEnabled": "Web Push: enabled (HTTPS)",
|
|
448
449
|
"cli.setup.webPushDisabled": "Web Push: disabled",
|
|
449
450
|
"cli.setup.completePending": "viveworker setup finished, but health has not responded yet.",
|
|
450
|
-
"cli.setup.caFlow.title": "Before opening viveworker on
|
|
451
|
-
"cli.setup.caFlow.step1": "1. Open the CA download URL or scan the QR code below on your
|
|
452
|
-
"cli.setup.caFlow.step2": "2. Install the rootCA.pem profile, then enable trust for it
|
|
451
|
+
"cli.setup.caFlow.title": "Before opening viveworker on your device, install and trust the local root certificate if needed:",
|
|
452
|
+
"cli.setup.caFlow.step1": "1. Open the CA download URL or scan the QR code below on your device.",
|
|
453
|
+
"cli.setup.caFlow.step2": "2. Install the rootCA.pem profile, then enable trust for it if your device requires local CA trust.",
|
|
453
454
|
"cli.setup.caFlow.step3": "3. After that, press Enter here to show the viveworker app URL and pairing QR.",
|
|
454
455
|
"cli.setup.instructions.https":
|
|
455
|
-
"Open the HTTPS URL in
|
|
456
|
+
"Open the HTTPS URL in your mobile browser, trust your local CA if needed, then add the app to the Home Screen to enable notifications.",
|
|
456
457
|
"cli.setup.instructions.afterCa":
|
|
457
|
-
"Now open the pairing URL
|
|
458
|
+
"Now open the pairing URL on your device, pair it if needed, then add viveworker to the Home Screen.",
|
|
458
459
|
"cli.setup.instructions.insecureHttpLan":
|
|
459
|
-
"Open the HTTP URL in
|
|
460
|
+
"Open the HTTP URL in a mobile browser only if you intentionally allowed insecure LAN access. This mode is not recommended on shared Wi-Fi.",
|
|
460
461
|
"cli.setup.instructions.localOnlyHttp":
|
|
461
462
|
"This setup is loopback-only HTTP for local development. Use the default HTTPS setup or --allow-insecure-http-lan if you explicitly need LAN access.",
|
|
462
463
|
"logout.confirm.title": "Choose how to log out",
|
|
@@ -474,6 +475,12 @@ const translations = {
|
|
|
474
475
|
"cli.setup.qrPairing": "Pairing QR:",
|
|
475
476
|
"cli.setup.qrCaDownload": "rootCA.pem download QR (IP):",
|
|
476
477
|
"cli.setup.prompt.continueToApp": "Press Enter to continue to the viveworker app URL.",
|
|
478
|
+
"cli.setup.error.mkcertInstallFailed": "viveworker could not install mkcert with Homebrew.",
|
|
479
|
+
"cli.setup.error.mkcertInstallPermission":
|
|
480
|
+
"Homebrew is installed, but its prefix is not writable by your current user, so viveworker could not auto-install mkcert.",
|
|
481
|
+
"cli.setup.error.mkcertInstallNext":
|
|
482
|
+
"Fix your Homebrew permissions, then rerun `npx viveworker setup --install-mkcert`, or install mkcert manually first.",
|
|
483
|
+
"cli.setup.error.mkcertInstallExample": "Example: brew install mkcert && mkcert -install",
|
|
477
484
|
"cli.start.progress.prepare": "Loading saved viveworker config...",
|
|
478
485
|
"cli.start.progress.refreshPairing": "Refreshing expired pairing credentials...",
|
|
479
486
|
"cli.start.progress.launchd": "Starting the launchd service...",
|
|
@@ -582,11 +589,11 @@ const translations = {
|
|
|
582
589
|
"tab.diff.title": "差分",
|
|
583
590
|
"tab.diff.label": "差分",
|
|
584
591
|
"tab.diff.eyebrow": "現在の変更",
|
|
585
|
-
"tab.diff.description": "Codex
|
|
592
|
+
"tab.diff.description": "Codex が残している現在の未ステージ変更を端末で確認します。",
|
|
586
593
|
"tab.code.title": "コード",
|
|
587
594
|
"tab.code.label": "コード",
|
|
588
595
|
"tab.code.eyebrow": "現在の変更",
|
|
589
|
-
"tab.code.description": "Codex
|
|
596
|
+
"tab.code.description": "Codex が残している現在の未ステージ変更を端末で確認します。",
|
|
590
597
|
"tab.completed.title": "完了",
|
|
591
598
|
"tab.completed.label": "完了",
|
|
592
599
|
"tab.completed.eyebrow": "履歴",
|
|
@@ -596,34 +603,34 @@ const translations = {
|
|
|
596
603
|
"tab.settings.eyebrow": "端末",
|
|
597
604
|
"tab.settings.description": "ペアリング、インストール、通知の設定です。",
|
|
598
605
|
"pair.copy":
|
|
599
|
-
"
|
|
606
|
+
"この端末をローカルの Codex desktop session とペアリングして、承認、プラン、選択、完了をすぐ確認できるようにします。",
|
|
600
607
|
"pair.codeLabel": "ペアリングコード",
|
|
601
608
|
"pair.codePlaceholder": "ペアリングコードを入力",
|
|
602
|
-
"pair.connect": "
|
|
609
|
+
"pair.connect": "この端末を接続",
|
|
603
610
|
"pair.installFromHomeScreen":
|
|
604
611
|
"この pairing link はホーム画面アプリ用に温存されています。Safari から viveworker をホーム画面に追加し、新しいアイコンを開いてそこで pairing を完了してください。",
|
|
605
612
|
"pair.helperTitle": "ホーム画面に追加",
|
|
606
613
|
"pair.helperCopy": "モバイル表示と Web Push を最適に使うには viveworker をインストールしてください。",
|
|
607
614
|
"banner.install.title": "viveworker をホーム画面に追加",
|
|
608
615
|
"banner.install.copy.safari":
|
|
609
|
-
"Safari
|
|
616
|
+
"Safari からインストールすると、モバイル向け表示とバックグラウンド通知が使いやすくなります。",
|
|
610
617
|
"banner.install.copy.other": "Safari でこのページを開いて、viveworker をホーム画面に追加してください。",
|
|
611
618
|
"banner.push.title": "プッシュ通知を有効にすると便利です",
|
|
612
619
|
"banner.push.copy.https": "プッシュ通知を使うには HTTPS の viveworker を開いてください。",
|
|
613
|
-
"banner.push.copy.standalone": "
|
|
620
|
+
"banner.push.copy.standalone": "この端末でプッシュ通知を受けるには、viveworker をホーム画面に追加してください。",
|
|
614
621
|
"banner.push.copy.denied":
|
|
615
|
-
"
|
|
622
|
+
"このアプリの通知は現在ブロックされています。あとで端末の設定から再度有効にできます。",
|
|
616
623
|
"banner.push.copy.default":
|
|
617
624
|
"バックグラウンドでも承認、プラン、質問、完了に気づけるよう、プッシュ通知を有効にしてください。",
|
|
618
625
|
"install.guide.title": "ホーム画面に追加",
|
|
619
626
|
"install.guide.intro.safari":
|
|
620
|
-
"
|
|
621
|
-
"install.guide.intro.other": "
|
|
627
|
+
"このブラウザではサイト側から直接追加できませんが、数タップでホーム画面に追加できます。",
|
|
628
|
+
"install.guide.intro.other": "ここでホーム画面に追加できない場合は、Safari など対応ブラウザでこのページを開いてください。",
|
|
622
629
|
"install.guide.step.openSafari": "この URL を Safari で開きます。",
|
|
623
630
|
"install.guide.step.tapShare": "共有ボタンをタップします。",
|
|
624
631
|
"install.guide.step.chooseAdd": "「ホーム画面に追加」を選びます。",
|
|
625
632
|
"install.guide.step.tapAdd": "「追加」を押して、新しいアイコンを開きます。",
|
|
626
|
-
"shell.subtitle.settings": "
|
|
633
|
+
"shell.subtitle.settings": "この端末のペアリング、通知、インストール設定です。",
|
|
627
634
|
"shell.subtitle.detail": "いまの流れを途切れさせずに、項目を確認して操作できます。",
|
|
628
635
|
"empty.pending": "いま対応が必要な項目はありません。新しい承認、プラン、選択がここに表示されます。",
|
|
629
636
|
"empty.timeline": "まだタイムライン項目はありません。新しい会話の更新がここに表示されます。",
|
|
@@ -661,7 +668,7 @@ const translations = {
|
|
|
661
668
|
"reply.imageAddMore": "さらに画像を追加",
|
|
662
669
|
"reply.imageReplace": "画像を差し替え",
|
|
663
670
|
"reply.imageRemove": "削除",
|
|
664
|
-
"reply.imageHint": "
|
|
671
|
+
"reply.imageHint": "この端末の画像を最大 {count} 枚まで添付して、メッセージと一緒に送れます。",
|
|
665
672
|
"reply.imageAttached": "添付した画像",
|
|
666
673
|
"reply.send": "Codex に送信",
|
|
667
674
|
"reply.sendConfirm": "それでも送信",
|
|
@@ -739,34 +746,34 @@ const translations = {
|
|
|
739
746
|
"diff.threadSummary": ({ count }) => `変更ファイル ${count}件`,
|
|
740
747
|
"diff.latestChange": "最終変更",
|
|
741
748
|
"diff.latestChangeFallback": "このスレッドに現在未ステージの変更があります。",
|
|
742
|
-
"settings.intro": "
|
|
749
|
+
"settings.intro": "この端末のペアリング、言語、インストール状況、Web Push の状態を確認できます。",
|
|
743
750
|
"settings.section.overview": "クイック確認",
|
|
744
751
|
"settings.section.notifications": "通知",
|
|
745
752
|
"settings.section.install": "インストール",
|
|
746
|
-
"settings.section.device": "
|
|
753
|
+
"settings.section.device": "この端末",
|
|
747
754
|
"settings.group.general": "一般",
|
|
748
|
-
"settings.group.device": "
|
|
755
|
+
"settings.group.device": "この端末",
|
|
749
756
|
"settings.group.values": "設定値",
|
|
750
757
|
"settings.group.advanced": "詳細設定",
|
|
751
758
|
"settings.group.actions": "操作",
|
|
752
759
|
"settings.overview.title": "ひと目で確認",
|
|
753
|
-
"settings.overview.copy": "
|
|
760
|
+
"settings.overview.copy": "この端末で準備できていること、まだ必要な設定、次にやることをまとめて確認できます。",
|
|
754
761
|
"settings.summary.notifications": "通知",
|
|
755
762
|
"settings.summary.install": "ホーム画面",
|
|
756
763
|
"settings.summary.pairing": "ペアリング",
|
|
757
764
|
"settings.nextStep.eyebrow": "次の一手",
|
|
758
765
|
"settings.nextStep.enableNotifications.title": "通知を有効にする",
|
|
759
|
-
"settings.nextStep.enableNotifications.copy": "
|
|
766
|
+
"settings.nextStep.enableNotifications.copy": "この端末で Web Push を有効にすると、承認や質問をバックグラウンドでも受け取れます。",
|
|
760
767
|
"settings.nextStep.serverDisabled.title": "サーバー側の設定がまだ必要です",
|
|
761
|
-
"settings.nextStep.serverDisabled.copy": "この viveworker サーバーでは Web Push
|
|
768
|
+
"settings.nextStep.serverDisabled.copy": "この viveworker サーバーでは Web Push がまだ有効ではないため、この端末からは購読できません。",
|
|
762
769
|
"settings.nextStep.openHttps.title": "HTTPS 版を開く",
|
|
763
|
-
"settings.nextStep.openHttps.copy": "
|
|
770
|
+
"settings.nextStep.openHttps.copy": "この端末で通知を有効にするには、このページを HTTPS で開く必要があります。",
|
|
764
771
|
"settings.nextStep.install.title": "ホーム画面に追加する",
|
|
765
|
-
"settings.nextStep.install.copy": "
|
|
772
|
+
"settings.nextStep.install.copy": "アプリをホーム画面に追加し、インストール版を開き直してから通知を有効にしてください。",
|
|
766
773
|
"settings.nextStep.permissionBlocked.title": "通知がブロックされています",
|
|
767
|
-
"settings.nextStep.permissionBlocked.copy": "
|
|
774
|
+
"settings.nextStep.permissionBlocked.copy": "端末の設定で通知を再許可してから、ここへ戻ってもう一度確認してください。",
|
|
768
775
|
"settings.nextStep.test.title": "準備は完了です",
|
|
769
|
-
"settings.nextStep.test.copy": "
|
|
776
|
+
"settings.nextStep.test.copy": "テスト通知を送って、この端末が viveworker の push を受け取れるか確認できます。",
|
|
770
777
|
"settings.status.notAvailable": "未対応",
|
|
771
778
|
"settings.status.unsupported": "非対応",
|
|
772
779
|
"settings.status.needsHttps": "HTTPS が必要",
|
|
@@ -784,7 +791,7 @@ const translations = {
|
|
|
784
791
|
"settings.notifications.serverDisabled": "サーバー側で Web Push がまだ有効化されていません。",
|
|
785
792
|
"settings.notifications.openHttps": "通知を有効にする前に、HTTPS の viveworker を開いてください。",
|
|
786
793
|
"settings.notifications.openHomeScreen":
|
|
787
|
-
"
|
|
794
|
+
"この端末で通知を有効にする前に、ホーム画面に追加したアプリを開いてください。",
|
|
788
795
|
"settings.action.enableNotifications": "通知を有効化",
|
|
789
796
|
"settings.action.disableNotifications": "通知を無効化",
|
|
790
797
|
"settings.action.sendTest": "テスト通知を送信",
|
|
@@ -793,7 +800,7 @@ const translations = {
|
|
|
793
800
|
"settings.action.removeThisDevice": "この端末を削除",
|
|
794
801
|
"settings.install.title": "ホーム画面に追加",
|
|
795
802
|
"settings.install.copy":
|
|
796
|
-
"
|
|
803
|
+
"いちばん使いやすい体験にするには、viveworker をホーム画面に追加して、インストール版を開いてください。",
|
|
797
804
|
"settings.device.title": "Devices",
|
|
798
805
|
"settings.device.copy": "この LAN 上で viveworker を開ける信頼済み端末を管理します。",
|
|
799
806
|
"settings.device.count": ({ count }) => `${count} 台の端末`,
|
|
@@ -803,7 +810,7 @@ const translations = {
|
|
|
803
810
|
"settings.device.emptyCurrent": "この端末の情報はまだ表示できません。",
|
|
804
811
|
"settings.device.emptyOther": "他に有効な信頼済み端末はありません。",
|
|
805
812
|
"settings.device.addAnother.title": "端末を追加する",
|
|
806
|
-
"settings.device.addAnother.heading": "
|
|
813
|
+
"settings.device.addAnother.heading": "新しい端末 / ブラウザを追加",
|
|
807
814
|
"settings.device.addAnother.copy":
|
|
808
815
|
"別の端末を追加したいときだけ、Mac で次のコマンドを実行します。通常の app URL は変わりません。",
|
|
809
816
|
"settings.device.addAnother.commandLabel": "Mac で実行",
|
|
@@ -816,12 +823,12 @@ const translations = {
|
|
|
816
823
|
"settings.device.mode.standalone": "ホーム画面アプリ",
|
|
817
824
|
"settings.device.mode.browser": "ブラウザ",
|
|
818
825
|
"settings.language.title": "言語",
|
|
819
|
-
"settings.language.copy": "
|
|
826
|
+
"settings.language.copy": "この端末で viveworker を表示する言語を選びます。",
|
|
820
827
|
"settings.pairing.title": "ペアリング",
|
|
821
|
-
"settings.pairing.copy": "
|
|
828
|
+
"settings.pairing.copy": "この端末はローカルの Codex desktop session とペアリングされています。",
|
|
822
829
|
"settings.pairing.connected": "ペアリング済み",
|
|
823
830
|
"settings.technical.title": "技術情報",
|
|
824
|
-
"settings.technical.copy": "
|
|
831
|
+
"settings.technical.copy": "この端末の接続状態や通知状態を確認できます。",
|
|
825
832
|
"settings.technical.copyShort": "接続や通知の診断情報です。",
|
|
826
833
|
"settings.row.deviceId": "Device ID",
|
|
827
834
|
"settings.row.pairedAt": "初回ペアリング",
|
|
@@ -848,11 +855,11 @@ const translations = {
|
|
|
848
855
|
"notice.loggedOutKeepTrusted": "ログアウトしました。この端末の信頼は維持されるので、次回は再 pairing なしで戻れます。",
|
|
849
856
|
"notice.loggedOutDeviceRemoved": "ログアウトし、この端末の信頼も削除しました。次回は再 pairing が必要です。",
|
|
850
857
|
"error.networkHint":
|
|
851
|
-
"最新の HTTPS URL
|
|
858
|
+
"最新の HTTPS URL をブラウザで開き直して、必要ならホーム画面アプリを追加し直してください。",
|
|
852
859
|
"error.webPushDisabled": "サーバー側で Web Push が有効ではありません。",
|
|
853
860
|
"error.notificationsRequireHttps": "通知には HTTPS が必要です。",
|
|
854
861
|
"error.pushUnsupported": "このブラウザは Web Push に対応していません。",
|
|
855
|
-
"error.openHomeScreen": "
|
|
862
|
+
"error.openHomeScreen": "この端末で通知を有効にする前に、ホーム画面の viveworker を開いてください。",
|
|
856
863
|
"error.notificationPermission": ({ status }) => `通知権限は ${status} です。`,
|
|
857
864
|
"error.pushServerNotReady": "サーバー側で Web Push の準備ができていません。",
|
|
858
865
|
"error.serviceWorkerUnavailable": "Service Worker は利用できません。",
|
|
@@ -897,10 +904,11 @@ const translations = {
|
|
|
897
904
|
"server.action.reject": "拒否",
|
|
898
905
|
"server.action.implement": "実装する",
|
|
899
906
|
"server.message.commandApprovalNeeded": "コマンド実行の承認が必要です。",
|
|
907
|
+
"server.message.commandLabel": "コマンド",
|
|
900
908
|
"server.message.fileApprovalNeeded": "ファイル変更の承認が必要です。",
|
|
901
909
|
"server.message.commandPrefix": ({ command }) => `Command: ${command}`,
|
|
902
910
|
"server.message.pathPrefix": ({ path }) => `Path: ${path}`,
|
|
903
|
-
"server.message.approveOnMac": "Mac で承認してください:
|
|
911
|
+
"server.message.approveOnMac": "Mac で承認してください: この端末の通知は codex.app の鏡像です。",
|
|
904
912
|
"server.message.approvalNeededInCodex": "Codex で承認が必要です。",
|
|
905
913
|
"server.message.extraApprovals": ({ count }) => `追加の承認: ${count}`,
|
|
906
914
|
"server.message.taskFinished": "タスクが完了しました。",
|
|
@@ -918,7 +926,7 @@ const translations = {
|
|
|
918
926
|
"server.confirm.approve": "この承認を確定して Codex を続行します。",
|
|
919
927
|
"server.confirm.reject": "この操作を拒否して Codex に続行しないよう伝えます。",
|
|
920
928
|
"server.pushTest.title": "viveworker テスト通知",
|
|
921
|
-
"server.pushTest.body": "
|
|
929
|
+
"server.pushTest.body": "この端末で通知が動作しています。",
|
|
922
930
|
"server.page.notFoundTitle": "見つかりません",
|
|
923
931
|
"server.page.notFoundBody": "この承認リンクは存在しません。",
|
|
924
932
|
"server.page.notFoundHint": "このページは閉じて大丈夫です。",
|
|
@@ -961,18 +969,18 @@ const translations = {
|
|
|
961
969
|
"cli.setup.webPushEnabled": "Web Push: enabled (HTTPS)",
|
|
962
970
|
"cli.setup.webPushDisabled": "Web Push: disabled",
|
|
963
971
|
"cli.setup.completePending": "viveworker の setup は完了しましたが、まだ health 応答は確認できていません。",
|
|
964
|
-
"cli.setup.caFlow.title": "
|
|
965
|
-
"cli.setup.caFlow.step1": "1. 下の CA download URL を開くか QR
|
|
966
|
-
"cli.setup.caFlow.step2": "2. rootCA.pem
|
|
972
|
+
"cli.setup.caFlow.title": "端末で viveworker を開く前に、必要ならローカルの証明書をインストールしてください:",
|
|
973
|
+
"cli.setup.caFlow.step1": "1. 下の CA download URL を開くか QR を読み取り、端末で rootCA.pem を取得します。",
|
|
974
|
+
"cli.setup.caFlow.step2": "2. rootCA.pem をインストールし、必要なら端末側でその証明書を信頼します。",
|
|
967
975
|
"cli.setup.caFlow.step3": "3. そのあと、このターミナルで Enter を押すと viveworker の接続先 URL と Pairing QR を表示します。",
|
|
968
976
|
"cli.setup.instructions.https":
|
|
969
|
-
"
|
|
977
|
+
"ブラウザで HTTPS URL を開き、必要ならローカル CA を信頼してから、ホーム画面に追加して通知を有効にしてください。",
|
|
970
978
|
"cli.setup.instructions.afterCa":
|
|
971
|
-
"
|
|
979
|
+
"次に端末のブラウザで Pairing URL を開き、必要ならペアリングしてから、viveworker をホーム画面に追加してください。",
|
|
972
980
|
"cli.setup.instructions.insecureHttpLan":
|
|
973
|
-
"HTTP URL は、LAN
|
|
981
|
+
"HTTP URL は、LAN 上の平文アクセスを明示的に許可した場合だけブラウザで開いてください。共有 Wi-Fi では非推奨です。",
|
|
974
982
|
"cli.setup.instructions.localOnlyHttp":
|
|
975
|
-
"この setup はローカル開発用の loopback-only HTTP
|
|
983
|
+
"この setup はローカル開発用の loopback-only HTTP です。別の端末から使うなら既定の HTTPS setup か --allow-insecure-http-lan を使ってください。",
|
|
976
984
|
"logout.confirm.title": "ログアウト方法を選んでください",
|
|
977
985
|
"logout.confirm.copy": "session だけ終えることも、この端末の信頼ごと削除して次回 pairing 必須にすることもできます。",
|
|
978
986
|
"logout.confirm.keepTrustedTitle": "ログアウトのみ",
|
|
@@ -988,6 +996,12 @@ const translations = {
|
|
|
988
996
|
"cli.setup.qrPairing": "Pairing QR:",
|
|
989
997
|
"cli.setup.qrCaDownload": "rootCA.pem download QR (IP):",
|
|
990
998
|
"cli.setup.prompt.continueToApp": "viveworker の接続先 URL を表示するには Enter を押してください。",
|
|
999
|
+
"cli.setup.error.mkcertInstallFailed": "Homebrew で mkcert をインストールできませんでした。",
|
|
1000
|
+
"cli.setup.error.mkcertInstallPermission":
|
|
1001
|
+
"Homebrew は見つかりましたが、その prefix に現在のユーザーの書き込み権限がないため、viveworker が mkcert を自動インストールできませんでした。",
|
|
1002
|
+
"cli.setup.error.mkcertInstallNext":
|
|
1003
|
+
"`npx viveworker setup --install-mkcert` をやり直す前に Homebrew の権限を直すか、先に mkcert を手動でインストールしてください。",
|
|
1004
|
+
"cli.setup.error.mkcertInstallExample": "例: brew install mkcert && mkcert -install",
|
|
991
1005
|
"cli.start.progress.prepare": "保存済みの viveworker 設定を読み込んでいます...",
|
|
992
1006
|
"cli.start.progress.refreshPairing": "期限切れの pairing 情報を更新しています...",
|
|
993
1007
|
"cli.start.progress.launchd": "launchd サービスを起動しています...",
|