sync-omo-config 1.0.1 → 1.1.0

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.
Files changed (2) hide show
  1. package/dist/index.js +99 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,9 +4,61 @@ import { homedir } from "os";
4
4
  import { existsSync } from "fs";
5
5
  import { mkdir } from "fs/promises";
6
6
  import path from "path";
7
+ function notifyDesktop(title, message) {
8
+ const platform = process.platform;
9
+ const esc = (value) => value.replaceAll(`
10
+ `, " ").replaceAll('"', "\\\"");
11
+ const escps = (value) => value.replaceAll(`
12
+ `, " ").replaceAll("'", "''");
13
+ if (platform === "darwin") {
14
+ const script = [
15
+ `display notification "${esc(message)}" with title "${esc(title)}"`,
16
+ `display alert "${esc(title)}" message "${esc(message)}" giving up after 10`
17
+ ].join(`
18
+ `);
19
+ const proc = Bun.spawn(["/usr/bin/osascript", "-e", script], { stdout: "ignore", stderr: "ignore" });
20
+ proc.exited.catch(() => {
21
+ return;
22
+ });
23
+ return;
24
+ }
25
+ if (platform === "linux") {
26
+ const proc = Bun.spawn(["/usr/bin/env", "notify-send", "--urgency=critical", title, message], { stdout: "ignore", stderr: "ignore" });
27
+ proc.exited.catch(() => {
28
+ return;
29
+ });
30
+ return;
31
+ }
32
+ if (platform === "win32") {
33
+ const script = [
34
+ "Add-Type -AssemblyName System.Windows.Forms",
35
+ "$n = New-Object System.Windows.Forms.NotifyIcon",
36
+ "$n.Icon = [System.Drawing.SystemIcons]::Warning",
37
+ `$n.BalloonTipTitle = '${escps(title)}'`,
38
+ `$n.BalloonTipText = '${escps(message)}'`,
39
+ "$n.BalloonTipIcon = 'Error'",
40
+ "$n.Visible = $true",
41
+ "$n.ShowBalloonTip(15000)",
42
+ "Start-Sleep -Seconds 16",
43
+ "$n.Dispose()"
44
+ ].join("; ");
45
+ const proc = Bun.spawn(["powershell", "-NoProfile", "-Command", script], {
46
+ stdout: "ignore",
47
+ stderr: "ignore"
48
+ });
49
+ proc.exited.catch(() => {
50
+ return;
51
+ });
52
+ return;
53
+ }
54
+ console.warn(`[sync-omo-config] Desktop notify not supported on ${platform}`);
55
+ }
7
56
  function getDataDir() {
8
57
  const home = homedir();
9
- const xdgData = process.env.XDG_DATA_HOME || path.join(home, ".local", "share");
58
+ const xdgData = process.env.XDG_DATA_HOME || (home ? path.join(home, ".local", "share") : undefined);
59
+ if (!xdgData) {
60
+ throw new Error("[sync-omo-config] Cannot determine data directory: HOME is not set");
61
+ }
10
62
  return path.join(xdgData, "opencode");
11
63
  }
