alink-cli 0.8.6 → 0.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +211 -13
- package/dist/bin.mjs.map +1 -1
- package/dist/daemon.js +33 -0
- package/dist/ui.js +11 -6
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -51996,12 +51996,20 @@ const makeWsRpcLayer = (currentSession) => WsRpcGroup.toLayer(gen(function* () {
|
|
|
51996
51996
|
return startup.enqueueCommand(dispatchEffect).pipe(mapError((cause) => toDispatchCommandError(cause, "Failed to dispatch orchestration command")));
|
|
51997
51997
|
};
|
|
51998
51998
|
const loadServerConfig = gen(function* () {
|
|
51999
|
+
console.error("[DEBUG-provider-checking-20260820] getConfig start");
|
|
51999
52000
|
const keybindingsConfig = yield* keybindings.loadConfigState;
|
|
52001
|
+
console.error("[DEBUG-provider-checking-20260820] getConfig keybindings");
|
|
52000
52002
|
const providers = yield* providerRegistry.getProviders;
|
|
52003
|
+
console.error("[DEBUG-provider-checking-20260820] getConfig providers");
|
|
52001
52004
|
const settings = redactServerSettingsForClient(yield* serverSettings.getSettings);
|
|
52005
|
+
console.error("[DEBUG-provider-checking-20260820] getConfig settings");
|
|
52006
|
+
const environment = yield* serverEnvironment.getDescriptor;
|
|
52007
|
+
console.error("[DEBUG-provider-checking-20260820] getConfig environment");
|
|
52008
|
+
const auth = yield* serverAuth.getDescriptor();
|
|
52009
|
+
console.error("[DEBUG-provider-checking-20260820] getConfig auth");
|
|
52002
52010
|
return {
|
|
52003
|
-
environment
|
|
52004
|
-
auth
|
|
52011
|
+
environment,
|
|
52012
|
+
auth,
|
|
52005
52013
|
cwd: config.cwd,
|
|
52006
52014
|
keybindingsConfigPath: config.keybindingsConfigPath,
|
|
52007
52015
|
keybindings: keybindingsConfig.keybindings,
|
|
@@ -52350,6 +52358,37 @@ function isEnvelope(value) {
|
|
|
52350
52358
|
//#endregion
|
|
52351
52359
|
//#region src/tunnel/bridge.ts
|
|
52352
52360
|
const TUNNEL_SCOPES = [AuthOrchestrationReadScope, AuthOrchestrationOperateScope];
|
|
52361
|
+
function machineIdFromToken(token) {
|
|
52362
|
+
const parts = token.split(".");
|
|
52363
|
+
if (parts.length !== 3 || parts[0] !== "al1" || !parts[1]) return null;
|
|
52364
|
+
try {
|
|
52365
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
52366
|
+
return typeof payload.mid === "string" && payload.mid.length > 0 ? payload.mid : null;
|
|
52367
|
+
} catch {
|
|
52368
|
+
return null;
|
|
52369
|
+
}
|
|
52370
|
+
}
|
|
52371
|
+
function hubHttpUrl(hub) {
|
|
52372
|
+
const url = new URL(hub);
|
|
52373
|
+
url.protocol = url.protocol === "wss:" ? "https:" : "http:";
|
|
52374
|
+
url.pathname = "";
|
|
52375
|
+
url.search = "";
|
|
52376
|
+
url.hash = "";
|
|
52377
|
+
return url.toString().replace(/\/$/, "");
|
|
52378
|
+
}
|
|
52379
|
+
const uploadHubEnckey = (config) => gen(function* () {
|
|
52380
|
+
const machineId = machineIdFromToken(config.machineToken);
|
|
52381
|
+
if (!machineId) return yield* fail(/* @__PURE__ */ new Error("machine id is missing from the machine token"));
|
|
52382
|
+
const httpClient = yield* HttpClient;
|
|
52383
|
+
const request = yield* post$1(`${hubHttpUrl(config.hubUrl)}/api/machines/${encodeURIComponent(machineId)}/enckey`).pipe(setHeaders({
|
|
52384
|
+
authorization: `Bearer ${config.machineToken}`,
|
|
52385
|
+
"content-type": "application/json",
|
|
52386
|
+
"x-requested-with": "agentlink"
|
|
52387
|
+
}), bodyJson({ enckey: config.enckey }));
|
|
52388
|
+
yield* filterStatusOk(yield* httpClient.execute(request));
|
|
52389
|
+
return machineId;
|
|
52390
|
+
});
|
|
52391
|
+
const hubFrameDecoder = new TextDecoder();
|
|
52353
52392
|
const ResponseDefectEncoded = (cause) => ({
|
|
52354
52393
|
_tag: "Defect",
|
|
52355
52394
|
defect: cause instanceof Error ? {
|
|
@@ -52386,17 +52425,30 @@ const makeTunnelSocketProtocol = gen(function* () {
|
|
|
52386
52425
|
};
|
|
52387
52426
|
clients.set(id, { write });
|
|
52388
52427
|
clientIds.add(id);
|
|
52428
|
+
console.error("[DEBUG-provider-checking-20260820] rpc socket ready", { clientId: id });
|
|
52389
52429
|
yield* socket.runRaw((data) => {
|
|
52390
52430
|
try {
|
|
52431
|
+
console.error("[DEBUG-provider-checking-20260820] rpc input", { byteLength: typeof data === "string" ? data.length : data.byteLength });
|
|
52391
52432
|
const decoded = parser.decode(data);
|
|
52433
|
+
console.error("[DEBUG-provider-checking-20260820] rpc decoded", {
|
|
52434
|
+
count: decoded.length,
|
|
52435
|
+
messages: decoded.map((item) => typeof item === "object" && item !== null ? {
|
|
52436
|
+
tag: "tag" in item ? String(item.tag) : void 0,
|
|
52437
|
+
id: "id" in item ? String(item.id) : void 0
|
|
52438
|
+
} : { type: typeof item })
|
|
52439
|
+
});
|
|
52392
52440
|
if (decoded.length === 0) return void_$1;
|
|
52393
52441
|
let i = 0;
|
|
52394
52442
|
return whileLoop({
|
|
52395
52443
|
while: () => i < decoded.length,
|
|
52396
|
-
body: () =>
|
|
52444
|
+
body: () => {
|
|
52445
|
+
console.error("[DEBUG-provider-checking-20260820] rpc dispatch", { clientId: id });
|
|
52446
|
+
return writeRequest(id, decoded[i++]);
|
|
52447
|
+
},
|
|
52397
52448
|
step: () => void 0
|
|
52398
52449
|
});
|
|
52399
52450
|
} catch (cause) {
|
|
52451
|
+
console.error("[DEBUG-provider-checking-20260820] rpc decode error", { message: cause instanceof Error ? cause.message : String(cause) });
|
|
52400
52452
|
if (isTagged(cause, "MaxBufferSizeExceeded")) return orDie(writeRaw(new CloseEvent(1009, String(cause))));
|
|
52401
52453
|
const defect = parser.encode(ResponseDefectEncoded(cause));
|
|
52402
52454
|
return defect === void 0 ? void_$1 : orDie(writeRaw(defect));
|
|
@@ -52433,6 +52485,12 @@ const splitHubFrame = (text) => {
|
|
|
52433
52485
|
rest: text.slice(spaceAt + 1)
|
|
52434
52486
|
};
|
|
52435
52487
|
};
|
|
52488
|
+
function isHttpTunnelRequestFrame(value) {
|
|
52489
|
+
return typeof value === "object" && value !== null && value._tag === "HttpRequest" && typeof value.id === "string" && typeof value.method === "string" && typeof value.path === "string" && Array.isArray(value.headers) && (value.body === null || typeof value.body === "string");
|
|
52490
|
+
}
|
|
52491
|
+
function encodeHttpTunnelResponseFrame(frame) {
|
|
52492
|
+
return JSON.stringify(frame);
|
|
52493
|
+
}
|
|
52436
52494
|
const makeTunnelSocket = (input) => {
|
|
52437
52495
|
const runRaw = (handler, options) => scopedWith((scope) => gen(function* () {
|
|
52438
52496
|
const fiberSet = yield* make$62().pipe(provide$2(scope));
|
|
@@ -52449,18 +52507,33 @@ const makeTunnelSocket = (input) => {
|
|
|
52449
52507
|
return make$63({
|
|
52450
52508
|
runRaw,
|
|
52451
52509
|
writer: sync(() => (chunk) => sync(() => {
|
|
52452
|
-
if (typeof chunk === "string")
|
|
52510
|
+
if (typeof chunk === "string") {
|
|
52511
|
+
console.error("[DEBUG-provider-checking-20260820] rpc outbound", { byteLength: chunk.length });
|
|
52512
|
+
input.sendEncrypted(JSON.stringify(utf8ToEnvelope(input.enckey, chunk)));
|
|
52513
|
+
}
|
|
52453
52514
|
}))
|
|
52454
52515
|
});
|
|
52455
52516
|
};
|
|
52456
52517
|
const runHubTunnel = (config) => gen(function* () {
|
|
52457
52518
|
const serverAuth = yield* EnvironmentAuth;
|
|
52458
52519
|
const sessionsStore = yield* SessionStore;
|
|
52520
|
+
const serverConfig = yield* ServerConfig;
|
|
52521
|
+
const httpClient = yield* HttpClient;
|
|
52459
52522
|
yield* retry(gen(function* () {
|
|
52460
52523
|
yield* logInfo("hub tunnel: connection effect entered");
|
|
52461
52524
|
const url = `${config.hubUrl.replace(/\/+$/, "")}/daemon-tunnel?token=${encodeURIComponent(config.machineToken)}&v=1`;
|
|
52462
52525
|
yield* logInfo("hub tunnel: connecting", { hubUrl: config.hubUrl });
|
|
52463
52526
|
const socket = yield* makeWebSocket(url).pipe(provide$1(layerWebSocketConstructor));
|
|
52527
|
+
const upload = gen(function* () {
|
|
52528
|
+
const machineId = machineIdFromToken(config.machineToken);
|
|
52529
|
+
if (!machineId) {
|
|
52530
|
+
yield* logWarning$1("[daemon] enckey upload skipped: machine id missing from token");
|
|
52531
|
+
return;
|
|
52532
|
+
}
|
|
52533
|
+
yield* logInfo(`[daemon] uploading enckey for machine ${machineId}...`);
|
|
52534
|
+
yield* uploadHubEnckey(config);
|
|
52535
|
+
yield* logInfo("[daemon] enckey uploaded to hub for cross-device recovery");
|
|
52536
|
+
}).pipe(provideService(HttpClient, httpClient), catch_((error) => logError(`[daemon] enckey upload failed: ${String(error)}`)), asVoid);
|
|
52464
52537
|
yield* addFinalizer((exit) => logInfo("hub tunnel: connection scope closing", { exit: String(exit) }));
|
|
52465
52538
|
const tunnels = /* @__PURE__ */ new Map();
|
|
52466
52539
|
const dropTunnel = (tunnelId) => {
|
|
@@ -52478,7 +52551,7 @@ const runHubTunnel = (config) => gen(function* () {
|
|
|
52478
52551
|
const writeRaw = yield* socket.writer;
|
|
52479
52552
|
yield* forever(take$1(outbox).pipe(flatMap$1(writeRaw)));
|
|
52480
52553
|
}));
|
|
52481
|
-
const
|
|
52554
|
+
const attachRpcTunnel = (tunnelId, live) => gen(function* () {
|
|
52482
52555
|
yield* addFinalizer(() => sync(() => {
|
|
52483
52556
|
if (tunnels.get(tunnelId) !== live) return;
|
|
52484
52557
|
tunnels.delete(tunnelId);
|
|
@@ -52504,25 +52577,141 @@ const runHubTunnel = (config) => gen(function* () {
|
|
|
52504
52577
|
}
|
|
52505
52578
|
});
|
|
52506
52579
|
const { protocol, onSocket } = yield* makeTunnelSocketProtocol.pipe(provide$1(layerJson));
|
|
52507
|
-
|
|
52580
|
+
console.error("[DEBUG-provider-checking-20260820] rpc server starting");
|
|
52581
|
+
yield* forkScoped(make$13(WsRpcGroup, { disableTracing: true }).pipe(provideService(Protocol, protocol), provide$1(makeWsRpcLayer({
|
|
52508
52582
|
sessionId: issued.sessionId,
|
|
52509
52583
|
subject: "hub-tunnel",
|
|
52510
52584
|
method: "bearer-access-token",
|
|
52511
52585
|
scopes: [...TUNNEL_SCOPES]
|
|
52512
|
-
}).pipe(provide(layer$8))),
|
|
52586
|
+
}).pipe(provideMerge(layerJson), provide(layer$8))), tapCause((cause) => logError("hub tunnel: RPC server failed", { cause: String(cause) }))));
|
|
52587
|
+
console.error("[DEBUG-provider-checking-20260820] rpc server started");
|
|
52513
52588
|
yield* forkScoped(onSocket(socketAdapter));
|
|
52514
52589
|
if (!writeHub(JSON.stringify({
|
|
52515
52590
|
type: "tunnel_open",
|
|
52516
52591
|
tunnelId
|
|
52517
52592
|
}))) dropTunnel(tunnelId);
|
|
52518
|
-
yield* logInfo("hub tunnel: attached", {
|
|
52593
|
+
yield* logInfo("hub tunnel: attached", {
|
|
52594
|
+
tunnelId,
|
|
52595
|
+
purpose: live.purpose
|
|
52596
|
+
});
|
|
52519
52597
|
yield* _await(live.closed);
|
|
52520
52598
|
}).pipe(catch_((error) => logWarning$1("hub tunnel: attach failed", {
|
|
52521
52599
|
tunnelId,
|
|
52522
52600
|
error
|
|
52523
52601
|
})));
|
|
52524
|
-
|
|
52525
|
-
|
|
52602
|
+
const attachHttpTunnel = (tunnelId, live) => gen(function* () {
|
|
52603
|
+
yield* addFinalizer(() => sync(() => {
|
|
52604
|
+
if (tunnels.get(tunnelId) !== live) return;
|
|
52605
|
+
tunnels.delete(tunnelId);
|
|
52606
|
+
writeHub(JSON.stringify({
|
|
52607
|
+
type: "tunnel_close",
|
|
52608
|
+
tunnelId,
|
|
52609
|
+
code: 1011,
|
|
52610
|
+
reason: "daemon http tunnel ended"
|
|
52611
|
+
}));
|
|
52612
|
+
}));
|
|
52613
|
+
const issued = yield* serverAuth.issueSession({
|
|
52614
|
+
subject: "hub-tunnel-http",
|
|
52615
|
+
label: "AgentLink hub tunnel HTTP proxy",
|
|
52616
|
+
scopes: [...TUNNEL_SCOPES]
|
|
52617
|
+
});
|
|
52618
|
+
yield* addFinalizer(() => sessionsStore.markDisconnected(issued.sessionId).pipe(andThen(serverAuth.revokeSession(issued.sessionId)), ignore$1));
|
|
52619
|
+
yield* sessionsStore.markConnected(issued.sessionId);
|
|
52620
|
+
const localBaseUrl = `http://127.0.0.1:${serverConfig.port}`;
|
|
52621
|
+
const sendEncrypted = (frame) => {
|
|
52622
|
+
try {
|
|
52623
|
+
const envelope = utf8ToEnvelope(config.enckey, encodeHttpTunnelResponseFrame(frame));
|
|
52624
|
+
if (!writeHub(`${tunnelId} ${JSON.stringify(envelope)}`)) dropTunnel(tunnelId);
|
|
52625
|
+
} catch {
|
|
52626
|
+
dropTunnel(tunnelId);
|
|
52627
|
+
}
|
|
52628
|
+
};
|
|
52629
|
+
live.onDecryptedFrame = (frame) => {
|
|
52630
|
+
let requestFrame;
|
|
52631
|
+
try {
|
|
52632
|
+
const parsed = JSON.parse(frame);
|
|
52633
|
+
if (!isHttpTunnelRequestFrame(parsed)) {
|
|
52634
|
+
sendEncrypted({
|
|
52635
|
+
_tag: "HttpResponse",
|
|
52636
|
+
id: typeof parsed?.id === "string" ? parsed.id : "unknown",
|
|
52637
|
+
status: 400,
|
|
52638
|
+
statusText: "Bad Request",
|
|
52639
|
+
headers: [["content-type", "text/plain"]],
|
|
52640
|
+
body: Buffer.from("Invalid HTTP tunnel request frame").toString("base64")
|
|
52641
|
+
});
|
|
52642
|
+
return;
|
|
52643
|
+
}
|
|
52644
|
+
requestFrame = parsed;
|
|
52645
|
+
} catch {
|
|
52646
|
+
sendEncrypted({
|
|
52647
|
+
_tag: "HttpResponse",
|
|
52648
|
+
id: "unknown",
|
|
52649
|
+
status: 400,
|
|
52650
|
+
statusText: "Bad Request",
|
|
52651
|
+
headers: [["content-type", "text/plain"]],
|
|
52652
|
+
body: Buffer.from("Malformed HTTP tunnel request frame").toString("base64")
|
|
52653
|
+
});
|
|
52654
|
+
return;
|
|
52655
|
+
}
|
|
52656
|
+
runFork(gen(function* () {
|
|
52657
|
+
const targetUrl = `${localBaseUrl}${requestFrame.path}`;
|
|
52658
|
+
const headers = { authorization: `Bearer ${issued.token}` };
|
|
52659
|
+
for (const [key, value] of requestFrame.headers) {
|
|
52660
|
+
const lower = key.toLowerCase();
|
|
52661
|
+
if (lower === "authorization" || lower === "cookie") continue;
|
|
52662
|
+
headers[lower] = value;
|
|
52663
|
+
}
|
|
52664
|
+
let request = make$61(requestFrame.method)(targetUrl).pipe(setHeaders(headers));
|
|
52665
|
+
if (requestFrame.body !== null) request = setBody(uint8Array(Buffer.from(requestFrame.body, "base64")))(request);
|
|
52666
|
+
const response = yield* httpClient.execute(request);
|
|
52667
|
+
const responseBody = yield* response.arrayBuffer;
|
|
52668
|
+
const responseHeaders = [];
|
|
52669
|
+
for (const [key, value] of Object.entries(response.headers)) if (typeof value === "string") responseHeaders.push([key, value]);
|
|
52670
|
+
return {
|
|
52671
|
+
_tag: "HttpResponse",
|
|
52672
|
+
id: requestFrame.id,
|
|
52673
|
+
status: response.status,
|
|
52674
|
+
statusText: "",
|
|
52675
|
+
headers: responseHeaders,
|
|
52676
|
+
body: Buffer.from(responseBody).toString("base64")
|
|
52677
|
+
};
|
|
52678
|
+
}).pipe(match$2({
|
|
52679
|
+
onFailure: (cause) => ({
|
|
52680
|
+
_tag: "HttpResponse",
|
|
52681
|
+
id: requestFrame.id,
|
|
52682
|
+
status: 502,
|
|
52683
|
+
statusText: "Bad Gateway",
|
|
52684
|
+
headers: [["content-type", "text/plain"]],
|
|
52685
|
+
body: Buffer.from(cause instanceof Error ? cause.message : String(cause)).toString("base64")
|
|
52686
|
+
}),
|
|
52687
|
+
onSuccess: (response) => response
|
|
52688
|
+
}), tap((frame) => sync(() => sendEncrypted(frame)))));
|
|
52689
|
+
};
|
|
52690
|
+
if (!writeHub(JSON.stringify({
|
|
52691
|
+
type: "tunnel_open",
|
|
52692
|
+
tunnelId
|
|
52693
|
+
}))) dropTunnel(tunnelId);
|
|
52694
|
+
yield* logInfo("hub tunnel: attached", {
|
|
52695
|
+
tunnelId,
|
|
52696
|
+
purpose: live.purpose
|
|
52697
|
+
});
|
|
52698
|
+
yield* _await(live.closed);
|
|
52699
|
+
}).pipe(provideService(HttpClient, httpClient), catch_((error) => logWarning$1("hub tunnel: attach failed", {
|
|
52700
|
+
tunnelId,
|
|
52701
|
+
error
|
|
52702
|
+
})));
|
|
52703
|
+
let debugProviderCheckingFrameCount = 0;
|
|
52704
|
+
yield* socket.runRaw((rawData) => {
|
|
52705
|
+
if (debugProviderCheckingFrameCount < 20) {
|
|
52706
|
+
debugProviderCheckingFrameCount += 1;
|
|
52707
|
+
console.error("[DEBUG-provider-checking-20260820] hub frame", {
|
|
52708
|
+
count: debugProviderCheckingFrameCount,
|
|
52709
|
+
type: typeof rawData,
|
|
52710
|
+
byteLength: typeof rawData === "string" ? rawData.length : rawData instanceof Uint8Array ? rawData.byteLength : -1
|
|
52711
|
+
});
|
|
52712
|
+
}
|
|
52713
|
+
const data = typeof rawData === "string" ? rawData : rawData instanceof Uint8Array ? hubFrameDecoder.decode(rawData) : null;
|
|
52714
|
+
if (data === null) return;
|
|
52526
52715
|
if (data.startsWith("{")) {
|
|
52527
52716
|
let control;
|
|
52528
52717
|
try {
|
|
@@ -52534,13 +52723,17 @@ const runHubTunnel = (config) => gen(function* () {
|
|
|
52534
52723
|
const tunnelId = control.tunnelId;
|
|
52535
52724
|
if (tunnels.has(tunnelId)) return;
|
|
52536
52725
|
return gen(function* () {
|
|
52726
|
+
const closed = yield* make$70();
|
|
52727
|
+
const purpose = control.purpose === "http" ? "http" : "rpc";
|
|
52537
52728
|
const live = {
|
|
52538
|
-
closed
|
|
52729
|
+
closed,
|
|
52539
52730
|
pendingFrames: [],
|
|
52731
|
+
purpose,
|
|
52540
52732
|
onDecryptedFrame: null
|
|
52541
52733
|
};
|
|
52542
52734
|
tunnels.set(tunnelId, live);
|
|
52543
|
-
|
|
52735
|
+
if (purpose === "http") runFork(scoped(attachHttpTunnel(tunnelId, live)));
|
|
52736
|
+
else runFork(scoped(attachRpcTunnel(tunnelId, live)));
|
|
52544
52737
|
});
|
|
52545
52738
|
}
|
|
52546
52739
|
if (control.type === "tunnel_close" && typeof control.tunnelId === "string") dropTunnel(control.tunnelId);
|
|
@@ -52563,12 +52756,17 @@ const runHubTunnel = (config) => gen(function* () {
|
|
|
52563
52756
|
} catch {
|
|
52564
52757
|
return dropTunnel(framed.tunnelId);
|
|
52565
52758
|
}
|
|
52759
|
+
console.error("[DEBUG-provider-checking-20260820] frame decrypted", {
|
|
52760
|
+
tunnelId: framed.tunnelId,
|
|
52761
|
+
byteLength: frame.length,
|
|
52762
|
+
delivery: live.onDecryptedFrame ? "deliver" : "pending"
|
|
52763
|
+
});
|
|
52566
52764
|
if (live.onDecryptedFrame) live.onDecryptedFrame(frame);
|
|
52567
52765
|
else if (live.pendingFrames.length < 16) live.pendingFrames.push(frame);
|
|
52568
52766
|
else dropTunnel(framed.tunnelId);
|
|
52569
52767
|
return;
|
|
52570
52768
|
}
|
|
52571
|
-
}, { onOpen: logInfo("hub tunnel: connected to hub") }).pipe(tapDefect((cause) => logWarning$1("hub tunnel: hub socket defect", { cause: String(cause) })));
|
|
52769
|
+
}, { onOpen: logInfo("hub tunnel: connected to hub").pipe(tap(() => sync(() => void runFork(upload)))) }).pipe(tapDefect((cause) => logWarning$1("hub tunnel: hub socket defect", { cause: String(cause) })));
|
|
52572
52770
|
yield* logWarning$1("hub tunnel: hub connection closed");
|
|
52573
52771
|
}).pipe(scoped, tapError((error) => logWarning$1("hub tunnel: connection error", { error }))), {
|
|
52574
52772
|
schedule: spaced("2 seconds"),
|