ugly-app 0.1.692 → 0.1.693

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.692";
1
+ export declare const CLI_VERSION = "0.1.693";
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.692";
2
+ export const CLI_VERSION = "0.1.693";
3
3
  //# sourceMappingURL=version.js.map
@@ -5,6 +5,7 @@ export { CHANNEL_FALLBACK, NAMESPACES, fallbackFor } from './contract-meta.js';
5
5
  export type { NativePlatform, UglyNative } from './protocol.js';
6
6
  export { NativeUnavailableError, PermissionDeniedError } from './protocol.js';
7
7
  export { installUglyNative } from './install.js';
8
+ export { createNodeUglyNative } from './node.js';
8
9
  export { native, permissions, nativePlatform, isNativeAvailable } from './wrapper.js';
9
10
  export type { PermissionsFacade } from './permissions.js';
10
11
  export type { UglyProcess } from './facades/process.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,GACT,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;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;AAEhE,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,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":"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,GACT,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;AAEhE,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,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"}
@@ -2,6 +2,9 @@ export { namespaceOf } from './contract.js';
2
2
  export { CHANNEL_FALLBACK, NAMESPACES, fallbackFor } from './contract-meta.js';
3
3
  export { NativeUnavailableError, PermissionDeniedError } from './protocol.js';
4
4
  export { installUglyNative } from './install.js';
5
+ // Node-backed UglyNative for background-task children (a task bundle sets
6
+ // globalThis.UglyNative = createNodeUglyNative() before importing the `native` facade).
7
+ export { createNodeUglyNative } from './node.js';
5
8
  export { native, permissions, nativePlatform, isNativeAvailable } from './wrapper.js';
6
9
  // Background-task authoring SDK (used inside a task bundle, not the app shell).
7
10
  export { defineTask, taskContext } from './defineTask.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"AAmCA,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,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAUtF,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;AAQnE,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":"AAmCA,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,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;AAQnE,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"}
