anycloud 0.0.12 → 0.0.14
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/binaries/anycloud-darwin-arm64 +0 -0
- package/binaries/anycloud-darwin-x64 +0 -0
- package/binaries/anycloud-linux-arm64 +0 -0
- package/binaries/anycloud-linux-x64 +0 -0
- package/dist/cli/commands/build.js +6 -6
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/daemon/index.js +34 -14
- package/dist/cli/commands/daemon/index.js.map +1 -1
- package/dist/cli/commands/logs.js +2 -9
- package/dist/cli/commands/logs.js.map +1 -1
- package/dist/cli/commands/run.js +82 -69
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/status.js +82 -20
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/terminate.js +27 -46
- package/dist/cli/commands/terminate.js.map +1 -1
- package/dist/cli/services/api-client.js +14 -6
- package/dist/cli/services/api-client.js.map +1 -1
- package/dist/cli/services/docker.js +14 -68
- package/dist/cli/services/docker.js.map +1 -1
- package/dist/daemon/cluster.js +23 -25
- package/dist/daemon/cluster.js.map +1 -1
- package/dist/shared/types.js +15 -1
- package/dist/shared/types.js.map +1 -1
- package/package.json +10 -9
- package/dist/daemon/cert.js +0 -31
- package/dist/daemon/cert.js.map +0 -1
- package/dist/daemon/docker.js +0 -208
- package/dist/daemon/docker.js.map +0 -1
- package/dist/daemon/server.js +0 -255
- package/dist/daemon/server.js.map +0 -1
package/dist/shared/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,iBAAiB,MAAM,2CAA2C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChG,OAAO,iBAAiB,MAAM,2CAA2C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChG,OAAO,mBAAmB,MAAM,6CAA6C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACpG,OAAO,iBAAiB,MAAM,2CAA2C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChG,OAAO,iBAAiB,MAAM,2CAA2C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChG,OAAO,mBAAmB,MAAM,6CAA6C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAEpG,sBAAsB;AACtB,MAAM,CAAN,IAAY,
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anycloud",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "Deploy containerized jobs and servers to any cloud provider",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -17,28 +17,27 @@
|
|
|
17
17
|
"node": ">=22.x"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
-
"build": "yarn clean && tsc",
|
|
20
|
+
"build": "yarn clean && tsc && yarn build:check",
|
|
21
|
+
"build:check": "LOCAL=true node --eval \"import('./dist/conductor/server.js').catch(e => { console.error(e.message); process.exit(1); })\"",
|
|
21
22
|
"build:binaries": "bash release/build-binaries.sh",
|
|
22
23
|
"clean": "rm -rf dist",
|
|
23
24
|
"start": "yarn build && wrangler dev",
|
|
24
|
-
"deploy:staging": "
|
|
25
|
-
"deploy:
|
|
25
|
+
"deploy:check:staging": "export $(cat .dev.vars.staging | xargs) && DEPLOY_ENV=staging npx tsx scripts/pre-deploy-check.ts",
|
|
26
|
+
"deploy:staging": "yarn deploy:check:staging && wrangler deploy --env staging",
|
|
27
|
+
"deploy:check:prod": "export $(cat .dev.vars.prod | xargs) && DEPLOY_ENV=production npx tsx scripts/pre-deploy-check.ts",
|
|
28
|
+
"deploy:prod": "yarn deploy:check:prod && wrangler deploy --env production",
|
|
26
29
|
"cli": "CONDUCTOR_URL=https://conductor.laplazahealth.org node ./dist/cli/index.js",
|
|
27
30
|
"cli:prod": "node ./dist/cli/index.js",
|
|
28
31
|
"start:conductor": "node ./dist/conductor/server.js",
|
|
29
32
|
"test": "LOCAL=true vitest run",
|
|
30
33
|
"test:watch": "LOCAL=true vitest",
|
|
31
|
-
"test:integration": "yarn test:daemon && yarn test:preemption",
|
|
32
34
|
"terminate": "export $(cat .dev.vars.staging | xargs) && ts-node-dev scripts/forceTerminate.ts",
|
|
33
|
-
"
|
|
34
|
-
"test:daemon": "bash test/integration/daemon-basic.test.sh",
|
|
35
|
-
"test:daemon-private-image": "bash test/integration/daemon-private-image.test.sh",
|
|
35
|
+
"test:private-image": "GITHUB_TOKEN=$(cat ~/.anycloud/.token) ./test/integration/daemon-private-image.test.sh",
|
|
36
36
|
"test:preemption": "bash test/integration/daemon-preemption.test.sh",
|
|
37
37
|
"test:vmstartup": "bash test/integration/vmstartup.test.sh",
|
|
38
38
|
"test:conductor": "bash test/integration/conductor-startup.test.sh",
|
|
39
39
|
"test:e2e:azure": "bash scripts/test-jobs-integration-azure.sh",
|
|
40
40
|
"test:binary": "bash test/integration/binary.test.sh",
|
|
41
|
-
"test:all": "yarn test && yarn test:integration",
|
|
42
41
|
"style": "yarn eslint . --ext .js,.ts && yarn prettier --check .",
|
|
43
42
|
"fmt": "yarn prettier --write .",
|
|
44
43
|
"typegen": "wrangler types && yarn fmt"
|
|
@@ -77,6 +76,7 @@
|
|
|
77
76
|
"posthog-node": "^5.11.2",
|
|
78
77
|
"prompts": "^2.4.2",
|
|
79
78
|
"semver": "^7.6.3",
|
|
79
|
+
"ssh2": "^1.17.0",
|
|
80
80
|
"systeminformation": "^5.21.0",
|
|
81
81
|
"unique-names-generator": "^4.3.1",
|
|
82
82
|
"uuid": "^11.0.5"
|
|
@@ -88,6 +88,7 @@
|
|
|
88
88
|
"@types/jest": "^26.0.20",
|
|
89
89
|
"@types/node-forge": "^0.9.7",
|
|
90
90
|
"@types/prompts": "^2.4.9",
|
|
91
|
+
"@types/ssh2": "^1.15.5",
|
|
91
92
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
92
93
|
"@typescript-eslint/parser": "^6.21.0",
|
|
93
94
|
"bun": "^1.3.3",
|
package/dist/daemon/cert.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generate self-signed SSL certificates for daemon HTTPS server
|
|
3
|
-
*/
|
|
4
|
-
import forge from 'node-forge';
|
|
5
|
-
export function generateSelfSignedCert() {
|
|
6
|
-
const keys = forge.pki.rsa.generateKeyPair(2048);
|
|
7
|
-
const cert = forge.pki.createCertificate();
|
|
8
|
-
cert.publicKey = keys.publicKey;
|
|
9
|
-
cert.serialNumber = '01';
|
|
10
|
-
cert.validity.notBefore = new Date();
|
|
11
|
-
cert.validity.notAfter = new Date();
|
|
12
|
-
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
|
|
13
|
-
const attrs = [
|
|
14
|
-
{ name: 'commonName', value: 'anycloud-daemon' },
|
|
15
|
-
{ name: 'countryName', value: 'US' },
|
|
16
|
-
{ shortName: 'ST', value: 'California' },
|
|
17
|
-
{ name: 'localityName', value: 'San Francisco' },
|
|
18
|
-
{ name: 'organizationName', value: 'AnyCloud' },
|
|
19
|
-
{ shortName: 'OU', value: 'Daemon' },
|
|
20
|
-
];
|
|
21
|
-
cert.setSubject(attrs);
|
|
22
|
-
cert.setIssuer(attrs);
|
|
23
|
-
cert.sign(keys.privateKey);
|
|
24
|
-
const pemCert = forge.pki.certificateToPem(cert);
|
|
25
|
-
const pemKey = forge.pki.privateKeyToPem(keys.privateKey);
|
|
26
|
-
return {
|
|
27
|
-
cert: pemCert,
|
|
28
|
-
key: pemKey,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
//# sourceMappingURL=cert.js.map
|
package/dist/daemon/cert.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cert.js","sourceRoot":"","sources":["../../src/daemon/cert.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,UAAU,sBAAsB;IACpC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC;IAE3C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;IAChC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,QAAQ,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;IACrC,IAAI,CAAC,QAAQ,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;IAE9E,MAAM,KAAK,GAAG;QACZ,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,iBAAiB,EAAE;QAChD,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE;QACpC,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE;QACxC,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,EAAE;QAChD,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,UAAU,EAAE;QAC/C,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;KACrC,CAAC;IAEF,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACvB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACtB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAE3B,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAE1D,OAAO;QACL,IAAI,EAAE,OAAO;QACb,GAAG,EAAE,MAAM;KACZ,CAAC;AACJ,CAAC"}
|
package/dist/daemon/docker.js
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Docker container management
|
|
3
|
-
*/
|
|
4
|
-
import Docker from 'dockerode';
|
|
5
|
-
import { log } from './logger.js';
|
|
6
|
-
import { DeploymentType } from '../shared/types.js';
|
|
7
|
-
const docker = new Docker();
|
|
8
|
-
const DEFAULT_SERVER_PORT = 8088;
|
|
9
|
-
export class DockerManager {
|
|
10
|
-
constructor() {
|
|
11
|
-
this.containerId = null;
|
|
12
|
-
this.containerName = null;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Run container from pre-built image
|
|
16
|
-
*/
|
|
17
|
-
async runFromImage(props) {
|
|
18
|
-
const { githubUsername, githubToken, image } = props;
|
|
19
|
-
try {
|
|
20
|
-
log.info('Pulling Docker image', { image });
|
|
21
|
-
// Prepare authentication for private registries
|
|
22
|
-
const pullOptions = {};
|
|
23
|
-
if (githubToken && githubUsername && image.includes('ghcr.io')) {
|
|
24
|
-
log.info('Using GitHub authentication for private registry', {
|
|
25
|
-
username: githubUsername,
|
|
26
|
-
});
|
|
27
|
-
// GitHub Container Registry authentication using OAuth token
|
|
28
|
-
pullOptions.authconfig = {
|
|
29
|
-
username: githubUsername,
|
|
30
|
-
password: githubToken, // OAuth access token with read:packages scope
|
|
31
|
-
serveraddress: 'ghcr.io',
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
// Pull image with optional authentication
|
|
35
|
-
const stream = await docker.pull(image, pullOptions);
|
|
36
|
-
await new Promise((resolve, reject) => {
|
|
37
|
-
docker.modem.followProgress(stream, (err, res) => {
|
|
38
|
-
if (err) {
|
|
39
|
-
log.error('Image pull failed', { error: err, image });
|
|
40
|
-
reject(err);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
resolve(res);
|
|
44
|
-
}
|
|
45
|
-
}, (event) => {
|
|
46
|
-
// Log pull progress events
|
|
47
|
-
if (event.error) {
|
|
48
|
-
log.error('Pull event error', { event });
|
|
49
|
-
}
|
|
50
|
-
else if (event.status) {
|
|
51
|
-
log.debug('Pull progress', {
|
|
52
|
-
status: event.status,
|
|
53
|
-
id: event.id,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
log.info('Image pulled successfully', { image });
|
|
59
|
-
// Run container
|
|
60
|
-
await this.runContainer(props);
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
log.error('Failed to run from image', error);
|
|
64
|
-
throw error;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Run Docker container with proper configuration
|
|
69
|
-
*/
|
|
70
|
-
async runContainer(props) {
|
|
71
|
-
const { id, image, deploymentType, domain, env, githubToken, bucketName, bucketHostPath, } = props;
|
|
72
|
-
this.containerName = `anycloud-${id}`;
|
|
73
|
-
// Stop and remove existing container if it exists
|
|
74
|
-
await this.stopContainer();
|
|
75
|
-
log.info('Starting container', {
|
|
76
|
-
image,
|
|
77
|
-
containerName: this.containerName,
|
|
78
|
-
deploymentType,
|
|
79
|
-
bucketName,
|
|
80
|
-
bucketHostPath,
|
|
81
|
-
});
|
|
82
|
-
// Only expose ports for app deployments (not for jobs)
|
|
83
|
-
const isJob = deploymentType === DeploymentType.Job;
|
|
84
|
-
const exposedPorts = isJob ? {} : { [`${DEFAULT_SERVER_PORT}/tcp`]: {} };
|
|
85
|
-
const portBindings = isJob
|
|
86
|
-
? {}
|
|
87
|
-
: {
|
|
88
|
-
[`${DEFAULT_SERVER_PORT}/tcp`]: [
|
|
89
|
-
{ HostPort: String(DEFAULT_SERVER_PORT) },
|
|
90
|
-
],
|
|
91
|
-
};
|
|
92
|
-
// Configure bucket volume mount if bucketName is provided
|
|
93
|
-
// Bind mount provides zero-copy access to bucket sync directory
|
|
94
|
-
let binds = [];
|
|
95
|
-
if (bucketName) {
|
|
96
|
-
// Use bucketHostPath if provided, otherwise read from file (written by VM startup script),
|
|
97
|
-
// otherwise fall back to /mnt for backward compatibility
|
|
98
|
-
let hostPath = bucketHostPath;
|
|
99
|
-
if (!hostPath) {
|
|
100
|
-
try {
|
|
101
|
-
const fs = await import('fs');
|
|
102
|
-
hostPath = fs
|
|
103
|
-
.readFileSync('/etc/anycloud-bucket-path', 'utf-8')
|
|
104
|
-
.trim();
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
// File doesn't exist, use default
|
|
108
|
-
hostPath = `/mnt/${bucketName}`;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
binds = [`${hostPath}:/mnt/${bucketName}`];
|
|
112
|
-
}
|
|
113
|
-
// Merge user-provided env vars with system env vars
|
|
114
|
-
// System env vars take priority
|
|
115
|
-
const userEnvArray = env
|
|
116
|
-
? Object.entries(env).map(([key, value]) => `${key}=${value}`)
|
|
117
|
-
: [];
|
|
118
|
-
const systemEnvArray = [
|
|
119
|
-
`ID=${id}`,
|
|
120
|
-
`DOMAIN=${domain}`,
|
|
121
|
-
`PORT=${DEFAULT_SERVER_PORT}`,
|
|
122
|
-
`DEPLOYMENT_TYPE=${deploymentType}`,
|
|
123
|
-
`GITHUB_TOKEN=${githubToken}`,
|
|
124
|
-
];
|
|
125
|
-
const mergedEnv = [...userEnvArray, ...systemEnvArray];
|
|
126
|
-
const container = await docker.createContainer({
|
|
127
|
-
Image: image,
|
|
128
|
-
name: this.containerName,
|
|
129
|
-
Env: mergedEnv,
|
|
130
|
-
ExposedPorts: exposedPorts,
|
|
131
|
-
HostConfig: {
|
|
132
|
-
Binds: binds,
|
|
133
|
-
PortBindings: portBindings,
|
|
134
|
-
RestartPolicy: {
|
|
135
|
-
Name: 'unless-stopped',
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
this.containerId = container.id;
|
|
140
|
-
await container.start();
|
|
141
|
-
log.info('Container started', { containerId: this.containerId });
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Check if container is running and healthy
|
|
145
|
-
*/
|
|
146
|
-
async isContainerHealthy() {
|
|
147
|
-
if (!this.containerId) {
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
try {
|
|
151
|
-
const container = docker.getContainer(this.containerId);
|
|
152
|
-
const info = await container.inspect();
|
|
153
|
-
return info.State.Running === true;
|
|
154
|
-
}
|
|
155
|
-
catch (error) {
|
|
156
|
-
log.debug('Container health check failed', error);
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Stop and remove container
|
|
162
|
-
*/
|
|
163
|
-
async stopContainer() {
|
|
164
|
-
if (!this.containerName) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
try {
|
|
168
|
-
const container = docker.getContainer(this.containerName);
|
|
169
|
-
const info = await container.inspect();
|
|
170
|
-
if (info.State.Running) {
|
|
171
|
-
log.info('Stopping existing container', {
|
|
172
|
-
containerName: this.containerName,
|
|
173
|
-
});
|
|
174
|
-
await container.stop();
|
|
175
|
-
}
|
|
176
|
-
await container.remove();
|
|
177
|
-
log.info('Container removed', { containerName: this.containerName });
|
|
178
|
-
}
|
|
179
|
-
catch (error) {
|
|
180
|
-
// Container doesn't exist, which is fine
|
|
181
|
-
if (error.statusCode !== 404) {
|
|
182
|
-
log.warn('Failed to stop container', error);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Get container logs
|
|
188
|
-
*/
|
|
189
|
-
async getLogs(tail = 100) {
|
|
190
|
-
if (!this.containerId) {
|
|
191
|
-
return '';
|
|
192
|
-
}
|
|
193
|
-
try {
|
|
194
|
-
const container = docker.getContainer(this.containerId);
|
|
195
|
-
const logs = await container.logs({
|
|
196
|
-
stdout: true,
|
|
197
|
-
stderr: true,
|
|
198
|
-
tail,
|
|
199
|
-
});
|
|
200
|
-
return logs.toString();
|
|
201
|
-
}
|
|
202
|
-
catch (error) {
|
|
203
|
-
log.error('Failed to get container logs', error);
|
|
204
|
-
return '';
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
//# sourceMappingURL=docker.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/daemon/docker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAe,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEjE,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;AAC5B,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAEjC,MAAM,OAAO,aAAa;IAA1B;QACU,gBAAW,GAAkB,IAAI,CAAC;QAClC,kBAAa,GAAkB,IAAI,CAAC;IAkO9C,CAAC;IAhOC;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,KAAkB;QACnC,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;QACrD,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAE5C,gDAAgD;YAChD,MAAM,WAAW,GAAQ,EAAE,CAAC;YAC5B,IAAI,WAAW,IAAI,cAAc,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC/D,GAAG,CAAC,IAAI,CAAC,kDAAkD,EAAE;oBAC3D,QAAQ,EAAE,cAAc;iBACzB,CAAC,CAAC;gBAEH,6DAA6D;gBAC7D,WAAW,CAAC,UAAU,GAAG;oBACvB,QAAQ,EAAE,cAAc;oBACxB,QAAQ,EAAE,WAAW,EAAE,8CAA8C;oBACrE,aAAa,EAAE,SAAS;iBACzB,CAAC;YACJ,CAAC;YAED,0CAA0C;YAC1C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;YACrD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACpC,MAAM,CAAC,KAAK,CAAC,cAAc,CACzB,MAAM,EACN,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;oBACX,IAAI,GAAG,EAAE,CAAC;wBACR,GAAG,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;wBACtD,MAAM,CAAC,GAAG,CAAC,CAAC;oBACd,CAAC;yBAAM,CAAC;wBACN,OAAO,CAAC,GAAG,CAAC,CAAC;oBACf,CAAC;gBACH,CAAC,EACD,CAAC,KAAU,EAAE,EAAE;oBACb,2BAA2B;oBAC3B,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;wBAChB,GAAG,CAAC,KAAK,CAAC,kBAAkB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;oBAC3C,CAAC;yBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;wBACxB,GAAG,CAAC,KAAK,CAAC,eAAe,EAAE;4BACzB,MAAM,EAAE,KAAK,CAAC,MAAM;4BACpB,EAAE,EAAE,KAAK,CAAC,EAAE;yBACb,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC,CACF,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAEjD,gBAAgB;YAChB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;YAC7C,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CAAC,KAAkB;QAC3C,MAAM,EACJ,EAAE,EACF,KAAK,EACL,cAAc,EACd,MAAM,EACN,GAAG,EACH,WAAW,EACX,UAAU,EACV,cAAc,GACf,GAAG,KAAK,CAAC;QACV,IAAI,CAAC,aAAa,GAAG,YAAY,EAAE,EAAE,CAAC;QAEtC,kDAAkD;QAClD,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAE3B,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE;YAC7B,KAAK;YACL,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,cAAc;YACd,UAAU;YACV,cAAc;SACf,CAAC,CAAC;QAEH,uDAAuD;QACvD,MAAM,KAAK,GAAG,cAAc,KAAK,cAAc,CAAC,GAAG,CAAC;QACpD,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,mBAAmB,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC;QACzE,MAAM,YAAY,GAAG,KAAK;YACxB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,CAAC,GAAG,mBAAmB,MAAM,CAAC,EAAE;oBAC9B,EAAE,QAAQ,EAAE,MAAM,CAAC,mBAAmB,CAAC,EAAE;iBAC1C;aACF,CAAC;QAEN,0DAA0D;QAC1D,gEAAgE;QAChE,IAAI,KAAK,GAAa,EAAE,CAAC;QACzB,IAAI,UAAU,EAAE,CAAC;YACf,2FAA2F;YAC3F,yDAAyD;YACzD,IAAI,QAAQ,GAAG,cAAc,CAAC;YAC9B,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,IAAI,CAAC;oBACH,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;oBAC9B,QAAQ,GAAG,EAAE;yBACV,YAAY,CAAC,2BAA2B,EAAE,OAAO,CAAC;yBAClD,IAAI,EAAE,CAAC;gBACZ,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,kCAAkC;oBAClC,QAAQ,GAAG,QAAQ,UAAU,EAAE,CAAC;gBAClC,CAAC;YACH,CAAC;YACD,KAAK,GAAG,CAAC,GAAG,QAAQ,SAAS,UAAU,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,oDAAoD;QACpD,gCAAgC;QAChC,MAAM,YAAY,GAAG,GAAG;YACtB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC;YAC9D,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,cAAc,GAAG;YACrB,MAAM,EAAE,EAAE;YACV,UAAU,MAAM,EAAE;YAClB,QAAQ,mBAAmB,EAAE;YAC7B,mBAAmB,cAAc,EAAE;YACnC,gBAAgB,WAAW,EAAE;SAC9B,CAAC;QACF,MAAM,SAAS,GAAG,CAAC,GAAG,YAAY,EAAE,GAAG,cAAc,CAAC,CAAC;QAEvD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC;YAC7C,KAAK,EAAE,KAAK;YACZ,IAAI,EAAE,IAAI,CAAC,aAAa;YACxB,GAAG,EAAE,SAAS;YACd,YAAY,EAAE,YAAY;YAC1B,UAAU,EAAE;gBACV,KAAK,EAAE,KAAK;gBACZ,YAAY,EAAE,YAAY;gBAC1B,aAAa,EAAE;oBACb,IAAI,EAAE,gBAAgB;iBACvB;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC,EAAE,CAAC;QAEhC,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;QAExB,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACnE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB;QACtB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACxD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE,CAAC;YAEvC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;YAClD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa;QACjB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC1D,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE,CAAC;YAEvC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBACvB,GAAG,CAAC,IAAI,CAAC,6BAA6B,EAAE;oBACtC,aAAa,EAAE,IAAI,CAAC,aAAa;iBAClC,CAAC,CAAC;gBACH,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;YACzB,CAAC;YAED,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC;YACzB,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QACvE,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,yCAAyC;YACzC,IAAI,KAAK,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;gBAC7B,GAAG,CAAC,IAAI,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,GAAG;QACtB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACxD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC;gBAChC,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,IAAI;gBACZ,IAAI;aACL,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACjD,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
|
package/dist/daemon/server.js
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP server for daemon control port (4142)
|
|
3
|
-
*/
|
|
4
|
-
import express from 'express';
|
|
5
|
-
import https from 'https';
|
|
6
|
-
import { log } from './logger.js';
|
|
7
|
-
import { generateSelfSignedCert } from './cert.js';
|
|
8
|
-
import { DockerManager } from './docker.js';
|
|
9
|
-
import { MetricsCollector } from './metrics.js';
|
|
10
|
-
import { Coordinator } from './cluster.js';
|
|
11
|
-
export const CONTROL_PORT = 4142;
|
|
12
|
-
let dockerManager;
|
|
13
|
-
let metricsCollector;
|
|
14
|
-
let clusterCoordinator;
|
|
15
|
-
/**
|
|
16
|
-
* Middleware to authenticate requests with deployment secret
|
|
17
|
-
*/
|
|
18
|
-
function authenticateRequest(deploymentSecret) {
|
|
19
|
-
return (req, res, next) => {
|
|
20
|
-
const providedSecret = req.headers[deploymentSecret.toLowerCase()];
|
|
21
|
-
if (providedSecret !== 'true') {
|
|
22
|
-
log.warn('Unauthorized request', { path: req.path, ip: req.ip });
|
|
23
|
-
return res.status(401).json({ error: 'Unauthorized' });
|
|
24
|
-
}
|
|
25
|
-
next();
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Check rclone supervisor service status
|
|
30
|
-
*/
|
|
31
|
-
async function getRcloneStatus() {
|
|
32
|
-
const { exec } = await import('child_process');
|
|
33
|
-
const { promisify } = await import('util');
|
|
34
|
-
const execAsync = promisify(exec);
|
|
35
|
-
try {
|
|
36
|
-
// Check if rclone service exists and is running via supervisor
|
|
37
|
-
const { stdout: statusOutput } = await execAsync('supervisorctl status rclone-sync');
|
|
38
|
-
// Supervisor status format: "rclone-sync RUNNING pid 1234, uptime 0:01:23"
|
|
39
|
-
const isActive = statusOutput.includes('RUNNING');
|
|
40
|
-
const status = statusOutput.split(/\s+/)[1] || 'unknown'; // Extract state (RUNNING, STOPPED, etc.)
|
|
41
|
-
// Get recent logs if service exists
|
|
42
|
-
let logs = '';
|
|
43
|
-
try {
|
|
44
|
-
const { stdout: logOutput } = await execAsync('supervisorctl tail -100 rclone-sync');
|
|
45
|
-
logs = logOutput.trim();
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
// Logs not available
|
|
49
|
-
logs = 'Unable to retrieve logs';
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
active: isActive,
|
|
53
|
-
status,
|
|
54
|
-
logs,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
// Service doesn't exist or supervisor failed
|
|
59
|
-
return {
|
|
60
|
-
active: false,
|
|
61
|
-
status: 'not-found',
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
// ============================================================================
|
|
66
|
-
// Application Factory
|
|
67
|
-
// ============================================================================
|
|
68
|
-
/**
|
|
69
|
-
* Creates and configures the Express application.
|
|
70
|
-
* Exported for testability - doesn't start the server.
|
|
71
|
-
*/
|
|
72
|
-
export function createApp(deploymentSecret, conductorUrl) {
|
|
73
|
-
const app = express();
|
|
74
|
-
app.use(express.json({ limit: '100mb' }));
|
|
75
|
-
// Authentication middleware
|
|
76
|
-
app.use(authenticateRequest(deploymentSecret));
|
|
77
|
-
// Initialize managers
|
|
78
|
-
dockerManager = new DockerManager();
|
|
79
|
-
metricsCollector = new MetricsCollector();
|
|
80
|
-
clusterCoordinator = new Coordinator(deploymentSecret, conductorUrl);
|
|
81
|
-
/**
|
|
82
|
-
* GET /health
|
|
83
|
-
* Basic health check - is the daemon alive?
|
|
84
|
-
*/
|
|
85
|
-
app.get('/health', (req, res) => {
|
|
86
|
-
res.json({ status: 'healthy', timestamp: Date.now() });
|
|
87
|
-
});
|
|
88
|
-
/**
|
|
89
|
-
* GET /deploymentHealth
|
|
90
|
-
* Check if the application container is running and healthy
|
|
91
|
-
*/
|
|
92
|
-
app.get('/deploymentHealth', async (req, res) => {
|
|
93
|
-
// Always get rclone status, even if other checks fail
|
|
94
|
-
const rclone = await getRcloneStatus();
|
|
95
|
-
try {
|
|
96
|
-
const isHealthy = await dockerManager.isContainerHealthy();
|
|
97
|
-
const metrics = await metricsCollector.collect();
|
|
98
|
-
if (!isHealthy) {
|
|
99
|
-
return res.status(503).json({
|
|
100
|
-
status: 'unhealthy',
|
|
101
|
-
reason: 'Container not running',
|
|
102
|
-
rclone,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
res.json({
|
|
106
|
-
status: 'healthy',
|
|
107
|
-
metrics,
|
|
108
|
-
rclone,
|
|
109
|
-
timestamp: Date.now(),
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
log.error('Health check failed', error);
|
|
114
|
-
res.status(500).json({ status: 'error', error: String(error), rclone });
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
/**
|
|
118
|
-
* GET /logs
|
|
119
|
-
* Get Docker container logs
|
|
120
|
-
*/
|
|
121
|
-
app.get('/logs', async (req, res) => {
|
|
122
|
-
try {
|
|
123
|
-
const tail = req.query.tail ? parseInt(req.query.tail) : 100;
|
|
124
|
-
log.info('Fetching container logs', { tail });
|
|
125
|
-
const logsString = await dockerManager.getLogs(tail);
|
|
126
|
-
const response = { logs: logsString };
|
|
127
|
-
res.json(response);
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
log.error('Failed to fetch logs', error);
|
|
131
|
-
const errorResponse = {
|
|
132
|
-
error: error instanceof Error ? error.message : String(error),
|
|
133
|
-
};
|
|
134
|
-
res.status(500).json(errorResponse);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
/**
|
|
138
|
-
* POST /start
|
|
139
|
-
* Receive deployment configuration and start the container
|
|
140
|
-
*/
|
|
141
|
-
app.post('/start', async (req, res) => {
|
|
142
|
-
try {
|
|
143
|
-
const props = req.body;
|
|
144
|
-
log.info('Received start command', {
|
|
145
|
-
id: props.id,
|
|
146
|
-
image: props.image,
|
|
147
|
-
});
|
|
148
|
-
if (!props.image) {
|
|
149
|
-
throw new Error('Image parameter is required');
|
|
150
|
-
}
|
|
151
|
-
// Pull and run container from image
|
|
152
|
-
await dockerManager.runFromImage(props);
|
|
153
|
-
// Start deployment coordination (metrics, scaling, etc.)
|
|
154
|
-
await clusterCoordinator.start(props);
|
|
155
|
-
res.sendStatus(200);
|
|
156
|
-
}
|
|
157
|
-
catch (error) {
|
|
158
|
-
log.error('Failed to start', error);
|
|
159
|
-
const errorResponse = {
|
|
160
|
-
error: error instanceof Error ? error.message : String(error),
|
|
161
|
-
};
|
|
162
|
-
res.status(500).json(errorResponse);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
/**
|
|
166
|
-
* Error handler
|
|
167
|
-
*/
|
|
168
|
-
app.use((err, req, res, _next) => {
|
|
169
|
-
log.error('Unhandled error', err);
|
|
170
|
-
res.status(500).json({ error: err.message });
|
|
171
|
-
});
|
|
172
|
-
return app;
|
|
173
|
-
}
|
|
174
|
-
// ============================================================================
|
|
175
|
-
// Process-Level Error Handlers
|
|
176
|
-
// ============================================================================
|
|
177
|
-
/**
|
|
178
|
-
* Sets up process-level error handlers for graceful degradation.
|
|
179
|
-
* Exported for testability.
|
|
180
|
-
*/
|
|
181
|
-
export function setupProcessHandlers() {
|
|
182
|
-
// Process-level error handlers to prevent crashes
|
|
183
|
-
process.on('uncaughtException', (error) => {
|
|
184
|
-
log.error('Uncaught exception - shutting down gracefully', {
|
|
185
|
-
error: error.message,
|
|
186
|
-
stack: error.stack,
|
|
187
|
-
});
|
|
188
|
-
// For uncaught exceptions, we should exit as the process is in an unknown state
|
|
189
|
-
// Give time for logs to flush
|
|
190
|
-
setTimeout(() => {
|
|
191
|
-
process.exit(1);
|
|
192
|
-
}, 1000);
|
|
193
|
-
});
|
|
194
|
-
process.on('unhandledRejection', (reason) => {
|
|
195
|
-
log.warn('Unhandled promise rejection', {
|
|
196
|
-
reason: String(reason),
|
|
197
|
-
});
|
|
198
|
-
// Log but don't exit - might be a background task
|
|
199
|
-
// Note: Future Node.js versions will exit on unhandled rejections
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
// ============================================================================
|
|
203
|
-
// Cluster Coordinator Access
|
|
204
|
-
// ============================================================================
|
|
205
|
-
/**
|
|
206
|
-
* Get the cluster coordinator instance.
|
|
207
|
-
* Exported for shutdown sequence.
|
|
208
|
-
*/
|
|
209
|
-
export function getCoordinator() {
|
|
210
|
-
return clusterCoordinator;
|
|
211
|
-
}
|
|
212
|
-
// ============================================================================
|
|
213
|
-
// Server Startup
|
|
214
|
-
// ============================================================================
|
|
215
|
-
/**
|
|
216
|
-
* Start the daemon HTTPS server.
|
|
217
|
-
* Used by CLI for backwards compatibility.
|
|
218
|
-
*/
|
|
219
|
-
export async function startServer(deploymentSecret, conductorUrl) {
|
|
220
|
-
// Setup process handlers first
|
|
221
|
-
setupProcessHandlers();
|
|
222
|
-
const app = createApp(deploymentSecret, conductorUrl);
|
|
223
|
-
// Generate self-signed SSL certificate
|
|
224
|
-
const { cert, key } = generateSelfSignedCert();
|
|
225
|
-
// Start HTTPS server
|
|
226
|
-
const server = https.createServer({ cert, key }, app);
|
|
227
|
-
// Graceful shutdown handlers
|
|
228
|
-
const shutdown = async () => {
|
|
229
|
-
log.info('Shutdown signal received, shutting down gracefully');
|
|
230
|
-
// Stop coordinator intervals
|
|
231
|
-
const coordinator = getCoordinator();
|
|
232
|
-
if (coordinator) {
|
|
233
|
-
await coordinator.stop();
|
|
234
|
-
}
|
|
235
|
-
// Close server
|
|
236
|
-
server.close(() => {
|
|
237
|
-
log.info('HTTPS server closed');
|
|
238
|
-
process.exit(0);
|
|
239
|
-
});
|
|
240
|
-
// Force exit if graceful shutdown takes too long
|
|
241
|
-
setTimeout(() => {
|
|
242
|
-
log.error('Graceful shutdown timed out, forcing exit');
|
|
243
|
-
process.exit(1);
|
|
244
|
-
}, 10000);
|
|
245
|
-
};
|
|
246
|
-
process.on('SIGTERM', shutdown);
|
|
247
|
-
process.on('SIGINT', shutdown);
|
|
248
|
-
await new Promise((resolve) => {
|
|
249
|
-
server.listen(CONTROL_PORT, () => {
|
|
250
|
-
log.info(`Daemon control port listening on https://0.0.0.0:${CONTROL_PORT}`);
|
|
251
|
-
resolve();
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
//# sourceMappingURL=server.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/daemon/server.ts"],"names":[],"mappings":"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"}
|