@silkweave/cli 4.3.0 → 5.0.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/README.md CHANGED
@@ -79,6 +79,21 @@ Message 2 about weather
79
79
 
80
80
  Backpressure is honoured: each chunk write awaits `stdout`'s `drain` event when the buffer is full, so the action throttles itself if a downstream pipe is slow.
81
81
 
82
+ ## Resource Results (binary)
83
+
84
+ An action with a `binary()` output (see [`@silkweave/core`](https://www.npmjs.com/package/@silkweave/core)) - or any action returning a `resource()`, `File`/`Blob`, or bare bytes - delivers its payload terminal-appropriately; binary is **never dumped onto an interactive terminal**:
85
+
86
+ ```bash
87
+ # Piped: raw bytes on stdout (description + status go to stderr)
88
+ $ mytool screenshot --url https://example.com > shot.png
89
+
90
+ # Interactive: written to --output, or a file named after the resource
91
+ $ mytool screenshot --url https://example.com --output shot.png
92
+ Wrote 48213 bytes (image/png) to shot.png
93
+ ```
94
+
95
+ The `-o, --output <path>` flag is added automatically for `binary()` actions (unless the input schema already has an `output` field, which then wins). The command banner moves to stderr for binary actions so a piped stdout stays byte-clean.
96
+
82
97
  ## See Also
83
98
 
84
99
  - [Silkweave README](https://github.com/silkweave/silkweave) - Full documentation
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/cli.ts"],"mappings":";;;cAqIa,GAAA,EAAK,cAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/cli.ts"],"mappings":";;;cA+Ka,GAAA,EAAK,cAAA"}
package/build/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
- import { SilkweaveError, createConsoleLogger, isStreamingAction, unwrap } from "@silkweave/core";
1
+ import { SilkweaveError, binarySchemaMeta, createConsoleLogger, isBinarySchema, isStreamingAction, resourceBytes, toActionResource, unwrap } from "@silkweave/core";
2
2
  import { camelCase, kebabCase } from "change-case";
3
3
  import { Command } from "commander";
4
4
  import { once } from "events";
5
+ import { writeFile } from "fs/promises";
5
6
  import z from "zod/v4";
6
7
  //#region src/adapter/cli.ts
7
8
  function handleCLIError(error) {
@@ -75,6 +76,27 @@ function addCliOption(command, key, type, defaultValue, isArgument) {
75
76
  if (parseArg) command.option(`--${flag} ${placeholder}`, description ?? "", parseArg, defaultValue);
76
77
  else command.option(`--${flag} ${placeholder}`, description, defaultValue);
77
78
  }
79
+ /** File extension derived from a media type's subtype (`image/png` -> `png`). */
80
+ function extensionFromMime(mimeType) {
81
+ return mimeType.split(";")[0].split("/")[1]?.split("+")[0]?.trim() || "bin";
82
+ }
83
+ /**
84
+ * Deliver a resource result terminal-appropriately: raw bytes to stdout when
85
+ * piped (`my-cli screenshot > shot.png`), otherwise written to `--output` or a
86
+ * file named after the resource - binary is never dumped onto an interactive
87
+ * terminal. Status lines go to stderr so a piped stdout stays byte-clean.
88
+ */
89
+ async function writeResourceResult(res, outputPath) {
90
+ const bytes = resourceBytes(res);
91
+ if (res.description) console.error(res.description);
92
+ if (!outputPath && !process.stdout.isTTY) {
93
+ if (!process.stdout.write(bytes)) await once(process.stdout, "drain");
94
+ return;
95
+ }
96
+ const target = outputPath ?? res.name ?? `resource.${extensionFromMime(res.mimeType)}`;
97
+ await writeFile(target, bytes);
98
+ console.error(`Wrote ${bytes.length} bytes (${res.mimeType}) to ${target}`);
99
+ }
78
100
  async function runStreamingCommand(action, input, context) {
79
101
  const streamRun = action.run;
80
102
  const iter = streamRun(input, context);
@@ -88,6 +110,8 @@ function registerCommand(program, action, options, context) {
88
110
  const shape = action.input.shape;
89
111
  const argKeys = action.args ?? [];
90
112
  const argSet = new Set(argKeys);
113
+ const binaryOutput = isBinarySchema(action.output) && !("output" in shape);
114
+ if (binaryOutput) command.option("-o, --output <path>", "Write the resource to this file (default: the resource name)");
91
115
  for (const key of Object.keys(shape)) {
92
116
  if (argSet.has(key)) continue;
93
117
  const [type, { defaultValue }] = unwrap(shape[key]);
@@ -109,9 +133,15 @@ function registerCommand(program, action, options, context) {
109
133
  runStreamingCommand(action, input, actionContext).catch(handleCLIError);
110
134
  return;
111
135
  }
112
- console.info(`${options.name} - ${action.name}`);
136
+ if (binaryOutput) console.error(`${options.name} - ${action.name}`);
137
+ else console.info(`${options.name} - ${action.name}`);
113
138
  const runFn = action.run;
114
- runFn(input, actionContext).then((result) => {
139
+ runFn(input, actionContext).then(async (result) => {
140
+ const res = await toActionResource(result, binarySchemaMeta(action.output));
141
+ if (res) {
142
+ await writeResourceResult(res, binaryOutput ? command.opts().output : void 0);
143
+ return;
144
+ }
115
145
  logger.info(JSON.stringify(result, null, 2));
116
146
  }).catch(handleCLIError);
117
147
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/adapter/cli.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Action, ActionRun, ActionStreamRun, AdapterFactory, createConsoleLogger, isStreamingAction, SilkweaveContext, SilkweaveError, SilkweaveOptions, unwrap } from '@silkweave/core'\nimport { camelCase, kebabCase } from 'change-case'\nimport { Command } from 'commander'\nimport { once } from 'events'\nimport z from 'zod/v4'\n\nfunction handleCLIError(error: unknown) {\n if (error instanceof SilkweaveError) {\n console.error(`[${error.code}] ${error.message}`)\n } else if (error instanceof z.ZodError) {\n console.error('Validation Error')\n for (const issue of error.issues) {\n console.error(`${issue.path}: ${issue.message}`)\n }\n } else if (error instanceof Error) {\n console.error(error.message)\n } else if (typeof error === 'string') {\n console.error(error)\n } else {\n console.error(JSON.stringify(error))\n }\n process.exitCode = 1\n}\n\nfunction parseCLIInput(action: Action, args: any[]) {\n const tmpArgs = args.slice(0, -1)\n const opts = tmpArgs.pop()\n // Options register as kebab-case flags (--action-id) and Commander stores them\n // camelized (actionId) - read back via camelCase(key) so snake_case schema keys\n // (action_id) map too, not just single-word ones.\n const rawInput: Record<string, unknown> = {}\n for (const key of Object.keys(action.input.shape)) {\n const value = opts[camelCase(key)]\n if (value !== undefined) { rawInput[key] = value }\n }\n action.args?.forEach((k, index) => { rawInput[String(k)] = args[index] })\n const { error, data } = action.input.safeParse(rawInput)\n if (error || !data) {\n handleCLIError(error)\n process.exit()\n }\n\n return data\n}\n\n/**\n * Map a Zod option type to its commander placeholder and, when the value needs\n * coercing from commander's raw string, a `parseArg`. Numeric/bigint/json fields\n * MUST carry a parser - without it every z.number() field fails Zod with\n * `expected number, received string`.\n */\nfunction optionSpec(type: z.ZodType): { placeholder: string; parseArg?: (value: string) => unknown } {\n if (type instanceof z.ZodNumber) {\n return { placeholder: '<number>', parseArg: (value) => Number(value) }\n }\n if (type instanceof z.ZodBigInt) {\n return { placeholder: '<bigint>', parseArg: (value) => { try { return BigInt(value) } catch { return value } } }\n }\n if (type instanceof z.ZodString || type instanceof z.ZodEnum) {\n return { placeholder: '<string>' }\n }\n if (type instanceof z.ZodObject || type instanceof z.ZodRecord || type instanceof z.ZodArray) {\n return { placeholder: '<json>', parseArg: JSON.parse }\n }\n throw new Error(`Invalid zod type: ${type.def.type}`)\n}\n\nfunction addCliOption(command: Command, key: string, type: z.ZodType, defaultValue: any, isArgument: boolean) {\n const description = type.description\n if (isArgument) {\n command.argument(`[${camelCase(key)}]`, description, defaultValue)\n return\n }\n const flag = kebabCase(key)\n if (type instanceof z.ZodBoolean) {\n command.option(`--${flag}`, description, defaultValue)\n command.option(`--no-${flag}`)\n return\n }\n const { placeholder, parseArg } = optionSpec(type)\n if (parseArg) {\n command.option(`--${flag} ${placeholder}`, description ?? '', parseArg, defaultValue)\n } else {\n command.option(`--${flag} ${placeholder}`, description, defaultValue)\n }\n}\n\nasync function runStreamingCommand(action: Action, input: object, context: SilkweaveContext) {\n const streamRun = action.run as ActionStreamRun<object, unknown>\n const iter = streamRun(input, context)\n for await (const chunk of iter) {\n const line = JSON.stringify(chunk) + '\\n'\n if (!process.stdout.write(line)) {\n await once(process.stdout, 'drain')\n }\n }\n}\n\nfunction registerCommand(program: Command, action: Action, options: SilkweaveOptions, context: SilkweaveContext) {\n const command = program.command(kebabCase(action.name)).description(action.description)\n const shape = action.input.shape\n const argKeys = action.args ?? []\n const argSet = new Set(argKeys)\n // Options first (order irrelevant), then positional arguments in `action.args`\n // order - not input-shape key order - so commander's positional slots line up\n // with how parseCLIInput reads them back (else the values are cross-assigned).\n for (const key of Object.keys(shape)) {\n if (argSet.has(key)) { continue }\n const [type, { defaultValue }] = unwrap(shape[key])\n addCliOption(command, key, type, defaultValue, false)\n }\n for (const argKey of argKeys) {\n const key = String(argKey)\n const [type, { defaultValue }] = unwrap(shape[key])\n addCliOption(command, key, type, defaultValue, true)\n }\n command.action((...args) => {\n const logger = createConsoleLogger()\n const input = parseCLIInput(action, args)\n const actionContext = context.fork({ logger, command })\n if (isStreamingAction(action)) {\n runStreamingCommand(action, input, actionContext).catch(handleCLIError)\n return\n }\n console.info(`${options.name} - ${action.name}`)\n const runFn = action.run as ActionRun<object, object>\n runFn(input, actionContext).then((result) => {\n logger.info(JSON.stringify(result, null, 2))\n }).catch(handleCLIError)\n })\n}\n\nexport const cli: AdapterFactory = () => {\n return (options, baseContext) => {\n const context = baseContext.fork({ adapter: 'cli' })\n const program = new Command()\n .name(options.name)\n .description(options.description)\n .version(options.version)\n\n return {\n context,\n start: async (actions) => {\n for (const action of actions) {\n registerCommand(program, action, options, context)\n }\n program.parse()\n },\n stop: async () => { }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/adapter/cli.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Action, ActionRun, ActionStreamRun, AdapterFactory, binarySchemaMeta, createConsoleLogger, isBinarySchema, isStreamingAction, resourceBytes, SilkweaveContext, SilkweaveError, SilkweaveOptions, toActionResource, unwrap, type ActionResource } from '@silkweave/core'\nimport { camelCase, kebabCase } from 'change-case'\nimport { Command } from 'commander'\nimport { once } from 'events'\nimport { writeFile } from 'fs/promises'\nimport z from 'zod/v4'\n\nfunction handleCLIError(error: unknown) {\n if (error instanceof SilkweaveError) {\n console.error(`[${error.code}] ${error.message}`)\n } else if (error instanceof z.ZodError) {\n console.error('Validation Error')\n for (const issue of error.issues) {\n console.error(`${issue.path}: ${issue.message}`)\n }\n } else if (error instanceof Error) {\n console.error(error.message)\n } else if (typeof error === 'string') {\n console.error(error)\n } else {\n console.error(JSON.stringify(error))\n }\n process.exitCode = 1\n}\n\nfunction parseCLIInput(action: Action, args: any[]) {\n const tmpArgs = args.slice(0, -1)\n const opts = tmpArgs.pop()\n // Options register as kebab-case flags (--action-id) and Commander stores them\n // camelized (actionId) - read back via camelCase(key) so snake_case schema keys\n // (action_id) map too, not just single-word ones.\n const rawInput: Record<string, unknown> = {}\n for (const key of Object.keys(action.input.shape)) {\n const value = opts[camelCase(key)]\n if (value !== undefined) { rawInput[key] = value }\n }\n action.args?.forEach((k, index) => { rawInput[String(k)] = args[index] })\n const { error, data } = action.input.safeParse(rawInput)\n if (error || !data) {\n handleCLIError(error)\n process.exit()\n }\n\n return data\n}\n\n/**\n * Map a Zod option type to its commander placeholder and, when the value needs\n * coercing from commander's raw string, a `parseArg`. Numeric/bigint/json fields\n * MUST carry a parser - without it every z.number() field fails Zod with\n * `expected number, received string`.\n */\nfunction optionSpec(type: z.ZodType): { placeholder: string; parseArg?: (value: string) => unknown } {\n if (type instanceof z.ZodNumber) {\n return { placeholder: '<number>', parseArg: (value) => Number(value) }\n }\n if (type instanceof z.ZodBigInt) {\n return { placeholder: '<bigint>', parseArg: (value) => { try { return BigInt(value) } catch { return value } } }\n }\n if (type instanceof z.ZodString || type instanceof z.ZodEnum) {\n return { placeholder: '<string>' }\n }\n if (type instanceof z.ZodObject || type instanceof z.ZodRecord || type instanceof z.ZodArray) {\n return { placeholder: '<json>', parseArg: JSON.parse }\n }\n throw new Error(`Invalid zod type: ${type.def.type}`)\n}\n\nfunction addCliOption(command: Command, key: string, type: z.ZodType, defaultValue: any, isArgument: boolean) {\n const description = type.description\n if (isArgument) {\n command.argument(`[${camelCase(key)}]`, description, defaultValue)\n return\n }\n const flag = kebabCase(key)\n if (type instanceof z.ZodBoolean) {\n command.option(`--${flag}`, description, defaultValue)\n command.option(`--no-${flag}`)\n return\n }\n const { placeholder, parseArg } = optionSpec(type)\n if (parseArg) {\n command.option(`--${flag} ${placeholder}`, description ?? '', parseArg, defaultValue)\n } else {\n command.option(`--${flag} ${placeholder}`, description, defaultValue)\n }\n}\n\n/** File extension derived from a media type's subtype (`image/png` -> `png`). */\nfunction extensionFromMime(mimeType: string): string {\n const subtype = mimeType.split(';')[0].split('/')[1]?.split('+')[0]?.trim()\n return subtype || 'bin'\n}\n\n/**\n * Deliver a resource result terminal-appropriately: raw bytes to stdout when\n * piped (`my-cli screenshot > shot.png`), otherwise written to `--output` or a\n * file named after the resource - binary is never dumped onto an interactive\n * terminal. Status lines go to stderr so a piped stdout stays byte-clean.\n */\nasync function writeResourceResult(res: ActionResource, outputPath: string | undefined) {\n const bytes = resourceBytes(res)\n if (res.description) { console.error(res.description) }\n if (!outputPath && !process.stdout.isTTY) {\n if (!process.stdout.write(bytes)) { await once(process.stdout, 'drain') }\n return\n }\n const target = outputPath ?? res.name ?? `resource.${extensionFromMime(res.mimeType)}`\n await writeFile(target, bytes)\n console.error(`Wrote ${bytes.length} bytes (${res.mimeType}) to ${target}`)\n}\n\nasync function runStreamingCommand(action: Action, input: object, context: SilkweaveContext) {\n const streamRun = action.run as ActionStreamRun<object, unknown>\n const iter = streamRun(input, context)\n for await (const chunk of iter) {\n const line = JSON.stringify(chunk) + '\\n'\n if (!process.stdout.write(line)) {\n await once(process.stdout, 'drain')\n }\n }\n}\n\nfunction registerCommand(program: Command, action: Action, options: SilkweaveOptions, context: SilkweaveContext) {\n const command = program.command(kebabCase(action.name)).description(action.description)\n const shape = action.input.shape\n const argKeys = action.args ?? []\n const argSet = new Set(argKeys)\n // Binary actions get an --output flag - unless the input schema claims the\n // name, in which case the schema field wins and only pipe/TTY behavior applies.\n const binaryOutput = isBinarySchema(action.output) && !('output' in shape)\n if (binaryOutput) {\n command.option('-o, --output <path>', 'Write the resource to this file (default: the resource name)')\n }\n // Options first (order irrelevant), then positional arguments in `action.args`\n // order - not input-shape key order - so commander's positional slots line up\n // with how parseCLIInput reads them back (else the values are cross-assigned).\n for (const key of Object.keys(shape)) {\n if (argSet.has(key)) { continue }\n const [type, { defaultValue }] = unwrap(shape[key])\n addCliOption(command, key, type, defaultValue, false)\n }\n for (const argKey of argKeys) {\n const key = String(argKey)\n const [type, { defaultValue }] = unwrap(shape[key])\n addCliOption(command, key, type, defaultValue, true)\n }\n command.action((...args) => {\n const logger = createConsoleLogger()\n const input = parseCLIInput(action, args)\n const actionContext = context.fork({ logger, command })\n if (isStreamingAction(action)) {\n runStreamingCommand(action, input, actionContext).catch(handleCLIError)\n return\n }\n // Binary actions keep stdout byte-clean (a piped stdout carries the\n // payload), so the banner goes to stderr for them.\n if (binaryOutput) {\n console.error(`${options.name} - ${action.name}`)\n } else {\n console.info(`${options.name} - ${action.name}`)\n }\n const runFn = action.run as ActionRun<object, object>\n runFn(input, actionContext).then(async (result) => {\n const res = await toActionResource(result, binarySchemaMeta(action.output))\n if (res) {\n await writeResourceResult(res, binaryOutput ? command.opts<{ output?: string }>().output : undefined)\n return\n }\n logger.info(JSON.stringify(result, null, 2))\n }).catch(handleCLIError)\n })\n}\n\nexport const cli: AdapterFactory = () => {\n return (options, baseContext) => {\n const context = baseContext.fork({ adapter: 'cli' })\n const program = new Command()\n .name(options.name)\n .description(options.description)\n .version(options.version)\n\n return {\n context,\n start: async (actions) => {\n for (const action of actions) {\n registerCommand(program, action, options, context)\n }\n program.parse()\n },\n stop: async () => { }\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silkweave/cli",
3
- "version": "4.3.0",
3
+ "version": "5.0.0",
4
4
  "description": "Silkweave CLI Adapter",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.silkweave.dev",
@@ -29,7 +29,7 @@
29
29
  "change-case": "^5.4.4",
30
30
  "commander": "^13.1.0",
31
31
  "zod": "^3.25.0",
32
- "@silkweave/core": "4.3.0"
32
+ "@silkweave/core": "5.0.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@eslint/js": "^10.0.1",