@instafy/cli 0.2.0 → 0.2.1

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 +4 -0
  2. package/dist/cli.js +1537 -300
  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
 
@@ -7071,7 +7570,7 @@ async function chatPrompt(options) {
7071
7570
 
7072
7571
  // src/conversations.ts
7073
7572
  init_project_manifest();
7074
- import kleur11 from "kleur";
7573
+ import kleur12 from "kleur";
7075
7574
  var DEFAULT_CONVERSATION_LIMIT = 50;
7076
7575
  var DEFAULT_MESSAGE_LIMIT = 80;
7077
7576
  var MAX_LIMIT = 200;
@@ -7298,7 +7797,7 @@ async function buildSearchMatches(conversations, options) {
7298
7797
  }));
7299
7798
  }
7300
7799
  function formatConversationMatch(match) {
7301
- const lines = [`- ${kleur11.green(match.title)} (${match.id})`];
7800
+ const lines = [`- ${kleur12.green(match.title)} (${match.id})`];
7302
7801
  const details = [];
7303
7802
  if (match.matchedIn.length > 0) {
7304
7803
  details.push(`match: ${match.matchedIn.join(", ")}`);
@@ -7310,7 +7809,7 @@ function formatConversationMatch(match) {
7310
7809
  lines.push(` ${details.join(" \xB7 ")}`);
7311
7810
  }
7312
7811
  if (match.preview) {
7313
- lines.push(` ${kleur11.gray(summarizeContent(match.preview))}`);
7812
+ lines.push(` ${kleur12.gray(summarizeContent(match.preview))}`);
7314
7813
  }
7315
7814
  return lines.join("\n");
7316
7815
  }
@@ -7369,16 +7868,16 @@ async function createConversation(options) {
7369
7868
  console.log(JSON.stringify(payload, null, 2));
7370
7869
  return;
7371
7870
  }
7372
- console.log(kleur11.green("Created conversation"));
7373
- console.log(`${kleur11.gray("ID:")} ${conversationId}`);
7871
+ console.log(kleur12.green("Created conversation"));
7872
+ console.log(`${kleur12.gray("ID:")} ${conversationId}`);
7374
7873
  if (title) {
7375
- console.log(`${kleur11.gray("Title:")} ${title}`);
7874
+ console.log(`${kleur12.gray("Title:")} ${title}`);
7376
7875
  }
7377
7876
  if (parentConversationId) {
7378
- console.log(`${kleur11.gray("Parent:")} ${parentConversationId}`);
7877
+ console.log(`${kleur12.gray("Parent:")} ${parentConversationId}`);
7379
7878
  }
7380
7879
  if (threadKind) {
7381
- console.log(`${kleur11.gray("Thread kind:")} ${threadKind}`);
7880
+ console.log(`${kleur12.gray("Thread kind:")} ${threadKind}`);
7382
7881
  }
7383
7882
  }
7384
7883
  async function listConversations(options) {
@@ -7405,10 +7904,10 @@ async function listConversations(options) {
7405
7904
  return;
7406
7905
  }
7407
7906
  if (items.length === 0) {
7408
- console.log(kleur11.yellow("No conversations found for this space."));
7907
+ console.log(kleur12.yellow("No conversations found for this space."));
7409
7908
  return;
7410
7909
  }
7411
- console.log(kleur11.green(`Conversations (${items.length})`));
7910
+ console.log(kleur12.green(`Conversations (${items.length})`));
7412
7911
  for (const item of items) {
7413
7912
  console.log(formatConversationMatch({ ...item, score: 0, matchedIn: [] }));
7414
7913
  }
@@ -7430,10 +7929,10 @@ async function searchConversations(options) {
7430
7929
  return matches;
7431
7930
  }
7432
7931
  if (matches.length === 0) {
7433
- console.log(kleur11.yellow(`No conversations matched "${options.query}".`));
7932
+ console.log(kleur12.yellow(`No conversations matched "${options.query}".`));
7434
7933
  return matches;
7435
7934
  }
7436
- console.log(kleur11.green(`Matches for "${options.query}"`));
7935
+ console.log(kleur12.green(`Matches for "${options.query}"`));
7437
7936
  for (const match of matches.slice(0, Math.min(limit, matches.length))) {
7438
7937
  console.log(formatConversationMatch(match));
7439
7938
  }
@@ -7517,35 +8016,35 @@ async function showConversation(options) {
7517
8016
  );
7518
8017
  return;
7519
8018
  }
7520
- console.log(kleur11.green(selected.title));
7521
- console.log(`${kleur11.gray("ID:")} ${selected.id}`);
8019
+ console.log(kleur12.green(selected.title));
8020
+ console.log(`${kleur12.gray("ID:")} ${selected.id}`);
7522
8021
  if (selected.updatedAt) {
7523
- console.log(`${kleur11.gray("Updated:")} ${selected.updatedAt}`);
8022
+ console.log(`${kleur12.gray("Updated:")} ${selected.updatedAt}`);
7524
8023
  }
7525
8024
  if (selected.preview) {
7526
- console.log(`${kleur11.gray("Preview:")} ${summarizeContent(selected.preview, 240)}`);
8025
+ console.log(`${kleur12.gray("Preview:")} ${summarizeContent(selected.preview, 240)}`);
7527
8026
  }
7528
8027
  console.log("");
7529
8028
  if (messages.length === 0) {
7530
- console.log(kleur11.yellow("No messages found in this conversation."));
8029
+ console.log(kleur12.yellow("No messages found in this conversation."));
7531
8030
  return;
7532
8031
  }
7533
8032
  for (const message of messages.slice().reverse()) {
7534
8033
  const role = (message.role ?? "unknown").trim().toLowerCase();
7535
8034
  const createdAt = trimOrNull2(message.createdAt) ?? trimOrNull2(message.created_at);
7536
8035
  const header = createdAt ? `${role} \xB7 ${createdAt}` : role;
7537
- console.log(kleur11.cyan(`[${header}]`));
8036
+ console.log(kleur12.cyan(`[${header}]`));
7538
8037
  console.log(summarizeContent(message.content ?? "", 4e3));
7539
8038
  console.log("");
7540
8039
  }
7541
8040
  }
7542
8041
 
7543
8042
  // src/invitations.ts
7544
- import kleur12 from "kleur";
7545
- import path10 from "node:path";
8043
+ import kleur13 from "kleur";
8044
+ import path11 from "node:path";
7546
8045
  init_project_manifest();
7547
8046
  function resolveStartDir(rawPath) {
7548
- return path10.resolve(rawPath ?? process.cwd());
8047
+ return path11.resolve(rawPath ?? process.cwd());
7549
8048
  }
