@agentrhq/webcmd 0.3.0 → 0.3.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.
Files changed (181) hide show
  1. package/README.md +13 -2
  2. package/cli-manifest.json +217 -13
  3. package/clis/chatgpt/ask.js +1 -1
  4. package/clis/chatgpt/ask.test.js +11 -0
  5. package/clis/chatgpt/commands.test.js +102 -1
  6. package/clis/chatgpt/deep-research-result.js +45 -3
  7. package/clis/chatgpt/image.js +22 -2
  8. package/clis/chatgpt/project-file-add.js +24 -1
  9. package/clis/chatgpt/utils.js +258 -52
  10. package/clis/chatgpt/utils.test.js +259 -1
  11. package/clis/claude/ask.js +21 -1
  12. package/clis/facebook/feed.js +110 -24
  13. package/clis/facebook/feed.test.js +62 -0
  14. package/clis/facebook/search.js +185 -38
  15. package/clis/facebook/search.test.js +95 -50
  16. package/clis/instagram/explore.js +30 -12
  17. package/clis/instagram/explore.test.js +77 -0
  18. package/clis/instagram/post.js +14 -1
  19. package/clis/instagram/reel.js +13 -1
  20. package/clis/linkedin/company.js +153 -0
  21. package/clis/linkedin/company.test.js +111 -0
  22. package/clis/linkedin/connections.js +135 -0
  23. package/clis/linkedin/connections.test.js +141 -0
  24. package/clis/mercury/reimbursement-draft.js +12 -1
  25. package/clis/twitter/article-evaluate.test.js +27 -0
  26. package/clis/twitter/article.js +73 -16
  27. package/clis/twitter/article.test.js +209 -0
  28. package/clis/twitter/download.js +6 -1
  29. package/clis/twitter/post.js +14 -1
  30. package/clis/twitter/profile.js +1 -1
  31. package/clis/twitter/profile.test.js +8 -0
  32. package/clis/twitter/quote.js +11 -1
  33. package/clis/twitter/reply.js +11 -1
  34. package/dist/src/browser/base-page.d.ts +26 -0
  35. package/dist/src/browser/base-page.js +23 -8
  36. package/dist/src/browser/base-page.test.js +45 -12
  37. package/dist/src/browser/command-catalog.d.ts +6 -0
  38. package/dist/src/browser/command-catalog.js +259 -0
  39. package/dist/src/browser/command-catalog.test.d.ts +1 -0
  40. package/dist/src/browser/command-catalog.test.js +79 -0
  41. package/dist/src/browser/runtime/local-cloak/darwin-background-launch.d.ts +17 -0
  42. package/dist/src/browser/runtime/local-cloak/darwin-background-launch.js +106 -0
  43. package/dist/src/browser/runtime/local-cloak/darwin-background-launch.test.d.ts +1 -0
  44. package/dist/src/browser/runtime/local-cloak/darwin-background-launch.test.js +65 -0
  45. package/dist/src/browser/runtime/local-cloak/session-manager.d.ts +7 -0
  46. package/dist/src/browser/runtime/local-cloak/session-manager.js +23 -9
  47. package/dist/src/browser/runtime/local-cloak/session-manager.test.js +36 -0
  48. package/dist/src/browser/target-resolver.d.ts +1 -0
  49. package/dist/src/browser/target-resolver.js +99 -4
  50. package/dist/src/browser/target-resolver.test.js +141 -1
  51. package/dist/src/build-manifest.d.ts +6 -0
  52. package/dist/src/build-manifest.js +18 -1
  53. package/dist/src/build-manifest.test.js +57 -1
  54. package/dist/src/builtin-command-surface.d.ts +9 -0
  55. package/dist/src/builtin-command-surface.js +16 -0
  56. package/dist/src/check-hosted-contract.test.d.ts +1 -0
  57. package/dist/src/check-hosted-contract.test.js +56 -0
  58. package/dist/src/cli.js +58 -106
  59. package/dist/src/cli.test.js +71 -1
  60. package/dist/src/command-presentation.d.ts +95 -0
  61. package/dist/src/command-presentation.js +486 -0
  62. package/dist/src/command-presentation.test.d.ts +1 -0
  63. package/dist/src/command-presentation.test.js +97 -0
  64. package/dist/src/command-surface.d.ts +43 -0
  65. package/dist/src/command-surface.js +205 -0
  66. package/dist/src/command-surface.test.d.ts +1 -0
  67. package/dist/src/command-surface.test.js +406 -0
  68. package/dist/src/commanderAdapter.d.ts +5 -1
  69. package/dist/src/commanderAdapter.js +12 -55
  70. package/dist/src/commanderAdapter.test.js +15 -3
  71. package/dist/src/community-plugin-sync.d.ts +11 -0
  72. package/dist/src/community-plugin-sync.js +138 -0
  73. package/dist/src/community-plugin-sync.test.d.ts +1 -0
  74. package/dist/src/community-plugin-sync.test.js +123 -0
  75. package/dist/src/completion-fast.d.ts +4 -4
  76. package/dist/src/completion-fast.js +15 -34
  77. package/dist/src/completion-shared.d.ts +4 -0
  78. package/dist/src/completion-shared.js +38 -0
  79. package/dist/src/completion.js +3 -27
  80. package/dist/src/completion.test.js +31 -2
  81. package/dist/src/docs-sync-review-cli.test.d.ts +1 -0
  82. package/dist/src/docs-sync-review-cli.test.js +322 -0
  83. package/dist/src/docs-sync-review.d.ts +117 -0
  84. package/dist/src/docs-sync-review.js +475 -0
  85. package/dist/src/docs-sync-review.test.d.ts +1 -0
  86. package/dist/src/docs-sync-review.test.js +324 -0
  87. package/dist/src/errors.d.ts +12 -3
  88. package/dist/src/errors.js +11 -7
  89. package/dist/src/errors.test.js +14 -1
  90. package/dist/src/execution.d.ts +1 -2
  91. package/dist/src/execution.js +4 -45
  92. package/dist/src/generate-release-notes-cli.test.js +7 -2
  93. package/dist/src/help.d.ts +4 -0
  94. package/dist/src/help.js +50 -255
  95. package/dist/src/help.test.js +27 -1
  96. package/dist/src/hosted/args.d.ts +2 -7
  97. package/dist/src/hosted/args.js +2 -99
  98. package/dist/src/hosted/args.test.js +15 -1
  99. package/dist/src/hosted/availability.d.ts +13 -0
  100. package/dist/src/hosted/availability.js +16 -0
  101. package/dist/src/hosted/availability.test.d.ts +1 -0
  102. package/dist/src/hosted/availability.test.js +180 -0
  103. package/dist/src/hosted/browser-args.d.ts +18 -0
  104. package/dist/src/hosted/browser-args.js +152 -0
  105. package/dist/src/hosted/browser-args.test.d.ts +1 -0
  106. package/dist/src/hosted/browser-args.test.js +182 -0
  107. package/dist/src/hosted/client.d.ts +31 -2
  108. package/dist/src/hosted/client.js +441 -53
  109. package/dist/src/hosted/client.test.js +651 -32
  110. package/dist/src/hosted/config.d.ts +7 -2
  111. package/dist/src/hosted/config.js +26 -4
  112. package/dist/src/hosted/config.test.js +34 -1
  113. package/dist/src/hosted/contract.d.ts +94 -0
  114. package/dist/src/hosted/contract.js +208 -0
  115. package/dist/src/hosted/contract.test.d.ts +1 -0
  116. package/dist/src/hosted/contract.test.js +361 -0
  117. package/dist/src/hosted/credentials.d.ts +38 -0
  118. package/dist/src/hosted/credentials.js +248 -0
  119. package/dist/src/hosted/credentials.test.d.ts +1 -0
  120. package/dist/src/hosted/credentials.test.js +93 -0
  121. package/dist/src/hosted/file-contract.test.d.ts +1 -0
  122. package/dist/src/hosted/file-contract.test.js +174 -0
  123. package/dist/src/hosted/files.d.ts +29 -0
  124. package/dist/src/hosted/files.js +296 -0
  125. package/dist/src/hosted/files.test.d.ts +1 -0
  126. package/dist/src/hosted/files.test.js +231 -0
  127. package/dist/src/hosted/main-lifecycle.test.d.ts +1 -0
  128. package/dist/src/hosted/main-lifecycle.test.js +213 -0
  129. package/dist/src/hosted/manifest.d.ts +5 -0
  130. package/dist/src/hosted/manifest.js +21 -66
  131. package/dist/src/hosted/manifest.test.js +120 -2
  132. package/dist/src/hosted/output-parity.test.d.ts +1 -0
  133. package/dist/src/hosted/output-parity.test.js +108 -0
  134. package/dist/src/hosted/root-command-surface.test.d.ts +1 -0
  135. package/dist/src/hosted/root-command-surface.test.js +646 -0
  136. package/dist/src/hosted/runner.d.ts +7 -0
  137. package/dist/src/hosted/runner.js +554 -253
  138. package/dist/src/hosted/runner.test.js +1471 -53
  139. package/dist/src/hosted/setup.d.ts +3 -2
  140. package/dist/src/hosted/setup.js +45 -15
  141. package/dist/src/hosted/setup.test.js +133 -6
  142. package/dist/src/hosted/types.d.ts +101 -23
  143. package/dist/src/main.js +120 -108
  144. package/dist/src/manifest-types.d.ts +2 -0
  145. package/dist/src/output.d.ts +17 -2
  146. package/dist/src/output.js +88 -81
  147. package/dist/src/output.test.js +141 -51
  148. package/dist/src/pipeline/executor.test.js +1 -0
  149. package/dist/src/pipeline/steps/download.test.js +1 -0
  150. package/dist/src/plugin-create-cli.test.d.ts +1 -0
  151. package/dist/src/plugin-create-cli.test.js +37 -0
  152. package/dist/src/plugin-manifest.d.ts +12 -0
  153. package/dist/src/plugin-manifest.js +16 -0
  154. package/dist/src/plugin-manifest.test.js +19 -1
  155. package/dist/src/plugin-scaffold.d.ts +4 -1
  156. package/dist/src/plugin-scaffold.js +4 -1
  157. package/dist/src/plugin-scaffold.test.js +23 -8
  158. package/dist/src/plugin.js +4 -1
  159. package/dist/src/plugin.test.js +13 -0
  160. package/dist/src/registry.d.ts +2 -0
  161. package/dist/src/release-notes.js +12 -4
  162. package/dist/src/release-notes.test.js +27 -15
  163. package/dist/src/root-command-surface.d.ts +31 -0
  164. package/dist/src/root-command-surface.js +106 -0
  165. package/dist/src/serialization.d.ts +1 -16
  166. package/dist/src/serialization.js +5 -55
  167. package/dist/src/stream-write.d.ts +12 -0
  168. package/dist/src/stream-write.js +91 -0
  169. package/dist/src/stream-write.test.d.ts +1 -0
  170. package/dist/src/stream-write.test.js +186 -0
  171. package/dist/src/types.d.ts +5 -0
  172. package/dist/src/utils.d.ts +1 -1
  173. package/dist/src/utils.js +2 -8
  174. package/dist/src/utils.test.js +50 -0
  175. package/hosted-contract.json +36748 -0
  176. package/package.json +6 -1
  177. package/scripts/check-hosted-contract.mjs +108 -0
  178. package/scripts/docs-sync-review.ts +332 -0
  179. package/scripts/sync-community-plugins.ts +14 -0
  180. package/skills/webcmd-autofix/SKILL.md +26 -37
  181. package/skills/webcmd-usage/SKILL.md +7 -0
