@amerged/ohmyhost-mcp 0.1.33 → 0.1.35
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/{chunk-YM2ZQAGS.js → chunk-CVTJZFHD.js} +5 -5
- package/dist/index.js +1 -1
- package/dist/stdio-main.js +66 -18
- package/package.json +1 -1
- package/src/apps/mcp/package.json +1 -1
- package/src/apps/mcp/src/local-server.ts +16 -4
- package/src/apps/product-cli/package.json +1 -1
- package/src/apps/product-cli/src/cli.ts +1 -1
- package/src/apps/product-cli/src/repository-init.ts +45 -10
- package/src/apps/product-cli/src/runtime-contract.ts +26 -2
- package/src/packages/agent-skills/src/generated-skill-resources.ts +4 -4
- package/src/packages/contracts/src/framework-admission.ts +32 -6
- package/src/packages/contracts/src/framework-build-script.mjs +8 -2
- package/src/packages/contracts/src/framework-family.ts +4 -0
- package/src/packages/contracts/src/framework-source-rules.ts +78 -9
- package/src/packages/sdk-ts/package.json +1 -1
- package/src/packages/sdk-ts/src/generated/sdk.gen.ts +1 -1
- package/src/packages/sdk-ts/src/generated/types.gen.ts +15 -1
package/dist/stdio-main.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
serverIdentityOf,
|
|
31
31
|
setNegotiatedProtocolVersion,
|
|
32
32
|
validateEnvelopeMeta
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-CVTJZFHD.js";
|
|
34
34
|
|
|
35
35
|
// node_modules/.pnpm/ignore@7.0.12/node_modules/ignore/index.js
|
|
36
36
|
var require_ignore = __commonJS({
|
|
@@ -20235,6 +20235,11 @@ function toError(value) {
|
|
|
20235
20235
|
// packages/contracts/dist/framework-build-script.mjs
|
|
20236
20236
|
var PINNED_BUN_VERSION = "1.2.22";
|
|
20237
20237
|
var FRAMEWORK_VERSION_POLICY = Object.freeze({
|
|
20238
|
+
astro: Object.freeze({ floors: Object.freeze(["7.4.0"]), denied: Object.freeze([]) }),
|
|
20239
|
+
"@astrojs/cloudflare": Object.freeze({
|
|
20240
|
+
floors: Object.freeze(["14.4.0"]),
|
|
20241
|
+
denied: Object.freeze([])
|
|
20242
|
+
}),
|
|
20238
20243
|
next: Object.freeze({ floors: Object.freeze(["15.5.27", "16.3.8"]), denied: Object.freeze([]) }),
|
|
20239
20244
|
vite: Object.freeze({ floors: Object.freeze(["5.4.6"]), denied: Object.freeze([]) }),
|
|
20240
20245
|
"@tanstack/react-start": Object.freeze({
|
|
@@ -20289,11 +20294,15 @@ var RESOLVED_VERSION_FIELD = anchoredPattern(RESOLVED_VERSION_SOURCE);
|
|
|
20289
20294
|
var DECLARED_VERSION_FIELD = anchoredPattern(`[~^]?${RESOLVED_VERSION_SOURCE}`);
|
|
20290
20295
|
var STABLE_VERSION_FIELD = anchoredPattern(STABLE_VERSION_SOURCE);
|
|
20291
20296
|
var FRAMEWORK_VERSION_PACKAGES = Object.freeze([
|
|
20297
|
+
"astro",
|
|
20298
|
+
"@astrojs/cloudflare",
|
|
20292
20299
|
"next",
|
|
20293
20300
|
"vite",
|
|
20294
20301
|
"@tanstack/react-start"
|
|
20295
20302
|
]);
|
|
20296
20303
|
var FRAMEWORK_DEPENDENCY_PACKAGES = Object.freeze([
|
|
20304
|
+
"astro",
|
|
20305
|
+
"@astrojs/cloudflare",
|
|
20297
20306
|
"next",
|
|
20298
20307
|
"react",
|
|
20299
20308
|
"react-dom",
|
|
@@ -20304,10 +20313,23 @@ var FRAMEWORK_DEPENDENCY_PACKAGES = Object.freeze([
|
|
|
20304
20313
|
"@cloudflare/vite-plugin"
|
|
20305
20314
|
]);
|
|
20306
20315
|
var REACT_PACKAGES = Object.freeze(["react", "react-dom"]);
|
|
20307
|
-
var BUILD_FRAMEWORKS = Object.freeze([
|
|
20316
|
+
var BUILD_FRAMEWORKS = Object.freeze([
|
|
20317
|
+
"astro",
|
|
20318
|
+
"nextjs",
|
|
20319
|
+
"vite-static",
|
|
20320
|
+
"tanstack-start"
|
|
20321
|
+
]);
|
|
20308
20322
|
var PUSH_AND_PLAN = "Commit and push the change, then plan the new commit.";
|
|
20309
20323
|
var VITE_BUILD_SCRIPT = '"vite build", optionally joined with && to one "tsc", "tsc -b", "tsc --build" or "tsc --noEmit" stage';
|
|
20310
20324
|
var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
|
|
20325
|
+
astro_template_import_unsupported: {
|
|
20326
|
+
paths: ["module"],
|
|
20327
|
+
template: '"{{path}}" uses a dynamic import, import.meta or require in an Astro template. Move server imports into frontmatter and dynamic browser imports into a normal statically imported browser module, then commit and push the change and plan the new commit.'
|
|
20328
|
+
},
|
|
20329
|
+
astro_config_unsupported: {
|
|
20330
|
+
paths: ["framework_config"],
|
|
20331
|
+
template: '"{{path}}" must use the standard Astro output directories and root base. For edge use output: "server", @astrojs/cloudflare with imageService: "passthrough", and session: false. Static hosting must omit a server adapter. Additional Cloudflare bindings, custom sessions and provider cache are not supported. Commit and push the change, then plan the new commit.'
|
|
20332
|
+
},
|
|
20311
20333
|
managed_auth_database_required: {
|
|
20312
20334
|
paths: ["repository_config"],
|
|
20313
20335
|
template: managedCustomerAuthDatabaseMessage("{{path}}")
|
|
@@ -20382,7 +20404,8 @@ var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
|
|
|
20382
20404
|
text: {
|
|
20383
20405
|
nextjs: 'exactly "next build" or "next build --webpack"; ohmyho.st adds the OpenNext build itself',
|
|
20384
20406
|
"vite-static": VITE_BUILD_SCRIPT,
|
|
20385
|
-
"tanstack-start": VITE_BUILD_SCRIPT
|
|
20407
|
+
"tanstack-start": VITE_BUILD_SCRIPT,
|
|
20408
|
+
astro: 'exactly "astro build"'
|
|
20386
20409
|
}
|
|
20387
20410
|
}
|
|
20388
20411
|
},
|
|
@@ -20398,11 +20421,11 @@ var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
|
|
|
20398
20421
|
},
|
|
20399
20422
|
framework_unsupported: {
|
|
20400
20423
|
paths: ["manifest"],
|
|
20401
|
-
template: `"{{path}}" declares none of next, vite or @tanstack/react-start. Add the app's framework to dependencies, or set runtime.mode: functions in ohmyhost.yaml and add src/ohmyhost/worker.ts. ${PUSH_AND_PLAN}`
|
|
20424
|
+
template: `"{{path}}" declares none of astro, next, vite or @tanstack/react-start. Add the app's framework to dependencies, or set runtime.mode: functions in ohmyhost.yaml and add src/ohmyhost/worker.ts. ${PUSH_AND_PLAN}`
|
|
20402
20425
|
},
|
|
20403
20426
|
framework_ambiguous: {
|
|
20404
20427
|
paths: ["manifest"],
|
|
20405
|
-
template: '"{{path}}" declares
|
|
20428
|
+
template: '"{{path}}" declares competing application framework packages, so ohmyho.st cannot choose one framework. Remove the packages of the framework this application does not use, commit and push the change, then plan the new commit.'
|
|
20406
20429
|
},
|
|
20407
20430
|
lockfile_missing: {
|
|
20408
20431
|
paths: ["manifest"],
|
|
@@ -20441,7 +20464,8 @@ var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
|
|
|
20441
20464
|
text: {
|
|
20442
20465
|
nextjs: "runtime.mode: edge",
|
|
20443
20466
|
"vite-static": "runtime.mode: edge with src/ohmyhost/companion.ts when database, auth, mail, storage or functions.crons is set, and static otherwise",
|
|
20444
|
-
"tanstack-start": "runtime.mode: edge, or static without functions.crons and storage"
|
|
20467
|
+
"tanstack-start": "runtime.mode: edge, or static without functions.crons and storage",
|
|
20468
|
+
astro: "runtime.mode: static for prerendered sites, or edge with the Cloudflare adapter"
|
|
20445
20469
|
}
|
|
20446
20470
|
}
|
|
20447
20471
|
},
|
|
@@ -20456,7 +20480,8 @@ var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
|
|
|
20456
20480
|
text: {
|
|
20457
20481
|
nextjs: ".open-next/assets",
|
|
20458
20482
|
"vite-static": "dist",
|
|
20459
|
-
"tanstack-start": "dist/client"
|
|
20483
|
+
"tanstack-start": "dist/client",
|
|
20484
|
+
astro: "dist for static, or dist/client for edge"
|
|
20460
20485
|
}
|
|
20461
20486
|
}
|
|
20462
20487
|
},
|
|
@@ -20515,8 +20540,8 @@ var DETAIL_DEFINITIONS = FRAMEWORK_CONVERSION_DETAILS;
|
|
|
20515
20540
|
var PLACEHOLDER = /\{\{([a-z]+)\}\}/gu;
|
|
20516
20541
|
var PATH_PATTERN = "[A-Za-z0-9_@()[\\]./${}-]{1,240}";
|
|
20517
20542
|
var SAFE_PATH = new RegExp(`^${PATH_PATTERN}$`, "u");
|
|
20518
|
-
var MODULE_PATH = /^[A-Za-z0-9_@()[\]./${}-]+\.(?:[cm]?[jt]sx?)$/u;
|
|
20519
|
-
var FRAMEWORK_CONFIG_NAME = /^(?:next|open-next|vite)\.config\.(?:js|mjs|ts)$/u;
|
|
20543
|
+
var MODULE_PATH = /^[A-Za-z0-9_@()[\]./${}-]+\.(?:astro|[cm]?[jt]sx?)$/u;
|
|
20544
|
+
var FRAMEWORK_CONFIG_NAME = /^(?:astro|next|open-next|vite)\.config\.(?:js|mjs|ts)$/u;
|
|
20520
20545
|
var MAXIMUM_DETAIL_LENGTH = 2e3;
|
|
20521
20546
|
var DETAIL_PATTERNS = Object.freeze(Object.entries(DETAIL_DEFINITIONS).map(([code, definition]) => Object.freeze({
|
|
20522
20547
|
code,
|
|
@@ -22322,14 +22347,25 @@ var parseDevAccessTicket = (value) => {
|
|
|
22322
22347
|
return input;
|
|
22323
22348
|
};
|
|
22324
22349
|
var parseCurrentIdentity = (value) => {
|
|
22325
|
-
const input = exactRecord2(value, ["actor_id", "organization_ids"]);
|
|
22350
|
+
const input = exactRecord2(value, ["actor_id", "email", "organization_ids", "organizations"]);
|
|
22351
|
+
const email = input["email"];
|
|
22326
22352
|
const organizationIds = input["organization_ids"];
|
|
22353
|
+
const organizations = input["organizations"];
|
|
22327
22354
|
if (typeof input["actor_id"] !== "string" || input["actor_id"].length === 0 || input["actor_id"].length > 256 || [...input["actor_id"]].some((character) => {
|
|
22328
22355
|
const codePoint = character.codePointAt(0) ?? 0;
|
|
22329
22356
|
return codePoint <= 31 || codePoint === 127;
|
|
22330
|
-
}) || !Array.isArray(organizationIds) || organizationIds.some((organizationId) => !isUlid(organizationId)) || new Set(organizationIds).size !== organizationIds.length) {
|
|
22357
|
+
}) || email !== null && (!boundedString(email, 3, 254) || !/^[^\s@]+@[^\s@.]+(?:\.[^\s@.]+)+$/u.test(email) || /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(email)) || !Array.isArray(organizationIds) || organizationIds.some((organizationId) => !isUlid(organizationId)) || new Set(organizationIds).size !== organizationIds.length || !Array.isArray(organizations) || organizations.length !== organizationIds.length) {
|
|
22331
22358
|
throw new ResponseContractError();
|
|
22332
22359
|
}
|
|
22360
|
+
organizations.forEach((organization, index) => {
|
|
22361
|
+
const entry = exactRecord2(organization, ["id", "name"]);
|
|
22362
|
+
if (entry["id"] !== organizationIds[index] || !boundedString(entry["name"], 1, 128) || entry["name"].trim() !== entry["name"] || [...entry["name"]].some((character) => {
|
|
22363
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
22364
|
+
return codePoint <= 31 || codePoint === 127;
|
|
22365
|
+
})) {
|
|
22366
|
+
throw new ResponseContractError();
|
|
22367
|
+
}
|
|
22368
|
+
});
|
|
22333
22369
|
return input;
|
|
22334
22370
|
};
|
|
22335
22371
|
var parseAccountProfile = (value) => {
|
|
@@ -23221,7 +23257,7 @@ function exact3(v, keys) {
|
|
|
23221
23257
|
// apps/product-cli/package.json
|
|
23222
23258
|
var package_default2 = {
|
|
23223
23259
|
name: "@ohmyhost/product-cli",
|
|
23224
|
-
version: "0.1.
|
|
23260
|
+
version: "0.1.35",
|
|
23225
23261
|
private: true,
|
|
23226
23262
|
ohmyhost: {
|
|
23227
23263
|
deployment: "production",
|
|
@@ -24327,7 +24363,7 @@ var HELP_USAGE = Object.freeze({
|
|
|
24327
24363
|
"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)",
|
|
24328
24364
|
"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)",
|
|
24329
24365
|
"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)",
|
|
24330
|
-
"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
|
|
24366
|
+
"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, Paid 0.5 CU/2 GB. performance: Paid 1 CU/4 GB, 2.5x database compute credits per equal active minute. All profiles use the provider-default idle suspension.",
|
|
24331
24367
|
"database compute get": "ohmyhost database compute get --project ULID [--environment dev|prod] --json",
|
|
24332
24368
|
"database write": "ohmyhost database write --project ULID --environment dev|prod --statement-file PATH --idempotency-key KEY [--parameters-json JSON] --yes --json",
|
|
24333
24369
|
"database query": "ohmyhost database query --project ULID --environment dev|prod --statement SQL [--parameters-json JSON] --json",
|
|
@@ -29147,7 +29183,7 @@ function createLocalOhmyhostMcpServer(dependencies) {
|
|
|
29147
29183
|
});
|
|
29148
29184
|
registerManagedSourceTools(server, dependencies);
|
|
29149
29185
|
registerProductTool(server, dependencies, "database_compute_get", "Read current managed database size, memory, region and compute state without running SQL or waking the database. Shared Dev/Prod resolves to one physical database. A null database means no confirmed managed placement. suspend_timeout_seconds=0 uses the provider default; -1 disables scale to zero. A configured value is not proof that an in-progress resize finished. This read never resizes or changes billing.", external_exports.object({ project_id: identifier2, environment: external_exports.enum(["dev", "prod"]).default("dev") }), (client2, input) => client2.getDatabaseCompute({ projectId: input.project_id, environment: input.environment }));
|
|
29150
|
-
registerProductTool(server, dependencies, "database_compute_set", "Select standard or performance compute for an existing database: Free 0.25 CU/1 GB
|
|
29186
|
+
registerProductTool(server, dependencies, "database_compute_set", "Select standard or performance compute for an existing database: Free 0.25 CU/1 GB, Paid 0.5 CU/2 GB. All profiles use provider-default idle suspension (suspend_timeout_seconds=0). First read database_compute_get and explain actual size, metered compute cost, possible brief connection interruption and that shared data changes both Dev and Prod. Require the user's decision before confirm=true. SQL data is preserved. Poll the returned operation every 60 seconds; never submit a second change while it runs. Performance requires active Paid access and selects 1 CU/4 GB at 2.5x Paid-standard compute credits per equal active minute. This affects only database compute. Read organization_credits_get for the published rate and active meter first. A successful choice is remembered per physical database; automatic Free downgrades preserve it, and an explicit standard choice clears it. Mixed/uncertain transition hours waive the premium; never multiply raw CU usage again.", external_exports.object({
|
|
29151
29187
|
project_id: identifier2,
|
|
29152
29188
|
environment: external_exports.enum(["dev", "prod"]),
|
|
29153
29189
|
profile: external_exports.enum(["standard", "performance"]),
|
|
@@ -29411,7 +29447,7 @@ function createLocalOhmyhostMcpServer(dependencies) {
|
|
|
29411
29447
|
});
|
|
29412
29448
|
return { revoked: true, key_id: input.key_id };
|
|
29413
29449
|
}, destructiveAnnotations());
|
|
29414
|
-
registerProductTool(server, dependencies, "identity_get", "Get the ohmyho.st customer/agent identity this call acts as: user, organization and, in context, the saved login or OHMYHOST_TOKEN that supplied it. Compare it with the user and organization the customer named before any change. This does not authenticate the hosted application's end users or provision a customer AuthKit tenant; application authentication is the customer's chosen integration.", external_exports.object({}), (client2, _input, signal) => client2.getCurrentIdentity(signal));
|
|
29450
|
+
registerProductTool(server, dependencies, "identity_get", "Get the ohmyho.st customer/agent identity this call acts as: user and account email, scoped organization IDs and names and, in context, the saved login or OHMYHOST_TOKEN that supplied it. Compare it with the user and organization the customer named before any change. This does not authenticate the hosted application's end users or provision a customer AuthKit tenant; application authentication is the customer's chosen integration.", external_exports.object({}), (client2, _input, signal) => client2.getCurrentIdentity(signal));
|
|
29415
29451
|
registerProductTool(server, dependencies, "project_handle_check", "Check whether a project address is free before offering it to the customer. A handle is one to five lowercase words of letters and digits joined by single hyphens, three to fifty-nine characters, and never a reserved word such as admin, api, dev or www; it becomes <handle>.check.omh.st for Prod and dev-<handle>.check.omh.st for Dev. The answer names why a handle cannot be used and returns free alternatives, so offer one of those instead of guessing again. Check the name here first, then move an existing project onto it with project_handle_set.", external_exports.object({ handle: external_exports.string().min(1).max(120) }), (client2, input) => client2.checkProjectHandle({ handle: input.handle }));
|
|
29416
29452
|
registerProductTool(server, dependencies, "project_handle_set", "Move a project to an address the customer chose, after project_handle_check said it is free. The Dev and Prod gateways are re-published at the new address and it is stored only once both serve it; the previous address stops answering and anyone may claim it, so a link already shared stops working. Needs the project's current ETag from project_status and one idempotency key; reuse the same key after an uncertain answer. Refused while another operation is running for the project, and refused for an address that is taken, unusable, or the one the project already has. Returns an operation to follow with operation_get.", external_exports.object({
|
|
29417
29453
|
project_id: identifier2,
|
|
@@ -29896,7 +29932,12 @@ function registerManagedSourceTools(server, dependencies) {
|
|
|
29896
29932
|
directory: input.directory,
|
|
29897
29933
|
metadataDirectory: resolveProductCliEnvironment(dependencies.environment).linkDirectory,
|
|
29898
29934
|
apiOrigin: client2.productApiOrigin ?? resolveProductCliEnvironment(dependencies.environment).apiOrigin,
|
|
29899
|
-
identity: {
|
|
29935
|
+
identity: {
|
|
29936
|
+
actor_id: current.actor_id,
|
|
29937
|
+
email: current.email,
|
|
29938
|
+
organization_ids: current.organization_ids,
|
|
29939
|
+
organizations: current.organizations
|
|
29940
|
+
},
|
|
29900
29941
|
projectId: input.project_id,
|
|
29901
29942
|
request,
|
|
29902
29943
|
explicitExpectedCommit: input.expected_commit_sha !== void 0,
|
|
@@ -29933,7 +29974,12 @@ function registerManagedSourceTools(server, dependencies) {
|
|
|
29933
29974
|
directory: input.directory,
|
|
29934
29975
|
metadataDirectory: resolveProductCliEnvironment(dependencies.environment).linkDirectory,
|
|
29935
29976
|
apiOrigin: client2.productApiOrigin ?? resolveProductCliEnvironment(dependencies.environment).apiOrigin,
|
|
29936
|
-
identity: {
|
|
29977
|
+
identity: {
|
|
29978
|
+
actor_id: current.actor_id,
|
|
29979
|
+
email: current.email,
|
|
29980
|
+
organization_ids: current.organization_ids,
|
|
29981
|
+
organizations: current.organizations
|
|
29982
|
+
},
|
|
29937
29983
|
projectId: input.project_id,
|
|
29938
29984
|
operationId: input.operation_id,
|
|
29939
29985
|
api: port(client2),
|
|
@@ -29993,7 +30039,9 @@ async function handoverAccount(client2, signal) {
|
|
|
29993
30039
|
const report = await client2.getCurrentIdentity(signal);
|
|
29994
30040
|
const identity = parseCurrentIdentity({
|
|
29995
30041
|
actor_id: report.actor_id,
|
|
29996
|
-
|
|
30042
|
+
email: report.email,
|
|
30043
|
+
organization_ids: report.organization_ids,
|
|
30044
|
+
organizations: report.organizations
|
|
29997
30045
|
});
|
|
29998
30046
|
const userId = identity.actor_id.startsWith("user:") ? identity.actor_id.slice(5) : "";
|
|
29999
30047
|
const [organizationId, ...others] = identity.organization_ids;
|
package/package.json
CHANGED
|
@@ -522,7 +522,7 @@ export function createLocalOhmyhostMcpServer(dependencies: LocalOhmyhostMcpDepen
|
|
|
522
522
|
server,
|
|
523
523
|
dependencies,
|
|
524
524
|
"database_compute_set",
|
|
525
|
-
"Select standard or performance compute for an existing database: Free 0.25 CU/1 GB
|
|
525
|
+
"Select standard or performance compute for an existing database: Free 0.25 CU/1 GB, Paid 0.5 CU/2 GB. All profiles use provider-default idle suspension (suspend_timeout_seconds=0). First read database_compute_get and explain actual size, metered compute cost, possible brief connection interruption and that shared data changes both Dev and Prod. Require the user's decision before confirm=true. SQL data is preserved. Poll the returned operation every 60 seconds; never submit a second change while it runs. Performance requires active Paid access and selects 1 CU/4 GB at 2.5x Paid-standard compute credits per equal active minute. This affects only database compute. Read organization_credits_get for the published rate and active meter first. A successful choice is remembered per physical database; automatic Free downgrades preserve it, and an explicit standard choice clears it. Mixed/uncertain transition hours waive the premium; never multiply raw CU usage again.",
|
|
526
526
|
z.object({
|
|
527
527
|
project_id: identifier,
|
|
528
528
|
environment: z.enum(["dev", "prod"]),
|
|
@@ -1119,7 +1119,7 @@ export function createLocalOhmyhostMcpServer(dependencies: LocalOhmyhostMcpDepen
|
|
|
1119
1119
|
server,
|
|
1120
1120
|
dependencies,
|
|
1121
1121
|
"identity_get",
|
|
1122
|
-
"Get the ohmyho.st customer/agent identity this call acts as: user, organization and, in context, the saved login or OHMYHOST_TOKEN that supplied it. Compare it with the user and organization the customer named before any change. This does not authenticate the hosted application's end users or provision a customer AuthKit tenant; application authentication is the customer's chosen integration.",
|
|
1122
|
+
"Get the ohmyho.st customer/agent identity this call acts as: user and account email, scoped organization IDs and names and, in context, the saved login or OHMYHOST_TOKEN that supplied it. Compare it with the user and organization the customer named before any change. This does not authenticate the hosted application's end users or provision a customer AuthKit tenant; application authentication is the customer's chosen integration.",
|
|
1123
1123
|
z.object({}),
|
|
1124
1124
|
(client, _input, signal) => client.getCurrentIdentity(signal),
|
|
1125
1125
|
);
|
|
@@ -2072,7 +2072,12 @@ function registerManagedSourceTools(
|
|
|
2072
2072
|
apiOrigin:
|
|
2073
2073
|
client.productApiOrigin ??
|
|
2074
2074
|
resolveProductCliEnvironment(dependencies.environment).apiOrigin,
|
|
2075
|
-
identity: {
|
|
2075
|
+
identity: {
|
|
2076
|
+
actor_id: current.actor_id,
|
|
2077
|
+
email: current.email,
|
|
2078
|
+
organization_ids: current.organization_ids,
|
|
2079
|
+
organizations: current.organizations,
|
|
2080
|
+
},
|
|
2076
2081
|
projectId: input.project_id,
|
|
2077
2082
|
request,
|
|
2078
2083
|
explicitExpectedCommit: input.expected_commit_sha !== undefined,
|
|
@@ -2120,7 +2125,12 @@ function registerManagedSourceTools(
|
|
|
2120
2125
|
apiOrigin:
|
|
2121
2126
|
client.productApiOrigin ??
|
|
2122
2127
|
resolveProductCliEnvironment(dependencies.environment).apiOrigin,
|
|
2123
|
-
identity: {
|
|
2128
|
+
identity: {
|
|
2129
|
+
actor_id: current.actor_id,
|
|
2130
|
+
email: current.email,
|
|
2131
|
+
organization_ids: current.organization_ids,
|
|
2132
|
+
organizations: current.organizations,
|
|
2133
|
+
},
|
|
2124
2134
|
projectId: input.project_id,
|
|
2125
2135
|
operationId: input.operation_id,
|
|
2126
2136
|
api: port(client),
|
|
@@ -2235,7 +2245,9 @@ async function handoverAccount(
|
|
|
2235
2245
|
const report = await client.getCurrentIdentity(signal);
|
|
2236
2246
|
const identity = parseCurrentIdentity({
|
|
2237
2247
|
actor_id: report.actor_id,
|
|
2248
|
+
email: report.email,
|
|
2238
2249
|
organization_ids: report.organization_ids,
|
|
2250
|
+
organizations: report.organizations,
|
|
2239
2251
|
});
|
|
2240
2252
|
const userId = identity.actor_id.startsWith("user:") ? identity.actor_id.slice(5) : "";
|
|
2241
2253
|
const [organizationId, ...others] = identity.organization_ids;
|
|
@@ -399,7 +399,7 @@ const HELP_USAGE = Object.freeze({
|
|
|
399
399
|
"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)",
|
|
400
400
|
"database compute set":
|
|
401
401
|
"ohmyhost database compute set --project ULID --environment dev|prod --profile standard|performance --idempotency-key KEY --yes [--wait] --json\n" +
|
|
402
|
-
" standard: Free 0.25 CU/1 GB
|
|
402
|
+
" standard: Free 0.25 CU/1 GB, Paid 0.5 CU/2 GB. performance: Paid 1 CU/4 GB, 2.5x database compute credits per equal active minute. All profiles use the provider-default idle suspension.",
|
|
403
403
|
"database compute get":
|
|
404
404
|
"ohmyhost database compute get --project ULID [--environment dev|prod] --json",
|
|
405
405
|
"database write":
|
|
@@ -310,8 +310,12 @@ export async function initializeRepository(
|
|
|
310
310
|
return match?.[1] === undefined || match[1].startsWith("_") ? [] : [match[1]];
|
|
311
311
|
}),
|
|
312
312
|
);
|
|
313
|
-
const apiRouteCount = applicationFiles.filter(
|
|
314
|
-
|
|
313
|
+
const apiRouteCount = applicationFiles.filter(
|
|
314
|
+
(path) =>
|
|
315
|
+
/^(?:src\/)?app\/api\/.+\/route\.(?:js|jsx|ts|tsx)$/u.test(path) ||
|
|
316
|
+
(framework === "astro" &&
|
|
317
|
+
/^src\/pages\/.+\.(?:js|ts)$/u.test(path) &&
|
|
318
|
+
!path.endsWith(".d.ts")),
|
|
315
319
|
).length;
|
|
316
320
|
const supabase = dependencies.has("@supabase/supabase-js") || dependencies.has("@supabase/ssr");
|
|
317
321
|
const configuredDatabase = existingConfiguration?.database?.enabled === true;
|
|
@@ -368,11 +372,15 @@ export async function initializeRepository(
|
|
|
368
372
|
const targetStorage = r2 || supabaseStorage;
|
|
369
373
|
// TanStack Start binds storage only on edge, as a Vite app does through its companion.
|
|
370
374
|
const tanStackRuntime =
|
|
371
|
-
framework !== "tanstack-start"
|
|
375
|
+
framework !== "tanstack-start" && framework !== "astro"
|
|
372
376
|
? null
|
|
373
377
|
: targetStorage
|
|
374
378
|
? "edge"
|
|
375
|
-
:
|
|
379
|
+
: framework === "astro"
|
|
380
|
+
? dependencies.has("@astrojs/cloudflare")
|
|
381
|
+
? "edge"
|
|
382
|
+
: "static"
|
|
383
|
+
: proposedTanStackRuntime(frameworkConfig, dependencies);
|
|
376
384
|
const companionRequired =
|
|
377
385
|
framework === "vite" &&
|
|
378
386
|
(existingConfiguration === null
|
|
@@ -390,7 +398,11 @@ export async function initializeRepository(
|
|
|
390
398
|
databaseProvider,
|
|
391
399
|
hasHealthcheck:
|
|
392
400
|
applicationFiles.includes("src/app/api/health/route.ts") ||
|
|
393
|
-
applicationFiles.includes("app/api/health/route.ts")
|
|
401
|
+
applicationFiles.includes("app/api/health/route.ts") ||
|
|
402
|
+
(framework === "astro" &&
|
|
403
|
+
applicationFiles.some((path) =>
|
|
404
|
+
/^src\/pages\/api\/health(?:\/index)?\.(?:js|ts)$/u.test(path),
|
|
405
|
+
)),
|
|
394
406
|
mailEnabled: targetMail,
|
|
395
407
|
betterAuthEnabled: targetBetterAuth,
|
|
396
408
|
storageEnabled: targetStorage,
|
|
@@ -596,7 +608,10 @@ async function workerModuleBlockers(context: SourceCheckContext): Promise<Reposi
|
|
|
596
608
|
const scheduled =
|
|
597
609
|
(config?.functions?.crons.length ?? 0) > 0 &&
|
|
598
610
|
((mode === "edge" &&
|
|
599
|
-
(framework === "
|
|
611
|
+
(framework === "astro" ||
|
|
612
|
+
framework === "vite" ||
|
|
613
|
+
framework === "nextjs" ||
|
|
614
|
+
framework === "tanstack-start")) ||
|
|
600
615
|
(mode === "functions" && framework === "functions"));
|
|
601
616
|
const path = framework === "vite" ? VITE_COMPANION_ENTRY_PATH : WORKER_MODULE_PATH;
|
|
602
617
|
if ((framework !== "functions" && !scheduled) || !files.includes(path)) return [];
|
|
@@ -989,7 +1004,10 @@ function hasDirectSafeBuildCommand(
|
|
|
989
1004
|
const scripts = manifest["scripts"];
|
|
990
1005
|
if (!isRecord(scripts) || typeof scripts["build"] !== "string") return false;
|
|
991
1006
|
return (
|
|
992
|
-
(framework === "
|
|
1007
|
+
(framework === "astro" ||
|
|
1008
|
+
framework === "nextjs" ||
|
|
1009
|
+
framework === "vite" ||
|
|
1010
|
+
framework === "tanstack-start") &&
|
|
993
1011
|
isAdmittedFrameworkBuildScript(framework, scripts["build"])
|
|
994
1012
|
);
|
|
995
1013
|
}
|
|
@@ -1144,10 +1162,23 @@ async function readFrameworkConfig(
|
|
|
1144
1162
|
files: readonly string[],
|
|
1145
1163
|
framework: RepositoryFramework,
|
|
1146
1164
|
): Promise<FrameworkConfigText | null> {
|
|
1147
|
-
if (
|
|
1165
|
+
if (
|
|
1166
|
+
framework !== "astro" &&
|
|
1167
|
+
framework !== "nextjs" &&
|
|
1168
|
+
framework !== "vite" &&
|
|
1169
|
+
framework !== "tanstack-start"
|
|
1170
|
+
)
|
|
1171
|
+
return null;
|
|
1148
1172
|
let path: string | null;
|
|
1149
1173
|
try {
|
|
1150
|
-
path = resolveFrameworkConfig(
|
|
1174
|
+
path = resolveFrameworkConfig(
|
|
1175
|
+
framework === "astro"
|
|
1176
|
+
? "astro.config"
|
|
1177
|
+
: framework === "nextjs"
|
|
1178
|
+
? "next.config"
|
|
1179
|
+
: "vite.config",
|
|
1180
|
+
files,
|
|
1181
|
+
);
|
|
1151
1182
|
} catch (error) {
|
|
1152
1183
|
// The shared rules name the ambiguous variants; none of them is judged.
|
|
1153
1184
|
if (error instanceof FrameworkAdmissionError) return null;
|
|
@@ -1266,7 +1297,11 @@ function renderConfiguration(input: {
|
|
|
1266
1297
|
|
|
1267
1298
|
let output = "dist";
|
|
1268
1299
|
if (input.framework === "nextjs") output = ".open-next/assets";
|
|
1269
|
-
else if (
|
|
1300
|
+
else if (
|
|
1301
|
+
input.framework === "tanstack-start" ||
|
|
1302
|
+
(input.framework === "astro" && input.tanStackRuntime === "edge")
|
|
1303
|
+
)
|
|
1304
|
+
output = "dist/client";
|
|
1270
1305
|
|
|
1271
1306
|
let mode = "auto";
|
|
1272
1307
|
if (input.framework === "functions") mode = "functions";
|
|
@@ -63,7 +63,9 @@ export interface SafeProblem {
|
|
|
63
63
|
|
|
64
64
|
export interface PublicCurrentIdentity {
|
|
65
65
|
readonly actor_id: string;
|
|
66
|
+
readonly email: string | null;
|
|
66
67
|
readonly organization_ids: readonly string[];
|
|
68
|
+
readonly organizations: readonly { readonly id: string; readonly name: string }[];
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
/** One workspace the signed-in user belongs to, with the provider identity a selection needs. */
|
|
@@ -635,8 +637,10 @@ export function parseProjectCreditBudget(value: unknown): ProjectCreditBudget {
|
|
|
635
637
|
}
|
|
636
638
|
|
|
637
639
|
export const parseCurrentIdentity = (value: unknown): PublicCurrentIdentity => {
|
|
638
|
-
const input = exactRecord(value, ["actor_id", "organization_ids"]);
|
|
640
|
+
const input = exactRecord(value, ["actor_id", "email", "organization_ids", "organizations"]);
|
|
641
|
+
const email = input["email"];
|
|
639
642
|
const organizationIds = input["organization_ids"];
|
|
643
|
+
const organizations = input["organizations"];
|
|
640
644
|
if (
|
|
641
645
|
typeof input["actor_id"] !== "string" ||
|
|
642
646
|
input["actor_id"].length === 0 ||
|
|
@@ -645,12 +649,32 @@ export const parseCurrentIdentity = (value: unknown): PublicCurrentIdentity => {
|
|
|
645
649
|
const codePoint = character.codePointAt(0) ?? 0;
|
|
646
650
|
return codePoint <= 31 || codePoint === 127;
|
|
647
651
|
}) ||
|
|
652
|
+
(email !== null &&
|
|
653
|
+
(!boundedString(email, 3, 254) ||
|
|
654
|
+
!/^[^\s@]+@[^\s@.]+(?:\.[^\s@.]+)+$/u.test(email) ||
|
|
655
|
+
/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(email))) ||
|
|
648
656
|
!Array.isArray(organizationIds) ||
|
|
649
657
|
organizationIds.some((organizationId) => !isUlid(organizationId)) ||
|
|
650
|
-
new Set(organizationIds).size !== organizationIds.length
|
|
658
|
+
new Set(organizationIds).size !== organizationIds.length ||
|
|
659
|
+
!Array.isArray(organizations) ||
|
|
660
|
+
organizations.length !== organizationIds.length
|
|
651
661
|
) {
|
|
652
662
|
throw new ResponseContractError();
|
|
653
663
|
}
|
|
664
|
+
organizations.forEach((organization: unknown, index) => {
|
|
665
|
+
const entry = exactRecord(organization, ["id", "name"]);
|
|
666
|
+
if (
|
|
667
|
+
entry["id"] !== organizationIds[index] ||
|
|
668
|
+
!boundedString(entry["name"], 1, 128) ||
|
|
669
|
+
entry["name"].trim() !== entry["name"] ||
|
|
670
|
+
[...entry["name"]].some((character) => {
|
|
671
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
672
|
+
return codePoint <= 31 || codePoint === 127;
|
|
673
|
+
})
|
|
674
|
+
) {
|
|
675
|
+
throw new ResponseContractError();
|
|
676
|
+
}
|
|
677
|
+
});
|
|
654
678
|
return input as unknown as PublicCurrentIdentity;
|
|
655
679
|
};
|
|
656
680
|
|