@amerged/ohmyhost-cli 0.1.15 → 0.1.18

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.
@@ -14984,6 +14984,8 @@ var recognizedCommandLabel = (argv) => {
14984
14984
  return `export ${values[0]}`;
14985
14985
  if (value === "feedback" && values[0] === "submit")
14986
14986
  return "feedback submit";
14987
+ if (value === "feedback" && values[0] === "status")
14988
+ return "feedback status";
14987
14989
  if (value === "project" && values[0] === "create")
14988
14990
  return "project create";
14989
14991
  if (value === "organization" && values[0] === "create")
@@ -15004,6 +15006,10 @@ var recognizedCommandLabel = (argv) => {
15004
15006
  return "project handle check";
15005
15007
  if (value === "project" && values[0] === "handle" && values[1] === "set")
15006
15008
  return "project handle set";
15009
+ if (value === "project" && values[0] === "dev-share" && ["link", "rotate", "revoke"].includes(values[1] ?? ""))
15010
+ return values[1] === "link" ? "project dev-share link" : values[1] === "rotate" ? "project dev-share rotate" : "project dev-share revoke";
15011
+ if (value === "project" && values[0] === "dev-access" && values[1] === "mode")
15012
+ return "project dev-access mode";
15007
15013
  if (value === "project" && values[0] === "dev-access" && values[1] === "create")
15008
15014
  return "project dev-access create";
15009
15015
  if (value === "database" && values[0] === "compute" && values[1] === "set")
@@ -15095,6 +15101,8 @@ var recognizedCommandLabel = (argv) => {
15095
15101
  return "mail domain set";
15096
15102
  if (values[1] === "status")
15097
15103
  return "mail domain status";
15104
+ if (values[1] === "delete")
15105
+ return "mail domain delete";
15098
15106
  }
15099
15107
  return "unknown";
15100
15108
  }
@@ -15203,6 +15211,19 @@ var parseProductCliCommand = (argv) => {
15203
15211
  return { kind: "token-revoke", organizationId, keyId: options["--key"], credentialStore };
15204
15212
  throw new InvalidCommandError();
15205
15213
  }
15214
+ if (first === "feedback" && values[0] === "status") {
15215
+ values.shift();
15216
+ const feedbackId = values.shift();
15217
+ if (yes || wait || follow || stdin || !isValue(feedbackId))
15218
+ throw new InvalidCommandError();
15219
+ const cursor = parseOptionalOptions(values, ["--cursor"])["--cursor"];
15220
+ return {
15221
+ kind: "feedback-status",
15222
+ feedbackId,
15223
+ ...cursor === void 0 ? {} : { cursor },
15224
+ credentialStore
15225
+ };
15226
+ }
15206
15227
  if (first === "feedback" && values.shift() === "submit") {
15207
15228
  if (yes || wait || follow || stdin)
15208
15229
  throw new InvalidCommandError();
@@ -15493,6 +15514,40 @@ var parseProductCliCommand = (argv) => {
15493
15514
  const options2 = parseOptions(values, ["--handle"]);
15494
15515
  return { kind: "project-handle-check", handle: options2["--handle"], credentialStore };
15495
15516
  }
15517
+ if (subcommand === "dev-share") {
15518
+ const action = values.shift();
15519
+ if (action === "link") {
15520
+ if (yes || wait || follow || stdin)
15521
+ throw new InvalidCommandError();
15522
+ const options2 = parseOptions(values, ["--project"]);
15523
+ return { kind: "project-dev-share-link", projectId: options2["--project"], credentialStore };
15524
+ }
15525
+ if ((action === "rotate" || action === "revoke") && yes && !wait && !follow && !stdin) {
15526
+ const options2 = parseOptions(values, ["--project", "--idempotency-key"]);
15527
+ return {
15528
+ kind: action === "rotate" ? "project-dev-share-rotate" : "project-dev-share-revoke",
15529
+ projectId: options2["--project"],
15530
+ idempotencyKey: options2["--idempotency-key"],
15531
+ credentialStore
15532
+ };
15533
+ }
15534
+ throw new InvalidCommandError();
15535
+ }
15536
+ if (subcommand === "dev-access" && values[0] === "mode") {
15537
+ values.shift();
15538
+ if (!yes || wait || follow || stdin)
15539
+ throw new InvalidCommandError();
15540
+ const options2 = parseOptions(values, ["--project", "--mode", "--idempotency-key"]);
15541
+ if (options2["--mode"] !== "protected" && options2["--mode"] !== "public")
15542
+ throw new InvalidCommandError();
15543
+ return {
15544
+ kind: "project-dev-access-mode-set",
15545
+ projectId: options2["--project"],
15546
+ mode: options2["--mode"],
15547
+ idempotencyKey: options2["--idempotency-key"],
15548
+ credentialStore
15549
+ };
15550
+ }
15496
15551
  if (subcommand === "dev-access" && values.shift() === "create") {
15497
15552
  if (yes || wait || follow || stdin)
15498
15553
  throw new InvalidCommandError();
@@ -15559,17 +15614,20 @@ var parseProductCliCommand = (argv) => {
15559
15614
  "--name",
15560
15615
  "--idempotency-key",
15561
15616
  "--data-mode",
15617
+ "--dev-access-mode",
15562
15618
  "--region"
15563
15619
  ]);
15564
15620
  const dataMode = options["--data-mode"];
15621
+ const devAccessMode = options["--dev-access-mode"];
15565
15622
  const region = options["--region"];
15566
- if (!options["--organization"] || !options["--name"] || !options["--idempotency-key"] || dataMode !== void 0 && dataMode !== "shared" && dataMode !== "isolated" || region !== void 0 && region !== "us" && region !== "eu")
15623
+ if (!options["--organization"] || !options["--name"] || !options["--idempotency-key"] || dataMode !== void 0 && dataMode !== "shared" && dataMode !== "isolated" || devAccessMode !== void 0 && devAccessMode !== "protected" && devAccessMode !== "public" || region !== void 0 && region !== "us" && region !== "eu")
15567
15624
  throw new InvalidCommandError();
15568
15625
  return {
15569
15626
  kind: "project-create",
15570
15627
  organizationId: options["--organization"],
15571
15628
  name: options["--name"],
15572
15629
  ...dataMode === void 0 ? {} : { dataMode },
15630
+ ...devAccessMode === void 0 ? {} : { devAccessMode },
15573
15631
  ...region === void 0 ? {} : { region },
15574
15632
  idempotencyKey: options["--idempotency-key"],
15575
15633
  credentialStore
@@ -15814,23 +15872,42 @@ var parseProductCliCommand = (argv) => {
15814
15872
  if (first === "plan") {
15815
15873
  if (yes || wait || follow || stdin)
15816
15874
  throw new InvalidCommandError();
15817
- const options = parseOptions(values, ["--project", "--commit"]);
15875
+ const options = parseOptionalOptions(values, ["--project", "--commit", "--environment"]);
15876
+ const projectId = options["--project"];
15877
+ const commitSha = options["--commit"];
15878
+ if (projectId === void 0 || commitSha === void 0)
15879
+ throw new InvalidCommandError();
15818
15880
  return {
15819
15881
  kind: "plan",
15820
- projectId: options["--project"],
15821
- commitSha: options["--commit"],
15882
+ projectId,
15883
+ commitSha,
15884
+ environment: deploymentEnvironment(options["--environment"]),
15822
15885
  credentialStore
15823
15886
  };
15824
15887
  }
15825
15888
  if (first === "deploy") {
15826
15889
  if (!yes || follow || stdin)
15827
15890
  throw new InvalidCommandError();
15828
- const options = parseOptions(values, ["--project", "--plan-id", "--idempotency-key"]);
15891
+ const options = parseOptionalOptions(values, [
15892
+ "--project",
15893
+ "--plan-id",
15894
+ "--commit",
15895
+ "--environment",
15896
+ "--idempotency-key"
15897
+ ]);
15898
+ const projectId = options["--project"];
15899
+ const idempotencyKey = options["--idempotency-key"];
15900
+ const planId = options["--plan-id"] ?? null;
15901
+ const commitSha = options["--commit"] ?? null;
15902
+ if (projectId === void 0 || idempotencyKey === void 0 || planId === null === (commitSha === null) || planId !== null && options["--environment"] !== void 0)
15903
+ throw new InvalidCommandError();
15829
15904
  return {
15830
15905
  kind: "deploy",
15831
- projectId: options["--project"],
15832
- planId: options["--plan-id"],
15833
- idempotencyKey: options["--idempotency-key"],
15906
+ projectId,
15907
+ planId,
15908
+ commitSha,
15909
+ environment: deploymentEnvironment(options["--environment"]),
15910
+ idempotencyKey,
15834
15911
  yes,
15835
15912
  wait,
15836
15913
  credentialStore
@@ -16127,10 +16204,11 @@ var parseProductCliCommand = (argv) => {
16127
16204
  throw new InvalidCommandError();
16128
16205
  }
16129
16206
  if (first === "mail") {
16130
- if (yes || wait || follow || stdin)
16131
- throw new InvalidCommandError();
16132
16207
  const area = values.shift();
16133
16208
  const sub = area === "webhook" || area === "messages" || area === "domain" ? values.shift() : void 0;
16209
+ const retiring = area === "domain" && sub === "delete";
16210
+ if (yes !== retiring || wait || follow || stdin)
16211
+ throw new InvalidCommandError();
16134
16212
  const options = parseOptionalOptions(values, [
16135
16213
  "--project",
16136
16214
  "--environment",
@@ -16166,6 +16244,13 @@ var parseProductCliCommand = (argv) => {
16166
16244
  } else if (area === "status" || area === "domain" && sub === "status") {
16167
16245
  request = { ...scope, action: "status" };
16168
16246
  label = area === "domain" ? "mail domain status" : "mail status";
16247
+ } else if (retiring) {
16248
+ request = {
16249
+ ...scope,
16250
+ action: "domain_delete",
16251
+ idempotencyKey: required("--idempotency-key")
16252
+ };
16253
+ label = "mail domain delete";
16169
16254
  } else if (area === "webhook" && sub === "set") {
16170
16255
  request = {
16171
16256
  ...scope,
@@ -16230,6 +16315,13 @@ var parseOptions = (values, allowed) => {
16230
16315
  throw new InvalidCommandError();
16231
16316
  return result;
16232
16317
  };
16318
+ var deploymentEnvironment = (value) => {
16319
+ if (value === void 0 || value === "dev")
16320
+ return "dev";
16321
+ if (value === "prod")
16322
+ return "prod";
16323
+ throw new InvalidCommandError();
16324
+ };
16233
16325
  var parseOptionalOptions = (values, allowed) => {
16234
16326
  const result = {};
16235
16327
  for (let index = 0; index < values.length; index += 2) {
@@ -16305,6 +16397,78 @@ var publicWorkspace = (organization) => ({
16305
16397
  name: organization.name
16306
16398
  });
16307
16399
 
16400
+ // packages/contracts/dist/feedback.js
16401
+ var FEEDBACK_HISTORY_PAGE_SIZE = 25;
16402
+ var FEEDBACK_REPLY_MAX_LENGTH = 2e3;
16403
+ var FEEDBACK_UPDATE_STATES = [
16404
+ "in_review",
16405
+ "planned",
16406
+ "in_progress",
16407
+ "resolved",
16408
+ "closed"
16409
+ ];
16410
+ var releasePattern = /^[A-Za-z0-9][A-Za-z0-9._:/+-]{0,63}$/u;
16411
+ var identifier = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
16412
+ var detailPrefix = "Feedback was not accepted:";
16413
+ var detailSuffix = "Nothing was stored; correct these fields and submit again.";
16414
+ var detailSentence = new RegExp("^" + detailPrefix + "((?: (?:" + [
16415
+ "Only organization_id, project_id, environment_id, operation_id, kind, title, description, error_code and client_version are accepted; remove other fields such as attachments or logs\\.",
16416
+ "organization_id must be the 26-character workspace ULID\\.",
16417
+ "(?:project|environment|operation)_id must be a 26-character ULID\\.",
16418
+ "environment_id and operation_id require project_id\\.",
16419
+ "kind must be bug, issue or feature_request\\.",
16420
+ "(?:title|description) must be a string\\.",
16421
+ "(?:title|description) must not be empty\\.",
16422
+ "(?:title|description) must not start or end with whitespace; trim it\\.",
16423
+ "(?:title|description) has [1-9][0-9]{0,8} characters, over the (?:160|8000)-character limit; shorten it, it is never truncated\\.",
16424
+ "title must not contain line breaks, tabs or other control characters\\.",
16425
+ "description must not contain control characters other than tabs and line breaks, such as terminal color codes\\.",
16426
+ "(?:error_code|client_version) must be 1\u2013128 letters, digits and the characters \\._:/\\+- starting with a letter or digit\\."
16427
+ ].join("|") + "))+) " + detailSuffix.replace(".", "\\.") + "$", "u");
16428
+ function parseFeedbackSubmissionDetail(value) {
16429
+ return typeof value === "string" && value.length <= 2e3 && detailSentence.test(value) ? value : null;
16430
+ }
16431
+ function parseFeedbackReceipt(value) {
16432
+ if (typeof value !== "object" || value === null || Array.isArray(value))
16433
+ throw new TypeError("Feedback was not acknowledged");
16434
+ const v = value;
16435
+ if (Object.keys(v).sort().join(",") !== "id,organization_id,submitted_at" || typeof v["id"] !== "string" || !identifier.test(v["id"]) || typeof v["organization_id"] !== "string" || !identifier.test(v["organization_id"]) || typeof v["submitted_at"] !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(v["submitted_at"]) || !Number.isFinite(Date.parse(v["submitted_at"])) || new Date(v["submitted_at"]).toISOString() !== v["submitted_at"])
16436
+ throw new TypeError("Feedback was not acknowledged");
16437
+ return v;
16438
+ }
16439
+ function parseFeedbackStatus(value) {
16440
+ if (typeof value !== "object" || value === null || Array.isArray(value))
16441
+ throw new TypeError("Feedback status is invalid");
16442
+ const v = value;
16443
+ const history = v["history"];
16444
+ if (Object.keys(v).sort().join(",") !== "history,id,next_cursor,organization_id,status,submitted_at,updated_at" || v["next_cursor"] !== null && !validIdentifier(v["next_cursor"]) || !validIdentifier(v["id"]) || !validIdentifier(v["organization_id"]) || !validTimestamp(v["submitted_at"]) || !validTimestamp(v["updated_at"]) || typeof v["status"] !== "string" || !(v["status"] === "received" || FEEDBACK_UPDATE_STATES.includes(v["status"])) || !Array.isArray(history) || history.length > FEEDBACK_HISTORY_PAGE_SIZE || v["next_cursor"] !== null && history.length !== FEEDBACK_HISTORY_PAGE_SIZE || !history.every(validUpdate))
16445
+ throw new TypeError("Feedback status is invalid");
16446
+ return v;
16447
+ }
16448
+ function validUpdate(value) {
16449
+ if (typeof value !== "object" || value === null || Array.isArray(value))
16450
+ return false;
16451
+ const v = value;
16452
+ const keys = Object.keys(v);
16453
+ const status = v["status"];
16454
+ return keys.every((key) => ["id", "created_at", "status", "release", "reply"].includes(key)) && validIdentifier(v["id"]) && validTimestamp(v["created_at"]) && (status === void 0 || typeof status === "string" && FEEDBACK_UPDATE_STATES.includes(status)) && (status !== void 0 || "reply" in v) && ("release" in v ? status === "resolved" && typeof v["release"] === "string" && releasePattern.test(v["release"]) : status !== "resolved") && (!("reply" in v) || validText(v["reply"], FEEDBACK_REPLY_MAX_LENGTH, true)) && (status !== "resolved" && status !== "closed" || "reply" in v);
16455
+ }
16456
+ function validIdentifier(value) {
16457
+ return typeof value === "string" && identifier.test(value);
16458
+ }
16459
+ function validTimestamp(value) {
16460
+ return typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString() === value;
16461
+ }
16462
+ function validText(value, max, multiline) {
16463
+ return typeof value === "string" && value === value.trim() && Array.from(value).length >= 1 && Array.from(value).length <= max && !hasControlCharacter(value, multiline);
16464
+ }
16465
+ function hasControlCharacter(value, multiline) {
16466
+ return Array.from(value).some((character) => {
16467
+ const code = character.charCodeAt(0);
16468
+ return code < 32 && !(multiline && (code === 9 || code === 10 || code === 13)) || code >= 127 && code <= 159;
16469
+ });
16470
+ }
16471
+
16308
16472
  // packages/contracts/dist/framework-admission.js
16309
16473
  var FrameworkAdmissionError = class extends Error {
16310
16474
  reason;
@@ -16754,7 +16918,7 @@ var PROBLEM_POLICY = {
16754
16918
  cloudflare_zone_not_bound: {
16755
16919
  status: 409,
16756
16920
  retryable: false,
16757
- action: "Configure the project domain first with 'ohmyhost domain paid plan' then 'ohmyhost domain paid apply', or configure its sender domain with 'ohmyhost mail domain set'. Then request Cloudflare authorization for that domain's zone."
16921
+ action: "Configure the project domain first with 'ohmyhost domain paid plan' then 'ohmyhost domain paid apply', or configure its sender domain with 'ohmyhost mail setup'. Then request Cloudflare authorization for that domain's zone."
16758
16922
  },
16759
16923
  github_connection_required: {
16760
16924
  status: 409,
@@ -16909,6 +17073,11 @@ var PROBLEM_POLICY = {
16909
17073
  retryable: false,
16910
17074
  action: "Select the current succeeded dev deployment and target prod."
16911
17075
  },
17076
+ mail_capacity_unavailable: {
17077
+ status: 503,
17078
+ retryable: true,
17079
+ action: "Report this request ID through feedback. The stored mail setup continues automatically once ohmyho.st has capacity; follow mail status and keep the original idempotency key."
17080
+ },
16912
17081
  mail_domain_conflict: {
16913
17082
  status: 409,
16914
17083
  retryable: false,
@@ -16917,13 +17086,23 @@ var PROBLEM_POLICY = {
16917
17086
  mail_domain_required: {
16918
17087
  status: 409,
16919
17088
  retryable: false,
16920
- action: "Run 'ohmyhost mail domain set' for this project, then plan again."
17089
+ action: "This app declares mail.enabled. Configure the project's own sender domain with 'ohmyhost mail setup' using its Prod environment ID, or set mail.enabled to false if the app sends no mail, then plan again."
16921
17090
  },
16922
17091
  storage_jurisdiction_conflict: {
16923
17092
  status: 409,
16924
17093
  retryable: false,
16925
17094
  action: "Set storage.jurisdiction in ohmyhost.yaml to the project's hosting region shown by project status, then plan again."
16926
17095
  },
17096
+ shared_data_requires_promotion: {
17097
+ status: 409,
17098
+ retryable: false,
17099
+ action: "Plan and deploy this commit to Dev, then run 'ohmyhost deployment promote plan' to reach Prod."
17100
+ },
17101
+ production_deployment_required: {
17102
+ status: 409,
17103
+ retryable: false,
17104
+ action: "Deploy to Prod first: run 'ohmyhost plan --environment prod' and deploy that plan, or promote the current Dev deployment with 'ohmyhost deployment promote plan'. Then plan the domain again."
17105
+ },
16927
17106
  framework_conversion_required: {
16928
17107
  status: 409,
16929
17108
  retryable: false,
@@ -17181,6 +17360,12 @@ var parseCloudflareDnsAuthorizationStatus = (value) => {
17181
17360
  }
17182
17361
  return input;
17183
17362
  };
17363
+ var parseDevAccessState = (value) => {
17364
+ const input = exactRecord2(value, ["project_id", "mode", "share_url"]);
17365
+ if (!isUlid(input["project_id"]) || input["mode"] !== "protected" && input["mode"] !== "public" || input["mode"] === "public" && input["share_url"] !== null || input["share_url"] !== null && (typeof input["share_url"] !== "string" || !/^https:\/\/dev-[a-z0-9]+(?:-[a-z0-9]+){0,4}\.(?:dev\.)?check\.omh\.st\/\.ohmyhost\/dev-access\/redeem\?stage=dev&ticket=[A-Za-z0-9_-]{43}$/u.test(input["share_url"])))
17366
+ throw new ResponseContractError();
17367
+ return input;
17368
+ };
17184
17369
  var parseDevAccessTicket = (value) => {
17185
17370
  const input = exactRecord2(value, [
17186
17371
  "ticket_id",
@@ -17336,6 +17521,7 @@ var parseProjectStatus = (value) => {
17336
17521
  "etag",
17337
17522
  "handle",
17338
17523
  "region",
17524
+ "dev_access_mode",
17339
17525
  "lifecycle",
17340
17526
  "source",
17341
17527
  "default_environment",
@@ -17347,7 +17533,7 @@ var parseProjectStatus = (value) => {
17347
17533
  "cleanup_state"
17348
17534
  ]);
17349
17535
  const handle = input["handle"];
17350
- if (!isUlid(input["project_id"]) || typeof input["etag"] !== "string" || !/^"sha256-[a-f0-9]{64}"$/u.test(input["etag"]) || !boundedString(handle, 3, 59) || !/^[a-z0-9]+(?:-[a-z0-9]+){0,4}$/u.test(handle) || input["region"] !== "us" && input["region"] !== "eu" || input["lifecycle"] !== "active" && input["lifecycle"] !== "deleting" && input["lifecycle"] !== "deleted" || input["dev_url"] !== null && ![`https://dev-${handle}.dev.check.omh.st`, `https://dev-${handle}.check.omh.st`].includes(String(input["dev_url"])) || input["prod_url"] !== null && ![`https://${handle}.dev.check.omh.st`, `https://${handle}.check.omh.st`].includes(String(input["prod_url"])) || input["cleanup_state"] !== "not_started" && input["cleanup_state"] !== "pending" && input["cleanup_state"] !== "completed" && input["cleanup_state"] !== "reconciliation_required") {
17536
+ if (!isUlid(input["project_id"]) || typeof input["etag"] !== "string" || !/^"sha256-[a-f0-9]{64}"$/u.test(input["etag"]) || !boundedString(handle, 3, 59) || !/^[a-z0-9]+(?:-[a-z0-9]+){0,4}$/u.test(handle) || input["region"] !== "us" && input["region"] !== "eu" || input["dev_access_mode"] !== "protected" && input["dev_access_mode"] !== "public" || input["lifecycle"] !== "active" && input["lifecycle"] !== "deleting" && input["lifecycle"] !== "deleted" || input["dev_url"] !== null && ![`https://dev-${handle}.dev.check.omh.st`, `https://dev-${handle}.check.omh.st`].includes(String(input["dev_url"])) || input["prod_url"] !== null && ![`https://${handle}.dev.check.omh.st`, `https://${handle}.check.omh.st`].includes(String(input["prod_url"])) || input["cleanup_state"] !== "not_started" && input["cleanup_state"] !== "pending" && input["cleanup_state"] !== "completed" && input["cleanup_state"] !== "reconciliation_required") {
17351
17537
  throw new ResponseContractError();
17352
17538
  }
17353
17539
  if (input["source"] !== null)
@@ -17518,6 +17704,7 @@ var parseDeploymentPlan = (value) => {
17518
17704
  const input = exactRecord2(value, [
17519
17705
  "id",
17520
17706
  "project_id",
17707
+ "environment",
17521
17708
  "commit_sha",
17522
17709
  "source_digest",
17523
17710
  "build_plan_digest",
@@ -17539,11 +17726,12 @@ var parseDeploymentPlan = (value) => {
17539
17726
  "currency",
17540
17727
  "rate_card_version",
17541
17728
  "credit_micros",
17729
+ "credits",
17542
17730
  "provider_cost_micros",
17543
17731
  "scope",
17544
17732
  "reserved_seconds"
17545
17733
  ]);
17546
- if (!isUlid(input["id"]) || !isUlid(input["project_id"]) || !isCommitSha(input["commit_sha"]) || !isDigest(input["source_digest"]) || !isDigest(input["build_plan_digest"]) || !isApplicationRoot(input["application_root"]) || input["artifact_digest"] !== void 0 && !isDigest(input["artifact_digest"]) || input["runtime"] !== "cloudflare_workers_static_assets" && input["runtime"] !== "cloudflare_workers_edge_ssr" || !isHttpsUrlValue(input["route"]) || !stringArray(input["resource_effects"]) || !stringArray(input["limits"]) || !stringArray(input["risks"]) || !stringArray(input["destructive_effects"]) || !stringArray(input["required_confirmations"]) || !isIsoInstant(input["created_at"]) || !isIsoInstant(input["expires_at"]) || !decimal(cost["amount_micros"]) || !boundedString(cost["currency"], 1, 16) || !boundedString(cost["rate_card_version"], 1, 128) || !decimal(cost["credit_micros"]) || !decimal(cost["provider_cost_micros"]) || cost["scope"] !== "build_compute" || !decimal(cost["reserved_seconds"]) || cost["reserved_seconds"] === "0")
17734
+ if (!isUlid(input["id"]) || !isUlid(input["project_id"]) || input["environment"] !== "dev" && input["environment"] !== "prod" || !isCommitSha(input["commit_sha"]) || !isDigest(input["source_digest"]) || !isDigest(input["build_plan_digest"]) || !isApplicationRoot(input["application_root"]) || input["artifact_digest"] !== void 0 && !isDigest(input["artifact_digest"]) || input["runtime"] !== "cloudflare_workers_static_assets" && input["runtime"] !== "cloudflare_workers_edge_ssr" || !isHttpsUrlValue(input["route"]) || !stringArray(input["resource_effects"]) || !stringArray(input["limits"]) || !stringArray(input["risks"]) || !stringArray(input["destructive_effects"]) || !stringArray(input["required_confirmations"]) || !isIsoInstant(input["created_at"]) || !isIsoInstant(input["expires_at"]) || !decimal(cost["amount_micros"]) || !boundedString(cost["currency"], 1, 16) || !boundedString(cost["rate_card_version"], 1, 128) || !decimal(cost["credit_micros"]) || typeof cost["credits"] !== "string" || !/^[0-9]+(?:\.[0-9]{1,6})?$/u.test(cost["credits"]) || !decimal(cost["provider_cost_micros"]) || cost["scope"] !== "build_compute" || !decimal(cost["reserved_seconds"]) || cost["reserved_seconds"] === "0")
17547
17735
  throw new ResponseContractError();
17548
17736
  return input;
17549
17737
  };
@@ -17624,11 +17812,13 @@ var parseSafeProblem = (value) => {
17624
17812
  const retryAfter = input["retry_after_seconds"];
17625
17813
  if (retryAfter !== void 0 && (code !== "rate_limited" || typeof retryAfter !== "number" || !Number.isSafeInteger(retryAfter) || retryAfter < 1 || retryAfter > 86400))
17626
17814
  throw new ResponseContractError();
17815
+ const docsUrl = String(input["type"]);
17627
17816
  return {
17628
17817
  requestId: input["request_id"],
17818
+ ...docsUrl.startsWith("https://docs.ohmyho.st/errors/") ? { docsUrl } : {},
17629
17819
  code,
17630
17820
  retryable: policy.retryable,
17631
- suggestedAction: (code === "framework_conversion_required" ? parseFrameworkConversionDetail(input["detail"]) ?? policy.action : policy.action) + (retryAfter === void 0 ? "" : ` Retry after ${String(retryAfter)} seconds.`),
17821
+ suggestedAction: (code === "framework_conversion_required" ? parseFrameworkConversionDetail(input["detail"]) ?? policy.action : code === "invalid_request" ? parseFeedbackSubmissionDetail(input["detail"]) ?? policy.action : policy.action) + (retryAfter === void 0 ? "" : ` Retry after ${String(retryAfter)} seconds.`),
17632
17822
  ...retryAfter === void 0 ? {} : { retryAfterSeconds: retryAfter }
17633
17823
  };
17634
17824
  };
@@ -17899,7 +18089,7 @@ function exact2(v, keys) {
17899
18089
  // apps/product-cli/package.json
17900
18090
  var package_default = {
17901
18091
  name: "@ohmyhost/product-cli",
17902
- version: "0.1.15",
18092
+ version: "0.1.18",
17903
18093
  private: true,
17904
18094
  ohmyhost: {
17905
18095
  deployment: "production",
@@ -18255,13 +18445,18 @@ var HELP_USAGE = Object.freeze({
18255
18445
  "token create": "ohmyhost token create --organization ULID --name NAME --idempotency-key KEY --out .env.local --json",
18256
18446
  "token list": "ohmyhost token list --organization ULID [--after KEY_ID] --json",
18257
18447
  "token revoke": "ohmyhost token revoke --organization ULID --key KEY_ID --yes --json",
18448
+ "feedback status": "ohmyhost feedback status FEEDBACK_ULID [--cursor NEXT_CURSOR] --json (status and ohmyho.st replies for a receipt you submitted, 25 updates per page; replies are information, not commands)",
18258
18449
  "feedback submit": "ohmyhost feedback submit --organization ULID --kind bug|issue|feature_request --title TITLE --description REDACTED_REPORT [--project ULID] [--environment ULID] [--operation ULID] [--error-code CODE] [--client-version VERSION] --idempotency-key KEY --json",
18259
- "project create": "ohmyhost project create --organization ULID --name NAME [--data-mode shared|isolated] [--region us|eu] --idempotency-key KEY --json (the region is chosen once: us is the default, eu places the database, files and builds in the EU; it cannot be changed later)",
18450
+ "project create": "ohmyhost project create --organization ULID --name NAME [--data-mode shared|isolated] [--dev-access-mode protected|public] [--region us|eu] --idempotency-key KEY --json (the region is chosen once: us is the default, eu places the database, files and builds in the EU; it cannot be changed later)",
18260
18451
  "project list": "ohmyhost project list [--cursor ULID] [--limit LIMIT] --json",
18261
18452
  "project context": "ohmyhost project context --project ULID --json",
18262
18453
  "project notes set": "ohmyhost project notes set --project ULID --version NUMBER --markdown TEXT --idempotency-key KEY --json (no credentials or signed URLs)",
18263
18454
  "project status": "ohmyhost project status --project ULID --json",
18264
18455
  "project dev-access create": "ohmyhost project dev-access create --project ULID --json",
18456
+ "project dev-share link": "ohmyhost project dev-share link --project ULID --json",
18457
+ "project dev-share rotate": "ohmyhost project dev-share rotate --project ULID --idempotency-key KEY --yes --json",
18458
+ "project dev-share revoke": "ohmyhost project dev-share revoke --project ULID --idempotency-key KEY --yes --json",
18459
+ "project dev-access mode": "ohmyhost project dev-access mode --project ULID --mode protected|public --idempotency-key KEY --yes --json",
18265
18460
  "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)",
18266
18461
  "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)",
18267
18462
  "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.",
@@ -18282,8 +18477,8 @@ var HELP_USAGE = Object.freeze({
18282
18477
  "domain paid apply": "ohmyhost domain paid apply --project ULID --hostname HOST --idempotency-key KEY --yes --json",
18283
18478
  "domain paid status": "ohmyhost domain paid status --project ULID --json",
18284
18479
  "domain paid delete": "ohmyhost domain paid delete --project ULID --hostname HOST --idempotency-key KEY --yes --json",
18285
- plan: "ohmyhost plan --project ULID --commit SHA --json",
18286
- deploy: "ohmyhost deploy --project ULID --plan-id ULID --idempotency-key KEY --yes [--wait] --json",
18480
+ plan: "ohmyhost plan --project ULID --commit SHA [--environment dev|prod] --json",
18481
+ deploy: "ohmyhost deploy --project ULID (--plan-id ULID | --commit SHA [--environment dev|prod]) --idempotency-key KEY --yes [--wait] --json",
18287
18482
  logs: "ohmyhost logs OPERATION_ULID --follow --json",
18288
18483
  "deployment logs": "ohmyhost deployment logs --project ULID --deployment ULID --follow --json",
18289
18484
  "rollback plan": "ohmyhost rollback plan --project ULID --deployment DEPLOYMENT_ULID --json",
@@ -18305,7 +18500,8 @@ var HELP_USAGE = Object.freeze({
18305
18500
  "mail messages get": "ohmyhost mail messages get --project ULID --environment ULID --message ULID --json",
18306
18501
  "mail messages retry": "ohmyhost mail messages retry --project ULID --environment ULID --message ULID --idempotency-key KEY --json",
18307
18502
  "mail domain set": "ohmyhost mail domain set --project ULID --environment ULID --domain DOMAIN --sending true --receiving false --idempotency-key KEY --json",
18308
- "mail domain status": "ohmyhost mail domain status --project ULID --environment ULID --json"
18503
+ "mail domain status": "ohmyhost mail domain status --project ULID --environment ULID --json",
18504
+ "mail domain delete": "ohmyhost mail domain delete --project ULID --environment ULID --idempotency-key KEY --yes --json"
18309
18505
  });
18310
18506
  var HELP_COMMANDS = Object.freeze(Object.entries(HELP_USAGE).map(([command, usage]) => Object.freeze({ command, usage })));
18311
18507
  var cliMetadataResult = (kind, topic) => kind === "help" ? success("help", {
@@ -18535,6 +18731,11 @@ var ProductCli = class {
18535
18731
  assertProjectExport(result);
18536
18732
  return success("export get", { status: result.state, export: result });
18537
18733
  }
18734
+ if (command.kind === "feedback-status") {
18735
+ const api = await this.authenticatedApi(command.credentialStore, signal);
18736
+ const feedback = await api.getFeedback(command.feedbackId, command.cursor);
18737
+ return success("feedback status", { status: "succeeded", feedback });
18738
+ }
18538
18739
  if (command.kind === "feedback-submit") {
18539
18740
  const api = await this.authenticatedApi(command.credentialStore, signal);
18540
18741
  return success("feedback submit", {
@@ -18581,12 +18782,17 @@ var ProductCli = class {
18581
18782
  return await this.getProjectStatus(command, signal);
18582
18783
  if (command.kind === "project-dev-access-create")
18583
18784
  return await this.createDevAccessTicket(command, signal);
18785
+ 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") {
18786
+ const api = await this.authenticatedApi(command.credentialStore, signal);
18787
+ 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));
18788
+ return success(command.kind === "project-dev-share-link" ? "project dev-share link" : command.kind === "project-dev-share-rotate" ? "project dev-share rotate" : command.kind === "project-dev-share-revoke" ? "project dev-share revoke" : "project dev-access mode", { status: "succeeded", state });
18789
+ }
18584
18790
  if (command.kind === "database-write") {
18585
18791
  const file = await open2(command.statementFile, "r"), buffer = Buffer.alloc(65537);
18586
18792
  let statement;
18587
18793
  try {
18588
- const stat2 = await file.stat();
18589
- if (!stat2.isFile() || stat2.size > 65536)
18794
+ const stat3 = await file.stat();
18795
+ if (!stat3.isFile() || stat3.size > 65536)
18590
18796
  throw new InvalidCommandError();
18591
18797
  let length = 0;
18592
18798
  while (length < buffer.length) {
@@ -18878,6 +19084,7 @@ var ProductCli = class {
18878
19084
  organizationId: command.organizationId,
18879
19085
  name: command.name,
18880
19086
  ...command.dataMode === void 0 ? {} : { dataMode: command.dataMode },
19087
+ ...command.devAccessMode === void 0 ? {} : { devAccessMode: command.devAccessMode },
18881
19088
  ...command.region === void 0 ? {} : { region: command.region },
18882
19089
  idempotencyKey: command.idempotencyKey
18883
19090
  }));
@@ -19069,7 +19276,10 @@ var ProductCli = class {
19069
19276
  async getAutoDeploy(command, signal) {
19070
19277
  const api = await this.authenticatedApi(command.credentialStore, signal);
19071
19278
  const autoDeploy = await api.getAutoDeploy(command.projectId);
19072
- return success("source auto-deploy status", { status: "ready", auto_deploy: autoDeploy });
19279
+ return success("source auto-deploy status", {
19280
+ status: autoDeploy.generation === 0 ? "not_configured" : "ready",
19281
+ auto_deploy: autoDeploy
19282
+ });
19073
19283
  }
19074
19284
  async authorizeCloudflareDns(command, signal) {
19075
19285
  const api = await this.authenticatedApi(command.credentialStore, signal);
@@ -19103,20 +19313,35 @@ var ProductCli = class {
19103
19313
  async planDeployment(command, signal) {
19104
19314
  const api = await this.authenticatedApi(command.credentialStore, signal);
19105
19315
  const plan = parseDeploymentPlan(await api.planDeployment(command));
19106
- return success("plan", { status: "planned", plan });
19316
+ const notes = plan["environment"] === "prod" ? [
19317
+ `This plan builds straight into Prod (${String(plan["route"])}); Prod traffic switches once it is activated.`
19318
+ ] : [
19319
+ `This plan updates Dev only (${String(plan["route"])}). To go live, deploy with --environment prod or promote the Dev deployment with '${this.dependencies.commandPrefix} deployment promote plan --project ULID --deployment DEV_DEPLOYMENT_ULID --json'.`
19320
+ ];
19321
+ return success("plan", { status: "planned", plan, notes });
19107
19322
  }
19108
19323
  async deploy(command, signal) {
19109
19324
  const api = await this.authenticatedApi(command.credentialStore, signal);
19110
- let operation = parseOperation(await api.createDeployment(command));
19325
+ const plan = command.commitSha === null ? null : parseDeploymentPlan(await api.planDeployment({
19326
+ projectId: command.projectId,
19327
+ commitSha: command.commitSha,
19328
+ environment: command.environment
19329
+ }));
19330
+ let operation = parseOperation(await api.createDeployment({
19331
+ projectId: command.projectId,
19332
+ planId: plan === null ? String(command.planId) : String(plan["id"]),
19333
+ idempotencyKey: command.idempotencyKey
19334
+ }));
19111
19335
  if (command.wait)
19112
19336
  operation = await this.waitForOperation(api, operation, signal);
19113
19337
  const notes = operation.state === "succeeded" ? [
19114
- `A Dev URL answers 404 until you redeem an access ticket: run '${this.dependencies.commandPrefix} project dev-access create --project ULID --json' and open its redeem_url once.`
19338
+ `Check project status for dev_access_mode. For protected Dev, run '${this.dependencies.commandPrefix} project dev-share link --project ULID --json' and open or share its persistent share_url; public Dev opens directly.`
19115
19339
  ] : [];
19116
19340
  return success("deploy", {
19117
19341
  status: command.wait ? "completed" : "accepted",
19118
19342
  operation,
19119
- ...notes.length > 0 ? { notes } : {}
19343
+ ...plan === null ? {} : { plan },
19344
+ ...notes.length > 0 && plan?.["environment"] !== "prod" ? { notes } : {}
19120
19345
  });
19121
19346
  }
19122
19347
  async logs(command, signal) {
@@ -19396,7 +19621,7 @@ var classifyFailure = (command, error, signal) => {
19396
19621
  if (looksLikeProblem(error)) {
19397
19622
  try {
19398
19623
  const problem = parseSafeProblem(error);
19399
- return failure(command, 7, problem.code, problem.retryable, problem.suggestedAction, void 0, problem.retryAfterSeconds, problem.requestId);
19624
+ return failure(command, 7, problem.code, problem.retryable, problem.suggestedAction, void 0, problem.retryAfterSeconds, problem.requestId, problem.docsUrl);
19400
19625
  } catch {
19401
19626
  return failure(command, 7, "response_contract_invalid", false, "Retry only after the client or server contract is repaired.");
19402
19627
  }
@@ -19416,7 +19641,7 @@ var success = (command, body) => ({
19416
19641
  `,
19417
19642
  stderr: ""
19418
19643
  });
19419
- var failure = (command, exitCode, code, retryable, suggestedAction, operation, retryAfterSeconds, requestId) => {
19644
+ var failure = (command, exitCode, code, retryable, suggestedAction, operation, retryAfterSeconds, requestId, docsUrl) => {
19420
19645
  const document = {
19421
19646
  version: 1,
19422
19647
  command,
@@ -19428,6 +19653,7 @@ var failure = (command, exitCode, code, retryable, suggestedAction, operation, r
19428
19653
  suggested_action: suggestedAction,
19429
19654
  ...retryAfterSeconds === void 0 ? {} : { retry_after_seconds: retryAfterSeconds },
19430
19655
  ...requestId === void 0 ? {} : { request_id: requestId },
19656
+ ...docsUrl === void 0 ? {} : { docs_url: docsUrl },
19431
19657
  ...operation?.error === void 0 ? {} : { message: operation.error.message }
19432
19658
  }
19433
19659
  };
@@ -19471,6 +19697,8 @@ var parsedCommandName = (command) => {
19471
19697
  return "operation reconcile";
19472
19698
  if (command.kind === "feedback-submit")
19473
19699
  return "feedback submit";
19700
+ if (command.kind === "feedback-status")
19701
+ return "feedback status";
19474
19702
  if (command.kind === "project-create")
19475
19703
  return "project create";
19476
19704
  if (command.kind === "organization-create")
@@ -19497,6 +19725,14 @@ var parsedCommandName = (command) => {
19497
19725
  return "project status";
19498
19726
  if (command.kind === "project-dev-access-create")
19499
19727
  return "project dev-access create";
19728
+ if (command.kind === "project-dev-share-link")
19729
+ return "project dev-share link";
19730
+ if (command.kind === "project-dev-share-rotate")
19731
+ return "project dev-share rotate";
19732
+ if (command.kind === "project-dev-share-revoke")
19733
+ return "project dev-share revoke";
19734
+ if (command.kind === "project-dev-access-mode-set")
19735
+ return "project dev-access mode";
19500
19736
  if (command.kind === "project-handle-check")
19501
19737
  return "project handle check";
19502
19738
  if (command.kind === "project-handle-set")
@@ -20630,6 +20866,24 @@ var submitFeedback = (parameters, options) => {
20630
20866
  }
20631
20867
  });
20632
20868
  };
20869
+ var getFeedback = (parameters, options) => {
20870
+ const params = buildClientParams([parameters], [
20871
+ {
20872
+ args: [
20873
+ { in: "headers", key: "X-Request-Id" },
20874
+ { in: "path", key: "feedback_id" },
20875
+ { in: "query", key: "cursor" }
20876
+ ]
20877
+ }
20878
+ ]);
20879
+ return (options?.client ?? client).get({
20880
+ responseStyle: "data",
20881
+ security: [{ scheme: "bearer", type: "http" }],
20882
+ url: "/v1/feedback/{feedback_id}",
20883
+ ...options,
20884
+ ...params
20885
+ });
20886
+ };
20633
20887
  var getCurrentIdentity = (parameters, options) => {
20634
20888
  const params = buildClientParams([parameters], [{ args: [{ in: "headers", key: "X-Request-Id" }] }]);
20635
20889
  return (options?.client ?? client).get({
@@ -21046,6 +21300,83 @@ var getProjectHandleAvailability = (parameters, options) => {
21046
21300
  ...params
21047
21301
  });
21048
21302
  };
21303
+ var revokeProjectDevShareLink = (parameters, options) => {
21304
+ const params = buildClientParams([parameters], [
21305
+ {
21306
+ args: [
21307
+ { in: "headers", key: "X-Request-Id" },
21308
+ { in: "headers", key: "Idempotency-Key" },
21309
+ { in: "path", key: "project_id" }
21310
+ ]
21311
+ }
21312
+ ]);
21313
+ return (options?.client ?? client).delete({
21314
+ responseStyle: "data",
21315
+ security: [{ scheme: "bearer", type: "http" }],
21316
+ url: "/v1/projects/{project_id}/dev-share-link",
21317
+ ...options,
21318
+ ...params
21319
+ });
21320
+ };
21321
+ var ensureProjectDevShareLink = (parameters, options) => {
21322
+ const params = buildClientParams([parameters], [
21323
+ {
21324
+ args: [
21325
+ { in: "headers", key: "X-Request-Id" },
21326
+ { in: "path", key: "project_id" }
21327
+ ]
21328
+ }
21329
+ ]);
21330
+ return (options?.client ?? client).post({
21331
+ responseStyle: "data",
21332
+ security: [{ scheme: "bearer", type: "http" }],
21333
+ url: "/v1/projects/{project_id}/dev-share-link",
21334
+ ...options,
21335
+ ...params
21336
+ });
21337
+ };
21338
+ var rotateProjectDevShareLink = (parameters, options) => {
21339
+ const params = buildClientParams([parameters], [
21340
+ {
21341
+ args: [
21342
+ { in: "headers", key: "X-Request-Id" },
21343
+ { in: "headers", key: "Idempotency-Key" },
21344
+ { in: "path", key: "project_id" }
21345
+ ]
21346
+ }
21347
+ ]);
21348
+ return (options?.client ?? client).post({
21349
+ responseStyle: "data",
21350
+ security: [{ scheme: "bearer", type: "http" }],
21351
+ url: "/v1/projects/{project_id}/dev-share-link:rotate",
21352
+ ...options,
21353
+ ...params
21354
+ });
21355
+ };
21356
+ var setProjectDevAccessMode = (parameters, options) => {
21357
+ const params = buildClientParams([parameters], [
21358
+ {
21359
+ args: [
21360
+ { in: "headers", key: "X-Request-Id" },
21361
+ { in: "headers", key: "Idempotency-Key" },
21362
+ { in: "path", key: "project_id" },
21363
+ { key: "setDevAccessModeRequest", map: "body" }
21364
+ ]
21365
+ }
21366
+ ]);
21367
+ return (options?.client ?? client).put({
21368
+ responseStyle: "data",
21369
+ security: [{ scheme: "bearer", type: "http" }],
21370
+ url: "/v1/projects/{project_id}/dev-access-mode",
21371
+ ...options,
21372
+ ...params,
21373
+ headers: {
21374
+ "Content-Type": "application/json",
21375
+ ...options?.headers,
21376
+ ...params.headers
21377
+ }
21378
+ });
21379
+ };
21049
21380
  var createProjectDevAccessTicket = (parameters, options) => {
21050
21381
  const params = buildClientParams([parameters], [
21051
21382
  {
@@ -21783,6 +22114,25 @@ var streamOperationEvents = (parameters, options) => {
21783
22114
  ...params
21784
22115
  });
21785
22116
  };
22117
+ var deleteManagedMailDomain = (parameters, options) => {
22118
+ const params = buildClientParams([parameters], [
22119
+ {
22120
+ args: [
22121
+ { in: "headers", key: "X-Request-Id" },
22122
+ { in: "headers", key: "Idempotency-Key" },
22123
+ { in: "path", key: "project_id" },
22124
+ { in: "path", key: "environment_id" }
22125
+ ]
22126
+ }
22127
+ ]);
22128
+ return (options?.client ?? client).delete({
22129
+ responseStyle: "data",
22130
+ security: [{ scheme: "bearer", type: "http" }],
22131
+ url: "/v1/projects/{project_id}/environments/{environment_id}/mail-domain",
22132
+ ...options,
22133
+ ...params
22134
+ });
22135
+ };
21786
22136
  var getManagedMailDomain = (parameters, options) => {
21787
22137
  const params = buildClientParams([parameters], [
21788
22138
  {
@@ -22009,6 +22359,14 @@ function assertManagedMailResponse(value, input) {
22009
22359
  invalid3();
22010
22360
  return;
22011
22361
  }
22362
+ if (input.action === "domain_delete") {
22363
+ exact3(r, ["status", "domain", "dns_records"]);
22364
+ if (r["status"] !== "deleted")
22365
+ invalid3();
22366
+ domain(r["domain"]);
22367
+ records(r["dns_records"]);
22368
+ return;
22369
+ }
22012
22370
  if (input.action === "messages_list") {
22013
22371
  exact3(r, ["items"]);
22014
22372
  if (!Array.isArray(r["items"]) || r["items"].length > 50)
@@ -22137,6 +22495,8 @@ function requestManagedMail(client2, input) {
22137
22495
  return verifyManagedMailWebhook({ ...scope, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
22138
22496
  case "webhook_disable":
22139
22497
  return disableManagedMailReceiving({ ...scope, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
22498
+ case "domain_delete":
22499
+ return deleteManagedMailDomain({ ...scope, "Idempotency-Key": input.idempotencyKey }, { client: client2 });
22140
22500
  case "messages_list":
22141
22501
  return listManagedMailMessages({ ...scope, ...input.after ? { after: input.after } : {} }, { client: client2 });
22142
22502
  case "message_get":
@@ -22151,17 +22511,6 @@ async function executeManagedMail(client2, input) {
22151
22511
  return result;
22152
22512
  }
22153
22513
 
22154
- // packages/contracts/dist/feedback.js
22155
- var identifier = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
22156
- function parseFeedbackReceipt(value) {
22157
- if (typeof value !== "object" || value === null || Array.isArray(value))
22158
- throw new TypeError("Feedback was not acknowledged");
22159
- const v = value;
22160
- if (Object.keys(v).sort().join(",") !== "id,organization_id,submitted_at" || typeof v["id"] !== "string" || !identifier.test(v["id"]) || typeof v["organization_id"] !== "string" || !identifier.test(v["organization_id"]) || typeof v["submitted_at"] !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(v["submitted_at"]) || !Number.isFinite(Date.parse(v["submitted_at"])) || new Date(v["submitted_at"]).toISOString() !== v["submitted_at"])
22161
- throw new TypeError("Feedback was not acknowledged");
22162
- return v;
22163
- }
22164
-
22165
22514
  // apps/product-cli/dist/product-api.js
22166
22515
  var GeneratedSdkProductApi = class {
22167
22516
  #client;
@@ -22376,12 +22725,31 @@ var GeneratedSdkProductApi = class {
22376
22725
  paidDomainRequest: { hostname: input.hostname }
22377
22726
  }, { client: this.#client });
22378
22727
  }
22728
+ async ensureDevShareLink(projectId) {
22729
+ return ensureProjectDevShareLink({ project_id: projectId }, { client: this.#client });
22730
+ }
22731
+ async rotateDevShareLink(projectId, idempotencyKey) {
22732
+ return rotateProjectDevShareLink({ project_id: projectId, "Idempotency-Key": idempotencyKey }, { client: this.#client });
22733
+ }
22734
+ async revokeDevShareLink(projectId, idempotencyKey) {
22735
+ return revokeProjectDevShareLink({ project_id: projectId, "Idempotency-Key": idempotencyKey }, { client: this.#client });
22736
+ }
22737
+ async setDevAccessMode(projectId, mode, idempotencyKey) {
22738
+ return setProjectDevAccessMode({
22739
+ project_id: projectId,
22740
+ "Idempotency-Key": idempotencyKey,
22741
+ setDevAccessModeRequest: { mode }
22742
+ }, { client: this.#client });
22743
+ }
22379
22744
  async createDevAccessTicket(projectId) {
22380
22745
  return createProjectDevAccessTicket({ project_id: projectId }, { client: this.#client });
22381
22746
  }
22382
22747
  async submitFeedback(input) {
22383
22748
  return parseFeedbackReceipt(await submitFeedback({ "Idempotency-Key": input.idempotencyKey, feedbackSubmission: input.report }, { client: this.#client }));
22384
22749
  }
22750
+ async getFeedback(feedbackId, cursor) {
22751
+ return parseFeedbackStatus(await getFeedback({ feedback_id: feedbackId, ...cursor === void 0 ? {} : { cursor } }, { client: this.#client }));
22752
+ }
22385
22753
  async createProject(input) {
22386
22754
  return createProject({
22387
22755
  "Idempotency-Key": input.idempotencyKey,
@@ -22389,6 +22757,7 @@ var GeneratedSdkProductApi = class {
22389
22757
  organization_id: input.organizationId,
22390
22758
  name: input.name,
22391
22759
  ...input.dataMode === void 0 ? {} : { data_mode: input.dataMode },
22760
+ ...input.devAccessMode === void 0 ? {} : { dev_access_mode: input.devAccessMode },
22392
22761
  ...input.region === void 0 ? {} : { region: input.region }
22393
22762
  }
22394
22763
  }, { client: this.#client });
@@ -22446,7 +22815,10 @@ var GeneratedSdkProductApi = class {
22446
22815
  return configureProjectDomains({ "Idempotency-Key": input.idempotencyKey, project_id: input.projectId }, { client: this.#client });
22447
22816
  }
22448
22817
  async planDeployment(input) {
22449
- return planDeployment({ project_id: input.projectId, planDeploymentRequest: { commit_sha: input.commitSha } }, { client: this.#client });
22818
+ return planDeployment({
22819
+ project_id: input.projectId,
22820
+ planDeploymentRequest: { commit_sha: input.commitSha, environment: input.environment }
22821
+ }, { client: this.#client });
22450
22822
  }
22451
22823
  async createDeployment(input) {
22452
22824
  return createDeployment({
@@ -22536,12 +22908,26 @@ var GeneratedSdkProductApi = class {
22536
22908
  };
22537
22909
 
22538
22910
  // apps/product-cli/dist/project-link-store.js
22539
- import { chmod, mkdir as mkdir2, rename, writeFile } from "node:fs/promises";
22911
+ import { appendFile, chmod, mkdir as mkdir2, readFile, rename, stat, writeFile } from "node:fs/promises";
22540
22912
  import { join } from "node:path";
22913
+ async function readLinkedProjectId(directory, apiOrigin) {
22914
+ try {
22915
+ const link = JSON.parse(await readFile(join(directory, "link.json"), "utf8"));
22916
+ if (typeof link !== "object" || link === null)
22917
+ return null;
22918
+ const { version: version2, api_origin, project_id } = link;
22919
+ return version2 === 1 && api_origin === apiOrigin && typeof project_id === "string" && /^[0-9A-HJKMNP-TV-Z]{26}$/u.test(project_id) ? project_id : null;
22920
+ } catch {
22921
+ return null;
22922
+ }
22923
+ }
22541
22924
  var OwnerOnlyProjectLinkStore = class {
22542
22925
  directory;
22543
- constructor(directory) {
22926
+ repositoryRoot;
22927
+ /** repositoryRoot: when it is a Git working tree, the link directory is kept out of Git. */
22928
+ constructor(directory, repositoryRoot) {
22544
22929
  this.directory = directory;
22930
+ this.repositoryRoot = repositoryRoot;
22545
22931
  }
22546
22932
  async save(metadata) {
22547
22933
  await mkdir2(this.directory, { mode: 448, recursive: true });
@@ -22553,12 +22939,33 @@ var OwnerOnlyProjectLinkStore = class {
22553
22939
  await chmod(temporary, 384);
22554
22940
  await rename(temporary, target);
22555
22941
  await chmod(target, 384);
22942
+ if (this.repositoryRoot !== void 0)
22943
+ await ignoreLinkDirectory(this.repositoryRoot);
22556
22944
  }
22557
22945
  };
22946
+ async function ignoreLinkDirectory(root) {
22947
+ try {
22948
+ await stat(join(root, ".git"));
22949
+ } catch {
22950
+ return;
22951
+ }
22952
+ const file = join(root, ".gitignore");
22953
+ let current = "";
22954
+ try {
22955
+ current = await readFile(file, "utf8");
22956
+ } catch {
22957
+ current = "";
22958
+ }
22959
+ const lines = current.split(/\r?\n/u).map((line) => line.trim());
22960
+ if (lines.some((line) => /^\/?\.ohmyhost\/?$/u.test(line)))
22961
+ return;
22962
+ await appendFile(file, `${current === "" || current.endsWith("\n") ? "" : "\n"}/.ohmyhost/
22963
+ `);
22964
+ }
22558
22965
 
22559
22966
  // apps/product-cli/dist/repository-init.js
22560
22967
  import { createHash } from "node:crypto";
22561
- import { lstat as lstat2, open as open3, readFile, readdir, stat } from "node:fs/promises";
22968
+ import { lstat as lstat2, open as open3, readFile as readFile2, readdir, stat as stat2 } from "node:fs/promises";
22562
22969
  import { dirname as dirname2, posix, resolve as resolve2 } from "node:path";
22563
22970
 
22564
22971
  // packages/contracts/dist/application-root.js
@@ -23346,7 +23753,7 @@ var STORAGE_RUNTIME_BINDINGS = Object.freeze([
23346
23753
  "OHMYHOST_STORAGE_GATEWAY_URL",
23347
23754
  "OHMYHOST_STORAGE_KEY"
23348
23755
  ]);
23349
- var CUSTOMER_RUNTIME_SOURCE = `https://ohmyho.st/releases/${package_default.version}/ohmyhost-customer-runtime-${package_default.version}.tgz`;
23756
+ var CUSTOMER_RUNTIME_SOURCE = `npm:@amerged/ohmyhost-runtime@${package_default.version}`;
23350
23757
  var SCHEDULED_HANDLER_PATTERN = /\bscheduled\s*(?:\(|:|,|\})/u;
23351
23758
  var DEFAULT_EXPORT_PATTERN = /\bexport\s+default\b/u;
23352
23759
  function defaultExportBlocker(modulePath) {
@@ -23440,7 +23847,7 @@ async function initializeRepository(input) {
23440
23847
  })
23441
23848
  });
23442
23849
  const targetBetterAuth = existingConfiguration?.auth?.provider === "better-auth";
23443
- const targetMail = targetBetterAuth || existingConfiguration?.mail?.enabled === true;
23850
+ const targetMail = existingConfiguration?.mail?.enabled === true;
23444
23851
  const targetStorage = r2 || supabaseStorage;
23445
23852
  const configuredCompanionRequired = existingConfiguration !== null && (existingConfiguration.database?.enabled === true || existingConfiguration.auth?.provider === "better-auth" || existingConfiguration.mail?.enabled === true || existingConfiguration.storage !== void 0 || (existingConfiguration.functions?.crons.length ?? 0) > 0);
23446
23853
  const companionRequired = framework === "vite" && (existingConfiguration === null ? databaseProvider !== "none" || targetBetterAuth || targetMail || targetStorage || edgeFunctionNames.size > 0 || applicationFiles.includes("src/ohmyhost/companion.ts") : configuredCompanionRequired);
@@ -23526,7 +23933,7 @@ async function cronBlockers(framework, config, applicationDirectory, files) {
23526
23933
  ];
23527
23934
  }
23528
23935
  const modulePath = framework === "vite" ? "src/ohmyhost/companion.ts" : WORKER_MODULE_PATH;
23529
- const source = files.includes(modulePath) ? await readFile(resolve2(applicationDirectory, modulePath), "utf8") : null;
23936
+ const source = files.includes(modulePath) ? await readFile2(resolve2(applicationDirectory, modulePath), "utf8") : null;
23530
23937
  if (framework !== "functions" && source !== null && !DEFAULT_EXPORT_PATTERN.test(source)) {
23531
23938
  return [defaultExportBlocker(modulePath)];
23532
23939
  }
@@ -23547,10 +23954,10 @@ async function storageClientBlockers(config, applicationDirectory, files) {
23547
23954
  if (!SOURCE_MODULE_PATTERN.test(path))
23548
23955
  continue;
23549
23956
  const absolute = resolve2(applicationDirectory, path);
23550
- const information = await stat(absolute);
23957
+ const information = await stat2(absolute);
23551
23958
  if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES)
23552
23959
  continue;
23553
- if (callsStorageClient(await readFile(absolute, "utf8")))
23960
+ if (callsStorageClient(await readFile2(absolute, "utf8")))
23554
23961
  return [];
23555
23962
  }
23556
23963
  return [
@@ -23568,10 +23975,10 @@ async function databaseContractBlockers(config, applicationDirectory, files) {
23568
23975
  if (!SOURCE_MODULE_PATTERN.test(path))
23569
23976
  continue;
23570
23977
  const absolute = resolve2(applicationDirectory, path);
23571
- const information = await stat(absolute);
23978
+ const information = await stat2(absolute);
23572
23979
  if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES)
23573
23980
  continue;
23574
- const source = (await readFile(absolute, "utf8")).replaceAll(INERT_SOURCE_PATTERN, " ");
23981
+ const source = (await readFile2(absolute, "utf8")).replaceAll(INERT_SOURCE_PATTERN, " ");
23575
23982
  if (DATABASE_PRIVATE_BINDING_PATTERN.test(source)) {
23576
23983
  blockers.push({
23577
23984
  code: "database_binding_private",
@@ -23590,7 +23997,7 @@ async function databaseContractBlockers(config, applicationDirectory, files) {
23590
23997
  async function workerModuleBlockers(framework, applicationDirectory, files) {
23591
23998
  if (framework !== "functions" || !files.includes(WORKER_MODULE_PATH))
23592
23999
  return [];
23593
- const source = await readFile(resolve2(applicationDirectory, WORKER_MODULE_PATH), "utf8");
24000
+ const source = await readFile2(resolve2(applicationDirectory, WORKER_MODULE_PATH), "utf8");
23594
24001
  return DEFAULT_EXPORT_PATTERN.test(source) ? [] : [defaultExportBlocker(WORKER_MODULE_PATH)];
23595
24002
  }
23596
24003
  function migrationFilenameBlockers(files, configuredPath, provider) {
@@ -23617,7 +24024,7 @@ async function migrationSqlBlockers(applicationDirectory, files, configuredPath,
23617
24024
  }
23618
24025
  const migrations = await Promise.all(migrationPaths.map(async (path) => Object.freeze({
23619
24026
  path,
23620
- body: new Uint8Array(await readFile(resolve2(applicationDirectory, root, path)))
24027
+ body: new Uint8Array(await readFile2(resolve2(applicationDirectory, root, path)))
23621
24028
  })));
23622
24029
  const blockers = [];
23623
24030
  for (const migration of migrations) {
@@ -23652,11 +24059,11 @@ async function readExistingConfiguration(repositoryRoot, repositoryFiles, applic
23652
24059
  if (!repositoryFiles.includes("ohmyhost.yaml"))
23653
24060
  return null;
23654
24061
  const configurationPath = resolve2(repositoryRoot, "ohmyhost.yaml");
23655
- const information = await stat(configurationPath);
24062
+ const information = await stat2(configurationPath);
23656
24063
  if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES) {
23657
24064
  throw new RepositoryInitError("repository_configuration_invalid", "Existing ohmyhost.yaml must be one bounded regular file.");
23658
24065
  }
23659
- const configuration = await readFile(configurationPath, "utf8");
24066
+ const configuration = await readFile2(configurationPath, "utf8");
23660
24067
  let parsed;
23661
24068
  try {
23662
24069
  parsed = parseOhmyhostConfigYaml(configuration);
@@ -23857,7 +24264,7 @@ async function readPackageJson(root) {
23857
24264
  const path = resolve2(root, "package.json");
23858
24265
  let information;
23859
24266
  try {
23860
- information = await stat(path);
24267
+ information = await stat2(path);
23861
24268
  } catch {
23862
24269
  throw new RepositoryInitError("package_manifest_missing", "package.json is required");
23863
24270
  }
@@ -23865,7 +24272,7 @@ async function readPackageJson(root) {
23865
24272
  throw new RepositoryInitError("package_manifest_invalid", "package.json is invalid");
23866
24273
  }
23867
24274
  try {
23868
- const value = JSON.parse(await readFile(path, "utf8"));
24275
+ const value = JSON.parse(await readFile2(path, "utf8"));
23869
24276
  if (!isRecord2(value))
23870
24277
  throw new TypeError();
23871
24278
  return value;
@@ -24127,10 +24534,10 @@ async function detectNextStandaloneOutput(root, framework, nextConfigPath) {
24127
24534
  if (framework !== "nextjs" || nextConfigPath === null)
24128
24535
  return false;
24129
24536
  const path = resolve2(root, nextConfigPath);
24130
- const information = await stat(path);
24537
+ const information = await stat2(path);
24131
24538
  if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES)
24132
24539
  return false;
24133
- return declaresStandaloneOutput(await readFile(path, "utf8"));
24540
+ return declaresStandaloneOutput(await readFile2(path, "utf8"));
24134
24541
  }
24135
24542
  function declaresStandaloneOutput(source) {
24136
24543
  const tokens = sourceTokens(source);
@@ -24196,10 +24603,10 @@ async function detectTanStackRuntime(root, files, framework, packageJson, viteCo
24196
24603
  if (viteConfigPath === null)
24197
24604
  return "ambiguous";
24198
24605
  const path = resolve2(root, viteConfigPath);
24199
- const information = await stat(path);
24606
+ const information = await stat2(path);
24200
24607
  if (!information.isFile() || information.size > MAXIMUM_PACKAGE_BYTES)
24201
24608
  return "ambiguous";
24202
- const source = await readFile(path, "utf8");
24609
+ const source = await readFile2(path, "utf8");
24203
24610
  const tokens = sourceTokens(source);
24204
24611
  if (isCompleteTanStackStatic(tokens))
24205
24612
  return "static";
@@ -24305,10 +24712,10 @@ async function detectR2Storage(root, files) {
24305
24712
  const configs = files.filter((path) => /(?:^|\/)wrangler\.(?:jsonc?|toml)$/u.test(path));
24306
24713
  for (const config of configs) {
24307
24714
  const path = resolve2(root, config);
24308
- const information = await stat(path);
24715
+ const information = await stat2(path);
24309
24716
  if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES)
24310
24717
  continue;
24311
- const source = await readFile(path, "utf8");
24718
+ const source = await readFile2(path, "utf8");
24312
24719
  if (/\br2_buckets\b/u.test(source))
24313
24720
  return true;
24314
24721
  }
@@ -24661,7 +25068,14 @@ var runProductionCli = async (argv, environment, io, signal, overrides = {}) =>
24661
25068
  const diagnostics = new StderrDiagnosticSink(io);
24662
25069
  let command;
24663
25070
  try {
24664
- command = parseProductCliCommand(argv);
25071
+ const parsed = await parseWithLinkedProject(argv, async () => {
25072
+ const profile2 = resolveProductCliEnvironment(environment);
25073
+ return readLinkedProjectId(join2(overrides.workingDirectory ?? process.cwd(), profile2.linkDirectory), profile2.apiOrigin);
25074
+ });
25075
+ command = parsed.command;
25076
+ if (parsed.linkedProjectId !== null)
25077
+ io.writeStderr(`ohmyhost: using linked project ${parsed.linkedProjectId}
25078
+ `);
24665
25079
  } catch (error) {
24666
25080
  const result2 = error instanceof InvalidCommandError ? invalidCommandFailure(recognizedCommandLabel(argv)) : errorDocument("unknown", 1, "internal_error", false, "Retry the command.");
24667
25081
  if (!(error instanceof InvalidCommandError)) {
@@ -24739,7 +25153,7 @@ var runProductionCli = async (argv, environment, io, signal, overrides = {}) =>
24739
25153
  }),
24740
25154
  signal
24741
25155
  }),
24742
- projectLinkStore: new OwnerOnlyProjectLinkStore(join2(process.cwd(), profile.linkDirectory)),
25156
+ projectLinkStore: new OwnerOnlyProjectLinkStore(join2(process.cwd(), profile.linkDirectory), process.cwd()),
24743
25157
  productApiOrigin: profile.apiOrigin,
24744
25158
  tokenRefresher: new PublicWorkosTokenRefresher(environment),
24745
25159
  tokenRevoker: lifecycle,
@@ -24925,6 +25339,30 @@ var GeneratedSdkSessionRevoker = class {
24925
25339
  }
24926
25340
  }
24927
25341
  };
25342
+ async function parseWithLinkedProject(argv, linkedProjectId) {
25343
+ try {
25344
+ return { command: parseProductCliCommand(argv), linkedProjectId: null };
25345
+ } catch (error) {
25346
+ if (!(error instanceof InvalidCommandError) || argv.includes("--project"))
25347
+ throw error;
25348
+ let projectId;
25349
+ try {
25350
+ projectId = await linkedProjectId();
25351
+ } catch {
25352
+ projectId = null;
25353
+ }
25354
+ if (projectId === null)
25355
+ throw error;
25356
+ try {
25357
+ return {
25358
+ command: parseProductCliCommand([...argv, "--project", projectId]),
25359
+ linkedProjectId: projectId
25360
+ };
25361
+ } catch {
25362
+ throw error;
25363
+ }
25364
+ }
25365
+ }
24928
25366
  var StderrDiagnosticSink = class {
24929
25367
  io;
24930
25368
  constructor(io) {
@@ -25133,6 +25571,7 @@ export {
25133
25571
  parsePaidDomain,
25134
25572
  parseCloudflareDnsAuthorization,
25135
25573
  parseCloudflareDnsAuthorizationStatus,
25574
+ parseDevAccessState,
25136
25575
  parseDevAccessTicket,
25137
25576
  parseCurrentIdentity,
25138
25577
  parseAccountProfile,
@@ -25149,6 +25588,7 @@ export {
25149
25588
  cliMetadataResult,
25150
25589
  ProductCli,
25151
25590
  GeneratedSdkProductApi,
25591
+ readLinkedProjectId,
25152
25592
  OwnerOnlyProjectLinkStore,
25153
25593
  RepositoryInitError,
25154
25594
  RepositoryInitBlockedError,
@@ -25162,6 +25602,7 @@ export {
25162
25602
  StderrDeviceAuthorizationPresenter,
25163
25603
  PublicWorkosTokenRefresher,
25164
25604
  GeneratedSdkSessionRevoker,
25605
+ parseWithLinkedProject,
25165
25606
  readBoundedResponseBody,
25166
25607
  createBoundedFetch,
25167
25608
  readSecretValueFromStandardInput