@instafy/cli 0.1.8-staging.365 → 0.1.8-staging.368

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 CHANGED
@@ -9,26 +9,27 @@ Run Instafy projects locally and connect them back to Instafy Studio — from an
9
9
  0. Log in once: `instafy login`
10
10
  - Opens a Studio URL; the CLI continues automatically after you sign in.
11
11
  - Also enables Git auth (credential helper) for Instafy Git Service. Disable with `instafy login --no-git-setup`.
12
- - Multiple accounts: `instafy login --profile work` (then bind folders with `instafy project:init --profile work` or `instafy project:profile work`).
12
+ - Multiple accounts: `instafy login --profile work` (then bind folders with `instafy project init --profile work` or `instafy project profile work`).
13
13
  - Optional: set defaults with `instafy config set controller-url <url>` / `instafy config set studio-url <url>`
14
14
  1. Link a folder to a project:
15
15
  - VS Code: install the Instafy extension and run `Instafy: Link Workspace to Project`, or
16
- - Terminal: `instafy project:init`
17
- 2. Start the runtime: `instafy runtime:start`
16
+ - Terminal: `instafy project init`
17
+ 2. Start the runtime: `instafy runtime start`
18
18
  3. Check status / stop:
19
- - `instafy runtime:status`
20
- - `instafy runtime:stop`
19
+ - `instafy runtime status`
20
+ - `instafy runtime stop`
21
21
 
22
22
  ## Common commands
23
23
 
24
- - `instafy runtime:start` — start a local runtime (agent + origin).
25
- - `instafy runtime:status` — show health of the last started runtime.
26
- - `instafy runtime:stop` — stop the last started runtime.
27
- - `instafy tunnel`start a detached tunnel for a local port.
28
- - `instafy tunnel:list` — list local tunnels started by the CLI.
29
- - `instafy tunnel:logs <tunnelId> --follow` — tail tunnel logs.
30
- - `instafy tunnel:stop <tunnelId>`stop + revoke a tunnel.
31
- - `instafy api:get` query controller endpoints (conversations, messages, runs, etc).
24
+ - `instafy runtime start` — start a local runtime (agent + origin).
25
+ - `instafy runtime status` — show health of the last started runtime.
26
+ - `instafy runtime stop` — stop the last started runtime.
27
+ - `instafy git <args...>` run git commands against an Instafy canonical checkout (`.instafy/.git`) when present.
28
+ - `instafy tunnel start` — start a detached tunnel for a local port.
29
+ - `instafy tunnel list` — list local tunnels started by the CLI.
30
+ - `instafy tunnel logs <tunnelId> --follow` tail tunnel logs.
31
+ - `instafy tunnel stop <tunnelId>` stop + revoke a tunnel.
32
+ - `instafy api get` — query controller endpoints (conversations, messages, runs, etc).
32
33
 
33
34
  Run `instafy --help` for the full command list and options.
34
35
 
package/dist/auth.js CHANGED
@@ -390,8 +390,8 @@ export async function login(options) {
390
390
  }
391
391
  console.log("");
392
392
  console.log("Next:");
393
- console.log(`- ${kleur.cyan("instafy project:init")}`);
394
- console.log(`- ${kleur.cyan("instafy runtime:start")}`);
393
+ console.log(`- ${kleur.cyan("instafy project init")}`);
394
+ console.log(`- ${kleur.cyan("instafy runtime start")}`);
395
395
  }
