@shulex/cxclawctl 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/capabilities.lock.json +3 -3
- package/dist/cli.js +151 -140
- package/dist/cli.js.map +1 -1
- package/package.json +1 -2
- package/runtime/auth-wait-state.d.mts +0 -6
- package/runtime/auth-wait-state.mjs +0 -56
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { readFile as
|
|
4
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
5
5
|
|
|
6
6
|
// src/auth.ts
|
|
7
7
|
import open from "open";
|
|
@@ -12,7 +12,7 @@ var AUTH_SERVER_URL = "https://ai.solvea.cx/api_v2/ticket";
|
|
|
12
12
|
// package.json
|
|
13
13
|
var package_default = {
|
|
14
14
|
name: "@shulex/cxclawctl",
|
|
15
|
-
version: "0.1.
|
|
15
|
+
version: "0.1.13",
|
|
16
16
|
description: "Solvea CXClaw CLI and Codex plugin runtime",
|
|
17
17
|
type: "module",
|
|
18
18
|
bin: {
|
|
@@ -22,7 +22,6 @@ var package_default = {
|
|
|
22
22
|
"bin",
|
|
23
23
|
"dist/cli.js",
|
|
24
24
|
"dist/cli.js.map",
|
|
25
|
-
"runtime",
|
|
26
25
|
"capabilities.lock.json"
|
|
27
26
|
],
|
|
28
27
|
scripts: {
|
|
@@ -75,9 +74,17 @@ var HttpError = class extends Error {
|
|
|
75
74
|
this.status = status;
|
|
76
75
|
this.responseBody = responseBody;
|
|
77
76
|
this.name = "HttpError";
|
|
77
|
+
try {
|
|
78
|
+
const value = JSON.parse(responseBody);
|
|
79
|
+
const code = Number(value?.code);
|
|
80
|
+
this.responseCode = Number.isFinite(code) ? code : void 0;
|
|
81
|
+
} catch {
|
|
82
|
+
this.responseCode = void 0;
|
|
83
|
+
}
|
|
78
84
|
}
|
|
79
85
|
status;
|
|
80
86
|
responseBody;
|
|
87
|
+
responseCode;
|
|
81
88
|
};
|
|
82
89
|
function unwrap(value) {
|
|
83
90
|
if (value && typeof value === "object" && Object.keys(value).length === 1 && "data" in value) return value.data;
|
|
@@ -133,38 +140,47 @@ async function deleteSession() {
|
|
|
133
140
|
await entry().deleteCredential();
|
|
134
141
|
}
|
|
135
142
|
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
143
|
+
// src/cli-error.ts
|
|
144
|
+
var CliError = class extends Error {
|
|
145
|
+
constructor(body) {
|
|
146
|
+
super(body.message);
|
|
147
|
+
this.body = body;
|
|
148
|
+
this.name = "CliError";
|
|
149
|
+
}
|
|
150
|
+
body;
|
|
151
|
+
};
|
|
152
|
+
function notAuthenticated(message = "CXClaw \u5C1A\u672A\u767B\u5F55") {
|
|
153
|
+
return new CliError({
|
|
154
|
+
code: 3,
|
|
155
|
+
category: "auth",
|
|
156
|
+
message,
|
|
157
|
+
reason: "not_authenticated",
|
|
158
|
+
retryable: false,
|
|
159
|
+
hint: "\u8FD0\u884C cxclawctl auth login \u5B8C\u6210\u9489\u9489\u6388\u6743",
|
|
160
|
+
actions: ["cxclawctl auth login"]
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function errorOutput(error) {
|
|
164
|
+
if (error instanceof CliError) return { error: error.body };
|
|
165
|
+
return {
|
|
166
|
+
error: {
|
|
167
|
+
code: 1,
|
|
168
|
+
category: "internal",
|
|
169
|
+
message: error instanceof Error ? error.message : String(error),
|
|
170
|
+
reason: "command_failed",
|
|
171
|
+
retryable: false
|
|
172
|
+
}
|
|
173
|
+
};
|
|
160
174
|
}
|
|
161
175
|
|
|
162
176
|
// src/auth.ts
|
|
163
177
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
164
|
-
var loginInFlight;
|
|
165
178
|
function publicAuthStatus(session) {
|
|
166
179
|
return { ok: true, loggedIn: true, email: session.email, channel: session.channel, accessExpiresAt: session.accessExpiry };
|
|
167
180
|
}
|
|
181
|
+
function isAuthorizationPending(error) {
|
|
182
|
+
return error instanceof HttpError && (error.status === 428 || error.responseCode === 428);
|
|
183
|
+
}
|
|
168
184
|
async function authorizeDevice(channel, runtime) {
|
|
169
185
|
const session = { channel, accessToken: "", refreshToken: "", accessExpiry: 0, employeeId: "", email: "" };
|
|
170
186
|
const start = await runtime.start(session);
|
|
@@ -184,10 +200,10 @@ ${start.verificationUriComplete}
|
|
|
184
200
|
const token = await runtime.poll(session, start.deviceCode);
|
|
185
201
|
Object.assign(session, { accessToken: token.accessToken, refreshToken: token.refreshToken, accessExpiry: Math.floor(runtime.now() / 1e3) + token.expiresIn, employeeId: token.employee.id, email: token.employee.email });
|
|
186
202
|
await runtime.save(session);
|
|
187
|
-
runtime.log("\u9489\u9489\u6388\u6743\u6210\u529F\
|
|
203
|
+
runtime.log("\u9489\u9489\u6388\u6743\u6210\u529F\u3002");
|
|
188
204
|
return session;
|
|
189
205
|
} catch (error) {
|
|
190
|
-
if (!(error
|
|
206
|
+
if (!isAuthorizationPending(error)) throw error;
|
|
191
207
|
if (runtime.now() >= nextProgressAt) {
|
|
192
208
|
runtime.log("\u4ECD\u5728\u7B49\u5F85\u9489\u9489\u6388\u6743\u2026\u2026");
|
|
193
209
|
nextProgressAt = runtime.now() + 15e3;
|
|
@@ -197,7 +213,7 @@ ${start.verificationUriComplete}
|
|
|
197
213
|
throw new Error("device authorization expired");
|
|
198
214
|
}
|
|
199
215
|
async function runLogin(channel) {
|
|
200
|
-
return
|
|
216
|
+
return authorizeDevice(channel, {
|
|
201
217
|
start: (session) => post(session, "/auth/device/start", { clientVersion: VERSION }),
|
|
202
218
|
poll: (session, deviceCode) => post(session, "/auth/device/token", { deviceCode, clientVersion: VERSION }),
|
|
203
219
|
openBrowser: async (url) => {
|
|
@@ -207,13 +223,10 @@ async function runLogin(channel) {
|
|
|
207
223
|
sleep,
|
|
208
224
|
now: Date.now,
|
|
209
225
|
log: (message) => console.error(message)
|
|
210
|
-
})
|
|
226
|
+
});
|
|
211
227
|
}
|
|
212
228
|
function login(channel) {
|
|
213
|
-
|
|
214
|
-
loginInFlight = void 0;
|
|
215
|
-
});
|
|
216
|
-
return loginInFlight;
|
|
229
|
+
return runLogin(channel);
|
|
217
230
|
}
|
|
218
231
|
async function ensureAccess(session) {
|
|
219
232
|
if (session.accessToken && Date.now() / 1e3 + 30 < session.accessExpiry) return session;
|
|
@@ -227,19 +240,17 @@ async function resolveAuthenticated(runtime) {
|
|
|
227
240
|
return await runtime.refresh(await runtime.load());
|
|
228
241
|
} catch (error) {
|
|
229
242
|
if (error instanceof SessionNotFoundError) {
|
|
230
|
-
|
|
231
|
-
return runtime.authorize();
|
|
243
|
+
throw notAuthenticated();
|
|
232
244
|
}
|
|
233
245
|
if (error instanceof HttpError && error.status === 401) {
|
|
234
|
-
console.error("CXClaw \u767B\u5F55\u5DF2\u5931\u6548\uFF0C\u6B63\u5728\u91CD\u65B0\u53D1\u8D77\u9489\u9489\u6388\u6743\u3002");
|
|
235
246
|
await runtime.remove().catch(() => void 0);
|
|
236
|
-
|
|
247
|
+
throw notAuthenticated("CXClaw \u767B\u5F55\u5DF2\u5931\u6548");
|
|
237
248
|
}
|
|
238
249
|
throw error;
|
|
239
250
|
}
|
|
240
251
|
}
|
|
241
252
|
function authenticated() {
|
|
242
|
-
return resolveAuthenticated({ load: loadSession, refresh: ensureAccess, remove: deleteSession
|
|
253
|
+
return resolveAuthenticated({ load: loadSession, refresh: ensureAccess, remove: deleteSession });
|
|
243
254
|
}
|
|
244
255
|
async function logout() {
|
|
245
256
|
let session;
|
|
@@ -291,10 +302,10 @@ import { dirname as dirname2, resolve } from "path";
|
|
|
291
302
|
import { fileURLToPath } from "url";
|
|
292
303
|
|
|
293
304
|
// src/solvea/commands.ts
|
|
294
|
-
import { readFile as
|
|
305
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
295
306
|
|
|
296
307
|
// src/solvea/client.ts
|
|
297
|
-
import { mkdir
|
|
308
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
298
309
|
import { basename, dirname, extname } from "path";
|
|
299
310
|
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
300
311
|
var KNOWLEDGE_TYPES = /* @__PURE__ */ new Set(["QA", "LEARN_SELF", "FILE", "WEBSITE", "PRODUCT"]);
|
|
@@ -742,7 +753,7 @@ var SolveaClient = class {
|
|
|
742
753
|
await client.send(new PutObjectCommand({
|
|
743
754
|
Bucket: String(credentials.bucketName),
|
|
744
755
|
Key: key,
|
|
745
|
-
Body: await
|
|
756
|
+
Body: await readFile(filePath),
|
|
746
757
|
ContentType: contentTypes[extname(filePath).toLowerCase()] ?? "application/octet-stream"
|
|
747
758
|
}));
|
|
748
759
|
return key;
|
|
@@ -759,8 +770,8 @@ var SolveaClient = class {
|
|
|
759
770
|
signal: AbortSignal.timeout(3e4)
|
|
760
771
|
});
|
|
761
772
|
if (!response.ok) throw new Error(`HTTP ${response.status} GET ${path}: ${await response.text()}`);
|
|
762
|
-
await
|
|
763
|
-
await
|
|
773
|
+
await mkdir(dirname(target), { recursive: true });
|
|
774
|
+
await writeFile(target, new Uint8Array(await response.arrayBuffer()));
|
|
764
775
|
return target;
|
|
765
776
|
}
|
|
766
777
|
async downloadLabelTemplate(templateType, target) {
|
|
@@ -1002,10 +1013,10 @@ var SolveaClient = class {
|
|
|
1002
1013
|
};
|
|
1003
1014
|
|
|
1004
1015
|
// src/solvea/input.ts
|
|
1005
|
-
import { readFile as
|
|
1016
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1006
1017
|
async function jsonData(args, required = true) {
|
|
1007
1018
|
const file = option(args, "--data-file");
|
|
1008
|
-
const raw = file ? await
|
|
1019
|
+
const raw = file ? await readFile2(file, "utf8") : option(args, "--data");
|
|
1009
1020
|
if (!raw) {
|
|
1010
1021
|
if (required) throw new Error("--data or --data-file is required");
|
|
1011
1022
|
return {};
|
|
@@ -1016,7 +1027,7 @@ async function jsonData(args, required = true) {
|
|
|
1016
1027
|
}
|
|
1017
1028
|
async function jsonValue(args) {
|
|
1018
1029
|
const file = option(args, "--data-file");
|
|
1019
|
-
const raw = file ? await
|
|
1030
|
+
const raw = file ? await readFile2(file, "utf8") : option(args, "--data", true);
|
|
1020
1031
|
return JSON.parse(raw);
|
|
1021
1032
|
}
|
|
1022
1033
|
function requireFlag(args, name, command) {
|
|
@@ -1199,7 +1210,7 @@ function enabled(args, command) {
|
|
|
1199
1210
|
}
|
|
1200
1211
|
async function data2(args) {
|
|
1201
1212
|
const file = option(args, "--data-file");
|
|
1202
|
-
const raw = file ? await
|
|
1213
|
+
const raw = file ? await readFile3(file, "utf8") : option(args, "--data");
|
|
1203
1214
|
if (!raw) throw new Error("--data or --data-file is required");
|
|
1204
1215
|
const parsed = JSON.parse(raw);
|
|
1205
1216
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("request data must be a JSON object");
|
|
@@ -1544,13 +1555,13 @@ async function runResourceCommand(session, resource, action2, args) {
|
|
|
1544
1555
|
|
|
1545
1556
|
// src/doctor.ts
|
|
1546
1557
|
import { access } from "fs/promises";
|
|
1547
|
-
import { join
|
|
1558
|
+
import { join } from "path";
|
|
1548
1559
|
async function diagnose() {
|
|
1549
1560
|
const root2 = packageRoot();
|
|
1550
1561
|
const requiredFiles = [
|
|
1551
1562
|
"package.json"
|
|
1552
1563
|
];
|
|
1553
|
-
await Promise.all(requiredFiles.map((path) => access(
|
|
1564
|
+
await Promise.all(requiredFiles.map((path) => access(join(root2, path))));
|
|
1554
1565
|
const expectedPluginVersion = process.env.CXCLAW_PLUGIN_VERSION || null;
|
|
1555
1566
|
if (expectedPluginVersion && expectedPluginVersion !== VERSION) {
|
|
1556
1567
|
throw new Error(
|
|
@@ -1602,9 +1613,9 @@ function publicTenant(t) {
|
|
|
1602
1613
|
|
|
1603
1614
|
// src/update.ts
|
|
1604
1615
|
import { spawn } from "child_process";
|
|
1605
|
-
import { mkdir as
|
|
1616
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
1606
1617
|
import { homedir, platform } from "os";
|
|
1607
|
-
import { isAbsolute, join as
|
|
1618
|
+
import { isAbsolute, join as join2 } from "path";
|
|
1608
1619
|
var exec = (command, args, options3 = {}) => new Promise((resolve5, reject) => {
|
|
1609
1620
|
const chunks = [];
|
|
1610
1621
|
const child = spawn(command, args, {
|
|
@@ -1677,11 +1688,11 @@ async function update() {
|
|
|
1677
1688
|
async function installScheduler() {
|
|
1678
1689
|
const invocation = schedulerInvocation();
|
|
1679
1690
|
if (platform() === "darwin") {
|
|
1680
|
-
const dir =
|
|
1681
|
-
const path =
|
|
1691
|
+
const dir = join2(homedir(), "Library/LaunchAgents");
|
|
1692
|
+
const path = join2(dir, "com.solvea.cxclaw.update.plist");
|
|
1682
1693
|
const programArguments = [invocation.command, ...invocation.args].map((value) => `<string>${xml(value)}</string>`).join("");
|
|
1683
|
-
await
|
|
1684
|
-
await
|
|
1694
|
+
await mkdir2(dir, { recursive: true });
|
|
1695
|
+
await writeFile2(
|
|
1685
1696
|
path,
|
|
1686
1697
|
`<?xml version="1.0" encoding="UTF-8"?>
|
|
1687
1698
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -1693,18 +1704,18 @@ async function installScheduler() {
|
|
|
1693
1704
|
return;
|
|
1694
1705
|
}
|
|
1695
1706
|
if (platform() === "linux") {
|
|
1696
|
-
const dir =
|
|
1707
|
+
const dir = join2(homedir(), ".config/systemd/user");
|
|
1697
1708
|
const command = [invocation.command, ...invocation.args].map(systemdArg).join(" ");
|
|
1698
|
-
await
|
|
1699
|
-
await
|
|
1700
|
-
|
|
1709
|
+
await mkdir2(dir, { recursive: true });
|
|
1710
|
+
await writeFile2(
|
|
1711
|
+
join2(dir, "cxclaw-update.service"),
|
|
1701
1712
|
`[Service]
|
|
1702
1713
|
Type=oneshot
|
|
1703
1714
|
ExecStart=${command}
|
|
1704
1715
|
`
|
|
1705
1716
|
);
|
|
1706
|
-
await
|
|
1707
|
-
|
|
1717
|
+
await writeFile2(
|
|
1718
|
+
join2(dir, "cxclaw-update.timer"),
|
|
1708
1719
|
"[Timer]\nOnBootSec=5m\nOnUnitActiveSec=6h\nPersistent=true\n[Install]\nWantedBy=timers.target\n"
|
|
1709
1720
|
);
|
|
1710
1721
|
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
@@ -1743,12 +1754,12 @@ async function installMarketplace(channel) {
|
|
|
1743
1754
|
// src/workflows/runtime.ts
|
|
1744
1755
|
import { execFile } from "child_process";
|
|
1745
1756
|
import { promises as fs } from "fs";
|
|
1746
|
-
import { join as
|
|
1757
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
1747
1758
|
import { promisify } from "util";
|
|
1748
1759
|
var exec2 = promisify(execFile);
|
|
1749
|
-
var workspace = () => resolve2(process.env.WORKSPACE_DIR ??
|
|
1750
|
-
var root = () =>
|
|
1751
|
-
var dirs = () => ({ output:
|
|
1760
|
+
var workspace = () => resolve2(process.env.WORKSPACE_DIR ?? join3(process.cwd(), "workspace"));
|
|
1761
|
+
var root = () => join3(workspace(), "runtime-inspector");
|
|
1762
|
+
var dirs = () => ({ output: join3(root(), "outputs"), state: join3(root(), "state"), tmp: join3(root(), "tmp") });
|
|
1752
1763
|
var timestamp = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 15);
|
|
1753
1764
|
var readJson = async (path, fallback) => {
|
|
1754
1765
|
try {
|
|
@@ -1784,26 +1795,26 @@ async function prepare(args) {
|
|
|
1784
1795
|
await fs.mkdir(tmp, { recursive: true });
|
|
1785
1796
|
const batches = [];
|
|
1786
1797
|
for (let i = 0; i < records.length; i += batchSize) batches.push(records.slice(i, i + batchSize));
|
|
1787
|
-
await Promise.all(batches.map((batch, i) => writeJson(
|
|
1788
|
-
await writeJson(
|
|
1798
|
+
await Promise.all(batches.map((batch, i) => writeJson(join3(tmp, `batch_${String(i).padStart(3, "0")}.json`), batch)));
|
|
1799
|
+
await writeJson(join3(state2, "inspect.json"), { status: "batched", total_records: records.length, total_batches: batches.length, batch_size: batchSize, source: source ?? "auto-generated", written_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1789
1800
|
return { status: "batched", totalRecords: records.length, totalBatches: batches.length, tmp };
|
|
1790
1801
|
}
|
|
1791
1802
|
async function processBatches() {
|
|
1792
1803
|
const { tmp, output, state: state2 } = dirs();
|
|
1793
|
-
const current = await readJson(
|
|
1804
|
+
const current = await readJson(join3(state2, "inspect.json"), {});
|
|
1794
1805
|
if (current.status !== "batched") throw new Error(`runtime state must be batched, got ${String(current.status ?? "missing")}`);
|
|
1795
1806
|
const names = (await fs.readdir(tmp)).filter((name) => /^batch_\d+\.json$/.test(name)).sort();
|
|
1796
1807
|
if (!names.length) throw new Error(`no batch files found in ${tmp}`);
|
|
1797
1808
|
const records = [];
|
|
1798
|
-
for (const name of names) for (const row of await readJson(
|
|
1799
|
-
const path =
|
|
1809
|
+
for (const name of names) for (const row of await readJson(join3(tmp, name), [])) records.push({ ...row, _processed: true, _summary: `processed: ${JSON.stringify(row).slice(0, 60)}`, _at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1810
|
+
const path = join3(output, `results_${timestamp()}.json`);
|
|
1800
1811
|
await writeJson(path, { total: records.length, processed_at: (/* @__PURE__ */ new Date()).toISOString(), source: current.source, records });
|
|
1801
|
-
await writeJson(
|
|
1812
|
+
await writeJson(join3(state2, "inspect.json"), { status: "done", total_processed: records.length, results_path: path, done_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1802
1813
|
return { status: "done", total: records.length, resultsPath: path };
|
|
1803
1814
|
}
|
|
1804
1815
|
async function state() {
|
|
1805
1816
|
const { state: stateDir2, output, tmp } = dirs();
|
|
1806
|
-
return { state: await readJson(
|
|
1817
|
+
return { state: await readJson(join3(stateDir2, "inspect.json"), null), outputFiles: await fs.readdir(output).catch(() => []), tmpFiles: await fs.readdir(tmp).catch(() => []) };
|
|
1807
1818
|
}
|
|
1808
1819
|
async function verify() {
|
|
1809
1820
|
const snapshot = await state();
|
|
@@ -1830,7 +1841,7 @@ async function sandbox(args) {
|
|
|
1830
1841
|
}
|
|
1831
1842
|
async function model(args) {
|
|
1832
1843
|
if (option(args, "--scenario", true) !== "truncation") throw new Error("only --scenario truncation is supported");
|
|
1833
|
-
const target =
|
|
1844
|
+
const target = join3(dirs().tmp, "truncation_test.md");
|
|
1834
1845
|
return { scenario: "truncation", target, instruction: "Use the Codex write_file tool to write 60+ long technical paragraphs, then inspect SLG model_response stop_reason and output_tokens." };
|
|
1835
1846
|
}
|
|
1836
1847
|
async function runRuntime(action2, args) {
|
|
@@ -1843,9 +1854,9 @@ async function runRuntime(action2, args) {
|
|
|
1843
1854
|
if (action2 === "write-test") {
|
|
1844
1855
|
const { output, state: stateDir2 } = dirs();
|
|
1845
1856
|
const runId = timestamp();
|
|
1846
|
-
await writeJson(
|
|
1847
|
-
const current = await readJson(
|
|
1848
|
-
await writeJson(
|
|
1857
|
+
await writeJson(join3(output, `${runId}_test.json`), { run_id: runId, written_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1858
|
+
const current = await readJson(join3(stateDir2, "inspect.json"), {});
|
|
1859
|
+
await writeJson(join3(stateDir2, "inspect.json"), { ...current, last_run_id: runId, write_count: Number(current.write_count ?? 0) + 1 });
|
|
1849
1860
|
return { ok: true, runId };
|
|
1850
1861
|
}
|
|
1851
1862
|
throw new Error("runtime action must be prepare, process, state, verify, sandbox, model, or write-test");
|
|
@@ -1854,10 +1865,10 @@ async function runRuntime(action2, args) {
|
|
|
1854
1865
|
// src/workflows/solvea-kit.ts
|
|
1855
1866
|
import ExcelJS from "exceljs";
|
|
1856
1867
|
import { promises as fs2 } from "fs";
|
|
1857
|
-
import { dirname as dirname3, join as
|
|
1858
|
-
var workspace2 = () => resolve3(process.env.WORKSPACE_DIR ??
|
|
1859
|
-
var outputDir = (owner) =>
|
|
1860
|
-
var stateDir = (owner) =>
|
|
1868
|
+
import { dirname as dirname3, join as join4, resolve as resolve3 } from "path";
|
|
1869
|
+
var workspace2 = () => resolve3(process.env.WORKSPACE_DIR ?? join4(process.cwd(), "workspace"));
|
|
1870
|
+
var outputDir = (owner) => join4(workspace2(), "outputs", owner);
|
|
1871
|
+
var stateDir = (owner) => join4(workspace2(), "state", owner);
|
|
1861
1872
|
var now = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 15);
|
|
1862
1873
|
var readJson2 = async (path) => JSON.parse(await fs2.readFile(path, "utf8"));
|
|
1863
1874
|
var writeJson2 = async (path, value) => {
|
|
@@ -1887,7 +1898,7 @@ function labelsFrom(value) {
|
|
|
1887
1898
|
async function generateLabelImport(args) {
|
|
1888
1899
|
const input = option(args, "--input") ?? option(args, "--data-file", true);
|
|
1889
1900
|
const init = args.includes("--init");
|
|
1890
|
-
const output = option(args, "--output") ??
|
|
1901
|
+
const output = option(args, "--output") ?? join4(stateDir("solvea-kit"), init ? "labels_init.xlsx" : "labels_update.xlsx");
|
|
1891
1902
|
const labels = labelsFrom(await readJson2(input));
|
|
1892
1903
|
const workbook = new ExcelJS.Workbook();
|
|
1893
1904
|
const sheet = workbook.addWorksheet("labels");
|
|
@@ -1927,9 +1938,9 @@ async function classifyIntents(args) {
|
|
|
1927
1938
|
if (!noExcel && !option(args, "--xlsx")) throw new Error("provide --xlsx or --no-excel");
|
|
1928
1939
|
const intents = Object.entries(TAXONOMY).map(([name, l2s]) => ({ name, definition: `Customer requests and issues related to ${name}.`, count: 0, pct: 0, sub_intents: l2s.map((l2) => ({ name: l2, definition: "", count: 0, pct: 0, samples: [], is_new: false })), samples: [], is_new: false }));
|
|
1929
1940
|
const result = { meta: { run_id: runId, total: 0, classified: 0, n_known: intents.length, n_new: 0, mode: noExcel ? "no-excel" : "structured" }, intents, new_intents: [] };
|
|
1930
|
-
const resultPath =
|
|
1941
|
+
const resultPath = join4(outputDir(owner), `results_${runId}.json`);
|
|
1931
1942
|
await writeJson2(resultPath, result);
|
|
1932
|
-
await writeJson2(
|
|
1943
|
+
await writeJson2(join4(stateDir(owner), "progress.json"), { status: "ready", run_id: runId, results_path: resultPath, updated_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1933
1944
|
return { ...result.meta, resultsPath: resultPath };
|
|
1934
1945
|
}
|
|
1935
1946
|
async function runSolveaKitWorkflow(action2, args) {
|
|
@@ -1939,7 +1950,7 @@ async function runSolveaKitWorkflow(action2, args) {
|
|
|
1939
1950
|
}
|
|
1940
1951
|
|
|
1941
1952
|
// src/workflows/support.ts
|
|
1942
|
-
import { readFile as
|
|
1953
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1943
1954
|
import { basename as basename3 } from "path";
|
|
1944
1955
|
var safe = (value) => encodeURIComponent(value);
|
|
1945
1956
|
var docsMcpUrl = process.env.CXCLAW_DOCS_MCP_URL ?? "https://solvea-39f11a36.mintlify.app/mcp";
|
|
@@ -2014,7 +2025,7 @@ async function runSupport(session, action2, args) {
|
|
|
2014
2025
|
const attachments = [];
|
|
2015
2026
|
for (const file of options2(args, "--attachment")) {
|
|
2016
2027
|
const form = new FormData();
|
|
2017
|
-
form.append("file", new Blob([await
|
|
2028
|
+
form.append("file", new Blob([await readFile4(file)]), basename3(file));
|
|
2018
2029
|
const tenant = session.tenant;
|
|
2019
2030
|
const base = tenant?.businessUrl?.replace(/\/$/, "");
|
|
2020
2031
|
if (!tenant || !base || !tenant.businessToken) throw new Error("no tenant selected");
|
|
@@ -2045,24 +2056,24 @@ async function runSupport(session, action2, args) {
|
|
|
2045
2056
|
}
|
|
2046
2057
|
|
|
2047
2058
|
// src/workflows/common.ts
|
|
2048
|
-
import { mkdir as
|
|
2049
|
-
import { dirname as dirname4, join as
|
|
2059
|
+
import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
2060
|
+
import { dirname as dirname4, join as join5, resolve as resolve4 } from "path";
|
|
2050
2061
|
import { homedir as homedir2 } from "os";
|
|
2051
|
-
var workspaceRoot = () => resolve4(process.env.CXCLAW_WORKSPACE ??
|
|
2052
|
-
var stateRoot = (domain) => resolve4(process.env.CXCLAW_STATE_DIR ??
|
|
2053
|
-
var outputRoot = (domain) => resolve4(process.env.CXCLAW_OUTPUT_DIR ??
|
|
2062
|
+
var workspaceRoot = () => resolve4(process.env.CXCLAW_WORKSPACE ?? join5(homedir2(), ".cxclaw", "workspace"));
|
|
2063
|
+
var stateRoot = (domain) => resolve4(process.env.CXCLAW_STATE_DIR ?? join5(workspaceRoot(), "state"), domain);
|
|
2064
|
+
var outputRoot = (domain) => resolve4(process.env.CXCLAW_OUTPUT_DIR ?? join5(workspaceRoot(), "outputs"), domain);
|
|
2054
2065
|
async function ensureParent(file) {
|
|
2055
|
-
await
|
|
2066
|
+
await mkdir3(dirname4(file), { recursive: true });
|
|
2056
2067
|
}
|
|
2057
2068
|
async function ensureDir(directory) {
|
|
2058
|
-
await
|
|
2069
|
+
await mkdir3(directory, { recursive: true });
|
|
2059
2070
|
}
|
|
2060
2071
|
async function readJson3(file) {
|
|
2061
|
-
return JSON.parse(await
|
|
2072
|
+
return JSON.parse(await readFile5(file, "utf8"));
|
|
2062
2073
|
}
|
|
2063
2074
|
async function writeJson3(file, value) {
|
|
2064
2075
|
await ensureParent(file);
|
|
2065
|
-
await
|
|
2076
|
+
await writeFile3(file, `${JSON.stringify(value, null, 2)}
|
|
2066
2077
|
`, "utf8");
|
|
2067
2078
|
}
|
|
2068
2079
|
function intOption(args, name, fallback) {
|
|
@@ -2080,7 +2091,7 @@ function htmlEscape(value) {
|
|
|
2080
2091
|
}
|
|
2081
2092
|
async function writeHtml(file, title, body) {
|
|
2082
2093
|
await ensureParent(file);
|
|
2083
|
-
await
|
|
2094
|
+
await writeFile3(file, `<!doctype html><html><head><meta charset="utf-8"><title>${htmlEscape(title)}</title><style>body{font-family:system-ui,sans-serif;margin:2rem;line-height:1.5}pre{white-space:pre-wrap;background:#f6f8fa;padding:1rem;border-radius:8px}.card{border:1px solid #ddd;border-radius:8px;padding:1rem;margin:1rem 0}</style></head><body><h1>${htmlEscape(title)}</h1>${body}</body></html>`, "utf8");
|
|
2084
2095
|
}
|
|
2085
2096
|
function asRows(value) {
|
|
2086
2097
|
if (Array.isArray(value)) return value.filter((item) => item && typeof item === "object");
|
|
@@ -2287,13 +2298,13 @@ async function runLocalAgentWorkflow(action2, args) {
|
|
|
2287
2298
|
}
|
|
2288
2299
|
async function writeFileText(file, content) {
|
|
2289
2300
|
await ensureParent(file);
|
|
2290
|
-
const { writeFile:
|
|
2291
|
-
await
|
|
2301
|
+
const { writeFile: writeFile4 } = await import("fs/promises");
|
|
2302
|
+
await writeFile4(file, `${content}
|
|
2292
2303
|
`, "utf8");
|
|
2293
2304
|
}
|
|
2294
2305
|
|
|
2295
2306
|
// src/workflows/tester.ts
|
|
2296
|
-
import { join as
|
|
2307
|
+
import { join as join6 } from "path";
|
|
2297
2308
|
import ExcelJS2 from "exceljs";
|
|
2298
2309
|
async function casesFrom(file) {
|
|
2299
2310
|
if (file.endsWith(".json")) return asRows(await readJson3(file));
|
|
@@ -2315,7 +2326,7 @@ async function casesFrom(file) {
|
|
|
2315
2326
|
}
|
|
2316
2327
|
async function createSheet(args) {
|
|
2317
2328
|
const cases = option(args, "--cases-file") ? await casesFrom(option(args, "--cases-file")) : [];
|
|
2318
|
-
const output = option(args, "--output") ??
|
|
2329
|
+
const output = option(args, "--output") ?? join6(outputRoot("agent-tester"), `cases_${Date.now()}.xlsx`);
|
|
2319
2330
|
await ensureParent(output);
|
|
2320
2331
|
const workbook = new ExcelJS2.Workbook();
|
|
2321
2332
|
const sheet = workbook.addWorksheet("cases");
|
|
@@ -2341,7 +2352,7 @@ async function runAgentTester(session, action2, args) {
|
|
|
2341
2352
|
}
|
|
2342
2353
|
|
|
2343
2354
|
// src/workflows/delivery.ts
|
|
2344
|
-
import { join as
|
|
2355
|
+
import { join as join7 } from "path";
|
|
2345
2356
|
import ExcelJS3 from "exceljs";
|
|
2346
2357
|
var KNOWN_INTENTS = ["Product Recommendation", "Discount Inquiry", "Price Inquiry", "Stock Availability", "Payment Issues", "Request Invoice", "Modify Order", "Check Order Status", "Cancel Order", "Track Shipment", "Shipping Exception", "Missing Items", "Wrong Items Received", "Damaged Package", "Product Usage", "Return and exchange"];
|
|
2347
2358
|
async function loadRows(file) {
|
|
@@ -2375,7 +2386,7 @@ async function writeRows(file, rows) {
|
|
|
2375
2386
|
const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
2376
2387
|
sheet.addRow(keys);
|
|
2377
2388
|
rows.forEach((row) => sheet.addRow(keys.map((key) => row[key] ?? "")));
|
|
2378
|
-
await ensureDir(
|
|
2389
|
+
await ensureDir(join7(file, ".."));
|
|
2379
2390
|
await workbook.xlsx.writeFile(file);
|
|
2380
2391
|
}
|
|
2381
2392
|
function classify(text) {
|
|
@@ -2396,13 +2407,13 @@ async function classifyCases(args, domain) {
|
|
|
2396
2407
|
const intents = [...grouped.entries()].map(([name, items]) => ({ name, definition: `Customer requests and issues related to ${name}.`, count: items.length, pct: rows.length ? items.length / rows.length : 0, samples: items.slice(0, 5), is_new: name === "New Intent" }));
|
|
2397
2408
|
const runId = option(args, "--run-id") ?? Date.now().toString();
|
|
2398
2409
|
const result = { meta: { run_id: runId, total: rows.length, classified: rows.length, mode: noExcel ? "no-excel" : "structured", domain }, intents, new_intents: intents.filter((item) => item.is_new) };
|
|
2399
|
-
const out = option(args, "--results") ??
|
|
2410
|
+
const out = option(args, "--results") ?? join7(outputRoot(domain), `results_${runId}.json`);
|
|
2400
2411
|
await writeJson3(out, result);
|
|
2401
|
-
await writeJson3(
|
|
2412
|
+
await writeJson3(join7(stateRoot(domain), "progress.json"), { status: "classified", run_id: runId, results_path: out, updated_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2402
2413
|
return { ...result.meta, resultsPath: out };
|
|
2403
2414
|
}
|
|
2404
2415
|
async function plan(domain, args) {
|
|
2405
|
-
const file =
|
|
2416
|
+
const file = join7(stateRoot(domain), "progress.json");
|
|
2406
2417
|
let current = {};
|
|
2407
2418
|
try {
|
|
2408
2419
|
current = await readJson3(file);
|
|
@@ -2426,7 +2437,7 @@ async function plan(domain, args) {
|
|
|
2426
2437
|
async function runBatch(client, args, domain) {
|
|
2427
2438
|
const input = option(args, "--input") ?? option(args, "--test-file", true);
|
|
2428
2439
|
const rows = await loadRows(input);
|
|
2429
|
-
const output = option(args, "--output") ??
|
|
2440
|
+
const output = option(args, "--output") ?? join7(outputRoot(domain), `test_results_${Date.now()}.json`);
|
|
2430
2441
|
const workers = Math.max(1, Math.min(intOption(args, "--workers", 4), 16));
|
|
2431
2442
|
const results = [];
|
|
2432
2443
|
let cursor = 0;
|
|
@@ -2450,7 +2461,7 @@ async function runBatch(client, args, domain) {
|
|
|
2450
2461
|
return { output, total: results.length, succeeded: results.filter((row) => row.ok).length, failed: results.filter((row) => !row.ok).length };
|
|
2451
2462
|
}
|
|
2452
2463
|
async function createAgents(client, args, domain) {
|
|
2453
|
-
const state2 = await readJson3(
|
|
2464
|
+
const state2 = await readJson3(join7(stateRoot(domain), "progress.json")).catch(() => ({}));
|
|
2454
2465
|
const resultsPath = option(args, "--results") ?? (typeof state2.results_path === "string" ? state2.results_path : void 0);
|
|
2455
2466
|
if (!resultsPath) throw new Error("classification results are required");
|
|
2456
2467
|
const result = await readJson3(resultsPath);
|
|
@@ -2461,12 +2472,12 @@ async function createAgents(client, args, domain) {
|
|
|
2461
2472
|
return { created, count: created.length };
|
|
2462
2473
|
}
|
|
2463
2474
|
async function renderReport(args, domain) {
|
|
2464
|
-
const progress = await readJson3(
|
|
2475
|
+
const progress = await readJson3(join7(stateRoot(domain), "progress.json")).catch(() => ({}));
|
|
2465
2476
|
const resultsFile = option(args, "--results") ?? option(args, "--input") ?? (typeof progress.results_path === "string" ? progress.results_path : void 0);
|
|
2466
2477
|
if (!resultsFile) throw new Error("--results/--input is required, or run classify first");
|
|
2467
2478
|
const value = await readJson3(resultsFile);
|
|
2468
2479
|
const rows = asRows(value.results ?? value.items ?? value);
|
|
2469
|
-
const output = option(args, "--html") ?? option(args, "--output") ??
|
|
2480
|
+
const output = option(args, "--html") ?? option(args, "--output") ?? join7(outputRoot(domain), `${domain}_report.html`);
|
|
2470
2481
|
const body = `<p>Total: ${rows.length}</p>${rows.map((row, index) => `<div class="card"><h2>${index + 1}. ${htmlEscape(row.intent ?? row.question ?? row.message ?? "Case")}</h2><pre>${htmlEscape(JSON.stringify(row, null, 2))}</pre></div>`).join("")}`;
|
|
2471
2482
|
await writeHtml(output, option(args, "--title") ?? `${domain} report`, body);
|
|
2472
2483
|
const md = option(args, "--md");
|
|
@@ -2477,12 +2488,12 @@ Total: ${rows.length}
|
|
|
2477
2488
|
return { output, count: rows.length };
|
|
2478
2489
|
}
|
|
2479
2490
|
async function genExcel(args, domain) {
|
|
2480
|
-
const progress = await readJson3(
|
|
2491
|
+
const progress = await readJson3(join7(stateRoot(domain), "progress.json")).catch(() => ({}));
|
|
2481
2492
|
const input = option(args, "--results") ?? option(args, "--items") ?? (typeof progress.results_path === "string" ? progress.results_path : void 0);
|
|
2482
2493
|
if (!input) throw new Error("--results or --items is required, or run classify first");
|
|
2483
2494
|
const value = await readJson3(input);
|
|
2484
2495
|
const rows = asRows(value);
|
|
2485
|
-
const output = option(args, "--output") ??
|
|
2496
|
+
const output = option(args, "--output") ?? join7(outputRoot(domain), `${domain}_results.xlsx`);
|
|
2486
2497
|
await writeRows(output, rows);
|
|
2487
2498
|
return { output, count: rows.length };
|
|
2488
2499
|
}
|
|
@@ -2522,7 +2533,7 @@ async function runDomain(session, domain, action2, args) {
|
|
|
2522
2533
|
}
|
|
2523
2534
|
|
|
2524
2535
|
// src/workflows/kb-builder.ts
|
|
2525
|
-
import { join as
|
|
2536
|
+
import { join as join8 } from "path";
|
|
2526
2537
|
import ExcelJS4 from "exceljs";
|
|
2527
2538
|
var clean = (value) => String(value ?? "").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
2528
2539
|
var absolute = (href, base) => {
|
|
@@ -2556,7 +2567,7 @@ async function crawl(args) {
|
|
|
2556
2567
|
}
|
|
2557
2568
|
}
|
|
2558
2569
|
const result = { url, mode: option(args, "--mode") ?? "auto", pages, fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), total: pages.length };
|
|
2559
|
-
const output = option(args, "--output") ??
|
|
2570
|
+
const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), "crawl_result.json");
|
|
2560
2571
|
await writeJson3(output, result);
|
|
2561
2572
|
return { ...result, output };
|
|
2562
2573
|
}
|
|
@@ -2571,13 +2582,13 @@ async function directoryPlan(args) {
|
|
|
2571
2582
|
}
|
|
2572
2583
|
const tree = [...groups.entries()].map(([name, pages]) => ({ name, children: pages.slice(0, 100).map((page2) => ({ name: String(page2.title ?? page2.url), sourceUrl: page2.url })) }));
|
|
2573
2584
|
const result = { source: option(args, "--crawl-result"), generatedAt: (/* @__PURE__ */ new Date()).toISOString(), tree, confirmed: false };
|
|
2574
|
-
const output = option(args, "--output") ??
|
|
2585
|
+
const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), "directory_plan.json");
|
|
2575
2586
|
await writeJson3(output, result);
|
|
2576
|
-
if (flag(args, "--write-confirmed-tree")) await writeJson3(
|
|
2587
|
+
if (flag(args, "--write-confirmed-tree")) await writeJson3(join8(stateRoot("kb-builder"), "confirmed_tree.json"), { ...result, confirmed: true });
|
|
2577
2588
|
return { output, directories: tree.length, pages: rows.length, tree };
|
|
2578
2589
|
}
|
|
2579
2590
|
async function createDirectories(client, args) {
|
|
2580
|
-
const file = option(args, "--tree-file") ??
|
|
2591
|
+
const file = option(args, "--tree-file") ?? join8(stateRoot("kb-builder"), "confirmed_tree.json");
|
|
2581
2592
|
const value = await readJson3(file);
|
|
2582
2593
|
const tree = Array.isArray(value.tree) ? value.tree : Array.isArray(value) ? value : [];
|
|
2583
2594
|
if (!tree.length) throw new Error("confirmed directory tree is empty");
|
|
@@ -2600,12 +2611,12 @@ async function createDirectories(client, args) {
|
|
|
2600
2611
|
async function extractWebQa(args) {
|
|
2601
2612
|
const input = await readJson3(option(args, "--crawl-result", true));
|
|
2602
2613
|
const pages = asRows(input.pages ?? input).slice(0, intOption(args, "--max-pages", 80));
|
|
2603
|
-
const outputDir2 = option(args, "--output-dir") ??
|
|
2614
|
+
const outputDir2 = option(args, "--output-dir") ?? join8(outputRoot("kb-builder"), "qa_parts");
|
|
2604
2615
|
await ensureDir(outputDir2);
|
|
2605
2616
|
const parts = pages.map((page2, index) => ({ question: String(page2.title ?? page2.url), answer: String(page2.text ?? "").slice(0, intOption(args, "--answer-limit", 3500)), sourceUrl: page2.url, sourceTitle: page2.title, index }));
|
|
2606
2617
|
const files = [];
|
|
2607
2618
|
for (const [index, part] of parts.entries()) {
|
|
2608
|
-
const file =
|
|
2619
|
+
const file = join8(outputDir2, `part_${String(index + 1).padStart(4, "0")}.json`);
|
|
2609
2620
|
await writeJson3(file, { items: [part] });
|
|
2610
2621
|
files.push(file);
|
|
2611
2622
|
}
|
|
@@ -2671,9 +2682,9 @@ async function importQa(client, args) {
|
|
|
2671
2682
|
items.push(...Array.isArray(value) ? value : value.items && Array.isArray(value.items) ? value.items : [value]);
|
|
2672
2683
|
}
|
|
2673
2684
|
if (fromDir) {
|
|
2674
|
-
const { readdir
|
|
2675
|
-
for (const file of (await
|
|
2676
|
-
const value = await readJson3(
|
|
2685
|
+
const { readdir } = await import("fs/promises");
|
|
2686
|
+
for (const file of (await readdir(fromDir)).filter((name) => name.endsWith(".json")).sort()) {
|
|
2687
|
+
const value = await readJson3(join8(fromDir, file));
|
|
2677
2688
|
items.push(...Array.isArray(value) ? value : value.items && Array.isArray(value.items) ? value.items : [value]);
|
|
2678
2689
|
}
|
|
2679
2690
|
}
|
|
@@ -2681,7 +2692,7 @@ async function importQa(client, args) {
|
|
|
2681
2692
|
if (!Number.isSafeInteger(maxDepth) || maxDepth <= 0) throw new Error("--max-depth must be a positive integer");
|
|
2682
2693
|
const normalized = validateQaItems(items, maxDepth);
|
|
2683
2694
|
const plan2 = { items, count: items.length, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2684
|
-
const planFile =
|
|
2695
|
+
const planFile = join8(outputRoot("kb-builder"), `qa_import_plan_${Date.now()}.json`);
|
|
2685
2696
|
await writeJson3(planFile, plan2);
|
|
2686
2697
|
if (flag(args, "--dry-run")) return { dryRun: true, planFile, count: items.length };
|
|
2687
2698
|
const directories = flattenDirectories(asRows(await client.listDirectories()));
|
|
@@ -2756,7 +2767,7 @@ async function importTemplates(client, args) {
|
|
|
2756
2767
|
});
|
|
2757
2768
|
}
|
|
2758
2769
|
const items = rows.map((row) => ({ name: String(row.name ?? row.Name ?? "").trim(), content: String(row.content ?? row.Content ?? row.template ?? ""), status: row.status ?? row.Status ?? "ENABLED", scene: row.scene ?? row.Scene })).filter((item) => item.name && item.content);
|
|
2759
|
-
const output = option(args, "--output") ??
|
|
2770
|
+
const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), `template_import_plan_${Date.now()}.json`);
|
|
2760
2771
|
await writeJson3(output, { items, count: items.length });
|
|
2761
2772
|
if (flag(args, "--dry-run")) return { dryRun: true, output, count: items.length };
|
|
2762
2773
|
const results = [];
|
|
@@ -2771,13 +2782,13 @@ async function exportKnowledge(client, args) {
|
|
|
2771
2782
|
const value = await client.listKnowledge({ directoryId: id, keyword: option(args, "--keyword") ?? "", type: option(args, "--type") ?? "ALL", pageSize: intOption(args, "--page-size", 1e3) });
|
|
2772
2783
|
rows.push(...asRows(value).map((row) => ({ ...row, directoryId: id, directoryName: directories.find((dir) => Number(dir.id ?? dir.directoryId) === id)?.name ?? "" })));
|
|
2773
2784
|
}
|
|
2774
|
-
const output = option(args, "--output") ??
|
|
2785
|
+
const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), `knowledge_${Date.now()}.xlsx`);
|
|
2775
2786
|
const workbook = new ExcelJS4.Workbook();
|
|
2776
2787
|
const sheet = workbook.addWorksheet("knowledge");
|
|
2777
2788
|
const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
2778
2789
|
sheet.addRow(keys);
|
|
2779
2790
|
for (const row of rows) sheet.addRow(keys.map((key) => row[key] ?? ""));
|
|
2780
|
-
await ensureDir(
|
|
2791
|
+
await ensureDir(join8(output, ".."));
|
|
2781
2792
|
await workbook.xlsx.writeFile(output);
|
|
2782
2793
|
return { output, count: rows.length };
|
|
2783
2794
|
}
|
|
@@ -2818,7 +2829,7 @@ async function extractTicketQa(args) {
|
|
|
2818
2829
|
const answer = String(record[replyCol] ?? "").trim();
|
|
2819
2830
|
if (question && question !== `${brand}:`) items.push({ question, answer, sourceRow: number });
|
|
2820
2831
|
});
|
|
2821
|
-
const output = option(args, "--output") ??
|
|
2832
|
+
const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), `ticket_qa_${Date.now()}.json`);
|
|
2822
2833
|
const result = { brand, count: items.length, items };
|
|
2823
2834
|
await writeJson3(output, result);
|
|
2824
2835
|
if (flag(args, "--dry-run")) return { dryRun: true, output, count: items.length };
|
|
@@ -2826,12 +2837,12 @@ async function extractTicketQa(args) {
|
|
|
2826
2837
|
}
|
|
2827
2838
|
|
|
2828
2839
|
// src/workflows/desk.ts
|
|
2829
|
-
import { join as
|
|
2840
|
+
import { join as join9 } from "path";
|
|
2830
2841
|
import ExcelJS5 from "exceljs";
|
|
2831
2842
|
var taskId = (args) => option(args, "--task-id") ?? (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
2832
|
-
var taskDir = (args) =>
|
|
2843
|
+
var taskDir = (args) => join9(stateRoot("solvea-desk"), taskId(args));
|
|
2833
2844
|
async function saveDatasource(dir, name, rows) {
|
|
2834
|
-
const file =
|
|
2845
|
+
const file = join9(dir, name);
|
|
2835
2846
|
await ensureParent(file);
|
|
2836
2847
|
const workbook = new ExcelJS5.Workbook();
|
|
2837
2848
|
const sheet = workbook.addWorksheet("data");
|
|
@@ -2854,7 +2865,7 @@ async function fetchTickets(client, args) {
|
|
|
2854
2865
|
if (pageRows.length < pageSize2) break;
|
|
2855
2866
|
}
|
|
2856
2867
|
const output = await saveDatasource(dir, "datasource.xlsx", rows);
|
|
2857
|
-
await writeJson3(
|
|
2868
|
+
await writeJson3(join9(dir, "metadata.json"), { taskId: taskId(args), rows: rows.length, output });
|
|
2858
2869
|
return { taskId: taskId(args), output, count: rows.length };
|
|
2859
2870
|
}
|
|
2860
2871
|
async function fetchSources(client, args) {
|
|
@@ -2865,7 +2876,7 @@ async function fetchSources(client, args) {
|
|
|
2865
2876
|
const rows = asRows(value).slice(0, intOption(args, "--max-rows", 1e4));
|
|
2866
2877
|
sources.tickets = rows;
|
|
2867
2878
|
const output = await saveDatasource(dir, "datasource.xlsx", rows);
|
|
2868
|
-
await writeJson3(
|
|
2879
|
+
await writeJson3(join9(dir, "sources.json"), sources);
|
|
2869
2880
|
return { taskId: taskId(args), output, count: rows.length };
|
|
2870
2881
|
}
|
|
2871
2882
|
async function tags(client, action2, args) {
|
|
@@ -2892,10 +2903,10 @@ async function tags(client, action2, args) {
|
|
|
2892
2903
|
throw new Error(`unknown tag action: ${action2}`);
|
|
2893
2904
|
}
|
|
2894
2905
|
async function renderReport2(args) {
|
|
2895
|
-
const dir = taskDir(args), dataFile = option(args, "--report-data") ??
|
|
2906
|
+
const dir = taskDir(args), dataFile = option(args, "--report-data") ?? join9(dir, "report_data.json"), notesFile = option(args, "--notes") ?? join9(dir, "calculation_notes.json");
|
|
2896
2907
|
const data3 = await readJson3(dataFile);
|
|
2897
2908
|
if (flag(args, "--validate-only")) return { valid: true, dataFile };
|
|
2898
|
-
const output = option(args, "--output") ??
|
|
2909
|
+
const output = option(args, "--output") ?? join9(outputRoot("solvea-desk"), taskId(args), `${option(args, "--report-slug") ?? "report"}.html`);
|
|
2899
2910
|
const body = `<pre>${htmlEscape(JSON.stringify({ data: data3, notes: await readJson3(notesFile).catch(() => ({})) }, null, 2))}</pre>`;
|
|
2900
2911
|
await writeHtml(output, option(args, "--report-slug") ?? "Solvea Desk report", body);
|
|
2901
2912
|
return { output, valid: true };
|
|
@@ -2926,7 +2937,6 @@ async function runAuth(action2, args) {
|
|
|
2926
2937
|
const session = await login(option(args, "--channel") ?? "stable");
|
|
2927
2938
|
return print(publicAuthStatus(session));
|
|
2928
2939
|
}
|
|
2929
|
-
if (action2 === "ensure") return print(publicAuthStatus(await authenticated()));
|
|
2930
2940
|
if (action2 === "logout") {
|
|
2931
2941
|
await logout();
|
|
2932
2942
|
return print({ ok: true, loggedOut: true });
|
|
@@ -2942,7 +2952,7 @@ async function runAuth(action2, args) {
|
|
|
2942
2952
|
if (action2 === "refresh") {
|
|
2943
2953
|
return print(publicAuthStatus(await authenticated()));
|
|
2944
2954
|
}
|
|
2945
|
-
throw new Error("auth action must be login,
|
|
2955
|
+
throw new Error("auth action must be login, logout, status, or refresh");
|
|
2946
2956
|
}
|
|
2947
2957
|
async function runTenant(action2, args) {
|
|
2948
2958
|
const session = await authenticated();
|
|
@@ -3015,11 +3025,12 @@ async function main(args = process.argv.slice(2)) {
|
|
|
3015
3025
|
if (flag(args, "--scheduled")) await new Promise((resolve5) => setTimeout(resolve5, Math.random() * 18e5));
|
|
3016
3026
|
return print(await update());
|
|
3017
3027
|
}
|
|
3018
|
-
if (command === "commands" || command === "capabilities") console.log(await
|
|
3028
|
+
if (command === "commands" || command === "capabilities") console.log(await readFile6(new URL("../capabilities.lock.json", import.meta.url), "utf8"));
|
|
3019
3029
|
else throw new Error(`unknown command: ${command}`);
|
|
3020
3030
|
}
|
|
3021
3031
|
main().catch((error) => {
|
|
3022
|
-
|
|
3023
|
-
|
|
3032
|
+
const output = errorOutput(error);
|
|
3033
|
+
console.error(JSON.stringify(output));
|
|
3034
|
+
process.exitCode = output.error.code;
|
|
3024
3035
|
});
|
|
3025
3036
|
//# sourceMappingURL=cli.js.map
|