mirror of
https://github.com/Jeuners/ECC.git
synced 2026-09-10 15:32:31 +02:00
feat: connect ECC to canonical Ito compute CLI (#2558)
This commit is contained in:
parent
9d54ee222d
commit
bc774282e6
32 changed files with 1043 additions and 1275 deletions
|
|
@ -29,7 +29,7 @@ const COMMANDS = {
|
|||
},
|
||||
ito: {
|
||||
script: 'ito.js',
|
||||
description: 'Prepare a read-only sandbox handoff to the Itô compute desk',
|
||||
description: 'Invoke the separately installed canonical Itô compute CLI',
|
||||
},
|
||||
'install-plan': {
|
||||
script: 'install-plan.js',
|
||||
|
|
@ -138,8 +138,9 @@ Examples:
|
|||
ecc catalog show framework:nextjs
|
||||
ecc consult "security reviews"
|
||||
ecc control-pane --port 8765
|
||||
ecc ito rent --accelerator h100 --count 1 --hours 24
|
||||
ecc --dry-run ito rent --accelerator h100 --count 1 --hours 24 --json
|
||||
ecc ito auth
|
||||
ecc ito find --gpu h200 --count 8 --nodes 1 --gpus-per-node 8 --days 30 --storage-tb 1 --start-window 2099-08-15 --max-rate 3.00 --form-factor bare_metal --contract-type reservation --fabric infiniband --region us-east-1
|
||||
ecc ito status --json
|
||||
ecc list-installed --json
|
||||
ecc doctor --target cursor
|
||||
ecc repair --dry-run
|
||||
|
|
@ -232,7 +233,12 @@ function runCommand(commandName, args) {
|
|||
{
|
||||
cwd: process.cwd(),
|
||||
env: commandName === 'ito'
|
||||
? { ...createSafeItoEnvironment(process.env, { includeControls: true }) }
|
||||
? {
|
||||
...createSafeItoEnvironment(process.env, {
|
||||
includeControls: true,
|
||||
includeItoRuntime: true,
|
||||
}),
|
||||
}
|
||||
: process.env,
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
|
|
|
|||
454
scripts/ito.js
454
scripts/ito.js
|
|
@ -2,306 +2,210 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { spawnSync } = require("child_process");
|
||||
const { createSafeItoEnvironment } = require("./lib/ito-environment");
|
||||
|
||||
const DESK_URL = "https://compute.itomarkets.com/desk";
|
||||
const SCHEMA_VERSION = "ito.compute.handoff.v1";
|
||||
const SUPPORTED_ACCELERATORS = Object.freeze({
|
||||
h100: "h100",
|
||||
"h100-pcie": "h100-pcie",
|
||||
"h100-sxm": "h100-sxm",
|
||||
});
|
||||
const REQUIRED_INTENT_OPTIONS = Object.freeze([
|
||||
"accelerator",
|
||||
"count",
|
||||
"hours",
|
||||
]);
|
||||
const SUPPORTED_COMMANDS = Object.freeze(["auth", "find", "status"]);
|
||||
const CANONICAL_REPOSITORY = "https://github.com/Ito-Markets/ito-cloud-runtime.git";
|
||||
const CANONICAL_PACKAGE_PATH = "cli/ito-compute-cli";
|
||||
const EXECUTABLE_OVERRIDE = "ECC_ITO_CLI_EXECUTABLE";
|
||||
const MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
|
||||
|
||||
function showHelp() {
|
||||
console.log(`
|
||||
ECC × Itô compute handoff
|
||||
ECC × Itô local CLI bridge
|
||||
|
||||
Usage:
|
||||
ecc ito rent --accelerator <h100|h100-pcie|h100-sxm> --count <1-64> --hours <1-720> [options]
|
||||
ecc ito auth
|
||||
ecc ito find <all required RFQ options>
|
||||
ecc ito status
|
||||
ecc ito <auth|find|status> --json
|
||||
|
||||
Options:
|
||||
--dry-run Emit the exact handoff without opening a browser
|
||||
--no-open Emit the exact handoff for manual browser navigation
|
||||
--json Emit the versioned response envelope as JSON
|
||||
--help Show this help
|
||||
The bridge invokes the separately installed canonical Itô CLI and returns its
|
||||
real stdout, stderr, and exit code unchanged. It performs no browser navigation
|
||||
and adds no lock, workload, inference, evaluation, or purchase path.
|
||||
|
||||
Example:
|
||||
ecc ito rent --accelerator h100 --count 1 --hours 24
|
||||
Important:
|
||||
- "find" reads live inventory and submits an authenticated RFQ.
|
||||
- Obtain explicit buyer authority and every hard constraint before invoking it.
|
||||
- "status" reads live RFQ and procurement status.
|
||||
- Inventory and RFQs are not reservations; only a returned firm quote is firm.
|
||||
|
||||
This command creates a sandbox-only, read-only intent. It opens the Itô desk
|
||||
for manual copy; sign-in may be required. It does not send the intent into Itô,
|
||||
file an RFQ, request or accept a quote, call a procurement endpoint, approve
|
||||
funds, or place an order. Stop before "Pay & buy".
|
||||
The canonical package is currently unpublished. Install it locally:
|
||||
Canonical source: Ito-Markets/ito-cloud-runtime/${CANONICAL_PACKAGE_PATH}
|
||||
git clone ${CANONICAL_REPOSITORY}
|
||||
cd ito-cloud-runtime/${CANONICAL_PACKAGE_PATH}
|
||||
npm ci
|
||||
npm run check
|
||||
|
||||
Then set ${EXECUTABLE_OVERRIDE} to the explicit absolute built entry:
|
||||
/absolute/path/to/ito-cloud-runtime/${CANONICAL_PACKAGE_PATH}/dist/bin/ito.js
|
||||
|
||||
For safety, ECC never discovers this credential-bearing client through PATH.
|
||||
|
||||
The same package's MCP server exposes only:
|
||||
ito_auth
|
||||
ito_find
|
||||
ito_status
|
||||
|
||||
Configure the MCP command as "node" with this absolute argument:
|
||||
/absolute/path/to/ito-cloud-runtime/${CANONICAL_PACKAGE_PATH}/dist/bin/ito-mcp.js
|
||||
|
||||
Inject ITO_API_KEY into the child process from 1Password or the launching
|
||||
environment. Never put the key in arguments, tracked files, or chat.
|
||||
`);
|
||||
}
|
||||
|
||||
function readValue(args, index, option) {
|
||||
const value = args[index + 1];
|
||||
if (value === undefined || value.startsWith("--")) {
|
||||
throw new Error(`--${option} requires a value`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseInteger(raw, option, minimum, maximum) {
|
||||
if (!/^[0-9]+$/.test(raw)) {
|
||||
throw new Error(`--${option} must be an integer between ${minimum} and ${maximum}`);
|
||||
}
|
||||
const value = Number(raw);
|
||||
if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
||||
throw new Error(`--${option} must be an integer between ${minimum} and ${maximum}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseAccelerator(raw) {
|
||||
const normalized = String(raw).trim().toLowerCase();
|
||||
const accelerator = SUPPORTED_ACCELERATORS[normalized];
|
||||
if (!accelerator) {
|
||||
throw new Error(
|
||||
"--accelerator must be one of h100, h100-pcie, or h100-sxm"
|
||||
);
|
||||
}
|
||||
return accelerator;
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
function parseArgs(argv, environment = process.env) {
|
||||
const args = [...argv];
|
||||
if (args.includes("--help") || args.includes("-h")) {
|
||||
return { help: true };
|
||||
if (
|
||||
args.length === 0
|
||||
|| args.includes("--help")
|
||||
|| args.includes("-h")
|
||||
) {
|
||||
return Object.freeze({ help: true, invocationArgs: [] });
|
||||
}
|
||||
|
||||
const command = args.shift();
|
||||
if (command !== "rent") {
|
||||
throw new Error(`unsupported Itô command: ${command || "(missing)"}`);
|
||||
}
|
||||
|
||||
const values = {
|
||||
accelerator: null,
|
||||
count: null,
|
||||
hours: null,
|
||||
dryRun: false,
|
||||
json: false,
|
||||
noOpen: false,
|
||||
};
|
||||
const seen = new Set();
|
||||
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
if (arg === "--dry-run") {
|
||||
if (seen.has("dry-run")) throw new Error("--dry-run may only be provided once");
|
||||
seen.add("dry-run");
|
||||
values.dryRun = true;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--json") {
|
||||
if (seen.has("json")) throw new Error("--json may only be provided once");
|
||||
seen.add("json");
|
||||
values.json = true;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--no-open") {
|
||||
if (seen.has("no-open")) throw new Error("--no-open may only be provided once");
|
||||
seen.add("no-open");
|
||||
values.noOpen = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const option = arg.startsWith("--") ? arg.slice(2) : "";
|
||||
if (!REQUIRED_INTENT_OPTIONS.includes(option)) {
|
||||
throw new Error(`unsupported option: ${arg}`);
|
||||
}
|
||||
if (seen.has(option)) {
|
||||
throw new Error(`--${option} may only be provided once`);
|
||||
}
|
||||
seen.add(option);
|
||||
const raw = readValue(args, index, option);
|
||||
if (option === "accelerator") values.accelerator = parseAccelerator(raw);
|
||||
if (option === "count") values.count = parseInteger(raw, option, 1, 64);
|
||||
if (option === "hours") values.hours = parseInteger(raw, option, 1, 720);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
const missing = REQUIRED_INTENT_OPTIONS.filter((option) => values[option] === null);
|
||||
if (missing.length > 0) {
|
||||
if (environment.ECC_DRY_RUN === "1" || args.includes("--dry-run")) {
|
||||
throw new Error(
|
||||
`missing required rental intent option${missing.length === 1 ? "" : "s"}: `
|
||||
+ missing.map((option) => `--${option}`).join(", ")
|
||||
"Itô compute has no paper or dry-run success mode. No CLI operation was invoked."
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
help: false,
|
||||
command,
|
||||
options: Object.freeze({
|
||||
...values,
|
||||
dryRun: values.dryRun || process.env.ECC_DRY_RUN === "1",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function buildHandoffMessage(intent) {
|
||||
const accelerator = intent.accelerator.toUpperCase();
|
||||
return [
|
||||
"[ECC sandbox-only compute handoff]",
|
||||
`Source: ECC CLI. Need ${intent.count} × ${accelerator} for ${intent.hours} hours.`,
|
||||
"Treat this as a read-only requirement for review.",
|
||||
"Do not file an RFQ, request or accept a quote, place an order, approve funds, or contact a counterparty without a separate explicit human action in Itô.",
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
function createEnvelope({ success, state, data, error }) {
|
||||
return Object.freeze({
|
||||
schemaVersion: SCHEMA_VERSION,
|
||||
success,
|
||||
state,
|
||||
data,
|
||||
error,
|
||||
links: Object.freeze({ desk: DESK_URL }),
|
||||
});
|
||||
}
|
||||
|
||||
function createInvalidEnvelope(message) {
|
||||
return createEnvelope({
|
||||
success: false,
|
||||
state: "invalid_request",
|
||||
data: null,
|
||||
error: Object.freeze({
|
||||
code: "INVALID_ARGUMENT",
|
||||
message,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
function defaultOpenUrl(url) {
|
||||
let executable;
|
||||
let args;
|
||||
const testExecutable = process.env.NODE_ENV === "test"
|
||||
? process.env.ECC_ITO_BROWSER_EXECUTABLE
|
||||
: null;
|
||||
|
||||
if (testExecutable) {
|
||||
if (process.platform === "win32" && /\.(?:bat|cmd)$/i.test(testExecutable)) {
|
||||
executable = process.env.ComSpec || "cmd.exe";
|
||||
args = ["/d", "/s", "/c", testExecutable, url];
|
||||
} else {
|
||||
executable = testExecutable;
|
||||
args = [url];
|
||||
}
|
||||
} else if (process.platform === "darwin") {
|
||||
executable = "open";
|
||||
args = [url];
|
||||
} else if (process.platform === "win32") {
|
||||
executable = "cmd.exe";
|
||||
args = ["/d", "/s", "/c", "start", "", url];
|
||||
} else {
|
||||
executable = "xdg-open";
|
||||
args = [url];
|
||||
const jsonIndexes = args
|
||||
.map((value, index) => (value === "--json" ? index : -1))
|
||||
.filter((index) => index >= 0);
|
||||
if (jsonIndexes.length > 1) {
|
||||
throw new Error("--json may only be provided once");
|
||||
}
|
||||
const withoutJson = args.filter((value) => value !== "--json");
|
||||
const command = withoutJson.shift();
|
||||
if (!SUPPORTED_COMMANDS.includes(command)) {
|
||||
throw new Error(
|
||||
`Unsupported Itô command "${command || "(missing)"}"; ECC permits only auth, find, and status.`
|
||||
);
|
||||
}
|
||||
|
||||
const result = spawnSync(executable, args, {
|
||||
env: { ...createSafeItoEnvironment(process.env) },
|
||||
stdio: "ignore",
|
||||
windowsHide: true,
|
||||
shell: false,
|
||||
});
|
||||
return !result.error && result.status === 0;
|
||||
}
|
||||
|
||||
function createHandoff(options, openUrl = defaultOpenUrl) {
|
||||
const shouldOpen = !options.dryRun && !options.noOpen;
|
||||
const opened = shouldOpen ? openUrl(DESK_URL) : false;
|
||||
const intent = Object.freeze({
|
||||
accelerator: options.accelerator,
|
||||
count: options.count,
|
||||
hours: options.hours,
|
||||
});
|
||||
|
||||
return createEnvelope({
|
||||
success: true,
|
||||
state: "manual_handoff",
|
||||
data: Object.freeze({
|
||||
authenticated: null,
|
||||
dryRun: options.dryRun,
|
||||
opened,
|
||||
intent,
|
||||
provenance: Object.freeze({
|
||||
source: "ecc-cli",
|
||||
command: "ecc ito rent",
|
||||
}),
|
||||
authority: Object.freeze({
|
||||
environment: "sandbox",
|
||||
readOnly: true,
|
||||
liveRfq: false,
|
||||
procurementMutation: false,
|
||||
quoteAcceptance: false,
|
||||
fundsApproval: false,
|
||||
orderCreation: false,
|
||||
outreach: false,
|
||||
}),
|
||||
handoff: Object.freeze({
|
||||
transport: "manual_copy",
|
||||
destination: "ito-desk",
|
||||
acceptedByIto: false,
|
||||
signInMayBeRequired: true,
|
||||
message: buildHandoffMessage(intent),
|
||||
limitation: "Itô /desk currently exposes no supported structured ECC intake or deep-link contract.",
|
||||
}),
|
||||
approvalGate: 'Stop before "Pay & buy".',
|
||||
priceQuote: null,
|
||||
orderId: null,
|
||||
}),
|
||||
error: null,
|
||||
return Object.freeze({
|
||||
help: false,
|
||||
invocationArgs: Object.freeze([
|
||||
...(jsonIndexes.length === 1 ? ["--json"] : []),
|
||||
command,
|
||||
...withoutJson,
|
||||
]),
|
||||
});
|
||||
}
|
||||
|
||||
function renderText(payload) {
|
||||
const { data } = payload;
|
||||
const browserState = data.dryRun
|
||||
? "Dry-run: browser not opened."
|
||||
: data.opened
|
||||
? "Opened the Itô desk; sign-in may be required."
|
||||
: "Browser not opened; sign-in may be required at the Itô desk.";
|
||||
return [
|
||||
"ECC × Itô sandbox compute handoff",
|
||||
"",
|
||||
browserState,
|
||||
`Desk: ${payload.links.desk}`,
|
||||
"Transport: manual copy; Itô has not accepted this intent.",
|
||||
`Message: ${data.handoff.message}`,
|
||||
`Limitation: ${data.handoff.limitation}`,
|
||||
`Approval gate: ${data.approvalGate}`,
|
||||
"",
|
||||
"ECC does not file an RFQ, request or accept a quote, use credentials, approve funds, create an order, or contact a counterparty.",
|
||||
].join("\n");
|
||||
function resolveItoExecutable(environment = process.env) {
|
||||
const configured = environment[EXECUTABLE_OVERRIDE]?.trim();
|
||||
if (!configured) {
|
||||
throw new Error([
|
||||
"The canonical ito-compute-cli is unpublished and ECC will not resolve",
|
||||
`a credential-bearing "ito" executable from PATH. Build it from`,
|
||||
`${CANONICAL_REPOSITORY.replace(/\.git$/, "")}/${CANONICAL_PACKAGE_PATH},`,
|
||||
"run npm ci and npm run check, then set",
|
||||
`${EXECUTABLE_OVERRIDE} to the explicit absolute dist/bin/ito.js path.`,
|
||||
].join(" "));
|
||||
}
|
||||
|
||||
if (!path.isAbsolute(configured)) {
|
||||
throw new Error(
|
||||
`${EXECUTABLE_OVERRIDE} must be an absolute path explicitly configured by the operator.`
|
||||
);
|
||||
}
|
||||
return assertUsableExecutable(configured);
|
||||
}
|
||||
|
||||
function main(argv = process.argv.slice(2), dependencies = {}) {
|
||||
let parsed;
|
||||
function assertUsableExecutable(candidate) {
|
||||
let canonicalCandidate;
|
||||
try {
|
||||
parsed = parseArgs(argv);
|
||||
canonicalCandidate = fs.realpathSync.native(candidate);
|
||||
} catch {
|
||||
throw new Error(
|
||||
`${EXECUTABLE_OVERRIDE} does not point to a readable local Itô CLI file.`
|
||||
);
|
||||
}
|
||||
if (!isUsableExecutable(canonicalCandidate)) {
|
||||
throw new Error(
|
||||
`${EXECUTABLE_OVERRIDE} does not point to a readable local Itô CLI file.`
|
||||
);
|
||||
}
|
||||
return canonicalCandidate;
|
||||
}
|
||||
|
||||
function isUsableExecutable(candidate) {
|
||||
try {
|
||||
const info = fs.statSync(candidate);
|
||||
if (!info.isFile()) return false;
|
||||
if (process.platform !== "win32" && path.extname(candidate) !== ".js") {
|
||||
fs.accessSync(candidate, fs.constants.X_OK);
|
||||
} else {
|
||||
fs.accessSync(candidate, fs.constants.R_OK);
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function buildInvocation(executable, args) {
|
||||
if (path.extname(executable).toLowerCase() === ".js") {
|
||||
return Object.freeze({
|
||||
executable: process.execPath,
|
||||
args: Object.freeze([executable, ...args]),
|
||||
});
|
||||
}
|
||||
if (
|
||||
process.platform === "win32"
|
||||
&& /\.(?:bat|cmd|ps1)$/i.test(executable)
|
||||
) {
|
||||
throw new Error(
|
||||
`Refusing to invoke the Itô CLI through a shell shim. Set ${EXECUTABLE_OVERRIDE} to the absolute dist/bin/ito.js path.`
|
||||
);
|
||||
}
|
||||
return Object.freeze({ executable, args: Object.freeze([...args]) });
|
||||
}
|
||||
|
||||
function invokeIto(executable, args, environment = process.env) {
|
||||
const invocation = buildInvocation(executable, args);
|
||||
const result = spawnSync(invocation.executable, invocation.args, {
|
||||
cwd: process.cwd(),
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...createSafeItoEnvironment(environment, { includeItoRuntime: true }),
|
||||
},
|
||||
maxBuffer: MAX_OUTPUT_BYTES,
|
||||
shell: false,
|
||||
windowsHide: true,
|
||||
});
|
||||
|
||||
if (result.stdout) process.stdout.write(result.stdout);
|
||||
if (result.stderr) process.stderr.write(result.stderr);
|
||||
if (result.error) {
|
||||
throw new Error(`The local Itô CLI could not be started: ${result.error.message}`);
|
||||
}
|
||||
if (typeof result.status === "number") return result.status;
|
||||
if (result.signal) {
|
||||
throw new Error(`The local Itô CLI terminated by signal ${result.signal}.`);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
function main(argv = process.argv.slice(2), environment = process.env) {
|
||||
try {
|
||||
const parsed = parseArgs(argv, environment);
|
||||
if (parsed.help) {
|
||||
showHelp();
|
||||
return 0;
|
||||
}
|
||||
const executable = resolveItoExecutable(environment);
|
||||
return invokeIto(executable, parsed.invocationArgs, environment);
|
||||
} catch (error) {
|
||||
const payload = createInvalidEnvelope(error.message);
|
||||
if (argv.includes("--json")) console.log(JSON.stringify(payload, null, 2));
|
||||
else console.error(`Error: ${error.message}`);
|
||||
console.error(`Error: ${error.message}`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (parsed.help) {
|
||||
showHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const payload = createHandoff(parsed.options, dependencies.openUrl);
|
||||
if (parsed.options.json) console.log(JSON.stringify(payload, null, 2));
|
||||
else console.log(renderText(payload));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
|
|
@ -309,13 +213,13 @@ if (require.main === module) {
|
|||
}
|
||||
|
||||
module.exports = Object.freeze({
|
||||
DESK_URL,
|
||||
SCHEMA_VERSION,
|
||||
buildHandoffMessage,
|
||||
createHandoff,
|
||||
createInvalidEnvelope,
|
||||
CANONICAL_PACKAGE_PATH,
|
||||
CANONICAL_REPOSITORY,
|
||||
EXECUTABLE_OVERRIDE,
|
||||
SUPPORTED_COMMANDS,
|
||||
buildInvocation,
|
||||
invokeIto,
|
||||
main,
|
||||
parseAccelerator,
|
||||
parseArgs,
|
||||
renderText,
|
||||
resolveItoExecutable,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@ function getComputeSponsorCopy() {
|
|||
return "Run or self-host any open-source model. Itô is ECC's preferred compute sponsor: "
|
||||
+ 'open its dashboard to sign in and rent or manage GPUs at '
|
||||
+ ITO_COMPUTE_URL
|
||||
+ '. Any GPU provider works. ECC only provides this link; it does not provision '
|
||||
+ 'compute or serving. Managed inference through Itô is not live yet.';
|
||||
+ '. Any GPU provider works. This sponsorship link is passive: it does not invoke '
|
||||
+ 'an RFQ, reserve capacity, provision compute, or configure serving. Separately, '
|
||||
+ 'the opt-in "ecc ito find" bridge invokes the explicitly configured canonical '
|
||||
+ 'Itô CLI and submits a live authenticated RFQ; it does not reserve capacity. '
|
||||
+ 'Managed inference through Itô is not live yet.';
|
||||
}
|
||||
|
||||
module.exports = Object.freeze({
|
||||
|
|
|
|||
|
|
@ -23,6 +23,18 @@ const SYSTEM_ENVIRONMENT_KEYS = Object.freeze([
|
|||
"XDG_RUNTIME_DIR",
|
||||
]);
|
||||
|
||||
const ITO_RUNTIME_ENVIRONMENT_KEYS = Object.freeze([
|
||||
"ITO_API_KEY",
|
||||
"ITO_API_URL",
|
||||
"ITO_INVENTORY_URL",
|
||||
]);
|
||||
|
||||
const ECC_ITO_CONTROL_KEYS = Object.freeze([
|
||||
"ECC_DRY_RUN",
|
||||
"ECC_ITO_CLI_EXECUTABLE",
|
||||
"NODE_ENV",
|
||||
]);
|
||||
|
||||
function copyDefined(source, target, key) {
|
||||
if (typeof source[key] === "string") {
|
||||
target[key] = source[key];
|
||||
|
|
@ -38,11 +50,15 @@ function createSafeItoEnvironment(source = process.env, options = {}) {
|
|||
if (key.startsWith("LC_")) copyDefined(source, safe, key);
|
||||
}
|
||||
|
||||
if (options.includeItoRuntime) {
|
||||
for (const key of ITO_RUNTIME_ENVIRONMENT_KEYS) {
|
||||
copyDefined(source, safe, key);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.includeControls) {
|
||||
copyDefined(source, safe, "ECC_DRY_RUN");
|
||||
copyDefined(source, safe, "NODE_ENV");
|
||||
if (source.NODE_ENV === "test") {
|
||||
copyDefined(source, safe, "ECC_ITO_BROWSER_EXECUTABLE");
|
||||
for (const key of ECC_ITO_CONTROL_KEYS) {
|
||||
copyDefined(source, safe, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,6 +66,8 @@ function createSafeItoEnvironment(source = process.env, options = {}) {
|
|||
}
|
||||
|
||||
module.exports = Object.freeze({
|
||||
ECC_ITO_CONTROL_KEYS,
|
||||
ITO_RUNTIME_ENVIRONMENT_KEYS,
|
||||
SYSTEM_ENVIRONMENT_KEYS,
|
||||
createSafeItoEnvironment,
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue