@amerged/ohmyhost-mcp 0.1.23 → 0.1.24
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 +5 -2
- package/dist/{chunk-P44YTAKK.js → chunk-EEFUMPKG.js} +25 -24
- package/dist/index.js +1 -1
- package/dist/stdio-main.js +731 -266
- package/package.json +1 -1
- package/src/apps/mcp/package.json +47 -0
- package/src/apps/mcp/{local-client.ts → src/local-client.ts} +69 -12
- package/src/apps/mcp/{local-server.ts → src/local-server.ts} +324 -142
- 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} +79 -29
- 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/agent-skills/package.json +24 -0
- package/src/packages/agent-skills/src/generated-skill-resources.ts +165 -0
- package/src/packages/agent-skills/{index.ts → src/index.ts} +1 -1
- 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 +36 -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} +88 -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 +112 -3
- 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/packages/agent-skills/generated-skill-resources.ts +0 -165
- /package/src/apps/mcp/{index.ts → src/index.ts} +0 -0
- /package/src/apps/mcp/{stdio-main.ts → src/stdio-main.ts} +0 -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
|
@@ -1,8 +1,30 @@
|
|
|
1
|
+
import { isUserSecretName } from "@ohmyhost/contracts/secret-names";
|
|
2
|
+
import {
|
|
3
|
+
isProjectDataChangeKind,
|
|
4
|
+
type ProjectDataChangeKind,
|
|
5
|
+
} from "@ohmyhost/contracts/project-data-changes";
|
|
1
6
|
import type { ManagedMailCommand } from "@ohmyhost/sdk-ts";
|
|
2
7
|
import { USER_API_KEY_ID } from "@ohmyhost/contracts/user-api-keys";
|
|
3
8
|
import { WORKOS_USER_API_KEY_PATTERN } from "@ohmyhost/workos-auth-contracts/user-api-keys";
|
|
4
9
|
import { isProfileName, isProfileUserId } from "./profile-store.js";
|
|
5
10
|
export type ProductCliCommand =
|
|
11
|
+
| {
|
|
12
|
+
readonly kind: "project-data-plan";
|
|
13
|
+
readonly projectId: string;
|
|
14
|
+
readonly change: ProjectDataChangeKind;
|
|
15
|
+
readonly credentialStore: "native";
|
|
16
|
+
}
|
|
17
|
+
| {
|
|
18
|
+
readonly kind: "project-data-change";
|
|
19
|
+
readonly projectId: string;
|
|
20
|
+
readonly change: ProjectDataChangeKind;
|
|
21
|
+
readonly ifMatch: string;
|
|
22
|
+
readonly confirmationToken: string;
|
|
23
|
+
readonly idempotencyKey: string;
|
|
24
|
+
readonly yes: true;
|
|
25
|
+
readonly wait: boolean;
|
|
26
|
+
readonly credentialStore: "native";
|
|
27
|
+
}
|
|
6
28
|
| {
|
|
7
29
|
readonly kind: "github-connect";
|
|
8
30
|
readonly organizationId: string;
|
|
@@ -550,6 +572,8 @@ export class InvalidCommandError extends Error {
|
|
|
550
572
|
}
|
|
551
573
|
|
|
552
574
|
export type RecognizedCommandLabel =
|
|
575
|
+
| "project data plan"
|
|
576
|
+
| "project data change"
|
|
553
577
|
| "database compute set"
|
|
554
578
|
| "database compute get"
|
|
555
579
|
| "project context"
|
|
@@ -696,6 +720,10 @@ export const recognizedCommandLabel = (argv: readonly string[]): RecognizedComma
|
|
|
696
720
|
if (value === "feedback" && values[0] === "submit") return "feedback submit";
|
|
697
721
|
if (value === "feedback" && values[0] === "status") return "feedback status";
|
|
698
722
|
if (value === "project" && values[0] === "create") return "project create";
|
|
723
|
+
if (value === "project" && values[0] === "data" && values[1] === "plan")
|
|
724
|
+
return "project data plan";
|
|
725
|
+
if (value === "project" && values[0] === "data" && values[1] === "change")
|
|
726
|
+
return "project data change";
|
|
699
727
|
if (value === "organization" && values[0] === "create") return "organization create";
|
|
700
728
|
if (value === "organization" && values[0] === "list") return "organization list";
|
|
701
729
|
if (value === "organization" && values[0] === "use") return "organization use";
|
|
@@ -1242,6 +1270,33 @@ export const parseProductCliCommand = (argv: readonly string[]): ProductCliComma
|
|
|
1242
1270
|
}
|
|
1243
1271
|
if (first === "project") {
|
|
1244
1272
|
const subcommand = values.shift();
|
|
1273
|
+
if (subcommand === "data") {
|
|
1274
|
+
const action = values.shift();
|
|
1275
|
+
if (follow || stdin || (action !== "plan" && action !== "change"))
|
|
1276
|
+
throw new InvalidCommandError();
|
|
1277
|
+
if ((action === "plan" && (yes || wait)) || (action === "change" && !yes))
|
|
1278
|
+
throw new InvalidCommandError();
|
|
1279
|
+
const options = parseOptions(
|
|
1280
|
+
values,
|
|
1281
|
+
action === "plan"
|
|
1282
|
+
? ["--project", "--change"]
|
|
1283
|
+
: ["--project", "--change", "--if-match", "--confirmation-token", "--idempotency-key"],
|
|
1284
|
+
);
|
|
1285
|
+
const change = options["--change"];
|
|
1286
|
+
if (!isProjectDataChangeKind(change)) throw new InvalidCommandError();
|
|
1287
|
+
const common = { projectId: options["--project"], change, credentialStore };
|
|
1288
|
+
return action === "plan"
|
|
1289
|
+
? { kind: "project-data-plan", ...common }
|
|
1290
|
+
: {
|
|
1291
|
+
kind: "project-data-change",
|
|
1292
|
+
...common,
|
|
1293
|
+
ifMatch: options["--if-match"],
|
|
1294
|
+
confirmationToken: options["--confirmation-token"],
|
|
1295
|
+
idempotencyKey: options["--idempotency-key"],
|
|
1296
|
+
yes: true,
|
|
1297
|
+
wait,
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1245
1300
|
if (subcommand === "handle" && values[0] === "set") {
|
|
1246
1301
|
values.shift();
|
|
1247
1302
|
if (yes || wait || follow || stdin) throw new InvalidCommandError();
|
|
@@ -1928,7 +1983,7 @@ export const parseProductCliCommand = (argv: readonly string[]): ProductCliComma
|
|
|
1928
1983
|
if (yes || follow || stdin) throw new InvalidCommandError();
|
|
1929
1984
|
values.shift();
|
|
1930
1985
|
const name = values.shift();
|
|
1931
|
-
if (!isUserSecretName(name)) throw new InvalidCommandError();
|
|
1986
|
+
if (!isUserSecretName(name, "delete")) throw new InvalidCommandError();
|
|
1932
1987
|
const options = parseOptions(values, ["--project", "--environment", "--idempotency-key"]);
|
|
1933
1988
|
return {
|
|
1934
1989
|
kind: "secret-delete",
|
|
@@ -2120,6 +2175,7 @@ const parseOptions = <T extends string>(
|
|
|
2120
2175
|
if (option === undefined || !allowed.includes(option as T) || !isValue(value)) {
|
|
2121
2176
|
throw new InvalidCommandError();
|
|
2122
2177
|
}
|
|
2178
|
+
if (option === "--idempotency-key" && value.length > 128) throw new InvalidCommandError();
|
|
2123
2179
|
if (option in result) throw new InvalidCommandError();
|
|
2124
2180
|
result[option as T] = value;
|
|
2125
2181
|
}
|
|
@@ -2144,6 +2200,7 @@ const parseOptionalOptions = <T extends string>(
|
|
|
2144
2200
|
if (option === undefined || !allowed.includes(option as T) || !isValue(value)) {
|
|
2145
2201
|
throw new InvalidCommandError();
|
|
2146
2202
|
}
|
|
2203
|
+
if (option === "--idempotency-key" && value.length > 128) throw new InvalidCommandError();
|
|
2147
2204
|
if (option in result) throw new InvalidCommandError();
|
|
2148
2205
|
result[option as T] = value;
|
|
2149
2206
|
}
|
|
@@ -2220,13 +2277,3 @@ const validPaidHostname = (value: string): boolean =>
|
|
|
2220
2277
|
!value.endsWith(".omh.st") &&
|
|
2221
2278
|
value !== "ohmyho.st" &&
|
|
2222
2279
|
!value.endsWith(".ohmyho.st");
|
|
2223
|
-
|
|
2224
|
-
const isUserSecretName = (value: string | undefined): value is string =>
|
|
2225
|
-
isValue(value) &&
|
|
2226
|
-
/^[A-Z][A-Z0-9_]{0,127}$/u.test(value) &&
|
|
2227
|
-
value !== "DATABASE_URL" &&
|
|
2228
|
-
value !== "HYPERDRIVE" &&
|
|
2229
|
-
value !== "BETTER_AUTH_SECRET" &&
|
|
2230
|
-
value !== "BETTER_AUTH_URL" &&
|
|
2231
|
-
value !== "OHMYHOST_MAIL_KEY" &&
|
|
2232
|
-
value !== "OHMYHOST_MAIL_GATEWAY_URL";
|
|
@@ -21,6 +21,11 @@ import {
|
|
|
21
21
|
ProductCli,
|
|
22
22
|
classifyCliFailure,
|
|
23
23
|
cliMetadataResult,
|
|
24
|
+
CREDENTIAL_STORE_UNAVAILABLE_ACTION,
|
|
25
|
+
endedLoginAction,
|
|
26
|
+
INTERNAL_ERROR_ACTION,
|
|
27
|
+
invalidCommandAction,
|
|
28
|
+
refreshFailedAction,
|
|
24
29
|
NetworkTransportError,
|
|
25
30
|
LifecycleTransportError,
|
|
26
31
|
TokenRefreshRejectedError,
|
|
@@ -550,7 +555,7 @@ export const runProductionCli = async (
|
|
|
550
555
|
args = extracted.argv;
|
|
551
556
|
profileName = extracted.profileName;
|
|
552
557
|
} catch {
|
|
553
|
-
return written(invalidCommandFailure(
|
|
558
|
+
return written(invalidCommandFailure(argv, environment));
|
|
554
559
|
}
|
|
555
560
|
const workingDirectory = overrides.workingDirectory ?? process.cwd();
|
|
556
561
|
let command: ProductCliCommand;
|
|
@@ -607,14 +612,20 @@ export const runProductionCli = async (
|
|
|
607
612
|
recognizedCommandLabel(args),
|
|
608
613
|
3,
|
|
609
614
|
"credential_store_unavailable",
|
|
610
|
-
|
|
611
|
-
|
|
615
|
+
false,
|
|
616
|
+
CREDENTIAL_STORE_UNAVAILABLE_ACTION,
|
|
612
617
|
),
|
|
613
618
|
);
|
|
614
619
|
const result =
|
|
615
620
|
error instanceof InvalidCommandError
|
|
616
|
-
? invalidCommandFailure(
|
|
617
|
-
: errorDocument(
|
|
621
|
+
? invalidCommandFailure(args, environment)
|
|
622
|
+
: errorDocument(
|
|
623
|
+
recognizedCommandLabel(args),
|
|
624
|
+
1,
|
|
625
|
+
"internal_error",
|
|
626
|
+
false,
|
|
627
|
+
INTERNAL_ERROR_ACTION,
|
|
628
|
+
);
|
|
618
629
|
if (!(error instanceof InvalidCommandError)) {
|
|
619
630
|
diagnostics.record({
|
|
620
631
|
correlationId: randomUUID(),
|
|
@@ -626,7 +637,16 @@ export const runProductionCli = async (
|
|
|
626
637
|
return written(result);
|
|
627
638
|
}
|
|
628
639
|
if (command.kind === "init") {
|
|
629
|
-
if (profileName !== undefined)
|
|
640
|
+
if (profileName !== undefined)
|
|
641
|
+
return written(
|
|
642
|
+
errorDocument(
|
|
643
|
+
"init",
|
|
644
|
+
2,
|
|
645
|
+
"invalid_command",
|
|
646
|
+
false,
|
|
647
|
+
"ohmyhost init runs offline and uses no login: remove --profile-name and run it again.",
|
|
648
|
+
),
|
|
649
|
+
);
|
|
630
650
|
return runOfflineInit(command, io, diagnostics);
|
|
631
651
|
}
|
|
632
652
|
if (command.kind === "help" || command.kind === "version") {
|
|
@@ -708,6 +728,9 @@ export const runProductionCli = async (
|
|
|
708
728
|
overrides.profiles ?? new NativeKeyringProfileRegistry(undefined, platform.credentialService);
|
|
709
729
|
const linkDirectory = join(workingDirectory, platform.linkDirectory);
|
|
710
730
|
const projectId = commandProjectId(command);
|
|
731
|
+
// A command that named its project read no link yet, but the link still says whose project it is.
|
|
732
|
+
if (projectId !== undefined && linked === null)
|
|
733
|
+
links = await readProjectLinks(linkDirectory, platform.apiOrigin);
|
|
711
734
|
// The organization this command is about: named on the command, or recorded by this checkout's
|
|
712
735
|
// link of its project. It narrows the saved logins and is checked against OHMYHOST_TOKEN.
|
|
713
736
|
const organizationId =
|
|
@@ -803,9 +826,21 @@ export const runProductionCli = async (
|
|
|
803
826
|
}
|
|
804
827
|
}
|
|
805
828
|
} catch (error) {
|
|
806
|
-
|
|
807
|
-
|
|
829
|
+
const result = compositionFailure(
|
|
830
|
+
recognizedCommandLabel(args),
|
|
831
|
+
error,
|
|
832
|
+
signal,
|
|
833
|
+
platform.commandPrefix,
|
|
834
|
+
profile,
|
|
808
835
|
);
|
|
836
|
+
if (result.exitCode === 1)
|
|
837
|
+
diagnostics.record({
|
|
838
|
+
correlationId: randomUUID(),
|
|
839
|
+
category: "internal",
|
|
840
|
+
subsystem: "authentication",
|
|
841
|
+
errorKind: "unexpected",
|
|
842
|
+
});
|
|
843
|
+
return written(result);
|
|
809
844
|
}
|
|
810
845
|
const selected = profile;
|
|
811
846
|
const productApiOrigin = platform.apiOrigin;
|
|
@@ -939,6 +974,7 @@ const compositionFailure = (
|
|
|
939
974
|
error: unknown,
|
|
940
975
|
signal: AbortSignal,
|
|
941
976
|
commandPrefix: string,
|
|
977
|
+
profile: LocalProfile | undefined,
|
|
942
978
|
): CliResult => {
|
|
943
979
|
if (error instanceof ProfileSelectionError)
|
|
944
980
|
return errorDocument(command, error.exitCode, error.code, false, error.suggestedAction);
|
|
@@ -957,14 +993,14 @@ const compositionFailure = (
|
|
|
957
993
|
5,
|
|
958
994
|
"authentication_required",
|
|
959
995
|
false,
|
|
960
|
-
|
|
996
|
+
endedLoginAction(commandPrefix, profile),
|
|
961
997
|
)
|
|
962
998
|
: errorDocument(
|
|
963
999
|
command,
|
|
964
1000
|
5,
|
|
965
1001
|
"token_refresh_failed",
|
|
966
1002
|
true,
|
|
967
|
-
|
|
1003
|
+
refreshFailedAction(commandPrefix, profile),
|
|
968
1004
|
);
|
|
969
1005
|
return classifyCliFailure(command, error, signal);
|
|
970
1006
|
};
|
|
@@ -1006,6 +1042,7 @@ const runOfflineInit = async (
|
|
|
1006
1042
|
// The functions runtime reports its own bindings and packages; dropping it left a
|
|
1007
1043
|
// storage-enabled functions app with no contract to read at all.
|
|
1008
1044
|
worker: result.worker,
|
|
1045
|
+
runtime: result.runtime,
|
|
1009
1046
|
})}\n`,
|
|
1010
1047
|
);
|
|
1011
1048
|
return blocked ? 9 : 0;
|
|
@@ -1031,19 +1068,42 @@ const runOfflineInit = async (
|
|
|
1031
1068
|
exitCode,
|
|
1032
1069
|
code,
|
|
1033
1070
|
false,
|
|
1034
|
-
error
|
|
1035
|
-
? "Keep the existing ohmyhost.yaml or remove it explicitly before retrying."
|
|
1036
|
-
: error instanceof RepositoryInitError && error.code === "repository_root_required"
|
|
1037
|
-
? "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."
|
|
1038
|
-
: error instanceof RepositoryInitError && error.code === "project_name_invalid"
|
|
1039
|
-
? "Pass a lowercase deployment slug with '--project', then rerun init."
|
|
1040
|
-
: "Run 'ohmyhost init --dry-run --json' and resolve every reported blocker.",
|
|
1071
|
+
known ? initFailureAction(error) : INTERNAL_ERROR_ACTION,
|
|
1041
1072
|
);
|
|
1042
1073
|
io.writeStdout(result.stdout);
|
|
1043
1074
|
return result.exitCode;
|
|
1044
1075
|
}
|
|
1045
1076
|
};
|
|
1046
1077
|
|
|
1078
|
+
/** How to fix one init failure: what went wrong, then the exact next step. */
|
|
1079
|
+
const initFailureAction = (error: RepositoryInitError): string => {
|
|
1080
|
+
if (error instanceof RepositoryInitBlockedError)
|
|
1081
|
+
return "Run 'ohmyhost init --dry-run --json' and resolve every reported blocker.";
|
|
1082
|
+
if (error instanceof RepositoryInitConflictError)
|
|
1083
|
+
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.";
|
|
1084
|
+
const message = /[.!?]$/u.test(error.message) ? error.message : `${error.message}.`;
|
|
1085
|
+
switch (error.code) {
|
|
1086
|
+
case "repository_root_required":
|
|
1087
|
+
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.";
|
|
1088
|
+
case "project_name_invalid":
|
|
1089
|
+
return "Pass a lowercase deployment slug with '--project', then rerun init.";
|
|
1090
|
+
case "application_root_ambiguous":
|
|
1091
|
+
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.`;
|
|
1092
|
+
case "application_root_not_found":
|
|
1093
|
+
case "application_root_unsupported":
|
|
1094
|
+
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.`;
|
|
1095
|
+
case "application_root_invalid":
|
|
1096
|
+
case "application_root_missing":
|
|
1097
|
+
return `${message} Pass --root as a path relative to the repository root, for example apps/web.`;
|
|
1098
|
+
case "repository_configuration_invalid":
|
|
1099
|
+
return `${error.message.replace(/\.$/u, "")}. Fix that, then run 'ohmyhost init --dry-run --json' again; do not delete ohmyhost.yaml.`;
|
|
1100
|
+
case "repository_configuration_root_mismatch":
|
|
1101
|
+
return error.message;
|
|
1102
|
+
default:
|
|
1103
|
+
return `${message} Fix that and run 'ohmyhost init --json' again from the Git repository root.`;
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
|
|
1047
1107
|
class FetchWorkosPublicTransport implements WorkosPublicTransport {
|
|
1048
1108
|
public constructor(
|
|
1049
1109
|
private readonly parentSignal: AbortSignal,
|
|
@@ -1423,8 +1483,8 @@ const runOfflineLogout = async (
|
|
|
1423
1483
|
"logout",
|
|
1424
1484
|
3,
|
|
1425
1485
|
"credential_store_unavailable",
|
|
1426
|
-
|
|
1427
|
-
|
|
1486
|
+
false,
|
|
1487
|
+
CREDENTIAL_STORE_UNAVAILABLE_ACTION,
|
|
1428
1488
|
);
|
|
1429
1489
|
}
|
|
1430
1490
|
io.writeStdout(result.stdout);
|
|
@@ -1461,8 +1521,25 @@ export const readSecretValueFromStandardInput = async (
|
|
|
1461
1521
|
}
|
|
1462
1522
|
};
|
|
1463
1523
|
|
|
1464
|
-
|
|
1465
|
-
|
|
1524
|
+
/** The refused command's usage, with the command prefix of the selected platform environment. */
|
|
1525
|
+
const invalidCommandFailure = (
|
|
1526
|
+
argv: readonly string[],
|
|
1527
|
+
environment: ProductCliEnvironment,
|
|
1528
|
+
): CliResult => {
|
|
1529
|
+
let commandPrefix = "ohmyhost";
|
|
1530
|
+
try {
|
|
1531
|
+
commandPrefix = resolveProductCliEnvironment(environment).commandPrefix;
|
|
1532
|
+
} catch {
|
|
1533
|
+
// An invalid OHMYHOST_ENVIRONMENT is reported once the command itself is valid.
|
|
1534
|
+
}
|
|
1535
|
+
return errorDocument(
|
|
1536
|
+
recognizedCommandLabel(argv),
|
|
1537
|
+
2,
|
|
1538
|
+
"invalid_command",
|
|
1539
|
+
false,
|
|
1540
|
+
invalidCommandAction(argv, commandPrefix),
|
|
1541
|
+
);
|
|
1542
|
+
};
|
|
1466
1543
|
|
|
1467
1544
|
const resultDocument = (
|
|
1468
1545
|
command: OutputCommand,
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { executeManagedMail, type ManagedMailCommand } from "@ohmyhost/sdk-ts";
|
|
2
|
+
import type {
|
|
3
|
+
ProjectDataChangeKind,
|
|
4
|
+
ProjectDataChangePlan,
|
|
5
|
+
} from "@ohmyhost/contracts/project-data-changes";
|
|
2
6
|
import {
|
|
3
7
|
assertProjectDatabaseAccess,
|
|
4
8
|
assertProjectDatabaseAccessCredential,
|
|
@@ -66,6 +70,8 @@ import {
|
|
|
66
70
|
setProjectPoweredByFlag,
|
|
67
71
|
createDeployment,
|
|
68
72
|
createProject,
|
|
73
|
+
planProjectDataChange,
|
|
74
|
+
createProjectDataChange,
|
|
69
75
|
submitFeedback,
|
|
70
76
|
getFeedback,
|
|
71
77
|
createOrganization,
|
|
@@ -107,7 +113,6 @@ import {
|
|
|
107
113
|
revokeProjectDatabaseAccess,
|
|
108
114
|
reconcileOperation,
|
|
109
115
|
streamOperationEvents,
|
|
110
|
-
streamDeploymentLogs,
|
|
111
116
|
type DeploymentPlan,
|
|
112
117
|
type CloudflareDnsAuthorization,
|
|
113
118
|
type CloudflareDnsAuthorizationStatus,
|
|
@@ -128,12 +133,16 @@ import {
|
|
|
128
133
|
type PaidDomain,
|
|
129
134
|
type PaidDomainPlan,
|
|
130
135
|
type ProjectDatabaseQueryResult,
|
|
131
|
-
type DeploymentDiagnostic,
|
|
132
136
|
type DeploymentDiagnosticPage,
|
|
133
137
|
type Deployment,
|
|
134
138
|
type SourceAutoDeploy,
|
|
135
139
|
} from "@ohmyhost/sdk-ts";
|
|
136
|
-
import {
|
|
140
|
+
import {
|
|
141
|
+
parseAccountProfile,
|
|
142
|
+
parseProjectDataChangePlan,
|
|
143
|
+
parseOperation,
|
|
144
|
+
type PublicAccount,
|
|
145
|
+
} from "./runtime-contract.js";
|
|
137
146
|
|
|
138
147
|
export interface ProjectCreateInput {
|
|
139
148
|
readonly organizationId: string;
|
|
@@ -145,6 +154,17 @@ export interface ProjectCreateInput {
|
|
|
145
154
|
}
|
|
146
155
|
|
|
147
156
|
export interface ProductApi {
|
|
157
|
+
planProjectDataChange(input: {
|
|
158
|
+
projectId: string;
|
|
159
|
+
change: ProjectDataChangeKind;
|
|
160
|
+
}): Promise<ProjectDataChangePlan>;
|
|
161
|
+
createProjectDataChange(input: {
|
|
162
|
+
projectId: string;
|
|
163
|
+
change: ProjectDataChangeKind;
|
|
164
|
+
ifMatch: string;
|
|
165
|
+
confirmationToken: string;
|
|
166
|
+
idempotencyKey: string;
|
|
167
|
+
}): Promise<Operation>;
|
|
148
168
|
changeDatabaseCompute(input: {
|
|
149
169
|
projectId: string;
|
|
150
170
|
environment: "dev" | "prod";
|
|
@@ -345,9 +365,14 @@ export interface ProductApi {
|
|
|
345
365
|
readonly projectId: string;
|
|
346
366
|
readonly deploymentId: string;
|
|
347
367
|
}): Promise<Deployment>;
|
|
368
|
+
/**
|
|
369
|
+
* One connection to the operation's events, resumed after `lastEventId` when given. It ends,
|
|
370
|
+
* without retrying, when the server closes it or refuses it; the caller decides what follows.
|
|
371
|
+
*/
|
|
348
372
|
streamOperationEvents(
|
|
349
373
|
operationId: string,
|
|
350
374
|
signal: AbortSignal,
|
|
375
|
+
lastEventId?: string,
|
|
351
376
|
): Promise<AsyncIterable<OperationEvent>>;
|
|
352
377
|
getDeploymentLogs(input: {
|
|
353
378
|
readonly projectId: string;
|
|
@@ -355,10 +380,6 @@ export interface ProductApi {
|
|
|
355
380
|
readonly cursor?: string;
|
|
356
381
|
readonly limit: number;
|
|
357
382
|
}): Promise<DeploymentDiagnosticPage>;
|
|
358
|
-
streamDeploymentLogs(
|
|
359
|
-
input: { readonly projectId: string; readonly deploymentId: string },
|
|
360
|
-
signal: AbortSignal,
|
|
361
|
-
): Promise<AsyncIterable<DeploymentDiagnostic>>;
|
|
362
383
|
planRollback(input: {
|
|
363
384
|
readonly projectId: string;
|
|
364
385
|
readonly deploymentId: string;
|
|
@@ -846,6 +867,32 @@ export class GeneratedSdkProductApi implements ProductApi {
|
|
|
846
867
|
) as import("@ohmyhost/sdk-ts").FeedbackStatus;
|
|
847
868
|
}
|
|
848
869
|
|
|
870
|
+
public async planProjectDataChange(
|
|
871
|
+
input: Parameters<ProductApi["planProjectDataChange"]>[0],
|
|
872
|
+
): Promise<ProjectDataChangePlan> {
|
|
873
|
+
return parseProjectDataChangePlan(
|
|
874
|
+
await planProjectDataChange(
|
|
875
|
+
{ project_id: input.projectId, projectDataChangeRequest: { change: input.change } },
|
|
876
|
+
{ client: this.#client },
|
|
877
|
+
),
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
public async createProjectDataChange(
|
|
881
|
+
input: Parameters<ProductApi["createProjectDataChange"]>[0],
|
|
882
|
+
): Promise<Operation> {
|
|
883
|
+
return parseOperation(
|
|
884
|
+
await createProjectDataChange(
|
|
885
|
+
{
|
|
886
|
+
project_id: input.projectId,
|
|
887
|
+
projectDataChangeRequest: { change: input.change },
|
|
888
|
+
"If-Match": input.ifMatch,
|
|
889
|
+
"X-Confirmation-Token": input.confirmationToken,
|
|
890
|
+
"Idempotency-Key": input.idempotencyKey,
|
|
891
|
+
},
|
|
892
|
+
{ client: this.#client },
|
|
893
|
+
),
|
|
894
|
+
);
|
|
895
|
+
}
|
|
849
896
|
public async createProject(input: ProjectCreateInput): Promise<Operation> {
|
|
850
897
|
return createProject(
|
|
851
898
|
{
|
|
@@ -1014,11 +1061,21 @@ export class GeneratedSdkProductApi implements ProductApi {
|
|
|
1014
1061
|
public async streamOperationEvents(
|
|
1015
1062
|
operationId: string,
|
|
1016
1063
|
signal: AbortSignal,
|
|
1064
|
+
lastEventId?: string,
|
|
1017
1065
|
): Promise<AsyncIterable<OperationEvent>> {
|
|
1018
1066
|
return (
|
|
1019
1067
|
await streamOperationEvents(
|
|
1020
1068
|
{ operation_id: operationId },
|
|
1021
|
-
{
|
|
1069
|
+
{
|
|
1070
|
+
client: this.#client,
|
|
1071
|
+
signal,
|
|
1072
|
+
// The generated client would otherwise retry a refused stream, such as a 404, forever.
|
|
1073
|
+
sseMaxRetryAttempts: 1,
|
|
1074
|
+
headers: {
|
|
1075
|
+
Accept: "text/event-stream",
|
|
1076
|
+
...(lastEventId === undefined ? {} : { "Last-Event-ID": lastEventId }),
|
|
1077
|
+
},
|
|
1078
|
+
},
|
|
1022
1079
|
)
|
|
1023
1080
|
).stream;
|
|
1024
1081
|
}
|
|
@@ -1038,17 +1095,6 @@ export class GeneratedSdkProductApi implements ProductApi {
|
|
|
1038
1095
|
{ client: this.#client },
|
|
1039
1096
|
);
|
|
1040
1097
|
}
|
|
1041
|
-
public async streamDeploymentLogs(
|
|
1042
|
-
input: { readonly projectId: string; readonly deploymentId: string },
|
|
1043
|
-
signal: AbortSignal,
|
|
1044
|
-
): Promise<AsyncIterable<DeploymentDiagnostic>> {
|
|
1045
|
-
return (
|
|
1046
|
-
await streamDeploymentLogs(
|
|
1047
|
-
{ project_id: input.projectId, deployment_id: input.deploymentId },
|
|
1048
|
-
{ client: this.#client, signal, headers: { Accept: "text/event-stream" } },
|
|
1049
|
-
)
|
|
1050
|
-
).stream;
|
|
1051
|
-
}
|
|
1052
1098
|
|
|
1053
1099
|
public async planRollback(input: {
|
|
1054
1100
|
readonly projectId: string;
|