@instafy/cli 0.2.0 → 0.2.2

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.
Files changed (3) hide show
  1. package/README.md +5 -0
  2. package/dist/cli.js +1547 -309
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -809,7 +809,7 @@ async function resolveOrg(controllerUrl, auth, options, retryCommand) {
809
809
  }
810
810
  const orgSlug = options.orgSlug?.trim() || null;
811
811
  const orgName = options.orgName?.trim() || null;
812
- const studioUrl = resolveConfiguredStudioUrl({ profile: options.profile ?? null }) ?? "https://staging.instafy.dev";
812
+ const studioUrl = resolveConfiguredStudioUrl({ profile: options.profile ?? null }) ?? "https://instafy.dev";
813
813
  const studioOrgUrl = `${studioUrl.replace(/\/$/, "")}/studio?panel=settings`;
814
814
  const allowInteractive = Boolean(
815
815
  input2.isTTY && process.stdout.isTTY && options.json !== true && process.env.CI !== "true"
@@ -1276,7 +1276,7 @@ var org_exports = {};
1276
1276
  __export(org_exports, {
1277
1277
  listOrganizations: () => listOrganizations
1278
1278
  });
1279
- import kleur15 from "kleur";
1279
+ import kleur17 from "kleur";
1280
1280
  function getTeamDisplayName2(name) {
1281
1281
  const trimmed = (name ?? "").trim();
1282
1282
  if (!trimmed || trimmed === "Personal organization" || trimmed === "Personal team" || trimmed === "Personal workspace") {
@@ -1314,11 +1314,11 @@ async function listOrganizations(params) {
1314
1314
  console.log(JSON.stringify(orgs, null, 2));
1315
1315
  } else {
1316
1316
  if (orgs.length === 0) {
1317
- console.log(kleur15.yellow("No teams found for this account."));
1317
+ console.log(kleur17.yellow("No teams found for this account."));
1318
1318
  } else {
1319
1319
  for (const org of orgs) {
1320
1320
  const role = org.role ? ` [${org.role}]` : "";
1321
- console.log(`${kleur15.green(getTeamDisplayName2(org.name))} (${org.id}${role})`);
1321
+ console.log(`${kleur17.green(getTeamDisplayName2(org.name))} (${org.id}${role})`);
1322
1322
  }
1323
1323
  }
1324
1324
  }
@@ -1348,9 +1348,9 @@ var init_hardwareProvider2 = __esm({
1348
1348
  });
1349
1349
 
1350
1350
  // ../instafy-sdk/src/hardwareNode.ts
1351
- import fs12 from "node:fs";
1351
+ import fs13 from "node:fs";
1352
1352
  import os5 from "node:os";
1353
- import path13 from "node:path";
1353
+ import path14 from "node:path";
1354
1354
  function uniqueStrings(values) {
1355
1355
  const seen = /* @__PURE__ */ new Set();
1356
1356
  const result = [];
@@ -1389,14 +1389,14 @@ function classifySerialPort(fileName, fullPath) {
1389
1389
  return null;
1390
1390
  }
1391
1391
  function inspectPort(portPath) {
1392
- const resolvedPath = path13.resolve(portPath);
1392
+ const resolvedPath = path14.resolve(portPath);
1393
1393
  let exists = false;
1394
1394
  let readable = false;
1395
1395
  let writable = false;
1396
1396
  let isCharacterDevice = false;
1397
1397
  let error = null;
1398
1398
  try {
1399
- const stat = fs12.statSync(resolvedPath);
1399
+ const stat = fs13.statSync(resolvedPath);
1400
1400
  exists = true;
1401
1401
  isCharacterDevice = stat.isCharacterDevice();
1402
1402
  } catch (caught) {
@@ -1404,13 +1404,13 @@ function inspectPort(portPath) {
1404
1404
  }
1405
1405
  if (exists) {
1406
1406
  try {
1407
- fs12.accessSync(resolvedPath, fs12.constants.R_OK);
1407
+ fs13.accessSync(resolvedPath, fs13.constants.R_OK);
1408
1408
  readable = true;
1409
1409
  } catch {
1410
1410
  readable = false;
1411
1411
  }
1412
1412
  try {
1413
- fs12.accessSync(resolvedPath, fs12.constants.W_OK);
1413
+ fs13.accessSync(resolvedPath, fs13.constants.W_OK);
1414
1414
  writable = true;
1415
1415
  } catch {
1416
1416
  writable = false;
@@ -1432,12 +1432,12 @@ function listCandidates(scanDirs) {
1432
1432
  for (const scanDir of scanDirs) {
1433
1433
  let entries = [];
1434
1434
  try {
1435
- entries = fs12.readdirSync(scanDir);
1435
+ entries = fs13.readdirSync(scanDir);
1436
1436
  } catch {
1437
1437
  continue;
1438
1438
  }
1439
1439
  for (const entry of entries) {
1440
- const fullPath = path13.join(scanDir, entry);
1440
+ const fullPath = path14.join(scanDir, entry);
1441
1441
  const kind = classifySerialPort(entry, fullPath);
1442
1442
  if (!kind) {
1443
1443
  continue;
@@ -1445,7 +1445,7 @@ function listCandidates(scanDirs) {
1445
1445
  candidates.push({
1446
1446
  path: fullPath,
1447
1447
  kind,
1448
- displayName: path13.basename(fullPath)
1448
+ displayName: path14.basename(fullPath)
1449
1449
  });
1450
1450
  }
1451
1451
  }
@@ -1611,37 +1611,37 @@ __export(hardware_exports, {
1611
1611
  hardwareSerialList: () => hardwareSerialList,
1612
1612
  hardwareSerialProbe: () => hardwareSerialProbe
1613
1613
  });
1614
- import kleur16 from "kleur";
1614
+ import kleur18 from "kleur";
1615
1615
  function printSerialPorts(result) {
1616
1616
  if (result.ports.length === 0) {
1617
- console.log(kleur16.yellow("No local serial devices were discovered."));
1617
+ console.log(kleur18.yellow("No local serial devices were discovered."));
1618
1618
  return;
1619
1619
  }
1620
- console.log(kleur16.cyan(`Local serial devices (${result.platform}):`));
1620
+ console.log(kleur18.cyan(`Local serial devices (${result.platform}):`));
1621
1621
  for (const port of result.ports) {
1622
- const status = port.available ? kleur16.green("available") : kleur16.yellow("not ready");
1622
+ const status = port.available ? kleur18.green("available") : kleur18.yellow("not ready");
1623
1623
  console.log(`- ${port.path} (${port.kind}, ${status})`);
1624
1624
  }
1625
1625
  }
1626
1626
  function printProbe(probe) {
1627
- console.log(kleur16.cyan(`Serial device: ${probe.path}`));
1627
+ console.log(kleur18.cyan(`Serial device: ${probe.path}`));
1628
1628
  console.log(`Exists: ${probe.exists ? "yes" : "no"}`);
1629
1629
  console.log(`Readable: ${probe.readable ? "yes" : "no"}`);
1630
1630
  console.log(`Writable: ${probe.writable ? "yes" : "no"}`);
1631
1631
  console.log(`Character device: ${probe.isCharacterDevice ? "yes" : "no"}`);
1632
- console.log(`Available: ${probe.available ? kleur16.green("yes") : kleur16.yellow("no")}`);
1632
+ console.log(`Available: ${probe.available ? kleur18.green("yes") : kleur18.yellow("no")}`);
1633
1633
  if (probe.error) {
1634
1634
  console.log(`Detail: ${probe.error}`);
1635
1635
  }
1636
1636
  }
1637
1637
  function printIoOpportunities(result) {
1638
1638
  if (result.opportunities.length === 0) {
1639
- console.log(kleur16.yellow("No local IO opportunities were discovered."));
1639
+ console.log(kleur18.yellow("No local IO opportunities were discovered."));
1640
1640
  return;
1641
1641
  }
1642
- console.log(kleur16.cyan(`Local IO opportunities (${result.platform}):`));
1642
+ console.log(kleur18.cyan(`Local IO opportunities (${result.platform}):`));
1643
1643
  for (const opportunity of result.opportunities) {
1644
- const status = opportunity.available ? kleur16.green("available") : kleur16.yellow("not ready");
1644
+ const status = opportunity.available ? kleur18.green("available") : kleur18.yellow("not ready");
1645
1645
  console.log(`- ${opportunity.title} (${status})`);
1646
1646
  console.log(` Resource: ${opportunity.resource.path}`);
1647
1647
  const available = opportunity.actions.filter((action) => action.status === "available").map((action) => action.id);
@@ -1655,8 +1655,8 @@ function printIoOpportunities(result) {
1655
1655
  }
1656
1656
  }
1657
1657
  function printIoActionResult(result) {
1658
- const status = result.ok ? kleur16.green("ok") : kleur16.red("failed");
1659
- console.log(kleur16.cyan(`Local IO action: ${result.actionId} (${status})`));
1658
+ const status = result.ok ? kleur18.green("ok") : kleur18.red("failed");
1659
+ console.log(kleur18.cyan(`Local IO action: ${result.actionId} (${status})`));
1660
1660
  console.log(result.message);
1661
1661
  if (result.serialProbe) {
1662
1662
  printProbe(result.serialProbe);
@@ -1665,11 +1665,11 @@ function printIoActionResult(result) {
1665
1665
  const exit = result.process.exitCode ?? result.process.signal ?? "unknown";
1666
1666
  console.log(`Process exit: ${exit}`);
1667
1667
  if (result.process.stdout.trim()) {
1668
- console.log(kleur16.dim("stdout:"));
1668
+ console.log(kleur18.dim("stdout:"));
1669
1669
  console.log(result.process.stdout.trimEnd());
1670
1670
  }
1671
1671
  if (result.process.stderr.trim()) {
1672
- console.log(kleur16.dim("stderr:"));
1672
+ console.log(kleur18.dim("stderr:"));
1673
1673
  console.log(result.process.stderr.trimEnd());
1674
1674
  }
1675
1675
  }
@@ -1732,7 +1732,7 @@ var init_hardware = __esm({
1732
1732
  import { Command, Option } from "commander";
1733
1733
  import { pathToFileURL as pathToFileURL2 } from "node:url";
1734
1734
  import { createRequire as createRequire3 } from "node:module";
1735
- import kleur17 from "kleur";
1735
+ import kleur19 from "kleur";
1736
1736
 
1737
1737
  // src/runtime.ts
1738
1738
  import { spawn as spawn2, spawnSync } from "node:child_process";
@@ -3139,7 +3139,7 @@ async function isStudioHealthy(studioUrl, timeoutMs) {
3139
3139
  }
3140
3140
  }
3141
3141
  async function resolveDefaultStudioUrl(controllerUrl) {
3142
- const hosted = "https://staging.instafy.dev";
3142
+ const hosted = "https://instafy.dev";
3143
3143
  if (isStagingCli2) {
3144
3144
  return hosted;
3145
3145
  }
@@ -3909,7 +3909,7 @@ function normalizeSecretRecord(input3) {
3909
3909
  }
3910
3910
  return out;
3911
3911
  };
3912
- const toMaybeString = (value) => {
3912
+ const toMaybeString2 = (value) => {
3913
3913
  if (typeof value !== "string") {
3914
3914
  return null;
3915
3915
  }
@@ -3922,10 +3922,10 @@ function normalizeSecretRecord(input3) {
3922
3922
  description,
3923
3923
  agentIds: toStringArray(record["agentIds"]),
3924
3924
  agentHandles: toStringArray(record["agentHandles"]),
3925
- lastUsedAt: toMaybeString(record["lastUsedAt"]),
3926
- revokedAt: toMaybeString(record["revokedAt"]),
3927
- createdAt: toMaybeString(record["createdAt"]),
3928
- updatedAt: toMaybeString(record["updatedAt"])
3925
+ lastUsedAt: toMaybeString2(record["lastUsedAt"]),
3926
+ revokedAt: toMaybeString2(record["revokedAt"]),
3927
+ createdAt: toMaybeString2(record["createdAt"]),
3928
+ updatedAt: toMaybeString2(record["updatedAt"])
3929
3929
  };
3930
3930
  }
3931
3931
  function normalizeSecretList(payload) {
@@ -4160,6 +4160,8 @@ init_config();
4160
4160
  init_controller_fetch();
4161
4161
  init_errors();
4162
4162
  init_project_manifest();
4163
+ import fs6 from "node:fs";
4164
+ import path7 from "node:path";
4163
4165
  import kleur5 from "kleur";
4164
4166
  var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4165
4167
  function normalizeUuidParam(raw) {
@@ -4253,24 +4255,25 @@ function normalizeAutomationRecord(input3) {
4253
4255
  if (!id || !name)
4254
4256
  return null;
4255
4257
  const toMaybeNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
4256
- const toMaybeString = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
4258
+ const toMaybeString2 = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
4257
4259
  return {
4258
4260
  id,
4259
4261
  name,
4260
4262
  scheduleKind: typeof record.scheduleKind === "string" ? record.scheduleKind : "",
4261
- runAt: toMaybeString(record.runAt),
4263
+ runAt: toMaybeString2(record.runAt),
4262
4264
  intervalHours: toMaybeNumber(record.intervalHours),
4263
4265
  byDay: Array.isArray(record.byDay) ? record.byDay.filter((entry) => typeof entry === "string") : [],
4264
4266
  byHour: toMaybeNumber(record.byHour),
4265
4267
  byMinute: toMaybeNumber(record.byMinute),
4266
4268
  timezone: typeof record.timezone === "string" ? record.timezone : "UTC",
4267
4269
  runtimeMode: typeof record.runtimeMode === "string" ? record.runtimeMode : "auto",
4268
- runtimeProvider: toMaybeString(record.runtimeProvider),
4270
+ runtimeProvider: toMaybeString2(record.runtimeProvider),
4269
4271
  silentWhenNothingToReport: record.silentWhenNothingToReport === true,
4272
+ resultVisibility: record.resultVisibility === "team" ? "team" : "private",
4270
4273
  status: typeof record.status === "string" ? record.status : "active",
4271
- nextRunAt: toMaybeString(record.nextRunAt),
4272
- lastRunAt: toMaybeString(record.lastRunAt),
4273
- lastError: toMaybeString(record.lastError)
4274
+ nextRunAt: toMaybeString2(record.nextRunAt),
4275
+ lastRunAt: toMaybeString2(record.lastRunAt),
4276
+ lastError: toMaybeString2(record.lastError)
4274
4277
  };
4275
4278
  }
4276
4279
  function listToRecords(payload) {
@@ -4369,9 +4372,10 @@ async function automationsList(options) {
4369
4372
  const status = record.status === "paused" ? kleur5.gray("paused") : kleur5.green("active");
4370
4373
  const next = record.nextRunAt ? kleur5.cyan(record.nextRunAt) : kleur5.gray("n/a");
4371
4374
  const delivery = record.silentWhenNothingToReport ? " \xB7 findings only" : "";
4375
+ const sharing = record.resultVisibility === "team" ? ` \xB7 ${kleur5.cyan("team-visible")}` : "";
4372
4376
  console.log(`${kleur5.bold(record.name)} ${kleur5.gray(record.id)}`);
4373
4377
  console.log(
4374
- ` ${schedule} \xB7 ${record.runtimeMode}${delivery} \xB7 ${status} \xB7 next ${next}`
4378
+ ` ${schedule} \xB7 ${record.runtimeMode}${delivery}${sharing} \xB7 ${status} \xB7 next ${next}`
4375
4379
  );
4376
4380
  if (record.lastError) {
4377
4381
  console.log(` ${kleur5.red(record.lastError)}`);
@@ -4399,6 +4403,7 @@ async function automationsCreate(options) {
4399
4403
  runtimeMode: options.runtimeMode ?? "auto",
4400
4404
  runtimeProvider: options.runtimeProvider?.trim() || void 0,
4401
4405
  silentWhenNothingToReport: options.silentWhenNothingToReport ?? false,
4406
+ resultVisibility: options.resultVisibility ?? void 0,
4402
4407
  status: options.paused ? "paused" : "active"
4403
4408
  };
4404
4409
  if (scheduleKind === "once") {
@@ -4435,6 +4440,138 @@ async function automationsCreate(options) {
4435
4440
  }
4436
4441
  console.log(`${kleur5.green("Created")} ${kleur5.bold(record.name)} (${record.id})`);
4437
4442
  }
4443
+ var SCHEDULE_KINDS = /* @__PURE__ */ new Set(["weekly", "hourly", "once"]);
4444
+ var RUNTIME_MODES = /* @__PURE__ */ new Set(["auto", "hosted", "existing"]);
4445
+ var UPDATE_FIELD_FLAGS = "--name, --prompt, --prompt-file, --schedule-kind, --run-at, --interval-hours, --days, --time, --timezone, --runtime-mode, --runtime-provider, --silent-when-nothing-to-report, --no-silent-when-nothing-to-report, --share-results, --no-share-results, --result-visibility";
4446
+ function readUpdatePrompt(options, cwd) {
4447
+ const hasPrompt = typeof options.prompt === "string";
4448
+ const hasPromptFile = typeof options.promptFile === "string";
4449
+ if (hasPrompt && hasPromptFile) {
4450
+ throw new Error("Pass either --prompt or --prompt-file, not both");
4451
+ }
4452
+ if (hasPromptFile) {
4453
+ const promptPath = path7.resolve(cwd, (options.promptFile ?? "").trim());
4454
+ let raw;
4455
+ try {
4456
+ raw = fs6.readFileSync(promptPath, "utf8");
4457
+ } catch (error) {
4458
+ const message = error instanceof Error ? error.message : String(error);
4459
+ throw new Error(`Unable to read --prompt-file ${promptPath}: ${message}`);
4460
+ }
4461
+ const promptText = raw.trim();
4462
+ if (!promptText) {
4463
+ throw new Error(`--prompt-file ${promptPath} is empty`);
4464
+ }
4465
+ return promptText;
4466
+ }
4467
+ if (hasPrompt) {
4468
+ const promptText = (options.prompt ?? "").trim();
4469
+ if (!promptText) {
4470
+ throw new Error("--prompt must not be empty");
4471
+ }
4472
+ return promptText;
4473
+ }
4474
+ return void 0;
4475
+ }
4476
+ function buildAutomationUpdateBody(options, cwd) {
4477
+ const body = {};
4478
+ if (typeof options.name === "string") {
4479
+ const name = options.name.trim();
4480
+ if (!name) {
4481
+ throw new Error("--name must not be empty");
4482
+ }
4483
+ body.name = name;
4484
+ }
4485
+ const promptText = readUpdatePrompt(options, cwd);
4486
+ if (promptText !== void 0) {
4487
+ body.promptText = promptText;
4488
+ }
4489
+ if (typeof options.scheduleKind === "string") {
4490
+ const scheduleKind = options.scheduleKind.trim().toLowerCase();
4491
+ if (!SCHEDULE_KINDS.has(scheduleKind)) {
4492
+ throw new Error("--schedule-kind must be weekly, hourly, or once");
4493
+ }
4494
+ body.scheduleKind = scheduleKind;
4495
+ }
4496
+ if (typeof options.runAt === "string") {
4497
+ const runAt = options.runAt.trim();
4498
+ if (!runAt) {
4499
+ throw new Error("--run-at must not be empty");
4500
+ }
4501
+ body.runAt = runAt;
4502
+ }
4503
+ if (options.intervalHours !== void 0) {
4504
+ const interval = Number(options.intervalHours);
4505
+ if (!Number.isFinite(interval) || interval < 1) {
4506
+ throw new Error("--interval-hours must be >= 1");
4507
+ }
4508
+ body.intervalHours = interval;
4509
+ }
4510
+ if (typeof options.days === "string") {
4511
+ body.byDay = normalizeDays(options.days);
4512
+ }
4513
+ if (typeof options.time === "string") {
4514
+ const { hour, minute } = parseTime(options.time);
4515
+ body.byHour = hour;
4516
+ body.byMinute = minute;
4517
+ }
4518
+ if (typeof options.timezone === "string") {
4519
+ const timezone = options.timezone.trim();
4520
+ if (!timezone) {
4521
+ throw new Error("--timezone must not be empty");
4522
+ }
4523
+ body.timezone = timezone;
4524
+ }
4525
+ if (typeof options.runtimeMode === "string") {
4526
+ const runtimeMode = options.runtimeMode.trim().toLowerCase();
4527
+ if (!RUNTIME_MODES.has(runtimeMode)) {
4528
+ throw new Error("--runtime-mode must be auto, hosted, or existing");
4529
+ }
4530
+ body.runtimeMode = runtimeMode;
4531
+ }
4532
+ if (typeof options.runtimeProvider === "string") {
4533
+ const runtimeProvider = options.runtimeProvider.trim();
4534
+ if (!runtimeProvider) {
4535
+ throw new Error("--runtime-provider must not be empty");
4536
+ }
4537
+ body.runtimeProvider = runtimeProvider;
4538
+ }
4539
+ if (typeof options.silentWhenNothingToReport === "boolean") {
4540
+ body.silentWhenNothingToReport = options.silentWhenNothingToReport;
4541
+ }
4542
+ if (options.resultVisibility !== void 0) {
4543
+ body.resultVisibility = options.resultVisibility;
4544
+ }
4545
+ return body;
4546
+ }
4547
+ async function automationsUpdate(options) {
4548
+ const automationId = normalizeUuidParam(options.automationId);
4549
+ if (!automationId) {
4550
+ throw new Error("automationId must be a UUID");
4551
+ }
4552
+ const cwd = options.cwd ?? process.cwd();
4553
+ const body = buildAutomationUpdateBody(options, cwd);
4554
+ if (Object.keys(body).length === 0) {
4555
+ throw new Error(`Nothing to update. Pass at least one of ${UPDATE_FIELD_FLAGS}.`);
4556
+ }
4557
+ const auth = resolveControllerAuth2(options, "instafy login");
4558
+ const payload = await controllerJsonRequest2(auth, "instafy login", {
4559
+ method: "PATCH",
4560
+ path: `/automations/${automationId}`,
4561
+ body
4562
+ });
4563
+ if (options.json) {
4564
+ console.log(JSON.stringify(payload, null, 2));
4565
+ return;
4566
+ }
4567
+ const record = normalizeAutomationRecord(payload);
4568
+ if (!record) {
4569
+ console.log(kleur5.green("Automation updated."));
4570
+ return;
4571
+ }
4572
+ const sharing = record.resultVisibility === "team" ? " \xB7 results shared with the team" : record.resultVisibility === "private" ? " \xB7 results kept private" : "";
4573
+ console.log(`${kleur5.green("Updated")} ${kleur5.bold(record.name)} (${record.id})${sharing}`);
4574
+ }
4438
4575
  async function automationsUpdateStatus(options) {
4439
4576
  const auth = resolveControllerAuth2(options, "instafy login");
4440
4577
  const automationId = normalizeUuidParam(options.automationId);
@@ -4485,13 +4622,375 @@ async function automationsDelete(options) {
4485
4622
  console.log(kleur5.green("Deleted."));
4486
4623
  }
4487
4624
 
4625
+ // src/credentials.ts
4626
+ init_config();
4627
+ init_controller_fetch();
4628
+ init_errors();
4629
+ import kleur6 from "kleur";
4630
+ var promptsModule2 = null;
4631
+ async function loadPrompts2() {
4632
+ promptsModule2 ??= import("@clack/prompts");
4633
+ return promptsModule2;
4634
+ }
4635
+ var RETRY_COMMAND = "instafy login";
4636
+ var SHORT_ID_LENGTH = 8;
4637
+ var TEST_OUTPUT_PREVIEW_CHARS = 300;
4638
+ var CREDENTIAL_TEST_TIMEOUT_MS = 9e4;
4639
+ function resolveControllerAuth3(options) {
4640
+ const cwd = options.cwd ?? process.cwd();
4641
+ const controllerUrl = resolveControllerUrl({
4642
+ controllerUrl: options.controllerUrl ?? null,
4643
+ cwd
4644
+ });
4645
+ const resolved = resolveUserAccessTokenWithSource({
4646
+ accessToken: options.accessToken ?? null,
4647
+ cwd
4648
+ });
4649
+ if (!resolved.token) {
4650
+ throw formatAuthRequiredError({
4651
+ retryCommand: RETRY_COMMAND,
4652
+ advancedHint: "pass --access-token, or set INSTAFY_ACCESS_TOKEN / SUPABASE_ACCESS_TOKEN"
4653
+ });
4654
+ }
4655
+ return {
4656
+ controllerUrl,
4657
+ accessToken: resolved.token,
4658
+ tokenSource: resolved.source,
4659
+ profile: resolved.profile,
4660
+ cwd
4661
+ };
4662
+ }
4663
+ async function controllerJsonRequest3(auth, params) {
4664
+ const url = `${auth.controllerUrl.replace(/\/$/, "")}${params.path}`;
4665
+ const headers = new Headers();
4666
+ headers.set("accept", "application/json");
4667
+ const init = { method: params.method, headers };
4668
+ if (params.timeoutMs !== void 0) {
4669
+ init.signal = AbortSignal.timeout(params.timeoutMs);
4670
+ }
4671
+ const response = (await fetchWithControllerAuth({
4672
+ url,
4673
+ init,
4674
+ accessToken: auth.accessToken,
4675
+ tokenSource: auth.tokenSource,
4676
+ profile: auth.profile,
4677
+ cwd: auth.cwd
4678
+ })).response;
4679
+ const text = await response.text().catch(() => "");
4680
+ if (response.status === 401 || response.status === 403) {
4681
+ throw formatAuthRejectedError({
4682
+ status: response.status,
4683
+ responseBody: text,
4684
+ retryCommand: RETRY_COMMAND,
4685
+ advancedHint: "run `instafy login`, or pass --access-token"
4686
+ });
4687
+ }
4688
+ if (!response.ok) {
4689
+ throw new Error(`Request failed (${response.status} ${response.statusText}): ${text}`);
4690
+ }
4691
+ return text ? JSON.parse(text) : null;
4692
+ }
4693
+ function credentialTestTimeoutMs() {
4694
+ const configured = Number(process.env["INSTAFY_HTTP_TIMEOUT_MS"]);
4695
+ const base = Number.isFinite(configured) && configured > 0 ? configured : 0;
4696
+ return Math.max(base, CREDENTIAL_TEST_TIMEOUT_MS);
4697
+ }
4698
+ function toMaybeString(value) {
4699
+ return typeof value === "string" && value.trim() ? value.trim() : null;
4700
+ }
4701
+ function normalizeCredentialRecord(input3) {
4702
+ if (!input3 || typeof input3 !== "object")
4703
+ return null;
4704
+ const record = input3;
4705
+ const id = toMaybeString(record.id);
4706
+ if (!id)
4707
+ return null;
4708
+ const metadata = record.metadata && typeof record.metadata === "object" ? record.metadata : {};
4709
+ return {
4710
+ id,
4711
+ kind: toMaybeString(record.kind) ?? "unknown",
4712
+ label: toMaybeString(record.label),
4713
+ provider: toMaybeString(metadata.provider),
4714
+ defaultModel: toMaybeString(metadata.default_model),
4715
+ isDefault: record.isDefault === true,
4716
+ lastUsedAt: toMaybeString(record.lastUsedAt),
4717
+ revokedAt: toMaybeString(record.revokedAt),
4718
+ createdAt: toMaybeString(record.createdAt),
4719
+ updatedAt: toMaybeString(record.updatedAt)
4720
+ };
4721
+ }
4722
+ function listToRecords2(payload) {
4723
+ if (!Array.isArray(payload))
4724
+ return [];
4725
+ const out = [];
4726
+ for (const item of payload) {
4727
+ const normalized = normalizeCredentialRecord(item);
4728
+ if (normalized)
4729
+ out.push(normalized);
4730
+ }
4731
+ return out;
4732
+ }
4733
+ function normalizeTestResult(payload) {
4734
+ const record = payload && typeof payload === "object" ? payload : {};
4735
+ const elapsed = record.elapsedMs;
4736
+ return {
4737
+ ok: record.ok === true,
4738
+ provider: toMaybeString(record.provider),
4739
+ model: toMaybeString(record.model),
4740
+ output: typeof record.output === "string" ? record.output : null,
4741
+ upstreamEndpoint: toMaybeString(record.upstreamEndpoint),
4742
+ elapsedMs: typeof elapsed === "number" && Number.isFinite(elapsed) ? elapsed : null
4743
+ };
4744
+ }
4745
+ async function fetchCredentialRecords(auth) {
4746
+ const payload = await controllerJsonRequest3(auth, {
4747
+ method: "GET",
4748
+ path: "/me/credentials"
4749
+ });
4750
+ return listToRecords2(payload);
4751
+ }
4752
+ function shortCredentialId(id) {
4753
+ return id.slice(0, SHORT_ID_LENGTH);
4754
+ }
4755
+ function describeCredential(record) {
4756
+ const parts = [shortCredentialId(record.id), record.kind];
4757
+ if (record.provider)
4758
+ parts.push(record.provider);
4759
+ if (record.label)
4760
+ parts.push(`"${record.label}"`);
4761
+ if (record.revokedAt)
4762
+ parts.push("revoked");
4763
+ return parts.join(" \xB7 ");
4764
+ }
4765
+ function resolveCredentialRecord(records, idOrPrefix) {
4766
+ const needle = idOrPrefix.trim().toLowerCase();
4767
+ if (!needle) {
4768
+ throw new Error("Credential id is required. Run `instafy credentials list` to see ids.");
4769
+ }
4770
+ const exact = records.find((record) => record.id.toLowerCase() === needle);
4771
+ if (exact)
4772
+ return exact;
4773
+ const matches = records.filter((record) => record.id.toLowerCase().startsWith(needle));
4774
+ const active = matches.filter((record) => !record.revokedAt);
4775
+ if (active.length === 1) {
4776
+ return active[0];
4777
+ }
4778
+ if (active.length > 1) {
4779
+ const candidates = active.map((record) => ` ${describeCredential(record)}`).join("\n");
4780
+ throw new Error(
4781
+ `Credential id prefix "${idOrPrefix}" is ambiguous; it matches ${active.length} credentials:
4782
+ ${candidates}
4783
+
4784
+ Use a longer prefix or the full id.`
4785
+ );
4786
+ }
4787
+ if (matches.length > 0) {
4788
+ throw new Error(
4789
+ `Credential "${idOrPrefix}" only matches revoked credentials. Run \`instafy credentials list --all\` to see them.`
4790
+ );
4791
+ }
4792
+ throw new Error(
4793
+ `No credential matches "${idOrPrefix}". Run \`instafy credentials list\` to see ids.`
4794
+ );
4795
+ }
4796
+ async function resolveCredential(auth, idOrPrefix) {
4797
+ const records = await fetchCredentialRecords(auth);
4798
+ return resolveCredentialRecord(records, idOrPrefix);
4799
+ }
4800
+ async function resolveActiveCredential(auth, idOrPrefix, action) {
4801
+ const record = await resolveCredential(auth, idOrPrefix);
4802
+ if (record.revokedAt) {
4803
+ throw new Error(
4804
+ `Credential ${describeCredential(record)} is revoked and cannot be ${action}. Run \`instafy credentials list\` to see active ids.`
4805
+ );
4806
+ }
4807
+ return record;
4808
+ }
4809
+ function formatTimestamp(value) {
4810
+ if (!value)
4811
+ return "never";
4812
+ const parsed = new Date(value);
4813
+ if (Number.isNaN(parsed.getTime()))
4814
+ return value;
4815
+ return `${parsed.toISOString().slice(0, 16).replace("T", " ")}Z`;
4816
+ }
4817
+ function truncateOutput(output2, limit) {
4818
+ const singleLine = output2.replace(/\s+/g, " ").trim();
4819
+ if (singleLine.length <= limit)
4820
+ return singleLine;
4821
+ return `${singleLine.slice(0, limit)}\u2026`;
4822
+ }
4823
+ function renderTable(headers, rows) {
4824
+ const widths = headers.map(
4825
+ (header, index) => Math.max(header.length, ...rows.map((row) => row[index]?.length ?? 0))
4826
+ );
4827
+ const line = (cells) => cells.map((cell, index) => cell.padEnd(widths[index])).join(" ").trimEnd();
4828
+ return [kleur6.bold(line(headers)), ...rows.map(line)].join("\n");
4829
+ }
4830
+ async function credentialsList(options) {
4831
+ const auth = resolveControllerAuth3(options);
4832
+ const records = await fetchCredentialRecords(auth);
4833
+ const visible = options.all ? records : records.filter((record) => !record.revokedAt);
4834
+ const hiddenRevoked = records.length - visible.length;
4835
+ if (options.json) {
4836
+ console.log(JSON.stringify(visible, null, 2));
4837
+ return;
4838
+ }
4839
+ if (visible.length === 0) {
4840
+ console.log(kleur6.gray(records.length === 0 ? "No credentials." : "No active credentials."));
4841
+ if (hiddenRevoked > 0) {
4842
+ console.log(kleur6.gray(`${hiddenRevoked} revoked hidden (use --all to show).`));
4843
+ }
4844
+ return;
4845
+ }
4846
+ const rows = visible.map((record) => [
4847
+ shortCredentialId(record.id),
4848
+ record.kind,
4849
+ record.provider ?? "-",
4850
+ record.defaultModel ?? "-",
4851
+ record.label ?? "-",
4852
+ record.isDefault ? "*" : "",
4853
+ formatTimestamp(record.lastUsedAt),
4854
+ record.revokedAt ? formatTimestamp(record.revokedAt) : ""
4855
+ ]);
4856
+ console.log(
4857
+ renderTable(
4858
+ ["ID", "KIND", "PROVIDER", "MODEL", "LABEL", "DEFAULT", "LAST USED", "REVOKED"],
4859
+ rows
4860
+ )
4861
+ );
4862
+ if (hiddenRevoked > 0) {
4863
+ console.log(kleur6.gray(`${hiddenRevoked} revoked hidden (use --all to show).`));
4864
+ }
4865
+ if (!visible.some((record) => record.isDefault && !record.revokedAt)) {
4866
+ console.log(
4867
+ kleur6.yellow(
4868
+ "No default credential. Jobs cannot pick one automatically; run `instafy credentials default <id>`."
4869
+ )
4870
+ );
4871
+ }
4872
+ }
4873
+ async function credentialsTest(options) {
4874
+ const auth = resolveControllerAuth3(options);
4875
+ const record = await resolveActiveCredential(auth, options.idOrPrefix, "tested");
4876
+ if (!options.json) {
4877
+ console.log(
4878
+ kleur6.gray(
4879
+ `Testing ${describeCredential(record)} through the proxy (this calls the upstream provider and may take up to a minute)\u2026`
4880
+ )
4881
+ );
4882
+ }
4883
+ const payload = await controllerJsonRequest3(auth, {
4884
+ method: "POST",
4885
+ path: `/me/credentials/${encodeURIComponent(record.id)}/test`,
4886
+ timeoutMs: credentialTestTimeoutMs()
4887
+ });
4888
+ const result = normalizeTestResult(payload);
4889
+ if (options.json) {
4890
+ console.log(JSON.stringify({ credentialId: record.id, ...result }, null, 2));
4891
+ return result;
4892
+ }
4893
+ const status = result.ok ? kleur6.green("ok") : kleur6.red("failed");
4894
+ console.log(`${status} ${shortCredentialId(record.id)}`);
4895
+ console.log(` provider ${result.provider ?? "-"} \xB7 model ${result.model ?? "-"}`);
4896
+ if (result.upstreamEndpoint) {
4897
+ console.log(` upstream ${result.upstreamEndpoint}`);
4898
+ }
4899
+ if (result.elapsedMs !== null) {
4900
+ console.log(` elapsed ${result.elapsedMs} ms`);
4901
+ }
4902
+ if (result.output) {
4903
+ const preview = truncateOutput(result.output, TEST_OUTPUT_PREVIEW_CHARS);
4904
+ console.log(` output ${result.ok ? preview : kleur6.red(preview)}`);
4905
+ }
4906
+ return result;
4907
+ }
4908
+ async function credentialsSetDefault(options) {
4909
+ const auth = resolveControllerAuth3(options);
4910
+ const record = await resolveActiveCredential(auth, options.idOrPrefix, "made the default");
4911
+ const payload = await controllerJsonRequest3(auth, {
4912
+ method: "POST",
4913
+ path: `/me/credentials/${encodeURIComponent(record.id)}/default`
4914
+ });
4915
+ const response = payload && typeof payload === "object" ? payload : {};
4916
+ if (options.json) {
4917
+ console.log(
4918
+ JSON.stringify(
4919
+ {
4920
+ ok: true,
4921
+ credentialId: toMaybeString(response.credentialId) ?? record.id,
4922
+ kind: toMaybeString(response.kind) ?? record.kind,
4923
+ isDefault: response.isDefault !== false
4924
+ },
4925
+ null,
4926
+ 2
4927
+ )
4928
+ );
4929
+ return;
4930
+ }
4931
+ console.log(`${kleur6.green("Default set:")} ${describeCredential(record)}`);
4932
+ }
4933
+ async function credentialsClearDefault(options) {
4934
+ const auth = resolveControllerAuth3(options);
4935
+ await controllerJsonRequest3(auth, {
4936
+ method: "DELETE",
4937
+ path: "/me/credentials/default"
4938
+ });
4939
+ if (options.json) {
4940
+ console.log(JSON.stringify({ ok: true, hasDefaultCredential: false }, null, 2));
4941
+ return;
4942
+ }
4943
+ console.log(kleur6.green("Default cleared."));
4944
+ console.log(kleur6.gray("Jobs will not pick a credential automatically until you set a new default."));
4945
+ }
4946
+ async function confirmRevoke(record, json) {
4947
+ const interactive = Boolean(
4948
+ process.stdin.isTTY && process.stdout.isTTY && json !== true && process.env.CI !== "true"
4949
+ );
4950
+ if (!interactive) {
4951
+ throw new Error(
4952
+ `Refusing to revoke ${describeCredential(record)} without confirmation. Re-run with --yes.`
4953
+ );
4954
+ }
4955
+ const { confirm, isCancel } = await loadPrompts2();
4956
+ console.log(kleur6.yellow(`Revoke ${describeCredential(record)} (${record.id})?`));
4957
+ console.log(kleur6.gray("Agents bound to this credential are disconnected and jobs stop using it."));
4958
+ const answer = await confirm({ message: "Revoke this credential?", initialValue: false });
4959
+ if (isCancel(answer) || !answer) {
4960
+ throw new Error("Cancelled.");
4961
+ }
4962
+ }
4963
+ async function credentialsRevoke(options) {
4964
+ const auth = resolveControllerAuth3(options);
4965
+ const record = await resolveActiveCredential(auth, options.idOrPrefix, "revoked again");
4966
+ if (!options.yes) {
4967
+ await confirmRevoke(record, options.json);
4968
+ }
4969
+ await controllerJsonRequest3(auth, {
4970
+ method: "DELETE",
4971
+ path: `/me/credentials/${encodeURIComponent(record.id)}`
4972
+ });
4973
+ if (options.json) {
4974
+ console.log(JSON.stringify({ ok: true, credentialId: record.id }, null, 2));
4975
+ return;
4976
+ }
4977
+ console.log(`${kleur6.green("Revoked:")} ${describeCredential(record)}`);
4978
+ if (record.isDefault) {
4979
+ console.log(
4980
+ kleur6.yellow(
4981
+ "This was the default credential. Set a new one with `instafy credentials default <id>`."
4982
+ )
4983
+ );
4984
+ }
4985
+ }
4986
+
4488
4987
  // src/tunnel.ts
4489
- import fs6 from "node:fs";
4988
+ import fs7 from "node:fs";
4490
4989
  import os4 from "node:os";
4491
- import path7 from "node:path";
4990
+ import path8 from "node:path";
4492
4991
  import { spawn as spawn3 } from "node:child_process";
4493
4992
  import { randomUUID } from "node:crypto";
4494
- import kleur6 from "kleur";
4993
+ import kleur7 from "kleur";
4495
4994
  init_config();
4496
4995
  init_errors();
4497
4996
  init_controller_fetch();
@@ -4551,10 +5050,10 @@ function resolveRuntimeBoundControllerUrl(credential, selectedControllerUrl) {
4551
5050
  }
4552
5051
 
4553
5052
  // src/tunnel.ts
4554
- var INSTAFY_DIR3 = path7.join(os4.homedir(), ".instafy");
4555
- var TUNNEL_STATE_FILE = path7.join(INSTAFY_DIR3, "cli-tunnel-state.json");
4556
- var TUNNEL_LOG_DIR = path7.join(INSTAFY_DIR3, "cli-tunnel-logs");
4557
- var TUNNEL_WORKDIR_DIR = path7.join(INSTAFY_DIR3, "cli-tunnel-workdirs");
5053
+ var INSTAFY_DIR3 = path8.join(os4.homedir(), ".instafy");
5054
+ var TUNNEL_STATE_FILE = path8.join(INSTAFY_DIR3, "cli-tunnel-state.json");
5055
+ var TUNNEL_LOG_DIR = path8.join(INSTAFY_DIR3, "cli-tunnel-logs");
5056
+ var TUNNEL_WORKDIR_DIR = path8.join(INSTAFY_DIR3, "cli-tunnel-workdirs");
4558
5057
  var RATHOLE_ENVIRONMENT_KEYS = [
4559
5058
  "PATH",
4560
5059
  "HOME",
@@ -4636,7 +5135,7 @@ function resolveProject(opts) {
4636
5135
  );
4637
5136
  }
4638
5137
  function writeProjectManifest(manifestPath, manifest) {
4639
- fs6.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
5138
+ fs7.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
4640
5139
  }
4641
5140
  function resolveTunnelAlias(opts, projectId) {
4642
5141
  const name = normalizeTunnelPurpose(opts.name?.trim() ?? "") ?? "web";
@@ -4678,7 +5177,7 @@ function persistTunnelAliasResult(config, hostname, url) {
4678
5177
  if (!config.manifestPath)
4679
5178
  return;
4680
5179
  try {
4681
- const raw = fs6.readFileSync(config.manifestPath, "utf8");
5180
+ const raw = fs7.readFileSync(config.manifestPath, "utf8");
4682
5181
  const parsed = JSON.parse(raw);
4683
5182
  if (!parsed?.tunnels || typeof parsed.tunnels !== "object") {
4684
5183
  return;
@@ -4752,7 +5251,7 @@ function resolvePort(opts) {
4752
5251
  return opts.port ?? (Number.isFinite(parsedEnv) ? parsedEnv : 3e3);
4753
5252
  }
4754
5253
  function ensureDir(dir) {
4755
- fs6.mkdirSync(dir, { recursive: true });
5254
+ fs7.mkdirSync(dir, { recursive: true });
4756
5255
  }
4757
5256
  function safePathSegment(value) {
4758
5257
  const trimmed = value.trim();
@@ -4762,15 +5261,15 @@ function safePathSegment(value) {
4762
5261
  }
4763
5262
  function writeStateFile(state) {
4764
5263
  ensureDir(INSTAFY_DIR3);
4765
- fs6.writeFileSync(TUNNEL_STATE_FILE, JSON.stringify(state, null, 2), "utf8");
5264
+ fs7.writeFileSync(TUNNEL_STATE_FILE, JSON.stringify(state, null, 2), "utf8");
4766
5265
  try {
4767
- fs6.chmodSync(TUNNEL_STATE_FILE, 384);
5266
+ fs7.chmodSync(TUNNEL_STATE_FILE, 384);
4768
5267
  } catch {
4769
5268
  }
4770
5269
  }
4771
5270
  function readStateFile() {
4772
5271
  try {
4773
- const raw = fs6.readFileSync(TUNNEL_STATE_FILE, "utf8");
5272
+ const raw = fs7.readFileSync(TUNNEL_STATE_FILE, "utf8");
4774
5273
  const parsed = JSON.parse(raw);
4775
5274
  if (!parsed || typeof parsed !== "object")
4776
5275
  return { version: 1, tunnels: [] };
@@ -4934,8 +5433,8 @@ async function startTunnelSession(opts) {
4934
5433
  env: process.env,
4935
5434
  version: process.env.RATHOLE_VERSION ?? null,
4936
5435
  cacheDir: process.env.RATHOLE_CACHE_DIR ?? null,
4937
- logger: (message) => console.log(kleur6.cyan(`[rathole] ${message}`)),
4938
- warn: (message) => console.warn(kleur6.yellow(message))
5436
+ logger: (message) => console.log(kleur7.cyan(`[rathole] ${message}`)),
5437
+ warn: (message) => console.warn(kleur7.yellow(message))
4939
5438
  });
4940
5439
  if (!rathole) {
4941
5440
  throw new Error(
@@ -4947,9 +5446,9 @@ async function startTunnelSession(opts) {
4947
5446
  persistTunnelAliasResult(alias, grant.hostname, grant.url ?? `https://${grant.hostname}`);
4948
5447
  const creds = grant.credentials && typeof grant.credentials === "object" ? grant.credentials : {};
4949
5448
  const configBody = buildRatholeConfig(creds, port);
4950
- const workdir = fs6.mkdtempSync(path7.join(os4.tmpdir(), "instafy-cli-tunnel-"));
4951
- const configPath2 = path7.join(workdir, "rathole.toml");
4952
- fs6.writeFileSync(configPath2, configBody, { encoding: "utf8", mode: 384 });
5449
+ const workdir = fs7.mkdtempSync(path8.join(os4.tmpdir(), "instafy-cli-tunnel-"));
5450
+ const configPath2 = path8.join(workdir, "rathole.toml");
5451
+ fs7.writeFileSync(configPath2, configBody, { encoding: "utf8", mode: 384 });
4953
5452
  const child = spawn3(rathole, ["-c", configPath2], {
4954
5453
  stdio: "inherit",
4955
5454
  cwd: workdir,
@@ -4961,11 +5460,11 @@ async function startTunnelSession(opts) {
4961
5460
  cleanedUp = true;
4962
5461
  child.kill("SIGTERM");
4963
5462
  await revokeTunnel(controllerUrl, controllerToken, projectId, grant.tunnelId);
4964
- fs6.rmSync(workdir, { recursive: true, force: true });
5463
+ fs7.rmSync(workdir, { recursive: true, force: true });
4965
5464
  };
4966
5465
  child.on("exit", () => {
4967
5466
  void cleanup().catch((error) => {
4968
- console.warn(kleur6.yellow(`Tunnel cleanup failed: ${String(error)}`));
5467
+ console.warn(kleur7.yellow(`Tunnel cleanup failed: ${String(error)}`));
4969
5468
  });
4970
5469
  });
4971
5470
  return {
@@ -4978,7 +5477,7 @@ async function startTunnelSession(opts) {
4978
5477
  async function runTunnelCommand(opts, options) {
4979
5478
  const session = await startTunnelSession(opts);
4980
5479
  console.log(
4981
- kleur6.green(`Tunnel ready: ${session.url} (tunnelId=${session.tunnelId})`)
5480
+ kleur7.green(`Tunnel ready: ${session.url} (tunnelId=${session.tunnelId})`)
4982
5481
  );
4983
5482
  const timeoutMs = options?.timeoutMs ?? null;
4984
5483
  await new Promise((resolve) => {
@@ -5012,8 +5511,8 @@ async function startTunnelDetached(opts) {
5012
5511
  env: process.env,
5013
5512
  version: process.env.RATHOLE_VERSION ?? null,
5014
5513
  cacheDir: process.env.RATHOLE_CACHE_DIR ?? null,
5015
- logger: (message) => console.log(kleur6.cyan(`[rathole] ${message}`)),
5016
- warn: (message) => console.warn(kleur6.yellow(message))
5514
+ logger: (message) => console.log(kleur7.cyan(`[rathole] ${message}`)),
5515
+ warn: (message) => console.warn(kleur7.yellow(message))
5017
5516
  });
5018
5517
  if (!rathole) {
5019
5518
  throw new Error(
@@ -5026,14 +5525,14 @@ async function startTunnelDetached(opts) {
5026
5525
  ensureDir(TUNNEL_WORKDIR_DIR);
5027
5526
  ensureDir(TUNNEL_LOG_DIR);
5028
5527
  const tunnelIdSafe = safePathSegment(grant.tunnelId);
5029
- const workdir = path7.join(TUNNEL_WORKDIR_DIR, tunnelIdSafe);
5528
+ const workdir = path8.join(TUNNEL_WORKDIR_DIR, tunnelIdSafe);
5030
5529
  ensureDir(workdir);
5031
5530
  const creds = grant.credentials && typeof grant.credentials === "object" ? grant.credentials : {};
5032
5531
  const configBody = buildRatholeConfig(creds, port);
5033
- const configPath2 = path7.join(workdir, "rathole.toml");
5034
- fs6.writeFileSync(configPath2, configBody, { encoding: "utf8", mode: 384 });
5035
- const logFile = opts.logFile?.trim() ? path7.resolve(opts.logFile.trim()) : path7.join(TUNNEL_LOG_DIR, `tunnel-${tunnelIdSafe}.log`);
5036
- const logFd = fs6.openSync(logFile, "a");
5532
+ const configPath2 = path8.join(workdir, "rathole.toml");
5533
+ fs7.writeFileSync(configPath2, configBody, { encoding: "utf8", mode: 384 });
5534
+ const logFile = opts.logFile?.trim() ? path8.resolve(opts.logFile.trim()) : path8.join(TUNNEL_LOG_DIR, `tunnel-${tunnelIdSafe}.log`);
5535
+ const logFd = fs7.openSync(logFile, "a");
5037
5536
  const child = spawn3(rathole, ["-c", configPath2], {
5038
5537
  stdio: ["ignore", logFd, logFd],
5039
5538
  cwd: workdir,
@@ -5050,7 +5549,7 @@ async function startTunnelDetached(opts) {
5050
5549
  if (exitedEarly || !isProcessAlive2(child.pid ?? -1)) {
5051
5550
  const logTail = (() => {
5052
5551
  try {
5053
- const text = fs6.readFileSync(logFile, "utf8");
5552
+ const text = fs7.readFileSync(logFile, "utf8");
5054
5553
  const lines = text.split(/\r?\n/).filter(Boolean);
5055
5554
  return lines.slice(-20).join("\n");
5056
5555
  } catch {
@@ -5133,7 +5632,7 @@ async function stopTunnelSession(opts) {
5133
5632
  throw error;
5134
5633
  }
5135
5634
  try {
5136
- fs6.rmSync(entry.workdir, { recursive: true, force: true });
5635
+ fs7.rmSync(entry.workdir, { recursive: true, force: true });
5137
5636
  } catch {
5138
5637
  }
5139
5638
  return { ok: true, tunnelId: entry.tunnelId };
@@ -5165,7 +5664,7 @@ async function tailTunnelLogs(options) {
5165
5664
  return;
5166
5665
  }
5167
5666
  if (!follow) {
5168
- const raw = fs6.existsSync(logFile) ? fs6.readFileSync(logFile, "utf8") : "";
5667
+ const raw = fs7.existsSync(logFile) ? fs7.readFileSync(logFile, "utf8") : "";
5169
5668
  const rows = raw.split(/\r?\n/);
5170
5669
  const tail = rows.slice(Math.max(0, rows.length - lineCount));
5171
5670
  console.log(tail.join("\n"));
@@ -5194,7 +5693,7 @@ async function tailTunnelLogs(options) {
5194
5693
  // src/config-command.ts
5195
5694
  init_config();
5196
5695
  init_config();
5197
- import kleur7 from "kleur";
5696
+ import kleur8 from "kleur";
5198
5697
  function normalizeKey(raw) {
5199
5698
  const key = raw.trim().toLowerCase().replace(/_/g, "-");
5200
5699
  if (key === "controller-url" || key === "controllerurl" || key === "server-url") {
@@ -5238,12 +5737,12 @@ function clearConfig(key) {
5238
5737
  }
5239
5738
  }
5240
5739
  function configPath(options) {
5241
- const path14 = getInstafyConfigPath();
5740
+ const path15 = getInstafyConfigPath();
5242
5741
  if (options?.json) {
5243
- console.log(JSON.stringify({ path: path14 }, null, 2));
5742
+ console.log(JSON.stringify({ path: path15 }, null, 2));
5244
5743
  return;
5245
5744
  }
5246
- console.log(path14);
5745
+ console.log(path15);
5247
5746
  }
5248
5747
  function configList(options) {
5249
5748
  const config = readInstafyCliConfig();
@@ -5260,13 +5759,13 @@ function configList(options) {
5260
5759
  console.log(JSON.stringify(payload, null, 2));
5261
5760
  return;
5262
5761
  }
5263
- console.log(kleur7.green("Instafy CLI config"));
5762
+ console.log(kleur8.green("Instafy CLI config"));
5264
5763
  console.log(`Path: ${payload.path}`);
5265
- console.log(`controller-url: ${payload.controllerUrl ?? kleur7.yellow("(not set)")}`);
5764
+ console.log(`controller-url: ${payload.controllerUrl ?? kleur8.yellow("(not set)")}`);
5266
5765
  console.log(`controller-url (effective): ${payload.effectiveControllerUrl}`);
5267
- console.log(`studio-url: ${payload.studioUrl ?? kleur7.yellow("(not set)")}`);
5268
- console.log(`access-token: ${payload.accessTokenSet ? kleur7.green("(set)") : kleur7.yellow("(not set)")}`);
5269
- console.log(`refresh-token: ${payload.refreshTokenSet ? kleur7.green("(set)") : kleur7.yellow("(not set)")}`);
5766
+ console.log(`studio-url: ${payload.studioUrl ?? kleur8.yellow("(not set)")}`);
5767
+ console.log(`access-token: ${payload.accessTokenSet ? kleur8.green("(set)") : kleur8.yellow("(not set)")}`);
5768
+ console.log(`refresh-token: ${payload.refreshTokenSet ? kleur8.green("(set)") : kleur8.yellow("(not set)")}`);
5270
5769
  if (payload.updatedAt) {
5271
5770
  console.log(`updated-at: ${payload.updatedAt}`);
5272
5771
  }
@@ -5295,7 +5794,7 @@ function configSet(params) {
5295
5794
  console.log(JSON.stringify({ key, value, path: getInstafyConfigPath() }, null, 2));
5296
5795
  return;
5297
5796
  }
5298
- console.log(kleur7.green(`Set ${key}`));
5797
+ console.log(kleur8.green(`Set ${key}`));
5299
5798
  console.log(value);
5300
5799
  }
5301
5800
  function configUnset(params) {
@@ -5305,7 +5804,7 @@ function configUnset(params) {
5305
5804
  console.log(JSON.stringify({ ok: true, key }, null, 2));
5306
5805
  return;
5307
5806
  }
5308
- console.log(kleur7.green(`Unset ${key}`));
5807
+ console.log(kleur8.green(`Unset ${key}`));
5309
5808
  }
5310
5809
 
5311
5810
  // src/index.ts
@@ -5314,28 +5813,28 @@ init_config();
5314
5813
  // src/git-wrapper.ts
5315
5814
  import { spawnSync as spawnSync4 } from "node:child_process";
5316
5815
  import { randomUUID as randomUUID2 } from "node:crypto";
5317
- import fs7 from "node:fs";
5318
- import path8 from "node:path";
5319
- import kleur8 from "kleur";
5816
+ import fs8 from "node:fs";
5817
+ import path9 from "node:path";
5818
+ import kleur9 from "kleur";
5320
5819
  function pathExists(candidate) {
5321
5820
  try {
5322
- fs7.statSync(candidate);
5821
+ fs8.statSync(candidate);
5323
5822
  return true;
5324
5823
  } catch {
5325
5824
  return false;
5326
5825
  }
5327
5826
  }
5328
5827
  function findInstafyGitContext(startDir) {
5329
- let current = path8.resolve(startDir);
5330
- const root = path8.parse(current).root;
5828
+ let current = path9.resolve(startDir);
5829
+ const root = path9.parse(current).root;
5331
5830
  while (true) {
5332
- const gitDir = path8.join(current, ".instafy", ".git");
5831
+ const gitDir = path9.join(current, ".instafy", ".git");
5333
5832
  if (pathExists(gitDir)) {
5334
5833
  return { workTree: current, gitDir };
5335
5834
  }
5336
5835
  if (current === root)
5337
5836
  break;
5338
- current = path8.dirname(current);
5837
+ current = path9.dirname(current);
5339
5838
  }
5340
5839
  return null;
5341
5840
  }
@@ -5382,9 +5881,9 @@ function validateGitArgs(userArgs) {
5382
5881
  }
5383
5882
  if (arg === "--git-dir" || arg.startsWith("--git-dir=") || arg === "--work-tree" || arg.startsWith("--work-tree=") || arg === "-C") {
5384
5883
  throw new Error(
5385
- `Unsupported argument "${arg}". Use ${kleur8.cyan(
5884
+ `Unsupported argument "${arg}". Use ${kleur9.cyan(
5386
5885
  "cd"
5387
- )} instead; ${kleur8.cyan("instafy git")} manages --git-dir/--work-tree automatically.`
5886
+ )} instead; ${kleur9.cyan("instafy git")} manages --git-dir/--work-tree automatically.`
5388
5887
  );
5389
5888
  }
5390
5889
  if (arg === "-c" || arg === "--config-env" || arg === "--exec-path" || arg === "--namespace") {
@@ -5496,7 +5995,7 @@ function collectDirtyPaths(context, options) {
5496
5995
  return unique;
5497
5996
  }
5498
5997
  function findEmbeddedGitDirs(context, pathSpecs) {
5499
- const instafyGitDir = path8.resolve(context.gitDir);
5998
+ const instafyGitDir = path9.resolve(context.gitDir);
5500
5999
  const seen = /* @__PURE__ */ new Set();
5501
6000
  for (const rawPath of pathSpecs) {
5502
6001
  const normalized = normalizePathSpec(rawPath).replace(/\/+$/, "");
@@ -5505,11 +6004,11 @@ function findEmbeddedGitDirs(context, pathSpecs) {
5505
6004
  }
5506
6005
  let cursor = normalized;
5507
6006
  while (cursor && cursor !== ".") {
5508
- const candidate = path8.join(context.workTree, ...cursor.split("/"), ".git");
5509
- if (pathExists(candidate) && path8.resolve(candidate) !== instafyGitDir) {
5510
- seen.add(path8.resolve(candidate));
6007
+ const candidate = path9.join(context.workTree, ...cursor.split("/"), ".git");
6008
+ if (pathExists(candidate) && path9.resolve(candidate) !== instafyGitDir) {
6009
+ seen.add(path9.resolve(candidate));
5511
6010
  }
5512
- const next = path8.posix.dirname(cursor);
6011
+ const next = path9.posix.dirname(cursor);
5513
6012
  if (next === "." || next === cursor) {
5514
6013
  break;
5515
6014
  }
@@ -5518,8 +6017,8 @@ function findEmbeddedGitDirs(context, pathSpecs) {
5518
6017
  }
5519
6018
  const candidates = Array.from(seen);
5520
6019
  candidates.sort((a, b) => {
5521
- const depthA = a.split(path8.sep).length;
5522
- const depthB = b.split(path8.sep).length;
6020
+ const depthA = a.split(path9.sep).length;
6021
+ const depthB = b.split(path9.sep).length;
5523
6022
  return depthB - depthA;
5524
6023
  });
5525
6024
  return candidates;
@@ -5529,17 +6028,17 @@ function hideEmbeddedGitDirs(context, pathSpecs) {
5529
6028
  if (candidates.length === 0) {
5530
6029
  return [];
5531
6030
  }
5532
- const stagingBase = path8.join(context.workTree, ".instafy", "origin-staging", "embedded-git");
5533
- fs7.mkdirSync(stagingBase, { recursive: true });
6031
+ const stagingBase = path9.join(context.workTree, ".instafy", "origin-staging", "embedded-git");
6032
+ fs8.mkdirSync(stagingBase, { recursive: true });
5534
6033
  const renames = [];
5535
6034
  for (const candidate of candidates) {
5536
- let hidden = path8.join(stagingBase, randomUUID2());
6035
+ let hidden = path9.join(stagingBase, randomUUID2());
5537
6036
  for (let attempt = 0; attempt < 8; attempt += 1) {
5538
6037
  if (!pathExists(hidden))
5539
6038
  break;
5540
- hidden = path8.join(stagingBase, randomUUID2());
6039
+ hidden = path9.join(stagingBase, randomUUID2());
5541
6040
  }
5542
- fs7.renameSync(candidate, hidden);
6041
+ fs8.renameSync(candidate, hidden);
5543
6042
  renames.push({ original: candidate, hidden });
5544
6043
  }
5545
6044
  return renames;
@@ -5550,7 +6049,7 @@ function restoreEmbeddedGitDirs(renames) {
5550
6049
  if (!rename)
5551
6050
  continue;
5552
6051
  try {
5553
- fs7.renameSync(rename.hidden, rename.original);
6052
+ fs8.renameSync(rename.hidden, rename.original);
5554
6053
  } catch {
5555
6054
  }
5556
6055
  }
@@ -5620,15 +6119,15 @@ function runInstafyGit(userArgs, options) {
5620
6119
  console.log("instafy git <git-args...>");
5621
6120
  console.log("");
5622
6121
  console.log(
5623
- `Runs ${kleur8.cyan("git")} against the Instafy canonical repo at ${kleur8.cyan(
6122
+ `Runs ${kleur9.cyan("git")} against the Instafy canonical repo at ${kleur9.cyan(
5624
6123
  ".instafy/.git"
5625
6124
  )} (auto-detected by walking up from cwd).`
5626
6125
  );
5627
6126
  console.log("");
5628
6127
  console.log("Example:");
5629
- console.log(` ${kleur8.cyan("instafy git status")}`);
5630
- console.log(` ${kleur8.cyan("instafy git add -A")}`);
5631
- console.log(` ${kleur8.cyan('instafy git commit -am "instafy: checkpoint"')}`);
6128
+ console.log(` ${kleur9.cyan("instafy git status")}`);
6129
+ console.log(` ${kleur9.cyan("instafy git add -A")}`);
6130
+ console.log(` ${kleur9.cyan('instafy git commit -am "instafy: checkpoint"')}`);
5632
6131
  return 0;
5633
6132
  }
5634
6133
  const context = findInstafyGitContext(cwd);
@@ -5638,8 +6137,8 @@ function runInstafyGit(userArgs, options) {
5638
6137
  "No Instafy canonical git checkout found (expected .instafy/.git).",
5639
6138
  "",
5640
6139
  "Tips:",
5641
- `- Run this inside a git-canonical workspace (where ${kleur8.cyan(".instafy/.git")} exists).`,
5642
- `- If you're trying to operate on a user repo, use normal ${kleur8.cyan("git")} (it uses .git).`
6140
+ `- Run this inside a git-canonical workspace (where ${kleur9.cyan(".instafy/.git")} exists).`,
6141
+ `- If you're trying to operate on a user repo, use normal ${kleur9.cyan("git")} (it uses .git).`
5643
6142
  ].join("\n")
5644
6143
  );
5645
6144
  }
@@ -5904,7 +6403,7 @@ async function runInstafyGitSync(args, options) {
5904
6403
  }
5905
6404
  } catch (error) {
5906
6405
  const message = error instanceof Error ? error.message : String(error);
5907
- console.warn(kleur8.yellow(`[instafy git sync] ${message}; falling back to environment token.`));
6406
+ console.warn(kleur9.yellow(`[instafy git sync] ${message}; falling back to environment token.`));
5908
6407
  }
5909
6408
  }
5910
6409
  if (!originToken) {
@@ -5952,7 +6451,7 @@ async function runInstafyGitSync(args, options) {
5952
6451
  } catch (error) {
5953
6452
  const message = error instanceof Error ? error.message : String(error);
5954
6453
  console.warn(
5955
- kleur8.yellow(
6454
+ kleur9.yellow(
5956
6455
  `[instafy git sync] failed to recover from invalid origin token: ${message}`
5957
6456
  )
5958
6457
  );
@@ -5960,13 +6459,13 @@ async function runInstafyGitSync(args, options) {
5960
6459
  }
5961
6460
  if (!response.ok) {
5962
6461
  const suffix = text.trim() ? `: ${text.trim()}` : "";
5963
- console.error(kleur8.red(`Origin git sync failed (${response.status} ${response.statusText})${suffix}`));
6462
+ console.error(kleur9.red(`Origin git sync failed (${response.status} ${response.statusText})${suffix}`));
5964
6463
  return 1;
5965
6464
  }
5966
6465
  const payload = text.trim() ? JSON.parse(text) : null;
5967
6466
  const rev = typeof payload?.rev === "string" ? payload.rev.trim() : "";
5968
6467
  if (!rev) {
5969
- console.error(kleur8.red("Origin git sync response missing rev field."));
6468
+ console.error(kleur9.red("Origin git sync response missing rev field."));
5970
6469
  return 1;
5971
6470
  }
5972
6471
  if (parsed.json) {
@@ -5981,7 +6480,7 @@ async function runInstafyGitSync(args, options) {
5981
6480
  init_config();
5982
6481
  init_errors();
5983
6482
  init_controller_fetch();
5984
- import fs8 from "node:fs";
6483
+ import fs9 from "node:fs";
5985
6484
  function normalizeUrl5(raw) {
5986
6485
  const value = (raw ?? "").trim();
5987
6486
  if (!value)
@@ -6060,7 +6559,7 @@ function parseHeader(raw) {
6060
6559
  }
6061
6560
  function parseJsonBody(options) {
6062
6561
  if (options.jsonFile) {
6063
- const raw = fs8.readFileSync(options.jsonFile, "utf8");
6562
+ const raw = fs9.readFileSync(options.jsonFile, "utf8");
6064
6563
  const parsed = JSON.parse(raw);
6065
6564
  return JSON.stringify(parsed);
6066
6565
  }
@@ -6298,9 +6797,9 @@ async function historyConversations(options) {
6298
6797
 
6299
6798
  // src/hardware-bindings.ts
6300
6799
  init_project_manifest();
6301
- import fs9 from "node:fs";
6302
- import path9 from "node:path";
6303
- import kleur9 from "kleur";
6800
+ import fs10 from "node:fs";
6801
+ import path10 from "node:path";
6802
+ import kleur10 from "kleur";
6304
6803
  var SERIAL_HARDWARE_PROVIDER_ID = "hardware.serial";
6305
6804
  var DEFAULT_SERIAL_CAPABILITIES = [
6306
6805
  "hardware_serial_list",
@@ -6310,7 +6809,7 @@ var SUPPORTED_HARDWARE_CAPABILITIES = new Set(
6310
6809
  DEFAULT_SERIAL_CAPABILITIES
6311
6810
  );
6312
6811
  function resolveHardwareBindingContext(startDir) {
6313
- const lookup = findProjectManifest(path9.resolve(startDir));
6812
+ const lookup = findProjectManifest(path10.resolve(startDir));
6314
6813
  if (!lookup.manifest || !lookup.path) {
6315
6814
  throw new Error(
6316
6815
  "Linked space manifest required. Run `instafy space init` in this folder or pass --path <dir>."
@@ -6320,17 +6819,17 @@ function resolveHardwareBindingContext(startDir) {
6320
6819
  if (!projectId) {
6321
6820
  throw new Error("Linked space manifest is missing a spaceId.");
6322
6821
  }
6323
- const instafyDir = path9.dirname(lookup.path);
6822
+ const instafyDir = path10.dirname(lookup.path);
6324
6823
  return {
6325
- bindingsPath: path9.join(instafyDir, "hardware-bindings.json"),
6824
+ bindingsPath: path10.join(instafyDir, "hardware-bindings.json"),
6326
6825
  projectId
6327
6826
  };
6328
6827
  }
6329
6828
  function readHardwareBindingStore(bindingsPath) {
6330
- if (!fs9.existsSync(bindingsPath)) {
6829
+ if (!fs10.existsSync(bindingsPath)) {
6331
6830
  return { version: 1, bindings: {} };
6332
6831
  }
6333
- const raw = JSON.parse(fs9.readFileSync(bindingsPath, "utf8"));
6832
+ const raw = JSON.parse(fs10.readFileSync(bindingsPath, "utf8"));
6334
6833
  const bindings = raw && typeof raw.bindings === "object" && raw.bindings !== null ? raw.bindings : {};
6335
6834
  return {
6336
6835
  version: 1,
@@ -6338,8 +6837,8 @@ function readHardwareBindingStore(bindingsPath) {
6338
6837
  };
6339
6838
  }
6340
6839
  function writeHardwareBindingStore(bindingsPath, store) {
6341
- fs9.mkdirSync(path9.dirname(bindingsPath), { recursive: true });
6342
- fs9.writeFileSync(bindingsPath, JSON.stringify(store, null, 2), "utf8");
6840
+ fs10.mkdirSync(path10.dirname(bindingsPath), { recursive: true });
6841
+ fs10.writeFileSync(bindingsPath, JSON.stringify(store, null, 2), "utf8");
6343
6842
  }
6344
6843
  function normalizeCapabilities(providerId, values) {
6345
6844
  const requested = Array.isArray(values) && values.length > 0 ? values : providerId === SERIAL_HARDWARE_PROVIDER_ID ? DEFAULT_SERIAL_CAPABILITIES : [];
@@ -6367,18 +6866,18 @@ function normalizeDevices(values) {
6367
6866
  if (!trimmed) {
6368
6867
  continue;
6369
6868
  }
6370
- const resolved = path9.resolve(trimmed);
6869
+ const resolved = path10.resolve(trimmed);
6371
6870
  unique.set(resolved, {
6372
6871
  kind: "serial_device",
6373
6872
  id: resolved,
6374
6873
  path: resolved,
6375
- displayName: path9.basename(resolved)
6874
+ displayName: path10.basename(resolved)
6376
6875
  });
6377
6876
  }
6378
6877
  return [...unique.values()];
6379
6878
  }
6380
6879
  function printHardwareBinding(record) {
6381
- console.log(kleur9.cyan(`Hardware provider: ${record.providerId}`));
6880
+ console.log(kleur10.cyan(`Hardware provider: ${record.providerId}`));
6382
6881
  console.log(`Status: ${record.status}`);
6383
6882
  console.log(`Project: ${record.projectId}`);
6384
6883
  console.log(`Capabilities: ${record.grantedCapabilities.join(", ") || "(none)"}`);
@@ -6424,10 +6923,10 @@ function showHardwareBinding(options) {
6424
6923
  return;
6425
6924
  }
6426
6925
  if (bindings.length === 0) {
6427
- console.log(kleur9.yellow("No hardware bindings recorded for this space."));
6926
+ console.log(kleur10.yellow("No hardware bindings recorded for this space."));
6428
6927
  return;
6429
6928
  }
6430
- console.log(kleur9.cyan(`Hardware bindings for ${context.projectId}:`));
6929
+ console.log(kleur10.cyan(`Hardware bindings for ${context.projectId}:`));
6431
6930
  for (const record of bindings) {
6432
6931
  console.log(`- ${record.providerId} (${record.status})`);
6433
6932
  }
@@ -6460,9 +6959,9 @@ function grantHardwareBinding(options) {
6460
6959
  console.log(JSON.stringify(record));
6461
6960
  return;
6462
6961
  }
6463
- console.log(kleur9.green(`Hardware binding granted: ${providerId}`));
6962
+ console.log(kleur10.green(`Hardware binding granted: ${providerId}`));
6464
6963
  printHardwareBinding(record);
6465
- console.log(kleur9.cyan(`Stored at ${context.bindingsPath}`));
6964
+ console.log(kleur10.cyan(`Stored at ${context.bindingsPath}`));
6466
6965
  }
6467
6966
  function revokeHardwareBinding(options) {
6468
6967
  const providerId = options.providerId.trim();
@@ -6481,12 +6980,12 @@ function revokeHardwareBinding(options) {
6481
6980
  console.log(JSON.stringify({ revoked: true, providerId }));
6482
6981
  return;
6483
6982
  }
6484
- console.log(kleur9.green(`Hardware binding revoked: ${providerId}`));
6485
- console.log(kleur9.cyan(`Updated ${context.bindingsPath}`));
6983
+ console.log(kleur10.green(`Hardware binding revoked: ${providerId}`));
6984
+ console.log(kleur10.cyan(`Updated ${context.bindingsPath}`));
6486
6985
  }
6487
6986
 
6488
6987
  // src/agents.ts
6489
- import kleur10 from "kleur";
6988
+ import kleur11 from "kleur";
6490
6989
  init_project_manifest();
6491
6990
  var DEFAULT_CONTEXT_LIMIT = 50;
6492
6991
  var MAX_CONTEXT_LIMIT = 200;
@@ -6597,16 +7096,16 @@ async function agentsList(options) {
6597
7096
  return;
6598
7097
  }
6599
7098
  if (!agents.length) {
6600
- console.log(kleur10.yellow("No agents found."));
7099
+ console.log(kleur11.yellow("No agents found."));
6601
7100
  return;
6602
7101
  }
6603
- console.log(kleur10.bold("Agents"));
7102
+ console.log(kleur11.bold("Agents"));
6604
7103
  for (const agent of agents) {
6605
7104
  const provider = trimOrNull(agent.provider);
6606
7105
  const model = trimOrNull(agent.model);
6607
7106
  const details = [provider, model].filter(Boolean).join(" \xB7 ");
6608
7107
  console.log(
6609
- `- ${kleur10.cyan(`@${agent.handle}`)} ${displayName(agent)}${details ? ` (${details})` : ""}`
7108
+ `- ${kleur11.cyan(`@${agent.handle}`)} ${displayName(agent)}${details ? ` (${details})` : ""}`
6610
7109
  );
6611
7110
  console.log(` id: ${agent.id}`);
6612
7111
  }
@@ -6626,15 +7125,15 @@ async function agentContextsList(options) {
6626
7125
  return;
6627
7126
  }
6628
7127
  if (!contexts.length) {
6629
- console.log(kleur10.yellow("No agent context cards found."));
7128
+ console.log(kleur11.yellow("No agent context cards found."));
6630
7129
  return;
6631
7130
  }
6632
- console.log(kleur10.bold("Agent context"));
7131
+ console.log(kleur11.bold("Agent context"));
6633
7132
  for (const card of contexts) {
6634
7133
  const title = trimOrNull(card.title);
6635
7134
  const updatedAt = trimOrNull(card.updatedAt) ?? trimOrNull(card.updated_at);
6636
7135
  console.log(
6637
- `- ${kleur10.cyan(contextAgentLabel(card))} ${kleur10.gray(`[${contextScope(card)}]`)}${title ? ` ${title}` : ""}`
7136
+ `- ${kleur11.cyan(contextAgentLabel(card))} ${kleur11.gray(`[${contextScope(card)}]`)}${title ? ` ${title}` : ""}`
6638
7137
  );
6639
7138
  console.log(` ${summarize(card.context)}`);
6640
7139
  if (updatedAt) {
@@ -6671,7 +7170,7 @@ async function agentContextPut(options) {
6671
7170
  return;
6672
7171
  }
6673
7172
  console.log(
6674
- `${kleur10.green("Saved")} ${kleur10.cyan(contextAgentLabel(saved))} context for ${kleur10.gray(contextScope(saved))}.`
7173
+ `${kleur11.green("Saved")} ${kleur11.cyan(contextAgentLabel(saved))} context for ${kleur11.gray(contextScope(saved))}.`
6675
7174
  );
6676
7175
  }
6677
7176
  function workerLabel(worker) {
@@ -6697,7 +7196,7 @@ async function agentPlanGroupStatus(options) {
6697
7196
  console.log(JSON.stringify(status, null, 2));
6698
7197
  return;
6699
7198
  }
6700
- console.log(kleur10.bold(`Plan group ${trimOrNull(status.groupId) ?? groupId}`));
7199
+ console.log(kleur11.bold(`Plan group ${trimOrNull(status.groupId) ?? groupId}`));
6701
7200
  const conversationId = trimOrNull(status.conversationId);
6702
7201
  if (conversationId) {
6703
7202
  console.log(` conversation: ${conversationId}`);
@@ -6707,13 +7206,13 @@ async function agentPlanGroupStatus(options) {
6707
7206
  );
6708
7207
  const workers = status.workers ?? [];
6709
7208
  if (!workers.length) {
6710
- console.log(kleur10.yellow("No worker jobs found."));
7209
+ console.log(kleur11.yellow("No worker jobs found."));
6711
7210
  return;
6712
7211
  }
6713
7212
  for (const worker of workers) {
6714
7213
  const marker = worker.terminal ? "terminal" : "running";
6715
7214
  const details = [trimOrNull(worker.status) ?? "unknown", marker, trimOrNull(worker.outcome)].filter(Boolean).join(" \xB7 ");
6716
- console.log(`- ${kleur10.cyan(workerLabel(worker))} ${kleur10.gray(`[${details}]`)}`);
7215
+ console.log(`- ${kleur11.cyan(workerLabel(worker))} ${kleur11.gray(`[${details}]`)}`);
6717
7216
  const scope = trimOrNull(worker.scopeSummary);
6718
7217
  if (scope) {
6719
7218
  console.log(` scope: ${scope}`);
@@ -6724,7 +7223,7 @@ async function agentPlanGroupStatus(options) {
6724
7223
  }
6725
7224
  const errorMessage = trimOrNull(worker.errorMessage);
6726
7225
  if (errorMessage) {
6727
- console.log(` ${kleur10.red(`error: ${summarize(errorMessage)}`)}`);
7226
+ console.log(` ${kleur11.red(`error: ${summarize(errorMessage)}`)}`);
6728
7227
  }
6729
7228
  }
6730
7229
  }
@@ -6751,11 +7250,11 @@ async function agentJobsCancel(options) {
6751
7250
  const canceledJobs = result.canceledJobIds?.length ?? 0;
6752
7251
  const canceledRuns = result.canceledRunIds?.length ?? 0;
6753
7252
  if (!canceledJobs && !canceledRuns) {
6754
- console.log(kleur10.yellow(`Nothing to cancel for ${target} (no queued or leased jobs).`));
7253
+ console.log(kleur11.yellow(`Nothing to cancel for ${target} (no queued or leased jobs).`));
6755
7254
  return;
6756
7255
  }
6757
7256
  console.log(
6758
- `${kleur10.green("Canceled")} ${canceledJobs} job(s) and ${canceledRuns} run(s) for ${target}.`
7257
+ `${kleur11.green("Canceled")} ${canceledJobs} job(s) and ${canceledRuns} run(s) for ${target}.`
6759
7258
  );
6760
7259
  }
6761
7260
 
@@ -6800,17 +7299,17 @@ function extractMentionedAgentHandles(prompt) {
6800
7299
  }
6801
7300
  return handles;
6802
7301
  }
6803
- function buildPromptMetadata(prompt) {
7302
+ function buildPromptMetadata(prompt, intent) {
6804
7303
  const mentionedHandles = extractMentionedAgentHandles(prompt);
7304
+ const metadata = intent.toLowerCase() === "terminal_command" ? {} : { writeIntent: true };
6805
7305
  if (mentionedHandles.length === 0) {
6806
- return {};
7306
+ return metadata;
6807
7307
  }
6808
- return {
6809
- agentSelection: {
6810
- active: mentionedHandles,
6811
- mentions: mentionedHandles
6812
- }
7308
+ metadata.agentSelection = {
7309
+ active: mentionedHandles,
7310
+ mentions: mentionedHandles
6813
7311
  };
7312
+ return metadata;
6814
7313
  }
6815
7314
  function normalizeBooleanEnv(value) {
6816
7315
  const normalized = value?.trim().toLowerCase();
@@ -6920,10 +7419,11 @@ async function sleep(ms) {
6920
7419
  }
6921
7420
  async function dispatchPrompt(options) {
6922
7421
  const prompt = normalizePrompt(options.prompt);
7422
+ const intent = options.intent?.trim() || "feature";
6923
7423
  const payload = {
6924
7424
  promptText: prompt,
6925
- intent: options.intent?.trim() || "feature",
6926
- metadata: buildPromptMetadata(prompt)
7425
+ intent,
7426
+ metadata: buildPromptMetadata(prompt, intent)
6927
7427
  };
6928
7428
  if (options.conversation?.trim()) {
6929
7429
  const conversationId2 = options.conversation.trim();
@@ -7071,7 +7571,7 @@ async function chatPrompt(options) {
7071
7571
 
7072
7572
  // src/conversations.ts
7073
7573
  init_project_manifest();
7074
- import kleur11 from "kleur";
7574
+ import kleur12 from "kleur";
7075
7575
  var DEFAULT_CONVERSATION_LIMIT = 50;
7076
7576
  var DEFAULT_MESSAGE_LIMIT = 80;
7077
7577
  var MAX_LIMIT = 200;
@@ -7298,7 +7798,7 @@ async function buildSearchMatches(conversations, options) {
7298
7798
  }));
7299
7799
  }
7300
7800
  function formatConversationMatch(match) {
7301
- const lines = [`- ${kleur11.green(match.title)} (${match.id})`];
7801
+ const lines = [`- ${kleur12.green(match.title)} (${match.id})`];
7302
7802
  const details = [];
7303
7803
  if (match.matchedIn.length > 0) {
7304
7804
  details.push(`match: ${match.matchedIn.join(", ")}`);
@@ -7310,7 +7810,7 @@ function formatConversationMatch(match) {
7310
7810
  lines.push(` ${details.join(" \xB7 ")}`);
7311
7811
  }
7312
7812
  if (match.preview) {
7313
- lines.push(` ${kleur11.gray(summarizeContent(match.preview))}`);
7813
+ lines.push(` ${kleur12.gray(summarizeContent(match.preview))}`);
7314
7814
  }
7315
7815
  return lines.join("\n");
7316
7816
  }
@@ -7369,16 +7869,16 @@ async function createConversation(options) {
7369
7869
  console.log(JSON.stringify(payload, null, 2));
7370
7870
  return;
7371
7871
  }
7372
- console.log(kleur11.green("Created conversation"));
7373
- console.log(`${kleur11.gray("ID:")} ${conversationId}`);
7872
+ console.log(kleur12.green("Created conversation"));
7873
+ console.log(`${kleur12.gray("ID:")} ${conversationId}`);
7374
7874
  if (title) {
7375
- console.log(`${kleur11.gray("Title:")} ${title}`);
7875
+ console.log(`${kleur12.gray("Title:")} ${title}`);
7376
7876
  }
7377
7877
  if (parentConversationId) {
7378
- console.log(`${kleur11.gray("Parent:")} ${parentConversationId}`);
7878
+ console.log(`${kleur12.gray("Parent:")} ${parentConversationId}`);
7379
7879
  }
7380
7880
  if (threadKind) {
7381
- console.log(`${kleur11.gray("Thread kind:")} ${threadKind}`);
7881
+ console.log(`${kleur12.gray("Thread kind:")} ${threadKind}`);
7382
7882
  }
7383
7883
  }
7384
7884
  async function listConversations(options) {
@@ -7405,10 +7905,10 @@ async function listConversations(options) {
7405
7905
  return;
7406
7906
  }
7407
7907
  if (items.length === 0) {
7408
- console.log(kleur11.yellow("No conversations found for this space."));
7908
+ console.log(kleur12.yellow("No conversations found for this space."));
7409
7909
  return;
7410
7910
  }
7411
- console.log(kleur11.green(`Conversations (${items.length})`));
7911
+ console.log(kleur12.green(`Conversations (${items.length})`));
7412
7912
  for (const item of items) {
7413
7913
  console.log(formatConversationMatch({ ...item, score: 0, matchedIn: [] }));
7414
7914
  }
@@ -7430,10 +7930,10 @@ async function searchConversations(options) {
7430
7930
  return matches;
7431
7931
  }
7432
7932
  if (matches.length === 0) {
7433
- console.log(kleur11.yellow(`No conversations matched "${options.query}".`));
7933
+ console.log(kleur12.yellow(`No conversations matched "${options.query}".`));
7434
7934
  return matches;
7435
7935
  }
7436
- console.log(kleur11.green(`Matches for "${options.query}"`));
7936
+ console.log(kleur12.green(`Matches for "${options.query}"`));
7437
7937
  for (const match of matches.slice(0, Math.min(limit, matches.length))) {
7438
7938
  console.log(formatConversationMatch(match));
7439
7939
  }
@@ -7517,35 +8017,35 @@ async function showConversation(options) {
7517
8017
  );
7518
8018
  return;
7519
8019
  }
7520
- console.log(kleur11.green(selected.title));
7521
- console.log(`${kleur11.gray("ID:")} ${selected.id}`);
8020
+ console.log(kleur12.green(selected.title));
8021
+ console.log(`${kleur12.gray("ID:")} ${selected.id}`);
7522
8022
  if (selected.updatedAt) {
7523
- console.log(`${kleur11.gray("Updated:")} ${selected.updatedAt}`);
8023
+ console.log(`${kleur12.gray("Updated:")} ${selected.updatedAt}`);
7524
8024
  }
7525
8025
  if (selected.preview) {
7526
- console.log(`${kleur11.gray("Preview:")} ${summarizeContent(selected.preview, 240)}`);
8026
+ console.log(`${kleur12.gray("Preview:")} ${summarizeContent(selected.preview, 240)}`);
7527
8027
  }
7528
8028
  console.log("");
7529
8029
  if (messages.length === 0) {
7530
- console.log(kleur11.yellow("No messages found in this conversation."));
8030
+ console.log(kleur12.yellow("No messages found in this conversation."));
7531
8031
  return;
7532
8032
  }
7533
8033
  for (const message of messages.slice().reverse()) {
7534
8034
  const role = (message.role ?? "unknown").trim().toLowerCase();
7535
8035
  const createdAt = trimOrNull2(message.createdAt) ?? trimOrNull2(message.created_at);
7536
8036
  const header = createdAt ? `${role} \xB7 ${createdAt}` : role;
7537
- console.log(kleur11.cyan(`[${header}]`));
8037
+ console.log(kleur12.cyan(`[${header}]`));
7538
8038
  console.log(summarizeContent(message.content ?? "", 4e3));
7539
8039
  console.log("");
7540
8040
  }
7541
8041
  }
7542
8042
 
7543
8043
  // src/invitations.ts
7544
- import kleur12 from "kleur";
7545
- import path10 from "node:path";
8044
+ import kleur13 from "kleur";
8045
+ import path11 from "node:path";
7546
8046
  init_project_manifest();
7547
8047
  function resolveStartDir(rawPath) {
7548
- return path10.resolve(rawPath ?? process.cwd());
8048
+ return path11.resolve(rawPath ?? process.cwd());
7549
8049
  }
7550
8050
  function trimOrNull3(value) {
7551
8051
  const trimmed = value?.trim() ?? "";
@@ -7687,7 +8187,7 @@ async function inviteSpaceMember(options) {
7687
8187
  return normalized;
7688
8188
  }
7689
8189
  const scopeLabel = orgName ? `${orgName}` : "this space";
7690
- console.log(kleur12.green(`Invitation sent to ${normalized.email}`));
8190
+ console.log(kleur13.green(`Invitation sent to ${normalized.email}`));
7691
8191
  console.log(`Scope: ${scopeLabel}`);
7692
8192
  console.log(`Role: ${normalized.role}`);
7693
8193
  if (normalized.expiresAt) {
@@ -7747,7 +8247,7 @@ async function setSpaceMemberRole(options) {
7747
8247
  return normalized;
7748
8248
  }
7749
8249
  console.log(
7750
- kleur12.green(
8250
+ kleur13.green(
7751
8251
  normalized.status === "updated" ? `Updated ${normalized.email} to ${normalized.role}` : `${normalized.email} already has role ${normalized.role}`
7752
8252
  )
7753
8253
  );
@@ -7786,7 +8286,7 @@ async function setSpaceMemberRole(options) {
7786
8286
  return normalized;
7787
8287
  }
7788
8288
  console.log(
7789
- kleur12.green(
8289
+ kleur13.green(
7790
8290
  normalized.status === "updated" ? `Updated pending invite for ${normalized.email} to ${normalized.role}` : `Pending invite for ${normalized.email} already has role ${normalized.role}`
7791
8291
  )
7792
8292
  );
@@ -7832,14 +8332,446 @@ async function sendSpaceInvitation(options) {
7832
8332
  };
7833
8333
  }
7834
8334
 
8335
+ // src/team.ts
8336
+ init_config();
8337
+ init_controller_fetch();
8338
+ init_errors();
8339
+ import kleur14 from "kleur";
8340
+ var DEFAULT_STUDIO_URL = "https://instafy.dev";
8341
+ var ORG_ROLES = ["owner", "admin", "builder", "viewer"];
8342
+ var INVITE_LINK_ROLES = ["builder", "viewer"];
8343
+ var UUID_PATTERN3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8344
+ var ControllerRequestError = class extends Error {
8345
+ constructor(status, serverMessage, message) {
8346
+ super(message);
8347
+ this.name = "ControllerRequestError";
8348
+ this.status = status;
8349
+ this.serverMessage = serverMessage;
8350
+ }
8351
+ };
8352
+ function normalizeBaseUrl(value) {
8353
+ const trimmed = typeof value === "string" ? value.trim() : "";
8354
+ if (!trimmed)
8355
+ return null;
8356
+ return trimmed.replace(/\/$/, "");
8357
+ }
8358
+ function isUuid3(value) {
8359
+ return UUID_PATTERN3.test(value.trim());
8360
+ }
8361
+ function requireUuid(value, label) {
8362
+ const trimmed = value?.trim() ?? "";
8363
+ if (!trimmed) {
8364
+ throw new Error(`${label} is required.`);
8365
+ }
8366
+ if (!isUuid3(trimmed)) {
8367
+ throw new Error(`${label} must be a UUID.`);
8368
+ }
8369
+ return trimmed;
8370
+ }
8371
+ function normalizeRole2(value, allowed, label) {
8372
+ const resolved = (value ?? "").trim().toLowerCase();
8373
+ if (!resolved) {
8374
+ throw new Error(`${label} is required.`);
8375
+ }
8376
+ if (!allowed.includes(resolved)) {
8377
+ throw new Error(`${label} must be one of ${allowed.join(", ")}.`);
8378
+ }
8379
+ return resolved;
8380
+ }
8381
+ function resolveControllerAuth4(options, retryCommand) {
8382
+ const cwd = options.cwd ?? process.cwd();
8383
+ const controllerUrl = resolveControllerUrl({
8384
+ controllerUrl: options.controllerUrl ?? null,
8385
+ cwd
8386
+ });
8387
+ const resolved = resolveUserAccessTokenWithSource({
8388
+ accessToken: options.accessToken ?? null,
8389
+ cwd
8390
+ });
8391
+ if (!resolved.token) {
8392
+ throw formatAuthRequiredError({
8393
+ retryCommand,
8394
+ advancedHint: "pass --access-token, or set INSTAFY_ACCESS_TOKEN / SUPABASE_ACCESS_TOKEN"
8395
+ });
8396
+ }
8397
+ return {
8398
+ controllerUrl,
8399
+ accessToken: resolved.token,
8400
+ tokenSource: resolved.source,
8401
+ profile: resolved.profile,
8402
+ cwd
8403
+ };
8404
+ }
8405
+ async function controllerJson(auth, retryCommand, params) {
8406
+ const url = `${auth.controllerUrl.replace(/\/$/, "")}${params.path}`;
8407
+ const headers = new Headers();
8408
+ headers.set("accept", "application/json");
8409
+ let bodyText;
8410
+ if (params.body !== void 0) {
8411
+ headers.set("content-type", "application/json");
8412
+ bodyText = JSON.stringify(params.body);
8413
+ }
8414
+ const response = (await fetchWithControllerAuth({
8415
+ url,
8416
+ init: {
8417
+ method: params.method,
8418
+ headers,
8419
+ body: bodyText
8420
+ },
8421
+ accessToken: auth.accessToken,
8422
+ tokenSource: auth.tokenSource,
8423
+ profile: auth.profile,
8424
+ cwd: auth.cwd
8425
+ })).response;
8426
+ const text = await response.text().catch(() => "");
8427
+ if (response.status === 401 || response.status === 403) {
8428
+ throw formatAuthRejectedError({
8429
+ status: response.status,
8430
+ responseBody: text,
8431
+ retryCommand,
8432
+ advancedHint: "run `instafy login`, or pass --access-token"
8433
+ });
8434
+ }
8435
+ if (!response.ok) {
8436
+ const serverMessage = extractControllerErrorMessage(text);
8437
+ throw new ControllerRequestError(
8438
+ response.status,
8439
+ serverMessage,
8440
+ `Request failed (${response.status} ${response.statusText})${serverMessage ? `: ${serverMessage}` : ""}`
8441
+ );
8442
+ }
8443
+ return text ? JSON.parse(text) : null;
8444
+ }
8445
+ async function fetchOrgs(auth) {
8446
+ const payload = await controllerJson(auth, "instafy team list", {
8447
+ method: "GET",
8448
+ path: "/orgs"
8449
+ });
8450
+ return Array.isArray(payload?.orgs) ? payload.orgs : [];
8451
+ }
8452
+ async function resolveTeamId(auth, teamOpt) {
8453
+ const raw = teamOpt?.trim();
8454
+ if (raw && isUuid3(raw)) {
8455
+ return raw;
8456
+ }
8457
+ const orgs = await fetchOrgs(auth);
8458
+ if (raw) {
8459
+ const bySlug = orgs.find(
8460
+ (org) => (org.slug ?? "").toLowerCase() === raw.toLowerCase()
8461
+ );
8462
+ if (bySlug)
8463
+ return bySlug.id;
8464
+ const byId = orgs.find((org) => org.id === raw);
8465
+ if (byId)
8466
+ return byId.id;
8467
+ throw new Error(
8468
+ `No team matches "${raw}". Run \`instafy team list\` to see the teams you belong to.`
8469
+ );
8470
+ }
8471
+ if (orgs.length === 1) {
8472
+ return orgs[0].id;
8473
+ }
8474
+ if (orgs.length === 0) {
8475
+ throw new Error(
8476
+ "No teams found for this account. Run `instafy team list` to see your teams."
8477
+ );
8478
+ }
8479
+ throw new Error(
8480
+ "Multiple teams found. Pass --team-id <uuid|slug>. Run `instafy team list` to see the options."
8481
+ );
8482
+ }
8483
+ function resolveStudioBaseUrl(auth, studioUrlOption) {
8484
+ return normalizeBaseUrl(studioUrlOption ?? null) ?? normalizeBaseUrl(process.env["INSTAFY_STUDIO_URL"] ?? null) ?? normalizeBaseUrl(
8485
+ resolveConfiguredStudioUrl({ profile: auth.profile, cwd: auth.cwd })
8486
+ ) ?? DEFAULT_STUDIO_URL;
8487
+ }
8488
+ function shortId(value) {
8489
+ const trimmed = value.trim();
8490
+ return trimmed.length > 8 ? trimmed.slice(0, 8) : trimmed;
8491
+ }
8492
+ function printTable(headers, rows) {
8493
+ const widths = headers.map(
8494
+ (header, index) => Math.max(header.length, ...rows.map((row) => (row[index] ?? "").length))
8495
+ );
8496
+ const format = (cells) => cells.map((cell, index) => (cell ?? "").padEnd(widths[index])).join(" ").trimEnd();
8497
+ console.log(kleur14.bold(format(headers)));
8498
+ for (const row of rows) {
8499
+ console.log(format(row));
8500
+ }
8501
+ }
8502
+ async function teamMembersList(options) {
8503
+ const auth = resolveControllerAuth4(options, "instafy team members");
8504
+ const orgId = await resolveTeamId(auth, options.teamId);
8505
+ const payload = await controllerJson(auth, "instafy team members", {
8506
+ method: "GET",
8507
+ path: `/orgs/${encodeURIComponent(orgId)}/members`
8508
+ });
8509
+ const members = Array.isArray(payload?.members) ? payload.members : [];
8510
+ if (options.json) {
8511
+ console.log(
8512
+ JSON.stringify(
8513
+ {
8514
+ members,
8515
+ nextCursor: payload?.nextCursor ?? null,
8516
+ hasMore: payload?.hasMore ?? false,
8517
+ total: payload?.total
8518
+ },
8519
+ null,
8520
+ 2
8521
+ )
8522
+ );
8523
+ return;
8524
+ }
8525
+ if (members.length === 0) {
8526
+ console.log(kleur14.gray("No members."));
8527
+ return;
8528
+ }
8529
+ const rows = members.map((member) => [
8530
+ shortId(member.userId),
8531
+ (member.email ?? "").trim() || "-",
8532
+ (member.fullName ?? "").trim() || "-",
8533
+ member.role,
8534
+ (member.createdAt ?? "").trim() || "-"
8535
+ ]);
8536
+ printTable(["ID", "EMAIL", "NAME", "ROLE", "JOINED"], rows);
8537
+ }
8538
+ async function teamInvite(options) {
8539
+ const auth = resolveControllerAuth4(options, "instafy team invite");
8540
+ const email = options.email?.trim() ?? "";
8541
+ if (!email) {
8542
+ throw new Error("An email address is required.");
8543
+ }
8544
+ const role = options.role !== void 0 ? normalizeRole2(options.role, ORG_ROLES, "--role") : void 0;
8545
+ const orgId = await resolveTeamId(auth, options.teamId);
8546
+ const body = { email };
8547
+ if (role)
8548
+ body.role = role;
8549
+ let payload;
8550
+ try {
8551
+ payload = await controllerJson(auth, "instafy team invite", {
8552
+ method: "POST",
8553
+ path: `/orgs/${encodeURIComponent(orgId)}/invitations`,
8554
+ body
8555
+ });
8556
+ } catch (error) {
8557
+ if (error instanceof ControllerRequestError && error.status >= 400 && error.status < 500 && /already a member|already exists|already has access/i.test(
8558
+ error.serverMessage ?? ""
8559
+ )) {
8560
+ throw new Error(
8561
+ `${error.serverMessage}
8562
+
8563
+ If they already have an Instafy account, add them directly with:
8564
+ instafy team add-member --user-id <uuid>${options.teamId ? ` --team-id ${options.teamId}` : ""}`
8565
+ );
8566
+ }
8567
+ throw error;
8568
+ }
8569
+ const invitation = payload?.invitation;
8570
+ if (!invitation?.id) {
8571
+ throw new Error("Invitation response missing invitation details.");
8572
+ }
8573
+ if (options.json) {
8574
+ console.log(
8575
+ JSON.stringify(
8576
+ { invitation, acceptUrl: payload?.acceptUrl ?? null },
8577
+ null,
8578
+ 2
8579
+ )
8580
+ );
8581
+ return;
8582
+ }
8583
+ console.log(kleur14.green(`Invitation sent to ${invitation.email}`));
8584
+ console.log(`Role: ${invitation.role}`);
8585
+ if (invitation.expiresAt) {
8586
+ console.log(`Expires: ${invitation.expiresAt}`);
8587
+ }
8588
+ console.log("");
8589
+ console.log(
8590
+ `${invitation.email} must sign in to Instafy with this email address to accept the invitation.`
8591
+ );
8592
+ }
8593
+ async function teamInviteLink(options) {
8594
+ const auth = resolveControllerAuth4(options, "instafy team invite-link");
8595
+ const role = options.role !== void 0 ? normalizeRole2(options.role, INVITE_LINK_ROLES, "--role") : void 0;
8596
+ const orgId = await resolveTeamId(auth, options.teamId);
8597
+ const body = {};
8598
+ if (role)
8599
+ body.role = role;
8600
+ const payload = await controllerJson(auth, "instafy team invite-link", {
8601
+ method: "POST",
8602
+ path: `/orgs/${encodeURIComponent(orgId)}/invite-links`,
8603
+ body
8604
+ });
8605
+ const inviteLink = payload?.inviteLink;
8606
+ if (!inviteLink?.token || !inviteLink.acceptPath) {
8607
+ throw new Error("Invite link response missing link details.");
8608
+ }
8609
+ const studioBase = resolveStudioBaseUrl(auth, options.studioUrl);
8610
+ const acceptUrl = typeof payload?.acceptUrl === "string" && payload.acceptUrl.trim() || `${studioBase}${inviteLink.acceptPath}`;
8611
+ if (options.json) {
8612
+ console.log(JSON.stringify({ inviteLink, acceptUrl }, null, 2));
8613
+ return;
8614
+ }
8615
+ console.log(kleur14.green("Shareable invite link created."));
8616
+ console.log(`Role: ${inviteLink.role}`);
8617
+ console.log(`Link: ${acceptUrl}`);
8618
+ console.log(`Token: ${inviteLink.token}`);
8619
+ if (inviteLink.expiresAt) {
8620
+ console.log(`Expires: ${inviteLink.expiresAt}`);
8621
+ }
8622
+ console.log("");
8623
+ console.log(
8624
+ "Anyone who opens this link and signs in (with any sign-in method) can join the team. Treat it like a shared secret."
8625
+ );
8626
+ }
8627
+ async function teamInvites(options) {
8628
+ const auth = resolveControllerAuth4(options, "instafy team invites");
8629
+ const orgId = await resolveTeamId(auth, options.teamId);
8630
+ const [invitationsPayload, linksPayload] = await Promise.all([
8631
+ controllerJson(auth, "instafy team invites", {
8632
+ method: "GET",
8633
+ path: `/orgs/${encodeURIComponent(orgId)}/invitations`
8634
+ }),
8635
+ controllerJson(auth, "instafy team invites", {
8636
+ method: "GET",
8637
+ path: `/orgs/${encodeURIComponent(orgId)}/invite-links`
8638
+ })
8639
+ ]);
8640
+ const invitations = Array.isArray(invitationsPayload?.invitations) ? invitationsPayload.invitations : [];
8641
+ const inviteLinks = Array.isArray(linksPayload?.inviteLinks) ? linksPayload.inviteLinks : [];
8642
+ if (options.json) {
8643
+ console.log(JSON.stringify({ invitations, inviteLinks }, null, 2));
8644
+ return;
8645
+ }
8646
+ if (invitations.length === 0 && inviteLinks.length === 0) {
8647
+ console.log(kleur14.gray("No pending invitations or invite links."));
8648
+ return;
8649
+ }
8650
+ if (invitations.length > 0) {
8651
+ console.log(kleur14.bold("Email invitations"));
8652
+ const rows = invitations.map((invitation) => [
8653
+ shortId(invitation.id),
8654
+ invitation.email,
8655
+ invitation.role,
8656
+ (invitation.expiresAt ?? "").trim() || "-"
8657
+ ]);
8658
+ printTable(["ID", "EMAIL", "ROLE", "EXPIRES"], rows);
8659
+ }
8660
+ if (inviteLinks.length > 0) {
8661
+ if (invitations.length > 0)
8662
+ console.log("");
8663
+ console.log(kleur14.bold("Invite links (shareable, grant join on sign-in)"));
8664
+ const rows = inviteLinks.map((link) => [
8665
+ shortId(link.id),
8666
+ link.role,
8667
+ (link.status ?? "").trim() || "-",
8668
+ (link.expiresAt ?? "").trim() || "-"
8669
+ ]);
8670
+ printTable(["ID", "ROLE", "STATUS", "EXPIRES"], rows);
8671
+ }
8672
+ }
8673
+ async function teamAddMember(options) {
8674
+ const auth = resolveControllerAuth4(options, "instafy team add-member");
8675
+ const userId = requireUuid(options.userId, "--user-id");
8676
+ const role = options.role !== void 0 ? normalizeRole2(options.role, ORG_ROLES, "--role") : void 0;
8677
+ const orgId = await resolveTeamId(auth, options.teamId);
8678
+ const body = { userId };
8679
+ if (role)
8680
+ body.role = role;
8681
+ const payload = await controllerJson(auth, "instafy team add-member", {
8682
+ method: "POST",
8683
+ path: `/orgs/${encodeURIComponent(orgId)}/members`,
8684
+ body
8685
+ });
8686
+ const member = payload?.member;
8687
+ if (!member?.userId) {
8688
+ throw new Error("Add member response missing member details.");
8689
+ }
8690
+ if (options.json) {
8691
+ console.log(JSON.stringify({ member }, null, 2));
8692
+ return;
8693
+ }
8694
+ const label = (member.email ?? "").trim() || member.userId;
8695
+ console.log(kleur14.green(`Added ${label} as ${member.role}.`));
8696
+ }
8697
+ async function teamAccept(options) {
8698
+ const auth = resolveControllerAuth4(options, "instafy team accept");
8699
+ const token = requireUuid(options.token, "token");
8700
+ const payload = await controllerJson(auth, "instafy team accept", {
8701
+ method: "POST",
8702
+ path: "/org-invitations/accept",
8703
+ body: { token }
8704
+ });
8705
+ if (options.json) {
8706
+ console.log(JSON.stringify(payload ?? {}, null, 2));
8707
+ return;
8708
+ }
8709
+ const name = (payload?.orgName ?? "").trim() || payload?.orgSlug || payload?.orgId || "team";
8710
+ console.log(kleur14.green(`Joined ${name} as ${payload?.role ?? "member"}.`));
8711
+ }
8712
+ async function confirmDestructive(message, yes, json) {
8713
+ if (yes)
8714
+ return;
8715
+ const input3 = process.stdin;
8716
+ const interactive = Boolean(
8717
+ input3.isTTY && process.stdout.isTTY && json !== true && process.env["CI"] !== "true"
8718
+ );
8719
+ if (!interactive) {
8720
+ throw new Error("Refusing to continue without confirmation. Re-run with --yes.");
8721
+ }
8722
+ const { confirm, isCancel } = await import("@clack/prompts");
8723
+ const proceed = await confirm({ message, initialValue: false });
8724
+ if (isCancel(proceed) || !proceed) {
8725
+ throw new Error("Cancelled.");
8726
+ }
8727
+ }
8728
+ async function teamRevokeInvite(options) {
8729
+ const auth = resolveControllerAuth4(options, "instafy team revoke-invite");
8730
+ const invitationId = requireUuid(options.invitationId, "invitation id");
8731
+ const orgId = await resolveTeamId(auth, options.teamId);
8732
+ await confirmDestructive(
8733
+ `Revoke pending invitation ${shortId(invitationId)}?`,
8734
+ options.yes,
8735
+ options.json
8736
+ );
8737
+ await controllerJson(auth, "instafy team revoke-invite", {
8738
+ method: "DELETE",
8739
+ path: `/orgs/${encodeURIComponent(orgId)}/invitations/${encodeURIComponent(invitationId)}`
8740
+ });
8741
+ if (options.json) {
8742
+ console.log(JSON.stringify({ ok: true, invitationId }, null, 2));
8743
+ return;
8744
+ }
8745
+ console.log(kleur14.green(`Revoked invitation ${invitationId}.`));
8746
+ }
8747
+ async function teamRevokeLink(options) {
8748
+ const auth = resolveControllerAuth4(options, "instafy team revoke-link");
8749
+ const inviteLinkId = requireUuid(options.inviteLinkId, "invite link id");
8750
+ const orgId = await resolveTeamId(auth, options.teamId);
8751
+ await confirmDestructive(
8752
+ `Revoke invite link ${shortId(inviteLinkId)}?`,
8753
+ options.yes,
8754
+ options.json
8755
+ );
8756
+ await controllerJson(auth, "instafy team revoke-link", {
8757
+ method: "DELETE",
8758
+ path: `/orgs/${encodeURIComponent(orgId)}/invite-links/${encodeURIComponent(inviteLinkId)}`
8759
+ });
8760
+ if (options.json) {
8761
+ console.log(JSON.stringify({ ok: true, inviteLinkId }, null, 2));
8762
+ return;
8763
+ }
8764
+ console.log(kleur14.green(`Revoked invite link ${inviteLinkId}.`));
8765
+ }
8766
+
7835
8767
  // src/provider-bindings.ts
7836
8768
  init_project_manifest();
7837
- import fs10 from "node:fs";
7838
- import path11 from "node:path";
8769
+ import fs11 from "node:fs";
8770
+ import path12 from "node:path";
7839
8771
  import { pathToFileURL } from "node:url";
7840
- import kleur13 from "kleur";
8772
+ import kleur15 from "kleur";
7841
8773
  function resolveBindingContext(startDir) {
7842
- const lookup = findProjectManifest(path11.resolve(startDir));
8774
+ const lookup = findProjectManifest(path12.resolve(startDir));
7843
8775
  if (!lookup.manifest || !lookup.path) {
7844
8776
  throw new Error(
7845
8777
  "Linked space manifest required. Run `instafy space init` in this folder or pass --path <dir>."
@@ -7849,19 +8781,19 @@ function resolveBindingContext(startDir) {
7849
8781
  if (!projectId) {
7850
8782
  throw new Error("Linked space manifest is missing a spaceId.");
7851
8783
  }
7852
- const instafyDir = path11.dirname(lookup.path);
7853
- const projectRoot = path11.dirname(instafyDir);
8784
+ const instafyDir = path12.dirname(lookup.path);
8785
+ const projectRoot = path12.dirname(instafyDir);
7854
8786
  return {
7855
- bindingsPath: path11.join(instafyDir, "provider-bindings.json"),
8787
+ bindingsPath: path12.join(instafyDir, "provider-bindings.json"),
7856
8788
  projectId,
7857
8789
  projectRoot
7858
8790
  };
7859
8791
  }
7860
8792
  function readBindingStore(bindingsPath) {
7861
- if (!fs10.existsSync(bindingsPath)) {
8793
+ if (!fs11.existsSync(bindingsPath)) {
7862
8794
  return { version: 1, bindings: {} };
7863
8795
  }
7864
- const raw = JSON.parse(fs10.readFileSync(bindingsPath, "utf8"));
8796
+ const raw = JSON.parse(fs11.readFileSync(bindingsPath, "utf8"));
7865
8797
  const bindings = raw && typeof raw.bindings === "object" && raw.bindings !== null ? raw.bindings : {};
7866
8798
  return {
7867
8799
  version: 1,
@@ -7869,8 +8801,8 @@ function readBindingStore(bindingsPath) {
7869
8801
  };
7870
8802
  }
7871
8803
  function writeBindingStore(bindingsPath, store) {
7872
- fs10.mkdirSync(path11.dirname(bindingsPath), { recursive: true });
7873
- fs10.writeFileSync(bindingsPath, JSON.stringify(store, null, 2), "utf8");
8804
+ fs11.mkdirSync(path12.dirname(bindingsPath), { recursive: true });
8805
+ fs11.writeFileSync(bindingsPath, JSON.stringify(store, null, 2), "utf8");
7874
8806
  }
7875
8807
  function normalizeCapabilities2(values) {
7876
8808
  const requested = Array.isArray(values) && values.length > 0 ? values : ["project_content_read", "project_content_write"];
@@ -7893,7 +8825,7 @@ function deriveStatus(capabilities) {
7893
8825
  return "bound_read_only";
7894
8826
  }
7895
8827
  function printBinding(record) {
7896
- console.log(kleur13.cyan(`Provider: ${record.providerId}`));
8828
+ console.log(kleur15.cyan(`Provider: ${record.providerId}`));
7897
8829
  console.log(`Status: ${record.status}`);
7898
8830
  console.log(`Project: ${record.projectId}`);
7899
8831
  console.log(`Root URI: ${record.rootUri}`);
@@ -7935,10 +8867,10 @@ function showProviderBinding(options) {
7935
8867
  return;
7936
8868
  }
7937
8869
  if (bindings.length === 0) {
7938
- console.log(kleur13.yellow("No provider bindings recorded for this space."));
8870
+ console.log(kleur15.yellow("No provider bindings recorded for this space."));
7939
8871
  return;
7940
8872
  }
7941
- console.log(kleur13.cyan(`Bindings for ${context.projectId}:`));
8873
+ console.log(kleur15.cyan(`Bindings for ${context.projectId}:`));
7942
8874
  for (const record of bindings) {
7943
8875
  console.log(`- ${record.providerId} (${record.status})`);
7944
8876
  }
@@ -7970,9 +8902,9 @@ function grantProviderBinding(options) {
7970
8902
  console.log(JSON.stringify(record));
7971
8903
  return;
7972
8904
  }
7973
- console.log(kleur13.green(`Provider binding granted: ${providerId}`));
8905
+ console.log(kleur15.green(`Provider binding granted: ${providerId}`));
7974
8906
  printBinding(record);
7975
- console.log(kleur13.cyan(`Stored at ${context.bindingsPath}`));
8907
+ console.log(kleur15.cyan(`Stored at ${context.bindingsPath}`));
7976
8908
  }
7977
8909
  function revokeProviderBinding(options) {
7978
8910
  const providerId = options.providerId.trim();
@@ -7991,12 +8923,12 @@ function revokeProviderBinding(options) {
7991
8923
  console.log(JSON.stringify({ revoked: true, providerId }));
7992
8924
  return;
7993
8925
  }
7994
- console.log(kleur13.green(`Provider binding revoked: ${providerId}`));
7995
- console.log(kleur13.cyan(`Updated ${context.bindingsPath}`));
8926
+ console.log(kleur15.green(`Provider binding revoked: ${providerId}`));
8927
+ console.log(kleur15.cyan(`Updated ${context.bindingsPath}`));
7996
8928
  }
7997
8929
 
7998
8930
  // src/providers.ts
7999
- import kleur14 from "kleur";
8931
+ import kleur16 from "kleur";
8000
8932
 
8001
8933
  // ../provider-client/index.js
8002
8934
  var DEFAULT_PROVIDER_HOST_BASE_URL = "http://127.0.0.1:8797";
@@ -8068,12 +9000,12 @@ function resolveFetchImplementation(fetchImpl) {
8068
9000
  function createProviderHostClient(options = {}) {
8069
9001
  const fetchImpl = resolveFetchImplementation(options.fetch);
8070
9002
  const baseUrl = normalizeProviderHostBaseUrl(options.baseUrl);
8071
- async function jsonRequest(path14, init) {
8072
- const response = await fetchImpl(`${baseUrl}${path14}`, init);
9003
+ async function jsonRequest(path15, init) {
9004
+ const response = await fetchImpl(`${baseUrl}${path15}`, init);
8073
9005
  const json = await response.json();
8074
9006
  if (!response.ok || json?.ok === false) {
8075
9007
  throw new Error(
8076
- json?.error || json?.stderr || `provider host request failed for ${path14}`
9008
+ json?.error || json?.stderr || `provider host request failed for ${path15}`
8077
9009
  );
8078
9010
  }
8079
9011
  return json;
@@ -8166,7 +9098,7 @@ function formatProviderLabel(provider) {
8166
9098
  return provider.title?.trim().length ? provider.title.trim() : provider.id;
8167
9099
  }
8168
9100
  function printProviderSummary(provider) {
8169
- console.log(kleur14.green(formatProviderLabel(provider)));
9101
+ console.log(kleur16.green(formatProviderLabel(provider)));
8170
9102
  console.log(` id: ${provider.id}`);
8171
9103
  if (provider.description) {
8172
9104
  console.log(` description: ${provider.description}`);
@@ -8178,7 +9110,7 @@ function printProviderSummary(provider) {
8178
9110
  console.log(` type: ${provider.providerType}`);
8179
9111
  }
8180
9112
  console.log(
8181
- ` discoverable: ${provider.discoverable !== false ? kleur14.green("yes") : kleur14.yellow("no")}`
9113
+ ` discoverable: ${provider.discoverable !== false ? kleur16.green("yes") : kleur16.yellow("no")}`
8182
9114
  );
8183
9115
  if (provider.capabilityIds?.length) {
8184
9116
  console.log(` capabilities: ${provider.capabilityIds.join(", ")}`);
@@ -8190,7 +9122,7 @@ function printProviderSummary(provider) {
8190
9122
  console.log(` resources: ${provider.resourceUris.join(", ")}`);
8191
9123
  }
8192
9124
  if (provider.error) {
8193
- console.log(` error: ${kleur14.yellow(provider.error)}`);
9125
+ console.log(` error: ${kleur16.yellow(provider.error)}`);
8194
9126
  }
8195
9127
  }
8196
9128
  function parseCommandJson(commandJson) {
@@ -8210,10 +9142,10 @@ async function providersList(options = {}) {
8210
9142
  });
8211
9143
  return;
8212
9144
  }
8213
- console.log(kleur14.green("Local providers"));
9145
+ console.log(kleur16.green("Local providers"));
8214
9146
  console.log(`Host: ${providerHostUrl}`);
8215
9147
  if (!result.providers.length) {
8216
- console.log(kleur14.yellow("No providers discovered."));
9148
+ console.log(kleur16.yellow("No providers discovered."));
8217
9149
  return;
8218
9150
  }
8219
9151
  for (const provider of result.providers) {
@@ -8230,7 +9162,7 @@ async function providersDiscover(params) {
8230
9162
  });
8231
9163
  return;
8232
9164
  }
8233
- console.log(kleur14.green(`Provider discovery: ${params.providerId}`));
9165
+ console.log(kleur16.green(`Provider discovery: ${params.providerId}`));
8234
9166
  console.log(`Host: ${providerHostUrl}`);
8235
9167
  console.log(JSON.stringify(result.provider ?? result, null, 2));
8236
9168
  }
@@ -8244,10 +9176,10 @@ async function providersRead(params) {
8244
9176
  });
8245
9177
  return;
8246
9178
  }
8247
- console.log(kleur14.green(`Provider resource: ${params.uri}`));
9179
+ console.log(kleur16.green(`Provider resource: ${params.uri}`));
8248
9180
  console.log(`Host: ${providerHostUrl}`);
8249
9181
  if (result.exists === false) {
8250
- console.log(kleur14.yellow("Resource does not exist."));
9182
+ console.log(kleur16.yellow("Resource does not exist."));
8251
9183
  return;
8252
9184
  }
8253
9185
  console.log(JSON.stringify(result.value ?? result, null, 2));
@@ -8273,7 +9205,7 @@ async function providersProbe(params) {
8273
9205
  });
8274
9206
  return;
8275
9207
  }
8276
- console.log(kleur14.green(`Provider transport probe: ${params.providerId}`));
9208
+ console.log(kleur16.green(`Provider transport probe: ${params.providerId}`));
8277
9209
  console.log(`Host: ${providerHostUrl}`);
8278
9210
  console.log(JSON.stringify(result, null, 2));
8279
9211
  }
@@ -8479,8 +9411,8 @@ async function diagnosticsRunResult(options) {
8479
9411
  }
8480
9412
 
8481
9413
  // src/support.ts
8482
- import fs11 from "node:fs";
8483
- import path12 from "node:path";
9414
+ import fs12 from "node:fs";
9415
+ import path13 from "node:path";
8484
9416
  init_project_manifest();
8485
9417
  var MAX_SUMMARY_CHARS = 500;
8486
9418
  var MAX_DETAILS_BYTES = 2e4;
@@ -8494,34 +9426,34 @@ function cleanText3(value) {
8494
9426
  return typeof value === "string" && value.trim() ? value.trim() : null;
8495
9427
  }
8496
9428
  function readRegularFile(filePath, maxBytes, label) {
8497
- const resolved = path12.resolve(filePath);
9429
+ const resolved = path13.resolve(filePath);
8498
9430
  const manifestPath = findProjectManifest(process.cwd()).path;
8499
- const workspaceRoot = manifestPath ? path12.dirname(path12.dirname(manifestPath)) : process.cwd();
8500
- const canonicalRoot = fs11.realpathSync(workspaceRoot);
8501
- const canonicalParent = fs11.realpathSync(path12.dirname(resolved));
8502
- const canonicalTarget = path12.join(canonicalParent, path12.basename(resolved));
8503
- const relativeTarget = path12.relative(canonicalRoot, canonicalTarget);
8504
- if (relativeTarget === ".." || relativeTarget.startsWith(`..${path12.sep}`) || path12.isAbsolute(relativeTarget)) {
9431
+ const workspaceRoot = manifestPath ? path13.dirname(path13.dirname(manifestPath)) : process.cwd();
9432
+ const canonicalRoot = fs12.realpathSync(workspaceRoot);
9433
+ const canonicalParent = fs12.realpathSync(path13.dirname(resolved));
9434
+ const canonicalTarget = path13.join(canonicalParent, path13.basename(resolved));
9435
+ const relativeTarget = path13.relative(canonicalRoot, canonicalTarget);
9436
+ if (relativeTarget === ".." || relativeTarget.startsWith(`..${path13.sep}`) || path13.isAbsolute(relativeTarget)) {
8505
9437
  throw new Error(`${label} must stay inside the active Instafy workspace.`);
8506
9438
  }
8507
- const initialStat = fs11.lstatSync(canonicalTarget);
9439
+ const initialStat = fs12.lstatSync(canonicalTarget);
8508
9440
  if (initialStat.isSymbolicLink() || !initialStat.isFile()) {
8509
9441
  throw new Error(`${label} must be a regular file, not a symlink.`);
8510
9442
  }
8511
- const noFollow = typeof fs11.constants.O_NOFOLLOW === "number" ? fs11.constants.O_NOFOLLOW : 0;
8512
- const descriptor = fs11.openSync(canonicalTarget, fs11.constants.O_RDONLY | noFollow);
9443
+ const noFollow = typeof fs12.constants.O_NOFOLLOW === "number" ? fs12.constants.O_NOFOLLOW : 0;
9444
+ const descriptor = fs12.openSync(canonicalTarget, fs12.constants.O_RDONLY | noFollow);
8513
9445
  try {
8514
- const openedStat = fs11.fstatSync(descriptor);
9446
+ const openedStat = fs12.fstatSync(descriptor);
8515
9447
  if (!openedStat.isFile() || openedStat.dev !== initialStat.dev || openedStat.ino !== initialStat.ino || openedStat.size > maxBytes) {
8516
9448
  throw new Error(`${label} must be a regular file no larger than ${maxBytes} bytes.`);
8517
9449
  }
8518
- const bytes = fs11.readFileSync(descriptor);
9450
+ const bytes = fs12.readFileSync(descriptor);
8519
9451
  if (bytes.length > maxBytes) {
8520
9452
  throw new Error(`${label} must be ${maxBytes} bytes or smaller.`);
8521
9453
  }
8522
9454
  return bytes;
8523
9455
  } finally {
8524
- fs11.closeSync(descriptor);
9456
+ fs12.closeSync(descriptor);
8525
9457
  }
8526
9458
  }
8527
9459
  function readJsonFile(filePath, maxBytes, label) {
@@ -8597,7 +9529,7 @@ function resolveScreenshots(filePaths = []) {
8597
9529
  );
8598
9530
  }
8599
9531
  return {
8600
- fileName: path12.basename(filePath),
9532
+ fileName: path13.basename(filePath),
8601
9533
  mediaType,
8602
9534
  dataBase64: bytes.toString("base64"),
8603
9535
  byteLength: bytes.length
@@ -8900,7 +9832,7 @@ program.command("login").description("Log in and save an access token for future
8900
9832
  json: opts.json
8901
9833
  });
8902
9834
  } catch (error) {
8903
- console.error(kleur17.red(String(error)));
9835
+ console.error(kleur19.red(String(error)));
8904
9836
  process.exit(1);
8905
9837
  }
8906
9838
  });
@@ -8908,7 +9840,7 @@ program.command("logout").description("Clear the saved CLI access token").option
8908
9840
  try {
8909
9841
  await logout({ json: opts.json, profile: opts.profile });
8910
9842
  } catch (error) {
8911
- console.error(kleur17.red(String(error)));
9843
+ console.error(kleur19.red(String(error)));
8912
9844
  process.exit(1);
8913
9845
  }
8914
9846
  });
@@ -8932,7 +9864,7 @@ chatCommand.option("--json", "Output JSON").action(async (promptParts, opts) =>
8932
9864
  json: opts.json
8933
9865
  });
8934
9866
  } catch (error) {
8935
- console.error(kleur17.red(String(error)));
9867
+ console.error(kleur19.red(String(error)));
8936
9868
  process.exit(1);
8937
9869
  }
8938
9870
  });
@@ -8956,7 +9888,7 @@ function registerSpaceCommand(command) {
8956
9888
  json: opts.json
8957
9889
  });
8958
9890
  } catch (error) {
8959
- console.error(kleur17.red(String(error)));
9891
+ console.error(kleur19.red(String(error)));
8960
9892
  process.exit(1);
8961
9893
  }
8962
9894
  });
@@ -8972,7 +9904,7 @@ function registerSpaceCommand(command) {
8972
9904
  json: opts.json
8973
9905
  });
8974
9906
  } catch (error) {
8975
- console.error(kleur17.red(String(error)));
9907
+ console.error(kleur19.red(String(error)));
8976
9908
  process.exit(1);
8977
9909
  }
8978
9910
  });
@@ -8986,7 +9918,7 @@ function registerSpaceCommand(command) {
8986
9918
  json: opts.json
8987
9919
  });
8988
9920
  } catch (error) {
8989
- console.error(kleur17.red(String(error)));
9921
+ console.error(kleur19.red(String(error)));
8990
9922
  process.exit(1);
8991
9923
  }
8992
9924
  });
@@ -9009,7 +9941,7 @@ function registerSpaceCommand(command) {
9009
9941
  json: opts.json
9010
9942
  });
9011
9943
  } catch (error) {
9012
- console.error(kleur17.red(String(error)));
9944
+ console.error(kleur19.red(String(error)));
9013
9945
  process.exit(1);
9014
9946
  }
9015
9947
  });
@@ -9021,7 +9953,7 @@ function registerSpaceCommand(command) {
9021
9953
  json: opts.json
9022
9954
  });
9023
9955
  } catch (error) {
9024
- console.error(kleur17.red(String(error)));
9956
+ console.error(kleur19.red(String(error)));
9025
9957
  process.exit(1);
9026
9958
  }
9027
9959
  });
@@ -9043,7 +9975,7 @@ function registerSpaceCommand(command) {
9043
9975
  json: opts.json
9044
9976
  });
9045
9977
  } catch (error) {
9046
- console.error(kleur17.red(String(error)));
9978
+ console.error(kleur19.red(String(error)));
9047
9979
  process.exit(1);
9048
9980
  }
9049
9981
  });
@@ -9059,7 +9991,7 @@ function registerSpaceCommand(command) {
9059
9991
  json: opts.json
9060
9992
  });
9061
9993
  } catch (error) {
9062
- console.error(kleur17.red(String(error)));
9994
+ console.error(kleur19.red(String(error)));
9063
9995
  process.exit(1);
9064
9996
  }
9065
9997
  });
@@ -9085,7 +10017,7 @@ function registerSpaceCommand(command) {
9085
10017
  json: opts.json
9086
10018
  });
9087
10019
  } catch (error) {
9088
- console.error(kleur17.red(String(error)));
10020
+ console.error(kleur19.red(String(error)));
9089
10021
  process.exit(1);
9090
10022
  }
9091
10023
  });
@@ -9110,7 +10042,7 @@ function registerSpaceCommand(command) {
9110
10042
  json: opts.json
9111
10043
  });
9112
10044
  } catch (error) {
9113
- console.error(kleur17.red(String(error)));
10045
+ console.error(kleur19.red(String(error)));
9114
10046
  process.exit(1);
9115
10047
  }
9116
10048
  });
@@ -9130,7 +10062,7 @@ hardwareBindingsCommand.command("show").description("Show hardware bindings for
9130
10062
  json: opts.json
9131
10063
  });
9132
10064
  } catch (error) {
9133
- console.error(kleur17.red(String(error)));
10065
+ console.error(kleur19.red(String(error)));
9134
10066
  process.exit(1);
9135
10067
  }
9136
10068
  });
@@ -9150,7 +10082,7 @@ hardwareBindingsCommand.command("grant").description("Grant a local hardware pro
9150
10082
  json: opts.json
9151
10083
  });
9152
10084
  } catch (error) {
9153
- console.error(kleur17.red(String(error)));
10085
+ console.error(kleur19.red(String(error)));
9154
10086
  process.exit(1);
9155
10087
  }
9156
10088
  });
@@ -9162,7 +10094,7 @@ hardwareBindingsCommand.command("revoke").description("Revoke a local hardware b
9162
10094
  json: opts.json
9163
10095
  });
9164
10096
  } catch (error) {
9165
- console.error(kleur17.red(String(error)));
10097
+ console.error(kleur19.red(String(error)));
9166
10098
  process.exit(1);
9167
10099
  }
9168
10100
  });
@@ -9176,7 +10108,7 @@ hardwareSerialCommand.command("list").description("List local serial devices vis
9176
10108
  scanDirs: opts.scanDir
9177
10109
  });
9178
10110
  } catch (error) {
9179
- console.error(kleur17.red(String(error)));
10111
+ console.error(kleur19.red(String(error)));
9180
10112
  process.exit(1);
9181
10113
  }
9182
10114
  });
@@ -9188,7 +10120,7 @@ hardwareSerialCommand.command("probe").description("Probe host permissions for o
9188
10120
  json: opts.json
9189
10121
  });
9190
10122
  } catch (error) {
9191
- console.error(kleur17.red(String(error)));
10123
+ console.error(kleur19.red(String(error)));
9192
10124
  process.exit(1);
9193
10125
  }
9194
10126
  });
@@ -9200,7 +10132,7 @@ hardwareCommand.command("opportunities").description("List host-local IO opportu
9200
10132
  scanDirs: opts.scanDir
9201
10133
  });
9202
10134
  } catch (error) {
9203
- console.error(kleur17.red(String(error)));
10135
+ console.error(kleur19.red(String(error)));
9204
10136
  process.exit(1);
9205
10137
  }
9206
10138
  });
@@ -9213,7 +10145,7 @@ hardwareCommand.command("run").description("Run one available host-local IO acti
9213
10145
  json: opts.json
9214
10146
  });
9215
10147
  } catch (error) {
9216
- console.error(kleur17.red(String(error)));
10148
+ console.error(kleur19.red(String(error)));
9217
10149
  process.exit(1);
9218
10150
  }
9219
10151
  });
@@ -9228,7 +10160,7 @@ providersListCommand.option("--json", "Output JSON").action(async (opts) => {
9228
10160
  json: opts.json
9229
10161
  });
9230
10162
  } catch (error) {
9231
- console.error(kleur17.red(String(error)));
10163
+ console.error(kleur19.red(String(error)));
9232
10164
  process.exit(1);
9233
10165
  }
9234
10166
  });
@@ -9242,7 +10174,7 @@ providersDiscoverCommand.option("--json", "Output JSON").action(async (providerI
9242
10174
  json: opts.json
9243
10175
  });
9244
10176
  } catch (error) {
9245
- console.error(kleur17.red(String(error)));
10177
+ console.error(kleur19.red(String(error)));
9246
10178
  process.exit(1);
9247
10179
  }
9248
10180
  });
@@ -9257,7 +10189,7 @@ providersReadCommand.option("--json", "Output JSON").action(async (providerId, u
9257
10189
  json: opts.json
9258
10190
  });
9259
10191
  } catch (error) {
9260
- console.error(kleur17.red(String(error)));
10192
+ console.error(kleur19.red(String(error)));
9261
10193
  process.exit(1);
9262
10194
  }
9263
10195
  });
@@ -9280,7 +10212,7 @@ providersProbeCommand.option("--json", "Output JSON").action(async (providerId,
9280
10212
  json: opts.json
9281
10213
  });
9282
10214
  } catch (error) {
9283
- console.error(kleur17.red(String(error)));
10215
+ console.error(kleur19.red(String(error)));
9284
10216
  process.exit(1);
9285
10217
  }
9286
10218
  });
@@ -9302,7 +10234,7 @@ agentsListCommand.option("--json", "Output JSON").action(async (opts) => {
9302
10234
  json: opts.json
9303
10235
  });
9304
10236
  } catch (error) {
9305
- console.error(kleur17.red(String(error)));
10237
+ console.error(kleur19.red(String(error)));
9306
10238
  process.exit(1);
9307
10239
  }
9308
10240
  });
@@ -9330,7 +10262,7 @@ agentsContextListCommand.option("--agent <handle>", "Filter by agent handle (for
9330
10262
  json: opts.json
9331
10263
  });
9332
10264
  } catch (error) {
9333
- console.error(kleur17.red(String(error)));
10265
+ console.error(kleur19.red(String(error)));
9334
10266
  process.exit(1);
9335
10267
  }
9336
10268
  });
@@ -9356,7 +10288,7 @@ agentsContextPutCommand.action(async (contextParts, opts) => {
9356
10288
  json: opts.json
9357
10289
  });
9358
10290
  } catch (error) {
9359
- console.error(kleur17.red(String(error)));
10291
+ console.error(kleur19.red(String(error)));
9360
10292
  process.exit(1);
9361
10293
  }
9362
10294
  });
@@ -9372,7 +10304,7 @@ agentsStatusCommand.option("--json", "Output JSON").action(async (groupId, opts)
9372
10304
  json: opts.json
9373
10305
  });
9374
10306
  } catch (error) {
9375
- console.error(kleur17.red(String(error)));
10307
+ console.error(kleur19.red(String(error)));
9376
10308
  process.exit(1);
9377
10309
  }
9378
10310
  });
@@ -9390,7 +10322,7 @@ agentsCancelCommand.option("--json", "Output JSON").action(async (opts) => {
9390
10322
  json: opts.json
9391
10323
  });
9392
10324
  } catch (error) {
9393
- console.error(kleur17.red(String(error)));
10325
+ console.error(kleur19.red(String(error)));
9394
10326
  process.exit(1);
9395
10327
  }
9396
10328
  });
@@ -9412,7 +10344,7 @@ secretsListCommand.option("--json", "Output JSON").action(async (opts) => {
9412
10344
  json: opts.json
9413
10345
  });
9414
10346
  } catch (error) {
9415
- console.error(kleur17.red(String(error)));
10347
+ console.error(kleur19.red(String(error)));
9416
10348
  process.exit(1);
9417
10349
  }
9418
10350
  });
@@ -9433,7 +10365,7 @@ secretsGetCommand.option("--json", "Output JSON").action(async (nameOrId, opts)
9433
10365
  json: opts.json
9434
10366
  });
9435
10367
  } catch (error) {
9436
- console.error(kleur17.red(String(error)));
10368
+ console.error(kleur19.red(String(error)));
9437
10369
  process.exit(1);
9438
10370
  }
9439
10371
  });
@@ -9458,7 +10390,7 @@ secretsPutCommand.option("--json", "Output JSON").action(async (name, opts) => {
9458
10390
  json: opts.json
9459
10391
  });
9460
10392
  } catch (error) {
9461
- console.error(kleur17.red(String(error)));
10393
+ console.error(kleur19.red(String(error)));
9462
10394
  process.exit(1);
9463
10395
  }
9464
10396
  });
@@ -9479,10 +10411,24 @@ secretsRevokeCommand.option("--json", "Output JSON").action(async (nameOrId, opt
9479
10411
  json: opts.json
9480
10412
  });
9481
10413
  } catch (error) {
9482
- console.error(kleur17.red(String(error)));
10414
+ console.error(kleur19.red(String(error)));
9483
10415
  process.exit(1);
9484
10416
  }
9485
10417
  });
10418
+ function resolveResultVisibilityFlag(opts) {
10419
+ if (opts.resultVisibility !== void 0) {
10420
+ const value = String(opts.resultVisibility).trim().toLowerCase();
10421
+ if (value !== "private" && value !== "team") {
10422
+ throw new Error("--result-visibility must be private or team");
10423
+ }
10424
+ return value;
10425
+ }
10426
+ if (opts.shareResults === true)
10427
+ return "team";
10428
+ if (opts.shareResults === false)
10429
+ return "private";
10430
+ return void 0;
10431
+ }
9486
10432
  var automationsCommand = program.command("automations").description("Manage scheduled automations (run prompts on a schedule)");
9487
10433
  failWithGroupHelp(automationsCommand);
9488
10434
  var automationsListCommand = automationsCommand.command("list").description("List automations for this space");
@@ -9501,13 +10447,22 @@ automationsListCommand.option("--json", "Output JSON").action(async (opts) => {
9501
10447
  json: opts.json
9502
10448
  });
9503
10449
  } catch (error) {
9504
- console.error(kleur17.red(String(error)));
10450
+ console.error(kleur19.red(String(error)));
9505
10451
  process.exit(1);
9506
10452
  }
9507
10453
  });
9508
10454
  var automationsCreateCommand = automationsCommand.command("create").description("Create an automation").requiredOption("--name <name>", "Automation name").requiredOption("--prompt <text>", "Prompt to run on schedule").option("--schedule-kind <kind>", "weekly|hourly|once (default: weekly)").option("--run-at <datetime>", "Run time for once schedule (RFC3339 or YYYY-MM-DDTHH:MM[:SS])").option("--interval-hours <n>", "Interval hours (hourly schedule)", (value) => Number(value)).option("--days <list>", "Weekdays for weekly schedule (e.g. mo,tu,we,th,fr)").option("--time <hh:mm>", "Time for weekly schedule (24h, e.g. 09:00)").option("--timezone <tz>", "IANA timezone (e.g. America/New_York)").option("--runtime-mode <mode>", "auto|hosted|existing (default: auto)").option("--runtime-provider <id>", "Runtime provider id (optional)").option(
9509
10455
  "--silent-when-nothing-to-report",
9510
10456
  "Do not post a completion result or send a result notification when a successful run has no findings"
10457
+ ).option(
10458
+ "--share-results",
10459
+ "Share this automation's result threads with your team (visible to anyone with space access)"
10460
+ ).option(
10461
+ "--no-share-results",
10462
+ "Keep this automation's result threads private to you (default)"
10463
+ ).option(
10464
+ "--result-visibility <private|team>",
10465
+ "Result-thread visibility (private|team); overrides --share-results"
9511
10466
  ).option("--paused", "Create paused");
9512
10467
  addSpaceOption(
9513
10468
  automationsCreateCommand,
@@ -9529,6 +10484,7 @@ automationsCreateCommand.option("--json", "Output JSON").action(async (opts) =>
9529
10484
  runtimeMode: opts.runtimeMode,
9530
10485
  runtimeProvider: opts.runtimeProvider,
9531
10486
  silentWhenNothingToReport: Boolean(opts.silentWhenNothingToReport),
10487
+ resultVisibility: resolveResultVisibilityFlag(opts),
9532
10488
  paused: Boolean(opts.paused),
9533
10489
  project: resolveSpaceIdOption(opts),
9534
10490
  controllerUrl: opts.serverUrl,
@@ -9536,7 +10492,53 @@ automationsCreateCommand.option("--json", "Output JSON").action(async (opts) =>
9536
10492
  json: opts.json
9537
10493
  });
9538
10494
  } catch (error) {
9539
- console.error(kleur17.red(String(error)));
10495
+ console.error(kleur19.red(String(error)));
10496
+ process.exit(1);
10497
+ }
10498
+ });
10499
+ var automationsUpdateCommand = automationsCommand.command("update").description(
10500
+ "Update an automation's name, prompt, schedule, runtime settings, or result-sharing visibility in place"
10501
+ ).argument("<automation-id>", "Automation UUID").option("--name <name>", "New automation name").option("--prompt <text>", "New prompt to run on schedule").option("--prompt-file <path>", "Read the new prompt from a file").option("--schedule-kind <kind>", "weekly|hourly|once").option("--run-at <datetime>", "Run time for once schedule (RFC3339 or YYYY-MM-DDTHH:MM[:SS])").option("--interval-hours <n>", "Interval hours (hourly schedule)", (value) => Number(value)).option("--days <list>", "Weekdays for weekly schedule (e.g. mo,tu,we,th,fr)").option("--time <hh:mm>", "Time for weekly schedule (24h, e.g. 09:00)").option("--timezone <tz>", "IANA timezone (e.g. America/New_York)").option("--runtime-mode <mode>", "auto|hosted|existing").option("--runtime-provider <id>", "Runtime provider id").option(
10502
+ "--silent-when-nothing-to-report",
10503
+ "Do not post a completion result or send a result notification when a successful run has no findings"
10504
+ ).option(
10505
+ "--no-silent-when-nothing-to-report",
10506
+ "Always post a completion result and send a result notification"
10507
+ ).option(
10508
+ "--share-results",
10509
+ "Share this automation's result threads with your team (visible to anyone with space access)"
10510
+ ).option(
10511
+ "--no-share-results",
10512
+ "Keep this automation's result threads private to you"
10513
+ ).option(
10514
+ "--result-visibility <private|team>",
10515
+ "Result-thread visibility (private|team); overrides --share-results"
10516
+ );
10517
+ addServerUrlOptions(automationsUpdateCommand);
10518
+ addAccessTokenOptions(automationsUpdateCommand, "Instafy access token");
10519
+ automationsUpdateCommand.option("--json", "Output JSON").action(async (automationId, opts) => {
10520
+ try {
10521
+ await automationsUpdate({
10522
+ automationId,
10523
+ name: opts.name,
10524
+ prompt: opts.prompt,
10525
+ promptFile: opts.promptFile,
10526
+ scheduleKind: opts.scheduleKind,
10527
+ runAt: opts.runAt,
10528
+ intervalHours: opts.intervalHours,
10529
+ days: opts.days,
10530
+ time: opts.time,
10531
+ timezone: opts.timezone,
10532
+ runtimeMode: opts.runtimeMode,
10533
+ runtimeProvider: opts.runtimeProvider,
10534
+ silentWhenNothingToReport: opts.silentWhenNothingToReport,
10535
+ resultVisibility: resolveResultVisibilityFlag(opts),
10536
+ controllerUrl: opts.serverUrl,
10537
+ accessToken: opts.accessToken,
10538
+ json: opts.json
10539
+ });
10540
+ } catch (error) {
10541
+ console.error(kleur19.red(String(error)));
9540
10542
  process.exit(1);
9541
10543
  }
9542
10544
  });
@@ -9553,7 +10555,7 @@ automationsPauseCommand.option("--json", "Output JSON").action(async (automation
9553
10555
  json: opts.json
9554
10556
  });
9555
10557
  } catch (error) {
9556
- console.error(kleur17.red(String(error)));
10558
+ console.error(kleur19.red(String(error)));
9557
10559
  process.exit(1);
9558
10560
  }
9559
10561
  });
@@ -9570,7 +10572,7 @@ automationsResumeCommand.option("--json", "Output JSON").action(async (automatio
9570
10572
  json: opts.json
9571
10573
  });
9572
10574
  } catch (error) {
9573
- console.error(kleur17.red(String(error)));
10575
+ console.error(kleur19.red(String(error)));
9574
10576
  process.exit(1);
9575
10577
  }
9576
10578
  });
@@ -9586,7 +10588,7 @@ automationsRunCommand.option("--json", "Output JSON").action(async (automationId
9586
10588
  json: opts.json
9587
10589
  });
9588
10590
  } catch (error) {
9589
- console.error(kleur17.red(String(error)));
10591
+ console.error(kleur19.red(String(error)));
9590
10592
  process.exit(1);
9591
10593
  }
9592
10594
  });
@@ -9602,7 +10604,92 @@ automationsDeleteCommand.option("--json", "Output JSON").action(async (automatio
9602
10604
  json: opts.json
9603
10605
  });
9604
10606
  } catch (error) {
9605
- console.error(kleur17.red(String(error)));
10607
+ console.error(kleur19.red(String(error)));
10608
+ process.exit(1);
10609
+ }
10610
+ });
10611
+ var credentialsCommand = program.command("credentials").description("Inspect, verify and pick the AI provider credentials your jobs use");
10612
+ failWithGroupHelp(credentialsCommand);
10613
+ var credentialsListCommand = credentialsCommand.command("list").description("List your AI credentials (secret material is never shown)").option("--all", "Include revoked credentials");
10614
+ addServerUrlOptions(credentialsListCommand);
10615
+ addAccessTokenOptions(credentialsListCommand, "Instafy access token");
10616
+ credentialsListCommand.option("--json", "Output JSON").action(async (opts) => {
10617
+ try {
10618
+ await credentialsList({
10619
+ all: Boolean(opts.all),
10620
+ controllerUrl: opts.serverUrl,
10621
+ accessToken: opts.accessToken,
10622
+ json: opts.json
10623
+ });
10624
+ } catch (error) {
10625
+ console.error(kleur19.red(String(error)));
10626
+ process.exit(1);
10627
+ }
10628
+ });
10629
+ var credentialsTestCommand = credentialsCommand.command("test").description("Probe one credential through the proxy against its upstream provider (exit 1 on failure)").argument("<id-or-prefix>", "Credential UUID or unique id prefix");
10630
+ addServerUrlOptions(credentialsTestCommand);
10631
+ addAccessTokenOptions(credentialsTestCommand, "Instafy access token");
10632
+ credentialsTestCommand.option("--json", "Output JSON").action(async (idOrPrefix, opts) => {
10633
+ try {
10634
+ const result = await credentialsTest({
10635
+ idOrPrefix,
10636
+ controllerUrl: opts.serverUrl,
10637
+ accessToken: opts.accessToken,
10638
+ json: opts.json
10639
+ });
10640
+ if (!result.ok) {
10641
+ process.exitCode = 1;
10642
+ }
10643
+ } catch (error) {
10644
+ console.error(kleur19.red(String(error)));
10645
+ process.exit(1);
10646
+ }
10647
+ });
10648
+ var credentialsDefaultCommand = credentialsCommand.command("default").description("Set the default credential jobs use, or clear it with --clear").argument("[id-or-prefix]", "Credential UUID or unique id prefix").option("--clear", "Clear the default credential instead of setting one");
10649
+ addServerUrlOptions(credentialsDefaultCommand);
10650
+ addAccessTokenOptions(credentialsDefaultCommand, "Instafy access token");
10651
+ credentialsDefaultCommand.option("--json", "Output JSON").action(async (idOrPrefix, opts) => {
10652
+ try {
10653
+ const target = pickTrimmedString2(idOrPrefix);
10654
+ if (opts.clear && target) {
10655
+ throw new Error("Pass either a credential id or --clear, not both.");
10656
+ }
10657
+ if (!opts.clear && !target) {
10658
+ throw new Error("Provide a credential id (or unique prefix), or pass --clear.");
10659
+ }
10660
+ if (opts.clear) {
10661
+ await credentialsClearDefault({
10662
+ controllerUrl: opts.serverUrl,
10663
+ accessToken: opts.accessToken,
10664
+ json: opts.json
10665
+ });
10666
+ return;
10667
+ }
10668
+ await credentialsSetDefault({
10669
+ idOrPrefix: target,
10670
+ controllerUrl: opts.serverUrl,
10671
+ accessToken: opts.accessToken,
10672
+ json: opts.json
10673
+ });
10674
+ } catch (error) {
10675
+ console.error(kleur19.red(String(error)));
10676
+ process.exit(1);
10677
+ }
10678
+ });
10679
+ var credentialsRevokeCommand = credentialsCommand.command("revoke").description("Revoke a credential (asks for confirmation unless --yes)").argument("<id-or-prefix>", "Credential UUID or unique id prefix").option("--yes", "Skip the confirmation prompt (required when not running in a terminal)");
10680
+ addServerUrlOptions(credentialsRevokeCommand);
10681
+ addAccessTokenOptions(credentialsRevokeCommand, "Instafy access token");
10682
+ credentialsRevokeCommand.option("--json", "Output JSON").action(async (idOrPrefix, opts) => {
10683
+ try {
10684
+ await credentialsRevoke({
10685
+ idOrPrefix,
10686
+ yes: Boolean(opts.yes),
10687
+ controllerUrl: opts.serverUrl,
10688
+ accessToken: opts.accessToken,
10689
+ json: opts.json
10690
+ });
10691
+ } catch (error) {
10692
+ console.error(kleur19.red(String(error)));
9606
10693
  process.exit(1);
9607
10694
  }
9608
10695
  });
