@agentrhq/webcmd 0.4.2 → 0.4.3

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.
Files changed (59) hide show
  1. package/cli-manifest.json +755 -174
  2. package/clis/_shared/site-auth.js +0 -1
  3. package/clis/_shared/site-auth.test.js +0 -1
  4. package/clis/amazon-in/checkout.js +0 -1
  5. package/clis/blinkit/checkout.js +0 -1
  6. package/clis/blinkit/place-order.js +0 -1
  7. package/clis/chatgpt/model.js +2 -2
  8. package/clis/chatgpt/model.test.js +7 -1
  9. package/clis/chatgpt/utils.js +8 -0
  10. package/clis/chatgpt/utils.test.js +92 -1
  11. package/clis/district/checkout.js +0 -1
  12. package/clis/district/seats.js +0 -1
  13. package/clis/district/set-location.js +0 -1
  14. package/clis/district/showtimes.js +0 -1
  15. package/clis/google/images.js +456 -0
  16. package/clis/google/images.test.js +375 -0
  17. package/clis/instagram/user.js +5 -13
  18. package/clis/instagram/user.test.js +66 -0
  19. package/clis/mercury/check-login.js +0 -1
  20. package/clis/mercury/reimbursement-draft.js +0 -1
  21. package/clis/practo/book-confirm.js +0 -1
  22. package/clis/practo/cancel.js +0 -1
  23. package/clis/trip/attraction.js +74 -0
  24. package/clis/trip/car.js +74 -0
  25. package/clis/trip/deals.js +61 -0
  26. package/clis/trip/flight-round.js +88 -0
  27. package/clis/trip/flight.js +83 -0
  28. package/clis/trip/hotel-search.js +80 -0
  29. package/clis/trip/hotel.js +54 -0
  30. package/clis/trip/package.js +93 -0
  31. package/clis/trip/search.js +43 -0
  32. package/clis/trip/tour.js +84 -0
  33. package/clis/trip/train.js +76 -0
  34. package/clis/trip/transfer.js +82 -0
  35. package/clis/trip/trip.test.js +1420 -0
  36. package/clis/trip/utils.js +911 -0
  37. package/dist/src/build-manifest.js +0 -1
  38. package/dist/src/build-manifest.test.js +4 -0
  39. package/dist/src/cli.js +7 -7
  40. package/dist/src/cli.test.js +26 -10
  41. package/dist/src/command-presentation.js +1 -1
  42. package/dist/src/command-presentation.test.js +3 -0
  43. package/dist/src/command-surface.js +1 -1
  44. package/dist/src/command-surface.test.js +4 -0
  45. package/dist/src/commands/auth.js +0 -2
  46. package/dist/src/commands/auth.test.js +0 -2
  47. package/dist/src/discovery.js +0 -1
  48. package/dist/src/execution.js +3 -3
  49. package/dist/src/execution.test.js +68 -15
  50. package/dist/src/hosted/browser-args.js +2 -2
  51. package/dist/src/hosted/browser-args.test.js +10 -0
  52. package/dist/src/hosted/client.js +2 -2
  53. package/dist/src/manifest-types.d.ts +0 -2
  54. package/dist/src/registry.d.ts +0 -2
  55. package/dist/src/registry.js +0 -1
  56. package/hosted-contract.json +767 -3
  57. package/package.json +2 -2
  58. package/skills/webcmd-browser/SKILL.md +2 -1
  59. package/skills/webcmd-usage/SKILL.md +1 -1
