lindoai-cli 1.0.0 → 1.1.0

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 CHANGED
@@ -1,216 +1,23 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
- var http = require('http');
5
- var fs3 = require('fs');
6
- var path3 = require('path');
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
- }
2
+ 'use strict';var Ye=require('http'),k=require('fs'),L=require('path'),ke=require('os'),commander=require('commander'),lindoai=require('lindoai'),child_process=require('child_process'),url=require('url'),ot=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 Ye__namespace=/*#__PURE__*/_interopNamespace(Ye);var k__namespace=/*#__PURE__*/_interopNamespace(k);var L__namespace=/*#__PURE__*/_interopNamespace(L);var ke__namespace=/*#__PURE__*/_interopNamespace(ke);var ot__namespace=/*#__PURE__*/_interopNamespace(ot);var ft=Object.defineProperty;var gt=(o,e)=>()=>(o&&(e=o(o=0)),e);var ht=(o,e)=>{for(var n in e)ft(o,n,{get:e[n],enumerable:true});};var _e={};ht(_e,{LIVE_RELOAD_SCRIPT:()=>Ce,injectLiveReload:()=>ze,startLivePreviewServer:()=>zt});function ze(o){let e=/<\/body>/i,n=o.match(e);return n&&n.index!==void 0?o.slice(0,n.index)+Ce+o.slice(n.index):o+Ce}function zt(o){return new Promise((e,n)=>{let t=L__namespace.resolve(o),i=new Set,a=null,c=100;function m(){for(let u of i)try{u.write(`data: reload
31
3
 
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>
4
+ `);}catch{i.delete(u);}}function l(){a&&clearTimeout(a),a=setTimeout(()=>{m(),a=null;},c);}function f(u,h){let v=u.url||"/";if((u.method||"GET")!=="GET"){h.writeHead(405,{"Content-Type":"text/plain"}),h.end("Method Not Allowed");return}if(v==="/__live-reload"){h.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),h.write(`data: connected
5
+
6
+ `),i.add(h),u.on("close",()=>{i.delete(h);});return}if(v==="/"||v==="/index.html"){try{let x=k__namespace.readFileSync(t,"utf-8"),w=ze(x);h.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-cache"}),h.end(w);}catch(x){let w=x instanceof Error?x.message:"Unknown error";h.writeHead(500,{"Content-Type":"text/html; charset=utf-8"}),h.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>${errorMessage}</p>
11
+ <p>${w}</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}h.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),h.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=Ye__namespace.createServer(f),y=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 h=u.port;try{y=k__namespace.watch(t,b=>{b==="change"&&l();}),y.on("error",b=>{console.error(`[Live Preview] File watcher error: ${b.message}`);});}catch(b){let x=b instanceof Error?b.message:"Unknown error";console.error(`[Live Preview] Failed to watch file: ${x}`);}let v=()=>{y&&(y.close(),y=null),a&&(clearTimeout(a),a=null);for(let w of i)try{w.end();}catch{}i.clear();let b=L__namespace.join(ke__namespace.tmpdir(),"lindoai-pages-preview.pid"),x=L__namespace.join(ke__namespace.tmpdir(),"lindoai-pages-preview.port");try{k__namespace.existsSync(b)&&k__namespace.unlinkSync(b);}catch{}try{k__namespace.existsSync(x)&&k__namespace.unlinkSync(x);}catch{}g.close(()=>{process.exit(0);}),setTimeout(()=>{process.exit(0);},1e3);};process.on("SIGTERM",v),process.on("SIGINT",v),e(h);});})}var Ce,Se=gt(()=>{Ce=`<script>
214
21
  (function() {
215
22
  var eventSource = new EventSource('/__live-reload');
216
23
  eventSource.onmessage = function(event) {
@@ -222,1382 +29,184 @@ 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
- var ENV_BASE_URL = "LINDO_BASE_URL";
230
- var CONFIG_DIR = ".lindo";
231
- var CONFIG_FILE = "config.json";
232
- var DEFAULT_BASE_URL = "https://api.lindo.ai";
233
- function getConfigDir() {
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
- }
32
+ </script>`;});var bt="LINDO_API_KEY",wt="LINDO_BASE_URL",yt=".lindo",vt="config.json",xt="https://api.lindo.ai";function Me(){return L__namespace.join(ke__namespace.homedir(),yt)}function E(){return L__namespace.join(Me(),vt)}function pe(){let o=E();try{if(k__namespace.existsSync(o)){let e=k__namespace.readFileSync(o,"utf-8");return JSON.parse(e)}}catch{}return {}}function Ue(o){let e=Me(),n=E();k__namespace.existsSync(e)||k__namespace.mkdirSync(e,{recursive:true}),k__namespace.writeFileSync(n,JSON.stringify(o,null,2),"utf-8");}function C(){let o=pe(),e=process.env[bt]||o.apiKey,n=process.env[wt]||o.baseUrl||xt;return {apiKey:e,baseUrl:n}}function Ke(o){let e=pe();e.apiKey=o,Ue(e);}function We(o,e){let n=pe();switch(o){case "apiKey":n.apiKey=e;break;case "baseUrl":n.baseUrl=e;break;default:throw new Error(`Unknown configuration key: ${o}`)}Ue(n);}function He(o){let e=C();switch(o){case "apiKey":return e.apiKey;case "baseUrl":return e.baseUrl;default:return}}function S(){return !!C().apiKey}var j={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",blue:"\x1B[34m",cyan:"\x1B[36m",gray:"\x1B[90m",bold:"\x1B[1m"};function kt(){return !process.env.NO_COLOR&&process.stdout.isTTY!==false}function D(o,e){return kt()?`${e}${o}${j.reset}`:o}function p(o){console.log(D(`\u2713 ${o}`,j.green));}function s(o){console.error(D(`\u2717 ${o}`,j.red));}function r(o){console.log(D(`\u2139 ${o}`,j.blue));}function Ct(o){return JSON.stringify(o,null,2)}function _t(o){if(o==null)return "";if(Array.isArray(o)){if(o.length===0)return "No data";let e=new Set;for(let t of o)typeof t=="object"&&t!==null&&Object.keys(t).forEach(i=>e.add(i));if(e.size===0)return o.map(t=>String(t)).join(`
33
+ `);let n=Array.from(e);return St(n,o)}if(typeof o=="object"){let n=Object.entries(o);if(n.length===0)return "No data";let t=Math.max(...n.map(([i])=>i.length));return n.map(([i,a])=>{let c=i.padEnd(t),m=ue(a);return `${D(c,j.cyan)} ${m}`}).join(`
34
+ `)}return String(o)}function St(o,e){let n={};for(let c of o)n[c]=c.length;for(let c of e)if(typeof c=="object"&&c!==null){let m=c;for(let l of o){let f=ue(m[l]);n[l]=Math.max(n[l],f.length);}}let t=o.map(c=>D(c.padEnd(n[c]),j.bold)).join(" "),i=o.map(c=>"-".repeat(n[c])).join(" "),a=e.map(c=>{if(typeof c=="object"&&c!==null){let m=c;return o.map(l=>ue(m[l]).padEnd(n[l])).join(" ")}return String(c)});return [t,i,...a].join(`
35
+ `)}function ue(o){return o==null?D("-",j.gray):typeof o=="boolean"?o?D("true",j.green):D("false",j.red):typeof o=="number"?String(o):typeof o=="object"?Array.isArray(o)?`[${o.length} items]`:JSON.stringify(o):String(o)}function d(o,e){console.log(e==="json"?Ct(o):_t(o));}var qe=["apiKey","baseUrl"];function ge(){let o=new commander.Command("config").description("Manage CLI configuration");return o.command("set <key> <value>").description("Set a configuration value").action((e,n)=>{qe.includes(e)||(s(`Invalid configuration key: ${e}`),r(`Valid keys: ${qe.join(", ")}`),process.exit(1));try{We(e,n),p(`Configuration saved: ${e}`),r(`Config file: ${E()}`);}catch(t){s(`Failed to save configuration: ${t instanceof Error?t.message:String(t)}`),process.exit(1);}}),o.command("get <key>").description("Get a configuration value").option("-f, --format <format>","Output format (json, table)","table").action((e,n)=>{let t=He(e);if(t===void 0){n.format==="json"?d({key:e,value:null},n.format):r(`Configuration key '${e}' is not set`);return}if(n.format==="json")d({key:e,value:t},n.format);else {let i=e==="apiKey"?fe(t):t;console.log(`${e}: ${i}`);}}),o.command("list").description("List all configuration values").option("-f, --format <format>","Output format (json, table)","table").action(e=>{let n=C(),t={apiKey:n.apiKey?fe(n.apiKey):"(not set)",baseUrl:n.baseUrl,configFile:E()};e.format==="json"?d({apiKey:n.apiKey?fe(n.apiKey):null,baseUrl:n.baseUrl,configFile:E()},e.format):d(t,e.format);}),o.command("path").description("Show the config file path").action(()=>{console.log(E());}),o}function fe(o){return o.length<=8?"*".repeat(o.length):`${o.slice(0,4)}${"*".repeat(o.length-8)}${o.slice(-4)}`}function he(){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(e,n)=>{S()||(s("API key not configured"),r("Run: lindo config set apiKey <your-api-key>"),r("Or set the LINDO_API_KEY environment variable"),process.exit(1));let t=C(),i=new lindoai.LindoClient({apiKey:t.apiKey,baseUrl:t.baseUrl}),a;try{a=JSON.parse(n.input);}catch{s("Invalid JSON input"),r(`Example: --input '{"prompt": "Hello!"}'`),process.exit(1);}try{r(`Running agent: ${e}`);let c=await i.agents.run({agent_id:e,input:a,stream:n.stream});c.success?(p("Agent run completed"),d(c,n.format)):(s(`Agent run failed: ${c.error||"Unknown error"}`),d(c,n.format),process.exit(1));}catch(c){Ft(c);}}),o}function Ft(o){o instanceof lindoai.AuthenticationError&&(s("Authentication failed"),r("Your API key may be invalid or expired"),r("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?s(o.message):s("An unexpected error occurred"),process.exit(1);}function be(){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 e=>{let n=J();try{let t=await n.workflows.list({workflow_name:e.name,status:e.status,website_id:e.website,client_id:e.client,limit:parseInt(e.limit)});t.success?d(t.data,e.format):(s("Failed to list workflows"),process.exit(1));}catch(t){Q(t);}}),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(e,n)=>{let t=J(),i;try{i=JSON.parse(n.params);}catch{s("Invalid JSON params"),r(`Example: --params '{"page_id": "page-123"}'`),process.exit(1);}try{r(`Starting workflow: ${e}`);let a=await t.workflows.start({workflow_name:e,params:i});a.success?(p(`Workflow started: ${a.instance_id}`),d(a,n.format)):(s("Failed to start workflow"),d(a,n.format),process.exit(1));}catch(a){Q(a);}}),o.command("status <instance-id>").description("Get workflow status").option("-f, --format <format>","Output format (json, table)","table").action(async(e,n)=>{let t=J();try{let i=await t.workflows.getStatus(e);d(i,n.format);}catch(i){Q(i);}}),o.command("pause <instance-id>").description("Pause a running workflow").option("-f, --format <format>","Output format (json, table)","table").action(async(e,n)=>{let t=J();try{r(`Pausing workflow: ${e}`);let i=await t.workflows.pause(e);i.success?p(i.message):(s(i.message),process.exit(1)),d(i,n.format);}catch(i){Q(i);}}),o.command("resume <instance-id>").description("Resume a paused workflow").option("-f, --format <format>","Output format (json, table)","table").action(async(e,n)=>{let t=J();try{r(`Resuming workflow: ${e}`);let i=await t.workflows.resume(e);i.success?p(i.message):(s(i.message),process.exit(1)),d(i,n.format);}catch(i){Q(i);}}),o.command("terminate <instance-id>").description("Terminate a workflow").option("-f, --format <format>","Output format (json, table)","table").action(async(e,n)=>{let t=J();try{r(`Terminating workflow: ${e}`);let i=await t.workflows.terminate(e);i.success?p(i.message):(s(i.message),process.exit(1)),d(i,n.format);}catch(i){Q(i);}}),o}function J(){S()||(s("API key not configured"),r("Run: lindo config set apiKey <your-api-key>"),r("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=C();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function Q(o){o instanceof lindoai.AuthenticationError&&(s("Authentication failed"),r("Your API key may be invalid or expired"),r("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?s(o.message):s("An unexpected error occurred"),process.exit(1);}function we(){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 e=>{let n=P();try{let t=await n.workspace.get();d(t,e.format);}catch(t){F(t);}}),o.command("credits").description("Get workspace credit balance").option("-f, --format <format>","Output format (json, table)","table").action(async e=>{let n=P();try{let t=await n.workspace.getCredits();d(t,e.format);}catch(t){F(t);}}),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 e=>{let n=P();try{let t=await n.workspace.getClientCredits(e.client);d(t,e.format);}catch(t){F(t);}}),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 e=>{let n=P();try{let t=await n.workspace.update({workspace_name:e.name,workspace_language:e.language,webhook_url:e.webhook});t.success&&p("Workspace updated"),d(t,e.format);}catch(t){F(t);}}),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 e=>{let n=P();try{let t=await n.workspace.addTeamMember(e.email,e.role);t.success&&p("Team member added"),d(t,e.format);}catch(t){F(t);}}),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 e=>{let n=P();try{let t=await n.workspace.removeTeamMember(e.member);t.success&&p("Team member removed"),d(t,e.format);}catch(t){F(t);}}),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 e=>{let n=P(),t;try{t=JSON.parse(e.config);}catch{s("Invalid JSON config"),process.exit(1);}try{let i=await n.workspace.addIntegration({integration_type:e.type,config:t});i.success&&p("Integration added"),d(i,e.format);}catch(i){F(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 e=>{let n=P();try{let t=await n.workspace.removeIntegration(e.type);t.success&&p("Integration removed"),d(t,e.format);}catch(t){F(t);}}),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 e=>{let n=P();try{let t=await n.workspace.setupWhitelabel({domain:e.domain,subdomain_domain:e.subdomain,email_sender:e.emailSender,wl_client_register:e.enableRegister?!0:e.disableRegister?!1:void 0});t.success&&p("Whitelabel settings updated"),d(t,e.format);}catch(t){F(t);}}),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 e=>{let n=P();try{let t=await n.workspace.updateAppearance({primary_color:e.primary,secondary_color:e.secondary,theme_mode:e.mode});t.success&&p("Appearance settings updated"),d(t,e.format);}catch(t){F(t);}}),o}function P(){S()||(s("API key not configured"),r("Run: lindo config set apiKey <your-api-key>"),r("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=C();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function F(o){o instanceof lindoai.AuthenticationError&&(s("Authentication failed"),r("Your API key may be invalid or expired"),r("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?s(o.message):s("An unexpected error occurred"),process.exit(1);}function ye(){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 e=>{let n=Be();try{let t=await n.analytics.getWorkspace({from:e.from,to:e.to});d(t,e.format);}catch(t){Ge(t);}}),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 e=>{let n=Be();try{let t=await n.analytics.getWebsite({website_id:e.website,from:e.from,to:e.to});d(t,e.format);}catch(t){Ge(t);}}),o}function Be(){S()||(s("API key not configured"),r("Run: lindo config set apiKey <your-api-key>"),r("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=C();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function Ge(o){o instanceof lindoai.AuthenticationError&&(s("Authentication failed"),r("Your API key may be invalid or expired"),r("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?s(o.message):s("An unexpected error occurred"),process.exit(1);}function ve(){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 e=>{let n=te();try{let t=await n.clients.list({page:parseInt(e.page,10),search:e.search});if(e.format==="json")d(t,"json");else if(t.clients&&t.clients.length>0){console.log(`
36
+ Clients:`),console.log("--------");for(let i of t.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: ${t.total??t.clients.length}`);}else r("No clients found");}catch(t){oe(t);}}),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 e=>{let n=te();try{let t=await n.clients.create({email:e.email,website_limit:parseInt(e.limit,10)});if(t.success&&t.client)p(`Client created: ${t.client.record_id}`),d(t.client,e.format);else if(s("Failed to create client"),t.errors)for(let i of t.errors)s(` ${i}`);}catch(t){oe(t);}}),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 e=>{let n=te();try{let t=await n.clients.update({client_id:e.id,website_limit:e.limit?parseInt(e.limit,10):void 0,suspended:e.suspend?!0:e.unsuspend?!1:void 0});if(t.success)p("Client updated"),t.client&&d(t.client,e.format);else if(s("Failed to update client"),t.errors)for(let i of t.errors)s(` ${i}`);}catch(t){oe(t);}}),o.command("delete").description("Delete a workspace client").requiredOption("-i, --id <id>","Client ID").action(async e=>{let n=te();try{let t=await n.clients.delete(e.id);if(t.success)p("Client deleted");else if(s("Failed to delete client"),t.errors)for(let i of t.errors)s(` ${i}`);}catch(t){oe(t);}}),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 e=>{let n=te();try{let t=await n.clients.createMagicLink(e.email);if(t.success)p("Magic link created"),d(t,e.format);else if(s("Failed to create magic link"),t.errors)for(let i of t.errors)s(` ${i}`);}catch(t){oe(t);}}),o}function te(){S()||(s("API key not configured"),r("Run: lindo config set apiKey <your-api-key>"),r("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=C();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function oe(o){o instanceof lindoai.AuthenticationError&&(s("Authentication failed"),r("Your API key may be invalid or expired"),r("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?s(o.message):s("An unexpected error occurred"),process.exit(1);}function xe(){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 e=>{let n=O();try{let t=await n.websites.list({page:parseInt(e.page,10),search:e.search});if(e.format==="json")d(t,"json");else {let i=t.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: ${t.result?.total??i.length}`);}else r("No websites found");}}catch(t){I(t);}}),o.command("get").description("Get website details").requiredOption("-i, --id <id>","Website ID").option("-f, --format <format>","Output format (json, table)","table").action(async e=>{let n=O();try{let t=await n.websites.getDetails(e.id);d(t,e.format);}catch(t){I(t);}}),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 e=>{let n=O();try{let t=await n.websites.update({website_id:e.id,business_name:e.name,activated:e.activate?!0:e.deactivate?!1:void 0});if(t.success)p("Website updated"),t.website&&d(t.website,e.format);else if(s("Failed to update website"),t.errors)for(let i of t.errors)s(` ${i}`);}catch(t){I(t);}}),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 e=>{let n=O();try{let t=await n.websites.updateSettings(e.id,{business_name:e.name,language:e.language,business_description:e.description});t.success&&p("Website settings updated"),d(t,e.format);}catch(t){I(t);}}),o.command("delete").description("Delete a website").requiredOption("-i, --id <id>","Website ID").action(async e=>{let n=O();try{let t=await n.websites.delete(e.id);if(t.success)p("Website deleted");else if(s("Failed to delete website"),t.errors)for(let i of t.errors)s(` ${i}`);}catch(t){I(t);}}),o.command("assign").description("Assign a website to a client").requiredOption("-w, --website <id>","Website ID").requiredOption("-c, --client <id>","Client ID").action(async e=>{let n=O();try{let t=await n.websites.assign({website_id:e.website,client_id:e.client});if(t.success)p("Website assigned to client");else if(s("Failed to assign website"),t.errors)for(let i of t.errors)s(` ${i}`);}catch(t){I(t);}}),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 e=>{let n=O();try{let t=await n.websites.addDomain(e.id,e.domain);if(t.success&&(p("Domain added"),t.result?.dns_records)){console.log(`
38
+ DNS Records to configure:`);for(let i of t.result.dns_records)console.log(` ${i.record_type} ${i.host} -> ${i.value}`);}d(t,e.format);}catch(t){I(t);}}),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 e=>{let n=O();try{let t=await n.websites.removeDomain(e.id);t.success&&p("Domain removed"),d(t,e.format);}catch(t){I(t);}}),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 e=>{let n=O(),t;try{t=JSON.parse(e.config);}catch{s("Invalid JSON config"),process.exit(1);}try{let i=await n.websites.addIntegration(e.id,{integration_type:e.type,config:t});i.success&&p("Integration added"),d(i,e.format);}catch(i){I(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 e=>{let n=O();try{let t=await n.websites.removeIntegration(e.id,e.type);t.success&&p("Integration removed"),d(t,e.format);}catch(t){I(t);}}),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 e=>{let n=O();try{let t=await n.websites.addTeamMember(e.id,e.email,e.role);t.success&&p("Team member added"),d(t,e.format);}catch(t){I(t);}}),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 e=>{let n=O();try{let t=await n.websites.removeTeamMember(e.id,e.member);t.success&&p("Team member removed"),d(t,e.format);}catch(t){I(t);}}),o}function O(){S()||(s("API key not configured"),r("Run: lindo config set apiKey <your-api-key>"),r("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=C();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function I(o){o instanceof lindoai.AuthenticationError&&(s("Authentication failed"),r("Your API key may be invalid or expired"),r("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?s(o.message):s("An unexpected error occurred"),process.exit(1);}function R(o){return new Promise(e=>{let n=ke.platform(),t;switch(n){case "darwin":t=`open "${o}"`;break;case "win32":t=`start "" "${o}"`;break;default:t=`xdg-open "${o}"`;break}child_process.exec(t,i=>{e(!i);});})}var H=L__namespace.join(ke__namespace.tmpdir(),"lindoai-pages-preview.pid"),ie=L__namespace.join(ke__namespace.tmpdir(),"lindoai-pages-preview.port");function $e(){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 e=>{let n=K();try{let t=await n.pages.list(e.website,{page:parseInt(e.page,10),search:e.search});if(e.format==="json")d(t,"json");else {let i=t.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?"Yes":"No"}`),console.log("");console.log(`Total: ${i.total??i.list.length}`);}else r("No pages found");}}catch(t){W(t);}}),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 e=>{let n=K();try{let t=await n.pages.get(e.website,e.id);if(e.format==="json")d(t,"json");else {let i=t.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?"Yes":"No"}`),console.log(` Created: ${i.created_date??"N/A"}`)):s("Page not found");}}catch(t){W(t);}}),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 e=>{let n=K();try{let t=await n.pages.unpublish(e.website,e.id);e.format==="json"?d(t,"json"):t.success?(p("Page unpublished successfully"),console.log(` Page ID: ${t.result?.page_id}`)):s("Failed to unpublish page");}catch(t){W(t);}}),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 e=>{let n=K();try{let t=await n.pages.deletePage(e.website,e.id);if(e.format==="json")d(t,"json");else if(t.success){if(p("Page deleted successfully"),console.log(` Page ID: ${t.result?.page_id}`),t.result?.warnings&&t.result.warnings.length>0){console.log(" Warnings:");for(let i of t.result.warnings)console.log(` - ${i}`);}}else s("Failed to delete page");}catch(t){W(t);}}),o.command("create").description("Create a new page with live preview").argument("<website_id>","Website ID").argument("<path>","URL path for the page (e.g., /about-us)").option("--title <title>","Page title","New Page").option("--file <path>","Output file path","./page.html").option("--background","Run preview server in background").action(async(e,n,t)=>{let i=K();try{let a=t.title,c=`<!-- Hero Section -->
41
+ <section class="relative min-h-[80vh] flex items-center justify-center bg-gradient-to-br from-neutral-50 to-neutral-100 dark:from-neutral-900 dark:to-neutral-800">
42
+ <div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
43
+ <h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold text-neutral-900 dark:text-white mb-6">
44
+ ${a}
45
+ </h1>
46
+ <p class="text-lg sm:text-xl text-neutral-600 dark:text-neutral-300 mb-8 max-w-2xl mx-auto">
47
+ Start building your page by editing this template. Add sections, customize styles, and create something amazing.
48
+ </p>
49
+ <div class="flex flex-col sm:flex-row gap-4 justify-center">
50
+ <a href="#features" class="inline-flex items-center justify-center px-6 py-3 bg-emerald-600 hover:bg-emerald-700 text-white font-medium rounded-lg transition">
51
+ Get Started
52
+ </a>
53
+ <a href="#contact" class="inline-flex items-center justify-center px-6 py-3 border border-neutral-300 dark:border-neutral-600 text-neutral-700 dark:text-neutral-200 font-medium rounded-lg hover:bg-neutral-100 dark:hover:bg-neutral-700 transition">
54
+ Learn More
55
+ </a>
56
+ </div>
57
+ </div>
58
+ </section>
59
+
60
+ <!-- Features Section -->
61
+ <section id="features" class="py-20 bg-white dark:bg-neutral-900">
62
+ <div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
63
+ <div class="text-center mb-16">
64
+ <h2 class="text-3xl sm:text-4xl font-bold text-neutral-900 dark:text-white mb-4">
65
+ Features
66
+ </h2>
67
+ <p class="text-lg text-neutral-600 dark:text-neutral-300 max-w-2xl mx-auto">
68
+ Discover what makes us different.
69
+ </p>
70
+ </div>
71
+ <div class="grid md:grid-cols-3 gap-8">
72
+ <div class="p-6 bg-neutral-50 dark:bg-neutral-800 rounded-xl">
73
+ <div class="w-12 h-12 bg-emerald-100 dark:bg-emerald-900 rounded-lg flex items-center justify-center mb-4">
74
+ <svg class="w-6 h-6 text-emerald-600 dark:text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
75
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
76
+ </svg>
77
+ </div>
78
+ <h3 class="text-xl font-semibold text-neutral-900 dark:text-white mb-2">Feature One</h3>
79
+ <p class="text-neutral-600 dark:text-neutral-300">Description of your first amazing feature goes here.</p>
80
+ </div>
81
+ <div class="p-6 bg-neutral-50 dark:bg-neutral-800 rounded-xl">
82
+ <div class="w-12 h-12 bg-emerald-100 dark:bg-emerald-900 rounded-lg flex items-center justify-center mb-4">
83
+ <svg class="w-6 h-6 text-emerald-600 dark:text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
84
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
85
+ </svg>
86
+ </div>
87
+ <h3 class="text-xl font-semibold text-neutral-900 dark:text-white mb-2">Feature Two</h3>
88
+ <p class="text-neutral-600 dark:text-neutral-300">Description of your second amazing feature goes here.</p>
89
+ </div>
90
+ <div class="p-6 bg-neutral-50 dark:bg-neutral-800 rounded-xl">
91
+ <div class="w-12 h-12 bg-emerald-100 dark:bg-emerald-900 rounded-lg flex items-center justify-center mb-4">
92
+ <svg class="w-6 h-6 text-emerald-600 dark:text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
93
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/>
94
+ </svg>
95
+ </div>
96
+ <h3 class="text-xl font-semibold text-neutral-900 dark:text-white mb-2">Feature Three</h3>
97
+ <p class="text-neutral-600 dark:text-neutral-300">Description of your third amazing feature goes here.</p>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </section>
102
+
103
+ <!-- Contact Section -->
104
+ <section id="contact" class="py-20 bg-neutral-50 dark:bg-neutral-800">
105
+ <div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
106
+ <h2 class="text-3xl sm:text-4xl font-bold text-neutral-900 dark:text-white mb-4">
107
+ Get in Touch
108
+ </h2>
109
+ <p class="text-lg text-neutral-600 dark:text-neutral-300 mb-8">
110
+ Have questions? We'd love to hear from you.
111
+ </p>
112
+ <a href="mailto:hello@example.com" class="inline-flex items-center justify-center px-8 py-4 bg-emerald-600 hover:bg-emerald-700 text-white font-medium rounded-lg transition text-lg">
113
+ Contact Us
114
+ </a>
115
+ </div>
116
+ </section>`,m={theme:{mode:"Dark",direction:"ltr",main_classes:"bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-100",animations_deactivated:!1},should_convert:!0},l={page_title:a,meta_description:"",social_title:a,social_description:"",noindex:!1,nofollow:!1};r("Creating page...");let f=await i.pages.create(e,{html:c,path:n,settings:m,template_name:a,seo:l});f.success||(s("Failed to create page"),process.exit(1));let g=f.result.page_id;p(`Page created: ${g}`);let y=`<!DOCTYPE html>
117
+ <html class="dark" lang="en">
118
+ <head>
119
+ <meta charset="UTF-8">
120
+ <meta name="robots" content="max-snippet:-1, max-image-preview:large, max-video-preview:-1">
121
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
301
122
 
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
- }
123
+ <title>${a}</title>
124
+ <meta name="description" content="">
418
125
 
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
- }
126
+ <meta property="og:title" content="${a}">
127
+ <meta property="og:description" content="">
128
+ <meta property="og:type" content="website">
1305
129
 
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 = `
130
+ <meta name="twitter:card" content="summary_large_image">
131
+ <meta name="twitter:title" content="${a}">
132
+ <meta name="twitter:description" content="">
133
+
134
+ <!-- Tailwind CSS v4 CDN for preview -->
135
+ <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
136
+
137
+ <!-- Motion for animations -->
138
+ <script src="https://cdn.jsdelivr.net/npm/motion@latest/dist/motion.js"></script>
139
+ <script src="https://lnui.pages.dev/motion-animate.js"></script>
140
+
141
+ <!-- Page Custom Code (Header) -->
142
+
143
+ </head>
144
+ <body>
145
+ <!-- ========== MAIN CONTENT ========== -->
146
+ <main dir="ltr" lindo-main-content id="content" role="main" class="bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-100">
147
+
148
+ ${c}
149
+
150
+ </main>
151
+ <!-- ========== END MAIN CONTENT ========== -->
152
+
153
+ <!-- Config script -->
154
+ <script src="https://cdn.ln-cdn.com/staging/js/config2.js"></script>
155
+
156
+ <!-- Page Custom Code (Footer) -->
157
+ </body>
158
+ </html>`,u=L__namespace.resolve(t.file);k__namespace.writeFileSync(u,y,"utf-8"),p(`HTML saved to ${u}`),await Ve(),t.background?await Je(u):await Qe(u),console.log(""),r(`To save changes: lindoai pages update ${e} ${g} --html-file ${t.file}`);}catch(a){W(a);}}),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(e,n,t)=>{let i=K();try{r("Fetching page HTML...");let a=await i.pages.getHtml(e,n);a.result||(s("Page not found"),process.exit(1));let c=a.result.html;c||(s("Page has no HTML content"),r("Make sure the page has been created with HTML content"),process.exit(1));let m=a.result.name||"Page",l=a.result.seo||{},f=a.result.settings||{},g=a.result.custom_codes||{},y=f.theme||{},u=y.mode!=="Light",h=y.direction||"ltr",v=y.main_classes||"",b=y.animations_deactivated||!1,x=`<!DOCTYPE html>
159
+ <html class="${u?"dark":""}" lang="en">
160
+ <head>
161
+ <meta charset="UTF-8">
162
+ <meta name="robots" content="max-snippet:-1, max-image-preview:large, max-video-preview:-1 ${l.noindex?"noindex":""} ${l.nofollow?"nofollow":""}">
163
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
164
+
165
+ <title>${l.page_title||m}</title>
166
+ <meta name="description" content="${l.meta_description||""}">
167
+
168
+ <meta property="og:title" content="${l.social_title||l.page_title||m}">
169
+ <meta property="og:description" content="${l.social_description||l.meta_description||""}">
170
+ ${l.social_image?`<meta property="og:image" content="${l.social_image}">`:""}
171
+ <meta property="og:type" content="website">
172
+ ${l.canonical_url?`<meta property="og:url" content="${l.canonical_url}">`:""}
173
+
174
+ <meta name="twitter:card" content="summary_large_image">
175
+ <meta name="twitter:title" content="${l.social_title||l.page_title||m}">
176
+ <meta name="twitter:description" content="${l.social_description||l.meta_description||""}">
177
+ ${l.social_image?`<meta name="twitter:image" content="${l.social_image}">`:""}
178
+
179
+ <!-- Tailwind CSS v4 CDN for preview -->
180
+ <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
181
+
182
+ ${b?"":`
183
+ <!-- Motion for animations -->
184
+ <script src="https://cdn.jsdelivr.net/npm/motion@latest/dist/motion.js"></script>
185
+ <script src="https://lnui.pages.dev/motion-animate.js"></script>
186
+ `}
187
+
188
+ ${l.canonical_url?`<link rel="canonical" href="${l.canonical_url}">`:""}
189
+
190
+ <!-- Page Custom Code (Header) -->
191
+ ${g.header||""}
192
+
193
+ </head>
194
+ <body>
195
+ <!-- ========== MAIN CONTENT ========== -->
196
+ <main dir="${h}" lindo-main-content id="content" role="main" class="${v}">
197
+
198
+ ${c}
199
+
200
+ </main>
201
+ <!-- ========== END MAIN CONTENT ========== -->
202
+
203
+ <!-- Config script -->
204
+ <script src="https://cdn.ln-cdn.com/staging/js/config2.js"></script>
205
+
206
+ <!-- Page Custom Code (Footer) -->
207
+ ${g.footer||""}
208
+ </body>
209
+ </html>`,w=L__namespace.resolve(t.file);k__namespace.writeFileSync(w,x,"utf-8"),p(`HTML saved to ${w}`),await Ve(),t.background?await Je(w):await Qe(w);}catch(a){W(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(e,n,t)=>{let i=K();try{t.htmlFile||(s("--html-file option is required"),r("Usage: lindoai pages update <website_id> <page_id> --html-file <path>"),process.exit(1));let a=L__namespace.resolve(t.htmlFile);k__namespace.existsSync(a)||(s(`File not found: ${a}`),process.exit(1));let c=k__namespace.readFileSync(a,"utf-8");r(`Read ${c.length} bytes from ${a}`);let m=c.match(/<main[^>]*>([\s\S]*)<\/main>/i),l=m?m[1].trim():c;m&&r("Extracted content from <main> tag");let f=c.match(/<title>([^<]*)<\/title>/i),g=f?f[1].trim():void 0;g&&r(`Extracted page title: ${g}`);let y="",u="",h=c.match(/<!-- Page Custom Code \(Header\) -->\s*([\s\S]*?)\s*<\/head>/i);h&&h[1].trim()&&(y=h[1].trim(),r("Extracted header custom code"));let v=c.match(/<!-- Page Custom Code \(Footer\) -->\s*([\s\S]*?)\s*<\/body>/i);v&&v[1].trim()&&(u=v[1].trim(),r("Extracted footer custom code"));let b=c.match(/<html[^>]*class="([^"]*)"/i),x=b?b[1].includes("dark"):!0,w=c.match(/<main[^>]*>/i),Z="ltr",ee="";if(w){let De=w[0].match(/dir="([^"]*)"/i);De&&(Z=De[1]);let Re=w[0].match(/class="([^"]*)"/i);Re&&(ee=Re[1]);}let ae=!c.includes("motion@latest"),B=c.match(/<meta\s+name="description"\s+content="([^"]*)"/i),G=c.match(/<meta\s+property="og:title"\s+content="([^"]*)"/i),Y=c.match(/<meta\s+property="og:description"\s+content="([^"]*)"/i),z=c.match(/<meta\s+property="og:image"\s+content="([^"]*)"/i),V=c.match(/<link\s+rel="canonical"\s+href="([^"]*)"/i),dt=c.match(/noindex/i),mt=c.match(/nofollow/i),$={};g&&($.page_title=g),B&&B[1]&&($.meta_description=B[1]),G&&G[1]&&($.social_title=G[1]),Y&&Y[1]&&($.social_description=Y[1]),z&&z[1]&&($.social_image=z[1]),V&&V[1]&&($.canonical_url=V[1]),$.noindex=!!dt,$.nofollow=!!mt,Object.keys($).length>2&&r("Extracted SEO metadata");let se=await i.pages.get(e,n);se.result||(s("Page not found"),process.exit(1));let pt=se.result.path,ut={...se.result.settings||{},theme:{...se.result.settings?.theme||{},mode:x?"Dark":"Light",direction:Z,main_classes:ee,animations_deactivated:ae},should_convert:!0},ce={};y&&(ce.header=y),u&&(ce.footer=u),r("Updating page...");let de=await i.pages.publish(e,n,{html:l,path:pt,settings:ut,template_name:g,custom_codes:Object.keys(ce).length>0?ce:void 0,seo:Object.keys($).length>0?$:void 0});t.format==="json"?d(de,"json"):de.success?(p("Page updated successfully"),console.log(` Page ID: ${de.result?.page_id}`)):s("Failed to update page");}catch(a){W(a);}}),o.command("stop-preview").description("Stop the background preview server").action(async()=>{try{if(!k__namespace.existsSync(H)){r("No preview server is running");return}let e=parseInt(k__namespace.readFileSync(H,"utf-8").trim(),10);if(isNaN(e)){s("Invalid PID file"),Oe();return}try{process.kill(e,"SIGTERM"),p(`Preview server (PID ${e}) stopped`);}catch(n){n.code==="ESRCH"?r("Preview server process not found (may have already stopped)"):s(`Failed to stop preview server: ${n.message}`);}Oe();}catch(e){e instanceof Error?s(e.message):s("An unexpected error occurred"),process.exit(1);}}),o}function K(){S()||(s("API key not configured"),r("Run: lindo config set apiKey <your-api-key>"),r("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=C();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function W(o){o instanceof lindoai.AuthenticationError&&(s("Authentication failed"),r("Your API key may be invalid or expired"),r("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?s(o.message):s("An unexpected error occurred"),process.exit(1);}function Oe(){try{k__namespace.existsSync(H)&&k__namespace.unlinkSync(H);}catch{}try{k__namespace.existsSync(ie)&&k__namespace.unlinkSync(ie);}catch{}}async function Ve(){if(k__namespace.existsSync(H)){try{let o=parseInt(k__namespace.readFileSync(H,"utf-8").trim(),10);if(!isNaN(o))try{process.kill(o,"SIGTERM"),r(`Terminated existing preview server (PID ${o})`),await new Promise(e=>setTimeout(e,500));}catch{}}catch{}Oe();}}async function Je(o){let e=L__namespace.resolve(o),n=`
1601
210
  const http = require('node:http');
1602
211
  const fs = require('node:fs');
1603
212
  const path = require('node:path');
@@ -1617,7 +226,7 @@ async function startBackgroundPreviewServer(filePath) {
1617
226
  })();
1618
227
  </script>\`;
1619
228
 
1620
- const filePath = ${JSON.stringify(absolutePath)};
229
+ const filePath = ${JSON.stringify(e)};
1621
230
  const pidFile = path.join(os.tmpdir(), 'lindoai-pages-preview.pid');
1622
231
  const portFile = path.join(os.tmpdir(), 'lindoai-pages-preview.port');
1623
232
  const sseClients = new Set();
@@ -1717,350 +326,31 @@ async function startBackgroundPreviewServer(filePath) {
1717
326
  });
1718
327
  } catch {}
1719
328
  });
1720
- `;
1721
- const child = child_process.spawn(process.execPath, ["-e", serverCode], {
1722
- detached: true,
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 = `
329
+ `,t=child_process.spawn(process.execPath,["-e",n],{detached:true,stdio:"ignore"});t.unref();let i=null;for(let m=0;m<50;m++)if(await new Promise(l=>setTimeout(l,100)),k__namespace.existsSync(ie))try{if(i=parseInt(k__namespace.readFileSync(ie,"utf-8").trim(),10),!isNaN(i))break}catch{}i||(s("Failed to start preview server"),process.exit(1));let a=`http://127.0.0.1:${i}/`;await R(a)||r(`Could not open browser. Visit: ${a}`),p("Preview server started in background"),console.log(` URL: ${a}`),console.log(` PID: ${t.pid}`),console.log(""),r("To update the page: lindoai pages update <website_id> <page_id> --html-file <path>"),r("To stop the server: lindoai pages stop-preview");}async function Qe(o){let{startLivePreviewServer:e}=await Promise.resolve().then(()=>(Se(),_e));r("Starting preview server...");let n=await e(o),t=`http://127.0.0.1:${n}/`;k__namespace.writeFileSync(H,process.pid.toString(),"utf-8"),k__namespace.writeFileSync(ie,n.toString(),"utf-8"),await R(t)||r(`Could not open browser. Visit: ${t}`),p("Preview server started"),console.log(` URL: ${t}`),console.log(""),r("Press Ctrl+C to stop the server"),r("Edit the HTML file and save to see changes in the browser");}var q=L__namespace.join(ke__namespace.tmpdir(),"lindoai-blogs-preview.pid"),re=L__namespace.join(ke__namespace.tmpdir(),"lindoai-blogs-preview.port");function Te(){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 e=>{let n=M();try{let t=await n.blogs.list(e.website,{page:parseInt(e.page,10),search:e.search});if(e.format==="json")d(t,"json");else {let i=t.result;if(i?.list&&i.list.length>0){console.log(`
330
+ 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 r("No blogs found");}}catch(t){U(t);}}),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 e=>{let n=M();try{let t=await n.blogs.get(e.website,e.id);if(e.format==="json")d(t,"json");else {let i=t.result;i?(console.log(`
331
+ 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"}`)):s("Blog not found");}}catch(t){U(t);}}),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 e=>{let n=M();try{let t=await n.blogs.publish(e.website,e.id);e.format==="json"?d(t,"json"):t.success?(p("Blog published successfully"),console.log(` Blog ID: ${t.result?.blog_id}`),console.log(` Published at: ${t.result?.publish_date?new Date(t.result.publish_date*1e3).toISOString():"N/A"}`)):s("Failed to publish blog");}catch(t){U(t);}}),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 e=>{let n=M();try{let t=await n.blogs.unpublish(e.website,e.id);e.format==="json"?d(t,"json"):t.success?(p("Blog unpublished successfully"),console.log(` Blog ID: ${t.result?.blog_id}`)):s("Failed to unpublish blog");}catch(t){U(t);}}),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 e=>{let n=M();try{let t=await n.blogs.delete(e.website,e.id);if(e.format==="json")d(t,"json");else if(t.success){if(p("Blog deleted successfully"),console.log(` Blog ID: ${t.result?.blog_id}`),t.result?.warnings&&t.result.warnings.length>0){console.log(" Warnings:");for(let i of t.result.warnings)console.log(` - ${i}`);}}else s("Failed to delete blog");}catch(t){U(t);}}),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(e,n,t)=>{let i=M();try{r("Fetching blog content...");let a=await i.blogs.getHtml(e,n);a.result||(s("Blog not found"),process.exit(1));let c=a.result,m=await i.websites.getDetails(e);m.result||(s("Website not found"),process.exit(1));let l=m.result,f=c.seo||{},g=c.blog_settings||{},y=c.blog_content||"<p>Start writing your blog content here...</p>",u=tt({seo:f,blogSettings:g,blogContent:y,websiteName:l.business_name||"Blog",blogPath:l.theme?.blog_path||"blog"}),h=L__namespace.resolve(t.file);k__namespace.writeFileSync(h,u,"utf-8"),p(`HTML saved to ${h}`),await Xe(),t.background?await Ze(h):await et(h),console.log(""),r(`To save changes: lindoai blogs update ${e} ${n} --html-file ${t.file}`);}catch(a){U(a);}}),o.command("update").description("Update a blog from HTML file").argument("<website_id>","Website ID").argument("<blog_id>","Blog ID").option("--html-file <path>","Path to local HTML file").option("--md-file <path>","Path to local Markdown file").option("-f, --format <format>","Output format (json, table)","table").action(async(e,n,t)=>{let i=M();try{!t.htmlFile&&!t.mdFile&&(s("Either --html-file or --md-file option is required"),r("Usage: lindoai blogs update <website_id> <blog_id> --html-file <path>"),r(" or: lindoai blogs update <website_id> <blog_id> --md-file <path>"),process.exit(1));let a,c={},m={};if(t.mdFile){let v=L__namespace.resolve(t.mdFile);k__namespace.existsSync(v)||(s(`File not found: ${v}`),process.exit(1));let b=k__namespace.readFileSync(v,"utf-8");r(`Read ${b.length} bytes from ${v}`);let{content:x,frontmatter:w}=io(b);a=ro(x),w.title&&(c.page_title=w.title),w.description&&(c.meta_description=w.description),w.image&&(c.social_image=w.image),w.author&&(m.author=w.author),w.excerpt&&(m.excerpt=w.excerpt),w.category&&(m.category=w.category),w.date&&(m.publish_date=w.date);}else {let v=L__namespace.resolve(t.htmlFile);k__namespace.existsSync(v)||(s(`File not found: ${v}`),process.exit(1));let b=k__namespace.readFileSync(v,"utf-8");r(`Read ${b.length} bytes from ${v}`);let x=b.match(/<article[^>]*class="[^"]*prose[^"]*"[^>]*>([\s\S]*?)<\/article>/i);a=x?x[1].trim():"",a||(s("Could not extract blog content from HTML"),r('Make sure the blog content is inside <article class="...prose...">...</article>'),process.exit(1));let w=b.match(/<title>([^<]*)<\/title>/i),Z=b.match(/<meta\s+name="description"\s+content="([^"]*)"/i),ee=b.match(/<meta\s+property="og:image"\s+content="([^"]*)"/i);w&&(c.page_title=w[1].trim()),Z&&(c.meta_description=Z[1]),ee&&(c.social_image=ee[1]);let ae=b.match(/id="blog-author-display"[^>]*>([^<]*)</i),B=b.match(/class="[^"]*bg-blue-500\/10[^"]*"[^>]*>([^<]*)</i),G=b.match(/class="[^"]*border-l-4 border-blue-500[^"]*"[^>]*>([\s\S]*?)<\/div>/i),Y=b.match(/<time[^>]*>([^<]*)<\/time>/i),z=b.match(/<span>(\d+\s*min\s*read)<\/span>/i),V=b.match(/<div class="aspect-video[^"]*"[^>]*>\s*<img[^>]*src="([^"]*)"/i);ae&&(m.author=ae[1].trim()),B&&(m.category=B[1].trim()),G&&(m.excerpt=G[1].trim()),Y&&(m.publish_date=Y[1].trim()),z&&(m.read_time=z[1].trim()),V&&!c.social_image&&(c.social_image=V[1]),r("Extracted blog content and metadata");}let l=await i.blogs.get(e,n);l.result||(s("Blog not found"),process.exit(1));let f=l.result,g=f.path,y={page_title:c.page_title||f.seo?.page_title||"Untitled",meta_description:c.meta_description||f.seo?.meta_description,social_title:c.social_title||f.seo?.social_title,social_description:c.social_description||f.seo?.social_description,social_image:c.social_image||f.seo?.social_image},u={author:m.author||f.blog_settings?.author||"Anonymous",excerpt:m.excerpt||f.blog_settings?.excerpt,category:m.category||f.blog_settings?.category,publish_date:m.publish_date||f.blog_settings?.publish_date,read_time:m.read_time||f.blog_settings?.read_time,author_image:m.author_image||f.blog_settings?.author_image};r("Updating blog...");let h=await i.blogs.publish(e,n,{path:g,blog_content:a,seo:y,blog_settings:u});t.format==="json"?d(h,"json"):h.success?(p("Blog updated successfully"),console.log(` Blog ID: ${h.result?.blog_id}`)):s("Failed to update blog");}catch(a){U(a);}}),o.command("create").description("Create a new blog with live preview").argument("<website_id>","Website ID").argument("<path>","URL path for the blog (e.g., /blog/my-first-post)").option("--title <title>","Blog title","New Blog Post").option("--author <author>","Author name","Anonymous").option("--file <path>","Output file path","./blog.html").option("--background","Run preview server in background").action(async(e,n,t)=>{let i=M();try{let a=await i.websites.getDetails(e);a.result||(s("Website not found"),process.exit(1));let c=a.result,m=t.title,l=t.author,f=new Date().toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"}),g=`<h2>Introduction</h2>
332
+ <p>Welcome to your new blog post! This is a starter template to help you get started. Edit this content to create your own amazing blog post.</p>
333
+
334
+ <h2>Getting Started</h2>
335
+ <p>Here are some tips for writing great blog content:</p>
336
+ <ul>
337
+ <li>Start with a compelling introduction that hooks your readers</li>
338
+ <li>Break up your content with headings and subheadings</li>
339
+ <li>Use bullet points and numbered lists for easy scanning</li>
340
+ <li>Include relevant images to illustrate your points</li>
341
+ <li>End with a clear call-to-action or conclusion</li>
342
+ </ul>
343
+
344
+ <h2>Adding Code Examples</h2>
345
+ <p>If you're writing technical content, you can include code blocks:</p>
346
+ <pre><code>function greet(name) {
347
+ return \`Hello, \${name}!\`;
348
+ }
349
+
350
+ console.log(greet('World'));</code></pre>
351
+
352
+ <h2>Conclusion</h2>
353
+ <p>Now it's your turn! Replace this content with your own ideas and publish your blog post to share it with the world.</p>`,y={page_title:m,meta_description:`Read ${m} by ${l}`,social_title:m,social_description:`Read ${m} by ${l}`},u={author:l,excerpt:"A new blog post. Edit this excerpt to provide a summary of your content.",publish_date:f,read_time:"3 min read"};r("Creating blog...");let h=await i.blogs.create(e,{path:n,blog_content:g,seo:y,blog_settings:u});h.success||(s("Failed to create blog"),process.exit(1));let v=h.result.blog_id;p(`Blog created: ${v}`);let b=tt({seo:y,blogSettings:u,blogContent:g,websiteName:c.business_name||"Blog",blogPath:c.theme?.blog_path||"blog"}),x=L__namespace.resolve(t.file);k__namespace.writeFileSync(x,b,"utf-8"),p(`HTML saved to ${x}`),await Xe(),t.background?await Ze(x):await et(x),console.log(""),r(`To save changes: lindoai blogs update ${e} ${v} --html-file ${t.file}`);}catch(a){U(a);}}),o.command("stop-preview").description("Stop the background preview server").action(async()=>{try{if(!k__namespace.existsSync(q)){r("No preview server is running");return}let e=parseInt(k__namespace.readFileSync(q,"utf-8").trim(),10);if(isNaN(e)){s("Invalid PID file"),Pe();return}try{process.kill(e,"SIGTERM"),p(`Preview server (PID ${e}) stopped`);}catch(n){n.code==="ESRCH"?r("Preview server process not found (may have already stopped)"):s(`Failed to stop preview server: ${n.message}`);}Pe();}catch(e){e instanceof Error?s(e.message):s("An unexpected error occurred"),process.exit(1);}}),o}function M(){S()||(s("API key not configured"),r("Run: lindo config set apiKey <your-api-key>"),r("Or set the LINDO_API_KEY environment variable"),process.exit(1));let o=C();return new lindoai.LindoClient({apiKey:o.apiKey,baseUrl:o.baseUrl})}function U(o){o instanceof lindoai.AuthenticationError&&(s("Authentication failed"),r("Your API key may be invalid or expired"),r("Run: lindo config set apiKey <your-api-key>"),process.exit(1)),o instanceof Error?s(o.message):s("An unexpected error occurred"),process.exit(1);}function Pe(){try{k__namespace.existsSync(q)&&k__namespace.unlinkSync(q);}catch{}try{k__namespace.existsSync(re)&&k__namespace.unlinkSync(re);}catch{}}async function Xe(){if(k__namespace.existsSync(q)){try{let o=parseInt(k__namespace.readFileSync(q,"utf-8").trim(),10);if(!isNaN(o))try{process.kill(o,"SIGTERM"),r(`Terminated existing preview server (PID ${o})`),await new Promise(e=>setTimeout(e,500));}catch{}}catch{}Pe();}}async function Ze(o){let e=L__namespace.resolve(o),n=`
2064
354
  const http = require('node:http');
2065
355
  const fs = require('node:fs');
2066
356
  const path = require('node:path');
@@ -2080,7 +370,7 @@ async function startBackgroundPreviewServer2(filePath) {
2080
370
  })();
2081
371
  </script>\`;
2082
372
 
2083
- const filePath = ${JSON.stringify(absolutePath)};
373
+ const filePath = ${JSON.stringify(e)};
2084
374
  const pidFile = path.join(os.tmpdir(), 'lindoai-blogs-preview.pid');
2085
375
  const portFile = path.join(os.tmpdir(), 'lindoai-blogs-preview.port');
2086
376
  const sseClients = new Set();
@@ -2180,81 +470,187 @@ async function startBackgroundPreviewServer2(filePath) {
2180
470
  });
2181
471
  } catch {}
2182
472
  });
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
- }
473
+ `,t=child_process.spawn(process.execPath,["-e",n],{detached:true,stdio:"ignore"});t.unref();let i=null;for(let m=0;m<50;m++)if(await new Promise(l=>setTimeout(l,100)),k__namespace.existsSync(re))try{if(i=parseInt(k__namespace.readFileSync(re,"utf-8").trim(),10),!isNaN(i))break}catch{}i||(s("Failed to start preview server"),process.exit(1));let a=`http://127.0.0.1:${i}/`;await R(a)||r(`Could not open browser. Visit: ${a}`),p("Preview server started in background"),console.log(` URL: ${a}`),console.log(` PID: ${t.pid}`),console.log(""),r("To update the blog: lindoai blogs update <website_id> <blog_id> --html-file <path>"),r("To stop the server: lindoai blogs stop-preview");}async function et(o){let{startLivePreviewServer:e}=await Promise.resolve().then(()=>(Se(),_e));r("Starting preview server...");let n=await e(o),t=`http://127.0.0.1:${n}/`;k__namespace.writeFileSync(q,process.pid.toString(),"utf-8"),k__namespace.writeFileSync(re,n.toString(),"utf-8"),await R(t)||r(`Could not open browser. Visit: ${t}`),p("Preview server started"),console.log(` URL: ${t}`),console.log(""),r("Press Ctrl+C to stop the server"),r("Edit the HTML file and save to see changes in the browser");}function no(o){return o?o.split(" ").map(e=>e.charAt(0).toUpperCase()).slice(0,2).join(""):"?"}function tt(o){let{seo:e,blogSettings:n,blogContent:t,websiteName:i,blogPath:a}=o;return `<!DOCTYPE html>
474
+ <html class="dark" lang="en">
475
+ <head>
476
+ <meta charset="UTF-8">
477
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
478
+
479
+ <title>${e.page_title||"Blog Post"}</title>
480
+ <meta name="description" content="${e.meta_description||""}">
481
+
482
+ <meta property="og:title" content="${e.social_title||e.page_title||""}">
483
+ <meta property="og:description" content="${e.social_description||e.meta_description||""}">
484
+ ${e.social_image?`<meta property="og:image" content="${e.social_image}">`:""}
485
+ <meta property="og:type" content="article">
486
+
487
+ <meta name="twitter:card" content="summary_large_image">
488
+ <meta name="twitter:title" content="${e.social_title||e.page_title||""}">
489
+ <meta name="twitter:description" content="${e.social_description||e.meta_description||""}">
490
+ ${e.social_image?`<meta name="twitter:image" content="${e.social_image}">`:""}
491
+
492
+ <!-- Tailwind CSS v4 CDN for preview -->
493
+ <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
494
+
495
+ <style>
496
+ /* Required default styles for content */
497
+ blockquote, dd, dl, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {
498
+ margin: revert;
499
+ }
500
+ menu, ol, ul {
501
+ list-style: revert;
502
+ margin: revert;
503
+ padding: revert;
504
+ }
505
+ /* Reading progress bar */
506
+ #reading-progress {
507
+ position: fixed;
508
+ top: 0;
509
+ left: 0;
510
+ width: 0%;
511
+ height: 3px;
512
+ background: linear-gradient(90deg, #3b82f6, #8b5cf6);
513
+ z-index: 1000;
514
+ transition: width 0.25s ease;
515
+ }
516
+ /* Custom scrollbar for dark mode */
517
+ .dark ::-webkit-scrollbar { width: 8px; }
518
+ .dark ::-webkit-scrollbar-track { background: #1f2937; }
519
+ .dark ::-webkit-scrollbar-thumb { background: #4b5563; border-radius: 4px; }
520
+ .dark ::-webkit-scrollbar-thumb:hover { background: #6b7280; }
521
+ </style>
522
+ </head>
523
+ <body>
524
+ <div class="bg-white dark:bg-neutral-950 text-gray-900 dark:text-neutral-100 font-sans antialiased min-h-screen">
525
+ <!-- Reading Progress Bar -->
526
+ <div id="reading-progress"></div>
527
+
528
+ <!-- Navigation -->
529
+ <nav class="fixed w-full top-0 z-50 bg-white/90 dark:bg-neutral-950/90 backdrop-blur-lg border-b border-gray-200 dark:border-neutral-800">
530
+ <div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
531
+ <div class="flex justify-between items-center py-4">
532
+ <div class="flex items-center space-x-2">
533
+ <a href="/${a}" class="flex items-center space-x-2 text-gray-600 dark:text-neutral-300 hover:text-gray-900 dark:hover:text-white transition-colors">
534
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
535
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
536
+ </svg>
537
+ </a>
538
+ </div>
539
+ <div class="flex items-center space-x-4">
540
+ <button onclick="copyLink()" class="text-gray-500 dark:text-neutral-400 hover:text-green-500 dark:hover:text-green-400 transition-colors">
541
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
542
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
543
+ </svg>
544
+ </button>
545
+ <button onclick="toggleTheme()" class="text-gray-500 dark:text-neutral-400 hover:text-gray-900 dark:hover:text-white p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-neutral-800 transition-all hover:scale-110">
546
+ <svg class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
547
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
548
+ </svg>
549
+ <svg class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
550
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
551
+ </svg>
552
+ </button>
553
+ </div>
554
+ </div>
555
+ </div>
556
+ </nav>
557
+
558
+ <!-- Article Header -->
559
+ <header class="pt-24 pb-8 px-4 sm:px-6 lg:px-8">
560
+ <article class="max-w-4xl mx-auto">
561
+ ${n.category?`
562
+ <div class="mb-6">
563
+ <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-500/10 text-blue-600 dark:text-blue-400 border border-blue-500/20">
564
+ ${n.category}
565
+ </span>
566
+ </div>
567
+ `:""}
568
+
569
+ <h1 class="text-3xl sm:text-4xl lg:text-5xl font-bold font-display leading-tight mb-6 text-gray-900 dark:text-neutral-100">
570
+ ${e.page_title||"Untitled"}
571
+ </h1>
572
+
573
+ <div class="flex flex-wrap items-center gap-4 text-sm text-gray-500 dark:text-neutral-400 mb-8">
574
+ <div class="flex items-center space-x-2">
575
+ <div id="blog-author-avatar" class="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white text-xs font-semibold">
576
+ ${no(n.author||"Anonymous")}
577
+ </div>
578
+ <span id="blog-author-display" class="font-medium text-neutral-200">
579
+ ${n.author||"Anonymous"}
580
+ </span>
581
+ </div>
582
+ <span>\u2022</span>
583
+ <time>${n.publish_date||new Date().toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"})}</time>
584
+ <span>\u2022</span>
585
+ <span>${n.read_time||"4 min read"}</span>
586
+ </div>
587
+
588
+ ${e.social_image?`
589
+ <div class="aspect-video rounded-xl overflow-hidden mb-8">
590
+ <img src="${e.social_image}" alt="" class="w-full h-full object-cover">
591
+ </div>
592
+ `:""}
593
+
594
+ ${n.excerpt?`
595
+ <div class="text-xl text-gray-700 dark:text-neutral-300 leading-relaxed mb-8 italic border-l-4 border-blue-500 pl-6">
596
+ ${n.excerpt}
597
+ </div>
598
+ `:""}
599
+ </article>
600
+ </header>
601
+
602
+ <!-- Article Content -->
603
+ <main class="px-4 sm:px-6 lg:px-8 pb-16">
604
+ <article class="max-w-3xl mx-auto prose prose-lg prose-gray dark:prose-invert">
605
+ ${t}
606
+ </article>
607
+ </main>
608
+
609
+ <!-- Footer -->
610
+ <footer class="bg-gray-50 dark:bg-neutral-900 border-t border-gray-200 dark:border-neutral-800 py-8 px-4 sm:px-6 lg:px-8">
611
+ <div class="max-w-4xl mx-auto text-center">
612
+ <p class="text-gray-600 dark:text-neutral-400">&copy; ${new Date().getFullYear()}. ${i}.</p>
613
+ </div>
614
+ </footer>
615
+ </div>
616
+
617
+ <script>
618
+ function toggleTheme() {
619
+ const html = document.documentElement;
620
+ if (html.classList.contains('dark')) {
621
+ html.classList.remove('dark');
622
+ localStorage.setItem('theme', 'light');
623
+ } else {
624
+ html.classList.add('dark');
625
+ localStorage.setItem('theme', 'dark');
626
+ }
2198
627
  }
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
- }
628
+ document.addEventListener('DOMContentLoaded', () => {
629
+ const savedTheme = localStorage.getItem('theme');
630
+ if (savedTheme === 'dark' || !savedTheme) {
631
+ document.documentElement.classList.add('dark');
632
+ }
633
+ });
634
+ window.addEventListener('scroll', () => {
635
+ const scrollTop = window.pageYOffset;
636
+ const docHeight = document.documentElement.scrollHeight - window.innerHeight;
637
+ const scrollPercent = (scrollTop / docHeight) * 100;
638
+ document.getElementById('reading-progress').style.width = scrollPercent + '%';
639
+ });
640
+ function copyLink() {
641
+ navigator.clipboard.writeText(window.location.href).then(() => {
642
+ alert('Link copied to clipboard!');
643
+ });
644
+ }
645
+ </script>
2239
646
 
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>
647
+ <!-- Config script -->
648
+ <script src="https://cdn.ln-cdn.com/staging/js/config2.js"></script>
649
+ </body>
650
+ </html>`}function io(o){let e={},n=o;if(o.startsWith("---")){let t=o.indexOf("---",3);if(t!==-1){let i=o.slice(3,t).trim();n=o.slice(t+3).trim();for(let a of i.split(`
651
+ `)){let c=a.indexOf(":");if(c!==-1){let m=a.slice(0,c).trim(),l=a.slice(c+1).trim();(l.startsWith('"')&&l.endsWith('"')||l.startsWith("'")&&l.endsWith("'"))&&(l=l.slice(1,-1)),e[m]=l;}}}}return {content:n,frontmatter:e}}function ro(o){let e=o;return e=e.replace(/^### (.+)$/gm,"<h3>$1</h3>"),e=e.replace(/^## (.+)$/gm,"<h2>$1</h2>"),e=e.replace(/^# (.+)$/gm,"<h1>$1</h1>"),e=e.replace(/\*\*\*(.+?)\*\*\*/g,"<strong><em>$1</em></strong>"),e=e.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>"),e=e.replace(/\*(.+?)\*/g,"<em>$1</em>"),e=e.replace(/```(\w*)\n([\s\S]*?)```/g,"<pre><code>$2</code></pre>"),e=e.replace(/`([^`]+)`/g,"<code>$1</code>"),e=e.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2">$1</a>'),e=e.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,'<img src="$2" alt="$1">'),e=e.replace(/^- (.+)$/gm,"<li>$1</li>"),e=e.replace(/(<li>.*<\/li>\n?)+/g,"<ul>$&</ul>"),e=e.replace(/^\d+\. (.+)$/gm,"<li>$1</li>"),e=e.split(`
652
+ `).map(i=>{let a=i.trim();return a?a.startsWith("<")?i:`<p>${a}</p>`:""}).join(`
653
+ `),e=e.replace(/<p><\/p>/g,""),e}function nt(){return ot__namespace.randomBytes(32).toString("hex")}function it(o,e){return o===e}function so(o){try{let e=o.startsWith("http")?o:`http://localhost${o}`,t=new url.URL(e).searchParams;return {key:t.get("key")??void 0,state:t.get("state")??void 0,error:t.get("error")??void 0,message:t.get("message")??void 0}}catch{return {}}}function co(){return `<!DOCTYPE html>
2258
654
  <html lang="en">
2259
655
  <head>
2260
656
  <meta charset="UTF-8">
@@ -2300,11 +696,7 @@ function generateSuccessHtml() {
2300
696
  <p>You can close this window and return to your terminal.</p>
2301
697
  </div>
2302
698
  </body>
2303
- </html>`;
2304
- }
2305
- function generateErrorHtml(errorMessage) {
2306
- const escapedMessage = errorMessage.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
2307
- return `<!DOCTYPE html>
699
+ </html>`}function le(o){return `<!DOCTYPE html>
2308
700
  <html lang="en">
2309
701
  <head>
2310
702
  <meta charset="UTF-8">
@@ -2356,13 +748,10 @@ function generateErrorHtml(errorMessage) {
2356
748
  <div class="icon">\u2717</div>
2357
749
  <h1>Authorization Failed</h1>
2358
750
  <p>Something went wrong during authorization.</p>
2359
- <div class="error-message">${escapedMessage}</div>
751
+ <div class="error-message">${o.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}</div>
2360
752
  </div>
2361
753
  </body>
2362
- </html>`;
2363
- }
2364
- function generate404Html() {
2365
- return `<!DOCTYPE html>
754
+ </html>`}function lo(){return `<!DOCTYPE html>
2366
755
  <html lang="en">
2367
756
  <head>
2368
757
  <meta charset="UTF-8">
@@ -2400,193 +789,13 @@ function generate404Html() {
2400
789
  <p>This endpoint is not available.</p>
2401
790
  </div>
2402
791
  </body>
2403
- </html>`;
2404
- }
2405
- function createCallbackServer() {
2406
- let server = null;
2407
- let pendingCallback = null;
2408
- function handleRequest(req, res) {
2409
- const url = req.url || "/";
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 = `---
792
+ </html>`}function at(){let o=null,e=null;function n(t,i){let a=t.url||"/",c=t.method||"GET",m=a==="/callback"||a.startsWith("/callback?");if(c!=="GET"||!m){i.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),i.end(lo());return}let l=so(a);if(!e){i.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),i.end(le("No pending authorization request"));return}let{expectedState:f,resolve:g,timeoutId:y}=e;if(clearTimeout(y),e=null,l.error){let u=l.message||l.error;i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(le(u)),g({success:false,error:u});return}if(!l.state||!it(l.state,f)){let u="State token mismatch - possible CSRF attack";i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(le(u)),g({success:false,error:u});return}if(!l.key){let u="No API key received";i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(le(u)),g({success:false,error:u});return}i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end(co()),g({success:true,apiKey:l.key});}return {async start(){return new Promise((t,i)=>{o=Ye__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 c=a.port,m=`http://127.0.0.1:${c}/callback`;t({port:c,url:m});});})},waitForCallback(t,i){return new Promise(a=>{let c=setTimeout(()=>{e&&(e=null,a({success:false,error:"Login timed out waiting for authorization"}));},i);e={expectedState:t,resolve:a,timeoutId:c};})},async stop(){return new Promise(t=>{e&&(clearTimeout(e.timeoutId),e=null),o?o.close(()=>{o=null,t();}):t();})}}}var po="https://app.lindo.ai",uo=120;function fo(o,e){let n=`http://127.0.0.1:${e}/callback`,t=new URLSearchParams({state:o,callback_url:n});return `${po}/cli/authorize?${t.toString()}`}function Ae(){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(uo)).option("--no-browser","Display the authorization URL without opening the browser").action(async e=>{let n=parseInt(e.timeout,10),t=n*1e3,i=e.browser,a=at(),c=false;try{r("Starting authentication flow...");let{port:m}=await a.start();c=!0;let l=nt(),f=fo(l,m);i?(r("Opening browser for authorization..."),await R(f)||(r("Could not open browser automatically."),console.log(`
793
+ Please open this URL in your browser:`),console.log(`
794
+ ${f}
795
+ `))):(console.log(`
796
+ Please open this URL in your browser:`),console.log(`
797
+ ${f}
798
+ `)),r(`Waiting for authorization (timeout: ${n}s)...`);let g=await a.waitForCallback(l,t);g.success&&g.apiKey?(Ke(g.apiKey),p("Successfully authenticated!"),r(`API key saved to: ${E()}`)):(s(g.error||"Authentication failed"),process.exit(1));}catch(m){s(m instanceof Error?m.message:"An unexpected error occurred"),process.exit(1);}finally{c&&await a.stop();}})}var Ee=L__namespace.join(ke__namespace.homedir(),".config","opencode","skills","lindoai"),lt=L__namespace.join(Ee,"SKILL.md"),bo=`---
2590
799
  name: lindoai
2591
800
  description: Lindo AI CLI - Command-line interface for the Lindo API
2592
801
  ---
@@ -2754,147 +963,5 @@ lindoai config set baseUrl <value>
2754
963
  3. **Run preview servers in background mode** (\`--background\`) to keep the terminal available.
2755
964
  4. **Remember to stop preview servers** when done editing with \`stop-preview\`.
2756
965
  5. **Use JSON format** (\`--format json\`) when you need to parse command output programmatically.
2757
- `;
2758
- function isOpenCodeInstalled() {
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
966
+ `;function wo(){try{let o=process.platform==="win32"?"where opencode":"which opencode";return child_process.execSync(o,{stdio:"ignore"}),!0}catch{return false}}function yo(){try{return r("Installing OpenCode..."),child_process.execSync("npm install -g opencode-ai@latest",{stdio:"inherit"}),!0}catch{return false}}function vo(){k__namespace.existsSync(Ee)||k__namespace.mkdirSync(Ee,{recursive:true}),k__namespace.writeFileSync(lt,bo,"utf-8");}function Le(){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 e=>{wo()||(e.install?(yo()||(s("Failed to install OpenCode"),r("Please install manually: npm install -g opencode-ai@latest"),process.exit(1)),p("OpenCode installed successfully")):(s("OpenCode is not installed"),r(""),r("To install OpenCode, run one of the following:"),r(" lindoai agent --install"),r(" npm install -g opencode-ai@latest"),process.exit(1)));try{vo(),r(`Skill file installed to: ${lt}`);}catch(i){s(`Failed to install skill file: ${i instanceof Error?i.message:"Unknown error"}`),process.exit(1);}let n=[];e.model&&n.push("--model",e.model),r("Launching OpenCode...");let t=child_process.spawn("opencode",n,{stdio:"inherit",shell:true});t.on("error",i=>{s(`Failed to launch OpenCode: ${i.message}`),process.exit(1);}),t.on("close",i=>{process.exit(i??0);});})}var Co="1.0.0";function _o(){let o=new commander.Command;return o.name("lindo").description("Command-line interface for the Lindo API").version(Co,"-v, --version","Output the current version").helpOption("-h, --help","Display help for command"),o.addCommand(ge()),o.addCommand(he()),o.addCommand(be()),o.addCommand(we()),o.addCommand(ye()),o.addCommand(ve()),o.addCommand(xe()),o.addCommand($e()),o.addCommand(Te()),o.addCommand(Ae()),o.addCommand(Le()),o.exitOverride(e=>{throw e.code==="commander.help"&&process.exit(0),e.code==="commander.version"&&process.exit(0),e.code==="commander.missingArgument"&&(s(e.message),process.exit(1)),e.code==="commander.unknownCommand"&&(s(e.message),r('Run "lindo --help" for available commands'),process.exit(1)),e}),o}function So(o){s("Authentication failed"),r(""),r("Your API key may be invalid or expired."),r(""),r("To configure your API key:"),r(" 1. Run: lindo config set apiKey <your-api-key>"),r(" 2. Or set the LINDO_API_KEY environment variable"),r(""),r("To get an API key:"),r(" Visit https://app.lindo.ai/settings/api-keys");}async function Oo(){let o=_o();try{await o.parseAsync(process.argv);}catch(e){throw e instanceof lindoai.AuthenticationError&&(So(),process.exit(1)),e}}Oo().catch(o=>{o instanceof Error?s(`Unexpected error: ${o.message}`):s("An unexpected error occurred"),process.exit(1);});
967
+ exports.createProgram=_o;