doordash-cli 0.4.0 → 0.4.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.
@@ -1,6 +1,6 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { bootstrapAuthSessionWithDeps, buildAddConsumerAddressPayload, buildAddToCartPayload, buildUpdateCartPayload, extractExistingOrdersFromApolloCache, normalizeItemName, parseExistingOrderLifecycleStatus, parseExistingOrdersResponse, parseOptionSelectionsJson, parseSearchRestaurantRow, resolveAttachedBrowserCdpCandidates, resolveAvailableAddressMatch, resolveSystemBrowserOpenCommand, selectAttachedBrowserImportMode, } from "./direct-api.js";
3
+ import { bootstrapAuthSessionWithDeps, buildAddConsumerAddressPayload, buildAddToCartPayload, buildUpdateCartPayload, extractExistingOrdersFromApolloCache, normalizeItemName, parseExistingOrderLifecycleStatus, parseExistingOrdersResponse, parseOptionSelectionsJson, parseSearchRestaurantRow, preferredBrowserSessionImportStrategies, resolveAttachedBrowserCdpCandidates, resolveAvailableAddressMatch, resolveSystemBrowserOpenCommand, selectAttachedBrowserImportMode, summarizeDesktopBrowserReuseGap, } from "./direct-api.js";
4
4
  function configurableItemDetail() {
5
5
  return {
6
6
  success: true,
@@ -181,6 +181,35 @@ test("resolveSystemBrowserOpenCommand stays generic across operating systems", (
181
181
  args: ["/c", "start", "", "https://www.doordash.com/home"],
182
182
  });
183
183
  });
