@todoforai/cli 0.1.26 → 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} +294 -223
- 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,50 +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.24",
|
|
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
|
-
postinstall: "rm -rf node_modules/@todoforai/edge && ln -s ../../../edge/bun node_modules/@todoforai/edge"
|
|
43199
|
-
},
|
|
43200
|
-
dependencies: {
|
|
43201
|
-
"@shared/credentials": "file:../packages/shared-credentials",
|
|
43202
|
-
"@todoforai/update-notifier": "^0.1.0",
|
|
43203
|
-
"cli-highlight": "^2.1.11",
|
|
43204
|
-
"diff-match-patch": "^1.0.5",
|
|
43205
|
-
ws: "^8.18.0"
|
|
43206
|
-
},
|
|
43207
|
-
peerDependencies: {
|
|
43208
|
-
"@todoforai/edge": "file:../edge/bun"
|
|
43209
|
-
},
|
|
43210
|
-
peerDependenciesMeta: {
|
|
43211
|
-
"@todoforai/edge": {
|
|
43212
|
-
optional: true
|
|
43213
|
-
}
|
|
43214
|
-
},
|
|
43215
|
-
devDependencies: {
|
|
43216
|
-
"@types/ws": "^8.5.13",
|
|
43217
|
-
"@todoforai/edge": "file:../edge/bun",
|
|
43218
|
-
typescript: "^5.7.0"
|
|
43219
|
-
}
|
|
43220
|
-
};
|
|
43221
43199
|
// ../packages/shared-fbe/src/enums.ts
|
|
43222
43200
|
var TodoStatus;
|
|
43223
43201
|
((TodoStatus2) => {
|
|
@@ -43240,6 +43218,59 @@ var TodoStatus;
|
|
|
43240
43218
|
TodoStatus2["ARCHIVED"] = "ARCHIVED";
|
|
43241
43219
|
TodoStatus2["DELETED"] = "DELETED";
|
|
43242
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
|
+
}
|
|
43243
43274
|
// ../packages/shared-fbe/src/billing.ts
|
|
43244
43275
|
var USAGE_WINDOWS = {
|
|
43245
43276
|
SESSION_MS: 5 * 60 * 60 * 1000,
|
|
@@ -43290,7 +43321,7 @@ var mimetypes_default = {
|
|
|
43290
43321
|
gif: { mime: "image/gif", type: "image" },
|
|
43291
43322
|
webp: { mime: "image/webp", type: "image" },
|
|
43292
43323
|
bmp: { mime: "image/bmp", type: "image" },
|
|
43293
|
-
svg: { mime: "image/svg+xml", type: "
|
|
43324
|
+
svg: { mime: "image/svg+xml", type: "image" },
|
|
43294
43325
|
heic: { mime: "image/heic", type: "image", conversionProfile: "to_jpeg_90" },
|
|
43295
43326
|
heif: { mime: "image/heif", type: "image", conversionProfile: "to_jpeg_90" },
|
|
43296
43327
|
mp3: { mime: "audio/mpeg", type: "audio" },
|
|
@@ -43498,7 +43529,7 @@ var SERVER_TO_FRONTENDS = {
|
|
|
43498
43529
|
predicted_reply_updated: (todoId) => `${"predict:reply_updated" /* PREDICTED_REPLY_UPDATED */}:${todoId}`,
|
|
43499
43530
|
error_result: (todoId) => `${"block:error_result" /* BLOCK_ERROR_RESULT */}:${todoId}`,
|
|
43500
43531
|
meta_result: (todoId) => `${"block:meta_result" /* BLOCK_META_RESULT */}:${todoId}`,
|
|
43501
|
-
file_changed: (edgeId,
|
|
43532
|
+
file_changed: (edgeId, path2) => `${"block:file_changed" /* BLOCK_FILE_CHANGED */}:${edgeId}:${path2}`,
|
|
43502
43533
|
notify: (todoId) => `${"NOTIFY" /* NOTIFY */}:${todoId}`
|
|
43503
43534
|
},
|
|
43504
43535
|
notify: {
|
|
@@ -43746,13 +43777,10 @@ function getBlockServerPrefix(block) {
|
|
|
43746
43777
|
const prefixMatch = agentPattern.match(/^(.*?:)BASH(?:\(|$)/);
|
|
43747
43778
|
return prefixMatch ? prefixMatch[1] : "";
|
|
43748
43779
|
}
|
|
43749
|
-
var BASH_LIKE_BLOCK_TYPES = new Set(["bash", "machine_exec"]);
|
|
43750
43780
|
function getBlockPatterns(block) {
|
|
43751
43781
|
if (Array.isArray(block.generalized_pattern))
|
|
43752
43782
|
return block.generalized_pattern;
|
|
43753
|
-
|
|
43754
|
-
const isBashLike = BASH_LIKE_BLOCK_TYPES.has(block.type.toLowerCase()) || /:BASH(\(|$)/.test(agentPattern);
|
|
43755
|
-
if (isBashLike && block.cmd) {
|
|
43783
|
+
if (block.type.toLowerCase() === "bash" && block.cmd) {
|
|
43756
43784
|
const prefix = getBlockServerPrefix(block);
|
|
43757
43785
|
return generalizeBashCmd(block.cmd).map((p) => prefix + p);
|
|
43758
43786
|
}
|
|
@@ -43850,8 +43878,7 @@ function getBlockNewPatterns(block, permissions) {
|
|
|
43850
43878
|
if (Array.isArray(block.generalized_pattern)) {
|
|
43851
43879
|
return getNewPatterns(block.generalized_pattern, permissions);
|
|
43852
43880
|
}
|
|
43853
|
-
const
|
|
43854
|
-
const isBash = (block.type.toLowerCase() === "bash" || agentPattern.includes(":BASH(")) && !!block.cmd;
|
|
43881
|
+
const isBash = block.type.toLowerCase() === "bash" && !!block.cmd;
|
|
43855
43882
|
if (!isBash) {
|
|
43856
43883
|
return getBlockPatterns(block).filter((p) => !isPatternAllowed(permissions, p));
|
|
43857
43884
|
}
|
|
@@ -43927,10 +43954,9 @@ When the user asks you to recommend how to grow (e.g. "recommend growth TODOs",
|
|
|
43927
43954
|
Recommending actionable TODOs from your recommendations:
|
|
43928
43955
|
To materialize growth recommendations, create recommendation cards in the project's Recommendations column. You NEVER create running todos \u2014 you only recommend templates by reference. Two tools, strict roles:
|
|
43929
43956
|
|
|
43930
|
-
1. GET A TEMPLATE (
|
|
43931
|
-
- Default: CREATE a template tailored to THIS business
|
|
43932
|
-
|
|
43933
|
-
- Before creating, you MAY \`todoregistry-cli search <query>\` / \`list -c <category>\` / \`get <id>\`; reuse an existing template ONLY when it already fits this business closely with no meaningful edits. If it needs tailoring, create a new one instead \u2014 do not recommend a generic playbook that ignores this company's specifics.
|
|
43957
|
+
1. GET A TEMPLATE (api_create_template) \u2014 generate-first, reuse only on a close fit:
|
|
43958
|
+
- Default: CREATE a template tailored to THIS business with the \`api_create_template\` tool. It returns the new template id.
|
|
43959
|
+
- Reuse an existing registry template ONLY when it already fits this business closely with no meaningful edits \u2014 pass its id straight to \`api_recommend_template\`. If it needs tailoring, create a new one instead \u2014 do not recommend a generic playbook that ignores this company's specifics.
|
|
43934
43960
|
|
|
43935
43961
|
Write every generated body to be unmistakably about THIS company \u2014 bake the specifics from the business context into the task itself: the company name, its actual competitors, the target segment, the specific channel/platform, and the concrete metric to move. A reader should never mistake it for a generic template.
|
|
43936
43962
|
|
|
@@ -43939,9 +43965,10 @@ To materialize growth recommendations, create recommendation cards in the projec
|
|
|
43939
43965
|
- Then the request, using these exact standalone heading lines (letters only, each on its own line, ending with a colon): \`Your task:\`, \`Steps:\`, \`Data needed:\`. Everything under these headings becomes the first user turn.
|
|
43940
43966
|
- Headings must match exactly (e.g. \`Your task:\`, not \`Your task (SEO):\`) or the split breaks. Put the business-specific detail in the body text under each heading, never in the heading itself.
|
|
43941
43967
|
|
|
43942
|
-
2. RECOMMEND (
|
|
43943
|
-
\`
|
|
43944
|
-
|
|
43968
|
+
2. RECOMMEND (api_recommend_template \u2014 references a template id onto the project; never creates a running todo):
|
|
43969
|
+
Call \`api_recommend_template\` with the template id, a one-line \`note\` (the WHY for THIS business \u2014 it becomes the card's tooltip) and a \`priority\` of high, medium or low. Project and business context default to the current session, so pass them only when targeting a different one. The card appears in the Recommendations column for the user to review before any credits are spent.
|
|
43970
|
+
|
|
43971
|
+
Both tools run in-process and need no machine or shell \u2014 never shell out to \`todoregistry-cli\` / \`todoforai-cli\` for this.
|
|
43945
43972
|
|
|
43946
43973
|
3. Recommend 3-6 cards (highest priority first). Then reply with a one-line summary per card and tell the user they're waiting in the Recommendations column for review.`,
|
|
43947
43974
|
mcpConfigs: {},
|
|
@@ -43953,8 +43980,6 @@ To materialize growth recommendations, create recommendation cards in the projec
|
|
|
43953
43980
|
"cloud:google_rag",
|
|
43954
43981
|
"builtin:todoforai_api",
|
|
43955
43982
|
"builtin:question",
|
|
43956
|
-
"cloud:BASH(cmd: todoregistry-cli *)",
|
|
43957
|
-
"cloud:BASH(cmd: todoforai-cli *)",
|
|
43958
43983
|
"cloud:BASH(cmd: curl *)"
|
|
43959
43984
|
],
|
|
43960
43985
|
ask: [],
|
|
@@ -44128,6 +44153,40 @@ var TOPICS = {
|
|
|
44128
44153
|
audience: "frontend"
|
|
44129
44154
|
}
|
|
44130
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
|
+
|
|
44131
44190
|
// src/args.ts
|
|
44132
44191
|
var DEFAULT_API_URL = "https://api.todofor.ai";
|
|
44133
44192
|
var VERSION = package_default.version;
|
|
@@ -44136,7 +44195,7 @@ function getEnv(name) {
|
|
|
44136
44195
|
}
|
|
44137
44196
|
function printUsage() {
|
|
44138
44197
|
process.stderr.write(`
|
|
44139
|
-
todoforai-cli \u2014 TODOforAI CLI (Bun)
|
|
44198
|
+
todoforai-cli \u2014 TODOforAI CLI (Bun). Aliases: tfa-cli, todoai.
|
|
44140
44199
|
|
|
44141
44200
|
Usage:
|
|
44142
44201
|
todoforai-cli login # Browser-based device auth
|
|
@@ -44167,6 +44226,8 @@ Options:
|
|
|
44167
44226
|
--path <dir> Workspace path (default: cwd)
|
|
44168
44227
|
--project <id> Project ID
|
|
44169
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)
|
|
44170
44231
|
--model <model> Override the agent's model for this todo
|
|
44171
44232
|
(e.g. anthropic:anthropic/claude-opus-5, openai:openai/gpt-5.6-sol)
|
|
44172
44233
|
--list-agents List available agents (name, id, workspace paths) and exit
|
|
@@ -44225,6 +44286,8 @@ function parseCliArgs() {
|
|
|
44225
44286
|
project: { type: "string" },
|
|
44226
44287
|
agent: { type: "string", short: "a" },
|
|
44227
44288
|
model: { type: "string" },
|
|
44289
|
+
group: { type: "string" },
|
|
44290
|
+
"group-name": { type: "string" },
|
|
44228
44291
|
"list-agents": { type: "boolean", default: false },
|
|
44229
44292
|
"list-models": { type: "boolean", default: false },
|
|
44230
44293
|
"api-url": { type: "string" },
|
|
@@ -44605,8 +44668,8 @@ import { resolve, basename } from "path";
|
|
|
44605
44668
|
// src/ensure-bridge.ts
|
|
44606
44669
|
import { spawn, spawnSync } from "child_process";
|
|
44607
44670
|
import fs2 from "fs";
|
|
44608
|
-
import
|
|
44609
|
-
import
|
|
44671
|
+
import path2 from "path";
|
|
44672
|
+
import os2 from "os";
|
|
44610
44673
|
function hasBridge() {
|
|
44611
44674
|
const probe = spawnSync("todoforai-bridge", ["--version"], { stdio: "ignore" });
|
|
44612
44675
|
return probe.status === 0;
|
|
@@ -44675,31 +44738,44 @@ function ensureBridgeCredentials(apiUrl) {
|
|
|
44675
44738
|
const login = spawnSync("todoforai-bridge", bridgeLoginArgs(apiUrl), { stdio: "inherit" });
|
|
44676
44739
|
return login.status === 0;
|
|
44677
44740
|
}
|
|
44678
|
-
function
|
|
44679
|
-
|
|
44680
|
-
|
|
44681
|
-
|
|
44682
|
-
|
|
44683
|
-
|
|
44684
|
-
|
|
44685
|
-
r.on("error", () => resolve(false));
|
|
44686
|
-
r.on("close", (code) => resolve(code === 0 && out.trim().length > 0));
|
|
44687
|
-
});
|
|
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
|
+
}
|
|
44688
44748
|
}
|
|
44689
|
-
async function
|
|
44690
|
-
|
|
44691
|
-
|
|
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) {
|
|
44692
44760
|
if (!hasBridge()) {
|
|
44693
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");
|
|
44694
|
-
return;
|
|
44762
|
+
return false;
|
|
44695
44763
|
}
|
|
44696
44764
|
if (!ensureBridgeCredentials(apiUrl)) {
|
|
44697
44765
|
console.error("\x1B[33mBridge not started: `todoforai-bridge login` did not complete successfully.\x1B[0m");
|
|
44698
|
-
return;
|
|
44766
|
+
return false;
|
|
44699
44767
|
}
|
|
44700
|
-
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");
|
|
44701
44777
|
fs2.mkdirSync(logDir, { recursive: true });
|
|
44702
|
-
const logFile =
|
|
44778
|
+
const logFile = path2.join(logDir, "bridge.log");
|
|
44703
44779
|
const out = fs2.openSync(logFile, "a");
|
|
44704
44780
|
const child = spawn("todoforai-bridge", bridgeRunArgs(apiUrl), {
|
|
44705
44781
|
detached: true,
|
|
@@ -44716,13 +44792,13 @@ async function ensureBridgeRunning(apiUrl, _apiKey) {
|
|
|
44716
44792
|
});
|
|
44717
44793
|
child.unref();
|
|
44718
44794
|
if (!child.pid)
|
|
44719
|
-
return;
|
|
44720
|
-
|
|
44721
|
-
|
|
44722
|
-
|
|
44723
|
-
|
|
44724
|
-
|
|
44725
|
-
|
|
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;
|
|
44726
44802
|
}
|
|
44727
44803
|
|
|
44728
44804
|
// src/agent.ts
|
|
@@ -44756,9 +44832,13 @@ async function autoCreateAgent(api, resolvedPath) {
|
|
|
44756
44832
|
return resp;
|
|
44757
44833
|
}
|
|
44758
44834
|
const edges = await api.listEdges();
|
|
44759
|
-
|
|
44760
|
-
if (!edgeId)
|
|
44835
|
+
if (!Array.isArray(edges) || edges.length === 0) {
|
|
44761
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;
|
|
44762
44842
|
await api.setAgentEdgeMcpConfig(agentId, agentSettingsId, edgeId, "todoai_edge", { workspacePaths: [resolvedPath] });
|
|
44763
44843
|
const matches = await api.listAgentSettings({ workspacePath: resolvedPath });
|
|
44764
44844
|
if (matches.length)
|
|
@@ -44769,20 +44849,20 @@ async function autoCreateAgent(api, resolvedPath) {
|
|
|
44769
44849
|
}
|
|
44770
44850
|
|
|
44771
44851
|
// src/config.ts
|
|
44772
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
44773
|
-
import { dirname, join, resolve as resolve2 } from "path";
|
|
44774
|
-
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";
|
|
44775
44855
|
function getConfigDir() {
|
|
44776
|
-
const sys =
|
|
44856
|
+
const sys = platform2();
|
|
44777
44857
|
if (sys === "win32") {
|
|
44778
|
-
const base = process.env.APPDATA ||
|
|
44779
|
-
return
|
|
44858
|
+
const base = process.env.APPDATA || join2(homedir2(), "AppData", "Roaming");
|
|
44859
|
+
return join2(base, "todoai-cli");
|
|
44780
44860
|
}
|
|
44781
44861
|
if (sys === "darwin") {
|
|
44782
|
-
return
|
|
44862
|
+
return join2(homedir2(), "Library", "Application Support", "todoai-cli");
|
|
44783
44863
|
}
|
|
44784
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
44785
|
-
return
|
|
44864
|
+
const xdg = process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
|
|
44865
|
+
return join2(xdg, "todoai-cli");
|
|
44786
44866
|
}
|
|
44787
44867
|
function defaultScope() {
|
|
44788
44868
|
return {
|
|
@@ -44834,10 +44914,10 @@ class ConfigStore {
|
|
|
44834
44914
|
data;
|
|
44835
44915
|
constructor(pathArg) {
|
|
44836
44916
|
if (pathArg) {
|
|
44837
|
-
const p = resolve2(pathArg.replace(/^~/,
|
|
44838
|
-
this.path = p.endsWith(".json") ? p :
|
|
44917
|
+
const p = resolve2(pathArg.replace(/^~/, homedir2()));
|
|
44918
|
+
this.path = p.endsWith(".json") ? p : join2(p, "config.json");
|
|
44839
44919
|
} else {
|
|
44840
|
-
this.path =
|
|
44920
|
+
this.path = join2(getConfigDir(), "config.json");
|
|
44841
44921
|
}
|
|
44842
44922
|
this.data = this.load();
|
|
44843
44923
|
}
|
|
@@ -44845,7 +44925,7 @@ class ConfigStore {
|
|
|
44845
44925
|
if (!existsSync(this.path))
|
|
44846
44926
|
return defaultConfig();
|
|
44847
44927
|
try {
|
|
44848
|
-
const raw = JSON.parse(
|
|
44928
|
+
const raw = JSON.parse(readFileSync2(this.path, "utf-8"));
|
|
44849
44929
|
delete raw.default_api_key;
|
|
44850
44930
|
const cfg = migrate(raw);
|
|
44851
44931
|
const needsMigration = raw.per_api_url == null;
|
|
@@ -44860,8 +44940,8 @@ class ConfigStore {
|
|
|
44860
44940
|
}
|
|
44861
44941
|
save() {
|
|
44862
44942
|
try {
|
|
44863
|
-
|
|
44864
|
-
|
|
44943
|
+
mkdirSync2(dirname2(this.path), { recursive: true });
|
|
44944
|
+
writeFileSync2(this.path, JSON.stringify(this.data, null, 2), "utf-8");
|
|
44865
44945
|
} catch {}
|
|
44866
44946
|
}
|
|
44867
44947
|
scope(apiUrl) {
|
|
@@ -44921,39 +45001,6 @@ class ScopedConfig {
|
|
|
44921
45001
|
this.store.save();
|
|
44922
45002
|
}
|
|
44923
45003
|
}
|
|
44924
|
-
|
|
44925
|
-
// ../packages/shared-credentials/src/index.ts
|
|
44926
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, chmodSync } from "fs";
|
|
44927
|
-
import { join as join2, dirname as dirname2 } from "path";
|
|
44928
|
-
import { homedir as homedir2, platform as platform2 } from "os";
|
|
44929
|
-
function credentialsPath2() {
|
|
44930
|
-
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");
|
|
44931
|
-
}
|
|
44932
|
-
function read() {
|
|
44933
|
-
try {
|
|
44934
|
-
return JSON.parse(readFileSync2(credentialsPath2(), "utf-8"));
|
|
44935
|
-
} catch {
|
|
44936
|
-
return {};
|
|
44937
|
-
}
|
|
44938
|
-
}
|
|
44939
|
-
function writeSecure(data) {
|
|
44940
|
-
const p = credentialsPath2();
|
|
44941
|
-
mkdirSync2(dirname2(p), { recursive: true });
|
|
44942
|
-
writeFileSync2(p, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
|
|
44943
|
-
try {
|
|
44944
|
-
chmodSync(p, 384);
|
|
44945
|
-
} catch {}
|
|
44946
|
-
}
|
|
44947
|
-
function readCredential(apiUrl) {
|
|
44948
|
-
const c2 = read();
|
|
44949
|
-
const v = c2[apiUrl] || c2.apiToken || c2.apiKey;
|
|
44950
|
-
return typeof v === "string" ? v : "";
|
|
44951
|
-
}
|
|
44952
|
-
function writeCredential(apiUrl, apiKey) {
|
|
44953
|
-
const c2 = read();
|
|
44954
|
-
c2[apiUrl] = apiKey;
|
|
44955
|
-
writeSecure(c2);
|
|
44956
|
-
}
|
|
44957
45004
|
// src/logo.ts
|
|
44958
45005
|
var LETTERS = {
|
|
44959
45006
|
t: [" x ", "xxxx", " x ", " xll", " xll", " xxx"],
|
|
@@ -45027,6 +45074,12 @@ function printLogo() {
|
|
|
45027
45074
|
`);
|
|
45028
45075
|
}
|
|
45029
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
|
+
|
|
45030
45083
|
// src/inspect.ts
|
|
45031
45084
|
var xmlAttr = (v) => JSON.stringify(String(v));
|
|
45032
45085
|
var DROP_IF_EMPTY = new Set(["runMeta", "meta", "attachments", "blocks"]);
|
|
@@ -45858,17 +45911,6 @@ async function watchTodo(ws, todoId, projectId, opts = {}) {
|
|
|
45858
45911
|
"block:start_catfile"
|
|
45859
45912
|
]);
|
|
45860
45913
|
const signalActivity = () => opts.activityEvent?.set();
|
|
45861
|
-
let edgeId;
|
|
45862
|
-
let rootPath = "";
|
|
45863
|
-
if (opts.agentSettings) {
|
|
45864
|
-
const emc = opts.agentSettings.edgesMcpConfigs || {};
|
|
45865
|
-
edgeId = Object.keys(emc)[0];
|
|
45866
|
-
if (edgeId) {
|
|
45867
|
-
const ec = emc[edgeId];
|
|
45868
|
-
const tc = ec?.todoai_edge || ec?.todoai || {};
|
|
45869
|
-
rootPath = (tc.workspacePaths || [])[0] || "";
|
|
45870
|
-
}
|
|
45871
|
-
}
|
|
45872
45914
|
const blocksStore = new Map;
|
|
45873
45915
|
const diffStore = diffStoreByWs.get(ws) ?? new Map;
|
|
45874
45916
|
diffStoreByWs.set(ws, diffStore);
|
|
@@ -46055,6 +46097,12 @@ ${YELLOW}*${RESET} ${YELLOW}${blockType}${RESET}${extra}
|
|
|
46055
46097
|
`);
|
|
46056
46098
|
signalActivity();
|
|
46057
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();
|
|
46058
46106
|
} else if (msgType === "todo:status") {
|
|
46059
46107
|
const status = payload.status || "";
|
|
46060
46108
|
process.stderr.write(`
|
|
@@ -46085,6 +46133,9 @@ ${DIM}[todo:status] ${status}${RESET}
|
|
|
46085
46133
|
const status = result?.payload?.status || result?.type || "unknown";
|
|
46086
46134
|
process.stderr.write(`Warning: Stopped: ${status}
|
|
46087
46135
|
`);
|
|
46136
|
+
process.exitCode = 1;
|
|
46137
|
+
} else {
|
|
46138
|
+
process.exitCode = 0;
|
|
46088
46139
|
}
|
|
46089
46140
|
return true;
|
|
46090
46141
|
} catch (e) {
|
|
@@ -46128,20 +46179,6 @@ async function listAgentsCommand(api, opts) {
|
|
|
46128
46179
|
}
|
|
46129
46180
|
}
|
|
46130
46181
|
|
|
46131
|
-
// src/list-models.ts
|
|
46132
|
-
async function listModelsCommand(api, opts) {
|
|
46133
|
-
const res = await api.listModels();
|
|
46134
|
-
const q = opts.filter?.toLowerCase();
|
|
46135
|
-
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));
|
|
46136
|
-
if (opts.json) {
|
|
46137
|
-
console.log(JSON.stringify(ids, null, 2));
|
|
46138
|
-
return;
|
|
46139
|
-
}
|
|
46140
|
-
for (const id of ids)
|
|
46141
|
-
process.stderr.write(`${BRAND}${id}${RESET}
|
|
46142
|
-
`);
|
|
46143
|
-
}
|
|
46144
|
-
|
|
46145
46182
|
// src/agent-command.ts
|
|
46146
46183
|
function printAgentHelp() {
|
|
46147
46184
|
process.stderr.write(`
|
|
@@ -46151,6 +46188,7 @@ Usage:
|
|
|
46151
46188
|
todoforai-cli agent list List agents (name, model, id, paths)
|
|
46152
46189
|
todoforai-cli agent get <agent> Show a single agent's settings
|
|
46153
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)
|
|
46154
46192
|
|
|
46155
46193
|
<agent> is a name or id (unique partial name also works).
|
|
46156
46194
|
Fields map directly to agent settings; values are parsed as JSON when possible
|
|
@@ -46248,6 +46286,32 @@ async function agentCommand(api, positionals, args, formatPath) {
|
|
|
46248
46286
|
}
|
|
46249
46287
|
const summary = Object.keys(updates).map((k) => `${k}=${JSON.stringify(updated[k])}`).join(" ");
|
|
46250
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}
|
|
46251
46315
|
`);
|
|
46252
46316
|
return;
|
|
46253
46317
|
}
|
|
@@ -46453,18 +46517,12 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
46453
46517
|
|
|
46454
46518
|
// src/index.ts
|
|
46455
46519
|
try {
|
|
46456
|
-
const pkgPath =
|
|
46520
|
+
const pkgPath = path3.resolve(fileURLToPath(import.meta.url), "../../package.json");
|
|
46457
46521
|
checkForUpdates(JSON.parse(readFileSync3(pkgPath, "utf-8")));
|
|
46458
46522
|
} catch {}
|
|
46459
|
-
function formatPathWithTilde(
|
|
46523
|
+
function formatPathWithTilde(path4) {
|
|
46460
46524
|
const home = homedir3();
|
|
46461
|
-
return
|
|
46462
|
-
}
|
|
46463
|
-
function getFrontendUrl(apiUrl, projectId, todoId) {
|
|
46464
|
-
if (apiUrl.includes("localhost:4000") || apiUrl.includes("127.0.0.1:4000")) {
|
|
46465
|
-
return `http://localhost:3000/${projectId}/${todoId}`;
|
|
46466
|
-
}
|
|
46467
|
-
return `https://todofor.ai/${projectId}/${todoId}`;
|
|
46525
|
+
return path4.startsWith(home) ? path4.replace(home, "~") : path4;
|
|
46468
46526
|
}
|
|
46469
46527
|
async function interactiveLoop(ws, api, todoId, projectId, agent, json, autoApprove, cfg) {
|
|
46470
46528
|
while (true) {
|
|
@@ -46541,6 +46599,8 @@ Cancelled by user (Ctrl+C)
|
|
|
46541
46599
|
process.exit(130);
|
|
46542
46600
|
});
|
|
46543
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"]);
|
|
46544
46604
|
if (positionals[0] === "start") {
|
|
46545
46605
|
if (!positionals[1] && !args.template) {
|
|
46546
46606
|
process.stderr.write(`${RED}Usage: todoforai-cli start <todo-id>${RESET}
|
|
@@ -46703,7 +46763,8 @@ Cancelled by user (Ctrl+C)
|
|
|
46703
46763
|
process.exit(2);
|
|
46704
46764
|
}
|
|
46705
46765
|
const todo2 = await api.getTodo(todoId);
|
|
46706
|
-
const
|
|
46766
|
+
const agent2 = todo2.agentSettings || await api.getAgentSettings(todo2.agentSettingsId);
|
|
46767
|
+
const msg = await api.addMessage(todo2.projectId, content2, agent2, todoId);
|
|
46707
46768
|
if (args.json)
|
|
46708
46769
|
console.log(JSON.stringify(msg, null, 2));
|
|
46709
46770
|
else
|
|
@@ -46736,7 +46797,7 @@ Cancelled by user (Ctrl+C)
|
|
|
46736
46797
|
process.exit(1);
|
|
46737
46798
|
}
|
|
46738
46799
|
blob = file;
|
|
46739
|
-
name =
|
|
46800
|
+
name = path3.basename(filePath);
|
|
46740
46801
|
}
|
|
46741
46802
|
const res = await api.showFile(todoId, blob, name, { title: args.title, alias: args.alias, mime: args.mime });
|
|
46742
46803
|
if (args.json)
|
|
@@ -46879,7 +46940,13 @@ Cancelled by user (Ctrl+C)
|
|
|
46879
46940
|
return;
|
|
46880
46941
|
}
|
|
46881
46942
|
if (args["list-models"]) {
|
|
46882
|
-
|
|
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
|
+
`);
|
|
46883
46950
|
return;
|
|
46884
46951
|
}
|
|
46885
46952
|
if (positionals[0] === "agent") {
|
|
@@ -46926,14 +46993,14 @@ Cancelled by user (Ctrl+C)
|
|
|
46926
46993
|
}
|
|
46927
46994
|
const mode = args.debug ? "debug" : args.detailed ? "detailed" : "default";
|
|
46928
46995
|
const format = args["format-anthropic"] ? "anthropic" : "compact";
|
|
46929
|
-
printFullChat(todo2, getFrontendUrl(apiUrl,
|
|
46996
|
+
printFullChat(todo2, getFrontendUrl(apiUrl, todoId), slice, mode, format);
|
|
46930
46997
|
return;
|
|
46931
46998
|
}
|
|
46932
46999
|
if (process.stderr.isTTY)
|
|
46933
47000
|
printLogo();
|
|
46934
47001
|
if (args.template) {
|
|
46935
47002
|
if (!args["no-bridge"] && !args["no-watch"])
|
|
46936
|
-
ensureBridgeRunning(apiUrl, apiKey);
|
|
47003
|
+
await ensureBridgeRunning(apiUrl, apiKey);
|
|
46937
47004
|
const templateId = args.template;
|
|
46938
47005
|
const spec = await api.getRegistrySpec(templateId);
|
|
46939
47006
|
process.stderr.write(`${DIM}Template:${RESET} ${BRAND}${spec.name}${RESET}
|
|
@@ -46952,10 +47019,13 @@ Cancelled by user (Ctrl+C)
|
|
|
46952
47019
|
`);
|
|
46953
47020
|
process.exit(1);
|
|
46954
47021
|
}
|
|
46955
|
-
const todo2 = await api.startFromSpec(projectId2, templateId
|
|
47022
|
+
const todo2 = await api.startFromSpec(projectId2, templateId, {
|
|
47023
|
+
...groupTag ? { groupTag } : {},
|
|
47024
|
+
...groupName ? { groupName } : {}
|
|
47025
|
+
});
|
|
46956
47026
|
const todoId = todo2.id;
|
|
46957
47027
|
cfgScope.setLastTodoId(todoId);
|
|
46958
|
-
const frontendUrl2 = getFrontendUrl(apiUrl,
|
|
47028
|
+
const frontendUrl2 = getFrontendUrl(apiUrl, todoId);
|
|
46959
47029
|
if (args.json) {
|
|
46960
47030
|
console.log(JSON.stringify({ ...todo2, frontend_url: frontendUrl2 }, null, 2));
|
|
46961
47031
|
} else if (args["no-watch"]) {
|
|
@@ -47000,7 +47070,7 @@ ${"\u2500".repeat(40)}
|
|
|
47000
47070
|
}
|
|
47001
47071
|
if (args.resume || args.continue) {
|
|
47002
47072
|
if (!args["no-bridge"])
|
|
47003
|
-
ensureBridgeRunning(apiUrl, apiKey);
|
|
47073
|
+
await ensureBridgeRunning(apiUrl, apiKey);
|
|
47004
47074
|
const todoId = args.resume || cfgScope.data.last_todo_id;
|
|
47005
47075
|
if (!todoId) {
|
|
47006
47076
|
process.stderr.write(`Error: No recent todo found
|
|
@@ -47017,7 +47087,7 @@ ${"\u2500".repeat(40)}
|
|
|
47017
47087
|
}
|
|
47018
47088
|
process.stderr.write(`
|
|
47019
47089
|
${"\u2500".repeat(40)}
|
|
47020
|
-
Resumed: ${CYAN}${getFrontendUrl(apiUrl,
|
|
47090
|
+
Resumed: ${CYAN}${getFrontendUrl(apiUrl, todoId)}${RESET}
|
|
47021
47091
|
`);
|
|
47022
47092
|
const ws2 = new FrontendWebSocket(apiUrl, apiKey);
|
|
47023
47093
|
await ws2.connect();
|
|
@@ -47041,8 +47111,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
|
|
|
47041
47111
|
}
|
|
47042
47112
|
const ws = args["no-watch"] ? null : new FrontendWebSocket(apiUrl, apiKey);
|
|
47043
47113
|
const wsReady = ws ? ws.connect() : null;
|
|
47044
|
-
|
|
47045
|
-
ensureBridgeRunning(apiUrl, apiKey);
|
|
47114
|
+
const bridgeReady = !args["no-bridge"] && !args["no-watch"] ? ensureBridgeRunning(apiUrl, apiKey) : null;
|
|
47046
47115
|
let preMatchedAgent = null;
|
|
47047
47116
|
let agents = null;
|
|
47048
47117
|
if (args.agent) {
|
|
@@ -47137,6 +47206,8 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
|
|
|
47137
47206
|
}
|
|
47138
47207
|
if (wsReady)
|
|
47139
47208
|
await wsReady;
|
|
47209
|
+
if (bridgeReady)
|
|
47210
|
+
await bridgeReady;
|
|
47140
47211
|
if (args.model)
|
|
47141
47212
|
agent = { ...agent, model: args.model };
|
|
47142
47213
|
if (args["raw-sysmsg"]) {
|
|
@@ -47150,7 +47221,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
|
|
|
47150
47221
|
cfg.addToHistory(content);
|
|
47151
47222
|
let todo;
|
|
47152
47223
|
try {
|
|
47153
|
-
todo = await api.addMessage(projectId, content, agent);
|
|
47224
|
+
todo = await api.addMessage(projectId, content, agent, undefined, undefined, undefined, groupTag || undefined, groupName);
|
|
47154
47225
|
} catch (e) {
|
|
47155
47226
|
if (!args.project && cfgScope.data.default_project_id === projectId && /failed: 403/.test(e.message || "")) {
|
|
47156
47227
|
cfgScope.clearDefaultProject();
|
|
@@ -47162,7 +47233,7 @@ Resumed: ${CYAN}${getFrontendUrl(apiUrl, projectId2, todoId)}${RESET}
|
|
|
47162
47233
|
}
|
|
47163
47234
|
const actualTodoId = todo.id || crypto.randomUUID();
|
|
47164
47235
|
cfgScope.setLastTodoId(actualTodoId);
|
|
47165
|
-
const frontendUrl = getFrontendUrl(apiUrl,
|
|
47236
|
+
const frontendUrl = getFrontendUrl(apiUrl, actualTodoId);
|
|
47166
47237
|
if (args.json) {
|
|
47167
47238
|
console.log(JSON.stringify({ ...todo, frontend_url: frontendUrl }, null, 2));
|
|
47168
47239
|
} else if (!ws) {
|
|
@@ -47187,7 +47258,7 @@ ${"\u2500".repeat(40)}
|
|
|
47187
47258
|
await ws.close();
|
|
47188
47259
|
}
|
|
47189
47260
|
}
|
|
47190
|
-
main().then(() => process.exit(0)).catch((e) => {
|
|
47261
|
+
main().then(() => process.exit(process.exitCode ?? 0)).catch((e) => {
|
|
47191
47262
|
process.stderr.write(`Error: ${e.message}
|
|
47192
47263
|
`);
|
|
47193
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"
|