7550
8049
  function trimOrNull3(value) {
7551
8050
  const trimmed = value?.trim() ?? "";
@@ -7687,7 +8186,7 @@ async function inviteSpaceMember(options) {
7687
8186
  return normalized;
7688
8187
  }
7689
8188
  const scopeLabel = orgName ? `${orgName}` : "this space";
7690
- console.log(kleur12.green(`Invitation sent to ${normalized.email}`));
8189
+ console.log(kleur13.green(`Invitation sent to ${normalized.email}`));
7691
8190
  console.log(`Scope: ${scopeLabel}`);
7692
8191
  console.log(`Role: ${normalized.role}`);
7693
8192
  if (normalized.expiresAt) {
@@ -7747,7 +8246,7 @@ async function setSpaceMemberRole(options) {
7747
8246
  return normalized;
7748
8247
  }
7749
8248
  console.log(
7750
- kleur12.green(
8249
+ kleur13.green(
7751
8250
  normalized.status === "updated" ? `Updated ${normalized.email} to ${normalized.role}` : `${normalized.email} already has role ${normalized.role}`
7752
8251
  )
7753
8252
  );
@@ -7786,7 +8285,7 @@ async function setSpaceMemberRole(options) {
7786
8285
  return normalized;
7787
8286
  }
7788
8287
  console.log(
7789
- kleur12.green(
8288
+ kleur13.green(
7790
8289
  normalized.status === "updated" ? `Updated pending invite for ${normalized.email} to ${normalized.role}` : `Pending invite for ${normalized.email} already has role ${normalized.role}`
7791
8290
  )
7792
8291
  );
@@ -7832,14 +8331,446 @@ async function sendSpaceInvitation(options) {
7832
8331
  };
7833
8332
  }
7834
8333
 
8334
+ // src/team.ts
8335
+ init_config();
8336
+ init_controller_fetch();
8337
+ init_errors();
8338
+ import kleur14 from "kleur";
8339
+ var DEFAULT_STUDIO_URL = "https://instafy.dev";
8340
+ var ORG_ROLES = ["owner", "admin", "builder", "viewer"];
8341
+ var INVITE_LINK_ROLES = ["builder", "viewer"];
8342
+ var UUID_PATTERN3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8343
+ var ControllerRequestError = class extends Error {
8344
+ constructor(status, serverMessage, message) {
8345
+ super(message);
8346
+ this.name = "ControllerRequestError";
8347
+ this.status = status;
8348
+ this.serverMessage = serverMessage;
8349
+ }
8350
+ };
8351
+ function normalizeBaseUrl(value) {
8352
+ const trimmed = typeof value === "string" ? value.trim() : "";
8353
+ if (!trimmed)
8354
+ return null;
8355
+ return trimmed.replace(/\/$/, "");
8356
+ }
8357
+ function isUuid3(value) {
8358
+ return UUID_PATTERN3.test(value.trim());
8359
+ }
8360
+ function requireUuid(value, label) {
8361
+ const trimmed = value?.trim() ?? "";
8362
+ if (!trimmed) {
8363
+ throw new Error(`${label} is required.`);
8364
+ }
8365
+ if (!isUuid3(trimmed)) {
8366
+ throw new Error(`${label} must be a UUID.`);
8367
+ }
8368
+ return trimmed;
8369
+ }
8370
+ function normalizeRole2(value, allowed, label) {
8371
+ const resolved = (value ?? "").trim().toLowerCase();
8372
+ if (!resolved) {
8373
+ throw new Error(`${label} is required.`);
8374
+ }
8375
+ if (!allowed.includes(resolved)) {
8376
+ throw new Error(`${label} must be one of ${allowed.join(", ")}.`);
8377
+ }
8378
+ return resolved;
8379
+ }
8380
+ function resolveControllerAuth4(options, retryCommand) {
8381
+ const cwd = options.cwd ?? process.cwd();
8382
+ const controllerUrl = resolveControllerUrl({
8383
+ controllerUrl: options.controllerUrl ?? null,
8384
+ cwd
8385
+ });
8386
+ const resolved = resolveUserAccessTokenWithSource({
8387
+ accessToken: options.accessToken ?? null,
8388
+ cwd
8389
+ });
8390
+ if (!resolved.token) {
8391
+ throw formatAuthRequiredError({
8392
+ retryCommand,
8393
+ advancedHint: "pass --access-token, or set INSTAFY_ACCESS_TOKEN / SUPABASE_ACCESS_TOKEN"
8394
+ });
8395
+ }
8396
+ return {
8397
+ controllerUrl,
8398
+ accessToken: resolved.token,
8399
+ tokenSource: resolved.source,
8400
+ profile: resolved.profile,
8401
+ cwd
8402
+ };
8403
+ }
8404
+ async function controllerJson(auth, retryCommand, params) {
8405
+ const url = `${auth.controllerUrl.replace(/\/$/, "")}${params.path}`;
8406
+ const headers = new Headers();
8407
+ headers.set("accept", "application/json");
8408
+ let bodyText;
8409
+ if (params.body !== void 0) {
8410
+ headers.set("content-type", "application/json");
8411
+ bodyText = JSON.stringify(params.body);
8412
+ }
8413
+ const response = (await fetchWithControllerAuth({
8414
+ url,
8415
+ init: {
8416
+ method: params.method,
8417
+ headers,
8418
+ body: bodyText
8419
+ },
8420
+ accessToken: auth.accessToken,
8421
+ tokenSource: auth.tokenSource,
8422
+ profile: auth.profile,
8423
+ cwd: auth.cwd
8424
+ })).response;
8425
+ const text = await response.text().catch(() => "");
8426
+ if (response.status === 401 || response.status === 403) {
8427
+ throw formatAuthRejectedError({
8428
+ status: response.status,
8429
+ responseBody: text,
8430
+ retryCommand,
8431
+ advancedHint: "run `instafy login`, or pass --access-token"
8432
+ });
8433
+ }
8434
+ if (!response.ok) {
8435
+ const serverMessage = extractControllerErrorMessage(text);
8436
+ throw new ControllerRequestError(
8437
+ response.status,
8438
+ serverMessage,
8439
+ `Request failed (${response.status} ${response.statusText})${serverMessage ? `: ${serverMessage}` : ""}`
8440
+ );
8441
+ }
8442
+ return text ? JSON.parse(text) : null;
8443
+ }
8444
+ async function fetchOrgs(auth) {
8445
+ const payload = await controllerJson(auth, "instafy team list", {
8446
+ method: "GET",
8447
+ path: "/orgs"
8448
+ });
8449
+ return Array.isArray(payload?.orgs) ? payload.orgs : [];
8450
+ }
8451
+ async function resolveTeamId(auth, teamOpt) {
8452
+ const raw = teamOpt?.trim();
8453
+ if (raw && isUuid3(raw)) {
8454
+ return raw;
8455
+ }
8456
+ const orgs = await fetchOrgs(auth);
8457
+ if (raw) {
8458
+ const bySlug = orgs.find(
8459
+ (org) => (org.slug ?? "").toLowerCase() === raw.toLowerCase()
8460
+ );
8461
+ if (bySlug)
8462
+ return bySlug.id;
8463
+ const byId = orgs.find((org) => org.id === raw);
8464
+ if (byId)
8465
+ return byId.id;
8466
+ throw new Error(
8467
+ `No team matches "${raw}". Run \`instafy team list\` to see the teams you belong to.`
8468
+ );
8469
+ }
8470
+ if (orgs.length === 1) {
8471
+ return orgs[0].id;
8472
+ }
8473
+ if (orgs.length === 0) {
8474
+ throw new Error(
8475
+ "No teams found for this account. Run `instafy team list` to see your teams."
8476
+ );
8477
+ }
8478
+ throw new Error(
8479
+ "Multiple teams found. Pass --team-id <uuid|slug>. Run `instafy team list` to see the options."
8480
+ );
8481
+ }
8482
+ function resolveStudioBaseUrl(auth, studioUrlOption) {
8483
+ return normalizeBaseUrl(studioUrlOption ?? null) ?? normalizeBaseUrl(process.env["INSTAFY_STUDIO_URL"] ?? null) ?? normalizeBaseUrl(
8484
+ resolveConfiguredStudioUrl({ profile: auth.profile, cwd: auth.cwd })
8485
+ ) ?? DEFAULT_STUDIO_URL;
8486
+ }
8487
+ function shortId(value) {
8488
+ const trimmed = value.trim();
8489
+ return trimmed.length > 8 ? trimmed.slice(0, 8) : trimmed;
8490
+ }
8491
+ function printTable(headers, rows) {
8492
+ const widths = headers.map(
8493
+ (header, index) => Math.max(header.length, ...rows.map((row) => (row[index] ?? "").length))
8494
+ );
8495
+ const format = (cells) => cells.map((cell, index) => (cell ?? "").padEnd(widths[index])).join(" ").trimEnd();
8496
+ console.log(kleur14.bold(format(headers)));
8497
+ for (const row of rows) {
8498
+ console.log(format(row));
8499
+ }
8500
+ }
8501
+ async function teamMembersList(options) {
8502
+ const auth = resolveControllerAuth4(options, "instafy team members");
8503
+ const orgId = await resolveTeamId(auth, options.teamId);
8504
+ const payload = await controllerJson(auth, "instafy team members", {
8505
+ method: "GET",
8506
+ path: `/orgs/${encodeURIComponent(orgId)}/members`
8507
+ });
8508
+ const members = Array.isArray(payload?.members) ? payload.members : [];
8509
+ if (options.json) {
8510
+ console.log(
8511
+ JSON.stringify(
8512
+ {
8513
+ members,
8514
+ nextCursor: payload?.nextCursor ?? null,
8515
+ hasMore: payload?.hasMore ?? false,
8516
+ total: payload?.total
8517
+ },
8518
+ null,
8519
+ 2
8520
+ )
8521
+ );
8522
+ return;
8523
+ }
8524
+ if (members.length === 0) {
8525
+ console.log(kleur14.gray("No members."));
8526
+ return;
8527
+ }
8528
+ const rows = members.map((member) => [
8529
+ shortId(member.userId),
8530
+ (member.email ?? "").trim() || "-",
8531
+ (member.fullName ?? "").trim() || "-",
8532
+ member.role,
8533
+ (member.createdAt ?? "").trim() || "-"
8534
+ ]);
8535
+ printTable(["ID", "EMAIL", "NAME", "ROLE", "JOINED"], rows);
8536
+ }
8537
+ async function teamInvite(options) {
8538
+ const auth = resolveControllerAuth4(options, "instafy team invite");
8539
+ const email = options.email?.trim() ?? "";
8540
+ if (!email) {
8541
+ throw new Error("An email address is required.");
8542
+ }
8543
+ const role = options.role !== void 0 ? normalizeRole2(options.role, ORG_ROLES, "--role") : void 0;
8544
+ const orgId = await resolveTeamId(auth, options.teamId);
8545
+ const body = { email };
8546
+ if (role)
8547
+ body.role = role;
8548
+ let payload;
8549
+ try {
8550
+ payload = await controllerJson(auth, "instafy team invite", {
8551
+ method: "POST",
8552
+ path: `/orgs/${encodeURIComponent(orgId)}/invitations`,
8553
+ body
8554
+ });
8555
+ } catch (error) {
8556
+ if (error instanceof ControllerRequestError && error.status >= 400 && error.status < 500 && /already a member|already exists|already has access/i.test(
8557
+ error.serverMessage ?? ""
8558
+ )) {
8559
+ throw new Error(
8560
+ `${error.serverMessage}
8561
+
8562
+ If they already have an Instafy account, add them directly with:
8563
+ instafy team add-member --user-id <uuid>${options.teamId ? ` --team-id ${options.teamId}` : ""}`
8564
+ );
8565
+ }
8566
+ throw error;
8567
+ }
8568
+ const invitation = payload?.invitation;
8569
+ if (!invitation?.id) {
8570
+ throw new Error("Invitation response missing invitation details.");
8571
+ }
8572
+ if (options.json) {
8573
+ console.log(
8574
+ JSON.stringify(
8575
+ { invitation, acceptUrl: payload?.acceptUrl ?? null },
8576
+ null,
8577
+ 2
8578
+ )
8579
+ );
8580
+ return;
8581
+ }
8582
+ console.log(kleur14.green(`Invitation sent to ${invitation.email}`));
8583
+ console.log(`Role: ${invitation.role}`);
8584
+ if (invitation.expiresAt) {
8585
+ console.log(`Expires: ${invitation.expiresAt}`);
8586
+ }
8587
+ console.log("");
8588
+ console.log(
8589
+ `${invitation.email} must sign in to Instafy with this email address to accept the invitation.`
8590
+ );
8591
+ }
8592
+ async function teamInviteLink(options) {
8593
+ const auth = resolveControllerAuth4(options, "instafy team invite-link");
8594
+ const role = options.role !== void 0 ? normalizeRole2(options.role, INVITE_LINK_ROLES, "--role") : void 0;
8595
+ const orgId = await resolveTeamId(auth, options.teamId);
8596
+ const body = {};
8597
+ if (role)
8598
+ body.role = role;
8599
+ const payload = await controllerJson(auth, "instafy team invite-link", {
8600
+ method: "POST",
8601
+ path: `/orgs/${encodeURIComponent(orgId)}/invite-links`,
8602
+ body
8603
+ });
8604
+ const inviteLink = payload?.inviteLink;
8605
+ if (!inviteLink?.token || !inviteLink.acceptPath) {
8606
+ throw new Error("Invite link response missing link details.");
8607
+ }
8608
+ const studioBase = resolveStudioBaseUrl(auth, options.studioUrl);
8609
+ const acceptUrl = typeof payload?.acceptUrl === "string" && payload.acceptUrl.trim() || `${studioBase}${inviteLink.acceptPath}`;
8610
+ if (options.json) {
8611
+ console.log(JSON.stringify({ inviteLink, acceptUrl }, null, 2));
8612
+ return;
8613
+ }
8614
+ console.log(kleur14.green("Shareable invite link created."));
8615
+ console.log(`Role: ${inviteLink.role}`);
8616
+ console.log(`Link: ${acceptUrl}`);
8617
+ console.log(`Token: ${inviteLink.token}`);
8618
+ if (inviteLink.expiresAt) {
8619
+ console.log(`Expires: ${inviteLink.expiresAt}`);
8620
+ }
8621
+ console.log("");
8622
+ console.log(
8623
+ "Anyone who opens this link and signs in (with any sign-in method) can join the team. Treat it like a shared secret."
8624
+ );
8625
+ }
8626
+ async function teamInvites(options) {
8627
+ const auth = resolveControllerAuth4(options, "instafy team invites");
8628
+ const orgId = await resolveTeamId(auth, options.teamId);
8629
+ const [invitationsPayload, linksPayload] = await Promise.all([
8630
+ controllerJson(auth, "instafy team invites", {
8631
+ method: "GET",
8632
+ path: `/orgs/${encodeURIComponent(orgId)}/invitations`
8633
+ }),
8634
+ controllerJson(auth, "instafy team invites", {
8635
+ method: "GET",
8636
+ path: `/orgs/${encodeURIComponent(orgId)}/invite-links`
8637
+ })
8638
+ ]);
8639
+ const invitations = Array.isArray(invitationsPayload?.invitations) ? invitationsPayload.invitations : [];
8640
+ const inviteLinks = Array.isArray(linksPayload?.inviteLinks) ? linksPayload.inviteLinks : [];
8641
+ if (options.json) {
8642
+ console.log(JSON.stringify({ invitations, inviteLinks }, null, 2));
8643
+ return;
8644
+ }
8645
+ if (invitations.length === 0 && inviteLinks.length === 0) {
8646
+ console.log(kleur14.gray("No pending invitations or invite links."));
8647
+ return;
8648
+ }
8649
+ if (invitations.length > 0) {
8650
+ console.log(kleur14.bold("Email invitations"));
8651
+ const rows = invitations.map((invitation) => [
8652
+ shortId(invitation.id),
8653
+ invitation.email,
8654
+ invitation.role,
8655
+ (invitation.expiresAt ?? "").trim() || "-"
8656
+ ]);
8657
+ printTable(["ID", "EMAIL", "ROLE", "EXPIRES"], rows);
8658
+ }
8659
+ if (inviteLinks.length > 0) {
8660
+ if (invitations.length > 0)
8661
+ console.log("");
8662
+ console.log(kleur14.bold("Invite links (shareable, grant join on sign-in)"));
8663
+ const rows = inviteLinks.map((link) => [
8664
+ shortId(link.id),
8665
+ link.role,
8666
+ (link.status ?? "").trim() || "-",
8667
+ (link.expiresAt ?? "").trim() || "-"
8668
+ ]);
8669
+ printTable(["ID", "ROLE", "STATUS", "EXPIRES"], rows);
8670
+ }
8671
+ }
8672
+ async function teamAddMember(options) {
8673
+ const auth = resolveControllerAuth4(options, "instafy team add-member");
8674
+ const userId = requireUuid(options.userId, "--user-id");
8675
+ const role = options.role !== void 0 ? normalizeRole2(options.role, ORG_ROLES, "--role") : void 0;
8676
+ const orgId = await resolveTeamId(auth, options.teamId);
8677
+ const body = { userId };
8678
+ if (role)
8679
+ body.role = role;
8680
+ const payload = await controllerJson(auth, "instafy team add-member", {
8681
+ method: "POST",
8682
+ path: `/orgs/${encodeURIComponent(orgId)}/members`,
8683
+ body
8684
+ });
8685
+ const member = payload?.member;
8686
+ if (!member?.userId) {
8687
+ throw new Error("Add member response missing member details.");
8688
+ }
8689
+ if (options.json) {
8690
+ console.log(JSON.stringify({ member }, null, 2));
8691
+ return;
8692
+ }
8693
+ const label = (member.email ?? "").trim() || member.userId;
8694
+ console.log(kleur14.green(`Added ${label} as ${member.role}.`));
8695
+ }
8696
+ async function teamAccept(options) {
8697
+ const auth = resolveControllerAuth4(options, "instafy team accept");
8698
+ const token = requireUuid(options.token, "token");
8699
+ const payload = await controllerJson(auth, "instafy team accept", {
8700
+ method: "POST",
8701
+ path: "/org-invitations/accept",
8702
+ body: { token }
8703
+ });
8704
+ if (options.json) {
8705
+ console.log(JSON.stringify(payload ?? {}, null, 2));
8706
+ return;
8707
+ }
8708
+ const name = (payload?.orgName ?? "").trim() || payload?.orgSlug || payload?.orgId || "team";
8709
+ console.log(kleur14.green(`Joined ${name} as ${payload?.role ?? "member"}.`));
8710
+ }
8711
+ async function confirmDestructive(message, yes, json) {
8712
+ if (yes)
8713
+ return;
8714
+ const input3 = process.stdin;
8715
+ const interactive = Boolean(
8716
+ input3.isTTY && process.stdout.isTTY && json !== true && process.env["CI"] !== "true"
8717
+ );
8718
+ if (!interactive) {
8719
+ throw new Error("Refusing to continue without confirmation. Re-run with --yes.");
8720
+ }
8721
+ const { confirm, isCancel } = await import("@clack/prompts");
8722
+ const proceed = await confirm({ message, initialValue: false });
8723
+ if (isCancel(proceed) || !proceed) {
8724
+ throw new Error("Cancelled.");
8725
+ }
8726
+ }
8727
+ async function teamRevokeInvite(options) {
8728
+ const auth = resolveControllerAuth4(options, "instafy team revoke-invite");
8729
+ const invitationId = requireUuid(options.invitationId, "invitation id");
8730
+ const orgId = await resolveTeamId(auth, options.teamId);
8731
+ await confirmDestructive(
8732
+ `Revoke pending invitation ${shortId(invitationId)}?`,
8733
+ options.yes,
8734
+ options.json
8735
+ );
8736
+ await controllerJson(auth, "instafy team revoke-invite", {
8737
+ method: "DELETE",
8738
+ path: `/orgs/${encodeURIComponent(orgId)}/invitations/${encodeURIComponent(invitationId)}`
8739
+ });
8740
+ if (options.json) {
8741
+ console.log(JSON.stringify({ ok: true, invitationId }, null, 2));
8742
+ return;
8743
+ }
8744
+ console.log(kleur14.green(`Revoked invitation ${invitationId}.`));
8745
+ }
8746
+ async function teamRevokeLink(options) {
8747
+ const auth = resolveControllerAuth4(options, "instafy team revoke-link");
8748
+ const inviteLinkId = requireUuid(options.inviteLinkId, "invite link id");
8749
+ const orgId = await resolveTeamId(auth, options.teamId);
8750
+ await confirmDestructive(
8751
+ `Revoke invite link ${shortId(inviteLinkId)}?`,
8752
+ options.yes,
8753
+ options.json
8754
+ );
8755
+ await controllerJson(auth, "instafy team revoke-link", {
8756
+ method: "DELETE",
8757
+ path: `/orgs/${encodeURIComponent(orgId)}/invite-links/${encodeURIComponent(inviteLinkId)}`
8758
+ });
8759
+ if (options.json) {
8760
+ console.log(JSON.stringify({ ok: true, inviteLinkId }, null, 2));
8761
+ return;
8762
+ }
8763
+ console.log(kleur14.green(`Revoked invite link ${inviteLinkId}.`));
8764
+ }
8765
+
7835
8766
  // src/provider-bindings.ts
7836
8767
  init_project_manifest();
7837
- import fs10 from "node:fs";
7838
- import path11 from "node:path";
8768
+ import fs11 from "node:fs";
8769
+ import path12 from "node:path";
7839
8770
  import { pathToFileURL } from "node:url";
7840
- import kleur13 from "kleur";
8771
+ import kleur15 from "kleur";
7841
8772
  function resolveBindingContext(startDir) {
7842
- const lookup = findProjectManifest(path11.resolve(startDir));
8773
+ const lookup = findProjectManifest(path12.resolve(startDir));
7843
8774
  if (!lookup.manifest || !lookup.path) {
7844
8775
  throw new Error(
7845
8776
  "Linked space manifest required. Run `instafy space init` in this folder or pass --path <dir>."
@@ -7849,19 +8780,19 @@ function resolveBindingContext(startDir) {
7849
8780
  if (!projectId) {
7850
8781
  throw new Error("Linked space manifest is missing a spaceId.");
7851
8782
  }
7852
- const instafyDir = path11.dirname(lookup.path);
7853
- const projectRoot = path11.dirname(instafyDir);
8783
+ const instafyDir = path12.dirname(lookup.path);
8784
+ const projectRoot = path12.dirname(instafyDir);
7854
8785
  return {
7855
- bindingsPath: path11.join(instafyDir, "provider-bindings.json"),
8786
+ bindingsPath: path12.join(instafyDir, "provider-bindings.json"),
7856
8787
  projectId,
7857
8788
  projectRoot
7858
8789
  };
7859
8790
  }
7860
8791
  function readBindingStore(bindingsPath) {
7861
- if (!fs10.existsSync(bindingsPath)) {
8792
+ if (!fs11.existsSync(bindingsPath)) {
7862
8793
  return { version: 1, bindings: {} };
7863
8794
  }
7864
- const raw = JSON.parse(fs10.readFileSync(bindingsPath, "utf8"));
8795
+ const raw = JSON.parse(fs11.readFileSync(bindingsPath, "utf8"));
7865
8796
  const bindings = raw && typeof raw.bindings === "object" && raw.bindings !== null ? raw.bindings : {};
7866
8797
  return {
7867
8798
  version: 1,
@@ -7869,8 +8800,8 @@ function readBindingStore(bindingsPath) {
7869
8800
  };
7870
8801
  }
7871
8802
  function writeBindingStore(bindingsPath, store) {
7872
- fs10.mkdirSync(path11.dirname(bindingsPath), { recursive: true });
7873
- fs10.writeFileSync(bindingsPath, JSON.stringify(store, null, 2), "utf8");
8803
+ fs11.mkdirSync(path12.dirname(bindingsPath), { recursive: true });
8804
+ fs11.writeFileSync(bindingsPath, JSON.stringify(store, null, 2), "utf8");
7874
8805
  }
7875
8806
  function normalizeCapabilities2(values) {
7876
8807
  const requested = Array.isArray(values) && values.length > 0 ? values : ["project_content_read", "project_content_write"];
@@ -7893,7 +8824,7 @@ function deriveStatus(capabilities) {
7893
8824
  return "bound_read_only";
7894
8825
  }
7895
8826
  function printBinding(record) {
7896
- console.log(kleur13.cyan(`Provider: ${record.providerId}`));
8827
+ console.log(kleur15.cyan(`Provider: ${record.providerId}`));
7897
8828
  console.log(`Status: ${record.status}`);
7898
8829
  console.log(`Project: ${record.projectId}`);
7899
8830
  console.log(`Root URI: ${record.rootUri}`);
@@ -7935,10 +8866,10 @@ function showProviderBinding(options) {
7935
8866
  return;
7936
8867
  }
7937
8868
  if (bindings.length === 0) {
7938
- console.log(kleur13.yellow("No provider bindings recorded for this space."));
8869
+ console.log(kleur15.yellow("No provider bindings recorded for this space."));
7939
8870
  return;
7940
8871
  }
7941
- console.log(kleur13.cyan(`Bindings for ${context.projectId}:`));
8872
+ console.log(kleur15.cyan(`Bindings for ${context.projectId}:`));
7942
8873
  for (const record of bindings) {
7943
8874
  console.log(`- ${record.providerId} (${record.status})`);
7944
8875
  }
@@ -7970,9 +8901,9 @@ function grantProviderBinding(options) {
7970
8901
  console.log(JSON.stringify(record));
7971
8902
  return;
7972
8903
  }
7973
- console.log(kleur13.green(`Provider binding granted: ${providerId}`));
8904
+ console.log(kleur15.green(`Provider binding granted: ${providerId}`));
7974
8905
  printBinding(record);
7975
- console.log(kleur13.cyan(`Stored at ${context.bindingsPath}`));
8906
+ console.log(kleur15.cyan(`Stored at ${context.bindingsPath}`));
7976
8907
  }
7977
8908
  function revokeProviderBinding(options) {
7978
8909
  const providerId = options.providerId.trim();
@@ -7991,12 +8922,12 @@ function revokeProviderBinding(options) {
7991
8922
  console.log(JSON.stringify({ revoked: true, providerId }));
7992
8923
  return;
7993
8924
  }
7994
- console.log(kleur13.green(`Provider binding revoked: ${providerId}`));
7995
- console.log(kleur13.cyan(`Updated ${context.bindingsPath}`));
8925
+ console.log(kleur15.green(`Provider binding revoked: ${providerId}`));
8926
+ console.log(kleur15.cyan(`Updated ${context.bindingsPath}`));
7996
8927
  }
7997
8928
 
7998
8929
  // src/providers.ts
7999
- import kleur14 from "kleur";
8930
+ import kleur16 from "kleur";
8000
8931
 
8001
8932
  // ../provider-client/index.js
8002
8933
  var DEFAULT_PROVIDER_HOST_BASE_URL = "http://127.0.0.1:8797";
@@ -8068,12 +8999,12 @@ function resolveFetchImplementation(fetchImpl) {
8068
8999
  function createProviderHostClient(options = {}) {
8069
9000
  const fetchImpl = resolveFetchImplementation(options.fetch);
8070
9001
  const baseUrl = normalizeProviderHostBaseUrl(options.baseUrl);
8071
- async function jsonRequest(path14, init) {
8072
- const response = await fetchImpl(`${baseUrl}${path14}`, init);
9002
+ async function jsonRequest(path15, init) {
9003
+ const response = await fetchImpl(`${baseUrl}${path15}`, init);
8073
9004
  const json = await response.json();
8074
9005
  if (!response.ok || json?.ok === false) {
8075
9006
  throw new Error(
8076
- json?.error || json?.stderr || `provider host request failed for ${path14}`
9007
+ json?.error || json?.stderr || `provider host request failed for ${path15}`
8077
9008
  );
8078
9009
  }
8079
9010
  return json;
@@ -8166,7 +9097,7 @@ function formatProviderLabel(provider) {
8166
9097
  return provider.title?.trim().length ? provider.title.trim() : provider.id;
8167
9098
  }
8168
9099
  function printProviderSummary(provider) {
8169
- console.log(kleur14.green(formatProviderLabel(provider)));
9100
+ console.log(kleur16.green(formatProviderLabel(provider)));
8170
9101
  console.log(` id: ${provider.id}`);
8171
9102
  if (provider.description) {
8172
9103
  console.log(` description: ${provider.description}`);
@@ -8178,7 +9109,7 @@ function printProviderSummary(provider) {
8178
9109
  console.log(` type: ${provider.providerType}`);
8179
9110
  }
8180
9111
  console.log(
8181
- ` discoverable: ${provider.discoverable !== false ? kleur14.green("yes") : kleur14.yellow("no")}`
9112
+ ` discoverable: ${provider.discoverable !== false ? kleur16.green("yes") : kleur16.yellow("no")}`
8182
9113
  );
8183
9114
  if (provider.capabilityIds?.length) {
8184
9115
  console.log(` capabilities: ${provider.capabilityIds.join(", ")}`);
@@ -8190,7 +9121,7 @@ function printProviderSummary(provider) {
8190
9121
  console.log(` resources: ${provider.resourceUris.join(", ")}`);
8191
9122
  }
8192
9123
  if (provider.error) {
8193
- console.log(` error: ${kleur14.yellow(provider.error)}`);
9124
+ console.log(` error: ${kleur16.yellow(provider.error)}`);
8194
9125
  }
8195
9126
  }
8196
9127
  function parseCommandJson(commandJson) {
@@ -8210,10 +9141,10 @@ async function providersList(options = {}) {
8210
9141
  });
8211
9142
  return;
8212
9143
  }
8213
- console.log(kleur14.green("Local providers"));
9144
+ console.log(kleur16.green("Local providers"));
8214
9145
  console.log(`Host: ${providerHostUrl}`);
8215
9146
  if (!result.providers.length) {
8216
- console.log(kleur14.yellow("No providers discovered."));
9147
+ console.log(kleur16.yellow("No providers discovered."));
8217
9148
  return;
8218
9149
  }
8219
9150
  for (const provider of result.providers) {
@@ -8230,7 +9161,7 @@ async function providersDiscover(params) {
8230
9161
  });
8231
9162
  return;
8232
9163
  }
8233
- console.log(kleur14.green(`Provider discovery: ${params.providerId}`));
9164
+ console.log(kleur16.green(`Provider discovery: ${params.providerId}`));
8234
9165
  console.log(`Host: ${providerHostUrl}`);
8235
9166
  console.log(JSON.stringify(result.provider ?? result, null, 2));
8236
9167
  }
@@ -8244,10 +9175,10 @@ async function providersRead(params) {
8244
9175
  });
8245
9176
  return;
8246
9177
  }
8247
- console.log(kleur14.green(`Provider resource: ${params.uri}`));
9178
+ console.log(kleur16.green(`Provider resource: ${params.uri}`));
8248
9179
  console.log(`Host: ${providerHostUrl}`);
8249
9180
  if (result.exists === false) {
8250
- console.log(kleur14.yellow("Resource does not exist."));
9181
+ console.log(kleur16.yellow("Resource does not exist."));
8251
9182
  return;
8252
9183
  }
8253
9184
  console.log(JSON.stringify(result.value ?? result, null, 2));
@@ -8273,7 +9204,7 @@ async function providersProbe(params) {
8273
9204
  });
8274
9205
  return;
8275
9206
  }
8276
- console.log(kleur14.green(`Provider transport probe: ${params.providerId}`));
9207
+ console.log(kleur16.green(`Provider transport probe: ${params.providerId}`));
8277
9208
  console.log(`Host: ${providerHostUrl}`);
8278
9209
  console.log(JSON.stringify(result, null, 2));
8279
9210
  }
@@ -8479,8 +9410,8 @@ async function diagnosticsRunResult(options) {
8479
9410
  }
8480
9411
 
8481
9412
  // src/support.ts
8482
- import fs11 from "node:fs";
8483
- import path12 from "node:path";
9413
+ import fs12 from "node:fs";
9414
+ import path13 from "node:path";
8484
9415
  init_project_manifest();
8485
9416
  var MAX_SUMMARY_CHARS = 500;
8486
9417
  var MAX_DETAILS_BYTES = 2e4;
@@ -8494,34 +9425,34 @@ function cleanText3(value) {
8494
9425
  return typeof value === "string" && value.trim() ? value.trim() : null;
8495
9426
  }
8496
9427
  function readRegularFile(filePath, maxBytes, label) {
8497
- const resolved = path12.resolve(filePath);
9428
+ const resolved = path13.resolve(filePath);
8498
9429
  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)) {
9430
+ const workspaceRoot = manifestPath ? path13.dirname(path13.dirname(manifestPath)) : process.cwd();
9431
+ const canonicalRoot = fs12.realpathSync(workspaceRoot);
9432
+ const canonicalParent = fs12.realpathSync(path13.dirname(resolved));
9433
+ const canonicalTarget = path13.join(canonicalParent, path13.basename(resolved));
9434
+ const relativeTarget = path13.relative(canonicalRoot, canonicalTarget);
9435
+ if (relativeTarget === ".." || relativeTarget.startsWith(`..${path13.sep}`) || path13.isAbsolute(relativeTarget)) {
8505
9436
  throw new Error(`${label} must stay inside the active Instafy workspace.`);
8506
9437
  }
8507
- const initialStat = fs11.lstatSync(canonicalTarget);
9438
+ const initialStat = fs12.lstatSync(canonicalTarget);
8508
9439
  if (initialStat.isSymbolicLink() || !initialStat.isFile()) {
8509
9440
  throw new Error(`${label} must be a regular file, not a symlink.`);
8510
9441
  }
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);
9442
+ const noFollow = typeof fs12.constants.O_NOFOLLOW === "number" ? fs12.constants.O_NOFOLLOW : 0;
9443
+ const descriptor = fs12.openSync(canonicalTarget, fs12.constants.O_RDONLY | noFollow);
8513
9444
  try {
8514
- const openedStat = fs11.fstatSync(descriptor);
9445
+ const openedStat = fs12.fstatSync(descriptor);
8515
9446
  if (!openedStat.isFile() || openedStat.dev !== initialStat.dev || openedStat.ino !== initialStat.ino || openedStat.size > maxBytes) {
8516
9447
  throw new Error(`${label} must be a regular file no larger than ${maxBytes} bytes.`);
8517
9448
  }
8518
- const bytes = fs11.readFileSync(descriptor);
9449
+ const bytes = fs12.readFileSync(descriptor);
8519
9450
  if (bytes.length > maxBytes) {
8520
9451
  throw new Error(`${label} must be ${maxBytes} bytes or smaller.`);
8521
9452
  }
8522
9453
  return bytes;
8523
9454
  } finally {
8524
- fs11.closeSync(descriptor);
9455
+ fs12.closeSync(descriptor);
8525
9456
  }
8526
9457
  }
8527
9458
  function readJsonFile(filePath, maxBytes, label) {
@@ -8597,7 +9528,7 @@ function resolveScreenshots(filePaths = []) {
8597
9528
  );
8598
9529
  }
8599
9530
  return {
8600
- fileName: path12.basename(filePath),
9531
+ fileName: path13.basename(filePath),
8601
9532
  mediaType,
8602
9533
  dataBase64: bytes.toString("base64"),
8603
9534
  byteLength: bytes.length
@@ -8900,7 +9831,7 @@ program.command("login").description("Log in and save an access token for future
8900
9831
  json: opts.json
8901
9832
  });
8902
9833
  } catch (error) {
8903
- console.error(kleur17.red(String(error)));
9834
+ console.error(kleur19.red(String(error)));
8904
9835
  process.exit(1);
8905
9836
  }
8906
9837
  });
@@ -8908,7 +9839,7 @@ program.command("logout").description("Clear the saved CLI access token").option
8908
9839
  try {
8909
9840
  await logout({ json: opts.json, profile: opts.profile });
8910
9841
  } catch (error) {
8911
- console.error(kleur17.red(String(error)));
9842
+ console.error(kleur19.red(String(error)));
8912
9843
  process.exit(1);
8913
9844
  }
8914
9845
  });
