@agentknight/client 0.9.47 → 0.9.48

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
@@ -3399,9 +3399,20 @@ function renderAgentSelectionPage(input) {
3399
3399
  const accountPanel = account === void 0 ? "" : `
3400
3400
  <div id="relay-account" class="account" data-state="${account.state}" role="status" aria-label="Relay \u8D26\u6237">
3401
3401
  <span id="relay-account-name" class="account-name">${escapeHtml(relayAccountLabel(account))}</span>
3402
- <div class="account-meta"><span id="relay-account-method">${account.method === "microsoft" ? "Microsoft" : "\u4F20\u7EDF\u8D26\u6237"}</span><span id="relay-account-role"${account.user === void 0 ? " hidden" : ""}>${account.user?.role === "admin" ? "\u7BA1\u7406\u5458" : "\u666E\u901A\u7528\u6237"}</span><span id="relay-account-state">${account.state === "signed-in" ? "\u5DF2\u767B\u5F55" : account.state === "connecting" ? "\u8FDE\u63A5\u4E2D" : account.state === "error" ? "\u767B\u5F55\u5F02\u5E38" : "\u72B6\u6001\u672A\u77E5"}</span>${accountUrl === void 0 ? "" : `<a id="relay-account-link" href="${escapeHtml(accountUrl)}" target="_blank" rel="noopener noreferrer"${account.method === "microsoft" ? "" : " hidden"}>\u67E5\u770B\u8D26\u6237</a>`}</div>
3402
+ <div class="account-meta"><span id="relay-account-method">${account.method === "microsoft" ? "Microsoft" : "\u4F20\u7EDF\u8D26\u6237"}</span><span id="relay-account-role"${account.user === void 0 ? " hidden" : ""}>${account.user?.role === "admin" ? "\u7BA1\u7406\u5458" : "\u666E\u901A\u7528\u6237"}</span><span id="relay-account-state">${account.state === "signed-in" ? "\u5DF2\u767B\u5F55" : account.state === "connecting" ? "\u8FDE\u63A5\u4E2D" : account.state === "disconnected" ? "\u5DF2\u65AD\u5F00" : account.state === "error" ? "\u767B\u5F55\u5F02\u5E38" : "\u72B6\u6001\u672A\u77E5"}</span>${accountUrl === void 0 ? "" : `<a id="relay-account-link" href="${escapeHtml(accountUrl)}" target="_blank" rel="noopener noreferrer"${account.method === "microsoft" ? "" : " hidden"}>\u67E5\u770B\u8D26\u6237</a>`}</div>
3403
3403
  <p id="relay-account-error" class="error"${account.error === void 0 ? " hidden" : ""}>${escapeHtml(account.error ?? "")}</p>
3404
3404
  </div>`;
3405
+ const reconnect = input.relayState === void 0 ? "" : `
3406
+ <form id="relay-reconnect-form" method="post" action="/relay-reconnect" data-state="${input.relayState}">
3407
+ <input type="hidden" name="token" value="${escapeHtml(input.token)}">
3408
+ <button id="relay-reconnect" class="secondary" type="submit"${input.relayState === "registered" ? " hidden" : ""}${input.relayState === "connecting" ? " disabled" : ""}>${input.relayState === "connecting" ? "\u8FDE\u63A5\u4E2D\u2026" : "\u91CD\u8FDE"}</button>
3409
+ <p id="relay-reconnect-error" class="error" role="alert"${input.reconnectError ? "" : " hidden"}>${escapeHtml(input.reconnectError ?? "")}</p>
3410
+ </form>`;
3411
+ const disconnect = !input.canDisconnectRelay || input.relayState === void 0 ? "" : `
3412
+ <form id="relay-disconnect-form" method="post" action="/relay-disconnect">
3413
+ <input type="hidden" name="token" value="${escapeHtml(input.token)}">
3414
+ <button id="relay-disconnect" class="secondary" type="submit"${input.relayState === "disconnected" ? " hidden" : ""}>\u65AD\u5F00\u8FDE\u63A5</button>
3415
+ </form>`;
3405
3416
  const deployment = input.deploymentInfo;
3406
3417
  const upgradedAt = deployment?.upgradedAt;
3407
3418
  const upgradeTime = upgradedAt === void 0 ? void 0 : new Date(upgradedAt);
@@ -3455,6 +3466,7 @@ function renderAgentSelectionPage(input) {
3455
3466
  header { display: flex; gap: 24px; align-items: center; justify-content: space-between; margin-bottom: 38px; }
3456
3467
  .header-actions { display: flex; align-items: center; gap: 24px; min-width: 0; }
3457
3468
  .header-actions > a { flex-shrink: 0; }
3469
+ #relay-reconnect, #relay-disconnect { white-space: nowrap; }
3458
3470
  .account { min-width: 0; max-width: 360px; text-align: right; }
3459
3471
  .account-name { display: block; font-size: 15px; font-weight: 650; overflow-wrap: anywhere; }
3460
3472
  .account-meta { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: 6px 12px; margin-top: 6px; font-size: 12px; color: var(--muted); }
@@ -3546,7 +3558,7 @@ function renderAgentSelectionPage(input) {
3546
3558
  </head>
3547
3559
  <body>
3548
3560
  <main>
3549
- <header><div><span class="brand"><span class="brand-mark" aria-hidden="true"></span>AgentKnight</span><h1>${escapeHtml(input.machineName)}</h1></div>${account === void 0 ? '<a href="/">\u5237\u65B0</a>' : `<div class="header-actions">${accountPanel}<a href="/">\u5237\u65B0</a></div>`}</header>
3561
+ <header><div><span class="brand"><span class="brand-mark" aria-hidden="true"></span>AgentKnight</span><h1>${escapeHtml(input.machineName)}</h1></div><div class="header-actions">${accountPanel}${reconnect}${disconnect}<a href="/">\u5237\u65B0</a></div></header>
3550
3562
  ${deploymentPanel}
3551
3563
  <div class="layout">
3552
3564
  <section aria-labelledby="agents-heading">
@@ -3570,7 +3582,7 @@ function renderAgentSelectionPage(input) {
3570
3582
  <input type="hidden" name="token" value="${escapeHtml(input.token)}">
3571
3583
  <div class="pair-actions"><button id="show-qr" class="secondary" type="submit"${input.pairing ? " hidden" : ""}>\u663E\u793A\u4E8C\u7EF4\u7801</button><button id="refresh-qr" class="secondary" type="submit" formaction="/refresh"${input.pairing ? "" : " hidden"}>\u5237\u65B0\u4E8C\u7EF4\u7801</button><button id="hide-qr" class="secondary" type="button"${input.pairing ? "" : " hidden"}>\u9690\u85CF</button></div>
3572
3584
  </form>
3573
- <p id="pair-error" class="error" role="alert"${input.pairingError ? "" : " hidden"}>${escapeHtml(input.pairingError ?? "")}</p>
3585
+ <p id="pair-error" class="error" role="alert"${input.pairingError ? "" : " hidden"}${input.pairingError && input.relayState !== void 0 && input.relayState !== "registered" ? ' data-relay-offline="true"' : ""}>${escapeHtml(input.pairingError ?? "")}</p>
3574
3586
  </aside>
3575
3587
  </div>
3576
3588
  </main>
@@ -3579,6 +3591,98 @@ function renderAgentSelectionPage(input) {
3579
3591
  }
3580
3592
  var localPageScript = `
3581
3593
  (() => {
3594
+ const reconnectForm = document.querySelector('#relay-reconnect-form');
3595
+ if (reconnectForm) {
3596
+ const button = document.querySelector('#relay-reconnect');
3597
+ const disconnectForm = document.querySelector('#relay-disconnect-form');
3598
+ const disconnectButton = document.querySelector('#relay-disconnect');
3599
+ const error = document.querySelector('#relay-reconnect-error');
3600
+ let stopped = false;
3601
+ let timer;
3602
+ let generation = 0;
3603
+ let submitting = false;
3604
+ let state = reconnectForm.dataset.state;
3605
+ let failureState;
3606
+ const update = next => {
3607
+ if (!next || !['connecting', 'registered', 'disconnected', 'error'].includes(next.state)) throw new Error('Invalid Relay status');
3608
+ state = next.state;
3609
+ button.hidden = next.state === 'registered';
3610
+ button.disabled = next.state === 'connecting';
3611
+ button.textContent = next.state === 'connecting' ? '\u8FDE\u63A5\u4E2D\u2026' : '\u91CD\u8FDE';
3612
+ if (disconnectButton) {
3613
+ disconnectButton.hidden = next.state === 'disconnected';
3614
+ disconnectButton.disabled = false;
3615
+ disconnectButton.textContent = '\u65AD\u5F00\u8FDE\u63A5';
3616
+ }
3617
+ if (next.state === 'disconnected') conceal();
3618
+ const pairingError = document.querySelector('#pair-error');
3619
+ if (next.state === 'registered' && pairingError?.dataset.relayOffline === 'true') {
3620
+ pairingError.hidden = true;
3621
+ pairingError.textContent = '';
3622
+ delete pairingError.dataset.relayOffline;
3623
+ }
3624
+ };
3625
+ const poll = async () => {
3626
+ const current = generation;
3627
+ try {
3628
+ const response = await fetch('/relay-status', { cache: 'no-store', signal: AbortSignal.timeout(5000) });
3629
+ if (!response.ok) throw new Error('Relay status unavailable');
3630
+ const next = await response.json();
3631
+ if (!stopped && current === generation && !submitting) {
3632
+ update(next);
3633
+ if (next.state !== failureState) error.hidden = true;
3634
+ }
3635
+ } catch {
3636
+ if (!stopped && current === generation && !submitting) {
3637
+ error.textContent = '\u65E0\u6CD5\u66F4\u65B0 Relay \u8FDE\u63A5\u72B6\u6001\uFF0C\u8BF7\u5237\u65B0\u9875\u9762\u3002';
3638
+ error.hidden = false;
3639
+ }
3640
+ } finally {
3641
+ if (!stopped && current === generation) timer = setTimeout(poll, 3000);
3642
+ }
3643
+ };
3644
+ const submit = async (event, form, actionButton, disconnecting) => {
3645
+ event.preventDefault();
3646
+ if (actionButton.disabled || submitting) return;
3647
+ const current = ++generation;
3648
+ clearTimeout(timer);
3649
+ submitting = true;
3650
+ button.disabled = true;
3651
+ if (disconnectButton) disconnectButton.disabled = true;
3652
+ actionButton.textContent = disconnecting ? '\u6B63\u5728\u65AD\u5F00\u2026' : '\u8FDE\u63A5\u4E2D\u2026';
3653
+ error.hidden = true;
3654
+ failureState = undefined;
3655
+ try {
3656
+ const response = await fetch(form.action, {
3657
+ method: 'POST', headers: { Accept: 'application/json' },
3658
+ body: new URLSearchParams(new FormData(form)), signal: AbortSignal.timeout(5000),
3659
+ });
3660
+ if (!response.ok) throw new Error('Relay reconnect failed');
3661
+ const next = await response.json();
3662
+ if (!stopped && current === generation) {
3663
+ update(next);
3664
+ if (actionButton.hidden) (button.hidden ? disconnectButton : button)?.focus();
3665
+ }
3666
+ } catch {
3667
+ if (!stopped && current === generation) {
3668
+ update({ state: disconnecting ? state : 'error' });
3669
+ failureState = state;
3670
+ error.textContent = disconnecting ? '\u65E0\u6CD5\u65AD\u5F00 Relay\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002' : '\u65E0\u6CD5\u91CD\u8FDE Relay\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002';
3671
+ error.hidden = false;
3672
+ }
3673
+ } finally {
3674
+ submitting = false;
3675
+ if (!stopped && current === generation) timer = setTimeout(poll, 1000);
3676
+ }
3677
+ };
3678
+ reconnectForm.addEventListener('submit', event => submit(event, reconnectForm, button, false));
3679
+ if (disconnectForm && disconnectButton) {
3680
+ disconnectForm.addEventListener('submit', event => submit(event, disconnectForm, disconnectButton, true));
3681
+ }
3682
+ addEventListener('pagehide', () => { stopped = true; generation++; clearTimeout(timer); });
3683
+ addEventListener('pageshow', () => { if (stopped) { stopped = false; timer = setTimeout(poll, 0); } });
3684
+ timer = setTimeout(poll, 1000);
3685
+ }
3582
3686
  const account = document.querySelector('#relay-account');
3583
3687
  if (account) {
3584
3688
  const accountLabel = ${relayAccountLabel.toString()};
@@ -3605,7 +3709,7 @@ var localPageScript = `
3605
3709
  role.hidden = !next.user;
3606
3710
  role.textContent = next.user?.role === 'admin' ? '\u7BA1\u7406\u5458' : '\u666E\u901A\u7528\u6237';
3607
3711
  account.dataset.state = next.state;
3608
- state.textContent = next.state === 'signed-in' ? '\u5DF2\u767B\u5F55' : next.state === 'connecting' ? '\u8FDE\u63A5\u4E2D' : next.state === 'error' ? '\u767B\u5F55\u5F02\u5E38' : '\u72B6\u6001\u672A\u77E5';
3712
+ state.textContent = next.state === 'signed-in' ? '\u5DF2\u767B\u5F55' : next.state === 'connecting' ? '\u8FDE\u63A5\u4E2D' : next.state === 'disconnected' ? '\u5DF2\u65AD\u5F00' : next.state === 'error' ? '\u767B\u5F55\u5F02\u5E38' : '\u72B6\u6001\u672A\u77E5';
3609
3713
  error.textContent = next.error ?? '';
3610
3714
  error.hidden = !next.error;
3611
3715
  } catch {
@@ -3786,6 +3890,7 @@ var localPageScript = `
3786
3890
  loading = true;
3787
3891
  show.disabled = refresh.disabled = hide.disabled = true;
3788
3892
  pairError.hidden = true;
3893
+ delete pairError.dataset.relayOffline;
3789
3894
  conceal();
3790
3895
  const generation = statusGeneration;
3791
3896
  const endpoint = event.submitter?.getAttribute('formaction') || pairing.action;
@@ -4131,6 +4236,11 @@ var PairingPageServer = class {
4131
4236
  return;
4132
4237
  }
4133
4238
  const relayLogin = this.#options.relayLogin;
4239
+ if (relayLogin === void 0 && this.#options.reconnectRelay !== void 0 && request.method === "GET" && requestUrl.pathname === "/relay-status") {
4240
+ this.#writeHeaders(response, 200, "application/json; charset=utf-8");
4241
+ response.end(JSON.stringify({ state: this.#options.getRegistrationStatus().state }));
4242
+ return;
4243
+ }
4134
4244
  if (relayLogin === void 0 && this.#options.getAccount !== void 0 && request.method === "GET" && requestUrl.pathname === "/relay-account") {
4135
4245
  const account = await this.#options.getAccount();
4136
4246
  this.#writeHeaders(response, 200, "application/json; charset=utf-8");
@@ -4174,12 +4284,44 @@ var PairingPageServer = class {
4174
4284
  this.#redirectLogin(response, operation === "start" && next.state === "pending" ? next.verificationUri : "/");
4175
4285
  return;
4176
4286
  }
4177
- if (request.method === "POST" && ["/pair", "/pair/status", "/refresh", "/selection", "/group-chat-settings", "/peer-chat-settings"].includes(requestUrl.pathname)) {
4287
+ if (request.method === "POST" && ["/pair", "/pair/status", "/refresh", "/selection", "/group-chat-settings", "/peer-chat-settings", "/relay-reconnect", "/relay-disconnect"].includes(requestUrl.pathname)) {
4178
4288
  if (await this.#readAuthorizedForm(request, response) === void 0) return;
4179
4289
  this.#renderLogin(response, 409, this.#loginState(relayLogin));
4180
4290
  return;
4181
4291
  }
4182
4292
  }
4293
+ const relayOperation = requestUrl.pathname === "/relay-reconnect" ? this.#options.reconnectRelay : requestUrl.pathname === "/relay-disconnect" ? this.#options.disconnectRelay : void 0;
4294
+ if (request.method === "POST" && relayOperation !== void 0) {
4295
+ const disconnecting = requestUrl.pathname === "/relay-disconnect";
4296
+ const form = await this.#readAuthorizedForm(request, response);
4297
+ if (form === void 0) return;
4298
+ if (form.getAll("token").length !== 1 || [...form.keys()].some((key) => key !== "token")) {
4299
+ this.#writeHeaders(response, 400, "text/plain; charset=utf-8");
4300
+ response.end(disconnecting ? "Invalid Relay disconnect request." : "Invalid Relay reconnect request.");
4301
+ return;
4302
+ }
4303
+ try {
4304
+ relayOperation();
4305
+ } catch (error) {
4306
+ const message = disconnecting ? "\u65E0\u6CD5\u65AD\u5F00 Relay\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" : "\u65E0\u6CD5\u91CD\u8FDE Relay\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
4307
+ this.#options.logger?.error({ error: toErrorDetails(error) }, disconnecting ? "relay.disconnect.failed" : "relay.reconnect.failed");
4308
+ if (wantsJson(request)) {
4309
+ this.#writeHeaders(response, 503, "application/json; charset=utf-8");
4310
+ response.end(JSON.stringify({ error: message }));
4311
+ } else {
4312
+ await this.#renderPage(response, 503, { reconnectError: message });
4313
+ }
4314
+ return;
4315
+ }
4316
+ if (wantsJson(request)) {
4317
+ this.#writeHeaders(response, 200, "application/json; charset=utf-8");
4318
+ response.end(JSON.stringify({ state: this.#options.getRegistrationStatus().state }));
4319
+ } else {
4320
+ response.writeHead(303, { location: "/", "cache-control": "no-store" });
4321
+ response.end();
4322
+ }
4323
+ return;
4324
+ }
4183
4325
  const chatKind = requestUrl.pathname === "/group-chat-settings" ? "group" : requestUrl.pathname === "/peer-chat-settings" ? "peer" : void 0;
4184
4326
  const chatSettings = chatKind === "group" ? this.#options.groupChatSettings : this.#options.peerChatSettings;
4185
4327
  if (request.method === "POST" && chatKind !== void 0 && chatSettings !== void 0) {
@@ -4312,9 +4454,12 @@ var PairingPageServer = class {
4312
4454
  "/relay-login/start",
4313
4455
  "/relay-login/cancel",
4314
4456
  "/relay-login/callback",
4457
+ "/relay-reconnect",
4458
+ "/relay-disconnect",
4459
+ ...this.#options.reconnectRelay === void 0 || relayLogin !== void 0 ? [] : ["/relay-status"],
4315
4460
  ...relayLogin === void 0 ? [] : ["/relay-login/status"]
4316
4461
  ].includes(requestUrl.pathname)) {
4317
- response.setHeader("allow", requestUrl.pathname === "/pair" ? "GET, POST" : ["/", "/local-page.js", "/relay-login.js", "/relay-login/status", "/relay-login/callback"].includes(requestUrl.pathname) ? "GET" : "POST");
4462
+ response.setHeader("allow", requestUrl.pathname === "/pair" ? "GET, POST" : ["/", "/local-page.js", "/relay-login.js", "/relay-login/status", "/relay-login/callback", "/relay-status"].includes(requestUrl.pathname) ? "GET" : "POST");
4318
4463
  this.#writeHeaders(response, 405, "text/plain; charset=utf-8");
4319
4464
  response.end("Method not allowed.");
4320
4465
  return;
@@ -4354,6 +4499,8 @@ var PairingPageServer = class {
4354
4499
  response.end(renderAgentSelectionPage({
4355
4500
  machineName: this.#options.machineName,
4356
4501
  token: this.#refreshToken,
4502
+ ...this.#options.reconnectRelay === void 0 ? {} : { relayState: registration.state },
4503
+ ...this.#options.disconnectRelay === void 0 ? {} : { canDisconnectRelay: true },
4357
4504
  ...deploymentInfo === void 0 ? {} : { deploymentInfo },
4358
4505
  ...account === void 0 ? {} : { account },
4359
4506
  ...agents === void 0 ? {} : { agents },
@@ -5313,8 +5460,8 @@ var RelayAccount = class {
5313
5460
  this.#expiresAt = 0;
5314
5461
  this.#pending = void 0;
5315
5462
  return this.#view(
5316
- registration.state === "connecting" ? "connecting" : "error",
5317
- registration.state === "error" ? "Relay \u8FDE\u63A5\u5F02\u5E38\uFF0C\u8BF7\u68C0\u67E5\u8FDE\u63A5\u6216\u91CD\u65B0\u767B\u5F55\u3002" : void 0
5463
+ registration.state === "connecting" ? "connecting" : "disconnected",
5464
+ registration.state === "error" ? "Relay \u8FDE\u63A5\u5DF2\u65AD\u5F00\u3002" : void 0
5318
5465
  );
5319
5466
  }
5320
5467
  if (this.#cached !== void 0 && Date.now() < this.#expiresAt) return this.#cached;
@@ -7248,6 +7395,8 @@ async function main() {
7248
7395
  port: config.pairingPagePort,
7249
7396
  pairingProvider,
7250
7397
  getRegistrationStatus: () => runningAgent.relayRegistrationStatus,
7398
+ reconnectRelay: () => runningAgent.reconnectRelay(),
7399
+ disconnectRelay: () => runningAgent.disconnectRelay(),
7251
7400
  getAccount: () => relayAccount.read(runningAgent.relayRegistrationStatus),
7252
7401
  getDeploymentInfo: async () => {
7253
7402
  const lastUpgrade = await readLastUpgrade(config.dataDirectory);
@@ -20193,7 +20193,7 @@ async function installPreparedPackage(options) {
20193
20193
  import { createConnection, createServer } from "node:net";
20194
20194
  import { join as join5, resolve as resolve3 } from "node:path";
20195
20195
 
20196
- // ../../shared/sdk/dist/chunks/chunk-DUMC5F2D.js
20196
+ // ../../shared/sdk/dist/chunks/chunk-BDKVGENQ.js
20197
20197
  var IdSchema = external_exports.string().trim().min(1).max(256);
20198
20198
  var IsoDateSchema = external_exports.iso.datetime({ offset: true });
20199
20199
  var Base64UrlKeyIdSchema = external_exports.string().regex(/^[A-Za-z0-9_-]{22}$/u, "Expected a 16-byte base64url key ID");
@@ -22765,7 +22765,7 @@ var AndroidProtocolSchemas = {
22765
22765
  ProtocolError: ProtocolErrorSchema
22766
22766
  };
22767
22767
 
22768
- // ../../shared/sdk/dist/chunks/chunk-GRXWNM6S.js
22768
+ // ../../shared/sdk/dist/chunks/chunk-7HZ5UZSC.js
22769
22769
  var OwnerSchema = external_exports.object({ pid: external_exports.number().int().positive(), instanceId: external_exports.uuid().optional() });
22770
22770
  var PeerChatRequestSchema = external_exports.object({
22771
22771
  sessionId: IdSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentknight/client",
3
- "version": "0.9.47",
3
+ "version": "0.9.48",
4
4
  "description": "AgentKnight PC Agent CLI for remote coding sessions",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -41,7 +41,7 @@
41
41
  "qrcode": "1.5.4",
42
42
  "qrcode-terminal": "0.12.0",
43
43
  "zod": "4.6.5",
44
- "@agentknight/sdk": "0.9.47"
44
+ "@agentknight/sdk": "0.9.48"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/qrcode": "1.5.6",
@@ -49,8 +49,8 @@
49
49
  "esbuild": "0.28.2",
50
50
  "sharp": "0.35.5",
51
51
  "socket.io-client": "4.8.4",
52
- "@agentknight/protocol": "0.1.0",
53
- "@agentknight/shared": "0.1.0"
52
+ "@agentknight/shared": "0.1.0",
53
+ "@agentknight/protocol": "0.1.0"
54
54
  },
55
55
  "scripts": {
56
56
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",