swytchcode-runtime 0.1.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 +109 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +23 -0
- package/dist/exec.d.ts +9 -0
- package/dist/exec.js +96 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +8 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.js +2 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# @swytchcode/runtime
|
|
2
|
+
|
|
3
|
+
Thin runtime wrapper around the Swytchcode CLI. Calls `swytchcode exec` for you so you can stay in TypeScript/JavaScript without shell boilerplate.
|
|
4
|
+
|
|
5
|
+
**Requires:** The `swytchcode` binary must be installed and on your `PATH`.
|
|
6
|
+
|
|
7
|
+
By default, the runtime runs Swytchcode in **JSON mode**: the CLI is invoked with `--json` and stdout must be valid JSON; empty stdout or parse failure throws. For **raw** output, use `output: "raw"` (or `raw: true`). For **streaming** output, use the Swytchcode CLI directly; this library does not support stream mode.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @swytchcode/runtime
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Use
|
|
16
|
+
|
|
17
|
+
### JSON mode (default)
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { exec } from "@swytchcode/runtime";
|
|
21
|
+
|
|
22
|
+
const result = await exec("api.account.create", {
|
|
23
|
+
email: "test@example.com",
|
|
24
|
+
});
|
|
25
|
+
// result is parsed JSON (unknown)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Equivalent to: `swytchcode exec api.account.create --json` with input on stdin.
|
|
29
|
+
|
|
30
|
+
### Raw mode
|
|
31
|
+
|
|
32
|
+
Get stdout as a string instead of parsing JSON:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { exec } from "@swytchcode/runtime";
|
|
36
|
+
|
|
37
|
+
const output = await exec("api.report.export", { id: "123" }, { raw: true });
|
|
38
|
+
// output is the raw stdout string
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Equivalent to: `swytchcode exec api.report.export --raw` with input on stdin.
|
|
42
|
+
|
|
43
|
+
### Options
|
|
44
|
+
|
|
45
|
+
- **`cwd`** – Working directory for the process (default: `process.cwd()`).
|
|
46
|
+
- **`env`** – Extra environment variables (merged with `process.env`).
|
|
47
|
+
- **`output`** – `"json"` (default), `"raw"`, or `"stream"`. Default is JSON (stdout must be valid JSON; parse failure throws). Use `"raw"` to get stdout as a string. `"stream"` is not supported and will throw; use the CLI directly for streaming.
|
|
48
|
+
- **`raw`** – If `true`, same as `output: "raw"`. Kept for backward compatibility.
|
|
49
|
+
- **`debug`** – If `true`, log spawn args, cwd, exit status, and stdout/stderr lengths to stderr.
|
|
50
|
+
|
|
51
|
+
**Debug logs** are also enabled when `SWYTCHCODE_RUNTIME_DEBUG=1` or `SWYTCHCODE_RUNTIME_DEBUG=true` (no code change):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
SWYTCHCODE_RUNTIME_DEBUG=1 node index.js
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or in code: `exec("shell.integration.list", undefined, { debug: true })`.
|
|
58
|
+
|
|
59
|
+
### Error handling
|
|
60
|
+
|
|
61
|
+
Failures throw `SwytchcodeError` (spawn error, non-zero exit, signal, empty stdout in JSON mode, invalid JSON). Use the type guard when you need to narrow:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { exec, isSwytchcodeError } from "@swytchcode/runtime";
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await exec("api.account.create", { email: "test@example.com" });
|
|
68
|
+
} catch (e) {
|
|
69
|
+
if (isSwytchcodeError(e)) {
|
|
70
|
+
console.error(e.message, e.cause);
|
|
71
|
+
}
|
|
72
|
+
throw e;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## What this library is
|
|
77
|
+
|
|
78
|
+
- A thin process wrapper over the Swytchcode shell.
|
|
79
|
+
- A stable call surface: `exec(canonicalId, input?, options?)`.
|
|
80
|
+
- A convenience for developers and IDE agents.
|
|
81
|
+
|
|
82
|
+
## What this library is not
|
|
83
|
+
|
|
84
|
+
- An HTTP SDK or API client.
|
|
85
|
+
- A replacement for the shell. All authority stays in the Swytchcode CLI.
|
|
86
|
+
|
|
87
|
+
This library **must not** and **does not**:
|
|
88
|
+
|
|
89
|
+
- Read `.swytchcode/*` or parse `tooling.json`.
|
|
90
|
+
- Validate schemas or add retries/timeouts.
|
|
91
|
+
- Call the registry or embed auth logic.
|
|
92
|
+
|
|
93
|
+
It is a pipe, not a brain.
|
|
94
|
+
|
|
95
|
+
## For Cursor / IDE agents
|
|
96
|
+
|
|
97
|
+
Generate code that uses this runtime instead of subprocess boilerplate.
|
|
98
|
+
|
|
99
|
+
**Preferred:**
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { exec } from "@swytchcode/runtime";
|
|
103
|
+
|
|
104
|
+
export async function createAccount(input: { email: string }) {
|
|
105
|
+
return exec("api.account.create", input);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Avoid:** subprocess boilerplate, HTTP calls, or config parsing. Use `exec(...)` and let the CLI handle execution and policy.
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when `exec()` fails: spawn error, non-zero exit, signal, or invalid JSON output.
|
|
3
|
+
*/
|
|
4
|
+
export declare class SwytchcodeError extends Error {
|
|
5
|
+
readonly cause?: unknown;
|
|
6
|
+
constructor(message: string, cause?: unknown);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Type guard for SwytchcodeError.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isSwytchcodeError(e: unknown): e is SwytchcodeError;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SwytchcodeError = void 0;
|
|
4
|
+
exports.isSwytchcodeError = isSwytchcodeError;
|
|
5
|
+
/**
|
|
6
|
+
* Thrown when `exec()` fails: spawn error, non-zero exit, signal, or invalid JSON output.
|
|
7
|
+
*/
|
|
8
|
+
class SwytchcodeError extends Error {
|
|
9
|
+
cause;
|
|
10
|
+
constructor(message, cause) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "SwytchcodeError";
|
|
13
|
+
this.cause = cause;
|
|
14
|
+
Object.setPrototypeOf(this, SwytchcodeError.prototype);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.SwytchcodeError = SwytchcodeError;
|
|
18
|
+
/**
|
|
19
|
+
* Type guard for SwytchcodeError.
|
|
20
|
+
*/
|
|
21
|
+
function isSwytchcodeError(e) {
|
|
22
|
+
return e instanceof SwytchcodeError;
|
|
23
|
+
}
|
package/dist/exec.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ExecOptions, ExecResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Run `swytchcode exec <canonicalId>` with optional JSON input on stdin.
|
|
4
|
+
* Default is JSON mode (stdout must be valid JSON; empty or parse failure throws).
|
|
5
|
+
* Use output: "raw" or raw: true for raw stdout string. Stream mode is not supported.
|
|
6
|
+
*
|
|
7
|
+
* Enable logs: pass `{ debug: true }` or set env SWYTCHCODE_RUNTIME_DEBUG=1
|
|
8
|
+
*/
|
|
9
|
+
export declare function exec(canonicalId: string, input?: unknown, options?: ExecOptions): Promise<ExecResult>;
|
package/dist/exec.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.exec = exec;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const errors_js_1 = require("./errors.js");
|
|
6
|
+
const LOG_PREFIX = "[swytchcode-runtime]";
|
|
7
|
+
function isDebug(options) {
|
|
8
|
+
return (options.debug === true ||
|
|
9
|
+
process.env.SWYTCHCODE_RUNTIME_DEBUG === "1" ||
|
|
10
|
+
process.env.SWYTCHCODE_RUNTIME_DEBUG === "true");
|
|
11
|
+
}
|
|
12
|
+
function log(debug, msg, detail) {
|
|
13
|
+
if (!debug)
|
|
14
|
+
return;
|
|
15
|
+
const line = detail ? `${LOG_PREFIX} ${msg} ${detail}\n` : `${LOG_PREFIX} ${msg}\n`;
|
|
16
|
+
process.stderr.write(line);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Run `swytchcode exec <canonicalId>` with optional JSON input on stdin.
|
|
20
|
+
* Default is JSON mode (stdout must be valid JSON; empty or parse failure throws).
|
|
21
|
+
* Use output: "raw" or raw: true for raw stdout string. Stream mode is not supported.
|
|
22
|
+
*
|
|
23
|
+
* Enable logs: pass `{ debug: true }` or set env SWYTCHCODE_RUNTIME_DEBUG=1
|
|
24
|
+
*/
|
|
25
|
+
function exec(canonicalId, input, options = {}) {
|
|
26
|
+
const debug = isDebug(options);
|
|
27
|
+
const canonicalIdTrimmed = canonicalId.trim();
|
|
28
|
+
if (canonicalIdTrimmed.length === 0) {
|
|
29
|
+
log(debug, "reject:", "canonicalId is empty");
|
|
30
|
+
return Promise.reject(new errors_js_1.SwytchcodeError("canonicalId must be a non-empty string", undefined));
|
|
31
|
+
}
|
|
32
|
+
const outputMode = options.output ?? (options.raw === true ? "raw" : "json");
|
|
33
|
+
if (outputMode === "stream") {
|
|
34
|
+
log(debug, "reject:", "stream mode not supported");
|
|
35
|
+
return Promise.reject(new errors_js_1.SwytchcodeError("Stream mode is not supported; use the Swytchcode CLI directly", undefined));
|
|
36
|
+
}
|
|
37
|
+
const raw = outputMode === "raw";
|
|
38
|
+
const args = ["exec", canonicalIdTrimmed, raw ? "--raw" : "--json"];
|
|
39
|
+
const cwd = options.cwd ?? process.cwd();
|
|
40
|
+
const hasInput = input !== undefined && input !== null;
|
|
41
|
+
log(debug, "spawn:", `swytchcode ${args.join(" ")}`);
|
|
42
|
+
log(debug, "cwd:", cwd);
|
|
43
|
+
log(debug, "stdin:", hasInput ? `JSON (${JSON.stringify(input).length} chars)` : "none");
|
|
44
|
+
const result = (0, node_child_process_1.spawnSync)("swytchcode", args, {
|
|
45
|
+
cwd,
|
|
46
|
+
env: { ...process.env, ...options.env },
|
|
47
|
+
input: hasInput ? JSON.stringify(input) : undefined,
|
|
48
|
+
encoding: "utf8",
|
|
49
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB
|
|
50
|
+
});
|
|
51
|
+
const stdoutRaw = result.stdout ?? "";
|
|
52
|
+
const stderrRaw = result.stderr ?? "";
|
|
53
|
+
const stdout = stdoutRaw.trim();
|
|
54
|
+
const stderr = stderrRaw.trim();
|
|
55
|
+
log(debug, "exit status:", String(result.status));
|
|
56
|
+
log(debug, "exit signal:", String(result.signal ?? "none"));
|
|
57
|
+
log(debug, "stdout length:", String(stdoutRaw.length));
|
|
58
|
+
log(debug, "stderr length:", String(stderrRaw.length));
|
|
59
|
+
if (stdoutRaw.length > 0) {
|
|
60
|
+
const preview = stdoutRaw.length > 400 ? stdoutRaw.slice(0, 400) + "..." : stdoutRaw;
|
|
61
|
+
log(debug, "stdout preview:", JSON.stringify(preview));
|
|
62
|
+
}
|
|
63
|
+
if (stderrRaw.length > 0) {
|
|
64
|
+
const preview = stderrRaw.length > 400 ? stderrRaw.slice(0, 400) + "..." : stderrRaw;
|
|
65
|
+
log(debug, "stderr preview:", JSON.stringify(preview));
|
|
66
|
+
}
|
|
67
|
+
if (result.error) {
|
|
68
|
+
log(debug, "reject:", "spawn error");
|
|
69
|
+
return Promise.reject(new errors_js_1.SwytchcodeError("Failed to spawn swytchcode", result.error));
|
|
70
|
+
}
|
|
71
|
+
if (result.status !== 0) {
|
|
72
|
+
const message = result.signal != null
|
|
73
|
+
? `swytchcode exec failed (signal ${String(result.signal)})`
|
|
74
|
+
: stderr || "swytchcode exec failed";
|
|
75
|
+
log(debug, "reject:", `status ${result.status}, ${message.slice(0, 100)}`);
|
|
76
|
+
return Promise.reject(new errors_js_1.SwytchcodeError(message, result.status ?? result.signal ?? stderr));
|
|
77
|
+
}
|
|
78
|
+
if (raw) {
|
|
79
|
+
log(debug, "resolve:", `raw stdout (${(result.stdout ?? "").length} chars)`);
|
|
80
|
+
return Promise.resolve(result.stdout ?? "");
|
|
81
|
+
}
|
|
82
|
+
// JSON mode: stdout only; no stderr fallback. Empty or invalid → throw.
|
|
83
|
+
if (stdout.length === 0) {
|
|
84
|
+
log(debug, "reject:", "empty stdout in JSON mode");
|
|
85
|
+
return Promise.reject(new errors_js_1.SwytchcodeError("Empty stdout in JSON mode; swytchcode must write JSON to stdout", undefined));
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const parsed = JSON.parse(stdout);
|
|
89
|
+
log(debug, "resolve:", "parsed JSON ok");
|
|
90
|
+
return Promise.resolve(parsed);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
log(debug, "reject:", "invalid JSON");
|
|
94
|
+
return Promise.reject(new errors_js_1.SwytchcodeError("Invalid JSON output from swytchcode", stdout));
|
|
95
|
+
}
|
|
96
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isSwytchcodeError = exports.SwytchcodeError = exports.exec = void 0;
|
|
4
|
+
var exec_js_1 = require("./exec.js");
|
|
5
|
+
Object.defineProperty(exports, "exec", { enumerable: true, get: function () { return exec_js_1.exec; } });
|
|
6
|
+
var errors_js_1 = require("./errors.js");
|
|
7
|
+
Object.defineProperty(exports, "SwytchcodeError", { enumerable: true, get: function () { return errors_js_1.SwytchcodeError; } });
|
|
8
|
+
Object.defineProperty(exports, "isSwytchcodeError", { enumerable: true, get: function () { return errors_js_1.isSwytchcodeError; } });
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Output mode: JSON (default), raw string, or stream (not supported in this runtime). */
|
|
2
|
+
export type OutputMode = "json" | "raw" | "stream";
|
|
3
|
+
export interface ExecOptions {
|
|
4
|
+
/** Working directory for the swytchcode process. Defaults to `process.cwd()`. */
|
|
5
|
+
cwd?: string;
|
|
6
|
+
/** Extra environment variables merged with `process.env`. */
|
|
7
|
+
env?: Record<string, string>;
|
|
8
|
+
/**
|
|
9
|
+
* Output mode. Default is `"json"` (stdout must be valid JSON; parse failure throws).
|
|
10
|
+
* Use `"raw"` to get stdout as a string. `"stream"` is not supported; use the CLI directly.
|
|
11
|
+
*/
|
|
12
|
+
output?: OutputMode;
|
|
13
|
+
/** If true, same as `output: "raw"`. Kept for backward compatibility. */
|
|
14
|
+
raw?: boolean;
|
|
15
|
+
/** If true, log spawn/capture details to process.stderr (for debugging). */
|
|
16
|
+
debug?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Result of `exec()` in JSON mode: parsed stdout. In raw mode the result is a string. */
|
|
19
|
+
export type ExecResult = unknown;
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "swytchcode-runtime",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Thin runtime wrapper around the Swytchcode CLI",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist"],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {},
|
|
20
|
+
"peerDependencies": {},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^18.0.0",
|
|
23
|
+
"typescript": "^5.0.0"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://gitlab.com/swytchcode/runtime-libraries"
|
|
32
|
+
},
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"keywords": ["swytchcode", "cli", "runtime", "exec"]
|
|
35
|
+
}
|