@@ -0,0 +1,3 @@
1
+ import type { UglyNative } from './protocol.js';
2
+ export declare function createNodeUglyNative(): UglyNative;
3
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/native/node.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAYhD,wBAAgB,oBAAoB,IAAI,UAAU,CAwFjD"}
@@ -0,0 +1,129 @@
1
+ // Node-backed UglyNative for background-task children. A task bundle runs in a plain Node
2
+ // child process (full Node API, OS-confined by the host), so inside it `native.fs.*` /
3
+ // `native.process.*` resolve to node:fs / child_process here — no host round-trip and no
4
+ // permission gate (the process boundary IS the boundary). A task bundle installs it before
5
+ // importing the framework facade: globalThis.UglyNative = createNodeUglyNative().
6
+ // Device caps + other desktop namespaces (net/serve/workers/browse/mcp/...) aren't provided
7
+ // and reject with NativeUnavailableError.
8
+ import { promises as fsp, realpathSync } from 'node:fs';
9
+ import { spawn } from 'node:child_process';
10
+ import { NativeUnavailableError } from './protocol.js';
11
+ // node = full local access, so every gated namespace reports granted (the facade's
12
+ // permissions.ensure() reads this before fs/process calls).
13
+ const ALL_GRANTED = {
14
+ granted: {
15
+ fs: 'full', process: 'full', net: 'granted', serve: 'granted', workers: 'granted',
16
+ secrets: 'granted', browse: 'granted', mcp: 'granted', terminal: 'granted', task: 'full',
17
+ },
18
+ };
19
+ export function createNodeUglyNative() {
20
+ const listeners = new Map();
21
+ const emit = (event, data) => {
22
+ const s = listeners.get(event);
23
+ if (s)
24
+ for (const cb of s) {
25
+ try {
26
+ cb(data);
27
+ }
28
+ catch { /* best-effort */ }
29
+ }
30
+ };
31
+ const procs = new Map();
32
+ let seq = 0;
33
+ function spawnProc(cmd, args, opts) {
34
+ const id = `n${seq++}`;
35
+ const env = opts?.replaceEnv ? (opts.env ?? {}) : { ...process.env, ...(opts?.env ?? {}) };
36
+ const child = spawn(cmd, args, { cwd: opts?.cwd, env, detached: opts?.detached ?? false });
37
+ procs.set(id, child);
38
+ child.stdout?.on('data', (d) => emit(`process.stdout:${id}`, { chunk: d.toString() }));
39
+ child.stderr?.on('data', (d) => emit(`process.stderr:${id}`, { chunk: d.toString() }));
40
+ child.on('error', (e) => emit(`process.error:${id}`, { err: e.message }));
41
+ child.on('exit', (code) => { emit(`process.exit:${id}`, { code }); procs.delete(id); });
42
+ return { id, pid: child.pid ?? null };
43
+ }
44
+ async function invoke(channel, payload) {
45
+ const p = (payload ?? {});
46
+ const str = (k) => String(p[k] ?? '');
47
+ switch (channel) {
48
+ // permissions — node grants everything (process isolation is the boundary)
49
+ case 'permissions.query':
50
+ case 'permissions.request':
51
+ return ALL_GRANTED;
52
+ case 'permissions.revoke':
53
+ return undefined;
54
+ // fs (text)
55
+ case 'fs.readFile':
56
+ return { content: await fsp.readFile(str('path'), 'utf8') };
57
+ case 'fs.writeFile':
58
+ await fsp.writeFile(str('path'), String(p.content ?? ''));
59
+ return undefined;
60
+ case 'fs.mkdir':
61
+ await fsp.mkdir(str('path'), { recursive: p.recursive === true });
62
+ return undefined;
63
+ case 'fs.rm':
64
+ await fsp.rm(str('path'), { recursive: p.recursive === true, force: p.force === true });
65
+ return undefined;
66
+ case 'fs.rename':
67
+ await fsp.rename(str('from'), str('to'));
68
+ return undefined;
69
+ case 'fs.readdir': {
70
+ const ents = await fsp.readdir(str('path'), { withFileTypes: true });
71
+ const entries = ents.map((e) => ({ name: e.name, isDirectory: e.isDirectory(), isFile: e.isFile() }));
72
+ return { entries };
73
+ }
74
+ case 'fs.stat': {
75
+ const st = await fsp.stat(str('path'));
76
+ const r = { size: st.size, isDirectory: st.isDirectory(), isFile: st.isFile(), mtimeMs: st.mtimeMs };
77
+ return r;
78
+ }
79
+ case 'fs.exists':
80
+ try {
81
+ await fsp.access(str('path'));
82
+ return { exists: true };
83
+ }
84
+ catch {
85
+ return { exists: false };
86
+ }
87
+ case 'fs.realpath':
88
+ return { path: realpathSync(str('path')) };
89
+ // fs (bytes ride as a latin1 binary string, one char per byte)
90
+ case 'fs.readFileBytes':
91
+ return { data: (await fsp.readFile(str('path'))).toString('latin1') };
92
+ case 'fs.writeFileBytes':
93
+ await fsp.writeFile(str('path'), Buffer.from(str('data'), 'latin1'));
94
+ return undefined;
95
+ // process (id-stream; stdout/stderr/exit/error arrive as process.<kind>:<id> events)
96
+ case 'process.spawn':
97
+ return spawnProc(str('cmd'), p.args ?? [], p.opts);
98
+ case 'process.write':
99
+ procs.get(str('id'))?.stdin?.write(String(p.data ?? ''));
100
+ return undefined;
101
+ case 'process.closeStdin':
102
+ procs.get(str('id'))?.stdin?.end();
103
+ return undefined;
104
+ case 'process.kill':
105
+ try {
106
+ procs.get(str('id'))?.kill(p.signal);
107
+ }
108
+ catch { /* dead */ }
109
+ return undefined;
110
+ default:
111
+ throw new NativeUnavailableError(channel);
112
+ }
113
+ }
114
+ return {
115
+ platform: 'desktop',
116
+ invoke: ((channel, payload) => invoke(channel, payload)),
117
+ subscribe: ((event, cb) => {
118
+ const key = event;
119
+ let s = listeners.get(key);
120
+ if (!s) {
121
+ s = new Set();
122
+ listeners.set(key, s);
123
+ }
124
+ s.add(cb);
125
+ return () => { listeners.get(key)?.delete(cb); };
126
+ }),
127
+ };
128
+ }
129
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/native/node.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugly-app",
3
- "version": "0.1.692",
3
+ "version": "0.1.693",
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.692";
2
+ export const CLI_VERSION = "0.1.693";
@@ -39,6 +39,9 @@ export { CHANNEL_FALLBACK, NAMESPACES, fallbackFor } from './contract-meta.js';
39
39
  export type { NativePlatform, UglyNative } from './protocol.js';
