ugly-app 0.1.743 → 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.
@@ -1,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.1.743";
1
+ export declare const CLI_VERSION = "0.1.745";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by prebuild — do not edit manually
2
- export const CLI_VERSION = "0.1.743";
2
+ export const CLI_VERSION = "0.1.745";
3
3
  //# sourceMappingURL=version.js.map
@@ -5,13 +5,14 @@
5
5
  * Supports GFM (tables, task lists, strikethrough) and custom nodes (mentions, bot tags).
6
6
  */
7
7
  import { Schema } from 'prosemirror-model';
8
- export declare const schema: Schema<"text" | "image" | "blockquote" | "table" | "svg" | "heading" | "paragraph" | "latex" | "mermaid" | "mention" | "table_row" | "table_cell" | "table_header" | "doc" | "code_block" | "horizontal_rule" | "bullet_list" | "ordered_list" | "list_item" | "hard_break" | "bot_tag" | "vegalite" | "markmap", "code" | "strong" | "link" | "em" | "strikethrough" | "highlight" | "comment_anchor">;
8
+ export declare const schema: Schema<"text" | "image" | "blockquote" | "table" | "svg" | "doc" | "heading" | "paragraph" | "latex" | "mermaid" | "mention" | "table_row" | "table_cell" | "table_header" | "code_block" | "horizontal_rule" | "bullet_list" | "ordered_list" | "list_item" | "hard_break" | "bot_tag" | "vegalite" | "markmap", "code" | "strong" | "link" | "em" | "strikethrough" | "highlight" | "comment_anchor">;
9
9
  export declare const nodeTypes: {
10
10
  readonly text: import("prosemirror-model").NodeType;
11
11
  readonly image: import("prosemirror-model").NodeType;
12
12
  readonly blockquote: import("prosemirror-model").NodeType;
13
13
  readonly table: import("prosemirror-model").NodeType;
14
14
  readonly svg: import("prosemirror-model").NodeType;
15
+ readonly doc: import("prosemirror-model").NodeType;
15
16
  readonly heading: import("prosemirror-model").NodeType;
16
17
  readonly paragraph: import("prosemirror-model").NodeType;
17
18
  readonly latex: import("prosemirror-model").NodeType;
@@ -20,7 +21,6 @@ export declare const nodeTypes: {
20
21
  readonly table_row: import("prosemirror-model").NodeType;
21
22
  readonly table_cell: import("prosemirror-model").NodeType;
22
23
  readonly table_header: import("prosemirror-model").NodeType;
23
- readonly doc: import("prosemirror-model").NodeType;
24
24
  readonly code_block: import("prosemirror-model").NodeType;
25
25
  readonly horizontal_rule: import("prosemirror-model").NodeType;
26
26
  readonly bullet_list: import("prosemirror-model").NodeType;
@@ -0,0 +1,15 @@
1
+ export interface BundledTool {
2
+ /** Request/grant key and default spawn name, e.g. 'pnpm'. */
3
+ name: string;
4
+ /** Executable basename when it differs from `name` (e.g. imagemagick → magick). */
5
+ exe?: string;
6
+ category: 'toolchain' | 'media' | 'data' | 'doc' | 'net';
7
+ /** Must be on PATH for `bash -lc` invocations (node/git/pnpm/uv/python). */
8
+ shellTool?: boolean;
9
+ /** Shown in the permission prompt, like mic/camera copy. */
10
+ description: string;
11
+ }
12
+ export declare const BUNDLED_TOOLS: readonly BundledTool[];
13
+ /** Look up a tool descriptor by its request/grant name. */
14
+ export declare function bundledTool(name: string): BundledTool | undefined;
15
+ //# sourceMappingURL=bundledTools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundledTools.d.ts","sourceRoot":"","sources":["../../src/native/bundledTools.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IACzD,4EAA4E;IAC5E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAyB9C,CAAC;AAIH,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAEjE"}
@@ -0,0 +1,36 @@
1
+ // The catalog of bundled command-line tools an app may request through the
2
+ // permission system (like mic/camera). This is VOCABULARY ONLY — names and
3
+ // static metadata. Versions, download sources, install, PATH, and upgrade all
4
+ // live in the host (ugly-studio); ugly-app never touches a binary.
5
+ export const BUNDLED_TOOLS = Object.freeze([
6
+ // toolchain — needed on PATH so shells (`bash -lc 'pnpm dev'`) resolve them.
7
+ { name: 'node', category: 'toolchain', shellTool: true, description: 'Node.js runtime — runs your project and its build tools.' },
8
+ { name: 'git', category: 'toolchain', shellTool: true, description: 'Git — version control for your project.' },
9
+ { name: 'pnpm', category: 'toolchain', shellTool: true, description: 'pnpm — installs your project’s dependencies.' },
10
+ { name: 'uv', category: 'toolchain', shellTool: true, description: 'uv — fast Python package manager.' },
11
+ { name: 'python', exe: 'python3', category: 'toolchain', shellTool: true, description: 'Python runtime.' },
12
+ // net
13
+ { name: 'gh', category: 'net', description: 'GitHub CLI — sign in and manage repositories.' },
14
+ { name: 'cloudflared', category: 'net', description: 'Cloudflare tunnel — exposes your local preview at a public URL.' },
15
+ { name: 'nats', category: 'net', description: 'NATS CLI — messaging server tooling.' },
16
+ // data
17
+ { name: 'postgres', category: 'data', description: 'PostgreSQL — local database for your project.' },
18
+ { name: 'jq', category: 'data', description: 'jq — command-line JSON processor.' },
19
+ { name: 'yq', category: 'data', description: 'yq — command-line YAML processor.' },
20
+ { name: 'pup', category: 'data', description: 'pup — command-line HTML parser.' },
21
+ { name: 'sqlite3', category: 'data', description: 'SQLite — command-line database shell.' },
22
+ // media
23
+ { name: 'ffmpeg', category: 'media', description: 'FFmpeg — audio/video processing.' },
24
+ { name: 'imagemagick', exe: 'magick', category: 'media', description: 'ImageMagick — image conversion and editing.' },
25
+ { name: 'yt-dlp', category: 'media', description: 'yt-dlp — download media from the web.' },
26
+ // doc
27
+ { name: 'pandoc', category: 'doc', description: 'Pandoc — document format conversion.' },
28
+ { name: 'pdftotext', category: 'doc', description: 'pdftotext — extract text from PDFs.' },
29
+ { name: 'tesseract', category: 'doc', description: 'Tesseract — OCR text from images.' },
30
+ ]);
31
+ const BY_NAME = new Map(BUNDLED_TOOLS.map((t) => [t.name, t]));
32
+ /** Look up a tool descriptor by its request/grant name. */
33
+ export function bundledTool(name) {
34
+ return BY_NAME.get(name);
35
+ }
36
+ //# sourceMappingURL=bundledTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundledTools.js","sourceRoot":"","sources":["../../src/native/bundledTools.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,2EAA2E;AAC3E,8EAA8E;AAC9E,mEAAmE;AAcnE,MAAM,CAAC,MAAM,aAAa,GAA2B,MAAM,CAAC,MAAM,CAAC;IACjE,6EAA6E;IAC7E,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,0DAA0D,EAAE;IACjI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,yCAAyC,EAAE;IAC/G,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,8CAA8C,EAAE;IACrH,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,mCAAmC,EAAE;IACxG,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE;IAC1G,MAAM;IACN,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,+CAA+C,EAAE;IAC7F,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,iEAAiE,EAAE;IACxH,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACtF,OAAO;IACP,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,+CAA+C,EAAE;IACpG,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,mCAAmC,EAAE;IAClF,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,mCAAmC,EAAE;IAClF,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,iCAAiC,EAAE;IACjF,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,uCAAuC,EAAE;IAC3F,QAAQ;IACR,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,kCAAkC,EAAE;IACtF,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,6CAA6C,EAAE;IACrH,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,uCAAuC,EAAE;IAC3F,MAAM;IACN,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACxF,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,qCAAqC,EAAE;IAC1F,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,mCAAmC,EAAE;CACzF,CAAC,CAAC;AAEH,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/D,2DAA2D;AAC3D,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC"}
@@ -20,6 +20,7 @@ export { taskEntryUrl } from './facades/task.js';
20
20
  export { defineTask, taskContext } from './defineTask.js';
21
21
  export type { TaskDefinition, TaskContext } from './defineTask.js';
22
22
  export { MCP_CATALOG } from './mcp-catalog.js';
23
+ export { BUNDLED_TOOLS, bundledTool, type BundledTool } from './bundledTools.js';
23
24
  export { createProxyClient, nativeSigner } from './proxyClient.js';
24
25
  export { createProxyHost } from './proxyHost.js';
25
26
  export type { ProxyHost, ProxyHostOpts } from './proxyHost.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,OAAO,EACP,WAAW,EACX,SAAS,EACT,QAAQ,EACR,MAAM,EACN,SAAS,EACT,cAAc,EACd,aAAa,EACb,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,EACb,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,eAAe,EACf,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/E,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtF,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/D,YAAY,EACV,WAAW,EACX,eAAe,EACf,WAAW,EACX,QAAQ,EACR,UAAU,GACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACvI,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACnF,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@ export { taskEntryUrl } from './facades/task.js';
10
10
  // Background-task authoring SDK (used inside a task bundle, not the app shell).
11
11
  export { defineTask, taskContext } from './defineTask.js';
12
12
  export { MCP_CATALOG } from './mcp-catalog.js';
13
+ export { BUNDLED_TOOLS, bundledTool } from './bundledTools.js';
13
14
  // ── Ugly Proxy (remote UglyNative over WebRTC) ──
14
15
  export { createProxyClient, nativeSigner } from './proxyClient.js';
15
16
  export { createProxyHost } from './proxyHost.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"AA0CA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE/E,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,0EAA0E;AAC1E,wFAAwF;AACxF,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAUtF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,gFAAgF;AAChF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,mDAAmD;AACnD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AASjD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvI,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"AA0CA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE/E,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,0EAA0E;AAC1E,wFAAwF;AACxF,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAUtF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,gFAAgF;AAChF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAEjF,mDAAmD;AACnD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AASjD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvI,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC"}
@@ -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;AAEhC,OAAO,EACL,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,eAAe,EACf,oBAAoB,GACrB,MAAM,eAAe,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;AAEhC,OAAO,EAEL,iBAAiB,EACjB,eAAe,EACf,oBAAoB,GACrB,MAAM,eAAe,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.743",
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": [
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by prebuild — do not edit manually
2
- export const CLI_VERSION = "0.1.743";
2
+ export const CLI_VERSION = "0.1.745";
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { BUNDLED_TOOLS, bundledTool } from './bundledTools';
3
+
4
+ describe('bundled tool catalog', () => {
5
+ it('includes the core toolchain as shell tools', () => {
6
+ for (const n of ['node', 'git', 'pnpm', 'uv']) {
7
+ const t = bundledTool(n);
8
+ expect(t, `${n} missing`).toBeDefined();
9
+ expect(t!.category).toBe('toolchain');
10
+ expect(t!.shellTool).toBe(true);
11
+ }
12
+ });
13
+
14
+ it('every tool has a non-empty description and unique name', () => {
15
+ const names = new Set<string>();
16
+ for (const t of BUNDLED_TOOLS) {
17
+ expect(t.description.length, `${t.name} desc`).toBeGreaterThan(0);
18
+ expect(names.has(t.name), `dup ${t.name}`).toBe(false);
19
+ names.add(t.name);
20
+ }
21
+ });
22
+
23
+ it('returns undefined for unknown tools', () => {
24
+ expect(bundledTool('definitely-not-a-tool')).toBeUndefined();
25
+ });
26
+ });
@@ -0,0 +1,50 @@
1
+ // The catalog of bundled command-line tools an app may request through the
2
+ // permission system (like mic/camera). This is VOCABULARY ONLY — names and
3
+ // static metadata. Versions, download sources, install, PATH, and upgrade all
4
+ // live in the host (ugly-studio); ugly-app never touches a binary.
5
+
6
+ export interface BundledTool {
7
+ /** Request/grant key and default spawn name, e.g. 'pnpm'. */
8
+ name: string;
9
+ /** Executable basename when it differs from `name` (e.g. imagemagick → magick). */
10
+ exe?: string;
11
+ category: 'toolchain' | 'media' | 'data' | 'doc' | 'net';
12
+ /** Must be on PATH for `bash -lc` invocations (node/git/pnpm/uv/python). */
13
+ shellTool?: boolean;
14
+ /** Shown in the permission prompt, like mic/camera copy. */
15
+ description: string;
16
+ }
17
+
18
+ export const BUNDLED_TOOLS: readonly BundledTool[] = Object.freeze([
19
+ // toolchain — needed on PATH so shells (`bash -lc 'pnpm dev'`) resolve them.
20
+ { name: 'node', category: 'toolchain', shellTool: true, description: 'Node.js runtime — runs your project and its build tools.' },
21
+ { name: 'git', category: 'toolchain', shellTool: true, description: 'Git — version control for your project.' },
22
+ { name: 'pnpm', category: 'toolchain', shellTool: true, description: 'pnpm — installs your project’s dependencies.' },
23
+ { name: 'uv', category: 'toolchain', shellTool: true, description: 'uv — fast Python package manager.' },
24
+ { name: 'python', exe: 'python3', category: 'toolchain', shellTool: true, description: 'Python runtime.' },
25
+ // net
26
+ { name: 'gh', category: 'net', description: 'GitHub CLI — sign in and manage repositories.' },
27
+ { name: 'cloudflared', category: 'net', description: 'Cloudflare tunnel — exposes your local preview at a public URL.' },
28
+ { name: 'nats', category: 'net', description: 'NATS CLI — messaging server tooling.' },
29
+ // data
30
+ { name: 'postgres', category: 'data', description: 'PostgreSQL — local database for your project.' },
31
+ { name: 'jq', category: 'data', description: 'jq — command-line JSON processor.' },
32
+ { name: 'yq', category: 'data', description: 'yq — command-line YAML processor.' },
33
+ { name: 'pup', category: 'data', description: 'pup — command-line HTML parser.' },
34
+ { name: 'sqlite3', category: 'data', description: 'SQLite — command-line database shell.' },
35
+ // media
36
+ { name: 'ffmpeg', category: 'media', description: 'FFmpeg — audio/video processing.' },
37
+ { name: 'imagemagick', exe: 'magick', category: 'media', description: 'ImageMagick — image conversion and editing.' },
38
+ { name: 'yt-dlp', category: 'media', description: 'yt-dlp — download media from the web.' },
39
+ // doc
40
+ { name: 'pandoc', category: 'doc', description: 'Pandoc — document format conversion.' },
41
+ { name: 'pdftotext', category: 'doc', description: 'pdftotext — extract text from PDFs.' },
42
+ { name: 'tesseract', category: 'doc', description: 'Tesseract — OCR text from images.' },
43
+ ]);
44
+
45
+ const BY_NAME = new Map(BUNDLED_TOOLS.map((t) => [t.name, t]));
46
+
47
+ /** Look up a tool descriptor by its request/grant name. */
48
+ export function bundledTool(name: string): BundledTool | undefined {
49
+ return BY_NAME.get(name);
50
+ }
@@ -65,6 +65,8 @@ export { defineTask, taskContext } from './defineTask.js';
65
65
  export type { TaskDefinition, TaskContext } from './defineTask.js';
66
66
  export { MCP_CATALOG } from './mcp-catalog.js';
67
67
 
68
+ export { BUNDLED_TOOLS, bundledTool, type BundledTool } from './bundledTools.js';
69
+
68
70
  // ── Ugly Proxy (remote UglyNative over WebRTC) ──
69
71
  export { createProxyClient, nativeSigner } from './proxyClient.js';
70
72
  export { createProxyHost } from './proxyHost.js';
@@ -18,10 +18,3 @@ export {
18
18
  type OriginGrant,
19
19
  type PermissionDecision,
20
20
  } from './permissions/types.js';
21
-
22
- export {
23
- type BinaryProvisionConfig,
24
- ensureBundledTool,
25
- findBundledTool,
26
- resolveBundledBinary,
27
- } from './binaries.js';
@@ -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
- }