spell-runtime 1.0.0 → 1.2.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.
Files changed (54) hide show
  1. package/README.md +109 -7
  2. package/README.txt +90 -7
  3. package/dist/api/index.d.ts +1 -0
  4. package/dist/api/index.js +70 -0
  5. package/dist/api/index.js.map +1 -0
  6. package/dist/api/server.d.ts +19 -0
  7. package/dist/api/server.js +868 -0
  8. package/dist/api/server.js.map +1 -0
  9. package/dist/api/ui.d.ts +2 -0
  10. package/dist/api/ui.js +474 -0
  11. package/dist/api/ui.js.map +1 -0
  12. package/dist/bundle/install.js +9 -0
  13. package/dist/bundle/install.js.map +1 -1
  14. package/dist/cli/index.js +93 -0
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/contract/buttonRegistry.d.ts +21 -0
  17. package/dist/contract/buttonRegistry.js +103 -0
  18. package/dist/contract/buttonRegistry.js.map +1 -0
  19. package/dist/logging/executionLog.js +3 -1
  20. package/dist/logging/executionLog.js.map +1 -1
  21. package/dist/runner/cast.js +43 -3
  22. package/dist/runner/cast.js.map +1 -1
  23. package/dist/runner/dockerRunner.d.ts +9 -0
  24. package/dist/runner/dockerRunner.js +121 -0
  25. package/dist/runner/dockerRunner.js.map +1 -0
  26. package/dist/runner/spell-runner.d.ts +11 -0
  27. package/dist/runner/spell-runner.js +144 -0
  28. package/dist/runner/spell-runner.js.map +1 -0
  29. package/dist/signature/bundleDigest.d.ts +6 -0
  30. package/dist/signature/bundleDigest.js +89 -0
  31. package/dist/signature/bundleDigest.js.map +1 -0
  32. package/dist/signature/signatureFile.d.ts +12 -0
  33. package/dist/signature/signatureFile.js +79 -0
  34. package/dist/signature/signatureFile.js.map +1 -0
  35. package/dist/signature/signing.d.ts +27 -0
  36. package/dist/signature/signing.js +87 -0
  37. package/dist/signature/signing.js.map +1 -0
  38. package/dist/signature/trustStore.d.ts +20 -0
  39. package/dist/signature/trustStore.js +173 -0
  40. package/dist/signature/trustStore.js.map +1 -0
  41. package/dist/signature/verify.d.ts +12 -0
  42. package/dist/signature/verify.js +119 -0
  43. package/dist/signature/verify.js.map +1 -0
  44. package/dist/types.d.ts +8 -0
  45. package/dist/util/paths.d.ts +2 -0
  46. package/dist/util/paths.js +9 -0
  47. package/dist/util/paths.js.map +1 -1
  48. package/dist/util/platform.d.ts +2 -0
  49. package/dist/util/platform.js +38 -0
  50. package/dist/util/platform.js.map +1 -1
  51. package/dist/util/redact.d.ts +2 -0
  52. package/dist/util/redact.js +66 -0
  53. package/dist/util/redact.js.map +1 -0
  54. package/package.json +5 -2
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.publisherFromId = publisherFromId;
7
+ exports.toPublisherKey = toPublisherKey;
8
+ exports.publisherTrustFilePath = publisherTrustFilePath;
9
+ exports.loadPublisherTrust = loadPublisherTrust;
10
+ exports.upsertTrustedPublisherKey = upsertTrustedPublisherKey;
11
+ exports.removeTrustedPublisher = removeTrustedPublisher;
12
+ exports.listTrustedPublishers = listTrustedPublishers;
13
+ const promises_1 = require("node:fs/promises");
14
+ const node_path_1 = __importDefault(require("node:path"));
15
+ const errors_1 = require("../util/errors");
16
+ const paths_1 = require("../util/paths");
17
+ function publisherFromId(id) {
18
+ const idx = id.indexOf("/");
19
+ return idx === -1 ? id : id.slice(0, idx);
20
+ }
21
+ function toPublisherKey(publisher) {
22
+ return Buffer.from(publisher, "utf8").toString("base64url");
23
+ }
24
+ function publisherTrustFilePath(publisher) {
25
+ const key = toPublisherKey(publisher);
26
+ return node_path_1.default.join((0, paths_1.trustedPublishersRoot)(), `${key}.json`);
27
+ }
28
+ async function loadPublisherTrust(publisher) {
29
+ const filePath = publisherTrustFilePath(publisher);
30
+ let raw;
31
+ try {
32
+ raw = await (0, promises_1.readFile)(filePath, "utf8");
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ let parsed;
38
+ try {
39
+ parsed = JSON.parse(raw);
40
+ }
41
+ catch {
42
+ throw new errors_1.SpellError(`failed to parse trust file: ${filePath}`);
43
+ }
44
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
45
+ throw new errors_1.SpellError(`trust file must be a JSON object: ${filePath}`);
46
+ }
47
+ const obj = parsed;
48
+ const version = readRequiredString(obj, "version");
49
+ if (version !== "v1") {
50
+ throw new errors_1.SpellError(`unsupported trust file version: ${version}`);
51
+ }
52
+ const publisherValue = readRequiredString(obj, "publisher");
53
+ if (publisherValue !== publisher) {
54
+ throw new errors_1.SpellError(`trust file publisher mismatch: expected '${publisher}', got '${publisherValue}'`);
55
+ }
56
+ const keysRaw = obj.keys;
57
+ if (!Array.isArray(keysRaw) || keysRaw.length === 0) {
58
+ throw new errors_1.SpellError("trust file keys must be a non-empty array");
59
+ }
60
+ const keys = keysRaw.map((entry, idx) => {
61
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
62
+ throw new errors_1.SpellError(`trust file keys[${idx}] must be an object`);
63
+ }
64
+ const e = entry;
65
+ const keyId = readRequiredString(e, "key_id");
66
+ const algorithm = readRequiredString(e, "algorithm");
67
+ if (algorithm !== "ed25519") {
68
+ throw new errors_1.SpellError(`unsupported trust key algorithm: ${algorithm}`);
69
+ }
70
+ const publicKey = readRequiredString(e, "public_key");
71
+ if (!/^[A-Za-z0-9_-]+$/.test(publicKey)) {
72
+ throw new errors_1.SpellError("trust key public_key must be base64url");
73
+ }
74
+ return { key_id: keyId, algorithm: "ed25519", public_key: publicKey };
75
+ });
76
+ assertUniqueKeyIds(keys);
77
+ return {
78
+ version: "v1",
79
+ publisher,
80
+ keys
81
+ };
82
+ }
83
+ async function upsertTrustedPublisherKey(publisher, key) {
84
+ await (0, promises_1.mkdir)((0, paths_1.trustedPublishersRoot)(), { recursive: true });
85
+ const existing = await loadPublisherTrust(publisher);
86
+ const next = existing ?? { version: "v1", publisher, keys: [] };
87
+ const filtered = next.keys.filter((entry) => entry.key_id !== key.key_id);
88
+ filtered.push(key);
89
+ filtered.sort((a, b) => a.key_id.localeCompare(b.key_id));
90
+ const payload = {
91
+ version: "v1",
92
+ publisher,
93
+ keys: filtered
94
+ };
95
+ const filePath = publisherTrustFilePath(publisher);
96
+ await (0, promises_1.writeFile)(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
97
+ }
98
+ async function removeTrustedPublisher(publisher) {
99
+ const filePath = publisherTrustFilePath(publisher);
100
+ const existed = await (0, promises_1.access)(filePath).then(() => true).catch(() => false);
101
+ if (!existed)
102
+ return false;
103
+ await (0, promises_1.rm)(filePath, { force: true });
104
+ return true;
105
+ }
106
+ async function listTrustedPublishers() {
107
+ const dir = (0, paths_1.trustedPublishersRoot)();
108
+ const entries = await (0, promises_1.readdir)(dir).catch(() => []);
109
+ const out = [];
110
+ for (const entry of entries) {
111
+ if (!entry.endsWith(".json"))
112
+ continue;
113
+ const filePath = node_path_1.default.join(dir, entry);
114
+ let raw;
115
+ try {
116
+ raw = await (0, promises_1.readFile)(filePath, "utf8");
117
+ }
118
+ catch {
119
+ continue;
120
+ }
121
+ let parsed;
122
+ try {
123
+ parsed = JSON.parse(raw);
124
+ }
125
+ catch {
126
+ continue;
127
+ }
128
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
129
+ continue;
130
+ }
131
+ const obj = parsed;
132
+ if (obj.version !== "v1")
133
+ continue;
134
+ if (typeof obj.publisher !== "string" || !obj.publisher.trim())
135
+ continue;
136
+ if (!Array.isArray(obj.keys))
137
+ continue;
138
+ const publisher = obj.publisher.trim();
139
+ const keys = [];
140
+ for (const k of obj.keys) {
141
+ if (!k || typeof k !== "object" || Array.isArray(k))
142
+ continue;
143
+ const kk = k;
144
+ if (typeof kk.key_id !== "string" || typeof kk.public_key !== "string")
145
+ continue;
146
+ if (kk.algorithm !== "ed25519")
147
+ continue;
148
+ keys.push({ key_id: kk.key_id, algorithm: "ed25519", public_key: kk.public_key });
149
+ }
150
+ if (keys.length === 0)
151
+ continue;
152
+ out.push({ publisher, keys });
153
+ }
154
+ out.sort((a, b) => a.publisher.localeCompare(b.publisher));
155
+ return out;
156
+ }
157
+ function readRequiredString(obj, key) {
158
+ const value = obj[key];
159
+ if (typeof value !== "string" || !value.trim()) {
160
+ throw new errors_1.SpellError(`missing '${key}' string`);
161
+ }
162
+ return value.trim();
163
+ }
164
+ function assertUniqueKeyIds(keys) {
165
+ const seen = new Set();
166
+ for (const key of keys) {
167
+ if (seen.has(key.key_id)) {
168
+ throw new errors_1.SpellError(`duplicate trust key_id: ${key.key_id}`);
169
+ }
170
+ seen.add(key.key_id);
171
+ }
172
+ }
173
+ //# sourceMappingURL=trustStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trustStore.js","sourceRoot":"","sources":["../../src/signature/trustStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { SpellBundleManifest } from "../types";
2
+ export type SignatureStatus = "verified" | "unsigned" | "untrusted" | "invalid";
3
+ export interface SignatureVerificationResult {
4
+ ok: boolean;
5
+ status: SignatureStatus;
6
+ publisher: string;
7
+ key_id?: string;
8
+ digest?: string;
9
+ message: string;
10
+ }
11
+ export declare function verifyBundleSignature(manifest: SpellBundleManifest, bundlePath: string): Promise<SignatureVerificationResult>;
12
+ export declare function enforceSignatureOrThrow(result: SignatureVerificationResult): void;
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verifyBundleSignature = verifyBundleSignature;
4
+ exports.enforceSignatureOrThrow = enforceSignatureOrThrow;
5
+ const node_crypto_1 = require("node:crypto");
6
+ const errors_1 = require("../util/errors");
7
+ const bundleDigest_1 = require("./bundleDigest");
8
+ const signatureFile_1 = require("./signatureFile");
9
+ const trustStore_1 = require("./trustStore");
10
+ async function verifyBundleSignature(manifest, bundlePath) {
11
+ const publisher = (0, trustStore_1.publisherFromId)(manifest.id);
12
+ const sig = await (0, signatureFile_1.readSignatureFile)(bundlePath);
13
+ if (!sig) {
14
+ return {
15
+ ok: false,
16
+ status: "unsigned",
17
+ publisher,
18
+ message: "spell.sig.json not found"
19
+ };
20
+ }
21
+ if (sig.publisher !== publisher) {
22
+ return {
23
+ ok: false,
24
+ status: "invalid",
25
+ publisher,
26
+ key_id: sig.key_id,
27
+ digest: sig.digest.value,
28
+ message: `signature publisher mismatch: expected '${publisher}', got '${sig.publisher}'`
29
+ };
30
+ }
31
+ const trust = await (0, trustStore_1.loadPublisherTrust)(publisher);
32
+ if (!trust) {
33
+ return {
34
+ ok: false,
35
+ status: "untrusted",
36
+ publisher,
37
+ key_id: sig.key_id,
38
+ digest: sig.digest.value,
39
+ message: `no trusted key for publisher: ${publisher}`
40
+ };
41
+ }
42
+ const key = trust.keys.find((entry) => entry.key_id === sig.key_id);
43
+ if (!key) {
44
+ return {
45
+ ok: false,
46
+ status: "untrusted",
47
+ publisher,
48
+ key_id: sig.key_id,
49
+ digest: sig.digest.value,
50
+ message: `trusted key_id not found: ${sig.key_id}`
51
+ };
52
+ }
53
+ const digest = await (0, bundleDigest_1.computeBundleDigest)(bundlePath);
54
+ if (digest.valueHex !== sig.digest.value) {
55
+ return {
56
+ ok: false,
57
+ status: "invalid",
58
+ publisher,
59
+ key_id: sig.key_id,
60
+ digest: digest.valueHex,
61
+ message: "digest mismatch"
62
+ };
63
+ }
64
+ const signatureBytes = decodeBase64Url(sig.signature, "spell.sig.json.signature");
65
+ const publicKeyDer = decodeBase64Url(key.public_key, "trust public_key");
66
+ let publicKey;
67
+ try {
68
+ publicKey = (0, node_crypto_1.createPublicKey)({ key: publicKeyDer, format: "der", type: "spki" });
69
+ }
70
+ catch (error) {
71
+ return {
72
+ ok: false,
73
+ status: "untrusted",
74
+ publisher,
75
+ key_id: sig.key_id,
76
+ digest: digest.valueHex,
77
+ message: `invalid trusted public key: ${error.message}`
78
+ };
79
+ }
80
+ const ok = (0, node_crypto_1.verify)(null, digest.value, publicKey, signatureBytes);
81
+ if (!ok) {
82
+ return {
83
+ ok: false,
84
+ status: "invalid",
85
+ publisher,
86
+ key_id: sig.key_id,
87
+ digest: digest.valueHex,
88
+ message: "signature verification failed"
89
+ };
90
+ }
91
+ return {
92
+ ok: true,
93
+ status: "verified",
94
+ publisher,
95
+ key_id: sig.key_id,
96
+ digest: digest.valueHex,
97
+ message: "verified"
98
+ };
99
+ }
100
+ function enforceSignatureOrThrow(result) {
101
+ if (result.ok)
102
+ return;
103
+ if (result.status === "unsigned") {
104
+ throw new errors_1.SpellError("signature required: spell.sig.json not found");
105
+ }
106
+ if (result.status === "untrusted") {
107
+ throw new errors_1.SpellError(`signature required: ${result.message}`);
108
+ }
109
+ throw new errors_1.SpellError(`signature required: ${result.message}`);
110
+ }
111
+ function decodeBase64Url(value, label) {
112
+ try {
113
+ return Buffer.from(value, "base64url");
114
+ }
115
+ catch (error) {
116
+ throw new errors_1.SpellError(`invalid base64url for ${label}: ${error.message}`);
117
+ }
118
+ }
119
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/signature/verify.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -81,6 +81,13 @@ export interface ExecutionLog {
81
81
  id: string;
82
82
  version: string;
83
83
  input: Record<string, unknown>;
84
+ signature?: {
85
+ required: boolean;
86
+ status: "skipped" | "verified" | "unsigned" | "untrusted" | "invalid";
87
+ publisher?: string;
88
+ key_id?: string;
89
+ digest?: string;
90
+ };
84
91
  summary: {
85
92
  risk: SpellRisk;
86
93
  billing: SpellBilling;
@@ -100,6 +107,7 @@ export interface CastOptions {
100
107
  dryRun: boolean;
101
108
  yes: boolean;
102
109
  allowBilling: boolean;
110
+ requireSignature: boolean;
103
111
  verbose: boolean;
104
112
  profile?: string;
105
113
  }
@@ -1,4 +1,6 @@
1
1
  export declare function spellHome(): string;
2
2
  export declare function spellsRoot(): string;
3
3
  export declare function logsRoot(): string;
4
+ export declare function trustRoot(): string;
5
+ export declare function trustedPublishersRoot(): string;
4
6
  export declare function ensureSpellDirs(): Promise<void>;
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.spellHome = spellHome;
7
7
  exports.spellsRoot = spellsRoot;
8
8
  exports.logsRoot = logsRoot;
9
+ exports.trustRoot = trustRoot;
10
+ exports.trustedPublishersRoot = trustedPublishersRoot;
9
11
  exports.ensureSpellDirs = ensureSpellDirs;
10
12
  const promises_1 = require("node:fs/promises");
11
13
  const node_os_1 = require("node:os");
@@ -19,8 +21,15 @@ function spellsRoot() {
19
21
  function logsRoot() {
20
22
  return node_path_1.default.join(spellHome(), "logs");
21
23
  }
24
+ function trustRoot() {
25
+ return node_path_1.default.join(spellHome(), "trust");
26
+ }
27
+ function trustedPublishersRoot() {
28
+ return node_path_1.default.join(trustRoot(), "publishers");
29
+ }
22
30
  async function ensureSpellDirs() {
23
31
  await (0, promises_1.mkdir)(spellsRoot(), { recursive: true });
24
32
  await (0, promises_1.mkdir)(logsRoot(), { recursive: true });
33
+ await (0, promises_1.mkdir)(trustedPublishersRoot(), { recursive: true });
25
34
  }
26
35
  //# sourceMappingURL=paths.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/util/paths.ts"],"names":[],"mappings":";;;;;AAIA,8BAEC;AAED,gCAEC;AAED,4BAEC;AAED,0CAGC;AAnBD,+CAAyC;AACzC,qCAAkC;AAClC,0DAA6B;AAE7B,SAAgB,SAAS;IACvB,OAAO,mBAAI,CAAC,IAAI,CAAC,IAAA,iBAAO,GAAE,EAAE,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED,SAAgB,UAAU;IACxB,OAAO,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAgB,QAAQ;IACtB,OAAO,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACxC,CAAC;AAEM,KAAK,UAAU,eAAe;IACnC,MAAM,IAAA,gBAAK,EAAC,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,IAAA,gBAAK,EAAC,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/util/paths.ts"],"names":[],"mappings":";;;;;AAIA,8BAEC;AAED,gCAEC;AAED,4BAEC;AAED,8BAEC;AAED,sDAEC;AAED,0CAIC;AA5BD,+CAAyC;AACzC,qCAAkC;AAClC,0DAA6B;AAE7B,SAAgB,SAAS;IACvB,OAAO,mBAAI,CAAC,IAAI,CAAC,IAAA,iBAAO,GAAE,EAAE,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED,SAAgB,UAAU;IACxB,OAAO,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAgB,QAAQ;IACtB,OAAO,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACxC,CAAC;AAED,SAAgB,SAAS;IACvB,OAAO,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC;AAED,SAAgB,qBAAqB;IACnC,OAAO,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,YAAY,CAAC,CAAC;AAC9C,CAAC;AAEM,KAAK,UAAU,eAAe;IACnC,MAAM,IAAA,gBAAK,EAAC,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,IAAA,gBAAK,EAAC,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,MAAM,IAAA,gBAAK,EAAC,qBAAqB,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC"}
@@ -1 +1,3 @@
1
1
  export declare function detectHostPlatform(): string;
2
+ export declare function detectDockerPlatformForHost(): string;
3
+ export declare function platformMatches(supported: string[], target: string): boolean;
@@ -1,7 +1,45 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.detectHostPlatform = detectHostPlatform;
4
+ exports.detectDockerPlatformForHost = detectDockerPlatformForHost;
5
+ exports.platformMatches = platformMatches;
4
6
  function detectHostPlatform() {
5
7
  return `${process.platform}/${process.arch}`;
6
8
  }
9
+ function detectDockerPlatformForHost() {
10
+ // Docker execution runs in a linux container even on darwin/win32 hosts.
11
+ return `linux/${normalizeArch(process.arch)}`;
12
+ }
13
+ function platformMatches(supported, target) {
14
+ const expanded = expandPlatformAliases(target);
15
+ return supported.some((entry) => expanded.includes(entry));
16
+ }
17
+ function expandPlatformAliases(platform) {
18
+ const trimmed = platform.trim();
19
+ if (!trimmed.includes("/")) {
20
+ return [trimmed];
21
+ }
22
+ const [os, archRaw] = trimmed.split("/", 2);
23
+ const arch = normalizeArch(archRaw);
24
+ const out = new Set();
25
+ out.add(`${os}/${archRaw}`);
26
+ out.add(`${os}/${arch}`);
27
+ // Treat x64 and amd64 as aliases across OSes.
28
+ if (arch === "amd64") {
29
+ out.add(`${os}/x64`);
30
+ }
31
+ if (arch === "x64") {
32
+ out.add(`${os}/amd64`);
33
+ }
34
+ return Array.from(out);
35
+ }
36
+ function normalizeArch(arch) {
37
+ if (arch === "x64") {
38
+ return "amd64";
39
+ }
40
+ if (arch === "amd64") {
41
+ return "amd64";
42
+ }
43
+ return arch;
44
+ }
7
45
  //# sourceMappingURL=platform.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/util/platform.ts"],"names":[],"mappings":";;AAAA,gDAEC;AAFD,SAAgB,kBAAkB;IAChC,OAAO,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/util/platform.ts"],"names":[],"mappings":";;AAAA,gDAEC;AAED,kEAGC;AAED,0CAGC;AAZD,SAAgB,kBAAkB;IAChC,OAAO,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED,SAAgB,2BAA2B;IACzC,yEAAyE;IACzE,OAAO,SAAS,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;AAChD,CAAC;AAED,SAAgB,eAAe,CAAC,SAAmB,EAAE,MAAc;IACjE,MAAM,QAAQ,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC/C,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,qBAAqB,CAAC,QAAgB;IAC7C,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,OAAO,CAAC,CAAC;IACnB,CAAC;IAED,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAEpC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,OAAO,EAAE,CAAC,CAAC;IAC5B,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAEzB,8CAA8C;IAC9C,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACvB,CAAC;IACD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function redactSecrets<T>(value: T, env?: NodeJS.ProcessEnv): T;
2
+ export declare function collectSensitiveEnvValues(env?: NodeJS.ProcessEnv): string[];
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.redactSecrets = redactSecrets;
4
+ exports.collectSensitiveEnvValues = collectSensitiveEnvValues;
5
+ const REDACTED = "[REDACTED]";
6
+ const SENSITIVE_KEY_RE = /(authorization|token|secret|password|api[-_]?key|private[-_]?key|cookie|set-cookie)/i;
7
+ const SENSITIVE_ENV_NAME_RE = /(TOKEN|SECRET|PASSWORD|PRIVATE_KEY|API_KEY|ACCESS_KEY|AUTH|COOKIE|SESSION|CREDENTIAL)/i;
8
+ const TOKEN_LIKE_RE = /\b(?:ghp|github_pat|npm|sk_live|sk_test|xoxb|xoxp)_[A-Za-z0-9_-]+\b/g;
9
+ const BEARER_RE = /(Bearer\s+)[^\s"']+/gi;
10
+ function redactSecrets(value, env = process.env) {
11
+ const secrets = collectSensitiveEnvValues(env);
12
+ const seen = new WeakSet();
13
+ return redactValue(value, secrets, seen);
14
+ }
15
+ function collectSensitiveEnvValues(env = process.env) {
16
+ const set = new Set();
17
+ for (const [name, rawValue] of Object.entries(env)) {
18
+ if (!SENSITIVE_ENV_NAME_RE.test(name)) {
19
+ continue;
20
+ }
21
+ if (typeof rawValue !== "string" || rawValue.trim() === "") {
22
+ continue;
23
+ }
24
+ set.add(rawValue);
25
+ }
26
+ return Array.from(set).sort((a, b) => b.length - a.length);
27
+ }
28
+ function redactValue(value, secrets, seen) {
29
+ if (typeof value === "string") {
30
+ return redactString(value, secrets);
31
+ }
32
+ if (Array.isArray(value)) {
33
+ return value.map((entry) => redactValue(entry, secrets, seen));
34
+ }
35
+ if (!value || typeof value !== "object") {
36
+ return value;
37
+ }
38
+ if (seen.has(value)) {
39
+ return REDACTED;
40
+ }
41
+ seen.add(value);
42
+ const out = {};
43
+ for (const [key, raw] of Object.entries(value)) {
44
+ if (SENSITIVE_KEY_RE.test(key)) {
45
+ out[key] = REDACTED;
46
+ continue;
47
+ }
48
+ out[key] = redactValue(raw, secrets, seen);
49
+ }
50
+ return out;
51
+ }
52
+ function redactString(value, secrets) {
53
+ let out = value;
54
+ for (const secret of secrets) {
55
+ if (secret.length < 4) {
56
+ continue;
57
+ }
58
+ if (out.includes(secret)) {
59
+ out = out.split(secret).join(REDACTED);
60
+ }
61
+ }
62
+ out = out.replace(BEARER_RE, `$1${REDACTED}`);
63
+ out = out.replace(TOKEN_LIKE_RE, REDACTED);
64
+ return out;
65
+ }
66
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/util/redact.ts"],"names":[],"mappings":";;AAOA,sCAIC;AAED,8DAcC;AA3BD,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B,MAAM,gBAAgB,GAAG,sFAAsF,CAAC;AAChH,MAAM,qBAAqB,GAAG,wFAAwF,CAAC;AACvH,MAAM,aAAa,GAAG,sEAAsE,CAAC;AAC7F,MAAM,SAAS,GAAG,uBAAuB,CAAC;AAE1C,SAAgB,aAAa,CAAI,KAAQ,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC7E,MAAM,OAAO,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,IAAI,OAAO,EAAU,CAAC;IACnC,OAAO,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAM,CAAC;AAChD,CAAC;AAED,SAAgB,yBAAyB,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC5E,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAE9B,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,SAAS;QACX,CAAC;QACD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC3D,SAAS;QACX,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,WAAW,CAAC,KAAc,EAAE,OAAiB,EAAE,IAAqB;IAC3E,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEhB,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;QAC1E,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,GAAG,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC;YACpB,SAAS;QACX,CAAC;QAED,GAAG,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,YAAY,CAAC,KAAa,EAAE,OAAiB;IACpD,IAAI,GAAG,GAAG,KAAK,CAAC;IAEhB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,QAAQ,EAAE,CAAC,CAAC;IAC9C,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAE3C,OAAO,GAAG,CAAC;AACb,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spell-runtime",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Minimal CLI runtime for SpellBundle v1",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -10,13 +10,16 @@
10
10
  "LICENSE"
11
11
  ],
12
12
  "bin": {
13
- "spell": "dist/cli/index.js"
13
+ "spell": "dist/cli/index.js",
14
+ "spell-runner": "dist/runner/spell-runner.js"
14
15
  },
15
16
  "engines": {
16
17
  "node": ">=20"
17
18
  },
18
19
  "scripts": {
19
20
  "dev": "tsx src/cli/index.ts",
21
+ "api:dev": "tsx src/api/index.ts",
22
+ "api:start": "node dist/api/index.js",
20
23
  "build": "tsc -p tsconfig.json",
21
24
  "prepare": "npm run build",
22
25
  "test": "vitest run",