@vornrun/connector-sdk 0.7.0-beta.7 → 0.7.0-beta.8
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 +18 -0
- package/dist/{chunk-457KOZUU.js → chunk-NXZBUV63.js} +149 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +25 -2
- package/dist/index.d.ts +3 -298
- package/dist/index.js +13 -1
- package/dist/pack-C3ZJx9d4.d.ts +340 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -257,6 +257,21 @@ hand:
|
|
|
257
257
|
npx vorn-connector setup ./dist/index.js
|
|
258
258
|
```
|
|
259
259
|
|
|
260
|
+
## Pack it as a file
|
|
261
|
+
|
|
262
|
+
`vorn-connector pack` builds a single installable file: the manifest plus one
|
|
263
|
+
bundled entry with every dependency inlined.
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
npx vorn-connector pack ./dist/index.js --out ./release
|
|
267
|
+
# → release/acme-1.2.3.vorn.tgz
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Packing runs `check` first, then two gates a pack must pass: the source package
|
|
271
|
+
declares no install-time scripts, and nothing was left outside the bundle. A
|
|
272
|
+
pack installs by copying files, so it works with no registry reachable — drop
|
|
273
|
+
it on **Settings → Connectors** and Vorn launches it from disk.
|
|
274
|
+
|
|
260
275
|
### Ship an icon
|
|
261
276
|
|
|
262
277
|
Without one, a connector shows the generic MCP glyph and is hard to pick out
|
|
@@ -290,9 +305,12 @@ vorn-connector manifest <module> Print the manifest as JSON
|
|
|
290
305
|
vorn-connector setup <module> [trigger] Print the Vorn connection settings
|
|
291
306
|
vorn-connector poll <module> <trigger> Run one poll against the environment
|
|
292
307
|
vorn-connector check <module> Verify the connector against the contract
|
|
308
|
+
vorn-connector pack <module> Build an installable .vorn.tgz pack
|
|
293
309
|
vorn-connector serve <module> Serve on stdio (what Vorn runs)
|
|
294
310
|
```
|
|
295
311
|
|
|
312
|
+
`pack` accepts `--out <dir>`.
|
|
313
|
+
|
|
296
314
|
`poll` accepts `--since <iso>` and `--limit <n>`, and reads the connector's
|
|
297
315
|
declared config from your shell environment — the fastest way to confirm
|
|
298
316
|
credentials and field mapping before wiring anything up.
|
|
@@ -600,6 +600,147 @@ function connectorManifest(connector) {
|
|
|
600
600
|
};
|
|
601
601
|
}
|
|
602
602
|
|
|
603
|
+
// src/pack.ts
|
|
604
|
+
import { builtinModules } from "module";
|
|
605
|
+
import { mkdtemp, mkdir, rm, stat, writeFile } from "fs/promises";
|
|
606
|
+
import { readFileSync } from "fs";
|
|
607
|
+
import { tmpdir } from "os";
|
|
608
|
+
import { dirname, isAbsolute, join, resolve } from "path";
|
|
609
|
+
var MAX_PACK_BYTES = 8 * 1024 * 1024;
|
|
610
|
+
var LIFECYCLE_SCRIPTS = [
|
|
611
|
+
"preinstall",
|
|
612
|
+
"install",
|
|
613
|
+
"postinstall",
|
|
614
|
+
"prepare",
|
|
615
|
+
"prepublish",
|
|
616
|
+
"prepublishOnly",
|
|
617
|
+
"postpublish"
|
|
618
|
+
];
|
|
619
|
+
var BUILTINS = new Set(builtinModules);
|
|
620
|
+
function finding2(code, target, message) {
|
|
621
|
+
return { level: "error", code, target, message };
|
|
622
|
+
}
|
|
623
|
+
function lifecycleScriptFindings(pkg) {
|
|
624
|
+
const scripts = pkg?.scripts;
|
|
625
|
+
if (!scripts || typeof scripts !== "object") return [];
|
|
626
|
+
const named = LIFECYCLE_SCRIPTS.filter((name) => typeof scripts[name] === "string");
|
|
627
|
+
if (named.length === 0) return [];
|
|
628
|
+
return [
|
|
629
|
+
finding2(
|
|
630
|
+
"lifecycle-scripts",
|
|
631
|
+
"package.json",
|
|
632
|
+
`Remove the ${named.join(", ")} script(s); a pack is installed by copying files, never by running them`
|
|
633
|
+
)
|
|
634
|
+
];
|
|
635
|
+
}
|
|
636
|
+
function bundleDependencyFindings(external) {
|
|
637
|
+
const specifiers = /* @__PURE__ */ new Set();
|
|
638
|
+
for (const specifier of external) {
|
|
639
|
+
if (specifier.startsWith(".") || specifier.startsWith("/")) continue;
|
|
640
|
+
if (specifier.startsWith("node:") || BUILTINS.has(specifier)) continue;
|
|
641
|
+
specifiers.add(specifier);
|
|
642
|
+
}
|
|
643
|
+
if (specifiers.size === 0) return [];
|
|
644
|
+
return [
|
|
645
|
+
finding2(
|
|
646
|
+
"runtime-dependencies",
|
|
647
|
+
"bundle",
|
|
648
|
+
`${[...specifiers].sort().join(", ")} stayed outside the bundle; a pack must launch with no install step`
|
|
649
|
+
)
|
|
650
|
+
];
|
|
651
|
+
}
|
|
652
|
+
function readNearestPackageJson(fromDir) {
|
|
653
|
+
let current = resolve(fromDir);
|
|
654
|
+
for (; ; ) {
|
|
655
|
+
try {
|
|
656
|
+
return JSON.parse(readFileSync(join(current, "package.json"), "utf8"));
|
|
657
|
+
} catch {
|
|
658
|
+
const parent = dirname(current);
|
|
659
|
+
if (parent === current) return void 0;
|
|
660
|
+
current = parent;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
async function esbuildBundle(request) {
|
|
665
|
+
const { build } = await import("esbuild");
|
|
666
|
+
const result = await build({
|
|
667
|
+
stdin: {
|
|
668
|
+
contents: request.contents,
|
|
669
|
+
resolveDir: request.resolveDir,
|
|
670
|
+
sourcefile: "vorn-connector-pack.js",
|
|
671
|
+
loader: "js"
|
|
672
|
+
},
|
|
673
|
+
bundle: true,
|
|
674
|
+
platform: "node",
|
|
675
|
+
target: "node20",
|
|
676
|
+
format: "esm",
|
|
677
|
+
write: false,
|
|
678
|
+
metafile: true,
|
|
679
|
+
legalComments: "none"
|
|
680
|
+
});
|
|
681
|
+
const output = Object.values(result.metafile.outputs)[0];
|
|
682
|
+
return {
|
|
683
|
+
code: result.outputFiles[0].text,
|
|
684
|
+
external: (output?.imports ?? []).filter((item) => item.external).map((item) => item.path)
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
function packFileName(connector) {
|
|
688
|
+
return `${connector.id}-${connector.version}.vorn.tgz`;
|
|
689
|
+
}
|
|
690
|
+
async function packConnector(connector, options) {
|
|
691
|
+
const resolveDir = resolve(options.resolveDir ?? process.cwd());
|
|
692
|
+
const entryDir = options.entry.startsWith(".") || isAbsolute(options.entry) ? dirname(resolve(resolveDir, options.entry)) : resolveDir;
|
|
693
|
+
const findings = await checkConnector(connector);
|
|
694
|
+
findings.push(...lifecycleScriptFindings(readNearestPackageJson(entryDir)));
|
|
695
|
+
if (findings.some((item) => item.level === "error")) return { findings };
|
|
696
|
+
const sdkModule = options.sdkModule ?? "@vornrun/connector-sdk";
|
|
697
|
+
const contents = [
|
|
698
|
+
`import { serveConnector } from ${JSON.stringify(sdkModule)}`,
|
|
699
|
+
`import * as entry from ${JSON.stringify(options.entry)}`,
|
|
700
|
+
"const exported = Object.values(entry).find((value) => value && Array.isArray(value.triggers))",
|
|
701
|
+
`if (!exported) throw new Error(${JSON.stringify(`${options.entry} exports no connector`)})`,
|
|
702
|
+
"await serveConnector(exported)",
|
|
703
|
+
""
|
|
704
|
+
].join("\n");
|
|
705
|
+
const bundle = options.bundle ?? esbuildBundle;
|
|
706
|
+
const built = await bundle({ contents, resolveDir });
|
|
707
|
+
findings.push(...bundleDependencyFindings(built.external));
|
|
708
|
+
if (findings.some((item) => item.level === "error")) return { findings };
|
|
709
|
+
const outDir = resolve(options.outDir ?? process.cwd());
|
|
710
|
+
await mkdir(outDir, { recursive: true });
|
|
711
|
+
const file = join(outDir, packFileName(connector));
|
|
712
|
+
const staging = await mkdtemp(join(tmpdir(), "vorn-pack-"));
|
|
713
|
+
try {
|
|
714
|
+
await writeFile(join(staging, "index.js"), built.code, "utf8");
|
|
715
|
+
await writeFile(
|
|
716
|
+
join(staging, "manifest.json"),
|
|
717
|
+
`${JSON.stringify(connectorManifest(connector), null, 2)}
|
|
718
|
+
`,
|
|
719
|
+
"utf8"
|
|
720
|
+
);
|
|
721
|
+
const { create } = await import("tar");
|
|
722
|
+
await create({ gzip: true, file, cwd: staging }, ["manifest.json", "index.js"]);
|
|
723
|
+
} finally {
|
|
724
|
+
await rm(staging, { recursive: true, force: true });
|
|
725
|
+
}
|
|
726
|
+
const bytes = (await stat(file)).size;
|
|
727
|
+
const maxBytes = options.maxBytes ?? MAX_PACK_BYTES;
|
|
728
|
+
if (bytes > maxBytes) {
|
|
729
|
+
await rm(file, { force: true });
|
|
730
|
+
return {
|
|
731
|
+
findings: [
|
|
732
|
+
...findings,
|
|
733
|
+
finding2(
|
|
734
|
+
"pack-too-large",
|
|
735
|
+
"bundle",
|
|
736
|
+
`The pack is ${Math.round(bytes / 1024)} KB; Vorn installs at most ${Math.round(maxBytes / 1024)} KB`
|
|
737
|
+
)
|
|
738
|
+
]
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
return { findings, file, bytes };
|
|
742
|
+
}
|
|
743
|
+
|
|
603
744
|
// src/server.ts
|
|
604
745
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
605
746
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -725,6 +866,8 @@ function createConnectorServer(connector, options = {}) {
|
|
|
725
866
|
server.registerTool(
|
|
726
867
|
action.type,
|
|
727
868
|
{
|
|
869
|
+
// Carries the authored label, so a picker can name the action rather than its tool.
|
|
870
|
+
title: action.label,
|
|
728
871
|
description: `${base}${retryHint}`,
|
|
729
872
|
inputSchema: inputShape(action.inputs ?? []),
|
|
730
873
|
outputSchema: outputSchema(action.outputs ?? [])
|
|
@@ -768,6 +911,12 @@ export {
|
|
|
768
911
|
PREFLIGHT_TOOL,
|
|
769
912
|
connectionSetup,
|
|
770
913
|
connectorManifest,
|
|
914
|
+
MAX_PACK_BYTES,
|
|
915
|
+
lifecycleScriptFindings,
|
|
916
|
+
bundleDependencyFindings,
|
|
917
|
+
readNearestPackageJson,
|
|
918
|
+
packFileName,
|
|
919
|
+
packConnector,
|
|
771
920
|
createConnectorServer,
|
|
772
921
|
serveConnector
|
|
773
922
|
};
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { B as BundleRequest, a as BundleOutput } from './pack-C3ZJx9d4.js';
|
|
3
|
+
|
|
2
4
|
interface CliDeps {
|
|
3
5
|
load(modulePath: string): Promise<unknown>;
|
|
4
6
|
write(line: string): void;
|
|
5
7
|
env?: NodeJS.ProcessEnv;
|
|
8
|
+
/** Directory module paths resolve from; defaults to the working directory. */
|
|
9
|
+
cwd?: string;
|
|
10
|
+
/** Replaced in tests so pack does not shell out to a bundler. */
|
|
11
|
+
bundle?(request: BundleRequest): Promise<BundleOutput>;
|
|
6
12
|
}
|
|
7
13
|
declare function runCli(argv: string[], deps: CliDeps): Promise<number>;
|
|
8
14
|
|
package/dist/cli.js
CHANGED
|
@@ -4,10 +4,11 @@ import {
|
|
|
4
4
|
connectionSetup,
|
|
5
5
|
connectorManifest,
|
|
6
6
|
formatFindings,
|
|
7
|
+
packConnector,
|
|
7
8
|
resolveConfig,
|
|
8
9
|
runPoll,
|
|
9
10
|
serveConnector
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NXZBUV63.js";
|
|
11
12
|
|
|
12
13
|
// src/cli.ts
|
|
13
14
|
import { pathToFileURL } from "url";
|
|
@@ -18,13 +19,15 @@ Commands:
|
|
|
18
19
|
manifest <module> Print the connector manifest as JSON
|
|
19
20
|
setup <module> [trigger] Print the Vorn connection settings to paste
|
|
20
21
|
check <module> Verify the connector against Vorn's contract
|
|
22
|
+
pack <module> Build an installable .vorn.tgz pack
|
|
21
23
|
poll <module> <trigger> Run one poll against the current environment
|
|
22
24
|
serve <module> Serve the connector on stdio (what Vorn runs)
|
|
23
25
|
|
|
24
26
|
Options:
|
|
25
27
|
--since <iso> Lower bound passed to poll
|
|
26
28
|
--limit <n> Maximum items to request
|
|
27
|
-
--live Let check poll for real using the environment
|
|
29
|
+
--live Let check poll for real using the environment
|
|
30
|
+
--out <dir> Directory pack writes the archive to`;
|
|
28
31
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["live"]);
|
|
29
32
|
function parseArgs(args) {
|
|
30
33
|
const flags = {};
|
|
@@ -110,6 +113,26 @@ ${connector.id} passed with ${findings.length} warning(s)`
|
|
|
110
113
|
);
|
|
111
114
|
return errors.length > 0 ? 1 : 0;
|
|
112
115
|
}
|
|
116
|
+
case "pack": {
|
|
117
|
+
const result = await packConnector(connector, {
|
|
118
|
+
entry: modulePath,
|
|
119
|
+
...flags.out !== void 0 && { outDir: flags.out },
|
|
120
|
+
...deps.cwd !== void 0 && { resolveDir: deps.cwd },
|
|
121
|
+
...deps.bundle !== void 0 && { bundle: deps.bundle }
|
|
122
|
+
});
|
|
123
|
+
if (result.findings.length > 0) deps.write(formatFindings(result.findings));
|
|
124
|
+
const errors = result.findings.filter((item) => item.level === "error");
|
|
125
|
+
if (!result.file) {
|
|
126
|
+
deps.write(`
|
|
127
|
+
${errors.length} error(s) \u2014 nothing was packed`);
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
deps.write(
|
|
131
|
+
`
|
|
132
|
+
Packed ${connector.id} ${connector.version} to ${result.file} (${Math.max(1, Math.round((result.bytes ?? 0) / 1024))} KB)`
|
|
133
|
+
);
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
113
136
|
case "poll": {
|
|
114
137
|
const triggerType = positional[0];
|
|
115
138
|
if (!triggerType) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,272 +1,7 @@
|
|
|
1
|
+
import { C as ConnectorDefinition, b as Connector, c as ConnectorConfig, T as TriggerDefinition, P as PollContext, d as PollOutcome, e as ConnectorItem, N as NormalizedItem, f as ConnectorIcon, S as StatusSuggestion, D as DefaultWorkflow } from './pack-C3ZJx9d4.js';
|
|
2
|
+
export { A as ActionContext, g as ActionDefinition, h as ActionInputField, a as BundleOutput, B as BundleRequest, i as CheckFinding, j as CheckOptions, k as ConnectorConfigField, l as DedupeStrategy, F as FetchContext, M as MAX_PACK_BYTES, m as PackOptions, n as PackResult, o as PreflightResult, p as bundleDependencyFindings, q as checkConnector, r as formatFindings, s as lifecycleScriptFindings, t as packConnector, u as packFileName, v as readNearestPackageJson } from './pack-C3ZJx9d4.js';
|
|
1
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
4
|
|
|
3
|
-
/**
|
|
4
|
-
* Author-facing types for Vorn connectors.
|
|
5
|
-
*
|
|
6
|
-
* A connector written with this SDK runs as an ordinary MCP stdio server, so
|
|
7
|
-
* it is shared as a normal npm package and installed by pointing a Vorn
|
|
8
|
-
* connection at `npx -y <package>`. Nothing about the host app has to change
|
|
9
|
-
* to accept a new connector.
|
|
10
|
-
*/
|
|
11
|
-
/** A raw item as the author's code returns it. Only id and title are required. */
|
|
12
|
-
interface ConnectorItem {
|
|
13
|
-
/** Stable upstream identity. Vorn dedupes on this, so it must not change. */
|
|
14
|
-
externalId: string | number;
|
|
15
|
-
title: string;
|
|
16
|
-
url?: string;
|
|
17
|
-
description?: string;
|
|
18
|
-
/** Raw upstream status (`open`, `Active`, `In Progress`, …). */
|
|
19
|
-
status?: string;
|
|
20
|
-
labels?: string[];
|
|
21
|
-
assignee?: string;
|
|
22
|
-
/**
|
|
23
|
-
* When the item last changed. Vorn advances its poll cursor from this field,
|
|
24
|
-
* so it must be monotonic per item and comparable as an ISO 8601 string.
|
|
25
|
-
* Defaults to poll time when omitted.
|
|
26
|
-
*/
|
|
27
|
-
updatedAt?: string | Date;
|
|
28
|
-
/** Extra fields to expose to workflow templates as `{{trigger.item.<key>}}`. */
|
|
29
|
-
data?: Record<string, unknown>;
|
|
30
|
-
}
|
|
31
|
-
/** A connector item after normalization. This is the exact JSON Vorn sees. */
|
|
32
|
-
interface NormalizedItem extends Record<string, unknown> {
|
|
33
|
-
externalId: string;
|
|
34
|
-
title: string;
|
|
35
|
-
url: string;
|
|
36
|
-
description: string;
|
|
37
|
-
status: string;
|
|
38
|
-
labels: string[];
|
|
39
|
-
updatedAt: string;
|
|
40
|
-
assignee?: string;
|
|
41
|
-
}
|
|
42
|
-
/** Declares a value the connector needs at runtime, read from the environment. */
|
|
43
|
-
interface ConnectorConfigField {
|
|
44
|
-
key: string;
|
|
45
|
-
label: string;
|
|
46
|
-
/** Environment variable the value is read from. Defaults to CONSTANT_CASE(key). */
|
|
47
|
-
env?: string;
|
|
48
|
-
required?: boolean;
|
|
49
|
-
/** Secrets are stored encrypted by Vorn and never printed by the CLI. */
|
|
50
|
-
secret?: boolean;
|
|
51
|
-
description?: string;
|
|
52
|
-
default?: string;
|
|
53
|
-
}
|
|
54
|
-
type ConnectorConfig = Record<string, string | undefined>;
|
|
55
|
-
interface PollContext {
|
|
56
|
-
config: ConnectorConfig;
|
|
57
|
-
/**
|
|
58
|
-
* Lower bound the host asked for, when it was able to supply one. Treat it
|
|
59
|
-
* as a hint: returning older items is safe because Vorn dedupes, but
|
|
60
|
-
* returning fewer than everything after `since` loses events.
|
|
61
|
-
*/
|
|
62
|
-
since?: string;
|
|
63
|
-
/** Opaque cursor previously returned by this trigger, when supplied. */
|
|
64
|
-
cursor?: string;
|
|
65
|
-
/** Upper bound on items to return in one page. */
|
|
66
|
-
limit?: number;
|
|
67
|
-
/** Injectable clock so tests are deterministic. */
|
|
68
|
-
now(): string;
|
|
69
|
-
}
|
|
70
|
-
interface PollOutcome {
|
|
71
|
-
items: ConnectorItem[];
|
|
72
|
-
nextCursor?: string;
|
|
73
|
-
hasMore?: boolean;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* How the SDK decides which fetched items are new.
|
|
77
|
-
*
|
|
78
|
-
* - `timestamp` — for sources that expose a reliable "last changed" field and
|
|
79
|
-
* can filter on it. Handles the boundary case where several items share the
|
|
80
|
-
* newest timestamp, which is the classic source of both duplicates and
|
|
81
|
-
* silently dropped items.
|
|
82
|
-
* - `lastItem` — for feeds that return newest-first with no dependable
|
|
83
|
-
* timestamp. The cursor is the newest id already delivered.
|
|
84
|
-
*/
|
|
85
|
-
type DedupeStrategy = 'timestamp' | 'lastItem';
|
|
86
|
-
/**
|
|
87
|
-
* What a declarative trigger's `fetch` receives. Deliberately smaller than
|
|
88
|
-
* {@link PollContext}: cursor encoding, ordering, windowing and de-duplication
|
|
89
|
-
* are the SDK's job, so the author only has to answer "what is there now?".
|
|
90
|
-
*/
|
|
91
|
-
interface FetchContext {
|
|
92
|
-
config: ConnectorConfig;
|
|
93
|
-
/**
|
|
94
|
-
* With `dedupe: 'timestamp'`, everything changed at or after this instant is
|
|
95
|
-
* worth returning. Absent on the very first poll. Returning a little too
|
|
96
|
-
* much is safe — the SDK drops what was already delivered.
|
|
97
|
-
*/
|
|
98
|
-
since?: string;
|
|
99
|
-
/**
|
|
100
|
-
* With `dedupe: 'lastItem'`, the newest id already delivered. Absent on the
|
|
101
|
-
* very first poll. Return the feed newest-first and the SDK will stop there.
|
|
102
|
-
*/
|
|
103
|
-
lastItemId?: string;
|
|
104
|
-
/** Upper bound on items worth returning in one call. */
|
|
105
|
-
limit?: number;
|
|
106
|
-
/** Injectable clock so tests are deterministic. */
|
|
107
|
-
now(): string;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* What an upstream state should become when an item is imported as a task.
|
|
111
|
-
*
|
|
112
|
-
* A suggestion, not a rule: it seeds the connection form, and the person
|
|
113
|
-
* setting it up can change it. Without any, everything a connector imports
|
|
114
|
-
* lands as `todo` regardless of whether it was closed a year ago.
|
|
115
|
-
*/
|
|
116
|
-
interface StatusSuggestion {
|
|
117
|
-
/** The value the connector reports in `ConnectorItem.status`. */
|
|
118
|
-
upstream: string;
|
|
119
|
-
suggestedLocal: 'todo' | 'in_progress' | 'in_review' | 'done' | 'cancelled';
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* The workflow to create when a connection is made.
|
|
123
|
-
*
|
|
124
|
-
* A connector that fires on a schedule is useless until something polls it, and
|
|
125
|
-
* expecting every person to build that workflow by hand is how a connection
|
|
126
|
-
* ends up configured and silent. Seeded workflows are ordinary, visible and
|
|
127
|
-
* editable — the schedule is a starting point, not a fixed rule.
|
|
128
|
-
*/
|
|
129
|
-
interface DefaultWorkflow {
|
|
130
|
-
name: string;
|
|
131
|
-
defaultCronFromMinutes: number;
|
|
132
|
-
}
|
|
133
|
-
interface TriggerBase {
|
|
134
|
-
/** Event key, e.g. `workItemCreated`. Becomes the `poll_<type>` MCP tool. */
|
|
135
|
-
type: string;
|
|
136
|
-
label: string;
|
|
137
|
-
description?: string;
|
|
138
|
-
/** Seeds the connection's status mapping; the person setting it up owns it. */
|
|
139
|
-
statusMapping?: StatusSuggestion[];
|
|
140
|
-
/** Seeds a polling workflow when a connection is created. */
|
|
141
|
-
defaultWorkflow?: DefaultWorkflow;
|
|
142
|
-
/**
|
|
143
|
-
* Representative items. `vorn-connector check` replays these through the
|
|
144
|
-
* real dedupe pipeline, so a connector can be verified before anyone has
|
|
145
|
-
* credentials for it.
|
|
146
|
-
*/
|
|
147
|
-
sample?: ConnectorItem[];
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* A trigger is either declarative or hand-written, never both — expressed as a
|
|
151
|
-
* union so the invalid combinations are a type error at authoring time rather
|
|
152
|
-
* than a throw when the connector is first loaded.
|
|
153
|
-
*/
|
|
154
|
-
type TriggerDefinition = TriggerBase & ({
|
|
155
|
-
/**
|
|
156
|
-
* Declarative polling: return what the source has and let the SDK
|
|
157
|
-
* handle cursors and de-duplication.
|
|
158
|
-
*/
|
|
159
|
-
dedupe: DedupeStrategy;
|
|
160
|
-
fetch(context: FetchContext): Promise<ConnectorItem[]> | ConnectorItem[];
|
|
161
|
-
poll?: never;
|
|
162
|
-
} | {
|
|
163
|
-
/**
|
|
164
|
-
* Full control over cursors and paging. Use only when the source's
|
|
165
|
-
* paging cannot be expressed as "give me everything since X".
|
|
166
|
-
*/
|
|
167
|
-
poll(context: PollContext): Promise<PollOutcome> | PollOutcome;
|
|
168
|
-
dedupe?: never;
|
|
169
|
-
fetch?: never;
|
|
170
|
-
});
|
|
171
|
-
interface ActionInputField {
|
|
172
|
-
key: string;
|
|
173
|
-
label: string;
|
|
174
|
-
type?: 'string' | 'number' | 'boolean';
|
|
175
|
-
required?: boolean;
|
|
176
|
-
description?: string;
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* A field the action is known to return. Declaring these is optional — extra
|
|
180
|
-
* keys always pass through — but declared fields show up in Vorn's variable
|
|
181
|
-
* autocomplete as `{{steps.<action>.<key>}}`.
|
|
182
|
-
*/
|
|
183
|
-
interface ActionOutputField {
|
|
184
|
-
key: string;
|
|
185
|
-
type?: 'string' | 'number' | 'boolean';
|
|
186
|
-
description?: string;
|
|
187
|
-
}
|
|
188
|
-
interface ActionContext {
|
|
189
|
-
config: ConnectorConfig;
|
|
190
|
-
now(): string;
|
|
191
|
-
}
|
|
192
|
-
interface ActionDefinition {
|
|
193
|
-
/** Action key, e.g. `closeWorkItem`. Becomes an MCP tool of the same name. */
|
|
194
|
-
type: string;
|
|
195
|
-
label: string;
|
|
196
|
-
description?: string;
|
|
197
|
-
/**
|
|
198
|
-
* Whether repeating the call with the same arguments is safe. Surfaced in
|
|
199
|
-
* the MCP tool description, because an agent retrying a failed step has no
|
|
200
|
-
* other way to know whether it is about to create a second issue.
|
|
201
|
-
*/
|
|
202
|
-
idempotent?: boolean;
|
|
203
|
-
inputs?: ActionInputField[];
|
|
204
|
-
outputs?: ActionOutputField[];
|
|
205
|
-
run(args: Record<string, unknown>, context: ActionContext): Promise<Record<string, unknown> | void> | Record<string, unknown> | void;
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* A connector's own glyph, so an installed connector is recognizable in a list
|
|
209
|
-
* rather than sharing one generic icon with every other one.
|
|
210
|
-
*
|
|
211
|
-
* Path data only — deliberately not markup. Vorn draws these itself as
|
|
212
|
-
* `<path d="...">` inside an `<svg>` it owns, so a connector cannot inject
|
|
213
|
-
* elements, scripts or external references into the app rendering it.
|
|
214
|
-
*/
|
|
215
|
-
interface ConnectorIcon {
|
|
216
|
-
/** Defaults to `0 0 24 24`. */
|
|
217
|
-
viewBox?: string;
|
|
218
|
-
/** SVG path `d` data, drawn with `fill="currentColor"` so it inherits color. */
|
|
219
|
-
paths: string[];
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* What a connector reports about its own readiness.
|
|
223
|
-
*
|
|
224
|
-
* `message` is shown to the user verbatim, so it should say what to do rather
|
|
225
|
-
* than what went wrong — "run `gh auth login`" beats "not authenticated".
|
|
226
|
-
*/
|
|
227
|
-
interface PreflightResult {
|
|
228
|
-
ok: boolean;
|
|
229
|
-
message?: string;
|
|
230
|
-
}
|
|
231
|
-
interface ConnectorDefinition {
|
|
232
|
-
/** Stable connector id, e.g. `azure-devops`. */
|
|
233
|
-
id: string;
|
|
234
|
-
name: string;
|
|
235
|
-
version?: string;
|
|
236
|
-
description?: string;
|
|
237
|
-
icon?: ConnectorIcon;
|
|
238
|
-
config?: ConnectorConfigField[];
|
|
239
|
-
triggers?: TriggerDefinition[];
|
|
240
|
-
actions?: ActionDefinition[];
|
|
241
|
-
/**
|
|
242
|
-
* Whether this connector could work right now, asked before anyone waits on
|
|
243
|
-
* a poll.
|
|
244
|
-
*
|
|
245
|
-
* A connector whose credentials come from config fields does not need this:
|
|
246
|
-
* a missing field is already a visible, nameable error. One that borrows an
|
|
247
|
-
* external tool's login — `gh auth login`, `az login` — has no field to be
|
|
248
|
-
* missing, so without this the first sign that the tool is absent or signed
|
|
249
|
-
* out is a poll failing some minutes after the connection was saved.
|
|
250
|
-
*
|
|
251
|
-
* Answer `ok: false` with a message saying what to do about it. Throwing is
|
|
252
|
-
* equivalent — the server catches it and reports the same shape with the
|
|
253
|
-
* error's message — so there is one result for a caller to read and no
|
|
254
|
-
* behaviour riding on which you choose. Prefer returning when the state is
|
|
255
|
-
* one you recognise, because then you get to write the sentence.
|
|
256
|
-
*
|
|
257
|
-
* Absent means there is nothing to check, which is not the same answer as a
|
|
258
|
-
* check that passed.
|
|
259
|
-
*/
|
|
260
|
-
preflight?(): Promise<PreflightResult> | PreflightResult;
|
|
261
|
-
}
|
|
262
|
-
/** A validated definition. Every accessor below is guaranteed non-null. */
|
|
263
|
-
interface Connector extends ConnectorDefinition {
|
|
264
|
-
readonly version: string;
|
|
265
|
-
readonly config: ConnectorConfigField[];
|
|
266
|
-
readonly triggers: TriggerDefinition[];
|
|
267
|
-
readonly actions: ActionDefinition[];
|
|
268
|
-
}
|
|
269
|
-
|
|
270
5
|
/** Environment variable a config field reads from, e.g. `apiToken` → `API_TOKEN`. */
|
|
271
6
|
declare function envNameFor(key: string, explicit?: string): string;
|
|
272
7
|
/**
|
|
@@ -285,36 +20,6 @@ declare function defineConnector(definition: ConnectorDefinition): Connector;
|
|
|
285
20
|
*/
|
|
286
21
|
declare function resolveConfig(connector: Connector, env?: NodeJS.ProcessEnv): ConnectorConfig;
|
|
287
22
|
|
|
288
|
-
interface CheckFinding {
|
|
289
|
-
/** `error` means the connector will misbehave in Vorn; `warn` is advisory. */
|
|
290
|
-
level: 'error' | 'warn';
|
|
291
|
-
code: string;
|
|
292
|
-
/** Which part of the connector the finding is about. */
|
|
293
|
-
target: string;
|
|
294
|
-
message: string;
|
|
295
|
-
}
|
|
296
|
-
interface CheckOptions {
|
|
297
|
-
/**
|
|
298
|
-
* Poll every trigger against the real source. Off by default, so a check
|
|
299
|
-
* runs on declared `sample` items and the definition alone.
|
|
300
|
-
*/
|
|
301
|
-
live?: boolean;
|
|
302
|
-
/** Credentials, required by `live`. */
|
|
303
|
-
config?: ConnectorConfig;
|
|
304
|
-
now?: () => string;
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Check a connector against the contract Vorn relies on.
|
|
308
|
-
*
|
|
309
|
-
* The point is a feedback loop: a connector — hand-written or generated — can
|
|
310
|
-
* be verified before it is ever installed, catching the failures that are
|
|
311
|
-
* otherwise invisible until duplicate tasks show up in someone's inbox days
|
|
312
|
-
* later.
|
|
313
|
-
*/
|
|
314
|
-
declare function checkConnector(connector: Connector, options?: CheckOptions): Promise<CheckFinding[]>;
|
|
315
|
-
/** Render findings for a terminal. Returns an empty string when all clear. */
|
|
316
|
-
declare function formatFindings(findings: CheckFinding[]): string;
|
|
317
|
-
|
|
318
23
|
/**
|
|
319
24
|
* Run a declarative trigger: call the author's `fetch`, then apply the chosen
|
|
320
25
|
* dedupe strategy.
|
|
@@ -491,4 +196,4 @@ interface ConnectorHarness {
|
|
|
491
196
|
*/
|
|
492
197
|
declare function createConnectorHarness(connector: Connector, harnessOptions?: HarnessOptions): ConnectorHarness;
|
|
493
198
|
|
|
494
|
-
export { type
|
|
199
|
+
export { type ConnectionSetup, Connector, ConnectorConfig, ConnectorDefinition, type ConnectorHarness, ConnectorIcon, ConnectorItem, type ConnectorManifest, type ConnectorServerOptions, DefaultWorkflow, type HarnessOptions, MANIFEST_TOOL, MAX_POLL_PAGES, NormalizedItem, PREFLIGHT_TOOL, PollContext, PollOutcome, type PollPage, type RunActionOptions, type RunPollOptions, StatusSuggestion, TriggerDefinition, connectionSetup, connectorManifest, createConnectorHarness, createConnectorServer, defineConnector, drainPoll, envNameFor, normalizeItem, normalizeItems, pollToolName, pollWithDedupe, resolveConfig, runAction, runPoll, serveConnector };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MANIFEST_TOOL,
|
|
3
|
+
MAX_PACK_BYTES,
|
|
3
4
|
MAX_POLL_PAGES,
|
|
4
5
|
PREFLIGHT_TOOL,
|
|
6
|
+
bundleDependencyFindings,
|
|
5
7
|
checkConnector,
|
|
6
8
|
connectionSetup,
|
|
7
9
|
connectorManifest,
|
|
@@ -10,15 +12,19 @@ import {
|
|
|
10
12
|
drainPoll,
|
|
11
13
|
envNameFor,
|
|
12
14
|
formatFindings,
|
|
15
|
+
lifecycleScriptFindings,
|
|
13
16
|
normalizeItem,
|
|
14
17
|
normalizeItems,
|
|
18
|
+
packConnector,
|
|
19
|
+
packFileName,
|
|
15
20
|
pollToolName,
|
|
16
21
|
pollWithDedupe,
|
|
22
|
+
readNearestPackageJson,
|
|
17
23
|
resolveConfig,
|
|
18
24
|
runAction,
|
|
19
25
|
runPoll,
|
|
20
26
|
serveConnector
|
|
21
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-NXZBUV63.js";
|
|
22
28
|
|
|
23
29
|
// src/harness.ts
|
|
24
30
|
function createConnectorHarness(connector, harnessOptions = {}) {
|
|
@@ -52,8 +58,10 @@ function createConnectorHarness(connector, harnessOptions = {}) {
|
|
|
52
58
|
}
|
|
53
59
|
export {
|
|
54
60
|
MANIFEST_TOOL,
|
|
61
|
+
MAX_PACK_BYTES,
|
|
55
62
|
MAX_POLL_PAGES,
|
|
56
63
|
PREFLIGHT_TOOL,
|
|
64
|
+
bundleDependencyFindings,
|
|
57
65
|
checkConnector,
|
|
58
66
|
connectionSetup,
|
|
59
67
|
connectorManifest,
|
|
@@ -63,10 +71,14 @@ export {
|
|
|
63
71
|
drainPoll,
|
|
64
72
|
envNameFor,
|
|
65
73
|
formatFindings,
|
|
74
|
+
lifecycleScriptFindings,
|
|
66
75
|
normalizeItem,
|
|
67
76
|
normalizeItems,
|
|
77
|
+
packConnector,
|
|
78
|
+
packFileName,
|
|
68
79
|
pollToolName,
|
|
69
80
|
pollWithDedupe,
|
|
81
|
+
readNearestPackageJson,
|
|
70
82
|
resolveConfig,
|
|
71
83
|
runAction,
|
|
72
84
|
runPoll,
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Author-facing types for Vorn connectors.
|
|
3
|
+
*
|
|
4
|
+
* A connector written with this SDK runs as an ordinary MCP stdio server, so
|
|
5
|
+
* it is shared as a normal npm package and installed by pointing a Vorn
|
|
6
|
+
* connection at `npx -y <package>`. Nothing about the host app has to change
|
|
7
|
+
* to accept a new connector.
|
|
8
|
+
*/
|
|
9
|
+
/** A raw item as the author's code returns it. Only id and title are required. */
|
|
10
|
+
interface ConnectorItem {
|
|
11
|
+
/** Stable upstream identity. Vorn dedupes on this, so it must not change. */
|
|
12
|
+
externalId: string | number;
|
|
13
|
+
title: string;
|
|
14
|
+
url?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
/** Raw upstream status (`open`, `Active`, `In Progress`, …). */
|
|
17
|
+
status?: string;
|
|
18
|
+
labels?: string[];
|
|
19
|
+
assignee?: string;
|
|
20
|
+
/**
|
|
21
|
+
* When the item last changed. Vorn advances its poll cursor from this field,
|
|
22
|
+
* so it must be monotonic per item and comparable as an ISO 8601 string.
|
|
23
|
+
* Defaults to poll time when omitted.
|
|
24
|
+
*/
|
|
25
|
+
updatedAt?: string | Date;
|
|
26
|
+
/** Extra fields to expose to workflow templates as `{{trigger.item.<key>}}`. */
|
|
27
|
+
data?: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
/** A connector item after normalization. This is the exact JSON Vorn sees. */
|
|
30
|
+
interface NormalizedItem extends Record<string, unknown> {
|
|
31
|
+
externalId: string;
|
|
32
|
+
title: string;
|
|
33
|
+
url: string;
|
|
34
|
+
description: string;
|
|
35
|
+
status: string;
|
|
36
|
+
labels: string[];
|
|
37
|
+
updatedAt: string;
|
|
38
|
+
assignee?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Declares a value the connector needs at runtime, read from the environment. */
|
|
41
|
+
interface ConnectorConfigField {
|
|
42
|
+
key: string;
|
|
43
|
+
label: string;
|
|
44
|
+
/** Environment variable the value is read from. Defaults to CONSTANT_CASE(key). */
|
|
45
|
+
env?: string;
|
|
46
|
+
required?: boolean;
|
|
47
|
+
/** Secrets are stored encrypted by Vorn and never printed by the CLI. */
|
|
48
|
+
secret?: boolean;
|
|
49
|
+
description?: string;
|
|
50
|
+
default?: string;
|
|
51
|
+
}
|
|
52
|
+
type ConnectorConfig = Record<string, string | undefined>;
|
|
53
|
+
interface PollContext {
|
|
54
|
+
config: ConnectorConfig;
|
|
55
|
+
/**
|
|
56
|
+
* Lower bound the host asked for, when it was able to supply one. Treat it
|
|
57
|
+
* as a hint: returning older items is safe because Vorn dedupes, but
|
|
58
|
+
* returning fewer than everything after `since` loses events.
|
|
59
|
+
*/
|
|
60
|
+
since?: string;
|
|
61
|
+
/** Opaque cursor previously returned by this trigger, when supplied. */
|
|
62
|
+
cursor?: string;
|
|
63
|
+
/** Upper bound on items to return in one page. */
|
|
64
|
+
limit?: number;
|
|
65
|
+
/** Injectable clock so tests are deterministic. */
|
|
66
|
+
now(): string;
|
|
67
|
+
}
|
|
68
|
+
interface PollOutcome {
|
|
69
|
+
items: ConnectorItem[];
|
|
70
|
+
nextCursor?: string;
|
|
71
|
+
hasMore?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* How the SDK decides which fetched items are new.
|
|
75
|
+
*
|
|
76
|
+
* - `timestamp` — for sources that expose a reliable "last changed" field and
|
|
77
|
+
* can filter on it. Handles the boundary case where several items share the
|
|
78
|
+
* newest timestamp, which is the classic source of both duplicates and
|
|
79
|
+
* silently dropped items.
|
|
80
|
+
* - `lastItem` — for feeds that return newest-first with no dependable
|
|
81
|
+
* timestamp. The cursor is the newest id already delivered.
|
|
82
|
+
*/
|
|
83
|
+
type DedupeStrategy = 'timestamp' | 'lastItem';
|
|
84
|
+
/**
|
|
85
|
+
* What a declarative trigger's `fetch` receives. Deliberately smaller than
|
|
86
|
+
* {@link PollContext}: cursor encoding, ordering, windowing and de-duplication
|
|
87
|
+
* are the SDK's job, so the author only has to answer "what is there now?".
|
|
88
|
+
*/
|
|
89
|
+
interface FetchContext {
|
|
90
|
+
config: ConnectorConfig;
|
|
91
|
+
/**
|
|
92
|
+
* With `dedupe: 'timestamp'`, everything changed at or after this instant is
|
|
93
|
+
* worth returning. Absent on the very first poll. Returning a little too
|
|
94
|
+
* much is safe — the SDK drops what was already delivered.
|
|
95
|
+
*/
|
|
96
|
+
since?: string;
|
|
97
|
+
/**
|
|
98
|
+
* With `dedupe: 'lastItem'`, the newest id already delivered. Absent on the
|
|
99
|
+
* very first poll. Return the feed newest-first and the SDK will stop there.
|
|
100
|
+
*/
|
|
101
|
+
lastItemId?: string;
|
|
102
|
+
/** Upper bound on items worth returning in one call. */
|
|
103
|
+
limit?: number;
|
|
104
|
+
/** Injectable clock so tests are deterministic. */
|
|
105
|
+
now(): string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* What an upstream state should become when an item is imported as a task.
|
|
109
|
+
*
|
|
110
|
+
* A suggestion, not a rule: it seeds the connection form, and the person
|
|
111
|
+
* setting it up can change it. Without any, everything a connector imports
|
|
112
|
+
* lands as `todo` regardless of whether it was closed a year ago.
|
|
113
|
+
*/
|
|
114
|
+
interface StatusSuggestion {
|
|
115
|
+
/** The value the connector reports in `ConnectorItem.status`. */
|
|
116
|
+
upstream: string;
|
|
117
|
+
suggestedLocal: 'todo' | 'in_progress' | 'in_review' | 'done' | 'cancelled';
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The workflow to create when a connection is made.
|
|
121
|
+
*
|
|
122
|
+
* A connector that fires on a schedule is useless until something polls it, and
|
|
123
|
+
* expecting every person to build that workflow by hand is how a connection
|
|
124
|
+
* ends up configured and silent. Seeded workflows are ordinary, visible and
|
|
125
|
+
* editable — the schedule is a starting point, not a fixed rule.
|
|
126
|
+
*/
|
|
127
|
+
interface DefaultWorkflow {
|
|
128
|
+
name: string;
|
|
129
|
+
defaultCronFromMinutes: number;
|
|
130
|
+
}
|
|
131
|
+
interface TriggerBase {
|
|
132
|
+
/** Event key, e.g. `workItemCreated`. Becomes the `poll_<type>` MCP tool. */
|
|
133
|
+
type: string;
|
|
134
|
+
label: string;
|
|
135
|
+
description?: string;
|
|
136
|
+
/** Seeds the connection's status mapping; the person setting it up owns it. */
|
|
137
|
+
statusMapping?: StatusSuggestion[];
|
|
138
|
+
/** Seeds a polling workflow when a connection is created. */
|
|
139
|
+
defaultWorkflow?: DefaultWorkflow;
|
|
140
|
+
/**
|
|
141
|
+
* Representative items. `vorn-connector check` replays these through the
|
|
142
|
+
* real dedupe pipeline, so a connector can be verified before anyone has
|
|
143
|
+
* credentials for it.
|
|
144
|
+
*/
|
|
145
|
+
sample?: ConnectorItem[];
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* A trigger is either declarative or hand-written, never both — expressed as a
|
|
149
|
+
* union so the invalid combinations are a type error at authoring time rather
|
|
150
|
+
* than a throw when the connector is first loaded.
|
|
151
|
+
*/
|
|
152
|
+
type TriggerDefinition = TriggerBase & ({
|
|
153
|
+
/**
|
|
154
|
+
* Declarative polling: return what the source has and let the SDK
|
|
155
|
+
* handle cursors and de-duplication.
|
|
156
|
+
*/
|
|
157
|
+
dedupe: DedupeStrategy;
|
|
158
|
+
fetch(context: FetchContext): Promise<ConnectorItem[]> | ConnectorItem[];
|
|
159
|
+
poll?: never;
|
|
160
|
+
} | {
|
|
161
|
+
/**
|
|
162
|
+
* Full control over cursors and paging. Use only when the source's
|
|
163
|
+
* paging cannot be expressed as "give me everything since X".
|
|
164
|
+
*/
|
|
165
|
+
poll(context: PollContext): Promise<PollOutcome> | PollOutcome;
|
|
166
|
+
dedupe?: never;
|
|
167
|
+
fetch?: never;
|
|
168
|
+
});
|
|
169
|
+
interface ActionInputField {
|
|
170
|
+
key: string;
|
|
171
|
+
label: string;
|
|
172
|
+
type?: 'string' | 'number' | 'boolean';
|
|
173
|
+
required?: boolean;
|
|
174
|
+
description?: string;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* A field the action is known to return. Declaring these is optional — extra
|
|
178
|
+
* keys always pass through — but declared fields show up in Vorn's variable
|
|
179
|
+
* autocomplete as `{{steps.<action>.<key>}}`.
|
|
180
|
+
*/
|
|
181
|
+
interface ActionOutputField {
|
|
182
|
+
key: string;
|
|
183
|
+
type?: 'string' | 'number' | 'boolean';
|
|
184
|
+
description?: string;
|
|
185
|
+
}
|
|
186
|
+
interface ActionContext {
|
|
187
|
+
config: ConnectorConfig;
|
|
188
|
+
now(): string;
|
|
189
|
+
}
|
|
190
|
+
interface ActionDefinition {
|
|
191
|
+
/** Action key, e.g. `closeWorkItem`. Becomes an MCP tool of the same name. */
|
|
192
|
+
type: string;
|
|
193
|
+
label: string;
|
|
194
|
+
description?: string;
|
|
195
|
+
/**
|
|
196
|
+
* Whether repeating the call with the same arguments is safe. Surfaced in
|
|
197
|
+
* the MCP tool description, because an agent retrying a failed step has no
|
|
198
|
+
* other way to know whether it is about to create a second issue.
|
|
199
|
+
*/
|
|
200
|
+
idempotent?: boolean;
|
|
201
|
+
inputs?: ActionInputField[];
|
|
202
|
+
outputs?: ActionOutputField[];
|
|
203
|
+
run(args: Record<string, unknown>, context: ActionContext): Promise<Record<string, unknown> | void> | Record<string, unknown> | void;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* A connector's own glyph, so an installed connector is recognizable in a list
|
|
207
|
+
* rather than sharing one generic icon with every other one.
|
|
208
|
+
*
|
|
209
|
+
* Path data only — deliberately not markup. Vorn draws these itself as
|
|
210
|
+
* `<path d="...">` inside an `<svg>` it owns, so a connector cannot inject
|
|
211
|
+
* elements, scripts or external references into the app rendering it.
|
|
212
|
+
*/
|
|
213
|
+
interface ConnectorIcon {
|
|
214
|
+
/** Defaults to `0 0 24 24`. */
|
|
215
|
+
viewBox?: string;
|
|
216
|
+
/** SVG path `d` data, drawn with `fill="currentColor"` so it inherits color. */
|
|
217
|
+
paths: string[];
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* What a connector reports about its own readiness.
|
|
221
|
+
*
|
|
222
|
+
* `message` is shown to the user verbatim, so it should say what to do rather
|
|
223
|
+
* than what went wrong — "run `gh auth login`" beats "not authenticated".
|
|
224
|
+
*/
|
|
225
|
+
interface PreflightResult {
|
|
226
|
+
ok: boolean;
|
|
227
|
+
message?: string;
|
|
228
|
+
}
|
|
229
|
+
interface ConnectorDefinition {
|
|
230
|
+
/** Stable connector id, e.g. `azure-devops`. */
|
|
231
|
+
id: string;
|
|
232
|
+
name: string;
|
|
233
|
+
version?: string;
|
|
234
|
+
description?: string;
|
|
235
|
+
icon?: ConnectorIcon;
|
|
236
|
+
config?: ConnectorConfigField[];
|
|
237
|
+
triggers?: TriggerDefinition[];
|
|
238
|
+
actions?: ActionDefinition[];
|
|
239
|
+
/**
|
|
240
|
+
* Whether this connector could work right now, asked before anyone waits on
|
|
241
|
+
* a poll.
|
|
242
|
+
*
|
|
243
|
+
* A connector whose credentials come from config fields does not need this:
|
|
244
|
+
* a missing field is already a visible, nameable error. One that borrows an
|
|
245
|
+
* external tool's login — `gh auth login`, `az login` — has no field to be
|
|
246
|
+
* missing, so without this the first sign that the tool is absent or signed
|
|
247
|
+
* out is a poll failing some minutes after the connection was saved.
|
|
248
|
+
*
|
|
249
|
+
* Answer `ok: false` with a message saying what to do about it. Throwing is
|
|
250
|
+
* equivalent — the server catches it and reports the same shape with the
|
|
251
|
+
* error's message — so there is one result for a caller to read and no
|
|
252
|
+
* behaviour riding on which you choose. Prefer returning when the state is
|
|
253
|
+
* one you recognise, because then you get to write the sentence.
|
|
254
|
+
*
|
|
255
|
+
* Absent means there is nothing to check, which is not the same answer as a
|
|
256
|
+
* check that passed.
|
|
257
|
+
*/
|
|
258
|
+
preflight?(): Promise<PreflightResult> | PreflightResult;
|
|
259
|
+
}
|
|
260
|
+
/** A validated definition. Every accessor below is guaranteed non-null. */
|
|
261
|
+
interface Connector extends ConnectorDefinition {
|
|
262
|
+
readonly version: string;
|
|
263
|
+
readonly config: ConnectorConfigField[];
|
|
264
|
+
readonly triggers: TriggerDefinition[];
|
|
265
|
+
readonly actions: ActionDefinition[];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
interface CheckFinding {
|
|
269
|
+
/** `error` means the connector will misbehave in Vorn; `warn` is advisory. */
|
|
270
|
+
level: 'error' | 'warn';
|
|
271
|
+
code: string;
|
|
272
|
+
/** Which part of the connector the finding is about. */
|
|
273
|
+
target: string;
|
|
274
|
+
message: string;
|
|
275
|
+
}
|
|
276
|
+
interface CheckOptions {
|
|
277
|
+
/**
|
|
278
|
+
* Poll every trigger against the real source. Off by default, so a check
|
|
279
|
+
* runs on declared `sample` items and the definition alone.
|
|
280
|
+
*/
|
|
281
|
+
live?: boolean;
|
|
282
|
+
/** Credentials, required by `live`. */
|
|
283
|
+
config?: ConnectorConfig;
|
|
284
|
+
now?: () => string;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Check a connector against the contract Vorn relies on.
|
|
288
|
+
*
|
|
289
|
+
* The point is a feedback loop: a connector — hand-written or generated — can
|
|
290
|
+
* be verified before it is ever installed, catching the failures that are
|
|
291
|
+
* otherwise invisible until duplicate tasks show up in someone's inbox days
|
|
292
|
+
* later.
|
|
293
|
+
*/
|
|
294
|
+
declare function checkConnector(connector: Connector, options?: CheckOptions): Promise<CheckFinding[]>;
|
|
295
|
+
/** Render findings for a terminal. Returns an empty string when all clear. */
|
|
296
|
+
declare function formatFindings(findings: CheckFinding[]): string;
|
|
297
|
+
|
|
298
|
+
/** Largest pack Vorn will install, matched by the server's own verification. */
|
|
299
|
+
declare const MAX_PACK_BYTES: number;
|
|
300
|
+
interface PackOptions {
|
|
301
|
+
/** Module specifier the connector was loaded from, bundled as the pack entry. */
|
|
302
|
+
entry: string;
|
|
303
|
+
/** Directory the `.vorn.tgz` is written to; defaults to the working directory. */
|
|
304
|
+
outDir?: string;
|
|
305
|
+
/** Directory module specifiers resolve from; defaults to the working directory. */
|
|
306
|
+
resolveDir?: string;
|
|
307
|
+
/** SDK specifier the generated stdio entry imports; overridden in tests. */
|
|
308
|
+
sdkModule?: string;
|
|
309
|
+
/** Size ceiling for the written archive; defaults to `MAX_PACK_BYTES`. */
|
|
310
|
+
maxBytes?: number;
|
|
311
|
+
/** Replaced in tests so packing does not shell out to a bundler. */
|
|
312
|
+
bundle?(request: BundleRequest): Promise<BundleOutput>;
|
|
313
|
+
}
|
|
314
|
+
interface BundleRequest {
|
|
315
|
+
contents: string;
|
|
316
|
+
resolveDir: string;
|
|
317
|
+
}
|
|
318
|
+
interface BundleOutput {
|
|
319
|
+
code: string;
|
|
320
|
+
/** Specifiers the bundler left for the runtime to resolve. */
|
|
321
|
+
external: string[];
|
|
322
|
+
}
|
|
323
|
+
interface PackResult {
|
|
324
|
+
findings: CheckFinding[];
|
|
325
|
+
/** Absolute path of the written pack; absent when a gate failed. */
|
|
326
|
+
file?: string;
|
|
327
|
+
bytes?: number;
|
|
328
|
+
}
|
|
329
|
+
/** Reject a source package whose install would run code on the user's machine. */
|
|
330
|
+
declare function lifecycleScriptFindings(pkg: unknown): CheckFinding[];
|
|
331
|
+
/** Specifiers left outside a bundle, which would need a registry at launch. */
|
|
332
|
+
declare function bundleDependencyFindings(external: string[]): CheckFinding[];
|
|
333
|
+
/** Nearest package.json at or above a directory, or undefined when there is none. */
|
|
334
|
+
declare function readNearestPackageJson(fromDir: string): Record<string, unknown> | undefined;
|
|
335
|
+
/** File name Vorn recognizes as a connector pack. */
|
|
336
|
+
declare function packFileName(connector: Connector): string;
|
|
337
|
+
/** The entry is generated, not the author's bin, so every pack launches alike. */
|
|
338
|
+
declare function packConnector(connector: Connector, options: PackOptions): Promise<PackResult>;
|
|
339
|
+
|
|
340
|
+
export { type ActionContext as A, type BundleRequest as B, type ConnectorDefinition as C, type DefaultWorkflow as D, type FetchContext as F, MAX_PACK_BYTES as M, type NormalizedItem as N, type PollContext as P, type StatusSuggestion as S, type TriggerDefinition as T, type BundleOutput as a, type Connector as b, type ConnectorConfig as c, type PollOutcome as d, type ConnectorItem as e, type ConnectorIcon as f, type ActionDefinition as g, type ActionInputField as h, type CheckFinding as i, type CheckOptions as j, type ConnectorConfigField as k, type DedupeStrategy as l, type PackOptions as m, type PackResult as n, type PreflightResult as o, bundleDependencyFindings as p, checkConnector as q, formatFindings as r, lifecycleScriptFindings as s, packConnector as t, packFileName as u, readNearestPackageJson as v };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vornrun/connector-sdk",
|
|
3
|
-
"version": "0.7.0-beta.
|
|
3
|
+
"version": "0.7.0-beta.8",
|
|
4
4
|
"description": "Build and share Vorn pull connectors as ordinary npm packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
40
|
+
"esbuild": "^0.28.2",
|
|
41
|
+
"tar": "^7.5.21",
|
|
40
42
|
"zod": "^4.4.3"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|