396
396
  export async function logout(options) {
397
397
  if (options?.profile) {
package/dist/git-setup.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
- const INSTAFY_GIT_HELPER_VALUE = "!instafy git:credential";
2
+ const INSTAFY_GIT_HELPER_VALUE = "!instafy git credential";
3
3
  function isLikelySameHelper(value) {
4
- return value.includes("instafy git:credential");
4
+ return value.includes("instafy git credential");
5
5
  }
6
6
  function runGit(args) {
7
7
  const result = spawnSync("git", args, { encoding: "utf8" });
@@ -0,0 +1,497 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import kleur from "kleur";
6
+ function pathExists(candidate) {
7
+ try {
8
+ fs.statSync(candidate);
9
+ return true;
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ }
15
+ export function findInstafyGitContext(startDir) {
16
+ let current = path.resolve(startDir);
17
+ const root = path.parse(current).root;
18
+ while (true) {
19
+ const gitDir = path.join(current, ".instafy", ".git");
20
+ if (pathExists(gitDir)) {
21
+ return { workTree: current, gitDir };
22
+ }
23
+ if (current === root)
24
+ break;
25
+ current = path.dirname(current);
26
+ }
27
+ return null;
28
+ }
29
+ function splitGitCommand(userArgs) {
30
+ const globalArgs = [];
31
+ let index = 0;
32
+ while (index < userArgs.length) {
33
+ const arg = userArgs[index] ?? "";
34
+ if (arg === "--") {
35
+ index += 1;
36
+ break;
37
+ }
38
+ if (!arg.startsWith("-") || arg === "-") {
39
+ break;
40
+ }
41
+ globalArgs.push(arg);
42
+ // Skip values for global options that consume the next token.
43
+ if (arg === "-c" || arg === "--config-env" || arg === "--exec-path" || arg === "--namespace") {
44
+ if (index + 1 < userArgs.length) {
45
+ globalArgs.push(userArgs[index + 1] ?? "");
46
+ index += 2;
47
+ continue;
48
+ }
49
+ }
50
+ index += 1;
51
+ }
52
+ const command = index < userArgs.length ? (userArgs[index] ?? null) : null;
53
+ const commandArgs = command ? userArgs.slice(index + 1) : [];
54
+ return { globalArgs, command, commandArgs };
55
+ }
56
+ function validateGitArgs(userArgs) {
57
+ let parsingGlobalOptions = true;
58
+ for (let index = 0; index < userArgs.length; index += 1) {
59
+ const arg = userArgs[index] ?? "";
60
+ if (!parsingGlobalOptions) {
61
+ continue;
62
+ }
63
+ if (arg === "--") {
64
+ parsingGlobalOptions = false;
65
+ continue;
66
+ }
67
+ if (!arg.startsWith("-") || arg === "-") {
68
+ parsingGlobalOptions = false;
69
+ continue;
70
+ }
71
+ if (arg === "--git-dir" || arg.startsWith("--git-dir=") || arg === "--work-tree" || arg.startsWith("--work-tree=") || arg === "-C") {
72
+ throw new Error(`Unsupported argument "${arg}". Use ${kleur.cyan("cd")} instead; ${kleur.cyan("instafy git")} manages --git-dir/--work-tree automatically.`);
73
+ }
74
+ // Skip the value for global options that consume the next token.
75
+ if (arg === "-c" || arg === "--config-env" || arg === "--exec-path" || arg === "--namespace") {
76
+ index += 1;
77
+ }
78
+ }
79
+ }
80
+ export function buildInstafyGitArgs(context, userArgs) {
81
+ validateGitArgs(userArgs);
82
+ return ["--git-dir", context.gitDir, "--work-tree", context.workTree, ...userArgs];
83
+ }
84
+ function normalizePathSpec(raw) {
85
+ return (raw ?? "").replace(/\\/g, "/").trim().replace(/^\/+/, "");
86
+ }
87
+ function isReservedSyncPath(pathSpec) {
88
+ const normalized = normalizePathSpec(pathSpec);
89
+ if (!normalized) {
90
+ return false;
91
+ }
92
+ if (normalized === ".instafy" || normalized.startsWith(".instafy/")) {
93
+ return true;
94
+ }
95
+ if (normalized === ".instafy/origin-staging" || normalized.startsWith(".instafy/origin-staging/")) {
96
+ return true;
97
+ }
98
+ return normalized.split("/").some((segment) => segment === ".git" || segment.startsWith(".git.instafy-hidden-"));
99
+ }
100
+ function gitCapture(context, args, options) {
101
+ const result = spawnSync("git", ["--git-dir", context.gitDir, "--work-tree", context.workTree, ...args], {
102
+ cwd: options.cwd,
103
+ encoding: "utf8",
104
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
105
+ });
106
+ return {
107
+ status: typeof result.status === "number" ? result.status : 1,
108
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
109
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
110
+ error: result.error,
111
+ };
112
+ }
113
+ function collectDirtyPaths(context, options) {
114
+ const result = gitCapture(context, ["status", "--porcelain=v1"], options);
115
+ if (result.error) {
116
+ throw new Error(`Failed to run git status: ${result.error.message}`);
117
+ }
118
+ if (result.status !== 0) {
119
+ throw new Error(`git status failed (${result.status}): ${result.stderr.trim()}`);
120
+ }
121
+ const paths = [];
122
+ for (const line of result.stdout.split(/\r?\n/)) {
123
+ if (!line.trim())
124
+ continue;
125
+ if (line.length < 4)
126
+ continue;
127
+ const raw = line.slice(3).trim();
128
+ if (!raw)
129
+ continue;
130
+ const rename = raw.split(" -> ");
131
+ if (rename.length === 2) {
132
+ for (const entry of rename) {
133
+ const normalized = normalizePathSpec(entry).replace(/\/+$/, "");
134
+ if (normalized)
135
+ paths.push(normalized);
136
+ }
137
+ continue;
138
+ }
139
+ const normalized = normalizePathSpec(raw).replace(/\/+$/, "");
140
+ if (normalized)
141
+ paths.push(normalized);
142
+ }
143
+ paths.sort((a, b) => a.localeCompare(b));
144
+ const unique = [];
145
+ for (const value of paths) {
146
+ if (unique.length === 0 || unique[unique.length - 1] !== value) {
147
+ unique.push(value);
148
+ }
149
+ }
150
+ return unique;
151
+ }
152
+ function findEmbeddedGitDirs(context, pathSpecs) {
153
+ const instafyGitDir = path.resolve(context.gitDir);
154
+ const seen = new Set();
155
+ for (const rawPath of pathSpecs) {
156
+ const normalized = normalizePathSpec(rawPath).replace(/\/+$/, "");
157
+ if (!normalized || normalized === "." || normalized === "/") {
158
+ continue;
159
+ }
160
+ let cursor = normalized;
161
+ while (cursor && cursor !== ".") {
162
+ const candidate = path.join(context.workTree, ...cursor.split("/"), ".git");
163
+ if (pathExists(candidate) && path.resolve(candidate) !== instafyGitDir) {
164
+ seen.add(path.resolve(candidate));
165
+ }
166
+ const next = path.posix.dirname(cursor);
167
+ if (next === "." || next === cursor) {
168
+ break;
169
+ }
170
+ cursor = next;
171
+ }
172
+ }
173
+ const candidates = Array.from(seen);
174
+ candidates.sort((a, b) => {
175
+ const depthA = a.split(path.sep).length;
176
+ const depthB = b.split(path.sep).length;
177
+ return depthB - depthA;
178
+ });
179
+ return candidates;
180
+ }
181
+ function hideEmbeddedGitDirs(context, pathSpecs) {
182
+ const candidates = findEmbeddedGitDirs(context, pathSpecs);
183
+ if (candidates.length === 0) {
184
+ return [];
185
+ }
186
+ const stagingBase = path.join(context.workTree, ".instafy", "origin-staging", "embedded-git");
187
+ fs.mkdirSync(stagingBase, { recursive: true });
188
+ const renames = [];
189
+ for (const candidate of candidates) {
190
+ let hidden = path.join(stagingBase, randomUUID());
191
+ for (let attempt = 0; attempt < 8; attempt += 1) {
192
+ if (!pathExists(hidden))
193
+ break;
194
+ hidden = path.join(stagingBase, randomUUID());
195
+ }
196
+ fs.renameSync(candidate, hidden);
197
+ renames.push({ original: candidate, hidden });
198
+ }
199
+ return renames;
200
+ }
201
+ function restoreEmbeddedGitDirs(renames) {
202
+ for (let index = renames.length - 1; index >= 0; index -= 1) {
203
+ const rename = renames[index];
204
+ if (!rename)
205
+ continue;
206
+ try {
207
+ fs.renameSync(rename.hidden, rename.original);
208
+ }
209
+ catch {
210
+ // ignore restore failures; leave evidence in .instafy/origin-staging for debugging
211
+ }
212
+ }
213
+ }
214
+ function parseAddArgs(commandArgs) {
215
+ const addOptions = [];
216
+ const pathSpecs = [];
217
+ let inPaths = false;
218
+ for (const arg of commandArgs) {
219
+ if (inPaths) {
220
+ pathSpecs.push(arg);
221
+ continue;
222
+ }
223
+ if (arg === "--") {
224
+ inPaths = true;
225
+ continue;
226
+ }
227
+ if (arg.startsWith("-") && arg !== "-") {
228
+ addOptions.push(arg);
229
+ continue;
230
+ }
231
+ inPaths = true;
232
+ pathSpecs.push(arg);
233
+ }
234
+ return { addOptions, pathSpecs };
235
+ }
236
+ function runInstafyGitAdd(context, globalArgs, commandArgs, options) {
237
+ const parsed = parseAddArgs(commandArgs);
238
+ const normalizedPathSpecs = parsed.pathSpecs
239
+ .map((spec) => normalizePathSpec(spec))
240
+ .filter((spec) => spec && spec !== "." && spec !== "/");
241
+ const stageAll = normalizedPathSpecs.length === 0;
242
+ const stagePaths = stageAll
243
+ ? collectDirtyPaths(context, options).filter((entry) => !isReservedSyncPath(entry))
244
+ : normalizedPathSpecs.filter((entry) => !isReservedSyncPath(entry));
245
+ if (stagePaths.length === 0) {
246
+ return 0;
247
+ }
248
+ const addOptions = [...parsed.addOptions];
249
+ if (stageAll && !addOptions.some((opt) => opt === "-A" || opt === "--all")) {
250
+ addOptions.unshift("-A");
251
+ }
252
+ const renames = hideEmbeddedGitDirs(context, stagePaths);
253
+ try {
254
+ const result = spawnSync("git", [
255
+ "--git-dir",
256
+ context.gitDir,
257
+ "--work-tree",
258
+ context.workTree,
259
+ ...globalArgs,
260
+ "add",
261
+ ...addOptions,
262
+ "--",
263
+ ...stagePaths,
264
+ ], { stdio: "inherit", cwd: options.cwd, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" } });
265
+ if (result.error) {
266
+ throw new Error(`Failed to run git add: ${result.error.message}`);
267
+ }
268
+ return typeof result.status === "number" ? result.status : 1;
269
+ }
270
+ finally {
271
+ restoreEmbeddedGitDirs(renames);
272
+ }
273
+ }
274
+ export function runInstafyGit(userArgs, options) {
275
+ const cwd = options?.cwd ?? process.cwd();
276
+ if (userArgs.length === 0 || userArgs[0] === "--help" || userArgs[0] === "-h") {
277
+ // Keep this minimal so it doesn't diverge from Git help output.
278
+ console.log("instafy git <git-args...>");
279
+ console.log("");
280
+ console.log(`Runs ${kleur.cyan("git")} against the Instafy canonical repo at ${kleur.cyan(".instafy/.git")} (auto-detected by walking up from cwd).`);
281
+ console.log("");
282
+ console.log("Example:");
283
+ console.log(` ${kleur.cyan('instafy git status')}`);
284
+ console.log(` ${kleur.cyan('instafy git add -A')}`);
285
+ console.log(` ${kleur.cyan('instafy git commit -am "instafy: checkpoint"')}`);
286
+ return 0;
287
+ }
288
+ const context = findInstafyGitContext(cwd);
289
+ if (!context) {
290
+ throw new Error([
291
+ "No Instafy canonical git checkout found (expected .instafy/.git).",
292
+ "",
293
+ "Tips:",
294
+ `- Run this inside a git-canonical workspace (where ${kleur.cyan(".instafy/.git")} exists).`,
295
+ `- If you're trying to operate on a user repo, use normal ${kleur.cyan("git")} (it uses .git).`,
296
+ ].join("\n"));
297
+ }
298
+ validateGitArgs(userArgs);
299
+ const split = splitGitCommand(userArgs);
300
+ if (split.command === "add") {
301
+ return runInstafyGitAdd(context, split.globalArgs, split.commandArgs, { cwd });
302
+ }
303
+ const args = buildInstafyGitArgs(context, userArgs);
304
+ const result = spawnSync("git", args, { stdio: "inherit", cwd });
305
+ if (result.error) {
306
+ throw new Error(`Failed to run git: ${result.error.message}`);
307
+ }
308
+ return typeof result.status === "number" ? result.status : 1;
309
+ }
310
+ function printGitSyncHelp() {
311
+ console.log("instafy git sync [options]");
312
+ console.log("");
313
+ console.log("Calls the Origin git sync endpoint (POST /git/sync) for the current runtime/workspace.");
314
+ console.log("");
315
+ console.log("Options:");
316
+ console.log(" -m, --message <msg> Commit message (recommended)");
317
+ console.log(" --path <relativePath> Only sync selected path(s) (repeatable)");
318
+ console.log(" --origin-endpoint <url> Override Origin base URL (default: $ORIGIN_ENDPOINT or localhost)");
319
+ console.log(" --origin-token <token> Override Origin bearer token (default: $ORIGIN_INTERNAL_TOKEN)");
320
+ console.log(" --json Output JSON");
321
+ console.log("");
322
+ console.log("Env fallback:");
323
+ console.log(" ORIGIN_ENDPOINT, ORIGIN_BIND_PORT, ORIGIN_INTERNAL_TOKEN, RUNTIME_ACCESS_TOKEN");
324
+ }
325
+ function normalizeToken(raw) {
326
+ if (typeof raw !== "string")
327
+ return null;
328
+ const trimmed = raw.trim();
329
+ return trimmed.length > 0 ? trimmed : null;
330
+ }
331
+ function normalizeUrl(raw) {
332
+ return raw.trim().replace(/\/+$/, "");
333
+ }
334
+ function resolveOriginEndpoint(override) {
335
+ const direct = normalizeToken(override) ?? normalizeToken(process.env["ORIGIN_ENDPOINT"]);
336
+ if (direct) {
337
+ return normalizeUrl(direct);
338
+ }
339
+ const portRaw = normalizeToken(process.env["ORIGIN_BIND_PORT"]);
340
+ const port = portRaw ? Number.parseInt(portRaw, 10) : 54332;
341
+ const resolvedPort = Number.isFinite(port) && port > 0 ? port : 54332;
342
+ return `http://127.0.0.1:${resolvedPort}`;
343
+ }
344
+ function resolveOriginToken(override) {
345
+ const token = normalizeToken(override) ??
346
+ normalizeToken(process.env["ORIGIN_INTERNAL_TOKEN"]) ??
347
+ normalizeToken(process.env["RUNTIME_ACCESS_TOKEN"]);
348
+ if (!token) {
349
+ throw new Error([
350
+ "Origin bearer token missing.",
351
+ "",
352
+ "Provide one of:",
353
+ "- --origin-token <token>",
354
+ "- ORIGIN_INTERNAL_TOKEN=<token> (recommended inside runtimes)",
355
+ "- RUNTIME_ACCESS_TOKEN=<token>",
356
+ ].join("\n"));
357
+ }
358
+ return token;
359
+ }
360
+ function parseGitSyncArgs(args) {
361
+ let message = null;
362
+ let json = false;
363
+ let originEndpointOverride = null;
364
+ let originTokenOverride = null;
365
+ const paths = [];
366
+ for (let index = 0; index < args.length; index += 1) {
367
+ const arg = args[index] ?? "";
368
+ if (arg === "--help" || arg === "-h") {
369
+ return "help";
370
+ }
371
+ if (arg === "--json") {
372
+ json = true;
373
+ continue;
374
+ }
375
+ if (arg === "-m" || arg === "--message") {
376
+ const value = args[index + 1];
377
+ if (typeof value !== "string" || value.trim().length === 0) {
378
+ throw new Error(`${arg} requires a non-empty value`);
379
+ }
380
+ message = value.trim();
381
+ index += 1;
382
+ continue;
383
+ }
384
+ if (arg.startsWith("--message=")) {
385
+ const value = arg.slice("--message=".length).trim();
386
+ if (!value) {
387
+ throw new Error("--message requires a non-empty value");
388
+ }
389
+ message = value;
390
+ continue;
391
+ }
392
+ if (arg === "--origin-endpoint") {
393
+ const value = args[index + 1];
394
+ if (typeof value !== "string" || value.trim().length === 0) {
395
+ throw new Error("--origin-endpoint requires a URL");
396
+ }
397
+ originEndpointOverride = value.trim();
398
+ index += 1;
399
+ continue;
400
+ }
401
+ if (arg.startsWith("--origin-endpoint=")) {
402
+ const value = arg.slice("--origin-endpoint=".length).trim();
403
+ if (!value) {
404
+ throw new Error("--origin-endpoint requires a URL");
405
+ }
406
+ originEndpointOverride = value;
407
+ continue;
408
+ }
409
+ if (arg === "--origin-token") {
410
+ const value = args[index + 1];
411
+ if (typeof value !== "string" || value.trim().length === 0) {
412
+ throw new Error("--origin-token requires a token");
413
+ }
414
+ originTokenOverride = value.trim();
415
+ index += 1;
416
+ continue;
417
+ }
418
+ if (arg.startsWith("--origin-token=")) {
419
+ const value = arg.slice("--origin-token=".length).trim();
420
+ if (!value) {
421
+ throw new Error("--origin-token requires a token");
422
+ }
423
+ originTokenOverride = value;
424
+ continue;
425
+ }
426
+ if (arg === "--path") {
427
+ const value = args[index + 1];
428
+ if (typeof value !== "string" || value.trim().length === 0) {
429
+ throw new Error("--path requires a relative path");
430
+ }
431
+ paths.push(value.trim());
432
+ index += 1;
433
+ continue;
434
+ }
435
+ if (arg.startsWith("--path=")) {
436
+ const value = arg.slice("--path=".length).trim();
437
+ if (!value) {
438
+ throw new Error("--path requires a relative path");
439
+ }
440
+ paths.push(value);
441
+ continue;
442
+ }
443
+ throw new Error(`Unknown argument: ${arg}`);
444
+ }
445
+ const originEndpoint = resolveOriginEndpoint(originEndpointOverride);
446
+ const originToken = resolveOriginToken(originTokenOverride);
447
+ const resolvedMessage = message ?? `instafy: sync ${new Date().toISOString().replace(/\..*$/, "")}Z`;
448
+ return {
449
+ message: resolvedMessage,
450
+ json,
451
+ originEndpoint,
452
+ originToken,
453
+ paths: paths.length > 0 ? paths : null,
454
+ };
455
+ }
456
+ export async function runInstafyGitSync(args, options) {
457
+ void options;
458
+ const parsed = parseGitSyncArgs(args);
459
+ if (parsed === "help") {
460
+ printGitSyncHelp();
461
+ return 0;
462
+ }
463
+ const url = `${parsed.originEndpoint.replace(/\/+$/, "")}/git/sync`;
464
+ const response = await fetch(url, {
465
+ method: "POST",
466
+ headers: {
467
+ accept: "application/json",
468
+ authorization: `Bearer ${parsed.originToken}`,
469
+ "content-type": "application/json",
470
+ },
471
+ body: JSON.stringify({
472
+ message: parsed.message,
473
+ paths: parsed.paths ?? undefined,
474
+ }),
475
+ }).catch((error) => {
476
+ throw new Error(`Origin git sync request failed: ${String(error)}`);
477
+ });
478
+ const text = await response.text().catch(() => "");
479
+ if (!response.ok) {
480
+ const suffix = text.trim() ? `: ${text.trim()}` : "";
481
+ console.error(kleur.red(`Origin git sync failed (${response.status} ${response.statusText})${suffix}`));
482
+ return 1;
483
+ }
484
+ const payload = text.trim() ? JSON.parse(text) : null;
485
+ const rev = typeof payload?.rev === "string" ? payload.rev.trim() : "";
486
+ if (!rev) {
487
+ console.error(kleur.red("Origin git sync response missing rev field."));
488
+ return 1;
489
+ }
490
+ if (parsed.json) {
491
+ console.log(JSON.stringify({ rev }, null, 2));
492
+ }
493
+ else {
494
+ console.log(rev);
495
+ }
496
+ return 0;
497
+ }
package/dist/index.js CHANGED
@@ -4,13 +4,13 @@ import { createRequire } from "node:module";
4
4
  import kleur from "kleur";
5
5
  import { runtimeStart, runtimeStatus, runtimeStop, runtimeToken, findProjectManifest, } from "./runtime.js";
6
6
  import { login, logout } from "./auth.js";
7
- import { gitToken } from "./git.js";
8
7
  import { runGitCredentialHelper } from "./git-credential.js";
9
8
  import { projectInit, projectProfile } from "./project.js";
10
9
  import { listTunnelSessions, startTunnelDetached, stopTunnelSession, tailTunnelLogs, runTunnelCommand } from "./tunnel.js";
11
10
  import { requestControllerApi } from "./api.js";
12
11
  import { configGet, configList, configPath, configSet, configUnset } from "./config-command.js";
13
12
  import { getInstafyProfileConfigPath, listInstafyProfileNames, readInstafyProfileConfig } from "./config.js";
13
+ import { runInstafyGit, runInstafyGitSync } from "./git-wrapper.js";
14
14
  export const program = new Command();
15
15
  const require = createRequire(import.meta.url);
16
16
  const pkg = require("../package.json");
@@ -77,8 +77,12 @@ program
77
77
  process.exit(1);
78
78
  }
79
79
  });
80
- const projectInitCommand = program
81
- .command("project:init")
80
+ const projectCommand = program
81
+ .command("project")
82
+ .description("Create/link and manage Instafy projects");
83
+ projectCommand.action(() => projectCommand.outputHelp());
84
+ const projectInitCommand = projectCommand
85
+ .command("init")
82
86
  .description("Create an Instafy project and link this folder (.instafy/project.json)")
83
87
  .option("--path <dir>", "Directory where the manifest should be written (default: cwd)");
84
88
  addServerUrlOptions(projectInitCommand);
@@ -111,8 +115,8 @@ projectInitCommand
111
115
  process.exit(1);
112
116
  }
113
117
  });
