@tealbrick/kit 0.3.0-rc.7 → 0.3.0-rc.8

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/buzz.js ADDED
@@ -0,0 +1,262 @@
1
+ import { chmod, copyFile, lstat, mkdir, readFile, readdir, rename, stat, unlink, writeFile } from 'node:fs/promises';
2
+ import { createHash } from 'node:crypto';
3
+ import { execFile } from 'node:child_process';
4
+ import { homedir } from 'node:os';
5
+ import { delimiter, dirname, isAbsolute, join, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { promisify } from 'node:util';
8
+ /** Buzz Desktop managed-agent provider (client Mac) and its agent host (Mac running buzz-acp + kit native acp). */
9
+ const run = promisify(execFile);
10
+ const assets = fileURLToPath(new URL('../assets/buzz/', import.meta.url));
11
+ export const PROVIDER_MARKER = 'tealbrick-buzz-provider';
12
+ export const buzzUsage = `Usage: tealbrick buzz provider install [--name NAME] [--bin-dir DIRECTORY] [--force]
13
+ tealbrick buzz host setup [--owners-dir DIRECTORY] [--buzz-app PATH] [--node PATH] [--kit-cli PATH] [--respond-to-default owner-only|anyone]
14
+ tealbrick buzz host remove <label>`;
15
+ async function kitVersion() { return JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')).version; }
16
+ function flagsOf(args, values, switches) {
17
+ const flags = {};
18
+ for (let i = 0; i < args.length; i++) {
19
+ const name = /^--([a-z-]+)$/.exec(args[i])?.[1];
20
+ if (!name || flags[name] !== undefined)
21
+ throw Error('kit_buzz_invalid_arguments');
22
+ if (switches.includes(name)) {
23
+ flags[name] = true;
24
+ continue;
25
+ }
26
+ if (!values.includes(name) || !args[i + 1] || args[i + 1].startsWith('--'))
27
+ throw Error('kit_buzz_invalid_arguments');
28
+ flags[name] = args[++i];
29
+ }
30
+ return flags;
31
+ }
32
+ function expand(path, home) { return resolve(path === '~' ? home : path.startsWith('~/') ? join(home, path.slice(2)) : path); }
33
+ async function exists(path) { try {
34
+ await stat(path);
35
+ return true;
36
+ }
37
+ catch {
38
+ return false;
39
+ } }
40
+ async function writeAtomic(path, body, mode) {
41
+ const tmp = `${path}.${process.pid}.tmp`;
42
+ await writeFile(tmp, body, { mode });
43
+ await chmod(tmp, mode);
44
+ await rename(tmp, path);
45
+ }
46
+ /** Paths end up in a shell script and in buzz-acp's comma-separated BUZZ_ACP_AGENT_ARGS. */
47
+ function hostPath(name, path) {
48
+ if (!isAbsolute(path) || /[,\x00-\x1f\x7f]/.test(path))
49
+ throw Error(`kit_buzz_${name}_path_invalid`);
50
+ return path;
51
+ }
52
+ function shellQuote(value) { return `'${value.replace(/'/g, `'\\''`)}'`; }
53
+ async function python3Ok() { try {
54
+ await run('/usr/bin/python3', ['-c', 'import json, plistlib']);
55
+ return true;
56
+ }
57
+ catch {
58
+ return false;
59
+ } }
60
+ export async function installProvider(options = {}) {
61
+ const home = options.home ?? homedir(), name = options.name ?? 'tealbrick-mac';
62
+ if (!/^[a-z0-9-]{3,40}$/.test(name))
63
+ throw Error('kit_buzz_provider_name_invalid');
64
+ const binDir = expand(options.binDir ?? '~/.local/bin', home), path = join(binDir, `buzz-backend-${name}`);
65
+ let existing;
66
+ try {
67
+ existing = await readFile(path, 'utf8');
68
+ }
69
+ catch (error) {
70
+ if (error?.code !== 'ENOENT')
71
+ throw error;
72
+ }
73
+ if (existing !== undefined && !existing.split('\n', 3).some(line => line.includes(PROVIDER_MARKER)) && !options.force)
74
+ throw Error('kit_buzz_provider_foreign_file');
75
+ const version = await kitVersion();
76
+ const body = (await readFile(join(assets, 'buzz-backend-provider.py'), 'utf8')).replaceAll('__TB_PROVIDER_ID__', name).replaceAll('__TB_KIT_VERSION__', version);
77
+ await mkdir(binDir, { recursive: true });
78
+ await writeAtomic(path, body, 0o755);
79
+ const onPath = (process.env.PATH ?? '').split(delimiter).some(entry => entry && resolve(entry) === binDir);
80
+ return { path, name, version, updated: existing !== undefined, discoverable: onPath || binDir === join(home, '.local', 'bin'), python3: await python3Ok() };
81
+ }
82
+ async function npmInstallKit(prefix, version) {
83
+ const local = join(dirname(process.execPath), 'npm');
84
+ const npm = await exists(local) ? local : 'npm';
85
+ try {
86
+ await run(npm, ['install', '--prefix', prefix, '--no-audit', '--no-fund', '--save-exact', `@tealbrick/kit@${version}`], { maxBuffer: 16 * 1024 * 1024 });
87
+ }
88
+ catch {
89
+ throw Error('kit_buzz_kit_install_failed');
90
+ }
91
+ return join(prefix, 'node_modules', '@tealbrick', 'kit', 'dist', 'cli.js');
92
+ }
93
+ async function bundleVersion(app) {
94
+ const plist = join(app, 'Contents', 'Info.plist');
95
+ try {
96
+ const { stdout } = await run('/usr/bin/plutil', ['-extract', 'CFBundleShortVersionString', 'raw', '-o', '-', plist]);
97
+ if (stdout.trim())
98
+ return stdout.trim();
99
+ }
100
+ catch { }
101
+ try {
102
+ const text = await readFile(plist, 'utf8');
103
+ return /<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(text)?.[1] ?? 'unknown';
104
+ }
105
+ catch {
106
+ return 'unknown';
107
+ }
108
+ }
109
+ async function sha256(path) { return createHash('sha256').update(await readFile(path)).digest('hex'); }
110
+ export async function setupBuzzHost(options = {}) {
111
+ const home = options.home ?? homedir(), version = await kitVersion();
112
+ if (options.respondToDefault !== undefined && !['owner-only', 'anyone'].includes(options.respondToDefault))
113
+ throw Error('kit_buzz_respond_to_default_invalid');
114
+ const ownersDir = hostPath('owners_dir', expand(options.ownersDir ?? '~/agents/tealbrick-connections', home));
115
+ const app = expand(options.buzzApp ?? '/Applications/Buzz.app', home);
116
+ const node = hostPath('node', expand(options.node ?? process.execPath, home));
117
+ if (!(await stat(node).catch(() => undefined))?.isFile())
118
+ throw Error('kit_buzz_node_missing');
119
+ const sources = { buzz: join(app, 'Contents', 'MacOS', 'buzz'), 'buzz-acp': join(app, 'Contents', 'MacOS', 'buzz-acp') };
120
+ for (const source of Object.values(sources))
121
+ if (!(await stat(source).catch(() => undefined))?.isFile())
122
+ throw Error('kit_buzz_app_binaries_missing');
123
+ const root = join(home, 'agents', 'buzz'), bin = join(root, 'bin'), runtime = join(root, 'runtime');
124
+ for (const dir of [root, bin, runtime]) {
125
+ await mkdir(dir, { recursive: true, mode: 0o700 });
126
+ await chmod(dir, 0o700);
127
+ }
128
+ const buzzBin = hostPath('buzz_bin', join(bin, 'buzz')), buzzAcpBin = hostPath('buzz_acp_bin', join(bin, 'buzz-acp'));
129
+ // Copy then rename, so a running buzz-acp keeps its inode and a Buzz.app update never changes agents silently.
130
+ for (const [source, target] of [[sources.buzz, buzzBin], [sources['buzz-acp'], buzzAcpBin]]) {
131
+ const tmp = `${target}.${process.pid}.tmp`;
132
+ await copyFile(source, tmp);
133
+ await chmod(tmp, 0o755);
134
+ await rename(tmp, target);
135
+ }
136
+ const kitCli = hostPath('kit_cli', options.kitCli ? expand(options.kitCli, home) : await (options.installKit ?? npmInstallKit)(runtime, version));
137
+ if (!(await stat(kitCli).catch(() => undefined))?.isFile())
138
+ throw Error('kit_buzz_kit_cli_missing');
139
+ const host = { version: 1, kitVersion: version, node, kitCli, ownersDir, buzzBin, buzzAcpBin, buzzAppVersion: await bundleVersion(app),
140
+ buzzSha256: { buzz: await sha256(buzzBin), 'buzz-acp': await sha256(buzzAcpBin) } };
141
+ await writeAtomic(join(root, 'host.json'), JSON.stringify(host, null, 1) + '\n', 0o600);
142
+ const values = { NODE: node, KIT_CLI: kitCli, OWNERS_DIR: ownersDir, BUZZ_BIN: buzzBin, BUZZ_ACP_BIN: buzzAcpBin };
143
+ const runner = (await readFile(join(assets, 'buzz-run.sh'), 'utf8')).replace(/@@([A-Z_]+)@@/g, (_, key) => { if (!Object.hasOwn(values, key))
144
+ throw Error('kit_buzz_asset_placeholder_unknown'); return shellQuote(values[key]); });
145
+ await writeAtomic(join(bin, 'buzz-run.sh'), runner, 0o700);
146
+ for (const script of ['buzz-plist.py', 'tb-provider-apply.py'])
147
+ await writeAtomic(join(bin, script), await readFile(join(assets, script)), 0o700);
148
+ if (options.respondToDefault)
149
+ await writeAtomic(join(root, 'fleet-defaults.json'), JSON.stringify({ respond_to: options.respondToDefault }) + '\n', 0o600);
150
+ const owners = [];
151
+ try {
152
+ for (const entry of await readdir(ownersDir, { withFileTypes: true }))
153
+ if (entry.isDirectory() && /^[a-z0-9-]{2,32}$/.test(entry.name) && await exists(join(ownersDir, entry.name, '.tealbrick', 'native-serve.json')))
154
+ owners.push(entry.name);
155
+ }
156
+ catch { }
157
+ const warnings = [];
158
+ if (!await python3Ok())
159
+ warnings.push('/usr/bin/python3 is not usable; install the Xcode Command Line Tools (xcode-select --install).');
160
+ if (!owners.length)
161
+ warnings.push(`No enrolled kit owners under ${ownersDir}; run \`tealbrick native setup\` and \`tealbrick native enroll\` in ${ownersDir}/<owner>.`);
162
+ return { hostJson: join(root, 'host.json'), ...host, scripts: ['buzz-run.sh', 'buzz-plist.py', 'tb-provider-apply.py'].map(name => join(bin, name)),
163
+ fleetDefaults: options.respondToDefault ?? null, owners, warnings };
164
+ }
165
+ const launchctl = args => run('/bin/launchctl', args).then(() => 0, (e) => typeof e?.code === 'number' ? e.code : 1);
166
+ const AGENT_LABEL = /^[a-z0-9][a-z0-9-]{2,62}$/;
167
+ /** Names under ~/agents/buzz that are host files, never agents. */
168
+ const RESERVED_LABELS = ['bin', 'runtime', 'logs'];
169
+ /**
170
+ * Remove one provider-managed agent from this host (Buzz Desktop has no undeploy operation): boot out its
171
+ * launchd unit, delete the plist and move ~/agents/buzz/<label> to ~/agents/buzz/.removed/<label>-<ts>
172
+ * (0700; it still holds the agent key until you delete it).
173
+ */
174
+ export async function removeBuzzAgent(label, options = {}) {
175
+ if (!AGENT_LABEL.test(label) || RESERVED_LABELS.includes(label))
176
+ throw Error('kit_buzz_label_invalid');
177
+ const home = options.home ?? homedir(), uid = options.uid ?? process.getuid?.() ?? 0, unit = `com.tealbrick.buzz-acp.${label}`, service = `gui/${uid}/${unit}`;
178
+ const root = join(home, 'agents', 'buzz'), dir = join(root, label), plist = join(home, 'Library', 'LaunchAgents', `${unit}.plist`);
179
+ const exec = options.launchctl ?? launchctl;
180
+ const loaded = await exec(['print', service]) === 0;
181
+ if (loaded && await exec(['bootout', service]) !== 0)
182
+ throw Error('kit_buzz_bootout_failed');
183
+ const plistStat = await lstat(plist).catch(() => undefined);
184
+ if (plistStat) {
185
+ if (!plistStat.isFile() || plistStat.isSymbolicLink())
186
+ throw Error('kit_buzz_plist_unsafe');
187
+ await unlink(plist);
188
+ }
189
+ const dirStat = await lstat(dir).catch(() => undefined);
190
+ let movedTo;
191
+ if (dirStat) {
192
+ if (!dirStat.isDirectory() || dirStat.isSymbolicLink())
193
+ throw Error('kit_buzz_agent_dir_unsafe');
194
+ const removed = join(root, '.removed');
195
+ await mkdir(removed, { recursive: true, mode: 0o700 });
196
+ const removedStat = await lstat(removed);
197
+ if (!removedStat.isDirectory() || removedStat.isSymbolicLink())
198
+ throw Error('kit_buzz_removed_dir_unsafe');
199
+ await chmod(removed, 0o700);
200
+ movedTo = join(removed, `${label}-${(options.now?.() ?? new Date()).toISOString().replace(/[:.]/g, '-')}`);
201
+ await rename(dir, movedTo);
202
+ await chmod(movedTo, 0o700);
203
+ }
204
+ if (!loaded && !plistStat && !dirStat)
205
+ throw Error('kit_buzz_agent_not_found');
206
+ return { label, service, bootedOut: loaded, plistRemoved: !!plistStat, movedTo };
207
+ }
208
+ export async function buzzCommand(args) {
209
+ const [group, action, ...rest] = args;
210
+ if (!group || group === 'help' || group === '--help' || group === '-h') {
211
+ console.log(buzzUsage);
212
+ return;
213
+ }
214
+ if (group === 'provider' && action === 'install') {
215
+ const flags = flagsOf(rest, ['name', 'bin-dir'], ['force']);
216
+ const result = await installProvider({ ...(typeof flags.name === 'string' ? { name: flags.name } : {}), ...(typeof flags['bin-dir'] === 'string' ? { binDir: flags['bin-dir'] } : {}), force: flags.force === true });
217
+ console.log(`${result.updated ? 'Updated' : 'Installed'} Buzz Desktop provider ${result.name} (kit ${result.version}):\n ${result.path}`);
218
+ if (!result.discoverable)
219
+ console.log(`Warning: ${dirname(result.path)} is not on PATH; Buzz Desktop looks in ~/.local/bin and on PATH.`);
220
+ if (!result.python3)
221
+ console.log('Warning: /usr/bin/python3 is not usable; install the Xcode Command Line Tools (xcode-select --install).');
222
+ console.log(`Next, in Buzz Desktop:
223
+ 1. Restart Buzz Desktop so it finds the new provider.
224
+ 2. Open Agents > New agent.
225
+ 3. Set "Run on" to ${result.name} (Teal Brick · ${result.name}).
226
+ 4. Enter owner (the kit owner directory on the host) and host (the ssh alias or user@host of the agent host).
227
+ 5. Create the agent. Desktop deploys it over ssh; the host must have run \`tealbrick buzz host setup\`.`);
228
+ return;
229
+ }
230
+ if (group === 'host' && action === 'setup') {
231
+ const flags = flagsOf(rest, ['owners-dir', 'buzz-app', 'node', 'kit-cli', 'respond-to-default'], []);
232
+ const str = (name) => typeof flags[name] === 'string' ? flags[name] : undefined;
233
+ const respond = str('respond-to-default');
234
+ if (respond !== undefined && respond !== 'owner-only' && respond !== 'anyone')
235
+ throw Error('kit_buzz_respond_to_default_invalid');
236
+ const result = await setupBuzzHost({ ownersDir: str('owners-dir'), buzzApp: str('buzz-app'), node: str('node'), kitCli: str('kit-cli'), respondToDefault: respond });
237
+ console.log(`Buzz agent host ready (kit ${result.kitVersion}, Buzz ${result.buzzAppVersion}).
238
+ host.json: ${result.hostJson}
239
+ node: ${result.node}
240
+ kit cli: ${result.kitCli}
241
+ owners dir: ${result.ownersDir}${result.owners.length ? ` (enrolled: ${result.owners.join(', ')})` : ''}
242
+ buzz: ${result.buzzBin}
243
+ buzz-acp: ${result.buzzAcpBin}
244
+ fleet respond_to default: ${result.fleetDefaults ?? '(none; Desktop choice applies)'}`);
245
+ for (const warning of result.warnings)
246
+ console.log(`Warning: ${warning}`);
247
+ return;
248
+ }
249
+ if (group === 'host' && action === 'remove') {
250
+ if (rest.length !== 1)
251
+ throw Error('kit_buzz_invalid_arguments');
252
+ const result = await removeBuzzAgent(rest[0]);
253
+ console.log(`Removed Buzz agent ${result.label}:
254
+ launchd unit: ${result.bootedOut ? `booted out (${result.service})` : 'was not loaded'}
255
+ plist: ${result.plistRemoved ? 'deleted' : 'not present'}
256
+ agent dir: ${result.movedTo ? `moved to ${result.movedTo} (still holds the agent key; delete it when no longer needed)` : 'not present'}
257
+ Also delete the agent in Buzz Desktop so it is not deployed again.`);
258
+ return;
259
+ }
260
+ console.error(buzzUsage);
261
+ process.exitCode = 2;
262
+ }
package/dist/cli.js CHANGED
@@ -20,6 +20,11 @@ async function main() {
20
20
  await nativeCommand(process.argv.slice(3), root);
21
21
  return;
22
22
  }
23
+ if (command === 'buzz') {
24
+ const { buzzCommand } = await import('./buzz.js');
25
+ await buzzCommand(process.argv.slice(3));
26
+ return;
27
+ }
23
28
  if (command === 'connect') {
24
29
  await runtimeOnboarding('connect', process.argv.slice(3), root);
25
30
  return;
@@ -41,7 +46,7 @@ async function main() {
41
46
  return;
42
47
  }
43
48
  if (command !== 'setup') {
44
- console.log('Usage: tealbrick setup | status | apply <config.json>\n tealbrick connect --workspace ID --node ID --agent ID [--issuer URL]\n tealbrick app claim --workspace ID --node ID --binding REF --endpoint HTTPS_ORIGIN --company PARTITION [--issuer URL]\nInstalled dependencies expose nothing until activated. Rebuild/restart Eve after changing selection.');
49
+ console.log('Usage: tealbrick setup | status | apply <config.json>\n tealbrick connect --workspace ID --node ID --agent ID [--issuer URL]\n tealbrick app claim --workspace ID --node ID --binding REF --endpoint HTTPS_ORIGIN --company PARTITION [--issuer URL]\n tealbrick buzz provider install | buzz host setup (Buzz Desktop agents on a Mac host; see BUZZ.md)\nInstalled dependencies expose nothing until activated. Rebuild/restart Eve after changing selection.');
45
50
  return;
46
51
  }
47
52
  console.log('Setup requires Portal sign-in and saves an agent card in your chosen workspace. Other capabilities are optional.');
package/dist/index.d.ts CHANGED
@@ -67,7 +67,7 @@ declare const schema: z.ZodObject<{
67
67
  }[] | undefined;
68
68
  spawns?: {
69
69
  worker: string;
70
- profile: "light" | "small" | "standard" | "browser";
70
+ profile: "small" | "light" | "standard" | "browser";
71
71
  mode: "ephemeral" | "durable";
72
72
  harnesses?: string[] | undefined;
73
73
  bundles?: string[] | undefined;
@@ -150,7 +150,7 @@ declare const schema: z.ZodObject<{
150
150
  kind: "user";
151
151
  id: string;
152
152
  };
153
- allow: ("capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "logs" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
153
+ allow: ("logs" | "capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
154
154
  } | {
155
155
  operation: "shareRevoke";
156
156
  worker: string;
@@ -194,7 +194,7 @@ declare const schema: z.ZodObject<{
194
194
  }[] | undefined;
195
195
  spawns?: {
196
196
  worker: string;
197
- profile: "light" | "small" | "standard" | "browser";
197
+ profile: "small" | "light" | "standard" | "browser";
198
198
  mode: "ephemeral" | "durable";
199
199
  harnesses?: string[] | undefined;
200
200
  bundles?: string[] | undefined;
@@ -277,7 +277,7 @@ declare const schema: z.ZodObject<{
277
277
  kind: "user";
278
278
  id: string;
279
279
  };
280
- allow: ("capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "logs" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
280
+ allow: ("logs" | "capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
281
281
  } | {
282
282
  operation: "shareRevoke";
283
283
  worker: string;
@@ -320,7 +320,7 @@ declare const schema: z.ZodObject<{
320
320
  }[] | undefined;
321
321
  spawns?: {
322
322
  worker: string;
323
- profile: "light" | "small" | "standard" | "browser";
323
+ profile: "small" | "light" | "standard" | "browser";
324
324
  mode: "ephemeral" | "durable";
325
325
  harnesses?: string[] | undefined;
326
326
  bundles?: string[] | undefined;
@@ -403,7 +403,7 @@ declare const schema: z.ZodObject<{
403
403
  kind: "user";
404
404
  id: string;
405
405
  };
406
- allow: ("capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "logs" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
406
+ allow: ("logs" | "capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
407
407
  } | {
408
408
  operation: "shareRevoke";
409
409
  worker: string;
@@ -447,7 +447,7 @@ declare const schema: z.ZodObject<{
447
447
  }[] | undefined;
448
448
  spawns?: {
449
449
  worker: string;
450
- profile: "light" | "small" | "standard" | "browser";
450
+ profile: "small" | "light" | "standard" | "browser";
451
451
  mode: "ephemeral" | "durable";
452
452
  harnesses?: string[] | undefined;
453
453
  bundles?: string[] | undefined;
@@ -530,7 +530,7 @@ declare const schema: z.ZodObject<{
530
530
  kind: "user";
531
531
  id: string;
532
532
  };
533
- allow: ("capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "logs" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
533
+ allow: ("logs" | "capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
534
534
  } | {
535
535
  operation: "shareRevoke";
536
536
  worker: string;
@@ -575,7 +575,7 @@ declare const schema: z.ZodObject<{
575
575
  }[] | undefined;
576
576
  spawns?: {
577
577
  worker: string;
578
- profile: "light" | "small" | "standard" | "browser";
578
+ profile: "small" | "light" | "standard" | "browser";
579
579
  mode: "ephemeral" | "durable";
580
580
  harnesses?: string[] | undefined;
581
581
  bundles?: string[] | undefined;
@@ -658,7 +658,7 @@ declare const schema: z.ZodObject<{
658
658
  kind: "user";
659
659
  id: string;
660
660
  };
661
- allow: ("capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "logs" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
661
+ allow: ("logs" | "capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
662
662
  } | {
663
663
  operation: "shareRevoke";
664
664
  worker: string;
@@ -704,7 +704,7 @@ declare const schema: z.ZodObject<{
704
704
  }[] | undefined;
705
705
  spawns?: {
706
706
  worker: string;
707
- profile: "light" | "small" | "standard" | "browser";
707
+ profile: "small" | "light" | "standard" | "browser";
708
708
  mode: "ephemeral" | "durable";
709
709
  harnesses?: string[] | undefined;
710
710
  bundles?: string[] | undefined;
@@ -787,7 +787,7 @@ declare const schema: z.ZodObject<{
787
787
  kind: "user";
788
788
  id: string;
789
789
  };
790
- allow: ("capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "logs" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
790
+ allow: ("logs" | "capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
791
791
  } | {
792
792
  operation: "shareRevoke";
793
793
  worker: string;
@@ -831,7 +831,7 @@ declare const schema: z.ZodObject<{
831
831
  }[] | undefined;
832
832
  spawns?: {
833
833
  worker: string;
834
- profile: "light" | "small" | "standard" | "browser";
834
+ profile: "small" | "light" | "standard" | "browser";
835
835
  mode: "ephemeral" | "durable";
836
836
  harnesses?: string[] | undefined;
837
837
  bundles?: string[] | undefined;
@@ -914,7 +914,7 @@ declare const schema: z.ZodObject<{
914
914
  kind: "user";
915
915
  id: string;
916
916
  };
917
- allow: ("capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "logs" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
917
+ allow: ("logs" | "capabilities" | "auth" | "start" | "stop" | "wake" | "schedule" | "cool" | "status" | "exec" | "ssh" | "events" | "runs" | "assignments" | "leases" | "resident" | "list" | "inventory" | "monitor" | "roles" | "ingress")[];
918
918
  } | {
919
919
  operation: "shareRevoke";
920
920
  worker: string;
@@ -0,0 +1,36 @@
1
+ import { type ClaudeSdk } from './native-claude.js';
2
+ import { type BuzzDownload, type BuzzRun } from './native-buzz.js';
3
+ /**
4
+ * Agent Client Protocol (ACP, JSON-RPC 2.0 over stdio) agent for this kit root, for chat harnesses
5
+ * such as Buzz's `buzz-acp`. It runs the SAME owner harness as `native serve` (profile, expert
6
+ * contracts, Portal grants, PreToolUse gate, traces) in-process; no HTTP, no extra credential.
7
+ * Messages are treated as untrusted. Payload approvals have no surface here: they are declined and
8
+ * the owner is told to continue in TBD, so a chat message can never approve an outward write.
9
+ * With a Buzz CLI, the owner session also gets the in-process `buzz` MCP tools (agent↔agent
10
+ * messaging) and each final answer is auto-posted with request/answer mention rules and a
11
+ * per-thread loop breaker. ACP `session/new` mcpServers are never attached (they could grant a shell).
12
+ */
13
+ export { buzzRoute, buzzTrigger } from './native-buzz.js';
14
+ export interface AcpDependencies {
15
+ claudeSdk?: ClaudeSdk;
16
+ log?: (event: Record<string, unknown>) => void;
17
+ env?: NodeJS.ProcessEnv;
18
+ input?: NodeJS.ReadableStream;
19
+ output?: NodeJS.WritableStream;
20
+ runtimeSync?: false;
21
+ /** Buzz CLI path: enables the buzz tools and posts each turn's final answer (identity comes from buzz-acp's injected BUZZ_* env). */
22
+ buzzCli?: string;
23
+ /** Injectable Buzz CLI runner (tests); behaves like buzzCli. */
24
+ buzz?: BuzzRun;
25
+ /** Injectable media downloader (tests); default `buzz media get` through buzzCli. */
26
+ download?: BuzzDownload;
27
+ /** Relay base URL for attachment URLs (default BUZZ_RELAY_URL). */
28
+ relayUrl?: string;
29
+ /** Overrides the auto-post transport only (legacy hook). */
30
+ send?: (args: string[], text: string) => Promise<boolean>;
31
+ /** Identity overrides (tests). Defaults: owner from BUZZ_AUTH_TAG, self derived from BUZZ_PRIVATE_KEY. */
32
+ ownerPubkey?: string;
33
+ selfPubkey?: string;
34
+ now?: () => number;
35
+ }
36
+ export declare function serveAcp(root: string, deps?: AcpDependencies): Promise<void>;