pomerado 0.1.2 → 0.2.0
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/CHANGELOG.md +45 -0
- package/LICENSE +21 -661
- package/README.md +12 -9
- package/dist/typescript/authoring/auth/SKILL.md +21 -159
- package/dist/typescript/authoring/caller-input/SKILL.md +7 -68
- package/dist/typescript/authoring/core/SKILL.md +40 -330
- package/dist/typescript/authoring/forms/SKILL.md +7 -78
- package/dist/typescript/authoring/pagination/SKILL.md +5 -22
- package/dist/typescript/authoring/workspace/AGENTS.md +53 -293
- package/dist/typescript/authoring/workspace/README.md +2 -10
- package/dist/typescript/authoring/writes/SKILL.md +12 -140
- package/dist/typescript/src/execution/sign-in-diagnostics.d.ts +19 -20
- package/dist/typescript/src/guardian/openai.js +3 -1
- package/dist/typescript/src/mint/contracts.d.ts +36 -9
- package/dist/typescript/src/mint/harness.js +80 -8
- package/dist/typescript/src/mint/openai.js +4 -2
- package/dist/typescript/src/mint/skills.d.ts +4 -0
- package/dist/typescript/src/mint/skills.js +60 -61
- package/dist/typescript/src/runtime/input-request.d.ts +1 -0
- package/dist/typescript/src/runtime/input-request.js +2 -1
- package/dist/typescript/src/runtime/provider-metadata.d.ts +7 -6
- package/dist/typescript/src/runtime/script-input.d.ts +2 -2
- package/dist/typescript/src/runtime/script-input.js +13 -3
- package/dist/typescript/src/standalone/mcp-cli.js +13 -2
- package/dist/typescript/src/standalone/mcp-package.js +73 -23
- package/package.json +3 -2
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
export type BrowserMode = "headless" | "headful" | "headful-gpu";
|
|
2
2
|
export type RetainedProviderStage = "profile_decode" | "connection_decode" | "connection_duplicate_fields" | "timeline_decode" | "browser_session_mismatch" | "login_browser_decode" | "login_already_running" | "login_response_decode" | "submit_state"
|
|
3
|
-
/**
|
|
3
|
+
/** The provider answered the submission `accepted: false`. */
|
|
4
4
|
| "submit_rejected" | "submit_response_decode" | "cleanup_identity_decode";
|
|
5
5
|
export type RetainedProviderSchemaField = "profile" | "profile.id" | "profile_save_changes" | "stealth" | "browser.stealth" | "other";
|
|
6
6
|
export type RetainedProviderCode = "Unavailable" | "InvalidConfiguration" | "ProxyUnavailable" | "AllocationUncertain"
|
|
7
|
-
/**
|
|
7
|
+
/** The provider answered the create with a definite refusal, so no browser exists. */
|
|
8
8
|
| "AllocationRejected" | "StopUnconfirmed" | "UnexpectedBrowserState" | "StorageUnavailable" | "BindingNotFound";
|
|
9
|
-
/**
|
|
10
|
-
export type
|
|
11
|
-
|
|
9
|
+
/** Network failure codes a host retains in sign-in diagnostics, as its provider reported them. */
|
|
10
|
+
export type RetainedNetworkError = "upstream_timeout" | "provider_unreachable" | "upstream_connect_failed" | "upstream_dns_failure" | "origin_tls_timeout" | "restricted_route_unavailable" | "destination_route_unavailable" | "proxy_unavailable" | "origin_response_incomplete" | "provider_rejected" | "provider_blacklisted" | "destination_blocked" | "mitm_certificate" | "mitm_tls" | "mitm_tls_rejected" | "mitm_connect" | "mitm_stream" | "mitm_io" | "mitm_timeout" | "mitm_canceled" | "mitm_upstream_proxy" | "mitm_h1" | "mitm_request_invalid" | "mitm_other" | "proxy_forbidden" | "proxy_auth_required" | "proxy_rate_limited" | "other";
|
|
11
|
+
/** How the host replaced a browser after a failure, as the agent sees it. */
|
|
12
|
+
export type BrowserRecoverySummary = {
|
|
12
13
|
readonly kind: "switched";
|
|
13
14
|
readonly egress: "proxy" | "direct";
|
|
14
15
|
readonly cause: string;
|
|
@@ -16,7 +17,7 @@ export type ProxySwitchSummary = {
|
|
|
16
17
|
readonly possiblySent: boolean;
|
|
17
18
|
readonly inFlightPossiblySent: true;
|
|
18
19
|
readonly repeated: false;
|
|
19
|
-
/** The browser's cookies, cache and site storage were cleared for the
|
|
20
|
+
/** The browser's cookies, cache and site storage were cleared for the replacement. */
|
|
20
21
|
readonly stateCleared: true;
|
|
21
22
|
/** Set when the clear ended the site's signed-in session. */
|
|
22
23
|
readonly signIn?: "again_once" | "unavailable";
|
|
@@ -32,7 +32,7 @@ export declare const ScriptQuestionDeclaration: Schema.Union<[Schema.Struct<{
|
|
|
32
32
|
prompt: Schema.filter<Schema.filter<typeof Schema.String>>;
|
|
33
33
|
}>]>;
|
|
34
34
|
export type ScriptQuestionDeclaration = typeof ScriptQuestionDeclaration.Type;
|
|
35
|
-
export declare const ScriptQuestionDeclarations: Schema.Record$<Schema.filter<typeof Schema.String>, Schema.Union<[Schema.Struct<{
|
|
35
|
+
export declare const ScriptQuestionDeclarations: Schema.transform<Schema.filter<typeof Schema.Unknown>, Schema.Record$<Schema.filter<typeof Schema.String>, Schema.Union<[Schema.Struct<{
|
|
36
36
|
type: Schema.Literal<["choice"]>;
|
|
37
37
|
prompt: Schema.filter<Schema.filter<typeof Schema.String>>;
|
|
38
38
|
allowOther: Schema.optional<typeof Schema.Boolean>;
|
|
@@ -61,7 +61,7 @@ export declare const ScriptQuestionDeclarations: Schema.Record$<Schema.filter<ty
|
|
|
61
61
|
maxLength: Schema.optional<Schema.filter<typeof Schema.Int>>;
|
|
62
62
|
}>, Schema.Struct<{
|
|
63
63
|
prompt: Schema.filter<Schema.filter<typeof Schema.String>>;
|
|
64
|
-
}>]
|
|
64
|
+
}>]>>>;
|
|
65
65
|
export type ScriptQuestionDeclarations = Readonly<Record<string, ScriptQuestionDeclaration>>;
|
|
66
66
|
/** One option the page offers now. */
|
|
67
67
|
export declare const ScriptOption: Schema.Struct<{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { Context, Data, Effect, Either, Schema } from "effect";
|
|
3
|
-
import { ConfirmQuestion, InputRequest, QuestionId, SecretQuestion, TextQuestion, validateAnswer, } from "./input-request.js";
|
|
2
|
+
import { Context, Data, Effect, Either, Schema, SchemaAST } from "effect";
|
|
3
|
+
import { ConfirmQuestion, InputRequest, QuestionId, questionIdPattern, SecretQuestion, TextQuestion, validateAnswer, } from "./input-request.js";
|
|
4
4
|
/**
|
|
5
5
|
* A script asks its caller through the one input request (source `script`). It declares each
|
|
6
6
|
* question once in its contract, so the publication review reads every prompt, and passes the
|
|
@@ -35,10 +35,20 @@ export const ScriptQuestionDeclaration = Schema.Union(Schema.Struct({
|
|
|
35
35
|
}),
|
|
36
36
|
// The pre-unification shape, `{ prompt }`, kept for published revisions: a single choice.
|
|
37
37
|
Schema.Struct({ prompt: Schema.String.pipe(Schema.minLength(1), Schema.maxLength(500)) }));
|
|
38
|
-
|
|
38
|
+
const QuestionDeclarations = Schema.Record({
|
|
39
39
|
key: QuestionId,
|
|
40
40
|
value: ScriptQuestionDeclaration,
|
|
41
41
|
});
|
|
42
|
+
export const ScriptQuestionDeclarations = Schema.Unknown.pipe(
|
|
43
|
+
// Check original keys before record construction can discard __proto__ or another invalid id.
|
|
44
|
+
Schema.filter((questions) => typeof questions !== "object" ||
|
|
45
|
+
questions === null ||
|
|
46
|
+
Object.keys(questions).every((id) => questionIdPattern.test(id)), {
|
|
47
|
+
message: () => "question ids must start with a lowercase letter and contain only lowercase letters, digits, or underscores (up to 64 characters)",
|
|
48
|
+
}), Schema.compose(QuestionDeclarations)).annotations({
|
|
49
|
+
// Schema generation still describes the declarations rather than the unvalidated input.
|
|
50
|
+
[SchemaAST.SurrogateAnnotationId]: QuestionDeclarations.ast,
|
|
51
|
+
});
|
|
42
52
|
/** One option the page offers now. */
|
|
43
53
|
export const ScriptOption = Schema.Struct({
|
|
44
54
|
/** What `ask` returns when the caller picks it. It never leaves the sandbox. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
2
3
|
import { parseArgs } from "node:util";
|
|
3
4
|
import { pathToFileURL } from "node:url";
|
|
4
5
|
import { resolve } from "node:path";
|
|
@@ -98,6 +99,16 @@ export const startMcpCli = (args = process.argv.slice(2), options = {}) => {
|
|
|
98
99
|
},
|
|
99
100
|
});
|
|
100
101
|
};
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
/** npm links a bin to this file, so the entry path counts once its links are resolved. */
|
|
103
|
+
const isEntrypoint = (path) => {
|
|
104
|
+
if (path === undefined)
|
|
105
|
+
return false;
|
|
106
|
+
try {
|
|
107
|
+
return pathToFileURL(realpathSync(path)).href === import.meta.url;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
if (isEntrypoint(process.argv[1]))
|
|
103
114
|
startMcpCli();
|
|
@@ -6,12 +6,29 @@ import { localFilePath, localPromise, localError, localRelativePath, } from "../
|
|
|
6
6
|
import { Deployment, writeArtifact } from "./artifact-files.js";
|
|
7
7
|
const launcher = `import { fileURLToPath } from 'node:url';
|
|
8
8
|
const runtime = process.argv[2];
|
|
9
|
-
if (!runtime) throw new Error('Start this integration
|
|
9
|
+
if (!runtime) throw new Error('Start this integration with the command and arguments in its mcp.json.');
|
|
10
10
|
const { startMcpCli } = await import(runtime);
|
|
11
11
|
startMcpCli(['serve', '--artifact', fileURLToPath(new URL('.', import.meta.url))]);
|
|
12
12
|
`;
|
|
13
13
|
const shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Top-level names authored source may not use, compared in lower case. They cover the packaging
|
|
16
|
+
* files, the TOML that 0.1.2 wrote and its docs told users to copy into Codex, and the project
|
|
17
|
+
* config an MCP client might load from this folder.
|
|
18
|
+
*/
|
|
19
|
+
const reserved = new Set([
|
|
20
|
+
"deployment.json",
|
|
21
|
+
"mcp.mjs",
|
|
22
|
+
"mcp.json",
|
|
23
|
+
"readme.md",
|
|
24
|
+
"codex-mcp.toml",
|
|
25
|
+
".mcp.json",
|
|
26
|
+
".vscode",
|
|
27
|
+
".cursor",
|
|
28
|
+
".codex",
|
|
29
|
+
".gemini",
|
|
30
|
+
".claude",
|
|
31
|
+
]);
|
|
15
32
|
/** Only the local operator's configuration chooses the root; tool arguments choose one slug. */
|
|
16
33
|
export const prepareIntegration = (options) => Effect.gen(function* () {
|
|
17
34
|
const deployment = yield* Schema.decodeUnknown(Deployment)({
|
|
@@ -46,37 +63,70 @@ export const prepareIntegration = (options) => Effect.gen(function* () {
|
|
|
46
63
|
yield* writeArtifact(directory, artifact);
|
|
47
64
|
const workspace = yield* createLocalWorkspace({ root: directory });
|
|
48
65
|
const launcherPath = join(directory, "mcp.mjs");
|
|
49
|
-
const configPath = join(directory, "
|
|
66
|
+
const configPath = join(directory, "mcp.json");
|
|
50
67
|
const runtime = new URL("./mcp-cli.js", import.meta.url).href;
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
const args = [launcherPath, runtime];
|
|
69
|
+
const configuration = {
|
|
70
|
+
mcpServers: { [deployment.name]: { command: process.execPath, args } },
|
|
71
|
+
};
|
|
72
|
+
const command = [process.execPath, ...args].map(shellQuote).join(" ");
|
|
56
73
|
yield* workspace.write("deployment.json", `${JSON.stringify(deployment, null, 2)}\n`);
|
|
57
74
|
yield* workspace.write("mcp.mjs", launcher);
|
|
58
|
-
yield* workspace.write("
|
|
75
|
+
yield* workspace.write("mcp.json", `${JSON.stringify(configuration, null, 2)}\n`);
|
|
59
76
|
yield* workspace.write("README.md", `# ${deployment.name}
|
|
60
77
|
|
|
61
|
-
This integration
|
|
62
|
-
|
|
78
|
+
This integration runs on your computer as a local MCP stdio server. mcp.json holds its server
|
|
79
|
+
entry in the standard mcpServers format. The entry starts your Node with this directory's
|
|
80
|
+
launcher and your installed Pomerado runtime.
|
|
81
|
+
|
|
82
|
+
## Add it to your MCP client
|
|
83
|
+
|
|
84
|
+
- Claude Code passes its own environment to the server.
|
|
85
|
+
|
|
86
|
+
\`\`\`sh
|
|
87
|
+
claude mcp add ${deployment.name} -- ${command}
|
|
88
|
+
\`\`\`
|
|
89
|
+
|
|
90
|
+
- Codex passes servers only a short list of environment variables. After adding the server, put
|
|
91
|
+
\`env_vars = ["OPENAI_API_KEY"]\` under \`[mcp_servers.${deployment.name}]\` in
|
|
92
|
+
~/.codex/config.toml, or $CODEX_HOME/config.toml when CODEX_HOME is set.
|
|
93
|
+
|
|
94
|
+
\`\`\`sh
|
|
95
|
+
codex mcp add ${deployment.name} -- ${command}
|
|
96
|
+
\`\`\`
|
|
63
97
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
98
|
+
- Gemini CLI hides variables named like keys from servers. The -e flag below passes
|
|
99
|
+
OPENAI_API_KEY by reference, so the settings file holds no key.
|
|
100
|
+
|
|
101
|
+
\`\`\`sh
|
|
102
|
+
gemini mcp add -e 'OPENAI_API_KEY=$OPENAI_API_KEY' ${deployment.name} ${command}
|
|
103
|
+
\`\`\`
|
|
104
|
+
|
|
105
|
+
- Cursor, VS Code, Claude Desktop and other clients that read an mcpServers JSON file take the
|
|
106
|
+
entry from mcp.json. In Cursor, add \`"env": { "OPENAI_API_KEY": "\${env:OPENAI_API_KEY}" }\`
|
|
107
|
+
to it.
|
|
108
|
+
|
|
109
|
+
## Model key
|
|
110
|
+
|
|
111
|
+
The server needs OPENAI_API_KEY in its environment, because Guardian reviews every run. Model
|
|
112
|
+
requests go to the configured provider. This directory and mcp.json hold no key. Give the key to
|
|
113
|
+
the server through your client's environment settings, never through chat.
|
|
114
|
+
|
|
115
|
+
## Call it
|
|
67
116
|
|
|
68
117
|
Call ${deployment.name} with its discovered input schema. Its URL, intent, authority and
|
|
69
|
-
authentication origins are pinned in deployment.json.
|
|
70
|
-
get_job, provide_input and cancel_job
|
|
118
|
+
authentication origins are pinned in deployment.json. A call that needs an answer or more time
|
|
119
|
+
returns a job ID. Continue that job with get_job, provide_input and cancel_job. Polling never
|
|
120
|
+
resubmits an operation.
|
|
121
|
+
|
|
122
|
+
Answers sent through provide_input are visible to your MCP client and its model provider.
|
|
123
|
+
Restarting the server loses live jobs.
|
|
71
124
|
|
|
72
|
-
|
|
73
|
-
local Chromium. Configured model providers receive model requests. The TOML forwards
|
|
74
|
-
OPENAI_API_KEY from Codex's environment. Supply provider keys in
|
|
75
|
-
the server's environment; this directory and its configuration contain no keys. Answers sent
|
|
76
|
-
through provide_input are visible to the MCP client and its model. Restarting loses live jobs.
|
|
125
|
+
## Paths
|
|
77
126
|
|
|
78
|
-
The launcher
|
|
79
|
-
|
|
127
|
+
The launcher uses your installed Pomerado runtime, its minter and Guardian dependencies, and
|
|
128
|
+
local Chromium. The launcher source is portable. mcp.json names your current Node and Pomerado
|
|
129
|
+
installation. Update those paths if you move either installation or this directory.
|
|
80
130
|
`);
|
|
81
131
|
completed = true;
|
|
82
132
|
return { directory, configPath, launcherPath };
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pomerado",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Pomerado's integration minter and Guardian with local compute and native Playwright",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"license": "
|
|
6
|
+
"license": "MIT",
|
|
7
7
|
"homepage": "https://pomerado.ai",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -595,6 +595,7 @@
|
|
|
595
595
|
},
|
|
596
596
|
"files": [
|
|
597
597
|
"dist",
|
|
598
|
+
"CHANGELOG.md",
|
|
598
599
|
"LICENSE",
|
|
599
600
|
"README.md",
|
|
600
601
|
"third-party/codex/LICENSE",
|