114
- program
115
- .command("project:profile")
118
+ projectCommand
119
+ .command("profile")
116
120
  .description("Get/set the CLI profile for this folder (.instafy/project.json)")
117
121
  .argument("[profile]", "Profile name to set (omit to print current)")
118
122
  .option("--unset", "Clear the configured profile for this folder")
@@ -132,38 +136,24 @@ program
132
136
  process.exit(1);
133
137
  }
134
138
  });
135
- program
136
- .command("profile:list")
137
- .description("List saved CLI profiles (~/.instafy/profiles)")
139
+ const projectListCommand = projectCommand
140
+ .command("list")
141
+ .description("List projects for your account")
142
+ .option("--access-token <token>", "Instafy or Supabase access token");
143
+ addServerUrlOptions(projectListCommand);
144
+ projectListCommand
145
+ .option("--org-id <uuid>", "Filter by organization id")
146
+ .option("--org-slug <slug>", "Filter by organization slug")
138
147
  .option("--json", "Output JSON")
139
148
  .action(async (opts) => {
140
149
  try {
141
- const names = listInstafyProfileNames();
142
- const profiles = names.map((name) => {
143
- const config = readInstafyProfileConfig(name);
144
- return {
145
- name,
146
- path: getInstafyProfileConfigPath(name),
147
- controllerUrl: config.controllerUrl ?? null,
148
- studioUrl: config.studioUrl ?? null,
149
- accessTokenSet: Boolean(config.accessToken),
150
- updatedAt: config.updatedAt ?? null,
151
- };
150
+ await (await import("./project.js")).listProjects({
151
+ controllerUrl: opts.serverUrl ?? opts.controllerUrl,
152
+ accessToken: opts.accessToken,
153
+ orgId: opts.orgId,
154
+ orgSlug: opts.orgSlug,
155
+ json: opts.json,
152
156
  });
153
- if (opts.json) {
154
- console.log(JSON.stringify({ profiles }, null, 2));
155
- return;
156
- }
157
- if (profiles.length === 0) {
158
- console.log(kleur.yellow("No profiles found."));
159
- console.log(`Create one with: ${kleur.cyan("instafy login --profile <name>")}`);
160
- return;
161
- }
162
- console.log(kleur.green("Instafy CLI profiles"));
163
- for (const profile of profiles) {
164
- const token = profile.accessTokenSet ? kleur.green("token") : kleur.yellow("no-token");
165
- console.log(`- ${kleur.cyan(profile.name)} (${token})`);
166
- }
167
157
  }
168
158
  catch (error) {
169
159
  console.error(kleur.red(String(error)));
@@ -171,6 +161,7 @@ program
171
161
  }
172
162
  });
173
163
  const configCommand = program.command("config").description("Get/set saved CLI configuration");
164
+ configCommand.action(() => configCommand.outputHelp());
174
165
  configCommand
175
166
  .command("path")
176
167
  .description("Print the config file path")
@@ -240,8 +231,12 @@ configCommand
240
231
  process.exit(1);
241
232
  }
242
233
  });
