replicas-cli 0.3.284 → 0.3.286
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/dist/index.cjs +184 -13
- package/package.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -15880,7 +15880,7 @@ var init_sandbox = __esm({
|
|
|
15880
15880
|
});
|
|
15881
15881
|
|
|
15882
15882
|
// ../shared/src/pricing.ts
|
|
15883
|
-
var TRIAL_CREDIT_CENTS, SCOUT_SOURCE, WORKSPACE_SOURCES, CLIENT_MESSAGE_SOURCES, MESSAGE_SOURCES, AUTOMATED_SOURCES, AUTOMATED_SOURCES_SQL, PLANS, TEAM_PLAN, ENTERPRISE_PLAN;
|
|
15883
|
+
var TRIAL_CREDIT_CENTS, SCOUT_SOURCE, WORKSPACE_SOURCES, CLIENT_MESSAGE_SOURCES, MESSAGE_SOURCES, AUTOMATED_SOURCES, AUTOMATED_SOURCES_SQL, EGRESS_MICRODOLLARS_PER_GB, EGRESS_RATE_LABEL, PLANS, TEAM_PLAN, ENTERPRISE_PLAN;
|
|
15884
15884
|
var init_pricing = __esm({
|
|
15885
15885
|
"../shared/src/pricing.ts"() {
|
|
15886
15886
|
"use strict";
|
|
@@ -15892,6 +15892,8 @@ var init_pricing = __esm({
|
|
|
15892
15892
|
MESSAGE_SOURCES = [...CLIENT_MESSAGE_SOURCES, ...WORKSPACE_SOURCES];
|
|
15893
15893
|
AUTOMATED_SOURCES = ["api", "automation"];
|
|
15894
15894
|
AUTOMATED_SOURCES_SQL = AUTOMATED_SOURCES.map((s) => `'${s}'`).join(", ");
|
|
15895
|
+
EGRESS_MICRODOLLARS_PER_GB = 15e4;
|
|
15896
|
+
EGRESS_RATE_LABEL = `$${(EGRESS_MICRODOLLARS_PER_GB / 1e6).toFixed(2)}/GB`;
|
|
15895
15897
|
PLANS = {
|
|
15896
15898
|
trial: {
|
|
15897
15899
|
id: "trial",
|
|
@@ -17208,7 +17210,7 @@ var init_workspace_identity = __esm({
|
|
|
17208
17210
|
});
|
|
17209
17211
|
|
|
17210
17212
|
// ../shared/src/routes/environment.ts
|
|
17211
|
-
var ENVIRONMENT_MCP_TRANSPORTS, stringMap, createEnvironmentMcpRequestSchema, updateEnvironmentMcpRequestSchema, screenPasswordSchema, createEnvironmentDeviceRequestSchema, updateEnvironmentDeviceRequestSchema, provisionedDeviceSchema;
|
|
17213
|
+
var ENVIRONMENT_MCP_TRANSPORTS, stringMap, createEnvironmentMcpRequestSchema, updateEnvironmentMcpRequestSchema, screenPasswordSchema, hostSchema, createEnvironmentDeviceRequestSchema, updateEnvironmentDeviceRequestSchema, deviceConnectionResponseSchema, deviceHostKeysRequestSchema, DEVICE_CONNECT_COMMAND, provisionedDeviceSchema;
|
|
17212
17214
|
var init_environment = __esm({
|
|
17213
17215
|
"../shared/src/routes/environment.ts"() {
|
|
17214
17216
|
"use strict";
|
|
@@ -17227,19 +17229,30 @@ var init_environment = __esm({
|
|
|
17227
17229
|
});
|
|
17228
17230
|
updateEnvironmentMcpRequestSchema = createEnvironmentMcpRequestSchema.partial();
|
|
17229
17231
|
screenPasswordSchema = external_exports.string().min(1, "Enter the screen password").max(256).describe('Screen Sharing password, for `replicas computer screen`. On a Mac, the "VNC viewers may control screen with password" password. Never returned.');
|
|
17232
|
+
hostSchema = external_exports.string().max(253).regex(/^(?:[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?|[0-9A-Fa-f:]+)$/, "Enter a hostname or IP address");
|
|
17230
17233
|
createEnvironmentDeviceRequestSchema = external_exports.object({
|
|
17231
17234
|
name: external_exports.string().regex(/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/, "Use lowercase letters, numbers and dashes").describe("SSH alias in workspaces, e.g. `mac-mini` for `ssh mac-mini`."),
|
|
17232
|
-
host:
|
|
17233
|
-
port: external_exports.number().int().min(1).max(65535).optional().describe("SSH port. Defaults to 22."),
|
|
17235
|
+
host: hostSchema.optional().describe("Hostname or IP, such as a Tailscale MagicDNS name (`mac-mini`) or a public address. Leave it out when the device connects to Replicas itself with `replicas device connect`."),
|
|
17236
|
+
port: external_exports.number().int().min(1).max(65535).optional().describe("SSH port on the device. Defaults to 22."),
|
|
17234
17237
|
username: external_exports.string().regex(/^[A-Za-z0-9_][A-Za-z0-9._-]{0,63}$/, "Enter the account name on the device").describe("Account the agent signs in as."),
|
|
17235
17238
|
screen_password: screenPasswordSchema.optional()
|
|
17236
17239
|
});
|
|
17237
17240
|
updateEnvironmentDeviceRequestSchema = createEnvironmentDeviceRequestSchema.partial().extend({
|
|
17241
|
+
host: external_exports.union([hostSchema, external_exports.literal("")]).optional().describe("New hostname or IP, or an empty string for a device that connects to Replicas itself."),
|
|
17238
17242
|
screen_password: external_exports.union([screenPasswordSchema, external_exports.literal("")]).optional().describe("New Screen Sharing password, or an empty string to remove it.")
|
|
17239
17243
|
});
|
|
17244
|
+
deviceConnectionResponseSchema = external_exports.object({
|
|
17245
|
+
name: external_exports.string(),
|
|
17246
|
+
port: external_exports.number(),
|
|
17247
|
+
workspace_ids: external_exports.array(external_exports.string())
|
|
17248
|
+
});
|
|
17249
|
+
deviceHostKeysRequestSchema = external_exports.object({
|
|
17250
|
+
host_keys: external_exports.array(external_exports.string().max(2048).regex(/^[a-z0-9@.-]+ [A-Za-z0-9+/]+={0,3}$/, "Each host key must be `<type> <base64>`")).min(1).max(8).transform((keys) => [...new Set(keys)].sort())
|
|
17251
|
+
});
|
|
17252
|
+
DEVICE_CONNECT_COMMAND = "npx -y replicas-cli device connect";
|
|
17240
17253
|
provisionedDeviceSchema = external_exports.object({
|
|
17241
17254
|
name: external_exports.string(),
|
|
17242
|
-
host: external_exports.string(),
|
|
17255
|
+
host: external_exports.string().nullable(),
|
|
17243
17256
|
port: external_exports.number(),
|
|
17244
17257
|
username: external_exports.string(),
|
|
17245
17258
|
private_key: external_exports.string(),
|
|
@@ -31704,7 +31717,7 @@ var CLI_VERSION;
|
|
|
31704
31717
|
var init_cli_version = __esm({
|
|
31705
31718
|
"../shared/src/cli-version.ts"() {
|
|
31706
31719
|
"use strict";
|
|
31707
|
-
CLI_VERSION = "0.3.
|
|
31720
|
+
CLI_VERSION = "0.3.286";
|
|
31708
31721
|
}
|
|
31709
31722
|
});
|
|
31710
31723
|
|
|
@@ -42288,6 +42301,29 @@ var init_variable_requests = __esm({
|
|
|
42288
42301
|
}
|
|
42289
42302
|
});
|
|
42290
42303
|
|
|
42304
|
+
// ../shared/src/websocket-liveness.ts
|
|
42305
|
+
function trackWebSocketLiveness(socket) {
|
|
42306
|
+
let answered = true;
|
|
42307
|
+
for (const event of ["pong", "message"]) socket.on(event, () => {
|
|
42308
|
+
answered = true;
|
|
42309
|
+
});
|
|
42310
|
+
return () => {
|
|
42311
|
+
if (socket.isPaused) {
|
|
42312
|
+
answered = true;
|
|
42313
|
+
return true;
|
|
42314
|
+
}
|
|
42315
|
+
if (!answered) return false;
|
|
42316
|
+
answered = false;
|
|
42317
|
+
socket.ping();
|
|
42318
|
+
return true;
|
|
42319
|
+
};
|
|
42320
|
+
}
|
|
42321
|
+
var init_websocket_liveness = __esm({
|
|
42322
|
+
"../shared/src/websocket-liveness.ts"() {
|
|
42323
|
+
"use strict";
|
|
42324
|
+
}
|
|
42325
|
+
});
|
|
42326
|
+
|
|
42291
42327
|
// ../shared/src/index.ts
|
|
42292
42328
|
var init_src = __esm({
|
|
42293
42329
|
"../shared/src/index.ts"() {
|
|
@@ -42465,6 +42501,7 @@ var init_src = __esm({
|
|
|
42465
42501
|
init_environment_setups();
|
|
42466
42502
|
init_variable_requests();
|
|
42467
42503
|
init_api_casing();
|
|
42504
|
+
init_websocket_liveness();
|
|
42468
42505
|
}
|
|
42469
42506
|
});
|
|
42470
42507
|
|
|
@@ -45428,16 +45465,24 @@ async function envDevicesListCommand(env) {
|
|
|
45428
45465
|
`/v1/environments/${await resolveEnvironmentId(env)}/devices`
|
|
45429
45466
|
);
|
|
45430
45467
|
output(response, ({ environment_devices }) => {
|
|
45431
|
-
for (const device of environment_devices)
|
|
45468
|
+
for (const device of environment_devices) {
|
|
45469
|
+
console.log(`${device.id} ${device.name} ${device.host === null ? `${device.username}, connects from the device` : `${device.username}@${device.host}:${device.port}`}`);
|
|
45470
|
+
}
|
|
45432
45471
|
});
|
|
45433
45472
|
}
|
|
45434
45473
|
async function envDevicesAddCommand(env, name, options) {
|
|
45435
45474
|
const id = await resolveEnvironmentId(env);
|
|
45436
45475
|
const body = { name, host: options.host, username: options.user, port: options.port, screen_password: options.screenPassword };
|
|
45437
45476
|
const response = await orgAuthenticatedFetch(`/v1/environments/${id}/devices`, { method: "POST", body });
|
|
45438
|
-
|
|
45477
|
+
const connect2 = options.host === void 0 ? await orgAuthenticatedFetch(`/v1/environments/${id}/devices/${response.environment_device.id}/connect-token`) : void 0;
|
|
45478
|
+
output({ ...response, connect_token: connect2?.token }, ({ environment_device, connect_token }) => {
|
|
45439
45479
|
console.log(import_chalk20.default.green(`Added device ${name}. Allow its key by running this on the device as ${options.user}:`));
|
|
45440
45480
|
console.log(`mkdir -p ~/.ssh && echo '${environment_device.public_key}' >> ~/.ssh/authorized_keys`);
|
|
45481
|
+
if (connect_token) {
|
|
45482
|
+
console.log(import_chalk20.default.green("Then keep this running on it, so workspaces can reach it, and paste this token when it asks:"));
|
|
45483
|
+
console.log(DEVICE_CONNECT_COMMAND);
|
|
45484
|
+
console.log(connect_token);
|
|
45485
|
+
}
|
|
45441
45486
|
});
|
|
45442
45487
|
}
|
|
45443
45488
|
async function envDevicesRemoveCommand(env, nameOrId) {
|
|
@@ -45448,6 +45493,18 @@ async function envDevicesRemoveCommand(env, nameOrId) {
|
|
|
45448
45493
|
await orgAuthenticatedFetch(`/v1/environments/${id}/devices/${device.id}`, { method: "DELETE" });
|
|
45449
45494
|
outputSuccess(`Removed device ${device.name}`);
|
|
45450
45495
|
}
|
|
45496
|
+
async function envDevicesResetCommand(env, nameOrId) {
|
|
45497
|
+
const id = await resolveEnvironmentId(env);
|
|
45498
|
+
const { environment_devices } = await orgAuthenticatedFetch(`/v1/environments/${id}/devices`);
|
|
45499
|
+
const device = resolveByNameOrId(nameOrId, environment_devices);
|
|
45500
|
+
if (!device) throw new Error(`Device not found: ${nameOrId}`);
|
|
45501
|
+
const response = await orgAuthenticatedFetch(`/v1/environments/${id}/devices/${device.id}/connect-token`, { method: "POST" });
|
|
45502
|
+
output(response, ({ token }) => {
|
|
45503
|
+
console.log(import_chalk20.default.green(`Reset ${device.name}'s connection. Its old token no longer works; run this on it again and paste the new token when it asks:`));
|
|
45504
|
+
console.log(DEVICE_CONNECT_COMMAND);
|
|
45505
|
+
console.log(token);
|
|
45506
|
+
});
|
|
45507
|
+
}
|
|
45451
45508
|
function envWarmPoolCommand(enabled) {
|
|
45452
45509
|
return async (env) => {
|
|
45453
45510
|
const id = await resolveEnvironmentId(env);
|
|
@@ -48592,6 +48649,116 @@ trim "$1"`;
|
|
|
48592
48649
|
}
|
|
48593
48650
|
});
|
|
48594
48651
|
|
|
48652
|
+
// src/commands/device.ts
|
|
48653
|
+
async function refuse(response) {
|
|
48654
|
+
const body = await response.json().catch(() => null);
|
|
48655
|
+
throw new Error(body && typeof body === "object" && "error" in body ? String(body.error) : `Replicas refused this device (${response.status})`);
|
|
48656
|
+
}
|
|
48657
|
+
async function scanHostKeys(port) {
|
|
48658
|
+
const { stdout } = await (0, import_node_util.promisify)(import_node_child_process4.execFile)("ssh-keyscan", ["-T", "5", "-p", String(port), "127.0.0.1"]).catch(() => ({ stdout: "" }));
|
|
48659
|
+
return stdout.split("\n").filter((line) => line && !line.startsWith("#")).map((line) => line.split(" ").slice(1, 3).join(" "));
|
|
48660
|
+
}
|
|
48661
|
+
async function deviceConnectCommand() {
|
|
48662
|
+
const token = process.env.REPLICAS_DEVICE_TOKEN ?? (isInteractive() ? (await ask({ type: "password", name: "token", message: "Device token, from the device in the environment's Devices settings" })).token : void 0);
|
|
48663
|
+
if (!token) throw new Error("Paste the device token when asked, or set REPLICAS_DEVICE_TOKEN to it. It's on the device in the environment's Devices settings.");
|
|
48664
|
+
const headers = { Authorization: `Bearer ${token}` };
|
|
48665
|
+
let pinned = false;
|
|
48666
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
48667
|
+
let port = 22;
|
|
48668
|
+
let summary = "";
|
|
48669
|
+
const wait = (workspaceId, failures = 0) => {
|
|
48670
|
+
if (!waiting.has(workspaceId)) return;
|
|
48671
|
+
const startedAt = Date.now();
|
|
48672
|
+
let opened = false;
|
|
48673
|
+
const socket = new import_ws.default(`${MONOLITH_URL.replace(/^http/, "ws")}/v1/devices/workspaces/${workspaceId}/ws`, { headers, handshakeTimeout: HEARTBEAT_MS });
|
|
48674
|
+
waiting.set(workspaceId, socket);
|
|
48675
|
+
const alive = trackWebSocketLiveness(socket);
|
|
48676
|
+
let heartbeat;
|
|
48677
|
+
socket.once("open", () => {
|
|
48678
|
+
heartbeat = setInterval(() => {
|
|
48679
|
+
if (!alive()) socket.terminate();
|
|
48680
|
+
}, HEARTBEAT_MS);
|
|
48681
|
+
});
|
|
48682
|
+
socket.once("message", (data, isBinary) => {
|
|
48683
|
+
if (isBinary || data.toString() !== "open") return socket.close();
|
|
48684
|
+
opened = true;
|
|
48685
|
+
wait(workspaceId);
|
|
48686
|
+
const sshd = (0, import_node_net.connect)(port, "127.0.0.1");
|
|
48687
|
+
const stream = (0, import_ws.createWebSocketStream)(socket);
|
|
48688
|
+
const close = () => {
|
|
48689
|
+
sshd.destroy();
|
|
48690
|
+
stream.destroy();
|
|
48691
|
+
};
|
|
48692
|
+
sshd.once("error", (error51) => {
|
|
48693
|
+
console.log(import_chalk32.default.yellow(`A workspace couldn't reach SSH on port ${port} here (${error51.message}). ${REMOTE_LOGIN_HINT}`));
|
|
48694
|
+
});
|
|
48695
|
+
sshd.on("error", close).on("close", close);
|
|
48696
|
+
stream.on("error", close).on("close", close);
|
|
48697
|
+
sshd.pipe(stream).pipe(sshd);
|
|
48698
|
+
});
|
|
48699
|
+
socket.on("error", () => void 0);
|
|
48700
|
+
socket.once("close", () => {
|
|
48701
|
+
clearInterval(heartbeat);
|
|
48702
|
+
if (opened) return;
|
|
48703
|
+
const failed = Date.now() - startedAt < POLL_INTERVAL_MS;
|
|
48704
|
+
const delay = failed ? Math.min(5e3 * 2 ** failures, MAX_RETRY_DELAY_MS) : 0;
|
|
48705
|
+
setTimeout(() => {
|
|
48706
|
+
if (waiting.get(workspaceId) === socket) wait(workspaceId, failed ? failures + 1 : 0);
|
|
48707
|
+
}, delay).unref();
|
|
48708
|
+
});
|
|
48709
|
+
};
|
|
48710
|
+
for (; ; ) {
|
|
48711
|
+
const response = await fetch(`${MONOLITH_URL}/v1/devices/workspaces`, { headers }).catch(() => null);
|
|
48712
|
+
if (response && [401, 403, 409].includes(response.status)) await refuse(response);
|
|
48713
|
+
const device = response?.ok ? deviceConnectionResponseSchema.parse(await response.json()) : void 0;
|
|
48714
|
+
let problem = device ? void 0 : `Can't reach Replicas (${response?.status ?? "network error"}), retrying...`;
|
|
48715
|
+
if (device && !pinned) {
|
|
48716
|
+
const body = { host_keys: await scanHostKeys(device.port) };
|
|
48717
|
+
const pin = body.host_keys.length === 0 ? void 0 : await fetch(`${MONOLITH_URL}/v1/devices/host-keys`, { method: "PUT", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify(body) }).catch(() => null);
|
|
48718
|
+
if (pin && [400, 401, 403, 409].includes(pin.status)) await refuse(pin);
|
|
48719
|
+
pinned = pin?.ok === true;
|
|
48720
|
+
if (pin === void 0) problem = `Can't reach SSH on port ${device.port} here, retrying... ${REMOTE_LOGIN_HINT}`;
|
|
48721
|
+
else if (!pinned) problem = `Can't reach Replicas (${pin?.status ?? "network error"}), retrying...`;
|
|
48722
|
+
}
|
|
48723
|
+
if (device && pinned) {
|
|
48724
|
+
port = device.port;
|
|
48725
|
+
for (const [workspaceId, socket] of waiting) {
|
|
48726
|
+
if (device.workspace_ids.includes(workspaceId)) continue;
|
|
48727
|
+
waiting.delete(workspaceId);
|
|
48728
|
+
socket?.close();
|
|
48729
|
+
}
|
|
48730
|
+
for (const workspaceId of device.workspace_ids) {
|
|
48731
|
+
if (waiting.has(workspaceId)) continue;
|
|
48732
|
+
waiting.set(workspaceId, void 0);
|
|
48733
|
+
wait(workspaceId);
|
|
48734
|
+
}
|
|
48735
|
+
}
|
|
48736
|
+
const next = problem ?? `\u2713 ${device?.name}: ready for ${waiting.size} ${waiting.size === 1 ? "workspace" : "workspaces"}, forwarding to SSH on port ${port}`;
|
|
48737
|
+
if (next !== summary) console.log(problem ? import_chalk32.default.yellow(next) : import_chalk32.default.green(next));
|
|
48738
|
+
summary = next;
|
|
48739
|
+
await (0, import_promises2.setTimeout)(POLL_INTERVAL_MS);
|
|
48740
|
+
}
|
|
48741
|
+
}
|
|
48742
|
+
var import_chalk32, import_node_child_process4, import_node_net, import_promises2, import_node_util, import_ws, POLL_INTERVAL_MS, HEARTBEAT_MS, MAX_RETRY_DELAY_MS, REMOTE_LOGIN_HINT;
|
|
48743
|
+
var init_device = __esm({
|
|
48744
|
+
"src/commands/device.ts"() {
|
|
48745
|
+
"use strict";
|
|
48746
|
+
import_chalk32 = __toESM(require("chalk"));
|
|
48747
|
+
import_node_child_process4 = require("child_process");
|
|
48748
|
+
import_node_net = require("net");
|
|
48749
|
+
import_promises2 = require("timers/promises");
|
|
48750
|
+
import_node_util = require("util");
|
|
48751
|
+
import_ws = __toESM(require("ws"));
|
|
48752
|
+
init_src();
|
|
48753
|
+
init_monolith_url();
|
|
48754
|
+
init_output();
|
|
48755
|
+
POLL_INTERVAL_MS = 1e4;
|
|
48756
|
+
HEARTBEAT_MS = 3e4;
|
|
48757
|
+
MAX_RETRY_DELAY_MS = 5 * 6e4;
|
|
48758
|
+
REMOTE_LOGIN_HINT = "On a Mac, turn on Remote Login in System Settings \u2192 General \u2192 Sharing.";
|
|
48759
|
+
}
|
|
48760
|
+
});
|
|
48761
|
+
|
|
48595
48762
|
// src/index.ts
|
|
48596
48763
|
var src_exports = {};
|
|
48597
48764
|
function parseBooleanOption(value) {
|
|
@@ -48663,6 +48830,7 @@ var init_src2 = __esm({
|
|
|
48663
48830
|
init_slack4();
|
|
48664
48831
|
init_service();
|
|
48665
48832
|
init_environment3();
|
|
48833
|
+
init_device();
|
|
48666
48834
|
init_config();
|
|
48667
48835
|
init_organization2();
|
|
48668
48836
|
init_output();
|
|
@@ -48680,6 +48848,7 @@ var init_src2 = __esm({
|
|
|
48680
48848
|
"codex-auth",
|
|
48681
48849
|
"muse-auth",
|
|
48682
48850
|
"config",
|
|
48851
|
+
"device",
|
|
48683
48852
|
"init",
|
|
48684
48853
|
"login",
|
|
48685
48854
|
"logout",
|
|
@@ -48858,12 +49027,14 @@ var init_src2 = __esm({
|
|
|
48858
49027
|
envMcps.command("remove <env> <name-or-id>").description("Remove an MCP server").action(run(envMcpsRemoveCommand));
|
|
48859
49028
|
envDevices = environment.command("devices", { hidden: true }).description("Computers the agent reaches over SSH, like a Mac mini or your own laptop");
|
|
48860
49029
|
envDevices.command("list <env>").description("List devices").action(run(envDevicesListCommand));
|
|
48861
|
-
envDevices.command("add <env> <name>").description("Add a device and print the command that allows its key on the device").
|
|
49030
|
+
envDevices.command("add <env> <name>").description("Add a device and print the command that allows its key on the device").option("--host <host>", "Hostname or IP, such as its Tailscale name. Leave it out to connect from the device instead").requiredOption("--user <user>", "Account the agent signs in as").option("--port <port>", "SSH port (default 22)", (value) => {
|
|
48862
49031
|
const port = Number(value);
|
|
48863
49032
|
if (!Number.isInteger(port)) throw new import_commander.InvalidArgumentError("must be a whole number");
|
|
48864
49033
|
return port;
|
|
48865
49034
|
}).option("--screen-password <password>", "Screen Sharing password, so the agent can see and control the screen").action(run(envDevicesAddCommand));
|
|
48866
49035
|
envDevices.command("remove <env> <name-or-id>").description("Remove a device and delete its key").action(run(envDevicesRemoveCommand));
|
|
49036
|
+
envDevices.command("reset <env> <name-or-id>").description("Replace the connect token of a device that connects from itself and forget its SSH identity, for a leaked token or a reinstalled device").action(run(envDevicesResetCommand));
|
|
49037
|
+
program.command("device", { hidden: true }).description("Run on one of your computers so Replicas workspaces can reach it").command("connect").description("Keep this computer reachable from the workspaces that have it, without opening ports or a tailnet. Asks for the token from the device's settings, or reads REPLICAS_DEVICE_TOKEN").action(run(deviceConnectCommand));
|
|
48867
49038
|
envWarmPool = environment.command("warm-pool").description("Keep pre-warmed workspaces ready for an environment");
|
|
48868
49039
|
envWarmPool.command("enable <env>").description("Enable the warm pool").action(run(envWarmPoolCommand(true)));
|
|
48869
49040
|
envWarmPool.command("disable <env>").description("Disable the warm pool").action(run(envWarmPoolCommand(false)));
|
|
@@ -48975,12 +49146,12 @@ ${VISUAL_PAGE_RULES}`).action(run(visualizeCommand));
|
|
|
48975
49146
|
});
|
|
48976
49147
|
|
|
48977
49148
|
// src/bin.ts
|
|
48978
|
-
var
|
|
48979
|
-
var
|
|
49149
|
+
var import_node_child_process5 = require("child_process");
|
|
49150
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
48980
49151
|
init_config();
|
|
48981
49152
|
if (process.argv[2] === "computer" && isAgentMode()) {
|
|
48982
|
-
const result = (0,
|
|
48983
|
-
if (result.error) console.error(
|
|
49153
|
+
const result = (0, import_node_child_process5.spawnSync)("replicas-computer", process.argv.slice(3), { stdio: "inherit" });
|
|
49154
|
+
if (result.error) console.error(import_chalk33.default.red(`
|
|
48984
49155
|
\u2717 replicas-computer is unavailable in this workspace image
|
|
48985
49156
|
`));
|
|
48986
49157
|
process.exitCode = result.error ? 1 : result.status ?? 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replicas-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.286",
|
|
4
4
|
"description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"bin": {
|
|
@@ -47,11 +47,13 @@
|
|
|
47
47
|
"dotenv": "^17.2.3",
|
|
48
48
|
"open": "^8.4.2",
|
|
49
49
|
"prompts": "^2.4.2",
|
|
50
|
-
"tslib": "^2.6.2"
|
|
50
|
+
"tslib": "^2.6.2",
|
|
51
|
+
"ws": "8.21.0"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
54
|
"@replicas/shared": "*",
|
|
54
55
|
"@types/node": "^20.11.0",
|
|
56
|
+
"@types/ws": "8.18.1",
|
|
55
57
|
"tsup": "^8.5.0",
|
|
56
58
|
"typescript": "^5.3.3"
|
|
57
59
|
}
|