@shulex/cxclawctl 0.1.8 → 0.1.10

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/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { readFile as readFile6 } from "fs/promises";
4
+ import { readFile as readFile7 } 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.8",
15
+ version: "0.1.10",
16
16
  description: "Solvea CXClaw CLI and Codex plugin runtime",
17
17
  type: "module",
18
18
  bin: {
@@ -22,6 +22,7 @@ var package_default = {
22
22
  "bin",
23
23
  "dist/cli.js",
24
24
  "dist/cli.js.map",
25
+ "runtime",
25
26
  "capabilities.lock.json"
26
27
  ],
27
28
  scripts: {
@@ -33,6 +34,7 @@ var package_default = {
33
34
  "check:release": "tsx scripts/check-release.ts",
34
35
  "test:package": "tsx scripts/test-packed-package.ts",
35
36
  "release:prepare": "tsx scripts/prepare-release.ts",
37
+ "release:create": "tsx scripts/create-release.ts",
36
38
  "package:agent-plugin": "node scripts/package-agent-plugin.mjs",
37
39
  "package:codex-plugin": "node scripts/package-codex-plugin.mjs"
38
40
  },
@@ -67,6 +69,16 @@ var TOOL_SCHEMA_VERSION = "1";
67
69
  var SERVER_API_VERSION = "1";
68
70
 
69
71
  // src/http.ts
72
+ var HttpError = class extends Error {
73
+ constructor(status, responseBody) {
74
+ super(`server returned ${status}: ${responseBody}`);
75
+ this.status = status;
76
+ this.responseBody = responseBody;
77
+ this.name = "HttpError";
78
+ }
79
+ status;
80
+ responseBody;
81
+ };
70
82
  function unwrap(value) {
71
83
  if (value && typeof value === "object" && Object.keys(value).length === 1 && "data" in value) return value.data;
72
84
  return value;
@@ -86,10 +98,11 @@ async function request(session, path, init = {}, tenant = false) {
86
98
  const response = await fetch(`${AUTH_SERVER_URL}${path}`, { ...init, headers, signal: AbortSignal.timeout(3e4) });
87
99
  const text = await response.text();
88
100
  const value = text ? JSON.parse(text) : void 0;
89
- if (!response.ok) throw new Error(`server returned ${response.status}: ${text}`);
101
+ if (!response.ok) throw new HttpError(response.status, text);
90
102
  return unwrap(value);
91
103
  } catch (error) {
92
104
  last = error;
105
+ if (error instanceof HttpError && error.status < 500) break;
93
106
  if (attempt < 2) await new Promise((r) => setTimeout(r, 200 * 2 ** attempt));
94
107
  }
95
108
  }
@@ -101,9 +114,15 @@ var post = (session, path, body, tenant = false) => request(session, path, { met
101
114
  import { AsyncEntry } from "@napi-rs/keyring";
102
115
  var options = process.platform === "linux" ? { linux: { store: "secret-service" } } : void 0;
103
116
  var entry = () => new AsyncEntry("com.solvea.cxclaw", "session", options);
117
+ var SessionNotFoundError = class extends Error {
118
+ constructor() {
119
+ super("\u672A\u767B\u5F55");
120
+ this.name = "SessionNotFoundError";
121
+ }
122
+ };
104
123
  async function loadSession() {
105
124
  const raw = await entry().getPassword();
106
- if (!raw) throw new Error("\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u8FD0\u884C cxclawctl auth login");
125
+ if (!raw) throw new SessionNotFoundError();
107
126
  return JSON.parse(raw);
108
127
  }
109
128
  async function saveSession(session) {
@@ -114,32 +133,88 @@ async function deleteSession() {
114
133
  await entry().deleteCredential();
115
134
  }
116
135
 
136
+ // runtime/auth-wait-state.mjs
137
+ import { createHash } from "crypto";
138
+ import { mkdir, readFile, readdir, rm, writeFile } from "fs/promises";
139
+ import { tmpdir } from "os";
140
+ import { join } from "path";
141
+ var markerRoot = join(tmpdir(), "cxclaw-auth-wait");
142
+ var markerTtlMs = 15 * 60 * 1e3;
143
+ var markerName = (sessionId, pid) => `${createHash("sha256").update(sessionId).digest("hex").slice(0, 24)}-${pid}.json`;
144
+ async function withAuthWaitMarker(operation, env = process.env) {
145
+ const sessionId = env.CODEX_SESSION_ID ?? env.CODEX_THREAD_ID;
146
+ if (!sessionId) return operation();
147
+ await mkdir(markerRoot, { recursive: true, mode: 448 });
148
+ const path = join(markerRoot, markerName(sessionId, process.pid));
149
+ await writeFile(path, JSON.stringify({
150
+ sessionId,
151
+ threadId: env.CODEX_THREAD_ID ?? null,
152
+ pid: process.pid,
153
+ expiresAt: Date.now() + markerTtlMs
154
+ }), { mode: 384 });
155
+ try {
156
+ return await operation();
157
+ } finally {
158
+ await rm(path, { force: true }).catch(() => void 0);
159
+ }
160
+ }
161
+
117
162
  // src/auth.ts
118
163
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
164
+ var loginInFlight;
119
165
  function publicAuthStatus(session) {
120
166
  return { ok: true, loggedIn: true, email: session.email, channel: session.channel, accessExpiresAt: session.accessExpiry };
121
167
  }
122
- async function login(channel) {
168
+ async function authorizeDevice(channel, runtime) {
123
169
  const session = { channel, accessToken: "", refreshToken: "", accessExpiry: 0, employeeId: "", email: "" };
124
- const start = await post(session, "/auth/device/start", { clientVersion: VERSION });
125
- console.error(`\u8BF7\u5728\u6D4F\u89C8\u5668\u6253\u5F00\u4EE5\u4E0B\u5730\u5740\u5B8C\u6210\u9489\u9489\u6388\u6743\uFF1A
170
+ const start = await runtime.start(session);
171
+ try {
172
+ await runtime.openBrowser(start.verificationUriComplete);
173
+ runtime.log("\u9489\u9489\u6388\u6743\u9875\u5DF2\u6253\u5F00\uFF0C\u5F53\u524D\u547D\u4EE4\u4F1A\u6301\u7EED\u7B49\u5F85\u6388\u6743\u7ED3\u679C\uFF0C\u65E0\u9700\u56DE\u590D Codex\u3002");
174
+ } catch {
175
+ runtime.log(`\u672A\u80FD\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF0C\u8BF7\u6253\u5F00\u4EE5\u4E0B\u5730\u5740\u5B8C\u6210\u9489\u9489\u6388\u6743\uFF1A
126
176
  ${start.verificationUriComplete}
127
177
  \u8BBE\u5907\u7801\uFF1A${start.userCode}`);
128
- await open(start.verificationUriComplete).catch(() => void 0);
129
- const deadline = Date.now() + start.expiresIn * 1e3;
130
- while (Date.now() < deadline) {
131
- await sleep(Math.max(2, start.interval) * 1e3);
178
+ }
179
+ const deadline = runtime.now() + start.expiresIn * 1e3;
180
+ let nextProgressAt = runtime.now() + 15e3;
181
+ while (runtime.now() < deadline) {
182
+ await runtime.sleep(Math.max(2, start.interval) * 1e3);
132
183
  try {
133
- const token = await post(session, "/auth/device/token", { deviceCode: start.deviceCode, clientVersion: VERSION });
134
- Object.assign(session, { accessToken: token.accessToken, refreshToken: token.refreshToken, accessExpiry: Math.floor(Date.now() / 1e3) + token.expiresIn, employeeId: token.employee.id, email: token.employee.email });
135
- await saveSession(session);
184
+ const token = await runtime.poll(session, start.deviceCode);
185
+ 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
+ await runtime.save(session);
187
+ runtime.log("\u9489\u9489\u6388\u6743\u6210\u529F\uFF0C\u7EE7\u7EED\u6267\u884C\u5F53\u524D\u547D\u4EE4\u3002");
136
188
  return session;
137
189
  } catch (error) {
138
- if (!String(error).includes("428")) throw error;
190
+ if (!(error instanceof HttpError) || error.status !== 428) throw error;
191
+ if (runtime.now() >= nextProgressAt) {
192
+ runtime.log("\u4ECD\u5728\u7B49\u5F85\u9489\u9489\u6388\u6743\u2026\u2026");
193
+ nextProgressAt = runtime.now() + 15e3;
194
+ }
139
195
  }
140
196
  }
141
197
  throw new Error("device authorization expired");
142
198
  }
199
+ async function runLogin(channel) {
200
+ return withAuthWaitMarker(() => authorizeDevice(channel, {
201
+ start: (session) => post(session, "/auth/device/start", { clientVersion: VERSION }),
202
+ poll: (session, deviceCode) => post(session, "/auth/device/token", { deviceCode, clientVersion: VERSION }),
203
+ openBrowser: async (url) => {
204
+ await open(url);
205
+ },
206
+ save: saveSession,
207
+ sleep,
208
+ now: Date.now,
209
+ log: (message) => console.error(message)
210
+ }));
211
+ }
212
+ function login(channel) {
213
+ if (!loginInFlight) loginInFlight = runLogin(channel).finally(() => {
214
+ loginInFlight = void 0;
215
+ });
216
+ return loginInFlight;
217
+ }
143
218
  async function ensureAccess(session) {
144
219
  if (session.accessToken && Date.now() / 1e3 + 30 < session.accessExpiry) return session;
145
220
  const token = await post(session, "/auth/refresh", { refreshToken: session.refreshToken, clientVersion: VERSION });
@@ -147,8 +222,24 @@ async function ensureAccess(session) {
147
222
  await saveSession(session);
148
223
  return session;
149
224
  }
150
- async function authenticated() {
151
- return ensureAccess(await loadSession());
225
+ async function resolveAuthenticated(runtime) {
226
+ try {
227
+ return await runtime.refresh(await runtime.load());
228
+ } catch (error) {
229
+ if (error instanceof SessionNotFoundError) {
230
+ console.error("CXClaw \u5C1A\u672A\u6388\u6743\uFF0C\u6B63\u5728\u81EA\u52A8\u53D1\u8D77\u9489\u9489\u6388\u6743\u3002");
231
+ return runtime.authorize();
232
+ }
233
+ 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
+ await runtime.remove().catch(() => void 0);
236
+ return runtime.authorize();
237
+ }
238
+ throw error;
239
+ }
240
+ }
241
+ function authenticated() {
242
+ return resolveAuthenticated({ load: loadSession, refresh: ensureAccess, remove: deleteSession, authorize: () => login("stable") });
152
243
  }
153
244
  async function logout() {
154
245
  let session;
@@ -200,10 +291,10 @@ import { dirname as dirname2, resolve } from "path";
200
291
  import { fileURLToPath } from "url";
201
292
 
202
293
  // src/solvea/commands.ts
203
- import { readFile as readFile3 } from "fs/promises";
294
+ import { readFile as readFile4 } from "fs/promises";
204
295
 
205
296
  // src/solvea/client.ts
206
- import { mkdir, readFile, writeFile } from "fs/promises";
297
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
207
298
  import { basename, dirname, extname } from "path";
208
299
  import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
209
300
  var KNOWLEDGE_TYPES = /* @__PURE__ */ new Set(["QA", "LEARN_SELF", "FILE", "WEBSITE", "PRODUCT"]);
@@ -651,7 +742,7 @@ var SolveaClient = class {
651
742
  await client.send(new PutObjectCommand({
652
743
  Bucket: String(credentials.bucketName),
653
744
  Key: key,
654
- Body: await readFile(filePath),
745
+ Body: await readFile2(filePath),
655
746
  ContentType: contentTypes[extname(filePath).toLowerCase()] ?? "application/octet-stream"
656
747
  }));
657
748
  return key;
@@ -668,8 +759,8 @@ var SolveaClient = class {
668
759
  signal: AbortSignal.timeout(3e4)
669
760
  });
670
761
  if (!response.ok) throw new Error(`HTTP ${response.status} GET ${path}: ${await response.text()}`);
671
- await mkdir(dirname(target), { recursive: true });
672
- await writeFile(target, new Uint8Array(await response.arrayBuffer()));
762
+ await mkdir2(dirname(target), { recursive: true });
763
+ await writeFile2(target, new Uint8Array(await response.arrayBuffer()));
673
764
  return target;
674
765
  }
675
766
  async downloadLabelTemplate(templateType, target) {
@@ -911,10 +1002,10 @@ var SolveaClient = class {
911
1002
  };
912
1003
 
913
1004
  // src/solvea/input.ts
914
- import { readFile as readFile2 } from "fs/promises";
1005
+ import { readFile as readFile3 } from "fs/promises";
915
1006
  async function jsonData(args, required = true) {
916
1007
  const file = option(args, "--data-file");
917
- const raw = file ? await readFile2(file, "utf8") : option(args, "--data");
1008
+ const raw = file ? await readFile3(file, "utf8") : option(args, "--data");
918
1009
  if (!raw) {
919
1010
  if (required) throw new Error("--data or --data-file is required");
920
1011
  return {};
@@ -925,7 +1016,7 @@ async function jsonData(args, required = true) {
925
1016
  }
926
1017
  async function jsonValue(args) {
927
1018
  const file = option(args, "--data-file");
928
- const raw = file ? await readFile2(file, "utf8") : option(args, "--data", true);
1019
+ const raw = file ? await readFile3(file, "utf8") : option(args, "--data", true);
929
1020
  return JSON.parse(raw);
930
1021
  }
931
1022
  function requireFlag(args, name, command) {
@@ -1108,7 +1199,7 @@ function enabled(args, command) {
1108
1199
  }
1109
1200
  async function data2(args) {
1110
1201
  const file = option(args, "--data-file");
1111
- const raw = file ? await readFile3(file, "utf8") : option(args, "--data");
1202
+ const raw = file ? await readFile4(file, "utf8") : option(args, "--data");
1112
1203
  if (!raw) throw new Error("--data or --data-file is required");
1113
1204
  const parsed = JSON.parse(raw);
1114
1205
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("request data must be a JSON object");
@@ -1453,13 +1544,13 @@ async function runResourceCommand(session, resource, action2, args) {
1453
1544
 
1454
1545
  // src/doctor.ts
1455
1546
  import { access } from "fs/promises";
1456
- import { join } from "path";
1547
+ import { join as join2 } from "path";
1457
1548
  async function diagnose() {
1458
1549
  const root2 = packageRoot();
1459
1550
  const requiredFiles = [
1460
1551
  "package.json"
1461
1552
  ];
1462
- await Promise.all(requiredFiles.map((path) => access(join(root2, path))));
1553
+ await Promise.all(requiredFiles.map((path) => access(join2(root2, path))));
1463
1554
  const expectedPluginVersion = process.env.CXCLAW_PLUGIN_VERSION || null;
1464
1555
  if (expectedPluginVersion && expectedPluginVersion !== VERSION) {
1465
1556
  throw new Error(
@@ -1511,9 +1602,9 @@ function publicTenant(t) {
1511
1602
 
1512
1603
  // src/update.ts
1513
1604
  import { spawn } from "child_process";
1514
- import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
1605
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
1515
1606
  import { homedir, platform } from "os";
1516
- import { isAbsolute, join as join2 } from "path";
1607
+ import { isAbsolute, join as join3 } from "path";
1517
1608
  var exec = (command, args, options3 = {}) => new Promise((resolve5, reject) => {
1518
1609
  const chunks = [];
1519
1610
  const child = spawn(command, args, {
@@ -1586,11 +1677,11 @@ async function update() {
1586
1677
  async function installScheduler() {
1587
1678
  const invocation = schedulerInvocation();
1588
1679
  if (platform() === "darwin") {
1589
- const dir = join2(homedir(), "Library/LaunchAgents");
1590
- const path = join2(dir, "com.solvea.cxclaw.update.plist");
1680
+ const dir = join3(homedir(), "Library/LaunchAgents");
1681
+ const path = join3(dir, "com.solvea.cxclaw.update.plist");
1591
1682
  const programArguments = [invocation.command, ...invocation.args].map((value) => `<string>${xml(value)}</string>`).join("");
1592
- await mkdir2(dir, { recursive: true });
1593
- await writeFile2(
1683
+ await mkdir3(dir, { recursive: true });
1684
+ await writeFile3(
1594
1685
  path,
1595
1686
  `<?xml version="1.0" encoding="UTF-8"?>
1596
1687
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -1602,18 +1693,18 @@ async function installScheduler() {
1602
1693
  return;
1603
1694
  }
1604
1695
  if (platform() === "linux") {
1605
- const dir = join2(homedir(), ".config/systemd/user");
1696
+ const dir = join3(homedir(), ".config/systemd/user");
1606
1697
  const command = [invocation.command, ...invocation.args].map(systemdArg).join(" ");
1607
- await mkdir2(dir, { recursive: true });
1608
- await writeFile2(
1609
- join2(dir, "cxclaw-update.service"),
1698
+ await mkdir3(dir, { recursive: true });
1699
+ await writeFile3(
1700
+ join3(dir, "cxclaw-update.service"),
1610
1701
  `[Service]
1611
1702
  Type=oneshot
1612
1703
  ExecStart=${command}
1613
1704
  `
1614
1705
  );
1615
- await writeFile2(
1616
- join2(dir, "cxclaw-update.timer"),
1706
+ await writeFile3(
1707
+ join3(dir, "cxclaw-update.timer"),
1617
1708
  "[Timer]\nOnBootSec=5m\nOnUnitActiveSec=6h\nPersistent=true\n[Install]\nWantedBy=timers.target\n"
1618
1709
  );
1619
1710
  await exec("systemctl", ["--user", "daemon-reload"]);
@@ -1652,12 +1743,12 @@ async function installMarketplace(channel) {
1652
1743
  // src/workflows/runtime.ts
1653
1744
  import { execFile } from "child_process";
1654
1745
  import { promises as fs } from "fs";
1655
- import { join as join3, resolve as resolve2 } from "path";
1746
+ import { join as join4, resolve as resolve2 } from "path";
1656
1747
  import { promisify } from "util";
1657
1748
  var exec2 = promisify(execFile);
1658
- var workspace = () => resolve2(process.env.WORKSPACE_DIR ?? join3(process.cwd(), "workspace"));
1659
- var root = () => join3(workspace(), "runtime-inspector");
1660
- var dirs = () => ({ output: join3(root(), "outputs"), state: join3(root(), "state"), tmp: join3(root(), "tmp") });
1749
+ var workspace = () => resolve2(process.env.WORKSPACE_DIR ?? join4(process.cwd(), "workspace"));
1750
+ var root = () => join4(workspace(), "runtime-inspector");
1751
+ var dirs = () => ({ output: join4(root(), "outputs"), state: join4(root(), "state"), tmp: join4(root(), "tmp") });
1661
1752
  var timestamp = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 15);
1662
1753
  var readJson = async (path, fallback) => {
1663
1754
  try {
@@ -1693,26 +1784,26 @@ async function prepare(args) {
1693
1784
  await fs.mkdir(tmp, { recursive: true });
1694
1785
  const batches = [];
1695
1786
  for (let i = 0; i < records.length; i += batchSize) batches.push(records.slice(i, i + batchSize));
1696
- await Promise.all(batches.map((batch, i) => writeJson(join3(tmp, `batch_${String(i).padStart(3, "0")}.json`), batch)));
1697
- 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() });
1787
+ await Promise.all(batches.map((batch, i) => writeJson(join4(tmp, `batch_${String(i).padStart(3, "0")}.json`), batch)));
1788
+ await writeJson(join4(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() });
1698
1789
  return { status: "batched", totalRecords: records.length, totalBatches: batches.length, tmp };
1699
1790
  }
1700
1791
  async function processBatches() {
1701
1792
  const { tmp, output, state: state2 } = dirs();
1702
- const current = await readJson(join3(state2, "inspect.json"), {});
1793
+ const current = await readJson(join4(state2, "inspect.json"), {});
1703
1794
  if (current.status !== "batched") throw new Error(`runtime state must be batched, got ${String(current.status ?? "missing")}`);
1704
1795
  const names = (await fs.readdir(tmp)).filter((name) => /^batch_\d+\.json$/.test(name)).sort();
1705
1796
  if (!names.length) throw new Error(`no batch files found in ${tmp}`);
1706
1797
  const records = [];
1707
- 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() });
1708
- const path = join3(output, `results_${timestamp()}.json`);
1798
+ for (const name of names) for (const row of await readJson(join4(tmp, name), [])) records.push({ ...row, _processed: true, _summary: `processed: ${JSON.stringify(row).slice(0, 60)}`, _at: (/* @__PURE__ */ new Date()).toISOString() });
1799
+ const path = join4(output, `results_${timestamp()}.json`);
1709
1800
  await writeJson(path, { total: records.length, processed_at: (/* @__PURE__ */ new Date()).toISOString(), source: current.source, records });
1710
- await writeJson(join3(state2, "inspect.json"), { status: "done", total_processed: records.length, results_path: path, done_at: (/* @__PURE__ */ new Date()).toISOString() });
1801
+ await writeJson(join4(state2, "inspect.json"), { status: "done", total_processed: records.length, results_path: path, done_at: (/* @__PURE__ */ new Date()).toISOString() });
1711
1802
  return { status: "done", total: records.length, resultsPath: path };
1712
1803
  }
1713
1804
  async function state() {
1714
1805
  const { state: stateDir2, output, tmp } = dirs();
1715
- return { state: await readJson(join3(stateDir2, "inspect.json"), null), outputFiles: await fs.readdir(output).catch(() => []), tmpFiles: await fs.readdir(tmp).catch(() => []) };
1806
+ return { state: await readJson(join4(stateDir2, "inspect.json"), null), outputFiles: await fs.readdir(output).catch(() => []), tmpFiles: await fs.readdir(tmp).catch(() => []) };
1716
1807
  }
1717
1808
  async function verify() {
1718
1809
  const snapshot = await state();
@@ -1739,7 +1830,7 @@ async function sandbox(args) {
1739
1830
  }
1740
1831
  async function model(args) {
1741
1832
  if (option(args, "--scenario", true) !== "truncation") throw new Error("only --scenario truncation is supported");
1742
- const target = join3(dirs().tmp, "truncation_test.md");
1833
+ const target = join4(dirs().tmp, "truncation_test.md");
1743
1834
  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." };
1744
1835
  }
1745
1836
  async function runRuntime(action2, args) {
@@ -1752,9 +1843,9 @@ async function runRuntime(action2, args) {
1752
1843
  if (action2 === "write-test") {
1753
1844
  const { output, state: stateDir2 } = dirs();
1754
1845
  const runId = timestamp();
1755
- await writeJson(join3(output, `${runId}_test.json`), { run_id: runId, written_at: (/* @__PURE__ */ new Date()).toISOString() });
1756
- const current = await readJson(join3(stateDir2, "inspect.json"), {});
1757
- await writeJson(join3(stateDir2, "inspect.json"), { ...current, last_run_id: runId, write_count: Number(current.write_count ?? 0) + 1 });
1846
+ await writeJson(join4(output, `${runId}_test.json`), { run_id: runId, written_at: (/* @__PURE__ */ new Date()).toISOString() });
1847
+ const current = await readJson(join4(stateDir2, "inspect.json"), {});
1848
+ await writeJson(join4(stateDir2, "inspect.json"), { ...current, last_run_id: runId, write_count: Number(current.write_count ?? 0) + 1 });
1758
1849
  return { ok: true, runId };
1759
1850
  }
1760
1851
  throw new Error("runtime action must be prepare, process, state, verify, sandbox, model, or write-test");
@@ -1763,10 +1854,10 @@ async function runRuntime(action2, args) {
1763
1854
  // src/workflows/solvea-kit.ts
1764
1855
  import ExcelJS from "exceljs";
1765
1856
  import { promises as fs2 } from "fs";
1766
- import { dirname as dirname3, join as join4, resolve as resolve3 } from "path";
1767
- var workspace2 = () => resolve3(process.env.WORKSPACE_DIR ?? join4(process.cwd(), "workspace"));
1768
- var outputDir = (owner) => join4(workspace2(), "outputs", owner);
1769
- var stateDir = (owner) => join4(workspace2(), "state", owner);
1857
+ import { dirname as dirname3, join as join5, resolve as resolve3 } from "path";
1858
+ var workspace2 = () => resolve3(process.env.WORKSPACE_DIR ?? join5(process.cwd(), "workspace"));
1859
+ var outputDir = (owner) => join5(workspace2(), "outputs", owner);
1860
+ var stateDir = (owner) => join5(workspace2(), "state", owner);
1770
1861
  var now = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 15);
1771
1862
  var readJson2 = async (path) => JSON.parse(await fs2.readFile(path, "utf8"));
1772
1863
  var writeJson2 = async (path, value) => {
@@ -1796,7 +1887,7 @@ function labelsFrom(value) {
1796
1887
  async function generateLabelImport(args) {
1797
1888
  const input = option(args, "--input") ?? option(args, "--data-file", true);
1798
1889
  const init = args.includes("--init");
1799
- const output = option(args, "--output") ?? join4(stateDir("solvea-kit"), init ? "labels_init.xlsx" : "labels_update.xlsx");
1890
+ const output = option(args, "--output") ?? join5(stateDir("solvea-kit"), init ? "labels_init.xlsx" : "labels_update.xlsx");
1800
1891
  const labels = labelsFrom(await readJson2(input));
1801
1892
  const workbook = new ExcelJS.Workbook();
1802
1893
  const sheet = workbook.addWorksheet("labels");
@@ -1836,9 +1927,9 @@ async function classifyIntents(args) {
1836
1927
  if (!noExcel && !option(args, "--xlsx")) throw new Error("provide --xlsx or --no-excel");
1837
1928
  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 }));
1838
1929
  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: [] };
1839
- const resultPath = join4(outputDir(owner), `results_${runId}.json`);
1930
+ const resultPath = join5(outputDir(owner), `results_${runId}.json`);
1840
1931
  await writeJson2(resultPath, result);
1841
- await writeJson2(join4(stateDir(owner), "progress.json"), { status: "ready", run_id: runId, results_path: resultPath, updated_at: (/* @__PURE__ */ new Date()).toISOString() });
1932
+ await writeJson2(join5(stateDir(owner), "progress.json"), { status: "ready", run_id: runId, results_path: resultPath, updated_at: (/* @__PURE__ */ new Date()).toISOString() });
1842
1933
  return { ...result.meta, resultsPath: resultPath };
1843
1934
  }
1844
1935
  async function runSolveaKitWorkflow(action2, args) {
@@ -1848,7 +1939,7 @@ async function runSolveaKitWorkflow(action2, args) {
1848
1939
  }
1849
1940
 
1850
1941
  // src/workflows/support.ts
1851
- import { readFile as readFile4 } from "fs/promises";
1942
+ import { readFile as readFile5 } from "fs/promises";
1852
1943
  import { basename as basename3 } from "path";
1853
1944
  var safe = (value) => encodeURIComponent(value);
1854
1945
  var docsMcpUrl = process.env.CXCLAW_DOCS_MCP_URL ?? "https://solvea-39f11a36.mintlify.app/mcp";
@@ -1923,7 +2014,7 @@ async function runSupport(session, action2, args) {
1923
2014
  const attachments = [];
1924
2015
  for (const file of options2(args, "--attachment")) {
1925
2016
  const form = new FormData();
1926
- form.append("file", new Blob([await readFile4(file)]), basename3(file));
2017
+ form.append("file", new Blob([await readFile5(file)]), basename3(file));
1927
2018
  const tenant = session.tenant;
1928
2019
  const base = tenant?.businessUrl?.replace(/\/$/, "");
1929
2020
  if (!tenant || !base || !tenant.businessToken) throw new Error("no tenant selected");
@@ -1954,24 +2045,24 @@ async function runSupport(session, action2, args) {
1954
2045
  }
1955
2046
 
1956
2047
  // src/workflows/common.ts
1957
- import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
1958
- import { dirname as dirname4, join as join5, resolve as resolve4 } from "path";
2048
+ import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
2049
+ import { dirname as dirname4, join as join6, resolve as resolve4 } from "path";
1959
2050
  import { homedir as homedir2 } from "os";
1960
- var workspaceRoot = () => resolve4(process.env.CXCLAW_WORKSPACE ?? join5(homedir2(), ".cxclaw", "workspace"));
1961
- var stateRoot = (domain) => resolve4(process.env.CXCLAW_STATE_DIR ?? join5(workspaceRoot(), "state"), domain);
1962
- var outputRoot = (domain) => resolve4(process.env.CXCLAW_OUTPUT_DIR ?? join5(workspaceRoot(), "outputs"), domain);
2051
+ var workspaceRoot = () => resolve4(process.env.CXCLAW_WORKSPACE ?? join6(homedir2(), ".cxclaw", "workspace"));
2052
+ var stateRoot = (domain) => resolve4(process.env.CXCLAW_STATE_DIR ?? join6(workspaceRoot(), "state"), domain);
2053
+ var outputRoot = (domain) => resolve4(process.env.CXCLAW_OUTPUT_DIR ?? join6(workspaceRoot(), "outputs"), domain);
1963
2054
  async function ensureParent(file) {
1964
- await mkdir3(dirname4(file), { recursive: true });
2055
+ await mkdir4(dirname4(file), { recursive: true });
1965
2056
  }
1966
2057
  async function ensureDir(directory) {
1967
- await mkdir3(directory, { recursive: true });
2058
+ await mkdir4(directory, { recursive: true });
1968
2059
  }
1969
2060
  async function readJson3(file) {
1970
- return JSON.parse(await readFile5(file, "utf8"));
2061
+ return JSON.parse(await readFile6(file, "utf8"));
1971
2062
  }
1972
2063
  async function writeJson3(file, value) {
1973
2064
  await ensureParent(file);
1974
- await writeFile3(file, `${JSON.stringify(value, null, 2)}
2065
+ await writeFile4(file, `${JSON.stringify(value, null, 2)}
1975
2066
  `, "utf8");
1976
2067
  }
1977
2068
  function intOption(args, name, fallback) {
@@ -1989,7 +2080,7 @@ function htmlEscape(value) {
1989
2080
  }
1990
2081
  async function writeHtml(file, title, body) {
1991
2082
  await ensureParent(file);
1992
- 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");
2083
+ await writeFile4(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");
1993
2084
  }
1994
2085
  function asRows(value) {
1995
2086
  if (Array.isArray(value)) return value.filter((item) => item && typeof item === "object");
@@ -2196,13 +2287,13 @@ async function runLocalAgentWorkflow(action2, args) {
2196
2287
  }
2197
2288
  async function writeFileText(file, content) {
2198
2289
  await ensureParent(file);
2199
- const { writeFile: writeFile4 } = await import("fs/promises");
2200
- await writeFile4(file, `${content}
2290
+ const { writeFile: writeFile5 } = await import("fs/promises");
2291
+ await writeFile5(file, `${content}
2201
2292
  `, "utf8");
2202
2293
  }
2203
2294
 
2204
2295
  // src/workflows/tester.ts
2205
- import { join as join6 } from "path";
2296
+ import { join as join7 } from "path";
2206
2297
  import ExcelJS2 from "exceljs";
2207
2298
  async function casesFrom(file) {
2208
2299
  if (file.endsWith(".json")) return asRows(await readJson3(file));
@@ -2224,7 +2315,7 @@ async function casesFrom(file) {
2224
2315
  }
2225
2316
  async function createSheet(args) {
2226
2317
  const cases = option(args, "--cases-file") ? await casesFrom(option(args, "--cases-file")) : [];
2227
- const output = option(args, "--output") ?? join6(outputRoot("agent-tester"), `cases_${Date.now()}.xlsx`);
2318
+ const output = option(args, "--output") ?? join7(outputRoot("agent-tester"), `cases_${Date.now()}.xlsx`);
2228
2319
  await ensureParent(output);
2229
2320
  const workbook = new ExcelJS2.Workbook();
2230
2321
  const sheet = workbook.addWorksheet("cases");
@@ -2250,7 +2341,7 @@ async function runAgentTester(session, action2, args) {
2250
2341
  }
2251
2342
 
2252
2343
  // src/workflows/delivery.ts
2253
- import { join as join7 } from "path";
2344
+ import { join as join8 } from "path";
2254
2345
  import ExcelJS3 from "exceljs";
2255
2346
  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"];
2256
2347
  async function loadRows(file) {
@@ -2284,7 +2375,7 @@ async function writeRows(file, rows) {
2284
2375
  const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))];
2285
2376
  sheet.addRow(keys);
2286
2377
  rows.forEach((row) => sheet.addRow(keys.map((key) => row[key] ?? "")));
2287
- await ensureDir(join7(file, ".."));
2378
+ await ensureDir(join8(file, ".."));
2288
2379
  await workbook.xlsx.writeFile(file);
2289
2380
  }
2290
2381
  function classify(text) {
@@ -2305,13 +2396,13 @@ async function classifyCases(args, domain) {
2305
2396
  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" }));
2306
2397
  const runId = option(args, "--run-id") ?? Date.now().toString();
2307
2398
  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) };
2308
- const out = option(args, "--results") ?? join7(outputRoot(domain), `results_${runId}.json`);
2399
+ const out = option(args, "--results") ?? join8(outputRoot(domain), `results_${runId}.json`);
2309
2400
  await writeJson3(out, result);
2310
- await writeJson3(join7(stateRoot(domain), "progress.json"), { status: "classified", run_id: runId, results_path: out, updated_at: (/* @__PURE__ */ new Date()).toISOString() });
2401
+ await writeJson3(join8(stateRoot(domain), "progress.json"), { status: "classified", run_id: runId, results_path: out, updated_at: (/* @__PURE__ */ new Date()).toISOString() });
2311
2402
  return { ...result.meta, resultsPath: out };
2312
2403
  }
2313
2404
  async function plan(domain, args) {
2314
- const file = join7(stateRoot(domain), "progress.json");
2405
+ const file = join8(stateRoot(domain), "progress.json");
2315
2406
  let current = {};
2316
2407
  try {
2317
2408
  current = await readJson3(file);
@@ -2335,7 +2426,7 @@ async function plan(domain, args) {
2335
2426
  async function runBatch(client, args, domain) {
2336
2427
  const input = option(args, "--input") ?? option(args, "--test-file", true);
2337
2428
  const rows = await loadRows(input);
2338
- const output = option(args, "--output") ?? join7(outputRoot(domain), `test_results_${Date.now()}.json`);
2429
+ const output = option(args, "--output") ?? join8(outputRoot(domain), `test_results_${Date.now()}.json`);
2339
2430
  const workers = Math.max(1, Math.min(intOption(args, "--workers", 4), 16));
2340
2431
  const results = [];
2341
2432
  let cursor = 0;
@@ -2359,7 +2450,7 @@ async function runBatch(client, args, domain) {
2359
2450
  return { output, total: results.length, succeeded: results.filter((row) => row.ok).length, failed: results.filter((row) => !row.ok).length };
2360
2451
  }
2361
2452
  async function createAgents(client, args, domain) {
2362
- const state2 = await readJson3(join7(stateRoot(domain), "progress.json")).catch(() => ({}));
2453
+ const state2 = await readJson3(join8(stateRoot(domain), "progress.json")).catch(() => ({}));
2363
2454
  const resultsPath = option(args, "--results") ?? (typeof state2.results_path === "string" ? state2.results_path : void 0);
2364
2455
  if (!resultsPath) throw new Error("classification results are required");
2365
2456
  const result = await readJson3(resultsPath);
@@ -2370,12 +2461,12 @@ async function createAgents(client, args, domain) {
2370
2461
  return { created, count: created.length };
2371
2462
  }
2372
2463
  async function renderReport(args, domain) {
2373
- const progress = await readJson3(join7(stateRoot(domain), "progress.json")).catch(() => ({}));
2464
+ const progress = await readJson3(join8(stateRoot(domain), "progress.json")).catch(() => ({}));
2374
2465
  const resultsFile = option(args, "--results") ?? option(args, "--input") ?? (typeof progress.results_path === "string" ? progress.results_path : void 0);
2375
2466
  if (!resultsFile) throw new Error("--results/--input is required, or run classify first");
2376
2467
  const value = await readJson3(resultsFile);
2377
2468
  const rows = asRows(value.results ?? value.items ?? value);
2378
- const output = option(args, "--html") ?? option(args, "--output") ?? join7(outputRoot(domain), `${domain}_report.html`);
2469
+ const output = option(args, "--html") ?? option(args, "--output") ?? join8(outputRoot(domain), `${domain}_report.html`);
2379
2470
  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("")}`;
2380
2471
  await writeHtml(output, option(args, "--title") ?? `${domain} report`, body);
2381
2472
  const md = option(args, "--md");
@@ -2386,12 +2477,12 @@ Total: ${rows.length}
2386
2477
  return { output, count: rows.length };
2387
2478
  }
2388
2479
  async function genExcel(args, domain) {
2389
- const progress = await readJson3(join7(stateRoot(domain), "progress.json")).catch(() => ({}));
2480
+ const progress = await readJson3(join8(stateRoot(domain), "progress.json")).catch(() => ({}));
2390
2481
  const input = option(args, "--results") ?? option(args, "--items") ?? (typeof progress.results_path === "string" ? progress.results_path : void 0);
2391
2482
  if (!input) throw new Error("--results or --items is required, or run classify first");
2392
2483
  const value = await readJson3(input);
2393
2484
  const rows = asRows(value);
2394
- const output = option(args, "--output") ?? join7(outputRoot(domain), `${domain}_results.xlsx`);
2485
+ const output = option(args, "--output") ?? join8(outputRoot(domain), `${domain}_results.xlsx`);
2395
2486
  await writeRows(output, rows);
2396
2487
  return { output, count: rows.length };
2397
2488
  }
@@ -2431,7 +2522,7 @@ async function runDomain(session, domain, action2, args) {
2431
2522
  }
2432
2523
 
2433
2524
  // src/workflows/kb-builder.ts
2434
- import { join as join8 } from "path";
2525
+ import { join as join9 } from "path";
2435
2526
  import ExcelJS4 from "exceljs";
2436
2527
  var clean = (value) => String(value ?? "").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
2437
2528
  var absolute = (href, base) => {
@@ -2465,7 +2556,7 @@ async function crawl(args) {
2465
2556
  }
2466
2557
  }
2467
2558
  const result = { url, mode: option(args, "--mode") ?? "auto", pages, fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), total: pages.length };
2468
- const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), "crawl_result.json");
2559
+ const output = option(args, "--output") ?? join9(outputRoot("kb-builder"), "crawl_result.json");
2469
2560
  await writeJson3(output, result);
2470
2561
  return { ...result, output };
2471
2562
  }
@@ -2480,13 +2571,13 @@ async function directoryPlan(args) {
2480
2571
  }
2481
2572
  const tree = [...groups.entries()].map(([name, pages]) => ({ name, children: pages.slice(0, 100).map((page2) => ({ name: String(page2.title ?? page2.url), sourceUrl: page2.url })) }));
2482
2573
  const result = { source: option(args, "--crawl-result"), generatedAt: (/* @__PURE__ */ new Date()).toISOString(), tree, confirmed: false };
2483
- const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), "directory_plan.json");
2574
+ const output = option(args, "--output") ?? join9(outputRoot("kb-builder"), "directory_plan.json");
2484
2575
  await writeJson3(output, result);
2485
- if (flag(args, "--write-confirmed-tree")) await writeJson3(join8(stateRoot("kb-builder"), "confirmed_tree.json"), { ...result, confirmed: true });
2576
+ if (flag(args, "--write-confirmed-tree")) await writeJson3(join9(stateRoot("kb-builder"), "confirmed_tree.json"), { ...result, confirmed: true });
2486
2577
  return { output, directories: tree.length, pages: rows.length, tree };
2487
2578
  }
2488
2579
  async function createDirectories(client, args) {
2489
- const file = option(args, "--tree-file") ?? join8(stateRoot("kb-builder"), "confirmed_tree.json");
2580
+ const file = option(args, "--tree-file") ?? join9(stateRoot("kb-builder"), "confirmed_tree.json");
2490
2581
  const value = await readJson3(file);
2491
2582
  const tree = Array.isArray(value.tree) ? value.tree : Array.isArray(value) ? value : [];
2492
2583
  if (!tree.length) throw new Error("confirmed directory tree is empty");
@@ -2509,12 +2600,12 @@ async function createDirectories(client, args) {
2509
2600
  async function extractWebQa(args) {
2510
2601
  const input = await readJson3(option(args, "--crawl-result", true));
2511
2602
  const pages = asRows(input.pages ?? input).slice(0, intOption(args, "--max-pages", 80));
2512
- const outputDir2 = option(args, "--output-dir") ?? join8(outputRoot("kb-builder"), "qa_parts");
2603
+ const outputDir2 = option(args, "--output-dir") ?? join9(outputRoot("kb-builder"), "qa_parts");
2513
2604
  await ensureDir(outputDir2);
2514
2605
  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 }));
2515
2606
  const files = [];
2516
2607
  for (const [index, part] of parts.entries()) {
2517
- const file = join8(outputDir2, `part_${String(index + 1).padStart(4, "0")}.json`);
2608
+ const file = join9(outputDir2, `part_${String(index + 1).padStart(4, "0")}.json`);
2518
2609
  await writeJson3(file, { items: [part] });
2519
2610
  files.push(file);
2520
2611
  }
@@ -2580,9 +2671,9 @@ async function importQa(client, args) {
2580
2671
  items.push(...Array.isArray(value) ? value : value.items && Array.isArray(value.items) ? value.items : [value]);
2581
2672
  }
2582
2673
  if (fromDir) {
2583
- const { readdir } = await import("fs/promises");
2584
- for (const file of (await readdir(fromDir)).filter((name) => name.endsWith(".json")).sort()) {
2585
- const value = await readJson3(join8(fromDir, file));
2674
+ const { readdir: readdir2 } = await import("fs/promises");
2675
+ for (const file of (await readdir2(fromDir)).filter((name) => name.endsWith(".json")).sort()) {
2676
+ const value = await readJson3(join9(fromDir, file));
2586
2677
  items.push(...Array.isArray(value) ? value : value.items && Array.isArray(value.items) ? value.items : [value]);
2587
2678
  }
2588
2679
  }
@@ -2590,7 +2681,7 @@ async function importQa(client, args) {
2590
2681
  if (!Number.isSafeInteger(maxDepth) || maxDepth <= 0) throw new Error("--max-depth must be a positive integer");
2591
2682
  const normalized = validateQaItems(items, maxDepth);
2592
2683
  const plan2 = { items, count: items.length, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
2593
- const planFile = join8(outputRoot("kb-builder"), `qa_import_plan_${Date.now()}.json`);
2684
+ const planFile = join9(outputRoot("kb-builder"), `qa_import_plan_${Date.now()}.json`);
2594
2685
  await writeJson3(planFile, plan2);
2595
2686
  if (flag(args, "--dry-run")) return { dryRun: true, planFile, count: items.length };
2596
2687
  const directories = flattenDirectories(asRows(await client.listDirectories()));
@@ -2665,7 +2756,7 @@ async function importTemplates(client, args) {
2665
2756
  });
2666
2757
  }
2667
2758
  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);
2668
- const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), `template_import_plan_${Date.now()}.json`);
2759
+ const output = option(args, "--output") ?? join9(outputRoot("kb-builder"), `template_import_plan_${Date.now()}.json`);
2669
2760
  await writeJson3(output, { items, count: items.length });
2670
2761
  if (flag(args, "--dry-run")) return { dryRun: true, output, count: items.length };
2671
2762
  const results = [];
@@ -2680,13 +2771,13 @@ async function exportKnowledge(client, args) {
2680
2771
  const value = await client.listKnowledge({ directoryId: id, keyword: option(args, "--keyword") ?? "", type: option(args, "--type") ?? "ALL", pageSize: intOption(args, "--page-size", 1e3) });
2681
2772
  rows.push(...asRows(value).map((row) => ({ ...row, directoryId: id, directoryName: directories.find((dir) => Number(dir.id ?? dir.directoryId) === id)?.name ?? "" })));
2682
2773
  }
2683
- const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), `knowledge_${Date.now()}.xlsx`);
2774
+ const output = option(args, "--output") ?? join9(outputRoot("kb-builder"), `knowledge_${Date.now()}.xlsx`);
2684
2775
  const workbook = new ExcelJS4.Workbook();
2685
2776
  const sheet = workbook.addWorksheet("knowledge");
2686
2777
  const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))];
2687
2778
  sheet.addRow(keys);
2688
2779
  for (const row of rows) sheet.addRow(keys.map((key) => row[key] ?? ""));
2689
- await ensureDir(join8(output, ".."));
2780
+ await ensureDir(join9(output, ".."));
2690
2781
  await workbook.xlsx.writeFile(output);
2691
2782
  return { output, count: rows.length };
2692
2783
  }
@@ -2727,7 +2818,7 @@ async function extractTicketQa(args) {
2727
2818
  const answer = String(record[replyCol] ?? "").trim();
2728
2819
  if (question && question !== `${brand}:`) items.push({ question, answer, sourceRow: number });
2729
2820
  });
2730
- const output = option(args, "--output") ?? join8(outputRoot("kb-builder"), `ticket_qa_${Date.now()}.json`);
2821
+ const output = option(args, "--output") ?? join9(outputRoot("kb-builder"), `ticket_qa_${Date.now()}.json`);
2731
2822
  const result = { brand, count: items.length, items };
2732
2823
  await writeJson3(output, result);
2733
2824
  if (flag(args, "--dry-run")) return { dryRun: true, output, count: items.length };
@@ -2735,12 +2826,12 @@ async function extractTicketQa(args) {
2735
2826
  }
2736
2827
 
2737
2828
  // src/workflows/desk.ts
2738
- import { join as join9 } from "path";
2829
+ import { join as join10 } from "path";
2739
2830
  import ExcelJS5 from "exceljs";
2740
2831
  var taskId = (args) => option(args, "--task-id") ?? (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
2741
- var taskDir = (args) => join9(stateRoot("solvea-desk"), taskId(args));
2832
+ var taskDir = (args) => join10(stateRoot("solvea-desk"), taskId(args));
2742
2833
  async function saveDatasource(dir, name, rows) {
2743
- const file = join9(dir, name);
2834
+ const file = join10(dir, name);
2744
2835
  await ensureParent(file);
2745
2836
  const workbook = new ExcelJS5.Workbook();
2746
2837
  const sheet = workbook.addWorksheet("data");
@@ -2763,7 +2854,7 @@ async function fetchTickets(client, args) {
2763
2854
  if (pageRows.length < pageSize2) break;
2764
2855
  }
2765
2856
  const output = await saveDatasource(dir, "datasource.xlsx", rows);
2766
- await writeJson3(join9(dir, "metadata.json"), { taskId: taskId(args), rows: rows.length, output });
2857
+ await writeJson3(join10(dir, "metadata.json"), { taskId: taskId(args), rows: rows.length, output });
2767
2858
  return { taskId: taskId(args), output, count: rows.length };
2768
2859
  }
2769
2860
  async function fetchSources(client, args) {
@@ -2774,7 +2865,7 @@ async function fetchSources(client, args) {
2774
2865
  const rows = asRows(value).slice(0, intOption(args, "--max-rows", 1e4));
2775
2866
  sources.tickets = rows;
2776
2867
  const output = await saveDatasource(dir, "datasource.xlsx", rows);
2777
- await writeJson3(join9(dir, "sources.json"), sources);
2868
+ await writeJson3(join10(dir, "sources.json"), sources);
2778
2869
  return { taskId: taskId(args), output, count: rows.length };
2779
2870
  }
2780
2871
  async function tags(client, action2, args) {
@@ -2801,10 +2892,10 @@ async function tags(client, action2, args) {
2801
2892
  throw new Error(`unknown tag action: ${action2}`);
2802
2893
  }
2803
2894
  async function renderReport2(args) {
2804
- const dir = taskDir(args), dataFile = option(args, "--report-data") ?? join9(dir, "report_data.json"), notesFile = option(args, "--notes") ?? join9(dir, "calculation_notes.json");
2895
+ const dir = taskDir(args), dataFile = option(args, "--report-data") ?? join10(dir, "report_data.json"), notesFile = option(args, "--notes") ?? join10(dir, "calculation_notes.json");
2805
2896
  const data3 = await readJson3(dataFile);
2806
2897
  if (flag(args, "--validate-only")) return { valid: true, dataFile };
2807
- const output = option(args, "--output") ?? join9(outputRoot("solvea-desk"), taskId(args), `${option(args, "--report-slug") ?? "report"}.html`);
2898
+ const output = option(args, "--output") ?? join10(outputRoot("solvea-desk"), taskId(args), `${option(args, "--report-slug") ?? "report"}.html`);
2808
2899
  const body = `<pre>${htmlEscape(JSON.stringify({ data: data3, notes: await readJson3(notesFile).catch(() => ({})) }, null, 2))}</pre>`;
2809
2900
  await writeHtml(output, option(args, "--report-slug") ?? "Solvea Desk report", body);
2810
2901
  return { output, valid: true };
@@ -2835,6 +2926,7 @@ async function runAuth(action2, args) {
2835
2926
  const session = await login(option(args, "--channel") ?? "stable");
2836
2927
  return print(publicAuthStatus(session));
2837
2928
  }
2929
+ if (action2 === "ensure") return print(publicAuthStatus(await authenticated()));
2838
2930
  if (action2 === "logout") {
2839
2931
  await logout();
2840
2932
  return print({ ok: true, loggedOut: true });
@@ -2850,7 +2942,7 @@ async function runAuth(action2, args) {
2850
2942
  if (action2 === "refresh") {
2851
2943
  return print(publicAuthStatus(await authenticated()));
2852
2944
  }
2853
- throw new Error("auth action must be login, logout, status, or refresh");
2945
+ throw new Error("auth action must be login, ensure, logout, status, or refresh");
2854
2946
  }
2855
2947
  async function runTenant(action2, args) {
2856
2948
  const session = await authenticated();
@@ -2923,7 +3015,7 @@ async function main(args = process.argv.slice(2)) {
2923
3015
  if (flag(args, "--scheduled")) await new Promise((resolve5) => setTimeout(resolve5, Math.random() * 18e5));
2924
3016
  return print(await update());
2925
3017
  }
2926
- if (command === "commands" || command === "capabilities") console.log(await readFile6(new URL("../capabilities.lock.json", import.meta.url), "utf8"));
3018
+ if (command === "commands" || command === "capabilities") console.log(await readFile7(new URL("../capabilities.lock.json", import.meta.url), "utf8"));
2927
3019
  else throw new Error(`unknown command: ${command}`);
2928
3020
  }
2929
3021
  main().catch((error) => {