@agentknight/client 0.9.40 → 0.9.42

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
@@ -680,13 +680,25 @@ var PairingCreateResultSchema = z5.discriminatedUnion("ok", [
680
680
  z5.object({
681
681
  ok: z5.literal(true),
682
682
  code: z5.string().min(32).max(256),
683
- expiresAt: z5.number().int().positive()
683
+ expiresAt: z5.number().int().positive(),
684
+ // Presence advertises the PC-only status endpoint; older Relays omit it.
685
+ statusId: z5.string().regex(/^[A-Za-z0-9_-]{43}$/u).optional()
684
686
  }),
685
687
  z5.object({
686
688
  ok: z5.literal(false),
687
689
  error: ProtocolFailureSchema
688
690
  })
689
691
  ]);
692
+ var PairingStatusRequestSchema = z5.object({
693
+ userId: IdSchema,
694
+ machineId: IdSchema,
695
+ statusId: z5.string().regex(/^[A-Za-z0-9_-]{43}$/u)
696
+ });
697
+ var PairingStatusSchema = z5.enum(["pending", "paired", "unavailable"]);
698
+ var PairingStatusResultSchema = z5.discriminatedUnion("ok", [
699
+ z5.object({ ok: z5.literal(true), state: PairingStatusSchema }),
700
+ z5.object({ ok: z5.literal(false), error: ProtocolFailureSchema })
701
+ ]);
690
702
  var PairingExchangeRequestSchema = z5.object({
691
703
  code: z5.string().min(32).max(256),
692
704
  clientId: IdSchema
@@ -3146,19 +3158,12 @@ var EnvironmentSchema = z28.object({
3146
3158
  SNAPSHOT_SUPPRESSION_ENABLED: EnabledBooleanStringSchema,
3147
3159
  TIMELINE_COMPRESSION_ENABLED: BooleanStringSchema,
3148
3160
  TIMELINE_INCREMENTAL_ENABLED: EnabledBooleanStringSchema,
3149
- SESSION_IMAGES_ALLOW_UNENCRYPTED_STORAGE: BooleanStringSchema,
3150
- SESSION_IMAGES_STORAGE_ACCOUNT: SessionImagesConfigSchema.shape.storageAccount.optional(),
3161
+ SESSION_IMAGES_ALLOW_UNENCRYPTED_STORAGE: EnabledBooleanStringSchema,
3162
+ SESSION_IMAGES_STORAGE_ACCOUNT: SessionImagesConfigSchema.shape.storageAccount.default("agentknightimagesbsp09"),
3151
3163
  SESSION_IMAGES_STORAGE_CONTAINER: SessionImagesConfigSchema.shape.storageContainer.optional(),
3152
3164
  SECURE_MESSAGE_PACKET_BUDGET: z28.coerce.number().int().min(2048).max(SECURE_MESSAGE_MAX_CIPHERTEXT_LENGTH).default(SECURE_MESSAGE_MAX_CIPHERTEXT_LENGTH)
3153
3165
  }).superRefine((value, context) => {
3154
3166
  if (value.SESSION_IMAGES_ALLOW_UNENCRYPTED_STORAGE) {
3155
- if (value.SESSION_IMAGES_STORAGE_ACCOUNT === void 0) {
3156
- context.addIssue({
3157
- code: "custom",
3158
- path: ["SESSION_IMAGES_STORAGE_ACCOUNT"],
3159
- message: "Session images require an independently configured Azure storage account"
3160
- });
3161
- }
3162
3167
  if (!SessionImageRelayUrlSchema.safeParse(value.RELAY_URL).success) {
3163
3168
  context.addIssue({
3164
3169
  code: "custom",
@@ -3166,12 +3171,6 @@ var EnvironmentSchema = z28.object({
3166
3171
  message: "Session images require an HTTPS Relay origin without a path, credentials, query or fragment"
3167
3172
  });
3168
3173
  }
3169
- } else if (value.SESSION_IMAGES_STORAGE_ACCOUNT !== void 0 || value.SESSION_IMAGES_STORAGE_CONTAINER !== void 0) {
3170
- context.addIssue({
3171
- code: "custom",
3172
- path: ["SESSION_IMAGES_ALLOW_UNENCRYPTED_STORAGE"],
3173
- message: "Session images require explicit consent to storage that is NOT end-to-end encrypted"
3174
- });
3175
3174
  }
3176
3175
  if (value.RELAY_AUTH === "token" && value.RELAY_TOKEN === void 0) {
3177
3176
  context.addIssue({ code: "custom", path: ["RELAY_TOKEN"], message: "Token authentication requires RELAY_TOKEN" });
@@ -3228,6 +3227,9 @@ function loadConfig(environment = process.env, options = {}) {
3228
3227
  }
3229
3228
  function loadStartupConfig(environment = process.env, options = {}) {
3230
3229
  const normalizedEnvironment = normalizeClientEnvironment(environment);
3230
+ if (normalizedEnvironment.SESSION_IMAGES_ALLOW_UNENCRYPTED_STORAGE === void 0 && !SessionImageRelayUrlSchema.safeParse(normalizedEnvironment.RELAY_URL).success) {
3231
+ normalizedEnvironment.SESSION_IMAGES_ALLOW_UNENCRYPTED_STORAGE = "false";
3232
+ }
3231
3233
  const initialSelection = options.agents === void 0 ? void 0 : AgentSelectionSchema2.parse({ agents: options.agents });
3232
3234
  const startupEnvironment = normalizedEnvironment.RELAY_AUTH === "microsoft" ? { ...normalizedEnvironment, RELAY_TOKEN: void 0, USER_ID: void 0 } : normalizedEnvironment;
3233
3235
  const parsed = EnvironmentSchema.parse(initialSelection === void 0 ? startupEnvironment : { ...startupEnvironment, AGENTKNIGHT_CODING_AGENT: "managed" });
@@ -3509,7 +3511,8 @@ function renderAgentSelectionPage(input) {
3509
3511
  <h2 id="pairing-heading">\u624B\u673A\u914D\u5BF9</h2>
3510
3512
  <div class="qr-area">
3511
3513
  <svg id="qr-placeholder" class="qr-placeholder" viewBox="0 0 64 64" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"${input.pairing ? " hidden" : ""}><rect x="8" y="8" width="18" height="18" rx="3"/><rect x="38" y="8" width="18" height="18" rx="3"/><rect x="8" y="38" width="18" height="18" rx="3"/><path d="M39 38h8v8h9M39 49v7h8M55 38v3"/></svg>
3512
- <div id="pairing-qr" class="qr" role="img" aria-label="\u914D\u5BF9\u4E8C\u7EF4\u7801"${input.pairing ? ` data-expires-at="${input.pairing.expiresAt}"` : " hidden"}>${input.pairing?.svg ?? ""}</div>
3514
+ <div id="pairing-qr" class="qr" role="img" aria-label="\u914D\u5BF9\u4E8C\u7EF4\u7801"${input.pairing ? ` data-expires-at="${input.pairing.expiresAt}"${input.pairing.statusId ? ` data-status-id="${escapeHtml(input.pairing.statusId)}"` : ""}` : " hidden"}>${input.pairing?.svg ?? ""}</div>
3515
+ <div id="pair-success" role="status" hidden>\u2713 \u914D\u5BF9\u6210\u529F</div>
3513
3516
  </div>
3514
3517
  <form id="pairing-form" method="post" action="/pair">
3515
3518
  <input type="hidden" name="token" value="${escapeHtml(input.token)}">
@@ -3655,17 +3658,64 @@ var localPageScript = `
3655
3658
  const refresh = document.querySelector('#refresh-qr');
3656
3659
  const hide = document.querySelector('#hide-qr');
3657
3660
  const pairError = document.querySelector('#pair-error');
3661
+ const pairSuccess = document.querySelector('#pair-success');
3658
3662
  let expiry;
3663
+ let statusTimer;
3664
+ let statusGeneration = 0;
3659
3665
  let loading = false;
3666
+ const stopStatus = () => {
3667
+ clearTimeout(statusTimer);
3668
+ statusGeneration++;
3669
+ };
3660
3670
  const conceal = () => {
3661
3671
  clearTimeout(expiry);
3672
+ stopStatus();
3662
3673
  qr.replaceChildren();
3663
3674
  qr.hidden = true;
3664
- placeholder.hidden = false;
3675
+ delete qr.dataset.statusId;
3676
+ delete qr.dataset.expiresAt;
3677
+ placeholder.removeAttribute('hidden');
3678
+ pairSuccess.hidden = true;
3665
3679
  show.hidden = false;
3666
3680
  refresh.hidden = true;
3667
3681
  hide.hidden = true;
3668
3682
  };
3683
+ const watchStatus = statusId => {
3684
+ stopStatus();
3685
+ if (typeof statusId !== 'string') return;
3686
+ const generation = statusGeneration;
3687
+ const poll = async () => {
3688
+ try {
3689
+ const body = new URLSearchParams(new FormData(pairing));
3690
+ body.set('statusId', statusId);
3691
+ const response = await fetch('/pair/status', { method: 'POST', headers: { Accept: 'application/json' }, body });
3692
+ const result = await response.json();
3693
+ if (generation !== statusGeneration) return;
3694
+ if (!response.ok || !['pending', 'paired', 'unavailable'].includes(result?.state)) throw new Error('Pairing status unavailable');
3695
+ if (result.state === 'paired') {
3696
+ conceal();
3697
+ placeholder.setAttribute('hidden', '');
3698
+ pairSuccess.hidden = false;
3699
+ pairError.hidden = true;
3700
+ return;
3701
+ }
3702
+ if (result.state === 'unavailable') {
3703
+ conceal();
3704
+ pairError.textContent = '\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u663E\u793A\u3002';
3705
+ pairError.hidden = false;
3706
+ return;
3707
+ }
3708
+ pairError.hidden = true;
3709
+ } catch {
3710
+ // A lost status response is not pairing success. Retry until this QR expires.
3711
+ if (generation !== statusGeneration) return;
3712
+ pairError.textContent = '\u65E0\u6CD5\u786E\u8BA4\u914D\u5BF9\u72B6\u6001\uFF0C\u6B63\u5728\u91CD\u8BD5\u3002';
3713
+ pairError.hidden = false;
3714
+ }
3715
+ if (generation === statusGeneration) statusTimer = setTimeout(poll, 1000);
3716
+ };
3717
+ statusTimer = setTimeout(poll, 1000);
3718
+ };
3669
3719
  const expire = expiresAt => {
3670
3720
  clearTimeout(expiry);
3671
3721
  expiry = setTimeout(() => {
@@ -3675,6 +3725,8 @@ var localPageScript = `
3675
3725
  }, Math.max(0, expiresAt - Date.now()));
3676
3726
  };
3677
3727
  if (qr.dataset.expiresAt) expire(Number(qr.dataset.expiresAt));
3728
+ if (qr.dataset.statusId) watchStatus(qr.dataset.statusId);
3729
+ if (typeof window !== 'undefined') window.addEventListener('pagehide', conceal);
3678
3730
  hide.addEventListener('click', conceal);
3679
3731
  pairing.addEventListener('submit', async event => {
3680
3732
  event.preventDefault();
@@ -3682,18 +3734,23 @@ var localPageScript = `
3682
3734
  loading = true;
3683
3735
  show.disabled = refresh.disabled = hide.disabled = true;
3684
3736
  pairError.hidden = true;
3737
+ conceal();
3738
+ const generation = statusGeneration;
3685
3739
  const endpoint = event.submitter?.getAttribute('formaction') || pairing.action;
3686
3740
  try {
3687
3741
  const response = await fetch(endpoint, { method: 'POST', headers: { Accept: 'application/json' }, body: new URLSearchParams(new FormData(pairing)) });
3688
3742
  const result = await response.json().catch(() => null);
3743
+ if (generation !== statusGeneration) return;
3689
3744
  if (!response.ok || typeof result?.svg !== 'string' || typeof result?.expiresAt !== 'number') throw new Error(result?.error || '\u65E0\u6CD5\u751F\u6210\u4E8C\u7EF4\u7801\uFF08HTTP ' + response.status + '\uFF09\uFF0C\u8BF7\u91CD\u8BD5\u3002');
3690
3745
  qr.innerHTML = result.svg;
3691
3746
  qr.hidden = false;
3692
- placeholder.hidden = true;
3747
+ placeholder.setAttribute('hidden', '');
3693
3748
  show.hidden = true;
3694
3749
  refresh.hidden = hide.hidden = false;
3695
3750
  expire(result.expiresAt);
3751
+ watchStatus(result.statusId);
3696
3752
  } catch (reason) {
3753
+ if (generation !== statusGeneration) return;
3697
3754
  conceal();
3698
3755
  pairError.textContent = reason instanceof Error ? reason.message : '\u65E0\u6CD5\u751F\u6210\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u91CD\u8BD5\u3002';
3699
3756
  pairError.hidden = false;
@@ -4065,7 +4122,7 @@ var PairingPageServer = class {
4065
4122
  this.#redirectLogin(response, operation === "start" && next.state === "pending" ? next.verificationUri : "/");
4066
4123
  return;
4067
4124
  }
4068
- if (request.method === "POST" && ["/pair", "/refresh", "/selection", "/group-chat-settings", "/peer-chat-settings"].includes(requestUrl.pathname)) {
4125
+ if (request.method === "POST" && ["/pair", "/pair/status", "/refresh", "/selection", "/group-chat-settings", "/peer-chat-settings"].includes(requestUrl.pathname)) {
4069
4126
  if (await this.#readAuthorizedForm(request, response) === void 0) return;
4070
4127
  this.#renderLogin(response, 409, this.#loginState(relayLogin));
4071
4128
  return;
@@ -4143,6 +4200,20 @@ var PairingPageServer = class {
4143
4200
  }
4144
4201
  return;
4145
4202
  }
4203
+ if (request.method === "POST" && requestUrl.pathname === "/pair/status") {
4204
+ const form = await this.#readAuthorizedForm(request, response);
4205
+ if (form === void 0) return;
4206
+ const statusId = PairingStatusRequestSchema.shape.statusId.safeParse(form.get("statusId"));
4207
+ if (!statusId.success || form.getAll("statusId").length !== 1) {
4208
+ this.#writeHeaders(response, 400, "application/json; charset=utf-8");
4209
+ response.end(JSON.stringify({ error: "Invalid pairing status identity." }));
4210
+ return;
4211
+ }
4212
+ const state = await this.#options.pairingProvider.getPairingStatus?.(statusId.data) ?? "unavailable";
4213
+ this.#writeHeaders(response, 200, "application/json; charset=utf-8");
4214
+ response.end(JSON.stringify({ state }));
4215
+ return;
4216
+ }
4146
4217
  if (request.method === "POST" && (requestUrl.pathname === "/pair" || requestUrl.pathname === "/refresh")) {
4147
4218
  if (await this.#readAuthorizedForm(request, response) === void 0) return;
4148
4219
  const registration = this.#options.getRegistrationStatus();
@@ -4159,7 +4230,11 @@ var PairingPageServer = class {
4159
4230
  width: 420,
4160
4231
  color: { dark: "#101820", light: "#ffffff" }
4161
4232
  });
4162
- const rendered = { svg, expiresAt: pairing.expiresAt };
4233
+ const rendered = {
4234
+ svg,
4235
+ expiresAt: pairing.expiresAt,
4236
+ ...pairing.statusId === void 0 ? {} : { statusId: pairing.statusId }
4237
+ };
4163
4238
  if (wantsJson(request)) {
4164
4239
  this.#writeHeaders(response, 200, "application/json; charset=utf-8");
4165
4240
  response.end(JSON.stringify(rendered));
@@ -4175,6 +4250,7 @@ var PairingPageServer = class {
4175
4250
  if ([
4176
4251
  "/",
4177
4252
  "/pair",
4253
+ "/pair/status",
4178
4254
  "/selection",
4179
4255
  "/group-chat-settings",
4180
4256
  "/peer-chat-settings",
@@ -19391,7 +19391,7 @@ async function installPreparedPackage(options) {
19391
19391
  import { createConnection, createServer } from "node:net";
19392
19392
  import { join as join5, resolve as resolve3 } from "node:path";
19393
19393
 
19394
- // ../../shared/sdk/dist/chunks/chunk-OV77TRIH.js
19394
+ // ../../shared/sdk/dist/chunks/chunk-QTBB2U7G.js
19395
19395
  var IdSchema = external_exports.string().trim().min(1).max(256);
19396
19396
  var IsoDateSchema = external_exports.iso.datetime({ offset: true });
19397
19397
  var Base64UrlKeyIdSchema = external_exports.string().regex(/^[A-Za-z0-9_-]{22}$/u, "Expected a 16-byte base64url key ID");
@@ -20499,13 +20499,25 @@ var PairingCreateResultSchema = external_exports.discriminatedUnion("ok", [
20499
20499
  external_exports.object({
20500
20500
  ok: external_exports.literal(true),
20501
20501
  code: external_exports.string().min(32).max(256),
20502
- expiresAt: external_exports.number().int().positive()
20502
+ expiresAt: external_exports.number().int().positive(),
20503
+ // Presence advertises the PC-only status endpoint; older Relays omit it.
20504
+ statusId: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/u).optional()
20503
20505
  }),
20504
20506
  external_exports.object({
20505
20507
  ok: external_exports.literal(false),
20506
20508
  error: ProtocolFailureSchema
20507
20509
  })
20508
20510
  ]);
20511
+ var PairingStatusRequestSchema = external_exports.object({
20512
+ userId: IdSchema,
20513
+ machineId: IdSchema,
20514
+ statusId: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/u)
20515
+ });
20516
+ var PairingStatusSchema = external_exports.enum(["pending", "paired", "unavailable"]);
20517
+ var PairingStatusResultSchema = external_exports.discriminatedUnion("ok", [
20518
+ external_exports.object({ ok: external_exports.literal(true), state: PairingStatusSchema }),
20519
+ external_exports.object({ ok: external_exports.literal(false), error: ProtocolFailureSchema })
20520
+ ]);
20509
20521
  var PairingExchangeRequestSchema = external_exports.object({
20510
20522
  code: external_exports.string().min(32).max(256),
20511
20523
  clientId: IdSchema
@@ -21912,7 +21924,7 @@ var AndroidProtocolSchemas = {
21912
21924
  ProtocolError: ProtocolErrorSchema
21913
21925
  };
21914
21926
 
21915
- // ../../shared/sdk/dist/chunks/chunk-CVWUKZX7.js
21927
+ // ../../shared/sdk/dist/chunks/chunk-QN3OD5UE.js
21916
21928
  var OwnerSchema = external_exports.object({ pid: external_exports.number().int().positive(), instanceId: external_exports.uuid().optional() });
21917
21929
  var PeerChatRequestSchema = external_exports.object({
21918
21930
  sessionId: IdSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentknight/client",
3
- "version": "0.9.40",
3
+ "version": "0.9.42",
4
4
  "description": "AgentKnight PC Agent CLI for remote Copilot, PI and ACP 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.5.4",
44
- "@agentknight/sdk": "0.9.40"
44
+ "@agentknight/sdk": "0.9.42"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/qrcode": "1.5.6",