@@ -8932,7 +9863,7 @@ chatCommand.option("--json", "Output JSON").action(async (promptParts, opts) =>
8932
9863
  json: opts.json
8933
9864
  });
8934
9865
  } catch (error) {
8935
- console.error(kleur17.red(String(error)));
9866
+ console.error(kleur19.red(String(error)));
8936
9867
  process.exit(1);
8937
9868
  }
8938
9869
  });
@@ -8956,7 +9887,7 @@ function registerSpaceCommand(command) {
8956
9887
  json: opts.json
8957
9888
  });
8958
9889
  } catch (error) {
8959
- console.error(kleur17.red(String(error)));
9890
+ console.error(kleur19.red(String(error)));
8960
9891
  process.exit(1);
8961
9892
  }
8962
9893
  });
@@ -8972,7 +9903,7 @@ function registerSpaceCommand(command) {
8972
9903
  json: opts.json
8973
9904
  });
8974
9905
  } catch (error) {
8975
- console.error(kleur17.red(String(error)));
9906
+ console.error(kleur19.red(String(error)));
8976
9907
  process.exit(1);
8977
9908
  }
8978
9909
  });
@@ -8986,7 +9917,7 @@ function registerSpaceCommand(command) {
8986
9917
  json: opts.json
8987
9918
  });
