doordash-cli 0.3.3 → 0.4.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.
@@ -1,6 +1,6 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { buildAddConsumerAddressPayload, buildAddToCartPayload, buildUpdateCartPayload, extractExistingOrdersFromApolloCache, hasDoorDashCookies, normalizeItemName, parseExistingOrderLifecycleStatus, parseExistingOrdersResponse, parseOptionSelectionsJson, parseSearchRestaurantRow, resolveAvailableAddressMatch, selectManagedBrowserImportMode, } from "./direct-api.js";
3
+ import { bootstrapAuthSessionWithDeps, buildAddConsumerAddressPayload, buildAddToCartPayload, buildUpdateCartPayload, extractExistingOrdersFromApolloCache, normalizeItemName, parseExistingOrderLifecycleStatus, parseExistingOrdersResponse, parseOptionSelectionsJson, parseSearchRestaurantRow, resolveAttachedBrowserCdpCandidates, resolveAvailableAddressMatch, resolveSystemBrowserOpenCommand, selectAttachedBrowserImportMode, summarizeDesktopBrowserReuseGap, } from "./direct-api.js";
4
4
  function configurableItemDetail() {
5
5
  return {
6
6
  success: true,
@@ -144,25 +144,540 @@ test("parseSearchRestaurantRow extracts restaurant metadata from facet rows", ()
144
144
  url: "https://www.doordash.com/store/24633898/?pickup=false",
145
145
  });
146
146
  });
