@garretapp/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -0
- package/dist/chunk-ENIDFSMM.js +29 -0
- package/dist/chunk-RZJO3X6O.js +9 -0
- package/dist/chunk-ZJYHRC5C.js +206 -0
- package/dist/host.cjs +263 -0
- package/dist/host.d.cts +54 -0
- package/dist/host.d.ts +54 -0
- package/dist/host.js +246 -0
- package/dist/index.cjs +77 -0
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +37 -0
- package/dist/platform-CZHtdUK0.d.ts +154 -0
- package/dist/platform-DnI7gJTx.d.cts +154 -0
- package/dist/protocol-Do0BJdeE.d.cts +64 -0
- package/dist/protocol-Do0BJdeE.d.ts +64 -0
- package/dist/react.cjs +496 -0
- package/dist/react.d.cts +128 -0
- package/dist/react.d.ts +128 -0
- package/dist/react.js +242 -0
- package/dist/types-BxSYAH_H.d.cts +88 -0
- package/dist/types-BxSYAH_H.d.ts +88 -0
- package/dist/ui.cjs +249 -0
- package/dist/ui.d.cts +23 -0
- package/dist/ui.d.ts +23 -0
- package/dist/ui.js +3 -0
- package/package.json +37 -0
package/dist/host.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { GarretError } from './chunk-ENIDFSMM.js';
|
|
2
|
+
export { GarretError } from './chunk-ENIDFSMM.js';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import { accessSync, constants, writeFileSync, renameSync, readFileSync, existsSync, mkdirSync } from 'fs';
|
|
5
|
+
import { delimiter, join } from 'path';
|
|
6
|
+
import { randomBytes, createCipheriv, createDecipheriv } from 'crypto';
|
|
7
|
+
|
|
8
|
+
var port = globalThis.process?.parentPort;
|
|
9
|
+
function send(msg) {
|
|
10
|
+
port?.postMessage(msg);
|
|
11
|
+
}
|
|
12
|
+
var STREAM_RUNTIME = /* @__PURE__ */ Symbol("garret.stream");
|
|
13
|
+
function isStream(v) {
|
|
14
|
+
return typeof v === "object" && v !== null && STREAM_RUNTIME in v;
|
|
15
|
+
}
|
|
16
|
+
function defineHost(factory) {
|
|
17
|
+
const children = /* @__PURE__ */ new Set();
|
|
18
|
+
const disposers = [];
|
|
19
|
+
const streams = /* @__PURE__ */ new Map();
|
|
20
|
+
let disposed = false;
|
|
21
|
+
const scrubbedEnv = (base) => {
|
|
22
|
+
const env = {};
|
|
23
|
+
for (const [k, v] of Object.entries(base)) if (!k.startsWith("GARRET_")) env[k] = v;
|
|
24
|
+
return env;
|
|
25
|
+
};
|
|
26
|
+
const track = (child) => {
|
|
27
|
+
children.add(child);
|
|
28
|
+
child.on("exit", () => children.delete(child));
|
|
29
|
+
const r = child;
|
|
30
|
+
r.text = () => new Promise((resolve, reject) => {
|
|
31
|
+
let out = "";
|
|
32
|
+
child.stdout?.on("data", (d) => out += d.toString());
|
|
33
|
+
child.on("error", reject);
|
|
34
|
+
child.on("close", () => resolve(out));
|
|
35
|
+
});
|
|
36
|
+
return r;
|
|
37
|
+
};
|
|
38
|
+
const ctx = {
|
|
39
|
+
emit: (channel, payload) => send({ t: "event", channel, payload }),
|
|
40
|
+
stream: (fn) => ({ [STREAM_RUNTIME]: fn }),
|
|
41
|
+
// Scrub GARRET_* from the FINAL merged env, always — a caller-supplied opts.env must never
|
|
42
|
+
// reopen the leak (the vault key would otherwise reach adb/scrcpy/shells the author didn't write).
|
|
43
|
+
spawn: (argv, opts) => track(
|
|
44
|
+
spawn(argv[0], argv.slice(1), {
|
|
45
|
+
...opts,
|
|
46
|
+
shell: false,
|
|
47
|
+
env: scrubbedEnv({ ...process.env, ...opts?.env ?? {} })
|
|
48
|
+
})
|
|
49
|
+
),
|
|
50
|
+
spawnShell: (command, opts) => track(spawn(command, { ...opts, shell: true, env: scrubbedEnv({ ...process.env, ...opts?.env ?? {} }) })),
|
|
51
|
+
resolveBinary,
|
|
52
|
+
storage: makeStorage(dataDir),
|
|
53
|
+
secrets: makeSecrets(dataDir, secretKey),
|
|
54
|
+
// Pack-shared store only when the host was launched with GARRET_PACK_SHARED_DIR (pack declares `shared`).
|
|
55
|
+
shared: process.env.GARRET_PACK_SHARED_DIR ? { storage: makeStorage(sharedDir), secrets: makeSecrets(sharedDir, sharedSecretKey) } : void 0,
|
|
56
|
+
fetch: (...a) => fetch(...a),
|
|
57
|
+
onDispose: (cb) => disposers.push(cb),
|
|
58
|
+
log: (...args) => console.error("[host]", ...args)
|
|
59
|
+
};
|
|
60
|
+
async function runDispose() {
|
|
61
|
+
if (disposed) return;
|
|
62
|
+
disposed = true;
|
|
63
|
+
for (const ac of streams.values()) ac.abort();
|
|
64
|
+
for (const cb of disposers) {
|
|
65
|
+
try {
|
|
66
|
+
await cb();
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
for (const c of children) c.kill();
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
function driveStream(id, fn) {
|
|
74
|
+
const ac = new AbortController();
|
|
75
|
+
streams.set(id, ac);
|
|
76
|
+
const done = (final) => {
|
|
77
|
+
if (!streams.has(id)) return;
|
|
78
|
+
streams.delete(id);
|
|
79
|
+
final();
|
|
80
|
+
};
|
|
81
|
+
const out = {
|
|
82
|
+
push: (chunk) => streams.has(id) && send({ t: "chunk", id, data: chunk }),
|
|
83
|
+
end: (result) => done(() => send({ t: "stream_end", id, result })),
|
|
84
|
+
error: (err) => done(() => send({ t: "stream_err", id, ...wireError(err) }))
|
|
85
|
+
};
|
|
86
|
+
Promise.resolve().then(() => fn(out, ac.signal)).catch((err) => out.error(err));
|
|
87
|
+
}
|
|
88
|
+
async function handleCall(id, method, args) {
|
|
89
|
+
const methods = ready;
|
|
90
|
+
const fn = methods?.[method];
|
|
91
|
+
if (typeof fn !== "function") {
|
|
92
|
+
return send({ t: "err", id, code: "BAD_ARGS", message: `unknown method: ${method}` });
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const result = await fn(args);
|
|
96
|
+
if (isStream(result)) driveStream(id, result[STREAM_RUNTIME]);
|
|
97
|
+
else send({ t: "res", id, result });
|
|
98
|
+
} catch (err) {
|
|
99
|
+
send({ t: "err", id, ...wireError(err) });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
let ready;
|
|
103
|
+
port?.on("message", (e) => {
|
|
104
|
+
const msg = e.data;
|
|
105
|
+
if (!msg || typeof msg !== "object") return;
|
|
106
|
+
switch (msg.t) {
|
|
107
|
+
case "req":
|
|
108
|
+
case "stream_start":
|
|
109
|
+
void handleCall(msg.id, msg.method, msg.args);
|
|
110
|
+
break;
|
|
111
|
+
case "cancel":
|
|
112
|
+
streams.get(msg.id)?.abort();
|
|
113
|
+
streams.delete(msg.id);
|
|
114
|
+
break;
|
|
115
|
+
case "dispose":
|
|
116
|
+
void runDispose();
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
process.on("SIGTERM", () => void runDispose());
|
|
121
|
+
Promise.resolve().then(() => factory(ctx)).then((methods) => {
|
|
122
|
+
ready = methods;
|
|
123
|
+
send({ t: "ready" });
|
|
124
|
+
}).catch((err) => {
|
|
125
|
+
console.error("[host] factory failed during startup:", err);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function wireError(err) {
|
|
130
|
+
if (err instanceof GarretError) return { code: err.code, message: err.message, hint: err.hint };
|
|
131
|
+
if (err instanceof Error && err.name === "AbortError") return { code: "CANCELLED", message: err.message };
|
|
132
|
+
return { code: "INTERNAL", message: err instanceof Error ? err.message : String(err) };
|
|
133
|
+
}
|
|
134
|
+
var PROBE = {
|
|
135
|
+
darwin: ["/opt/homebrew/bin", "/usr/local/bin", "/opt/homebrew/sbin"],
|
|
136
|
+
linux: ["/usr/local/bin", "/usr/bin", "/snap/bin", `${process.env.HOME}/.local/bin`],
|
|
137
|
+
win32: [`${process.env.LOCALAPPDATA}\\Microsoft\\WinGet\\Packages`, "C:\\ProgramData\\chocolatey\\bin"]
|
|
138
|
+
};
|
|
139
|
+
async function resolveBinary(name, opts) {
|
|
140
|
+
const platform = process.platform;
|
|
141
|
+
const exe = platform === "win32" ? `${name}.exe` : name;
|
|
142
|
+
const dirs = [...process.env.PATH?.split(delimiter) ?? [], ...PROBE[platform] ?? []];
|
|
143
|
+
for (const dir of dirs) {
|
|
144
|
+
if (!dir || dir.includes("undefined")) continue;
|
|
145
|
+
const full = join(dir, exe);
|
|
146
|
+
try {
|
|
147
|
+
accessSync(full, constants.X_OK);
|
|
148
|
+
return full;
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
throw new GarretError("BINARY_NOT_FOUND", `${name} not found on PATH`, {
|
|
153
|
+
hint: opts?.hint ?? `install ${name} via your package manager`
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function dirFromEnv(name) {
|
|
157
|
+
const dir = process.env[name];
|
|
158
|
+
if (!dir) throw new GarretError("UNAVAILABLE", "storage/secrets unavailable (no data dir)");
|
|
159
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
160
|
+
return dir;
|
|
161
|
+
}
|
|
162
|
+
var dataDir = () => dirFromEnv("GARRET_EXT_DATA_DIR");
|
|
163
|
+
var sharedDir = () => dirFromEnv("GARRET_PACK_SHARED_DIR");
|
|
164
|
+
function readJson(file) {
|
|
165
|
+
try {
|
|
166
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
167
|
+
} catch {
|
|
168
|
+
return {};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function writeJsonAtomic(file, obj) {
|
|
172
|
+
const tmp = `${file}.${randomBytes(4).toString("hex")}.tmp`;
|
|
173
|
+
writeFileSync(tmp, JSON.stringify(obj));
|
|
174
|
+
renameSync(tmp, file);
|
|
175
|
+
}
|
|
176
|
+
function makeChain() {
|
|
177
|
+
let chain = Promise.resolve();
|
|
178
|
+
return (fn) => {
|
|
179
|
+
const next = chain.then(fn, fn);
|
|
180
|
+
chain = next.catch(() => void 0);
|
|
181
|
+
return next;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function makeStorage(dir) {
|
|
185
|
+
const queue = makeChain();
|
|
186
|
+
const file = () => join(dir(), "storage.json");
|
|
187
|
+
return {
|
|
188
|
+
get: async (key) => readJson(file())[key],
|
|
189
|
+
set: (key, value) => queue(() => {
|
|
190
|
+
const f = file();
|
|
191
|
+
const all = readJson(f);
|
|
192
|
+
all[key] = value;
|
|
193
|
+
writeJsonAtomic(f, all);
|
|
194
|
+
}),
|
|
195
|
+
delete: (key) => queue(() => {
|
|
196
|
+
const f = file();
|
|
197
|
+
const all = readJson(f);
|
|
198
|
+
delete all[key];
|
|
199
|
+
writeJsonAtomic(f, all);
|
|
200
|
+
}),
|
|
201
|
+
keys: async () => Object.keys(readJson(file())),
|
|
202
|
+
clear: () => queue(() => writeJsonAtomic(file(), {}))
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function keyFromEnv(name) {
|
|
206
|
+
const hex = process.env[name];
|
|
207
|
+
if (!hex) throw new GarretError("UNAVAILABLE", "secrets unavailable on this platform");
|
|
208
|
+
return Buffer.from(hex, "hex");
|
|
209
|
+
}
|
|
210
|
+
var secretKey = () => keyFromEnv("GARRET_EXT_SECRET_KEY");
|
|
211
|
+
var sharedSecretKey = () => keyFromEnv("GARRET_PACK_SHARED_KEY");
|
|
212
|
+
function makeSecrets(dir, getKey) {
|
|
213
|
+
const queue = makeChain();
|
|
214
|
+
const file = () => join(dir(), "secrets.json");
|
|
215
|
+
return {
|
|
216
|
+
get: async (key) => {
|
|
217
|
+
const box = readJson(file())[key];
|
|
218
|
+
if (!box) return void 0;
|
|
219
|
+
const decipher = createDecipheriv("aes-256-gcm", getKey(), Buffer.from(box.iv, "base64"));
|
|
220
|
+
decipher.setAuthTag(Buffer.from(box.tag, "base64"));
|
|
221
|
+
try {
|
|
222
|
+
return decipher.update(Buffer.from(box.ct, "base64")).toString("utf8") + decipher.final("utf8");
|
|
223
|
+
} catch {
|
|
224
|
+
throw new GarretError("INTERNAL", `secret "${key}" failed to decrypt`);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
set: (key, value) => queue(() => {
|
|
228
|
+
const iv = randomBytes(12);
|
|
229
|
+
const cipher = createCipheriv("aes-256-gcm", getKey(), iv);
|
|
230
|
+
const ct = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
231
|
+
const box = { v: 1, iv: iv.toString("base64"), tag: cipher.getAuthTag().toString("base64"), ct: ct.toString("base64") };
|
|
232
|
+
const f = file();
|
|
233
|
+
const all = readJson(f);
|
|
234
|
+
all[key] = box;
|
|
235
|
+
writeJsonAtomic(f, all);
|
|
236
|
+
}),
|
|
237
|
+
delete: (key) => queue(() => {
|
|
238
|
+
const f = file();
|
|
239
|
+
const all = readJson(f);
|
|
240
|
+
delete all[key];
|
|
241
|
+
writeJsonAtomic(f, all);
|
|
242
|
+
})
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export { defineHost };
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var GarretError = class _GarretError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
hint;
|
|
7
|
+
constructor(code, message, options) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "GarretError";
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.hint = options?.hint;
|
|
12
|
+
Object.setPrototypeOf(this, _GarretError.prototype);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function garretErrorFromWire(code, message, hint) {
|
|
16
|
+
const known = [
|
|
17
|
+
"BINARY_NOT_FOUND",
|
|
18
|
+
"NOT_FOUND",
|
|
19
|
+
"PERMISSION",
|
|
20
|
+
"UNAVAILABLE",
|
|
21
|
+
"BAD_ARGS",
|
|
22
|
+
"TIMEOUT",
|
|
23
|
+
"CANCELLED",
|
|
24
|
+
"NETWORK",
|
|
25
|
+
"INTERNAL"
|
|
26
|
+
];
|
|
27
|
+
const c = known.includes(code) ? code : "INTERNAL";
|
|
28
|
+
return new GarretError(c, message, hint ? { hint } : void 0);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/argv.ts
|
|
32
|
+
function parseArgv(command) {
|
|
33
|
+
const out = [];
|
|
34
|
+
let cur = "";
|
|
35
|
+
let has = false;
|
|
36
|
+
let quote = null;
|
|
37
|
+
for (let i = 0; i < command.length; i++) {
|
|
38
|
+
const c = command[i];
|
|
39
|
+
if (quote) {
|
|
40
|
+
if (c === quote) quote = null;
|
|
41
|
+
else if (c === "\\" && quote === '"' && i + 1 < command.length) cur += command[++i];
|
|
42
|
+
else cur += c;
|
|
43
|
+
} else if (c === '"' || c === "'") {
|
|
44
|
+
quote = c;
|
|
45
|
+
has = true;
|
|
46
|
+
} else if (c === " " || c === " " || c === "\n") {
|
|
47
|
+
if (has) {
|
|
48
|
+
out.push(cur);
|
|
49
|
+
cur = "";
|
|
50
|
+
has = false;
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
cur += c;
|
|
54
|
+
has = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (has) out.push(cur);
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/types.ts
|
|
62
|
+
function defineManifest(manifest) {
|
|
63
|
+
return manifest;
|
|
64
|
+
}
|
|
65
|
+
function defineConfig(schema) {
|
|
66
|
+
return schema;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/protocol.ts
|
|
70
|
+
var ACTIVE_CHANNEL = "$active";
|
|
71
|
+
|
|
72
|
+
exports.ACTIVE_CHANNEL = ACTIVE_CHANNEL;
|
|
73
|
+
exports.GarretError = GarretError;
|
|
74
|
+
exports.defineConfig = defineConfig;
|
|
75
|
+
exports.defineManifest = defineManifest;
|
|
76
|
+
exports.garretErrorFromWire = garretErrorFromWire;
|
|
77
|
+
exports.parseArgv = parseArgv;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { C as Capability, a as ConfigField, b as ConfigFieldType, c as ConfigSchema, E as EventMap, G as GarretError, d as GarretErrorCode, e as GarretErrorOptions, H as HostClient, M as Manifest, S as Stream, f as StreamCall, g as defineConfig, h as defineManifest, i as garretErrorFromWire } from './types-BxSYAH_H.cjs';
|
|
2
|
+
export { A as ACTIVE_CHANNEL, C as CallId, T as Transport, W as WireMessage } from './protocol-Do0BJdeE.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Split a command string into an argv array, honoring single/double quotes and backslash escapes
|
|
6
|
+
* (inside double quotes). Dependency-free — for command-runner-style widgets where the user types a
|
|
7
|
+
* command and you pass the result to `ctx.spawn(argv)` (which is array-only, no shell). NOT a full
|
|
8
|
+
* shell parser: no pipes, redirects, globbing, or variable expansion.
|
|
9
|
+
*
|
|
10
|
+
* parseArgv('echo "hello world"') → ['echo', 'hello world']
|
|
11
|
+
* parseArgv("git commit -m 'a b'") → ['git', 'commit', '-m', 'a b']
|
|
12
|
+
*/
|
|
13
|
+
declare function parseArgv(command: string): string[];
|
|
14
|
+
|
|
15
|
+
export { parseArgv };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { C as Capability, a as ConfigField, b as ConfigFieldType, c as ConfigSchema, E as EventMap, G as GarretError, d as GarretErrorCode, e as GarretErrorOptions, H as HostClient, M as Manifest, S as Stream, f as StreamCall, g as defineConfig, h as defineManifest, i as garretErrorFromWire } from './types-BxSYAH_H.js';
|
|
2
|
+
export { A as ACTIVE_CHANNEL, C as CallId, T as Transport, W as WireMessage } from './protocol-Do0BJdeE.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Split a command string into an argv array, honoring single/double quotes and backslash escapes
|
|
6
|
+
* (inside double quotes). Dependency-free — for command-runner-style widgets where the user types a
|
|
7
|
+
* command and you pass the result to `ctx.spawn(argv)` (which is array-only, no shell). NOT a full
|
|
8
|
+
* shell parser: no pipes, redirects, globbing, or variable expansion.
|
|
9
|
+
*
|
|
10
|
+
* parseArgv('echo "hello world"') → ['echo', 'hello world']
|
|
11
|
+
* parseArgv("git commit -m 'a b'") → ['git', 'commit', '-m', 'a b']
|
|
12
|
+
*/
|
|
13
|
+
declare function parseArgv(command: string): string[];
|
|
14
|
+
|
|
15
|
+
export { parseArgv };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export { defineConfig, defineManifest } from './chunk-RZJO3X6O.js';
|
|
2
|
+
export { GarretError, garretErrorFromWire } from './chunk-ENIDFSMM.js';
|
|
3
|
+
|
|
4
|
+
// src/argv.ts
|
|
5
|
+
function parseArgv(command) {
|
|
6
|
+
const out = [];
|
|
7
|
+
let cur = "";
|
|
8
|
+
let has = false;
|
|
9
|
+
let quote = null;
|
|
10
|
+
for (let i = 0; i < command.length; i++) {
|
|
11
|
+
const c = command[i];
|
|
12
|
+
if (quote) {
|
|
13
|
+
if (c === quote) quote = null;
|
|
14
|
+
else if (c === "\\" && quote === '"' && i + 1 < command.length) cur += command[++i];
|
|
15
|
+
else cur += c;
|
|
16
|
+
} else if (c === '"' || c === "'") {
|
|
17
|
+
quote = c;
|
|
18
|
+
has = true;
|
|
19
|
+
} else if (c === " " || c === " " || c === "\n") {
|
|
20
|
+
if (has) {
|
|
21
|
+
out.push(cur);
|
|
22
|
+
cur = "";
|
|
23
|
+
has = false;
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
cur += c;
|
|
27
|
+
has = true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (has) out.push(cur);
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/protocol.ts
|
|
35
|
+
var ACTIVE_CHANNEL = "$active";
|
|
36
|
+
|
|
37
|
+
export { ACTIVE_CHANNEL, parseArgv };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { T as Transport } from './protocol-Do0BJdeE.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Platform capabilities — the Garret-brokered surface available to a widget UI in BOTH tiers
|
|
5
|
+
* (`useGarret()`). Every call is enforced in Garret's main process against the manifest's declared
|
|
6
|
+
* capabilities. The concrete implementation is injected by the preload as `window.__garret` (U3);
|
|
7
|
+
* outside Garret (dev in a browser) a fallback reports `inGarret === false` and throws on use.
|
|
8
|
+
*/
|
|
9
|
+
interface ServiceStatus {
|
|
10
|
+
connected: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface ServiceClient {
|
|
13
|
+
/** Connection state is async (the account is connected in Garret's Settings, brokered by main). */
|
|
14
|
+
status(): Promise<ServiceStatus>;
|
|
15
|
+
query<R = unknown>(method: string, params?: Record<string, unknown>): Promise<R>;
|
|
16
|
+
}
|
|
17
|
+
interface StorageApi {
|
|
18
|
+
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
19
|
+
set(key: string, value: unknown): Promise<void>;
|
|
20
|
+
delete(key: string): Promise<void>;
|
|
21
|
+
keys(): Promise<string[]>;
|
|
22
|
+
clear(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
interface SecretsApi {
|
|
25
|
+
get(key: string): Promise<string | undefined>;
|
|
26
|
+
set(key: string, value: string): Promise<void>;
|
|
27
|
+
delete(key: string): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/** What `g.fetch` resolves to. NOT a DOM `Response` — a Response can't cross the contextBridge from
|
|
30
|
+
* the preload, so Garret hands back this serializable shape (primitive fields + body readers). */
|
|
31
|
+
interface GarretResponse {
|
|
32
|
+
ok: boolean;
|
|
33
|
+
status: number;
|
|
34
|
+
statusText: string;
|
|
35
|
+
headers: Record<string, string>;
|
|
36
|
+
text(): Promise<string>;
|
|
37
|
+
json<T = unknown>(): Promise<T>;
|
|
38
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
39
|
+
}
|
|
40
|
+
/** The pack-shared store (only usable when the pack manifest declares `shared`): one namespace across
|
|
41
|
+
* all the pack's widgets — e.g. a single credential set for a multi-widget service pack. */
|
|
42
|
+
interface SharedApi {
|
|
43
|
+
storage: StorageApi;
|
|
44
|
+
secrets: SecretsApi;
|
|
45
|
+
}
|
|
46
|
+
/** Options for opening a floating sibling surface. All fields optional; sizes in px. */
|
|
47
|
+
interface SurfaceOpenOptions {
|
|
48
|
+
/** initial props delivered to the opened surface as `g.props`; structured-cloned, so each surface
|
|
49
|
+
* gets its own isolated copy (mutations are local). Must be structured-cloneable (no functions). */
|
|
50
|
+
props?: Record<string, unknown>;
|
|
51
|
+
title?: string;
|
|
52
|
+
size?: {
|
|
53
|
+
w: number;
|
|
54
|
+
h: number;
|
|
55
|
+
};
|
|
56
|
+
/** default true — the window stays above normal windows while you work. */
|
|
57
|
+
alwaysOnTop?: boolean;
|
|
58
|
+
/** singleton: a repeat open with the same key focuses the existing window instead of spawning. */
|
|
59
|
+
key?: string;
|
|
60
|
+
}
|
|
61
|
+
/** A handle to a floating surface window opened via `g.surfaces.open`. */
|
|
62
|
+
interface SurfaceHandle {
|
|
63
|
+
readonly id: string;
|
|
64
|
+
close(): Promise<boolean>;
|
|
65
|
+
focus(): Promise<boolean>;
|
|
66
|
+
/** Resolves when the window closes — by the user, `close()`, or its opener being removed. */
|
|
67
|
+
closed(): Promise<void>;
|
|
68
|
+
onClose(cb: () => void): () => void;
|
|
69
|
+
}
|
|
70
|
+
/** Controls for the surface window THIS UI runs in (no-op for a board-placed widget). */
|
|
71
|
+
interface WindowControls {
|
|
72
|
+
/** Lock the window's aspect ratio (width/height); `0` clears it. Use once you know your content
|
|
73
|
+
* size — e.g. a mirror sets the device's ratio after the first video frame.
|
|
74
|
+
*
|
|
75
|
+
* `inset` reserves guest-drawn chrome (px) that is EXCLUDED from the aspect-locked area, so the
|
|
76
|
+
* ratio applies to your content, not the whole window — e.g. a fixed 48px side toolbar passes
|
|
77
|
+
* `{ width: 48 }` and the remaining area keeps `ratio`. Combined with the host's own titlebar. */
|
|
78
|
+
setAspectRatio(ratio: number, inset?: {
|
|
79
|
+
width?: number;
|
|
80
|
+
height?: number;
|
|
81
|
+
}): void;
|
|
82
|
+
/** Resize the window (px). */
|
|
83
|
+
resize(width: number, height: number): void;
|
|
84
|
+
/** Close this surface window (for a frameless surface's own close button). */
|
|
85
|
+
close(): void;
|
|
86
|
+
}
|
|
87
|
+
interface SurfaceApi {
|
|
88
|
+
/** Open a sibling surface (declared in this package's manifest) as a floating, focusable window.
|
|
89
|
+
* Rejects with an Error (message from Garret) if denied — e.g. missing `windows` capability,
|
|
90
|
+
* unknown surface, or the concurrent-window limit. */
|
|
91
|
+
open(surfaceId: string, opts?: SurfaceOpenOptions): Promise<SurfaceHandle>;
|
|
92
|
+
/** Observe closes of this opener's surfaces by instanceId. Unlike a handle's `onClose`/`closed()`
|
|
93
|
+
* (scoped to the current context), this SURVIVES an opener reload — re-subscribe on mount for
|
|
94
|
+
* reload-durable close tracking. */
|
|
95
|
+
onClosed(cb: (instanceId: string) => void): () => void;
|
|
96
|
+
}
|
|
97
|
+
interface GarretPlatform {
|
|
98
|
+
/** per-extension (shared across placements), atomic + key-merged. */
|
|
99
|
+
storage: StorageApi;
|
|
100
|
+
/** per-placement (isolated) — safe for cursors/state that mustn't clobber other instances. */
|
|
101
|
+
instanceStorage: StorageApi;
|
|
102
|
+
secrets: SecretsApi;
|
|
103
|
+
/** Brokered HTTPS fetch (capability `network:<host>` / `network:*`). Resolves to a GarretResponse
|
|
104
|
+
* (a Response-like shape — see the type), not a DOM Response. */
|
|
105
|
+
fetch(url: string, init?: RequestInit): Promise<GarretResponse>;
|
|
106
|
+
/** Pack-shared store — present only when the pack declares `shared`; otherwise its calls reject. */
|
|
107
|
+
shared: SharedApi;
|
|
108
|
+
service<T extends ServiceClient = ServiceClient>(id: string): T;
|
|
109
|
+
notify(title: string, body?: string): void;
|
|
110
|
+
openExternal(url: string): Promise<boolean>;
|
|
111
|
+
clipboard: {
|
|
112
|
+
readText(): Promise<string>;
|
|
113
|
+
writeText(value: string): Promise<void>;
|
|
114
|
+
};
|
|
115
|
+
/** false when the board is ambient/idle — pause rAF/animations, throttle polling. */
|
|
116
|
+
active: boolean;
|
|
117
|
+
onActiveChange(cb: (active: boolean) => void): () => void;
|
|
118
|
+
/** The host (frame ⋯→Settings) asks this widget to open its own config UI. */
|
|
119
|
+
onOpenSettings(cb: () => void): () => void;
|
|
120
|
+
/** Open sibling surfaces (same package) as floating windows. Requires the `windows` capability. */
|
|
121
|
+
surfaces: SurfaceApi;
|
|
122
|
+
/** Controls for this UI's own surface window (no-op for a board-placed widget). */
|
|
123
|
+
window: WindowControls;
|
|
124
|
+
/** Fires once the runtime has bound, with this surface's launch props (`{}` for the board surface).
|
|
125
|
+
* Fires immediately if already ready. Props arrive via the callback — NOT a live getter — because
|
|
126
|
+
* a getter would be frozen at contextBridge exposure time (before bind). Use `useProps()` in React. */
|
|
127
|
+
onReady(cb: (props: Record<string, unknown>) => void): () => void;
|
|
128
|
+
/** false in a plain browser (dev) — render a "run inside Garret" state instead of a blank UI. */
|
|
129
|
+
inGarret: boolean;
|
|
130
|
+
}
|
|
131
|
+
/** What the preload injects. Extends the platform with the wiring the SDK runtimes need. */
|
|
132
|
+
interface GarretRuntime extends GarretPlatform {
|
|
133
|
+
instanceId: string;
|
|
134
|
+
/** the per-widget host bridge; null for web widgets (no host). */
|
|
135
|
+
hostTransport: Transport | null;
|
|
136
|
+
config: {
|
|
137
|
+
get(): unknown;
|
|
138
|
+
/** replace=false → shallow merge (patch); true → full replace. */
|
|
139
|
+
set(value: unknown, replace?: boolean): void;
|
|
140
|
+
subscribe(cb: (config: unknown) => void): () => void;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
declare global {
|
|
144
|
+
interface Window {
|
|
145
|
+
__garret?: GarretRuntime;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
declare function getRuntime(): GarretRuntime | undefined;
|
|
149
|
+
declare function getHostTransport(): Transport | null;
|
|
150
|
+
declare function getInstanceId(): string;
|
|
151
|
+
/** The platform capabilities. Real inside Garret; a fail-loud fallback in a plain browser. */
|
|
152
|
+
declare function getGarret(): GarretPlatform;
|
|
153
|
+
|
|
154
|
+
export { type GarretPlatform as G, type SurfaceApi as S, type WindowControls as W, type SurfaceHandle as a, type SurfaceOpenOptions as b, type GarretRuntime as c, type SecretsApi as d, type ServiceClient as e, type StorageApi as f, getGarret as g, getHostTransport as h, getInstanceId as i, getRuntime as j };
|