@amerged/ohmyhost-cli 0.1.20 → 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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @amerged/ohmyhost-cli
2
2
 
3
- ohmyho.st cli client, version 0.1.20.
3
+ ohmyho.st cli client, version 0.1.21.
4
4
 
5
5
  ```sh
6
6
  npm install --global @amerged/ohmyhost-cli
package/dist/bin.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { createRequire as __ohmyhostCreateRequire } from "node:module"; const require = __ohmyhostCreateRequire(import.meta.url);
3
3
  import {
4
4
  runProductionCli
5
- } from "./chunk-2VVAKEZO.js";
5
+ } from "./chunk-EPTONNNW.js";
6
6
  import "./chunk-7D2RFLWM.js";
7
7
 
8
8
  // apps/product-cli/dist/bin.js
@@ -15358,6 +15358,8 @@ var recognizedCommandLabel = (argv) => {
15358
15358
  return "operation get";
15359
15359
  if (value === "credits" && values[0] === "account")
15360
15360
  return "credits account";
15361
+ if (value === "referral" && values[0] === "link")
15362
+ return "referral link";
15361
15363
  if (value === "credits" && values[0] === "balance")
15362
15364
  return "credits balance";
15363
15365
  if (value === "billing" && values[0] === "recharge" && values[1] === "get")
@@ -15412,6 +15414,10 @@ var recognizedCommandLabel = (argv) => {
15412
15414
  return "project dev-access mode";
15413
15415
  if (value === "project" && values[0] === "dev-access" && values[1] === "create")
15414
15416
  return "project dev-access create";
15417
+ if (value === "project" && values[0] === "flag" && values[1] === "status")
15418
+ return "project flag status";
15419
+ if (value === "project" && values[0] === "flag" && values[1] === "set")
15420
+ return "project flag set";
15415
15421
  if (value === "database" && values[0] === "compute" && values[1] === "set")
15416
15422
  return "database compute set";
15417
15423
  if (value === "database" && values[0] === "compute" && values[1] === "get")
@@ -15756,6 +15762,13 @@ var parseProductCliCommand = (argv) => {
15756
15762
  const options = parseOptions(values, ["--organization"]);
15757
15763
  return { kind: "credits-account", organizationId: options["--organization"], credentialStore };
15758
15764
  }
15765
+ if (first === "referral" && values[0] === "link") {
15766
+ values.shift();
15767
+ if (yes || wait || follow || stdin)
15768
+ throw new InvalidCommandError();
15769
+ const options = parseOptions(values, ["--organization"]);
15770
+ return { kind: "referral-link", organizationId: options["--organization"], credentialStore };
15771
+ }
15759
15772
  if (first === "credits" && values.shift() === "balance") {
15760
15773
  if (yes || wait || follow || stdin)
15761
15774
  throw new InvalidCommandError();
@@ -15952,6 +15965,28 @@ var parseProductCliCommand = (argv) => {
15952
15965
  credentialStore
15953
15966
  };
15954
15967
  }
15968
+ if (subcommand === "flag") {
15969
+ const action = values.shift();
15970
+ if (yes || wait || follow || stdin)
15971
+ throw new InvalidCommandError();
15972
+ if (action === "status") {
15973
+ const options2 = parseOptions(values, ["--project"]);
15974
+ return { kind: "project-flag-status", projectId: options2["--project"], credentialStore };
15975
+ }
15976
+ if (action === "set") {
15977
+ const options2 = parseOptions(values, ["--project", "--enabled", "--idempotency-key"]);
15978
+ if (options2["--enabled"] !== "true" && options2["--enabled"] !== "false")
15979
+ throw new InvalidCommandError();
15980
+ return {
15981
+ kind: "project-flag-set",
15982
+ projectId: options2["--project"],
15983
+ enabled: options2["--enabled"] === "true",
15984
+ idempotencyKey: options2["--idempotency-key"],
15985
+ credentialStore
15986
+ };
15987
+ }
15988
+ throw new InvalidCommandError();
15989
+ }
15955
15990
  if (subcommand === "dev-access" && values.shift() === "create") {
15956
15991
  if (yes || wait || follow || stdin)
15957
15992
  throw new InvalidCommandError();
@@ -17189,7 +17224,7 @@ function assertOrganizationAccount(value) {
17189
17224
  var failures = {
17190
17225
  operation_abandoned: [
17191
17226
  "The operation stopped renewing its execution lease and was ended.",
17192
- "Inspect project status and deployment diagnostics before creating a fresh plan. Keep this operation ID; the expired execution cannot resume."
17227
+ "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."
17193
17228
  ],
17194
17229
  database_write_rejected: [
17195
17230
  "The database rejected this write; it was not committed.",
@@ -17253,7 +17288,7 @@ var failures = {
17253
17288
  ],
17254
17289
  build_failed: [
17255
17290
  "The build did not produce a verified deployable artifact.",
17256
- "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."
17291
+ "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."
17257
17292
  ],
17258
17293
  insufficient_organization_credits: [
17259
17294
  "The organization does not have sufficient available credits for this operation.",
@@ -17269,7 +17304,7 @@ var failures = {
17269
17304
  ],
17270
17305
  runtime_candidate_failed: [
17271
17306
  "The deployed candidate failed its runtime checks.",
17272
- "Inspect deployment runtime diagnostics, required secrets and the health-check route before creating a new deployment plan."
17307
+ "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."
17273
17308
  ],
17274
17309
  runtime_candidate_rejected: [
17275
17310
  "The runtime rejected the built Worker script.",
@@ -17526,6 +17561,11 @@ var PROBLEM_POLICY = {
17526
17561
  retryable: false,
17527
17562
  action: "Finish database provisioning, then request the export again. No backup was accepted."
17528
17563
  },
17564
+ powered_by_flag_required: {
17565
+ status: 409,
17566
+ retryable: false,
17567
+ action: "Keep the flag on, remove the custom domain with 'ohmyhost domain paid delete', or buy Paid, then switch the flag off."
17568
+ },
17529
17569
  rate_limited: { status: 429, retryable: true, action: "Retry after the server delay." },
17530
17570
  insufficient_organization_credits: {
17531
17571
  status: 402,
@@ -17540,7 +17580,7 @@ var PROBLEM_POLICY = {
17540
17580
  paid_plan_required: {
17541
17581
  status: 402,
17542
17582
  retryable: false,
17543
- action: "Ask the organization Owner to activate Paid through billing checkout, then retry. Top-ups and signup credits do not activate Paid."
17583
+ 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."
17544
17584
  },
17545
17585
  interactive_login_required: {
17546
17586
  status: 403,
@@ -17758,6 +17798,18 @@ var parseDevAccessState = (value) => {
17758
17798
  throw new ResponseContractError();
17759
17799
  return input;
17760
17800
  };
17801
+ var parsePoweredByFlag = (value) => {
17802
+ const input = exactRecord2(value, ["project_id", "enabled", "enabled_at"]);
17803
+ if (!isUlid(input["project_id"]) || typeof input["enabled"] !== "boolean" || (input["enabled"] ? !isIsoInstant(input["enabled_at"]) : input["enabled_at"] !== null))
17804
+ throw new ResponseContractError();
17805
+ return input;
17806
+ };
17807
+ var parseOrganizationReferral = (value) => {
17808
+ const input = exactRecord2(value, ["organization_id", "referral_url"]);
17809
+ if (!isUlid(input["organization_id"]) || input["referral_url"] !== `https://ohmyho.st/?r=ref-${String(input["organization_id"]).toLowerCase()}`)
17810
+ throw new ResponseContractError();
17811
+ return input;
17812
+ };
17761
17813
  var parseDevAccessTicket = (value) => {
17762
17814
  const input = exactRecord2(value, [
17763
17815
  "ticket_id",
@@ -18217,6 +18269,7 @@ var parseSafeProblem = (value) => {
18217
18269
  var parseOperation = (value) => {
18218
18270
  const input = exactRecord2(value, [
18219
18271
  "blocking_operation_id",
18272
+ "deployment_id",
18220
18273
  "id",
18221
18274
  "state",
18222
18275
  "created_at",
@@ -18232,7 +18285,8 @@ var parseOperation = (value) => {
18232
18285
  const updatedAt = input["updated_at"];
18233
18286
  const result = input["result"];
18234
18287
  const blockingOperationId = input["blocking_operation_id"];
18235
- if (blockingOperationId !== void 0 && (state !== "queued" || !isUlid(blockingOperationId) || blockingOperationId === id3))
18288
+ const deploymentId = input["deployment_id"];
18289
+ if (blockingOperationId !== void 0 && (state !== "queued" || !isUlid(blockingOperationId) || blockingOperationId === id3) || deploymentId !== void 0 && !isUlid(deploymentId))
18236
18290
  throw new ResponseContractError();
18237
18291
  if (!isUlid(id3) || !isOperationState(state) || !isIsoInstant(createdAt) || !isIsoInstant(updatedAt) || result !== void 0 && !isRecord2(result)) {
18238
18292
  throw new ResponseContractError();
@@ -18279,6 +18333,7 @@ var parseOperation = (value) => {
18279
18333
  created_at: createdAt,
18280
18334
  updated_at: updatedAt,
18281
18335
  ...blockingOperationId === void 0 ? {} : { blocking_operation_id: blockingOperationId },
18336
+ ...deploymentId === void 0 ? {} : { deployment_id: deploymentId },
18282
18337
  ...progress === void 0 ? {} : { progress },
18283
18338
  ...result === void 0 ? {} : { result },
18284
18339
  ...error === void 0 ? {} : { error },
@@ -18554,7 +18609,7 @@ function exact2(v, keys) {
18554
18609
  // apps/product-cli/package.json
18555
18610
  var package_default = {
18556
18611
  name: "@ohmyhost/product-cli",
18557
- version: "0.1.20",
18612
+ version: "0.1.21",
18558
18613
  private: true,
18559
18614
  ohmyhost: {
18560
18615
  deployment: "production",
@@ -18853,6 +18908,7 @@ var HELP_USAGE = Object.freeze({
18853
18908
  "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)",
18854
18909
  "export get": "ohmyhost export get EXPORT_ULID --project ULID --json (poll the original job; signed ZIP download lasts 24 hours)",
18855
18910
  "credits account": "ohmyhost credits account --organization ULID --json",
18911
+ "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)",
18856
18912
  "credits balance": "ohmyhost credits balance --organization ULID --json",
18857
18913
  "billing recharge get": "ohmyhost billing recharge get --organization ULID --json",
18858
18914
  "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)",
@@ -18882,6 +18938,8 @@ var HELP_USAGE = Object.freeze({
18882
18938
  "project dev-share rotate": "ohmyhost project dev-share rotate --project ULID --idempotency-key KEY --yes --json",
18883
18939
  "project dev-share revoke": "ohmyhost project dev-share revoke --project ULID --idempotency-key KEY --yes --json",
18884
18940
  "project dev-access mode": "ohmyhost project dev-access mode --project ULID --mode protected|public --idempotency-key KEY --yes --json",
18941
+ "project flag status": "ohmyhost project flag status --project ULID --json",
18942
+ "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)",
18885
18943
  "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)",
18886
18944
  "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)",
18887
18945
  "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.",
@@ -19064,6 +19122,11 @@ var ProductCli = class {
19064
19122
  }
19065
19123
  return success(name, { status: "succeeded", billing: result });
19066
19124
  }
19125
+ if (command.kind === "referral-link") {
19126
+ const api = await this.authenticatedApi(command.credentialStore, signal);
19127
+ const referral = parseOrganizationReferral(await api.getOrganizationReferral(command.organizationId));
19128
+ return success("referral link", { status: "succeeded", referral });
19129
+ }
19067
19130
  if (command.kind === "credits-account") {
19068
19131
  const api = await this.authenticatedApi(command.credentialStore, signal);
19069
19132
  const account = await api.getOrganizationAccount(command.organizationId);
@@ -19225,6 +19288,11 @@ var ProductCli = class {
19225
19288
  return await this.getProjectStatus(command, signal);
19226
19289
  if (command.kind === "project-dev-access-create")
19227
19290
  return await this.createDevAccessTicket(command, signal);
19291
+ if (command.kind === "project-flag-status" || command.kind === "project-flag-set") {
19292
+ const api = await this.authenticatedApi(command.credentialStore, signal);
19293
+ const flag = parsePoweredByFlag(command.kind === "project-flag-status" ? await api.getPoweredByFlag(command.projectId) : await api.setPoweredByFlag(command.projectId, command.enabled, command.idempotencyKey));
19294
+ return success(command.kind === "project-flag-status" ? "project flag status" : "project flag set", { status: "succeeded", flag });
19295
+ }
19228
19296
  if (command.kind === "project-dev-share-link" || command.kind === "project-dev-share-rotate" || command.kind === "project-dev-share-revoke" || command.kind === "project-dev-access-mode-set") {
19229
19297
  const api = await this.authenticatedApi(command.credentialStore, signal);
19230
19298
  const state = parseDevAccessState(command.kind === "project-dev-share-link" ? await api.ensureDevShareLink(command.projectId) : command.kind === "project-dev-share-rotate" ? await api.rotateDevShareLink(command.projectId, command.idempotencyKey) : command.kind === "project-dev-share-revoke" ? await api.revokeDevShareLink(command.projectId, command.idempotencyKey) : await api.setDevAccessMode(command.projectId, command.mode, command.idempotencyKey));
@@ -20258,6 +20326,8 @@ var parsedCommandName = (command) => {
20258
20326
  return "billing portal";
20259
20327
  if (command.kind === "credits-account")
20260
20328
  return "credits account";
20329
+ if (command.kind === "referral-link")
20330
+ return "referral link";
20261
20331
  if (command.kind === "credits-balance")
20262
20332
  return "credits balance";
20263
20333
  if (command.kind === "credits-usage")
@@ -20308,6 +20378,10 @@ var parsedCommandName = (command) => {
20308
20378
  return "project dev-share revoke";
20309
20379
  if (command.kind === "project-dev-access-mode-set")
20310
20380
  return "project dev-access mode";
20381
+ if (command.kind === "project-flag-status")
20382
+ return "project flag status";
20383
+ if (command.kind === "project-flag-set")
20384
+ return "project flag set";
20311
20385
  if (command.kind === "project-handle-check")
20312
20386
  return "project handle check";
20313
20387
  if (command.kind === "project-handle-set")
@@ -21350,6 +21424,23 @@ var getGithubOrganizationConnection = (parameters, options) => {
21350
21424
  ...params
21351
21425
  });
21352
21426
  };
21427
+ var getOrganizationReferral = (parameters, options) => {
21428
+ const params = buildClientParams([parameters], [
21429
+ {
21430
+ args: [
21431
+ { in: "headers", key: "X-Request-Id" },
21432
+ { in: "path", key: "organization_id" }
21433
+ ]
21434
+ }
21435
+ ]);
21436
+ return (options?.client ?? client).get({
21437
+ responseStyle: "data",
21438
+ security: [{ scheme: "bearer", type: "http" }],
21439
+ url: "/v1/organizations/{organization_id}/referral",
21440
+ ...options,
21441
+ ...params
21442
+ });
21443
+ };
21353
21444
  var getOrganizationAccount = (parameters, options) => {
21354
21445
  const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "organization_id" }] }]);
21355
21446
  return (options?.client ?? client).get({
@@ -21971,6 +22062,47 @@ var setProjectDevAccessMode = (parameters, options) => {
21971
22062
  }
21972
22063
  });
21973
22064
  };
22065
+ var getProjectPoweredByFlag = (parameters, options) => {
22066
+ const params = buildClientParams([parameters], [
22067
+ {
22068
+ args: [
22069
+ { in: "headers", key: "X-Request-Id" },
22070
+ { in: "path", key: "project_id" }
22071
+ ]
22072
+ }
22073
+ ]);
22074
+ return (options?.client ?? client).get({
22075
+ responseStyle: "data",
22076
+ security: [{ scheme: "bearer", type: "http" }],
22077
+ url: "/v1/projects/{project_id}/powered-by-flag",
22078
+ ...options,
22079
+ ...params
22080
+ });
22081
+ };
22082
+ var setProjectPoweredByFlag = (parameters, options) => {
22083
+ const params = buildClientParams([parameters], [
22084
+ {
22085
+ args: [
22086
+ { in: "headers", key: "X-Request-Id" },
22087
+ { in: "headers", key: "Idempotency-Key" },
22088
+ { in: "path", key: "project_id" },
22089
+ { key: "setPoweredByFlagRequest", map: "body" }
22090
+ ]
22091
+ }
22092
+ ]);
22093
+ return (options?.client ?? client).put({
22094
+ responseStyle: "data",
22095
+ security: [{ scheme: "bearer", type: "http" }],
22096
+ url: "/v1/projects/{project_id}/powered-by-flag",
22097
+ ...options,
22098
+ ...params,
22099
+ headers: {
22100
+ "Content-Type": "application/json",
22101
+ ...options?.headers,
22102
+ ...params.headers
22103
+ }
22104
+ });
22105
+ };
21974
22106
  var createProjectDevAccessTicket = (parameters, options) => {
21975
22107
  const params = buildClientParams([parameters], [
21976
22108
  {
@@ -23162,6 +23294,9 @@ var GeneratedSdkProductApi = class {
23162
23294
  async getAccountProfile() {
23163
23295
  return parseAccountProfile(await getAccountProfile({ client: this.#client }));
23164
23296
  }
23297
+ getOrganizationReferral(organizationId) {
23298
+ return getOrganizationReferral({ organization_id: organizationId }, { client: this.#client });
23299
+ }
23165
23300
  getOrganizationAccount(organizationId) {
23166
23301
  return getOrganizationAccount({ organization_id: organizationId }, { client: this.#client });
23167
23302
  }
@@ -23335,6 +23470,16 @@ var GeneratedSdkProductApi = class {
23335
23470
  setDevAccessModeRequest: { mode }
23336
23471
  }, { client: this.#client });
23337
23472
  }
23473
+ async getPoweredByFlag(projectId) {
23474
+ return getProjectPoweredByFlag({ project_id: projectId }, { client: this.#client });
23475
+ }
23476
+ async setPoweredByFlag(projectId, enabled, idempotencyKey) {
23477
+ return setProjectPoweredByFlag({
23478
+ project_id: projectId,
23479
+ "Idempotency-Key": idempotencyKey,
23480
+ setPoweredByFlagRequest: { enabled }
23481
+ }, { client: this.#client });
23482
+ }
23338
23483
  async createDevAccessTicket(projectId) {
23339
23484
  return createProjectDevAccessTicket({ project_id: projectId }, { client: this.#client });
23340
23485
  }
@@ -26551,6 +26696,8 @@ export {
26551
26696
  parseCloudflareDnsAuthorization,
26552
26697
  parseCloudflareDnsAuthorizationStatus,
26553
26698
  parseDevAccessState,
26699
+ parsePoweredByFlag,
26700
+ parseOrganizationReferral,
26554
26701
  parseDevAccessTicket,
26555
26702
  parseCurrentIdentity,
26556
26703
  parseAccountProfile,
package/dist/index.js CHANGED
@@ -54,8 +54,10 @@ import {
54
54
  parseDevAccessTicket,
55
55
  parseOperation,
56
56
  parseOperationEvent,
57
+ parseOrganizationReferral,
57
58
  parsePaidDomain,
58
59
  parsePaidDomainPlan,
60
+ parsePoweredByFlag,
59
61
  parseProductCliCommand,
60
62
  parseSafeProblem,
61
63
  parseWithLinkedProject,
@@ -78,7 +80,7 @@ import {
78
80
  selectWorkspace,
79
81
  verifiedUserId,
80
82
  workspaceOf
81
- } from "./chunk-2VVAKEZO.js";
83
+ } from "./chunk-EPTONNNW.js";
82
84
  import {
83
85
  CredentialStoreIntegrityError,
84
86
  CredentialStoreUnavailableError,
@@ -287,8 +289,10 @@ export {
287
289
  parseDevAccessTicket,
288
290
  parseOperation,
289
291
  parseOperationEvent,
292
+ parseOrganizationReferral,
290
293
  parsePaidDomain,
291
294
  parsePaidDomainPlan,
295
+ parsePoweredByFlag,
292
296
  parseProductCliCommand,
293
297
  parseSafeProblem,
294
298
  parseWithLinkedProject,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amerged/ohmyhost-cli",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22"
@@ -80,6 +80,8 @@ import {
80
80
  parseProjectCreditBudget,
81
81
  parseDevAccessTicket,
82
82
  parseDevAccessState,
83
+ parsePoweredByFlag,
84
+ parseOrganizationReferral,
83
85
  parseGuardedActionPlan,
84
86
  parseEnvironmentSecret,
85
87
  parseEnvironmentSecretDeletion,
@@ -304,6 +306,8 @@ const HELP_USAGE = Object.freeze({
304
306
  "export get":
305
307
  "ohmyhost export get EXPORT_ULID --project ULID --json (poll the original job; signed ZIP download lasts 24 hours)",
306
308
  "credits account": "ohmyhost credits account --organization ULID --json",
309
+ "referral link":
310
+ "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)",
307
311
  "credits balance": "ohmyhost credits balance --organization ULID --json",
308
312
  "billing recharge get": "ohmyhost billing recharge get --organization ULID --json",
309
313
  "billing recharge set":
@@ -350,6 +354,9 @@ const HELP_USAGE = Object.freeze({
350
354
  "ohmyhost project dev-share revoke --project ULID --idempotency-key KEY --yes --json",
351
355
  "project dev-access mode":
352
356
  "ohmyhost project dev-access mode --project ULID --mode protected|public --idempotency-key KEY --yes --json",
357
+ "project flag status": "ohmyhost project flag status --project ULID --json",
358
+ "project flag set":
359
+ "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)",
353
360
  "project handle check":
354
361
  "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)",
355
362
  "project handle set":
@@ -619,6 +626,13 @@ export class ProductCli {
619
626
  }
620
627
  return success(name, { status: "succeeded", billing: result });
621
628
  }
629
+ if (command.kind === "referral-link") {
630
+ const api = await this.authenticatedApi(command.credentialStore, signal);
631
+ const referral = parseOrganizationReferral(
632
+ await api.getOrganizationReferral(command.organizationId),
633
+ );
634
+ return success("referral link", { status: "succeeded", referral });
635
+ }
622
636
  if (command.kind === "credits-account") {
623
637
  const api = await this.authenticatedApi(command.credentialStore, signal);
624
638
  const account = await api.getOrganizationAccount(command.organizationId);
@@ -809,6 +823,22 @@ export class ProductCli {
809
823
  if (command.kind === "project-status") return await this.getProjectStatus(command, signal);
810
824
  if (command.kind === "project-dev-access-create")
811
825
  return await this.createDevAccessTicket(command, signal);
826
+ if (command.kind === "project-flag-status" || command.kind === "project-flag-set") {
827
+ const api = await this.authenticatedApi(command.credentialStore, signal);
828
+ const flag = parsePoweredByFlag(
829
+ command.kind === "project-flag-status"
830
+ ? await api.getPoweredByFlag(command.projectId)
831
+ : await api.setPoweredByFlag(
832
+ command.projectId,
833
+ command.enabled,
834
+ command.idempotencyKey,
835
+ ),
836
+ );
837
+ return success(
838
+ command.kind === "project-flag-status" ? "project flag status" : "project flag set",
839
+ { status: "succeeded", flag },
840
+ );
841
+ }
812
842
  if (
813
843
  command.kind === "project-dev-share-link" ||
814
844
  command.kind === "project-dev-share-rotate" ||
@@ -2450,6 +2480,7 @@ const parsedCommandName = (command: ProductCliCommand): CommandLabel => {
2450
2480
  if (command.kind === "billing-status") return "billing status";
2451
2481
  if (command.kind === "billing-portal") return "billing portal";
2452
2482
  if (command.kind === "credits-account") return "credits account";
2483
+ if (command.kind === "referral-link") return "referral link";
2453
2484
  if (command.kind === "credits-balance") return "credits balance";
2454
2485
  if (command.kind === "credits-usage") return "credits usage";
2455
2486
  if (command.kind === "budget-get") return "budget get";
@@ -2475,6 +2506,8 @@ const parsedCommandName = (command: ProductCliCommand): CommandLabel => {
2475
2506
  if (command.kind === "project-dev-share-rotate") return "project dev-share rotate";
2476
2507
  if (command.kind === "project-dev-share-revoke") return "project dev-share revoke";
2477
2508
  if (command.kind === "project-dev-access-mode-set") return "project dev-access mode";
2509
+ if (command.kind === "project-flag-status") return "project flag status";
2510
+ if (command.kind === "project-flag-set") return "project flag set";
2478
2511
  if (command.kind === "project-handle-check") return "project handle check";
2479
2512
  if (command.kind === "project-handle-set") return "project handle set";
2480
2513
  if (command.kind === "database-write") return "database write";
@@ -127,6 +127,11 @@ export type ProductCliCommand =
127
127
  readonly organizationId: string;
128
128
  readonly credentialStore: "native";
129
129
  }
130
+ | {
131
+ readonly kind: "referral-link";
132
+ readonly organizationId: string;
133
+ readonly credentialStore: "native";
134
+ }
130
135
  | {
131
136
  readonly kind: "credits-balance";
132
137
  readonly organizationId: string;
@@ -256,6 +261,18 @@ export type ProductCliCommand =
256
261
  readonly projectId: string;
257
262
  readonly credentialStore: "native";
258
263
  }
264
+ | {
265
+ readonly kind: "project-flag-status";
266
+ readonly projectId: string;
267
+ readonly credentialStore: "native";
268
+ }
269
+ | {
270
+ readonly kind: "project-flag-set";
271
+ readonly projectId: string;
272
+ readonly enabled: boolean;
273
+ readonly idempotencyKey: string;
274
+ readonly credentialStore: "native";
275
+ }
259
276
  | {
260
277
  readonly kind: "database-write";
261
278
  readonly projectId: string;
@@ -549,6 +566,7 @@ export type RecognizedCommandLabel =
549
566
  | "version"
550
567
  | "whoami"
551
568
  | "credits account"
569
+ | "referral link"
552
570
  | "credits balance"
553
571
  | "credits usage"
554
572
  | "budget get"
@@ -577,6 +595,8 @@ export type RecognizedCommandLabel =
577
595
  | "project dev-share rotate"
578
596
  | "project dev-share revoke"
579
597
  | "project dev-access mode"
598
+ | "project flag status"
599
+ | "project flag set"
580
600
  | "project handle check"
581
601
  | "project handle set"
582
602
  | "database write"
@@ -656,6 +676,7 @@ export const recognizedCommandLabel = (argv: readonly string[]): RecognizedComma
656
676
  if (value === "github" && values[0] === "status") return "github status";
657
677
  if (value === "operation" && values[0] === "get") return "operation get";
658
678
  if (value === "credits" && values[0] === "account") return "credits account";
679
+ if (value === "referral" && values[0] === "link") return "referral link";
659
680
  if (value === "credits" && values[0] === "balance") return "credits balance";
660
681
  if (value === "billing" && values[0] === "recharge" && values[1] === "get")
661
682
  return "billing recharge get";
@@ -701,6 +722,10 @@ export const recognizedCommandLabel = (argv: readonly string[]): RecognizedComma
701
722
  return "project dev-access mode";
702
723
  if (value === "project" && values[0] === "dev-access" && values[1] === "create")
703
724
  return "project dev-access create";
725
+ if (value === "project" && values[0] === "flag" && values[1] === "status")
726
+ return "project flag status";
727
+ if (value === "project" && values[0] === "flag" && values[1] === "set")
728
+ return "project flag set";
704
729
  if (value === "database" && values[0] === "compute" && values[1] === "set")
705
730
  return "database compute set";
706
731
  if (value === "database" && values[0] === "compute" && values[1] === "get")
@@ -1080,6 +1105,12 @@ export const parseProductCliCommand = (argv: readonly string[]): ProductCliComma
1080
1105
  const options = parseOptions(values, ["--organization"]);
1081
1106
  return { kind: "credits-account", organizationId: options["--organization"], credentialStore };
1082
1107
  }
1108
+ if (first === "referral" && values[0] === "link") {
1109
+ values.shift();
1110
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1111
+ const options = parseOptions(values, ["--organization"]);
1112
+ return { kind: "referral-link", organizationId: options["--organization"], credentialStore };
1113
+ }
1083
1114
  if (first === "credits" && values.shift() === "balance") {
1084
1115
  if (yes || wait || follow || stdin) throw new InvalidCommandError();
1085
1116
  const options = parseOptions(values, ["--organization"]);
@@ -1266,6 +1297,27 @@ export const parseProductCliCommand = (argv: readonly string[]): ProductCliComma
1266
1297
  credentialStore,
1267
1298
  };
1268
1299
  }
1300
+ if (subcommand === "flag") {
1301
+ const action = values.shift();
1302
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1303
+ if (action === "status") {
1304
+ const options = parseOptions(values, ["--project"]);
1305
+ return { kind: "project-flag-status", projectId: options["--project"], credentialStore };
1306
+ }
1307
+ if (action === "set") {
1308
+ const options = parseOptions(values, ["--project", "--enabled", "--idempotency-key"]);
1309
+ if (options["--enabled"] !== "true" && options["--enabled"] !== "false")
1310
+ throw new InvalidCommandError();
1311
+ return {
1312
+ kind: "project-flag-set",
1313
+ projectId: options["--project"],
1314
+ enabled: options["--enabled"] === "true",
1315
+ idempotencyKey: options["--idempotency-key"],
1316
+ credentialStore,
1317
+ };
1318
+ }
1319
+ throw new InvalidCommandError();
1320
+ }
1269
1321
  if (subcommand === "dev-access" && values.shift() === "create") {
1270
1322
  if (yes || wait || follow || stdin) throw new InvalidCommandError();
1271
1323
  const options = parseOptions(values, ["--project"]);
@@ -14,6 +14,8 @@ export {
14
14
  parseCurrentIdentity,
15
15
  parseDevAccessTicket,
16
16
  parseDevAccessState,
17
+ parsePoweredByFlag,
18
+ parseOrganizationReferral,
17
19
  parseCloudflareDnsAuthorization,
18
20
  parseCloudflareDnsAuthorizationStatus,
19
21
  parsePaidDomain,
@@ -45,6 +45,7 @@ import {
45
45
  type BillingPortal,
46
46
  getOrganizationCredits,
47
47
  getOrganizationAccount,
48
+ getOrganizationReferral,
48
49
  type OrganizationAccount,
49
50
  getOrganizationCreditUsage,
50
51
  type OrganizationCreditUsage,
@@ -61,6 +62,8 @@ import {
61
62
  rotateProjectDevShareLink,
62
63
  revokeProjectDevShareLink,
63
64
  setProjectDevAccessMode,
65
+ getProjectPoweredByFlag,
66
+ setProjectPoweredByFlag,
64
67
  createDeployment,
65
68
  createProject,
66
69
  submitFeedback,
@@ -193,6 +196,9 @@ export interface ProductApi {
193
196
  }): Promise<import("@ohmyhost/sdk-ts").BillingRecharge>;
194
197
  createBillingPortal(organizationId: string): Promise<BillingPortal>;
195
198
  getOrganizationAccount(organizationId: string): Promise<OrganizationAccount>;
199
+ getOrganizationReferral(
200
+ organizationId: string,
201
+ ): Promise<import("@ohmyhost/sdk-ts").OrganizationReferral>;
196
202
  getOrganizationCredits(organizationId: string): Promise<OrganizationCredits>;
197
203
  getOrganizationCreditUsage(input: {
198
204
  organizationId: string;
@@ -277,6 +283,12 @@ export interface ProductApi {
277
283
  mode: "protected" | "public",
278
284
  idempotencyKey: string,
279
285
  ): Promise<import("@ohmyhost/sdk-ts").DevAccessState>;
286
+ getPoweredByFlag(projectId: string): Promise<import("@ohmyhost/sdk-ts").PoweredByFlag>;
287
+ setPoweredByFlag(
288
+ projectId: string,
289
+ enabled: boolean,
290
+ idempotencyKey: string,
291
+ ): Promise<import("@ohmyhost/sdk-ts").PoweredByFlag>;
280
292
  submitFeedback(input: {
281
293
  report: import("@ohmyhost/sdk-ts").FeedbackSubmission;
282
294
  idempotencyKey: string;
@@ -489,6 +501,9 @@ export class GeneratedSdkProductApi implements ProductApi {
489
501
  return parseAccountProfile(await getAccountProfile({ client: this.#client }));
490
502
  }
491
503
 
504
+ public getOrganizationReferral(organizationId: string) {
505
+ return getOrganizationReferral({ organization_id: organizationId }, { client: this.#client });
506
+ }
492
507
  public getOrganizationAccount(organizationId: string) {
493
508
  return getOrganizationAccount({ organization_id: organizationId }, { client: this.#client });
494
509
  }
@@ -790,6 +805,19 @@ export class GeneratedSdkProductApi implements ProductApi {
790
805
  { client: this.#client },
791
806
  );
792
807
  }
808
+ public async getPoweredByFlag(projectId: string) {
809
+ return getProjectPoweredByFlag({ project_id: projectId }, { client: this.#client });
810
+ }
811
+ public async setPoweredByFlag(projectId: string, enabled: boolean, idempotencyKey: string) {
812
+ return setProjectPoweredByFlag(
813
+ {
814
+ project_id: projectId,
815
+ "Idempotency-Key": idempotencyKey,
816
+ setPoweredByFlagRequest: { enabled },
817
+ },
818
+ { client: this.#client },
819
+ );
820
+ }
793
821
  public async createDevAccessTicket(projectId: string): Promise<DevAccessTicket> {
794
822
  return createProjectDevAccessTicket({ project_id: projectId }, { client: this.#client });
795
823
  }
@@ -273,6 +273,12 @@ export const PROBLEM_POLICY = {
273
273
  retryable: false,
274
274
  action: "Finish database provisioning, then request the export again. No backup was accepted.",
275
275
  },
276
+ powered_by_flag_required: {
277
+ status: 409,
278
+ retryable: false,
279
+ action:
280
+ "Keep the flag on, remove the custom domain with 'ohmyhost domain paid delete', or buy Paid, then switch the flag off.",
281
+ },
276
282
  rate_limited: { status: 429, retryable: true, action: "Retry after the server delay." },
277
283
  insufficient_organization_credits: {
278
284
  status: 402,
@@ -289,7 +295,7 @@ export const PROBLEM_POLICY = {
289
295
  status: 402,
290
296
  retryable: false,
291
297
  action:
292
- "Ask the organization Owner to activate Paid through billing checkout, then retry. Top-ups and signup credits do not activate Paid.",
298
+ "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.",
293
299
  },
294
300
  interactive_login_required: {
295
301
  status: 403,
@@ -740,6 +746,39 @@ export const parseDevAccessState = (value: unknown): PublicDevAccessState => {
740
746
  return input as unknown as PublicDevAccessState;
741
747
  };
742
748
 
749
+ export interface PublicPoweredByFlag {
750
+ readonly project_id: string;
751
+ readonly enabled: boolean;
752
+ readonly enabled_at: string | null;
753
+ }
754
+
755
+ export const parsePoweredByFlag = (value: unknown): PublicPoweredByFlag => {
756
+ const input = exactRecord(value, ["project_id", "enabled", "enabled_at"]);
757
+ if (
758
+ !isUlid(input["project_id"]) ||
759
+ typeof input["enabled"] !== "boolean" ||
760
+ (input["enabled"] ? !isIsoInstant(input["enabled_at"]) : input["enabled_at"] !== null)
761
+ )
762
+ throw new ResponseContractError();
763
+ return input as unknown as PublicPoweredByFlag;
764
+ };
765
+
766
+ export interface PublicOrganizationReferral {
767
+ readonly organization_id: string;
768
+ readonly referral_url: string;
769
+ }
770
+
771
+ export const parseOrganizationReferral = (value: unknown): PublicOrganizationReferral => {
772
+ const input = exactRecord(value, ["organization_id", "referral_url"]);
773
+ if (
774
+ !isUlid(input["organization_id"]) ||
775
+ input["referral_url"] !==
776
+ `https://ohmyho.st/?r=ref-${String(input["organization_id"]).toLowerCase()}`
777
+ )
778
+ throw new ResponseContractError();
779
+ return input as unknown as PublicOrganizationReferral;
780
+ };
781
+
743
782
  export const parseDevAccessTicket = (value: unknown): PublicDevAccessTicket => {
744
783
  const input = exactRecord(value, [
745
784
  "ticket_id",
@@ -1141,6 +1180,8 @@ const parseProjectDeployment = (value: unknown): void => {
1141
1180
 
1142
1181
  export interface PublicOperation {
1143
1182
  readonly blocking_operation_id?: string;
1183
+ /** The deployment this operation created; its diagnostics explain a failure. */
1184
+ readonly deployment_id?: string;
1144
1185
  readonly progress?: OperationDeploymentProgress;
1145
1186
  readonly id: string;
1146
1187
  readonly state: "queued" | "running" | "succeeded" | "failed" | "cancelled";
@@ -1580,6 +1621,7 @@ export const parseSafeProblem = (value: unknown): SafeProblem => {
1580
1621
  export const parseOperation = (value: unknown): PublicOperation => {
1581
1622
  const input = exactRecord(value, [
1582
1623
  "blocking_operation_id",
1624
+ "deployment_id",
1583
1625
  "id",
1584
1626
  "state",
1585
1627
  "created_at",
@@ -1595,9 +1637,11 @@ export const parseOperation = (value: unknown): PublicOperation => {
1595
1637
  const updatedAt = input["updated_at"];
1596
1638
  const result = input["result"];
1597
1639
  const blockingOperationId = input["blocking_operation_id"];
1640
+ const deploymentId = input["deployment_id"];
1598
1641
  if (
1599
- blockingOperationId !== undefined &&
1600
- (state !== "queued" || !isUlid(blockingOperationId) || blockingOperationId === id)
1642
+ (blockingOperationId !== undefined &&
1643
+ (state !== "queued" || !isUlid(blockingOperationId) || blockingOperationId === id)) ||
1644
+ (deploymentId !== undefined && !isUlid(deploymentId))
1601
1645
  )
1602
1646
  throw new ResponseContractError();
1603
1647
  if (
@@ -1659,6 +1703,7 @@ export const parseOperation = (value: unknown): PublicOperation => {
1659
1703
  ...(blockingOperationId === undefined
1660
1704
  ? {}
1661
1705
  : { blocking_operation_id: blockingOperationId as string }),
1706
+ ...(deploymentId === undefined ? {} : { deployment_id: deploymentId as string }),
1662
1707
  ...(progress === undefined ? {} : { progress }),
1663
1708
  ...(result === undefined ? {} : { result }),
1664
1709
  ...(error === undefined ? {} : { error }),
@@ -1,7 +1,7 @@
1
1
  const failures = {
2
2
  operation_abandoned: [
3
3
  "The operation stopped renewing its execution lease and was ended.",
4
- "Inspect project status and deployment diagnostics before creating a fresh plan. Keep this operation ID; the expired execution cannot resume.",
4
+ "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.",
5
5
  ],
6
6
  database_write_rejected: [
7
7
  "The database rejected this write; it was not committed.",
@@ -66,7 +66,7 @@ const failures = {
66
66
  ],
67
67
  build_failed: [
68
68
  "The build did not produce a verified deployable artifact.",
69
- "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.",
69
+ "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.",
70
70
  ],
71
71
  insufficient_organization_credits: [
72
72
  "The organization does not have sufficient available credits for this operation.",
@@ -82,7 +82,7 @@ const failures = {
82
82
  ],
83
83
  runtime_candidate_failed: [
84
84
  "The deployed candidate failed its runtime checks.",
85
- "Inspect deployment runtime diagnostics, required secrets and the health-check route before creating a new deployment plan.",
85
+ "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.",
86
86
  ],
87
87
  runtime_candidate_rejected: [
88
88
  "The runtime rejected the built Worker script.",
@@ -45,12 +45,14 @@ export {
45
45
  getOrganizationAccount,
46
46
  getOrganizationCredits,
47
47
  getOrganizationCreditUsage,
48
+ getOrganizationReferral,
48
49
  getPaidProjectDomain,
49
50
  getProject,
50
51
  getProjectContext,
51
52
  getProjectCreditBudget,
52
53
  getProjectExport,
53
54
  getProjectHandleAvailability,
55
+ getProjectPoweredByFlag,
54
56
  getProjectSource,
55
57
  getProjectSourceAutoDeploy,
56
58
  getProjectStatus,
@@ -87,6 +89,7 @@ export {
87
89
  setProjectCreditBudget,
88
90
  setProjectDevAccessMode,
89
91
  setProjectNotes,
92
+ setProjectPoweredByFlag,
90
93
  streamDeploymentLogs,
91
94
  streamOperationEvents,
92
95
  submitContactRequest,
@@ -364,6 +367,11 @@ export type {
364
367
  GetOrganizationCreditUsageErrors,
365
368
  GetOrganizationCreditUsageResponse,
366
369
  GetOrganizationCreditUsageResponses,
370
+ GetOrganizationReferralData,
371
+ GetOrganizationReferralError,
372
+ GetOrganizationReferralErrors,
373
+ GetOrganizationReferralResponse,
374
+ GetOrganizationReferralResponses,
367
375
  GetPaidProjectDomainData,
368
376
  GetPaidProjectDomainError,
369
377
  GetPaidProjectDomainErrors,
@@ -392,6 +400,11 @@ export type {
392
400
  GetProjectHandleAvailabilityErrors,
393
401
  GetProjectHandleAvailabilityResponse,
394
402
  GetProjectHandleAvailabilityResponses,
403
+ GetProjectPoweredByFlagData,
404
+ GetProjectPoweredByFlagError,
405
+ GetProjectPoweredByFlagErrors,
406
+ GetProjectPoweredByFlagResponse,
407
+ GetProjectPoweredByFlagResponses,
395
408
  GetProjectResponse,
396
409
  GetProjectResponses,
397
410
  GetProjectSourceAutoDeployData,
@@ -493,6 +506,7 @@ export type {
493
506
  OrganizationAccount,
494
507
  OrganizationCredits,
495
508
  OrganizationCreditUsage,
509
+ OrganizationReferral,
496
510
  PaidDomain,
497
511
  PaidDomainPlan,
498
512
  PaidDomainRequest,
@@ -522,6 +536,7 @@ export type {
522
536
  PlanProjectDeletionErrors,
523
537
  PlanProjectDeletionResponse,
524
538
  PlanProjectDeletionResponses,
539
+ PoweredByFlag,
525
540
  PrepareManagedMailWebhookData,
526
541
  PrepareManagedMailWebhookError,
527
542
  PrepareManagedMailWebhookErrors,
@@ -623,6 +638,7 @@ export type {
623
638
  RotateProjectDevShareLinkResponses,
624
639
  SecretName,
625
640
  SetDevAccessModeRequest,
641
+ SetPoweredByFlagRequest,
626
642
  SetProjectCreditBudgetData,
627
643
  SetProjectCreditBudgetError,
628
644
  SetProjectCreditBudgetErrors,
@@ -639,6 +655,11 @@ export type {
639
655
  SetProjectNotesErrors,
640
656
  SetProjectNotesResponse,
641
657
  SetProjectNotesResponses,
658
+ SetProjectPoweredByFlagData,
659
+ SetProjectPoweredByFlagError,
660
+ SetProjectPoweredByFlagErrors,
661
+ SetProjectPoweredByFlagResponse,
662
+ SetProjectPoweredByFlagResponses,
642
663
  Sha256Digest,
643
664
  SourceAutoDeploy,
644
665
  SourceDeploymentId,
@@ -106,6 +106,8 @@ import type {
106
106
  GetOrganizationCreditsResponses,
107
107
  GetOrganizationCreditUsageErrors,
108
108
  GetOrganizationCreditUsageResponses,
109
+ GetOrganizationReferralErrors,
110
+ GetOrganizationReferralResponses,
109
111
  GetPaidProjectDomainErrors,
110
112
  GetPaidProjectDomainResponses,
111
113
  GetProjectContextErrors,
@@ -117,6 +119,8 @@ import type {
117
119
  GetProjectExportResponses,
118
120
  GetProjectHandleAvailabilityErrors,
119
121
  GetProjectHandleAvailabilityResponses,
122
+ GetProjectPoweredByFlagErrors,
123
+ GetProjectPoweredByFlagResponses,
120
124
  GetProjectResponses,
121
125
  GetProjectSourceAutoDeployErrors,
122
126
  GetProjectSourceAutoDeployResponses,
@@ -192,6 +196,7 @@ import type {
192
196
  RotateProjectDevShareLinkErrors,
193
197
  RotateProjectDevShareLinkResponses,
194
198
  SetDevAccessModeRequest,
199
+ SetPoweredByFlagRequest,
195
200
  SetProjectCreditBudgetErrors,
196
201
  SetProjectCreditBudgetRequest,
197
202
  SetProjectCreditBudgetResponses,
@@ -199,6 +204,8 @@ import type {
199
204
  SetProjectDevAccessModeResponses,
200
205
  SetProjectNotesErrors,
201
206
  SetProjectNotesResponses,
207
+ SetProjectPoweredByFlagErrors,
208
+ SetProjectPoweredByFlagResponses,
202
209
  StreamDeploymentLogsErrors,
203
210
  StreamDeploymentLogsResponse,
204
211
  StreamDeploymentLogsResponses,
@@ -375,6 +382,48 @@ export const getGithubOrganizationConnection = <ThrowOnError extends boolean = t
375
382
  });
376
383
  };
377
384
 
385
+ /**
386
+ * Read the workspace's referral link
387
+ *
388
+ * Any member may share it. A new user whose first workspace comes from this link starts with 30 days of Paid access and 1,000 credits; that workspace's first payment gives this workspace 1,000 credits and 30 more days of granted Paid access.
389
+ */
390
+ export const getOrganizationReferral = <ThrowOnError extends boolean = true>(
391
+ parameters: {
392
+ "X-Request-Id"?: string;
393
+ organization_id: Ulid;
394
+ },
395
+ options?: Options<never, ThrowOnError>,
396
+ ): RequestResult<
397
+ GetOrganizationReferralResponses,
398
+ GetOrganizationReferralErrors,
399
+ ThrowOnError,
400
+ "data"
401
+ > => {
402
+ const params = buildClientParams(
403
+ [parameters],
404
+ [
405
+ {
406
+ args: [
407
+ { in: "headers", key: "X-Request-Id" },
408
+ { in: "path", key: "organization_id" },
409
+ ],
410
+ },
411
+ ],
412
+ );
413
+ return (options?.client ?? client).get<
414
+ GetOrganizationReferralResponses,
415
+ GetOrganizationReferralErrors,
416
+ ThrowOnError,
417
+ "data"
418
+ >({
419
+ responseStyle: "data",
420
+ security: [{ scheme: "bearer", type: "http" }],
421
+ url: "/v1/organizations/{organization_id}/referral",
422
+ ...options,
423
+ ...params,
424
+ });
425
+ };
426
+
378
427
  /**
379
428
  * Read effective plan and monthly versus one-time credit balance
380
429
  */
@@ -1974,6 +2023,99 @@ export const setProjectDevAccessMode = <ThrowOnError extends boolean = true>(
1974
2023
  });
1975
2024
  };
1976
2025
 
2026
+ /**
2027
+ * Read the "Powered by ohmyho.st" flag
2028
+ *
2029
+ * Whether the production site shows the opt-in flag for the current owner organization.
2030
+ */
2031
+ export const getProjectPoweredByFlag = <ThrowOnError extends boolean = true>(
2032
+ parameters: {
2033
+ "X-Request-Id"?: string;
2034
+ project_id: Ulid;
2035
+ },
2036
+ options?: Options<never, ThrowOnError>,
2037
+ ): RequestResult<
2038
+ GetProjectPoweredByFlagResponses,
2039
+ GetProjectPoweredByFlagErrors,
2040
+ ThrowOnError,
2041
+ "data"
2042
+ > => {
2043
+ const params = buildClientParams(
2044
+ [parameters],
2045
+ [
2046
+ {
2047
+ args: [
2048
+ { in: "headers", key: "X-Request-Id" },
2049
+ { in: "path", key: "project_id" },
2050
+ ],
2051
+ },
2052
+ ],
2053
+ );
2054
+ return (options?.client ?? client).get<
2055
+ GetProjectPoweredByFlagResponses,
2056
+ GetProjectPoweredByFlagErrors,
2057
+ ThrowOnError,
2058
+ "data"
2059
+ >({
2060
+ responseStyle: "data",
2061
+ security: [{ scheme: "bearer", type: "http" }],
2062
+ url: "/v1/projects/{project_id}/powered-by-flag",
2063
+ ...options,
2064
+ ...params,
2065
+ });
2066
+ };
2067
+
2068
+ /**
2069
+ * Show or hide the "Powered by ohmyho.st" flag
2070
+ *
2071
+ * Owner-only. The production site shows a small flag on its right edge that links to ohmyho.st. While it is on, a Free workspace may connect its own domain to this project and the custom-hostname fee is waived; Paid periods receive bonus credits. Hiding it is refused while a Free workspace's domain depends on it. A retry with the same Idempotency-Key and body returns the first result without switching again; a changed request under a used key returns idempotency_key_reused (409).
2072
+ */
2073
+ export const setProjectPoweredByFlag = <ThrowOnError extends boolean = true>(
2074
+ parameters: {
2075
+ "X-Request-Id"?: string;
2076
+ "Idempotency-Key": string;
2077
+ project_id: Ulid;
2078
+ setPoweredByFlagRequest: SetPoweredByFlagRequest;
2079
+ },
2080
+ options?: Options<never, ThrowOnError>,
2081
+ ): RequestResult<
2082
+ SetProjectPoweredByFlagResponses,
2083
+ SetProjectPoweredByFlagErrors,
2084
+ ThrowOnError,
2085
+ "data"
2086
+ > => {
2087
+ const params = buildClientParams(
2088
+ [parameters],
2089
+ [
2090
+ {
2091
+ args: [
2092
+ { in: "headers", key: "X-Request-Id" },
2093
+ { in: "headers", key: "Idempotency-Key" },
2094
+ { in: "path", key: "project_id" },
2095
+ { key: "setPoweredByFlagRequest", map: "body" },
2096
+ ],
2097
+ },
2098
+ ],
2099
+ );
2100
+ return (options?.client ?? client).put<
2101
+ SetProjectPoweredByFlagResponses,
2102
+ SetProjectPoweredByFlagErrors,
2103
+ ThrowOnError,
2104
+ "data"
2105
+ >({
2106
+ responseStyle: "data",
2107
+ security: [{ scheme: "bearer", type: "http" }],
2108
+ url: "/v1/projects/{project_id}/powered-by-flag",
2109
+ ...options,
2110
+ ...params,
2111
+ headers: {
2112
+ "Content-Type": "application/json",
2113
+ ...options?.headers,
2114
+ ...params.headers,
2115
+ },
2116
+ });
2117
+ };
2118
+
1977
2119
  /**
1978
2120
  * Create a single-use dev access ticket
1979
2121
  *
@@ -569,13 +569,17 @@ export type DeploymentDiagnostic = {
569
569
  trace_id?: string;
570
570
  duration_ms?: number;
571
571
  /**
572
- * The sanitized tail of the customer's own install/build output, newest lines last. Present only on a BUILD_FAILED diagnostic; terminal escapes and control characters are removed before storage.
572
+ * The customer's own output with control characters removed before storage: on BUILD_FAILED the tail of the install/build output (newest lines last), on HEALTH_CHECK_FAILED the head of the body the health check was answered with (at most 4096 characters, never a header). A running application holds its environment's secrets and this answer is not scanned for them, so, like a query of the project's database, a HEALTH_CHECK_FAILED excerpt is returned only to an owner of the project; never answer a health check with a secret.
573
573
  */
574
574
  excerpt?: string;
575
575
  /**
576
- * Whether earlier build output was omitted from excerpt. Present exactly when excerpt is present.
576
+ * Whether output was omitted from excerpt (earlier build output, or the rest of the health answer). Present exactly when excerpt is present.
577
577
  */
578
578
  excerpt_truncated?: boolean;
579
+ /**
580
+ * The HTTP status the health check was answered with; present only on HEALTH_CHECK_FAILED. The probe is a cookieless GET of route that follows no redirect, so a 3xx is unhealthy too.
581
+ */
582
+ status_code?: number;
579
583
  occurred_at: string;
580
584
  };
581
585
 
@@ -670,6 +674,24 @@ export type SetDevAccessModeRequest = {
670
674
  mode: "protected" | "public";
671
675
  };
672
676
 
677
+ export type OrganizationReferral = {
678
+ organization_id: Ulid;
679
+ referral_url: string;
680
+ };
681
+
682
+ export type SetPoweredByFlagRequest = {
683
+ enabled: boolean;
684
+ };
685
+
686
+ export type PoweredByFlag = {
687
+ project_id: Ulid;
688
+ enabled: boolean;
689
+ /**
690
+ * When the current owner switched the flag on; null while it is hidden.
691
+ */
692
+ enabled_at: string | null;
693
+ };
694
+
673
695
  export type DevAccessState = {
674
696
  project_id: Ulid;
675
697
  mode: "protected" | "public";
@@ -1076,6 +1098,7 @@ export type ProblemDetails = {
1076
1098
  | "cloudflare_authorization_closed"
1077
1099
  | "project_notes_conflict"
1078
1100
  | "project_export_not_ready"
1101
+ | "powered_by_flag_required"
1079
1102
  | "interactive_login_required"
1080
1103
  | "api_key_creation_uncertain"
1081
1104
  | "api_key_permissions_unavailable"
@@ -1098,6 +1121,10 @@ export type Operation = {
1098
1121
  * The active operation holding this queued operation's project mutation claim.
1099
1122
  */
1100
1123
  blocking_operation_id?: Ulid;
1124
+ /**
1125
+ * The deployment this operation created (deploy, promotion or rollback), in every state. After a failure, read its diagnostics with deployment logs.
1126
+ */
1127
+ deployment_id?: Ulid;
1101
1128
  id: Ulid;
1102
1129
  state: "queued" | "running" | "succeeded" | "failed" | "cancelled";
1103
1130
  created_at: string;
@@ -1641,6 +1668,61 @@ export type GetGithubOrganizationConnectionResponses = {
1641
1668
  export type GetGithubOrganizationConnectionResponse =
1642
1669
  GetGithubOrganizationConnectionResponses[keyof GetGithubOrganizationConnectionResponses];
1643
1670
 
1671
+ export type GetOrganizationReferralData = {
1672
+ body?: never;
1673
+ headers?: {
1674
+ /**
1675
+ * Optional caller-provided correlation identifier.
1676
+ */
1677
+ "X-Request-Id"?: string;
1678
+ };
1679
+ path: {
1680
+ organization_id: Ulid;
1681
+ };
1682
+ query?: never;
1683
+ url: "/v1/organizations/{organization_id}/referral";
1684
+ };
1685
+
1686
+ export type GetOrganizationReferralErrors = {
1687
+ /**
1688
+ * The request failed.
1689
+ */
1690
+ 400: ProblemDetails;
1691
+ /**
1692
+ * The request failed.
1693
+ */
1694
+ 401: ProblemDetails;
1695
+ /**
1696
+ * The request failed.
1697
+ */
1698
+ 403: ProblemDetails;
1699
+ /**
1700
+ * The request failed.
1701
+ */
1702
+ 404: ProblemDetails;
1703
+ /**
1704
+ * The request failed.
1705
+ */
1706
+ 429: ProblemDetails;
1707
+ /**
1708
+ * The request failed.
1709
+ */
1710
+ 503: ProblemDetails;
1711
+ };
1712
+
1713
+ export type GetOrganizationReferralError =
1714
+ GetOrganizationReferralErrors[keyof GetOrganizationReferralErrors];
1715
+
1716
+ export type GetOrganizationReferralResponses = {
1717
+ /**
1718
+ * The workspace's referral link.
1719
+ */
1720
+ 200: OrganizationReferral;
1721
+ };
1722
+
1723
+ export type GetOrganizationReferralResponse =
1724
+ GetOrganizationReferralResponses[keyof GetOrganizationReferralResponses];
1725
+
1644
1726
  export type GetOrganizationAccountData = {
1645
1727
  body?: never;
1646
1728
  path: {
@@ -3751,6 +3833,122 @@ export type SetProjectDevAccessModeResponses = {
3751
3833
  export type SetProjectDevAccessModeResponse =
3752
3834
  SetProjectDevAccessModeResponses[keyof SetProjectDevAccessModeResponses];
3753
3835
 
3836
+ export type GetProjectPoweredByFlagData = {
3837
+ body?: never;
3838
+ headers?: {
3839
+ /**
3840
+ * Optional caller-provided correlation identifier.
3841
+ */
3842
+ "X-Request-Id"?: string;
3843
+ };
3844
+ path: {
3845
+ /**
3846
+ * Project identifier.
3847
+ */
3848
+ project_id: Ulid;
3849
+ };
3850
+ query?: never;
3851
+ url: "/v1/projects/{project_id}/powered-by-flag";
3852
+ };
3853
+
3854
+ export type GetProjectPoweredByFlagErrors = {
3855
+ /**
3856
+ * The request failed.
3857
+ */
3858
+ 400: ProblemDetails;
3859
+ /**
3860
+ * The request failed.
3861
+ */
3862
+ 401: ProblemDetails;
3863
+ /**
3864
+ * The request failed.
3865
+ */
3866
+ 403: ProblemDetails;
3867
+ /**
3868
+ * The request failed.
3869
+ */
3870
+ 404: ProblemDetails;
3871
+ /**
3872
+ * The request failed.
3873
+ */
3874
+ 503: ProblemDetails;
3875
+ };
3876
+
3877
+ export type GetProjectPoweredByFlagError =
3878
+ GetProjectPoweredByFlagErrors[keyof GetProjectPoweredByFlagErrors];
3879
+
3880
+ export type GetProjectPoweredByFlagResponses = {
3881
+ /**
3882
+ * Current flag state.
3883
+ */
3884
+ 200: PoweredByFlag;
3885
+ };
3886
+
3887
+ export type GetProjectPoweredByFlagResponse =
3888
+ GetProjectPoweredByFlagResponses[keyof GetProjectPoweredByFlagResponses];
3889
+
3890
+ export type SetProjectPoweredByFlagData = {
3891
+ body: SetPoweredByFlagRequest;
3892
+ headers: {
3893
+ /**
3894
+ * Optional caller-provided correlation identifier.
3895
+ */
3896
+ "X-Request-Id"?: string;
3897
+ /**
3898
+ * Identifies one mutation and its canonical request payload.
3899
+ */
3900
+ "Idempotency-Key": string;
3901
+ };
3902
+ path: {
3903
+ /**
3904
+ * Project identifier.
3905
+ */
3906
+ project_id: Ulid;
3907
+ };
3908
+ query?: never;
3909
+ url: "/v1/projects/{project_id}/powered-by-flag";
3910
+ };
3911
+
3912
+ export type SetProjectPoweredByFlagErrors = {
3913
+ /**
3914
+ * The request failed.
3915
+ */
3916
+ 400: ProblemDetails;
3917
+ /**
3918
+ * The request failed.
3919
+ */
3920
+ 401: ProblemDetails;
3921
+ /**
3922
+ * The request failed.
3923
+ */
3924
+ 403: ProblemDetails;
3925
+ /**
3926
+ * The request failed.
3927
+ */
3928
+ 404: ProblemDetails;
3929
+ /**
3930
+ * The request failed.
3931
+ */
3932
+ 409: ProblemDetails;
3933
+ /**
3934
+ * The request failed.
3935
+ */
3936
+ 503: ProblemDetails;
3937
+ };
3938
+
3939
+ export type SetProjectPoweredByFlagError =
3940
+ SetProjectPoweredByFlagErrors[keyof SetProjectPoweredByFlagErrors];
3941
+
3942
+ export type SetProjectPoweredByFlagResponses = {
3943
+ /**
3944
+ * Current flag state.
3945
+ */
3946
+ 200: PoweredByFlag;
3947
+ };
3948
+
3949
+ export type SetProjectPoweredByFlagResponse =
3950
+ SetProjectPoweredByFlagResponses[keyof SetProjectPoweredByFlagResponses];
3951
+
3754
3952
  export type CreateProjectDevAccessTicketData = {
3755
3953
  body?: never;
3756
3954
  headers?: {