@x12i/api-hub-runtime 0.0.0-stage → 0.1.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/README.md CHANGED
@@ -1,3 +1,11 @@
1
- # Temporary Holding Version
1
+ # @x12i/api-hub-runtime
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Shared REST and MCP call path for the x12i API hub
4
+
5
+ ESM package for Node.js 24 or newer. Import the public name:
6
+
7
+ ```js
8
+ import * as runtime from '@x12i/api-hub-runtime';
9
+ ```
10
+
11
+ The whole set is also available from [`@x12i/api-hub`](https://www.npmjs.com/package/@x12i/api-hub).
package/dist/env.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /** Server variables that are not tied to one provider or hub. Rendered into docs/ENV.md and .env.example. */
2
+ export interface EnvVar {
3
+ name: string;
4
+ example: string;
5
+ description: string;
6
+ /** Recommended in production even though the server starts without it. */
7
+ recommended?: boolean;
8
+ }
9
+ export declare const PLATFORM_ENV: EnvVar[];
package/dist/env.js ADDED
@@ -0,0 +1,24 @@
1
+ export const PLATFORM_ENV = [
2
+ { name: 'PORT', example: '8080', description: 'REST gateway port (default 8080).' },
3
+ { name: 'HOST', example: '0.0.0.0', description: 'REST gateway bind address (default 0.0.0.0).' },
4
+ { name: 'MCP_PORT', example: '8081', description: 'Streamable HTTP MCP server port (default 8081).' },
5
+ {
6
+ name: 'PUBLIC_URL',
7
+ example: 'https://api.example.com',
8
+ description: 'Public base URL of the gateway. Used for absolute signed file links and the OpenAPI server entry; links are relative when unset.',
9
+ recommended: true,
10
+ },
11
+ {
12
+ name: 'FILES_SIGNING_SECRET',
13
+ example: '',
14
+ description: 'Random secret (32 or more characters) that signs file download links. Without it links stop working after a restart.',
15
+ recommended: true,
16
+ },
17
+ {
18
+ name: 'USAGE_LOG_PATH',
19
+ example: '.data/usage.jsonl',
20
+ description: 'JSONL file for the proxied-call usage log (capability, hub, latency, outcome, upstream cost, opaque caller id). Wave 6 replacement reports read it. Default .data/usage.jsonl.',
21
+ },
22
+ { name: 'ENV_FILE', example: '', description: 'Load a different env file instead of .env (path relative to the repo root). Set it in the shell, not in the file.' },
23
+ ];
24
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../../../../packages/runtime/src/env.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,YAAY,GAAa;IACpC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,mCAAmC,EAAE;IACnF,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,8CAA8C,EAAE;IACjG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,iDAAiD,EAAE;IACrG;QACE,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,yBAAyB;QAClC,WAAW,EAAE,kIAAkI;QAC/I,WAAW,EAAE,IAAI;KAClB;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,sHAAsH;QACnI,WAAW,EAAE,IAAI;KAClB;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,mBAAmB;QAC5B,WAAW,EAAE,gLAAgL;KAC9L;IACD,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,mHAAmH,EAAE;CACpK,CAAC","sourcesContent":["/** Server variables that are not tied to one provider or hub. Rendered into docs/ENV.md and .env.example. */\nexport interface EnvVar {\n name: string;\n example: string;\n description: string;\n /** Recommended in production even though the server starts without it. */\n recommended?: boolean;\n}\n\nexport const PLATFORM_ENV: EnvVar[] = [\n { name: 'PORT', example: '8080', description: 'REST gateway port (default 8080).' },\n { name: 'HOST', example: '0.0.0.0', description: 'REST gateway bind address (default 0.0.0.0).' },\n { name: 'MCP_PORT', example: '8081', description: 'Streamable HTTP MCP server port (default 8081).' },\n {\n name: 'PUBLIC_URL',\n example: 'https://api.example.com',\n description: 'Public base URL of the gateway. Used for absolute signed file links and the OpenAPI server entry; links are relative when unset.',\n recommended: true,\n },\n {\n name: 'FILES_SIGNING_SECRET',\n example: '',\n description: 'Random secret (32 or more characters) that signs file download links. Without it links stop working after a restart.',\n recommended: true,\n },\n {\n name: 'USAGE_LOG_PATH',\n example: '.data/usage.jsonl',\n description: 'JSONL file for the proxied-call usage log (capability, hub, latency, outcome, upstream cost, opaque caller id). Wave 6 replacement reports read it. Default .data/usage.jsonl.',\n },\n { name: 'ENV_FILE', example: '', description: 'Load a different env file instead of .env (path relative to the repo root). Set it in the shell, not in the file.' },\n];\n"]}
@@ -0,0 +1,102 @@
1
+ import { CapabilityRegistry, HubError, type AnyCapability, type AuthContext, type CapabilityFilter, type CatalogItem, type ConnectionEntry, type ConnectionInfo, type ConnectionProvider, type Ctx, type JobRecord, type JobRunner, type Logger, type MissingItem, type ServiceId } from '@x12i/api-hub-core';
2
+ export type { JobRunner } from '@x12i/api-hub-core';
3
+ export { createServicesFromEnv, type Services } from './services.js';
4
+ export { PLATFORM_ENV, type EnvVar } from './env.js';
5
+ export type ContextServices = Omit<Ctx, 'auth' | 'signal' | 'log'>;
6
+ export interface RuntimeOptions {
7
+ registry: CapabilityRegistry;
8
+ services?: Partial<ContextServices>;
9
+ jobs?: JobRunner;
10
+ log?: Logger;
11
+ /** Environment used to work out which services are connected. Defaults to process.env. */
12
+ env?: Record<string, string | undefined>;
13
+ /** Connection state for capabilities served by proxy routes (packages/upstream). */
14
+ connections?: ConnectionProvider | ((registry: CapabilityRegistry) => ConnectionProvider);
15
+ }
16
+ export interface ListFilter extends CapabilityFilter {
17
+ /** Only capabilities that are served and fully connected (true) or served but missing something (false). */
18
+ connected?: boolean;
19
+ }
20
+ export type ConnectedItem = CatalogItem & ConnectionInfo;
21
+ export type CallOutcome = {
22
+ kind: 'result';
23
+ output: unknown;
24
+ } | {
25
+ kind: 'job';
26
+ job: JobRecord;
27
+ } | {
28
+ kind: 'error';
29
+ error: HubError;
30
+ };
31
+ export interface CallOptions {
32
+ auth: AuthContext;
33
+ signal?: AbortSignal;
34
+ }
35
+ /** The one call path shared by REST and MCP. */
36
+ export declare class Runtime {
37
+ readonly registry: CapabilityRegistry;
38
+ readonly log: Logger;
39
+ private readonly services;
40
+ private readonly jobs;
41
+ private readonly env;
42
+ private readonly connectionProvider;
43
+ constructor(options: RuntimeOptions);
44
+ makeCtx(auth: AuthContext, signal: AbortSignal, impl?: AnyCapability): Ctx;
45
+ /** An injected client decides (a stub means not connected); without one, the env set decides. */
46
+ serviceConnected(id: ServiceId): boolean;
47
+ connection(slug: string): ConnectionInfo;
48
+ list(filter?: ListFilter): {
49
+ total: number;
50
+ items: ConnectedItem[];
51
+ };
52
+ /** Every service and hub the operator can connect, with the env variable names and what each unlocks. */
53
+ connections(): ConnectionEntry[];
54
+ describe(slug: string): {
55
+ limits: import("@x12i/api-hub-core").CapabilityLimits | null;
56
+ inputSchema: import("@x12i/api-hub-core").JsonSchema | null;
57
+ outputSchema: import("@x12i/api-hub-core").JsonSchema | null;
58
+ examples: unknown[];
59
+ listings: {
60
+ hub: string;
61
+ api: string;
62
+ hubCategory: string | null;
63
+ sourcing: "NATIVE" | "AI" | "PROXY";
64
+ effort: "XS" | "S" | "M" | "L" | "XL" | null;
65
+ price: string | null;
66
+ url: string | null;
67
+ }[];
68
+ connected: boolean | null;
69
+ missing: MissingItem[];
70
+ summary: string | null;
71
+ mode: "sync" | "job" | null;
72
+ slug: string;
73
+ exampleApi: string | null;
74
+ hubs: string[];
75
+ hubCount: number;
76
+ listingCount: number;
77
+ sourcing: import("@x12i/api-hub-core").Sourcing;
78
+ labelsDisagree: boolean;
79
+ effort: import("@x12i/api-hub-core").Effort | null;
80
+ effortDays: number | null;
81
+ inApimesa: boolean;
82
+ x12iToday: string | null;
83
+ x12iPackages: string[];
84
+ hostable: string | null;
85
+ plan: import("@x12i/api-hub-core").Plan;
86
+ hubCategories: string[];
87
+ status: import("@x12i/api-hub-core").Status;
88
+ statusReason: string | null;
89
+ wave: import("@x12i/api-hub-core").Wave | null;
90
+ } | undefined;
91
+ call(slug: string, input: unknown, options: CallOptions): Promise<CallOutcome>;
92
+ job(id: string, auth: AuthContext): Promise<JobRecord | undefined>;
93
+ }
94
+ /**
95
+ * Loads `<repo>/.env` (or ENV_FILE) into process.env with Node's built-in loader. Variables
96
+ * already set in the environment win. Returns the file used, or undefined when there is none.
97
+ */
98
+ export declare function loadDotEnv(root?: string): string | undefined;
99
+ export interface CreateRuntimeOptions extends Omit<RuntimeOptions, 'registry'> {
100
+ root?: string;
101
+ }
102
+ export declare function createRuntime(options?: CreateRuntimeOptions): Promise<Runtime>;
package/dist/index.js ADDED
@@ -0,0 +1,169 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { CapabilityRegistry, createLogger, createStubContext, findRepoRoot, HubError, invokeCapability, SERVICES, serviceCredentials, serviceState, stubLlm, stubModels, stubSearch, toJsonSchema, } from '@x12i/api-hub-core';
4
+ export { createServicesFromEnv } from "./services.js";
5
+ export { PLATFORM_ENV } from "./env.js";
6
+ const NOT_PERMITTED = new Set(['blocked-terms', 'excluded']);
7
+ /** The one call path shared by REST and MCP. */
8
+ export class Runtime {
9
+ registry;
10
+ log;
11
+ services;
12
+ jobs;
13
+ env;
14
+ connectionProvider;
15
+ constructor(options) {
16
+ this.registry = options.registry;
17
+ this.services = options.services ?? {};
18
+ this.jobs = options.jobs;
19
+ this.log = options.log ?? createLogger();
20
+ this.env = options.env ?? process.env;
21
+ this.connectionProvider = typeof options.connections === 'function' ? options.connections(options.registry) : options.connections;
22
+ }
23
+ makeCtx(auth, signal, impl) {
24
+ const declared = new Set((impl?.requires ?? []).map((r) => r.service));
25
+ const credentials = (service) => {
26
+ if (!declared.has(service))
27
+ throw new HubError('internal', 'Capability asked for a service it does not declare', { service });
28
+ return serviceCredentials(service, this.env);
29
+ };
30
+ return createStubContext({ credentials, ...this.services, auth, signal, log: this.log });
31
+ }
32
+ /** An injected client decides (a stub means not connected); without one, the env set decides. */
33
+ serviceConnected(id) {
34
+ const clients = {
35
+ llm: [this.services.llm, stubLlm],
36
+ models: [this.services.models, stubModels],
37
+ search: [this.services.search, stubSearch],
38
+ };
39
+ const injected = clients[id];
40
+ if (injected?.[0])
41
+ return injected[0] !== injected[1];
42
+ return serviceState(id, this.env).connected;
43
+ }
44
+ connection(slug) {
45
+ const impl = this.registry.served(slug);
46
+ if (!impl)
47
+ return { connected: null, missing: [] };
48
+ const fromProvider = this.connectionProvider?.capability(slug);
49
+ if (fromProvider)
50
+ return fromProvider;
51
+ const missing = [];
52
+ for (const { service } of impl.requires ?? []) {
53
+ if (this.serviceConnected(service))
54
+ continue;
55
+ missing.push({ kind: 'service', service, env: serviceState(service, this.env).missingEnv });
56
+ }
57
+ return { connected: missing.length === 0, missing };
58
+ }
59
+ list(filter = {}) {
60
+ const { connected, ...rest } = filter;
61
+ if (connected === undefined) {
62
+ const { total, items } = this.registry.list(rest);
63
+ return { total, items: items.map((item) => ({ ...item, ...this.connection(item.slug) })) };
64
+ }
65
+ const all = this.registry
66
+ .list({ ...rest, limit: undefined, offset: undefined })
67
+ .items.map((item) => ({ ...item, ...this.connection(item.slug) }))
68
+ .filter((item) => item.connected === connected);
69
+ const offset = rest.offset ?? 0;
70
+ return { total: all.length, items: rest.limit === undefined ? all.slice(offset) : all.slice(offset, offset + rest.limit) };
71
+ }
72
+ /** Every service and hub the operator can connect, with the env variable names and what each unlocks. */
73
+ connections() {
74
+ const served = this.registry.servedCapabilities();
75
+ const services = Object.keys(SERVICES).map((id) => {
76
+ const service = SERVICES[id];
77
+ const state = serviceState(id, this.env);
78
+ return {
79
+ kind: 'service',
80
+ id,
81
+ title: service.title,
82
+ connected: this.serviceConnected(id),
83
+ env: service.envSets.map((set) => set.join(' + ')),
84
+ missingEnv: this.serviceConnected(id) ? [] : state.missingEnv,
85
+ unlocks: served.filter((cap) => cap.requires?.some((r) => r.service === id)).length,
86
+ docs: `docs/ENV.md#${id}`,
87
+ };
88
+ });
89
+ return [...services, ...(this.connectionProvider?.entries() ?? [])];
90
+ }
91
+ describe(slug) {
92
+ const item = this.registry.item(slug);
93
+ const entry = this.registry.entry(slug);
94
+ if (!item || !entry)
95
+ return undefined;
96
+ const impl = this.registry.served(slug);
97
+ return {
98
+ ...item,
99
+ ...this.connection(slug),
100
+ limits: impl?.limits ?? null,
101
+ inputSchema: impl ? toJsonSchema(impl.input, 'input') : null,
102
+ outputSchema: impl ? toJsonSchema(impl.output, 'output') : null,
103
+ examples: impl?.examples ?? [],
104
+ listings: entry.listings.map((l) => ({
105
+ hub: l.hub,
106
+ api: l.api,
107
+ hubCategory: l.hubCategory,
108
+ sourcing: l.sourcing,
109
+ effort: l.effort,
110
+ price: l.price,
111
+ url: l.url,
112
+ })),
113
+ };
114
+ }
115
+ async call(slug, input, options) {
116
+ const entry = this.registry.entry(slug);
117
+ if (!entry) {
118
+ return { kind: 'error', error: new HubError('unsupported', `Unknown capability: ${slug}`, { slug }) };
119
+ }
120
+ const impl = this.registry.served(slug);
121
+ if (!impl) {
122
+ const code = NOT_PERMITTED.has(entry.status) ? 'not_permitted' : 'unsupported';
123
+ return {
124
+ kind: 'error',
125
+ error: new HubError(code, `Capability ${slug} is listed but not served (status: ${entry.status})`, {
126
+ status: entry.status,
127
+ reason: entry.statusReason,
128
+ }),
129
+ };
130
+ }
131
+ if (impl.mode === 'job') {
132
+ if (!this.jobs)
133
+ return { kind: 'error', error: new HubError('unsupported', 'Async jobs are not configured') };
134
+ const parsed = impl.input.safeParse(input ?? {});
135
+ if (!parsed.success) {
136
+ return {
137
+ kind: 'error',
138
+ error: new HubError('invalid_input', 'Input does not match the schema', {
139
+ issues: parsed.error.issues.map((i) => ({ path: i.path.map(String).join('.'), message: i.message })),
140
+ }),
141
+ };
142
+ }
143
+ const job = await this.jobs.submit(impl, input, options.auth, (signal) => this.makeCtx(options.auth, signal, impl));
144
+ return { kind: 'job', job };
145
+ }
146
+ const signal = options.signal ?? new AbortController().signal;
147
+ const result = await invokeCapability(impl, input, this.makeCtx(options.auth, signal, impl));
148
+ return result.ok ? { kind: 'result', output: result.output } : { kind: 'error', error: result.error };
149
+ }
150
+ async job(id, auth) {
151
+ return this.jobs?.get(id, auth);
152
+ }
153
+ }
154
+ /**
155
+ * Loads `<repo>/.env` (or ENV_FILE) into process.env with Node's built-in loader. Variables
156
+ * already set in the environment win. Returns the file used, or undefined when there is none.
157
+ */
158
+ export function loadDotEnv(root = findRepoRoot()) {
159
+ const file = process.env.ENV_FILE ? resolve(root, process.env.ENV_FILE) : join(root, '.env');
160
+ if (!existsSync(file))
161
+ return undefined;
162
+ process.loadEnvFile(file);
163
+ return file;
164
+ }
165
+ export async function createRuntime(options = {}) {
166
+ const registry = await CapabilityRegistry.load(options.root);
167
+ return new Runtime({ ...options, registry });
168
+ }
169
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/runtime/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport {\n CapabilityRegistry,\n createLogger,\n createStubContext,\n findRepoRoot,\n HubError,\n invokeCapability,\n SERVICES,\n serviceCredentials,\n serviceState,\n stubLlm,\n stubModels,\n stubSearch,\n toJsonSchema,\n type AnyCapability,\n type AuthContext,\n type CapabilityFilter,\n type CatalogItem,\n type ConnectionEntry,\n type ConnectionInfo,\n type ConnectionProvider,\n type Ctx,\n type JobRecord,\n type JobRunner,\n type Logger,\n type MissingItem,\n type ServiceId,\n} from '@x12i/api-hub-core';\n\nexport type { JobRunner } from '@x12i/api-hub-core';\nexport { createServicesFromEnv, type Services } from './services.ts';\nexport { PLATFORM_ENV, type EnvVar } from './env.ts';\n\nexport type ContextServices = Omit<Ctx, 'auth' | 'signal' | 'log'>;\n\nexport interface RuntimeOptions {\n registry: CapabilityRegistry;\n services?: Partial<ContextServices>;\n jobs?: JobRunner;\n log?: Logger;\n /** Environment used to work out which services are connected. Defaults to process.env. */\n env?: Record<string, string | undefined>;\n /** Connection state for capabilities served by proxy routes (packages/upstream). */\n connections?: ConnectionProvider | ((registry: CapabilityRegistry) => ConnectionProvider);\n}\n\nexport interface ListFilter extends CapabilityFilter {\n /** Only capabilities that are served and fully connected (true) or served but missing something (false). */\n connected?: boolean;\n}\n\nexport type ConnectedItem = CatalogItem & ConnectionInfo;\n\nexport type CallOutcome =\n | { kind: 'result'; output: unknown }\n | { kind: 'job'; job: JobRecord }\n | { kind: 'error'; error: HubError };\n\nexport interface CallOptions {\n auth: AuthContext;\n signal?: AbortSignal;\n}\n\nconst NOT_PERMITTED = new Set(['blocked-terms', 'excluded']);\n\n/** The one call path shared by REST and MCP. */\nexport class Runtime {\n readonly registry: CapabilityRegistry;\n readonly log: Logger;\n private readonly services: Partial<ContextServices>;\n private readonly jobs: JobRunner | undefined;\n private readonly env: Record<string, string | undefined>;\n private readonly connectionProvider: ConnectionProvider | undefined;\n\n constructor(options: RuntimeOptions) {\n this.registry = options.registry;\n this.services = options.services ?? {};\n this.jobs = options.jobs;\n this.log = options.log ?? createLogger();\n this.env = options.env ?? process.env;\n this.connectionProvider = typeof options.connections === 'function' ? options.connections(options.registry) : options.connections;\n }\n\n makeCtx(auth: AuthContext, signal: AbortSignal, impl?: AnyCapability): Ctx {\n const declared = new Set((impl?.requires ?? []).map((r) => r.service));\n const credentials = (service: ServiceId) => {\n if (!declared.has(service)) throw new HubError('internal', 'Capability asked for a service it does not declare', { service });\n return serviceCredentials(service, this.env);\n };\n return createStubContext({ credentials, ...this.services, auth, signal, log: this.log });\n }\n\n /** An injected client decides (a stub means not connected); without one, the env set decides. */\n serviceConnected(id: ServiceId): boolean {\n const clients: Partial<Record<ServiceId, [unknown, unknown]>> = {\n llm: [this.services.llm, stubLlm],\n models: [this.services.models, stubModels],\n search: [this.services.search, stubSearch],\n };\n const injected = clients[id];\n if (injected?.[0]) return injected[0] !== injected[1];\n return serviceState(id, this.env).connected;\n }\n\n connection(slug: string): ConnectionInfo {\n const impl = this.registry.served(slug);\n if (!impl) return { connected: null, missing: [] };\n const fromProvider = this.connectionProvider?.capability(slug);\n if (fromProvider) return fromProvider;\n const missing: MissingItem[] = [];\n for (const { service } of impl.requires ?? []) {\n if (this.serviceConnected(service)) continue;\n missing.push({ kind: 'service', service, env: serviceState(service, this.env).missingEnv });\n }\n return { connected: missing.length === 0, missing };\n }\n\n list(filter: ListFilter = {}): { total: number; items: ConnectedItem[] } {\n const { connected, ...rest } = filter;\n if (connected === undefined) {\n const { total, items } = this.registry.list(rest);\n return { total, items: items.map((item) => ({ ...item, ...this.connection(item.slug) })) };\n }\n const all = this.registry\n .list({ ...rest, limit: undefined, offset: undefined })\n .items.map((item) => ({ ...item, ...this.connection(item.slug) }))\n .filter((item) => item.connected === connected);\n const offset = rest.offset ?? 0;\n return { total: all.length, items: rest.limit === undefined ? all.slice(offset) : all.slice(offset, offset + rest.limit) };\n }\n\n /** Every service and hub the operator can connect, with the env variable names and what each unlocks. */\n connections(): ConnectionEntry[] {\n const served = this.registry.servedCapabilities();\n const services = (Object.keys(SERVICES) as ServiceId[]).map((id): ConnectionEntry => {\n const service = SERVICES[id];\n const state = serviceState(id, this.env);\n return {\n kind: 'service',\n id,\n title: service.title,\n connected: this.serviceConnected(id),\n env: (service.envSets as readonly (readonly string[])[]).map((set) => set.join(' + ')),\n missingEnv: this.serviceConnected(id) ? [] : state.missingEnv,\n unlocks: served.filter((cap) => cap.requires?.some((r) => r.service === id)).length,\n docs: `docs/ENV.md#${id}`,\n };\n });\n return [...services, ...(this.connectionProvider?.entries() ?? [])];\n }\n\n describe(slug: string) {\n const item = this.registry.item(slug);\n const entry = this.registry.entry(slug);\n if (!item || !entry) return undefined;\n const impl = this.registry.served(slug);\n return {\n ...item,\n ...this.connection(slug),\n limits: impl?.limits ?? null,\n inputSchema: impl ? toJsonSchema(impl.input, 'input') : null,\n outputSchema: impl ? toJsonSchema(impl.output, 'output') : null,\n examples: impl?.examples ?? [],\n listings: entry.listings.map((l) => ({\n hub: l.hub,\n api: l.api,\n hubCategory: l.hubCategory,\n sourcing: l.sourcing,\n effort: l.effort,\n price: l.price,\n url: l.url,\n })),\n };\n }\n\n async call(slug: string, input: unknown, options: CallOptions): Promise<CallOutcome> {\n const entry = this.registry.entry(slug);\n if (!entry) {\n return { kind: 'error', error: new HubError('unsupported', `Unknown capability: ${slug}`, { slug }) };\n }\n const impl = this.registry.served(slug);\n if (!impl) {\n const code = NOT_PERMITTED.has(entry.status) ? 'not_permitted' : 'unsupported';\n return {\n kind: 'error',\n error: new HubError(code, `Capability ${slug} is listed but not served (status: ${entry.status})`, {\n status: entry.status,\n reason: entry.statusReason,\n }),\n };\n }\n\n if (impl.mode === 'job') {\n if (!this.jobs) return { kind: 'error', error: new HubError('unsupported', 'Async jobs are not configured') };\n const parsed = impl.input.safeParse(input ?? {});\n if (!parsed.success) {\n return {\n kind: 'error',\n error: new HubError('invalid_input', 'Input does not match the schema', {\n issues: parsed.error.issues.map((i) => ({ path: i.path.map(String).join('.'), message: i.message })),\n }),\n };\n }\n const job = await this.jobs.submit(impl, input, options.auth, (signal) => this.makeCtx(options.auth, signal, impl));\n return { kind: 'job', job };\n }\n\n const signal = options.signal ?? new AbortController().signal;\n const result = await invokeCapability(impl, input, this.makeCtx(options.auth, signal, impl));\n return result.ok ? { kind: 'result', output: result.output } : { kind: 'error', error: result.error };\n }\n\n async job(id: string, auth: AuthContext): Promise<JobRecord | undefined> {\n return this.jobs?.get(id, auth);\n }\n}\n\n/**\n * Loads `<repo>/.env` (or ENV_FILE) into process.env with Node's built-in loader. Variables\n * already set in the environment win. Returns the file used, or undefined when there is none.\n */\nexport function loadDotEnv(root: string = findRepoRoot()): string | undefined {\n const file = process.env.ENV_FILE ? resolve(root, process.env.ENV_FILE) : join(root, '.env');\n if (!existsSync(file)) return undefined;\n process.loadEnvFile(file);\n return file;\n}\n\nexport interface CreateRuntimeOptions extends Omit<RuntimeOptions, 'registry'> {\n root?: string;\n}\n\nexport async function createRuntime(options: CreateRuntimeOptions = {}): Promise<Runtime> {\n const registry = await CapabilityRegistry.load(options.root);\n return new Runtime({ ...options, registry });\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import { type CapabilityRegistry, type ConnectionProvider, type Logger } from '@x12i/api-hub-core';
2
+ import { type FilesService } from '@x12i/api-hub-files';
3
+ import { type ManagedJobRunner } from '@x12i/api-hub-jobs';
4
+ import type { ContextServices } from './index.js';
5
+ export interface Services {
6
+ context: Partial<ContextServices>;
7
+ files: FilesService;
8
+ jobs: ManagedJobRunner;
9
+ log: Logger;
10
+ /** Pass to `createRuntime({ connections })` so the catalog reports hub connection state. */
11
+ connections: (registry: CapabilityRegistry) => ConnectionProvider;
12
+ }
13
+ /** Wires the wave-1 foundations from environment variables. Unconfigured providers stay stubbed (`service_not_connected`). */
14
+ export declare function createServicesFromEnv(env?: NodeJS.ProcessEnv, log?: Logger, root?: string): Services;
@@ -0,0 +1,50 @@
1
+ import { createCache } from '@x12i/api-hub-cache';
2
+ import { createLogger, findRepoRoot } from '@x12i/api-hub-core';
3
+ import { createFiles, hmacSigner } from '@x12i/api-hub-files';
4
+ import { createJobRunner } from '@x12i/api-hub-jobs';
5
+ import { createDnsHelper } from '@x12i/api-hub-dns';
6
+ import { createLlmFromEnv } from '@x12i/api-hub-llm';
7
+ import { createModelsFromEnv } from '@x12i/api-hub-models';
8
+ import { createSafeFetch } from '@x12i/api-hub-safe-fetch';
9
+ import { createSearchFromEnv } from '@x12i/api-hub-search';
10
+ import { createUpstreamFromEnv, upstreamConnections } from '@x12i/api-hub-upstream';
11
+ /** Wires the wave-1 foundations from environment variables. Unconfigured providers stay stubbed (`service_not_connected`). */
12
+ export function createServicesFromEnv(env = process.env, log = createLogger(), root = findRepoRoot()) {
13
+ if (!env.FILES_SIGNING_SECRET) {
14
+ log.warn('FILES_SIGNING_SECRET is not set; signed file URLs will not survive a restart');
15
+ }
16
+ const files = createFiles({
17
+ publicBaseUrl: env.PUBLIC_URL,
18
+ signer: env.FILES_SIGNING_SECRET ? hmacSigner(env.FILES_SIGNING_SECRET) : undefined,
19
+ });
20
+ const fetch = createSafeFetch();
21
+ const upstream = createUpstreamFromEnv({ root, fetch, env });
22
+ return {
23
+ context: {
24
+ fetch,
25
+ dns: createDnsHelper(),
26
+ llm: createLlmFromEnv(env, log),
27
+ models: createModelsFromEnv(env),
28
+ search: createSearchFromEnv(env, log),
29
+ cache: createCache(),
30
+ files,
31
+ upstream,
32
+ },
33
+ files,
34
+ jobs: createJobRunner({ log }),
35
+ log,
36
+ connections: (registry) => upstreamConnections({
37
+ upstream,
38
+ capabilities: () => registry.servedCapabilities(),
39
+ listedPerHub: listedPerHub(registry),
40
+ }),
41
+ };
42
+ }
43
+ function listedPerHub(registry) {
44
+ const counts = new Map();
45
+ for (const cap of registry.registry.capabilities)
46
+ for (const hub of cap.hubs)
47
+ counts.set(hub, (counts.get(hub) ?? 0) + 1);
48
+ return counts;
49
+ }
50
+ //# sourceMappingURL=services.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.js","sourceRoot":"","sources":["../../../../../packages/runtime/src/services.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAiE,MAAM,WAAW,CAAC;AACtH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAqB,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,eAAe,EAAyB,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAY3E,8HAA8H;AAC9H,MAAM,UAAU,qBAAqB,CAAC,MAAyB,OAAO,CAAC,GAAG,EAAE,MAAc,YAAY,EAAE,EAAE,OAAe,YAAY,EAAE;IACrI,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC;QAC9B,GAAG,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,KAAK,GAAG,WAAW,CAAC;QACxB,aAAa,EAAE,GAAG,CAAC,UAAU;QAC7B,MAAM,EAAE,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,SAAS;KACpF,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAG,qBAAqB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7D,OAAO;QACL,OAAO,EAAE;YACP,KAAK;YACL,GAAG,EAAE,eAAe,EAAE;YACtB,GAAG,EAAE,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC;YAC/B,MAAM,EAAE,mBAAmB,CAAC,GAAG,CAAC;YAChC,MAAM,EAAE,mBAAmB,CAAC,GAAG,EAAE,GAAG,CAAC;YACrC,KAAK,EAAE,WAAW,EAAE;YACpB,KAAK;YACL,QAAQ;SACT;QACD,KAAK;QACL,IAAI,EAAE,eAAe,CAAC,EAAE,GAAG,EAAE,CAAC;QAC9B,GAAG;QACH,WAAW,EAAE,CAAC,QAAQ,EAAE,EAAE,CACxB,mBAAmB,CAAC;YAClB,QAAQ;YACR,YAAY,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,kBAAkB,EAAE;YACjD,YAAY,EAAE,YAAY,CAAC,QAAQ,CAAC;SACrC,CAAC;KACL,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,QAA4B;IAChD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY;QAAE,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1H,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { createCache } from '@x12i/api-hub-cache';\nimport { createLogger, findRepoRoot, type CapabilityRegistry, type ConnectionProvider, type Logger } from '@x12i/api-hub-core';\nimport { createFiles, hmacSigner, type FilesService } from '@x12i/api-hub-files';\nimport { createJobRunner, type ManagedJobRunner } from '@x12i/api-hub-jobs';\nimport { createDnsHelper } from '@x12i/api-hub-dns';\nimport { createLlmFromEnv } from '@x12i/api-hub-llm';\nimport { createModelsFromEnv } from '@x12i/api-hub-models';\nimport { createSafeFetch } from '@x12i/api-hub-safe-fetch';\nimport { createSearchFromEnv } from '@x12i/api-hub-search';\nimport { createUpstreamFromEnv, upstreamConnections } from '@x12i/api-hub-upstream';\nimport type { ContextServices } from './index.ts';\n\nexport interface Services {\n context: Partial<ContextServices>;\n files: FilesService;\n jobs: ManagedJobRunner;\n log: Logger;\n /** Pass to `createRuntime({ connections })` so the catalog reports hub connection state. */\n connections: (registry: CapabilityRegistry) => ConnectionProvider;\n}\n\n/** Wires the wave-1 foundations from environment variables. Unconfigured providers stay stubbed (`service_not_connected`). */\nexport function createServicesFromEnv(env: NodeJS.ProcessEnv = process.env, log: Logger = createLogger(), root: string = findRepoRoot()): Services {\n if (!env.FILES_SIGNING_SECRET) {\n log.warn('FILES_SIGNING_SECRET is not set; signed file URLs will not survive a restart');\n }\n const files = createFiles({\n publicBaseUrl: env.PUBLIC_URL,\n signer: env.FILES_SIGNING_SECRET ? hmacSigner(env.FILES_SIGNING_SECRET) : undefined,\n });\n const fetch = createSafeFetch();\n const upstream = createUpstreamFromEnv({ root, fetch, env });\n return {\n context: {\n fetch,\n dns: createDnsHelper(),\n llm: createLlmFromEnv(env, log),\n models: createModelsFromEnv(env),\n search: createSearchFromEnv(env, log),\n cache: createCache(),\n files,\n upstream,\n },\n files,\n jobs: createJobRunner({ log }),\n log,\n connections: (registry) =>\n upstreamConnections({\n upstream,\n capabilities: () => registry.servedCapabilities(),\n listedPerHub: listedPerHub(registry),\n }),\n };\n}\n\nfunction listedPerHub(registry: CapabilityRegistry): Map<string, number> {\n const counts = new Map<string, number>();\n for (const cap of registry.registry.capabilities) for (const hub of cap.hubs) counts.set(hub, (counts.get(hub) ?? 0) + 1);\n return counts;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "@x12i/api-hub-runtime",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Shared REST and MCP call path for the x12i API hub",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=24"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/x12i/apis-hub.git",
24
+ "directory": "packages/runtime"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/x12i/apis-hub/issues"
28
+ },
29
+ "homepage": "https://github.com/x12i/apis-hub",
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "dependencies": {
34
+ "@x12i/api-hub-cache": "0.1.0",
35
+ "@x12i/api-hub-core": "0.1.0",
36
+ "@x12i/api-hub-dns": "0.1.0",
37
+ "@x12i/api-hub-files": "0.1.0",
38
+ "@x12i/api-hub-jobs": "0.1.0",
39
+ "@x12i/api-hub-llm": "0.1.0",
40
+ "@x12i/api-hub-models": "0.1.0",
41
+ "@x12i/api-hub-safe-fetch": "0.1.0",
42
+ "@x12i/api-hub-search": "0.1.0",
43
+ "@x12i/api-hub-upstream": "0.1.0"
44
+ }
45
+ }