harnery 0.6.0 → 0.7.1
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 +16 -6
- package/dist/commander.d.ts +19 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +2 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +51 -4
- package/dist/commands/deinit.d.ts.map +1 -1
- package/dist/commands/deinit.js +4 -0
- package/dist/commands/devtools.d.ts +4 -0
- package/dist/commands/devtools.d.ts.map +1 -0
- package/dist/commands/devtools.js +239 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +69 -1
- package/dist/commands/doctor.js +12 -4
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +3 -63
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +54 -14
- package/dist/commands/scratch.js +1 -1
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +273 -62
- package/dist/commands/web-fetch.js +1 -1
- package/dist/core/agents/cli.js +48 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +32 -8
- package/dist/core/agents/events/emit.d.ts.map +1 -1
- package/dist/core/agents/events/emit.js +4 -0
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +16 -5
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +10 -3
- package/dist/core/config.d.ts +10 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +13 -0
- package/dist/core/hooks/cli.js +3 -3
- package/dist/core/hooks/effects/index.d.ts +11 -7
- package/dist/core/hooks/effects/index.d.ts.map +1 -1
- package/dist/core/hooks/effects/index.js +15 -18
- package/dist/core/hooks/events/emit.d.ts.map +1 -1
- package/dist/core/hooks/events/emit.js +4 -0
- package/dist/core/hooks/events/rotate.d.ts +43 -0
- package/dist/core/hooks/events/rotate.d.ts.map +1 -0
- package/dist/core/hooks/events/rotate.js +142 -0
- package/dist/core/hooks/harness/events.d.ts +11 -1
- package/dist/core/hooks/harness/events.d.ts.map +1 -1
- package/dist/core/hooks/harness/events.js +22 -3
- package/dist/core/hooks/harness/wiring.d.ts +8 -0
- package/dist/core/hooks/harness/wiring.d.ts.map +1 -1
- package/dist/core/hooks/harness/wiring.js +34 -5
- package/dist/core/scratch/index.d.ts.map +1 -0
- package/dist/{lib → core}/scratch/index.js +2 -2
- package/dist/lib/devtools.d.ts +178 -0
- package/dist/lib/devtools.d.ts.map +1 -0
- package/dist/lib/devtools.js +1328 -0
- package/dist/lib/docs-frontmatter-migrate.d.ts +33 -0
- package/dist/lib/docs-frontmatter-migrate.d.ts.map +1 -0
- package/dist/lib/docs-frontmatter-migrate.js +364 -0
- package/dist/lib/docs-frontmatter.d.ts +33 -0
- package/dist/lib/docs-frontmatter.d.ts.map +1 -0
- package/dist/lib/docs-frontmatter.js +130 -0
- package/dist/lib/docs-index.d.ts +1 -0
- package/dist/lib/docs-index.d.ts.map +1 -1
- package/dist/lib/docs-index.js +4 -5
- package/dist/lib/docs-lint.d.ts +2 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +18 -12
- package/dist/lib/docs-meta.d.ts +14 -0
- package/dist/lib/docs-meta.d.ts.map +1 -0
- package/dist/lib/docs-meta.js +34 -0
- package/dist/lib/docs-sweep.d.ts +12 -0
- package/dist/lib/docs-sweep.d.ts.map +1 -1
- package/dist/lib/docs-sweep.js +98 -103
- package/dist/lib/format.js +2 -2
- package/dist/lib/http/index.d.ts +1 -0
- package/dist/lib/http/index.d.ts.map +1 -1
- package/dist/lib/http/index.js +1 -0
- package/dist/lib/http/request.d.ts +77 -0
- package/dist/lib/http/request.d.ts.map +1 -0
- package/dist/lib/http/request.js +105 -0
- package/dist/lib/instructions/apply.d.ts +63 -0
- package/dist/lib/instructions/apply.d.ts.map +1 -0
- package/dist/lib/instructions/apply.js +255 -0
- package/dist/lib/instructions/splice.d.ts +73 -0
- package/dist/lib/instructions/splice.d.ts.map +1 -0
- package/dist/lib/instructions/splice.js +118 -0
- package/dist/lib/instructions/templates.d.ts +45 -0
- package/dist/lib/instructions/templates.d.ts.map +1 -0
- package/dist/lib/instructions/templates.js +258 -0
- package/dist/lib/tunnel/gate.d.ts +1 -0
- package/dist/lib/tunnel/gate.d.ts.map +1 -1
- package/dist/lib/tunnel/gate.js +14 -9
- package/dist/lib/tunnel/state.d.ts +11 -1
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +8 -3
- package/package.json +7 -6
- package/src/commander.ts +23 -0
- package/src/commands/agents.ts +50 -3
- package/src/commands/deinit.ts +5 -0
- package/src/commands/devtools.ts +284 -0
- package/src/commands/docs.ts +81 -1
- package/src/commands/doctor.ts +13 -4
- package/src/commands/env.ts +11 -77
- package/src/commands/init.ts +66 -15
- package/src/commands/scratch.ts +1 -1
- package/src/commands/tunnel.ts +316 -65
- package/src/commands/web-fetch.ts +1 -1
- package/src/core/agents/cli.ts +55 -0
- package/src/core/agents/coord-client.ts +34 -7
- package/src/core/agents/events/emit.ts +5 -0
- package/src/core/agents/rules/claim-conflict.ts +17 -6
- package/src/core/agents/state/heartbeat-projector.ts +11 -3
- package/src/core/config.ts +14 -0
- package/src/core/hooks/cli.ts +3 -3
- package/src/core/hooks/effects/index.ts +23 -17
- package/src/core/hooks/events/emit.ts +5 -0
- package/src/core/hooks/events/rotate.ts +151 -0
- package/src/core/hooks/harness/events.ts +30 -3
- package/src/core/hooks/harness/wiring.ts +46 -5
- package/src/{lib → core}/scratch/index.ts +2 -2
- package/src/lib/devtools.ts +1653 -0
- package/src/lib/docs-frontmatter-migrate.ts +427 -0
- package/src/lib/docs-frontmatter.ts +151 -0
- package/src/lib/docs-index.ts +4 -5
- package/src/lib/docs-lint.ts +17 -11
- package/src/lib/docs-meta.ts +44 -0
- package/src/lib/docs-sweep.ts +104 -102
- package/src/lib/format.ts +2 -2
- package/src/lib/http/index.ts +1 -0
- package/src/lib/http/request.ts +154 -0
- package/src/lib/instructions/apply.ts +318 -0
- package/src/lib/instructions/splice.ts +148 -0
- package/src/lib/instructions/templates.ts +295 -0
- package/src/lib/tunnel/gate.ts +14 -9
- package/src/lib/tunnel/state.ts +19 -4
- package/dist/lib/scratch/index.d.ts.map +0 -1
- /package/dist/{lib → core}/scratch/index.d.ts +0 -0
package/dist/commands/tunnel.js
CHANGED
|
@@ -6,17 +6,18 @@ function cachePath(tool, filename) {
|
|
|
6
6
|
import { spawn, spawnSync } from "node:child_process";
|
|
7
7
|
import { existsSync, openSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { resolve } from "node:path";
|
|
9
|
-
import {
|
|
9
|
+
import { clearState, DEFAULT_INSTANCE, ensureCloudflared, gateLogFile, isProcessAlive, listStates, providerLogFile, readConfig, readState, writeConfig, writeState, } from "../lib/tunnel/state.js";
|
|
10
10
|
/**
|
|
11
|
-
* `tunnel`:
|
|
11
|
+
* `tunnel`: provider-backed tunnel in front of a local upstream.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* A Bun reverse-proxy worker (lib/tunnel/gate.ts) rewrites Host for the
|
|
14
|
+
* upstream. Cloudflare quick tunnels add an IP allowlist at the gate via the
|
|
15
|
+
* Cloudflare-set `CF-Connecting-IP` header; Tailscale Serve/Funnel exposes the
|
|
16
|
+
* same gate through tailscaled, with Tailscale owning the access boundary.
|
|
17
17
|
*
|
|
18
18
|
* State + config persisted under `.cache/tunnel/`. cloudflared auto-installs
|
|
19
|
-
* to ~/.local/bin/ on first run (Linux only; macOS users `brew install`)
|
|
19
|
+
* to ~/.local/bin/ on first run (Linux only; macOS users `brew install`);
|
|
20
|
+
* Tailscale requires an installed/authenticated `tailscale` CLI.
|
|
20
21
|
*/
|
|
21
22
|
const DEFAULT_TARGET = "127.0.0.1:8001";
|
|
22
23
|
const DEFAULT_VHOST = "localhost";
|
|
@@ -38,6 +39,54 @@ function resolveName(raw) {
|
|
|
38
39
|
}
|
|
39
40
|
return name;
|
|
40
41
|
}
|
|
42
|
+
function resolveProvider(raw) {
|
|
43
|
+
const provider = (raw ?? "cloudflare").trim().toLowerCase();
|
|
44
|
+
if (provider === "cloudflare" || provider === "cf")
|
|
45
|
+
return "cloudflare";
|
|
46
|
+
if (provider === "tailscale" || provider === "ts")
|
|
47
|
+
return "tailscale";
|
|
48
|
+
emit.error({
|
|
49
|
+
code: "tunnel_bad_provider",
|
|
50
|
+
message: `Invalid provider "${raw}". Use cloudflare or tailscale.`,
|
|
51
|
+
});
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
function resolveTailscaleMode(raw) {
|
|
55
|
+
const visibility = (raw ?? "tailnet").trim().toLowerCase();
|
|
56
|
+
if (visibility === "tailnet" || visibility === "serve")
|
|
57
|
+
return "serve";
|
|
58
|
+
if (visibility === "public" || visibility === "internet" || visibility === "funnel") {
|
|
59
|
+
return "funnel";
|
|
60
|
+
}
|
|
61
|
+
emit.error({
|
|
62
|
+
code: "tunnel_bad_visibility",
|
|
63
|
+
message: `Invalid Tailscale visibility "${raw}". Use tailnet or public.`,
|
|
64
|
+
});
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
function resolveTailscalePath(raw, name) {
|
|
68
|
+
const fallback = name === DEFAULT_INSTANCE ? "/" : `/${name}`;
|
|
69
|
+
const path = (raw ?? fallback).trim();
|
|
70
|
+
if (!path.startsWith("/")) {
|
|
71
|
+
emit.error({
|
|
72
|
+
code: "tunnel_bad_path",
|
|
73
|
+
message: `Tailscale path "${path}" must start with /.`,
|
|
74
|
+
});
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
return path === "" ? "/" : path;
|
|
78
|
+
}
|
|
79
|
+
function resolveHttpsPort(raw) {
|
|
80
|
+
const port = raw === undefined ? 443 : Number(raw);
|
|
81
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
82
|
+
emit.error({
|
|
83
|
+
code: "tunnel_bad_https_port",
|
|
84
|
+
message: `Invalid HTTPS port "${raw}". Use a number from 1 to 65535.`,
|
|
85
|
+
});
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
return port;
|
|
89
|
+
}
|
|
41
90
|
function gateScriptPath() {
|
|
42
91
|
return resolve(import.meta.dirname, "..", "lib", "tunnel", "gate.ts");
|
|
43
92
|
}
|
|
@@ -51,6 +100,9 @@ function gateScriptPath() {
|
|
|
51
100
|
function bunAvailable() {
|
|
52
101
|
return spawnSync("bun", ["--version"], { stdio: "ignore" }).status === 0;
|
|
53
102
|
}
|
|
103
|
+
function tailscaleAvailable() {
|
|
104
|
+
return spawnSync("tailscale", ["version"], { stdio: "ignore" }).status === 0;
|
|
105
|
+
}
|
|
54
106
|
function sleep(ms) {
|
|
55
107
|
return new Promise((r) => setTimeout(r, ms));
|
|
56
108
|
}
|
|
@@ -169,12 +221,98 @@ async function waitForReady(logPath, timeoutMs) {
|
|
|
169
221
|
}
|
|
170
222
|
return { url, registered: false };
|
|
171
223
|
}
|
|
224
|
+
function tailscaleDnsName() {
|
|
225
|
+
const r = spawnSync("tailscale", ["status", "--json"], { encoding: "utf-8" });
|
|
226
|
+
if (r.status !== 0) {
|
|
227
|
+
emit.error({
|
|
228
|
+
code: "tunnel_tailscale_status_failed",
|
|
229
|
+
message: "Failed to read `tailscale status --json`. Install Tailscale and connect this machine to a tailnet first.",
|
|
230
|
+
});
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
const parsed = JSON.parse(r.stdout);
|
|
235
|
+
const dns = parsed.Self?.DNSName?.replace(/\.$/, "");
|
|
236
|
+
if (dns)
|
|
237
|
+
return dns;
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
/* handled below */
|
|
241
|
+
}
|
|
242
|
+
emit.error({
|
|
243
|
+
code: "tunnel_tailscale_no_dns",
|
|
244
|
+
message: "Tailscale status did not report a MagicDNS name. Enable MagicDNS for the tailnet, then retry.",
|
|
245
|
+
});
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
function tailscaleUrl(path, httpsPort) {
|
|
249
|
+
const base = `https://${tailscaleDnsName()}${httpsPort === 443 ? "" : `:${httpsPort}`}`;
|
|
250
|
+
return path === "/" ? `${base}/` : `${base}${path}`;
|
|
251
|
+
}
|
|
252
|
+
function tailscaleCommand(mode) {
|
|
253
|
+
return mode === "funnel" ? "funnel" : "serve";
|
|
254
|
+
}
|
|
255
|
+
function runTailscaleShare(mode, targetUrl, path, httpsPort, logPath) {
|
|
256
|
+
const fd = openSync(logPath, "a");
|
|
257
|
+
const args = [
|
|
258
|
+
tailscaleCommand(mode),
|
|
259
|
+
"--bg",
|
|
260
|
+
"--yes",
|
|
261
|
+
`--https=${httpsPort}`,
|
|
262
|
+
`--set-path=${path}`,
|
|
263
|
+
targetUrl,
|
|
264
|
+
];
|
|
265
|
+
const r = spawnSync("tailscale", args, {
|
|
266
|
+
stdio: ["ignore", fd, fd],
|
|
267
|
+
});
|
|
268
|
+
if (r.status !== 0) {
|
|
269
|
+
emit.error({
|
|
270
|
+
code: "tunnel_tailscale_failed",
|
|
271
|
+
message: `tailscale ${tailscaleCommand(mode)} failed. Check log: ${logPath}`,
|
|
272
|
+
});
|
|
273
|
+
process.exit(r.status ?? 1);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function stopTailscaleShare(state) {
|
|
277
|
+
if (state.provider !== "tailscale" || !state.tailscale_mode)
|
|
278
|
+
return true;
|
|
279
|
+
const cmd = tailscaleCommand(state.tailscale_mode);
|
|
280
|
+
const logPath = cachePath("tunnel", providerLogFile(state.name, "tailscale"));
|
|
281
|
+
const fd = openSync(logPath, "a");
|
|
282
|
+
const args = [
|
|
283
|
+
cmd,
|
|
284
|
+
`--https=${state.tailscale_https_port ?? 443}`,
|
|
285
|
+
`--set-path=${state.tailscale_path ?? "/"}`,
|
|
286
|
+
"off",
|
|
287
|
+
];
|
|
288
|
+
const r = spawnSync("tailscale", args, { stdio: ["ignore", fd, fd] });
|
|
289
|
+
if (r.status !== 0) {
|
|
290
|
+
// The gate is torn down and its port is freed for reuse regardless, so a
|
|
291
|
+
// surviving serve/funnel mapping could later re-expose whatever next binds
|
|
292
|
+
// that port. Surface it loudly so the operator can clear it by hand.
|
|
293
|
+
emit.text(`\n ⚠ Failed to remove the Tailscale ${cmd} mapping (path ${state.tailscale_path ?? "/"}).\n` +
|
|
294
|
+
` It may still be exposing this machine. Clear it with:\n` +
|
|
295
|
+
` tailscale ${cmd} --https=${state.tailscale_https_port ?? 443} --set-path=${state.tailscale_path ?? "/"} off\n` +
|
|
296
|
+
` Log: ${logPath}\n\n`);
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
172
301
|
/** Resolve the context-supplied default vhost (literal or lazy resolver). */
|
|
173
302
|
function contextVhost() {
|
|
174
303
|
const v = context?.tunnelDefaultVhost;
|
|
175
304
|
const resolved = typeof v === "function" ? v() : v;
|
|
176
305
|
return resolved ?? null;
|
|
177
306
|
}
|
|
307
|
+
function providerIsAlive(state) {
|
|
308
|
+
if (state.provider === "tailscale")
|
|
309
|
+
return true;
|
|
310
|
+
const pid = state.cloudflared_pid ?? state.provider_pid;
|
|
311
|
+
return typeof pid === "number" && isProcessAlive(pid);
|
|
312
|
+
}
|
|
313
|
+
function tunnelIsAlive(state) {
|
|
314
|
+
return isProcessAlive(state.gate_pid) && providerIsAlive(state);
|
|
315
|
+
}
|
|
178
316
|
async function up(opts) {
|
|
179
317
|
if (!bunAvailable()) {
|
|
180
318
|
emit.error({
|
|
@@ -185,18 +323,30 @@ async function up(opts) {
|
|
|
185
323
|
process.exit(1);
|
|
186
324
|
}
|
|
187
325
|
const name = resolveName(opts.name);
|
|
326
|
+
const provider = resolveProvider(opts.provider);
|
|
188
327
|
const target = opts.target ?? DEFAULT_TARGET;
|
|
189
328
|
// Precedence: explicit --vhost > the consumer's configured default (via
|
|
190
329
|
// context.tunnelDefaultVhost) > "localhost".
|
|
191
330
|
const vhost = opts.vhost ?? contextVhost() ?? DEFAULT_VHOST;
|
|
331
|
+
const tailscaleMode = provider === "tailscale" ? resolveTailscaleMode(opts.visibility) : undefined;
|
|
332
|
+
const tailscalePath = provider === "tailscale" ? resolveTailscalePath(opts.path, name) : undefined;
|
|
333
|
+
const tailscaleHttpsPort = provider === "tailscale" ? resolveHttpsPort(opts.httpsPort) : undefined;
|
|
192
334
|
const existing = readState(name);
|
|
193
|
-
if (existing &&
|
|
335
|
+
if (existing && tunnelIsAlive(existing)) {
|
|
336
|
+
if (existing.provider !== provider) {
|
|
337
|
+
emit.error({
|
|
338
|
+
code: "tunnel_instance_in_use",
|
|
339
|
+
message: `Tunnel [${name}] is already up with provider ${existing.provider}. Stop it before starting ${provider}.`,
|
|
340
|
+
});
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
194
343
|
emit.text(`Already up [${name}]: ${existing.url}\n`);
|
|
344
|
+
emit.text(` Provider: ${existing.provider}\n`);
|
|
195
345
|
emit.text(` Forwarding: ${existing.target} (Host: ${existing.vhost})\n`);
|
|
196
346
|
return;
|
|
197
347
|
}
|
|
198
348
|
if (existing)
|
|
199
|
-
|
|
349
|
+
downOne(name, new Set());
|
|
200
350
|
// Allocate the gate port (after clearing dead state so its old port frees up
|
|
201
351
|
// for reuse). Explicit --gate-port is validated; otherwise auto-scan.
|
|
202
352
|
const gatePort = allocateGatePort(opts.gatePort ? Number(opts.gatePort) : undefined);
|
|
@@ -205,18 +355,25 @@ async function up(opts) {
|
|
|
205
355
|
if (sweepStrays(gatePort, new Set()))
|
|
206
356
|
await sleep(500);
|
|
207
357
|
const cfg = readConfig();
|
|
208
|
-
if (cfg.allowed_ips.length === 0) {
|
|
358
|
+
if (provider === "cloudflare" && cfg.allowed_ips.length === 0) {
|
|
209
359
|
emit.error({
|
|
210
360
|
code: "tunnel_allowlist_empty",
|
|
211
361
|
message: "Allowlist is empty; refusing to start. Add an IP first: harn tunnel allow add <ip>",
|
|
212
362
|
});
|
|
213
363
|
process.exit(1);
|
|
214
364
|
}
|
|
215
|
-
const cloudflaredBin = ensureCloudflared();
|
|
365
|
+
const cloudflaredBin = provider === "cloudflare" ? ensureCloudflared() : null;
|
|
366
|
+
if (provider === "tailscale" && !tailscaleAvailable()) {
|
|
367
|
+
emit.error({
|
|
368
|
+
code: "tunnel_tailscale_missing",
|
|
369
|
+
message: "tailscale CLI not found or unavailable. Install Tailscale and sign in, then retry.",
|
|
370
|
+
});
|
|
371
|
+
process.exit(1);
|
|
372
|
+
}
|
|
216
373
|
const gateLogPath = cachePath("tunnel", gateLogFile(name));
|
|
217
|
-
const
|
|
374
|
+
const providerLogPath = cachePath("tunnel", providerLogFile(name, provider));
|
|
218
375
|
writeFileSync(gateLogPath, "");
|
|
219
|
-
writeFileSync(
|
|
376
|
+
writeFileSync(providerLogPath, "");
|
|
220
377
|
const gateFd = openSync(gateLogPath, "a");
|
|
221
378
|
// `--name`/`--port` on argv mirror the env vars; they're what makes the gate
|
|
222
379
|
// process distinguishable per-instance in `pgrep -f` (see sweepStrays).
|
|
@@ -226,6 +383,7 @@ async function up(opts) {
|
|
|
226
383
|
env: {
|
|
227
384
|
...process.env,
|
|
228
385
|
HARNERY_TUNNEL_ALLOW: cfg.allowed_ips.join(","),
|
|
386
|
+
HARNERY_TUNNEL_ACCESS: provider === "cloudflare" ? "cloudflare-allowlist" : "trusted-local-proxy",
|
|
229
387
|
HARNERY_TUNNEL_TARGET: target,
|
|
230
388
|
HARNERY_TUNNEL_VHOST: vhost,
|
|
231
389
|
HARNERY_TUNNEL_PORT: String(gatePort),
|
|
@@ -240,54 +398,85 @@ async function up(opts) {
|
|
|
240
398
|
});
|
|
241
399
|
process.exit(1);
|
|
242
400
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
process.kill(gateProc.pid);
|
|
258
|
-
}
|
|
259
|
-
catch {
|
|
260
|
-
/* already dead */
|
|
261
|
-
}
|
|
262
|
-
try {
|
|
263
|
-
process.kill(cfdProc.pid);
|
|
264
|
-
}
|
|
265
|
-
catch {
|
|
266
|
-
/* already dead */
|
|
267
|
-
}
|
|
268
|
-
emit.error({
|
|
269
|
-
code: "tunnel_url_timeout",
|
|
270
|
-
message: `Failed to obtain tunnel URL within 30s. Check log: ${cfdLogPath}`,
|
|
401
|
+
let url;
|
|
402
|
+
let registered = true;
|
|
403
|
+
let cloudflaredPid;
|
|
404
|
+
if (provider === "cloudflare") {
|
|
405
|
+
const cfdFd = openSync(providerLogPath, "a");
|
|
406
|
+
// Force HTTP/2 transport. The default QUIC transport wedges at precheck on
|
|
407
|
+
// constrained hosts (e.g. WSL, where UDP receive buffers can't grow and ICMP
|
|
408
|
+
// is restricted): the URL prints but the edge never registers. HTTP/2 is
|
|
409
|
+
// marginally higher-latency but registers reliably, which is what a dev
|
|
410
|
+
// tunnel needs.
|
|
411
|
+
const cfdProc = spawn(cloudflaredBin, ["tunnel", "--protocol", "http2", "--url", `http://localhost:${gatePort}`], {
|
|
412
|
+
detached: true,
|
|
413
|
+
stdio: ["ignore", cfdFd, cfdFd],
|
|
271
414
|
});
|
|
272
|
-
|
|
415
|
+
cfdProc.unref();
|
|
416
|
+
cloudflaredPid = cfdProc.pid;
|
|
417
|
+
const ready = await waitForReady(providerLogPath, 30_000);
|
|
418
|
+
registered = ready.registered;
|
|
419
|
+
if (!ready.url) {
|
|
420
|
+
try {
|
|
421
|
+
process.kill(gateProc.pid);
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
/* already dead */
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
process.kill(cloudflaredPid);
|
|
428
|
+
}
|
|
429
|
+
catch {
|
|
430
|
+
/* already dead */
|
|
431
|
+
}
|
|
432
|
+
emit.error({
|
|
433
|
+
code: "tunnel_url_timeout",
|
|
434
|
+
message: `Failed to obtain tunnel URL within 30s. Check log: ${providerLogPath}`,
|
|
435
|
+
});
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
url = ready.url;
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
// Resolve the public URL (MagicDNS name) BEFORE starting the share.
|
|
442
|
+
// tailscaleUrl -> tailscaleDnsName() hard-exits when MagicDNS is
|
|
443
|
+
// unavailable; doing it first means we fail cleanly rather than leaving a
|
|
444
|
+
// live serve/funnel exposure with no state file — which `down`/`status`/
|
|
445
|
+
// `heal` would then be unable to see or clean up.
|
|
446
|
+
url = tailscaleUrl(tailscalePath, tailscaleHttpsPort);
|
|
447
|
+
const gateTarget = `http://127.0.0.1:${gatePort}`;
|
|
448
|
+
runTailscaleShare(tailscaleMode, gateTarget, tailscalePath, tailscaleHttpsPort, providerLogPath);
|
|
273
449
|
}
|
|
274
450
|
const state = {
|
|
275
451
|
name,
|
|
452
|
+
provider,
|
|
276
453
|
url,
|
|
277
454
|
gate_pid: gateProc.pid,
|
|
278
|
-
cloudflared_pid:
|
|
455
|
+
cloudflared_pid: cloudflaredPid,
|
|
279
456
|
started_at: new Date().toISOString(),
|
|
280
457
|
target,
|
|
281
458
|
vhost,
|
|
282
459
|
gate_port: gatePort,
|
|
460
|
+
tailscale_mode: tailscaleMode,
|
|
461
|
+
tailscale_path: tailscalePath,
|
|
462
|
+
tailscale_https_port: tailscaleHttpsPort,
|
|
283
463
|
};
|
|
284
464
|
writeState(state);
|
|
285
465
|
const stopHint = name === DEFAULT_INSTANCE ? "harn tunnel down" : `harn tunnel down --name ${name}`;
|
|
286
466
|
emit.text(`\n Instance: ${name}\n`);
|
|
467
|
+
emit.text(` Provider: ${provider}${tailscaleMode ? ` (${tailscaleMode})` : ""}\n`);
|
|
287
468
|
emit.text(` URL: ${url}\n\n`);
|
|
288
469
|
emit.text(` Forwarding: ${target} (Host: ${vhost})\n`);
|
|
289
470
|
emit.text(` Gate port: ${gatePort}\n`);
|
|
290
|
-
|
|
471
|
+
if (provider === "cloudflare") {
|
|
472
|
+
emit.text(` Allowed IPs: ${cfg.allowed_ips.join(", ")}\n\n`);
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
emit.text(` Tailscale path: ${tailscalePath}\n`);
|
|
476
|
+
emit.text(tailscaleMode === "funnel"
|
|
477
|
+
? " Visibility: public internet via Tailscale Funnel\n\n"
|
|
478
|
+
: " Visibility: tailnet only via Tailscale Serve\n\n");
|
|
479
|
+
}
|
|
291
480
|
if (!registered) {
|
|
292
481
|
emit.text(` ⚠ Edge connection didn't register within 30s (QUIC can wedge on a cold\n start). If the URL 404s or times out, bounce it: ${stopHint} && harn tunnel up\n\n`);
|
|
293
482
|
}
|
|
@@ -299,7 +488,11 @@ function downOne(name, killed) {
|
|
|
299
488
|
const before = killed.size;
|
|
300
489
|
const state = readState(name);
|
|
301
490
|
if (state) {
|
|
302
|
-
|
|
491
|
+
if (state.provider === "tailscale")
|
|
492
|
+
stopTailscaleShare(state);
|
|
493
|
+
for (const pid of [state.gate_pid, state.cloudflared_pid, state.provider_pid]) {
|
|
494
|
+
if (typeof pid !== "number")
|
|
495
|
+
continue;
|
|
303
496
|
if (isProcessAlive(pid)) {
|
|
304
497
|
try {
|
|
305
498
|
process.kill(pid);
|
|
@@ -346,7 +539,7 @@ function down(opts) {
|
|
|
346
539
|
: `Stopped ${killed.size} process(es). Tunnel [${name}] down.\n`);
|
|
347
540
|
}
|
|
348
541
|
function instanceState(state) {
|
|
349
|
-
return
|
|
542
|
+
return tunnelIsAlive(state) ? "up" : "stale";
|
|
350
543
|
}
|
|
351
544
|
function fmtUptime(startedAt) {
|
|
352
545
|
const secs = Math.floor((Date.now() - new Date(startedAt).getTime()) / 1000);
|
|
@@ -361,15 +554,24 @@ function fmtUptime(startedAt) {
|
|
|
361
554
|
/** Detailed single-instance block (the pre-multi-instance format). */
|
|
362
555
|
function statusDetail(state) {
|
|
363
556
|
const gateAlive = isProcessAlive(state.gate_pid);
|
|
364
|
-
const
|
|
557
|
+
const providerAlive = providerIsAlive(state);
|
|
558
|
+
const providerPid = state.cloudflared_pid ?? state.provider_pid;
|
|
365
559
|
const cfg = readConfig();
|
|
366
|
-
emit.text(`${gateAlive &&
|
|
560
|
+
emit.text(`${gateAlive && providerAlive ? "up" : "stale"} [${state.name}]\n`);
|
|
561
|
+
emit.text(` Provider: ${state.provider}${state.tailscale_mode ? ` (${state.tailscale_mode})` : ""}\n`);
|
|
367
562
|
emit.text(` URL: ${state.url}\n`);
|
|
368
563
|
emit.text(` Forwarding: ${state.target} (Host: ${state.vhost})\n`);
|
|
369
564
|
emit.text(` Gate port: ${state.gate_port}\n`);
|
|
370
|
-
|
|
565
|
+
if (state.provider === "cloudflare") {
|
|
566
|
+
emit.text(` Allowed IPs: ${cfg.allowed_ips.join(", ")}\n`);
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
emit.text(` TS path: ${state.tailscale_path ?? "/"}\n`);
|
|
570
|
+
}
|
|
371
571
|
emit.text(` Gate PID: ${state.gate_pid}${gateAlive ? "" : " (DEAD)"}\n`);
|
|
372
|
-
|
|
572
|
+
if (state.provider === "cloudflare") {
|
|
573
|
+
emit.text(` CFD PID: ${providerPid ?? "?"}${providerAlive ? "" : " (DEAD)"}\n`);
|
|
574
|
+
}
|
|
373
575
|
emit.text(` Uptime: ${fmtUptime(state.started_at)}\n`);
|
|
374
576
|
}
|
|
375
577
|
function status(opts) {
|
|
@@ -396,6 +598,7 @@ function status(opts) {
|
|
|
396
598
|
}
|
|
397
599
|
const rows = states.map((s) => ({
|
|
398
600
|
name: s.name,
|
|
601
|
+
provider: s.provider,
|
|
399
602
|
state: instanceState(s),
|
|
400
603
|
url: s.url,
|
|
401
604
|
fwd: `${s.target} (${s.vhost})`,
|
|
@@ -404,20 +607,23 @@ function status(opts) {
|
|
|
404
607
|
}));
|
|
405
608
|
const w = {
|
|
406
609
|
name: Math.max(4, ...rows.map((r) => r.name.length)),
|
|
610
|
+
provider: Math.max(8, ...rows.map((r) => r.provider.length)),
|
|
407
611
|
state: 5,
|
|
408
612
|
url: Math.max(3, ...rows.map((r) => r.url.length)),
|
|
409
613
|
fwd: Math.max(10, ...rows.map((r) => r.fwd.length)),
|
|
410
614
|
port: 4,
|
|
411
615
|
};
|
|
412
616
|
const pad = (s, n) => s.padEnd(n);
|
|
413
|
-
emit.text(`${pad("NAME", w.name)} ${pad("STATE", w.state)} ${pad("URL", w.url)} ${pad("FORWARDING", w.fwd)} ${pad("PORT", w.port)} UPTIME\n`);
|
|
617
|
+
emit.text(`${pad("NAME", w.name)} ${pad("PROVIDER", w.provider)} ${pad("STATE", w.state)} ${pad("URL", w.url)} ${pad("FORWARDING", w.fwd)} ${pad("PORT", w.port)} UPTIME\n`);
|
|
414
618
|
for (const r of rows) {
|
|
415
|
-
emit.text(`${pad(r.name, w.name)} ${pad(r.state, w.state)} ${pad(r.url, w.url)} ${pad(r.fwd, w.fwd)} ${pad(r.port, w.port)} ${r.up}\n`);
|
|
619
|
+
emit.text(`${pad(r.name, w.name)} ${pad(r.provider, w.provider)} ${pad(r.state, w.state)} ${pad(r.url, w.url)} ${pad(r.fwd, w.fwd)} ${pad(r.port, w.port)} ${r.up}\n`);
|
|
416
620
|
}
|
|
417
621
|
}
|
|
418
622
|
function logs(opts) {
|
|
419
623
|
const name = resolveName(opts.name);
|
|
420
|
-
const
|
|
624
|
+
const state = readState(name);
|
|
625
|
+
const provider = state?.provider ?? "cloudflare";
|
|
626
|
+
const which = opts.cloudflared || opts.provider ? providerLogFile(name, provider) : gateLogFile(name);
|
|
421
627
|
const path = cachePath("tunnel", which);
|
|
422
628
|
if (!existsSync(path)) {
|
|
423
629
|
emit.error({ code: "tunnel_no_log", message: `No log file at ${path}` });
|
|
@@ -446,9 +652,9 @@ function allowAdd(ip) {
|
|
|
446
652
|
cfg.allowed_ips.push(ip);
|
|
447
653
|
writeConfig(cfg);
|
|
448
654
|
emit.text(`Added ${ip}.\n`);
|
|
449
|
-
const up = listStates();
|
|
655
|
+
const up = listStates().filter((s) => s.provider === "cloudflare");
|
|
450
656
|
if (up.length > 0) {
|
|
451
|
-
emit.text(`
|
|
657
|
+
emit.text(`Cloudflare allowlist is shared across Cloudflare tunnels; restart each to apply (${up.map((s) => s.name).join(", ")}).\n`);
|
|
452
658
|
}
|
|
453
659
|
}
|
|
454
660
|
function allowRm(ip) {
|
|
@@ -461,9 +667,9 @@ function allowRm(ip) {
|
|
|
461
667
|
cfg.allowed_ips.splice(idx, 1);
|
|
462
668
|
writeConfig(cfg);
|
|
463
669
|
emit.text(`Removed ${ip}.\n`);
|
|
464
|
-
const up = listStates();
|
|
670
|
+
const up = listStates().filter((s) => s.provider === "cloudflare");
|
|
465
671
|
if (up.length > 0) {
|
|
466
|
-
emit.text(`
|
|
672
|
+
emit.text(`Cloudflare allowlist is shared across Cloudflare tunnels; restart each to apply (${up.map((s) => s.name).join(", ")}).\n`);
|
|
467
673
|
}
|
|
468
674
|
}
|
|
469
675
|
let emit;
|
|
@@ -473,16 +679,20 @@ export function registerTunnelCommand(program, emitParam, contextParam) {
|
|
|
473
679
|
context = contextParam;
|
|
474
680
|
const cmd = program
|
|
475
681
|
.command("tunnel")
|
|
476
|
-
.description("
|
|
682
|
+
.description("Provider-backed tunnel(s) in front of a local upstream (default upstream: 127.0.0.1:8001). " +
|
|
477
683
|
"Run several at once with --name <instance>.");
|
|
478
684
|
cmd
|
|
479
685
|
.command("up")
|
|
480
|
-
.description("Start a gate +
|
|
686
|
+
.description("Start a gate + provider tunnel (one per --name instance)")
|
|
481
687
|
.option("--name <name>", "instance name; run multiple tunnels side by side", DEFAULT_INSTANCE)
|
|
688
|
+
.option("--provider <provider>", "provider: cloudflare (default) or tailscale", "cloudflare")
|
|
482
689
|
.option("--target <addr>", "upstream to forward to", DEFAULT_TARGET)
|
|
483
690
|
.option("--vhost <host>", "Host header sent to the upstream (default: the consumer's configured " +
|
|
484
691
|
"default, else localhost).")
|
|
485
692
|
.option("--gate-port <port>", "local port the gate binds to (default: auto-allocate the first free port from 9001)")
|
|
693
|
+
.option("--visibility <visibility>", "Tailscale only: tailnet (Serve, default) or public (Funnel)", "tailnet")
|
|
694
|
+
.option("--path <path>", "Tailscale only: URL path mount (default: / for default, /<name> for named instances)")
|
|
695
|
+
.option("--https-port <port>", "Tailscale only: HTTPS listen port (default: 443)", "443")
|
|
486
696
|
.action(up);
|
|
487
697
|
cmd
|
|
488
698
|
.command("down")
|
|
@@ -497,15 +707,16 @@ export function registerTunnelCommand(program, emitParam, contextParam) {
|
|
|
497
707
|
.action(status);
|
|
498
708
|
cmd
|
|
499
709
|
.command("logs")
|
|
500
|
-
.description("Tail the gate log (default) or
|
|
710
|
+
.description("Tail the gate log (default) or provider log for an instance")
|
|
501
711
|
.option("--name <name>", "instance whose log to tail", DEFAULT_INSTANCE)
|
|
502
712
|
.option("-f, --follow", "follow the log")
|
|
503
713
|
.option("--gate", "tail the gate log (default)")
|
|
504
|
-
.option("--
|
|
714
|
+
.option("--provider", "tail the provider log instead")
|
|
715
|
+
.option("--cloudflared", "tail the Cloudflare provider log instead (legacy alias)")
|
|
505
716
|
.action(logs);
|
|
506
717
|
const allow = cmd
|
|
507
718
|
.command("allow")
|
|
508
|
-
.description("Manage the CF-Connecting-IP allowlist")
|
|
719
|
+
.description("Manage the Cloudflare CF-Connecting-IP allowlist")
|
|
509
720
|
.action(allowList);
|
|
510
721
|
allow.command("add <ip>").description("Add an IP to the allowlist").action(allowAdd);
|
|
511
722
|
allow.command("rm <ip>").description("Remove an IP from the allowlist").action(allowRm);
|
|
@@ -14,7 +14,7 @@ import { fileURLToPath } from "node:url";
|
|
|
14
14
|
* harnery repo at the *matching version tag* into a cache dir and install the
|
|
15
15
|
* web app's deps. Subsequent runs reuse the cache.
|
|
16
16
|
*
|
|
17
|
-
* The only thing `web/` pulls from `src/` is `harnery/
|
|
17
|
+
* The only thing `web/` pulls from `src/` is `harnery/core/scratch` (a single,
|
|
18
18
|
* dependency-free module), so the install is `web/` deps only: no root install,
|
|
19
19
|
* no Playwright browser download.
|
|
20
20
|
*/
|
package/dist/core/agents/cli.js
CHANGED
|
@@ -150,6 +150,32 @@ async function handleProject(root, rest) {
|
|
|
150
150
|
}
|
|
151
151
|
return 0;
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Append a canonical `claim.release` event for a path dropped from an owner's
|
|
155
|
+
* files_touched. The path is canonicalized to repo-relative (matching the
|
|
156
|
+
* projector's normalization) so the subtraction matches on replay regardless
|
|
157
|
+
* of the form the caller passed. Soft-fails: a failed emit must never break
|
|
158
|
+
* the release/kill flow — the file mutation already happened.
|
|
159
|
+
*/
|
|
160
|
+
async function emitClaimRelease(root, owner, hb, path, reason) {
|
|
161
|
+
try {
|
|
162
|
+
const { emit } = await import("./events/emit.js");
|
|
163
|
+
const canonical = path.startsWith(`${root}/`) ? path.slice(root.length + 1) : path;
|
|
164
|
+
const platform = hb.platform;
|
|
165
|
+
const harness = platform === "cursor" ? "cursor" : platform === "codex" ? "codex" : "claude-code";
|
|
166
|
+
emit(root, {
|
|
167
|
+
event_type: "claim.release",
|
|
168
|
+
instance_id: owner,
|
|
169
|
+
session_id: hb.session_id ?? owner,
|
|
170
|
+
harness,
|
|
171
|
+
source: "agent-coord",
|
|
172
|
+
data: { path: canonical, reason },
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
/* soft-fail: never break the caller */
|
|
177
|
+
}
|
|
178
|
+
}
|
|
153
179
|
async function handleStateAction(root, action, rest) {
|
|
154
180
|
const writer = await import("./state/heartbeat-writer.js");
|
|
155
181
|
const [owner, ...args] = rest;
|
|
@@ -189,14 +215,36 @@ async function handleStateAction(root, action, rest) {
|
|
|
189
215
|
process.stderr.write("agent-coord release-claim: missing <path>\n");
|
|
190
216
|
return 2;
|
|
191
217
|
}
|
|
218
|
+
const before = writer.readHeartbeat(root, owner);
|
|
192
219
|
const hb = writer.releaseClaim(root, owner, path);
|
|
193
220
|
if (!hb)
|
|
194
221
|
return 1;
|
|
222
|
+
// Durability: the projector rebuilds files_touched by replaying the
|
|
223
|
+
// permanent Edit/Write events, so a file-only release is silently
|
|
224
|
+
// reverted by the next full replay. Emitting claim.release puts the
|
|
225
|
+
// subtraction into the stream so every future replay honors it. Only
|
|
226
|
+
// emit when the release actually removed a held path (idempotent
|
|
227
|
+
// re-releases stay quiet).
|
|
228
|
+
const heldBefore = before?.files_touched?.length ?? 0;
|
|
229
|
+
const heldAfter = hb.files_touched?.length ?? 0;
|
|
230
|
+
if (heldBefore > heldAfter) {
|
|
231
|
+
await emitClaimRelease(root, owner, before ?? hb, path, "explicit");
|
|
232
|
+
}
|
|
195
233
|
process.stdout.write(`${JSON.stringify({ instance_id: owner, files_touched: hb.files_touched })}\n`);
|
|
196
234
|
return 0;
|
|
197
235
|
}
|
|
198
236
|
case "kill-heartbeat": {
|
|
237
|
+
// Read held claims BEFORE the unlink so they can be released durably —
|
|
238
|
+
// killing only the file leaves the claims resurrectable from the
|
|
239
|
+
// permanent Edit/Write events on the next full replay (observed: a
|
|
240
|
+
// 6-day-dead agent's claims returning after its heartbeat was killed).
|
|
241
|
+
const before = writer.readHeartbeat(root, owner);
|
|
199
242
|
const ok = writer.killHeartbeat(root, owner);
|
|
243
|
+
if (ok && before) {
|
|
244
|
+
for (const held of before.files_touched ?? []) {
|
|
245
|
+
await emitClaimRelease(root, owner, before, held, "heal");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
200
248
|
process.stdout.write(`${JSON.stringify({ instance_id: owner, removed: ok })}\n`);
|
|
201
249
|
return ok ? 0 : 1;
|
|
202
250
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coord-client.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oGAAoG;IACpG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0IAA0I;IAC1I,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mEAAmE;IACnE,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CA0C5C;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI1D;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,iBAAiB,GAAG,aAAa,GAAG,kBAAkB,GAAG,MAAM,CAAC;CAC5F,
|
|
1
|
+
{"version":3,"file":"coord-client.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oGAAoG;IACpG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0IAA0I;IAC1I,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mEAAmE;IACnE,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CA0C5C;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI1D;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,iBAAiB,GAAG,aAAa,GAAG,kBAAkB,GAAG,MAAM,CAAC;CAC5F,CAkEA;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA2BpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBpE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAKlE;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAIxC"}
|