@honeybee-ai/waggle-cli 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -188,14 +188,14 @@ var init_hooks = __esm({
188
188
  }
189
189
  });
190
190
 
191
- // node_modules/.pnpm/@agentcoordinationprotocol+sdk@file+..+acp+packages+sdk/node_modules/@agentcoordinationprotocol/sdk/dist/client.js
191
+ // node_modules/.pnpm/@agentcoordinationprotocol+sdk@0.1.0/node_modules/@agentcoordinationprotocol/sdk/dist/client.js
192
192
  import { request as httpRequest } from "node:http";
193
193
  import { request as httpsRequest } from "node:https";
194
194
  function createAcpClient(config) {
195
195
  const base = config.server.replace(/\/$/, "");
196
196
  const ns = (config.namespace ?? "default") === "default" ? "" : `/${config.namespace}`;
197
197
  function fetch(method, path, body) {
198
- return new Promise((resolve, reject) => {
198
+ return new Promise((resolve3, reject) => {
199
199
  const url = new URL(`${base}/api${ns}${path}`);
200
200
  const isHttps = url.protocol === "https:";
201
201
  const reqFn = isHttps ? httpsRequest : httpRequest;
@@ -214,9 +214,9 @@ function createAcpClient(config) {
214
214
  res.on("end", () => {
215
215
  try {
216
216
  const parsed = data ? JSON.parse(data) : {};
217
- resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, data: parsed });
217
+ resolve3({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, data: parsed });
218
218
  } catch {
219
- resolve({ ok: false, status: res.statusCode, data: {} });
219
+ resolve3({ ok: false, status: res.statusCode, data: {} });
220
220
  }
221
221
  });
222
222
  });
@@ -341,14 +341,14 @@ function createAcpClient(config) {
341
341
  }
342
342
  var TIMEOUT;
343
343
  var init_client = __esm({
344
- "node_modules/.pnpm/@agentcoordinationprotocol+sdk@file+..+acp+packages+sdk/node_modules/@agentcoordinationprotocol/sdk/dist/client.js"() {
344
+ "node_modules/.pnpm/@agentcoordinationprotocol+sdk@0.1.0/node_modules/@agentcoordinationprotocol/sdk/dist/client.js"() {
345
345
  TIMEOUT = 3e3;
346
346
  }
347
347
  });
348
348
 
349
- // node_modules/.pnpm/@agentcoordinationprotocol+sdk@file+..+acp+packages+sdk/node_modules/@agentcoordinationprotocol/sdk/dist/index.js
349
+ // node_modules/.pnpm/@agentcoordinationprotocol+sdk@0.1.0/node_modules/@agentcoordinationprotocol/sdk/dist/index.js
350
350
  var init_dist = __esm({
351
- "node_modules/.pnpm/@agentcoordinationprotocol+sdk@file+..+acp+packages+sdk/node_modules/@agentcoordinationprotocol/sdk/dist/index.js"() {
351
+ "node_modules/.pnpm/@agentcoordinationprotocol+sdk@0.1.0/node_modules/@agentcoordinationprotocol/sdk/dist/index.js"() {
352
352
  init_client();
353
353
  }
354
354
  });
@@ -361,9 +361,9 @@ __export(init_exports, {
361
361
  import { createInterface } from "node:readline";
362
362
  function prompt(rl, question, defaultVal) {
363
363
  const suffix = defaultVal ? ` ${c("dim", `[${defaultVal}]`)}` : "";
364
- return new Promise((resolve) => {
364
+ return new Promise((resolve3) => {
365
365
  rl.question(` ${question}${suffix}: `, (answer) => {
366
- resolve(answer.trim() || defaultVal || "");
366
+ resolve3(answer.trim() || defaultVal || "");
367
367
  });
368
368
  });
369
369
  }
@@ -691,8 +691,8 @@ async function tryParseSpec(content) {
691
691
  }
692
692
  async function tryParseYaml(content) {
693
693
  try {
694
- const yaml = await import(JS_YAML);
695
- const loadYaml = yaml.load || yaml.default?.load;
694
+ const yaml3 = await import(JS_YAML);
695
+ const loadYaml = yaml3.load || yaml3.default?.load;
696
696
  if (loadYaml) return loadYaml(content);
697
697
  } catch {
698
698
  }
@@ -1865,6 +1865,745 @@ var init_hooks_cmd = __esm({
1865
1865
  }
1866
1866
  });
1867
1867
 
1868
+ // src/brood.ts
1869
+ import { readFileSync as readFileSync5, existsSync as existsSync5 } from "node:fs";
1870
+ import { join as join5, resolve } from "node:path";
1871
+ import yaml from "js-yaml";
1872
+ function normalizeStartOn(raw) {
1873
+ if (typeof raw === "string") {
1874
+ return {
1875
+ conditions: [{ event: raw, count: 1 }],
1876
+ timeout: DEFAULT_START_ON_TIMEOUT
1877
+ };
1878
+ }
1879
+ const timeout = typeof raw.timeout === "number" ? raw.timeout : DEFAULT_START_ON_TIMEOUT;
1880
+ if (raw.all) {
1881
+ const conditions = raw.all.map((item) => {
1882
+ if (typeof item === "string") return { event: item, count: 1 };
1883
+ return { event: item.event, count: item.count ?? 1 };
1884
+ });
1885
+ return { conditions, timeout };
1886
+ }
1887
+ if (raw.event) {
1888
+ return {
1889
+ conditions: [{ event: raw.event, count: raw.count ?? 1 }],
1890
+ timeout
1891
+ };
1892
+ }
1893
+ throw new Error("startOn must be a string, { event, count? }, or { all: [...] }");
1894
+ }
1895
+ function findBroodFile(fileArg) {
1896
+ if (fileArg) {
1897
+ const p = resolve(fileArg);
1898
+ return existsSync5(p) ? p : null;
1899
+ }
1900
+ for (const name of BROOD_FILES) {
1901
+ const p = join5(process.cwd(), name);
1902
+ if (existsSync5(p)) return p;
1903
+ }
1904
+ return null;
1905
+ }
1906
+ async function parseBroodFile(filePath) {
1907
+ const content = readFileSync5(filePath, "utf8");
1908
+ const raw = filePath.endsWith(".json") ? JSON.parse(content) : parseYaml(content);
1909
+ return validate2(raw);
1910
+ }
1911
+ function parseYaml(content) {
1912
+ return yaml.load(content);
1913
+ }
1914
+ function validate2(raw) {
1915
+ if (!raw || typeof raw !== "object") {
1916
+ throw new Error("brood file must be a YAML/JSON object");
1917
+ }
1918
+ const obj = raw;
1919
+ if (typeof obj.name !== "string" || !obj.name) {
1920
+ throw new Error('brood file requires a "name" field');
1921
+ }
1922
+ if (!obj.hives || typeof obj.hives !== "object") {
1923
+ throw new Error('brood file requires a "hives" map');
1924
+ }
1925
+ const hives = {};
1926
+ const hivesObj = obj.hives;
1927
+ for (const [name, value] of Object.entries(hivesObj)) {
1928
+ if (!value || typeof value !== "object") {
1929
+ throw new Error(`hive "${name}" must be an object`);
1930
+ }
1931
+ const hive = value;
1932
+ const config = {};
1933
+ if (hive.worktree !== void 0) {
1934
+ if (typeof hive.worktree !== "string") throw new Error(`hive "${name}".worktree must be a string`);
1935
+ config.worktree = hive.worktree;
1936
+ }
1937
+ const acpRaw = hive.acp ?? hive.spec ?? hive.protocol;
1938
+ if (acpRaw !== void 0) {
1939
+ if (typeof acpRaw === "string") {
1940
+ config.acp = acpRaw;
1941
+ } else if (typeof acpRaw === "object" && acpRaw !== null && !Array.isArray(acpRaw)) {
1942
+ config.acp = acpRaw;
1943
+ } else {
1944
+ throw new Error(`hive "${name}".acp must be a string (file/URL) or inline spec object`);
1945
+ }
1946
+ }
1947
+ if (hive.port !== void 0) {
1948
+ if (typeof hive.port !== "number") throw new Error(`hive "${name}".port must be a number`);
1949
+ config.port = hive.port;
1950
+ }
1951
+ const agentsRaw = hive.workers ?? hive.agents;
1952
+ if (agentsRaw !== void 0) {
1953
+ if (!Array.isArray(agentsRaw)) throw new Error(`hive "${name}".agents must be an array`);
1954
+ config.agents = agentsRaw.map((a, i) => {
1955
+ if (!a || typeof a !== "object") throw new Error(`hive "${name}".agents[${i}] must be an object`);
1956
+ const agent = a;
1957
+ if (typeof agent.role !== "string") throw new Error(`hive "${name}".agents[${i}].role must be a string`);
1958
+ let tools;
1959
+ if (agent.tools !== void 0) {
1960
+ if (agent.tools === "all") {
1961
+ tools = "all";
1962
+ } else if (Array.isArray(agent.tools) && agent.tools.every((t) => typeof t === "string")) {
1963
+ tools = agent.tools;
1964
+ } else {
1965
+ throw new Error(`hive "${name}".agents[${i}].tools must be 'all' or an array of strings`);
1966
+ }
1967
+ }
1968
+ let coordination;
1969
+ if (agent.coordination !== void 0) {
1970
+ if (typeof agent.coordination === "string" && ["lite", "full", "none"].includes(agent.coordination)) {
1971
+ coordination = agent.coordination;
1972
+ } else if (Array.isArray(agent.coordination) && agent.coordination.every((t) => typeof t === "string")) {
1973
+ coordination = agent.coordination;
1974
+ } else {
1975
+ throw new Error(`hive "${name}".agents[${i}].coordination must be 'lite', 'full', 'none', or an array of strings`);
1976
+ }
1977
+ }
1978
+ let agentType;
1979
+ if (agent.type !== void 0) {
1980
+ if (agent.type !== "worker" && agent.type !== "drone" && agent.type !== "claude") {
1981
+ throw new Error(`hive "${name}".agents[${i}].type must be 'worker', 'drone', or 'claude'`);
1982
+ }
1983
+ agentType = agent.type;
1984
+ }
1985
+ const startOn = agent.startOn !== void 0 ? agent.startOn : void 0;
1986
+ let wakeOn;
1987
+ if (agent.wake_on !== void 0) {
1988
+ if (!agent.wake_on || typeof agent.wake_on !== "object") {
1989
+ throw new Error(`hive "${name}".agents[${i}].wake_on must be an object`);
1990
+ }
1991
+ const wo = agent.wake_on;
1992
+ wakeOn = {
1993
+ types: Array.isArray(wo.types) ? wo.types.filter((t) => typeof t === "string") : void 0,
1994
+ timeout: typeof wo.timeout === "number" ? wo.timeout : void 0,
1995
+ max_wakes: typeof wo.max_wakes === "number" ? wo.max_wakes : void 0
1996
+ };
1997
+ }
1998
+ return {
1999
+ role: agent.role,
2000
+ count: typeof agent.count === "number" ? agent.count : void 0,
2001
+ type: agentType,
2002
+ model_hint: typeof agent.model_hint === "string" ? agent.model_hint : void 0,
2003
+ prompt: typeof agent.prompt === "string" ? agent.prompt : void 0,
2004
+ pluginDir: typeof agent.plugin_dir === "string" ? agent.plugin_dir : void 0,
2005
+ tools,
2006
+ coordination,
2007
+ startOn,
2008
+ wake_on: wakeOn
2009
+ };
2010
+ });
2011
+ }
2012
+ const honeycombRaw = hive.honeycomb ?? hive.comb;
2013
+ if (honeycombRaw !== void 0) {
2014
+ if (!honeycombRaw || typeof honeycombRaw !== "object") {
2015
+ throw new Error(`hive "${name}".honeycomb must be an object`);
2016
+ }
2017
+ const hc = honeycombRaw;
2018
+ config.honeycomb = {
2019
+ publishes: Array.isArray(hc.publishes) ? hc.publishes.filter((x) => typeof x === "string") : void 0,
2020
+ subscribes: Array.isArray(hc.subscribes) ? hc.subscribes.filter((x) => typeof x === "string") : void 0
2021
+ };
2022
+ }
2023
+ const dancesRaw = hive.dances ?? hive.logic;
2024
+ if (dancesRaw !== void 0) {
2025
+ if (typeof dancesRaw !== "string") throw new Error(`hive "${name}".dances must be a string (file path)`);
2026
+ config.dances = dancesRaw;
2027
+ }
2028
+ hives[name] = config;
2029
+ }
2030
+ let provider;
2031
+ if (obj.provider !== void 0) {
2032
+ if (typeof obj.provider !== "string") throw new Error('"provider" must be a string (e.g. "ollama/qwen3:8b")');
2033
+ provider = obj.provider;
2034
+ }
2035
+ let models;
2036
+ if (obj.models !== void 0) {
2037
+ if (!obj.models || typeof obj.models !== "object" || Array.isArray(obj.models)) {
2038
+ throw new Error('"models" must be an object mapping role names to provider strings');
2039
+ }
2040
+ models = {};
2041
+ for (const [role, val] of Object.entries(obj.models)) {
2042
+ if (typeof val !== "string") throw new Error(`models.${role} must be a string`);
2043
+ models[role] = val;
2044
+ }
2045
+ }
2046
+ let env;
2047
+ if (obj.env !== void 0) {
2048
+ if (!obj.env || typeof obj.env !== "object" || Array.isArray(obj.env)) {
2049
+ throw new Error('"env" must be an object mapping variable names to string values');
2050
+ }
2051
+ env = {};
2052
+ for (const [key, val] of Object.entries(obj.env)) {
2053
+ env[key] = String(val);
2054
+ }
2055
+ }
2056
+ let stagger;
2057
+ if (obj.stagger !== void 0) {
2058
+ if (typeof obj.stagger !== "number" || obj.stagger < 0) {
2059
+ throw new Error('"stagger" must be a non-negative number (seconds between agent spawns)');
2060
+ }
2061
+ stagger = obj.stagger;
2062
+ }
2063
+ let dances;
2064
+ const dancesTopRaw = obj.dances ?? obj.logic;
2065
+ if (dancesTopRaw !== void 0) {
2066
+ if (typeof dancesTopRaw !== "string") throw new Error('"dances" must be a string (file path)');
2067
+ dances = dancesTopRaw;
2068
+ }
2069
+ return {
2070
+ name: obj.name,
2071
+ version: typeof obj.version === "string" ? obj.version : void 0,
2072
+ provider,
2073
+ models,
2074
+ env,
2075
+ stagger,
2076
+ dances,
2077
+ hives
2078
+ };
2079
+ }
2080
+ var DEFAULT_START_ON_TIMEOUT, BROOD_FILES;
2081
+ var init_brood = __esm({
2082
+ "src/brood.ts"() {
2083
+ "use strict";
2084
+ DEFAULT_START_ON_TIMEOUT = 0;
2085
+ BROOD_FILES = ["brood.yaml", "brood.yml", "brood.json"];
2086
+ }
2087
+ });
2088
+
2089
+ // src/lib/ipc-broker.ts
2090
+ import { createServer } from "node:net";
2091
+ var IPCBroker;
2092
+ var init_ipc_broker = __esm({
2093
+ "src/lib/ipc-broker.ts"() {
2094
+ "use strict";
2095
+ IPCBroker = class {
2096
+ server = null;
2097
+ hives = /* @__PURE__ */ new Map();
2098
+ socketMap = /* @__PURE__ */ new Map();
2099
+ onHiveConnect;
2100
+ onHiveDisconnect;
2101
+ constructor(opts) {
2102
+ this.onHiveConnect = opts?.onHiveConnect;
2103
+ this.onHiveDisconnect = opts?.onHiveDisconnect;
2104
+ }
2105
+ async listen(socketPath) {
2106
+ return new Promise((resolve3, reject) => {
2107
+ const srv = createServer((socket) => this.onConnection(socket));
2108
+ srv.on("error", reject);
2109
+ srv.listen(socketPath, () => {
2110
+ this.server = srv;
2111
+ resolve3();
2112
+ });
2113
+ });
2114
+ }
2115
+ async close() {
2116
+ for (const conn of this.hives.values()) {
2117
+ conn.socket.end();
2118
+ }
2119
+ this.hives.clear();
2120
+ this.socketMap.clear();
2121
+ if (this.server) {
2122
+ return new Promise((resolve3) => {
2123
+ this.server.close(() => resolve3());
2124
+ });
2125
+ }
2126
+ }
2127
+ getConnectedHives() {
2128
+ return [...this.hives.keys()];
2129
+ }
2130
+ onConnection(socket) {
2131
+ let buffer = "";
2132
+ socket.setEncoding("utf8");
2133
+ socket.on("data", (chunk) => {
2134
+ buffer += chunk;
2135
+ const lines = buffer.split("\n");
2136
+ buffer = lines.pop();
2137
+ for (const line of lines) {
2138
+ if (!line) continue;
2139
+ try {
2140
+ this.handleMessage(socket, JSON.parse(line));
2141
+ } catch {
2142
+ }
2143
+ }
2144
+ });
2145
+ socket.on("close", () => {
2146
+ const conn = this.socketMap.get(socket);
2147
+ if (conn) {
2148
+ this.hives.delete(conn.name);
2149
+ this.socketMap.delete(socket);
2150
+ this.onHiveDisconnect?.(conn.name);
2151
+ }
2152
+ });
2153
+ socket.on("error", () => {
2154
+ });
2155
+ }
2156
+ handleMessage(socket, msg2) {
2157
+ if (msg2.type === "register" && msg2.hive) {
2158
+ const conn = {
2159
+ name: msg2.hive,
2160
+ socket,
2161
+ subscribes: new Set(msg2.subscribes ?? [])
2162
+ };
2163
+ this.hives.set(msg2.hive, conn);
2164
+ this.socketMap.set(socket, conn);
2165
+ this.onHiveConnect?.(msg2.hive);
2166
+ } else if (msg2.type === "event" && msg2.hive && msg2.topic) {
2167
+ this.routeEvent(msg2.hive, msg2.topic, msg2.event);
2168
+ }
2169
+ }
2170
+ routeEvent(sourceHive, topic, event) {
2171
+ for (const [name, conn] of this.hives) {
2172
+ if (name === sourceHive) continue;
2173
+ if (!conn.subscribes.has(topic)) continue;
2174
+ const forward = JSON.stringify({ type: "forward", topic, event }) + "\n";
2175
+ conn.socket.write(forward);
2176
+ }
2177
+ }
2178
+ };
2179
+ }
2180
+ });
2181
+
2182
+ // src/commands/up.ts
2183
+ var up_exports = {};
2184
+ __export(up_exports, {
2185
+ up: () => up
2186
+ });
2187
+ import { spawn } from "node:child_process";
2188
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync4, existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6 } from "node:fs";
2189
+ import { join as join6, dirname as dirname2, resolve as resolve2 } from "node:path";
2190
+ import { tmpdir } from "node:os";
2191
+ import { randomBytes as randomBytes2 } from "node:crypto";
2192
+ import { createRequire } from "node:module";
2193
+ import yaml2 from "js-yaml";
2194
+ function resolvePackage(pkg, cwd) {
2195
+ const bases = [
2196
+ join6(cwd, "__placeholder.js"),
2197
+ import.meta.url
2198
+ ];
2199
+ for (const base of bases) {
2200
+ try {
2201
+ const req = createRequire(base);
2202
+ return req.resolve(pkg);
2203
+ } catch {
2204
+ }
2205
+ }
2206
+ return null;
2207
+ }
2208
+ function stateDir() {
2209
+ return join6(process.cwd(), ".honeybee");
2210
+ }
2211
+ function stateFile() {
2212
+ return join6(stateDir(), "brood.state.json");
2213
+ }
2214
+ async function up(args2) {
2215
+ if (args2[0] === "--help" || args2[0] === "-h") {
2216
+ console.log(`
2217
+ ${c("bold", "wgl up")} \u2014 start a local brood (multi-hive incubator cluster)
2218
+
2219
+ ${c("cyan", "USAGE:")}
2220
+ wgl up [options]
2221
+
2222
+ ${c("cyan", "OPTIONS:")}
2223
+ --file <path> Path to brood.yaml (default: auto-detect in cwd)
2224
+ -f <path> Alias for --file
2225
+ --base-port <N> Starting port for hives (default: 3100)
2226
+ --provider <str> LLM provider (e.g. cerebras/qwen-3-32b)
2227
+ --spec <path> ACP spec file path (no brood.yaml needed)
2228
+ --dances <path> Dance file path
2229
+ --no-acp Benchmark mode: LLM sees all coordination tools directly
2230
+
2231
+ ${c("cyan", "DESCRIPTION:")}
2232
+ Reads brood.yaml, starts an IPC broker, spawns incubator processes
2233
+ per hive, and wires Honeycomb cross-hive event routing between them.
2234
+
2235
+ When hives define agents, incubator spawns Propolis (environment MCP
2236
+ server) and Drone (ReAct agent runtime) child processes within its hive.
2237
+
2238
+ Same brood.yaml works locally (IPC) and managed (Colony).
2239
+
2240
+ ${c("cyan", "EXAMPLES:")}
2241
+ wgl up
2242
+ wgl up --file ./my-project/brood.yaml
2243
+ wgl up --base-port 4000
2244
+ `);
2245
+ return;
2246
+ }
2247
+ if (existsSync6(stateFile())) {
2248
+ console.log(error("A brood is already running"));
2249
+ console.log(c("dim", " Run `wgl down` to stop it first"));
2250
+ process.exit(1);
2251
+ }
2252
+ const fileIdx = args2.indexOf("--file");
2253
+ const fileArg = fileIdx !== -1 ? args2[fileIdx + 1] : void 0;
2254
+ const fIdx = args2.indexOf("-f");
2255
+ const fArg = fIdx !== -1 ? args2[fIdx + 1] : void 0;
2256
+ const basePortIdx = args2.indexOf("--base-port");
2257
+ const basePort = basePortIdx !== -1 ? parseInt(args2[basePortIdx + 1], 10) : 3100;
2258
+ const noAcp = args2.includes("--no-acp");
2259
+ const providerIdx = args2.indexOf("--provider");
2260
+ const providerArg = providerIdx !== -1 ? args2[providerIdx + 1] : void 0;
2261
+ const specIdx = args2.indexOf("--spec");
2262
+ const specArg = specIdx !== -1 ? args2[specIdx + 1] : void 0;
2263
+ const dancesIdx = args2.indexOf("--dances");
2264
+ const dancesArg = dancesIdx !== -1 ? args2[dancesIdx + 1] : void 0;
2265
+ const provider = providerArg ?? process.env.WAGGLE_PROVIDER ?? void 0;
2266
+ const specPath = specArg ?? process.env.WAGGLE_SPEC ?? void 0;
2267
+ const dancesPath = dancesArg ?? process.env.WAGGLE_DANCES ?? void 0;
2268
+ let broodPath = findBroodFile(fArg ?? fileArg);
2269
+ let config;
2270
+ if (broodPath) {
2271
+ try {
2272
+ config = await parseBroodFile(broodPath);
2273
+ } catch (err) {
2274
+ console.log(error(`Invalid brood file: ${err.message}`));
2275
+ process.exit(1);
2276
+ return;
2277
+ }
2278
+ } else if (specPath) {
2279
+ const specName = specPath.replace(/\.(acp|yaml|yml|json)$/i, "").split("/").pop() ?? "default";
2280
+ config = {
2281
+ name: specName,
2282
+ provider: provider ?? "ollama/qwen3:8b",
2283
+ dances: dancesPath,
2284
+ hives: {
2285
+ [specName]: {
2286
+ acp: specPath,
2287
+ dances: dancesPath
2288
+ }
2289
+ }
2290
+ };
2291
+ broodPath = join6(process.cwd(), "brood.yaml");
2292
+ } else {
2293
+ console.log(error("No brood.yaml found and no --spec provided"));
2294
+ console.log(c("dim", " Create a brood.yaml or pass --spec <path>"));
2295
+ process.exit(1);
2296
+ return;
2297
+ }
2298
+ const hiveNames = Object.keys(config.hives);
2299
+ if (hiveNames.length === 0) {
2300
+ console.log(error("brood.yaml has no hives defined"));
2301
+ process.exit(1);
2302
+ return;
2303
+ }
2304
+ const hivesWithAgents = hiveNames.filter((n) => (config.hives[n].agents?.length ?? 0) > 0);
2305
+ const needsHivePackage = hivesWithAgents.some(
2306
+ (n) => config.hives[n].agents?.some((a) => !a.type || a.type === "worker" || a.type === "drone")
2307
+ );
2308
+ const cwd = process.cwd();
2309
+ const incubatorEntry = resolvePackage(INCUBATOR_PKG, cwd);
2310
+ if (!incubatorEntry) {
2311
+ console.log(error("Incubator not installed"));
2312
+ console.log(`
2313
+ Install it with:
2314
+ ${c("cyan", "npm install @honeybee-ai/incubator")}
2315
+ `);
2316
+ process.exit(1);
2317
+ return;
2318
+ }
2319
+ let hiveEntry = null;
2320
+ if (needsHivePackage) {
2321
+ hiveEntry = resolvePackage(HIVE_PKG, cwd);
2322
+ if (!hiveEntry) {
2323
+ console.log(error("Hive not installed (required for worker/drone agents)"));
2324
+ console.log(`
2325
+ Install it with:
2326
+ ${c("cyan", "npm install @honeybee-ai/hive")}
2327
+ `);
2328
+ process.exit(1);
2329
+ return;
2330
+ }
2331
+ }
2332
+ const baseEnv = {
2333
+ ...process.env,
2334
+ ...config.env
2335
+ };
2336
+ console.log(heading(`Starting brood: ${config.name}`));
2337
+ console.log(label("File", broodPath));
2338
+ console.log(label("Hives", hiveNames.join(", ")));
2339
+ if (provider && broodPath) {
2340
+ config.provider = provider;
2341
+ }
2342
+ if (config.provider) console.log(label("Provider", config.provider));
2343
+ if (config.env) console.log(label("Env", Object.keys(config.env).join(", ")));
2344
+ if (hivesWithAgents.length > 0) console.log(label("Agent hives", hivesWithAgents.join(", ")));
2345
+ if (noAcp) console.log(label("Mode", "no-acp (benchmark \u2014 LLM sees coordination tools)"));
2346
+ console.log();
2347
+ const broodId = randomBytes2(4).toString("hex");
2348
+ const socketDir = join6(tmpdir(), `honeybee-brood-${broodId}`);
2349
+ mkdirSync2(socketDir, { recursive: true });
2350
+ const socketPath = join6(socketDir, "broker.sock");
2351
+ const broker = new IPCBroker({
2352
+ onHiveConnect: (name) => console.log(success(`Hive "${name}" connected to broker`)),
2353
+ onHiveDisconnect: (name) => console.log(warn(`Hive "${name}" disconnected`))
2354
+ });
2355
+ await broker.listen(socketPath);
2356
+ console.log(success("IPC broker started"));
2357
+ const children = /* @__PURE__ */ new Map();
2358
+ const hiveState = {};
2359
+ let port = basePort;
2360
+ let propolisPort = basePort + 100;
2361
+ for (const [name, hive] of Object.entries(config.hives)) {
2362
+ const hivePort = hive.port ?? port++;
2363
+ const hiveCwd = hive.worktree ? resolve2(dirname2(broodPath), hive.worktree) : dirname2(broodPath);
2364
+ const protocolPath = resolveProtocol(hive, broodPath, name);
2365
+ const incubatorArgs = ["--http", `--port=${hivePort}`];
2366
+ if (protocolPath) {
2367
+ incubatorArgs.push(`--protocol=${protocolPath}`);
2368
+ }
2369
+ const dancesField = dancesPath ?? hive.dances ?? config.dances;
2370
+ if (dancesField) {
2371
+ const resolvedDances = resolve2(dirname2(broodPath), dancesField);
2372
+ incubatorArgs.push(`--dances=${resolvedDances}`);
2373
+ }
2374
+ const hiveAgents = hive.agents?.length ? hive.agents : deriveAgentsFromSpec(protocolPath);
2375
+ if (hiveAgents.length) {
2376
+ const pPort = propolisPort++;
2377
+ const worktreePath = hive.worktree ? resolve2(dirname2(broodPath), hive.worktree) : resolve2(dirname2(broodPath));
2378
+ const agentsConfig = {
2379
+ provider: config.provider ?? "ollama/qwen3:8b",
2380
+ stagger: config.stagger ?? 2,
2381
+ noAcp,
2382
+ propolisPort: pPort,
2383
+ worktree: worktreePath,
2384
+ hiveEntry: hiveEntry ?? "",
2385
+ ...protocolPath ? { protocolPath } : {},
2386
+ agents: hiveAgents.map((a) => ({
2387
+ role: a.role,
2388
+ count: a.count,
2389
+ type: a.type,
2390
+ tools: a.tools,
2391
+ coordination: a.coordination,
2392
+ modelHint: a.model_hint ?? null,
2393
+ prompt: a.prompt ?? null,
2394
+ startOn: a.startOn ? normalizeStartOn(a.startOn) : null,
2395
+ wakeOn: a.wake_on ? {
2396
+ types: a.wake_on.types ?? null,
2397
+ timeout: a.wake_on.timeout ?? 0,
2398
+ maxWakes: a.wake_on.max_wakes ?? 0
2399
+ } : null
2400
+ })),
2401
+ models: config.models,
2402
+ env: config.env
2403
+ };
2404
+ incubatorArgs.push(`--agents=${JSON.stringify(agentsConfig)}`);
2405
+ incubatorArgs.push("--verbose");
2406
+ const workerCount = hiveAgents.filter((a) => !a.type || a.type === "worker").reduce((sum, a) => sum + (a.count ?? 1), 0);
2407
+ const droneCount = hiveAgents.filter((a) => a.type === "drone").reduce((sum, a) => sum + (a.count ?? 1), 0);
2408
+ const claudeCount = hiveAgents.filter((a) => a.type === "claude").reduce((sum, a) => sum + (a.count ?? 1), 0);
2409
+ const parts = [];
2410
+ if (workerCount > 0) parts.push(`${workerCount} worker${workerCount !== 1 ? "s" : ""}`);
2411
+ if (droneCount > 0) parts.push(`${droneCount} drone${droneCount !== 1 ? "s" : ""}`);
2412
+ if (claudeCount > 0) parts.push(`${claudeCount} claude${claudeCount !== 1 ? "s" : ""}`);
2413
+ console.log(label(`${name}/agents`, `${parts.join(" + ")} (delegated to incubator)`));
2414
+ }
2415
+ const env = {
2416
+ ...baseEnv,
2417
+ HONEYCOMB_BROKER_SOCKET: socketPath,
2418
+ HONEYCOMB_HIVE_NAME: name
2419
+ };
2420
+ if (hive.honeycomb?.publishes?.length) {
2421
+ env["HONEYCOMB_PUBLISHES"] = hive.honeycomb.publishes.join(",");
2422
+ }
2423
+ if (hive.honeycomb?.subscribes?.length) {
2424
+ env["HONEYCOMB_SUBSCRIBES"] = hive.honeycomb.subscribes.join(",");
2425
+ }
2426
+ const incChild = spawn(process.execPath, [incubatorEntry, ...incubatorArgs], {
2427
+ env,
2428
+ cwd: hiveCwd,
2429
+ stdio: ["ignore", "pipe", "pipe"]
2430
+ });
2431
+ incChild.stderr?.on("data", (data) => {
2432
+ const line = data.toString().trim();
2433
+ if (line) console.log(c("dim", `[${name}] ${line}`));
2434
+ });
2435
+ incChild.on("exit", (code) => {
2436
+ children.delete(`incubator:${name}`);
2437
+ if (code !== null && code !== 0) {
2438
+ console.log(warn(`Hive "${name}" incubator exited with code ${code}`));
2439
+ }
2440
+ });
2441
+ if (incChild.pid) {
2442
+ children.set(`incubator:${name}`, incChild);
2443
+ hiveState[name] = { pid: incChild.pid, port: hivePort };
2444
+ console.log(label(name, `incubator port ${hivePort} (pid ${incChild.pid})`));
2445
+ }
2446
+ }
2447
+ mkdirSync2(stateDir(), { recursive: true });
2448
+ const state2 = {
2449
+ name: config.name,
2450
+ socketPath,
2451
+ hives: hiveState,
2452
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
2453
+ };
2454
+ writeFileSync4(stateFile(), JSON.stringify(state2, null, 2));
2455
+ console.log();
2456
+ console.log(success(`Brood "${config.name}" running (${hiveNames.length} hive${hiveNames.length !== 1 ? "s" : ""})`));
2457
+ console.log(c("dim", " Press Ctrl+C or run `wgl down` to stop"));
2458
+ const shutdown = async () => {
2459
+ console.log("\n" + c("dim", "Shutting down..."));
2460
+ for (const [key, child] of children) {
2461
+ child.kill("SIGTERM");
2462
+ console.log(c("dim", ` Stopped ${key}`));
2463
+ }
2464
+ await broker.close();
2465
+ try {
2466
+ unlinkSync2(socketPath);
2467
+ } catch {
2468
+ }
2469
+ try {
2470
+ unlinkSync2(stateFile());
2471
+ } catch {
2472
+ }
2473
+ console.log(success("Brood stopped"));
2474
+ process.exit(0);
2475
+ };
2476
+ process.on("SIGINT", shutdown);
2477
+ process.on("SIGTERM", shutdown);
2478
+ await new Promise(() => {
2479
+ });
2480
+ }
2481
+ function deriveAgentsFromSpec(protocolPath) {
2482
+ if (!protocolPath || protocolPath.startsWith("http://") || protocolPath.startsWith("https://")) return [];
2483
+ try {
2484
+ const raw = readFileSync6(protocolPath, "utf-8");
2485
+ const spec = protocolPath.endsWith(".json") ? JSON.parse(raw) : yaml2.load(raw);
2486
+ const roles = spec.roles;
2487
+ if (!roles) return [];
2488
+ const agents = [];
2489
+ for (const [roleName, roleDef] of Object.entries(roles)) {
2490
+ const count = typeof roleDef.agents === "number" ? roleDef.agents : 1;
2491
+ for (let i = 0; i < count; i++) {
2492
+ agents.push({ role: roleName });
2493
+ }
2494
+ }
2495
+ return agents;
2496
+ } catch {
2497
+ return [];
2498
+ }
2499
+ }
2500
+ function resolveProtocol(hive, broodPath, hiveName) {
2501
+ const acpField = hive.acp ?? hive.protocol;
2502
+ if (!acpField) return void 0;
2503
+ if (typeof acpField === "string") {
2504
+ if (acpField.startsWith("http://") || acpField.startsWith("https://")) {
2505
+ return acpField;
2506
+ }
2507
+ return resolve2(dirname2(broodPath), acpField);
2508
+ }
2509
+ const tempDir = join6(tmpdir(), `honeybee-protocol-${randomBytes2(4).toString("hex")}`);
2510
+ mkdirSync2(tempDir, { recursive: true });
2511
+ const tempPath = join6(tempDir, `${hiveName}.acp.json`);
2512
+ writeFileSync4(tempPath, JSON.stringify(acpField, null, 2));
2513
+ return tempPath;
2514
+ }
2515
+ var INCUBATOR_PKG, HIVE_PKG;
2516
+ var init_up = __esm({
2517
+ "src/commands/up.ts"() {
2518
+ "use strict";
2519
+ init_format();
2520
+ init_brood();
2521
+ init_ipc_broker();
2522
+ INCUBATOR_PKG = "@honeybee-ai/incubator";
2523
+ HIVE_PKG = "@honeybee-ai/hive";
2524
+ }
2525
+ });
2526
+
2527
+ // src/commands/down.ts
2528
+ var down_exports = {};
2529
+ __export(down_exports, {
2530
+ down: () => down
2531
+ });
2532
+ import { readFileSync as readFileSync7, existsSync as existsSync7, unlinkSync as unlinkSync3, rmSync } from "node:fs";
2533
+ import { join as join7 } from "node:path";
2534
+ function stateFile2() {
2535
+ return join7(process.cwd(), ".honeybee", "brood.state.json");
2536
+ }
2537
+ async function down(args2) {
2538
+ if (args2[0] === "--help" || args2[0] === "-h") {
2539
+ console.log(`
2540
+ ${c("bold", "wgl down")} \u2014 stop a running brood
2541
+
2542
+ ${c("cyan", "USAGE:")}
2543
+ wgl down
2544
+
2545
+ ${c("cyan", "DESCRIPTION:")}
2546
+ Reads .honeybee/brood.state.json, sends SIGTERM to all incubator
2547
+ processes (which clean up their own propolis + drone children),
2548
+ removes the broker socket, and deletes the state file.
2549
+
2550
+ ${c("cyan", "EXAMPLES:")}
2551
+ wgl down
2552
+ `);
2553
+ return;
2554
+ }
2555
+ const path = stateFile2();
2556
+ if (!existsSync7(path)) {
2557
+ console.log(error("No running brood found"));
2558
+ console.log(c("dim", " No .honeybee/brood.state.json in current directory"));
2559
+ process.exit(1);
2560
+ return;
2561
+ }
2562
+ let state2;
2563
+ try {
2564
+ state2 = JSON.parse(readFileSync7(path, "utf8"));
2565
+ } catch {
2566
+ console.log(error("Corrupt state file"));
2567
+ process.exit(1);
2568
+ return;
2569
+ }
2570
+ console.log(heading(`Stopping brood: ${state2.name}`));
2571
+ let stopped = 0;
2572
+ for (const [name, info4] of Object.entries(state2.hives)) {
2573
+ try {
2574
+ process.kill(info4.pid, "SIGTERM");
2575
+ console.log(label(name, `incubator stopped (pid ${info4.pid})`));
2576
+ stopped++;
2577
+ } catch {
2578
+ console.log(label(name, c("dim", `not running (pid ${info4.pid})`)));
2579
+ }
2580
+ }
2581
+ if (state2.socketPath) {
2582
+ try {
2583
+ unlinkSync3(state2.socketPath);
2584
+ } catch {
2585
+ }
2586
+ const { dirname: dirname4 } = await import("node:path");
2587
+ try {
2588
+ rmSync(dirname4(state2.socketPath), { recursive: true, force: true });
2589
+ } catch {
2590
+ }
2591
+ }
2592
+ try {
2593
+ unlinkSync3(path);
2594
+ } catch {
2595
+ }
2596
+ const hiveCount = Object.keys(state2.hives).length;
2597
+ console.log();
2598
+ console.log(success(`Brood stopped (${hiveCount} hive${hiveCount !== 1 ? "s" : ""} terminated)`));
2599
+ }
2600
+ var init_down = __esm({
2601
+ "src/commands/down.ts"() {
2602
+ "use strict";
2603
+ init_format();
2604
+ }
2605
+ });
2606
+
1868
2607
  // src/commands/serve.ts
1869
2608
  var serve_exports = {};
1870
2609
  __export(serve_exports, {
@@ -1944,18 +2683,18 @@ var init_serve = __esm({
1944
2683
  });
1945
2684
 
1946
2685
  // src/lib/global-config.ts
1947
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "node:fs";
1948
- import { join as join5 } from "node:path";
2686
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "node:fs";
2687
+ import { join as join8 } from "node:path";
1949
2688
  import { homedir } from "node:os";
1950
2689
  function ensureDir() {
1951
- if (!existsSync5(WGL_DIR)) {
1952
- mkdirSync2(WGL_DIR, { recursive: true });
2690
+ if (!existsSync8(WGL_DIR)) {
2691
+ mkdirSync3(WGL_DIR, { recursive: true });
1953
2692
  }
1954
2693
  }
1955
2694
  function loadGlobalConfig() {
1956
- if (!existsSync5(CONFIG_PATH)) return { ...DEFAULTS };
2695
+ if (!existsSync8(CONFIG_PATH)) return { ...DEFAULTS };
1957
2696
  try {
1958
- const raw = JSON.parse(readFileSync5(CONFIG_PATH, "utf8"));
2697
+ const raw = JSON.parse(readFileSync8(CONFIG_PATH, "utf8"));
1959
2698
  return { ...DEFAULTS, ...raw };
1960
2699
  } catch {
1961
2700
  return { ...DEFAULTS };
@@ -1963,7 +2702,7 @@ function loadGlobalConfig() {
1963
2702
  }
1964
2703
  function saveGlobalConfig(config) {
1965
2704
  ensureDir();
1966
- writeFileSync4(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
2705
+ writeFileSync5(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
1967
2706
  }
1968
2707
  function setGlobalConfigValue(key, value) {
1969
2708
  const config = loadGlobalConfig();
@@ -1974,8 +2713,8 @@ var WGL_DIR, CONFIG_PATH, DEFAULTS;
1974
2713
  var init_global_config = __esm({
1975
2714
  "src/lib/global-config.ts"() {
1976
2715
  "use strict";
1977
- WGL_DIR = join5(homedir(), ".wgl");
1978
- CONFIG_PATH = join5(WGL_DIR, "config.json");
2716
+ WGL_DIR = join8(homedir(), ".wgl");
2717
+ CONFIG_PATH = join8(WGL_DIR, "config.json");
1979
2718
  DEFAULTS = {
1980
2719
  defaultPlatform: "https://hivemind.honeyb.dev",
1981
2720
  defaultRole: "developer",
@@ -1985,11 +2724,11 @@ var init_global_config = __esm({
1985
2724
  }
1986
2725
  });
1987
2726
 
1988
- // node_modules/.pnpm/@honeybee-ai+sdk@file+..+sdk+packages+sdk/node_modules/@honeybee-ai/sdk/dist/http.js
2727
+ // ../sdk/dist/http.js
1989
2728
  import { request as httpRequest2 } from "node:http";
1990
2729
  import { request as httpsRequest2 } from "node:https";
1991
2730
  function httpReq(method, url, body, headers = {}) {
1992
- return new Promise((resolve, reject) => {
2731
+ return new Promise((resolve3, reject) => {
1993
2732
  const parsed = new URL(url);
1994
2733
  const isHttps = parsed.protocol === "https:";
1995
2734
  const reqFn = isHttps ? httpsRequest2 : httpRequest2;
@@ -2006,13 +2745,13 @@ function httpReq(method, url, body, headers = {}) {
2006
2745
  res.on("end", () => {
2007
2746
  try {
2008
2747
  const parsed2 = data ? JSON.parse(data) : {};
2009
- resolve({
2748
+ resolve3({
2010
2749
  ok: res.statusCode >= 200 && res.statusCode < 300,
2011
2750
  status: res.statusCode,
2012
2751
  data: parsed2
2013
2752
  });
2014
2753
  } catch {
2015
- resolve({ ok: false, status: res.statusCode, data: {} });
2754
+ resolve3({ ok: false, status: res.statusCode, data: {} });
2016
2755
  }
2017
2756
  });
2018
2757
  });
@@ -2028,28 +2767,30 @@ function httpReq(method, url, body, headers = {}) {
2028
2767
  }
2029
2768
  var TIMEOUT2;
2030
2769
  var init_http = __esm({
2031
- "node_modules/.pnpm/@honeybee-ai+sdk@file+..+sdk+packages+sdk/node_modules/@honeybee-ai/sdk/dist/http.js"() {
2770
+ "../sdk/dist/http.js"() {
2771
+ "use strict";
2032
2772
  TIMEOUT2 = 1e4;
2033
2773
  }
2034
2774
  });
2035
2775
 
2036
- // node_modules/.pnpm/@honeybee-ai+sdk@file+..+sdk+packages+sdk/node_modules/@honeybee-ai/sdk/dist/auth.js
2037
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "node:fs";
2038
- import { join as join6 } from "node:path";
2776
+ // ../sdk/dist/auth.js
2777
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync9, mkdirSync as mkdirSync4, chmodSync } from "node:fs";
2778
+ import { join as join9 } from "node:path";
2039
2779
  import { homedir as homedir2 } from "node:os";
2040
2780
  function loadAuthStore() {
2041
- if (!existsSync6(AUTH_PATH))
2781
+ if (!existsSync9(AUTH_PATH))
2042
2782
  return { profiles: {} };
2043
2783
  try {
2044
- return JSON.parse(readFileSync6(AUTH_PATH, "utf8"));
2784
+ return JSON.parse(readFileSync9(AUTH_PATH, "utf8"));
2045
2785
  } catch {
2046
2786
  return { profiles: {} };
2047
2787
  }
2048
2788
  }
2049
2789
  function saveAuthStore(store) {
2050
- if (!existsSync6(WGL_DIR2))
2051
- mkdirSync3(WGL_DIR2, { recursive: true });
2052
- writeFileSync5(AUTH_PATH, JSON.stringify(store, null, 2) + "\n", { mode: 384 });
2790
+ if (!existsSync9(WGL_DIR2))
2791
+ mkdirSync4(WGL_DIR2, { recursive: true });
2792
+ writeFileSync6(AUTH_PATH, JSON.stringify(store, null, 2) + "\n", { mode: 384 });
2793
+ chmodSync(AUTH_PATH, 384);
2053
2794
  }
2054
2795
  function getActiveProfile(profileName) {
2055
2796
  const store = loadAuthStore();
@@ -2134,15 +2875,16 @@ async function refreshToken(platform, refreshTokenValue) {
2134
2875
  }
2135
2876
  var WGL_DIR2, AUTH_PATH, GITHUB_CLIENT_ID;
2136
2877
  var init_auth = __esm({
2137
- "node_modules/.pnpm/@honeybee-ai+sdk@file+..+sdk+packages+sdk/node_modules/@honeybee-ai/sdk/dist/auth.js"() {
2878
+ "../sdk/dist/auth.js"() {
2879
+ "use strict";
2138
2880
  init_http();
2139
- WGL_DIR2 = join6(homedir2(), ".wgl");
2140
- AUTH_PATH = join6(WGL_DIR2, "auth.json");
2881
+ WGL_DIR2 = join9(homedir2(), ".wgl");
2882
+ AUTH_PATH = join9(WGL_DIR2, "auth.json");
2141
2883
  GITHUB_CLIENT_ID = "Ov23liy8H1qGZdVAk1Vr";
2142
2884
  }
2143
2885
  });
2144
2886
 
2145
- // node_modules/.pnpm/@honeybee-ai+sdk@file+..+sdk+packages+sdk/node_modules/@honeybee-ai/sdk/dist/platform.js
2887
+ // ../sdk/dist/platform.js
2146
2888
  function createPlatformClient(options) {
2147
2889
  const profileName = options?.profileName || "personal";
2148
2890
  const maybeProfile = getActiveProfile(profileName);
@@ -2154,9 +2896,8 @@ function createPlatformClient(options) {
2154
2896
  const platformUrl = new URL(platform);
2155
2897
  const apiBase = `${platformUrl.protocol}//api.${platformUrl.host}/rest/v1`;
2156
2898
  let accessToken = profile.accessToken;
2157
- async function ensureValidToken() {
2158
- if (!isTokenExpired(profile))
2159
- return;
2899
+ let refreshPromise = null;
2900
+ async function doRefresh() {
2160
2901
  try {
2161
2902
  const tokens = await refreshToken(profile.platform, profile.refreshToken);
2162
2903
  accessToken = tokens.access_token;
@@ -2170,7 +2911,17 @@ function createPlatformClient(options) {
2170
2911
  });
2171
2912
  } catch {
2172
2913
  throw new Error("Session expired. Run `wgl auth login` to re-authenticate.");
2914
+ } finally {
2915
+ refreshPromise = null;
2916
+ }
2917
+ }
2918
+ async function ensureValidToken() {
2919
+ if (!isTokenExpired(profile))
2920
+ return;
2921
+ if (!refreshPromise) {
2922
+ refreshPromise = doRefresh();
2173
2923
  }
2924
+ return refreshPromise;
2174
2925
  }
2175
2926
  function fetch(method, path, body, skipAuth = false) {
2176
2927
  const url = `${apiBase}${path}`;
@@ -2184,21 +2935,11 @@ function createPlatformClient(options) {
2184
2935
  await ensureValidToken();
2185
2936
  const res = await fetch(method, path, body);
2186
2937
  if (res.status === 401) {
2187
- try {
2188
- const tokens = await refreshToken(profile.platform, profile.refreshToken);
2189
- accessToken = tokens.access_token;
2190
- setProfile(profileName, {
2191
- platform: profile.platform,
2192
- email: profile.email,
2193
- org: profile.org,
2194
- accessToken: tokens.access_token,
2195
- refreshToken: tokens.refresh_token,
2196
- expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
2197
- });
2198
- return fetch(method, path, body);
2199
- } catch {
2200
- throw new Error("Session expired. Run `wgl auth login` to re-authenticate.");
2938
+ if (!refreshPromise) {
2939
+ refreshPromise = doRefresh();
2201
2940
  }
2941
+ await refreshPromise;
2942
+ return fetch(method, path, body);
2202
2943
  }
2203
2944
  return res;
2204
2945
  }
@@ -2284,20 +3025,34 @@ function createPlatformClient(options) {
2284
3025
  listCarapaceKeys: () => authedFetch("GET", "/carapace/dashboard/keys"),
2285
3026
  createCarapaceKey: (name, mode) => authedFetch("POST", "/carapace/dashboard/keys", { name, mode }),
2286
3027
  deleteCarapaceKey: (id) => authedFetch("DELETE", `/carapace/dashboard/keys/${encodeURIComponent(id)}`),
3028
+ // --- Telemetry ---
3029
+ telemetryOverview: (days) => authedFetch("GET", `/carapace/api/telemetry/overview${days ? "?days=" + days : ""}`),
3030
+ telemetryTimeline: (days) => authedFetch("GET", `/carapace/api/telemetry/timeline${days ? "?days=" + days : ""}`),
3031
+ telemetryEvents: (opts) => {
3032
+ const params = new URLSearchParams();
3033
+ if (opts?.limit)
3034
+ params.set("limit", String(opts.limit));
3035
+ if (opts?.offset)
3036
+ params.set("offset", String(opts.offset));
3037
+ const qs = params.toString();
3038
+ return authedFetch("GET", `/carapace/api/telemetry/events${qs ? "?" + qs : ""}`);
3039
+ },
2287
3040
  // --- Auth ---
2288
3041
  getMe: () => authedFetch("GET", "/auth/me")
2289
3042
  };
2290
3043
  }
2291
3044
  var init_platform = __esm({
2292
- "node_modules/.pnpm/@honeybee-ai+sdk@file+..+sdk+packages+sdk/node_modules/@honeybee-ai/sdk/dist/platform.js"() {
3045
+ "../sdk/dist/platform.js"() {
3046
+ "use strict";
2293
3047
  init_http();
2294
3048
  init_auth();
2295
3049
  }
2296
3050
  });
2297
3051
 
2298
- // node_modules/.pnpm/@honeybee-ai+sdk@file+..+sdk+packages+sdk/node_modules/@honeybee-ai/sdk/dist/index.js
3052
+ // ../sdk/dist/index.js
2299
3053
  var init_dist2 = __esm({
2300
- "node_modules/.pnpm/@honeybee-ai+sdk@file+..+sdk+packages+sdk/node_modules/@honeybee-ai/sdk/dist/index.js"() {
3054
+ "../sdk/dist/index.js"() {
3055
+ "use strict";
2301
3056
  init_platform();
2302
3057
  init_auth();
2303
3058
  }
@@ -2545,7 +3300,7 @@ var marketplace_exports = {};
2545
3300
  __export(marketplace_exports, {
2546
3301
  marketplace: () => marketplace
2547
3302
  });
2548
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
3303
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
2549
3304
  import { basename as basename2 } from "node:path";
2550
3305
  async function marketplace(args2) {
2551
3306
  const sub = args2[0];
@@ -2720,7 +3475,7 @@ async function install(args2) {
2720
3475
  }
2721
3476
  const safeName = basename2(name).replace(/[/\\]/g, "_");
2722
3477
  const filename = `${safeName}.acp.json`;
2723
- writeFileSync6(filename, JSON.stringify(specContent, null, 2) + "\n");
3478
+ writeFileSync7(filename, JSON.stringify(specContent, null, 2) + "\n");
2724
3479
  console.log(success(`Downloaded: ${filename}`));
2725
3480
  console.log(c("dim", " No local server \u2014 saved to file. Load with: wgl protocol load " + filename));
2726
3481
  } catch (err) {
@@ -2748,7 +3503,7 @@ async function publish(args2) {
2748
3503
  }
2749
3504
  let content;
2750
3505
  try {
2751
- content = readFileSync7(file, "utf8");
3506
+ content = readFileSync10(file, "utf8");
2752
3507
  } catch {
2753
3508
  console.log(error(`Cannot read file: ${file}`));
2754
3509
  process.exit(1);
@@ -2830,30 +3585,30 @@ var integrations_exports = {};
2830
3585
  __export(integrations_exports, {
2831
3586
  integrations: () => integrations
2832
3587
  });
2833
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync4, existsSync as existsSync7 } from "node:fs";
2834
- import { join as join7 } from "node:path";
3588
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync10 } from "node:fs";
3589
+ import { join as join10 } from "node:path";
2835
3590
  import { homedir as homedir3 } from "node:os";
2836
3591
  function configDir() {
2837
- return join7(homedir3(), ".config", "honeybee");
3592
+ return join10(homedir3(), ".config", "honeybee");
2838
3593
  }
2839
3594
  function configPath() {
2840
- return join7(configDir(), "integrations.json");
3595
+ return join10(configDir(), "integrations.json");
2841
3596
  }
2842
3597
  function loadLocalConfig() {
2843
3598
  const path = configPath();
2844
- if (!existsSync7(path)) return {};
3599
+ if (!existsSync10(path)) return {};
2845
3600
  try {
2846
- return JSON.parse(readFileSync8(path, "utf8"));
3601
+ return JSON.parse(readFileSync11(path, "utf8"));
2847
3602
  } catch {
2848
3603
  return {};
2849
3604
  }
2850
3605
  }
2851
3606
  function saveLocalConfig(config) {
2852
3607
  const dir = configDir();
2853
- if (!existsSync7(dir)) {
2854
- mkdirSync4(dir, { recursive: true });
3608
+ if (!existsSync10(dir)) {
3609
+ mkdirSync5(dir, { recursive: true });
2855
3610
  }
2856
- writeFileSync7(configPath(), JSON.stringify(config, null, 2) + "\n");
3611
+ writeFileSync8(configPath(), JSON.stringify(config, null, 2) + "\n", { mode: 384 });
2857
3612
  }
2858
3613
  async function integrations(args2) {
2859
3614
  const sub = args2[0];
@@ -3026,9 +3781,13 @@ async function install2(args2) {
3026
3781
  }
3027
3782
  const manifest = res.data;
3028
3783
  const npmPackage = manifest.npm_package || name;
3029
- const { execSync: execSync2 } = await import("node:child_process");
3784
+ if (!/^(@[a-z0-9-~][a-z0-9._-~]*\/)?[a-z0-9-~][a-z0-9._-~]*(@[^;|&`$\s]+)?$/.test(npmPackage)) {
3785
+ console.log(error(`Invalid npm package name: ${npmPackage}`));
3786
+ process.exit(1);
3787
+ }
3788
+ const { execFileSync } = await import("node:child_process");
3030
3789
  console.log(c("dim", ` Installing ${npmPackage}...`));
3031
- execSync2(`npm install -g ${npmPackage}`, { stdio: "inherit" });
3790
+ execFileSync("npm", ["install", "-g", npmPackage], { stdio: "inherit" });
3032
3791
  const config = loadLocalConfig();
3033
3792
  config[name] = {
3034
3793
  package: npmPackage,
@@ -3125,10 +3884,10 @@ async function publish2(args2) {
3125
3884
  }
3126
3885
  let content;
3127
3886
  try {
3128
- content = readFileSync8(manifestPath, "utf8");
3887
+ content = readFileSync11(manifestPath, "utf8");
3129
3888
  } catch {
3130
3889
  try {
3131
- content = readFileSync8(join7(manifestPath, "manifest.json"), "utf8");
3890
+ content = readFileSync11(join10(manifestPath, "manifest.json"), "utf8");
3132
3891
  } catch {
3133
3892
  console.log(error(`Cannot read manifest: ${manifestPath}`));
3134
3893
  process.exit(1);
@@ -3196,7 +3955,7 @@ var swarm_exports = {};
3196
3955
  __export(swarm_exports, {
3197
3956
  swarm: () => swarm
3198
3957
  });
3199
- import { readFileSync as readFileSync9 } from "node:fs";
3958
+ import { readFileSync as readFileSync12 } from "node:fs";
3200
3959
  async function swarm(args2) {
3201
3960
  const sub = args2[0];
3202
3961
  if (!sub || sub === "--help" || sub === "-h") {
@@ -3280,7 +4039,7 @@ async function create(args2) {
3280
4039
  if (protoIdx !== -1) {
3281
4040
  const protoFile = args2[protoIdx + 1];
3282
4041
  try {
3283
- const content = readFileSync9(protoFile, "utf8");
4042
+ const content = readFileSync12(protoFile, "utf8");
3284
4043
  const parsed = JSON.parse(content);
3285
4044
  opts.protocol_name = parsed.name || protoFile;
3286
4045
  opts.protocol_content = content;
@@ -3587,7 +4346,7 @@ var scan_exports = {};
3587
4346
  __export(scan_exports, {
3588
4347
  scan: () => scan
3589
4348
  });
3590
- import { readFileSync as readFileSync10 } from "node:fs";
4349
+ import { readFileSync as readFileSync13 } from "node:fs";
3591
4350
  async function scan(args2) {
3592
4351
  if (args2[0] === "--help" || args2[0] === "-h") {
3593
4352
  console.log(`
@@ -3635,7 +4394,7 @@ ${c("cyan", "EXAMPLES:")}
3635
4394
  return;
3636
4395
  }
3637
4396
  try {
3638
- text = readFileSync10(filePath, "utf8");
4397
+ text = readFileSync13(filePath, "utf8");
3639
4398
  } catch {
3640
4399
  console.log(error(`Cannot read file: ${filePath}`));
3641
4400
  process.exit(1);
@@ -3735,6 +4494,157 @@ var init_scan = __esm({
3735
4494
  }
3736
4495
  });
3737
4496
 
4497
+ // src/commands/telemetry.ts
4498
+ var telemetry_exports = {};
4499
+ __export(telemetry_exports, {
4500
+ telemetry: () => telemetry
4501
+ });
4502
+ async function telemetry(args2) {
4503
+ const sub = args2[0];
4504
+ if (sub === "--help" || sub === "-h" || !sub) {
4505
+ console.log(`
4506
+ ${c("bold", "wgl telemetry")} \u2014 view telemetry data from Carapace
4507
+
4508
+ ${c("cyan", "USAGE:")}
4509
+ wgl telemetry overview Aggregate counters (scans, threats, latency)
4510
+ wgl telemetry events Recent telemetry events
4511
+ wgl telemetry timeline Daily breakdown by event type
4512
+
4513
+ ${c("cyan", "OPTIONS:")}
4514
+ --days N Time window in days (default: 30, max: 90)
4515
+ --limit N Number of events to show (default: 20, max: 100)
4516
+ --json JSON output (pipe-friendly)
4517
+
4518
+ ${c("cyan", "EXAMPLES:")}
4519
+ wgl telemetry overview
4520
+ wgl telemetry overview --days 7
4521
+ wgl telemetry events --limit 10
4522
+ wgl telemetry timeline --json
4523
+ `);
4524
+ return;
4525
+ }
4526
+ let client;
4527
+ try {
4528
+ client = createPlatformClient();
4529
+ } catch {
4530
+ console.log(error("Not logged in. Run `wgl auth login` first."));
4531
+ process.exit(1);
4532
+ return;
4533
+ }
4534
+ const jsonOutput = args2.includes("--json");
4535
+ const daysIdx = args2.indexOf("--days");
4536
+ const days = daysIdx !== -1 ? parseInt(args2[daysIdx + 1], 10) || 30 : void 0;
4537
+ const limitIdx = args2.indexOf("--limit");
4538
+ const limit = limitIdx !== -1 ? parseInt(args2[limitIdx + 1], 10) || 20 : void 0;
4539
+ switch (sub) {
4540
+ case "overview":
4541
+ await showOverview(client, days, jsonOutput);
4542
+ break;
4543
+ case "events":
4544
+ await showEvents(client, limit, jsonOutput);
4545
+ break;
4546
+ case "timeline":
4547
+ await showTimeline(client, days, jsonOutput);
4548
+ break;
4549
+ default:
4550
+ console.log(error(`Unknown subcommand: ${sub}`));
4551
+ console.log("Run `wgl telemetry --help` for usage");
4552
+ process.exit(2);
4553
+ }
4554
+ }
4555
+ async function showOverview(client, days, json) {
4556
+ const res = await client.telemetryOverview(days);
4557
+ if (!res.ok) {
4558
+ console.log(error(`Failed to fetch overview (${res.status})`));
4559
+ process.exit(1);
4560
+ return;
4561
+ }
4562
+ const data = res.data;
4563
+ if (json) {
4564
+ console.log(JSON.stringify(data, null, 2));
4565
+ return;
4566
+ }
4567
+ console.log(heading(`Telemetry Overview (${data.days} days)`));
4568
+ console.log(table([
4569
+ ["Scans", String(data.totalScans)],
4570
+ ["Threats Blocked", data.threatsBlocked > 0 ? c("red", String(data.threatsBlocked)) : "0"],
4571
+ ["MCP Proxy Requests", String(data.totalMcpRequests)],
4572
+ ["LLM Proxy Requests", String(data.totalProxyRequests)],
4573
+ ["Avg Latency", `${data.avgLatencyMs}ms`]
4574
+ ]));
4575
+ console.log();
4576
+ }
4577
+ async function showEvents(client, limit, json) {
4578
+ const res = await client.telemetryEvents({ limit: limit ?? 20 });
4579
+ if (!res.ok) {
4580
+ console.log(error(`Failed to fetch events (${res.status})`));
4581
+ process.exit(1);
4582
+ return;
4583
+ }
4584
+ const page = res.data;
4585
+ if (json) {
4586
+ console.log(JSON.stringify(page, null, 2));
4587
+ return;
4588
+ }
4589
+ console.log(heading(`Recent Events (${page.events.length} of ${page.total})`));
4590
+ if (page.events.length === 0) {
4591
+ console.log(` ${c("dim", "No telemetry events yet.")}`);
4592
+ console.log();
4593
+ return;
4594
+ }
4595
+ for (const event of page.events) {
4596
+ const typeColor = event.eventType === "threat_detected" ? "red" : event.eventType.startsWith("agent_") ? "cyan" : event.eventType.startsWith("llm_") ? "yellow" : "dim";
4597
+ const ts = new Date(event.createdAt).toLocaleString();
4598
+ console.log(` ${c("dim", ts)} ${c(typeColor, event.eventType.padEnd(18))}`);
4599
+ if (event.metadata) {
4600
+ const meta = Object.entries(event.metadata).slice(0, 4).map(([k, v]) => `${k}=${typeof v === "object" ? JSON.stringify(v) : v}`).join(" ");
4601
+ if (meta) console.log(` ${c("dim", meta)}`);
4602
+ }
4603
+ }
4604
+ console.log();
4605
+ }
4606
+ async function showTimeline(client, days, json) {
4607
+ const res = await client.telemetryTimeline(days);
4608
+ if (!res.ok) {
4609
+ console.log(error(`Failed to fetch timeline (${res.status})`));
4610
+ process.exit(1);
4611
+ return;
4612
+ }
4613
+ const data = res.data;
4614
+ if (json) {
4615
+ console.log(JSON.stringify(data, null, 2));
4616
+ return;
4617
+ }
4618
+ console.log(heading(`Telemetry Timeline (${data.days} days)`));
4619
+ if (data.timeline.length === 0) {
4620
+ console.log(` ${c("dim", "No data for this period.")}`);
4621
+ console.log();
4622
+ return;
4623
+ }
4624
+ const byDate = /* @__PURE__ */ new Map();
4625
+ for (const entry of data.timeline) {
4626
+ const key = entry.date;
4627
+ if (!byDate.has(key)) byDate.set(key, []);
4628
+ byDate.get(key).push({ type: entry.event_type, count: entry.count });
4629
+ }
4630
+ for (const [date, entries] of byDate) {
4631
+ const total = entries.reduce((s, e) => s + e.count, 0);
4632
+ console.log(` ${c("bold", date)} ${c("dim", `(${total} events)`)}`);
4633
+ for (const e of entries) {
4634
+ const bar = "|".repeat(Math.min(e.count, 40));
4635
+ console.log(` ${e.type.padEnd(18)} ${c("cyan", bar)} ${e.count}`);
4636
+ }
4637
+ }
4638
+ console.log();
4639
+ }
4640
+ var init_telemetry = __esm({
4641
+ "src/commands/telemetry.ts"() {
4642
+ "use strict";
4643
+ init_dist2();
4644
+ init_format();
4645
+ }
4646
+ });
4647
+
3738
4648
  // src/commands/completion.ts
3739
4649
  var completion_exports = {};
3740
4650
  __export(completion_exports, {
@@ -3931,28 +4841,28 @@ var upgrade_exports = {};
3931
4841
  __export(upgrade_exports, {
3932
4842
  upgrade: () => upgrade
3933
4843
  });
3934
- import { readFileSync as readFileSync11 } from "node:fs";
3935
- import { join as join8, dirname as dirname2 } from "node:path";
4844
+ import { readFileSync as readFileSync14 } from "node:fs";
4845
+ import { join as join11, dirname as dirname3 } from "node:path";
3936
4846
  import { request as httpsRequest3 } from "node:https";
3937
4847
  import { execSync } from "node:child_process";
3938
4848
  function getCurrentVersion() {
3939
- if (true) return "1.0.0";
4849
+ if (true) return "1.0.2";
3940
4850
  try {
3941
- const pkgPath = join8(dirname2(new URL(import.meta.url).pathname), "..", "package.json");
3942
- return JSON.parse(readFileSync11(pkgPath, "utf8")).version;
4851
+ const pkgPath = join11(dirname3(new URL(import.meta.url).pathname), "..", "package.json");
4852
+ return JSON.parse(readFileSync14(pkgPath, "utf8")).version;
3943
4853
  } catch {
3944
4854
  return "0.0.0-dev";
3945
4855
  }
3946
4856
  }
3947
4857
  function fetchLatestVersion() {
3948
- return new Promise((resolve, reject) => {
4858
+ return new Promise((resolve3, reject) => {
3949
4859
  const req = httpsRequest3(REGISTRY_URL, { timeout: 1e4 }, (res) => {
3950
4860
  let data = "";
3951
4861
  res.on("data", (chunk) => data += chunk);
3952
4862
  res.on("end", () => {
3953
4863
  try {
3954
4864
  const parsed = JSON.parse(data);
3955
- resolve(parsed.version);
4865
+ resolve3(parsed.version);
3956
4866
  } catch {
3957
4867
  reject(new Error("Failed to parse registry response"));
3958
4868
  }
@@ -4082,6 +4992,10 @@ ${c("cyan", "CONTROL:")}
4082
4992
  pause [reason] Pause coordination
4083
4993
  resume Resume coordination
4084
4994
 
4995
+ ${c("cyan", "BROOD:")}
4996
+ up Start local brood (multi-hive cluster)
4997
+ down Stop a running brood
4998
+
4085
4999
  ${c("cyan", "SERVER & HOOKS:")}
4086
5000
  serve Start local incubator server
4087
5001
  hooks install Install Claude Code hooks
@@ -4122,6 +5036,11 @@ ${c("cyan", "CLOUD:")}
4122
5036
  swarm keys Manage API keys
4123
5037
  swarm logs Tail swarm events
4124
5038
 
5039
+ ${c("cyan", "TELEMETRY:")}
5040
+ telemetry overview Aggregate counters (scans, threats, latency)
5041
+ telemetry events Recent telemetry events
5042
+ telemetry timeline Daily breakdown by event type
5043
+
4125
5044
  ${c("cyan", "TOOLS:")}
4126
5045
  scan <text> Scan text for prompt injection
4127
5046
  completion <shell> Generate shell completion script
@@ -4153,12 +5072,12 @@ async function main() {
4153
5072
  }
4154
5073
  if (command === "--version" || command === "-v") {
4155
5074
  if (true) {
4156
- console.log("1.0.0");
5075
+ console.log("1.0.2");
4157
5076
  } else {
4158
- const { readFileSync: readFileSync12 } = await null;
4159
- const { join: join9, dirname: dirname3 } = await null;
4160
- const pkgPath = join9(dirname3(new URL(import.meta.url).pathname), "..", "package.json");
4161
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
5077
+ const { readFileSync: readFileSync15 } = await null;
5078
+ const { join: join12, dirname: dirname4 } = await null;
5079
+ const pkgPath = join12(dirname4(new URL(import.meta.url).pathname), "..", "package.json");
5080
+ const pkg = JSON.parse(readFileSync15(pkgPath, "utf8"));
4162
5081
  console.log(pkg.version);
4163
5082
  }
4164
5083
  return;
@@ -4170,8 +5089,8 @@ async function main() {
4170
5089
  break;
4171
5090
  }
4172
5091
  case "join": {
4173
- const { join: join9 } = await Promise.resolve().then(() => (init_join(), join_exports));
4174
- await join9(commandArgs);
5092
+ const { join: join12 } = await Promise.resolve().then(() => (init_join(), join_exports));
5093
+ await join12(commandArgs);
4175
5094
  break;
4176
5095
  }
4177
5096
  case "status": {
@@ -4249,6 +5168,16 @@ async function main() {
4249
5168
  await hooksCmd2(commandArgs);
4250
5169
  break;
4251
5170
  }
5171
+ case "up": {
5172
+ const { up: up2 } = await Promise.resolve().then(() => (init_up(), up_exports));
5173
+ await up2(commandArgs);
5174
+ break;
5175
+ }
5176
+ case "down": {
5177
+ const { down: down2 } = await Promise.resolve().then(() => (init_down(), down_exports));
5178
+ await down2(commandArgs);
5179
+ break;
5180
+ }
4252
5181
  case "serve": {
4253
5182
  const { serve: serve2 } = await Promise.resolve().then(() => (init_serve(), serve_exports));
4254
5183
  await serve2(commandArgs);
@@ -4284,6 +5213,11 @@ async function main() {
4284
5213
  await scan2(commandArgs);
4285
5214
  break;
4286
5215
  }
5216
+ case "telemetry": {
5217
+ const { telemetry: telemetry2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
5218
+ await telemetry2(commandArgs);
5219
+ break;
5220
+ }
4287
5221
  case "completion": {
4288
5222
  const { completion: completion2 } = await Promise.resolve().then(() => (init_completion(), completion_exports));
4289
5223
  await completion2(commandArgs);