ugly-app 0.1.744 → 0.1.745
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/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/native/server/index.d.ts +0 -1
- package/dist/native/server/index.d.ts.map +1 -1
- package/dist/native/server/index.js +0 -1
- package/dist/native/server/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/version.ts +1 -1
- package/src/native/server/index.ts +0 -7
- package/dist/native/server/binaries.d.ts +0 -23
- package/dist/native/server/binaries.d.ts.map +0 -1
- package/dist/native/server/binaries.js +0 -142
- package/dist/native/server/binaries.js.map +0 -1
- package/src/native/server/binaries.ts +0 -155
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.745";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
|
@@ -12,5 +12,4 @@ export { CapabilityGate, type GateConfig, PermissionDenied } from './permissions
|
|
|
12
12
|
export { GrantStore, type GrantSnapshot } from './permissions/grants.js';
|
|
13
13
|
export { isWithinScope, originScopeDir, resolveScopedPath, ScopeViolation } from './permissions/scope.js';
|
|
14
14
|
export { type CapabilityRequest, EMPTY_GRANT, type OriginGrant, type PermissionDecision, } from './permissions/types.js';
|
|
15
|
-
export { type BinaryProvisionConfig, ensureBundledTool, findBundledTool, resolveBundledBinary, } from './binaries.js';
|
|
16
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/native/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC1G,OAAO,EACL,KAAK,iBAAiB,EACtB,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/native/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC1G,OAAO,EACL,KAAK,iBAAiB,EACtB,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAC"}
|
|
@@ -12,5 +12,4 @@ export { CapabilityGate, PermissionDenied } from './permissions/gate.js';
|
|
|
12
12
|
export { GrantStore } from './permissions/grants.js';
|
|
13
13
|
export { isWithinScope, originScopeDir, resolveScopedPath, ScopeViolation } from './permissions/scope.js';
|
|
14
14
|
export { EMPTY_GRANT, } from './permissions/types.js';
|
|
15
|
-
export { ensureBundledTool, findBundledTool, resolveBundledBinary, } from './binaries.js';
|
|
16
15
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/native/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAmB,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAmB,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAsB,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC1G,OAAO,EAEL,WAAW,GAGZ,MAAM,wBAAwB,CAAC
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/native/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAmB,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAmB,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAsB,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC1G,OAAO,EAEL,WAAW,GAGZ,MAAM,wBAAwB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugly-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.745",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"comment:files": "Allowlist what ships to npm. dist = runtime; src = sourcemap targets (dist/*.js.map reference ../../src/); templates = CLI scaffold. Everything else at repo root (.pgdata local Postgres, coverage, assets/icons sources, test/, test-results/) is excluded by omission. The !negations strip the scaffold's installed deps + cruft (templates/node_modules is 200MB+ and must never ship). package.json/README/LICENSE ship automatically.",
|
|
6
6
|
"files": [
|
package/src/cli/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by prebuild — do not edit manually
|
|
2
|
-
export const CLI_VERSION = "0.1.
|
|
2
|
+
export const CLI_VERSION = "0.1.745";
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export interface BinaryProvisionConfig {
|
|
2
|
-
/** Where bundled tools are installed, e.g. `~/.ugly-studio/binaries`. */
|
|
3
|
-
root: string;
|
|
4
|
-
/** Base URL hosting `binaries-manifest.json` (the R2 mirror). */
|
|
5
|
-
manifestBase: string;
|
|
6
|
-
}
|
|
7
|
-
/** The installed executable for `tool` under `root`, version-agnostic, or null. */
|
|
8
|
-
export declare function findBundledTool(root: string, tool: string): string | null;
|
|
9
|
-
/**
|
|
10
|
-
* Resolve a tool name to a path: a downloaded binary under `cfg.root` when
|
|
11
|
-
* present, else the bare name so the daemon's spawn resolves it on the system
|
|
12
|
-
* PATH. `downloadable` lists tools the host actually mirrors (others always
|
|
13
|
-
* fall through to PATH without a directory probe).
|
|
14
|
-
*/
|
|
15
|
-
export declare function resolveBundledBinary(name: string, cfg: Pick<BinaryProvisionConfig, 'root'>, downloadable?: readonly string[]): string;
|
|
16
|
-
/**
|
|
17
|
-
* Best-effort: download `tool` from the mirror into `cfg.root` on first use.
|
|
18
|
-
* Never fetches from the upstream project at runtime (the binary is mirrored to
|
|
19
|
-
* R2 by the host's publish step). Never throws (falls back to a system tool);
|
|
20
|
-
* idempotent + single-flight per tool.
|
|
21
|
-
*/
|
|
22
|
-
export declare function ensureBundledTool(tool: string, cfg: BinaryProvisionConfig): Promise<void>;
|
|
23
|
-
//# sourceMappingURL=binaries.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"binaries.d.ts","sourceRoot":"","sources":["../../../src/native/server/binaries.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;CACtB;AAgBD,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWzE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,EACxC,YAAY,GAAE,SAAS,MAAM,EAAW,GACvC,MAAM,CAMR;AAsBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzF"}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bundled-binary provisioning for the native daemon (lifted from Ugly Studio so
|
|
3
|
-
* the daemon AND the test harness share one implementation). A host (Ugly
|
|
4
|
-
* Studio) bundles a small set of tools by downloading them from its own R2
|
|
5
|
-
* mirror on first launch; everything else resolves on the system PATH.
|
|
6
|
-
*
|
|
7
|
-
* Pure fs/child_process/fetch (no Electron) and fully parameterized — the
|
|
8
|
-
* install root + manifest mirror are injected via `BinaryProvisionConfig`, so
|
|
9
|
-
* nothing here hardcodes a particular app's paths or hostnames.
|
|
10
|
-
*
|
|
11
|
-
* The mirror exposes `<manifestBase>/binaries-manifest.json`:
|
|
12
|
-
* { "<tool>": { "version": "x", "archives": { "<platform>-<arch>": { "url" } } } }
|
|
13
|
-
* One `tar -xf` covers every platform's asset (bsdtar on mac/win unpacks zip).
|
|
14
|
-
*/
|
|
15
|
-
import { basename, join } from 'node:path';
|
|
16
|
-
import { chmodSync, existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
17
|
-
import { execFile } from 'node:child_process';
|
|
18
|
-
import { promisify } from 'node:util';
|
|
19
|
-
const execFileP = promisify(execFile);
|
|
20
|
-
const exeName = (tool) => (process.platform === 'win32' ? `${tool}.exe` : tool);
|
|
21
|
-
function safeReaddir(dir) {
|
|
22
|
-
try {
|
|
23
|
-
return readdirSync(dir);
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
/** The installed executable for `tool` under `root`, version-agnostic, or null. */
|
|
30
|
-
export function findBundledTool(root, tool) {
|
|
31
|
-
const toolRoot = join(root, tool);
|
|
32
|
-
if (!existsSync(toolRoot))
|
|
33
|
-
return null;
|
|
34
|
-
for (const ver of safeReaddir(toolRoot)) {
|
|
35
|
-
const verDir = join(toolRoot, ver);
|
|
36
|
-
for (const sub of safeReaddir(verDir)) {
|
|
37
|
-
const p = join(verDir, sub, 'bin', exeName(tool));
|
|
38
|
-
if (existsSync(p))
|
|
39
|
-
return p;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Resolve a tool name to a path: a downloaded binary under `cfg.root` when
|
|
46
|
-
* present, else the bare name so the daemon's spawn resolves it on the system
|
|
47
|
-
* PATH. `downloadable` lists tools the host actually mirrors (others always
|
|
48
|
-
* fall through to PATH without a directory probe).
|
|
49
|
-
*/
|
|
50
|
-
export function resolveBundledBinary(name, cfg, downloadable = ['gh']) {
|
|
51
|
-
if (downloadable.includes(name)) {
|
|
52
|
-
const found = findBundledTool(cfg.root, name);
|
|
53
|
-
if (found)
|
|
54
|
-
return found;
|
|
55
|
-
}
|
|
56
|
-
return name;
|
|
57
|
-
}
|
|
58
|
-
/** Look up a tool's archive for this platform in the mirror's manifest. */
|
|
59
|
-
async function toolFromManifest(cfg, tool) {
|
|
60
|
-
try {
|
|
61
|
-
const res = await fetch(`${cfg.manifestBase}/binaries-manifest.json`, { cache: 'no-store' });
|
|
62
|
-
if (!res.ok)
|
|
63
|
-
return null;
|
|
64
|
-
const m = (await res.json());
|
|
65
|
-
const entry = m[tool];
|
|
66
|
-
const archive = entry?.archives[`${process.platform}-${process.arch}`];
|
|
67
|
-
if (!entry || !archive?.url)
|
|
68
|
-
return null;
|
|
69
|
-
return { version: entry.version, url: archive.url };
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
const ensuring = new Map();
|
|
76
|
-
/**
|
|
77
|
-
* Best-effort: download `tool` from the mirror into `cfg.root` on first use.
|
|
78
|
-
* Never fetches from the upstream project at runtime (the binary is mirrored to
|
|
79
|
-
* R2 by the host's publish step). Never throws (falls back to a system tool);
|
|
80
|
-
* idempotent + single-flight per tool.
|
|
81
|
-
*/
|
|
82
|
-
export function ensureBundledTool(tool, cfg) {
|
|
83
|
-
const key = `${cfg.root}:${tool}`;
|
|
84
|
-
const inflight = ensuring.get(key);
|
|
85
|
-
if (inflight)
|
|
86
|
-
return inflight;
|
|
87
|
-
const run = (async () => {
|
|
88
|
-
// Every failure below leaves the tool UNINSTALLED and is best-effort (spawns
|
|
89
|
-
// fall back to the system PATH). But when there's no system fallback either —
|
|
90
|
-
// the common case for pnpm/node on a fresh host — the miss surfaces much later
|
|
91
|
-
// as a cryptic `command not found` / ENOENT (-2) at spawn time with no trace.
|
|
92
|
-
// So log each failure to console.error/warn (the desktop shells ship these to
|
|
93
|
-
// studio.ugly.bot's errorLog) with the tool + reason, making bundle-binary
|
|
94
|
-
// provisioning issues findable remotely instead of silent.
|
|
95
|
-
const plat = `${process.platform}-${process.arch}`;
|
|
96
|
-
try {
|
|
97
|
-
if (findBundledTool(cfg.root, tool))
|
|
98
|
-
return;
|
|
99
|
-
const man = await toolFromManifest(cfg, tool);
|
|
100
|
-
if (!man) {
|
|
101
|
-
console.warn(`[binaries] ${tool}: not in the mirror manifest for ${plat} — falling back to system PATH (spawns will fail if it isn't installed)`);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
const dir = join(cfg.root, tool, man.version);
|
|
105
|
-
mkdirSync(dir, { recursive: true });
|
|
106
|
-
const archive = join(dir, basename(new URL(man.url).pathname));
|
|
107
|
-
const res = await fetch(man.url);
|
|
108
|
-
if (!res.ok) {
|
|
109
|
-
console.error(`[binaries] ${tool} ${man.version}: download failed HTTP ${res.status} ${res.statusText} from ${man.url} — tool will be UNAVAILABLE`);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
writeFileSync(archive, Buffer.from(await res.arrayBuffer()));
|
|
113
|
-
await execFileP('tar', ['-xf', archive, '-C', dir]);
|
|
114
|
-
try {
|
|
115
|
-
rmSync(archive);
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
118
|
-
/* leave it */
|
|
119
|
-
}
|
|
120
|
-
const bin = findBundledTool(cfg.root, tool);
|
|
121
|
-
if (!bin) {
|
|
122
|
-
console.error(`[binaries] ${tool} ${man.version}: archive extracted to ${dir} but no runnable binary was found (corrupt download or unexpected layout) — tool will be UNAVAILABLE`);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (process.platform !== 'win32') {
|
|
126
|
-
try {
|
|
127
|
-
chmodSync(bin, 0o755);
|
|
128
|
-
}
|
|
129
|
-
catch (e) {
|
|
130
|
-
console.warn(`[binaries] ${tool}: could not chmod +x ${bin} (${e?.message ?? e}) — spawns may fail with EACCES`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
catch (e) {
|
|
135
|
-
// Network / tar-extract / disk failure. Previously swallowed entirely.
|
|
136
|
-
console.error(`[binaries] ${tool} (${plat}): provisioning failed — ${e?.message ?? String(e)} — tool will be UNAVAILABLE, spawns fall back to system PATH`);
|
|
137
|
-
}
|
|
138
|
-
})();
|
|
139
|
-
ensuring.set(key, run);
|
|
140
|
-
return run;
|
|
141
|
-
}
|
|
142
|
-
//# sourceMappingURL=binaries.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"binaries.js","sourceRoot":"","sources":["../../../src/native/server/binaries.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bundled-binary provisioning for the native daemon (lifted from Ugly Studio so
|
|
3
|
-
* the daemon AND the test harness share one implementation). A host (Ugly
|
|
4
|
-
* Studio) bundles a small set of tools by downloading them from its own R2
|
|
5
|
-
* mirror on first launch; everything else resolves on the system PATH.
|
|
6
|
-
*
|
|
7
|
-
* Pure fs/child_process/fetch (no Electron) and fully parameterized — the
|
|
8
|
-
* install root + manifest mirror are injected via `BinaryProvisionConfig`, so
|
|
9
|
-
* nothing here hardcodes a particular app's paths or hostnames.
|
|
10
|
-
*
|
|
11
|
-
* The mirror exposes `<manifestBase>/binaries-manifest.json`:
|
|
12
|
-
* { "<tool>": { "version": "x", "archives": { "<platform>-<arch>": { "url" } } } }
|
|
13
|
-
* One `tar -xf` covers every platform's asset (bsdtar on mac/win unpacks zip).
|
|
14
|
-
*/
|
|
15
|
-
import { basename, join } from 'node:path';
|
|
16
|
-
import { chmodSync, existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
17
|
-
import { execFile } from 'node:child_process';
|
|
18
|
-
import { promisify } from 'node:util';
|
|
19
|
-
|
|
20
|
-
const execFileP = promisify(execFile);
|
|
21
|
-
|
|
22
|
-
export interface BinaryProvisionConfig {
|
|
23
|
-
/** Where bundled tools are installed, e.g. `~/.ugly-studio/binaries`. */
|
|
24
|
-
root: string;
|
|
25
|
-
/** Base URL hosting `binaries-manifest.json` (the R2 mirror). */
|
|
26
|
-
manifestBase: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface BinariesManifest {
|
|
30
|
-
[tool: string]: { version: string; archives: Record<string, { url: string }> } | undefined;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const exeName = (tool: string): string => (process.platform === 'win32' ? `${tool}.exe` : tool);
|
|
34
|
-
|
|
35
|
-
function safeReaddir(dir: string): string[] {
|
|
36
|
-
try {
|
|
37
|
-
return readdirSync(dir);
|
|
38
|
-
} catch {
|
|
39
|
-
return [];
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** The installed executable for `tool` under `root`, version-agnostic, or null. */
|
|
44
|
-
export function findBundledTool(root: string, tool: string): string | null {
|
|
45
|
-
const toolRoot = join(root, tool);
|
|
46
|
-
if (!existsSync(toolRoot)) return null;
|
|
47
|
-
for (const ver of safeReaddir(toolRoot)) {
|
|
48
|
-
const verDir = join(toolRoot, ver);
|
|
49
|
-
for (const sub of safeReaddir(verDir)) {
|
|
50
|
-
const p = join(verDir, sub, 'bin', exeName(tool));
|
|
51
|
-
if (existsSync(p)) return p;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Resolve a tool name to a path: a downloaded binary under `cfg.root` when
|
|
59
|
-
* present, else the bare name so the daemon's spawn resolves it on the system
|
|
60
|
-
* PATH. `downloadable` lists tools the host actually mirrors (others always
|
|
61
|
-
* fall through to PATH without a directory probe).
|
|
62
|
-
*/
|
|
63
|
-
export function resolveBundledBinary(
|
|
64
|
-
name: string,
|
|
65
|
-
cfg: Pick<BinaryProvisionConfig, 'root'>,
|
|
66
|
-
downloadable: readonly string[] = ['gh'],
|
|
67
|
-
): string {
|
|
68
|
-
if (downloadable.includes(name)) {
|
|
69
|
-
const found = findBundledTool(cfg.root, name);
|
|
70
|
-
if (found) return found;
|
|
71
|
-
}
|
|
72
|
-
return name;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Look up a tool's archive for this platform in the mirror's manifest. */
|
|
76
|
-
async function toolFromManifest(
|
|
77
|
-
cfg: BinaryProvisionConfig,
|
|
78
|
-
tool: string,
|
|
79
|
-
): Promise<{ version: string; url: string } | null> {
|
|
80
|
-
try {
|
|
81
|
-
const res = await fetch(`${cfg.manifestBase}/binaries-manifest.json`, { cache: 'no-store' });
|
|
82
|
-
if (!res.ok) return null;
|
|
83
|
-
const m = (await res.json()) as BinariesManifest;
|
|
84
|
-
const entry = m[tool];
|
|
85
|
-
const archive = entry?.archives[`${process.platform}-${process.arch}`];
|
|
86
|
-
if (!entry || !archive?.url) return null;
|
|
87
|
-
return { version: entry.version, url: archive.url };
|
|
88
|
-
} catch {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const ensuring = new Map<string, Promise<void>>();
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Best-effort: download `tool` from the mirror into `cfg.root` on first use.
|
|
97
|
-
* Never fetches from the upstream project at runtime (the binary is mirrored to
|
|
98
|
-
* R2 by the host's publish step). Never throws (falls back to a system tool);
|
|
99
|
-
* idempotent + single-flight per tool.
|
|
100
|
-
*/
|
|
101
|
-
export function ensureBundledTool(tool: string, cfg: BinaryProvisionConfig): Promise<void> {
|
|
102
|
-
const key = `${cfg.root}:${tool}`;
|
|
103
|
-
const inflight = ensuring.get(key);
|
|
104
|
-
if (inflight) return inflight;
|
|
105
|
-
const run = (async () => {
|
|
106
|
-
// Every failure below leaves the tool UNINSTALLED and is best-effort (spawns
|
|
107
|
-
// fall back to the system PATH). But when there's no system fallback either —
|
|
108
|
-
// the common case for pnpm/node on a fresh host — the miss surfaces much later
|
|
109
|
-
// as a cryptic `command not found` / ENOENT (-2) at spawn time with no trace.
|
|
110
|
-
// So log each failure to console.error/warn (the desktop shells ship these to
|
|
111
|
-
// studio.ugly.bot's errorLog) with the tool + reason, making bundle-binary
|
|
112
|
-
// provisioning issues findable remotely instead of silent.
|
|
113
|
-
const plat = `${process.platform}-${process.arch}`;
|
|
114
|
-
try {
|
|
115
|
-
if (findBundledTool(cfg.root, tool)) return;
|
|
116
|
-
const man = await toolFromManifest(cfg, tool);
|
|
117
|
-
if (!man) {
|
|
118
|
-
console.warn(`[binaries] ${tool}: not in the mirror manifest for ${plat} — falling back to system PATH (spawns will fail if it isn't installed)`);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const dir = join(cfg.root, tool, man.version);
|
|
122
|
-
mkdirSync(dir, { recursive: true });
|
|
123
|
-
const archive = join(dir, basename(new URL(man.url).pathname));
|
|
124
|
-
const res = await fetch(man.url);
|
|
125
|
-
if (!res.ok) {
|
|
126
|
-
console.error(`[binaries] ${tool} ${man.version}: download failed HTTP ${res.status} ${res.statusText} from ${man.url} — tool will be UNAVAILABLE`);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
writeFileSync(archive, Buffer.from(await res.arrayBuffer()));
|
|
130
|
-
await execFileP('tar', ['-xf', archive, '-C', dir]);
|
|
131
|
-
try {
|
|
132
|
-
rmSync(archive);
|
|
133
|
-
} catch {
|
|
134
|
-
/* leave it */
|
|
135
|
-
}
|
|
136
|
-
const bin = findBundledTool(cfg.root, tool);
|
|
137
|
-
if (!bin) {
|
|
138
|
-
console.error(`[binaries] ${tool} ${man.version}: archive extracted to ${dir} but no runnable binary was found (corrupt download or unexpected layout) — tool will be UNAVAILABLE`);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
if (process.platform !== 'win32') {
|
|
142
|
-
try {
|
|
143
|
-
chmodSync(bin, 0o755);
|
|
144
|
-
} catch (e) {
|
|
145
|
-
console.warn(`[binaries] ${tool}: could not chmod +x ${bin} (${(e as Error)?.message ?? e}) — spawns may fail with EACCES`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
} catch (e) {
|
|
149
|
-
// Network / tar-extract / disk failure. Previously swallowed entirely.
|
|
150
|
-
console.error(`[binaries] ${tool} (${plat}): provisioning failed — ${(e as Error)?.message ?? String(e)} — tool will be UNAVAILABLE, spawns fall back to system PATH`);
|
|
151
|
-
}
|
|
152
|
-
})();
|
|
153
|
-
ensuring.set(key, run);
|
|
154
|
-
return run;
|
|
155
|
-
}
|