nolo-cli 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/_executeModel-UD44KGZ4.js +203 -0
  2. package/action-ZJWE3Y5S.js +28 -0
  3. package/addContentAction-TVDBNJM5.js +26 -0
  4. package/addDialogAgentAction-EKH774VT.js +68 -0
  5. package/agentDeleteCommand-TCPEFJTF.js +327 -0
  6. package/agentEmailCommands-CZ3UILZ4.js +626 -0
  7. package/agentListCommands-SJKO2FFB.js +223 -0
  8. package/agentMachineCommands-PSNABISZ.js +425 -0
  9. package/agentPullCommand-O2MTUAFR.js +122 -0
  10. package/agentRecordCommands-MH2JKFHS.js +94 -0
  11. package/agentRunCommand-4KH6MIZR.js +110 -0
  12. package/agentRunControl-K4VKNEHF.js +57 -0
  13. package/agentRuntimeLocal-SKHJUWSS.js +104 -0
  14. package/agentSlice-3WXXN4VK.js +42 -0
  15. package/authCommand-PXFX6JDT.js +581 -0
  16. package/authCommands-356AYWEK.js +35 -0
  17. package/chunk-2EISRLRC.js +238 -0
  18. package/chunk-2FTTDCD4.js +675 -0
  19. package/chunk-2K2ECKSG.js +222 -0
  20. package/chunk-2PUGVR6E.js +19 -0
  21. package/chunk-3ASDTTEI.js +1774 -0
  22. package/chunk-3EUW3WEO.js +8 -0
  23. package/chunk-3LY4F7SV.js +9293 -0
  24. package/chunk-3TZDXYSF.js +71 -0
  25. package/chunk-3WYQEUBI.js +34 -0
  26. package/chunk-4CQLVGRV.js +50 -0
  27. package/chunk-4IJYA27C.js +342 -0
  28. package/chunk-4K2KKCSD.js +577 -0
  29. package/chunk-4SN6J65J.js +38 -0
  30. package/chunk-4VNS5WPM.js +42 -0
  31. package/chunk-4ZDM73SD.js +34 -0
  32. package/chunk-5CS33IBL.js +301 -0
  33. package/chunk-5KLGBAAR.js +386 -0
  34. package/chunk-5MIDPSSK.js +206 -0
  35. package/chunk-5MMAK75Q.js +15 -0
  36. package/chunk-5PJ3VWCS.js +360 -0
  37. package/chunk-63SLEOLK.js +175 -0
  38. package/chunk-63W5N6P3.js +150 -0
  39. package/chunk-6GANEXVO.js +231 -0
  40. package/chunk-6HYVJV7B.js +102 -0
  41. package/chunk-6WD3OOJS.js +20 -0
  42. package/chunk-7OOXPE3N.js +628 -0
  43. package/chunk-7QRKCBOP.js +1078 -0
  44. package/chunk-ATNTHIZI.js +13 -0
  45. package/chunk-AUQ747NZ.js +902 -0
  46. package/chunk-AVMKZHBC.js +85 -0
  47. package/chunk-AZPCASRE.js +36 -0
  48. package/chunk-BBYVOSU2.js +792 -0
  49. package/chunk-C3RUNRHT.js +51 -0
  50. package/chunk-CF2WZF4N.js +134 -0
  51. package/chunk-CF7PE763.js +10 -0
  52. package/chunk-CKOJQDKR.js +198 -0
  53. package/chunk-COMXDWNM.js +807 -0
  54. package/chunk-CPB2YM4D.js +171 -0
  55. package/chunk-CWMSB67P.js +34 -0
  56. package/chunk-DCOA4QSN.js +105 -0
  57. package/chunk-DFFIZ25G.js +30 -0
  58. package/chunk-DWLEOOGR.js +8 -0
  59. package/chunk-DZ3XJVET.js +231 -0
  60. package/chunk-EHLHSX7R.js +1433 -0
  61. package/chunk-ELRRLDWI.js +478 -0
  62. package/chunk-EXTIQUCI.js +482 -0
  63. package/chunk-EYEEHCU4.js +3066 -0
  64. package/chunk-G3SUCT4W.js +8 -0
  65. package/chunk-G43KEVSC.js +1447 -0
  66. package/chunk-GAKEBQD6.js +30 -0
  67. package/chunk-GBRG75BQ.js +86 -0
  68. package/chunk-GG4AJPSR.js +191 -0
  69. package/chunk-H33743XH.js +25 -0
  70. package/chunk-HNMXYYJR.js +12 -0
  71. package/chunk-I42VC5KR.js +15 -0
  72. package/chunk-IDZTNRP2.js +85 -0
  73. package/chunk-ISNCLGUZ.js +146 -0
  74. package/chunk-IWX4UW2E.js +548 -0
  75. package/chunk-JBTJAPUF.js +35 -0
  76. package/chunk-JEXVIMYZ.js +55 -0
  77. package/chunk-JRKKFRAV.js +537 -0
  78. package/chunk-JZPBVBIY.js +94 -0
  79. package/chunk-KKY4B2Q2.js +21 -0
  80. package/chunk-KOCA5FCS.js +4750 -0
  81. package/chunk-KQHGZWSV.js +63 -0
  82. package/chunk-KTVVPRJS.js +138 -0
  83. package/chunk-KUG52IAP.js +8 -0
  84. package/chunk-KX6YBIHG.js +8 -0
  85. package/chunk-LARL2V6G.js +9017 -0
  86. package/chunk-LCBAMY2M.js +60 -0
  87. package/chunk-LM6IYQ3F.js +16 -0
  88. package/chunk-M76ECQRD.js +13658 -0
  89. package/chunk-MGOJ5R6J.js +113 -0
  90. package/chunk-MKMWT7SA.js +27196 -0
  91. package/chunk-MRC4Q34M.js +188 -0
  92. package/chunk-OLZBQWZU.js +6 -0
  93. package/chunk-OPZYGHUC.js +89 -0
  94. package/chunk-OWOHNGCN.js +50 -0
  95. package/chunk-PAKN26FP.js +239 -0
  96. package/chunk-PCWC3JTN.js +91 -0
  97. package/chunk-PG5PNA7Y.js +939 -0
  98. package/chunk-Q2YZULBH.js +104 -0
  99. package/chunk-QSJ3HFEF.js +8 -0
  100. package/chunk-QYXLLLGL.js +4519 -0
  101. package/chunk-ROWQEJRP.js +139 -0
  102. package/chunk-S7Z2HMCN.js +25 -0
  103. package/chunk-SBR32ZZS.js +1901 -0
  104. package/chunk-SH2CFQOH.js +3391 -0
  105. package/chunk-SL35K7AH.js +226 -0
  106. package/chunk-TILFE6LV.js +381 -0
  107. package/chunk-TSS3XJY4.js +2052 -0
  108. package/chunk-U4TRT5V4.js +246 -0
  109. package/chunk-U6VC4MU4.js +8 -0
  110. package/chunk-UAD2WT54.js +64 -0
  111. package/chunk-UUJDGGAK.js +421 -0
  112. package/chunk-VF4K2QE6.js +21 -0
  113. package/chunk-VHBWHNVT.js +59 -0
  114. package/chunk-VIDPVFP3.js +20 -0
  115. package/chunk-VP4WJWKW.js +8882 -0
  116. package/chunk-W4NVOSFF.js +37 -0
  117. package/chunk-WKM6FH35.js +190 -0
  118. package/chunk-WWC7I6TA.js +8 -0
  119. package/chunk-X7W4TNB4.js +1550 -0
  120. package/chunk-XE635NI4.js +1948 -0
  121. package/chunk-XKIMRZO2.js +221 -0
  122. package/chunk-XS466O7S.js +933 -0
  123. package/chunk-Y5GCAMGP.js +9 -0
  124. package/chunk-YBWOP46I.js +3272 -0
  125. package/chunk-YPRZSPF3.js +148 -0
  126. package/chunk-YYUCRDOK.js +69 -0
  127. package/chunk-Z4NR32XR.js +50 -0
  128. package/chunk-ZIG5LJ7X.js +24765 -0
  129. package/chunk-ZNFJWEN4.js +56 -0
  130. package/chunk-ZQ2UM4KG.js +2878 -0
  131. package/cliExecutor-DNE7TCC4.js +36 -0
  132. package/clipboardy-BVE3LVEH.js +12001 -0
  133. package/createAgentAutomationAction-63OIP6X5.js +866 -0
  134. package/createDialogAction-ZLYZDYBB.js +54 -0
  135. package/createPageAction-AL5FSDCK.js +171 -0
  136. package/db-HCJ5PRB5.js +271 -0
  137. package/dbSlice-PR44QPXY.js +60 -0
  138. package/deleteFile-4SKGPHOZ.js +41 -0
  139. package/dialogCommands-F6YI5742.js +63 -0
  140. package/dialogSlice-JSBNZRLR.js +148 -0
  141. package/docCreateCommands-4CTO7ERT.js +219 -0
  142. package/docDeleteCommands-VBWERDRG.js +180 -0
  143. package/docReadCommands-KNP4MFYB.js +133 -0
  144. package/docStore-ADP5VYY4.js +98 -0
  145. package/docUpdateCommands-4BCBJQHA.js +317 -0
  146. package/fetchMessages-U6ZHN56A.js +17 -0
  147. package/getPricing-3E6UNPKL.js +30 -0
  148. package/handleSendMessageAction-IJDQ6ARA.js +185 -0
  149. package/index.js +188 -112943
  150. package/lib-FYYBQIXS.js +19839 -0
  151. package/localDialogRead-Z5UTAJPU.js +26 -0
  152. package/localFirstLog-DHNOY2NA.js +19 -0
  153. package/localRuntimeAuthority-TEGCOWX4.js +471 -0
  154. package/localRuntimeDb-LGDF6FQP.js +12 -0
  155. package/machineCommands-EA3M4XTN.js +779 -0
  156. package/machineInfo-M6KB6HZN.js +10 -0
  157. package/machineWsRunDispatch-NAPKE5ZA.js +110 -0
  158. package/memoryCommands-O4MIPIWQ.js +476 -0
  159. package/models-4OPOOLFS.js +9 -0
  160. package/offlineMarxistsAgentCommand-BCPJITJL.js +242 -0
  161. package/package.json +3 -25
  162. package/read-RIJCIVKX.js +17 -0
  163. package/readDocTool-GZD2N3LO.js +16 -0
  164. package/readlineWorkspace-KMWT4T4S.js +5509 -0
  165. package/removeDialogAgentAction-GJQJNDW6.js +73 -0
  166. package/runAgentClientLoop-U5Q6NDTJ.js +291 -0
  167. package/runtimeDoctorCommands-LRHLPDRC.js +289 -0
  168. package/setDialogExtraReferencesAction-M7OQG54N.js +60 -0
  169. package/setPrimaryDialogAgentAction-46UNFJDL.js +80 -0
  170. package/settingSlice-FVUUH4JA.js +199 -0
  171. package/skillSummaryMarker-42HKSXYY.js +9 -0
  172. package/slateToRenderMarkdown-Y5IYN73U.js +7 -0
  173. package/slateUtils-2M7MUTWX.js +19 -0
  174. package/spaceCommands-E2CFRPZJ.js +454 -0
  175. package/spaceSlice-L3R6IMXE.js +147 -0
  176. package/streamAgentChatTurn-UBDAOQWE.js +4528 -0
  177. package/tableCommands-GOFP7QKA.js +1286 -0
  178. package/toolApiClient-QI3YBNWB.js +26 -0
  179. package/tools-TMJXZTCA.js +85 -0
  180. package/tuiSummaryLlmCaller-RV3THLZF.js +78 -0
  181. package/understanding-GQKNDDWM.js +271 -0
  182. package/updateCommands-VOSPKXFF.js +31 -0
  183. package/updateDialogTitleAction-ZTWRDXI2.js +188 -0
