@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,9 +1,10 @@
1
1
  import { type ConfigIo } from './config.js';
2
- export interface SetupIo extends ConfigIo {
2
+ import { type HostedCredentialIo } from './credentials.js';
3
+ export interface SetupIo extends ConfigIo, HostedCredentialIo {
3
4
  input?: NodeJS.ReadableStream;
4
5
  output?: NodeJS.WritableStream;
5
6
  fetchImpl?: typeof fetch;
6
7
  question?: (prompt: string) => Promise<string>;
7
- write?: (message: string) => void;
8
+ write?: (message: string) => void | Promise<void>;
8
9
  }
9
10
  export declare function runHostedSetup(io?: SetupIo): Promise<number>;
@@ -1,49 +1,79 @@
1
1
  import { createInterface } from 'node:readline/promises';
2
2
  import { stdin as defaultInput, stdout as defaultOutput } from 'node:process';
3
+ import { writeToStream } from '../stream-write.js';
3
4
  import { HostedClient } from './client.js';
4
- import { defaultHostedApiBaseUrl, makeHostedConfig, makeLocalConfig, saveWebcmdConfig, } from './config.js';
5
+ import { defaultHostedApiBaseUrl, makeLocalConfig, saveWebcmdConfig, } from './config.js';
6
+ import { makeStoredHostedConfig, storeHostedApiKey, } from './credentials.js';
5
7
  export async function runHostedSetup(io = {}) {
6
- const write = io.write ?? ((message) => (io.output ?? defaultOutput).write(message));
8
+ const write = io.write
9
+ ? async (message) => { await io.write(message); }
10
+ : async (message) => writeToStream(io.output ?? defaultOutput, message);
7
11
  const ownedReadline = io.question ? undefined : createInterface({
8
12
  input: io.input ?? defaultInput,
9
13
  output: io.output ?? defaultOutput,
10
14
  });
11
15
  const ask = io.question ?? ((prompt) => ownedReadline.question(prompt));
12
16
  try {
13
- write('Webcmd setup\n');
17
+ await write('Webcmd setup\n');
14
18
  const mode = await ask('Use hosted Webcmd Cloud or local Webcmd? [hosted/local] ');
15
19
  if (mode.trim().toLowerCase().startsWith('l')) {
16
20
  saveWebcmdConfig(makeLocalConfig(io.now?.() ?? new Date()), io);
17
- write('Webcmd is now configured for local mode.\n');
21
+ await write('Webcmd is now configured for local mode.\n');
18
22
  return 0;
19
23
  }
20
24
  const apiBaseUrl = defaultHostedApiBaseUrl(io.env ?? process.env);
21
25
  const apiKey = (await ask('Webcmd API key: ')).trim();
22
26
  if (!apiKey) {
23
- write('A Webcmd API key is required for hosted mode.\n');
27
+ await write('A Webcmd API key is required for hosted mode.\n');
24
28
  return 2;
25
29
  }
26
- const config = makeHostedConfig({
27
- apiBaseUrl,
28
- apiKey,
29
- now: io.now?.() ?? new Date(),
30
- });
30
+ let accountLabel;
31
31
  try {
32
- await new HostedClient({
33
- apiBaseUrl: config.hosted.apiBaseUrl,
34
- apiKey: config.hosted.apiKey,
32
+ const me = await new HostedClient({
33
+ apiBaseUrl,
34
+ apiKey,
35
35
  fetchImpl: io.fetchImpl,
36
36
  }).getMe();
37
+ accountLabel = hostedAccountLabel(me);
37
38
  }
38
39
  catch (err) {
39
40
  const message = err instanceof Error ? err.message : String(err);
40
- write(`Warning: could not verify API key yet: ${message}\n`);
41
+ await write(`Warning: could not verify API key yet: ${message}\n`);
41
42
  }
43
+ const credential = await storeHostedApiKey(apiKey, io);
44
+ const config = makeStoredHostedConfig({
45
+ apiBaseUrl,
46
+ apiKeyRef: credential.apiKeyRef,
47
+ credentialBackend: credential.credentialBackend,
48
+ now: io.now?.() ?? new Date(),
49
+ });
42
50
  saveWebcmdConfig(config, io);
43
- write('Webcmd is now configured for hosted mode.\n');
51
+ if (accountLabel)
52
+ await write(`Verified Webcmd Cloud account: ${accountLabel}\n`);
53
+ if (credential.credentialBackend === 'file-fallback') {
54
+ await write('Warning: OS credential storage was unavailable; API key stored in a protected Webcmd credentials file.\n');
55
+ }
56
+ await write(`Credential backend: ${credentialBackendLabel(credential.credentialBackend)}.\n`);
57
+ await write('Webcmd is now configured for hosted mode.\n');
44
58
  return 0;
45
59
  }
46
60
  finally {
47
61
  ownedReadline?.close();
48
62
  }
49
63
  }
64
+ function hostedAccountLabel(body) {
65
+ if (!body || typeof body !== 'object' || Array.isArray(body))
66
+ return undefined;
67
+ const user = body.user;
68
+ if (!user || typeof user !== 'object' || Array.isArray(user))
69
+ return undefined;
70
+ const record = user;
71
+ if (typeof record.email === 'string' && record.email.trim())
72
+ return record.email.trim();
73
+ if (typeof record.id === 'string' && record.id.trim())
74
+ return record.id.trim();
75
+ return undefined;
76
+ }
77
+ function credentialBackendLabel(backend) {
78
+ return backend === 'os' ? 'OS credential store' : 'protected file fallback';
79
+ }
@@ -1,10 +1,15 @@
1
+ import { spawn } from 'node:child_process';
1
2
  import { mkdtemp, readFile, rm } from 'node:fs/promises';
2
3
  import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
- import { afterEach, describe, expect, it } from 'vitest';
4
+ import path, { join } from 'node:path';
5
+ import { Writable } from 'node:stream';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { afterEach, describe, expect, it, vi } from 'vitest';
5
8
  import { getConfigPath } from './config.js';
9
+ import { getHostedCredentialPath } from './credentials.js';
6
10
  import { runHostedSetup } from './setup.js';
7
11
  let tempDir;
12
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
8
13
  afterEach(async () => {
9
14
  if (tempDir)
10
15
  await rm(tempDir, { recursive: true, force: true });
@@ -18,9 +23,10 @@ describe('webcmd setup', () => {
18
23
  const env = { WEBCMD_CONFIG_DIR: tempDir };
19
24
  const code = await runHostedSetup({
20
25
  env,
26
+ platform: 'linux',
21
27
  now: () => new Date('2026-07-08T00:00:00.000Z'),
22
28
  question: async () => answers.shift() ?? '',
23
- write: (message) => messages.push(message),
29
+ write: (message) => { messages.push(message); },
24
30
  });
25
31
  expect(code).toBe(0);
26
32
  expect(JSON.parse(await readFile(getConfigPath({ env }), 'utf8'))).toEqual({
@@ -32,11 +38,16 @@ describe('webcmd setup', () => {
32
38
  it('writes hosted mode and validates with /v1/me', async () => {
33
39
  tempDir = await mkdtemp(join(tmpdir(), 'webcmd-setup-'));
34
40
  const answers = ['hosted', 'wcmd_live_test'];
35
- const env = { WEBCMD_CONFIG_DIR: tempDir };
41
+ const env = {
42
+ WEBCMD_CONFIG_DIR: tempDir,
43
+ WEBCMD_CREDENTIAL_BACKEND: 'file',
44
+ };
36
45
  const requests = [];
37
46
  const prompts = [];
47
+ const messages = [];
38
48
  const code = await runHostedSetup({
39
49
  env,
50
+ platform: 'linux',
40
51
  now: () => new Date('2026-07-08T00:00:00.000Z'),
41
52
  question: async (prompt) => {
42
53
  prompts.push(prompt);
@@ -49,7 +60,7 @@ describe('webcmd setup', () => {
49
60
  });
50
61
  return new Response(JSON.stringify({ ok: true, user: { id: 'user_demo' } }), { status: 200 });
51
62
  },
52
- write: () => undefined,
63
+ write: (message) => { messages.push(message); },
53
64
  });
54
65
  expect(code).toBe(0);
55
66
  expect(prompts).toEqual([
@@ -61,8 +72,124 @@ describe('webcmd setup', () => {
61
72
  mode: 'hosted',
62
73
  hosted: {
63
74
  apiBaseUrl: 'https://api.webcmd.dev',
64
- apiKey: 'wcmd_live_test',
75
+ apiKeyRef: expect.stringMatching(/^wcmd_cred_/),
76
+ credentialBackend: 'file-fallback',
65
77
  },
66
78
  });
79
+ expect(await readFile(getConfigPath({ env }), 'utf8')).not.toContain('wcmd_live_test');
80
+ expect(await readFile(getHostedCredentialPath({ env }), 'utf8')).toContain('wcmd_live_test');
81
+ expect(messages.join('')).toContain('Verified Webcmd Cloud account: user_demo');
82
+ expect(messages.join('')).toContain('Credential backend: protected file fallback.');
83
+ });
84
+ it('persists interactive local setup before the real CLI process completes', async () => {
85
+ tempDir = await mkdtemp(join(tmpdir(), 'webcmd-setup-process-'));
86
+ const child = spawn(process.execPath, ['--import', 'tsx', 'src/main.ts', 'setup'], {
87
+ cwd: packageRoot,
88
+ env: { ...process.env, WEBCMD_CONFIG_DIR: tempDir, WEBCMD_NO_UPDATE_CHECK: '1' },
89
+ stdio: ['pipe', 'pipe', 'pipe'],
90
+ });
91
+ const stdout = [];
92
+ const stderr = [];
93
+ child.stdout.on('data', chunk => stdout.push(Buffer.from(chunk)));
94
+ child.stderr.on('data', chunk => stderr.push(Buffer.from(chunk)));
95
+ child.stdin.end('local\n');
96
+ const status = await new Promise((resolve, reject) => {
97
+ child.once('error', reject);
98
+ child.once('close', resolve);
99
+ });
100
+ expect(status).toBe(0);
101
+ expect(Buffer.concat(stderr).toString('utf8')).toBe('');
102
+ expect(Buffer.concat(stdout).toString('utf8')).toContain('Webcmd is now configured for local mode.');
103
+ expect(JSON.parse(await readFile(getConfigPath({ env: { WEBCMD_CONFIG_DIR: tempDir } }), 'utf8')))
104
+ .toMatchObject({ mode: 'local' });
105
+ }, 20_000);
106
+ it('does not resolve until all caller-owned output writes complete', async () => {
107
+ tempDir = await mkdtemp(join(tmpdir(), 'webcmd-setup-slow-output-'));
108
+ const output = new SetupControlledWritable();
109
+ let settled = false;
110
+ const run = runHostedSetup({
111
+ env: { WEBCMD_CONFIG_DIR: tempDir },
112
+ output,
113
+ question: async () => 'local',
114
+ }).then(code => {
115
+ settled = true;
116
+ return code;
117
+ });
118
+ await Promise.resolve();
119
+ expect(settled).toBe(false);
120
+ expect(output.pendingCount()).toBe(1);
121
+ output.release();
122
+ await new Promise(resolve => setImmediate(resolve));
123
+ expect(settled).toBe(false);
124
+ expect(output.pendingCount()).toBe(1);
125
+ output.release();
126
+ await expect(run).resolves.toBe(0);
127
+ expect(output.text()).toBe('Webcmd setup\nWebcmd is now configured for local mode.\n');
128
+ });
129
+ it('rejects caller-owned stream errors without ending the stream', async () => {
130
+ tempDir = await mkdtemp(join(tmpdir(), 'webcmd-setup-output-error-'));
131
+ const output = new Writable({
132
+ write(_chunk, _encoding, callback) {
133
+ callback(new Error('setup output failed'));
134
+ },
135
+ });
136
+ const end = vi.spyOn(output, 'end');
137
+ await expect(runHostedSetup({
138
+ env: { WEBCMD_CONFIG_DIR: tempDir },
139
+ output,
140
+ question: async () => 'local',
141
+ })).rejects.toThrow('setup output failed');
142
+ expect(end).not.toHaveBeenCalled();
143
+ });
144
+ it('rejects within a bound when caller-owned output closes before its callback', async () => {
145
+ tempDir = await mkdtemp(join(tmpdir(), 'webcmd-setup-output-close-'));
146
+ const output = new SetupCloseBeforeCallbackWritable();
147
+ const end = vi.spyOn(output, 'end');
148
+ await expect(within(runHostedSetup({
149
+ env: { WEBCMD_CONFIG_DIR: tempDir },
150
+ output,
151
+ question: async () => 'local',
152
+ }))).rejects.toThrow('closed before the write completed');
153
+ expect(end).not.toHaveBeenCalled();
67
154
  });
68
155
  });
156
+ async function within(promise, milliseconds = 500) {
157
+ let timer;
158
+ try {
159
+ return await Promise.race([
160
+ promise,
161
+ new Promise((_resolve, reject) => {
162
+ timer = setTimeout(() => reject(new Error(`promise did not settle within ${milliseconds}ms`)), milliseconds);
163
+ }),
164
+ ]);
165
+ }
166
+ finally {
167
+ if (timer)
168
+ clearTimeout(timer);
169
+ }
170
+ }
171
+ class SetupCloseBeforeCallbackWritable extends Writable {
172
+ _write(_chunk, _encoding, _callback) {
173
+ this.destroy();
174
+ }
175
+ }
176
+ class SetupControlledWritable extends Writable {
177
+ chunks = [];
178
+ releases = [];
179
+ _write(chunk, _encoding, callback) {
180
+ this.chunks.push(Buffer.from(chunk));
181
+ this.releases.push(callback);
182
+ }
183
+ pendingCount() {
184
+ return this.releases.length;
185
+ }
186
+ release() {
187
+ const callback = this.releases.shift();
188
+ if (!callback)
189
+ throw new Error('No setup write is pending');
190
+ callback();
191
+ }
192
+ text() {
193
+ return Buffer.concat(this.chunks).toString('utf8');
194
+ }
195
+ }
@@ -1,15 +1,21 @@
1
+ import type { CommandSurfaceMetadata } from '../command-surface.js';
2
+ import type { Arg } from '../registry.js';
1
3
  export type HostedCommandStrategy = 'PUBLIC' | 'COOKIE' | 'INTERCEPT' | 'UI' | 'LOCAL' | string;
2
- export interface HostedCommandArg {
4
+ export interface HostedCommandArg extends Arg {
5
+ }
6
+ export interface HostedFileArgument {
3
7
  name: string;
4
- type?: string;
5
- required?: boolean;
6
- valueRequired?: boolean;
7
- positional?: boolean;
8
- default?: unknown;
9
- help?: string;
10
- choices?: unknown[];
11
- }
12
- export interface HostedCommand {
8
+ direction: 'input' | 'output';
9
+ pathKind: 'file' | 'directory';
10
+ multiple: boolean;
11
+ required: boolean;
12
+ separator?: ',';
13
+ contentTypes?: string[];
14
+ contentType?: string;
15
+ maxBytes?: number;
16
+ defaultPath?: string;
17
+ }
18
+ export interface HostedCommand extends CommandSurfaceMetadata {
13
19
  site: string;
14
20
  name: string;
15
21
  aliases?: string[];
@@ -19,24 +25,87 @@ export interface HostedCommand {
19
25
  strategy: HostedCommandStrategy;
20
26
  browser: boolean;
21
27
  args: HostedCommandArg[];
22
- columns?: string[];
28
+ columns: string[];
23
29
  domain?: string | null;
24
30
  defaultFormat?: string | null;
25
31
  }
26
32
  export interface HostedManifest {
27
33
  userId: string;
28
- generatedAt: string;
34
+ metadata: {
35
+ contractSchemaVersion: number;
36
+ webcmdPackageVersion: string;
37
+ generatedAt: string;
38
+ };
29
39
  commands: HostedCommand[];
30
40
  }
41
+ export interface HostedPublicProfile {
42
+ name: string;
43
+ default: boolean;
44
+ status: 'available';
45
+ createdAt: string;
46
+ lastUsedAt: string;
47
+ }
48
+ export interface HostedProfilesResponse {
49
+ ok: true;
50
+ profiles: HostedPublicProfile[];
51
+ }
52
+ export interface HostedExecution {
53
+ id: string;
54
+ command: string;
55
+ status: 'succeeded' | 'failed' | 'timed_out';
56
+ }
57
+ export interface HostedTraceReceipt {
58
+ receipt: string;
59
+ executionId: string;
60
+ artifactsUrl?: string;
61
+ liveViewUrl?: string;
62
+ replayUrl?: string;
63
+ }
31
64
  export interface HostedExecuteResponse {
32
65
  ok: true;
33
- result?: unknown;
34
- data?: unknown;
35
- rows?: unknown;
66
+ result: unknown;
36
67
  columns?: string[];
37
- trace?: unknown;
68
+ footerExtra?: string;
69
+ execution: HostedExecution;
70
+ trace?: HostedTraceReceipt;
71
+ artifacts?: HostedArtifactReceipt[];
38
72
  }
39
- export type HostedBrowserActionName = 'back' | 'click' | 'close-window' | 'console' | 'exec' | 'fill' | 'frames' | 'insert-text' | 'navigate' | 'network' | 'press-key' | 'screenshot' | 'scroll' | 'set-file-input' | 'snapshot' | 'tabs' | 'type' | 'wait';
73
+ export interface HostedPreparedExecution {
74
+ id: string;
75
+ command: string;
76
+ status: 'queued';
77
+ }
78
+ export interface HostedPrepareExecutionResponse {
79
+ ok: true;
80
+ execution: HostedPreparedExecution;
81
+ fileArguments: HostedFileArgument[];
82
+ }
83
+ export interface HostedArtifactReceipt {
84
+ artifactId: string;
85
+ argument: string;
86
+ direction: 'input' | 'output';
87
+ pathKind: 'file' | 'directory';
88
+ filename: string;
89
+ contentType: string;
90
+ byteSize: number;
91
+ sha256?: string;
92
+ relativePath?: string;
93
+ expiresAt: string;
94
+ }
95
+ export interface HostedArtifactReference {
96
+ $webcmdArtifact: {
97
+ id?: string;
98
+ direction?: 'input' | 'output';
99
+ filename?: string;
100
+ contentType?: string;
101
+ };
102
+ }
103
+ export interface HostedUploadArtifactResponse {
104
+ ok: true;
105
+ artifact: HostedArtifactReceipt;
106
+ reference: HostedArtifactReference;
107
+ }
108
+ export type HostedBrowserActionName = 'analyze' | 'back' | 'check' | 'click' | 'close-window' | 'console' | 'dblclick' | 'dialog-accept' | 'dialog-dismiss' | 'drag' | 'exec' | 'extract' | 'fill' | 'find' | 'focus' | 'frames' | 'get-attributes' | 'get-html' | 'get-text' | 'get-title' | 'get-url' | 'get-value' | 'hover' | 'init' | 'insert-text' | 'navigate' | 'network' | 'press-key' | 'screenshot' | 'scroll' | 'select' | 'set-file-input' | 'snapshot' | 'tabs' | 'type' | 'uncheck' | 'verify' | 'wait';
40
109
  export interface HostedBrowserRunRequest {
41
110
  command: string;
42
111
  args: Record<string, unknown>;
@@ -63,9 +132,17 @@ export interface HostedBrowserActionRequest {
63
132
  }
64
133
  export interface HostedBrowserActionResponse {
65
134
  ok: true;
66
- result?: unknown;
67
- columns?: string[];
68
- trace?: unknown;
135
+ result: unknown;
136
+ columns: string[];
137
+ trace: HostedBrowserActionTrace | null;
138
+ }
139
+ export interface HostedBrowserActionTrace {
140
+ id: string;
141
+ receipt: string;
142
+ kind: string;
143
+ contentType?: string;
144
+ byteSize?: number;
145
+ storagePath?: string;
69
146
  }
70
147
  export interface HostedBrowserFinishRequest {
71
148
  status: 'succeeded' | 'failed' | 'timed_out';
@@ -88,10 +165,11 @@ export interface HostedBrowserRunActionResponse extends HostedBrowserActionRespo
88
165
  export interface HostedErrorResponse {
89
166
  ok: false;
90
167
  error: {
91
- code?: string;
92
- message?: string;
168
+ code: string;
169
+ message: string;
93
170
  help?: string;
94
- hint?: string;
95
171
  exitCode?: number;
96
172
  };
173
+ execution?: HostedExecution;
174
+ trace?: HostedTraceReceipt;
97
175
  }