147
- test("managed browser import falls back to cookie reuse without opening a DoorDash page", () => {
148
- const cookies = [{ domain: ".doordash.com" }];
149
- assert.equal(hasDoorDashCookies(cookies), true);
150
- assert.equal(selectManagedBrowserImportMode({ pageUrls: [], cookies }), "cookies");
147
+ test("resolveAttachedBrowserCdpCandidates prioritizes explicit envs, compatibility envs, config, and defaults", () => {
148
+ const env = {
149
+ DOORDASH_BROWSER_CDP_URLS: "http://127.0.0.1:9555/, http://127.0.0.1:9556",
150
+ DOORDASH_ATTACHED_BROWSER_CDP_URL: "http://127.0.0.1:9666/",
151
+ DOORDASH_BROWSER_CDP_PORTS: "9333, 9334",
152
+ DOORDASH_BROWSER_CDP_PORT: "9444",
153
+ OPENCLAW_BROWSER_CDP_URL: "http://127.0.0.1:18888/",
154
+ };
155
+ const candidates = resolveAttachedBrowserCdpCandidates(env, ["http://127.0.0.1:9777"]);
156
+ assert.deepEqual(candidates.slice(0, 7), [
157
+ "http://127.0.0.1:9555",
158
+ "http://127.0.0.1:9556",
159
+ "http://127.0.0.1:9666",
160
+ "http://127.0.0.1:9333",
161
+ "http://127.0.0.1:9334",
162
+ "http://127.0.0.1:9444",
163
+ "http://127.0.0.1:18888",
164
+ ]);
165
+ assert.ok(candidates.includes("http://127.0.0.1:9777"));
166
+ assert.ok(candidates.includes("http://127.0.0.1:18792"));
167
+ assert.ok(candidates.includes("http://127.0.0.1:18800"));
168
+ assert.ok(candidates.includes("http://127.0.0.1:9222"));
151
169
  });
152
- test("managed browser import prefers an existing DoorDash page when one is already open", () => {
153
- const cookies = [{ domain: ".doordash.com" }];
154
- assert.equal(selectManagedBrowserImportMode({
155
- pageUrls: ["https://github.com/LatencyTDH/doordash-cli/pulls", "https://www.doordash.com/home"],
156
- cookies,
157
- }), "page");
170
+ test("resolveSystemBrowserOpenCommand stays generic across operating systems", () => {
171
+ assert.deepEqual(resolveSystemBrowserOpenCommand("https://www.doordash.com/home", "darwin"), {
172
+ command: "open",
173
+ args: ["https://www.doordash.com/home"],
174
+ });
175
+ assert.deepEqual(resolveSystemBrowserOpenCommand("https://www.doordash.com/home", "linux"), {
176
+ command: "xdg-open",
177
+ args: ["https://www.doordash.com/home"],
178
+ });
179
+ assert.deepEqual(resolveSystemBrowserOpenCommand("https://www.doordash.com/home", "win32"), {
180
+ command: "cmd",
181
+ args: ["/c", "start", "", "https://www.doordash.com/home"],
182
+ });
158
183
  });
159
- test("managed browser import skips unrelated browser contexts", () => {
160
- assert.equal(hasDoorDashCookies([{ domain: ".github.com" }]), false);
161
- assert.equal(selectManagedBrowserImportMode({
184
+ test("summarizeDesktopBrowserReuseGap explains why a merely-open Brave window is 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 ?? "", /normal open browser window is not automatically reusable/i);
195
+ assert.match(message ?? "", /attach to/i);
196
+ });
197
+ test("summarizeDesktopBrowserReuseGap stays quiet once the browser exposes attach signals", () => {
198
+ assert.equal(summarizeDesktopBrowserReuseGap({
199
+ processCommands: ["/bin/bash /usr/bin/brave-browser-stable --remote-debugging-port=9222"],
200
+ hasAnyDevToolsActivePort: false,
201
+ }), null);
202
+ assert.equal(summarizeDesktopBrowserReuseGap({
203
+ processCommands: ["/bin/bash /usr/bin/brave-browser-stable"],
204
+ hasAnyDevToolsActivePort: true,
205
+ }), null);
206
+ });
207
+ test("selectAttachedBrowserImportMode treats an authenticated browser with DoorDash cookies as an immediate import candidate", () => {
208
+ assert.equal(selectAttachedBrowserImportMode({
209
+ pageUrls: ["https://github.com/LatencyTDH/doordash-cli/pulls"],
210
+ cookies: [{ domain: ".doordash.com" }],
211
+ }), "cookies");
212
+ assert.equal(selectAttachedBrowserImportMode({
213
+ pageUrls: ["https://www.doordash.com/home"],
214
+ cookies: [{ domain: ".github.com" }],
215
+ }), "page");
216
+ assert.equal(selectAttachedBrowserImportMode({
162
217
  pageUrls: ["https://github.com/LatencyTDH/doordash-cli"],
163
218
  cookies: [{ domain: ".github.com" }],
164
219
  }), "skip");
165
220
  });
221
+ test("bootstrapAuthSessionWithDeps returns immediately when saved local auth is already valid", async () => {
222
+ const auth = {
223
+ success: true,
224
+ isLoggedIn: true,
225
+ email: "user@example.com",
226
+ firstName: "Test",
227
+ lastName: "User",
228
+ consumerId: "consumer-1",
229
+ marketId: "market-1",
230
+ defaultAddress: null,
231
+ cookiesPath: "/tmp/cookies.json",
232
+ storageStatePath: "/tmp/storage-state.json",
233
+ };
234
+ let importCalls = 0;
235
+ let openCalls = 0;
236
+ let waitCalls = 0;
237
+ let markCalls = 0;
238
+ const logs = [];
239
+ const result = await bootstrapAuthSessionWithDeps({
240
+ clearBlockedBrowserImport: async () => { },
241
+ checkPersistedAuth: async () => auth,
242
+ importBrowserSessionIfAvailable: async () => {
243
+ importCalls += 1;
244
+ return true;
245
+ },
246
+ markBrowserImportAttempted: () => {
247
+ markCalls += 1;
248
+ },
249
+ getAttachedBrowserCdpCandidates: async () => {
250
+ throw new Error("should not inspect candidates when saved auth is already valid");
251
+ },
252
+ getReachableCdpCandidates: async () => {
253
+ throw new Error("should not probe reachability when saved auth is already valid");
254
+ },
255
+ describeDesktopBrowserReuseGap: async () => null,
256
+ openUrlInAttachedBrowser: async () => {
257
+ throw new Error("should not try to open an attached browser when saved auth is already valid");
258
+ },
259
+ openUrlInDefaultBrowser: async () => {
260
+ openCalls += 1;
261
+ return true;
262
+ },
263
+ waitForAttachedBrowserSessionImport: async () => {
264
+ waitCalls += 1;
265
+ return true;
266
+ },
267
+ waitForManagedBrowserLogin: async () => {
268
+ throw new Error("should not launch a managed browser when saved auth is already valid");
269
+ },
270
+ canPromptForManagedBrowserConfirmation: () => false,
271
+ checkAuthDirect: async () => {
272
+ throw new Error("should not re-check auth through the live session when saved auth is already valid");
273
+ },
274
+ log: (message) => {
275
+ logs.push(message);
276
+ },
277
+ });
278
+ assert.equal(importCalls, 0);
279
+ assert.equal(markCalls, 0);
280
+ assert.equal(openCalls, 0);
281
+ assert.equal(waitCalls, 0);
282
+ assert.equal(logs.length, 0);
283
+ assert.equal(result.isLoggedIn, true);
284
+ assert.match(result.message, /Already signed in with saved local DoorDash session state/);
285
+ });
286
+ test("bootstrapAuthSessionWithDeps returns immediately when an attached browser session is already authenticated", async () => {
287
+ const auth = {
288
+ success: true,
289
+ isLoggedIn: true,
290
+ email: "user@example.com",
291
+ firstName: "Test",
292
+ lastName: "User",
293
+ consumerId: "consumer-1",
294
+ marketId: "market-1",
295
+ defaultAddress: null,
296
+ cookiesPath: "/tmp/cookies.json",
297
+ storageStatePath: "/tmp/storage-state.json",
298
+ };
299
+ let openCalls = 0;
300
+ let waitCalls = 0;
301
+ let markCalls = 0;
302
+ const logs = [];
303
+ const result = await bootstrapAuthSessionWithDeps({
304
+ clearBlockedBrowserImport: async () => { },
305
+ checkPersistedAuth: async () => null,
306
+ importBrowserSessionIfAvailable: async () => true,
307
+ markBrowserImportAttempted: () => {
308
+ markCalls += 1;
309
+ },
310
+ getAttachedBrowserCdpCandidates: async () => {
311
+ throw new Error("should not inspect candidates on immediate browser-session import");
312
+ },
313
+ getReachableCdpCandidates: async () => {
314
+ throw new Error("should not probe reachability on immediate browser-session import");
315
+ },
316
+ describeDesktopBrowserReuseGap: async () => null,
317
+ openUrlInAttachedBrowser: async () => {
318
+ throw new Error("should not open a browser when immediate browser-session import succeeded");
319
+ },
320
+ openUrlInDefaultBrowser: async () => {
321
+ openCalls += 1;
322
+ return true;
323
+ },
324
+ waitForAttachedBrowserSessionImport: async () => {
325
+ waitCalls += 1;
326
+ return true;
327
+ },
328
+ waitForManagedBrowserLogin: async () => {
329
+ throw new Error("should not launch a managed browser when immediate browser-session import succeeded");
330
+ },
331
+ canPromptForManagedBrowserConfirmation: () => false,
332
+ checkAuthDirect: async () => auth,
333
+ log: (message) => {
334
+ logs.push(message);
335
+ },
336
+ });
337
+ assert.equal(markCalls, 1);
338
+ assert.equal(openCalls, 0);
339
+ assert.equal(waitCalls, 0);
340
+ assert.equal(logs.length, 0);
341
+ assert.equal(result.isLoggedIn, true);
342
+ assert.match(result.message, /Imported an existing signed-in browser session/);
343
+ });
344
+ test("bootstrapAuthSessionWithDeps opens a watchable attached browser session before entering the full wait path", async () => {
345
+ const auth = {
346
+ success: true,
347
+ isLoggedIn: true,
348
+ email: "user@example.com",
349
+ firstName: "Test",
350
+ lastName: "User",
351
+ consumerId: "consumer-1",
352
+ marketId: "market-1",
353
+ defaultAddress: null,
354
+ cookiesPath: "/tmp/cookies.json",
355
+ storageStatePath: "/tmp/storage-state.json",
356
+ };
357
+ let openAttachedCalls = 0;
358
+ let openDefaultCalls = 0;
359
+ let waitCalls = 0;
360
+ let reachableCalls = 0;
361
+ let waitTimeoutMs = 0;
362
+ const logs = [];
363
+ const result = await bootstrapAuthSessionWithDeps({
364
+ clearBlockedBrowserImport: async () => { },
365
+ checkPersistedAuth: async () => null,
366
+ importBrowserSessionIfAvailable: async () => false,
367
+ markBrowserImportAttempted: () => { },
368
+ getAttachedBrowserCdpCandidates: async () => ["http://127.0.0.1:9222"],
369
+ getReachableCdpCandidates: async (candidates) => {
370
+ reachableCalls += 1;
371
+ return candidates;
372
+ },
373
+ describeDesktopBrowserReuseGap: async () => null,
374
+ openUrlInAttachedBrowser: async () => {
375
+ openAttachedCalls += 1;
376
+ return true;
377
+ },
378
+ openUrlInDefaultBrowser: async () => {
379
+ openDefaultCalls += 1;
380
+ return true;
381
+ },
382
+ waitForAttachedBrowserSessionImport: async (input) => {
383
+ waitCalls += 1;
384
+ waitTimeoutMs = input.timeoutMs;
385
+ return true;
386
+ },
387
+ waitForManagedBrowserLogin: async () => {
388
+ throw new Error("should not launch a managed browser when an attached browser is reachable");
389
+ },
390
+ canPromptForManagedBrowserConfirmation: () => false,
391
+ checkAuthDirect: async () => auth,
392
+ log: (message) => {
393
+ logs.push(message);
394
+ },
395
+ });
396
+ assert.equal(reachableCalls, 1);
397
+ assert.equal(openAttachedCalls, 1);
398
+ assert.equal(openDefaultCalls, 0);
399
+ assert.equal(waitCalls, 1);
400
+ assert.equal(waitTimeoutMs, 180_000);
401
+ assert.match(logs.join("\n"), /Opened DoorDash in the attachable browser session I'm watching/);
402
+ assert.match(logs.join("\n"), /Detected 1 attachable browser session/);
403
+ assert.match(result.message, /saved it for direct API use/);
404
+ });
405
+ test("bootstrapAuthSessionWithDeps falls back to a managed browser login window and auto-completes when it can prove login", async () => {
406
+ const auth = {
407
+ success: true,
408
+ isLoggedIn: true,
409
+ email: "user@example.com",
410
+ firstName: "Test",
411
+ lastName: "User",
412
+ consumerId: "consumer-1",
413
+ marketId: "market-1",
414
+ defaultAddress: null,
415
+ cookiesPath: "/tmp/cookies.json",
416
+ storageStatePath: "/tmp/storage-state.json",
417
+ };
418
+ let managedCalls = 0;
419
+ let attachedWaitCalls = 0;
420
+ const logs = [];
421
+ const result = await bootstrapAuthSessionWithDeps({
422
+ clearBlockedBrowserImport: async () => { },
423
+ checkPersistedAuth: async () => null,
424
+ importBrowserSessionIfAvailable: async () => false,
425
+ markBrowserImportAttempted: () => { },
426
+ getAttachedBrowserCdpCandidates: async () => ["http://127.0.0.1:9222"],
427
+ getReachableCdpCandidates: async () => [],
428
+ describeDesktopBrowserReuseGap: async () => null,
429
+ openUrlInAttachedBrowser: async () => false,
430
+ openUrlInDefaultBrowser: async () => true,
431
+ waitForAttachedBrowserSessionImport: async () => {
432
+ attachedWaitCalls += 1;
433
+ return false;
434
+ },
435
+ waitForManagedBrowserLogin: async () => {
436
+ managedCalls += 1;
437
+ return {
438
+ status: "completed",
439
+ completion: "automatic",
440
+ auth,
441
+ };
442
+ },
443
+ canPromptForManagedBrowserConfirmation: () => true,
444
+ checkAuthDirect: async () => auth,
445
+ log: (message) => {
446
+ logs.push(message);
447
+ },
448
+ });
449
+ assert.equal(managedCalls, 1);
450
+ assert.equal(attachedWaitCalls, 0);
451
+ assert.match(logs.join("\n"), /temporary Chromium login window/);
452
+ assert.match(logs.join("\n"), /press Enter here to force an immediate recheck/i);
453
+ assert.match(result.message, /detected the signed-in session there automatically/i);
454
+ assert.equal(result.success, true);
455
+ assert.equal(result.isLoggedIn, true);
456
+ });
457
+ test("bootstrapAuthSessionWithDeps logs why an already-open desktop browser still is not reusable", async () => {
458
+ const auth = {
459
+ success: true,
460
+ isLoggedIn: true,
461
+ email: "user@example.com",
462
+ firstName: "Test",
463
+ lastName: "User",
464
+ consumerId: "consumer-1",
465
+ marketId: "market-1",
466
+ defaultAddress: null,
467
+ cookiesPath: "/tmp/cookies.json",
468
+ storageStatePath: "/tmp/storage-state.json",
469
+ };
470
+ const logs = [];
471
+ const result = await bootstrapAuthSessionWithDeps({
472
+ clearBlockedBrowserImport: async () => { },
473
+ checkPersistedAuth: async () => null,
474
+ importBrowserSessionIfAvailable: async () => false,
475
+ markBrowserImportAttempted: () => { },
476
+ getAttachedBrowserCdpCandidates: async () => [],
477
+ getReachableCdpCandidates: async () => [],
478
+ describeDesktopBrowserReuseGap: async () => "I can see Brave is already running on this desktop, but it is not exposing an attachable browser automation session right now.",
479
+ openUrlInAttachedBrowser: async () => false,
480
+ openUrlInDefaultBrowser: async () => true,
481
+ waitForAttachedBrowserSessionImport: async () => false,
482
+ waitForManagedBrowserLogin: async () => ({
483
+ status: "completed",
484
+ completion: "automatic",
485
+ auth,
486
+ }),
487
+ canPromptForManagedBrowserConfirmation: () => true,
488
+ checkAuthDirect: async () => auth,
489
+ log: (message) => {
490
+ logs.push(message);
491
+ },
492
+ });
493
+ assert.match(logs.join("\n"), /Brave is already running on this desktop/i);
494
+ assert.match(logs.join("\n"), /couldn't find an attachable browser session I can reuse/i);
495
+ assert.equal(result.success, true);
496
+ assert.equal(result.isLoggedIn, true);
497
+ });
498
+ test("bootstrapAuthSessionWithDeps restores an explicit Enter-style completion path for the managed browser fallback", async () => {
499
+ const auth = {
500
+ success: true,
501
+ isLoggedIn: true,
502
+ email: "user@example.com",
503
+ firstName: "Test",
504
+ lastName: "User",
505
+ consumerId: "consumer-1",
506
+ marketId: "market-1",
507
+ defaultAddress: null,
508
+ cookiesPath: "/tmp/cookies.json",
509
+ storageStatePath: "/tmp/storage-state.json",
510
+ };
511
+ const logs = [];
512
+ const result = await bootstrapAuthSessionWithDeps({
513
+ clearBlockedBrowserImport: async () => { },
514
+ checkPersistedAuth: async () => null,
515
+ importBrowserSessionIfAvailable: async () => false,
516
+ markBrowserImportAttempted: () => { },
517
+ getAttachedBrowserCdpCandidates: async () => [],
518
+ getReachableCdpCandidates: async () => [],
519
+ describeDesktopBrowserReuseGap: async () => null,
520
+ openUrlInAttachedBrowser: async () => false,
521
+ openUrlInDefaultBrowser: async () => true,
522
+ waitForAttachedBrowserSessionImport: async () => false,
523
+ waitForManagedBrowserLogin: async () => ({
524
+ status: "completed",
525
+ completion: "manual",
526
+ auth,
527
+ }),
528
+ canPromptForManagedBrowserConfirmation: () => true,
529
+ checkAuthDirect: async () => auth,
530
+ log: (message) => {
531
+ logs.push(message);
532
+ },
533
+ });
534
+ assert.match(logs.join("\n"), /press Enter here to force an immediate recheck/i);
535
+ assert.match(result.message, /After you pressed Enter to confirm the browser login was complete/i);
536
+ assert.equal(result.success, true);
537
+ assert.equal(result.isLoggedIn, true);
538
+ });
539
+ test("bootstrapAuthSessionWithDeps returns a bounded failure instead of a dead-end when managed browser auto-detection cannot prove login", async () => {
540
+ const auth = {
541
+ success: true,
542
+ isLoggedIn: false,
543
+ email: null,
544
+ firstName: null,
545
+ lastName: null,
546
+ consumerId: null,
547
+ marketId: null,
548
+ defaultAddress: null,
549
+ cookiesPath: "/tmp/cookies.json",
550
+ storageStatePath: "/tmp/storage-state.json",
551
+ };
552
+ let attachedWaitCalls = 0;
553
+ let attachedWaitTimeoutMs = 0;
554
+ const logs = [];
555
+ const result = await bootstrapAuthSessionWithDeps({
556
+ clearBlockedBrowserImport: async () => { },
557
+ checkPersistedAuth: async () => null,
558
+ importBrowserSessionIfAvailable: async () => false,
559
+ markBrowserImportAttempted: () => { },
560
+ getAttachedBrowserCdpCandidates: async () => ["http://127.0.0.1:9222"],
561
+ getReachableCdpCandidates: async () => [],
562
+ describeDesktopBrowserReuseGap: async () => null,
563
+ openUrlInAttachedBrowser: async () => false,
564
+ openUrlInDefaultBrowser: async () => true,
565
+ waitForAttachedBrowserSessionImport: async (input) => {
566
+ attachedWaitCalls += 1;
567
+ attachedWaitTimeoutMs = input.timeoutMs;
568
+ return false;
569
+ },
570
+ waitForManagedBrowserLogin: async () => ({
571
+ status: "timed-out",
572
+ auth,
573
+ }),
574
+ canPromptForManagedBrowserConfirmation: () => true,
575
+ checkAuthDirect: async () => auth,
576
+ log: (message) => {
577
+ logs.push(message);
578
+ },
579
+ });
580
+ assert.equal(attachedWaitCalls, 0);
581
+ assert.equal(attachedWaitTimeoutMs, 0);
582
+ assert.match(logs.join("\n"), /temporary Chromium login window/i);
583
+ assert.match(logs.join("\n"), /press Enter here to force an immediate recheck/i);
584
+ assert.equal(result.success, false);
585
+ assert.equal(result.isLoggedIn, false);
586
+ assert.match(result.message, /couldn't prove an authenticated DoorDash session/i);
587
+ assert.match(result.message, /press Enter sooner next time/i);
588
+ });
589
+ test("bootstrapAuthSessionWithDeps falls back to quick troubleshooting guidance when the managed browser login window cannot launch", async () => {
590
+ const auth = {
591
+ success: true,
592
+ isLoggedIn: false,
593
+ email: null,
594
+ firstName: null,
595
+ lastName: null,
596
+ consumerId: null,
597
+ marketId: null,
598
+ defaultAddress: null,
599
+ cookiesPath: "/tmp/cookies.json",
600
+ storageStatePath: "/tmp/storage-state.json",
601
+ };
602
+ let attachedWaitCalls = 0;
603
+ let attachedWaitTimeoutMs = 0;
604
+ const logs = [];
605
+ const result = await bootstrapAuthSessionWithDeps({
606
+ clearBlockedBrowserImport: async () => { },
607
+ checkPersistedAuth: async () => null,
608
+ importBrowserSessionIfAvailable: async () => false,
609
+ markBrowserImportAttempted: () => { },
610
+ getAttachedBrowserCdpCandidates: async () => ["http://127.0.0.1:9222"],
611
+ getReachableCdpCandidates: async () => [],
612
+ describeDesktopBrowserReuseGap: async () => null,
613
+ openUrlInAttachedBrowser: async () => false,
614
+ openUrlInDefaultBrowser: async () => true,
615
+ waitForAttachedBrowserSessionImport: async (input) => {
616
+ attachedWaitCalls += 1;
617
+ attachedWaitTimeoutMs = input.timeoutMs;
618
+ return false;
619
+ },
620
+ waitForManagedBrowserLogin: async () => ({ status: "launch-failed" }),
621
+ canPromptForManagedBrowserConfirmation: () => true,
622
+ checkAuthDirect: async () => auth,
623
+ log: (message) => {
624
+ logs.push(message);
625
+ },
626
+ });
627
+ assert.equal(attachedWaitCalls, 1);
628
+ assert.equal(attachedWaitTimeoutMs, 10_000);
629
+ assert.match(logs.join("\n"), /couldn't launch the temporary Chromium login window/i);
630
+ assert.match(logs.join("\n"), /won't keep you waiting for the full login timeout/i);
631
+ assert.equal(result.success, false);
632
+ assert.equal(result.isLoggedIn, false);
633
+ assert.match(result.message, /still isn't exposing an attachable browser session/);
634
+ });
635
+ test("bootstrapAuthSessionWithDeps clears the logout block before an explicit login reuses an attached browser session", async () => {
636
+ const auth = {
637
+ success: true,
638
+ isLoggedIn: true,
639
+ email: "user@example.com",
640
+ firstName: "Test",
641
+ lastName: "User",
642
+ consumerId: "consumer-1",
643
+ marketId: "market-1",
644
+ defaultAddress: null,
645
+ cookiesPath: "/tmp/cookies.json",
646
+ storageStatePath: "/tmp/storage-state.json",
647
+ };
648
+ let blocked = true;
649
+ let clearCalls = 0;
650
+ let importCalls = 0;
651
+ const result = await bootstrapAuthSessionWithDeps({
652
+ clearBlockedBrowserImport: async () => {
653
+ clearCalls += 1;
654
+ blocked = false;
655
+ },
656
+ checkPersistedAuth: async () => null,
657
+ importBrowserSessionIfAvailable: async () => {
658
+ importCalls += 1;
659
+ return blocked === false;
660
+ },
661
+ markBrowserImportAttempted: () => { },
662
+ getAttachedBrowserCdpCandidates: async () => [],
663
+ getReachableCdpCandidates: async () => [],
664
+ describeDesktopBrowserReuseGap: async () => null,
665
+ openUrlInAttachedBrowser: async () => false,
666
+ openUrlInDefaultBrowser: async () => false,
667
+ waitForAttachedBrowserSessionImport: async () => false,
668
+ waitForManagedBrowserLogin: async () => {
669
+ throw new Error("should not launch a managed browser when explicit login can immediately reuse an attached browser session");
670
+ },
671
+ canPromptForManagedBrowserConfirmation: () => false,
672
+ checkAuthDirect: async () => auth,
673
+ log: () => { },
674
+ });
675
+ assert.equal(clearCalls, 1);
676
+ assert.equal(importCalls, 1);
677
+ assert.equal(result.success, true);
678
+ assert.equal(result.isLoggedIn, true);
679
+ assert.match(result.message, /Imported an existing signed-in browser session/);
680
+ });
166
681
  test("parseOptionSelectionsJson parses structured recursive option selections", () => {
167
682
  assert.deepEqual(parseOptionSelectionsJson('[{"groupId":"703393388","optionId":"4716032529"},{"groupId":"recommended_option_546935995","optionId":"546936011","children":[{"groupId":"780057412","optionId":"4702669757","quantity":2}]}]'), [
168
683
  { groupId: "703393388", optionId: "4716032529" },
@@ -1,3 +1,4 @@
1
1
  export declare function getSessionConfigDir(): string;
2
2
  export declare function getCookiesPath(): string;
3
3
  export declare function getStorageStatePath(): string;
4
+ export declare function getBrowserImportBlockPath(): string;
@@ -5,6 +5,7 @@ import { join } from "node:path";
5
5
  const SESSION_CONFIG_DIR = join(homedir(), ".config", "striderlabs-mcp-doordash");
6
6
  const COOKIES_FILE = join(SESSION_CONFIG_DIR, "cookies.json");
7
7
  const STORAGE_STATE_FILE = join(SESSION_CONFIG_DIR, "storage-state.json");
8
+ const BROWSER_IMPORT_BLOCK_FILE = join(SESSION_CONFIG_DIR, "browser-import-blocked");
8
9
  export function getSessionConfigDir() {
9
10
  return SESSION_CONFIG_DIR;
10
11
  }
@@ -14,3 +15,6 @@ export function getCookiesPath() {
14
15
  export function getStorageStatePath() {
15
16
  return STORAGE_STATE_FILE;
16
17
  }
18
+ export function getBrowserImportBlockPath() {
19
+ return BROWSER_IMPORT_BLOCK_FILE;
20
+ }
@@ -2,10 +2,11 @@ import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
- import { getCookiesPath, getSessionConfigDir, getStorageStatePath } from "./session-storage.js";
5
+ import { getBrowserImportBlockPath, getCookiesPath, getSessionConfigDir, getStorageStatePath } from "./session-storage.js";
6
6
  test("session storage paths stay compatible with the historical StriderLabs location", () => {
7
7
  const configDir = join(homedir(), ".config", "striderlabs-mcp-doordash");
8
8
  assert.equal(getSessionConfigDir(), configDir);
9
9
  assert.equal(getCookiesPath(), join(configDir, "cookies.json"));
10
10
  assert.equal(getStorageStatePath(), join(configDir, "storage-state.json"));
11
+ assert.equal(getBrowserImportBlockPath(), join(configDir, "browser-import-blocked"));
11
12
  });
package/docs/examples.md CHANGED
@@ -14,7 +14,7 @@ All commands print JSON.
14
14
 
15
15
  ## Session setup
16
16
 
17
- Install the matching Chromium build once if you do not already have it:
17
+ If your environment does not already have the bundled Playwright runtime installed, install it once:
18
18
 
19
19
  ```bash
20
20
  doordash-cli install-browser
@@ -26,13 +26,13 @@ Check whether you already have reusable session state:
26
26
  doordash-cli auth-check
27
27
  ```
28
28
 
29
- If needed, launch Chromium for a one-time sign-in and save reusable state:
29
+ If your saved local state is still valid, this exits immediately. Otherwise it tries to reuse a discoverable attachable signed-in browser session. A merely-open Chrome/Brave window is not automatically reusable unless the CLI can actually attach to it, so the fallback is 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 start:
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
@@ -31,9 +31,9 @@ npm run cli -- --help
31
31
 
32
32
  If you stay in checkout mode, replace `doordash-cli` with `npm run cli --` in the examples below.
33
33
 
34
- ## Install the browser once
34
+ ## Install the bundled runtime if needed
35
35
 
36
- If you plan to sign in with `login`, install the matching Playwright Chromium build:
36
+ If your environment does not already have Playwright's bundled Chromium runtime installed, install it once:
37
37
 
38
38
  ### Global or linked install
39
39
 
@@ -47,6 +47,8 @@ doordash-cli install-browser
47
47
  npm run install:browser
48
48
  ```
49
49
 
50
+ That runtime is used when the CLI needs a local browser, including the temporary login window fallback.
51
+
50
52
  ## First run
51
53
 
52
54
  ```bash
@@ -56,8 +58,16 @@ doordash-cli set-address --address "350 5th Ave, New York, NY 10118"
56
58
  doordash-cli search --query sushi
57
59
  ```
58
60
 
59
- ## Session reuse
61
+ ## Login and session reuse
62
+
63
+ `doordash-cli login` reuses saved local auth when it is still valid. Otherwise it tries to import a discoverable attachable signed-in browser session. A merely-open Chrome/Brave window is not automatically reusable unless the CLI can actually attach to it. If no attachable session is available, it opens a temporary Chromium login window and saves the session there. If authentication still is not established, `login` exits non-zero.
64
+
65
+ `doordash-cli auth-check` can also quietly import a discoverable attachable signed-in browser session unless `doordash-cli logout` disabled that auto-reuse.
66
+
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
+
69
+ ## Browser-session troubleshooting
60
70
 
61
- If you already have a compatible signed-in DoorDash managed-browser session available, direct commands may quietly reuse it instead of opening or navigating a DoorDash tab.
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.
62
72
 
63
- If not, run `doordash-cli login` once to save reusable state for later commands.
73
+ If you expected reuse from another browser instead, make sure that browser exposes an attachable browser automation session the CLI can actually import. A merely-open browser window is not enough today, even if it is already your main browser.