@@ -90,7 +90,6 @@ export function registerSiteAuthCommands(config) {
90
90
  strategy: Strategy.COOKIE,
91
91
  browser: true,
92
92
  navigateBefore: false,
93
- defaultWindowMode: 'foreground',
94
93
  siteSession: 'persistent',
95
94
  args: [],
96
95
  columns: loginColumns(config),
@@ -35,7 +35,6 @@ describe('site auth command helper', () => {
35
35
  access: 'write',
36
36
  browser: true,
37
37
  navigateBefore: false,
38
- defaultWindowMode: 'foreground',
39
38
  siteSession: 'persistent',
40
39
  });
41
40
  expect(login.args).toEqual([]);
@@ -376,7 +376,6 @@ cli({
376
376
  navigateBefore: false,
377
377
  siteSession: 'persistent',
378
378
  freshPage: true,
379
- defaultWindowMode: 'foreground',
380
379
  args: [
381
380
  { name: 'input', required: true, positional: true, help: 'Amazon.in product URL or ASIN' },
382
381
  { name: 'quantity', type: 'int', default: 1, help: 'Quantity (1-10)' },
@@ -10,7 +10,6 @@ cli({
10
10
  strategy: Strategy.COOKIE,
11
11
  browser: true,
12
12
  navigateBefore: false,
13
- defaultWindowMode: 'foreground',
14
13
  args: [],
15
14
  columns: ['status', 'itemCount', 'itemsTotal', 'deliveryCharge', 'handlingCharge', 'payable', 'cartState', 'checkoutBlocked', 'validations'],
16
15
  func: async (page) => {
@@ -35,7 +35,6 @@ cli({
35
35
  strategy: Strategy.COOKIE,
36
36
  browser: true,
37
37
  navigateBefore: false,
38
- defaultWindowMode: 'foreground',
39
38
  args: [
40
39
  { name: 'confirm', type: 'bool', default: false, help: 'Required acknowledgement that this may place/pay for a real order' },
41
40
  ],
@@ -10,14 +10,14 @@ export const modelCommand = cli({
10
10
  site: 'chatgpt',
11
11
  name: 'model',
12
12
  access: 'write',
13
- description: 'Switch ChatGPT web intelligence level (fast, balanced, advanced, very-high, pro)',
13
+ description: 'Switch ChatGPT web model or intelligence level (GPT-5.6 Pro, fast, balanced, advanced, very-high, pro)',
14
14
  domain: CHATGPT_DOMAIN,
15
15
  strategy: Strategy.COOKIE,
16
16
  browser: true,
17
17
  siteSession: 'persistent',
18
18
  navigateBefore: false,
19
19
  args: [
20
- { name: 'model', required: true, positional: true, help: 'Intelligence level to switch to', choices: CHATGPT_MODEL_CHOICES },
20
+ { name: 'model', required: true, positional: true, help: 'ChatGPT model or intelligence level to switch to', choices: CHATGPT_MODEL_CHOICES },
21
21
  { name: 'project', valueRequired: true, help: 'Open a ChatGPT project ID or /g/g-p-<id> URL before switching intelligence level' },
22
22
  ],
23
23
  columns: ['Status', 'Model'],
@@ -7,7 +7,7 @@ const mocks = vi.hoisted(() => ({
7
7
 
8
8
  vi.mock('./utils.js', () => ({
9
9
  CHATGPT_DOMAIN: 'chatgpt.com',
10
- CHATGPT_MODEL_CHOICES: ['fast', 'speed', 'instant', 'balanced', 'advanced', 'high', 'thinking', 'very-high', 'pro'],
10
+ CHATGPT_MODEL_CHOICES: ['fast', 'speed', 'instant', 'balanced', 'advanced', 'high', 'thinking', 'very-high', 'pro', 'gpt-5.6-pro'],
11
11
  navigateToProject: mocks.navigateToProject,
12
12
  selectChatGPTModel: mocks.selectChatGPTModel,
13
13
  }));
@@ -21,6 +21,12 @@ beforeEach(() => {
21
21
  });
22
22
 
23
23
  describe('chatgpt model project routing', () => {
24
+ it('documents exact model targets alongside intelligence levels', () => {
25
+ expect(modelCommand.description).toContain('GPT-5.6 Pro');
26
+ expect(modelCommand.args[0].help).toContain('model or intelligence level');
27
+ expect(modelCommand.args[0].choices).toContain('gpt-5.6-pro');
28
+ });
29
+
24
30
  it('opens a project before selecting the requested model', async () => {
25
31
  const page = {};
26
32
 
@@ -43,6 +43,14 @@ const CHATGPT_MODEL_TARGETS = {
43
43
  intelligenceOrder: 3,
44
44
  aliases: ['ultra', 'xhigh', 'x-high', 'extra-high', 'very high'],
45
45
  },
46
+ 'gpt-5.6-pro': {
47
+ label: 'GPT-5.6 Pro',
48
+ labels: ['GPT-5.6 Pro', 'GPT-5.6 Sol Pro'],
49
+ optionLabels: ['GPT-5.6 Pro', 'GPT-5.6 Sol Pro'],
50
+ testIds: ['model-switcher-gpt-5-6-pro'],
51
+ aliases: ['gpt-5-6-pro', 'gpt-5.6-sol-pro', 'gpt-5-6-sol-pro', 'gpt-5.6', 'gpt-5-6', '5.6-pro', '5.6'],
52
+ modelConfig: { modelSlug: 'gpt-5-6-pro', effort: 'standard' },
53
+ },
46
54
  pro: {
47
55
  label: 'Pro',
48
56
  labels: ['Pro', 'Professional', 'professional', 'professional'],
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  import { JSDOM } from 'jsdom';
5
5
  import { afterEach, describe, expect, it, vi } from 'vitest';
6
6
  import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors';
7
- import { __test__, getChatGPTDetailRows, getChatGPTImageAssets, getChatGPTResponsePairCounts, getChatGPTVisibleImageUrls, getCurrentChatGPTModel, getCurrentChatGPTTool, getVisibleMessages, isGenerating, navigateToProject, openChatGPTConversation, prepareChatGPTImagePaths, selectChatGPTModel, selectChatGPTTool, sendChatGPTMessage, uploadChatGPTImages, waitForChatGPTDeepResearchResult, waitForChatGPTDetailRows, waitForChatGPTImages, waitForChatGPTResponse } from './utils.js';
7
+ import { CHATGPT_MODEL_CHOICES, __test__, getChatGPTDetailRows, getChatGPTImageAssets, getChatGPTResponsePairCounts, getChatGPTVisibleImageUrls, getCurrentChatGPTModel, getCurrentChatGPTTool, getVisibleMessages, isGenerating, navigateToProject, openChatGPTConversation, prepareChatGPTImagePaths, selectChatGPTModel, selectChatGPTTool, sendChatGPTMessage, uploadChatGPTImages, waitForChatGPTDeepResearchResult, waitForChatGPTDetailRows, waitForChatGPTImages, waitForChatGPTResponse } from './utils.js';
8
8
 
9
9
  const tempDirs = [];
10
10
 
@@ -431,6 +431,16 @@ describe('chatgpt deep research result extraction', () => {
431
431
  });
432
432
 
433
433
  describe('chatgpt model selection validation', () => {
434
+ it('offers practical GPT-5.6 Pro aliases to CLI callers', () => {
435
+ expect(CHATGPT_MODEL_CHOICES).toEqual(expect.arrayContaining([
436
+ 'gpt-5.6-pro',
437
+ 'gpt-5-6-pro',
438
+ 'gpt-5.6-sol-pro',
439
+ 'gpt-5.6',
440
+ '5.6',
441
+ ]));
442
+ });
443
+
434
444
  it('rejects unknown model names', async () => {
435
445
  await expect(selectChatGPTModel({ nativeClick: vi.fn() }, 'unknown'))
436
446
  .rejects.toBeInstanceOf(ArgumentError);
@@ -497,6 +507,63 @@ describe('chatgpt model selection validation', () => {
497
507
  expect(page.nativeClick).not.toHaveBeenCalled();
498
508
  });
499
509
 
510
+ it('sets GPT-5.6 Pro through the exact ChatGPT model config slug', async () => {
511
+ const fetchMock = vi.spyOn(globalThis, 'fetch')
512
+ .mockResolvedValueOnce(new Response(JSON.stringify({ accessToken: 'token' }), { status: 200 }))
513
+ .mockResolvedValueOnce(new Response(JSON.stringify({ success: true }), { status: 200 }));
514
+ let objectCall = 0;
515
+ const page = {
516
+ goto: vi.fn().mockResolvedValue(undefined),
517
+ wait: vi.fn().mockResolvedValue(undefined),
518
+ nativeClick: vi.fn().mockResolvedValue(undefined),
519
+ getCookies: vi.fn().mockResolvedValue([{ name: '__Secure-next-auth.session-token', value: 'cookie', domain: '.chatgpt.com' }]),
520
+ evaluate: vi.fn((script) => {
521
+ if (script === 'window.location.href') return Promise.resolve('https://chatgpt.com/c/demo');
522
+ if (String(script).includes('oai-last-model-config')) return Promise.resolve(true);
523
+ objectCall += 1;
524
+ if (objectCall === 1) return Promise.resolve({ isLoggedIn: true, hasLoginGate: false, hasComposer: true });
525
+ if (objectCall === 2) return Promise.resolve({ model: 'balanced', label: 'Balanced' });
526
+ if (objectCall === 3) return Promise.resolve({ isLoggedIn: true, hasLoginGate: false, hasComposer: true });
527
+ if (objectCall === 4) return Promise.resolve({ model: 'gpt-5.6-pro', label: 'GPT-5.6 Pro' });
528
+ return Promise.resolve({});
529
+ }),
530
+ };
531
+
532
+ await expect(selectChatGPTModel(page, 'gpt-5.6-pro'))
533
+ .resolves.toEqual({ Status: 'Success', Model: 'GPT-5.6 Pro' });
534
+ expect(fetchMock.mock.calls[1][0]).toContain('model_slug=gpt-5-6-pro');
535
+ expect(fetchMock.mock.calls[1][0]).toContain('thinking_effort=standard');
536
+ expect(page.nativeClick).not.toHaveBeenCalled();
537
+ });
538
+
539
+ it('does not accept generic Pro read-back as proof of GPT-5.6 Pro selection', async () => {
540
+ vi.spyOn(globalThis, 'fetch')
541
+ .mockResolvedValueOnce(new Response(JSON.stringify({ accessToken: 'token' }), { status: 200 }))
542
+ .mockResolvedValueOnce(new Response(JSON.stringify({ success: true }), { status: 200 }));
543
+ let objectCall = 0;
544
+ const page = {
545
+ goto: vi.fn().mockResolvedValue(undefined),
546
+ wait: vi.fn().mockResolvedValue(undefined),
547
+ nativeClick: vi.fn().mockResolvedValue(undefined),
548
+ getCookies: vi.fn().mockResolvedValue([{ name: '__Secure-next-auth.session-token', value: 'cookie', domain: '.chatgpt.com' }]),
549
+ evaluate: vi.fn((script) => {
550
+ if (script === 'window.location.href') return Promise.resolve('https://chatgpt.com/c/demo');
551
+ if (String(script).includes('oai-last-model-config')) return Promise.resolve(true);
552
+ objectCall += 1;
553
+ if (objectCall === 1) return Promise.resolve({ isLoggedIn: true, hasLoginGate: false, hasComposer: true });
554
+ if (objectCall === 2) return Promise.resolve({ model: 'pro', label: 'Pro' });
555
+ if (objectCall === 3) return Promise.resolve({ isLoggedIn: true, hasLoginGate: false, hasComposer: true });
556
+ if (objectCall === 4) return Promise.resolve({ model: 'pro', label: 'Pro' });
557
+ if (objectCall === 5) return Promise.resolve({ found: true, x: 10, y: 20 });
558
+ return Promise.resolve({ found: false });
559
+ }),
560
+ };
561
+
562
+ await expect(selectChatGPTModel(page, 'gpt-5.6'))
563
+ .rejects.toBeInstanceOf(CommandExecutionError);
564
+ expect(page.nativeClick).toHaveBeenCalledWith(10, 20);
565
+ });
566
+
500
567
  it('falls back to the visible picker when the model config API does not prove selection', async () => {
501
568
  vi.spyOn(globalThis, 'fetch')
502
569
  .mockResolvedValueOnce(new Response(JSON.stringify({ accessToken: 'token' }), { status: 200 }))
@@ -1157,6 +1224,30 @@ describe('chatgpt current model detection', () => {
1157
1224
  await expect(getCurrentChatGPTModel(page)).resolves.toEqual({ model: 'pro', label: 'Pro' });
1158
1225
  });
1159
1226
 
1227
+ it('distinguishes the GPT-5.6 Pro test id from the generic Pro level', async () => {
1228
+ const page = createDomEvaluatePage(`
1229
+ <form>
1230
+ <button type="button">
1231
+ <span data-testid="model-switcher-gpt-5-6-pro">Pro</span>
1232
+ </button>
1233
+ </form>
1234
+ `);
1235
+
1236
+ await expect(getCurrentChatGPTModel(page))
1237
+ .resolves.toEqual({ model: 'gpt-5.6-pro', label: 'GPT-5.6 Pro' });
1238
+ });
1239
+
1240
+ it('recognizes the GPT-5.6 Sol Pro visible label', async () => {
1241
+ const page = createDomEvaluatePage(`
1242
+ <form>
1243
+ <button type="button">GPT-5.6 Sol Pro</button>
1244
+ </form>
1245
+ `);
1246
+
1247
+ await expect(getCurrentChatGPTModel(page))
1248
+ .resolves.toEqual({ model: 'gpt-5.6-pro', label: 'GPT-5.6 Pro' });
1249
+ });
1250
+
1160
1251
  it('returns null fields when the model selector is missing', async () => {
1161
1252
  const page = createDomEvaluatePage('<form><button>Send</button></form>');
1162
1253
 
@@ -264,7 +264,6 @@ cli({
264
264
  strategy: Strategy.COOKIE,
265
265
  browser: true,
266
266
  navigateBefore: false,
267
- defaultWindowMode: 'foreground',
268
267
  siteSession: 'persistent',
269
268
  // Checkout is the most state-sensitive district command: always start on a
270
269
  // clean page so modals/drawers left by earlier commands cannot poison it.
@@ -140,7 +140,6 @@ cli({
140
140
  strategy: Strategy.COOKIE,
141
141
  browser: true,
142
142
  navigateBefore: false,
143
- defaultWindowMode: 'foreground',
144
143
  siteSession: 'persistent',
145
144
  args: [
146
145
  {
@@ -32,7 +32,6 @@ cli({
32
32
  strategy: Strategy.COOKIE,
33
33
  browser: true,
34
34
  navigateBefore: false,
35
- defaultWindowMode: 'foreground',
36
35
  siteSession: 'persistent',
37
36
  args: [
38
37
  {
@@ -351,7 +351,6 @@ cli({
351
351
  strategy: Strategy.COOKIE,
352
352
  browser: true,
353
353
  navigateBefore: false,
354
- defaultWindowMode: 'foreground',
355
354
  siteSession: 'persistent',
356
355
  args: [
357
356
  { name: 'movie', positional: true, required: true, help: 'Movie name or District movie URL' },