8988
9919
  } catch (error) {
8989
- console.error(kleur17.red(String(error)));
9920
+ console.error(kleur19.red(String(error)));
8990
9921
  process.exit(1);
8991
9922
  }
8992
9923
  });
@@ -9009,7 +9940,7 @@ function registerSpaceCommand(command) {
9009
9940
  json: opts.json
9010
9941
  });
9011
9942
  } catch (error) {
9012
- console.error(kleur17.red(String(error)));
9943
+ console.error(kleur19.red(String(error)));
9013
9944
  process.exit(1);
9014
9945
  }
9015
9946
  });
@@ -9021,7 +9952,7 @@ function registerSpaceCommand(command) {
9021
9952
  json: opts.json
9022
9953
  });
9023
9954
  } catch (error) {
9024
- console.error(kleur17.red(String(error)));
9955
+ console.error(kleur19.red(String(error)));
9025
9956
  process.exit(1);
9026
9957
  }
9027
9958
  });
@@ -9043,7 +9974,7 @@ function registerSpaceCommand(command) {
9043
9974
  json: opts.json
9044
9975
  });
9045
9976
  } catch (error) {
9046
- console.error(kleur17.red(String(error)));
9977
+ console.error(kleur19.red(String(error)));
9047
9978
  process.exit(1);
9048
9979
  }
