@mcoda/codali 0.1.108 → 0.1.109
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/agents/AgentInventory.d.ts +56 -0
- package/dist/agents/AgentInventory.d.ts.map +1 -0
- package/dist/agents/AgentInventory.js +131 -0
- package/dist/agents/RoleResolution.d.ts +49 -0
- package/dist/agents/RoleResolution.d.ts.map +1 -0
- package/dist/agents/RoleResolution.js +166 -0
- package/dist/api/ChatCompletionsAdapter.d.ts +76 -0
- package/dist/api/ChatCompletionsAdapter.d.ts.map +1 -0
- package/dist/api/ChatCompletionsAdapter.js +54 -0
- package/dist/api/CodaliApi.d.ts +78 -0
- package/dist/api/CodaliApi.d.ts.map +1 -0
- package/dist/api/CodaliApi.js +273 -0
- package/dist/api/CodaliServer.d.ts +42 -0
- package/dist/api/CodaliServer.d.ts.map +1 -0
- package/dist/api/CodaliServer.js +118 -0
- package/dist/cli/AskCommand.d.ts +66 -0
- package/dist/cli/AskCommand.d.ts.map +1 -0
- package/dist/cli/AskCommand.js +536 -0
- package/dist/cli/AuthCommand.d.ts +19 -0
- package/dist/cli/AuthCommand.d.ts.map +1 -0
- package/dist/cli/AuthCommand.js +161 -0
- package/dist/cli/ServeCommand.d.ts +5 -0
- package/dist/cli/ServeCommand.d.ts.map +1 -0
- package/dist/cli/ServeCommand.js +87 -0
- package/dist/cli/ToolsCommand.d.ts +22 -0
- package/dist/cli/ToolsCommand.d.ts.map +1 -0
- package/dist/cli/ToolsCommand.js +208 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +29 -1
- package/dist/connectors/http/HttpToolDefinition.d.ts +74 -0
- package/dist/connectors/http/HttpToolDefinition.d.ts.map +1 -0
- package/dist/connectors/http/HttpToolDefinition.js +190 -0
- package/dist/connectors/http/HttpToolSource.d.ts +18 -0
- package/dist/connectors/http/HttpToolSource.d.ts.map +1 -0
- package/dist/connectors/http/HttpToolSource.js +29 -0
- package/dist/connectors/mcp/McpClient.d.ts +132 -0
- package/dist/connectors/mcp/McpClient.d.ts.map +1 -0
- package/dist/connectors/mcp/McpClient.js +215 -0
- package/dist/connectors/mcp/McpServerRegistry.d.ts +75 -0
- package/dist/connectors/mcp/McpServerRegistry.d.ts.map +1 -0
- package/dist/connectors/mcp/McpServerRegistry.js +180 -0
- package/dist/connectors/mcp/McpToolAdapter.d.ts +48 -0
- package/dist/connectors/mcp/McpToolAdapter.d.ts.map +1 -0
- package/dist/connectors/mcp/McpToolAdapter.js +101 -0
- package/dist/connectors/mcp/McpToolSource.d.ts +27 -0
- package/dist/connectors/mcp/McpToolSource.d.ts.map +1 -0
- package/dist/connectors/mcp/McpToolSource.js +33 -0
- package/dist/connectors/media/ImageGenerationTool.d.ts +43 -0
- package/dist/connectors/media/ImageGenerationTool.d.ts.map +1 -0
- package/dist/connectors/media/ImageGenerationTool.js +122 -0
- package/dist/connectors/oauth/DeviceCodeAuth.d.ts +74 -0
- package/dist/connectors/oauth/DeviceCodeAuth.d.ts.map +1 -0
- package/dist/connectors/oauth/DeviceCodeAuth.js +153 -0
- package/dist/docdex/DocdexClient.d.ts +2 -0
- package/dist/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/docdex/DocdexClient.js +4 -0
- package/dist/gateway/CodaliGateway.d.ts +51 -3
- package/dist/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/gateway/CodaliGateway.js +226 -2
- package/dist/gateway/CodaliGatewayTypes.d.ts +34 -0
- package/dist/gateway/CodaliGatewayTypes.d.ts.map +1 -1
- package/dist/gateway/EvidenceNormalizer.d.ts.map +1 -1
- package/dist/gateway/EvidenceNormalizer.js +90 -1
- package/dist/gateway/Finalizer.d.ts +50 -0
- package/dist/gateway/Finalizer.d.ts.map +1 -0
- package/dist/gateway/Finalizer.js +79 -0
- package/dist/gateway/GatewayPlanner.d.ts +13 -0
- package/dist/gateway/GatewayPlanner.d.ts.map +1 -1
- package/dist/gateway/GatewayPlanner.js +81 -16
- package/dist/gateway/GatewayStateMachine.d.ts.map +1 -1
- package/dist/gateway/GatewayStateMachine.js +6 -0
- package/dist/gateway/GatewayTracer.d.ts +93 -0
- package/dist/gateway/GatewayTracer.d.ts.map +1 -0
- package/dist/gateway/GatewayTracer.js +214 -0
- package/dist/gateway/LocalGatewayProvider.d.ts +46 -0
- package/dist/gateway/LocalGatewayProvider.d.ts.map +1 -0
- package/dist/gateway/LocalGatewayProvider.js +161 -0
- package/dist/gateway/LocalGatewayTaskRunner.d.ts +79 -0
- package/dist/gateway/LocalGatewayTaskRunner.d.ts.map +1 -0
- package/dist/gateway/LocalGatewayTaskRunner.js +354 -0
- package/dist/gateway/RelevanceJudge.d.ts +14 -0
- package/dist/gateway/RelevanceJudge.d.ts.map +1 -0
- package/dist/gateway/RelevanceJudge.js +87 -0
- package/dist/gateway/ResponseSchema.d.ts +32 -0
- package/dist/gateway/ResponseSchema.d.ts.map +1 -0
- package/dist/gateway/ResponseSchema.js +120 -0
- package/dist/gateway/TemporalContext.d.ts +36 -0
- package/dist/gateway/TemporalContext.d.ts.map +1 -0
- package/dist/gateway/TemporalContext.js +115 -0
- package/dist/gateway/ToolCapabilityCompiler.d.ts +5 -0
- package/dist/gateway/ToolCapabilityCompiler.d.ts.map +1 -1
- package/dist/gateway/ToolCapabilityCompiler.js +27 -2
- package/dist/gateway/ToolDescriptorSource.d.ts +6 -0
- package/dist/gateway/ToolDescriptorSource.d.ts.map +1 -0
- package/dist/gateway/ToolDescriptorSource.js +30 -0
- package/dist/gateway/ToolExposure.d.ts +36 -0
- package/dist/gateway/ToolExposure.d.ts.map +1 -0
- package/dist/gateway/ToolExposure.js +154 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -0
- package/dist/providers/ClaudeCliProvider.d.ts +2 -0
- package/dist/providers/ClaudeCliProvider.d.ts.map +1 -1
- package/dist/providers/ClaudeCliProvider.js +2 -0
- package/dist/providers/CodexCliProvider.d.ts +2 -0
- package/dist/providers/CodexCliProvider.d.ts.map +1 -1
- package/dist/providers/CodexCliProvider.js +2 -0
- package/dist/providers/MswarmWorkerProvider.d.ts +2 -0
- package/dist/providers/MswarmWorkerProvider.d.ts.map +1 -1
- package/dist/providers/MswarmWorkerProvider.js +2 -0
- package/dist/providers/OllamaRemoteProvider.d.ts +2 -0
- package/dist/providers/OllamaRemoteProvider.d.ts.map +1 -1
- package/dist/providers/OllamaRemoteProvider.js +2 -0
- package/dist/providers/OpenAiCompatibleProvider.d.ts +2 -0
- package/dist/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
- package/dist/providers/OpenAiCompatibleProvider.js +2 -0
- package/dist/providers/ProviderTypes.d.ts +13 -0
- package/dist/providers/ProviderTypes.d.ts.map +1 -1
- package/dist/providers/ProviderTypes.js +1 -1
- package/dist/runcontext/CredentialFile.d.ts +12 -0
- package/dist/runcontext/CredentialFile.d.ts.map +1 -0
- package/dist/runcontext/CredentialFile.js +81 -0
- package/dist/runcontext/RunContextResolver.d.ts +126 -0
- package/dist/runcontext/RunContextResolver.d.ts.map +1 -0
- package/dist/runcontext/RunContextResolver.js +389 -0
- package/dist/storage/CodaliDatasetPrivacyEngine.d.ts +1 -1
- package/dist/storage/CodaliDatasetPrivacyEngine.d.ts.map +1 -1
- package/dist/storage/CodaliDatasetPrivacyEngine.js +12 -0
- package/dist/tools/ToolRegistry.d.ts +14 -1
- package/dist/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/tools/ToolRegistry.js +38 -1
- package/dist/tools/ToolTypes.d.ts +35 -0
- package/dist/tools/ToolTypes.d.ts.map +1 -1
- package/dist/tools/ToolTypes.js +21 -0
- package/dist/tools/TruncateResult.d.ts +29 -0
- package/dist/tools/TruncateResult.d.ts.map +1 -0
- package/dist/tools/TruncateResult.js +91 -0
- package/dist/tools/diff/DiffTool.d.ts.map +1 -1
- package/dist/tools/diff/DiffTool.js +2 -0
- package/dist/tools/docdex/DocdexTools.d.ts +21 -1
- package/dist/tools/docdex/DocdexTools.d.ts.map +1 -1
- package/dist/tools/docdex/DocdexTools.js +108 -16
- package/dist/tools/filesystem/FileTools.d.ts.map +1 -1
- package/dist/tools/filesystem/FileTools.js +8 -0
- package/dist/tools/search/SearchTool.d.ts.map +1 -1
- package/dist/tools/search/SearchTool.js +3 -1
- package/dist/tools/shell/ShellTool.d.ts.map +1 -1
- package/dist/tools/shell/ShellTool.js +2 -0
- package/package.json +4 -3
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { ToolExecutionError } from "../../tools/ToolTypes.js";
|
|
2
|
+
import { McpClient, McpClientError, } from "./McpClient.js";
|
|
3
|
+
import { mcpToolsToDefinitions } from "./McpToolAdapter.js";
|
|
4
|
+
/**
|
|
5
|
+
* Owns the lifetime of every configured MCP server.
|
|
6
|
+
*
|
|
7
|
+
* Three properties matter more than features here:
|
|
8
|
+
*
|
|
9
|
+
* - **Isolation.** One broken server degrades its own tools to unavailable
|
|
10
|
+
* and never fails the run or affects another server.
|
|
11
|
+
* - **Boundedness.** Every call has a timeout, at most one retry, and
|
|
12
|
+
* competes for a global concurrency budget, so a slow server cannot
|
|
13
|
+
* monopolise a run.
|
|
14
|
+
* - **Cleanup.** stdio servers are child processes. They must be shut down
|
|
15
|
+
* even when a run fails, or the CLI leaves orphans behind.
|
|
16
|
+
*/
|
|
17
|
+
/** Concurrent in-flight MCP calls across all servers. */
|
|
18
|
+
export const DEFAULT_MAX_CONCURRENT_CALLS = 4;
|
|
19
|
+
/** One retry, and only for transport-level failures. */
|
|
20
|
+
const MAX_CALL_ATTEMPTS = 2;
|
|
21
|
+
/** A minimal semaphore. Keeps concurrent MCP work inside a known bound. */
|
|
22
|
+
class Semaphore {
|
|
23
|
+
constructor(limit) {
|
|
24
|
+
this.limit = limit;
|
|
25
|
+
this.active = 0;
|
|
26
|
+
this.waiting = [];
|
|
27
|
+
}
|
|
28
|
+
async acquire() {
|
|
29
|
+
if (this.active < this.limit) {
|
|
30
|
+
this.active += 1;
|
|
31
|
+
return () => this.release();
|
|
32
|
+
}
|
|
33
|
+
await new Promise((resolve) => this.waiting.push(resolve));
|
|
34
|
+
this.active += 1;
|
|
35
|
+
return () => this.release();
|
|
36
|
+
}
|
|
37
|
+
release() {
|
|
38
|
+
this.active -= 1;
|
|
39
|
+
const next = this.waiting.shift();
|
|
40
|
+
if (next)
|
|
41
|
+
next();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class McpServerRegistry {
|
|
45
|
+
constructor(options) {
|
|
46
|
+
this.options = options;
|
|
47
|
+
this.clients = new Map();
|
|
48
|
+
this.health = new Map();
|
|
49
|
+
this.discovered = false;
|
|
50
|
+
this.semaphore = new Semaphore(options.maxConcurrentCalls ?? DEFAULT_MAX_CONCURRENT_CALLS);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Connects to every enabled server and returns their tools as Codali tool
|
|
54
|
+
* definitions. Servers are contacted in parallel; a failure is recorded as
|
|
55
|
+
* unhealthy rather than thrown, so one bad entry in a tenant's config cannot
|
|
56
|
+
* take down the run.
|
|
57
|
+
*/
|
|
58
|
+
async discoverTools() {
|
|
59
|
+
const definitions = [];
|
|
60
|
+
const results = await Promise.all(this.options.servers.map(async (definition) => {
|
|
61
|
+
if (definition.enabled === false) {
|
|
62
|
+
this.health.set(definition.name, {
|
|
63
|
+
name: definition.name,
|
|
64
|
+
status: "disabled",
|
|
65
|
+
toolCount: 0,
|
|
66
|
+
});
|
|
67
|
+
this.options.onEvent?.({
|
|
68
|
+
type: "server_skipped",
|
|
69
|
+
server: definition.name,
|
|
70
|
+
reason: "disabled",
|
|
71
|
+
});
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
const startedMs = Date.now();
|
|
75
|
+
const client = this.options.createClient
|
|
76
|
+
? this.options.createClient(definition)
|
|
77
|
+
: new McpClient({ definition });
|
|
78
|
+
this.clients.set(definition.name, client);
|
|
79
|
+
let tools;
|
|
80
|
+
try {
|
|
81
|
+
tools = await client.listTools();
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
const message = error instanceof McpClientError
|
|
85
|
+
? error.message
|
|
86
|
+
: error instanceof Error
|
|
87
|
+
? error.message
|
|
88
|
+
: String(error);
|
|
89
|
+
this.health.set(definition.name, {
|
|
90
|
+
name: definition.name,
|
|
91
|
+
status: "failed",
|
|
92
|
+
toolCount: 0,
|
|
93
|
+
error: message,
|
|
94
|
+
});
|
|
95
|
+
this.options.onEvent?.({
|
|
96
|
+
type: "server_failed",
|
|
97
|
+
server: definition.name,
|
|
98
|
+
error: message,
|
|
99
|
+
});
|
|
100
|
+
// Drop the client so a later call does not retry a dead connection.
|
|
101
|
+
this.clients.delete(definition.name);
|
|
102
|
+
await client.close();
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
const durationMs = Date.now() - startedMs;
|
|
106
|
+
this.health.set(definition.name, {
|
|
107
|
+
name: definition.name,
|
|
108
|
+
status: "connected",
|
|
109
|
+
toolCount: tools.length,
|
|
110
|
+
discoveryMs: durationMs,
|
|
111
|
+
});
|
|
112
|
+
this.options.onEvent?.({
|
|
113
|
+
type: "server_connected",
|
|
114
|
+
server: definition.name,
|
|
115
|
+
toolCount: tools.length,
|
|
116
|
+
durationMs,
|
|
117
|
+
});
|
|
118
|
+
return mcpToolsToDefinitions(tools, {
|
|
119
|
+
client: this.wrapClient(client),
|
|
120
|
+
server: definition.name,
|
|
121
|
+
capability: definition.name,
|
|
122
|
+
// Operator declaration only. Claiming read-only for an unvetted
|
|
123
|
+
// server would mislabel tools like `edit_file` and `move_file`, which
|
|
124
|
+
// is worse than admitting we do not know.
|
|
125
|
+
readOnly: definition.readOnly === true,
|
|
126
|
+
});
|
|
127
|
+
}));
|
|
128
|
+
for (const group of results)
|
|
129
|
+
definitions.push(...group);
|
|
130
|
+
this.discovered = true;
|
|
131
|
+
return definitions;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Wraps a client so every tool call is bounded by the concurrency budget and
|
|
135
|
+
* retried once on a transport failure. Retrying is safe here because Phase 2
|
|
136
|
+
* connectors are read-only.
|
|
137
|
+
*/
|
|
138
|
+
wrapClient(client) {
|
|
139
|
+
const semaphore = this.semaphore;
|
|
140
|
+
const wrapped = Object.create(client);
|
|
141
|
+
wrapped.callTool = async (name, args) => {
|
|
142
|
+
const release = await semaphore.acquire();
|
|
143
|
+
try {
|
|
144
|
+
let lastError;
|
|
145
|
+
for (let attempt = 1; attempt <= MAX_CALL_ATTEMPTS; attempt += 1) {
|
|
146
|
+
try {
|
|
147
|
+
return await client.callTool(name, args);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
lastError = error;
|
|
151
|
+
const retryable = error instanceof McpClientError && error.retryable;
|
|
152
|
+
if (!retryable || attempt === MAX_CALL_ATTEMPTS)
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
throw new ToolExecutionError("tool_execution_failed", lastError instanceof Error ? lastError.message : String(lastError), { retryable: false, details: { tool: name } });
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
release();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
return wrapped;
|
|
163
|
+
}
|
|
164
|
+
healthReport() {
|
|
165
|
+
return [...this.health.values()];
|
|
166
|
+
}
|
|
167
|
+
get isDiscovered() {
|
|
168
|
+
return this.discovered;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Shuts down every connection. Must run even when a run fails: stdio servers
|
|
172
|
+
* are child processes and would otherwise be orphaned.
|
|
173
|
+
*/
|
|
174
|
+
async close() {
|
|
175
|
+
const clients = [...this.clients.values()];
|
|
176
|
+
this.clients.clear();
|
|
177
|
+
await Promise.all(clients.map((client) => client.close()));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export const createMcpServerRegistry = (options) => new McpServerRegistry(options);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ToolDefinition, ToolInputSchema } from "../../tools/ToolTypes.js";
|
|
2
|
+
import type { McpClient, McpToolDefinition } from "./McpClient.js";
|
|
3
|
+
/**
|
|
4
|
+
* Adapts MCP tools into Codali's own tool contract.
|
|
5
|
+
*
|
|
6
|
+
* The direction matters: MCP adapts *into* the neutral `ToolDefinition`, never
|
|
7
|
+
* the reverse. Codali's internals stay decoupled from MCP's evolution, and a
|
|
8
|
+
* built-in tool, a docdex tool and an MCP tool are indistinguishable to the
|
|
9
|
+
* planner and the executor.
|
|
10
|
+
*/
|
|
11
|
+
/** `mcp:<server>:<tool>` — the namespace that keeps two servers from colliding. */
|
|
12
|
+
export declare const mcpToolName: (server: string, tool: string) => string;
|
|
13
|
+
export declare const parseMcpToolName: (name: string) => {
|
|
14
|
+
server: string;
|
|
15
|
+
tool: string;
|
|
16
|
+
} | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Normalizes an MCP input schema into Codali's `ToolInputSchema`.
|
|
19
|
+
*
|
|
20
|
+
* `additionalProperties` is forced open because the registry rejects unknown
|
|
21
|
+
* arguments by default, and MCP servers frequently omit properties they still
|
|
22
|
+
* accept. Rejecting a valid argument is worse here than passing an extra one
|
|
23
|
+
* through: the server validates its own inputs.
|
|
24
|
+
*/
|
|
25
|
+
export declare const normalizeMcpInputSchema: (schema: Record<string, unknown> | undefined) => ToolInputSchema;
|
|
26
|
+
export interface McpToolAdapterOptions {
|
|
27
|
+
client: McpClient;
|
|
28
|
+
server: string;
|
|
29
|
+
/**
|
|
30
|
+
* Capability group the server's tools belong to, used for the orchestrator's
|
|
31
|
+
* first-stage selection. Defaults to the server name, which is usually what
|
|
32
|
+
* an operator means ("github", "jira").
|
|
33
|
+
*/
|
|
34
|
+
capability?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Whether these tools may mutate anything.
|
|
37
|
+
*
|
|
38
|
+
* Decided by Codali policy — an operator marking a connector read-only —
|
|
39
|
+
* never by the server's own `readOnlyHint`. A server asserting it is harmless
|
|
40
|
+
* is the party being constrained vouching for itself.
|
|
41
|
+
*/
|
|
42
|
+
readOnly: boolean;
|
|
43
|
+
/** Result text beyond this is truncated before it reaches a model. */
|
|
44
|
+
maxOutputChars?: number;
|
|
45
|
+
}
|
|
46
|
+
export declare const mcpToolToDefinition: (tool: McpToolDefinition, options: McpToolAdapterOptions) => ToolDefinition;
|
|
47
|
+
export declare const mcpToolsToDefinitions: (tools: readonly McpToolDefinition[], options: McpToolAdapterOptions) => ToolDefinition[];
|
|
48
|
+
//# sourceMappingURL=McpToolAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpToolAdapter.d.ts","sourceRoot":"","sources":["../../../src/connectors/mcp/McpToolAdapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhF,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnE;;;;;;;GAOG;AAEH,mFAAmF;AACnF,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,KAAG,MAClC,CAAC;AAE1B,eAAO,MAAM,gBAAgB,GAC3B,MAAM,MAAM,KACX;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAIrC,CAAC;AAKF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAClC,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,KAC1C,eAeF,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AA4BD,eAAO,MAAM,mBAAmB,GAC9B,MAAM,iBAAiB,EACvB,SAAS,qBAAqB,KAC7B,cAsCF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,OAAO,SAAS,iBAAiB,EAAE,EACnC,SAAS,qBAAqB,KAC7B,cAAc,EAA6D,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { ToolExecutionError } from "../../tools/ToolTypes.js";
|
|
2
|
+
import { truncateToolResult } from "../../tools/TruncateResult.js";
|
|
3
|
+
/**
|
|
4
|
+
* Adapts MCP tools into Codali's own tool contract.
|
|
5
|
+
*
|
|
6
|
+
* The direction matters: MCP adapts *into* the neutral `ToolDefinition`, never
|
|
7
|
+
* the reverse. Codali's internals stay decoupled from MCP's evolution, and a
|
|
8
|
+
* built-in tool, a docdex tool and an MCP tool are indistinguishable to the
|
|
9
|
+
* planner and the executor.
|
|
10
|
+
*/
|
|
11
|
+
/** `mcp:<server>:<tool>` — the namespace that keeps two servers from colliding. */
|
|
12
|
+
export const mcpToolName = (server, tool) => `mcp:${server}:${tool}`;
|
|
13
|
+
export const parseMcpToolName = (name) => {
|
|
14
|
+
const parts = name.split(":");
|
|
15
|
+
if (parts.length < 3 || parts[0] !== "mcp")
|
|
16
|
+
return undefined;
|
|
17
|
+
return { server: parts[1] ?? "", tool: parts.slice(2).join(":") };
|
|
18
|
+
};
|
|
19
|
+
const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
20
|
+
/**
|
|
21
|
+
* Normalizes an MCP input schema into Codali's `ToolInputSchema`.
|
|
22
|
+
*
|
|
23
|
+
* `additionalProperties` is forced open because the registry rejects unknown
|
|
24
|
+
* arguments by default, and MCP servers frequently omit properties they still
|
|
25
|
+
* accept. Rejecting a valid argument is worse here than passing an extra one
|
|
26
|
+
* through: the server validates its own inputs.
|
|
27
|
+
*/
|
|
28
|
+
export const normalizeMcpInputSchema = (schema) => {
|
|
29
|
+
if (!isRecord(schema)) {
|
|
30
|
+
return { type: "object", properties: {}, additionalProperties: true };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
...schema,
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: isRecord(schema.properties)
|
|
36
|
+
? schema.properties
|
|
37
|
+
: {},
|
|
38
|
+
required: Array.isArray(schema.required)
|
|
39
|
+
? schema.required.filter((entry) => typeof entry === "string")
|
|
40
|
+
: undefined,
|
|
41
|
+
additionalProperties: true,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
const DEFAULT_MAX_OUTPUT_CHARS = 12000;
|
|
45
|
+
const tryParseJson = (value) => {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(value);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const truncate = (value, limit) => value.length > limit
|
|
54
|
+
? `${value.slice(0, limit)}\n…[truncated ${value.length - limit} characters]`
|
|
55
|
+
: value;
|
|
56
|
+
/**
|
|
57
|
+
* Builds a description the planner can act on. MCP descriptions are sometimes
|
|
58
|
+
* absent, in which case the tool name is all we have — say so plainly rather
|
|
59
|
+
* than inventing capability the tool may not have.
|
|
60
|
+
*/
|
|
61
|
+
const describeMcpTool = (tool, server) => {
|
|
62
|
+
const base = tool.description?.trim();
|
|
63
|
+
if (base)
|
|
64
|
+
return base;
|
|
65
|
+
return `${tool.name} (provided by the ${server} MCP server; no description supplied).`;
|
|
66
|
+
};
|
|
67
|
+
export const mcpToolToDefinition = (tool, options) => {
|
|
68
|
+
const maxOutputChars = options.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
69
|
+
const qualified = mcpToolName(options.server, tool.name);
|
|
70
|
+
return {
|
|
71
|
+
name: qualified,
|
|
72
|
+
description: describeMcpTool(tool, options.server),
|
|
73
|
+
inputSchema: normalizeMcpInputSchema(tool.inputSchema),
|
|
74
|
+
outputSchema: tool.outputSchema,
|
|
75
|
+
readOnly: options.readOnly,
|
|
76
|
+
capability: options.capability ?? options.server,
|
|
77
|
+
handler: async (args) => {
|
|
78
|
+
const result = await options.client.callTool(tool.name, args);
|
|
79
|
+
if (!result.ok) {
|
|
80
|
+
// Surface as a tool failure so the runner records it and the model is
|
|
81
|
+
// told, rather than passing an error string off as a result.
|
|
82
|
+
throw new ToolExecutionError("tool_execution_failed", result.text || `MCP tool ${qualified} reported an error.`, { retryable: false, details: { server: options.server, tool: tool.name } });
|
|
83
|
+
}
|
|
84
|
+
// Structural where possible: an oversized list keeps whole items so the
|
|
85
|
+
// model receives valid JSON, not a document cut mid-token.
|
|
86
|
+
const structured = result.structured ?? tryParseJson(result.text);
|
|
87
|
+
const trimmed = structured !== undefined
|
|
88
|
+
? truncateToolResult(structured, result.text, maxOutputChars)
|
|
89
|
+
: { text: truncate(result.text, maxOutputChars), truncated: false };
|
|
90
|
+
return {
|
|
91
|
+
output: trimmed.text || "(the tool returned no content)",
|
|
92
|
+
// The *parsed* payload, not the MCP envelope. Evidence normalization
|
|
93
|
+
// reads this to turn each returned record into its own evidence item;
|
|
94
|
+
// handed `{content:[{type:"text",…}]}` it finds no records and emits a
|
|
95
|
+
// single "returned structured data" placeholder instead.
|
|
96
|
+
data: structured ?? result.raw,
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
export const mcpToolsToDefinitions = (tools, options) => tools.map((tool) => mcpToolToDefinition(tool, options));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createMcpServerRegistry, type McpServerRegistry } from "./McpServerRegistry.js";
|
|
2
|
+
import type { McpServerHealth } from "./McpServerRegistry.js";
|
|
3
|
+
import type { RunContext } from "../../runcontext/RunContextResolver.js";
|
|
4
|
+
import type { ToolRegistry } from "../../tools/ToolRegistry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Connects a run's configured MCP servers and registers what they expose into
|
|
7
|
+
* the run's `ToolRegistry`.
|
|
8
|
+
*
|
|
9
|
+
* Registration goes into the *existing* registry rather than a parallel store:
|
|
10
|
+
* one source of truth means the planner cannot see a schema the executor would
|
|
11
|
+
* not honour, no matter where the tool came from.
|
|
12
|
+
*/
|
|
13
|
+
export interface McpAttachResult {
|
|
14
|
+
registry?: McpServerRegistry;
|
|
15
|
+
health: McpServerHealth[];
|
|
16
|
+
registered: string[];
|
|
17
|
+
warnings: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface McpAttachOptions {
|
|
20
|
+
context: RunContext;
|
|
21
|
+
toolRegistry: ToolRegistry;
|
|
22
|
+
maxConcurrentCalls?: number;
|
|
23
|
+
/** Injected in tests. */
|
|
24
|
+
createRegistry?: typeof createMcpServerRegistry;
|
|
25
|
+
}
|
|
26
|
+
export declare const attachMcpTools: (options: McpAttachOptions) => Promise<McpAttachResult>;
|
|
27
|
+
//# sourceMappingURL=McpToolSource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpToolSource.d.ts","sourceRoot":"","sources":["../../../src/connectors/mcp/McpToolSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE;;;;;;;GAOG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,UAAU,CAAC;IACpB,YAAY,EAAE,YAAY,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,cAAc,CAAC,EAAE,OAAO,uBAAuB,CAAC;CACjD;AAED,eAAO,MAAM,cAAc,GACzB,SAAS,gBAAgB,KACxB,OAAO,CAAC,eAAe,CAuCzB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createMcpServerRegistry } from "./McpServerRegistry.js";
|
|
2
|
+
export const attachMcpTools = async (options) => {
|
|
3
|
+
const servers = options.context.mcpServers ?? [];
|
|
4
|
+
if (servers.length === 0) {
|
|
5
|
+
return { health: [], registered: [], warnings: [] };
|
|
6
|
+
}
|
|
7
|
+
const warnings = [];
|
|
8
|
+
const registry = (options.createRegistry ?? createMcpServerRegistry)({
|
|
9
|
+
servers,
|
|
10
|
+
maxConcurrentCalls: options.maxConcurrentCalls,
|
|
11
|
+
});
|
|
12
|
+
const definitions = await registry.discoverTools();
|
|
13
|
+
const registered = [];
|
|
14
|
+
for (const definition of definitions) {
|
|
15
|
+
try {
|
|
16
|
+
options.toolRegistry.register(definition);
|
|
17
|
+
registered.push(definition.name);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
// A duplicate name means two servers claimed the same namespaced tool,
|
|
21
|
+
// which the `mcp:<server>:<tool>` scheme should prevent. Report rather
|
|
22
|
+
// than crash, and keep the first registration.
|
|
23
|
+
warnings.push(`mcp_tool_registration_failed:${definition.name}:${error instanceof Error ? error.message : String(error)}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const health = registry.healthReport();
|
|
27
|
+
for (const entry of health) {
|
|
28
|
+
if (entry.status === "failed") {
|
|
29
|
+
warnings.push(`mcp_server_unavailable:${entry.name}:${entry.error ?? "unknown"}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { registry, health, registered, warnings };
|
|
33
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ToolDefinition } from "../../tools/ToolTypes.js";
|
|
2
|
+
/**
|
|
3
|
+
* Image generation, as an ordinary tool.
|
|
4
|
+
*
|
|
5
|
+
* The original design gave media its own orchestration route, which forced
|
|
6
|
+
* every request to be classified as "media or not" up front and duplicated the
|
|
7
|
+
* planning path. It is simpler and more accurate to treat generation as a tool
|
|
8
|
+
* the orchestrator may call: "generate an image of a puppy" becomes a plan with
|
|
9
|
+
* one task, exactly like any other single-source question, and a request that
|
|
10
|
+
* needs both research *and* an image needs no special case at all.
|
|
11
|
+
*
|
|
12
|
+
* The tool returns an `ArtifactRef`. Image bytes are written to disk and
|
|
13
|
+
* referenced — never inlined into evidence or an answer, which would blow the
|
|
14
|
+
* context budget and put base64 in the trace.
|
|
15
|
+
*/
|
|
16
|
+
export interface ImageGenerationConfig {
|
|
17
|
+
/** OpenAI-compatible `/v1/images/generations` endpoint. */
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
model: string;
|
|
20
|
+
apiKey?: string;
|
|
21
|
+
/** Where artifacts are written. Defaults to `<workspace>/.codali/artifacts`. */
|
|
22
|
+
artifactDir?: string;
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
size?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ImageToolOptions {
|
|
27
|
+
config: ImageGenerationConfig;
|
|
28
|
+
workspaceRoot: string;
|
|
29
|
+
runId?: string;
|
|
30
|
+
fetchImpl?: typeof fetch;
|
|
31
|
+
/** Called for each artifact so the gateway can surface it in the result. */
|
|
32
|
+
onArtifact?: (artifact: {
|
|
33
|
+
id: string;
|
|
34
|
+
type: string;
|
|
35
|
+
path: string;
|
|
36
|
+
mimeType: string;
|
|
37
|
+
model: string;
|
|
38
|
+
prompt: string;
|
|
39
|
+
}) => void;
|
|
40
|
+
}
|
|
41
|
+
export declare const IMAGE_TOOL_NAME = "media_generate_image";
|
|
42
|
+
export declare const createImageGenerationTool: (options: ImageToolOptions) => ToolDefinition;
|
|
43
|
+
//# sourceMappingURL=ImageGenerationTool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageGenerationTool.d.ts","sourceRoot":"","sources":["../../../src/connectors/media/ImageGenerationTool.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,qBAAqB;IACpC,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AA0BD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,qBAAqB,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE;QACtB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,IAAI,CAAC;CACZ;AAED,eAAO,MAAM,eAAe,yBAAyB,CAAC;AAEtD,eAAO,MAAM,yBAAyB,GAAI,SAAS,gBAAgB,KAAG,cAgIrE,CAAC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { ToolExecutionError } from "../../tools/ToolTypes.js";
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 180000;
|
|
6
|
+
const DEFAULT_SIZE = "512x512";
|
|
7
|
+
const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
8
|
+
/**
|
|
9
|
+
* Extracts image bytes from an OpenAI-compatible response. Servers return
|
|
10
|
+
* either `b64_json` or a `url`; both shapes are handled because local
|
|
11
|
+
* stable-diffusion servers and hosted APIs differ here.
|
|
12
|
+
*/
|
|
13
|
+
const readImagePayload = (payload) => {
|
|
14
|
+
if (!isRecord(payload))
|
|
15
|
+
return undefined;
|
|
16
|
+
const data = payload.data;
|
|
17
|
+
if (!Array.isArray(data) || data.length === 0)
|
|
18
|
+
return undefined;
|
|
19
|
+
const first = data[0];
|
|
20
|
+
if (!isRecord(first))
|
|
21
|
+
return undefined;
|
|
22
|
+
const b64 = typeof first.b64_json === "string" ? first.b64_json : undefined;
|
|
23
|
+
const url = typeof first.url === "string" ? first.url : undefined;
|
|
24
|
+
return b64 || url ? { b64, url } : undefined;
|
|
25
|
+
};
|
|
26
|
+
export const IMAGE_TOOL_NAME = "media_generate_image";
|
|
27
|
+
export const createImageGenerationTool = (options) => {
|
|
28
|
+
const { config } = options;
|
|
29
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
30
|
+
return {
|
|
31
|
+
name: IMAGE_TOOL_NAME,
|
|
32
|
+
description: "Generate an image from a text prompt. Returns the path to the written image file, not the image itself. Use for any request to draw, render, or generate a picture.",
|
|
33
|
+
capability: "media",
|
|
34
|
+
// Generation writes a file, but only inside the run's own artifact
|
|
35
|
+
// directory — it cannot touch the workspace or anything the user owns.
|
|
36
|
+
readOnly: true,
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: "object",
|
|
39
|
+
required: ["prompt"],
|
|
40
|
+
properties: {
|
|
41
|
+
prompt: { type: "string", minLength: 1 },
|
|
42
|
+
size: { type: "string" },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
handler: async (args) => {
|
|
46
|
+
const record = isRecord(args) ? args : {};
|
|
47
|
+
const prompt = typeof record.prompt === "string" ? record.prompt.trim() : "";
|
|
48
|
+
if (!prompt) {
|
|
49
|
+
throw new ToolExecutionError("tool_invalid_args", "An image prompt is required.", {
|
|
50
|
+
retryable: false,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const controller = new AbortController();
|
|
54
|
+
const timer = setTimeout(() => controller.abort(), config.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
55
|
+
let response;
|
|
56
|
+
try {
|
|
57
|
+
response = await fetchImpl(new URL("images/generations", config.baseUrl.endsWith("/")
|
|
58
|
+
? config.baseUrl
|
|
59
|
+
: `${config.baseUrl}/`).toString(), {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: {
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
model: config.model,
|
|
67
|
+
prompt,
|
|
68
|
+
n: 1,
|
|
69
|
+
size: typeof record.size === "string" ? record.size : (config.size ?? DEFAULT_SIZE),
|
|
70
|
+
response_format: "b64_json",
|
|
71
|
+
}),
|
|
72
|
+
signal: controller.signal,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
throw new ToolExecutionError("tool_execution_failed", `Image generation request failed: ${error instanceof Error ? error.message : String(error)}`, { retryable: true });
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
}
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
throw new ToolExecutionError("tool_execution_failed", `Image generation returned HTTP ${response.status}.`, { retryable: response.status >= 500, details: { status: response.status } });
|
|
83
|
+
}
|
|
84
|
+
const payload = await response.json();
|
|
85
|
+
const image = readImagePayload(payload);
|
|
86
|
+
if (!image) {
|
|
87
|
+
throw new ToolExecutionError("tool_execution_failed", "Image generation returned no image data.", { retryable: false });
|
|
88
|
+
}
|
|
89
|
+
let bytes;
|
|
90
|
+
if (image.b64) {
|
|
91
|
+
bytes = Buffer.from(image.b64, "base64");
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const fetched = await fetchImpl(image.url);
|
|
95
|
+
if (!fetched.ok) {
|
|
96
|
+
throw new ToolExecutionError("tool_execution_failed", `Could not download the generated image (HTTP ${fetched.status}).`, { retryable: true });
|
|
97
|
+
}
|
|
98
|
+
bytes = Buffer.from(await fetched.arrayBuffer());
|
|
99
|
+
}
|
|
100
|
+
const artifactDir = config.artifactDir ??
|
|
101
|
+
path.join(options.workspaceRoot, ".codali", "artifacts", options.runId ?? "run");
|
|
102
|
+
await mkdir(artifactDir, { recursive: true });
|
|
103
|
+
const id = `img-${randomUUID()}`;
|
|
104
|
+
const filePath = path.join(artifactDir, `${id}.png`);
|
|
105
|
+
await writeFile(filePath, bytes);
|
|
106
|
+
options.onArtifact?.({
|
|
107
|
+
id,
|
|
108
|
+
type: "image",
|
|
109
|
+
path: filePath,
|
|
110
|
+
mimeType: "image/png",
|
|
111
|
+
model: config.model,
|
|
112
|
+
prompt,
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
// The path, not the bytes: inlining base64 would blow the context
|
|
116
|
+
// budget and put megabytes of noise in the trace.
|
|
117
|
+
output: `Generated an image and wrote it to ${filePath}`,
|
|
118
|
+
data: { artifactId: id, path: filePath, mimeType: "image/png", prompt },
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.0 device authorization grant (RFC 8628).
|
|
3
|
+
*
|
|
4
|
+
* The right flow for a CLI. Authorization-code needs a redirect URI and a local
|
|
5
|
+
* web server; client-credentials gets app-only permissions, which for
|
|
6
|
+
* "summarize *my* mail" would mean reading the entire tenant — both overreach
|
|
7
|
+
* and a different consent conversation.
|
|
8
|
+
*
|
|
9
|
+
* Device code needs neither: the user is shown a short code, approves in a
|
|
10
|
+
* browser on any device, and we receive an access token plus a refresh token.
|
|
11
|
+
* The refresh token is what gets stored; access tokens live about an hour and
|
|
12
|
+
* are minted on demand.
|
|
13
|
+
*/
|
|
14
|
+
export interface DeviceCodeStartResult {
|
|
15
|
+
deviceCode: string;
|
|
16
|
+
userCode: string;
|
|
17
|
+
verificationUri: string;
|
|
18
|
+
expiresInSeconds: number;
|
|
19
|
+
intervalSeconds: number;
|
|
20
|
+
message?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface DeviceCodeTokens {
|
|
23
|
+
accessToken: string;
|
|
24
|
+
refreshToken?: string;
|
|
25
|
+
expiresInSeconds: number;
|
|
26
|
+
scope?: string;
|
|
27
|
+
tokenType: string;
|
|
28
|
+
}
|
|
29
|
+
export interface DeviceCodeConfig {
|
|
30
|
+
deviceCodeUrl: string;
|
|
31
|
+
tokenUrl: string;
|
|
32
|
+
clientId: string;
|
|
33
|
+
scope: string;
|
|
34
|
+
}
|
|
35
|
+
export declare class DeviceCodeError extends Error {
|
|
36
|
+
readonly code: string;
|
|
37
|
+
readonly description?: string;
|
|
38
|
+
constructor(code: string, description?: string);
|
|
39
|
+
}
|
|
40
|
+
export declare const startDeviceCode: (config: DeviceCodeConfig, fetchImpl?: typeof fetch) => Promise<DeviceCodeStartResult>;
|
|
41
|
+
export interface PollOptions {
|
|
42
|
+
onPending?: (secondsRemaining: number) => void;
|
|
43
|
+
now?: () => number;
|
|
44
|
+
sleep?: (ms: number) => Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Polls until the user approves, declines, or the code expires.
|
|
48
|
+
*
|
|
49
|
+
* `authorization_pending` is the normal state and must not be treated as an
|
|
50
|
+
* error; `slow_down` requires backing off, and ignoring it gets the request
|
|
51
|
+
* throttled.
|
|
52
|
+
*/
|
|
53
|
+
export declare const pollForDeviceCodeTokens: (config: DeviceCodeConfig, start: DeviceCodeStartResult, fetchImpl?: typeof fetch, options?: PollOptions) => Promise<DeviceCodeTokens>;
|
|
54
|
+
/**
|
|
55
|
+
* Exchanges a stored refresh token for a fresh access token.
|
|
56
|
+
*
|
|
57
|
+
* Microsoft rotates refresh tokens, so the response may carry a new one; a
|
|
58
|
+
* caller that ignores it will find its stored token dead after the old one's
|
|
59
|
+
* sliding window closes.
|
|
60
|
+
*/
|
|
61
|
+
export declare const refreshAccessToken: (config: Omit<DeviceCodeConfig, "deviceCodeUrl">, refreshToken: string, fetchImpl?: typeof fetch) => Promise<DeviceCodeTokens>;
|
|
62
|
+
/** Microsoft identity platform endpoints for a tenant. */
|
|
63
|
+
export declare const microsoftEndpoints: (tenantId: string) => {
|
|
64
|
+
deviceCodeUrl: string;
|
|
65
|
+
tokenUrl: string;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Delegated Graph scopes Codali asks for, matching what okacam's
|
|
69
|
+
* `microsoft-integration-service` already requests. `offline_access` is what
|
|
70
|
+
* yields a refresh token — without it the grant is good for one hour and then
|
|
71
|
+
* gone.
|
|
72
|
+
*/
|
|
73
|
+
export declare const MICROSOFT_GRAPH_SCOPES: string;
|
|
74
|
+
//# sourceMappingURL=DeviceCodeAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeviceCodeAuth.d.ts","sourceRoot":"","sources":["../../../src/connectors/oauth/DeviceCodeAuth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;CAM/C;AAQD,eAAO,MAAM,eAAe,GAC1B,QAAQ,gBAAgB,EACxB,YAAW,OAAO,KAAa,KAC9B,OAAO,CAAC,qBAAqB,CAyB/B,CAAC;AAWF,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAClC,QAAQ,gBAAgB,EACxB,OAAO,qBAAqB,EAC5B,YAAW,OAAO,KAAa,EAC/B,UAAS,WAAgB,KACxB,OAAO,CAAC,gBAAgB,CA+C1B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,IAAI,CAAC,gBAAgB,EAAE,eAAe,CAAC,EAC/C,cAAc,MAAM,EACpB,YAAW,OAAO,KAAa,KAC9B,OAAO,CAAC,gBAAgB,CAsB1B,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM;;;CAGjD,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QASxB,CAAC"}
|