@todoforai/cli 0.1.27 → 0.1.28
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/bin/todoforai-cli.js +1 -1
- package/dist/{todoai.js → todoforai-cli.js} +287 -212
- package/package.json +4 -12
package/bin/todoforai-cli.js
CHANGED
|
@@ -5,5 +5,5 @@ import { existsSync } from "node:fs";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
const src = fileURLToPath(new URL("../src/index.ts", import.meta.url));
|
|
8
|
-
const dist = fileURLToPath(new URL("../dist/
|
|
8
|
+
const dist = fileURLToPath(new URL("../dist/todoforai-cli.js", import.meta.url));
|
|
9
9
|
await import(existsSync(src) ? src : dist);
|
|
@@ -41771,9 +41771,9 @@ var require_route = __commonJS((exports, module) => {
|
|
|
41771
41771
|
var conversions = require_conversions();
|
|
41772
41772
|
function buildGraph() {
|
|
41773
41773
|
const graph = {};
|
|
41774
|
-
const
|
|
41775
|
-
for (let len =
|
|
41776
|
-
graph[
|
|
41774
|
+
const models2 = Object.keys(conversions);
|
|
41775
|
+
for (let len = models2.length, i = 0;i < len; i++) {
|
|
41776
|
+
graph[models2[i]] = {
|
|
41777
41777
|
distance: -1,
|
|
41778
41778
|
parent: null
|
|
41779
41779
|
};
|
|
@@ -41805,23 +41805,23 @@ var require_route = __commonJS((exports, module) => {
|
|
|
41805
41805
|
};
|
|
41806
41806
|
}
|
|
41807
41807
|
function wrapConversion(toModel, graph) {
|
|
41808
|
-
const
|
|
41808
|
+
const path3 = [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
|
+
path3.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 = path3;
|
|
41817
41817
|
return fn;
|
|
41818
41818
|
}
|
|
41819
41819
|
module.exports = function(fromModel) {
|
|
41820
41820
|
const graph = deriveBFS(fromModel);
|
|
41821
41821
|
const conversion = {};
|
|
41822
|
-
const
|
|
41823
|
-
for (let len =
|
|
41824
|
-
const toModel =
|
|
41822
|
+
const models2 = Object.keys(graph);
|
|
41823
|
+
for (let len = models2.length, i = 0;i < len; i++) {
|
|
41824
|
+
const toModel = models2[i];
|
|
41825
41825
|
const node = graph[toModel];
|
|
41826
41826
|
if (node.parent === null) {
|
|
41827
41827
|
continue;
|
|
@@ -41837,7 +41837,7 @@ var require_color_convert = __commonJS((exports, module) => {
|
|
|
41837
41837
|
var conversions = require_conversions();
|
|
41838
41838
|
var route = require_route();
|
|
41839
41839
|
var convert = {};
|
|
41840
|
-
var
|
|
41840
|
+
var models2 = Object.keys(conversions);
|
|
41841
41841
|
function wrapRaw(fn) {
|
|
41842
41842
|
const wrappedFn = function(...args) {
|
|
41843
41843
|
const arg0 = args[0];
|
|
@@ -41876,7 +41876,7 @@ var require_color_convert = __commonJS((exports, module) => {
|
|
|
41876
41876
|
}
|
|
41877
41877
|
return wrappedFn;
|
|
41878
41878
|
}
|
|
41879
|
-
|
|
41879
|
+
models2.forEach((fromModel) => {
|
|
41880
41880
|
convert[fromModel] = {};
|
|
41881
41881
|
Object.defineProperty(convert[fromModel], "channels", { value: conversions[fromModel].channels });
|
|
41882
41882
|
Object.defineProperty(convert[fromModel], "labels", { value: conversions[fromModel].labels });
|
|
@@ -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 os3 = __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 = os3.release().split(".");
|
|
42091
42091
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
42092
42092
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
42093
42093
|
}
|
|
@@ -42665,7 +42665,7 @@ import { realpathSync, readFileSync as readFileSync3 } from "fs";
|
|
|
42665
42665
|
import { resolve as resolve3 } from "path";
|
|
42666
42666
|
import { fileURLToPath } from "url";
|
|
42667
42667
|
import { homedir as homedir3 } from "os";
|
|
42668
|
-
import
|
|
42668
|
+
import path3 from "path";
|
|
42669
42669
|
|
|
42670
42670
|
// node_modules/@todoforai/update-notifier/src/index.ts
|
|
42671
42671
|
import fs from "fs";
|
|
@@ -42731,7 +42731,7 @@ function randomTip() {
|
|
|
42731
42731
|
return TIPS[Math.floor(Math.random() * TIPS.length)];
|
|
42732
42732
|
}
|
|
42733
42733
|
|
|
42734
|
-
// ../
|
|
42734
|
+
// ../packages/shared-api/src/api.ts
|
|
42735
42735
|
function restBasePath(apiKey) {
|
|
42736
42736
|
return apiKey.startsWith("dst_") ? "/dst/v1" : "/api/v1";
|
|
42737
42737
|
}
|
|
@@ -42812,8 +42812,8 @@ class ApiClient {
|
|
|
42812
42812
|
deleteProject(projectId) {
|
|
42813
42813
|
return this.request("DELETE", `/api/v1/projects/${projectId}`);
|
|
42814
42814
|
}
|
|
42815
|
-
createTodo(projectId, content, agentSettings) {
|
|
42816
|
-
return this.request("POST", `/api/v1/projects/${projectId}/todos`, { content, agentSettings });
|
|
42815
|
+
createTodo(projectId, content, agentSettings, groupTag, groupName) {
|
|
42816
|
+
return this.request("POST", `/api/v1/projects/${projectId}/todos`, { content, agentSettings, groupTag, groupName });
|
|
42817
42817
|
}
|
|
42818
42818
|
listTodos(projectId, opts) {
|
|
42819
42819
|
if (!projectId)
|
|
@@ -42851,12 +42851,16 @@ class ApiClient {
|
|
|
42851
42851
|
getAgentSettings(id) {
|
|
42852
42852
|
return this.request("GET", `/api/v1/agents/${id}`);
|
|
42853
42853
|
}
|
|
42854
|
-
addMessage(projectId, content, agentSettings, todoId, attachments, scheduledTimestamp) {
|
|
42854
|
+
addMessage(projectId, content, agentSettings, todoId, attachments, scheduledTimestamp, groupTag, groupName) {
|
|
42855
42855
|
const payload = { content, agentSettings, attachments: attachments || [] };
|
|
42856
42856
|
if (todoId)
|
|
42857
42857
|
payload.todoId = todoId;
|
|
42858
42858
|
if (scheduledTimestamp)
|
|
42859
42859
|
payload.scheduledTimestamp = scheduledTimestamp;
|
|
42860
|
+
if (!todoId && groupTag)
|
|
42861
|
+
payload.groupTag = groupTag;
|
|
42862
|
+
if (!todoId && groupName)
|
|
42863
|
+
payload.groupName = groupName;
|
|
42860
42864
|
return this.request("POST", `/api/v1/projects/${projectId}/todos`, payload);
|
|
42861
42865
|
}
|
|
42862
42866
|
async showFile(todoId, file, filename, opts = {}) {
|
|
@@ -42898,6 +42902,9 @@ class ApiClient {
|
|
|
42898
42902
|
updateAgentSettings(agentId, agentSettingsId, updates) {
|
|
42899
42903
|
return this.request("PUT", `/api/v1/agents/${agentId}/settings`, { agentSettingsId, updates });
|
|
42900
42904
|
}
|
|
42905
|
+
deleteAgentSettings(agentSettingsId) {
|
|
42906
|
+
return this.request("DELETE", `/api/v1/agents/${agentSettingsId}`);
|
|
42907
|
+
}
|
|
42901
42908
|
getGlobalAgentSettings() {
|
|
42902
42909
|
return this.request("GET", "/api/v1/agents/global");
|
|
42903
42910
|
}
|
|
@@ -42933,11 +42940,11 @@ class ApiClient {
|
|
|
42933
42940
|
throw new Error(`Spec '${specId}' not found (${res.status})`);
|
|
42934
42941
|
return res.json();
|
|
42935
42942
|
}
|
|
42936
|
-
async initDeviceLogin(clientName = "edge") {
|
|
42943
|
+
async initDeviceLogin(clientName = "edge", opts = {}) {
|
|
42937
42944
|
const res = await fetch(`${this.apiUrl}/api/v1/cli/login/init`, {
|
|
42938
42945
|
method: "POST",
|
|
42939
42946
|
headers: { "content-type": "application/json" },
|
|
42940
|
-
body: JSON.stringify({ clientName })
|
|
42947
|
+
body: JSON.stringify({ clientName, ...opts })
|
|
42941
42948
|
});
|
|
42942
42949
|
if (!res.ok)
|
|
42943
42950
|
throw new Error(`Device login init failed: ${res.status}`);
|
|
@@ -42957,35 +42964,50 @@ class ApiClient {
|
|
|
42957
42964
|
return res.json();
|
|
42958
42965
|
}
|
|
42959
42966
|
}
|
|
42960
|
-
|
|
42961
|
-
// ../edge/bun/src/frontend-ws.ts
|
|
42967
|
+
// ../packages/shared-api/src/frontend-ws.ts
|
|
42962
42968
|
import WebSocket from "ws";
|
|
42963
42969
|
|
|
42964
|
-
// ../
|
|
42965
|
-
import
|
|
42966
|
-
import
|
|
42970
|
+
// ../packages/shared-credentials/src/index.ts
|
|
42971
|
+
import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "fs";
|
|
42972
|
+
import { join, dirname } from "path";
|
|
42973
|
+
import { homedir, platform } from "os";
|
|
42967
42974
|
function normalizeApiUrl(url) {
|
|
42968
42975
|
if (url.startsWith("localhost"))
|
|
42969
42976
|
return `http://${url}`;
|
|
42970
42977
|
if (!url.startsWith("http://") && !url.startsWith("https://"))
|
|
42971
42978
|
return `https://${url}`;
|
|
42972
|
-
return url;
|
|
42979
|
+
return url.replace(/\/+$/, "");
|
|
42973
42980
|
}
|
|
42974
|
-
var SUBCOMMANDS = new Set(["login", "logout", "update"]);
|
|
42975
42981
|
function credentialsPath() {
|
|
42976
|
-
|
|
42977
|
-
|
|
42978
|
-
|
|
42979
|
-
|
|
42980
|
-
|
|
42981
|
-
|
|
42982
|
+
return platform() === "win32" ? join(homedir(), "AppData", "Roaming", "todoforai", "credentials.json") : platform() === "darwin" ? join(homedir(), "Library", "Application Support", "todoforai", "credentials.json") : join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "todoforai", "credentials.json");
|
|
42983
|
+
}
|
|
42984
|
+
function read() {
|
|
42985
|
+
try {
|
|
42986
|
+
return JSON.parse(readFileSync(credentialsPath(), "utf-8"));
|
|
42987
|
+
} catch {
|
|
42988
|
+
return {};
|
|
42982
42989
|
}
|
|
42983
|
-
const xdg = process.env.XDG_CONFIG_HOME || path2.join(os2.homedir(), ".config");
|
|
42984
|
-
return path2.join(xdg, "todoforai", "credentials.json");
|
|
42985
42990
|
}
|
|
42986
|
-
|
|
42991
|
+
function writeSecure(data) {
|
|
42992
|
+
const p = credentialsPath();
|
|
42993
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
42994
|
+
writeFileSync(p, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
|
|
42995
|
+
try {
|
|
42996
|
+
chmodSync(p, 384);
|
|
42997
|
+
} catch {}
|
|
42998
|
+
}
|
|
42999
|
+
function readCredential(apiUrl) {
|
|
43000
|
+
const c = read();
|
|
43001
|
+
const v = c[apiUrl] || c.apiToken || c.apiKey;
|
|
43002
|
+
return typeof v === "string" ? v : "";
|
|
43003
|
+
}
|
|
43004
|
+
function writeCredential(apiUrl, apiKey) {
|
|
43005
|
+
const c = read();
|
|
43006
|
+
c[apiUrl] = apiKey;
|
|
43007
|
+
writeSecure(c);
|
|
43008
|
+
}
|
|
42987
43009
|
|
|
42988
|
-
// ../
|
|
43010
|
+
// ../packages/shared-api/src/frontend-ws.ts
|
|
42989
43011
|
var log = (level, ...args) => console.log(`[frontend-ws:${level}]`, ...args);
|
|
42990
43012
|
|
|
42991
43013
|
class FrontendWebSocket {
|
|
@@ -43174,48 +43196,6 @@ class FrontendWebSocket {
|
|
|
43174
43196
|
});
|
|
43175
43197
|
}
|
|
43176
43198
|
}
|
|
43177
|
-
|
|
43178
|
-
// src/args.ts
|
|
43179
|
-
import { parseArgs } from "util";
|
|
43180
|
-
// package.json
|
|
43181
|
-
var package_default = {
|
|
43182
|
-
name: "@todoforai/cli",
|
|
43183
|
-
version: "0.1.27",
|
|
43184
|
-
type: "module",
|
|
43185
|
-
bin: {
|
|
43186
|
-
"todoforai-cli": "bin/todoforai-cli.js",
|
|
43187
|
-
todoai: "bin/todoforai-cli.js"
|
|
43188
|
-
},
|
|
43189
|
-
files: [
|
|
43190
|
-
"bin",
|
|
43191
|
-
"dist/todoai.js"
|
|
43192
|
-
],
|
|
43193
|
-
scripts: {
|
|
43194
|
-
build: "bun build src/index.ts --target=bun --outfile dist/todoai.js --external ws",
|
|
43195
|
-
prepublishOnly: "bun run build",
|
|
43196
|
-
start: "bun run src/index.ts",
|
|
43197
|
-
dev: "bun run src/index.ts"
|
|
43198
|
-
},
|
|
43199
|
-
dependencies: {
|
|
43200
|
-
"@shared/credentials": "file:../packages/shared-credentials",
|
|
43201
|
-
"@todoforai/update-notifier": "^0.1.0",
|
|
43202
|
-
"cli-highlight": "^2.1.11",
|
|
43203
|
-
"diff-match-patch": "^1.0.5",
|
|
43204
|
-
ws: "^8.18.0"
|
|
43205
|
-
},
|
|
43206
|
-
peerDependencies: {
|
|
43207
|
-
"@todoforai/edge": ">=0.12.0"
|
|
43208
|
-
},
|
|
43209
|
-
peerDependenciesMeta: {
|
|
43210
|
-
"@todoforai/edge": {
|
|
43211
|
-
optional: true
|
|
43212
|
-
}
|
|
43213
|
-
},
|
|
43214
|
-
devDependencies: {
|
|
43215
|
-
"@types/ws": "^8.5.13",
|
|
43216
|
-
typescript: "^5.7.0"
|
|
43217
|
-
}
|
|
43218
|
-
};
|
|
43219
43199
|
// ../packages/shared-fbe/src/enums.ts
|
|
43220
43200
|
var TodoStatus;
|
|
43221
43201
|
((TodoStatus2) => {
|
|
@@ -43238,6 +43218,59 @@ var TodoStatus;
|
|
|
43238
43218
|
TodoStatus2["ARCHIVED"] = "ARCHIVED";
|
|
43239
43219
|
TodoStatus2["DELETED"] = "DELETED";
|
|
43240
43220
|
})(TodoStatus ||= {});
|
|
43221
|
+
// ../packages/shared-fbe/src/models.ts
|
|
43222
|
+
var DEFAULT_MODEL = "anthropic:anthropic/claude-sonnet-5";
|
|
43223
|
+
var MODEL_ALIASES = {
|
|
43224
|
+
claude: DEFAULT_MODEL
|
|
43225
|
+
};
|
|
43226
|
+
var PROVIDER_BY_AUTHOR = {
|
|
43227
|
+
anthropic: "anthropic",
|
|
43228
|
+
openai: "openai",
|
|
43229
|
+
deepseek: "deepseek",
|
|
43230
|
+
poolside: "poolside",
|
|
43231
|
+
"x-ai": "xai",
|
|
43232
|
+
"z-ai": "z.ai",
|
|
43233
|
+
google: "google-ai-studio",
|
|
43234
|
+
moonshotai: "opencode_go"
|
|
43235
|
+
};
|
|
43236
|
+
function isQualifiedModel(model) {
|
|
43237
|
+
const id = model.trim();
|
|
43238
|
+
const colon = id.indexOf(":");
|
|
43239
|
+
const slash = id.indexOf("/");
|
|
43240
|
+
return colon > 0 && colon < id.length - 1 && (slash === -1 || colon < slash);
|
|
43241
|
+
}
|
|
43242
|
+
function qualifyModel(model) {
|
|
43243
|
+
const id = model?.trim();
|
|
43244
|
+
if (!id)
|
|
43245
|
+
return { model: DEFAULT_MODEL };
|
|
43246
|
+
if (isQualifiedModel(id))
|
|
43247
|
+
return { model: id };
|
|
43248
|
+
const suffix = id.match(/\([^)]+\)$/)?.[0] ?? "";
|
|
43249
|
+
const bare = suffix ? id.slice(0, -suffix.length) : id;
|
|
43250
|
+
const aliased = MODEL_ALIASES[bare.toLowerCase()];
|
|
43251
|
+
if (aliased)
|
|
43252
|
+
return { model: aliased + suffix };
|
|
43253
|
+
const lower = bare.toLowerCase();
|
|
43254
|
+
const provider = PROVIDER_BY_AUTHOR[lower.split("/")[0]];
|
|
43255
|
+
if (provider && lower.includes("/"))
|
|
43256
|
+
return { model: `${provider}:${lower}${suffix}` };
|
|
43257
|
+
return { model: DEFAULT_MODEL, fallback: id };
|
|
43258
|
+
}
|
|
43259
|
+
function qualifiedModelIds(listing, filter) {
|
|
43260
|
+
const q = filter?.toLowerCase();
|
|
43261
|
+
const ids = [];
|
|
43262
|
+
for (const m of listing?.data ?? []) {
|
|
43263
|
+
const id = String(m.id);
|
|
43264
|
+
if (m.provider) {
|
|
43265
|
+
ids.push(`${m.provider.toLowerCase()}:${id}`);
|
|
43266
|
+
continue;
|
|
43267
|
+
}
|
|
43268
|
+
const r = qualifyModel(id);
|
|
43269
|
+
if (!r.fallback)
|
|
43270
|
+
ids.push(r.model);
|
|
43271
|
+
}
|
|
43272
|
+
return q ? ids.filter((id) => id.toLowerCase().includes(q)) : ids;
|
|
43273
|
+
}
|
|
43241
43274
|
// ../packages/shared-fbe/src/billing.ts
|
|
43242
43275
|
var USAGE_WINDOWS = {
|
|
43243
43276
|
SESSION_MS: 5 * 60 * 60 * 1000,
|
|
@@ -43288,7 +43321,7 @@ var mimetypes_default = {
|
|
|
43288
43321
|
gif: { mime: "image/gif", type: "image" },
|
|
43289
43322
|
webp: { mime: "image/webp", type: "image" },
|
|
43290
43323
|
bmp: { mime: "image/bmp", type: "image" },
|
|
43291
|
-
svg: { mime: "image/svg+xml", type: "
|
|
43324
|
+
svg: { mime: "image/svg+xml", type: "image" },
|
|
43292
43325
|
heic: { mime: "image/heic", type: "image", conversionProfile: "to_jpeg_90" },
|
|
43293
43326
|
heif: { mime: "image/heif", type: "image", conversionProfile: "to_jpeg_90" },
|
|
43294
43327
|
mp3: { mime: "audio/mpeg", type: "audio" },
|
|
@@ -43496,7 +43529,7 @@ var SERVER_TO_FRONTENDS = {
|
|
|
43496
43529
|
predicted_reply_updated: (todoId) => `${"predict:reply_updated" /* PREDICTED_REPLY_UPDATED */}:${todoId}`,
|
|
43497
43530
|
error_result: (todoId) => `${"block:error_result" /* BLOCK_ERROR_RESULT */}:${todoId}`,
|
|
43498
43531
|
meta_result: (todoId) => `${"block:meta_result" /* BLOCK_META_RESULT */}:${todoId}`,
|
|
43499
|
-
file_changed: (edgeId,
|
|
43532
|
+
file_changed: (edgeId, path2) => `${"block:file_changed" /* BLOCK_FILE_CHANGED */}:${edgeId}:${path2}`,
|
|
43500
43533
|
notify: (todoId) => `${"NOTIFY" /* NOTIFY */}:${todoId}`
|
|
43501
43534
|
},
|
|
43502
43535
|
notify: {
|
|
@@ -43744,13 +43777,10 @@ function getBlockServerPrefix(block) {
|
|
|
43744
43777
|
const prefixMatch = agentPattern.match(/^(.*?:)BASH(?:\(|$)/);
|
|
43745
43778
|
return prefixMatch ? prefixMatch[1] : "";
|
|
43746
43779
|
}
|
|
43747
|
-
var BASH_LIKE_BLOCK_TYPES = new Set(["bash", "machine_exec"]);
|
|
43748
43780
|
function getBlockPatterns(block) {
|
|
43749
43781
|
if (Array.isArray(block.generalized_pattern))
|
|
43750
43782
|
return block.generalized_pattern;
|
|
43751
|
-
|
|
43752
|
-
const isBashLike = BASH_LIKE_BLOCK_TYPES.has(block.type.toLowerCase()) || /:BASH(\(|$)/.test(agentPattern);
|
|
43753
|
-
if (isBashLike && block.cmd) {
|
|
43783
|
+
if (block.type.toLowerCase() === "bash" && block.cmd) {
|
|
43754
43784
|
const prefix = getBlockServerPrefix(block);
|
|
43755
43785
|
return generalizeBashCmd(block.cmd).map((p) => prefix + p);
|
|
43756
43786
|
}
|
|
@@ -43848,8 +43878,7 @@ function getBlockNewPatterns(block, permissions) {
|
|
|
43848
43878
|
if (Array.isArray(block.generalized_pattern)) {
|
|
43849
43879
|
return getNewPatterns(block.generalized_pattern, permissions);
|
|
43850
43880
|
}
|
|
43851
|
-
const
|
|
43852
|
-
const isBash = (block.type.toLowerCase() === "bash" || agentPattern.includes(":BASH(")) && !!block.cmd;
|
|
43881
|
+
const isBash = block.type.toLowerCase() === "bash" && !!block.cmd;
|
|
43853
43882
|
if (!isBash) {
|
|
43854
43883
|
return getBlockPatterns(block).filter((p) => !isPatternAllowed(permissions, p));
|
|
43855
43884
|
}
|
|
@@ -44124,6 +44153,40 @@ var TOPICS = {
|
|
|
44124
44153
|
audience: "frontend"
|
|
44125
44154
|
}
|
|
44126
44155
|
};
|
|
44156
|
+
// src/args.ts
|
|
44157
|
+
import { parseArgs } from "util";
|
|
44158
|
+
// package.json
|
|
44159
|
+
var package_default = {
|
|
44160
|
+
name: "@todoforai/cli",
|
|
44161
|
+
version: "0.1.28",
|
|
44162
|
+
type: "module",
|
|
44163
|
+
bin: {
|
|
44164
|
+
"todoforai-cli": "bin/todoforai-cli.js",
|
|
44165
|
+
"tfa-cli": "bin/todoforai-cli.js",
|
|
44166
|
+
todoai: "bin/todoforai-cli.js"
|
|
44167
|
+
},
|
|
44168
|
+
files: [
|
|
44169
|
+
"bin",
|
|
44170
|
+
"dist/todoforai-cli.js"
|
|
44171
|
+
],
|
|
44172
|
+
scripts: {
|
|
44173
|
+
build: "bun build src/index.ts --target=bun --outfile dist/todoforai-cli.js --external ws",
|
|
44174
|
+
prepublishOnly: "bun run build",
|
|
44175
|
+
start: "bun run src/index.ts",
|
|
44176
|
+
dev: "bun run src/index.ts"
|
|
44177
|
+
},
|
|
44178
|
+
dependencies: {
|
|
44179
|
+
"@todoforai/update-notifier": "^0.1.0",
|
|
44180
|
+
"cli-highlight": "^2.1.11",
|
|
44181
|
+
"diff-match-patch": "^1.0.5",
|
|
44182
|
+
ws: "^8.18.0"
|
|
44183
|
+
},
|
|
44184
|
+
devDependencies: {
|
|
44185
|
+
"@types/ws": "^8.5.13",
|
|
44186
|
+
typescript: "^5.7.0"
|
|
44187
|
+
}
|
|
44188
|
+
};
|
|
44189
|
+
|
|
44127
44190
|
// src/args.ts
|
|
44128
44191
|
var DEFAULT_API_URL = "https://api.todofor.ai";
|
|
44129
44192
|
var VERSION = package_default.version;
|
|
@@ -44132,7 +44195,7 @@ function getEnv(name) {
|
|
|
44132
44195
|
}
|
|
44133
44196
|
function printUsage() {
|
|
44134
44197
|
process.stderr.write(`
|
|
44135
|
-
todoforai-cli \u2014 TODOforAI CLI (Bun)
|
|
44198
|
+
todoforai-cli \u2014 TODOforAI CLI (Bun). Aliases: tfa-cli, todoai.
|
|
44136
44199
|
|
|
44137
44200
|
Usage:
|
|
44138
44201
|
todoforai-cli login # Browser-based device auth
|
|
@@ -44163,6 +44226,8 @@ Options:
|
|
|
44163
44226
|
--path <dir> Workspace path (default: cwd)
|
|
44164
44227
|
--project <id> Project ID
|
|
44165
44228
|
--agent, -a <name> Agent name (partial match)
|
|
44229
|
+
--group <slug> Group new todo; omitted inherits TODOFORAI_GROUP_ID
|
|
44230
|
+
--group-name <name> Display name for --group (last write wins)
|
|
44166
44231
|
--model <model> Override the agent's model for this todo
|
|
44167
44232
|
(e.g. anthropic:anthropic/claude-opus-5, openai:openai/gpt-5.6-sol)
|
|
44168
44233
|
--list-agents List available agents (name, id, workspace paths) and exit
|
|
@@ -44221,6 +44286,8 @@ function parseCliArgs() {
|
|
|
44221
44286
|
project: { type: "string" },
|
|
44222
44287
|
agent: { type: "string", short: "a" },
|
|
44223
44288
|
model: { type: "string" },
|
|
44289
|
+
group: { type: "string" },
|
|
44290
|
+
"group-name": { type: "string" },
|
|
44224
44291
|
"list-agents": { type: "boolean", default: false },
|
|
44225
44292
|
"list-models": { type: "boolean", default: false },
|
|
44226
44293
|
"api-url": { type: "string" },
|
|
@@ -44601,8 +44668,8 @@ import { resolve, basename } from "path";
|
|
|
44601
44668
|
// src/ensure-bridge.ts
|
|
44602
44669
|
import { spawn, spawnSync } from "child_process";
|
|
44603
44670
|
import fs2 from "fs";
|
|
44604
|
-
import
|
|
44605
|
-
import
|
|
44671
|
+
import path2 from "path";
|
|
44672
|
+
import os2 from "os";
|
|
44606
44673
|
function hasBridge() {
|
|
44607
44674
|
const probe = spawnSync("todoforai-bridge", ["--version"], { stdio: "ignore" });
|
|
44608
44675
|
return probe.status === 0;
|
|
@@ -44671,31 +44738,44 @@ function ensureBridgeCredentials(apiUrl) {
|
|
|
44671
44738
|
const login = spawnSync("todoforai-bridge", bridgeLoginArgs(apiUrl), { stdio: "inherit" });
|
|
44672
44739
|
return login.status === 0;
|
|
44673
44740
|
}
|
|
44674
|
-
function
|
|
44675
|
-
|
|
44676
|
-
|
|
44677
|
-
|
|
44678
|
-
|
|
44679
|
-
|
|
44680
|
-
|
|
44681
|
-
r.on("error", () => resolve(false));
|
|
44682
|
-
r.on("close", (code) => resolve(code === 0 && out.trim().length > 0));
|
|
44683
|
-
});
|
|
44741
|
+
async function bridgeState(apiUrl, apiKey, deviceId) {
|
|
44742
|
+
try {
|
|
44743
|
+
const devices = await new ApiClient(apiUrl, apiKey).listDevices();
|
|
44744
|
+
return Array.isArray(devices) && devices.some((device) => device.id === deviceId && device.status === "ONLINE") ? "online" : "offline";
|
|
44745
|
+
} catch {
|
|
44746
|
+
return "unknown";
|
|
44747
|
+
}
|
|
44684
44748
|
}
|
|
44685
|
-
async function
|
|
44686
|
-
|
|
44687
|
-
|
|
44749
|
+
async function waitForBridgeOnline(apiUrl, apiKey, deviceId, timeoutMs = 5000) {
|
|
44750
|
+
const deadline = Date.now() + timeoutMs;
|
|
44751
|
+
do {
|
|
44752
|
+
const state = await bridgeState(apiUrl, apiKey, deviceId);
|
|
44753
|
+
if (state !== "offline")
|
|
44754
|
+
return state === "online";
|
|
44755
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
44756
|
+
} while (Date.now() < deadline);
|
|
44757
|
+
return false;
|
|
44758
|
+
}
|
|
44759
|
+
async function ensureBridgeRunning(apiUrl, apiKey) {
|
|
44688
44760
|
if (!hasBridge()) {
|
|
44689
44761
|
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");
|
|
44690
|
-
return;
|
|
44762
|
+
return false;
|
|
44691
44763
|
}
|
|
44692
44764
|
if (!ensureBridgeCredentials(apiUrl)) {
|
|
44693
44765
|
console.error("\x1B[33mBridge not started: `todoforai-bridge login` did not complete successfully.\x1B[0m");
|
|
44694
|
-
return;
|
|
44766
|
+
return false;
|
|
44695
44767
|
}
|
|
44696
|
-
const
|
|
44768
|
+
const deviceId = bridgeDeviceId(apiUrl);
|
|
44769
|
+
if (!deviceId) {
|
|
44770
|
+
console.error("\x1B[33mBridge not started: could not determine this device's id.\x1B[0m");
|
|
44771
|
+
return false;
|
|
44772
|
+
}
|
|
44773
|
+
const initialState = await bridgeState(apiUrl, apiKey, deviceId);
|
|
44774
|
+
if (initialState === "online" || initialState === "unknown")
|
|
44775
|
+
return initialState === "online";
|
|
44776
|
+
const logDir = path2.join(os2.homedir(), ".todoforai");
|
|
44697
44777
|
fs2.mkdirSync(logDir, { recursive: true });
|
|
44698
|
-
const logFile =
|
|
44778
|
+
const logFile = path2.join(logDir, "bridge.log");
|
|
44699
44779
|
const out = fs2.openSync(logFile, "a");
|
|
44700
44780
|
const child = spawn("todoforai-bridge", bridgeRunArgs(apiUrl), {
|
|
44701
44781
|
detached: true,
|
|
@@ -44712,13 +44792,13 @@ async function ensureBridgeRunning(apiUrl, _apiKey) {
|
|
|
44712
44792
|
});
|
|
44713
44793
|
child.unref();
|
|
44714
44794
|
if (!child.pid)
|
|
44715
|
-
return;
|
|
44716
|
-
|
|
44717
|
-
|
|
44718
|
-
|
|
44719
|
-
|
|
44720
|
-
|
|
44721
|
-
|
|
44795
|
+
return false;
|
|
44796
|
+
if (await waitForBridgeOnline(apiUrl, apiKey, deviceId))
|
|
44797
|
+
return true;
|
|
44798
|
+
const shortLog = logFile.replace(os2.homedir(), "~");
|
|
44799
|
+
const reason = exited ? `exit ${exitCode}` : "startup timed out";
|
|
44800
|
+
console.error(`\x1B[33mBridge not ready (${reason}), see ${shortLog}\x1B[0m`);
|
|
44801
|
+
return false;
|
|
44722
44802
|
}
|
|
44723
44803
|
|
|
44724
44804
|
// src/agent.ts
|
|
@@ -44752,9 +44832,13 @@ async function autoCreateAgent(api, resolvedPath) {
|
|
|
44752
44832
|
return resp;
|
|
44753
44833
|
}
|
|
44754
44834
|
const edges = await api.listEdges();
|
|
44755
|
-
|
|
44756
|
-
if (!edgeId)
|
|
44835
|
+
if (!Array.isArray(edges) || edges.length === 0) {
|
|
44757
44836
|
throw new Error("No bridge device or edge available to configure workspace path");
|
|
44837
|
+
}
|
|
44838
|
+
if (edges.length > 1) {
|
|
44839
|
+
throw new Error("No local bridge device found and multiple legacy edges are available; choose an existing agent explicitly");
|
|
44840
|
+
}
|
|
44841
|
+
const edgeId = edges[0].id;
|
|
44758
44842
|
await api.setAgentEdgeMcpConfig(agentId, agentSettingsId, edgeId, "todoai_edge", { workspacePaths: [resolvedPath] });
|
|
44759
44843
|
const matches = await api.listAgentSettings({ workspacePath: resolvedPath });
|
|
44760
44844
|
if (matches.length)
|
|
@@ -44765,20 +44849,20 @@ async function autoCreateAgent(api, resolvedPath) {
|
|
|
44765
44849
|
}
|
|
44766
44850
|
|
|
44767
44851
|
// src/config.ts
|
|
44768
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
44769
|
-
import { dirname, join, resolve as resolve2 } from "path";
|
|
44770
|
-
import { homedir, platform } from "os";
|
|
44852
|
+
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
44853
|
+
import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
44854
|
+
import { homedir as homedir2, platform as platform2 } from "os";
|
|
44771
44855
|
function getConfigDir() {
|
|
44772
|
-
const sys =
|
|
44856
|
+
const sys = platform2();
|
|
44773
44857
|
if (sys === "win32") {
|
|
44774
|
-
const base = process.env.APPDATA ||
|
|
44775
|
-
return
|
|
44858
|
+
const base = process.env.APPDATA || join2(homedir2(), "AppData", "Roaming");
|
|
44859
|
+
return join2(base, "todoai-cli");
|
|
44776
44860
|
}
|
|
44777
44861
|
if (sys === "darwin") {
|
|
44778
|
-
return
|
|
44862
|
+
return join2(homedir2(), "Library", "Application Support", "todoai-cli");
|
|
44779
44863
|
}
|
|
44780
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
44781
|
-
return
|
|
44864
|
+
const xdg = process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
|
|
44865
|
+
return join2(xdg, "todoai-cli");
|
|
44782
44866
|
}
|
|
44783
44867
|
function defaultScope() {
|
|
44784
44868
|
return {
|
|
@@ -44830,10 +44914,10 @@ class ConfigStore {
|
|
|
44830
44914
|
data;
|
|
44831
44915
|
constructor(pathArg) {
|
|
44832
44916
|
if (pathArg) {
|
|
44833
|
-
const p = resolve2(pathArg.replace(/^~/,
|
|
44834
|
-
this.path = p.endsWith(".json") ? p :
|
|
44917
|
+
const p = resolve2(pathArg.replace(/^~/, homedir2()));
|
|
44918
|
+
this.path = p.endsWith(".json") ? p : join2(p, "config.json");
|
|
44835
44919
|
} else {
|
|
44836
|
-
this.path =
|
|
44920
|
+
this.path = join2(getConfigDir(), "config.json");
|
|
44837
44921
|
}
|
|
44838
44922
|
this.data = this.load();
|
|
44839
44923
|
}
|
|
@@ -44841,7 +44925,7 @@ class ConfigStore {
|
|
|
44841
44925
|
if (!existsSync(this.path))
|
|
44842
44926
|
return defaultConfig();
|
|
44843
44927
|
try {
|
|
44844
|
-
const raw = JSON.parse(
|
|
44928
|
+
const raw = JSON.parse(readFileSync2(this.path, "utf-8"));
|
|
44845
44929
|
delete raw.default_api_key;
|
|
44846
44930
|
const cfg = migrate(raw);
|
|
44847
44931
|
const needsMigration = raw.per_api_url == null;
|
|
@@ -44856,8 +44940,8 @@ class ConfigStore {
|
|
|
44856
44940
|
}
|
|
44857
44941
|
save() {
|
|
44858
44942
|
try {
|
|
44859
|
-
|
|
44860
|
-
|
|
44943
|
+
mkdirSync2(dirname2(this.path), { recursive: true });
|
|
44944
|
+
writeFileSync2(this.path, JSON.stringify(this.data, null, 2), "utf-8");
|
|
44861
44945
|
} catch {}
|
|
44862
44946
|
}
|
|
44863
44947
|
scope(apiUrl) {
|
|
@@ -44917,39 +45001,6 @@ class ScopedConfig {
|
|
|
44917
45001
|
this.store.save();
|
|
44918
45002
|
}
|
|
44919
45003
|
}
|
|
44920
|
-
|
|
44921
|
-
// ../packages/shared-credentials/src/index.ts
|
|
44922
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, chmodSync } from "fs";
|
|
44923
|
-
import { join as join2, dirname as dirname2 } from "path";
|
|
44924
|
-
import { homedir as homedir2, platform as platform2 } from "os";
|
|
44925
|
-
function credentialsPath2() {
|
|
44926
|
-
return platform2() === "win32" ? join2(homedir2(), "AppData", "Roaming", "todoforai", "credentials.json") : platform2() === "darwin" ? join2(homedir2(), "Library", "Application Support", "todoforai", "credentials.json") : join2(process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config"), "todoforai", "credentials.json");
|
|
44927
|
-
}
|
|
44928
|
-
function read() {
|
|
44929
|
-
try {
|
|
44930
|
-
return JSON.parse(readFileSync2(credentialsPath2(), "utf-8"));
|
|
44931
|
-
} catch {
|
|
44932
|
-
return {};
|
|
44933
|
-
}
|
|
44934
|
-
}
|
|
44935
|
-
function writeSecure(data) {
|
|
44936
|
-
const p = credentialsPath2();
|
|
44937
|
-
mkdirSync2(dirname2(p), { recursive: true });
|
|
44938
|
-
writeFileSync2(p, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
|
|
44939
|
-
try {
|
|
44940
|
-
chmodSync(p, 384);
|
|
44941
|
-
} catch {}
|
|
44942
|
-
}
|
|
44943
|
-
function readCredential(apiUrl) {
|
|
44944
|
-
const c2 = read();
|
|
44945
|
-
const v = c2[apiUrl] || c2.apiToken || c2.apiKey;
|
|
44946
|
-
return typeof v === "string" ? v : "";
|
|
44947
|
-
}
|
|
44948
|
-
function writeCredential(apiUrl, apiKey) {
|
|
44949
|
-
const c2 = read();
|
|
44950
|
-
c2[apiUrl] = apiKey;
|
|
44951
|
-
writeSecure(c2);
|
|
44952
|
-
}
|
|
44953
45004
|
// src/logo.ts
|
|
44954
45005
|
var LETTERS = {
|
|
44955
45006
|
t: [" x ", "xxxx", " x ", " xll", " xll", " xxx"],
|
|
@@ -45023,6 +45074,12 @@ function printLogo() {
|
|
|
45023
45074
|
`);
|
|
45024
45075
|
}
|
|
45025
45076
|
|
|
45077
|
+
// src/urls.ts
|
|
45078
|
+
function getFrontendUrl(apiUrl, todoId) {
|
|
45079
|
+
const local = apiUrl.includes("localhost:4000") || apiUrl.includes("127.0.0.1:4000");
|
|
45080
|
+
return `${local ? "http://localhost:3000" : "https://todofor.ai"}/t/${todoId}`;
|
|
45081
|
+
}
|
|
45082
|
+
|
|
45026
45083
|
// src/inspect.ts
|
|
45027
45084
|
var xmlAttr = (v) => JSON.stringify(String(v));
|
|
45028
45085
|
var DROP_IF_EMPTY = new Set(["runMeta", "meta", "attachments", "blocks"]);
|
|
@@ -45854,17 +45911,6 @@ async function watchTodo(ws, todoId, projectId, opts = {}) {
|
|
|
45854
45911
|
"block:start_catfile"
|
|
45855
45912
|
]);
|
|
45856
45913
|
const signalActivity = () => opts.activityEvent?.set();
|
|
45857
|
-
let edgeId;
|
|
45858
|
-
let rootPath = "";
|
|
45859
|
-
if (opts.agentSettings) {
|
|
45860
|
-
const emc = opts.agentSettings.edgesMcpConfigs || {};
|
|
45861
|
-
edgeId = Object.keys(emc)[0];
|
|
45862
|
-
if (edgeId) {
|
|
45863
|
-
const ec = emc[edgeId];
|
|
45864
|
-
const tc = ec?.todoai_edge || ec?.todoai || {};
|
|
45865
|
-
rootPath = (tc.workspacePaths || [])[0] || "";
|
|
45866
|
-
}
|
|
45867
|
-
}
|
|
45868
45914
|
const blocksStore = new Map;
|
|
45869
45915
|
const diffStore = diffStoreByWs.get(ws) ?? new Map;
|
|
45870
45916
|
diffStoreByWs.set(ws, diffStore);
|
|
@@ -46051,6 +46097,12 @@ ${YELLOW}*${RESET} ${YELLOW}${blockType}${RESET}${extra}
|
|
|
46051
46097
|
`);
|
|
46052
46098
|
signalActivity();
|
|
46053
46099
|
}
|
|
46100
|
+
} else if (msgType === "NOTIFY") {
|
|
46101
|
+
const sevColor = payload.severity === "error" ? RED : YELLOW;
|
|
46102
|
+
process.stderr.write(`
|
|
46103
|
+
${sevColor}\u26A0 ${payload.title || "Notice"}${RESET}${payload.message ? ` ${DIM}${payload.message}${RESET}` : ""}
|
|
46104
|
+
`);
|
|
46105
|
+
signalActivity();
|
|
46054
46106
|
} else if (msgType === "todo:status") {
|
|
46055
46107
|
const status = payload.status || "";
|
|
46056
46108
|
process.stderr.write(`
|
|
@@ -46081,6 +46133,9 @@ ${DIM}[todo:status] ${status}${RESET}
|
|
|
46081
46133
|
const status = result?.payload?.status || result?.type || "unknown";
|
|
46082
46134
|
process.stderr.write(`Warning: Stopped: ${status}
|
|
46083
46135
|
`);
|
|
46136
|
+
process.exitCode = 1;
|
|
46137
|
+
} else {
|
|
46138
|
+
process.exitCode = 0;
|
|
46084
46139
|
}
|
|
46085
46140
|
return true;
|
|
46086
46141
|
} catch (e) {
|
|
@@ -46124,20 +46179,6 @@ async function listAgentsCommand(api, opts) {
|
|
|
46124
46179
|
}
|
|
46125
46180
|
}
|
|
46126
46181
|
|
|
46127
|
-
// src/list-models.ts
|
|
46128
|
-
async function listModelsCommand(api, opts) {
|
|
46129
|
-
const res = await api.listModels();
|
|
46130
|
-
const q = opts.filter?.toLowerCase();
|
|
46131
|
-
const ids = (res?.data || []).map((m) => String(m.id).includes("/") ? `${String(m.id).split("/")[0]}:${m.id}` : String(m.id)).filter((id) => !q || id.toLowerCase().includes(q));
|
|
46132
|
-
if (opts.json) {
|
|
46133
|
-
console.log(JSON.stringify(ids, null, 2));
|
|
46134
|
-
return;
|
|
46135
|
-
}
|
|
46136
|
-
for (const id of ids)
|
|
46137
|
-
process.stderr.write(`${BRAND}${id}${RESET}
|
|
46138
|
-
`);
|
|
46139
|
-
}
|
|
46140
|
-
|
|
46141
46182
|
// src/agent-command.ts
|
|
46142
46183
|
function printAgentHelp() {
|
|
46143
46184
|
process.stderr.write(`
|
|
@@ -46147,6 +46188,7 @@ Usage:
|
|
|
46147
46188
|
todoforai-cli agent list List agents (name, model, id, paths)
|
|
46148
46189
|
todoforai-cli agent get <agent> Show a single agent's settings
|
|
46149
46190
|
todoforai-cli agent update <agent> <field=value>\u2026 Update one or more settings
|
|
46191
|
+
todoforai-cli agent delete <agent> Delete an agent configuration (asks to confirm)
|
|
46150
46192
|
|
|
46151
46193
|
<agent> is a name or id (unique partial name also works).
|
|
46152
46194
|
Fields map directly to agent settings; values are parsed as JSON when possible
|
|
@@ -46244,6 +46286,32 @@ async function agentCommand(api, positionals, args, formatPath) {
|
|
|
46244
46286
|
}
|
|
46245
46287
|
const summary = Object.keys(updates).map((k) => `${k}=${JSON.stringify(updated[k])}`).join(" ");
|
|
46246
46288
|
process.stderr.write(`${GREEN}\u2705 ${getDisplayName(agent)} updated: ${summary}${RESET}
|
|
46289
|
+
`);
|
|
46290
|
+
return;
|
|
46291
|
+
}
|
|
46292
|
+
if (sub === "delete" || sub === "rm") {
|
|
46293
|
+
const query = positionals[2];
|
|
46294
|
+
if (!query) {
|
|
46295
|
+
process.stderr.write(`${RED}Usage: todoforai-cli agent delete <name|id>${RESET}
|
|
46296
|
+
`);
|
|
46297
|
+
process.exit(2);
|
|
46298
|
+
}
|
|
46299
|
+
const agents = await api.listAgentSettings();
|
|
46300
|
+
const agent = resolveAgent(agents, query);
|
|
46301
|
+
const name = getDisplayName(agent);
|
|
46302
|
+
if (!process.stdin.isTTY) {
|
|
46303
|
+
process.stderr.write(`${RED}Refusing to delete '${name}' non-interactively (delete needs an interactive confirm).${RESET}
|
|
46304
|
+
`);
|
|
46305
|
+
process.exit(2);
|
|
46306
|
+
}
|
|
46307
|
+
const ok = await singleChar(`${RED}Delete agent '${name}'? [y/N] ${RESET}`);
|
|
46308
|
+
if (ok !== "y") {
|
|
46309
|
+
process.stderr.write(`Aborted
|
|
46310
|
+
`);
|
|
46311
|
+
return;
|
|
46312
|
+
}
|
|
46313
|
+
await api.deleteAgentSettings(getItemId(agent));
|
|
46314
|
+
process.stderr.write(`${GREEN}\u2705 Deleted ${name}${RESET}
|
|
46247
46315
|
`);
|
|
46248
46316
|
return;
|
|
46249
46317
|
}
|
|
@@ -46449,18 +46517,12 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
46449
46517
|
|
|
46450
46518
|
// src/index.ts
|
|
46451
46519
|
try {
|
|
46452
|
-
const pkgPath =
|
|
46520
|
+
const pkgPath = path3.resolve(fileURLToPath(import.meta.url), "../../package.json");
|
|
46453
46521
|
checkForUpdates(JSON.parse(readFileSync3(pkgPath, "utf-8")));
|
|
46454
46522
|
} catch {}
|
|
46455
|
-
function formatPathWithTilde(
|
|
46523
|
+
function formatPathWithTilde(path4) {
|
|
46456
46524
|
const home = homedir3();
|
|
46457
|
-
return
|
|
46458
|
-
}
|
|
46459
|
-
function getFrontendUrl(apiUrl, projectId, todoId) {
|
|
46460
|
-
if (apiUrl.includes("localhost:4000") || apiUrl.includes("127.0.0.1:4000")) {
|
|
46461
|
-
return `http://localhost:3000/${projectId}/${todoId}`;
|
|
46462
|
-
}
|
|
46463
|
-
return `https://todofor.ai/${projectId}/${todoId}`;
|
|
46525
|
+
return path4.startsWith(home) ? path4.replace(home, "~") : path4;
|
|
46464
46526
|
}
|
|
46465
46527
|
async function interactiveLoop(ws, api, todoId, projectId, agent, json, autoApprove, cfg) {
|
|
46466
46528
|
while (true) {
|
|
@@ -46537,6 +46599,8 @@ Cancelled by user (Ctrl+C)
|
|
|
46537
46599
|
process.exit(130);
|
|
46538
46600
|
});
|
|
46539
46601
|
const { values: args, positionals } = parseCliArgs();
|
|
46602
|
+
const groupTag = args.group === undefined ? getEnv("GROUP_ID") : String(args.group);
|
|
46603
|
+
const groupName = args["group-name"] === undefined ? undefined : String(args["group-name"]);
|
|
46540
46604
|
if (positionals[0] === "start") {
|
|
46541
46605
|
if (!positionals[1] && !args.template) {
|
|
46542
46606
|
process.stderr.write(`${RED}Usage: todoforai-cli start <todo-id>${RESET}
|
|
@@ -46699,7 +46763,8 @@ Cancelled by user (Ctrl+C)
|
|
|
46699
46763
|
process.exit(2);
|
|
46700
46764
|
}
|
|
46701
46765
|
const todo2 = await api.getTodo(todoId);
|
|
46702
|
-
const
|
|
46766
|
+
const agent2 = todo2.agentSettings || await api.getAgentSettings(todo2.agentSettingsId);
|
|
46767
|
+
const msg = await api.addMessage(todo2.projectId, content2, agent2, todoId);
|
|
46703
46768
|
if (args.json)
|
|
46704
46769
|
console.log(JSON.stringify(msg, null, 2));
|
|
46705
46770
|
else
|
|
@@ -46732,7 +46797,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46732
46797
|
process.exit(1);
|
|
46733
46798
|
}
|
|
46734
46799
|
blob = file;
|
|
46735
|
-
name =
|
|
46800
|
+
name = path3.basename(filePath);
|
|
46736
46801
|
}
|
|
46737
46802
|
const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime });
|
|
46738
46803
|
if (args.json)
|
|
@@ -46875,7 +46940,13 @@ Cancelled by user (Ctrl+C)
|
|
|
46875
46940
|
return;
|
|
46876
46941
|
}
|
|
46877
46942
|
if (args["list-models"]) {
|
|
46878
|
-
|
|
46943
|
+
const ids = qualifiedModelIds(await api.listModels(), positionals[0]);
|
|
46944
|
+
if (args.json)
|
|
46945
|
+
console.log(JSON.stringify(ids, null, 2));
|
|
46946
|
+
else
|
|
46947
|
+
for (const id of ids)
|
|
46948
|
+
process.stderr.write(`${BRAND}${id}${RESET}
|
|
46949
|
+
`);
|
|
46879
46950
|
return;
|
|
46880
46951
|
}
|
|
46881
46952
|
if (positionals[0] === "agent") {
|
|
@@ -46922,14 +46993,14 @@ Cancelled by user (Ctrl+C)
|
|
|
46922
46993
|
}
|
|
46923
46994
|
const mode = args.debug ? "debug" : args.detailed ? "detailed" : "default";
|
|
46924
46995
|
const format = args["format-anthropic"] ? "anthropic" : "compact";
|
|
46925
|
-
printFullChat(todo2, getFrontendUrl(apiUrl,
|
|
46996
|
+
printFullChat(todo2, getFrontendUrl(apiUrl, todoId), slice, mode, format);
|
|
46926
46997
|
return;
|
|
46927
46998
|
}
|
|
46928
46999
|
if (process.stderr.isTTY)
|
|
46929
47000
|
printLogo();
|
|
46930
47001
|
if (args.template) {
|
|
46931
47002
|
if (!args["no-bridge"] && !args["no-watch"])
|
|
46932
|
-
ensureBridgeRunning(apiUrl, apiKey);
|
|
47003
|
+
await ensureBridgeRunning(apiUrl, apiKey);
|
|
46933
47004
|
const templateId = args.template;
|
|
46934
47005
|
const spec = await api.getRegistrySpec(templateId);
|
|
46935
47006
|
process.stderr.write(`${DIM}Template:${RESET} ${BRAND}${spec.name}${RESET}
|
|
@@ -46948,10 +47019,13 @@ Cancelled by user (Ctrl+C)
|
|
|
46948
47019
|
`);
|
|
46949
47020
|
process.exit(1);
|
|
46950
47021
|
}
|
|
46951
|
-
const todo2 = await api.startFromSpec(projectId2, templateId
|
|
47022
|
+
const todo2 = await api.startFromSpec(projectId2, templateId, {
|
|
47023
|
+
...groupTag ? { groupTag } : {},
|
|
47024
|
+
...groupName ? { groupName } : {}
|
|
47025
|
+
});
|
|
46952
47026
|
const todoId = todo2.id;
|
|
46953
47027
|
cfgScope.setLastTodoId(todoId);
|
|
46954
|
-
const frontendUrl2 = getFrontendUrl(apiUrl,
|
|
47028
|
+
const frontendUrl2 = getFrontendUrl(apiUrl, todoId);
|
|
46955
47029
|
if (args.json) {
|
|
46956
47030
|
console.log(JSON.stringify({ ...todo2, frontend_url: frontendUrl2 }, null, 2));
|
|
46957
47031
|
} else if (args["no-watch"]) {
|
|
@@ -46996,7 +47070,7 @@ ${"\u2500".repeat(40)}
|
|
|
46996
47070
|
}
|
|
46997
47071
|
if (args.resume || args.continue) {
|
|
46998
47072
|
if (!args["no-bridge"])
|
|
46999
|
-
ensureBridgeRunning(apiUrl, apiKey);
|
|
47073
|
+
await ensureBridgeRunning(apiUrl, apiKey);
|
|
47000
47074
|
const todoId = args.resume || cfgScope.data.last_todo_id;
|
|
47001
47075
|
if (!todoId) {
|
|
47002
47076
|
process.stderr.write(`Error: No recent todo found
|
|
@@ -47013,7 +47087,7 @@ ${"\u2500".repeat(40)}
|
|
|
47013
47087
|
}
|
|
47014
47088
|
process.stderr.write(`
|
|
47015
47089
|
${"\u2500".repeat(40)}
|
|
47016
|
-
Resumed: ${CYAN}${getFrontendUrl(apiUrl,
|
|
47090
|
+
Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
|
|
47017
47091
|
`);
|
|
47018
47092
|
const ws2 = new FrontendWebSocket(apiUrl, apiKey);
|
|
47019
47093
|
await ws2.connect();
|
|
@@ -47037,8 +47111,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
|
|
|
47037
47111
|
}
|
|
47038
47112
|
const ws = args["no-watch"] ? null : new FrontendWebSocket(apiUrl, apiKey);
|
|
47039
47113
|
const wsReady = ws ? ws.connect() : null;
|
|
47040
|
-
|
|
47041
|
-
ensureBridgeRunning(apiUrl, apiKey);
|
|
47114
|
+
const bridgeReady = !args["no-bridge"] && !args["no-watch"] ? ensureBridgeRunning(apiUrl, apiKey) : null;
|
|
47042
47115
|
let preMatchedAgent = null;
|
|
47043
47116
|
let agents = null;
|
|
47044
47117
|
if (args.agent) {
|
|
@@ -47133,6 +47206,8 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
|
|
|
47133
47206
|
}
|
|
47134
47207
|
if (wsReady)
|
|
47135
47208
|
await wsReady;
|
|
47209
|
+
if (bridgeReady)
|
|
47210
|
+
await bridgeReady;
|
|
47136
47211
|
if (args.model)
|
|
47137
47212
|
agent = { ...agent, model: args.model };
|
|
47138
47213
|
if (args["raw-sysmsg"]) {
|
|
@@ -47146,7 +47221,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
|
|
|
47146
47221
|
cfg.addToHistory(content);
|
|
47147
47222
|
let todo;
|
|
47148
47223
|
try {
|
|
47149
|
-
todo = await api.addMessage(projectId, content, agent);
|
|
47224
|
+
todo = await api.addMessage(projectId, content, agent, undefined, undefined, undefined, groupTag || undefined, groupName);
|
|
47150
47225
|
} catch (e) {
|
|
47151
47226
|
if (!args.project && cfgScope.data.default_project_id === projectId && /failed: 403/.test(e.message || "")) {
|
|
47152
47227
|
cfgScope.clearDefaultProject();
|
|
@@ -47158,7 +47233,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
|
|
|
47158
47233
|
}
|
|
47159
47234
|
const actualTodoId = todo.id || crypto.randomUUID();
|
|
47160
47235
|
cfgScope.setLastTodoId(actualTodoId);
|
|
47161
|
-
const frontendUrl = getFrontendUrl(apiUrl,
|
|
47236
|
+
const frontendUrl = getFrontendUrl(apiUrl, actualTodoId);
|
|
47162
47237
|
if (args.json) {
|
|
47163
47238
|
console.log(JSON.stringify({ ...todo, frontend_url: frontendUrl }, null, 2));
|
|
47164
47239
|
} else if (!ws) {
|
|
@@ -47183,7 +47258,7 @@ ${"\u2500".repeat(40)}
|
|
|
47183
47258
|
await ws.close();
|
|
47184
47259
|
}
|
|
47185
47260
|
}
|
|
47186
|
-
main().then(() => process.exit(0)).catch((e) => {
|
|
47261
|
+
main().then(() => process.exit(process.exitCode ?? 0)).catch((e) => {
|
|
47187
47262
|
process.stderr.write(`Error: ${e.message}
|
|
47188
47263
|
`);
|
|
47189
47264
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,36 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@todoforai/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
7
|
+
"tfa-cli": "bin/todoforai-cli.js",
|
|
7
8
|
"todoai": "bin/todoforai-cli.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"bin",
|
|
11
|
-
"dist/
|
|
12
|
+
"dist/todoforai-cli.js"
|
|
12
13
|
],
|
|
13
14
|
"scripts": {
|
|
14
|
-
"build": "bun build src/index.ts --target=bun --outfile dist/
|
|
15
|
+
"build": "bun build src/index.ts --target=bun --outfile dist/todoforai-cli.js --external ws",
|
|
15
16
|
"prepublishOnly": "bun run build",
|
|
16
17
|
"start": "bun run src/index.ts",
|
|
17
18
|
"dev": "bun run src/index.ts"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
|
-
"@shared/credentials": "file:../packages/shared-credentials",
|
|
21
21
|
"@todoforai/update-notifier": "^0.1.0",
|
|
22
22
|
"cli-highlight": "^2.1.11",
|
|
23
23
|
"diff-match-patch": "^1.0.5",
|
|
24
24
|
"ws": "^8.18.0"
|
|
25
25
|
},
|
|
26
|
-
"peerDependencies": {
|
|
27
|
-
"@todoforai/edge": ">=0.12.0"
|
|
28
|
-
},
|
|
29
|
-
"peerDependenciesMeta": {
|
|
30
|
-
"@todoforai/edge": {
|
|
31
|
-
"optional": true
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
26
|
"devDependencies": {
|
|
35
27
|
"@types/ws": "^8.5.13",
|
|
36
28
|
"typescript": "^5.7.0"
|