9049
9980
  });
@@ -9059,7 +9990,7 @@ function registerSpaceCommand(command) {
9059
9990
  json: opts.json
9060
9991
  });
9061
9992
  } catch (error) {
9062
- console.error(kleur17.red(String(error)));
9993
+ console.error(kleur19.red(String(error)));
9063
9994
  process.exit(1);
9064
9995
  }
9065
9996
  });
@@ -9085,7 +10016,7 @@ function registerSpaceCommand(command) {
9085
10016
  json: opts.json
9086
10017
  });
9087
10018
  } catch (error) {
9088
- console.error(kleur17.red(String(error)));
10019
+ console.error(kleur19.red(String(error)));
9089
10020
  process.exit(1);
9090
10021
  }
9091
10022
  });
@@ -9110,7 +10041,7 @@ function registerSpaceCommand(command) {
9110
10041
  json: opts.json
9111
10042
  });
9112
10043
  } catch (error) {
9113
- console.error(kleur17.red(String(error)));
10044
+ console.error(kleur19.red(String(error)));
9114
10045
  process.exit(1);
9115
10046
  }
9116
10047
  });
@@ -9130,7 +10061,7 @@ hardwareBindingsCommand.command("show").description("Show hardware bindings for
9130
10061
  json: opts.json
9131
10062
  });
9132
10063
  } catch (error) {
9133
- console.error(kleur17.red(String(error)));
10064
+ console.error(kleur19.red(String(error)));
9134
10065
  process.exit(1);
9135
10066
  }
9136
10067
  });
@@ -9150,7 +10081,7 @@ hardwareBindingsCommand.command("grant").description("Grant a local hardware pro
9150
10081
  json: opts.json
9151
10082
  });
9152
10083
  } catch (error) {
9153
- console.error(kleur17.red(String(error)));
10084
+ console.error(kleur19.red(String(error)));
9154
10085
  process.exit(1);
9155
10086
  }
9156
10087
  });
@@ -9162,7 +10093,7 @@ hardwareBindingsCommand.command("revoke").description("Revoke a local hardware b
9162
10093
  json: opts.json
9163
10094
  });
9164
10095
  } catch (error) {
9165
- console.error(kleur17.red(String(error)));
10096
+ console.error(kleur19.red(String(error)));
9166
10097
  process.exit(1);
9167
10098
  }
9168
10099
  });
@@ -9176,7 +10107,7 @@ hardwareSerialCommand.command("list").description("List local serial devices vis
9176
10107
  scanDirs: opts.scanDir
9177
10108
  });
9178
10109
  } catch (error) {
9179
- console.error(kleur17.red(String(error)));
10110
+ console.error(kleur19.red(String(error)));
9180
10111
  process.exit(1);
9181
10112
  }
9182
10113
  });
@@ -9188,7 +10119,7 @@ hardwareSerialCommand.command("probe").description("Probe host permissions for o
9188
10119
  json: opts.json
9189
10120
  });
9190
10121
  } catch (error) {
9191
- console.error(kleur17.red(String(error)));
10122
+ console.error(kleur19.red(String(error)));
9192
10123
  process.exit(1);
9193
10124
  }
9194
10125
  });
@@ -9200,7 +10131,7 @@ hardwareCommand.command("opportunities").description("List host-local IO opportu
9200
10131
  scanDirs: opts.scanDir
9201
10132
  });
9202
10133
  } catch (error) {
9203
- console.error(kleur17.red(String(error)));
10134
+ console.error(kleur19.red(String(error)));
9204
10135
  process.exit(1);
9205
10136
  }
9206
10137
  });
@@ -9213,7 +10144,7 @@ hardwareCommand.command("run").description("Run one available host-local IO acti
9213
10144
  json: opts.json
9214
10145
  });
9215
10146
  } catch (error) {
9216
- console.error(kleur17.red(String(error)));
10147
+ console.error(kleur19.red(String(error)));
9217
10148
  process.exit(1);
9218
10149
  }
9219
10150
  });
@@ -9228,7 +10159,7 @@ providersListCommand.option("--json", "Output JSON").action(async (opts) => {
9228
10159
  json: opts.json
9229
10160
  });
9230
10161
  } catch (error) {
9231
- console.error(kleur17.red(String(error)));
10162
+ console.error(kleur19.red(String(error)));
9232
10163
  process.exit(1);
9233
10164
  }
9234
10165
  });
@@ -9242,7 +10173,7 @@ providersDiscoverCommand.option("--json", "Output JSON").action(async (providerI
9242
10173
  json: opts.json
9243
10174
  });
9244
10175
  } catch (error) {
9245
- console.error(kleur17.red(String(error)));
10176
+ console.error(kleur19.red(String(error)));
9246
10177
  process.exit(1);
9247
10178
  }
9248
10179
  });
@@ -9257,7 +10188,7 @@ providersReadCommand.option("--json", "Output JSON").action(async (providerId, u
9257
10188
  json: opts.json
9258
10189
  });
9259
10190
  } catch (error) {
9260
- console.error(kleur17.red(String(error)));
10191
+ console.error(kleur19.red(String(error)));
9261
10192
  process.exit(1);
9262
10193
  }
9263
10194
  });
@@ -9280,7 +10211,7 @@ providersProbeCommand.option("--json", "Output JSON").action(async (providerId,
9280
10211
  json: opts.json
9281
10212
  });
9282
10213
  } catch (error) {
9283
- console.error(kleur17.red(String(error)));
10214
+ console.error(kleur19.red(String(error)));
9284
10215
  process.exit(1);
9285
10216
  }
9286
10217
  });
@@ -9302,7 +10233,7 @@ agentsListCommand.option("--json", "Output JSON").action(async (opts) => {
9302
10233
  json: opts.json
9303
10234
  });
9304
10235
  } catch (error) {
9305
- console.error(kleur17.red(String(error)));
10236
+ console.error(kleur19.red(String(error)));
9306
10237
  process.exit(1);
9307
10238
  }
9308
10239
  });
@@ -9330,7 +10261,7 @@ agentsContextListCommand.option("--agent <handle>", "Filter by agent handle (for
9330
10261
  json: opts.json
9331
10262
  });
9332
10263
  } catch (error) {
9333
- console.error(kleur17.red(String(error)));
10264
+ console.error(kleur19.red(String(error)));
9334
10265
  process.exit(1);
9335
10266
  }
9336
10267
  });
@@ -9356,7 +10287,7 @@ agentsContextPutCommand.action(async (contextParts, opts) => {
9356
10287
  json: opts.json
9357
10288
  });
9358
10289
  } catch (error) {
9359
- console.error(kleur17.red(String(error)));
10290
+ console.error(kleur19.red(String(error)));
9360
10291
  process.exit(1);
9361
10292
  }
9362
10293
  });
@@ -9372,7 +10303,7 @@ agentsStatusCommand.option("--json", "Output JSON").action(async (groupId, opts)
9372
10303
  json: opts.json
9373
10304
  });
9374
10305
  } catch (error) {
9375
- console.error(kleur17.red(String(error)));
10306
+ console.error(kleur19.red(String(error)));
9376
10307
  process.exit(1);
9377
10308
  }
9378
10309
  });
@@ -9390,7 +10321,7 @@ agentsCancelCommand.option("--json", "Output JSON").action(async (opts) => {
9390
10321
  json: opts.json
9391
10322
  });
9392
10323
  } catch (error) {
9393
- console.error(kleur17.red(String(error)));
10324
+ console.error(kleur19.red(String(error)));
9394
10325
  process.exit(1);
9395
10326
  }
9396
10327
  });
@@ -9412,7 +10343,7 @@ secretsListCommand.option("--json", "Output JSON").action(async (opts) => {
9412
10343
  json: opts.json
9413
10344
  });
9414
10345
  } catch (error) {
9415
- console.error(kleur17.red(String(error)));
10346
+ console.error(kleur19.red(String(error)));
9416
10347
  process.exit(1);
9417
10348
  }
9418
10349
  });
@@ -9433,7 +10364,7 @@ secretsGetCommand.option("--json", "Output JSON").action(async (nameOrId, opts)
9433
10364
  json: opts.json
9434
10365
  });
9435
10366
  } catch (error) {
9436
- console.error(kleur17.red(String(error)));
10367
+ console.error(kleur19.red(String(error)));
9437
10368
  process.exit(1);
9438
10369
  }
9439
10370
  });
@@ -9458,7 +10389,7 @@ secretsPutCommand.option("--json", "Output JSON").action(async (name, opts) => {
9458
10389
  json: opts.json
9459
10390
  });
9460
10391
  } catch (error) {
9461
- console.error(kleur17.red(String(error)));
10392
+ console.error(kleur19.red(String(error)));
9462
10393
  process.exit(1);
9463
10394
  }
9464
10395
  });
@@ -9479,10 +10410,24 @@ secretsRevokeCommand.option("--json", "Output JSON").action(async (nameOrId, opt
9479
10410
  json: opts.json
9480
10411
  });
9481
10412
  } catch (error) {
9482
- console.error(kleur17.red(String(error)));
10413
+ console.error(kleur19.red(String(error)));
9483
10414
  process.exit(1);
9484
10415
  }
9485
10416
  });
10417
+ function resolveResultVisibilityFlag(opts) {
10418
+ if (opts.resultVisibility !== void 0) {
10419
+ const value = String(opts.resultVisibility).trim().toLowerCase();
10420
+ if (value !== "private" && value !== "team") {
10421
+ throw new Error("--result-visibility must be private or team");
10422
+ }
10423
+ return value;
10424
+ }
10425
+ if (opts.shareResults === true)
10426
+ return "team";
10427
+ if (opts.shareResults === false)
10428
+ return "private";
10429
+ return void 0;
10430
+ }
9486
10431
  var automationsCommand = program.command("automations").description("Manage scheduled automations (run prompts on a schedule)");
9487
10432
  failWithGroupHelp(automationsCommand);
9488
10433
  var automationsListCommand = automationsCommand.command("list").description("List automations for this space");
@@ -9501,13 +10446,22 @@ automationsListCommand.option("--json", "Output JSON").action(async (opts) => {
9501
10446
  json: opts.json
9502
10447
  });
9503
10448
  } catch (error) {
9504
- console.error(kleur17.red(String(error)));
10449
+ console.error(kleur19.red(String(error)));
9505
10450
  process.exit(1);
9506
10451
  }
9507
10452
  });
9508
10453
  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
10454
  "--silent-when-nothing-to-report",
9510
10455
  "Do not post a completion result or send a result notification when a successful run has no findings"
10456
+ ).option(
10457
+ "--share-results",
10458
+ "Share this automation's result threads with your team (visible to anyone with space access)"
10459
+ ).option(
10460
+ "--no-share-results",
10461
+ "Keep this automation's result threads private to you (default)"
10462
+ ).option(
10463
+ "--result-visibility <private|team>",
10464
+ "Result-thread visibility (private|team); overrides --share-results"
9511
10465
  ).option("--paused", "Create paused");
9512
10466
  addSpaceOption(
9513
10467
  automationsCreateCommand,
@@ -9529,6 +10483,7 @@ automationsCreateCommand.option("--json", "Output JSON").action(async (opts) =>
9529
10483
  runtimeMode: opts.runtimeMode,
9530
10484
  runtimeProvider: opts.runtimeProvider,
9531
10485
  silentWhenNothingToReport: Boolean(opts.silentWhenNothingToReport),
10486
+ resultVisibility: resolveResultVisibilityFlag(opts),
9532
10487
  paused: Boolean(opts.paused),
9533
10488
  project: resolveSpaceIdOption(opts),
9534
10489
  controllerUrl: opts.serverUrl,
@@ -9536,7 +10491,53 @@ automationsCreateCommand.option("--json", "Output JSON").action(async (opts) =>
9536
10491
  json: opts.json
9537
10492
  });
9538
10493
  } catch (error) {
9539
- console.error(kleur17.red(String(error)));
10494
+ console.error(kleur19.red(String(error)));
10495
+ process.exit(1);
10496
+ }
10497
+ });
10498
+ var automationsUpdateCommand = automationsCommand.command("update").description(
10499
+ "Update an automation's name, prompt, schedule, runtime settings, or result-sharing visibility in place"
10500
+ ).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(
10501
+ "--silent-when-nothing-to-report",
10502
+ "Do not post a completion result or send a result notification when a successful run has no findings"
10503
+ ).option(
10504
+ "--no-silent-when-nothing-to-report",
10505
+ "Always post a completion result and send a result notification"
10506
+ ).option(
10507
+ "--share-results",
10508
+ "Share this automation's result threads with your team (visible to anyone with space access)"
10509
+ ).option(
10510
+ "--no-share-results",
10511
+ "Keep this automation's result threads private to you"
10512
+ ).option(
10513
+ "--result-visibility <private|team>",
10514
+ "Result-thread visibility (private|team); overrides --share-results"
10515
+ );
10516
+ addServerUrlOptions(automationsUpdateCommand);
10517
+ addAccessTokenOptions(automationsUpdateCommand, "Instafy access token");
10518
+ automationsUpdateCommand.option("--json", "Output JSON").action(async (automationId, opts) => {
10519
+ try {
10520
+ await automationsUpdate({
10521
+ automationId,
10522
+ name: opts.name,
10523
+ prompt: opts.prompt,
10524
+ promptFile: opts.promptFile,
10525
+ scheduleKind: opts.scheduleKind,
10526
+ runAt: opts.runAt,
10527
+ intervalHours: opts.intervalHours,
10528
+ days: opts.days,
10529
+ time: opts.time,
10530
+ timezone: opts.timezone,
10531
+ runtimeMode: opts.runtimeMode,
10532
+ runtimeProvider: opts.runtimeProvider,
10533
+ silentWhenNothingToReport: opts.silentWhenNothingToReport,
10534
+ resultVisibility: resolveResultVisibilityFlag(opts),
10535
+ controllerUrl: opts.serverUrl,
10536
+ accessToken: opts.accessToken,
10537
+ json: opts.json
10538
+ });
10539
+ } catch (error) {
10540
+ console.error(kleur19.red(String(error)));
9540
10541
  process.exit(1);
9541
10542
  }
9542
10543
  });
@@ -9553,7 +10554,7 @@ automationsPauseCommand.option("--json", "Output JSON").action(async (automation
9553
10554
  json: opts.json
9554
10555
  });
9555
10556
  } catch (error) {
9556
- console.error(kleur17.red(String(error)));
10557
+ console.error(kleur19.red(String(error)));
9557
10558
  process.exit(1);
9558
10559
  }
9559
10560
  });
@@ -9570,7 +10571,7 @@ automationsResumeCommand.option("--json", "Output JSON").action(async (automatio
9570
10571
  json: opts.json
9571
10572
  });
9572
10573
  } catch (error) {
9573
- console.error(kleur17.red(String(error)));
10574
+ console.error(kleur19.red(String(error)));
9574
10575
  process.exit(1);
9575
10576
  }
9576
10577
  });
@@ -9586,7 +10587,7 @@ automationsRunCommand.option("--json", "Output JSON").action(async (automationId
9586
10587
  json: opts.json
9587
10588
  });
9588
10589
  } catch (error) {
9589
- console.error(kleur17.red(String(error)));
10590
+ console.error(kleur19.red(String(error)));
9590
10591
  process.exit(1);
9591
10592
  }
9592
10593
  });
@@ -9602,7 +10603,92 @@ automationsDeleteCommand.option("--json", "Output JSON").action(async (automatio
9602
10603
  json: opts.json
9603
10604
  });
9604
10605
  } catch (error) {
9605
- console.error(kleur17.red(String(error)));
10606
+ console.error(kleur19.red(String(error)));
10607
+ process.exit(1);
10608
+ }
10609
+ });
10610
+ var credentialsCommand = program.command("credentials").description("Inspect, verify and pick the AI provider credentials your jobs use");
10611
+ failWithGroupHelp(credentialsCommand);
10612
+ var credentialsListCommand = credentialsCommand.command("list").description("List your AI credentials (secret material is never shown)").option("--all", "Include revoked credentials");
10613
+ addServerUrlOptions(credentialsListCommand);
10614
+ addAccessTokenOptions(credentialsListCommand, "Instafy access token");
10615
+ credentialsListCommand.option("--json", "Output JSON").action(async (opts) => {
10616
+ try {
10617
+ await credentialsList({
10618
+ all: Boolean(opts.all),
10619
+ controllerUrl: opts.serverUrl,
10620
+ accessToken: opts.accessToken,
10621
+ json: opts.json
10622
+ });
10623
+ } catch (error) {
10624
+ console.error(kleur19.red(String(error)));
10625
+ process.exit(1);
10626
+ }
10627
+ });
10628
+ 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");
10629
+ addServerUrlOptions(credentialsTestCommand);
10630
+ addAccessTokenOptions(credentialsTestCommand, "Instafy access token");
10631
+ credentialsTestCommand.option("--json", "Output JSON").action(async (idOrPrefix, opts) => {
10632
+ try {
10633
+ const result = await credentialsTest({
10634
+ idOrPrefix,
10635
+ controllerUrl: opts.serverUrl,
10636
+ accessToken: opts.accessToken,
10637
+ json: opts.json
10638
+ });
10639
+ if (!result.ok) {
10640
+ process.exitCode = 1;
10641
+ }
10642
+ } catch (error) {
10643
+ console.error(kleur19.red(String(error)));
10644
+ process.exit(1);
10645
+ }
10646
+ });
10647
+ 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");
10648
+ addServerUrlOptions(credentialsDefaultCommand);
10649
+ addAccessTokenOptions(credentialsDefaultCommand, "Instafy access token");
10650
+ credentialsDefaultCommand.option("--json", "Output JSON").action(async (idOrPrefix, opts) => {
10651
+ try {
10652
+ const target = pickTrimmedString2(idOrPrefix);
10653
+ if (opts.clear && target) {
10654
+ throw new Error("Pass either a credential id or --clear, not both.");
10655
+ }
10656
+ if (!opts.clear && !target) {
10657
+ throw new Error("Provide a credential id (or unique prefix), or pass --clear.");
10658
+ }
10659
+ if (opts.clear) {
10660
+ await credentialsClearDefault({
10661
+ controllerUrl: opts.serverUrl,
10662
+ accessToken: opts.accessToken,
10663
+ json: opts.json
10664
+ });
10665
+ return;
10666
+ }
10667
+ await credentialsSetDefault({
10668
+ idOrPrefix: target,
10669
+ controllerUrl: opts.serverUrl,
10670
+ accessToken: opts.accessToken,
10671
+ json: opts.json
10672
+ });
10673
+ } catch (error) {
10674
+ console.error(kleur19.red(String(error)));
10675
+ process.exit(1);
10676
+ }
10677
+ });
10678
+ 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)");
10679
+ addServerUrlOptions(credentialsRevokeCommand);
10680
+ addAccessTokenOptions(credentialsRevokeCommand, "Instafy access token");
10681
+ credentialsRevokeCommand.option("--json", "Output JSON").action(async (idOrPrefix, opts) => {
10682
+ try {
10683
+ await credentialsRevoke({
10684
+ idOrPrefix,
10685
+ yes: Boolean(opts.yes),
10686
+ controllerUrl: opts.serverUrl,
10687
+ accessToken: opts.accessToken,
10688
+ json: opts.json
10689
+ });
10690
+ } catch (error) {
10691
+ console.error(kleur19.red(String(error)));
9606
10692
  process.exit(1);
9607
10693
  }
9608
10694
  });
