@codexhost/cli-darwin-x64 0.12.0 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,12 +41,13 @@
41
41
  }
42
42
  return payload;
43
43
  }
44
- async function consolePost(path, body = {}) {
44
+ async function consolePost(path, body = {}, signal) {
45
45
  const response = await fetch(path, {
46
46
  method: "POST",
47
47
  credentials: "same-origin",
48
48
  headers: { "content-type": "application/json", [CONSOLE_REQUEST_HEADER]: "1" },
49
- body: JSON.stringify(body)
49
+ body: JSON.stringify(body),
50
+ ...signal ? { signal } : {}
50
51
  });
51
52
  const payload = await response.json().catch(() => null);
52
53
  if (!response.ok) {
@@ -57,8 +58,9 @@
57
58
  }
58
59
  function hostRequestManager() {
59
60
  return {
60
- async sendRequest(method, params) {
61
- const reply = await consolePost("/api/host/request", { method, params });
61
+ async sendRequest(method, params, options) {
62
+ const signal = options instanceof AbortSignal ? options : void 0;
63
+ const reply = await consolePost("/api/host/request", { method, params }, signal);
62
64
  if (reply.error) {
63
65
  throw Object.assign(new Error(reply.error.message), { code: reply.error.code });
64
66
  }
@@ -14661,6 +14663,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14661
14663
  initializeOnly: external_exports.boolean().optional()
14662
14664
  });
14663
14665
 
14666
+ // ../shared-contracts/dist/ids.js
14667
+ var opaqueIdSchema = external_exports.string().refine((value) => value.trim().length > 0, {
14668
+ message: "Identifier must not be empty or whitespace"
14669
+ });
14670
+ var harnessIdSchema = opaqueIdSchema.brand();
14671
+ var hostThreadIdSchema = opaqueIdSchema.brand();
14672
+ var hostTurnIdSchema = opaqueIdSchema.brand();
14673
+ var hostItemIdSchema = opaqueIdSchema.brand();
14674
+ var hostInteractionIdSchema = opaqueIdSchema.brand();
14675
+
14676
+ // ../shared-contracts/dist/thread-prewarm.js
14677
+ var threadPrewarmDiscardParamsSchema = external_exports.object({ threadId: hostThreadIdSchema });
14678
+ var threadPrewarmDiscardResultSchema = external_exports.object({ discarded: external_exports.boolean() });
14679
+
14664
14680
  // ../shared-contracts/dist/console.js
14665
14681
  var consoleAnnouncementSchema = external_exports.strictObject({
14666
14682
  title: external_exports.string().trim().min(1).max(200),
@@ -14674,6 +14690,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14674
14690
  url: external_exports.string().max(2048).regex(/^http:\/\/(?:127\.0\.0\.1|localhost):\d{1,5}\/(?:\?.*)?$/u)
14675
14691
  });
14676
14692
  var CONSOLE_HOST_METHODS = Object.freeze([
14693
+ "codexhost/console/remote-connections",
14694
+ "codexhost/runtime/status",
14695
+ "codexhost/remote/ssh-setup",
14677
14696
  "codexhost/harness/plugins/list",
14678
14697
  "codexhost/harness/display-settings/get",
14679
14698
  "codexhost/harness/display-settings/set",
@@ -14742,16 +14761,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14742
14761
  inactiveMs: external_exports.number().nonnegative()
14743
14762
  }));
14744
14763
 
14745
- // ../shared-contracts/dist/ids.js
14746
- var opaqueIdSchema = external_exports.string().refine((value) => value.trim().length > 0, {
14747
- message: "Identifier must not be empty or whitespace"
14748
- });
14749
- var harnessIdSchema = opaqueIdSchema.brand();
14750
- var hostThreadIdSchema = opaqueIdSchema.brand();
14751
- var hostTurnIdSchema = opaqueIdSchema.brand();
14752
- var hostItemIdSchema = opaqueIdSchema.brand();
14753
- var hostInteractionIdSchema = opaqueIdSchema.brand();
14754
-
14755
14764
  // ../shared-contracts/dist/manual-compaction.js
14756
14765
  var threadManualCompactionStartedSchema = external_exports.strictObject({
14757
14766
  threadId: hostThreadIdSchema,
@@ -15670,11 +15679,54 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15670
15679
  hostname: external_exports.string().min(1).max(1024).refine((value) => !value.startsWith("-") && !/\s/u.test(value)),
15671
15680
  port: external_exports.number().int().min(1).max(65535).nullable(),
15672
15681
  identity: external_exports.string().max(4096).nullable(),
15673
- action: external_exports.enum(["inspect", "install", "update", "repair"]),
15682
+ action: external_exports.enum(["inspect", "install", "update", "repair", "uninstall"]),
15683
+ uninstallPackage: external_exports.boolean().optional(),
15674
15684
  version: external_exports.string().regex(/^\d+\.\d+\.\d+$/u).nullable()
15675
15685
  }).strict();
15676
15686
  var remoteSshSetupResultSchema = external_exports.object({ state: external_exports.enum(["installed", "not-installed"]) }).strict();
15677
15687
 
15688
+ // ../shared-contracts/dist/remote-connections.js
15689
+ var CONSOLE_REMOTE_CONNECTIONS_METHOD = "codexhost/console/remote-connections";
15690
+ var hostId = external_exports.string().min(1).max(1024);
15691
+ var codexSshConnectionSchema = external_exports.object({
15692
+ hostId,
15693
+ displayName: external_exports.string().min(1).max(1024),
15694
+ source: external_exports.enum(["codex-managed", "discovered"]),
15695
+ sshAlias: external_exports.string().max(1024).nullable(),
15696
+ sshHost: external_exports.string().min(1).max(1024),
15697
+ sshPort: external_exports.number().int().min(1).max(65535).nullable(),
15698
+ identity: external_exports.string().max(4096).nullable(),
15699
+ autoConnect: external_exports.boolean(),
15700
+ connectionAnalyticsId: external_exports.string().max(1024).optional()
15701
+ });
15702
+ var codexSshDraftSchema = external_exports.strictObject({
15703
+ displayName: external_exports.string().min(1).max(1024),
15704
+ hostname: external_exports.string().min(1).max(1024),
15705
+ sshPort: external_exports.number().int().min(1).max(65535).nullable(),
15706
+ identity: external_exports.string().max(4096).nullable()
15707
+ });
15708
+ var remoteConnectionsRequestSchema = external_exports.discriminatedUnion("action", [
15709
+ external_exports.strictObject({ action: external_exports.literal("list") }),
15710
+ external_exports.strictObject({
15711
+ action: external_exports.literal("save"),
15712
+ draft: codexSshDraftSchema,
15713
+ previous: codexSshConnectionSchema.nullable()
15714
+ }),
15715
+ external_exports.strictObject({ action: external_exports.literal("remove"), previous: codexSshConnectionSchema }),
15716
+ external_exports.strictObject({ action: external_exports.literal("connect"), hostId, enabled: external_exports.boolean() }),
15717
+ external_exports.strictObject({ action: external_exports.literal("state"), hostId }),
15718
+ external_exports.strictObject({ action: external_exports.literal("runtime"), hostId }),
15719
+ external_exports.strictObject({
15720
+ action: external_exports.literal("update"),
15721
+ hostId: hostId.refine((id) => id !== "local"),
15722
+ version: remoteUpdateParamsSchema.shape.version
15723
+ })
15724
+ ]);
15725
+ var remoteConnectionsReplySchema = external_exports.union([
15726
+ external_exports.strictObject({ result: jsonValueSchema }),
15727
+ external_exports.strictObject({ error: external_exports.strictObject({ code: external_exports.number().int(), message: external_exports.string() }) })
15728
+ ]);
15729
+
15678
15730
  // ../shared-contracts/dist/index.js
15679
15731
  var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
15680
15732
 
@@ -15797,6 +15849,35 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15797
15849
  ["path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }]
15798
15850
  ];
15799
15851
 
15852
+ // ../../node_modules/lucide/dist/esm/icons/eye.mjs
15853
+ var Eye = [
15854
+ [
15855
+ "path",
15856
+ {
15857
+ d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"
15858
+ }
15859
+ ],
15860
+ ["circle", { cx: "12", cy: "12", r: "3" }]
15861
+ ];
15862
+
15863
+ // ../../node_modules/lucide/dist/esm/icons/eye-off.mjs
15864
+ var EyeOff = [
15865
+ [
15866
+ "path",
15867
+ {
15868
+ d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"
15869
+ }
15870
+ ],
15871
+ ["path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }],
15872
+ [
15873
+ "path",
15874
+ {
15875
+ d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"
15876
+ }
15877
+ ],
15878
+ ["path", { d: "m2 2 20 20" }]
15879
+ ];
15880
+
15800
15881
  // ../../node_modules/lucide/dist/esm/icons/ellipsis.mjs
15801
15882
  var Ellipsis = [
15802
15883
  ["circle", { cx: "12", cy: "12", r: "1" }],
@@ -16040,6 +16121,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16040
16121
  info: Info,
16041
16122
  "external-link": ExternalLink,
16042
16123
  refresh: RefreshCw,
16124
+ eye: Eye,
16125
+ "eye-off": EyeOff,
16043
16126
  unavailable: CircleOff,
16044
16127
  alert: TriangleAlert,
16045
16128
  check: CircleCheck,
@@ -16585,8 +16668,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16585
16668
  const installation = client.installation?.bind(client);
16586
16669
  return {
16587
16670
  ...installation ? {
16588
- installation: async (hostId, agent, action) => {
16589
- if (hostId !== "local") {
16671
+ installation: async (hostId2, agent, action) => {
16672
+ if (hostId2 !== "local") {
16590
16673
  throw new Error("Web console installation only supports the local Host");
16591
16674
  }
16592
16675
  return installation({ harnessId: harnessIdSchema.parse(agent), action });
@@ -17392,6 +17475,60 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17392
17475
  }
17393
17476
  };
17394
17477
 
17478
+ // src/console/remote-connections.ts
17479
+ function createConsoleRemoteConnections(manager) {
17480
+ const request = (input, signal) => manager.sendRequest(
17481
+ CONSOLE_REMOTE_CONNECTIONS_METHOD,
17482
+ remoteConnectionsRequestSchema.parse(input),
17483
+ signal
17484
+ );
17485
+ return {
17486
+ ssh: {
17487
+ async list(signal) {
17488
+ return codexSshConnectionSchema.array().parse(await request({ action: "list" }, signal));
17489
+ },
17490
+ async save(draft, previous, signal) {
17491
+ await request({ action: "save", draft, previous }, signal);
17492
+ },
17493
+ async remove(previous, signal) {
17494
+ await request({ action: "remove", previous }, signal);
17495
+ },
17496
+ async connect(hostId2, enabled, signal) {
17497
+ await request({ action: "connect", hostId: hostId2, enabled }, signal);
17498
+ },
17499
+ async state(hostId2, signal) {
17500
+ const result = await request({ action: "state", hostId: hostId2 }, signal);
17501
+ if (!result || typeof result !== "object" || !("state" in result) || typeof result.state !== "string")
17502
+ throw new Error("Invalid Codex connection state");
17503
+ return {
17504
+ state: result.state,
17505
+ error: "error" in result && typeof result.error === "string" ? result.error : null
17506
+ };
17507
+ }
17508
+ },
17509
+ async setup(connection, action, version2, uninstallPackage) {
17510
+ return remoteSshSetupResultSchema.parse(
17511
+ await manager.sendRequest(REMOTE_SSH_SETUP_METHOD, {
17512
+ hostname: connection.sshAlias ?? connection.sshHost,
17513
+ port: connection.sshPort,
17514
+ identity: connection.identity,
17515
+ action,
17516
+ version: version2 ?? null,
17517
+ ...action === "uninstall" ? { uninstallPackage: uninstallPackage ?? false } : {}
17518
+ })
17519
+ );
17520
+ },
17521
+ async runtime(hostId2) {
17522
+ return runtimeStatusSchema.parse(
17523
+ await (hostId2 === "local" ? manager.sendRequest(RUNTIME_STATUS_METHOD, {}) : request({ action: "runtime", hostId: hostId2 }))
17524
+ );
17525
+ },
17526
+ async update(hostId2, version2) {
17527
+ return runtimeStatusSchema.parse(await request({ action: "update", hostId: hostId2, version: version2 }));
17528
+ }
17529
+ };
17530
+ }
17531
+
17395
17532
  // src/renderer-request-sender.ts
17396
17533
  var RendererMethodUnavailableError = class extends Error {
17397
17534
  constructor(method, cause) {
@@ -17871,6 +18008,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17871
18008
  color: var(--settings-muted);
17872
18009
  font-variant-numeric: tabular-nums;
17873
18010
  }
18011
+ /* Email privacy belongs to the identity column, so it sits beside the account count on the
18012
+ left; quota controls (used/remaining, refresh) stay together on the right. */
18013
+ .settings-account-privacy-toggle {
18014
+ margin-left: -8px;
18015
+ }
18016
+ .settings-account-privacy-toggle[aria-pressed="true"] {
18017
+ color: var(--settings-text);
18018
+ background: var(--settings-active);
18019
+ }
17874
18020
  .settings-account-search {
17875
18021
  display: flex;
17876
18022
  align-items: center;
@@ -17940,10 +18086,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17940
18086
  background: light-dark(#f8f8fa, #202024);
17941
18087
  font-size: 11px;
17942
18088
  font-weight: 500;
17943
- text-align: left;
17944
- }
17945
- .settings-account-table th:last-child {
17946
- text-align: right;
18089
+ text-align: center;
17947
18090
  }
17948
18091
  .settings-account-table td {
17949
18092
  padding: 17px 16px;
@@ -18263,26 +18406,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18263
18406
  border: 0;
18264
18407
  }
18265
18408
  .settings-account-person-cell,
18266
- .settings-account-management-cell,
18409
+ .settings-account-usage-cell[colspan="2"],
18267
18410
  .settings-account-usage-cell--message,
18268
18411
  .settings-account-balance-cell {
18269
18412
  grid-column: 1 / -1;
18270
18413
  }
18271
- .settings-account-table .settings-account-row > td.settings-account-management-cell--empty {
18272
- display: none;
18273
- }
18274
- .settings-account-row--targets {
18275
- position: relative;
18276
- }
18277
- .settings-account-row--targets .settings-account-person-cell {
18278
- padding-right: 32px;
18279
- }
18280
- .settings-account-table .settings-account-row--targets > td.settings-account-management-cell {
18281
- position: absolute;
18282
- top: 14px;
18283
- right: 12px;
18284
- width: auto;
18285
- }
18286
18414
  .settings-account-email {
18287
18415
  font-size: 13px;
18288
18416
  }
@@ -18300,32 +18428,57 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18300
18428
  }
18301
18429
  }
18302
18430
 
18303
- /* Small Harness target mark: muted when nothing is copied, full-strength with a dot when it is. */
18304
- .settings-account-harness-target {
18305
- position: relative;
18306
- width: 26px;
18307
- height: 28px;
18308
- color: var(--settings-subtle);
18431
+ /* Identity extras: reset cards and the Pi import chip share one wrapping line. */
18432
+ .settings-account-row__extras {
18433
+ display: flex;
18434
+ flex-wrap: wrap;
18435
+ align-items: center;
18436
+ gap: 4px 10px;
18437
+ margin-top: 6px;
18438
+ }
18439
+ /* Pi import chip: dashed "Import into Pi" until copied, then the entry name with a check. */
18440
+ .settings-account-pi-import {
18441
+ display: inline-flex;
18442
+ align-items: center;
18443
+ gap: 5px;
18444
+ min-height: 24px;
18445
+ padding: 2px 8px;
18446
+ color: var(--settings-muted);
18447
+ font: inherit;
18448
+ font-size: 11px;
18449
+ white-space: nowrap;
18450
+ background: transparent;
18451
+ border: 1px dashed var(--settings-border);
18452
+ border-radius: 6px;
18453
+ cursor: pointer;
18309
18454
  }
18310
- .settings-account-harness-target:hover:not(:disabled) {
18455
+ .settings-account-pi-import:hover:not(:disabled) {
18311
18456
  color: var(--settings-text);
18457
+ border-style: solid;
18458
+ background: var(--settings-hover);
18312
18459
  }
18313
- .settings-account-harness-target:disabled {
18314
- opacity: 0.4;
18460
+ .settings-account-pi-import:disabled {
18461
+ opacity: 0.45;
18315
18462
  cursor: default;
18316
18463
  }
18317
- .settings-account-harness-target[data-state] {
18464
+ .settings-account-pi-import[data-state="imported"] {
18318
18465
  color: var(--settings-text);
18466
+ background: var(--settings-inset);
18467
+ border-style: solid;
18319
18468
  }
18320
- .settings-account-harness-target[data-state]::after {
18321
- content: "";
18322
- position: absolute;
18323
- top: 5px;
18324
- right: 3px;
18325
- width: 6px;
18326
- height: 6px;
18327
- border-radius: 50%;
18328
- background: var(--settings-text);
18469
+ .settings-account-pi-import code {
18470
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
18471
+ font-size: 11px;
18472
+ }
18473
+ .settings-account-pi-import > svg {
18474
+ flex: none;
18475
+ }
18476
+ .settings-account-pi-import[data-state="imported"] > svg:last-child {
18477
+ color: light-dark(#2f7d4f, #7fd19b);
18478
+ }
18479
+ .settings-account-pi-import:focus-visible {
18480
+ outline: 2px solid var(--settings-focus);
18481
+ outline-offset: 1px;
18329
18482
  }
18330
18483
  /* Dedicated "imported into Pi" section: same card language as the account table, lighter header. */
18331
18484
  .settings-pi-accounts {
@@ -18336,18 +18489,67 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18336
18489
  }
18337
18490
  .settings-pi-accounts__header {
18338
18491
  display: flex;
18492
+ flex-wrap: wrap;
18339
18493
  align-items: center;
18340
- gap: 8px;
18494
+ gap: 8px 12px;
18341
18495
  padding: 0 2px 12px;
18342
18496
  font-size: 13px;
18343
18497
  }
18344
- .settings-pi-accounts__header > svg {
18498
+ .settings-pi-accounts__fold {
18499
+ display: inline-flex;
18500
+ align-items: center;
18501
+ gap: 8px;
18502
+ padding: 4px 0;
18503
+ color: var(--settings-text);
18504
+ font: inherit;
18505
+ background: transparent;
18506
+ border: 0;
18507
+ cursor: pointer;
18508
+ }
18509
+ .settings-pi-accounts__fold > svg {
18345
18510
  flex: none;
18346
18511
  }
18347
- .settings-pi-accounts__header > :last-child {
18512
+ .settings-pi-accounts__fold > svg:first-child {
18513
+ color: var(--settings-muted);
18514
+ transition: transform 120ms ease;
18515
+ }
18516
+ .settings-pi-accounts__fold[aria-expanded="true"] > svg:first-child {
18517
+ transform: rotate(90deg);
18518
+ }
18519
+ .settings-pi-accounts__count {
18348
18520
  color: var(--settings-muted);
18349
18521
  font-variant-numeric: tabular-nums;
18350
18522
  }
18523
+ .settings-pi-accounts__hint {
18524
+ margin-right: auto;
18525
+ color: var(--settings-muted);
18526
+ font-size: 12px;
18527
+ }
18528
+ .settings-pi-accounts__import {
18529
+ display: inline-flex;
18530
+ align-items: center;
18531
+ gap: 6px;
18532
+ min-height: 30px;
18533
+ padding: 4px 10px;
18534
+ color: var(--settings-text);
18535
+ font: inherit;
18536
+ font-size: 12px;
18537
+ background: light-dark(#ffffff, #232326);
18538
+ border: 1px solid var(--settings-border);
18539
+ border-radius: 7px;
18540
+ cursor: pointer;
18541
+ }
18542
+ .settings-pi-accounts__import:hover:not(:disabled) {
18543
+ background: var(--settings-hover);
18544
+ }
18545
+ .settings-pi-accounts__import:disabled {
18546
+ opacity: 0.45;
18547
+ cursor: default;
18548
+ }
18549
+ .settings-pi-accounts__import[hidden],
18550
+ .settings-pi-accounts__card[hidden] {
18551
+ display: none;
18552
+ }
18351
18553
  .settings-pi-accounts__card {
18352
18554
  container-type: inline-size;
18353
18555
  overflow: hidden;
@@ -18484,6 +18686,54 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18484
18686
  color: var(--settings-muted);
18485
18687
  font-size: 12px;
18486
18688
  }
18689
+ .settings-credential-dialog__sources {
18690
+ display: grid;
18691
+ gap: 8px;
18692
+ margin: 0;
18693
+ padding: 0;
18694
+ border: 0;
18695
+ }
18696
+ .settings-credential-dialog__sources > legend {
18697
+ margin-bottom: 6px;
18698
+ padding: 0;
18699
+ color: var(--settings-muted);
18700
+ font-size: 12px;
18701
+ }
18702
+ .settings-credential-dialog__source {
18703
+ display: flex;
18704
+ align-items: center;
18705
+ gap: 10px;
18706
+ padding: 9px 12px;
18707
+ border: 1px solid var(--settings-border);
18708
+ border-radius: 8px;
18709
+ cursor: pointer;
18710
+ }
18711
+ .settings-credential-dialog__source:has(input:checked) {
18712
+ border-color: var(--settings-focus);
18713
+ background: var(--settings-hover);
18714
+ }
18715
+ .settings-credential-dialog__source > span {
18716
+ display: grid;
18717
+ gap: 2px;
18718
+ min-width: 0;
18719
+ }
18720
+ .settings-credential-dialog__source strong {
18721
+ font-size: 13px;
18722
+ font-weight: 500;
18723
+ overflow-wrap: anywhere;
18724
+ }
18725
+ .settings-credential-dialog__source > span > span {
18726
+ color: var(--settings-muted);
18727
+ font-size: 12px;
18728
+ }
18729
+ .settings-credential-dialog .settings-credential-dialog__source input {
18730
+ flex: none;
18731
+ width: auto;
18732
+ min-height: 0;
18733
+ margin: 0;
18734
+ padding: 0;
18735
+ accent-color: var(--settings-focus);
18736
+ }
18487
18737
  .settings-credential-dialog input {
18488
18738
  width: 100%;
18489
18739
  box-sizing: border-box;
@@ -18530,9 +18780,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18530
18780
  }
18531
18781
 
18532
18782
  @media (forced-colors: active) {
18533
- .settings-account-harness-target[data-state]::after {
18534
- background: Highlight;
18535
- }
18536
18783
  .settings-account-usage__bar {
18537
18784
  border: 1px solid CanvasText;
18538
18785
  }
@@ -18587,9 +18834,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18587
18834
 
18588
18835
  // src/settings/credential-import-messages.ts
18589
18836
  var credentialImportEnglish = {
18590
- column: "Use in Harness",
18591
18837
  sectionTitle: "Accounts in Pi",
18592
- sectionEmpty: "No logins in Pi yet. Use the Pi mark on a Codex or Grok account above to import one.",
18838
+ sectionHint: "Copy a local Codex or Grok login into a Pi model entry.",
18839
+ sectionImport: "Import account",
18840
+ sectionEmpty: "No logins in Pi yet. Use \u201CImport account\u201D, or \u201CImport into Pi\u201D on a Codex or Grok account above.",
18593
18841
  rowReimport: "Copy again",
18594
18842
  rowRemove: "Remove",
18595
18843
  othersTitle: "Pi's own logins",
@@ -18602,6 +18850,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18602
18850
  entry: "Entry in Pi",
18603
18851
  name: "Model entry name",
18604
18852
  source: "Source account",
18853
+ sourceCurrent: "Current login",
18854
+ sourceImported: "Already in Pi as {name}/\u2026; another entry can be added",
18605
18855
  preview: "Creates {name}/\u2026 in Pi. Existing Provider configurations remain unchanged.",
18606
18856
  warning: "Copies the current authorization once; it does not synchronize logins or add quota. Pi and the original app may refresh the same token, which can require signing in again. Usage follows this Provider's billing rules.",
18607
18857
  confirm: "Confirm import",
@@ -18617,9 +18867,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18617
18867
  close: "Done"
18618
18868
  };
18619
18869
  var credentialImportChinese = {
18620
- column: "\u7528\u4E8E Harness",
18621
18870
  sectionTitle: "Pi \u4E2D\u7684\u8D26\u53F7",
18622
- sectionEmpty: "Pi \u4E2D\u8FD8\u6CA1\u6709\u767B\u5F55\u3002\u70B9\u51FB\u4E0A\u65B9 Codex \u6216 Grok \u8D26\u53F7\u884C\u7684 Pi \u56FE\u6807\u5373\u53EF\u5BFC\u5165\u3002",
18871
+ sectionHint: "\u628A Codex\u3001Grok \u7684\u672C\u5730\u767B\u5F55\u590D\u5236\u6210 Pi \u7684\u6A21\u578B\u5165\u53E3",
18872
+ sectionImport: "\u5BFC\u5165\u8D26\u53F7",
18873
+ sectionEmpty: "Pi \u4E2D\u8FD8\u6CA1\u6709\u767B\u5F55\u3002\u70B9\u51FB\u300C\u5BFC\u5165\u8D26\u53F7\u300D\uFF0C\u6216\u4E0A\u65B9 Codex\u3001Grok \u8D26\u53F7\u884C\u7684\u300C\u5BFC\u5165\u5230 Pi\u300D\u5373\u53EF\u5BFC\u5165\u3002",
18623
18874
  rowReimport: "\u91CD\u65B0\u5BFC\u5165",
18624
18875
  rowRemove: "\u79FB\u9664",
18625
18876
  othersTitle: "Pi \u81EA\u6709\u914D\u7F6E",
@@ -18632,6 +18883,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18632
18883
  entry: "Pi \u4E2D\u7684\u5165\u53E3",
18633
18884
  name: "\u6A21\u578B\u5165\u53E3\u540D\u79F0",
18634
18885
  source: "\u6765\u6E90\u8D26\u53F7",
18886
+ sourceCurrent: "\u5F53\u524D\u767B\u5F55",
18887
+ sourceImported: "\u5DF2\u5BFC\u5165\u4E3A {name}/\u2026\uFF0C\u53EF\u518D\u65B0\u589E\u4E00\u4E2A\u5165\u53E3",
18635
18888
  preview: "\u5C06\u5728 Pi \u4E2D\u65B0\u589E {name}/\u2026\uFF0C\u4FDD\u7559\u5168\u90E8\u5DF2\u6709 Provider \u914D\u7F6E\u3002",
18636
18889
  warning: "\u4EC5\u590D\u5236\u5F53\u524D\u6388\u6743\uFF0C\u4E0D\u6301\u7EED\u540C\u6B65\u767B\u5F55\uFF0C\u4E5F\u4E0D\u589E\u52A0\u989D\u5EA6\u3002Pi \u4E0E\u539F\u751F\u5DE5\u5177\u5206\u522B\u5237\u65B0\u540C\u4E00\u51ED\u8BC1\u65F6\uFF0C\u53EF\u80FD\u9700\u8981\u91CD\u65B0\u767B\u5F55\u3002\u5B9E\u9645\u7528\u91CF\u9075\u5FAA\u8BE5 Provider \u7684\u8BA1\u8D39\u89C4\u5219\u3002",
18637
18890
  confirm: "\u786E\u8BA4\u5BFC\u5165",
@@ -18805,6 +19058,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18805
19058
  accountCreditsFailed: "Could not load limits",
18806
19059
  accountCreditsRetry: "Retry",
18807
19060
  accountCreditsRefresh: "Refresh limits",
19061
+ accountEmailsHide: "Hide emails",
19062
+ accountEmailsShow: "Show emails",
18808
19063
  accountLoadFailed: "Could not load Codex Accounts.",
18809
19064
  accountCreditsUsed: "Used",
18810
19065
  accountCreditsResetAt: "Quota resets: {time}",
@@ -19075,6 +19330,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19075
19330
  accountCreditsFailed: "\u989D\u5EA6\u8BFB\u53D6\u5931\u8D25",
19076
19331
  accountCreditsRetry: "\u91CD\u8BD5",
19077
19332
  accountCreditsRefresh: "\u5237\u65B0\u989D\u5EA6",
19333
+ accountEmailsHide: "\u9690\u85CF\u90AE\u7BB1",
19334
+ accountEmailsShow: "\u663E\u793A\u90AE\u7BB1",
19078
19335
  accountLoadFailed: "\u65E0\u6CD5\u52A0\u8F7D Codex \u8D26\u53F7\u3002",
19079
19336
  accountCreditsUsed: "\u5DF2\u7528",
19080
19337
  accountCreditsResetAt: "\u989D\u5EA6\u91CD\u7F6E\u65F6\u95F4\uFF1A{time}",
@@ -19273,6 +19530,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19273
19530
 
19274
19531
  // src/settings/remote-failure-messages.ts
19275
19532
  var REMOTE_FAILURE_CHINESE = Object.freeze({
19533
+ "Remote connection management is unavailable. Restart Codex through codexhost": "\u8FDC\u7A0B\u8FDE\u63A5\u7BA1\u7406\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u901A\u8FC7 codexhost \u91CD\u65B0\u542F\u52A8 Codex",
19534
+ "Remote connection request timed out. Refresh before retrying": "\u8FDC\u7A0B\u8FDE\u63A5\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u5237\u65B0\u540E\u518D\u8BD5",
19535
+ "Invalid remote connection request": "\u8FDC\u7A0B\u8FDE\u63A5\u8BF7\u6C42\u65E0\u6548",
19276
19536
  // SSH helper: installing, updating and repairing over SSH.
19277
19537
  "Invalid SSH address or port": "SSH \u5730\u5740\u6216\u7AEF\u53E3\u65E0\u6548",
19278
19538
  "Install requires a published stable version": "\u53EA\u80FD\u5B89\u88C5\u5DF2\u53D1\u5E03\u7684\u6B63\u5F0F\u7248\u672C",
@@ -19282,7 +19542,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19282
19542
  "This release is not published on npm, or the remote computer cannot reach the registry": "\u8BE5\u7248\u672C\u5C1A\u672A\u53D1\u5E03\u5230 npm\uFF0C\u6216\u8FDC\u7A0B\u7535\u8111\u65E0\u6CD5\u8BBF\u95EE npm \u4ED3\u5E93",
19283
19543
  "codexhost was installed but is not on the remote PATH. Update this remote manually": "codexhost \u5DF2\u5B89\u88C5\uFF0C\u4F46\u4E0D\u5728\u8FDC\u7A0B\u7535\u8111\u7684 PATH \u4E2D\uFF0C\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u624B\u52A8\u66F4\u65B0",
19284
19544
  "Only Mac and Linux remote computers are supported": "\u53EA\u652F\u6301 Mac \u548C Linux \u8FDC\u7A0B\u7535\u8111",
19285
- "Node.js, npm or Codex CLI was not found over SSH. Install them on the remote computer and make sure non-interactive SSH sessions have them on PATH": "\u901A\u8FC7 SSH \u627E\u4E0D\u5230 Node.js\u3001npm \u6216 Codex CLI\u3002\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u5B89\u88C5\u5B83\u4EEC\uFF0C\u5E76\u786E\u4FDD\u975E\u4EA4\u4E92\u5F0F SSH \u4F1A\u8BDD\u7684 PATH \u4E2D\u5305\u542B\u5B83\u4EEC",
19545
+ "Node.js was not detected over SSH. Install it on the remote computer or add it to the login shell PATH, then retry": "\u901A\u8FC7 SSH \u672A\u68C0\u6D4B\u5230 Node.js\u3002\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u5B89\u88C5\uFF0C\u6216\u5C06\u5DF2\u5B89\u88C5\u7684 Node.js \u52A0\u5165\u767B\u5F55 shell \u7684 PATH \u540E\u91CD\u8BD5",
19546
+ "npm was not detected over SSH. Install it on the remote computer or add it to the login shell PATH, then retry": "\u901A\u8FC7 SSH \u672A\u68C0\u6D4B\u5230 npm\u3002\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u5B89\u88C5\uFF0C\u6216\u5C06\u5DF2\u5B89\u88C5\u7684 npm \u52A0\u5165\u767B\u5F55 shell \u7684 PATH \u540E\u91CD\u8BD5",
19547
+ "Codex CLI was not detected over SSH. Install the command-line tool on the remote computer or add it to the login shell PATH, then retry. Codex Desktop is not required": "\u901A\u8FC7 SSH \u672A\u68C0\u6D4B\u5230 Codex CLI\uFF08\u547D\u4EE4\u884C\u5DE5\u5177\uFF09\u3002\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u5B89\u88C5\uFF0C\u6216\u5C06\u5DF2\u5B89\u88C5\u7684 Codex CLI \u52A0\u5165\u767B\u5F55 shell \u7684 PATH \u540E\u91CD\u8BD5\u3002\u65E0\u9700\u5B89\u88C5 Codex \u684C\u9762\u7AEF",
19548
+ "codexhost was not detected over SSH. Install it on the remote computer or add it to the login shell PATH, then retry": "\u901A\u8FC7 SSH \u672A\u68C0\u6D4B\u5230 codexhost\u3002\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u5B89\u88C5\uFF0C\u6216\u5C06\u5DF2\u5B89\u88C5\u7684 codexhost \u52A0\u5165\u767B\u5F55 shell \u7684 PATH \u540E\u91CD\u8BD5",
19549
+ "The active codexhost does not match the global npm installation. Uninstall only the remote service, or remove the package manually on the remote computer": "\u5F53\u524D codexhost \u4E0E npm \u5168\u5C40\u5B89\u88C5\u4F4D\u7F6E\u4E0D\u5339\u914D\u3002\u8BF7\u9009\u62E9\u4EC5\u5378\u8F7D\u8FDC\u7A0B\u670D\u52A1\uFF0C\u6216\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u624B\u52A8\u5378\u8F7D\u8F6F\u4EF6\u5305",
19550
+ "The remote service was uninstalled, but removing the codexhost package failed. Check npm permissions on the remote computer and retry": "\u8FDC\u7A0B\u670D\u52A1\u5DF2\u5378\u8F7D\uFF0C\u4F46 codexhost \u8F6F\u4EF6\u5305\u5378\u8F7D\u5931\u8D25\u3002\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u7535\u8111\u4E0A\u7684 npm \u6743\u9650\u540E\u91CD\u8BD5",
19551
+ "Remote Node.js is unsupported. Select Node.js 22.19 or later in the 22.x series, or Node.js 24.x": "\u8FDC\u7A0B Node.js \u7248\u672C\u4E0D\u53D7\u652F\u6301\uFF0C\u8BF7\u9009\u62E9 Node.js 22.19 \u53CA\u4EE5\u4E0A\u7684 22.x \u7248\u672C\uFF0C\u6216 Node.js 24.x",
19286
19552
  "npm installation failed. Check network access and global installation permissions": "npm \u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u548C\u5168\u5C40\u5B89\u88C5\u6743\u9650",
19287
19553
  "Remote service configuration failed. Check Codex CLI and the remote desktop login": "\u8FDC\u7A0B\u670D\u52A1\u914D\u7F6E\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 Codex CLI \u4EE5\u53CA\u8FDC\u7A0B\u7535\u8111\u7684\u684C\u9762\u767B\u5F55\u72B6\u6001",
19288
19554
  "Remote service could not stop. Check the remote service before retrying": "\u8FDC\u7A0B\u670D\u52A1\u65E0\u6CD5\u505C\u6B62\uFF0C\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u670D\u52A1\u540E\u91CD\u8BD5",
@@ -19580,6 +19846,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19580
19846
  }
19581
19847
  const serviceActions = remoteElement(document2, "div", "settings-remote-card__actions");
19582
19848
  if (!row.probing) {
19849
+ if (row.installation === "installed" && !remoteBusy(remote)) {
19850
+ serviceActions.append(command("uninstall", t("\u5378\u8F7D\u8FDC\u7A0B\u670D\u52A1", "Uninstall remote service")));
19851
+ }
19583
19852
  const outdated = !remote && !!row.remoteIssue?.outdated && row.installation !== "not-installed";
19584
19853
  if (outdated) {
19585
19854
  const upgrade = command("upgrade", t("\u66F4\u65B0\u8FDC\u7A0B\u670D\u52A1", "Update remote service"), true);
@@ -19896,6 +20165,61 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19896
20165
  });
19897
20166
  dialog.show(t("\u79FB\u9664", "Remove"), t("\u79FB\u9664\u4E2D\u2026", "Removing\u2026"), true);
19898
20167
  }
20168
+ function openRemoteServiceUninstall(options) {
20169
+ const { t, connection } = options;
20170
+ const dialog = createRemoteDialog(
20171
+ options,
20172
+ t(
20173
+ `\u5378\u8F7D\u201C${connection.displayName}\u201D\u7684\u8FDC\u7A0B\u670D\u52A1\uFF1F`,
20174
+ `Uninstall remote service on "${connection.displayName}"?`
20175
+ )
20176
+ );
20177
+ if (!dialog) return;
20178
+ const document2 = options.container.ownerDocument;
20179
+ dialog.body.append(
20180
+ remoteElement(
20181
+ document2,
20182
+ "p",
20183
+ "",
20184
+ t(
20185
+ "\u5378\u8F7D\u4F1A\u4E2D\u65AD codexhost \u542F\u52A8\u7684\u4F1A\u8BDD\u3002\u5982\u9700\u5728\u8BE5\u7535\u8111\u7EE7\u7EED\u4F7F\u7528\uFF0C\u8BF7\u624B\u52A8\u901A\u8FC7 codexhost \u91CD\u65B0\u542F\u52A8\u3002",
20186
+ "Uninstalling interrupts sessions started through codexhost. To keep using it on that computer, manually relaunch through codexhost."
20187
+ )
20188
+ )
20189
+ );
20190
+ const label = remoteElement(document2, "label", "settings-remote-dialog__package-option");
20191
+ const removePackage = remoteElement(document2, "input");
20192
+ removePackage.type = "checkbox";
20193
+ label.append(
20194
+ removePackage,
20195
+ t("\u540C\u65F6\u5378\u8F7D codexhost \u8F6F\u4EF6\u5305", "Also uninstall the codexhost package")
20196
+ );
20197
+ dialog.body.append(
20198
+ label,
20199
+ remoteElement(
20200
+ document2,
20201
+ "p",
20202
+ "",
20203
+ t(
20204
+ "\u52FE\u9009\u540E\uFF0C\u518D\u6B21\u4F7F\u7528\u524D\u9700\u91CD\u65B0\u5B89\u88C5 codexhost\u3002",
20205
+ "If selected, reinstall codexhost before using it again."
20206
+ )
20207
+ )
20208
+ );
20209
+ dialog.form.addEventListener("submit", (event) => {
20210
+ event.preventDefault();
20211
+ const selected = removePackage.checked;
20212
+ dialog.submit(async () => {
20213
+ removePackage.disabled = true;
20214
+ try {
20215
+ await options.uninstall(selected);
20216
+ } finally {
20217
+ removePackage.disabled = false;
20218
+ }
20219
+ });
20220
+ });
20221
+ dialog.show(t("\u5378\u8F7D", "Uninstall"), t("\u5378\u8F7D\u4E2D\u2026", "Uninstalling\u2026"), true);
20222
+ }
19899
20223
 
19900
20224
  // src/settings/remote-connections-page.ts
19901
20225
  var POLL_INTERVAL_MS = 5e3;
@@ -20024,17 +20348,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20024
20348
  banner.hidden = !loadError;
20025
20349
  bannerText.textContent = loadError ? `${t("\u8BFB\u53D6\u8FDE\u63A5\u5931\u8D25", "Could not load connections")}: ${localizeRemoteFailure(loadError, t)}` : "";
20026
20350
  renderPlaceholder();
20027
- for (const [hostId, card] of cards) {
20028
- if (rows.has(hostId)) continue;
20351
+ for (const [hostId2, card] of cards) {
20352
+ if (rows.has(hostId2)) continue;
20029
20353
  card.element.remove();
20030
- cards.delete(hostId);
20354
+ cards.delete(hostId2);
20031
20355
  }
20032
20356
  const active = focusedElement();
20033
- order.forEach((hostId, index) => {
20034
- const row = rows.get(hostId);
20357
+ order.forEach((hostId2, index) => {
20358
+ const row = rows.get(hostId2);
20035
20359
  if (!row) return;
20036
20360
  const signature = JSON.stringify([row, local]);
20037
- let card = cards.get(hostId);
20361
+ let card = cards.get(hostId2);
20038
20362
  if (card?.signature !== signature) {
20039
20363
  const element = renderRemoteCard(
20040
20364
  document2,
@@ -20046,7 +20370,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20046
20370
  const focusedAction = active && card?.element.contains(active) ? active.dataset.action : void 0;
20047
20371
  if (card) card.element.replaceWith(element);
20048
20372
  card = { element, signature };
20049
- cards.set(hostId, card);
20373
+ cards.set(hostId2, card);
20050
20374
  if (focusedAction) focusAction(element, focusedAction);
20051
20375
  }
20052
20376
  if (list.children[index] !== card.element)
@@ -20058,38 +20382,41 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20058
20382
  target?.focus();
20059
20383
  }
20060
20384
  async function probe(row) {
20061
- const { hostId } = row.connection;
20062
- const token = (probeTokens.get(hostId) ?? 0) + 1;
20063
- probeTokens.set(hostId, token);
20064
- const state = await control.ssh.state(hostId, signal).catch((error51) => ({ state: "unknown", error: remoteErrorMessage(error51) }));
20385
+ const { hostId: hostId2 } = row.connection;
20386
+ const token = (probeTokens.get(hostId2) ?? 0) + 1;
20387
+ probeTokens.set(hostId2, token);
20388
+ const state = await control.ssh.state(hostId2, signal).catch((error51) => ({ state: "unknown", error: remoteErrorMessage(error51) }));
20065
20389
  let remote = null;
20066
20390
  let remoteIssue = null;
20067
20391
  if (state.state === "connected")
20068
- remote = await control.runtime(hostId).catch((error51) => {
20392
+ remote = await control.runtime(hostId2).catch((error51) => {
20069
20393
  remoteIssue = remoteVersionIssue(error51);
20070
20394
  return null;
20071
20395
  });
20396
+ if (signal.aborted || rows.get(hostId2) !== row || probeTokens.get(hostId2) !== token) return;
20072
20397
  let expectedGap = false;
20073
- if (remote && remoteBusy(remote)) restarting.set(hostId, Date.now() + RESTART_GRACE_MS);
20074
- else if (remote || (restarting.get(hostId) ?? 0) <= Date.now()) restarting.delete(hostId);
20398
+ if (remote && remoteBusy(remote)) restarting.set(hostId2, Date.now() + RESTART_GRACE_MS);
20399
+ else if (remote || (restarting.get(hostId2) ?? 0) <= Date.now()) restarting.delete(hostId2);
20075
20400
  else if (row.remote) {
20076
20401
  expectedGap = true;
20077
20402
  remote = { ...row.remote, update: { ...row.remote.update, phase: "restarting" } };
20078
20403
  remoteIssue = null;
20079
20404
  }
20080
- if (remote) installations.set(hostId, "installed");
20081
- else if (!installations.has(hostId) && Date.now() - (inspectFailures.get(hostId) ?? -INSPECT_RETRY_MS) >= INSPECT_RETRY_MS) {
20405
+ if (remote) installations.set(hostId2, "installed");
20406
+ else if (!installations.has(hostId2) && Date.now() - (inspectFailures.get(hostId2) ?? -INSPECT_RETRY_MS) >= INSPECT_RETRY_MS) {
20082
20407
  const inspected = await control.setup(row.connection, "inspect").catch(() => null);
20083
- if (inspected) installations.set(hostId, inspected.state);
20084
- else inspectFailures.set(hostId, Date.now());
20408
+ if (signal.aborted || rows.get(hostId2) !== row || probeTokens.get(hostId2) !== token)
20409
+ return;
20410
+ if (inspected) installations.set(hostId2, inspected.state);
20411
+ else inspectFailures.set(hostId2, Date.now());
20085
20412
  }
20086
- if (signal.aborted || rows.get(hostId) !== row || probeTokens.get(hostId) !== token) return;
20413
+ if (signal.aborted || rows.get(hostId2) !== row || probeTokens.get(hostId2) !== token) return;
20087
20414
  row.probing = false;
20088
20415
  row.state = expectedGap ? "connecting" : state.state;
20089
20416
  row.stateError = expectedGap ? null : state.error;
20090
20417
  row.remote = remote;
20091
20418
  row.remoteIssue = remoteIssue;
20092
- row.installation = installations.get(hostId) ?? "unknown";
20419
+ row.installation = installations.get(hostId2) ?? "unknown";
20093
20420
  render();
20094
20421
  }
20095
20422
  async function refresh(actingHostId, wait = "acting") {
@@ -20129,27 +20456,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20129
20456
  notice: null
20130
20457
  });
20131
20458
  }
20132
- for (const hostId of [...rows.keys()]) {
20133
- if (order.includes(hostId)) continue;
20134
- rows.delete(hostId);
20135
- installations.delete(hostId);
20136
- probeTokens.delete(hostId);
20137
- inspectFailures.delete(hostId);
20138
- restarting.delete(hostId);
20459
+ for (const hostId2 of [...rows.keys()]) {
20460
+ if (order.includes(hostId2)) continue;
20461
+ rows.delete(hostId2);
20462
+ installations.delete(hostId2);
20463
+ probeTokens.delete(hostId2);
20464
+ inspectFailures.delete(hostId2);
20465
+ restarting.delete(hostId2);
20139
20466
  }
20140
20467
  render();
20141
20468
  const probes = [...rows.values()].filter((row) => !row.pending || row.connection.hostId === actingHostId).map((row) => ({ hostId: row.connection.hostId, done: probe(row) }));
20142
20469
  const awaited = wait === "all" ? probes : probes.filter((p) => p.hostId === actingHostId);
20143
20470
  await Promise.all([localTask, ...awaited.map((p) => p.done)]);
20144
20471
  }
20145
- async function act(row, action, label, operation) {
20146
- const { hostId } = row.connection;
20472
+ async function act(row, action, label, operation, rethrow = false) {
20473
+ const { hostId: hostId2 } = row.connection;
20474
+ probeTokens.set(hostId2, (probeTokens.get(hostId2) ?? 0) + 1);
20147
20475
  row.pending = label;
20148
20476
  row.notice = null;
20149
20477
  render();
20150
20478
  try {
20151
20479
  const done = await operation();
20152
- await refresh(hostId);
20480
+ await refresh(hostId2);
20153
20481
  if (typeof done === "string") {
20154
20482
  const notice = { tone: "success", text: done };
20155
20483
  row.notice = notice;
@@ -20161,14 +20489,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20161
20489
  }
20162
20490
  } catch (error51) {
20163
20491
  row.notice = { tone: "danger", text: remoteErrorMessage(error51) };
20164
- installations.delete(hostId);
20165
- inspectFailures.delete(hostId);
20166
- void refresh(hostId);
20492
+ installations.delete(hostId2);
20493
+ inspectFailures.delete(hostId2);
20494
+ void refresh(hostId2);
20495
+ if (rethrow) throw error51;
20167
20496
  } finally {
20168
20497
  row.pending = null;
20169
20498
  if (!signal.aborted) {
20170
20499
  render();
20171
- const card = cards.get(hostId)?.element;
20500
+ const card = cards.get(hostId2)?.element;
20172
20501
  if (card && !focusedElement()) focusAction(card, action);
20173
20502
  }
20174
20503
  }
@@ -20193,9 +20522,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20193
20522
  function handle(row, action) {
20194
20523
  if (row.pending) return;
20195
20524
  const { connection } = row;
20196
- const { hostId } = connection;
20525
+ const { hostId: hostId2 } = connection;
20197
20526
  const setConnected = async (enabled) => {
20198
- const request = control.ssh.connect(hostId, enabled, signal);
20527
+ const request = control.ssh.connect(hostId2, enabled, signal);
20199
20528
  request.catch(() => void 0);
20200
20529
  await Promise.race([
20201
20530
  request,
@@ -20203,8 +20532,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20203
20532
  ]);
20204
20533
  };
20205
20534
  const reconnect = async () => {
20206
- installations.delete(hostId);
20207
- inspectFailures.delete(hostId);
20535
+ installations.delete(hostId2);
20536
+ inspectFailures.delete(hostId2);
20208
20537
  await setConnected(true);
20209
20538
  };
20210
20539
  switch (action) {
@@ -20239,8 +20568,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20239
20568
  break;
20240
20569
  case "recheck":
20241
20570
  void act(row, action, t("\u6B63\u5728\u91CD\u65B0\u68C0\u6D4B\u2026", "Checking again\u2026"), async () => {
20242
- installations.delete(hostId);
20243
- inspectFailures.delete(hostId);
20571
+ installations.delete(hostId2);
20572
+ inspectFailures.delete(hostId2);
20244
20573
  });
20245
20574
  break;
20246
20575
  case "install": {
@@ -20285,6 +20614,47 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20285
20614
  }
20286
20615
  );
20287
20616
  break;
20617
+ case "uninstall":
20618
+ if (row.installation !== "installed" || remoteBusy(row.remote)) break;
20619
+ openRemoteServiceUninstall({
20620
+ container: root,
20621
+ signal,
20622
+ t,
20623
+ connection,
20624
+ async uninstall(removePackage) {
20625
+ if (row.pending || remoteBusy(row.remote))
20626
+ throw new Error(
20627
+ t(
20628
+ "\u8FDC\u7A0B\u670D\u52A1\u6B63\u5728\u6267\u884C\u5176\u4ED6\u64CD\u4F5C\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
20629
+ "Another remote operation is active; retry shortly"
20630
+ )
20631
+ );
20632
+ await act(
20633
+ row,
20634
+ action,
20635
+ t("\u6B63\u5728\u5378\u8F7D\u8FDC\u7A0B\u670D\u52A1\u2026", "Uninstalling remote service\u2026"),
20636
+ async () => {
20637
+ await control.ssh.connect(hostId2, false, signal);
20638
+ await control.setup(connection, "uninstall", void 0, removePackage);
20639
+ installations.set(hostId2, "not-installed");
20640
+ inspectFailures.delete(hostId2);
20641
+ restarting.delete(hostId2);
20642
+ row.remote = null;
20643
+ row.remoteIssue = null;
20644
+ row.stateError = null;
20645
+ return removePackage ? t(
20646
+ "\u8FDC\u7A0B\u670D\u52A1\u548C codexhost \u8F6F\u4EF6\u5305\u5DF2\u5378\u8F7D",
20647
+ "Remote service and codexhost package uninstalled"
20648
+ ) : t(
20649
+ "\u8FDC\u7A0B\u670D\u52A1\u5DF2\u5378\u8F7D\uFF0Ccodexhost \u8F6F\u4EF6\u5305\u5DF2\u4FDD\u7559",
20650
+ "Remote service uninstalled; codexhost package kept"
20651
+ );
20652
+ },
20653
+ true
20654
+ );
20655
+ }
20656
+ });
20657
+ break;
20288
20658
  case "update": {
20289
20659
  const target = row.remote ? remoteMaintenanceTarget(local, row.remote) : null;
20290
20660
  if (!target) break;
@@ -20294,7 +20664,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20294
20664
  action,
20295
20665
  restart ? t("\u6B63\u5728\u91CD\u542F\u8FDC\u7A0B\u670D\u52A1\u2026", "Restarting remote service\u2026") : t("\u6B63\u5728\u63D0\u4EA4\u66F4\u65B0\u2026", "Starting update\u2026"),
20296
20666
  async () => {
20297
- await control.update(hostId, target);
20667
+ await control.update(hostId2, target);
20298
20668
  await setConnected(true);
20299
20669
  }
20300
20670
  );
@@ -20766,7 +21136,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20766
21136
  }
20767
21137
 
20768
21138
  // src/settings/harness-installation-panel.ts
20769
- function createHarnessInstallationPanel(document2, agent, hostId, messages, copy, installation) {
21139
+ function createHarnessInstallationPanel(document2, agent, hostId2, messages, copy, installation) {
20770
21140
  const zh = messages.locale === "zh-CN";
20771
21141
  const guide = harnessInstallationGuide(agent, messages.locale);
20772
21142
  const panel = document2.createElement("div");
@@ -20776,7 +21146,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20776
21146
  p.textContent = text;
20777
21147
  panel.append(p);
20778
21148
  };
20779
- if (hostId !== "local") {
21149
+ if (hostId2 !== "local") {
20780
21150
  paragraph(zh ? "\u8BF7\u5728\u8FDC\u7A0B Host \u4E0A\u5B89\u88C5\u3002" : "Install on the remote Host.");
20781
21151
  }
20782
21152
  if (guide.before) paragraph(guide.before);
@@ -20969,9 +21339,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20969
21339
  }
20970
21340
  };
20971
21341
  update.addEventListener("click", () => void run("update"));
20972
- signal.addEventListener("abort", render, { once: true });
21342
+ const owner = document2.defaultView;
21343
+ let checkFrame = null;
21344
+ let checkTimer = null;
21345
+ signal.addEventListener(
21346
+ "abort",
21347
+ () => {
21348
+ if (checkFrame !== null) owner?.cancelAnimationFrame(checkFrame);
21349
+ if (checkTimer !== null) owner?.clearTimeout(checkTimer);
21350
+ render();
21351
+ },
21352
+ { once: true }
21353
+ );
21354
+ status.textContent = messages.harnessVersionChecking;
20973
21355
  render();
20974
- void run("check");
21356
+ if (!signal.aborted && owner?.requestAnimationFrame) {
21357
+ checkFrame = owner.requestAnimationFrame(() => {
21358
+ checkFrame = null;
21359
+ if (signal.aborted) return;
21360
+ checkTimer = owner.setTimeout(() => {
21361
+ checkTimer = null;
21362
+ void run("check");
21363
+ }, 0);
21364
+ });
21365
+ } else {
21366
+ void run("check");
21367
+ }
20975
21368
  return panel;
20976
21369
  }
20977
21370
 
@@ -20996,11 +21389,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20996
21389
  if (availability === "notInstalled") return "setup";
20997
21390
  return "failed";
20998
21391
  }
20999
- function diagnosticText(hostId, item) {
21392
+ function diagnosticText(hostId2, item) {
21000
21393
  const error51 = item.error;
21001
21394
  return [
21002
21395
  "codexhost connection diagnostics",
21003
- `host: ${hostId}`,
21396
+ `host: ${hostId2}`,
21004
21397
  `agent: ${item.name}`,
21005
21398
  `status: ${item.availability}`,
21006
21399
  ...error51 ? [
@@ -21035,10 +21428,10 @@ ${error51.stderrTail}`] : []
21035
21428
  () => showCopyButtonFeedback(button2, messages.connectionCopyFailed, restoreLabel)
21036
21429
  );
21037
21430
  }
21038
- function connectionHostName(hostId, messages) {
21039
- if (hostId === "local") return messages.connectionLocalHost;
21040
- const separator = hostId.lastIndexOf(":");
21041
- const encodedName = separator >= 0 ? hostId.slice(separator + 1) : hostId;
21431
+ function connectionHostName(hostId2, messages) {
21432
+ if (hostId2 === "local") return messages.connectionLocalHost;
21433
+ const separator = hostId2.lastIndexOf(":");
21434
+ const encodedName = separator >= 0 ? hostId2.slice(separator + 1) : hostId2;
21042
21435
  try {
21043
21436
  return decodeURIComponent(encodedName);
21044
21437
  } catch {
@@ -21233,7 +21626,7 @@ ${error51.stderrTail}`] : []
21233
21626
  header.append(identity, status);
21234
21627
  return header;
21235
21628
  }
21236
- function renderConnectionInspector(document2, inspector, item, hostId, messages, diagnostics, existingLaunchControls, refresh) {
21629
+ function renderConnectionInspector(document2, inspector, item, hostId2, messages, diagnostics, existingLaunchControls, refresh) {
21237
21630
  inspector.replaceChildren(createInspectorHeader(document2, item, messages));
21238
21631
  const body = document2.createElement("div");
21239
21632
  body.className = "settings-connection-inspector__body";
@@ -21263,7 +21656,7 @@ ${error51.stderrTail}`] : []
21263
21656
  createHarnessInstallationPanel(
21264
21657
  document2,
21265
21658
  item.agentSnapshot.agent,
21266
- hostId,
21659
+ hostId2,
21267
21660
  messages,
21268
21661
  (button2, command, label) => copyDiagnosticsToClipboard(document2, button2, command, messages, label),
21269
21662
  item.install ? {
@@ -21302,7 +21695,7 @@ ${error51.stderrTail}`] : []
21302
21695
  copy.type = "button";
21303
21696
  copy.className = "settings-command-button settings-command-button--secondary";
21304
21697
  setCopyButtonLabel(copy, messages.connectionCopyDetails);
21305
- const report = diagnosticText(hostId, item);
21698
+ const report = diagnosticText(hostId2, item);
21306
21699
  copy.addEventListener("click", () => {
21307
21700
  copyDiagnosticsToClipboard(document2, copy, report, messages, messages.connectionCopyDetails);
21308
21701
  });
@@ -21355,10 +21748,10 @@ ${error51.stderrTail}`] : []
21355
21748
  const agent = item.agentSnapshot?.agent;
21356
21749
  const getLaunchSettings = diagnostics?.getLaunchSettings?.bind(diagnostics);
21357
21750
  const setLaunchSettings = diagnostics?.setLaunchSettings?.bind(diagnostics);
21358
- if (hostId === "local" && (agent === "zcode" || agent === "workbuddy") && getLaunchSettings && setLaunchSettings) {
21751
+ if (hostId2 === "local" && (agent === "zcode" || agent === "workbuddy") && getLaunchSettings && setLaunchSettings) {
21359
21752
  launchControls = existingLaunchControls ?? createHarnessLaunchControls(document2, messages, agent, {
21360
- get: () => getLaunchSettings(hostId, agent),
21361
- set: (path) => setLaunchSettings(hostId, agent, path)
21753
+ get: () => getLaunchSettings(hostId2, agent),
21754
+ set: (path) => setLaunchSettings(hostId2, agent, path)
21362
21755
  });
21363
21756
  body.append(launchControls);
21364
21757
  }
@@ -21412,7 +21805,7 @@ ${error51.stderrTail}`] : []
21412
21805
  button2.addEventListener("click", onReset);
21413
21806
  return button2;
21414
21807
  }
21415
- function createConnectionsSettingsPage(messages, getDiagnostics, groupPreference = getSharedAgentGroupPreferenceStore()) {
21808
+ function createConnectionsSettingsPage(messages, getDiagnostics, groupPreference = getSharedAgentGroupPreferenceStore(), getRemoteConnections = () => null) {
21416
21809
  return Object.freeze({
21417
21810
  id: "connections",
21418
21811
  label: messages.pageLabels.connections,
@@ -21447,8 +21840,33 @@ ${error51.stderrTail}`] : []
21447
21840
  let launchControls = null;
21448
21841
  const versionPanels = /* @__PURE__ */ new Map();
21449
21842
  const updatingVersions = /* @__PURE__ */ new Set();
21843
+ const hostNames = /* @__PURE__ */ new Map();
21450
21844
  let latestSnapshot = null;
21451
21845
  let disposeHostScroller = () => void 0;
21846
+ let disposed = false;
21847
+ let renderQueued = false;
21848
+ let renderFrame = null;
21849
+ const scheduleRender = () => {
21850
+ if (disposed || context.signal.aborted || renderQueued) return;
21851
+ renderQueued = true;
21852
+ const flush = () => {
21853
+ renderFrame = null;
21854
+ renderQueued = false;
21855
+ if (!disposed && !context.signal.aborted) {
21856
+ render(diagnostics?.snapshot() ?? latestSnapshot);
21857
+ }
21858
+ };
21859
+ if (document2.defaultView?.requestAnimationFrame) {
21860
+ renderFrame = document2.defaultView.requestAnimationFrame(flush);
21861
+ } else {
21862
+ queueMicrotask(flush);
21863
+ }
21864
+ };
21865
+ const cancelRender = () => {
21866
+ disposed = true;
21867
+ if (renderFrame !== null) document2.defaultView?.cancelAnimationFrame(renderFrame);
21868
+ renderFrame = null;
21869
+ };
21452
21870
  const diagnostics = getDiagnostics();
21453
21871
  const installs = diagnostics ? harnessInstallStore(diagnostics) : null;
21454
21872
  const runRefresh = () => {
@@ -21538,7 +21956,7 @@ ${error51.stderrTail}`] : []
21538
21956
  tab.setAttribute("aria-controls", panelId);
21539
21957
  tab.setAttribute("aria-selected", String(host.hostId === selectedHost.hostId));
21540
21958
  tab.tabIndex = host.hostId === selectedHost.hostId ? 0 : -1;
21541
- const hostName = connectionHostName(host.hostId, messages);
21959
+ const hostName = hostNames.get(host.hostId) || connectionHostName(host.hostId, messages);
21542
21960
  tab.textContent = hostName;
21543
21961
  tab.title = host.active ? `${hostName} \xB7 ${messages.connectionActiveHost}` : hostName;
21544
21962
  tab.addEventListener("click", () => {
@@ -21632,14 +22050,14 @@ ${error51.stderrTail}`] : []
21632
22050
  const key = JSON.stringify([selectedHost.hostId, agent]);
21633
22051
  let panel = versionPanels.get(key);
21634
22052
  if (!panel) {
21635
- const hostId = selectedHost.hostId;
22053
+ const hostId2 = selectedHost.hostId;
21636
22054
  panel = createHarnessVersionPanel(document2, messages, context.signal, agent, {
21637
22055
  async run(action) {
21638
- if (action !== "update") return installation(hostId, agent, action);
22056
+ if (action !== "update") return installation(hostId2, agent, action);
21639
22057
  updatingVersions.add(key);
21640
22058
  if (diagnostics) render(diagnostics.snapshot());
21641
22059
  try {
21642
- return await installation(hostId, agent, action);
22060
+ return await installation(hostId2, agent, action);
21643
22061
  } finally {
21644
22062
  updatingVersions.delete(key);
21645
22063
  if (!context.signal.aborted && diagnostics) render(diagnostics.snapshot());
@@ -21831,20 +22249,31 @@ ${error51.stderrTail}`] : []
21831
22249
  }
21832
22250
  };
21833
22251
  render(diagnostics?.snapshot() ?? null);
21834
- const unsubscribeGroup = groupPreference.subscribe(
21835
- () => render(diagnostics?.snapshot() ?? latestSnapshot)
22252
+ void getRemoteConnections()?.ssh.list(context.signal).then(
22253
+ (connections) => {
22254
+ if (context.signal.aborted) return;
22255
+ for (const connection of connections) {
22256
+ hostNames.set(connection.hostId, connection.displayName.trim());
22257
+ }
22258
+ render(diagnostics?.snapshot() ?? null);
22259
+ },
22260
+ () => {
22261
+ }
21836
22262
  );
22263
+ const unsubscribeGroup = groupPreference.subscribe(scheduleRender);
21837
22264
  if (!diagnostics) {
21838
22265
  refresh.disabled = true;
21839
22266
  return () => {
22267
+ cancelRender();
21840
22268
  disposeHostScroller();
21841
22269
  unsubscribeGroup();
21842
22270
  };
21843
22271
  }
21844
22272
  refresh.addEventListener("click", runRefresh);
21845
- const unsubscribe = diagnostics.subscribe(() => render(diagnostics.snapshot()));
21846
- const unsubscribeInstalls = installs?.subscribe(() => render(diagnostics.snapshot()));
22273
+ const unsubscribe = diagnostics.subscribe(scheduleRender);
22274
+ const unsubscribeInstalls = installs?.subscribe(scheduleRender);
21847
22275
  return () => {
22276
+ cancelRender();
21848
22277
  disposeHostScroller();
21849
22278
  unsubscribeInstalls?.();
21850
22279
  unsubscribe();
@@ -22415,7 +22844,7 @@ ${error51.stderrTail}`] : []
22415
22844
  void context.runLatest(
22416
22845
  async (signal) => {
22417
22846
  const missing = new Set(
22418
- getDiagnostics()?.snapshot().hosts.find(({ hostId }) => hostId === "local")?.agents.filter(({ availability }) => availability === "notInstalled").map(({ agent }) => agent)
22847
+ getDiagnostics()?.snapshot().hosts.find(({ hostId: hostId2 }) => hostId2 === "local")?.agents.filter(({ availability }) => availability === "notInstalled").map(({ agent }) => agent)
22419
22848
  );
22420
22849
  const harnesses = (await client.listSessionImportSources()).harnesses.filter(
22421
22850
  ({ harnessId }) => !missing.has(harnessId)
@@ -22758,8 +23187,28 @@ ${error51.stderrTail}`] : []
22758
23187
  });
22759
23188
  }
22760
23189
 
23190
+ // src/settings/account-privacy.ts
23191
+ var EMAIL_PATTERN = /([^\s@]+)@([^\s@]+\.[^\s@]+)/gu;
23192
+ var MASK = "****";
23193
+ function maskEmailLocalPart(local) {
23194
+ const chars = Array.from(local);
23195
+ if (chars.length <= 1) return MASK;
23196
+ if (chars.length <= 4) return `${chars[0]}${MASK}${chars.length > 2 ? chars.at(-1) : ""}`;
23197
+ return `${chars.slice(0, 2).join("")}${MASK}${chars.slice(-2).join("")}`;
23198
+ }
23199
+ function maskEmails(text) {
23200
+ return text.replace(
23201
+ EMAIL_PATTERN,
23202
+ (_match, local, domain2) => `${maskEmailLocalPart(local)}@${domain2}`
23203
+ );
23204
+ }
23205
+ function accountDisplayText(text, hideEmails) {
23206
+ return hideEmails ? maskEmails(text) : text;
23207
+ }
23208
+
22761
23209
  // src/settings/credential-imports.ts
22762
23210
  var TARGET_HARNESS_ID = "pi";
23211
+ var SECTION_IMPORT_KEY = "section:import";
22763
23212
  var VENDOR_AGENTS = {
22764
23213
  "openai-codex": "codex",
22765
23214
  xai: "grok"
@@ -22794,9 +23243,17 @@ ${error51.stderrTail}`] : []
22794
23243
  let busy = false;
22795
23244
  let dialog;
22796
23245
  let othersExpanded = false;
23246
+ let sectionExpanded = true;
23247
+ let hideEmails = false;
23248
+ const shown = (text) => accountDisplayText(text, hideEmails);
22797
23249
  const targetButtons = /* @__PURE__ */ new Map();
22798
23250
  const importedRecords = () => snapshot.targets.find((target) => target.harnessId === TARGET_HARNESS_ID)?.imports ?? [];
22799
23251
  const sourceOf = (record2) => snapshot.sources.find((source) => source.id === record2.source.id);
23252
+ const piTarget = () => snapshot.targets.find((candidate) => candidate.harnessId === TARGET_HARNESS_ID);
23253
+ const compatibleSources = () => {
23254
+ const target = piTarget();
23255
+ return target ? snapshot.sources.filter((source) => target.providers.includes(source.provider)) : [];
23256
+ };
22800
23257
  const section = document2.createElement("section");
22801
23258
  section.className = "settings-pi-accounts";
22802
23259
  section.hidden = true;
@@ -22804,12 +23261,46 @@ ${error51.stderrTail}`] : []
22804
23261
  section.setAttribute("aria-label", messages.sectionTitle);
22805
23262
  const sectionHeader = document2.createElement("div");
22806
23263
  sectionHeader.className = "settings-pi-accounts__header";
23264
+ const sectionToggle = document2.createElement("button");
23265
+ sectionToggle.type = "button";
23266
+ sectionToggle.className = "settings-pi-accounts__fold";
23267
+ sectionToggle.setAttribute("aria-expanded", "true");
22807
23268
  const sectionTitle = document2.createElement("strong");
22808
23269
  sectionTitle.textContent = messages.sectionTitle;
22809
23270
  const sectionCount = document2.createElement("span");
22810
- sectionHeader.append(createRendererAgentIcon("pi", 16, document2), sectionTitle, sectionCount);
23271
+ sectionCount.className = "settings-pi-accounts__count";
23272
+ sectionToggle.append(
23273
+ createRendererSettingsIcon("chevron-right", 14),
23274
+ createRendererAgentIcon("pi", 16, document2),
23275
+ sectionTitle,
23276
+ sectionCount
23277
+ );
23278
+ const sectionHint = document2.createElement("span");
23279
+ sectionHint.className = "settings-pi-accounts__hint";
23280
+ sectionHint.textContent = messages.sectionHint;
23281
+ const sectionImport = document2.createElement("button");
23282
+ sectionImport.type = "button";
23283
+ sectionImport.className = "settings-pi-accounts__import";
23284
+ sectionImport.setAttribute("aria-haspopup", "dialog");
23285
+ sectionImport.append(createRendererSettingsIcon("add", 14), messages.sectionImport);
23286
+ sectionImport.addEventListener("click", () => {
23287
+ const sources = compatibleSources();
23288
+ const free = sources.find(
23289
+ (source2) => !importedRecords().some((record2) => record2.source.id === source2.id)
23290
+ );
23291
+ const source = free ?? sources[0];
23292
+ if (source) openDialog("add", { source, choose: true }, SECTION_IMPORT_KEY);
23293
+ });
23294
+ sectionHeader.append(sectionToggle, sectionHint, sectionImport);
22811
23295
  const card = document2.createElement("div");
22812
23296
  card.className = "settings-pi-accounts__card";
23297
+ card.id = "settings-pi-accounts-card";
23298
+ sectionToggle.setAttribute("aria-controls", card.id);
23299
+ sectionToggle.addEventListener("click", () => {
23300
+ sectionExpanded = !sectionExpanded;
23301
+ card.hidden = !sectionExpanded;
23302
+ sectionToggle.setAttribute("aria-expanded", String(sectionExpanded));
23303
+ });
22813
23304
  const othersToggle = document2.createElement("button");
22814
23305
  othersToggle.type = "button";
22815
23306
  othersToggle.className = "settings-pi-accounts__toggle";
@@ -22850,15 +23341,15 @@ ${error51.stderrTail}`] : []
22850
23341
  row.className = "settings-pi-accounts__row";
22851
23342
  row.tabIndex = -1;
22852
23343
  row.dataset.importName = record2.name;
22853
- row.setAttribute("aria-label", record2.source.label);
23344
+ row.setAttribute("aria-label", shown(record2.source.label));
22854
23345
  const agent = KNOWN_RENDERER_AGENTS.find((candidate) => candidate === record2.source.harnessId);
22855
23346
  const mark = createMark(agent);
22856
23347
  const identity = document2.createElement("div");
22857
23348
  identity.className = "settings-pi-accounts__identity";
22858
23349
  const label = document2.createElement("strong");
22859
23350
  label.className = "settings-account-email";
22860
- label.textContent = record2.source.label;
22861
- label.title = record2.source.label;
23351
+ label.textContent = shown(record2.source.label);
23352
+ label.title = label.textContent;
22862
23353
  label.translate = false;
22863
23354
  const meta3 = document2.createElement("div");
22864
23355
  meta3.className = "settings-account-metadata";
@@ -22888,7 +23379,7 @@ ${error51.stderrTail}`] : []
22888
23379
  reimport.type = "button";
22889
23380
  reimport.className = "settings-account-action";
22890
23381
  reimport.textContent = messages.rowReimport;
22891
- reimport.setAttribute("aria-label", `${messages.reimport}: ${record2.source.label}`);
23382
+ reimport.setAttribute("aria-label", `${messages.reimport}: ${shown(record2.source.label)}`);
22892
23383
  reimport.disabled = busy || !source;
22893
23384
  reimport.title = messages.reimport;
22894
23385
  reimport.addEventListener("click", () => {
@@ -22898,7 +23389,7 @@ ${error51.stderrTail}`] : []
22898
23389
  remove.type = "button";
22899
23390
  remove.className = "settings-account-action settings-pi-accounts__remove";
22900
23391
  remove.textContent = messages.rowRemove;
22901
- remove.setAttribute("aria-label", `${messages.remove}: ${record2.source.label}`);
23392
+ remove.setAttribute("aria-label", `${messages.remove}: ${shown(record2.source.label)}`);
22902
23393
  remove.disabled = busy;
22903
23394
  remove.addEventListener("click", () => openDialog("remove", { record: record2 }, `row:${record2.name}`));
22904
23395
  if (source) actions.append(reimport);
@@ -22914,14 +23405,14 @@ ${error51.stderrTail}`] : []
22914
23405
  const agent = login.vendor ? VENDOR_AGENTS[login.vendor] : void 0;
22915
23406
  row.setAttribute(
22916
23407
  "aria-label",
22917
- [login.label ?? login.provider, agent && RENDERER_AGENT_LABELS[agent]].filter(Boolean).join(" \xB7 ")
23408
+ [shown(login.label ?? login.provider), agent && RENDERER_AGENT_LABELS[agent]].filter(Boolean).join(" \xB7 ")
22918
23409
  );
22919
23410
  const mark = createMark(agent);
22920
23411
  const identity = document2.createElement("div");
22921
23412
  identity.className = "settings-pi-accounts__identity";
22922
23413
  const title = document2.createElement("strong");
22923
23414
  title.className = "settings-account-email";
22924
- title.textContent = login.label ?? login.provider;
23415
+ title.textContent = shown(login.label ?? login.provider);
22925
23416
  title.title = title.textContent;
22926
23417
  title.translate = false;
22927
23418
  const meta3 = document2.createElement("div");
@@ -22942,11 +23433,14 @@ ${error51.stderrTail}`] : []
22942
23433
  return row;
22943
23434
  };
22944
23435
  const renderSection = () => {
22945
- const target = snapshot.targets.find((candidate) => candidate.harnessId === TARGET_HARNESS_ID);
23436
+ const target = piTarget();
22946
23437
  section.hidden = !target;
22947
23438
  const records = importedRecords();
22948
23439
  const others = target?.others ?? [];
22949
23440
  sectionCount.textContent = String(records.length + others.length);
23441
+ card.hidden = !sectionExpanded;
23442
+ sectionImport.hidden = compatibleSources().length === 0;
23443
+ sectionImport.disabled = busy;
22950
23444
  rows.clear();
22951
23445
  if (records.length + others.length === 0) {
22952
23446
  const empty = document2.createElement("p");
@@ -23009,6 +23503,9 @@ ${error51.stderrTail}`] : []
23009
23503
  let state = initial;
23010
23504
  let mode = initialMode;
23011
23505
  let nameInput;
23506
+ let nameEdited = false;
23507
+ let sourceRadios = [];
23508
+ let refreshPreview = () => void 0;
23012
23509
  const command = (text, onClick, variant = "secondary") => {
23013
23510
  const button2 = document2.createElement("button");
23014
23511
  button2.type = "button";
@@ -23032,9 +23529,50 @@ ${error51.stderrTail}`] : []
23032
23529
  paragraph.textContent = text;
23033
23530
  return paragraph;
23034
23531
  };
23532
+ const sourcePicker = (choices) => {
23533
+ const group = document2.createElement("fieldset");
23534
+ group.className = "settings-credential-dialog__sources";
23535
+ const legend = document2.createElement("legend");
23536
+ legend.textContent = messages.source;
23537
+ group.append(legend);
23538
+ for (const choice of choices) {
23539
+ const option = document2.createElement("label");
23540
+ option.className = "settings-credential-dialog__source";
23541
+ const radio = document2.createElement("input");
23542
+ radio.type = "radio";
23543
+ radio.name = "settings-credential-source";
23544
+ radio.value = choice.id;
23545
+ radio.checked = choice.id === state.source?.id;
23546
+ sourceRadios.push(radio);
23547
+ const copy = document2.createElement("span");
23548
+ const label = document2.createElement("strong");
23549
+ label.textContent = shown(choice.label);
23550
+ label.translate = false;
23551
+ const existing = importedRecords().find((record2) => record2.source.id === choice.id);
23552
+ const agent = KNOWN_RENDERER_AGENTS.find((candidate) => candidate === choice.harnessId);
23553
+ const detail = document2.createElement("span");
23554
+ detail.textContent = [
23555
+ agent && RENDERER_AGENT_LABELS[agent] || choice.harnessId,
23556
+ existing ? messages.sourceImported.replace("{name}", existing.name) : messages.sourceCurrent
23557
+ ].join(" \xB7 ");
23558
+ copy.append(label, detail);
23559
+ radio.addEventListener("change", () => {
23560
+ if (!radio.checked) return;
23561
+ state = { ...state, source: choice };
23562
+ if (nameInput && !nameEdited) {
23563
+ nameInput.value = defaultImportName(choice, importedRecords());
23564
+ }
23565
+ refreshPreview();
23566
+ });
23567
+ option.append(radio, copy);
23568
+ group.append(option);
23569
+ }
23570
+ return group;
23571
+ };
23035
23572
  const setBusy = (value) => {
23036
23573
  for (const child of controls.children) child.disabled = value;
23037
23574
  if (nameInput) nameInput.disabled = value;
23575
+ for (const radio of sourceRadios) radio.disabled = value;
23038
23576
  };
23039
23577
  const submit = async (request, next) => {
23040
23578
  error51.textContent = "";
@@ -23062,6 +23600,7 @@ ${error51.stderrTail}`] : []
23062
23600
  mode = next;
23063
23601
  error51.textContent = "";
23064
23602
  nameInput = void 0;
23603
+ sourceRadios = [];
23065
23604
  body.replaceChildren();
23066
23605
  controls.replaceChildren();
23067
23606
  const { source, record: record2 } = state;
@@ -23070,7 +23609,9 @@ ${error51.stderrTail}`] : []
23070
23609
  title.textContent = mode === "add" ? messages.add : messages.reimport;
23071
23610
  modal.setAttribute("aria-label", title.textContent);
23072
23611
  const active = source ?? record2?.source;
23073
- if (active) body.append(line(messages.source, active.label));
23612
+ const choices = mode === "add" && state.choose ? compatibleSources() : [];
23613
+ if (choices.length > 1) body.append(sourcePicker(choices));
23614
+ else if (active) body.append(line(messages.source, shown(active.label)));
23074
23615
  const input = document2.createElement("input");
23075
23616
  input.type = "text";
23076
23617
  input.maxLength = 48;
@@ -23078,6 +23619,9 @@ ${error51.stderrTail}`] : []
23078
23619
  input.spellcheck = false;
23079
23620
  input.value = record2?.name ?? (source ? defaultImportName(source, importedRecords()) : "codex");
23080
23621
  input.readOnly = Boolean(record2);
23622
+ input.addEventListener("input", () => {
23623
+ nameEdited = true;
23624
+ });
23081
23625
  nameInput = input;
23082
23626
  const label = document2.createElement("label");
23083
23627
  label.className = "settings-credential-dialog__field";
@@ -23089,6 +23633,7 @@ ${error51.stderrTail}`] : []
23089
23633
  preview.textContent = (mode === "reimport" ? messages.reimportPreview : messages.preview).replace("{name}", input.value);
23090
23634
  };
23091
23635
  input.addEventListener("input", update);
23636
+ refreshPreview = update;
23092
23637
  update();
23093
23638
  body.append(label, preview, note(messages.warning));
23094
23639
  controls.append(
@@ -23096,7 +23641,8 @@ ${error51.stderrTail}`] : []
23096
23641
  command(
23097
23642
  messages.confirm,
23098
23643
  async () => {
23099
- if (!source) return;
23644
+ const source2 = state.source;
23645
+ if (!source2) return;
23100
23646
  if (!credentialImportNameSchema.safeParse(input.value).success) {
23101
23647
  error51.textContent = messages.invalidName;
23102
23648
  input.focus();
@@ -23105,7 +23651,7 @@ ${error51.stderrTail}`] : []
23105
23651
  await submit(
23106
23652
  {
23107
23653
  action: mode === "reimport" ? "reimport" : "import",
23108
- sourceId: source.id,
23654
+ sourceId: source2.id,
23109
23655
  name: record2?.name ?? input.value,
23110
23656
  confirmed: true
23111
23657
  },
@@ -23147,7 +23693,7 @@ ${error51.stderrTail}`] : []
23147
23693
  modal.addEventListener("close", () => {
23148
23694
  modal.remove();
23149
23695
  dialog = void 0;
23150
- const target = returnKey ? targetButtons.get(returnKey) ?? rows.get(returnKey.replace(/^row:/, "")) : void 0;
23696
+ const target = returnKey ? returnKey === SECTION_IMPORT_KEY ? sectionImport : targetButtons.get(returnKey) ?? rows.get(returnKey.replace(/^row:/, "")) : void 0;
23151
23697
  (target?.isConnected ? target : section).focus();
23152
23698
  });
23153
23699
  show(initialMode);
@@ -23157,6 +23703,12 @@ ${error51.stderrTail}`] : []
23157
23703
  signal.addEventListener("abort", () => dialog?.remove(), { once: true });
23158
23704
  renderSection();
23159
23705
  return {
23706
+ /** Mask the middle of email addresses in the section and dialogs (screen sharing). */
23707
+ setHideEmails(value) {
23708
+ if (hideEmails === value) return;
23709
+ hideEmails = value;
23710
+ renderSection();
23711
+ },
23160
23712
  /** Dedicated "imported into Pi" section, rendered below the account table. */
23161
23713
  section,
23162
23714
  refresh: async () => {
@@ -23178,20 +23730,33 @@ ${error51.stderrTail}`] : []
23178
23730
  const key = `${harnessId}:${accountLabel}:import`;
23179
23731
  const button2 = document2.createElement("button");
23180
23732
  button2.type = "button";
23181
- button2.className = "settings-icon-button settings-account-harness-target";
23733
+ button2.className = "settings-account-pi-import";
23182
23734
  button2.dataset.accountFocus = key;
23183
23735
  button2.disabled = busy;
23184
- if (record2) button2.dataset.state = "imported";
23185
23736
  const hint = record2 ? messages.importedHint.replace("{name}", record2.name) : messages.add;
23186
23737
  button2.title = hint;
23187
23738
  button2.setAttribute("aria-label", hint);
23188
- button2.setAttribute("aria-haspopup", "dialog");
23189
- button2.append(createRendererAgentIcon("pi", 16, document2));
23739
+ button2.append(createRendererAgentIcon("pi", 12, document2));
23740
+ if (record2) {
23741
+ button2.dataset.state = "imported";
23742
+ const name = document2.createElement("code");
23743
+ name.textContent = `${record2.name}/\u2026`;
23744
+ name.translate = false;
23745
+ button2.append(name, createRendererSettingsIcon("check", 12));
23746
+ } else {
23747
+ button2.setAttribute("aria-haspopup", "dialog");
23748
+ button2.append(messages.add);
23749
+ }
23190
23750
  button2.addEventListener("click", () => {
23191
23751
  if (!record2) {
23192
23752
  openDialog("add", { source }, key);
23193
23753
  return;
23194
23754
  }
23755
+ if (!sectionExpanded) {
23756
+ sectionExpanded = true;
23757
+ renderSection();
23758
+ sectionToggle.setAttribute("aria-expanded", "true");
23759
+ }
23195
23760
  const row = rows.get(record2.name);
23196
23761
  row?.scrollIntoView({ block: "nearest", behavior: "smooth" });
23197
23762
  row?.focus();
@@ -23572,37 +24137,35 @@ ${error51.stderrTail}`] : []
23572
24137
  return { cells: [cell], continuationCells: [] };
23573
24138
  }
23574
24139
  const { rows, additional } = splitUsageWindows(state.credits, messages, filter);
23575
- const onlyRow = rows.length === 1 ? rows[0] : void 0;
23576
- if (additional.length && onlyRow && !Object.keys(onlyRow.columns).length) rows.pop();
23577
- for (let index = 0; index < additional.length; index += 2) {
23578
- const first = additional[index];
23579
- const second = additional[index + 1];
23580
- rows.push({
23581
- columns: {
23582
- ...first ? { five_hour: first } : {},
23583
- ...second ? { seven_day: second } : {}
23584
- }
23585
- });
24140
+ const windows = [
24141
+ ...rows.flatMap(
24142
+ (row) => ["five_hour", "seven_day"].flatMap(
24143
+ (period) => row.columns[period] ? [row.columns[period]] : []
24144
+ )
24145
+ ),
24146
+ ...additional
24147
+ ];
24148
+ const packed = [];
24149
+ for (let index = 0; index < windows.length; index += 2) {
24150
+ const pair = windows.slice(index, index + 2);
24151
+ packed.push(
24152
+ pair.map((window2) => {
24153
+ const cell = document2.createElement("td");
24154
+ cell.className = "settings-account-usage-cell";
24155
+ if (pair.length === 1) cell.colSpan = 2;
24156
+ cell.append(renderUsageWindow(document2, window2, messages, display));
24157
+ return cell;
24158
+ })
24159
+ );
23586
24160
  }
23587
- const renderCells = (row) => ["five_hour", "seven_day"].map((period) => {
24161
+ if (packed.length === 0) {
23588
24162
  const cell = document2.createElement("td");
23589
- cell.className = "settings-account-usage-cell";
23590
- const window2 = row.columns[period];
23591
- if (window2) cell.append(renderUsageWindow(document2, window2, messages, display));
23592
- else {
23593
- const missing = document2.createElement("div");
23594
- missing.className = "settings-account-usage__missing";
23595
- const dash = document2.createElement("span");
23596
- dash.textContent = "\u2014";
23597
- dash.setAttribute("aria-hidden", "true");
23598
- missing.append(dash);
23599
- cell.append(missing);
23600
- }
23601
- return cell;
23602
- });
23603
- const [firstRow = { columns: {} }, ...continuations] = rows;
23604
- const cells = renderCells(firstRow);
23605
- const continuationCells = continuations.map(renderCells);
24163
+ cell.colSpan = 2;
24164
+ cell.className = "settings-account-usage-cell settings-account-usage-cell--message";
24165
+ cell.textContent = messages.accountCreditsEmpty;
24166
+ packed.push([cell]);
24167
+ }
24168
+ const [cells = [], ...continuationCells] = packed;
23606
24169
  return { cells, continuationCells };
23607
24170
  }
23608
24171
  function renderAccountResetCredits(document2, credits, messages) {
@@ -23696,14 +24259,14 @@ ${error51.stderrTail}`] : []
23696
24259
  table.className = "settings-account-table";
23697
24260
  table.setAttribute("aria-label", messages.pageLabels.accounts);
23698
24261
  const columns = document2.createElement("colgroup");
23699
- for (const width of [32, 24, 24, 20]) {
24262
+ for (const width of [36, 32, 32]) {
23700
24263
  const column = document2.createElement("col");
23701
24264
  column.style.width = `${width}%`;
23702
24265
  columns.append(column);
23703
24266
  }
23704
24267
  const head = document2.createElement("thead");
23705
24268
  const row = document2.createElement("tr");
23706
- const headers = Array.from({ length: 3 }, (_, index) => {
24269
+ const headers = Array.from({ length: 2 }, (_, index) => {
23707
24270
  const cell = document2.createElement("th");
23708
24271
  cell.scope = index === 1 ? "colgroup" : "col";
23709
24272
  if (index === 1) cell.colSpan = 2;
@@ -23711,11 +24274,7 @@ ${error51.stderrTail}`] : []
23711
24274
  return cell;
23712
24275
  });
23713
24276
  const updateDisplay = (display) => {
23714
- const labels = [
23715
- messages.accountColumnAccount,
23716
- accountUsageColumnLabel(display, messages),
23717
- messages.credentialImports.column
23718
- ];
24277
+ const labels = [messages.accountColumnAccount, accountUsageColumnLabel(display, messages)];
23719
24278
  headers.forEach((cell, index) => {
23720
24279
  cell.textContent = labels[index] ?? "";
23721
24280
  });
@@ -23767,11 +24326,13 @@ ${error51.stderrTail}`] : []
23767
24326
  person.append(input.mark, identity);
23768
24327
  return person;
23769
24328
  }
23770
- function createTargetCell(document2, action) {
23771
- const cell = document2.createElement("td");
23772
- cell.className = action ? "settings-account-management-cell" : "settings-account-management-cell settings-account-management-cell--empty";
23773
- if (action) cell.append(action);
23774
- return cell;
24329
+ function appendAccountExtras(cell, items) {
24330
+ const present = items.filter((item) => Boolean(item));
24331
+ if (present.length === 0) return;
24332
+ const extras = cell.ownerDocument.createElement("div");
24333
+ extras.className = "settings-account-row__extras";
24334
+ extras.append(...present);
24335
+ cell.append(extras);
23775
24336
  }
23776
24337
  function renderAccountBalance(document2, messages, balance) {
23777
24338
  const cell = document2.createElement("td");
@@ -23796,8 +24357,8 @@ ${error51.stderrTail}`] : []
23796
24357
  row.dataset.accountId = account.accountId;
23797
24358
  row.dataset.accountFocus = `${account.accountId}:row`;
23798
24359
  row.tabIndex = -1;
23799
- const name = codexAccountDisplayName(account);
23800
- row.setAttribute("aria-label", name.full);
24360
+ const name = accountDisplayText(codexAccountDisplayName(account).full, input.hideEmails === true);
24361
+ row.setAttribute("aria-label", name);
23801
24362
  const personCell = document2.createElement("td");
23802
24363
  personCell.className = "settings-account-person-cell";
23803
24364
  const mark = document2.createElement("div");
@@ -23807,7 +24368,7 @@ ${error51.stderrTail}`] : []
23807
24368
  mark.append(createRendererAgentIcon("codex", 26, document2));
23808
24369
  personCell.append(
23809
24370
  createAccountPerson(document2, messages, {
23810
- name: name.full,
24371
+ name,
23811
24372
  agent: "Codex",
23812
24373
  plan: accountPlanLabel(account.planType),
23813
24374
  highlighted: account.planType === "pro" || account.planType === "prolite",
@@ -23823,8 +24384,6 @@ ${error51.stderrTail}`] : []
23823
24384
  input.onRetry,
23824
24385
  account.planType === "pro" ? "weekly-only" : "all"
23825
24386
  );
23826
- const actionsCell = createTargetCell(document2, input.importAction);
23827
- if (input.importAction) row.className += " settings-account-row--targets";
23828
24387
  const continuationRows = usage.continuationCells.map((cells) => {
23829
24388
  const continuation = document2.createElement("tr");
23830
24389
  continuation.className = "settings-account-row settings-account-quota-continuation-row";
@@ -23835,18 +24394,19 @@ ${error51.stderrTail}`] : []
23835
24394
  if (continuationRows.length > 0) {
23836
24395
  personCell.rowSpan = continuationRows.length + 1;
23837
24396
  personCell.className += " settings-account-spanning-cell";
23838
- actionsCell.rowSpan = continuationRows.length + 1;
23839
- actionsCell.className += " settings-account-spanning-cell";
23840
24397
  }
23841
- row.append(personCell, ...usage.cells, actionsCell);
24398
+ row.append(personCell, ...usage.cells);
23842
24399
  const reset = input.usage?.status === "ready" ? renderAccountResetCredits(document2, input.usage.credits, messages) : null;
23843
- if (!reset) return [row, ...continuationRows];
24400
+ if (!reset) {
24401
+ appendAccountExtras(personCell, [input.importAction]);
24402
+ return [row, ...continuationRows];
24403
+ }
23844
24404
  const detailsRow = document2.createElement("tr");
23845
24405
  detailsRow.className = "settings-account-details-row";
23846
24406
  detailsRow.id = `settings-account-reset-${++resetDetailsSequence}`;
23847
24407
  detailsRow.hidden = !input.resetExpanded;
23848
24408
  const detailsCell = document2.createElement("td");
23849
- detailsCell.colSpan = 4;
24409
+ detailsCell.colSpan = 3;
23850
24410
  detailsCell.append(reset.details);
23851
24411
  detailsRow.append(detailsCell);
23852
24412
  reset.summary.dataset.accountFocus = `${account.accountId}:reset`;
@@ -23857,15 +24417,18 @@ ${error51.stderrTail}`] : []
23857
24417
  reset.summary.setAttribute("aria-expanded", String(!detailsRow.hidden));
23858
24418
  input.onResetExpanded(!detailsRow.hidden);
23859
24419
  });
23860
- personCell.append(reset.summary);
24420
+ appendAccountExtras(personCell, [reset.summary, input.importAction]);
23861
24421
  return [row, ...continuationRows, detailsRow];
23862
24422
  }
23863
- function renderHarnessAccountRows(document2, account, messages, display, importAction) {
24423
+ function renderHarnessAccountRows(document2, account, messages, display, importAction, hideEmails = false) {
23864
24424
  const row = document2.createElement("tr");
23865
24425
  row.className = "settings-account-row";
23866
24426
  row.dataset.harnessId = account.harnessId;
23867
24427
  row.tabIndex = -1;
23868
- const name = account.email ?? account.label ?? account.harnessName;
24428
+ const name = accountDisplayText(
24429
+ account.email ?? account.label ?? account.harnessName,
24430
+ hideEmails
24431
+ );
23869
24432
  row.setAttribute("aria-label", name);
23870
24433
  const personCell = document2.createElement("td");
23871
24434
  personCell.className = "settings-account-person-cell";
@@ -23890,9 +24453,8 @@ ${error51.stderrTail}`] : []
23890
24453
  () => void 0,
23891
24454
  account.harnessId === "grok" ? "weekly-only" : "all"
23892
24455
  ) : renderAccountBalance(document2, messages, account.balance);
23893
- const managementCell = createTargetCell(document2, importAction);
23894
- if (importAction) row.className += " settings-account-row--targets";
23895
24456
  personCell.title = messages.accountNativeManagementHint.replace("{harness}", account.harnessName);
24457
+ appendAccountExtras(personCell, [importAction]);
23896
24458
  const continuationRows = usage.continuationCells.map((cells) => {
23897
24459
  const continuation = document2.createElement("tr");
23898
24460
  continuation.className = "settings-account-row settings-account-quota-continuation-row";
@@ -23903,10 +24465,8 @@ ${error51.stderrTail}`] : []
23903
24465
  if (continuationRows.length > 0) {
23904
24466
  personCell.rowSpan = continuationRows.length + 1;
23905
24467
  personCell.className += " settings-account-spanning-cell";
23906
- managementCell.rowSpan = continuationRows.length + 1;
23907
- managementCell.className += " settings-account-spanning-cell";
23908
24468
  }
23909
- row.append(personCell, ...usage.cells, managementCell);
24469
+ row.append(personCell, ...usage.cells);
23910
24470
  return [row, ...continuationRows];
23911
24471
  }
23912
24472
 
@@ -24074,6 +24634,26 @@ ${error51.stderrTail}`] : []
24074
24634
  refreshUsage.title = messages.accountCreditsRefresh;
24075
24635
  refreshUsage.setAttribute("aria-label", messages.accountCreditsRefresh);
24076
24636
  refreshUsage.append(createRendererSettingsIcon("refresh", 16));
24637
+ let hideEmails = false;
24638
+ const privacyToggle = document2.createElement("button");
24639
+ privacyToggle.type = "button";
24640
+ privacyToggle.className = "settings-icon-button settings-account-privacy-toggle";
24641
+ const updatePrivacyToggle = () => {
24642
+ const label = hideEmails ? messages.accountEmailsShow : messages.accountEmailsHide;
24643
+ privacyToggle.title = label;
24644
+ privacyToggle.setAttribute("aria-label", label);
24645
+ privacyToggle.setAttribute("aria-pressed", String(hideEmails));
24646
+ privacyToggle.replaceChildren(
24647
+ createRendererSettingsIcon(hideEmails ? "eye-off" : "eye", 16)
24648
+ );
24649
+ };
24650
+ updatePrivacyToggle();
24651
+ privacyToggle.addEventListener("click", () => {
24652
+ hideEmails = !hideEmails;
24653
+ updatePrivacyToggle();
24654
+ credentialImports.setHideEmails(hideEmails);
24655
+ render();
24656
+ });
24077
24657
  refreshUsage.addEventListener("click", () => {
24078
24658
  usageByAccountId.clear();
24079
24659
  loadUsage(accounts);
@@ -24081,7 +24661,7 @@ ${error51.stderrTail}`] : []
24081
24661
  void credentialImports.refresh();
24082
24662
  });
24083
24663
  search.addEventListener("input", () => render());
24084
- toolbar.append(connected, searchWrapper, displayControls, refreshUsage);
24664
+ toolbar.append(connected, privacyToggle, searchWrapper, displayControls, refreshUsage);
24085
24665
  const list = document2.createElement("div");
24086
24666
  list.className = "settings-account-list";
24087
24667
  const { table, body, updateDisplay } = createAccountsTable(document2, messages);
@@ -24126,7 +24706,7 @@ ${error51.stderrTail}`] : []
24126
24706
  if (visibleAccounts.length + visibleHarnessAccounts.length === 0) {
24127
24707
  const emptyRow = document2.createElement("tr");
24128
24708
  const emptyCell = document2.createElement("td");
24129
- emptyCell.colSpan = 4;
24709
+ emptyCell.colSpan = 3;
24130
24710
  emptyCell.className = "settings-account-empty";
24131
24711
  emptyCell.textContent = query ? messages.accountNoMatches : messages.accountEmpty;
24132
24712
  emptyRow.append(emptyCell);
@@ -24136,10 +24716,6 @@ ${error51.stderrTail}`] : []
24136
24716
  body.append(
24137
24717
  ...renderAccountRows(document2, account, messages, {
24138
24718
  current: accountPhase === "ready" && account.accountId === currentAccountId,
24139
- importAction: credentialImports.button(
24140
- "codex",
24141
- codexAccountDisplayName(account).full
24142
- ),
24143
24719
  usage: usageByAccountId.get(account.accountId),
24144
24720
  display: usageDisplay,
24145
24721
  resetExpanded: expandedResetAccounts.has(account.accountId),
@@ -24150,7 +24726,12 @@ ${error51.stderrTail}`] : []
24150
24726
  onResetExpanded: (open) => {
24151
24727
  if (open) expandedResetAccounts.add(account.accountId);
24152
24728
  else expandedResetAccounts.delete(account.accountId);
24153
- }
24729
+ },
24730
+ importAction: credentialImports.button(
24731
+ "codex",
24732
+ codexAccountDisplayName(account).full
24733
+ ),
24734
+ hideEmails
24154
24735
  })
24155
24736
  );
24156
24737
  }
@@ -24164,7 +24745,8 @@ ${error51.stderrTail}`] : []
24164
24745
  credentialImports.button(
24165
24746
  account.harnessId,
24166
24747
  account.email ?? account.label ?? account.harnessName
24167
- )
24748
+ ),
24749
+ hideEmails
24168
24750
  )
24169
24751
  );
24170
24752
  }
@@ -24271,8 +24853,8 @@ ${error51.stderrTail}`] : []
24271
24853
  } catch {
24272
24854
  }
24273
24855
  const harnessAccounts = createHarnessAccounts(context.signal, getClient, render);
24274
- void credentialImports.refresh();
24275
24856
  void harnessAccounts.refresh();
24857
+ void credentialImports.refresh();
24276
24858
  load();
24277
24859
  return () => {
24278
24860
  stopCountdowns();
@@ -24834,7 +25416,7 @@ ${error51.stderrTail}`] : []
24834
25416
  }
24835
25417
  function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread = () => Promise.reject(new Error("Imported Thread navigation is unavailable")), getLoadedSessionsClient = () => null, getRemoteConnections = () => null) {
24836
25418
  return Object.freeze([
24837
- createConnectionsSettingsPage(messages, getDiagnostics),
25419
+ createConnectionsSettingsPage(messages, getDiagnostics, void 0, getRemoteConnections),
24838
25420
  createRemoteConnectionsPage(messages, getRemoteConnections),
24839
25421
  createAccountsSettingsPage(messages, getAccountClient),
24840
25422
  createSessionImportSettingsPage(
@@ -27338,6 +27920,23 @@ ${error51.stderrTail}`] : []
27338
27920
  outline-offset: -1px;
27339
27921
  }
27340
27922
 
27923
+ .settings-remote-dialog__package-option {
27924
+ display: flex;
27925
+ align-items: center;
27926
+ gap: 8px;
27927
+ cursor: pointer;
27928
+ }
27929
+
27930
+ .settings-remote-dialog .settings-remote-dialog__package-option input {
27931
+ flex: none;
27932
+ width: 16px;
27933
+ height: 16px;
27934
+ min-height: 16px;
27935
+ margin: 0;
27936
+ padding: 0;
27937
+ accent-color: var(--settings-focus);
27938
+ }
27939
+
27341
27940
  @keyframes settings-remote-spin {
27342
27941
  to {
27343
27942
  transform: rotate(360deg);
@@ -27441,13 +28040,16 @@ ${error51.stderrTail}`] : []
27441
28040
  (method, params) => manager.sendRequest(method, params)
27442
28041
  );
27443
28042
  const updateClient = consoleUpdateClient();
28043
+ const remoteConnections = createConsoleRemoteConnections(manager);
27444
28044
  const settingsPages = createDefaultRendererSettingsPages(
27445
28045
  settingsMessages,
27446
28046
  () => updateClient,
27447
28047
  () => diagnostics,
27448
28048
  () => modelClient,
27449
28049
  () => sessionImportClient,
27450
- null
28050
+ null,
28051
+ () => null,
28052
+ () => remoteConnections
27451
28053
  );
27452
28054
  const navigate = (pageId) => {
27453
28055
  window.location.hash = pageId;
@@ -27470,6 +28072,7 @@ ${error51.stderrTail}`] : []
27470
28072
  () => state.overview?.inspect?.runtime.running ?? false
27471
28073
  )
27472
28074
  ),
28075
+ hostPage(required2(settingsPages, "remote-connections"), messages, state),
27473
28076
  hostPage(required2(settingsPages, "accounts"), messages, state),
27474
28077
  hostPage(required2(settingsPages, "session-import"), messages, state),
27475
28078
  required2(settingsPages, "updates")
@@ -27640,6 +28243,8 @@ lucide/dist/esm/icons/circle-off.mjs:
27640
28243
  lucide/dist/esm/icons/copy.mjs:
27641
28244
  lucide/dist/esm/icons/download.mjs:
27642
28245
  lucide/dist/esm/icons/external-link.mjs:
28246
+ lucide/dist/esm/icons/eye.mjs:
28247
+ lucide/dist/esm/icons/eye-off.mjs:
27643
28248
  lucide/dist/esm/icons/ellipsis.mjs:
27644
28249
  lucide/dist/esm/icons/folder-input.mjs:
27645
28250
  lucide/dist/esm/icons/grip-vertical.mjs: