rl-rock 1.2.4 → 1.2.6
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 +80 -2
- package/dist/index.d.mts +1082 -73
- package/dist/index.d.ts +1082 -73
- package/dist/index.js +1616 -448
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1602 -444
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -14
package/dist/index.js
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
3
5
|
var zod = require('zod');
|
|
4
|
-
var
|
|
6
|
+
var axios = require('axios');
|
|
5
7
|
var https = require('https');
|
|
6
8
|
var tsCaseConvert = require('ts-case-convert');
|
|
7
|
-
require('os');
|
|
8
|
-
var path = require('path');
|
|
9
9
|
var winston = require('winston');
|
|
10
|
-
var
|
|
10
|
+
var os = require('os');
|
|
11
11
|
var crypto = require('crypto');
|
|
12
|
-
var
|
|
12
|
+
var promises = require('fs/promises');
|
|
13
13
|
|
|
14
14
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
15
|
|
|
16
|
-
var
|
|
16
|
+
var axios__default = /*#__PURE__*/_interopDefault(axios);
|
|
17
17
|
var https__default = /*#__PURE__*/_interopDefault(https);
|
|
18
18
|
var winston__default = /*#__PURE__*/_interopDefault(winston);
|
|
19
19
|
|
|
20
|
+
// src/index.ts
|
|
21
|
+
|
|
20
22
|
// src/types/codes.ts
|
|
21
23
|
var Codes = /* @__PURE__ */ ((Codes2) => {
|
|
22
24
|
Codes2[Codes2["OK"] = 2e3] = "OK";
|
|
@@ -177,14 +179,6 @@ var RunMode = {
|
|
|
177
179
|
NORMAL: "normal",
|
|
178
180
|
NOHUP: "nohup"
|
|
179
181
|
};
|
|
180
|
-
var Constants = class {
|
|
181
|
-
static BASE_URL_PRODUCT = "";
|
|
182
|
-
static BASE_URL_ALIYUN = "";
|
|
183
|
-
static BASE_URL_INNER = "";
|
|
184
|
-
static BASE_URL_PRE = "";
|
|
185
|
-
static BASE_URL_LOCAL = "";
|
|
186
|
-
static REQUEST_TIMEOUT_SECONDS = 180;
|
|
187
|
-
};
|
|
188
182
|
var PID_PREFIX = "__ROCK_PID_START__";
|
|
189
183
|
var PID_SUFFIX = "__ROCK_PID_END__";
|
|
190
184
|
|
|
@@ -262,6 +256,10 @@ var objectToCamel = tsCaseConvert.objectToCamel;
|
|
|
262
256
|
var objectToSnake = tsCaseConvert.objectToSnake;
|
|
263
257
|
|
|
264
258
|
// src/utils/http.ts
|
|
259
|
+
var sharedHttpsAgent = new https__default.default.Agent({
|
|
260
|
+
rejectUnauthorized: true,
|
|
261
|
+
keepAlive: true
|
|
262
|
+
});
|
|
265
263
|
var HttpUtils = class {
|
|
266
264
|
static defaultTimeout = 3e5;
|
|
267
265
|
// 5 minutes
|
|
@@ -270,15 +268,13 @@ var HttpUtils = class {
|
|
|
270
268
|
* Create axios instance with default config
|
|
271
269
|
*/
|
|
272
270
|
static createClient(config) {
|
|
273
|
-
return
|
|
271
|
+
return axios__default.default.create({
|
|
274
272
|
timeout: config?.timeout ?? this.defaultTimeout,
|
|
275
273
|
headers: {
|
|
276
274
|
"Content-Type": "application/json",
|
|
277
275
|
...config?.headers
|
|
278
276
|
},
|
|
279
|
-
httpsAgent:
|
|
280
|
-
rejectUnauthorized: true
|
|
281
|
-
})
|
|
277
|
+
httpsAgent: sharedHttpsAgent
|
|
282
278
|
});
|
|
283
279
|
}
|
|
284
280
|
/**
|
|
@@ -307,7 +303,7 @@ var HttpUtils = class {
|
|
|
307
303
|
}
|
|
308
304
|
return httpResponse;
|
|
309
305
|
} catch (error) {
|
|
310
|
-
if (error instanceof
|
|
306
|
+
if (error instanceof axios.AxiosError) {
|
|
311
307
|
throw new Error(`Failed to POST ${url}: ${error.message}`);
|
|
312
308
|
}
|
|
313
309
|
throw error;
|
|
@@ -334,7 +330,7 @@ var HttpUtils = class {
|
|
|
334
330
|
}
|
|
335
331
|
return httpResponse;
|
|
336
332
|
} catch (error) {
|
|
337
|
-
if (error instanceof
|
|
333
|
+
if (error instanceof axios.AxiosError) {
|
|
338
334
|
throw new Error(`Failed to GET ${url}: ${error.message}`);
|
|
339
335
|
}
|
|
340
336
|
throw error;
|
|
@@ -394,12 +390,15 @@ var HttpUtils = class {
|
|
|
394
390
|
}
|
|
395
391
|
const client = this.createClient({
|
|
396
392
|
headers: {
|
|
397
|
-
...headers
|
|
398
|
-
"Content-Type": "multipart/form-data"
|
|
393
|
+
...headers
|
|
399
394
|
}
|
|
400
395
|
});
|
|
401
396
|
try {
|
|
402
|
-
const response = await client.post(url, formData
|
|
397
|
+
const response = await client.post(url, formData, {
|
|
398
|
+
headers: {
|
|
399
|
+
"Content-Type": null
|
|
400
|
+
}
|
|
401
|
+
});
|
|
403
402
|
const camelData = objectToCamel(response.data);
|
|
404
403
|
const httpResponse = {
|
|
405
404
|
status: camelData.status ?? "Success",
|
|
@@ -413,7 +412,7 @@ var HttpUtils = class {
|
|
|
413
412
|
}
|
|
414
413
|
return httpResponse;
|
|
415
414
|
} catch (error) {
|
|
416
|
-
if (error instanceof
|
|
415
|
+
if (error instanceof axios.AxiosError) {
|
|
417
416
|
throw new Error(`Failed to POST multipart ${url}: ${error.message}`);
|
|
418
417
|
}
|
|
419
418
|
throw error;
|
|
@@ -444,13 +443,21 @@ var HttpUtils = class {
|
|
|
444
443
|
return mimeTypes[ext ?? ""] ?? "application/octet-stream";
|
|
445
444
|
}
|
|
446
445
|
};
|
|
446
|
+
function extractNohupPid(output) {
|
|
447
|
+
const pattern = new RegExp(`${PID_PREFIX}(\\d+)${PID_SUFFIX}`);
|
|
448
|
+
const match = output.match(pattern);
|
|
449
|
+
if (match?.[1]) {
|
|
450
|
+
return parseInt(match[1], 10);
|
|
451
|
+
}
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
447
454
|
|
|
448
455
|
// src/utils/retry.ts
|
|
449
456
|
function retryAsync(fn, options = {}) {
|
|
450
457
|
const {
|
|
451
458
|
maxAttempts = 3,
|
|
452
459
|
delaySeconds = 1,
|
|
453
|
-
backoff =
|
|
460
|
+
backoff = 2,
|
|
454
461
|
jitter = false
|
|
455
462
|
} = options;
|
|
456
463
|
return retryAsyncImpl(fn, {
|
|
@@ -491,43 +498,12 @@ function withRetry(fn, options = {}) {
|
|
|
491
498
|
});
|
|
492
499
|
}
|
|
493
500
|
|
|
494
|
-
// src/utils/deprecated.ts
|
|
495
|
-
function deprecated(reason = "") {
|
|
496
|
-
return function(target, propertyKey, descriptor) {
|
|
497
|
-
const originalMethod = descriptor.value;
|
|
498
|
-
descriptor.value = function(...args) {
|
|
499
|
-
console.warn(
|
|
500
|
-
`${String(propertyKey)} is deprecated. ${reason}`
|
|
501
|
-
);
|
|
502
|
-
return originalMethod.apply(this, args);
|
|
503
|
-
};
|
|
504
|
-
return descriptor;
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
function deprecatedClass(reason = "") {
|
|
508
|
-
return function(constructor) {
|
|
509
|
-
return class extends constructor {
|
|
510
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
511
|
-
constructor(...args) {
|
|
512
|
-
console.warn(`${constructor.name} is deprecated. ${reason}`);
|
|
513
|
-
super(...args);
|
|
514
|
-
}
|
|
515
|
-
};
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
|
|
519
501
|
// src/utils/system.ts
|
|
520
502
|
function isNode() {
|
|
521
503
|
return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
522
504
|
}
|
|
523
|
-
function isBrowser() {
|
|
524
|
-
return typeof globalThis !== "undefined" && "window" in globalThis && typeof globalThis.window !== "undefined";
|
|
525
|
-
}
|
|
526
505
|
function getEnv(key, defaultValue) {
|
|
527
|
-
|
|
528
|
-
return process.env[key] ?? defaultValue;
|
|
529
|
-
}
|
|
530
|
-
return defaultValue;
|
|
506
|
+
return process.env[key] ?? defaultValue;
|
|
531
507
|
}
|
|
532
508
|
function getRequiredEnv(key) {
|
|
533
509
|
const value = getEnv(key);
|
|
@@ -537,10 +513,7 @@ function getRequiredEnv(key) {
|
|
|
537
513
|
return value;
|
|
538
514
|
}
|
|
539
515
|
function isEnvSet(key) {
|
|
540
|
-
|
|
541
|
-
return key in process.env;
|
|
542
|
-
}
|
|
543
|
-
return false;
|
|
516
|
+
return key in process.env;
|
|
544
517
|
}
|
|
545
518
|
var envVars = {
|
|
546
519
|
// Logging
|
|
@@ -553,23 +526,306 @@ var envVars = {
|
|
|
553
526
|
get ROCK_LOGGING_LEVEL() {
|
|
554
527
|
return getEnv("ROCK_LOGGING_LEVEL", "INFO");
|
|
555
528
|
},
|
|
529
|
+
// Service
|
|
530
|
+
get ROCK_SERVICE_STATUS_DIR() {
|
|
531
|
+
return getEnv("ROCK_SERVICE_STATUS_DIR", "/data/service_status");
|
|
532
|
+
},
|
|
533
|
+
get ROCK_SCHEDULER_STATUS_DIR() {
|
|
534
|
+
return getEnv("ROCK_SCHEDULER_STATUS_DIR", "/data/scheduler_status");
|
|
535
|
+
},
|
|
536
|
+
// Config
|
|
537
|
+
get ROCK_CONFIG() {
|
|
538
|
+
return getEnv("ROCK_CONFIG");
|
|
539
|
+
},
|
|
540
|
+
get ROCK_CONFIG_DIR_NAME() {
|
|
541
|
+
return getEnv("ROCK_CONFIG_DIR_NAME", "rock-conf");
|
|
542
|
+
},
|
|
556
543
|
// Base URLs
|
|
557
544
|
get ROCK_BASE_URL() {
|
|
558
545
|
return getEnv("ROCK_BASE_URL", "http://localhost:8080");
|
|
559
546
|
},
|
|
547
|
+
get ROCK_WORKER_ROCKLET_PORT() {
|
|
548
|
+
const val = getEnv("ROCK_WORKER_ROCKLET_PORT");
|
|
549
|
+
return val ? parseInt(val, 10) : void 0;
|
|
550
|
+
},
|
|
560
551
|
get ROCK_SANDBOX_STARTUP_TIMEOUT_SECONDS() {
|
|
561
552
|
return parseInt(getEnv("ROCK_SANDBOX_STARTUP_TIMEOUT_SECONDS", "180"), 10);
|
|
562
553
|
},
|
|
554
|
+
get ROCK_CODE_SANDBOX_BASE_URL() {
|
|
555
|
+
return getEnv("ROCK_CODE_SANDBOX_BASE_URL", "");
|
|
556
|
+
},
|
|
563
557
|
// EnvHub
|
|
564
558
|
get ROCK_ENVHUB_BASE_URL() {
|
|
565
559
|
return getEnv("ROCK_ENVHUB_BASE_URL", "http://localhost:8081");
|
|
566
560
|
},
|
|
561
|
+
get ROCK_ENVHUB_DEFAULT_DOCKER_IMAGE() {
|
|
562
|
+
return getEnv("ROCK_ENVHUB_DEFAULT_DOCKER_IMAGE", "python:3.11");
|
|
563
|
+
},
|
|
564
|
+
get ROCK_ENVHUB_DB_URL() {
|
|
565
|
+
return getEnv(
|
|
566
|
+
"ROCK_ENVHUB_DB_URL",
|
|
567
|
+
`sqlite:///${path.join(os.homedir(), ".rock", "rock_envs.db")}`
|
|
568
|
+
);
|
|
569
|
+
},
|
|
570
|
+
// Auto clear
|
|
571
|
+
get ROCK_DEFAULT_AUTO_CLEAR_TIME_MINUTES() {
|
|
572
|
+
return parseInt(getEnv("ROCK_DEFAULT_AUTO_CLEAR_TIME_MINUTES", "360"), 10);
|
|
573
|
+
},
|
|
574
|
+
// Ray
|
|
575
|
+
get ROCK_RAY_NAMESPACE() {
|
|
576
|
+
return getEnv("ROCK_RAY_NAMESPACE", "xrl-sandbox");
|
|
577
|
+
},
|
|
578
|
+
get ROCK_SANDBOX_EXPIRE_TIME_KEY() {
|
|
579
|
+
return getEnv("ROCK_SANDBOX_EXPIRE_TIME_KEY", "expire_time");
|
|
580
|
+
},
|
|
581
|
+
get ROCK_SANDBOX_AUTO_CLEAR_TIME_KEY() {
|
|
582
|
+
return getEnv("ROCK_SANDBOX_AUTO_CLEAR_TIME_KEY", "auto_clear_time");
|
|
583
|
+
},
|
|
584
|
+
// Timezone
|
|
585
|
+
get ROCK_TIME_ZONE() {
|
|
586
|
+
return getEnv("ROCK_TIME_ZONE", "Asia/Shanghai");
|
|
587
|
+
},
|
|
588
|
+
// OSS
|
|
589
|
+
get ROCK_OSS_ENABLE() {
|
|
590
|
+
return getEnv("ROCK_OSS_ENABLE", "false")?.toLowerCase() === "true";
|
|
591
|
+
},
|
|
592
|
+
get ROCK_OSS_BUCKET_ENDPOINT() {
|
|
593
|
+
return getEnv("ROCK_OSS_BUCKET_ENDPOINT");
|
|
594
|
+
},
|
|
595
|
+
get ROCK_OSS_BUCKET_NAME() {
|
|
596
|
+
return getEnv("ROCK_OSS_BUCKET_NAME");
|
|
597
|
+
},
|
|
598
|
+
get ROCK_OSS_BUCKET_REGION() {
|
|
599
|
+
return getEnv("ROCK_OSS_BUCKET_REGION");
|
|
600
|
+
},
|
|
601
|
+
// Pip
|
|
602
|
+
get ROCK_PIP_INDEX_URL() {
|
|
603
|
+
return getEnv("ROCK_PIP_INDEX_URL", "https://pypi.org/simple/");
|
|
604
|
+
},
|
|
605
|
+
// Monitor
|
|
606
|
+
get ROCK_MONITOR_ENABLE() {
|
|
607
|
+
return getEnv("ROCK_MONITOR_ENABLE", "false")?.toLowerCase() === "true";
|
|
608
|
+
},
|
|
609
|
+
// Project
|
|
610
|
+
get ROCK_PROJECT_ROOT() {
|
|
611
|
+
return getEnv("ROCK_PROJECT_ROOT", process.cwd());
|
|
612
|
+
},
|
|
613
|
+
get ROCK_WORKER_ENV_TYPE() {
|
|
614
|
+
return getEnv("ROCK_WORKER_ENV_TYPE", "local");
|
|
615
|
+
},
|
|
616
|
+
get ROCK_PYTHON_ENV_PATH() {
|
|
617
|
+
return getEnv("ROCK_PYTHON_ENV_PATH", process.cwd());
|
|
618
|
+
},
|
|
619
|
+
// Admin
|
|
620
|
+
get ROCK_ADMIN_ENV() {
|
|
621
|
+
return getEnv("ROCK_ADMIN_ENV", "dev");
|
|
622
|
+
},
|
|
623
|
+
get ROCK_ADMIN_ROLE() {
|
|
624
|
+
return getEnv("ROCK_ADMIN_ROLE", "write");
|
|
625
|
+
},
|
|
626
|
+
// CLI
|
|
627
|
+
get ROCK_CLI_LOAD_PATHS() {
|
|
628
|
+
return getEnv("ROCK_CLI_LOAD_PATHS", "");
|
|
629
|
+
},
|
|
630
|
+
get ROCK_CLI_DEFAULT_CONFIG_PATH() {
|
|
631
|
+
return getEnv("ROCK_CLI_DEFAULT_CONFIG_PATH", path.join(os.homedir(), ".rock", "config.ini"));
|
|
632
|
+
},
|
|
567
633
|
// Model Service
|
|
568
634
|
get ROCK_MODEL_SERVICE_DATA_DIR() {
|
|
569
635
|
return getEnv("ROCK_MODEL_SERVICE_DATA_DIR", "/data/logs");
|
|
570
|
-
}
|
|
636
|
+
},
|
|
637
|
+
get ROCK_MODEL_SERVICE_TRAJ_APPEND_MODE() {
|
|
638
|
+
return getEnv("ROCK_MODEL_SERVICE_TRAJ_APPEND_MODE", "false")?.toLowerCase() === "true";
|
|
639
|
+
},
|
|
640
|
+
// RuntimeEnv
|
|
641
|
+
get ROCK_RTENV_PYTHON_V31114_INSTALL_CMD() {
|
|
642
|
+
return getEnv(
|
|
643
|
+
"ROCK_RTENV_PYTHON_V31114_INSTALL_CMD",
|
|
644
|
+
"[ -f cpython31114.tar.gz ] && rm cpython31114.tar.gz; [ -d python ] && rm -rf python; wget -q -O cpython31114.tar.gz https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.11.14+20251120-x86_64-unknown-linux-gnu-install_only.tar.gz && tar -xzf cpython31114.tar.gz && mv python runtime-env"
|
|
645
|
+
);
|
|
646
|
+
},
|
|
647
|
+
get ROCK_RTENV_PYTHON_V31212_INSTALL_CMD() {
|
|
648
|
+
return getEnv(
|
|
649
|
+
"ROCK_RTENV_PYTHON_V31212_INSTALL_CMD",
|
|
650
|
+
"[ -f cpython-3.12.12.tar.gz ] && rm cpython-3.12.12.tar.gz; [ -d python ] && rm -rf python; wget -q -O cpython-3.12.12.tar.gz https://github.com/astral-sh/python-build-standalone/releases/download/20251217/cpython-3.12.12+20251217-x86_64-unknown-linux-gnu-install_only.tar.gz && tar -xzf cpython-3.12.12.tar.gz && mv python runtime-env"
|
|
651
|
+
);
|
|
652
|
+
},
|
|
653
|
+
get ROCK_RTENV_NODE_V22180_INSTALL_CMD() {
|
|
654
|
+
return getEnv(
|
|
655
|
+
"ROCK_RTENV_NODE_V22180_INSTALL_CMD",
|
|
656
|
+
"[ -f node.tar.xz ] && rm node.tar.xz; [ -d node ] && rm -rf node; wget -q -O node.tar.xz --tries=10 --waitretry=2 https://nodejs.org/dist/v22.18.0/node-v22.18.0-linux-x64.tar.xz && tar -xf node.tar.xz && mv node-v22.18.0-linux-x64 runtime-env"
|
|
657
|
+
);
|
|
658
|
+
},
|
|
659
|
+
// Agent
|
|
660
|
+
get ROCK_AGENT_PRE_INIT_BASH_CMD_LIST() {
|
|
661
|
+
const val = getEnv("ROCK_AGENT_PRE_INIT_BASH_CMD_LIST", "[]");
|
|
662
|
+
try {
|
|
663
|
+
return JSON.parse(val);
|
|
664
|
+
} catch {
|
|
665
|
+
return [];
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
get ROCK_AGENT_IFLOW_CLI_INSTALL_CMD() {
|
|
669
|
+
return getEnv("ROCK_AGENT_IFLOW_CLI_INSTALL_CMD", "npm i -g @iflow-ai/iflow-cli@latest");
|
|
670
|
+
},
|
|
671
|
+
get ROCK_MODEL_SERVICE_INSTALL_CMD() {
|
|
672
|
+
return getEnv("ROCK_MODEL_SERVICE_INSTALL_CMD", "pip install rl_rock[model-service]");
|
|
673
|
+
},
|
|
674
|
+
// Doccuum
|
|
675
|
+
get ROCK_DOCUUM_INSTALL_URL() {
|
|
676
|
+
return getEnv(
|
|
677
|
+
"ROCK_DOCUUM_INSTALL_URL",
|
|
678
|
+
"https://raw.githubusercontent.com/stepchowfun/docuum/main/install.sh"
|
|
679
|
+
);
|
|
680
|
+
},
|
|
681
|
+
// ========== Sandbox Defaults ==========
|
|
682
|
+
// Sandbox configuration defaults - allow users to override via environment variables
|
|
683
|
+
get ROCK_DEFAULT_IMAGE() {
|
|
684
|
+
return getEnv("ROCK_DEFAULT_IMAGE", "python:3.11");
|
|
685
|
+
},
|
|
686
|
+
get ROCK_DEFAULT_MEMORY() {
|
|
687
|
+
return getEnv("ROCK_DEFAULT_MEMORY", "8g");
|
|
688
|
+
},
|
|
689
|
+
get ROCK_DEFAULT_CPUS() {
|
|
690
|
+
return parseFloat(getEnv("ROCK_DEFAULT_CPUS", "2"));
|
|
691
|
+
},
|
|
692
|
+
get ROCK_DEFAULT_CLUSTER() {
|
|
693
|
+
return getEnv("ROCK_DEFAULT_CLUSTER", "zb");
|
|
694
|
+
},
|
|
695
|
+
get ROCK_DEFAULT_AUTO_CLEAR_SECONDS() {
|
|
696
|
+
return parseInt(getEnv("ROCK_DEFAULT_AUTO_CLEAR_SECONDS", "300"), 10);
|
|
697
|
+
},
|
|
698
|
+
// ========== SandboxGroup Defaults ==========
|
|
699
|
+
get ROCK_DEFAULT_GROUP_SIZE() {
|
|
700
|
+
return parseInt(getEnv("ROCK_DEFAULT_GROUP_SIZE", "2"), 10);
|
|
701
|
+
},
|
|
702
|
+
get ROCK_DEFAULT_START_CONCURRENCY() {
|
|
703
|
+
return parseInt(getEnv("ROCK_DEFAULT_START_CONCURRENCY", "2"), 10);
|
|
704
|
+
},
|
|
705
|
+
get ROCK_DEFAULT_START_RETRY_TIMES() {
|
|
706
|
+
return parseInt(getEnv("ROCK_DEFAULT_START_RETRY_TIMES", "3"), 10);
|
|
707
|
+
},
|
|
708
|
+
// ========== Client Timeouts (in seconds) ==========
|
|
709
|
+
get ROCK_DEFAULT_ARUN_TIMEOUT() {
|
|
710
|
+
return parseInt(getEnv("ROCK_DEFAULT_ARUN_TIMEOUT", "300"), 10);
|
|
711
|
+
},
|
|
712
|
+
get ROCK_DEFAULT_NOHUP_WAIT_TIMEOUT() {
|
|
713
|
+
return parseInt(getEnv("ROCK_DEFAULT_NOHUP_WAIT_TIMEOUT", "300"), 10);
|
|
714
|
+
},
|
|
715
|
+
get ROCK_DEFAULT_NOHUP_WAIT_INTERVAL() {
|
|
716
|
+
return parseInt(getEnv("ROCK_DEFAULT_NOHUP_WAIT_INTERVAL", "10"), 10);
|
|
717
|
+
},
|
|
718
|
+
get ROCK_DEFAULT_STATUS_CHECK_INTERVAL() {
|
|
719
|
+
return parseInt(getEnv("ROCK_DEFAULT_STATUS_CHECK_INTERVAL", "3"), 10);
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
var levels = {
|
|
723
|
+
error: 0,
|
|
724
|
+
warn: 1,
|
|
725
|
+
info: 2,
|
|
726
|
+
http: 3,
|
|
727
|
+
debug: 4
|
|
728
|
+
};
|
|
729
|
+
var colors = {
|
|
730
|
+
error: "red",
|
|
731
|
+
warn: "yellow",
|
|
732
|
+
info: "green",
|
|
733
|
+
http: "magenta",
|
|
734
|
+
debug: "cyan"
|
|
735
|
+
};
|
|
736
|
+
winston__default.default.addColors(colors);
|
|
737
|
+
var consoleFormat = winston__default.default.format.combine(
|
|
738
|
+
winston__default.default.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss.SSS" }),
|
|
739
|
+
winston__default.default.format.colorize({ all: true }),
|
|
740
|
+
winston__default.default.format.printf((info) => {
|
|
741
|
+
const { timestamp, level, message, ...meta } = info;
|
|
742
|
+
const metaStr = Object.keys(meta).length ? JSON.stringify(meta) : "";
|
|
743
|
+
return `${timestamp} ${level}: ${message} ${metaStr}`;
|
|
744
|
+
})
|
|
745
|
+
);
|
|
746
|
+
var fileFormat = winston__default.default.format.combine(
|
|
747
|
+
winston__default.default.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss.SSS" }),
|
|
748
|
+
winston__default.default.format.json()
|
|
749
|
+
);
|
|
750
|
+
function getLogLevel() {
|
|
751
|
+
const level = envVars.ROCK_LOGGING_LEVEL.toLowerCase();
|
|
752
|
+
if (level in levels) {
|
|
753
|
+
return level;
|
|
754
|
+
}
|
|
755
|
+
return "info";
|
|
756
|
+
}
|
|
757
|
+
var loggerContainer = new winston__default.default.Container();
|
|
758
|
+
function initLogger(name = "rock", fileName) {
|
|
759
|
+
if (loggerContainer.has(name)) {
|
|
760
|
+
return loggerContainer.get(name);
|
|
761
|
+
}
|
|
762
|
+
const transports = [];
|
|
763
|
+
const logLevel = getLogLevel();
|
|
764
|
+
const logPath = envVars.ROCK_LOGGING_PATH;
|
|
765
|
+
const logFileName = envVars.ROCK_LOGGING_FILE_NAME;
|
|
766
|
+
if (logPath) {
|
|
767
|
+
if (!fs.existsSync(logPath)) {
|
|
768
|
+
fs.mkdirSync(logPath, { recursive: true });
|
|
769
|
+
}
|
|
770
|
+
transports.push(
|
|
771
|
+
new winston__default.default.transports.File({
|
|
772
|
+
filename: path.join(logPath, logFileName),
|
|
773
|
+
format: fileFormat,
|
|
774
|
+
level: logLevel
|
|
775
|
+
})
|
|
776
|
+
);
|
|
777
|
+
} else {
|
|
778
|
+
transports.push(
|
|
779
|
+
new winston__default.default.transports.Console({
|
|
780
|
+
format: consoleFormat,
|
|
781
|
+
level: logLevel
|
|
782
|
+
})
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
const logger13 = loggerContainer.add(name, {
|
|
786
|
+
levels,
|
|
787
|
+
defaultMeta: { service: name },
|
|
788
|
+
transports
|
|
789
|
+
});
|
|
790
|
+
return logger13;
|
|
791
|
+
}
|
|
792
|
+
initLogger("rock");
|
|
571
793
|
|
|
572
|
-
// src/
|
|
794
|
+
// src/utils/deprecated.ts
|
|
795
|
+
var warnedKeys = /* @__PURE__ */ new Set();
|
|
796
|
+
function getLogger() {
|
|
797
|
+
return initLogger("rock.deprecated");
|
|
798
|
+
}
|
|
799
|
+
function warnOnce(key, message) {
|
|
800
|
+
if (warnedKeys.has(key)) {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
getLogger().warn(message);
|
|
804
|
+
warnedKeys.add(key);
|
|
805
|
+
}
|
|
806
|
+
function deprecated(reason = "") {
|
|
807
|
+
return function(target, propertyKey, descriptor) {
|
|
808
|
+
const originalMethod = descriptor.value;
|
|
809
|
+
const key = String(propertyKey);
|
|
810
|
+
descriptor.value = function(...args) {
|
|
811
|
+
warnOnce(key, `${key} is deprecated. ${reason}`);
|
|
812
|
+
return originalMethod.apply(this, args);
|
|
813
|
+
};
|
|
814
|
+
return descriptor;
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function deprecatedClass(reason = "") {
|
|
818
|
+
return function(constructor) {
|
|
819
|
+
const key = constructor.name;
|
|
820
|
+
return class extends constructor {
|
|
821
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
822
|
+
constructor(...args) {
|
|
823
|
+
warnOnce(key, `${key} is deprecated. ${reason}`);
|
|
824
|
+
super(...args);
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
};
|
|
828
|
+
}
|
|
573
829
|
var EnvHubClientConfigSchema = zod.z.object({
|
|
574
830
|
baseUrl: zod.z.string().default(envVars.ROCK_ENVHUB_BASE_URL)
|
|
575
831
|
});
|
|
@@ -698,104 +954,51 @@ var EnvHubClient = class {
|
|
|
698
954
|
}
|
|
699
955
|
}
|
|
700
956
|
};
|
|
701
|
-
var levels = {
|
|
702
|
-
error: 0,
|
|
703
|
-
warn: 1,
|
|
704
|
-
info: 2,
|
|
705
|
-
http: 3,
|
|
706
|
-
debug: 4
|
|
707
|
-
};
|
|
708
|
-
var colors = {
|
|
709
|
-
error: "red",
|
|
710
|
-
warn: "yellow",
|
|
711
|
-
info: "green",
|
|
712
|
-
http: "magenta",
|
|
713
|
-
debug: "cyan"
|
|
714
|
-
};
|
|
715
|
-
winston__default.default.addColors(colors);
|
|
716
|
-
var consoleFormat = winston__default.default.format.combine(
|
|
717
|
-
winston__default.default.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss.SSS" }),
|
|
718
|
-
winston__default.default.format.colorize({ all: true }),
|
|
719
|
-
winston__default.default.format.printf((info) => {
|
|
720
|
-
const { timestamp, level, message, ...meta } = info;
|
|
721
|
-
const metaStr = Object.keys(meta).length ? JSON.stringify(meta) : "";
|
|
722
|
-
return `${timestamp} ${level}: ${message} ${metaStr}`;
|
|
723
|
-
})
|
|
724
|
-
);
|
|
725
|
-
var fileFormat = winston__default.default.format.combine(
|
|
726
|
-
winston__default.default.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss.SSS" }),
|
|
727
|
-
winston__default.default.format.json()
|
|
728
|
-
);
|
|
729
|
-
function getLogLevel() {
|
|
730
|
-
const level = envVars.ROCK_LOGGING_LEVEL.toLowerCase();
|
|
731
|
-
if (level in levels) {
|
|
732
|
-
return level;
|
|
733
|
-
}
|
|
734
|
-
return "info";
|
|
735
|
-
}
|
|
736
|
-
var loggerCache = /* @__PURE__ */ new Map();
|
|
737
|
-
function initLogger(name = "rock", fileName) {
|
|
738
|
-
if (loggerCache.has(name)) {
|
|
739
|
-
return loggerCache.get(name);
|
|
740
|
-
}
|
|
741
|
-
const transports = [];
|
|
742
|
-
const logLevel = getLogLevel();
|
|
743
|
-
const logPath = envVars.ROCK_LOGGING_PATH;
|
|
744
|
-
const logFileName = envVars.ROCK_LOGGING_FILE_NAME;
|
|
745
|
-
if (logPath) {
|
|
746
|
-
if (!fs.existsSync(logPath)) {
|
|
747
|
-
fs.mkdirSync(logPath, { recursive: true });
|
|
748
|
-
}
|
|
749
|
-
transports.push(
|
|
750
|
-
new winston__default.default.transports.File({
|
|
751
|
-
filename: path.join(logPath, logFileName),
|
|
752
|
-
format: fileFormat,
|
|
753
|
-
level: logLevel
|
|
754
|
-
})
|
|
755
|
-
);
|
|
756
|
-
} else {
|
|
757
|
-
transports.push(
|
|
758
|
-
new winston__default.default.transports.Console({
|
|
759
|
-
format: consoleFormat,
|
|
760
|
-
level: logLevel
|
|
761
|
-
})
|
|
762
|
-
);
|
|
763
|
-
}
|
|
764
|
-
const logger10 = winston__default.default.createLogger({
|
|
765
|
-
levels,
|
|
766
|
-
defaultMeta: { service: name },
|
|
767
|
-
transports
|
|
768
|
-
});
|
|
769
|
-
loggerCache.set(name, logger10);
|
|
770
|
-
return logger10;
|
|
771
|
-
}
|
|
772
|
-
initLogger("rock");
|
|
773
957
|
|
|
774
958
|
// src/envs/rock_env.ts
|
|
775
959
|
var logger = initLogger("rock.envs");
|
|
776
|
-
var RockEnv = class {
|
|
960
|
+
var RockEnv = class _RockEnv {
|
|
777
961
|
envId;
|
|
778
962
|
sandboxId = null;
|
|
779
963
|
isClosed = false;
|
|
780
|
-
client;
|
|
781
964
|
constructor(config) {
|
|
782
965
|
this.envId = config.envId;
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Create and initialize a RockEnv instance
|
|
969
|
+
*
|
|
970
|
+
* @param config - Environment configuration
|
|
971
|
+
* @returns Initialized RockEnv instance
|
|
972
|
+
*/
|
|
973
|
+
static async create(config) {
|
|
974
|
+
const env = new _RockEnv(config);
|
|
788
975
|
try {
|
|
789
|
-
|
|
976
|
+
await env.initializeEnvironment();
|
|
790
977
|
} catch (e) {
|
|
791
978
|
throw new Error(`Failed to initialize environment: ${e}`);
|
|
792
979
|
}
|
|
980
|
+
return env;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Get the sandbox ID
|
|
984
|
+
*/
|
|
985
|
+
getSandboxId() {
|
|
986
|
+
return this.sandboxId;
|
|
793
987
|
}
|
|
794
988
|
/**
|
|
795
989
|
* Initialize environment instance
|
|
796
990
|
*/
|
|
797
|
-
initializeEnvironment() {
|
|
991
|
+
async initializeEnvironment() {
|
|
798
992
|
logger.debug(`Initializing environment: ${this.envId}`);
|
|
993
|
+
const response = await HttpUtils.post(
|
|
994
|
+
`${envVars.ROCK_BASE_URL}/apis/v1/envs/gem/make`,
|
|
995
|
+
{ "Content-Type": "application/json" },
|
|
996
|
+
{ envId: this.envId }
|
|
997
|
+
);
|
|
998
|
+
this.sandboxId = response.result?.sandboxId ?? null;
|
|
999
|
+
if (!this.sandboxId) {
|
|
1000
|
+
throw new Error("Failed to get environment instance ID");
|
|
1001
|
+
}
|
|
799
1002
|
}
|
|
800
1003
|
/**
|
|
801
1004
|
* Execute an action step
|
|
@@ -805,19 +1008,12 @@ var RockEnv = class {
|
|
|
805
1008
|
*/
|
|
806
1009
|
async step(action) {
|
|
807
1010
|
this.ensureNotClosed();
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
"/apis/v1/envs/gem/step",
|
|
815
|
-
params
|
|
816
|
-
);
|
|
817
|
-
return this.parseStepResult(response.data);
|
|
818
|
-
} catch (e) {
|
|
819
|
-
throw new Error(`Failed to execute step with action ${action}: ${e}`);
|
|
820
|
-
}
|
|
1011
|
+
const response = await HttpUtils.post(
|
|
1012
|
+
`${envVars.ROCK_BASE_URL}/apis/v1/envs/gem/step`,
|
|
1013
|
+
{ "Content-Type": "application/json" },
|
|
1014
|
+
{ sandboxId: this.sandboxId, action }
|
|
1015
|
+
);
|
|
1016
|
+
return this.parseStepResult(response.result);
|
|
821
1017
|
}
|
|
822
1018
|
/**
|
|
823
1019
|
* Reset environment to initial state
|
|
@@ -827,19 +1023,16 @@ var RockEnv = class {
|
|
|
827
1023
|
*/
|
|
828
1024
|
async reset(seed) {
|
|
829
1025
|
this.ensureNotClosed();
|
|
830
|
-
const params = {
|
|
1026
|
+
const params = { sandboxId: this.sandboxId };
|
|
831
1027
|
if (seed !== void 0) {
|
|
832
1028
|
params.seed = seed;
|
|
833
1029
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
} catch (e) {
|
|
841
|
-
throw new Error(`Failed to reset environment: ${e}`);
|
|
842
|
-
}
|
|
1030
|
+
const response = await HttpUtils.post(
|
|
1031
|
+
`${envVars.ROCK_BASE_URL}/apis/v1/envs/gem/reset`,
|
|
1032
|
+
{ "Content-Type": "application/json" },
|
|
1033
|
+
params
|
|
1034
|
+
);
|
|
1035
|
+
return this.parseResetResult(response.result);
|
|
843
1036
|
}
|
|
844
1037
|
/**
|
|
845
1038
|
* Close environment and clean up resources
|
|
@@ -849,9 +1042,11 @@ var RockEnv = class {
|
|
|
849
1042
|
return;
|
|
850
1043
|
}
|
|
851
1044
|
try {
|
|
852
|
-
await
|
|
853
|
-
|
|
854
|
-
|
|
1045
|
+
await HttpUtils.post(
|
|
1046
|
+
`${envVars.ROCK_BASE_URL}/apis/v1/envs/gem/close`,
|
|
1047
|
+
{ "Content-Type": "application/json" },
|
|
1048
|
+
{ sandboxId: this.sandboxId }
|
|
1049
|
+
);
|
|
855
1050
|
} catch (e) {
|
|
856
1051
|
throw new Error(`Failed to close environment: ${e}`);
|
|
857
1052
|
} finally {
|
|
@@ -863,6 +1058,9 @@ var RockEnv = class {
|
|
|
863
1058
|
* Parse step result from API response
|
|
864
1059
|
*/
|
|
865
1060
|
parseStepResult(data) {
|
|
1061
|
+
if (!data) {
|
|
1062
|
+
throw new Error("Invalid step result: no data");
|
|
1063
|
+
}
|
|
866
1064
|
return [
|
|
867
1065
|
data.observation,
|
|
868
1066
|
data.reward,
|
|
@@ -875,6 +1073,9 @@ var RockEnv = class {
|
|
|
875
1073
|
* Parse reset result from API response
|
|
876
1074
|
*/
|
|
877
1075
|
parseResetResult(data) {
|
|
1076
|
+
if (!data) {
|
|
1077
|
+
throw new Error("Invalid reset result: no data");
|
|
1078
|
+
}
|
|
878
1079
|
return [data.observation, data.info];
|
|
879
1080
|
}
|
|
880
1081
|
/**
|
|
@@ -888,30 +1089,30 @@ var RockEnv = class {
|
|
|
888
1089
|
};
|
|
889
1090
|
|
|
890
1091
|
// src/envs/registration.ts
|
|
891
|
-
function make(envId, options) {
|
|
892
|
-
return
|
|
1092
|
+
async function make(envId, options) {
|
|
1093
|
+
return RockEnv.create({ envId, ...options });
|
|
893
1094
|
}
|
|
894
1095
|
var BaseConfigSchema = zod.z.object({
|
|
895
|
-
baseUrl: zod.z.string().default(envVars.ROCK_BASE_URL),
|
|
1096
|
+
baseUrl: zod.z.string().default(() => envVars.ROCK_BASE_URL),
|
|
896
1097
|
xrlAuthorization: zod.z.string().optional(),
|
|
897
1098
|
extraHeaders: zod.z.record(zod.z.string()).default({})
|
|
898
1099
|
});
|
|
899
1100
|
var SandboxConfigSchema = BaseConfigSchema.extend({
|
|
900
|
-
image: zod.z.string().default(
|
|
901
|
-
autoClearSeconds: zod.z.number().default(
|
|
1101
|
+
image: zod.z.string().default(() => envVars.ROCK_DEFAULT_IMAGE),
|
|
1102
|
+
autoClearSeconds: zod.z.number().default(() => envVars.ROCK_DEFAULT_AUTO_CLEAR_SECONDS),
|
|
902
1103
|
routeKey: zod.z.string().optional(),
|
|
903
|
-
startupTimeout: zod.z.number().default(envVars.ROCK_SANDBOX_STARTUP_TIMEOUT_SECONDS),
|
|
904
|
-
memory: zod.z.string().default(
|
|
905
|
-
cpus: zod.z.number().default(
|
|
1104
|
+
startupTimeout: zod.z.number().default(() => envVars.ROCK_SANDBOX_STARTUP_TIMEOUT_SECONDS),
|
|
1105
|
+
memory: zod.z.string().default(() => envVars.ROCK_DEFAULT_MEMORY),
|
|
1106
|
+
cpus: zod.z.number().default(() => envVars.ROCK_DEFAULT_CPUS),
|
|
906
1107
|
userId: zod.z.string().optional(),
|
|
907
1108
|
experimentId: zod.z.string().optional(),
|
|
908
|
-
cluster: zod.z.string().default(
|
|
1109
|
+
cluster: zod.z.string().default(() => envVars.ROCK_DEFAULT_CLUSTER),
|
|
909
1110
|
namespace: zod.z.string().optional()
|
|
910
1111
|
});
|
|
911
1112
|
var SandboxGroupConfigSchema = SandboxConfigSchema.extend({
|
|
912
|
-
size: zod.z.number().default(
|
|
913
|
-
startConcurrency: zod.z.number().default(
|
|
914
|
-
startRetryTimes: zod.z.number().default(
|
|
1113
|
+
size: zod.z.number().default(() => envVars.ROCK_DEFAULT_GROUP_SIZE),
|
|
1114
|
+
startConcurrency: zod.z.number().default(() => envVars.ROCK_DEFAULT_START_CONCURRENCY),
|
|
1115
|
+
startRetryTimes: zod.z.number().default(() => envVars.ROCK_DEFAULT_START_RETRY_TIMES)
|
|
915
1116
|
});
|
|
916
1117
|
function createSandboxConfig(config) {
|
|
917
1118
|
return SandboxConfigSchema.parse(config ?? {});
|
|
@@ -984,8 +1185,100 @@ var Deploy = class {
|
|
|
984
1185
|
}
|
|
985
1186
|
};
|
|
986
1187
|
|
|
1188
|
+
// src/utils/shell.ts
|
|
1189
|
+
function shellQuote(str) {
|
|
1190
|
+
if (str === "") {
|
|
1191
|
+
return "''";
|
|
1192
|
+
}
|
|
1193
|
+
return `'${str.replace(/'/g, "'\\''")}'`;
|
|
1194
|
+
}
|
|
1195
|
+
function validateUrl(url, allowedProtocols = ["http:", "https:"]) {
|
|
1196
|
+
try {
|
|
1197
|
+
const parsed = new URL(url);
|
|
1198
|
+
if (!allowedProtocols.includes(parsed.protocol)) {
|
|
1199
|
+
throw new Error(
|
|
1200
|
+
`Protocol ${parsed.protocol} is not allowed. Allowed: ${allowedProtocols.join(", ")}`
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
return url.replace(/\/$/, "");
|
|
1204
|
+
} catch (e) {
|
|
1205
|
+
if (e instanceof Error && e.message.includes("Protocol")) {
|
|
1206
|
+
throw e;
|
|
1207
|
+
}
|
|
1208
|
+
throw new Error(`Invalid URL: ${url}. ${e instanceof Error ? e.message : String(e)}`);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
function validateIpAddress(ip) {
|
|
1212
|
+
const pattern = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
1213
|
+
if (!pattern.test(ip)) {
|
|
1214
|
+
throw new Error(`Invalid IP address format: ${ip}. Expected format: x.x.x.x`);
|
|
1215
|
+
}
|
|
1216
|
+
const octets = ip.split(".");
|
|
1217
|
+
for (const octet of octets) {
|
|
1218
|
+
const value = parseInt(octet, 10);
|
|
1219
|
+
if (value < 0 || value > 255) {
|
|
1220
|
+
throw new Error(`Invalid IP address: ${ip}. Each octet must be 0-255.`);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
return ip;
|
|
1224
|
+
}
|
|
1225
|
+
function validatePath(path) {
|
|
1226
|
+
if (!path.startsWith("/")) {
|
|
1227
|
+
throw new Error(`Path must be absolute: ${path}`);
|
|
1228
|
+
}
|
|
1229
|
+
if (path.includes("..")) {
|
|
1230
|
+
throw new Error(`Path cannot contain ..: ${path}`);
|
|
1231
|
+
}
|
|
1232
|
+
if (/[`$(){};|&<>()]/.test(path)) {
|
|
1233
|
+
throw new Error(`Path contains forbidden characters: ${path}`);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
function validateUsername(username) {
|
|
1237
|
+
if (username.length === 0) {
|
|
1238
|
+
throw new Error("Username cannot be empty");
|
|
1239
|
+
}
|
|
1240
|
+
if (username.startsWith("-")) {
|
|
1241
|
+
throw new Error(`Username starting with dash is not allowed: ${username}`);
|
|
1242
|
+
}
|
|
1243
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(username)) {
|
|
1244
|
+
throw new Error(`Invalid username format: ${username}. Only alphanumeric, underscore, and hyphen allowed.`);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
function validateChmodMode(mode) {
|
|
1248
|
+
if (mode.length === 0) {
|
|
1249
|
+
throw new Error("Mode cannot be empty");
|
|
1250
|
+
}
|
|
1251
|
+
if (/^[0-7]{3,4}$/.test(mode)) {
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
if (/^[ugoa]*[+-=][rwxXstugo]*([,][ugoa]*[+-=][rwxXstugo]*)*$/.test(mode)) {
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
throw new Error(`Invalid chmod mode: ${mode}. Expected octal (e.g., 755) or symbolic (e.g., u+x)`);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
987
1260
|
// src/sandbox/file_system.ts
|
|
988
1261
|
var logger3 = initLogger("rock.sandbox.fs");
|
|
1262
|
+
async function createTarGz(sourceDir, outputPath) {
|
|
1263
|
+
const { spawn } = await import('child_process');
|
|
1264
|
+
return new Promise((resolve3, reject) => {
|
|
1265
|
+
const tar = spawn("tar", ["-czf", outputPath, "-C", sourceDir, "."]);
|
|
1266
|
+
let stderr = "";
|
|
1267
|
+
tar.stderr.on("data", (data) => {
|
|
1268
|
+
stderr += data.toString();
|
|
1269
|
+
});
|
|
1270
|
+
tar.on("close", (code) => {
|
|
1271
|
+
if (code === 0) {
|
|
1272
|
+
resolve3();
|
|
1273
|
+
} else {
|
|
1274
|
+
reject(new Error(`tar command failed with code ${code}: ${stderr}`));
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
tar.on("error", (err) => {
|
|
1278
|
+
reject(err);
|
|
1279
|
+
});
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
989
1282
|
var FileSystem = class {
|
|
990
1283
|
sandbox;
|
|
991
1284
|
constructor(sandbox) {
|
|
@@ -1001,6 +1294,10 @@ var LinuxFileSystem = class extends FileSystem {
|
|
|
1001
1294
|
if (!paths || paths.length === 0) {
|
|
1002
1295
|
throw new Error("paths is empty");
|
|
1003
1296
|
}
|
|
1297
|
+
validateUsername(remoteUser);
|
|
1298
|
+
for (const p of paths) {
|
|
1299
|
+
validatePath(p);
|
|
1300
|
+
}
|
|
1004
1301
|
const command = ["chown"];
|
|
1005
1302
|
if (recursive) {
|
|
1006
1303
|
command.push("-R");
|
|
@@ -1018,6 +1315,10 @@ var LinuxFileSystem = class extends FileSystem {
|
|
|
1018
1315
|
if (!paths || paths.length === 0) {
|
|
1019
1316
|
throw new Error("paths is empty");
|
|
1020
1317
|
}
|
|
1318
|
+
validateChmodMode(mode);
|
|
1319
|
+
for (const p of paths) {
|
|
1320
|
+
validatePath(p);
|
|
1321
|
+
}
|
|
1021
1322
|
const command = ["chmod"];
|
|
1022
1323
|
if (recursive) {
|
|
1023
1324
|
command.push("-R");
|
|
@@ -1030,27 +1331,128 @@ var LinuxFileSystem = class extends FileSystem {
|
|
|
1030
1331
|
}
|
|
1031
1332
|
return { success: true, message: JSON.stringify(response) };
|
|
1032
1333
|
}
|
|
1033
|
-
async uploadDir(sourceDir, targetDir,
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1334
|
+
async uploadDir(sourceDir, targetDir, extractTimeout = 600) {
|
|
1335
|
+
let localTarPath = null;
|
|
1336
|
+
let remoteTarPath = null;
|
|
1337
|
+
let session = null;
|
|
1338
|
+
try {
|
|
1339
|
+
const src = path.resolve(sourceDir);
|
|
1340
|
+
if (!fs.existsSync(src)) {
|
|
1341
|
+
return {
|
|
1342
|
+
output: "",
|
|
1343
|
+
exitCode: 1,
|
|
1344
|
+
failureReason: `source_dir not found: ${src}`,
|
|
1345
|
+
expectString: ""
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
const stats = fs.statSync(src);
|
|
1349
|
+
if (!stats.isDirectory()) {
|
|
1350
|
+
return {
|
|
1351
|
+
output: "",
|
|
1352
|
+
exitCode: 1,
|
|
1353
|
+
failureReason: `source_dir must be a directory: ${src}`,
|
|
1354
|
+
expectString: ""
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
try {
|
|
1358
|
+
validatePath(targetDir);
|
|
1359
|
+
} catch (e) {
|
|
1360
|
+
return {
|
|
1361
|
+
output: "",
|
|
1362
|
+
exitCode: 1,
|
|
1363
|
+
failureReason: e instanceof Error ? e.message : "Invalid target directory",
|
|
1364
|
+
expectString: ""
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
const ts = Date.now().toString();
|
|
1368
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "rock-upload-"));
|
|
1369
|
+
localTarPath = path.join(tmpDir, `rock_upload_${ts}.tar.gz`);
|
|
1370
|
+
remoteTarPath = `/tmp/rock_upload_${ts}.tar.gz`;
|
|
1371
|
+
session = `bash-${ts}`;
|
|
1372
|
+
logger3.info(`uploadDir: ${src} -> ${targetDir}`);
|
|
1373
|
+
await this.sandbox.createSession({ session, startupSource: [], envEnable: false });
|
|
1374
|
+
const checkResult = await this.sandbox.arun("command -v tar >/dev/null 2>&1", {
|
|
1375
|
+
session,
|
|
1376
|
+
mode: RunMode.NORMAL
|
|
1377
|
+
});
|
|
1378
|
+
if (checkResult.exitCode !== 0) {
|
|
1379
|
+
return {
|
|
1380
|
+
output: "",
|
|
1381
|
+
exitCode: 1,
|
|
1382
|
+
failureReason: "sandbox has no tar command; cannot extract tarball",
|
|
1383
|
+
expectString: ""
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
try {
|
|
1387
|
+
await createTarGz(src, localTarPath);
|
|
1388
|
+
} catch (e) {
|
|
1389
|
+
throw new Error(`tar pack failed: ${e}`);
|
|
1390
|
+
}
|
|
1391
|
+
const uploadResponse = await this.sandbox.upload({
|
|
1392
|
+
sourcePath: localTarPath,
|
|
1393
|
+
targetPath: remoteTarPath
|
|
1394
|
+
});
|
|
1395
|
+
if (!uploadResponse.success) {
|
|
1396
|
+
return {
|
|
1397
|
+
output: "",
|
|
1398
|
+
exitCode: 1,
|
|
1399
|
+
failureReason: `tar upload failed: ${uploadResponse.message}`,
|
|
1400
|
+
expectString: ""
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
const escapedTargetDir = shellQuote(targetDir);
|
|
1404
|
+
const escapedRemoteTar = shellQuote(remoteTarPath);
|
|
1405
|
+
const extractCmd = `rm -rf ${escapedTargetDir} && mkdir -p ${escapedTargetDir} && tar -xzf ${escapedRemoteTar} -C ${escapedTargetDir}`;
|
|
1406
|
+
const extractResult = await this.sandbox.arun(`bash -c ${shellQuote(extractCmd)}`, {
|
|
1407
|
+
session,
|
|
1408
|
+
mode: RunMode.NOHUP,
|
|
1409
|
+
waitTimeout: extractTimeout
|
|
1410
|
+
});
|
|
1411
|
+
if (extractResult.exitCode !== 0) {
|
|
1412
|
+
return {
|
|
1413
|
+
output: "",
|
|
1414
|
+
exitCode: 1,
|
|
1415
|
+
failureReason: `tar extract failed: ${extractResult.output}`,
|
|
1416
|
+
expectString: ""
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
try {
|
|
1420
|
+
await this.sandbox.execute({ command: ["rm", "-f", remoteTarPath], timeout: 30 });
|
|
1421
|
+
} catch {
|
|
1422
|
+
}
|
|
1423
|
+
return {
|
|
1424
|
+
output: `uploaded ${src} -> ${targetDir} via tar`,
|
|
1425
|
+
exitCode: 0,
|
|
1426
|
+
failureReason: "",
|
|
1427
|
+
expectString: ""
|
|
1428
|
+
};
|
|
1429
|
+
} catch (e) {
|
|
1430
|
+
return {
|
|
1431
|
+
output: "",
|
|
1432
|
+
exitCode: 1,
|
|
1433
|
+
failureReason: `upload_dir unexpected error: ${e}`,
|
|
1434
|
+
expectString: ""
|
|
1435
|
+
};
|
|
1436
|
+
} finally {
|
|
1437
|
+
try {
|
|
1438
|
+
if (localTarPath) {
|
|
1439
|
+
const tmpDir = path.basename(localTarPath).replace(/\.tar\.gz$/, "");
|
|
1440
|
+
fs.rmSync(path.join(os.tmpdir(), `rock-upload-${tmpDir.split("_").pop()}`), { recursive: true, force: true });
|
|
1441
|
+
}
|
|
1442
|
+
} catch {
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1041
1445
|
}
|
|
1042
1446
|
};
|
|
1043
1447
|
|
|
1044
|
-
// src/sandbox/
|
|
1448
|
+
// src/sandbox/network.ts
|
|
1449
|
+
var logger4 = initLogger("rock.sandbox.network");
|
|
1045
1450
|
var SpeedupType = /* @__PURE__ */ ((SpeedupType2) => {
|
|
1046
1451
|
SpeedupType2["APT"] = "apt";
|
|
1047
1452
|
SpeedupType2["PIP"] = "pip";
|
|
1048
1453
|
SpeedupType2["GITHUB"] = "github";
|
|
1049
1454
|
return SpeedupType2;
|
|
1050
1455
|
})(SpeedupType || {});
|
|
1051
|
-
|
|
1052
|
-
// src/sandbox/network.ts
|
|
1053
|
-
var logger4 = initLogger("rock.sandbox.network");
|
|
1054
1456
|
var Network = class {
|
|
1055
1457
|
sandbox;
|
|
1056
1458
|
constructor(sandbox) {
|
|
@@ -1069,45 +1471,179 @@ var Network = class {
|
|
|
1069
1471
|
logger4.info(
|
|
1070
1472
|
`[${sandboxId}] Configuring ${speedupType} speedup: ${speedupValue}`
|
|
1071
1473
|
);
|
|
1072
|
-
let
|
|
1474
|
+
let validatedValue;
|
|
1073
1475
|
switch (speedupType) {
|
|
1074
1476
|
case "apt" /* APT */:
|
|
1075
|
-
command = this.buildAptSpeedupCommand(speedupValue);
|
|
1076
|
-
break;
|
|
1077
1477
|
case "pip" /* PIP */:
|
|
1078
|
-
|
|
1478
|
+
validatedValue = validateUrl(speedupValue);
|
|
1079
1479
|
break;
|
|
1080
1480
|
case "github" /* GITHUB */:
|
|
1081
|
-
|
|
1481
|
+
validatedValue = validateIpAddress(speedupValue);
|
|
1082
1482
|
break;
|
|
1083
1483
|
default:
|
|
1084
1484
|
throw new Error(`Unsupported speedup type: ${speedupType}`);
|
|
1085
1485
|
}
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1486
|
+
const scriptContent = this.generateSpeedupScript(speedupType, validatedValue);
|
|
1487
|
+
const result = await this.sandbox.getProcess().executeScript({
|
|
1488
|
+
scriptContent,
|
|
1088
1489
|
waitTimeout: timeout
|
|
1089
1490
|
});
|
|
1090
1491
|
return result;
|
|
1091
1492
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1493
|
+
/**
|
|
1494
|
+
* Generate speedup script content based on type
|
|
1495
|
+
*/
|
|
1496
|
+
generateSpeedupScript(speedupType, value) {
|
|
1497
|
+
switch (speedupType) {
|
|
1498
|
+
case "apt" /* APT */:
|
|
1499
|
+
return this.buildAptSpeedupScript(value);
|
|
1500
|
+
case "pip" /* PIP */:
|
|
1501
|
+
return this.buildPipSpeedupScript(value);
|
|
1502
|
+
case "github" /* GITHUB */:
|
|
1503
|
+
return this.buildGithubSpeedupScript(value);
|
|
1504
|
+
default:
|
|
1505
|
+
throw new Error(`Unsupported speedup type: ${speedupType}`);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Build APT speedup script
|
|
1510
|
+
* Uses script file approach for safety
|
|
1511
|
+
*/
|
|
1512
|
+
buildAptSpeedupScript(mirrorUrl) {
|
|
1513
|
+
return `#!/bin/bash
|
|
1514
|
+
detect_system_and_version() {
|
|
1515
|
+
if [ -f /etc/debian_version ]; then
|
|
1516
|
+
. /etc/os-release
|
|
1517
|
+
if [ "$ID" = "ubuntu" ]; then
|
|
1518
|
+
echo "ubuntu:$VERSION_CODENAME"
|
|
1519
|
+
elif [ "$ID" = "debian" ]; then
|
|
1520
|
+
echo "debian:$VERSION_CODENAME"
|
|
1521
|
+
else
|
|
1522
|
+
echo "unknown:"
|
|
1523
|
+
fi
|
|
1524
|
+
else
|
|
1525
|
+
echo "unknown:"
|
|
1526
|
+
fi
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
SYSTEM_INFO=$(detect_system_and_version)
|
|
1530
|
+
SYSTEM=$(echo "$SYSTEM_INFO" | cut -d: -f1)
|
|
1531
|
+
CODENAME=$(echo "$SYSTEM_INFO" | cut -d: -f2)
|
|
1532
|
+
echo "System type: $SYSTEM, Version codename: $CODENAME"
|
|
1533
|
+
|
|
1534
|
+
# Backup original sources file
|
|
1535
|
+
if [ ! -f /etc/apt/sources.list.backup ]; then
|
|
1536
|
+
cp /etc/apt/sources.list /etc/apt/sources.list.backup
|
|
1537
|
+
fi
|
|
1538
|
+
|
|
1539
|
+
if [ "$SYSTEM" = "debian" ]; then
|
|
1540
|
+
if [ -z "$CODENAME" ]; then
|
|
1541
|
+
CODENAME="bookworm"
|
|
1542
|
+
fi
|
|
1543
|
+
cat > /etc/apt/sources.list <<EOF
|
|
1544
|
+
deb ${mirrorUrl}/debian/ \${CODENAME} main non-free non-free-firmware contrib
|
|
1545
|
+
deb ${mirrorUrl}/debian-security/ \${CODENAME}-security main
|
|
1546
|
+
deb ${mirrorUrl}/debian/ \${CODENAME}-updates main non-free non-free-firmware contrib
|
|
1098
1547
|
EOF
|
|
1099
|
-
|
|
1548
|
+
elif [ "$SYSTEM" = "ubuntu" ]; then
|
|
1549
|
+
if [ -z "$CODENAME" ]; then
|
|
1550
|
+
if [ -f /etc/os-release ]; then
|
|
1551
|
+
VERSION_ID=$(grep VERSION_ID /etc/os-release | cut -d'"' -f2)
|
|
1552
|
+
case "$VERSION_ID" in
|
|
1553
|
+
"24.04") CODENAME="noble" ;;
|
|
1554
|
+
"22.04") CODENAME="jammy" ;;
|
|
1555
|
+
"20.04") CODENAME="focal" ;;
|
|
1556
|
+
*) CODENAME="noble" ;;
|
|
1557
|
+
esac
|
|
1558
|
+
else
|
|
1559
|
+
CODENAME="noble"
|
|
1560
|
+
fi
|
|
1561
|
+
fi
|
|
1562
|
+
cat > /etc/apt/sources.list <<EOF
|
|
1563
|
+
deb ${mirrorUrl}/ubuntu/ $CODENAME main restricted universe multiverse
|
|
1564
|
+
deb ${mirrorUrl}/ubuntu/ $CODENAME-security main restricted universe multiverse
|
|
1565
|
+
deb ${mirrorUrl}/ubuntu/ $CODENAME-updates main restricted universe multiverse
|
|
1566
|
+
deb ${mirrorUrl}/ubuntu/ $CODENAME-backports main restricted universe multiverse
|
|
1567
|
+
EOF
|
|
1568
|
+
fi
|
|
1569
|
+
|
|
1570
|
+
# Clean up other source files
|
|
1571
|
+
rm -rf /etc/apt/sources.list.d
|
|
1572
|
+
|
|
1573
|
+
# Clean APT cache and update
|
|
1574
|
+
apt-get clean
|
|
1575
|
+
rm -rf /var/lib/apt/lists/*
|
|
1576
|
+
echo ">>> APT source configuration completed"
|
|
1577
|
+
`;
|
|
1100
1578
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1579
|
+
/**
|
|
1580
|
+
* Build PIP speedup script
|
|
1581
|
+
* Uses script file approach for safety
|
|
1582
|
+
*/
|
|
1583
|
+
buildPipSpeedupScript(mirrorUrl) {
|
|
1584
|
+
const parsed = new URL(mirrorUrl);
|
|
1585
|
+
const trustedHost = parsed.host;
|
|
1586
|
+
const indexUrl = `${mirrorUrl}/pypi/simple/`;
|
|
1587
|
+
return `#!/bin/bash
|
|
1588
|
+
echo ">>> Configuring pip source..."
|
|
1589
|
+
|
|
1590
|
+
# Configure for root user
|
|
1591
|
+
mkdir -p /root/.pip
|
|
1592
|
+
cat > /root/.pip/pip.conf <<EOF
|
|
1104
1593
|
[global]
|
|
1105
|
-
index-url = ${
|
|
1106
|
-
trusted-host = $
|
|
1107
|
-
|
|
1594
|
+
index-url = ${indexUrl}
|
|
1595
|
+
trusted-host = ${trustedHost}
|
|
1596
|
+
timeout = 120
|
|
1597
|
+
|
|
1598
|
+
[install]
|
|
1599
|
+
trusted-host = ${trustedHost}
|
|
1600
|
+
EOF
|
|
1601
|
+
|
|
1602
|
+
# Configure for other existing users
|
|
1603
|
+
for home_dir in /home/*; do
|
|
1604
|
+
if [ -d "$home_dir" ]; then
|
|
1605
|
+
username=$(basename "$home_dir")
|
|
1606
|
+
mkdir -p "$home_dir/.pip"
|
|
1607
|
+
cat > "$home_dir/.pip/pip.conf" <<EOF
|
|
1608
|
+
[global]
|
|
1609
|
+
index-url = ${indexUrl}
|
|
1610
|
+
trusted-host = ${trustedHost}
|
|
1611
|
+
timeout = 120
|
|
1612
|
+
|
|
1613
|
+
[install]
|
|
1614
|
+
trusted-host = ${trustedHost}
|
|
1615
|
+
EOF
|
|
1616
|
+
chown -R "$username:$username" "$home_dir/.pip" 2>/dev/null || true
|
|
1617
|
+
fi
|
|
1618
|
+
done
|
|
1619
|
+
|
|
1620
|
+
echo ">>> pip source configuration completed"
|
|
1621
|
+
`;
|
|
1108
1622
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1623
|
+
/**
|
|
1624
|
+
* Build GitHub speedup script
|
|
1625
|
+
* Uses script file approach for safety
|
|
1626
|
+
*/
|
|
1627
|
+
buildGithubSpeedupScript(ipAddress) {
|
|
1628
|
+
return `#!/bin/bash
|
|
1629
|
+
echo ">>> Configuring GitHub hosts for github.com acceleration..."
|
|
1630
|
+
|
|
1631
|
+
# Backup original hosts file if not already backed up
|
|
1632
|
+
if [ ! -f /etc/hosts.backup ]; then
|
|
1633
|
+
cp /etc/hosts /etc/hosts.backup
|
|
1634
|
+
echo "Hosts file backed up to /etc/hosts.backup"
|
|
1635
|
+
fi
|
|
1636
|
+
|
|
1637
|
+
# Remove existing github.com entry if any
|
|
1638
|
+
sed -i '/github.com$/d' /etc/hosts
|
|
1639
|
+
|
|
1640
|
+
# Add new github.com hosts entry
|
|
1641
|
+
echo "${ipAddress} github.com" | tee -a /etc/hosts
|
|
1642
|
+
|
|
1643
|
+
echo ">>> GitHub hosts configuration completed"
|
|
1644
|
+
echo "Current github.com entry in /etc/hosts:"
|
|
1645
|
+
grep 'github.com$' /etc/hosts || echo "No github.com entry found"
|
|
1646
|
+
`;
|
|
1111
1647
|
}
|
|
1112
1648
|
};
|
|
1113
1649
|
|
|
@@ -1299,16 +1835,6 @@ async function arunWithRetry(sandbox, cmd, session, mode, options = {}) {
|
|
|
1299
1835
|
}
|
|
1300
1836
|
throw lastError ?? new Error(`${errorMsg}: all attempts failed`);
|
|
1301
1837
|
}
|
|
1302
|
-
function extractNohupPid(output) {
|
|
1303
|
-
const PID_PREFIX2 = "__ROCK_PID_START__";
|
|
1304
|
-
const PID_SUFFIX2 = "__ROCK_PID_END__";
|
|
1305
|
-
const pattern = new RegExp(`${PID_PREFIX2}(\\d+)${PID_SUFFIX2}`);
|
|
1306
|
-
const match = output.match(pattern);
|
|
1307
|
-
if (match?.[1]) {
|
|
1308
|
-
return parseInt(match[1], 10);
|
|
1309
|
-
}
|
|
1310
|
-
return null;
|
|
1311
|
-
}
|
|
1312
1838
|
|
|
1313
1839
|
// src/sandbox/client.ts
|
|
1314
1840
|
var logger8 = initLogger("rock.sandbox");
|
|
@@ -1415,45 +1941,45 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1415
1941
|
};
|
|
1416
1942
|
logger8.debug(`Calling start_async API: ${url}`);
|
|
1417
1943
|
logger8.debug(`Request data: ${JSON.stringify(data)}`);
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
)
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
} catch (e) {
|
|
1449
|
-
logger8.debug(`Status check failed (will retry): ${e}`);
|
|
1944
|
+
const response = await HttpUtils.post(
|
|
1945
|
+
url,
|
|
1946
|
+
headers,
|
|
1947
|
+
data
|
|
1948
|
+
);
|
|
1949
|
+
logger8.debug(`Start sandbox response: ${JSON.stringify(response)}`);
|
|
1950
|
+
if (response.status !== "Success") {
|
|
1951
|
+
const code = response.result?.code;
|
|
1952
|
+
raiseForCode(code, `Failed to start sandbox: ${JSON.stringify(response)}`);
|
|
1953
|
+
throw new Error(`Failed to start sandbox: ${JSON.stringify(response)}`);
|
|
1954
|
+
}
|
|
1955
|
+
this.sandboxId = response.result?.sandboxId ?? null;
|
|
1956
|
+
this.hostName = response.result?.hostName ?? null;
|
|
1957
|
+
this.hostIp = response.result?.hostIp ?? null;
|
|
1958
|
+
logger8.info(`Sandbox ID: ${this.sandboxId}`);
|
|
1959
|
+
await sleep(2e3);
|
|
1960
|
+
const startTime = Date.now();
|
|
1961
|
+
const checkTimeout = 1e4;
|
|
1962
|
+
const checkInterval = 3e3;
|
|
1963
|
+
while (Date.now() - startTime < this.config.startupTimeout * 1e3) {
|
|
1964
|
+
try {
|
|
1965
|
+
logger8.info(`Checking status... (elapsed: ${Math.round((Date.now() - startTime) / 1e3)}s)`);
|
|
1966
|
+
const statusPromise = this.getStatus();
|
|
1967
|
+
const timeoutPromise = new Promise(
|
|
1968
|
+
(_, reject) => setTimeout(() => reject(new Error("Status check timeout")), checkTimeout)
|
|
1969
|
+
);
|
|
1970
|
+
const status = await Promise.race([statusPromise, timeoutPromise]);
|
|
1971
|
+
if (status && status.isAlive) {
|
|
1972
|
+
logger8.info("Sandbox is alive");
|
|
1973
|
+
return;
|
|
1450
1974
|
}
|
|
1451
|
-
|
|
1975
|
+
} catch (e) {
|
|
1976
|
+
logger8.debug(`Status check failed (will retry): ${e}`);
|
|
1452
1977
|
}
|
|
1453
|
-
|
|
1454
|
-
} catch (e) {
|
|
1455
|
-
throw new Error(`Failed to start sandbox: ${e}`);
|
|
1978
|
+
await sleep(checkInterval);
|
|
1456
1979
|
}
|
|
1980
|
+
throw new InternalServerRockError(
|
|
1981
|
+
`Failed to start sandbox within ${this.config.startupTimeout}s, sandbox: ${this.toString()}`
|
|
1982
|
+
);
|
|
1457
1983
|
}
|
|
1458
1984
|
async stop() {
|
|
1459
1985
|
if (!this.sandboxId) return;
|
|
@@ -1468,10 +1994,10 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1468
1994
|
async isAlive() {
|
|
1469
1995
|
try {
|
|
1470
1996
|
const status = await this.getStatus();
|
|
1471
|
-
return {
|
|
1997
|
+
return IsAliveResponseSchema.parse({
|
|
1472
1998
|
isAlive: status.isAlive,
|
|
1473
1999
|
message: status.hostName ?? ""
|
|
1474
|
-
};
|
|
2000
|
+
});
|
|
1475
2001
|
} catch (e) {
|
|
1476
2002
|
throw new Error(`Failed to get is alive: ${e}`);
|
|
1477
2003
|
}
|
|
@@ -1482,9 +2008,11 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1482
2008
|
const response = await HttpUtils.get(url, headers);
|
|
1483
2009
|
if (response.status !== "Success") {
|
|
1484
2010
|
const errorDetail = response.error ? `, error=${response.error}` : "";
|
|
2011
|
+
const code = response.result?.code;
|
|
2012
|
+
raiseForCode(code, `Failed to get status: status=${response.status}${errorDetail}, result=${JSON.stringify(response.result)}`);
|
|
1485
2013
|
throw new Error(`Failed to get status: status=${response.status}${errorDetail}, result=${JSON.stringify(response.result)}`);
|
|
1486
2014
|
}
|
|
1487
|
-
const result = response.result;
|
|
2015
|
+
const result = SandboxStatusResponseSchema.parse(response.result);
|
|
1488
2016
|
result.cluster = response.headers["x-rock-gateway-target-cluster"] || this.config.cluster || "N/A";
|
|
1489
2017
|
result.requestId = response.headers["x-request-id"] || response.headers["request-id"] || "N/A";
|
|
1490
2018
|
result.eagleeyeTraceid = response.headers["eagleeye-traceid"] || "N/A";
|
|
@@ -1501,20 +2029,18 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1501
2029
|
cwd: command.cwd,
|
|
1502
2030
|
env: command.env
|
|
1503
2031
|
};
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
}
|
|
1514
|
-
return response.result;
|
|
1515
|
-
} catch (e) {
|
|
1516
|
-
throw new Error(`Failed to execute command: ${e}`);
|
|
2032
|
+
const response = await HttpUtils.post(
|
|
2033
|
+
url,
|
|
2034
|
+
headers,
|
|
2035
|
+
data
|
|
2036
|
+
);
|
|
2037
|
+
if (response.status !== "Success") {
|
|
2038
|
+
const errorDetail = response.error ? `, error=${response.error}` : "";
|
|
2039
|
+
const code = response.result?.code;
|
|
2040
|
+
raiseForCode(code, `Failed to execute command: status=${response.status}${errorDetail}, result=${JSON.stringify(response.result)}`);
|
|
2041
|
+
throw new Error(`Failed to execute command: status=${response.status}${errorDetail}, result=${JSON.stringify(response.result)}`);
|
|
1517
2042
|
}
|
|
2043
|
+
return CommandResponseSchema.parse(response.result);
|
|
1518
2044
|
}
|
|
1519
2045
|
// Session management
|
|
1520
2046
|
async createSession(request) {
|
|
@@ -1524,20 +2050,18 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1524
2050
|
sandboxId: this.sandboxId,
|
|
1525
2051
|
...request
|
|
1526
2052
|
};
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
}
|
|
1537
|
-
return response.result;
|
|
1538
|
-
} catch (e) {
|
|
1539
|
-
throw new Error(`Failed to create session: ${e}`);
|
|
2053
|
+
const response = await HttpUtils.post(
|
|
2054
|
+
url,
|
|
2055
|
+
headers,
|
|
2056
|
+
data
|
|
2057
|
+
);
|
|
2058
|
+
if (response.status !== "Success") {
|
|
2059
|
+
const errorDetail = response.error ? `, error=${response.error}` : "";
|
|
2060
|
+
const code = response.result?.code;
|
|
2061
|
+
raiseForCode(code, `Failed to create session: status=${response.status}${errorDetail}, result=${JSON.stringify(response.result)}`);
|
|
2062
|
+
throw new Error(`Failed to create session: status=${response.status}${errorDetail}, result=${JSON.stringify(response.result)}`);
|
|
1540
2063
|
}
|
|
2064
|
+
return CreateSessionResponseSchema.parse(response.result);
|
|
1541
2065
|
}
|
|
1542
2066
|
async closeSession(request) {
|
|
1543
2067
|
const url = `${this.url}/close_session`;
|
|
@@ -1546,20 +2070,18 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1546
2070
|
sandboxId: this.sandboxId,
|
|
1547
2071
|
...request
|
|
1548
2072
|
};
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
}
|
|
1559
|
-
return response.result ?? { sessionType: "bash" };
|
|
1560
|
-
} catch (e) {
|
|
1561
|
-
throw new Error(`Failed to close session: ${e}`);
|
|
2073
|
+
const response = await HttpUtils.post(
|
|
2074
|
+
url,
|
|
2075
|
+
headers,
|
|
2076
|
+
data
|
|
2077
|
+
);
|
|
2078
|
+
if (response.status !== "Success") {
|
|
2079
|
+
const errorDetail = response.error ? `, error=${response.error}` : "";
|
|
2080
|
+
const code = response.result?.code;
|
|
2081
|
+
raiseForCode(code, `Failed to close session: status=${response.status}${errorDetail}, result=${JSON.stringify(response.result)}`);
|
|
2082
|
+
throw new Error(`Failed to close session: status=${response.status}${errorDetail}, result=${JSON.stringify(response.result)}`);
|
|
1562
2083
|
}
|
|
2084
|
+
return CloseSessionResponseSchema.parse(response.result ?? {});
|
|
1563
2085
|
}
|
|
1564
2086
|
// Run command in session
|
|
1565
2087
|
async arun(cmd, options = {}) {
|
|
@@ -1570,13 +2092,6 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1570
2092
|
} = options;
|
|
1571
2093
|
const sessionName = session ?? "default";
|
|
1572
2094
|
if (mode === "normal") {
|
|
1573
|
-
try {
|
|
1574
|
-
await this.createSession({ session: sessionName, startupSource: [], envEnable: false });
|
|
1575
|
-
} catch (e) {
|
|
1576
|
-
if (String(e).includes("already exists")) ; else {
|
|
1577
|
-
throw e;
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
2095
|
return this.runInSession({ command: cmd, session: sessionName, timeout });
|
|
1581
2096
|
}
|
|
1582
2097
|
return this.arunWithNohup(cmd, options);
|
|
@@ -1591,22 +2106,20 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1591
2106
|
sandboxId: this.sandboxId,
|
|
1592
2107
|
timeout: action.timeout
|
|
1593
2108
|
};
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
}
|
|
1606
|
-
return response.result;
|
|
1607
|
-
} catch (e) {
|
|
1608
|
-
throw new Error(`Failed to run in session: ${e}`);
|
|
2109
|
+
const timeoutMs = action.timeout ? action.timeout * 1e3 : void 0;
|
|
2110
|
+
const response = await HttpUtils.post(
|
|
2111
|
+
url,
|
|
2112
|
+
headers,
|
|
2113
|
+
data,
|
|
2114
|
+
timeoutMs
|
|
2115
|
+
);
|
|
2116
|
+
if (response.status !== "Success") {
|
|
2117
|
+
const errorDetail = response.error ? `, error=${response.error}` : "";
|
|
2118
|
+
const code = response.result?.code;
|
|
2119
|
+
raiseForCode(code, `Failed to run in session: status=${response.status}${errorDetail}, result=${JSON.stringify(response.result)}`);
|
|
2120
|
+
throw new Error(`Failed to run in session: status=${response.status}${errorDetail}, result=${JSON.stringify(response.result)}`);
|
|
1609
2121
|
}
|
|
2122
|
+
return ObservationSchema.parse(response.result);
|
|
1610
2123
|
}
|
|
1611
2124
|
async arunWithNohup(cmd, options) {
|
|
1612
2125
|
const {
|
|
@@ -1618,13 +2131,12 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1618
2131
|
outputFile
|
|
1619
2132
|
} = options;
|
|
1620
2133
|
const timestamp = Date.now();
|
|
1621
|
-
|
|
1622
|
-
|
|
2134
|
+
let tmpSession;
|
|
2135
|
+
if (session === void 0 || session === null) {
|
|
2136
|
+
tmpSession = `bash-${timestamp}`;
|
|
1623
2137
|
await this.createSession({ session: tmpSession, startupSource: [], envEnable: false });
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
throw e;
|
|
1627
|
-
}
|
|
2138
|
+
} else {
|
|
2139
|
+
tmpSession = session;
|
|
1628
2140
|
}
|
|
1629
2141
|
const tmpFile = outputFile ?? `/tmp/tmp_${timestamp}.out`;
|
|
1630
2142
|
const nohupCommand = `nohup ${cmd} < /dev/null > ${tmpFile} 2>&1 & echo __ROCK_PID_START__$!__ROCK_PID_END__;disown`;
|
|
@@ -1717,7 +2229,7 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1717
2229
|
headers,
|
|
1718
2230
|
data
|
|
1719
2231
|
);
|
|
1720
|
-
return
|
|
2232
|
+
return ReadFileResponseSchema.parse(response.result ?? {});
|
|
1721
2233
|
}
|
|
1722
2234
|
// Upload
|
|
1723
2235
|
async upload(request) {
|
|
@@ -1727,11 +2239,13 @@ var Sandbox = class extends AbstractSandbox {
|
|
|
1727
2239
|
const url = `${this.url}/upload`;
|
|
1728
2240
|
const headers = this.buildHeaders();
|
|
1729
2241
|
try {
|
|
1730
|
-
const fs = await import('fs');
|
|
1731
|
-
|
|
2242
|
+
const fs = await import('fs/promises');
|
|
2243
|
+
try {
|
|
2244
|
+
await fs.access(sourcePath);
|
|
2245
|
+
} catch {
|
|
1732
2246
|
return { success: false, message: `File not found: ${sourcePath}` };
|
|
1733
2247
|
}
|
|
1734
|
-
const fileBuffer = fs.
|
|
2248
|
+
const fileBuffer = await fs.readFile(sourcePath);
|
|
1735
2249
|
const fileName = sourcePath.split("/").pop() ?? "file";
|
|
1736
2250
|
const response = await HttpUtils.postMultipart(
|
|
1737
2251
|
url,
|
|
@@ -1801,6 +2315,7 @@ var SandboxGroup = class {
|
|
|
1801
2315
|
}
|
|
1802
2316
|
};
|
|
1803
2317
|
var logger9 = initLogger("rock.model.client");
|
|
2318
|
+
var DEFAULT_POLL_TIMEOUT = 60;
|
|
1804
2319
|
var REQUEST_START_MARKER = "__REQUEST_START__";
|
|
1805
2320
|
var REQUEST_END_MARKER = "__REQUEST_END__";
|
|
1806
2321
|
var RESPONSE_START_MARKER = "__RESPONSE_START__";
|
|
@@ -1838,7 +2353,7 @@ var ModelClient = class {
|
|
|
1838
2353
|
const content = this.constructResponse(lastResponse, index);
|
|
1839
2354
|
const lastResponseLine = await this.readLastResponseLine();
|
|
1840
2355
|
if (lastResponseLine === null) {
|
|
1841
|
-
this.appendResponse(content);
|
|
2356
|
+
await this.appendResponse(content);
|
|
1842
2357
|
return;
|
|
1843
2358
|
}
|
|
1844
2359
|
const { meta } = this.parseResponseLine(lastResponseLine);
|
|
@@ -1850,42 +2365,77 @@ var ModelClient = class {
|
|
|
1850
2365
|
logger9.debug(`response index ${index} already exists, skip`);
|
|
1851
2366
|
return;
|
|
1852
2367
|
}
|
|
1853
|
-
this.appendResponse(content);
|
|
2368
|
+
await this.appendResponse(content);
|
|
1854
2369
|
}
|
|
1855
2370
|
/**
|
|
1856
2371
|
* Pop request from log file
|
|
2372
|
+
*
|
|
2373
|
+
* @param index - The index of the request to pop
|
|
2374
|
+
* @param options - Optional configuration for timeout and cancellation
|
|
2375
|
+
* @returns The request JSON string or SESSION_END_MARKER
|
|
2376
|
+
* @throws Error if timeout expires or operation is aborted
|
|
1857
2377
|
*/
|
|
1858
|
-
|
|
1859
|
-
|
|
2378
|
+
async popRequest(index, options) {
|
|
2379
|
+
const timeout = options?.timeout ?? DEFAULT_POLL_TIMEOUT;
|
|
2380
|
+
const startTime = Date.now();
|
|
1860
2381
|
while (true) {
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
if (requestJson === SESSION_END_MARKER) {
|
|
1864
|
-
return SESSION_END_MARKER;
|
|
2382
|
+
if (options?.signal?.aborted) {
|
|
2383
|
+
throw new Error(`popRequest(index=${index}) aborted`);
|
|
1865
2384
|
}
|
|
1866
|
-
if (
|
|
1867
|
-
|
|
2385
|
+
if ((Date.now() - startTime) / 1e3 > timeout) {
|
|
2386
|
+
throw new Error(`popRequest timed out after ${timeout} seconds`);
|
|
2387
|
+
}
|
|
2388
|
+
try {
|
|
2389
|
+
const lastRequestLine = await this.readLastRequestLine();
|
|
2390
|
+
const { requestJson, meta } = this.parseRequestLine(lastRequestLine);
|
|
2391
|
+
if (requestJson === SESSION_END_MARKER) {
|
|
2392
|
+
return SESSION_END_MARKER;
|
|
2393
|
+
}
|
|
2394
|
+
if (meta.index === index) {
|
|
2395
|
+
return requestJson;
|
|
2396
|
+
}
|
|
2397
|
+
logger9.debug(`Last request is not the index ${index} we want, waiting...`);
|
|
2398
|
+
await sleep(1e3);
|
|
2399
|
+
} catch (e) {
|
|
2400
|
+
if (e instanceof Error) {
|
|
2401
|
+
if (e.message.includes("aborted")) {
|
|
2402
|
+
throw e;
|
|
2403
|
+
}
|
|
2404
|
+
if (e.message.includes("Invalid request line format")) {
|
|
2405
|
+
throw e;
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
logger9.debug(`Error reading request: ${e}, waiting...`);
|
|
2409
|
+
await sleep(1e3);
|
|
1868
2410
|
}
|
|
1869
|
-
logger9.debug(`Last request is not the index ${index} we want, waiting...`);
|
|
1870
|
-
await this.sleep(1e3);
|
|
1871
2411
|
}
|
|
1872
2412
|
}
|
|
1873
2413
|
/**
|
|
1874
2414
|
* Wait for first request
|
|
2415
|
+
*
|
|
2416
|
+
* @param options - Optional configuration for timeout and cancellation
|
|
2417
|
+
* @throws Error if timeout expires or operation is aborted
|
|
1875
2418
|
*/
|
|
1876
|
-
|
|
1877
|
-
|
|
2419
|
+
async waitForFirstRequest(options) {
|
|
2420
|
+
const timeout = options?.timeout ?? DEFAULT_POLL_TIMEOUT;
|
|
2421
|
+
const startTime = Date.now();
|
|
1878
2422
|
while (true) {
|
|
2423
|
+
if (options?.signal?.aborted) {
|
|
2424
|
+
throw new Error("waitForFirstRequest aborted");
|
|
2425
|
+
}
|
|
2426
|
+
if ((Date.now() - startTime) / 1e3 > timeout) {
|
|
2427
|
+
throw new Error(`waitForFirstRequest timed out after ${timeout} seconds`);
|
|
2428
|
+
}
|
|
1879
2429
|
if (!fs.existsSync(this.logFile)) {
|
|
1880
2430
|
logger9.debug(`Log file ${this.logFile} not found, waiting...`);
|
|
1881
|
-
await
|
|
2431
|
+
await sleep(1e3);
|
|
1882
2432
|
continue;
|
|
1883
2433
|
}
|
|
1884
|
-
const content =
|
|
2434
|
+
const content = await promises.readFile(this.logFile, "utf-8");
|
|
1885
2435
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
1886
2436
|
if (lines.length === 0) {
|
|
1887
2437
|
logger9.debug(`Log file ${this.logFile} is empty, waiting for the first request...`);
|
|
1888
|
-
await
|
|
2438
|
+
await sleep(1e3);
|
|
1889
2439
|
continue;
|
|
1890
2440
|
}
|
|
1891
2441
|
return;
|
|
@@ -1895,21 +2445,31 @@ var ModelClient = class {
|
|
|
1895
2445
|
if (lineContent.includes(SESSION_END_MARKER)) {
|
|
1896
2446
|
return { requestJson: SESSION_END_MARKER, meta: {} };
|
|
1897
2447
|
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2448
|
+
try {
|
|
2449
|
+
const parts = lineContent.split(REQUEST_END_MARKER);
|
|
2450
|
+
const metaJson = parts[1] ?? "";
|
|
2451
|
+
const requestJson = parts[0]?.split(REQUEST_START_MARKER)[1] ?? "";
|
|
2452
|
+
const meta = JSON.parse(metaJson);
|
|
2453
|
+
return { requestJson, meta };
|
|
2454
|
+
} catch (e) {
|
|
2455
|
+
logger9.error(`Failed to parse request line: ${lineContent}, error: ${e}`);
|
|
2456
|
+
throw new Error(`Invalid request line format: ${e}`);
|
|
2457
|
+
}
|
|
1903
2458
|
}
|
|
1904
2459
|
parseResponseLine(lineContent) {
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
2460
|
+
try {
|
|
2461
|
+
const parts = lineContent.split(RESPONSE_END_MARKER);
|
|
2462
|
+
const metaJson = parts[1] ?? "";
|
|
2463
|
+
const responseJson = parts[0]?.split(RESPONSE_START_MARKER)[1] ?? "";
|
|
2464
|
+
const meta = JSON.parse(metaJson);
|
|
2465
|
+
return { responseJson, meta };
|
|
2466
|
+
} catch (e) {
|
|
2467
|
+
logger9.error(`Failed to parse response line: ${lineContent}, error: ${e}`);
|
|
2468
|
+
throw new Error(`Invalid response line format: ${e}`);
|
|
2469
|
+
}
|
|
1910
2470
|
}
|
|
1911
2471
|
async readLastRequestLine() {
|
|
1912
|
-
const content =
|
|
2472
|
+
const content = await promises.readFile(this.logFile, "utf-8");
|
|
1913
2473
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
1914
2474
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
1915
2475
|
const line = lines[i];
|
|
@@ -1920,7 +2480,7 @@ var ModelClient = class {
|
|
|
1920
2480
|
throw new Error(`No request found in log file ${this.logFile}`);
|
|
1921
2481
|
}
|
|
1922
2482
|
async readLastResponseLine() {
|
|
1923
|
-
const content =
|
|
2483
|
+
const content = await promises.readFile(this.logFile, "utf-8");
|
|
1924
2484
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
1925
2485
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
1926
2486
|
const line = lines[i];
|
|
@@ -1930,8 +2490,8 @@ var ModelClient = class {
|
|
|
1930
2490
|
}
|
|
1931
2491
|
return null;
|
|
1932
2492
|
}
|
|
1933
|
-
appendResponse(content) {
|
|
1934
|
-
|
|
2493
|
+
async appendResponse(content) {
|
|
2494
|
+
await promises.appendFile(this.logFile, content);
|
|
1935
2495
|
}
|
|
1936
2496
|
constructResponse(lastResponse, index) {
|
|
1937
2497
|
const meta = {
|
|
@@ -1942,114 +2502,711 @@ var ModelClient = class {
|
|
|
1942
2502
|
return `${RESPONSE_START_MARKER}${lastResponse}${RESPONSE_END_MARKER}${metaJson}
|
|
1943
2503
|
`;
|
|
1944
2504
|
}
|
|
1945
|
-
sleep(ms) {
|
|
1946
|
-
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
1947
|
-
}
|
|
1948
2505
|
};
|
|
1949
|
-
var
|
|
1950
|
-
|
|
2506
|
+
var RuntimeEnvConfigSchema = zod.z.object({
|
|
2507
|
+
/** Runtime type discriminator. */
|
|
2508
|
+
type: zod.z.string(),
|
|
2509
|
+
/** Runtime version. Use 'default' for the default version of each runtime. */
|
|
2510
|
+
version: zod.z.string().default("default"),
|
|
2511
|
+
/** Environment variables for the runtime session. */
|
|
2512
|
+
env: zod.z.record(zod.z.string()).default({}),
|
|
2513
|
+
/** Timeout in seconds for installation commands. */
|
|
2514
|
+
installTimeout: zod.z.number().int().positive().default(600),
|
|
2515
|
+
/** Custom install command to run after init. */
|
|
2516
|
+
customInstallCmd: zod.z.string().nullable().default(null),
|
|
2517
|
+
/** Directory to create symlinks of executables. If null, no symlinks are created. */
|
|
2518
|
+
extraSymlinkDir: zod.z.string().nullable().default(null),
|
|
2519
|
+
/** List of executable names to symlink. Empty list means no symlinks. */
|
|
2520
|
+
extraSymlinkExecutables: zod.z.array(zod.z.string()).default([])
|
|
2521
|
+
});
|
|
2522
|
+
var logger10 = initLogger("rock.sandbox.runtime_env");
|
|
2523
|
+
function createRuntimeEnvId() {
|
|
2524
|
+
return crypto.randomUUID().slice(0, 8);
|
|
2525
|
+
}
|
|
2526
|
+
var RUNTIME_ENV_REGISTRY = {};
|
|
2527
|
+
var RuntimeEnv = class {
|
|
2528
|
+
/** Registry for subclasses */
|
|
2529
|
+
static registry = RUNTIME_ENV_REGISTRY;
|
|
2530
|
+
/** Sandbox instance */
|
|
2531
|
+
_sandbox;
|
|
2532
|
+
/** Configuration */
|
|
2533
|
+
_config;
|
|
2534
|
+
/** Version */
|
|
2535
|
+
_version;
|
|
2536
|
+
/** Environment variables */
|
|
2537
|
+
_env;
|
|
2538
|
+
/** Install timeout in seconds */
|
|
2539
|
+
_installTimeout;
|
|
2540
|
+
/** Custom install command */
|
|
2541
|
+
_customInstallCmd;
|
|
2542
|
+
/** Extra symlink directory */
|
|
2543
|
+
_extraSymlinkDir;
|
|
2544
|
+
/** Extra symlink executables */
|
|
2545
|
+
_extraSymlinkExecutables;
|
|
2546
|
+
/** Unique ID for this runtime env instance */
|
|
2547
|
+
_runtimeEnvId;
|
|
2548
|
+
/** Working directory */
|
|
2549
|
+
_workdir;
|
|
2550
|
+
/** Session name */
|
|
2551
|
+
_session;
|
|
2552
|
+
/** Whether the runtime has been initialized */
|
|
2553
|
+
_initialized = false;
|
|
2554
|
+
/** Whether the session is ready */
|
|
2555
|
+
_sessionReady = false;
|
|
2556
|
+
constructor(sandbox, config) {
|
|
2557
|
+
this._sandbox = sandbox;
|
|
2558
|
+
this._config = config;
|
|
2559
|
+
this._version = config.version;
|
|
2560
|
+
this._env = config.env;
|
|
2561
|
+
this._installTimeout = config.installTimeout;
|
|
2562
|
+
this._customInstallCmd = config.customInstallCmd;
|
|
2563
|
+
this._extraSymlinkDir = config.extraSymlinkDir;
|
|
2564
|
+
this._extraSymlinkExecutables = config.extraSymlinkExecutables;
|
|
2565
|
+
this._runtimeEnvId = createRuntimeEnvId();
|
|
2566
|
+
const versionStr = this._version || "default";
|
|
2567
|
+
this._workdir = `/tmp/rock-runtime-envs/${config.type}/${versionStr}/${this._runtimeEnvId}`;
|
|
2568
|
+
this._session = `runtime-env-${config.type}-${versionStr}-${this._runtimeEnvId}`;
|
|
2569
|
+
}
|
|
2570
|
+
/** Whether the runtime has been initialized */
|
|
2571
|
+
get initialized() {
|
|
2572
|
+
return this._initialized;
|
|
2573
|
+
}
|
|
2574
|
+
/** Unique ID for this runtime env instance */
|
|
2575
|
+
get runtimeEnvId() {
|
|
2576
|
+
return this._runtimeEnvId;
|
|
2577
|
+
}
|
|
2578
|
+
/** Working directory for this runtime env instance */
|
|
2579
|
+
get workdir() {
|
|
2580
|
+
return this._workdir;
|
|
2581
|
+
}
|
|
2582
|
+
/** Binary directory for this runtime env instance */
|
|
2583
|
+
get binDir() {
|
|
2584
|
+
return `${this._workdir}/runtime-env/bin`;
|
|
2585
|
+
}
|
|
1951
2586
|
/**
|
|
1952
|
-
*
|
|
2587
|
+
* Initialize the runtime environment.
|
|
2588
|
+
* This method performs installation and validation.
|
|
2589
|
+
* It is idempotent: calling multiple times only initializes once.
|
|
1953
2590
|
*/
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
if (requestTimeout) {
|
|
1981
|
-
cmd.push("--request-timeout", String(requestTimeout));
|
|
1982
|
-
}
|
|
1983
|
-
const command = cmd[0] ?? "node";
|
|
1984
|
-
this.process = child_process.spawn(command, cmd.slice(1), {
|
|
1985
|
-
cwd: path.resolve(__dirname, "server"),
|
|
1986
|
-
stdio: "inherit"
|
|
2591
|
+
async init() {
|
|
2592
|
+
if (this._initialized) {
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
await this._ensureSession();
|
|
2596
|
+
await this._ensureWorkdir();
|
|
2597
|
+
await this._installRuntime();
|
|
2598
|
+
await this._postInit();
|
|
2599
|
+
if (this._customInstallCmd) {
|
|
2600
|
+
await this._doCustomInstall();
|
|
2601
|
+
}
|
|
2602
|
+
await this._createSysPathLinks();
|
|
2603
|
+
this._initialized = true;
|
|
2604
|
+
}
|
|
2605
|
+
/**
|
|
2606
|
+
* Run a command under this runtime
|
|
2607
|
+
*/
|
|
2608
|
+
async run(cmd, options = {}) {
|
|
2609
|
+
const { mode = "nohup", waitTimeout = 600, errorMsg = "runtime env command failed" } = options;
|
|
2610
|
+
await this._ensureSession();
|
|
2611
|
+
const wrapped = this.wrappedCmd(cmd, true);
|
|
2612
|
+
logger10.debug(`[${this._sandbox.sandboxId}] RuntimeEnv run cmd: ${wrapped}`);
|
|
2613
|
+
const result = await this._sandbox.arun(wrapped, {
|
|
2614
|
+
session: this._session,
|
|
2615
|
+
mode,
|
|
2616
|
+
waitTimeout
|
|
1987
2617
|
});
|
|
1988
|
-
if (
|
|
1989
|
-
throw new Error(
|
|
2618
|
+
if (result.exitCode !== void 0 && result.exitCode !== 0) {
|
|
2619
|
+
throw new Error(`${errorMsg} with exit code: ${result.exitCode}, output: ${result.output}`);
|
|
1990
2620
|
}
|
|
1991
|
-
return
|
|
2621
|
+
return result;
|
|
1992
2622
|
}
|
|
1993
2623
|
/**
|
|
1994
|
-
*
|
|
2624
|
+
* Wrap command with PATH export.
|
|
2625
|
+
* Always wrap with bash -c to ensure it only affects current cmd.
|
|
2626
|
+
* Default prepend=true to give current runtime_env highest priority.
|
|
1995
2627
|
*/
|
|
1996
|
-
|
|
1997
|
-
const
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2628
|
+
wrappedCmd(cmd, prepend = true) {
|
|
2629
|
+
const binDir = this.binDir;
|
|
2630
|
+
let wrapped;
|
|
2631
|
+
if (prepend) {
|
|
2632
|
+
wrapped = `export PATH='${binDir}':$PATH && ${cmd}`;
|
|
2633
|
+
} else {
|
|
2634
|
+
wrapped = `export PATH=$PATH:'${binDir}' && ${cmd}`;
|
|
2635
|
+
}
|
|
2636
|
+
return `bash -c '${wrapped.replace(/'/g, `'"'"'`)}'`;
|
|
2637
|
+
}
|
|
2638
|
+
/**
|
|
2639
|
+
* Ensure runtime env session exists. Safe to call multiple times.
|
|
2640
|
+
*/
|
|
2641
|
+
async _ensureSession() {
|
|
2642
|
+
if (this._sessionReady) {
|
|
2643
|
+
return;
|
|
2644
|
+
}
|
|
2645
|
+
await this._sandbox.createSession({
|
|
2646
|
+
session: this._session,
|
|
2647
|
+
envEnable: true,
|
|
2648
|
+
env: this._env
|
|
2649
|
+
});
|
|
2650
|
+
this._sessionReady = true;
|
|
2651
|
+
}
|
|
2652
|
+
/**
|
|
2653
|
+
* Create workdir for runtime environment.
|
|
2654
|
+
*/
|
|
2655
|
+
async _ensureWorkdir() {
|
|
2656
|
+
const result = await this._sandbox.arun(`mkdir -p ${this._workdir}`, {
|
|
2657
|
+
session: this._session
|
|
2658
|
+
});
|
|
2659
|
+
if (result.exitCode !== void 0 && result.exitCode !== 0) {
|
|
2660
|
+
throw new Error(`Failed to create workdir: ${this._workdir}, exit_code: ${result.exitCode}`);
|
|
2002
2661
|
}
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2662
|
+
}
|
|
2663
|
+
/**
|
|
2664
|
+
* Install the runtime environment.
|
|
2665
|
+
*/
|
|
2666
|
+
async _installRuntime() {
|
|
2667
|
+
const installCmd = `cd '${this._workdir}' && ${this._getInstallCmd()}`;
|
|
2668
|
+
const wrappedCmd = `bash -c '${installCmd.replace(/'/g, `'"'"'`)}'`;
|
|
2669
|
+
const result = await this._sandbox.arun(wrappedCmd, {
|
|
2670
|
+
session: this._session,
|
|
2671
|
+
mode: "nohup",
|
|
2672
|
+
waitTimeout: this._installTimeout
|
|
2673
|
+
});
|
|
2674
|
+
if (result.exitCode !== void 0 && result.exitCode !== 0) {
|
|
2675
|
+
throw new Error(
|
|
2676
|
+
`${this.runtimeEnvType} runtime installation failed with exit code: ${result.exitCode}, output: ${result.output}`
|
|
2677
|
+
);
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
/**
|
|
2681
|
+
* Additional initialization after runtime installation.
|
|
2682
|
+
* Override in subclasses.
|
|
2683
|
+
*/
|
|
2684
|
+
async _postInit() {
|
|
2685
|
+
}
|
|
2686
|
+
/**
|
|
2687
|
+
* Execute custom install command after _postInit.
|
|
2688
|
+
*/
|
|
2689
|
+
async _doCustomInstall() {
|
|
2690
|
+
if (!this._customInstallCmd) {
|
|
2691
|
+
return;
|
|
2692
|
+
}
|
|
2693
|
+
await this.run(this._customInstallCmd, {
|
|
2694
|
+
waitTimeout: this._installTimeout,
|
|
2695
|
+
errorMsg: "custom_install_cmd failed"
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
2698
|
+
/**
|
|
2699
|
+
* Create symlinks in target directory for executables.
|
|
2700
|
+
*/
|
|
2701
|
+
async _createSysPathLinks() {
|
|
2702
|
+
if (this._extraSymlinkDir === null) {
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2705
|
+
if (this._extraSymlinkExecutables.length === 0) {
|
|
2706
|
+
return;
|
|
2707
|
+
}
|
|
2708
|
+
const links = this._extraSymlinkExecutables.map((exe) => `ln -sf '${this.binDir}/${exe}' '${this._extraSymlinkDir}/${exe}'`).join(" && ");
|
|
2709
|
+
await this.run(links);
|
|
2710
|
+
}
|
|
2711
|
+
};
|
|
2712
|
+
|
|
2713
|
+
// src/sandbox/runtime_env/python_runtime_env.ts
|
|
2714
|
+
var PythonRuntimeEnvConfigSchema = RuntimeEnvConfigSchema.extend({
|
|
2715
|
+
/** Runtime type discriminator. Must be 'python'. */
|
|
2716
|
+
type: zod.z.literal("python").default("python"),
|
|
2717
|
+
/** Python version. Use "default" for 3.11. */
|
|
2718
|
+
version: zod.z.enum(["3.11", "3.12", "default"]).default("default"),
|
|
2719
|
+
/**
|
|
2720
|
+
* Pip packages to install.
|
|
2721
|
+
* Can be:
|
|
2722
|
+
* - string[]: List of package names to install
|
|
2723
|
+
* - string: Path to requirements.txt file
|
|
2724
|
+
* - null: No packages to install
|
|
2725
|
+
*/
|
|
2726
|
+
pip: zod.z.union([zod.z.array(zod.z.string()), zod.z.string()]).nullable().default(null),
|
|
2727
|
+
/** Pip index URL for package installation. If set, will use this mirror. */
|
|
2728
|
+
pipIndexUrl: zod.z.string().nullable().default(null),
|
|
2729
|
+
/** List of Python executables to symlink. */
|
|
2730
|
+
extraSymlinkExecutables: zod.z.array(zod.z.string()).default(["python", "python3", "pip", "pip3"])
|
|
2731
|
+
});
|
|
2732
|
+
function getDefaultPipIndexUrl() {
|
|
2733
|
+
return envVars.ROCK_PIP_INDEX_URL;
|
|
2734
|
+
}
|
|
2735
|
+
var PythonRuntimeEnv = class extends RuntimeEnv {
|
|
2736
|
+
runtimeEnvType = "python";
|
|
2737
|
+
_pip;
|
|
2738
|
+
_pipIndexUrl;
|
|
2739
|
+
constructor(sandbox, config) {
|
|
2740
|
+
if (config.version !== "3.11" && config.version !== "3.12" && config.version !== "default") {
|
|
2741
|
+
throw new Error(
|
|
2742
|
+
`Unsupported Python version: ${config.version}. Supported versions: 3.11, 3.12, default`
|
|
2743
|
+
);
|
|
2744
|
+
}
|
|
2745
|
+
super(sandbox, config);
|
|
2746
|
+
this._pip = config.pip;
|
|
2747
|
+
this._pipIndexUrl = config.pipIndexUrl;
|
|
2748
|
+
}
|
|
2749
|
+
_getInstallCmd() {
|
|
2750
|
+
const version = this._version;
|
|
2751
|
+
if (version === "3.11" || version === "default") {
|
|
2752
|
+
return envVars.ROCK_RTENV_PYTHON_V31114_INSTALL_CMD;
|
|
2753
|
+
}
|
|
2754
|
+
return envVars.ROCK_RTENV_PYTHON_V31212_INSTALL_CMD;
|
|
2755
|
+
}
|
|
2756
|
+
async _postInit() {
|
|
2757
|
+
await this._validatePython();
|
|
2758
|
+
if (this._pipIndexUrl) {
|
|
2759
|
+
await this._configurePip();
|
|
2760
|
+
}
|
|
2761
|
+
if (this._pip) {
|
|
2762
|
+
await this._installPip();
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
async _validatePython() {
|
|
2766
|
+
await this.run("test -x python");
|
|
2767
|
+
}
|
|
2768
|
+
async _configurePip() {
|
|
2769
|
+
const escapedUrl = this._pipIndexUrl.replace(/'/g, "'\\''");
|
|
2770
|
+
await this.run(`pip config set global.index-url '${escapedUrl}'`);
|
|
2771
|
+
}
|
|
2772
|
+
async _installPip() {
|
|
2773
|
+
if (!this._pip) {
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
if (typeof this._pip === "string") {
|
|
2777
|
+
await this.run(`pip install -r '${this._pip.replace(/'/g, "'\\''")}'`);
|
|
2778
|
+
} else {
|
|
2779
|
+
const packages = this._pip.map((pkg) => `'${pkg.replace(/'/g, "'\\''")}'`).join(" ");
|
|
2780
|
+
await this.run(`pip install ${packages}`);
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
};
|
|
2784
|
+
var NODE_DEFAULT_VERSION = "22.18.0";
|
|
2785
|
+
var NodeRuntimeEnvConfigSchema = RuntimeEnvConfigSchema.extend({
|
|
2786
|
+
/** Runtime type discriminator. Must be 'node'. */
|
|
2787
|
+
type: zod.z.literal("node").default("node"),
|
|
2788
|
+
/** Node.js version. Use "default" for 22.18.0. */
|
|
2789
|
+
version: zod.z.enum(["22.18.0", "default"]).default("default"),
|
|
2790
|
+
/** NPM registry URL. If set, will run 'npm config set registry <url>' during init. */
|
|
2791
|
+
npmRegistry: zod.z.string().nullable().default(null),
|
|
2792
|
+
/** List of Node.js executables to symlink. */
|
|
2793
|
+
extraSymlinkExecutables: zod.z.array(zod.z.string()).default(["node", "npm", "npx"])
|
|
2794
|
+
});
|
|
2795
|
+
var NodeRuntimeEnv = class extends RuntimeEnv {
|
|
2796
|
+
runtimeEnvType = "node";
|
|
2797
|
+
_npmRegistry;
|
|
2798
|
+
constructor(sandbox, config) {
|
|
2799
|
+
if (config.version !== "default" && config.version !== NODE_DEFAULT_VERSION) {
|
|
2800
|
+
throw new Error(
|
|
2801
|
+
`Unsupported Node version: ${config.version}. Only ${NODE_DEFAULT_VERSION} is supported right now.`
|
|
2802
|
+
);
|
|
2803
|
+
}
|
|
2804
|
+
super(sandbox, config);
|
|
2805
|
+
this._npmRegistry = config.npmRegistry;
|
|
2806
|
+
}
|
|
2807
|
+
_getInstallCmd() {
|
|
2808
|
+
return envVars.ROCK_RTENV_NODE_V22180_INSTALL_CMD;
|
|
2809
|
+
}
|
|
2810
|
+
async _postInit() {
|
|
2811
|
+
await this._validateNode();
|
|
2812
|
+
if (this._npmRegistry) {
|
|
2813
|
+
await this._configureNpmRegistry();
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
async _validateNode() {
|
|
2817
|
+
await this.run("test -x node");
|
|
2818
|
+
}
|
|
2819
|
+
async _configureNpmRegistry() {
|
|
2820
|
+
const escapedRegistry = this._npmRegistry.replace(/'/g, "'\\''");
|
|
2821
|
+
await this.run(`npm config set registry '${escapedRegistry}'`);
|
|
2822
|
+
}
|
|
2823
|
+
};
|
|
2824
|
+
var logger11 = initLogger("rock.model_service");
|
|
2825
|
+
var ModelServiceConfigSchema = zod.z.object({
|
|
2826
|
+
/** Whether to enable model service */
|
|
2827
|
+
enabled: zod.z.boolean().default(false),
|
|
2828
|
+
/** Type of model service to start */
|
|
2829
|
+
type: zod.z.string().default("local"),
|
|
2830
|
+
/** Command to install model service package */
|
|
2831
|
+
installCmd: zod.z.string().default(envVars.ROCK_MODEL_SERVICE_INSTALL_CMD),
|
|
2832
|
+
/** Timeout for model service installation in seconds */
|
|
2833
|
+
installTimeout: zod.z.number().positive().default(300),
|
|
2834
|
+
/** Runtime environment configuration for the model service */
|
|
2835
|
+
runtimeEnvConfig: PythonRuntimeEnvConfigSchema.default({ type: "python", version: "default" }),
|
|
2836
|
+
/** Command to start model service with type placeholder */
|
|
2837
|
+
startCmd: zod.z.string().default("rock model-service start --type ${type}"),
|
|
2838
|
+
/** Command to stop model service */
|
|
2839
|
+
stopCmd: zod.z.string().default("rock model-service stop"),
|
|
2840
|
+
/** Command to create Rock config file */
|
|
2841
|
+
configIniCmd: zod.z.string().default("mkdir -p ~/.rock && touch ~/.rock/config.ini"),
|
|
2842
|
+
/** Command to watch agent with pid placeholder */
|
|
2843
|
+
watchAgentCmd: zod.z.string().default("rock model-service watch-agent --pid ${pid}"),
|
|
2844
|
+
/** Command to anti-call LLM with index and response_payload placeholders */
|
|
2845
|
+
antiCallLlmCmd: zod.z.string().default(
|
|
2846
|
+
"rock model-service anti-call-llm --index ${index} --response ${response_payload}"
|
|
2847
|
+
),
|
|
2848
|
+
/** Command to anti-call LLM with only index placeholder */
|
|
2849
|
+
antiCallLlmCmdNoResponse: zod.z.string().default("rock model-service anti-call-llm --index ${index}"),
|
|
2850
|
+
/** Path for logging directory */
|
|
2851
|
+
loggingPath: zod.z.string().default("/data/logs"),
|
|
2852
|
+
/** Name of the log file */
|
|
2853
|
+
loggingFileName: zod.z.string().default("model_service.log")
|
|
2854
|
+
});
|
|
2855
|
+
var ModelService = class {
|
|
2856
|
+
_sandbox;
|
|
2857
|
+
_config;
|
|
2858
|
+
_runtimeEnv = null;
|
|
2859
|
+
_isInstalled = false;
|
|
2860
|
+
_isStarted = false;
|
|
2861
|
+
constructor(sandbox, config) {
|
|
2862
|
+
this._sandbox = sandbox;
|
|
2863
|
+
this._config = config;
|
|
2864
|
+
}
|
|
2865
|
+
/** Whether the model service has been installed */
|
|
2866
|
+
get isInstalled() {
|
|
2867
|
+
return this._isInstalled;
|
|
2868
|
+
}
|
|
2869
|
+
/** Whether the model service has been started */
|
|
2870
|
+
get isStarted() {
|
|
2871
|
+
return this._isStarted;
|
|
2872
|
+
}
|
|
2873
|
+
/**
|
|
2874
|
+
* Install model service in the sandbox.
|
|
2875
|
+
*
|
|
2876
|
+
* Performs the following installation steps:
|
|
2877
|
+
* 1. Create and initialize Python runtime environment (via RuntimeEnv).
|
|
2878
|
+
* 2. Install model service package.
|
|
2879
|
+
*/
|
|
2880
|
+
async install() {
|
|
2881
|
+
if (this._config.runtimeEnvConfig.type !== "python") {
|
|
2882
|
+
throw new Error("ModelService requires a Python runtime environment");
|
|
2883
|
+
}
|
|
2884
|
+
const runtimeConfigResult = PythonRuntimeEnvConfigSchema.safeParse(this._config.runtimeEnvConfig);
|
|
2885
|
+
if (!runtimeConfigResult.success) {
|
|
2886
|
+
throw new Error(`Invalid runtime config: ${runtimeConfigResult.error.message}`);
|
|
2887
|
+
}
|
|
2888
|
+
this._runtimeEnv = new PythonRuntimeEnv(
|
|
2889
|
+
this._sandbox,
|
|
2890
|
+
runtimeConfigResult.data
|
|
2007
2891
|
);
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2892
|
+
await this._runtimeEnv.init();
|
|
2893
|
+
await this._createRockConfig();
|
|
2894
|
+
await this._installModelService();
|
|
2895
|
+
this._isInstalled = true;
|
|
2896
|
+
}
|
|
2897
|
+
async _createRockConfig() {
|
|
2898
|
+
if (!this._runtimeEnv) {
|
|
2899
|
+
throw new Error("Runtime environment not initialized");
|
|
2900
|
+
}
|
|
2901
|
+
await this._runtimeEnv.run(this._config.configIniCmd);
|
|
2902
|
+
}
|
|
2903
|
+
async _installModelService() {
|
|
2904
|
+
if (!this._runtimeEnv) {
|
|
2905
|
+
throw new Error("Runtime environment not initialized");
|
|
2011
2906
|
}
|
|
2012
|
-
|
|
2907
|
+
const installCmd = `cd ${this._runtimeEnv.workdir} && ${this._config.installCmd}`;
|
|
2908
|
+
await this._runtimeEnv.run(installCmd, {
|
|
2909
|
+
waitTimeout: this._config.installTimeout,
|
|
2910
|
+
errorMsg: "Model service installation failed"
|
|
2911
|
+
});
|
|
2013
2912
|
}
|
|
2014
2913
|
/**
|
|
2015
|
-
* Start
|
|
2914
|
+
* Start the model service in the sandbox.
|
|
2915
|
+
*
|
|
2916
|
+
* Starts the service with configured logging settings.
|
|
2016
2917
|
*/
|
|
2017
|
-
async
|
|
2018
|
-
|
|
2918
|
+
async start() {
|
|
2919
|
+
if (!this._isInstalled) {
|
|
2920
|
+
throw new Error(
|
|
2921
|
+
`[${this._sandbox.sandboxId}] Cannot start model service: ModelService has not been installed yet. Please call install() first.`
|
|
2922
|
+
);
|
|
2923
|
+
}
|
|
2924
|
+
if (!this._runtimeEnv) {
|
|
2925
|
+
throw new Error("Runtime environment not initialized");
|
|
2926
|
+
}
|
|
2927
|
+
const startCmd = this._config.startCmd.replace(/\$\{type\}/g, this._config.type);
|
|
2928
|
+
const bashStartCmd = `export ROCK_LOGGING_PATH=${this._config.loggingPath} && export ROCK_LOGGING_FILE_NAME=${this._config.loggingFileName} && ${this._config.stopCmd} && ` + startCmd;
|
|
2929
|
+
logger11.debug(`[${this._sandbox.sandboxId}] Model service Start command: ${bashStartCmd}`);
|
|
2930
|
+
await this._runtimeEnv.run(bashStartCmd);
|
|
2931
|
+
this._isStarted = true;
|
|
2019
2932
|
}
|
|
2020
2933
|
/**
|
|
2021
|
-
* Stop service
|
|
2934
|
+
* Stop the model service.
|
|
2022
2935
|
*/
|
|
2023
|
-
async stop(
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2936
|
+
async stop() {
|
|
2937
|
+
if (!this._isStarted) {
|
|
2938
|
+
logger11.warn(
|
|
2939
|
+
`[${this._sandbox.sandboxId}] Model service is not running, skipping stop operation. is_started=${this._isStarted}`
|
|
2940
|
+
);
|
|
2941
|
+
return;
|
|
2028
2942
|
}
|
|
2943
|
+
if (!this._runtimeEnv) {
|
|
2944
|
+
throw new Error("Runtime environment not initialized");
|
|
2945
|
+
}
|
|
2946
|
+
await this._runtimeEnv.run(this._config.stopCmd);
|
|
2947
|
+
this._isStarted = false;
|
|
2029
2948
|
}
|
|
2030
2949
|
/**
|
|
2031
|
-
*
|
|
2950
|
+
* Watch agent process with the specified PID.
|
|
2032
2951
|
*/
|
|
2033
|
-
async
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2952
|
+
async watchAgent(pid) {
|
|
2953
|
+
if (!this._isStarted) {
|
|
2954
|
+
throw new Error(
|
|
2955
|
+
`[${this._sandbox.sandboxId}] Cannot watch agent: ModelService is not started. Please call start() first.`
|
|
2956
|
+
);
|
|
2957
|
+
}
|
|
2958
|
+
if (!this._runtimeEnv) {
|
|
2959
|
+
throw new Error("Runtime environment not initialized");
|
|
2960
|
+
}
|
|
2961
|
+
const watchCmd = this._config.watchAgentCmd.replace(/\$\{pid\}/g, pid);
|
|
2962
|
+
logger11.debug(
|
|
2963
|
+
`[${this._sandbox.sandboxId}] Model service watch agent with pid=${pid}, cmd: ${watchCmd}`
|
|
2964
|
+
);
|
|
2965
|
+
await this._runtimeEnv.run(watchCmd);
|
|
2966
|
+
}
|
|
2967
|
+
/**
|
|
2968
|
+
* Execute anti-call LLM command.
|
|
2969
|
+
*/
|
|
2970
|
+
async antiCallLlm(index, responsePayload, callTimeout = 600) {
|
|
2971
|
+
if (!this._isStarted) {
|
|
2972
|
+
throw new Error(
|
|
2973
|
+
`[${this._sandbox.sandboxId}] Cannot execute anti-call LLM: ModelService is not started. Please call start() first.`
|
|
2974
|
+
);
|
|
2975
|
+
}
|
|
2976
|
+
if (!this._runtimeEnv) {
|
|
2977
|
+
throw new Error("Runtime environment not initialized");
|
|
2978
|
+
}
|
|
2979
|
+
logger11.info(
|
|
2980
|
+
`[${this._sandbox.sandboxId}] Executing anti-call LLM: index=${index}, has_response=${responsePayload !== void 0}, timeout=${callTimeout}s`
|
|
2981
|
+
);
|
|
2982
|
+
let cmd;
|
|
2983
|
+
if (responsePayload !== void 0) {
|
|
2984
|
+
const escapedPayload = `'${responsePayload.replace(/'/g, "'\\''")}'`;
|
|
2985
|
+
cmd = this._config.antiCallLlmCmd.replace(/\$\{index\}/g, String(index)).replace(/\$\{response_payload\}/g, escapedPayload);
|
|
2986
|
+
} else {
|
|
2987
|
+
cmd = this._config.antiCallLlmCmdNoResponse.replace(/\$\{index\}/g, String(index));
|
|
2988
|
+
}
|
|
2989
|
+
const bashCmd = this._runtimeEnv.wrappedCmd(cmd);
|
|
2990
|
+
logger11.debug(`[${this._sandbox.sandboxId}] Executing command: ${bashCmd}`);
|
|
2991
|
+
const result = await this._sandbox.arun(bashCmd, {
|
|
2992
|
+
mode: "nohup",
|
|
2993
|
+
waitTimeout: callTimeout,
|
|
2994
|
+
waitInterval: 3
|
|
2995
|
+
});
|
|
2996
|
+
if (result.exitCode !== 0) {
|
|
2997
|
+
throw new Error(`Anti-call LLM command failed: ${result.output}`);
|
|
2998
|
+
}
|
|
2999
|
+
return result.output;
|
|
3000
|
+
}
|
|
3001
|
+
};
|
|
3002
|
+
|
|
3003
|
+
// src/sandbox/agent/base.ts
|
|
3004
|
+
var logger12 = initLogger("rock.agent");
|
|
3005
|
+
var Agent = class {
|
|
3006
|
+
_sandbox;
|
|
3007
|
+
_modelService = null;
|
|
3008
|
+
constructor(sandbox) {
|
|
3009
|
+
this._sandbox = sandbox;
|
|
3010
|
+
}
|
|
3011
|
+
get sandbox() {
|
|
3012
|
+
return this._sandbox;
|
|
3013
|
+
}
|
|
3014
|
+
get modelService() {
|
|
3015
|
+
return this._modelService;
|
|
3016
|
+
}
|
|
3017
|
+
};
|
|
3018
|
+
var DefaultAgent = class extends Agent {
|
|
3019
|
+
_config = null;
|
|
3020
|
+
_agentSession = null;
|
|
3021
|
+
get config() {
|
|
3022
|
+
return this._config;
|
|
3023
|
+
}
|
|
3024
|
+
get agentSession() {
|
|
3025
|
+
return this._agentSession;
|
|
3026
|
+
}
|
|
3027
|
+
get modelService() {
|
|
3028
|
+
return this._modelService;
|
|
3029
|
+
}
|
|
3030
|
+
async install(config) {
|
|
3031
|
+
this._config = config;
|
|
3032
|
+
this._agentSession = config.agentSession;
|
|
3033
|
+
const sandboxId = this._sandbox.sandboxId;
|
|
3034
|
+
logger12.info(`[${sandboxId}] Starting agent initialization`);
|
|
3035
|
+
try {
|
|
3036
|
+
await this._setupSession();
|
|
3037
|
+
await this._executeInitCommands(config.preInitCmds, "pre-init");
|
|
3038
|
+
await this._executeInitCommands(config.postInitCmds, "post-init");
|
|
3039
|
+
logger12.info(`[${sandboxId}] Agent initialization completed`);
|
|
3040
|
+
} catch (e) {
|
|
3041
|
+
const error = e;
|
|
3042
|
+
logger12.error(`[${sandboxId}] Agent initialization failed: ${error.message}`);
|
|
3043
|
+
throw error;
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
async run(prompt) {
|
|
3047
|
+
if (!this._config) {
|
|
3048
|
+
throw new Error("Agent is not installed. Please call install() first.");
|
|
3049
|
+
}
|
|
3050
|
+
if (!this._config.runCmd) {
|
|
3051
|
+
throw new Error("runCmd is not configured");
|
|
3052
|
+
}
|
|
3053
|
+
const cmd = await this._createAgentRunCmd(prompt);
|
|
3054
|
+
return this._agentRun(cmd);
|
|
3055
|
+
}
|
|
3056
|
+
async _setupSession() {
|
|
3057
|
+
if (!this._config) return;
|
|
3058
|
+
const sandboxId = this._sandbox.sandboxId;
|
|
3059
|
+
logger12.info(`[${sandboxId}] Creating bash session: ${this._agentSession}`);
|
|
3060
|
+
await this._sandbox.createSession({
|
|
3061
|
+
session: this._agentSession,
|
|
3062
|
+
envEnable: true,
|
|
3063
|
+
env: this._config.env
|
|
3064
|
+
});
|
|
3065
|
+
logger12.info(`[${sandboxId}] Bash session '${this._agentSession}' created successfully`);
|
|
3066
|
+
}
|
|
3067
|
+
async _executeInitCommands(cmdList, stepName) {
|
|
3068
|
+
if (!cmdList || cmdList.length === 0) return;
|
|
3069
|
+
const sandboxId = this._sandbox.sandboxId;
|
|
3070
|
+
logger12.info(`[${sandboxId}] ${stepName} started: Executing ${cmdList.length} commands`);
|
|
3071
|
+
for (let idx = 0; idx < cmdList.length; idx++) {
|
|
3072
|
+
const cmdConfig = cmdList[idx];
|
|
3073
|
+
if (!cmdConfig) continue;
|
|
3074
|
+
const command = cmdConfig.command;
|
|
3075
|
+
const timeout = cmdConfig.timeoutSeconds;
|
|
3076
|
+
logger12.debug(
|
|
3077
|
+
`[${sandboxId}] Executing ${stepName} command ${idx + 1}/${cmdList.length}: ${command.substring(0, 100)}...`
|
|
3078
|
+
);
|
|
3079
|
+
const result = await this._sandbox.arun(`bash -c ${JSON.stringify(command)}`, {
|
|
3080
|
+
waitTimeout: timeout,
|
|
3081
|
+
mode: "NOHUP"
|
|
3082
|
+
});
|
|
3083
|
+
if (result.exitCode !== 0) {
|
|
3084
|
+
throw new Error(
|
|
3085
|
+
`[${sandboxId}] ${stepName} command ${idx + 1} failed with exit code ${result.exitCode}: ${result.output.substring(0, 200)}`
|
|
3086
|
+
);
|
|
2041
3087
|
}
|
|
3088
|
+
logger12.debug(`[${sandboxId}] ${stepName} command ${idx + 1} completed successfully`);
|
|
2042
3089
|
}
|
|
2043
|
-
|
|
3090
|
+
logger12.info(`[${sandboxId}] ${stepName} completed: Completed ${cmdList.length} commands`);
|
|
2044
3091
|
}
|
|
2045
|
-
|
|
2046
|
-
|
|
3092
|
+
async _createAgentRunCmd(prompt) {
|
|
3093
|
+
if (!this._config || !this._config.runCmd) {
|
|
3094
|
+
throw new Error("runCmd is not configured");
|
|
3095
|
+
}
|
|
3096
|
+
let runCmd = this._config.runCmd.replace(/{prompt}/g, JSON.stringify(prompt));
|
|
3097
|
+
if (this._config.skipWrapRunCmd) {
|
|
3098
|
+
return `bash -c ${JSON.stringify(runCmd)}`;
|
|
3099
|
+
}
|
|
3100
|
+
return `bash -c ${JSON.stringify(runCmd)}`;
|
|
3101
|
+
}
|
|
3102
|
+
async _agentRun(cmd) {
|
|
3103
|
+
const sandboxId = this._sandbox.sandboxId;
|
|
3104
|
+
try {
|
|
3105
|
+
const timestamp = Date.now().toString();
|
|
3106
|
+
const tmpFile = `/tmp/tmp_${timestamp}.out`;
|
|
3107
|
+
if (!this._sandbox.startNohupProcess) {
|
|
3108
|
+
const msg = "startNohupProcess method is not available on sandbox";
|
|
3109
|
+
return { output: msg, exitCode: 1, failureReason: msg, expectString: "" };
|
|
3110
|
+
}
|
|
3111
|
+
const { pid, errorResponse } = await this._sandbox.startNohupProcess(cmd, tmpFile, this._agentSession);
|
|
3112
|
+
if (errorResponse) {
|
|
3113
|
+
return errorResponse;
|
|
3114
|
+
}
|
|
3115
|
+
if (!pid) {
|
|
3116
|
+
const msg = "Failed to submit command, nohup failed to extract PID";
|
|
3117
|
+
return { output: msg, exitCode: 1, failureReason: msg, expectString: "" };
|
|
3118
|
+
}
|
|
3119
|
+
logger12.info(`[${sandboxId}] Agent process started with PID: ${pid}`);
|
|
3120
|
+
if (!this._sandbox.waitForProcessCompletion) {
|
|
3121
|
+
const msg = "waitForProcessCompletion method is not available on sandbox";
|
|
3122
|
+
return { output: msg, exitCode: 1, failureReason: msg, expectString: "" };
|
|
3123
|
+
}
|
|
3124
|
+
const { success, message } = await this._sandbox.waitForProcessCompletion(
|
|
3125
|
+
pid,
|
|
3126
|
+
this._agentSession,
|
|
3127
|
+
this._config.agentRunTimeout,
|
|
3128
|
+
this._config.agentRunCheckInterval
|
|
3129
|
+
);
|
|
3130
|
+
if (!this._sandbox.handleNohupOutput) {
|
|
3131
|
+
const msg = "handleNohupOutput method is not available on sandbox";
|
|
3132
|
+
return { output: msg, exitCode: 1, failureReason: msg, expectString: "" };
|
|
3133
|
+
}
|
|
3134
|
+
const result = await this._sandbox.handleNohupOutput(tmpFile, this._agentSession, success, message, false, null);
|
|
3135
|
+
return result;
|
|
3136
|
+
} catch (e) {
|
|
3137
|
+
const error = e;
|
|
3138
|
+
const errorMsg = `Failed to execute nohup command: ${error.message}`;
|
|
3139
|
+
logger12.error(`[${sandboxId}] ${errorMsg}`);
|
|
3140
|
+
return { output: errorMsg, exitCode: 1, failureReason: errorMsg, expectString: "" };
|
|
3141
|
+
}
|
|
2047
3142
|
}
|
|
2048
3143
|
};
|
|
3144
|
+
var AgentConfigSchema = zod.z.object({
|
|
3145
|
+
agentType: zod.z.string(),
|
|
3146
|
+
version: zod.z.string().default("default")
|
|
3147
|
+
});
|
|
3148
|
+
var AgentBashCommandSchema = zod.z.object({
|
|
3149
|
+
command: zod.z.string(),
|
|
3150
|
+
timeoutSeconds: zod.z.number().int().positive().default(300)
|
|
3151
|
+
});
|
|
3152
|
+
var DefaultAgentConfigSchema = zod.z.object({
|
|
3153
|
+
agentType: zod.z.string(),
|
|
3154
|
+
version: zod.z.string().default("default"),
|
|
3155
|
+
// Session management
|
|
3156
|
+
agentSession: zod.z.string().default("default-agent-session"),
|
|
3157
|
+
// Startup/shutdown commands
|
|
3158
|
+
preInitBashCmdList: zod.z.array(AgentBashCommandSchema).default(
|
|
3159
|
+
envVars.ROCK_AGENT_PRE_INIT_BASH_CMD_LIST.map((cmd) => ({
|
|
3160
|
+
command: cmd.command,
|
|
3161
|
+
timeoutSeconds: cmd.timeoutSeconds || 300
|
|
3162
|
+
}))
|
|
3163
|
+
),
|
|
3164
|
+
postInitBashCmdList: zod.z.array(AgentBashCommandSchema).default([]),
|
|
3165
|
+
// Environment variables for the session
|
|
3166
|
+
sessionEnvs: zod.z.record(zod.z.string()).default({}),
|
|
3167
|
+
// Optional ModelService configuration
|
|
3168
|
+
modelServiceConfig: zod.z.custom().nullable().default(null)
|
|
3169
|
+
});
|
|
3170
|
+
var RockAgentConfigSchema = zod.z.object({
|
|
3171
|
+
agentType: zod.z.string().default("default"),
|
|
3172
|
+
agentName: zod.z.string().default(() => crypto.randomUUID().replace(/-/g, "")),
|
|
3173
|
+
version: zod.z.string().default("default"),
|
|
3174
|
+
agentInstalledDir: zod.z.string().default("/tmp/installed_agent"),
|
|
3175
|
+
instanceId: zod.z.string().default(() => `instance-id-${crypto.randomUUID().replace(/-/g, "")}`),
|
|
3176
|
+
projectPath: zod.z.string().nullable().default(null),
|
|
3177
|
+
useDeployWorkingDirAsFallback: zod.z.boolean().default(true),
|
|
3178
|
+
agentSession: zod.z.string().default(() => `agent-session-${crypto.randomUUID().replace(/-/g, "")}`),
|
|
3179
|
+
env: zod.z.record(zod.z.string()).default({}),
|
|
3180
|
+
preInitCmds: zod.z.array(AgentBashCommandSchema).default(
|
|
3181
|
+
envVars.ROCK_AGENT_PRE_INIT_BASH_CMD_LIST.map((cmd) => ({
|
|
3182
|
+
command: cmd.command,
|
|
3183
|
+
timeoutSeconds: cmd.timeoutSeconds || 300
|
|
3184
|
+
}))
|
|
3185
|
+
),
|
|
3186
|
+
postInitCmds: zod.z.array(AgentBashCommandSchema).default([]),
|
|
3187
|
+
agentInstallTimeout: zod.z.number().int().positive().default(600),
|
|
3188
|
+
agentRunTimeout: zod.z.number().int().positive().default(1800),
|
|
3189
|
+
agentRunCheckInterval: zod.z.number().int().positive().default(30),
|
|
3190
|
+
workingDir: zod.z.string().nullable().default(null),
|
|
3191
|
+
runCmd: zod.z.string().nullable().default(null),
|
|
3192
|
+
skipWrapRunCmd: zod.z.boolean().default(false),
|
|
3193
|
+
runtimeEnvConfig: zod.z.any().nullable().default(null),
|
|
3194
|
+
modelServiceConfig: zod.z.custom().nullable().default(null)
|
|
3195
|
+
}).refine((data) => data.agentRunCheckInterval < data.agentRunTimeout, {
|
|
3196
|
+
message: "agentRunCheckInterval must be less than agentRunTimeout"
|
|
3197
|
+
});
|
|
2049
3198
|
|
|
2050
3199
|
// src/index.ts
|
|
2051
|
-
|
|
3200
|
+
function getVersion() {
|
|
3201
|
+
const packageJsonPath = path.join(__dirname, "..", "package.json");
|
|
3202
|
+
const content = fs.readFileSync(packageJsonPath, "utf-8");
|
|
3203
|
+
return JSON.parse(content).version;
|
|
3204
|
+
}
|
|
3205
|
+
var VERSION = getVersion();
|
|
2052
3206
|
|
|
3207
|
+
exports.Agent = Agent;
|
|
3208
|
+
exports.AgentBashCommandSchema = AgentBashCommandSchema;
|
|
3209
|
+
exports.AgentConfigSchema = AgentConfigSchema;
|
|
2053
3210
|
exports.BadRequestRockError = BadRequestRockError;
|
|
2054
3211
|
exports.BashActionSchema = BashActionSchema;
|
|
2055
3212
|
exports.ChmodRequestSchema = ChmodRequestSchema;
|
|
@@ -2063,9 +3220,10 @@ exports.Codes = Codes;
|
|
|
2063
3220
|
exports.CommandResponseSchema = CommandResponseSchema;
|
|
2064
3221
|
exports.CommandRockError = CommandRockError;
|
|
2065
3222
|
exports.CommandSchema = CommandSchema;
|
|
2066
|
-
exports.Constants = Constants;
|
|
2067
3223
|
exports.CreateBashSessionRequestSchema = CreateBashSessionRequestSchema;
|
|
2068
3224
|
exports.CreateSessionResponseSchema = CreateSessionResponseSchema;
|
|
3225
|
+
exports.DefaultAgent = DefaultAgent;
|
|
3226
|
+
exports.DefaultAgentConfigSchema = DefaultAgentConfigSchema;
|
|
2069
3227
|
exports.Deploy = Deploy;
|
|
2070
3228
|
exports.EnvHubClient = EnvHubClient;
|
|
2071
3229
|
exports.EnvHubClientConfigSchema = EnvHubClientConfigSchema;
|
|
@@ -2079,19 +3237,28 @@ exports.LinuxFileSystem = LinuxFileSystem;
|
|
|
2079
3237
|
exports.LinuxRemoteUser = LinuxRemoteUser;
|
|
2080
3238
|
exports.ModelClient = ModelClient;
|
|
2081
3239
|
exports.ModelService = ModelService;
|
|
3240
|
+
exports.ModelServiceConfigSchema = ModelServiceConfigSchema;
|
|
3241
|
+
exports.NODE_DEFAULT_VERSION = NODE_DEFAULT_VERSION;
|
|
2082
3242
|
exports.Network = Network;
|
|
3243
|
+
exports.NodeRuntimeEnv = NodeRuntimeEnv;
|
|
3244
|
+
exports.NodeRuntimeEnvConfigSchema = NodeRuntimeEnvConfigSchema;
|
|
2083
3245
|
exports.ObservationSchema = ObservationSchema;
|
|
2084
3246
|
exports.OssSetupResponseSchema = OssSetupResponseSchema;
|
|
2085
3247
|
exports.PID_PREFIX = PID_PREFIX;
|
|
2086
3248
|
exports.PID_SUFFIX = PID_SUFFIX;
|
|
2087
3249
|
exports.Process = Process;
|
|
3250
|
+
exports.PythonRuntimeEnv = PythonRuntimeEnv;
|
|
3251
|
+
exports.PythonRuntimeEnvConfigSchema = PythonRuntimeEnvConfigSchema;
|
|
2088
3252
|
exports.ReadFileRequestSchema = ReadFileRequestSchema;
|
|
2089
3253
|
exports.ReadFileResponseSchema = ReadFileResponseSchema;
|
|
2090
3254
|
exports.ReasonPhrases = ReasonPhrases;
|
|
3255
|
+
exports.RockAgentConfigSchema = RockAgentConfigSchema;
|
|
2091
3256
|
exports.RockEnv = RockEnv;
|
|
2092
3257
|
exports.RockEnvInfoSchema = RockEnvInfoSchema;
|
|
2093
3258
|
exports.RockException = RockException;
|
|
2094
3259
|
exports.RunMode = RunMode;
|
|
3260
|
+
exports.RuntimeEnv = RuntimeEnv;
|
|
3261
|
+
exports.RuntimeEnvConfigSchema = RuntimeEnvConfigSchema;
|
|
2095
3262
|
exports.Sandbox = Sandbox;
|
|
2096
3263
|
exports.SandboxConfigSchema = SandboxConfigSchema;
|
|
2097
3264
|
exports.SandboxGroup = SandboxGroup;
|
|
@@ -2106,16 +3273,17 @@ exports.WriteFileRequestSchema = WriteFileRequestSchema;
|
|
|
2106
3273
|
exports.WriteFileResponseSchema = WriteFileResponseSchema;
|
|
2107
3274
|
exports.arunWithRetry = arunWithRetry;
|
|
2108
3275
|
exports.createRockEnvInfo = createRockEnvInfo;
|
|
3276
|
+
exports.createRuntimeEnvId = createRuntimeEnvId;
|
|
2109
3277
|
exports.createSandboxConfig = createSandboxConfig;
|
|
2110
3278
|
exports.createSandboxGroupConfig = createSandboxGroupConfig;
|
|
2111
3279
|
exports.deprecated = deprecated;
|
|
2112
3280
|
exports.deprecatedClass = deprecatedClass;
|
|
2113
3281
|
exports.extractNohupPidFromSandbox = extractNohupPid;
|
|
2114
3282
|
exports.fromRockException = fromRockException;
|
|
3283
|
+
exports.getDefaultPipIndexUrl = getDefaultPipIndexUrl;
|
|
2115
3284
|
exports.getEnv = getEnv;
|
|
2116
3285
|
exports.getReasonPhrase = getReasonPhrase;
|
|
2117
3286
|
exports.getRequiredEnv = getRequiredEnv;
|
|
2118
|
-
exports.isBrowser = isBrowser;
|
|
2119
3287
|
exports.isClientError = isClientError;
|
|
2120
3288
|
exports.isCommandError = isCommandError;
|
|
2121
3289
|
exports.isEnvSet = isEnvSet;
|