@@ -9612,7 +10698,7 @@ configCommand.command("path").description("Print the config file path").option("
9612
10698
  try {
9613
10699
  configPath({ json: opts.json });
9614
10700
  } catch (error) {
9615
- console.error(kleur17.red(String(error)));
10701
+ console.error(kleur19.red(String(error)));
9616
10702
  process.exit(1);
9617
10703
  }
9618
10704
  });
@@ -9620,7 +10706,7 @@ configCommand.command("list").description("List saved configuration").option("--
9620
10706
  try {
9621
10707
  configList({ json: opts.json });
9622
10708
  } catch (error) {
9623
- console.error(kleur17.red(String(error)));
10709
+ console.error(kleur19.red(String(error)));
9624
10710
  process.exit(1);
9625
10711
  }
9626
10712
  });
@@ -9628,7 +10714,7 @@ configCommand.command("get").description("Get a config value (controller-url, st
9628
10714
  try {
9629
10715
  configGet({ key, json: opts.json });
9630
10716
  } catch (error) {
9631
- console.error(kleur17.red(String(error)));
10717
+ console.error(kleur19.red(String(error)));
9632
10718
  process.exit(1);
9633
10719
  }
9634
10720
  });
@@ -9636,7 +10722,7 @@ configCommand.command("set").description("Set a config value (controller-url, st
9636
10722
  try {
9637
10723
  configSet({ key, value, json: opts.json });
9638
10724
  } catch (error) {
9639
- console.error(kleur17.red(String(error)));
10725
+ console.error(kleur19.red(String(error)));
9640
10726
  process.exit(1);
9641
10727
  }
9642
10728
  });
@@ -9644,7 +10730,7 @@ configCommand.command("unset").description("Unset a config value (controller-url
9644
10730
  try {
9645
10731
  configUnset({ key, json: opts.json });
9646
10732
  } catch (error) {
9647
- console.error(kleur17.red(String(error)));
10733
+ console.error(kleur19.red(String(error)));
9648
10734
  process.exit(1);
9649
10735
  }
9650
10736
  });
@@ -9664,7 +10750,7 @@ runtimeStartCommand.option("--supabase-access-token <token>", "Supabase session
9664
10750
  controllerAccessToken: opts.accessToken
9665
10751
  });
9666
10752
  } catch (error) {
9667
- console.error(kleur17.red(String(error)));
10753
+ console.error(kleur19.red(String(error)));
9668
10754
  process.exit(1);
9669
10755
  }
9670
10756
  });
@@ -9672,7 +10758,7 @@ runtimeCommand.command("status").description("Show runtime health").option("--js
9672
10758
  try {
9673
10759
  await runtimeStatus({ json: opts.json });
9674
10760
  } catch (error) {
9675
- console.error(kleur17.red(String(error)));
10761
+ console.error(kleur19.red(String(error)));
9676
10762
  process.exit(1);
9677
10763
  }
9678
10764
  });
@@ -9680,7 +10766,7 @@ runtimeCommand.command("stop").description("Stop the local Instafy runtime").opt
9680
10766
  try {
9681
10767
  await runtimeStop({ json: opts.json });
9682
10768
  } catch (error) {
9683
- console.error(kleur17.red(String(error)));
10769
+ console.error(kleur19.red(String(error)));
9684
10770
  process.exit(1);
9685
10771
  }
9686
10772
  });
@@ -9706,7 +10792,7 @@ runtimeTokenCommand.option("--runtime-id <uuid>", "Runtime ID to bind token to")
9706
10792
  json: opts.json
9707
10793
  });
9708
10794
  } catch (error) {
9709
- console.error(kleur17.red(String(error)));
10795
+ console.error(kleur19.red(String(error)));
9710
10796
  process.exit(1);
9711
10797
  }
9712
10798
  });
@@ -9728,7 +10814,7 @@ diagnosticsRuntimeEventsCommand.action(async (opts) => {
9728
10814
  accessToken: opts.accessToken
9729
10815
  });
9730
10816
  } catch (error) {
9731
- console.error(kleur17.red(String(error)));
10817
+ console.error(kleur19.red(String(error)));
9732
10818
  process.exit(1);
9733
10819
  }
9734
10820
  });
@@ -9743,7 +10829,7 @@ diagnosticsRunResultCommand.action(async (runId, opts) => {
9743
10829
  accessToken: opts.accessToken
9744
10830
  });
9745
10831
  } catch (error) {
9746
- console.error(kleur17.red(String(error)));
10832
+ console.error(kleur19.red(String(error)));
9747
10833
  process.exit(1);
9748
10834
  }
9749
10835
  });
@@ -9777,7 +10863,7 @@ supportReportCommand.action(async (summaryParts, opts) => {
9777
10863
  json: opts.json
9778
10864
  });
9779
10865
  } catch (error) {
9780
- console.error(kleur17.red(String(error)));
10866
+ console.error(kleur19.red(String(error)));
9781
10867
  process.exit(1);
9782
10868
  }
9783
10869
  });
@@ -9796,7 +10882,7 @@ supportListCommand.action(async (opts) => {
9796
10882
  json: opts.json
9797
10883
  });
9798
10884
  } catch (error) {
9799
- console.error(kleur17.red(String(error)));
10885
+ console.error(kleur19.red(String(error)));
9800
10886
  process.exit(1);
9801
10887
  }
9802
10888
  });
@@ -9812,7 +10898,7 @@ supportShowCommand.action(async (reportId, opts) => {
9812
10898
  json: opts.json
9813
10899
  });
9814
10900
  } catch (error) {
9815
- console.error(kleur17.red(String(error)));
10901
+ console.error(kleur19.red(String(error)));
9816
10902
  process.exit(1);
9817
10903
  }
9818
10904
  });
@@ -9857,19 +10943,19 @@ tunnelStartCommand.option("--no-detach", "Run in foreground until interrupted").
9857
10943
  console.log(JSON.stringify(started, null, 2));
9858
10944
  return;
