@amerged/ohmyhost-cli 0.1.11 → 0.1.13
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 +1 -1
- package/dist/{chunk-WT2RRQUB.js → chunk-ZWAAQEOZ.js} +124 -9
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/apps/product-cli/cli.ts +25 -0
- package/src/apps/product-cli/command.ts +30 -0
- package/src/apps/product-cli/composition.ts +5 -3
- package/src/apps/product-cli/product-api.ts +24 -0
- package/src/apps/product-cli/repository-init.ts +25 -1
- package/src/apps/product-cli/runtime-contract.ts +41 -6
- package/src/packages/sdk-ts/generated/index.ts +6 -0
- package/src/packages/sdk-ts/generated/sdk.gen.ts +50 -0
- package/src/packages/sdk-ts/generated/types.gen.ts +83 -0
package/README.md
CHANGED
package/dist/bin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire as __ohmyhostCreateRequire } from "node:module"; const require = __ohmyhostCreateRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
runProductionCli
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZWAAQEOZ.js";
|
|
6
6
|
import "./chunk-4HFQ54FP.js";
|
|
7
7
|
|
|
8
8
|
// apps/product-cli/dist/bin.js
|
|
@@ -15002,6 +15002,8 @@ var recognizedCommandLabel = (argv) => {
|
|
|
15002
15002
|
return "project status";
|
|
15003
15003
|
if (value === "project" && values[0] === "handle" && values[1] === "check")
|
|
15004
15004
|
return "project handle check";
|
|
15005
|
+
if (value === "project" && values[0] === "handle" && values[1] === "set")
|
|
15006
|
+
return "project handle set";
|
|
15005
15007
|
if (value === "project" && values[0] === "dev-access" && values[1] === "create")
|
|
15006
15008
|
return "project dev-access create";
|
|
15007
15009
|
if (value === "database" && values[0] === "compute" && values[1] === "set")
|
|
@@ -15444,6 +15446,25 @@ var parseProductCliCommand = (argv) => {
|
|
|
15444
15446
|
}
|
|
15445
15447
|
if (first === "project") {
|
|
15446
15448
|
const subcommand = values.shift();
|
|
15449
|
+
if (subcommand === "handle" && values[0] === "set") {
|
|
15450
|
+
values.shift();
|
|
15451
|
+
if (yes || wait || follow || stdin)
|
|
15452
|
+
throw new InvalidCommandError();
|
|
15453
|
+
const options2 = parseOptions(values, [
|
|
15454
|
+
"--project",
|
|
15455
|
+
"--handle",
|
|
15456
|
+
"--if-match",
|
|
15457
|
+
"--idempotency-key"
|
|
15458
|
+
]);
|
|
15459
|
+
return {
|
|
15460
|
+
kind: "project-handle-set",
|
|
15461
|
+
projectId: options2["--project"],
|
|
15462
|
+
handle: options2["--handle"],
|
|
15463
|
+
ifMatch: options2["--if-match"],
|
|
15464
|
+
idempotencyKey: options2["--idempotency-key"],
|
|
15465
|
+
credentialStore
|
|
15466
|
+
};
|
|
15467
|
+
}
|
|
15447
15468
|
if (subcommand === "handle" && values.shift() === "check") {
|
|
15448
15469
|
if (yes || wait || follow || stdin)
|
|
15449
15470
|
throw new InvalidCommandError();
|
|
@@ -16751,6 +16772,26 @@ var PROBLEM_POLICY = {
|
|
|
16751
16772
|
retryable: true,
|
|
16752
16773
|
action: "Retry project creation with the same idempotency key."
|
|
16753
16774
|
},
|
|
16775
|
+
project_handle_taken: {
|
|
16776
|
+
status: 409,
|
|
16777
|
+
retryable: false,
|
|
16778
|
+
action: "Run 'ohmyhost project handle check' for a free address, then rename to one of the alternatives it returns."
|
|
16779
|
+
},
|
|
16780
|
+
project_handle_invalid: {
|
|
16781
|
+
status: 409,
|
|
16782
|
+
retryable: false,
|
|
16783
|
+
action: "Choose one to five lowercase words of letters and digits, three to fifty-nine characters, avoiding reserved words."
|
|
16784
|
+
},
|
|
16785
|
+
project_handle_unchanged: {
|
|
16786
|
+
status: 409,
|
|
16787
|
+
retryable: false,
|
|
16788
|
+
action: "The project already answers on this address; no rename is needed."
|
|
16789
|
+
},
|
|
16790
|
+
project_rename_blocked: {
|
|
16791
|
+
status: 409,
|
|
16792
|
+
retryable: true,
|
|
16793
|
+
action: "Wait for the running operation to finish, read the project again, then rename with the new ETag."
|
|
16794
|
+
},
|
|
16754
16795
|
project_identity_unavailable: {
|
|
16755
16796
|
status: 503,
|
|
16756
16797
|
retryable: true,
|
|
@@ -16816,6 +16857,11 @@ var PROBLEM_POLICY = {
|
|
|
16816
16857
|
retryable: false,
|
|
16817
16858
|
action: "Run 'ohmyhost init', apply the matching ohmyhost framework skill, then plan the new commit."
|
|
16818
16859
|
},
|
|
16860
|
+
repository_configuration_missing: {
|
|
16861
|
+
status: 409,
|
|
16862
|
+
retryable: false,
|
|
16863
|
+
action: "Place ohmyhost.yaml at the Git repository root and set applicationRoot to the application directory (for example website). Commit and push the change, then plan the new commit."
|
|
16864
|
+
},
|
|
16819
16865
|
migration_filename_noncanonical: {
|
|
16820
16866
|
status: 409,
|
|
16821
16867
|
retryable: false,
|
|
@@ -17074,7 +17120,7 @@ var parseDevAccessTicket = (value) => {
|
|
|
17074
17120
|
]);
|
|
17075
17121
|
const origin = input["origin"];
|
|
17076
17122
|
const redeemUrl = input["redeem_url"];
|
|
17077
|
-
if (!isUlid(input["ticket_id"]) || !isUlid(input["project_id"]) || !isUlid(input["environment_id"]) || typeof origin !== "string" || !/^https:\/\/dev-[a-
|
|
17123
|
+
if (!isUlid(input["ticket_id"]) || !isUlid(input["project_id"]) || !isUlid(input["environment_id"]) || typeof origin !== "string" || !/^https:\/\/dev-[a-z0-9]+(?:-[a-z0-9]+){0,4}\.(?:dev\.)?check\.omh\.st$/u.test(origin) || typeof redeemUrl !== "string" || !/^https:\/\/dev-[a-z0-9]+(?:-[a-z0-9]+){0,4}\.(?:dev\.)?check\.omh\.st\/\.ohmyhost\/dev-access\/redeem\?stage=dev&ticket=[A-Za-z0-9_-]{43}$/u.test(redeemUrl) || new URL(redeemUrl).origin !== origin || !isIsoInstant(input["expires_at"])) {
|
|
17078
17124
|
throw new ResponseContractError();
|
|
17079
17125
|
}
|
|
17080
17126
|
return input;
|
|
@@ -17197,7 +17243,7 @@ var parseProject = (value) => {
|
|
|
17197
17243
|
"region",
|
|
17198
17244
|
"created_at"
|
|
17199
17245
|
]);
|
|
17200
|
-
if (!isUlid(input["id"]) || !isUlid(input["organization_id"]) || !boundedString(input["name"], 1, 128) || input["name"] !== input["name"].trim() || !boundedString(input["handle"],
|
|
17246
|
+
if (!isUlid(input["id"]) || !isUlid(input["organization_id"]) || !boundedString(input["name"], 1, 128) || input["name"] !== input["name"].trim() || !boundedString(input["handle"], 3, 59) || !/^[a-z0-9]+(?:-[a-z0-9]+){0,4}$/u.test(input["handle"]) || input["data_mode"] !== "shared" && input["data_mode"] !== "isolated" || input["region"] !== "us" && input["region"] !== "eu" || !isIsoInstant(input["created_at"])) {
|
|
17201
17247
|
throw new ResponseContractError();
|
|
17202
17248
|
}
|
|
17203
17249
|
return input;
|
|
@@ -17215,6 +17261,7 @@ var parseProjectPage = (value) => {
|
|
|
17215
17261
|
var parseProjectStatus = (value) => {
|
|
17216
17262
|
const input = exactRecord2(value, [
|
|
17217
17263
|
"project_id",
|
|
17264
|
+
"etag",
|
|
17218
17265
|
"handle",
|
|
17219
17266
|
"region",
|
|
17220
17267
|
"lifecycle",
|
|
@@ -17228,7 +17275,7 @@ var parseProjectStatus = (value) => {
|
|
|
17228
17275
|
"cleanup_state"
|
|
17229
17276
|
]);
|
|
17230
17277
|
const handle = input["handle"];
|
|
17231
|
-
if (!isUlid(input["project_id"]) || !boundedString(handle,
|
|
17278
|
+
if (!isUlid(input["project_id"]) || typeof input["etag"] !== "string" || !/^"sha256-[a-f0-9]{64}"$/u.test(input["etag"]) || !boundedString(handle, 3, 59) || !/^[a-z0-9]+(?:-[a-z0-9]+){0,4}$/u.test(handle) || input["region"] !== "us" && input["region"] !== "eu" || input["lifecycle"] !== "active" && input["lifecycle"] !== "deleting" && input["lifecycle"] !== "deleted" || input["dev_url"] !== null && ![`https://dev-${handle}.dev.check.omh.st`, `https://dev-${handle}.check.omh.st`].includes(String(input["dev_url"])) || input["prod_url"] !== null && ![`https://${handle}.dev.check.omh.st`, `https://${handle}.check.omh.st`].includes(String(input["prod_url"])) || input["cleanup_state"] !== "not_started" && input["cleanup_state"] !== "pending" && input["cleanup_state"] !== "completed" && input["cleanup_state"] !== "reconciliation_required") {
|
|
17232
17279
|
throw new ResponseContractError();
|
|
17233
17280
|
}
|
|
17234
17281
|
if (input["source"] !== null)
|
|
@@ -17538,13 +17585,14 @@ var parseSafeProblem = (value) => {
|
|
|
17538
17585
|
if (typeof code !== "string" || !(code in PROBLEM_POLICY))
|
|
17539
17586
|
throw new ResponseContractError();
|
|
17540
17587
|
const policy = PROBLEM_POLICY[code];
|
|
17541
|
-
if (input["status"] !== policy.status || !boundedString(input["type"], 1, 512) || !isHttpsUrl(input["type"]) || !boundedString(input["title"], 1, 160) || !boundedString(input["request_id"], 1, 128) || typeof input["retryable"] !== "boolean" || !boundedString(input["suggested_action"], 1, 500) || input["detail"] !== void 0 && !boundedString(input["detail"], 1, 2e3) || input["instance"] !== void 0 && !boundedString(input["instance"], 1, 512)) {
|
|
17588
|
+
if (input["status"] !== policy.status || !boundedString(input["type"], 1, 512) || !isHttpsUrl(input["type"]) || !boundedString(input["title"], 1, 160) || !boundedString(input["request_id"], 1, 128) || /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(input["request_id"]) || typeof input["retryable"] !== "boolean" || !boundedString(input["suggested_action"], 1, 500) || input["detail"] !== void 0 && !boundedString(input["detail"], 1, 2e3) || input["instance"] !== void 0 && !boundedString(input["instance"], 1, 512)) {
|
|
17542
17589
|
throw new ResponseContractError();
|
|
17543
17590
|
}
|
|
17544
17591
|
const retryAfter = input["retry_after_seconds"];
|
|
17545
17592
|
if (retryAfter !== void 0 && (code !== "rate_limited" || typeof retryAfter !== "number" || !Number.isSafeInteger(retryAfter) || retryAfter < 1 || retryAfter > 86400))
|
|
17546
17593
|
throw new ResponseContractError();
|
|
17547
17594
|
return {
|
|
17595
|
+
requestId: input["request_id"],
|
|
17548
17596
|
code,
|
|
17549
17597
|
retryable: policy.retryable,
|
|
17550
17598
|
suggestedAction: (code === "framework_conversion_required" ? parseFrameworkConversionDetail(input["detail"]) ?? policy.action : policy.action) + (retryAfter === void 0 ? "" : ` Retry after ${String(retryAfter)} seconds.`),
|
|
@@ -17818,7 +17866,7 @@ function exact2(v, keys) {
|
|
|
17818
17866
|
// apps/product-cli/package.json
|
|
17819
17867
|
var package_default = {
|
|
17820
17868
|
name: "@ohmyhost/product-cli",
|
|
17821
|
-
version: "0.1.
|
|
17869
|
+
version: "0.1.13",
|
|
17822
17870
|
private: true,
|
|
17823
17871
|
ohmyhost: {
|
|
17824
17872
|
deployment: "production",
|
|
@@ -18182,6 +18230,7 @@ var HELP_USAGE = Object.freeze({
|
|
|
18182
18230
|
"project status": "ohmyhost project status --project ULID --json",
|
|
18183
18231
|
"project dev-access create": "ohmyhost project dev-access create --project ULID --json",
|
|
18184
18232
|
"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)",
|
|
18233
|
+
"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)",
|
|
18185
18234
|
"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.",
|
|
18186
18235
|
"database compute get": "ohmyhost database compute get --project ULID [--environment dev|prod] --json",
|
|
18187
18236
|
"database write": "ohmyhost database write --project ULID --environment dev|prod --statement-file PATH --idempotency-key KEY [--parameters-json JSON] --yes --json",
|
|
@@ -18458,6 +18507,8 @@ var ProductCli = class {
|
|
|
18458
18507
|
return await this.listProjects(command, signal);
|
|
18459
18508
|
if (command.kind === "project-handle-check")
|
|
18460
18509
|
return await this.checkProjectHandle(command, signal);
|
|
18510
|
+
if (command.kind === "project-handle-set")
|
|
18511
|
+
return await this.changeProjectHandle(command, signal);
|
|
18461
18512
|
if (command.kind === "database-compute-set") {
|
|
18462
18513
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
18463
18514
|
let operation = parseOperation(await api.changeDatabaseCompute(command));
|
|
@@ -18797,6 +18848,16 @@ var ProductCli = class {
|
|
|
18797
18848
|
}));
|
|
18798
18849
|
return success("project list", { status: "succeeded", projects });
|
|
18799
18850
|
}
|
|
18851
|
+
async changeProjectHandle(command, signal) {
|
|
18852
|
+
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
18853
|
+
const operation = parseOperation(await api.changeProjectHandle({
|
|
18854
|
+
projectId: command.projectId,
|
|
18855
|
+
handle: command.handle,
|
|
18856
|
+
ifMatch: command.ifMatch,
|
|
18857
|
+
idempotencyKey: command.idempotencyKey
|
|
18858
|
+
}));
|
|
18859
|
+
return success("project handle set", { status: "accepted", operation });
|
|
18860
|
+
}
|
|
18800
18861
|
async checkProjectHandle(command, signal) {
|
|
18801
18862
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
18802
18863
|
const handle = await api.checkProjectHandle({ handle: command.handle });
|
|
@@ -19306,7 +19367,7 @@ var classifyFailure = (command, error, signal) => {
|
|
|
19306
19367
|
if (looksLikeProblem(error)) {
|
|
19307
19368
|
try {
|
|
19308
19369
|
const problem = parseSafeProblem(error);
|
|
19309
|
-
return failure(command, 7, problem.code, problem.retryable, problem.suggestedAction, void 0, problem.retryAfterSeconds);
|
|
19370
|
+
return failure(command, 7, problem.code, problem.retryable, problem.suggestedAction, void 0, problem.retryAfterSeconds, problem.requestId);
|
|
19310
19371
|
} catch {
|
|
19311
19372
|
return failure(command, 7, "response_contract_invalid", false, "Retry only after the client or server contract is repaired.");
|
|
19312
19373
|
}
|
|
@@ -19326,7 +19387,7 @@ var success = (command, body) => ({
|
|
|
19326
19387
|
`,
|
|
19327
19388
|
stderr: ""
|
|
19328
19389
|
});
|
|
19329
|
-
var failure = (command, exitCode, code, retryable, suggestedAction, operation, retryAfterSeconds) => {
|
|
19390
|
+
var failure = (command, exitCode, code, retryable, suggestedAction, operation, retryAfterSeconds, requestId) => {
|
|
19330
19391
|
const document = {
|
|
19331
19392
|
version: 1,
|
|
19332
19393
|
command,
|
|
@@ -19337,6 +19398,7 @@ var failure = (command, exitCode, code, retryable, suggestedAction, operation, r
|
|
|
19337
19398
|
retryable,
|
|
19338
19399
|
suggested_action: suggestedAction,
|
|
19339
19400
|
...retryAfterSeconds === void 0 ? {} : { retry_after_seconds: retryAfterSeconds },
|
|
19401
|
+
...requestId === void 0 ? {} : { request_id: requestId },
|
|
19340
19402
|
...operation?.error === void 0 ? {} : { message: operation.error.message }
|
|
19341
19403
|
}
|
|
19342
19404
|
};
|
|
@@ -19408,6 +19470,8 @@ var parsedCommandName = (command) => {
|
|
|
19408
19470
|
return "project dev-access create";
|
|
19409
19471
|
if (command.kind === "project-handle-check")
|
|
19410
19472
|
return "project handle check";
|
|
19473
|
+
if (command.kind === "project-handle-set")
|
|
19474
|
+
return "project handle set";
|
|
19411
19475
|
if (command.kind === "database-write")
|
|
19412
19476
|
return "database write";
|
|
19413
19477
|
if (command.kind === "database-query")
|
|
@@ -21106,6 +21170,31 @@ var getCloudflareDnsAuthorizationStatus = (parameters, options) => {
|
|
|
21106
21170
|
...params
|
|
21107
21171
|
});
|
|
21108
21172
|
};
|
|
21173
|
+
var changeProjectHandle = (parameters, options) => {
|
|
21174
|
+
const params = buildClientParams([parameters], [
|
|
21175
|
+
{
|
|
21176
|
+
args: [
|
|
21177
|
+
{ in: "headers", key: "X-Request-Id" },
|
|
21178
|
+
{ in: "headers", key: "Idempotency-Key" },
|
|
21179
|
+
{ in: "headers", key: "If-Match" },
|
|
21180
|
+
{ in: "path", key: "project_id" },
|
|
21181
|
+
{ in: "body", key: "handle" }
|
|
21182
|
+
]
|
|
21183
|
+
}
|
|
21184
|
+
]);
|
|
21185
|
+
return (options?.client ?? client).put({
|
|
21186
|
+
responseStyle: "data",
|
|
21187
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
21188
|
+
url: "/v1/projects/{project_id}/handle",
|
|
21189
|
+
...options,
|
|
21190
|
+
...params,
|
|
21191
|
+
headers: {
|
|
21192
|
+
"Content-Type": "application/json",
|
|
21193
|
+
...options?.headers,
|
|
21194
|
+
...params.headers
|
|
21195
|
+
}
|
|
21196
|
+
});
|
|
21197
|
+
};
|
|
21109
21198
|
var configureProjectDomains = (parameters, options) => {
|
|
21110
21199
|
const params = buildClientParams([parameters], [
|
|
21111
21200
|
{
|
|
@@ -21852,6 +21941,14 @@ var GeneratedSdkProductApi = class {
|
|
|
21852
21941
|
async listProjects(input) {
|
|
21853
21942
|
return listProjects(input, { client: this.#client });
|
|
21854
21943
|
}
|
|
21944
|
+
async changeProjectHandle(input) {
|
|
21945
|
+
return changeProjectHandle({
|
|
21946
|
+
project_id: input.projectId,
|
|
21947
|
+
"Idempotency-Key": input.idempotencyKey,
|
|
21948
|
+
"If-Match": input.ifMatch,
|
|
21949
|
+
handle: input.handle
|
|
21950
|
+
}, { client: this.#client });
|
|
21951
|
+
}
|
|
21855
21952
|
async checkProjectHandle(input) {
|
|
21856
21953
|
return getProjectHandleAvailability({ handle: input.handle }, { client: this.#client });
|
|
21857
21954
|
}
|
|
@@ -22116,7 +22213,7 @@ var OwnerOnlyProjectLinkStore = class {
|
|
|
22116
22213
|
// apps/product-cli/dist/repository-init.js
|
|
22117
22214
|
import { createHash } from "node:crypto";
|
|
22118
22215
|
import { lstat as lstat2, open as open3, readFile, readdir, stat } from "node:fs/promises";
|
|
22119
|
-
import { posix, resolve as resolve2 } from "node:path";
|
|
22216
|
+
import { dirname as dirname2, posix, resolve as resolve2 } from "node:path";
|
|
22120
22217
|
|
|
22121
22218
|
// packages/contracts/dist/application-root.js
|
|
22122
22219
|
var APPLICATION_ROOT_PATTERN = /^(?:\.|[A-Za-z0-9][A-Za-z0-9._-]*(?:\/[A-Za-z0-9][A-Za-z0-9._-]*)*)$/u;
|
|
@@ -23363,6 +23460,24 @@ async function assertRepositoryRoot(root) {
|
|
|
23363
23460
|
if (!information.isDirectory()) {
|
|
23364
23461
|
throw new RepositoryInitError("repository_not_directory", "Repository path is not a directory");
|
|
23365
23462
|
}
|
|
23463
|
+
for (let ancestor = root; ; ancestor = dirname2(ancestor)) {
|
|
23464
|
+
let marker;
|
|
23465
|
+
try {
|
|
23466
|
+
marker = await lstat2(resolve2(ancestor, ".git"));
|
|
23467
|
+
} catch (error) {
|
|
23468
|
+
if (error.code !== "ENOENT") {
|
|
23469
|
+
throw new RepositoryInitError("repository_unreadable", "Could not determine the Git repository root");
|
|
23470
|
+
}
|
|
23471
|
+
}
|
|
23472
|
+
if (marker !== void 0) {
|
|
23473
|
+
if (ancestor !== root) {
|
|
23474
|
+
throw new RepositoryInitError("repository_root_required", "Run ohmyhost init from the Git repository root and select the application directory with --root; ohmyhost.yaml must be at the repository root.");
|
|
23475
|
+
}
|
|
23476
|
+
return;
|
|
23477
|
+
}
|
|
23478
|
+
if (dirname2(ancestor) === ancestor)
|
|
23479
|
+
return;
|
|
23480
|
+
}
|
|
23366
23481
|
}
|
|
23367
23482
|
async function collectRepositoryFiles(root) {
|
|
23368
23483
|
const files = [];
|
|
@@ -24340,7 +24455,7 @@ var runOfflineInit = async (command, io, diagnostics) => {
|
|
|
24340
24455
|
});
|
|
24341
24456
|
const code = error instanceof RepositoryInitBlockedError ? "repository_init_blocked" : error instanceof RepositoryInitConflictError ? "repository_configuration_exists" : known ? error.code : "internal_error";
|
|
24342
24457
|
const exitCode = error instanceof RepositoryInitBlockedError ? 9 : known ? 2 : 1;
|
|
24343
|
-
const result = errorDocument("init", exitCode, code, false, error instanceof RepositoryInitConflictError ? "Keep the existing ohmyhost.yaml or remove it explicitly before retrying." : error instanceof RepositoryInitError && error.code === "project_name_invalid" ? "Pass a lowercase deployment slug with '--project', then rerun init." : "Run 'ohmyhost init --dry-run --json' and resolve every reported blocker.");
|
|
24458
|
+
const result = errorDocument("init", exitCode, code, false, error instanceof RepositoryInitConflictError ? "Keep the existing ohmyhost.yaml or remove it explicitly before retrying." : error instanceof RepositoryInitError && error.code === "repository_root_required" ? "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." : error instanceof RepositoryInitError && error.code === "project_name_invalid" ? "Pass a lowercase deployment slug with '--project', then rerun init." : "Run 'ohmyhost init --dry-run --json' and resolve every reported blocker.");
|
|
24344
24459
|
io.writeStdout(result.stdout);
|
|
24345
24460
|
return result.exitCode;
|
|
24346
24461
|
}
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -254,6 +254,7 @@ interface CliErrorDocument {
|
|
|
254
254
|
readonly suggested_action: string;
|
|
255
255
|
readonly message?: string;
|
|
256
256
|
readonly retry_after_seconds?: number;
|
|
257
|
+
readonly request_id?: string;
|
|
257
258
|
};
|
|
258
259
|
}
|
|
259
260
|
|
|
@@ -312,6 +313,8 @@ const HELP_USAGE = Object.freeze({
|
|
|
312
313
|
"project dev-access create": "ohmyhost project dev-access create --project ULID --json",
|
|
313
314
|
"project handle check":
|
|
314
315
|
"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)",
|
|
316
|
+
"project handle set":
|
|
317
|
+
"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)",
|
|
315
318
|
"database compute set":
|
|
316
319
|
"ohmyhost database compute set --project ULID --environment dev|prod --profile standard|performance --idempotency-key KEY --yes [--wait] --json\n" +
|
|
317
320
|
" 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.",
|
|
@@ -691,6 +694,8 @@ export class ProductCli {
|
|
|
691
694
|
if (command.kind === "project-list") return await this.listProjects(command, signal);
|
|
692
695
|
if (command.kind === "project-handle-check")
|
|
693
696
|
return await this.checkProjectHandle(command, signal);
|
|
697
|
+
if (command.kind === "project-handle-set")
|
|
698
|
+
return await this.changeProjectHandle(command, signal);
|
|
694
699
|
if (command.kind === "database-compute-set") {
|
|
695
700
|
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
696
701
|
let operation = parseOperation(await api.changeDatabaseCompute(command));
|
|
@@ -1169,6 +1174,22 @@ export class ProductCli {
|
|
|
1169
1174
|
return success("project list", { status: "succeeded", projects });
|
|
1170
1175
|
}
|
|
1171
1176
|
|
|
1177
|
+
private async changeProjectHandle(
|
|
1178
|
+
command: Extract<ProductCliCommand, { kind: "project-handle-set" }>,
|
|
1179
|
+
signal: AbortSignal,
|
|
1180
|
+
): Promise<CliResult> {
|
|
1181
|
+
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
1182
|
+
const operation = parseOperation(
|
|
1183
|
+
await api.changeProjectHandle({
|
|
1184
|
+
projectId: command.projectId,
|
|
1185
|
+
handle: command.handle,
|
|
1186
|
+
ifMatch: command.ifMatch,
|
|
1187
|
+
idempotencyKey: command.idempotencyKey,
|
|
1188
|
+
}),
|
|
1189
|
+
);
|
|
1190
|
+
return success("project handle set", { status: "accepted", operation });
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1172
1193
|
private async checkProjectHandle(
|
|
1173
1194
|
command: Extract<ProductCliCommand, { kind: "project-handle-check" }>,
|
|
1174
1195
|
signal: AbortSignal,
|
|
@@ -2030,6 +2051,7 @@ const classifyFailure = (command: CommandLabel, error: unknown, signal: AbortSig
|
|
|
2030
2051
|
problem.suggestedAction,
|
|
2031
2052
|
undefined,
|
|
2032
2053
|
problem.retryAfterSeconds,
|
|
2054
|
+
problem.requestId,
|
|
2033
2055
|
);
|
|
2034
2056
|
} catch {
|
|
2035
2057
|
return failure(
|
|
@@ -2079,6 +2101,7 @@ const failure = (
|
|
|
2079
2101
|
suggestedAction: string,
|
|
2080
2102
|
operation?: ReturnType<typeof parseOperation>,
|
|
2081
2103
|
retryAfterSeconds?: number,
|
|
2104
|
+
requestId?: string,
|
|
2082
2105
|
): CliResult => {
|
|
2083
2106
|
const document: CliErrorDocument = {
|
|
2084
2107
|
version: 1,
|
|
@@ -2090,6 +2113,7 @@ const failure = (
|
|
|
2090
2113
|
retryable,
|
|
2091
2114
|
suggested_action: suggestedAction,
|
|
2092
2115
|
...(retryAfterSeconds === undefined ? {} : { retry_after_seconds: retryAfterSeconds }),
|
|
2116
|
+
...(requestId === undefined ? {} : { request_id: requestId }),
|
|
2093
2117
|
...(operation?.error === undefined ? {} : { message: operation.error.message }),
|
|
2094
2118
|
},
|
|
2095
2119
|
};
|
|
@@ -2129,6 +2153,7 @@ const parsedCommandName = (command: ProductCliCommand): CommandLabel => {
|
|
|
2129
2153
|
if (command.kind === "project-status") return "project status";
|
|
2130
2154
|
if (command.kind === "project-dev-access-create") return "project dev-access create";
|
|
2131
2155
|
if (command.kind === "project-handle-check") return "project handle check";
|
|
2156
|
+
if (command.kind === "project-handle-set") return "project handle set";
|
|
2132
2157
|
if (command.kind === "database-write") return "database write";
|
|
2133
2158
|
if (command.kind === "database-query") return "database query";
|
|
2134
2159
|
if (command.kind === "database-access-create") return "database access create";
|
|
@@ -132,6 +132,15 @@ export type ProductCliCommand =
|
|
|
132
132
|
readonly idempotencyKey: string;
|
|
133
133
|
readonly credentialStore: "native";
|
|
134
134
|
}
|
|
135
|
+
| {
|
|
136
|
+
/** Moves a project to a chosen address once its gateways serve it. */
|
|
137
|
+
readonly kind: "project-handle-set";
|
|
138
|
+
readonly projectId: string;
|
|
139
|
+
readonly handle: string;
|
|
140
|
+
readonly ifMatch: string;
|
|
141
|
+
readonly idempotencyKey: string;
|
|
142
|
+
readonly credentialStore: "native";
|
|
143
|
+
}
|
|
135
144
|
| {
|
|
136
145
|
/** Asks whether a project address is free; the candidate is sent exactly as the agent typed it. */
|
|
137
146
|
readonly kind: "project-handle-check";
|
|
@@ -494,6 +503,7 @@ export type RecognizedCommandLabel =
|
|
|
494
503
|
| "project status"
|
|
495
504
|
| "project dev-access create"
|
|
496
505
|
| "project handle check"
|
|
506
|
+
| "project handle set"
|
|
497
507
|
| "database write"
|
|
498
508
|
| "database query"
|
|
499
509
|
| "database access create"
|
|
@@ -585,6 +595,8 @@ export const recognizedCommandLabel = (argv: readonly string[]): RecognizedComma
|
|
|
585
595
|
if (value === "project" && values[0] === "status") return "project status";
|
|
586
596
|
if (value === "project" && values[0] === "handle" && values[1] === "check")
|
|
587
597
|
return "project handle check";
|
|
598
|
+
if (value === "project" && values[0] === "handle" && values[1] === "set")
|
|
599
|
+
return "project handle set";
|
|
588
600
|
if (value === "project" && values[0] === "dev-access" && values[1] === "create")
|
|
589
601
|
return "project dev-access create";
|
|
590
602
|
if (value === "database" && values[0] === "compute" && values[1] === "set")
|
|
@@ -1054,6 +1066,24 @@ export const parseProductCliCommand = (argv: readonly string[]): ProductCliComma
|
|
|
1054
1066
|
}
|
|
1055
1067
|
if (first === "project") {
|
|
1056
1068
|
const subcommand = values.shift();
|
|
1069
|
+
if (subcommand === "handle" && values[0] === "set") {
|
|
1070
|
+
values.shift();
|
|
1071
|
+
if (yes || wait || follow || stdin) throw new InvalidCommandError();
|
|
1072
|
+
const options = parseOptions(values, [
|
|
1073
|
+
"--project",
|
|
1074
|
+
"--handle",
|
|
1075
|
+
"--if-match",
|
|
1076
|
+
"--idempotency-key",
|
|
1077
|
+
]);
|
|
1078
|
+
return {
|
|
1079
|
+
kind: "project-handle-set",
|
|
1080
|
+
projectId: options["--project"],
|
|
1081
|
+
handle: options["--handle"],
|
|
1082
|
+
ifMatch: options["--if-match"],
|
|
1083
|
+
idempotencyKey: options["--idempotency-key"],
|
|
1084
|
+
credentialStore,
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1057
1087
|
if (subcommand === "handle" && values.shift() === "check") {
|
|
1058
1088
|
if (yes || wait || follow || stdin) throw new InvalidCommandError();
|
|
1059
1089
|
const options = parseOptions(values, ["--handle"]);
|
|
@@ -429,9 +429,11 @@ const runOfflineInit = async (
|
|
|
429
429
|
false,
|
|
430
430
|
error instanceof RepositoryInitConflictError
|
|
431
431
|
? "Keep the existing ohmyhost.yaml or remove it explicitly before retrying."
|
|
432
|
-
: error instanceof RepositoryInitError && error.code === "
|
|
433
|
-
? "
|
|
434
|
-
:
|
|
432
|
+
: error instanceof RepositoryInitError && error.code === "repository_root_required"
|
|
433
|
+
? "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."
|
|
434
|
+
: error instanceof RepositoryInitError && error.code === "project_name_invalid"
|
|
435
|
+
? "Pass a lowercase deployment slug with '--project', then rerun init."
|
|
436
|
+
: "Run 'ohmyhost init --dry-run --json' and resolve every reported blocker.",
|
|
435
437
|
);
|
|
436
438
|
io.writeStdout(result.stdout);
|
|
437
439
|
return result.exitCode;
|
|
@@ -83,6 +83,7 @@ import {
|
|
|
83
83
|
listEnvironmentSecrets,
|
|
84
84
|
listFunctionRuns,
|
|
85
85
|
listProjects,
|
|
86
|
+
changeProjectHandle,
|
|
86
87
|
getProjectHandleAvailability,
|
|
87
88
|
planDeployment,
|
|
88
89
|
planPaidProjectDomain,
|
|
@@ -211,6 +212,12 @@ export interface ProductApi {
|
|
|
211
212
|
getAccountProfile(): Promise<readonly PublicWorkspace[]>;
|
|
212
213
|
listProjects(input: { readonly cursor?: string; readonly limit: number }): Promise<ProjectPage>;
|
|
213
214
|
checkProjectHandle(input: { readonly handle: string }): Promise<ProjectHandleAvailability>;
|
|
215
|
+
changeProjectHandle(input: {
|
|
216
|
+
readonly projectId: string;
|
|
217
|
+
readonly handle: string;
|
|
218
|
+
readonly ifMatch: string;
|
|
219
|
+
readonly idempotencyKey: string;
|
|
220
|
+
}): Promise<Operation>;
|
|
214
221
|
getProjectStatus(projectId: string): Promise<ProjectStatus>;
|
|
215
222
|
writeProjectDatabase(
|
|
216
223
|
input: ProjectDatabaseWriteRequest & { projectId: string; idempotencyKey: string },
|
|
@@ -585,6 +592,23 @@ export class GeneratedSdkProductApi implements ProductApi {
|
|
|
585
592
|
return listProjects(input, { client: this.#client });
|
|
586
593
|
}
|
|
587
594
|
|
|
595
|
+
public async changeProjectHandle(input: {
|
|
596
|
+
readonly projectId: string;
|
|
597
|
+
readonly handle: string;
|
|
598
|
+
readonly ifMatch: string;
|
|
599
|
+
readonly idempotencyKey: string;
|
|
600
|
+
}): Promise<Operation> {
|
|
601
|
+
return changeProjectHandle(
|
|
602
|
+
{
|
|
603
|
+
project_id: input.projectId,
|
|
604
|
+
"Idempotency-Key": input.idempotencyKey,
|
|
605
|
+
"If-Match": input.ifMatch,
|
|
606
|
+
handle: input.handle,
|
|
607
|
+
},
|
|
608
|
+
{ client: this.#client },
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
|
|
588
612
|
public async checkProjectHandle(input: {
|
|
589
613
|
readonly handle: string;
|
|
590
614
|
}): Promise<ProjectHandleAvailability> {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { lstat, open, readFile, readdir, stat } from "node:fs/promises";
|
|
3
|
-
import { posix, resolve } from "node:path";
|
|
3
|
+
import { dirname, posix, resolve } from "node:path";
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
normalizeApplicationRoot,
|
|
@@ -876,6 +876,30 @@ async function assertRepositoryRoot(root: string): Promise<void> {
|
|
|
876
876
|
if (!information.isDirectory()) {
|
|
877
877
|
throw new RepositoryInitError("repository_not_directory", "Repository path is not a directory");
|
|
878
878
|
}
|
|
879
|
+
// A Git worktree uses a .git file; never read or follow that file's target.
|
|
880
|
+
for (let ancestor = root; ; ancestor = dirname(ancestor)) {
|
|
881
|
+
let marker;
|
|
882
|
+
try {
|
|
883
|
+
marker = await lstat(resolve(ancestor, ".git"));
|
|
884
|
+
} catch (error) {
|
|
885
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
886
|
+
throw new RepositoryInitError(
|
|
887
|
+
"repository_unreadable",
|
|
888
|
+
"Could not determine the Git repository root",
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
if (marker !== undefined) {
|
|
893
|
+
if (ancestor !== root) {
|
|
894
|
+
throw new RepositoryInitError(
|
|
895
|
+
"repository_root_required",
|
|
896
|
+
"Run ohmyhost init from the Git repository root and select the application directory with --root; ohmyhost.yaml must be at the repository root.",
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
if (dirname(ancestor) === ancestor) return;
|
|
902
|
+
}
|
|
879
903
|
}
|
|
880
904
|
|
|
881
905
|
async function collectRepositoryFiles(root: string): Promise<string[]> {
|
|
@@ -129,6 +129,29 @@ const PROBLEM_POLICY = {
|
|
|
129
129
|
retryable: true,
|
|
130
130
|
action: "Retry project creation with the same idempotency key.",
|
|
131
131
|
},
|
|
132
|
+
project_handle_taken: {
|
|
133
|
+
status: 409,
|
|
134
|
+
retryable: false,
|
|
135
|
+
action:
|
|
136
|
+
"Run 'ohmyhost project handle check' for a free address, then rename to one of the alternatives it returns.",
|
|
137
|
+
},
|
|
138
|
+
project_handle_invalid: {
|
|
139
|
+
status: 409,
|
|
140
|
+
retryable: false,
|
|
141
|
+
action:
|
|
142
|
+
"Choose one to five lowercase words of letters and digits, three to fifty-nine characters, avoiding reserved words.",
|
|
143
|
+
},
|
|
144
|
+
project_handle_unchanged: {
|
|
145
|
+
status: 409,
|
|
146
|
+
retryable: false,
|
|
147
|
+
action: "The project already answers on this address; no rename is needed.",
|
|
148
|
+
},
|
|
149
|
+
project_rename_blocked: {
|
|
150
|
+
status: 409,
|
|
151
|
+
retryable: true,
|
|
152
|
+
action:
|
|
153
|
+
"Wait for the running operation to finish, read the project again, then rename with the new ETag.",
|
|
154
|
+
},
|
|
132
155
|
project_identity_unavailable: {
|
|
133
156
|
status: 503,
|
|
134
157
|
retryable: true,
|
|
@@ -196,6 +219,12 @@ const PROBLEM_POLICY = {
|
|
|
196
219
|
action:
|
|
197
220
|
"Run 'ohmyhost init', apply the matching ohmyhost framework skill, then plan the new commit.",
|
|
198
221
|
},
|
|
222
|
+
repository_configuration_missing: {
|
|
223
|
+
status: 409,
|
|
224
|
+
retryable: false,
|
|
225
|
+
action:
|
|
226
|
+
"Place ohmyhost.yaml at the Git repository root and set applicationRoot to the application directory (for example website). Commit and push the change, then plan the new commit.",
|
|
227
|
+
},
|
|
199
228
|
migration_filename_noncanonical: {
|
|
200
229
|
status: 409,
|
|
201
230
|
retryable: false,
|
|
@@ -278,6 +307,7 @@ export class ResponseContractError extends Error {
|
|
|
278
307
|
}
|
|
279
308
|
|
|
280
309
|
export interface SafeProblem {
|
|
310
|
+
readonly requestId: string;
|
|
281
311
|
readonly retryAfterSeconds?: number;
|
|
282
312
|
readonly code: ProblemCode;
|
|
283
313
|
readonly retryable: boolean;
|
|
@@ -676,9 +706,9 @@ export const parseDevAccessTicket = (value: unknown): PublicDevAccessTicket => {
|
|
|
676
706
|
!isUlid(input["project_id"]) ||
|
|
677
707
|
!isUlid(input["environment_id"]) ||
|
|
678
708
|
typeof origin !== "string" ||
|
|
679
|
-
!/^https:\/\/dev-[a-
|
|
709
|
+
!/^https:\/\/dev-[a-z0-9]+(?:-[a-z0-9]+){0,4}\.(?:dev\.)?check\.omh\.st$/u.test(origin) ||
|
|
680
710
|
typeof redeemUrl !== "string" ||
|
|
681
|
-
!/^https:\/\/dev-[a-
|
|
711
|
+
!/^https:\/\/dev-[a-z0-9]+(?:-[a-z0-9]+){0,4}\.(?:dev\.)?check\.omh\.st\/\.ohmyhost\/dev-access\/redeem\?stage=dev&ticket=[A-Za-z0-9_-]{43}$/u.test(
|
|
682
712
|
redeemUrl,
|
|
683
713
|
) ||
|
|
684
714
|
new URL(redeemUrl).origin !== origin ||
|
|
@@ -880,8 +910,8 @@ const parseProject = (value: unknown): PublicProject => {
|
|
|
880
910
|
!isUlid(input["organization_id"]) ||
|
|
881
911
|
!boundedString(input["name"], 1, 128) ||
|
|
882
912
|
input["name"] !== input["name"].trim() ||
|
|
883
|
-
!boundedString(input["handle"],
|
|
884
|
-
!/^[a-
|
|
913
|
+
!boundedString(input["handle"], 3, 59) ||
|
|
914
|
+
!/^[a-z0-9]+(?:-[a-z0-9]+){0,4}$/u.test(input["handle"]) ||
|
|
885
915
|
(input["data_mode"] !== "shared" && input["data_mode"] !== "isolated") ||
|
|
886
916
|
(input["region"] !== "us" && input["region"] !== "eu") ||
|
|
887
917
|
!isIsoInstant(input["created_at"])
|
|
@@ -910,6 +940,7 @@ export const parseProjectPage = (
|
|
|
910
940
|
export const parseProjectStatus = (value: unknown): Readonly<Record<string, unknown>> => {
|
|
911
941
|
const input = exactRecord(value, [
|
|
912
942
|
"project_id",
|
|
943
|
+
"etag",
|
|
913
944
|
"handle",
|
|
914
945
|
"region",
|
|
915
946
|
"lifecycle",
|
|
@@ -925,8 +956,10 @@ export const parseProjectStatus = (value: unknown): Readonly<Record<string, unkn
|
|
|
925
956
|
const handle = input["handle"];
|
|
926
957
|
if (
|
|
927
958
|
!isUlid(input["project_id"]) ||
|
|
928
|
-
|
|
929
|
-
!/^
|
|
959
|
+
typeof input["etag"] !== "string" ||
|
|
960
|
+
!/^"sha256-[a-f0-9]{64}"$/u.test(input["etag"]) ||
|
|
961
|
+
!boundedString(handle, 3, 59) ||
|
|
962
|
+
!/^[a-z0-9]+(?:-[a-z0-9]+){0,4}$/u.test(handle) ||
|
|
930
963
|
(input["region"] !== "us" && input["region"] !== "eu") ||
|
|
931
964
|
(input["lifecycle"] !== "active" &&
|
|
932
965
|
input["lifecycle"] !== "deleting" &&
|
|
@@ -1540,6 +1573,7 @@ export const parseSafeProblem = (value: unknown): SafeProblem => {
|
|
|
1540
1573
|
!isHttpsUrl(input["type"]) ||
|
|
1541
1574
|
!boundedString(input["title"], 1, 160) ||
|
|
1542
1575
|
!boundedString(input["request_id"], 1, 128) ||
|
|
1576
|
+
/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(input["request_id"]) ||
|
|
1543
1577
|
typeof input["retryable"] !== "boolean" ||
|
|
1544
1578
|
!boundedString(input["suggested_action"], 1, 500) ||
|
|
1545
1579
|
(input["detail"] !== undefined && !boundedString(input["detail"], 1, 2_000)) ||
|
|
@@ -1558,6 +1592,7 @@ export const parseSafeProblem = (value: unknown): SafeProblem => {
|
|
|
1558
1592
|
)
|
|
1559
1593
|
throw new ResponseContractError();
|
|
1560
1594
|
return {
|
|
1595
|
+
requestId: input["request_id"],
|
|
1561
1596
|
code: code as ProblemCode,
|
|
1562
1597
|
retryable: policy.retryable,
|
|
1563
1598
|
suggestedAction:
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
export {
|
|
4
4
|
applyPaidProjectDomain,
|
|
5
5
|
changeDatabaseCompute,
|
|
6
|
+
changeProjectHandle,
|
|
6
7
|
completeCloudflareDnsAuthorization,
|
|
7
8
|
completeGithubSourceAuthorization,
|
|
8
9
|
configureBillingRecharge,
|
|
@@ -101,6 +102,11 @@ export type {
|
|
|
101
102
|
ChangeDatabaseComputeErrors,
|
|
102
103
|
ChangeDatabaseComputeResponse,
|
|
103
104
|
ChangeDatabaseComputeResponses,
|
|
105
|
+
ChangeProjectHandleData,
|
|
106
|
+
ChangeProjectHandleError,
|
|
107
|
+
ChangeProjectHandleErrors,
|
|
108
|
+
ChangeProjectHandleResponse,
|
|
109
|
+
ChangeProjectHandleResponses,
|
|
104
110
|
ClientOptions,
|
|
105
111
|
CloudflareDnsAuthorization,
|
|
106
112
|
CloudflareDnsAuthorizationStatus,
|
|
@@ -15,6 +15,8 @@ import type {
|
|
|
15
15
|
ApplyPaidProjectDomainResponses,
|
|
16
16
|
ChangeDatabaseComputeErrors,
|
|
17
17
|
ChangeDatabaseComputeResponses,
|
|
18
|
+
ChangeProjectHandleErrors,
|
|
19
|
+
ChangeProjectHandleResponses,
|
|
18
20
|
CompleteCloudflareDnsAuthorizationErrors,
|
|
19
21
|
CompleteCloudflareDnsAuthorizationResponses,
|
|
20
22
|
CompleteGithubSourceAuthorizationErrors,
|
|
@@ -2070,6 +2072,54 @@ export const getCloudflareDnsAuthorizationStatus = <ThrowOnError extends boolean
|
|
|
2070
2072
|
});
|
|
2071
2073
|
};
|
|
2072
2074
|
|
|
2075
|
+
/**
|
|
2076
|
+
* Move a project to a chosen address
|
|
2077
|
+
*
|
|
2078
|
+
* Starts a durable operation that re-publishes the project's Dev and Prod gateways at the chosen address and stores it only once both serve it. The previous address stops answering and returns to the pool for any project to claim. Ask GET /v1/project-handles/{handle} first; a taken, unusable or unchanged address is refused before any gateway moves, and a project with an operation in flight is refused until it finishes.
|
|
2079
|
+
*/
|
|
2080
|
+
export const changeProjectHandle = <ThrowOnError extends boolean = true>(
|
|
2081
|
+
parameters: {
|
|
2082
|
+
"X-Request-Id"?: string;
|
|
2083
|
+
"Idempotency-Key": string;
|
|
2084
|
+
"If-Match": string;
|
|
2085
|
+
project_id: Ulid;
|
|
2086
|
+
handle: string;
|
|
2087
|
+
},
|
|
2088
|
+
options?: Options<never, ThrowOnError>,
|
|
2089
|
+
): RequestResult<ChangeProjectHandleResponses, ChangeProjectHandleErrors, ThrowOnError, "data"> => {
|
|
2090
|
+
const params = buildClientParams(
|
|
2091
|
+
[parameters],
|
|
2092
|
+
[
|
|
2093
|
+
{
|
|
2094
|
+
args: [
|
|
2095
|
+
{ in: "headers", key: "X-Request-Id" },
|
|
2096
|
+
{ in: "headers", key: "Idempotency-Key" },
|
|
2097
|
+
{ in: "headers", key: "If-Match" },
|
|
2098
|
+
{ in: "path", key: "project_id" },
|
|
2099
|
+
{ in: "body", key: "handle" },
|
|
2100
|
+
],
|
|
2101
|
+
},
|
|
2102
|
+
],
|
|
2103
|
+
);
|
|
2104
|
+
return (options?.client ?? client).put<
|
|
2105
|
+
ChangeProjectHandleResponses,
|
|
2106
|
+
ChangeProjectHandleErrors,
|
|
2107
|
+
ThrowOnError,
|
|
2108
|
+
"data"
|
|
2109
|
+
>({
|
|
2110
|
+
responseStyle: "data",
|
|
2111
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
2112
|
+
url: "/v1/projects/{project_id}/handle",
|
|
2113
|
+
...options,
|
|
2114
|
+
...params,
|
|
2115
|
+
headers: {
|
|
2116
|
+
"Content-Type": "application/json",
|
|
2117
|
+
...options?.headers,
|
|
2118
|
+
...params.headers,
|
|
2119
|
+
},
|
|
2120
|
+
});
|
|
2121
|
+
};
|
|
2122
|
+
|
|
2073
2123
|
/**
|
|
2074
2124
|
* Apply the server-derived project domains
|
|
2075
2125
|
*
|
|
@@ -865,6 +865,10 @@ export type ProjectHandleAvailability = {
|
|
|
865
865
|
};
|
|
866
866
|
|
|
867
867
|
export type ProjectStatus = {
|
|
868
|
+
/**
|
|
869
|
+
* Optimistic-concurrency token for changing this project address; use it as If-Match on the handle endpoint.
|
|
870
|
+
*/
|
|
871
|
+
etag: string;
|
|
868
872
|
project_id: Ulid;
|
|
869
873
|
handle: string;
|
|
870
874
|
region: HostingRegion;
|
|
@@ -912,9 +916,14 @@ export type ProblemDetails = {
|
|
|
912
916
|
| "mail_domain_required"
|
|
913
917
|
| "storage_jurisdiction_conflict"
|
|
914
918
|
| "framework_conversion_required"
|
|
919
|
+
| "repository_configuration_missing"
|
|
915
920
|
| "migration_filename_noncanonical"
|
|
916
921
|
| "environment_secret_mutation_blocked"
|
|
917
922
|
| "workers_runtime_incompatible"
|
|
923
|
+
| "project_handle_invalid"
|
|
924
|
+
| "project_handle_taken"
|
|
925
|
+
| "project_handle_unchanged"
|
|
926
|
+
| "project_rename_blocked"
|
|
918
927
|
| "payload_too_large"
|
|
919
928
|
| "rate_limited"
|
|
920
929
|
| "insufficient_organization_credits"
|
|
@@ -3812,6 +3821,80 @@ export type GetCloudflareDnsAuthorizationStatusResponses = {
|
|
|
3812
3821
|
export type GetCloudflareDnsAuthorizationStatusResponse =
|
|
3813
3822
|
GetCloudflareDnsAuthorizationStatusResponses[keyof GetCloudflareDnsAuthorizationStatusResponses];
|
|
3814
3823
|
|
|
3824
|
+
export type ChangeProjectHandleData = {
|
|
3825
|
+
body: {
|
|
3826
|
+
/**
|
|
3827
|
+
* One to five lowercase words of letters and digits.
|
|
3828
|
+
*/
|
|
3829
|
+
handle: string;
|
|
3830
|
+
};
|
|
3831
|
+
headers: {
|
|
3832
|
+
/**
|
|
3833
|
+
* Optional caller-provided correlation identifier.
|
|
3834
|
+
*/
|
|
3835
|
+
"X-Request-Id"?: string;
|
|
3836
|
+
/**
|
|
3837
|
+
* Identifies one mutation and its canonical request payload.
|
|
3838
|
+
*/
|
|
3839
|
+
"Idempotency-Key": string;
|
|
3840
|
+
/**
|
|
3841
|
+
* Exact ETag returned for the current project or deployment state.
|
|
3842
|
+
*/
|
|
3843
|
+
"If-Match": string;
|
|
3844
|
+
};
|
|
3845
|
+
path: {
|
|
3846
|
+
/**
|
|
3847
|
+
* Project identifier.
|
|
3848
|
+
*/
|
|
3849
|
+
project_id: Ulid;
|
|
3850
|
+
};
|
|
3851
|
+
query?: never;
|
|
3852
|
+
url: "/v1/projects/{project_id}/handle";
|
|
3853
|
+
};
|
|
3854
|
+
|
|
3855
|
+
export type ChangeProjectHandleErrors = {
|
|
3856
|
+
/**
|
|
3857
|
+
* The request failed.
|
|
3858
|
+
*/
|
|
3859
|
+
400: ProblemDetails;
|
|
3860
|
+
/**
|
|
3861
|
+
* The request failed.
|
|
3862
|
+
*/
|
|
3863
|
+
401: ProblemDetails;
|
|
3864
|
+
/**
|
|
3865
|
+
* The request failed.
|
|
3866
|
+
*/
|
|
3867
|
+
403: ProblemDetails;
|
|
3868
|
+
/**
|
|
3869
|
+
* The resource does not exist or is not visible to the authenticated principal.
|
|
3870
|
+
*/
|
|
3871
|
+
404: ProblemDetails;
|
|
3872
|
+
/**
|
|
3873
|
+
* The idempotency key was already used with a different canonical request.
|
|
3874
|
+
*/
|
|
3875
|
+
409: IdempotencyConflictProblem;
|
|
3876
|
+
/**
|
|
3877
|
+
* The request failed.
|
|
3878
|
+
*/
|
|
3879
|
+
429: ProblemDetails;
|
|
3880
|
+
/**
|
|
3881
|
+
* The request failed.
|
|
3882
|
+
*/
|
|
3883
|
+
503: ProblemDetails;
|
|
3884
|
+
};
|
|
3885
|
+
|
|
3886
|
+
export type ChangeProjectHandleError = ChangeProjectHandleErrors[keyof ChangeProjectHandleErrors];
|
|
3887
|
+
|
|
3888
|
+
export type ChangeProjectHandleResponses = {
|
|
3889
|
+
/**
|
|
3890
|
+
* The mutation was accepted for asynchronous processing.
|
|
3891
|
+
*/
|
|
3892
|
+
202: Operation;
|
|
3893
|
+
};
|
|
3894
|
+
|
|
3895
|
+
export type ChangeProjectHandleResponse =
|
|
3896
|
+
ChangeProjectHandleResponses[keyof ChangeProjectHandleResponses];
|
|
3897
|
+
|
|
3815
3898
|
export type ConfigureProjectDomainsData = {
|
|
3816
3899
|
body?: never;
|
|
3817
3900
|
headers: {
|