@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.
- package/README.md +13 -2
- package/cli-manifest.json +217 -13
- package/clis/chatgpt/ask.js +1 -1
- package/clis/chatgpt/ask.test.js +11 -0
- package/clis/chatgpt/commands.test.js +102 -1
- package/clis/chatgpt/deep-research-result.js +45 -3
- package/clis/chatgpt/image.js +22 -2
- package/clis/chatgpt/project-file-add.js +24 -1
- package/clis/chatgpt/utils.js +258 -52
- package/clis/chatgpt/utils.test.js +259 -1
- package/clis/claude/ask.js +21 -1
- package/clis/facebook/feed.js +110 -24
- package/clis/facebook/feed.test.js +62 -0
- package/clis/facebook/search.js +185 -38
- package/clis/facebook/search.test.js +95 -50
- package/clis/instagram/explore.js +30 -12
- package/clis/instagram/explore.test.js +77 -0
- package/clis/instagram/post.js +14 -1
- package/clis/instagram/reel.js +13 -1
- package/clis/linkedin/company.js +153 -0
- package/clis/linkedin/company.test.js +111 -0
- package/clis/linkedin/connections.js +135 -0
- package/clis/linkedin/connections.test.js +141 -0
- package/clis/mercury/reimbursement-draft.js +12 -1
- package/clis/twitter/article-evaluate.test.js +27 -0
- package/clis/twitter/article.js +73 -16
- package/clis/twitter/article.test.js +209 -0
- package/clis/twitter/download.js +6 -1
- package/clis/twitter/post.js +14 -1
- package/clis/twitter/profile.js +1 -1
- package/clis/twitter/profile.test.js +8 -0
- package/clis/twitter/quote.js +11 -1
- package/clis/twitter/reply.js +11 -1
- package/dist/src/browser/base-page.d.ts +26 -0
- package/dist/src/browser/base-page.js +23 -8
- package/dist/src/browser/base-page.test.js +45 -12
- package/dist/src/browser/command-catalog.d.ts +6 -0
- package/dist/src/browser/command-catalog.js +259 -0
- package/dist/src/browser/command-catalog.test.d.ts +1 -0
- package/dist/src/browser/command-catalog.test.js +79 -0
- package/dist/src/browser/runtime/local-cloak/darwin-background-launch.d.ts +17 -0
- package/dist/src/browser/runtime/local-cloak/darwin-background-launch.js +106 -0
- package/dist/src/browser/runtime/local-cloak/darwin-background-launch.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/darwin-background-launch.test.js +65 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.d.ts +7 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.js +23 -9
- package/dist/src/browser/runtime/local-cloak/session-manager.test.js +36 -0
- package/dist/src/browser/target-resolver.d.ts +1 -0
- package/dist/src/browser/target-resolver.js +99 -4
- package/dist/src/browser/target-resolver.test.js +141 -1
- package/dist/src/build-manifest.d.ts +6 -0
- package/dist/src/build-manifest.js +18 -1
- package/dist/src/build-manifest.test.js +57 -1
- package/dist/src/builtin-command-surface.d.ts +9 -0
- package/dist/src/builtin-command-surface.js +16 -0
- package/dist/src/check-hosted-contract.test.d.ts +1 -0
- package/dist/src/check-hosted-contract.test.js +56 -0
- package/dist/src/cli.js +58 -106
- package/dist/src/cli.test.js +71 -1
- package/dist/src/command-presentation.d.ts +95 -0
- package/dist/src/command-presentation.js +486 -0
- package/dist/src/command-presentation.test.d.ts +1 -0
- package/dist/src/command-presentation.test.js +97 -0
- package/dist/src/command-surface.d.ts +43 -0
- package/dist/src/command-surface.js +205 -0
- package/dist/src/command-surface.test.d.ts +1 -0
- package/dist/src/command-surface.test.js +406 -0
- package/dist/src/commanderAdapter.d.ts +5 -1
- package/dist/src/commanderAdapter.js +12 -55
- package/dist/src/commanderAdapter.test.js +15 -3
- package/dist/src/community-plugin-sync.d.ts +11 -0
- package/dist/src/community-plugin-sync.js +138 -0
- package/dist/src/community-plugin-sync.test.d.ts +1 -0
- package/dist/src/community-plugin-sync.test.js +123 -0
- package/dist/src/completion-fast.d.ts +4 -4
- package/dist/src/completion-fast.js +15 -34
- package/dist/src/completion-shared.d.ts +4 -0
- package/dist/src/completion-shared.js +38 -0
- package/dist/src/completion.js +3 -27
- package/dist/src/completion.test.js +31 -2
- package/dist/src/docs-sync-review-cli.test.d.ts +1 -0
- package/dist/src/docs-sync-review-cli.test.js +322 -0
- package/dist/src/docs-sync-review.d.ts +117 -0
- package/dist/src/docs-sync-review.js +475 -0
- package/dist/src/docs-sync-review.test.d.ts +1 -0
- package/dist/src/docs-sync-review.test.js +324 -0
- package/dist/src/errors.d.ts +12 -3
- package/dist/src/errors.js +11 -7
- package/dist/src/errors.test.js +14 -1
- package/dist/src/execution.d.ts +1 -2
- package/dist/src/execution.js +4 -45
- package/dist/src/generate-release-notes-cli.test.js +7 -2
- package/dist/src/help.d.ts +4 -0
- package/dist/src/help.js +50 -255
- package/dist/src/help.test.js +27 -1
- package/dist/src/hosted/args.d.ts +2 -7
- package/dist/src/hosted/args.js +2 -99
- package/dist/src/hosted/args.test.js +15 -1
- package/dist/src/hosted/availability.d.ts +13 -0
- package/dist/src/hosted/availability.js +16 -0
- package/dist/src/hosted/availability.test.d.ts +1 -0
- package/dist/src/hosted/availability.test.js +180 -0
- package/dist/src/hosted/browser-args.d.ts +18 -0
- package/dist/src/hosted/browser-args.js +152 -0
- package/dist/src/hosted/browser-args.test.d.ts +1 -0
- package/dist/src/hosted/browser-args.test.js +182 -0
- package/dist/src/hosted/client.d.ts +31 -2
- package/dist/src/hosted/client.js +441 -53
- package/dist/src/hosted/client.test.js +651 -32
- package/dist/src/hosted/config.d.ts +7 -2
- package/dist/src/hosted/config.js +26 -4
- package/dist/src/hosted/config.test.js +34 -1
- package/dist/src/hosted/contract.d.ts +94 -0
- package/dist/src/hosted/contract.js +208 -0
- package/dist/src/hosted/contract.test.d.ts +1 -0
- package/dist/src/hosted/contract.test.js +361 -0
- package/dist/src/hosted/credentials.d.ts +38 -0
- package/dist/src/hosted/credentials.js +248 -0
- package/dist/src/hosted/credentials.test.d.ts +1 -0
- package/dist/src/hosted/credentials.test.js +93 -0
- package/dist/src/hosted/file-contract.test.d.ts +1 -0
- package/dist/src/hosted/file-contract.test.js +174 -0
- package/dist/src/hosted/files.d.ts +29 -0
- package/dist/src/hosted/files.js +296 -0
- package/dist/src/hosted/files.test.d.ts +1 -0
- package/dist/src/hosted/files.test.js +231 -0
- package/dist/src/hosted/main-lifecycle.test.d.ts +1 -0
- package/dist/src/hosted/main-lifecycle.test.js +213 -0
- package/dist/src/hosted/manifest.d.ts +5 -0
- package/dist/src/hosted/manifest.js +21 -66
- package/dist/src/hosted/manifest.test.js +120 -2
- package/dist/src/hosted/output-parity.test.d.ts +1 -0
- package/dist/src/hosted/output-parity.test.js +108 -0
- package/dist/src/hosted/root-command-surface.test.d.ts +1 -0
- package/dist/src/hosted/root-command-surface.test.js +646 -0
- package/dist/src/hosted/runner.d.ts +7 -0
- package/dist/src/hosted/runner.js +554 -253
- package/dist/src/hosted/runner.test.js +1471 -53
- package/dist/src/hosted/setup.d.ts +3 -2
- package/dist/src/hosted/setup.js +45 -15
- package/dist/src/hosted/setup.test.js +133 -6
- package/dist/src/hosted/types.d.ts +101 -23
- package/dist/src/main.js +120 -108
- package/dist/src/manifest-types.d.ts +2 -0
- package/dist/src/output.d.ts +17 -2
- package/dist/src/output.js +88 -81
- package/dist/src/output.test.js +141 -51
- package/dist/src/pipeline/executor.test.js +1 -0
- package/dist/src/pipeline/steps/download.test.js +1 -0
- package/dist/src/plugin-create-cli.test.d.ts +1 -0
- package/dist/src/plugin-create-cli.test.js +37 -0
- package/dist/src/plugin-manifest.d.ts +12 -0
- package/dist/src/plugin-manifest.js +16 -0
- package/dist/src/plugin-manifest.test.js +19 -1
- package/dist/src/plugin-scaffold.d.ts +4 -1
- package/dist/src/plugin-scaffold.js +4 -1
- package/dist/src/plugin-scaffold.test.js +23 -8
- package/dist/src/plugin.js +4 -1
- package/dist/src/plugin.test.js +13 -0
- package/dist/src/registry.d.ts +2 -0
- package/dist/src/release-notes.js +12 -4
- package/dist/src/release-notes.test.js +27 -15
- package/dist/src/root-command-surface.d.ts +31 -0
- package/dist/src/root-command-surface.js +106 -0
- package/dist/src/serialization.d.ts +1 -16
- package/dist/src/serialization.js +5 -55
- package/dist/src/stream-write.d.ts +12 -0
- package/dist/src/stream-write.js +91 -0
- package/dist/src/stream-write.test.d.ts +1 -0
- package/dist/src/stream-write.test.js +186 -0
- package/dist/src/types.d.ts +5 -0
- package/dist/src/utils.d.ts +1 -1
- package/dist/src/utils.js +2 -8
- package/dist/src/utils.test.js +50 -0
- package/hosted-contract.json +36748 -0
- package/package.json +6 -1
- package/scripts/check-hosted-contract.mjs +108 -0
- package/scripts/docs-sync-review.ts +332 -0
- package/scripts/sync-community-plugins.ts +14 -0
- package/skills/webcmd-autofix/SKILL.md +26 -37
- 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
|
-
|
|
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
|
|
22
|
-
throw
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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 (!
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
192
|
+
throw protocolError('Webcmd Cloud returned non-JSON response.');
|
|
115
193
|
}
|
|
116
194
|
}
|
|
117
195
|
function isHostedError(value) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
}
|