@particle-academy/fancy-term-host 0.3.0 → 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/dist/electron.cjs +28 -5
- package/dist/electron.cjs.map +1 -1
- package/dist/electron.d.cts +17 -0
- package/dist/electron.d.ts +17 -0
- package/dist/electron.js +28 -5
- package/dist/electron.js.map +1 -1
- package/docs/packaging.md +18 -3
- package/package.json +1 -1
package/dist/electron.cjs
CHANGED
|
@@ -64,8 +64,26 @@ function resolveNodePtyDir(appOutDir, platform, io) {
|
|
|
64
64
|
}
|
|
65
65
|
return null;
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
var ARCH_ENUM_NAMES = {
|
|
68
|
+
0: "ia32",
|
|
69
|
+
1: "x64",
|
|
70
|
+
2: "armv7l",
|
|
71
|
+
3: "arm64",
|
|
72
|
+
4: "universal"
|
|
73
|
+
};
|
|
74
|
+
function targetArchName(context, opts) {
|
|
75
|
+
if (opts.arch) return opts.arch;
|
|
76
|
+
const a = context.arch;
|
|
77
|
+
if (typeof a === "string") return a;
|
|
78
|
+
if (typeof a === "number") return ARCH_ENUM_NAMES[a];
|
|
79
|
+
return void 0;
|
|
80
|
+
}
|
|
81
|
+
function findConptySource(nodePty, io, preferDir) {
|
|
68
82
|
const prebuilds = path__default.default.join(nodePty, "prebuilds");
|
|
83
|
+
if (preferDir) {
|
|
84
|
+
const c = path__default.default.join(prebuilds, preferDir, "conpty");
|
|
85
|
+
if (io.exists(path__default.default.join(c, "conpty.dll"))) return c;
|
|
86
|
+
}
|
|
69
87
|
for (const name of io.readdir(prebuilds)) {
|
|
70
88
|
const c = path__default.default.join(prebuilds, name, "conpty");
|
|
71
89
|
if (io.exists(path__default.default.join(c, "conpty.dll"))) return c;
|
|
@@ -75,12 +93,12 @@ function findConptySource(nodePty, io) {
|
|
|
75
93
|
return null;
|
|
76
94
|
}
|
|
77
95
|
var CONPTY_ASSETS = ["conpty.dll", "OpenConsole.exe"];
|
|
78
|
-
function winFixConpty(nodePty, release, io) {
|
|
96
|
+
function winFixConpty(nodePty, release, io, select = {}) {
|
|
79
97
|
const destDir = path__default.default.join(release, "conpty");
|
|
80
98
|
if (CONPTY_ASSETS.every((f) => io.exists(path__default.default.join(destDir, f)))) {
|
|
81
99
|
return { platform: "win32", action: "already-present", ok: true };
|
|
82
100
|
}
|
|
83
|
-
const src = findConptySource(nodePty, io);
|
|
101
|
+
const src = select.conptySource ?? findConptySource(nodePty, io, select.archDir);
|
|
84
102
|
if (!src) {
|
|
85
103
|
return {
|
|
86
104
|
platform: "win32",
|
|
@@ -135,8 +153,13 @@ async function fancyTermAfterPack(context, opts = {}, io = nodeAfterPackIo()) {
|
|
|
135
153
|
}
|
|
136
154
|
const release = path__default.default.join(nodePty, "build", "Release");
|
|
137
155
|
switch (platform) {
|
|
138
|
-
case "win32":
|
|
139
|
-
|
|
156
|
+
case "win32": {
|
|
157
|
+
const archName = targetArchName(context, opts);
|
|
158
|
+
return winFixConpty(nodePty, release, io, {
|
|
159
|
+
archDir: archName ? `${platform}-${archName}` : null,
|
|
160
|
+
conptySource: opts.conptySource
|
|
161
|
+
});
|
|
162
|
+
}
|
|
140
163
|
case "darwin":
|
|
141
164
|
return macSignSpawnHelper(release, io);
|
|
142
165
|
case "linux":
|
package/dist/electron.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/electron/after-pack.ts"],"names":["fs","spawnSync","path"],"mappings":"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","file":"electron.cjs","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\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\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/** Find the conpty asset source dir (`prebuilds/<plat>/conpty` or `third_party/conpty`). */\nfunction findConptySource(nodePty: string, io: AfterPackIo): string | null {\n const prebuilds = path.join(nodePty, 'prebuilds');\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(nodePty: string, release: string, io: AfterPackIo): 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 = findConptySource(nodePty, io);\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 return winFixConpty(nodePty, release, io);\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"]}
|
|
1
|
+
{"version":3,"sources":["../src/electron/after-pack.ts"],"names":["fs","spawnSync","path"],"mappings":"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","file":"electron.cjs","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/electron.d.cts
CHANGED
|
@@ -34,12 +34,29 @@ interface AfterPackContext {
|
|
|
34
34
|
appOutDir: string;
|
|
35
35
|
/** `'darwin' | 'win32' | 'linux'` — the platform being packed. */
|
|
36
36
|
electronPlatformName: string;
|
|
37
|
+
/**
|
|
38
|
+
* The target architecture being packed — electron-builder's `Arch` enum
|
|
39
|
+
* (`ia32=0, x64=1, armv7l=2, arm64=3, universal=4`). Used to pick the
|
|
40
|
+
* matching `conpty.dll` prebuild on Windows (#9). A string arch name
|
|
41
|
+
* (`'x64'`, `'arm64'`, …) is also accepted.
|
|
42
|
+
*/
|
|
43
|
+
arch?: number | string;
|
|
37
44
|
}
|
|
38
45
|
interface AfterPackOptions {
|
|
39
46
|
/** Override the platform (defaults to `context.electronPlatformName`). */
|
|
40
47
|
platform?: string;
|
|
41
48
|
/** Explicit node-pty package dir; skips the search under `appOutDir`. */
|
|
42
49
|
nodePtyDir?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Override the target arch name (`'x64'`, `'arm64'`, `'ia32'`, …) used to
|
|
52
|
+
* select the Windows `conpty` prebuild. Defaults to `context.arch`.
|
|
53
|
+
*/
|
|
54
|
+
arch?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Pin the conpty source dir outright, skipping prebuild discovery. Use when
|
|
57
|
+
* your `conpty.dll` + `OpenConsole.exe` live somewhere non-standard.
|
|
58
|
+
*/
|
|
59
|
+
conptySource?: string;
|
|
43
60
|
}
|
|
44
61
|
type AfterPackAction = 'fixed' | 'already-present' | 'skipped' | 'failed';
|
|
45
62
|
interface AfterPackResult {
|
package/dist/electron.d.ts
CHANGED
|
@@ -34,12 +34,29 @@ interface AfterPackContext {
|
|
|
34
34
|
appOutDir: string;
|
|
35
35
|
/** `'darwin' | 'win32' | 'linux'` — the platform being packed. */
|
|
36
36
|
electronPlatformName: string;
|
|
37
|
+
/**
|
|
38
|
+
* The target architecture being packed — electron-builder's `Arch` enum
|
|
39
|
+
* (`ia32=0, x64=1, armv7l=2, arm64=3, universal=4`). Used to pick the
|
|
40
|
+
* matching `conpty.dll` prebuild on Windows (#9). A string arch name
|
|
41
|
+
* (`'x64'`, `'arm64'`, …) is also accepted.
|
|
42
|
+
*/
|
|
43
|
+
arch?: number | string;
|
|
37
44
|
}
|
|
38
45
|
interface AfterPackOptions {
|
|
39
46
|
/** Override the platform (defaults to `context.electronPlatformName`). */
|
|
40
47
|
platform?: string;
|
|
41
48
|
/** Explicit node-pty package dir; skips the search under `appOutDir`. */
|
|
42
49
|
nodePtyDir?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Override the target arch name (`'x64'`, `'arm64'`, `'ia32'`, …) used to
|
|
52
|
+
* select the Windows `conpty` prebuild. Defaults to `context.arch`.
|
|
53
|
+
*/
|
|
54
|
+
arch?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Pin the conpty source dir outright, skipping prebuild discovery. Use when
|
|
57
|
+
* your `conpty.dll` + `OpenConsole.exe` live somewhere non-standard.
|
|
58
|
+
*/
|
|
59
|
+
conptySource?: string;
|
|
43
60
|
}
|
|
44
61
|
type AfterPackAction = 'fixed' | 'already-present' | 'skipped' | 'failed';
|
|
45
62
|
interface AfterPackResult {
|
package/dist/electron.js
CHANGED
|
@@ -57,8 +57,26 @@ function resolveNodePtyDir(appOutDir, platform, io) {
|
|
|
57
57
|
}
|
|
58
58
|
return null;
|
|
59
59
|
}
|
|
60
|
-
|
|
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) {
|
|
61
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
|
+
}
|
|
62
80
|
for (const name of io.readdir(prebuilds)) {
|
|
63
81
|
const c = path.join(prebuilds, name, "conpty");
|
|
64
82
|
if (io.exists(path.join(c, "conpty.dll"))) return c;
|
|
@@ -68,12 +86,12 @@ function findConptySource(nodePty, io) {
|
|
|
68
86
|
return null;
|
|
69
87
|
}
|
|
70
88
|
var CONPTY_ASSETS = ["conpty.dll", "OpenConsole.exe"];
|
|
71
|
-
function winFixConpty(nodePty, release, io) {
|
|
89
|
+
function winFixConpty(nodePty, release, io, select = {}) {
|
|
72
90
|
const destDir = path.join(release, "conpty");
|
|
73
91
|
if (CONPTY_ASSETS.every((f) => io.exists(path.join(destDir, f)))) {
|
|
74
92
|
return { platform: "win32", action: "already-present", ok: true };
|
|
75
93
|
}
|
|
76
|
-
const src = findConptySource(nodePty, io);
|
|
94
|
+
const src = select.conptySource ?? findConptySource(nodePty, io, select.archDir);
|
|
77
95
|
if (!src) {
|
|
78
96
|
return {
|
|
79
97
|
platform: "win32",
|
|
@@ -128,8 +146,13 @@ async function fancyTermAfterPack(context, opts = {}, io = nodeAfterPackIo()) {
|
|
|
128
146
|
}
|
|
129
147
|
const release = path.join(nodePty, "build", "Release");
|
|
130
148
|
switch (platform) {
|
|
131
|
-
case "win32":
|
|
132
|
-
|
|
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
|
+
}
|
|
133
156
|
case "darwin":
|
|
134
157
|
return macSignSpawnHelper(release, io);
|
|
135
158
|
case "linux":
|
package/dist/electron.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/electron/after-pack.ts"],"names":[],"mappings":"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","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\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\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/** Find the conpty asset source dir (`prebuilds/<plat>/conpty` or `third_party/conpty`). */\nfunction findConptySource(nodePty: string, io: AfterPackIo): string | null {\n const prebuilds = path.join(nodePty, 'prebuilds');\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(nodePty: string, release: string, io: AfterPackIo): 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 = findConptySource(nodePty, io);\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 return winFixConpty(nodePty, release, io);\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"]}
|
|
1
|
+
{"version":3,"sources":["../src/electron/after-pack.ts"],"names":[],"mappings":"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","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/docs/packaging.md
CHANGED
|
@@ -58,6 +58,20 @@ can't find node-pty under the packed app, it returns `{ action: 'skipped', ok:
|
|
|
58
58
|
false }` — check your `asarUnpack` glob, or pass an explicit dir:
|
|
59
59
|
`fancyTermAfterPack(context, { nodePtyDir })`.
|
|
60
60
|
|
|
61
|
+
### Windows arch selection
|
|
62
|
+
|
|
63
|
+
`conpty.dll` is **per-arch machine code** — the x64 and arm64 dlls are not
|
|
64
|
+
interchangeable. When `node-pty` ships more than one prebuild
|
|
65
|
+
(`prebuilds/win32-x64/`, `prebuilds/win32-arm64/`), the helper copies the one
|
|
66
|
+
matching the build's **target arch** (`context.arch`), so an x64 app never gets
|
|
67
|
+
the arm64 dll (which would fail `LoadLibrary` and spawn no terminal). Override
|
|
68
|
+
the arch or pin the source explicitly if needed:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
fancyTermAfterPack(context, { arch: 'x64' }); // force the arch name
|
|
72
|
+
fancyTermAfterPack(context, { conptySource: '/abs/dir' }); // pin the source dir
|
|
73
|
+
```
|
|
74
|
+
|
|
61
75
|
### macOS signing note
|
|
62
76
|
|
|
63
77
|
The hook **ad-hoc** signs `spawn-helper` (`codesign --force --sign -`) so it runs
|
|
@@ -72,10 +86,11 @@ If you'd rather inline it, this is exactly what the helper runs against the
|
|
|
72
86
|
packaged `node-pty` (`…/Resources/app.asar.unpacked/node_modules/node-pty`):
|
|
73
87
|
|
|
74
88
|
```sh
|
|
75
|
-
# Windows — create the conpty subdir node-pty LoadLibrary's
|
|
89
|
+
# Windows — create the conpty subdir node-pty LoadLibrary's.
|
|
90
|
+
# Use YOUR target arch (win32-x64 / win32-arm64) — the dlls are not interchangeable.
|
|
76
91
|
mkdir -p build/Release/conpty
|
|
77
|
-
cp prebuilds/win32
|
|
78
|
-
cp prebuilds/win32
|
|
92
|
+
cp prebuilds/win32-x64/conpty/conpty.dll build/Release/conpty/
|
|
93
|
+
cp prebuilds/win32-x64/conpty/OpenConsole.exe build/Release/conpty/
|
|
79
94
|
# (falls back to third_party/conpty/ when prebuilds/ is absent)
|
|
80
95
|
|
|
81
96
|
# macOS — ad-hoc sign spawn-helper so arm64 doesn't SIGKILL it
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@particle-academy/fancy-term-host",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Headless Node terminal backend for @particle-academy/fancy-term — owns the PTYs (node-pty) and the T1/T2/T3 persistence engine (snapshot+replay, retained PTYs, detached pty-host) behind four injected ports. OS-agnostic, zero hard third-party deps.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|