@fragno-dev/pi-fragment 0.0.1
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/LICENSE.md +16 -0
- package/README.md +107 -0
- package/bin/run.js +72 -0
- package/dist/browser/client/react.d.ts +264 -0
- package/dist/browser/client/react.d.ts.map +1 -0
- package/dist/browser/client/react.js +84 -0
- package/dist/browser/client/react.js.map +1 -0
- package/dist/browser/client/solid.d.ts +266 -0
- package/dist/browser/client/solid.d.ts.map +1 -0
- package/dist/browser/client/solid.js +122 -0
- package/dist/browser/client/solid.js.map +1 -0
- package/dist/browser/client/svelte.d.ts +261 -0
- package/dist/browser/client/svelte.d.ts.map +1 -0
- package/dist/browser/client/svelte.js +126 -0
- package/dist/browser/client/svelte.js.map +1 -0
- package/dist/browser/client/vanilla.d.ts +238 -0
- package/dist/browser/client/vanilla.d.ts.map +1 -0
- package/dist/browser/client/vanilla.js +11 -0
- package/dist/browser/client/vanilla.js.map +1 -0
- package/dist/browser/client/vue.d.ts +264 -0
- package/dist/browser/client/vue.d.ts.map +1 -0
- package/dist/browser/client/vue.js +125 -0
- package/dist/browser/client/vue.js.map +1 -0
- package/dist/browser/client-Bk-J98pf.d.ts +679 -0
- package/dist/browser/client-Bk-J98pf.d.ts.map +1 -0
- package/dist/browser/factory-DKoO_lRA.js +2470 -0
- package/dist/browser/factory-DKoO_lRA.js.map +1 -0
- package/dist/browser/index.d.ts +776 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +3 -0
- package/dist/cli/cli.d.ts +1 -0
- package/dist/cli/cli.js +10 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/config.d.ts +13 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +64 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/http/client.js +95 -0
- package/dist/cli/http/client.js.map +1 -0
- package/dist/cli/mod.d.ts +62 -0
- package/dist/cli/mod.d.ts.map +1 -0
- package/dist/cli/mod.js +644 -0
- package/dist/cli/mod.js.map +1 -0
- package/dist/cli/render/index.d.ts +23 -0
- package/dist/cli/render/index.d.ts.map +1 -0
- package/dist/cli/render/index.js +37 -0
- package/dist/cli/render/index.js.map +1 -0
- package/dist/node/index.d.ts +10 -0
- package/dist/node/index.js +9 -0
- package/dist/node/pi/clients.d.ts +240 -0
- package/dist/node/pi/clients.d.ts.map +1 -0
- package/dist/node/pi/clients.js +18 -0
- package/dist/node/pi/clients.js.map +1 -0
- package/dist/node/pi/constants.d.ts +9 -0
- package/dist/node/pi/constants.d.ts.map +1 -0
- package/dist/node/pi/constants.js +22 -0
- package/dist/node/pi/constants.js.map +1 -0
- package/dist/node/pi/definition.d.ts +13 -0
- package/dist/node/pi/definition.d.ts.map +1 -0
- package/dist/node/pi/definition.js +10 -0
- package/dist/node/pi/definition.js.map +1 -0
- package/dist/node/pi/dsl.d.ts +24 -0
- package/dist/node/pi/dsl.d.ts.map +1 -0
- package/dist/node/pi/dsl.js +57 -0
- package/dist/node/pi/dsl.js.map +1 -0
- package/dist/node/pi/factory.d.ts +220 -0
- package/dist/node/pi/factory.d.ts.map +1 -0
- package/dist/node/pi/factory.js +12 -0
- package/dist/node/pi/factory.js.map +1 -0
- package/dist/node/pi/mappers.js +47 -0
- package/dist/node/pi/mappers.js.map +1 -0
- package/dist/node/pi/route-schemas.js +112 -0
- package/dist/node/pi/route-schemas.js.map +1 -0
- package/dist/node/pi/types.d.ts +67 -0
- package/dist/node/pi/types.d.ts.map +1 -0
- package/dist/node/pi/workflow.d.ts +31 -0
- package/dist/node/pi/workflow.d.ts.map +1 -0
- package/dist/node/pi/workflow.js +242 -0
- package/dist/node/pi/workflow.js.map +1 -0
- package/dist/node/routes.d.ts +217 -0
- package/dist/node/routes.d.ts.map +1 -0
- package/dist/node/routes.js +328 -0
- package/dist/node/routes.js.map +1 -0
- package/dist/node/schema.js +12 -0
- package/dist/node/schema.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +125 -0
package/dist/cli/mod.js
ADDED
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
import { resolveConfig } from "./config.js";
|
|
2
|
+
import { createHttpClient } from "./http/client.js";
|
|
3
|
+
import { renderOutput } from "./render/index.js";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
|
|
6
|
+
//#region src/cli/mod.ts
|
|
7
|
+
const USAGE = `fragno-pi <command> [options]
|
|
8
|
+
|
|
9
|
+
Commands:
|
|
10
|
+
sessions list List pi-fragment sessions
|
|
11
|
+
sessions create Create a pi-fragment session
|
|
12
|
+
sessions get Fetch session detail/status
|
|
13
|
+
sessions send-message Send a user message to a session
|
|
14
|
+
|
|
15
|
+
Global options:
|
|
16
|
+
-b, --base-url <url> Fragment base URL (FRAGNO_PI_BASE_URL)
|
|
17
|
+
-H, --header <header> Extra HTTP header (repeatable)
|
|
18
|
+
--timeout <ms> Request timeout in ms (default: 15000)
|
|
19
|
+
--retries <n> Retry count (default: 2)
|
|
20
|
+
--retry-delay <ms> Delay between retries (default: 500)
|
|
21
|
+
--json Output raw JSON
|
|
22
|
+
--debug Log request metadata to stderr
|
|
23
|
+
-h, --help Show this help message
|
|
24
|
+
|
|
25
|
+
sessions list:
|
|
26
|
+
--limit <n> Limit results (default server: 50)
|
|
27
|
+
|
|
28
|
+
sessions create:
|
|
29
|
+
--agent <name> Agent name (required)
|
|
30
|
+
--name <label> Session label
|
|
31
|
+
--tag <tag> Tag (repeatable)
|
|
32
|
+
--metadata <json> Metadata JSON string
|
|
33
|
+
--steering-mode <mode> all|one-at-a-time
|
|
34
|
+
|
|
35
|
+
sessions get:
|
|
36
|
+
-s, --session <id> Session id (or positional)
|
|
37
|
+
--status-only Only output status fields
|
|
38
|
+
|
|
39
|
+
sessions send-message:
|
|
40
|
+
-s, --session <id> Session id (or positional)
|
|
41
|
+
--text <message> Message text
|
|
42
|
+
--file <path> Read message text from file
|
|
43
|
+
--done Mark session done
|
|
44
|
+
--steering-mode <mode> all|one-at-a-time`;
|
|
45
|
+
const buildErrorResult = (message, usage = USAGE) => ({
|
|
46
|
+
stderr: `${message}\n\n${usage}`,
|
|
47
|
+
exitCode: 1
|
|
48
|
+
});
|
|
49
|
+
const requireBaseUrl = (config) => {
|
|
50
|
+
if (config.baseUrl) return null;
|
|
51
|
+
return buildErrorResult("Missing required option: --base-url (or FRAGNO_PI_BASE_URL)");
|
|
52
|
+
};
|
|
53
|
+
const debugLog = (config, logger, message) => {
|
|
54
|
+
if (!config.debug) return;
|
|
55
|
+
if (logger?.error) {
|
|
56
|
+
logger.error(message);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
console.error(message);
|
|
60
|
+
};
|
|
61
|
+
const readResponseBody = async (response) => {
|
|
62
|
+
const text = await response.text();
|
|
63
|
+
if (!text) return;
|
|
64
|
+
try {
|
|
65
|
+
return JSON.parse(text);
|
|
66
|
+
} catch {
|
|
67
|
+
return text;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const formatErrorMessage = (status, body) => {
|
|
71
|
+
if (body && typeof body === "object" && "message" in body) {
|
|
72
|
+
const message = body.message;
|
|
73
|
+
if (message) return `Request failed (${status}): ${message}`;
|
|
74
|
+
}
|
|
75
|
+
if (typeof body === "string" && body.trim()) return `Request failed (${status}): ${body}`;
|
|
76
|
+
return `Request failed (${status}).`;
|
|
77
|
+
};
|
|
78
|
+
const buildClient = (config) => createHttpClient({
|
|
79
|
+
baseUrl: config.baseUrl ?? "",
|
|
80
|
+
headers: config.headers,
|
|
81
|
+
timeoutMs: config.timeoutMs,
|
|
82
|
+
retries: config.retries,
|
|
83
|
+
retryDelayMs: config.retryDelayMs
|
|
84
|
+
});
|
|
85
|
+
const requestJson = async (config, logger, options) => {
|
|
86
|
+
const baseError = requireBaseUrl(config);
|
|
87
|
+
if (baseError) return {
|
|
88
|
+
ok: false,
|
|
89
|
+
error: baseError
|
|
90
|
+
};
|
|
91
|
+
const client = buildClient(config);
|
|
92
|
+
debugLog(config, logger, `request ${options.method} ${options.path}`);
|
|
93
|
+
try {
|
|
94
|
+
const response = await client.request({
|
|
95
|
+
method: options.method,
|
|
96
|
+
path: options.path,
|
|
97
|
+
body: options.body
|
|
98
|
+
});
|
|
99
|
+
const body = await readResponseBody(response);
|
|
100
|
+
debugLog(config, logger, `response ${response.status} ${options.method} ${options.path}`);
|
|
101
|
+
if (!response.ok) return {
|
|
102
|
+
ok: false,
|
|
103
|
+
error: {
|
|
104
|
+
stderr: formatErrorMessage(response.status, body),
|
|
105
|
+
exitCode: 2
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
ok: true,
|
|
110
|
+
data: body
|
|
111
|
+
};
|
|
112
|
+
} catch (error) {
|
|
113
|
+
return {
|
|
114
|
+
ok: false,
|
|
115
|
+
error: {
|
|
116
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
117
|
+
exitCode: 2
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const getRecordValue = (record, key) => {
|
|
123
|
+
const value = record[key];
|
|
124
|
+
return value === null || value === void 0 ? "" : String(value);
|
|
125
|
+
};
|
|
126
|
+
const buildSessionsListOutput = (data, json) => {
|
|
127
|
+
if (!Array.isArray(data)) return {
|
|
128
|
+
stderr: "Unexpected response for sessions list.",
|
|
129
|
+
exitCode: 2
|
|
130
|
+
};
|
|
131
|
+
if (json) return { output: {
|
|
132
|
+
format: "json",
|
|
133
|
+
data
|
|
134
|
+
} };
|
|
135
|
+
return { output: {
|
|
136
|
+
format: "table",
|
|
137
|
+
columns: [
|
|
138
|
+
{
|
|
139
|
+
key: "id",
|
|
140
|
+
label: "ID"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
key: "agent",
|
|
144
|
+
label: "Agent"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
key: "name",
|
|
148
|
+
label: "Name"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
key: "status",
|
|
152
|
+
label: "Status"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
key: "updated",
|
|
156
|
+
label: "Updated"
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
rows: data.map((session) => {
|
|
160
|
+
if (session && typeof session === "object") {
|
|
161
|
+
const record = session;
|
|
162
|
+
return {
|
|
163
|
+
id: getRecordValue(record, "id"),
|
|
164
|
+
agent: getRecordValue(record, "agent"),
|
|
165
|
+
name: getRecordValue(record, "name"),
|
|
166
|
+
status: getRecordValue(record, "status"),
|
|
167
|
+
updated: getRecordValue(record, "updatedAt")
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
id: "",
|
|
172
|
+
agent: "",
|
|
173
|
+
name: "",
|
|
174
|
+
status: "",
|
|
175
|
+
updated: ""
|
|
176
|
+
};
|
|
177
|
+
})
|
|
178
|
+
} };
|
|
179
|
+
};
|
|
180
|
+
const buildDetailOutput = (data, json) => {
|
|
181
|
+
if (json) return { output: {
|
|
182
|
+
format: "json",
|
|
183
|
+
data
|
|
184
|
+
} };
|
|
185
|
+
return { output: {
|
|
186
|
+
format: "pretty-json",
|
|
187
|
+
data
|
|
188
|
+
} };
|
|
189
|
+
};
|
|
190
|
+
const buildSendMessageOutput = (data, json) => {
|
|
191
|
+
if (json) return { output: {
|
|
192
|
+
format: "json",
|
|
193
|
+
data
|
|
194
|
+
} };
|
|
195
|
+
if (!data || typeof data !== "object") return { output: {
|
|
196
|
+
format: "pretty-json",
|
|
197
|
+
data
|
|
198
|
+
} };
|
|
199
|
+
const record = data;
|
|
200
|
+
const status = record["status"];
|
|
201
|
+
const assistant = record["assistant"];
|
|
202
|
+
const hasAssistant = assistant !== void 0 && assistant !== null;
|
|
203
|
+
const lines = [];
|
|
204
|
+
if (status !== void 0) lines.push(`Status: ${String(status)}`);
|
|
205
|
+
if (hasAssistant) {
|
|
206
|
+
const assistantText = typeof assistant === "string" ? assistant : JSON.stringify(assistant, null, 2);
|
|
207
|
+
lines.push(assistantText);
|
|
208
|
+
} else if (status !== void 0) lines.push("Message accepted. Use `sessions get` to fetch the response.");
|
|
209
|
+
if (lines.length === 0) return { output: {
|
|
210
|
+
format: "pretty-json",
|
|
211
|
+
data
|
|
212
|
+
} };
|
|
213
|
+
return { output: {
|
|
214
|
+
format: "text",
|
|
215
|
+
text: lines.join("\n\n")
|
|
216
|
+
} };
|
|
217
|
+
};
|
|
218
|
+
const resolveMessageText = async (args) => {
|
|
219
|
+
if (args.text) return {
|
|
220
|
+
ok: true,
|
|
221
|
+
text: args.text
|
|
222
|
+
};
|
|
223
|
+
if (!args.file) return {
|
|
224
|
+
ok: false,
|
|
225
|
+
error: buildErrorResult("Missing required option: --text (or --file)")
|
|
226
|
+
};
|
|
227
|
+
try {
|
|
228
|
+
return {
|
|
229
|
+
ok: true,
|
|
230
|
+
text: await readFile(args.file, "utf8")
|
|
231
|
+
};
|
|
232
|
+
} catch (error) {
|
|
233
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
234
|
+
return {
|
|
235
|
+
ok: false,
|
|
236
|
+
error: buildErrorResult(`Unable to read --file ${args.file}: ${message}`)
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const defaultActions = {
|
|
241
|
+
sessionsList: async (args, ctx) => {
|
|
242
|
+
const query = new URLSearchParams();
|
|
243
|
+
if (args.limit !== void 0) query.set("limit", String(args.limit));
|
|
244
|
+
const path = query.size ? `/sessions?${query.toString()}` : "/sessions";
|
|
245
|
+
const response = await requestJson(ctx.config, ctx.logger, {
|
|
246
|
+
method: "GET",
|
|
247
|
+
path
|
|
248
|
+
});
|
|
249
|
+
if (!response.ok) return response.error;
|
|
250
|
+
return buildSessionsListOutput(response.data, ctx.config.json);
|
|
251
|
+
},
|
|
252
|
+
sessionsCreate: async (args, ctx) => {
|
|
253
|
+
const body = { agent: args.agent };
|
|
254
|
+
if (args.name) body["name"] = args.name;
|
|
255
|
+
if (args.tags && args.tags.length > 0) body["tags"] = args.tags;
|
|
256
|
+
if (args.metadata !== void 0) body["metadata"] = args.metadata;
|
|
257
|
+
if (args.steeringMode) body["steeringMode"] = args.steeringMode;
|
|
258
|
+
const response = await requestJson(ctx.config, ctx.logger, {
|
|
259
|
+
method: "POST",
|
|
260
|
+
path: "/sessions",
|
|
261
|
+
body
|
|
262
|
+
});
|
|
263
|
+
if (!response.ok) return response.error;
|
|
264
|
+
return buildDetailOutput(response.data, ctx.config.json);
|
|
265
|
+
},
|
|
266
|
+
sessionsGet: async (args, ctx) => {
|
|
267
|
+
const path = `/sessions/${encodeURIComponent(args.sessionId)}`;
|
|
268
|
+
const response = await requestJson(ctx.config, ctx.logger, {
|
|
269
|
+
method: "GET",
|
|
270
|
+
path
|
|
271
|
+
});
|
|
272
|
+
if (!response.ok) return response.error;
|
|
273
|
+
const data = response.data;
|
|
274
|
+
return buildDetailOutput(args.statusOnly && data && typeof data === "object" ? {
|
|
275
|
+
status: data["status"],
|
|
276
|
+
workflow: data["workflow"],
|
|
277
|
+
summaries: data["summaries"]
|
|
278
|
+
} : data, ctx.config.json);
|
|
279
|
+
},
|
|
280
|
+
sessionsSendMessage: async (args, ctx) => {
|
|
281
|
+
const resolved = await resolveMessageText(args);
|
|
282
|
+
if (!resolved.ok) return resolved.error;
|
|
283
|
+
const body = { text: resolved.text };
|
|
284
|
+
if (args.done) body["done"] = true;
|
|
285
|
+
if (args.steeringMode) body["steeringMode"] = args.steeringMode;
|
|
286
|
+
const path = `/sessions/${encodeURIComponent(args.sessionId)}/messages`;
|
|
287
|
+
const response = await requestJson(ctx.config, ctx.logger, {
|
|
288
|
+
method: "POST",
|
|
289
|
+
path,
|
|
290
|
+
body
|
|
291
|
+
});
|
|
292
|
+
if (!response.ok) return response.error;
|
|
293
|
+
return buildSendMessageOutput(response.data, ctx.config.json);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
const VALUE_OPTIONS = new Set([
|
|
297
|
+
"base-url",
|
|
298
|
+
"header",
|
|
299
|
+
"timeout",
|
|
300
|
+
"retries",
|
|
301
|
+
"retry-delay",
|
|
302
|
+
"limit",
|
|
303
|
+
"agent",
|
|
304
|
+
"name",
|
|
305
|
+
"tag",
|
|
306
|
+
"metadata",
|
|
307
|
+
"steering-mode",
|
|
308
|
+
"session",
|
|
309
|
+
"text",
|
|
310
|
+
"file"
|
|
311
|
+
]);
|
|
312
|
+
const SHORT_OPTIONS = {
|
|
313
|
+
"-b": "base-url",
|
|
314
|
+
"-H": "header",
|
|
315
|
+
"-s": "session"
|
|
316
|
+
};
|
|
317
|
+
const ALL_LONG_OPTIONS = new Set([
|
|
318
|
+
...VALUE_OPTIONS,
|
|
319
|
+
"json",
|
|
320
|
+
"debug",
|
|
321
|
+
"status-only",
|
|
322
|
+
"done"
|
|
323
|
+
]);
|
|
324
|
+
const ALL_SHORT_OPTIONS = new Set(Object.keys(SHORT_OPTIONS));
|
|
325
|
+
const editDistance = (left, right) => {
|
|
326
|
+
const leftLen = left.length;
|
|
327
|
+
const rightLen = right.length;
|
|
328
|
+
const dp = Array.from({ length: rightLen + 1 }, (_, index) => index);
|
|
329
|
+
for (let i = 1; i <= leftLen; i += 1) {
|
|
330
|
+
let prev = dp[0] ?? 0;
|
|
331
|
+
dp[0] = i;
|
|
332
|
+
const leftChar = left[i - 1];
|
|
333
|
+
for (let j = 1; j <= rightLen; j += 1) {
|
|
334
|
+
const temp = dp[j] ?? 0;
|
|
335
|
+
const cost = leftChar === right[j - 1] ? 0 : 1;
|
|
336
|
+
dp[j] = Math.min((dp[j] ?? 0) + 1, (dp[j - 1] ?? 0) + 1, prev + cost);
|
|
337
|
+
prev = temp;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return dp[rightLen] ?? Math.max(leftLen, rightLen);
|
|
341
|
+
};
|
|
342
|
+
const suggestLongOption = (key) => {
|
|
343
|
+
let best = null;
|
|
344
|
+
for (const candidate of ALL_LONG_OPTIONS) {
|
|
345
|
+
const score = editDistance(key, candidate);
|
|
346
|
+
if (!best || score < best.score) best = {
|
|
347
|
+
key: candidate,
|
|
348
|
+
score
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
if (best && best.score <= 3) return best.key;
|
|
352
|
+
return null;
|
|
353
|
+
};
|
|
354
|
+
const addOption = (options, key, value) => {
|
|
355
|
+
if (typeof value === "boolean") {
|
|
356
|
+
options[key] = value;
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const current = options[key];
|
|
360
|
+
if (!current) {
|
|
361
|
+
options[key] = value;
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (Array.isArray(current)) {
|
|
365
|
+
current.push(value);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
options[key] = [String(current), value];
|
|
369
|
+
};
|
|
370
|
+
const coerceBooleanValue = (value) => {
|
|
371
|
+
const normalized = value.trim().toLowerCase();
|
|
372
|
+
if (normalized === "false" || normalized === "0" || normalized === "no") return false;
|
|
373
|
+
if (normalized === "true" || normalized === "1" || normalized === "yes") return true;
|
|
374
|
+
return value;
|
|
375
|
+
};
|
|
376
|
+
const parseArgs = (argv) => {
|
|
377
|
+
const options = {};
|
|
378
|
+
const positionals = [];
|
|
379
|
+
const errors = [];
|
|
380
|
+
let help = false;
|
|
381
|
+
let i = 0;
|
|
382
|
+
while (i < argv.length) {
|
|
383
|
+
const arg = argv[i];
|
|
384
|
+
if (arg === "--") {
|
|
385
|
+
positionals.push(...argv.slice(i + 1));
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
if (arg === "--help" || arg === "-h") {
|
|
389
|
+
help = true;
|
|
390
|
+
i += 1;
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (arg.startsWith("--")) {
|
|
394
|
+
const [rawKey, rawValue] = arg.split("=", 2);
|
|
395
|
+
const key = rawKey.slice(2);
|
|
396
|
+
if (!ALL_LONG_OPTIONS.has(key)) {
|
|
397
|
+
const suggestion = suggestLongOption(key);
|
|
398
|
+
errors.push(`Unknown option: --${key}${suggestion ? ` (did you mean --${suggestion}?)` : ""}`);
|
|
399
|
+
if (rawValue !== void 0) {
|
|
400
|
+
i += 1;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
const next = argv[i + 1];
|
|
404
|
+
if (next && !next.startsWith("-")) i += 2;
|
|
405
|
+
else i += 1;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
if (rawValue !== void 0) {
|
|
409
|
+
addOption(options, key, VALUE_OPTIONS.has(key) ? rawValue : coerceBooleanValue(rawValue));
|
|
410
|
+
i += 1;
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
if (VALUE_OPTIONS.has(key)) {
|
|
414
|
+
const next = argv[i + 1];
|
|
415
|
+
if (!next || next.startsWith("-")) {
|
|
416
|
+
errors.push(`Missing value for --${key}`);
|
|
417
|
+
i += 1;
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
addOption(options, key, next);
|
|
421
|
+
i += 2;
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
addOption(options, key, true);
|
|
425
|
+
i += 1;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (arg.startsWith("-") && arg.length > 1) {
|
|
429
|
+
if (arg === "-h") {
|
|
430
|
+
help = true;
|
|
431
|
+
i += 1;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (!ALL_SHORT_OPTIONS.has(arg)) {
|
|
435
|
+
errors.push(`Unknown option: ${arg}`);
|
|
436
|
+
i += 1;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
const key = SHORT_OPTIONS[arg];
|
|
440
|
+
if (key) {
|
|
441
|
+
const next = argv[i + 1];
|
|
442
|
+
if (!next || next.startsWith("-")) {
|
|
443
|
+
errors.push(`Missing value for ${arg}`);
|
|
444
|
+
i += 1;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
addOption(options, key, next);
|
|
448
|
+
i += 2;
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
positionals.push(arg);
|
|
452
|
+
i += 1;
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
positionals.push(arg);
|
|
456
|
+
i += 1;
|
|
457
|
+
}
|
|
458
|
+
const [command, action, ...rest] = positionals;
|
|
459
|
+
return {
|
|
460
|
+
command,
|
|
461
|
+
action,
|
|
462
|
+
options,
|
|
463
|
+
positionals: rest,
|
|
464
|
+
help,
|
|
465
|
+
errors
|
|
466
|
+
};
|
|
467
|
+
};
|
|
468
|
+
const getStringOption = (options, key) => {
|
|
469
|
+
const value = options[key];
|
|
470
|
+
if (Array.isArray(value)) return value[0];
|
|
471
|
+
if (typeof value === "string") return value;
|
|
472
|
+
};
|
|
473
|
+
const getStringArrayOption = (options, key) => {
|
|
474
|
+
const value = options[key];
|
|
475
|
+
if (Array.isArray(value)) return value.map(String);
|
|
476
|
+
if (typeof value === "string") return [value];
|
|
477
|
+
};
|
|
478
|
+
const getBooleanOption = (options, key) => Boolean(options[key]);
|
|
479
|
+
const parseNumberOption = (options, key) => {
|
|
480
|
+
const value = getStringOption(options, key);
|
|
481
|
+
if (!value) return;
|
|
482
|
+
const parsed = Number(value);
|
|
483
|
+
if (!Number.isFinite(parsed)) throw new Error(`Invalid value for --${key}`);
|
|
484
|
+
return parsed;
|
|
485
|
+
};
|
|
486
|
+
const parseSteeringMode = (value) => {
|
|
487
|
+
if (!value) return;
|
|
488
|
+
if (value === "all" || value === "one-at-a-time") return value;
|
|
489
|
+
throw new Error("Invalid --steering-mode. Expected: all|one-at-a-time");
|
|
490
|
+
};
|
|
491
|
+
const resolveRunOptions = (loggerOrOptions) => {
|
|
492
|
+
if (!loggerOrOptions) return {};
|
|
493
|
+
if ("log" in loggerOrOptions) return { logger: loggerOrOptions };
|
|
494
|
+
return loggerOrOptions;
|
|
495
|
+
};
|
|
496
|
+
const buildConfig = (options) => {
|
|
497
|
+
return resolveConfig({
|
|
498
|
+
baseUrl: getStringOption(options, "base-url"),
|
|
499
|
+
headers: getStringArrayOption(options, "header"),
|
|
500
|
+
timeoutMs: getStringOption(options, "timeout"),
|
|
501
|
+
retries: getStringOption(options, "retries"),
|
|
502
|
+
retryDelayMs: getStringOption(options, "retry-delay"),
|
|
503
|
+
json: getBooleanOption(options, "json"),
|
|
504
|
+
debug: getBooleanOption(options, "debug")
|
|
505
|
+
});
|
|
506
|
+
};
|
|
507
|
+
const renderResult = (result, config) => {
|
|
508
|
+
if (!result) return {};
|
|
509
|
+
if (!result.stdout && result.output) return {
|
|
510
|
+
...result,
|
|
511
|
+
stdout: renderOutput(result.output, config.json)
|
|
512
|
+
};
|
|
513
|
+
return result;
|
|
514
|
+
};
|
|
515
|
+
async function run(argv, loggerOrOptions = console) {
|
|
516
|
+
const options = resolveRunOptions(loggerOrOptions);
|
|
517
|
+
const logger = options.logger ?? console;
|
|
518
|
+
const actions = options.actions ?? defaultActions;
|
|
519
|
+
const parsed = parseArgs(argv.slice(2));
|
|
520
|
+
if (parsed.errors.length > 0) {
|
|
521
|
+
const result = buildErrorResult(parsed.errors.join("\n"));
|
|
522
|
+
logger.error(result.stderr ?? "");
|
|
523
|
+
return result.exitCode ?? 1;
|
|
524
|
+
}
|
|
525
|
+
if (parsed.help || !parsed.command || parsed.command === "help") {
|
|
526
|
+
logger.log(USAGE);
|
|
527
|
+
return parsed.command && !parsed.help ? 1 : 0;
|
|
528
|
+
}
|
|
529
|
+
let config;
|
|
530
|
+
try {
|
|
531
|
+
config = buildConfig(parsed.options);
|
|
532
|
+
} catch (error) {
|
|
533
|
+
const result = buildErrorResult(error instanceof Error ? error.message : String(error));
|
|
534
|
+
logger.error(result.stderr ?? "");
|
|
535
|
+
return result.exitCode ?? 1;
|
|
536
|
+
}
|
|
537
|
+
const ctx = {
|
|
538
|
+
config,
|
|
539
|
+
logger
|
|
540
|
+
};
|
|
541
|
+
const command = parsed.command;
|
|
542
|
+
const action = parsed.action;
|
|
543
|
+
const opts = parsed.options;
|
|
544
|
+
try {
|
|
545
|
+
let result;
|
|
546
|
+
switch (command) {
|
|
547
|
+
case "sessions":
|
|
548
|
+
if (!action) {
|
|
549
|
+
result = buildErrorResult("Missing sessions action.");
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
switch (action) {
|
|
553
|
+
case "list": {
|
|
554
|
+
const limit = parseNumberOption(opts, "limit");
|
|
555
|
+
result = await actions.sessionsList({ limit }, ctx);
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
case "create": {
|
|
559
|
+
const agent = getStringOption(opts, "agent");
|
|
560
|
+
if (!agent) {
|
|
561
|
+
result = buildErrorResult("Missing required option: --agent");
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
const steeringMode = parseSteeringMode(getStringOption(opts, "steering-mode"));
|
|
565
|
+
const metadataRaw = getStringOption(opts, "metadata");
|
|
566
|
+
let metadata;
|
|
567
|
+
if (metadataRaw) try {
|
|
568
|
+
metadata = JSON.parse(metadataRaw);
|
|
569
|
+
} catch {
|
|
570
|
+
result = buildErrorResult("Invalid JSON for --metadata");
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
result = await actions.sessionsCreate({
|
|
574
|
+
agent,
|
|
575
|
+
name: getStringOption(opts, "name"),
|
|
576
|
+
tags: getStringArrayOption(opts, "tag"),
|
|
577
|
+
metadata,
|
|
578
|
+
steeringMode
|
|
579
|
+
}, ctx);
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
case "get": {
|
|
583
|
+
const sessionId = getStringOption(opts, "session") ?? parsed.positionals[0];
|
|
584
|
+
if (!sessionId) {
|
|
585
|
+
result = buildErrorResult("Missing required option: --session");
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
result = await actions.sessionsGet({
|
|
589
|
+
sessionId,
|
|
590
|
+
statusOnly: getBooleanOption(opts, "status-only")
|
|
591
|
+
}, ctx);
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
case "send-message": {
|
|
595
|
+
const sessionId = getStringOption(opts, "session") ?? parsed.positionals[0];
|
|
596
|
+
if (!sessionId) {
|
|
597
|
+
result = buildErrorResult("Missing required option: --session");
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
const steeringMode = parseSteeringMode(getStringOption(opts, "steering-mode"));
|
|
601
|
+
const text = getStringOption(opts, "text");
|
|
602
|
+
const file = getStringOption(opts, "file");
|
|
603
|
+
if (!text && !file) {
|
|
604
|
+
result = buildErrorResult("Missing required option: --text (or --file)");
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
result = await actions.sessionsSendMessage({
|
|
608
|
+
sessionId,
|
|
609
|
+
text,
|
|
610
|
+
file,
|
|
611
|
+
done: getBooleanOption(opts, "done"),
|
|
612
|
+
steeringMode
|
|
613
|
+
}, ctx);
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
default:
|
|
617
|
+
result = buildErrorResult(`Unknown sessions action: ${action}`);
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
break;
|
|
621
|
+
default:
|
|
622
|
+
result = buildErrorResult(`Unknown command: ${command}`);
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
const resolved = renderResult(result, config);
|
|
626
|
+
if (resolved.stdout) logger.log(resolved.stdout);
|
|
627
|
+
if (resolved.stderr) logger.error(resolved.stderr);
|
|
628
|
+
if (typeof resolved.exitCode === "number") return resolved.exitCode;
|
|
629
|
+
return resolved.stderr ? 1 : 0;
|
|
630
|
+
} catch (error) {
|
|
631
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
632
|
+
logger.error(`${message}\n\n${USAGE}`);
|
|
633
|
+
return 1;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const __testing = {
|
|
637
|
+
USAGE,
|
|
638
|
+
parseArgs,
|
|
639
|
+
renderOutput
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
//#endregion
|
|
643
|
+
export { __testing, run };
|
|
644
|
+
//# sourceMappingURL=mod.js.map
|