@@ -0,0 +1,1286 @@
1
+ import {
2
+ getSpaceContentKeys,
3
+ normalizeSpaceIdInput
4
+ } from "./chunk-4CQLVGRV.js";
5
+ import {
6
+ getCurrentProfile,
7
+ getDefaultProfileConfigPath,
8
+ loadProfileConfig
9
+ } from "./chunk-ROWQEJRP.js";
10
+ import {
11
+ DEFAULT_NOLO_SERVER_URL
12
+ } from "./chunk-OLZBQWZU.js";
13
+ import {
14
+ toErrorMessage
15
+ } from "./chunk-HNMXYYJR.js";
16
+ import {
17
+ asOptionalFiniteNumber
18
+ } from "./chunk-WWC7I6TA.js";
19
+ import {
20
+ asTrimmedLowercaseString
21
+ } from "./chunk-QSJ3HFEF.js";
22
+ import {
23
+ getAllServers
24
+ } from "./chunk-QYXLLLGL.js";
25
+ import {
26
+ NOLO_CLUSTER_SERVERS
27
+ } from "./chunk-LCBAMY2M.js";
28
+ import "./chunk-CF7PE763.js";
29
+ import {
30
+ asOptionalTrimmedString
31
+ } from "./chunk-G3SUCT4W.js";
32
+ import {
33
+ isRecord
34
+ } from "./chunk-KUG52IAP.js";
35
+ import "./chunk-4VNS5WPM.js";
36
+
37
+ // packages/core/table/activityColumns.ts
38
+ var TABLE_ACTIVITY_COLUMNS = ["meta.latestActivityRef", "meta.activityRefs"];
39
+ function includeTableActivityColumns(columns) {
40
+ if (!Array.isArray(columns)) return [...TABLE_ACTIVITY_COLUMNS];
41
+ return Array.from(/* @__PURE__ */ new Set([...columns, ...TABLE_ACTIVITY_COLUMNS]));
42
+ }
43
+
44
+ // packages/cli/tableCommands.ts
45
+ var MULTI_SERVER_FETCH_LIMIT = 1e4;
46
+ var DELETE_ROWS_QUERY_PAGE_SIZE = 200;
47
+ var DEFAULT_TABLE_LIST_LIMIT = 50;
48
+ var DEFAULT_TABLE_QUERY_LIMIT = 20;
49
+ var UNBOUNDED_CLIENT_CAP = 1e5;
50
+ function readOption(args, flag) {
51
+ for (let i = 0; i < args.length; i++) {
52
+ const value = args[i];
53
+ if (value === flag) return args[i + 1] ?? "";
54
+ if (value.startsWith(`${flag}=`)) return value.slice(flag.length + 1);
55
+ }
56
+ return "";
57
+ }
58
+ function hasFlag(args, flag) {
59
+ return args.includes(flag);
60
+ }
61
+ function parseJsonOption(args, flag) {
62
+ const raw = readOption(args, flag);
63
+ if (!raw) return void 0;
64
+ try {
65
+ return JSON.parse(raw);
66
+ } catch (error) {
67
+ throw new Error(`${flag} must be valid JSON: ${toErrorMessage(error)}`);
68
+ }
69
+ }
70
+ function parseJsonOptionAlias(args, primaryFlag, aliasFlag) {
71
+ const primary = readOption(args, primaryFlag);
72
+ const alias = readOption(args, aliasFlag);
73
+ const raw = primary || alias;
74
+ if (!raw) return void 0;
75
+ try {
76
+ return JSON.parse(raw);
77
+ } catch (error) {
78
+ const usedFlag = primary ? primaryFlag : aliasFlag;
79
+ throw new Error(`${usedFlag} must be valid JSON: ${toErrorMessage(error)}`);
80
+ }
81
+ }
82
+ function parseOutputMode(raw) {
83
+ if (!raw) return "full";
84
+ if (raw === "json") return "raw";
85
+ if (raw === "full" || raw === "raw" || raw === "items" || raw === "jsonl") return raw;
86
+ throw new Error(`--output must be one of full, raw, items, json, jsonl; got ${raw}`);
87
+ }
88
+ function resolveResultLimit(args, fallback) {
89
+ if (hasFlag(args, "--all")) {
90
+ return { limit: null, unlimited: true, fromDefault: false };
91
+ }
92
+ const raw = readOption(args, "--limit");
93
+ if (!raw) {
94
+ return { limit: fallback, unlimited: false, fromDefault: true };
95
+ }
96
+ const parsed = Number(raw);
97
+ if (!Number.isFinite(parsed) || parsed < 0) {
98
+ return { limit: fallback, unlimited: false, fromDefault: true };
99
+ }
100
+ if (parsed === 0) {
101
+ return { limit: null, unlimited: true, fromDefault: false };
102
+ }
103
+ return { limit: Math.floor(parsed), unlimited: false, fromDefault: false };
104
+ }
105
+ function extractOutputItems(rawData) {
106
+ if (Array.isArray(rawData?.items)) return rawData.items;
107
+ if (Array.isArray(rawData)) return rawData;
108
+ return [];
109
+ }
110
+ function writeJsonArrayStream(output, items) {
111
+ output.write("[\n");
112
+ for (let i = 0; i < items.length; i++) {
113
+ const pretty = JSON.stringify(items[i], null, 2);
114
+ const indented = pretty.split("\n").map((line) => ` ${line}`).join("\n");
115
+ output.write(i === 0 ? indented : `,
116
+ ${indented}`);
117
+ }
118
+ output.write("\n]");
119
+ }
120
+ function writeJsonlStream(output, items) {
121
+ for (const item of items) {
122
+ output.write(`${JSON.stringify(item)}
123
+ `);
124
+ }
125
+ }
126
+ function writePrettyJsonStream(output, value) {
127
+ if (Array.isArray(value)) {
128
+ writeJsonArrayStream(output, value);
129
+ output.write("\n");
130
+ return;
131
+ }
132
+ if (value && typeof value === "object" && Array.isArray(value.items)) {
133
+ const record = value;
134
+ const items = record.items;
135
+ const keys = Object.keys(record);
136
+ output.write("{\n");
137
+ let wroteField = false;
138
+ for (const key of keys) {
139
+ if (key === "items") continue;
140
+ const prefix = wroteField ? ",\n" : "";
141
+ const pretty = JSON.stringify(record[key], null, 2).split("\n").map((line, idx) => idx === 0 ? line : ` ${line}`).join("\n");
142
+ output.write(`${prefix} ${JSON.stringify(key)}: ${pretty}`);
143
+ wroteField = true;
144
+ }
145
+ output.write(wroteField ? ',\n "items": ' : ' "items": ');
146
+ output.write("[\n");
147
+ for (let i = 0; i < items.length; i++) {
148
+ const pretty = JSON.stringify(items[i], null, 2);
149
+ const indented = pretty.split("\n").map((line) => ` ${line}`).join("\n");
150
+ output.write(i === 0 ? indented : `,
151
+ ${indented}`);
152
+ }
153
+ output.write("\n ]\n}\n");
154
+ return;
155
+ }
156
+ output.write(`${JSON.stringify(value, null, 2)}
157
+ `);
158
+ }
159
+ function writeTableOutput(output, input) {
160
+ if (input.mode === "jsonl") {
161
+ writeJsonlStream(output, extractOutputItems(input.rawData));
162
+ return;
163
+ }
164
+ if (input.mode === "items") {
165
+ writeJsonArrayStream(output, extractOutputItems(input.rawData));
166
+ output.write("\n");
167
+ return;
168
+ }
169
+ if (input.mode === "raw") {
170
+ writePrettyJsonStream(output, input.rawData);
171
+ return;
172
+ }
173
+ writePrettyJsonStream(output, input.envelope);
174
+ }
175
+ function resolveQueryColumns(columns, includeActivity) {
176
+ if (!includeActivity) return columns;
177
+ return Array.isArray(columns) ? includeTableActivityColumns(columns) : void 0;
178
+ }
179
+ function parseTableArg(raw) {
180
+ const trimmed = raw.trim();
181
+ if (!trimmed) return {};
182
+ if (!trimmed.startsWith("meta-")) return { tableId: trimmed };
183
+ const parts = trimmed.split("-");
184
+ return {
185
+ tenantId: parts[1],
186
+ tableId: parts.slice(2).join("-")
187
+ };
188
+ }
189
+ function resolveServerUrl(args, env) {
190
+ return (readOption(args, "--server-url") || readOption(args, "--server") || readOption(args, "--base-url") || env.NOLO_SERVER_URL || env.NOLO_SERVER || env.BASE_URL || DEFAULT_NOLO_SERVER_URL).replace(/\/+$/, "");
191
+ }
192
+ function resolveQueryServerUrls(args, env) {
193
+ const baseServer = resolveServerUrl(args, env);
194
+ if (!hasFlag(args, "--multi-server")) return [baseServer];
195
+ let syncServers;
196
+ try {
197
+ const configPath = readOption(args, "--profile-config") || getDefaultProfileConfigPath();
198
+ const config = loadProfileConfig(configPath);
199
+ const profile = getCurrentProfile(config);
200
+ if (profile && "syncServers" in profile && Array.isArray(profile.syncServers)) {
201
+ syncServers = profile.syncServers.filter((s) => typeof s === "string");
202
+ }
203
+ } catch {
204
+ }
205
+ if (!syncServers || syncServers.length === 0) {
206
+ syncServers = NOLO_CLUSTER_SERVERS;
207
+ }
208
+ return getAllServers(baseServer, syncServers);
209
+ }
210
+ async function fetchTableRowsFromServer(serverUrl, requestBody, authToken, fetchImpl) {
211
+ try {
212
+ const response = await fetchImpl(`${serverUrl}/api/table/query-rows`, {
213
+ method: "POST",
214
+ headers: {
215
+ Authorization: `Bearer ${authToken}`,
216
+ "Content-Type": "application/json"
217
+ },
218
+ body: JSON.stringify(requestBody)
219
+ });
220
+ const text = await response.text();
221
+ let payload;
222
+ try {
223
+ payload = text ? JSON.parse(text) : {};
224
+ } catch {
225
+ return { serverUrl, ok: response.ok, error: text || response.statusText };
226
+ }
227
+ if (!response.ok || payload?.error) {
228
+ return { serverUrl, ok: false, error: payload?.error ?? response.statusText };
229
+ }
230
+ return { serverUrl, ok: true, payload };
231
+ } catch (error) {
232
+ return {
233
+ serverUrl,
234
+ ok: false,
235
+ error: toErrorMessage(error)
236
+ };
237
+ }
238
+ }
239
+ async function readTableCommandError(response) {
240
+ const text = await response.text();
241
+ if (!text) return response.statusText;
242
+ try {
243
+ const payload = JSON.parse(text);
244
+ if (payload && typeof payload.error === "string") return payload.error;
245
+ } catch {
246
+ }
247
+ return text;
248
+ }
249
+ function resolveAuthToken(args, env) {
250
+ return readOption(args, "--machine-key") || readOption(args, "--token") || env.AUTH_TOKEN || env.AUTH || "";
251
+ }
252
+ function parseJwtTenantId(token) {
253
+ for (const parsed of parseJwtPayloadCandidates(token)) {
254
+ const userId = parsed.userId;
255
+ const sub = parsed.sub;
256
+ if (typeof userId === "string" && userId) return userId;
257
+ if (typeof sub === "string" && sub) return sub;
258
+ }
259
+ return void 0;
260
+ }
261
+ function parseJwtUsername(token) {
262
+ for (const parsed of parseJwtPayloadCandidates(token)) {
263
+ const username = asOptionalTrimmedString(parsed.username);
264
+ if (username) return username;
265
+ }
266
+ return void 0;
267
+ }
268
+ function parseJwtPayloadCandidates(token) {
269
+ const trimmed = token.trim();
270
+ if (!trimmed) return [];
271
+ const parts = trimmed.split(".").filter(Boolean);
272
+ const payloadCandidates = parts.length >= 3 ? [parts[1], parts[0]] : [parts[0]];
273
+ const parsed = [];
274
+ for (const payload of payloadCandidates) {
275
+ try {
276
+ const decoded = Buffer.from(payload, "base64").toString("utf8");
277
+ const value = JSON.parse(decoded);
278
+ if (isRecord(value)) {
279
+ parsed.push(value);
280
+ }
281
+ } catch {
282
+ }
283
+ }
284
+ return parsed;
285
+ }
286
+ function resolveUserId(args, env, authToken) {
287
+ const explicit = readOption(args, "--tenant-id") || env.USER_ID || parseJwtTenantId(authToken);
288
+ if (explicit) return explicit;
289
+ const username = parseJwtUsername(authToken);
290
+ return username && /^[a-z0-9_-]+$/i.test(username) ? username : void 0;
291
+ }
292
+ function mergeQueryShortcutFilters(args, filters) {
293
+ const row = readOption(args, "--row") || readOption(args, "--row-id");
294
+ const rowDbKey = readOption(args, "--row-dbkey");
295
+ const shortcutFilters = {};
296
+ if (row) {
297
+ if (row.startsWith("row-")) shortcutFilters.dbKey = row;
298
+ else shortcutFilters.rowId = row;
299
+ }
300
+ if (rowDbKey) shortcutFilters.dbKey = rowDbKey;
301
+ if (Object.keys(shortcutFilters).length === 0) return filters;
302
+ return {
303
+ ...filters ?? {},
304
+ ...shortcutFilters
305
+ };
306
+ }
307
+ function usage() {
308
+ return [
309
+ "Usage:",
310
+ " nolo table query --table <tableId|metaKey> [--tenant-id <userId>] [--filters <json>|--filter <json>] [--row <rowId|rowDbKey>] [--columns <json-array>] [--include-activity] [--no-base-fields] [--limit <n>] [--offset <n>] [--all] [--output full|raw|items|json|jsonl]",
311
+ "",
312
+ "Options:",
313
+ ` --limit <n> Max rows to return. Default: ${DEFAULT_TABLE_QUERY_LIMIT} (1 with --row). Use 0 with care.`,
314
+ " --offset <n> Skip first n rows (paging). Default: 0.",
315
+ " --all Full dump (same as --limit 0). Prefer --output jsonl for large results.",
316
+ " --output full|raw|items|json|jsonl. jsonl streams one row per line (lowest memory).",
317
+ ""
318
+ ].join("\n");
319
+ }
320
+ function deleteRowsUsage() {
321
+ return [
322
+ "Usage:",
323
+ " nolo table delete-rows --table <tableId|metaKey> (--row-ids <json-array> | --row-dbkeys <json-array> | --filters <json-object>)",
324
+ ""
325
+ ].join("\n");
326
+ }
327
+ function listUsage() {
328
+ return [
329
+ "Usage:",
330
+ " nolo table list [--limit <n>] [--all] [--title-query <text>] [--purpose <purpose>] [--output full|raw|items|json|jsonl]",
331
+ "",
332
+ "Options:",
333
+ ` --limit <n> Max tables to return. Default: ${DEFAULT_TABLE_LIST_LIMIT}. Use --limit 0 or --all for full dump.`,
334
+ " --all Full dump (same as --limit 0).",
335
+ " --output full|raw|items|json|jsonl. jsonl streams one table per line.",
336
+ ""
337
+ ].join("\n");
338
+ }
339
+ function tableHasPurpose(table, purpose) {
340
+ return typeof table?.purpose === "string" && table.purpose.toLowerCase() === purpose.toLowerCase();
341
+ }
342
+ function getComparableUpdatedAt(record) {
343
+ const raw = record?.updatedAt ?? record?.updated_at ?? record?.createdAt ?? record?.created;
344
+ const asNumber = asOptionalFiniteNumber(raw);
345
+ if (asNumber !== void 0) return asNumber;
346
+ if (typeof raw === "string") return Date.parse(raw) || 0;
347
+ return 0;
348
+ }
349
+ function shouldReplaceTableRecord(next, current) {
350
+ const nextTs = getComparableUpdatedAt(next);
351
+ const currentTs = getComparableUpdatedAt(current);
352
+ if (nextTs !== currentTs) return nextTs > currentTs;
353
+ return Boolean(next?.deletedAt) && !Boolean(current?.deletedAt);
354
+ }
355
+ function getTableQueryRawData(payload) {
356
+ return payload?.rawData ?? payload;
357
+ }
358
+ function getTableQueryItems(payload) {
359
+ const rawData = getTableQueryRawData(payload);
360
+ if (rawData?.tableMeta?.deletedAt) return [];
361
+ const liveItems = Array.isArray(rawData?.items) ? rawData.items : [];
362
+ const deletedItems = Array.isArray(rawData?.deletedItems) ? rawData.deletedItems : [];
363
+ return [...liveItems, ...deletedItems].filter((item) => item && typeof item === "object");
364
+ }
365
+ function isLatestTableMetaDeleted(payloads) {
366
+ let latestMeta = null;
367
+ for (const payload of payloads) {
368
+ const tableMeta = getTableQueryRawData(payload)?.tableMeta;
369
+ if (!tableMeta || typeof tableMeta !== "object") continue;
370
+ if (!latestMeta || shouldReplaceTableRecord(tableMeta, latestMeta)) {
371
+ latestMeta = tableMeta;
372
+ }
373
+ }
374
+ return Boolean(latestMeta?.deletedAt);
375
+ }
376
+ async function runTableListCommand(args, deps = {}) {
377
+ const env = deps.env ?? process.env;
378
+ const output = deps.output ?? process.stdout;
379
+ const fetchImpl = deps.fetchImpl ?? fetch;
380
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
381
+ output.write(listUsage());
382
+ return 0;
383
+ }
384
+ let outputMode;
385
+ try {
386
+ outputMode = parseOutputMode(readOption(args, "--output") || "items");
387
+ } catch (error) {
388
+ output.write(`[nolo] table list failed: ${toErrorMessage(error)}
389
+ `);
390
+ return 1;
391
+ }
392
+ const authToken = resolveAuthToken(args, env);
393
+ const userId = resolveUserId(args, env, authToken);
394
+ if (!userId) {
395
+ output.write("[nolo] table list failed: user id is required. Pass --tenant-id or use a login token.\n");
396
+ return 1;
397
+ }
398
+ if (!authToken) {
399
+ output.write("[nolo] table list failed: AUTH_TOKEN is required.\n");
400
+ return 1;
401
+ }
402
+ const resultLimit = resolveResultLimit(args, DEFAULT_TABLE_LIST_LIMIT);
403
+ const limit = resultLimit.unlimited ? UNBOUNDED_CLIENT_CAP : Math.max(1, resultLimit.limit ?? DEFAULT_TABLE_LIST_LIMIT);
404
+ const titleQuery = asTrimmedLowercaseString(readOption(args, "--title-query"));
405
+ const purpose = readOption(args, "--purpose").trim();
406
+ const spaceId = normalizeSpaceIdInput(readOption(args, "--space") || readOption(args, "--space-id"));
407
+ const scanLimit = titleQuery || purpose ? Math.max(limit * 5, 200) : limit;
408
+ const serverUrl = resolveServerUrl(args, env);
409
+ let records;
410
+ try {
411
+ records = spaceId ? await fetchSpaceTableRecords({
412
+ authToken,
413
+ fetchImpl,
414
+ serverUrl,
415
+ spaceId
416
+ }) : await fetchUserTableRecords({
417
+ authToken,
418
+ fetchImpl,
419
+ limit: Math.min(scanLimit, resultLimit.unlimited ? UNBOUNDED_CLIENT_CAP : 500),
420
+ serverUrl,
421
+ userId
422
+ });
423
+ } catch (error) {
424
+ output.write(`[nolo] table list failed: ${toErrorMessage(error)}
425
+ `);
426
+ return 1;
427
+ }
428
+ const filtered = records.filter((table) => table && typeof table === "object" && !table.deletedAt).filter((table) => !titleQuery || String(table.displayName ?? table.name ?? table.title ?? "").toLowerCase().includes(titleQuery)).filter((table) => !purpose || tableHasPurpose(table, purpose)).sort((left, right) => getComparableUpdatedAt(right) - getComparableUpdatedAt(left));
429
+ const hitLimit = !resultLimit.unlimited && filtered.length > limit;
430
+ const tables = filtered.slice(0, limit).map((table) => ({
431
+ dbKey: table.dbKey ?? null,
432
+ tableId: table.tableId ?? null,
433
+ displayName: table.displayName ?? table.name ?? table.title ?? "(untitled)",
434
+ spaceId: table.spaceId ?? null,
435
+ tenantId: table.tenantId ?? null,
436
+ columnCount: Array.isArray(table.columns) ? table.columns.length : 0,
437
+ createdAt: table.createdAt ?? null,
438
+ updatedAt: table.updatedAt ?? table.updated_at ?? null
439
+ }));
440
+ const envelope = {
441
+ success: true,
442
+ userId,
443
+ ...spaceId ? { space: spaceId } : {},
444
+ total: tables.length,
445
+ ...hitLimit ? { limit, truncated: true, nextOffset: limit } : {},
446
+ tables
447
+ };
448
+ writeTableOutput(output, {
449
+ mode: outputMode,
450
+ envelope,
451
+ // list raw/items/jsonl historically print the tables array (not { items })
452
+ rawData: tables
453
+ });
454
+ return 0;
455
+ }
456
+ async function readJsonResponse(response) {
457
+ const text = await response.text();
458
+ let payload;
459
+ try {
460
+ payload = text ? JSON.parse(text) : {};
461
+ } catch {
462
+ if (!response.ok) throw new Error(text || response.statusText);
463
+ return text;
464
+ }
465
+ if (!response.ok || payload?.error) {
466
+ throw new Error(payload?.error ?? response.statusText);
467
+ }
468
+ return payload;
469
+ }
470
+ async function fetchUserTableRecords(args) {
471
+ const response = await args.fetchImpl(
472
+ `${args.serverUrl}/api/v1/db/query/${encodeURIComponent(args.userId)}?limit=${args.limit}`,
473
+ {
474
+ method: "POST",
475
+ headers: {
476
+ Authorization: `Bearer ${args.authToken}`,
477
+ "Content-Type": "application/json"
478
+ },
479
+ body: JSON.stringify({ type: "table" })
480
+ }
481
+ );
482
+ const payload = await readJsonResponse(response);
483
+ return Array.isArray(payload?.data?.data) ? payload.data.data : Array.isArray(payload?.data) ? payload.data : [];
484
+ }
485
+ async function fetchSpaceTableRecords(args) {
486
+ const readRecord = async (dbKey) => {
487
+ const response = await args.fetchImpl(
488
+ `${args.serverUrl}/api/v1/db/read/${encodeURIComponent(dbKey)}`,
489
+ { headers: { Authorization: `Bearer ${args.authToken}` } }
490
+ );
491
+ const payload = await readJsonResponse(response);
492
+ return payload?.data ?? payload;
493
+ };
494
+ const space = await readRecord(`space-${args.spaceId}`);
495
+ const tableKeys = [...getSpaceContentKeys(space)].filter((key) => key.startsWith("meta-"));
496
+ const records = await Promise.all(
497
+ tableKeys.map(async (key) => {
498
+ try {
499
+ return await readRecord(key);
500
+ } catch {
501
+ return null;
502
+ }
503
+ })
504
+ );
505
+ return records.filter(Boolean);
506
+ }
507
+ async function runTableQueryCommand(args, deps = {}) {
508
+ const env = deps.env ?? process.env;
509
+ const output = deps.output ?? process.stdout;
510
+ const fetchImpl = deps.fetchImpl ?? fetch;
511
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
512
+ output.write(usage());
513
+ return 0;
514
+ }
515
+ let outputMode;
516
+ let columns;
517
+ let filters;
518
+ try {
519
+ outputMode = parseOutputMode(readOption(args, "--output"));
520
+ columns = parseJsonOption(args, "--columns");
521
+ filters = mergeQueryShortcutFilters(
522
+ args,
523
+ parseJsonOptionAlias(args, "--filters", "--filter")
524
+ );
525
+ } catch (error) {
526
+ output.write(`[nolo] table query failed: ${toErrorMessage(error)}
527
+ `);
528
+ return 1;
529
+ }
530
+ const tableArg = parseTableArg(readOption(args, "--table"));
531
+ const authToken = resolveAuthToken(args, env);
532
+ const tenantId = readOption(args, "--tenant-id") || tableArg.tenantId || resolveUserId(args, env, authToken);
533
+ const tableId = tableArg.tableId;
534
+ const hasSingleRowShortcut = Boolean(readOption(args, "--row") || readOption(args, "--row-id") || readOption(args, "--row-dbkey"));
535
+ if (!tenantId || !tableId) {
536
+ output.write(usage());
537
+ return 1;
538
+ }
539
+ if (!authToken) {
540
+ output.write("[nolo] table query failed: AUTH_TOKEN is required.\n");
541
+ return 1;
542
+ }
543
+ const serverUrls = resolveQueryServerUrls(args, env);
544
+ const isMultiServer = serverUrls.length > 1;
545
+ const defaultQueryLimit = hasSingleRowShortcut ? 1 : DEFAULT_TABLE_QUERY_LIMIT;
546
+ const resultLimit = resolveResultLimit(args, defaultQueryLimit);
547
+ const requestedLimit = resultLimit.unlimited ? UNBOUNDED_CLIENT_CAP : Math.max(1, resultLimit.limit ?? defaultQueryLimit);
548
+ const requestedOffset = Math.max(0, Math.floor(Number(readOption(args, "--offset") || 0) || 0));
549
+ const sortBy = readOption(args, "--sort-by") || "updatedAt";
550
+ const sortOrder = readOption(args, "--sort-order") === "asc" ? "asc" : "desc";
551
+ const requestBody = {
552
+ tenantId,
553
+ tableId,
554
+ filters: filters ?? {},
555
+ columns: resolveQueryColumns(columns, hasFlag(args, "--include-activity")),
556
+ includeBaseFields: !hasFlag(args, "--no-base-fields"),
557
+ sortBy,
558
+ sortOrder,
559
+ includeDeleted: true,
560
+ envelope: "table-sync-v1"
561
+ };
562
+ if (isMultiServer) {
563
+ requestBody.limit = MULTI_SERVER_FETCH_LIMIT;
564
+ } else {
565
+ requestBody.limit = requestedLimit;
566
+ requestBody.offset = requestedOffset;
567
+ }
568
+ const results = await Promise.all(
569
+ serverUrls.map(
570
+ (serverUrl) => fetchTableRowsFromServer(serverUrl, requestBody, authToken, fetchImpl)
571
+ )
572
+ );
573
+ const multiServerWarnings = [];
574
+ const failedServers = results.filter((r) => !r.ok);
575
+ if (failedServers.length === results.length) {
576
+ const errors = failedServers.map((r) => `${r.serverUrl}: ${r.error}`).join("; ");
577
+ output.write(`[nolo] table query failed: ${errors}
578
+ `);
579
+ return 1;
580
+ }
581
+ if (failedServers.length > 0) {
582
+ const errors = failedServers.map((r) => `${r.serverUrl}: ${r.error}`).join("; ");
583
+ multiServerWarnings.push(`partial server failures: ${errors}`);
584
+ }
585
+ let mergedItems = [];
586
+ let fetchedBeforeSlice = 0;
587
+ if (isMultiServer) {
588
+ const itemMap = /* @__PURE__ */ new Map();
589
+ const tableDeleted = isLatestTableMetaDeleted(
590
+ results.filter((result) => result.ok && result.payload).map((result) => result.payload)
591
+ );
592
+ for (const result of results) {
593
+ if (!result.ok || !result.payload) continue;
594
+ const items = tableDeleted ? [] : getTableQueryItems(result.payload);
595
+ for (const item of items) {
596
+ if (!item || typeof item.dbKey !== "string") continue;
597
+ const existing = itemMap.get(item.dbKey);
598
+ if (!existing || shouldReplaceTableRecord(item, existing)) {
599
+ itemMap.set(item.dbKey, { ...item, _serverOrigin: result.serverUrl });
600
+ }
601
+ }
602
+ }
603
+ mergedItems = Array.from(itemMap.values()).filter((item) => !item.deletedAt);
604
+ mergedItems.sort((a, b) => {
605
+ const diff = getComparableUpdatedAt(b) - getComparableUpdatedAt(a);
606
+ return sortOrder === "asc" ? -diff : diff;
607
+ });
608
+ fetchedBeforeSlice = mergedItems.length;
609
+ mergedItems = resultLimit.unlimited ? mergedItems.slice(requestedOffset, requestedOffset + UNBOUNDED_CLIENT_CAP) : mergedItems.slice(requestedOffset, requestedOffset + requestedLimit);
610
+ } else {
611
+ const result = results[0];
612
+ if (!result || !result.ok || !result.payload) {
613
+ output.write(`[nolo] table query failed: ${result?.error ?? "unknown error"}
614
+ `);
615
+ return 1;
616
+ }
617
+ const live = getTableQueryItems(result.payload).filter((item) => !item.deletedAt);
618
+ fetchedBeforeSlice = live.length;
619
+ mergedItems = resultLimit.unlimited ? live.slice(0, UNBOUNDED_CLIENT_CAP) : live.slice(0, requestedLimit);
620
+ }
621
+ const hitLimit = !resultLimit.unlimited && (fetchedBeforeSlice > mergedItems.length || mergedItems.length >= requestedLimit && requestedLimit > 0);
622
+ const truncationFields = hitLimit ? {
623
+ limit: requestedLimit,
624
+ offset: requestedOffset,
625
+ truncated: true,
626
+ nextOffset: requestedOffset + mergedItems.length
627
+ } : {};
628
+ const singleRawData = !isMultiServer ? (() => {
629
+ const raw = getTableQueryRawData(results[0].payload);
630
+ if (!raw || typeof raw !== "object") return raw;
631
+ const { deletedItems: _deletedItems, ...rest } = raw;
632
+ return rest;
633
+ })() : null;
634
+ const rawData = isMultiServer ? {
635
+ items: mergedItems,
636
+ ...truncationFields
637
+ } : singleRawData && typeof singleRawData === "object" ? {
638
+ ...singleRawData,
639
+ items: mergedItems,
640
+ ...truncationFields
641
+ } : { items: mergedItems, ...truncationFields };
642
+ const envelope = isMultiServer ? {
643
+ rawData: { items: mergedItems, ...truncationFields },
644
+ _multiServerOrigins: serverUrls,
645
+ _multiServerWarnings: multiServerWarnings,
646
+ ...truncationFields
647
+ } : results[0].payload && typeof results[0].payload === "object" ? {
648
+ ...results[0].payload,
649
+ rawData,
650
+ ...truncationFields
651
+ } : { rawData, ...truncationFields };
652
+ writeTableOutput(output, {
653
+ envelope,
654
+ rawData,
655
+ mode: outputMode
656
+ });
657
+ return 0;
658
+ }
659
+ async function runTableDeleteRowsCommand(args, deps = {}) {
660
+ const env = deps.env ?? process.env;
661
+ const output = deps.output ?? process.stdout;
662
+ const fetchImpl = deps.fetchImpl ?? fetch;
663
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
664
+ output.write(deleteRowsUsage());
665
+ return 0;
666
+ }
667
+ const tableArg = parseTableArg(readOption(args, "--table"));
668
+ const authToken = resolveAuthToken(args, env);
669
+ const tenantId = readOption(args, "--tenant-id") || tableArg.tenantId || resolveUserId(args, env, authToken);
670
+ const tableId = tableArg.tableId;
671
+ if (!tenantId || !tableId) {
672
+ output.write(deleteRowsUsage());
673
+ return 1;
674
+ }
675
+ if (!authToken) {
676
+ output.write("[nolo] table delete-rows failed: AUTH_TOKEN is required.\n");
677
+ return 1;
678
+ }
679
+ const deletionSources = [
680
+ hasFlag(args, "--row-ids") ? "--row-ids" : void 0,
681
+ hasFlag(args, "--row-dbkeys") ? "--row-dbkeys" : void 0,
682
+ hasFlag(args, "--filters") || hasFlag(args, "--filter") ? "--filters" : void 0
683
+ ].filter(Boolean);
684
+ if (deletionSources.length > 1) {
685
+ output.write(`[nolo] table delete-rows failed: only one deletion source allowed; got ${deletionSources.join(", ")}.
686
+ `);
687
+ return 1;
688
+ }
689
+ let deletionSpec;
690
+ try {
691
+ const rowIds = parseJsonOption(args, "--row-ids");
692
+ if (rowIds !== void 0) {
693
+ if (!Array.isArray(rowIds) || rowIds.length === 0 || !rowIds.every((id) => typeof id === "string")) {
694
+ throw new Error("--row-ids must be a non-empty JSON array of strings.");
695
+ }
696
+ deletionSpec = rowIds.map((rowId) => ({
697
+ dbKey: `row-${tenantId}-${tableId}-${rowId}`,
698
+ source: `--row-ids:${rowId}`
699
+ }));
700
+ }
701
+ } catch (error) {
702
+ output.write(`[nolo] table delete-rows failed: ${toErrorMessage(error)}
703
+ `);
704
+ return 1;
705
+ }
706
+ if (!deletionSpec) {
707
+ try {
708
+ const rowDbKeys = parseJsonOption(args, "--row-dbkeys");
709
+ if (rowDbKeys !== void 0) {
710
+ if (!Array.isArray(rowDbKeys) || rowDbKeys.length === 0 || !rowDbKeys.every((k) => typeof k === "string")) {
711
+ throw new Error("--row-dbkeys must be a non-empty JSON array of strings.");
712
+ }
713
+ deletionSpec = rowDbKeys.map((dbKey) => ({ dbKey, source: "--row-dbkeys" }));
714
+ }
715
+ } catch (error) {
716
+ output.write(`[nolo] table delete-rows failed: ${toErrorMessage(error)}
717
+ `);
718
+ return 1;
719
+ }
720
+ }
721
+ const serverUrl = resolveServerUrl(args, env);
722
+ if (!deletionSpec) {
723
+ const filtersRaw = parseJsonOptionAlias(args, "--filters", "--filter");
724
+ if (filtersRaw !== void 0) {
725
+ const queryLimit = DELETE_ROWS_QUERY_PAGE_SIZE;
726
+ const matchedRows = [];
727
+ let offset = 0;
728
+ let total = Number.POSITIVE_INFINITY;
729
+ while (offset < total) {
730
+ const response = await fetchImpl(`${serverUrl}/api/table/query-rows`, {
731
+ method: "POST",
732
+ headers: {
733
+ "Content-Type": "application/json",
734
+ Authorization: `Bearer ${authToken}`
735
+ },
736
+ body: JSON.stringify({
737
+ tenantId,
738
+ tableId,
739
+ filters: filtersRaw,
740
+ limit: queryLimit,
741
+ offset,
742
+ includeDeleted: true,
743
+ envelope: "table-sync-v1"
744
+ })
745
+ });
746
+ if (!response.ok) {
747
+ output.write(`[nolo] table delete-rows failed: query rows failed: ${response.statusText}
748
+ `);
749
+ return 1;
750
+ }
751
+ const payload = await response.json();
752
+ if (payload && typeof payload === "object" && "error" in payload) {
753
+ output.write(`[nolo] table delete-rows failed: query rows failed: ${payload.error ?? "unknown error"}
754
+ `);
755
+ return 1;
756
+ }
757
+ const rawData = getTableQueryRawData(payload);
758
+ const items = getTableQueryItems(payload).filter((item) => !item.deletedAt);
759
+ matchedRows.push(...items);
760
+ const nextTotal = Number(rawData?.total);
761
+ total = Number.isFinite(nextTotal) && nextTotal >= 0 ? nextTotal : offset + items.length;
762
+ if (items.length === 0) break;
763
+ offset += items.length;
764
+ }
765
+ deletionSpec = matchedRows.filter((item) => typeof item?.dbKey === "string").map((item) => ({ dbKey: item.dbKey, source: "--filters" }));
766
+ if (deletionSpec.length === 0) {
767
+ output.write(`${JSON.stringify({ ok: true, deleted: 0, results: [] })}
768
+ `);
769
+ return 0;
770
+ }
771
+ if (!hasFlag(args, "--yes")) {
772
+ output.write(`${JSON.stringify({ ok: true, dryRun: true, wouldDelete: deletionSpec.length, dbKeys: deletionSpec.map((s) => s.dbKey) })}
773
+ `);
774
+ output.write("Dry-run only. Re-run with --yes to delete these rows.\n");
775
+ return 0;
776
+ }
777
+ }
778
+ }
779
+ if (!deletionSpec || deletionSpec.length === 0) {
780
+ output.write(`[nolo] table delete-rows failed: nothing to delete; provide --row-ids, --row-dbkeys, or --filters.
781
+ `);
782
+ output.write(deleteRowsUsage());
783
+ return 1;
784
+ }
785
+ const results = [];
786
+ try {
787
+ const response = await fetchImpl(`${serverUrl}/api/table/delete-rows`, {
788
+ method: "POST",
789
+ headers: {
790
+ "Content-Type": "application/json",
791
+ Authorization: `Bearer ${authToken}`
792
+ },
793
+ body: JSON.stringify({
794
+ tenantId,
795
+ tableId,
796
+ dbKeys: deletionSpec.map((item) => item.dbKey)
797
+ })
798
+ });
799
+ if (response.ok) {
800
+ const payload = await response.json().catch(() => ({}));
801
+ const responseResults = Array.isArray(payload?.rawData?.results) ? payload.rawData.results : Array.isArray(payload?.results) ? payload.results : [];
802
+ if (responseResults.length > 0) {
803
+ const resultByKey = new Map(
804
+ responseResults.filter((item) => typeof item?.dbKey === "string").map((item) => [item.dbKey, item])
805
+ );
806
+ results.push(
807
+ ...deletionSpec.map(({ dbKey, source }) => {
808
+ const result = resultByKey.get(dbKey);
809
+ if (!result) return { dbKey, source, ok: false, error: "missing delete result" };
810
+ return {
811
+ dbKey,
812
+ source,
813
+ ok: result.ok === true,
814
+ ...result.ok === true ? {} : { error: String(result.error ?? "delete failed") }
815
+ };
816
+ })
817
+ );
818
+ } else {
819
+ results.push(...deletionSpec.map(({ dbKey, source }) => ({ dbKey, source, ok: true })));
820
+ }
821
+ } else {
822
+ const error = await readTableCommandError(response);
823
+ results.push(...deletionSpec.map(({ dbKey, source }) => ({ dbKey, source, ok: false, error })));
824
+ }
825
+ } catch (error) {
826
+ const message = toErrorMessage(error);
827
+ results.push(...deletionSpec.map(({ dbKey, source }) => ({ dbKey, source, ok: false, error: message })));
828
+ }
829
+ output.write(`${JSON.stringify({ ok: true, deleted: results.filter((r) => r.ok).length, results })}
830
+ `);
831
+ return results.every((r) => r.ok) ? 0 : 1;
832
+ }
833
+ function parseValuesObject(flag, raw) {
834
+ let parsed;
835
+ try {
836
+ parsed = JSON.parse(raw);
837
+ } catch (error) {
838
+ throw new Error(`${flag} must be valid JSON: ${toErrorMessage(error)}`);
839
+ }
840
+ if (!isRecord(parsed)) {
841
+ throw new Error(`${flag} must be a JSON object`);
842
+ }
843
+ return parsed;
844
+ }
845
+ function parseRowsArray(flag, raw) {
846
+ let parsed;
847
+ try {
848
+ parsed = JSON.parse(raw);
849
+ } catch (error) {
850
+ throw new Error(`${flag} must be valid JSON: ${toErrorMessage(error)}`);
851
+ }
852
+ if (!Array.isArray(parsed) || parsed.length === 0) {
853
+ throw new Error(`${flag} must be a non-empty JSON array`);
854
+ }
855
+ if (!parsed.every(isRecord)) {
856
+ throw new Error(`${flag} must be an array of JSON objects`);
857
+ }
858
+ return parsed;
859
+ }
860
+ function parseUpdatesArray(flag, raw) {
861
+ const updates = parseRowsArray(flag, raw);
862
+ for (const update of updates) {
863
+ if (typeof update.rowId !== "string" && typeof update.rowDbKey !== "string") {
864
+ throw new Error(`${flag}[*] must include rowId or rowDbKey`);
865
+ }
866
+ if (!isRecord(update.changes)) {
867
+ throw new Error(`${flag}[*].changes must be a JSON object`);
868
+ }
869
+ }
870
+ return updates;
871
+ }
872
+ function resolveRowTarget(raw) {
873
+ if (!raw) return {};
874
+ const trimmed = raw.trim();
875
+ if (trimmed.startsWith("row-")) return { rowDbKey: trimmed };
876
+ return { rowId: trimmed };
877
+ }
878
+ function resolveTableContext(args, env, output) {
879
+ const tableArg = parseTableArg(readOption(args, "--table"));
880
+ const authToken = resolveAuthToken(args, env);
881
+ const tenantId = readOption(args, "--tenant-id") || tableArg.tenantId || resolveUserId(args, env, authToken);
882
+ const tableId = tableArg.tableId;
883
+ if (!tenantId || !tableId) {
884
+ output.write("Usage:\n nolo table <action> --table <tableId|metaKey> [action-specific flags]\n\n");
885
+ return { ok: false };
886
+ }
887
+ if (!authToken) {
888
+ output.write("[nolo] table failed: AUTH_TOKEN is required.\n");
889
+ return { ok: false };
890
+ }
891
+ return { ok: true, authToken, tenantId, tableId, serverUrl: resolveServerUrl(args, env), tableArg };
892
+ }
893
+ async function postTableJson(deps, ctx, path, body) {
894
+ const fetchImpl = deps.fetchImpl ?? fetch;
895
+ const response = await fetchImpl(`${ctx.serverUrl}${path}`, {
896
+ method: "POST",
897
+ headers: {
898
+ "Content-Type": "application/json",
899
+ Authorization: `Bearer ${ctx.authToken}`
900
+ },
901
+ body: JSON.stringify(body)
902
+ });
903
+ if (!response.ok) {
904
+ return { ok: false, message: await readTableCommandError(response) };
905
+ }
906
+ let payload = null;
907
+ try {
908
+ payload = await response.json();
909
+ } catch {
910
+ payload = null;
911
+ }
912
+ return { ok: true, payload };
913
+ }
914
+ var TABLE_ADD_COLUMN_USAGE = "Usage:\n nolo table add-column --table <tableId|metaKey> --name <columnName> [--label <label>] [--type <text|number|boolean|date|datetime|select|multi_select>] [--options <json-array>]\n\n";
915
+ var TABLE_ADD_ROW_USAGE = "Usage:\n nolo table add-row --table <tableId|metaKey> --values <json-object>\n\n";
916
+ var TABLE_ADD_ROWS_USAGE = "Usage:\n nolo table add-rows --table <tableId|metaKey> --rows <non-empty-json-array>\n\n";
917
+ var TABLE_UPDATE_ROW_USAGE = "Usage:\n nolo table update-row --table <tableId|metaKey> --row <rowId|rowDbKey> --changes <json-object>\n\n";
918
+ var TABLE_UPDATE_ROWS_USAGE = "Usage:\n nolo table update-rows --table <tableId|metaKey> --updates <non-empty-json-array>\n\n";
919
+ var TABLE_DELETE_ROW_USAGE = "Usage:\n nolo table delete-row --table <tableId|metaKey> --row <rowId|rowDbKey>\n\n";
920
+ var TABLE_PURGE_ROWS_USAGE = "Usage:\n nolo table purge-rows --table <exact-meta-dbKey> --row-dbkeys <non-empty-json-array> [--yes] [--json]\n\n";
921
+ var TABLE_REMOVE_ROW_FIELDS_USAGE = "Usage:\n nolo table remove-row-fields --table <exact-meta-dbKey> --row-dbkeys <non-empty-json-array> --fields <non-empty-json-array> [--yes] [--json]\n\n";
922
+ function parseNonEmptyStringArray(flag, raw) {
923
+ let parsed;
924
+ try {
925
+ parsed = JSON.parse(raw);
926
+ } catch (error) {
927
+ throw new Error(`${flag} must be valid JSON: ${toErrorMessage(error)}`);
928
+ }
929
+ if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every((value) => typeof value === "string" && value.trim().length > 0)) {
930
+ throw new Error(`${flag} must be a non-empty JSON array of strings.`);
931
+ }
932
+ return [...new Set(parsed.map((value) => value.trim()))];
933
+ }
934
+ function readAffectedCount(payload) {
935
+ if (!isRecord(payload)) return 0;
936
+ const direct = asOptionalFiniteNumber(payload.affectedCount);
937
+ if (direct !== void 0) return direct;
938
+ if (isRecord(payload.rawData)) {
939
+ return asOptionalFiniteNumber(payload.rawData.affectedCount) ?? 0;
940
+ }
941
+ return 0;
942
+ }
943
+ async function runExactTableMaintenanceCommand(input) {
944
+ const env = input.deps.env ?? process.env;
945
+ const output = input.deps.output ?? process.stdout;
946
+ if (hasFlag(input.args, "--help") || hasFlag(input.args, "-h")) {
947
+ output.write(input.usage);
948
+ return 0;
949
+ }
950
+ const tableDbKey = readOption(input.args, "--table").trim();
951
+ if (!tableDbKey || !tableDbKey.startsWith("meta-")) {
952
+ output.write(`[nolo] table ${input.action} failed: --table must be an exact table dbKey beginning with "meta-".
953
+ ${input.usage}`);
954
+ return 1;
955
+ }
956
+ const authToken = resolveAuthToken(input.args, env);
957
+ if (!authToken) {
958
+ output.write(`[nolo] table ${input.action} failed: AUTH_TOKEN is required.
959
+ `);
960
+ return 1;
961
+ }
962
+ const rowDbKeysRaw = readOption(input.args, "--row-dbkeys");
963
+ if (!rowDbKeysRaw) {
964
+ output.write(`[nolo] table ${input.action} failed: --row-dbkeys is required.
965
+ ${input.usage}`);
966
+ return 1;
967
+ }
968
+ let rowDbKeys;
969
+ let fields;
970
+ try {
971
+ rowDbKeys = parseNonEmptyStringArray("--row-dbkeys", rowDbKeysRaw);
972
+ if (!rowDbKeys.every((dbKey) => dbKey.startsWith("row-"))) {
973
+ throw new Error(
974
+ '--row-dbkeys must contain exact row dbKeys beginning with "row-".'
975
+ );
976
+ }
977
+ if (input.requireFields) {
978
+ const fieldsRaw = readOption(input.args, "--fields");
979
+ if (!fieldsRaw) {
980
+ output.write(`[nolo] table ${input.action} failed: --fields is required.
981
+ ${input.usage}`);
982
+ return 1;
983
+ }
984
+ fields = parseNonEmptyStringArray("--fields", fieldsRaw);
985
+ }
986
+ } catch (error) {
987
+ output.write(`[nolo] table ${input.action} failed: ${toErrorMessage(error)}
988
+ ${input.usage}`);
989
+ return 1;
990
+ }
991
+ const execute = hasFlag(input.args, "--yes");
992
+ const body = {
993
+ tableDbKey,
994
+ rowDbKeys,
995
+ ...fields ? { fields } : {},
996
+ dryRun: !execute,
997
+ ...execute ? { confirmTableDbKey: tableDbKey } : {}
998
+ };
999
+ const result = await postTableJson(
1000
+ input.deps,
1001
+ { authToken, serverUrl: resolveServerUrl(input.args, env) },
1002
+ input.path,
1003
+ body
1004
+ );
1005
+ if (!result.ok) {
1006
+ output.write(`[nolo] table ${input.action} failed: ${result.message}
1007
+ `);
1008
+ return 1;
1009
+ }
1010
+ if (hasFlag(input.args, "--json") || readOption(input.args, "--output") === "json") {
1011
+ output.write(`${JSON.stringify(result.payload)}
1012
+ `);
1013
+ return 0;
1014
+ }
1015
+ const status = execute ? "EXECUTED" : "DRY RUN";
1016
+ output.write(`[nolo] table ${input.action} ${status}: affectedCount=${readAffectedCount(result.payload)}
1017
+ `);
1018
+ return 0;
1019
+ }
1020
+ async function runTablePurgeRowsCommand(args, deps = {}) {
1021
+ return runExactTableMaintenanceCommand({
1022
+ args,
1023
+ deps,
1024
+ action: "purge-rows",
1025
+ path: "/api/table/purge-rows",
1026
+ usage: TABLE_PURGE_ROWS_USAGE,
1027
+ requireFields: false
1028
+ });
1029
+ }
1030
+ async function runTableRemoveRowFieldsCommand(args, deps = {}) {
1031
+ return runExactTableMaintenanceCommand({
1032
+ args,
1033
+ deps,
1034
+ action: "remove-row-fields",
1035
+ path: "/api/table/remove-row-fields",
1036
+ usage: TABLE_REMOVE_ROW_FIELDS_USAGE,
1037
+ requireFields: true
1038
+ });
1039
+ }
1040
+ async function runTableAddColumnCommand(args, deps = {}) {
1041
+ const env = deps.env ?? process.env;
1042
+ const output = deps.output ?? process.stdout;
1043
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
1044
+ output.write(TABLE_ADD_COLUMN_USAGE);
1045
+ return 0;
1046
+ }
1047
+ const ctx = resolveTableContext(args, env, output);
1048
+ if (!ctx.ok) {
1049
+ output.write(TABLE_ADD_COLUMN_USAGE);
1050
+ return 1;
1051
+ }
1052
+ const name = readOption(args, "--name").trim();
1053
+ if (!name) {
1054
+ output.write(`[nolo] table add-column failed: --name is required.
1055
+ ${TABLE_ADD_COLUMN_USAGE}`);
1056
+ return 1;
1057
+ }
1058
+ const body = {
1059
+ tenantId: ctx.tenantId,
1060
+ tableId: ctx.tableId,
1061
+ name
1062
+ };
1063
+ const label = readOption(args, "--label").trim();
1064
+ if (label) body.label = label;
1065
+ const type = readOption(args, "--type").trim();
1066
+ if (type) body.type = type;
1067
+ const optionsRaw = readOption(args, "--options");
1068
+ if (optionsRaw) {
1069
+ let optionsParsed;
1070
+ try {
1071
+ optionsParsed = JSON.parse(optionsRaw);
1072
+ } catch (error) {
1073
+ output.write(`[nolo] table add-column failed: --options must be valid JSON: ${toErrorMessage(error)}
1074
+ `);
1075
+ return 1;
1076
+ }
1077
+ if (!Array.isArray(optionsParsed)) {
1078
+ output.write(`[nolo] table add-column failed: --options must be a JSON array.
1079
+ `);
1080
+ return 1;
1081
+ }
1082
+ body.options = optionsParsed;
1083
+ }
1084
+ const result = await postTableJson(deps, ctx, "/api/table/add-column", body);
1085
+ if (!result.ok) {
1086
+ output.write(`[nolo] table add-column failed: ${result.message}
1087
+ `);
1088
+ return 1;
1089
+ }
1090
+ output.write(`${JSON.stringify({ ok: true, action: "add-column", result: result.payload })}
1091
+ `);
1092
+ return 0;
1093
+ }
1094
+ async function runTableAddRowCommand(args, deps = {}) {
1095
+ const env = deps.env ?? process.env;
1096
+ const output = deps.output ?? process.stdout;
1097
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
1098
+ output.write(TABLE_ADD_ROW_USAGE);
1099
+ return 0;
1100
+ }
1101
+ const ctx = resolveTableContext(args, env, output);
1102
+ if (!ctx.ok) {
1103
+ output.write(TABLE_ADD_ROW_USAGE);
1104
+ return 1;
1105
+ }
1106
+ const valuesRaw = readOption(args, "--values");
1107
+ if (!valuesRaw) {
1108
+ output.write(`[nolo] table add-row failed: --values is required.
1109
+ ${TABLE_ADD_ROW_USAGE}`);
1110
+ return 1;
1111
+ }
1112
+ let values;
1113
+ try {
1114
+ values = parseValuesObject("--values", valuesRaw);
1115
+ } catch (error) {
1116
+ output.write(`[nolo] table add-row failed: ${toErrorMessage(error)}
1117
+ ${TABLE_ADD_ROW_USAGE}`);
1118
+ return 1;
1119
+ }
1120
+ const result = await postTableJson(deps, ctx, "/api/table/add-row", { tenantId: ctx.tenantId, tableId: ctx.tableId, values });
1121
+ if (!result.ok) {
1122
+ output.write(`[nolo] table add-row failed: ${result.message}
1123
+ `);
1124
+ return 1;
1125
+ }
1126
+ output.write(`${JSON.stringify({ ok: true, action: "add-row", result: result.payload })}
1127
+ `);
1128
+ return 0;
1129
+ }
1130
+ async function runTableAddRowsCommand(args, deps = {}) {
1131
+ const env = deps.env ?? process.env;
1132
+ const output = deps.output ?? process.stdout;
1133
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
1134
+ output.write(TABLE_ADD_ROWS_USAGE);
1135
+ return 0;
1136
+ }
1137
+ const ctx = resolveTableContext(args, env, output);
1138
+ if (!ctx.ok) {
1139
+ output.write(TABLE_ADD_ROWS_USAGE);
1140
+ return 1;
1141
+ }
1142
+ const rowsRaw = readOption(args, "--rows");
1143
+ if (!rowsRaw) {
1144
+ output.write(`[nolo] table add-rows failed: --rows is required.
1145
+ ${TABLE_ADD_ROWS_USAGE}`);
1146
+ return 1;
1147
+ }
1148
+ let rows;
1149
+ try {
1150
+ rows = parseRowsArray("--rows", rowsRaw);
1151
+ } catch (error) {
1152
+ output.write(`[nolo] table add-rows failed: ${toErrorMessage(error)}
1153
+ ${TABLE_ADD_ROWS_USAGE}`);
1154
+ return 1;
1155
+ }
1156
+ const result = await postTableJson(deps, ctx, "/api/table/add-rows", { tenantId: ctx.tenantId, tableId: ctx.tableId, rows });
1157
+ if (!result.ok) {
1158
+ output.write(`[nolo] table add-rows failed: ${result.message}
1159
+ `);
1160
+ return 1;
1161
+ }
1162
+ output.write(`${JSON.stringify({ ok: true, action: "add-rows", result: result.payload })}
1163
+ `);
1164
+ return 0;
1165
+ }
1166
+ async function runTableUpdateRowCommand(args, deps = {}) {
1167
+ const env = deps.env ?? process.env;
1168
+ const output = deps.output ?? process.stdout;
1169
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
1170
+ output.write(TABLE_UPDATE_ROW_USAGE);
1171
+ return 0;
1172
+ }
1173
+ const ctx = resolveTableContext(args, env, output);
1174
+ if (!ctx.ok) {
1175
+ output.write(TABLE_UPDATE_ROW_USAGE);
1176
+ return 1;
1177
+ }
1178
+ const rowRaw = readOption(args, "--row");
1179
+ const target = resolveRowTarget(rowRaw);
1180
+ if (!target.rowId && !target.rowDbKey) {
1181
+ output.write(`[nolo] table update-row failed: --row is required (rowId or row dbKey).
1182
+ ${TABLE_UPDATE_ROW_USAGE}`);
1183
+ return 1;
1184
+ }
1185
+ const changesRaw = readOption(args, "--changes");
1186
+ if (!changesRaw) {
1187
+ output.write(`[nolo] table update-row failed: --changes is required.
1188
+ ${TABLE_UPDATE_ROW_USAGE}`);
1189
+ return 1;
1190
+ }
1191
+ let changes;
1192
+ try {
1193
+ changes = parseValuesObject("--changes", changesRaw);
1194
+ } catch (error) {
1195
+ output.write(`[nolo] table update-row failed: ${toErrorMessage(error)}
1196
+ ${TABLE_UPDATE_ROW_USAGE}`);
1197
+ return 1;
1198
+ }
1199
+ const result = await postTableJson(deps, ctx, "/api/table/update-row", { tenantId: ctx.tenantId, tableId: ctx.tableId, ...target, changes });
1200
+ if (!result.ok) {
1201
+ output.write(`[nolo] table update-row failed: ${result.message}
1202
+ `);
1203
+ return 1;
1204
+ }
1205
+ output.write(`${JSON.stringify({ ok: true, action: "update-row", result: result.payload })}
1206
+ `);
1207
+ return 0;
1208
+ }
1209
+ async function runTableUpdateRowsCommand(args, deps = {}) {
1210
+ const env = deps.env ?? process.env;
1211
+ const output = deps.output ?? process.stdout;
1212
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
1213
+ output.write(TABLE_UPDATE_ROWS_USAGE);
1214
+ return 0;
1215
+ }
1216
+ const ctx = resolveTableContext(args, env, output);
1217
+ if (!ctx.ok) {
1218
+ output.write(TABLE_UPDATE_ROWS_USAGE);
1219
+ return 1;
1220
+ }
1221
+ const updatesRaw = readOption(args, "--updates");
1222
+ if (!updatesRaw) {
1223
+ output.write(`[nolo] table update-rows failed: --updates is required.
1224
+ ${TABLE_UPDATE_ROWS_USAGE}`);
1225
+ return 1;
1226
+ }
1227
+ let updates;
1228
+ try {
1229
+ updates = parseUpdatesArray("--updates", updatesRaw);
1230
+ } catch (error) {
1231
+ output.write(`[nolo] table update-rows failed: ${toErrorMessage(error)}
1232
+ ${TABLE_UPDATE_ROWS_USAGE}`);
1233
+ return 1;
1234
+ }
1235
+ const result = await postTableJson(deps, ctx, "/api/table/update-rows", { tenantId: ctx.tenantId, tableId: ctx.tableId, updates });
1236
+ if (!result.ok) {
1237
+ output.write(`[nolo] table update-rows failed: ${result.message}
1238
+ `);
1239
+ return 1;
1240
+ }
1241
+ output.write(`${JSON.stringify({ ok: true, action: "update-rows", result: result.payload })}
1242
+ `);
1243
+ return 0;
1244
+ }
1245
+ async function runTableDeleteRowCommand(args, deps = {}) {
1246
+ const env = deps.env ?? process.env;
1247
+ const output = deps.output ?? process.stdout;
1248
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
1249
+ output.write(TABLE_DELETE_ROW_USAGE);
1250
+ return 0;
1251
+ }
1252
+ const ctx = resolveTableContext(args, env, output);
1253
+ if (!ctx.ok) {
1254
+ output.write(TABLE_DELETE_ROW_USAGE);
1255
+ return 1;
1256
+ }
1257
+ const rowRaw = readOption(args, "--row");
1258
+ const target = resolveRowTarget(rowRaw);
1259
+ if (!target.rowId && !target.rowDbKey) {
1260
+ output.write(`[nolo] table delete-row failed: --row is required (rowId or row dbKey).
1261
+ ${TABLE_DELETE_ROW_USAGE}`);
1262
+ return 1;
1263
+ }
1264
+ const result = await postTableJson(deps, ctx, "/api/table/delete-row", { tenantId: ctx.tenantId, tableId: ctx.tableId, ...target });
1265
+ if (!result.ok) {
1266
+ output.write(`[nolo] table delete-row failed: ${result.message}
1267
+ `);
1268
+ return 1;
1269
+ }
1270
+ output.write(`${JSON.stringify({ ok: true, action: "delete-row", result: result.payload })}
1271
+ `);
1272
+ return 0;
1273
+ }
1274
+ export {
1275
+ runTableAddColumnCommand,
1276
+ runTableAddRowCommand,
1277
+ runTableAddRowsCommand,
1278
+ runTableDeleteRowCommand,
1279
+ runTableDeleteRowsCommand,
1280
+ runTableListCommand,
1281
+ runTablePurgeRowsCommand,
1282
+ runTableQueryCommand,
1283
+ runTableRemoveRowFieldsCommand,
1284
+ runTableUpdateRowCommand,
1285
+ runTableUpdateRowsCommand
1286
+ };