243
- const runtimeStartCommand = program
244
- .command("runtime:start")
234
+ const runtimeCommand = program
235
+ .command("runtime")
236
+ .description("Start/stop the local Instafy runtime");
237
+ runtimeCommand.action(() => runtimeCommand.outputHelp());
238
+ const runtimeStartCommand = runtimeCommand
239
+ .command("start")
245
240
  .description("Start a local runtime for this project")
246
241
  .option("--project <id>", "Project UUID");
247
242
  addServerUrlOptions(runtimeStartCommand);
@@ -280,8 +275,8 @@ runtimeStartCommand
280
275
  process.exit(1);
281
276
  }
282
277
  });
283
- program
284
- .command("runtime:status")
278
+ runtimeCommand
279
+ .command("status")
285
280
  .description("Show runtime health")
286
281
  .option("--json", "Output status as JSON")
287
282
  .action(async (opts) => {
@@ -293,8 +288,8 @@ program
293
288
  process.exit(1);
294
289
  }
295
290
  });
296
- program
297
- .command("runtime:stop")
291
+ runtimeCommand
292
+ .command("stop")
298
293
  .description("Stop the local Instafy runtime")
299
294
  .option("--json", "Output result as JSON")
300
295
  .action(async (opts) => {
@@ -306,8 +301,8 @@ program
306
301
  process.exit(1);
307
302
  }
308
303
  });
