@zapier/connectors-sdk 0.1.0-experimental.1 → 0.1.0-experimental.11
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.internal.md +0 -26
- package/dist/index.cjs +223 -48
- package/dist/index.d.cts +49 -52
- package/dist/index.d.ts +49 -52
- package/dist/index.js +222 -48
- package/package.json +5 -5
package/README.internal.md
CHANGED
|
@@ -346,32 +346,6 @@ A multi-credential resolver `{ name: "hmac", keySuffixes: ["KEY", "SECRET"] }` w
|
|
|
346
346
|
|
|
347
347
|
The author never sees `process.env`, never threads a `Fetch` through their script body, never re-implements auth logic.
|
|
348
348
|
|
|
349
|
-
## `inputDependencies` — out-of-band dependent-field metadata
|
|
350
|
-
|
|
351
|
-
Some scripts have input fields whose accepted shape depends on another input field — e.g. Notion's `create_database_item` takes a `databaseId` and a `properties` map whose schema is determined by which database was chosen. JSON Schema can't express that conditional shape, but adapters that drive option-loading or schema-resolution chains need to know about it.
|
|
352
|
-
|
|
353
|
-
`defineTool` publishes the metadata in two places:
|
|
354
|
-
|
|
355
|
-
1. `definition.inputDependencies` — programmatic readers reach for it directly on the script's default export.
|
|
356
|
-
2. MCP registration shape — `_meta["zapier:inputDependencies"]` via either `toMcpTool` (wire-format) or `toMcpServerTool` (`McpServer.registerTool` config).
|
|
357
|
-
|
|
358
|
-
Shape constraints:
|
|
359
|
-
|
|
360
|
-
- Dependencies are keyed by the dependent input field's name (`databaseId`, `properties`).
|
|
361
|
-
- Each entry names another tool by **string** (`fromTool: "list-databases"`), not by function reference — so the chain stays serializable.
|
|
362
|
-
- Args passed to the resolver tool use `$<fieldName>` syntax to reference other input fields (`fromArgs: { databaseId: "$databaseId" }`). The adapter reads and evaluates these references; the script body does not.
|
|
363
|
-
|
|
364
|
-
```ts
|
|
365
|
-
inputDependencies: {
|
|
366
|
-
databaseId: { kind: "options", fromTool: "list-databases", fromArgs: {} },
|
|
367
|
-
properties: {
|
|
368
|
-
kind: "schema",
|
|
369
|
-
fromTool: "get-database-schema",
|
|
370
|
-
fromArgs: { databaseId: "$databaseId" },
|
|
371
|
-
},
|
|
372
|
-
} as const,
|
|
373
|
-
```
|
|
374
|
-
|
|
375
349
|
## `<bin> mcp` — local-MCP-server execution surface
|
|
376
350
|
|
|
377
351
|
Reached through the bundled CLI as `npx @zapier/<x>-connector mcp` — no per-app glue. The dispatcher reserves the primary command `mcp` and delegates to the internal `serveMcpStdio` helper:
|
package/dist/index.cjs
CHANGED
|
@@ -109,6 +109,7 @@ function defineConnectionResolver(resolver) {
|
|
|
109
109
|
}
|
|
110
110
|
var zapierConnectionResolver = defineConnectionResolver({
|
|
111
111
|
name: "zapier-connection-id",
|
|
112
|
+
optionalPackages: ["@zapier/zapier-sdk"],
|
|
112
113
|
resolve: async (connectionId) => {
|
|
113
114
|
const { buildZapierFetch: buildZapierFetch2 } = await Promise.resolve().then(() => (init_build_zapier_fetch(), build_zapier_fetch_exports));
|
|
114
115
|
return buildZapierFetch2(connectionId);
|
|
@@ -128,12 +129,16 @@ function defineBearerTokenResolver(opts = {}) {
|
|
|
128
129
|
});
|
|
129
130
|
}
|
|
130
131
|
|
|
132
|
+
// src/normalize-connections.ts
|
|
133
|
+
var import_node_module = require("module");
|
|
134
|
+
|
|
131
135
|
// src/connection-key.ts
|
|
132
136
|
function envFromKey(name) {
|
|
133
137
|
return name.toUpperCase().replace(/-/g, "_");
|
|
134
138
|
}
|
|
135
139
|
|
|
136
140
|
// src/normalize-connections.ts
|
|
141
|
+
var import_meta = {};
|
|
137
142
|
function* walkConnections(definition) {
|
|
138
143
|
if (typeof definition.connection === "string") {
|
|
139
144
|
yield { slotName: void 0, connectionKey: definition.connection };
|
|
@@ -267,23 +272,103 @@ function buildRunOptionsFromEnv(definition, env, connectionResolvers) {
|
|
|
267
272
|
}
|
|
268
273
|
return { connections: wrapped };
|
|
269
274
|
}
|
|
270
|
-
function
|
|
275
|
+
function isPackageInstalled(name) {
|
|
276
|
+
try {
|
|
277
|
+
(0, import_node_module.createRequire)(import_meta.url).resolve(name);
|
|
278
|
+
return true;
|
|
279
|
+
} catch {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function buildMissingConnectionError(scriptName, missingSlots) {
|
|
284
|
+
const lines = [];
|
|
285
|
+
if (missingSlots.length === 1 && missingSlots[0].slotName === void 0) {
|
|
286
|
+
const { connectionKey, resolvers } = missingSlots[0];
|
|
287
|
+
lines.push(
|
|
288
|
+
`"${scriptName}" requires the "${connectionKey}" connection but no credentials were found in the environment.`
|
|
289
|
+
);
|
|
290
|
+
lines.push("");
|
|
291
|
+
lines.push(
|
|
292
|
+
"Set one of the following environment variables before running:"
|
|
293
|
+
);
|
|
294
|
+
lines.push("");
|
|
295
|
+
for (const resolver of resolvers) {
|
|
296
|
+
const vars = envVarsFor(void 0, connectionKey, resolver);
|
|
297
|
+
lines.push(` ${vars.join(" ")} (${resolver.name})`);
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
lines.push(
|
|
301
|
+
`"${scriptName}" is missing credentials for connection slot(s):`
|
|
302
|
+
);
|
|
303
|
+
for (const { slotName, connectionKey, resolvers } of missingSlots) {
|
|
304
|
+
lines.push("");
|
|
305
|
+
lines.push(` ${slotName} (${connectionKey}):`);
|
|
306
|
+
for (const resolver of resolvers) {
|
|
307
|
+
const vars = envVarsFor(slotName, connectionKey, resolver);
|
|
308
|
+
lines.push(` ${vars.join(" ")} (${resolver.name})`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
lines.push("");
|
|
313
|
+
lines.push("Run with --help for the full auth guide.");
|
|
314
|
+
return new Error(lines.join("\n"));
|
|
315
|
+
}
|
|
316
|
+
function formatHelpForConnections(definition, connectionResolvers, env) {
|
|
271
317
|
const slots = [...walkConnections(definition)];
|
|
272
318
|
if (slots.length === 0) return [];
|
|
273
319
|
const lines = [];
|
|
274
320
|
lines.push(
|
|
275
|
-
"
|
|
321
|
+
"Auth (set as environment variables; do NOT pass via CLI argument):"
|
|
276
322
|
);
|
|
323
|
+
const notReadySlots = [];
|
|
277
324
|
for (const { slotName, connectionKey } of slots) {
|
|
278
325
|
const resolvers = resolversForKey(connectionResolvers, connectionKey);
|
|
279
|
-
|
|
280
|
-
|
|
326
|
+
if (slotName !== void 0) {
|
|
327
|
+
lines.push(` ${slotName} (${connectionKey}):`);
|
|
328
|
+
}
|
|
329
|
+
const resolverIndent = slotName !== void 0 ? " " : " ";
|
|
330
|
+
const varIndent = slotName !== void 0 ? " " : " ";
|
|
331
|
+
let slotHasReady = false;
|
|
281
332
|
for (const resolver of resolvers) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
333
|
+
const vars = envVarsFor(slotName, connectionKey, resolver);
|
|
334
|
+
let isReady = false;
|
|
335
|
+
let missingPkgs = [];
|
|
336
|
+
if (env !== void 0) {
|
|
337
|
+
const envVarsOk = vars.every(
|
|
338
|
+
(k) => typeof env[k] === "string" && env[k] !== ""
|
|
339
|
+
);
|
|
340
|
+
if (envVarsOk) isReady = true;
|
|
341
|
+
missingPkgs = (resolver.optionalPackages ?? []).filter(
|
|
342
|
+
(pkg) => !isPackageInstalled(pkg)
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
const markReady = isReady && !slotHasReady;
|
|
346
|
+
if (markReady) slotHasReady = true;
|
|
347
|
+
const resolverSuffix = env !== void 0 && markReady ? " [READY \u2014 use this]" : "";
|
|
348
|
+
lines.push(`${resolverIndent}${resolver.name}:${resolverSuffix}`);
|
|
349
|
+
for (const envVar of vars) {
|
|
350
|
+
const varSuffix = env !== void 0 ? env[envVar] !== void 0 && env[envVar] !== "" ? " [set]" : " [not set]" : "";
|
|
351
|
+
lines.push(`${varIndent}${envVar}${varSuffix}`);
|
|
352
|
+
}
|
|
353
|
+
if (env !== void 0) {
|
|
354
|
+
for (const pkg of resolver.optionalPackages ?? []) {
|
|
355
|
+
if (missingPkgs.includes(pkg)) {
|
|
356
|
+
lines.push(
|
|
357
|
+
`${varIndent}${pkg} [not installed \u2014 run \`npm install ${pkg}\` first]`
|
|
358
|
+
);
|
|
359
|
+
} else {
|
|
360
|
+
lines.push(`${varIndent}${pkg} [installed]`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
285
363
|
}
|
|
286
364
|
}
|
|
365
|
+
if (env !== void 0 && !slotHasReady) {
|
|
366
|
+
notReadySlots.push(slotName ?? connectionKey);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (env !== void 0 && notReadySlots.length > 0) {
|
|
370
|
+
const label = notReadySlots.length === slots.length ? "No option is ready" : `No option is ready for slot${notReadySlots.length > 1 ? "s" : ""} ${notReadySlots.join(", ")}`;
|
|
371
|
+
lines.push(`${label} \u2014 set one of the env vars above.`);
|
|
287
372
|
}
|
|
288
373
|
return lines;
|
|
289
374
|
}
|
|
@@ -368,8 +453,8 @@ function ensureConnectionValue(scriptName, slotName, connectionKey, value) {
|
|
|
368
453
|
return value;
|
|
369
454
|
}
|
|
370
455
|
async function buildContext(definition, opts, connectionResolvers) {
|
|
371
|
-
const hasConnection = "connection" in opts && opts.connection !== void 0;
|
|
372
|
-
const hasConnections = "connections" in opts && opts.connections !== void 0;
|
|
456
|
+
const hasConnection = opts !== void 0 && "connection" in opts && opts.connection !== void 0;
|
|
457
|
+
const hasConnections = opts !== void 0 && "connections" in opts && opts.connections !== void 0;
|
|
373
458
|
if (definition.connection === void 0 && definition.connections === void 0) {
|
|
374
459
|
if (hasConnection || hasConnections) {
|
|
375
460
|
throw new Error(
|
|
@@ -378,6 +463,11 @@ async function buildContext(definition, opts, connectionResolvers) {
|
|
|
378
463
|
}
|
|
379
464
|
return {};
|
|
380
465
|
}
|
|
466
|
+
if (opts === void 0) {
|
|
467
|
+
throw new Error(
|
|
468
|
+
`ToolDefinition "${definition.name}": \`RunOptions\` is required \u2014 pass \`{ connection: ... }\` or \`{ connections: ... }\`.`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
381
471
|
if (hasConnection && hasConnections) {
|
|
382
472
|
throw new Error(
|
|
383
473
|
`ToolDefinition "${definition.name}": \`RunOptions\` sets both \`connection\` and \`connections\`. Use one or the other.`
|
|
@@ -395,7 +485,7 @@ async function buildContext(definition, opts, connectionResolvers) {
|
|
|
395
485
|
definition.name,
|
|
396
486
|
void 0,
|
|
397
487
|
connectionKey,
|
|
398
|
-
opts.connection
|
|
488
|
+
"connection" in opts ? opts.connection : void 0
|
|
399
489
|
);
|
|
400
490
|
const fetch = await resolveSlotFetch(
|
|
401
491
|
definition.name,
|
|
@@ -412,12 +502,12 @@ async function buildContext(definition, opts, connectionResolvers) {
|
|
|
412
502
|
`ToolDefinition "${definition.name}" is multi-connection (slots: ${declaredSlots.join(", ")}) \u2014 call with \`{ connections: { ... } }\`, not \`{ connection: ... }\`.`
|
|
413
503
|
);
|
|
414
504
|
}
|
|
415
|
-
|
|
505
|
+
const provided = opts.connections;
|
|
506
|
+
if (provided === void 0) {
|
|
416
507
|
throw new Error(
|
|
417
508
|
`ToolDefinition "${definition.name}" is multi-connection \u2014 \`RunOptions.connections\` is required.`
|
|
418
509
|
);
|
|
419
510
|
}
|
|
420
|
-
const provided = opts.connections;
|
|
421
511
|
const unknown = Object.keys(provided).filter(
|
|
422
512
|
(s) => !declaredSlots.includes(s)
|
|
423
513
|
);
|
|
@@ -471,28 +561,18 @@ function toChatCompletionTool(definition) {
|
|
|
471
561
|
|
|
472
562
|
// src/surfaces/to-mcp-server-tool.ts
|
|
473
563
|
function toMcpServerTool(definition) {
|
|
474
|
-
|
|
564
|
+
return {
|
|
475
565
|
title: definition.title,
|
|
476
566
|
description: definition.description,
|
|
477
567
|
inputSchema: definition.inputSchema,
|
|
478
568
|
outputSchema: definition.outputSchema,
|
|
479
569
|
annotations: definition.annotations
|
|
480
570
|
};
|
|
481
|
-
if (definition.inputDependencies) {
|
|
482
|
-
config._meta = {
|
|
483
|
-
"zapier:inputDependencies": definition.inputDependencies
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
return config;
|
|
487
571
|
}
|
|
488
572
|
|
|
489
573
|
// src/surfaces/to-mcp-tool.ts
|
|
490
574
|
var import_zod2 = require("zod");
|
|
491
575
|
function toMcpTool(definition) {
|
|
492
|
-
const meta = {};
|
|
493
|
-
if (definition.inputDependencies) {
|
|
494
|
-
meta["zapier:inputDependencies"] = definition.inputDependencies;
|
|
495
|
-
}
|
|
496
576
|
return {
|
|
497
577
|
name: definition.name,
|
|
498
578
|
title: definition.title,
|
|
@@ -501,8 +581,7 @@ function toMcpTool(definition) {
|
|
|
501
581
|
outputSchema: import_zod2.z.toJSONSchema(
|
|
502
582
|
definition.outputSchema
|
|
503
583
|
),
|
|
504
|
-
annotations: definition.annotations
|
|
505
|
-
...Object.keys(meta).length > 0 ? { _meta: meta } : {}
|
|
584
|
+
annotations: definition.annotations
|
|
506
585
|
};
|
|
507
586
|
}
|
|
508
587
|
|
|
@@ -516,24 +595,9 @@ function toResponsesTool(definition) {
|
|
|
516
595
|
|
|
517
596
|
// src/define-connector.ts
|
|
518
597
|
function wrapScriptWithResolvers(definition, connectionResolvers) {
|
|
519
|
-
const authorRun = definition.run;
|
|
520
598
|
const wrappedRun = async (input, opts) => {
|
|
521
|
-
const validated = definition.inputSchema.parse(input);
|
|
522
|
-
if (definition.connection === void 0 && definition.connections === void 0) {
|
|
523
|
-
if (opts !== void 0 && (opts.connection !== void 0 || opts.connections !== void 0)) {
|
|
524
|
-
throw new Error(
|
|
525
|
-
`ToolDefinition "${definition.name}" is credential-free \u2014 \`RunOptions\` must not include \`connection\` or \`connections\`.`
|
|
526
|
-
);
|
|
527
|
-
}
|
|
528
|
-
return authorRun(validated);
|
|
529
|
-
}
|
|
530
|
-
if (opts === void 0) {
|
|
531
|
-
throw new Error(
|
|
532
|
-
`ToolDefinition "${definition.name}": \`RunOptions\` is required \u2014 pass \`{ connection: ... }\` or \`{ connections: ... }\`.`
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
599
|
const ctx = await buildContext(definition, opts, connectionResolvers);
|
|
536
|
-
return
|
|
600
|
+
return definition.run(input, ctx);
|
|
537
601
|
};
|
|
538
602
|
return { ...definition, run: wrappedRun };
|
|
539
603
|
}
|
|
@@ -543,6 +607,11 @@ function defineConnector(config) {
|
|
|
543
607
|
validateConnectionResolvers(scriptsAsAny, connectionResolvers);
|
|
544
608
|
const wrapped = {};
|
|
545
609
|
for (const [key, definition] of Object.entries(scriptsAsAny)) {
|
|
610
|
+
if (key !== definition.name) {
|
|
611
|
+
throw new Error(
|
|
612
|
+
`defineConnector: script key "${key}" must match its tool name \u2014 got name "${definition.name}". Rename the key to "${definition.name}" or update the tool's name field.`
|
|
613
|
+
);
|
|
614
|
+
}
|
|
546
615
|
wrapped[key] = wrapScriptWithResolvers(definition, connectionResolvers);
|
|
547
616
|
}
|
|
548
617
|
return {
|
|
@@ -612,6 +681,12 @@ function defineTool(config) {
|
|
|
612
681
|
validateConnectionKey(config.name, value);
|
|
613
682
|
}
|
|
614
683
|
}
|
|
684
|
+
const authorRun = config.run;
|
|
685
|
+
const wrappedRun = async (input, ctx) => {
|
|
686
|
+
const validated = config.inputSchema.parse(input);
|
|
687
|
+
const result = await authorRun(validated, ctx);
|
|
688
|
+
return config.outputSchema.parse(result);
|
|
689
|
+
};
|
|
615
690
|
const base = {
|
|
616
691
|
kind: "tool",
|
|
617
692
|
name: config.name,
|
|
@@ -620,9 +695,7 @@ function defineTool(config) {
|
|
|
620
695
|
inputSchema: config.inputSchema,
|
|
621
696
|
outputSchema: config.outputSchema,
|
|
622
697
|
annotations: config.annotations,
|
|
623
|
-
|
|
624
|
-
inputDependencies: config.inputDependencies,
|
|
625
|
-
run: config.run
|
|
698
|
+
run: wrappedRun
|
|
626
699
|
};
|
|
627
700
|
if (hasSingular) base.connection = config.connection;
|
|
628
701
|
if (hasPlural) base.connections = config.connections;
|
|
@@ -766,10 +839,73 @@ async function runDispatchCliBody(connector, opts) {
|
|
|
766
839
|
});
|
|
767
840
|
return;
|
|
768
841
|
}
|
|
842
|
+
if (first === "skill") {
|
|
843
|
+
if (!opts.meta) {
|
|
844
|
+
throw new Error(
|
|
845
|
+
"runDispatchCliBody: `skill` requires `meta` in options (the connector's `import.meta` from `cli.ts`)."
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
const cliPath = (0, import_node_url2.fileURLToPath)(opts.meta.url);
|
|
849
|
+
const connectorDir = path2.dirname(cliPath);
|
|
850
|
+
const skillPath = path2.join(connectorDir, "SKILL.md");
|
|
851
|
+
let content;
|
|
852
|
+
try {
|
|
853
|
+
content = await fs2.readFile(skillPath, "utf8");
|
|
854
|
+
} catch {
|
|
855
|
+
throw new Error(
|
|
856
|
+
`SKILL.md not found at ${skillPath}. Ensure the connector ships a SKILL.md.`
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
opts.stdout.write(resolveRelativeLinks(content, connectorDir));
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
if (first === "reference") {
|
|
863
|
+
if (!opts.meta) {
|
|
864
|
+
throw new Error(
|
|
865
|
+
"runDispatchCliBody: `reference` requires `meta` in options (the connector's `import.meta` from `cli.ts`)."
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
const cliPath = (0, import_node_url2.fileURLToPath)(opts.meta.url);
|
|
869
|
+
const connectorDir = path2.dirname(cliPath);
|
|
870
|
+
const refsDir = path2.join(connectorDir, "references");
|
|
871
|
+
const name = args[1];
|
|
872
|
+
if (!name) {
|
|
873
|
+
let entries;
|
|
874
|
+
try {
|
|
875
|
+
const files = await fs2.readdir(refsDir);
|
|
876
|
+
entries = files.filter((f) => f.endsWith(".md")).map((f) => f.slice(0, -".md".length));
|
|
877
|
+
} catch {
|
|
878
|
+
entries = [];
|
|
879
|
+
}
|
|
880
|
+
if (entries.length === 0) {
|
|
881
|
+
opts.stdout.write(`Available references: <none>
|
|
882
|
+
`);
|
|
883
|
+
} else {
|
|
884
|
+
opts.stdout.write(`Available references:
|
|
885
|
+
`);
|
|
886
|
+
for (const entry of entries) {
|
|
887
|
+
opts.stdout.write(` ${entry}
|
|
888
|
+
`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
const refPath = path2.join(refsDir, `${name}.md`);
|
|
894
|
+
let content;
|
|
895
|
+
try {
|
|
896
|
+
content = await fs2.readFile(refPath, "utf8");
|
|
897
|
+
} catch {
|
|
898
|
+
throw new Error(
|
|
899
|
+
`Reference file not found at ${refPath}. Ensure the connector ships \`references/${name}.md\`.`
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
opts.stdout.write(resolveRelativeLinks(content, refsDir));
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
769
905
|
if (first !== "run") {
|
|
770
906
|
printUsage(scripts, errOut, packageName);
|
|
771
907
|
throw new Error(
|
|
772
|
-
`Unknown command "${first}". Use \`run <script>\` to dispatch a script (available: ${Object.keys(scripts).join(", ") || "<none>"}),
|
|
908
|
+
`Unknown command "${first}". Use \`run <script>\` to dispatch a script (available: ${Object.keys(scripts).join(", ") || "<none>"}), \`mcp\` to boot a local MCP server, \`skill\` to print SKILL.md, or \`reference [<name>]\` to print or list reference docs.`
|
|
773
909
|
);
|
|
774
910
|
}
|
|
775
911
|
const scriptName = args[1];
|
|
@@ -796,6 +932,17 @@ async function runDispatchCliBody(connector, opts) {
|
|
|
796
932
|
invocation
|
|
797
933
|
});
|
|
798
934
|
}
|
|
935
|
+
function resolveRelativeLinks(content, baseDir) {
|
|
936
|
+
return content.replace(
|
|
937
|
+
/(!?\[([^\]]*)\])\(([^)]+)\)/g,
|
|
938
|
+
(match, prefix, _alt, url) => {
|
|
939
|
+
if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file:") || url.startsWith("#") || url.startsWith("/")) {
|
|
940
|
+
return match;
|
|
941
|
+
}
|
|
942
|
+
return `${prefix}(${path2.resolve(baseDir, url)})`;
|
|
943
|
+
}
|
|
944
|
+
);
|
|
945
|
+
}
|
|
799
946
|
async function resolvePackageName(meta) {
|
|
800
947
|
if (!meta?.url) return void 0;
|
|
801
948
|
try {
|
|
@@ -814,6 +961,8 @@ function printUsage(scripts, out, packageName) {
|
|
|
814
961
|
`Usage: <bin> run <script> [<json-input>]
|
|
815
962
|
<bin> run <script> --help (per-script input + required env vars)
|
|
816
963
|
<bin> mcp (run as a local MCP server over stdio)
|
|
964
|
+
<bin> skill (print SKILL.md to stdout)
|
|
965
|
+
<bin> reference [<name>] (print references/<name>.md, or list all)
|
|
817
966
|
|
|
818
967
|
`
|
|
819
968
|
);
|
|
@@ -926,12 +1075,13 @@ async function handleIfScriptMainBody(wrappedScript, connectionResolvers, io) {
|
|
|
926
1075
|
if (helpRequested) {
|
|
927
1076
|
io.stdout.write(
|
|
928
1077
|
buildHelpText(wrappedScript, connectionResolvers, {
|
|
929
|
-
invocation: io.invocation
|
|
1078
|
+
invocation: io.invocation,
|
|
1079
|
+
env: io.env
|
|
930
1080
|
})
|
|
931
1081
|
);
|
|
932
1082
|
return;
|
|
933
1083
|
}
|
|
934
|
-
const raw = positional ?? await
|
|
1084
|
+
const raw = positional ?? await readStdinInput(io.stdin, wrappedScript.name);
|
|
935
1085
|
if (raw.trim().length === 0) {
|
|
936
1086
|
throw new Error(
|
|
937
1087
|
`Missing JSON input for "${wrappedScript.name}". Pass it as a positional argument or via stdin.`
|
|
@@ -940,9 +1090,33 @@ async function handleIfScriptMainBody(wrappedScript, connectionResolvers, io) {
|
|
|
940
1090
|
const input = wrappedScript.inputSchema.parse(JSON.parse(raw));
|
|
941
1091
|
const hasConnections = [...walkConnections(wrappedScript)].length > 0;
|
|
942
1092
|
const runOpts = hasConnections ? buildRunOptionsFromEnv(wrappedScript, io.env, connectionResolvers) : void 0;
|
|
1093
|
+
if (runOpts !== void 0) {
|
|
1094
|
+
const missing = [];
|
|
1095
|
+
for (const { slotName, connectionKey } of walkConnections(wrappedScript)) {
|
|
1096
|
+
const value = slotName === void 0 ? "connection" in runOpts ? runOpts.connection : void 0 : "connections" in runOpts ? runOpts.connections[slotName] : void 0;
|
|
1097
|
+
if (value === void 0) {
|
|
1098
|
+
missing.push({
|
|
1099
|
+
slotName,
|
|
1100
|
+
connectionKey,
|
|
1101
|
+
resolvers: resolversForKey(connectionResolvers, connectionKey)
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
if (missing.length > 0) {
|
|
1106
|
+
throw buildMissingConnectionError(wrappedScript.name, missing);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
943
1109
|
const result = await wrappedScript.run(input, runOpts);
|
|
944
1110
|
io.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
945
1111
|
}
|
|
1112
|
+
async function readStdinInput(stdin, scriptName) {
|
|
1113
|
+
if (stdin.isTTY) {
|
|
1114
|
+
throw new Error(
|
|
1115
|
+
`Missing JSON input for "${scriptName}". Pass it as a positional argument or pipe it via stdin.`
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
return new Response(stdin).text();
|
|
1119
|
+
}
|
|
946
1120
|
function buildHelpText(definition, connectionResolvers, opts = {}) {
|
|
947
1121
|
const lines = [];
|
|
948
1122
|
const description = definition.description.split("\n")[0]?.trim() ?? "";
|
|
@@ -966,7 +1140,8 @@ function buildHelpText(definition, connectionResolvers, opts = {}) {
|
|
|
966
1140
|
}
|
|
967
1141
|
const connectionsBlock = formatHelpForConnections(
|
|
968
1142
|
definition,
|
|
969
|
-
connectionResolvers
|
|
1143
|
+
connectionResolvers,
|
|
1144
|
+
opts.env
|
|
970
1145
|
);
|
|
971
1146
|
if (connectionsBlock.length > 0) {
|
|
972
1147
|
lines.push(...connectionsBlock);
|