@jixoai/gh-runner 0.2.0 → 0.2.2
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/dist/main.mjs +28 -28
- package/dist/ui/index.html +12 -1
- package/package.json +2 -1
package/dist/main.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { arch, homedir, platform } from "node:os";
|
|
3
3
|
import { mkdir, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
-
import { execFile, spawn } from "node:child_process";
|
|
5
4
|
import { createTray } from "opentray";
|
|
6
5
|
import { attachDialog } from "@opentray/ext-dialog";
|
|
6
|
+
import { attachOpener } from "@opentray/ext-opener";
|
|
7
7
|
import { dirname, isAbsolute, join } from "node:path";
|
|
8
8
|
import { createWriteStream, existsSync } from "node:fs";
|
|
9
|
+
import { execFile, spawn } from "node:child_process";
|
|
9
10
|
import { promisify } from "node:util";
|
|
10
11
|
import { Readable } from "node:stream";
|
|
11
12
|
import { pipeline } from "node:stream/promises";
|
|
@@ -223,21 +224,6 @@ function defaultSearchDirs() {
|
|
|
223
224
|
if (IS_WIN) return ["C:\\actions-runner", homedir()];
|
|
224
225
|
return [homedir()];
|
|
225
226
|
}
|
|
226
|
-
/** 在系统文件管理器中揭示目录/文件。 */
|
|
227
|
-
function revealInFileManager(path) {
|
|
228
|
-
if (PLATFORM === "darwin") return {
|
|
229
|
-
file: "open",
|
|
230
|
-
args: ["-R", path]
|
|
231
|
-
};
|
|
232
|
-
if (IS_WIN) return {
|
|
233
|
-
file: "explorer",
|
|
234
|
-
args: [`/select,${path}`]
|
|
235
|
-
};
|
|
236
|
-
return {
|
|
237
|
-
file: "xdg-open",
|
|
238
|
-
args: [path]
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
227
|
/** 平台路径分段(win32 反斜杠)。 */
|
|
242
228
|
function basenameOf(dir) {
|
|
243
229
|
const parts = dir.split(/[\\/]+/).filter(Boolean);
|
|
@@ -461,14 +447,23 @@ async function stopProcessGroup(pid, graceMs = 8e3) {
|
|
|
461
447
|
// src/config.ts
|
|
462
448
|
const DEFAULT_SEARCH_DIRS = defaultSearchDirs();
|
|
463
449
|
function configDir() {
|
|
450
|
+
if (process.platform === "win32") {
|
|
451
|
+
const appData = process.env.APPDATA ?? join(homedir(), "AppData", "Roaming");
|
|
452
|
+
return join(appData, "gh-runner");
|
|
453
|
+
}
|
|
464
454
|
return join(homedir(), "Library", "Application Support", "gh-runner");
|
|
465
455
|
}
|
|
466
456
|
function configPath() {
|
|
467
457
|
return join(configDir(), "config.json");
|
|
468
458
|
}
|
|
469
|
-
/** 形状校验:损坏的实例项直接丢弃(手编配置/撕裂写的容错),不让 boot 崩掉。
|
|
459
|
+
/** 形状校验:损坏的实例项直接丢弃(手编配置/撕裂写的容错),不让 boot 崩掉。
|
|
460
|
+
* 路径合法性平台无关(POSIX 绝对 / win32 盘符 / UNC 均可)——config 是本机产物,
|
|
461
|
+
* 但 Windows 盘符路径曾被 startsWith("/") 全量误杀并回写空清单(v0.2.2 实证事故)。 */
|
|
462
|
+
function isAbsoluteish(p) {
|
|
463
|
+
return p.startsWith("/") || /^[A-Za-z]:[\\/]/.test(p) || p.startsWith("\\\\");
|
|
464
|
+
}
|
|
470
465
|
function validInstance(item) {
|
|
471
|
-
return typeof item === "object" && item !== null && typeof item.dir === "string" && item.dir
|
|
466
|
+
return typeof item === "object" && item !== null && typeof item.dir === "string" && isAbsoluteish(item.dir) && typeof item.id === "string";
|
|
472
467
|
}
|
|
473
468
|
async function loadConfig() {
|
|
474
469
|
try {
|
|
@@ -1226,6 +1221,7 @@ var UiHost = class {
|
|
|
1226
1221
|
webviewTray;
|
|
1227
1222
|
onOpenDiag;
|
|
1228
1223
|
pickPackageFile;
|
|
1224
|
+
openExternal;
|
|
1229
1225
|
#window = null;
|
|
1230
1226
|
#lastSnapshot = null;
|
|
1231
1227
|
#ackedSeq = 0;
|
|
@@ -1237,11 +1233,12 @@ var UiHost = class {
|
|
|
1237
1233
|
/** 单飞守卫:hello 全量重推与去抖推送并发交错会让同一批日志行重复送达页面。 */
|
|
1238
1234
|
#pushInFlight = false;
|
|
1239
1235
|
#pushPending = false;
|
|
1240
|
-
constructor(fleet, webviewTray, onOpenDiag, pickPackageFile) {
|
|
1236
|
+
constructor(fleet, webviewTray, onOpenDiag, pickPackageFile, openExternal) {
|
|
1241
1237
|
this.fleet = fleet;
|
|
1242
1238
|
this.webviewTray = webviewTray;
|
|
1243
1239
|
this.onOpenDiag = onOpenDiag;
|
|
1244
1240
|
this.pickPackageFile = pickPackageFile;
|
|
1241
|
+
this.openExternal = openExternal;
|
|
1245
1242
|
}
|
|
1246
1243
|
async open() {
|
|
1247
1244
|
if (this.#window) {
|
|
@@ -1431,6 +1428,13 @@ var UiHost = class {
|
|
|
1431
1428
|
case "open-diag":
|
|
1432
1429
|
this.onOpenDiag(cmd.id);
|
|
1433
1430
|
break;
|
|
1431
|
+
case "open-url":
|
|
1432
|
+
try {
|
|
1433
|
+
await this.openExternal(cmd.url);
|
|
1434
|
+
} catch (err) {
|
|
1435
|
+
console.error("[gh-runner] 打开链接失败:", err);
|
|
1436
|
+
}
|
|
1437
|
+
break;
|
|
1434
1438
|
case "register":
|
|
1435
1439
|
await this.fleet.register({
|
|
1436
1440
|
dir: cmd.dir,
|
|
@@ -1459,12 +1463,6 @@ function extendWebview(tray) {
|
|
|
1459
1463
|
}
|
|
1460
1464
|
// src/main.ts
|
|
1461
1465
|
const APP_ID = "com.jixoai.gh-runner";
|
|
1462
|
-
async function revealPath(path) {
|
|
1463
|
-
const { file, args } = revealInFileManager(path);
|
|
1464
|
-
await new Promise((resolve) => {
|
|
1465
|
-
execFile(file, args, () => resolve());
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
1466
|
/** 导入本地分发包:系统文件选择框(ext-dialog;darwin NSOpenPanel / win32 对话框)。 */
|
|
1469
1467
|
async function pickPackageFile(tray) {
|
|
1470
1468
|
try {
|
|
@@ -1624,10 +1622,12 @@ async function main() {
|
|
|
1624
1622
|
},
|
|
1625
1623
|
quit: () => void shutdown()
|
|
1626
1624
|
});
|
|
1627
|
-
const
|
|
1625
|
+
const webviewTray = extendWebview(tray);
|
|
1626
|
+
const opener = attachOpener(tray);
|
|
1627
|
+
const uiHost = new UiHost(fleet, webviewTray, (id) => {
|
|
1628
1628
|
const view = fleet.view(id);
|
|
1629
|
-
if (view)
|
|
1630
|
-
}, () => pickPackageFile(tray));
|
|
1629
|
+
if (view) opener.revealInFolder(`${view.dir}/_diag`).catch((err) => console.error("[gh-runner] 揭示目录失败:", err));
|
|
1630
|
+
}, () => pickPackageFile(tray), (url) => opener.open(url));
|
|
1631
1631
|
fleet.onSnapshot((snap) => {
|
|
1632
1632
|
projection.update(snap);
|
|
1633
1633
|
uiHost.push(snap);
|
package/dist/ui/index.html
CHANGED
|
@@ -126,6 +126,7 @@
|
|
|
126
126
|
.field input:focus { outline: 2px solid color-mix(in srgb, var(--run) 55%, transparent); border-color: transparent; }
|
|
127
127
|
.field .hint { font-size: 10.5px; color: var(--ink-2); }
|
|
128
128
|
.field .hint a { color: var(--run); text-decoration: none; }
|
|
129
|
+
a.ext-link { cursor: pointer; }
|
|
129
130
|
.pkg-row { display: flex; gap: 6px; }
|
|
130
131
|
.pkg-row select {
|
|
131
132
|
flex: 1; min-width: 0; font: inherit; padding: 5px 7px; border: 1px solid var(--line);
|
|
@@ -196,7 +197,7 @@
|
|
|
196
197
|
<div class="field">
|
|
197
198
|
<label>注册 Token(org 设置页生成,一次性)</label>
|
|
198
199
|
<input name="token" type="password" placeholder="粘贴一次性注册 Token" required>
|
|
199
|
-
<span class="hint">获取:<a href="https://github.com/organizations/jixoai/settings/actions/runners/new"
|
|
200
|
+
<span class="hint">获取:<a href="#" id="link-runner-new" data-url="https://github.com/organizations/jixoai/settings/actions/runners/new">org runners 新建页</a>(系统浏览器打开;自动解包本机已有分发包)</span>
|
|
200
201
|
</div>
|
|
201
202
|
<div class="field">
|
|
202
203
|
<label>Labels</label>
|
|
@@ -329,6 +330,16 @@ function renderPackages(packages, download) {
|
|
|
329
330
|
}
|
|
330
331
|
}
|
|
331
332
|
|
|
333
|
+
document.querySelectorAll("a.ext-link").forEach((a) => {
|
|
334
|
+
a.addEventListener("click", (e) => {
|
|
335
|
+
e.preventDefault();
|
|
336
|
+
const url = a.dataset.url;
|
|
337
|
+
if (!url) return;
|
|
338
|
+
const ipc = navigator.opentray && navigator.opentray.ipc;
|
|
339
|
+
if (ipc) void ipc.postMessage({ type: "open-url", url });
|
|
340
|
+
else window.open(url, "_blank");
|
|
341
|
+
});
|
|
342
|
+
});
|
|
332
343
|
$("#pkg-download").addEventListener("click", () => send({ type: "download-latest" }));
|
|
333
344
|
$("#pkg-import").addEventListener("click", () => send({ type: "import-package" }));
|
|
334
345
|
document.querySelector('#register-form input[name="labels"]').addEventListener("input", () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jixoai/gh-runner",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Menu-bar (tray) manager for multiple GitHub Actions self-hosted runners: discover, register from downloaded/imported packages, start/stop, and live job-result analysis. macOS & Windows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@opentray/ext-dialog": "^0.36.0",
|
|
48
|
+
"@opentray/ext-opener": "^0.36.0",
|
|
48
49
|
"@opentray/ext-webview": "^0.36.0",
|
|
49
50
|
"opentray": "^0.36.0"
|
|
50
51
|
},
|