@zackbart/connecta 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +527 -0
- package/README.md +83 -7
- package/SECURITY.md +10 -6
- package/dist/activity.d.ts +8 -0
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js +1 -0
- package/dist/activity.js.map +1 -1
- package/dist/auth/bearer.d.ts +10 -3
- package/dist/auth/bearer.d.ts.map +1 -1
- package/dist/auth/bearer.js +21 -0
- package/dist/auth/bearer.js.map +1 -1
- package/dist/auth/clerk.d.ts +26 -1
- package/dist/auth/clerk.d.ts.map +1 -1
- package/dist/auth/clerk.js +161 -4
- package/dist/auth/clerk.js.map +1 -1
- package/dist/connectors/api.d.ts +13 -0
- package/dist/connectors/api.d.ts.map +1 -1
- package/dist/connectors/api.js +2 -0
- package/dist/connectors/api.js.map +1 -1
- package/dist/connectors/remote-mcp.d.ts +13 -0
- package/dist/connectors/remote-mcp.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.js +10 -0
- package/dist/connectors/remote-mcp.js.map +1 -1
- package/dist/credential-health.d.ts +212 -0
- package/dist/credential-health.d.ts.map +1 -0
- package/dist/credential-health.js +535 -0
- package/dist/credential-health.js.map +1 -0
- package/dist/execute.d.ts +4 -4
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +16 -4
- package/dist/execute.js.map +1 -1
- package/dist/index.d.ts +77 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +112 -2
- package/dist/index.js.map +1 -1
- package/dist/meta-tools.d.ts +76 -7
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +328 -98
- package/dist/meta-tools.js.map +1 -1
- package/dist/registry.d.ts +245 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +377 -27
- package/dist/registry.js.map +1 -1
- package/dist/server.d.ts +7 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +342 -27
- package/dist/server.js.map +1 -1
- package/dist/skills.d.ts +53 -2
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +162 -2
- package/dist/skills.js.map +1 -1
- package/dist/timeout.d.ts +16 -0
- package/dist/timeout.d.ts.map +1 -0
- package/dist/timeout.js +38 -0
- package/dist/timeout.js.map +1 -0
- package/dist/toolkits.d.ts +138 -0
- package/dist/toolkits.d.ts.map +1 -0
- package/dist/toolkits.js +319 -0
- package/dist/toolkits.js.map +1 -0
- package/dist/types.d.ts +90 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts +63 -0
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +176 -11
- package/dist/ui.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -2
- package/src/activity.ts +9 -0
- package/src/auth/bearer.ts +35 -1
- package/src/auth/clerk.ts +202 -5
- package/src/connectors/api.ts +15 -0
- package/src/connectors/remote-mcp.ts +24 -0
- package/src/credential-health.ts +736 -0
- package/src/execute.ts +32 -8
- package/src/index.ts +226 -2
- package/src/meta-tools.ts +397 -119
- package/src/registry.ts +540 -29
- package/src/server.ts +431 -25
- package/src/skills.ts +185 -2
- package/src/timeout.ts +49 -0
- package/src/toolkits.ts +450 -0
- package/src/types.ts +96 -2
- package/src/ui.ts +190 -11
- package/src/version.ts +1 -1
package/src/skills.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Connector } from "./types.js";
|
|
2
|
+
|
|
1
3
|
export const CONNECTA_INSTRUCTIONS =
|
|
2
4
|
'Connecta exposes many integrations behind meta-tools. When an address is unknown, start with search_tools and includeSchemas="compact"; use describe_tools only when that schema is insufficient. Use call_tool for one explicitly read-only call, batch_call for 2–10 independent explicitly read-only calls, and execute_code (when available) only for dependent read-only steps, loops, joins, or reducing large results. Unannotated, write-capable, and destructive tools must use call_destructive_tool individually. Use authorize_connector only after auth_required and get_result only for truncated results. For the detailed workflow, call skills({ name: "usage" }) once per task.';
|
|
3
5
|
|
|
@@ -14,7 +16,7 @@ export const USAGE_SKILL = `# Connecta usage
|
|
|
14
16
|
- Truncated result: retry with \`fields\` when possible; otherwise page it with \`get_result\`.
|
|
15
17
|
- \`auth_required\`: use \`authorize_connector\`, have the operator complete consent, then confirm with \`list_connectors\`.
|
|
16
18
|
|
|
17
|
-
Use \`list_connectors({ probe: false })\` for a fast inventory. Use \`probe: true\` only when diagnosing live health or authorization.
|
|
19
|
+
Use \`list_connectors({ probe: false })\` for a fast inventory. Use \`probe: true\` only when diagnosing live health or authorization. The fast inventory already reports a connector whose stored credential failed a proactive check as \`auth_required\` (with \`credentialCheck\` and the URL to open), so trust it and authorize up front rather than probing to confirm.
|
|
18
20
|
|
|
19
21
|
## Code mode
|
|
20
22
|
|
|
@@ -53,11 +55,192 @@ async () => {
|
|
|
53
55
|
\`\`\`
|
|
54
56
|
`;
|
|
55
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Appended to USAGE_SKILL only when the deployment actually has at least one
|
|
60
|
+
* connector guide. A deployment with none — every deployment that has not
|
|
61
|
+
* adopted the feature — keeps the base guide byte-for-byte, rather than paying
|
|
62
|
+
* context for an instruction to fetch guides that do not exist.
|
|
63
|
+
*/
|
|
64
|
+
export const CONNECTOR_GUIDES_SECTION = `
|
|
65
|
+
## Per-connector guides
|
|
66
|
+
|
|
67
|
+
Some connectors here ship their own usage guide — preferred tools, address quirks, pagination conventions, rate-limit etiquette, query patterns. \`skills({})\` lists each one as \`connector:<connectorId>\`; fetch it with \`skills({ name: "connector:<connectorId>" })\`. \`search_tools\` and \`describe_tools\` set \`guide\` on matches whose connector has one. Read a connector's guide before working with it for the first time in a task.
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
/** True when at least one of `connectors` carries a usage guide. */
|
|
71
|
+
export function hasConnectorGuides(connectors: readonly Connector[]): boolean {
|
|
72
|
+
return connectors.some(
|
|
73
|
+
(connector) => connectorGuide(connector) !== undefined,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The built-in usage guide, plus the guides section when there is one to point at. */
|
|
78
|
+
export function usageSkill(connectors: readonly Connector[]): string {
|
|
79
|
+
return hasConnectorGuides(connectors)
|
|
80
|
+
? USAGE_SKILL + CONNECTOR_GUIDES_SECTION
|
|
81
|
+
: USAGE_SKILL;
|
|
82
|
+
}
|
|
83
|
+
|
|
56
84
|
export const AVAILABLE_SKILLS = [
|
|
57
85
|
{
|
|
58
86
|
name: "usage",
|
|
59
87
|
description:
|
|
60
88
|
"How to choose among Connecta discovery, direct, batch, destructive, and code-mode tools.",
|
|
61
|
-
content:
|
|
89
|
+
content: usageSkill,
|
|
62
90
|
},
|
|
63
91
|
] as const;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Namespace for operator-authored per-connector guides. Built-in skill names
|
|
95
|
+
* are bare identifiers and never contain ":", so `connector:<id>` cannot
|
|
96
|
+
* collide with one — not even when a connector's id is literally "usage".
|
|
97
|
+
* The prefixed form is the ONLY way to reach a connector guide: a bare
|
|
98
|
+
* connector id is never resolved, so nothing shadows anything silently.
|
|
99
|
+
*/
|
|
100
|
+
export const CONNECTOR_SKILL_PREFIX = "connector:";
|
|
101
|
+
|
|
102
|
+
/** The skill name that fetches `connector`'s guide. */
|
|
103
|
+
export function connectorSkillName(connectorId: string): string {
|
|
104
|
+
return `${CONNECTOR_SKILL_PREFIX}${connectorId}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The connector's guide, or undefined when it declares none (or a blank one). */
|
|
108
|
+
export function connectorGuide(connector: Connector): string | undefined {
|
|
109
|
+
const guide = connector.usageGuide;
|
|
110
|
+
return guide && guide.trim() !== "" ? guide : undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const SUMMARY_LENGTH = 120;
|
|
114
|
+
|
|
115
|
+
/** A `---`/`***`/`___` rule, which also opens and closes YAML frontmatter. */
|
|
116
|
+
const RULE_RE = /^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/;
|
|
117
|
+
|
|
118
|
+
/** A fenced code block's delimiter. */
|
|
119
|
+
const FENCE_RE = /^\s*(?:```|~~~)/;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Markup that carries no summary text of its own: horizontal rules, HTML
|
|
123
|
+
* comments, and table rows. Skipped so a guide that opens with one is
|
|
124
|
+
* summarized by its first real line instead of by punctuation.
|
|
125
|
+
*/
|
|
126
|
+
const NOT_SUMMARY_RE = /^\s*(?:<!--|\|)|^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/;
|
|
127
|
+
|
|
128
|
+
/** Drop a leading YAML frontmatter block — metadata, not summary text. */
|
|
129
|
+
function withoutFrontmatter(lines: string[]): string[] {
|
|
130
|
+
let start = 0;
|
|
131
|
+
while (start < lines.length && lines[start].trim() === "") start++;
|
|
132
|
+
if (start >= lines.length || !RULE_RE.test(lines[start])) return lines;
|
|
133
|
+
const close = lines.findIndex((line, i) => i > start && RULE_RE.test(line));
|
|
134
|
+
return close === -1 ? lines : lines.slice(close + 1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* One line describing a guide, for the cheap list view: the guide's first
|
|
139
|
+
* meaningful line (heading marks and list bullets stripped), falling back to
|
|
140
|
+
* the connector's own description when the guide opens with nothing but
|
|
141
|
+
* markup.
|
|
142
|
+
*/
|
|
143
|
+
function summarizeGuide(connector: Connector, guide: string): string {
|
|
144
|
+
let inFence = false;
|
|
145
|
+
for (const raw of withoutFrontmatter(guide.split("\n"))) {
|
|
146
|
+
if (FENCE_RE.test(raw)) {
|
|
147
|
+
inFence = !inFence;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (inFence) continue;
|
|
151
|
+
if (raw.trim() === "" || NOT_SUMMARY_RE.test(raw)) continue;
|
|
152
|
+
const line = raw
|
|
153
|
+
// `\s*` (not `\s+`) so a bare `#` strips to nothing and is skipped, and
|
|
154
|
+
// an unspaced `#Heading` is still read as a heading.
|
|
155
|
+
.replace(/^\s*#{1,6}\s*/, "")
|
|
156
|
+
.replace(/^\s*[-*+]\s+/, "")
|
|
157
|
+
.replace(/\s+/g, " ")
|
|
158
|
+
.trim();
|
|
159
|
+
if (line === "") continue;
|
|
160
|
+
return line.length <= SUMMARY_LENGTH
|
|
161
|
+
? line
|
|
162
|
+
: `${line.slice(0, SUMMARY_LENGTH - 1).trimEnd()}…`;
|
|
163
|
+
}
|
|
164
|
+
return connector.description ?? `Usage guide for "${connector.id}".`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface SkillListing {
|
|
168
|
+
name: string;
|
|
169
|
+
description: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Every fetchable skill: the built-in guides plus one entry per connector that
|
|
174
|
+
* carries a usage guide. Derived from the connector list passed in — the single
|
|
175
|
+
* place guide visibility is decided. The `skills` meta-tool passes its
|
|
176
|
+
* connection's `registry.listConnectors()`, so a toolkit-scoped session lists
|
|
177
|
+
* only in-scope guides, and `resolveSkill` below reports an out-of-scope
|
|
178
|
+
* `connector:<id>` exactly as it reports an unknown connector.
|
|
179
|
+
*/
|
|
180
|
+
export function listSkills(connectors: readonly Connector[]): SkillListing[] {
|
|
181
|
+
const listing: SkillListing[] = AVAILABLE_SKILLS.map((skill) => ({
|
|
182
|
+
name: skill.name,
|
|
183
|
+
description: skill.description,
|
|
184
|
+
}));
|
|
185
|
+
for (const connector of connectors) {
|
|
186
|
+
const guide = connectorGuide(connector);
|
|
187
|
+
if (!guide) continue;
|
|
188
|
+
listing.push({
|
|
189
|
+
name: connectorSkillName(connector.id),
|
|
190
|
+
description: summarizeGuide(connector, guide),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return listing;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export type SkillLookup =
|
|
197
|
+
{ found: true; content: string } | { found: false; message: string };
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Resolve one skill name. Built-in names match exactly; connector guides are
|
|
201
|
+
* reachable only through the `connector:` prefix. Every miss — unknown name,
|
|
202
|
+
* unknown connector, connector without a guide — is an explicit error, never a
|
|
203
|
+
* silent fallback to the generic guide.
|
|
204
|
+
*/
|
|
205
|
+
export function resolveSkill(
|
|
206
|
+
name: string,
|
|
207
|
+
connectors: readonly Connector[],
|
|
208
|
+
): SkillLookup {
|
|
209
|
+
const builtIn = AVAILABLE_SKILLS.find((skill) => skill.name === name);
|
|
210
|
+
if (builtIn) return { found: true, content: builtIn.content(connectors) };
|
|
211
|
+
const available = () =>
|
|
212
|
+
listSkills(connectors)
|
|
213
|
+
.map((skill) => skill.name)
|
|
214
|
+
.join(", ");
|
|
215
|
+
if (name.startsWith(CONNECTOR_SKILL_PREFIX)) {
|
|
216
|
+
const id = name.slice(CONNECTOR_SKILL_PREFIX.length);
|
|
217
|
+
const connector = connectors.find((c) => c.id === id);
|
|
218
|
+
if (!connector) {
|
|
219
|
+
return {
|
|
220
|
+
found: false,
|
|
221
|
+
message: `Unknown connector "${id}". Available skills: ${available()}.`,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const guide = connectorGuide(connector);
|
|
225
|
+
if (!guide) {
|
|
226
|
+
return {
|
|
227
|
+
found: false,
|
|
228
|
+
message: `Connector "${id}" has no usage guide. Available skills: ${available()}.`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return { found: true, content: guide };
|
|
232
|
+
}
|
|
233
|
+
const bare = connectors.find((c) => c.id === name);
|
|
234
|
+
if (bare) {
|
|
235
|
+
return {
|
|
236
|
+
found: false,
|
|
237
|
+
message: connectorGuide(bare)
|
|
238
|
+
? `Unknown skill "${name}". Connector guides are fetched as "${connectorSkillName(name)}". Available: ${available()}.`
|
|
239
|
+
: `Connector "${name}" has no usage guide. Available skills: ${available()}.`,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
found: false,
|
|
244
|
+
message: `Unknown skill "${name}". Available: ${available()}.`,
|
|
245
|
+
};
|
|
246
|
+
}
|
package/src/timeout.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// The deadline vocabulary shared by every non-call downstream probe: the
|
|
2
|
+
// discovery meta-tools' catalog fan-out (src/meta-tools.ts) and the credential
|
|
3
|
+
// liveness checks (src/credential-health.ts). One definition so a "probe" means
|
|
4
|
+
// the same thing, and is bounded the same way, wherever one is issued.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generous default bound for a single downstream probe/catalog call. High enough
|
|
8
|
+
* to trip only on a pathological hang, not a realistically slow probe.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_PROBE_TIMEOUT_MS = 30_000;
|
|
11
|
+
|
|
12
|
+
/** A finite, positive integer number of milliseconds, or undefined. */
|
|
13
|
+
export function normalizeTimeoutMs(
|
|
14
|
+
value: number | undefined,
|
|
15
|
+
): number | undefined {
|
|
16
|
+
if (value === undefined || !Number.isFinite(value) || !(value > 0)) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
return Math.max(1, Math.trunc(value));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Reject `promise` after `ms` if it has not settled, so one hung downstream
|
|
24
|
+
* cannot stall a whole fan-out. NOTE: this bounds only the caller-facing wait —
|
|
25
|
+
* the registry probe methods take no AbortSignal, so the underlying fetch is
|
|
26
|
+
* NOT cancelled and keeps running in the background. Real cancellation
|
|
27
|
+
* (AbortSignal plumbed through the registry) is a deferred follow-up.
|
|
28
|
+
*/
|
|
29
|
+
export function withTimeout<T>(
|
|
30
|
+
promise: Promise<T>,
|
|
31
|
+
ms: number,
|
|
32
|
+
label: string,
|
|
33
|
+
): Promise<T> {
|
|
34
|
+
return new Promise<T>((resolve, reject) => {
|
|
35
|
+
const timer = setTimeout(() => {
|
|
36
|
+
reject(new Error(`${label} timed out after ${ms}ms`));
|
|
37
|
+
}, ms);
|
|
38
|
+
promise.then(
|
|
39
|
+
(value) => {
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
resolve(value);
|
|
42
|
+
},
|
|
43
|
+
(err) => {
|
|
44
|
+
clearTimeout(timer);
|
|
45
|
+
reject(err);
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
}
|