12
64
  function getConfigDir() {
@@ -58,23 +110,24 @@ async function syncOmoConfig() {
58
110
  const authFile = Bun.file(authPath);
59
111
  if (!await authFile.exists()) {
60
112
  console.log(`${LOG_PREFIX} auth.json not found at ${authPath}, skipping sync`);
61
- return;
113
+ return [];
62
114
  }
63
115
  let auth;
64
116
  try {
65
117
  auth = await authFile.json();
66
118
  } catch (e) {
67
119
  console.error(`${LOG_PREFIX} Failed to parse auth.json:`, e);
68
- return;
120
+ return [];
69
121
  }
70
122
  const omoConfigs = [];
123
+ const expiredServers = [];
71
124
  for (const [serverUrl, authInfo] of Object.entries(auth)) {
72
125
  if (!isWellKnownAuth(authInfo)) {
73
126
  continue;
74
127
  }
75
128
  console.log(`${LOG_PREFIX} Fetching config from ${serverUrl}`);
76
129
  try {
77
- const wellKnownUrl = `${serverUrl}/.well-known/opencode`;
130
+ const wellKnownUrl = serverUrl.includes("/.well-known/opencode") ? serverUrl : `${serverUrl}/.well-known/opencode`;
78
131
  const response = await fetch(wellKnownUrl, {
79
132
  headers: {
80
133
  Accept: "application/json"
@@ -85,6 +138,15 @@ async function syncOmoConfig() {
85
138
  continue;
86
139
  }
87
140
  const data = await response.json();
141
+ if (data.auth_status === "invalid" || data.auth_status === "reauth_required") {
142
+ console.warn(`${LOG_PREFIX} Auth expired for ${serverUrl}, will notify via TUI toast`);
143
+ notifyDesktop("OpenCode: \u6388\u6743\u5DF2\u5931\u6548", `\u670D\u52A1\u5668 ${serverUrl} \u7684\u6388\u6743\u5DF2\u8FC7\u671F\uFF0C\u5DF2\u81EA\u52A8\u6E05\u9664\u51ED\u8BC1\u3002\u8BF7\u91CD\u65B0\u767B\u5F55\u8BA4\u8BC1\u3002`);
144
+ expiredServers.push(serverUrl);
145
+ delete auth[serverUrl];
146
+ await Bun.write(authPath, JSON.stringify(auth, null, 2), { mode: 384 });
147
+ console.log(`${LOG_PREFIX} Removed expired credential for ${serverUrl} from auth.json`);
148
+ continue;
149
+ }
88
150
  if (data.omo && typeof data.omo === "object") {
89
151
  console.log(`${LOG_PREFIX} Found omo config from ${serverUrl}`);
90
152
  omoConfigs.push(data.omo);
@@ -96,7 +158,7 @@ async function syncOmoConfig() {
96
158
  }
97
159
  if (omoConfigs.length === 0) {
98
160
  console.log(`${LOG_PREFIX} No omo configs found, skipping write`);
99
- return;
161
+ return expiredServers;
100
162
  }
101
163
  console.log(`${LOG_PREFIX} Found ${omoConfigs.length} omo config(s)`);
102
164
  const configDir = getConfigDir();
@@ -119,12 +181,42 @@ async function syncOmoConfig() {
119
181
  }
120
182
  await Bun.write(omoPath, JSON.stringify(mergedConfig, null, 2));
121
183
  console.log(`${LOG_PREFIX} Successfully wrote oh-my-opencode.json to ${omoPath}`);
184
+ return expiredServers;
122
185
  } catch (error) {
123
186
  console.error(`${LOG_PREFIX} Unexpected error during sync:`, error);
187
+ return [];
124
188
  }
125
189
  }
126
- var SyncOmoConfigPlugin = async function SyncOmoConfigPlugin2() {
127
- await syncOmoConfig();
190
+ var SyncOmoConfigPlugin = async function SyncOmoConfigPlugin2(input) {
191
+ const expiredServers = await syncOmoConfig();
192
+ if (expiredServers.length === 0)
193
+ return {};
194
+ const pending = new Set(expiredServers);
195
+ const delays = [2000, 4000, 8000, 15000];
196
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
197
+ const notify = async () => {
198
+ for (const delay of delays) {
199
+ if (pending.size === 0)
200
+ return;
201
+ await sleep(delay);
202
+ for (const server of [...pending]) {
203
+ const ok = await input.client.tui.showToast({
204
+ body: {
205
+ title: "\u26A0 \u6388\u6743\u5DF2\u5931\u6548",
206
+ message: `\u670D\u52A1\u5668 ${server} \u7684\u6388\u6743\u5DF2\u8FC7\u671F\uFF0C\u5DF2\u81EA\u52A8\u6E05\u9664\u51ED\u8BC1\u3002\u8BF7\u91CD\u65B0\u767B\u5F55\u8BA4\u8BC1\u3002`,
207
+ variant: "error",
208
+ duration: 30000
209
+ }
210
+ }).then(() => true).catch((e) => {
211
+ console.error("[sync-omo-config] Failed to show toast:", e);
212
+ return false;
213
+ });
214
+ if (ok)
215
+ pending.delete(server);
216
+ }
217
+ }
218
+ };
219
+ notify();
128
220
  return {};
129
221
  };
130
222
  var src_default = SyncOmoConfigPlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sync-omo-config",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "OpenCode plugin to sync omo config from well-known endpoint to oh-my-opencode.json",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",