@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,7 +1,13 @@
1
- import { CliError, EXIT_CODES } from '../errors.js';
1
+ import { attachTraceReceipt, CliError, EXIT_CODES } from '../errors.js';
2
2
  export class HostedClientError extends CliError {
3
- constructor(code, message, help, exitCode = EXIT_CODES.GENERIC_ERROR) {
3
+ execution;
4
+ trace;
5
+ constructor(code, message, help, exitCode = EXIT_CODES.GENERIC_ERROR, metadata = {}) {
4
6
  super(code, message, help, exitCode);
7
+ this.execution = metadata.execution;
8
+ this.trace = metadata.trace;
9
+ if (metadata.trace)
10
+ attachTraceReceipt(this, metadata.trace);
5
11
  }
6
12
  }
7
13
  export class HostedClient {
@@ -18,70 +24,135 @@ export class HostedClient {
18
24
  }
19
25
  async getManifest() {
20
26
  const body = await this.request('/v1/manifest');
21
- if (!body.manifest || !Array.isArray(body.manifest.commands)) {
22
- throw new HostedClientError('HOSTED_PROTOCOL', 'Webcmd Cloud returned an invalid manifest.');
27
+ if (!hasExactKeys(body, ['ok', 'manifest']) || !isHostedManifest(body.manifest)) {
28
+ throw protocolError('Webcmd Cloud returned an invalid manifest.');
23
29
  }
24
30
  return body.manifest;
25
31
  }
32
+ async listProfiles() {
33
+ const body = await this.request('/v1/profiles');
34
+ if (!isHostedProfilesResponse(body)) {
35
+ throw protocolError('Webcmd Cloud returned an invalid profiles response.');
36
+ }
37
+ return body;
38
+ }
26
39
  async execute(input) {
27
- return this.request('/v1/execute', {
40
+ const traceMode = normalizeTraceMode(input.trace);
41
+ const body = await this.request('/v1/execute', {
28
42
  method: 'POST',
29
43
  body: JSON.stringify(input),
44
+ }, { command: input.command, traceMode });
45
+ if (!isHostedExecuteResponse(body, input.command, traceMode)) {
46
+ throw protocolError('Webcmd Cloud returned an invalid execution response.');
47
+ }
48
+ return body;
49
+ }
50
+ async prepareExecution(input) {
51
+ const body = await this.request('/v1/executions', {
52
+ method: 'POST',
53
+ body: JSON.stringify(input),
54
+ });
55
+ if (!isHostedPrepareExecutionResponse(body, input.command)) {
56
+ throw protocolError('Webcmd Cloud returned an invalid prepared execution response.');
57
+ }
58
+ return body;
59
+ }
60
+ async uploadExecutionArtifact(input) {
61
+ const body = await this.request(`/v1/executions/${encodeURIComponent(input.executionId)}/artifacts/${encodeURIComponent(input.argument)}`, {
62
+ method: 'POST',
63
+ headers: {
64
+ 'content-type': 'application/octet-stream',
65
+ 'x-webcmd-filename': input.filename,
66
+ 'x-webcmd-content-type': input.contentType,
67
+ },
68
+ body: input.body,
69
+ });
70
+ if (!isHostedUploadArtifactResponse(body, input.argument)) {
71
+ throw protocolError('Webcmd Cloud returned an invalid artifact upload response.');
72
+ }
73
+ return body;
74
+ }
75
+ async runPreparedExecution(input) {
76
+ const traceMode = normalizeTraceMode(input.trace);
77
+ const body = await this.request(`/v1/executions/${encodeURIComponent(input.executionId)}/run`, {
78
+ method: 'POST',
79
+ body: JSON.stringify({
80
+ command: input.command,
81
+ args: input.args,
82
+ ...(input.format !== undefined ? { format: input.format } : {}),
83
+ ...(input.trace !== undefined ? { trace: input.trace } : {}),
84
+ ...(input.profile !== undefined ? { profile: input.profile } : {}),
85
+ }),
86
+ }, { command: input.command, traceMode });
87
+ if (!isHostedExecuteResponse(body, input.command, traceMode)) {
88
+ throw protocolError('Webcmd Cloud returned an invalid execution response.');
89
+ }
90
+ return body;
91
+ }
92
+ async downloadExecutionArtifact(input) {
93
+ const response = await this.fetchImpl(`${this.apiBaseUrl}/v1/executions/${encodeURIComponent(input.executionId)}/artifacts/${encodeURIComponent(input.artifactId)}`, {
94
+ headers: {
95
+ accept: 'application/octet-stream',
96
+ authorization: `Bearer ${this.apiKey}`,
97
+ },
30
98
  });
99
+ if (!response.ok) {
100
+ const text = await response.text();
101
+ const body = text ? parseJson(text) : {};
102
+ if (!isHostedError(body))
103
+ throw protocolError('Webcmd Cloud returned an invalid artifact download failure.');
104
+ const error = body.error;
105
+ throw new HostedClientError(error.code, error.message, error.help, normalizeExitCode(error.exitCode, response.status === 401 ? EXIT_CODES.NOPERM : EXIT_CODES.GENERIC_ERROR), {
106
+ ...(body.execution ? { execution: body.execution } : {}),
107
+ ...(body.trace ? { trace: body.trace } : {}),
108
+ });
109
+ }
110
+ return new Uint8Array(await response.arrayBuffer());
31
111
  }
32
112
  async startBrowserRun(session, input) {
33
- return this.request(`/v1/browser/${encodeURIComponent(session)}/runs`, {
113
+ const body = await this.request(`/v1/browser/${encodeURIComponent(session)}/runs`, {
34
114
  method: 'POST',
35
115
  body: JSON.stringify(input),
36
116
  });
117
+ if (!isHostedBrowserRunResponse(body, session)) {
118
+ throw protocolError('Webcmd Cloud returned an invalid browser run response.');
119
+ }
120
+ return body;
37
121
  }
38
122
  async browserAction(session, executionId, input) {
39
- return this.request(`/v1/browser/${encodeURIComponent(session)}/runs/${encodeURIComponent(executionId)}/actions`, {
123
+ const body = await this.request(`/v1/browser/${encodeURIComponent(session)}/runs/${encodeURIComponent(executionId)}/actions`, {
40
124
  method: 'POST',
41
125
  body: JSON.stringify(input),
42
126
  });
127
+ if (!isHostedBrowserActionResponse(body)) {
128
+ throw protocolError('Webcmd Cloud returned an invalid browser action response.');
129
+ }
130
+ return body;
43
131
  }
44
132
  async finishBrowserRun(session, executionId, input) {
45
- return this.request(`/v1/browser/${encodeURIComponent(session)}/runs/${encodeURIComponent(executionId)}/finish`, {
133
+ const body = await this.request(`/v1/browser/${encodeURIComponent(session)}/runs/${encodeURIComponent(executionId)}/finish`, {
46
134
  method: 'POST',
47
135
  body: JSON.stringify(input),
48
136
  });
137
+ if (!isHostedBrowserFinishResponse(body, executionId, input.status)) {
138
+ throw protocolError('Webcmd Cloud returned an invalid browser finish response.');
139
+ }
140
+ return body;
49
141
  }
50
142
  async runBrowserAction(session, input) {
51
- const { command, trace, windowMode, action, profile, args } = input;
52
- const run = await this.startBrowserRun(session, {
53
- command,
54
- args,
55
- ...(profile !== undefined ? { profile } : {}),
56
- ...(windowMode !== undefined ? { windowMode } : {}),
57
- ...(trace !== undefined ? { trace } : {}),
143
+ return this.executeBrowserCommand(session, input);
144
+ }
145
+ async executeBrowserCommand(session, input) {
146
+ const body = await this.request(`/v1/browser/${encodeURIComponent(session)}/commands`, {
147
+ method: 'POST',
148
+ body: JSON.stringify(input),
58
149
  });
59
- try {
60
- const actionResponse = await this.browserAction(session, run.run.executionId, {
61
- action,
62
- args,
63
- ...(profile !== undefined ? { profile } : {}),
64
- });
65
- const finished = await this.finishBrowserRun(session, run.run.executionId, {
66
- status: 'succeeded',
67
- ...(profile !== undefined ? { profile } : {}),
68
- });
69
- return {
70
- ...actionResponse,
71
- run: run.run,
72
- execution: finished.execution,
73
- };
74
- }
75
- catch (error) {
76
- await this.finishBrowserRun(session, run.run.executionId, {
77
- status: 'failed',
78
- errorCode: error instanceof HostedClientError ? error.code : 'HOSTED_BROWSER_ACTION_FAILED',
79
- ...(profile !== undefined ? { profile } : {}),
80
- }).catch(() => undefined);
81
- throw error;
150
+ if (!isHostedBrowserRunActionResponse(body, session)) {
151
+ throw protocolError('Webcmd Cloud returned an invalid browser action response.');
82
152
  }
153
+ return body;
83
154
  }
84
- async request(path, init = {}) {
155
+ async request(path, init = {}, executionExpectation) {
85
156
  const response = await this.fetchImpl(`${this.apiBaseUrl}${path}`, {
86
157
  ...init,
87
158
  headers: {
@@ -93,30 +164,347 @@ export class HostedClient {
93
164
  });
94
165
  const text = await response.text();
95
166
  const body = text ? parseJson(text) : {};
96
- if (!response.ok || isHostedError(body)) {
97
- const error = isHostedError(body)
98
- ? body.error
99
- : { code: `HTTP_${response.status}`, message: `Webcmd Cloud request failed with HTTP ${response.status}.` };
100
- throw new HostedClientError(error.code || `HTTP_${response.status}`, error.message || `Webcmd Cloud request failed with HTTP ${response.status}.`, error.help ?? error.hint, normalizeExitCode(error.exitCode, response.status === 401 ? EXIT_CODES.NOPERM : EXIT_CODES.GENERIC_ERROR));
167
+ if (!isRecord(body) || (body.ok !== true && body.ok !== false)) {
168
+ throw protocolError('Webcmd Cloud returned an invalid response envelope.');
169
+ }
170
+ if (body.ok === false) {
171
+ if (!isHostedError(body))
172
+ throw protocolError('Webcmd Cloud returned an invalid failure response.');
173
+ if (body.execution && !isValidExecutedFailure(body, executionExpectation)) {
174
+ throw protocolError('Webcmd Cloud returned an invalid executed failure response.');
175
+ }
176
+ const error = body.error;
177
+ throw new HostedClientError(error.code, error.message, error.help, normalizeExitCode(error.exitCode, response.status === 401 ? EXIT_CODES.NOPERM : EXIT_CODES.GENERIC_ERROR), {
178
+ ...(body.execution ? { execution: body.execution } : {}),
179
+ ...(body.trace ? { trace: body.trace } : {}),
180
+ });
101
181
  }
182
+ if (!response.ok)
183
+ throw protocolError('Webcmd Cloud returned a success envelope with an HTTP error status.');
102
184
  return body;
103
185
  }
104
186
  }
105
- function normalizeExitCode(value, fallback) {
106
- const allowed = new Set(Object.values(EXIT_CODES));
107
- return value !== undefined && allowed.has(value) ? value : fallback;
108
- }
109
187
  function parseJson(text) {
110
188
  try {
111
189
  return JSON.parse(text);
112
190
  }
113
191
  catch {
114
- throw new HostedClientError('HOSTED_PROTOCOL', 'Webcmd Cloud returned non-JSON response.');
192
+ throw protocolError('Webcmd Cloud returned non-JSON response.');
115
193
  }
116
194
  }
117
195
  function isHostedError(value) {
118
- return !!value
119
- && typeof value === 'object'
120
- && value.ok === false
121
- && typeof value.error === 'object';
196
+ if (!hasOnlyKeys(value, ['ok', 'error', 'execution', 'trace']) || value.ok !== false || !isRecord(value.error))
197
+ return false;
198
+ if (!hasOnlyKeys(value.error, ['code', 'message', 'help', 'exitCode']))
199
+ return false;
200
+ if (typeof value.error.code !== 'string' || typeof value.error.message !== 'string')
201
+ return false;
202
+ if (value.error.exitCode !== undefined
203
+ && (typeof value.error.exitCode !== 'number' || !isAllowedExitCode(value.error.exitCode)))
204
+ return false;
205
+ if (value.error.help !== undefined && typeof value.error.help !== 'string')
206
+ return false;
207
+ if (value.execution !== undefined && !isHostedExecution(value.execution))
208
+ return false;
209
+ if (value.trace !== undefined && !isHostedTraceReceipt(value.trace))
210
+ return false;
211
+ if (value.execution?.status === 'succeeded')
212
+ return false;
213
+ if (value.trace && (!value.execution || value.trace.executionId !== value.execution.id))
214
+ return false;
215
+ return true;
216
+ }
217
+ function isHostedManifest(value) {
218
+ return hasExactKeys(value, ['userId', 'metadata', 'commands'])
219
+ && typeof value.userId === 'string'
220
+ && hasExactKeys(value.metadata, ['contractSchemaVersion', 'webcmdPackageVersion', 'generatedAt'])
221
+ && typeof value.metadata.contractSchemaVersion === 'number'
222
+ && Number.isInteger(value.metadata.contractSchemaVersion)
223
+ && value.metadata.contractSchemaVersion > 0
224
+ && typeof value.metadata.webcmdPackageVersion === 'string'
225
+ && typeof value.metadata.generatedAt === 'string'
226
+ && Array.isArray(value.commands)
227
+ && value.commands.every(isHostedManifestCommand);
228
+ }
229
+ function isHostedExecuteResponse(value, requestedCommand, traceMode) {
230
+ if (!hasOnlyKeys(value, ['ok', 'result', 'columns', 'footerExtra', 'execution', 'trace', 'artifacts'])
231
+ || value.ok !== true
232
+ || !Object.prototype.hasOwnProperty.call(value, 'result'))
233
+ return false;
234
+ if (!isHostedExecution(value.execution) || value.execution.status !== 'succeeded')
235
+ return false;
236
+ if (value.execution.command !== requestedCommand)
237
+ return false;
238
+ if (value.columns !== undefined && (!Array.isArray(value.columns) || !value.columns.every(column => typeof column === 'string'))) {
239
+ return false;
240
+ }
241
+ if (value.footerExtra !== undefined && typeof value.footerExtra !== 'string')
242
+ return false;
243
+ if (value.artifacts !== undefined && (!Array.isArray(value.artifacts) || !value.artifacts.every(isHostedArtifactReceipt)))
244
+ return false;
245
+ if (value.trace !== undefined && !isHostedTraceReceipt(value.trace))
246
+ return false;
247
+ if (value.trace && value.trace.executionId !== value.execution.id)
248
+ return false;
249
+ if (traceMode === 'on' ? !value.trace : value.trace !== undefined)
250
+ return false;
251
+ return true;
252
+ }
253
+ function isHostedPrepareExecutionResponse(value, requestedCommand) {
254
+ return hasExactKeys(value, ['ok', 'execution', 'fileArguments'])
255
+ && value.ok === true
256
+ && hasExactKeys(value.execution, ['id', 'command', 'status'])
257
+ && typeof value.execution.id === 'string'
258
+ && value.execution.command === requestedCommand
259
+ && value.execution.status === 'queued'
260
+ && Array.isArray(value.fileArguments)
261
+ && value.fileArguments.every(isHostedFileArgument);
262
+ }
263
+ function isHostedUploadArtifactResponse(value, argument) {
264
+ if (!hasExactKeys(value, ['ok', 'artifact', 'reference']) || value.ok !== true)
265
+ return false;
266
+ const artifact = value.artifact;
267
+ if (!isHostedArtifactReceipt(artifact))
268
+ return false;
269
+ if (artifact.argument !== argument)
270
+ return false;
271
+ if (!hasExactKeys(value.reference, ['$webcmdArtifact']))
272
+ return false;
273
+ const reference = value.reference.$webcmdArtifact;
274
+ return hasOnlyKeys(reference, ['id', 'direction', 'filename', 'contentType'])
275
+ && typeof reference.id === 'string'
276
+ && (reference.direction === undefined || reference.direction === 'input');
277
+ }
278
+ function isHostedProfilesResponse(value) {
279
+ return hasExactKeys(value, ['ok', 'profiles'])
280
+ && value.ok === true
281
+ && Array.isArray(value.profiles)
282
+ && value.profiles.every(isHostedPublicProfile);
283
+ }
284
+ function isHostedPublicProfile(value) {
285
+ return hasExactKeys(value, ['name', 'default', 'status', 'createdAt', 'lastUsedAt'])
286
+ && typeof value.name === 'string'
287
+ && typeof value.default === 'boolean'
288
+ && value.status === 'available'
289
+ && typeof value.createdAt === 'string'
290
+ && typeof value.lastUsedAt === 'string';
291
+ }
292
+ function isHostedManifestCommand(value) {
293
+ if (!hasOnlyKeys(value, [
294
+ 'site', 'name', 'aliases', 'command', 'description', 'access', 'example', 'domain', 'strategy', 'browser',
295
+ 'args', 'columns', 'pipeline', 'defaultFormat', 'type', 'modulePath', 'sourceFile', 'navigateBefore',
296
+ 'siteSession', 'defaultWindowMode', 'adapterPackageId', 'adapterPackageName', 'adapterPackageVersion',
297
+ ]))
298
+ return false;
299
+ if (typeof value.site !== 'string' || typeof value.name !== 'string' || typeof value.command !== 'string')
300
+ return false;
301
+ if (typeof value.description !== 'string' || typeof value.access !== 'string' || typeof value.strategy !== 'string')
302
+ return false;
303
+ if (typeof value.browser !== 'boolean' || !Array.isArray(value.args) || !value.args.every(isHostedManifestArg))
304
+ return false;
305
+ if (value.aliases !== undefined && (!Array.isArray(value.aliases) || !value.aliases.every(item => typeof item === 'string')))
306
+ return false;
307
+ if (!Array.isArray(value.columns) || !value.columns.every(item => typeof item === 'string'))
308
+ return false;
309
+ if (value.domain !== undefined && value.domain !== null && typeof value.domain !== 'string')
310
+ return false;
311
+ if (value.defaultFormat !== undefined && value.defaultFormat !== null && typeof value.defaultFormat !== 'string')
312
+ return false;
313
+ if (value.example !== undefined && typeof value.example !== 'string')
314
+ return false;
315
+ if (value.pipeline !== undefined && (!Array.isArray(value.pipeline) || !value.pipeline.every(isRecord)))
316
+ return false;
317
+ for (const key of ['type', 'modulePath', 'sourceFile', 'siteSession', 'defaultWindowMode', 'adapterPackageId', 'adapterPackageName', 'adapterPackageVersion']) {
318
+ if (value[key] !== undefined && typeof value[key] !== 'string')
319
+ return false;
320
+ }
321
+ return value.navigateBefore === undefined || typeof value.navigateBefore === 'boolean' || typeof value.navigateBefore === 'string';
322
+ }
323
+ function isHostedManifestArg(value) {
324
+ if (!hasOnlyKeys(value, ['name', 'type', 'required', 'default', 'valueRequired', 'positional', 'help', 'choices', 'file']))
325
+ return false;
326
+ if (typeof value.name !== 'string')
327
+ return false;
328
+ if (value.type !== undefined && typeof value.type !== 'string')
329
+ return false;
330
+ for (const key of ['required', 'valueRequired', 'positional']) {
331
+ if (value[key] !== undefined && typeof value[key] !== 'boolean')
332
+ return false;
333
+ }
334
+ if (value.help !== undefined && typeof value.help !== 'string')
335
+ return false;
336
+ if (value.file !== undefined && !isHostedArgFileMetadata(value.file))
337
+ return false;
338
+ return value.choices === undefined
339
+ || (Array.isArray(value.choices) && value.choices.every(choice => typeof choice === 'string'));
340
+ }
341
+ function isHostedArgFileMetadata(value) {
342
+ return hasOnlyKeys(value, ['direction', 'pathKind', 'multiple', 'separator', 'contentTypes', 'contentType', 'maxBytes', 'defaultPath'])
343
+ && (value.direction === 'input' || value.direction === 'output')
344
+ && (value.pathKind === 'file' || value.pathKind === 'directory')
345
+ && typeof value.multiple === 'boolean'
346
+ && (value.separator === undefined || value.separator === ',')
347
+ && (value.contentTypes === undefined || (Array.isArray(value.contentTypes) && value.contentTypes.every(item => typeof item === 'string')))
348
+ && (value.contentType === undefined || typeof value.contentType === 'string')
349
+ && (value.maxBytes === undefined || (typeof value.maxBytes === 'number' && Number.isFinite(value.maxBytes) && value.maxBytes > 0))
350
+ && (value.defaultPath === undefined || typeof value.defaultPath === 'string');
351
+ }
352
+ function isHostedFileArgument(value) {
353
+ return hasOnlyKeys(value, ['name', 'direction', 'pathKind', 'multiple', 'required', 'separator', 'contentTypes', 'contentType', 'maxBytes', 'defaultPath'])
354
+ && typeof value.name === 'string'
355
+ && (value.direction === 'input' || value.direction === 'output')
356
+ && (value.pathKind === 'file' || value.pathKind === 'directory')
357
+ && typeof value.multiple === 'boolean'
358
+ && typeof value.required === 'boolean'
359
+ && (value.separator === undefined || value.separator === ',')
360
+ && (value.contentTypes === undefined || (Array.isArray(value.contentTypes) && value.contentTypes.every(item => typeof item === 'string')))
361
+ && (value.contentType === undefined || typeof value.contentType === 'string')
362
+ && (value.maxBytes === undefined || (typeof value.maxBytes === 'number' && Number.isFinite(value.maxBytes) && value.maxBytes > 0))
363
+ && (value.defaultPath === undefined || typeof value.defaultPath === 'string');
364
+ }
365
+ function isHostedArtifactReceipt(value) {
366
+ return hasOnlyKeys(value, [
367
+ 'artifactId', 'argument', 'direction', 'pathKind', 'filename', 'contentType',
368
+ 'byteSize', 'sha256', 'relativePath', 'expiresAt',
369
+ ])
370
+ && typeof value.artifactId === 'string'
371
+ && typeof value.argument === 'string'
372
+ && (value.direction === 'input' || value.direction === 'output')
373
+ && (value.pathKind === 'file' || value.pathKind === 'directory')
374
+ && typeof value.filename === 'string'
375
+ && typeof value.contentType === 'string'
376
+ && typeof value.byteSize === 'number'
377
+ && Number.isInteger(value.byteSize)
378
+ && value.byteSize >= 0
379
+ && (value.sha256 === undefined || typeof value.sha256 === 'string')
380
+ && (value.relativePath === undefined || isSafeRelativeArtifactPath(value.relativePath))
381
+ && typeof value.expiresAt === 'string';
382
+ }
383
+ function isSafeRelativeArtifactPath(value) {
384
+ return typeof value === 'string'
385
+ && value.length > 0
386
+ && !value.startsWith('/')
387
+ && !value.includes('\\')
388
+ && !value.split('/').some(segment => !segment || segment === '.' || segment === '..' || segment.includes('\0'));
389
+ }
390
+ function isHostedBrowserRunResponse(value, requestedSession) {
391
+ return hasExactKeys(value, ['ok', 'run']) && value.ok === true && isHostedBrowserRunPayload(value.run, requestedSession);
392
+ }
393
+ function isHostedBrowserRunPayload(value, requestedSession) {
394
+ const run = value;
395
+ if (!hasOnlyKeys(run, ['executionId', 'session', 'profile', 'liveViewUrl']))
396
+ return false;
397
+ if (typeof run.executionId !== 'string' || run.session !== requestedSession)
398
+ return false;
399
+ if (!hasExactKeys(run.profile, ['id', 'displayName']))
400
+ return false;
401
+ if (typeof run.profile.id !== 'string' || typeof run.profile.displayName !== 'string')
402
+ return false;
403
+ return run.liveViewUrl === undefined || typeof run.liveViewUrl === 'string';
404
+ }
405
+ function isHostedBrowserActionResponse(value) {
406
+ if (!hasExactKeys(value, ['ok', 'result', 'columns', 'trace']) || value.ok !== true)
407
+ return false;
408
+ if (!Array.isArray(value.columns) || !value.columns.every(column => typeof column === 'string'))
409
+ return false;
410
+ return value.trace === null || isHostedBrowserActionTrace(value.trace);
411
+ }
412
+ function isHostedBrowserRunActionResponse(value, requestedSession) {
413
+ if (!hasExactKeys(value, ['ok', 'result', 'columns', 'trace', 'run', 'execution']) || value.ok !== true)
414
+ return false;
415
+ if (!Array.isArray(value.columns) || !value.columns.every(column => typeof column === 'string'))
416
+ return false;
417
+ if (value.trace !== null && !isHostedBrowserActionTrace(value.trace))
418
+ return false;
419
+ if (!isHostedBrowserRunPayload(value.run, requestedSession))
420
+ return false;
421
+ return hasExactKeys(value.execution, ['id', 'status'])
422
+ && typeof value.execution.id === 'string'
423
+ && value.execution.id === value.run.executionId
424
+ && (value.execution.status === 'succeeded' || value.execution.status === 'failed' || value.execution.status === 'timed_out');
425
+ }
426
+ function isHostedBrowserActionTrace(value) {
427
+ if (!hasOnlyKeys(value, ['id', 'receipt', 'kind', 'contentType', 'byteSize', 'storagePath']))
428
+ return false;
429
+ if (typeof value.id !== 'string' || typeof value.receipt !== 'string' || typeof value.kind !== 'string')
430
+ return false;
431
+ if (value.contentType !== undefined && typeof value.contentType !== 'string')
432
+ return false;
433
+ if (value.byteSize !== undefined
434
+ && (typeof value.byteSize !== 'number' || !Number.isInteger(value.byteSize) || value.byteSize < 0))
435
+ return false;
436
+ return value.storagePath === undefined || typeof value.storagePath === 'string';
437
+ }
438
+ function isHostedBrowserFinishResponse(value, executionId, status) {
439
+ return hasExactKeys(value, ['ok', 'execution'])
440
+ && value.ok === true
441
+ && hasExactKeys(value.execution, ['id', 'status'])
442
+ && value.execution.id === executionId
443
+ && value.execution.status === status;
444
+ }
445
+ function isHostedExecution(value) {
446
+ return hasExactKeys(value, ['id', 'command', 'status'])
447
+ && typeof value.id === 'string'
448
+ && typeof value.command === 'string'
449
+ && (value.status === 'succeeded' || value.status === 'failed' || value.status === 'timed_out');
450
+ }
451
+ function isHostedTraceReceipt(value) {
452
+ if (!hasOnlyKeys(value, ['receipt', 'executionId', 'artifactsUrl', 'liveViewUrl', 'replayUrl'])
453
+ || !isSafeReceiptToken(value.receipt)
454
+ || !isSafeReceiptToken(value.executionId))
455
+ return false;
456
+ const executionBase = publicExecutionBase(value.executionId);
457
+ if (!executionBase)
458
+ return false;
459
+ return optionalExactPath(value.artifactsUrl, `${executionBase}/artifacts`)
460
+ && optionalExactPath(value.liveViewUrl, `${executionBase}/live`)
461
+ && optionalExactPath(value.replayUrl, `${executionBase}/replay`);
462
+ }
463
+ function publicExecutionBase(executionId) {
464
+ try {
465
+ const encoded = encodeURIComponent(executionId);
466
+ if (encoded === '.' || encoded === '..')
467
+ return undefined;
468
+ return `/v1/executions/${encoded}`;
469
+ }
470
+ catch {
471
+ return undefined;
472
+ }
473
+ }
474
+ function optionalExactPath(value, expected) {
475
+ return value === undefined || value === expected;
476
+ }
477
+ function isSafeReceiptToken(value) {
478
+ return typeof value === 'string'
479
+ && value.length > 0
480
+ && !/[\u0000-\u001f\u007f\u2028\u2029]/u.test(value);
481
+ }
482
+ function isRecord(value) {
483
+ return !!value && typeof value === 'object' && !Array.isArray(value);
484
+ }
485
+ function hasOnlyKeys(value, allowed) {
486
+ return isRecord(value) && Object.keys(value).every(key => allowed.includes(key));
487
+ }
488
+ function hasExactKeys(value, expected) {
489
+ return hasOnlyKeys(value, expected) && expected.every(key => Object.prototype.hasOwnProperty.call(value, key));
490
+ }
491
+ function isAllowedExitCode(value) {
492
+ return Object.values(EXIT_CODES).includes(value);
493
+ }
494
+ function normalizeExitCode(value, fallback) {
495
+ return value !== undefined && isAllowedExitCode(value) ? value : fallback;
496
+ }
497
+ function protocolError(message) {
498
+ return new HostedClientError('HOSTED_PROTOCOL', message);
499
+ }
500
+ function normalizeTraceMode(value) {
501
+ return value === 'on' || value === 'retain-on-failure' ? value : 'off';
502
+ }
503
+ function isValidExecutedFailure(value, expectation) {
504
+ if (!value.execution || !expectation || value.error.exitCode === undefined)
505
+ return false;
506
+ if (value.execution.command !== expectation.command)
507
+ return false;
508
+ const traceRequired = expectation.traceMode === 'on' || expectation.traceMode === 'retain-on-failure';
509
+ return traceRequired ? value.trace !== undefined : value.trace === undefined;
122
510
  }