lindoai-cli 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +35 -2339
- package/dist/index.js +35 -2314
- package/package.json +1 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,216 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var os2 = require('os');
|
|
8
|
-
var commander = require('commander');
|
|
9
|
-
var lindoai = require('lindoai');
|
|
10
|
-
var child_process = require('child_process');
|
|
11
|
-
var url = require('url');
|
|
12
|
-
var crypto = require('crypto');
|
|
13
|
-
|
|
14
|
-
function _interopNamespace(e) {
|
|
15
|
-
if (e && e.__esModule) return e;
|
|
16
|
-
var n = Object.create(null);
|
|
17
|
-
if (e) {
|
|
18
|
-
Object.keys(e).forEach(function (k) {
|
|
19
|
-
if (k !== 'default') {
|
|
20
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
21
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
22
|
-
enumerable: true,
|
|
23
|
-
get: function () { return e[k]; }
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
n.default = e;
|
|
29
|
-
return Object.freeze(n);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
var http__namespace = /*#__PURE__*/_interopNamespace(http);
|
|
33
|
-
var fs3__namespace = /*#__PURE__*/_interopNamespace(fs3);
|
|
34
|
-
var path3__namespace = /*#__PURE__*/_interopNamespace(path3);
|
|
35
|
-
var os2__namespace = /*#__PURE__*/_interopNamespace(os2);
|
|
36
|
-
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
37
|
-
|
|
38
|
-
var __defProp = Object.defineProperty;
|
|
39
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
40
|
-
var __esm = (fn, res) => function __init() {
|
|
41
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
42
|
-
};
|
|
43
|
-
var __export = (target, all) => {
|
|
44
|
-
for (var name in all)
|
|
45
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// src/server/live-preview-server.ts
|
|
49
|
-
var live_preview_server_exports = {};
|
|
50
|
-
__export(live_preview_server_exports, {
|
|
51
|
-
LIVE_RELOAD_SCRIPT: () => LIVE_RELOAD_SCRIPT,
|
|
52
|
-
injectLiveReload: () => injectLiveReload,
|
|
53
|
-
startLivePreviewServer: () => startLivePreviewServer
|
|
54
|
-
});
|
|
55
|
-
function injectLiveReload(html) {
|
|
56
|
-
const bodyCloseTagRegex = /<\/body>/i;
|
|
57
|
-
const match = html.match(bodyCloseTagRegex);
|
|
58
|
-
if (match && match.index !== void 0) {
|
|
59
|
-
return html.slice(0, match.index) + LIVE_RELOAD_SCRIPT + html.slice(match.index);
|
|
60
|
-
}
|
|
61
|
-
return html + LIVE_RELOAD_SCRIPT;
|
|
62
|
-
}
|
|
63
|
-
function startLivePreviewServer(filePath) {
|
|
64
|
-
return new Promise((resolve4, reject) => {
|
|
65
|
-
const absolutePath = path3__namespace.resolve(filePath);
|
|
66
|
-
const sseClients = /* @__PURE__ */ new Set();
|
|
67
|
-
let debounceTimer = null;
|
|
68
|
-
const DEBOUNCE_MS = 100;
|
|
69
|
-
function notifyClients() {
|
|
70
|
-
for (const client of sseClients) {
|
|
71
|
-
try {
|
|
72
|
-
client.write("data: reload\n\n");
|
|
73
|
-
} catch {
|
|
74
|
-
sseClients.delete(client);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function handleFileChange() {
|
|
79
|
-
if (debounceTimer) {
|
|
80
|
-
clearTimeout(debounceTimer);
|
|
81
|
-
}
|
|
82
|
-
debounceTimer = setTimeout(() => {
|
|
83
|
-
notifyClients();
|
|
84
|
-
debounceTimer = null;
|
|
85
|
-
}, DEBOUNCE_MS);
|
|
86
|
-
}
|
|
87
|
-
function handleRequest(req, res) {
|
|
88
|
-
const url = req.url || "/";
|
|
89
|
-
const method = req.method || "GET";
|
|
90
|
-
if (method !== "GET") {
|
|
91
|
-
res.writeHead(405, { "Content-Type": "text/plain" });
|
|
92
|
-
res.end("Method Not Allowed");
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
if (url === "/__live-reload") {
|
|
96
|
-
res.writeHead(200, {
|
|
97
|
-
"Content-Type": "text/event-stream",
|
|
98
|
-
"Cache-Control": "no-cache",
|
|
99
|
-
"Connection": "keep-alive",
|
|
100
|
-
"Access-Control-Allow-Origin": "*"
|
|
101
|
-
});
|
|
102
|
-
res.write("data: connected\n\n");
|
|
103
|
-
sseClients.add(res);
|
|
104
|
-
req.on("close", () => {
|
|
105
|
-
sseClients.delete(res);
|
|
106
|
-
});
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
if (url === "/" || url === "/index.html") {
|
|
110
|
-
try {
|
|
111
|
-
const html = fs3__namespace.readFileSync(absolutePath, "utf-8");
|
|
112
|
-
const injectedHtml = injectLiveReload(html);
|
|
113
|
-
res.writeHead(200, {
|
|
114
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
115
|
-
"Cache-Control": "no-cache"
|
|
116
|
-
});
|
|
117
|
-
res.end(injectedHtml);
|
|
118
|
-
} catch (err) {
|
|
119
|
-
const errorMessage = err instanceof Error ? err.message : "Unknown error";
|
|
120
|
-
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
121
|
-
res.end(`<!DOCTYPE html>
|
|
2
|
+
'use strict';var Ee=require('http'),w=require('fs'),j=require('path'),de=require('os'),commander=require('commander'),lindoai=require('lindoai'),child_process=require('child_process'),url=require('url'),De=require('crypto');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var Ee__namespace=/*#__PURE__*/_interopNamespace(Ee);var w__namespace=/*#__PURE__*/_interopNamespace(w);var j__namespace=/*#__PURE__*/_interopNamespace(j);var de__namespace=/*#__PURE__*/_interopNamespace(de);var De__namespace=/*#__PURE__*/_interopNamespace(De);var Me=Object.defineProperty;var Be=(o,t)=>()=>(o&&(t=o(o=0)),t);var He=(o,t)=>{for(var n in t)Me(o,n,{get:t[n],enumerable:true});};var pe={};He(pe,{LIVE_RELOAD_SCRIPT:()=>me,injectLiveReload:()=>Le,startLivePreviewServer:()=>xt});function Le(o){let t=/<\/body>/i,n=o.match(t);return n&&n.index!==void 0?o.slice(0,n.index)+me+o.slice(n.index):o+me}function xt(o){return new Promise((t,n)=>{let e=j__namespace.resolve(o),i=new Set,a=null,l=100;function m(){for(let u of i)try{u.write(`data: reload
|
|
3
|
+
|
|
4
|
+
`);}catch{i.delete(u);}}function p(){a&&clearTimeout(a),a=setTimeout(()=>{m(),a=null;},l);}function b(u,f){let S=u.url||"/";if((u.method||"GET")!=="GET"){f.writeHead(405,{"Content-Type":"text/plain"}),f.end("Method Not Allowed");return}if(S==="/__live-reload"){f.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),f.write(`data: connected
|
|
5
|
+
|
|
6
|
+
`),i.add(f),u.on("close",()=>{i.delete(f);});return}if(S==="/"||S==="/index.html"){try{let _=w__namespace.readFileSync(e,"utf-8"),G=Le(_);f.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-cache"}),f.end(G);}catch(_){let G=_ instanceof Error?_.message:"Unknown error";f.writeHead(500,{"Content-Type":"text/html; charset=utf-8"}),f.end(`<!DOCTYPE html>
|
|
122
7
|
<html>
|
|
123
8
|
<head><title>Error</title></head>
|
|
124
9
|
<body>
|
|
125
10
|
<h1>Error loading file</h1>
|
|
126
|
-
<p>${
|
|
11
|
+
<p>${G}</p>
|
|
127
12
|
</body>
|
|
128
|
-
</html>`);
|
|
129
|
-
}
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
|
|
133
|
-
res.end(`<!DOCTYPE html>
|
|
13
|
+
</html>`);}return}f.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),f.end(`<!DOCTYPE html>
|
|
134
14
|
<html>
|
|
135
15
|
<head><title>Not Found</title></head>
|
|
136
16
|
<body>
|
|
137
17
|
<h1>404 Not Found</h1>
|
|
138
18
|
<p>The requested resource was not found.</p>
|
|
139
19
|
</body>
|
|
140
|
-
</html>`);
|
|
141
|
-
}
|
|
142
|
-
const server = http__namespace.createServer(handleRequest);
|
|
143
|
-
let watcher = null;
|
|
144
|
-
server.on("error", (err) => {
|
|
145
|
-
reject(new Error(`Failed to start preview server: ${err.message}`));
|
|
146
|
-
});
|
|
147
|
-
server.listen(0, "127.0.0.1", () => {
|
|
148
|
-
const address = server.address();
|
|
149
|
-
if (!address || typeof address === "string") {
|
|
150
|
-
reject(new Error("Failed to get server address"));
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const port = address.port;
|
|
154
|
-
try {
|
|
155
|
-
watcher = fs3__namespace.watch(absolutePath, (eventType) => {
|
|
156
|
-
if (eventType === "change") {
|
|
157
|
-
handleFileChange();
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
watcher.on("error", (err) => {
|
|
161
|
-
console.error(`[Live Preview] File watcher error: ${err.message}`);
|
|
162
|
-
});
|
|
163
|
-
} catch (err) {
|
|
164
|
-
const errorMessage = err instanceof Error ? err.message : "Unknown error";
|
|
165
|
-
console.error(`[Live Preview] Failed to watch file: ${errorMessage}`);
|
|
166
|
-
}
|
|
167
|
-
const handleShutdown = () => {
|
|
168
|
-
if (watcher) {
|
|
169
|
-
watcher.close();
|
|
170
|
-
watcher = null;
|
|
171
|
-
}
|
|
172
|
-
if (debounceTimer) {
|
|
173
|
-
clearTimeout(debounceTimer);
|
|
174
|
-
debounceTimer = null;
|
|
175
|
-
}
|
|
176
|
-
for (const client of sseClients) {
|
|
177
|
-
try {
|
|
178
|
-
client.end();
|
|
179
|
-
} catch {
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
sseClients.clear();
|
|
183
|
-
const pidFilePath = path3__namespace.join(os2__namespace.tmpdir(), "lindoai-pages-preview.pid");
|
|
184
|
-
const portFilePath = path3__namespace.join(os2__namespace.tmpdir(), "lindoai-pages-preview.port");
|
|
185
|
-
try {
|
|
186
|
-
if (fs3__namespace.existsSync(pidFilePath)) {
|
|
187
|
-
fs3__namespace.unlinkSync(pidFilePath);
|
|
188
|
-
}
|
|
189
|
-
} catch {
|
|
190
|
-
}
|
|
191
|
-
try {
|
|
192
|
-
if (fs3__namespace.existsSync(portFilePath)) {
|
|
193
|
-
fs3__namespace.unlinkSync(portFilePath);
|
|
194
|
-
}
|
|
195
|
-
} catch {
|
|
196
|
-
}
|
|
197
|
-
server.close(() => {
|
|
198
|
-
process.exit(0);
|
|
199
|
-
});
|
|
200
|
-
setTimeout(() => {
|
|
201
|
-
process.exit(0);
|
|
202
|
-
}, 1e3);
|
|
203
|
-
};
|
|
204
|
-
process.on("SIGTERM", handleShutdown);
|
|
205
|
-
process.on("SIGINT", handleShutdown);
|
|
206
|
-
resolve4(port);
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
var LIVE_RELOAD_SCRIPT;
|
|
211
|
-
var init_live_preview_server = __esm({
|
|
212
|
-
"src/server/live-preview-server.ts"() {
|
|
213
|
-
LIVE_RELOAD_SCRIPT = `<script>
|
|
20
|
+
</html>`);}let g=Ee__namespace.createServer(b),v=null;g.on("error",u=>{n(new Error(`Failed to start preview server: ${u.message}`));}),g.listen(0,"127.0.0.1",()=>{let u=g.address();if(!u||typeof u=="string"){n(new Error("Failed to get server address"));return}let f=u.port;try{v=w__namespace.watch(e,k=>{k==="change"&&p();}),v.on("error",k=>{console.error(`[Live Preview] File watcher error: ${k.message}`);});}catch(k){let _=k instanceof Error?k.message:"Unknown error";console.error(`[Live Preview] Failed to watch file: ${_}`);}let S=()=>{v&&(v.close(),v=null),a&&(clearTimeout(a),a=null);for(let G of i)try{G.end();}catch{}i.clear();let k=j__namespace.join(de__namespace.tmpdir(),"lindoai-pages-preview.pid"),_=j__namespace.join(de__namespace.tmpdir(),"lindoai-pages-preview.port");try{w__namespace.existsSync(k)&&w__namespace.unlinkSync(k);}catch{}try{w__namespace.existsSync(_)&&w__namespace.unlinkSync(_);}catch{}g.close(()=>{process.exit(0);}),setTimeout(()=>{process.exit(0);},1e3);};process.on("SIGTERM",S),process.on("SIGINT",S),t(f);});})}var me,ue=Be(()=>{me=`<script>
|
|
214
21
|
(function() {
|
|
215
22
|
var eventSource = new EventSource('/__live-reload');
|
|
216
23
|
eventSource.onmessage = function(event) {
|
|
@@ -222,1382 +29,15 @@ var init_live_preview_server = __esm({
|
|
|
222
29
|
console.log('[Live Reload] Connection lost, attempting to reconnect...');
|
|
223
30
|
};
|
|
224
31
|
})();
|
|
225
|
-
</script>`;
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
var ENV_API_KEY = "LINDO_API_KEY";
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
function
|
|
234
|
-
return path3__namespace.join(os2__namespace.homedir(), CONFIG_DIR);
|
|
235
|
-
}
|
|
236
|
-
function getConfigPath() {
|
|
237
|
-
return path3__namespace.join(getConfigDir(), CONFIG_FILE);
|
|
238
|
-
}
|
|
239
|
-
function readConfigFile() {
|
|
240
|
-
const configPath = getConfigPath();
|
|
241
|
-
try {
|
|
242
|
-
if (fs3__namespace.existsSync(configPath)) {
|
|
243
|
-
const content = fs3__namespace.readFileSync(configPath, "utf-8");
|
|
244
|
-
return JSON.parse(content);
|
|
245
|
-
}
|
|
246
|
-
} catch {
|
|
247
|
-
}
|
|
248
|
-
return {};
|
|
249
|
-
}
|
|
250
|
-
function writeConfigFile(config) {
|
|
251
|
-
const configDir = getConfigDir();
|
|
252
|
-
const configPath = getConfigPath();
|
|
253
|
-
if (!fs3__namespace.existsSync(configDir)) {
|
|
254
|
-
fs3__namespace.mkdirSync(configDir, { recursive: true });
|
|
255
|
-
}
|
|
256
|
-
fs3__namespace.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
257
|
-
}
|
|
258
|
-
function loadConfig() {
|
|
259
|
-
const fileConfig = readConfigFile();
|
|
260
|
-
const apiKey = process.env[ENV_API_KEY] || fileConfig.apiKey;
|
|
261
|
-
const baseUrl = process.env[ENV_BASE_URL] || fileConfig.baseUrl || DEFAULT_BASE_URL;
|
|
262
|
-
return {
|
|
263
|
-
apiKey,
|
|
264
|
-
baseUrl
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
function saveApiKey(apiKey) {
|
|
268
|
-
const config = readConfigFile();
|
|
269
|
-
config.apiKey = apiKey;
|
|
270
|
-
writeConfigFile(config);
|
|
271
|
-
}
|
|
272
|
-
function saveConfig(key, value) {
|
|
273
|
-
const config = readConfigFile();
|
|
274
|
-
switch (key) {
|
|
275
|
-
case "apiKey":
|
|
276
|
-
config.apiKey = value;
|
|
277
|
-
break;
|
|
278
|
-
case "baseUrl":
|
|
279
|
-
config.baseUrl = value;
|
|
280
|
-
break;
|
|
281
|
-
default:
|
|
282
|
-
throw new Error(`Unknown configuration key: ${key}`);
|
|
283
|
-
}
|
|
284
|
-
writeConfigFile(config);
|
|
285
|
-
}
|
|
286
|
-
function getConfigValue(key) {
|
|
287
|
-
const config = loadConfig();
|
|
288
|
-
switch (key) {
|
|
289
|
-
case "apiKey":
|
|
290
|
-
return config.apiKey;
|
|
291
|
-
case "baseUrl":
|
|
292
|
-
return config.baseUrl;
|
|
293
|
-
default:
|
|
294
|
-
return void 0;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
function hasApiKey() {
|
|
298
|
-
const config = loadConfig();
|
|
299
|
-
return !!config.apiKey;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// src/output.ts
|
|
303
|
-
var colors = {
|
|
304
|
-
reset: "\x1B[0m",
|
|
305
|
-
red: "\x1B[31m",
|
|
306
|
-
green: "\x1B[32m",
|
|
307
|
-
blue: "\x1B[34m",
|
|
308
|
-
cyan: "\x1B[36m",
|
|
309
|
-
gray: "\x1B[90m",
|
|
310
|
-
bold: "\x1B[1m"
|
|
311
|
-
};
|
|
312
|
-
function useColors() {
|
|
313
|
-
return !process.env.NO_COLOR && process.stdout.isTTY !== false;
|
|
314
|
-
}
|
|
315
|
-
function colorize(text, color) {
|
|
316
|
-
if (!useColors()) {
|
|
317
|
-
return text;
|
|
318
|
-
}
|
|
319
|
-
return `${color}${text}${colors.reset}`;
|
|
320
|
-
}
|
|
321
|
-
function success(message) {
|
|
322
|
-
console.log(colorize(`\u2713 ${message}`, colors.green));
|
|
323
|
-
}
|
|
324
|
-
function error(message) {
|
|
325
|
-
console.error(colorize(`\u2717 ${message}`, colors.red));
|
|
326
|
-
}
|
|
327
|
-
function info(message) {
|
|
328
|
-
console.log(colorize(`\u2139 ${message}`, colors.blue));
|
|
329
|
-
}
|
|
330
|
-
function formatJson(data) {
|
|
331
|
-
return JSON.stringify(data, null, 2);
|
|
332
|
-
}
|
|
333
|
-
function formatTable(data) {
|
|
334
|
-
if (data === null || data === void 0) {
|
|
335
|
-
return "";
|
|
336
|
-
}
|
|
337
|
-
if (Array.isArray(data)) {
|
|
338
|
-
if (data.length === 0) {
|
|
339
|
-
return "No data";
|
|
340
|
-
}
|
|
341
|
-
const keys = /* @__PURE__ */ new Set();
|
|
342
|
-
for (const item of data) {
|
|
343
|
-
if (typeof item === "object" && item !== null) {
|
|
344
|
-
Object.keys(item).forEach((key) => keys.add(key));
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
if (keys.size === 0) {
|
|
348
|
-
return data.map((item) => String(item)).join("\n");
|
|
349
|
-
}
|
|
350
|
-
const columns = Array.from(keys);
|
|
351
|
-
return formatTableFromRows(columns, data);
|
|
352
|
-
}
|
|
353
|
-
if (typeof data === "object") {
|
|
354
|
-
const obj = data;
|
|
355
|
-
const entries = Object.entries(obj);
|
|
356
|
-
if (entries.length === 0) {
|
|
357
|
-
return "No data";
|
|
358
|
-
}
|
|
359
|
-
const maxKeyLength = Math.max(...entries.map(([key]) => key.length));
|
|
360
|
-
return entries.map(([key, value]) => {
|
|
361
|
-
const paddedKey = key.padEnd(maxKeyLength);
|
|
362
|
-
const formattedValue = formatValue(value);
|
|
363
|
-
return `${colorize(paddedKey, colors.cyan)} ${formattedValue}`;
|
|
364
|
-
}).join("\n");
|
|
365
|
-
}
|
|
366
|
-
return String(data);
|
|
367
|
-
}
|
|
368
|
-
function formatTableFromRows(columns, rows) {
|
|
369
|
-
const widths = {};
|
|
370
|
-
for (const col of columns) {
|
|
371
|
-
widths[col] = col.length;
|
|
372
|
-
}
|
|
373
|
-
for (const row of rows) {
|
|
374
|
-
if (typeof row === "object" && row !== null) {
|
|
375
|
-
const obj = row;
|
|
376
|
-
for (const col of columns) {
|
|
377
|
-
const value = formatValue(obj[col]);
|
|
378
|
-
widths[col] = Math.max(widths[col], value.length);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
const header = columns.map((col) => colorize(col.padEnd(widths[col]), colors.bold)).join(" ");
|
|
383
|
-
const separator = columns.map((col) => "-".repeat(widths[col])).join(" ");
|
|
384
|
-
const dataRows = rows.map((row) => {
|
|
385
|
-
if (typeof row === "object" && row !== null) {
|
|
386
|
-
const obj = row;
|
|
387
|
-
return columns.map((col) => formatValue(obj[col]).padEnd(widths[col])).join(" ");
|
|
388
|
-
}
|
|
389
|
-
return String(row);
|
|
390
|
-
});
|
|
391
|
-
return [header, separator, ...dataRows].join("\n");
|
|
392
|
-
}
|
|
393
|
-
function formatValue(value) {
|
|
394
|
-
if (value === null || value === void 0) {
|
|
395
|
-
return colorize("-", colors.gray);
|
|
396
|
-
}
|
|
397
|
-
if (typeof value === "boolean") {
|
|
398
|
-
return value ? colorize("true", colors.green) : colorize("false", colors.red);
|
|
399
|
-
}
|
|
400
|
-
if (typeof value === "number") {
|
|
401
|
-
return String(value);
|
|
402
|
-
}
|
|
403
|
-
if (typeof value === "object") {
|
|
404
|
-
if (Array.isArray(value)) {
|
|
405
|
-
return `[${value.length} items]`;
|
|
406
|
-
}
|
|
407
|
-
return JSON.stringify(value);
|
|
408
|
-
}
|
|
409
|
-
return String(value);
|
|
410
|
-
}
|
|
411
|
-
function output(data, format) {
|
|
412
|
-
if (format === "json") {
|
|
413
|
-
console.log(formatJson(data));
|
|
414
|
-
} else {
|
|
415
|
-
console.log(formatTable(data));
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// src/commands/config.ts
|
|
420
|
-
var VALID_KEYS = ["apiKey", "baseUrl"];
|
|
421
|
-
function createConfigCommand() {
|
|
422
|
-
const config = new commander.Command("config").description("Manage CLI configuration");
|
|
423
|
-
config.command("set <key> <value>").description("Set a configuration value").action((key, value) => {
|
|
424
|
-
if (!VALID_KEYS.includes(key)) {
|
|
425
|
-
error(`Invalid configuration key: ${key}`);
|
|
426
|
-
info(`Valid keys: ${VALID_KEYS.join(", ")}`);
|
|
427
|
-
process.exit(1);
|
|
428
|
-
}
|
|
429
|
-
try {
|
|
430
|
-
saveConfig(key, value);
|
|
431
|
-
success(`Configuration saved: ${key}`);
|
|
432
|
-
info(`Config file: ${getConfigPath()}`);
|
|
433
|
-
} catch (err) {
|
|
434
|
-
error(`Failed to save configuration: ${err instanceof Error ? err.message : String(err)}`);
|
|
435
|
-
process.exit(1);
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
config.command("get <key>").description("Get a configuration value").option("-f, --format <format>", "Output format (json, table)", "table").action((key, options) => {
|
|
439
|
-
const value = getConfigValue(key);
|
|
440
|
-
if (value === void 0) {
|
|
441
|
-
if (options.format === "json") {
|
|
442
|
-
output({ key, value: null }, options.format);
|
|
443
|
-
} else {
|
|
444
|
-
info(`Configuration key '${key}' is not set`);
|
|
445
|
-
}
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
if (options.format === "json") {
|
|
449
|
-
output({ key, value }, options.format);
|
|
450
|
-
} else {
|
|
451
|
-
const displayValue = key === "apiKey" ? maskApiKey(value) : value;
|
|
452
|
-
console.log(`${key}: ${displayValue}`);
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
config.command("list").description("List all configuration values").option("-f, --format <format>", "Output format (json, table)", "table").action((options) => {
|
|
456
|
-
const resolvedConfig = loadConfig();
|
|
457
|
-
const configData = {
|
|
458
|
-
apiKey: resolvedConfig.apiKey ? maskApiKey(resolvedConfig.apiKey) : "(not set)",
|
|
459
|
-
baseUrl: resolvedConfig.baseUrl,
|
|
460
|
-
configFile: getConfigPath()
|
|
461
|
-
};
|
|
462
|
-
if (options.format === "json") {
|
|
463
|
-
output(
|
|
464
|
-
{
|
|
465
|
-
apiKey: resolvedConfig.apiKey ? maskApiKey(resolvedConfig.apiKey) : null,
|
|
466
|
-
baseUrl: resolvedConfig.baseUrl,
|
|
467
|
-
configFile: getConfigPath()
|
|
468
|
-
},
|
|
469
|
-
options.format
|
|
470
|
-
);
|
|
471
|
-
} else {
|
|
472
|
-
output(configData, options.format);
|
|
473
|
-
}
|
|
474
|
-
});
|
|
475
|
-
config.command("path").description("Show the config file path").action(() => {
|
|
476
|
-
console.log(getConfigPath());
|
|
477
|
-
});
|
|
478
|
-
return config;
|
|
479
|
-
}
|
|
480
|
-
function maskApiKey(apiKey) {
|
|
481
|
-
if (apiKey.length <= 8) {
|
|
482
|
-
return "*".repeat(apiKey.length);
|
|
483
|
-
}
|
|
484
|
-
return `${apiKey.slice(0, 4)}${"*".repeat(apiKey.length - 8)}${apiKey.slice(-4)}`;
|
|
485
|
-
}
|
|
486
|
-
function createAgentsCommand() {
|
|
487
|
-
const agents = new commander.Command("agents").description("Run AI agents");
|
|
488
|
-
agents.command("run <agent-id>").description("Run an AI agent").option("-i, --input <json>", "Input data as JSON string", "{}").option("-s, --stream", "Stream the response", false).option("-f, --format <format>", "Output format (json, table)", "table").action(async (agentId, options) => {
|
|
489
|
-
if (!hasApiKey()) {
|
|
490
|
-
error("API key not configured");
|
|
491
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
492
|
-
info("Or set the LINDO_API_KEY environment variable");
|
|
493
|
-
process.exit(1);
|
|
494
|
-
}
|
|
495
|
-
const config = loadConfig();
|
|
496
|
-
const client = new lindoai.LindoClient({
|
|
497
|
-
apiKey: config.apiKey,
|
|
498
|
-
baseUrl: config.baseUrl
|
|
499
|
-
});
|
|
500
|
-
let input;
|
|
501
|
-
try {
|
|
502
|
-
input = JSON.parse(options.input);
|
|
503
|
-
} catch {
|
|
504
|
-
error("Invalid JSON input");
|
|
505
|
-
info(`Example: --input '{"prompt": "Hello!"}'`);
|
|
506
|
-
process.exit(1);
|
|
507
|
-
}
|
|
508
|
-
try {
|
|
509
|
-
info(`Running agent: ${agentId}`);
|
|
510
|
-
const result = await client.agents.run({
|
|
511
|
-
agent_id: agentId,
|
|
512
|
-
input,
|
|
513
|
-
stream: options.stream
|
|
514
|
-
});
|
|
515
|
-
if (result.success) {
|
|
516
|
-
success("Agent run completed");
|
|
517
|
-
output(result, options.format);
|
|
518
|
-
} else {
|
|
519
|
-
error(`Agent run failed: ${result.error || "Unknown error"}`);
|
|
520
|
-
output(result, options.format);
|
|
521
|
-
process.exit(1);
|
|
522
|
-
}
|
|
523
|
-
} catch (err) {
|
|
524
|
-
handleError(err);
|
|
525
|
-
}
|
|
526
|
-
});
|
|
527
|
-
return agents;
|
|
528
|
-
}
|
|
529
|
-
function handleError(err) {
|
|
530
|
-
if (err instanceof lindoai.AuthenticationError) {
|
|
531
|
-
error("Authentication failed");
|
|
532
|
-
info("Your API key may be invalid or expired");
|
|
533
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
534
|
-
process.exit(1);
|
|
535
|
-
}
|
|
536
|
-
if (err instanceof Error) {
|
|
537
|
-
error(err.message);
|
|
538
|
-
} else {
|
|
539
|
-
error("An unexpected error occurred");
|
|
540
|
-
}
|
|
541
|
-
process.exit(1);
|
|
542
|
-
}
|
|
543
|
-
function createWorkflowsCommand() {
|
|
544
|
-
const workflows = new commander.Command("workflows").description("Manage workflows");
|
|
545
|
-
workflows.command("list").description("List workflow logs").option("-n, --name <name>", "Filter by workflow name").option("-s, --status <status>", "Filter by status").option("-w, --website <id>", "Filter by website ID").option("-c, --client <id>", "Filter by client ID").option("-l, --limit <number>", "Maximum number of results", "50").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
546
|
-
const client = getClient();
|
|
547
|
-
try {
|
|
548
|
-
const result = await client.workflows.list({
|
|
549
|
-
workflow_name: options.name,
|
|
550
|
-
status: options.status,
|
|
551
|
-
website_id: options.website,
|
|
552
|
-
client_id: options.client,
|
|
553
|
-
limit: parseInt(options.limit)
|
|
554
|
-
});
|
|
555
|
-
if (result.success) {
|
|
556
|
-
output(result.data, options.format);
|
|
557
|
-
} else {
|
|
558
|
-
error("Failed to list workflows");
|
|
559
|
-
process.exit(1);
|
|
560
|
-
}
|
|
561
|
-
} catch (err) {
|
|
562
|
-
handleError2(err);
|
|
563
|
-
}
|
|
564
|
-
});
|
|
565
|
-
workflows.command("start <workflow-name>").description("Start a workflow").option("-p, --params <json>", "Workflow parameters as JSON string", "{}").option("-f, --format <format>", "Output format (json, table)", "table").action(async (workflowName, options) => {
|
|
566
|
-
const client = getClient();
|
|
567
|
-
let params;
|
|
568
|
-
try {
|
|
569
|
-
params = JSON.parse(options.params);
|
|
570
|
-
} catch {
|
|
571
|
-
error("Invalid JSON params");
|
|
572
|
-
info(`Example: --params '{"page_id": "page-123"}'`);
|
|
573
|
-
process.exit(1);
|
|
574
|
-
}
|
|
575
|
-
try {
|
|
576
|
-
info(`Starting workflow: ${workflowName}`);
|
|
577
|
-
const result = await client.workflows.start({
|
|
578
|
-
workflow_name: workflowName,
|
|
579
|
-
params
|
|
580
|
-
});
|
|
581
|
-
if (result.success) {
|
|
582
|
-
success(`Workflow started: ${result.instance_id}`);
|
|
583
|
-
output(result, options.format);
|
|
584
|
-
} else {
|
|
585
|
-
error("Failed to start workflow");
|
|
586
|
-
output(result, options.format);
|
|
587
|
-
process.exit(1);
|
|
588
|
-
}
|
|
589
|
-
} catch (err) {
|
|
590
|
-
handleError2(err);
|
|
591
|
-
}
|
|
592
|
-
});
|
|
593
|
-
workflows.command("status <instance-id>").description("Get workflow status").option("-f, --format <format>", "Output format (json, table)", "table").action(async (instanceId, options) => {
|
|
594
|
-
const client = getClient();
|
|
595
|
-
try {
|
|
596
|
-
const status = await client.workflows.getStatus(instanceId);
|
|
597
|
-
output(status, options.format);
|
|
598
|
-
} catch (err) {
|
|
599
|
-
handleError2(err);
|
|
600
|
-
}
|
|
601
|
-
});
|
|
602
|
-
workflows.command("pause <instance-id>").description("Pause a running workflow").option("-f, --format <format>", "Output format (json, table)", "table").action(async (instanceId, options) => {
|
|
603
|
-
const client = getClient();
|
|
604
|
-
try {
|
|
605
|
-
info(`Pausing workflow: ${instanceId}`);
|
|
606
|
-
const result = await client.workflows.pause(instanceId);
|
|
607
|
-
if (result.success) {
|
|
608
|
-
success(result.message);
|
|
609
|
-
} else {
|
|
610
|
-
error(result.message);
|
|
611
|
-
process.exit(1);
|
|
612
|
-
}
|
|
613
|
-
output(result, options.format);
|
|
614
|
-
} catch (err) {
|
|
615
|
-
handleError2(err);
|
|
616
|
-
}
|
|
617
|
-
});
|
|
618
|
-
workflows.command("resume <instance-id>").description("Resume a paused workflow").option("-f, --format <format>", "Output format (json, table)", "table").action(async (instanceId, options) => {
|
|
619
|
-
const client = getClient();
|
|
620
|
-
try {
|
|
621
|
-
info(`Resuming workflow: ${instanceId}`);
|
|
622
|
-
const result = await client.workflows.resume(instanceId);
|
|
623
|
-
if (result.success) {
|
|
624
|
-
success(result.message);
|
|
625
|
-
} else {
|
|
626
|
-
error(result.message);
|
|
627
|
-
process.exit(1);
|
|
628
|
-
}
|
|
629
|
-
output(result, options.format);
|
|
630
|
-
} catch (err) {
|
|
631
|
-
handleError2(err);
|
|
632
|
-
}
|
|
633
|
-
});
|
|
634
|
-
workflows.command("terminate <instance-id>").description("Terminate a workflow").option("-f, --format <format>", "Output format (json, table)", "table").action(async (instanceId, options) => {
|
|
635
|
-
const client = getClient();
|
|
636
|
-
try {
|
|
637
|
-
info(`Terminating workflow: ${instanceId}`);
|
|
638
|
-
const result = await client.workflows.terminate(instanceId);
|
|
639
|
-
if (result.success) {
|
|
640
|
-
success(result.message);
|
|
641
|
-
} else {
|
|
642
|
-
error(result.message);
|
|
643
|
-
process.exit(1);
|
|
644
|
-
}
|
|
645
|
-
output(result, options.format);
|
|
646
|
-
} catch (err) {
|
|
647
|
-
handleError2(err);
|
|
648
|
-
}
|
|
649
|
-
});
|
|
650
|
-
return workflows;
|
|
651
|
-
}
|
|
652
|
-
function getClient() {
|
|
653
|
-
if (!hasApiKey()) {
|
|
654
|
-
error("API key not configured");
|
|
655
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
656
|
-
info("Or set the LINDO_API_KEY environment variable");
|
|
657
|
-
process.exit(1);
|
|
658
|
-
}
|
|
659
|
-
const config = loadConfig();
|
|
660
|
-
return new lindoai.LindoClient({
|
|
661
|
-
apiKey: config.apiKey,
|
|
662
|
-
baseUrl: config.baseUrl
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
function handleError2(err) {
|
|
666
|
-
if (err instanceof lindoai.AuthenticationError) {
|
|
667
|
-
error("Authentication failed");
|
|
668
|
-
info("Your API key may be invalid or expired");
|
|
669
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
670
|
-
process.exit(1);
|
|
671
|
-
}
|
|
672
|
-
if (err instanceof Error) {
|
|
673
|
-
error(err.message);
|
|
674
|
-
} else {
|
|
675
|
-
error("An unexpected error occurred");
|
|
676
|
-
}
|
|
677
|
-
process.exit(1);
|
|
678
|
-
}
|
|
679
|
-
function createWorkspaceCommand() {
|
|
680
|
-
const workspace = new commander.Command("workspace").description("Workspace operations");
|
|
681
|
-
workspace.command("get").description("Get workspace details").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
682
|
-
const client = getClient2();
|
|
683
|
-
try {
|
|
684
|
-
const response = await client.workspace.get();
|
|
685
|
-
output(response, options.format);
|
|
686
|
-
} catch (err) {
|
|
687
|
-
handleError3(err);
|
|
688
|
-
}
|
|
689
|
-
});
|
|
690
|
-
workspace.command("credits").description("Get workspace credit balance").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
691
|
-
const client = getClient2();
|
|
692
|
-
try {
|
|
693
|
-
const credits = await client.workspace.getCredits();
|
|
694
|
-
output(credits, options.format);
|
|
695
|
-
} catch (err) {
|
|
696
|
-
handleError3(err);
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
workspace.command("client-credits").description("Get credit balance for a specific client").requiredOption("-c, --client <id>", "Client ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
700
|
-
const client = getClient2();
|
|
701
|
-
try {
|
|
702
|
-
const credits = await client.workspace.getClientCredits(options.client);
|
|
703
|
-
output(credits, options.format);
|
|
704
|
-
} catch (err) {
|
|
705
|
-
handleError3(err);
|
|
706
|
-
}
|
|
707
|
-
});
|
|
708
|
-
workspace.command("update").description("Update workspace settings").option("-n, --name <name>", "Workspace name").option("-l, --language <lang>", "Workspace language").option("-w, --webhook <url>", "Webhook URL").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
709
|
-
const client = getClient2();
|
|
710
|
-
try {
|
|
711
|
-
const response = await client.workspace.update({
|
|
712
|
-
workspace_name: options.name,
|
|
713
|
-
workspace_language: options.language,
|
|
714
|
-
webhook_url: options.webhook
|
|
715
|
-
});
|
|
716
|
-
if (response.success) {
|
|
717
|
-
success("Workspace updated");
|
|
718
|
-
}
|
|
719
|
-
output(response, options.format);
|
|
720
|
-
} catch (err) {
|
|
721
|
-
handleError3(err);
|
|
722
|
-
}
|
|
723
|
-
});
|
|
724
|
-
workspace.command("team-add").description("Add a team member to the workspace").requiredOption("-e, --email <email>", "Team member email").option("-r, --role <role>", "Role (Team)", "Team").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
725
|
-
const client = getClient2();
|
|
726
|
-
try {
|
|
727
|
-
const response = await client.workspace.addTeamMember(options.email, options.role);
|
|
728
|
-
if (response.success) {
|
|
729
|
-
success("Team member added");
|
|
730
|
-
}
|
|
731
|
-
output(response, options.format);
|
|
732
|
-
} catch (err) {
|
|
733
|
-
handleError3(err);
|
|
734
|
-
}
|
|
735
|
-
});
|
|
736
|
-
workspace.command("team-remove").description("Remove a team member from the workspace").requiredOption("-m, --member <id>", "Member ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
737
|
-
const client = getClient2();
|
|
738
|
-
try {
|
|
739
|
-
const response = await client.workspace.removeTeamMember(options.member);
|
|
740
|
-
if (response.success) {
|
|
741
|
-
success("Team member removed");
|
|
742
|
-
}
|
|
743
|
-
output(response, options.format);
|
|
744
|
-
} catch (err) {
|
|
745
|
-
handleError3(err);
|
|
746
|
-
}
|
|
747
|
-
});
|
|
748
|
-
workspace.command("integration-add").description("Add an integration to the workspace").requiredOption("-t, --type <type>", "Integration type (e.g., matomo)").requiredOption("-c, --config <json>", "Integration config as JSON").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
749
|
-
const client = getClient2();
|
|
750
|
-
let config;
|
|
751
|
-
try {
|
|
752
|
-
config = JSON.parse(options.config);
|
|
753
|
-
} catch {
|
|
754
|
-
error("Invalid JSON config");
|
|
755
|
-
process.exit(1);
|
|
756
|
-
}
|
|
757
|
-
try {
|
|
758
|
-
const response = await client.workspace.addIntegration({
|
|
759
|
-
integration_type: options.type,
|
|
760
|
-
config
|
|
761
|
-
});
|
|
762
|
-
if (response.success) {
|
|
763
|
-
success("Integration added");
|
|
764
|
-
}
|
|
765
|
-
output(response, options.format);
|
|
766
|
-
} catch (err) {
|
|
767
|
-
handleError3(err);
|
|
768
|
-
}
|
|
769
|
-
});
|
|
770
|
-
workspace.command("integration-remove").description("Remove an integration from the workspace").requiredOption("-t, --type <type>", "Integration type").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
771
|
-
const client = getClient2();
|
|
772
|
-
try {
|
|
773
|
-
const response = await client.workspace.removeIntegration(options.type);
|
|
774
|
-
if (response.success) {
|
|
775
|
-
success("Integration removed");
|
|
776
|
-
}
|
|
777
|
-
output(response, options.format);
|
|
778
|
-
} catch (err) {
|
|
779
|
-
handleError3(err);
|
|
780
|
-
}
|
|
781
|
-
});
|
|
782
|
-
workspace.command("whitelabel").description("Setup or update whitelabel settings").option("-d, --domain <domain>", "Custom domain").option("-s, --subdomain <domain>", "Subdomain domain").option("-e, --email-sender <email>", "Email sender address").option("--enable-register", "Enable client registration").option("--disable-register", "Disable client registration").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
783
|
-
const client = getClient2();
|
|
784
|
-
try {
|
|
785
|
-
const response = await client.workspace.setupWhitelabel({
|
|
786
|
-
domain: options.domain,
|
|
787
|
-
subdomain_domain: options.subdomain,
|
|
788
|
-
email_sender: options.emailSender,
|
|
789
|
-
wl_client_register: options.enableRegister ? true : options.disableRegister ? false : void 0
|
|
790
|
-
});
|
|
791
|
-
if (response.success) {
|
|
792
|
-
success("Whitelabel settings updated");
|
|
793
|
-
}
|
|
794
|
-
output(response, options.format);
|
|
795
|
-
} catch (err) {
|
|
796
|
-
handleError3(err);
|
|
797
|
-
}
|
|
798
|
-
});
|
|
799
|
-
workspace.command("appearance").description("Update workspace appearance settings").option("-p, --primary <color>", "Primary color (hex)").option("-s, --secondary <color>", "Secondary color (hex)").option("-m, --mode <mode>", "Theme mode (light/dark)").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
800
|
-
const client = getClient2();
|
|
801
|
-
try {
|
|
802
|
-
const response = await client.workspace.updateAppearance({
|
|
803
|
-
primary_color: options.primary,
|
|
804
|
-
secondary_color: options.secondary,
|
|
805
|
-
theme_mode: options.mode
|
|
806
|
-
});
|
|
807
|
-
if (response.success) {
|
|
808
|
-
success("Appearance settings updated");
|
|
809
|
-
}
|
|
810
|
-
output(response, options.format);
|
|
811
|
-
} catch (err) {
|
|
812
|
-
handleError3(err);
|
|
813
|
-
}
|
|
814
|
-
});
|
|
815
|
-
return workspace;
|
|
816
|
-
}
|
|
817
|
-
function getClient2() {
|
|
818
|
-
if (!hasApiKey()) {
|
|
819
|
-
error("API key not configured");
|
|
820
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
821
|
-
info("Or set the LINDO_API_KEY environment variable");
|
|
822
|
-
process.exit(1);
|
|
823
|
-
}
|
|
824
|
-
const config = loadConfig();
|
|
825
|
-
return new lindoai.LindoClient({
|
|
826
|
-
apiKey: config.apiKey,
|
|
827
|
-
baseUrl: config.baseUrl
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
function handleError3(err) {
|
|
831
|
-
if (err instanceof lindoai.AuthenticationError) {
|
|
832
|
-
error("Authentication failed");
|
|
833
|
-
info("Your API key may be invalid or expired");
|
|
834
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
835
|
-
process.exit(1);
|
|
836
|
-
}
|
|
837
|
-
if (err instanceof Error) {
|
|
838
|
-
error(err.message);
|
|
839
|
-
} else {
|
|
840
|
-
error("An unexpected error occurred");
|
|
841
|
-
}
|
|
842
|
-
process.exit(1);
|
|
843
|
-
}
|
|
844
|
-
function createAnalyticsCommand() {
|
|
845
|
-
const analytics = new commander.Command("analytics").description("Analytics operations");
|
|
846
|
-
analytics.command("workspace").description("Get workspace analytics").option("--from <date>", "Start date (ISO format)").option("--to <date>", "End date (ISO format)").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
847
|
-
const client = getClient3();
|
|
848
|
-
try {
|
|
849
|
-
const analytics2 = await client.analytics.getWorkspace({
|
|
850
|
-
from: options.from,
|
|
851
|
-
to: options.to
|
|
852
|
-
});
|
|
853
|
-
output(analytics2, options.format);
|
|
854
|
-
} catch (err) {
|
|
855
|
-
handleError4(err);
|
|
856
|
-
}
|
|
857
|
-
});
|
|
858
|
-
analytics.command("website").description("Get website analytics").requiredOption("-w, --website <id>", "Website ID (required)").option("--from <date>", "Start date (ISO format)").option("--to <date>", "End date (ISO format)").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
859
|
-
const client = getClient3();
|
|
860
|
-
try {
|
|
861
|
-
const analytics2 = await client.analytics.getWebsite({
|
|
862
|
-
website_id: options.website,
|
|
863
|
-
from: options.from,
|
|
864
|
-
to: options.to
|
|
865
|
-
});
|
|
866
|
-
output(analytics2, options.format);
|
|
867
|
-
} catch (err) {
|
|
868
|
-
handleError4(err);
|
|
869
|
-
}
|
|
870
|
-
});
|
|
871
|
-
return analytics;
|
|
872
|
-
}
|
|
873
|
-
function getClient3() {
|
|
874
|
-
if (!hasApiKey()) {
|
|
875
|
-
error("API key not configured");
|
|
876
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
877
|
-
info("Or set the LINDO_API_KEY environment variable");
|
|
878
|
-
process.exit(1);
|
|
879
|
-
}
|
|
880
|
-
const config = loadConfig();
|
|
881
|
-
return new lindoai.LindoClient({
|
|
882
|
-
apiKey: config.apiKey,
|
|
883
|
-
baseUrl: config.baseUrl
|
|
884
|
-
});
|
|
885
|
-
}
|
|
886
|
-
function handleError4(err) {
|
|
887
|
-
if (err instanceof lindoai.AuthenticationError) {
|
|
888
|
-
error("Authentication failed");
|
|
889
|
-
info("Your API key may be invalid or expired");
|
|
890
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
891
|
-
process.exit(1);
|
|
892
|
-
}
|
|
893
|
-
if (err instanceof Error) {
|
|
894
|
-
error(err.message);
|
|
895
|
-
} else {
|
|
896
|
-
error("An unexpected error occurred");
|
|
897
|
-
}
|
|
898
|
-
process.exit(1);
|
|
899
|
-
}
|
|
900
|
-
function createClientsCommand() {
|
|
901
|
-
const clients = new commander.Command("clients").description("Client management operations");
|
|
902
|
-
clients.command("list").description("List all workspace clients").option("-p, --page <page>", "Page number", "1").option("-s, --search <search>", "Search term").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
903
|
-
const client = getClient4();
|
|
904
|
-
try {
|
|
905
|
-
const response = await client.clients.list({
|
|
906
|
-
page: parseInt(options.page, 10),
|
|
907
|
-
search: options.search
|
|
908
|
-
});
|
|
909
|
-
if (options.format === "json") {
|
|
910
|
-
output(response, "json");
|
|
911
|
-
} else {
|
|
912
|
-
if (response.clients && response.clients.length > 0) {
|
|
913
|
-
console.log("\nClients:");
|
|
914
|
-
console.log("--------");
|
|
915
|
-
for (const c of response.clients) {
|
|
916
|
-
console.log(` ID: ${c.record_id}`);
|
|
917
|
-
console.log(` Email: ${c.email}`);
|
|
918
|
-
console.log(` Website Limit: ${c.website_limit ?? "N/A"}`);
|
|
919
|
-
console.log(` Suspended: ${c.suspended ?? false}`);
|
|
920
|
-
console.log("");
|
|
921
|
-
}
|
|
922
|
-
console.log(`Total: ${response.total ?? response.clients.length}`);
|
|
923
|
-
} else {
|
|
924
|
-
info("No clients found");
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
} catch (err) {
|
|
928
|
-
handleError5(err);
|
|
929
|
-
}
|
|
930
|
-
});
|
|
931
|
-
clients.command("create").description("Create a new workspace client").requiredOption("-e, --email <email>", "Client email address").option("-l, --limit <limit>", "Website limit", "5").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
932
|
-
const client = getClient4();
|
|
933
|
-
try {
|
|
934
|
-
const response = await client.clients.create({
|
|
935
|
-
email: options.email,
|
|
936
|
-
website_limit: parseInt(options.limit, 10)
|
|
937
|
-
});
|
|
938
|
-
if (response.success && response.client) {
|
|
939
|
-
success(`Client created: ${response.client.record_id}`);
|
|
940
|
-
output(response.client, options.format);
|
|
941
|
-
} else {
|
|
942
|
-
error("Failed to create client");
|
|
943
|
-
if (response.errors) {
|
|
944
|
-
for (const e of response.errors) {
|
|
945
|
-
error(` ${e}`);
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
} catch (err) {
|
|
950
|
-
handleError5(err);
|
|
951
|
-
}
|
|
952
|
-
});
|
|
953
|
-
clients.command("update").description("Update a workspace client").requiredOption("-i, --id <id>", "Client ID").option("-l, --limit <limit>", "Website limit").option("--suspend", "Suspend the client").option("--unsuspend", "Unsuspend the client").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
954
|
-
const client = getClient4();
|
|
955
|
-
try {
|
|
956
|
-
const response = await client.clients.update({
|
|
957
|
-
client_id: options.id,
|
|
958
|
-
website_limit: options.limit ? parseInt(options.limit, 10) : void 0,
|
|
959
|
-
suspended: options.suspend ? true : options.unsuspend ? false : void 0
|
|
960
|
-
});
|
|
961
|
-
if (response.success) {
|
|
962
|
-
success("Client updated");
|
|
963
|
-
if (response.client) {
|
|
964
|
-
output(response.client, options.format);
|
|
965
|
-
}
|
|
966
|
-
} else {
|
|
967
|
-
error("Failed to update client");
|
|
968
|
-
if (response.errors) {
|
|
969
|
-
for (const e of response.errors) {
|
|
970
|
-
error(` ${e}`);
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
} catch (err) {
|
|
975
|
-
handleError5(err);
|
|
976
|
-
}
|
|
977
|
-
});
|
|
978
|
-
clients.command("delete").description("Delete a workspace client").requiredOption("-i, --id <id>", "Client ID").action(async (options) => {
|
|
979
|
-
const client = getClient4();
|
|
980
|
-
try {
|
|
981
|
-
const response = await client.clients.delete(options.id);
|
|
982
|
-
if (response.success) {
|
|
983
|
-
success("Client deleted");
|
|
984
|
-
} else {
|
|
985
|
-
error("Failed to delete client");
|
|
986
|
-
if (response.errors) {
|
|
987
|
-
for (const e of response.errors) {
|
|
988
|
-
error(` ${e}`);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
} catch (err) {
|
|
993
|
-
handleError5(err);
|
|
994
|
-
}
|
|
995
|
-
});
|
|
996
|
-
clients.command("magic-link").description("Create a magic link for client authentication").requiredOption("-e, --email <email>", "Client email address").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
997
|
-
const client = getClient4();
|
|
998
|
-
try {
|
|
999
|
-
const response = await client.clients.createMagicLink(options.email);
|
|
1000
|
-
if (response.success) {
|
|
1001
|
-
success("Magic link created");
|
|
1002
|
-
output(response, options.format);
|
|
1003
|
-
} else {
|
|
1004
|
-
error("Failed to create magic link");
|
|
1005
|
-
if (response.errors) {
|
|
1006
|
-
for (const e of response.errors) {
|
|
1007
|
-
error(` ${e}`);
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
} catch (err) {
|
|
1012
|
-
handleError5(err);
|
|
1013
|
-
}
|
|
1014
|
-
});
|
|
1015
|
-
return clients;
|
|
1016
|
-
}
|
|
1017
|
-
function getClient4() {
|
|
1018
|
-
if (!hasApiKey()) {
|
|
1019
|
-
error("API key not configured");
|
|
1020
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
1021
|
-
info("Or set the LINDO_API_KEY environment variable");
|
|
1022
|
-
process.exit(1);
|
|
1023
|
-
}
|
|
1024
|
-
const config = loadConfig();
|
|
1025
|
-
return new lindoai.LindoClient({
|
|
1026
|
-
apiKey: config.apiKey,
|
|
1027
|
-
baseUrl: config.baseUrl
|
|
1028
|
-
});
|
|
1029
|
-
}
|
|
1030
|
-
function handleError5(err) {
|
|
1031
|
-
if (err instanceof lindoai.AuthenticationError) {
|
|
1032
|
-
error("Authentication failed");
|
|
1033
|
-
info("Your API key may be invalid or expired");
|
|
1034
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
1035
|
-
process.exit(1);
|
|
1036
|
-
}
|
|
1037
|
-
if (err instanceof Error) {
|
|
1038
|
-
error(err.message);
|
|
1039
|
-
} else {
|
|
1040
|
-
error("An unexpected error occurred");
|
|
1041
|
-
}
|
|
1042
|
-
process.exit(1);
|
|
1043
|
-
}
|
|
1044
|
-
function createWebsitesCommand() {
|
|
1045
|
-
const websites = new commander.Command("websites").description("Website management operations");
|
|
1046
|
-
websites.command("list").description("List all workspace websites").option("-p, --page <page>", "Page number", "1").option("-s, --search <search>", "Search term").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1047
|
-
const client = getClient5();
|
|
1048
|
-
try {
|
|
1049
|
-
const response = await client.websites.list({
|
|
1050
|
-
page: parseInt(options.page, 10),
|
|
1051
|
-
search: options.search
|
|
1052
|
-
});
|
|
1053
|
-
if (options.format === "json") {
|
|
1054
|
-
output(response, "json");
|
|
1055
|
-
} else {
|
|
1056
|
-
if (response.websites && response.websites.length > 0) {
|
|
1057
|
-
console.log("\nWebsites:");
|
|
1058
|
-
console.log("---------");
|
|
1059
|
-
for (const w of response.websites) {
|
|
1060
|
-
console.log(` ID: ${w.record_id}`);
|
|
1061
|
-
console.log(` Business Name: ${w.business_name ?? "N/A"}`);
|
|
1062
|
-
console.log(` Preview URL: ${w.preview_url ?? "N/A"}`);
|
|
1063
|
-
console.log(` Activated: ${w.activated ?? false}`);
|
|
1064
|
-
console.log("");
|
|
1065
|
-
}
|
|
1066
|
-
console.log(`Total: ${response.total ?? response.websites.length}`);
|
|
1067
|
-
} else {
|
|
1068
|
-
info("No websites found");
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
} catch (err) {
|
|
1072
|
-
handleError6(err);
|
|
1073
|
-
}
|
|
1074
|
-
});
|
|
1075
|
-
websites.command("get").description("Get website details").requiredOption("-i, --id <id>", "Website ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1076
|
-
const client = getClient5();
|
|
1077
|
-
try {
|
|
1078
|
-
const response = await client.websites.getDetails(options.id);
|
|
1079
|
-
output(response, options.format);
|
|
1080
|
-
} catch (err) {
|
|
1081
|
-
handleError6(err);
|
|
1082
|
-
}
|
|
1083
|
-
});
|
|
1084
|
-
websites.command("update").description("Update a website").requiredOption("-i, --id <id>", "Website ID").option("-n, --name <name>", "Business name").option("--activate", "Activate the website").option("--deactivate", "Deactivate the website").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1085
|
-
const client = getClient5();
|
|
1086
|
-
try {
|
|
1087
|
-
const response = await client.websites.update({
|
|
1088
|
-
website_id: options.id,
|
|
1089
|
-
business_name: options.name,
|
|
1090
|
-
activated: options.activate ? true : options.deactivate ? false : void 0
|
|
1091
|
-
});
|
|
1092
|
-
if (response.success) {
|
|
1093
|
-
success("Website updated");
|
|
1094
|
-
if (response.website) {
|
|
1095
|
-
output(response.website, options.format);
|
|
1096
|
-
}
|
|
1097
|
-
} else {
|
|
1098
|
-
error("Failed to update website");
|
|
1099
|
-
if (response.errors) {
|
|
1100
|
-
for (const e of response.errors) {
|
|
1101
|
-
error(` ${e}`);
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
} catch (err) {
|
|
1106
|
-
handleError6(err);
|
|
1107
|
-
}
|
|
1108
|
-
});
|
|
1109
|
-
websites.command("settings").description("Update website settings").requiredOption("-i, --id <id>", "Website ID").option("-n, --name <name>", "Business name").option("-l, --language <lang>", "Language").option("-d, --description <desc>", "Business description").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1110
|
-
const client = getClient5();
|
|
1111
|
-
try {
|
|
1112
|
-
const response = await client.websites.updateSettings(options.id, {
|
|
1113
|
-
business_name: options.name,
|
|
1114
|
-
language: options.language,
|
|
1115
|
-
business_description: options.description
|
|
1116
|
-
});
|
|
1117
|
-
if (response.success) {
|
|
1118
|
-
success("Website settings updated");
|
|
1119
|
-
}
|
|
1120
|
-
output(response, options.format);
|
|
1121
|
-
} catch (err) {
|
|
1122
|
-
handleError6(err);
|
|
1123
|
-
}
|
|
1124
|
-
});
|
|
1125
|
-
websites.command("delete").description("Delete a website").requiredOption("-i, --id <id>", "Website ID").action(async (options) => {
|
|
1126
|
-
const client = getClient5();
|
|
1127
|
-
try {
|
|
1128
|
-
const response = await client.websites.delete(options.id);
|
|
1129
|
-
if (response.success) {
|
|
1130
|
-
success("Website deleted");
|
|
1131
|
-
} else {
|
|
1132
|
-
error("Failed to delete website");
|
|
1133
|
-
if (response.errors) {
|
|
1134
|
-
for (const e of response.errors) {
|
|
1135
|
-
error(` ${e}`);
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
} catch (err) {
|
|
1140
|
-
handleError6(err);
|
|
1141
|
-
}
|
|
1142
|
-
});
|
|
1143
|
-
websites.command("assign").description("Assign a website to a client").requiredOption("-w, --website <id>", "Website ID").requiredOption("-c, --client <id>", "Client ID").action(async (options) => {
|
|
1144
|
-
const client = getClient5();
|
|
1145
|
-
try {
|
|
1146
|
-
const response = await client.websites.assign({
|
|
1147
|
-
website_id: options.website,
|
|
1148
|
-
client_id: options.client
|
|
1149
|
-
});
|
|
1150
|
-
if (response.success) {
|
|
1151
|
-
success("Website assigned to client");
|
|
1152
|
-
} else {
|
|
1153
|
-
error("Failed to assign website");
|
|
1154
|
-
if (response.errors) {
|
|
1155
|
-
for (const e of response.errors) {
|
|
1156
|
-
error(` ${e}`);
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
} catch (err) {
|
|
1161
|
-
handleError6(err);
|
|
1162
|
-
}
|
|
1163
|
-
});
|
|
1164
|
-
websites.command("domain-add").description("Add a custom domain to a website").requiredOption("-i, --id <id>", "Website ID").requiredOption("-d, --domain <domain>", "Custom domain").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1165
|
-
const client = getClient5();
|
|
1166
|
-
try {
|
|
1167
|
-
const response = await client.websites.addDomain(options.id, options.domain);
|
|
1168
|
-
if (response.success) {
|
|
1169
|
-
success("Domain added");
|
|
1170
|
-
if (response.result?.dns_records) {
|
|
1171
|
-
console.log("\nDNS Records to configure:");
|
|
1172
|
-
for (const record of response.result.dns_records) {
|
|
1173
|
-
console.log(` ${record.record_type} ${record.host} -> ${record.value}`);
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
output(response, options.format);
|
|
1178
|
-
} catch (err) {
|
|
1179
|
-
handleError6(err);
|
|
1180
|
-
}
|
|
1181
|
-
});
|
|
1182
|
-
websites.command("domain-remove").description("Remove a custom domain from a website").requiredOption("-i, --id <id>", "Website ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1183
|
-
const client = getClient5();
|
|
1184
|
-
try {
|
|
1185
|
-
const response = await client.websites.removeDomain(options.id);
|
|
1186
|
-
if (response.success) {
|
|
1187
|
-
success("Domain removed");
|
|
1188
|
-
}
|
|
1189
|
-
output(response, options.format);
|
|
1190
|
-
} catch (err) {
|
|
1191
|
-
handleError6(err);
|
|
1192
|
-
}
|
|
1193
|
-
});
|
|
1194
|
-
websites.command("integration-add").description("Add an integration to a website").requiredOption("-i, --id <id>", "Website ID").requiredOption("-t, --type <type>", "Integration type (e.g., matomo)").requiredOption("-c, --config <json>", "Integration config as JSON").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1195
|
-
const client = getClient5();
|
|
1196
|
-
let config;
|
|
1197
|
-
try {
|
|
1198
|
-
config = JSON.parse(options.config);
|
|
1199
|
-
} catch {
|
|
1200
|
-
error("Invalid JSON config");
|
|
1201
|
-
process.exit(1);
|
|
1202
|
-
}
|
|
1203
|
-
try {
|
|
1204
|
-
const response = await client.websites.addIntegration(options.id, {
|
|
1205
|
-
integration_type: options.type,
|
|
1206
|
-
config
|
|
1207
|
-
});
|
|
1208
|
-
if (response.success) {
|
|
1209
|
-
success("Integration added");
|
|
1210
|
-
}
|
|
1211
|
-
output(response, options.format);
|
|
1212
|
-
} catch (err) {
|
|
1213
|
-
handleError6(err);
|
|
1214
|
-
}
|
|
1215
|
-
});
|
|
1216
|
-
websites.command("integration-remove").description("Remove an integration from a website").requiredOption("-i, --id <id>", "Website ID").requiredOption("-t, --type <type>", "Integration type").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1217
|
-
const client = getClient5();
|
|
1218
|
-
try {
|
|
1219
|
-
const response = await client.websites.removeIntegration(options.id, options.type);
|
|
1220
|
-
if (response.success) {
|
|
1221
|
-
success("Integration removed");
|
|
1222
|
-
}
|
|
1223
|
-
output(response, options.format);
|
|
1224
|
-
} catch (err) {
|
|
1225
|
-
handleError6(err);
|
|
1226
|
-
}
|
|
1227
|
-
});
|
|
1228
|
-
websites.command("team-add").description("Add a team member to a website").requiredOption("-i, --id <id>", "Website ID").requiredOption("-e, --email <email>", "Team member email").requiredOption("-r, --role <role>", "Role (Editor or Commenter)").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1229
|
-
const client = getClient5();
|
|
1230
|
-
try {
|
|
1231
|
-
const response = await client.websites.addTeamMember(options.id, options.email, options.role);
|
|
1232
|
-
if (response.success) {
|
|
1233
|
-
success("Team member added");
|
|
1234
|
-
}
|
|
1235
|
-
output(response, options.format);
|
|
1236
|
-
} catch (err) {
|
|
1237
|
-
handleError6(err);
|
|
1238
|
-
}
|
|
1239
|
-
});
|
|
1240
|
-
websites.command("team-remove").description("Remove a team member from a website").requiredOption("-i, --id <id>", "Website ID").requiredOption("-m, --member <memberId>", "Member ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1241
|
-
const client = getClient5();
|
|
1242
|
-
try {
|
|
1243
|
-
const response = await client.websites.removeTeamMember(options.id, options.member);
|
|
1244
|
-
if (response.success) {
|
|
1245
|
-
success("Team member removed");
|
|
1246
|
-
}
|
|
1247
|
-
output(response, options.format);
|
|
1248
|
-
} catch (err) {
|
|
1249
|
-
handleError6(err);
|
|
1250
|
-
}
|
|
1251
|
-
});
|
|
1252
|
-
return websites;
|
|
1253
|
-
}
|
|
1254
|
-
function getClient5() {
|
|
1255
|
-
if (!hasApiKey()) {
|
|
1256
|
-
error("API key not configured");
|
|
1257
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
1258
|
-
info("Or set the LINDO_API_KEY environment variable");
|
|
1259
|
-
process.exit(1);
|
|
1260
|
-
}
|
|
1261
|
-
const config = loadConfig();
|
|
1262
|
-
return new lindoai.LindoClient({
|
|
1263
|
-
apiKey: config.apiKey,
|
|
1264
|
-
baseUrl: config.baseUrl
|
|
1265
|
-
});
|
|
1266
|
-
}
|
|
1267
|
-
function handleError6(err) {
|
|
1268
|
-
if (err instanceof lindoai.AuthenticationError) {
|
|
1269
|
-
error("Authentication failed");
|
|
1270
|
-
info("Your API key may be invalid or expired");
|
|
1271
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
1272
|
-
process.exit(1);
|
|
1273
|
-
}
|
|
1274
|
-
if (err instanceof Error) {
|
|
1275
|
-
error(err.message);
|
|
1276
|
-
} else {
|
|
1277
|
-
error("An unexpected error occurred");
|
|
1278
|
-
}
|
|
1279
|
-
process.exit(1);
|
|
1280
|
-
}
|
|
1281
|
-
function openBrowser(url) {
|
|
1282
|
-
return new Promise((resolve4) => {
|
|
1283
|
-
const currentPlatform = os2.platform();
|
|
1284
|
-
let command;
|
|
1285
|
-
switch (currentPlatform) {
|
|
1286
|
-
case "darwin":
|
|
1287
|
-
command = `open "${url}"`;
|
|
1288
|
-
break;
|
|
1289
|
-
case "win32":
|
|
1290
|
-
command = `start "" "${url}"`;
|
|
1291
|
-
break;
|
|
1292
|
-
default:
|
|
1293
|
-
command = `xdg-open "${url}"`;
|
|
1294
|
-
break;
|
|
1295
|
-
}
|
|
1296
|
-
child_process.exec(command, (error2) => {
|
|
1297
|
-
if (error2) {
|
|
1298
|
-
resolve4(false);
|
|
1299
|
-
} else {
|
|
1300
|
-
resolve4(true);
|
|
1301
|
-
}
|
|
1302
|
-
});
|
|
1303
|
-
});
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
// src/commands/pages.ts
|
|
1307
|
-
var PID_FILE = path3__namespace.join(os2__namespace.tmpdir(), "lindoai-pages-preview.pid");
|
|
1308
|
-
var PORT_FILE = path3__namespace.join(os2__namespace.tmpdir(), "lindoai-pages-preview.port");
|
|
1309
|
-
function createPagesCommand() {
|
|
1310
|
-
const pages = new commander.Command("pages").description("Page management operations");
|
|
1311
|
-
pages.command("list").description("List all pages for a website").requiredOption("-w, --website <id>", "Website ID").option("-p, --page <page>", "Page number", "1").option("-s, --search <search>", "Search term").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1312
|
-
const client = getClient6();
|
|
1313
|
-
try {
|
|
1314
|
-
const response = await client.pages.list(options.website, {
|
|
1315
|
-
page: parseInt(options.page, 10),
|
|
1316
|
-
search: options.search
|
|
1317
|
-
});
|
|
1318
|
-
if (options.format === "json") {
|
|
1319
|
-
output(response, "json");
|
|
1320
|
-
} else {
|
|
1321
|
-
const result = response.result;
|
|
1322
|
-
if (result?.list && result.list.length > 0) {
|
|
1323
|
-
console.log("\nPages:");
|
|
1324
|
-
console.log("------");
|
|
1325
|
-
for (const p of result.list) {
|
|
1326
|
-
console.log(` ID: ${p.page_id}`);
|
|
1327
|
-
console.log(` Name: ${p.name ?? "N/A"}`);
|
|
1328
|
-
console.log(` Path: ${p.path ?? "N/A"}`);
|
|
1329
|
-
console.log(` Status: ${p.status ?? "N/A"}`);
|
|
1330
|
-
console.log(` Published: ${p.publish_date ? new Date(p.publish_date * 1e3).toISOString() : "No"}`);
|
|
1331
|
-
console.log("");
|
|
1332
|
-
}
|
|
1333
|
-
console.log(`Total: ${result.total ?? result.list.length}`);
|
|
1334
|
-
} else {
|
|
1335
|
-
info("No pages found");
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
} catch (err) {
|
|
1339
|
-
handleError7(err);
|
|
1340
|
-
}
|
|
1341
|
-
});
|
|
1342
|
-
pages.command("get").description("Get details of a specific page").requiredOption("-w, --website <id>", "Website ID").requiredOption("-i, --id <id>", "Page ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1343
|
-
const client = getClient6();
|
|
1344
|
-
try {
|
|
1345
|
-
const response = await client.pages.get(options.website, options.id);
|
|
1346
|
-
if (options.format === "json") {
|
|
1347
|
-
output(response, "json");
|
|
1348
|
-
} else {
|
|
1349
|
-
const result = response.result;
|
|
1350
|
-
if (result) {
|
|
1351
|
-
console.log("\nPage Details:");
|
|
1352
|
-
console.log("-------------");
|
|
1353
|
-
console.log(` ID: ${result.page_id}`);
|
|
1354
|
-
console.log(` Name: ${result.name ?? "N/A"}`);
|
|
1355
|
-
console.log(` Path: ${result.path ?? "N/A"}`);
|
|
1356
|
-
console.log(` Status: ${result.status ?? "N/A"}`);
|
|
1357
|
-
console.log(` Published: ${result.publish_date ? new Date(result.publish_date * 1e3).toISOString() : "No"}`);
|
|
1358
|
-
console.log(` Created: ${result.created_date ?? "N/A"}`);
|
|
1359
|
-
} else {
|
|
1360
|
-
error("Page not found");
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
} catch (err) {
|
|
1364
|
-
handleError7(err);
|
|
1365
|
-
}
|
|
1366
|
-
});
|
|
1367
|
-
pages.command("publish").description("Publish a page").requiredOption("-w, --website <id>", "Website ID").requiredOption("-i, --id <id>", "Page ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1368
|
-
const client = getClient6();
|
|
1369
|
-
try {
|
|
1370
|
-
const response = await client.pages.publish(options.website, options.id);
|
|
1371
|
-
if (options.format === "json") {
|
|
1372
|
-
output(response, "json");
|
|
1373
|
-
} else {
|
|
1374
|
-
if (response.success) {
|
|
1375
|
-
success("Page published successfully");
|
|
1376
|
-
console.log(` Page ID: ${response.result?.page_id}`);
|
|
1377
|
-
console.log(` Published at: ${response.result?.publish_date ? new Date(response.result.publish_date * 1e3).toISOString() : "N/A"}`);
|
|
1378
|
-
} else {
|
|
1379
|
-
error("Failed to publish page");
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
} catch (err) {
|
|
1383
|
-
handleError7(err);
|
|
1384
|
-
}
|
|
1385
|
-
});
|
|
1386
|
-
pages.command("unpublish").description("Unpublish a page").requiredOption("-w, --website <id>", "Website ID").requiredOption("-i, --id <id>", "Page ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1387
|
-
const client = getClient6();
|
|
1388
|
-
try {
|
|
1389
|
-
const response = await client.pages.unpublish(options.website, options.id);
|
|
1390
|
-
if (options.format === "json") {
|
|
1391
|
-
output(response, "json");
|
|
1392
|
-
} else {
|
|
1393
|
-
if (response.success) {
|
|
1394
|
-
success("Page unpublished successfully");
|
|
1395
|
-
console.log(` Page ID: ${response.result?.page_id}`);
|
|
1396
|
-
} else {
|
|
1397
|
-
error("Failed to unpublish page");
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
} catch (err) {
|
|
1401
|
-
handleError7(err);
|
|
1402
|
-
}
|
|
1403
|
-
});
|
|
1404
|
-
pages.command("edit").description("Edit a page with live preview").argument("<website_id>", "Website ID").argument("<page_id>", "Page ID").option("--file <path>", "Output file path", "./page.html").option("--background", "Run preview server in background").action(async (websiteId, pageId, options) => {
|
|
1405
|
-
const client = getClient6();
|
|
1406
|
-
try {
|
|
1407
|
-
info("Fetching page details...");
|
|
1408
|
-
const pageResponse = await client.pages.get(websiteId, pageId);
|
|
1409
|
-
if (!pageResponse.result) {
|
|
1410
|
-
error("Page not found");
|
|
1411
|
-
process.exit(1);
|
|
1412
|
-
}
|
|
1413
|
-
const pagePath = pageResponse.result.path;
|
|
1414
|
-
const websiteResponse = await client.websites.getDetails(websiteId);
|
|
1415
|
-
if (!websiteResponse.result) {
|
|
1416
|
-
error("Website not found");
|
|
1417
|
-
process.exit(1);
|
|
1418
|
-
}
|
|
1419
|
-
const baseUrl = websiteResponse.result.custom_domain || websiteResponse.result.verified_domain || websiteResponse.result.preview_url;
|
|
1420
|
-
if (!baseUrl) {
|
|
1421
|
-
error("Could not determine website URL");
|
|
1422
|
-
process.exit(1);
|
|
1423
|
-
}
|
|
1424
|
-
const pageUrl = `https://${baseUrl}${pagePath}`;
|
|
1425
|
-
info(`Fetching HTML from ${pageUrl}...`);
|
|
1426
|
-
const htmlResponse = await fetch(pageUrl);
|
|
1427
|
-
if (!htmlResponse.ok) {
|
|
1428
|
-
error(`Failed to fetch page HTML: ${htmlResponse.status} ${htmlResponse.statusText}`);
|
|
1429
|
-
info("Make sure the page is published before editing");
|
|
1430
|
-
process.exit(1);
|
|
1431
|
-
}
|
|
1432
|
-
const html = await htmlResponse.text();
|
|
1433
|
-
const outputPath = path3__namespace.resolve(options.file);
|
|
1434
|
-
fs3__namespace.writeFileSync(outputPath, html, "utf-8");
|
|
1435
|
-
success(`HTML saved to ${outputPath}`);
|
|
1436
|
-
await terminateExistingPreviewServer();
|
|
1437
|
-
if (options.background) {
|
|
1438
|
-
await startBackgroundPreviewServer(outputPath);
|
|
1439
|
-
} else {
|
|
1440
|
-
await startForegroundPreviewServer(outputPath);
|
|
1441
|
-
}
|
|
1442
|
-
} catch (err) {
|
|
1443
|
-
handleError7(err);
|
|
1444
|
-
}
|
|
1445
|
-
});
|
|
1446
|
-
pages.command("update").description("Update a page").argument("<website_id>", "Website ID").argument("<page_id>", "Page ID").option("--html-file <path>", "Path to local HTML file to upload").option("-f, --format <format>", "Output format (json, table)", "table").action(async (websiteId, pageId, options) => {
|
|
1447
|
-
const client = getClient6();
|
|
1448
|
-
const config = loadConfig();
|
|
1449
|
-
try {
|
|
1450
|
-
if (!options.htmlFile) {
|
|
1451
|
-
error("--html-file option is required");
|
|
1452
|
-
info("Usage: lindoai pages update <website_id> <page_id> --html-file <path>");
|
|
1453
|
-
process.exit(1);
|
|
1454
|
-
}
|
|
1455
|
-
const htmlPath = path3__namespace.resolve(options.htmlFile);
|
|
1456
|
-
if (!fs3__namespace.existsSync(htmlPath)) {
|
|
1457
|
-
error(`File not found: ${htmlPath}`);
|
|
1458
|
-
process.exit(1);
|
|
1459
|
-
}
|
|
1460
|
-
const html = fs3__namespace.readFileSync(htmlPath, "utf-8");
|
|
1461
|
-
info(`Read ${html.length} bytes from ${htmlPath}`);
|
|
1462
|
-
const pageResponse = await client.pages.get(websiteId, pageId);
|
|
1463
|
-
if (!pageResponse.result) {
|
|
1464
|
-
error("Page not found");
|
|
1465
|
-
process.exit(1);
|
|
1466
|
-
}
|
|
1467
|
-
const pagePath = pageResponse.result.path;
|
|
1468
|
-
info("Updating page...");
|
|
1469
|
-
const apiBaseUrl = config.baseUrl || "https://api.lindo.ai";
|
|
1470
|
-
const updateUrl = `${apiBaseUrl}/v1/workspace/website/${websiteId}/pages/${pageId}/update`;
|
|
1471
|
-
const response = await fetch(updateUrl, {
|
|
1472
|
-
method: "POST",
|
|
1473
|
-
headers: {
|
|
1474
|
-
"Content-Type": "application/json",
|
|
1475
|
-
"Authorization": `Bearer ${config.apiKey}`
|
|
1476
|
-
},
|
|
1477
|
-
body: JSON.stringify({
|
|
1478
|
-
html,
|
|
1479
|
-
path: pagePath
|
|
1480
|
-
})
|
|
1481
|
-
});
|
|
1482
|
-
const result = await response.json();
|
|
1483
|
-
if (options.format === "json") {
|
|
1484
|
-
output(result, "json");
|
|
1485
|
-
} else {
|
|
1486
|
-
if (result.success) {
|
|
1487
|
-
success("Page updated successfully");
|
|
1488
|
-
console.log(` Page ID: ${result.result?.page_id}`);
|
|
1489
|
-
if (result.result?.publish_date) {
|
|
1490
|
-
console.log(` Published at: ${new Date(result.result.publish_date * 1e3).toISOString()}`);
|
|
1491
|
-
}
|
|
1492
|
-
} else {
|
|
1493
|
-
error("Failed to update page");
|
|
1494
|
-
if (result.errors) {
|
|
1495
|
-
for (const err of result.errors) {
|
|
1496
|
-
console.log(` ${err}`);
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
} catch (err) {
|
|
1502
|
-
handleError7(err);
|
|
1503
|
-
}
|
|
1504
|
-
});
|
|
1505
|
-
pages.command("stop-preview").description("Stop the background preview server").action(async () => {
|
|
1506
|
-
try {
|
|
1507
|
-
if (!fs3__namespace.existsSync(PID_FILE)) {
|
|
1508
|
-
info("No preview server is running");
|
|
1509
|
-
return;
|
|
1510
|
-
}
|
|
1511
|
-
const pid = parseInt(fs3__namespace.readFileSync(PID_FILE, "utf-8").trim(), 10);
|
|
1512
|
-
if (isNaN(pid)) {
|
|
1513
|
-
error("Invalid PID file");
|
|
1514
|
-
cleanupPidFiles();
|
|
1515
|
-
return;
|
|
1516
|
-
}
|
|
1517
|
-
try {
|
|
1518
|
-
process.kill(pid, "SIGTERM");
|
|
1519
|
-
success(`Preview server (PID ${pid}) stopped`);
|
|
1520
|
-
} catch (killErr) {
|
|
1521
|
-
if (killErr.code === "ESRCH") {
|
|
1522
|
-
info("Preview server process not found (may have already stopped)");
|
|
1523
|
-
} else {
|
|
1524
|
-
error(`Failed to stop preview server: ${killErr.message}`);
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
cleanupPidFiles();
|
|
1528
|
-
} catch (err) {
|
|
1529
|
-
if (err instanceof Error) {
|
|
1530
|
-
error(err.message);
|
|
1531
|
-
} else {
|
|
1532
|
-
error("An unexpected error occurred");
|
|
1533
|
-
}
|
|
1534
|
-
process.exit(1);
|
|
1535
|
-
}
|
|
1536
|
-
});
|
|
1537
|
-
return pages;
|
|
1538
|
-
}
|
|
1539
|
-
function getClient6() {
|
|
1540
|
-
if (!hasApiKey()) {
|
|
1541
|
-
error("API key not configured");
|
|
1542
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
1543
|
-
info("Or set the LINDO_API_KEY environment variable");
|
|
1544
|
-
process.exit(1);
|
|
1545
|
-
}
|
|
1546
|
-
const config = loadConfig();
|
|
1547
|
-
return new lindoai.LindoClient({
|
|
1548
|
-
apiKey: config.apiKey,
|
|
1549
|
-
baseUrl: config.baseUrl
|
|
1550
|
-
});
|
|
1551
|
-
}
|
|
1552
|
-
function handleError7(err) {
|
|
1553
|
-
if (err instanceof lindoai.AuthenticationError) {
|
|
1554
|
-
error("Authentication failed");
|
|
1555
|
-
info("Your API key may be invalid or expired");
|
|
1556
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
1557
|
-
process.exit(1);
|
|
1558
|
-
}
|
|
1559
|
-
if (err instanceof Error) {
|
|
1560
|
-
error(err.message);
|
|
1561
|
-
} else {
|
|
1562
|
-
error("An unexpected error occurred");
|
|
1563
|
-
}
|
|
1564
|
-
process.exit(1);
|
|
1565
|
-
}
|
|
1566
|
-
function cleanupPidFiles() {
|
|
1567
|
-
try {
|
|
1568
|
-
if (fs3__namespace.existsSync(PID_FILE)) {
|
|
1569
|
-
fs3__namespace.unlinkSync(PID_FILE);
|
|
1570
|
-
}
|
|
1571
|
-
} catch {
|
|
1572
|
-
}
|
|
1573
|
-
try {
|
|
1574
|
-
if (fs3__namespace.existsSync(PORT_FILE)) {
|
|
1575
|
-
fs3__namespace.unlinkSync(PORT_FILE);
|
|
1576
|
-
}
|
|
1577
|
-
} catch {
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
async function terminateExistingPreviewServer() {
|
|
1581
|
-
if (!fs3__namespace.existsSync(PID_FILE)) {
|
|
1582
|
-
return;
|
|
1583
|
-
}
|
|
1584
|
-
try {
|
|
1585
|
-
const pid = parseInt(fs3__namespace.readFileSync(PID_FILE, "utf-8").trim(), 10);
|
|
1586
|
-
if (!isNaN(pid)) {
|
|
1587
|
-
try {
|
|
1588
|
-
process.kill(pid, "SIGTERM");
|
|
1589
|
-
info(`Terminated existing preview server (PID ${pid})`);
|
|
1590
|
-
await new Promise((resolve4) => setTimeout(resolve4, 500));
|
|
1591
|
-
} catch {
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
} catch {
|
|
1595
|
-
}
|
|
1596
|
-
cleanupPidFiles();
|
|
1597
|
-
}
|
|
1598
|
-
async function startBackgroundPreviewServer(filePath) {
|
|
1599
|
-
const absolutePath = path3__namespace.resolve(filePath);
|
|
1600
|
-
const serverCode = `
|
|
32
|
+
</script>`;});var Ge="LINDO_API_KEY",Ye="LINDO_BASE_URL",Je=".lindo",Ve="config.json",ze="https://api.lindo.ai";function ke(){return j__namespace.join(de__namespace.homedir(),Je)}function T(){return j__namespace.join(ke(),Ve)}function ee(){let o=T();try{if(w__namespace.existsSync(o)){let t=w__namespace.readFileSync(o,"utf-8");return JSON.parse(t)}}catch{}return {}}function Ie(o){let t=ke(),n=T();w__namespace.existsSync(t)||w__namespace.mkdirSync(t,{recursive:true}),w__namespace.writeFileSync(n,JSON.stringify(o,null,2),"utf-8");}function h(){let o=ee(),t=process.env[Ge]||o.apiKey,n=process.env[Ye]||o.baseUrl||ze;return {apiKey:t,baseUrl:n}}function Pe(o){let t=ee();t.apiKey=o,Ie(t);}function Fe(o,t){let n=ee();switch(o){case "apiKey":n.apiKey=t;break;case "baseUrl":n.baseUrl=t;break;default:throw new Error(`Unknown configuration key: ${o}`)}Ie(n);}function _e(o){let t=h();switch(o){case "apiKey":return t.apiKey;case "baseUrl":return t.baseUrl;default:return}}function C(){return !!h().apiKey}var $={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",blue:"\x1B[34m",cyan:"\x1B[36m",gray:"\x1B[90m",bold:"\x1B[1m"};function Qe(){return !process.env.NO_COLOR&&process.stdout.isTTY!==false}function E(o,t){return Qe()?`${t}${o}${$.reset}`:o}function d(o){console.log(E(`\u2713 ${o}`,$.green));}function r(o){console.error(E(`\u2717 ${o}`,$.red));}function s(o){console.log(E(`\u2139 ${o}`,$.blue));}function Xe(o){return JSON.stringify(o,null,2)}function Ze(o){if(o==null)return "";if(Array.isArray(o)){if(o.length===0)return "No data";let t=new Set;for(let e of o)typeof e=="object"&&e!==null&&Object.keys(e).forEach(i=>t.add(i));if(t.size===0)return o.map(e=>String(e)).join(`
|
|
33
|
+
`);let n=Array.from(t);return et(n,o)}if(typeof o=="object"){let n=Object.entries(o);if(n.length===0)return "No data";let e=Math.max(...n.map(([i])=>i.length));return n.map(([i,a])=>{let l=i.padEnd(e),m=te(a);return `${E(l,$.cyan)} ${m}`}).join(`
|
|
34
|
+
`)}return String(o)}function et(o,t){let n={};for(let l of o)n[l]=l.length;for(let l of t)if(typeof l=="object"&&l!==null){let m=l;for(let p of o){let b=te(m[p]);n[p]=Math.max(n[p],b.length);}}let e=o.map(l=>E(l.padEnd(n[l]),$.bold)).join(" "),i=o.map(l=>"-".repeat(n[l])).join(" "),a=t.map(l=>{if(typeof l=="object"&&l!==null){let m=l;return o.map(p=>te(m[p]).padEnd(n[p])).join(" ")}return String(l)});return [e,i,...a].join(`
|
|
35
|
+
`)}function te(o){return o==null?E("-",$.gray):typeof o=="boolean"?o?E("true",$.green):E("false",$.red):typeof o=="number"?String(o):typeof o=="object"?Array.isArray(o)?`[${o.length} items]`:JSON.stringify(o):String(o)}function c(o,t){console.log(t==="json"?Xe(o):Ze(o));}var Ae=["apiKey","baseUrl"];function ne(){let o=new commander.Command("config").description("Manage CLI configuration");return o.command("set <key> <value>").description("Set a configuration value").action((t,n)=>{Ae.includes(t)||(r(`Invalid configuration key: ${t}`),s(`Valid keys: ${Ae.join(", ")}`),process.exit(1));try{Fe(t,n),d(`Configuration saved: ${t}`),s(`Config file: ${T()}`);}catch(e){r(`Failed to save configuration: ${e instanceof Error?e.message:String(e)}`),process.exit(1);}}),o.command("get <key>").description("Get a configuration value").option("-f, --format <format>","Output format (json, table)","table").action((t,n)=>{let e=_e(t);if(e===void 0){n.format==="json"?c({key:t,value:null},n.format):s(`Configuration key '${t}' is not set`);return}if(n.format==="json")c({key:t,value:e},n.format);else {let i=t==="apiKey"?oe(e):e;console.log(`${t}: ${i}`);}}),o.command("list").description("List all configuration values").option("-f, --format <format>","Output format (json, table)","table").action(t=>{let n=h(),e={apiKey:n.apiKey?oe(n.apiKey):"(not set)",baseUrl:n.baseUrl,configFile:T()};t.format==="json"?c({apiKey:n.apiKey?oe(n.apiKey):null,baseUrl:n.baseUrl,configFile:T()},t.format):c(e,t.format);}),o.command("path").description("Show the config file path").action(()=>{console.log(T());}),o}function oe(o){return o.length<=8?"*".repeat(o.length):`${o.slice(0,4)}${"*".repeat(o.length-8)}${o.slice(-4)}`}function ie(){let o=new commander.Command("agents").description("Run AI agents");return o.command("run <agent-id>").description("Run an AI agent").option("-i, --input <json>","Input data as JSON string","{}").option("-s, --stream","Stream the response",false).option("-f, --format <format>","Output format (json, table)","table").action(async(t,n)=>{C()||(r("API key not configured"),s("Run: lindo config set apiKey <your-api-key>"),s("Or set the LINDO_API_KEY environment variable"),process.exit(1));let e=h(),i=new lindoai.LindoClient({apiKey:e.apiKey,baseUrl:e.baseUrl}),a;try{a=JSON.parse(n.input);}catch{r("Invalid JSON input"),s(`Example: --input '{"prompt": "Hello!"}'`),process.exit(1);}try{s(`Running agent: ${t}`);let l=await i.agents.run({agent_id:t,input:a,stream:n.stream});l.success?(d("Agent run completed"),c(l,n.format)):(r(`Agent run failed: ${l.error||"Unknown error"}`),c(l,n.format),process.exit(1));}catch(l){rt(l);}}),o}function rt(o){o instanceof lindoai.AuthenticationError&&(r("Authentication failed"),s("Your API key may be invalid or expired"),s("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?r(o.message):r("An unexpected error occurred"),process.exit(1);}function re(){let o=new commander.Command("workflows").description("Manage workflows");return o.command("list").description("List workflow logs").option("-n, --name <name>","Filter by workflow name").option("-s, --status <status>","Filter by status").option("-w, --website <id>","Filter by website ID").option("-c, --client <id>","Filter by client ID").option("-l, --limit <number>","Maximum number of results","50").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=M();try{let e=await n.workflows.list({workflow_name:t.name,status:t.status,website_id:t.website,client_id:t.client,limit:parseInt(t.limit)});e.success?c(e.data,t.format):(r("Failed to list workflows"),process.exit(1));}catch(e){B(e);}}),o.command("start <workflow-name>").description("Start a workflow").option("-p, --params <json>","Workflow parameters as JSON string","{}").option("-f, --format <format>","Output format (json, table)","table").action(async(t,n)=>{let e=M(),i;try{i=JSON.parse(n.params);}catch{r("Invalid JSON params"),s(`Example: --params '{"page_id": "page-123"}'`),process.exit(1);}try{s(`Starting workflow: ${t}`);let a=await e.workflows.start({workflow_name:t,params:i});a.success?(d(`Workflow started: ${a.instance_id}`),c(a,n.format)):(r("Failed to start workflow"),c(a,n.format),process.exit(1));}catch(a){B(a);}}),o.command("status <instance-id>").description("Get workflow status").option("-f, --format <format>","Output format (json, table)","table").action(async(t,n)=>{let e=M();try{let i=await e.workflows.getStatus(t);c(i,n.format);}catch(i){B(i);}}),o.command("pause <instance-id>").description("Pause a running workflow").option("-f, --format <format>","Output format (json, table)","table").action(async(t,n)=>{let e=M();try{s(`Pausing workflow: ${t}`);let i=await e.workflows.pause(t);i.success?d(i.message):(r(i.message),process.exit(1)),c(i,n.format);}catch(i){B(i);}}),o.command("resume <instance-id>").description("Resume a paused workflow").option("-f, --format <format>","Output format (json, table)","table").action(async(t,n)=>{let e=M();try{s(`Resuming workflow: ${t}`);let i=await e.workflows.resume(t);i.success?d(i.message):(r(i.message),process.exit(1)),c(i,n.format);}catch(i){B(i);}}),o.command("terminate <instance-id>").description("Terminate a workflow").option("-f, --format <format>","Output format (json, table)","table").action(async(t,n)=>{let e=M();try{s(`Terminating workflow: ${t}`);let i=await e.workflows.terminate(t);i.success?d(i.message):(r(i.message),process.exit(1)),c(i,n.format);}catch(i){B(i);}}),o}function M(){C()||(r("API key not configured"),s("Run: lindo config set apiKey <your-api-key>"),s("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=h();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function B(o){o instanceof lindoai.AuthenticationError&&(r("Authentication failed"),s("Your API key may be invalid or expired"),s("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?r(o.message):r("An unexpected error occurred"),process.exit(1);}function se(){let o=new commander.Command("workspace").description("Workspace operations");return o.command("get").description("Get workspace details").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=I();try{let e=await n.workspace.get();c(e,t.format);}catch(e){P(e);}}),o.command("credits").description("Get workspace credit balance").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=I();try{let e=await n.workspace.getCredits();c(e,t.format);}catch(e){P(e);}}),o.command("client-credits").description("Get credit balance for a specific client").requiredOption("-c, --client <id>","Client ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=I();try{let e=await n.workspace.getClientCredits(t.client);c(e,t.format);}catch(e){P(e);}}),o.command("update").description("Update workspace settings").option("-n, --name <name>","Workspace name").option("-l, --language <lang>","Workspace language").option("-w, --webhook <url>","Webhook URL").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=I();try{let e=await n.workspace.update({workspace_name:t.name,workspace_language:t.language,webhook_url:t.webhook});e.success&&d("Workspace updated"),c(e,t.format);}catch(e){P(e);}}),o.command("team-add").description("Add a team member to the workspace").requiredOption("-e, --email <email>","Team member email").option("-r, --role <role>","Role (Team)","Team").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=I();try{let e=await n.workspace.addTeamMember(t.email,t.role);e.success&&d("Team member added"),c(e,t.format);}catch(e){P(e);}}),o.command("team-remove").description("Remove a team member from the workspace").requiredOption("-m, --member <id>","Member ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=I();try{let e=await n.workspace.removeTeamMember(t.member);e.success&&d("Team member removed"),c(e,t.format);}catch(e){P(e);}}),o.command("integration-add").description("Add an integration to the workspace").requiredOption("-t, --type <type>","Integration type (e.g., matomo)").requiredOption("-c, --config <json>","Integration config as JSON").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=I(),e;try{e=JSON.parse(t.config);}catch{r("Invalid JSON config"),process.exit(1);}try{let i=await n.workspace.addIntegration({integration_type:t.type,config:e});i.success&&d("Integration added"),c(i,t.format);}catch(i){P(i);}}),o.command("integration-remove").description("Remove an integration from the workspace").requiredOption("-t, --type <type>","Integration type").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=I();try{let e=await n.workspace.removeIntegration(t.type);e.success&&d("Integration removed"),c(e,t.format);}catch(e){P(e);}}),o.command("whitelabel").description("Setup or update whitelabel settings").option("-d, --domain <domain>","Custom domain").option("-s, --subdomain <domain>","Subdomain domain").option("-e, --email-sender <email>","Email sender address").option("--enable-register","Enable client registration").option("--disable-register","Disable client registration").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=I();try{let e=await n.workspace.setupWhitelabel({domain:t.domain,subdomain_domain:t.subdomain,email_sender:t.emailSender,wl_client_register:t.enableRegister?!0:t.disableRegister?!1:void 0});e.success&&d("Whitelabel settings updated"),c(e,t.format);}catch(e){P(e);}}),o.command("appearance").description("Update workspace appearance settings").option("-p, --primary <color>","Primary color (hex)").option("-s, --secondary <color>","Secondary color (hex)").option("-m, --mode <mode>","Theme mode (light/dark)").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=I();try{let e=await n.workspace.updateAppearance({primary_color:t.primary,secondary_color:t.secondary,theme_mode:t.mode});e.success&&d("Appearance settings updated"),c(e,t.format);}catch(e){P(e);}}),o}function I(){C()||(r("API key not configured"),s("Run: lindo config set apiKey <your-api-key>"),s("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=h();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function P(o){o instanceof lindoai.AuthenticationError&&(r("Authentication failed"),s("Your API key may be invalid or expired"),s("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?r(o.message):r("An unexpected error occurred"),process.exit(1);}function ae(){let o=new commander.Command("analytics").description("Analytics operations");return o.command("workspace").description("Get workspace analytics").option("--from <date>","Start date (ISO format)").option("--to <date>","End date (ISO format)").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=Te();try{let e=await n.analytics.getWorkspace({from:t.from,to:t.to});c(e,t.format);}catch(e){$e(e);}}),o.command("website").description("Get website analytics").requiredOption("-w, --website <id>","Website ID (required)").option("--from <date>","Start date (ISO format)").option("--to <date>","End date (ISO format)").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=Te();try{let e=await n.analytics.getWebsite({website_id:t.website,from:t.from,to:t.to});c(e,t.format);}catch(e){$e(e);}}),o}function Te(){C()||(r("API key not configured"),s("Run: lindo config set apiKey <your-api-key>"),s("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=h();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function $e(o){o instanceof lindoai.AuthenticationError&&(r("Authentication failed"),s("Your API key may be invalid or expired"),s("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?r(o.message):r("An unexpected error occurred"),process.exit(1);}function ce(){let o=new commander.Command("clients").description("Client management operations");return o.command("list").description("List all workspace clients").option("-p, --page <page>","Page number","1").option("-s, --search <search>","Search term").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=Y();try{let e=await n.clients.list({page:parseInt(t.page,10),search:t.search});if(t.format==="json")c(e,"json");else if(e.clients&&e.clients.length>0){console.log(`
|
|
36
|
+
Clients:`),console.log("--------");for(let i of e.clients)console.log(` ID: ${i.record_id}`),console.log(` Email: ${i.email}`),console.log(` Website Limit: ${i.website_limit??"N/A"}`),console.log(` Suspended: ${i.suspended??!1}`),console.log("");console.log(`Total: ${e.total??e.clients.length}`);}else s("No clients found");}catch(e){J(e);}}),o.command("create").description("Create a new workspace client").requiredOption("-e, --email <email>","Client email address").option("-l, --limit <limit>","Website limit","5").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=Y();try{let e=await n.clients.create({email:t.email,website_limit:parseInt(t.limit,10)});if(e.success&&e.client)d(`Client created: ${e.client.record_id}`),c(e.client,t.format);else if(r("Failed to create client"),e.errors)for(let i of e.errors)r(` ${i}`);}catch(e){J(e);}}),o.command("update").description("Update a workspace client").requiredOption("-i, --id <id>","Client ID").option("-l, --limit <limit>","Website limit").option("--suspend","Suspend the client").option("--unsuspend","Unsuspend the client").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=Y();try{let e=await n.clients.update({client_id:t.id,website_limit:t.limit?parseInt(t.limit,10):void 0,suspended:t.suspend?!0:t.unsuspend?!1:void 0});if(e.success)d("Client updated"),e.client&&c(e.client,t.format);else if(r("Failed to update client"),e.errors)for(let i of e.errors)r(` ${i}`);}catch(e){J(e);}}),o.command("delete").description("Delete a workspace client").requiredOption("-i, --id <id>","Client ID").action(async t=>{let n=Y();try{let e=await n.clients.delete(t.id);if(e.success)d("Client deleted");else if(r("Failed to delete client"),e.errors)for(let i of e.errors)r(` ${i}`);}catch(e){J(e);}}),o.command("magic-link").description("Create a magic link for client authentication").requiredOption("-e, --email <email>","Client email address").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=Y();try{let e=await n.clients.createMagicLink(t.email);if(e.success)d("Magic link created"),c(e,t.format);else if(r("Failed to create magic link"),e.errors)for(let i of e.errors)r(` ${i}`);}catch(e){J(e);}}),o}function Y(){C()||(r("API key not configured"),s("Run: lindo config set apiKey <your-api-key>"),s("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=h();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function J(o){o instanceof lindoai.AuthenticationError&&(r("Authentication failed"),s("Your API key may be invalid or expired"),s("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?r(o.message):r("An unexpected error occurred"),process.exit(1);}function le(){let o=new commander.Command("websites").description("Website management operations");return o.command("list").description("List all workspace websites").option("-p, --page <page>","Page number","1").option("-s, --search <search>","Search term").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=O();try{let e=await n.websites.list({page:parseInt(t.page,10),search:t.search});if(t.format==="json")c(e,"json");else {let i=e.result?.list??[];if(i.length>0){console.log(`
|
|
37
|
+
Websites:`),console.log("---------");for(let a of i)console.log(` ID: ${a.website_id}`),console.log(` Name: ${a.website_name??"N/A"}`),console.log(` Domain: ${a.domain??"N/A"}`),console.log(` Activated: ${a.activated??!1}`),console.log("");console.log(`Total: ${e.result?.total??i.length}`);}else s("No websites found");}}catch(e){x(e);}}),o.command("get").description("Get website details").requiredOption("-i, --id <id>","Website ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=O();try{let e=await n.websites.getDetails(t.id);c(e,t.format);}catch(e){x(e);}}),o.command("update").description("Update a website").requiredOption("-i, --id <id>","Website ID").option("-n, --name <name>","Business name").option("--activate","Activate the website").option("--deactivate","Deactivate the website").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=O();try{let e=await n.websites.update({website_id:t.id,business_name:t.name,activated:t.activate?!0:t.deactivate?!1:void 0});if(e.success)d("Website updated"),e.website&&c(e.website,t.format);else if(r("Failed to update website"),e.errors)for(let i of e.errors)r(` ${i}`);}catch(e){x(e);}}),o.command("settings").description("Update website settings").requiredOption("-i, --id <id>","Website ID").option("-n, --name <name>","Business name").option("-l, --language <lang>","Language").option("-d, --description <desc>","Business description").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=O();try{let e=await n.websites.updateSettings(t.id,{business_name:t.name,language:t.language,business_description:t.description});e.success&&d("Website settings updated"),c(e,t.format);}catch(e){x(e);}}),o.command("delete").description("Delete a website").requiredOption("-i, --id <id>","Website ID").action(async t=>{let n=O();try{let e=await n.websites.delete(t.id);if(e.success)d("Website deleted");else if(r("Failed to delete website"),e.errors)for(let i of e.errors)r(` ${i}`);}catch(e){x(e);}}),o.command("assign").description("Assign a website to a client").requiredOption("-w, --website <id>","Website ID").requiredOption("-c, --client <id>","Client ID").action(async t=>{let n=O();try{let e=await n.websites.assign({website_id:t.website,client_id:t.client});if(e.success)d("Website assigned to client");else if(r("Failed to assign website"),e.errors)for(let i of e.errors)r(` ${i}`);}catch(e){x(e);}}),o.command("domain-add").description("Add a custom domain to a website").requiredOption("-i, --id <id>","Website ID").requiredOption("-d, --domain <domain>","Custom domain").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=O();try{let e=await n.websites.addDomain(t.id,t.domain);if(e.success&&(d("Domain added"),e.result?.dns_records)){console.log(`
|
|
38
|
+
DNS Records to configure:`);for(let i of e.result.dns_records)console.log(` ${i.record_type} ${i.host} -> ${i.value}`);}c(e,t.format);}catch(e){x(e);}}),o.command("domain-remove").description("Remove a custom domain from a website").requiredOption("-i, --id <id>","Website ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=O();try{let e=await n.websites.removeDomain(t.id);e.success&&d("Domain removed"),c(e,t.format);}catch(e){x(e);}}),o.command("integration-add").description("Add an integration to a website").requiredOption("-i, --id <id>","Website ID").requiredOption("-t, --type <type>","Integration type (e.g., matomo)").requiredOption("-c, --config <json>","Integration config as JSON").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=O(),e;try{e=JSON.parse(t.config);}catch{r("Invalid JSON config"),process.exit(1);}try{let i=await n.websites.addIntegration(t.id,{integration_type:t.type,config:e});i.success&&d("Integration added"),c(i,t.format);}catch(i){x(i);}}),o.command("integration-remove").description("Remove an integration from a website").requiredOption("-i, --id <id>","Website ID").requiredOption("-t, --type <type>","Integration type").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=O();try{let e=await n.websites.removeIntegration(t.id,t.type);e.success&&d("Integration removed"),c(e,t.format);}catch(e){x(e);}}),o.command("team-add").description("Add a team member to a website").requiredOption("-i, --id <id>","Website ID").requiredOption("-e, --email <email>","Team member email").requiredOption("-r, --role <role>","Role (Editor or Commenter)").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=O();try{let e=await n.websites.addTeamMember(t.id,t.email,t.role);e.success&&d("Team member added"),c(e,t.format);}catch(e){x(e);}}),o.command("team-remove").description("Remove a team member from a website").requiredOption("-i, --id <id>","Website ID").requiredOption("-m, --member <memberId>","Member ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=O();try{let e=await n.websites.removeTeamMember(t.id,t.member);e.success&&d("Team member removed"),c(e,t.format);}catch(e){x(e);}}),o}function O(){C()||(r("API key not configured"),s("Run: lindo config set apiKey <your-api-key>"),s("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=h();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function x(o){o instanceof lindoai.AuthenticationError&&(r("Authentication failed"),s("Your API key may be invalid or expired"),s("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?r(o.message):r("An unexpected error occurred"),process.exit(1);}function L(o){return new Promise(t=>{let n=de.platform(),e;switch(n){case "darwin":e=`open "${o}"`;break;case "win32":e=`start "" "${o}"`;break;default:e=`xdg-open "${o}"`;break}child_process.exec(e,i=>{t(!i);});})}var N=j__namespace.join(de__namespace.tmpdir(),"lindoai-pages-preview.pid"),z=j__namespace.join(de__namespace.tmpdir(),"lindoai-pages-preview.port");function he(){let o=new commander.Command("pages").description("Page management operations");return o.command("list").description("List all pages for a website").requiredOption("-w, --website <id>","Website ID").option("-p, --page <page>","Page number","1").option("-s, --search <search>","Search term").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=D();try{let e=await n.pages.list(t.website,{page:parseInt(t.page,10),search:t.search});if(t.format==="json")c(e,"json");else {let i=e.result;if(i?.list&&i.list.length>0){console.log(`
|
|
39
|
+
Pages:`),console.log("------");for(let a of i.list)console.log(` ID: ${a.page_id}`),console.log(` Name: ${a.name??"N/A"}`),console.log(` Path: ${a.path??"N/A"}`),console.log(` Status: ${a.status??"N/A"}`),console.log(` Published: ${a.publish_date?new Date(a.publish_date*1e3).toISOString():"No"}`),console.log("");console.log(`Total: ${i.total??i.list.length}`);}else s("No pages found");}}catch(e){R(e);}}),o.command("get").description("Get details of a specific page").requiredOption("-w, --website <id>","Website ID").requiredOption("-i, --id <id>","Page ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=D();try{let e=await n.pages.get(t.website,t.id);if(t.format==="json")c(e,"json");else {let i=e.result;i?(console.log(`
|
|
40
|
+
Page Details:`),console.log("-------------"),console.log(` ID: ${i.page_id}`),console.log(` Name: ${i.name??"N/A"}`),console.log(` Path: ${i.path??"N/A"}`),console.log(` Status: ${i.status??"N/A"}`),console.log(` Published: ${i.publish_date?new Date(i.publish_date*1e3).toISOString():"No"}`),console.log(` Created: ${i.created_date??"N/A"}`)):r("Page not found");}}catch(e){R(e);}}),o.command("publish").description("Publish a page").requiredOption("-w, --website <id>","Website ID").requiredOption("-i, --id <id>","Page ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=D();try{let e=await n.pages.publish(t.website,t.id);t.format==="json"?c(e,"json"):e.success?(d("Page published successfully"),console.log(` Page ID: ${e.result?.page_id}`),console.log(` Published at: ${e.result?.publish_date?new Date(e.result.publish_date*1e3).toISOString():"N/A"}`)):r("Failed to publish page");}catch(e){R(e);}}),o.command("unpublish").description("Unpublish a page").requiredOption("-w, --website <id>","Website ID").requiredOption("-i, --id <id>","Page ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=D();try{let e=await n.pages.unpublish(t.website,t.id);t.format==="json"?c(e,"json"):e.success?(d("Page unpublished successfully"),console.log(` Page ID: ${e.result?.page_id}`)):r("Failed to unpublish page");}catch(e){R(e);}}),o.command("delete").description("Delete a page").requiredOption("-w, --website <id>","Website ID").requiredOption("-i, --id <id>","Page ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=D();try{let e=await n.pages.delete(t.website,t.id);if(t.format==="json")c(e,"json");else if(e.success){if(d("Page deleted successfully"),console.log(` Page ID: ${e.result?.page_id}`),e.result?.warnings&&e.result.warnings.length>0){console.log(" Warnings:");for(let i of e.result.warnings)console.log(` - ${i}`);}}else r("Failed to delete page");}catch(e){R(e);}}),o.command("edit").description("Edit a page with live preview").argument("<website_id>","Website ID").argument("<page_id>","Page ID").option("--file <path>","Output file path","./page.html").option("--background","Run preview server in background").action(async(t,n,e)=>{let i=D();try{s("Fetching page details...");let a=await i.pages.get(t,n);a.result||(r("Page not found"),process.exit(1));let l=a.result.path,m=await i.websites.getDetails(t);m.result||(r("Website not found"),process.exit(1));let p=m.result.custom_domain||m.result.verified_domain||m.result.preview_url;p||(r("Could not determine website URL"),process.exit(1));let b=`https://${p}${l}`;s(`Fetching HTML from ${b}...`);let g=await fetch(b);g.ok||(r(`Failed to fetch page HTML: ${g.status} ${g.statusText}`),s("Make sure the page is published before editing"),process.exit(1));let v=await g.text(),u=j__namespace.resolve(e.file);w__namespace.writeFileSync(u,v,"utf-8"),d(`HTML saved to ${u}`),await Ft(),e.background?await _t(u):await At(u);}catch(a){R(a);}}),o.command("update").description("Update a page").argument("<website_id>","Website ID").argument("<page_id>","Page ID").option("--html-file <path>","Path to local HTML file to upload").option("-f, --format <format>","Output format (json, table)","table").action(async(t,n,e)=>{let i=D(),a=h();try{e.htmlFile||(r("--html-file option is required"),s("Usage: lindoai pages update <website_id> <page_id> --html-file <path>"),process.exit(1));let l=j__namespace.resolve(e.htmlFile);w__namespace.existsSync(l)||(r(`File not found: ${l}`),process.exit(1));let m=w__namespace.readFileSync(l,"utf-8");s(`Read ${m.length} bytes from ${l}`);let p=await i.pages.get(t,n);p.result||(r("Page not found"),process.exit(1));let b=p.result.path;s("Updating page...");let v=`${a.baseUrl||"https://api.lindo.ai"}/v1/workspace/website/${t}/pages/${n}/update`,f=await(await fetch(v,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.apiKey}`},body:JSON.stringify({html:m,path:b})})).json();if(e.format==="json")c(f,"json");else if(f.success)d("Page updated successfully"),console.log(` Page ID: ${f.result?.page_id}`),f.result?.publish_date&&console.log(` Published at: ${new Date(f.result.publish_date*1e3).toISOString()}`);else if(r("Failed to update page"),f.errors)for(let S of f.errors)console.log(` ${S}`);}catch(l){R(l);}}),o.command("stop-preview").description("Stop the background preview server").action(async()=>{try{if(!w__namespace.existsSync(N)){s("No preview server is running");return}let t=parseInt(w__namespace.readFileSync(N,"utf-8").trim(),10);if(isNaN(t)){r("Invalid PID file"),fe();return}try{process.kill(t,"SIGTERM"),d(`Preview server (PID ${t}) stopped`);}catch(n){n.code==="ESRCH"?s("Preview server process not found (may have already stopped)"):r(`Failed to stop preview server: ${n.message}`);}fe();}catch(t){t instanceof Error?r(t.message):r("An unexpected error occurred"),process.exit(1);}}),o}function D(){C()||(r("API key not configured"),s("Run: lindo config set apiKey <your-api-key>"),s("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=h();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function R(o){o instanceof lindoai.AuthenticationError&&(r("Authentication failed"),s("Your API key may be invalid or expired"),s("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?r(o.message):r("An unexpected error occurred"),process.exit(1);}function fe(){try{w__namespace.existsSync(N)&&w__namespace.unlinkSync(N);}catch{}try{w__namespace.existsSync(z)&&w__namespace.unlinkSync(z);}catch{}}async function Ft(){if(w__namespace.existsSync(N)){try{let o=parseInt(w__namespace.readFileSync(N,"utf-8").trim(),10);if(!isNaN(o))try{process.kill(o,"SIGTERM"),s(`Terminated existing preview server (PID ${o})`),await new Promise(t=>setTimeout(t,500));}catch{}}catch{}fe();}}async function _t(o){let t=j__namespace.resolve(o),n=`
|
|
1601
41
|
const http = require('node:http');
|
|
1602
42
|
const fs = require('node:fs');
|
|
1603
43
|
const path = require('node:path');
|
|
@@ -1617,7 +57,7 @@ async function startBackgroundPreviewServer(filePath) {
|
|
|
1617
57
|
})();
|
|
1618
58
|
</script>\`;
|
|
1619
59
|
|
|
1620
|
-
const filePath = ${JSON.stringify(
|
|
60
|
+
const filePath = ${JSON.stringify(t)};
|
|
1621
61
|
const pidFile = path.join(os.tmpdir(), 'lindoai-pages-preview.pid');
|
|
1622
62
|
const portFile = path.join(os.tmpdir(), 'lindoai-pages-preview.port');
|
|
1623
63
|
const sseClients = new Set();
|
|
@@ -1717,350 +157,9 @@ async function startBackgroundPreviewServer(filePath) {
|
|
|
1717
157
|
});
|
|
1718
158
|
} catch {}
|
|
1719
159
|
});
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
stdio: "ignore"
|
|
1724
|
-
});
|
|
1725
|
-
child.unref();
|
|
1726
|
-
let port = null;
|
|
1727
|
-
for (let i = 0; i < 50; i++) {
|
|
1728
|
-
await new Promise((resolve4) => setTimeout(resolve4, 100));
|
|
1729
|
-
if (fs3__namespace.existsSync(PORT_FILE)) {
|
|
1730
|
-
try {
|
|
1731
|
-
port = parseInt(fs3__namespace.readFileSync(PORT_FILE, "utf-8").trim(), 10);
|
|
1732
|
-
if (!isNaN(port)) break;
|
|
1733
|
-
} catch {
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
if (!port) {
|
|
1738
|
-
error("Failed to start preview server");
|
|
1739
|
-
process.exit(1);
|
|
1740
|
-
}
|
|
1741
|
-
const previewUrl = `http://127.0.0.1:${port}/`;
|
|
1742
|
-
const browserOpened = await openBrowser(previewUrl);
|
|
1743
|
-
if (!browserOpened) {
|
|
1744
|
-
info(`Could not open browser. Visit: ${previewUrl}`);
|
|
1745
|
-
}
|
|
1746
|
-
success("Preview server started in background");
|
|
1747
|
-
console.log(` URL: ${previewUrl}`);
|
|
1748
|
-
console.log(` PID: ${child.pid}`);
|
|
1749
|
-
console.log("");
|
|
1750
|
-
info("To update the page: lindoai pages update <website_id> <page_id> --html-file <path>");
|
|
1751
|
-
info("To stop the server: lindoai pages stop-preview");
|
|
1752
|
-
}
|
|
1753
|
-
async function startForegroundPreviewServer(filePath) {
|
|
1754
|
-
const { startLivePreviewServer: startLivePreviewServer2 } = await Promise.resolve().then(() => (init_live_preview_server(), live_preview_server_exports));
|
|
1755
|
-
info("Starting preview server...");
|
|
1756
|
-
const port = await startLivePreviewServer2(filePath);
|
|
1757
|
-
const previewUrl = `http://127.0.0.1:${port}/`;
|
|
1758
|
-
fs3__namespace.writeFileSync(PID_FILE, process.pid.toString(), "utf-8");
|
|
1759
|
-
fs3__namespace.writeFileSync(PORT_FILE, port.toString(), "utf-8");
|
|
1760
|
-
const browserOpened = await openBrowser(previewUrl);
|
|
1761
|
-
if (!browserOpened) {
|
|
1762
|
-
info(`Could not open browser. Visit: ${previewUrl}`);
|
|
1763
|
-
}
|
|
1764
|
-
success("Preview server started");
|
|
1765
|
-
console.log(` URL: ${previewUrl}`);
|
|
1766
|
-
console.log("");
|
|
1767
|
-
info("Press Ctrl+C to stop the server");
|
|
1768
|
-
info("Edit the HTML file and save to see changes in the browser");
|
|
1769
|
-
}
|
|
1770
|
-
var PID_FILE2 = path3__namespace.join(os2__namespace.tmpdir(), "lindoai-blogs-preview.pid");
|
|
1771
|
-
var PORT_FILE2 = path3__namespace.join(os2__namespace.tmpdir(), "lindoai-blogs-preview.port");
|
|
1772
|
-
function createBlogsCommand() {
|
|
1773
|
-
const blogs = new commander.Command("blogs").description("Blog management operations");
|
|
1774
|
-
blogs.command("list").description("List all blogs for a website").requiredOption("-w, --website <id>", "Website ID").option("-p, --page <page>", "Page number", "1").option("-s, --search <search>", "Search term").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1775
|
-
const client = getClient7();
|
|
1776
|
-
try {
|
|
1777
|
-
const response = await client.blogs.list(options.website, {
|
|
1778
|
-
page: parseInt(options.page, 10),
|
|
1779
|
-
search: options.search
|
|
1780
|
-
});
|
|
1781
|
-
if (options.format === "json") {
|
|
1782
|
-
output(response, "json");
|
|
1783
|
-
} else {
|
|
1784
|
-
const result = response.result;
|
|
1785
|
-
if (result?.list && result.list.length > 0) {
|
|
1786
|
-
console.log("\nBlogs:");
|
|
1787
|
-
console.log("------");
|
|
1788
|
-
for (const b of result.list) {
|
|
1789
|
-
console.log(` ID: ${b.blog_id}`);
|
|
1790
|
-
console.log(` Name: ${b.name ?? "N/A"}`);
|
|
1791
|
-
console.log(` Path: ${b.path ?? "N/A"}`);
|
|
1792
|
-
console.log(` Status: ${b.status ?? "N/A"}`);
|
|
1793
|
-
console.log(` Published: ${b.publish_date ? new Date(b.publish_date * 1e3).toISOString() : "No"}`);
|
|
1794
|
-
console.log("");
|
|
1795
|
-
}
|
|
1796
|
-
console.log(`Total: ${result.total ?? result.list.length}`);
|
|
1797
|
-
} else {
|
|
1798
|
-
info("No blogs found");
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
} catch (err) {
|
|
1802
|
-
handleError8(err);
|
|
1803
|
-
}
|
|
1804
|
-
});
|
|
1805
|
-
blogs.command("get").description("Get details of a specific blog").requiredOption("-w, --website <id>", "Website ID").requiredOption("-i, --id <id>", "Blog ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1806
|
-
const client = getClient7();
|
|
1807
|
-
try {
|
|
1808
|
-
const response = await client.blogs.get(options.website, options.id);
|
|
1809
|
-
if (options.format === "json") {
|
|
1810
|
-
output(response, "json");
|
|
1811
|
-
} else {
|
|
1812
|
-
const result = response.result;
|
|
1813
|
-
if (result) {
|
|
1814
|
-
console.log("\nBlog Details:");
|
|
1815
|
-
console.log("-------------");
|
|
1816
|
-
console.log(` ID: ${result.blog_id}`);
|
|
1817
|
-
console.log(` Name: ${result.name ?? "N/A"}`);
|
|
1818
|
-
console.log(` Path: ${result.path ?? "N/A"}`);
|
|
1819
|
-
console.log(` Status: ${result.status ?? "N/A"}`);
|
|
1820
|
-
console.log(` Published: ${result.publish_date ? new Date(result.publish_date * 1e3).toISOString() : "No"}`);
|
|
1821
|
-
console.log(` Created: ${result.created_date ?? "N/A"}`);
|
|
1822
|
-
} else {
|
|
1823
|
-
error("Blog not found");
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
} catch (err) {
|
|
1827
|
-
handleError8(err);
|
|
1828
|
-
}
|
|
1829
|
-
});
|
|
1830
|
-
blogs.command("publish").description("Publish a blog").requiredOption("-w, --website <id>", "Website ID").requiredOption("-i, --id <id>", "Blog ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1831
|
-
const client = getClient7();
|
|
1832
|
-
try {
|
|
1833
|
-
const response = await client.blogs.publish(options.website, options.id);
|
|
1834
|
-
if (options.format === "json") {
|
|
1835
|
-
output(response, "json");
|
|
1836
|
-
} else {
|
|
1837
|
-
if (response.success) {
|
|
1838
|
-
success("Blog published successfully");
|
|
1839
|
-
console.log(` Blog ID: ${response.result?.blog_id}`);
|
|
1840
|
-
console.log(` Published at: ${response.result?.publish_date ? new Date(response.result.publish_date * 1e3).toISOString() : "N/A"}`);
|
|
1841
|
-
} else {
|
|
1842
|
-
error("Failed to publish blog");
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
} catch (err) {
|
|
1846
|
-
handleError8(err);
|
|
1847
|
-
}
|
|
1848
|
-
});
|
|
1849
|
-
blogs.command("unpublish").description("Unpublish a blog").requiredOption("-w, --website <id>", "Website ID").requiredOption("-i, --id <id>", "Blog ID").option("-f, --format <format>", "Output format (json, table)", "table").action(async (options) => {
|
|
1850
|
-
const client = getClient7();
|
|
1851
|
-
try {
|
|
1852
|
-
const response = await client.blogs.unpublish(options.website, options.id);
|
|
1853
|
-
if (options.format === "json") {
|
|
1854
|
-
output(response, "json");
|
|
1855
|
-
} else {
|
|
1856
|
-
if (response.success) {
|
|
1857
|
-
success("Blog unpublished successfully");
|
|
1858
|
-
console.log(` Blog ID: ${response.result?.blog_id}`);
|
|
1859
|
-
} else {
|
|
1860
|
-
error("Failed to unpublish blog");
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
} catch (err) {
|
|
1864
|
-
handleError8(err);
|
|
1865
|
-
}
|
|
1866
|
-
});
|
|
1867
|
-
blogs.command("edit").description("Edit a blog with live preview").argument("<website_id>", "Website ID").argument("<blog_id>", "Blog ID").option("--file <path>", "Output file path", "./blog.html").option("--background", "Run preview server in background").action(async (websiteId, blogId, options) => {
|
|
1868
|
-
const client = getClient7();
|
|
1869
|
-
try {
|
|
1870
|
-
info("Fetching blog details...");
|
|
1871
|
-
const blogResponse = await client.blogs.get(websiteId, blogId);
|
|
1872
|
-
if (!blogResponse.result) {
|
|
1873
|
-
error("Blog not found");
|
|
1874
|
-
process.exit(1);
|
|
1875
|
-
}
|
|
1876
|
-
const blogPath = blogResponse.result.path;
|
|
1877
|
-
const websiteResponse = await client.websites.getDetails(websiteId);
|
|
1878
|
-
if (!websiteResponse.result) {
|
|
1879
|
-
error("Website not found");
|
|
1880
|
-
process.exit(1);
|
|
1881
|
-
}
|
|
1882
|
-
const baseUrl = websiteResponse.result.custom_domain || websiteResponse.result.verified_domain || websiteResponse.result.preview_url;
|
|
1883
|
-
if (!baseUrl) {
|
|
1884
|
-
error("Could not determine website URL");
|
|
1885
|
-
process.exit(1);
|
|
1886
|
-
}
|
|
1887
|
-
const blogUrl = `https://${baseUrl}${blogPath}`;
|
|
1888
|
-
info(`Fetching HTML from ${blogUrl}...`);
|
|
1889
|
-
const htmlResponse = await fetch(blogUrl);
|
|
1890
|
-
if (!htmlResponse.ok) {
|
|
1891
|
-
error(`Failed to fetch blog HTML: ${htmlResponse.status} ${htmlResponse.statusText}`);
|
|
1892
|
-
info("Make sure the blog is published before editing");
|
|
1893
|
-
process.exit(1);
|
|
1894
|
-
}
|
|
1895
|
-
const html = await htmlResponse.text();
|
|
1896
|
-
const outputPath = path3__namespace.resolve(options.file);
|
|
1897
|
-
fs3__namespace.writeFileSync(outputPath, html, "utf-8");
|
|
1898
|
-
success(`HTML saved to ${outputPath}`);
|
|
1899
|
-
await terminateExistingPreviewServer2();
|
|
1900
|
-
if (options.background) {
|
|
1901
|
-
await startBackgroundPreviewServer2(outputPath);
|
|
1902
|
-
} else {
|
|
1903
|
-
await startForegroundPreviewServer2(outputPath);
|
|
1904
|
-
}
|
|
1905
|
-
} catch (err) {
|
|
1906
|
-
handleError8(err);
|
|
1907
|
-
}
|
|
1908
|
-
});
|
|
1909
|
-
blogs.command("update").description("Update a blog").argument("<website_id>", "Website ID").argument("<blog_id>", "Blog ID").option("--html-file <path>", "Path to local HTML file to upload").option("-f, --format <format>", "Output format (json, table)", "table").action(async (websiteId, blogId, options) => {
|
|
1910
|
-
const client = getClient7();
|
|
1911
|
-
const config = loadConfig();
|
|
1912
|
-
try {
|
|
1913
|
-
if (!options.htmlFile) {
|
|
1914
|
-
error("--html-file option is required");
|
|
1915
|
-
info("Usage: lindoai blogs update <website_id> <blog_id> --html-file <path>");
|
|
1916
|
-
process.exit(1);
|
|
1917
|
-
}
|
|
1918
|
-
const htmlPath = path3__namespace.resolve(options.htmlFile);
|
|
1919
|
-
if (!fs3__namespace.existsSync(htmlPath)) {
|
|
1920
|
-
error(`File not found: ${htmlPath}`);
|
|
1921
|
-
process.exit(1);
|
|
1922
|
-
}
|
|
1923
|
-
const html = fs3__namespace.readFileSync(htmlPath, "utf-8");
|
|
1924
|
-
info(`Read ${html.length} bytes from ${htmlPath}`);
|
|
1925
|
-
const blogResponse = await client.blogs.get(websiteId, blogId);
|
|
1926
|
-
if (!blogResponse.result) {
|
|
1927
|
-
error("Blog not found");
|
|
1928
|
-
process.exit(1);
|
|
1929
|
-
}
|
|
1930
|
-
const blogPath = blogResponse.result.path;
|
|
1931
|
-
info("Updating blog...");
|
|
1932
|
-
const apiBaseUrl = config.baseUrl || "https://api.lindo.ai";
|
|
1933
|
-
const updateUrl = `${apiBaseUrl}/v1/workspace/website/${websiteId}/blogs/${blogId}/update`;
|
|
1934
|
-
const response = await fetch(updateUrl, {
|
|
1935
|
-
method: "POST",
|
|
1936
|
-
headers: {
|
|
1937
|
-
"Content-Type": "application/json",
|
|
1938
|
-
"Authorization": `Bearer ${config.apiKey}`
|
|
1939
|
-
},
|
|
1940
|
-
body: JSON.stringify({
|
|
1941
|
-
html,
|
|
1942
|
-
path: blogPath
|
|
1943
|
-
})
|
|
1944
|
-
});
|
|
1945
|
-
const result = await response.json();
|
|
1946
|
-
if (options.format === "json") {
|
|
1947
|
-
output(result, "json");
|
|
1948
|
-
} else {
|
|
1949
|
-
if (result.success) {
|
|
1950
|
-
success("Blog updated successfully");
|
|
1951
|
-
console.log(` Blog ID: ${result.result?.blog_id}`);
|
|
1952
|
-
if (result.result?.publish_date) {
|
|
1953
|
-
console.log(` Published at: ${new Date(result.result.publish_date * 1e3).toISOString()}`);
|
|
1954
|
-
}
|
|
1955
|
-
} else {
|
|
1956
|
-
error("Failed to update blog");
|
|
1957
|
-
if (result.errors) {
|
|
1958
|
-
for (const err of result.errors) {
|
|
1959
|
-
console.log(` ${err}`);
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
} catch (err) {
|
|
1965
|
-
handleError8(err);
|
|
1966
|
-
}
|
|
1967
|
-
});
|
|
1968
|
-
blogs.command("stop-preview").description("Stop the background preview server").action(async () => {
|
|
1969
|
-
try {
|
|
1970
|
-
if (!fs3__namespace.existsSync(PID_FILE2)) {
|
|
1971
|
-
info("No preview server is running");
|
|
1972
|
-
return;
|
|
1973
|
-
}
|
|
1974
|
-
const pid = parseInt(fs3__namespace.readFileSync(PID_FILE2, "utf-8").trim(), 10);
|
|
1975
|
-
if (isNaN(pid)) {
|
|
1976
|
-
error("Invalid PID file");
|
|
1977
|
-
cleanupPidFiles2();
|
|
1978
|
-
return;
|
|
1979
|
-
}
|
|
1980
|
-
try {
|
|
1981
|
-
process.kill(pid, "SIGTERM");
|
|
1982
|
-
success(`Preview server (PID ${pid}) stopped`);
|
|
1983
|
-
} catch (killErr) {
|
|
1984
|
-
if (killErr.code === "ESRCH") {
|
|
1985
|
-
info("Preview server process not found (may have already stopped)");
|
|
1986
|
-
} else {
|
|
1987
|
-
error(`Failed to stop preview server: ${killErr.message}`);
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
cleanupPidFiles2();
|
|
1991
|
-
} catch (err) {
|
|
1992
|
-
if (err instanceof Error) {
|
|
1993
|
-
error(err.message);
|
|
1994
|
-
} else {
|
|
1995
|
-
error("An unexpected error occurred");
|
|
1996
|
-
}
|
|
1997
|
-
process.exit(1);
|
|
1998
|
-
}
|
|
1999
|
-
});
|
|
2000
|
-
return blogs;
|
|
2001
|
-
}
|
|
2002
|
-
function getClient7() {
|
|
2003
|
-
if (!hasApiKey()) {
|
|
2004
|
-
error("API key not configured");
|
|
2005
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
2006
|
-
info("Or set the LINDO_API_KEY environment variable");
|
|
2007
|
-
process.exit(1);
|
|
2008
|
-
}
|
|
2009
|
-
const config = loadConfig();
|
|
2010
|
-
return new lindoai.LindoClient({
|
|
2011
|
-
apiKey: config.apiKey,
|
|
2012
|
-
baseUrl: config.baseUrl
|
|
2013
|
-
});
|
|
2014
|
-
}
|
|
2015
|
-
function handleError8(err) {
|
|
2016
|
-
if (err instanceof lindoai.AuthenticationError) {
|
|
2017
|
-
error("Authentication failed");
|
|
2018
|
-
info("Your API key may be invalid or expired");
|
|
2019
|
-
info("Run: lindo config set apiKey <your-api-key>");
|
|
2020
|
-
process.exit(1);
|
|
2021
|
-
}
|
|
2022
|
-
if (err instanceof Error) {
|
|
2023
|
-
error(err.message);
|
|
2024
|
-
} else {
|
|
2025
|
-
error("An unexpected error occurred");
|
|
2026
|
-
}
|
|
2027
|
-
process.exit(1);
|
|
2028
|
-
}
|
|
2029
|
-
function cleanupPidFiles2() {
|
|
2030
|
-
try {
|
|
2031
|
-
if (fs3__namespace.existsSync(PID_FILE2)) {
|
|
2032
|
-
fs3__namespace.unlinkSync(PID_FILE2);
|
|
2033
|
-
}
|
|
2034
|
-
} catch {
|
|
2035
|
-
}
|
|
2036
|
-
try {
|
|
2037
|
-
if (fs3__namespace.existsSync(PORT_FILE2)) {
|
|
2038
|
-
fs3__namespace.unlinkSync(PORT_FILE2);
|
|
2039
|
-
}
|
|
2040
|
-
} catch {
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
async function terminateExistingPreviewServer2() {
|
|
2044
|
-
if (!fs3__namespace.existsSync(PID_FILE2)) {
|
|
2045
|
-
return;
|
|
2046
|
-
}
|
|
2047
|
-
try {
|
|
2048
|
-
const pid = parseInt(fs3__namespace.readFileSync(PID_FILE2, "utf-8").trim(), 10);
|
|
2049
|
-
if (!isNaN(pid)) {
|
|
2050
|
-
try {
|
|
2051
|
-
process.kill(pid, "SIGTERM");
|
|
2052
|
-
info(`Terminated existing preview server (PID ${pid})`);
|
|
2053
|
-
await new Promise((resolve4) => setTimeout(resolve4, 500));
|
|
2054
|
-
} catch {
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
} catch {
|
|
2058
|
-
}
|
|
2059
|
-
cleanupPidFiles2();
|
|
2060
|
-
}
|
|
2061
|
-
async function startBackgroundPreviewServer2(filePath) {
|
|
2062
|
-
const absolutePath = path3__namespace.resolve(filePath);
|
|
2063
|
-
const serverCode = `
|
|
160
|
+
`,e=child_process.spawn(process.execPath,["-e",n],{detached:true,stdio:"ignore"});e.unref();let i=null;for(let m=0;m<50;m++)if(await new Promise(p=>setTimeout(p,100)),w__namespace.existsSync(z))try{if(i=parseInt(w__namespace.readFileSync(z,"utf-8").trim(),10),!isNaN(i))break}catch{}i||(r("Failed to start preview server"),process.exit(1));let a=`http://127.0.0.1:${i}/`;await L(a)||s(`Could not open browser. Visit: ${a}`),d("Preview server started in background"),console.log(` URL: ${a}`),console.log(` PID: ${e.pid}`),console.log(""),s("To update the page: lindoai pages update <website_id> <page_id> --html-file <path>"),s("To stop the server: lindoai pages stop-preview");}async function At(o){let{startLivePreviewServer:t}=await Promise.resolve().then(()=>(ue(),pe));s("Starting preview server...");let n=await t(o),e=`http://127.0.0.1:${n}/`;w__namespace.writeFileSync(N,process.pid.toString(),"utf-8"),w__namespace.writeFileSync(z,n.toString(),"utf-8"),await L(e)||s(`Could not open browser. Visit: ${e}`),d("Preview server started"),console.log(` URL: ${e}`),console.log(""),s("Press Ctrl+C to stop the server"),s("Edit the HTML file and save to see changes in the browser");}var q=j__namespace.join(de__namespace.tmpdir(),"lindoai-blogs-preview.pid"),Q=j__namespace.join(de__namespace.tmpdir(),"lindoai-blogs-preview.port");function ye(){let o=new commander.Command("blogs").description("Blog management operations");return o.command("list").description("List all blogs for a website").requiredOption("-w, --website <id>","Website ID").option("-p, --page <page>","Page number","1").option("-s, --search <search>","Search term").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=U();try{let e=await n.blogs.list(t.website,{page:parseInt(t.page,10),search:t.search});if(t.format==="json")c(e,"json");else {let i=e.result;if(i?.list&&i.list.length>0){console.log(`
|
|
161
|
+
Blogs:`),console.log("------");for(let a of i.list)console.log(` ID: ${a.blog_id}`),console.log(` Name: ${a.name??"N/A"}`),console.log(` Path: ${a.path??"N/A"}`),console.log(` Status: ${a.status??"N/A"}`),console.log(` Published: ${a.publish_date?new Date(a.publish_date*1e3).toISOString():"No"}`),console.log("");console.log(`Total: ${i.total??i.list.length}`);}else s("No blogs found");}}catch(e){K(e);}}),o.command("get").description("Get details of a specific blog").requiredOption("-w, --website <id>","Website ID").requiredOption("-i, --id <id>","Blog ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=U();try{let e=await n.blogs.get(t.website,t.id);if(t.format==="json")c(e,"json");else {let i=e.result;i?(console.log(`
|
|
162
|
+
Blog Details:`),console.log("-------------"),console.log(` ID: ${i.blog_id}`),console.log(` Name: ${i.name??"N/A"}`),console.log(` Path: ${i.path??"N/A"}`),console.log(` Status: ${i.status??"N/A"}`),console.log(` Published: ${i.publish_date?new Date(i.publish_date*1e3).toISOString():"No"}`),console.log(` Created: ${i.created_date??"N/A"}`)):r("Blog not found");}}catch(e){K(e);}}),o.command("publish").description("Publish a blog").requiredOption("-w, --website <id>","Website ID").requiredOption("-i, --id <id>","Blog ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=U();try{let e=await n.blogs.publish(t.website,t.id);t.format==="json"?c(e,"json"):e.success?(d("Blog published successfully"),console.log(` Blog ID: ${e.result?.blog_id}`),console.log(` Published at: ${e.result?.publish_date?new Date(e.result.publish_date*1e3).toISOString():"N/A"}`)):r("Failed to publish blog");}catch(e){K(e);}}),o.command("unpublish").description("Unpublish a blog").requiredOption("-w, --website <id>","Website ID").requiredOption("-i, --id <id>","Blog ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=U();try{let e=await n.blogs.unpublish(t.website,t.id);t.format==="json"?c(e,"json"):e.success?(d("Blog unpublished successfully"),console.log(` Blog ID: ${e.result?.blog_id}`)):r("Failed to unpublish blog");}catch(e){K(e);}}),o.command("delete").description("Delete a blog").requiredOption("-w, --website <id>","Website ID").requiredOption("-i, --id <id>","Blog ID").option("-f, --format <format>","Output format (json, table)","table").action(async t=>{let n=U();try{let e=await n.blogs.delete(t.website,t.id);if(t.format==="json")c(e,"json");else if(e.success){if(d("Blog deleted successfully"),console.log(` Blog ID: ${e.result?.blog_id}`),e.result?.warnings&&e.result.warnings.length>0){console.log(" Warnings:");for(let i of e.result.warnings)console.log(` - ${i}`);}}else r("Failed to delete blog");}catch(e){K(e);}}),o.command("edit").description("Edit a blog with live preview").argument("<website_id>","Website ID").argument("<blog_id>","Blog ID").option("--file <path>","Output file path","./blog.html").option("--background","Run preview server in background").action(async(t,n,e)=>{let i=U();try{s("Fetching blog details...");let a=await i.blogs.get(t,n);a.result||(r("Blog not found"),process.exit(1));let l=a.result.path,m=await i.websites.getDetails(t);m.result||(r("Website not found"),process.exit(1));let p=m.result.custom_domain||m.result.verified_domain||m.result.preview_url;p||(r("Could not determine website URL"),process.exit(1));let b=`https://${p}${l}`;s(`Fetching HTML from ${b}...`);let g=await fetch(b);g.ok||(r(`Failed to fetch blog HTML: ${g.status} ${g.statusText}`),s("Make sure the blog is published before editing"),process.exit(1));let v=await g.text(),u=j__namespace.resolve(e.file);w__namespace.writeFileSync(u,v,"utf-8"),d(`HTML saved to ${u}`),await Dt(),e.background?await Rt(u):await jt(u);}catch(a){K(a);}}),o.command("update").description("Update a blog").argument("<website_id>","Website ID").argument("<blog_id>","Blog ID").option("--html-file <path>","Path to local HTML file to upload").option("-f, --format <format>","Output format (json, table)","table").action(async(t,n,e)=>{let i=U(),a=h();try{e.htmlFile||(r("--html-file option is required"),s("Usage: lindoai blogs update <website_id> <blog_id> --html-file <path>"),process.exit(1));let l=j__namespace.resolve(e.htmlFile);w__namespace.existsSync(l)||(r(`File not found: ${l}`),process.exit(1));let m=w__namespace.readFileSync(l,"utf-8");s(`Read ${m.length} bytes from ${l}`);let p=await i.blogs.get(t,n);p.result||(r("Blog not found"),process.exit(1));let b=p.result.path;s("Updating blog...");let v=`${a.baseUrl||"https://api.lindo.ai"}/v1/workspace/website/${t}/blogs/${n}/update`,f=await(await fetch(v,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.apiKey}`},body:JSON.stringify({html:m,path:b})})).json();if(e.format==="json")c(f,"json");else if(f.success)d("Blog updated successfully"),console.log(` Blog ID: ${f.result?.blog_id}`),f.result?.publish_date&&console.log(` Published at: ${new Date(f.result.publish_date*1e3).toISOString()}`);else if(r("Failed to update blog"),f.errors)for(let S of f.errors)console.log(` ${S}`);}catch(l){K(l);}}),o.command("stop-preview").description("Stop the background preview server").action(async()=>{try{if(!w__namespace.existsSync(q)){s("No preview server is running");return}let t=parseInt(w__namespace.readFileSync(q,"utf-8").trim(),10);if(isNaN(t)){r("Invalid PID file"),be();return}try{process.kill(t,"SIGTERM"),d(`Preview server (PID ${t}) stopped`);}catch(n){n.code==="ESRCH"?s("Preview server process not found (may have already stopped)"):r(`Failed to stop preview server: ${n.message}`);}be();}catch(t){t instanceof Error?r(t.message):r("An unexpected error occurred"),process.exit(1);}}),o}function U(){C()||(r("API key not configured"),s("Run: lindo config set apiKey <your-api-key>"),s("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=h();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function K(o){o instanceof lindoai.AuthenticationError&&(r("Authentication failed"),s("Your API key may be invalid or expired"),s("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?r(o.message):r("An unexpected error occurred"),process.exit(1);}function be(){try{w__namespace.existsSync(q)&&w__namespace.unlinkSync(q);}catch{}try{w__namespace.existsSync(Q)&&w__namespace.unlinkSync(Q);}catch{}}async function Dt(){if(w__namespace.existsSync(q)){try{let o=parseInt(w__namespace.readFileSync(q,"utf-8").trim(),10);if(!isNaN(o))try{process.kill(o,"SIGTERM"),s(`Terminated existing preview server (PID ${o})`),await new Promise(t=>setTimeout(t,500));}catch{}}catch{}be();}}async function Rt(o){let t=j__namespace.resolve(o),n=`
|
|
2064
163
|
const http = require('node:http');
|
|
2065
164
|
const fs = require('node:fs');
|
|
2066
165
|
const path = require('node:path');
|
|
@@ -2080,7 +179,7 @@ async function startBackgroundPreviewServer2(filePath) {
|
|
|
2080
179
|
})();
|
|
2081
180
|
</script>\`;
|
|
2082
181
|
|
|
2083
|
-
const filePath = ${JSON.stringify(
|
|
182
|
+
const filePath = ${JSON.stringify(t)};
|
|
2084
183
|
const pidFile = path.join(os.tmpdir(), 'lindoai-blogs-preview.pid');
|
|
2085
184
|
const portFile = path.join(os.tmpdir(), 'lindoai-blogs-preview.port');
|
|
2086
185
|
const sseClients = new Set();
|
|
@@ -2180,81 +279,7 @@ async function startBackgroundPreviewServer2(filePath) {
|
|
|
2180
279
|
});
|
|
2181
280
|
} catch {}
|
|
2182
281
|
});
|
|
2183
|
-
|
|
2184
|
-
const child = child_process.spawn(process.execPath, ["-e", serverCode], {
|
|
2185
|
-
detached: true,
|
|
2186
|
-
stdio: "ignore"
|
|
2187
|
-
});
|
|
2188
|
-
child.unref();
|
|
2189
|
-
let port = null;
|
|
2190
|
-
for (let i = 0; i < 50; i++) {
|
|
2191
|
-
await new Promise((resolve4) => setTimeout(resolve4, 100));
|
|
2192
|
-
if (fs3__namespace.existsSync(PORT_FILE2)) {
|
|
2193
|
-
try {
|
|
2194
|
-
port = parseInt(fs3__namespace.readFileSync(PORT_FILE2, "utf-8").trim(), 10);
|
|
2195
|
-
if (!isNaN(port)) break;
|
|
2196
|
-
} catch {
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2200
|
-
if (!port) {
|
|
2201
|
-
error("Failed to start preview server");
|
|
2202
|
-
process.exit(1);
|
|
2203
|
-
}
|
|
2204
|
-
const previewUrl = `http://127.0.0.1:${port}/`;
|
|
2205
|
-
const browserOpened = await openBrowser(previewUrl);
|
|
2206
|
-
if (!browserOpened) {
|
|
2207
|
-
info(`Could not open browser. Visit: ${previewUrl}`);
|
|
2208
|
-
}
|
|
2209
|
-
success("Preview server started in background");
|
|
2210
|
-
console.log(` URL: ${previewUrl}`);
|
|
2211
|
-
console.log(` PID: ${child.pid}`);
|
|
2212
|
-
console.log("");
|
|
2213
|
-
info("To update the blog: lindoai blogs update <website_id> <blog_id> --html-file <path>");
|
|
2214
|
-
info("To stop the server: lindoai blogs stop-preview");
|
|
2215
|
-
}
|
|
2216
|
-
async function startForegroundPreviewServer2(filePath) {
|
|
2217
|
-
const { startLivePreviewServer: startLivePreviewServer2 } = await Promise.resolve().then(() => (init_live_preview_server(), live_preview_server_exports));
|
|
2218
|
-
info("Starting preview server...");
|
|
2219
|
-
const port = await startLivePreviewServer2(filePath);
|
|
2220
|
-
const previewUrl = `http://127.0.0.1:${port}/`;
|
|
2221
|
-
fs3__namespace.writeFileSync(PID_FILE2, process.pid.toString(), "utf-8");
|
|
2222
|
-
fs3__namespace.writeFileSync(PORT_FILE2, port.toString(), "utf-8");
|
|
2223
|
-
const browserOpened = await openBrowser(previewUrl);
|
|
2224
|
-
if (!browserOpened) {
|
|
2225
|
-
info(`Could not open browser. Visit: ${previewUrl}`);
|
|
2226
|
-
}
|
|
2227
|
-
success("Preview server started");
|
|
2228
|
-
console.log(` URL: ${previewUrl}`);
|
|
2229
|
-
console.log("");
|
|
2230
|
-
info("Press Ctrl+C to stop the server");
|
|
2231
|
-
info("Edit the HTML file and save to see changes in the browser");
|
|
2232
|
-
}
|
|
2233
|
-
function generateState() {
|
|
2234
|
-
return crypto__namespace.randomBytes(32).toString("hex");
|
|
2235
|
-
}
|
|
2236
|
-
function verifyState(received, expected) {
|
|
2237
|
-
return received === expected;
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
// src/server/callback.ts
|
|
2241
|
-
function parseCallbackParams(url$1) {
|
|
2242
|
-
try {
|
|
2243
|
-
const fullUrl = url$1.startsWith("http") ? url$1 : `http://localhost${url$1}`;
|
|
2244
|
-
const parsed = new url.URL(fullUrl);
|
|
2245
|
-
const params = parsed.searchParams;
|
|
2246
|
-
return {
|
|
2247
|
-
key: params.get("key") ?? void 0,
|
|
2248
|
-
state: params.get("state") ?? void 0,
|
|
2249
|
-
error: params.get("error") ?? void 0,
|
|
2250
|
-
message: params.get("message") ?? void 0
|
|
2251
|
-
};
|
|
2252
|
-
} catch {
|
|
2253
|
-
return {};
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
function generateSuccessHtml() {
|
|
2257
|
-
return `<!DOCTYPE html>
|
|
282
|
+
`,e=child_process.spawn(process.execPath,["-e",n],{detached:true,stdio:"ignore"});e.unref();let i=null;for(let m=0;m<50;m++)if(await new Promise(p=>setTimeout(p,100)),w__namespace.existsSync(Q))try{if(i=parseInt(w__namespace.readFileSync(Q,"utf-8").trim(),10),!isNaN(i))break}catch{}i||(r("Failed to start preview server"),process.exit(1));let a=`http://127.0.0.1:${i}/`;await L(a)||s(`Could not open browser. Visit: ${a}`),d("Preview server started in background"),console.log(` URL: ${a}`),console.log(` PID: ${e.pid}`),console.log(""),s("To update the blog: lindoai blogs update <website_id> <blog_id> --html-file <path>"),s("To stop the server: lindoai blogs stop-preview");}async function jt(o){let{startLivePreviewServer:t}=await Promise.resolve().then(()=>(ue(),pe));s("Starting preview server...");let n=await t(o),e=`http://127.0.0.1:${n}/`;w__namespace.writeFileSync(q,process.pid.toString(),"utf-8"),w__namespace.writeFileSync(Q,n.toString(),"utf-8"),await L(e)||s(`Could not open browser. Visit: ${e}`),d("Preview server started"),console.log(` URL: ${e}`),console.log(""),s("Press Ctrl+C to stop the server"),s("Edit the HTML file and save to see changes in the browser");}function Re(){return De__namespace.randomBytes(32).toString("hex")}function je(o,t){return o===t}function Ut(o){try{let t=o.startsWith("http")?o:`http://localhost${o}`,e=new url.URL(t).searchParams;return {key:e.get("key")??void 0,state:e.get("state")??void 0,error:e.get("error")??void 0,message:e.get("message")??void 0}}catch{return {}}}function Kt(){return `<!DOCTYPE html>
|
|
2258
283
|
<html lang="en">
|
|
2259
284
|
<head>
|
|
2260
285
|
<meta charset="UTF-8">
|
|
@@ -2300,11 +325,7 @@ function generateSuccessHtml() {
|
|
|
2300
325
|
<p>You can close this window and return to your terminal.</p>
|
|
2301
326
|
</div>
|
|
2302
327
|
</body>
|
|
2303
|
-
</html
|
|
2304
|
-
}
|
|
2305
|
-
function generateErrorHtml(errorMessage) {
|
|
2306
|
-
const escapedMessage = errorMessage.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2307
|
-
return `<!DOCTYPE html>
|
|
328
|
+
</html>`}function X(o){return `<!DOCTYPE html>
|
|
2308
329
|
<html lang="en">
|
|
2309
330
|
<head>
|
|
2310
331
|
<meta charset="UTF-8">
|
|
@@ -2356,13 +377,10 @@ function generateErrorHtml(errorMessage) {
|
|
|
2356
377
|
<div class="icon">\u2717</div>
|
|
2357
378
|
<h1>Authorization Failed</h1>
|
|
2358
379
|
<p>Something went wrong during authorization.</p>
|
|
2359
|
-
<div class="error-message">${
|
|
380
|
+
<div class="error-message">${o.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}</div>
|
|
2360
381
|
</div>
|
|
2361
382
|
</body>
|
|
2362
|
-
</html
|
|
2363
|
-
}
|
|
2364
|
-
function generate404Html() {
|
|
2365
|
-
return `<!DOCTYPE html>
|
|
383
|
+
</html>`}function Wt(){return `<!DOCTYPE html>
|
|
2366
384
|
<html lang="en">
|
|
2367
385
|
<head>
|
|
2368
386
|
<meta charset="UTF-8">
|
|
@@ -2400,193 +418,13 @@ function generate404Html() {
|
|
|
2400
418
|
<p>This endpoint is not available.</p>
|
|
2401
419
|
</div>
|
|
2402
420
|
</body>
|
|
2403
|
-
</html
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
const method = req.method || "GET";
|
|
2411
|
-
const isCallbackPath = url === "/callback" || url.startsWith("/callback?");
|
|
2412
|
-
if (method !== "GET" || !isCallbackPath) {
|
|
2413
|
-
res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
|
|
2414
|
-
res.end(generate404Html());
|
|
2415
|
-
return;
|
|
2416
|
-
}
|
|
2417
|
-
const params = parseCallbackParams(url);
|
|
2418
|
-
if (!pendingCallback) {
|
|
2419
|
-
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
2420
|
-
res.end(generateErrorHtml("No pending authorization request"));
|
|
2421
|
-
return;
|
|
2422
|
-
}
|
|
2423
|
-
const { expectedState, resolve: resolve4, timeoutId } = pendingCallback;
|
|
2424
|
-
clearTimeout(timeoutId);
|
|
2425
|
-
pendingCallback = null;
|
|
2426
|
-
if (params.error) {
|
|
2427
|
-
const errorMessage = params.message || params.error;
|
|
2428
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
2429
|
-
res.end(generateErrorHtml(errorMessage));
|
|
2430
|
-
resolve4({
|
|
2431
|
-
success: false,
|
|
2432
|
-
error: errorMessage
|
|
2433
|
-
});
|
|
2434
|
-
return;
|
|
2435
|
-
}
|
|
2436
|
-
if (!params.state || !verifyState(params.state, expectedState)) {
|
|
2437
|
-
const errorMessage = "State token mismatch - possible CSRF attack";
|
|
2438
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
2439
|
-
res.end(generateErrorHtml(errorMessage));
|
|
2440
|
-
resolve4({
|
|
2441
|
-
success: false,
|
|
2442
|
-
error: errorMessage
|
|
2443
|
-
});
|
|
2444
|
-
return;
|
|
2445
|
-
}
|
|
2446
|
-
if (!params.key) {
|
|
2447
|
-
const errorMessage = "No API key received";
|
|
2448
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
2449
|
-
res.end(generateErrorHtml(errorMessage));
|
|
2450
|
-
resolve4({
|
|
2451
|
-
success: false,
|
|
2452
|
-
error: errorMessage
|
|
2453
|
-
});
|
|
2454
|
-
return;
|
|
2455
|
-
}
|
|
2456
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
2457
|
-
res.end(generateSuccessHtml());
|
|
2458
|
-
resolve4({
|
|
2459
|
-
success: true,
|
|
2460
|
-
apiKey: params.key
|
|
2461
|
-
});
|
|
2462
|
-
}
|
|
2463
|
-
return {
|
|
2464
|
-
async start() {
|
|
2465
|
-
return new Promise((resolve4, reject) => {
|
|
2466
|
-
server = http__namespace.createServer(handleRequest);
|
|
2467
|
-
server.on("error", (err) => {
|
|
2468
|
-
reject(new Error(`Failed to start callback server: ${err.message}`));
|
|
2469
|
-
});
|
|
2470
|
-
server.listen(0, "127.0.0.1", () => {
|
|
2471
|
-
const address = server.address();
|
|
2472
|
-
if (!address || typeof address === "string") {
|
|
2473
|
-
reject(new Error("Failed to get server address"));
|
|
2474
|
-
return;
|
|
2475
|
-
}
|
|
2476
|
-
const port = address.port;
|
|
2477
|
-
const url = `http://127.0.0.1:${port}/callback`;
|
|
2478
|
-
resolve4({ port, url });
|
|
2479
|
-
});
|
|
2480
|
-
});
|
|
2481
|
-
},
|
|
2482
|
-
waitForCallback(state, timeoutMs) {
|
|
2483
|
-
return new Promise((resolve4) => {
|
|
2484
|
-
const timeoutId = setTimeout(() => {
|
|
2485
|
-
if (pendingCallback) {
|
|
2486
|
-
pendingCallback = null;
|
|
2487
|
-
resolve4({
|
|
2488
|
-
success: false,
|
|
2489
|
-
error: "Login timed out waiting for authorization"
|
|
2490
|
-
});
|
|
2491
|
-
}
|
|
2492
|
-
}, timeoutMs);
|
|
2493
|
-
pendingCallback = {
|
|
2494
|
-
expectedState: state,
|
|
2495
|
-
resolve: resolve4,
|
|
2496
|
-
timeoutId
|
|
2497
|
-
};
|
|
2498
|
-
});
|
|
2499
|
-
},
|
|
2500
|
-
async stop() {
|
|
2501
|
-
return new Promise((resolve4) => {
|
|
2502
|
-
if (pendingCallback) {
|
|
2503
|
-
clearTimeout(pendingCallback.timeoutId);
|
|
2504
|
-
pendingCallback = null;
|
|
2505
|
-
}
|
|
2506
|
-
if (server) {
|
|
2507
|
-
server.close(() => {
|
|
2508
|
-
server = null;
|
|
2509
|
-
resolve4();
|
|
2510
|
-
});
|
|
2511
|
-
} else {
|
|
2512
|
-
resolve4();
|
|
2513
|
-
}
|
|
2514
|
-
});
|
|
2515
|
-
}
|
|
2516
|
-
};
|
|
2517
|
-
}
|
|
2518
|
-
|
|
2519
|
-
// src/commands/login.ts
|
|
2520
|
-
var WEBAPP_BASE_URL = "https://app.lindo.ai";
|
|
2521
|
-
var DEFAULT_TIMEOUT_SECONDS = 120;
|
|
2522
|
-
function buildAuthorizationUrl(state, callbackPort) {
|
|
2523
|
-
const callbackUrl = `http://127.0.0.1:${callbackPort}/callback`;
|
|
2524
|
-
const params = new URLSearchParams({
|
|
2525
|
-
state,
|
|
2526
|
-
callback_url: callbackUrl
|
|
2527
|
-
});
|
|
2528
|
-
return `${WEBAPP_BASE_URL}/cli/authorize?${params.toString()}`;
|
|
2529
|
-
}
|
|
2530
|
-
function createLoginCommand() {
|
|
2531
|
-
const login = new commander.Command("login").description("Authenticate via browser to configure your API key").option(
|
|
2532
|
-
"-t, --timeout <seconds>",
|
|
2533
|
-
"Timeout in seconds for the login flow",
|
|
2534
|
-
String(DEFAULT_TIMEOUT_SECONDS)
|
|
2535
|
-
).option(
|
|
2536
|
-
"--no-browser",
|
|
2537
|
-
"Display the authorization URL without opening the browser"
|
|
2538
|
-
).action(async (options) => {
|
|
2539
|
-
const timeoutSeconds = parseInt(options.timeout, 10);
|
|
2540
|
-
const timeoutMs = timeoutSeconds * 1e3;
|
|
2541
|
-
const shouldOpenBrowser = options.browser;
|
|
2542
|
-
const server = createCallbackServer();
|
|
2543
|
-
let serverStarted = false;
|
|
2544
|
-
try {
|
|
2545
|
-
info("Starting authentication flow...");
|
|
2546
|
-
const { port } = await server.start();
|
|
2547
|
-
serverStarted = true;
|
|
2548
|
-
const state = generateState();
|
|
2549
|
-
const authUrl = buildAuthorizationUrl(state, port);
|
|
2550
|
-
if (shouldOpenBrowser) {
|
|
2551
|
-
info("Opening browser for authorization...");
|
|
2552
|
-
const browserOpened = await openBrowser(authUrl);
|
|
2553
|
-
if (!browserOpened) {
|
|
2554
|
-
info("Could not open browser automatically.");
|
|
2555
|
-
console.log("\nPlease open this URL in your browser:");
|
|
2556
|
-
console.log(`
|
|
2557
|
-
${authUrl}
|
|
2558
|
-
`);
|
|
2559
|
-
}
|
|
2560
|
-
} else {
|
|
2561
|
-
console.log("\nPlease open this URL in your browser:");
|
|
2562
|
-
console.log(`
|
|
2563
|
-
${authUrl}
|
|
2564
|
-
`);
|
|
2565
|
-
}
|
|
2566
|
-
info(`Waiting for authorization (timeout: ${timeoutSeconds}s)...`);
|
|
2567
|
-
const result = await server.waitForCallback(state, timeoutMs);
|
|
2568
|
-
if (result.success && result.apiKey) {
|
|
2569
|
-
saveApiKey(result.apiKey);
|
|
2570
|
-
success("Successfully authenticated!");
|
|
2571
|
-
info(`API key saved to: ${getConfigPath()}`);
|
|
2572
|
-
} else {
|
|
2573
|
-
error(result.error || "Authentication failed");
|
|
2574
|
-
process.exit(1);
|
|
2575
|
-
}
|
|
2576
|
-
} catch (err) {
|
|
2577
|
-
error(err instanceof Error ? err.message : "An unexpected error occurred");
|
|
2578
|
-
process.exit(1);
|
|
2579
|
-
} finally {
|
|
2580
|
-
if (serverStarted) {
|
|
2581
|
-
await server.stop();
|
|
2582
|
-
}
|
|
2583
|
-
}
|
|
2584
|
-
});
|
|
2585
|
-
return login;
|
|
2586
|
-
}
|
|
2587
|
-
var OPENCODE_SKILLS_DIR = path3__namespace.join(os2__namespace.homedir(), ".config", "opencode", "skills", "lindoai");
|
|
2588
|
-
var SKILL_FILE_PATH = path3__namespace.join(OPENCODE_SKILLS_DIR, "SKILL.md");
|
|
2589
|
-
var SKILL_FILE_CONTENT = `---
|
|
421
|
+
</html>`}function Ue(){let o=null,t=null;function n(e,i){let a=e.url||"/",l=e.method||"GET",m=a==="/callback"||a.startsWith("/callback?");if(l!=="GET"||!m){i.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),i.end(Wt());return}let p=Ut(a);if(!t){i.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),i.end(X("No pending authorization request"));return}let{expectedState:b,resolve:g,timeoutId:v}=t;if(clearTimeout(v),t=null,p.error){let u=p.message||p.error;i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(X(u)),g({success:false,error:u});return}if(!p.state||!je(p.state,b)){let u="State token mismatch - possible CSRF attack";i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(X(u)),g({success:false,error:u});return}if(!p.key){let u="No API key received";i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(X(u)),g({success:false,error:u});return}i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(Kt()),g({success:true,apiKey:p.key});}return {async start(){return new Promise((e,i)=>{o=Ee__namespace.createServer(n),o.on("error",a=>{i(new Error(`Failed to start callback server: ${a.message}`));}),o.listen(0,"127.0.0.1",()=>{let a=o.address();if(!a||typeof a=="string"){i(new Error("Failed to get server address"));return}let l=a.port,m=`http://127.0.0.1:${l}/callback`;e({port:l,url:m});});})},waitForCallback(e,i){return new Promise(a=>{let l=setTimeout(()=>{t&&(t=null,a({success:false,error:"Login timed out waiting for authorization"}));},i);t={expectedState:e,resolve:a,timeoutId:l};})},async stop(){return new Promise(e=>{t&&(clearTimeout(t.timeoutId),t=null),o?o.close(()=>{o=null,e();}):e();})}}}var Mt="https://app.lindo.ai",Bt=120;function Ht(o,t){let n=`http://127.0.0.1:${t}/callback`,e=new URLSearchParams({state:o,callback_url:n});return `${Mt}/cli/authorize?${e.toString()}`}function ve(){return new commander.Command("login").description("Authenticate via browser to configure your API key").option("-t, --timeout <seconds>","Timeout in seconds for the login flow",String(Bt)).option("--no-browser","Display the authorization URL without opening the browser").action(async t=>{let n=parseInt(t.timeout,10),e=n*1e3,i=t.browser,a=Ue(),l=false;try{s("Starting authentication flow...");let{port:m}=await a.start();l=!0;let p=Re(),b=Ht(p,m);i?(s("Opening browser for authorization..."),await L(b)||(s("Could not open browser automatically."),console.log(`
|
|
422
|
+
Please open this URL in your browser:`),console.log(`
|
|
423
|
+
${b}
|
|
424
|
+
`))):(console.log(`
|
|
425
|
+
Please open this URL in your browser:`),console.log(`
|
|
426
|
+
${b}
|
|
427
|
+
`)),s(`Waiting for authorization (timeout: ${n}s)...`);let g=await a.waitForCallback(p,e);g.success&&g.apiKey?(Pe(g.apiKey),d("Successfully authenticated!"),s(`API key saved to: ${T()}`)):(r(g.error||"Authentication failed"),process.exit(1));}catch(m){r(m instanceof Error?m.message:"An unexpected error occurred"),process.exit(1);}finally{l&&await a.stop();}})}var Ce=j__namespace.join(de__namespace.homedir(),".config","opencode","skills","lindoai"),qe=j__namespace.join(Ce,"SKILL.md"),Jt=`---
|
|
2590
428
|
name: lindoai
|
|
2591
429
|
description: Lindo AI CLI - Command-line interface for the Lindo API
|
|
2592
430
|
---
|
|
@@ -2754,147 +592,5 @@ lindoai config set baseUrl <value>
|
|
|
2754
592
|
3. **Run preview servers in background mode** (\`--background\`) to keep the terminal available.
|
|
2755
593
|
4. **Remember to stop preview servers** when done editing with \`stop-preview\`.
|
|
2756
594
|
5. **Use JSON format** (\`--format json\`) when you need to parse command output programmatically.
|
|
2757
|
-
`;
|
|
2758
|
-
|
|
2759
|
-
try {
|
|
2760
|
-
const command = process.platform === "win32" ? "where opencode" : "which opencode";
|
|
2761
|
-
child_process.execSync(command, { stdio: "ignore" });
|
|
2762
|
-
return true;
|
|
2763
|
-
} catch {
|
|
2764
|
-
return false;
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
2767
|
-
function installOpenCode() {
|
|
2768
|
-
try {
|
|
2769
|
-
info("Installing OpenCode...");
|
|
2770
|
-
child_process.execSync("npm install -g opencode-ai@latest", { stdio: "inherit" });
|
|
2771
|
-
return true;
|
|
2772
|
-
} catch {
|
|
2773
|
-
return false;
|
|
2774
|
-
}
|
|
2775
|
-
}
|
|
2776
|
-
function ensureSkillInstalled() {
|
|
2777
|
-
if (!fs3__namespace.existsSync(OPENCODE_SKILLS_DIR)) {
|
|
2778
|
-
fs3__namespace.mkdirSync(OPENCODE_SKILLS_DIR, { recursive: true });
|
|
2779
|
-
}
|
|
2780
|
-
fs3__namespace.writeFileSync(SKILL_FILE_PATH, SKILL_FILE_CONTENT, "utf-8");
|
|
2781
|
-
}
|
|
2782
|
-
function createAgentCommand() {
|
|
2783
|
-
const agent = new commander.Command("agent").description("Launch an AI agent that understands all CLI commands").option("--install", "Install OpenCode if not already installed").option("--model <model>", "Specify the model to use with OpenCode").action(async (options) => {
|
|
2784
|
-
if (!isOpenCodeInstalled()) {
|
|
2785
|
-
if (options.install) {
|
|
2786
|
-
const installed = installOpenCode();
|
|
2787
|
-
if (!installed) {
|
|
2788
|
-
error("Failed to install OpenCode");
|
|
2789
|
-
info("Please install manually: npm install -g opencode-ai@latest");
|
|
2790
|
-
process.exit(1);
|
|
2791
|
-
}
|
|
2792
|
-
success("OpenCode installed successfully");
|
|
2793
|
-
} else {
|
|
2794
|
-
error("OpenCode is not installed");
|
|
2795
|
-
info("");
|
|
2796
|
-
info("To install OpenCode, run one of the following:");
|
|
2797
|
-
info(" lindoai agent --install");
|
|
2798
|
-
info(" npm install -g opencode-ai@latest");
|
|
2799
|
-
process.exit(1);
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
try {
|
|
2803
|
-
ensureSkillInstalled();
|
|
2804
|
-
info(`Skill file installed to: ${SKILL_FILE_PATH}`);
|
|
2805
|
-
} catch (err) {
|
|
2806
|
-
error(`Failed to install skill file: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
2807
|
-
process.exit(1);
|
|
2808
|
-
}
|
|
2809
|
-
const args = [];
|
|
2810
|
-
if (options.model) {
|
|
2811
|
-
args.push("--model", options.model);
|
|
2812
|
-
}
|
|
2813
|
-
info("Launching OpenCode...");
|
|
2814
|
-
const opencode = child_process.spawn("opencode", args, {
|
|
2815
|
-
stdio: "inherit",
|
|
2816
|
-
shell: true
|
|
2817
|
-
});
|
|
2818
|
-
opencode.on("error", (err) => {
|
|
2819
|
-
error(`Failed to launch OpenCode: ${err.message}`);
|
|
2820
|
-
process.exit(1);
|
|
2821
|
-
});
|
|
2822
|
-
opencode.on("close", (code) => {
|
|
2823
|
-
process.exit(code ?? 0);
|
|
2824
|
-
});
|
|
2825
|
-
});
|
|
2826
|
-
return agent;
|
|
2827
|
-
}
|
|
2828
|
-
|
|
2829
|
-
// src/index.ts
|
|
2830
|
-
var VERSION = "1.0.0";
|
|
2831
|
-
function createProgram() {
|
|
2832
|
-
const program = new commander.Command();
|
|
2833
|
-
program.name("lindo").description("Command-line interface for the Lindo API").version(VERSION, "-v, --version", "Output the current version").helpOption("-h, --help", "Display help for command");
|
|
2834
|
-
program.addCommand(createConfigCommand());
|
|
2835
|
-
program.addCommand(createAgentsCommand());
|
|
2836
|
-
program.addCommand(createWorkflowsCommand());
|
|
2837
|
-
program.addCommand(createWorkspaceCommand());
|
|
2838
|
-
program.addCommand(createAnalyticsCommand());
|
|
2839
|
-
program.addCommand(createClientsCommand());
|
|
2840
|
-
program.addCommand(createWebsitesCommand());
|
|
2841
|
-
program.addCommand(createPagesCommand());
|
|
2842
|
-
program.addCommand(createBlogsCommand());
|
|
2843
|
-
program.addCommand(createLoginCommand());
|
|
2844
|
-
program.addCommand(createAgentCommand());
|
|
2845
|
-
program.exitOverride((err) => {
|
|
2846
|
-
if (err.code === "commander.help") {
|
|
2847
|
-
process.exit(0);
|
|
2848
|
-
}
|
|
2849
|
-
if (err.code === "commander.version") {
|
|
2850
|
-
process.exit(0);
|
|
2851
|
-
}
|
|
2852
|
-
if (err.code === "commander.missingArgument") {
|
|
2853
|
-
error(err.message);
|
|
2854
|
-
process.exit(1);
|
|
2855
|
-
}
|
|
2856
|
-
if (err.code === "commander.unknownCommand") {
|
|
2857
|
-
error(err.message);
|
|
2858
|
-
info('Run "lindo --help" for available commands');
|
|
2859
|
-
process.exit(1);
|
|
2860
|
-
}
|
|
2861
|
-
throw err;
|
|
2862
|
-
});
|
|
2863
|
-
return program;
|
|
2864
|
-
}
|
|
2865
|
-
function handleAuthenticationError(_err) {
|
|
2866
|
-
error("Authentication failed");
|
|
2867
|
-
info("");
|
|
2868
|
-
info("Your API key may be invalid or expired.");
|
|
2869
|
-
info("");
|
|
2870
|
-
info("To configure your API key:");
|
|
2871
|
-
info(" 1. Run: lindo config set apiKey <your-api-key>");
|
|
2872
|
-
info(" 2. Or set the LINDO_API_KEY environment variable");
|
|
2873
|
-
info("");
|
|
2874
|
-
info("To get an API key:");
|
|
2875
|
-
info(" Visit https://app.lindo.ai/settings/api-keys");
|
|
2876
|
-
}
|
|
2877
|
-
async function main() {
|
|
2878
|
-
const program = createProgram();
|
|
2879
|
-
try {
|
|
2880
|
-
await program.parseAsync(process.argv);
|
|
2881
|
-
} catch (err) {
|
|
2882
|
-
if (err instanceof lindoai.AuthenticationError) {
|
|
2883
|
-
handleAuthenticationError();
|
|
2884
|
-
process.exit(1);
|
|
2885
|
-
}
|
|
2886
|
-
throw err;
|
|
2887
|
-
}
|
|
2888
|
-
}
|
|
2889
|
-
main().catch((err) => {
|
|
2890
|
-
if (err instanceof Error) {
|
|
2891
|
-
error(`Unexpected error: ${err.message}`);
|
|
2892
|
-
} else {
|
|
2893
|
-
error("An unexpected error occurred");
|
|
2894
|
-
}
|
|
2895
|
-
process.exit(1);
|
|
2896
|
-
});
|
|
2897
|
-
|
|
2898
|
-
exports.createProgram = createProgram;
|
|
2899
|
-
//# sourceMappingURL=index.cjs.map
|
|
2900
|
-
//# sourceMappingURL=index.cjs.map
|
|
595
|
+
`;function Vt(){try{let o=process.platform==="win32"?"where opencode":"which opencode";return child_process.execSync(o,{stdio:"ignore"}),!0}catch{return false}}function zt(){try{return s("Installing OpenCode..."),child_process.execSync("npm install -g opencode-ai@latest",{stdio:"inherit"}),!0}catch{return false}}function Qt(){w__namespace.existsSync(Ce)||w__namespace.mkdirSync(Ce,{recursive:true}),w__namespace.writeFileSync(qe,Jt,"utf-8");}function xe(){return new commander.Command("agent").description("Launch an AI agent that understands all CLI commands").option("--install","Install OpenCode if not already installed").option("--model <model>","Specify the model to use with OpenCode").action(async t=>{Vt()||(t.install?(zt()||(r("Failed to install OpenCode"),s("Please install manually: npm install -g opencode-ai@latest"),process.exit(1)),d("OpenCode installed successfully")):(r("OpenCode is not installed"),s(""),s("To install OpenCode, run one of the following:"),s(" lindoai agent --install"),s(" npm install -g opencode-ai@latest"),process.exit(1)));try{Qt(),s(`Skill file installed to: ${qe}`);}catch(i){r(`Failed to install skill file: ${i instanceof Error?i.message:"Unknown error"}`),process.exit(1);}let n=[];t.model&&n.push("--model",t.model),s("Launching OpenCode...");let e=child_process.spawn("opencode",n,{stdio:"inherit",shell:true});e.on("error",i=>{r(`Failed to launch OpenCode: ${i.message}`),process.exit(1);}),e.on("close",i=>{process.exit(i??0);});})}var eo="1.0.0";function to(){let o=new commander.Command;return o.name("lindo").description("Command-line interface for the Lindo API").version(eo,"-v, --version","Output the current version").helpOption("-h, --help","Display help for command"),o.addCommand(ne()),o.addCommand(ie()),o.addCommand(re()),o.addCommand(se()),o.addCommand(ae()),o.addCommand(ce()),o.addCommand(le()),o.addCommand(he()),o.addCommand(ye()),o.addCommand(ve()),o.addCommand(xe()),o.exitOverride(t=>{throw t.code==="commander.help"&&process.exit(0),t.code==="commander.version"&&process.exit(0),t.code==="commander.missingArgument"&&(r(t.message),process.exit(1)),t.code==="commander.unknownCommand"&&(r(t.message),s('Run "lindo --help" for available commands'),process.exit(1)),t}),o}function oo(o){r("Authentication failed"),s(""),s("Your API key may be invalid or expired."),s(""),s("To configure your API key:"),s(" 1. Run: lindo config set apiKey <your-api-key>"),s(" 2. Or set the LINDO_API_KEY environment variable"),s(""),s("To get an API key:"),s(" Visit https://app.lindo.ai/settings/api-keys");}async function no(){let o=to();try{await o.parseAsync(process.argv);}catch(t){throw t instanceof lindoai.AuthenticationError&&(oo(),process.exit(1)),t}}no().catch(o=>{o instanceof Error?r(`Unexpected error: ${o.message}`):r("An unexpected error occurred"),process.exit(1);});
|
|
596
|
+
exports.createProgram=to;
|