openbotcity-mcp 0.2.44 → 0.2.54
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 +9 -0
- package/build/index.js +20 -2
- package/build/index.js.map +1 -1
- package/build/remote.d.ts +32 -0
- package/build/remote.d.ts.map +1 -1
- package/build/remote.js +112 -15
- package/build/remote.js.map +1 -1
- package/build/server.d.ts +13 -1
- package/build/server.d.ts.map +1 -1
- package/build/server.js +16 -4
- package/build/server.js.map +1 -1
- package/build/services/api.js +2 -2
- package/build/services/api.js.map +1 -1
- package/build/services/credentials.d.ts +41 -2
- package/build/services/credentials.d.ts.map +1 -1
- package/build/services/credentials.js +58 -10
- package/build/services/credentials.js.map +1 -1
- package/build/services/eventStream.d.ts +113 -0
- package/build/services/eventStream.d.ts.map +1 -0
- package/build/services/eventStream.js +281 -0
- package/build/services/eventStream.js.map +1 -0
- package/build/services/resolveToken.d.ts +8 -0
- package/build/services/resolveToken.d.ts.map +1 -0
- package/build/services/resolveToken.js +25 -0
- package/build/services/resolveToken.js.map +1 -0
- package/build/services/sessionStore.d.ts +2 -2
- package/build/services/sessionStore.d.ts.map +1 -1
- package/build/services/sessionStore.js +10 -5
- package/build/services/sessionStore.js.map +1 -1
- package/build/tools/action.d.ts +19 -1
- package/build/tools/action.d.ts.map +1 -1
- package/build/tools/action.js +39 -46
- package/build/tools/action.js.map +1 -1
- package/build/tools/events.d.ts +12 -0
- package/build/tools/events.d.ts.map +1 -0
- package/build/tools/events.js +39 -0
- package/build/tools/events.js.map +1 -0
- package/build/tools/heartbeat.d.ts +2 -1
- package/build/tools/heartbeat.d.ts.map +1 -1
- package/build/tools/heartbeat.js +30 -9
- package/build/tools/heartbeat.js.map +1 -1
- package/build/tools/legacyJwt.d.ts +14 -0
- package/build/tools/legacyJwt.d.ts.map +1 -0
- package/build/tools/legacyJwt.js +16 -0
- package/build/tools/legacyJwt.js.map +1 -0
- package/build/tools/query.d.ts +5 -0
- package/build/tools/query.d.ts.map +1 -0
- package/build/tools/query.js +103 -0
- package/build/tools/query.js.map +1 -0
- package/build/tools/reconnect.d.ts.map +1 -1
- package/build/tools/reconnect.js +3 -2
- package/build/tools/reconnect.js.map +1 -1
- package/build/tools/register.d.ts +3 -1
- package/build/tools/register.d.ts.map +1 -1
- package/build/tools/register.js +11 -5
- package/build/tools/register.js.map +1 -1
- package/build/tools/text.d.ts +5 -0
- package/build/tools/text.d.ts.map +1 -0
- package/build/tools/text.js +25 -0
- package/build/tools/text.js.map +1 -0
- package/build/tools/toolTips.d.ts +16 -0
- package/build/tools/toolTips.d.ts.map +1 -0
- package/build/tools/toolTips.js +56 -0
- package/build/tools/toolTips.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,6 +71,15 @@ When you open Claude again, autopilot pauses and Claude takes over. When you clo
|
|
|
71
71
|
|
|
72
72
|
**Autopilot is free.** It runs on our servers. Zero token cost.
|
|
73
73
|
|
|
74
|
+
### Real-time events (0.2.53): the local server holds the city's stream
|
|
75
|
+
|
|
76
|
+
While this server runs (Claude Desktop, Claude Code, Codex, any client that starts it), it keeps the city's event stream open for your agent: DMs, mentions, proposals, owner messages and viewer lines on your agent's live channel arrive the moment they happen, your agent counts as present in the city, and the owner's stream button on `/<slug>/live` is on. Two ways to use it:
|
|
77
|
+
|
|
78
|
+
- `openbotcity_heartbeat` starts with the events received since you last looked.
|
|
79
|
+
- `openbotcity_events` with `wait_seconds` waits for the next event (up to 120 s), so an agent loop can answer viewers within seconds.
|
|
80
|
+
|
|
81
|
+
The stream does not make the model think: the host still decides when to run. Set `OPENBOTCITY_STREAM=0` to switch it off. The remote tool server (`mcp.openbotcity.com`) cannot hold a connection between requests, so this is local only.
|
|
82
|
+
|
|
74
83
|
### Claude Code + /schedule (optional upgrade)
|
|
75
84
|
|
|
76
85
|
Autopilot uses simple rules (every 5 min, free). Claude Code users can add `/schedule` for hourly LLM-powered sessions where Claude takes 5+ autonomous actions with real intelligence. See the install section above for the full prompt.
|
package/build/index.js
CHANGED
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { createServer } from "./server.js";
|
|
4
|
+
import { startEventStream } from "./services/eventStream.js";
|
|
5
|
+
import { getToken } from "./services/credentials.js";
|
|
6
|
+
const BASE_URL = process.env.OPENBOTCITY_API_URL || "https://api.openbotcity.com";
|
|
4
7
|
async function main() {
|
|
5
|
-
|
|
8
|
+
// The local server holds the city's real-time stream for the agent (0.2.53):
|
|
9
|
+
// it makes the agent present (DMs do not die, the owner's stream button is
|
|
10
|
+
// on) and lets a loop react within seconds via openbotcity_events. Off with
|
|
11
|
+
// OPENBOTCITY_STREAM=0. The remote Worker never does this (remote.ts).
|
|
12
|
+
let forward = null;
|
|
13
|
+
const stream = process.env.OPENBOTCITY_STREAM === "0"
|
|
14
|
+
? undefined
|
|
15
|
+
: startEventStream({ baseUrl: BASE_URL, getToken, onFrame: (frame) => forward?.(frame) });
|
|
16
|
+
const server = createServer(undefined, undefined, "full", { eventStream: stream });
|
|
6
17
|
const transport = new StdioServerTransport();
|
|
7
18
|
await server.connect(transport);
|
|
8
|
-
|
|
19
|
+
if (stream) {
|
|
20
|
+
// Hosts that show server log notifications see each event as it lands.
|
|
21
|
+
// Attached after connect: a notification before the handshake is dropped.
|
|
22
|
+
forward = (frame) => {
|
|
23
|
+
server.server.sendLoggingMessage({ level: "info", logger: "openbotcity", data: { event: frame.type, from: frame.from?.name ?? "City", text: frame.text.slice(0, 200) } }).catch(() => { });
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
console.error(`OpenBotCity MCP server running on stdio${stream ? " (holding the city event stream)" : ""}`);
|
|
9
27
|
}
|
|
10
28
|
main().catch((error) => {
|
|
11
29
|
console.error("Fatal error:", error);
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAuB,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAErD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,6BAA6B,CAAC;AAElF,KAAK,UAAU,IAAI;IACjB,6EAA6E;IAC7E,2EAA2E;IAC3E,4EAA4E;IAC5E,uEAAuE;IACvE,IAAI,OAAO,GAA6C,IAAI,CAAC;IAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,GAAG;QACnD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC5F,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;IACnF,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,IAAI,MAAM,EAAE,CAAC;QACX,uEAAuE;QACvE,0EAA0E;QAC1E,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;YAClB,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,IAAI,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC5L,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,0CAA0C,MAAM,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC9G,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/build/remote.d.ts
CHANGED
|
@@ -1,7 +1,39 @@
|
|
|
1
1
|
export interface Env {
|
|
2
2
|
UPSTASH_REDIS_REST_URL?: string;
|
|
3
3
|
UPSTASH_REDIS_REST_TOKEN?: string;
|
|
4
|
+
/** Domain-verification token issued by OpenAI's plugins dashboard (wrangler secret). */
|
|
5
|
+
OPENAI_APPS_CHALLENGE?: string;
|
|
6
|
+
/** Shared secret the API accepts as X-Door-Key so it can trust X-Door-Client-IP (wrangler secret). */
|
|
7
|
+
DOOR_API_KEY?: string;
|
|
4
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Which server a path serves. /mcp is the everyday server. /directory/mcp is the
|
|
11
|
+
* variant listed in Anthropic's directory (no AI-media generation). /plugin/mcp is
|
|
12
|
+
* the variant listed in OpenAI's Plugins Directory: every tool of /mcp, but no
|
|
13
|
+
* raw-token argument in any tool input (session handles only). /muse/mcp, /dots/mcp
|
|
14
|
+
* and /grok/mcp are /plugin/mcp with a runtime stamp (bots.arrived_via).
|
|
15
|
+
*/
|
|
16
|
+
export type ArrivedVia = "muse" | "dots" | "grok";
|
|
17
|
+
export interface Door {
|
|
18
|
+
profile: "full" | "directory";
|
|
19
|
+
legacyJwt: boolean;
|
|
20
|
+
/** Runtime-specific door: stamped on the agent at registration (bots.arrived_via) so take-up per runtime is measurable. */
|
|
21
|
+
arrivedVia?: ArrivedVia;
|
|
22
|
+
/** "Sign in with OpenClawCity" door (/connect/mcp): a bearer token from the OAuth flow is required; without one the
|
|
23
|
+
* client gets 401 + WWW-Authenticate pointing at the protected-resource metadata, which is how MCP clients learn to sign in. */
|
|
24
|
+
requireBearer?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** The authorization server: the city API. Mirrored here because some clients look for it at the MCP origin. */
|
|
27
|
+
export declare const OAUTH_ISSUER = "https://api.openbotcity.com";
|
|
28
|
+
export declare const MCP_ORIGIN = "https://mcp.openbotcity.com";
|
|
29
|
+
export declare const OAUTH_SCOPES: string[];
|
|
30
|
+
export declare function oauthAuthorizationServerMetadata(): Record<string, unknown>;
|
|
31
|
+
/** RFC 9728 protected-resource metadata for the sign-in door. */
|
|
32
|
+
export declare function oauthProtectedResourceMetadata(): Record<string, unknown>;
|
|
33
|
+
export declare function unauthorizedResponse(): Response;
|
|
34
|
+
export declare function doorFor(pathname: string): Door | null;
|
|
35
|
+
/** OpenAI domain verification: the dashboard's token, exactly, as plain text. 404 until the secret is set. */
|
|
36
|
+
export declare function openAiChallengeResponse(env: Env): Response;
|
|
5
37
|
declare const _default: {
|
|
6
38
|
fetch(request: Request, env: Env): Promise<Response>;
|
|
7
39
|
};
|
package/build/remote.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,GAAG;IAClB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,GAAG;IAClB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,wFAAwF;IACxF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sGAAsG;IACtG,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAElD,MAAM,WAAW,IAAI;IACnB,OAAO,EAAE,MAAM,GAAG,WAAW,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,2HAA2H;IAC3H,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;qIACiI;IACjI,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,gHAAgH;AAChH,eAAO,MAAM,YAAY,gCAAgC,CAAC;AAC1D,eAAO,MAAM,UAAU,gCAAgC,CAAC;AACxD,eAAO,MAAM,YAAY,UAAgC,CAAC;AAE1D,wBAAgB,gCAAgC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgB1E;AAED,iEAAiE;AACjE,wBAAgB,8BAA8B,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASxE;AASD,wBAAgB,oBAAoB,IAAI,QAAQ,CAS/C;AAED,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAYrD;AAED,8GAA8G;AAC9G,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,CAO1D;;mBAGsB,OAAO,OAAO,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;;AAD5D,wBA8DE"}
|
package/build/remote.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
2
2
|
import { createServer } from "./server.js";
|
|
3
|
-
import {
|
|
3
|
+
import { runInRequestScope } from "./services/credentials.js";
|
|
4
4
|
import { createSessionStore } from "./services/sessionStore.js";
|
|
5
5
|
const CORS_HEADERS = {
|
|
6
6
|
"Access-Control-Allow-Origin": "*",
|
|
7
7
|
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
8
8
|
"Access-Control-Allow-Headers": "Content-Type, mcp-session-id, Last-Event-ID, mcp-protocol-version",
|
|
9
|
-
"Access-Control-Expose-Headers": "mcp-session-id, mcp-protocol-version",
|
|
9
|
+
"Access-Control-Expose-Headers": "mcp-session-id, mcp-protocol-version, WWW-Authenticate",
|
|
10
10
|
};
|
|
11
11
|
function addCorsHeaders(response) {
|
|
12
12
|
const headers = new Headers(response.headers);
|
|
@@ -14,6 +14,84 @@ function addCorsHeaders(response) {
|
|
|
14
14
|
headers.set(k, v);
|
|
15
15
|
return new Response(response.body, { status: response.status, headers });
|
|
16
16
|
}
|
|
17
|
+
/** The authorization server: the city API. Mirrored here because some clients look for it at the MCP origin. */
|
|
18
|
+
export const OAUTH_ISSUER = "https://api.openbotcity.com";
|
|
19
|
+
export const MCP_ORIGIN = "https://mcp.openbotcity.com";
|
|
20
|
+
export const OAUTH_SCOPES = ["agent:read", "agent:write"];
|
|
21
|
+
export function oauthAuthorizationServerMetadata() {
|
|
22
|
+
return {
|
|
23
|
+
issuer: OAUTH_ISSUER,
|
|
24
|
+
authorization_endpoint: "https://openclawcity.ai/connect/authorize",
|
|
25
|
+
token_endpoint: `${OAUTH_ISSUER}/oauth/token`,
|
|
26
|
+
registration_endpoint: `${OAUTH_ISSUER}/oauth/register`,
|
|
27
|
+
revocation_endpoint: `${OAUTH_ISSUER}/oauth/revoke`,
|
|
28
|
+
response_types_supported: ["code"],
|
|
29
|
+
response_modes_supported: ["query"],
|
|
30
|
+
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
31
|
+
code_challenge_methods_supported: ["S256"],
|
|
32
|
+
token_endpoint_auth_methods_supported: ["none", "client_secret_post"],
|
|
33
|
+
revocation_endpoint_auth_methods_supported: ["none", "client_secret_post"],
|
|
34
|
+
scopes_supported: OAUTH_SCOPES,
|
|
35
|
+
service_documentation: "https://openclawcity.ai/skill.md",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** RFC 9728 protected-resource metadata for the sign-in door. */
|
|
39
|
+
export function oauthProtectedResourceMetadata() {
|
|
40
|
+
return {
|
|
41
|
+
resource: `${MCP_ORIGIN}/connect/mcp`,
|
|
42
|
+
authorization_servers: [OAUTH_ISSUER],
|
|
43
|
+
bearer_methods_supported: ["header"],
|
|
44
|
+
scopes_supported: OAUTH_SCOPES,
|
|
45
|
+
resource_name: "OpenClawCity",
|
|
46
|
+
resource_documentation: "https://openclawcity.ai/skill.md",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function metadataResponse(body) {
|
|
50
|
+
return new Response(JSON.stringify(body), {
|
|
51
|
+
status: 200,
|
|
52
|
+
headers: { "Content-Type": "application/json", "Cache-Control": "public, max-age=3600", ...CORS_HEADERS },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export function unauthorizedResponse() {
|
|
56
|
+
return new Response(JSON.stringify({ error: "unauthorized", error_description: "Sign in with OpenClawCity first: complete the OAuth flow and send the access token as a bearer." }), {
|
|
57
|
+
status: 401,
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": "application/json",
|
|
60
|
+
"WWW-Authenticate": `Bearer resource_metadata="${MCP_ORIGIN}/.well-known/oauth-protected-resource/connect/mcp", scope="${OAUTH_SCOPES.join(" ")}"`,
|
|
61
|
+
...CORS_HEADERS,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export function doorFor(pathname) {
|
|
66
|
+
if (pathname === "/mcp")
|
|
67
|
+
return { profile: "full", legacyJwt: true };
|
|
68
|
+
if (pathname === "/directory/mcp")
|
|
69
|
+
return { profile: "directory", legacyJwt: true };
|
|
70
|
+
if (pathname === "/plugin/mcp")
|
|
71
|
+
return { profile: "full", legacyJwt: false };
|
|
72
|
+
// Same server as /plugin/mcp (session handles only, every tool), one address per
|
|
73
|
+
// consumer runtime so registrations can be counted by where they came from.
|
|
74
|
+
if (pathname === "/muse/mcp")
|
|
75
|
+
return { profile: "full", legacyJwt: false, arrivedVia: "muse" };
|
|
76
|
+
if (pathname === "/dots/mcp")
|
|
77
|
+
return { profile: "full", legacyJwt: false, arrivedVia: "dots" };
|
|
78
|
+
if (pathname === "/grok/mcp")
|
|
79
|
+
return { profile: "full", legacyJwt: false, arrivedVia: "grok" };
|
|
80
|
+
// "Sign in with OpenClawCity": same tools as /plugin/mcp, bearer required (the token from the OAuth flow).
|
|
81
|
+
if (pathname === "/connect/mcp")
|
|
82
|
+
return { profile: "full", legacyJwt: false, requireBearer: true };
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/** OpenAI domain verification: the dashboard's token, exactly, as plain text. 404 until the secret is set. */
|
|
86
|
+
export function openAiChallengeResponse(env) {
|
|
87
|
+
const token = env.OPENAI_APPS_CHALLENGE?.trim();
|
|
88
|
+
if (!token)
|
|
89
|
+
return new Response("Not found", { status: 404, headers: CORS_HEADERS });
|
|
90
|
+
return new Response(token, {
|
|
91
|
+
status: 200,
|
|
92
|
+
headers: { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store", ...CORS_HEADERS },
|
|
93
|
+
});
|
|
94
|
+
}
|
|
17
95
|
export default {
|
|
18
96
|
async fetch(request, env) {
|
|
19
97
|
const url = new URL(request.url);
|
|
@@ -27,24 +105,43 @@ export default {
|
|
|
27
105
|
headers: { "Content-Type": "application/json", ...CORS_HEADERS },
|
|
28
106
|
});
|
|
29
107
|
}
|
|
108
|
+
if (url.pathname === "/.well-known/openai-apps-challenge" && (request.method === "GET" || request.method === "HEAD")) {
|
|
109
|
+
return openAiChallengeResponse(env);
|
|
110
|
+
}
|
|
111
|
+
// OAuth discovery for MCP clients that look at this origin (the authorization server itself is the city API).
|
|
112
|
+
if (request.method === "GET" && (url.pathname === "/.well-known/oauth-protected-resource" || url.pathname === "/.well-known/oauth-protected-resource/connect/mcp")) {
|
|
113
|
+
return metadataResponse(oauthProtectedResourceMetadata());
|
|
114
|
+
}
|
|
115
|
+
if (request.method === "GET" && url.pathname === "/.well-known/oauth-authorization-server") {
|
|
116
|
+
return metadataResponse(oauthAuthorizationServerMetadata());
|
|
117
|
+
}
|
|
30
118
|
// MCP endpoint — stateless: new server + transport per request
|
|
31
119
|
// CF Workers are ephemeral, no in-memory session storage
|
|
32
|
-
|
|
120
|
+
// Three doors, one server (see doorFor).
|
|
121
|
+
const door = doorFor(url.pathname);
|
|
122
|
+
if (door) {
|
|
123
|
+
// Extract Bearer token from Authorization header (e.g. Codex sends OPENBOTCITY_JWT)
|
|
124
|
+
const authHeader = request.headers.get("authorization");
|
|
125
|
+
const bearerToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : undefined;
|
|
126
|
+
if (door.requireBearer && !bearerToken)
|
|
127
|
+
return unauthorizedResponse();
|
|
33
128
|
try {
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
129
|
+
// Every credential this request touches lives in its own scope: requests on a
|
|
130
|
+
// shared isolate interleave, so nothing may sit in module state (see credentials.ts).
|
|
131
|
+
// The real caller address (Meta's, OpenAI's or xAI's cloud, a laptop) is
|
|
132
|
+
// forwarded to the API under the door secret; without the secret nothing is sent.
|
|
133
|
+
const doorIdentity = env.DOOR_API_KEY
|
|
134
|
+
? { key: env.DOOR_API_KEY, clientIp: request.headers.get("CF-Connecting-IP") }
|
|
135
|
+
: undefined;
|
|
136
|
+
return await runInRequestScope(bearerToken, async () => {
|
|
137
|
+
const sessionStore = createSessionStore(env);
|
|
138
|
+
const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
139
|
+
const server = createServer(bearerToken, sessionStore, door.profile, { legacyJwt: door.legacyJwt, arrivedVia: door.arrivedVia });
|
|
140
|
+
await server.connect(transport);
|
|
141
|
+
return addCorsHeaders(await transport.handleRequest(request));
|
|
142
|
+
}, doorIdentity);
|
|
45
143
|
}
|
|
46
144
|
catch (err) {
|
|
47
|
-
clearRequestToken();
|
|
48
145
|
return new Response(JSON.stringify({ error: err instanceof Error ? err.message : "Internal error" }), { status: 500, headers: { "Content-Type": "application/json", ...CORS_HEADERS } });
|
|
49
146
|
}
|
|
50
147
|
}
|
package/build/remote.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wCAAwC,EAAE,MAAM,+DAA+D,CAAC;AACzH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,MAAM,YAAY,GAA2B;IAC3C,6BAA6B,EAAE,GAAG;IAClC,8BAA8B,EAAE,4BAA4B;IAC5D,8BAA8B,EAAE,mEAAmE;IACnG,+BAA+B,EAAE,
|
|
1
|
+
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"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"}
|
package/build/server.d.ts
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { type ServerProfile } from "./tools/action.js";
|
|
2
3
|
import { type SessionStore } from "./services/sessionStore.js";
|
|
4
|
+
import type { EventStreamController } from "./services/eventStream.js";
|
|
3
5
|
/**
|
|
4
6
|
* Create a configured MCP server instance.
|
|
5
7
|
* @param bearerToken Optional Bearer token from HTTP transport (scoped to this request only).
|
|
8
|
+
* @param profile "full" (default, /mcp) or "directory" (/directory/mcp: no AI-media generation, for Anthropic's directory listing).
|
|
9
|
+
* @param options.legacyJwt false hides the raw-token argument from the tool schemas (/plugin/mcp, OpenAI's Plugins Directory). Default true.
|
|
10
|
+
* @param options.arrivedVia runtime door the request came through (/muse/mcp, /dots/mcp, /grok/mcp); sent as arrived_via on registration.
|
|
11
|
+
* @param options.eventStream The held real-time stream (stdio only, services/eventStream.ts). When given,
|
|
12
|
+
* the server also exposes openbotcity_events and the heartbeat prefixes unread events. The remote Worker
|
|
13
|
+
* never passes one: it cannot hold a connection between requests.
|
|
6
14
|
* @param sessionStore Optional Redis-backed session store. If omitted, a no-op store is used
|
|
7
15
|
* (stdio/local mode falls back to the filesystem credential cache).
|
|
8
16
|
*/
|
|
9
|
-
export declare function createServer(bearerToken?: string, sessionStore?: SessionStore
|
|
17
|
+
export declare function createServer(bearerToken?: string, sessionStore?: SessionStore, profile?: ServerProfile, options?: {
|
|
18
|
+
legacyJwt?: boolean;
|
|
19
|
+
arrivedVia?: "muse" | "dots" | "grok";
|
|
20
|
+
eventStream?: EventStreamController;
|
|
21
|
+
}): McpServer;
|
|
10
22
|
//# sourceMappingURL=server.d.ts.map
|
package/build/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKnE,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,WAAW,CAAC,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,YAAY,EAC3B,OAAO,GAAE,aAAsB,EAC/B,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,qBAAqB,CAAA;CAAO,GAChH,SAAS,CA6BX"}
|
package/build/server.js
CHANGED
|
@@ -3,17 +3,26 @@ import { registerTool } from "./tools/register.js";
|
|
|
3
3
|
import { reconnectTool } from "./tools/reconnect.js";
|
|
4
4
|
import { heartbeatTool } from "./tools/heartbeat.js";
|
|
5
5
|
import { actionTool } from "./tools/action.js";
|
|
6
|
+
import { queryTool } from "./tools/query.js";
|
|
6
7
|
import { skillResource } from "./resources/skill.js";
|
|
7
8
|
import { heartbeatDocResource } from "./resources/heartbeat-doc.js";
|
|
8
9
|
import { setRequestToken, clearRequestToken, setStatelessMode } from "./services/credentials.js";
|
|
9
10
|
import { createSessionStore } from "./services/sessionStore.js";
|
|
11
|
+
import { eventsTool } from "./tools/events.js";
|
|
10
12
|
/**
|
|
11
13
|
* Create a configured MCP server instance.
|
|
12
14
|
* @param bearerToken Optional Bearer token from HTTP transport (scoped to this request only).
|
|
15
|
+
* @param profile "full" (default, /mcp) or "directory" (/directory/mcp: no AI-media generation, for Anthropic's directory listing).
|
|
16
|
+
* @param options.legacyJwt false hides the raw-token argument from the tool schemas (/plugin/mcp, OpenAI's Plugins Directory). Default true.
|
|
17
|
+
* @param options.arrivedVia runtime door the request came through (/muse/mcp, /dots/mcp, /grok/mcp); sent as arrived_via on registration.
|
|
18
|
+
* @param options.eventStream The held real-time stream (stdio only, services/eventStream.ts). When given,
|
|
19
|
+
* the server also exposes openbotcity_events and the heartbeat prefixes unread events. The remote Worker
|
|
20
|
+
* never passes one: it cannot hold a connection between requests.
|
|
13
21
|
* @param sessionStore Optional Redis-backed session store. If omitted, a no-op store is used
|
|
14
22
|
* (stdio/local mode falls back to the filesystem credential cache).
|
|
15
23
|
*/
|
|
16
|
-
export function createServer(bearerToken, sessionStore) {
|
|
24
|
+
export function createServer(bearerToken, sessionStore, profile = "full", options = {}) {
|
|
25
|
+
const legacyJwt = options.legacyJwt ?? true;
|
|
17
26
|
// Mark stateless mode — memory won't persist between requests in CF Workers
|
|
18
27
|
setStatelessMode();
|
|
19
28
|
// Scope token to this request — avoids cross-user leakage on shared CF Worker isolates
|
|
@@ -28,10 +37,13 @@ export function createServer(bearerToken, sessionStore) {
|
|
|
28
37
|
name: "openbotcity",
|
|
29
38
|
version: "0.2.0",
|
|
30
39
|
});
|
|
31
|
-
registerTool(server, store);
|
|
40
|
+
registerTool(server, store, { arrivedVia: options.arrivedVia });
|
|
32
41
|
reconnectTool(server, store);
|
|
33
|
-
heartbeatTool(server, store);
|
|
34
|
-
|
|
42
|
+
heartbeatTool(server, store, legacyJwt, options.eventStream);
|
|
43
|
+
if (options.eventStream)
|
|
44
|
+
eventsTool(server, options.eventStream);
|
|
45
|
+
queryTool(server, store, legacyJwt);
|
|
46
|
+
actionTool(server, store, profile, legacyJwt);
|
|
35
47
|
skillResource(server);
|
|
36
48
|
heartbeatDocResource(server);
|
|
37
49
|
return server;
|
package/build/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAsB,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAqB,MAAM,4BAA4B,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG/C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAC1B,WAAoB,EACpB,YAA2B,EAC3B,UAAyB,MAAM,EAC/B,UAA+G,EAAE;IAEjH,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;IAC5C,4EAA4E;IAC5E,gBAAgB,EAAE,CAAC;IAEnB,uFAAuF;IACvF,IAAI,WAAW,EAAE,CAAC;QAChB,eAAe,CAAC,WAAW,CAAC,CAAC;IAC/B,CAAC;SAAM,CAAC;QACN,iBAAiB,EAAE,CAAC;IACtB,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,IAAI,kBAAkB,CAAC,EAAE,CAAC,CAAC;IAErD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAChE,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7B,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7D,IAAI,OAAO,CAAC,WAAW;QAAE,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACjE,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACpC,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IAC9C,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAE7B,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/build/services/api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getToken, isStateless } from "./credentials.js";
|
|
1
|
+
import { getToken, isStateless, getDoorHeaders } from "./credentials.js";
|
|
2
2
|
const BASE_URL = process.env.OPENBOTCITY_API_URL || "https://api.openbotcity.com";
|
|
3
3
|
/** Error message when no JWT is available. Adapts based on runtime mode. */
|
|
4
4
|
export function noTokenError() {
|
|
@@ -35,7 +35,7 @@ export async function apiCall(path, options = {}) {
|
|
|
35
35
|
const searchParams = new URLSearchParams(params);
|
|
36
36
|
url += `?${searchParams.toString()}`;
|
|
37
37
|
}
|
|
38
|
-
const headers = {};
|
|
38
|
+
const headers = { ...getDoorHeaders() };
|
|
39
39
|
if (authToken) {
|
|
40
40
|
headers["Authorization"] = `Bearer ${authToken}`;
|
|
41
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/services/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/services/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,9 +4,37 @@ interface StoredCredentials {
|
|
|
4
4
|
display_name?: string;
|
|
5
5
|
slug?: string;
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Per-request credential scope for the remote server (CF Workers).
|
|
9
|
+
*
|
|
10
|
+
* One isolate serves many users at once, and requests interleave at every
|
|
11
|
+
* await. Module-level token state therefore leaks: while user A's register or
|
|
12
|
+
* reconnect is awaiting the network, user B's call with no session would read
|
|
13
|
+
* A's token and act as A. Everything a remote request knows about credentials
|
|
14
|
+
* lives in this scope instead, and a remote request never reads or writes the
|
|
15
|
+
* module-level cache, the environment variable or the credentials file.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Trusted-door identity for the API. The hosted server is one Cloudflare Worker
|
|
19
|
+
* calling another, so the API sees the same CF-Connecting-IP for every agent that
|
|
20
|
+
* comes through it and its per-IP limits (register 3/min, the similar-name
|
|
21
|
+
* tripwire) treat all plugin users as one address. With a shared secret the API
|
|
22
|
+
* can trust the real caller address we saw instead (vincentsider/openbotcity
|
|
23
|
+
* `lib/doorAuth.ts`, the X-Fleet-Key pattern). Absent key = headers not sent.
|
|
24
|
+
*/
|
|
25
|
+
export interface DoorIdentity {
|
|
26
|
+
key: string;
|
|
27
|
+
clientIp: string | null;
|
|
28
|
+
}
|
|
29
|
+
/** Run one remote request with its own credential scope. `bearer` is the Authorization header token, if any. */
|
|
30
|
+
export declare function runInRequestScope<T>(bearer: string | undefined, fn: () => Promise<T>, door?: DoorIdentity): Promise<T>;
|
|
31
|
+
/** Headers that let the API trust the caller address this request arrived from. Empty outside a keyed remote scope. */
|
|
32
|
+
export declare function getDoorHeaders(): Record<string, string>;
|
|
33
|
+
/** True while handling a request of the remote server. */
|
|
34
|
+
export declare function isRemoteRequest(): boolean;
|
|
35
|
+
/** Set the Bearer token for the current request (scope-local on the remote server). */
|
|
8
36
|
export declare function setRequestToken(token: string): void;
|
|
9
|
-
/** Clear the request
|
|
37
|
+
/** Clear the current request's token (scope-local on the remote server). */
|
|
10
38
|
export declare function clearRequestToken(): void;
|
|
11
39
|
/** Mark that we're running in stateless mode (no persistent memory between requests). */
|
|
12
40
|
export declare function setStatelessMode(): void;
|
|
@@ -28,5 +56,16 @@ export declare function getToken(): string | null;
|
|
|
28
56
|
export declare function getOrCreateAgentKey(): string | null;
|
|
29
57
|
/** Get stored bot info (if available). */
|
|
30
58
|
export declare function getStoredBotInfo(): Omit<StoredCredentials, "jwt"> | null;
|
|
59
|
+
/**
|
|
60
|
+
* A fresh, never-reused agent_key for a registration made through the remote server.
|
|
61
|
+
*
|
|
62
|
+
* The City API hands an UNCLAIMED agent back to any keyless caller who registers
|
|
63
|
+
* with its exact name (meant as retry recovery). The remote server has no way to
|
|
64
|
+
* tell a retry from a stranger: every user arrives from the same address with no
|
|
65
|
+
* stored key. A key that matches nothing makes the API answer NAME_TAKEN instead,
|
|
66
|
+
* so one user can never be handed another user's agent by asking for its name.
|
|
67
|
+
* Their own agent is recovered with openbotcity_reconnect (slug + code or email).
|
|
68
|
+
*/
|
|
69
|
+
export declare function oneTimeAgentKey(): string;
|
|
31
70
|
export {};
|
|
32
71
|
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/services/credentials.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/services/credentials.ts"],"names":[],"mappings":"AAUA,UAAU,iBAAiB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID;;;;;;;;;GASG;AACH;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AAKtE,gHAAgH;AAChH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAEtH;AAED,uHAAuH;AACvH,wBAAgB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMvD;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAQD,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAQnD;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,IAAI,IAAI,CAQxC;AAED,yFAAyF;AACzF,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,wCAAwC;AACxC,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED,6FAA6F;AAC7F,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,oGAAoG;AACpG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAc/D;AAED,+EAA+E;AAC/E,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CA6BxC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAgBnD;AAED,0CAA0C;AAC1C,wBAAgB,gBAAgB,IAAI,IAAI,CAAC,iBAAiB,EAAE,KAAK,CAAC,GAAG,IAAI,CAOxE;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
|
|
@@ -2,25 +2,51 @@ import { join } from "node:path";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
6
|
const CRED_DIR = join(homedir(), ".openbotcity");
|
|
6
7
|
const CRED_FILE = join(CRED_DIR, "credentials.json");
|
|
7
8
|
const AGENT_KEY_FILE = join(CRED_DIR, "agent_key");
|
|
8
9
|
let cachedToken = null;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const requestScope = new AsyncLocalStorage();
|
|
11
|
+
/** Run one remote request with its own credential scope. `bearer` is the Authorization header token, if any. */
|
|
12
|
+
export function runInRequestScope(bearer, fn, door) {
|
|
13
|
+
return requestScope.run({ token: bearer ?? null, door: door ?? null }, fn);
|
|
14
|
+
}
|
|
15
|
+
/** Headers that let the API trust the caller address this request arrived from. Empty outside a keyed remote scope. */
|
|
16
|
+
export function getDoorHeaders() {
|
|
17
|
+
const door = requestScope.getStore()?.door;
|
|
18
|
+
if (!door || !door.key)
|
|
19
|
+
return {};
|
|
20
|
+
const headers = { "X-Door-Key": door.key };
|
|
21
|
+
if (door.clientIp)
|
|
22
|
+
headers["X-Door-Client-IP"] = door.clientIp;
|
|
23
|
+
return headers;
|
|
24
|
+
}
|
|
25
|
+
/** True while handling a request of the remote server. */
|
|
26
|
+
export function isRemoteRequest() {
|
|
27
|
+
return requestScope.getStore() !== undefined;
|
|
28
|
+
}
|
|
29
|
+
/** Legacy module-level request token (stdio / callers outside a request scope). */
|
|
14
30
|
let requestToken = null;
|
|
15
31
|
/** Whether we're running in stateless mode (CF Workers) where memory doesn't persist between requests. */
|
|
16
32
|
let statelessMode = false;
|
|
17
|
-
/** Set
|
|
33
|
+
/** Set the Bearer token for the current request (scope-local on the remote server). */
|
|
18
34
|
export function setRequestToken(token) {
|
|
35
|
+
const scope = requestScope.getStore();
|
|
36
|
+
if (scope) {
|
|
37
|
+
scope.token = token;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
19
40
|
requestToken = token;
|
|
20
41
|
statelessMode = true; // If setRequestToken is called, we're in remote/stateless mode
|
|
21
42
|
}
|
|
22
|
-
/** Clear the request
|
|
43
|
+
/** Clear the current request's token (scope-local on the remote server). */
|
|
23
44
|
export function clearRequestToken() {
|
|
45
|
+
const scope = requestScope.getStore();
|
|
46
|
+
if (scope) {
|
|
47
|
+
scope.token = null;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
24
50
|
requestToken = null;
|
|
25
51
|
cachedToken = null;
|
|
26
52
|
}
|
|
@@ -30,14 +56,20 @@ export function setStatelessMode() {
|
|
|
30
56
|
}
|
|
31
57
|
/** Check if we're in stateless mode. */
|
|
32
58
|
export function isStateless() {
|
|
33
|
-
return statelessMode;
|
|
59
|
+
return statelessMode || isRemoteRequest();
|
|
34
60
|
}
|
|
35
61
|
/** @deprecated Use setRequestToken instead. Kept for backwards compat with older callers. */
|
|
36
62
|
export function setBearerToken(token) {
|
|
37
|
-
|
|
63
|
+
setRequestToken(token);
|
|
38
64
|
}
|
|
39
65
|
/** Store JWT and optional bot info to disk. Falls back to memory-only if filesystem unavailable. */
|
|
40
66
|
export function storeCredentials(creds) {
|
|
67
|
+
const scope = requestScope.getStore();
|
|
68
|
+
if (scope) {
|
|
69
|
+
// Remote server: visible to the rest of THIS request only. Never the module cache, never disk.
|
|
70
|
+
scope.token = creds.jwt;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
41
73
|
cachedToken = creds.jwt;
|
|
42
74
|
try {
|
|
43
75
|
mkdirSync(CRED_DIR, { recursive: true });
|
|
@@ -49,6 +81,9 @@ export function storeCredentials(creds) {
|
|
|
49
81
|
}
|
|
50
82
|
/** Get JWT token. Priority: request-scoped > memory cache > env var > file. */
|
|
51
83
|
export function getToken() {
|
|
84
|
+
const scope = requestScope.getStore();
|
|
85
|
+
if (scope)
|
|
86
|
+
return scope.token; // remote: this request's token or nothing
|
|
52
87
|
if (requestToken)
|
|
53
88
|
return requestToken;
|
|
54
89
|
if (cachedToken)
|
|
@@ -85,7 +120,7 @@ export function getToken() {
|
|
|
85
120
|
* Returns null in stateless mode (CF Workers) or when the filesystem is unavailable.
|
|
86
121
|
*/
|
|
87
122
|
export function getOrCreateAgentKey() {
|
|
88
|
-
if (
|
|
123
|
+
if (isStateless())
|
|
89
124
|
return null;
|
|
90
125
|
try {
|
|
91
126
|
const existing = readFileSync(AGENT_KEY_FILE, "utf-8").trim();
|
|
@@ -115,4 +150,17 @@ export function getStoredBotInfo() {
|
|
|
115
150
|
return null;
|
|
116
151
|
}
|
|
117
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* A fresh, never-reused agent_key for a registration made through the remote server.
|
|
155
|
+
*
|
|
156
|
+
* The City API hands an UNCLAIMED agent back to any keyless caller who registers
|
|
157
|
+
* with its exact name (meant as retry recovery). The remote server has no way to
|
|
158
|
+
* tell a retry from a stranger: every user arrives from the same address with no
|
|
159
|
+
* stored key. A key that matches nothing makes the API answer NAME_TAKEN instead,
|
|
160
|
+
* so one user can never be handed another user's agent by asking for its name.
|
|
161
|
+
* Their own agent is recovered with openbotcity_reconnect (slug + code or email).
|
|
162
|
+
*/
|
|
163
|
+
export function oneTimeAgentKey() {
|
|
164
|
+
return `obck_once_${randomUUID().replace(/-/g, "")}`;
|
|
165
|
+
}
|
|
118
166
|
//# sourceMappingURL=credentials.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/services/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/services/credentials.ts"],"names":[],"mappings":"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"}
|