@amerged/ohmyhost-cli 0.1.23 → 0.1.25
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 +1 -1
- package/dist/bin.js +3 -2
- package/dist/{chunk-74DROG4F.js → chunk-YOEEWHSQ.js} +985 -304
- package/dist/index.js +13 -1
- package/package.json +1 -1
- package/src/apps/product-cli/package.json +45 -0
- package/src/apps/product-cli/{bin.ts → src/bin.ts} +3 -0
- package/src/apps/product-cli/{cli.ts → src/cli.ts} +363 -87
- package/src/apps/product-cli/{command.ts → src/command.ts} +58 -11
- package/src/apps/product-cli/{composition.ts → src/composition.ts} +98 -21
- package/src/apps/product-cli/{index.ts → src/index.ts} +1 -0
- package/src/apps/product-cli/{product-api.ts → src/product-api.ts} +65 -19
- package/src/apps/product-cli/{repository-init.ts → src/repository-init.ts} +90 -36
- package/src/apps/product-cli/{runtime-contract.ts → src/runtime-contract.ts} +16 -309
- package/src/apps/product-cli/{user-token-file.ts → src/user-token-file.ts} +46 -35
- package/src/apps/product-cli/{workspace.ts → src/workspace.ts} +6 -0
- package/src/packages/contracts/package.json +137 -0
- package/src/packages/contracts/src/browser-capabilities.ts +133 -0
- package/src/packages/contracts/src/customer-auth-admission.ts +28 -0
- package/src/packages/contracts/{database-access.ts → src/database-access.ts} +10 -2
- package/src/packages/contracts/{framework-admission.ts → src/framework-admission.ts} +76 -13
- package/src/packages/contracts/src/framework-build-script.mjs +22 -0
- package/src/packages/contracts/src/framework-build-script.ts +1 -0
- package/src/packages/contracts/{index.ts → src/index.ts} +17 -16
- package/src/packages/contracts/src/operation-data-change-result.ts +86 -0
- package/src/packages/contracts/{operation-deployment-result.ts → src/operation-deployment-result.ts} +2 -2
- package/src/packages/contracts/{operation-failure.ts → src/operation-failure.ts} +24 -12
- package/src/packages/contracts/src/problem-policy.ts +498 -0
- package/src/packages/contracts/src/project-data-changes.ts +90 -0
- package/src/packages/contracts/src/secret-names.ts +27 -0
- package/src/packages/sdk-ts/package.json +21 -0
- package/src/packages/sdk-ts/{generated → src/generated}/index.ts +15 -0
- package/src/packages/sdk-ts/{generated → src/generated}/sdk.gen.ts +113 -4
- package/src/packages/sdk-ts/{generated → src/generated}/types.gen.ts +201 -10
- package/src/packages/workos-auth-contracts/package.json +39 -0
- /package/src/apps/product-cli/{credential-store.ts → src/credential-store.ts} +0 -0
- /package/src/apps/product-cli/{device-token-expiry.ts → src/device-token-expiry.ts} +0 -0
- /package/src/apps/product-cli/{file-credential-store.ts → src/file-credential-store.ts} +0 -0
- /package/src/apps/product-cli/{profile-store.ts → src/profile-store.ts} +0 -0
- /package/src/apps/product-cli/{project-link-store.ts → src/project-link-store.ts} +0 -0
- /package/src/apps/product-cli/{psql-session.ts → src/psql-session.ts} +0 -0
- /package/src/packages/contracts/{application-root.ts → src/application-root.ts} +0 -0
- /package/src/packages/contracts/{billing.ts → src/billing.ts} +0 -0
- /package/src/packages/contracts/{credit-pricing.ts → src/credit-pricing.ts} +0 -0
- /package/src/packages/contracts/{database-compute.ts → src/database-compute.ts} +0 -0
- /package/src/packages/contracts/{database-migration-admission.ts → src/database-migration-admission.ts} +0 -0
- /package/src/packages/contracts/{feedback.ts → src/feedback.ts} +0 -0
- /package/src/packages/contracts/{framework-family.ts → src/framework-family.ts} +0 -0
- /package/src/packages/contracts/{functions.ts → src/functions.ts} +0 -0
- /package/src/packages/contracts/{platform-origins.ts → src/platform-origins.ts} +0 -0
- /package/src/packages/contracts/{project-context.ts → src/project-context.ts} +0 -0
- /package/src/packages/contracts/{project-exports.ts → src/project-exports.ts} +0 -0
- /package/src/packages/contracts/{user-api-keys.ts → src/user-api-keys.ts} +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/client/client.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/client/index.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/client/types.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/client/utils.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/client.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/core/auth.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/core/bodySerializer.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/core/params.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/core/pathSerializer.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/core/queryKeySerializer.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/core/serverSentEvents.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/core/types.gen.ts +0 -0
- /package/src/packages/sdk-ts/{generated → src/generated}/core/utils.gen.ts +0 -0
- /package/src/packages/sdk-ts/{index.ts → src/index.ts} +0 -0
- /package/src/packages/sdk-ts/{managed-mail-response.ts → src/managed-mail-response.ts} +0 -0
- /package/src/packages/sdk-ts/{managed-mail.ts → src/managed-mail.ts} +0 -0
- /package/src/packages/workos-auth-contracts/{device-flow.ts → src/device-flow.ts} +0 -0
- /package/src/packages/workos-auth-contracts/{index.ts → src/index.ts} +0 -0
- /package/src/packages/workos-auth-contracts/{management-rpc.ts → src/management-rpc.ts} +0 -0
- /package/src/packages/workos-auth-contracts/{organizations.ts → src/organizations.ts} +0 -0
- /package/src/packages/workos-auth-contracts/{parsing.ts → src/parsing.ts} +0 -0
- /package/src/packages/workos-auth-contracts/{session-status.ts → src/session-status.ts} +0 -0
- /package/src/packages/workos-auth-contracts/{tokens.ts → src/tokens.ts} +0 -0
- /package/src/packages/workos-auth-contracts/{user-api-keys.ts → src/user-api-keys.ts} +0 -0
- /package/src/packages/workos-auth-contracts/{webhooks.ts → src/webhooks.ts} +0 -0
|
@@ -5019,8 +5019,8 @@ var require_multipleOf = __commonJS({
|
|
|
5019
5019
|
const { gen, data, schemaCode, it } = cxt;
|
|
5020
5020
|
const prec = it.opts.multipleOfPrecision;
|
|
5021
5021
|
const res = gen.let("res");
|
|
5022
|
-
const
|
|
5023
|
-
cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${
|
|
5022
|
+
const invalid5 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
|
|
5023
|
+
cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid5}))`);
|
|
5024
5024
|
}
|
|
5025
5025
|
};
|
|
5026
5026
|
exports.default = def;
|
|
@@ -14769,17 +14769,25 @@ var UserTokenFileError = class extends Error {
|
|
|
14769
14769
|
code;
|
|
14770
14770
|
keyId;
|
|
14771
14771
|
constructor(code, keyId) {
|
|
14772
|
-
super(
|
|
14773
|
-
token_file_not_ignored: "Add the selected env file to .gitignore or choose a location outside the repository. No token was created or written.",
|
|
14774
|
-
token_file_unsafe: "Choose a private .env file owned by you. Symlinks, shared files and files larger than 64 KiB are not accepted.",
|
|
14775
|
-
token_file_has_token: "The selected file already contains OHMYHOST_TOKEN. Use that token or choose a new file; existing credentials were preserved.",
|
|
14776
|
-
token_file_changed: "The environment file changed during creation. It was not overwritten. List tokens to inspect the issued key before creating another.",
|
|
14777
|
-
token_value_unavailable: "This token was already issued and its full value cannot be retrieved. Use the previously saved file, or explicitly revoke the listed key before creating a replacement."
|
|
14778
|
-
}[code] + (keyId ? ` Token ID: ${keyId}.` : ""));
|
|
14772
|
+
super(tokenFileAction(code, keyId));
|
|
14779
14773
|
this.code = code;
|
|
14780
14774
|
this.keyId = keyId;
|
|
14781
14775
|
}
|
|
14782
14776
|
};
|
|
14777
|
+
var tokenFileAction = (code, keyId) => {
|
|
14778
|
+
switch (code) {
|
|
14779
|
+
case "token_file_not_ignored":
|
|
14780
|
+
return "Add the selected env file to .gitignore or choose a location outside the repository. No token was created or written.";
|
|
14781
|
+
case "token_file_unsafe":
|
|
14782
|
+
return "Choose an env file whose name starts or ends with .env, for example .env.local: a regular file you own, not a symlink or hard link, at most 64 KiB.";
|
|
14783
|
+
case "token_file_has_token":
|
|
14784
|
+
return "The selected file already contains OHMYHOST_TOKEN. Use that token or choose a new file; existing credentials were preserved.";
|
|
14785
|
+
case "token_file_changed":
|
|
14786
|
+
return keyId === void 0 ? "The env file changed while it was being read; no key was issued. Run the same command again." : `The env file changed while the token was being saved, so its value may not be in the file; key ${keyId} was issued. Revoke it with 'ohmyhost token revoke --organization ULID --key ${keyId} --yes --json' (MCP token_revoke), then create a new one with a new idempotency key.`;
|
|
14787
|
+
case "token_value_unavailable":
|
|
14788
|
+
return `A key was already issued for this idempotency key, and its value is shown only once. Use the file it was saved to, or revoke key ${String(keyId)} (ohmyhost token revoke, MCP token_revoke) and create a replacement with a new idempotency key.`;
|
|
14789
|
+
}
|
|
14790
|
+
};
|
|
14783
14791
|
async function prepareUserTokenFile(output) {
|
|
14784
14792
|
const path = resolve(output);
|
|
14785
14793
|
if (!basename(path).startsWith(".env") && !basename(path).endsWith(".env"))
|
|
@@ -14812,18 +14820,22 @@ OHMYHOST_TOKEN=${preview}
|
|
|
14812
14820
|
const result = parseUserApiKeyCreation(value);
|
|
14813
14821
|
if (result.value === null)
|
|
14814
14822
|
throw new UserTokenFileError("token_value_unavailable", result.key.id);
|
|
14815
|
-
|
|
14816
|
-
|
|
14817
|
-
|
|
14818
|
-
|
|
14823
|
+
try {
|
|
14824
|
+
const current = await lstat(path);
|
|
14825
|
+
if (!current.isFile() || current.isSymbolicLink() || current.nlink !== 1 || current.ino !== identity.ino || current.dev !== identity.dev || await tokenFileText(handle) !== initial)
|
|
14826
|
+
throw new UserTokenFileError("token_file_changed");
|
|
14827
|
+
await handle.appendFile(`
|
|
14819
14828
|
OHMYHOST_TOKEN=${result.value}
|
|
14820
14829
|
`, "utf8");
|
|
14821
|
-
|
|
14822
|
-
|
|
14823
|
-
|
|
14824
|
-
|
|
14825
|
-
|
|
14830
|
+
await handle.sync();
|
|
14831
|
+
if (parseEnv(await tokenFileText(handle))["OHMYHOST_TOKEN"] !== result.value)
|
|
14832
|
+
throw new UserTokenFileError("token_file_changed");
|
|
14833
|
+
const saved = await lstat(path);
|
|
14834
|
+
if (!saved.isFile() || saved.isSymbolicLink() || saved.ino !== identity.ino || saved.dev !== identity.dev)
|
|
14835
|
+
throw new UserTokenFileError("token_file_changed");
|
|
14836
|
+
} catch {
|
|
14826
14837
|
throw new UserTokenFileError("token_file_changed", result.key.id);
|
|
14838
|
+
}
|
|
14827
14839
|
return { request_id: result.request_id, key: result.key, token_file: path };
|
|
14828
14840
|
},
|
|
14829
14841
|
close: () => handle.close()
|
|
@@ -15271,6 +15283,63 @@ var describeRequest = (request) => {
|
|
|
15271
15283
|
};
|
|
15272
15284
|
var describeProfile = (profile) => `profile ${profile.name} (user ${profile.userId}, ${profile.organizationId === null ? "no organization" : `organization ${profile.organizationName === null ? "" : `${profile.organizationName} `}${profile.organizationId}`})`;
|
|
15273
15285
|
|
|
15286
|
+
// packages/contracts/dist/secret-names.js
|
|
15287
|
+
var USER_SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]{0,127}$/u;
|
|
15288
|
+
var FIXED_PLATFORM_NAMES = /* @__PURE__ */ new Set([
|
|
15289
|
+
"BETTER_AUTH_SECRET",
|
|
15290
|
+
"BETTER_AUTH_URL",
|
|
15291
|
+
"DATABASE_URL",
|
|
15292
|
+
"HYPERDRIVE",
|
|
15293
|
+
"ASSETS",
|
|
15294
|
+
"FILES",
|
|
15295
|
+
"IMAGES",
|
|
15296
|
+
"STORAGE"
|
|
15297
|
+
]);
|
|
15298
|
+
var INSTALLED_PLATFORM_KEYS = /* @__PURE__ */ new Set([
|
|
15299
|
+
"OHMYHOST_STORAGE_KEY",
|
|
15300
|
+
"OHMYHOST_MAIL_KEY",
|
|
15301
|
+
"BETTER_AUTH_SECRET"
|
|
15302
|
+
]);
|
|
15303
|
+
function isUserSecretName(value, action = "set") {
|
|
15304
|
+
if (typeof value !== "string" || !USER_SECRET_NAME_PATTERN.test(value))
|
|
15305
|
+
return false;
|
|
15306
|
+
return action === "delete" ? !INSTALLED_PLATFORM_KEYS.has(value) : !value.startsWith("OHMYHOST_") && !FIXED_PLATFORM_NAMES.has(value);
|
|
15307
|
+
}
|
|
15308
|
+
|
|
15309
|
+
// packages/contracts/dist/project-data-changes.js
|
|
15310
|
+
var PROJECT_DATA_CHANGE_KINDS = [
|
|
15311
|
+
"isolate_prod_keeps_data",
|
|
15312
|
+
"isolate_dev_keeps_data",
|
|
15313
|
+
"share_prod_keeps_data",
|
|
15314
|
+
"reset_dev"
|
|
15315
|
+
];
|
|
15316
|
+
function parseProjectDataChangePlan(value) {
|
|
15317
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
15318
|
+
invalid();
|
|
15319
|
+
const input = value;
|
|
15320
|
+
if (Object.keys(input).sort().join(",") !== "action,change,confirmation_token,created_at,data_mode_after,data_mode_before,destructive_effects,effects,expires_at,project_id,redeploy_environments,resource_etag,risks" || input["action"] !== "data_change" || typeof input["project_id"] !== "string" || !/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(input["project_id"]) || !isProjectDataChangeKind(input["change"]) || !["shared", "isolated"].includes(String(input["data_mode_before"])) || input["data_mode_after"] !== (input["change"] === "share_prod_keeps_data" ? "shared" : "isolated") || input["data_mode_before"] !== (input["change"].startsWith("isolate_") ? "shared" : "isolated") || !Array.isArray(input["redeploy_environments"]) || input["redeploy_environments"].length !== 1 || input["redeploy_environments"][0] !== (input["change"] === "isolate_dev_keeps_data" ? "prod" : "dev") || !planStrings(input["effects"]) || !planStrings(input["destructive_effects"]) || !planStrings(input["risks"]) || typeof input["resource_etag"] !== "string" || !/^"sha256-[a-f0-9]{64}"$/u.test(input["resource_etag"]) || typeof input["confirmation_token"] !== "string" || !/^[A-Za-z0-9_-]{43}$/u.test(input["confirmation_token"]) || !instant(input["created_at"]) || !instant(input["expires_at"]) || Date.parse(input["expires_at"]) - Date.parse(input["created_at"]) !== 6e5)
|
|
15321
|
+
invalid();
|
|
15322
|
+
return Object.freeze({
|
|
15323
|
+
...input,
|
|
15324
|
+
effects: Object.freeze([...input["effects"]]),
|
|
15325
|
+
destructive_effects: Object.freeze([...input["destructive_effects"]]),
|
|
15326
|
+
risks: Object.freeze([...input["risks"]]),
|
|
15327
|
+
redeploy_environments: Object.freeze([...input["redeploy_environments"]])
|
|
15328
|
+
});
|
|
15329
|
+
}
|
|
15330
|
+
function isProjectDataChangeKind(value) {
|
|
15331
|
+
return typeof value === "string" && PROJECT_DATA_CHANGE_KINDS.some((kind) => kind === value);
|
|
15332
|
+
}
|
|
15333
|
+
function planStrings(value) {
|
|
15334
|
+
return Array.isArray(value) && value.length <= 32 && value.every((text) => typeof text === "string" && text.length > 0 && text.length <= 2e3 && !/[\p{Cc}\p{Cf}]/u.test(text));
|
|
15335
|
+
}
|
|
15336
|
+
function instant(value) {
|
|
15337
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value)) && new Date(value).toISOString() === value;
|
|
15338
|
+
}
|
|
15339
|
+
function invalid() {
|
|
15340
|
+
throw new TypeError("Project data change plan is invalid");
|
|
15341
|
+
}
|
|
15342
|
+
|
|
15274
15343
|
// packages/workos-auth-contracts/dist/parsing.js
|
|
15275
15344
|
var WorkosContractError = class extends Error {
|
|
15276
15345
|
code;
|
|
@@ -15390,6 +15459,10 @@ var recognizedCommandLabel = (argv) => {
|
|
|
15390
15459
|
return "feedback status";
|
|
15391
15460
|
if (value === "project" && values[0] === "create")
|
|
15392
15461
|
return "project create";
|
|
15462
|
+
if (value === "project" && values[0] === "data" && values[1] === "plan")
|
|
15463
|
+
return "project data plan";
|
|
15464
|
+
if (value === "project" && values[0] === "data" && values[1] === "change")
|
|
15465
|
+
return "project data change";
|
|
15393
15466
|
if (value === "organization" && values[0] === "create")
|
|
15394
15467
|
return "organization create";
|
|
15395
15468
|
if (value === "organization" && values[0] === "list")
|
|
@@ -15906,6 +15979,27 @@ var parseProductCliCommand = (argv) => {
|
|
|
15906
15979
|
}
|
|
15907
15980
|
if (first === "project") {
|
|
15908
15981
|
const subcommand = values.shift();
|
|
15982
|
+
if (subcommand === "data") {
|
|
15983
|
+
const action = values.shift();
|
|
15984
|
+
if (follow || stdin || action !== "plan" && action !== "change")
|
|
15985
|
+
throw new InvalidCommandError();
|
|
15986
|
+
if (action === "plan" && (yes || wait) || action === "change" && !yes)
|
|
15987
|
+
throw new InvalidCommandError();
|
|
15988
|
+
const options2 = parseOptions(values, action === "plan" ? ["--project", "--change"] : ["--project", "--change", "--if-match", "--confirmation-token", "--idempotency-key"]);
|
|
15989
|
+
const change = options2["--change"];
|
|
15990
|
+
if (!isProjectDataChangeKind(change))
|
|
15991
|
+
throw new InvalidCommandError();
|
|
15992
|
+
const common = { projectId: options2["--project"], change, credentialStore };
|
|
15993
|
+
return action === "plan" ? { kind: "project-data-plan", ...common } : {
|
|
15994
|
+
kind: "project-data-change",
|
|
15995
|
+
...common,
|
|
15996
|
+
ifMatch: options2["--if-match"],
|
|
15997
|
+
confirmationToken: options2["--confirmation-token"],
|
|
15998
|
+
idempotencyKey: options2["--idempotency-key"],
|
|
15999
|
+
yes: true,
|
|
16000
|
+
wait
|
|
16001
|
+
};
|
|
16002
|
+
}
|
|
15909
16003
|
if (subcommand === "handle" && values[0] === "set") {
|
|
15910
16004
|
values.shift();
|
|
15911
16005
|
if (yes || wait || follow || stdin)
|
|
@@ -16565,7 +16659,7 @@ var parseProductCliCommand = (argv) => {
|
|
|
16565
16659
|
throw new InvalidCommandError();
|
|
16566
16660
|
values.shift();
|
|
16567
16661
|
const name = values.shift();
|
|
16568
|
-
if (!isUserSecretName(name))
|
|
16662
|
+
if (!isUserSecretName(name, "delete"))
|
|
16569
16663
|
throw new InvalidCommandError();
|
|
16570
16664
|
const options = parseOptions(values, ["--project", "--environment", "--idempotency-key"]);
|
|
16571
16665
|
return {
|
|
@@ -16763,6 +16857,8 @@ var parseOptions = (values, allowed) => {
|
|
|
16763
16857
|
if (option === void 0 || !allowed.includes(option) || !isValue(value)) {
|
|
16764
16858
|
throw new InvalidCommandError();
|
|
16765
16859
|
}
|
|
16860
|
+
if (option === "--idempotency-key" && value.length > 128)
|
|
16861
|
+
throw new InvalidCommandError();
|
|
16766
16862
|
if (option in result)
|
|
16767
16863
|
throw new InvalidCommandError();
|
|
16768
16864
|
result[option] = value;
|
|
@@ -16786,6 +16882,8 @@ var parseOptionalOptions = (values, allowed) => {
|
|
|
16786
16882
|
if (option === void 0 || !allowed.includes(option) || !isValue(value)) {
|
|
16787
16883
|
throw new InvalidCommandError();
|
|
16788
16884
|
}
|
|
16885
|
+
if (option === "--idempotency-key" && value.length > 128)
|
|
16886
|
+
throw new InvalidCommandError();
|
|
16789
16887
|
if (option in result)
|
|
16790
16888
|
throw new InvalidCommandError();
|
|
16791
16889
|
result[option] = value;
|
|
@@ -16818,7 +16916,6 @@ var validGithubBranchName = (value) => !(value.length < 1 || value.length > 1024
|
|
|
16818
16916
|
return code <= 32 || code === 127 || "\\~^:?*[]".includes(character);
|
|
16819
16917
|
}) || value.split("/").some((component) => component.startsWith(".") || component.endsWith(".lock")));
|
|
16820
16918
|
var validPaidHostname = (value) => /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/u.test(value) && !value.endsWith(".poc.aixyte.com") && value !== "omh.st" && !value.endsWith(".omh.st") && value !== "ohmyho.st" && !value.endsWith(".ohmyho.st");
|
|
16821
|
-
var isUserSecretName = (value) => isValue(value) && /^[A-Z][A-Z0-9_]{0,127}$/u.test(value) && value !== "DATABASE_URL" && value !== "HYPERDRIVE" && value !== "BETTER_AUTH_SECRET" && value !== "BETTER_AUTH_URL" && value !== "OHMYHOST_MAIL_KEY" && value !== "OHMYHOST_MAIL_GATEWAY_URL";
|
|
16822
16919
|
|
|
16823
16920
|
// packages/contracts/dist/feedback.js
|
|
16824
16921
|
var FEEDBACK_HISTORY_PAGE_SIZE = 25;
|
|
@@ -16892,6 +16989,41 @@ function hasControlCharacter(value, multiline) {
|
|
|
16892
16989
|
});
|
|
16893
16990
|
}
|
|
16894
16991
|
|
|
16992
|
+
// packages/contracts/dist/framework-build-script.mjs
|
|
16993
|
+
var PINNED_BUN_VERSION = "1.2.22";
|
|
16994
|
+
var TYPESCRIPT_BUILD_STAGE = /^tsc(?:[ \t]+(?:--noEmit|-b|--build))?$/u;
|
|
16995
|
+
function isAdmittedFrameworkBuildScript(framework, script) {
|
|
16996
|
+
if (typeof script !== "string")
|
|
16997
|
+
return false;
|
|
16998
|
+
if (framework === "nextjs")
|
|
16999
|
+
return script === "next build" || script === "next build --webpack";
|
|
17000
|
+
if (framework !== "vite" && framework !== "tanstack-start")
|
|
17001
|
+
return false;
|
|
17002
|
+
const stages = script.trim().split(/[ \t]*&&[ \t]*/u);
|
|
17003
|
+
if (stages.length === 1)
|
|
17004
|
+
return stages[0] === "vite build";
|
|
17005
|
+
if (stages.length !== 2)
|
|
17006
|
+
return false;
|
|
17007
|
+
return stages[0] === "vite build" && TYPESCRIPT_BUILD_STAGE.test(stages[1] ?? "") || TYPESCRIPT_BUILD_STAGE.test(stages[0] ?? "") && stages[1] === "vite build";
|
|
17008
|
+
}
|
|
17009
|
+
|
|
17010
|
+
// packages/contracts/dist/customer-auth-admission.js
|
|
17011
|
+
function managedCustomerAuthDatabaseMessage(path) {
|
|
17012
|
+
return `"${path}" selects the managed Better Auth bridge, which requires the managed PostgreSQL database. Set database.enabled: true with an admitted migrations path, or set auth.provider: none and keep the application-owned authentication configuration. Commit and push the change, then plan the new commit.`;
|
|
17013
|
+
}
|
|
17014
|
+
function managedCustomerAuthAdmissionIssue(capabilities) {
|
|
17015
|
+
if (capabilities?.auth?.provider !== "better-auth") {
|
|
17016
|
+
return null;
|
|
17017
|
+
}
|
|
17018
|
+
if (capabilities.database?.enabled !== true) {
|
|
17019
|
+
return Object.freeze({
|
|
17020
|
+
code: "managed_auth_database_required",
|
|
17021
|
+
message: managedCustomerAuthDatabaseMessage("ohmyhost.yaml")
|
|
17022
|
+
});
|
|
17023
|
+
}
|
|
17024
|
+
return null;
|
|
17025
|
+
}
|
|
17026
|
+
|
|
16895
17027
|
// packages/contracts/dist/framework-admission.js
|
|
16896
17028
|
var FrameworkAdmissionError = class extends Error {
|
|
16897
17029
|
reason;
|
|
@@ -16946,6 +17078,9 @@ function resolvePackageManager(input) {
|
|
|
16946
17078
|
if (!contract.lockfiles.includes(lockfile)) {
|
|
16947
17079
|
throw new FrameworkAdmissionError("package_manager_lockfile_mismatch");
|
|
16948
17080
|
}
|
|
17081
|
+
if (manager === "bun" && version2 !== PINNED_BUN_VERSION) {
|
|
17082
|
+
throw new FrameworkAdmissionError("package_manager_version_unsupported");
|
|
17083
|
+
}
|
|
16949
17084
|
return Object.freeze({
|
|
16950
17085
|
manager,
|
|
16951
17086
|
version: version2,
|
|
@@ -17006,26 +17141,47 @@ function compareVersions(left, right) {
|
|
|
17006
17141
|
function versionAdmission(classification, reason) {
|
|
17007
17142
|
return Object.freeze({ classification, reason });
|
|
17008
17143
|
}
|
|
17144
|
+
var PUSH_AND_PLAN = "Commit and push the change, then plan the new commit.";
|
|
17009
17145
|
var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
|
|
17146
|
+
managed_auth_database_required: Object.freeze({
|
|
17147
|
+
render: managedCustomerAuthDatabaseMessage,
|
|
17148
|
+
pattern: /^"([^"]+)" selects the managed Better Auth bridge, which requires the managed PostgreSQL database\. Set database\.enabled: true with an admitted migrations path, or set auth\.provider: none and keep the application-owned authentication configuration\. Commit and push the change, then plan the new commit\.$/u
|
|
17149
|
+
}),
|
|
17010
17150
|
database_binding_private: Object.freeze({
|
|
17011
|
-
render: (path) => `"${path}" reads a
|
|
17012
|
-
pattern: /^"([^"]+)" reads a
|
|
17151
|
+
render: (path) => `"${path}" reads a database connection string (DATABASE_URL, HYPERDRIVE, connectionString or a postgres:// URL); none reaches a customer Worker. Call the database with createPrivateDatabaseClient from "@ohmyhost/customer-runtime/database". ${PUSH_AND_PLAN}`,
|
|
17152
|
+
pattern: /^"([^"]+)" reads a database connection string \(DATABASE_URL, HYPERDRIVE, connectionString or a postgres:\/\/ URL\); none reaches a customer Worker\. Call the database with createPrivateDatabaseClient from "@ohmyhost\/customer-runtime\/database"\. Commit and push the change, then plan the new commit\.$/u
|
|
17013
17153
|
}),
|
|
17014
17154
|
database_driver_unsupported: Object.freeze({
|
|
17015
|
-
render: (path) => `"${path}" imports a
|
|
17016
|
-
pattern: /^"([^"]+)" imports a
|
|
17155
|
+
render: (path) => `"${path}" imports a database socket driver (pg, postgres, pg-native or mysql2); customer Workers cannot open sockets. Call the database with createPrivateDatabaseClient from "@ohmyhost/customer-runtime/database". ${PUSH_AND_PLAN}`,
|
|
17156
|
+
pattern: /^"([^"]+)" imports a database socket driver \(pg, postgres, pg-native or mysql2\); customer Workers cannot open sockets\. Call the database with createPrivateDatabaseClient from "@ohmyhost\/customer-runtime\/database"\. Commit and push the change, then plan the new commit\.$/u
|
|
17017
17157
|
}),
|
|
17018
17158
|
source_syntax_invalid: Object.freeze({
|
|
17019
|
-
render: (path) => `Invalid JavaScript or TypeScript in "${path}". Fix the file, commit the change, then plan
|
|
17020
|
-
pattern: /^Invalid JavaScript or TypeScript in "([^"]+)"\. Fix the file, commit the change, then plan
|
|
17159
|
+
render: (path) => `Invalid JavaScript or TypeScript in "${path}". Fix the file, commit and push the change, then plan the new commit.`,
|
|
17160
|
+
pattern: /^Invalid JavaScript or TypeScript in "([^"]+)"\. Fix the file, commit and push the change, then plan the new commit\.$/u
|
|
17021
17161
|
}),
|
|
17022
17162
|
worker_module_default_export_required: Object.freeze({
|
|
17023
|
-
render: (path) => `Worker module "${path}" has no default export. Export default { fetch, scheduled } with the handlers ohmyho.st invokes; named exports are never called.
|
|
17024
|
-
pattern: /^Worker module "([^"]+)" has no default export\. Export default \{ fetch, scheduled \} with the handlers ohmyho\.st invokes; named exports are never called\. Commit the change, then plan
|
|
17163
|
+
render: (path) => `Worker module "${path}" has no default export. Export default { fetch, scheduled } with the handlers ohmyho.st invokes; named exports are never called. ${PUSH_AND_PLAN}`,
|
|
17164
|
+
pattern: /^Worker module "([^"]+)" has no default export\. Export default \{ fetch, scheduled \} with the handlers ohmyho\.st invokes; named exports are never called\. Commit and push the change, then plan the new commit\.$/u
|
|
17025
17165
|
}),
|
|
17026
17166
|
scheduled_handler_required: Object.freeze({
|
|
17027
|
-
render: (path) => `functions.crons is declared but "${path}" exports no scheduled handler. Add scheduled(controller, env, ctx) to its default export, commit the change, then plan
|
|
17028
|
-
pattern: /^functions\.crons is declared but "([^"]+)" exports no scheduled handler\. Add scheduled\(controller, env, ctx\) to its default export, commit the change, then plan
|
|
17167
|
+
render: (path) => `functions.crons is declared but "${path}" exports no scheduled handler. Add scheduled(controller, env, ctx) to its default export, commit and push the change, then plan the new commit.`,
|
|
17168
|
+
pattern: /^functions\.crons is declared but "([^"]+)" exports no scheduled handler\. Add scheduled\(controller, env, ctx\) to its default export, commit and push the change, then plan the new commit\.$/u
|
|
17169
|
+
}),
|
|
17170
|
+
framework_base_path_unsupported: Object.freeze({
|
|
17171
|
+
render: (path) => `"${path}" sets a Vite base. ohmyho.st serves the app at the root origin: remove base or set base: "/". ${PUSH_AND_PLAN}`,
|
|
17172
|
+
pattern: /^"([^"]+)" sets a Vite base\. ohmyho\.st serves the app at the root origin: remove base or set base: "\/"\. Commit and push the change, then plan the new commit\.$/u
|
|
17173
|
+
}),
|
|
17174
|
+
tanstack_prerender_required: Object.freeze({
|
|
17175
|
+
render: (path) => `"${path}" does not call tanstackStart({ prerender: { enabled: true } }), which the static TanStack Start runtime needs; enable prerender, or set runtime.mode: edge with the Cloudflare Vite plugin. ${PUSH_AND_PLAN}`,
|
|
17176
|
+
pattern: /^"([^"]+)" does not call tanstackStart\(\{ prerender: \{ enabled: true \} \}\), which the static TanStack Start runtime needs; enable prerender, or set runtime\.mode: edge with the Cloudflare Vite plugin\. Commit and push the change, then plan the new commit\.$/u
|
|
17177
|
+
}),
|
|
17178
|
+
tanstack_vite_plugins_required: Object.freeze({
|
|
17179
|
+
render: (path) => `"${path}" does not follow the TanStack Start plugin contract: import tanstackStart from "@tanstack/react-start/plugin/vite"; on runtime edge also import cloudflare from "@cloudflare/vite-plugin" and call cloudflare({ viteEnvironment: { name: "ssr" } }) before tanstackStart(); on runtime static do not import the Cloudflare plugin. ${PUSH_AND_PLAN}`,
|
|
17180
|
+
pattern: /^"([^"]+)" does not follow the TanStack Start plugin contract: import tanstackStart from "@tanstack\/react-start\/plugin\/vite"; on runtime edge also import cloudflare from "@cloudflare\/vite-plugin" and call cloudflare\(\{ viteEnvironment: \{ name: "ssr" \} \}\) before tanstackStart\(\); on runtime static do not import the Cloudflare plugin\. Commit and push the change, then plan the new commit\.$/u
|
|
17181
|
+
}),
|
|
17182
|
+
next_cache_components_unsupported: Object.freeze({
|
|
17183
|
+
render: (path) => `"${path}" sets cacheComponents: true, which ohmyho.st does not support; remove it or set it to false. ${PUSH_AND_PLAN}`,
|
|
17184
|
+
pattern: /^"([^"]+)" sets cacheComponents: true, which ohmyho\.st does not support; remove it or set it to false\. Commit and push the change, then plan the new commit\.$/u
|
|
17029
17185
|
})
|
|
17030
17186
|
});
|
|
17031
17187
|
function isFrameworkConversionDiagnosticCode(value) {
|
|
@@ -17037,7 +17193,7 @@ function parseFrameworkConversionDiagnostic(value) {
|
|
|
17037
17193
|
const record5 = value;
|
|
17038
17194
|
const path = record5["path"];
|
|
17039
17195
|
const code = record5["code"];
|
|
17040
|
-
if (Object.keys(record5).length !== 2 || !isFrameworkConversionDiagnosticCode(code) || typeof path !== "string" || path.length > 240 || !/^[A-Za-z0-9_@()[\]./-]+\.(?:[cm]?[jt]sx?)$/u.test(path) || path.split("/").some((segment) => segment === "" || segment === "." || segment === ".."))
|
|
17196
|
+
if (Object.keys(record5).length !== 2 || !isFrameworkConversionDiagnosticCode(code) || typeof path !== "string" || path.length > 240 || (code === "managed_auth_database_required" ? path !== "ohmyhost.yaml" : !/^[A-Za-z0-9_@()[\]./-]+\.(?:[cm]?[jt]sx?)$/u.test(path)) || path.split("/").some((segment) => segment === "" || segment === "." || segment === ".."))
|
|
17041
17197
|
return null;
|
|
17042
17198
|
return Object.freeze({ code, path });
|
|
17043
17199
|
}
|
|
@@ -17059,36 +17215,36 @@ function parseFrameworkConversionDetail(value) {
|
|
|
17059
17215
|
// packages/contracts/dist/operation-deployment-result.js
|
|
17060
17216
|
function exactRecord(value, keys) {
|
|
17061
17217
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
17062
|
-
|
|
17218
|
+
invalid2();
|
|
17063
17219
|
const actual = Object.keys(value).sort();
|
|
17064
17220
|
const expected = [...keys].sort();
|
|
17065
17221
|
if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
|
|
17066
|
-
|
|
17222
|
+
invalid2();
|
|
17067
17223
|
}
|
|
17068
17224
|
return value;
|
|
17069
17225
|
}
|
|
17070
17226
|
function ulid2(value) {
|
|
17071
17227
|
if (typeof value !== "string" || !/^[0-9A-HJKMNP-TV-Z]{26}$/u.test(value))
|
|
17072
|
-
|
|
17228
|
+
invalid2();
|
|
17073
17229
|
return value;
|
|
17074
17230
|
}
|
|
17075
|
-
function
|
|
17231
|
+
function invalid2() {
|
|
17076
17232
|
throw new TypeError("Operation deployment result is invalid");
|
|
17077
17233
|
}
|
|
17078
17234
|
var deploymentProgressActions = {
|
|
17079
17235
|
queued: "The deployment is queued. Poll this same operation; do not submit another deployment.",
|
|
17080
17236
|
building: "The build has not yet completed. Poll this same operation and read deployment logs if it fails.",
|
|
17081
17237
|
publishing: "The build has completed. Runtime preparation or publication is still running; poll this same operation.",
|
|
17082
|
-
waiting_for_mail: "Read 'ohmyhost mail
|
|
17083
|
-
mail_status_unavailable: "Mail readiness could not be observed. Read 'ohmyhost mail
|
|
17238
|
+
waiting_for_mail: "Read 'ohmyhost mail status --project PROJECT --environment PROD_ENVIRONMENT_ULID --json' (MCP mail_status) now, with the Prod environment ID from 'ohmyhost project status', and follow its verification issue and DNS records. Keep this operation; do not rebuild or repeat correct DNS changes.",
|
|
17239
|
+
mail_status_unavailable: "Mail readiness could not be observed. Read 'ohmyhost mail status --project PROJECT --environment PROD_ENVIRONMENT_ULID --json' (MCP mail_status) with the Prod environment ID from 'ohmyhost project status', and report a persistent error; keep this operation."
|
|
17084
17240
|
};
|
|
17085
17241
|
function createOperationDeploymentProgress(input) {
|
|
17086
17242
|
const project = ulid2(input.project_id);
|
|
17087
17243
|
ulid2(input.deployment_id);
|
|
17088
17244
|
if (!Object.hasOwn(deploymentProgressActions, input.phase) || !Number.isFinite(Date.parse(input.observed_at)) || new Date(input.observed_at).toISOString() !== input.observed_at)
|
|
17089
|
-
|
|
17245
|
+
invalid2();
|
|
17090
17246
|
if (input.build_completed_at !== void 0 && (!Number.isFinite(Date.parse(input.build_completed_at)) || new Date(input.build_completed_at).toISOString() !== input.build_completed_at))
|
|
17091
|
-
|
|
17247
|
+
invalid2();
|
|
17092
17248
|
return Object.freeze({
|
|
17093
17249
|
...input.build_completed_at === void 0 ? {} : { build_completed_at: input.build_completed_at },
|
|
17094
17250
|
phase: input.phase,
|
|
@@ -17119,7 +17275,7 @@ function parseOperationDeploymentProgress(value) {
|
|
|
17119
17275
|
observed_at: row["observed_at"]
|
|
17120
17276
|
});
|
|
17121
17277
|
if (row["next_poll_after_seconds"] !== 60 || row["suggested_action"] !== parsed.suggested_action)
|
|
17122
|
-
|
|
17278
|
+
invalid2();
|
|
17123
17279
|
return parsed;
|
|
17124
17280
|
}
|
|
17125
17281
|
|
|
@@ -17223,12 +17379,12 @@ function assertOrganizationAccount(value) {
|
|
|
17223
17379
|
// packages/contracts/dist/operation-failure.js
|
|
17224
17380
|
var failures = {
|
|
17225
17381
|
operation_abandoned: [
|
|
17226
|
-
"
|
|
17227
|
-
"
|
|
17382
|
+
"ohmyho.st stopped this operation before it finished and ended it.",
|
|
17383
|
+
"Repeating this operation cannot resume it. Read project status for what is live; if this operation names a deployment_id, read 'ohmyhost deployment logs --project PROJECT_ULID --deployment DEPLOYMENT_ULID --follow --json' or MCP deployment_logs. Then submit the request once more with a new idempotency key: a deployment, promotion, rollback or deletion from a fresh plan, a rename with the ETag from project status. If it ends the same way, report both operation IDs through feedback."
|
|
17228
17384
|
],
|
|
17229
17385
|
database_write_rejected: [
|
|
17230
17386
|
"The database rejected this write; it was not committed.",
|
|
17231
|
-
"
|
|
17387
|
+
"The statement failed on PostgreSQL, ran or waited for a lock longer than 5 seconds, affected more than 1,000 rows, or was not one INSERT, UPDATE or DELETE. Check table, column and parameter types with database query, split a large change into batches of at most 1,000 rows, then submit with a new key. Nothing was committed; do not reset the database."
|
|
17232
17388
|
],
|
|
17233
17389
|
database_write_unavailable: [
|
|
17234
17390
|
"The database write could not start.",
|
|
@@ -17280,7 +17436,7 @@ var failures = {
|
|
|
17280
17436
|
],
|
|
17281
17437
|
operation_failed: [
|
|
17282
17438
|
"The operation failed.",
|
|
17283
|
-
"
|
|
17439
|
+
"Read project status and, if this operation names a deployment_id, its diagnostics with 'ohmyhost deployment logs --project PROJECT_ULID --deployment DEPLOYMENT_ULID --follow --json'. Without one (a source link or project creation), check project status or project list, then repeat that command once with a new idempotency key. If it fails again, report the operation ID through feedback."
|
|
17284
17440
|
],
|
|
17285
17441
|
build_not_started: [
|
|
17286
17442
|
"The deployment ended without starting a build.",
|
|
@@ -17288,7 +17444,7 @@ var failures = {
|
|
|
17288
17444
|
],
|
|
17289
17445
|
build_failed: [
|
|
17290
17446
|
"The build did not produce a verified deployable artifact.",
|
|
17291
|
-
"Read the BUILD_FAILED
|
|
17447
|
+
"Read the BUILD_FAILED item 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 your install and build output: fix that error, push and plan the new commit. Without an excerpt the build printed nothing, passed the 8-minute limit or failed inside ohmyho.st: plan the same commit once more, and report the operation ID through feedback if it fails again."
|
|
17292
17448
|
],
|
|
17293
17449
|
insufficient_organization_credits: [
|
|
17294
17450
|
"The organization does not have sufficient available credits for this operation.",
|
|
@@ -17296,27 +17452,39 @@ var failures = {
|
|
|
17296
17452
|
],
|
|
17297
17453
|
paid_plan_required: [
|
|
17298
17454
|
"Transactional mail requires an active Paid plan for this organization.",
|
|
17299
|
-
"
|
|
17455
|
+
"Ask the organization Owner to activate Paid through billing checkout; top-ups and signup credits do not activate it. Then plan the same deployment or promotion again. If the app should send no mail, instead set mail.enabled to false in ohmyhost.yaml, commit and push, and plan the new commit. Replaying this operation cannot enable mail. Do not reset the database."
|
|
17300
17456
|
],
|
|
17301
17457
|
database_migration_failed: [
|
|
17302
|
-
"The database migrations
|
|
17303
|
-
"
|
|
17458
|
+
"The database migrations were not applied; none of this deployment's new migration files took effect.",
|
|
17459
|
+
"Read the DATABASE_MIGRATION_FAILED item in 'ohmyhost deployment logs --project PROJECT_ULID --deployment DEPLOYMENT_ULID --follow --json' and fix what it names; run 'ohmyhost init --dry-run --json' for migration_sql_not_admitted. Keep applied files unchanged, give new files later timestamps, and add to existing tables only nullable columns without default and non-unique indexes. Push and plan the new commit; never reset the database."
|
|
17304
17460
|
],
|
|
17305
17461
|
runtime_candidate_failed: [
|
|
17306
17462
|
"The deployed candidate failed its runtime checks.",
|
|
17307
|
-
"Read the HEALTH_CHECK_FAILED
|
|
17463
|
+
"Read the HEALTH_CHECK_FAILED item with 'ohmyhost deployment logs --project PROJECT_ULID --deployment DEPLOYMENT_ULID --follow --json' or MCP deployment_logs. ohmyhost.yaml runtime.healthcheck (default /) must answer 2xx to a cookieless GET without redirect; status_code and, for owners, excerpt show the answer. After a promotion or rollback, fix that environment, often a missing secret, and plan it again; else fix the app, push and plan the new commit. No such item: plan once more, then report."
|
|
17308
17464
|
],
|
|
17309
17465
|
runtime_candidate_rejected: [
|
|
17310
|
-
"The runtime
|
|
17311
|
-
"
|
|
17466
|
+
"The runtime refused to load the built Worker.",
|
|
17467
|
+
"The built Worker could not be loaded: top-level code throws or calls fetch, timers or random values outside a handler, an import cannot be resolved, a Worker module such as src/ohmyhost/companion.ts has no default export, or the bundle is too large. Fix that, push and plan the new commit; replaying this operation cannot pass. If none of this applies, report the operation ID through feedback."
|
|
17312
17468
|
],
|
|
17313
17469
|
storage_jurisdiction_conflict: [
|
|
17314
17470
|
"storage.jurisdiction must equal the project's hosting region.",
|
|
17315
|
-
"Set storage.jurisdiction in ohmyhost.yaml to the project's
|
|
17471
|
+
"Set storage.jurisdiction in ohmyhost.yaml to the project's region (us or eu) shown by 'ohmyhost project status', commit and push, then plan the new commit (for a promotion, deploy it to Dev and promote again); the unchanged commit fails again. A project cannot move its files between regions; do not delete or recreate the project to change it."
|
|
17316
17472
|
],
|
|
17317
17473
|
provider_state_absent: [
|
|
17318
17474
|
"Reconciliation confirmed that the required deployment resources are absent.",
|
|
17319
17475
|
"Create a fresh deployment plan. The original operation is terminal; do not repeatedly reconcile it."
|
|
17476
|
+
],
|
|
17477
|
+
auto_deploy_plan_rejected: [
|
|
17478
|
+
"The GitHub push could not be planned for deployment.",
|
|
17479
|
+
"Read project status and the operation's error or deployment diagnostics. Correct the source or configuration named there, push the new commit and plan it before deploying. Replaying this failed operation cannot start a build."
|
|
17480
|
+
],
|
|
17481
|
+
promotion_source_stale: [
|
|
17482
|
+
"The Dev deployment changed before promotion could start.",
|
|
17483
|
+
"Read the current Dev deployment with 'ohmyhost project status --project ULID --json', then run 'ohmyhost deployment promote plan' for that succeeded deployment and confirm the new plan. Replaying this terminal promotion cannot select another source."
|
|
17484
|
+
],
|
|
17485
|
+
promotion_target_stale: [
|
|
17486
|
+
"The Prod deployment changed before promotion could start.",
|
|
17487
|
+
"Read current Prod status, run 'ohmyhost deployment promote plan' again and confirm it with the new resource_etag and confirmation_token. Replaying this terminal operation cannot replace its stale target."
|
|
17320
17488
|
]
|
|
17321
17489
|
};
|
|
17322
17490
|
function publicOperationReconciliation(observation) {
|
|
@@ -17336,280 +17504,436 @@ function publicOperationFailure(internalCode) {
|
|
|
17336
17504
|
return Object.freeze({ code, message, retryable: false, suggested_action });
|
|
17337
17505
|
}
|
|
17338
17506
|
|
|
17339
|
-
//
|
|
17507
|
+
// packages/contracts/dist/problem-policy.js
|
|
17340
17508
|
var PROBLEM_POLICY = {
|
|
17341
17509
|
cloudflare_zone_not_bound: {
|
|
17342
17510
|
status: 409,
|
|
17343
17511
|
retryable: false,
|
|
17344
|
-
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."
|
|
17512
|
+
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.",
|
|
17513
|
+
title: "Cloudflare zone is not bound to this project"
|
|
17345
17514
|
},
|
|
17346
17515
|
github_connection_required: {
|
|
17347
17516
|
status: 409,
|
|
17348
17517
|
retryable: false,
|
|
17349
|
-
action: "This workspace has no GitHub connection yet. Run 'ohmyhost github connect --organization ULID --idempotency-key KEY --json' for it, complete its browser link, then repeat the original source link. If the project belongs to another account, use that account's login instead; error.acting_as names the login this ran as."
|
|
17518
|
+
action: "This workspace has no GitHub connection yet. Run 'ohmyhost github connect --organization ULID --idempotency-key KEY --json' for it, complete its browser link, then repeat the original source link. If the project belongs to another account, use that account's login instead; error.acting_as in the CLI and MCP names the login this ran as.",
|
|
17519
|
+
title: "GitHub connection required"
|
|
17350
17520
|
},
|
|
17351
17521
|
github_connection_revoked: {
|
|
17352
17522
|
status: 409,
|
|
17353
17523
|
retryable: false,
|
|
17354
|
-
action: "Run 'ohmyhost github connect --organization ULID --idempotency-key
|
|
17524
|
+
action: "The workspace's GitHub App installation was removed or can no longer be used. Run 'ohmyhost github connect --organization ULID --idempotency-key NEW_KEY --json' with a new key (an earlier key only replays the old connection), send the customer its authorization_url, repeat that command with the same new key until status is connected, then repeat the original source link.",
|
|
17525
|
+
title: "GitHub connection revoked",
|
|
17526
|
+
restAction: "The workspace's GitHub App installation was removed or can no longer be used. Run 'ohmyhost github connect --organization ULID --idempotency-key NEW_KEY --json' with a new key (an earlier key only replays the old connection), send the customer its authorization_url, repeat that command with the same new key until status is connected, then repeat the original source link."
|
|
17355
17527
|
},
|
|
17356
17528
|
repository_not_installed: {
|
|
17357
17529
|
status: 403,
|
|
17358
17530
|
retryable: false,
|
|
17359
|
-
action: "Read 'ohmyhost github status --organization ULID --json', open connection.settings_url and add this repository to the installation. Then repeat the original source link and key."
|
|
17531
|
+
action: "Read 'ohmyhost github status --organization ULID --json', open connection.settings_url and add this repository to the installation. Then repeat the original source link and key.",
|
|
17532
|
+
title: "Repository access required"
|
|
17360
17533
|
},
|
|
17361
17534
|
database_write_pending: {
|
|
17362
17535
|
status: 409,
|
|
17363
17536
|
retryable: false,
|
|
17364
|
-
action: "
|
|
17537
|
+
action: "A database write of this project is still running, so the project cannot be deleted yet. Read latest_operation from 'ohmyhost project status --project ULID --json' until it is no longer running; writes are short. Then repeat the deletion, planning it again if its confirmation expired.",
|
|
17538
|
+
title: "Database write in progress",
|
|
17539
|
+
restAction: "A database write of this project is still running, so the project cannot be deleted yet. Read latest_operation from 'ohmyhost project status --project ULID --json' until it is no longer running; writes are short. Then repeat the deletion, planning it again if its confirmation expired."
|
|
17365
17540
|
},
|
|
17366
17541
|
cloudflare_authorization_closed: {
|
|
17367
17542
|
status: 409,
|
|
17368
17543
|
retryable: false,
|
|
17369
|
-
action: "Read Cloudflare DNS status. Reuse a valid grant for the requested zone; otherwise request fresh authorization with a new idempotency key. Do not replay the callback."
|
|
17544
|
+
action: "Read Cloudflare DNS status. Reuse a valid grant for the requested zone; otherwise request fresh authorization with a new idempotency key. Do not replay the callback.",
|
|
17545
|
+
title: "Cloudflare authorization closed"
|
|
17370
17546
|
},
|
|
17371
17547
|
billing_recharge_conflict: {
|
|
17372
17548
|
status: 409,
|
|
17373
17549
|
retryable: false,
|
|
17374
|
-
action: "
|
|
17550
|
+
action: "The auto-recharge settings changed since you read them, or this key was used with other settings. Run 'ohmyhost billing recharge get --organization ULID --json'. If it already shows the requested change, stop. Otherwise, only if the Owner still wants it, send the change again with the returned revision and a new idempotency key. Never re-enable charges without the Owner's approval.",
|
|
17551
|
+
title: "Auto-recharge settings conflict",
|
|
17552
|
+
restAction: "The auto-recharge settings changed since you read them, or this key was used with other settings. Run 'ohmyhost billing recharge get --organization ULID --json'. If it already shows the requested change, stop. Otherwise, only if the Owner still wants it, send the change again with the returned revision and a new idempotency key. Never re-enable charges without the Owner's approval."
|
|
17375
17553
|
},
|
|
17376
17554
|
billing_purchase_conflict: {
|
|
17377
17555
|
status: 409,
|
|
17378
17556
|
retryable: false,
|
|
17379
|
-
action: "Read the original checkout
|
|
17557
|
+
action: "This workspace already has Paid or an open Paid checkout, this key belongs to a checkout with a different offer or pack count, or, for 'billing portal', the workspace has never started a checkout. Read the original checkout with 'ohmyhost billing status --organization ULID --checkout CHECKOUT_ULID --json'; manage an existing Paid subscription with 'ohmyhost billing portal'. Never start a second purchase after an uncertain one.",
|
|
17558
|
+
title: "Billing purchase conflict",
|
|
17559
|
+
restAction: "This workspace already has Paid or an open Paid checkout, this key belongs to a checkout with a different offer or pack count, or, for 'billing portal', the workspace has never started a checkout. Read the original checkout with 'ohmyhost billing status --organization ULID --checkout CHECKOUT_ULID --json'; manage an existing Paid subscription with 'ohmyhost billing portal'. Never start a second purchase after an uncertain one."
|
|
17380
17560
|
},
|
|
17381
17561
|
compute_performance_paid_required: {
|
|
17382
17562
|
status: 403,
|
|
17383
17563
|
retryable: false,
|
|
17384
|
-
action: "
|
|
17564
|
+
action: "Performance compute needs active Paid and usable credits or grace; repeating unchanged does not help. Read 'ohmyhost credits account --organization ULID --json'. On Free, the Owner may start Paid with 'ohmyhost billing checkout --offer paid'; without usable credits, they may add credits. Until then, keep standard compute.",
|
|
17565
|
+
title: "Performance requires Paid access",
|
|
17566
|
+
restAction: "Performance compute needs active Paid and usable credits or grace; repeating unchanged does not help. Read 'ohmyhost credits account --organization ULID --json'. On Free, the Owner may start Paid with 'ohmyhost billing checkout --offer paid'; without usable credits, they may add credits. Until then, keep standard compute."
|
|
17385
17567
|
},
|
|
17386
17568
|
compute_performance_unavailable: {
|
|
17387
17569
|
status: 503,
|
|
17388
17570
|
retryable: false,
|
|
17389
|
-
action: "Choose standard compute or report inactive performance pricing through feedback. Do not repeatedly resubmit."
|
|
17571
|
+
action: "Choose standard compute or report inactive performance pricing through feedback. Do not repeatedly resubmit.",
|
|
17572
|
+
title: "Performance pricing unavailable"
|
|
17390
17573
|
},
|
|
17391
17574
|
compute_change_pending: {
|
|
17392
17575
|
status: 409,
|
|
17393
17576
|
retryable: false,
|
|
17394
|
-
action: "
|
|
17577
|
+
action: "A database size change of this project is still running. Every 60 seconds read latest_operation from 'ohmyhost project status --project ULID --json', or 'ohmyhost operation get OPERATION_ID --json' for the change you started, until it ends, then repeat this request. Do not submit another size change or a deletion meanwhile.",
|
|
17578
|
+
title: "Database size change running",
|
|
17579
|
+
restAction: "A database size change of this project is still running. Every 60 seconds read latest_operation from 'ohmyhost project status --project ULID --json', or 'ohmyhost operation get OPERATION_ID --json' for the change you started, until it ends, then repeat this request. Do not submit another size change or a deletion meanwhile."
|
|
17395
17580
|
},
|
|
17396
17581
|
compute_change_conflict: {
|
|
17397
17582
|
status: 409,
|
|
17398
17583
|
retryable: false,
|
|
17399
|
-
action: "Read the original operation and current database compute. Preserve data and report unresolved conflicts through feedback with the operation ID."
|
|
17584
|
+
action: "Read the original operation and current database compute. Preserve data and report unresolved conflicts through feedback with the operation ID.",
|
|
17585
|
+
title: "Database size change conflict"
|
|
17400
17586
|
},
|
|
17401
17587
|
database_access_limit: {
|
|
17402
17588
|
status: 409,
|
|
17403
17589
|
retryable: false,
|
|
17404
|
-
action: "List the project's database credentials and revoke one you no longer need, then request access again."
|
|
17590
|
+
action: "List the project's database credentials and revoke one you no longer need, then request access again.",
|
|
17591
|
+
title: "Database access limit reached"
|
|
17405
17592
|
},
|
|
17406
17593
|
project_notes_conflict: {
|
|
17407
17594
|
status: 409,
|
|
17408
17595
|
retryable: false,
|
|
17409
|
-
action: "Read project context, merge the current notes and retry with its version and a new idempotency key."
|
|
17596
|
+
action: "Read project context, merge the current notes and retry with its version and a new idempotency key.",
|
|
17597
|
+
title: "Project notes changed"
|
|
17598
|
+
},
|
|
17599
|
+
invalid_request: {
|
|
17600
|
+
status: 400,
|
|
17601
|
+
retryable: false,
|
|
17602
|
+
action: "The server rejected this request; repeating it unchanged fails again. Check every argument against the command usage or the tool schema. For 'mail webhook verify' and mail receiving, the app's webhook route must answer the signed test with 2xx using the installed signing secret; receiving needs a verified webhook. For 'database query', send one read-only SELECT or WITH statement that runs on the current schema.",
|
|
17603
|
+
title: "Invalid request",
|
|
17604
|
+
restAction: "Read detail for the rejected field or condition, correct it and send the corrected request; repeating it unchanged fails again."
|
|
17410
17605
|
},
|
|
17411
|
-
invalid_request: { status: 400, retryable: false, action: "Check the request and retry." },
|
|
17412
17606
|
unauthenticated: {
|
|
17413
17607
|
status: 401,
|
|
17414
17608
|
retryable: false,
|
|
17415
|
-
action: "
|
|
17609
|
+
action: "The login or token was not accepted; repeating unchanged does not help. If OHMYHOST_TOKEN is set, it was revoked, mistyped or belongs to a different platform than OHMYHOST_ENVIRONMENT selects (a platform setting, not the project's dev or prod): ask the customer for a current token from the portal's API Tokens page, or unset it and run 'ohmyhost login --json'. Otherwise run 'ohmyhost login --json', then repeat the request.",
|
|
17610
|
+
title: "Authentication required",
|
|
17611
|
+
restAction: "The login or token was not accepted; repeating unchanged does not help. If OHMYHOST_TOKEN is set, it was revoked, mistyped or belongs to a different platform than OHMYHOST_ENVIRONMENT selects (a platform setting, not the project's dev or prod): ask the customer for a current token from the portal's API Tokens page, or unset it and run 'ohmyhost login --json'. Otherwise run 'ohmyhost login --json', then repeat the request."
|
|
17612
|
+
},
|
|
17613
|
+
forbidden: {
|
|
17614
|
+
status: 403,
|
|
17615
|
+
retryable: false,
|
|
17616
|
+
action: "This login may not do this; repeating unchanged does not help. error.acting_as names the login used: use a workspace this login belongs to ('ohmyhost whoami --json'), or the login of the account that owns it. For 'organization create', pass --source exactly as the Signup source on the portal Profile page, or omit it when that page shows Direct.",
|
|
17617
|
+
title: "Forbidden",
|
|
17618
|
+
restAction: "This identity may not do this; repeating unchanged does not help. Use a workspace this identity belongs to. A new workspace must preserve the signup source already recorded for this user; a scoped API token may create another workspace through the ordinary authorized flow."
|
|
17416
17619
|
},
|
|
17417
|
-
forbidden: { status: 403, retryable: false, action: "Check organization access." },
|
|
17418
17620
|
organization_required: {
|
|
17419
17621
|
status: 409,
|
|
17420
17622
|
retryable: false,
|
|
17421
|
-
action: "Run 'ohmyhost organization list --json' and select one with 'ohmyhost organization use --organization ULID --json', or create the first workspace."
|
|
17623
|
+
action: "Run 'ohmyhost organization list --json' and select one with 'ohmyhost organization use --organization ULID --json', or create the first workspace.",
|
|
17624
|
+
title: "Workspace selection required"
|
|
17422
17625
|
},
|
|
17423
17626
|
resource_not_found: {
|
|
17424
17627
|
status: 404,
|
|
17425
17628
|
retryable: false,
|
|
17426
|
-
action: "Check the resource identifier and the account: a project is visible only to its own organization, and error.acting_as names the login this ran as. Use the login of the account that owns it."
|
|
17629
|
+
action: "Check the resource identifier and the account: a project is visible only to its own organization, and error.acting_as in the CLI and MCP names the login this ran as. Use the login of the account that owns it.",
|
|
17630
|
+
title: "Resource not found"
|
|
17427
17631
|
},
|
|
17428
17632
|
idempotency_key_reused: {
|
|
17429
17633
|
status: 409,
|
|
17430
17634
|
retryable: false,
|
|
17431
|
-
action: "
|
|
17635
|
+
action: "This key was already used for a request with different arguments or from another login. To repeat that original request, send exactly its original arguments with this key from the same login; for a new request, use a new key. Never pair a new key with a request whose first attempt had an uncertain outcome: read its result first.",
|
|
17636
|
+
title: "Idempotency key reused",
|
|
17637
|
+
restAction: "This key was already used for a request with different arguments or from another login. To repeat that original request, send exactly its original arguments with this key from the same login; for a new request, use a new key. Never pair a new key with a request whose first attempt had an uncertain outcome: read its result first."
|
|
17432
17638
|
},
|
|
17433
17639
|
project_handle_unavailable: {
|
|
17434
17640
|
status: 503,
|
|
17435
17641
|
retryable: true,
|
|
17436
|
-
action: "Retry project creation with the same idempotency key."
|
|
17642
|
+
action: "Retry project creation with the same idempotency key.",
|
|
17643
|
+
title: "Project handle unavailable"
|
|
17437
17644
|
},
|
|
17438
17645
|
project_handle_taken: {
|
|
17439
17646
|
status: 409,
|
|
17440
17647
|
retryable: false,
|
|
17441
|
-
action: "Run 'ohmyhost project handle check'
|
|
17648
|
+
action: "Another project uses this address. Run 'ohmyhost project handle check --handle NAME --json' and offer the customer its alternatives; rename only to the one they choose.",
|
|
17649
|
+
title: "Project address is taken",
|
|
17650
|
+
restAction: "Another project uses this address. Run 'ohmyhost project handle check --handle NAME --json' and offer the customer its alternatives; rename only to the one they choose."
|
|
17442
17651
|
},
|
|
17443
17652
|
project_handle_invalid: {
|
|
17444
17653
|
status: 409,
|
|
17445
17654
|
retryable: false,
|
|
17446
|
-
action: "Choose one to five lowercase words of letters and digits, three to fifty-nine characters, avoiding reserved words."
|
|
17655
|
+
action: "Choose one to five lowercase words of letters and digits, three to fifty-nine characters, avoiding reserved words.",
|
|
17656
|
+
title: "Project address is invalid"
|
|
17447
17657
|
},
|
|
17448
17658
|
project_handle_unchanged: {
|
|
17449
17659
|
status: 409,
|
|
17450
17660
|
retryable: false,
|
|
17451
|
-
action: "The project already answers on this address; no rename is needed."
|
|
17661
|
+
action: "The project already answers on this address; no rename is needed.",
|
|
17662
|
+
title: "Project address is unchanged"
|
|
17452
17663
|
},
|
|
17453
17664
|
project_rename_blocked: {
|
|
17454
17665
|
status: 409,
|
|
17455
17666
|
retryable: true,
|
|
17456
|
-
action: "Wait for the running operation to finish, read the project again, then rename with the new ETag."
|
|
17667
|
+
action: "Wait for the running operation to finish, read the project again, then rename with the new ETag.",
|
|
17668
|
+
title: "Project address change blocked"
|
|
17457
17669
|
},
|
|
17458
17670
|
project_identity_unavailable: {
|
|
17459
17671
|
status: 503,
|
|
17460
17672
|
retryable: true,
|
|
17461
|
-
action: "Retry project creation with the same idempotency key."
|
|
17673
|
+
action: "Retry project creation with the same idempotency key.",
|
|
17674
|
+
title: "Project identity unavailable"
|
|
17462
17675
|
},
|
|
17463
17676
|
deployment_plan_expired: {
|
|
17464
17677
|
status: 409,
|
|
17465
17678
|
retryable: false,
|
|
17466
|
-
action: "Run 'ohmyhost plan' again, then deploy the new plan."
|
|
17679
|
+
action: "Run 'ohmyhost plan' again, then deploy the new plan.",
|
|
17680
|
+
title: "Deployment plan expired"
|
|
17467
17681
|
},
|
|
17468
17682
|
deployment_plan_incompatible: {
|
|
17469
17683
|
status: 409,
|
|
17470
17684
|
retryable: false,
|
|
17471
|
-
action: "Run 'ohmyhost plan' again with the current source revision."
|
|
17685
|
+
action: "Run 'ohmyhost plan' again with the current source revision.",
|
|
17686
|
+
title: "Deployment plan incompatible"
|
|
17472
17687
|
},
|
|
17473
17688
|
confirmation_expired: {
|
|
17474
17689
|
status: 409,
|
|
17475
17690
|
retryable: false,
|
|
17476
|
-
action: "Plan the destructive action again before confirming it."
|
|
17691
|
+
action: "Plan the destructive action again before confirming it.",
|
|
17692
|
+
title: "Confirmation expired"
|
|
17477
17693
|
},
|
|
17478
17694
|
confirmation_invalid: {
|
|
17479
17695
|
status: 409,
|
|
17480
17696
|
retryable: false,
|
|
17481
|
-
action: "
|
|
17697
|
+
action: "The confirmation token does not match this action or the project's current state. A deletion is also refused while a SQL export of the project is being captured, for up to about ten minutes ('ohmyhost export get'). Then create a new plan and confirm with its confirmation_token and resource_etag before the token expires.",
|
|
17698
|
+
title: "Confirmation invalid",
|
|
17699
|
+
restAction: "The confirmation token does not match this action or the project's current state. A deletion is also refused while a SQL export of the project is being captured, for up to about ten minutes ('ohmyhost export get'). Then create a new plan and confirm with its confirmation_token and resource_etag before the token expires."
|
|
17482
17700
|
},
|
|
17483
17701
|
etag_mismatch: {
|
|
17484
17702
|
status: 409,
|
|
17485
17703
|
retryable: false,
|
|
17486
|
-
action: "
|
|
17704
|
+
action: "The project changed after you read its ETag. For a rename, read etag from 'ohmyhost project status --project ULID --json' and repeat 'ohmyhost project handle set' with it. For a rollback, promotion or deletion, create a new plan and confirm with its resource_etag and confirmation_token.",
|
|
17705
|
+
title: "Resource changed",
|
|
17706
|
+
restAction: "The project changed after you read its ETag. For a rename, read etag from 'ohmyhost project status --project ULID --json' and repeat 'ohmyhost project handle set' with it. For a rollback, promotion or deletion, create a new plan and confirm with its resource_etag and confirmation_token."
|
|
17487
17707
|
},
|
|
17488
17708
|
promotion_source_stale: {
|
|
17489
17709
|
status: 409,
|
|
17490
17710
|
retryable: false,
|
|
17491
|
-
action: "Read the current dev deployment, then plan promotion again."
|
|
17711
|
+
action: "Read the current dev deployment, then plan promotion again.",
|
|
17712
|
+
title: "Promotion source changed"
|
|
17492
17713
|
},
|
|
17493
17714
|
promotion_target_stale: {
|
|
17494
17715
|
status: 409,
|
|
17495
17716
|
retryable: false,
|
|
17496
|
-
action: "Read the current prod status, then plan promotion again."
|
|
17717
|
+
action: "Read the current prod status, then plan promotion again.",
|
|
17718
|
+
title: "Promotion target changed"
|
|
17497
17719
|
},
|
|
17498
17720
|
promotion_invalid_target: {
|
|
17499
17721
|
status: 409,
|
|
17500
17722
|
retryable: false,
|
|
17501
|
-
action: "Select the current succeeded dev deployment and target prod."
|
|
17723
|
+
action: "Select the current succeeded dev deployment and target prod.",
|
|
17724
|
+
title: "Promotion target invalid"
|
|
17502
17725
|
},
|
|
17503
17726
|
mail_capacity_unavailable: {
|
|
17504
17727
|
status: 503,
|
|
17505
17728
|
retryable: true,
|
|
17506
|
-
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."
|
|
17729
|
+
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.",
|
|
17730
|
+
title: "Mail capacity unavailable"
|
|
17507
17731
|
},
|
|
17508
17732
|
mail_domain_conflict: {
|
|
17509
17733
|
status: 409,
|
|
17510
17734
|
retryable: false,
|
|
17511
|
-
action: "
|
|
17735
|
+
action: "Use the sender domain already configured for this project, or retire it with 'ohmyhost mail domain delete' (repeat while the removal is pending) and then set up the new domain; a domain registered to another project cannot be set up.",
|
|
17736
|
+
title: "Mail domain conflict"
|
|
17512
17737
|
},
|
|
17513
17738
|
mail_domain_required: {
|
|
17514
17739
|
status: 409,
|
|
17515
17740
|
retryable: false,
|
|
17516
|
-
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."
|
|
17741
|
+
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.",
|
|
17742
|
+
title: "Mail domain required"
|
|
17517
17743
|
},
|
|
17518
17744
|
storage_jurisdiction_conflict: {
|
|
17519
17745
|
status: 409,
|
|
17520
17746
|
retryable: false,
|
|
17521
|
-
action: "Set storage.jurisdiction in ohmyhost.yaml to the project's
|
|
17747
|
+
action: "Set storage.jurisdiction in ohmyhost.yaml to the project's region (us or eu) shown by 'ohmyhost project status', commit and push, then plan the new commit (for a promotion, deploy it to Dev and promote again); the unchanged commit fails again. A project cannot move its files between regions; do not delete or recreate the project to change it.",
|
|
17748
|
+
title: "Storage jurisdiction conflict",
|
|
17749
|
+
restAction: "Set storage.jurisdiction in ohmyhost.yaml to the project's region (us or eu) shown by 'ohmyhost project status', commit and push, then plan the new commit (for a promotion, deploy it to Dev and promote again); the unchanged commit fails again. A project cannot move its files between regions; do not delete or recreate the project to change it."
|
|
17522
17750
|
},
|
|
17523
17751
|
shared_data_requires_promotion: {
|
|
17524
17752
|
status: 409,
|
|
17525
17753
|
retryable: false,
|
|
17526
|
-
action: "Plan and deploy this commit to Dev, then run 'ohmyhost deployment promote plan' to reach Prod."
|
|
17754
|
+
action: "Plan and deploy this commit to Dev, then run 'ohmyhost deployment promote plan' to reach Prod.",
|
|
17755
|
+
title: "Shared data requires promotion"
|
|
17527
17756
|
},
|
|
17528
17757
|
production_deployment_required: {
|
|
17529
17758
|
status: 409,
|
|
17530
17759
|
retryable: false,
|
|
17531
|
-
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."
|
|
17760
|
+
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.",
|
|
17761
|
+
title: "Production deployment required"
|
|
17532
17762
|
},
|
|
17533
17763
|
framework_conversion_required: {
|
|
17534
17764
|
status: 409,
|
|
17535
17765
|
retryable: false,
|
|
17536
|
-
action: "Run
|
|
17766
|
+
action: "Read detail for the file, reason and required change. Run ohmyhost init --dry-run --json at that commit, apply its blockers and the matching ohmyho.st framework Skill, then commit and push. Plan the new commit; repeating the unchanged source cannot fix an admission error.",
|
|
17767
|
+
title: "Framework conversion required",
|
|
17768
|
+
restAction: "Read detail for the file, reason and required change. Run ohmyhost init --dry-run --json at that commit, apply its blockers and the matching ohmyho.st framework Skill, then commit and push. Plan the new commit; repeating the unchanged source cannot fix an admission error."
|
|
17537
17769
|
},
|
|
17538
17770
|
repository_configuration_missing: {
|
|
17539
17771
|
status: 409,
|
|
17540
17772
|
retryable: false,
|
|
17541
|
-
action: "
|
|
17773
|
+
action: "The planned commit has no ohmyhost.yaml at the Git repository root. If the repository has none, run 'ohmyhost init --json' at the root (--root DIR when the app lives in a subdirectory, --region eu for an EU project). Otherwise move the existing file to the root and set applicationRoot to the app directory, for example website. Commit and push, then plan the new commit.",
|
|
17774
|
+
title: "Repository configuration missing",
|
|
17775
|
+
restAction: "The planned commit has no ohmyhost.yaml at the Git repository root. If the repository has none, run 'ohmyhost init --json' at the root (--root DIR when the app lives in a subdirectory, --region eu for an EU project). Otherwise move the existing file to the root and set applicationRoot to the app directory, for example website. Commit and push, then plan the new commit."
|
|
17542
17776
|
},
|
|
17543
17777
|
migration_filename_noncanonical: {
|
|
17544
17778
|
status: 409,
|
|
17545
17779
|
retryable: false,
|
|
17546
|
-
action: "
|
|
17780
|
+
action: "Every file in the migrations directory (database.migrations in ohmyhost.yaml) must be named YYYYMMDDHHMMSS_name.sql: 14 digits, an underscore, then lowercase letters, digits, _ or -. Rename each file that does not match and move any other file, such as README.md or .gitkeep, out of that directory. Commit and push, then plan the new commit. Never rename a migration a deployment already applied.",
|
|
17781
|
+
title: "Migration filename noncanonical",
|
|
17782
|
+
restAction: "Every file in the migrations directory (database.migrations in ohmyhost.yaml) must be named YYYYMMDDHHMMSS_name.sql: 14 digits, an underscore, then lowercase letters, digits, _ or -. Rename each file that does not match and move any other file, such as README.md or .gitkeep, out of that directory. Commit and push, then plan the new commit. Never rename a migration a deployment already applied."
|
|
17547
17783
|
},
|
|
17548
17784
|
environment_secret_mutation_blocked: {
|
|
17549
17785
|
status: 409,
|
|
17550
17786
|
retryable: true,
|
|
17551
|
-
action: "Wait for the active deployment or rollback to finish, then retry unchanged."
|
|
17787
|
+
action: "Wait for the active deployment or rollback to finish, then retry unchanged.",
|
|
17788
|
+
title: "Environment secret mutation blocked"
|
|
17552
17789
|
},
|
|
17553
17790
|
workers_runtime_incompatible: {
|
|
17554
17791
|
status: 409,
|
|
17555
17792
|
retryable: false,
|
|
17556
|
-
action: "
|
|
17793
|
+
action: "A Next.js file under app/, pages/, middleware or proxy imports a native addon (.node) or a Node.js module that does not run on Workers: child_process, cluster, dgram, domain, http2, inspector, readline, repl, sqlite, trace_events, tty, v8, vm, wasi, worker_threads or _stream_wrap, with or without node:. Remove or replace that import, commit and push, then plan the new commit.",
|
|
17794
|
+
title: "Workers runtime incompatible",
|
|
17795
|
+
restAction: "A Next.js file under app/, pages/, middleware or proxy imports a native addon (.node) or a Node.js module that does not run on Workers: child_process, cluster, dgram, domain, http2, inspector, readline, repl, sqlite, trace_events, tty, v8, vm, wasi, worker_threads or _stream_wrap, with or without node:. Remove or replace that import, commit and push, then plan the new commit."
|
|
17796
|
+
},
|
|
17797
|
+
payload_too_large: {
|
|
17798
|
+
status: 413,
|
|
17799
|
+
retryable: false,
|
|
17800
|
+
action: "Reduce the request size.",
|
|
17801
|
+
title: "Payload too large"
|
|
17557
17802
|
},
|
|
17558
|
-
payload_too_large: { status: 413, retryable: false, action: "Reduce the request size." },
|
|
17559
17803
|
project_export_not_ready: {
|
|
17560
17804
|
status: 409,
|
|
17561
17805
|
retryable: false,
|
|
17562
|
-
action: "
|
|
17806
|
+
action: "No database of this project is ready, so no export was accepted. A database is created when a commit whose ohmyhost.yaml sets database.enabled: true is deployed; request the export again after that deployment succeeds ('ohmyhost operation get OPERATION_ID --json'). A project without a database has no SQL export.",
|
|
17807
|
+
title: "Project database not ready for export",
|
|
17808
|
+
restAction: "No database of this project is ready, so no export was accepted. A database is created when a commit whose ohmyhost.yaml sets database.enabled: true is deployed; request the export again after that deployment succeeds ('ohmyhost operation get OPERATION_ID --json'). A project without a database has no SQL export."
|
|
17563
17809
|
},
|
|
17564
17810
|
powered_by_flag_required: {
|
|
17565
17811
|
status: 409,
|
|
17566
17812
|
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."
|
|
17813
|
+
action: "Keep the flag on, remove the custom domain with 'ohmyhost domain paid delete', or buy Paid, then switch the flag off.",
|
|
17814
|
+
title: "Powered-by flag required"
|
|
17815
|
+
},
|
|
17816
|
+
rate_limited: {
|
|
17817
|
+
status: 429,
|
|
17818
|
+
retryable: true,
|
|
17819
|
+
action: "Too many requests. Wait retry_after_seconds, or 60 seconds when none is shown, then repeat the same request with the same idempotency key. SQL exports allow one request per project per rolling 24 hours: poll the export already requested with 'ohmyhost export get EXPORT_ID --project ULID --json' instead of creating another.",
|
|
17820
|
+
title: "Rate limited",
|
|
17821
|
+
restAction: "Wait the Retry-After interval (also retry_after_seconds), then repeat the same request with the same idempotency key."
|
|
17568
17822
|
},
|
|
17569
|
-
rate_limited: { status: 429, retryable: true, action: "Retry after the server delay." },
|
|
17570
17823
|
insufficient_organization_credits: {
|
|
17571
17824
|
status: 402,
|
|
17572
17825
|
retryable: false,
|
|
17573
|
-
action: "Add organization credits or wait for reservations to settle, then retry the same request."
|
|
17826
|
+
action: "Add organization credits or wait for reservations to settle, then retry the same request.",
|
|
17827
|
+
title: "Insufficient organization credits"
|
|
17574
17828
|
},
|
|
17575
17829
|
project_budget_exceeded: {
|
|
17576
17830
|
status: 402,
|
|
17577
17831
|
retryable: false,
|
|
17578
|
-
action: "
|
|
17832
|
+
action: "This project's Stop budget for the current UTC month is reached; nothing was started. Show the customer 'ohmyhost budget get --project ULID --json'. Only on their request raise the limit or switch the mode to continue with 'ohmyhost budget set', then retry the same request; otherwise wait for the next UTC month.",
|
|
17833
|
+
title: "Project budget exceeded",
|
|
17834
|
+
restAction: "This project's Stop budget for the current UTC month is reached; nothing was started. Show the customer 'ohmyhost budget get --project ULID --json'. Only on their request raise the limit or switch the mode to continue with 'ohmyhost budget set', then retry the same request; otherwise wait for the next UTC month."
|
|
17579
17835
|
},
|
|
17580
17836
|
paid_plan_required: {
|
|
17581
17837
|
status: 402,
|
|
17582
17838
|
retryable: false,
|
|
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."
|
|
17839
|
+
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.",
|
|
17840
|
+
title: "Paid plan required"
|
|
17584
17841
|
},
|
|
17585
17842
|
interactive_login_required: {
|
|
17586
17843
|
status: 403,
|
|
17587
17844
|
retryable: false,
|
|
17588
|
-
action: "Unset OHMYHOST_TOKEN and run ohmyhost login before managing tokens. Existing token files remain unchanged."
|
|
17845
|
+
action: "Unset OHMYHOST_TOKEN and run ohmyhost login before managing tokens. Existing token files remain unchanged.",
|
|
17846
|
+
title: "Interactive login required"
|
|
17589
17847
|
},
|
|
17590
17848
|
api_key_creation_uncertain: {
|
|
17591
17849
|
status: 503,
|
|
17592
17850
|
retryable: true,
|
|
17593
|
-
action: "Repeat the original token name and Idempotency-Key to observe its result; do not blindly create another key."
|
|
17851
|
+
action: "Repeat the original token name and Idempotency-Key to observe its result; do not blindly create another key.",
|
|
17852
|
+
title: "Token creation unconfirmed"
|
|
17594
17853
|
},
|
|
17595
17854
|
api_key_permissions_unavailable: {
|
|
17596
17855
|
status: 503,
|
|
17597
17856
|
retryable: false,
|
|
17598
|
-
action: "Report the platform user-key permission configuration through feedback. Repeated login or creation will not fix it."
|
|
17857
|
+
action: "Report the platform user-key permission configuration through feedback. Repeated login or creation will not fix it.",
|
|
17858
|
+
title: "Token permissions unavailable"
|
|
17599
17859
|
},
|
|
17600
17860
|
reconciliation_exhausted: {
|
|
17601
17861
|
status: 409,
|
|
17602
17862
|
retryable: false,
|
|
17603
|
-
action: "Stop retries and submit feedback with this operation ID. A platform recovery review is required."
|
|
17863
|
+
action: "Stop retries and submit feedback with this operation ID. A platform recovery review is required.",
|
|
17864
|
+
title: "Recovery attempts exhausted"
|
|
17865
|
+
},
|
|
17866
|
+
service_unavailable: {
|
|
17867
|
+
status: 503,
|
|
17868
|
+
retryable: true,
|
|
17869
|
+
action: "The service could not complete or confirm this request; it may already have taken effect. Wait, then repeat exactly the same request with the same idempotency key, waiting longer each time; never switch to a new key. If 'ohmyhost plan' keeps failing, check that the commit is pushed to the linked repository and that 'ohmyhost init --dry-run --json' at that commit reports no blockers. If it still fails, report the request_id through feedback.",
|
|
17870
|
+
title: "Service unavailable",
|
|
17871
|
+
restAction: "The service could not complete or confirm this request; it may already have taken effect. Wait, then repeat exactly the same request with the same idempotency key, waiting longer each time; never switch to a new key. If 'ohmyhost plan' keeps failing, check that the commit is pushed to the linked repository and that 'ohmyhost init --dry-run --json' at that commit reports no blockers. If it still fails, report the request_id through feedback."
|
|
17604
17872
|
},
|
|
17605
|
-
|
|
17873
|
+
source_commit_not_found: {
|
|
17874
|
+
status: 409,
|
|
17875
|
+
retryable: false,
|
|
17876
|
+
action: "The requested commit is not in the linked repository. Push that exact commit to the repository named by project status, or select a commit already there, then run 'ohmyhost plan' with its full SHA. Repeating the unchanged unavailable commit cannot work.",
|
|
17877
|
+
restAction: "The requested commit is not in the linked repository. Push that exact commit to the repository named by project status, or select a commit already there, then run 'ohmyhost plan' with its full SHA. Repeating the unchanged unavailable commit cannot work.",
|
|
17878
|
+
title: "Source commit not found"
|
|
17879
|
+
},
|
|
17880
|
+
domain_hostname_taken: {
|
|
17881
|
+
status: 409,
|
|
17882
|
+
retryable: false,
|
|
17883
|
+
action: "A live project connection already holds this hostname. Remove it from the project you manage with 'ohmyhost domain paid delete --project ULID --hostname HOST --yes --json', then apply it to the new project. Repeating while that connection remains cannot work; report the request_id if you cannot manage its owning project.",
|
|
17884
|
+
restAction: "A live project connection already holds this hostname. Remove it from the project you manage with 'ohmyhost domain paid delete --project ULID --hostname HOST --yes --json', then apply it to the new project. Repeating while that connection remains cannot work; report the request_id if you cannot manage its owning project.",
|
|
17885
|
+
title: "Domain hostname is already connected"
|
|
17886
|
+
},
|
|
17887
|
+
data_change_blocked: {
|
|
17888
|
+
status: 409,
|
|
17889
|
+
retryable: true,
|
|
17890
|
+
action: "An operation of this project is still running. Wait until 'ohmyhost operation get OPERATION_ULID --json' shows it finished, plan the data change again and confirm it with the new ETag and token.",
|
|
17891
|
+
restAction: "An operation of this project is still running. Wait until 'ohmyhost operation get OPERATION_ULID --json' shows it finished, plan the data change again and confirm it with the new ETag and token.",
|
|
17892
|
+
title: "Data change blocked"
|
|
17893
|
+
},
|
|
17894
|
+
data_change_in_progress: {
|
|
17895
|
+
status: 409,
|
|
17896
|
+
retryable: true,
|
|
17897
|
+
action: "A data change of this project is running. Wait until its operation has finished ('ohmyhost project status --project ULID --json'), then repeat the request unchanged.",
|
|
17898
|
+
restAction: "A data change of this project is running. Wait until its operation has finished ('ohmyhost project status --project ULID --json'), then repeat the request unchanged.",
|
|
17899
|
+
title: "Data change in progress"
|
|
17900
|
+
},
|
|
17901
|
+
data_change_not_applicable: {
|
|
17902
|
+
status: 409,
|
|
17903
|
+
retryable: false,
|
|
17904
|
+
action: "Read 'ohmyhost project status --project ULID --json'. Isolation applies to shared projects; returning to shared needs a deployed Prod database, and reset_dev applies to isolated projects. If detail names an old database or storage binding, deploy the affected environment again before planning the change. Repeating unchanged does not help.",
|
|
17905
|
+
restAction: "Read 'ohmyhost project status --project ULID --json'. Isolation applies to shared projects; returning to shared needs a deployed Prod database, and reset_dev applies to isolated projects. If detail names an old database or storage binding, deploy the affected environment again before planning the change. Repeating unchanged does not help.",
|
|
17906
|
+
title: "Data change is not applicable"
|
|
17907
|
+
},
|
|
17908
|
+
rollback_target_data_changed: {
|
|
17909
|
+
status: 409,
|
|
17910
|
+
retryable: false,
|
|
17911
|
+
action: "This deployment used a data area its environment no longer has. Deploy that commit again with a fresh plan instead of rolling back to its old bindings; repeating this rollback cannot work.",
|
|
17912
|
+
restAction: "This deployment used a data area its environment no longer has. Deploy that commit again with a fresh plan instead of rolling back to its old bindings; repeating this rollback cannot work.",
|
|
17913
|
+
title: "Rollback data area changed"
|
|
17914
|
+
},
|
|
17915
|
+
project_domain_delete_required: {
|
|
17916
|
+
status: 409,
|
|
17917
|
+
retryable: false,
|
|
17918
|
+
title: "Remove the project domain first",
|
|
17919
|
+
action: "This project still has a live or pending custom-domain connection. Read 'ohmyhost domain paid status --project ULID --json', remove that connection with 'ohmyhost domain paid delete --project ULID --hostname HOST --yes --json', then create a fresh project deletion plan. Repeating while it remains connected cannot work."
|
|
17920
|
+
}
|
|
17606
17921
|
};
|
|
17922
|
+
|
|
17923
|
+
// apps/product-cli/dist/runtime-contract.js
|
|
17607
17924
|
var ResponseContractError = class extends Error {
|
|
17608
17925
|
constructor() {
|
|
17609
17926
|
super("The server response does not match the public contract.");
|
|
17610
17927
|
this.name = "ResponseContractError";
|
|
17611
17928
|
}
|
|
17612
17929
|
};
|
|
17930
|
+
function parseProjectDataChangePlan2(value) {
|
|
17931
|
+
try {
|
|
17932
|
+
return parseProjectDataChangePlan(value);
|
|
17933
|
+
} catch {
|
|
17934
|
+
throw new ResponseContractError();
|
|
17935
|
+
}
|
|
17936
|
+
}
|
|
17613
17937
|
var parsePaidDomainPlan = (value) => {
|
|
17614
17938
|
const input = exactRecord2(value, [
|
|
17615
17939
|
"cname_target",
|
|
@@ -18259,6 +18583,7 @@ var parseSafeProblem = (value) => {
|
|
|
18259
18583
|
const docsUrl = String(input["type"]);
|
|
18260
18584
|
return {
|
|
18261
18585
|
requestId: input["request_id"],
|
|
18586
|
+
...input["detail"] === void 0 ? {} : { message: String(input["detail"]) },
|
|
18262
18587
|
...docsUrl.startsWith("https://docs.ohmyho.st/errors/") ? { docsUrl } : {},
|
|
18263
18588
|
code,
|
|
18264
18589
|
retryable: policy.retryable,
|
|
@@ -18439,8 +18764,10 @@ var selectWorkspace = async (session, organization, signal, identity) => {
|
|
|
18439
18764
|
throw new InteractiveSessionRequiredError();
|
|
18440
18765
|
const refreshed = await session.refresher.refresh(stored.refreshToken, signal, organization.workos_id);
|
|
18441
18766
|
assertVerifiedLogin(await identity(refreshed.accessToken), session.profile.userId, organization.id);
|
|
18767
|
+
const existing = (await session.profiles.list()).find((saved) => saved.userId === session.profile.userId && saved.organizationId === organization.id);
|
|
18442
18768
|
const bound = {
|
|
18443
18769
|
...session.profile,
|
|
18770
|
+
name: existing?.name ?? session.profile.name,
|
|
18444
18771
|
organizationId: organization.id,
|
|
18445
18772
|
organizationName: organization.name
|
|
18446
18773
|
};
|
|
@@ -18537,7 +18864,7 @@ function assertProjectDatabaseAccessPage(value) {
|
|
|
18537
18864
|
}
|
|
18538
18865
|
function assertAccessFields(value) {
|
|
18539
18866
|
const revoked = value["revoked_at"], reason = value["revocation_reason"];
|
|
18540
|
-
if (typeof value["access_id"] !== "string" || !/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(value["access_id"]) || !["dev", "prod"].includes(String(value["environment"])) || !["read", "write"].includes(String(value["mode"])) || typeof value["role_name"] !== "string" || !ACCESS_ROLE_PATTERN.test(value["role_name"]) || typeof value["host"] !== "string" || !/^[a-z0-9]([a-z0-9.-]{0,251}[a-z0-9])?$/u.test(value["host"]) || value["database"] !== "neondb" || value["label"] !== null && accessLabel(value["label"]) !== value["label"] || !["active", "expired", "revoked"].includes(String(value["state"])) || !timestamp(value["issued_at"]) || !timestamp(value["expires_at"]) || Date.parse(String(value["expires_at"])) <= Date.parse(String(value["issued_at"])) || revoked !== null && !timestamp(revoked) || revoked === null !== (reason === null) || reason !== null && !["principal", "expired", "budget", "creation_failed"].includes(String(reason)) || value["state"] === "revoked" !== (revoked !== null))
|
|
18867
|
+
if (typeof value["access_id"] !== "string" || !/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(value["access_id"]) || !["dev", "prod"].includes(String(value["environment"])) || !["read", "write"].includes(String(value["mode"])) || typeof value["role_name"] !== "string" || !ACCESS_ROLE_PATTERN.test(value["role_name"]) || typeof value["host"] !== "string" || !/^[a-z0-9]([a-z0-9.-]{0,251}[a-z0-9])?$/u.test(value["host"]) || value["database"] !== "neondb" || value["label"] !== null && accessLabel(value["label"]) !== value["label"] || !["active", "expired", "revoked"].includes(String(value["state"])) || !timestamp(value["issued_at"]) || !timestamp(value["expires_at"]) || Date.parse(String(value["expires_at"])) <= Date.parse(String(value["issued_at"])) || revoked !== null && !timestamp(revoked) || revoked === null !== (reason === null) || reason !== null && !["principal", "expired", "budget", "creation_failed", "data_change"].includes(String(reason)) || value["state"] === "revoked" !== (revoked !== null))
|
|
18541
18868
|
throw new TypeError("Invalid database access");
|
|
18542
18869
|
}
|
|
18543
18870
|
function accessLabel(value) {
|
|
@@ -18609,7 +18936,7 @@ function exact2(v, keys) {
|
|
|
18609
18936
|
// apps/product-cli/package.json
|
|
18610
18937
|
var package_default = {
|
|
18611
18938
|
name: "@ohmyhost/product-cli",
|
|
18612
|
-
version: "0.1.
|
|
18939
|
+
version: "0.1.25",
|
|
18613
18940
|
private: true,
|
|
18614
18941
|
ohmyhost: {
|
|
18615
18942
|
deployment: "production",
|
|
@@ -18672,43 +18999,43 @@ function assertProjectContext(v) {
|
|
|
18672
18999
|
// packages/contracts/dist/project-exports.js
|
|
18673
19000
|
function assertProjectExport(value) {
|
|
18674
19001
|
if (!record4(value) || Object.keys(value).sort().join(",") !== "archive,error,id,next_poll_after_seconds,next_request_at,project_id,requested_at,state" || typeof value["id"] !== "string" || !id.test(value["id"]) || typeof value["project_id"] !== "string" || !id.test(value["project_id"]) || !["queued", "running", "succeeded", "failed", "cancelled"].includes(String(value["state"])) || !timestamp3(value["requested_at"]) || !timestamp3(value["next_request_at"]))
|
|
18675
|
-
|
|
19002
|
+
invalid3();
|
|
18676
19003
|
const active = value["state"] === "queued" || value["state"] === "running";
|
|
18677
19004
|
if (value["next_poll_after_seconds"] !== (active ? 5 : null))
|
|
18678
|
-
|
|
19005
|
+
invalid3();
|
|
18679
19006
|
if (value["state"] === "failed") {
|
|
18680
19007
|
const error = value["error"];
|
|
18681
19008
|
if (!record4(error) || Object.keys(error).sort().join(",") !== "code,message,retryable,suggested_action" || typeof error["code"] !== "string")
|
|
18682
|
-
|
|
19009
|
+
invalid3();
|
|
18683
19010
|
const expected = publicOperationFailure(error["code"]);
|
|
18684
19011
|
for (const key of Object.keys(expected))
|
|
18685
19012
|
if (error[key] !== expected[key])
|
|
18686
|
-
|
|
19013
|
+
invalid3();
|
|
18687
19014
|
} else if (value["error"] !== null)
|
|
18688
|
-
|
|
19015
|
+
invalid3();
|
|
18689
19016
|
const archive = value["archive"];
|
|
18690
19017
|
if (archive === null) {
|
|
18691
19018
|
if (value["state"] === "succeeded")
|
|
18692
|
-
|
|
19019
|
+
invalid3();
|
|
18693
19020
|
return;
|
|
18694
19021
|
}
|
|
18695
19022
|
if (value["state"] !== "succeeded" || !record4(archive) || Object.keys(archive).sort().join(",") !== "bytes,captured_at,download_expires_at,download_url,expires_at,sha256,sql_files" || !Number.isSafeInteger(archive["bytes"]) || Number(archive["bytes"]) < 1 || Number(archive["bytes"]) > 536870912 || typeof archive["sha256"] !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(archive["sha256"]) || !['["dev.sql"]', '["prod.sql"]', '["dev.sql","prod.sql"]', '["shared.sql"]'].includes(JSON.stringify(archive["sql_files"])) || !timestamp3(archive["captured_at"]) || !timestamp3(archive["expires_at"]))
|
|
18696
|
-
|
|
19023
|
+
invalid3();
|
|
18697
19024
|
if (archive["download_url"] === null) {
|
|
18698
19025
|
if (archive["download_expires_at"] !== null)
|
|
18699
|
-
|
|
19026
|
+
invalid3();
|
|
18700
19027
|
return;
|
|
18701
19028
|
}
|
|
18702
19029
|
if (typeof archive["download_url"] !== "string" || !timestamp3(archive["download_expires_at"]) || Date.parse(archive["download_expires_at"]) > Date.parse(archive["expires_at"]))
|
|
18703
|
-
|
|
19030
|
+
invalid3();
|
|
18704
19031
|
let url;
|
|
18705
19032
|
try {
|
|
18706
19033
|
url = new URL(archive["download_url"]);
|
|
18707
19034
|
} catch {
|
|
18708
|
-
|
|
19035
|
+
invalid3();
|
|
18709
19036
|
}
|
|
18710
19037
|
if (url.protocol !== "https:" || url.username || url.password || url.hash)
|
|
18711
|
-
|
|
19038
|
+
invalid3();
|
|
18712
19039
|
}
|
|
18713
19040
|
var id = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
|
|
18714
19041
|
function record4(value) {
|
|
@@ -18717,7 +19044,7 @@ function record4(value) {
|
|
|
18717
19044
|
function timestamp3(value) {
|
|
18718
19045
|
return typeof value === "string" && Number.isFinite(Date.parse(value)) && new Date(value).toISOString() === value;
|
|
18719
19046
|
}
|
|
18720
|
-
function
|
|
19047
|
+
function invalid3() {
|
|
18721
19048
|
throw new TypeError("Project export response is invalid");
|
|
18722
19049
|
}
|
|
18723
19050
|
|
|
@@ -18908,7 +19235,7 @@ var HELP_USAGE = Object.freeze({
|
|
|
18908
19235
|
"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)",
|
|
18909
19236
|
"export get": "ohmyhost export get EXPORT_ULID --project ULID --json (poll the original job; signed ZIP download lasts 24 hours)",
|
|
18910
19237
|
"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
|
|
19238
|
+
"referral link": "ohmyhost referral link --organization ULID --json (the workspace's link to share; a new user whose first workspace comes from it starts with 30 days of Paid and 1,000 credits, and its first payment gives this workspace 1,000 credits plus 30 days of Paid unless a subscription or an unbounded grant already covers it)",
|
|
18912
19239
|
"credits balance": "ohmyhost credits balance --organization ULID --json",
|
|
18913
19240
|
"billing recharge get": "ohmyhost billing recharge get --organization ULID --json",
|
|
18914
19241
|
"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)",
|
|
@@ -18928,7 +19255,9 @@ var HELP_USAGE = Object.freeze({
|
|
|
18928
19255
|
"token revoke": "ohmyhost token revoke --organization ULID --key KEY_ID --yes --json",
|
|
18929
19256
|
"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)",
|
|
18930
19257
|
"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",
|
|
18931
|
-
"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)",
|
|
19258
|
+
"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 (data mode is optional and defaults to shared; later assignments use project data plan/change without copying data; the region is chosen once: us is the default, eu places the database, files and builds in the EU; it cannot be changed later)",
|
|
19259
|
+
"project data plan": "ohmyhost project data plan --project ULID --change isolate_prod_keeps_data|isolate_dev_keeps_data|share_prod_keeps_data|reset_dev --json (shared is the optional creation default; review which data is kept or deleted; no data is copied)",
|
|
19260
|
+
"project data change": "ohmyhost project data change --project ULID --change isolate_prod_keeps_data|isolate_dev_keeps_data|share_prod_keeps_data|reset_dev --if-match ETAG --confirmation-token TOKEN --idempotency-key KEY --yes [--wait] --json (Owner only; use the reviewed plan's guards and the same key after uncertainty; no data is copied)",
|
|
18932
19261
|
"project list": "ohmyhost project list [--cursor ULID] [--limit LIMIT] --json",
|
|
18933
19262
|
"project context": "ohmyhost project context --project ULID --json",
|
|
18934
19263
|
"project notes set": "ohmyhost project notes set --project ULID --version NUMBER --markdown TEXT --idempotency-key KEY --json (no credentials or signed URLs)",
|
|
@@ -18939,7 +19268,7 @@ var HELP_USAGE = Object.freeze({
|
|
|
18939
19268
|
"project dev-share revoke": "ohmyhost project dev-share revoke --project ULID --idempotency-key KEY --yes --json",
|
|
18940
19269
|
"project dev-access mode": "ohmyhost project dev-access mode --project ULID --mode protected|public --idempotency-key KEY --yes --json",
|
|
18941
19270
|
"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,
|
|
19271
|
+
"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, this project's custom domain uses no domain credits and also works on Free, and each Paid period bought through Stripe adds 250 credits to the workspace, however many projects show the flag)",
|
|
18943
19272
|
"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)",
|
|
18944
19273
|
"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)",
|
|
18945
19274
|
"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.",
|
|
@@ -18994,6 +19323,30 @@ var cliMetadataResult = (kind, topic) => kind === "help" ? success("help", {
|
|
|
18994
19323
|
environment: "OHMYHOST_ENVIRONMENT=development|production (default: production); OHMYHOST_PROFILE=NAME binds this process to one saved login",
|
|
18995
19324
|
commands: HELP_COMMANDS.filter((entry) => !topic || entry.command === topic)
|
|
18996
19325
|
}) : success("version", { status: "succeeded", cli_version: CLI_VERSION });
|
|
19326
|
+
var invalidCommandAction = (argv, commandPrefix) => {
|
|
19327
|
+
const label = recognizedCommandLabel(argv);
|
|
19328
|
+
if (label === "secret set" || label === "secret delete") {
|
|
19329
|
+
const action = label === "secret set" ? "set" : "delete";
|
|
19330
|
+
const at = argv.findIndex((value, index) => value === "secret" && argv[index + 1] === action);
|
|
19331
|
+
const name = at < 0 ? void 0 : argv[at + 2];
|
|
19332
|
+
if (name !== void 0 && !isUserSecretName(name, action))
|
|
19333
|
+
return action === "set" ? "Choose a customer secret name of at most 128 uppercase letters, digits and underscores, starting with a letter. OHMYHOST_* and DATABASE_URL, HYPERDRIVE, BETTER_AUTH_SECRET, BETTER_AUTH_URL, ASSETS, FILES, IMAGES and STORAGE are reserved for the platform. Rename the application's own variable and its source references; platform bindings are installed automatically. Nothing was sent; changing only the idempotency key will not help." : "Choose an existing customer secret name of at most 128 uppercase letters, digits and underscores, starting with a letter. The installed platform keys OHMYHOST_STORAGE_KEY, OHMYHOST_MAIL_KEY and BETTER_AUTH_SECRET cannot be deleted. Older reserved-name collisions can be removed with secret delete; deployed platform keys stay managed by ohmyho.st. Nothing was sent; changing only the idempotency key will not help.";
|
|
19334
|
+
}
|
|
19335
|
+
const usage = Object.hasOwn(HELP_USAGE, label) ? HELP_USAGE[label].split("\n")[0] : void 0;
|
|
19336
|
+
if (usage === void 0)
|
|
19337
|
+
return "Unknown command; nothing was sent. 'ohmyhost --help --json' lists every command with its flags. If a documented command is missing, this CLI is older than https://ohmyho.st/client-release.json: install the published version.";
|
|
19338
|
+
const linkable = /(?:^|\s)--project ULID/u.test(usage) && !argv.includes("--project");
|
|
19339
|
+
return `The arguments of '${label}' are invalid or incomplete; nothing was sent. Usage: ${usage}.${linkable ? ` --project may be omitted only inside a directory linked with '${commandPrefix} link' for this OHMYHOST_ENVIRONMENT; otherwise pass --project ULID.` : ""}`;
|
|
19340
|
+
};
|
|
19341
|
+
var INTERNAL_ERROR_ACTION = "The ohmyhost client stopped on an unexpected error; the stderr line starting with [ohmyhost: identifies it. Compare 'ohmyhost --version' with https://ohmyho.st/client-release.json and install the published client if it is older, then run the same command again with the same --idempotency-key. If the current client fails the same way, report it with feedback submit (kind bug, error code internal_error) and quote that stderr line.";
|
|
19342
|
+
var CREDENTIAL_STORE_UNAVAILABLE_ACTION = "This computer's credential store (for example macOS Keychain or Windows Credential Manager) or the saved-login list in ~/.ohmyhost/profiles could not be read or written; nothing was changed. If the agent runs in a sandbox, run the command with access to them before asking the customer to sign in again. If the store is locked, ask the customer to unlock it, then run the command again. On a server, container or CI runner without a credential store, do not run login: load OHMYHOST_TOKEN from a private env file with a token from the portal's API Tokens page.";
|
|
19343
|
+
var RESPONSE_CONTRACT_INVALID_ACTION = "The ohmyho.st API answered in a form this client version does not accept, usually because the client is older than the service. Compare 'ohmyhost --version' with https://ohmyho.st/client-release.json; if it is older, install the published CLI and MCP and reload MCP. Then run the same command again with the same --idempotency-key: the change may already have been applied. If the current version answers this too, report it with feedback submit (kind bug, error code response_contract_invalid).";
|
|
19344
|
+
var REQUEST_TIMEOUT_ACTION = "The ohmyho.st API did not answer within 30 seconds. Run the same command again with the same --idempotency-key; the change may already be under way, and a new key could apply it twice.";
|
|
19345
|
+
var LOGOUT_UNCONFIRMED_ACTION = "The saved login was removed from this computer, but ohmyho.st did not confirm that its session ended. Running logout again cannot retry this, because the login is gone; do not sign in again just to revoke it. Tell the customer the session end was not confirmed.";
|
|
19346
|
+
var STATEMENT_FILE_INVALID_ACTION = "Put one INSERT, UPDATE or DELETE statement that starts with that keyword (no leading comment or WITH), UTF-8, at most 64 KiB, in the file named by --statement-file, then rerun with the same --idempotency-key.";
|
|
19347
|
+
var signInCommand = (commandPrefix, profile) => profile === void 0 ? `${commandPrefix} login --json` : `${commandPrefix} login${profile.organizationId === null ? "" : ` --organization ${profile.organizationId}`} --user ${profile.userId} --profile-name ${profile.name} --json`;
|
|
19348
|
+
var endedLoginAction = (commandPrefix, profile) => `The sign-in of ${profile === void 0 ? "this computer's saved login" : `saved login ${profile.name} (user ${profile.userId}${profile.organizationId === null ? "" : `, organization ${profile.organizationId}`})`} ended and it was removed. Run '${signInCommand(commandPrefix, profile)}' to sign it in again.`;
|
|
19349
|
+
var refreshFailedAction = (commandPrefix, profile) => `Renewing the saved login${profile === void 0 ? "" : ` ${profile.name}`} at the sign-in service failed. Run the same command again in a minute, with the same --idempotency-key if it has one. If it fails again, run '${signInCommand(commandPrefix, profile)}' and send the customer the link and code.`;
|
|
18997
19350
|
var ProductCliFailure = class extends Error {
|
|
18998
19351
|
exitCode;
|
|
18999
19352
|
code;
|
|
@@ -19024,14 +19377,14 @@ var ProductCli = class {
|
|
|
19024
19377
|
command = parseProductCliCommand(argv);
|
|
19025
19378
|
} catch (error) {
|
|
19026
19379
|
if (error instanceof InvalidCommandError) {
|
|
19027
|
-
return failure(recognizedCommandLabel(argv), 2, "invalid_command", false,
|
|
19380
|
+
return failure(recognizedCommandLabel(argv), 2, "invalid_command", false, invalidCommandAction(argv, this.dependencies.commandPrefix));
|
|
19028
19381
|
}
|
|
19029
19382
|
this.recordDiagnostic({
|
|
19030
19383
|
category: "internal",
|
|
19031
19384
|
subsystem: "parser",
|
|
19032
19385
|
errorKind: "unexpected"
|
|
19033
19386
|
});
|
|
19034
|
-
return failure(
|
|
19387
|
+
return failure(recognizedCommandLabel(argv), 1, "internal_error", false, INTERNAL_ERROR_ACTION);
|
|
19035
19388
|
}
|
|
19036
19389
|
return this.runCommand(command, signal);
|
|
19037
19390
|
}
|
|
@@ -19251,6 +19604,10 @@ var ProductCli = class {
|
|
|
19251
19604
|
}
|
|
19252
19605
|
if (command.kind === "project-create")
|
|
19253
19606
|
return await this.createProject(command, signal);
|
|
19607
|
+
if (command.kind === "project-data-plan")
|
|
19608
|
+
return await this.planProjectData(command, signal);
|
|
19609
|
+
if (command.kind === "project-data-change")
|
|
19610
|
+
return await this.changeProjectData(command, signal);
|
|
19254
19611
|
if (command.kind === "project-list")
|
|
19255
19612
|
return await this.listProjects(command, signal);
|
|
19256
19613
|
if (command.kind === "project-handle-check")
|
|
@@ -19299,25 +19656,15 @@ var ProductCli = class {
|
|
|
19299
19656
|
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 });
|
|
19300
19657
|
}
|
|
19301
19658
|
if (command.kind === "database-write") {
|
|
19302
|
-
const
|
|
19303
|
-
let statement;
|
|
19659
|
+
const statement = await readStatementFile(command.statementFile);
|
|
19304
19660
|
try {
|
|
19305
|
-
|
|
19306
|
-
|
|
19307
|
-
|
|
19308
|
-
|
|
19309
|
-
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
break;
|
|
19313
|
-
length += read.bytesRead;
|
|
19314
|
-
}
|
|
19315
|
-
if (length > 65536)
|
|
19316
|
-
throw new InvalidCommandError();
|
|
19317
|
-
statement = new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, length));
|
|
19318
|
-
} finally {
|
|
19319
|
-
buffer.fill(0);
|
|
19320
|
-
await file.close();
|
|
19661
|
+
parseProjectDatabaseWrite({
|
|
19662
|
+
environment: command.environment,
|
|
19663
|
+
statement,
|
|
19664
|
+
parameters: []
|
|
19665
|
+
});
|
|
19666
|
+
} catch {
|
|
19667
|
+
throw new ProductCliFailure(2, "statement_file_invalid", false, STATEMENT_FILE_INVALID_ACTION);
|
|
19321
19668
|
}
|
|
19322
19669
|
const body = parseProjectDatabaseWrite({
|
|
19323
19670
|
environment: command.environment,
|
|
@@ -19544,8 +19891,13 @@ var ProductCli = class {
|
|
|
19544
19891
|
expiresAt
|
|
19545
19892
|
};
|
|
19546
19893
|
const account = await this.dependencies.accountProfile(async () => signedIn.accessToken);
|
|
19894
|
+
const loginFlags = {
|
|
19895
|
+
organizationId,
|
|
19896
|
+
userId: expectedUserId,
|
|
19897
|
+
profileName: this.dependencies.profileName
|
|
19898
|
+
};
|
|
19547
19899
|
if (!isProfileUserId(account.userId))
|
|
19548
|
-
throw this.unverifiedLogin();
|
|
19900
|
+
throw this.unverifiedLogin(loginFlags);
|
|
19549
19901
|
if (expectedUserId !== void 0 && account.userId !== expectedUserId)
|
|
19550
19902
|
throw new ProductCliFailure(4, "login_account_mismatch", false, `The browser signed in as user ${account.userId}, not ${expectedUserId}. Nothing was saved and every saved login is unchanged. Sign out of ohmyho.st in that browser or use the browser profile of ${expectedUserId}, then run '${this.dependencies.commandPrefix} login${organizationId === void 0 ? "" : ` --organization ${organizationId}`} --user ${expectedUserId} --json' again.`);
|
|
19551
19903
|
const selection = chooseWorkspace(account.organizations, organizationId);
|
|
@@ -19556,12 +19908,12 @@ var ProductCli = class {
|
|
|
19556
19908
|
const scopedId = selection.outcome === "selected" ? selection.organization.id : parseCurrentIdentity(identity).organization_ids[0] ?? null;
|
|
19557
19909
|
const organization = scopedId === null ? null : account.organizations.find((candidate) => candidate.id === scopedId);
|
|
19558
19910
|
if (organization === void 0)
|
|
19559
|
-
throw this.unverifiedLogin();
|
|
19911
|
+
throw this.unverifiedLogin(loginFlags);
|
|
19560
19912
|
try {
|
|
19561
19913
|
assertVerifiedLogin(identity, account.userId, scopedId);
|
|
19562
19914
|
} catch (error) {
|
|
19563
19915
|
if (error instanceof AccountVerificationError)
|
|
19564
|
-
throw this.unverifiedLogin();
|
|
19916
|
+
throw this.unverifiedLogin(loginFlags);
|
|
19565
19917
|
throw error;
|
|
19566
19918
|
}
|
|
19567
19919
|
const login = {
|
|
@@ -19605,8 +19957,8 @@ var ProductCli = class {
|
|
|
19605
19957
|
}
|
|
19606
19958
|
}
|
|
19607
19959
|
/** The provider answered with another account than the one this login requested. */
|
|
19608
|
-
unverifiedLogin() {
|
|
19609
|
-
return new ProductCliFailure(4, "login_verification_failed", true, `The signed-in account could not be verified; nothing was saved. Run '${this.dependencies.commandPrefix} login --json' again.`);
|
|
19960
|
+
unverifiedLogin(flags) {
|
|
19961
|
+
return new ProductCliFailure(4, "login_verification_failed", true, `The signed-in account could not be verified; nothing was saved. Run '${this.dependencies.commandPrefix} login${flags.organizationId === void 0 ? "" : ` --organization ${flags.organizationId}`}${flags.userId === void 0 ? "" : ` --user ${flags.userId}`}${flags.profileName === void 0 ? "" : ` --profile-name ${flags.profileName}`} --json' again with the same flags and send the customer the new link and code.`);
|
|
19610
19962
|
}
|
|
19611
19963
|
/**
|
|
19612
19964
|
* Bind the login of this run, which has no organization yet, to one of its user's organizations.
|
|
@@ -19627,7 +19979,11 @@ var ProductCli = class {
|
|
|
19627
19979
|
}, organization, bounded2, (accessToken) => this.dependencies.productApi(async () => accessToken).getCurrentIdentity()), signal, this.dependencies.lifecycleTimeoutMs);
|
|
19628
19980
|
} catch (error) {
|
|
19629
19981
|
if (error instanceof AccountVerificationError)
|
|
19630
|
-
throw this.unverifiedLogin(
|
|
19982
|
+
throw this.unverifiedLogin({
|
|
19983
|
+
organizationId: organization.id,
|
|
19984
|
+
userId: profile.userId,
|
|
19985
|
+
profileName: profile.name
|
|
19986
|
+
});
|
|
19631
19987
|
if (!(error instanceof ProfileOrganizationFixedError))
|
|
19632
19988
|
throw error;
|
|
19633
19989
|
throw new ProductCliFailure(2, "profile_organization_fixed", false, `Profile ${profile.name} stays signed in to organization ${String(profile.organizationId)}. Run '${this.dependencies.commandPrefix} login --organization ${organization.id} --user ${profile.userId} --json' to add a separate login for ${organization.id} as the same user; a saved login never switches organizations.`);
|
|
@@ -19663,10 +20019,10 @@ var ProductCli = class {
|
|
|
19663
20019
|
throw new ProductCliFailure(9, "blocked_prerequisite", false, "Remote token revocation is not available in this release.");
|
|
19664
20020
|
}
|
|
19665
20021
|
if (isTimeout(revocationError)) {
|
|
19666
|
-
throw new ProductCliFailure(8, "request_timeout",
|
|
20022
|
+
throw new ProductCliFailure(8, "request_timeout", false, LOGOUT_UNCONFIRMED_ACTION);
|
|
19667
20023
|
}
|
|
19668
20024
|
if (revocationError instanceof LifecycleTransportError) {
|
|
19669
|
-
throw new ProductCliFailure(6, "token_revocation_failed",
|
|
20025
|
+
throw new ProductCliFailure(6, "token_revocation_failed", false, LOGOUT_UNCONFIRMED_ACTION);
|
|
19670
20026
|
}
|
|
19671
20027
|
throw revocationError;
|
|
19672
20028
|
}
|
|
@@ -19707,6 +20063,29 @@ var ProductCli = class {
|
|
|
19707
20063
|
}));
|
|
19708
20064
|
return success("operation reconcile", { status: "accepted", reconciliation });
|
|
19709
20065
|
}
|
|
20066
|
+
async planProjectData(command, signal) {
|
|
20067
|
+
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
20068
|
+
return success("project data plan", {
|
|
20069
|
+
status: "planned",
|
|
20070
|
+
plan: parseProjectDataChangePlan2(await api.planProjectDataChange({ projectId: command.projectId, change: command.change }))
|
|
20071
|
+
});
|
|
20072
|
+
}
|
|
20073
|
+
async changeProjectData(command, signal) {
|
|
20074
|
+
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
20075
|
+
let operation = parseOperation(await api.createProjectDataChange({
|
|
20076
|
+
projectId: command.projectId,
|
|
20077
|
+
change: command.change,
|
|
20078
|
+
ifMatch: command.ifMatch,
|
|
20079
|
+
confirmationToken: command.confirmationToken,
|
|
20080
|
+
idempotencyKey: command.idempotencyKey
|
|
20081
|
+
}));
|
|
20082
|
+
if (command.wait)
|
|
20083
|
+
operation = await this.waitForOperation(api, operation, signal);
|
|
20084
|
+
return success("project data change", {
|
|
20085
|
+
status: command.wait ? "completed" : "accepted",
|
|
20086
|
+
operation
|
|
20087
|
+
});
|
|
20088
|
+
}
|
|
19710
20089
|
async createProject(command, signal) {
|
|
19711
20090
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
19712
20091
|
const operation = parseOperation(await api.createProject({
|
|
@@ -19960,11 +20339,20 @@ var ProductCli = class {
|
|
|
19960
20339
|
commitSha: command.commitSha,
|
|
19961
20340
|
environment: command.environment
|
|
19962
20341
|
}));
|
|
19963
|
-
let
|
|
19964
|
-
|
|
19965
|
-
|
|
19966
|
-
|
|
19967
|
-
|
|
20342
|
+
let accepted;
|
|
20343
|
+
try {
|
|
20344
|
+
accepted = await api.createDeployment({
|
|
20345
|
+
projectId: command.projectId,
|
|
20346
|
+
planId: plan === null ? String(command.planId) : String(plan["id"]),
|
|
20347
|
+
idempotencyKey: command.idempotencyKey
|
|
20348
|
+
});
|
|
20349
|
+
} catch (error) {
|
|
20350
|
+
const reused = plan === null ? void 0 : reusedKeyProblem(error);
|
|
20351
|
+
if (reused === void 0)
|
|
20352
|
+
throw error;
|
|
20353
|
+
return failure("deploy", 7, reused.code, false, `This key already started a deployment from an earlier run of this command; a new key would build the same commit again. Run 'ohmyhost project status --project ${command.projectId} --json' and follow latest_operation with 'ohmyhost operation get OPERATION_ULID --json'.`, void 0, void 0, reused.requestId, reused.docsUrl, void 0, reused.message);
|
|
20354
|
+
}
|
|
20355
|
+
let operation = parseOperation(accepted);
|
|
19968
20356
|
if (command.wait)
|
|
19969
20357
|
operation = await this.waitForOperation(api, operation, signal);
|
|
19970
20358
|
const notes = operation.state === "succeeded" ? [
|
|
@@ -19979,27 +20367,40 @@ var ProductCli = class {
|
|
|
19979
20367
|
}
|
|
19980
20368
|
async logs(command, signal) {
|
|
19981
20369
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
20370
|
+
let operation = parseOperation(await api.getOperation(command.operationId));
|
|
19982
20371
|
let eventCount = 0;
|
|
19983
|
-
|
|
19984
|
-
|
|
19985
|
-
|
|
20372
|
+
let lastEventId;
|
|
20373
|
+
for (let connection = 0; connection < OPERATION_STREAM_CONNECTIONS; connection += 1) {
|
|
20374
|
+
if (connection > 0)
|
|
20375
|
+
await this.dependencies.waitUntil(new Date(this.dependencies.now().getTime() + 1e3), signal);
|
|
20376
|
+
const events = lastEventId === void 0 ? await api.streamOperationEvents(command.operationId, signal) : await api.streamOperationEvents(command.operationId, signal, lastEventId);
|
|
20377
|
+
for await (const event of events) {
|
|
20378
|
+
const parsed = parseOperationEvent(event);
|
|
20379
|
+
this.dependencies.operationEventSink?.write(`${JSON.stringify(parsed)}
|
|
19986
20380
|
`);
|
|
19987
|
-
|
|
19988
|
-
|
|
19989
|
-
|
|
20381
|
+
eventCount += 1;
|
|
20382
|
+
lastEventId = String(parsed["event_id"]);
|
|
20383
|
+
if (parsed["type"] === "OperationSucceeded" || parsed["type"] === "OperationFailed" || parsed["type"] === "OperationCancelled")
|
|
20384
|
+
return success("logs", { status: "completed", event_count: eventCount });
|
|
19990
20385
|
}
|
|
20386
|
+
throwIfAborted(signal);
|
|
20387
|
+
operation = parseOperation(await api.getOperation(command.operationId));
|
|
20388
|
+
if (operation.state !== "queued" && operation.state !== "running")
|
|
20389
|
+
return success("logs", { status: "completed", event_count: eventCount, operation });
|
|
19991
20390
|
}
|
|
19992
|
-
|
|
20391
|
+
throw new ProductCliFailure(8, "operation_stream_ended", true, `The event stream ended before operation ${command.operationId} finished, and it is still running; nothing was cancelled. Poll 'ohmyhost operation get ${command.operationId} --json' about every 60 seconds until it is succeeded, failed or cancelled.`, operation);
|
|
19993
20392
|
}
|
|
19994
20393
|
async deploymentLogs(command, signal) {
|
|
19995
20394
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
19996
|
-
|
|
19997
|
-
|
|
19998
|
-
|
|
20395
|
+
const page = await api.getDeploymentLogs({
|
|
20396
|
+
projectId: command.projectId,
|
|
20397
|
+
deploymentId: command.deploymentId,
|
|
20398
|
+
limit: 100
|
|
20399
|
+
});
|
|
20400
|
+
for (const item of page.items)
|
|
20401
|
+
this.dependencies.operationEventSink?.write(`${JSON.stringify(item)}
|
|
19999
20402
|
`);
|
|
20000
|
-
|
|
20001
|
-
}
|
|
20002
|
-
return success("deployment logs", { status: "completed", event_count: count });
|
|
20403
|
+
return success("deployment logs", { status: "completed", event_count: page.items.length });
|
|
20003
20404
|
}
|
|
20004
20405
|
async planRollback(command, signal) {
|
|
20005
20406
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
@@ -20077,7 +20478,7 @@ var ProductCli = class {
|
|
|
20077
20478
|
}));
|
|
20078
20479
|
const completed = command.wait ? await this.waitForSecretDelivery(api, command.projectId, command.environmentId, secret, signal) : secret;
|
|
20079
20480
|
return success("secret set", {
|
|
20080
|
-
status: command.wait ? "ready" : "stored",
|
|
20481
|
+
status: command.wait && completed.delivery_state === "ready" ? "ready" : "stored",
|
|
20081
20482
|
secret: completed
|
|
20082
20483
|
});
|
|
20083
20484
|
} finally {
|
|
@@ -20094,7 +20495,7 @@ var ProductCli = class {
|
|
|
20094
20495
|
}));
|
|
20095
20496
|
const delivery = command.wait ? await this.waitForSecretDelivery(api, command.projectId, command.environmentId, { desired_generation: deleted.desired_generation }, signal) : deleted;
|
|
20096
20497
|
return success("secret delete", {
|
|
20097
|
-
status: command.wait ? "ready" : deleted.status,
|
|
20498
|
+
status: command.wait && delivery.delivery_state === "ready" ? "ready" : deleted.status,
|
|
20098
20499
|
secret: { name: deleted.name, ...secretDeliveryProjection(delivery) }
|
|
20099
20500
|
});
|
|
20100
20501
|
}
|
|
@@ -20103,9 +20504,9 @@ var ProductCli = class {
|
|
|
20103
20504
|
for (let attempt = 0; attempt < 120; attempt += 1) {
|
|
20104
20505
|
const page = parseEnvironmentSecretPage(await api.listEnvironmentSecrets({ projectId, environmentId }));
|
|
20105
20506
|
if (page.last_error !== null || page.delivery_state === "failed") {
|
|
20106
|
-
throw new ProductCliFailure(10, "secret_delivery_failed", true,
|
|
20507
|
+
throw new ProductCliFailure(10, "secret_delivery_failed", true, `The secret is saved, but delivering it to the running app failed (last_error in 'ohmyhost secret list --project ${projectId} --environment ${environmentId} --json'). Do not set the value again: ohmyho.st retries delivery automatically, and the next deployment of this environment also delivers it. Read secret list again in a few minutes; if delivery_state stays failed, report last_error with feedback submit (kind bug, error code secret_delivery_failed).`);
|
|
20107
20508
|
}
|
|
20108
|
-
if (page.delivery_state === "ready" && page.desired_generation >= desiredGeneration && page.applied_generation >= desiredGeneration) {
|
|
20509
|
+
if (page.delivery_state === "not_deployed" || page.delivery_state === "ready" && page.desired_generation >= desiredGeneration && page.applied_generation >= desiredGeneration) {
|
|
20109
20510
|
if (!("name" in stored))
|
|
20110
20511
|
return page;
|
|
20111
20512
|
return page.items.find((secret) => secret.name === stored.name) ?? {
|
|
@@ -20117,7 +20518,7 @@ var ProductCli = class {
|
|
|
20117
20518
|
}
|
|
20118
20519
|
await this.dependencies.waitUntil(new Date(this.dependencies.now().getTime() + 1e3), signal);
|
|
20119
20520
|
}
|
|
20120
|
-
throw new ProductCliFailure(8, "secret_delivery_wait_timeout", true,
|
|
20521
|
+
throw new ProductCliFailure(8, "secret_delivery_wait_timeout", true, `The secret is saved, but it had not reached the running app after about 2 minutes. Read 'ohmyhost secret list --project ${projectId} --environment ${environmentId} --json': pending means delivery is still under way, and not_deployed means it arrives with the first deployment. Do not set the value again.`);
|
|
20121
20522
|
}
|
|
20122
20523
|
async waitForOperation(api, initial, signal) {
|
|
20123
20524
|
let operation = initial;
|
|
@@ -20128,7 +20529,7 @@ var ProductCli = class {
|
|
|
20128
20529
|
if (operation.reconciliation?.state === "required")
|
|
20129
20530
|
throw new ProductCliFailure(10, "operation_reconciliation_required", false, `${operation.reconciliation.suggested_action} Inspect 'ohmyhost operation get ${operation.id} --json' and use 'ohmyhost operation reconcile' after confirmation.`, operation);
|
|
20130
20531
|
if (operation.state === "queued" || operation.state === "running")
|
|
20131
|
-
throw new ProductCliFailure(8, "operation_wait_timeout", true, `
|
|
20532
|
+
throw new ProductCliFailure(8, "operation_wait_timeout", true, `The operation is still running; this command stopped waiting after about 2 minutes and cancelled nothing. Poll 'ohmyhost operation get ${operation.id} --json' about every 60 seconds until it is succeeded, failed or cancelled. Do not submit the same change again; a new key starts a second operation.`, operation);
|
|
20132
20533
|
if (operation.state === "failed" && operation.error !== void 0)
|
|
20133
20534
|
throw new ProductCliFailure(10, operation.error.code, operation.error.retryable, operation.error.suggested_action, operation);
|
|
20134
20535
|
if (operation.state === "cancelled")
|
|
@@ -20153,8 +20554,9 @@ var ProductCli = class {
|
|
|
20153
20554
|
} catch (error) {
|
|
20154
20555
|
if (!(error instanceof TokenRefreshRejectedError))
|
|
20155
20556
|
throw error;
|
|
20557
|
+
const ended = this.#profile;
|
|
20156
20558
|
await this.forgetLogin(store);
|
|
20157
|
-
throw new ProductCliFailure(5, "authentication_required", false, this.
|
|
20559
|
+
throw new ProductCliFailure(5, "authentication_required", false, endedLoginAction(this.dependencies.commandPrefix, ended));
|
|
20158
20560
|
}
|
|
20159
20561
|
if (refresh.refreshed)
|
|
20160
20562
|
await store.replace(refresh.credential);
|
|
@@ -20187,10 +20589,10 @@ var ProductCli = class {
|
|
|
20187
20589
|
throw new ProductCliFailure(9, "blocked_prerequisite", false, "Token refresh is not available in this release; run 'ohmyhost login'.");
|
|
20188
20590
|
}
|
|
20189
20591
|
if (isTimeout(error)) {
|
|
20190
|
-
throw new ProductCliFailure(8, "request_timeout", true,
|
|
20592
|
+
throw new ProductCliFailure(8, "request_timeout", true, REQUEST_TIMEOUT_ACTION);
|
|
20191
20593
|
}
|
|
20192
20594
|
if (error instanceof LifecycleTransportError) {
|
|
20193
|
-
throw new ProductCliFailure(5, "token_refresh_failed", true,
|
|
20595
|
+
throw new ProductCliFailure(5, "token_refresh_failed", true, refreshFailedAction(this.dependencies.commandPrefix, this.#profile));
|
|
20194
20596
|
}
|
|
20195
20597
|
throw error;
|
|
20196
20598
|
}
|
|
@@ -20231,31 +20633,31 @@ var ACCOUNT_SENSITIVE_PROBLEMS = /* @__PURE__ */ new Set([
|
|
|
20231
20633
|
var classifyCliFailure = (command, error, signal) => classifyFailure(command, error, signal);
|
|
20232
20634
|
var classifyFailure = (command, error, signal, actingAs) => {
|
|
20233
20635
|
if (signal.aborted || isCancellation(error)) {
|
|
20234
|
-
return failure(command, 130, "command_cancelled", true, "
|
|
20636
|
+
return failure(command, 130, "command_cancelled", true, "The command was stopped by an interrupt or termination signal, for example the shell's command timeout, before it finished. Run it again with the same --idempotency-key; a change may already be under way. Give login and commands with --wait or --follow several minutes, or run them in the background.");
|
|
20235
20637
|
}
|
|
20236
20638
|
if (isTimeout(error)) {
|
|
20237
|
-
return failure(command, 8, "request_timeout", true,
|
|
20639
|
+
return failure(command, 8, "request_timeout", true, REQUEST_TIMEOUT_ACTION);
|
|
20238
20640
|
}
|
|
20239
20641
|
if (error instanceof ProductCliFailure) {
|
|
20240
20642
|
return failure(command, error.exitCode, error.code, error.retryable, error.suggestedAction, error.operation);
|
|
20241
20643
|
}
|
|
20242
20644
|
if (error instanceof SecretInputError) {
|
|
20243
|
-
return failure(command, 2, "secret_input_invalid", false, "Pipe a non-empty UTF-8 value of at most 5120 bytes through standard input.");
|
|
20645
|
+
return failure(command, 2, "secret_input_invalid", false, command === "export create" ? "Pipe the ZIP password through standard input: non-blank UTF-8 of at most 1024 bytes. A final line break becomes part of the password." : "Pipe a non-empty UTF-8 value of at most 5120 bytes through standard input.");
|
|
20244
20646
|
}
|
|
20245
20647
|
if (error instanceof WorkosContractError) {
|
|
20246
20648
|
if (error.code === "access_denied") {
|
|
20247
|
-
return failure(command, 4, "device_authorization_denied", false, "
|
|
20649
|
+
return failure(command, 4, "device_authorization_denied", false, "The customer declined the sign-in in the browser; nothing was saved. Ask the customer whether to sign in, then run the same login command again and send them the new link and code.");
|
|
20248
20650
|
}
|
|
20249
20651
|
if (error.code === "expired_token") {
|
|
20250
|
-
return failure(command, 4, "device_authorization_expired", false, "Run login again.");
|
|
20652
|
+
return failure(command, 4, "device_authorization_expired", false, "Nobody finished the sign-in within 30 minutes; nothing was saved. Run the same login command again and send the customer the new link and code at once.");
|
|
20251
20653
|
}
|
|
20252
|
-
return failure(command, 4, "device_authorization_failed", true, "Run login again.");
|
|
20654
|
+
return failure(command, 4, "device_authorization_failed", true, "The sign-in service answered unexpectedly; nothing was saved. Run the same login command again; if it fails twice more, ask the customer to try again later.");
|
|
20253
20655
|
}
|
|
20254
20656
|
if (error instanceof CredentialStoreUnavailableError) {
|
|
20255
|
-
return failure(command, 3, "credential_store_unavailable",
|
|
20657
|
+
return failure(command, 3, "credential_store_unavailable", false, CREDENTIAL_STORE_UNAVAILABLE_ACTION);
|
|
20256
20658
|
}
|
|
20257
20659
|
if (error instanceof ResponseContractError) {
|
|
20258
|
-
return failure(command, 7, "response_contract_invalid", false,
|
|
20660
|
+
return failure(command, 7, "response_contract_invalid", false, RESPONSE_CONTRACT_INVALID_ACTION);
|
|
20259
20661
|
}
|
|
20260
20662
|
if (error instanceof PublicResponseTooLargeError) {
|
|
20261
20663
|
return failure(command, 7, "response_too_large", false, "Retry only after the remote response is repaired.");
|
|
@@ -20263,18 +20665,61 @@ var classifyFailure = (command, error, signal, actingAs) => {
|
|
|
20263
20665
|
if (looksLikeProblem(error)) {
|
|
20264
20666
|
try {
|
|
20265
20667
|
const problem = parseSafeProblem(error);
|
|
20266
|
-
return failure(command, 7, problem.code, problem.retryable, problem.suggestedAction, void 0, problem.retryAfterSeconds, problem.requestId, problem.docsUrl, ACCOUNT_SENSITIVE_PROBLEMS.has(problem.code) ? actingAs : void 0);
|
|
20668
|
+
return failure(command, 7, problem.code, problem.retryable, problem.suggestedAction, void 0, problem.retryAfterSeconds, problem.requestId, problem.docsUrl, ACCOUNT_SENSITIVE_PROBLEMS.has(problem.code) ? actingAs : void 0, problem.message);
|
|
20267
20669
|
} catch {
|
|
20268
|
-
return failure(command, 7, "response_contract_invalid", false,
|
|
20670
|
+
return failure(command, 7, "response_contract_invalid", false, RESPONSE_CONTRACT_INVALID_ACTION);
|
|
20269
20671
|
}
|
|
20270
20672
|
}
|
|
20271
20673
|
if (looksLikeRemoteObject(error)) {
|
|
20272
|
-
return failure(command, 7, "response_contract_invalid", false,
|
|
20674
|
+
return failure(command, 7, "response_contract_invalid", false, RESPONSE_CONTRACT_INVALID_ACTION);
|
|
20273
20675
|
}
|
|
20274
20676
|
if (error instanceof NetworkTransportError) {
|
|
20275
|
-
return failure(command, 8, "network_error", true, "
|
|
20677
|
+
return failure(command, 8, "network_error", true, "The request did not reach the ohmyho.st API, or its answer was cut off. If the agent runs in a sandbox without network access, run the command with network access. Otherwise run the same command again with the same --idempotency-key; the change may already have been applied, and a new key could apply it twice.");
|
|
20678
|
+
}
|
|
20679
|
+
return failure(command, 1, "internal_error", false, INTERNAL_ERROR_ACTION);
|
|
20680
|
+
};
|
|
20681
|
+
var reusedKeyProblem = (error) => {
|
|
20682
|
+
if (!looksLikeProblem(error))
|
|
20683
|
+
return void 0;
|
|
20684
|
+
try {
|
|
20685
|
+
const problem = parseSafeProblem(error);
|
|
20686
|
+
return problem.code === "idempotency_key_reused" ? problem : void 0;
|
|
20687
|
+
} catch {
|
|
20688
|
+
return void 0;
|
|
20689
|
+
}
|
|
20690
|
+
};
|
|
20691
|
+
var OPERATION_STREAM_CONNECTIONS = 4;
|
|
20692
|
+
var readStatementFile = async (path) => {
|
|
20693
|
+
const invalid5 = () => new ProductCliFailure(2, "statement_file_invalid", false, STATEMENT_FILE_INVALID_ACTION);
|
|
20694
|
+
let file;
|
|
20695
|
+
try {
|
|
20696
|
+
file = await open3(path, "r");
|
|
20697
|
+
} catch {
|
|
20698
|
+
throw invalid5();
|
|
20699
|
+
}
|
|
20700
|
+
const buffer = Buffer.alloc(65537);
|
|
20701
|
+
try {
|
|
20702
|
+
const stat3 = await file.stat();
|
|
20703
|
+
if (!stat3.isFile() || stat3.size > 65536)
|
|
20704
|
+
throw invalid5();
|
|
20705
|
+
let length = 0;
|
|
20706
|
+
while (length < buffer.length) {
|
|
20707
|
+
const read = await file.read(buffer, length, buffer.length - length, null);
|
|
20708
|
+
if (read.bytesRead === 0)
|
|
20709
|
+
break;
|
|
20710
|
+
length += read.bytesRead;
|
|
20711
|
+
}
|
|
20712
|
+
if (length > 65536)
|
|
20713
|
+
throw invalid5();
|
|
20714
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, length));
|
|
20715
|
+
} catch (error) {
|
|
20716
|
+
if (error instanceof ProductCliFailure)
|
|
20717
|
+
throw error;
|
|
20718
|
+
throw invalid5();
|
|
20719
|
+
} finally {
|
|
20720
|
+
buffer.fill(0);
|
|
20721
|
+
await file.close();
|
|
20276
20722
|
}
|
|
20277
|
-
return failure(command, 1, "internal_error", false, "Retry the command.");
|
|
20278
20723
|
};
|
|
20279
20724
|
var psqlUnavailable = () => failure("database psql", 9, "psql_unavailable", false, "Install the PostgreSQL client (psql) and make it available on PATH, or run 'ohmyhost database access create' and connect with your own SQL client.");
|
|
20280
20725
|
var success = (command, body) => ({
|
|
@@ -20283,7 +20728,7 @@ var success = (command, body) => ({
|
|
|
20283
20728
|
`,
|
|
20284
20729
|
stderr: ""
|
|
20285
20730
|
});
|
|
20286
|
-
var failure = (command, exitCode, code, retryable, suggestedAction, operation, retryAfterSeconds, requestId, docsUrl, actingAs) => {
|
|
20731
|
+
var failure = (command, exitCode, code, retryable, suggestedAction, operation, retryAfterSeconds, requestId, docsUrl, actingAs, message) => {
|
|
20287
20732
|
const document = {
|
|
20288
20733
|
version: 1,
|
|
20289
20734
|
command,
|
|
@@ -20297,6 +20742,7 @@ var failure = (command, exitCode, code, retryable, suggestedAction, operation, r
|
|
|
20297
20742
|
...requestId === void 0 ? {} : { request_id: requestId },
|
|
20298
20743
|
...docsUrl === void 0 ? {} : { docs_url: docsUrl },
|
|
20299
20744
|
...operation?.error === void 0 ? {} : { message: operation.error.message },
|
|
20745
|
+
...message === void 0 ? {} : { message },
|
|
20300
20746
|
...actingAs === void 0 ? {} : { acting_as: actingAs }
|
|
20301
20747
|
}
|
|
20302
20748
|
};
|
|
@@ -20346,6 +20792,10 @@ var parsedCommandName = (command) => {
|
|
|
20346
20792
|
return "feedback status";
|
|
20347
20793
|
if (command.kind === "project-create")
|
|
20348
20794
|
return "project create";
|
|
20795
|
+
if (command.kind === "project-data-plan")
|
|
20796
|
+
return "project data plan";
|
|
20797
|
+
if (command.kind === "project-data-change")
|
|
20798
|
+
return "project data change";
|
|
20349
20799
|
if (command.kind === "organization-create")
|
|
20350
20800
|
return "organization create";
|
|
20351
20801
|
if (command.kind === "organization-list")
|
|
@@ -22459,24 +22909,6 @@ var getDeploymentLogs = (parameters, options) => {
|
|
|
22459
22909
|
...params
|
|
22460
22910
|
});
|
|
22461
22911
|
};
|
|
22462
|
-
var streamDeploymentLogs = (parameters, options) => {
|
|
22463
|
-
const params = buildClientParams([parameters], [
|
|
22464
|
-
{
|
|
22465
|
-
args: [
|
|
22466
|
-
{ in: "headers", key: "X-Request-Id" },
|
|
22467
|
-
{ in: "path", key: "project_id" },
|
|
22468
|
-
{ in: "path", key: "deployment_id" }
|
|
22469
|
-
]
|
|
22470
|
-
}
|
|
22471
|
-
]);
|
|
22472
|
-
return (options?.client ?? client).sse.get({
|
|
22473
|
-
responseStyle: "data",
|
|
22474
|
-
security: [{ scheme: "bearer", type: "http" }],
|
|
22475
|
-
url: "/v1/projects/{project_id}/deployments/{deployment_id}/logs/events",
|
|
22476
|
-
...options,
|
|
22477
|
-
...params
|
|
22478
|
-
});
|
|
22479
|
-
};
|
|
22480
22912
|
var getDatabaseCompute = (parameters, options) => {
|
|
22481
22913
|
const params = buildClientParams([parameters], [
|
|
22482
22914
|
{
|
|
@@ -23023,6 +23455,55 @@ var retryManagedMailMessage = (parameters, options) => {
|
|
|
23023
23455
|
...params
|
|
23024
23456
|
});
|
|
23025
23457
|
};
|
|
23458
|
+
var planProjectDataChange = (parameters, options) => {
|
|
23459
|
+
const params = buildClientParams([parameters], [
|
|
23460
|
+
{
|
|
23461
|
+
args: [
|
|
23462
|
+
{ in: "headers", key: "X-Request-Id" },
|
|
23463
|
+
{ in: "path", key: "project_id" },
|
|
23464
|
+
{ key: "projectDataChangeRequest", map: "body" }
|
|
23465
|
+
]
|
|
23466
|
+
}
|
|
23467
|
+
]);
|
|
23468
|
+
return (options?.client ?? client).post({
|
|
23469
|
+
responseStyle: "data",
|
|
23470
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
23471
|
+
url: "/v1/projects/{project_id}/data-changes:plan",
|
|
23472
|
+
...options,
|
|
23473
|
+
...params,
|
|
23474
|
+
headers: {
|
|
23475
|
+
"Content-Type": "application/json",
|
|
23476
|
+
...options?.headers,
|
|
23477
|
+
...params.headers
|
|
23478
|
+
}
|
|
23479
|
+
});
|
|
23480
|
+
};
|
|
23481
|
+
var createProjectDataChange = (parameters, options) => {
|
|
23482
|
+
const params = buildClientParams([parameters], [
|
|
23483
|
+
{
|
|
23484
|
+
args: [
|
|
23485
|
+
{ in: "headers", key: "X-Request-Id" },
|
|
23486
|
+
{ in: "headers", key: "Idempotency-Key" },
|
|
23487
|
+
{ in: "headers", key: "If-Match" },
|
|
23488
|
+
{ in: "headers", key: "X-Confirmation-Token" },
|
|
23489
|
+
{ in: "path", key: "project_id" },
|
|
23490
|
+
{ key: "projectDataChangeRequest", map: "body" }
|
|
23491
|
+
]
|
|
23492
|
+
}
|
|
23493
|
+
]);
|
|
23494
|
+
return (options?.client ?? client).post({
|
|
23495
|
+
responseStyle: "data",
|
|
23496
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
23497
|
+
url: "/v1/projects/{project_id}/data-changes",
|
|
23498
|
+
...options,
|
|
23499
|
+
...params,
|
|
23500
|
+
headers: {
|
|
23501
|
+
"Content-Type": "application/json",
|
|
23502
|
+
...options?.headers,
|
|
23503
|
+
...params.headers
|
|
23504
|
+
}
|
|
23505
|
+
});
|
|
23506
|
+
};
|
|
23026
23507
|
|
|
23027
23508
|
// packages/sdk-ts/dist/managed-mail-response.js
|
|
23028
23509
|
var ULID = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
|
|
@@ -23045,30 +23526,30 @@ function assertManagedMailResponse(value, input) {
|
|
|
23045
23526
|
id2(r["domain_id"]);
|
|
23046
23527
|
domain(r["domain"]);
|
|
23047
23528
|
if (r["environment_id"] !== input.environmentId)
|
|
23048
|
-
|
|
23529
|
+
invalid4();
|
|
23049
23530
|
for (const key of ["sending", "receiving"])
|
|
23050
23531
|
if (!["disabled", "pending", "ready", "failed"].includes(String(r[key])))
|
|
23051
|
-
|
|
23532
|
+
invalid4();
|
|
23052
23533
|
if (!["ready", "verification_pending"].includes(String(r["status"])))
|
|
23053
|
-
|
|
23534
|
+
invalid4();
|
|
23054
23535
|
time(r["configured_at"]);
|
|
23055
23536
|
if (r["observed_at"] !== null)
|
|
23056
23537
|
time(r["observed_at"]);
|
|
23057
23538
|
records(r["dns_records"]);
|
|
23058
23539
|
if (r["next_check_after_seconds"] !== null && r["next_check_after_seconds"] !== 60)
|
|
23059
|
-
|
|
23540
|
+
invalid4();
|
|
23060
23541
|
if (r["webhook"] !== null) {
|
|
23061
23542
|
const hook = object2(r["webhook"]);
|
|
23062
23543
|
exact3(hook, ["url", "verified"]);
|
|
23063
23544
|
https(hook["url"]);
|
|
23064
23545
|
if (typeof hook["verified"] !== "boolean")
|
|
23065
|
-
|
|
23546
|
+
invalid4();
|
|
23066
23547
|
}
|
|
23067
23548
|
if (r["dns"] !== void 0) {
|
|
23068
23549
|
const dns = object2(r["dns"]);
|
|
23069
23550
|
exact3(dns, ["status", "records"]);
|
|
23070
23551
|
if (!["configured", "action_required", "conflict"].includes(String(dns["status"])))
|
|
23071
|
-
|
|
23552
|
+
invalid4();
|
|
23072
23553
|
records(dns["records"]);
|
|
23073
23554
|
}
|
|
23074
23555
|
return;
|
|
@@ -23076,19 +23557,19 @@ function assertManagedMailResponse(value, input) {
|
|
|
23076
23557
|
if (input.action === "webhook_set") {
|
|
23077
23558
|
exact3(r, ["url", "signing_secret", "status"]);
|
|
23078
23559
|
if (r["url"] !== input.url || r["status"] !== "verification_required" || typeof r["signing_secret"] !== "string" || !/^whsec_[A-Za-z0-9+/]{43}=$/u.test(r["signing_secret"]))
|
|
23079
|
-
|
|
23560
|
+
invalid4();
|
|
23080
23561
|
return;
|
|
23081
23562
|
}
|
|
23082
23563
|
if (input.action === "webhook_disable") {
|
|
23083
23564
|
exact3(r, ["status"]);
|
|
23084
23565
|
if (r["status"] !== "disabled")
|
|
23085
|
-
|
|
23566
|
+
invalid4();
|
|
23086
23567
|
return;
|
|
23087
23568
|
}
|
|
23088
23569
|
if (input.action === "domain_delete") {
|
|
23089
23570
|
exact3(r, ["status", "domain", "dns_records"]);
|
|
23090
23571
|
if (r["status"] !== "deleted")
|
|
23091
|
-
|
|
23572
|
+
invalid4();
|
|
23092
23573
|
domain(r["domain"]);
|
|
23093
23574
|
records(r["dns_records"]);
|
|
23094
23575
|
return;
|
|
@@ -23096,25 +23577,25 @@ function assertManagedMailResponse(value, input) {
|
|
|
23096
23577
|
if (input.action === "messages_list") {
|
|
23097
23578
|
exact3(r, ["items"]);
|
|
23098
23579
|
if (!Array.isArray(r["items"]) || r["items"].length > 50)
|
|
23099
|
-
|
|
23580
|
+
invalid4();
|
|
23100
23581
|
for (const value2 of r["items"]) {
|
|
23101
23582
|
const item = object2(value2);
|
|
23102
23583
|
exact3(item, ["message_id", "received_at", "state", "attempts"]);
|
|
23103
23584
|
id2(item["message_id"]);
|
|
23104
23585
|
time(item["received_at"]);
|
|
23105
23586
|
if (!["pending", "delivering", "delivered", "delivery_failed", "expired"].includes(String(item["state"])) || !Number.isInteger(item["attempts"]) || Number(item["attempts"]) < 0 || Number(item["attempts"]) > 4)
|
|
23106
|
-
|
|
23587
|
+
invalid4();
|
|
23107
23588
|
}
|
|
23108
23589
|
return;
|
|
23109
23590
|
}
|
|
23110
23591
|
if (input.action === "message_retry") {
|
|
23111
23592
|
exact3(r, ["attempted"], ["delivered"]);
|
|
23112
23593
|
if (typeof r["attempted"] !== "boolean" || r["delivered"] !== void 0 && typeof r["delivered"] !== "boolean")
|
|
23113
|
-
|
|
23594
|
+
invalid4();
|
|
23114
23595
|
return;
|
|
23115
23596
|
}
|
|
23116
23597
|
if (input.action !== "message_get")
|
|
23117
|
-
|
|
23598
|
+
invalid4();
|
|
23118
23599
|
if (r["status"] === "expired") {
|
|
23119
23600
|
exact3(r, ["status"]);
|
|
23120
23601
|
return;
|
|
@@ -23133,65 +23614,65 @@ function assertManagedMailResponse(value, input) {
|
|
|
23133
23614
|
"attachments"
|
|
23134
23615
|
]);
|
|
23135
23616
|
if (r["id"] !== input.messageId)
|
|
23136
|
-
|
|
23617
|
+
invalid4();
|
|
23137
23618
|
domain(r["domain"]);
|
|
23138
23619
|
time(r["received_at"]);
|
|
23139
23620
|
time(r["expires_at"]);
|
|
23140
23621
|
if (Date.parse(String(r["expires_at"])) - Date.parse(String(r["received_at"])) !== 72 * 36e5)
|
|
23141
|
-
|
|
23622
|
+
invalid4();
|
|
23142
23623
|
for (const key of ["from", "subject", "message_id"])
|
|
23143
23624
|
if (typeof r[key] !== "string")
|
|
23144
|
-
|
|
23625
|
+
invalid4();
|
|
23145
23626
|
for (const key of ["text", "html"])
|
|
23146
23627
|
if (r[key] !== null && typeof r[key] !== "string")
|
|
23147
|
-
|
|
23628
|
+
invalid4();
|
|
23148
23629
|
if (!Array.isArray(r["to"]) || r["to"].some((v) => typeof v !== "string" || v.slice(v.lastIndexOf("@") + 1).toLowerCase() !== r["domain"]))
|
|
23149
|
-
|
|
23630
|
+
invalid4();
|
|
23150
23631
|
if (!Array.isArray(r["attachments"]))
|
|
23151
|
-
|
|
23632
|
+
invalid4();
|
|
23152
23633
|
for (const value2 of r["attachments"]) {
|
|
23153
23634
|
const a = object2(value2);
|
|
23154
23635
|
exact3(a, ["id", "filename", "content_type", "download_path"]);
|
|
23155
23636
|
if (typeof a["id"] !== "string" || !/^[A-Za-z0-9_-]{1,128}$/u.test(a["id"]) || a["filename"] !== null && typeof a["filename"] !== "string" || typeof a["content_type"] !== "string" || a["download_path"] !== `/v1/projects/${input.projectId}/environments/${input.environmentId}/mail/messages/${input.messageId}/attachments/${a["id"]}`)
|
|
23156
|
-
|
|
23637
|
+
invalid4();
|
|
23157
23638
|
}
|
|
23158
23639
|
}
|
|
23159
23640
|
function object2(v) {
|
|
23160
23641
|
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
23161
|
-
|
|
23642
|
+
invalid4();
|
|
23162
23643
|
return v;
|
|
23163
23644
|
}
|
|
23164
23645
|
function exact3(v, required, optional = []) {
|
|
23165
23646
|
if (required.some((k) => !(k in v)) || Object.keys(v).some((k) => !required.includes(k) && !optional.includes(k)))
|
|
23166
|
-
|
|
23647
|
+
invalid4();
|
|
23167
23648
|
}
|
|
23168
23649
|
function id2(v) {
|
|
23169
23650
|
if (typeof v !== "string" || !ULID.test(v))
|
|
23170
|
-
|
|
23651
|
+
invalid4();
|
|
23171
23652
|
}
|
|
23172
23653
|
function domain(v) {
|
|
23173
23654
|
if (typeof v !== "string" || !/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/u.test(v))
|
|
23174
|
-
|
|
23655
|
+
invalid4();
|
|
23175
23656
|
}
|
|
23176
23657
|
function time(v) {
|
|
23177
23658
|
if (typeof v !== "string" || !Number.isFinite(Date.parse(v)) || new Date(v).toISOString() !== v)
|
|
23178
|
-
|
|
23659
|
+
invalid4();
|
|
23179
23660
|
}
|
|
23180
23661
|
function https(v) {
|
|
23181
23662
|
if (typeof v !== "string" || !v.startsWith("https://"))
|
|
23182
|
-
|
|
23663
|
+
invalid4();
|
|
23183
23664
|
}
|
|
23184
23665
|
function records(v) {
|
|
23185
23666
|
if (!Array.isArray(v))
|
|
23186
|
-
|
|
23667
|
+
invalid4();
|
|
23187
23668
|
for (const value of v) {
|
|
23188
23669
|
const r = object2(value);
|
|
23189
23670
|
exact3(r, ["type", "name", "value", "purpose", "status"], ["priority"]);
|
|
23190
23671
|
if (!["TXT", "CNAME", "MX"].includes(String(r["type"])) || ["name", "value", "purpose", "status"].some((k) => typeof r[k] !== "string") || r["type"] === "MX" && (!Number.isInteger(r["priority"]) || Number(r["priority"]) < 0 || Number(r["priority"]) > 65535))
|
|
23191
|
-
|
|
23672
|
+
invalid4();
|
|
23192
23673
|
}
|
|
23193
23674
|
}
|
|
23194
|
-
function
|
|
23675
|
+
function invalid4() {
|
|
23195
23676
|
throw new TypeError("Mail response contract invalid");
|
|
23196
23677
|
}
|
|
23197
23678
|
|
|
@@ -23489,6 +23970,18 @@ var GeneratedSdkProductApi = class {
|
|
|
23489
23970
|
async getFeedback(feedbackId, cursor) {
|
|
23490
23971
|
return parseFeedbackStatus(await getFeedback({ feedback_id: feedbackId, ...cursor === void 0 ? {} : { cursor } }, { client: this.#client }));
|
|
23491
23972
|
}
|
|
23973
|
+
async planProjectDataChange(input) {
|
|
23974
|
+
return parseProjectDataChangePlan2(await planProjectDataChange({ project_id: input.projectId, projectDataChangeRequest: { change: input.change } }, { client: this.#client }));
|
|
23975
|
+
}
|
|
23976
|
+
async createProjectDataChange(input) {
|
|
23977
|
+
return parseOperation(await createProjectDataChange({
|
|
23978
|
+
project_id: input.projectId,
|
|
23979
|
+
projectDataChangeRequest: { change: input.change },
|
|
23980
|
+
"If-Match": input.ifMatch,
|
|
23981
|
+
"X-Confirmation-Token": input.confirmationToken,
|
|
23982
|
+
"Idempotency-Key": input.idempotencyKey
|
|
23983
|
+
}, { client: this.#client }));
|
|
23984
|
+
}
|
|
23492
23985
|
async createProject(input) {
|
|
23493
23986
|
return createProject({
|
|
23494
23987
|
"Idempotency-Key": input.idempotencyKey,
|
|
@@ -23569,8 +24062,17 @@ var GeneratedSdkProductApi = class {
|
|
|
23569
24062
|
async getDeployment(input) {
|
|
23570
24063
|
return getDeployment({ project_id: input.projectId, deployment_id: input.deploymentId }, { client: this.#client });
|
|
23571
24064
|
}
|
|
23572
|
-
async streamOperationEvents(operationId, signal) {
|
|
23573
|
-
return (await streamOperationEvents({ operation_id: operationId }, {
|
|
24065
|
+
async streamOperationEvents(operationId, signal, lastEventId) {
|
|
24066
|
+
return (await streamOperationEvents({ operation_id: operationId }, {
|
|
24067
|
+
client: this.#client,
|
|
24068
|
+
signal,
|
|
24069
|
+
// The generated client would otherwise retry a refused stream, such as a 404, forever.
|
|
24070
|
+
sseMaxRetryAttempts: 1,
|
|
24071
|
+
headers: {
|
|
24072
|
+
Accept: "text/event-stream",
|
|
24073
|
+
...lastEventId === void 0 ? {} : { "Last-Event-ID": lastEventId }
|
|
24074
|
+
}
|
|
24075
|
+
})).stream;
|
|
23574
24076
|
}
|
|
23575
24077
|
async getDeploymentLogs(input) {
|
|
23576
24078
|
return getDeploymentLogs({
|
|
@@ -23580,9 +24082,6 @@ var GeneratedSdkProductApi = class {
|
|
|
23580
24082
|
...input.cursor === void 0 ? {} : { cursor: input.cursor }
|
|
23581
24083
|
}, { client: this.#client });
|
|
23582
24084
|
}
|
|
23583
|
-
async streamDeploymentLogs(input, signal) {
|
|
23584
|
-
return (await streamDeploymentLogs({ project_id: input.projectId, deployment_id: input.deploymentId }, { client: this.#client, signal, headers: { Accept: "text/event-stream" } })).stream;
|
|
23585
|
-
}
|
|
23586
24085
|
async planRollback(input) {
|
|
23587
24086
|
return planDeploymentRollback({ project_id: input.projectId, target_deployment_id: input.deploymentId }, { client: this.#client });
|
|
23588
24087
|
}
|
|
@@ -23951,6 +24450,9 @@ var ohmyhost_schema_default = {
|
|
|
23951
24450
|
maxLength: 256,
|
|
23952
24451
|
pattern: "^/"
|
|
23953
24452
|
},
|
|
24453
|
+
browser: {
|
|
24454
|
+
$ref: "#/$defs/browser"
|
|
24455
|
+
},
|
|
23954
24456
|
egress: {
|
|
23955
24457
|
type: "object",
|
|
23956
24458
|
additionalProperties: false,
|
|
@@ -23958,7 +24460,7 @@ var ohmyhost_schema_default = {
|
|
|
23958
24460
|
properties: {
|
|
23959
24461
|
allow: {
|
|
23960
24462
|
type: "array",
|
|
23961
|
-
maxItems:
|
|
24463
|
+
maxItems: 13,
|
|
23962
24464
|
uniqueItems: true,
|
|
23963
24465
|
items: {
|
|
23964
24466
|
type: "string",
|
|
@@ -23971,6 +24473,56 @@ var ohmyhost_schema_default = {
|
|
|
23971
24473
|
}
|
|
23972
24474
|
}
|
|
23973
24475
|
},
|
|
24476
|
+
browserOrigins: {
|
|
24477
|
+
type: "array",
|
|
24478
|
+
maxItems: 16,
|
|
24479
|
+
uniqueItems: true,
|
|
24480
|
+
items: {
|
|
24481
|
+
type: "string",
|
|
24482
|
+
minLength: 1,
|
|
24483
|
+
maxLength: 256,
|
|
24484
|
+
pattern: "^https://"
|
|
24485
|
+
}
|
|
24486
|
+
},
|
|
24487
|
+
browser: {
|
|
24488
|
+
type: "object",
|
|
24489
|
+
additionalProperties: false,
|
|
24490
|
+
properties: {
|
|
24491
|
+
connect: {
|
|
24492
|
+
type: "array",
|
|
24493
|
+
maxItems: 16,
|
|
24494
|
+
uniqueItems: true,
|
|
24495
|
+
items: {
|
|
24496
|
+
type: "string",
|
|
24497
|
+
minLength: 1,
|
|
24498
|
+
maxLength: 256,
|
|
24499
|
+
pattern: "^(?:https|wss)://"
|
|
24500
|
+
}
|
|
24501
|
+
},
|
|
24502
|
+
scripts: { $ref: "#/$defs/browserOrigins" },
|
|
24503
|
+
styles: { $ref: "#/$defs/browserOrigins" },
|
|
24504
|
+
images: { $ref: "#/$defs/browserOrigins" },
|
|
24505
|
+
fonts: { $ref: "#/$defs/browserOrigins" },
|
|
24506
|
+
frames: { $ref: "#/$defs/browserOrigins" },
|
|
24507
|
+
frameAncestors: { $ref: "#/$defs/browserOrigins" },
|
|
24508
|
+
workers: {
|
|
24509
|
+
type: "object",
|
|
24510
|
+
additionalProperties: false,
|
|
24511
|
+
properties: {
|
|
24512
|
+
self: { type: "boolean" },
|
|
24513
|
+
blob: { type: "boolean" }
|
|
24514
|
+
}
|
|
24515
|
+
},
|
|
24516
|
+
cors: {
|
|
24517
|
+
type: "object",
|
|
24518
|
+
additionalProperties: false,
|
|
24519
|
+
properties: {
|
|
24520
|
+
origins: { $ref: "#/$defs/browserOrigins" },
|
|
24521
|
+
credentials: { type: "boolean" }
|
|
24522
|
+
}
|
|
24523
|
+
}
|
|
24524
|
+
}
|
|
24525
|
+
},
|
|
23974
24526
|
database: {
|
|
23975
24527
|
type: "object",
|
|
23976
24528
|
additionalProperties: false,
|
|
@@ -24228,6 +24780,74 @@ function validCronField(value, minimum, maximum) {
|
|
|
24228
24780
|
return parsed >= minimum && parsed <= maximum;
|
|
24229
24781
|
}
|
|
24230
24782
|
|
|
24783
|
+
// packages/contracts/dist/browser-capabilities.js
|
|
24784
|
+
var RESOURCE_GROUPS = [
|
|
24785
|
+
"connect",
|
|
24786
|
+
"scripts",
|
|
24787
|
+
"styles",
|
|
24788
|
+
"images",
|
|
24789
|
+
"fonts",
|
|
24790
|
+
"frames",
|
|
24791
|
+
"frameAncestors"
|
|
24792
|
+
];
|
|
24793
|
+
var POLICY_KEYS = [...RESOURCE_GROUPS, "workers", "cors"];
|
|
24794
|
+
var MAX_ORIGINS = 16;
|
|
24795
|
+
function normalizeBrowserPolicy(value) {
|
|
24796
|
+
const input = value === void 0 ? {} : policyRecord(value, POLICY_KEYS);
|
|
24797
|
+
const workers = input["workers"] === void 0 ? {} : policyRecord(input["workers"], ["self", "blob"]);
|
|
24798
|
+
const cors = input["cors"] === void 0 ? {} : policyRecord(input["cors"], ["origins", "credentials"]);
|
|
24799
|
+
const resources = Object.fromEntries(RESOURCE_GROUPS.map((group) => [group, normalizeOrigins(input[group], group === "connect")]));
|
|
24800
|
+
return Object.freeze({
|
|
24801
|
+
...resources,
|
|
24802
|
+
workers: Object.freeze({
|
|
24803
|
+
self: normalizeBoolean(workers["self"]),
|
|
24804
|
+
blob: normalizeBoolean(workers["blob"])
|
|
24805
|
+
}),
|
|
24806
|
+
cors: Object.freeze({
|
|
24807
|
+
origins: normalizeOrigins(cors["origins"], false),
|
|
24808
|
+
credentials: normalizeBoolean(cors["credentials"])
|
|
24809
|
+
})
|
|
24810
|
+
});
|
|
24811
|
+
}
|
|
24812
|
+
function policyRecord(value, keys, complete = false) {
|
|
24813
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)) || Object.keys(value).some((key) => !keys.includes(key)) || complete && Object.keys(value).length !== keys.length)
|
|
24814
|
+
throw invalidPolicy();
|
|
24815
|
+
return value;
|
|
24816
|
+
}
|
|
24817
|
+
function normalizeBoolean(value) {
|
|
24818
|
+
if (value === void 0)
|
|
24819
|
+
return false;
|
|
24820
|
+
if (typeof value !== "boolean")
|
|
24821
|
+
throw invalidPolicy();
|
|
24822
|
+
return value;
|
|
24823
|
+
}
|
|
24824
|
+
function normalizeOrigins(value, allowWebSocket) {
|
|
24825
|
+
if (value === void 0)
|
|
24826
|
+
return Object.freeze([]);
|
|
24827
|
+
if (!Array.isArray(value) || value.length > MAX_ORIGINS)
|
|
24828
|
+
throw invalidPolicy();
|
|
24829
|
+
const origins = [];
|
|
24830
|
+
for (const origin of value) {
|
|
24831
|
+
if (!browserOrigin(origin, allowWebSocket) || origins.includes(origin))
|
|
24832
|
+
throw invalidPolicy();
|
|
24833
|
+
origins.push(origin);
|
|
24834
|
+
}
|
|
24835
|
+
return Object.freeze(origins.sort());
|
|
24836
|
+
}
|
|
24837
|
+
function browserOrigin(value, allowWebSocket) {
|
|
24838
|
+
if (typeof value !== "string" || value.length > 256)
|
|
24839
|
+
return false;
|
|
24840
|
+
try {
|
|
24841
|
+
const url = new URL(value);
|
|
24842
|
+
return (url.protocol === "https:" || allowWebSocket && url.protocol === "wss:") && url.origin === value && url.username === "" && url.password === "" && url.pathname === "/" && url.search === "" && url.hash === "" && url.hostname.includes(".") && !url.hostname.endsWith(".local") && !url.hostname.endsWith(".internal") && !/^\d+(?:\.\d+){3}$/u.test(url.hostname) && url.hostname.split(".").every((label) => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/u.test(label));
|
|
24843
|
+
} catch {
|
|
24844
|
+
return false;
|
|
24845
|
+
}
|
|
24846
|
+
}
|
|
24847
|
+
function invalidPolicy() {
|
|
24848
|
+
return new TypeError("Invalid browser capability policy");
|
|
24849
|
+
}
|
|
24850
|
+
|
|
24231
24851
|
// packages/contracts/dist/index.js
|
|
24232
24852
|
var validateOhmyhostConfig = new import__.Ajv2020({
|
|
24233
24853
|
allErrors: true,
|
|
@@ -24252,7 +24872,8 @@ function parseOhmyhostConfig(value) {
|
|
|
24252
24872
|
healthcheck: normalizeHealthcheck(input.runtime.healthcheck),
|
|
24253
24873
|
egress: Object.freeze({
|
|
24254
24874
|
allow: normalizeEgressOrigins(input.runtime.egress?.allow)
|
|
24255
|
-
})
|
|
24875
|
+
}),
|
|
24876
|
+
...input.runtime.browser === void 0 ? {} : { browser: normalizeBrowserPolicy(input.runtime.browser) }
|
|
24256
24877
|
}),
|
|
24257
24878
|
...input.database === void 0 ? {} : {
|
|
24258
24879
|
database: Object.freeze({
|
|
@@ -24306,7 +24927,7 @@ function normalizeEgressOrigins(value) {
|
|
|
24306
24927
|
} catch {
|
|
24307
24928
|
throw new TypeError("ohmyhost.yaml runtime egress origin is invalid");
|
|
24308
24929
|
}
|
|
24309
|
-
if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || parsed.origin !== origin || parsed.pathname !== "/" || parsed.search !== "" || parsed.hash !== "" || forbiddenEgressHostname(parsed.hostname)) {
|
|
24930
|
+
if (parsed.protocol !== "https:" || parsed.port !== "" || parsed.username !== "" || parsed.password !== "" || parsed.origin !== origin || parsed.pathname !== "/" || parsed.search !== "" || parsed.hash !== "" || forbiddenEgressHostname(parsed.hostname)) {
|
|
24310
24931
|
throw new TypeError("ohmyhost.yaml runtime egress origin is invalid");
|
|
24311
24932
|
}
|
|
24312
24933
|
}
|
|
@@ -24315,16 +24936,7 @@ function normalizeEgressOrigins(value) {
|
|
|
24315
24936
|
}
|
|
24316
24937
|
function forbiddenEgressHostname(hostname) {
|
|
24317
24938
|
const normalized = hostname.toLowerCase();
|
|
24318
|
-
|
|
24319
|
-
return true;
|
|
24320
|
-
}
|
|
24321
|
-
const ipv4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/u.exec(normalized);
|
|
24322
|
-
if (ipv4 === null)
|
|
24323
|
-
return normalized.startsWith("[") || normalized.endsWith("]");
|
|
24324
|
-
const octets = ipv4.slice(1).map(Number);
|
|
24325
|
-
if (octets.some((octet) => octet > 255))
|
|
24326
|
-
return true;
|
|
24327
|
-
return octets[0] === 10 || octets[0] === 127 || octets[0] === 169 && octets[1] === 254 || octets[0] === 172 && (octets[1] ?? 0) >= 16 && (octets[1] ?? 0) <= 31 || octets[0] === 192 && octets[1] === 168;
|
|
24939
|
+
return normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || normalized.endsWith(".internal") || /^\d{1,3}(?:\.\d{1,3}){3}$/u.test(normalized) || normalized.startsWith("[") || normalized.endsWith("]");
|
|
24328
24940
|
}
|
|
24329
24941
|
function parseOhmyhostConfigYaml(source) {
|
|
24330
24942
|
if (typeof source !== "string") {
|
|
@@ -24625,6 +25237,7 @@ async function initializeRepository(input) {
|
|
|
24625
25237
|
const configuredDatabase = existingConfiguration?.database?.enabled === true;
|
|
24626
25238
|
const databaseProvider = supabaseMigrationCount > 0 ? "supabase" : postgresMigrationCount > 0 || configuredDatabase ? "postgresql" : "none";
|
|
24627
25239
|
const migrationCount = databaseProvider === "supabase" ? supabaseMigrationCount : postgresMigrationCount;
|
|
25240
|
+
const selectedDatabaseProvider = existingConfiguration === null ? databaseProvider : configuredDatabase ? "postgresql" : "none";
|
|
24628
25241
|
const betterAuth = dependencies.has("better-auth") || existingConfiguration?.auth?.provider === "better-auth";
|
|
24629
25242
|
const workos = [
|
|
24630
25243
|
"@workos-inc/authkit-react",
|
|
@@ -24677,19 +25290,19 @@ async function initializeRepository(input) {
|
|
|
24677
25290
|
});
|
|
24678
25291
|
const configurationYaml = existingConfigurationYaml ?? generatedConfigurationYaml;
|
|
24679
25292
|
const capabilities = Object.freeze({
|
|
24680
|
-
database:
|
|
25293
|
+
database: selectedDatabaseProvider !== "none",
|
|
24681
25294
|
auth: targetBetterAuth,
|
|
24682
25295
|
mail: targetMail,
|
|
24683
25296
|
storage: targetStorage
|
|
24684
25297
|
});
|
|
24685
25298
|
const companion = companionRequired ? viteCompanionScaffold(applicationFiles, capabilities) : null;
|
|
24686
25299
|
const companionSourcePresent = companion !== null && applicationFiles.includes(companion.sourceEntryPoint);
|
|
24687
|
-
const migrationSqlAdmissionBlockers = await migrationSqlBlockers(applicationDirectory, applicationFiles, existingConfiguration?.database?.migrations,
|
|
25300
|
+
const migrationSqlAdmissionBlockers = await migrationSqlBlockers(applicationDirectory, applicationFiles, existingConfiguration?.database?.migrations, selectedDatabaseProvider);
|
|
24688
25301
|
const blockers = [
|
|
24689
25302
|
...repositoryAdmissionBlockers,
|
|
24690
25303
|
...await workerModuleBlockers(framework, applicationDirectory, applicationFiles),
|
|
24691
25304
|
...await cronBlockers(framework, existingConfiguration, applicationDirectory, applicationFiles),
|
|
24692
|
-
...migrationFilenameBlockers(applicationFiles, existingConfiguration?.database?.migrations,
|
|
25305
|
+
...selectedDatabaseProvider === "none" ? [] : migrationFilenameBlockers(applicationFiles, existingConfiguration?.database?.migrations, selectedDatabaseProvider),
|
|
24693
25306
|
...migrationSqlAdmissionBlockers,
|
|
24694
25307
|
...await storageClientBlockers(existingConfiguration, applicationDirectory, applicationFiles),
|
|
24695
25308
|
...await databaseContractBlockers(existingConfiguration, applicationDirectory, applicationFiles)
|
|
@@ -24718,7 +25331,9 @@ async function initializeRepository(input) {
|
|
|
24718
25331
|
sourceEntryPoint: WORKER_MODULE_PATH,
|
|
24719
25332
|
present: applicationFiles.includes(WORKER_MODULE_PATH),
|
|
24720
25333
|
...runtimeContract(capabilities)
|
|
24721
|
-
}) : null
|
|
25334
|
+
}) : null,
|
|
25335
|
+
// Next.js and TanStack Start have neither a companion nor a Worker module, and read it here.
|
|
25336
|
+
runtime: Object.values(capabilities).some(Boolean) ? runtimeContract(capabilities) : null
|
|
24722
25337
|
});
|
|
24723
25338
|
}
|
|
24724
25339
|
function detectRepositoryFramework(dependencies, files, existingConfiguration) {
|
|
@@ -24738,7 +25353,7 @@ async function cronBlockers(framework, config, applicationDirectory, files) {
|
|
|
24738
25353
|
return [
|
|
24739
25354
|
{
|
|
24740
25355
|
code: "scheduled_functions_runtime_unsupported",
|
|
24741
|
-
message: "
|
|
25356
|
+
message: "functions.crons needs runtime.mode edge (Next.js, TanStack Start, or Vite with src/ohmyhost/companion.ts) or functions (src/ohmyhost/worker.ts) in ohmyhost.yaml. Change runtime.mode, or remove functions.crons."
|
|
24742
25357
|
}
|
|
24743
25358
|
];
|
|
24744
25359
|
}
|
|
@@ -24815,14 +25430,20 @@ function migrationFilenameBlockers(files, configuredPath, provider) {
|
|
|
24815
25430
|
return [];
|
|
24816
25431
|
const root = configuredPath ?? "postgres/migrations";
|
|
24817
25432
|
const prefix = `${root}/`;
|
|
24818
|
-
const
|
|
24819
|
-
return
|
|
25433
|
+
const invalid5 = files.filter((path) => path.startsWith(prefix)).map((path) => path.slice(prefix.length)).find((path) => !/^(?:[A-Za-z0-9][A-Za-z0-9._-]*\/)*\d{14}_[a-z0-9][a-z0-9_-]*\.sql$/u.test(path));
|
|
25434
|
+
return invalid5 === void 0 ? [] : [
|
|
24820
25435
|
{
|
|
24821
25436
|
code: "migration_filename_noncanonical",
|
|
24822
|
-
message:
|
|
25437
|
+
message: `Migration file '${invalid5}' in ${root} is not named YYYYMMDDHHMMSS_name.sql (14 digits, an underscore, then lowercase letters, digits, _ or -). Every file in that directory must match, README and .gitkeep included: rename or move it, keeping the run order.`
|
|
24823
25438
|
}
|
|
24824
|
-
]
|
|
25439
|
+
];
|
|
24825
25440
|
}
|
|
25441
|
+
var MIGRATION_REJECTION = {
|
|
25442
|
+
destructive: "It contains DROP, TRUNCATE, INSERT, UPDATE, DELETE, BEGIN/COMMIT/ROLLBACK, CREATE OR REPLACE, or ALTER TABLE other than ADD; move row changes to 'ohmyhost database write'.",
|
|
25443
|
+
provider_specific: "It mentions supabase, CREATE POLICY, ROW LEVEL SECURITY, the auth. or storage. schema, or the anon, authenticated or service_role role; comments count.",
|
|
25444
|
+
unsupported: "It has a statement other than CREATE TABLE, TYPE, DOMAIN, SEQUENCE, VIEW, MATERIALIZED VIEW, TRIGGER, INDEX or FUNCTION, ALTER TABLE ... ADD, COMMENT ON, CREATE SCHEMA IF NOT EXISTS auth|extensions|private or CREATE EXTENSION IF NOT EXISTS pgcrypto|btree_gist|unaccent; or a function not in LANGUAGE sql or plpgsql, SECURITY DEFINER without SET search_path, or the word ohmyhost, comments included.",
|
|
25445
|
+
invalid: "It is empty, not UTF-8 with LF line endings, has a BOM or NUL byte or an unterminated quote or comment, or exceeds 256 KiB (at most 128 files and 2 MiB in total)."
|
|
25446
|
+
};
|
|
24826
25447
|
async function migrationSqlBlockers(applicationDirectory, files, configuredPath, provider) {
|
|
24827
25448
|
if (provider !== "postgresql")
|
|
24828
25449
|
return [];
|
|
@@ -24845,7 +25466,7 @@ async function migrationSqlBlockers(applicationDirectory, files, configuredPath,
|
|
|
24845
25466
|
throw error;
|
|
24846
25467
|
blockers.push({
|
|
24847
25468
|
code: "migration_sql_not_admitted",
|
|
24848
|
-
message: `PostgreSQL migration '${migration.path}' is not admitted (${error.reason}).`
|
|
25469
|
+
message: `PostgreSQL migration '${migration.path}' is not admitted (${error.reason}): ${MIGRATION_REJECTION[error.reason]} Migrations are expand-only. Fix the file before its first deployment; never edit a migration a deployment already applied.`
|
|
24849
25470
|
});
|
|
24850
25471
|
}
|
|
24851
25472
|
}
|
|
@@ -24860,7 +25481,7 @@ async function migrationSqlBlockers(applicationDirectory, files, configuredPath,
|
|
|
24860
25481
|
return [
|
|
24861
25482
|
{
|
|
24862
25483
|
code: "migration_sql_not_admitted",
|
|
24863
|
-
message: `
|
|
25484
|
+
message: `The migration set is not admitted (${error.reason}): ${MIGRATION_REJECTION[error.reason]}`
|
|
24864
25485
|
}
|
|
24865
25486
|
];
|
|
24866
25487
|
}
|
|
@@ -24871,17 +25492,17 @@ async function readExistingConfiguration(repositoryRoot, repositoryFiles, applic
|
|
|
24871
25492
|
const configurationPath = resolve2(repositoryRoot, "ohmyhost.yaml");
|
|
24872
25493
|
const information = await stat2(configurationPath);
|
|
24873
25494
|
if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES) {
|
|
24874
|
-
throw new RepositoryInitError("repository_configuration_invalid", "
|
|
25495
|
+
throw new RepositoryInitError("repository_configuration_invalid", "ohmyhost.yaml must be one regular file of at most 64 KiB");
|
|
24875
25496
|
}
|
|
24876
25497
|
const configuration = await readFile3(configurationPath, "utf8");
|
|
24877
25498
|
let parsed;
|
|
24878
25499
|
try {
|
|
24879
25500
|
parsed = parseOhmyhostConfigYaml(configuration);
|
|
24880
|
-
} catch {
|
|
24881
|
-
throw new RepositoryInitError("repository_configuration_invalid", "
|
|
25501
|
+
} catch (error) {
|
|
25502
|
+
throw new RepositoryInitError("repository_configuration_invalid", error instanceof TypeError ? error.message : "ohmyhost.yaml is invalid");
|
|
24882
25503
|
}
|
|
24883
25504
|
if (parsed.applicationRoot !== applicationRoot) {
|
|
24884
|
-
throw new RepositoryInitError("repository_configuration_root_mismatch",
|
|
25505
|
+
throw new RepositoryInitError("repository_configuration_root_mismatch", `ohmyhost.yaml sets applicationRoot to '${parsed.applicationRoot}', but init selected '${applicationRoot}'. Run 'ohmyhost init --root ${parsed.applicationRoot} --dry-run --json', or correct applicationRoot.`);
|
|
24885
25506
|
}
|
|
24886
25507
|
return configuration;
|
|
24887
25508
|
}
|
|
@@ -25003,12 +25624,13 @@ function hasDirectSafeBuildCommand(packageJson, framework) {
|
|
|
25003
25624
|
const scripts = packageJson["scripts"];
|
|
25004
25625
|
if (!isRecord4(scripts) || typeof scripts["build"] !== "string")
|
|
25005
25626
|
return false;
|
|
25006
|
-
|
|
25007
|
-
|
|
25008
|
-
|
|
25627
|
+
return (framework === "nextjs" || framework === "vite" || framework === "tanstack-start") && isAdmittedFrameworkBuildScript(framework, scripts["build"]);
|
|
25628
|
+
}
|
|
25629
|
+
function buildScriptMessage(framework, command) {
|
|
25630
|
+
const quoted = command.slice(0, 200);
|
|
25009
25631
|
if (framework === "nextjs")
|
|
25010
|
-
return
|
|
25011
|
-
return
|
|
25632
|
+
return `package.json scripts.build must be exactly "next build" or "next build --webpack", not "${quoted}". Move other steps into their own scripts, then commit and push.`;
|
|
25633
|
+
return `package.json scripts.build must be "vite build", optionally joined with && to exactly one "tsc", "tsc -b", "tsc --build" or "tsc --noEmit" stage before or after it, not "${quoted}". Move other steps into their own scripts, then commit and push.`;
|
|
25012
25634
|
}
|
|
25013
25635
|
async function assertRepositoryRoot(root) {
|
|
25014
25636
|
let information;
|
|
@@ -25157,11 +25779,16 @@ function frameworkConfigurations(files) {
|
|
|
25157
25779
|
ambiguous: Object.freeze(ambiguous)
|
|
25158
25780
|
});
|
|
25159
25781
|
}
|
|
25782
|
+
var FRAMEWORK_PACKAGE = {
|
|
25783
|
+
nextjs: "next",
|
|
25784
|
+
"tanstack-start": "@tanstack/react-start",
|
|
25785
|
+
vite: "vite"
|
|
25786
|
+
};
|
|
25160
25787
|
function frameworkCompatibility(packageJson, framework) {
|
|
25161
25788
|
const targets = framework === "tanstack-start" ? [
|
|
25162
|
-
{ framework: "tanstack-start", packageName: "
|
|
25163
|
-
{ framework: "vite", packageName:
|
|
25164
|
-
] : framework === "nextjs" ? [{ framework: "nextjs", packageName:
|
|
25789
|
+
{ framework: "tanstack-start", packageName: FRAMEWORK_PACKAGE["tanstack-start"] },
|
|
25790
|
+
{ framework: "vite", packageName: FRAMEWORK_PACKAGE.vite }
|
|
25791
|
+
] : framework === "nextjs" ? [{ framework: "nextjs", packageName: FRAMEWORK_PACKAGE.nextjs }] : framework === "vite" ? [{ framework: "vite", packageName: FRAMEWORK_PACKAGE.vite }] : [];
|
|
25165
25792
|
const frameworks = targets.map(({ framework: targetFramework, packageName }) => {
|
|
25166
25793
|
const version2 = dependencyVersion(packageJson, packageName) ?? "";
|
|
25167
25794
|
return Object.freeze({
|
|
@@ -25199,7 +25826,7 @@ function repositoryBlockers(packageJson, framework, nextStandaloneOutput, tanSta
|
|
|
25199
25826
|
if (tanStackRuntime === "ambiguous") {
|
|
25200
25827
|
blockers.push({
|
|
25201
25828
|
code: "framework_ambiguous",
|
|
25202
|
-
message:
|
|
25829
|
+
message: 'TanStack Start needs one vite.config.js, .mjs or .ts that selects a mode. Static: tanstackStart({ prerender: { enabled: true } }) from "@tanstack/react-start/plugin/vite" and no "@cloudflare/vite-plugin". Server: cloudflare({ viteEnvironment: { name: "ssr" } }) from "@cloudflare/vite-plugin" listed before tanstackStart() in plugins, with @cloudflare/vite-plugin, @tanstack/react-router, @tanstack/react-start, react, react-dom, @vitejs/plugin-react and vite in package.json.'
|
|
25203
25830
|
});
|
|
25204
25831
|
}
|
|
25205
25832
|
const relevantAmbiguousConfigs = frameworkConfigs.ambiguous.filter((config) => framework === "nextjs" ? config === "next" || config === "open-next" : config === "vite");
|
|
@@ -25217,19 +25844,19 @@ function repositoryBlockers(packageJson, framework, nextStandaloneOutput, tanSta
|
|
|
25217
25844
|
} else if (packageManagerAdmission instanceof FrameworkAdmissionError) {
|
|
25218
25845
|
blockers.push({
|
|
25219
25846
|
code: "package_manager_unpinned",
|
|
25220
|
-
message: "Declare one exact npm, pnpm, Yarn, or Bun packageManager version and commit its matching lockfile."
|
|
25847
|
+
message: packageManagerAdmission.reason === "package_manager_version_unsupported" ? `This build image runs Bun ${PINNED_BUN_VERSION}; pin packageManager to bun@${PINNED_BUN_VERSION} and commit its matching lockfile. Repeating the unchanged version cannot work.` : "Declare one exact npm, pnpm, Yarn, or Bun packageManager version and commit its matching lockfile."
|
|
25221
25848
|
});
|
|
25222
25849
|
} else if (existingConfiguration !== null && (existingConfiguration.build.install !== packageManagerAdmission.installCommand || "command" in existingConfiguration.build && existingConfiguration.build.command !== packageManagerAdmission.buildCommand)) {
|
|
25223
25850
|
blockers.push({
|
|
25224
25851
|
code: "build_command_unsupported",
|
|
25225
|
-
message:
|
|
25852
|
+
message: `ohmyhost.yaml build.install must be exactly "${packageManagerAdmission.installCommand}"${"command" in existingConfiguration.build ? ` and build.command "${packageManagerAdmission.buildCommand}"` : ""} for the packageManager in package.json. Set them, then commit and push.`
|
|
25226
25853
|
});
|
|
25227
25854
|
}
|
|
25228
25855
|
if (compatibility.classification === "unsupported" && compatibility.frameworks.length > 0) {
|
|
25229
|
-
const unsupported = compatibility.frameworks.filter((entry) => entry.classification === "unsupported").map((entry) => `${entry.framework}@${entry.version || "missing"}`).join(", ");
|
|
25856
|
+
const unsupported = compatibility.frameworks.filter((entry) => entry.classification === "unsupported").map((entry) => `${FRAMEWORK_PACKAGE[entry.framework]}@${entry.version || "missing"}`).join(", ");
|
|
25230
25857
|
blockers.push({
|
|
25231
25858
|
code: "framework_version_unsupported",
|
|
25232
|
-
message:
|
|
25859
|
+
message: `${unsupported} is outside the versions ohmyho.st builds: next 15.5.x or 16.0.0\u201316.3.4, vite 5.4.0\u20138.2.2, @tanstack/react-start 1.168.26\u20131.168.49, as an exact version or a ^ or ~ range whose base version is inside. Pin a version in that range in package.json, update the lockfile, commit and push.`
|
|
25233
25860
|
});
|
|
25234
25861
|
}
|
|
25235
25862
|
const scripts = packageJson["scripts"];
|
|
@@ -25240,10 +25867,13 @@ function repositoryBlockers(packageJson, framework, nextStandaloneOutput, tanSta
|
|
|
25240
25867
|
} else if (!hasDirectSafeBuildCommand(packageJson, framework)) {
|
|
25241
25868
|
blockers.push({
|
|
25242
25869
|
code: "build_command_unsupported",
|
|
25243
|
-
message:
|
|
25870
|
+
message: buildScriptMessage(framework, scripts["build"])
|
|
25244
25871
|
});
|
|
25245
25872
|
}
|
|
25246
25873
|
const betterAuthVersion = dependencyVersion(packageJson, "better-auth");
|
|
25874
|
+
const managedAuthIssue = managedCustomerAuthAdmissionIssue(existingConfiguration);
|
|
25875
|
+
if (managedAuthIssue !== null)
|
|
25876
|
+
blockers.push(managedAuthIssue);
|
|
25247
25877
|
if (existingConfiguration?.auth?.provider === "better-auth" && betterAuthVersion !== null && betterAuthVersion !== SUPPORTED_BETTER_AUTH_VERSION) {
|
|
25248
25878
|
blockers.push({
|
|
25249
25879
|
code: "better_auth_version_unsupported",
|
|
@@ -25264,7 +25894,7 @@ function repositoryBlockers(packageJson, framework, nextStandaloneOutput, tanSta
|
|
|
25264
25894
|
if (!nextConfigured) {
|
|
25265
25895
|
blockers.push({
|
|
25266
25896
|
code: "next_adapter_unconfigured",
|
|
25267
|
-
message: "
|
|
25897
|
+
message: 'Next.js needs scripts.build exactly "next build" or "next build --webpack", react and react-dom at exact versions such as "19.1.0" (no ^ or ~), next within 15.5.x or 16.0.0\u201316.3.4, and exactly one next.config.js, .mjs or .ts, with at most one open-next.config file. ohmyho.st adds the OpenNext build itself; do not make it the build script.'
|
|
25268
25898
|
});
|
|
25269
25899
|
}
|
|
25270
25900
|
}
|
|
@@ -26021,7 +26651,7 @@ var runProductionCli = async (argv, environment, io, signal, overrides = {}) =>
|
|
|
26021
26651
|
args = extracted.argv;
|
|
26022
26652
|
profileName = extracted.profileName;
|
|
26023
26653
|
} catch {
|
|
26024
|
-
return written(invalidCommandFailure(
|
|
26654
|
+
return written(invalidCommandFailure(argv, environment));
|
|
26025
26655
|
}
|
|
26026
26656
|
const workingDirectory = overrides.workingDirectory ?? process.cwd();
|
|
26027
26657
|
let command;
|
|
@@ -26053,8 +26683,8 @@ var runProductionCli = async (argv, environment, io, signal, overrides = {}) =>
|
|
|
26053
26683
|
if (error instanceof LinkedProjectAmbiguousError)
|
|
26054
26684
|
return written(errorDocument(recognizedCommandLabel(args), 2, "linked_project_selection_required", false, `This directory is linked to projects of several organizations (${error.organizationIds.join(", ")}). Pass --project ULID, or choose the login with --profile-name NAME; '${resolveProductCliEnvironment(environment).commandPrefix} profile list --json' shows the saved logins.`));
|
|
26055
26685
|
if (error instanceof CredentialStoreUnavailableError)
|
|
26056
|
-
return written(errorDocument(recognizedCommandLabel(args), 3, "credential_store_unavailable",
|
|
26057
|
-
const result = error instanceof InvalidCommandError ? invalidCommandFailure(
|
|
26686
|
+
return written(errorDocument(recognizedCommandLabel(args), 3, "credential_store_unavailable", false, CREDENTIAL_STORE_UNAVAILABLE_ACTION));
|
|
26687
|
+
const result = error instanceof InvalidCommandError ? invalidCommandFailure(args, environment) : errorDocument(recognizedCommandLabel(args), 1, "internal_error", false, INTERNAL_ERROR_ACTION);
|
|
26058
26688
|
if (!(error instanceof InvalidCommandError)) {
|
|
26059
26689
|
diagnostics.record({
|
|
26060
26690
|
correlationId: randomUUID2(),
|
|
@@ -26067,7 +26697,7 @@ var runProductionCli = async (argv, environment, io, signal, overrides = {}) =>
|
|
|
26067
26697
|
}
|
|
26068
26698
|
if (command.kind === "init") {
|
|
26069
26699
|
if (profileName !== void 0)
|
|
26070
|
-
return written(
|
|
26700
|
+
return written(errorDocument("init", 2, "invalid_command", false, "ohmyhost init runs offline and uses no login: remove --profile-name and run it again."));
|
|
26071
26701
|
return runOfflineInit(command, io, diagnostics);
|
|
26072
26702
|
}
|
|
26073
26703
|
if (command.kind === "help" || command.kind === "version") {
|
|
@@ -26101,6 +26731,8 @@ var runProductionCli = async (argv, environment, io, signal, overrides = {}) =>
|
|
|
26101
26731
|
const registry = overrides.profiles ?? new NativeKeyringProfileRegistry(void 0, platform.credentialService);
|
|
26102
26732
|
const linkDirectory = join3(workingDirectory, platform.linkDirectory);
|
|
26103
26733
|
const projectId = commandProjectId(command);
|
|
26734
|
+
if (projectId !== void 0 && linked === null)
|
|
26735
|
+
links = await readProjectLinks(linkDirectory, platform.apiOrigin);
|
|
26104
26736
|
const organizationId = handedAccount2?.organizationId ?? commandOrganizationId(command) ?? linked?.organizationId ?? (projectId === void 0 ? void 0 : linkedOrganization(links, projectId));
|
|
26105
26737
|
const request = profileRequest({
|
|
26106
26738
|
...profileName === void 0 ? {} : { name: profileName },
|
|
@@ -26179,7 +26811,15 @@ var runProductionCli = async (argv, environment, io, signal, overrides = {}) =>
|
|
|
26179
26811
|
}
|
|
26180
26812
|
}
|
|
26181
26813
|
} catch (error) {
|
|
26182
|
-
|
|
26814
|
+
const result = compositionFailure(recognizedCommandLabel(args), error, signal, platform.commandPrefix, profile);
|
|
26815
|
+
if (result.exitCode === 1)
|
|
26816
|
+
diagnostics.record({
|
|
26817
|
+
correlationId: randomUUID2(),
|
|
26818
|
+
category: "internal",
|
|
26819
|
+
subsystem: "authentication",
|
|
26820
|
+
errorKind: "unexpected"
|
|
26821
|
+
});
|
|
26822
|
+
return written(result);
|
|
26183
26823
|
}
|
|
26184
26824
|
const selected = profile;
|
|
26185
26825
|
const productApiOrigin = platform.apiOrigin;
|
|
@@ -26263,13 +26903,13 @@ var adoptLegacyLink = async (input) => {
|
|
|
26263
26903
|
});
|
|
26264
26904
|
await removeLegacyProjectLink(input.directory);
|
|
26265
26905
|
};
|
|
26266
|
-
var compositionFailure = (command, error, signal, commandPrefix) => {
|
|
26906
|
+
var compositionFailure = (command, error, signal, commandPrefix, profile) => {
|
|
26267
26907
|
if (error instanceof ProfileSelectionError)
|
|
26268
26908
|
return errorDocument(command, error.exitCode, error.code, false, error.suggestedAction);
|
|
26269
26909
|
if (error instanceof ProfileLimitError)
|
|
26270
26910
|
return errorDocument(command, 2, "profile_limit_reached", false, profileLimitAction(commandPrefix));
|
|
26271
26911
|
if (error instanceof ProductApiAuthenticationError)
|
|
26272
|
-
return error.code === "authentication_required" ? errorDocument(command, 5, "authentication_required", false,
|
|
26912
|
+
return error.code === "authentication_required" ? errorDocument(command, 5, "authentication_required", false, endedLoginAction(commandPrefix, profile)) : errorDocument(command, 5, "token_refresh_failed", true, refreshFailedAction(commandPrefix, profile));
|
|
26273
26913
|
return classifyCliFailure(command, error, signal);
|
|
26274
26914
|
};
|
|
26275
26915
|
var runOfflineInit = async (command, io, diagnostics) => {
|
|
@@ -26301,7 +26941,8 @@ var runOfflineInit = async (command, io, diagnostics) => {
|
|
|
26301
26941
|
companion: result.companion,
|
|
26302
26942
|
// The functions runtime reports its own bindings and packages; dropping it left a
|
|
26303
26943
|
// storage-enabled functions app with no contract to read at all.
|
|
26304
|
-
worker: result.worker
|
|
26944
|
+
worker: result.worker,
|
|
26945
|
+
runtime: result.runtime
|
|
26305
26946
|
})}
|
|
26306
26947
|
`);
|
|
26307
26948
|
return blocked ? 9 : 0;
|
|
@@ -26315,11 +26956,38 @@ var runOfflineInit = async (command, io, diagnostics) => {
|
|
|
26315
26956
|
});
|
|
26316
26957
|
const code = error instanceof RepositoryInitBlockedError ? "repository_init_blocked" : error instanceof RepositoryInitConflictError ? "repository_configuration_exists" : known ? error.code : "internal_error";
|
|
26317
26958
|
const exitCode = error instanceof RepositoryInitBlockedError ? 9 : known ? 2 : 1;
|
|
26318
|
-
const result = errorDocument("init", exitCode, code, false,
|
|
26959
|
+
const result = errorDocument("init", exitCode, code, false, known ? initFailureAction(error) : INTERNAL_ERROR_ACTION);
|
|
26319
26960
|
io.writeStdout(result.stdout);
|
|
26320
26961
|
return result.exitCode;
|
|
26321
26962
|
}
|
|
26322
26963
|
};
|
|
26964
|
+
var initFailureAction = (error) => {
|
|
26965
|
+
if (error instanceof RepositoryInitBlockedError)
|
|
26966
|
+
return "Run 'ohmyhost init --dry-run --json' and resolve every reported blocker.";
|
|
26967
|
+
if (error instanceof RepositoryInitConflictError)
|
|
26968
|
+
return "ohmyhost.yaml already exists and init never overwrites it; that file stays authoritative. Run 'ohmyhost init --dry-run --json' to check it and list blockers, then edit the file by hand. Do not delete it to rerun init.";
|
|
26969
|
+
const message = /[.!?]$/u.test(error.message) ? error.message : `${error.message}.`;
|
|
26970
|
+
switch (error.code) {
|
|
26971
|
+
case "repository_root_required":
|
|
26972
|
+
return "Run ohmyhost init from the Git repository root with --root set to the application directory (for example --root website); commit the root ohmyhost.yaml before planning.";
|
|
26973
|
+
case "project_name_invalid":
|
|
26974
|
+
return "Pass a lowercase deployment slug with '--project', then rerun init.";
|
|
26975
|
+
case "application_root_ambiguous":
|
|
26976
|
+
return `${message} Run 'ohmyhost init --root DIR --dry-run --json' from the Git repository root with the directory to deploy; ask the customer if it is unclear.`;
|
|
26977
|
+
case "application_root_not_found":
|
|
26978
|
+
case "application_root_unsupported":
|
|
26979
|
+
return `${message} init needs a package.json that depends on next (without vite or @tanstack/react-start), on vite, or on @tanstack/react-start, or a functions app with src/ohmyhost/worker.ts. Pass --root DIR with the app's directory, or fix its package.json, then run init again.`;
|
|
26980
|
+
case "application_root_invalid":
|
|
26981
|
+
case "application_root_missing":
|
|
26982
|
+
return `${message} Pass --root as a path relative to the repository root, for example apps/web.`;
|
|
26983
|
+
case "repository_configuration_invalid":
|
|
26984
|
+
return `${error.message.replace(/\.$/u, "")}. Fix that, then run 'ohmyhost init --dry-run --json' again; do not delete ohmyhost.yaml.`;
|
|
26985
|
+
case "repository_configuration_root_mismatch":
|
|
26986
|
+
return error.message;
|
|
26987
|
+
default:
|
|
26988
|
+
return `${message} Fix that and run 'ohmyhost init --json' again from the Git repository root.`;
|
|
26989
|
+
}
|
|
26990
|
+
};
|
|
26323
26991
|
var FetchWorkosPublicTransport = class {
|
|
26324
26992
|
parentSignal;
|
|
26325
26993
|
timeoutMs;
|
|
@@ -26565,8 +27233,8 @@ var decodeUtf8 = (decoder, chunk, stream) => {
|
|
|
26565
27233
|
throw new ResponseContractError();
|
|
26566
27234
|
}
|
|
26567
27235
|
};
|
|
26568
|
-
var waitUntil = async (
|
|
26569
|
-
const delay = Math.max(0,
|
|
27236
|
+
var waitUntil = async (instant2, signal) => {
|
|
27237
|
+
const delay = Math.max(0, instant2.getTime() - Date.now());
|
|
26570
27238
|
await new Promise((resolve3, reject) => {
|
|
26571
27239
|
const cleanup = () => signal.removeEventListener("abort", cancel);
|
|
26572
27240
|
const complete = () => {
|
|
@@ -26610,7 +27278,7 @@ var runOfflineLogout = async (registry, request, commandPrefix, io, diagnostics)
|
|
|
26610
27278
|
subsystem: "credential_store",
|
|
26611
27279
|
errorKind: "unavailable"
|
|
26612
27280
|
});
|
|
26613
|
-
result = errorDocument("logout", 3, "credential_store_unavailable",
|
|
27281
|
+
result = errorDocument("logout", 3, "credential_store_unavailable", false, CREDENTIAL_STORE_UNAVAILABLE_ACTION);
|
|
26614
27282
|
}
|
|
26615
27283
|
io.writeStdout(result.stdout);
|
|
26616
27284
|
return result.exitCode;
|
|
@@ -26645,7 +27313,14 @@ var readSecretValueFromStandardInput = async (input, signal) => {
|
|
|
26645
27313
|
value.fill(0);
|
|
26646
27314
|
}
|
|
26647
27315
|
};
|
|
26648
|
-
var invalidCommandFailure = (
|
|
27316
|
+
var invalidCommandFailure = (argv, environment) => {
|
|
27317
|
+
let commandPrefix = "ohmyhost";
|
|
27318
|
+
try {
|
|
27319
|
+
commandPrefix = resolveProductCliEnvironment(environment).commandPrefix;
|
|
27320
|
+
} catch {
|
|
27321
|
+
}
|
|
27322
|
+
return errorDocument(recognizedCommandLabel(argv), 2, "invalid_command", false, invalidCommandAction(argv, commandPrefix));
|
|
27323
|
+
};
|
|
26649
27324
|
var resultDocument = (command, exitCode, status, body = {}) => ({
|
|
26650
27325
|
exitCode,
|
|
26651
27326
|
stdout: `${JSON.stringify({ version: 1, command, status, ...body })}
|
|
@@ -26691,6 +27366,7 @@ export {
|
|
|
26691
27366
|
InvalidCommandError,
|
|
26692
27367
|
recognizedCommandLabel,
|
|
26693
27368
|
parseProductCliCommand,
|
|
27369
|
+
parseProjectDataChangePlan2 as parseProjectDataChangePlan,
|
|
26694
27370
|
parsePaidDomainPlan,
|
|
26695
27371
|
parsePaidDomain,
|
|
26696
27372
|
parseCloudflareDnsAuthorization,
|
|
@@ -26721,6 +27397,11 @@ export {
|
|
|
26721
27397
|
PublicResponseTooLargeError,
|
|
26722
27398
|
PsqlUnavailableError,
|
|
26723
27399
|
cliMetadataResult,
|
|
27400
|
+
invalidCommandAction,
|
|
27401
|
+
INTERNAL_ERROR_ACTION,
|
|
27402
|
+
CREDENTIAL_STORE_UNAVAILABLE_ACTION,
|
|
27403
|
+
endedLoginAction,
|
|
27404
|
+
refreshFailedAction,
|
|
26724
27405
|
ProductCli,
|
|
26725
27406
|
ACCOUNT_SENSITIVE_PROBLEMS,
|
|
26726
27407
|
classifyCliFailure,
|