@intuned/runtime-dev 0.0.1-split.0
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/.babelrc +21 -0
- package/.eslintignore +10 -0
- package/.eslintrc.js +39 -0
- package/.vite/deps_temp_01af7156/package.json +3 -0
- package/.vscode/extensions.json +3 -0
- package/.vscode/launch.json +102 -0
- package/.vscode/settings.json +12 -0
- package/WebTemplate/accessKeyHelpers.ts +28 -0
- package/WebTemplate/api.ts +139 -0
- package/WebTemplate/app.ts +18 -0
- package/WebTemplate/controllers/async.ts +138 -0
- package/WebTemplate/controllers/authSessions/check.ts +68 -0
- package/WebTemplate/controllers/authSessions/create.ts +128 -0
- package/WebTemplate/controllers/authSessions/index.ts +41 -0
- package/WebTemplate/controllers/authSessions/killOperation.ts +35 -0
- package/WebTemplate/controllers/authSessions/resumeOperation.ts +80 -0
- package/WebTemplate/controllers/authSessions/store.ts +14 -0
- package/WebTemplate/controllers/controllers.ts +73 -0
- package/WebTemplate/controllers/runApi/helpers.ts +220 -0
- package/WebTemplate/controllers/runApi/index.ts +68 -0
- package/WebTemplate/controllers/runApi/types.ts +13 -0
- package/WebTemplate/controllers/traces.ts +151 -0
- package/WebTemplate/features.ts +8 -0
- package/WebTemplate/headers.ts +6 -0
- package/WebTemplate/index.playwright.ts +47 -0
- package/WebTemplate/index.vanilla.ts +44 -0
- package/WebTemplate/jobs.ts +356 -0
- package/WebTemplate/shutdown.ts +64 -0
- package/WebTemplate/utils.ts +294 -0
- package/bin/intuned-api-run +2 -0
- package/bin/intuned-auth-session-check +2 -0
- package/bin/intuned-auth-session-create +2 -0
- package/bin/intuned-auth-session-load +2 -0
- package/bin/intuned-auth-session-refresh +2 -0
- package/bin/intuned-browser-save-state +2 -0
- package/bin/intuned-browser-start +2 -0
- package/bin/intuned-build +2 -0
- package/bin/intuned-ts-check +2 -0
- package/package.json +133 -0
- package/playwright.config.ts +48 -0
- package/src/commands/api/run.ts +225 -0
- package/src/commands/auth-sessions/load.ts +42 -0
- package/src/commands/auth-sessions/run-check.ts +70 -0
- package/src/commands/auth-sessions/run-create.ts +124 -0
- package/src/commands/browser/save-state.ts +22 -0
- package/src/commands/browser/start-browser.ts +17 -0
- package/src/commands/build.ts +125 -0
- package/src/commands/common/browserUtils.ts +80 -0
- package/src/commands/common/getDefaultExportFromFile.ts +13 -0
- package/src/commands/common/getFirstLineNumber.test.ts +274 -0
- package/src/commands/common/getFirstLineNumber.ts +146 -0
- package/src/commands/common/sendMessageToClient.ts +8 -0
- package/src/commands/common/utils/fileUtils.ts +25 -0
- package/src/commands/common/utils/settings.ts +23 -0
- package/src/commands/common/utils/webTemplate.ts +46 -0
- package/src/commands/testing/saveVisibleHtml.ts +29 -0
- package/src/commands/ts-check.ts +88 -0
- package/src/common/Logger/Logger/index.ts +64 -0
- package/src/common/Logger/Logger/types.ts +9 -0
- package/src/common/Logger/index.ts +64 -0
- package/src/common/Logger/types.ts +9 -0
- package/src/common/assets/browser_scripts.js +2214 -0
- package/src/common/asyncLocalStorage/index.ts +29 -0
- package/src/common/cleanEnvironmentVariables.ts +13 -0
- package/src/common/constants.ts +1 -0
- package/src/common/contextStorageStateHelpers.ts +71 -0
- package/src/common/getPlaywrightConstructs.ts +283 -0
- package/src/common/jwtTokenManager.ts +111 -0
- package/src/common/settingsSchema.ts +16 -0
- package/src/common/telemetry.ts +49 -0
- package/src/index.ts +14 -0
- package/src/runtime/RunError.ts +16 -0
- package/src/runtime/downloadDirectory.ts +14 -0
- package/src/runtime/enums.d.ts +11 -0
- package/src/runtime/enums.ts +11 -0
- package/src/runtime/executionHelpers.test.ts +70 -0
- package/src/runtime/export.d.ts +202 -0
- package/src/runtime/extendPayload.ts +22 -0
- package/src/runtime/extendTimeout.ts +32 -0
- package/src/runtime/index.ts +8 -0
- package/src/runtime/requestMoreInfo.ts +40 -0
- package/src/runtime/runInfo.ts +19 -0
- package/template.tsconfig.json +14 -0
- package/tsconfig.eslint.json +5 -0
- package/tsconfig.json +24 -0
- package/typedoc.json +49 -0
- package/vite.config.ts +17 -0
package/.babelrc
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"presets": [
|
|
3
|
+
[
|
|
4
|
+
"@babel/preset-env",
|
|
5
|
+
{
|
|
6
|
+
"targets": {
|
|
7
|
+
"chrome": 115,
|
|
8
|
+
"node": "16"
|
|
9
|
+
},
|
|
10
|
+
"modules": "commonjs"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"@babel/preset-typescript"
|
|
14
|
+
],
|
|
15
|
+
"plugins": [
|
|
16
|
+
"babel-plugin-macros",
|
|
17
|
+
"@babel/plugin-transform-export-namespace-from"
|
|
18
|
+
],
|
|
19
|
+
"sourceMaps": false,
|
|
20
|
+
"comments": false
|
|
21
|
+
}
|
package/.eslintignore
ADDED
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
root: true,
|
|
3
|
+
parser: "@typescript-eslint/parser",
|
|
4
|
+
parserOptions: {
|
|
5
|
+
project: "./tsconfig.eslint.json",
|
|
6
|
+
tsconfigRootDir: __dirname,
|
|
7
|
+
},
|
|
8
|
+
plugins: ["@typescript-eslint", "deprecation", "prettier"],
|
|
9
|
+
ignorePatterns: ["src/common/browserScripts/rollup.config.mjs"],
|
|
10
|
+
extends: [
|
|
11
|
+
"eslint:recommended",
|
|
12
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
13
|
+
"plugin:@typescript-eslint/recommended",
|
|
14
|
+
],
|
|
15
|
+
rules: {
|
|
16
|
+
"@next/next/no-html-link-for-pages": 0,
|
|
17
|
+
"prettier/prettier": "error",
|
|
18
|
+
|
|
19
|
+
// recommended for safety
|
|
20
|
+
"@typescript-eslint/no-floating-promises": "error", // forgetting to await Activities and Workflow APIs is bad
|
|
21
|
+
"deprecation/deprecation": "warn",
|
|
22
|
+
|
|
23
|
+
// code style preference
|
|
24
|
+
"object-shorthand": ["warn", "always"],
|
|
25
|
+
|
|
26
|
+
// relaxed rules, for convenience
|
|
27
|
+
"@typescript-eslint/no-unused-vars": [
|
|
28
|
+
"warn",
|
|
29
|
+
{
|
|
30
|
+
argsIgnorePattern: "^_",
|
|
31
|
+
varsIgnorePattern: "^_",
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
35
|
+
"@typescript-eslint/no-empty-interface": "off",
|
|
36
|
+
"@typescript-eslint/ban-ts-comment": "warn",
|
|
37
|
+
"no-empty": "warn",
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Use IntelliSense to learn about possible attributes.
|
|
3
|
+
// Hover to view descriptions of existing attributes.
|
|
4
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"configurations": [
|
|
7
|
+
{
|
|
8
|
+
"name": "Hiltion api",
|
|
9
|
+
"type": "node",
|
|
10
|
+
"request": "launch",
|
|
11
|
+
"runtimeExecutable": "node",
|
|
12
|
+
"runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"],
|
|
13
|
+
|
|
14
|
+
"args": ["src/commands/api/run.ts", "hilton", "-j", "{}", "playwright"],
|
|
15
|
+
|
|
16
|
+
"cwd": "${workspaceRoot}",
|
|
17
|
+
"internalConsoleOptions": "openOnSessionStart",
|
|
18
|
+
"skipFiles": ["<node_internals>/**", "node_modules/**"]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "Books Extractor",
|
|
22
|
+
"type": "node",
|
|
23
|
+
"request": "launch",
|
|
24
|
+
"runtimeExecutable": "node",
|
|
25
|
+
"runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"],
|
|
26
|
+
|
|
27
|
+
"args": [
|
|
28
|
+
"src/commands/api/run.ts",
|
|
29
|
+
"books-array",
|
|
30
|
+
"-j",
|
|
31
|
+
"{}",
|
|
32
|
+
"playwright"
|
|
33
|
+
],
|
|
34
|
+
|
|
35
|
+
"cwd": "${workspaceRoot}",
|
|
36
|
+
"internalConsoleOptions": "openOnSessionStart",
|
|
37
|
+
"skipFiles": ["<node_internals>/**", "node_modules/**"]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "Run execution-info",
|
|
41
|
+
"type": "node",
|
|
42
|
+
"request": "launch",
|
|
43
|
+
"runtimeExecutable": "node",
|
|
44
|
+
"runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"],
|
|
45
|
+
|
|
46
|
+
"args": [
|
|
47
|
+
"src/commands/api/run.ts",
|
|
48
|
+
"execution-info",
|
|
49
|
+
"-j",
|
|
50
|
+
"{}",
|
|
51
|
+
"playwright"
|
|
52
|
+
],
|
|
53
|
+
|
|
54
|
+
"cwd": "${workspaceRoot}",
|
|
55
|
+
"internalConsoleOptions": "openOnSessionStart",
|
|
56
|
+
"skipFiles": ["<node_internals>/**", "node_modules/**"]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "Run progressive",
|
|
60
|
+
"type": "node",
|
|
61
|
+
"request": "launch",
|
|
62
|
+
"runtimeExecutable": "node",
|
|
63
|
+
"runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"],
|
|
64
|
+
|
|
65
|
+
"args": [
|
|
66
|
+
"src/commands/api/run.ts",
|
|
67
|
+
"progressive",
|
|
68
|
+
"-j",
|
|
69
|
+
"{}",
|
|
70
|
+
"playwright"
|
|
71
|
+
],
|
|
72
|
+
|
|
73
|
+
"cwd": "${workspaceRoot}",
|
|
74
|
+
"internalConsoleOptions": "openOnSessionStart",
|
|
75
|
+
"skipFiles": ["<node_internals>/**", "node_modules/**"]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "Run progressive - standalone",
|
|
79
|
+
"type": "node",
|
|
80
|
+
"request": "launch",
|
|
81
|
+
"runtimeExecutable": "node",
|
|
82
|
+
"runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"],
|
|
83
|
+
|
|
84
|
+
"args": ["src/commands/api/run.ts", "progressive", "-j", "{}"],
|
|
85
|
+
"cwd": "${workspaceRoot}",
|
|
86
|
+
"internalConsoleOptions": "openOnSessionStart",
|
|
87
|
+
"skipFiles": ["<node_internals>/**", "node_modules/**"]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "Run progressive extract - standalone",
|
|
91
|
+
"type": "node",
|
|
92
|
+
"request": "launch",
|
|
93
|
+
"runtimeExecutable": "node",
|
|
94
|
+
"runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"],
|
|
95
|
+
|
|
96
|
+
"args": ["src/commands/api/run.ts", "progressive-extract", "-j", "{}"],
|
|
97
|
+
"cwd": "${workspaceRoot}",
|
|
98
|
+
"internalConsoleOptions": "openOnSessionStart",
|
|
99
|
+
"skipFiles": ["<node_internals>/**", "node_modules/**"]
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"editor.formatOnSave": true,
|
|
3
|
+
"cSpell.words": ["backcompat", "ـget", "INFINITI", "neverthrow"],
|
|
4
|
+
"editor.detectIndentation": true,
|
|
5
|
+
"files.exclude": {
|
|
6
|
+
"dist": false,
|
|
7
|
+
"node_modules": false,
|
|
8
|
+
"output": false,
|
|
9
|
+
"intuned": false,
|
|
10
|
+
"htmlFiles": true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
|
|
3
|
+
function hashToken(token: string) {
|
|
4
|
+
// This has to match the logic from the workflow that saves adds the token to fly io App.
|
|
5
|
+
return createHash("sha256").update(token).digest("hex");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const accessKeyValidatorMiddleware = (req: any, res: any, next: any) => {
|
|
9
|
+
const authHeader = req.headers.authorization;
|
|
10
|
+
|
|
11
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
12
|
+
return res.status(401).send("Unauthorized");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const token = authHeader.split(" ")[1];
|
|
16
|
+
|
|
17
|
+
// Create a hash of the provided token
|
|
18
|
+
const hashedToken = hashToken(token);
|
|
19
|
+
|
|
20
|
+
const hashedTokenFromEnv = process.env.HASHED_ACCESS_KEY;
|
|
21
|
+
|
|
22
|
+
if (hashedToken !== hashedTokenFromEnv) {
|
|
23
|
+
console.log("Token mismatch", hashedToken, hashedTokenFromEnv);
|
|
24
|
+
return res.status(401).send("Unauthorized");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
next();
|
|
28
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
errorRetryMiddleware,
|
|
3
|
+
proxyToUrl,
|
|
4
|
+
ProxyConfig,
|
|
5
|
+
isJobRunMachine,
|
|
6
|
+
} from "./utils";
|
|
7
|
+
import { accessKeyValidatorMiddleware } from "./accessKeyHelpers";
|
|
8
|
+
import { runWithContext } from "@intuned/runtime";
|
|
9
|
+
import {
|
|
10
|
+
createAuthSessionController,
|
|
11
|
+
createAuthSessionAsyncController,
|
|
12
|
+
resumeAuthSessionOperationController,
|
|
13
|
+
resumeAuthSessionOperationAsyncController,
|
|
14
|
+
killAuthSessionOperationController,
|
|
15
|
+
checkAuthSessionController,
|
|
16
|
+
checkAuthSessionAsyncController,
|
|
17
|
+
} from "./controllers/authSessions";
|
|
18
|
+
import { app } from "./app";
|
|
19
|
+
import {
|
|
20
|
+
getActiveAsyncEndpointController,
|
|
21
|
+
runApiAsyncController,
|
|
22
|
+
runApiController,
|
|
23
|
+
} from "./controllers/runApi";
|
|
24
|
+
import {
|
|
25
|
+
uploadTraceController,
|
|
26
|
+
deleteTraceController,
|
|
27
|
+
} from "./controllers/traces";
|
|
28
|
+
import {
|
|
29
|
+
RUN_ID_HEADER,
|
|
30
|
+
JOB_ID_HEADER,
|
|
31
|
+
JOB_RUN_ID_HEADER,
|
|
32
|
+
QUEUE_ID_HEADER,
|
|
33
|
+
} from "./headers";
|
|
34
|
+
import { FEATURES } from "./features";
|
|
35
|
+
|
|
36
|
+
if (!isJobRunMachine()) {
|
|
37
|
+
app.use((req, _, next) => {
|
|
38
|
+
const runId = req.headers[RUN_ID_HEADER] as string;
|
|
39
|
+
const jobId = req.headers[JOB_ID_HEADER] as string;
|
|
40
|
+
const jobRunId = req.headers[JOB_RUN_ID_HEADER] as string;
|
|
41
|
+
const queueId = req.headers[QUEUE_ID_HEADER] as string;
|
|
42
|
+
const proxy = req?.body?.proxy
|
|
43
|
+
? (proxyToUrl(req.body.proxy as ProxyConfig) as string)
|
|
44
|
+
: undefined;
|
|
45
|
+
const contextData = {
|
|
46
|
+
runId: runId ?? "",
|
|
47
|
+
jobId,
|
|
48
|
+
jobRunId,
|
|
49
|
+
queueId,
|
|
50
|
+
proxy,
|
|
51
|
+
...(req?.body?.executionContext ?? {}),
|
|
52
|
+
};
|
|
53
|
+
runWithContext(contextData, next);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
app.get(
|
|
57
|
+
"/api/protected/health",
|
|
58
|
+
accessKeyValidatorMiddleware,
|
|
59
|
+
async (req, res) => {
|
|
60
|
+
res.status(200).json({ status: "ok" });
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
app.post(
|
|
65
|
+
"/api/auth-session/create",
|
|
66
|
+
accessKeyValidatorMiddleware,
|
|
67
|
+
errorRetryMiddleware(createAuthSessionController)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
app.post(
|
|
71
|
+
"/api/auth-session-async/create",
|
|
72
|
+
accessKeyValidatorMiddleware,
|
|
73
|
+
errorRetryMiddleware(createAuthSessionAsyncController)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
app.post(
|
|
77
|
+
"/api/auth-session/check",
|
|
78
|
+
accessKeyValidatorMiddleware,
|
|
79
|
+
errorRetryMiddleware(checkAuthSessionController)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
app.post(
|
|
83
|
+
"/api/auth-session-async/check",
|
|
84
|
+
accessKeyValidatorMiddleware,
|
|
85
|
+
errorRetryMiddleware(checkAuthSessionAsyncController)
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
app.post(
|
|
89
|
+
"/api/auth-session/kill",
|
|
90
|
+
accessKeyValidatorMiddleware,
|
|
91
|
+
errorRetryMiddleware(killAuthSessionOperationController)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
app.post(
|
|
95
|
+
"/api/auth-session/resume",
|
|
96
|
+
accessKeyValidatorMiddleware,
|
|
97
|
+
errorRetryMiddleware(resumeAuthSessionOperationController)
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
app.post(
|
|
101
|
+
"/api/auth-session-async/resume",
|
|
102
|
+
accessKeyValidatorMiddleware,
|
|
103
|
+
errorRetryMiddleware(resumeAuthSessionOperationAsyncController)
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
app.post(
|
|
107
|
+
"/api/run/*",
|
|
108
|
+
accessKeyValidatorMiddleware,
|
|
109
|
+
errorRetryMiddleware(runApiController)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
app.post(
|
|
113
|
+
"/api/run-async/start/*",
|
|
114
|
+
accessKeyValidatorMiddleware,
|
|
115
|
+
errorRetryMiddleware(runApiAsyncController) // todo: this probably needs changing
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
app.get(
|
|
119
|
+
"/api/run-async/count",
|
|
120
|
+
accessKeyValidatorMiddleware,
|
|
121
|
+
getActiveAsyncEndpointController
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
app.post(
|
|
125
|
+
"/api/trace/upload/:runId",
|
|
126
|
+
accessKeyValidatorMiddleware,
|
|
127
|
+
uploadTraceController
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
app.post(
|
|
131
|
+
"/api/trace/delete/:runId",
|
|
132
|
+
accessKeyValidatorMiddleware,
|
|
133
|
+
deleteTraceController
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
app.get("/api/features", accessKeyValidatorMiddleware, async (_, res) => {
|
|
138
|
+
res.status(200).json({ features: FEATURES });
|
|
139
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { App } from "@tinyhttp/app";
|
|
2
|
+
import { json as parser } from "milliparsec";
|
|
3
|
+
|
|
4
|
+
export const port = process.env.PORT ? parseInt(process.env.PORT) : 4000;
|
|
5
|
+
|
|
6
|
+
export const app = new App();
|
|
7
|
+
|
|
8
|
+
app.use("/", parser());
|
|
9
|
+
|
|
10
|
+
// add header with flyio instance id where the request is served from
|
|
11
|
+
app.use((_, res, next) => {
|
|
12
|
+
res.setHeader("fly-instance-id", process.env.FLY_ALLOC_ID ?? "");
|
|
13
|
+
next();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
app.get("/api/health", async (req, res) => {
|
|
17
|
+
res.status(200).json({ status: "ok" });
|
|
18
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { AsyncRunEndpointController, getErrorResponse } from "../utils";
|
|
2
|
+
import {
|
|
3
|
+
backendFunctionsTokenManager,
|
|
4
|
+
callBackendFunctionWithToken,
|
|
5
|
+
} from "@intuned/runtime/dist/common/jwtTokenManager";
|
|
6
|
+
import retry from "async-retry";
|
|
7
|
+
import { ShutdownController } from "../shutdown";
|
|
8
|
+
import { Handler } from "@tinyhttp/app";
|
|
9
|
+
import { RequestHandler } from "./controllers";
|
|
10
|
+
|
|
11
|
+
export async function runEndpointAsync<_Parameters extends any[], _Result>(
|
|
12
|
+
handler: RequestHandler<_Parameters, _Result>,
|
|
13
|
+
parameters: _Parameters,
|
|
14
|
+
endpointParameters: {
|
|
15
|
+
functionsToken: string;
|
|
16
|
+
taskToken: string;
|
|
17
|
+
startTime: number;
|
|
18
|
+
// only used for logging
|
|
19
|
+
requestId?: string;
|
|
20
|
+
}
|
|
21
|
+
): Promise<{
|
|
22
|
+
status: number;
|
|
23
|
+
body: _Result;
|
|
24
|
+
}> {
|
|
25
|
+
const {
|
|
26
|
+
functionsToken,
|
|
27
|
+
taskToken,
|
|
28
|
+
startTime,
|
|
29
|
+
requestId = "endpoint",
|
|
30
|
+
} = endpointParameters;
|
|
31
|
+
AsyncRunEndpointController.addRequest(taskToken);
|
|
32
|
+
|
|
33
|
+
backendFunctionsTokenManager.token = functionsToken;
|
|
34
|
+
|
|
35
|
+
let status: number, body: _Result;
|
|
36
|
+
try {
|
|
37
|
+
console.log("Running", requestId, "in async mode");
|
|
38
|
+
({ status, body } = await handler(...parameters));
|
|
39
|
+
console.log("Finished running", requestId, "in async mode");
|
|
40
|
+
} catch (e: any) {
|
|
41
|
+
console.error(`Error running ${requestId} in async mode:`, e);
|
|
42
|
+
({ status, body } = getErrorResponse(e));
|
|
43
|
+
} finally {
|
|
44
|
+
AsyncRunEndpointController.removeRequest(taskToken);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// report result
|
|
48
|
+
console.log("Reporting result for", requestId);
|
|
49
|
+
try {
|
|
50
|
+
// `retry` will keep retrying any errors unless they are thrown through calling `bail`
|
|
51
|
+
await retry(
|
|
52
|
+
async (bail: (e: Error) => void) => {
|
|
53
|
+
const response = await callBackendFunctionWithToken(
|
|
54
|
+
"run/reportResult",
|
|
55
|
+
{
|
|
56
|
+
method: "POST",
|
|
57
|
+
headers: {
|
|
58
|
+
"Content-Type": "application/json",
|
|
59
|
+
"fly-instance-id": process.env.FLY_ALLOC_ID ?? "",
|
|
60
|
+
},
|
|
61
|
+
body: JSON.stringify({
|
|
62
|
+
status,
|
|
63
|
+
body,
|
|
64
|
+
startTime,
|
|
65
|
+
taskToken,
|
|
66
|
+
}),
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
// Do not retry unauthenticated, forbidden, not found or too large responses
|
|
71
|
+
if ([401, 403, 404, 413].includes(response.status)) {
|
|
72
|
+
bail(
|
|
73
|
+
new Error(
|
|
74
|
+
`Reporting result failed for ${requestId} (non-retryable), status ${response.status}`
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
// retry other errors (such as 502)
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Reporting result failed for ${requestId}, status ${response.status}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return response;
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
retries: 5,
|
|
88
|
+
factor: 2,
|
|
89
|
+
maxTimeout: 1000 * 60, // 1 minute
|
|
90
|
+
minTimeout: 1000 * 5, // 5 seconds
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
console.log("Reported result for", requestId);
|
|
94
|
+
} catch (e: any) {
|
|
95
|
+
console.log(e?.message ?? `Reporting result failed for ${requestId}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
await ShutdownController.instance.checkForShutdown();
|
|
99
|
+
return { status, body };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type AsyncEndpointBody<T = unknown> = T & {
|
|
103
|
+
functionsToken: string;
|
|
104
|
+
taskToken: string;
|
|
105
|
+
startTime: number;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export function makeAsyncEndpointController<_Parameters extends any[]>({
|
|
109
|
+
requestId,
|
|
110
|
+
handler,
|
|
111
|
+
parameters,
|
|
112
|
+
}: {
|
|
113
|
+
requestId: string | ((req: any) => string);
|
|
114
|
+
handler: RequestHandler<_Parameters, any>;
|
|
115
|
+
parameters: _Parameters | ((req: any) => _Parameters);
|
|
116
|
+
}): Handler {
|
|
117
|
+
return async (req, res) => {
|
|
118
|
+
const { taskToken, startTime, functionsToken } =
|
|
119
|
+
req.body as AsyncEndpointBody<_Parameters>;
|
|
120
|
+
if (AsyncRunEndpointController.isRunning(taskToken)) {
|
|
121
|
+
res.status(409).json({ error: "Already running" });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
void runEndpointAsync(
|
|
126
|
+
handler,
|
|
127
|
+
typeof parameters === "function" ? parameters(req) : parameters,
|
|
128
|
+
{
|
|
129
|
+
taskToken,
|
|
130
|
+
startTime,
|
|
131
|
+
functionsToken,
|
|
132
|
+
requestId: typeof requestId === "function" ? requestId(req) : requestId,
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
res.status(202).json({});
|
|
137
|
+
};
|
|
138
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { getDownloadDirectoryPath } from "@intuned/runtime";
|
|
2
|
+
import { getProductionPlaywrightConstructs } from "@intuned/runtime";
|
|
3
|
+
import { checkAuthSessionWithRetries, isHeadless } from "../../utils";
|
|
4
|
+
import * as fs from "fs-extra";
|
|
5
|
+
import { readJSON } from "fs-extra";
|
|
6
|
+
|
|
7
|
+
export async function checkAuthSession({
|
|
8
|
+
mode,
|
|
9
|
+
proxy,
|
|
10
|
+
session,
|
|
11
|
+
}: {
|
|
12
|
+
mode: "async" | "sync";
|
|
13
|
+
session: any;
|
|
14
|
+
proxy?: {
|
|
15
|
+
server: string;
|
|
16
|
+
username: string;
|
|
17
|
+
password: string;
|
|
18
|
+
};
|
|
19
|
+
}): Promise<{
|
|
20
|
+
status: number;
|
|
21
|
+
body: any;
|
|
22
|
+
}> {
|
|
23
|
+
const isAuthSessionEnabled = (await readJSON("./Intuned.json")).authSessions
|
|
24
|
+
?.enabled;
|
|
25
|
+
|
|
26
|
+
if (!isAuthSessionEnabled) {
|
|
27
|
+
return {
|
|
28
|
+
status: 400,
|
|
29
|
+
body: {
|
|
30
|
+
error: "Invalid Request",
|
|
31
|
+
message: "auth sessions are not enabled",
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (mode !== "async") {
|
|
37
|
+
return {
|
|
38
|
+
status: 400,
|
|
39
|
+
body: {
|
|
40
|
+
error: "Invalid Request",
|
|
41
|
+
message: "only async mode is supported",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const downloadsPath = getDownloadDirectoryPath();
|
|
47
|
+
const headless = isHeadless();
|
|
48
|
+
const { page, context } = await getProductionPlaywrightConstructs({
|
|
49
|
+
headless,
|
|
50
|
+
proxy,
|
|
51
|
+
downloadsPath,
|
|
52
|
+
storageState: session,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const authSessionCheckResult = await checkAuthSessionWithRetries(
|
|
56
|
+
page,
|
|
57
|
+
context,
|
|
58
|
+
2
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
await fs.remove(downloadsPath);
|
|
62
|
+
return {
|
|
63
|
+
status: 200,
|
|
64
|
+
body: {
|
|
65
|
+
result: authSessionCheckResult,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|