@amerged/ohmyhost-mcp 0.1.19 → 0.1.21

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.
@@ -26,7 +26,7 @@ import {
26
26
  serverIdentityOf,
27
27
  setNegotiatedProtocolVersion,
28
28
  validateEnvelopeMeta
29
- } from "./chunk-PXGLL6SR.js";
29
+ } from "./chunk-4WY7AAPR.js";
30
30
 
31
31
  // node_modules/.pnpm/@modelcontextprotocol+server@2.0.0/node_modules/@modelcontextprotocol/server/dist/stdio.mjs
32
32
  var StdioServerTransport = class {
@@ -516,7 +516,7 @@ function toError(value) {
516
516
  var failures = {
517
517
  operation_abandoned: [
518
518
  "The operation stopped renewing its execution lease and was ended.",
519
- "Inspect project status and deployment diagnostics before creating a fresh plan. Keep this operation ID; the expired execution cannot resume."
519
+ "Inspect project status and, when this operation names a deployment_id, its diagnostics with 'ohmyhost deployment logs --project PROJECT_ULID --deployment DEPLOYMENT_ULID --follow --json' or MCP deployment_logs before creating a fresh plan. Keep this operation ID; the expired execution cannot resume."
520
520
  ],
521
521
  database_write_rejected: [
522
522
  "The database rejected this write; it was not committed.",
@@ -580,7 +580,7 @@ var failures = {
580
580
  ],
581
581
  build_failed: [
582
582
  "The build did not produce a verified deployable artifact.",
583
- "Inspect the deployment build diagnostics and verify the install and build commands for the admitted commit. Correct the reported failure before planning a new deployment."
583
+ "Read the BUILD_FAILED diagnostic of this operation's deployment_id with 'ohmyhost deployment logs --project PROJECT_ULID --deployment DEPLOYMENT_ULID --follow --json' or MCP deployment_logs; its excerpt is the tail of the install and build output. Correct that failure and verify the commands for the admitted commit before planning a new deployment."
584
584
  ],
585
585
  insufficient_organization_credits: [
586
586
  "The organization does not have sufficient available credits for this operation.",
@@ -596,7 +596,7 @@ var failures = {
596
596
  ],
597
597
  runtime_candidate_failed: [
598
598
  "The deployed candidate failed its runtime checks.",
599
- "Inspect deployment runtime diagnostics, required secrets and the health-check route before creating a new deployment plan."
599
+ "Read the HEALTH_CHECK_FAILED diagnostic of this operation's deployment_id with 'ohmyhost deployment logs --project PROJECT_ULID --deployment DEPLOYMENT_ULID --follow --json' or MCP deployment_logs. Its route must answer 2xx to a cookieless GET without a redirect; status_code and, for owners, excerpt show what it answered. Fix that, often a missing secret or a redirect, then plan a new deployment."
600
600
  ],
601
601
  runtime_candidate_rejected: [
602
602
  "The runtime rejected the built Worker script.",
@@ -799,7 +799,7 @@ function exact2(v, keys) {
799
799
  // apps/product-cli/package.json
800
800
  var package_default = {
801
801
  name: "@ohmyhost/product-cli",
802
- version: "0.1.19",
802
+ version: "0.1.21",
803
803
  private: true,
804
804
  ohmyhost: {
805
805
  deployment: "production",
@@ -1210,6 +1210,15 @@ function expectPositiveInteger(value, field, options = {}) {
1210
1210
  return value;
1211
1211
  }
1212
1212
 
1213
+ // packages/workos-auth-contracts/dist/user-api-keys.js
1214
+ var WORKOS_USER_API_KEY_PATTERN = /^sk_[A-Za-z0-9_-]{20,128}$/u;
1215
+
1216
+ // apps/product-cli/dist/profile-store.js
1217
+ import { randomUUID } from "node:crypto";
1218
+ import { link, mkdir as mkdir2, open as open2, readFile, readdir, unlink } from "node:fs/promises";
1219
+ import { homedir } from "node:os";
1220
+ import { join } from "node:path";
1221
+
1213
1222
  // apps/product-cli/dist/credential-store.js
1214
1223
  var CredentialStoreUnavailableError = class extends Error {
1215
1224
  constructor() {
@@ -1223,10 +1232,12 @@ var CredentialStoreIntegrityError = class extends Error {
1223
1232
  this.name = "CredentialStoreIntegrityError";
1224
1233
  }
1225
1234
  };
1226
- var CREDENTIAL_ACCOUNT = "credential";
1235
+ var LEGACY_CREDENTIAL_ACCOUNT = "credential";
1227
1236
  var NativeNapiKeyringCredentialStore = class {
1237
+ account;
1228
1238
  createEntry;
1229
- constructor(createEntry = void 0, serviceName = "ohmyhost") {
1239
+ constructor(createEntry = void 0, serviceName = "ohmyhost", account = LEGACY_CREDENTIAL_ACCOUNT) {
1240
+ this.account = account;
1230
1241
  this.createEntry = createEntry ?? createNativeEntryFactory(serviceName);
1231
1242
  }
1232
1243
  async read() {
@@ -1248,7 +1259,7 @@ var NativeNapiKeyringCredentialStore = class {
1248
1259
  throw new CredentialStoreIntegrityError();
1249
1260
  }
1250
1261
  entry() {
1251
- return this.createEntry(CREDENTIAL_ACCOUNT);
1262
+ return this.createEntry(this.account);
1252
1263
  }
1253
1264
  };
1254
1265
  var parseVerifiedCredential = (serialized) => {
@@ -1301,6 +1312,340 @@ var parseCredential = (serialized) => {
1301
1312
  };
1302
1313
  };
1303
1314
 
1315
+ // apps/product-cli/dist/profile-store.js
1316
+ var ProfileNameConflictError = class extends Error {
1317
+ profile;
1318
+ constructor(profile) {
1319
+ super("The profile name is used by another saved login");
1320
+ this.profile = profile;
1321
+ this.name = "ProfileNameConflictError";
1322
+ }
1323
+ };
1324
+ var ProfileLimitError = class extends Error {
1325
+ saved;
1326
+ constructor(saved) {
1327
+ super("The saved logins of this environment reached their limit");
1328
+ this.saved = saved;
1329
+ this.name = "ProfileLimitError";
1330
+ }
1331
+ };
1332
+ var MAX_PROFILES = 64;
1333
+ var MAX_INDEX_ATTEMPTS = 64;
1334
+ var INDEX_FILE = /^index-([1-9][0-9]{0,14})\.json$/u;
1335
+ var SERVICE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
1336
+ var PROFILE_NAME = /^[a-z0-9][a-z0-9_-]{0,62}$/u;
1337
+ var USER_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/u;
1338
+ var ORGANIZATION_ID = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
1339
+ var isProfileName = (value) => PROFILE_NAME.test(value);
1340
+ var isProfileUserId = (value) => USER_ID.test(value);
1341
+ var profileAccount = (profile) => `profile:${profile.userId}:${profile.organizationId ?? "none"}`;
1342
+ var sameLogin = (left, right) => profileAccount(left) === profileAccount(right);
1343
+ var profileIndexDirectory = (serviceName) => join(homedir(), ".ohmyhost", "profiles", serviceName);
1344
+ var nextProfileIndex = (current, profile, replaces) => [
1345
+ ...current.filter((saved) => !sameLogin(saved, profile) && (replaces === void 0 || !sameLogin(saved, replaces))),
1346
+ profile
1347
+ ];
1348
+ var admitProfile = (next, profile) => {
1349
+ const conflict = next.find((saved) => saved.name === profile.name && saved !== profile);
1350
+ if (conflict !== void 0)
1351
+ throw new ProfileNameConflictError(conflict);
1352
+ if (next.length > MAX_PROFILES)
1353
+ throw new ProfileLimitError(next.length - 1);
1354
+ };
1355
+ var NativeKeyringProfileRegistry = class {
1356
+ serviceName;
1357
+ #createEntry;
1358
+ #index;
1359
+ constructor(createEntry = void 0, serviceName = "ohmyhost", indexDirectory) {
1360
+ this.serviceName = serviceName;
1361
+ if (!SERVICE_NAME.test(serviceName))
1362
+ throw new CredentialStoreUnavailableError();
1363
+ this.#createEntry = createEntry ?? createNativeEntryFactory(serviceName);
1364
+ this.#index = new ProfileIndexFiles(indexDirectory ?? profileIndexDirectory(serviceName));
1365
+ }
1366
+ async list() {
1367
+ return (await this.#index.read()).profiles;
1368
+ }
1369
+ credential(profile) {
1370
+ return new NativeNapiKeyringCredentialStore(this.#createEntry, this.serviceName, profileAccount(profile));
1371
+ }
1372
+ legacy() {
1373
+ return new NativeNapiKeyringCredentialStore(this.#createEntry, this.serviceName, LEGACY_CREDENTIAL_ACCOUNT);
1374
+ }
1375
+ async save(profile, credential, replaces) {
1376
+ assertProfile(profile);
1377
+ const before = await this.list();
1378
+ admitProfile(nextProfileIndex(before, profile, replaces), profile);
1379
+ await this.credential(profile).replace(credential);
1380
+ try {
1381
+ await this.#index.update((current) => {
1382
+ const next = nextProfileIndex(current, profile, replaces);
1383
+ admitProfile(next, profile);
1384
+ return next;
1385
+ });
1386
+ } catch (error) {
1387
+ if (!before.some((saved) => sameLogin(saved, profile)))
1388
+ await this.credential(profile).clear();
1389
+ throw error;
1390
+ }
1391
+ if (replaces !== void 0 && !sameLogin(replaces, profile))
1392
+ await this.credential(replaces).clear();
1393
+ }
1394
+ async remove(profile) {
1395
+ await this.credential(profile).clear();
1396
+ await this.#index.update((current) => current.some((saved) => sameLogin(saved, profile)) ? current.filter((saved) => !sameLogin(saved, profile)) : void 0);
1397
+ }
1398
+ };
1399
+ var ProfileIndexFiles = class {
1400
+ directory;
1401
+ constructor(directory) {
1402
+ this.directory = directory;
1403
+ }
1404
+ async read() {
1405
+ for (let attempt = 0; attempt < MAX_INDEX_ATTEMPTS; attempt += 1) {
1406
+ const version2 = await this.#newest();
1407
+ if (version2 === 0)
1408
+ return { version: version2, profiles: [] };
1409
+ let serialized;
1410
+ try {
1411
+ serialized = await readFile(this.#file(version2), "utf8");
1412
+ } catch (error) {
1413
+ if (fileErrorCode(error) === "ENOENT")
1414
+ continue;
1415
+ throw new CredentialStoreUnavailableError();
1416
+ }
1417
+ return { version: version2, profiles: parseProfileIndex(serialized) };
1418
+ }
1419
+ throw new CredentialStoreUnavailableError();
1420
+ }
1421
+ /** Applies `change` to the newest index; `undefined` means nothing to change. */
1422
+ async update(change) {
1423
+ try {
1424
+ await mkdir2(this.directory, { recursive: true, mode: 448 });
1425
+ } catch {
1426
+ throw new CredentialStoreUnavailableError();
1427
+ }
1428
+ for (let attempt = 0; attempt < MAX_INDEX_ATTEMPTS; attempt += 1) {
1429
+ const { version: version2, profiles } = await this.read();
1430
+ const next = change(profiles);
1431
+ if (next === void 0)
1432
+ return;
1433
+ const temporary = join(this.directory, `.index-${randomUUID()}.tmp`);
1434
+ try {
1435
+ await writeDurably(temporary, serializeProfileIndex(next));
1436
+ await link(temporary, this.#file(version2 + 1));
1437
+ } catch (error) {
1438
+ if (fileErrorCode(error) === "EEXIST")
1439
+ continue;
1440
+ throw new CredentialStoreUnavailableError();
1441
+ } finally {
1442
+ await removeTemporary(temporary);
1443
+ }
1444
+ if (await this.#newest() !== version2 + 1)
1445
+ continue;
1446
+ await this.#prune(version2);
1447
+ return;
1448
+ }
1449
+ throw new CredentialStoreUnavailableError();
1450
+ }
1451
+ async #newest() {
1452
+ let names;
1453
+ try {
1454
+ names = await readdir(this.directory);
1455
+ } catch (error) {
1456
+ if (fileErrorCode(error) === "ENOENT")
1457
+ return 0;
1458
+ throw new CredentialStoreUnavailableError();
1459
+ }
1460
+ return names.reduce((newest, name) => Math.max(newest, indexVersion(name)), 0);
1461
+ }
1462
+ /**
1463
+ * Keeps the superseded version for readers that listed it just now; older ones go. Cleanup only:
1464
+ * a file left behind is ignored, since readers take the newest version, and another process may
1465
+ * be removing the same files at this moment.
1466
+ */
1467
+ async #prune(superseded) {
1468
+ let names;
1469
+ try {
1470
+ names = await readdir(this.directory);
1471
+ } catch {
1472
+ return;
1473
+ }
1474
+ for (const name of names) {
1475
+ const version2 = indexVersion(name);
1476
+ if (version2 === 0 || version2 >= superseded)
1477
+ continue;
1478
+ try {
1479
+ await unlink(join(this.directory, name));
1480
+ } catch {
1481
+ }
1482
+ }
1483
+ }
1484
+ #file(version2) {
1485
+ return join(this.directory, `index-${String(version2)}.json`);
1486
+ }
1487
+ };
1488
+ var indexVersion = (name) => Number(INDEX_FILE.exec(name)?.[1] ?? 0);
1489
+ var removeTemporary = async (path) => {
1490
+ try {
1491
+ await unlink(path);
1492
+ } catch {
1493
+ }
1494
+ };
1495
+ var fileErrorCode = (error) => typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
1496
+ var writeDurably = async (path, text) => {
1497
+ const handle = await open2(path, "wx", 384);
1498
+ try {
1499
+ await handle.writeFile(text, "utf8");
1500
+ await handle.sync();
1501
+ } finally {
1502
+ await handle.close();
1503
+ }
1504
+ };
1505
+ var serializeProfileIndex = (profiles) => JSON.stringify({
1506
+ version: 1,
1507
+ profiles: [...profiles].sort((left, right) => left.name < right.name ? -1 : 1).map((profile) => ({
1508
+ name: profile.name,
1509
+ user_id: profile.userId,
1510
+ organization_id: profile.organizationId,
1511
+ organization_name: profile.organizationName
1512
+ }))
1513
+ });
1514
+ var parseProfileIndex = (serialized) => {
1515
+ try {
1516
+ const value = JSON.parse(serialized);
1517
+ if (!isRecord(value, ["version", "profiles"]) || value["version"] !== 1)
1518
+ throw new Error("Invalid profile index");
1519
+ const entries = value["profiles"];
1520
+ if (!Array.isArray(entries) || entries.length > MAX_PROFILES)
1521
+ throw new Error("Invalid profile index");
1522
+ const profiles = entries.map((entry) => {
1523
+ if (!isRecord(entry, ["name", "user_id", "organization_id", "organization_name"]))
1524
+ throw new Error("Invalid profile");
1525
+ const profile = {
1526
+ name: entry["name"],
1527
+ userId: entry["user_id"],
1528
+ organizationId: entry["organization_id"],
1529
+ organizationName: entry["organization_name"]
1530
+ };
1531
+ assertProfile(profile);
1532
+ return profile;
1533
+ });
1534
+ if (new Set(profiles.map((profile) => profile.name)).size !== profiles.length || new Set(profiles.map(profileAccount)).size !== profiles.length)
1535
+ throw new Error("Duplicate profile");
1536
+ return profiles;
1537
+ } catch {
1538
+ throw new CredentialStoreIntegrityError();
1539
+ }
1540
+ };
1541
+ var assertProfile = (profile) => {
1542
+ if (typeof profile.name !== "string" || !PROFILE_NAME.test(profile.name) || typeof profile.userId !== "string" || !USER_ID.test(profile.userId) || !(profile.organizationId === null || typeof profile.organizationId === "string" && ORGANIZATION_ID.test(profile.organizationId)) || !(profile.organizationName === null || typeof profile.organizationName === "string" && profile.organizationName.length > 0 && profile.organizationName.length <= 128 && !new RegExp("\\p{Cc}", "u").test(profile.organizationName)) || profile.organizationId === null && profile.organizationName !== null)
1543
+ throw new CredentialStoreIntegrityError();
1544
+ };
1545
+ var isRecord = (value, keys) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).sort().join(",") === [...keys].sort().join(",");
1546
+ var selectProfile = (profiles, request) => {
1547
+ if (request.name !== void 0 && request.bound !== void 0 && request.name !== request.bound)
1548
+ return { outcome: "conflict", field: "binding", profile: null };
1549
+ const named = request.name ?? request.bound;
1550
+ if (named !== void 0) {
1551
+ const profile = profiles.find((saved) => saved.name === named);
1552
+ if (profile === void 0)
1553
+ return { outcome: "not_found" };
1554
+ if (request.organizationId !== void 0 && profile.organizationId !== request.organizationId)
1555
+ return { outcome: "conflict", field: "organization", profile };
1556
+ if (request.userId !== void 0 && profile.userId !== request.userId)
1557
+ return { outcome: "conflict", field: "user", profile };
1558
+ return { outcome: "selected", profile };
1559
+ }
1560
+ const candidates = profiles.filter((profile) => (request.organizationId === void 0 || profile.organizationId === request.organizationId) && (request.userId === void 0 || profile.userId === request.userId));
1561
+ const only = candidates[0];
1562
+ if (candidates.length === 1 && only !== void 0)
1563
+ return { outcome: "selected", profile: only };
1564
+ if (candidates.length > 1)
1565
+ return { outcome: "ambiguous", candidates };
1566
+ return profiles.length === 0 ? { outcome: "none" } : { outcome: "not_found" };
1567
+ };
1568
+ var selectProfileForOrganization = (profiles, request) => {
1569
+ const user = request.userId === void 0 ? {} : { userId: request.userId };
1570
+ if (request.name !== void 0 || request.bound !== void 0)
1571
+ return selectProfile(profiles, {
1572
+ ...request.name === void 0 ? {} : { name: request.name },
1573
+ ...request.bound === void 0 ? {} : { bound: request.bound },
1574
+ ...user
1575
+ });
1576
+ const candidates = profiles.filter((profile) => request.userId === void 0 || profile.userId === request.userId);
1577
+ const inOrganization = candidates.filter((profile) => profile.organizationId === request.organizationId);
1578
+ if (inOrganization.length > 1)
1579
+ return { outcome: "ambiguous", candidates: inOrganization };
1580
+ const unscoped = candidates.filter((profile) => profile.organizationId === null);
1581
+ const [member] = inOrganization;
1582
+ if (member !== void 0)
1583
+ return { outcome: "selected", profile: member };
1584
+ const [open4] = unscoped;
1585
+ if (unscoped.length === 1 && open4 !== void 0)
1586
+ return { outcome: "selected", profile: open4 };
1587
+ return selectProfile(profiles, user);
1588
+ };
1589
+ var defaultProfileName = (profiles, login) => {
1590
+ const same = profiles.find((profile) => profile.userId === login.userId && profile.organizationId === login.organizationId);
1591
+ if (same !== void 0)
1592
+ return same.name;
1593
+ const base = slug(login.organizationName ?? "") || (login.organizationId === null ? "account" : "workspace");
1594
+ const free = (name) => profiles.every((profile) => profile.name !== name);
1595
+ if (free(base))
1596
+ return base;
1597
+ const suffix = (login.organizationId ?? login.userId).replace(/[^A-Za-z0-9]/gu, "").slice(-6).toLowerCase();
1598
+ for (let attempt = 1; attempt <= profiles.length + 1; attempt += 1) {
1599
+ const candidate = `${base.slice(0, 48)}-${suffix}${attempt === 1 ? "" : `-${String(attempt)}`}`;
1600
+ if (free(candidate))
1601
+ return candidate;
1602
+ }
1603
+ throw new CredentialStoreIntegrityError();
1604
+ };
1605
+ var slug = (value) => value.normalize("NFKD").replace(/[\u0300-\u036f]/gu, "").toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+/u, "").slice(0, 40).replace(/-+$/u, "");
1606
+ var publicProfile = (profile) => ({
1607
+ name: profile.name,
1608
+ user_id: profile.userId,
1609
+ organization_id: profile.organizationId,
1610
+ organization_name: profile.organizationName
1611
+ });
1612
+ var profileLimitAction = (commandPrefix) => `Nothing was saved: this environment already keeps ${String(MAX_PROFILES)} saved logins, the most one computer holds. Remove one you no longer need with '${commandPrefix} logout --profile-name NAME --json' ('${commandPrefix} profile list --json' shows them), then retry.`;
1613
+ var ProfileSelectionError = class extends Error {
1614
+ code;
1615
+ exitCode;
1616
+ suggestedAction;
1617
+ constructor(code, exitCode, suggestedAction) {
1618
+ super(`${code}: ${suggestedAction}`);
1619
+ this.code = code;
1620
+ this.exitCode = exitCode;
1621
+ this.suggestedAction = suggestedAction;
1622
+ this.name = "ProfileSelectionError";
1623
+ }
1624
+ };
1625
+ var profileSelectionError = (selection, request, commandPrefix, selector) => {
1626
+ const choose = selector === "cli" ? `pass --profile-name NAME (see '${commandPrefix} profile list --json') or set OHMYHOST_PROFILE for this process` : "pass profile_name (see profile_list) or set OHMYHOST_PROFILE in this MCP server's configuration";
1627
+ const login = `${commandPrefix} login${request.organizationId === void 0 ? "" : ` --organization ${request.organizationId}`}${request.userId === void 0 ? "" : ` --user ${request.userId}`}${(request.name ?? request.bound) === void 0 ? "" : ` --profile-name ${String(request.name ?? request.bound)}`} --json`;
1628
+ if (selection.outcome === "none")
1629
+ return new ProfileSelectionError("authentication_required", 5, `No login is saved on this computer. Run '${login}'.`);
1630
+ if (selection.outcome === "not_found")
1631
+ return new ProfileSelectionError("profile_not_found", 5, `No saved login matches ${describeRequest(request)}. Run '${login}' to add it, signing in as that user; '${commandPrefix} profile list --json' shows the saved logins.`);
1632
+ if (selection.outcome === "ambiguous")
1633
+ return new ProfileSelectionError("profile_selection_required", 2, `Several saved logins could run this: ${selection.candidates.map((profile2) => profile2.name).join(", ")}. Ask the customer which account to use, then ${choose}.`);
1634
+ if (selection.field === "binding")
1635
+ return new ProfileSelectionError("profile_context_mismatch", 2, `This call names profile ${String(request.name)}, but the process is bound to OHMYHOST_PROFILE=${String(request.bound)}. Use the bound profile or start a process for the other one.`);
1636
+ const profile = selection.profile;
1637
+ return new ProfileSelectionError("profile_context_mismatch", 2, selection.field === "organization" ? `Profile ${String(profile?.name)} is signed in to organization ${String(profile?.organizationId ?? "none")}, but this targets organization ${String(request.organizationId)}. Use the profile of that organization or change the target; nothing was sent.` : `Profile ${String(profile?.name)} belongs to user ${String(profile?.userId)}, not ${String(request.userId)}. Use that user's profile; nothing was sent.`);
1638
+ };
1639
+ var describeRequest = (request) => {
1640
+ const parts = [
1641
+ ...(request.name ?? request.bound) === void 0 ? [] : [`profile ${String(request.name ?? request.bound)}`],
1642
+ ...request.organizationId === void 0 ? [] : [`organization ${request.organizationId}`],
1643
+ ...request.userId === void 0 ? [] : [`user ${request.userId}`]
1644
+ ];
1645
+ return parts.length === 0 ? "this request" : parts.join(", ");
1646
+ };
1647
+ var describeProfile = (profile) => `profile ${profile.name} (user ${profile.userId}, ${profile.organizationId === null ? "no organization" : `organization ${profile.organizationName === null ? "" : `${profile.organizationName} `}${profile.organizationId}`})`;
1648
+
1304
1649
  // apps/product-cli/dist/device-token-expiry.js
1305
1650
  var DeviceTokenExpiryError = class extends Error {
1306
1651
  constructor() {
@@ -1375,46 +1720,6 @@ var resolveDeviceTokenExpiresAt = (accessToken, expiresInSeconds, now) => {
1375
1720
  return new Date(jwtExpiryMilliseconds).toISOString();
1376
1721
  };
1377
1722
 
1378
- // apps/product-cli/dist/workspace.js
1379
- var InteractiveSessionRequiredError = class extends Error {
1380
- constructor() {
1381
- super("Workspace selection requires an interactive login");
1382
- this.name = "InteractiveSessionRequiredError";
1383
- }
1384
- };
1385
- var chooseWorkspace = (organizations, requested) => {
1386
- if (requested !== void 0) {
1387
- const match = organizations.find((organization) => organization.id === requested);
1388
- return match === void 0 ? { outcome: "not_a_member", organizations } : { outcome: "selected", organization: match };
1389
- }
1390
- if (organizations.length === 0)
1391
- return { outcome: "no_workspace" };
1392
- const only = organizations[0];
1393
- if (organizations.length === 1 && only !== void 0)
1394
- return { outcome: "selected", organization: only };
1395
- return { outcome: "choice_required", organizations };
1396
- };
1397
- var selectWorkspace = async (session, organization, signal) => {
1398
- const stored = await session.store.read();
1399
- if (stored === void 0)
1400
- throw new InteractiveSessionRequiredError();
1401
- const refreshed = await session.refresher.refresh(stored.refreshToken, signal, organization.workos_id);
1402
- await session.store.replace({
1403
- ...refreshed,
1404
- workosOrganizationId: organization.workos_id,
1405
- organizationId: organization.id
1406
- });
1407
- };
1408
- var selectedWorkspaceId = async (session) => (await session.store.read())?.organizationId ?? null;
1409
- var workspaceOf = (credential) => ({
1410
- ...credential.workosOrganizationId === void 0 ? {} : { workosOrganizationId: credential.workosOrganizationId },
1411
- ...credential.organizationId === void 0 ? {} : { organizationId: credential.organizationId }
1412
- });
1413
- var publicWorkspace = (organization) => ({
1414
- id: organization.id,
1415
- name: organization.name
1416
- });
1417
-
1418
1723
  // packages/contracts/dist/feedback.js
1419
1724
  var FEEDBACK_HISTORY_PAGE_SIZE = 25;
1420
1725
  var FEEDBACK_REPLY_MAX_LENGTH = 2e3;
@@ -1648,7 +1953,7 @@ var PROBLEM_POLICY = {
1648
1953
  github_connection_required: {
1649
1954
  status: 409,
1650
1955
  retryable: false,
1651
- action: "Run 'ohmyhost github connect --organization ULID --idempotency-key KEY --json' for this workspace, complete its browser link, then repeat the original source link."
1956
+ action: "This workspace has no GitHub connection yet. Run 'ohmyhost github connect --organization ULID --idempotency-key KEY --json' for it, complete its browser link, then repeat the original source link. If the project belongs to another account, use that account's login instead; error.acting_as names the login this ran as."
1652
1957
  },
1653
1958
  github_connection_revoked: {
1654
1959
  status: 409,
@@ -1722,7 +2027,11 @@ var PROBLEM_POLICY = {
1722
2027
  retryable: false,
1723
2028
  action: "Run 'ohmyhost organization list --json' and select one with 'ohmyhost organization use --organization ULID --json', or create the first workspace."
1724
2029
  },
1725
- resource_not_found: { status: 404, retryable: false, action: "Check the resource identifier." },
2030
+ resource_not_found: {
2031
+ status: 404,
2032
+ retryable: false,
2033
+ action: "Check the resource identifier and the account: a project is visible only to its own organization, and error.acting_as names the login this ran as. Use the login of the account that owns it."
2034
+ },
1726
2035
  idempotency_key_reused: {
1727
2036
  status: 409,
1728
2037
  retryable: false,
@@ -1859,6 +2168,11 @@ var PROBLEM_POLICY = {
1859
2168
  retryable: false,
1860
2169
  action: "Finish database provisioning, then request the export again. No backup was accepted."
1861
2170
  },
2171
+ powered_by_flag_required: {
2172
+ status: 409,
2173
+ retryable: false,
2174
+ action: "Keep the flag on, remove the custom domain with 'ohmyhost domain paid delete', or buy Paid, then switch the flag off."
2175
+ },
1862
2176
  rate_limited: { status: 429, retryable: true, action: "Retry after the server delay." },
1863
2177
  insufficient_organization_credits: {
1864
2178
  status: 402,
@@ -1873,7 +2187,7 @@ var PROBLEM_POLICY = {
1873
2187
  paid_plan_required: {
1874
2188
  status: 402,
1875
2189
  retryable: false,
1876
- action: "Ask the organization Owner to activate Paid through billing checkout, then retry. Top-ups and signup credits do not activate Paid."
2190
+ action: "Ask the organization Owner to activate Paid through billing checkout, then retry. Top-ups and signup credits do not activate Paid. For a website domain alone, a Free workspace may instead show the powered-by flag on the project."
1877
2191
  },
1878
2192
  interactive_login_required: {
1879
2193
  status: 403,
@@ -1981,6 +2295,18 @@ var parseDevAccessState = (value) => {
1981
2295
  throw new ResponseContractError();
1982
2296
  return input;
1983
2297
  };
2298
+ var parsePoweredByFlag = (value) => {
2299
+ const input = exactRecord2(value, ["project_id", "enabled", "enabled_at"]);
2300
+ if (!isUlid(input["project_id"]) || typeof input["enabled"] !== "boolean" || (input["enabled"] ? !isIsoInstant(input["enabled_at"]) : input["enabled_at"] !== null))
2301
+ throw new ResponseContractError();
2302
+ return input;
2303
+ };
2304
+ var parseOrganizationReferral = (value) => {
2305
+ const input = exactRecord2(value, ["organization_id", "referral_url"]);
2306
+ if (!isUlid(input["organization_id"]) || input["referral_url"] !== `https://ohmyho.st/?r=ref-${String(input["organization_id"]).toLowerCase()}`)
2307
+ throw new ResponseContractError();
2308
+ return input;
2309
+ };
1984
2310
  var parseDevAccessTicket = (value) => {
1985
2311
  const input = exactRecord2(value, [
1986
2312
  "ticket_id",
@@ -2034,7 +2360,7 @@ var parseAccountProfile = (value) => {
2034
2360
  });
2035
2361
  if (new Set(workspaces.map((workspace) => workspace.id)).size !== organizationIds.length)
2036
2362
  throw new ResponseContractError();
2037
- return workspaces;
2363
+ return { userId: input["user_id"], organizations: workspaces };
2038
2364
  };
2039
2365
  var parseSafeProblem = (value) => {
2040
2366
  const input = exactRecord2(value, [
@@ -2072,6 +2398,7 @@ var parseSafeProblem = (value) => {
2072
2398
  var parseOperation = (value) => {
2073
2399
  const input = exactRecord2(value, [
2074
2400
  "blocking_operation_id",
2401
+ "deployment_id",
2075
2402
  "id",
2076
2403
  "state",
2077
2404
  "created_at",
@@ -2087,9 +2414,10 @@ var parseOperation = (value) => {
2087
2414
  const updatedAt = input["updated_at"];
2088
2415
  const result = input["result"];
2089
2416
  const blockingOperationId = input["blocking_operation_id"];
2090
- if (blockingOperationId !== void 0 && (state !== "queued" || !isUlid(blockingOperationId) || blockingOperationId === id3))
2417
+ const deploymentId = input["deployment_id"];
2418
+ if (blockingOperationId !== void 0 && (state !== "queued" || !isUlid(blockingOperationId) || blockingOperationId === id3) || deploymentId !== void 0 && !isUlid(deploymentId))
2091
2419
  throw new ResponseContractError();
2092
- if (!isUlid(id3) || !isOperationState(state) || !isIsoInstant(createdAt) || !isIsoInstant(updatedAt) || result !== void 0 && !isRecord(result)) {
2420
+ if (!isUlid(id3) || !isOperationState(state) || !isIsoInstant(createdAt) || !isIsoInstant(updatedAt) || result !== void 0 && !isRecord2(result)) {
2093
2421
  throw new ResponseContractError();
2094
2422
  }
2095
2423
  let progress;
@@ -2134,6 +2462,7 @@ var parseOperation = (value) => {
2134
2462
  created_at: createdAt,
2135
2463
  updated_at: updatedAt,
2136
2464
  ...blockingOperationId === void 0 ? {} : { blocking_operation_id: blockingOperationId },
2465
+ ...deploymentId === void 0 ? {} : { deployment_id: deploymentId },
2137
2466
  ...progress === void 0 ? {} : { progress },
2138
2467
  ...result === void 0 ? {} : { result },
2139
2468
  ...error === void 0 ? {} : { error },
@@ -2150,12 +2479,12 @@ function parseOperationFailure(value) {
2150
2479
  return safe;
2151
2480
  }
2152
2481
  var exactRecord2 = (value, allowed) => {
2153
- if (!isRecord(value) || Object.keys(value).some((key) => !allowed.includes(key))) {
2482
+ if (!isRecord2(value) || Object.keys(value).some((key) => !allowed.includes(key))) {
2154
2483
  throw new ResponseContractError();
2155
2484
  }
2156
2485
  return value;
2157
2486
  };
2158
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2487
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2159
2488
  var boundedString = (value, minimum, maximum) => typeof value === "string" && value.length >= minimum && value.length <= maximum;
2160
2489
  var isUlid = (value) => typeof value === "string" && /^[0-9A-HJKMNP-TV-Z]{26}$/u.test(value);
2161
2490
  var isIsoInstant = (value) => typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
@@ -2178,6 +2507,79 @@ var isPaidValidationRecord = (value) => {
2178
2507
  return (record5["type"] === "CNAME" || record5["type"] === "TXT") && boundedString(record5["name"], 1, 253) && boundedString(record5["content"], 1, 2048) && ["traffic", "ownership", "ssl", "dcv_delegation"].includes(String(record5["purpose"]));
2179
2508
  };
2180
2509
 
2510
+ // apps/product-cli/dist/workspace.js
2511
+ var InteractiveSessionRequiredError = class extends Error {
2512
+ constructor() {
2513
+ super("Workspace selection requires an interactive login");
2514
+ this.name = "InteractiveSessionRequiredError";
2515
+ }
2516
+ };
2517
+ var ProfileOrganizationFixedError = class extends Error {
2518
+ profile;
2519
+ organization;
2520
+ constructor(profile, organization) {
2521
+ super("The saved login is bound to another organization");
2522
+ this.profile = profile;
2523
+ this.organization = organization;
2524
+ this.name = "ProfileOrganizationFixedError";
2525
+ }
2526
+ };
2527
+ var AccountVerificationError = class extends Error {
2528
+ constructor() {
2529
+ super("The signed-in account does not match the requested one");
2530
+ this.name = "AccountVerificationError";
2531
+ }
2532
+ };
2533
+ var chooseWorkspace = (organizations, requested) => {
2534
+ if (requested !== void 0) {
2535
+ const match = organizations.find((organization) => organization.id === requested);
2536
+ return match === void 0 ? { outcome: "not_a_member", organizations } : { outcome: "selected", organization: match };
2537
+ }
2538
+ if (organizations.length === 0)
2539
+ return { outcome: "no_workspace" };
2540
+ const only = organizations[0];
2541
+ if (organizations.length === 1 && only !== void 0)
2542
+ return { outcome: "selected", organization: only };
2543
+ return { outcome: "choice_required", organizations };
2544
+ };
2545
+ var verifiedUserId = (identity) => {
2546
+ const actor = parseCurrentIdentity(identity).actor_id;
2547
+ if (!actor.startsWith("user:") || actor.length === 5)
2548
+ throw new AccountVerificationError();
2549
+ return actor.slice(5);
2550
+ };
2551
+ var assertVerifiedLogin = (identity, userId, organizationId) => {
2552
+ const organizations = parseCurrentIdentity(identity).organization_ids;
2553
+ if (verifiedUserId(identity) !== userId || (organizationId === null ? organizations.length !== 0 : organizations.length !== 1 || organizations[0] !== organizationId))
2554
+ throw new AccountVerificationError();
2555
+ };
2556
+ var selectWorkspace = async (session, organization, signal, identity) => {
2557
+ if (session.profile.organizationId === organization.id)
2558
+ return session.profile;
2559
+ if (session.profile.organizationId !== null)
2560
+ throw new ProfileOrganizationFixedError(session.profile, organization);
2561
+ const stored = await session.store.read();
2562
+ if (stored === void 0)
2563
+ throw new InteractiveSessionRequiredError();
2564
+ const refreshed = await session.refresher.refresh(stored.refreshToken, signal, organization.workos_id);
2565
+ assertVerifiedLogin(await identity(refreshed.accessToken), session.profile.userId, organization.id);
2566
+ const bound = {
2567
+ ...session.profile,
2568
+ organizationId: organization.id,
2569
+ organizationName: organization.name
2570
+ };
2571
+ await session.profiles.save(bound, { ...refreshed, workosOrganizationId: organization.workos_id, organizationId: organization.id }, session.profile);
2572
+ return bound;
2573
+ };
2574
+ var workspaceOf = (credential) => ({
2575
+ ...credential.workosOrganizationId === void 0 ? {} : { workosOrganizationId: credential.workosOrganizationId },
2576
+ ...credential.organizationId === void 0 ? {} : { organizationId: credential.organizationId }
2577
+ });
2578
+ var publicWorkspace = (organization) => ({
2579
+ id: organization.id,
2580
+ name: organization.name
2581
+ });
2582
+
2181
2583
  // apps/product-cli/dist/cli.js
2182
2584
  var NetworkTransportError = class extends Error {
2183
2585
  constructor() {
@@ -2206,14 +2608,16 @@ var PublicResponseTooLargeError = class extends Error {
2206
2608
  var CLI_VERSION = package_default.version;
2207
2609
  var HELP_USAGE = Object.freeze({
2208
2610
  init: "ohmyhost init [--directory PATH] [--root PATH] [--project SLUG] [--region us|eu] [--dry-run] --json (pass the project's hosting region so storage.jurisdiction matches it; us when omitted)",
2209
- login: "ohmyhost login [--organization ULID] --json",
2210
- logout: "ohmyhost logout [--revoke] --json",
2211
- whoami: "ohmyhost whoami --json",
2611
+ login: "ohmyhost login [--organization ULID] [--user USER_ID] [--profile-name NAME] --json (adds one saved login; nothing is saved unless the browser signed in as that user and organization)",
2612
+ logout: "ohmyhost logout [--profile-name NAME] [--revoke] --json (removes only the selected saved login)",
2613
+ whoami: "ohmyhost whoami [--profile-name NAME] --json (the effective user, organization and saved login)",
2614
+ "profile list": "ohmyhost profile list --json (saved logins on this computer: name, user and organization, never a token; pass --profile-name NAME or set OHMYHOST_PROFILE to choose one)",
2212
2615
  "github connect": "ohmyhost github connect --organization ULID --idempotency-key KEY --json (connect once, then link covered repositories without another browser consent)",
2213
2616
  "github status": "ohmyhost github status --organization ULID --json",
2214
2617
  "export create": "ohmyhost export create --project ULID --idempotency-key KEY --stdin --json (password on stdin only; one accepted SQL ZIP per project per 24 hours)",
2215
2618
  "export get": "ohmyhost export get EXPORT_ULID --project ULID --json (poll the original job; signed ZIP download lasts 24 hours)",
2216
2619
  "credits account": "ohmyhost credits account --organization ULID --json",
2620
+ "referral link": "ohmyhost referral link --organization ULID --json (the workspace's link to share; a new user who signs up through it starts with a free Paid month and 1,000 credits, and their first payment gives this workspace the same)",
2217
2621
  "credits balance": "ohmyhost credits balance --organization ULID --json",
2218
2622
  "billing recharge get": "ohmyhost billing recharge get --organization ULID --json",
2219
2623
  "billing recharge set": "ohmyhost billing recharge set --organization ULID --enabled true|false --monthly-limit-minor CENTS --revision N --idempotency-key KEY [--consent off_session_v1] --json (explicit Owner consent required before enabling)",
@@ -2223,9 +2627,9 @@ var HELP_USAGE = Object.freeze({
2223
2627
  "credits usage": "ohmyhost credits usage --organization ULID --month YYYY-MM [--cursor ULID] --json",
2224
2628
  "budget get": "ohmyhost budget get --project ULID --json",
2225
2629
  "budget set": "ohmyhost budget set --project ULID --credits NUMBER|none [--mode continue|stop] --idempotency-key KEY --json",
2226
- "organization create": "ohmyhost organization create --name NAME --idempotency-key KEY [--source SOURCE] --json (SOURCE is optional attribution from a link's r value; the new workspace is selected immediately)",
2227
- "organization list": "ohmyhost organization list --json (the workspaces you belong to and the selected one)",
2228
- "organization use": "ohmyhost organization use --organization ULID --json",
2630
+ "organization create": "ohmyhost organization create --name NAME --idempotency-key KEY [--source SOURCE] --json (SOURCE is optional attribution from a link's r value; a login without organization is bound to the new workspace, another login keeps its own)",
2631
+ "organization list": "ohmyhost organization list [--profile-name NAME] --json (the workspaces of the chosen login's user and the one that login is scoped to)",
2632
+ "organization use": "ohmyhost organization use --organization ULID [--profile-name NAME] --json (binds a login that has no organization yet; another organization needs its own login)",
2229
2633
  "operation get": "ohmyhost operation get OPERATION_ULID --json",
2230
2634
  "operation reconcile": "ohmyhost operation reconcile OPERATION_ULID --idempotency-key KEY --yes --json",
2231
2635
  "token create": "ohmyhost token create --organization ULID --name NAME --idempotency-key KEY --out .env.local --json",
@@ -2243,6 +2647,8 @@ var HELP_USAGE = Object.freeze({
2243
2647
  "project dev-share rotate": "ohmyhost project dev-share rotate --project ULID --idempotency-key KEY --yes --json",
2244
2648
  "project dev-share revoke": "ohmyhost project dev-share revoke --project ULID --idempotency-key KEY --yes --json",
2245
2649
  "project dev-access mode": "ohmyhost project dev-access mode --project ULID --mode protected|public --idempotency-key KEY --yes --json",
2650
+ "project flag status": "ohmyhost project flag status --project ULID --json",
2651
+ "project flag set": "ohmyhost project flag set --project ULID --enabled true|false --idempotency-key KEY --json (shows the small Powered by ohmyho.st flag on the production site; while it shows, a Free workspace may connect its own domain without the domain fee and each Paid period adds 250 credits)",
2246
2652
  "project handle check": "ohmyhost project handle check --handle HANDLE --json (is this address free? answers with a reason and free alternatives; the address becomes HANDLE.check.omh.st)",
2247
2653
  "project handle set": "ohmyhost project handle set --project ULID --handle HANDLE --if-match ETAG --idempotency-key KEY --json (moves the project to a free address; the old one stops working and anyone may claim it)",
2248
2654
  "database compute set": "ohmyhost database compute set --project ULID --environment dev|prod --profile standard|performance --idempotency-key KEY --yes [--wait] --json\n standard: Free 0.25 CU/1 GB/60s idle, Paid 0.5 CU/2 GB/60s idle. performance: Paid 1 CU/4 GB/300s idle, 2.5x database compute credits per equal active minute; longer idle time also consumes credits.",
@@ -2275,7 +2681,7 @@ var HELP_USAGE = Object.freeze({
2275
2681
  delete: "ohmyhost delete --project ULID --if-match ETAG --confirmation-token TOKEN --idempotency-key KEY --yes --json",
2276
2682
  "secret list": "ohmyhost secret list --project ULID --environment ENVIRONMENT_ULID --json",
2277
2683
  "function runs": "ohmyhost function runs --project ULID --environment ENVIRONMENT_ULID [--limit 1-100] --json",
2278
- "secret set": `printf '%s' "$SECRET_VALUE" | ohmyhost secret set NAME --project ULID --environment ENVIRONMENT_ULID --idempotency-key KEY --stdin [--wait] --json`,
2684
+ "secret set": `printf '%s' "$SECRET_VALUE" | ohmyhost secret set NAME --project ULID --environment ENVIRONMENT_ULID --idempotency-key KEY [--profile-user USER_ID --profile-organization ULID | --token-user USER_ID --token-organization ULID] --stdin [--wait] --json (with --profile-user and --profile-organization the saved login that runs it must belong to that user and organization; with --token-user and --token-organization it runs only with an OHMYHOST_TOKEN of that user and organization, never with a saved login)`,
2279
2685
  "secret delete": "ohmyhost secret delete NAME --project ULID --environment ENVIRONMENT_ULID --idempotency-key KEY [--wait] --json",
2280
2686
  "mail setup": "ohmyhost mail setup --project ULID --environment ULID --domain DOMAIN --sending true --receiving false --idempotency-key KEY --json",
2281
2687
  "mail status": "ohmyhost mail status --project ULID --environment ULID --json",
@@ -2291,6 +2697,14 @@ var HELP_USAGE = Object.freeze({
2291
2697
  });
2292
2698
  var HELP_COMMANDS = Object.freeze(Object.entries(HELP_USAGE).map(([command, usage]) => Object.freeze({ command, usage })));
2293
2699
  var SIGN_IN_WINDOW_MS = 30 * 60 * 1e3;
2700
+ var ACCOUNT_SENSITIVE_PROBLEMS = /* @__PURE__ */ new Set([
2701
+ "resource_not_found",
2702
+ "forbidden",
2703
+ "organization_required",
2704
+ "github_connection_required",
2705
+ "github_connection_revoked",
2706
+ "repository_not_installed"
2707
+ ]);
2294
2708
 
2295
2709
  // packages/sdk-ts/dist/generated/core/bodySerializer.gen.js
2296
2710
  var jsonBodySerializer = {
@@ -3176,6 +3590,23 @@ var getGithubOrganizationConnection = (parameters, options) => {
3176
3590
  ...params
3177
3591
  });
3178
3592
  };
3593
+ var getOrganizationReferral = (parameters, options) => {
3594
+ const params = buildClientParams([parameters], [
3595
+ {
3596
+ args: [
3597
+ { in: "headers", key: "X-Request-Id" },
3598
+ { in: "path", key: "organization_id" }
3599
+ ]
3600
+ }
3601
+ ]);
3602
+ return (options?.client ?? client).get({
3603
+ responseStyle: "data",
3604
+ security: [{ scheme: "bearer", type: "http" }],
3605
+ url: "/v1/organizations/{organization_id}/referral",
3606
+ ...options,
3607
+ ...params
3608
+ });
3609
+ };
3179
3610
  var getOrganizationAccount = (parameters, options) => {
3180
3611
  const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "organization_id" }] }]);
3181
3612
  return (options?.client ?? client).get({
@@ -3780,6 +4211,47 @@ var setProjectDevAccessMode = (parameters, options) => {
3780
4211
  }
3781
4212
  });
3782
4213
  };
4214
+ var getProjectPoweredByFlag = (parameters, options) => {
4215
+ const params = buildClientParams([parameters], [
4216
+ {
4217
+ args: [
4218
+ { in: "headers", key: "X-Request-Id" },
4219
+ { in: "path", key: "project_id" }
4220
+ ]
4221
+ }
4222
+ ]);
4223
+ return (options?.client ?? client).get({
4224
+ responseStyle: "data",
4225
+ security: [{ scheme: "bearer", type: "http" }],
4226
+ url: "/v1/projects/{project_id}/powered-by-flag",
4227
+ ...options,
4228
+ ...params
4229
+ });
4230
+ };
4231
+ var setProjectPoweredByFlag = (parameters, options) => {
4232
+ const params = buildClientParams([parameters], [
4233
+ {
4234
+ args: [
4235
+ { in: "headers", key: "X-Request-Id" },
4236
+ { in: "headers", key: "Idempotency-Key" },
4237
+ { in: "path", key: "project_id" },
4238
+ { key: "setPoweredByFlagRequest", map: "body" }
4239
+ ]
4240
+ }
4241
+ ]);
4242
+ return (options?.client ?? client).put({
4243
+ responseStyle: "data",
4244
+ security: [{ scheme: "bearer", type: "http" }],
4245
+ url: "/v1/projects/{project_id}/powered-by-flag",
4246
+ ...options,
4247
+ ...params,
4248
+ headers: {
4249
+ "Content-Type": "application/json",
4250
+ ...options?.headers,
4251
+ ...params.headers
4252
+ }
4253
+ });
4254
+ };
3783
4255
  var createProjectDevAccessTicket = (parameters, options) => {
3784
4256
  const params = buildClientParams([parameters], [
3785
4257
  {
@@ -3855,31 +4327,7 @@ var getProjectSource = (parameters, options) => {
3855
4327
  ...params
3856
4328
  });
3857
4329
  };
3858
- var createCloudflareDnsAuthorization = (parameters, options) => {
3859
- const params = buildClientParams([parameters], [
3860
- {
3861
- args: [
3862
- { in: "headers", key: "X-Request-Id" },
3863
- { in: "headers", key: "Idempotency-Key" },
3864
- { in: "path", key: "project_id" },
3865
- { key: "createCloudflareDnsAuthorizationRequest", map: "body" }
3866
- ]
3867
- }
3868
- ]);
3869
- return (options?.client ?? client).post({
3870
- responseStyle: "data",
3871
- security: [{ scheme: "bearer", type: "http" }],
3872
- url: "/v1/projects/{project_id}/cloudflare-dns/authorization",
3873
- ...options,
3874
- ...params,
3875
- headers: {
3876
- "Content-Type": "application/json",
3877
- ...options?.headers,
3878
- ...params.headers
3879
- }
3880
- });
3881
- };
3882
- var getCloudflareDnsAuthorizationStatus = (parameters, options) => {
4330
+ var getProjectSourceAutoDeploy = (parameters, options) => {
3883
4331
  const params = buildClientParams([parameters], [
3884
4332
  {
3885
4333
  args: [
@@ -3891,27 +4339,26 @@ var getCloudflareDnsAuthorizationStatus = (parameters, options) => {
3891
4339
  return (options?.client ?? client).get({
3892
4340
  responseStyle: "data",
3893
4341
  security: [{ scheme: "bearer", type: "http" }],
3894
- url: "/v1/projects/{project_id}/cloudflare-dns",
4342
+ url: "/v1/projects/{project_id}/source/auto-deploy",
3895
4343
  ...options,
3896
4344
  ...params
3897
4345
  });
3898
4346
  };
3899
- var changeProjectHandle = (parameters, options) => {
4347
+ var configureProjectSourceAutoDeploy = (parameters, options) => {
3900
4348
  const params = buildClientParams([parameters], [
3901
4349
  {
3902
4350
  args: [
3903
4351
  { in: "headers", key: "X-Request-Id" },
3904
4352
  { in: "headers", key: "Idempotency-Key" },
3905
- { in: "headers", key: "If-Match" },
3906
4353
  { in: "path", key: "project_id" },
3907
- { in: "body", key: "handle" }
4354
+ { key: "configureSourceAutoDeployRequest", map: "body" }
3908
4355
  ]
3909
4356
  }
3910
4357
  ]);
3911
4358
  return (options?.client ?? client).put({
3912
4359
  responseStyle: "data",
3913
4360
  security: [{ scheme: "bearer", type: "http" }],
3914
- url: "/v1/projects/{project_id}/handle",
4361
+ url: "/v1/projects/{project_id}/source/auto-deploy",
3915
4362
  ...options,
3916
4363
  ...params,
3917
4364
  headers: {
@@ -3921,13 +4368,97 @@ var changeProjectHandle = (parameters, options) => {
3921
4368
  }
3922
4369
  });
3923
4370
  };
3924
- var planPaidProjectDomain = (parameters, options) => {
4371
+ var createCloudflareDnsAuthorization = (parameters, options) => {
3925
4372
  const params = buildClientParams([parameters], [
3926
4373
  {
3927
4374
  args: [
3928
4375
  { in: "headers", key: "X-Request-Id" },
4376
+ { in: "headers", key: "Idempotency-Key" },
3929
4377
  { in: "path", key: "project_id" },
3930
- { key: "paidDomainRequest", map: "body" }
4378
+ { key: "createCloudflareDnsAuthorizationRequest", map: "body" }
4379
+ ]
4380
+ }
4381
+ ]);
4382
+ return (options?.client ?? client).post({
4383
+ responseStyle: "data",
4384
+ security: [{ scheme: "bearer", type: "http" }],
4385
+ url: "/v1/projects/{project_id}/cloudflare-dns/authorization",
4386
+ ...options,
4387
+ ...params,
4388
+ headers: {
4389
+ "Content-Type": "application/json",
4390
+ ...options?.headers,
4391
+ ...params.headers
4392
+ }
4393
+ });
4394
+ };
4395
+ var getCloudflareDnsAuthorizationStatus = (parameters, options) => {
4396
+ const params = buildClientParams([parameters], [
4397
+ {
4398
+ args: [
4399
+ { in: "headers", key: "X-Request-Id" },
4400
+ { in: "path", key: "project_id" }
4401
+ ]
4402
+ }
4403
+ ]);
4404
+ return (options?.client ?? client).get({
4405
+ responseStyle: "data",
4406
+ security: [{ scheme: "bearer", type: "http" }],
4407
+ url: "/v1/projects/{project_id}/cloudflare-dns",
4408
+ ...options,
4409
+ ...params
4410
+ });
4411
+ };
4412
+ var changeProjectHandle = (parameters, options) => {
4413
+ const params = buildClientParams([parameters], [
4414
+ {
4415
+ args: [
4416
+ { in: "headers", key: "X-Request-Id" },
4417
+ { in: "headers", key: "Idempotency-Key" },
4418
+ { in: "headers", key: "If-Match" },
4419
+ { in: "path", key: "project_id" },
4420
+ { in: "body", key: "handle" }
4421
+ ]
4422
+ }
4423
+ ]);
4424
+ return (options?.client ?? client).put({
4425
+ responseStyle: "data",
4426
+ security: [{ scheme: "bearer", type: "http" }],
4427
+ url: "/v1/projects/{project_id}/handle",
4428
+ ...options,
4429
+ ...params,
4430
+ headers: {
4431
+ "Content-Type": "application/json",
4432
+ ...options?.headers,
4433
+ ...params.headers
4434
+ }
4435
+ });
4436
+ };
4437
+ var configureProjectDomains = (parameters, options) => {
4438
+ const params = buildClientParams([parameters], [
4439
+ {
4440
+ args: [
4441
+ { in: "headers", key: "X-Request-Id" },
4442
+ { in: "headers", key: "Idempotency-Key" },
4443
+ { in: "path", key: "project_id" }
4444
+ ]
4445
+ }
4446
+ ]);
4447
+ return (options?.client ?? client).put({
4448
+ responseStyle: "data",
4449
+ security: [{ scheme: "bearer", type: "http" }],
4450
+ url: "/v1/projects/{project_id}/domains",
4451
+ ...options,
4452
+ ...params
4453
+ });
4454
+ };
4455
+ var planPaidProjectDomain = (parameters, options) => {
4456
+ const params = buildClientParams([parameters], [
4457
+ {
4458
+ args: [
4459
+ { in: "headers", key: "X-Request-Id" },
4460
+ { in: "path", key: "project_id" },
4461
+ { key: "paidDomainRequest", map: "body" }
3931
4462
  ]
3932
4463
  }
3933
4464
  ]);
@@ -4113,6 +4644,24 @@ var getDeploymentLogs = (parameters, options) => {
4113
4644
  ...params
4114
4645
  });
4115
4646
  };
4647
+ var streamDeploymentLogs = (parameters, options) => {
4648
+ const params = buildClientParams([parameters], [
4649
+ {
4650
+ args: [
4651
+ { in: "headers", key: "X-Request-Id" },
4652
+ { in: "path", key: "project_id" },
4653
+ { in: "path", key: "deployment_id" }
4654
+ ]
4655
+ }
4656
+ ]);
4657
+ return (options?.client ?? client).sse.get({
4658
+ responseStyle: "data",
4659
+ security: [{ scheme: "bearer", type: "http" }],
4660
+ url: "/v1/projects/{project_id}/deployments/{deployment_id}/logs/events",
4661
+ ...options,
4662
+ ...params
4663
+ });
4664
+ };
4116
4665
  var getDatabaseCompute = (parameters, options) => {
4117
4666
  const params = buildClientParams([parameters], [
4118
4667
  {
@@ -4398,6 +4947,32 @@ var deleteEnvironmentSecret = (parameters, options) => {
4398
4947
  ...params
4399
4948
  });
4400
4949
  };
4950
+ var putEnvironmentSecret = (parameters, options) => {
4951
+ const params = buildClientParams([parameters], [
4952
+ {
4953
+ args: [
4954
+ { in: "headers", key: "X-Request-Id" },
4955
+ { in: "headers", key: "Idempotency-Key" },
4956
+ { in: "path", key: "project_id" },
4957
+ { in: "path", key: "environment_id" },
4958
+ { in: "path", key: "secret_name" },
4959
+ { key: "putEnvironmentSecretRequest", map: "body" }
4960
+ ]
4961
+ }
4962
+ ]);
4963
+ return (options?.client ?? client).put({
4964
+ responseStyle: "data",
4965
+ security: [{ scheme: "bearer", type: "http" }],
4966
+ url: "/v1/projects/{project_id}/environments/{environment_id}/secrets/{secret_name}",
4967
+ ...options,
4968
+ ...params,
4969
+ headers: {
4970
+ "Content-Type": "application/json",
4971
+ ...options?.headers,
4972
+ ...params.headers
4973
+ }
4974
+ });
4975
+ };
4401
4976
  var getOperation = (parameters, options) => {
4402
4977
  const params = buildClientParams([parameters], [
4403
4978
  {
@@ -4765,120 +5340,526 @@ function assertManagedMailResponse(value, input) {
4765
5340
  if (typeof a["id"] !== "string" || !/^[A-Za-z0-9_-]{1,128}$/u.test(a["id"]) || a["filename"] !== null && typeof a["filename"] !== "string" || typeof a["content_type"] !== "string" || a["download_path"] !== `/v1/projects/${input.projectId}/environments/${input.environmentId}/mail/messages/${input.messageId}/attachments/${a["id"]}`)
4766
5341
  invalid3();
4767
5342
  }
4768
- }
4769
- function object2(v) {
4770
- if (typeof v !== "object" || v === null || Array.isArray(v))
4771
- invalid3();
4772
- return v;
4773
- }
4774
- function exact3(v, required, optional = []) {
4775
- if (required.some((k) => !(k in v)) || Object.keys(v).some((k) => !required.includes(k) && !optional.includes(k)))
4776
- invalid3();
4777
- }
4778
- function id2(v) {
4779
- if (typeof v !== "string" || !ULID.test(v))
4780
- invalid3();
4781
- }
4782
- function domain(v) {
4783
- if (typeof v !== "string" || !/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/u.test(v))
4784
- invalid3();
4785
- }
4786
- function time(v) {
4787
- if (typeof v !== "string" || !Number.isFinite(Date.parse(v)) || new Date(v).toISOString() !== v)
4788
- invalid3();
4789
- }
4790
- function https(v) {
4791
- if (typeof v !== "string" || !v.startsWith("https://"))
4792
- invalid3();
4793
- }
4794
- function records(v) {
4795
- if (!Array.isArray(v))
4796
- invalid3();
4797
- for (const value of v) {
4798
- const r = object2(value);
4799
- exact3(r, ["type", "name", "value", "purpose", "status"], ["priority"]);
4800
- if (!["TXT", "CNAME", "MX"].includes(String(r["type"])) || ["name", "value", "purpose", "status"].some((k) => typeof r[k] !== "string") || r["type"] === "MX" && (!Number.isInteger(r["priority"]) || Number(r["priority"]) < 0 || Number(r["priority"]) > 65535))
4801
- invalid3();
5343
+ }
5344
+ function object2(v) {
5345
+ if (typeof v !== "object" || v === null || Array.isArray(v))
5346
+ invalid3();
5347
+ return v;
5348
+ }
5349
+ function exact3(v, required, optional = []) {
5350
+ if (required.some((k) => !(k in v)) || Object.keys(v).some((k) => !required.includes(k) && !optional.includes(k)))
5351
+ invalid3();
5352
+ }
5353
+ function id2(v) {
5354
+ if (typeof v !== "string" || !ULID.test(v))
5355
+ invalid3();
5356
+ }
5357
+ function domain(v) {
5358
+ if (typeof v !== "string" || !/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/u.test(v))
5359
+ invalid3();
5360
+ }
5361
+ function time(v) {
5362
+ if (typeof v !== "string" || !Number.isFinite(Date.parse(v)) || new Date(v).toISOString() !== v)
5363
+ invalid3();
5364
+ }
5365
+ function https(v) {
5366
+ if (typeof v !== "string" || !v.startsWith("https://"))
5367
+ invalid3();
5368
+ }
5369
+ function records(v) {
5370
+ if (!Array.isArray(v))
5371
+ invalid3();
5372
+ for (const value of v) {
5373
+ const r = object2(value);
5374
+ exact3(r, ["type", "name", "value", "purpose", "status"], ["priority"]);
5375
+ if (!["TXT", "CNAME", "MX"].includes(String(r["type"])) || ["name", "value", "purpose", "status"].some((k) => typeof r[k] !== "string") || r["type"] === "MX" && (!Number.isInteger(r["priority"]) || Number(r["priority"]) < 0 || Number(r["priority"]) > 65535))
5376
+ invalid3();
5377
+ }
5378
+ }
5379
+ function invalid3() {
5380
+ throw new TypeError("Mail response contract invalid");
5381
+ }
5382
+
5383
+ // packages/sdk-ts/dist/managed-mail.js
5384
+ function requestManagedMail(client2, input) {
5385
+ const scope = { project_id: input.projectId, environment_id: input.environmentId };
5386
+ switch (input.action) {
5387
+ case "configure":
5388
+ return configureManagedMailDomain({
5389
+ ...scope,
5390
+ "Idempotency-Key": input.idempotencyKey,
5391
+ managedMailDomainRequest: {
5392
+ domain: input.domain,
5393
+ sending: input.sending,
5394
+ receiving: input.receiving
5395
+ }
5396
+ }, { client: client2 });
5397
+ case "status":
5398
+ return getManagedMailDomain(scope, { client: client2 });
5399
+ case "webhook_set":
5400
+ return prepareManagedMailWebhook({
5401
+ ...scope,
5402
+ "Idempotency-Key": input.idempotencyKey,
5403
+ managedMailWebhookRequest: { url: input.url }
5404
+ }, { client: client2 });
5405
+ case "webhook_verify":
5406
+ return verifyManagedMailWebhook({ ...scope, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
5407
+ case "webhook_disable":
5408
+ return disableManagedMailReceiving({ ...scope, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
5409
+ case "domain_delete":
5410
+ return deleteManagedMailDomain({ ...scope, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
5411
+ case "messages_list":
5412
+ return listManagedMailMessages({ ...scope, ...input.after ? { after: input.after } : {} }, { client: client2 });
5413
+ case "message_get":
5414
+ return getManagedMailMessage({ ...scope, message_id: input.messageId }, { client: client2 });
5415
+ case "message_retry":
5416
+ return retryManagedMailMessage({ ...scope, message_id: input.messageId, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
5417
+ }
5418
+ }
5419
+ async function executeManagedMail(client2, input) {
5420
+ const result = await requestManagedMail(client2, input);
5421
+ assertManagedMailResponse(result, input);
5422
+ return result;
5423
+ }
5424
+
5425
+ // packages/workos-auth-contracts/dist/device-flow.js
5426
+ var WORKOS_AUTHKIT_DEVICE_TOKEN_ENDPOINT = "https://api.workos.com/user_management/authenticate";
5427
+ var allowedDeviceTokenFields = /* @__PURE__ */ new Set([
5428
+ "access_token",
5429
+ "refresh_token",
5430
+ "token_type",
5431
+ "expires_in",
5432
+ "organization_id",
5433
+ "authentication_method",
5434
+ "user"
5435
+ ]);
5436
+ var parseDeviceTokens = (value) => {
5437
+ const input = expectRecord(value);
5438
+ for (const field of Object.keys(input)) {
5439
+ if (!allowedDeviceTokenFields.has(field)) {
5440
+ throw new WorkosContractError("unexpected_device_token_field", "Device Flow response contained an unexpected field");
5441
+ }
5442
+ }
5443
+ const expiresIn = expectPositiveInteger(input["expires_in"], "expires_in", { optional: true });
5444
+ const organizationId = expectString(input["organization_id"], "organization_id", {
5445
+ optional: true
5446
+ });
5447
+ return {
5448
+ accessToken: expectString(input["access_token"], "access_token"),
5449
+ refreshToken: expectString(input["refresh_token"], "refresh_token"),
5450
+ tokenType: expectString(input["token_type"], "token_type", { optional: true }) ?? "Bearer",
5451
+ ...expiresIn === void 0 ? {} : { expiresInSeconds: expiresIn },
5452
+ ...organizationId === void 0 ? {} : { organizationId }
5453
+ };
5454
+ };
5455
+
5456
+ // apps/product-cli/dist/product-api.js
5457
+ var GeneratedSdkProductApi = class {
5458
+ #client;
5459
+ constructor(baseUrl, accessToken, fetch2) {
5460
+ this.#client = createClient({ auth: accessToken, baseUrl, fetch: fetch2, throwOnError: true });
5461
+ }
5462
+ async changeDatabaseCompute(input) {
5463
+ return changeDatabaseCompute({
5464
+ project_id: input.projectId,
5465
+ environment: input.environment,
5466
+ profile: input.profile,
5467
+ confirm: true,
5468
+ "Idempotency-Key": input.idempotencyKey
5469
+ }, { client: this.#client });
5470
+ }
5471
+ async getDatabaseCompute(input) {
5472
+ const value = await getDatabaseCompute({ project_id: input.projectId, environment: input.environment }, { client: this.#client });
5473
+ assertDatabaseCompute(value);
5474
+ if (value.project_id !== input.projectId || value.environment !== input.environment)
5475
+ throw new TypeError("Compute response scope mismatch");
5476
+ return value;
5477
+ }
5478
+ async getProjectContext(projectId) {
5479
+ const result = await getProjectContext({ project_id: projectId }, { client: this.#client });
5480
+ assertProjectContext(result);
5481
+ if (result.project_id !== projectId)
5482
+ throw new TypeError("Project context scope mismatch");
5483
+ return result;
5484
+ }
5485
+ async setProjectNotes(input) {
5486
+ const result = await setProjectNotes({
5487
+ project_id: input.projectId,
5488
+ markdown: input.markdown,
5489
+ expected_version: input.expectedVersion,
5490
+ "Idempotency-Key": input.idempotencyKey
5491
+ }, { client: this.#client });
5492
+ assertProjectNotesReceipt(result);
5493
+ return result;
5494
+ }
5495
+ createProjectExport(input) {
5496
+ return createProjectExport({
5497
+ project_id: input.projectId,
5498
+ password: input.password,
5499
+ "Idempotency-Key": input.idempotencyKey
5500
+ }, { client: this.#client });
5501
+ }
5502
+ async getProjectExport(input) {
5503
+ const result = await getProjectExport({ project_id: input.projectId, export_id: input.exportId }, { client: this.#client });
5504
+ assertProjectExport(result);
5505
+ return result;
5506
+ }
5507
+ async getCurrentIdentity() {
5508
+ return getCurrentIdentity({}, { client: this.#client });
5509
+ }
5510
+ async getAccountProfile() {
5511
+ return parseAccountProfile(await getAccountProfile({ client: this.#client }));
5512
+ }
5513
+ getOrganizationReferral(organizationId) {
5514
+ return getOrganizationReferral({ organization_id: organizationId }, { client: this.#client });
5515
+ }
5516
+ getOrganizationAccount(organizationId) {
5517
+ return getOrganizationAccount({ organization_id: organizationId }, { client: this.#client });
5518
+ }
5519
+ getOrganizationCredits(organizationId) {
5520
+ return getOrganizationCredits({ organization_id: organizationId }, { client: this.#client });
5521
+ }
5522
+ async createUserApiKey(input) {
5523
+ return parseUserApiKeyCreation(await createUserApiKey({
5524
+ organization_id: input.organizationId,
5525
+ name: input.name,
5526
+ "Idempotency-Key": input.idempotencyKey
5527
+ }, { client: this.#client }));
5528
+ }
5529
+ async listUserApiKeys(input) {
5530
+ return parseUserApiKeyPage(await listUserApiKeys({ organization_id: input.organizationId, ...input.after ? { after: input.after } : {} }, { client: this.#client }));
5531
+ }
5532
+ async revokeUserApiKey(input) {
5533
+ await revokeUserApiKey({ organization_id: input.organizationId, key_id: input.keyId }, { client: this.#client });
5534
+ }
5535
+ createBillingCheckout(input) {
5536
+ return createBillingCheckout({
5537
+ organization_id: input.organizationId,
5538
+ offer: input.offer,
5539
+ packs: input.packs,
5540
+ "Idempotency-Key": input.idempotencyKey
5541
+ }, { client: this.#client });
5542
+ }
5543
+ getBillingCheckout(input) {
5544
+ return getBillingCheckout({ organization_id: input.organizationId, checkout_id: input.checkoutId }, { client: this.#client });
5545
+ }
5546
+ getBillingRecharge(organizationId) {
5547
+ return getBillingRecharge({ organization_id: organizationId }, { client: this.#client });
5548
+ }
5549
+ configureBillingRecharge(input) {
5550
+ return configureBillingRecharge({
5551
+ organization_id: input.organizationId,
5552
+ "Idempotency-Key": input.idempotencyKey,
5553
+ enabled: input.enabled,
5554
+ monthly_limit_minor: input.monthlyLimitMinor,
5555
+ consent: input.consent,
5556
+ revision: input.revision
5557
+ }, { client: this.#client });
5558
+ }
5559
+ createBillingPortal(organizationId) {
5560
+ return createBillingPortal({ organization_id: organizationId, body: {} }, { client: this.#client });
5561
+ }
5562
+ getOrganizationCreditUsage(input) {
5563
+ return getOrganizationCreditUsage({
5564
+ organization_id: input.organizationId,
5565
+ month: input.month,
5566
+ ...input.cursor ? { cursor: input.cursor } : {}
5567
+ }, { client: this.#client });
5568
+ }
5569
+ getProjectCreditBudget(projectId) {
5570
+ return getProjectCreditBudget({ project_id: projectId }, { client: this.#client });
5571
+ }
5572
+ setProjectCreditBudget(input) {
5573
+ return setProjectCreditBudget({
5574
+ project_id: input.projectId,
5575
+ "Idempotency-Key": input.idempotencyKey,
5576
+ setProjectCreditBudgetRequest: { amount_micros: input.amountMicros, mode: input.mode }
5577
+ }, { client: this.#client });
5578
+ }
5579
+ createOrganization(input) {
5580
+ return createOrganization({
5581
+ "Idempotency-Key": input.idempotencyKey,
5582
+ createOrganizationRequest: {
5583
+ name: input.name,
5584
+ ...input.signupSource === void 0 ? {} : { signup_source: input.signupSource }
5585
+ }
5586
+ }, { client: this.#client });
5587
+ }
5588
+ async listProjects(input) {
5589
+ return listProjects(input, { client: this.#client });
5590
+ }
5591
+ async changeProjectHandle(input) {
5592
+ return changeProjectHandle({
5593
+ project_id: input.projectId,
5594
+ "Idempotency-Key": input.idempotencyKey,
5595
+ "If-Match": input.ifMatch,
5596
+ handle: input.handle
5597
+ }, { client: this.#client });
5598
+ }
5599
+ async checkProjectHandle(input) {
5600
+ return getProjectHandleAvailability({ handle: input.handle }, { client: this.#client });
5601
+ }
5602
+ async getProjectStatus(projectId) {
5603
+ return getProjectStatus({ project_id: projectId }, { client: this.#client });
5604
+ }
5605
+ async writeProjectDatabase(input) {
5606
+ return writeProjectDatabase({
5607
+ project_id: input.projectId,
5608
+ "Idempotency-Key": input.idempotencyKey,
5609
+ projectDatabaseWriteRequest: {
5610
+ environment: input.environment,
5611
+ statement: input.statement,
5612
+ parameters: [...input.parameters]
5613
+ }
5614
+ }, { client: this.#client });
5615
+ }
5616
+ async queryProjectDatabase(input) {
5617
+ return queryProjectDatabase({
5618
+ project_id: input.projectId,
5619
+ projectDatabaseQueryRequest: {
5620
+ environment: input.environment,
5621
+ statement: input.statement,
5622
+ parameters: [...input.parameters]
5623
+ }
5624
+ }, { client: this.#client });
5625
+ }
5626
+ /** The connection URI exists only in this response; nothing in the CLI persists it. */
5627
+ async createProjectDatabaseAccess(input) {
5628
+ const value = await createProjectDatabaseAccess({
5629
+ project_id: input.projectId,
5630
+ projectDatabaseAccessRequest: {
5631
+ environment: input.environment,
5632
+ mode: input.mode,
5633
+ ttl_seconds: input.ttlSeconds,
5634
+ label: input.label
5635
+ }
5636
+ }, { client: this.#client });
5637
+ assertProjectDatabaseAccessCredential(value);
5638
+ return value;
5639
+ }
5640
+ async listProjectDatabaseAccess(input) {
5641
+ const value = await listProjectDatabaseAccess({
5642
+ project_id: input.projectId,
5643
+ ...input.environment === void 0 ? {} : { environment: input.environment }
5644
+ }, { client: this.#client });
5645
+ assertProjectDatabaseAccessPage(value);
5646
+ return value;
5647
+ }
5648
+ async revokeProjectDatabaseAccess(input) {
5649
+ const value = await revokeProjectDatabaseAccess({ project_id: input.projectId, access_id: input.accessId }, { client: this.#client });
5650
+ assertProjectDatabaseAccess(value);
5651
+ return value;
5652
+ }
5653
+ async planPaidDomain(input) {
5654
+ return planPaidProjectDomain({ project_id: input.projectId, paidDomainRequest: { hostname: input.hostname } }, { client: this.#client });
5655
+ }
5656
+ async applyPaidDomain(input) {
5657
+ return applyPaidProjectDomain({
5658
+ "Idempotency-Key": input.idempotencyKey,
5659
+ project_id: input.projectId,
5660
+ paidDomainRequest: { hostname: input.hostname }
5661
+ }, { client: this.#client });
5662
+ }
5663
+ async getPaidDomain(projectId) {
5664
+ return getPaidProjectDomain({ project_id: projectId }, { client: this.#client });
5665
+ }
5666
+ async deletePaidDomain(input) {
5667
+ return deletePaidProjectDomain({
5668
+ "Idempotency-Key": input.idempotencyKey,
5669
+ project_id: input.projectId,
5670
+ paidDomainRequest: { hostname: input.hostname }
5671
+ }, { client: this.#client });
5672
+ }
5673
+ async ensureDevShareLink(projectId) {
5674
+ return ensureProjectDevShareLink({ project_id: projectId }, { client: this.#client });
5675
+ }
5676
+ async rotateDevShareLink(projectId, idempotencyKey2) {
5677
+ return rotateProjectDevShareLink({ project_id: projectId, "Idempotency-Key": idempotencyKey2 }, { client: this.#client });
5678
+ }
5679
+ async revokeDevShareLink(projectId, idempotencyKey2) {
5680
+ return revokeProjectDevShareLink({ project_id: projectId, "Idempotency-Key": idempotencyKey2 }, { client: this.#client });
5681
+ }
5682
+ async setDevAccessMode(projectId, mode, idempotencyKey2) {
5683
+ return setProjectDevAccessMode({
5684
+ project_id: projectId,
5685
+ "Idempotency-Key": idempotencyKey2,
5686
+ setDevAccessModeRequest: { mode }
5687
+ }, { client: this.#client });
5688
+ }
5689
+ async getPoweredByFlag(projectId) {
5690
+ return getProjectPoweredByFlag({ project_id: projectId }, { client: this.#client });
5691
+ }
5692
+ async setPoweredByFlag(projectId, enabled, idempotencyKey2) {
5693
+ return setProjectPoweredByFlag({
5694
+ project_id: projectId,
5695
+ "Idempotency-Key": idempotencyKey2,
5696
+ setPoweredByFlagRequest: { enabled }
5697
+ }, { client: this.#client });
5698
+ }
5699
+ async createDevAccessTicket(projectId) {
5700
+ return createProjectDevAccessTicket({ project_id: projectId }, { client: this.#client });
5701
+ }
5702
+ async submitFeedback(input) {
5703
+ return parseFeedbackReceipt(await submitFeedback({ "Idempotency-Key": input.idempotencyKey, feedbackSubmission: input.report }, { client: this.#client }));
5704
+ }
5705
+ async getFeedback(feedbackId, cursor) {
5706
+ return parseFeedbackStatus(await getFeedback({ feedback_id: feedbackId, ...cursor === void 0 ? {} : { cursor } }, { client: this.#client }));
5707
+ }
5708
+ async createProject(input) {
5709
+ return createProject({
5710
+ "Idempotency-Key": input.idempotencyKey,
5711
+ createProjectRequest: {
5712
+ organization_id: input.organizationId,
5713
+ name: input.name,
5714
+ ...input.dataMode === void 0 ? {} : { data_mode: input.dataMode },
5715
+ ...input.devAccessMode === void 0 ? {} : { dev_access_mode: input.devAccessMode },
5716
+ ...input.region === void 0 ? {} : { region: input.region }
5717
+ }
5718
+ }, { client: this.#client });
5719
+ }
5720
+ async getOperation(operationId) {
5721
+ return getOperation({ operation_id: operationId }, { client: this.#client });
5722
+ }
5723
+ async reconcileOperation(input) {
5724
+ return reconcileOperation({
5725
+ "Idempotency-Key": input.idempotencyKey,
5726
+ operation_id: input.operationId
5727
+ }, { client: this.#client });
5728
+ }
5729
+ async connectGithub(input) {
5730
+ return connectGithubOrganization({
5731
+ organization_id: input.organizationId,
5732
+ "Idempotency-Key": input.idempotencyKey,
5733
+ connectGithubOrganizationRequest: {}
5734
+ }, { client: this.#client });
5735
+ }
5736
+ async getGithubConnection(organizationId) {
5737
+ return getGithubOrganizationConnection({ organization_id: organizationId }, { client: this.#client });
5738
+ }
5739
+ async linkSource(input) {
5740
+ return linkProjectSource({
5741
+ "Idempotency-Key": input.idempotencyKey,
5742
+ project_id: input.projectId,
5743
+ linkProjectSourceRequest: {
5744
+ repository_owner: input.repositoryOwner,
5745
+ repository_name: input.repositoryName
5746
+ }
5747
+ }, { client: this.#client });
5748
+ }
5749
+ async configureAutoDeploy(input) {
5750
+ return configureProjectSourceAutoDeploy({
5751
+ "Idempotency-Key": input.idempotencyKey,
5752
+ project_id: input.projectId,
5753
+ configureSourceAutoDeployRequest: { branch: input.branch, enabled: input.enabled }
5754
+ }, { client: this.#client });
5755
+ }
5756
+ async getAutoDeploy(projectId) {
5757
+ return getProjectSourceAutoDeploy({ project_id: projectId }, { client: this.#client });
5758
+ }
5759
+ async authorizeCloudflareDns(input) {
5760
+ return createCloudflareDnsAuthorization({
5761
+ "Idempotency-Key": input.idempotencyKey,
5762
+ project_id: input.projectId,
5763
+ createCloudflareDnsAuthorizationRequest: { zone: input.zone }
5764
+ }, { client: this.#client });
5765
+ }
5766
+ async getCloudflareDnsStatus(projectId) {
5767
+ return getCloudflareDnsAuthorizationStatus({ project_id: projectId }, { client: this.#client });
5768
+ }
5769
+ async applyProjectDomains(input) {
5770
+ return configureProjectDomains({ "Idempotency-Key": input.idempotencyKey, project_id: input.projectId }, { client: this.#client });
5771
+ }
5772
+ async planDeployment(input) {
5773
+ return planDeployment({
5774
+ project_id: input.projectId,
5775
+ planDeploymentRequest: { commit_sha: input.commitSha, environment: input.environment }
5776
+ }, { client: this.#client });
5777
+ }
5778
+ async createDeployment(input) {
5779
+ return createDeployment({
5780
+ "Idempotency-Key": input.idempotencyKey,
5781
+ project_id: input.projectId,
5782
+ createDeploymentRequest: { plan_id: input.planId }
5783
+ }, { client: this.#client });
5784
+ }
5785
+ async getDeployment(input) {
5786
+ return getDeployment({ project_id: input.projectId, deployment_id: input.deploymentId }, { client: this.#client });
5787
+ }
5788
+ async streamOperationEvents(operationId, signal) {
5789
+ return (await streamOperationEvents({ operation_id: operationId }, { client: this.#client, signal, headers: { Accept: "text/event-stream" } })).stream;
5790
+ }
5791
+ async getDeploymentLogs(input) {
5792
+ return getDeploymentLogs({
5793
+ project_id: input.projectId,
5794
+ deployment_id: input.deploymentId,
5795
+ limit: input.limit,
5796
+ ...input.cursor === void 0 ? {} : { cursor: input.cursor }
5797
+ }, { client: this.#client });
5798
+ }
5799
+ async streamDeploymentLogs(input, signal) {
5800
+ return (await streamDeploymentLogs({ project_id: input.projectId, deployment_id: input.deploymentId }, { client: this.#client, signal, headers: { Accept: "text/event-stream" } })).stream;
5801
+ }
5802
+ async planRollback(input) {
5803
+ return planDeploymentRollback({ project_id: input.projectId, target_deployment_id: input.deploymentId }, { client: this.#client });
5804
+ }
5805
+ async rollback(input) {
5806
+ return rollbackDeployment({
5807
+ "Idempotency-Key": input.idempotencyKey,
5808
+ "If-Match": input.ifMatch,
5809
+ "X-Confirmation-Token": input.confirmationToken,
5810
+ project_id: input.projectId,
5811
+ target_deployment_id: input.deploymentId
5812
+ }, { client: this.#client });
5813
+ }
5814
+ async planPromotion(input) {
5815
+ return planDeploymentPromotion({ project_id: input.projectId, source_deployment_id: input.sourceDeploymentId }, { client: this.#client });
4802
5816
  }
4803
- }
4804
- function invalid3() {
4805
- throw new TypeError("Mail response contract invalid");
4806
- }
4807
-
4808
- // packages/sdk-ts/dist/managed-mail.js
4809
- function requestManagedMail(client2, input) {
4810
- const scope = { project_id: input.projectId, environment_id: input.environmentId };
4811
- switch (input.action) {
4812
- case "configure":
4813
- return configureManagedMailDomain({
4814
- ...scope,
4815
- "Idempotency-Key": input.idempotencyKey,
4816
- managedMailDomainRequest: {
4817
- domain: input.domain,
4818
- sending: input.sending,
4819
- receiving: input.receiving
4820
- }
4821
- }, { client: client2 });
4822
- case "status":
4823
- return getManagedMailDomain(scope, { client: client2 });
4824
- case "webhook_set":
4825
- return prepareManagedMailWebhook({
4826
- ...scope,
4827
- "Idempotency-Key": input.idempotencyKey,
4828
- managedMailWebhookRequest: { url: input.url }
4829
- }, { client: client2 });
4830
- case "webhook_verify":
4831
- return verifyManagedMailWebhook({ ...scope, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
4832
- case "webhook_disable":
4833
- return disableManagedMailReceiving({ ...scope, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
4834
- case "domain_delete":
4835
- return deleteManagedMailDomain({ ...scope, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
4836
- case "messages_list":
4837
- return listManagedMailMessages({ ...scope, ...input.after ? { after: input.after } : {} }, { client: client2 });
4838
- case "message_get":
4839
- return getManagedMailMessage({ ...scope, message_id: input.messageId }, { client: client2 });
4840
- case "message_retry":
4841
- return retryManagedMailMessage({ ...scope, message_id: input.messageId, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
5817
+ async promote(input) {
5818
+ return promoteDeployment({
5819
+ "Idempotency-Key": input.idempotencyKey,
5820
+ "If-Match": input.ifMatch,
5821
+ "X-Confirmation-Token": input.confirmationToken,
5822
+ project_id: input.projectId,
5823
+ source_deployment_id: input.sourceDeploymentId
5824
+ }, { client: this.#client });
4842
5825
  }
4843
- }
4844
- async function executeManagedMail(client2, input) {
4845
- const result = await requestManagedMail(client2, input);
4846
- assertManagedMailResponse(result, input);
4847
- return result;
4848
- }
4849
-
4850
- // packages/workos-auth-contracts/dist/user-api-keys.js
4851
- var WORKOS_USER_API_KEY_PATTERN = /^sk_[A-Za-z0-9_-]{20,128}$/u;
4852
-
4853
- // packages/workos-auth-contracts/dist/device-flow.js
4854
- var WORKOS_AUTHKIT_DEVICE_TOKEN_ENDPOINT = "https://api.workos.com/user_management/authenticate";
4855
- var allowedDeviceTokenFields = /* @__PURE__ */ new Set([
4856
- "access_token",
4857
- "refresh_token",
4858
- "token_type",
4859
- "expires_in",
4860
- "organization_id",
4861
- "authentication_method",
4862
- "user"
4863
- ]);
4864
- var parseDeviceTokens = (value) => {
4865
- const input = expectRecord(value);
4866
- for (const field of Object.keys(input)) {
4867
- if (!allowedDeviceTokenFields.has(field)) {
4868
- throw new WorkosContractError("unexpected_device_token_field", "Device Flow response contained an unexpected field");
4869
- }
5826
+ async planDelete(projectId) {
5827
+ return planProjectDeletion({ project_id: projectId }, { client: this.#client });
5828
+ }
5829
+ async deleteProject(input) {
5830
+ return deleteProject({
5831
+ "Idempotency-Key": input.idempotencyKey,
5832
+ "If-Match": input.ifMatch,
5833
+ "X-Confirmation-Token": input.confirmationToken,
5834
+ project_id: input.projectId
5835
+ }, { client: this.#client });
5836
+ }
5837
+ async listEnvironmentSecrets(input) {
5838
+ return listEnvironmentSecrets({ project_id: input.projectId, environment_id: input.environmentId }, { client: this.#client });
5839
+ }
5840
+ async listFunctionRuns(input) {
5841
+ return listFunctionRuns({ project_id: input.projectId, environment_id: input.environmentId, limit: input.limit }, { client: this.#client });
5842
+ }
5843
+ async putEnvironmentSecret(input) {
5844
+ return putEnvironmentSecret({
5845
+ "Idempotency-Key": input.idempotencyKey,
5846
+ project_id: input.projectId,
5847
+ environment_id: input.environmentId,
5848
+ secret_name: input.name,
5849
+ putEnvironmentSecretRequest: { value: input.value }
5850
+ }, { client: this.#client });
5851
+ }
5852
+ async deleteEnvironmentSecret(input) {
5853
+ return deleteEnvironmentSecret({
5854
+ "Idempotency-Key": input.idempotencyKey,
5855
+ project_id: input.projectId,
5856
+ environment_id: input.environmentId,
5857
+ secret_name: input.name
5858
+ }, { client: this.#client });
5859
+ }
5860
+ managedMail(input) {
5861
+ return executeManagedMail(this.#client, input);
4870
5862
  }
4871
- const expiresIn = expectPositiveInteger(input["expires_in"], "expires_in", { optional: true });
4872
- const organizationId = expectString(input["organization_id"], "organization_id", {
4873
- optional: true
4874
- });
4875
- return {
4876
- accessToken: expectString(input["access_token"], "access_token"),
4877
- refreshToken: expectString(input["refresh_token"], "refresh_token"),
4878
- tokenType: expectString(input["token_type"], "token_type", { optional: true }) ?? "Bearer",
4879
- ...expiresIn === void 0 ? {} : { expiresInSeconds: expiresIn },
4880
- ...organizationId === void 0 ? {} : { organizationId }
4881
- };
4882
5863
  };
4883
5864
 
4884
5865
  // apps/product-cli/dist/repository-init.js
@@ -4942,33 +5923,95 @@ function environmentToken(environment2 = {}) {
4942
5923
  throw new ProductApiAuthenticationError("invalid_environment_token");
4943
5924
  return value;
4944
5925
  }
5926
+ function environmentProfile(environment2 = {}) {
5927
+ const value = environment2.OHMYHOST_PROFILE;
5928
+ if (value === void 0 || value === "")
5929
+ return void 0;
5930
+ if (!isProfileName(value))
5931
+ throw new ProductApiAuthenticationError("invalid_profile_binding");
5932
+ return value;
5933
+ }
5934
+ var profileRequest = (input, bound) => ({
5935
+ ...input?.name === void 0 ? {} : { name: input.name },
5936
+ ...bound === void 0 ? {} : { bound },
5937
+ ...input?.organizationId === void 0 ? {} : { organizationId: input.organizationId },
5938
+ ...input?.userId === void 0 ? {} : { userId: input.userId }
5939
+ });
4945
5940
  var createAuthenticatedProductTransport = async (signal, overrides = {}) => {
4946
- const profile = resolveProductCliEnvironment(overrides.environment);
5941
+ const platform = resolveProductCliEnvironment(overrides.environment);
4947
5942
  const apiToken = environmentToken(overrides.environment);
5943
+ const bound = environmentProfile(overrides.environment);
5944
+ const baseUrl = overrides.productApiOrigin ?? platform.apiOrigin;
4948
5945
  const boundedFetch = createBoundedFetch(overrides.fetch ?? fetch, signal, REQUEST_TIMEOUT_MS, MAX_PUBLIC_RESPONSE_BYTES);
4949
- if (apiToken !== void 0)
5946
+ const registry = overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform.credentialService);
5947
+ const binding = overrides.request?.bindOrganization === true;
5948
+ const request = profileRequest(overrides.request, bound);
5949
+ if (apiToken !== void 0) {
5950
+ await verifyEnvironmentTokenContext({
5951
+ token: apiToken,
5952
+ request: binding ? profileRequest({}, bound) : request,
5953
+ registry,
5954
+ baseUrl,
5955
+ fetch: boundedFetch
5956
+ });
4950
5957
  return {
4951
- baseUrl: overrides.productApiOrigin ?? profile.apiOrigin,
5958
+ baseUrl,
4952
5959
  accessToken: apiToken,
4953
5960
  fetch: boundedFetch,
4954
- // A user API token carries a fixed scope; there is no session that could select a workspace.
4955
- workspace: void 0
5961
+ workspace: void 0,
5962
+ context: { credential: "environment_token", profile: null }
4956
5963
  };
4957
- const store = overrides.credentialStore ?? new NativeNapiKeyringCredentialStore(void 0, profile.credentialService);
4958
- const stored = await store.read();
4959
- if (stored === void 0)
4960
- throw new ProductApiAuthenticationError("authentication_required");
5964
+ }
4961
5965
  const now = overrides.now ?? (() => /* @__PURE__ */ new Date());
4962
5966
  const refresher = overrides.tokenRefresher ?? new PublicWorkosTokenRefresher(overrides.environment, overrides.fetch, now);
4963
- const credential = await currentCredential(stored, store, signal, now, refresher);
5967
+ const profiles = await loadProfiles(registry, {
5968
+ baseUrl,
5969
+ fetch: boundedFetch,
5970
+ signal,
5971
+ now,
5972
+ refresher
5973
+ });
5974
+ const selection = binding && request.organizationId !== void 0 ? selectProfileForOrganization(profiles, {
5975
+ ...request,
5976
+ organizationId: request.organizationId
5977
+ }) : selectProfile(profiles, request);
5978
+ if (selection.outcome !== "selected")
5979
+ throw profileSelectionError(selection, request, platform.commandPrefix, overrides.selector ?? "cli");
5980
+ const profile = selection.profile;
5981
+ const store = registry.credential(profile);
5982
+ const stored = await store.read();
5983
+ if (stored === void 0)
5984
+ throw missingLogin(profile, platform.commandPrefix);
5985
+ const credential = await currentCredential(stored, store, signal, now, refresher, () => registry.remove(profile));
4964
5986
  return {
4965
- baseUrl: overrides.productApiOrigin ?? profile.apiOrigin,
5987
+ baseUrl,
4966
5988
  accessToken: credential.accessToken,
4967
5989
  fetch: boundedFetch,
4968
- workspace: { store, refresher }
5990
+ workspace: { store, refresher, profiles: registry, profile },
5991
+ context: { credential: "profile", profile }
5992
+ };
5993
+ };
5994
+ var listSavedProfiles = async (signal, overrides = {}) => {
5995
+ const platform = resolveProductCliEnvironment(overrides.environment);
5996
+ const apiToken = environmentToken(overrides.environment);
5997
+ const bound = environmentProfile(overrides.environment);
5998
+ const registry = overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform.credentialService);
5999
+ const now = overrides.now ?? (() => /* @__PURE__ */ new Date());
6000
+ const profiles = apiToken === void 0 ? await loadProfiles(registry, {
6001
+ baseUrl: overrides.productApiOrigin ?? platform.apiOrigin,
6002
+ fetch: createBoundedFetch(overrides.fetch ?? fetch, signal, REQUEST_TIMEOUT_MS, MAX_PUBLIC_RESPONSE_BYTES),
6003
+ signal,
6004
+ now,
6005
+ refresher: overrides.tokenRefresher ?? new PublicWorkosTokenRefresher(overrides.environment, overrides.fetch, now)
6006
+ }) : await registry.list();
6007
+ return {
6008
+ profiles: profiles.map(publicProfile),
6009
+ bound: bound ?? null,
6010
+ environment_token: apiToken !== void 0
4969
6011
  };
4970
6012
  };
4971
- var currentCredential = async (stored, store, signal, now, tokenRefresher) => {
6013
+ var missingLogin = (profile, commandPrefix) => new ProfileSelectionError("authentication_required", 5, `The saved login ${profile.name} has no credential on this computer. Run '${commandPrefix} login${profile.organizationId === null ? "" : ` --organization ${profile.organizationId}`} --profile-name ${profile.name} --json'.`);
6014
+ var currentCredential = async (stored, store, signal, now, tokenRefresher, forget) => {
4972
6015
  const expiresAt = stored.expiresAt === void 0 ? NaN : Date.parse(stored.expiresAt);
4973
6016
  if (Number.isFinite(expiresAt) && expiresAt > now().getTime() + REQUEST_TIMEOUT_MS) {
4974
6017
  return stored;
@@ -4984,12 +6027,73 @@ var currentCredential = async (stored, store, signal, now, tokenRefresher) => {
4984
6027
  return carried;
4985
6028
  } catch (error) {
4986
6029
  if (error instanceof TokenRefreshRejectedError) {
4987
- await store.clear();
6030
+ await forget();
4988
6031
  throw new ProductApiAuthenticationError("authentication_required");
4989
6032
  }
4990
6033
  throw new ProductApiAuthenticationError("token_refresh_failed");
4991
6034
  }
4992
6035
  };
6036
+ var loadProfiles = async (registry, context) => {
6037
+ const legacy = registry.legacy();
6038
+ const stored = await legacy.read();
6039
+ if (stored !== void 0)
6040
+ await migrateLegacyLogin(registry, legacy, stored, context);
6041
+ return registry.list();
6042
+ };
6043
+ var migrateLegacyLogin = async (registry, legacy, stored, context) => {
6044
+ let credential;
6045
+ try {
6046
+ credential = await currentCredential(stored, legacy, context.signal, context.now, context.refresher, () => legacy.clear());
6047
+ } catch (error) {
6048
+ if (error instanceof ProductApiAuthenticationError && error.code === "authentication_required")
6049
+ return;
6050
+ throw error;
6051
+ }
6052
+ const api = new GeneratedSdkProductApi(context.baseUrl, credential.accessToken, context.fetch);
6053
+ let identity;
6054
+ try {
6055
+ identity = await api.getCurrentIdentity();
6056
+ } catch (error) {
6057
+ if (!isProblem(error, "unauthenticated"))
6058
+ throw error;
6059
+ return;
6060
+ }
6061
+ const userId = verifiedUserId(identity);
6062
+ const organizationId = parseCurrentIdentity(identity).organization_ids[0] ?? null;
6063
+ const organizationName = organizationId === null ? null : (await api.getAccountProfile()).organizations.find((organization) => organization.id === organizationId)?.name ?? null;
6064
+ const login = { userId, organizationId, organizationName };
6065
+ await registry.save({ name: defaultProfileName(await registry.list(), login), ...login }, credential);
6066
+ await legacy.clear();
6067
+ };
6068
+ var isProblem = (error, code) => typeof error === "object" && error !== null && "code" in error && error.code === code;
6069
+ var verifyEnvironmentTokenContext = async (input) => {
6070
+ const { request } = input;
6071
+ const named = request.name ?? request.bound;
6072
+ if (named === void 0 && request.organizationId === void 0 && request.userId === void 0)
6073
+ return null;
6074
+ const refuse = (target) => new ProfileSelectionError("environment_token_context_mismatch", 2, `${target} Unset OHMYHOST_TOKEN to use the saved login, or load the key of that account from its private env file. Nothing was sent.`);
6075
+ if (request.name !== void 0 && request.bound !== void 0 && request.name !== request.bound)
6076
+ throw refuse(`This call names profile ${request.name}, but the process is bound to OHMYHOST_PROFILE=${request.bound}.`);
6077
+ const profile = named === void 0 ? void 0 : (await input.registry.list()).find((saved) => saved.name === named);
6078
+ if (named !== void 0 && profile === void 0)
6079
+ throw refuse(`OHMYHOST_TOKEN overrides saved logins in this process, and no saved login is named ${named}.`);
6080
+ const { userId, organizationId } = await readTokenIdentity(input);
6081
+ const owner = `OHMYHOST_TOKEN belongs to user ${userId} in organization ${organizationId ?? "none"},`;
6082
+ if (profile !== void 0 && (profile.userId !== userId || profile.organizationId !== organizationId))
6083
+ throw refuse(`${owner} but this call names ${describeProfile(profile)}.`);
6084
+ if (request.organizationId !== void 0 && request.organizationId !== organizationId)
6085
+ throw refuse(`${owner} but this call targets organization ${request.organizationId}.`);
6086
+ if (request.userId !== void 0 && request.userId !== userId)
6087
+ throw refuse(`${owner} but this call targets user ${request.userId}.`);
6088
+ return { userId, organizationId };
6089
+ };
6090
+ var readTokenIdentity = async (input) => {
6091
+ const identity = parseCurrentIdentity(await new GeneratedSdkProductApi(input.baseUrl, input.token, input.fetch).getCurrentIdentity());
6092
+ return {
6093
+ userId: identity.actor_id.startsWith("user:") ? identity.actor_id.slice(5) : identity.actor_id,
6094
+ organizationId: identity.organization_ids[0] ?? null
6095
+ };
6096
+ };
4993
6097
  var FetchWorkosPublicTransport = class {
4994
6098
  parentSignal;
4995
6099
  timeoutMs;
@@ -5167,7 +6271,7 @@ var boundedSignal = (parent, timeoutMs) => AbortSignal.any([parent, AbortSignal.
5167
6271
  var isAbortOrTimeout = (error) => error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError");
5168
6272
 
5169
6273
  // apps/mcp/dist/local-client.js
5170
- import { lstat as lstat2, open as open2 } from "node:fs/promises";
6274
+ import { lstat as lstat2, open as open3 } from "node:fs/promises";
5171
6275
  import { isAbsolute } from "node:path";
5172
6276
  var OperationEventsUnavailableError = class extends Error {
5173
6277
  constructor() {
@@ -5177,8 +6281,11 @@ var OperationEventsUnavailableError = class extends Error {
5177
6281
  };
5178
6282
  async function createAuthenticatedLocalMcpClient(signal, overrides = {}) {
5179
6283
  try {
5180
- const transport = await createAuthenticatedProductTransport(signal, overrides);
5181
- return new GeneratedSdkLocalMcpClient(transport.baseUrl, transport.accessToken, transport.fetch, transport.workspace);
6284
+ const transport = await createAuthenticatedProductTransport(signal, {
6285
+ ...overrides,
6286
+ selector: "mcp"
6287
+ });
6288
+ return new GeneratedSdkLocalMcpClient(transport.baseUrl, transport.accessToken, transport.fetch, transport.workspace, transport.context, resolveProductCliEnvironment(overrides.environment).commandPrefix);
5182
6289
  } catch (error) {
5183
6290
  if (!(error instanceof ProductApiAuthenticationError))
5184
6291
  throw error;
@@ -5191,11 +6298,44 @@ async function createAuthenticatedLocalMcpClient(signal, overrides = {}) {
5191
6298
  var GeneratedSdkLocalMcpClient = class {
5192
6299
  #client;
5193
6300
  #workspace;
6301
+ #context;
6302
+ #commandPrefix;
5194
6303
  #rescope;
5195
- constructor(baseUrl, accessToken, fetch2, workspace) {
6304
+ constructor(baseUrl, accessToken, fetch2, workspace, context, commandPrefix = "OHMYHOST_ENVIRONMENT=production ohmyhost") {
5196
6305
  this.#rescope = (token) => createClient({ auth: token, baseUrl, fetch: fetch2, throwOnError: true });
5197
6306
  this.#client = this.#rescope(accessToken);
5198
6307
  this.#workspace = workspace;
6308
+ this.#context = context ?? {
6309
+ credential: workspace === void 0 ? "environment_token" : "profile",
6310
+ profile: workspace?.profile ?? null
6311
+ };
6312
+ this.#commandPrefix = commandPrefix;
6313
+ }
6314
+ actingAs() {
6315
+ return this.#contextReport();
6316
+ }
6317
+ /** Which account this call acts as, without any credential. */
6318
+ #contextReport() {
6319
+ const profile = this.#workspace?.profile ?? this.#context.profile;
6320
+ return {
6321
+ credential: this.#context.credential,
6322
+ profile: profile === null ? null : publicProfile(profile)
6323
+ };
6324
+ }
6325
+ /**
6326
+ * Bind this call's login, which has no organization yet, and continue with its new credential.
6327
+ * A login in another organization keeps it; the provider verifies the account first.
6328
+ */
6329
+ async #bind(organization, signal) {
6330
+ const session = this.#interactiveSession();
6331
+ const bound = await selectWorkspace(session, organization, signal, (token) => getCurrentIdentity({}, { client: this.#rescope(token) }));
6332
+ const store = session.profiles.credential(bound);
6333
+ const credential = await store.read();
6334
+ if (credential === void 0)
6335
+ throw new InteractiveSessionRequiredError();
6336
+ this.#workspace = { ...session, store, profile: bound };
6337
+ this.#client = this.#rescope(credential.accessToken);
6338
+ return bound;
5199
6339
  }
5200
6340
  async changeDatabaseCompute(input) {
5201
6341
  return changeDatabaseCompute({
@@ -5261,13 +6401,15 @@ var GeneratedSdkLocalMcpClient = class {
5261
6401
  async getCurrentIdentity(signal) {
5262
6402
  const identity = await getCurrentIdentity({}, { client: this.#client });
5263
6403
  if (this.#workspace === void 0 || parseCurrentIdentity(identity).organization_ids.length > 0)
5264
- return identity;
6404
+ return { ...identity, context: this.#contextReport() };
5265
6405
  const selection = chooseWorkspace(await this.#readWorkspaces(), void 0);
5266
6406
  if (selection.outcome !== "selected")
5267
- return identity;
5268
- await selectWorkspace(this.#workspace, selection.organization, signal);
5269
- const scoped = (await this.#workspace.store.read())?.accessToken;
5270
- return scoped === void 0 ? identity : getCurrentIdentity({}, { client: this.#rescope(scoped) });
6407
+ return { ...identity, context: this.#contextReport() };
6408
+ await this.#bind(selection.organization, signal);
6409
+ return {
6410
+ ...await getCurrentIdentity({}, { client: this.#client }),
6411
+ context: this.#contextReport()
6412
+ };
5271
6413
  }
5272
6414
  async changeProjectHandle(input) {
5273
6415
  return changeProjectHandle({
@@ -5313,6 +6455,9 @@ var GeneratedSdkLocalMcpClient = class {
5313
6455
  assertOrganizationAccount(account);
5314
6456
  return account;
5315
6457
  }
6458
+ async getOrganizationReferral(organizationId) {
6459
+ return parseOrganizationReferral(await getOrganizationReferral({ organization_id: organizationId }, { client: this.#client }));
6460
+ }
5316
6461
  getOrganizationCredits(organizationId) {
5317
6462
  return getOrganizationCredits({ organization_id: organizationId }, { client: this.#client });
5318
6463
  }
@@ -5380,29 +6525,39 @@ var GeneratedSdkLocalMcpClient = class {
5380
6525
  ...input.signupSource === void 0 ? {} : { signup_source: input.signupSource }
5381
6526
  }
5382
6527
  }, { client: this.#client });
6528
+ const current = this.#interactiveSession().profile;
6529
+ if (current.organizationId !== null && current.organizationId !== created.id)
6530
+ return {
6531
+ organization: { id: created.id, name: created.name },
6532
+ selected: current.organizationId,
6533
+ profile: publicProfile(current),
6534
+ next_action: `Run '${this.#commandPrefix} login --organization ${created.id} --user ${current.userId} --json' to add a login for the new workspace as the same user, then pass its profile_name.`
6535
+ };
5383
6536
  return this.selectOrganization(created.id, signal);
5384
6537
  }
5385
6538
  async listOrganizations() {
5386
6539
  const session = this.#interactiveSession();
5387
6540
  return {
5388
6541
  organizations: (await this.#readWorkspaces()).map(publicWorkspace),
5389
- selected: await selectedWorkspaceId(session)
6542
+ selected: session.profile.organizationId,
6543
+ profile: publicProfile(session.profile)
5390
6544
  };
5391
6545
  }
5392
6546
  async selectOrganization(organizationId, signal) {
5393
- const session = this.#interactiveSession();
6547
+ this.#interactiveSession();
5394
6548
  const selection = chooseWorkspace(await this.#readWorkspaces(), organizationId);
5395
6549
  if (selection.outcome !== "selected")
5396
6550
  throw new Error(`ORGANIZATION_NOT_FOUND:${organizationId}`);
5397
- await selectWorkspace(session, selection.organization, signal);
6551
+ const profile = await this.#bind(selection.organization, signal);
5398
6552
  return {
5399
6553
  organization: publicWorkspace(selection.organization),
5400
- selected: selection.organization.id
6554
+ selected: selection.organization.id,
6555
+ profile: publicProfile(profile)
5401
6556
  };
5402
6557
  }
5403
6558
  async #readWorkspaces() {
5404
6559
  this.#interactiveSession();
5405
- return parseAccountProfile(await getAccountProfile({ client: this.#client }));
6560
+ return parseAccountProfile(await getAccountProfile({ client: this.#client })).organizations;
5406
6561
  }
5407
6562
  #interactiveSession() {
5408
6563
  if (this.#workspace === void 0)
@@ -5431,6 +6586,16 @@ var GeneratedSdkLocalMcpClient = class {
5431
6586
  setDevAccessModeRequest: { mode: input.mode }
5432
6587
  }, { client: this.#client }));
5433
6588
  }
6589
+ async getPoweredByFlag(projectId) {
6590
+ return parsePoweredByFlag(await getProjectPoweredByFlag({ project_id: projectId }, { client: this.#client }));
6591
+ }
6592
+ async setPoweredByFlag(input) {
6593
+ return parsePoweredByFlag(await setProjectPoweredByFlag({
6594
+ project_id: input.projectId,
6595
+ "Idempotency-Key": input.idempotencyKey,
6596
+ setPoweredByFlagRequest: { enabled: input.enabled }
6597
+ }, { client: this.#client }));
6598
+ }
5434
6599
  async rotateDevShareLink(input) {
5435
6600
  return parseDevAccessState(await rotateProjectDevShareLink({ project_id: input.projectId, "Idempotency-Key": input.idempotencyKey }, { client: this.#client }));
5436
6601
  }
@@ -5681,7 +6846,7 @@ async function readExportPasswordFile(path) {
5681
6846
  const before = await lstat2(path);
5682
6847
  if (!before.isFile() || before.isSymbolicLink() || before.size < 1 || before.size > 1024 || process.platform !== "win32" && (before.mode & 63) !== 0)
5683
6848
  throw new Error();
5684
- file = await open2(path, "r");
6849
+ file = await open3(path, "r");
5685
6850
  const after = await file.stat();
5686
6851
  if (after.ino !== before.ino || after.dev !== before.dev || after.size !== before.size)
5687
6852
  throw new Error();
@@ -5702,6 +6867,7 @@ async function readExportPasswordFile(path) {
5702
6867
 
5703
6868
  // apps/mcp/dist/local-server.js
5704
6869
  var identifier2 = external_exports.string().min(1).max(256);
6870
+ var profileName = external_exports.string().regex(/^[a-z0-9][a-z0-9_-]{0,62}$/u).optional().describe("Saved login to act as (profile_list shows them). Needed only when several logins could run this call; OHMYHOST_PROFILE of this server must match it.");
5705
6871
  var idempotencyKey = external_exports.string().min(1).max(256);
5706
6872
  var commitSha = external_exports.string().regex(/^[a-f0-9]{40}$/u);
5707
6873
  var domainName = external_exports.string().regex(/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/u);
@@ -5835,6 +7001,7 @@ function createLocalOhmyhostMcpServer(dependencies) {
5835
7001
  ...input.cursor ? { cursor: input.cursor } : {}
5836
7002
  }));
5837
7003
  registerProductTool(server, dependencies, "organization_account_get", "Owner-only: read the effective Free/Paid plan, its Stripe or granted source, available expiring Free credits and purchased credits that never expire, reservations and next expiry. Granted Paid features do not imply a Stripe subscription or extra monthly Paid allowance. This read grants no access or credits.", external_exports.object({ organization_id: identifier2 }), (client2, input) => client2.getOrganizationAccount(input.organization_id));
7004
+ registerProductTool(server, dependencies, "referral_link_get", "Read the workspace's referral link to share. A new user whose first workspace comes from it starts with a free Paid month and 1,000 credits; that workspace's first payment gives this workspace the same. Any member may read it.", external_exports.object({ organization_id: identifier2 }), (client2, input) => client2.getOrganizationReferral(input.organization_id));
5838
7005
  registerProductTool(server, dependencies, "organization_credits_get", "Read the owner's shared organization credit pool, seven-day grace_started_at/grace_expires_at and published rate_cards. Existing services/domains are not immediately shut down at zero credit; an explicit project stop budget is separate. One credit is 1000000 microcredits. Each rate states credit_micros for units_per_charge; publication alone does not enable billing. active_meters identifies billed sources; provider outages and platform overrun are not customer debt. Works at zero credit.", external_exports.object({ organization_id: identifier2 }), (client2, input) => client2.getOrganizationCredits(input.organization_id));
5839
7006
  registerProductTool(server, dependencies, "project_budget_get", "Read the owner's project UTC-month budget, measured usage and open reservations. A budget never allocates a second wallet.", external_exports.object({ project_id: identifier2 }), (client2, input) => client2.getProjectCreditBudget(input.project_id));
5840
7007
  registerProductTool(server, dependencies, "project_budget_set", "Set an owner's optional monthly project budget in microcredits (1000000 = one credit). continue is the default and uses the org pool after the threshold; stop blocks new billable work. null clears the budget and requires continue. Changing it never resets usage or adds credits. Replay the same key after uncertainty; read the budget again for current usage.", external_exports.object({
@@ -5848,7 +7015,7 @@ function createLocalOhmyhostMcpServer(dependencies) {
5848
7015
  mode: input.mode,
5849
7016
  idempotencyKey: input.idempotency_key
5850
7017
  }), { readOnlyHint: false, destructiveHint: false, idempotentHint: true });
5851
- registerProductTool(server, dependencies, "organization_create", `Create an organization owned by the signed-in user and select it for this machine. Signup is open. signup_source is optional attribution from a link's r parameter, never a secret; omit it when the customer arrived directly. The server decides any once-per-user bonus; do not promise credits for arbitrary sources. Repeat the same name/source/key after uncertainty. No second login is needed: selected names the workspace every later call uses, so continue with identity_get, github_status and github_connect if needed, then projects. An OHMYHOST_TOKEN process cannot do this; it needs ${commandPrefix} login --json.`, external_exports.object({
7018
+ registerProductTool(server, dependencies, "organization_create", `Create an organization owned by the signed-in user. Signup is open. signup_source is optional attribution from a link's r parameter, never a secret; omit it when the customer arrived directly. The server decides any once-per-user bonus; do not promise credits for arbitrary sources. Repeat the same name/source/key after uncertainty. A login that has no organization yet is bound to the new one, so selected names the workspace later calls use; a login already in another organization keeps it and next_action names the login for the new one. Continue with identity_get, github_status and github_connect if needed, then projects. An OHMYHOST_TOKEN process cannot do this; it needs ${commandPrefix} login --json.`, external_exports.object({
5852
7019
  name: external_exports.string().min(1).max(128),
5853
7020
  signup_source: external_exports.string().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/u).optional(),
5854
7021
  idempotency_key: idempotencyKey
@@ -5857,8 +7024,25 @@ function createLocalOhmyhostMcpServer(dependencies) {
5857
7024
  ...input.signup_source === void 0 ? {} : { signupSource: input.signup_source },
5858
7025
  idempotencyKey: input.idempotency_key
5859
7026
  }, signal), mutationAnnotations());
5860
- registerProductTool(server, dependencies, "organization_list", "List the workspaces the signed-in user belongs to and which one this machine currently uses. selected is null while none is chosen; every project and billing call then answers resource_not_found. Ask the customer which workspace to use instead of guessing, then call organization_use.", external_exports.object({}), (client2) => client2.listOrganizations());
5861
- registerProductTool(server, dependencies, "organization_use", `Select one workspace for this machine's stored login, so later calls act inside it. Read organization_list first, then github_status for the selected workspace and github_connect if needed; the identifier is the platform ULID, not the provider identifier. An OHMYHOST_TOKEN process cannot select anything and needs ${commandPrefix} login --json instead.`, external_exports.object({ organization_id: identifier2 }), (client2, input, signal) => client2.selectOrganization(input.organization_id, signal), mutationAnnotations());
7027
+ registerProductTool(server, dependencies, "organization_list", "List the workspaces the chosen login's user belongs to and which one that login is scoped to. selected is null while none is chosen; every project and billing call then answers resource_not_found. Ask the customer which workspace to use instead of guessing, then call organization_use.", external_exports.object({}), (client2) => client2.listOrganizations());
7028
+ registerProductTool(server, dependencies, "organization_use", `Bind a saved login that has no organization yet to one workspace, so later calls act inside it. A login already in another organization keeps it (profile_organization_fixed): add a login for the other workspace as the same user with ${commandPrefix} login --organization ULID --user USER_ID --json. Read organization_list first, then github_status for the selected workspace and github_connect if needed; the identifier is the platform ULID, not the provider identifier. An OHMYHOST_TOKEN process cannot select anything and needs ${commandPrefix} login --json instead.`, external_exports.object({ organization_id: identifier2 }), (client2, input, signal) => client2.selectOrganization(input.organization_id, signal), mutationAnnotations(), (input) => ({
7029
+ ...input.profile_name === void 0 ? {} : { name: input.profile_name },
7030
+ organizationId: input.organization_id,
7031
+ bindOrganization: true
7032
+ }));
7033
+ server.registerTool("profile_list", {
7034
+ description: `List the saved ohmyho.st logins on this computer: each has a name, a user and an organization, never a token. When the customer named a user or organization, pass the matching name as profile_name on later calls; with several logins and no profile_name a call answers profile_selection_required. With no match, the customer adds one with ${commandPrefix} login --organization ULID --user USER_ID --json, which saves nothing if the browser signs in as another account. bound is this server's OHMYHOST_PROFILE; environment_token means OHMYHOST_TOKEN replaces saved logins here.`,
7035
+ inputSchema: external_exports.object({}),
7036
+ annotations: readAnnotations()
7037
+ }, (async (_input, context) => {
7038
+ try {
7039
+ if (dependencies.listProfiles === void 0)
7040
+ throw new CredentialStoreUnavailableError();
7041
+ return toolResult(await dependencies.listProfiles(context.mcpReq.signal));
7042
+ } catch (error) {
7043
+ return toolError(error, dependencies);
7044
+ }
7045
+ }));
5862
7046
  registerProductTool(server, dependencies, "database_query", "Read one owner-authorized Dev or Prod database query (at most 100 rows, five-second timeout). Explicitly choose the environment. Shared data resolves to the same physical database. Application RLS policies apply; use an authorized SQL export when a full archive is required.", external_exports.object({
5863
7047
  project_id: identifier2,
5864
7048
  environment: external_exports.enum(["dev", "prod"]),
@@ -5956,7 +7140,7 @@ function createLocalOhmyhostMcpServer(dependencies) {
5956
7140
  });
5957
7141
  return { revoked: true, key_id: input.key_id };
5958
7142
  }, destructiveAnnotations());
5959
- registerProductTool(server, dependencies, "identity_get", "Get the current ohmyho.st customer/agent identity. This does not authenticate the hosted application's end users or provision a customer AuthKit tenant; application authentication is the customer's chosen integration.", external_exports.object({}), (client2, _input, signal) => client2.getCurrentIdentity(signal));
7143
+ registerProductTool(server, dependencies, "identity_get", "Get the ohmyho.st customer/agent identity this call acts as: user, organization and, in context, the saved login or OHMYHOST_TOKEN that supplied it. Compare it with the user and organization the customer named before any change. This does not authenticate the hosted application's end users or provision a customer AuthKit tenant; application authentication is the customer's chosen integration.", external_exports.object({}), (client2, _input, signal) => client2.getCurrentIdentity(signal));
5960
7144
  registerProductTool(server, dependencies, "project_handle_check", "Check whether a project address is free before offering it to the customer. A handle is one to five lowercase words of letters and digits joined by single hyphens, three to fifty-nine characters, and never a reserved word such as admin, api, dev or www; it becomes <handle>.check.omh.st for Prod and dev-<handle>.check.omh.st for Dev. The answer names why a handle cannot be used and returns free alternatives, so offer one of those instead of guessing again. Check the name here first, then move an existing project onto it with project_handle_set.", external_exports.object({ handle: external_exports.string().min(1).max(120) }), (client2, input) => client2.checkProjectHandle({ handle: input.handle }));
5961
7145
  registerProductTool(server, dependencies, "project_handle_set", "Move a project to an address the customer chose, after project_handle_check said it is free. The Dev and Prod gateways are re-published at the new address and it is stored only once both serve it; the previous address stops answering and anyone may claim it, so a link already shared stops working. Needs the project's current ETag from project_status and one idempotency key; reuse the same key after an uncertain answer. Refused while another operation is running for the project, and refused for an address that is taken, unusable, or the one the project already has. Returns an operation to follow with operation_get.", external_exports.object({
5962
7146
  project_id: identifier2,
@@ -6019,6 +7203,12 @@ function createLocalOhmyhostMcpServer(dependencies) {
6019
7203
  mode: input.mode,
6020
7204
  idempotencyKey: input.idempotency_key
6021
7205
  }), destructiveAnnotations());
7206
+ registerProductTool(server, dependencies, "powered_by_flag_get", 'Read whether the production site shows the opt-in "Powered by ohmyho.st" flag.', external_exports.object({ project_id: identifier2 }), (client2, input) => client2.getPoweredByFlag(input.project_id));
7207
+ registerProductTool(server, dependencies, "powered_by_flag_set", `Owner only, ask the human first: show or hide a small "Powered by ohmyho.st" flag on the right edge of the production site. While it is on, a Free workspace may connect its own domain to this project free of the domain fee, and each Paid period adds 250 credits. Hiding it is refused while a Free workspace's domain depends on it. Replay the same key and value after uncertainty to get the first answer without switching again; every new switch needs a new key.`, external_exports.object({ project_id: identifier2, enabled: external_exports.boolean(), idempotency_key: idempotencyKey }), (client2, input) => client2.setPoweredByFlag({
7208
+ projectId: input.project_id,
7209
+ enabled: input.enabled,
7210
+ idempotencyKey: input.idempotency_key
7211
+ }), mutationAnnotations());
6022
7212
  registerProductTool(server, dependencies, "project_dev_share_link_rotate", "Owner only: replace the persistent Dev link and immediately revoke old links and sessions. Repeat the same key after uncertainty.", external_exports.object({ project_id: identifier2, idempotency_key: idempotencyKey }), (client2, input) => client2.rotateDevShareLink({
6023
7213
  projectId: input.project_id,
6024
7214
  idempotencyKey: input.idempotency_key
@@ -6032,8 +7222,8 @@ function createLocalOhmyhostMcpServer(dependencies) {
6032
7222
  organizationId: input.organization_id,
6033
7223
  idempotencyKey: input.idempotency_key
6034
7224
  }), mutationAnnotations());
6035
- registerProductTool(server, dependencies, "github_status", "Read this workspace's GitHub connection. If not_connected or revoked, an Owner or Admin must run github_connect. A connected installation covers only repositories selected in its settings.", external_exports.object({ organization_id: identifier2 }), (client2, input) => client2.getGithubConnection(input.organization_id));
6036
- registerProductTool(server, dependencies, "source_link", "Link a repository covered by the workspace GitHub connection. Call github_status first and github_connect once if needed. This returns an operation without another browser consent and never starts a build. Wait for that operation before planning a deploy. If repository access is missing, update the installation using the returned settings action, then retry the same project and key. Never ask for an installation ID or provider token.", external_exports.object({
7225
+ registerProductTool(server, dependencies, "github_status", "Read this workspace's GitHub connection. If not_connected or revoked, an Owner or Admin must run github_connect. A connected installation covers only repositories selected in its settings. Each workspace has its own connection, so the same repository can be connected and linked in the workspaces of separate accounts.", external_exports.object({ organization_id: identifier2 }), (client2, input) => client2.getGithubConnection(input.organization_id));
7226
+ registerProductTool(server, dependencies, "source_link", "Link a repository covered by the workspace GitHub connection. Call github_status first and github_connect once if needed. This returns an operation without another browser consent and never starts a build. Wait for that operation before planning a deploy. If repository access is missing, update the installation using the returned settings action, then retry the same project and key. Never ask for an installation ID or provider token. Errors name the account the call ran as (acting_as): resource_not_found means that account cannot see the project (use the login of the account that owns it), github_connection_required means that workspace has no GitHub connection yet, and repository_not_installed means its GitHub App installation does not cover the repository.", external_exports.object({
6037
7227
  project_id: identifier2,
6038
7228
  repository_owner: identifier2,
6039
7229
  repository_name: identifier2,
@@ -6069,7 +7259,7 @@ function createLocalOhmyhostMcpServer(dependencies) {
6069
7259
  ...input.limit === void 0 ? {} : { limit: input.limit }
6070
7260
  }));
6071
7261
  registerProductTool(server, dependencies, "deployment_get", "Get one deployment", external_exports.object({ project_id: identifier2, deployment_id: identifier2 }), (client2, input) => client2.getDeployment({ projectId: input.project_id, deploymentId: input.deployment_id }));
6072
- registerProductTool(server, dependencies, "deployment_logs", "List the newest normalized diagnostics of one deployment (build, control, runtime and function failures with catalog codes). A BUILD_FAILED item carries excerpt: the sanitized tail of the customer's own install/build output, newest lines last, with excerpt_truncated when earlier output was omitted. Read it after build_failed before changing source; scheduled runs are in function_runs_list, not here.", external_exports.object({
7262
+ registerProductTool(server, dependencies, "deployment_logs", "List the newest normalized diagnostics of one deployment (build, control, runtime and function failures with catalog codes). A failed operation_get names the deployment as deployment_id. A BUILD_FAILED item carries excerpt: the sanitized tail of the customer's own install/build output, newest lines last, with excerpt_truncated when earlier output was omitted. A HEALTH_CHECK_FAILED item carries the probed route and status_code and, only for a project owner, excerpt: the head (at most 4096 characters, control characters removed, never a header) of what the app answered. The probe is a cookieless GET that follows no redirect, 5 s per try and up to four tries, so a 3xx or an empty 503 is unhealthy too. Read the item before changing source; scheduled runs are in function_runs_list, not here.", external_exports.object({
6073
7263
  project_id: identifier2,
6074
7264
  deployment_id: identifier2,
6075
7265
  limit: external_exports.number().int().min(1).max(100).default(100)
@@ -6078,7 +7268,7 @@ function createLocalOhmyhostMcpServer(dependencies) {
6078
7268
  deploymentId: input.deployment_id,
6079
7269
  limit: input.limit
6080
7270
  }));
6081
- registerProductTool(server, dependencies, "operation_get", "Get durable operation status and current deployment progress/reconciliation guidance. Follow progress.phase and progress.suggested_action. waiting_for_mail requires mail_domain_status now for the current verification issue and DNS records; publishing means the build completed but runtime preparation/publication is unfinished. Poll this operation after progress.next_poll_after_seconds. A queued/running operation with reconciliation.state=required needs one confirmed operation_reconcile using the same operation; pending means poll this operation after 60 seconds. Never infer deployment success from a completed reconciliation attempt or start another build to recover it.", external_exports.object({ operation_id: identifier2 }), (client2, input) => client2.getOperation(input.operation_id));
7271
+ registerProductTool(server, dependencies, "operation_get", "Get durable operation status and current deployment progress/reconciliation guidance. Follow progress.phase and progress.suggested_action. waiting_for_mail requires mail_domain_status now for the current verification issue and DNS records; publishing means the build completed but runtime preparation/publication is unfinished. Poll this operation after progress.next_poll_after_seconds. A queued/running operation with reconciliation.state=required needs one confirmed operation_reconcile using the same operation; pending means poll this operation after 60 seconds. Never infer deployment success from a completed reconciliation attempt or start another build to recover it. A deploy, promotion or rollback names its deployment_id in every state; when state is failed, read that deployment with deployment_logs and follow error.suggested_action before changing source.", external_exports.object({ operation_id: identifier2 }), (client2, input) => client2.getOperation(input.operation_id));
6082
7272
  registerProductTool(server, dependencies, "operation_logs", "Read available operation events for at most ten seconds, stopping earlier at max_events or a terminal event. This is a bounded snapshot, not a wait for deployment completion. Use operation_get for current progress and keep the same operation. Stream failures are errors, not empty logs.", external_exports.object({
6083
7273
  operation_id: identifier2,
6084
7274
  max_events: external_exports.number().int().min(1).max(100).default(50)
@@ -6251,31 +7441,33 @@ function createLocalOhmyhostMcpServer(dependencies) {
6251
7441
  name: input.name,
6252
7442
  idempotencyKey: input.idempotency_key
6253
7443
  }), destructiveAnnotations());
6254
- server.registerTool("secret_set_command", {
6255
- description: "Return the stdin-only CLI command for setting a secret; the value never enters MCP.",
6256
- inputSchema: external_exports.object({
6257
- project_id: identifier2,
6258
- environment_id: identifier2,
6259
- name: secretName,
6260
- idempotency_key: idempotencyKey
6261
- }),
6262
- annotations: readAnnotations()
6263
- }, (input) => toolResult({
6264
- command: [
6265
- `${commandPrefix} secret set`,
6266
- shellQuote(input.name),
6267
- "--project",
6268
- shellQuote(input.project_id),
6269
- "--environment",
6270
- shellQuote(input.environment_id),
6271
- "--idempotency-key",
6272
- shellQuote(input.idempotency_key),
6273
- "--stdin",
6274
- "--wait",
6275
- "--json"
6276
- ].join(" "),
6277
- instruction: "Pass the secret value on stdin in a local terminal. Never place it in MCP arguments, prompts, command-line arguments, source, or logs."
6278
- }));
7444
+ registerProductTool(server, dependencies, "secret_set_command", "Return the stdin-only CLI command for setting a secret; the value never enters MCP. The call runs as the chosen login and first reads the project, so an account that cannot see it answers resource_not_found. The command names that login with --profile-name plus its user and organization (--profile-user, --profile-organization), so a login of the same name that belongs to anyone else is refused, for example on another computer; a server with OHMYHOST_TOKEN instead names its key's user and organization with --token-user and --token-organization, and the CLI then runs only with an OHMYHOST_TOKEN of that account. Either way the later write runs as exactly this account or not at all (acting_as shows which).", external_exports.object({
7445
+ project_id: identifier2,
7446
+ environment_id: identifier2,
7447
+ name: secretName,
7448
+ idempotency_key: idempotencyKey
7449
+ }), async (client2, input, signal) => {
7450
+ await client2.getProject(input.project_id);
7451
+ const account = await handoverAccount(client2, signal);
7452
+ return {
7453
+ command: [
7454
+ `${commandPrefix} secret set`,
7455
+ shellQuote(input.name),
7456
+ "--project",
7457
+ shellQuote(input.project_id),
7458
+ "--environment",
7459
+ shellQuote(input.environment_id),
7460
+ "--idempotency-key",
7461
+ shellQuote(input.idempotency_key),
7462
+ ...account.flags,
7463
+ "--stdin",
7464
+ "--wait",
7465
+ "--json"
7466
+ ].join(" "),
7467
+ acting_as: client2.actingAs?.() ?? null,
7468
+ instruction: `Pass the secret value on stdin in a local terminal; ${account.runs}. Never place the value or a key in MCP arguments, prompts, command-line arguments, source, or logs.`
7469
+ };
7470
+ });
6279
7471
  registerProductTool(server, dependencies, "rollback_plan", "Plan a rollback", external_exports.object({ project_id: identifier2, deployment_id: identifier2 }), (client2, input) => client2.planRollback({ projectId: input.project_id, deploymentId: input.deployment_id }));
6280
7472
  registerProductTool(server, dependencies, "rollback_execute", "Execute a reviewed rollback", external_exports.object({
6281
7473
  project_id: identifier2,
@@ -6304,11 +7496,20 @@ function createLocalOhmyhostMcpServer(dependencies) {
6304
7496
  }), destructiveAnnotations());
6305
7497
  return server;
6306
7498
  }
6307
- function registerProductTool(server, dependencies, name, description, inputSchema, execute, annotations = readAnnotations()) {
7499
+ function registerProductTool(server, dependencies, name, description, schema, execute, annotations = readAnnotations(), selection = (input) => {
7500
+ const organizationId = input["organization_id"];
7501
+ return {
7502
+ ...input.profile_name === void 0 ? {} : { name: input.profile_name },
7503
+ ...typeof organizationId === "string" ? { organizationId } : {}
7504
+ };
7505
+ }) {
7506
+ const inputSchema = schema.extend({ profile_name: profileName });
6308
7507
  const callback = (async (input, context) => {
6309
7508
  const parsed = inputSchema.parse(input);
7509
+ let client2;
6310
7510
  try {
6311
- const result = toolResult(await execute(await dependencies.authenticatedClient(context.mcpReq.signal), parsed, context.mcpReq.signal));
7511
+ client2 = await dependencies.authenticatedClient(context.mcpReq.signal, selection(parsed));
7512
+ const result = toolResult(await execute(client2, parsed, context.mcpReq.signal));
6312
7513
  const scope = parsed;
6313
7514
  if (typeof scope["project_id"] === "string" && identifier2.safeParse(scope["project_id"]).success)
6314
7515
  result.content.push({
@@ -6320,48 +7521,106 @@ function registerProductTool(server, dependencies, name, description, inputSchem
6320
7521
  });
6321
7522
  return result;
6322
7523
  } catch (error) {
6323
- let safe;
6324
- try {
6325
- safe = error instanceof Error && error.name === "InteractiveSessionRequiredError" ? {
6326
- code: "interactive_login_required",
6327
- retryable: false,
6328
- suggestedAction: `A user API token cannot create, list or select a workspace. Run ${resolveProductCliEnvironment(dependencies.environment).commandPrefix} login --json in a process without OHMYHOST_TOKEN, then retry.`
6329
- } : error instanceof Error && error.message.startsWith("ORGANIZATION_NOT_FOUND:") ? {
6330
- code: "organization_not_found",
6331
- retryable: false,
6332
- suggestedAction: "Call organization_list and choose one of the returned workspace identifiers. Do not create another workspace to recover."
6333
- } : error instanceof OperationEventsUnavailableError ? {
6334
- code: "operation_events_unavailable",
6335
- retryable: true,
6336
- suggestedAction: "Read operation_get for current status, then retry operation_logs for this same operation. Do not submit another deployment to recover logs."
6337
- } : error instanceof Error && error.message.startsWith("EXPORT_PASSWORD_FILE_INVALID:") ? {
6338
- code: "export_password_file_invalid",
6339
- retryable: false,
6340
- suggestedAction: "Choose an existing private UTF-8 password file of 1\u20131024 bytes. The user retains the file and password. Do not paste the password into MCP arguments."
6341
- } : parseSafeProblem(error);
6342
- } catch {
6343
- safe = {
6344
- code: "client_request_failed",
6345
- retryable: false,
6346
- suggestedAction: "Check the connection and run 'ohmyhost login' if authentication expired. Inspect the existing operation before resubmitting any mutation."
6347
- };
6348
- }
6349
- return {
6350
- ...toolResult({
6351
- error: {
6352
- code: safe.code,
6353
- retryable: safe.retryable,
6354
- suggested_action: safe.suggestedAction,
6355
- ...safe.requestId === void 0 ? {} : { request_id: safe.requestId },
6356
- ...safe.retryAfterSeconds === void 0 ? {} : { retry_after_seconds: safe.retryAfterSeconds }
6357
- }
6358
- }),
6359
- isError: true
6360
- };
7524
+ return toolError(error, dependencies, client2?.actingAs?.());
6361
7525
  }
6362
7526
  });
6363
7527
  server.registerTool(name, { description, inputSchema, annotations }, callback);
6364
7528
  }
7529
+ async function handoverAccount(client2, signal) {
7530
+ const profile = client2.actingAs?.()?.profile ?? null;
7531
+ if (profile !== null) {
7532
+ if (profile.organization_id === null)
7533
+ throw new ProfileSelectionError("profile_context_mismatch", 2, `The saved login ${profile.name} has no organization, so no command can be tied to its account. Nothing was sent.`);
7534
+ return {
7535
+ flags: [
7536
+ "--profile-name",
7537
+ shellQuote(profile.name),
7538
+ "--profile-user",
7539
+ shellQuote(profile.user_id),
7540
+ "--profile-organization",
7541
+ shellQuote(profile.organization_id)
7542
+ ],
7543
+ runs: `the command runs only as the saved login it names and only while that login belongs to user ${profile.user_id} in organization ${profile.organization_id}, so a login of the same name that belongs to anyone else, for example on another computer, is refused before anything is read or sent`
7544
+ };
7545
+ }
7546
+ const report = await client2.getCurrentIdentity(signal);
7547
+ const identity = parseCurrentIdentity({
7548
+ actor_id: report.actor_id,
7549
+ organization_ids: report.organization_ids
7550
+ });
7551
+ const userId = identity.actor_id.startsWith("user:") ? identity.actor_id.slice(5) : "";
7552
+ const [organizationId, ...others] = identity.organization_ids;
7553
+ if (!isProfileUserId(userId) || organizationId === void 0 || others.length > 0)
7554
+ throw new ProfileSelectionError("environment_token_context_mismatch", 2, "This server's OHMYHOST_TOKEN does not belong to one user in one organization, so no command can be tied to its account. Nothing was sent.");
7555
+ return {
7556
+ flags: ["--token-user", shellQuote(userId), "--token-organization", shellQuote(organizationId)],
7557
+ runs: `run it only where OHMYHOST_TOKEN holds a key of user ${userId} in organization ${organizationId}, loaded from its private env file; it refuses a saved login and any other account's key before anything is sent`
7558
+ };
7559
+ }
7560
+ function fixedOrganizationAction(error, commandPrefix) {
7561
+ return `This saved login stays in its organization. Add a login for the other workspace with ${commandPrefix} login --organization ${error.organization.id} --user ${error.profile.userId} --json, then pass its profile_name.`;
7562
+ }
7563
+ function toolError(error, dependencies, actingAs) {
7564
+ let safe;
7565
+ try {
7566
+ safe = error instanceof Error && error.name === "ProfileSelectionError" ? {
7567
+ code: error.code,
7568
+ retryable: false,
7569
+ suggestedAction: error.suggestedAction
7570
+ } : error instanceof Error && error.name === "ProfileLimitError" ? {
7571
+ code: "profile_limit_reached",
7572
+ retryable: false,
7573
+ suggestedAction: profileLimitAction(resolveProductCliEnvironment(dependencies.environment).commandPrefix)
7574
+ } : error instanceof Error && error.name === "ProfileOrganizationFixedError" ? {
7575
+ code: "profile_organization_fixed",
7576
+ retryable: false,
7577
+ suggestedAction: fixedOrganizationAction(error, resolveProductCliEnvironment(dependencies.environment).commandPrefix)
7578
+ } : error instanceof Error && error.name === "AccountVerificationError" ? {
7579
+ code: "login_verification_failed",
7580
+ retryable: true,
7581
+ suggestedAction: `The signed-in account could not be verified; nothing was saved. Run ${resolveProductCliEnvironment(dependencies.environment).commandPrefix} login --json again.`
7582
+ } : error instanceof Error && error.name === "CredentialStoreUnavailableError" ? {
7583
+ code: "credential_store_unavailable",
7584
+ retryable: true,
7585
+ suggestedAction: "Unlock or repair this computer's credential store and retry. Saved logins were not changed."
7586
+ } : error instanceof Error && error.name === "InteractiveSessionRequiredError" ? {
7587
+ code: "interactive_login_required",
7588
+ retryable: false,
7589
+ suggestedAction: `A user API token cannot create, list or select a workspace. Run ${resolveProductCliEnvironment(dependencies.environment).commandPrefix} login --json in a process without OHMYHOST_TOKEN, then retry.`
7590
+ } : error instanceof Error && error.message.startsWith("ORGANIZATION_NOT_FOUND:") ? {
7591
+ code: "organization_not_found",
7592
+ retryable: false,
7593
+ suggestedAction: "Call organization_list and choose one of the returned workspace identifiers. Do not create another workspace to recover."
7594
+ } : error instanceof OperationEventsUnavailableError ? {
7595
+ code: "operation_events_unavailable",
7596
+ retryable: true,
7597
+ suggestedAction: "Read operation_get for current status, then retry operation_logs for this same operation. Do not submit another deployment to recover logs."
7598
+ } : error instanceof Error && error.message.startsWith("EXPORT_PASSWORD_FILE_INVALID:") ? {
7599
+ code: "export_password_file_invalid",
7600
+ retryable: false,
7601
+ suggestedAction: "Choose an existing private UTF-8 password file of 1\u20131024 bytes. The user retains the file and password. Do not paste the password into MCP arguments."
7602
+ } : parseSafeProblem(error);
7603
+ } catch {
7604
+ safe = {
7605
+ code: "client_request_failed",
7606
+ retryable: false,
7607
+ suggestedAction: "Check the connection and run 'ohmyhost login' if authentication expired. Inspect the existing operation before resubmitting any mutation."
7608
+ };
7609
+ }
7610
+ return {
7611
+ ...toolResult({
7612
+ error: {
7613
+ code: safe.code,
7614
+ retryable: safe.retryable,
7615
+ suggested_action: safe.suggestedAction,
7616
+ ...safe.requestId === void 0 ? {} : { request_id: safe.requestId },
7617
+ ...safe.retryAfterSeconds === void 0 ? {} : { retry_after_seconds: safe.retryAfterSeconds },
7618
+ ...actingAs !== void 0 && ACCOUNT_SENSITIVE_PROBLEMS.has(safe.code) ? { acting_as: actingAs } : {}
7619
+ }
7620
+ }),
7621
+ isError: true
7622
+ };
7623
+ }
6365
7624
  function toolResult(result) {
6366
7625
  const serialized = JSON.stringify(result);
6367
7626
  return {
@@ -6392,11 +7651,17 @@ function destructiveAnnotations() {
6392
7651
  // apps/mcp/dist/stdio-main.js
6393
7652
  var environment = {
6394
7653
  ...process.env["OHMYHOST_ENVIRONMENT"] === void 0 ? {} : { OHMYHOST_ENVIRONMENT: process.env["OHMYHOST_ENVIRONMENT"] },
6395
- ...process.env["OHMYHOST_TOKEN"] === void 0 ? {} : { OHMYHOST_TOKEN: process.env["OHMYHOST_TOKEN"] }
7654
+ ...process.env["OHMYHOST_TOKEN"] === void 0 ? {} : { OHMYHOST_TOKEN: process.env["OHMYHOST_TOKEN"] },
7655
+ ...process.env["OHMYHOST_PROFILE"] === void 0 ? {} : { OHMYHOST_PROFILE: process.env["OHMYHOST_PROFILE"] }
6396
7656
  };
6397
7657
  resolveProductCliEnvironment(environment);
6398
7658
  serveStdio(() => createLocalOhmyhostMcpServer({
6399
7659
  environment,
6400
- authenticatedClient: (signal) => createAuthenticatedLocalMcpClient(signal, { environment })
7660
+ // Every tool call chooses its own saved login; nothing this process remembers can retarget it.
7661
+ authenticatedClient: (signal, request) => createAuthenticatedLocalMcpClient(signal, {
7662
+ environment,
7663
+ ...request === void 0 ? {} : { request }
7664
+ }),
7665
+ listProfiles: (signal) => listSavedProfiles(signal, { environment })
6401
7666
  }), { onerror: (error) => process.stderr.write(`${error.message}
6402
7667
  `) });