@@ -1,8 +1,20 @@
1
- import { describe, expect, it } from 'vitest';
1
+ import { Writable } from 'node:stream';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import { commandHelpData, formatCommandHelpText, formatSiteHelpText, renderStructuredHelp, siteHelpData, } from '../help.js';
4
+ import { serializeCommand } from '../serialization.js';
5
+ import { Strategy } from '../registry.js';
2
6
  import { commandNamesForSite, findHostedCommand, hostedListRows, renderHostedCommandHelp, renderHostedSiteHelp, siteNames, } from './manifest.js';
7
+ import { makeHostedConfig } from './config.js';
8
+ import { runHostedCli } from './runner.js';
9
+ import { formatRootHelp } from '../command-presentation.js';
10
+ import { HOSTED_ROOT_HELP } from '../completion-shared.js';
3
11
  const manifest = {
4
12
  userId: 'user_demo',
5
- generatedAt: '2026-07-08T00:00:00.000Z',
13
+ metadata: {
14
+ contractSchemaVersion: 1,
15
+ webcmdPackageVersion: '0.3.0',
16
+ generatedAt: '2026-07-08T00:00:00.000Z',
17
+ },
6
18
  commands: [
7
19
  {
8
20
  site: 'github',
@@ -26,9 +38,34 @@ const manifest = {
26
38
  strategy: 'LOCAL',
27
39
  browser: false,
28
40
  args: [],
41
+ columns: [],
29
42
  },
30
43
  ],
31
44
  };
45
+ const equivalentLocalCommand = {
46
+ site: 'github',
47
+ name: 'whoami',
48
+ aliases: ['me'],
49
+ description: 'Show GitHub identity',
50
+ access: 'read',
51
+ strategy: Strategy.COOKIE,
52
+ browser: true,
53
+ args: [],
54
+ columns: ['username'],
55
+ domain: 'github.com',
56
+ };
57
+ function sink() {
58
+ let data = '';
59
+ return {
60
+ stream: new Writable({
61
+ write(chunk, _encoding, callback) {
62
+ data += String(chunk);
63
+ callback();
64
+ },
65
+ }),
66
+ text: () => data,
67
+ };
68
+ }
32
69
  describe('hosted manifest helpers', () => {
33
70
  it('filters LOCAL commands from hosted list rows', () => {
34
71
  expect(hostedListRows(manifest, true).map((row) => row.command)).toEqual(['github/whoami']);
@@ -43,4 +80,85 @@ describe('hosted manifest helpers', () => {
43
80
  expect(renderHostedSiteHelp(manifest, 'github')).toContain('whoami');
44
81
  expect(renderHostedCommandHelp(manifest.commands[0])).toContain('Output columns: username');
45
82
  });
83
+ it('matches local site and command help byte-for-byte for equal metadata', () => {
84
+ expect(renderHostedSiteHelp(manifest, 'github')).toBe(formatSiteHelpText('github', [equivalentLocalCommand]));
85
+ expect(renderHostedCommandHelp(manifest.commands[0])).toBe(formatCommandHelpText(equivalentLocalCommand));
86
+ });
87
+ it('matches local structured list rows for equal metadata', () => {
88
+ expect(hostedListRows({ ...manifest, commands: [manifest.commands[0]] }, true))
89
+ .toEqual([serializeCommand(equivalentLocalCommand)]);
90
+ });
91
+ it('describes universal hosted surfaces and accepted local-only commands at the root', async () => {
92
+ const stdout = sink();
93
+ const result = await runHostedCli(['--help'], {
94
+ config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }),
95
+ stdout: stdout.stream,
96
+ });
97
+ expect(result).toEqual({ handled: true, exitCode: 0 });
98
+ expect(stdout.text()).toBe(formatRootHelp(HOSTED_ROOT_HELP));
99
+ expect(stdout.text()).toContain('completion');
100
+ expect(stdout.text()).toContain('--profile <name>');
101
+ expect(stdout.text()).toContain('Local-only commands:');
102
+ expect(stdout.text()).toContain('Run `webcmd setup` and choose local mode to use local-only commands.');
103
+ });
104
+ it('completes private hosted manifest commands without local discovery', async () => {
105
+ const stdout = sink();
106
+ const privateManifest = {
107
+ ...manifest,
108
+ commands: [
109
+ ...manifest.commands,
110
+ {
111
+ site: 'private-tools',
112
+ name: 'deploy-preview',
113
+ aliases: ['preview'],
114
+ command: 'private-tools/deploy-preview',
115
+ description: 'Deploy a private preview',
116
+ access: 'write',
117
+ strategy: 'PUBLIC',
118
+ browser: false,
119
+ args: [],
120
+ columns: ['url'],
121
+ },
122
+ ],
123
+ };
124
+ const fetchImpl = vi.fn(async () => new Response(JSON.stringify({ ok: true, manifest: privateManifest }), { status: 200 }));
125
+ const result = await runHostedCli(['--get-completions', '--cursor', '2', 'private-tools'], {
126
+ config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }),
127
+ stdout: stdout.stream,
128
+ fetchImpl,
129
+ });
130
+ expect(result).toEqual({ handled: true, exitCode: 0 });
131
+ expect(stdout.text().trim().split('\n')).toEqual(['deploy-preview', 'preview']);
132
+ expect(fetchImpl).toHaveBeenCalledTimes(1);
133
+ });
134
+ it.each([
135
+ ['json', 'json'],
136
+ ['yaml', 'yaml'],
137
+ ['yml', 'yaml'],
138
+ ])('matches local structured site and command help bytes for -f %s', async (requested, rendered) => {
139
+ const fetchImpl = async () => new Response(JSON.stringify({ ok: true, manifest }), { status: 200 });
140
+ const siteStdout = sink();
141
+ await runHostedCli(['github', '--help', '-f', requested], {
142
+ config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }),
143
+ stdout: siteStdout.stream,
144
+ fetchImpl,
145
+ });
146
+ expect(siteStdout.text()).toBe(renderStructuredHelp(siteHelpData('github', [equivalentLocalCommand]), rendered));
147
+ const commandStdout = sink();
148
+ await runHostedCli(['github', 'whoami', '--help', '-f', requested], {
149
+ config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }),
150
+ stdout: commandStdout.stream,
151
+ fetchImpl,
152
+ });
153
+ expect(commandStdout.text()).toBe(renderStructuredHelp(commandHelpData(equivalentLocalCommand), rendered));
154
+ });
155
+ it('uses only executable hosted root capabilities as root completion candidates', async () => {
156
+ const stdout = sink();
157
+ await runHostedCli(['--get-completions', '--cursor', '1'], {
158
+ config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }),
159
+ stdout: stdout.stream,
160
+ fetchImpl: async () => new Response(JSON.stringify({ ok: true, manifest }), { status: 200 }),
161
+ });
162
+ expect(stdout.text().trim().split('\n')).toEqual(['browser', 'completion', 'github', 'list', 'setup']);
163
+ });
46
164
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,108 @@
1
+ import { Writable } from 'node:stream';
2
+ import { Command } from 'commander';
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import { Strategy } from '../registry.js';
5
+ import { registerCommandToProgram } from '../commanderAdapter.js';
6
+ import { makeHostedConfig } from './config.js';
7
+ import { runHostedCli } from './runner.js';
8
+ const { mockExecuteCommand } = vi.hoisted(() => ({
9
+ mockExecuteCommand: vi.fn(),
10
+ }));
11
+ vi.mock('../execution.js', async () => {
12
+ const actual = await vi.importActual('../execution.js');
13
+ return { ...actual, executeCommand: mockExecuteCommand };
14
+ });
15
+ const command = {
16
+ site: 'github',
17
+ name: 'whoami',
18
+ description: 'Show GitHub identity',
19
+ access: 'read',
20
+ strategy: Strategy.PUBLIC,
21
+ browser: false,
22
+ args: [],
23
+ columns: ['username'],
24
+ };
25
+ const manifest = {
26
+ userId: 'user_demo',
27
+ metadata: {
28
+ contractSchemaVersion: 1,
29
+ webcmdPackageVersion: '0.3.0',
30
+ generatedAt: '2026-07-14T00:00:00.000Z',
31
+ },
32
+ commands: [{
33
+ site: 'github',
34
+ name: 'whoami',
35
+ command: 'github/whoami',
36
+ description: 'Show GitHub identity',
37
+ access: 'read',
38
+ strategy: 'PUBLIC',
39
+ browser: false,
40
+ args: [],
41
+ columns: ['username'],
42
+ }],
43
+ };
44
+ function sink(isTTY) {
45
+ let data = '';
46
+ const stream = new Writable({
47
+ write(chunk, _encoding, callback) {
48
+ data += String(chunk);
49
+ callback();
50
+ },
51
+ });
52
+ Object.defineProperty(stream, 'isTTY', { value: isTTY });
53
+ return { stream, text: () => data };
54
+ }
55
+ function clock() {
56
+ const values = [1_000, 1_250];
57
+ return () => values.shift() ?? 1_250;
58
+ }
59
+ async function localBytes(result, argv, isTTY, footerExtra) {
60
+ mockExecuteCommand.mockResolvedValueOnce(result);
61
+ const stdout = sink(isTTY);
62
+ const program = new Command();
63
+ const site = program.command('github');
64
+ registerCommandToProgram(site, {
65
+ ...command,
66
+ ...(footerExtra ? { footerExtra: () => footerExtra } : {}),
67
+ }, { stdout: stdout.stream, now: clock() });
68
+ await program.parseAsync(['node', 'webcmd', 'github', 'whoami', ...argv]);
69
+ return stdout.text();
70
+ }
71
+ async function hostedBytes(result, argv, isTTY, footerExtra) {
72
+ const stdout = sink(isTTY);
73
+ await runHostedCli(['github', 'whoami', ...argv], {
74
+ config: makeHostedConfig({ apiBaseUrl: 'https://api.example.com', apiKey: 'key' }),
75
+ stdout: stdout.stream,
76
+ now: clock(),
77
+ fetchImpl: async (url) => String(url).endsWith('/v1/manifest')
78
+ ? new Response(JSON.stringify({ ok: true, manifest }), { status: 200 })
79
+ : new Response(JSON.stringify({
80
+ ok: true,
81
+ result,
82
+ columns: ['username'],
83
+ ...(footerExtra ? { footerExtra } : {}),
84
+ execution: { id: 'exec_parity', command: 'github/whoami', status: 'succeeded' },
85
+ }), { status: 200 }),
86
+ });
87
+ return stdout.text();
88
+ }
89
+ beforeEach(() => {
90
+ mockExecuteCommand.mockReset();
91
+ });
92
+ describe('local/hosted command output differential', () => {
93
+ it('suppresses a null result in both modes', async () => {
94
+ const result = null;
95
+ const hosted = await hostedBytes(result, ['-f', 'json'], false);
96
+ const local = await localBytes(result, ['-f', 'json'], false);
97
+ expect(hosted).toBe(local);
98
+ expect(local).toBe('');
99
+ });
100
+ it.each([undefined, 'canonical footer'])('matches canonical local TTY table decorations byte-for-byte with footer %s', async (footerExtra) => {
101
+ const result = [{ username: 'octocat' }];
102
+ const hosted = await hostedBytes(result, ['-f', 'table'], true, footerExtra);
103
+ const local = await localBytes(result, ['-f', 'table'], true, footerExtra);
104
+ expect(hosted).toBe(local);
105
+ expect(local).toContain(' github/whoami');
106
+ expect(local).toContain(`1 items | 0.3s | github/whoami${footerExtra ? ` | ${footerExtra}` : ''}`);
107
+ });
108
+ });
@@ -0,0 +1 @@
1
+ export {};