@particle-academy/fancy-term-host 0.2.3 → 0.3.1
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 +17 -0
- package/dist/{chunk-7NXYRMNA.js → chunk-2A4TFTNQ.js} +20 -3
- package/dist/chunk-2A4TFTNQ.js.map +1 -0
- package/dist/{chunk-DB3SIE53.js → chunk-Q4WC2TIE.js} +3 -3
- package/dist/{chunk-DB3SIE53.js.map → chunk-Q4WC2TIE.js.map} +1 -1
- package/dist/electron.cjs +176 -0
- package/dist/electron.cjs.map +1 -0
- package/dist/electron.d.cts +96 -0
- package/dist/electron.d.ts +96 -0
- package/dist/electron.js +167 -0
- package/dist/electron.js.map +1 -0
- package/dist/index.cjs +89 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +75 -30
- package/dist/index.js.map +1 -1
- package/dist/pty-host.cjs.map +1 -1
- package/dist/pty-host.js +1 -1
- package/dist/service.cjs.map +1 -1
- package/dist/service.js +2 -2
- package/docs/packaging.md +111 -0
- package/package.json +11 -1
- package/dist/chunk-7NXYRMNA.js.map +0 -1
package/dist/electron.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { spawnSync } from 'child_process';
|
|
4
|
+
|
|
5
|
+
// src/electron/after-pack.ts
|
|
6
|
+
function nodeAfterPackIo() {
|
|
7
|
+
return {
|
|
8
|
+
exists: (p) => {
|
|
9
|
+
try {
|
|
10
|
+
return fs.existsSync(p);
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
readdir: (d) => {
|
|
16
|
+
try {
|
|
17
|
+
return fs.readdirSync(d);
|
|
18
|
+
} catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
mkdirp: (d) => {
|
|
23
|
+
fs.mkdirSync(d, { recursive: true });
|
|
24
|
+
},
|
|
25
|
+
copyFile: (s, d) => {
|
|
26
|
+
fs.copyFileSync(s, d);
|
|
27
|
+
},
|
|
28
|
+
chmodExec: (p) => {
|
|
29
|
+
fs.chmodSync(p, 493);
|
|
30
|
+
},
|
|
31
|
+
run: (cmd, args) => {
|
|
32
|
+
const r = spawnSync(cmd, args, { encoding: "utf8" });
|
|
33
|
+
return {
|
|
34
|
+
code: r.status ?? -1,
|
|
35
|
+
stderr: (r.stderr ?? "") + (r.error ? String(r.error) : "")
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function resourcesDirs(appOutDir, platform, io) {
|
|
41
|
+
if (platform === "darwin") {
|
|
42
|
+
return io.readdir(appOutDir).filter((n) => n.endsWith(".app")).map((a) => path.join(appOutDir, a, "Contents", "Resources"));
|
|
43
|
+
}
|
|
44
|
+
return [path.join(appOutDir, "resources")];
|
|
45
|
+
}
|
|
46
|
+
function nodePtyCandidates(resources) {
|
|
47
|
+
return [
|
|
48
|
+
path.join(resources, "app.asar.unpacked", "node_modules", "node-pty"),
|
|
49
|
+
path.join(resources, "app", "node_modules", "node-pty")
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
function resolveNodePtyDir(appOutDir, platform, io) {
|
|
53
|
+
for (const res of resourcesDirs(appOutDir, platform, io)) {
|
|
54
|
+
for (const dir of nodePtyCandidates(res)) {
|
|
55
|
+
if (io.exists(dir)) return dir;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
var ARCH_ENUM_NAMES = {
|
|
61
|
+
0: "ia32",
|
|
62
|
+
1: "x64",
|
|
63
|
+
2: "armv7l",
|
|
64
|
+
3: "arm64",
|
|
65
|
+
4: "universal"
|
|
66
|
+
};
|
|
67
|
+
function targetArchName(context, opts) {
|
|
68
|
+
if (opts.arch) return opts.arch;
|
|
69
|
+
const a = context.arch;
|
|
70
|
+
if (typeof a === "string") return a;
|
|
71
|
+
if (typeof a === "number") return ARCH_ENUM_NAMES[a];
|
|
72
|
+
return void 0;
|
|
73
|
+
}
|
|
74
|
+
function findConptySource(nodePty, io, preferDir) {
|
|
75
|
+
const prebuilds = path.join(nodePty, "prebuilds");
|
|
76
|
+
if (preferDir) {
|
|
77
|
+
const c = path.join(prebuilds, preferDir, "conpty");
|
|
78
|
+
if (io.exists(path.join(c, "conpty.dll"))) return c;
|
|
79
|
+
}
|
|
80
|
+
for (const name of io.readdir(prebuilds)) {
|
|
81
|
+
const c = path.join(prebuilds, name, "conpty");
|
|
82
|
+
if (io.exists(path.join(c, "conpty.dll"))) return c;
|
|
83
|
+
}
|
|
84
|
+
const tp = path.join(nodePty, "third_party", "conpty");
|
|
85
|
+
if (io.exists(path.join(tp, "conpty.dll"))) return tp;
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
var CONPTY_ASSETS = ["conpty.dll", "OpenConsole.exe"];
|
|
89
|
+
function winFixConpty(nodePty, release, io, select = {}) {
|
|
90
|
+
const destDir = path.join(release, "conpty");
|
|
91
|
+
if (CONPTY_ASSETS.every((f) => io.exists(path.join(destDir, f)))) {
|
|
92
|
+
return { platform: "win32", action: "already-present", ok: true };
|
|
93
|
+
}
|
|
94
|
+
const src = select.conptySource ?? findConptySource(nodePty, io, select.archDir);
|
|
95
|
+
if (!src) {
|
|
96
|
+
return {
|
|
97
|
+
platform: "win32",
|
|
98
|
+
action: "failed",
|
|
99
|
+
ok: false,
|
|
100
|
+
detail: "conpty source not found (prebuilds/*/conpty or third_party/conpty)"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
io.mkdirp(destDir);
|
|
104
|
+
const copied = [];
|
|
105
|
+
for (const f of CONPTY_ASSETS) {
|
|
106
|
+
const s = path.join(src, f);
|
|
107
|
+
if (io.exists(s)) {
|
|
108
|
+
io.copyFile(s, path.join(destDir, f));
|
|
109
|
+
copied.push(f);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const ok = CONPTY_ASSETS.every((f) => io.exists(path.join(destDir, f)));
|
|
113
|
+
return {
|
|
114
|
+
platform: "win32",
|
|
115
|
+
action: ok ? "fixed" : "failed",
|
|
116
|
+
ok,
|
|
117
|
+
detail: `copied [${copied.join(", ")}] into build/Release/conpty from ${src}`
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function macSignSpawnHelper(release, io) {
|
|
121
|
+
const helper = path.join(release, "spawn-helper");
|
|
122
|
+
if (!io.exists(helper)) {
|
|
123
|
+
return { platform: "darwin", action: "skipped", ok: true, detail: "spawn-helper not present" };
|
|
124
|
+
}
|
|
125
|
+
const { code, stderr } = io.run("codesign", ["--force", "--sign", "-", helper]);
|
|
126
|
+
return code === 0 ? { platform: "darwin", action: "fixed", ok: true, detail: "ad-hoc signed spawn-helper" } : { platform: "darwin", action: "failed", ok: false, detail: `codesign failed: ${stderr.trim()}` };
|
|
127
|
+
}
|
|
128
|
+
function linuxChmodSpawnHelper(release, io) {
|
|
129
|
+
const helper = path.join(release, "spawn-helper");
|
|
130
|
+
if (!io.exists(helper)) {
|
|
131
|
+
return { platform: "linux", action: "skipped", ok: true, detail: "spawn-helper not present" };
|
|
132
|
+
}
|
|
133
|
+
io.chmodExec(helper);
|
|
134
|
+
return { platform: "linux", action: "fixed", ok: true, detail: "chmod +x spawn-helper" };
|
|
135
|
+
}
|
|
136
|
+
async function fancyTermAfterPack(context, opts = {}, io = nodeAfterPackIo()) {
|
|
137
|
+
const platform = opts.platform ?? context.electronPlatformName;
|
|
138
|
+
const nodePty = opts.nodePtyDir ?? resolveNodePtyDir(context.appOutDir, platform, io);
|
|
139
|
+
if (!nodePty) {
|
|
140
|
+
return {
|
|
141
|
+
platform,
|
|
142
|
+
action: "skipped",
|
|
143
|
+
ok: false,
|
|
144
|
+
detail: "node-pty not found under appOutDir (adjust asarUnpack or pass nodePtyDir)"
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const release = path.join(nodePty, "build", "Release");
|
|
148
|
+
switch (platform) {
|
|
149
|
+
case "win32": {
|
|
150
|
+
const archName = targetArchName(context, opts);
|
|
151
|
+
return winFixConpty(nodePty, release, io, {
|
|
152
|
+
archDir: archName ? `${platform}-${archName}` : null,
|
|
153
|
+
conptySource: opts.conptySource
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
case "darwin":
|
|
157
|
+
return macSignSpawnHelper(release, io);
|
|
158
|
+
case "linux":
|
|
159
|
+
return linuxChmodSpawnHelper(release, io);
|
|
160
|
+
default:
|
|
161
|
+
return { platform, action: "skipped", ok: true, detail: "no packaging fix needed" };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export { fancyTermAfterPack, nodeAfterPackIo, resolveNodePtyDir };
|
|
166
|
+
//# sourceMappingURL=electron.js.map
|
|
167
|
+
//# sourceMappingURL=electron.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/electron/after-pack.ts"],"names":[],"mappings":";;;;;AA2FO,SAAS,eAAA,GAA+B;AAC3C,EAAA,OAAO;AAAA,IACH,MAAA,EAAQ,CAAC,CAAA,KAAM;AACX,MAAA,IAAI;AACA,QAAA,OAAO,EAAA,CAAG,WAAW,CAAC,CAAA;AAAA,MAC1B,CAAA,CAAA,MAAQ;AACJ,QAAA,OAAO,KAAA;AAAA,MACX;AAAA,IACJ,CAAA;AAAA,IACA,OAAA,EAAS,CAAC,CAAA,KAAM;AACZ,MAAA,IAAI;AACA,QAAA,OAAO,EAAA,CAAG,YAAY,CAAC,CAAA;AAAA,MAC3B,CAAA,CAAA,MAAQ;AACJ,QAAA,OAAO,EAAC;AAAA,MACZ;AAAA,IACJ,CAAA;AAAA,IACA,MAAA,EAAQ,CAAC,CAAA,KAAM;AACX,MAAA,EAAA,CAAG,SAAA,CAAU,CAAA,EAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA,IACvC,CAAA;AAAA,IACA,QAAA,EAAU,CAAC,CAAA,EAAG,CAAA,KAAM;AAChB,MAAA,EAAA,CAAG,YAAA,CAAa,GAAG,CAAC,CAAA;AAAA,IACxB,CAAA;AAAA,IACA,SAAA,EAAW,CAAC,CAAA,KAAM;AACd,MAAA,EAAA,CAAG,SAAA,CAAU,GAAG,GAAK,CAAA;AAAA,IACzB,CAAA;AAAA,IACA,GAAA,EAAK,CAAC,GAAA,EAAK,IAAA,KAAS;AAChB,MAAA,MAAM,IAAI,SAAA,CAAU,GAAA,EAAK,MAAM,EAAE,QAAA,EAAU,QAAQ,CAAA;AACnD,MAAA,OAAO;AAAA,QACH,IAAA,EAAM,EAAE,MAAA,IAAU,EAAA;AAAA,QAClB,MAAA,EAAA,CAAS,EAAE,MAAA,IAAU,EAAA,KAAO,EAAE,KAAA,GAAQ,MAAA,CAAO,CAAA,CAAE,KAAK,CAAA,GAAI,EAAA;AAAA,OAC5D;AAAA,IACJ;AAAA,GACJ;AACJ;AAGA,SAAS,aAAA,CAAc,SAAA,EAAmB,QAAA,EAAkB,EAAA,EAA2B;AACnF,EAAA,IAAI,aAAa,QAAA,EAAU;AAEvB,IAAA,OAAO,EAAA,CACF,QAAQ,SAAS,CAAA,CACjB,OAAO,CAAC,CAAA,KAAM,EAAE,QAAA,CAAS,MAAM,CAAC,CAAA,CAChC,GAAA,CAAI,CAAC,CAAA,KAAM,IAAA,CAAK,KAAK,SAAA,EAAW,CAAA,EAAG,UAAA,EAAY,WAAW,CAAC,CAAA;AAAA,EACpE;AAEA,EAAA,OAAO,CAAC,IAAA,CAAK,IAAA,CAAK,SAAA,EAAW,WAAW,CAAC,CAAA;AAC7C;AAGA,SAAS,kBAAkB,SAAA,EAA6B;AACpD,EAAA,OAAO;AAAA,IACH,IAAA,CAAK,IAAA,CAAK,SAAA,EAAW,mBAAA,EAAqB,gBAAgB,UAAU,CAAA;AAAA,IACpE,IAAA,CAAK,IAAA,CAAK,SAAA,EAAW,KAAA,EAAO,gBAAgB,UAAU;AAAA,GAC1D;AACJ;AAGO,SAAS,iBAAA,CACZ,SAAA,EACA,QAAA,EACA,EAAA,EACa;AACb,EAAA,KAAA,MAAW,GAAA,IAAO,aAAA,CAAc,SAAA,EAAW,QAAA,EAAU,EAAE,CAAA,EAAG;AACtD,IAAA,KAAA,MAAW,GAAA,IAAO,iBAAA,CAAkB,GAAG,CAAA,EAAG;AACtC,MAAA,IAAI,EAAA,CAAG,MAAA,CAAO,GAAG,CAAA,EAAG,OAAO,GAAA;AAAA,IAC/B;AAAA,EACJ;AACA,EAAA,OAAO,IAAA;AACX;AAGA,IAAM,eAAA,GAA0C;AAAA,EAC5C,CAAA,EAAG,MAAA;AAAA,EACH,CAAA,EAAG,KAAA;AAAA,EACH,CAAA,EAAG,QAAA;AAAA,EACH,CAAA,EAAG,OAAA;AAAA,EACH,CAAA,EAAG;AACP,CAAA;AAGA,SAAS,cAAA,CAAe,SAA2B,IAAA,EAA4C;AAC3F,EAAA,IAAI,IAAA,CAAK,IAAA,EAAM,OAAO,IAAA,CAAK,IAAA;AAC3B,EAAA,MAAM,IAAI,OAAA,CAAQ,IAAA;AAClB,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,OAAO,CAAA;AAClC,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,OAAO,gBAAgB,CAAC,CAAA;AACnD,EAAA,OAAO,MAAA;AACX;AAaA,SAAS,gBAAA,CAAiB,OAAA,EAAiB,EAAA,EAAiB,SAAA,EAA0C;AAClG,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,WAAW,CAAA;AAChD,EAAA,IAAI,SAAA,EAAW;AACX,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,SAAA,EAAW,WAAW,QAAQ,CAAA;AAClD,IAAA,IAAI,EAAA,CAAG,OAAO,IAAA,CAAK,IAAA,CAAK,GAAG,YAAY,CAAC,GAAG,OAAO,CAAA;AAAA,EACtD;AACA,EAAA,KAAA,MAAW,IAAA,IAAQ,EAAA,CAAG,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtC,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,SAAA,EAAW,MAAM,QAAQ,CAAA;AAC7C,IAAA,IAAI,EAAA,CAAG,OAAO,IAAA,CAAK,IAAA,CAAK,GAAG,YAAY,CAAC,GAAG,OAAO,CAAA;AAAA,EACtD;AACA,EAAA,MAAM,EAAA,GAAK,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,eAAe,QAAQ,CAAA;AACrD,EAAA,IAAI,EAAA,CAAG,OAAO,IAAA,CAAK,IAAA,CAAK,IAAI,YAAY,CAAC,GAAG,OAAO,EAAA;AACnD,EAAA,OAAO,IAAA;AACX;AAEA,IAAM,aAAA,GAAgB,CAAC,YAAA,EAAc,iBAAiB,CAAA;AAEtD,SAAS,aACL,OAAA,EACA,OAAA,EACA,EAAA,EACA,MAAA,GAAoE,EAAC,EACtD;AACf,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,QAAQ,CAAA;AAC3C,EAAA,IAAI,aAAA,CAAc,KAAA,CAAM,CAAC,CAAA,KAAM,EAAA,CAAG,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,CAAC,CAAC,CAAC,CAAA,EAAG;AAC9D,IAAA,OAAO,EAAE,QAAA,EAAU,OAAA,EAAS,MAAA,EAAQ,iBAAA,EAAmB,IAAI,IAAA,EAAK;AAAA,EACpE;AACA,EAAA,MAAM,MAAM,MAAA,CAAO,YAAA,IAAgB,iBAAiB,OAAA,EAAS,EAAA,EAAI,OAAO,OAAO,CAAA;AAC/E,EAAA,IAAI,CAAC,GAAA,EAAK;AACN,IAAA,OAAO;AAAA,MACH,QAAA,EAAU,OAAA;AAAA,MACV,MAAA,EAAQ,QAAA;AAAA,MACR,EAAA,EAAI,KAAA;AAAA,MACJ,MAAA,EAAQ;AAAA,KACZ;AAAA,EACJ;AACA,EAAA,EAAA,CAAG,OAAO,OAAO,CAAA;AACjB,EAAA,MAAM,SAAmB,EAAC;AAC1B,EAAA,KAAA,MAAW,KAAK,aAAA,EAAe;AAC3B,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,GAAA,EAAK,CAAC,CAAA;AAC1B,IAAA,IAAI,EAAA,CAAG,MAAA,CAAO,CAAC,CAAA,EAAG;AACd,MAAA,EAAA,CAAG,SAAS,CAAA,EAAG,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,CAAC,CAAC,CAAA;AACpC,MAAA,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IACjB;AAAA,EACJ;AACA,EAAA,MAAM,EAAA,GAAK,aAAA,CAAc,KAAA,CAAM,CAAC,CAAA,KAAM,EAAA,CAAG,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,CAAC,CAAC,CAAC,CAAA;AACtE,EAAA,OAAO;AAAA,IACH,QAAA,EAAU,OAAA;AAAA,IACV,MAAA,EAAQ,KAAK,OAAA,GAAU,QAAA;AAAA,IACvB,EAAA;AAAA,IACA,QAAQ,CAAA,QAAA,EAAW,MAAA,CAAO,KAAK,IAAI,CAAC,oCAAoC,GAAG,CAAA;AAAA,GAC/E;AACJ;AAEA,SAAS,kBAAA,CAAmB,SAAiB,EAAA,EAAkC;AAC3E,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,cAAc,CAAA;AAChD,EAAA,IAAI,CAAC,EAAA,CAAG,MAAA,CAAO,MAAM,CAAA,EAAG;AACpB,IAAA,OAAO,EAAE,UAAU,QAAA,EAAU,MAAA,EAAQ,WAAW,EAAA,EAAI,IAAA,EAAM,QAAQ,0BAAA,EAA2B;AAAA,EACjG;AACA,EAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAO,GAAI,EAAA,CAAG,GAAA,CAAI,UAAA,EAAY,CAAC,SAAA,EAAW,QAAA,EAAU,GAAA,EAAK,MAAM,CAAC,CAAA;AAC9E,EAAA,OAAO,IAAA,KAAS,CAAA,GACV,EAAE,QAAA,EAAU,QAAA,EAAU,QAAQ,OAAA,EAAS,EAAA,EAAI,IAAA,EAAM,MAAA,EAAQ,4BAAA,EAA6B,GACtF,EAAE,QAAA,EAAU,QAAA,EAAU,MAAA,EAAQ,QAAA,EAAU,EAAA,EAAI,KAAA,EAAO,QAAQ,CAAA,iBAAA,EAAoB,MAAA,CAAO,IAAA,EAAM,CAAA,CAAA,EAAG;AACzG;AAEA,SAAS,qBAAA,CAAsB,SAAiB,EAAA,EAAkC;AAC9E,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,cAAc,CAAA;AAChD,EAAA,IAAI,CAAC,EAAA,CAAG,MAAA,CAAO,MAAM,CAAA,EAAG;AACpB,IAAA,OAAO,EAAE,UAAU,OAAA,EAAS,MAAA,EAAQ,WAAW,EAAA,EAAI,IAAA,EAAM,QAAQ,0BAAA,EAA2B;AAAA,EAChG;AACA,EAAA,EAAA,CAAG,UAAU,MAAM,CAAA;AACnB,EAAA,OAAO,EAAE,UAAU,OAAA,EAAS,MAAA,EAAQ,SAAS,EAAA,EAAI,IAAA,EAAM,QAAQ,uBAAA,EAAwB;AAC3F;AAQA,eAAsB,mBAClB,OAAA,EACA,IAAA,GAAyB,EAAC,EAC1B,EAAA,GAAkB,iBAAgB,EACV;AACxB,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,QAAA,IAAY,OAAA,CAAQ,oBAAA;AAC1C,EAAA,MAAM,UAAU,IAAA,CAAK,UAAA,IAAc,kBAAkB,OAAA,CAAQ,SAAA,EAAW,UAAU,EAAE,CAAA;AACpF,EAAA,IAAI,CAAC,OAAA,EAAS;AACV,IAAA,OAAO;AAAA,MACH,QAAA;AAAA,MACA,MAAA,EAAQ,SAAA;AAAA,MACR,EAAA,EAAI,KAAA;AAAA,MACJ,MAAA,EAAQ;AAAA,KACZ;AAAA,EACJ;AACA,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,SAAS,SAAS,CAAA;AACrD,EAAA,QAAQ,QAAA;AAAU,IACd,KAAK,OAAA,EAAS;AACV,MAAA,MAAM,QAAA,GAAW,cAAA,CAAe,OAAA,EAAS,IAAI,CAAA;AAC7C,MAAA,OAAO,YAAA,CAAa,OAAA,EAAS,OAAA,EAAS,EAAA,EAAI;AAAA,QACtC,SAAS,QAAA,GAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA,GAAK,IAAA;AAAA,QAChD,cAAc,IAAA,CAAK;AAAA,OACtB,CAAA;AAAA,IACL;AAAA,IACA,KAAK,QAAA;AACD,MAAA,OAAO,kBAAA,CAAmB,SAAS,EAAE,CAAA;AAAA,IACzC,KAAK,OAAA;AACD,MAAA,OAAO,qBAAA,CAAsB,SAAS,EAAE,CAAA;AAAA,IAC5C;AACI,MAAA,OAAO,EAAE,QAAA,EAAU,MAAA,EAAQ,WAAW,EAAA,EAAI,IAAA,EAAM,QAAQ,yBAAA,EAA0B;AAAA;AAE9F","file":"electron.js","sourcesContent":["/**\n * electron-builder `afterPack` helper (Tier 0 — packaging).\n *\n * fancy-term-host wraps `node-pty`, a NATIVE addon. `electron-builder`\n * (via `install-app-deps`) rebuilds it, but the rebuild output is left\n * unusable per-OS, so a standard packaged app spawns NO terminal until each\n * consumer discovers + patches it. This helper performs the three known fixes\n * in one call so consumers don't have to hand-roll them (#7):\n *\n * • **Windows** — node-pty's `conpty.node` `LoadLibrary`s a `conpty.dll` from a\n * `build/Release/conpty/` SUBDIR that the rebuild doesn't create; the dll +\n * `OpenConsole.exe` ship only under `prebuilds/<plat>/conpty/` (or\n * `third_party/conpty/`). Runtime error otherwise:\n * `Cannot find conpty.dll at .../build/Release/conpty/conpty.dll`. We copy\n * them into place.\n * • **macOS** — node-pty's `spawn-helper` ships UNSIGNED, so Apple Silicon\n * SIGKILLs it on exec (all arm64 code must be at least ad-hoc signed) and the\n * shell child never starts. We ad-hoc sign it (`codesign --force --sign -`).\n * • **Linux** — `spawn-helper` must stay executable after packaging. We\n * `chmod +x` it.\n *\n * Usage (electron-builder config):\n * ```js\n * const { fancyTermAfterPack } = require('@particle-academy/fancy-term-host/electron');\n * module.exports = { afterPack: (context) => fancyTermAfterPack(context) };\n * ```\n *\n * The IO is injected (see {@link AfterPackIo}) so the decision logic is unit\n * testable without touching the filesystem or spawning `codesign`.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { spawnSync } from 'node:child_process';\n\n/** The subset of electron-builder's AfterPackContext this helper reads. */\nexport interface AfterPackContext {\n /** The per-OS output dir being packed (e.g. `dist/mac-arm64`, `dist/win-unpacked`). */\n appOutDir: string;\n /** `'darwin' | 'win32' | 'linux'` — the platform being packed. */\n electronPlatformName: string;\n /**\n * The target architecture being packed — electron-builder's `Arch` enum\n * (`ia32=0, x64=1, armv7l=2, arm64=3, universal=4`). Used to pick the\n * matching `conpty.dll` prebuild on Windows (#9). A string arch name\n * (`'x64'`, `'arm64'`, …) is also accepted.\n */\n arch?: number | string;\n}\n\nexport interface AfterPackOptions {\n /** Override the platform (defaults to `context.electronPlatformName`). */\n platform?: string;\n /** Explicit node-pty package dir; skips the search under `appOutDir`. */\n nodePtyDir?: string;\n /**\n * Override the target arch name (`'x64'`, `'arm64'`, `'ia32'`, …) used to\n * select the Windows `conpty` prebuild. Defaults to `context.arch`.\n */\n arch?: string;\n /**\n * Pin the conpty source dir outright, skipping prebuild discovery. Use when\n * your `conpty.dll` + `OpenConsole.exe` live somewhere non-standard.\n */\n conptySource?: string;\n}\n\nexport type AfterPackAction = 'fixed' | 'already-present' | 'skipped' | 'failed';\n\nexport interface AfterPackResult {\n platform: string;\n action: AfterPackAction;\n /** True when the terminal should now work (fixed / already-ok / nothing-to-do). */\n ok: boolean;\n detail?: string;\n}\n\n/** Injected IO so the orchestration is unit-testable. `nodeAfterPackIo()` is prod. */\nexport interface AfterPackIo {\n exists(p: string): boolean;\n /** Directory entries, or [] on any failure. */\n readdir(dir: string): string[];\n mkdirp(dir: string): void;\n copyFile(src: string, dest: string): void;\n /** Make a file executable (0o755). */\n chmodExec(p: string): void;\n /** Run a command; resolves the exit code + captured stderr. */\n run(cmd: string, args: string[]): { code: number; stderr: string };\n}\n\n/** Production {@link AfterPackIo}: real fs + child_process. */\nexport function nodeAfterPackIo(): AfterPackIo {\n return {\n exists: (p) => {\n try {\n return fs.existsSync(p);\n } catch {\n return false;\n }\n },\n readdir: (d) => {\n try {\n return fs.readdirSync(d);\n } catch {\n return [];\n }\n },\n mkdirp: (d) => {\n fs.mkdirSync(d, { recursive: true });\n },\n copyFile: (s, d) => {\n fs.copyFileSync(s, d);\n },\n chmodExec: (p) => {\n fs.chmodSync(p, 0o755);\n },\n run: (cmd, args) => {\n const r = spawnSync(cmd, args, { encoding: 'utf8' });\n return {\n code: r.status ?? -1,\n stderr: (r.stderr ?? '') + (r.error ? String(r.error) : ''),\n };\n },\n };\n}\n\n/** Resources dir(s) inside a packed app where `app.asar.unpacked` lives. */\nfunction resourcesDirs(appOutDir: string, platform: string, io: AfterPackIo): string[] {\n if (platform === 'darwin') {\n // <appOutDir>/<Product>.app/Contents/Resources — the .app name varies.\n return io\n .readdir(appOutDir)\n .filter((n) => n.endsWith('.app'))\n .map((a) => path.join(appOutDir, a, 'Contents', 'Resources'));\n }\n // Windows / Linux: <appOutDir>/resources\n return [path.join(appOutDir, 'resources')];\n}\n\n/** Candidate node-pty package dirs under a resources dir (asar-unpacked or plain). */\nfunction nodePtyCandidates(resources: string): string[] {\n return [\n path.join(resources, 'app.asar.unpacked', 'node_modules', 'node-pty'),\n path.join(resources, 'app', 'node_modules', 'node-pty'),\n ];\n}\n\n/** Locate the packaged node-pty dir under `appOutDir`, or null. */\nexport function resolveNodePtyDir(\n appOutDir: string,\n platform: string,\n io: AfterPackIo,\n): string | null {\n for (const res of resourcesDirs(appOutDir, platform, io)) {\n for (const dir of nodePtyCandidates(res)) {\n if (io.exists(dir)) return dir;\n }\n }\n return null;\n}\n\n/** electron-builder `Arch` enum → Node/node-pty arch name (used in prebuild dirs). */\nconst ARCH_ENUM_NAMES: Record<number, string> = {\n 0: 'ia32',\n 1: 'x64',\n 2: 'armv7l',\n 3: 'arm64',\n 4: 'universal',\n};\n\n/** The target arch name to match a prebuild against: opts override → context.arch. */\nfunction targetArchName(context: AfterPackContext, opts: AfterPackOptions): string | undefined {\n if (opts.arch) return opts.arch;\n const a = context.arch;\n if (typeof a === 'string') return a;\n if (typeof a === 'number') return ARCH_ENUM_NAMES[a];\n return undefined;\n}\n\n/**\n * Find the conpty asset source dir (`prebuilds/<plat>-<arch>/conpty` or\n * `third_party/conpty`).\n *\n * When `preferDir` is given (e.g. `win32-x64`), that arch-specific prebuild wins\n * — node-pty ships a `conpty.dll` per arch (distinct machine code, NOT\n * interchangeable), and an x64 app given the arm64 dll can't `LoadLibrary` it,\n * so no terminal spawns (#9). We fall back to the first prebuild carrying a\n * `conpty.dll` only for single-arch installs or when the target arch's prebuild\n * is genuinely absent.\n */\nfunction findConptySource(nodePty: string, io: AfterPackIo, preferDir?: string | null): string | null {\n const prebuilds = path.join(nodePty, 'prebuilds');\n if (preferDir) {\n const c = path.join(prebuilds, preferDir, 'conpty');\n if (io.exists(path.join(c, 'conpty.dll'))) return c;\n }\n for (const name of io.readdir(prebuilds)) {\n const c = path.join(prebuilds, name, 'conpty');\n if (io.exists(path.join(c, 'conpty.dll'))) return c;\n }\n const tp = path.join(nodePty, 'third_party', 'conpty');\n if (io.exists(path.join(tp, 'conpty.dll'))) return tp;\n return null;\n}\n\nconst CONPTY_ASSETS = ['conpty.dll', 'OpenConsole.exe'];\n\nfunction winFixConpty(\n nodePty: string,\n release: string,\n io: AfterPackIo,\n select: { archDir?: string | null; conptySource?: string | null } = {},\n): AfterPackResult {\n const destDir = path.join(release, 'conpty');\n if (CONPTY_ASSETS.every((f) => io.exists(path.join(destDir, f)))) {\n return { platform: 'win32', action: 'already-present', ok: true };\n }\n const src = select.conptySource ?? findConptySource(nodePty, io, select.archDir);\n if (!src) {\n return {\n platform: 'win32',\n action: 'failed',\n ok: false,\n detail: 'conpty source not found (prebuilds/*/conpty or third_party/conpty)',\n };\n }\n io.mkdirp(destDir);\n const copied: string[] = [];\n for (const f of CONPTY_ASSETS) {\n const s = path.join(src, f);\n if (io.exists(s)) {\n io.copyFile(s, path.join(destDir, f));\n copied.push(f);\n }\n }\n const ok = CONPTY_ASSETS.every((f) => io.exists(path.join(destDir, f)));\n return {\n platform: 'win32',\n action: ok ? 'fixed' : 'failed',\n ok,\n detail: `copied [${copied.join(', ')}] into build/Release/conpty from ${src}`,\n };\n}\n\nfunction macSignSpawnHelper(release: string, io: AfterPackIo): AfterPackResult {\n const helper = path.join(release, 'spawn-helper');\n if (!io.exists(helper)) {\n return { platform: 'darwin', action: 'skipped', ok: true, detail: 'spawn-helper not present' };\n }\n const { code, stderr } = io.run('codesign', ['--force', '--sign', '-', helper]);\n return code === 0\n ? { platform: 'darwin', action: 'fixed', ok: true, detail: 'ad-hoc signed spawn-helper' }\n : { platform: 'darwin', action: 'failed', ok: false, detail: `codesign failed: ${stderr.trim()}` };\n}\n\nfunction linuxChmodSpawnHelper(release: string, io: AfterPackIo): AfterPackResult {\n const helper = path.join(release, 'spawn-helper');\n if (!io.exists(helper)) {\n return { platform: 'linux', action: 'skipped', ok: true, detail: 'spawn-helper not present' };\n }\n io.chmodExec(helper);\n return { platform: 'linux', action: 'fixed', ok: true, detail: 'chmod +x spawn-helper' };\n}\n\n/**\n * Make a packaged node-pty spawn a working terminal on the platform being packed.\n * Call from electron-builder's `afterPack`. NEVER throws for the expected\n * outcomes; returns a result describing what it did (inspect `ok`). Idempotent —\n * safe to run on every build.\n */\nexport async function fancyTermAfterPack(\n context: AfterPackContext,\n opts: AfterPackOptions = {},\n io: AfterPackIo = nodeAfterPackIo(),\n): Promise<AfterPackResult> {\n const platform = opts.platform ?? context.electronPlatformName;\n const nodePty = opts.nodePtyDir ?? resolveNodePtyDir(context.appOutDir, platform, io);\n if (!nodePty) {\n return {\n platform,\n action: 'skipped',\n ok: false,\n detail: 'node-pty not found under appOutDir (adjust asarUnpack or pass nodePtyDir)',\n };\n }\n const release = path.join(nodePty, 'build', 'Release');\n switch (platform) {\n case 'win32': {\n const archName = targetArchName(context, opts);\n return winFixConpty(nodePty, release, io, {\n archDir: archName ? `${platform}-${archName}` : null,\n conptySource: opts.conptySource,\n });\n }\n case 'darwin':\n return macSignSpawnHelper(release, io);\n case 'linux':\n return linuxChmodSpawnHelper(release, io);\n default:\n return { platform, action: 'skipped', ok: true, detail: 'no packaging fix needed' };\n }\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -6,6 +6,7 @@ var fs2 = require('fs');
|
|
|
6
6
|
var os2 = require('os');
|
|
7
7
|
var path = require('path');
|
|
8
8
|
var crypto = require('crypto');
|
|
9
|
+
var child_process = require('child_process');
|
|
9
10
|
var zlib = require('zlib');
|
|
10
11
|
var net = require('net');
|
|
11
12
|
var url = require('url');
|
|
@@ -155,6 +156,27 @@ function unixCandidates() {
|
|
|
155
156
|
}
|
|
156
157
|
];
|
|
157
158
|
}
|
|
159
|
+
function parseDsclUserShell(output) {
|
|
160
|
+
const m = /UserShell:\s*(\S+)/.exec(output);
|
|
161
|
+
return m ? m[1] : null;
|
|
162
|
+
}
|
|
163
|
+
function macUserShell() {
|
|
164
|
+
try {
|
|
165
|
+
const user = os2__default.default.userInfo().username;
|
|
166
|
+
const out = child_process.execFileSync("dscl", [".", "-read", `/Users/${user}`, "UserShell"], {
|
|
167
|
+
encoding: "utf8",
|
|
168
|
+
timeout: 2e3
|
|
169
|
+
});
|
|
170
|
+
return parseDsclUserShell(out);
|
|
171
|
+
} catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function resolveLoginShell(env = process.env, platform = process.platform, macLookup = macUserShell) {
|
|
176
|
+
if (env.SHELL) return env.SHELL;
|
|
177
|
+
if (platform === "darwin") return macLookup();
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
158
180
|
function detectShells() {
|
|
159
181
|
const candidates = process.platform === "win32" ? windowsCandidates() : unixCandidates();
|
|
160
182
|
const found = [];
|
|
@@ -163,14 +185,18 @@ function detectShells() {
|
|
|
163
185
|
if (command) found.push({ id: c.id, label: c.label, command, args: c.args });
|
|
164
186
|
}
|
|
165
187
|
if (process.platform !== "win32") {
|
|
166
|
-
const login =
|
|
167
|
-
if (login &&
|
|
168
|
-
found.
|
|
188
|
+
const login = resolveLoginShell();
|
|
189
|
+
if (login && fs2__default.default.existsSync(login)) {
|
|
190
|
+
const existing = found.find((s) => s.command === login);
|
|
191
|
+
const rest = found.filter((s) => s.command !== login);
|
|
192
|
+
const head = existing ?? {
|
|
169
193
|
id: path__default.default.basename(login),
|
|
170
194
|
label: path__default.default.basename(login),
|
|
171
195
|
command: login,
|
|
172
196
|
args: ["-l"]
|
|
173
|
-
}
|
|
197
|
+
};
|
|
198
|
+
found.length = 0;
|
|
199
|
+
found.push(head, ...rest);
|
|
174
200
|
}
|
|
175
201
|
}
|
|
176
202
|
return found;
|
|
@@ -232,6 +258,12 @@ function cwdHookSpawn(command, settings) {
|
|
|
232
258
|
path__default.default.join(dir, ".zshenv"),
|
|
233
259
|
`# fancy-term-host (generated)
|
|
234
260
|
[ -f "${orig}/.zshenv" ] && source "${orig}/.zshenv"
|
|
261
|
+
`
|
|
262
|
+
);
|
|
263
|
+
const okProfile = writeShim(
|
|
264
|
+
path__default.default.join(dir, ".zprofile"),
|
|
265
|
+
`# fancy-term-host (generated)
|
|
266
|
+
[ -f "${orig}/.zprofile" ] && source "${orig}/.zprofile"
|
|
235
267
|
`
|
|
236
268
|
);
|
|
237
269
|
const okRc = writeShim(
|
|
@@ -244,7 +276,7 @@ typeset -ga precmd_functions
|
|
|
244
276
|
precmd_functions+=(__fth_osc7)
|
|
245
277
|
`
|
|
246
278
|
);
|
|
247
|
-
return okEnv && okRc ? { env: { ZDOTDIR: dir }, args: [] } : empty;
|
|
279
|
+
return okEnv && okProfile && okRc ? { env: { ZDOTDIR: dir }, args: [] } : empty;
|
|
248
280
|
}
|
|
249
281
|
if (kind === "fish") {
|
|
250
282
|
const dir = hookDir();
|
|
@@ -1081,6 +1113,23 @@ function isPidAlive(pid) {
|
|
|
1081
1113
|
return err.code === "EPERM";
|
|
1082
1114
|
}
|
|
1083
1115
|
}
|
|
1116
|
+
function terminateHost(pid, signal = "SIGTERM") {
|
|
1117
|
+
if (!pid || pid <= 0 || pid === process.pid) return false;
|
|
1118
|
+
try {
|
|
1119
|
+
process.kill(pid, signal);
|
|
1120
|
+
return true;
|
|
1121
|
+
} catch {
|
|
1122
|
+
return false;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
async function awaitPidGone(pid, timeoutMs = 2e3) {
|
|
1126
|
+
const deadline = Date.now() + timeoutMs;
|
|
1127
|
+
while (Date.now() < deadline) {
|
|
1128
|
+
if (!isPidAlive(pid)) return true;
|
|
1129
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
1130
|
+
}
|
|
1131
|
+
return !isPidAlive(pid);
|
|
1132
|
+
}
|
|
1084
1133
|
function pidfileUsable(pf) {
|
|
1085
1134
|
if (!pf) return false;
|
|
1086
1135
|
if (pf.protocolVersion !== PROTOCOL_VERSION) return false;
|
|
@@ -1141,6 +1190,35 @@ async function awaitUsableHost(userData, timeoutMs = 4e3) {
|
|
|
1141
1190
|
}
|
|
1142
1191
|
return false;
|
|
1143
1192
|
}
|
|
1193
|
+
async function reapHost(userData, pf) {
|
|
1194
|
+
if (pf && isPidAlive(pf.pid) && terminateHost(pf.pid)) {
|
|
1195
|
+
await awaitPidGone(pf.pid, 2e3);
|
|
1196
|
+
}
|
|
1197
|
+
deletePidfile(userData);
|
|
1198
|
+
}
|
|
1199
|
+
async function connectOrSpawnHost(userData, spawner, snapshots) {
|
|
1200
|
+
let pf = readPidfile(userData);
|
|
1201
|
+
if (pidfileUsable(pf)) {
|
|
1202
|
+
try {
|
|
1203
|
+
return await HostClient.connect(pf.socketPath, snapshots);
|
|
1204
|
+
} catch {
|
|
1205
|
+
await reapHost(userData, pf);
|
|
1206
|
+
pf = null;
|
|
1207
|
+
}
|
|
1208
|
+
} else if (pf) {
|
|
1209
|
+
await reapHost(userData, pf);
|
|
1210
|
+
pf = null;
|
|
1211
|
+
}
|
|
1212
|
+
deletePidfile(userData);
|
|
1213
|
+
const hostScript = spawner.resolveHostScript();
|
|
1214
|
+
if (!hostScript) return null;
|
|
1215
|
+
spawner.spawnDetached(hostScript, { GENIE_USERDATA: userData });
|
|
1216
|
+
const up = await awaitUsableHost(userData);
|
|
1217
|
+
if (!up) return null;
|
|
1218
|
+
pf = readPidfile(userData);
|
|
1219
|
+
if (!pf) return null;
|
|
1220
|
+
return await HostClient.connect(pf.socketPath, snapshots);
|
|
1221
|
+
}
|
|
1144
1222
|
async function initTerminalBackend() {
|
|
1145
1223
|
setActiveBackend(inProcessBackend());
|
|
1146
1224
|
if (!deps || !detachedEnabled()) {
|
|
@@ -1149,33 +1227,17 @@ async function initTerminalBackend() {
|
|
|
1149
1227
|
const { spawner, snapshots } = deps;
|
|
1150
1228
|
const userData = spawner.userDataDir();
|
|
1151
1229
|
try {
|
|
1152
|
-
const
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
status(
|
|
1158
|
-
"Detached terminals unavailable (host not found) \u2014 using in-process. Sessions won't survive a full quit."
|
|
1159
|
-
);
|
|
1160
|
-
return { host: false, reattachIds: [] };
|
|
1161
|
-
}
|
|
1162
|
-
spawner.spawnDetached(hostScript, { GENIE_USERDATA: userData });
|
|
1163
|
-
const up = await awaitUsableHost(userData);
|
|
1164
|
-
if (!up) {
|
|
1165
|
-
status(
|
|
1166
|
-
"Detached terminals unavailable (host didn't start) \u2014 using in-process. Sessions won't survive a full quit."
|
|
1167
|
-
);
|
|
1168
|
-
return { host: false, reattachIds: [] };
|
|
1169
|
-
}
|
|
1170
|
-
pf = readPidfile(userData);
|
|
1171
|
-
}
|
|
1172
|
-
if (!pf) {
|
|
1230
|
+
const c = await connectOrSpawnHost(userData, spawner, snapshots);
|
|
1231
|
+
if (!c) {
|
|
1232
|
+
client = null;
|
|
1233
|
+
usingHost = false;
|
|
1234
|
+
setActiveBackend(inProcessBackend());
|
|
1173
1235
|
status(
|
|
1174
1236
|
"Detached terminals unavailable \u2014 using in-process. Sessions won't survive a full quit."
|
|
1175
1237
|
);
|
|
1176
1238
|
return { host: false, reattachIds: [] };
|
|
1177
1239
|
}
|
|
1178
|
-
client =
|
|
1240
|
+
client = c;
|
|
1179
1241
|
client.on("error", onHostError);
|
|
1180
1242
|
setActiveBackend(client);
|
|
1181
1243
|
usingHost = true;
|