9859
10945
  }
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}`));
10946
+ console.log(kleur19.green(`Tunnel started: ${started.url} (tunnelId=${started.tunnelId})`));
10947
+ console.log(kleur19.gray(`pid=${started.pid} \xB7 port=${started.localPort}`));
10948
+ console.log(kleur19.gray(`log: ${started.logFile}`));
9863
10949
  console.log("");
9864
10950
  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}`)}`);
10951
+ console.log(`- ${kleur19.cyan(`instafy tunnel list`)}`);
10952
+ console.log(`- ${kleur19.cyan(`instafy tunnel logs ${started.tunnelId} --follow`)}`);
10953
+ console.log(`- ${kleur19.cyan(`instafy tunnel stop ${started.tunnelId}`)}`);
9868
10954
  if (process.platform !== "win32") {
9869
- console.log(kleur17.gray(`(or) tail -n 200 -f ${started.logFile}`));
10955
+ console.log(kleur19.gray(`(or) tail -n 200 -f ${started.logFile}`));
9870
10956
  }
9871
10957
  } catch (error) {
9872
- console.error(kleur17.red(String(error)));
10958
+ console.error(kleur19.red(String(error)));
9873
10959
  process.exit(1);
9874
10960
  }
9875
10961
  });
@@ -9881,14 +10967,14 @@ tunnelCommand.command("list").description("List local tunnel sessions started by
9881
10967
  return;
9882
10968
  }
9883
10969
  if (tunnels.length === 0) {
9884
- console.log(kleur17.yellow("No tunnels found."));
10970
+ console.log(kleur19.yellow("No tunnels found."));
9885
10971
  return;
9886
10972
  }
9887
10973
  for (const tunnel of tunnels) {
9888
10974
  console.log(`${tunnel.tunnelId} \xB7 ${tunnel.url} \xB7 port=${tunnel.localPort} \xB7 pid=${tunnel.pid}`);
9889
10975
  }
9890
10976
  } catch (error) {
9891
- console.error(kleur17.red(String(error)));
10977
+ console.error(kleur19.red(String(error)));
9892
10978
  process.exit(1);
9893
10979
  }
9894
10980
  });
@@ -9904,9 +10990,9 @@ tunnelCommand.command("stop").description("Stop a local tunnel session and revok
9904
10990
  console.log(JSON.stringify(result, null, 2));
9905
10991
  return;
9906
10992
  }
9907
- console.log(kleur17.green(`Tunnel stopped: ${result.tunnelId}`));
10993
+ console.log(kleur19.green(`Tunnel stopped: ${result.tunnelId}`));
9908
10994
  } catch (error) {
9909
- console.error(kleur17.red(String(error)));
10995
+ console.error(kleur19.red(String(error)));
9910
10996
  process.exit(1);
9911
10997
  }
9912
10998
  });
@@ -9920,7 +11006,7 @@ tunnelCommand.command("logs").description("Show logs for a local tunnel session"
9920
11006
  json: Boolean(opts.json)
9921
11007
  });
9922
11008
  } catch (error) {
9923
- console.error(kleur17.red(String(error)));
11009
+ console.error(kleur19.red(String(error)));
9924
11010
  process.exit(1);
9925
11011
  }
9926
11012
  });
@@ -9936,7 +11022,158 @@ orgListCommand.option("--access-token <token>", "Instafy or Supabase access toke
9936
11022
  json: opts.json
9937
11023
  });
9938
11024
  } catch (error) {
9939
- console.error(kleur17.red(String(error)));
11025
+ console.error(kleur19.red(String(error)));
11026
+ process.exit(1);
11027
+ }
11028
+ });
11029
+ function teamScopeCommandFactory(nameAndArgs, description) {
11030
+ const command = orgCommand.command(nameAndArgs).description(description);
11031
+ addServerUrlOptions(command);
11032
+ addAccessTokenOptions(command, "Instafy access token");
11033
+ command.option("--team-id <team>", "Team id (UUID) or slug (defaults to your only team)").option("--json", "Output JSON");
11034
+ return command;
11035
+ }
11036
+ var teamMembersCommand = teamScopeCommandFactory("members", "List members of a team");
11037
+ teamMembersCommand.action(async (opts) => {
11038
+ try {
11039
+ await teamMembersList({
11040
+ teamId: opts.teamId,
11041
+ controllerUrl: opts.serverUrl,
11042
+ accessToken: opts.accessToken,
11043
+ json: opts.json
11044
+ });
11045
+ } catch (error) {
11046
+ console.error(kleur19.red(String(error)));
11047
+ process.exit(1);
11048
+ }
11049
+ });
11050
+ var teamInviteCommand = teamScopeCommandFactory(
11051
+ "invite <email>",
11052
+ "Invite a teammate by email (they must sign in with that email to accept)"
11053
+ );
11054
+ teamInviteCommand.option("--role <role>", "owner, admin, builder, or viewer (default: builder)").action(async (email, opts) => {
11055
+ try {
11056
+ await teamInvite({
11057
+ email,
11058
+ role: opts.role,
11059
+ teamId: opts.teamId,
11060
+ controllerUrl: opts.serverUrl,
11061
+ accessToken: opts.accessToken,
11062
+ json: opts.json
11063
+ });
11064
+ } catch (error) {
11065
+ console.error(kleur19.red(String(error)));
11066
+ process.exit(1);
11067
+ }
11068
+ });
11069
+ var teamInviteLinkCommand = teamScopeCommandFactory(
11070
+ "invite-link",
11071
+ "Create a shareable invite link that works with any sign-in method"
11072
+ );
11073
+ 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) => {
11074
+ try {
11075
+ await teamInviteLink({
11076
+ role: opts.role,
11077
+ studioUrl: opts.studioUrl,
11078
+ teamId: opts.teamId,
11079
+ controllerUrl: opts.serverUrl,
11080
+ accessToken: opts.accessToken,
11081
+ json: opts.json
11082
+ });
11083
+ } catch (error) {
11084
+ console.error(kleur19.red(String(error)));
11085
+ process.exit(1);
11086
+ }
11087
+ });
11088
+ var teamInvitesCommand = teamScopeCommandFactory(
11089
+ "invites",
11090
+ "List pending email invitations and invite links"
11091
+ );
11092
+ teamInvitesCommand.action(async (opts) => {
11093
+ try {
11094
+ await teamInvites({
11095
+ teamId: opts.teamId,
11096
+ controllerUrl: opts.serverUrl,
11097
+ accessToken: opts.accessToken,
11098
+ json: opts.json
11099
+ });
11100
+ } catch (error) {
11101
+ console.error(kleur19.red(String(error)));
11102
+ process.exit(1);
11103
+ }
11104
+ });
11105
+ var teamAddMemberCommand = teamScopeCommandFactory(
11106
+ "add-member",
11107
+ "Add an existing Instafy account to a team by user id"
11108
+ );
11109
+ 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) => {
11110
+ try {
11111
+ await teamAddMember({
11112
+ userId: opts.userId,
11113
+ role: opts.role,
11114
+ teamId: opts.teamId,
11115
+ controllerUrl: opts.serverUrl,
11116
+ accessToken: opts.accessToken,
11117
+ json: opts.json
11118
+ });
11119
+ } catch (error) {
11120
+ console.error(kleur19.red(String(error)));
11121
+ process.exit(1);
11122
+ }
11123
+ });
11124
+ var teamAcceptCommand = teamScopeCommandFactory(
11125
+ "accept <token>",
11126
+ "Accept a team invitation or invite link with the current account"
11127
+ );
11128
+ teamAcceptCommand.action(async (token, opts) => {
11129
+ try {
11130
+ await teamAccept({
11131
+ token,
11132
+ teamId: opts.teamId,
11133
+ controllerUrl: opts.serverUrl,
11134
+ accessToken: opts.accessToken,
11135
+ json: opts.json
11136
+ });
11137
+ } catch (error) {
11138
+ console.error(kleur19.red(String(error)));
11139
+ process.exit(1);
11140
+ }
11141
+ });
11142
+ var teamRevokeInviteCommand = teamScopeCommandFactory(
11143
+ "revoke-invite <invitation-id>",
11144
+ "Revoke a pending email invitation"
11145
+ );
11146
+ teamRevokeInviteCommand.option("--yes", "Skip the confirmation prompt").action(async (invitationId, opts) => {
11147
+ try {
11148
+ await teamRevokeInvite({
11149
+ invitationId,
11150
+ yes: opts.yes,
11151
+ teamId: opts.teamId,
11152
+ controllerUrl: opts.serverUrl,
11153
+ accessToken: opts.accessToken,
11154
+ json: opts.json
11155
+ });
11156
+ } catch (error) {
11157
+ console.error(kleur19.red(String(error)));
11158
+ process.exit(1);
11159
+ }
11160
+ });
11161
+ var teamRevokeLinkCommand = teamScopeCommandFactory(
11162
+ "revoke-link <invite-link-id>",
11163
+ "Revoke a shareable invite link"
11164
+ );
11165
+ teamRevokeLinkCommand.option("--yes", "Skip the confirmation prompt").action(async (inviteLinkId, opts) => {
11166
+ try {
11167
+ await teamRevokeLink({
11168
+ inviteLinkId,
11169
+ yes: opts.yes,
11170
+ teamId: opts.teamId,
11171
+ controllerUrl: opts.serverUrl,
11172
+ accessToken: opts.accessToken,
11173
+ json: opts.json
11174
+ });
11175
+ } catch (error) {
11176
+ console.error(kleur19.red(String(error)));
9940
11177
  process.exit(1);
9941
11178
  }
9942
11179
  });
@@ -9961,17 +11198,17 @@ profileCommand.command("list").description("List saved CLI profiles").option("--
9961
11198
  return;
9962
11199
  }
9963
11200
  if (profiles.length === 0) {
9964
- console.log(kleur17.yellow("No profiles found."));
9965
- console.log(`Create one with: ${kleur17.cyan("instafy login --profile <name>")}`);
11201
+ console.log(kleur19.yellow("No profiles found."));
11202
+ console.log(`Create one with: ${kleur19.cyan("instafy login --profile <name>")}`);
9966
11203
  return;
9967
11204
  }
9968
- console.log(kleur17.green("Instafy CLI profiles"));
11205
+ console.log(kleur19.green("Instafy CLI profiles"));
9969
11206
  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})`);
11207
+ const token = profile.accessTokenSet ? kleur19.green("token") : kleur19.yellow("no-token");
11208
+ console.log(`- ${kleur19.cyan(profile.name)} (${token})`);
9972
11209
  }
9973
11210
  } catch (error) {
9974
- console.error(kleur17.red(String(error)));
11211
+ console.error(kleur19.red(String(error)));
9975
11212
  process.exit(1);
9976
11213
  }
9977
11214
  });
@@ -9991,7 +11228,7 @@ historyMessagesCommand.option("--limit <n>", "Max messages to return (1-200, def
9991
11228
  pretty: opts.pretty
9992
11229
  });
9993
11230
  } catch (error) {
9994
- console.error(kleur17.red(String(error)));
11231
+ console.error(kleur19.red(String(error)));
9995
11232
  process.exit(1);
9996
11233
  }
9997
11234
  });
@@ -10008,7 +11245,7 @@ historyRunsCommand.option("--limit <n>", "Max runs to return (1-200, default: 50
10008
11245
  pretty: opts.pretty
10009
11246
  });
10010
11247
  } catch (error) {
10011
- console.error(kleur17.red(String(error)));
11248
+ console.error(kleur19.red(String(error)));
10012
11249
  process.exit(1);
10013
11250
  }
10014
11251
  });
@@ -10025,7 +11262,7 @@ historyConversationsCommand.option("--limit <n>", "Max conversations to return (
10025
11262
  pretty: opts.pretty
10026
11263
  });
10027
11264
  } catch (error) {
10028
- console.error(kleur17.red(String(error)));
11265
+ console.error(kleur19.red(String(error)));
10029
11266
  process.exit(1);
10030
11267
  }
10031
11268
  });
@@ -10046,7 +11283,7 @@ conversationCreateCommand.action(async (opts) => {
10046
11283
  json: opts.json
10047
11284
  });
10048
11285
  } catch (error) {
10049
- console.error(kleur17.red(String(error)));
11286
+ console.error(kleur19.red(String(error)));
10050
11287
  process.exit(1);
10051
11288
  }
10052
11289
  });
@@ -10064,7 +11301,7 @@ conversationListCommand.action(async (opts) => {
10064
11301
  json: opts.json
10065
11302
  });
10066
11303
  } catch (error) {
10067
- console.error(kleur17.red(String(error)));
11304
+ console.error(kleur19.red(String(error)));
10068
11305
  process.exit(1);
10069
11306
  }
10070
11307
  });
@@ -10083,7 +11320,7 @@ conversationSearchCommand.action(async (queryParts, opts) => {
10083
11320
  json: opts.json
10084
11321
  });
10085
11322
  } catch (error) {
10086
- console.error(kleur17.red(String(error)));
11323
+ console.error(kleur19.red(String(error)));
10087
11324
  process.exit(1);
10088
11325
  }
10089
11326
  });
@@ -10102,7 +11339,7 @@ conversationShowCommand.action(async (targetParts, opts) => {
10102
11339
  json: opts.json
10103
11340
  });
10104
11341
  } catch (error) {
10105
- console.error(kleur17.red(String(error)));
11342
+ console.error(kleur19.red(String(error)));
10106
11343
  process.exit(1);
10107
11344
  }
10108
11345
  });