@x12i/api-hub-runtime 0.1.0 → 0.1.1

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/dist/index.d.ts CHANGED
@@ -47,11 +47,16 @@ export declare class Runtime {
47
47
  connection(slug: string): ConnectionInfo;
48
48
  list(filter?: ListFilter): {
49
49
  total: number;
50
+ unique: number;
50
51
  items: ConnectedItem[];
51
52
  };
52
53
  /** Every service and hub the operator can connect, with the env variable names and what each unlocks. */
53
54
  connections(): ConnectionEntry[];
55
+ /** A planned alias with no implementation of its own is answered by the canonical capability. */
56
+ private aliasTarget;
54
57
  describe(slug: string): {
58
+ servedVia: string | null;
59
+ upstreams: import("@x12i/api-hub-core").CatalogUpstream[];
55
60
  limits: import("@x12i/api-hub-core").CapabilityLimits | null;
56
61
  inputSchema: import("@x12i/api-hub-core").JsonSchema | null;
57
62
  outputSchema: import("@x12i/api-hub-core").JsonSchema | null;
@@ -69,6 +74,7 @@ export declare class Runtime {
69
74
  missing: MissingItem[];
70
75
  summary: string | null;
71
76
  mode: "sync" | "job" | null;
77
+ failover: boolean;
72
78
  slug: string;
73
79
  exampleApi: string | null;
74
80
  hubs: string[];
@@ -87,6 +93,7 @@ export declare class Runtime {
87
93
  status: import("@x12i/api-hub-core").Status;
88
94
  statusReason: string | null;
89
95
  wave: import("@x12i/api-hub-core").Wave | null;
96
+ aliasOf: string | null;
90
97
  } | undefined;
91
98
  call(slug: string, input: unknown, options: CallOptions): Promise<CallOutcome>;
92
99
  job(id: string, auth: AuthContext): Promise<JobRecord | undefined>;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync } from 'node:fs';
2
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';
3
+ import { CapabilityRegistry, createLogger, createStubContext, findRepoRoot, HubError, invokeCapability, SERVICES, serviceCredentials, serviceState, stubLlm, stubModels, stubSearch, toJsonSchema, upstreamsOf, } from '@x12i/api-hub-core';
4
4
  export { createServicesFromEnv } from "./services.js";
5
5
  export { PLATFORM_ENV } from "./env.js";
6
6
  const NOT_PERMITTED = new Set(['blocked-terms', 'excluded']);
@@ -59,15 +59,19 @@ export class Runtime {
59
59
  list(filter = {}) {
60
60
  const { connected, ...rest } = filter;
61
61
  if (connected === undefined) {
62
- const { total, items } = this.registry.list(rest);
63
- return { total, items: items.map((item) => ({ ...item, ...this.connection(item.slug) })) };
62
+ const { total, unique, items } = this.registry.list(rest);
63
+ return { total, unique, items: items.map((item) => ({ ...item, ...this.connection(item.slug) })) };
64
64
  }
65
65
  const all = this.registry
66
66
  .list({ ...rest, limit: undefined, offset: undefined })
67
67
  .items.map((item) => ({ ...item, ...this.connection(item.slug) }))
68
68
  .filter((item) => item.connected === connected);
69
69
  const offset = rest.offset ?? 0;
70
- return { total: all.length, items: rest.limit === undefined ? all.slice(offset) : all.slice(offset, offset + rest.limit) };
70
+ return {
71
+ total: all.length,
72
+ unique: all.filter((item) => !item.aliasOf).length,
73
+ items: rest.limit === undefined ? all.slice(offset) : all.slice(offset, offset + rest.limit),
74
+ };
71
75
  }
72
76
  /** Every service and hub the operator can connect, with the env variable names and what each unlocks. */
73
77
  connections() {
@@ -88,19 +92,29 @@ export class Runtime {
88
92
  });
89
93
  return [...services, ...(this.connectionProvider?.entries() ?? [])];
90
94
  }
95
+ /** A planned alias with no implementation of its own is answered by the canonical capability. */
96
+ aliasTarget(slug) {
97
+ const entry = this.registry.entry(slug);
98
+ if (!entry?.aliasOf || entry.status !== 'planned' || this.registry.served(slug))
99
+ return null;
100
+ return this.registry.served(entry.aliasOf) ? entry.aliasOf : null;
101
+ }
91
102
  describe(slug) {
92
103
  const item = this.registry.item(slug);
93
104
  const entry = this.registry.entry(slug);
94
105
  if (!item || !entry)
95
106
  return undefined;
96
- const impl = this.registry.served(slug);
107
+ const via = this.aliasTarget(slug);
108
+ const shown = this.registry.served(slug) ?? (via ? this.registry.served(via) : undefined);
97
109
  return {
98
110
  ...item,
99
111
  ...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 ?? [],
112
+ servedVia: via,
113
+ upstreams: upstreamsOf(shown),
114
+ limits: shown?.limits ?? null,
115
+ inputSchema: shown ? toJsonSchema(shown.input, 'input') : null,
116
+ outputSchema: shown ? toJsonSchema(shown.output, 'output') : null,
117
+ examples: shown?.examples ?? [],
104
118
  listings: entry.listings.map((l) => ({
105
119
  hub: l.hub,
106
120
  api: l.api,
@@ -117,7 +131,8 @@ export class Runtime {
117
131
  if (!entry) {
118
132
  return { kind: 'error', error: new HubError('unsupported', `Unknown capability: ${slug}`, { slug }) };
119
133
  }
120
- const impl = this.registry.served(slug);
134
+ const via = this.aliasTarget(slug);
135
+ const impl = this.registry.served(slug) ?? (via ? this.registry.served(via) : undefined);
121
136
  if (!impl) {
122
137
  const code = NOT_PERMITTED.has(entry.status) ? 'not_permitted' : 'unsupported';
123
138
  return {
@@ -125,6 +140,7 @@ export class Runtime {
125
140
  error: new HubError(code, `Capability ${slug} is listed but not served (status: ${entry.status})`, {
126
141
  status: entry.status,
127
142
  reason: entry.statusReason,
143
+ ...(entry.aliasOf ? { aliasOf: entry.aliasOf } : {}),
128
144
  }),
129
145
  };
130
146
  }
package/dist/index.js.map CHANGED
@@ -1 +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"]}
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 upstreamsOf,\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; unique: number; items: ConnectedItem[] } {\n const { connected, ...rest } = filter;\n if (connected === undefined) {\n const { total, unique, items } = this.registry.list(rest);\n return { total, unique, 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 {\n total: all.length,\n unique: all.filter((item) => !item.aliasOf).length,\n items: rest.limit === undefined ? all.slice(offset) : all.slice(offset, offset + rest.limit),\n };\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 /** A planned alias with no implementation of its own is answered by the canonical capability. */\n private aliasTarget(slug: string): string | null {\n const entry = this.registry.entry(slug);\n if (!entry?.aliasOf || entry.status !== 'planned' || this.registry.served(slug)) return null;\n return this.registry.served(entry.aliasOf) ? entry.aliasOf : null;\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 via = this.aliasTarget(slug);\n const shown = this.registry.served(slug) ?? (via ? this.registry.served(via) : undefined);\n return {\n ...item,\n ...this.connection(slug),\n servedVia: via,\n upstreams: upstreamsOf(shown),\n limits: shown?.limits ?? null,\n inputSchema: shown ? toJsonSchema(shown.input, 'input') : null,\n outputSchema: shown ? toJsonSchema(shown.output, 'output') : null,\n examples: shown?.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 via = this.aliasTarget(slug);\n const impl = this.registry.served(slug) ?? (via ? this.registry.served(via) : undefined);\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 ...(entry.aliasOf ? { aliasOf: entry.aliasOf } : {}),\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x12i/api-hub-runtime",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Shared REST and MCP call path for the x12i API hub",
5
5
  "type": "module",
6
6
  "engines": {
@@ -31,15 +31,15 @@
31
31
  "access": "public"
32
32
  },
33
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"
34
+ "@x12i/api-hub-cache": "0.1.1",
35
+ "@x12i/api-hub-core": "0.1.1",
36
+ "@x12i/api-hub-dns": "0.1.1",
37
+ "@x12i/api-hub-files": "0.1.1",
38
+ "@x12i/api-hub-jobs": "0.1.1",
39
+ "@x12i/api-hub-llm": "0.1.1",
40
+ "@x12i/api-hub-models": "0.1.1",
41
+ "@x12i/api-hub-safe-fetch": "0.1.1",
42
+ "@x12i/api-hub-search": "0.1.1",
43
+ "@x12i/api-hub-upstream": "0.1.1"
44
44
  }
45
45
  }