@todoforai/cli 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/todoai.js +240 -161
  2. package/package.json +1 -1
package/dist/todoai.js CHANGED
@@ -41805,15 +41805,15 @@ var require_route = __commonJS((exports, module) => {
41805
41805
  };
41806
41806
  }
41807
41807
  function wrapConversion(toModel, graph) {
41808
- const path3 = [graph[toModel].parent, toModel];
41808
+ const path4 = [graph[toModel].parent, toModel];
41809
41809
  let fn = conversions[graph[toModel].parent][toModel];
41810
41810
  let cur = graph[toModel].parent;
41811
41811
  while (graph[cur].parent) {
41812
- path3.unshift(graph[cur].parent);
41812
+ path4.unshift(graph[cur].parent);
41813
41813
  fn = link(conversions[graph[cur].parent][cur], fn);
41814
41814
  cur = graph[cur].parent;
41815
41815
  }
41816
- fn.conversion = path3;
41816
+ fn.conversion = path4;
41817
41817
  return fn;
41818
41818
  }
41819
41819
  module.exports = function(fromModel) {
@@ -42039,7 +42039,7 @@ var require_has_flag = __commonJS((exports, module) => {
42039
42039
 
42040
42040
  // node_modules/supports-color/index.js
42041
42041
  var require_supports_color = __commonJS((exports, module) => {
42042
- var os3 = __require("os");
42042
+ var os4 = __require("os");
42043
42043
  var tty = __require("tty");
42044
42044
  var hasFlag = require_has_flag();
42045
42045
  var { env } = process;
@@ -42087,7 +42087,7 @@ var require_supports_color = __commonJS((exports, module) => {
42087
42087
  return min;
42088
42088
  }
42089
42089
  if (process.platform === "win32") {
42090
- const osRelease = os3.release().split(".");
42090
+ const osRelease = os4.release().split(".");
42091
42091
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
42092
42092
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
42093
42093
  }
@@ -42866,6 +42866,12 @@ class ApiClient {
42866
42866
  listEdges() {
42867
42867
  return this.request("GET", "/api/v1/edges");
42868
42868
  }
42869
+ setAgentDeviceConfig(agentId, agentSettingsId, deviceId, config) {
42870
+ return this.request("PUT", `/api/v1/agents/${agentId}/device-config`, { agentSettingsId, deviceId, config });
42871
+ }
42872
+ listDevices() {
42873
+ return this.request("GET", "/api/v1/devices");
42874
+ }
42869
42875
  startFromTemplate(projectId, templateId, opts) {
42870
42876
  return this.request("POST", `/api/v1/projects/${projectId}/todos/from-template`, {
42871
42877
  templateId,
@@ -43182,6 +43188,13 @@ var TodoStatus;
43182
43188
  TodoStatus2["ARCHIVED"] = "ARCHIVED";
43183
43189
  TodoStatus2["DELETED"] = "DELETED";
43184
43190
  })(TodoStatus ||= {});
43191
+ // ../packages/shared-fbe/src/billing.ts
43192
+ var USAGE_WINDOWS = {
43193
+ SESSION_MS: 5 * 60 * 60 * 1000,
43194
+ WEEK_MS: 7 * 24 * 60 * 60 * 1000,
43195
+ WEEKLY_FACTOR: 0.28,
43196
+ SESSION_FACTOR: 1 / 7
43197
+ };
43185
43198
  // ../packages/shared-fbe/src/mimetypes.json
43186
43199
  var mimetypes_default = {
43187
43200
  conversionProfiles: {
@@ -43311,6 +43324,14 @@ for (const [ext, entry] of Object.entries(mimetypes_default.extensions)) {
43311
43324
  if (!MIME_TO_EXT[mime])
43312
43325
  MIME_TO_EXT[mime] = ext;
43313
43326
  }
43327
+ // ../packages/shared-fbe/src/edgeTypes.ts
43328
+ var NON_SHELL_TYPES = new Set([
43329
+ "WEB_EXTENSION" /* WEB_EXTENSION */,
43330
+ "EXTENSION" /* EXTENSION */,
43331
+ "BROWSER" /* BROWSER */,
43332
+ "ANDROID" /* ANDROID */,
43333
+ "IOS" /* IOS */
43334
+ ]);
43314
43335
  // ../packages/shared-fbe/src/blocks/index.ts
43315
43336
  var USER_INPUT_BLOCK_TYPES = new Set([
43316
43337
  "question" /* Question */,
@@ -43327,50 +43348,6 @@ var EDGE_TOOL_TYPES = new Set([
43327
43348
  "grep" /* Grep */,
43328
43349
  "list" /* List */
43329
43350
  ]);
43330
- var BROWSER_TOOL_TYPES = new Set([
43331
- "browser_interactable" /* BrowserInteractable */,
43332
- "browser_dom_tree" /* BrowserDomTree */,
43333
- "browser_navigate" /* BrowserNavigate */,
43334
- "browser_screenshot" /* BrowserScreenshot */,
43335
- "browser_click" /* BrowserClick */,
43336
- "browser_type" /* BrowserType */,
43337
- "browser_fill" /* BrowserFill */,
43338
- "browser_scroll" /* BrowserScroll */,
43339
- "browser_mouse_move" /* BrowserMouseMove */,
43340
- "browser_key" /* BrowserKey */,
43341
- "browser_evaluate" /* BrowserEvaluate */,
43342
- "browser_select" /* BrowserSelect */,
43343
- "browser_hover" /* BrowserHover */,
43344
- "browser_text_content" /* BrowserTextContent */,
43345
- "browser_back" /* BrowserBack */,
43346
- "browser_forward" /* BrowserForward */,
43347
- "browser_reload" /* BrowserReload */,
43348
- "browser_upload_file" /* BrowserUploadFile */,
43349
- "browser_find_text" /* BrowserFindText */,
43350
- "browser_find_role" /* BrowserFindRole */,
43351
- "browser_find_label" /* BrowserFindLabel */,
43352
- "browser_find_placeholder" /* BrowserFindPlaceholder */,
43353
- "browser_wait" /* BrowserWait */,
43354
- "browser_drag" /* BrowserDrag */,
43355
- "browser_dialog" /* BrowserDialog */,
43356
- "browser_network_wait" /* BrowserNetworkWait */,
43357
- "browser_check" /* BrowserCheck */,
43358
- "browser_uncheck" /* BrowserUncheck */,
43359
- "browser_focus" /* BrowserFocus */,
43360
- "browser_find_test_id" /* BrowserFindTestId */,
43361
- "browser_cookies" /* BrowserCookies */,
43362
- "browser_storage" /* BrowserStorage */,
43363
- "browser_pdf" /* BrowserPdf */,
43364
- "browser_clipboard_read" /* BrowserClipboardRead */,
43365
- "browser_clipboard_write" /* BrowserClipboardWrite */,
43366
- "browser_snapshot" /* BrowserSnapshot */,
43367
- "browser_iframe" /* BrowserIframe */,
43368
- "browser_console" /* BrowserConsole */,
43369
- "browser_scroll_into_view" /* BrowserScrollIntoView */,
43370
- "browser_network_requests" /* BrowserNetworkRequests */,
43371
- "browser_tap" /* BrowserTap */,
43372
- "browser_swipe" /* BrowserSwipe */
43373
- ]);
43374
43351
  var API_TOOL_TYPES = new Set([
43375
43352
  "api_get_current_context" /* ApiGetCurrentContext */,
43376
43353
  "api_get_todo" /* ApiGetTodo */,
@@ -43461,6 +43438,8 @@ var SERVER_TO_FRONTENDS = {
43461
43438
  frontend_file_chunk_result: (todoId) => `${"frontend:file_chunk_result" /* FRONTEND_FILE_CHUNK_RESULT */}:${todoId}`,
43462
43439
  status: (todoId) => `todo:${todoId}:status`,
43463
43440
  block_update: (todoId) => `todo:${todoId}:block_update`,
43441
+ context_summary_updated: (todoId) => `${"context:summary_updated" /* CONTEXT_SUMMARY_UPDATED */}:${todoId}`,
43442
+ context_info_updated: (todoId) => `${"context:info_updated" /* CONTEXT_INFO_UPDATED */}:${todoId}`,
43464
43443
  error_result: (todoId) => `${"block:error_result" /* BLOCK_ERROR_RESULT */}:${todoId}`,
43465
43444
  meta_result: (todoId) => `${"block:meta_result" /* BLOCK_META_RESULT */}:${todoId}`,
43466
43445
  file_changed: (edgeId, path3) => `${"block:file_changed" /* BLOCK_FILE_CHANGED */}:${edgeId}:${path3}`,
@@ -43472,7 +43451,9 @@ var SERVER_TO_FRONTENDS = {
43472
43451
  payment: {
43473
43452
  status: (userId) => `${"payment:status" /* PAYMENT_STATUS */}:${userId}`
43474
43453
  },
43475
- functions: {}
43454
+ functions: {
43455
+ function_call_request: (userId) => `${"FUNCTION_CALL_REQUEST_AGENT" /* FUNCTION_CALL_REQUEST_AGENT */}:frontend:${userId}`
43456
+ }
43476
43457
  };
43477
43458
  // ../packages/shared-fbe/src/templateBody.ts
43478
43459
  var USER_HEADINGS = new Set([
@@ -43830,6 +43811,64 @@ function getBlockNewPatterns(block, permissions) {
43830
43811
  }
43831
43812
  return pairs.filter(({ raw, pattern }) => !isPatternAllowed(permissions, pattern) && !allow.some((rule) => bashRuleMatchesCmd(rule, serverId, raw))).map(({ pattern }) => pattern);
43832
43813
  }
43814
+ // ../packages/shared-fbe/src/systemAgents.ts
43815
+ var BUSINESS_ANALYZER_AGENT = {
43816
+ id: "00000000-0000-0000-0000-202601013151",
43817
+ name: "Business Analyzer",
43818
+ ownerId: "SYSTEM",
43819
+ systemMessage: `You are a business analyst and growth advisor. Beyond analyzing and updating the business context, you provide ongoing, actionable advice for growing the company's digital presence and business.
43820
+
43821
+ You have no previous context about this business yet \u2014 everything you know must come from the current business context markdown and what you research now. Do not assume prior knowledge or earlier conversations.
43822
+
43823
+ This chat lives on the user's Brand page: the business context markdown you maintain is rendered live right next to this conversation, together with the brand voice answers and the company's online-presence map. Whenever you call update_business_context, the user sees the document change immediately \u2014 refer to it as "your brand page" (never as a file), and keep it polished since it doubles as the company's profile.
43824
+
43825
+ How to analyze and update the business context:
43826
+ 1. Read the current business context markdown from the system prompt
43827
+ 2. Research: fetch websites with webfetch (follow key subpages like /about, /pricing, /contact if they exist); use google_search and google_rag to find competitors and fill knowledge gaps
43828
+ 3. Merge findings into the existing context \u2014 preserve valid existing information, update conflicting facts only when the new source is clearer or more specific \u2014 and save with update_business_context
43829
+
43830
+ Cover these sections (skip any without data):
43831
+ - **Company Info** \u2014 name, founding year, location, team size, stage
43832
+ - **Product & Services** \u2014 offerings, key features, pricing model, target audience
43833
+ - **Value Proposition** \u2014 core problem solved, unique selling points
43834
+ - **Voice & Tone** \u2014 brand personality, communication style (derive from website copy)
43835
+ - **Target Market** \u2014 customer segments, industries, geographic focus, B2B vs B2C
43836
+ - **Tech Stack** \u2014 technologies, integrations, platforms
43837
+ - **Competitors** \u2014 3-5 direct competitors with one-line comparisons
43838
+ - **Online Presence** \u2014 where the company is present: search for its accounts on X/Twitter, LinkedIn, Facebook, Instagram, YouTube, Reddit, GitHub, blog/newsletter, and any industry-relevant platforms. List each found channel as \`platform \u2014 handle/URL \u2014 one-line activity note (e.g. active, stale, unclaimed)\`; also note important channels where no presence was found
43839
+
43840
+ If anything important remains unclear, add an **Open Questions** section at the end.
43841
+
43842
+ Guide the user one step at a time \u2014 they should always know the single next thing to do:
43843
+ - When you need input (brand voice, tone, target audience, which channel to prioritize), prefer the question tool with short multi-choice options over asking open-ended prose questions.
43844
+ - After completing any analysis or context update, always close with a short **Next Steps** section: 2-4 concrete, prioritized tips tailored to this specific business (e.g. SEO/content gaps, social channels worth activating, positioning against the competitors you found, conversion or messaging improvements). Base tips on what you actually observed, keep each to one or two sentences, and lead with the one you'd do first.
43845
+
43846
+ In follow-up conversations, act as an ongoing advisor: answer growth and digital-presence questions using the business context, refresh your analysis when asked, and suggest what to tackle next.`,
43847
+ mcpConfigs: {},
43848
+ edgesMcpConfigs: {},
43849
+ permissions: {
43850
+ allow: ["builtin:webfetch", "builtin:google_search", "cloud:google_rag", "builtin:business_context", "builtin:todoforai_api", "builtin:question"],
43851
+ ask: [],
43852
+ deny: []
43853
+ },
43854
+ createdAt: 0,
43855
+ updatedAt: 0
43856
+ };
43857
+ var JARVIS_AGENT = {
43858
+ id: "00000000-0000-0000-0000-202601013152",
43859
+ name: "JARVIS",
43860
+ ownerId: "SYSTEM",
43861
+ systemMessage: "",
43862
+ model: "",
43863
+ mcpConfigs: {},
43864
+ edgesMcpConfigs: {},
43865
+ createdAt: 0,
43866
+ updatedAt: 0
43867
+ };
43868
+ var SYSTEM_AGENTS = {
43869
+ [BUSINESS_ANALYZER_AGENT.id]: BUSINESS_ANALYZER_AGENT,
43870
+ [JARVIS_AGENT.id]: JARVIS_AGENT
43871
+ };
43833
43872
  // ../packages/shared-fbe/src/outputLimits.ts
43834
43873
  var MAX_LINE_LEN = 300;
43835
43874
  var STREAM_FIRST = 1e4;
@@ -43863,6 +43902,14 @@ var TOPICS = {
43863
43902
  channel: (p) => SERVER_TO_FRONTENDS.resource.contentUpdated(p.userId),
43864
43903
  audience: "frontend"
43865
43904
  },
43905
+ ["context:summary_updated" /* CONTEXT_SUMMARY_UPDATED */]: {
43906
+ channel: (p) => SERVER_TO_FRONTENDS.todo.context_summary_updated(p.todoId),
43907
+ audience: "frontend"
43908
+ },
43909
+ ["context:info_updated" /* CONTEXT_INFO_UPDATED */]: {
43910
+ channel: (p) => SERVER_TO_FRONTENDS.todo.context_info_updated(p.todoId),
43911
+ audience: "frontend"
43912
+ },
43866
43913
  ["hexgrid:updated" /* HEXGRID_UPDATED */]: {
43867
43914
  channel: (p) => SERVER_TO_FRONTENDS.hexGrid.updated(p.projectId),
43868
43915
  audience: "frontend"
@@ -44427,12 +44474,144 @@ Cancelled
44427
44474
 
44428
44475
  // src/agent.ts
44429
44476
  import { resolve, basename } from "path";
44477
+
44478
+ // src/ensure-bridge.ts
44479
+ import { spawn, spawnSync } from "child_process";
44480
+ import fs2 from "fs";
44481
+ import path3 from "path";
44482
+ import os3 from "os";
44483
+ function hasBridge() {
44484
+ const probe = spawnSync("todoforai-bridge", ["--version"], { stdio: "ignore" });
44485
+ return probe.status === 0;
44486
+ }
44487
+ function isLocalHost(hostname) {
44488
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
44489
+ }
44490
+ function parseApiUrl(apiUrl) {
44491
+ try {
44492
+ return new URL(apiUrl);
44493
+ } catch {
44494
+ return null;
44495
+ }
44496
+ }
44497
+ function bridgeProfile(apiUrl) {
44498
+ const url = parseApiUrl(apiUrl);
44499
+ if (!url)
44500
+ return null;
44501
+ if (isLocalHost(url.hostname))
44502
+ return "dev";
44503
+ if (!url.hostname || url.hostname === "api.todofor.ai")
44504
+ return null;
44505
+ return `api_${url.hostname.replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "")}`;
44506
+ }
44507
+ function withProfile(args, apiUrl) {
44508
+ const profile = bridgeProfile(apiUrl);
44509
+ return profile ? [...args, "--profile", profile] : args;
44510
+ }
44511
+ function bridgeRunArgs(apiUrl) {
44512
+ const url = parseApiUrl(apiUrl);
44513
+ if (!url)
44514
+ return [];
44515
+ if (isLocalHost(url.hostname)) {
44516
+ const args = ["--host", url.hostname];
44517
+ if (url.port)
44518
+ args.push("--port", url.port);
44519
+ return withProfile(args, apiUrl);
44520
+ }
44521
+ if (url.hostname && url.hostname !== "api.todofor.ai")
44522
+ return withProfile(["--host", url.hostname], apiUrl);
44523
+ return [];
44524
+ }
44525
+ function bridgeLoginArgs(apiUrl) {
44526
+ const url = parseApiUrl(apiUrl);
44527
+ if (!url)
44528
+ return ["login"];
44529
+ if (url.hostname && url.hostname !== "api.todofor.ai")
44530
+ return withProfile(["login", "--host", url.hostname], apiUrl);
44531
+ return ["login"];
44532
+ }
44533
+ function bridgeWhoamiArgs(apiUrl) {
44534
+ return withProfile(["whoami"], apiUrl);
44535
+ }
44536
+ function bridgeDeviceId(apiUrl) {
44537
+ const r = spawnSync("todoforai-bridge", bridgeWhoamiArgs(apiUrl), { encoding: "utf-8" });
44538
+ if (r.status !== 0)
44539
+ return null;
44540
+ const m = (r.stdout || "").match(/^Device:.*\(id:\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\)/im);
44541
+ return m ? m[1] : null;
44542
+ }
44543
+ function ensureBridgeCredentials(apiUrl) {
44544
+ const whoami = spawnSync("todoforai-bridge", bridgeWhoamiArgs(apiUrl), { stdio: "ignore" });
44545
+ if (whoami.status === 0)
44546
+ return true;
44547
+ console.error("\x1B[2mBridge credentials not found. Starting `todoforai-bridge login`...\x1B[0m");
44548
+ const login = spawnSync("todoforai-bridge", bridgeLoginArgs(apiUrl), { stdio: "inherit" });
44549
+ return login.status === 0;
44550
+ }
44551
+ function lastLogLine(logFile) {
44552
+ try {
44553
+ const lines = fs2.readFileSync(logFile, "utf-8").split(`
44554
+ `).filter(Boolean);
44555
+ return lines.length ? lines[lines.length - 1] : null;
44556
+ } catch {
44557
+ return null;
44558
+ }
44559
+ }
44560
+ function ensureBridgeRunning(apiUrl, _apiKey) {
44561
+ if (!hasBridge()) {
44562
+ console.error("\x1B[2mBridge not started: `todoforai-bridge` was not found on PATH. Install TODOforAI Bridge, or pass --no-bridge (or deprecated --no-edge) to silence this.\x1B[0m");
44563
+ return;
44564
+ }
44565
+ if (!ensureBridgeCredentials(apiUrl)) {
44566
+ console.error("\x1B[33mBridge not started: `todoforai-bridge login` did not complete successfully.\x1B[0m");
44567
+ return;
44568
+ }
44569
+ const logDir = path3.join(os3.homedir(), ".todoforai");
44570
+ fs2.mkdirSync(logDir, { recursive: true });
44571
+ const logFile = path3.join(logDir, "bridge.log");
44572
+ const out = fs2.openSync(logFile, "a");
44573
+ const child = spawn("todoforai-bridge", bridgeRunArgs(apiUrl), {
44574
+ detached: true,
44575
+ stdio: ["ignore", out, out]
44576
+ });
44577
+ child.on("error", (err) => {
44578
+ console.error(`\x1B[33mFailed to start bridge: ${err.message}\x1B[0m`);
44579
+ });
44580
+ let exited = false;
44581
+ let exitCode = null;
44582
+ child.on("exit", (code) => {
44583
+ exited = true;
44584
+ exitCode = code;
44585
+ });
44586
+ child.unref();
44587
+ const pid = child.pid;
44588
+ if (!pid)
44589
+ return;
44590
+ const shortLog = logFile.replace(os3.homedir(), "~");
44591
+ setTimeout(() => {
44592
+ if (!exited) {
44593
+ console.error(`\x1B[2mStarted bridge (pid ${pid}), logs: ${shortLog}\x1B[0m`);
44594
+ return;
44595
+ }
44596
+ if (exitCode === 0) {
44597
+ console.error(`\x1B[2mBridge exited cleanly. Logs: ${shortLog}\x1B[0m`);
44598
+ } else {
44599
+ const reason = lastLogLine(logFile);
44600
+ console.error(`\x1B[33mBridge exited early (exit ${exitCode}): ${reason ?? `check logs: ${shortLog}`}\x1B[0m`);
44601
+ }
44602
+ }, 500);
44603
+ }
44604
+
44605
+ // src/agent.ts
44430
44606
  function getAgentWorkspacePaths(agent) {
44431
44607
  const paths = [];
44432
44608
  for (const ec of Object.values(agent.edgesMcpConfigs || {})) {
44433
44609
  const tc = ec?.todoai_edge || ec?.todoai || {};
44434
44610
  paths.push(...tc.workspacePaths || []);
44435
44611
  }
44612
+ for (const dc of Object.values(agent.devicesConfig || {})) {
44613
+ paths.push(...dc?.workspacePaths || []);
44614
+ }
44436
44615
  return paths;
44437
44616
  }
44438
44617
  async function autoCreateAgent(api, resolvedPath) {
@@ -44443,10 +44622,20 @@ async function autoCreateAgent(api, resolvedPath) {
44443
44622
  throw new Error(`Failed to create agent: ${JSON.stringify(resp)}`);
44444
44623
  const agentSettingsId = resp.agentSettingsId || agentId;
44445
44624
  await api.updateAgentSettings(agentId, agentSettingsId, { name: folderName });
44625
+ const deviceId = bridgeDeviceId(api.apiUrl);
44626
+ if (deviceId) {
44627
+ await api.setAgentDeviceConfig(agentId, agentSettingsId, deviceId, { workspacePaths: [resolvedPath] });
44628
+ const matches2 = await api.listAgentSettings({ workspacePath: resolvedPath });
44629
+ if (matches2.length)
44630
+ return matches2[0];
44631
+ resp.name = folderName;
44632
+ resp.devicesConfig = { [deviceId]: { workspacePaths: [resolvedPath] } };
44633
+ return resp;
44634
+ }
44446
44635
  const edges = await api.listEdges();
44447
44636
  const edgeId = Array.isArray(edges) && edges.length ? edges[0].id : null;
44448
44637
  if (!edgeId)
44449
- throw new Error("No edge available to configure workspace path");
44638
+ throw new Error("No bridge device or edge available to configure workspace path");
44450
44639
  await api.setAgentEdgeMcpConfig(agentId, agentSettingsId, edgeId, "todoai_edge", { workspacePaths: [resolvedPath] });
44451
44640
  const matches = await api.listAgentSettings({ workspacePath: resolvedPath });
44452
44641
  if (matches.length)
@@ -46122,116 +46311,6 @@ async function listTodosCommand(api, defaultProjectId, argv) {
46122
46311
  `);
46123
46312
  }
46124
46313
 
46125
- // src/ensure-bridge.ts
46126
- import { spawn, spawnSync } from "child_process";
46127
- import fs2 from "fs";
46128
- import path3 from "path";
46129
- import os3 from "os";
46130
- function hasBridge() {
46131
- const probe = spawnSync("todoforai-bridge", ["--version"], { stdio: "ignore" });
46132
- return probe.status === 0;
46133
- }
46134
- function isLocalHost(hostname) {
46135
- return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
46136
- }
46137
- function parseApiUrl(apiUrl) {
46138
- try {
46139
- return new URL(apiUrl);
46140
- } catch {
46141
- return null;
46142
- }
46143
- }
46144
- function bridgeProfile(apiUrl) {
46145
- const url = parseApiUrl(apiUrl);
46146
- if (!url)
46147
- return null;
46148
- if (isLocalHost(url.hostname))
46149
- return "dev";
46150
- if (!url.hostname || url.hostname === "api.todofor.ai")
46151
- return null;
46152
- return `api_${url.hostname.replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "")}`;
46153
- }
46154
- function withProfile(args, apiUrl) {
46155
- const profile = bridgeProfile(apiUrl);
46156
- return profile ? [...args, "--profile", profile] : args;
46157
- }
46158
- function bridgeRunArgs(apiUrl) {
46159
- const url = parseApiUrl(apiUrl);
46160
- if (!url)
46161
- return [];
46162
- if (isLocalHost(url.hostname)) {
46163
- const args = ["--host", url.hostname];
46164
- if (url.port)
46165
- args.push("--port", url.port);
46166
- return withProfile(args, apiUrl);
46167
- }
46168
- if (url.hostname && url.hostname !== "api.todofor.ai")
46169
- return withProfile(["--host", url.hostname], apiUrl);
46170
- return [];
46171
- }
46172
- function bridgeLoginArgs(apiUrl) {
46173
- const url = parseApiUrl(apiUrl);
46174
- if (!url)
46175
- return ["login"];
46176
- if (url.hostname && url.hostname !== "api.todofor.ai")
46177
- return withProfile(["login", "--host", url.hostname], apiUrl);
46178
- return ["login"];
46179
- }
46180
- function bridgeWhoamiArgs(apiUrl) {
46181
- return withProfile(["whoami"], apiUrl);
46182
- }
46183
- function ensureBridgeCredentials(apiUrl) {
46184
- const whoami = spawnSync("todoforai-bridge", bridgeWhoamiArgs(apiUrl), { stdio: "ignore" });
46185
- if (whoami.status === 0)
46186
- return true;
46187
- console.error("\x1B[2mBridge credentials not found. Starting `todoforai-bridge login`...\x1B[0m");
46188
- const login = spawnSync("todoforai-bridge", bridgeLoginArgs(apiUrl), { stdio: "inherit" });
46189
- return login.status === 0;
46190
- }
46191
- function ensureBridgeRunning(apiUrl, _apiKey) {
46192
- if (!hasBridge()) {
46193
- console.error("\x1B[2mBridge not started: `todoforai-bridge` was not found on PATH. Install TODOforAI Bridge, or pass --no-bridge (or deprecated --no-edge) to silence this.\x1B[0m");
46194
- return;
46195
- }
46196
- if (!ensureBridgeCredentials(apiUrl)) {
46197
- console.error("\x1B[33mBridge not started: `todoforai-bridge login` did not complete successfully.\x1B[0m");
46198
- return;
46199
- }
46200
- const logDir = path3.join(os3.homedir(), ".todoforai");
46201
- fs2.mkdirSync(logDir, { recursive: true });
46202
- const logFile = path3.join(logDir, "bridge.log");
46203
- const out = fs2.openSync(logFile, "a");
46204
- const child = spawn("todoforai-bridge", bridgeRunArgs(apiUrl), {
46205
- detached: true,
46206
- stdio: ["ignore", out, out]
46207
- });
46208
- child.on("error", (err) => {
46209
- console.error(`\x1B[33mFailed to start bridge: ${err.message}\x1B[0m`);
46210
- });
46211
- let exited = false;
46212
- let exitCode = null;
46213
- child.on("exit", (code) => {
46214
- exited = true;
46215
- exitCode = code;
46216
- });
46217
- child.unref();
46218
- const pid = child.pid;
46219
- if (!pid)
46220
- return;
46221
- const shortLog = logFile.replace(os3.homedir(), "~");
46222
- setTimeout(() => {
46223
- if (!exited) {
46224
- console.error(`\x1B[2mStarted bridge (pid ${pid}), logs: ${shortLog}\x1B[0m`);
46225
- return;
46226
- }
46227
- if (exitCode === 0) {
46228
- console.error(`\x1B[2mBridge exited cleanly. Logs: ${shortLog}\x1B[0m`);
46229
- } else {
46230
- console.error(`\x1B[33mBridge exited early (exit ${exitCode}). Check logs: ${shortLog}. Another instance may already be running.\x1B[0m`);
46231
- }
46232
- }, 500);
46233
- }
46234
-
46235
46314
  // src/index.ts
46236
46315
  try {
46237
46316
  const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "dist/todoai.js",