@carto-knowledge/runner 0.2.2 → 0.2.4
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/dist/index.cjs +341 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +151 -0
- package/dist/index.d.ts +151 -0
- package/dist/index.js +304 -0
- package/dist/index.js.map +1 -0
- package/package.json +22 -14
- package/.turbo/turbo-typecheck.log +0 -6
- package/bun.lock +0 -38
- package/src/allowlist.test.ts +0 -126
- package/src/allowlist.ts +0 -105
- package/src/envelope.ts +0 -62
- package/src/index.ts +0 -12
- package/src/runner.test.ts +0 -95
- package/src/runner.ts +0 -169
- package/src/runtime.ts +0 -90
- package/src/tokenizer.test.ts +0 -83
- package/src/tokenizer.ts +0 -75
- package/tsconfig.json +0 -15
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
CliRuntime: () => CliRuntime,
|
|
24
|
+
DEFAULT_ALLOWLIST: () => DEFAULT_ALLOWLIST,
|
|
25
|
+
ErrorCodes: () => ErrorCodes,
|
|
26
|
+
WRITE_ALLOWLIST: () => WRITE_ALLOWLIST,
|
|
27
|
+
WorkerRuntime: () => WorkerRuntime,
|
|
28
|
+
checkAllowlist: () => checkAllowlist,
|
|
29
|
+
errorEnvelope: () => errorEnvelope,
|
|
30
|
+
extractCommandPath: () => extractCommandPath,
|
|
31
|
+
runInProcess: () => runInProcess,
|
|
32
|
+
successEnvelope: () => successEnvelope,
|
|
33
|
+
tokenize: () => tokenize
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/runner.ts
|
|
38
|
+
var import_clipanion = require("clipanion");
|
|
39
|
+
|
|
40
|
+
// src/tokenizer.ts
|
|
41
|
+
function tokenize(commandLine) {
|
|
42
|
+
const tokens = [];
|
|
43
|
+
let current = "";
|
|
44
|
+
let inQuote = null;
|
|
45
|
+
let escaped = false;
|
|
46
|
+
for (let i = 0; i < commandLine.length; i++) {
|
|
47
|
+
const char = commandLine[i];
|
|
48
|
+
if (escaped) {
|
|
49
|
+
current += char;
|
|
50
|
+
escaped = false;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (char === "\\") {
|
|
54
|
+
escaped = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (inQuote) {
|
|
58
|
+
if (char === inQuote) {
|
|
59
|
+
tokens.push(current);
|
|
60
|
+
current = "";
|
|
61
|
+
inQuote = null;
|
|
62
|
+
} else {
|
|
63
|
+
current += char;
|
|
64
|
+
}
|
|
65
|
+
} else if (char === '"' || char === "'") {
|
|
66
|
+
inQuote = char;
|
|
67
|
+
} else if (char === " " || char === " ") {
|
|
68
|
+
if (current) {
|
|
69
|
+
tokens.push(current);
|
|
70
|
+
current = "";
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
current += char;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (current) {
|
|
77
|
+
tokens.push(current);
|
|
78
|
+
}
|
|
79
|
+
return tokens;
|
|
80
|
+
}
|
|
81
|
+
function extractCommandPath(tokens) {
|
|
82
|
+
const path = [];
|
|
83
|
+
for (const token of tokens) {
|
|
84
|
+
if (token.startsWith("-")) break;
|
|
85
|
+
path.push(token);
|
|
86
|
+
}
|
|
87
|
+
return path.join(" ");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/allowlist.ts
|
|
91
|
+
function checkAllowlist(commandPath, config) {
|
|
92
|
+
const normalizedPath = commandPath.toLowerCase().trim();
|
|
93
|
+
if (config.commands.some((cmd) => cmd.toLowerCase() === normalizedPath)) {
|
|
94
|
+
return { allowed: true };
|
|
95
|
+
}
|
|
96
|
+
if (config.commands.some((cmd) => normalizedPath.startsWith(cmd.toLowerCase() + " "))) {
|
|
97
|
+
return { allowed: true };
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
allowed: false,
|
|
101
|
+
error: {
|
|
102
|
+
code: "COMMAND_NOT_ALLOWED",
|
|
103
|
+
message: `Command '${commandPath}' is not in the allowlist. Allowed commands: ${config.commands.join(", ")}`
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
var DEFAULT_ALLOWLIST = {
|
|
108
|
+
commands: [
|
|
109
|
+
// Folder reads
|
|
110
|
+
"folder tree",
|
|
111
|
+
"folder list",
|
|
112
|
+
"folder get",
|
|
113
|
+
// Item reads
|
|
114
|
+
"item search",
|
|
115
|
+
"item get",
|
|
116
|
+
"item list",
|
|
117
|
+
"item get-by-uri",
|
|
118
|
+
"item segments",
|
|
119
|
+
// Edge reads
|
|
120
|
+
"edge list",
|
|
121
|
+
"edge get",
|
|
122
|
+
// Share link reads
|
|
123
|
+
"share-link list",
|
|
124
|
+
"share-link analytics",
|
|
125
|
+
// Librarian
|
|
126
|
+
"librarian chat"
|
|
127
|
+
]
|
|
128
|
+
};
|
|
129
|
+
var WRITE_ALLOWLIST = {
|
|
130
|
+
commands: [
|
|
131
|
+
...DEFAULT_ALLOWLIST.commands,
|
|
132
|
+
// Folder writes
|
|
133
|
+
"folder create",
|
|
134
|
+
"folder update",
|
|
135
|
+
"folder delete",
|
|
136
|
+
"folder move",
|
|
137
|
+
"folder restructure",
|
|
138
|
+
"folder create-tree",
|
|
139
|
+
// Item writes
|
|
140
|
+
"item create",
|
|
141
|
+
"item update",
|
|
142
|
+
"item delete",
|
|
143
|
+
"item update content",
|
|
144
|
+
"item upload",
|
|
145
|
+
"item assign",
|
|
146
|
+
// Edge writes
|
|
147
|
+
"edge create",
|
|
148
|
+
"edge update",
|
|
149
|
+
"edge delete",
|
|
150
|
+
// Share link writes
|
|
151
|
+
"share-link create",
|
|
152
|
+
"share-link delete"
|
|
153
|
+
]
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// src/envelope.ts
|
|
157
|
+
function successEnvelope(command, result, requestId) {
|
|
158
|
+
return {
|
|
159
|
+
ok: true,
|
|
160
|
+
command,
|
|
161
|
+
result,
|
|
162
|
+
requestId,
|
|
163
|
+
schemaVersion: 1
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function errorEnvelope(command, error, requestId) {
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
command,
|
|
170
|
+
error,
|
|
171
|
+
requestId,
|
|
172
|
+
schemaVersion: 1
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
var ErrorCodes = {
|
|
176
|
+
COMMAND_NOT_ALLOWED: "COMMAND_NOT_ALLOWED",
|
|
177
|
+
COMMAND_NOT_FOUND: "COMMAND_NOT_FOUND",
|
|
178
|
+
INVALID_ARGUMENTS: "INVALID_ARGUMENTS",
|
|
179
|
+
AUTH_ERROR: "AUTH_ERROR",
|
|
180
|
+
EXECUTION_ERROR: "EXECUTION_ERROR",
|
|
181
|
+
PARSE_ERROR: "PARSE_ERROR"
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/runtime.ts
|
|
185
|
+
var WorkerRuntime = class {
|
|
186
|
+
constructor(authToken, apiBaseUrl) {
|
|
187
|
+
this.authToken = authToken;
|
|
188
|
+
this.apiBaseUrl = apiBaseUrl;
|
|
189
|
+
}
|
|
190
|
+
isInteractive = false;
|
|
191
|
+
isToolMode = true;
|
|
192
|
+
async getAuthToken() {
|
|
193
|
+
return this.authToken;
|
|
194
|
+
}
|
|
195
|
+
log(message) {
|
|
196
|
+
console.log(`[carto-cli] ${message}`);
|
|
197
|
+
}
|
|
198
|
+
warn(message) {
|
|
199
|
+
console.warn(`[carto-cli] ${message}`);
|
|
200
|
+
}
|
|
201
|
+
error(message) {
|
|
202
|
+
console.error(`[carto-cli] ${message}`);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var CliRuntime = class {
|
|
206
|
+
constructor(config) {
|
|
207
|
+
this.config = config;
|
|
208
|
+
this.isInteractive = process.stdout.isTTY ?? false;
|
|
209
|
+
}
|
|
210
|
+
isInteractive;
|
|
211
|
+
isToolMode = false;
|
|
212
|
+
get apiBaseUrl() {
|
|
213
|
+
return this.config.apiBaseUrl;
|
|
214
|
+
}
|
|
215
|
+
async getAuthToken() {
|
|
216
|
+
if (!this.config.authToken) {
|
|
217
|
+
throw new Error("Not authenticated. Run `carto auth login` first.");
|
|
218
|
+
}
|
|
219
|
+
return this.config.authToken;
|
|
220
|
+
}
|
|
221
|
+
log(message) {
|
|
222
|
+
console.log(message);
|
|
223
|
+
}
|
|
224
|
+
warn(message) {
|
|
225
|
+
console.warn(message);
|
|
226
|
+
}
|
|
227
|
+
error(message) {
|
|
228
|
+
console.error(message);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// src/runner.ts
|
|
233
|
+
var import_commands = require("@carto-knowledge/commands");
|
|
234
|
+
var import_core = require("@carto-knowledge/core");
|
|
235
|
+
async function runInProcess(commandLine, options) {
|
|
236
|
+
const { requestId, authToken, apiBaseUrl, allowlist = DEFAULT_ALLOWLIST } = options;
|
|
237
|
+
if (!commandLine || typeof commandLine !== "string") {
|
|
238
|
+
return errorEnvelope("", {
|
|
239
|
+
code: ErrorCodes.INVALID_ARGUMENTS,
|
|
240
|
+
message: "commandLine must be a non-empty string"
|
|
241
|
+
}, requestId);
|
|
242
|
+
}
|
|
243
|
+
if (!authToken) {
|
|
244
|
+
return errorEnvelope("", {
|
|
245
|
+
code: ErrorCodes.AUTH_ERROR,
|
|
246
|
+
message: "authToken is required"
|
|
247
|
+
}, requestId);
|
|
248
|
+
}
|
|
249
|
+
const tokens = tokenize(commandLine.trim());
|
|
250
|
+
if (tokens.length === 0) {
|
|
251
|
+
return errorEnvelope("", {
|
|
252
|
+
code: ErrorCodes.INVALID_ARGUMENTS,
|
|
253
|
+
message: "Empty command line"
|
|
254
|
+
}, requestId);
|
|
255
|
+
}
|
|
256
|
+
const commandPath = extractCommandPath(tokens);
|
|
257
|
+
const allowlistResult = checkAllowlist(commandPath, allowlist);
|
|
258
|
+
if (!allowlistResult.allowed) {
|
|
259
|
+
return errorEnvelope(commandPath, allowlistResult.error, requestId);
|
|
260
|
+
}
|
|
261
|
+
const augmentedTokens = [
|
|
262
|
+
...tokens,
|
|
263
|
+
"--format",
|
|
264
|
+
"json"
|
|
265
|
+
];
|
|
266
|
+
const cli = new import_clipanion.Cli({
|
|
267
|
+
binaryName: "carto",
|
|
268
|
+
enableCapture: true
|
|
269
|
+
});
|
|
270
|
+
for (const CommandClass of import_commands.allCommands) {
|
|
271
|
+
cli.register(CommandClass);
|
|
272
|
+
}
|
|
273
|
+
let stdoutData = "";
|
|
274
|
+
let stderrData = "";
|
|
275
|
+
const stdout = {
|
|
276
|
+
write(chunk) {
|
|
277
|
+
stdoutData += typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
const stderr = {
|
|
282
|
+
write(chunk) {
|
|
283
|
+
stderrData += typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
const runtime = new WorkerRuntime(authToken, apiBaseUrl);
|
|
288
|
+
const client = new import_core.CartoClient({
|
|
289
|
+
baseUrl: apiBaseUrl,
|
|
290
|
+
getAuthToken: async () => authToken
|
|
291
|
+
});
|
|
292
|
+
const context = {
|
|
293
|
+
stdin: process.stdin,
|
|
294
|
+
stdout,
|
|
295
|
+
stderr,
|
|
296
|
+
env: {},
|
|
297
|
+
colorDepth: 1,
|
|
298
|
+
client,
|
|
299
|
+
runtime
|
|
300
|
+
};
|
|
301
|
+
try {
|
|
302
|
+
const exitCode = await cli.run(augmentedTokens, context);
|
|
303
|
+
const output = stdoutData.trim();
|
|
304
|
+
const errorOutput = stderrData.trim();
|
|
305
|
+
if (exitCode === 0) {
|
|
306
|
+
try {
|
|
307
|
+
const result = output ? JSON.parse(output) : null;
|
|
308
|
+
return successEnvelope(commandPath, result, requestId);
|
|
309
|
+
} catch {
|
|
310
|
+
return successEnvelope(commandPath, { text: output }, requestId);
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
return errorEnvelope(commandPath, {
|
|
314
|
+
code: ErrorCodes.EXECUTION_ERROR,
|
|
315
|
+
message: errorOutput || output || `Command exited with code ${exitCode}`
|
|
316
|
+
}, requestId);
|
|
317
|
+
}
|
|
318
|
+
} catch (error) {
|
|
319
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
320
|
+
console.error(`[carto-cli] Execution error for '${commandPath}':`, error);
|
|
321
|
+
return errorEnvelope(commandPath, {
|
|
322
|
+
code: ErrorCodes.EXECUTION_ERROR,
|
|
323
|
+
message
|
|
324
|
+
}, requestId);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
328
|
+
0 && (module.exports = {
|
|
329
|
+
CliRuntime,
|
|
330
|
+
DEFAULT_ALLOWLIST,
|
|
331
|
+
ErrorCodes,
|
|
332
|
+
WRITE_ALLOWLIST,
|
|
333
|
+
WorkerRuntime,
|
|
334
|
+
checkAllowlist,
|
|
335
|
+
errorEnvelope,
|
|
336
|
+
extractCommandPath,
|
|
337
|
+
runInProcess,
|
|
338
|
+
successEnvelope,
|
|
339
|
+
tokenize
|
|
340
|
+
});
|
|
341
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/runner.ts","../src/tokenizer.ts","../src/allowlist.ts","../src/envelope.ts","../src/runtime.ts"],"sourcesContent":["// ABOUTME: Public API for @carto/runner package\n// ABOUTME: Exports in-process runner and supporting utilities\n\nexport { runInProcess } from './runner';\nexport type { RunOptions } from './runner';\nexport type { CommandOutput } from './envelope';\nexport { successEnvelope, errorEnvelope, ErrorCodes } from './envelope';\nexport { tokenize, extractCommandPath } from './tokenizer';\nexport { checkAllowlist, DEFAULT_ALLOWLIST, WRITE_ALLOWLIST } from './allowlist';\nexport type { AllowlistConfig } from './allowlist';\nexport { WorkerRuntime, CliRuntime } from './runtime';\nexport type { CartoRuntime } from './runtime';\n","// ABOUTME: Executes CLI commands in-process without subprocess spawning\n// ABOUTME: Designed for Cloudflare Worker compatibility\n\nimport { Cli } from 'clipanion';\nimport { tokenize, extractCommandPath } from './tokenizer';\nimport { checkAllowlist, AllowlistConfig, DEFAULT_ALLOWLIST } from './allowlist';\nimport { successEnvelope, errorEnvelope, CommandOutput, ErrorCodes } from './envelope';\nimport { WorkerRuntime } from './runtime';\nimport { allCommands } from '@carto-knowledge/commands';\nimport { CartoClient } from '@carto-knowledge/core';\n\nexport interface RunOptions {\n /** Correlation ID for tracing */\n requestId?: string;\n\n /** Auth token for API calls */\n authToken: string;\n\n /** Base URL for Carto API */\n apiBaseUrl: string;\n\n /** Command allowlist (defaults to read-only operations) */\n allowlist?: AllowlistConfig;\n}\n\n/**\n * Executes a CLI command in-process.\n *\n * Designed for Worker runtime:\n * - No subprocess spawning\n * - Captures output to memory streams\n * - Forces JSON output and non-interactive mode\n * - Validates against allowlist\n *\n * @param commandLine - Command string, e.g., \"folder tree --library my-lib\"\n * @param options - Execution options including auth and allowlist\n * @returns Structured command output envelope\n */\nexport async function runInProcess(\n commandLine: string,\n options: RunOptions\n): Promise<CommandOutput> {\n const { requestId, authToken, apiBaseUrl, allowlist = DEFAULT_ALLOWLIST } = options;\n\n // Defensive: Validate inputs\n if (!commandLine || typeof commandLine !== 'string') {\n return errorEnvelope('', {\n code: ErrorCodes.INVALID_ARGUMENTS,\n message: 'commandLine must be a non-empty string',\n }, requestId);\n }\n\n if (!authToken) {\n return errorEnvelope('', {\n code: ErrorCodes.AUTH_ERROR,\n message: 'authToken is required',\n }, requestId);\n }\n\n // Parse command line\n const tokens = tokenize(commandLine.trim());\n\n if (tokens.length === 0) {\n return errorEnvelope('', {\n code: ErrorCodes.INVALID_ARGUMENTS,\n message: 'Empty command line',\n }, requestId);\n }\n\n const commandPath = extractCommandPath(tokens);\n\n // Check allowlist\n const allowlistResult = checkAllowlist(commandPath, allowlist);\n if (!allowlistResult.allowed) {\n return errorEnvelope(commandPath, allowlistResult.error!, requestId);\n }\n\n // Force tool-mode flags\n const augmentedTokens = [\n ...tokens,\n '--format', 'json',\n ];\n\n // Create CLI instance\n const cli = new Cli({\n binaryName: 'carto',\n enableCapture: true,\n });\n\n // Register all commands\n for (const CommandClass of allCommands) {\n cli.register(CommandClass);\n }\n\n // Capture output to memory using simple buffers (Worker-compatible)\n let stdoutData = '';\n let stderrData = '';\n\n const stdout = {\n write(chunk: string | Uint8Array): boolean {\n stdoutData += typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk);\n return true;\n },\n };\n\n const stderr = {\n write(chunk: string | Uint8Array): boolean {\n stderrData += typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk);\n return true;\n },\n };\n\n // Create runtime and client\n const runtime = new WorkerRuntime(authToken, apiBaseUrl);\n const client = new CartoClient({\n baseUrl: apiBaseUrl,\n getAuthToken: async () => authToken,\n });\n\n // Create context matching CartoCommandContext\n // Type assertion needed because we use minimal stream stubs for Worker compatibility\n // Clipanion only uses the write() method at runtime\n const context = {\n stdin: process.stdin,\n stdout,\n stderr,\n env: {},\n colorDepth: 1,\n client,\n runtime,\n } as unknown as Parameters<typeof cli.run>[1];\n\n try {\n const exitCode = await cli.run(augmentedTokens, context);\n const output = stdoutData.trim();\n const errorOutput = stderrData.trim();\n\n if (exitCode === 0) {\n // Parse JSON output from command\n try {\n const result = output ? JSON.parse(output) : null;\n return successEnvelope(commandPath, result, requestId);\n } catch {\n // Command succeeded but output wasn't JSON (shouldn't happen in tool mode)\n return successEnvelope(commandPath, { text: output }, requestId);\n }\n } else {\n // Command failed\n return errorEnvelope(commandPath, {\n code: ErrorCodes.EXECUTION_ERROR,\n message: errorOutput || output || `Command exited with code ${exitCode}`,\n }, requestId);\n }\n } catch (error) {\n // Unexpected error during execution\n const message = error instanceof Error ? error.message : String(error);\n console.error(`[carto-cli] Execution error for '${commandPath}':`, error);\n\n return errorEnvelope(commandPath, {\n code: ErrorCodes.EXECUTION_ERROR,\n message,\n }, requestId);\n }\n}\n\n// Re-export for convenience\nexport { DEFAULT_ALLOWLIST, WRITE_ALLOWLIST } from './allowlist';\nexport type { AllowlistConfig } from './allowlist';\nexport type { CommandOutput } from './envelope';\n","// ABOUTME: Tokenizes command line strings for in-process CLI execution\n// ABOUTME: Handles quoted strings and shell-like argument parsing\n\n/**\n * Tokenizes a command line string, handling quoted strings.\n *\n * Examples:\n * - \"folder tree\" => ['folder', 'tree']\n * - \"--name 'My Folder'\" => ['--name', 'My Folder']\n * - '--name \"My Folder\"' => ['--name', 'My Folder']\n * - \"folder create --name 'Test's Folder'\" => handles escaped quotes\n */\nexport function tokenize(commandLine: string): string[] {\n const tokens: string[] = [];\n let current = '';\n let inQuote: string | null = null;\n let escaped = false;\n\n for (let i = 0; i < commandLine.length; i++) {\n const char = commandLine[i];\n\n if (escaped) {\n current += char;\n escaped = false;\n continue;\n }\n\n if (char === '\\\\') {\n escaped = true;\n continue;\n }\n\n if (inQuote) {\n if (char === inQuote) {\n // Empty quoted string should still produce a token\n tokens.push(current);\n current = '';\n inQuote = null;\n } else {\n current += char;\n }\n } else if (char === '\"' || char === \"'\") {\n inQuote = char;\n } else if (char === ' ' || char === '\\t') {\n if (current) {\n tokens.push(current);\n current = '';\n }\n } else {\n current += char;\n }\n }\n\n if (current) {\n tokens.push(current);\n }\n\n return tokens;\n}\n\n/**\n * Extracts the command path from tokens (before any options).\n *\n * Examples:\n * - ['folder', 'tree', '--library', 'x'] => 'folder tree'\n * - ['item', 'search', '-q', 'hello'] => 'item search'\n */\nexport function extractCommandPath(tokens: string[]): string {\n const path: string[] = [];\n for (const token of tokens) {\n if (token.startsWith('-')) break;\n path.push(token);\n }\n return path.join(' ');\n}\n","// ABOUTME: Validates commands against an allowlist for tool-mode execution\n// ABOUTME: Prevents unauthorized command execution in Worker context\n\nexport interface AllowlistConfig {\n commands: string[]; // e.g., ['folder tree', 'item search']\n}\n\nexport interface AllowlistResult {\n allowed: boolean;\n error?: {\n code: 'COMMAND_NOT_ALLOWED';\n message: string;\n };\n}\n\n/**\n * Checks if a command matches the allowlist.\n *\n * Defensive validation:\n * - Normalizes command path (lowercase, trim)\n * - Supports prefix matching for subcommands\n */\nexport function checkAllowlist(\n commandPath: string,\n config: AllowlistConfig\n): AllowlistResult {\n const normalizedPath = commandPath.toLowerCase().trim();\n\n // Check exact match\n if (config.commands.some(cmd => cmd.toLowerCase() === normalizedPath)) {\n return { allowed: true };\n }\n\n // Check prefix match (e.g., 'folder' allows 'folder tree', 'folder create')\n if (config.commands.some(cmd => normalizedPath.startsWith(cmd.toLowerCase() + ' '))) {\n return { allowed: true };\n }\n\n return {\n allowed: false,\n error: {\n code: 'COMMAND_NOT_ALLOWED',\n message: `Command '${commandPath}' is not in the allowlist. ` +\n `Allowed commands: ${config.commands.join(', ')}`,\n },\n };\n}\n\n/**\n * Default allowlist for Worker tool mode.\n * Read-only operations by default.\n */\nexport const DEFAULT_ALLOWLIST: AllowlistConfig = {\n commands: [\n // Folder reads\n 'folder tree',\n 'folder list',\n 'folder get',\n // Item reads\n 'item search',\n 'item get',\n 'item list',\n 'item get-by-uri',\n 'item segments',\n // Edge reads\n 'edge list',\n 'edge get',\n // Share link reads\n 'share-link list',\n 'share-link analytics',\n // Librarian\n 'librarian chat',\n ],\n};\n\n/**\n * Extended allowlist including write operations.\n * Use when agent needs to modify data.\n */\nexport const WRITE_ALLOWLIST: AllowlistConfig = {\n commands: [\n ...DEFAULT_ALLOWLIST.commands,\n // Folder writes\n 'folder create',\n 'folder update',\n 'folder delete',\n 'folder move',\n 'folder restructure',\n 'folder create-tree',\n // Item writes\n 'item create',\n 'item update',\n 'item delete',\n 'item update content',\n 'item upload',\n 'item assign',\n // Edge writes\n 'edge create',\n 'edge update',\n 'edge delete',\n // Share link writes\n 'share-link create',\n 'share-link delete',\n ],\n};\n","// ABOUTME: Wraps CLI command outputs in structured JSON envelopes\n// ABOUTME: Provides consistent response format for Mastra tool integration\n\nexport interface CommandError {\n code: string;\n message: string;\n}\n\nexport interface CommandOutput<T = unknown> {\n ok: boolean;\n command: string;\n result?: T;\n error?: CommandError;\n requestId?: string;\n schemaVersion: 1;\n}\n\n/**\n * Creates a successful output envelope.\n */\nexport function successEnvelope<T>(\n command: string,\n result: T,\n requestId?: string\n): CommandOutput<T> {\n return {\n ok: true,\n command,\n result,\n requestId,\n schemaVersion: 1,\n };\n}\n\n/**\n * Creates an error output envelope.\n */\nexport function errorEnvelope(\n command: string,\n error: CommandError,\n requestId?: string\n): CommandOutput<never> {\n return {\n ok: false,\n command,\n error,\n requestId,\n schemaVersion: 1,\n };\n}\n\n/**\n * Common error codes for CLI operations.\n */\nexport const ErrorCodes = {\n COMMAND_NOT_ALLOWED: 'COMMAND_NOT_ALLOWED',\n COMMAND_NOT_FOUND: 'COMMAND_NOT_FOUND',\n INVALID_ARGUMENTS: 'INVALID_ARGUMENTS',\n AUTH_ERROR: 'AUTH_ERROR',\n EXECUTION_ERROR: 'EXECUTION_ERROR',\n PARSE_ERROR: 'PARSE_ERROR',\n} as const;\n","// ABOUTME: Abstracts runtime differences between CLI and Worker execution\n// ABOUTME: Provides consistent interface for auth, logging, and config\n\nexport interface CartoRuntime {\n // Authentication\n getAuthToken(): Promise<string>;\n\n // API access\n apiBaseUrl: string;\n\n // Environment flags\n isInteractive: boolean;\n isToolMode: boolean;\n\n // Logging (respects --quiet, routes to correct streams)\n log(message: string): void;\n warn(message: string): void;\n error(message: string): void;\n}\n\n/**\n * Worker runtime - receives auth from request context.\n * Non-interactive, always tool mode.\n */\nexport class WorkerRuntime implements CartoRuntime {\n isInteractive = false;\n isToolMode = true;\n\n constructor(\n private authToken: string,\n public apiBaseUrl: string,\n ) {}\n\n async getAuthToken(): Promise<string> {\n return this.authToken;\n }\n\n log(message: string): void {\n console.log(`[carto-cli] ${message}`);\n }\n\n warn(message: string): void {\n console.warn(`[carto-cli] ${message}`);\n }\n\n error(message: string): void {\n console.error(`[carto-cli] ${message}`);\n }\n}\n\n/**\n * CLI runtime - reads from config file or env vars.\n * Interactive when terminal attached.\n *\n * Compatible with both Bun and Node.js runtimes.\n */\nexport class CliRuntime implements CartoRuntime {\n isInteractive: boolean;\n isToolMode = false;\n\n constructor(\n private config: { apiBaseUrl: string; authToken?: string },\n ) {\n // Works in both Bun and Node.js\n this.isInteractive = process.stdout.isTTY ?? false;\n }\n\n get apiBaseUrl(): string {\n return this.config.apiBaseUrl;\n }\n\n async getAuthToken(): Promise<string> {\n if (!this.config.authToken) {\n throw new Error('Not authenticated. Run `carto auth login` first.');\n }\n return this.config.authToken;\n }\n\n log(message: string): void {\n console.log(message);\n }\n\n warn(message: string): void {\n console.warn(message);\n }\n\n error(message: string): void {\n console.error(message);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGA,uBAAoB;;;ACSb,SAAS,SAAS,aAA+B;AACtD,QAAM,SAAmB,CAAC;AAC1B,MAAI,UAAU;AACd,MAAI,UAAyB;AAC7B,MAAI,UAAU;AAEd,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,UAAM,OAAO,YAAY,CAAC;AAE1B,QAAI,SAAS;AACX,iBAAW;AACX,gBAAU;AACV;AAAA,IACF;AAEA,QAAI,SAAS,MAAM;AACjB,gBAAU;AACV;AAAA,IACF;AAEA,QAAI,SAAS;AACX,UAAI,SAAS,SAAS;AAEpB,eAAO,KAAK,OAAO;AACnB,kBAAU;AACV,kBAAU;AAAA,MACZ,OAAO;AACL,mBAAW;AAAA,MACb;AAAA,IACF,WAAW,SAAS,OAAO,SAAS,KAAK;AACvC,gBAAU;AAAA,IACZ,WAAW,SAAS,OAAO,SAAS,KAAM;AACxC,UAAI,SAAS;AACX,eAAO,KAAK,OAAO;AACnB,kBAAU;AAAA,MACZ;AAAA,IACF,OAAO;AACL,iBAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI,SAAS;AACX,WAAO,KAAK,OAAO;AAAA,EACrB;AAEA,SAAO;AACT;AASO,SAAS,mBAAmB,QAA0B;AAC3D,QAAM,OAAiB,CAAC;AACxB,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,WAAW,GAAG,EAAG;AAC3B,SAAK,KAAK,KAAK;AAAA,EACjB;AACA,SAAO,KAAK,KAAK,GAAG;AACtB;;;ACpDO,SAAS,eACd,aACA,QACiB;AACjB,QAAM,iBAAiB,YAAY,YAAY,EAAE,KAAK;AAGtD,MAAI,OAAO,SAAS,KAAK,SAAO,IAAI,YAAY,MAAM,cAAc,GAAG;AACrE,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAGA,MAAI,OAAO,SAAS,KAAK,SAAO,eAAe,WAAW,IAAI,YAAY,IAAI,GAAG,CAAC,GAAG;AACnF,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,YAAY,WAAW,gDACT,OAAO,SAAS,KAAK,IAAI,CAAC;AAAA,IACnD;AAAA,EACF;AACF;AAMO,IAAM,oBAAqC;AAAA,EAChD,UAAU;AAAA;AAAA,IAER;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,EACF;AACF;AAMO,IAAM,kBAAmC;AAAA,EAC9C,UAAU;AAAA,IACR,GAAG,kBAAkB;AAAA;AAAA,IAErB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,EACF;AACF;;;ACpFO,SAAS,gBACd,SACA,QACA,WACkB;AAClB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,EACjB;AACF;AAKO,SAAS,cACd,SACA,OACA,WACsB;AACtB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,EACjB;AACF;AAKO,IAAM,aAAa;AAAA,EACxB,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,aAAa;AACf;;;ACrCO,IAAM,gBAAN,MAA4C;AAAA,EAIjD,YACU,WACD,YACP;AAFQ;AACD;AAAA,EACN;AAAA,EANH,gBAAgB;AAAA,EAChB,aAAa;AAAA,EAOb,MAAM,eAAgC;AACpC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAuB;AACzB,YAAQ,IAAI,eAAe,OAAO,EAAE;AAAA,EACtC;AAAA,EAEA,KAAK,SAAuB;AAC1B,YAAQ,KAAK,eAAe,OAAO,EAAE;AAAA,EACvC;AAAA,EAEA,MAAM,SAAuB;AAC3B,YAAQ,MAAM,eAAe,OAAO,EAAE;AAAA,EACxC;AACF;AAQO,IAAM,aAAN,MAAyC;AAAA,EAI9C,YACU,QACR;AADQ;AAGR,SAAK,gBAAgB,QAAQ,OAAO,SAAS;AAAA,EAC/C;AAAA,EARA;AAAA,EACA,aAAa;AAAA,EASb,IAAI,aAAqB;AACvB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,MAAM,eAAgC;AACpC,QAAI,CAAC,KAAK,OAAO,WAAW;AAC1B,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AACA,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,IAAI,SAAuB;AACzB,YAAQ,IAAI,OAAO;AAAA,EACrB;AAAA,EAEA,KAAK,SAAuB;AAC1B,YAAQ,KAAK,OAAO;AAAA,EACtB;AAAA,EAEA,MAAM,SAAuB;AAC3B,YAAQ,MAAM,OAAO;AAAA,EACvB;AACF;;;AJjFA,sBAA4B;AAC5B,kBAA4B;AA6B5B,eAAsB,aACpB,aACA,SACwB;AACxB,QAAM,EAAE,WAAW,WAAW,YAAY,YAAY,kBAAkB,IAAI;AAG5E,MAAI,CAAC,eAAe,OAAO,gBAAgB,UAAU;AACnD,WAAO,cAAc,IAAI;AAAA,MACvB,MAAM,WAAW;AAAA,MACjB,SAAS;AAAA,IACX,GAAG,SAAS;AAAA,EACd;AAEA,MAAI,CAAC,WAAW;AACd,WAAO,cAAc,IAAI;AAAA,MACvB,MAAM,WAAW;AAAA,MACjB,SAAS;AAAA,IACX,GAAG,SAAS;AAAA,EACd;AAGA,QAAM,SAAS,SAAS,YAAY,KAAK,CAAC;AAE1C,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,cAAc,IAAI;AAAA,MACvB,MAAM,WAAW;AAAA,MACjB,SAAS;AAAA,IACX,GAAG,SAAS;AAAA,EACd;AAEA,QAAM,cAAc,mBAAmB,MAAM;AAG7C,QAAM,kBAAkB,eAAe,aAAa,SAAS;AAC7D,MAAI,CAAC,gBAAgB,SAAS;AAC5B,WAAO,cAAc,aAAa,gBAAgB,OAAQ,SAAS;AAAA,EACrE;AAGA,QAAM,kBAAkB;AAAA,IACtB,GAAG;AAAA,IACH;AAAA,IAAY;AAAA,EACd;AAGA,QAAM,MAAM,IAAI,qBAAI;AAAA,IAClB,YAAY;AAAA,IACZ,eAAe;AAAA,EACjB,CAAC;AAGD,aAAW,gBAAgB,6BAAa;AACtC,QAAI,SAAS,YAAY;AAAA,EAC3B;AAGA,MAAI,aAAa;AACjB,MAAI,aAAa;AAEjB,QAAM,SAAS;AAAA,IACb,MAAM,OAAqC;AACzC,oBAAc,OAAO,UAAU,WAAW,QAAQ,IAAI,YAAY,EAAE,OAAO,KAAK;AAChF,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,SAAS;AAAA,IACb,MAAM,OAAqC;AACzC,oBAAc,OAAO,UAAU,WAAW,QAAQ,IAAI,YAAY,EAAE,OAAO,KAAK;AAChF,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,UAAU,IAAI,cAAc,WAAW,UAAU;AACvD,QAAM,SAAS,IAAI,wBAAY;AAAA,IAC7B,SAAS;AAAA,IACT,cAAc,YAAY;AAAA,EAC5B,CAAC;AAKD,QAAM,UAAU;AAAA,IACd,OAAO,QAAQ;AAAA,IACf;AAAA,IACA;AAAA,IACA,KAAK,CAAC;AAAA,IACN,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,EACF;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,IAAI,IAAI,iBAAiB,OAAO;AACvD,UAAM,SAAS,WAAW,KAAK;AAC/B,UAAM,cAAc,WAAW,KAAK;AAEpC,QAAI,aAAa,GAAG;AAElB,UAAI;AACF,cAAM,SAAS,SAAS,KAAK,MAAM,MAAM,IAAI;AAC7C,eAAO,gBAAgB,aAAa,QAAQ,SAAS;AAAA,MACvD,QAAQ;AAEN,eAAO,gBAAgB,aAAa,EAAE,MAAM,OAAO,GAAG,SAAS;AAAA,MACjE;AAAA,IACF,OAAO;AAEL,aAAO,cAAc,aAAa;AAAA,QAChC,MAAM,WAAW;AAAA,QACjB,SAAS,eAAe,UAAU,4BAA4B,QAAQ;AAAA,MACxE,GAAG,SAAS;AAAA,IACd;AAAA,EACF,SAAS,OAAO;AAEd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAQ,MAAM,oCAAoC,WAAW,MAAM,KAAK;AAExE,WAAO,cAAc,aAAa;AAAA,MAChC,MAAM,WAAW;AAAA,MACjB;AAAA,IACF,GAAG,SAAS;AAAA,EACd;AACF;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
interface AllowlistConfig {
|
|
2
|
+
commands: string[];
|
|
3
|
+
}
|
|
4
|
+
interface AllowlistResult {
|
|
5
|
+
allowed: boolean;
|
|
6
|
+
error?: {
|
|
7
|
+
code: 'COMMAND_NOT_ALLOWED';
|
|
8
|
+
message: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Checks if a command matches the allowlist.
|
|
13
|
+
*
|
|
14
|
+
* Defensive validation:
|
|
15
|
+
* - Normalizes command path (lowercase, trim)
|
|
16
|
+
* - Supports prefix matching for subcommands
|
|
17
|
+
*/
|
|
18
|
+
declare function checkAllowlist(commandPath: string, config: AllowlistConfig): AllowlistResult;
|
|
19
|
+
/**
|
|
20
|
+
* Default allowlist for Worker tool mode.
|
|
21
|
+
* Read-only operations by default.
|
|
22
|
+
*/
|
|
23
|
+
declare const DEFAULT_ALLOWLIST: AllowlistConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Extended allowlist including write operations.
|
|
26
|
+
* Use when agent needs to modify data.
|
|
27
|
+
*/
|
|
28
|
+
declare const WRITE_ALLOWLIST: AllowlistConfig;
|
|
29
|
+
|
|
30
|
+
interface CommandError {
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
}
|
|
34
|
+
interface CommandOutput<T = unknown> {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
command: string;
|
|
37
|
+
result?: T;
|
|
38
|
+
error?: CommandError;
|
|
39
|
+
requestId?: string;
|
|
40
|
+
schemaVersion: 1;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Creates a successful output envelope.
|
|
44
|
+
*/
|
|
45
|
+
declare function successEnvelope<T>(command: string, result: T, requestId?: string): CommandOutput<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Creates an error output envelope.
|
|
48
|
+
*/
|
|
49
|
+
declare function errorEnvelope(command: string, error: CommandError, requestId?: string): CommandOutput<never>;
|
|
50
|
+
/**
|
|
51
|
+
* Common error codes for CLI operations.
|
|
52
|
+
*/
|
|
53
|
+
declare const ErrorCodes: {
|
|
54
|
+
readonly COMMAND_NOT_ALLOWED: "COMMAND_NOT_ALLOWED";
|
|
55
|
+
readonly COMMAND_NOT_FOUND: "COMMAND_NOT_FOUND";
|
|
56
|
+
readonly INVALID_ARGUMENTS: "INVALID_ARGUMENTS";
|
|
57
|
+
readonly AUTH_ERROR: "AUTH_ERROR";
|
|
58
|
+
readonly EXECUTION_ERROR: "EXECUTION_ERROR";
|
|
59
|
+
readonly PARSE_ERROR: "PARSE_ERROR";
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
interface RunOptions {
|
|
63
|
+
/** Correlation ID for tracing */
|
|
64
|
+
requestId?: string;
|
|
65
|
+
/** Auth token for API calls */
|
|
66
|
+
authToken: string;
|
|
67
|
+
/** Base URL for Carto API */
|
|
68
|
+
apiBaseUrl: string;
|
|
69
|
+
/** Command allowlist (defaults to read-only operations) */
|
|
70
|
+
allowlist?: AllowlistConfig;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Executes a CLI command in-process.
|
|
74
|
+
*
|
|
75
|
+
* Designed for Worker runtime:
|
|
76
|
+
* - No subprocess spawning
|
|
77
|
+
* - Captures output to memory streams
|
|
78
|
+
* - Forces JSON output and non-interactive mode
|
|
79
|
+
* - Validates against allowlist
|
|
80
|
+
*
|
|
81
|
+
* @param commandLine - Command string, e.g., "folder tree --library my-lib"
|
|
82
|
+
* @param options - Execution options including auth and allowlist
|
|
83
|
+
* @returns Structured command output envelope
|
|
84
|
+
*/
|
|
85
|
+
declare function runInProcess(commandLine: string, options: RunOptions): Promise<CommandOutput>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Tokenizes a command line string, handling quoted strings.
|
|
89
|
+
*
|
|
90
|
+
* Examples:
|
|
91
|
+
* - "folder tree" => ['folder', 'tree']
|
|
92
|
+
* - "--name 'My Folder'" => ['--name', 'My Folder']
|
|
93
|
+
* - '--name "My Folder"' => ['--name', 'My Folder']
|
|
94
|
+
* - "folder create --name 'Test's Folder'" => handles escaped quotes
|
|
95
|
+
*/
|
|
96
|
+
declare function tokenize(commandLine: string): string[];
|
|
97
|
+
/**
|
|
98
|
+
* Extracts the command path from tokens (before any options).
|
|
99
|
+
*
|
|
100
|
+
* Examples:
|
|
101
|
+
* - ['folder', 'tree', '--library', 'x'] => 'folder tree'
|
|
102
|
+
* - ['item', 'search', '-q', 'hello'] => 'item search'
|
|
103
|
+
*/
|
|
104
|
+
declare function extractCommandPath(tokens: string[]): string;
|
|
105
|
+
|
|
106
|
+
interface CartoRuntime {
|
|
107
|
+
getAuthToken(): Promise<string>;
|
|
108
|
+
apiBaseUrl: string;
|
|
109
|
+
isInteractive: boolean;
|
|
110
|
+
isToolMode: boolean;
|
|
111
|
+
log(message: string): void;
|
|
112
|
+
warn(message: string): void;
|
|
113
|
+
error(message: string): void;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Worker runtime - receives auth from request context.
|
|
117
|
+
* Non-interactive, always tool mode.
|
|
118
|
+
*/
|
|
119
|
+
declare class WorkerRuntime implements CartoRuntime {
|
|
120
|
+
private authToken;
|
|
121
|
+
apiBaseUrl: string;
|
|
122
|
+
isInteractive: boolean;
|
|
123
|
+
isToolMode: boolean;
|
|
124
|
+
constructor(authToken: string, apiBaseUrl: string);
|
|
125
|
+
getAuthToken(): Promise<string>;
|
|
126
|
+
log(message: string): void;
|
|
127
|
+
warn(message: string): void;
|
|
128
|
+
error(message: string): void;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* CLI runtime - reads from config file or env vars.
|
|
132
|
+
* Interactive when terminal attached.
|
|
133
|
+
*
|
|
134
|
+
* Compatible with both Bun and Node.js runtimes.
|
|
135
|
+
*/
|
|
136
|
+
declare class CliRuntime implements CartoRuntime {
|
|
137
|
+
private config;
|
|
138
|
+
isInteractive: boolean;
|
|
139
|
+
isToolMode: boolean;
|
|
140
|
+
constructor(config: {
|
|
141
|
+
apiBaseUrl: string;
|
|
142
|
+
authToken?: string;
|
|
143
|
+
});
|
|
144
|
+
get apiBaseUrl(): string;
|
|
145
|
+
getAuthToken(): Promise<string>;
|
|
146
|
+
log(message: string): void;
|
|
147
|
+
warn(message: string): void;
|
|
148
|
+
error(message: string): void;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export { type AllowlistConfig, type CartoRuntime, CliRuntime, type CommandOutput, DEFAULT_ALLOWLIST, ErrorCodes, type RunOptions, WRITE_ALLOWLIST, WorkerRuntime, checkAllowlist, errorEnvelope, extractCommandPath, runInProcess, successEnvelope, tokenize };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
interface AllowlistConfig {
|
|
2
|
+
commands: string[];
|
|
3
|
+
}
|
|
4
|
+
interface AllowlistResult {
|
|
5
|
+
allowed: boolean;
|
|
6
|
+
error?: {
|
|
7
|
+
code: 'COMMAND_NOT_ALLOWED';
|
|
8
|
+
message: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Checks if a command matches the allowlist.
|
|
13
|
+
*
|
|
14
|
+
* Defensive validation:
|
|
15
|
+
* - Normalizes command path (lowercase, trim)
|
|
16
|
+
* - Supports prefix matching for subcommands
|
|
17
|
+
*/
|
|
18
|
+
declare function checkAllowlist(commandPath: string, config: AllowlistConfig): AllowlistResult;
|
|
19
|
+
/**
|
|
20
|
+
* Default allowlist for Worker tool mode.
|
|
21
|
+
* Read-only operations by default.
|
|
22
|
+
*/
|
|
23
|
+
declare const DEFAULT_ALLOWLIST: AllowlistConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Extended allowlist including write operations.
|
|
26
|
+
* Use when agent needs to modify data.
|
|
27
|
+
*/
|
|
28
|
+
declare const WRITE_ALLOWLIST: AllowlistConfig;
|
|
29
|
+
|
|
30
|
+
interface CommandError {
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
}
|
|
34
|
+
interface CommandOutput<T = unknown> {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
command: string;
|
|
37
|
+
result?: T;
|
|
38
|
+
error?: CommandError;
|
|
39
|
+
requestId?: string;
|
|
40
|
+
schemaVersion: 1;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Creates a successful output envelope.
|
|
44
|
+
*/
|
|
45
|
+
declare function successEnvelope<T>(command: string, result: T, requestId?: string): CommandOutput<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Creates an error output envelope.
|
|
48
|
+
*/
|
|
49
|
+
declare function errorEnvelope(command: string, error: CommandError, requestId?: string): CommandOutput<never>;
|
|
50
|
+
/**
|
|
51
|
+
* Common error codes for CLI operations.
|
|
52
|
+
*/
|
|
53
|
+
declare const ErrorCodes: {
|
|
54
|
+
readonly COMMAND_NOT_ALLOWED: "COMMAND_NOT_ALLOWED";
|
|
55
|
+
readonly COMMAND_NOT_FOUND: "COMMAND_NOT_FOUND";
|
|
56
|
+
readonly INVALID_ARGUMENTS: "INVALID_ARGUMENTS";
|
|
57
|
+
readonly AUTH_ERROR: "AUTH_ERROR";
|
|
58
|
+
readonly EXECUTION_ERROR: "EXECUTION_ERROR";
|
|
59
|
+
readonly PARSE_ERROR: "PARSE_ERROR";
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
interface RunOptions {
|
|
63
|
+
/** Correlation ID for tracing */
|
|
64
|
+
requestId?: string;
|
|
65
|
+
/** Auth token for API calls */
|
|
66
|
+
authToken: string;
|
|
67
|
+
/** Base URL for Carto API */
|
|
68
|
+
apiBaseUrl: string;
|
|
69
|
+
/** Command allowlist (defaults to read-only operations) */
|
|
70
|
+
allowlist?: AllowlistConfig;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Executes a CLI command in-process.
|
|
74
|
+
*
|
|
75
|
+
* Designed for Worker runtime:
|
|
76
|
+
* - No subprocess spawning
|
|
77
|
+
* - Captures output to memory streams
|
|
78
|
+
* - Forces JSON output and non-interactive mode
|
|
79
|
+
* - Validates against allowlist
|
|
80
|
+
*
|
|
81
|
+
* @param commandLine - Command string, e.g., "folder tree --library my-lib"
|
|
82
|
+
* @param options - Execution options including auth and allowlist
|
|
83
|
+
* @returns Structured command output envelope
|
|
84
|
+
*/
|
|
85
|
+
declare function runInProcess(commandLine: string, options: RunOptions): Promise<CommandOutput>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Tokenizes a command line string, handling quoted strings.
|
|
89
|
+
*
|
|
90
|
+
* Examples:
|
|
91
|
+
* - "folder tree" => ['folder', 'tree']
|
|
92
|
+
* - "--name 'My Folder'" => ['--name', 'My Folder']
|
|
93
|
+
* - '--name "My Folder"' => ['--name', 'My Folder']
|
|
94
|
+
* - "folder create --name 'Test's Folder'" => handles escaped quotes
|
|
95
|
+
*/
|
|
96
|
+
declare function tokenize(commandLine: string): string[];
|
|
97
|
+
/**
|
|
98
|
+
* Extracts the command path from tokens (before any options).
|
|
99
|
+
*
|
|
100
|
+
* Examples:
|
|
101
|
+
* - ['folder', 'tree', '--library', 'x'] => 'folder tree'
|
|
102
|
+
* - ['item', 'search', '-q', 'hello'] => 'item search'
|
|
103
|
+
*/
|
|
104
|
+
declare function extractCommandPath(tokens: string[]): string;
|
|
105
|
+
|
|
106
|
+
interface CartoRuntime {
|
|
107
|
+
getAuthToken(): Promise<string>;
|
|
108
|
+
apiBaseUrl: string;
|
|
109
|
+
isInteractive: boolean;
|
|
110
|
+
isToolMode: boolean;
|
|
111
|
+
log(message: string): void;
|
|
112
|
+
warn(message: string): void;
|
|
113
|
+
error(message: string): void;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Worker runtime - receives auth from request context.
|
|
117
|
+
* Non-interactive, always tool mode.
|
|
118
|
+
*/
|
|
119
|
+
declare class WorkerRuntime implements CartoRuntime {
|
|
120
|
+
private authToken;
|
|
121
|
+
apiBaseUrl: string;
|
|
122
|
+
isInteractive: boolean;
|
|
123
|
+
isToolMode: boolean;
|
|
124
|
+
constructor(authToken: string, apiBaseUrl: string);
|
|
125
|
+
getAuthToken(): Promise<string>;
|
|
126
|
+
log(message: string): void;
|
|
127
|
+
warn(message: string): void;
|
|
128
|
+
error(message: string): void;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* CLI runtime - reads from config file or env vars.
|
|
132
|
+
* Interactive when terminal attached.
|
|
133
|
+
*
|
|
134
|
+
* Compatible with both Bun and Node.js runtimes.
|
|
135
|
+
*/
|
|
136
|
+
declare class CliRuntime implements CartoRuntime {
|
|
137
|
+
private config;
|
|
138
|
+
isInteractive: boolean;
|
|
139
|
+
isToolMode: boolean;
|
|
140
|
+
constructor(config: {
|
|
141
|
+
apiBaseUrl: string;
|
|
142
|
+
authToken?: string;
|
|
143
|
+
});
|
|
144
|
+
get apiBaseUrl(): string;
|
|
145
|
+
getAuthToken(): Promise<string>;
|
|
146
|
+
log(message: string): void;
|
|
147
|
+
warn(message: string): void;
|
|
148
|
+
error(message: string): void;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export { type AllowlistConfig, type CartoRuntime, CliRuntime, type CommandOutput, DEFAULT_ALLOWLIST, ErrorCodes, type RunOptions, WRITE_ALLOWLIST, WorkerRuntime, checkAllowlist, errorEnvelope, extractCommandPath, runInProcess, successEnvelope, tokenize };
|