@amerged/ohmyhost-mcp 0.1.37 → 0.1.39
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/THIRD_PARTY_NOTICES.md +27 -1
- package/dist/{chunk-CHAFMFTK.js → chunk-KQS5IMSO.js} +7606 -2403
- package/dist/index.js +1 -1
- package/dist/licenses/pgcf-bridge-LICENSE +201 -0
- package/dist/licenses/pgcf-bridge-NOTICE +1 -0
- package/dist/stdio-main.js +3853 -34
- package/package.json +1 -1
- package/src/apps/mcp/package.json +4 -4
- package/src/apps/mcp/src/local-client.ts +15 -9
- package/src/apps/mcp/src/local-server.ts +36 -18
- package/src/apps/product-cli/package.json +4 -3
- package/src/apps/product-cli/src/cli.ts +7 -5
- package/src/apps/product-cli/src/command.ts +18 -5
- package/src/apps/product-cli/src/product-api.ts +22 -15
- package/src/apps/product-cli/src/psql-session.ts +54 -19
- package/src/apps/product-cli/src/repository-init.ts +181 -9
- package/src/packages/agent-skills/package.json +1 -1
- package/src/packages/agent-skills/src/generated-skill-resources.ts +16 -16
- package/src/packages/contracts/package.json +6 -1
- package/src/packages/contracts/src/credit-pricing.ts +7 -1
- package/src/packages/contracts/src/database-access.ts +53 -12
- package/src/packages/contracts/src/database-compute.ts +66 -11
- package/src/packages/contracts/src/database-sizes.ts +114 -0
- package/src/packages/contracts/src/framework-admission-corpus.ts +15 -18
- package/src/packages/contracts/src/framework-build-script.mjs +10 -3
- package/src/packages/contracts/src/index.ts +3 -0
- package/src/packages/contracts/src/operation-failure.ts +2 -2
- package/src/packages/contracts/src/platform-origins.ts +8 -0
- package/src/packages/contracts/src/problem-policy.ts +16 -2
- package/src/packages/contracts/src/storage-gateway-origin.ts +93 -0
- package/src/packages/postgres-bridge/LICENSE +201 -0
- package/src/packages/postgres-bridge/NOTICE +1 -0
- package/src/packages/postgres-bridge/package.json +26 -0
- package/src/packages/postgres-bridge/src/auth.ts +159 -0
- package/src/packages/postgres-bridge/src/bridge.ts +168 -0
- package/src/packages/postgres-bridge/src/index.ts +2 -0
- package/src/packages/postgres-bridge/src/options.ts +49 -0
- package/src/packages/sdk-ts/package.json +2 -2
- package/src/packages/sdk-ts/src/generated/index.ts +1 -0
- package/src/packages/sdk-ts/src/generated/sdk.gen.ts +9 -8
- package/src/packages/sdk-ts/src/generated/types.gen.ts +91 -24
- package/src/packages/sdk-ts/src/index.ts +1 -0
- package/src/packages/sdk-ts/src/managed-mail-attachment.ts +107 -0
- package/src/packages/workos-auth-contracts/package.json +1 -1
|
@@ -75,6 +75,8 @@ export type RepositoryFramework = Exclude<FrameworkFamilyClassification, "ambigu
|
|
|
75
75
|
const MAXIMUM_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
76
76
|
const SOURCE_MODULE_PATTERN = /\.(?:[cm]?[jt]s|[jt]sx)$/u;
|
|
77
77
|
const STORAGE_CLIENT_CALL_PATTERN = /\bcreatePrivateStorageClient\s*\(/u;
|
|
78
|
+
const GATEWAY_BINDING_NAME = /^OHMYHOST_(?:MAIL|STORAGE)_GATEWAY$/u;
|
|
79
|
+
const GATEWAY_WORD_TOKEN = /[\p{ID_Start}_$][\p{ID_Continue}$]*/uy;
|
|
78
80
|
// The catalog names a module only by a path of its grammar; a file name with a space, for example,
|
|
79
81
|
// is not one, so its blocker carries the path beside a sentence that does not quote it.
|
|
80
82
|
const UNNAMED_MODULE_MESSAGES: Readonly<Record<DatabaseContractCode, string>> = Object.freeze({
|
|
@@ -89,15 +91,19 @@ function callsStorageClient(source: string): boolean {
|
|
|
89
91
|
return STORAGE_CLIENT_CALL_PATTERN.test(sourceWithoutComments(source));
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
+
// Global fetch reaches the private storage gateway using these values and its environment key.
|
|
95
|
+
// No raw bucket binding is handed to customer code.
|
|
94
96
|
const STORAGE_RUNTIME_BINDINGS = Object.freeze([
|
|
95
97
|
"OHMYHOST_ENVIRONMENT_ID",
|
|
96
98
|
"OHMYHOST_PROJECT_ID",
|
|
97
|
-
"OHMYHOST_STORAGE_GATEWAY",
|
|
98
99
|
"OHMYHOST_STORAGE_GATEWAY_URL",
|
|
99
100
|
"OHMYHOST_STORAGE_KEY",
|
|
100
101
|
]);
|
|
102
|
+
const MAIL_RUNTIME_BINDINGS = Object.freeze([
|
|
103
|
+
"OHMYHOST_MAIL_GATEWAY_URL",
|
|
104
|
+
"OHMYHOST_MAIL_KEY",
|
|
105
|
+
"OHMYHOST_PROJECT_ID",
|
|
106
|
+
]);
|
|
101
107
|
// The runtime release is published unchanged as `@amerged/ohmyhost-runtime`; this pinned alias keeps
|
|
102
108
|
// its import name and, unlike the website archive each release removes, stays installable.
|
|
103
109
|
const CUSTOMER_RUNTIME_SOURCE = `npm:@amerged/ohmyhost-runtime@${cliPackage.version}`;
|
|
@@ -105,6 +111,7 @@ const CUSTOMER_RUNTIME_SOURCE = `npm:@amerged/ohmyhost-runtime@${cliPackage.vers
|
|
|
105
111
|
/** A catalog code, which planning answers with the same sentence, or a check only init runs. */
|
|
106
112
|
export type RepositoryInitBlockerCode =
|
|
107
113
|
| FrameworkConversionDiagnosticCode
|
|
114
|
+
| "gateway_binding_unsupported"
|
|
108
115
|
| "migration_filename_noncanonical"
|
|
109
116
|
| "migration_sql_not_admitted"
|
|
110
117
|
| "storage_client_missing";
|
|
@@ -507,7 +514,7 @@ interface SourceCheckContext {
|
|
|
507
514
|
/**
|
|
508
515
|
* Every blocker of a source with a valid or proposed configuration, in the order planning judges:
|
|
509
516
|
* migration filenames, the shared source rules, the Worker module handlers and the database
|
|
510
|
-
* contract; init's own storage client and migration SQL checks come last.
|
|
517
|
+
* contract; init's own gateway binding, storage client and migration SQL checks come last.
|
|
511
518
|
*/
|
|
512
519
|
async function judgeSource(
|
|
513
520
|
context: SourceCheckContext &
|
|
@@ -528,6 +535,7 @@ async function judgeSource(
|
|
|
528
535
|
...source.findings.map((finding) => requiredCatalogBlocker(finding, context.applicationRoot)),
|
|
529
536
|
...(await workerModuleBlockers(context)),
|
|
530
537
|
...(await databaseContractBlockers(context, context.provider)),
|
|
538
|
+
...(await gatewayBindingBlockers(context)),
|
|
531
539
|
...(await storageClientBlockers(context)),
|
|
532
540
|
...(await migrationSqlBlockers(context, context.provider)),
|
|
533
541
|
],
|
|
@@ -621,6 +629,168 @@ async function workerModuleBlockers(context: SourceCheckContext): Promise<Reposi
|
|
|
621
629
|
);
|
|
622
630
|
}
|
|
623
631
|
|
|
632
|
+
/** Keep literal text inert, but scan executable template interpolations as ordinary code. */
|
|
633
|
+
function gatewaySourceTokens(source: string): readonly string[] {
|
|
634
|
+
const tokens: string[] = [];
|
|
635
|
+
let cursor = 0;
|
|
636
|
+
const quoted = (quote: string) => {
|
|
637
|
+
const start = cursor++;
|
|
638
|
+
while (cursor < source.length) {
|
|
639
|
+
const character = source[cursor++];
|
|
640
|
+
if (character === "\\") cursor++;
|
|
641
|
+
else if (character === quote) break;
|
|
642
|
+
}
|
|
643
|
+
tokens.push(source.slice(start, cursor));
|
|
644
|
+
};
|
|
645
|
+
const code = (interpolation: boolean): void => {
|
|
646
|
+
let braces = 0;
|
|
647
|
+
while (cursor < source.length) {
|
|
648
|
+
const character = source[cursor] as string;
|
|
649
|
+
if (interpolation && character === "}" && braces === 0) {
|
|
650
|
+
cursor++;
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (character === '"' || character === "'") {
|
|
654
|
+
quoted(character);
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
if (character === "`") {
|
|
658
|
+
cursor++;
|
|
659
|
+
while (cursor < source.length) {
|
|
660
|
+
const literal = source[cursor++];
|
|
661
|
+
if (literal === "\\") cursor++;
|
|
662
|
+
else if (literal === "`") break;
|
|
663
|
+
else if (literal === "$" && source[cursor] === "{") {
|
|
664
|
+
cursor++;
|
|
665
|
+
tokens.push("(");
|
|
666
|
+
code(true);
|
|
667
|
+
tokens.push(")");
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
if (/\s/u.test(character)) {
|
|
673
|
+
cursor++;
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
if (character === "{") braces++;
|
|
677
|
+
if (character === "}") braces--;
|
|
678
|
+
GATEWAY_WORD_TOKEN.lastIndex = cursor;
|
|
679
|
+
const word = GATEWAY_WORD_TOKEN.exec(source)?.[0];
|
|
680
|
+
const token =
|
|
681
|
+
word ??
|
|
682
|
+
(["?.", "=>"].includes(source.slice(cursor, cursor + 2))
|
|
683
|
+
? source.slice(cursor, cursor + 2)
|
|
684
|
+
: character);
|
|
685
|
+
tokens.push(token);
|
|
686
|
+
cursor += token.length;
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
code(false);
|
|
690
|
+
return tokens;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/** Static value accesses only: declarations, literal messages and object-literal keys are inert. */
|
|
694
|
+
function usesGatewayBinding(source: string): boolean {
|
|
695
|
+
const tokens = gatewaySourceTokens(sourceWithoutComments(source));
|
|
696
|
+
const objects: boolean[] = [];
|
|
697
|
+
let declaration: "type" | "interface" | "annotation" | undefined;
|
|
698
|
+
let declarationDepth = 0;
|
|
699
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
700
|
+
const token = tokens[index] as string;
|
|
701
|
+
const previous = tokens[index - 1];
|
|
702
|
+
const next = tokens[index + 1];
|
|
703
|
+
if (declaration !== undefined) {
|
|
704
|
+
if (
|
|
705
|
+
declarationDepth === 0 &&
|
|
706
|
+
(token === ";" || /^(?:export|const|let|var|function|class|type|interface)$/u.test(token))
|
|
707
|
+
) {
|
|
708
|
+
declaration = undefined;
|
|
709
|
+
if (token === ";") continue;
|
|
710
|
+
} else {
|
|
711
|
+
if (token === "{" || token === "(" || token === "[") declarationDepth++;
|
|
712
|
+
if (token === "}" || token === ")" || token === "]") {
|
|
713
|
+
declarationDepth--;
|
|
714
|
+
if (declaration !== "type" && declarationDepth === 0) declaration = undefined;
|
|
715
|
+
}
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (
|
|
720
|
+
(token === "type" || token === "interface") &&
|
|
721
|
+
next !== undefined &&
|
|
722
|
+
/^[\p{ID_Start}_$][\p{ID_Continue}$]*$/u.test(next) &&
|
|
723
|
+
previous !== "." &&
|
|
724
|
+
previous !== "?."
|
|
725
|
+
) {
|
|
726
|
+
declaration = token;
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
// An inline variable type is not an object binding pattern; resume at its initializer.
|
|
730
|
+
if (token === "{" && previous === ":" && /^(?:const|let|var)$/u.test(tokens[index - 3] ?? "")) {
|
|
731
|
+
declaration = "annotation";
|
|
732
|
+
declarationDepth = 1;
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
if (token === "{") objects.push(false);
|
|
736
|
+
if (token === "}") {
|
|
737
|
+
const gateway = objects.pop();
|
|
738
|
+
let after = index + 1;
|
|
739
|
+
if (tokens[after] === ":") {
|
|
740
|
+
after++;
|
|
741
|
+
while (after < tokens.length && !/^(?:=|;|\{|\})$/u.test(tokens[after] as string)) after++;
|
|
742
|
+
}
|
|
743
|
+
if (gateway && tokens[after] === "=") return true;
|
|
744
|
+
}
|
|
745
|
+
const quoted = token.startsWith('"') || token.startsWith("'");
|
|
746
|
+
const name = quoted ? token.slice(1, -1) : token;
|
|
747
|
+
if (!GATEWAY_BINDING_NAME.test(name)) continue;
|
|
748
|
+
if (!quoted && (previous === "." || previous === "?.")) return true;
|
|
749
|
+
const receiver = tokens[index - (tokens[index - 2] === "?." ? 3 : 2)] ?? "";
|
|
750
|
+
if (
|
|
751
|
+
quoted &&
|
|
752
|
+
previous === "[" &&
|
|
753
|
+
next === "]" &&
|
|
754
|
+
(/^[\p{ID_Start}_$][\p{ID_Continue}$]*$/u.test(receiver) ||
|
|
755
|
+
receiver === ")" ||
|
|
756
|
+
receiver === "]") &&
|
|
757
|
+
!/^(?:return|throw|yield|case|new|typeof|void|delete|await|in|of)$/u.test(receiver)
|
|
758
|
+
)
|
|
759
|
+
return true;
|
|
760
|
+
if (
|
|
761
|
+
objects.length > 0 &&
|
|
762
|
+
(previous === "{" || previous === ",") &&
|
|
763
|
+
(next === ":" || next === "," || next === "}" || next === "=")
|
|
764
|
+
)
|
|
765
|
+
objects[objects.length - 1] = true;
|
|
766
|
+
}
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Init names legacy mail/storage bindings before a customer builds code whose gateway calls the
|
|
772
|
+
* egress policy cannot serve. This local guidance does not change planning's admission policy.
|
|
773
|
+
*/
|
|
774
|
+
async function gatewayBindingBlockers(
|
|
775
|
+
context: SourceCheckContext,
|
|
776
|
+
): Promise<RepositoryInitBlocker[]> {
|
|
777
|
+
const blockers: RepositoryInitBlocker[] = [];
|
|
778
|
+
for (const path of context.files) {
|
|
779
|
+
if (!SOURCE_MODULE_PATTERN.test(path) || /\.d\.[cm]?ts$/u.test(path)) continue;
|
|
780
|
+
const absolute = resolve(context.applicationDirectory, path);
|
|
781
|
+
const information = await stat(absolute);
|
|
782
|
+
if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES) continue;
|
|
783
|
+
if (!usesGatewayBinding(await readFile(absolute, "utf8"))) continue;
|
|
784
|
+
blockers.push({
|
|
785
|
+
code: "gateway_binding_unsupported",
|
|
786
|
+
path: repositoryPath(context.applicationRoot, path),
|
|
787
|
+
message:
|
|
788
|
+
"Mail and storage gateway Service Bindings are unsupported. In server code, create the client from its OHMYHOST_MAIL_GATEWAY_URL or OHMYHOST_STORAGE_GATEWAY_URL, environment key and project ID, plus the environment ID for storage. Use the global fetch with fetch: (request) => fetch(request), and capabilityFetch: (request) => fetch(request) for storage signed URLs.",
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
return blockers;
|
|
792
|
+
}
|
|
793
|
+
|
|
624
794
|
/**
|
|
625
795
|
* Declared storage is reached only through the private gateway client. A storage-enabled app whose
|
|
626
796
|
* source never constructs that client deploys green and fails at its first upload, so name it here.
|
|
@@ -641,7 +811,7 @@ async function storageClientBlockers(
|
|
|
641
811
|
code: "storage_client_missing",
|
|
642
812
|
path: null,
|
|
643
813
|
message:
|
|
644
|
-
'ohmyhost.yaml declares storage, but no application source calls createPrivateStorageClient. Import it from "@ohmyhost/customer-runtime/storage" and build it in server code from
|
|
814
|
+
'ohmyhost.yaml declares storage, but no application source calls createPrivateStorageClient. Import it from "@ohmyhost/customer-runtime/storage" and build it in server code from OHMYHOST_STORAGE_GATEWAY_URL, OHMYHOST_PROJECT_ID, OHMYHOST_ENVIRONMENT_ID and OHMYHOST_STORAGE_KEY, with the global fetch as fetch: (request) => fetch(request) and capabilityFetch: (request) => fetch(request). Reserve an upload, PUT the bytes to the returned signed URL, complete it, and serve reads with createSignedRead. There is no FILES bucket binding.',
|
|
645
815
|
},
|
|
646
816
|
];
|
|
647
817
|
}
|
|
@@ -1418,10 +1588,12 @@ function runtimeContract(capabilities: RuntimeCapabilities): Readonly<{
|
|
|
1418
1588
|
return Object.freeze({
|
|
1419
1589
|
bindings: Object.freeze(
|
|
1420
1590
|
[
|
|
1421
|
-
...(
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1591
|
+
...new Set([
|
|
1592
|
+
...(capabilities.auth ? ["BETTER_AUTH_SECRET"] : []),
|
|
1593
|
+
...(capabilities.storage ? STORAGE_RUNTIME_BINDINGS : []),
|
|
1594
|
+
...(capabilities.database ? ["OHMYHOST_DATABASE"] : []),
|
|
1595
|
+
...(capabilities.mail ? MAIL_RUNTIME_BINDINGS : []),
|
|
1596
|
+
]),
|
|
1425
1597
|
].sort(),
|
|
1426
1598
|
),
|
|
1427
1599
|
packages: Object.freeze({
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"build": "node scripts/generate-skill-resources.mjs && tsc --project tsconfig.build.json",
|
|
15
15
|
"generate:check": "node scripts/generate-skill-resources.mjs --check",
|
|
16
16
|
"test": "node scripts/generate-skill-resources.mjs --check && vitest run --root ../.. packages/agent-skills/test",
|
|
17
|
-
"typecheck": "tsc --project tsconfig.json --noEmit"
|
|
17
|
+
"typecheck": "tsc --project tsconfig.json --noEmit --incremental --tsBuildInfoFile typecheck.tsbuildinfo"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@ohmyhost/customer-runtime": "workspace:*",
|