309
- const runtimeTokenCommand = program
310
- .command("runtime:token")
304
+ const runtimeTokenCommand = runtimeCommand
305
+ .command("token")
311
306
  .description("Mint a runtime access token")
312
307
  .option("--project <id>", "Project UUID (defaults to .instafy/project.json)");
313
308
  addServerUrlOptions(runtimeTokenCommand);
@@ -322,7 +317,7 @@ runtimeTokenCommand
322
317
  ? opts.project.trim()
323
318
  : findProjectManifest(process.cwd()).manifest?.projectId ?? null;
324
319
  if (!project) {
325
- throw new Error("No project configured. Run `instafy project:init` or pass --project.");
320
+ throw new Error("No project configured. Run `instafy project init` or pass --project.");
326
321
  }
327
322
  await runtimeToken({
328
323
  project,
@@ -338,69 +333,23 @@ runtimeTokenCommand
338
333
  process.exit(1);
339
334
  }
340
335
  });
341
- const gitTokenCommand = program
342
- .command("git:token", { hidden: true })
343
- .description("Advanced: mint a git access token for the project repo")
344
- .option("--project <id>", "Project UUID (defaults to .instafy/project.json)");
345
- addServerUrlOptions(gitTokenCommand);
346
- addAccessTokenOptions(gitTokenCommand, "Instafy access token (required)");
347
- gitTokenCommand
348
- .option("--supabase-access-token <token>", "Supabase session token (alternative to Studio token)")
349
- .option("--supabase-access-token-file <path>", "File containing the Supabase session token")
350
- .option("--scope <scope...>", "Requested scopes (git.read, git.write)")
351
- .option("--ttl-seconds <seconds>", "Token TTL in seconds (default server policy)")
352
- .option("--json", "Output token response as JSON")
353
- .action(async (opts) => {
354
- try {
355
- const project = typeof opts.project === "string" && opts.project.trim()
356
- ? opts.project.trim()
357
- : findProjectManifest(process.cwd()).manifest?.projectId ?? null;
358
- if (!project) {
359
- throw new Error("No project configured. Run `instafy project:init` or pass --project.");
360
- }
361
- const ttlSecondsRaw = typeof opts.ttlSeconds === "string" ? opts.ttlSeconds.trim() : "";
362
- const ttlSeconds = ttlSecondsRaw.length > 0 ? Number.parseInt(ttlSecondsRaw, 10) : undefined;
363
- if (ttlSecondsRaw.length > 0 && (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0)) {
364
- throw new Error("--ttl-seconds must be a positive integer");
365
- }
366
- await gitToken({
367
- project,
368
- controllerUrl: opts.serverUrl ?? opts.controllerUrl,
369
- controllerAccessToken: opts.accessToken ?? opts.controllerAccessToken,
370
- supabaseAccessToken: opts.supabaseAccessToken,
371
- supabaseAccessTokenFile: opts.supabaseAccessTokenFile,
372
- scopes: opts.scope,
373
- ttlSeconds,
374
- json: opts.json,
375
- });
376
- }
377
- catch (error) {
378
- console.error(kleur.red(String(error)));
379
- process.exit(1);
380
- }
381
- });
382
336
  program