184
+ test("summarizeDesktopBrowserReuseGap explains why a running Brave session still was not reusable", () => {
185
+ const message = summarizeDesktopBrowserReuseGap({
186
+ processCommands: [
187
+ "/bin/bash /usr/bin/brave-browser-stable",
188
+ "/opt/brave.com/brave/brave",
189
+ "/opt/brave.com/brave/brave --type=renderer",
190
+ ],
191
+ hasAnyDevToolsActivePort: false,
192
+ });
193
+ assert.match(message ?? "", /Brave is already running on this desktop/i);
194
+ assert.match(message ?? "", /couldn't reuse it automatically/i);
195
+ assert.match(message ?? "", /attachable browser automation session/i);
196
+ assert.match(message ?? "", /no importable signed-in DoorDash browser profile state was found/i);
197
+ });
198
+ test("summarizeDesktopBrowserReuseGap stays quiet once the browser exposes attach signals", () => {
199
+ assert.equal(summarizeDesktopBrowserReuseGap({
200
+ processCommands: ["/bin/bash /usr/bin/brave-browser-stable --remote-debugging-port=9222"],
201
+ hasAnyDevToolsActivePort: false,
202
+ }), null);
203
+ assert.equal(summarizeDesktopBrowserReuseGap({
204
+ processCommands: ["/bin/bash /usr/bin/brave-browser-stable"],
205
+ hasAnyDevToolsActivePort: true,
206
+ }), null);
207
+ });
208
+ test("preferredBrowserSessionImportStrategies prefers same-machine linux profile imports before CDP attach", () => {
209
+ assert.deepEqual(preferredBrowserSessionImportStrategies("linux"), ["local-linux-chromium-profile", "attached-browser-cdp"]);
210
+ assert.deepEqual(preferredBrowserSessionImportStrategies("darwin"), ["attached-browser-cdp"]);
211
+ assert.deepEqual(preferredBrowserSessionImportStrategies("win32"), ["attached-browser-cdp"]);
212
+ });
184
213
  test("selectAttachedBrowserImportMode treats an authenticated browser with DoorDash cookies as an immediate import candidate", () => {
185
214
  assert.equal(selectAttachedBrowserImportMode({
186
215
  pageUrls: ["https://github.com/LatencyTDH/doordash-cli/pulls"],
@@ -229,6 +258,7 @@ test("bootstrapAuthSessionWithDeps returns immediately when saved local auth is
229
258
  getReachableCdpCandidates: async () => {
230
259
  throw new Error("should not probe reachability when saved auth is already valid");
231
260
  },
261
+ describeDesktopBrowserReuseGap: async () => null,
232
262
  openUrlInAttachedBrowser: async () => {
233
263
  throw new Error("should not try to open an attached browser when saved auth is already valid");
234
264
  },
@@ -243,6 +273,7 @@ test("bootstrapAuthSessionWithDeps returns immediately when saved local auth is
243
273
  waitForManagedBrowserLogin: async () => {
244
274
  throw new Error("should not launch a managed browser when saved auth is already valid");
245
275
  },
276
+ canPromptForManagedBrowserConfirmation: () => false,
246
277
  checkAuthDirect: async () => {
247
278
  throw new Error("should not re-check auth through the live session when saved auth is already valid");
248
279
  },
@@ -288,6 +319,7 @@ test("bootstrapAuthSessionWithDeps returns immediately when an attached browser
288
319
  getReachableCdpCandidates: async () => {
289
320
  throw new Error("should not probe reachability on immediate browser-session import");
290
321
  },
322
+ describeDesktopBrowserReuseGap: async () => null,
291
323
  openUrlInAttachedBrowser: async () => {
292
324
  throw new Error("should not open a browser when immediate browser-session import succeeded");
293
325
  },
@@ -302,6 +334,7 @@ test("bootstrapAuthSessionWithDeps returns immediately when an attached browser
302
334
  waitForManagedBrowserLogin: async () => {
303
335
  throw new Error("should not launch a managed browser when immediate browser-session import succeeded");
304
336
  },
337
+ canPromptForManagedBrowserConfirmation: () => false,
305
338
  checkAuthDirect: async () => auth,
306
339
  log: (message) => {
307
340
  logs.push(message);
@@ -343,6 +376,7 @@ test("bootstrapAuthSessionWithDeps opens a watchable attached browser session be
343
376
  reachableCalls += 1;
344
377
  return candidates;
345
378
  },
379
+ describeDesktopBrowserReuseGap: async () => null,
346
380
  openUrlInAttachedBrowser: async () => {
347
381
  openAttachedCalls += 1;
348
382
  return true;
@@ -359,6 +393,7 @@ test("bootstrapAuthSessionWithDeps opens a watchable attached browser session be
359
393
  waitForManagedBrowserLogin: async () => {
360
394
  throw new Error("should not launch a managed browser when an attached browser is reachable");
361
395
  },
396
+ canPromptForManagedBrowserConfirmation: () => false,
362
397
  checkAuthDirect: async () => auth,
363
398
  log: (message) => {
364
399
  logs.push(message);
@@ -369,11 +404,11 @@ test("bootstrapAuthSessionWithDeps opens a watchable attached browser session be
369
404
  assert.equal(openDefaultCalls, 0);
370
405
  assert.equal(waitCalls, 1);
371
406
  assert.equal(waitTimeoutMs, 180_000);
372
- assert.match(logs.join("\n"), /Opened DoorDash in the reusable browser session I'm watching/);
373
- assert.match(logs.join("\n"), /Detected 1 reusable browser connection/);
407
+ assert.match(logs.join("\n"), /Opened DoorDash in the attachable browser session I'm watching/);
408
+ assert.match(logs.join("\n"), /Detected 1 attachable browser session/);
374
409
  assert.match(result.message, /saved it for direct API use/);
375
410
  });
376
- test("bootstrapAuthSessionWithDeps falls back to a managed browser login window when no reusable browser connection is discoverable", async () => {
411
+ test("bootstrapAuthSessionWithDeps falls back to a managed browser login window and auto-completes when it can prove login", async () => {
377
412
  const auth = {
378
413
  success: true,
379
414
  isLoggedIn: true,
@@ -396,6 +431,7 @@ test("bootstrapAuthSessionWithDeps falls back to a managed browser login window
396
431
  markBrowserImportAttempted: () => { },
397
432
  getAttachedBrowserCdpCandidates: async () => ["http://127.0.0.1:9222"],
398
433
  getReachableCdpCandidates: async () => [],
434
+ describeDesktopBrowserReuseGap: async () => null,
399
435
  openUrlInAttachedBrowser: async () => false,
400
436
  openUrlInDefaultBrowser: async () => true,
401
437
  waitForAttachedBrowserSessionImport: async () => {
@@ -404,8 +440,13 @@ test("bootstrapAuthSessionWithDeps falls back to a managed browser login window
404
440
  },
405
441
  waitForManagedBrowserLogin: async () => {
406
442
  managedCalls += 1;
407
- return auth;
443
+ return {
444
+ status: "completed",
445
+ completion: "automatic",
446
+ auth,
447
+ };
408
448
  },
449
+ canPromptForManagedBrowserConfirmation: () => true,
409
450
  checkAuthDirect: async () => auth,
410
451
  log: (message) => {
411
452
  logs.push(message);
@@ -414,10 +455,143 @@ test("bootstrapAuthSessionWithDeps falls back to a managed browser login window
414
455
  assert.equal(managedCalls, 1);
415
456
  assert.equal(attachedWaitCalls, 0);
416
457
  assert.match(logs.join("\n"), /temporary Chromium login window/);
417
- assert.match(result.message, /temporary Chromium login window/);
458
+ assert.match(logs.join("\n"), /press Enter here to force an immediate recheck/i);
459
+ assert.match(result.message, /detected the signed-in session there automatically/i);
460
+ assert.equal(result.success, true);
461
+ assert.equal(result.isLoggedIn, true);
462
+ });
463
+ test("bootstrapAuthSessionWithDeps logs why an already-open desktop browser still is not reusable", async () => {
464
+ const auth = {
465
+ success: true,
466
+ isLoggedIn: true,
467
+ email: "user@example.com",
468
+ firstName: "Test",
469
+ lastName: "User",
470
+ consumerId: "consumer-1",
471
+ marketId: "market-1",
472
+ defaultAddress: null,
473
+ cookiesPath: "/tmp/cookies.json",
474
+ storageStatePath: "/tmp/storage-state.json",
475
+ };
476
+ const logs = [];
477
+ const result = await bootstrapAuthSessionWithDeps({
478
+ clearBlockedBrowserImport: async () => { },
479
+ checkPersistedAuth: async () => null,
480
+ importBrowserSessionIfAvailable: async () => false,
481
+ markBrowserImportAttempted: () => { },
482
+ getAttachedBrowserCdpCandidates: async () => [],
483
+ getReachableCdpCandidates: async () => [],
484
+ describeDesktopBrowserReuseGap: async () => "I can see Brave is already running on this desktop, but it is not exposing an attachable browser automation session right now.",
485
+ openUrlInAttachedBrowser: async () => false,
486
+ openUrlInDefaultBrowser: async () => true,
487
+ waitForAttachedBrowserSessionImport: async () => false,
488
+ waitForManagedBrowserLogin: async () => ({
489
+ status: "completed",
490
+ completion: "automatic",
491
+ auth,
492
+ }),
493
+ canPromptForManagedBrowserConfirmation: () => true,
494
+ checkAuthDirect: async () => auth,
495
+ log: (message) => {
496
+ logs.push(message);
497
+ },
498
+ });
499
+ assert.match(logs.join("\n"), /Brave is already running on this desktop/i);
500
+ assert.match(logs.join("\n"), /couldn't find an attachable browser session I can reuse/i);
418
501
  assert.equal(result.success, true);
419
502
  assert.equal(result.isLoggedIn, true);
420
503
  });
504
+ test("bootstrapAuthSessionWithDeps restores an explicit Enter-style completion path for the managed browser fallback", async () => {
505
+ const auth = {
506
+ success: true,
507
+ isLoggedIn: true,
508
+ email: "user@example.com",
509
+ firstName: "Test",
510
+ lastName: "User",
511
+ consumerId: "consumer-1",
512
+ marketId: "market-1",
513
+ defaultAddress: null,
514
+ cookiesPath: "/tmp/cookies.json",
515
+ storageStatePath: "/tmp/storage-state.json",
516
+ };
517
+ const logs = [];
518
+ const result = await bootstrapAuthSessionWithDeps({
519
+ clearBlockedBrowserImport: async () => { },
520
+ checkPersistedAuth: async () => null,
521
+ importBrowserSessionIfAvailable: async () => false,
522
+ markBrowserImportAttempted: () => { },
523
+ getAttachedBrowserCdpCandidates: async () => [],
524
+ getReachableCdpCandidates: async () => [],
525
+ describeDesktopBrowserReuseGap: async () => null,
526
+ openUrlInAttachedBrowser: async () => false,
527
+ openUrlInDefaultBrowser: async () => true,
528
+ waitForAttachedBrowserSessionImport: async () => false,
529
+ waitForManagedBrowserLogin: async () => ({
530
+ status: "completed",
531
+ completion: "manual",
532
+ auth,
533
+ }),
534
+ canPromptForManagedBrowserConfirmation: () => true,
535
+ checkAuthDirect: async () => auth,
536
+ log: (message) => {
537
+ logs.push(message);
538
+ },
539
+ });
540
+ assert.match(logs.join("\n"), /press Enter here to force an immediate recheck/i);
541
+ assert.match(result.message, /After you pressed Enter to confirm the browser login was complete/i);
542
+ assert.equal(result.success, true);
543
+ assert.equal(result.isLoggedIn, true);
544
+ });
545
+ test("bootstrapAuthSessionWithDeps returns a bounded failure instead of a dead-end when managed browser auto-detection cannot prove login", async () => {
546
+ const auth = {
547
+ success: true,
548
+ isLoggedIn: false,
549
+ email: null,
550
+ firstName: null,
551
+ lastName: null,
552
+ consumerId: null,
553
+ marketId: null,
554
+ defaultAddress: null,
555
+ cookiesPath: "/tmp/cookies.json",
556
+ storageStatePath: "/tmp/storage-state.json",
557
+ };
558
+ let attachedWaitCalls = 0;
559
+ let attachedWaitTimeoutMs = 0;
560
+ const logs = [];
561
+ const result = await bootstrapAuthSessionWithDeps({
562
+ clearBlockedBrowserImport: async () => { },
563
+ checkPersistedAuth: async () => null,
564
+ importBrowserSessionIfAvailable: async () => false,
565
+ markBrowserImportAttempted: () => { },
566
+ getAttachedBrowserCdpCandidates: async () => ["http://127.0.0.1:9222"],
567
+ getReachableCdpCandidates: async () => [],
568
+ describeDesktopBrowserReuseGap: async () => null,
569
+ openUrlInAttachedBrowser: async () => false,
570
+ openUrlInDefaultBrowser: async () => true,
571
+ waitForAttachedBrowserSessionImport: async (input) => {
572
+ attachedWaitCalls += 1;
573
+ attachedWaitTimeoutMs = input.timeoutMs;
574
+ return false;
575
+ },
576
+ waitForManagedBrowserLogin: async () => ({
577
+ status: "timed-out",
578
+ auth,
579
+ }),
580
+ canPromptForManagedBrowserConfirmation: () => true,
581
+ checkAuthDirect: async () => auth,
582
+ log: (message) => {
583
+ logs.push(message);
584
+ },
585
+ });
586
+ assert.equal(attachedWaitCalls, 0);
587
+ assert.equal(attachedWaitTimeoutMs, 0);
588
+ assert.match(logs.join("\n"), /temporary Chromium login window/i);
589
+ assert.match(logs.join("\n"), /press Enter here to force an immediate recheck/i);
590
+ assert.equal(result.success, false);
591
+ assert.equal(result.isLoggedIn, false);
592
+ assert.match(result.message, /couldn't prove an authenticated DoorDash session/i);
593
+ assert.match(result.message, /press Enter sooner next time/i);
594
+ });
421
595
  test("bootstrapAuthSessionWithDeps falls back to quick troubleshooting guidance when the managed browser login window cannot launch", async () => {
422
596
  const auth = {
423
597
  success: true,
@@ -441,6 +615,7 @@ test("bootstrapAuthSessionWithDeps falls back to quick troubleshooting guidance
441
615
  markBrowserImportAttempted: () => { },
442
616
  getAttachedBrowserCdpCandidates: async () => ["http://127.0.0.1:9222"],
443
617
  getReachableCdpCandidates: async () => [],
618
+ describeDesktopBrowserReuseGap: async () => null,
444
619
  openUrlInAttachedBrowser: async () => false,
445
620
  openUrlInDefaultBrowser: async () => true,
446
621
  waitForAttachedBrowserSessionImport: async (input) => {
@@ -448,7 +623,8 @@ test("bootstrapAuthSessionWithDeps falls back to quick troubleshooting guidance
448
623
  attachedWaitTimeoutMs = input.timeoutMs;
449
624
  return false;
450
625
  },
451
- waitForManagedBrowserLogin: async () => null,
626
+ waitForManagedBrowserLogin: async () => ({ status: "launch-failed" }),
627
+ canPromptForManagedBrowserConfirmation: () => true,
452
628
  checkAuthDirect: async () => auth,
453
629
  log: (message) => {
454
630
  logs.push(message);
@@ -460,7 +636,53 @@ test("bootstrapAuthSessionWithDeps falls back to quick troubleshooting guidance
460
636
  assert.match(logs.join("\n"), /won't keep you waiting for the full login timeout/i);
461
637
  assert.equal(result.success, false);
462
638
  assert.equal(result.isLoggedIn, false);
463
- assert.match(result.message, /still isn't exposing a reusable browser session/);
639
+ assert.match(result.message, /still isn't exposing an attachable browser session/);
640
+ });
641
+ test("bootstrapAuthSessionWithDeps clears the logout block before an explicit login reuses an attached browser session", async () => {
642
+ const auth = {
643
+ success: true,
644
+ isLoggedIn: true,
645
+ email: "user@example.com",
646
+ firstName: "Test",
647
+ lastName: "User",
648
+ consumerId: "consumer-1",
649
+ marketId: "market-1",
650
+ defaultAddress: null,
651
+ cookiesPath: "/tmp/cookies.json",
652
+ storageStatePath: "/tmp/storage-state.json",
653
+ };
654
+ let blocked = true;
655
+ let clearCalls = 0;
656
+ let importCalls = 0;
657
+ const result = await bootstrapAuthSessionWithDeps({
658
+ clearBlockedBrowserImport: async () => {
659
+ clearCalls += 1;
660
+ blocked = false;
661
+ },
662
+ checkPersistedAuth: async () => null,
663
+ importBrowserSessionIfAvailable: async () => {
664
+ importCalls += 1;
665
+ return blocked === false;
666
+ },
667
+ markBrowserImportAttempted: () => { },
668
+ getAttachedBrowserCdpCandidates: async () => [],
669
+ getReachableCdpCandidates: async () => [],
670
+ describeDesktopBrowserReuseGap: async () => null,
671
+ openUrlInAttachedBrowser: async () => false,
672
+ openUrlInDefaultBrowser: async () => false,
673
+ waitForAttachedBrowserSessionImport: async () => false,
674
+ waitForManagedBrowserLogin: async () => {
675
+ throw new Error("should not launch a managed browser when explicit login can immediately reuse an attached browser session");
676
+ },
677
+ canPromptForManagedBrowserConfirmation: () => false,
678
+ checkAuthDirect: async () => auth,
679
+ log: () => { },
680
+ });
681
+ assert.equal(clearCalls, 1);
682
+ assert.equal(importCalls, 1);
683
+ assert.equal(result.success, true);
684
+ assert.equal(result.isLoggedIn, true);
685
+ assert.match(result.message, /Imported an existing signed-in browser session/);
464
686
  });
465
687
  test("parseOptionSelectionsJson parses structured recursive option selections", () => {
466
688
  assert.deepEqual(parseOptionSelectionsJson('[{"groupId":"703393388","optionId":"4716032529"},{"groupId":"recommended_option_546935995","optionId":"546936011","children":[{"groupId":"780057412","optionId":"4702669757","quantity":2}]}]'), [
package/docs/examples.md CHANGED
@@ -26,13 +26,13 @@ Check whether you already have reusable session state:
26
26
  doordash-cli auth-check
27
27
  ```
28
28
 
29
- If your saved local state is still valid, this exits immediately. Otherwise it tries to reuse a discoverable signed-in browser session, or opens a temporary Chromium login window and saves the session there:
29
+ If your saved local state is still valid, this exits immediately. Otherwise it first tries to reuse same-machine Linux Brave/Chrome profile state, then a discoverable attachable signed-in browser session, and finally falls back to a temporary Chromium login window the CLI can watch directly. In that temporary-browser fallback, the CLI keeps checking automatically and you can also press Enter in the terminal to force an immediate recheck once the page shows you are signed in:
30
30
 
31
31
  ```bash
32
32
  doordash-cli login
33
33
  ```
34
34
 
35
- Reset saved session state when you want a clean logged-out start. This also disables automatic browser-session reuse until your next explicit `doordash-cli login`:
35
+ Reset saved session state when you want a clean logged-out start. This also disables passive browser-session reuse until your next explicit `doordash-cli login`:
36
36
 
37
37
  ```bash
38
38
  doordash-cli logout
package/docs/install.md CHANGED
@@ -60,14 +60,14 @@ doordash-cli search --query sushi
60
60
 
61
61
  ## Login and session reuse
62
62
 
63
- `doordash-cli login` reuses saved local auth when it is still valid. Otherwise it tries to import a discoverable signed-in browser session. If neither is available, it opens a temporary Chromium login window and saves the session there. If authentication still is not established, `login` exits non-zero.
63
+ `doordash-cli login` reuses saved local auth when it is still valid. Otherwise it first tries to import signed-in same-machine Linux Brave/Chrome profile state, then falls back to a discoverable attachable signed-in browser session, and finally opens a temporary Chromium login window it can watch directly. If authentication still is not established, `login` exits non-zero.
64
64
 
65
- `doordash-cli auth-check` can also quietly import a discoverable signed-in browser session unless `doordash-cli logout` disabled that auto-reuse.
65
+ `doordash-cli auth-check` can also quietly import same-machine Linux Brave/Chrome profile state or a discoverable attachable signed-in browser session unless `doordash-cli logout` disabled that auto-reuse.
66
66
 
67
- `doordash-cli logout` clears persisted cookies and stored browser state, then keeps automatic browser-session reuse disabled until you explicitly run `doordash-cli login` again.
67
+ `doordash-cli logout` clears persisted cookies and stored browser state, then keeps passive browser-session reuse disabled until your next explicit `doordash-cli login` attempt.
68
68
 
69
69
  ## Browser-session troubleshooting
70
70
 
71
- Normally you should not need to think about browser plumbing. If `doordash-cli login` opens a temporary Chromium window, finish signing in there and let the CLI save the session.
71
+ Normally you should not need to think about browser plumbing. If `doordash-cli login` opens a temporary Chromium window, finish signing in there and let the CLI save the session. The CLI keeps checking automatically, and if the page already shows you are signed in but the command has not finished yet, press Enter in the terminal to force an immediate recheck.
72
72
 
73
- If you expected reuse from another browser instead, make sure that browser exposes a compatible CDP endpoint, then rerun `doordash-cli login`.
73
+ On Linux, the preferred reuse path is a signed-in local Brave or Google Chrome profile on the same machine, which does not need CDP/remote debugging. If that same-machine profile import is unavailable or not signed in, the next reuse path is an attachable browser automation session.
package/man/dd-cli.1 CHANGED
@@ -35,20 +35,25 @@ local browser, including the temporary login-window fallback.
35
35
  .TP
36
36
  .B auth-check
37
37
  Verify whether the saved session appears authenticated. This command can also
38
- quietly reuse or import an already-signed-in browser session when one is
39
- available, unless
38
+ quietly reuse or import same-machine Linux Brave/Chrome browser profile state
39
+ or an already-signed-in attachable browser session when one is available,
40
+ unless
40
41
  .B logout
41
42
  explicitly disabled that auto-reuse.
42
43
  .TP
43
44
  .B login
44
- Reuse saved local auth when possible. Otherwise try to import a discoverable
45
- signed-in browser session, or open a temporary Chromium login window and save
46
- that session there. If authentication is still not established,
45
+ Reuse saved local auth when possible. Otherwise first try to import signed-in
46
+ same-machine Linux Brave/Chrome browser profile state, then a discoverable
47
+ attachable signed-in browser session, and finally open a temporary Chromium
48
+ login window and save that session there. The temporary-browser fallback
49
+ auto-detects completion when it can, and also accepts an explicit Enter
50
+ keypress in the terminal to force an immediate recheck once the page shows the
51
+ user is signed in. If authentication is still not established,
47
52
  .B login
48
53
  exits non-zero.
49
54
  .TP
50
55
  .B logout
51
- Delete stored session material used by this CLI and disable automatic
56
+ Delete stored session material used by this CLI and disable passive
52
57
  browser-session reuse until the next explicit
53
58
  .BR login .
54
59
  .TP
@@ -205,14 +210,18 @@ time, but the saved state is reused across runs unless cleared with
205
210
  .BR logout .
206
211
  After
207
212
  .BR logout ,
208
- automatic browser-session reuse stays disabled until the next explicit
213
+ passive browser-session reuse stays disabled until the next explicit
209
214
  .BR login .
210
215
  .SH ENVIRONMENT
211
216
  .PP
212
217
  In the common case you should not need to configure browser plumbing manually.
213
- For attached-browser reuse, the CLI can probe compatible CDP URLs/ports and a
214
- few localhost defaults. If no reusable browser connection is discoverable,
215
- login falls back to a temporary Chromium window it can watch directly. See
218
+ On Linux, the preferred browser-reuse path is a signed-in local Brave or Google
219
+ Chrome profile on the same machine, which does not need CDP/remote debugging.
220
+ For attached-browser reuse, the CLI can also probe compatible CDP URLs/ports
221
+ and a few localhost defaults. If neither same-machine profile import nor an
222
+ attachable browser session is discoverable, login falls back to a temporary
223
+ Chromium window it can watch directly, with an explicit Enter-to-recheck
224
+ fallback in the terminal if automatic detection is not yet convincing. See
216
225
  .I docs/install.md
217
226
  for setup and troubleshooting.
218
227
  .SH EXIT STATUS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doordash-cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Cart-safe DoorDash CLI with direct API support for browse, read-only existing-order, and cart workflows.",
5
5
  "type": "module",
6
6
  "main": "dist/lib.js",