40
40
  export { NativeUnavailableError, PermissionDeniedError } from './protocol.js';
41
41
  export { installUglyNative } from './install.js';
42
+ // Node-backed UglyNative for background-task children (a task bundle sets
43
+ // globalThis.UglyNative = createNodeUglyNative() before importing the `native` facade).
44
+ export { createNodeUglyNative } from './node.js';
42
45
  export { native, permissions, nativePlatform, isNativeAvailable } from './wrapper.js';
43
46
  export type { PermissionsFacade } from './permissions.js';
44
47
  export type { UglyProcess } from './facades/process.js';
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect, afterAll } from 'vitest';
2
+ import { promises as fsp } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { createNodeUglyNative } from './node.js';
6
+ import { NativeUnavailableError } from './protocol.js';
7
+
8
+ const n = createNodeUglyNative();
9
+ let dir = '';
10
+
11
+ describe('createNodeUglyNative', () => {
12
+ it('platform is desktop and permissions report granted', async () => {
13
+ expect(n.platform).toBe('desktop');
14
+ const r = (await n.invoke('permissions.query', undefined)) as { granted: Record<string, string> };
15
+ expect(r.granted.fs).toBe('full');
16
+ expect(r.granted.process).toBe('full');
17
+ });
18
+
19
+ it('fs round-trips (write/read/readdir/stat/exists/rm)', async () => {
20
+ dir = await fsp.mkdtemp(path.join(os.tmpdir(), 'node-native-'));
21
+ const f = path.join(dir, 'a.txt');
22
+ await n.invoke('fs.writeFile', { path: f, content: 'hello' });
23
+ expect(await n.invoke('fs.readFile', { path: f })).toEqual({ content: 'hello' });
24
+ const ls = (await n.invoke('fs.readdir', { path: dir })) as { entries: { name: string; isFile: boolean }[] };
25
+ expect(ls.entries.find((e) => e.name === 'a.txt')?.isFile).toBe(true);
26
+ const st = (await n.invoke('fs.stat', { path: f })) as { size: number; isFile: boolean };
27
+ expect(st.isFile).toBe(true);
28
+ expect(st.size).toBe(5);
29
+ expect(await n.invoke('fs.exists', { path: f })).toEqual({ exists: true });
30
+ await n.invoke('fs.rm', { path: f });
31
+ expect(await n.invoke('fs.exists', { path: f })).toEqual({ exists: false });
32
+ });
33
+
34
+ it('byte ops round-trip binary via latin1', async () => {
35
+ const f = path.join(dir, 'bin');
36
+ const data = String.fromCharCode(0, 1, 2, 255, 128);
37
+ await n.invoke('fs.writeFileBytes', { path: f, data });
38
+ expect(await n.invoke('fs.readFileBytes', { path: f })).toEqual({ data });
39
+ });
40
+
41
+ it('process.spawn streams stdout + exit', async () => {
42
+ const r = (await n.invoke('process.spawn', { cmd: 'node', args: ['-e', 'process.stdout.write("hi")'] })) as { id: string; pid: number | null };
43
+ expect(typeof r.id).toBe('string');
44
+ const out: string[] = [];
45
+ n.subscribe(`process.stdout:${r.id}` as never, ((d: { chunk: string }) => out.push(d.chunk)) as never);
46
+ const code = await new Promise<number | null>((resolve) => {
47
+ n.subscribe(`process.exit:${r.id}` as never, ((d: { code: number | null }) => resolve(d.code)) as never);
48
+ });
49
+ expect(code).toBe(0);
50
+ expect(out.join('')).toBe('hi');
51
+ });
52
+
53
+ it('unsupported namespaces reject', async () => {
54
+ await expect(n.invoke('net.connect' as never, { host: 'x', port: 1 } as never)).rejects.toBeInstanceOf(NativeUnavailableError);
55
+ });
56
+
57
+ afterAll(async () => {
58
+ if (dir) await fsp.rm(dir, { recursive: true, force: true });
59
+ });
60
+ });
@@ -0,0 +1,111 @@
1
+ // Node-backed UglyNative for background-task children. A task bundle runs in a plain Node
2
+ // child process (full Node API, OS-confined by the host), so inside it `native.fs.*` /
3
+ // `native.process.*` resolve to node:fs / child_process here — no host round-trip and no
4
+ // permission gate (the process boundary IS the boundary). A task bundle installs it before
5
+ // importing the framework facade: globalThis.UglyNative = createNodeUglyNative().
6
+ // Device caps + other desktop namespaces (net/serve/workers/browse/mcp/...) aren't provided
7
+ // and reject with NativeUnavailableError.
8
+ import { promises as fsp, realpathSync } from 'node:fs';
9
+ import { spawn, type ChildProcess } from 'node:child_process';
10
+ import type { Channel, DataOf, HostDirent, HostStat, NativeEvent, PayloadOf, ResultOf, SpawnOpts } from './contract.js';
11
+ import type { UglyNative } from './protocol.js';
12
+ import { NativeUnavailableError } from './protocol.js';
13
+
14
+ // node = full local access, so every gated namespace reports granted (the facade's
15
+ // permissions.ensure() reads this before fs/process calls).
16
+ const ALL_GRANTED = {
17
+ granted: {
18
+ fs: 'full', process: 'full', net: 'granted', serve: 'granted', workers: 'granted',
19
+ secrets: 'granted', browse: 'granted', mcp: 'granted', terminal: 'granted', task: 'full',
20
+ },
21
+ };
22
+
23
+ export function createNodeUglyNative(): UglyNative {
24
+ const listeners = new Map<string, Set<(d: unknown) => void>>();
25
+ const emit = (event: string, data: unknown): void => {
26
+ const s = listeners.get(event);
27
+ if (s) for (const cb of s) { try { cb(data); } catch { /* best-effort */ } }
28
+ };
29
+
30
+ const procs = new Map<string, ChildProcess>();
31
+ let seq = 0;
32
+
33
+ function spawnProc(cmd: string, args: string[], opts?: SpawnOpts): { id: string; pid: number | null } {
34
+ const id = `n${seq++}`;
35
+ const env = opts?.replaceEnv ? (opts.env ?? {}) : { ...process.env, ...(opts?.env ?? {}) };
36
+ const child = spawn(cmd, args, { cwd: opts?.cwd, env, detached: opts?.detached ?? false });
37
+ procs.set(id, child);
38
+ child.stdout?.on('data', (d: Buffer) => emit(`process.stdout:${id}`, { chunk: d.toString() }));
39
+ child.stderr?.on('data', (d: Buffer) => emit(`process.stderr:${id}`, { chunk: d.toString() }));
40
+ child.on('error', (e: Error) => emit(`process.error:${id}`, { err: e.message }));
41
+ child.on('exit', (code: number | null) => { emit(`process.exit:${id}`, { code }); procs.delete(id); });
42
+ return { id, pid: child.pid ?? null };
43
+ }
44
+
45
+ async function invoke(channel: string, payload: unknown): Promise<unknown> {
46
+ const p = (payload ?? {}) as Record<string, unknown>;
47
+ const str = (k: string): string => String(p[k] ?? '');
48
+ switch (channel) {
49
+ // permissions — node grants everything (process isolation is the boundary)
50
+ case 'permissions.query':
51
+ case 'permissions.request':
52
+ return ALL_GRANTED;
53
+ case 'permissions.revoke':
54
+ return undefined;
55
+ // fs (text)
56
+ case 'fs.readFile':
57
+ return { content: await fsp.readFile(str('path'), 'utf8') };
58
+ case 'fs.writeFile':
59
+ await fsp.writeFile(str('path'), String(p.content ?? '')); return undefined;
60
+ case 'fs.mkdir':
61
+ await fsp.mkdir(str('path'), { recursive: p.recursive === true }); return undefined;
62
+ case 'fs.rm':
63
+ await fsp.rm(str('path'), { recursive: p.recursive === true, force: p.force === true }); return undefined;
64
+ case 'fs.rename':
65
+ await fsp.rename(str('from'), str('to')); return undefined;
66
+ case 'fs.readdir': {
67
+ const ents = await fsp.readdir(str('path'), { withFileTypes: true });
68
+ const entries: HostDirent[] = ents.map((e) => ({ name: e.name, isDirectory: e.isDirectory(), isFile: e.isFile() }));
69
+ return { entries };
70
+ }
71
+ case 'fs.stat': {
72
+ const st = await fsp.stat(str('path'));
73
+ const r: HostStat = { size: st.size, isDirectory: st.isDirectory(), isFile: st.isFile(), mtimeMs: st.mtimeMs };
74
+ return r;
75
+ }
76
+ case 'fs.exists':
77
+ try { await fsp.access(str('path')); return { exists: true }; } catch { return { exists: false }; }
78
+ case 'fs.realpath':
79
+ return { path: realpathSync(str('path')) };
80
+ // fs (bytes ride as a latin1 binary string, one char per byte)
81
+ case 'fs.readFileBytes':
82
+ return { data: (await fsp.readFile(str('path'))).toString('latin1') };
83
+ case 'fs.writeFileBytes':
84
+ await fsp.writeFile(str('path'), Buffer.from(str('data'), 'latin1')); return undefined;
85
+ // process (id-stream; stdout/stderr/exit/error arrive as process.<kind>:<id> events)
86
+ case 'process.spawn':
87
+ return spawnProc(str('cmd'), (p.args as string[]) ?? [], p.opts as SpawnOpts | undefined);
88
+ case 'process.write':
89
+ procs.get(str('id'))?.stdin?.write(String(p.data ?? '')); return undefined;
90
+ case 'process.closeStdin':
91
+ procs.get(str('id'))?.stdin?.end(); return undefined;
92
+ case 'process.kill':
93
+ try { procs.get(str('id'))?.kill((p.signal as NodeJS.Signals | undefined)); } catch { /* dead */ } return undefined;
94
+ default:
95
+ throw new NativeUnavailableError(channel);
96
+ }
97
+ }
98
+
99
+ return {
100
+ platform: 'desktop',
101
+ invoke: (<C extends Channel>(channel: C, payload: PayloadOf<C>) =>
102
+ invoke(channel, payload) as Promise<ResultOf<C>>) as UglyNative['invoke'],
103
+ subscribe: (<E extends NativeEvent>(event: E, cb: (data: DataOf<E>) => void) => {
104
+ const key = event as string;
105
+ let s = listeners.get(key);
106
+ if (!s) { s = new Set(); listeners.set(key, s); }
107
+ s.add(cb as (d: unknown) => void);
108
+ return () => { listeners.get(key)?.delete(cb as (d: unknown) => void); };
109
+ }) as UglyNative['subscribe'],
110
+ };
111
+ }