383
- .command("git:credential", { hidden: true })
384
- .description("Internal: git credential helper (used by Git when configured)")
385
- .argument("<operation>", "Operation (get|store|erase)")
386
- .action(async (operation) => {
387
- try {
388
- await runGitCredentialHelper(operation);
389
- }
390
- catch (error) {
391
- console.error(String(error));
392
- process.exit(1);
393
- }
394
- });
337
+ .command("git")
338
+ .description("Run git against the Instafy canonical checkout (.instafy/.git)")
339
+ .allowUnknownOption(true);
395
340
  const tunnelCommand = program
396
341
  .command("tunnel")
397
- .description("Start a shareable tunnel URL for a local port (defaults to detached)")
342
+ .description("Create and manage shareable tunnels");
343
+ tunnelCommand.action(() => tunnelCommand.outputHelp());
344
+ const tunnelStartCommand = tunnelCommand
345
+ .command("start")
346
+ .description("Create a shareable tunnel URL for a local port (defaults to detached)")
398
347
  .option("--port <port>", "Local port to expose (default 3000)")
399
348
  .option("--project <id>", "Project UUID (defaults to .instafy/project.json or PROJECT_ID)");
400
- addServerUrlOptions(tunnelCommand);
401
- addAccessTokenOptions(tunnelCommand, "Instafy access token (defaults to saved `instafy login` token)");
402
- addServiceTokenOptions(tunnelCommand, "Instafy service token (advanced)");
403
- tunnelCommand
349
+ addServerUrlOptions(tunnelStartCommand);
350
+ addAccessTokenOptions(tunnelStartCommand, "Instafy access token (defaults to saved `instafy login` token)");
351
+ addServiceTokenOptions(tunnelStartCommand, "Instafy service token (advanced)");
352
+ tunnelStartCommand
404
353
  .option("--no-detach", "Run in foreground until interrupted")
405
354
  .option("--rathole-bin <path>", "Path to rathole binary (or set RATHOLE_BIN)")
406
355
  .option("--log-file <path>", "Write tunnel logs to a file (default: ~/.instafy/cli-tunnel-logs/*)")
@@ -439,9 +388,9 @@ tunnelCommand
439
388
  console.log(kleur.gray(`log: ${started.logFile}`));
440
389
  console.log("");
441
390
  console.log("Next:");
442
- console.log(`- ${kleur.cyan(`instafy tunnel:list`)}`);
443
- console.log(`- ${kleur.cyan(`instafy tunnel:logs ${started.tunnelId} --follow`)}`);
444
- console.log(`- ${kleur.cyan(`instafy tunnel:stop ${started.tunnelId}`)}`);
391
+ console.log(`- ${kleur.cyan(`instafy tunnel list`)}`);
392
+ console.log(`- ${kleur.cyan(`instafy tunnel logs ${started.tunnelId} --follow`)}`);
393
+ console.log(`- ${kleur.cyan(`instafy tunnel stop ${started.tunnelId}`)}`);
445
394
  if (process.platform !== "win32") {
446
395
  console.log(kleur.gray(`(or) tail -n 200 -f ${started.logFile}`));
447
396
  }
@@ -451,8 +400,8 @@ tunnelCommand
451
400
  process.exit(1);
452
401
  }
453
402
  });
454
- program
455
- .command("tunnel:list")
403
+ tunnelCommand
404
+ .command("list")
456
405
  .description("List local tunnel sessions started by this CLI")
457
406
  .option("--all", "Include stopped/stale tunnels")
458
407
  .option("--json", "Output JSON")
@@ -476,8 +425,8 @@ program
476
425
  process.exit(1);
477
426
  }
478
427
  });
479
- program
480
- .command("tunnel:stop")
428
+ tunnelCommand
429
+ .command("stop")
481
430
  .description("Stop a local tunnel session and revoke it")
482
431
  .argument("[tunnelId]", "Tunnel ID (defaults to the only active tunnel)")
483
432
  .option("--server-url <url>", "Instafy server URL")
@@ -503,8 +452,8 @@ program
503
452
  process.exit(1);
504
453
  }
505
454
  });
506
- program
507
- .command("tunnel:logs")
455
+ tunnelCommand
456
+ .command("logs")
508
457
  .description("Show logs for a local tunnel session")
509
458
  .argument("[tunnelId]", "Tunnel ID (defaults to the only active tunnel)")
510
459
  .option("--lines <n>", "Number of lines to show", "200")
@@ -525,8 +474,12 @@ program
525
474
  process.exit(1);
526
475
  }
527
476
  });
528
- const orgListCommand = program
529
- .command("org:list")
477
+ const orgCommand = program
478
+ .command("org")
479
+ .description("Organization utilities");
480
+ orgCommand.action(() => orgCommand.outputHelp());
481
+ const orgListCommand = orgCommand
482
+ .command("list")
530
483
  .description("List organizations for your account");
531
484
  addServerUrlOptions(orgListCommand);
