@instafy/cli 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bind/src/config.mjs +5 -16
- package/dist/bind/src/detached-env.mjs +27 -0
- package/dist/cli.js +236 -114
- package/package.json +1 -1
package/dist/bind/src/config.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { credentialFreeEnv } from "./detached-env.mjs";
|
|
6
7
|
|
|
7
8
|
export const DEFAULT_CONTROLLER_URL = "http://127.0.0.1:8788";
|
|
8
9
|
|
|
@@ -145,7 +146,6 @@ export function agentUsesOwnerConversation(config) {
|
|
|
145
146
|
|
|
146
147
|
/** Credential names the binder itself reads as the owner's login (above). */
|
|
147
148
|
const BINDER_LOGIN_ENV = ["INSTAFY_ACCESS_TOKEN", "SUPABASE_ACCESS_TOKEN"];
|
|
148
|
-
const CREDENTIAL_ENV_RE = /(TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|PRIVATE|(^|_)KEY$|_KEY_|API_?KEY)/;
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
151
|
* Why --agent-executor is refused here, or null. It treats a click from this machine's loopback as the owner's own,
|
|
@@ -158,23 +158,12 @@ export function agentExecutorRefusal(env = process.env) {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
|
-
* The environment of a binder started with `serve --detach
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
* names passed with --allow-env, and the owner's login only with `keepLogin` (--agent-executor, the one mode that
|
|
161
|
+
* The environment of a binder started with `serve --detach`: `credentialFreeEnv` (no runtime job token or other
|
|
162
|
+
* CONTROLLER_* secret, no SUPABASE_* service values, no credential-like names such as space secrets), except names
|
|
163
|
+
* passed with --allow-env, and the owner's login only with `keepLogin` (--agent-executor, the one mode that
|
|
165
164
|
* dispatches Octo turns itself). Everything else (PATH, HOME, the space and conversation ids, plain settings a
|
|
166
165
|
* handler may declare in `env`) is kept.
|
|
167
166
|
*/
|
|
168
167
|
export function detachedBinderEnv(env = process.env, allowEnv = [], { keepLogin = false } = {}) {
|
|
169
|
-
|
|
170
|
-
for (const [name, value] of Object.entries(env)) {
|
|
171
|
-
if (value === undefined) continue;
|
|
172
|
-
const keep =
|
|
173
|
-
(keepLogin && BINDER_LOGIN_ENV.includes(name)) ||
|
|
174
|
-
allowEnv.includes(name) ||
|
|
175
|
-
name === "CONTROLLER_BASE_URL" ||
|
|
176
|
-
(!/^(CONTROLLER|SUPABASE)_/.test(name) && !CREDENTIAL_ENV_RE.test(name));
|
|
177
|
-
if (keep) out[name] = value;
|
|
178
|
-
}
|
|
179
|
-
return out;
|
|
168
|
+
return credentialFreeEnv(env, [...allowEnv, ...(keepLogin ? BINDER_LOGIN_ENV : [])]);
|
|
180
169
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// The environment of a process started to outlive the command (or Octo turn) that started it.
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
//
|
|
4
|
+
// Shared by `instafy bind serve --detach` and `instafy preview open --start` (the CLI loads this file from its bundled
|
|
5
|
+
// copy of the binder), so both strip the same credentials.
|
|
6
|
+
|
|
7
|
+
const CREDENTIAL_ENV_RE = /(TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|PRIVATE|(^|_)KEY$|_KEY_|API_?KEY)/;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* `env` without credentials a long-lived process does not need. Such a process outlives the job that started it, and
|
|
11
|
+
* every process in a runtime can read its /proc/<pid>/environ, so it keeps no runtime job token or other CONTROLLER_*
|
|
12
|
+
* value (except CONTROLLER_BASE_URL, which is not secret), no SUPABASE_* value and no credential-like name (space
|
|
13
|
+
* secrets, logins). Names in `keep` are kept anyway. Everything else (PATH, HOME, the space and conversation ids,
|
|
14
|
+
* plain settings) is kept.
|
|
15
|
+
*/
|
|
16
|
+
export function credentialFreeEnv(env = process.env, keep = []) {
|
|
17
|
+
const out = {};
|
|
18
|
+
for (const [name, value] of Object.entries(env)) {
|
|
19
|
+
if (value === undefined) continue;
|
|
20
|
+
const kept =
|
|
21
|
+
keep.includes(name) ||
|
|
22
|
+
name === "CONTROLLER_BASE_URL" ||
|
|
23
|
+
(!/^(CONTROLLER|SUPABASE)_/.test(name) && !CREDENTIAL_ENV_RE.test(name));
|
|
24
|
+
if (kept) out[name] = value;
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1717,7 +1717,7 @@ var init_hardware = __esm({
|
|
|
1717
1717
|
|
|
1718
1718
|
// src/index.ts
|
|
1719
1719
|
import { Command, Option } from "commander";
|
|
1720
|
-
import { pathToFileURL as
|
|
1720
|
+
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
1721
1721
|
import { createRequire as createRequire4 } from "node:module";
|
|
1722
1722
|
import kleur20 from "kleur";
|
|
1723
1723
|
|
|
@@ -5770,10 +5770,75 @@ async function tailTunnelLogs(options) {
|
|
|
5770
5770
|
}
|
|
5771
5771
|
|
|
5772
5772
|
// src/preview.ts
|
|
5773
|
-
import
|
|
5773
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
5774
|
+
import fs9 from "node:fs";
|
|
5774
5775
|
import os5 from "node:os";
|
|
5775
|
-
import
|
|
5776
|
+
import path10 from "node:path";
|
|
5776
5777
|
import kleur8 from "kleur";
|
|
5778
|
+
|
|
5779
|
+
// src/bind.ts
|
|
5780
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
5781
|
+
import fs8 from "node:fs";
|
|
5782
|
+
import path9 from "node:path";
|
|
5783
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
|
|
5784
|
+
var BIN_REL = path9.join("bin", "instafy-bind.mjs");
|
|
5785
|
+
function resolveBindBin(env = process.env) {
|
|
5786
|
+
const fromEnv = env.INSTAFY_BIND_BIN?.trim();
|
|
5787
|
+
if (fromEnv) return fs8.existsSync(fromEnv) ? path9.resolve(fromEnv) : null;
|
|
5788
|
+
const here = path9.dirname(fileURLToPath2(import.meta.url));
|
|
5789
|
+
for (const candidate of [path9.join(here, "bind", BIN_REL), path9.resolve(here, "..", "..", "instafy-bind", BIN_REL)]) {
|
|
5790
|
+
if (fs8.existsSync(candidate)) return candidate;
|
|
5791
|
+
}
|
|
5792
|
+
return null;
|
|
5793
|
+
}
|
|
5794
|
+
function bundledBindRoot() {
|
|
5795
|
+
const here = path9.dirname(fileURLToPath2(import.meta.url));
|
|
5796
|
+
for (const candidate of [path9.join(here, "bind"), path9.resolve(here, "..", "..", "instafy-bind")]) {
|
|
5797
|
+
if (fs8.existsSync(path9.join(candidate, BIN_REL))) return candidate;
|
|
5798
|
+
}
|
|
5799
|
+
return null;
|
|
5800
|
+
}
|
|
5801
|
+
async function loadCredentialFreeEnv() {
|
|
5802
|
+
const root = bundledBindRoot();
|
|
5803
|
+
const file = root ? path9.join(root, "src", "detached-env.mjs") : null;
|
|
5804
|
+
if (!file || !fs8.existsSync(file)) {
|
|
5805
|
+
throw new Error("the bundled binder (dist/bind) was not found, so a site cannot be started without credentials; rebuild the CLI");
|
|
5806
|
+
}
|
|
5807
|
+
const module = await import(pathToFileURL(file).href);
|
|
5808
|
+
if (typeof module.credentialFreeEnv !== "function") {
|
|
5809
|
+
throw new Error(`${file} does not export credentialFreeEnv; rebuild the CLI`);
|
|
5810
|
+
}
|
|
5811
|
+
return module.credentialFreeEnv;
|
|
5812
|
+
}
|
|
5813
|
+
function runBind(args) {
|
|
5814
|
+
const bin = resolveBindBin();
|
|
5815
|
+
if (!bin) {
|
|
5816
|
+
process.stderr.write(
|
|
5817
|
+
"instafy bind: the bundled binder (dist/bind) was not found; rebuild the CLI, or set INSTAFY_BIND_BIN to packages/instafy-bind/bin/instafy-bind.mjs, or run it directly: node packages/instafy-bind/bin/instafy-bind.mjs <command>\n"
|
|
5818
|
+
);
|
|
5819
|
+
return Promise.resolve(1);
|
|
5820
|
+
}
|
|
5821
|
+
return new Promise((resolve) => {
|
|
5822
|
+
const child = spawn4(process.execPath, [bin, ...args], { stdio: "inherit", env: process.env });
|
|
5823
|
+
const forward = (signal) => {
|
|
5824
|
+
if (!child.killed) child.kill(signal);
|
|
5825
|
+
};
|
|
5826
|
+
process.on("SIGINT", forward);
|
|
5827
|
+
process.on("SIGTERM", forward);
|
|
5828
|
+
child.on("error", (error) => {
|
|
5829
|
+
process.stderr.write(`instafy bind: ${error.message}
|
|
5830
|
+
`);
|
|
5831
|
+
resolve(1);
|
|
5832
|
+
});
|
|
5833
|
+
child.on("exit", (code, signal) => {
|
|
5834
|
+
process.off("SIGINT", forward);
|
|
5835
|
+
process.off("SIGTERM", forward);
|
|
5836
|
+
resolve(code ?? (signal ? 130 : 1));
|
|
5837
|
+
});
|
|
5838
|
+
});
|
|
5839
|
+
}
|
|
5840
|
+
|
|
5841
|
+
// src/preview.ts
|
|
5777
5842
|
init_controller_fetch();
|
|
5778
5843
|
init_errors();
|
|
5779
5844
|
|
|
@@ -5907,19 +5972,19 @@ var MAX_PATH_LENGTH = 512;
|
|
|
5907
5972
|
var STATE_LOCK_WAIT_MS = 12e4;
|
|
5908
5973
|
var STATE_LOCK_STALE_MS = 10 * 6e4;
|
|
5909
5974
|
function instafyDir() {
|
|
5910
|
-
return
|
|
5975
|
+
return path10.join(os5.homedir(), ".instafy");
|
|
5911
5976
|
}
|
|
5912
5977
|
function previewStateFile() {
|
|
5913
|
-
return
|
|
5978
|
+
return path10.join(instafyDir(), "cli-preview-state.json");
|
|
5914
5979
|
}
|
|
5915
5980
|
function previewStateLockFile() {
|
|
5916
|
-
return
|
|
5981
|
+
return path10.join(instafyDir(), "cli-preview-state.lock");
|
|
5917
5982
|
}
|
|
5918
5983
|
function workdirsRoot() {
|
|
5919
|
-
return
|
|
5984
|
+
return path10.join(instafyDir(), "cli-preview-workdirs");
|
|
5920
5985
|
}
|
|
5921
5986
|
function workdirFor(previewId) {
|
|
5922
|
-
return
|
|
5987
|
+
return path10.join(workdirsRoot(), safePathSegment2(previewId));
|
|
5923
5988
|
}
|
|
5924
5989
|
var HOST_SUFFIX_RE = /^[a-z0-9-]+(?:\.[a-z0-9-]+)*(?::\d{1,5})?$/;
|
|
5925
5990
|
function parseViewerHostSuffix(raw) {
|
|
@@ -5972,7 +6037,7 @@ function parsePreviewPath(raw) {
|
|
|
5972
6037
|
}
|
|
5973
6038
|
function readStateFile2() {
|
|
5974
6039
|
try {
|
|
5975
|
-
const parsed = JSON.parse(
|
|
6040
|
+
const parsed = JSON.parse(fs9.readFileSync(previewStateFile(), "utf8"));
|
|
5976
6041
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.previews)) {
|
|
5977
6042
|
return { version: 1, previews: [] };
|
|
5978
6043
|
}
|
|
@@ -5982,8 +6047,8 @@ function readStateFile2() {
|
|
|
5982
6047
|
}
|
|
5983
6048
|
}
|
|
5984
6049
|
function writeStateFile2(state) {
|
|
5985
|
-
|
|
5986
|
-
|
|
6050
|
+
fs9.mkdirSync(instafyDir(), { recursive: true });
|
|
6051
|
+
fs9.writeFileSync(previewStateFile(), JSON.stringify(state, null, 2), { encoding: "utf8", mode: 384 });
|
|
5987
6052
|
}
|
|
5988
6053
|
function upsertPreviewState(entry) {
|
|
5989
6054
|
const state = readStateFile2();
|
|
@@ -5998,7 +6063,7 @@ function removePreviewState(previewId) {
|
|
|
5998
6063
|
return existing;
|
|
5999
6064
|
}
|
|
6000
6065
|
function removeWorkdir(previewId) {
|
|
6001
|
-
|
|
6066
|
+
fs9.rmSync(workdirFor(previewId), { recursive: true, force: true });
|
|
6002
6067
|
}
|
|
6003
6068
|
function forgetPreview(previewId) {
|
|
6004
6069
|
const entry = removePreviewState(previewId);
|
|
@@ -6010,9 +6075,9 @@ function sleep(ms) {
|
|
|
6010
6075
|
}
|
|
6011
6076
|
function lockHolderIsGone(lockFile) {
|
|
6012
6077
|
try {
|
|
6013
|
-
const stat =
|
|
6078
|
+
const stat = fs9.statSync(lockFile);
|
|
6014
6079
|
if (Date.now() - stat.mtimeMs > STATE_LOCK_STALE_MS) return true;
|
|
6015
|
-
const pid = Number.parseInt(
|
|
6080
|
+
const pid = Number.parseInt(fs9.readFileSync(lockFile, "utf8").trim(), 10);
|
|
6016
6081
|
if (!Number.isInteger(pid) || pid <= 0) return Date.now() - stat.mtimeMs > 5e3;
|
|
6017
6082
|
try {
|
|
6018
6083
|
process.kill(pid, 0);
|
|
@@ -6026,18 +6091,18 @@ function lockHolderIsGone(lockFile) {
|
|
|
6026
6091
|
}
|
|
6027
6092
|
async function withStateLock(fn) {
|
|
6028
6093
|
const lockFile = previewStateLockFile();
|
|
6029
|
-
|
|
6094
|
+
fs9.mkdirSync(instafyDir(), { recursive: true });
|
|
6030
6095
|
const deadline = Date.now() + STATE_LOCK_WAIT_MS;
|
|
6031
6096
|
for (; ; ) {
|
|
6032
6097
|
try {
|
|
6033
|
-
const fd =
|
|
6034
|
-
|
|
6035
|
-
|
|
6098
|
+
const fd = fs9.openSync(lockFile, "wx", 384);
|
|
6099
|
+
fs9.writeSync(fd, String(process.pid));
|
|
6100
|
+
fs9.closeSync(fd);
|
|
6036
6101
|
break;
|
|
6037
6102
|
} catch (error) {
|
|
6038
6103
|
if (error.code !== "EEXIST") throw error;
|
|
6039
6104
|
if (lockHolderIsGone(lockFile)) {
|
|
6040
|
-
|
|
6105
|
+
fs9.rmSync(lockFile, { force: true });
|
|
6041
6106
|
continue;
|
|
6042
6107
|
}
|
|
6043
6108
|
if (Date.now() > deadline) {
|
|
@@ -6049,11 +6114,11 @@ async function withStateLock(fn) {
|
|
|
6049
6114
|
try {
|
|
6050
6115
|
return await fn();
|
|
6051
6116
|
} finally {
|
|
6052
|
-
|
|
6117
|
+
fs9.rmSync(lockFile, { force: true });
|
|
6053
6118
|
}
|
|
6054
6119
|
}
|
|
6055
6120
|
function configPathFor(workdir) {
|
|
6056
|
-
return
|
|
6121
|
+
return path10.join(workdir, "rathole.toml");
|
|
6057
6122
|
}
|
|
6058
6123
|
async function stopClient(entry) {
|
|
6059
6124
|
await stopRatholeClient(entry.pid, configPathFor(entry.workdir), entry.binary);
|
|
@@ -6185,7 +6250,7 @@ async function resolveRathole(ratholeBin, log) {
|
|
|
6185
6250
|
}
|
|
6186
6251
|
function readFileOrNull(file) {
|
|
6187
6252
|
try {
|
|
6188
|
-
return
|
|
6253
|
+
return fs9.readFileSync(file, "utf8");
|
|
6189
6254
|
} catch {
|
|
6190
6255
|
return null;
|
|
6191
6256
|
}
|
|
@@ -6293,8 +6358,8 @@ function parseServeCommand(raw) {
|
|
|
6293
6358
|
}
|
|
6294
6359
|
function realDir(dir) {
|
|
6295
6360
|
try {
|
|
6296
|
-
const resolved =
|
|
6297
|
-
return
|
|
6361
|
+
const resolved = fs9.realpathSync(dir);
|
|
6362
|
+
return fs9.statSync(resolved).isDirectory() ? resolved : null;
|
|
6298
6363
|
} catch {
|
|
6299
6364
|
return null;
|
|
6300
6365
|
}
|
|
@@ -6304,22 +6369,22 @@ function resolveWorkspaceRoot(env, projectId2) {
|
|
|
6304
6369
|
if (explicit) return realDir(explicit);
|
|
6305
6370
|
const base = env["WORKSPACE_DIR"]?.trim();
|
|
6306
6371
|
if (!base || !projectId2) return null;
|
|
6307
|
-
return realDir(
|
|
6372
|
+
return realDir(path10.join(base, projectId2));
|
|
6308
6373
|
}
|
|
6309
6374
|
function workspaceRelativeDir(root, dir) {
|
|
6310
6375
|
const resolved = realDir(dir);
|
|
6311
6376
|
if (!resolved) return null;
|
|
6312
|
-
const relative =
|
|
6377
|
+
const relative = path10.relative(root, resolved);
|
|
6313
6378
|
if (relative === "") return ".";
|
|
6314
|
-
if (relative.startsWith("..") ||
|
|
6315
|
-
return relative.split(
|
|
6379
|
+
if (relative.startsWith("..") || path10.isAbsolute(relative)) return null;
|
|
6380
|
+
return relative.split(path10.sep).join("/");
|
|
6316
6381
|
}
|
|
6317
6382
|
function listeningSocketInodes(procRoot, port) {
|
|
6318
6383
|
const inodes = /* @__PURE__ */ new Set();
|
|
6319
6384
|
for (const table of ["net/tcp", "net/tcp6"]) {
|
|
6320
6385
|
let text2 = "";
|
|
6321
6386
|
try {
|
|
6322
|
-
text2 =
|
|
6387
|
+
text2 = fs9.readFileSync(path10.join(procRoot, table), "utf8");
|
|
6323
6388
|
} catch {
|
|
6324
6389
|
continue;
|
|
6325
6390
|
}
|
|
@@ -6340,20 +6405,20 @@ function detectListeningProcess(port, procRoot = "/proc") {
|
|
|
6340
6405
|
if (inodes.size === 0) return null;
|
|
6341
6406
|
let pids = [];
|
|
6342
6407
|
try {
|
|
6343
|
-
pids =
|
|
6408
|
+
pids = fs9.readdirSync(procRoot).filter((name) => /^\d+$/.test(name)).map(Number).sort((a, b) => a - b);
|
|
6344
6409
|
} catch {
|
|
6345
6410
|
return null;
|
|
6346
6411
|
}
|
|
6347
6412
|
for (const pid of pids) {
|
|
6348
6413
|
let fds = [];
|
|
6349
6414
|
try {
|
|
6350
|
-
fds =
|
|
6415
|
+
fds = fs9.readdirSync(path10.join(procRoot, String(pid), "fd"));
|
|
6351
6416
|
} catch {
|
|
6352
6417
|
continue;
|
|
6353
6418
|
}
|
|
6354
6419
|
const holds = fds.some((fd) => {
|
|
6355
6420
|
try {
|
|
6356
|
-
const target =
|
|
6421
|
+
const target = fs9.readlinkSync(path10.join(procRoot, String(pid), "fd", fd));
|
|
6357
6422
|
const match = /^socket:\[(\d+)\]$/.exec(target);
|
|
6358
6423
|
return Boolean(match && inodes.has(match[1]));
|
|
6359
6424
|
} catch {
|
|
@@ -6362,9 +6427,9 @@ function detectListeningProcess(port, procRoot = "/proc") {
|
|
|
6362
6427
|
});
|
|
6363
6428
|
if (!holds) continue;
|
|
6364
6429
|
try {
|
|
6365
|
-
const argv =
|
|
6430
|
+
const argv = fs9.readFileSync(path10.join(procRoot, String(pid), "cmdline"), "utf8").split("\0");
|
|
6366
6431
|
while (argv.length > 0 && argv[argv.length - 1] === "") argv.pop();
|
|
6367
|
-
const cwd =
|
|
6432
|
+
const cwd = fs9.readlinkSync(path10.join(procRoot, String(pid), "cwd"));
|
|
6368
6433
|
if (argv.length === 0) continue;
|
|
6369
6434
|
return { pid, argv, cwd };
|
|
6370
6435
|
} catch {
|
|
@@ -6445,6 +6510,66 @@ function resolvePreviewServe(params) {
|
|
|
6445
6510
|
}
|
|
6446
6511
|
return { serve: { argv: found.argv, cwd } };
|
|
6447
6512
|
}
|
|
6513
|
+
var START_PORT_WAIT_MS = 6e4;
|
|
6514
|
+
var START_POLL_MS = 300;
|
|
6515
|
+
var START_PROBE_TIMEOUT_MS = 2e3;
|
|
6516
|
+
var START_LOGS_DIR = path10.join(".instafy", "agent", "logs");
|
|
6517
|
+
function displayPath(file, cwd = process.cwd()) {
|
|
6518
|
+
const relative = path10.relative(cwd, file);
|
|
6519
|
+
return relative && !relative.startsWith("..") && !path10.isAbsolute(relative) ? relative : file;
|
|
6520
|
+
}
|
|
6521
|
+
async function startPreviewSite(params) {
|
|
6522
|
+
const { port, sitePath, serve, root } = params;
|
|
6523
|
+
const credentialFreeEnv = await loadCredentialFreeEnv();
|
|
6524
|
+
const env = credentialFreeEnv(params.env ?? process.env);
|
|
6525
|
+
const cwd = path10.join(root, serve.cwd);
|
|
6526
|
+
const logFile = path10.join(root, START_LOGS_DIR, `preview-${port}.log`);
|
|
6527
|
+
fs9.mkdirSync(path10.dirname(logFile), { recursive: true });
|
|
6528
|
+
const fd = fs9.openSync(logFile, "a", 384);
|
|
6529
|
+
let exit = null;
|
|
6530
|
+
let spawnError = null;
|
|
6531
|
+
let pid;
|
|
6532
|
+
try {
|
|
6533
|
+
fs9.writeSync(fd, `
|
|
6534
|
+
[instafy preview open --start] ${(/* @__PURE__ */ new Date()).toISOString()} ${JSON.stringify(serve.argv)} in ${serve.cwd}
|
|
6535
|
+
`);
|
|
6536
|
+
const child = spawn5(serve.argv[0], serve.argv.slice(1), {
|
|
6537
|
+
cwd,
|
|
6538
|
+
env,
|
|
6539
|
+
detached: true,
|
|
6540
|
+
stdio: ["ignore", fd, fd]
|
|
6541
|
+
});
|
|
6542
|
+
child.on("error", (error) => {
|
|
6543
|
+
spawnError = error.code ?? error.message;
|
|
6544
|
+
});
|
|
6545
|
+
child.on("exit", (code, signal) => {
|
|
6546
|
+
exit = signal ? `signal ${signal}` : `exit status ${code}`;
|
|
6547
|
+
});
|
|
6548
|
+
child.unref();
|
|
6549
|
+
pid = child.pid;
|
|
6550
|
+
} finally {
|
|
6551
|
+
fs9.closeSync(fd);
|
|
6552
|
+
}
|
|
6553
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
6554
|
+
const shownLog = displayPath(logFile);
|
|
6555
|
+
const fail = (why) => new PreviewCommandError(`The site ${why} and did not answer on 127.0.0.1:${port}; log: ${shownLog}`, PREVIEW_EXIT_NOT_LISTENING);
|
|
6556
|
+
const deadline = Date.now() + (params.timeoutMs ?? START_PORT_WAIT_MS);
|
|
6557
|
+
for (; ; ) {
|
|
6558
|
+
if (spawnError || pid === void 0) throw fail(`could not be started (${spawnError ?? "no process"})`);
|
|
6559
|
+
if (exit) throw fail(`started with --serve-cmd exited (${exit})`);
|
|
6560
|
+
const probe = await probeLocalSite(port, sitePath, START_PROBE_TIMEOUT_MS, RATHOLE_LOCAL_HOSTS);
|
|
6561
|
+
if (probe.listening) return { pid, logFile };
|
|
6562
|
+
if (exit) throw fail(`started with --serve-cmd exited (${exit})`);
|
|
6563
|
+
if (Date.now() >= deadline) {
|
|
6564
|
+
try {
|
|
6565
|
+
process.kill(-pid, "SIGTERM");
|
|
6566
|
+
} catch {
|
|
6567
|
+
}
|
|
6568
|
+
throw fail(`started with --serve-cmd was stopped after ${Math.round((params.timeoutMs ?? START_PORT_WAIT_MS) / 1e3)} s`);
|
|
6569
|
+
}
|
|
6570
|
+
await new Promise((resolve) => setTimeout(resolve, START_POLL_MS));
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6448
6573
|
async function createAndStartClient(params) {
|
|
6449
6574
|
const { opts, port, title, sitePath, projectId: projectId2, retryCommand, auth, controllerUrl, serverUrl, log, serve } = params;
|
|
6450
6575
|
const conversationId = opts.conversationId?.trim() || void 0;
|
|
@@ -6472,7 +6597,7 @@ async function createAndStartClient(params) {
|
|
|
6472
6597
|
throw new Error("Preview response is missing previewId.");
|
|
6473
6598
|
}
|
|
6474
6599
|
const workdir = workdirFor(previewId);
|
|
6475
|
-
const logFile =
|
|
6600
|
+
const logFile = path10.join(instafyDir(), "cli-preview-logs", `preview-${safePathSegment2(previewId)}.log`);
|
|
6476
6601
|
const configPath2 = configPathFor(workdir);
|
|
6477
6602
|
const hostSuffix = parseViewerHostSuffix(grant["viewerHostSuffix"]);
|
|
6478
6603
|
const studioOrigin = parseStudioOrigin(grant["studioOrigin"]);
|
|
@@ -6483,8 +6608,8 @@ async function createAndStartClient(params) {
|
|
|
6483
6608
|
try {
|
|
6484
6609
|
const credentials = grant["credentials"] && typeof grant["credentials"] === "object" ? grant["credentials"] : {};
|
|
6485
6610
|
const configBody = buildRatholeConfig(credentials, port);
|
|
6486
|
-
|
|
6487
|
-
|
|
6611
|
+
fs9.mkdirSync(workdir, { recursive: true });
|
|
6612
|
+
fs9.mkdirSync(path10.dirname(logFile), { recursive: true });
|
|
6488
6613
|
if (current && readFileOrNull(configPath2) === configBody && isRatholeClientFor(current.pid, configPath2, current.binary)) {
|
|
6489
6614
|
pid = current.pid;
|
|
6490
6615
|
}
|
|
@@ -6503,7 +6628,7 @@ async function createAndStartClient(params) {
|
|
|
6503
6628
|
}
|
|
6504
6629
|
if (pid === null) {
|
|
6505
6630
|
const rathole = await resolveRathole(opts.ratholeBin, log);
|
|
6506
|
-
|
|
6631
|
+
fs9.writeFileSync(configPath2, configBody, { encoding: "utf8", mode: 384 });
|
|
6507
6632
|
binary = rathole;
|
|
6508
6633
|
const started = await spawnRatholeDetached({ rathole, configPath: configPath2, workdir, logFile });
|
|
6509
6634
|
if (started.exit !== null) {
|
|
@@ -6556,7 +6681,24 @@ async function previewOpen(opts) {
|
|
|
6556
6681
|
const { auth, controllerUrl } = resolveAuth(opts, retryCommand);
|
|
6557
6682
|
const serverUrl = cleanUrl(controllerUrl);
|
|
6558
6683
|
const log = (message) => console.error(kleur8.cyan(message));
|
|
6559
|
-
const
|
|
6684
|
+
const explicitServe = opts.start && opts.serveCmd?.trim() ? resolvePreviewServe({ port, serveCmd: opts.serveCmd, projectId: projectId2, cwd: opts.cwd }).serve : null;
|
|
6685
|
+
let probe = await probeLocalSite(port, sitePath, void 0, RATHOLE_LOCAL_HOSTS);
|
|
6686
|
+
let started = null;
|
|
6687
|
+
if (!probe.listening && opts.start && probe.status === null && !probe.timedOut) {
|
|
6688
|
+
const ipv6 = await probeLocalSite(port, sitePath, void 0, ["::1"]);
|
|
6689
|
+
if (ipv6.status === null && !ipv6.timedOut) {
|
|
6690
|
+
if (!explicitServe) {
|
|
6691
|
+
throw new PreviewCommandError(
|
|
6692
|
+
`Nothing answers on 127.0.0.1:${port}, and --start needs --serve-cmd "<command>" to start the site.`,
|
|
6693
|
+
PREVIEW_EXIT_NOT_LISTENING
|
|
6694
|
+
);
|
|
6695
|
+
}
|
|
6696
|
+
const root = resolveWorkspaceRoot(process.env, projectId2);
|
|
6697
|
+
started = await startPreviewSite({ port, sitePath, serve: explicitServe, root, timeoutMs: opts.startTimeoutMs });
|
|
6698
|
+
log(`Started the site (pid ${started.pid}); log: ${displayPath(started.logFile)}`);
|
|
6699
|
+
probe = await probeLocalSite(port, sitePath, void 0, RATHOLE_LOCAL_HOSTS);
|
|
6700
|
+
}
|
|
6701
|
+
}
|
|
6560
6702
|
if (!probe.listening) {
|
|
6561
6703
|
throw await notListeningError(port, sitePath, probe);
|
|
6562
6704
|
}
|
|
@@ -6595,7 +6737,8 @@ async function previewOpen(opts) {
|
|
|
6595
6737
|
messageId: stringField(readyBody, "messageId"),
|
|
6596
6738
|
viewerHostSuffix: parseViewerHostSuffix(grant["viewerHostSuffix"]) ?? null,
|
|
6597
6739
|
restartable,
|
|
6598
|
-
...restartable ? {} : { restartNote: restartNote ?? "the server did not record a start command" }
|
|
6740
|
+
...restartable ? {} : { restartNote: restartNote ?? "the server did not record a start command" },
|
|
6741
|
+
...opts.start ? { startedPid: started?.pid ?? null, logFile: started?.logFile ?? null } : {}
|
|
6599
6742
|
};
|
|
6600
6743
|
}
|
|
6601
6744
|
function toPreviewSummary(raw) {
|
|
@@ -6608,7 +6751,8 @@ function toPreviewSummary(raw) {
|
|
|
6608
6751
|
title: stringField(record2, "title") ?? "",
|
|
6609
6752
|
port: numberField(record2, "port", "localPort") ?? 0,
|
|
6610
6753
|
path: stringField(record2, "path") ?? "/",
|
|
6611
|
-
status: stringField(record2, "status") ?? "unknown"
|
|
6754
|
+
status: stringField(record2, "status") ?? "unknown",
|
|
6755
|
+
...typeof record2["restartable"] === "boolean" ? { restartable: record2["restartable"] } : {}
|
|
6612
6756
|
};
|
|
6613
6757
|
}
|
|
6614
6758
|
async function previewList(opts) {
|
|
@@ -6656,10 +6800,19 @@ function printPreviewOpen(result, json) {
|
|
|
6656
6800
|
}
|
|
6657
6801
|
const label = result.status === "live" ? kleur8.green("Ready to preview") : result.status === "not_responding" ? kleur8.yellow("Not responding yet") : kleur8.yellow("Preview opened (the server did not report whether the site answers)");
|
|
6658
6802
|
console.log(`${label}: ${result.title} (port ${result.port}). Open it from the card in the conversation.`);
|
|
6803
|
+
if (result.startedPid) {
|
|
6804
|
+
console.log(kleur8.gray(`Started the site in the background (pid ${result.startedPid}); log: ${displayPath(result.logFile ?? "")}`));
|
|
6805
|
+
} else if (result.startedPid === null) {
|
|
6806
|
+
console.log(kleur8.gray("The site was already running; nothing was started."));
|
|
6807
|
+
}
|
|
6659
6808
|
if (result.restartable) {
|
|
6660
|
-
console.log(kleur8.gray("Open starts the site again
|
|
6809
|
+
console.log(kleur8.gray("Open starts the site again when the process that started it exits, or the space sleeps."));
|
|
6661
6810
|
} else {
|
|
6662
|
-
console.log(
|
|
6811
|
+
console.log(
|
|
6812
|
+
kleur8.yellow(
|
|
6813
|
+
`When the process that started it exits, or the space sleeps, Open cannot start the site by itself: ${result.restartNote}.`
|
|
6814
|
+
)
|
|
6815
|
+
);
|
|
6663
6816
|
}
|
|
6664
6817
|
console.log(kleur8.gray(`previewId=${result.previewId}`));
|
|
6665
6818
|
if (result.httpStatus !== null && result.httpStatus >= 400) {
|
|
@@ -6678,15 +6831,23 @@ function printPreviewList(previews, json) {
|
|
|
6678
6831
|
console.log(JSON.stringify(previews, null, 2));
|
|
6679
6832
|
return;
|
|
6680
6833
|
}
|
|
6834
|
+
const stoppedNote = "A preview whose machine stopped or restarted is not listed; its card reads Stopped, and Open on a restartable card starts the site again.";
|
|
6681
6835
|
if (previews.length === 0) {
|
|
6682
|
-
console.log(kleur8.yellow("No previews
|
|
6836
|
+
console.log(kleur8.yellow("No open previews."));
|
|
6837
|
+
console.log(kleur8.gray(stoppedNote));
|
|
6683
6838
|
return;
|
|
6684
6839
|
}
|
|
6685
6840
|
for (const preview of previews) {
|
|
6841
|
+
const restart = preview.restartable === void 0 ? "" : preview.restartable ? " \xB7 restartable" : " \xB7 not restartable";
|
|
6686
6842
|
console.log(
|
|
6687
|
-
`${preview.previewId} \xB7 ${preview.title || "(untitled)"} \xB7 port=${preview.port}${preview.path === "/" ? "" : ` \xB7 path=${preview.path}`} \xB7 ${preview.status}`
|
|
6843
|
+
`${preview.previewId} \xB7 ${preview.title || "(untitled)"} \xB7 port=${preview.port}${preview.path === "/" ? "" : ` \xB7 path=${preview.path}`} \xB7 preview ${preview.status}${restart}`
|
|
6688
6844
|
);
|
|
6689
6845
|
}
|
|
6846
|
+
console.log(
|
|
6847
|
+
kleur8.gray(
|
|
6848
|
+
`This lists open previews; it does not check whether a site answers (${kleur8.cyan("instafy preview check --port <port>")} does). ${stoppedNote}`
|
|
6849
|
+
)
|
|
6850
|
+
);
|
|
6690
6851
|
}
|
|
6691
6852
|
|
|
6692
6853
|
// src/config-command.ts
|
|
@@ -6810,27 +6971,27 @@ init_config();
|
|
|
6810
6971
|
// src/git-wrapper.ts
|
|
6811
6972
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
6812
6973
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
6813
|
-
import
|
|
6814
|
-
import
|
|
6974
|
+
import fs10 from "node:fs";
|
|
6975
|
+
import path11 from "node:path";
|
|
6815
6976
|
import kleur10 from "kleur";
|
|
6816
6977
|
function pathExists(candidate) {
|
|
6817
6978
|
try {
|
|
6818
|
-
|
|
6979
|
+
fs10.statSync(candidate);
|
|
6819
6980
|
return true;
|
|
6820
6981
|
} catch {
|
|
6821
6982
|
return false;
|
|
6822
6983
|
}
|
|
6823
6984
|
}
|
|
6824
6985
|
function findInstafyGitContext(startDir) {
|
|
6825
|
-
let current =
|
|
6826
|
-
const root =
|
|
6986
|
+
let current = path11.resolve(startDir);
|
|
6987
|
+
const root = path11.parse(current).root;
|
|
6827
6988
|
while (true) {
|
|
6828
|
-
const gitDir =
|
|
6989
|
+
const gitDir = path11.join(current, ".instafy", ".git");
|
|
6829
6990
|
if (pathExists(gitDir)) {
|
|
6830
6991
|
return { workTree: current, gitDir };
|
|
6831
6992
|
}
|
|
6832
6993
|
if (current === root) break;
|
|
6833
|
-
current =
|
|
6994
|
+
current = path11.dirname(current);
|
|
6834
6995
|
}
|
|
6835
6996
|
return null;
|
|
6836
6997
|
}
|
|
@@ -6986,7 +7147,7 @@ function collectDirtyPaths(context, options) {
|
|
|
6986
7147
|
return unique;
|
|
6987
7148
|
}
|
|
6988
7149
|
function findEmbeddedGitDirs(context, pathSpecs) {
|
|
6989
|
-
const instafyGitDir =
|
|
7150
|
+
const instafyGitDir = path11.resolve(context.gitDir);
|
|
6990
7151
|
const seen = /* @__PURE__ */ new Set();
|
|
6991
7152
|
for (const rawPath of pathSpecs) {
|
|
6992
7153
|
const normalized = normalizePathSpec(rawPath).replace(/\/+$/, "");
|
|
@@ -6995,11 +7156,11 @@ function findEmbeddedGitDirs(context, pathSpecs) {
|
|
|
6995
7156
|
}
|
|
6996
7157
|
let cursor = normalized;
|
|
6997
7158
|
while (cursor && cursor !== ".") {
|
|
6998
|
-
const candidate =
|
|
6999
|
-
if (pathExists(candidate) &&
|
|
7000
|
-
seen.add(
|
|
7159
|
+
const candidate = path11.join(context.workTree, ...cursor.split("/"), ".git");
|
|
7160
|
+
if (pathExists(candidate) && path11.resolve(candidate) !== instafyGitDir) {
|
|
7161
|
+
seen.add(path11.resolve(candidate));
|
|
7001
7162
|
}
|
|
7002
|
-
const next =
|
|
7163
|
+
const next = path11.posix.dirname(cursor);
|
|
7003
7164
|
if (next === "." || next === cursor) {
|
|
7004
7165
|
break;
|
|
7005
7166
|
}
|
|
@@ -7008,8 +7169,8 @@ function findEmbeddedGitDirs(context, pathSpecs) {
|
|
|
7008
7169
|
}
|
|
7009
7170
|
const candidates = Array.from(seen);
|
|
7010
7171
|
candidates.sort((a, b) => {
|
|
7011
|
-
const depthA = a.split(
|
|
7012
|
-
const depthB = b.split(
|
|
7172
|
+
const depthA = a.split(path11.sep).length;
|
|
7173
|
+
const depthB = b.split(path11.sep).length;
|
|
7013
7174
|
return depthB - depthA;
|
|
7014
7175
|
});
|
|
7015
7176
|
return candidates;
|
|
@@ -7019,16 +7180,16 @@ function hideEmbeddedGitDirs(context, pathSpecs) {
|
|
|
7019
7180
|
if (candidates.length === 0) {
|
|
7020
7181
|
return [];
|
|
7021
7182
|
}
|
|
7022
|
-
const stagingBase =
|
|
7023
|
-
|
|
7183
|
+
const stagingBase = path11.join(context.workTree, ".instafy", "origin-staging", "embedded-git");
|
|
7184
|
+
fs10.mkdirSync(stagingBase, { recursive: true });
|
|
7024
7185
|
const renames = [];
|
|
7025
7186
|
for (const candidate of candidates) {
|
|
7026
|
-
let hidden =
|
|
7187
|
+
let hidden = path11.join(stagingBase, randomUUID2());
|
|
7027
7188
|
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
7028
7189
|
if (!pathExists(hidden)) break;
|
|
7029
|
-
hidden =
|
|
7190
|
+
hidden = path11.join(stagingBase, randomUUID2());
|
|
7030
7191
|
}
|
|
7031
|
-
|
|
7192
|
+
fs10.renameSync(candidate, hidden);
|
|
7032
7193
|
renames.push({ original: candidate, hidden });
|
|
7033
7194
|
}
|
|
7034
7195
|
return renames;
|
|
@@ -7038,7 +7199,7 @@ function restoreEmbeddedGitDirs(renames) {
|
|
|
7038
7199
|
const rename = renames[index];
|
|
7039
7200
|
if (!rename) continue;
|
|
7040
7201
|
try {
|
|
7041
|
-
|
|
7202
|
+
fs10.renameSync(rename.hidden, rename.original);
|
|
7042
7203
|
} catch {
|
|
7043
7204
|
}
|
|
7044
7205
|
}
|
|
@@ -7597,49 +7758,6 @@ function saveReportJson(report, notSaved) {
|
|
|
7597
7758
|
};
|
|
7598
7759
|
}
|
|
7599
7760
|
|
|
7600
|
-
// src/bind.ts
|
|
7601
|
-
import { spawn as spawn4 } from "node:child_process";
|
|
7602
|
-
import fs10 from "node:fs";
|
|
7603
|
-
import path11 from "node:path";
|
|
7604
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7605
|
-
var BIN_REL = path11.join("bin", "instafy-bind.mjs");
|
|
7606
|
-
function resolveBindBin(env = process.env) {
|
|
7607
|
-
const fromEnv = env.INSTAFY_BIND_BIN?.trim();
|
|
7608
|
-
if (fromEnv) return fs10.existsSync(fromEnv) ? path11.resolve(fromEnv) : null;
|
|
7609
|
-
const here = path11.dirname(fileURLToPath2(import.meta.url));
|
|
7610
|
-
for (const candidate of [path11.join(here, "bind", BIN_REL), path11.resolve(here, "..", "..", "instafy-bind", BIN_REL)]) {
|
|
7611
|
-
if (fs10.existsSync(candidate)) return candidate;
|
|
7612
|
-
}
|
|
7613
|
-
return null;
|
|
7614
|
-
}
|
|
7615
|
-
function runBind(args) {
|
|
7616
|
-
const bin = resolveBindBin();
|
|
7617
|
-
if (!bin) {
|
|
7618
|
-
process.stderr.write(
|
|
7619
|
-
"instafy bind: the bundled binder (dist/bind) was not found; rebuild the CLI, or set INSTAFY_BIND_BIN to packages/instafy-bind/bin/instafy-bind.mjs, or run it directly: node packages/instafy-bind/bin/instafy-bind.mjs <command>\n"
|
|
7620
|
-
);
|
|
7621
|
-
return Promise.resolve(1);
|
|
7622
|
-
}
|
|
7623
|
-
return new Promise((resolve) => {
|
|
7624
|
-
const child = spawn4(process.execPath, [bin, ...args], { stdio: "inherit", env: process.env });
|
|
7625
|
-
const forward = (signal) => {
|
|
7626
|
-
if (!child.killed) child.kill(signal);
|
|
7627
|
-
};
|
|
7628
|
-
process.on("SIGINT", forward);
|
|
7629
|
-
process.on("SIGTERM", forward);
|
|
7630
|
-
child.on("error", (error) => {
|
|
7631
|
-
process.stderr.write(`instafy bind: ${error.message}
|
|
7632
|
-
`);
|
|
7633
|
-
resolve(1);
|
|
7634
|
-
});
|
|
7635
|
-
child.on("exit", (code, signal) => {
|
|
7636
|
-
process.off("SIGINT", forward);
|
|
7637
|
-
process.off("SIGTERM", forward);
|
|
7638
|
-
resolve(code ?? (signal ? 130 : 1));
|
|
7639
|
-
});
|
|
7640
|
-
});
|
|
7641
|
-
}
|
|
7642
|
-
|
|
7643
7761
|
// src/site-check.ts
|
|
7644
7762
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
7645
7763
|
import { once } from "node:events";
|
|
@@ -10990,7 +11108,7 @@ async function teamRevokeLink(options) {
|
|
|
10990
11108
|
init_project_manifest();
|
|
10991
11109
|
import fs15 from "node:fs";
|
|
10992
11110
|
import path16 from "node:path";
|
|
10993
|
-
import { pathToFileURL } from "node:url";
|
|
11111
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
10994
11112
|
import kleur16 from "kleur";
|
|
10995
11113
|
function resolveBindingContext(startDir) {
|
|
10996
11114
|
const lookup = findProjectManifest(path16.resolve(startDir));
|
|
@@ -11110,7 +11228,7 @@ function grantProviderBinding(options) {
|
|
|
11110
11228
|
const record2 = {
|
|
11111
11229
|
providerId,
|
|
11112
11230
|
projectId: context.projectId,
|
|
11113
|
-
rootUri:
|
|
11231
|
+
rootUri: pathToFileURL2(context.projectRoot).toString(),
|
|
11114
11232
|
grantedCapabilities: capabilities,
|
|
11115
11233
|
grantedPrefix: options.prefix?.trim() || `.instafy/providers/${providerId}/`,
|
|
11116
11234
|
purpose: options.purpose?.trim() || null,
|
|
@@ -13489,7 +13607,10 @@ addServerUrlOptions(previewOpenCommand);
|
|
|
13489
13607
|
addAccessTokenOptions(previewOpenCommand, "Instafy access token (defaults to the job token, then the saved `instafy login` token)");
|
|
13490
13608
|
previewOpenCommand.option("--conversation <id>", "Conversation for the card (only needed without a job token)").option("--rathole-bin <path>", "Path to rathole binary (or set RATHOLE_BIN)").option(
|
|
13491
13609
|
"--serve-cmd <command>",
|
|
13492
|
-
"How to start the site again
|
|
13610
|
+
"How to start the site again when the process that started it exits, or the space sleeps, run from the current directory (one command, no shell); default: read from the process listening on the port"
|
|
13611
|
+
).option(
|
|
13612
|
+
"--start",
|
|
13613
|
+
"Start --serve-cmd in the background, in its own session, without Instafy credentials, logging to .instafy/agent/logs, and wait for the port (nothing is started when the port already answers)"
|
|
13493
13614
|
).option("--json", "Output JSON").action(async (opts) => {
|
|
13494
13615
|
try {
|
|
13495
13616
|
const result = await previewOpen({
|
|
@@ -13501,14 +13622,15 @@ previewOpenCommand.option("--conversation <id>", "Conversation for the card (onl
|
|
|
13501
13622
|
title: opts.title,
|
|
13502
13623
|
path: opts.path,
|
|
13503
13624
|
ratholeBin: opts.ratholeBin,
|
|
13504
|
-
serveCmd: opts.serveCmd
|
|
13625
|
+
serveCmd: opts.serveCmd,
|
|
13626
|
+
start: Boolean(opts.start)
|
|
13505
13627
|
});
|
|
13506
13628
|
printPreviewOpen(result, Boolean(opts.json));
|
|
13507
13629
|
} catch (error) {
|
|
13508
13630
|
exitWithPreviewError(error);
|
|
13509
13631
|
}
|
|
13510
13632
|
});
|
|
13511
|
-
var previewListCommand = previewCommand.command("list").description("List this space's previews");
|
|
13633
|
+
var previewListCommand = previewCommand.command("list").description("List this space's open previews (it does not check the sites: `instafy preview check` does)");
|
|
13512
13634
|
addSpaceOption(previewListCommand, "Space UUID (defaults to .instafy/space.json or SPACE_ID)");
|
|
13513
13635
|
addServerUrlOptions(previewListCommand);
|
|
13514
13636
|
addAccessTokenOptions(previewListCommand, "Instafy access token (defaults to the job token, then the saved `instafy login` token)");
|
|
@@ -13956,7 +14078,7 @@ async function runCli(argv = process.argv) {
|
|
|
13956
14078
|
}
|
|
13957
14079
|
await program.parseAsync(argv);
|
|
13958
14080
|
}
|
|
13959
|
-
if (import.meta.url ===
|
|
14081
|
+
if (import.meta.url === pathToFileURL3(process.argv[1] ?? "").href) {
|
|
13960
14082
|
void runCli(process.argv);
|
|
13961
14083
|
}
|
|
13962
14084
|
export {
|