@camerontaylor/paseo-client 0.9.1-fork.1 → 0.10.2-fork.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,6 +22,7 @@ export declare class ConnectionSubscriptions extends OwnedSubscriptions {
22
22
  };
23
23
  }
24
24
  export declare const DEFAULT_CLIENT_CAPABILITIES: {
25
+ hello_rejection: true;
25
26
  owned_subscriptions: true;
26
27
  all_providers: true;
27
28
  selective_agent_timeline: true;
@@ -90,6 +90,7 @@ export class ConnectionSubscriptions extends OwnedSubscriptions {
90
90
  // Protocol support belongs to the installed client. Only browser hosting needs
91
91
  // a resource supplied by the caller. Keep this exhaustive as the protocol evolves.
92
92
  export const DEFAULT_CLIENT_CAPABILITIES = {
93
+ [CLIENT_CAPS.helloRejection]: true,
93
94
  [CLIENT_CAPS.ownedSubscriptions]: true,
94
95
  [CLIENT_CAPS.allProviders]: true,
95
96
  [CLIENT_CAPS.selectiveAgentTimeline]: true,
@@ -4,5 +4,6 @@ export declare function createRelayE2eeTransportFactory(args: {
4
4
  daemonPublicKeyB64: string;
5
5
  logger: TransportLogger;
6
6
  }): DaemonTransportFactory;
7
+ export declare function createRelayTransportFactory(baseFactory: DaemonTransportFactory): DaemonTransportFactory;
7
8
  export declare function createEncryptedTransport(base: DaemonTransport, daemonPublicKeyB64: string, logger: TransportLogger): DaemonTransport;
8
9
  //# sourceMappingURL=daemon-client-relay-e2ee-transport.d.ts.map
@@ -1,11 +1,15 @@
1
1
  import { createClientChannel, } from "@camerontaylor/paseo-relay/e2ee";
2
2
  import { extractRelayMessage, normalizeTransportPayload } from "./daemon-client-transport-utils.js";
3
3
  export function createRelayE2eeTransportFactory(args) {
4
- return ({ url, headers }) => {
5
- const base = args.baseFactory({ url, headers });
4
+ return ({ url }) => {
5
+ const base = args.baseFactory({ url });
6
6
  return createEncryptedTransport(base, args.daemonPublicKeyB64, args.logger);
7
7
  };
8
8
  }
9
+ export function createRelayTransportFactory(baseFactory) {
10
+ // The relay upgrade precedes E2EE. Only the encrypted hello may carry daemon credentials.
11
+ return ({ url }) => baseFactory({ url });
12
+ }
9
13
  export function createEncryptedTransport(base, daemonPublicKeyB64, logger) {
10
14
  let channel = null;
11
15
  let opened = false;
@@ -1,5 +1,5 @@
1
1
  export type { DaemonTransport, DaemonTransportFactory, TransportLogger, WebSocketFactory, WebSocketLike, } from "./daemon-client-transport-types.js";
2
2
  export { decodeMessageData, describeTransportClose, describeTransportError, encodeUtf8String, extractRelayMessage, normalizeTransportPayload, safeRandomId, } from "./daemon-client-transport-utils.js";
3
- export { createEncryptedTransport, createRelayE2eeTransportFactory, } from "./daemon-client-relay-e2ee-transport.js";
3
+ export { createEncryptedTransport, createRelayE2eeTransportFactory, createRelayTransportFactory, } from "./daemon-client-relay-e2ee-transport.js";
4
4
  export { bindWsHandler, createWebSocketTransportFactory, defaultWebSocketFactory, } from "./daemon-client-websocket-transport.js";
5
5
  //# sourceMappingURL=daemon-client-transport.d.ts.map
@@ -1,4 +1,4 @@
1
1
  export { decodeMessageData, describeTransportClose, describeTransportError, encodeUtf8String, extractRelayMessage, normalizeTransportPayload, safeRandomId, } from "./daemon-client-transport-utils.js";
2
- export { createEncryptedTransport, createRelayE2eeTransportFactory, } from "./daemon-client-relay-e2ee-transport.js";
2
+ export { createEncryptedTransport, createRelayE2eeTransportFactory, createRelayTransportFactory, } from "./daemon-client-relay-e2ee-transport.js";
3
3
  export { bindWsHandler, createWebSocketTransportFactory, defaultWebSocketFactory, } from "./daemon-client-websocket-transport.js";
4
4
  //# sourceMappingURL=daemon-client-transport.js.map
@@ -5,7 +5,7 @@ import type { z } from "zod";
5
5
  import type { SessionEventSubscription } from "@camerontaylor/paseo-protocol/messages";
6
6
  import type { ClientCapability } from "@camerontaylor/paseo-protocol/client-capabilities";
7
7
  import type { AgentAttentionNotificationPayload } from "@camerontaylor/paseo-protocol/agent-attention-notification";
8
- import { AgentRefreshedStatusPayloadSchema, CheckoutRenameBranchResponseSchema, RenameTerminalResponseSchema, RestartRequestedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, DaemonUpdateResponseSchema, type ActiveTurnBehavior, type ServerInfoStatusPayload } from "@camerontaylor/paseo-protocol/messages";
8
+ import { AgentRefreshedStatusPayloadSchema, CheckoutRenameBranchResponseSchema, CheckoutBaseRefSetResponseSchema, RenameTerminalResponseSchema, RestartRequestedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, DaemonUpdateResponseSchema, type ActiveTurnBehavior, type ServerInfoStatusPayload } from "@camerontaylor/paseo-protocol/messages";
9
9
  import type { AgentStreamEventPayload, AgentSnapshotPayload, ProjectPlacementPayload, AgentPermissionResolvedMessage, CreateAgentRequestMessage, CreatePaseoWorktreeRequest, FileDownloadTokenResponse, FileUploadResponse, FileExplorerResponse, FileVersion, FileWriteResult, FetchAgentTimelineResponseMessage, AgentForkContextResponseMessage, GitSetupOptions, CheckoutStatusResponse, CheckoutCommit, ParsedDiffFile, CheckoutCommitResponse, CheckoutMergeResponse, CheckoutMergeFromBaseResponse, CheckoutPullResponse, CheckoutPushResponse, CheckoutRefreshResponse, CheckoutPrCreateResponse, CheckoutPrMergeResponse, CheckoutPrMergeMethod, CheckoutForgeSetAutoMergeResponse, CheckoutGithubSetAutoMergeResponse, CheckoutForgeGetCheckDetailsResponse, CheckoutGithubGetCheckDetailsResponse, CheckoutPrStatusResponse, PullRequestTimelineResponse, CheckoutSwitchBranchResponse, StashSaveResponse, StashPopResponse, StashListResponse, ValidateBranchResponse, BranchSuggestionsResponse, ForgeSearchResponse, ForgeSearchRequest, GitHubSearchResponse, GitHubSearchRequest, DirectorySuggestionsResponse, PaseoWorktreeListResponse, PaseoWorktreeArchiveResponse, ProjectIconSource, ProjectIconResponse, ProjectIconGetResponse, ProjectAddResponse, ProjectCreateDirectoryResponse, OpenProjectResponseMessage, WorkspaceGithubSearchRepositoriesResponse, ProjectGithubCloneProtocol, ProjectGithubCloneResponse, ArchiveWorkspaceResponseMessage, WorkspaceSetupStatusResponseMessage, ListCommandsResponse, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ListProviderModesResponseMessage, ListAvailableProvidersResponse, GetProvidersSnapshotResponseMessage, RefreshProvidersSnapshotResponseMessage, ProviderDiagnosticResponseMessage, ProviderUsageListResponseMessage, DaemonGetStatusResponse, DaemonGetPairingOfferResponse, DaemonConfigReloadResponse, DiagnosticsResponse, AgentRewindResponseMessage, ListTerminalsResponse, CreateTerminalResponse, SubscribeTerminalResponse, SubscribeTerminalRequest, CloseItemsResponse, KillTerminalResponse, CaptureTerminalResponse, TerminalInput, SessionInboundMessage, SessionOutboundMessage, SendAgentMessageRequest, PaseoConfigRaw, PaseoConfigRevision, WorkspaceCreateRequest, WorkspaceRecoveryState, PluginListItem, PluginLogEntry, PluginSourceStatusItem, PluginSourceUpdateItem, PluginUpdateSelection, PluginUpdateProposal, PluginUpdatePreview, PluginUpdateResult, AgentSkillSelection, AgentSkillsStatus, AgentSkillsSaveResult } from "@camerontaylor/paseo-protocol/messages";
10
10
  import type { AgentPermissionRequest, AgentPermissionResponse, AgentPersistenceHandle, AgentProviderNotice, AgentProvider, AgentSessionConfig } from "@camerontaylor/paseo-protocol/agent-types";
11
11
  import type { AgentConfigApply, MutableDaemonConfig, MutableDaemonConfigPatch } from "@camerontaylor/paseo-protocol/messages";
@@ -30,6 +30,12 @@ export type ImportAgentInput = (ImportAgentInputBase & {
30
30
  provider: AgentProvider;
31
31
  sessionId: string;
32
32
  });
33
+ export type DaemonAuthFailureReason = "password_required" | "incorrect_password";
34
+ export declare class DaemonAuthenticationError extends Error {
35
+ readonly reason: DaemonAuthFailureReason;
36
+ constructor(reason: DaemonAuthFailureReason);
37
+ }
38
+ export declare function getDaemonAuthFailureReason(error: unknown): DaemonAuthFailureReason | null;
33
39
  export type { DaemonTransport, DaemonTransportFactory, WebSocketFactory, WebSocketLike, } from "./daemon-client-transport.js";
34
40
  export type { TerminalStreamEvent };
35
41
  export type ConnectionState = {
@@ -114,6 +120,7 @@ export interface DaemonClientConfig {
114
120
  appVersion?: string;
115
121
  runtimeGeneration?: number | null;
116
122
  password?: string;
123
+ localCredential?: () => string | undefined | Promise<string | undefined>;
117
124
  authHeader?: string;
118
125
  suppressSendErrors?: boolean;
119
126
  transportFactory?: DaemonTransportFactory;
@@ -211,6 +218,7 @@ type CheckoutPrStatusPayload = CheckoutPrStatusResponse["payload"];
211
218
  type PullRequestTimelinePayload = PullRequestTimelineResponse["payload"];
212
219
  type CheckoutSwitchBranchPayload = CheckoutSwitchBranchResponse["payload"];
213
220
  export type RenameBranchResult = z.infer<typeof CheckoutRenameBranchResponseSchema>["payload"];
221
+ export type SetCheckoutBaseRefResult = z.infer<typeof CheckoutBaseRefSetResponseSchema>["payload"];
214
222
  type StashSavePayload = StashSaveResponse["payload"];
215
223
  type StashPopPayload = StashPopResponse["payload"];
216
224
  type StashListPayload = StashListResponse["payload"];
@@ -529,6 +537,11 @@ export interface RenameBranchInput {
529
537
  branch: string;
530
538
  requestId?: string;
531
539
  }
540
+ export interface SetCheckoutBaseRefInput {
541
+ cwd: string;
542
+ baseRef: string;
543
+ requestId?: string;
544
+ }
532
545
  export interface RenameTerminalInput {
533
546
  terminalId: string;
534
547
  title: string;
@@ -578,6 +591,7 @@ export declare class DaemonClient {
578
591
  private readonly providerSnapshotUpdates;
579
592
  private readonly owned;
580
593
  private transport;
594
+ private helloAuth;
581
595
  private transportCleanup;
582
596
  private rawMessageListeners;
583
597
  private messageHandlers;
@@ -594,6 +608,7 @@ export declare class DaemonClient {
594
608
  private connectResolve;
595
609
  private connectReject;
596
610
  private lastErrorValue;
611
+ private authFailureReasonValue;
597
612
  private connectionState;
598
613
  private readonly terminalStreams;
599
614
  private pendingBinaryFileReads;
@@ -616,6 +631,7 @@ export declare class DaemonClient {
616
631
  constructor(config: DaemonClientConfig);
617
632
  connect(): Promise<void>;
618
633
  private attemptConnect;
634
+ private assertEncryptedRelayAuth;
619
635
  private resolveConnect;
620
636
  private rejectConnect;
621
637
  close(): Promise<void>;
@@ -628,6 +644,7 @@ export declare class DaemonClient {
628
644
  get isConnected(): boolean;
629
645
  get isConnecting(): boolean;
630
646
  get lastError(): string | null;
647
+ get authFailureReason(): DaemonAuthFailureReason | null;
631
648
  getLastLivenessRttMs(): number | null;
632
649
  subscribe(handler: DaemonEventHandler): () => void;
633
650
  subscribeRawMessages(handler: (message: SessionOutboundMessage) => void): () => void;
@@ -973,6 +990,7 @@ export declare class DaemonClient {
973
990
  }, requestId?: string): Promise<PullRequestTimelinePayload>;
974
991
  checkoutSwitchBranch(cwd: string, branch: string, requestId?: string): Promise<CheckoutSwitchBranchPayload>;
975
992
  renameBranch(input: RenameBranchInput): Promise<RenameBranchResult>;
993
+ setCheckoutBaseRef(input: SetCheckoutBaseRefInput): Promise<SetCheckoutBaseRefResult>;
976
994
  stashSave(cwd: string, options?: {
977
995
  branch?: string;
978
996
  }, requestId?: string): Promise<StashSavePayload>;
@@ -7,7 +7,7 @@ import { AgentCreateFailedStatusPayloadSchema, AgentCreatedStatusPayloadSchema,
7
7
  import { validateWSOutboundMessage } from "@camerontaylor/paseo-protocol/validation/ws-outbound";
8
8
  import { isRelayClientWebSocketUrl } from "@camerontaylor/paseo-protocol/daemon-endpoints";
9
9
  import { asUint8Array, decodeFileTransferFrame, encodeFileTransferFrame, decodeTerminalStreamFrame, FileTransferOpcode, TerminalStreamOpcode, } from "@camerontaylor/paseo-protocol/binary-frames/index";
10
- import { createRelayE2eeTransportFactory, createWebSocketTransportFactory, decodeMessageData, defaultWebSocketFactory, describeTransportClose, describeTransportError, } from "./daemon-client-transport.js";
10
+ import { createRelayE2eeTransportFactory, createRelayTransportFactory, createWebSocketTransportFactory, decodeMessageData, defaultWebSocketFactory, describeTransportClose, describeTransportError, } from "./daemon-client-transport.js";
11
11
  import { DaemonClientRuntimeMetrics } from "./daemon-client-runtime-metrics.js";
12
12
  import { normalizeListProviderModelsPayload, normalizeProviderSnapshotUpdateMessage, normalizeProvidersSnapshotPayload, } from "./compat/normalize-provider-models.js";
13
13
  import { TerminalStreamRouter } from "./terminal-stream-router.js";
@@ -27,6 +27,55 @@ function normalizePassword(value) {
27
27
  }
28
28
  return value.length > 0 ? value : null;
29
29
  }
30
+ function compatibleBearerPassword(password) {
31
+ return password && /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(password) ? password : null;
32
+ }
33
+ export class DaemonAuthenticationError extends Error {
34
+ constructor(reason) {
35
+ super(reason === "password_required" ? "Password required" : "Incorrect password");
36
+ this.name = "DaemonAuthenticationError";
37
+ this.reason = reason;
38
+ }
39
+ }
40
+ export function getDaemonAuthFailureReason(error) {
41
+ return error instanceof DaemonAuthenticationError ? error.reason : null;
42
+ }
43
+ function authFailureFromLegacyClose(event) {
44
+ if (!event || typeof event !== "object" || !("reason" in event))
45
+ return null;
46
+ if (event.reason === "Password required")
47
+ return "password_required";
48
+ if (event.reason === "Incorrect password")
49
+ return "incorrect_password";
50
+ return null;
51
+ }
52
+ function chooseConnectionAuth(config, localCredential) {
53
+ const password = normalizePassword(config.password);
54
+ let helloAuth;
55
+ if (localCredential)
56
+ helloAuth = { kind: "localCredential", token: localCredential };
57
+ else if (password)
58
+ helloAuth = { kind: "password", password };
59
+ const headers = {};
60
+ const compatibleBearer = localCredential ? null : compatibleBearerPassword(password);
61
+ // COMPAT(headerAuth): added in v0.9.1, remove after 2027-03-24.
62
+ if (compatibleBearer)
63
+ headers.Authorization = `Bearer ${compatibleBearer}`;
64
+ else if (!localCredential && config.authHeader)
65
+ headers.Authorization = config.authHeader;
66
+ return {
67
+ helloAuth,
68
+ headers,
69
+ ...(compatibleBearer ? { protocols: [`paseo.bearer.${compatibleBearer}`] } : {}),
70
+ };
71
+ }
72
+ function resolveConnectionAuth(config) {
73
+ const resolution = config.localCredential?.();
74
+ if (resolution instanceof Promise) {
75
+ return resolution.then((credential) => chooseConnectionAuth(config, credential));
76
+ }
77
+ return chooseConnectionAuth(config, resolution);
78
+ }
30
79
  function extractCorrelatedResponseIdentity(input) {
31
80
  if (!input || typeof input !== "object") {
32
81
  return null;
@@ -279,6 +328,7 @@ export class DaemonClient {
279
328
  this.connectResolve = null;
280
329
  this.connectReject = null;
281
330
  this.lastErrorValue = null;
331
+ this.authFailureReasonValue = null;
282
332
  this.connectionState = { status: "idle" };
283
333
  this.terminalStreams = new TerminalStreamRouter();
284
334
  this.pendingBinaryFileReads = new Map();
@@ -393,7 +443,7 @@ export class DaemonClient {
393
443
  });
394
444
  return this.connectPromise;
395
445
  }
396
- attemptConnect() {
446
+ async attemptConnect() {
397
447
  if (this.connectionState.status === "disposed") {
398
448
  this.rejectConnect(new Error("Daemon client is disposed"));
399
449
  return;
@@ -411,30 +461,30 @@ export class DaemonClient {
411
461
  clearTimeout(this.reconnectTimeout);
412
462
  this.reconnectTimeout = null;
413
463
  }
414
- const headers = {};
415
- const password = normalizePassword(this.config.password);
416
- if (password) {
417
- headers.Authorization = `Bearer ${password}`;
418
- }
419
- else if (this.config.authHeader) {
420
- headers.Authorization = this.config.authHeader;
421
- }
422
- const protocols = password ? [`paseo.bearer.${password}`] : undefined;
423
464
  try {
465
+ const resolution = resolveConnectionAuth(this.config);
466
+ const selected = resolution instanceof Promise ? await resolution : resolution;
467
+ if (!this.shouldReconnect)
468
+ return;
469
+ this.helloAuth = selected.helloAuth;
424
470
  // Reconnect can overlap with browser close/error delivery ordering.
425
471
  // Always dispose previous transport before constructing the next one.
426
472
  this.disposeTransport();
427
473
  const baseTransportFactory = this.config.transportFactory ??
428
474
  createWebSocketTransportFactory(this.config.webSocketFactory ?? defaultWebSocketFactory);
429
- const shouldUseRelayE2ee = this.config.e2ee?.enabled === true && isRelayClientWebSocketUrl(this.config.url);
430
- let transportFactory = baseTransportFactory;
475
+ const isRelayTransport = isRelayClientWebSocketUrl(this.config.url);
476
+ const shouldUseRelayE2ee = this.config.e2ee?.enabled === true && isRelayTransport;
477
+ this.assertEncryptedRelayAuth(selected.helloAuth, isRelayTransport);
478
+ let transportFactory = isRelayTransport
479
+ ? createRelayTransportFactory(baseTransportFactory)
480
+ : baseTransportFactory;
431
481
  if (shouldUseRelayE2ee) {
432
482
  const daemonPublicKeyB64 = this.config.e2ee?.daemonPublicKeyB64;
433
483
  if (!daemonPublicKeyB64) {
434
484
  throw new Error("daemonPublicKeyB64 is required for relay E2EE");
435
485
  }
436
486
  transportFactory = createRelayE2eeTransportFactory({
437
- baseFactory: baseTransportFactory,
487
+ baseFactory: transportFactory,
438
488
  daemonPublicKeyB64,
439
489
  logger: this.logger,
440
490
  });
@@ -442,11 +492,12 @@ export class DaemonClient {
442
492
  const transportUrl = this.resolveTransportUrlForAttempt();
443
493
  const transport = transportFactory({
444
494
  url: transportUrl,
445
- headers,
446
- ...(protocols ? { protocols } : {}),
495
+ headers: selected.headers,
496
+ ...(selected.protocols ? { protocols: selected.protocols } : {}),
447
497
  });
448
498
  this.transport = transport;
449
499
  this.lastServerInfoMessage = null;
500
+ this.authFailureReasonValue = null;
450
501
  this.updateConnectionState({
451
502
  status: "connecting",
452
503
  attempt: this.reconnectAttempt,
@@ -472,7 +523,7 @@ export class DaemonClient {
472
523
  this.pendingGenericTransportErrorTimeout = null;
473
524
  }
474
525
  this.lastErrorValue = null;
475
- this.sendHelloMessage();
526
+ void this.sendHelloMessage();
476
527
  }),
477
528
  transport.onClose((event) => {
478
529
  this.resetConnectTimeout();
@@ -480,7 +531,10 @@ export class DaemonClient {
480
531
  clearTimeout(this.pendingGenericTransportErrorTimeout);
481
532
  this.pendingGenericTransportErrorTimeout = null;
482
533
  }
483
- const reason = describeTransportClose(event);
534
+ this.authFailureReasonValue ?? (this.authFailureReasonValue = authFailureFromLegacyClose(event));
535
+ const reason = this.authFailureReasonValue
536
+ ? new DaemonAuthenticationError(this.authFailureReasonValue).message
537
+ : describeTransportClose(event);
484
538
  if (reason) {
485
539
  this.lastErrorValue = reason;
486
540
  }
@@ -544,6 +598,12 @@ export class DaemonClient {
544
598
  this.rejectConnect(error instanceof Error ? error : new Error(message));
545
599
  }
546
600
  }
601
+ assertEncryptedRelayAuth(helloAuth, isRelayTransport) {
602
+ if (!isRelayTransport || !helloAuth || this.config.e2ee?.enabled === true)
603
+ return;
604
+ this.setReconnectEnabled(false);
605
+ throw new Error("Relay credentials require E2EE");
606
+ }
547
607
  resolveConnect() {
548
608
  if (this.connectResolve) {
549
609
  this.connectResolve();
@@ -653,6 +713,9 @@ export class DaemonClient {
653
713
  get lastError() {
654
714
  return this.lastErrorValue;
655
715
  }
716
+ get authFailureReason() {
717
+ return this.authFailureReasonValue;
718
+ }
656
719
  getLastLivenessRttMs() {
657
720
  return this.lastLivenessRttMs;
658
721
  }
@@ -2898,6 +2961,17 @@ export class DaemonClient {
2898
2961
  responseType: "checkout.rename_branch.response",
2899
2962
  });
2900
2963
  }
2964
+ async setCheckoutBaseRef(input) {
2965
+ return this.sendCorrelatedSessionRequest({
2966
+ requestId: input.requestId,
2967
+ message: {
2968
+ type: "checkout.base_ref.set.request",
2969
+ cwd: input.cwd,
2970
+ baseRef: input.baseRef,
2971
+ },
2972
+ responseType: "checkout.base_ref.set.response",
2973
+ });
2974
+ }
2901
2975
  async stashSave(cwd, options, requestId) {
2902
2976
  return this.sendCorrelatedSessionRequest({
2903
2977
  requestId,
@@ -4174,8 +4248,9 @@ export class DaemonClient {
4174
4248
  resolveTransportUrlForAttempt() {
4175
4249
  return this.config.url;
4176
4250
  }
4177
- sendHelloMessage() {
4178
- if (!this.transport) {
4251
+ async sendHelloMessage() {
4252
+ const transport = this.transport;
4253
+ if (!transport) {
4179
4254
  this.scheduleReconnect({
4180
4255
  reason: "Transport unavailable before hello",
4181
4256
  event: "HELLO_TRANSPORT_MISSING",
@@ -4184,11 +4259,15 @@ export class DaemonClient {
4184
4259
  return;
4185
4260
  }
4186
4261
  try {
4262
+ if (this.transport !== transport)
4263
+ return;
4264
+ const auth = this.helloAuth;
4187
4265
  this.sendJsonMessage("hello", "hello", {
4188
4266
  type: "hello",
4189
4267
  clientId: this.config.clientId,
4190
4268
  clientType: this.config.clientType ?? "cli",
4191
4269
  protocolVersion: 1,
4270
+ ...(auth ? { auth } : {}),
4192
4271
  capabilities: {
4193
4272
  ...DEFAULT_CLIENT_CAPABILITIES,
4194
4273
  // COMPAT(sideConversations): added in 0.7.0-beta.2.fork.1, fork-only.
@@ -4322,6 +4401,17 @@ export class DaemonClient {
4322
4401
  this.runtimeMetrics?.recordMessage("pong", bytes, perfNow() - startMs);
4323
4402
  return;
4324
4403
  }
4404
+ if (parsed.data.type === "hello.rejected") {
4405
+ const reasonMessage = {
4406
+ password_required: "Password required",
4407
+ incorrect_password: "Incorrect password",
4408
+ incompatible_protocol: "Incompatible protocol version",
4409
+ };
4410
+ this.lastErrorValue = reasonMessage[parsed.data.reason];
4411
+ this.authFailureReasonValue =
4412
+ parsed.data.reason === "incompatible_protocol" ? null : parsed.data.reason;
4413
+ return;
4414
+ }
4325
4415
  this.traceInstant("paseo.ws.message.inbound", {
4326
4416
  envelopeType: "session",
4327
4417
  messageType: parsed.data.message.type,
@@ -4494,13 +4584,17 @@ export class DaemonClient {
4494
4584
  this.rejectPingProbe(new DaemonConnectionError(reason ?? "Connection lost"));
4495
4585
  this.terminalStreams.clearSlots();
4496
4586
  this.lastServerInfoMessage = null;
4587
+ if (this.authFailureReasonValue)
4588
+ this.setReconnectEnabled(false);
4497
4589
  if (wasDisposed) {
4498
4590
  this.rejectConnect(new Error(reason ?? "Daemon client is disposed"));
4499
4591
  return;
4500
4592
  }
4501
4593
  this.emitDisconnectedStateForReconnect(reason, input);
4502
4594
  if (!this.shouldReconnect || this.config.reconnect?.enabled === false) {
4503
- this.rejectConnect(new Error(reason ?? "Transport disconnected before connect"));
4595
+ this.rejectConnect(this.authFailureReasonValue
4596
+ ? new DaemonAuthenticationError(this.authFailureReasonValue)
4597
+ : new Error(reason ?? "Transport disconnected before connect"));
4504
4598
  return;
4505
4599
  }
4506
4600
  this.armReconnectTimer();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camerontaylor/paseo-client",
3
- "version": "0.9.1-fork.1",
3
+ "version": "0.10.2-fork.1",
4
4
  "description": "Paseo client SDK package",
5
5
  "files": [
6
6
  "dist",
@@ -39,8 +39,8 @@
39
39
  "test": "vitest run"
40
40
  },
41
41
  "dependencies": {
42
- "@camerontaylor/paseo-protocol": "0.9.1-fork.1",
43
- "@camerontaylor/paseo-relay": "0.9.1-fork.1",
42
+ "@camerontaylor/paseo-protocol": "0.10.2-fork.1",
43
+ "@camerontaylor/paseo-relay": "0.10.2-fork.1",
44
44
  "zod": "^4.4.3"
45
45
  },
46
46
  "devDependencies": {