532
485
  orgListCommand
@@ -545,24 +498,42 @@ orgListCommand
545
498
  process.exit(1);
546
499
  }
547
500
  });
548
- const projectListCommand = program
549
- .command("project:list")
550
- .description("List projects for your account")
551
- .option("--access-token <token>", "Instafy or Supabase access token");
552
- addServerUrlOptions(projectListCommand);
553
- projectListCommand
554
- .option("--org-id <uuid>", "Filter by organization id")
555
- .option("--org-slug <slug>", "Filter by organization slug")
501
+ const profileCommand = program
502
+ .command("profile")
503
+ .description("Manage saved CLI profiles (~/.instafy/profiles)");
504
+ profileCommand.action(() => profileCommand.outputHelp());
505
+ profileCommand
506
+ .command("list")
507
+ .description("List saved CLI profiles")
556
508
  .option("--json", "Output JSON")
557
509
  .action(async (opts) => {
558
510
  try {
559
- await (await import("./project.js")).listProjects({
560
- controllerUrl: opts.serverUrl ?? opts.controllerUrl,
561
- accessToken: opts.accessToken,
562
- orgId: opts.orgId,
563
- orgSlug: opts.orgSlug,
564
- json: opts.json,
511
+ const names = listInstafyProfileNames();
512
+ const profiles = names.map((name) => {
513
+ const config = readInstafyProfileConfig(name);
514
+ return {
515
+ name,
516
+ path: getInstafyProfileConfigPath(name),
517
+ controllerUrl: config.controllerUrl ?? null,
518
+ studioUrl: config.studioUrl ?? null,
519
+ accessTokenSet: Boolean(config.accessToken),
520
+ updatedAt: config.updatedAt ?? null,
521
+ };
565
522
  });
523
+ if (opts.json) {
524
+ console.log(JSON.stringify({ profiles }, null, 2));
525
+ return;
526
+ }
527
+ if (profiles.length === 0) {
528
+ console.log(kleur.yellow("No profiles found."));
529
+ console.log(`Create one with: ${kleur.cyan("instafy login --profile <name>")}`);
530
+ return;
531
+ }
532
+ console.log(kleur.green("Instafy CLI profiles"));
533
+ for (const profile of profiles) {
534
+ const token = profile.accessTokenSet ? kleur.green("token") : kleur.yellow("no-token");
535
+ console.log(`- ${kleur.cyan(profile.name)} (${token})`);
536
+ }
566
537
  }
567
538
  catch (error) {
568
539
  console.error(kleur.red(String(error)));
@@ -600,23 +571,27 @@ function configureApiCommand(command, method) {
600
571
  }
601
572
  });
602
573
  }
603
- const apiGetCommand = program
604
- .command("api:get", { hidden: true })
574
+ const apiCommand = program
575
+ .command("api", { hidden: true })
576
+ .description("Advanced: authenticated requests to the controller API");
577
+ apiCommand.action(() => apiCommand.outputHelp());
578
+ const apiGetCommand = apiCommand
579
+ .command("get")
605
580
  .description("Advanced: authenticated GET request to the controller API")
606
581
  .argument("<path>", "API path (or full URL), e.g. /conversations/<id>/messages?limit=50");
607
582
  configureApiCommand(apiGetCommand, "GET");
608
- const apiPostCommand = program
609
- .command("api:post", { hidden: true })
583
+ const apiPostCommand = apiCommand
584
+ .command("post")
610
585
  .description("Advanced: authenticated POST request to the controller API")
611
586
  .argument("<path>", "API path (or full URL)");
612
587
  configureApiCommand(apiPostCommand, "POST");
613
- const apiPatchCommand = program
614
- .command("api:patch", { hidden: true })
588
+ const apiPatchCommand = apiCommand
589
+ .command("patch")
615
590
  .description("Advanced: authenticated PATCH request to the controller API")
616
591
  .argument("<path>", "API path (or full URL)");
617
592
  configureApiCommand(apiPatchCommand, "PATCH");
618
- const apiDeleteCommand = program
619
- .command("api:delete", { hidden: true })
593
+ const apiDeleteCommand = apiCommand
594
+ .command("delete")
620
595
  .description("Advanced: authenticated DELETE request to the controller API")
621
596
  .argument("<path>", "API path (or full URL)");
622
597
  configureApiCommand(apiDeleteCommand, "DELETE");
@@ -625,6 +600,27 @@ export async function runCli(argv = process.argv) {
625
600
  program.outputHelp();
626
601
  return;
627
602
  }
603
+ const args = argv.slice(2);
604
+ if (args[0] === "git") {
605
+ if (args[1] === "credential") {
606
+ try {
607
+ await runGitCredentialHelper(args[2] ?? "");
608
+ }
609
+ catch (error) {
610
+ console.error(String(error));
611
+ process.exitCode = 1;
612
+ }
613
+ return;
614
+ }
615
+ if (args[1] === "sync") {
616
+ const code = await runInstafyGitSync(args.slice(2));
617
+ process.exitCode = code;
618
+ return;
619
+ }
620
+ const code = runInstafyGit(args.slice(1));
621
+ process.exitCode = code;
622
+ return;
623
+ }
628
624
  await program.parseAsync(argv);
629
625
  }
630
626
  if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
package/dist/org.js CHANGED
@@ -5,7 +5,7 @@ export async function listOrganizations(params) {
5
5
  const controllerUrl = resolveControllerUrl({ controllerUrl: params.controllerUrl ?? null });
6
6
  const token = resolveUserAccessToken({ accessToken: params.accessToken ?? null });
7
7
  if (!token) {
8
- throw formatAuthRequiredError({ retryCommand: "instafy org:list" });
8
+ throw formatAuthRequiredError({ retryCommand: "instafy org list" });
9
9
  }
10
10
  const response = await fetch(`${controllerUrl}/orgs`, {
11
11
  headers: { authorization: `Bearer ${token}` },
package/dist/project.js CHANGED
@@ -67,7 +67,7 @@ export async function listProjects(options) {
67
67
  const controllerUrl = resolveControllerUrl({ controllerUrl: options.controllerUrl ?? null });
68
68
  const token = resolveUserAccessToken({ accessToken: options.accessToken ?? null });
69
69
  if (!token) {
70
- throw formatAuthRequiredError({ retryCommand: "instafy project:list" });
70
+ throw formatAuthRequiredError({ retryCommand: "instafy project list" });
71
71
  }
72
72
  const orgs = await fetchOrganizations(controllerUrl, token);
73
73
  let targetOrgs = orgs;
@@ -124,7 +124,7 @@ export async function projectInit(options) {
124
124
  });
125
125
  if (!token) {
126
126
  throw formatAuthRequiredError({
127
- retryCommand: "instafy project:init",
127
+ retryCommand: "instafy project init",
128
128
  advancedHint: "pass --access-token or set INSTAFY_ACCESS_TOKEN / SUPABASE_ACCESS_TOKEN",
129
129
  });
130
130
  }
@@ -185,7 +185,7 @@ export function projectProfile(options) {
185
185
  const rootDir = path.resolve(options.path ?? process.cwd());
186
186
  const manifestInfo = findProjectManifest(rootDir);
187
187
  if (!manifestInfo.path || !manifestInfo.manifest) {
188
- throw new Error("No project configured. Run `instafy project:init` in this folder first.");
188
+ throw new Error("No project configured. Run `instafy project init` in this folder first.");
189
189
  }
190
190
  const shouldUpdate = options.unset === true || typeof options.profile === "string";
191
191
  const currentProfile = typeof manifestInfo.manifest.profile === "string" && manifestInfo.manifest.profile.trim()
package/dist/runtime.js CHANGED
@@ -29,7 +29,7 @@ function resolveRuntimeBinary() {
29
29
  return candidate;
30
30
  }
31
31
  }
32
- throw new Error("runtime-agent binary not found. If you're in the instafy repo, run `cargo build -p runtime-agent`. Otherwise install Docker and rerun `instafy runtime:start`.");
32
+ throw new Error("runtime-agent binary not found. If you're in the instafy repo, run `cargo build -p runtime-agent`. Otherwise install Docker and rerun `instafy runtime start`.");
33
33
  }
34
34
  function tryResolveRuntimeBinary() {
35
35
  try {
@@ -289,7 +289,7 @@ export async function runtimeStart(options) {
289
289
  const manifestInfo = findProjectManifest(process.cwd());
290
290
  const projectId = options.project ?? env["PROJECT_ID"] ?? manifestInfo.manifest?.projectId ?? null;
291
291
  if (!projectId) {
292
- throw new Error("No project configured. Run `instafy project:init` in this folder (recommended) or pass --project.");
292
+ throw new Error("No project configured. Run `instafy project init` in this folder (recommended) or pass --project.");
293
293
  }
294
294
  env["PROJECT_ID"] = projectId;
295
295
  const supabaseAccessToken = resolveSupabaseAccessToken(options, env);
package/dist/tunnel.js CHANGED
@@ -32,7 +32,7 @@ function resolveProject(opts) {
32
32
  if (manifest?.projectId) {
33
33
  return manifest.projectId;
34
34
  }
35
- throw new Error("No project configured for this folder.\n\nNext:\n- instafy project:init\n\nOr pass --project <uuid> / set PROJECT_ID.");
35
+ throw new Error("No project configured for this folder.\n\nNext:\n- instafy project init\n\nOr pass --project <uuid> / set PROJECT_ID.");
36
36
  }
37
37
  function resolveControllerUrl(opts) {
38
38
  const explicit = opts.controllerUrl?.trim();
@@ -53,7 +53,7 @@ function resolveControllerUrl(opts) {
53
53
  }
54
54
  return resolveConfiguredControllerUrl() ?? "http://127.0.0.1:8788";
55
55
  }
56
- function resolveControllerToken(opts) {
56
+ function resolveControllerToken(opts, retryCommand = "instafy tunnel start") {
57
57
  const explicit = opts.controllerToken?.trim();
58
58
  if (explicit) {
59
59
  return explicit;
@@ -74,7 +74,7 @@ function resolveControllerToken(opts) {
74
74
  return serviceToken;
75
75
  }
76
76
  throw formatAuthRequiredError({
77
- retryCommand: "instafy tunnel",
77
+ retryCommand,
78
78
  advancedHint: "pass --access-token / --service-token, or set INSTAFY_ACCESS_TOKEN / SUPABASE_ACCESS_TOKEN / INSTAFY_SERVICE_TOKEN",
79
79
  });
80
80
  }
@@ -336,7 +336,7 @@ export async function startTunnelDetached(opts) {
336
336
  exitedEarly = true;
337
337
  exitMessage = code !== null ? `code ${code}` : `signal ${signal}`;
338
338
  });
339
- // Give rathole a moment to fail fast, so `instafy tunnel` can report errors.
339
+ // Give rathole a moment to fail fast, so `instafy tunnel start` can report errors.
340
340
  await new Promise((resolve) => setTimeout(resolve, 250));
341
341
  if (exitedEarly || !isProcessAlive(child.pid ?? -1)) {
342
342
  const logTail = (() => {
@@ -382,7 +382,7 @@ export async function stopTunnelSession(opts) {
382
382
  if (active.length === 1) {
383
383
  return stopTunnelSession({ ...opts, tunnelId: active[0]?.tunnelId });
384
384
  }
385
- throw new Error("Tunnel id is required. Use `instafy tunnel:list` to find it.");
385
+ throw new Error("Tunnel id is required. Use `instafy tunnel list` to find it.");
386
386
  }
387
387
  const entry = removeTunnelState(tunnelId);
388
388
  if (!entry) {
@@ -405,7 +405,7 @@ export async function stopTunnelSession(opts) {
405
405
  }
406
406
  }
407
407
  }
408
- const controllerToken = resolveControllerToken(opts);
408
+ const controllerToken = resolveControllerToken(opts, `instafy tunnel stop ${tunnelId}`);
409
409
  await revokeTunnel(entry.controllerUrl, controllerToken, entry.projectId, entry.tunnelId);
410
410
  try {
411
411
  fs.rmSync(entry.workdir, { recursive: true, force: true });
@@ -429,7 +429,7 @@ export function resolveTunnelLogFile(tunnelId) {
429
429
  if (active.length === 1) {
430
430
  return active[0];
431
431
  }
432
- throw new Error("Tunnel id is required. Use `instafy tunnel:list` to find it.");
432
+ throw new Error("Tunnel id is required. Use `instafy tunnel list` to find it.");
433
433
  }
434
434
  export async function tailTunnelLogs(options) {
435
435
  const entry = resolveTunnelLogFile(options.tunnelId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instafy/cli",
3
- "version": "0.1.8-staging.365",
3
+ "version": "0.1.8-staging.368",
4
4
  "description": "Run Instafy projects locally, link folders to Studio, and share previews/webhooks via tunnels.",
5
5
  "private": false,
6
6
  "publishConfig": {