@todoforai/cli 0.1.15 → 0.1.17
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/README.md +9 -6
- package/dist/todoai.js +165 -106
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,11 +6,13 @@ CLI for [TODOforAI](https://todofor.ai) — create, watch, and inspect AI-powere
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
bun install -g @todoforai/cli
|
|
9
|
+
# Install the native bridge once, if it is not already on PATH:
|
|
10
|
+
curl -fsSL https://raw.githubusercontent.com/todoforai/bridge/main/install.sh | sh
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
## Setup
|
|
12
14
|
|
|
13
|
-
Just run `todoforai-cli` — on first use it opens a browser for **device login** and saves the API key
|
|
15
|
+
Just run `todoforai-cli` — on first use it opens a browser for **device login** and saves the CLI API key in the shared TODOforAI credentials file. The bridge uses the same file for its own device credentials.
|
|
14
16
|
|
|
15
17
|
```bash
|
|
16
18
|
todoforai-cli # prompts device login if no key found
|
|
@@ -19,15 +21,15 @@ todoforai-cli login # explicit login
|
|
|
19
21
|
|
|
20
22
|
API URL resolution: `--api-url` flag → `TODOFORAI_API_URL` env → `https://api.todofor.ai`.
|
|
21
23
|
|
|
22
|
-
Auth resolution: `--api-key` flag → `TODOFORAI_API_KEY` env → shared credentials
|
|
24
|
+
Auth resolution: `--api-key` flag → `TODOFORAI_API_KEY` env → shared credentials file → device login.
|
|
23
25
|
|
|
24
26
|
Project, agent, and last-todo state are stored **per API URL** under `per_api_url[<url>]` in the config — switching between e.g. `https://api.todofor.ai` and `http://localhost:4000` keeps each environment's defaults isolated. Legacy top-level fields are auto-migrated on first run.
|
|
25
27
|
|
|
26
|
-
##
|
|
28
|
+
## Bridge
|
|
27
29
|
|
|
28
|
-
The CLI talks to the backend over WebSocket; **shell execution, file I/O, and tool calls happen in the
|
|
30
|
+
The CLI talks to the backend over WebSocket; **shell execution, file I/O, and tool calls happen in the bridge** running locally. On create/resume/template runs, `todoforai-cli` starts a detached `todoforai-bridge` process if needed (the bridge enforces its own single-instance lock, logs at `~/.todoforai/bridge.log`). If bridge credentials are missing, the CLI runs `todoforai-bridge login` in the foreground first so you can see and approve the device-login URL. The bridge keeps running after the CLI exits, so long-running tasks survive `Ctrl+D`.
|
|
29
31
|
|
|
30
|
-
Disable with `--no-
|
|
32
|
+
Disable with `--no-bridge` if you manage the bridge yourself (e.g. systemd, separate terminal). `--no-edge` remains supported as a deprecated alias.
|
|
31
33
|
|
|
32
34
|
## Usage
|
|
33
35
|
|
|
@@ -82,7 +84,8 @@ todoforai-cli --resume <todo-id> # resume specific todo
|
|
|
82
84
|
--dangerously-skip-permissions Auto-approve all blocks (CI/benchmarks)
|
|
83
85
|
--allow-all Set permissions to allow all tools (no approval needed)
|
|
84
86
|
--no-watch Create todo and exit
|
|
85
|
-
--no-
|
|
87
|
+
--no-bridge Do not auto-spawn bridge
|
|
88
|
+
--no-edge Deprecated alias for --no-bridge
|
|
86
89
|
--json Output as JSON
|
|
87
90
|
--safe Validate API key upfront
|
|
88
91
|
--debug, -d Debug output
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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,
|
|
@@ -43327,50 +43333,6 @@ var EDGE_TOOL_TYPES = new Set([
|
|
|
43327
43333
|
"grep" /* Grep */,
|
|
43328
43334
|
"list" /* List */
|
|
43329
43335
|
]);
|
|
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
43336
|
var API_TOOL_TYPES = new Set([
|
|
43375
43337
|
"api_get_current_context" /* ApiGetCurrentContext */,
|
|
43376
43338
|
"api_get_todo" /* ApiGetTodo */,
|
|
@@ -43461,6 +43423,8 @@ var SERVER_TO_FRONTENDS = {
|
|
|
43461
43423
|
frontend_file_chunk_result: (todoId) => `${"frontend:file_chunk_result" /* FRONTEND_FILE_CHUNK_RESULT */}:${todoId}`,
|
|
43462
43424
|
status: (todoId) => `todo:${todoId}:status`,
|
|
43463
43425
|
block_update: (todoId) => `todo:${todoId}:block_update`,
|
|
43426
|
+
context_summary_updated: (todoId) => `${"context:summary_updated" /* CONTEXT_SUMMARY_UPDATED */}:${todoId}`,
|
|
43427
|
+
context_info_updated: (todoId) => `${"context:info_updated" /* CONTEXT_INFO_UPDATED */}:${todoId}`,
|
|
43464
43428
|
error_result: (todoId) => `${"block:error_result" /* BLOCK_ERROR_RESULT */}:${todoId}`,
|
|
43465
43429
|
meta_result: (todoId) => `${"block:meta_result" /* BLOCK_META_RESULT */}:${todoId}`,
|
|
43466
43430
|
file_changed: (edgeId, path3) => `${"block:file_changed" /* BLOCK_FILE_CHANGED */}:${edgeId}:${path3}`,
|
|
@@ -43863,6 +43827,14 @@ var TOPICS = {
|
|
|
43863
43827
|
channel: (p) => SERVER_TO_FRONTENDS.resource.contentUpdated(p.userId),
|
|
43864
43828
|
audience: "frontend"
|
|
43865
43829
|
},
|
|
43830
|
+
["context:summary_updated" /* CONTEXT_SUMMARY_UPDATED */]: {
|
|
43831
|
+
channel: (p) => SERVER_TO_FRONTENDS.todo.context_summary_updated(p.todoId),
|
|
43832
|
+
audience: "frontend"
|
|
43833
|
+
},
|
|
43834
|
+
["context:info_updated" /* CONTEXT_INFO_UPDATED */]: {
|
|
43835
|
+
channel: (p) => SERVER_TO_FRONTENDS.todo.context_info_updated(p.todoId),
|
|
43836
|
+
audience: "frontend"
|
|
43837
|
+
},
|
|
43866
43838
|
["hexgrid:updated" /* HEXGRID_UPDATED */]: {
|
|
43867
43839
|
channel: (p) => SERVER_TO_FRONTENDS.hexGrid.updated(p.projectId),
|
|
43868
43840
|
audience: "frontend"
|
|
@@ -44008,7 +43980,8 @@ Options:
|
|
|
44008
43980
|
--allow-all Set permissions to allow all tools (no approval needed)
|
|
44009
43981
|
--raw-sysmsg <file> Use file contents verbatim as system prompt (new TODO only)
|
|
44010
43982
|
--no-watch Create todo and exit
|
|
44011
|
-
--no-
|
|
43983
|
+
--no-bridge Do not auto-spawn bridge
|
|
43984
|
+
--no-edge Deprecated alias for --no-bridge
|
|
44012
43985
|
--json Output as JSON
|
|
44013
43986
|
--detailed 'inspect --json': keep ids, timestamps, agentSettingsId, scheduledTimestamp
|
|
44014
43987
|
--format-anthropic 'inspect --json': Anthropic-style shape (tool_result in next user msg); attachment sources are uri-typed, so not a 1:1 messages.create input
|
|
@@ -44062,6 +44035,7 @@ function parseCliArgs() {
|
|
|
44062
44035
|
"allow-all": { type: "boolean", default: false },
|
|
44063
44036
|
"raw-sysmsg": { type: "string" },
|
|
44064
44037
|
"no-watch": { type: "boolean", default: false },
|
|
44038
|
+
"no-bridge": { type: "boolean", default: false },
|
|
44065
44039
|
"no-edge": { type: "boolean", default: false },
|
|
44066
44040
|
json: { type: "boolean", default: false },
|
|
44067
44041
|
detailed: { type: "boolean", default: false },
|
|
@@ -44077,6 +44051,8 @@ function parseCliArgs() {
|
|
|
44077
44051
|
allowPositionals: true,
|
|
44078
44052
|
strict: false
|
|
44079
44053
|
});
|
|
44054
|
+
if (values["no-edge"])
|
|
44055
|
+
values["no-bridge"] = true;
|
|
44080
44056
|
return { values, positionals };
|
|
44081
44057
|
}
|
|
44082
44058
|
|
|
@@ -44423,12 +44399,134 @@ Cancelled
|
|
|
44423
44399
|
|
|
44424
44400
|
// src/agent.ts
|
|
44425
44401
|
import { resolve, basename } from "path";
|
|
44402
|
+
|
|
44403
|
+
// src/ensure-bridge.ts
|
|
44404
|
+
import { spawn, spawnSync } from "child_process";
|
|
44405
|
+
import fs2 from "fs";
|
|
44406
|
+
import path3 from "path";
|
|
44407
|
+
import os3 from "os";
|
|
44408
|
+
function hasBridge() {
|
|
44409
|
+
const probe = spawnSync("todoforai-bridge", ["--version"], { stdio: "ignore" });
|
|
44410
|
+
return probe.status === 0;
|
|
44411
|
+
}
|
|
44412
|
+
function isLocalHost(hostname) {
|
|
44413
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
44414
|
+
}
|
|
44415
|
+
function parseApiUrl(apiUrl) {
|
|
44416
|
+
try {
|
|
44417
|
+
return new URL(apiUrl);
|
|
44418
|
+
} catch {
|
|
44419
|
+
return null;
|
|
44420
|
+
}
|
|
44421
|
+
}
|
|
44422
|
+
function bridgeProfile(apiUrl) {
|
|
44423
|
+
const url = parseApiUrl(apiUrl);
|
|
44424
|
+
if (!url)
|
|
44425
|
+
return null;
|
|
44426
|
+
if (isLocalHost(url.hostname))
|
|
44427
|
+
return "dev";
|
|
44428
|
+
if (!url.hostname || url.hostname === "api.todofor.ai")
|
|
44429
|
+
return null;
|
|
44430
|
+
return `api_${url.hostname.replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
44431
|
+
}
|
|
44432
|
+
function withProfile(args, apiUrl) {
|
|
44433
|
+
const profile = bridgeProfile(apiUrl);
|
|
44434
|
+
return profile ? [...args, "--profile", profile] : args;
|
|
44435
|
+
}
|
|
44436
|
+
function bridgeRunArgs(apiUrl) {
|
|
44437
|
+
const url = parseApiUrl(apiUrl);
|
|
44438
|
+
if (!url)
|
|
44439
|
+
return [];
|
|
44440
|
+
if (isLocalHost(url.hostname)) {
|
|
44441
|
+
const args = ["--host", url.hostname];
|
|
44442
|
+
if (url.port)
|
|
44443
|
+
args.push("--port", url.port);
|
|
44444
|
+
return withProfile(args, apiUrl);
|
|
44445
|
+
}
|
|
44446
|
+
if (url.hostname && url.hostname !== "api.todofor.ai")
|
|
44447
|
+
return withProfile(["--host", url.hostname], apiUrl);
|
|
44448
|
+
return [];
|
|
44449
|
+
}
|
|
44450
|
+
function bridgeLoginArgs(apiUrl) {
|
|
44451
|
+
const url = parseApiUrl(apiUrl);
|
|
44452
|
+
if (!url)
|
|
44453
|
+
return ["login"];
|
|
44454
|
+
if (url.hostname && url.hostname !== "api.todofor.ai")
|
|
44455
|
+
return withProfile(["login", "--host", url.hostname], apiUrl);
|
|
44456
|
+
return ["login"];
|
|
44457
|
+
}
|
|
44458
|
+
function bridgeWhoamiArgs(apiUrl) {
|
|
44459
|
+
return withProfile(["whoami"], apiUrl);
|
|
44460
|
+
}
|
|
44461
|
+
function bridgeDeviceId(apiUrl) {
|
|
44462
|
+
const r = spawnSync("todoforai-bridge", bridgeWhoamiArgs(apiUrl), { encoding: "utf-8" });
|
|
44463
|
+
if (r.status !== 0)
|
|
44464
|
+
return null;
|
|
44465
|
+
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);
|
|
44466
|
+
return m ? m[1] : null;
|
|
44467
|
+
}
|
|
44468
|
+
function ensureBridgeCredentials(apiUrl) {
|
|
44469
|
+
const whoami = spawnSync("todoforai-bridge", bridgeWhoamiArgs(apiUrl), { stdio: "ignore" });
|
|
44470
|
+
if (whoami.status === 0)
|
|
44471
|
+
return true;
|
|
44472
|
+
console.error("\x1B[2mBridge credentials not found. Starting `todoforai-bridge login`...\x1B[0m");
|
|
44473
|
+
const login = spawnSync("todoforai-bridge", bridgeLoginArgs(apiUrl), { stdio: "inherit" });
|
|
44474
|
+
return login.status === 0;
|
|
44475
|
+
}
|
|
44476
|
+
function ensureBridgeRunning(apiUrl, _apiKey) {
|
|
44477
|
+
if (!hasBridge()) {
|
|
44478
|
+
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");
|
|
44479
|
+
return;
|
|
44480
|
+
}
|
|
44481
|
+
if (!ensureBridgeCredentials(apiUrl)) {
|
|
44482
|
+
console.error("\x1B[33mBridge not started: `todoforai-bridge login` did not complete successfully.\x1B[0m");
|
|
44483
|
+
return;
|
|
44484
|
+
}
|
|
44485
|
+
const logDir = path3.join(os3.homedir(), ".todoforai");
|
|
44486
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
44487
|
+
const logFile = path3.join(logDir, "bridge.log");
|
|
44488
|
+
const out = fs2.openSync(logFile, "a");
|
|
44489
|
+
const child = spawn("todoforai-bridge", bridgeRunArgs(apiUrl), {
|
|
44490
|
+
detached: true,
|
|
44491
|
+
stdio: ["ignore", out, out]
|
|
44492
|
+
});
|
|
44493
|
+
child.on("error", (err) => {
|
|
44494
|
+
console.error(`\x1B[33mFailed to start bridge: ${err.message}\x1B[0m`);
|
|
44495
|
+
});
|
|
44496
|
+
let exited = false;
|
|
44497
|
+
let exitCode = null;
|
|
44498
|
+
child.on("exit", (code) => {
|
|
44499
|
+
exited = true;
|
|
44500
|
+
exitCode = code;
|
|
44501
|
+
});
|
|
44502
|
+
child.unref();
|
|
44503
|
+
const pid = child.pid;
|
|
44504
|
+
if (!pid)
|
|
44505
|
+
return;
|
|
44506
|
+
const shortLog = logFile.replace(os3.homedir(), "~");
|
|
44507
|
+
setTimeout(() => {
|
|
44508
|
+
if (!exited) {
|
|
44509
|
+
console.error(`\x1B[2mStarted bridge (pid ${pid}), logs: ${shortLog}\x1B[0m`);
|
|
44510
|
+
return;
|
|
44511
|
+
}
|
|
44512
|
+
if (exitCode === 0) {
|
|
44513
|
+
console.error(`\x1B[2mBridge exited cleanly. Logs: ${shortLog}\x1B[0m`);
|
|
44514
|
+
} else {
|
|
44515
|
+
console.error(`\x1B[33mBridge exited early (exit ${exitCode}). Check logs: ${shortLog}. Another instance may already be running.\x1B[0m`);
|
|
44516
|
+
}
|
|
44517
|
+
}, 500);
|
|
44518
|
+
}
|
|
44519
|
+
|
|
44520
|
+
// src/agent.ts
|
|
44426
44521
|
function getAgentWorkspacePaths(agent) {
|
|
44427
44522
|
const paths = [];
|
|
44428
44523
|
for (const ec of Object.values(agent.edgesMcpConfigs || {})) {
|
|
44429
44524
|
const tc = ec?.todoai_edge || ec?.todoai || {};
|
|
44430
44525
|
paths.push(...tc.workspacePaths || []);
|
|
44431
44526
|
}
|
|
44527
|
+
for (const dc of Object.values(agent.devicesConfig || {})) {
|
|
44528
|
+
paths.push(...dc?.workspacePaths || []);
|
|
44529
|
+
}
|
|
44432
44530
|
return paths;
|
|
44433
44531
|
}
|
|
44434
44532
|
async function autoCreateAgent(api, resolvedPath) {
|
|
@@ -44439,10 +44537,20 @@ async function autoCreateAgent(api, resolvedPath) {
|
|
|
44439
44537
|
throw new Error(`Failed to create agent: ${JSON.stringify(resp)}`);
|
|
44440
44538
|
const agentSettingsId = resp.agentSettingsId || agentId;
|
|
44441
44539
|
await api.updateAgentSettings(agentId, agentSettingsId, { name: folderName });
|
|
44540
|
+
const deviceId = bridgeDeviceId(api.apiUrl);
|
|
44541
|
+
if (deviceId) {
|
|
44542
|
+
await api.setAgentDeviceConfig(agentId, agentSettingsId, deviceId, { workspacePaths: [resolvedPath] });
|
|
44543
|
+
const matches2 = await api.listAgentSettings({ workspacePath: resolvedPath });
|
|
44544
|
+
if (matches2.length)
|
|
44545
|
+
return matches2[0];
|
|
44546
|
+
resp.name = folderName;
|
|
44547
|
+
resp.devicesConfig = { [deviceId]: { workspacePaths: [resolvedPath] } };
|
|
44548
|
+
return resp;
|
|
44549
|
+
}
|
|
44442
44550
|
const edges = await api.listEdges();
|
|
44443
44551
|
const edgeId = Array.isArray(edges) && edges.length ? edges[0].id : null;
|
|
44444
44552
|
if (!edgeId)
|
|
44445
|
-
throw new Error("No edge available to configure workspace path");
|
|
44553
|
+
throw new Error("No bridge device or edge available to configure workspace path");
|
|
44446
44554
|
await api.setAgentEdgeMcpConfig(agentId, agentSettingsId, edgeId, "todoai_edge", { workspacePaths: [resolvedPath] });
|
|
44447
44555
|
const matches = await api.listAgentSettings({ workspacePath: resolvedPath });
|
|
44448
44556
|
if (matches.length)
|
|
@@ -46118,55 +46226,6 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
46118
46226
|
`);
|
|
46119
46227
|
}
|
|
46120
46228
|
|
|
46121
|
-
// src/ensure-edge.ts
|
|
46122
|
-
import { spawn, spawnSync } from "child_process";
|
|
46123
|
-
import fs2 from "fs";
|
|
46124
|
-
import path3 from "path";
|
|
46125
|
-
import os3 from "os";
|
|
46126
|
-
function hasBunx() {
|
|
46127
|
-
const probe = spawnSync(process.platform === "win32" ? "where" : "which", ["bunx"], { stdio: "ignore" });
|
|
46128
|
-
return probe.status === 0;
|
|
46129
|
-
}
|
|
46130
|
-
function ensureEdgeRunning(apiUrl, apiKey) {
|
|
46131
|
-
if (!hasBunx()) {
|
|
46132
|
-
console.error("\x1B[2mEdge daemon not started: `bunx` is missing. Install Bun from https://bun.sh to enable it, or pass --no-edge to silence this.\x1B[0m");
|
|
46133
|
-
return;
|
|
46134
|
-
}
|
|
46135
|
-
const logDir = path3.join(os3.homedir(), ".todoforai");
|
|
46136
|
-
fs2.mkdirSync(logDir, { recursive: true });
|
|
46137
|
-
const logFile = path3.join(logDir, "edge.log");
|
|
46138
|
-
const out = fs2.openSync(logFile, "a");
|
|
46139
|
-
const child = spawn("bunx", ["@todoforai/edge", "--api-url", apiUrl, "--api-key", apiKey], {
|
|
46140
|
-
detached: true,
|
|
46141
|
-
stdio: ["ignore", out, out]
|
|
46142
|
-
});
|
|
46143
|
-
child.on("error", (err) => {
|
|
46144
|
-
console.error(`\x1B[33mFailed to start edge daemon: ${err.message}\x1B[0m`);
|
|
46145
|
-
});
|
|
46146
|
-
let exited = false;
|
|
46147
|
-
let exitCode = null;
|
|
46148
|
-
child.on("exit", (code) => {
|
|
46149
|
-
exited = true;
|
|
46150
|
-
exitCode = code;
|
|
46151
|
-
});
|
|
46152
|
-
child.unref();
|
|
46153
|
-
const pid = child.pid;
|
|
46154
|
-
if (!pid)
|
|
46155
|
-
return;
|
|
46156
|
-
const shortLog = logFile.replace(os3.homedir(), "~");
|
|
46157
|
-
setTimeout(() => {
|
|
46158
|
-
if (!exited) {
|
|
46159
|
-
console.error(`\x1B[2mStarted edge daemon (pid ${pid}), logs: ${shortLog}\x1B[0m`);
|
|
46160
|
-
return;
|
|
46161
|
-
}
|
|
46162
|
-
if (exitCode === 0) {
|
|
46163
|
-
console.error(`\x1B[2mEdge daemon exited cleanly (another instance likely already running). Logs: ${shortLog}\x1B[0m`);
|
|
46164
|
-
} else {
|
|
46165
|
-
console.error(`\x1B[31mEdge daemon died (exit ${exitCode}). Check logs: ${shortLog}\x1B[0m`);
|
|
46166
|
-
}
|
|
46167
|
-
}, 500);
|
|
46168
|
-
}
|
|
46169
|
-
|
|
46170
46229
|
// src/index.ts
|
|
46171
46230
|
try {
|
|
46172
46231
|
const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
|
|
@@ -46448,8 +46507,8 @@ Cancelled by user (Ctrl+C)
|
|
|
46448
46507
|
if (process.stderr.isTTY)
|
|
46449
46508
|
printLogo();
|
|
46450
46509
|
if (args.template) {
|
|
46451
|
-
if (!args["no-
|
|
46452
|
-
|
|
46510
|
+
if (!args["no-bridge"] && !args["no-watch"])
|
|
46511
|
+
ensureBridgeRunning(apiUrl, apiKey);
|
|
46453
46512
|
const templateId = args.template;
|
|
46454
46513
|
const inputValues = {};
|
|
46455
46514
|
for (const kv of args.input || []) {
|
|
@@ -46536,8 +46595,8 @@ ${"\u2500".repeat(40)}
|
|
|
46536
46595
|
`);
|
|
46537
46596
|
}
|
|
46538
46597
|
if (args.resume || args.continue) {
|
|
46539
|
-
if (!args["no-
|
|
46540
|
-
|
|
46598
|
+
if (!args["no-bridge"])
|
|
46599
|
+
ensureBridgeRunning(apiUrl, apiKey);
|
|
46541
46600
|
const todoId = args.resume || cfgScope.data.last_todo_id;
|
|
46542
46601
|
if (!todoId) {
|
|
46543
46602
|
process.stderr.write(`Error: No recent todo found
|
|
@@ -46622,8 +46681,8 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
|
|
|
46622
46681
|
}
|
|
46623
46682
|
process.stderr.write(`${DIM}Tip: ${randomTip()}${RESET}
|
|
46624
46683
|
`);
|
|
46625
|
-
if (!args["no-
|
|
46626
|
-
|
|
46684
|
+
if (!args["no-bridge"] && !args["no-watch"])
|
|
46685
|
+
ensureBridgeRunning(apiUrl, apiKey);
|
|
46627
46686
|
let content;
|
|
46628
46687
|
if (positionals.length > 0) {
|
|
46629
46688
|
content = positionals.join(" ");
|