@todoforai/cli 0.1.54 → 0.1.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/todoforai-cli.js +766 -74
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,6 +70,17 @@ todoforai-cli -c # continue most recent todo
|
|
|
70
70
|
todoforai-cli --resume <todo-id> # resume specific todo
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
### Notifications
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
tfa-cli inbox # the bell: newest across all tabs
|
|
77
|
+
tfa-cli inbox messages --unread # one filter: needs-you | messages | activity
|
|
78
|
+
tfa-cli inbox seen # mark everything read
|
|
79
|
+
tfa-cli notify "Deploy done" "v2 is live" --href /t/<todo-id> # note to YOUR user (Messages tab + phone push)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`notify` has no recipient option: it always goes to the user the key belongs to (never a teammate or other account), is shown as from the calling agent, delivers the same text once (`--subject` to control the dedupe key) and is capped at 10 new notes per day.
|
|
83
|
+
|
|
73
84
|
## IDE integration (ACP)
|
|
74
85
|
|
|
75
86
|
`todoforai-cli acp` speaks the [Agent Client Protocol](https://agentclientprotocol.com) over stdio, so any ACP-capable editor can drive your agent: prompts from the editor chat, file edits as native diffs, permission prompts inline, and shell commands running on your machine through the bridge. One ACP thread = one todo. Requires `todoforai-cli login` first; `--isolated` / `--user-id` are not supported in this mode.
|
package/dist/todoforai-cli.js
CHANGED
|
@@ -42745,7 +42745,7 @@ function randomTip() {
|
|
|
42745
42745
|
return TIPS[Math.floor(Math.random() * TIPS.length)];
|
|
42746
42746
|
}
|
|
42747
42747
|
|
|
42748
|
-
//
|
|
42748
|
+
// ../../../home/hm/repo/todoforai/packages/shared-api/src/api.ts
|
|
42749
42749
|
function restBasePath(apiKey) {
|
|
42750
42750
|
return apiKey.startsWith("dst_") ? "/dst/v1" : "/api/v1";
|
|
42751
42751
|
}
|
|
@@ -42926,6 +42926,19 @@ class ApiClient {
|
|
|
42926
42926
|
upsertProjectGroup(projectId, slug, fields) {
|
|
42927
42927
|
return this.request("PUT", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(slug)}`, { projectId, slug, ...fields });
|
|
42928
42928
|
}
|
|
42929
|
+
listFeed(opts = {}) {
|
|
42930
|
+
const qs = new URLSearchParams({ all: "true" });
|
|
42931
|
+
for (const [k, v] of Object.entries(opts))
|
|
42932
|
+
if (v !== undefined)
|
|
42933
|
+
qs.set(k, String(v));
|
|
42934
|
+
return this.request("GET", `/api/v1/feed?${qs}`);
|
|
42935
|
+
}
|
|
42936
|
+
markFeedSeen(opts = {}) {
|
|
42937
|
+
return this.request("POST", "/api/v1/feed/seen", { all: true, ...opts });
|
|
42938
|
+
}
|
|
42939
|
+
sendFeedMessage(msg) {
|
|
42940
|
+
return this.request("POST", "/api/v1/feed/message", msg);
|
|
42941
|
+
}
|
|
42929
42942
|
listProjectSurfaces(projectId) {
|
|
42930
42943
|
return this.request("GET", `/api/v1/projects/${projectId}/surfaces`);
|
|
42931
42944
|
}
|
|
@@ -43156,11 +43169,11 @@ class ApiClient {
|
|
|
43156
43169
|
return res.json();
|
|
43157
43170
|
}
|
|
43158
43171
|
}
|
|
43159
|
-
//
|
|
43172
|
+
// ../../../home/hm/repo/todoforai/packages/shared-api/src/frontend-ws.ts
|
|
43160
43173
|
import { randomUUID } from "crypto";
|
|
43161
43174
|
import WebSocket from "ws";
|
|
43162
43175
|
|
|
43163
|
-
//
|
|
43176
|
+
// ../../../home/hm/repo/todoforai/packages/shared-credentials/src/index.ts
|
|
43164
43177
|
import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
|
|
43165
43178
|
import { join, dirname } from "node:path";
|
|
43166
43179
|
import { homedir, platform } from "node:os";
|
|
@@ -43200,13 +43213,14 @@ function writeCredential(apiUrl, apiKey) {
|
|
|
43200
43213
|
writeSecure(c);
|
|
43201
43214
|
}
|
|
43202
43215
|
|
|
43203
|
-
//
|
|
43216
|
+
// ../../../home/hm/repo/todoforai/packages/shared-api/src/frontend-ws.ts
|
|
43204
43217
|
var log = (level, ...args) => console.log(`[frontend-ws:${level}]`, ...args);
|
|
43205
43218
|
|
|
43206
43219
|
class FrontendWebSocket {
|
|
43207
43220
|
apiUrl;
|
|
43208
43221
|
apiKey;
|
|
43209
43222
|
ws = null;
|
|
43223
|
+
connecting = null;
|
|
43210
43224
|
tabId = randomUUID();
|
|
43211
43225
|
connected = false;
|
|
43212
43226
|
callbacks = new Map;
|
|
@@ -43230,28 +43244,61 @@ class FrontendWebSocket {
|
|
|
43230
43244
|
wsUrl = `wss://${url}`;
|
|
43231
43245
|
return `${wsUrl}/ws/v1/frontend?tabId=${this.tabId}`;
|
|
43232
43246
|
}
|
|
43233
|
-
|
|
43247
|
+
connect() {
|
|
43234
43248
|
if (this.connected && this.ws)
|
|
43235
|
-
return true;
|
|
43249
|
+
return Promise.resolve(true);
|
|
43250
|
+
this.connecting ??= this.open().finally(() => {
|
|
43251
|
+
this.connecting = null;
|
|
43252
|
+
});
|
|
43253
|
+
return this.connecting;
|
|
43254
|
+
}
|
|
43255
|
+
open() {
|
|
43236
43256
|
return new Promise((resolve) => {
|
|
43237
43257
|
const wsUrl = this.getWsUrl();
|
|
43238
|
-
|
|
43239
|
-
this.ws
|
|
43258
|
+
const sock = new WebSocket(wsUrl, [this.apiKey], { maxPayload: 5 * 1024 * 1024, handshakeTimeout: 1e4 });
|
|
43259
|
+
this.ws = sock;
|
|
43260
|
+
const current = () => this.ws === sock;
|
|
43261
|
+
let alive = true;
|
|
43262
|
+
let heartbeat;
|
|
43263
|
+
sock.on("pong", () => {
|
|
43264
|
+
alive = true;
|
|
43265
|
+
});
|
|
43266
|
+
sock.on("open", () => {
|
|
43267
|
+
if (!current())
|
|
43268
|
+
return;
|
|
43240
43269
|
this.connected = true;
|
|
43270
|
+
heartbeat = setInterval(() => {
|
|
43271
|
+
if (!alive) {
|
|
43272
|
+
sock.terminate();
|
|
43273
|
+
return;
|
|
43274
|
+
}
|
|
43275
|
+
alive = false;
|
|
43276
|
+
try {
|
|
43277
|
+
sock.ping();
|
|
43278
|
+
} catch {}
|
|
43279
|
+
}, 15000);
|
|
43280
|
+
heartbeat.unref?.();
|
|
43241
43281
|
resolve(true);
|
|
43242
43282
|
});
|
|
43243
|
-
|
|
43283
|
+
sock.on("message", (data) => {
|
|
43284
|
+
if (!current())
|
|
43285
|
+
return;
|
|
43244
43286
|
try {
|
|
43245
43287
|
this.handleMessage(JSON.parse(data.toString()));
|
|
43246
43288
|
} catch {}
|
|
43247
43289
|
});
|
|
43248
|
-
|
|
43290
|
+
sock.on("close", () => {
|
|
43291
|
+
clearInterval(heartbeat);
|
|
43292
|
+
if (!current())
|
|
43293
|
+
return;
|
|
43249
43294
|
this.connected = false;
|
|
43250
43295
|
this.settleAll();
|
|
43296
|
+
resolve(false);
|
|
43251
43297
|
});
|
|
43252
|
-
|
|
43298
|
+
sock.on("error", (err) => {
|
|
43253
43299
|
log("error", "WS error:", err.message);
|
|
43254
|
-
|
|
43300
|
+
if (current())
|
|
43301
|
+
this.connected = false;
|
|
43255
43302
|
resolve(false);
|
|
43256
43303
|
});
|
|
43257
43304
|
});
|
|
@@ -43320,7 +43367,7 @@ class FrontendWebSocket {
|
|
|
43320
43367
|
}
|
|
43321
43368
|
}
|
|
43322
43369
|
}
|
|
43323
|
-
async subscribe(todoId, callback) {
|
|
43370
|
+
async subscribe(todoId, callback, opts = {}) {
|
|
43324
43371
|
if (!this.connected && !await this.connect())
|
|
43325
43372
|
return false;
|
|
43326
43373
|
if (callback)
|
|
@@ -43328,8 +43375,16 @@ class FrontendWebSocket {
|
|
|
43328
43375
|
const res = await fetch(`${this.apiUrl}/api/v1/todos/${todoId}/subscribe`, {
|
|
43329
43376
|
method: "POST",
|
|
43330
43377
|
headers: { "x-api-key": this.apiKey, "x-tab-id": this.tabId, "Content-Type": "application/json" },
|
|
43331
|
-
body: JSON.stringify({ todoId })
|
|
43378
|
+
body: JSON.stringify({ todoId }),
|
|
43379
|
+
signal: AbortSignal.timeout(15000)
|
|
43332
43380
|
});
|
|
43381
|
+
if (res.ok && opts.replayStatus) {
|
|
43382
|
+
this.pendingBlocks.delete(todoId);
|
|
43383
|
+
this.lastReadyPayload.delete(todoId);
|
|
43384
|
+
const status = (await res.json().catch(() => null))?.status;
|
|
43385
|
+
if (status)
|
|
43386
|
+
this.handleMessage({ type: "todo:status", payload: { todoId, status } });
|
|
43387
|
+
}
|
|
43333
43388
|
return res.ok;
|
|
43334
43389
|
}
|
|
43335
43390
|
setCallback(todoId, callback) {
|
|
@@ -43339,17 +43394,16 @@ class FrontendWebSocket {
|
|
|
43339
43394
|
this.callbacks.delete(todoId);
|
|
43340
43395
|
}
|
|
43341
43396
|
async sendInterrupt(projectId, todoId) {
|
|
43342
|
-
|
|
43343
|
-
|
|
43344
|
-
try {
|
|
43345
|
-
this.ws.send(JSON.stringify({
|
|
43346
|
-
type: "todo:interrupt_signal",
|
|
43347
|
-
payload: { projectId, todoId }
|
|
43348
|
-
}));
|
|
43349
|
-
return true;
|
|
43350
|
-
} catch {
|
|
43397
|
+
const ws = this.ws;
|
|
43398
|
+
if (!this.connected || !ws)
|
|
43351
43399
|
return false;
|
|
43352
|
-
|
|
43400
|
+
return new Promise((resolve) => {
|
|
43401
|
+
try {
|
|
43402
|
+
ws.send(JSON.stringify({ type: "todo:interrupt_signal", payload: { projectId, todoId } }), (err) => resolve(!err));
|
|
43403
|
+
} catch {
|
|
43404
|
+
resolve(false);
|
|
43405
|
+
}
|
|
43406
|
+
});
|
|
43353
43407
|
}
|
|
43354
43408
|
async sendQueueCancel(todoId, messageId) {
|
|
43355
43409
|
if (!this.connected || !this.ws)
|
|
@@ -43407,6 +43461,10 @@ class FrontendWebSocket {
|
|
|
43407
43461
|
return this.completion(todoId);
|
|
43408
43462
|
}
|
|
43409
43463
|
completion(todoId) {
|
|
43464
|
+
if (!this.connected && !this.completionResults.has(todoId)) {
|
|
43465
|
+
this.callbacks.delete(todoId);
|
|
43466
|
+
return Promise.resolve({ type: "socket:closed", payload: { todoId, status: "DISCONNECTED" }, success: false });
|
|
43467
|
+
}
|
|
43410
43468
|
const early = this.completionResults.get(todoId);
|
|
43411
43469
|
if (early) {
|
|
43412
43470
|
this.completionResults.delete(todoId);
|
|
@@ -43425,7 +43483,7 @@ class FrontendWebSocket {
|
|
|
43425
43483
|
});
|
|
43426
43484
|
}
|
|
43427
43485
|
}
|
|
43428
|
-
//
|
|
43486
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/enums.ts
|
|
43429
43487
|
var TodoStatus;
|
|
43430
43488
|
((TodoStatus2) => {
|
|
43431
43489
|
TodoStatus2["PROPOSED"] = "PROPOSED";
|
|
@@ -43449,7 +43507,7 @@ var TodoStatus;
|
|
|
43449
43507
|
TodoStatus2["ARCHIVED"] = "ARCHIVED";
|
|
43450
43508
|
TodoStatus2["DELETED"] = "DELETED";
|
|
43451
43509
|
})(TodoStatus ||= {});
|
|
43452
|
-
//
|
|
43510
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/models.ts
|
|
43453
43511
|
var DEFAULT_MODEL = "anthropic:anthropic/claude-opus-5.5";
|
|
43454
43512
|
var MODEL_ALIASES = {
|
|
43455
43513
|
claude: DEFAULT_MODEL
|
|
@@ -43520,7 +43578,7 @@ function qualifiedModelIds(listing, filter) {
|
|
|
43520
43578
|
}
|
|
43521
43579
|
return q ? ids.filter((id) => id.toLowerCase().includes(q)) : ids;
|
|
43522
43580
|
}
|
|
43523
|
-
//
|
|
43581
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/billing.ts
|
|
43524
43582
|
var USAGE_WINDOWS = {
|
|
43525
43583
|
SESSION_MS: 5 * 60 * 60 * 1000,
|
|
43526
43584
|
WEEK_MS: 7 * 24 * 60 * 60 * 1000,
|
|
@@ -43528,7 +43586,7 @@ var USAGE_WINDOWS = {
|
|
|
43528
43586
|
SESSION_FACTOR: 1 / 5
|
|
43529
43587
|
};
|
|
43530
43588
|
var NEVER_SCHEDULED_TIMESTAMP = 8640000000000000;
|
|
43531
|
-
//
|
|
43589
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/mimetypes.json
|
|
43532
43590
|
var mimetypes_default = {
|
|
43533
43591
|
conversionProfiles: {
|
|
43534
43592
|
to_jpeg_90: { toMime: "image/jpeg", toExt: "jpg", quality: 0.9 }
|
|
@@ -43621,7 +43679,7 @@ var mimetypes_default = {
|
|
|
43621
43679
|
}
|
|
43622
43680
|
};
|
|
43623
43681
|
|
|
43624
|
-
//
|
|
43682
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/attachmentUtils.ts
|
|
43625
43683
|
var MCP_TEXT_MIME = Object.entries(mimetypes_default.aliases).find(([, type]) => type === "mcp_text")[0];
|
|
43626
43684
|
var MCP_AUDIO_MIME = Object.entries(mimetypes_default.aliases).find(([, type]) => type === "mcp_audio")[0];
|
|
43627
43685
|
var AttachmentType = Object.fromEntries(Object.keys(mimetypes_default.types).map((k) => [k.toUpperCase(), k]));
|
|
@@ -43661,7 +43719,7 @@ for (const [ext, entry] of Object.entries(mimetypes_default.extensions)) {
|
|
|
43661
43719
|
if (!MIME_TO_EXT[mime])
|
|
43662
43720
|
MIME_TO_EXT[mime] = ext;
|
|
43663
43721
|
}
|
|
43664
|
-
//
|
|
43722
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/edgeTypes.ts
|
|
43665
43723
|
var NON_SHELL_TYPES = new Set([
|
|
43666
43724
|
"WEB_EXTENSION" /* WEB_EXTENSION */,
|
|
43667
43725
|
"EXTENSION" /* EXTENSION */,
|
|
@@ -43669,7 +43727,7 @@ var NON_SHELL_TYPES = new Set([
|
|
|
43669
43727
|
"ANDROID" /* ANDROID */,
|
|
43670
43728
|
"IOS" /* IOS */
|
|
43671
43729
|
]);
|
|
43672
|
-
//
|
|
43730
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/blocks/index.ts
|
|
43673
43731
|
var USER_INPUT_BLOCK_TYPES = new Set([
|
|
43674
43732
|
"question" /* Question */,
|
|
43675
43733
|
"login_prompt" /* Login */,
|
|
@@ -43723,7 +43781,7 @@ var FILE_TOOL_TYPES = new Set([
|
|
|
43723
43781
|
"edit" /* Edit */,
|
|
43724
43782
|
"modify_file" /* ModifyFile */
|
|
43725
43783
|
]);
|
|
43726
|
-
//
|
|
43784
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/channels.ts
|
|
43727
43785
|
var privateSuffix = (privateOwnerId) => privateOwnerId ? `:private:${privateOwnerId}` : "";
|
|
43728
43786
|
var SERVER_TO_FRONTENDS = {
|
|
43729
43787
|
task: {
|
|
@@ -43782,6 +43840,7 @@ var SERVER_TO_FRONTENDS = {
|
|
|
43782
43840
|
msgMetaAi: (todoId) => `${"todo:msg_meta_ai" /* TODO_MSG_META_AI */}:${todoId}`,
|
|
43783
43841
|
msgQueueUpdate: (todoId) => `${"todo:msg_queue_update" /* TODO_MSG_QUEUE_UPDATE */}:${todoId}`,
|
|
43784
43842
|
message_removed: (todoId) => `${"todo:message_removed" /* TODO_MESSAGE_REMOVED */}:${todoId}`,
|
|
43843
|
+
commentUpdate: (todoId) => `${"todo:comment_update" /* TODO_COMMENT_UPDATE */}:${todoId}`,
|
|
43785
43844
|
start_universal: (todoId) => `${"block:start_universal" /* BLOCK_START_UNIVERSAL */}:${todoId}`,
|
|
43786
43845
|
message: (todoId) => `${"block:message" /* BLOCK_MESSAGE */}:${todoId}`,
|
|
43787
43846
|
end: (todoId) => `${"block:end" /* BLOCK_END */}:${todoId}`,
|
|
@@ -43812,7 +43871,7 @@ var SERVER_TO_FRONTENDS = {
|
|
|
43812
43871
|
function_call_request: (userId) => `${"FUNCTION_CALL_REQUEST_AGENT" /* FUNCTION_CALL_REQUEST_AGENT */}:frontend:${userId}`
|
|
43813
43872
|
}
|
|
43814
43873
|
};
|
|
43815
|
-
//
|
|
43874
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/REST_types.ts
|
|
43816
43875
|
var MANUAL_CHANNEL = "Manual";
|
|
43817
43876
|
function manualAnswersOf(sources) {
|
|
43818
43877
|
const manual = sources?.find((s) => s.channel === MANUAL_CHANNEL);
|
|
@@ -43823,7 +43882,8 @@ function manualAnswersOf(sources) {
|
|
|
43823
43882
|
return out;
|
|
43824
43883
|
}
|
|
43825
43884
|
var manualSamples = (answers) => Object.entries(answers).filter(([, a]) => a.trim()).map(([prompt, text]) => ({ prompt, text: text.trim() }));
|
|
43826
|
-
|
|
43885
|
+
var senderLabel = (s) => s.kind === "team" ? `${s.name} · TODO for AI team` : `${s.name} · your agent`;
|
|
43886
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/templateBody.ts
|
|
43827
43887
|
var USER_HEADINGS = new Set([
|
|
43828
43888
|
"your task",
|
|
43829
43889
|
"your tasks",
|
|
@@ -43832,7 +43892,7 @@ var USER_HEADINGS = new Set([
|
|
|
43832
43892
|
"steps",
|
|
43833
43893
|
"data needed"
|
|
43834
43894
|
]);
|
|
43835
|
-
//
|
|
43895
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/bashPatterns.ts
|
|
43836
43896
|
function splitShellCommands(input) {
|
|
43837
43897
|
const parts = [];
|
|
43838
43898
|
let current = "";
|
|
@@ -44074,7 +44134,7 @@ function getBlockServerId(block) {
|
|
|
44074
44134
|
return prefix ? prefix.slice(0, -1) : "*";
|
|
44075
44135
|
}
|
|
44076
44136
|
|
|
44077
|
-
//
|
|
44137
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/permissionUtils.ts
|
|
44078
44138
|
function parsePattern(pattern) {
|
|
44079
44139
|
const colonIndex = pattern.indexOf(":");
|
|
44080
44140
|
if (colonIndex === -1)
|
|
@@ -44175,7 +44235,7 @@ function getBlockNewPatterns(block, permissions) {
|
|
|
44175
44235
|
}
|
|
44176
44236
|
return pairs.filter(({ raw, pattern }) => !isPatternAllowed(permissions, pattern) && !allow.some((rule) => bashRuleMatchesCmd(rule, serverId, raw))).map(({ pattern }) => pattern);
|
|
44177
44237
|
}
|
|
44178
|
-
//
|
|
44238
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/systemAgents.ts
|
|
44179
44239
|
var BUSINESS_ANALYZER_AGENT = {
|
|
44180
44240
|
id: "00000000-0000-0000-0000-202601013151",
|
|
44181
44241
|
name: "Business Analyzer",
|
|
@@ -44321,7 +44381,7 @@ var SYSTEM_AGENTS = {
|
|
|
44321
44381
|
[ONBOARDING_AGENT.id]: ONBOARDING_AGENT
|
|
44322
44382
|
};
|
|
44323
44383
|
var RECOMMEND_STEP = "Recommend growth TODOs for this project: materialize the ones you genuinely believe fit " + "as proposal cards on the board (api_recommend_template, one call per recommendation). " + "No quota — fewer good cards beat padding.";
|
|
44324
|
-
//
|
|
44384
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/outputLimits.ts
|
|
44325
44385
|
var MAX_LINE_LEN = 300;
|
|
44326
44386
|
var STREAM_FIRST = 1e4;
|
|
44327
44387
|
var STREAM_LAST = 1e4;
|
|
@@ -44332,7 +44392,7 @@ var OUTPUT_POLICIES = {
|
|
|
44332
44392
|
full: { firstLimit: Infinity, lastLimit: 0, hardCap: RUN_OUTPUT_CAP, lineLimit: MAX_LINE_LEN },
|
|
44333
44393
|
raw: { firstLimit: Infinity, lastLimit: 0, hardCap: Infinity, lineLimit: Infinity }
|
|
44334
44394
|
};
|
|
44335
|
-
//
|
|
44395
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/model_promos.json
|
|
44336
44396
|
var model_promos_default = {
|
|
44337
44397
|
_doc: [
|
|
44338
44398
|
"Single source of truth for TODOforAI promotional model discounts.",
|
|
@@ -44374,10 +44434,10 @@ var model_promos_default = {
|
|
|
44374
44434
|
]
|
|
44375
44435
|
};
|
|
44376
44436
|
|
|
44377
|
-
//
|
|
44437
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/modelPromos.ts
|
|
44378
44438
|
var BOOST_FACTOR = model_promos_default.boost_remaining_price_factor;
|
|
44379
44439
|
var MODEL_PROMOS = model_promos_default.promos;
|
|
44380
|
-
//
|
|
44440
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/realtime/topics.ts
|
|
44381
44441
|
var TOPICS = {
|
|
44382
44442
|
["todo:new" /* NEW_TODO */]: {
|
|
44383
44443
|
channel: (p) => SERVER_TO_FRONTENDS.project.new_todo(p.projectId, p.privateOwnerId),
|
|
@@ -44483,6 +44543,10 @@ var TOPICS = {
|
|
|
44483
44543
|
channel: (p) => SERVER_TO_FRONTENDS.todo.message_removed(p.todoId),
|
|
44484
44544
|
audience: "frontend"
|
|
44485
44545
|
},
|
|
44546
|
+
["todo:comment_update" /* TODO_COMMENT_UPDATE */]: {
|
|
44547
|
+
channel: (p) => SERVER_TO_FRONTENDS.todo.commentUpdate(p.todoId),
|
|
44548
|
+
audience: "frontend"
|
|
44549
|
+
},
|
|
44486
44550
|
["todo:msg_meta_usr" /* TODO_MSG_META_USR */]: {
|
|
44487
44551
|
channel: (p) => SERVER_TO_FRONTENDS.todo.msgMetaUsr(p.todoId),
|
|
44488
44552
|
audience: "frontend"
|
|
@@ -44540,12 +44604,12 @@ var TOPICS = {
|
|
|
44540
44604
|
audience: "frontend"
|
|
44541
44605
|
}
|
|
44542
44606
|
};
|
|
44543
|
-
//
|
|
44607
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/modelNames.ts
|
|
44544
44608
|
var LEGACY_MODEL_MAPPING = {
|
|
44545
44609
|
claude: DEFAULT_MODEL.slice(DEFAULT_MODEL.indexOf(":") + 1),
|
|
44546
44610
|
"gemini-pro": "google/gemini-3.1-pro-preview"
|
|
44547
44611
|
};
|
|
44548
|
-
//
|
|
44612
|
+
// ../../../home/hm/repo/todoforai/packages/shared-credentials/src/index.ts
|
|
44549
44613
|
function normalizeApiUrl2(url) {
|
|
44550
44614
|
if (url.startsWith("localhost"))
|
|
44551
44615
|
return `http://${url}`;
|
|
@@ -44559,7 +44623,7 @@ import { parseArgs } from "util";
|
|
|
44559
44623
|
// package.json
|
|
44560
44624
|
var package_default = {
|
|
44561
44625
|
name: "@todoforai/cli",
|
|
44562
|
-
version: "0.1.
|
|
44626
|
+
version: "0.1.55",
|
|
44563
44627
|
type: "module",
|
|
44564
44628
|
bin: {
|
|
44565
44629
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -44627,6 +44691,9 @@ Examples:
|
|
|
44627
44691
|
tfa-cli show list|rm … # List / take down shown files
|
|
44628
44692
|
tfa-cli open <url> [todo-id] # Live url preview in the chat
|
|
44629
44693
|
tfa-cli recommend --template <id> # Add a template as a recommendation card
|
|
44694
|
+
tfa-cli inbox [needs-you|messages|activity] [--unread] # Read the notification feed (the bell)
|
|
44695
|
+
tfa-cli inbox seen [tab] # Mark notifications read
|
|
44696
|
+
tfa-cli notify "Title" "message" [--href /t/<id>] # Note to YOUR OWN user only (Messages tab + phone push); no --to
|
|
44630
44697
|
tfa-cli claim mint --seed <projectId> [--emails a@x,b@y] [--ttl <sec>] # Mint /claim/<token> links for a project you own
|
|
44631
44698
|
tfa-cli next [--direction "<text>"] # Analyzer growth recommendation cards
|
|
44632
44699
|
tfa-cli <command> help # Details per command
|
|
@@ -44707,7 +44774,7 @@ var WORD_FLAGS = {
|
|
|
44707
44774
|
agents: "list-agents",
|
|
44708
44775
|
config: "show-config"
|
|
44709
44776
|
};
|
|
44710
|
-
var HELP_SUBCOMMANDS = ["agent", "todo", "project", "brand", "device", "list", "ls", "status", "show", "open"];
|
|
44777
|
+
var HELP_SUBCOMMANDS = ["agent", "todo", "project", "brand", "device", "list", "ls", "status", "show", "open", "inbox", "notify"];
|
|
44711
44778
|
function parseCliArgs() {
|
|
44712
44779
|
const { values, positionals } = parseArgs({
|
|
44713
44780
|
args: process.argv.slice(2),
|
|
@@ -44742,6 +44809,10 @@ function parseCliArgs() {
|
|
|
44742
44809
|
seed: { type: "string" },
|
|
44743
44810
|
emails: { type: "string" },
|
|
44744
44811
|
ttl: { type: "string" },
|
|
44812
|
+
subject: { type: "string" },
|
|
44813
|
+
href: { type: "string" },
|
|
44814
|
+
unread: { type: "boolean", default: false },
|
|
44815
|
+
limit: { type: "string" },
|
|
44745
44816
|
resume: { type: "string", short: "r" },
|
|
44746
44817
|
continue: { type: "boolean", short: "c", default: false },
|
|
44747
44818
|
"non-interactive": { type: "boolean", short: "n", default: false },
|
|
@@ -46999,7 +47070,88 @@ function renderDiff(originalContent, modifiedContent, filePath) {
|
|
|
46999
47070
|
return parts.join("");
|
|
47000
47071
|
}
|
|
47001
47072
|
|
|
47073
|
+
// src/shutdown.ts
|
|
47074
|
+
var activeRun = null;
|
|
47075
|
+
var cleanups = [];
|
|
47076
|
+
var exiting = null;
|
|
47077
|
+
var cancelling = null;
|
|
47078
|
+
var CANCEL_TIMEOUT_MS = 5000;
|
|
47079
|
+
function setActiveRun(run) {
|
|
47080
|
+
activeRun = run;
|
|
47081
|
+
}
|
|
47082
|
+
function withTimeout(p, ms, fallback) {
|
|
47083
|
+
let timer;
|
|
47084
|
+
const timeout = new Promise((r) => {
|
|
47085
|
+
timer = setTimeout(() => r(fallback), ms);
|
|
47086
|
+
});
|
|
47087
|
+
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
47088
|
+
}
|
|
47089
|
+
function cancelActiveRun(timeoutMs = CANCEL_TIMEOUT_MS) {
|
|
47090
|
+
if (cancelling)
|
|
47091
|
+
return cancelling;
|
|
47092
|
+
const pending = activeRun;
|
|
47093
|
+
if (!pending)
|
|
47094
|
+
return Promise.resolve({ ok: false, todoId: null });
|
|
47095
|
+
const res = { ok: false };
|
|
47096
|
+
const attempt = (async () => {
|
|
47097
|
+
const run = await pending;
|
|
47098
|
+
res.todoId = run?.todoId ?? null;
|
|
47099
|
+
if (!run)
|
|
47100
|
+
return res;
|
|
47101
|
+
if (!run.ws.connected && !await run.ws.connect())
|
|
47102
|
+
return res;
|
|
47103
|
+
res.ok = await run.ws.sendInterrupt(run.projectId, run.todoId);
|
|
47104
|
+
return res;
|
|
47105
|
+
})().catch(() => res);
|
|
47106
|
+
cancelling = withTimeout(attempt, timeoutMs, res).finally(() => {
|
|
47107
|
+
if (activeRun === pending)
|
|
47108
|
+
activeRun = null;
|
|
47109
|
+
cancelling = null;
|
|
47110
|
+
});
|
|
47111
|
+
return cancelling;
|
|
47112
|
+
}
|
|
47113
|
+
function shutdown(code, reason) {
|
|
47114
|
+
return exiting ??= (async () => {
|
|
47115
|
+
if (reason)
|
|
47116
|
+
process.stderr.write(`
|
|
47117
|
+
${reason}
|
|
47118
|
+
`);
|
|
47119
|
+
if (activeRun) {
|
|
47120
|
+
const { ok, todoId } = await cancelActiveRun();
|
|
47121
|
+
if (ok)
|
|
47122
|
+
process.stderr.write(`Stopped todo ${todoId}
|
|
47123
|
+
`);
|
|
47124
|
+
else if (todoId)
|
|
47125
|
+
process.stderr.write(`Warning: could not stop todo ${todoId} (backend unreachable)
|
|
47126
|
+
`);
|
|
47127
|
+
else if (todoId === undefined)
|
|
47128
|
+
process.stderr.write(`Warning: could not stop the todo being created (timed out)
|
|
47129
|
+
`);
|
|
47130
|
+
}
|
|
47131
|
+
for (const fn of cleanups.splice(0)) {
|
|
47132
|
+
try {
|
|
47133
|
+
fn();
|
|
47134
|
+
} catch {}
|
|
47135
|
+
}
|
|
47136
|
+
process.exit(code);
|
|
47137
|
+
})();
|
|
47138
|
+
}
|
|
47139
|
+
|
|
47002
47140
|
// src/watch.ts
|
|
47141
|
+
var RECONNECT_WINDOW_MS = 60000;
|
|
47142
|
+
var DISCONNECTED = { type: "socket:closed", success: false };
|
|
47143
|
+
async function resubscribe(ws, todoId, callback, windowMs = RECONNECT_WINDOW_MS) {
|
|
47144
|
+
const deadline = Date.now() + windowMs;
|
|
47145
|
+
for (let delay = 1000;; delay = Math.min(delay * 2, 5000)) {
|
|
47146
|
+
await new Promise((r) => setTimeout(r, Math.max(0, Math.min(delay, deadline - Date.now()))));
|
|
47147
|
+
const left = deadline - Date.now();
|
|
47148
|
+
if (left <= 0)
|
|
47149
|
+
return false;
|
|
47150
|
+
const attempt = ws.subscribe(todoId, callback, { replayStatus: true }).catch(() => false);
|
|
47151
|
+
if (await withTimeout(attempt, left, false))
|
|
47152
|
+
return true;
|
|
47153
|
+
}
|
|
47154
|
+
}
|
|
47003
47155
|
var diffStoreByWs = new WeakMap;
|
|
47004
47156
|
function classifyBlock(info) {
|
|
47005
47157
|
const inner = (info.block_type || "").toLowerCase();
|
|
@@ -47081,12 +47233,8 @@ async function watchTodo(ws, todoId, projectId, opts = {}) {
|
|
|
47081
47233
|
process.removeAllListeners("SIGINT");
|
|
47082
47234
|
process.on("SIGINT", () => {
|
|
47083
47235
|
interruptCount++;
|
|
47084
|
-
if (interruptCount >= 2)
|
|
47085
|
-
|
|
47086
|
-
${RED}Force exit (double Ctrl+C)${RESET}
|
|
47087
|
-
`);
|
|
47088
|
-
process.exit(130);
|
|
47089
|
-
}
|
|
47236
|
+
if (opts.exitOnInterrupt || interruptCount >= 2)
|
|
47237
|
+
return void shutdown(130, "Cancelled by user (Ctrl+C)");
|
|
47090
47238
|
process.stderr.write(`
|
|
47091
47239
|
${YELLOW}Interrupting... (Ctrl+C again to force exit)${RESET}
|
|
47092
47240
|
`);
|
|
@@ -47285,8 +47433,22 @@ ${DIM}[todo:status] ${status}${RESET}
|
|
|
47285
47433
|
callback(msgType, payload);
|
|
47286
47434
|
}
|
|
47287
47435
|
}
|
|
47436
|
+
setActiveRun({ ws, projectId, todoId });
|
|
47288
47437
|
try {
|
|
47289
|
-
const
|
|
47438
|
+
const subscribed = await ws.subscribe(todoId, callback).catch(() => false);
|
|
47439
|
+
let result = subscribed ? await ws.completion(todoId) : DISCONNECTED;
|
|
47440
|
+
while (result?.type === "socket:closed") {
|
|
47441
|
+
process.stderr.write(`
|
|
47442
|
+
${YELLOW}Connection lost — reconnecting...${RESET}
|
|
47443
|
+
`);
|
|
47444
|
+
if (!await resubscribe(ws, todoId, callback)) {
|
|
47445
|
+
await shutdown(1, `${RED}Error: backend unreachable for ${RECONNECT_WINDOW_MS / 1000}s — giving up on todo ${todoId}${RESET}`);
|
|
47446
|
+
}
|
|
47447
|
+
process.stderr.write(`${DIM}Reconnected.${RESET}
|
|
47448
|
+
`);
|
|
47449
|
+
result = await ws.completion(todoId);
|
|
47450
|
+
}
|
|
47451
|
+
setActiveRun(null);
|
|
47290
47452
|
process.stdout.write(`
|
|
47291
47453
|
`);
|
|
47292
47454
|
if (!result?.success) {
|
|
@@ -47299,6 +47461,8 @@ ${DIM}[todo:status] ${status}${RESET}
|
|
|
47299
47461
|
}
|
|
47300
47462
|
return true;
|
|
47301
47463
|
} catch (e) {
|
|
47464
|
+
await cancelActiveRun();
|
|
47465
|
+
process.exitCode = 1;
|
|
47302
47466
|
if (!opts.suppressCancelNotice) {
|
|
47303
47467
|
process.stderr.write(`${YELLOW}Interrupted${RESET}
|
|
47304
47468
|
`);
|
|
@@ -48569,6 +48733,425 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
48569
48733
|
`);
|
|
48570
48734
|
}
|
|
48571
48735
|
|
|
48736
|
+
// src/notify-command.ts
|
|
48737
|
+
import { createHash } from "crypto";
|
|
48738
|
+
|
|
48739
|
+
// src/input.ts
|
|
48740
|
+
function readMultiline2(prompt, history) {
|
|
48741
|
+
let cancelFn = () => {};
|
|
48742
|
+
const promise = new Promise((resolve4, reject) => {
|
|
48743
|
+
const out = process.stderr;
|
|
48744
|
+
let buf = "";
|
|
48745
|
+
let cursor = 0;
|
|
48746
|
+
let pasting = false;
|
|
48747
|
+
let done = false;
|
|
48748
|
+
let screenRow = 0;
|
|
48749
|
+
let historyIndex = -1;
|
|
48750
|
+
let savedCurrent = "";
|
|
48751
|
+
const promptLen = prompt.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
48752
|
+
out.write(prompt);
|
|
48753
|
+
process.stdin.setRawMode(true);
|
|
48754
|
+
process.stdin.resume();
|
|
48755
|
+
out.write("\x1B[?2004h");
|
|
48756
|
+
function rowOf(pos) {
|
|
48757
|
+
const cols = process.stderr.columns || 80;
|
|
48758
|
+
const lines = buf.slice(0, pos).split(`
|
|
48759
|
+
`);
|
|
48760
|
+
let row = 0;
|
|
48761
|
+
for (let i = 0;i < lines.length; i++) {
|
|
48762
|
+
const len = (i === 0 ? promptLen : 0) + lines[i].length;
|
|
48763
|
+
if (i < lines.length - 1) {
|
|
48764
|
+
row += len === 0 ? 1 : Math.ceil(len / cols);
|
|
48765
|
+
} else {
|
|
48766
|
+
row += len > 0 ? Math.ceil(len / cols) - 1 : 0;
|
|
48767
|
+
}
|
|
48768
|
+
}
|
|
48769
|
+
return row;
|
|
48770
|
+
}
|
|
48771
|
+
function colOf(pos) {
|
|
48772
|
+
const cols = process.stderr.columns || 80;
|
|
48773
|
+
const lastNl = buf.lastIndexOf(`
|
|
48774
|
+
`, pos - 1);
|
|
48775
|
+
const lineStart = lastNl + 1;
|
|
48776
|
+
const offset = lineStart === 0 ? promptLen : 0;
|
|
48777
|
+
const linePos = offset + (pos - lineStart);
|
|
48778
|
+
if (linePos > 0 && linePos % cols === 0)
|
|
48779
|
+
return cols;
|
|
48780
|
+
return linePos % cols;
|
|
48781
|
+
}
|
|
48782
|
+
function totalRows() {
|
|
48783
|
+
const cols = process.stderr.columns || 80;
|
|
48784
|
+
const lines = buf.split(`
|
|
48785
|
+
`);
|
|
48786
|
+
let rows = 0;
|
|
48787
|
+
for (let i = 0;i < lines.length; i++) {
|
|
48788
|
+
const len = (i === 0 ? promptLen : 0) + lines[i].length;
|
|
48789
|
+
rows += len === 0 ? 1 : Math.ceil(len / cols);
|
|
48790
|
+
}
|
|
48791
|
+
return rows;
|
|
48792
|
+
}
|
|
48793
|
+
function redraw() {
|
|
48794
|
+
const cols = process.stderr.columns || 80;
|
|
48795
|
+
out.write("\r");
|
|
48796
|
+
if (screenRow > 0)
|
|
48797
|
+
out.write(`\x1B[${screenRow}A`);
|
|
48798
|
+
out.write("\x1B[J");
|
|
48799
|
+
out.write(prompt + buf.replace(/\n/g, `\r
|
|
48800
|
+
`));
|
|
48801
|
+
const targetRow = rowOf(cursor);
|
|
48802
|
+
const endRow = totalRows() - 1;
|
|
48803
|
+
const rowsBack = endRow - targetRow;
|
|
48804
|
+
const col = colOf(cursor);
|
|
48805
|
+
if (rowsBack > 0)
|
|
48806
|
+
out.write(`\x1B[${rowsBack}A`);
|
|
48807
|
+
if (col <= cols) {
|
|
48808
|
+
out.write("\r");
|
|
48809
|
+
if (col > 0 && col < cols)
|
|
48810
|
+
out.write(`\x1B[${col}C`);
|
|
48811
|
+
if (col === cols)
|
|
48812
|
+
out.write(`\x1B[${cols}G`);
|
|
48813
|
+
}
|
|
48814
|
+
screenRow = targetRow;
|
|
48815
|
+
}
|
|
48816
|
+
function finish(cancelled) {
|
|
48817
|
+
if (done)
|
|
48818
|
+
return;
|
|
48819
|
+
done = true;
|
|
48820
|
+
const rowsDown = totalRows() - 1 - screenRow;
|
|
48821
|
+
if (rowsDown > 0)
|
|
48822
|
+
out.write(`\x1B[${rowsDown}B`);
|
|
48823
|
+
out.write("\x1B[?2004l");
|
|
48824
|
+
out.write(`
|
|
48825
|
+
`);
|
|
48826
|
+
process.stdin.setRawMode(false);
|
|
48827
|
+
process.stdin.pause();
|
|
48828
|
+
process.stdin.removeListener("data", onData);
|
|
48829
|
+
if (cancelled)
|
|
48830
|
+
reject(new Error("cancelled"));
|
|
48831
|
+
else
|
|
48832
|
+
resolve4(buf.trim());
|
|
48833
|
+
}
|
|
48834
|
+
function wordRight() {
|
|
48835
|
+
let p = cursor;
|
|
48836
|
+
while (p < buf.length && buf[p] === " ")
|
|
48837
|
+
p++;
|
|
48838
|
+
while (p < buf.length && buf[p] !== " ")
|
|
48839
|
+
p++;
|
|
48840
|
+
return p;
|
|
48841
|
+
}
|
|
48842
|
+
function wordLeft() {
|
|
48843
|
+
let p = cursor;
|
|
48844
|
+
while (p > 0 && buf[p - 1] === " ")
|
|
48845
|
+
p--;
|
|
48846
|
+
while (p > 0 && buf[p - 1] !== " ")
|
|
48847
|
+
p--;
|
|
48848
|
+
return p;
|
|
48849
|
+
}
|
|
48850
|
+
function killToEnd() {
|
|
48851
|
+
buf = buf.slice(0, cursor);
|
|
48852
|
+
redraw();
|
|
48853
|
+
}
|
|
48854
|
+
function deleteWordBack() {
|
|
48855
|
+
const to = wordLeft();
|
|
48856
|
+
if (to === cursor)
|
|
48857
|
+
return;
|
|
48858
|
+
buf = buf.slice(0, to) + buf.slice(cursor);
|
|
48859
|
+
cursor = to;
|
|
48860
|
+
redraw();
|
|
48861
|
+
}
|
|
48862
|
+
function handleCSI(chunk, start) {
|
|
48863
|
+
let i = start + 1;
|
|
48864
|
+
if (i >= chunk.length || chunk[i] !== "[") {
|
|
48865
|
+
if (i < chunk.length)
|
|
48866
|
+
i++;
|
|
48867
|
+
return i;
|
|
48868
|
+
}
|
|
48869
|
+
i++;
|
|
48870
|
+
let params = "";
|
|
48871
|
+
while (i < chunk.length && /[0-9;]/.test(chunk[i])) {
|
|
48872
|
+
params += chunk[i];
|
|
48873
|
+
i++;
|
|
48874
|
+
}
|
|
48875
|
+
const final = i < chunk.length ? chunk[i] : "";
|
|
48876
|
+
i++;
|
|
48877
|
+
const parts = params.split(";");
|
|
48878
|
+
const modifier = parts.length > 1 ? parseInt(parts[1]) : 0;
|
|
48879
|
+
const code = parts[0] || "";
|
|
48880
|
+
const ctrl = modifier === 5;
|
|
48881
|
+
switch (final) {
|
|
48882
|
+
case "D":
|
|
48883
|
+
if (ctrl) {
|
|
48884
|
+
cursor = wordLeft();
|
|
48885
|
+
redraw();
|
|
48886
|
+
} else if (cursor > 0) {
|
|
48887
|
+
cursor--;
|
|
48888
|
+
redraw();
|
|
48889
|
+
}
|
|
48890
|
+
break;
|
|
48891
|
+
case "C":
|
|
48892
|
+
if (ctrl) {
|
|
48893
|
+
cursor = wordRight();
|
|
48894
|
+
redraw();
|
|
48895
|
+
} else if (cursor < buf.length) {
|
|
48896
|
+
cursor++;
|
|
48897
|
+
redraw();
|
|
48898
|
+
}
|
|
48899
|
+
break;
|
|
48900
|
+
case "A":
|
|
48901
|
+
if (history && history.length > 0) {
|
|
48902
|
+
if (historyIndex === -1)
|
|
48903
|
+
savedCurrent = buf;
|
|
48904
|
+
if (historyIndex < history.length - 1) {
|
|
48905
|
+
historyIndex++;
|
|
48906
|
+
buf = history[history.length - 1 - historyIndex];
|
|
48907
|
+
cursor = buf.length;
|
|
48908
|
+
redraw();
|
|
48909
|
+
}
|
|
48910
|
+
}
|
|
48911
|
+
break;
|
|
48912
|
+
case "B":
|
|
48913
|
+
if (history && historyIndex >= 0) {
|
|
48914
|
+
historyIndex--;
|
|
48915
|
+
if (historyIndex === -1) {
|
|
48916
|
+
buf = savedCurrent;
|
|
48917
|
+
} else {
|
|
48918
|
+
buf = history[history.length - 1 - historyIndex];
|
|
48919
|
+
}
|
|
48920
|
+
cursor = buf.length;
|
|
48921
|
+
redraw();
|
|
48922
|
+
}
|
|
48923
|
+
break;
|
|
48924
|
+
case "H":
|
|
48925
|
+
cursor = 0;
|
|
48926
|
+
redraw();
|
|
48927
|
+
break;
|
|
48928
|
+
case "F":
|
|
48929
|
+
cursor = buf.length;
|
|
48930
|
+
redraw();
|
|
48931
|
+
break;
|
|
48932
|
+
case "~":
|
|
48933
|
+
if (code === "3" && cursor < buf.length) {
|
|
48934
|
+
buf = buf.slice(0, cursor) + buf.slice(cursor + 1);
|
|
48935
|
+
redraw();
|
|
48936
|
+
}
|
|
48937
|
+
break;
|
|
48938
|
+
}
|
|
48939
|
+
return i;
|
|
48940
|
+
}
|
|
48941
|
+
function onData(data) {
|
|
48942
|
+
let s = data.toString("utf-8");
|
|
48943
|
+
while (s.length > 0 && !done) {
|
|
48944
|
+
if (pasting) {
|
|
48945
|
+
const end = s.indexOf("\x1B[201~");
|
|
48946
|
+
if (end >= 0) {
|
|
48947
|
+
const text = s.slice(0, end).replace(/\r\n?|\n/g, `
|
|
48948
|
+
`);
|
|
48949
|
+
buf = buf.slice(0, cursor) + text + buf.slice(cursor);
|
|
48950
|
+
cursor += text.length;
|
|
48951
|
+
pasting = false;
|
|
48952
|
+
s = s.slice(end + 6);
|
|
48953
|
+
redraw();
|
|
48954
|
+
} else {
|
|
48955
|
+
const text = s.replace(/\r\n?|\n/g, `
|
|
48956
|
+
`);
|
|
48957
|
+
buf = buf.slice(0, cursor) + text + buf.slice(cursor);
|
|
48958
|
+
cursor += text.length;
|
|
48959
|
+
s = "";
|
|
48960
|
+
redraw();
|
|
48961
|
+
}
|
|
48962
|
+
} else {
|
|
48963
|
+
const ps = s.indexOf("\x1B[200~");
|
|
48964
|
+
const chunk = ps >= 0 ? s.slice(0, ps) : s;
|
|
48965
|
+
for (let i = 0;i < chunk.length && !done; i++) {
|
|
48966
|
+
const c3 = chunk.charCodeAt(i);
|
|
48967
|
+
if (c3 === 3) {
|
|
48968
|
+
finish(true);
|
|
48969
|
+
return;
|
|
48970
|
+
}
|
|
48971
|
+
if (c3 === 13 || c3 === 10) {
|
|
48972
|
+
finish(false);
|
|
48973
|
+
return;
|
|
48974
|
+
}
|
|
48975
|
+
if (c3 === 23) {
|
|
48976
|
+
deleteWordBack();
|
|
48977
|
+
} else if (c3 === 11) {
|
|
48978
|
+
killToEnd();
|
|
48979
|
+
} else if (c3 === 127 || c3 === 8) {
|
|
48980
|
+
if (cursor > 0) {
|
|
48981
|
+
buf = buf.slice(0, cursor - 1) + buf.slice(cursor);
|
|
48982
|
+
cursor--;
|
|
48983
|
+
redraw();
|
|
48984
|
+
}
|
|
48985
|
+
} else if (c3 === 27) {
|
|
48986
|
+
if (i + 1 < chunk.length && chunk.charCodeAt(i + 1) === 13) {
|
|
48987
|
+
buf = buf.slice(0, cursor) + `
|
|
48988
|
+
` + buf.slice(cursor);
|
|
48989
|
+
cursor++;
|
|
48990
|
+
i++;
|
|
48991
|
+
redraw();
|
|
48992
|
+
} else {
|
|
48993
|
+
i = handleCSI(chunk, i) - 1;
|
|
48994
|
+
}
|
|
48995
|
+
} else if (c3 === 1) {
|
|
48996
|
+
cursor = 0;
|
|
48997
|
+
redraw();
|
|
48998
|
+
} else if (c3 === 5) {
|
|
48999
|
+
cursor = buf.length;
|
|
49000
|
+
redraw();
|
|
49001
|
+
} else if (c3 >= 32) {
|
|
49002
|
+
buf = buf.slice(0, cursor) + chunk[i] + buf.slice(cursor);
|
|
49003
|
+
cursor++;
|
|
49004
|
+
redraw();
|
|
49005
|
+
}
|
|
49006
|
+
}
|
|
49007
|
+
if (ps >= 0) {
|
|
49008
|
+
pasting = true;
|
|
49009
|
+
s = s.slice(ps + 6);
|
|
49010
|
+
} else
|
|
49011
|
+
s = "";
|
|
49012
|
+
}
|
|
49013
|
+
}
|
|
49014
|
+
}
|
|
49015
|
+
process.stdin.on("data", onData);
|
|
49016
|
+
cancelFn = () => finish(true);
|
|
49017
|
+
});
|
|
49018
|
+
return { promise, cancel: () => cancelFn() };
|
|
49019
|
+
}
|
|
49020
|
+
async function readStdin2() {
|
|
49021
|
+
if (process.stdin.isTTY) {
|
|
49022
|
+
try {
|
|
49023
|
+
const content2 = await readMultiline2(`${BRIGHT_WHITE}TODO>${RESET} `).promise;
|
|
49024
|
+
if (!content2) {
|
|
49025
|
+
process.stderr.write(`Error: Empty input
|
|
49026
|
+
`);
|
|
49027
|
+
process.exit(1);
|
|
49028
|
+
}
|
|
49029
|
+
return content2;
|
|
49030
|
+
} catch {
|
|
49031
|
+
process.stderr.write(`
|
|
49032
|
+
Cancelled
|
|
49033
|
+
`);
|
|
49034
|
+
process.exit(1);
|
|
49035
|
+
}
|
|
49036
|
+
}
|
|
49037
|
+
const chunks = [];
|
|
49038
|
+
for await (const chunk of process.stdin)
|
|
49039
|
+
chunks.push(chunk);
|
|
49040
|
+
const content = Buffer.concat(chunks).toString("utf-8").trim();
|
|
49041
|
+
if (!content) {
|
|
49042
|
+
process.stderr.write(`Error: Empty input
|
|
49043
|
+
`);
|
|
49044
|
+
process.exit(1);
|
|
49045
|
+
}
|
|
49046
|
+
return content;
|
|
49047
|
+
}
|
|
49048
|
+
|
|
49049
|
+
// src/notify-command.ts
|
|
49050
|
+
var TIERS = { "needs-you": "priority", priority: "priority", activity: "activity", messages: "user_messages" };
|
|
49051
|
+
function printInboxHelp() {
|
|
49052
|
+
process.stderr.write(`
|
|
49053
|
+
tfa-cli inbox — the user's notification feed (the bell)
|
|
49054
|
+
|
|
49055
|
+
Usage:
|
|
49056
|
+
tfa-cli inbox [needs-you|messages|activity] [--unread] [--limit 20] [--json]
|
|
49057
|
+
tfa-cli inbox seen [needs-you|messages|activity] Mark read (omit = everything)
|
|
49058
|
+
|
|
49059
|
+
Filters (same as the bell's chips):
|
|
49060
|
+
needs-you invites, offers, a todo waiting on the user, a failed todo (alias: priority)
|
|
49061
|
+
messages notes from the TODO for AI team or from an agent
|
|
49062
|
+
activity finished / recommended todos
|
|
49063
|
+
`);
|
|
49064
|
+
}
|
|
49065
|
+
function printNotifyHelp() {
|
|
49066
|
+
process.stderr.write(`
|
|
49067
|
+
tfa-cli notify — leave a note for YOUR user (their Messages tab + phone push)
|
|
49068
|
+
|
|
49069
|
+
Usage:
|
|
49070
|
+
tfa-cli notify "Title" "message" [--href /t/<todo-id>] [--subject <key>] [--json]
|
|
49071
|
+
echo "message" | tfa-cli notify "Title"
|
|
49072
|
+
|
|
49073
|
+
Recipient: ALWAYS the user this key belongs to (the person you work for). There is no --to:
|
|
49074
|
+
it cannot message a teammate, a customer or any other account. Shown as from your agent.
|
|
49075
|
+
--subject dedupes: the same subject is delivered once (default: hash of title+message,
|
|
49076
|
+
so a retry never double-notifies). --href must be an in-app path. Max 10 new notes/day.
|
|
49077
|
+
`);
|
|
49078
|
+
}
|
|
49079
|
+
function tierArg(name) {
|
|
49080
|
+
if (!name)
|
|
49081
|
+
return;
|
|
49082
|
+
const tier = TIERS[name];
|
|
49083
|
+
if (!tier) {
|
|
49084
|
+
process.stderr.write(`${RED}Unknown tab "${name}" — use needs-you, messages, activity${RESET}
|
|
49085
|
+
`);
|
|
49086
|
+
process.exit(2);
|
|
49087
|
+
}
|
|
49088
|
+
return tier;
|
|
49089
|
+
}
|
|
49090
|
+
var when = (ms) => new Date(ms).toLocaleString(undefined, { dateStyle: "short", timeStyle: "short" });
|
|
49091
|
+
async function inboxCommand(api, positionals, args) {
|
|
49092
|
+
const [, first, second] = positionals;
|
|
49093
|
+
if (first === "seen") {
|
|
49094
|
+
const tier2 = tierArg(second);
|
|
49095
|
+
await api.markFeedSeen(tier2 ? { tier: tier2 } : {});
|
|
49096
|
+
process.stderr.write(`${GREEN}✅ Marked ${second ?? "everything"} read${RESET}
|
|
49097
|
+
`);
|
|
49098
|
+
return;
|
|
49099
|
+
}
|
|
49100
|
+
const tier = tierArg(first);
|
|
49101
|
+
const limit = args.limit ? Number(args.limit) : 20;
|
|
49102
|
+
const page = await api.listFeed({ tier, limit, sinceSeen: !!args.unread || undefined });
|
|
49103
|
+
if (args.json) {
|
|
49104
|
+
console.log(JSON.stringify(page));
|
|
49105
|
+
return;
|
|
49106
|
+
}
|
|
49107
|
+
const counts = page.unseenByTier;
|
|
49108
|
+
if (counts)
|
|
49109
|
+
process.stderr.write(`${DIM}unread — needs you ${counts.priority} · messages ${counts.user_messages} · activity ${counts.activity}${RESET}
|
|
49110
|
+
`);
|
|
49111
|
+
if (!page.items.length) {
|
|
49112
|
+
process.stderr.write(`${DIM}Nothing here${RESET}
|
|
49113
|
+
`);
|
|
49114
|
+
return;
|
|
49115
|
+
}
|
|
49116
|
+
for (const e of page.items) {
|
|
49117
|
+
const from = e.sender ? `${CYAN}${senderLabel(e.sender)}${RESET} ` : "";
|
|
49118
|
+
const dot = e.unread ? `${YELLOW}●${RESET}` : " ";
|
|
49119
|
+
const state = e.resolution ? ` ${DIM}[${e.resolution}]${RESET}` : "";
|
|
49120
|
+
console.log(`${dot} ${DIM}${when(e.createdAt)}${RESET} ${from}${BOLD}${e.title ?? e.message}${RESET}${state}`);
|
|
49121
|
+
if (e.title && e.message)
|
|
49122
|
+
console.log(` ${e.message.replace(/\n/g, `
|
|
49123
|
+
`)}`);
|
|
49124
|
+
if (e.href)
|
|
49125
|
+
console.log(` ${DIM}${e.href}${RESET}`);
|
|
49126
|
+
}
|
|
49127
|
+
}
|
|
49128
|
+
var RECIPIENT_FLAGS = ["--to", "--email", "--user", "--recipient", "--cc"];
|
|
49129
|
+
async function notifyCommand(api, positionals, args) {
|
|
49130
|
+
const argv = process.argv.slice(2);
|
|
49131
|
+
const bad = argv.find((a) => RECIPIENT_FLAGS.some((f) => a === f || a.startsWith(`${f}=`)));
|
|
49132
|
+
if (bad) {
|
|
49133
|
+
process.stderr.write(`${RED}notify has no recipient option (${bad.split("=")[0]}): it only reaches the user this key belongs to — your own user. Messaging other people is not supported.${RESET}
|
|
49134
|
+
`);
|
|
49135
|
+
process.exit(2);
|
|
49136
|
+
}
|
|
49137
|
+
const [, title, ...rest] = positionals;
|
|
49138
|
+
const message = rest.join(" ") || (process.stdin.isTTY ? "" : (await readStdin2()).trim());
|
|
49139
|
+
if (!title || !message) {
|
|
49140
|
+
printNotifyHelp();
|
|
49141
|
+
process.exit(2);
|
|
49142
|
+
}
|
|
49143
|
+
const subject = args.subject || createHash("sha256").update(`${title}
|
|
49144
|
+
${message}`).digest("hex").slice(0, 16);
|
|
49145
|
+
const row = await api.sendFeedMessage({ subject, title, message, ...args.href ? { href: String(args.href) } : {} });
|
|
49146
|
+
if (args.json) {
|
|
49147
|
+
console.log(JSON.stringify(row));
|
|
49148
|
+
return;
|
|
49149
|
+
}
|
|
49150
|
+
process.stderr.write(row.created !== false ? `${GREEN}✅ Delivered to your own user's Messages inbox (not anyone else)${RESET} ${DIM}(subject ${subject})${RESET}
|
|
49151
|
+
` : `${DIM}Already sent (subject ${subject}) — nothing new delivered${RESET}
|
|
49152
|
+
`);
|
|
49153
|
+
}
|
|
49154
|
+
|
|
48572
49155
|
// src/index.ts
|
|
48573
49156
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
48574
49157
|
|
|
@@ -48833,7 +49416,7 @@ async function spawnMayflyBridge(apiUrl, todoId, workspace, opts = {}) {
|
|
|
48833
49416
|
}
|
|
48834
49417
|
});
|
|
48835
49418
|
});
|
|
48836
|
-
return { child, stop };
|
|
49419
|
+
return { child, stop, stopped: () => stopped };
|
|
48837
49420
|
}
|
|
48838
49421
|
|
|
48839
49422
|
// src/acp.ts
|
|
@@ -72898,6 +73481,93 @@ async function runAcp(apiUrl, apiKey, opts) {
|
|
|
72898
73481
|
await agent2.close();
|
|
72899
73482
|
}
|
|
72900
73483
|
|
|
73484
|
+
// src/shutdown.ts
|
|
73485
|
+
var activeRun2 = null;
|
|
73486
|
+
var cleanups2 = [];
|
|
73487
|
+
var exiting2 = null;
|
|
73488
|
+
var cancelling2 = null;
|
|
73489
|
+
var CANCEL_TIMEOUT_MS2 = 5000;
|
|
73490
|
+
function setActiveRun2(run2) {
|
|
73491
|
+
activeRun2 = run2;
|
|
73492
|
+
}
|
|
73493
|
+
function trackStart(ws, projectId, started) {
|
|
73494
|
+
setActiveRun2(started.then((todoId) => ({ ws, projectId, todoId }), () => null));
|
|
73495
|
+
}
|
|
73496
|
+
function onShutdown(fn) {
|
|
73497
|
+
cleanups2.push(fn);
|
|
73498
|
+
}
|
|
73499
|
+
function withTimeout2(p, ms, fallback) {
|
|
73500
|
+
let timer;
|
|
73501
|
+
const timeout = new Promise((r) => {
|
|
73502
|
+
timer = setTimeout(() => r(fallback), ms);
|
|
73503
|
+
});
|
|
73504
|
+
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
73505
|
+
}
|
|
73506
|
+
function cancelActiveRun2(timeoutMs = CANCEL_TIMEOUT_MS2) {
|
|
73507
|
+
if (cancelling2)
|
|
73508
|
+
return cancelling2;
|
|
73509
|
+
const pending = activeRun2;
|
|
73510
|
+
if (!pending)
|
|
73511
|
+
return Promise.resolve({ ok: false, todoId: null });
|
|
73512
|
+
const res = { ok: false };
|
|
73513
|
+
const attempt = (async () => {
|
|
73514
|
+
const run2 = await pending;
|
|
73515
|
+
res.todoId = run2?.todoId ?? null;
|
|
73516
|
+
if (!run2)
|
|
73517
|
+
return res;
|
|
73518
|
+
if (!run2.ws.connected && !await run2.ws.connect())
|
|
73519
|
+
return res;
|
|
73520
|
+
res.ok = await run2.ws.sendInterrupt(run2.projectId, run2.todoId);
|
|
73521
|
+
return res;
|
|
73522
|
+
})().catch(() => res);
|
|
73523
|
+
cancelling2 = withTimeout2(attempt, timeoutMs, res).finally(() => {
|
|
73524
|
+
if (activeRun2 === pending)
|
|
73525
|
+
activeRun2 = null;
|
|
73526
|
+
cancelling2 = null;
|
|
73527
|
+
});
|
|
73528
|
+
return cancelling2;
|
|
73529
|
+
}
|
|
73530
|
+
function shutdown2(code, reason) {
|
|
73531
|
+
return exiting2 ??= (async () => {
|
|
73532
|
+
if (reason)
|
|
73533
|
+
process.stderr.write(`
|
|
73534
|
+
${reason}
|
|
73535
|
+
`);
|
|
73536
|
+
if (activeRun2) {
|
|
73537
|
+
const { ok, todoId } = await cancelActiveRun2();
|
|
73538
|
+
if (ok)
|
|
73539
|
+
process.stderr.write(`Stopped todo ${todoId}
|
|
73540
|
+
`);
|
|
73541
|
+
else if (todoId)
|
|
73542
|
+
process.stderr.write(`Warning: could not stop todo ${todoId} (backend unreachable)
|
|
73543
|
+
`);
|
|
73544
|
+
else if (todoId === undefined)
|
|
73545
|
+
process.stderr.write(`Warning: could not stop the todo being created (timed out)
|
|
73546
|
+
`);
|
|
73547
|
+
}
|
|
73548
|
+
for (const fn of cleanups2.splice(0)) {
|
|
73549
|
+
try {
|
|
73550
|
+
fn();
|
|
73551
|
+
} catch {}
|
|
73552
|
+
}
|
|
73553
|
+
process.exit(code);
|
|
73554
|
+
})();
|
|
73555
|
+
}
|
|
73556
|
+
function finish(code) {
|
|
73557
|
+
if (exiting2)
|
|
73558
|
+
return;
|
|
73559
|
+
process.exitCode = code;
|
|
73560
|
+
process.stdout.end(() => {
|
|
73561
|
+
if (!exiting2)
|
|
73562
|
+
process.exit(code);
|
|
73563
|
+
});
|
|
73564
|
+
}
|
|
73565
|
+
function installSignalHandlers() {
|
|
73566
|
+
process.on("SIGINT", () => void shutdown2(130, "Cancelled by user (Ctrl+C)"));
|
|
73567
|
+
process.on("SIGTERM", () => void shutdown2(143, "Terminated (SIGTERM)"));
|
|
73568
|
+
process.on("SIGHUP", () => void shutdown2(129, "Hangup (SIGHUP)"));
|
|
73569
|
+
}
|
|
73570
|
+
|
|
72901
73571
|
// src/index.ts
|
|
72902
73572
|
try {
|
|
72903
73573
|
const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
|
|
@@ -72973,7 +73643,7 @@ async function interactiveLoop(ws, api2, todoId, projectId, agent2, json2, autoA
|
|
|
72973
73643
|
cfg.addToHistory(input2);
|
|
72974
73644
|
process.stderr.write("\u2500".repeat(40) + `
|
|
72975
73645
|
`);
|
|
72976
|
-
await api2
|
|
73646
|
+
await appendTracked(api2, ws, projectId, input2, agent2, todoId);
|
|
72977
73647
|
await watchTodo(ws, todoId, projectId, {
|
|
72978
73648
|
json: json2,
|
|
72979
73649
|
autoApprove,
|
|
@@ -72984,6 +73654,11 @@ async function interactiveLoop(ws, api2, todoId, projectId, agent2, json2, autoA
|
|
|
72984
73654
|
}
|
|
72985
73655
|
}
|
|
72986
73656
|
}
|
|
73657
|
+
async function appendTracked(api2, ws, projectId, text, agent2, todoId) {
|
|
73658
|
+
const sent = api2.addMessage(projectId, text, agent2, todoId).then(() => todoId);
|
|
73659
|
+
trackStart(ws, projectId, sent);
|
|
73660
|
+
await sent;
|
|
73661
|
+
}
|
|
72987
73662
|
async function linkToSpawningBlock(api2, todoId) {
|
|
72988
73663
|
const { TODOFORAI_TODO_ID: pTodo, TODOFORAI_MESSAGE_ID: pMsg, TODOFORAI_BLOCK_ID: pBlock } = process.env;
|
|
72989
73664
|
if (!pTodo || !pMsg || !pBlock || pTodo === todoId)
|
|
@@ -72992,12 +73667,7 @@ async function linkToSpawningBlock(api2, todoId) {
|
|
|
72992
73667
|
`));
|
|
72993
73668
|
}
|
|
72994
73669
|
async function main() {
|
|
72995
|
-
|
|
72996
|
-
process.stderr.write(`
|
|
72997
|
-
Cancelled by user (Ctrl+C)
|
|
72998
|
-
`);
|
|
72999
|
-
process.exit(130);
|
|
73000
|
-
});
|
|
73670
|
+
installSignalHandlers();
|
|
73001
73671
|
const { values: args, positionals } = parseCliArgs();
|
|
73002
73672
|
const groupTag = args.group === undefined ? getEnv("GROUP_ID") : String(args.group);
|
|
73003
73673
|
const groupName = args["group-name"] === undefined ? undefined : String(args["group-name"]);
|
|
@@ -73042,11 +73712,19 @@ Cancelled by user (Ctrl+C)
|
|
|
73042
73712
|
printDeviceHelp();
|
|
73043
73713
|
process.exit(0);
|
|
73044
73714
|
}
|
|
73715
|
+
if (positionals[0] === "inbox" && args.help) {
|
|
73716
|
+
printInboxHelp();
|
|
73717
|
+
process.exit(0);
|
|
73718
|
+
}
|
|
73719
|
+
if (positionals[0] === "notify" && args.help) {
|
|
73720
|
+
printNotifyHelp();
|
|
73721
|
+
process.exit(0);
|
|
73722
|
+
}
|
|
73045
73723
|
if ((positionals[0] === "list" || positionals[0] === "ls") && args.help) {
|
|
73046
73724
|
printListTodosHelp();
|
|
73047
73725
|
process.exit(0);
|
|
73048
73726
|
}
|
|
73049
|
-
if (args.help && !["list", "ls", "agent", "todo", "project", "brand", "device"].includes(positionals[0])) {
|
|
73727
|
+
if (args.help && !["list", "ls", "agent", "todo", "project", "brand", "device", "inbox", "notify"].includes(positionals[0])) {
|
|
73050
73728
|
printUsage();
|
|
73051
73729
|
process.exit(0);
|
|
73052
73730
|
}
|
|
@@ -73177,6 +73855,14 @@ Cancelled by user (Ctrl+C)
|
|
|
73177
73855
|
await deviceCommand(api2, positionals, args);
|
|
73178
73856
|
return;
|
|
73179
73857
|
}
|
|
73858
|
+
if (positionals[0] === "inbox") {
|
|
73859
|
+
await inboxCommand(api2, positionals, args);
|
|
73860
|
+
return;
|
|
73861
|
+
}
|
|
73862
|
+
if (positionals[0] === "notify") {
|
|
73863
|
+
await notifyCommand(api2, positionals, args);
|
|
73864
|
+
return;
|
|
73865
|
+
}
|
|
73180
73866
|
if (positionals[0] === "delete") {
|
|
73181
73867
|
const todoId = positionals[1];
|
|
73182
73868
|
if (!todoId) {
|
|
@@ -73533,6 +74219,7 @@ Cancelled by user (Ctrl+C)
|
|
|
73533
74219
|
}
|
|
73534
74220
|
if (!args["no-watch"]) {
|
|
73535
74221
|
const ws2 = new FrontendWebSocket(apiUrl, apiKey);
|
|
74222
|
+
setActiveRun2({ ws: ws2, projectId: projectId2, todoId });
|
|
73536
74223
|
await ws2.connect();
|
|
73537
74224
|
const autoApprove = !!args["dangerously-skip-permissions"];
|
|
73538
74225
|
let agent3 = todo2.agentSettings || { id: todo2.agentSettingsId };
|
|
@@ -73543,7 +74230,8 @@ Cancelled by user (Ctrl+C)
|
|
|
73543
74230
|
await watchTodo(ws2, todoId, projectId2, {
|
|
73544
74231
|
json: !!args.json,
|
|
73545
74232
|
autoApprove,
|
|
73546
|
-
agentSettings: agent3
|
|
74233
|
+
agentSettings: agent3,
|
|
74234
|
+
exitOnInterrupt: !!args["non-interactive"]
|
|
73547
74235
|
});
|
|
73548
74236
|
if (!args["non-interactive"]) {
|
|
73549
74237
|
process.stderr.write(`
|
|
@@ -73592,9 +74280,9 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
73592
74280
|
const followUp = promptArg(positionals) ?? (process.stdin.isTTY ? "" : await readStdin());
|
|
73593
74281
|
if (followUp) {
|
|
73594
74282
|
cfg.addToHistory(followUp);
|
|
73595
|
-
await api2
|
|
74283
|
+
await appendTracked(api2, ws2, projectId2, followUp, agent3, todoId);
|
|
73596
74284
|
await linkToSpawningBlock(api2, todoId);
|
|
73597
|
-
await watchTodo(ws2, todoId, projectId2, { json: !!args.json, autoApprove, agentSettings: agent3 });
|
|
74285
|
+
await watchTodo(ws2, todoId, projectId2, { json: !!args.json, autoApprove, agentSettings: agent3, exitOnInterrupt: !!args["non-interactive"] });
|
|
73598
74286
|
}
|
|
73599
74287
|
if (!args["non-interactive"]) {
|
|
73600
74288
|
await interactiveLoop(ws2, api2, todoId, projectId2, agent3, !!args.json, autoApprove, cfg);
|
|
@@ -73613,9 +74301,15 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
73613
74301
|
const bridgeReady = !args["no-bridge"] && !args["no-watch"] && !args.isolated ? ensureBridgeRunning2(apiUrl, apiKey) : null;
|
|
73614
74302
|
const isolatedTodoId = args.isolated ? randomUUID3() : undefined;
|
|
73615
74303
|
const mayfly = isolatedTodoId ? await spawnMayflyBridge(apiUrl, isolatedTodoId, realpathSync2(resolve4(args.path || ".")), { debug: !!args.debug, apiKey }) : null;
|
|
73616
|
-
if (mayfly)
|
|
74304
|
+
if (mayfly) {
|
|
73617
74305
|
process.stderr.write(`${DIM2}Isolated bridge:${RESET2} ${CYAN2}mayfly-${isolatedTodoId}${RESET2}
|
|
73618
74306
|
`);
|
|
74307
|
+
onShutdown(mayfly.stop);
|
|
74308
|
+
mayfly.child.on("exit", (code, sig) => {
|
|
74309
|
+
if (!mayfly.stopped())
|
|
74310
|
+
shutdown2(1, `${RED3}Error: isolated bridge died (${sig ?? `code ${code}`}) \u2014 stopping todo${RESET2}`);
|
|
74311
|
+
});
|
|
74312
|
+
}
|
|
73619
74313
|
let preMatchedAgent = null;
|
|
73620
74314
|
let agents = null;
|
|
73621
74315
|
if (args.agent) {
|
|
@@ -73719,15 +74413,16 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
73719
74413
|
agent2 = { ...agent2, permissions: { ...perms, allow: [...perms.allow || [], "*:*"] } };
|
|
73720
74414
|
}
|
|
73721
74415
|
cfg.addToHistory(content);
|
|
74416
|
+
const created = api2.addMessage(projectId, content, agent2, isolatedTodoId, undefined, undefined, groupTag || undefined, groupName);
|
|
74417
|
+
if (ws)
|
|
74418
|
+
trackStart(ws, projectId, created.then((t) => t.id));
|
|
73722
74419
|
let todo;
|
|
73723
74420
|
try {
|
|
73724
|
-
todo = await
|
|
74421
|
+
todo = await created;
|
|
73725
74422
|
} catch (e) {
|
|
73726
74423
|
if (!args.project && !envProjectId && cfgScope.data.default_project_id === projectId && /failed: 403/.test(e.message || "")) {
|
|
73727
74424
|
cfgScope.clearDefaultProject();
|
|
73728
|
-
|
|
73729
|
-
`);
|
|
73730
|
-
process.exit(1);
|
|
74425
|
+
throw new Error(`Not authorized for cached default project ${projectName} (${projectId}) \u2014 cleared it. Re-run to pick a project.`);
|
|
73731
74426
|
}
|
|
73732
74427
|
throw e;
|
|
73733
74428
|
}
|
|
@@ -73748,7 +74443,8 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
73748
74443
|
await watchTodo(ws, actualTodoId, projectId, {
|
|
73749
74444
|
json: !!args.json,
|
|
73750
74445
|
autoApprove,
|
|
73751
|
-
agentSettings: agent2
|
|
74446
|
+
agentSettings: agent2,
|
|
74447
|
+
exitOnInterrupt: !!args["non-interactive"]
|
|
73752
74448
|
});
|
|
73753
74449
|
if (!args["non-interactive"]) {
|
|
73754
74450
|
process.stderr.write(`
|
|
@@ -73760,10 +74456,6 @@ ${"\u2500".repeat(40)}
|
|
|
73760
74456
|
}
|
|
73761
74457
|
mayfly?.stop();
|
|
73762
74458
|
}
|
|
73763
|
-
var finish = (code) => {
|
|
73764
|
-
process.exitCode = code;
|
|
73765
|
-
process.stdout.end(() => process.exit(code));
|
|
73766
|
-
};
|
|
73767
74459
|
main().then(() => finish(process.exitCode ?? 0)).catch((e) => {
|
|
73768
74460
|
process.stderr.write(`Error: ${e.message}
|
|
73769
74461
|
`);
|