doordash-cli 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +14 -3
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +16 -8
- package/dist/cli.test.js +41 -3
- package/dist/direct-api.d.ts +39 -5
- package/dist/direct-api.js +439 -73
- package/dist/direct-api.test.js +313 -14
- package/dist/session-storage.d.ts +1 -0
- package/dist/session-storage.js +4 -0
- package/dist/session-storage.test.js +2 -1
- package/docs/examples.md +3 -3
- package/docs/install.md +15 -5
- package/man/dd-cli.1 +26 -14
- package/package.json +1 -1
package/dist/direct-api.test.js
CHANGED
|
@@ -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,
|
|
3
|
+
import { bootstrapAuthSessionWithDeps, buildAddConsumerAddressPayload, buildAddToCartPayload, buildUpdateCartPayload, extractExistingOrdersFromApolloCache, normalizeItemName, parseExistingOrderLifecycleStatus, parseExistingOrdersResponse, parseOptionSelectionsJson, parseSearchRestaurantRow, resolveAttachedBrowserCdpCandidates, resolveAvailableAddressMatch, resolveSystemBrowserOpenCommand, selectAttachedBrowserImportMode, } from "./direct-api.js";
|
|
4
4
|
function configurableItemDetail() {
|
|
5
5
|
return {
|
|
6
6
|
success: true,
|
|
@@ -144,25 +144,324 @@ 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("
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
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("
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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("
|
|
160
|
-
assert.equal(
|
|
161
|
-
|
|
184
|
+
test("selectAttachedBrowserImportMode treats an authenticated browser with DoorDash cookies as an immediate import candidate", () => {
|
|
185
|
+
assert.equal(selectAttachedBrowserImportMode({
|
|
186
|
+
pageUrls: ["https://github.com/LatencyTDH/doordash-cli/pulls"],
|
|
187
|
+
cookies: [{ domain: ".doordash.com" }],
|
|
188
|
+
}), "cookies");
|
|
189
|
+
assert.equal(selectAttachedBrowserImportMode({
|
|
190
|
+
pageUrls: ["https://www.doordash.com/home"],
|
|
191
|
+
cookies: [{ domain: ".github.com" }],
|
|
192
|
+
}), "page");
|
|
193
|
+
assert.equal(selectAttachedBrowserImportMode({
|
|
162
194
|
pageUrls: ["https://github.com/LatencyTDH/doordash-cli"],
|
|
163
195
|
cookies: [{ domain: ".github.com" }],
|
|
164
196
|
}), "skip");
|
|
165
197
|
});
|
|
198
|
+
test("bootstrapAuthSessionWithDeps returns immediately when saved local auth is already valid", async () => {
|
|
199
|
+
const auth = {
|
|
200
|
+
success: true,
|
|
201
|
+
isLoggedIn: true,
|
|
202
|
+
email: "user@example.com",
|
|
203
|
+
firstName: "Test",
|
|
204
|
+
lastName: "User",
|
|
205
|
+
consumerId: "consumer-1",
|
|
206
|
+
marketId: "market-1",
|
|
207
|
+
defaultAddress: null,
|
|
208
|
+
cookiesPath: "/tmp/cookies.json",
|
|
209
|
+
storageStatePath: "/tmp/storage-state.json",
|
|
210
|
+
};
|
|
211
|
+
let importCalls = 0;
|
|
212
|
+
let openCalls = 0;
|
|
213
|
+
let waitCalls = 0;
|
|
214
|
+
let markCalls = 0;
|
|
215
|
+
const logs = [];
|
|
216
|
+
const result = await bootstrapAuthSessionWithDeps({
|
|
217
|
+
clearBlockedBrowserImport: async () => { },
|
|
218
|
+
checkPersistedAuth: async () => auth,
|
|
219
|
+
importBrowserSessionIfAvailable: async () => {
|
|
220
|
+
importCalls += 1;
|
|
221
|
+
return true;
|
|
222
|
+
},
|
|
223
|
+
markBrowserImportAttempted: () => {
|
|
224
|
+
markCalls += 1;
|
|
225
|
+
},
|
|
226
|
+
getAttachedBrowserCdpCandidates: async () => {
|
|
227
|
+
throw new Error("should not inspect candidates when saved auth is already valid");
|
|
228
|
+
},
|
|
229
|
+
getReachableCdpCandidates: async () => {
|
|
230
|
+
throw new Error("should not probe reachability when saved auth is already valid");
|
|
231
|
+
},
|
|
232
|
+
openUrlInAttachedBrowser: async () => {
|
|
233
|
+
throw new Error("should not try to open an attached browser when saved auth is already valid");
|
|
234
|
+
},
|
|
235
|
+
openUrlInDefaultBrowser: async () => {
|
|
236
|
+
openCalls += 1;
|
|
237
|
+
return true;
|
|
238
|
+
},
|
|
239
|
+
waitForAttachedBrowserSessionImport: async () => {
|
|
240
|
+
waitCalls += 1;
|
|
241
|
+
return true;
|
|
242
|
+
},
|
|
243
|
+
waitForManagedBrowserLogin: async () => {
|
|
244
|
+
throw new Error("should not launch a managed browser when saved auth is already valid");
|
|
245
|
+
},
|
|
246
|
+
checkAuthDirect: async () => {
|
|
247
|
+
throw new Error("should not re-check auth through the live session when saved auth is already valid");
|
|
248
|
+
},
|
|
249
|
+
log: (message) => {
|
|
250
|
+
logs.push(message);
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
assert.equal(importCalls, 0);
|
|
254
|
+
assert.equal(markCalls, 0);
|
|
255
|
+
assert.equal(openCalls, 0);
|
|
256
|
+
assert.equal(waitCalls, 0);
|
|
257
|
+
assert.equal(logs.length, 0);
|
|
258
|
+
assert.equal(result.isLoggedIn, true);
|
|
259
|
+
assert.match(result.message, /Already signed in with saved local DoorDash session state/);
|
|
260
|
+
});
|
|
261
|
+
test("bootstrapAuthSessionWithDeps returns immediately when an attached browser session is already authenticated", async () => {
|
|
262
|
+
const auth = {
|
|
263
|
+
success: true,
|
|
264
|
+
isLoggedIn: true,
|
|
265
|
+
email: "user@example.com",
|
|
266
|
+
firstName: "Test",
|
|
267
|
+
lastName: "User",
|
|
268
|
+
consumerId: "consumer-1",
|
|
269
|
+
marketId: "market-1",
|
|
270
|
+
defaultAddress: null,
|
|
271
|
+
cookiesPath: "/tmp/cookies.json",
|
|
272
|
+
storageStatePath: "/tmp/storage-state.json",
|
|
273
|
+
};
|
|
274
|
+
let openCalls = 0;
|
|
275
|
+
let waitCalls = 0;
|
|
276
|
+
let markCalls = 0;
|
|
277
|
+
const logs = [];
|
|
278
|
+
const result = await bootstrapAuthSessionWithDeps({
|
|
279
|
+
clearBlockedBrowserImport: async () => { },
|
|
280
|
+
checkPersistedAuth: async () => null,
|
|
281
|
+
importBrowserSessionIfAvailable: async () => true,
|
|
282
|
+
markBrowserImportAttempted: () => {
|
|
283
|
+
markCalls += 1;
|
|
284
|
+
},
|
|
285
|
+
getAttachedBrowserCdpCandidates: async () => {
|
|
286
|
+
throw new Error("should not inspect candidates on immediate browser-session import");
|
|
287
|
+
},
|
|
288
|
+
getReachableCdpCandidates: async () => {
|
|
289
|
+
throw new Error("should not probe reachability on immediate browser-session import");
|
|
290
|
+
},
|
|
291
|
+
openUrlInAttachedBrowser: async () => {
|
|
292
|
+
throw new Error("should not open a browser when immediate browser-session import succeeded");
|
|
293
|
+
},
|
|
294
|
+
openUrlInDefaultBrowser: async () => {
|
|
295
|
+
openCalls += 1;
|
|
296
|
+
return true;
|
|
297
|
+
},
|
|
298
|
+
waitForAttachedBrowserSessionImport: async () => {
|
|
299
|
+
waitCalls += 1;
|
|
300
|
+
return true;
|
|
301
|
+
},
|
|
302
|
+
waitForManagedBrowserLogin: async () => {
|
|
303
|
+
throw new Error("should not launch a managed browser when immediate browser-session import succeeded");
|
|
304
|
+
},
|
|
305
|
+
checkAuthDirect: async () => auth,
|
|
306
|
+
log: (message) => {
|
|
307
|
+
logs.push(message);
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
assert.equal(markCalls, 1);
|
|
311
|
+
assert.equal(openCalls, 0);
|
|
312
|
+
assert.equal(waitCalls, 0);
|
|
313
|
+
assert.equal(logs.length, 0);
|
|
314
|
+
assert.equal(result.isLoggedIn, true);
|
|
315
|
+
assert.match(result.message, /Imported an existing signed-in browser session/);
|
|
316
|
+
});
|
|
317
|
+
test("bootstrapAuthSessionWithDeps opens a watchable attached browser session before entering the full wait path", async () => {
|
|
318
|
+
const auth = {
|
|
319
|
+
success: true,
|
|
320
|
+
isLoggedIn: true,
|
|
321
|
+
email: "user@example.com",
|
|
322
|
+
firstName: "Test",
|
|
323
|
+
lastName: "User",
|
|
324
|
+
consumerId: "consumer-1",
|
|
325
|
+
marketId: "market-1",
|
|
326
|
+
defaultAddress: null,
|
|
327
|
+
cookiesPath: "/tmp/cookies.json",
|
|
328
|
+
storageStatePath: "/tmp/storage-state.json",
|
|
329
|
+
};
|
|
330
|
+
let openAttachedCalls = 0;
|
|
331
|
+
let openDefaultCalls = 0;
|
|
332
|
+
let waitCalls = 0;
|
|
333
|
+
let reachableCalls = 0;
|
|
334
|
+
let waitTimeoutMs = 0;
|
|
335
|
+
const logs = [];
|
|
336
|
+
const result = await bootstrapAuthSessionWithDeps({
|
|
337
|
+
clearBlockedBrowserImport: async () => { },
|
|
338
|
+
checkPersistedAuth: async () => null,
|
|
339
|
+
importBrowserSessionIfAvailable: async () => false,
|
|
340
|
+
markBrowserImportAttempted: () => { },
|
|
341
|
+
getAttachedBrowserCdpCandidates: async () => ["http://127.0.0.1:9222"],
|
|
342
|
+
getReachableCdpCandidates: async (candidates) => {
|
|
343
|
+
reachableCalls += 1;
|
|
344
|
+
return candidates;
|
|
345
|
+
},
|
|
346
|
+
openUrlInAttachedBrowser: async () => {
|
|
347
|
+
openAttachedCalls += 1;
|
|
348
|
+
return true;
|
|
349
|
+
},
|
|
350
|
+
openUrlInDefaultBrowser: async () => {
|
|
351
|
+
openDefaultCalls += 1;
|
|
352
|
+
return true;
|
|
353
|
+
},
|
|
354
|
+
waitForAttachedBrowserSessionImport: async (input) => {
|
|
355
|
+
waitCalls += 1;
|
|
356
|
+
waitTimeoutMs = input.timeoutMs;
|
|
357
|
+
return true;
|
|
358
|
+
},
|
|
359
|
+
waitForManagedBrowserLogin: async () => {
|
|
360
|
+
throw new Error("should not launch a managed browser when an attached browser is reachable");
|
|
361
|
+
},
|
|
362
|
+
checkAuthDirect: async () => auth,
|
|
363
|
+
log: (message) => {
|
|
364
|
+
logs.push(message);
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
assert.equal(reachableCalls, 1);
|
|
368
|
+
assert.equal(openAttachedCalls, 1);
|
|
369
|
+
assert.equal(openDefaultCalls, 0);
|
|
370
|
+
assert.equal(waitCalls, 1);
|
|
371
|
+
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/);
|
|
374
|
+
assert.match(result.message, /saved it for direct API use/);
|
|
375
|
+
});
|
|
376
|
+
test("bootstrapAuthSessionWithDeps falls back to a managed browser login window when no reusable browser connection is discoverable", async () => {
|
|
377
|
+
const auth = {
|
|
378
|
+
success: true,
|
|
379
|
+
isLoggedIn: true,
|
|
380
|
+
email: "user@example.com",
|
|
381
|
+
firstName: "Test",
|
|
382
|
+
lastName: "User",
|
|
383
|
+
consumerId: "consumer-1",
|
|
384
|
+
marketId: "market-1",
|
|
385
|
+
defaultAddress: null,
|
|
386
|
+
cookiesPath: "/tmp/cookies.json",
|
|
387
|
+
storageStatePath: "/tmp/storage-state.json",
|
|
388
|
+
};
|
|
389
|
+
let managedCalls = 0;
|
|
390
|
+
let attachedWaitCalls = 0;
|
|
391
|
+
const logs = [];
|
|
392
|
+
const result = await bootstrapAuthSessionWithDeps({
|
|
393
|
+
clearBlockedBrowserImport: async () => { },
|
|
394
|
+
checkPersistedAuth: async () => null,
|
|
395
|
+
importBrowserSessionIfAvailable: async () => false,
|
|
396
|
+
markBrowserImportAttempted: () => { },
|
|
397
|
+
getAttachedBrowserCdpCandidates: async () => ["http://127.0.0.1:9222"],
|
|
398
|
+
getReachableCdpCandidates: async () => [],
|
|
399
|
+
openUrlInAttachedBrowser: async () => false,
|
|
400
|
+
openUrlInDefaultBrowser: async () => true,
|
|
401
|
+
waitForAttachedBrowserSessionImport: async () => {
|
|
402
|
+
attachedWaitCalls += 1;
|
|
403
|
+
return false;
|
|
404
|
+
},
|
|
405
|
+
waitForManagedBrowserLogin: async () => {
|
|
406
|
+
managedCalls += 1;
|
|
407
|
+
return auth;
|
|
408
|
+
},
|
|
409
|
+
checkAuthDirect: async () => auth,
|
|
410
|
+
log: (message) => {
|
|
411
|
+
logs.push(message);
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
assert.equal(managedCalls, 1);
|
|
415
|
+
assert.equal(attachedWaitCalls, 0);
|
|
416
|
+
assert.match(logs.join("\n"), /temporary Chromium login window/);
|
|
417
|
+
assert.match(result.message, /temporary Chromium login window/);
|
|
418
|
+
assert.equal(result.success, true);
|
|
419
|
+
assert.equal(result.isLoggedIn, true);
|
|
420
|
+
});
|
|
421
|
+
test("bootstrapAuthSessionWithDeps falls back to quick troubleshooting guidance when the managed browser login window cannot launch", async () => {
|
|
422
|
+
const auth = {
|
|
423
|
+
success: true,
|
|
424
|
+
isLoggedIn: false,
|
|
425
|
+
email: null,
|
|
426
|
+
firstName: null,
|
|
427
|
+
lastName: null,
|
|
428
|
+
consumerId: null,
|
|
429
|
+
marketId: null,
|
|
430
|
+
defaultAddress: null,
|
|
431
|
+
cookiesPath: "/tmp/cookies.json",
|
|
432
|
+
storageStatePath: "/tmp/storage-state.json",
|
|
433
|
+
};
|
|
434
|
+
let attachedWaitCalls = 0;
|
|
435
|
+
let attachedWaitTimeoutMs = 0;
|
|
436
|
+
const logs = [];
|
|
437
|
+
const result = await bootstrapAuthSessionWithDeps({
|
|
438
|
+
clearBlockedBrowserImport: async () => { },
|
|
439
|
+
checkPersistedAuth: async () => null,
|
|
440
|
+
importBrowserSessionIfAvailable: async () => false,
|
|
441
|
+
markBrowserImportAttempted: () => { },
|
|
442
|
+
getAttachedBrowserCdpCandidates: async () => ["http://127.0.0.1:9222"],
|
|
443
|
+
getReachableCdpCandidates: async () => [],
|
|
444
|
+
openUrlInAttachedBrowser: async () => false,
|
|
445
|
+
openUrlInDefaultBrowser: async () => true,
|
|
446
|
+
waitForAttachedBrowserSessionImport: async (input) => {
|
|
447
|
+
attachedWaitCalls += 1;
|
|
448
|
+
attachedWaitTimeoutMs = input.timeoutMs;
|
|
449
|
+
return false;
|
|
450
|
+
},
|
|
451
|
+
waitForManagedBrowserLogin: async () => null,
|
|
452
|
+
checkAuthDirect: async () => auth,
|
|
453
|
+
log: (message) => {
|
|
454
|
+
logs.push(message);
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
assert.equal(attachedWaitCalls, 1);
|
|
458
|
+
assert.equal(attachedWaitTimeoutMs, 10_000);
|
|
459
|
+
assert.match(logs.join("\n"), /couldn't launch the temporary Chromium login window/i);
|
|
460
|
+
assert.match(logs.join("\n"), /won't keep you waiting for the full login timeout/i);
|
|
461
|
+
assert.equal(result.success, false);
|
|
462
|
+
assert.equal(result.isLoggedIn, false);
|
|
463
|
+
assert.match(result.message, /still isn't exposing a reusable browser session/);
|
|
464
|
+
});
|
|
166
465
|
test("parseOptionSelectionsJson parses structured recursive option selections", () => {
|
|
167
466
|
assert.deepEqual(parseOptionSelectionsJson('[{"groupId":"703393388","optionId":"4716032529"},{"groupId":"recommended_option_546935995","optionId":"546936011","children":[{"groupId":"780057412","optionId":"4702669757","quantity":2}]}]'), [
|
|
168
467
|
{ groupId: "703393388", optionId: "4716032529" },
|
package/dist/session-storage.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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:
|
|
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 automatic 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
|
|
34
|
+
## Install the bundled runtime if needed
|
|
35
35
|
|
|
36
|
-
If
|
|
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
|
-
##
|
|
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 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.
|
|
64
|
+
|
|
65
|
+
`doordash-cli auth-check` can also quietly import a discoverable 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 automatic browser-session reuse disabled until you explicitly run `doordash-cli login` again.
|
|
68
|
+
|
|
69
|
+
## Browser-session troubleshooting
|
|
60
70
|
|
|
61
|
-
|
|
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.
|
|
62
72
|
|
|
63
|
-
If
|
|
73
|
+
If you expected reuse from another browser instead, make sure that browser exposes a compatible CDP endpoint, then rerun `doordash-cli login`.
|
package/man/dd-cli.1
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH DD-CLI 1 "2026-
|
|
1
|
+
.TH DD-CLI 1 "2026-04-10" "doordash-cli 0.3.3" "User Commands"
|
|
2
2
|
.SH NAME
|
|
3
3
|
dd-cli, doordash-cli \- unofficial cart-safe DoorDash CLI for browsing, read-only existing-order inspection, and cart management
|
|
4
4
|
.SH SYNOPSIS
|
|
@@ -30,19 +30,27 @@ is installed as a lowercase alias.
|
|
|
30
30
|
.SH COMMANDS
|
|
31
31
|
.TP
|
|
32
32
|
.B install-browser
|
|
33
|
-
Install the
|
|
33
|
+
Install the bundled Playwright Chromium runtime used when the CLI needs a
|
|
34
|
+
local browser, including the temporary login-window fallback.
|
|
34
35
|
.TP
|
|
35
36
|
.B auth-check
|
|
36
37
|
Verify whether the saved session appears authenticated. This command can also
|
|
37
|
-
quietly reuse an already-signed-in
|
|
38
|
-
|
|
38
|
+
quietly reuse or import an already-signed-in browser session when one is
|
|
39
|
+
available, unless
|
|
40
|
+
.B logout
|
|
41
|
+
explicitly disabled that auto-reuse.
|
|
39
42
|
.TP
|
|
40
43
|
.B login
|
|
41
|
-
|
|
42
|
-
|
|
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,
|
|
47
|
+
.B login
|
|
48
|
+
exits non-zero.
|
|
43
49
|
.TP
|
|
44
50
|
.B logout
|
|
45
|
-
Delete stored session material used by this CLI
|
|
51
|
+
Delete stored session material used by this CLI and disable automatic
|
|
52
|
+
browser-session reuse until the next explicit
|
|
53
|
+
.BR login .
|
|
46
54
|
.TP
|
|
47
55
|
.BI "set-address --address " ADDRESS
|
|
48
56
|
Resolve and persist the active delivery address. Saved addresses are reused when
|
|
@@ -152,7 +160,7 @@ dd-cli --version
|
|
|
152
160
|
man dd-cli
|
|
153
161
|
.EE
|
|
154
162
|
.PP
|
|
155
|
-
Install the
|
|
163
|
+
Install the bundled runtime once if needed:
|
|
156
164
|
.PP
|
|
157
165
|
.EX
|
|
158
166
|
doordash-cli install-browser
|
|
@@ -195,14 +203,18 @@ The CLI persists reusable session state under the user's configuration
|
|
|
195
203
|
directory. The exact file layout is an implementation detail and may evolve over
|
|
196
204
|
time, but the saved state is reused across runs unless cleared with
|
|
197
205
|
.BR logout .
|
|
206
|
+
After
|
|
207
|
+
.BR logout ,
|
|
208
|
+
automatic browser-session reuse stays disabled until the next explicit
|
|
209
|
+
.BR login .
|
|
198
210
|
.SH ENVIRONMENT
|
|
199
|
-
.TP
|
|
200
|
-
.B DOORDASH_MANAGED_BROWSER_CDP_URL
|
|
201
|
-
Preferred Chrome DevTools Protocol endpoint for importing an already-signed-in
|
|
202
|
-
compatible managed-browser session.
|
|
203
211
|
.PP
|
|
204
|
-
|
|
205
|
-
|
|
212
|
+
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
|
|
216
|
+
.I docs/install.md
|
|
217
|
+
for setup and troubleshooting.
|
|
206
218
|
.SH EXIT STATUS
|
|
207
219
|
.TP
|
|
208
220
|
.B 0
|
package/package.json
CHANGED