@@ -9612,7 +10699,7 @@ configCommand.command("path").description("Print the config file path").option("
9612
10699
  try {
9613
10700
  configPath({ json: opts.json });
9614
10701
  } catch (error) {
9615
- console.error(kleur17.red(String(error)));
10702
+ console.error(kleur19.red(String(error)));
9616
10703
  process.exit(1);
9617
10704
  }
9618
10705
  });
@@ -9620,7 +10707,7 @@ configCommand.command("list").description("List saved configuration").option("--
9620
10707
  try {
9621
10708
  configList({ json: opts.json });
9622
10709
  } catch (error) {
9623
- console.error(kleur17.red(String(error)));
10710
+ console.error(kleur19.red(String(error)));
9624
10711
  process.exit(1);
9625
10712
  }
9626
10713
  });
@@ -9628,7 +10715,7 @@ configCommand.command("get").description("Get a config value (controller-url, st
9628
10715
  try {
9629
10716
  configGet({ key, json: opts.json });
9630
10717
  } catch (error) {
9631
- console.error(kleur17.red(String(error)));
10718
+ console.error(kleur19.red(String(error)));
9632
10719
  process.exit(1);
9633
10720
  }
9634
10721
  });
@@ -9636,7 +10723,7 @@ configCommand.command("set").description("Set a config value (controller-url, st
9636
10723
  try {
9637
10724
  configSet({ key, value, json: opts.json });
9638
10725
  } catch (error) {
9639
- console.error(kleur17.red(String(error)));
10726
+ console.error(kleur19.red(String(error)));
9640
10727
  process.exit(1);
9641
10728
  }
9642
10729
  });
@@ -9644,7 +10731,7 @@ configCommand.command("unset").description("Unset a config value (controller-url
9644
10731
  try {
9645
10732
  configUnset({ key, json: opts.json });
9646
10733
  } catch (error) {
9647
- console.error(kleur17.red(String(error)));
10734
+ console.error(kleur19.red(String(error)));
9648
10735
  process.exit(1);
9649
10736
  }
9650
10737
  });
@@ -9664,7 +10751,7 @@ runtimeStartCommand.option("--supabase-access-token <token>", "Supabase session
9664
10751
  controllerAccessToken: opts.accessToken
9665
10752
  });
9666
10753
  } catch (error) {
9667
- console.error(kleur17.red(String(error)));
10754
+ console.error(kleur19.red(String(error)));
9668
10755
  process.exit(1);
9669
10756
  }
9670
10757
  });
@@ -9672,7 +10759,7 @@ runtimeCommand.command("status").description("Show runtime health").option("--js
9672
10759
  try {
9673
10760
  await runtimeStatus({ json: opts.json });
9674
10761
  } catch (error) {
9675
- console.error(kleur17.red(String(error)));
10762
+ console.error(kleur19.red(String(error)));
9676
10763
  process.exit(1);
9677
10764
  }
9678
10765
  });
@@ -9680,7 +10767,7 @@ runtimeCommand.command("stop").description("Stop the local Instafy runtime").opt
9680
10767
  try {
9681
10768
  await runtimeStop({ json: opts.json });
9682
10769
  } catch (error) {
9683
- console.error(kleur17.red(String(error)));
10770
+ console.error(kleur19.red(String(error)));
9684
10771
  process.exit(1);
9685
10772
  }
9686
10773
  });
@@ -9706,7 +10793,7 @@ runtimeTokenCommand.option("--runtime-id <uuid>", "Runtime ID to bind token to")
9706
10793
  json: opts.json
9707
10794
  });
9708
10795
  } catch (error) {
9709
- console.error(kleur17.red(String(error)));
10796
+ console.error(kleur19.red(String(error)));
9710
10797
  process.exit(1);
9711
10798
  }
9712
10799
  });
@@ -9728,7 +10815,7 @@ diagnosticsRuntimeEventsCommand.action(async (opts) => {
9728
10815
  accessToken: opts.accessToken
9729
10816
  });
9730
10817
  } catch (error) {
9731
- console.error(kleur17.red(String(error)));
10818
+ console.error(kleur19.red(String(error)));
9732
10819
  process.exit(1);
9733
10820
  }
9734
10821
  });
@@ -9743,7 +10830,7 @@ diagnosticsRunResultCommand.action(async (runId, opts) => {
9743
10830
  accessToken: opts.accessToken
9744
10831
  });
9745
10832
  } catch (error) {
9746
- console.error(kleur17.red(String(error)));
10833
+ console.error(kleur19.red(String(error)));
9747
10834
  process.exit(1);
9748
10835
  }
9749
10836
  });
@@ -9777,7 +10864,7 @@ supportReportCommand.action(async (summaryParts, opts) => {
9777
10864
  json: opts.json
9778
10865
  });
9779
10866
  } catch (error) {
9780
- console.error(kleur17.red(String(error)));
10867
+ console.error(kleur19.red(String(error)));
9781
10868
  process.exit(1);
9782
10869
  }
9783
10870
  });
@@ -9796,7 +10883,7 @@ supportListCommand.action(async (opts) => {
9796
10883
  json: opts.json
9797
10884
  });
9798
10885
  } catch (error) {
9799
- console.error(kleur17.red(String(error)));
10886
+ console.error(kleur19.red(String(error)));
9800
10887
  process.exit(1);
9801
10888
  }
9802
10889
  });
@@ -9812,7 +10899,7 @@ supportShowCommand.action(async (reportId, opts) => {
9812
10899
  json: opts.json
9813
10900
  });
9814
10901
  } catch (error) {
9815
- console.error(kleur17.red(String(error)));
10902
+ console.error(kleur19.red(String(error)));
9816
10903
  process.exit(1);
9817
10904
  }
9818
10905
  });
@@ -9857,19 +10944,19 @@ tunnelStartCommand.option("--no-detach", "Run in foreground until interrupted").
9857
10944
  console.log(JSON.stringify(started, null, 2));
9858
10945
  return;
9859
10946
  }
9860
- console.log(kleur17.green(`Tunnel started: ${started.url} (tunnelId=${started.tunnelId})`));
9861
- console.log(kleur17.gray(`pid=${started.pid} \xB7 port=${started.localPort}`));
9862
- console.log(kleur17.gray(`log: ${started.logFile}`));
10947
+ console.log(kleur19.green(`Tunnel started: ${started.url} (tunnelId=${started.tunnelId})`));
10948
+ console.log(kleur19.gray(`pid=${started.pid} \xB7 port=${started.localPort}`));
10949
+ console.log(kleur19.gray(`log: ${started.logFile}`));
9863
10950
  console.log("");
9864
10951
  console.log("Next:");
9865
- console.log(`- ${kleur17.cyan(`instafy tunnel list`)}`);
9866
- console.log(`- ${kleur17.cyan(`instafy tunnel logs ${started.tunnelId} --follow`)}`);
9867
- console.log(`- ${kleur17.cyan(`instafy tunnel stop ${started.tunnelId}`)}`);
10952
+ console.log(`- ${kleur19.cyan(`instafy tunnel list`)}`);
10953
+ console.log(`- ${kleur19.cyan(`instafy tunnel logs ${started.tunnelId} --follow`)}`);
10954
+ console.log(`- ${kleur19.cyan(`instafy tunnel stop ${started.tunnelId}`)}`);
9868
10955
  if (process.platform !== "win32") {
9869
- console.log(kleur17.gray(`(or) tail -n 200 -f ${started.logFile}`));
10956
+ console.log(kleur19.gray(`(or) tail -n 200 -f ${started.logFile}`));
9870
10957
  }
9871
10958
  } catch (error) {
9872
- console.error(kleur17.red(String(error)));
10959
+ console.error(kleur19.red(String(error)));
9873
10960
  process.exit(1);
9874
10961
  }
9875
10962
  });
@@ -9881,14 +10968,14 @@ tunnelCommand.command("list").description("List local tunnel sessions started by
9881
10968
  return;
9882
10969
  }
9883
10970
  if (tunnels.length === 0) {
9884
- console.log(kleur17.yellow("No tunnels found."));
10971
+ console.log(kleur19.yellow("No tunnels found."));
9885
10972
  return;
9886
10973
  }
9887
10974
  for (const tunnel of tunnels) {
9888
10975
  console.log(`${tunnel.tunnelId} \xB7 ${tunnel.url} \xB7 port=${tunnel.localPort} \xB7 pid=${tunnel.pid}`);
9889
10976
  }
9890
10977
  } catch (error) {
9891
- console.error(kleur17.red(String(error)));
10978
+ console.error(kleur19.red(String(error)));
9892
10979
  process.exit(1);
9893
10980
  }
9894
10981
  });
@@ -9904,9 +10991,9 @@ tunnelCommand.command("stop").description("Stop a local tunnel session and revok
9904
10991
  console.log(JSON.stringify(result, null, 2));
9905
10992
  return;
9906
10993
  }
9907
- console.log(kleur17.green(`Tunnel stopped: ${result.tunnelId}`));
10994
+ console.log(kleur19.green(`Tunnel stopped: ${result.tunnelId}`));
9908
10995
  } catch (error) {
9909
- console.error(kleur17.red(String(error)));
10996
+ console.error(kleur19.red(String(error)));
9910
10997
  process.exit(1);
9911
10998
  }
9912
10999
  });
@@ -9920,7 +11007,7 @@ tunnelCommand.command("logs").description("Show logs for a local tunnel session"
9920
11007
  json: Boolean(opts.json)
9921
11008
  });
9922
11009
  } catch (error) {
9923
- console.error(kleur17.red(String(error)));
11010
+ console.error(kleur19.red(String(error)));
9924
11011
  process.exit(1);
9925
11012
  }
9926
11013
  });
@@ -9936,7 +11023,158 @@ orgListCommand.option("--access-token <token>", "Instafy or Supabase access toke
9936
11023
  json: opts.json
9937
11024
  });
9938
11025
  } catch (error) {
9939
- console.error(kleur17.red(String(error)));
11026
+ console.error(kleur19.red(String(error)));
11027
+ process.exit(1);
11028
+ }
11029
+ });
11030
+ function teamScopeCommandFactory(nameAndArgs, description) {
11031
+ const command = orgCommand.command(nameAndArgs).description(description);
11032
+ addServerUrlOptions(command);
11033
+ addAccessTokenOptions(command, "Instafy access token");
11034
+ command.option("--team-id <team>", "Team id (UUID) or slug (defaults to your only team)").option("--json", "Output JSON");
11035
+ return command;
11036
+ }
11037
+ var teamMembersCommand = teamScopeCommandFactory("members", "List members of a team");
11038
+ teamMembersCommand.action(async (opts) => {
11039
+ try {
11040
+ await teamMembersList({
11041
+ teamId: opts.teamId,
11042
+ controllerUrl: opts.serverUrl,
11043
+ accessToken: opts.accessToken,
11044
+ json: opts.json
11045
+ });
11046
+ } catch (error) {
11047
+ console.error(kleur19.red(String(error)));
11048
+ process.exit(1);
11049
+ }
11050
+ });
11051
+ var teamInviteCommand = teamScopeCommandFactory(
11052
+ "invite <email>",
11053
+ "Invite a teammate by email (they must sign in with that email to accept)"
11054
+ );
11055
+ teamInviteCommand.option("--role <role>", "owner, admin, builder, or viewer (default: builder)").action(async (email, opts) => {
11056
+ try {
11057
+ await teamInvite({
11058
+ email,
11059
+ role: opts.role,
11060
+ teamId: opts.teamId,
11061
+ controllerUrl: opts.serverUrl,
11062
+ accessToken: opts.accessToken,
11063
+ json: opts.json
11064
+ });
11065
+ } catch (error) {
11066
+ console.error(kleur19.red(String(error)));
11067
+ process.exit(1);
11068
+ }
11069
+ });
11070
+ var teamInviteLinkCommand = teamScopeCommandFactory(
11071
+ "invite-link",
11072
+ "Create a shareable invite link that works with any sign-in method"
11073
+ );
11074
+ teamInviteLinkCommand.option("--role <role>", "builder or viewer (default: builder)").option("--studio-url <url>", "Studio base URL for the accept link (default: https://instafy.dev)").action(async (opts) => {
11075
+ try {
11076
+ await teamInviteLink({
11077
+ role: opts.role,
11078
+ studioUrl: opts.studioUrl,
11079
+ teamId: opts.teamId,
11080
+ controllerUrl: opts.serverUrl,
11081
+ accessToken: opts.accessToken,
11082
+ json: opts.json
11083
+ });
11084
+ } catch (error) {
11085
+ console.error(kleur19.red(String(error)));
11086
+ process.exit(1);
11087
+ }
11088
+ });
11089
+ var teamInvitesCommand = teamScopeCommandFactory(
11090
+ "invites",
11091
+ "List pending email invitations and invite links"
11092
+ );
11093
+ teamInvitesCommand.action(async (opts) => {
11094
+ try {
11095
+ await teamInvites({
11096
+ teamId: opts.teamId,
11097
+ controllerUrl: opts.serverUrl,
11098
+ accessToken: opts.accessToken,
11099
+ json: opts.json
11100
+ });
11101
+ } catch (error) {
11102
+ console.error(kleur19.red(String(error)));
11103
+ process.exit(1);
11104
+ }
11105
+ });
11106
+ var teamAddMemberCommand = teamScopeCommandFactory(
11107
+ "add-member",
11108
+ "Add an existing Instafy account to a team by user id"
11109
+ );
11110
+ teamAddMemberCommand.requiredOption("--user-id <uuid>", "User id of an existing Instafy account").option("--role <role>", "owner, admin, builder, or viewer (default: builder)").action(async (opts) => {
11111
+ try {
11112
+ await teamAddMember({
11113
+ userId: opts.userId,
11114
+ role: opts.role,
11115
+ teamId: opts.teamId,
11116
+ controllerUrl: opts.serverUrl,
11117
+ accessToken: opts.accessToken,
11118
+ json: opts.json
11119
+ });
11120
+ } catch (error) {
11121
+ console.error(kleur19.red(String(error)));
11122
+ process.exit(1);
11123
+ }
11124
+ });
11125
+ var teamAcceptCommand = teamScopeCommandFactory(
11126
+ "accept <token>",
11127
+ "Accept a team invitation or invite link with the current account"
11128
+ );
11129
+ teamAcceptCommand.action(async (token, opts) => {
11130
+ try {
11131
+ await teamAccept({
11132
+ token,
11133
+ teamId: opts.teamId,
11134
+ controllerUrl: opts.serverUrl,
11135
+ accessToken: opts.accessToken,
11136
+ json: opts.json
11137
+ });
11138
+ } catch (error) {
11139
+ console.error(kleur19.red(String(error)));
11140
+ process.exit(1);
11141
+ }
11142
+ });
11143
+ var teamRevokeInviteCommand = teamScopeCommandFactory(
11144
+ "revoke-invite <invitation-id>",
11145
+ "Revoke a pending email invitation"
11146
+ );
11147
+ teamRevokeInviteCommand.option("--yes", "Skip the confirmation prompt").action(async (invitationId, opts) => {
11148
+ try {
11149
+ await teamRevokeInvite({
11150
+ invitationId,
11151
+ yes: opts.yes,
11152
+ teamId: opts.teamId,
11153
+ controllerUrl: opts.serverUrl,
11154
+ accessToken: opts.accessToken,
11155
+ json: opts.json
11156
+ });
11157
+ } catch (error) {
11158
+ console.error(kleur19.red(String(error)));
11159
+ process.exit(1);
11160
+ }
11161
+ });
11162
+ var teamRevokeLinkCommand = teamScopeCommandFactory(
11163
+ "revoke-link <invite-link-id>",
11164
+ "Revoke a shareable invite link"
11165
+ );
11166
+ teamRevokeLinkCommand.option("--yes", "Skip the confirmation prompt").action(async (inviteLinkId, opts) => {
11167
+ try {
11168
+ await teamRevokeLink({
11169
+ inviteLinkId,
11170
+ yes: opts.yes,
11171
+ teamId: opts.teamId,
11172
+ controllerUrl: opts.serverUrl,
11173
+ accessToken: opts.accessToken,
11174
+ json: opts.json
11175
+ });
11176
+ } catch (error) {
11177
+ console.error(kleur19.red(String(error)));
9940
11178
  process.exit(1);
9941
11179
  }
9942
11180
  });
@@ -9961,17 +11199,17 @@ profileCommand.command("list").description("List saved CLI profiles").option("--
9961
11199
  return;
9962
11200
  }
9963
11201
  if (profiles.length === 0) {
9964
- console.log(kleur17.yellow("No profiles found."));
9965
- console.log(`Create one with: ${kleur17.cyan("instafy login --profile <name>")}`);
11202
+ console.log(kleur19.yellow("No profiles found."));
11203
+ console.log(`Create one with: ${kleur19.cyan("instafy login --profile <name>")}`);
9966
11204
  return;
9967
11205
  }
9968
- console.log(kleur17.green("Instafy CLI profiles"));
11206
+ console.log(kleur19.green("Instafy CLI profiles"));
9969
11207
  for (const profile of profiles) {
9970
- const token = profile.accessTokenSet ? kleur17.green("token") : kleur17.yellow("no-token");
9971
- console.log(`- ${kleur17.cyan(profile.name)} (${token})`);
11208
+ const token = profile.accessTokenSet ? kleur19.green("token") : kleur19.yellow("no-token");
11209
+ console.log(`- ${kleur19.cyan(profile.name)} (${token})`);
9972
11210
  }
9973
11211
  } catch (error) {
9974
- console.error(kleur17.red(String(error)));
11212
+ console.error(kleur19.red(String(error)));
9975
11213
  process.exit(1);
9976
11214
  }
9977
11215
  });
@@ -9991,7 +11229,7 @@ historyMessagesCommand.option("--limit <n>", "Max messages to return (1-200, def
9991
11229
  pretty: opts.pretty
9992
11230
  });
9993
11231
  } catch (error) {
9994
- console.error(kleur17.red(String(error)));
11232
+ console.error(kleur19.red(String(error)));
9995
11233
  process.exit(1);
9996
11234
  }
9997
11235
  });
@@ -10008,7 +11246,7 @@ historyRunsCommand.option("--limit <n>", "Max runs to return (1-200, default: 50
10008
11246
  pretty: opts.pretty
10009
11247
  });
10010
11248
  } catch (error) {
10011
- console.error(kleur17.red(String(error)));
11249
+ console.error(kleur19.red(String(error)));
10012
11250
  process.exit(1);
10013
11251
  }
10014
11252
  });
@@ -10025,7 +11263,7 @@ historyConversationsCommand.option("--limit <n>", "Max conversations to return (
10025
11263
  pretty: opts.pretty
10026
11264
  });
10027
11265
  } catch (error) {
10028
- console.error(kleur17.red(String(error)));
11266
+ console.error(kleur19.red(String(error)));
10029
11267
  process.exit(1);
10030
11268
  }
10031
11269
  });
@@ -10046,7 +11284,7 @@ conversationCreateCommand.action(async (opts) => {
10046
11284
  json: opts.json
10047
11285
  });
10048
11286
  } catch (error) {
10049
- console.error(kleur17.red(String(error)));
11287
+ console.error(kleur19.red(String(error)));
10050
11288
  process.exit(1);
10051
11289
  }
10052
11290
  });
@@ -10064,7 +11302,7 @@ conversationListCommand.action(async (opts) => {
10064
11302
  json: opts.json
10065
11303
  });
10066
11304
  } catch (error) {
10067
- console.error(kleur17.red(String(error)));
11305
+ console.error(kleur19.red(String(error)));
10068
11306
  process.exit(1);
10069
11307
  }
10070
11308
  });
@@ -10083,7 +11321,7 @@ conversationSearchCommand.action(async (queryParts, opts) => {
10083
11321
  json: opts.json
10084
11322
  });
10085
11323
  } catch (error) {
10086
- console.error(kleur17.red(String(error)));
11324
+ console.error(kleur19.red(String(error)));
10087
11325
  process.exit(1);
10088
11326
  }
10089
11327
  });
@@ -10102,7 +11340,7 @@ conversationShowCommand.action(async (targetParts, opts) => {
10102
11340
  json: opts.json
10103
11341
  });
10104
11342
  } catch (error) {
10105
- console.error(kleur17.red(String(error)));
11343
+ console.error(kleur19.red(String(error)));
10106
11344
  process.exit(1);
10107
11345
  }
10108
11346
  });