@todoforai/cli 0.1.53 → 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 +779 -89
- 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
|
}
|
|
@@ -42875,6 +42875,9 @@ class ApiClient {
|
|
|
42875
42875
|
setDefaultProject(projectId) {
|
|
42876
42876
|
return this.request("POST", "/api/v1/users/default-project", { projectId });
|
|
42877
42877
|
}
|
|
42878
|
+
updateProjectSettings(projectId, settings) {
|
|
42879
|
+
return this.request("PUT", `/api/v1/settings/projects/${projectId}`, { projectId, settings });
|
|
42880
|
+
}
|
|
42878
42881
|
setProjectDefaultAgent(projectId, agentSettingsId) {
|
|
42879
42882
|
return this.request("POST", "/api/v1/users/project-agent-mapping", { projectId, agentSettingsId });
|
|
42880
42883
|
}
|
|
@@ -42923,6 +42926,19 @@ class ApiClient {
|
|
|
42923
42926
|
upsertProjectGroup(projectId, slug, fields) {
|
|
42924
42927
|
return this.request("PUT", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(slug)}`, { projectId, slug, ...fields });
|
|
42925
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
|
+
}
|
|
42926
42942
|
listProjectSurfaces(projectId) {
|
|
42927
42943
|
return this.request("GET", `/api/v1/projects/${projectId}/surfaces`);
|
|
42928
42944
|
}
|
|
@@ -43153,11 +43169,11 @@ class ApiClient {
|
|
|
43153
43169
|
return res.json();
|
|
43154
43170
|
}
|
|
43155
43171
|
}
|
|
43156
|
-
//
|
|
43172
|
+
// ../../../home/hm/repo/todoforai/packages/shared-api/src/frontend-ws.ts
|
|
43157
43173
|
import { randomUUID } from "crypto";
|
|
43158
43174
|
import WebSocket from "ws";
|
|
43159
43175
|
|
|
43160
|
-
//
|
|
43176
|
+
// ../../../home/hm/repo/todoforai/packages/shared-credentials/src/index.ts
|
|
43161
43177
|
import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
|
|
43162
43178
|
import { join, dirname } from "node:path";
|
|
43163
43179
|
import { homedir, platform } from "node:os";
|
|
@@ -43197,13 +43213,14 @@ function writeCredential(apiUrl, apiKey) {
|
|
|
43197
43213
|
writeSecure(c);
|
|
43198
43214
|
}
|
|
43199
43215
|
|
|
43200
|
-
//
|
|
43216
|
+
// ../../../home/hm/repo/todoforai/packages/shared-api/src/frontend-ws.ts
|
|
43201
43217
|
var log = (level, ...args) => console.log(`[frontend-ws:${level}]`, ...args);
|
|
43202
43218
|
|
|
43203
43219
|
class FrontendWebSocket {
|
|
43204
43220
|
apiUrl;
|
|
43205
43221
|
apiKey;
|
|
43206
43222
|
ws = null;
|
|
43223
|
+
connecting = null;
|
|
43207
43224
|
tabId = randomUUID();
|
|
43208
43225
|
connected = false;
|
|
43209
43226
|
callbacks = new Map;
|
|
@@ -43227,28 +43244,61 @@ class FrontendWebSocket {
|
|
|
43227
43244
|
wsUrl = `wss://${url}`;
|
|
43228
43245
|
return `${wsUrl}/ws/v1/frontend?tabId=${this.tabId}`;
|
|
43229
43246
|
}
|
|
43230
|
-
|
|
43247
|
+
connect() {
|
|
43231
43248
|
if (this.connected && this.ws)
|
|
43232
|
-
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() {
|
|
43233
43256
|
return new Promise((resolve) => {
|
|
43234
43257
|
const wsUrl = this.getWsUrl();
|
|
43235
|
-
|
|
43236
|
-
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;
|
|
43237
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?.();
|
|
43238
43281
|
resolve(true);
|
|
43239
43282
|
});
|
|
43240
|
-
|
|
43283
|
+
sock.on("message", (data) => {
|
|
43284
|
+
if (!current())
|
|
43285
|
+
return;
|
|
43241
43286
|
try {
|
|
43242
43287
|
this.handleMessage(JSON.parse(data.toString()));
|
|
43243
43288
|
} catch {}
|
|
43244
43289
|
});
|
|
43245
|
-
|
|
43290
|
+
sock.on("close", () => {
|
|
43291
|
+
clearInterval(heartbeat);
|
|
43292
|
+
if (!current())
|
|
43293
|
+
return;
|
|
43246
43294
|
this.connected = false;
|
|
43247
43295
|
this.settleAll();
|
|
43296
|
+
resolve(false);
|
|
43248
43297
|
});
|
|
43249
|
-
|
|
43298
|
+
sock.on("error", (err) => {
|
|
43250
43299
|
log("error", "WS error:", err.message);
|
|
43251
|
-
|
|
43300
|
+
if (current())
|
|
43301
|
+
this.connected = false;
|
|
43252
43302
|
resolve(false);
|
|
43253
43303
|
});
|
|
43254
43304
|
});
|
|
@@ -43317,7 +43367,7 @@ class FrontendWebSocket {
|
|
|
43317
43367
|
}
|
|
43318
43368
|
}
|
|
43319
43369
|
}
|
|
43320
|
-
async subscribe(todoId, callback) {
|
|
43370
|
+
async subscribe(todoId, callback, opts = {}) {
|
|
43321
43371
|
if (!this.connected && !await this.connect())
|
|
43322
43372
|
return false;
|
|
43323
43373
|
if (callback)
|
|
@@ -43325,8 +43375,16 @@ class FrontendWebSocket {
|
|
|
43325
43375
|
const res = await fetch(`${this.apiUrl}/api/v1/todos/${todoId}/subscribe`, {
|
|
43326
43376
|
method: "POST",
|
|
43327
43377
|
headers: { "x-api-key": this.apiKey, "x-tab-id": this.tabId, "Content-Type": "application/json" },
|
|
43328
|
-
body: JSON.stringify({ todoId })
|
|
43378
|
+
body: JSON.stringify({ todoId }),
|
|
43379
|
+
signal: AbortSignal.timeout(15000)
|
|
43329
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
|
+
}
|
|
43330
43388
|
return res.ok;
|
|
43331
43389
|
}
|
|
43332
43390
|
setCallback(todoId, callback) {
|
|
@@ -43336,17 +43394,16 @@ class FrontendWebSocket {
|
|
|
43336
43394
|
this.callbacks.delete(todoId);
|
|
43337
43395
|
}
|
|
43338
43396
|
async sendInterrupt(projectId, todoId) {
|
|
43339
|
-
|
|
43397
|
+
const ws = this.ws;
|
|
43398
|
+
if (!this.connected || !ws)
|
|
43340
43399
|
return false;
|
|
43341
|
-
|
|
43342
|
-
|
|
43343
|
-
type: "todo:interrupt_signal",
|
|
43344
|
-
|
|
43345
|
-
|
|
43346
|
-
|
|
43347
|
-
}
|
|
43348
|
-
return false;
|
|
43349
|
-
}
|
|
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
|
+
});
|
|
43350
43407
|
}
|
|
43351
43408
|
async sendQueueCancel(todoId, messageId) {
|
|
43352
43409
|
if (!this.connected || !this.ws)
|
|
@@ -43404,6 +43461,10 @@ class FrontendWebSocket {
|
|
|
43404
43461
|
return this.completion(todoId);
|
|
43405
43462
|
}
|
|
43406
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
|
+
}
|
|
43407
43468
|
const early = this.completionResults.get(todoId);
|
|
43408
43469
|
if (early) {
|
|
43409
43470
|
this.completionResults.delete(todoId);
|
|
@@ -43422,7 +43483,7 @@ class FrontendWebSocket {
|
|
|
43422
43483
|
});
|
|
43423
43484
|
}
|
|
43424
43485
|
}
|
|
43425
|
-
//
|
|
43486
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/enums.ts
|
|
43426
43487
|
var TodoStatus;
|
|
43427
43488
|
((TodoStatus2) => {
|
|
43428
43489
|
TodoStatus2["PROPOSED"] = "PROPOSED";
|
|
@@ -43446,7 +43507,7 @@ var TodoStatus;
|
|
|
43446
43507
|
TodoStatus2["ARCHIVED"] = "ARCHIVED";
|
|
43447
43508
|
TodoStatus2["DELETED"] = "DELETED";
|
|
43448
43509
|
})(TodoStatus ||= {});
|
|
43449
|
-
//
|
|
43510
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/models.ts
|
|
43450
43511
|
var DEFAULT_MODEL = "anthropic:anthropic/claude-opus-5.5";
|
|
43451
43512
|
var MODEL_ALIASES = {
|
|
43452
43513
|
claude: DEFAULT_MODEL
|
|
@@ -43517,7 +43578,7 @@ function qualifiedModelIds(listing, filter) {
|
|
|
43517
43578
|
}
|
|
43518
43579
|
return q ? ids.filter((id) => id.toLowerCase().includes(q)) : ids;
|
|
43519
43580
|
}
|
|
43520
|
-
//
|
|
43581
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/billing.ts
|
|
43521
43582
|
var USAGE_WINDOWS = {
|
|
43522
43583
|
SESSION_MS: 5 * 60 * 60 * 1000,
|
|
43523
43584
|
WEEK_MS: 7 * 24 * 60 * 60 * 1000,
|
|
@@ -43525,7 +43586,7 @@ var USAGE_WINDOWS = {
|
|
|
43525
43586
|
SESSION_FACTOR: 1 / 5
|
|
43526
43587
|
};
|
|
43527
43588
|
var NEVER_SCHEDULED_TIMESTAMP = 8640000000000000;
|
|
43528
|
-
//
|
|
43589
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/mimetypes.json
|
|
43529
43590
|
var mimetypes_default = {
|
|
43530
43591
|
conversionProfiles: {
|
|
43531
43592
|
to_jpeg_90: { toMime: "image/jpeg", toExt: "jpg", quality: 0.9 }
|
|
@@ -43618,7 +43679,7 @@ var mimetypes_default = {
|
|
|
43618
43679
|
}
|
|
43619
43680
|
};
|
|
43620
43681
|
|
|
43621
|
-
//
|
|
43682
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/attachmentUtils.ts
|
|
43622
43683
|
var MCP_TEXT_MIME = Object.entries(mimetypes_default.aliases).find(([, type]) => type === "mcp_text")[0];
|
|
43623
43684
|
var MCP_AUDIO_MIME = Object.entries(mimetypes_default.aliases).find(([, type]) => type === "mcp_audio")[0];
|
|
43624
43685
|
var AttachmentType = Object.fromEntries(Object.keys(mimetypes_default.types).map((k) => [k.toUpperCase(), k]));
|
|
@@ -43658,7 +43719,7 @@ for (const [ext, entry] of Object.entries(mimetypes_default.extensions)) {
|
|
|
43658
43719
|
if (!MIME_TO_EXT[mime])
|
|
43659
43720
|
MIME_TO_EXT[mime] = ext;
|
|
43660
43721
|
}
|
|
43661
|
-
//
|
|
43722
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/edgeTypes.ts
|
|
43662
43723
|
var NON_SHELL_TYPES = new Set([
|
|
43663
43724
|
"WEB_EXTENSION" /* WEB_EXTENSION */,
|
|
43664
43725
|
"EXTENSION" /* EXTENSION */,
|
|
@@ -43666,7 +43727,7 @@ var NON_SHELL_TYPES = new Set([
|
|
|
43666
43727
|
"ANDROID" /* ANDROID */,
|
|
43667
43728
|
"IOS" /* IOS */
|
|
43668
43729
|
]);
|
|
43669
|
-
//
|
|
43730
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/blocks/index.ts
|
|
43670
43731
|
var USER_INPUT_BLOCK_TYPES = new Set([
|
|
43671
43732
|
"question" /* Question */,
|
|
43672
43733
|
"login_prompt" /* Login */,
|
|
@@ -43720,7 +43781,7 @@ var FILE_TOOL_TYPES = new Set([
|
|
|
43720
43781
|
"edit" /* Edit */,
|
|
43721
43782
|
"modify_file" /* ModifyFile */
|
|
43722
43783
|
]);
|
|
43723
|
-
//
|
|
43784
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/channels.ts
|
|
43724
43785
|
var privateSuffix = (privateOwnerId) => privateOwnerId ? `:private:${privateOwnerId}` : "";
|
|
43725
43786
|
var SERVER_TO_FRONTENDS = {
|
|
43726
43787
|
task: {
|
|
@@ -43779,6 +43840,7 @@ var SERVER_TO_FRONTENDS = {
|
|
|
43779
43840
|
msgMetaAi: (todoId) => `${"todo:msg_meta_ai" /* TODO_MSG_META_AI */}:${todoId}`,
|
|
43780
43841
|
msgQueueUpdate: (todoId) => `${"todo:msg_queue_update" /* TODO_MSG_QUEUE_UPDATE */}:${todoId}`,
|
|
43781
43842
|
message_removed: (todoId) => `${"todo:message_removed" /* TODO_MESSAGE_REMOVED */}:${todoId}`,
|
|
43843
|
+
commentUpdate: (todoId) => `${"todo:comment_update" /* TODO_COMMENT_UPDATE */}:${todoId}`,
|
|
43782
43844
|
start_universal: (todoId) => `${"block:start_universal" /* BLOCK_START_UNIVERSAL */}:${todoId}`,
|
|
43783
43845
|
message: (todoId) => `${"block:message" /* BLOCK_MESSAGE */}:${todoId}`,
|
|
43784
43846
|
end: (todoId) => `${"block:end" /* BLOCK_END */}:${todoId}`,
|
|
@@ -43809,7 +43871,7 @@ var SERVER_TO_FRONTENDS = {
|
|
|
43809
43871
|
function_call_request: (userId) => `${"FUNCTION_CALL_REQUEST_AGENT" /* FUNCTION_CALL_REQUEST_AGENT */}:frontend:${userId}`
|
|
43810
43872
|
}
|
|
43811
43873
|
};
|
|
43812
|
-
//
|
|
43874
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/REST_types.ts
|
|
43813
43875
|
var MANUAL_CHANNEL = "Manual";
|
|
43814
43876
|
function manualAnswersOf(sources) {
|
|
43815
43877
|
const manual = sources?.find((s) => s.channel === MANUAL_CHANNEL);
|
|
@@ -43820,7 +43882,8 @@ function manualAnswersOf(sources) {
|
|
|
43820
43882
|
return out;
|
|
43821
43883
|
}
|
|
43822
43884
|
var manualSamples = (answers) => Object.entries(answers).filter(([, a]) => a.trim()).map(([prompt, text]) => ({ prompt, text: text.trim() }));
|
|
43823
|
-
|
|
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
|
|
43824
43887
|
var USER_HEADINGS = new Set([
|
|
43825
43888
|
"your task",
|
|
43826
43889
|
"your tasks",
|
|
@@ -43829,7 +43892,7 @@ var USER_HEADINGS = new Set([
|
|
|
43829
43892
|
"steps",
|
|
43830
43893
|
"data needed"
|
|
43831
43894
|
]);
|
|
43832
|
-
//
|
|
43895
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/bashPatterns.ts
|
|
43833
43896
|
function splitShellCommands(input) {
|
|
43834
43897
|
const parts = [];
|
|
43835
43898
|
let current = "";
|
|
@@ -44071,7 +44134,7 @@ function getBlockServerId(block) {
|
|
|
44071
44134
|
return prefix ? prefix.slice(0, -1) : "*";
|
|
44072
44135
|
}
|
|
44073
44136
|
|
|
44074
|
-
//
|
|
44137
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/permissionUtils.ts
|
|
44075
44138
|
function parsePattern(pattern) {
|
|
44076
44139
|
const colonIndex = pattern.indexOf(":");
|
|
44077
44140
|
if (colonIndex === -1)
|
|
@@ -44172,7 +44235,7 @@ function getBlockNewPatterns(block, permissions) {
|
|
|
44172
44235
|
}
|
|
44173
44236
|
return pairs.filter(({ raw, pattern }) => !isPatternAllowed(permissions, pattern) && !allow.some((rule) => bashRuleMatchesCmd(rule, serverId, raw))).map(({ pattern }) => pattern);
|
|
44174
44237
|
}
|
|
44175
|
-
//
|
|
44238
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/systemAgents.ts
|
|
44176
44239
|
var BUSINESS_ANALYZER_AGENT = {
|
|
44177
44240
|
id: "00000000-0000-0000-0000-202601013151",
|
|
44178
44241
|
name: "Business Analyzer",
|
|
@@ -44318,7 +44381,7 @@ var SYSTEM_AGENTS = {
|
|
|
44318
44381
|
[ONBOARDING_AGENT.id]: ONBOARDING_AGENT
|
|
44319
44382
|
};
|
|
44320
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.";
|
|
44321
|
-
//
|
|
44384
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/outputLimits.ts
|
|
44322
44385
|
var MAX_LINE_LEN = 300;
|
|
44323
44386
|
var STREAM_FIRST = 1e4;
|
|
44324
44387
|
var STREAM_LAST = 1e4;
|
|
@@ -44329,7 +44392,7 @@ var OUTPUT_POLICIES = {
|
|
|
44329
44392
|
full: { firstLimit: Infinity, lastLimit: 0, hardCap: RUN_OUTPUT_CAP, lineLimit: MAX_LINE_LEN },
|
|
44330
44393
|
raw: { firstLimit: Infinity, lastLimit: 0, hardCap: Infinity, lineLimit: Infinity }
|
|
44331
44394
|
};
|
|
44332
|
-
//
|
|
44395
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/model_promos.json
|
|
44333
44396
|
var model_promos_default = {
|
|
44334
44397
|
_doc: [
|
|
44335
44398
|
"Single source of truth for TODOforAI promotional model discounts.",
|
|
@@ -44371,10 +44434,10 @@ var model_promos_default = {
|
|
|
44371
44434
|
]
|
|
44372
44435
|
};
|
|
44373
44436
|
|
|
44374
|
-
//
|
|
44437
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/modelPromos.ts
|
|
44375
44438
|
var BOOST_FACTOR = model_promos_default.boost_remaining_price_factor;
|
|
44376
44439
|
var MODEL_PROMOS = model_promos_default.promos;
|
|
44377
|
-
//
|
|
44440
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/realtime/topics.ts
|
|
44378
44441
|
var TOPICS = {
|
|
44379
44442
|
["todo:new" /* NEW_TODO */]: {
|
|
44380
44443
|
channel: (p) => SERVER_TO_FRONTENDS.project.new_todo(p.projectId, p.privateOwnerId),
|
|
@@ -44480,6 +44543,10 @@ var TOPICS = {
|
|
|
44480
44543
|
channel: (p) => SERVER_TO_FRONTENDS.todo.message_removed(p.todoId),
|
|
44481
44544
|
audience: "frontend"
|
|
44482
44545
|
},
|
|
44546
|
+
["todo:comment_update" /* TODO_COMMENT_UPDATE */]: {
|
|
44547
|
+
channel: (p) => SERVER_TO_FRONTENDS.todo.commentUpdate(p.todoId),
|
|
44548
|
+
audience: "frontend"
|
|
44549
|
+
},
|
|
44483
44550
|
["todo:msg_meta_usr" /* TODO_MSG_META_USR */]: {
|
|
44484
44551
|
channel: (p) => SERVER_TO_FRONTENDS.todo.msgMetaUsr(p.todoId),
|
|
44485
44552
|
audience: "frontend"
|
|
@@ -44537,12 +44604,12 @@ var TOPICS = {
|
|
|
44537
44604
|
audience: "frontend"
|
|
44538
44605
|
}
|
|
44539
44606
|
};
|
|
44540
|
-
//
|
|
44607
|
+
// ../../../home/hm/repo/todoforai/packages/shared-fbe/src/modelNames.ts
|
|
44541
44608
|
var LEGACY_MODEL_MAPPING = {
|
|
44542
44609
|
claude: DEFAULT_MODEL.slice(DEFAULT_MODEL.indexOf(":") + 1),
|
|
44543
44610
|
"gemini-pro": "google/gemini-3.1-pro-preview"
|
|
44544
44611
|
};
|
|
44545
|
-
//
|
|
44612
|
+
// ../../../home/hm/repo/todoforai/packages/shared-credentials/src/index.ts
|
|
44546
44613
|
function normalizeApiUrl2(url) {
|
|
44547
44614
|
if (url.startsWith("localhost"))
|
|
44548
44615
|
return `http://${url}`;
|
|
@@ -44556,7 +44623,7 @@ import { parseArgs } from "util";
|
|
|
44556
44623
|
// package.json
|
|
44557
44624
|
var package_default = {
|
|
44558
44625
|
name: "@todoforai/cli",
|
|
44559
|
-
version: "0.1.
|
|
44626
|
+
version: "0.1.55",
|
|
44560
44627
|
type: "module",
|
|
44561
44628
|
bin: {
|
|
44562
44629
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -44624,6 +44691,9 @@ Examples:
|
|
|
44624
44691
|
tfa-cli show list|rm … # List / take down shown files
|
|
44625
44692
|
tfa-cli open <url> [todo-id] # Live url preview in the chat
|
|
44626
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
|
|
44627
44697
|
tfa-cli claim mint --seed <projectId> [--emails a@x,b@y] [--ttl <sec>] # Mint /claim/<token> links for a project you own
|
|
44628
44698
|
tfa-cli next [--direction "<text>"] # Analyzer growth recommendation cards
|
|
44629
44699
|
tfa-cli <command> help # Details per command
|
|
@@ -44704,7 +44774,7 @@ var WORD_FLAGS = {
|
|
|
44704
44774
|
agents: "list-agents",
|
|
44705
44775
|
config: "show-config"
|
|
44706
44776
|
};
|
|
44707
|
-
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"];
|
|
44708
44778
|
function parseCliArgs() {
|
|
44709
44779
|
const { values, positionals } = parseArgs({
|
|
44710
44780
|
args: process.argv.slice(2),
|
|
@@ -44739,6 +44809,10 @@ function parseCliArgs() {
|
|
|
44739
44809
|
seed: { type: "string" },
|
|
44740
44810
|
emails: { type: "string" },
|
|
44741
44811
|
ttl: { type: "string" },
|
|
44812
|
+
subject: { type: "string" },
|
|
44813
|
+
href: { type: "string" },
|
|
44814
|
+
unread: { type: "boolean", default: false },
|
|
44815
|
+
limit: { type: "string" },
|
|
44742
44816
|
resume: { type: "string", short: "r" },
|
|
44743
44817
|
continue: { type: "boolean", short: "c", default: false },
|
|
44744
44818
|
"non-interactive": { type: "boolean", short: "n", default: false },
|
|
@@ -46996,7 +47070,88 @@ function renderDiff(originalContent, modifiedContent, filePath) {
|
|
|
46996
47070
|
return parts.join("");
|
|
46997
47071
|
}
|
|
46998
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
|
+
|
|
46999
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
|
+
}
|
|
47000
47155
|
var diffStoreByWs = new WeakMap;
|
|
47001
47156
|
function classifyBlock(info) {
|
|
47002
47157
|
const inner = (info.block_type || "").toLowerCase();
|
|
@@ -47078,12 +47233,8 @@ async function watchTodo(ws, todoId, projectId, opts = {}) {
|
|
|
47078
47233
|
process.removeAllListeners("SIGINT");
|
|
47079
47234
|
process.on("SIGINT", () => {
|
|
47080
47235
|
interruptCount++;
|
|
47081
|
-
if (interruptCount >= 2)
|
|
47082
|
-
|
|
47083
|
-
${RED}Force exit (double Ctrl+C)${RESET}
|
|
47084
|
-
`);
|
|
47085
|
-
process.exit(130);
|
|
47086
|
-
}
|
|
47236
|
+
if (opts.exitOnInterrupt || interruptCount >= 2)
|
|
47237
|
+
return void shutdown(130, "Cancelled by user (Ctrl+C)");
|
|
47087
47238
|
process.stderr.write(`
|
|
47088
47239
|
${YELLOW}Interrupting... (Ctrl+C again to force exit)${RESET}
|
|
47089
47240
|
`);
|
|
@@ -47282,8 +47433,22 @@ ${DIM}[todo:status] ${status}${RESET}
|
|
|
47282
47433
|
callback(msgType, payload);
|
|
47283
47434
|
}
|
|
47284
47435
|
}
|
|
47436
|
+
setActiveRun({ ws, projectId, todoId });
|
|
47285
47437
|
try {
|
|
47286
|
-
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);
|
|
47287
47452
|
process.stdout.write(`
|
|
47288
47453
|
`);
|
|
47289
47454
|
if (!result?.success) {
|
|
@@ -47296,6 +47461,8 @@ ${DIM}[todo:status] ${status}${RESET}
|
|
|
47296
47461
|
}
|
|
47297
47462
|
return true;
|
|
47298
47463
|
} catch (e) {
|
|
47464
|
+
await cancelActiveRun();
|
|
47465
|
+
process.exitCode = 1;
|
|
47299
47466
|
if (!opts.suppressCancelNotice) {
|
|
47300
47467
|
process.stderr.write(`${YELLOW}Interrupted${RESET}
|
|
47301
47468
|
`);
|
|
@@ -47860,7 +48027,7 @@ function printProjectHelp() {
|
|
|
47860
48027
|
process.stderr.write(`
|
|
47861
48028
|
tfa-cli project — edit the current project
|
|
47862
48029
|
|
|
47863
|
-
--project <id
|
|
48030
|
+
--project <id>, $TODOFORAI_PROJECT_ID, or the configured default selects the project.
|
|
47864
48031
|
|
|
47865
48032
|
Usage:
|
|
47866
48033
|
tfa-cli project list Projects you can access (* = default)
|
|
@@ -47893,6 +48060,7 @@ var TODO_ALIASES = {
|
|
|
47893
48060
|
agent: "agentSettingsId",
|
|
47894
48061
|
public: "isPublic"
|
|
47895
48062
|
};
|
|
48063
|
+
var NO_PROJECT = "No project — pass --project <id> or set TODOFORAI_PROJECT_ID";
|
|
47896
48064
|
function fail(msg) {
|
|
47897
48065
|
process.stderr.write(`${RED}${msg}${RESET}
|
|
47898
48066
|
`);
|
|
@@ -47943,9 +48111,8 @@ async function todoCommand(api, positionals, args) {
|
|
|
47943
48111
|
process.stderr.write(`${GREEN}✅ ${todoId} updated: ${Object.keys(updates).join(", ")}${RESET}
|
|
47944
48112
|
`);
|
|
47945
48113
|
}
|
|
47946
|
-
async function projectCommand(api, positionals, args) {
|
|
48114
|
+
async function projectCommand(api, positionals, args, projectId) {
|
|
47947
48115
|
const [, sub, ...rest] = positionals;
|
|
47948
|
-
const projectId = args.project || getEnv2("PROJECT_ID");
|
|
47949
48116
|
if (!sub) {
|
|
47950
48117
|
printProjectHelp();
|
|
47951
48118
|
process.exit(0);
|
|
@@ -47964,7 +48131,7 @@ async function projectCommand(api, positionals, args) {
|
|
|
47964
48131
|
return;
|
|
47965
48132
|
}
|
|
47966
48133
|
if (!projectId)
|
|
47967
|
-
fail(
|
|
48134
|
+
fail(NO_PROJECT);
|
|
47968
48135
|
if (sub === "set") {
|
|
47969
48136
|
if (!rest.length)
|
|
47970
48137
|
fail("Usage: tfa-cli project set <field=value>…");
|
|
@@ -48077,7 +48244,7 @@ function printBrandHelp() {
|
|
|
48077
48244
|
process.stderr.write(`
|
|
48078
48245
|
tfa-cli brand — the project's brand page and its learned writing voice
|
|
48079
48246
|
|
|
48080
|
-
The project IS the brand. --project <id
|
|
48247
|
+
The project IS the brand. --project <id>, $TODOFORAI_PROJECT_ID, or the configured default selects it.
|
|
48081
48248
|
The brand page text itself is a file: read/write todoforai:business-context.
|
|
48082
48249
|
|
|
48083
48250
|
Usage:
|
|
@@ -48107,15 +48274,9 @@ Questions the UI asks (keys for 'answers'):
|
|
|
48107
48274
|
"A frustrated customer says things aren't working. How do you reply?"
|
|
48108
48275
|
`);
|
|
48109
48276
|
}
|
|
48110
|
-
function
|
|
48111
|
-
const projectId = args.project || getEnv2("PROJECT_ID");
|
|
48112
|
-
if (!projectId)
|
|
48113
|
-
fail("No project — pass --project <id> or set TODOFORAI_PROJECT_ID");
|
|
48114
|
-
return projectId;
|
|
48115
|
-
}
|
|
48116
|
-
async function brandCommand(api, positionals, args) {
|
|
48277
|
+
async function brandCommand(api, positionals, args, scoped) {
|
|
48117
48278
|
const [, sub, ...rest] = positionals;
|
|
48118
|
-
const projectId =
|
|
48279
|
+
const projectId = scoped || fail(NO_PROJECT);
|
|
48119
48280
|
if (!sub || sub === "show") {
|
|
48120
48281
|
const brand = await api.getBusinessContext(projectId);
|
|
48121
48282
|
if (args.json) {
|
|
@@ -48572,6 +48733,425 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
48572
48733
|
`);
|
|
48573
48734
|
}
|
|
48574
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
|
+
|
|
48575
49155
|
// src/index.ts
|
|
48576
49156
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
48577
49157
|
|
|
@@ -48836,7 +49416,7 @@ async function spawnMayflyBridge(apiUrl, todoId, workspace, opts = {}) {
|
|
|
48836
49416
|
}
|
|
48837
49417
|
});
|
|
48838
49418
|
});
|
|
48839
|
-
return { child, stop };
|
|
49419
|
+
return { child, stop, stopped: () => stopped };
|
|
48840
49420
|
}
|
|
48841
49421
|
|
|
48842
49422
|
// src/acp.ts
|
|
@@ -72901,6 +73481,93 @@ async function runAcp(apiUrl, apiKey, opts) {
|
|
|
72901
73481
|
await agent2.close();
|
|
72902
73482
|
}
|
|
72903
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
|
+
|
|
72904
73571
|
// src/index.ts
|
|
72905
73572
|
try {
|
|
72906
73573
|
const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
|
|
@@ -72976,7 +73643,7 @@ async function interactiveLoop(ws, api2, todoId, projectId, agent2, json2, autoA
|
|
|
72976
73643
|
cfg.addToHistory(input2);
|
|
72977
73644
|
process.stderr.write("\u2500".repeat(40) + `
|
|
72978
73645
|
`);
|
|
72979
|
-
await api2
|
|
73646
|
+
await appendTracked(api2, ws, projectId, input2, agent2, todoId);
|
|
72980
73647
|
await watchTodo(ws, todoId, projectId, {
|
|
72981
73648
|
json: json2,
|
|
72982
73649
|
autoApprove,
|
|
@@ -72987,6 +73654,11 @@ async function interactiveLoop(ws, api2, todoId, projectId, agent2, json2, autoA
|
|
|
72987
73654
|
}
|
|
72988
73655
|
}
|
|
72989
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
|
+
}
|
|
72990
73662
|
async function linkToSpawningBlock(api2, todoId) {
|
|
72991
73663
|
const { TODOFORAI_TODO_ID: pTodo, TODOFORAI_MESSAGE_ID: pMsg, TODOFORAI_BLOCK_ID: pBlock } = process.env;
|
|
72992
73664
|
if (!pTodo || !pMsg || !pBlock || pTodo === todoId)
|
|
@@ -72995,12 +73667,7 @@ async function linkToSpawningBlock(api2, todoId) {
|
|
|
72995
73667
|
`));
|
|
72996
73668
|
}
|
|
72997
73669
|
async function main() {
|
|
72998
|
-
|
|
72999
|
-
process.stderr.write(`
|
|
73000
|
-
Cancelled by user (Ctrl+C)
|
|
73001
|
-
`);
|
|
73002
|
-
process.exit(130);
|
|
73003
|
-
});
|
|
73670
|
+
installSignalHandlers();
|
|
73004
73671
|
const { values: args, positionals } = parseCliArgs();
|
|
73005
73672
|
const groupTag = args.group === undefined ? getEnv("GROUP_ID") : String(args.group);
|
|
73006
73673
|
const groupName = args["group-name"] === undefined ? undefined : String(args["group-name"]);
|
|
@@ -73045,11 +73712,19 @@ Cancelled by user (Ctrl+C)
|
|
|
73045
73712
|
printDeviceHelp();
|
|
73046
73713
|
process.exit(0);
|
|
73047
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
|
+
}
|
|
73048
73723
|
if ((positionals[0] === "list" || positionals[0] === "ls") && args.help) {
|
|
73049
73724
|
printListTodosHelp();
|
|
73050
73725
|
process.exit(0);
|
|
73051
73726
|
}
|
|
73052
|
-
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])) {
|
|
73053
73728
|
printUsage();
|
|
73054
73729
|
process.exit(0);
|
|
73055
73730
|
}
|
|
@@ -73167,18 +73842,27 @@ Cancelled by user (Ctrl+C)
|
|
|
73167
73842
|
await todoCommand(api2, positionals, args);
|
|
73168
73843
|
return;
|
|
73169
73844
|
}
|
|
73845
|
+
const scopedProject = args.project || getEnv("PROJECT_ID") || cfgScope.data.default_project_id || undefined;
|
|
73170
73846
|
if (positionals[0] === "project") {
|
|
73171
|
-
await projectCommand(api2, positionals, args);
|
|
73847
|
+
await projectCommand(api2, positionals, args, scopedProject);
|
|
73172
73848
|
return;
|
|
73173
73849
|
}
|
|
73174
73850
|
if (positionals[0] === "brand") {
|
|
73175
|
-
await brandCommand(api2, positionals, args);
|
|
73851
|
+
await brandCommand(api2, positionals, args, scopedProject);
|
|
73176
73852
|
return;
|
|
73177
73853
|
}
|
|
73178
73854
|
if (positionals[0] === "device") {
|
|
73179
73855
|
await deviceCommand(api2, positionals, args);
|
|
73180
73856
|
return;
|
|
73181
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
|
+
}
|
|
73182
73866
|
if (positionals[0] === "delete") {
|
|
73183
73867
|
const todoId = positionals[1];
|
|
73184
73868
|
if (!todoId) {
|
|
@@ -73535,6 +74219,7 @@ Cancelled by user (Ctrl+C)
|
|
|
73535
74219
|
}
|
|
73536
74220
|
if (!args["no-watch"]) {
|
|
73537
74221
|
const ws2 = new FrontendWebSocket(apiUrl, apiKey);
|
|
74222
|
+
setActiveRun2({ ws: ws2, projectId: projectId2, todoId });
|
|
73538
74223
|
await ws2.connect();
|
|
73539
74224
|
const autoApprove = !!args["dangerously-skip-permissions"];
|
|
73540
74225
|
let agent3 = todo2.agentSettings || { id: todo2.agentSettingsId };
|
|
@@ -73545,7 +74230,8 @@ Cancelled by user (Ctrl+C)
|
|
|
73545
74230
|
await watchTodo(ws2, todoId, projectId2, {
|
|
73546
74231
|
json: !!args.json,
|
|
73547
74232
|
autoApprove,
|
|
73548
|
-
agentSettings: agent3
|
|
74233
|
+
agentSettings: agent3,
|
|
74234
|
+
exitOnInterrupt: !!args["non-interactive"]
|
|
73549
74235
|
});
|
|
73550
74236
|
if (!args["non-interactive"]) {
|
|
73551
74237
|
process.stderr.write(`
|
|
@@ -73594,9 +74280,9 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
73594
74280
|
const followUp = promptArg(positionals) ?? (process.stdin.isTTY ? "" : await readStdin());
|
|
73595
74281
|
if (followUp) {
|
|
73596
74282
|
cfg.addToHistory(followUp);
|
|
73597
|
-
await api2
|
|
74283
|
+
await appendTracked(api2, ws2, projectId2, followUp, agent3, todoId);
|
|
73598
74284
|
await linkToSpawningBlock(api2, todoId);
|
|
73599
|
-
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"] });
|
|
73600
74286
|
}
|
|
73601
74287
|
if (!args["non-interactive"]) {
|
|
73602
74288
|
await interactiveLoop(ws2, api2, todoId, projectId2, agent3, !!args.json, autoApprove, cfg);
|
|
@@ -73615,9 +74301,15 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
73615
74301
|
const bridgeReady = !args["no-bridge"] && !args["no-watch"] && !args.isolated ? ensureBridgeRunning2(apiUrl, apiKey) : null;
|
|
73616
74302
|
const isolatedTodoId = args.isolated ? randomUUID3() : undefined;
|
|
73617
74303
|
const mayfly = isolatedTodoId ? await spawnMayflyBridge(apiUrl, isolatedTodoId, realpathSync2(resolve4(args.path || ".")), { debug: !!args.debug, apiKey }) : null;
|
|
73618
|
-
if (mayfly)
|
|
74304
|
+
if (mayfly) {
|
|
73619
74305
|
process.stderr.write(`${DIM2}Isolated bridge:${RESET2} ${CYAN2}mayfly-${isolatedTodoId}${RESET2}
|
|
73620
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
|
+
}
|
|
73621
74313
|
let preMatchedAgent = null;
|
|
73622
74314
|
let agents = null;
|
|
73623
74315
|
if (args.agent) {
|
|
@@ -73721,15 +74413,16 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
73721
74413
|
agent2 = { ...agent2, permissions: { ...perms, allow: [...perms.allow || [], "*:*"] } };
|
|
73722
74414
|
}
|
|
73723
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));
|
|
73724
74419
|
let todo;
|
|
73725
74420
|
try {
|
|
73726
|
-
todo = await
|
|
74421
|
+
todo = await created;
|
|
73727
74422
|
} catch (e) {
|
|
73728
74423
|
if (!args.project && !envProjectId && cfgScope.data.default_project_id === projectId && /failed: 403/.test(e.message || "")) {
|
|
73729
74424
|
cfgScope.clearDefaultProject();
|
|
73730
|
-
|
|
73731
|
-
`);
|
|
73732
|
-
process.exit(1);
|
|
74425
|
+
throw new Error(`Not authorized for cached default project ${projectName} (${projectId}) \u2014 cleared it. Re-run to pick a project.`);
|
|
73733
74426
|
}
|
|
73734
74427
|
throw e;
|
|
73735
74428
|
}
|
|
@@ -73750,7 +74443,8 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
73750
74443
|
await watchTodo(ws, actualTodoId, projectId, {
|
|
73751
74444
|
json: !!args.json,
|
|
73752
74445
|
autoApprove,
|
|
73753
|
-
agentSettings: agent2
|
|
74446
|
+
agentSettings: agent2,
|
|
74447
|
+
exitOnInterrupt: !!args["non-interactive"]
|
|
73754
74448
|
});
|
|
73755
74449
|
if (!args["non-interactive"]) {
|
|
73756
74450
|
process.stderr.write(`
|
|
@@ -73762,10 +74456,6 @@ ${"\u2500".repeat(40)}
|
|
|
73762
74456
|
}
|
|
73763
74457
|
mayfly?.stop();
|
|
73764
74458
|
}
|
|
73765
|
-
var finish = (code) => {
|
|
73766
|
-
process.exitCode = code;
|
|
73767
|
-
process.stdout.end(() => process.exit(code));
|
|
73768
|
-
};
|
|
73769
74459
|
main().then(() => finish(process.exitCode ?? 0)).catch((e) => {
|
|
73770
74460
|
process.stderr.write(`Error: ${e.message}
|
|
73771
74461
|
`);
|