@todoforai/cli 0.1.65 → 0.1.67
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 +2 -0
- package/dist/todoforai-cli.js +558 -179
- package/package.json +1 -1
package/dist/todoforai-cli.js
CHANGED
|
@@ -37682,19 +37682,19 @@ var require_doctype = __commonJS((exports) => {
|
|
|
37682
37682
|
return DOCUMENT_MODE.NO_QUIRKS;
|
|
37683
37683
|
};
|
|
37684
37684
|
exports.serializeContent = function(name, publicId, systemId) {
|
|
37685
|
-
let
|
|
37685
|
+
let str2 = "!DOCTYPE ";
|
|
37686
37686
|
if (name) {
|
|
37687
|
-
|
|
37687
|
+
str2 += name;
|
|
37688
37688
|
}
|
|
37689
37689
|
if (publicId) {
|
|
37690
|
-
|
|
37690
|
+
str2 += " PUBLIC " + enquoteDoctypeId(publicId);
|
|
37691
37691
|
} else if (systemId) {
|
|
37692
|
-
|
|
37692
|
+
str2 += " SYSTEM";
|
|
37693
37693
|
}
|
|
37694
37694
|
if (systemId !== null) {
|
|
37695
|
-
|
|
37695
|
+
str2 += " " + enquoteDoctypeId(systemId);
|
|
37696
37696
|
}
|
|
37697
|
-
return
|
|
37697
|
+
return str2;
|
|
37698
37698
|
};
|
|
37699
37699
|
});
|
|
37700
37700
|
|
|
@@ -40266,14 +40266,14 @@ var require_serializer = __commonJS((exports, module) => {
|
|
|
40266
40266
|
this.html += "<" + doctype.serializeContent(name, null, null) + ">";
|
|
40267
40267
|
}
|
|
40268
40268
|
}
|
|
40269
|
-
Serializer.escapeString = function(
|
|
40270
|
-
|
|
40269
|
+
Serializer.escapeString = function(str2, attrMode) {
|
|
40270
|
+
str2 = str2.replace(AMP_REGEX, "&").replace(NBSP_REGEX, " ");
|
|
40271
40271
|
if (attrMode) {
|
|
40272
|
-
|
|
40272
|
+
str2 = str2.replace(DOUBLE_QUOTE_REGEX, """);
|
|
40273
40273
|
} else {
|
|
40274
|
-
|
|
40274
|
+
str2 = str2.replace(LT_REGEX, "<").replace(GT_REGEX, ">");
|
|
40275
40275
|
}
|
|
40276
|
-
return
|
|
40276
|
+
return str2;
|
|
40277
40277
|
};
|
|
40278
40278
|
module.exports = Serializer;
|
|
40279
40279
|
});
|
|
@@ -40664,19 +40664,19 @@ var require_doctype2 = __commonJS((exports) => {
|
|
|
40664
40664
|
return DOCUMENT_MODE.NO_QUIRKS;
|
|
40665
40665
|
};
|
|
40666
40666
|
exports.serializeContent = function(name, publicId, systemId) {
|
|
40667
|
-
let
|
|
40667
|
+
let str2 = "!DOCTYPE ";
|
|
40668
40668
|
if (name) {
|
|
40669
|
-
|
|
40669
|
+
str2 += name;
|
|
40670
40670
|
}
|
|
40671
40671
|
if (publicId) {
|
|
40672
|
-
|
|
40672
|
+
str2 += " PUBLIC " + enquoteDoctypeId(publicId);
|
|
40673
40673
|
} else if (systemId) {
|
|
40674
|
-
|
|
40674
|
+
str2 += " SYSTEM";
|
|
40675
40675
|
}
|
|
40676
40676
|
if (systemId !== null) {
|
|
40677
|
-
|
|
40677
|
+
str2 += " " + enquoteDoctypeId(systemId);
|
|
40678
40678
|
}
|
|
40679
|
-
return
|
|
40679
|
+
return str2;
|
|
40680
40680
|
};
|
|
40681
40681
|
});
|
|
40682
40682
|
|
|
@@ -42969,6 +42969,9 @@ class ApiClient {
|
|
|
42969
42969
|
unshareDevice(id, email) {
|
|
42970
42970
|
return this.request("POST", `/api/v1/devices/${id}/unshare`, { id, email });
|
|
42971
42971
|
}
|
|
42972
|
+
leaveDevice(id) {
|
|
42973
|
+
return this.request("POST", `/api/v1/devices/${id}/leave`, { id });
|
|
42974
|
+
}
|
|
42972
42975
|
setDeviceWallpaperMode(id, mode) {
|
|
42973
42976
|
return this.request("POST", `/api/v1/devices/${id}/wallpaper-mode`, { id, mode });
|
|
42974
42977
|
}
|
|
@@ -42981,8 +42984,8 @@ class ApiClient {
|
|
|
42981
42984
|
renameBusinessContext(projectId, name) {
|
|
42982
42985
|
return this.request("PUT", `/api/v1/projects/${projectId}/business-context`, { projectId, name });
|
|
42983
42986
|
}
|
|
42984
|
-
listVoiceSources(projectId) {
|
|
42985
|
-
return this.request("GET", `/api/v1/projects/${projectId}/voice-sources`);
|
|
42987
|
+
listVoiceSources(projectId, scope) {
|
|
42988
|
+
return this.request("GET", `/api/v1/projects/${projectId}/voice-sources${scope ? `?scope=${scope}` : ""}`);
|
|
42986
42989
|
}
|
|
42987
42990
|
collectVoiceSource(projectId, channel, opts) {
|
|
42988
42991
|
return this.request("POST", "/api/v1/business-context/collect-voice-source", { projectId, channel, ...opts }, 130000);
|
|
@@ -42990,8 +42993,8 @@ class ApiClient {
|
|
|
42990
42993
|
removeVoiceSource(projectId, channel, opts = {}) {
|
|
42991
42994
|
return this.request("POST", "/api/v1/business-context/remove-voice-source", { projectId, channel, ...opts });
|
|
42992
42995
|
}
|
|
42993
|
-
refineBrandVoice(projectId, source, correction) {
|
|
42994
|
-
return this.request("POST", "/api/v1/business-context/refine-brand-voice", { projectId, ...source && { source }, ...correction && { correction } });
|
|
42996
|
+
refineBrandVoice(projectId, source, correction, scope) {
|
|
42997
|
+
return this.request("POST", "/api/v1/business-context/refine-brand-voice", { projectId, ...source && { source }, ...correction && { correction }, ...scope && { scope } });
|
|
42995
42998
|
}
|
|
42996
42999
|
getProfile() {
|
|
42997
43000
|
return this.request("GET", "/api/v1/users/profile");
|
|
@@ -43008,6 +43011,15 @@ class ApiClient {
|
|
|
43008
43011
|
listProjectGroups(projectId) {
|
|
43009
43012
|
return this.request("GET", `/api/v1/projects/${projectId}/groups`);
|
|
43010
43013
|
}
|
|
43014
|
+
getProjectGroupIntake(projectId, groupTag) {
|
|
43015
|
+
return this.request("GET", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(groupTag)}/intake`);
|
|
43016
|
+
}
|
|
43017
|
+
updateProjectGroupIntake(projectId, groupTag, fields) {
|
|
43018
|
+
return this.request("PUT", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(groupTag)}/intake`, { projectId, groupTag, ...fields });
|
|
43019
|
+
}
|
|
43020
|
+
regenerateProjectGroupIntake(projectId, groupTag) {
|
|
43021
|
+
return this.request("POST", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(groupTag)}/intake/regenerate`, { projectId, groupTag });
|
|
43022
|
+
}
|
|
43011
43023
|
upsertProjectGroup(projectId, slug, fields) {
|
|
43012
43024
|
return this.request("PUT", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(slug)}`, { projectId, slug, ...fields });
|
|
43013
43025
|
}
|
|
@@ -43609,6 +43621,7 @@ var TodoStatus;
|
|
|
43609
43621
|
TodoStatus2["PAUSED"] = "PAUSED";
|
|
43610
43622
|
TodoStatus2["POSTPONED"] = "POSTPONED";
|
|
43611
43623
|
TodoStatus2["REVIEW_REQUESTED"] = "REVIEW_REQUESTED";
|
|
43624
|
+
TodoStatus2["WAITING_AGENT"] = "WAITING_AGENT";
|
|
43612
43625
|
TodoStatus2["RUNNING"] = "RUNNING";
|
|
43613
43626
|
TodoStatus2["WAITING_FOR_USER"] = "WAITING_FOR_USER";
|
|
43614
43627
|
TodoStatus2["COMPACTING"] = "COMPACTING";
|
|
@@ -43623,6 +43636,11 @@ var TodoStatus;
|
|
|
43623
43636
|
TodoStatus2["ARCHIVED"] = "ARCHIVED";
|
|
43624
43637
|
TodoStatus2["DELETED"] = "DELETED";
|
|
43625
43638
|
})(TodoStatus ||= {});
|
|
43639
|
+
var CLOSED_STATUSES = [
|
|
43640
|
+
"DONE" /* DONE */,
|
|
43641
|
+
"ARCHIVED" /* ARCHIVED */,
|
|
43642
|
+
"DELETED" /* DELETED */
|
|
43643
|
+
];
|
|
43626
43644
|
// ../packages/shared-fbe/src/constants.ts
|
|
43627
43645
|
var APPEAL_EMAIL = "support@todofor.ai";
|
|
43628
43646
|
var ACCOUNT_SUSPENDED_MESSAGE = `Account suspended for suspected abuse. If you think this is a mistake, email ${APPEAL_EMAIL}.`;
|
|
@@ -43639,22 +43657,28 @@ var PROVIDER_BY_AUTHOR = {
|
|
|
43639
43657
|
"x-ai": "xai",
|
|
43640
43658
|
"z-ai": "z.ai",
|
|
43641
43659
|
google: "google-ai-studio",
|
|
43642
|
-
moonshotai: "opencode_go"
|
|
43660
|
+
moonshotai: "opencode_go",
|
|
43661
|
+
meta: "meta"
|
|
43643
43662
|
};
|
|
43644
43663
|
var MODEL_PROVIDER_OVERRIDES = {
|
|
43645
43664
|
"openai/gpt-5.5-pro": "openrouter"
|
|
43646
43665
|
};
|
|
43666
|
+
var MODEL_ENDPOINT_OVERRIDES = {
|
|
43667
|
+
"google/gemini-4-argon": { "google-ai-studio": "vertex" }
|
|
43668
|
+
};
|
|
43647
43669
|
function resolveDispatchProvider(modelId, endpointProvider) {
|
|
43648
|
-
|
|
43670
|
+
const id = modelId.toLowerCase();
|
|
43671
|
+
return MODEL_PROVIDER_OVERRIDES[id] ?? MODEL_ENDPOINT_OVERRIDES[id]?.[endpointProvider.toLowerCase()] ?? endpointProvider;
|
|
43649
43672
|
}
|
|
43650
43673
|
var SUBSCRIPTION_PROVIDERS = new Set(["opencode_go"]);
|
|
43651
43674
|
function applyProviderOverride(qualified) {
|
|
43652
43675
|
const colon = qualified.indexOf(":");
|
|
43676
|
+
const provider = qualified.slice(0, colon);
|
|
43653
43677
|
const rest = qualified.slice(colon + 1);
|
|
43654
43678
|
const suffix = rest.match(/\([^)]+\)$/)?.[0] ?? "";
|
|
43655
43679
|
const bare = (suffix ? rest.slice(0, -suffix.length) : rest).toLowerCase();
|
|
43656
|
-
const
|
|
43657
|
-
return
|
|
43680
|
+
const resolved = resolveDispatchProvider(bare, provider);
|
|
43681
|
+
return resolved === provider ? qualified : `${resolved}:${bare}${suffix}`;
|
|
43658
43682
|
}
|
|
43659
43683
|
function isQualifiedModel(model) {
|
|
43660
43684
|
const id = model.trim();
|
|
@@ -43677,9 +43701,9 @@ function qualifyModel(model) {
|
|
|
43677
43701
|
const override = MODEL_PROVIDER_OVERRIDES[lower];
|
|
43678
43702
|
if (override)
|
|
43679
43703
|
return { model: `${override}:${lower}${suffix}` };
|
|
43680
|
-
const
|
|
43681
|
-
if (
|
|
43682
|
-
return { model: `${
|
|
43704
|
+
const author = PROVIDER_BY_AUTHOR[lower.split("/")[0]];
|
|
43705
|
+
if (author && lower.includes("/"))
|
|
43706
|
+
return { model: `${resolveDispatchProvider(lower, author)}:${lower}${suffix}` };
|
|
43683
43707
|
return { model: id };
|
|
43684
43708
|
}
|
|
43685
43709
|
function qualifiedModelIds(listing, filter) {
|
|
@@ -43701,6 +43725,7 @@ function qualifiedModelIds(listing, filter) {
|
|
|
43701
43725
|
var HOBBY_ALLOWED = new Set([
|
|
43702
43726
|
"anthropic/claude-sonnet-5.5",
|
|
43703
43727
|
"anthropic/claude-sonnet-5",
|
|
43728
|
+
"anthropic/claude-haiku-5.5",
|
|
43704
43729
|
"anthropic/claude-haiku-4.5"
|
|
43705
43730
|
]);
|
|
43706
43731
|
var USAGE_WINDOWS = {
|
|
@@ -43737,6 +43762,7 @@ var mimetypes_default = {
|
|
|
43737
43762
|
js: { mime: "text/javascript", type: "text" },
|
|
43738
43763
|
ts: { mime: "text/typescript", type: "text" },
|
|
43739
43764
|
html: { mime: "text/html", type: "text" },
|
|
43765
|
+
htm: { mime: "text/html", type: "text" },
|
|
43740
43766
|
css: { mime: "text/css", type: "text" },
|
|
43741
43767
|
xml: { mime: "text/xml", type: "text" },
|
|
43742
43768
|
py: { mime: "text/x-python", type: "text" },
|
|
@@ -43775,7 +43801,7 @@ var mimetypes_default = {
|
|
|
43775
43801
|
docx: { mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", type: "document", tags: ["docx"] },
|
|
43776
43802
|
ppt: { mime: "application/vnd.ms-powerpoint", type: "document" },
|
|
43777
43803
|
pptx: { mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation", type: "document", tags: ["pptx"] },
|
|
43778
|
-
xls: { mime: "application/vnd.ms-excel", type: "spreadsheet" },
|
|
43804
|
+
xls: { mime: "application/vnd.ms-excel", type: "spreadsheet", tags: ["xls"] },
|
|
43779
43805
|
xlsx: { mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", type: "spreadsheet", tags: ["xlsx"] },
|
|
43780
43806
|
zip: { mime: "application/zip", type: "archive" },
|
|
43781
43807
|
rar: { mime: "application/vnd.rar", type: "archive" },
|
|
@@ -43836,6 +43862,53 @@ function getMimeTypeFromFilename(filename) {
|
|
|
43836
43862
|
const ext = filename.toLowerCase().split(".").pop();
|
|
43837
43863
|
return EXT_TO_MIME[ext || ""] || "";
|
|
43838
43864
|
}
|
|
43865
|
+
function truncatedImageReason(data) {
|
|
43866
|
+
const n = data.length;
|
|
43867
|
+
const ascii = (i, len) => String.fromCharCode(...data.subarray(i, i + len));
|
|
43868
|
+
if (n >= 4 && data[0] === 255 && data[1] === 216 && data[2] === 255) {
|
|
43869
|
+
let i = 2;
|
|
43870
|
+
while (i + 1 < n) {
|
|
43871
|
+
if (data[i] !== 255)
|
|
43872
|
+
return;
|
|
43873
|
+
const marker = data[i + 1];
|
|
43874
|
+
if (marker === 217)
|
|
43875
|
+
return;
|
|
43876
|
+
if (marker === 255) {
|
|
43877
|
+
i++;
|
|
43878
|
+
continue;
|
|
43879
|
+
}
|
|
43880
|
+
if (marker === 1 || marker >= 208 && marker <= 216) {
|
|
43881
|
+
i += 2;
|
|
43882
|
+
continue;
|
|
43883
|
+
}
|
|
43884
|
+
if (i + 3 >= n)
|
|
43885
|
+
break;
|
|
43886
|
+
i += 2 + (data[i + 2] << 8 | data[i + 3]);
|
|
43887
|
+
if (marker === 218) {
|
|
43888
|
+
while (i + 1 < n && !(data[i] === 255 && data[i + 1] !== 0 && !(data[i + 1] >= 208 && data[i + 1] <= 215)))
|
|
43889
|
+
i++;
|
|
43890
|
+
}
|
|
43891
|
+
}
|
|
43892
|
+
return "JPEG ends before its end-of-image marker (FF D9)";
|
|
43893
|
+
}
|
|
43894
|
+
if (n >= 8 && ascii(0, 8) === `PNG\r
|
|
43895
|
+
\x1A
|
|
43896
|
+
`) {
|
|
43897
|
+
let i = 8;
|
|
43898
|
+
while (i + 8 <= n) {
|
|
43899
|
+
const len = (data[i] << 24 | data[i + 1] << 16 | data[i + 2] << 8 | data[i + 3]) >>> 0;
|
|
43900
|
+
if (ascii(i + 4, 4) === "IEND")
|
|
43901
|
+
return i + 12 <= n ? undefined : "PNG ends inside its IEND chunk";
|
|
43902
|
+
i += 12 + len;
|
|
43903
|
+
}
|
|
43904
|
+
return "PNG ends before its IEND chunk";
|
|
43905
|
+
}
|
|
43906
|
+
if (n >= 12 && ascii(0, 4) === "RIFF" && ascii(8, 4) === "WEBP") {
|
|
43907
|
+
const riff = (data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24) >>> 0;
|
|
43908
|
+
return n >= riff + 8 ? undefined : `WebP RIFF header declares ${riff + 8} bytes, file has ${n}`;
|
|
43909
|
+
}
|
|
43910
|
+
return;
|
|
43911
|
+
}
|
|
43839
43912
|
var NEEDS_OBJECT_URL = new Set(Object.entries(mimetypes_default.types).filter(([, meta]) => meta.needsObjectUrl).map(([type]) => type));
|
|
43840
43913
|
var MIME_TO_EXT = {};
|
|
43841
43914
|
for (const [ext, entry] of Object.entries(mimetypes_default.extensions)) {
|
|
@@ -44121,6 +44194,8 @@ var USER_HEADINGS = new Set([
|
|
|
44121
44194
|
"steps",
|
|
44122
44195
|
"data needed"
|
|
44123
44196
|
]);
|
|
44197
|
+
// ../packages/shared-fbe/src/bridgeProtocol.ts
|
|
44198
|
+
var ADB_DATA_CHUNK = 40 * 1024;
|
|
44124
44199
|
// ../packages/shared-fbe/src/bashPatterns.ts
|
|
44125
44200
|
function splitShellCommands(input) {
|
|
44126
44201
|
const parts = [];
|
|
@@ -44362,7 +44437,6 @@ function getBlockServerId(block) {
|
|
|
44362
44437
|
const prefix = getBlockServerPrefix(block);
|
|
44363
44438
|
return prefix ? prefix.slice(0, -1) : "*";
|
|
44364
44439
|
}
|
|
44365
|
-
|
|
44366
44440
|
// ../packages/shared-fbe/src/permissionUtils.ts
|
|
44367
44441
|
function parsePattern(pattern) {
|
|
44368
44442
|
const colonIndex = pattern.indexOf(":");
|
|
@@ -44592,7 +44666,7 @@ Exactly ONE \`api_recommend_template\` call per proposal: it carries both what t
|
|
|
44592
44666
|
};
|
|
44593
44667
|
var JARVIS_AGENT = {
|
|
44594
44668
|
id: "00000000-0000-0000-0000-202601013152",
|
|
44595
|
-
name: "
|
|
44669
|
+
name: "Tod",
|
|
44596
44670
|
ownerId: "SYSTEM",
|
|
44597
44671
|
systemMessage: "",
|
|
44598
44672
|
model: "",
|
|
@@ -45125,7 +45199,7 @@ var tool_catalog_default = {
|
|
|
45125
45199
|
installer: "npm",
|
|
45126
45200
|
label: "TODOforAI",
|
|
45127
45201
|
summary: "Create and follow up on your TODOs, and show results in the chat.",
|
|
45128
|
-
description: 'Manage the user\'s OWN TODOforAI tasks (not code `// TODO` comments). Delegate/spin off work: `tfa-cli "prompt"` creates a real TODO with its own agent and chat (`-n` wait for completion, `--no-watch` fire-and-forget, `--project/--agent/--model`, `--resume <id>`, `addmessage <id> "text"`). Inspect: `list [--status open]`, `--inspect <id>`. Edit any todo without opening it: `status <id> DONE|READY|ARCHIVED|POSTPONED|CANCELLED|DELETED` (close, reopen, shelve, trash — `todo restore <id>` undoes a delete) and `todo set <id|-> title=… group=… star=true schedule=<unix-ms> agent=… brand=…` (own todo fields). Both take ONE id, so a loop over `list --json | jq -r \'.items[].id\'` is how you close, retitle, refile or archive MANY todos at once; `project set|settings|groups …` (name, settings, todo groups), `agent create|update <agent> model=… sysmsg=…` (permissions/MCP/device wiring and delete are refused from an agent shell). Surface results into chat: `show <file|-> [todo-id]` (renders by mimetype, private to the todo\'s readers; `--card <name>` via project renderer; `--alias <name>` updates in place, `--alias <todoId>:<name>` updates another todo\'s artifact; `show share <ref>` makes it public and prints the link, `show unshare <ref>` makes it private again), `open <url>` embeds a live preview. Pick by where the output lives: read once → plain reply; interactive, this chat only → `html_snippet`; kept/revised later → `show --alias`, then `show share` only if someone outside needs a link; format the user asked for (pptx, docx…) → `show` the file. Notifications: `inbox [needs-you|messages|activity] [--unread]` reads the user\'s bell feed (what finished/failed/needs them while away), `inbox seen [tab]` marks it read; `notify "Title" "message" [--href /t/<id>]` leaves a note in the user\'s Messages tab + phone push (shown as from you); `--to <email>` reaches a member of the current project instead (`project members` lists them, 20/day) — nobody else; same text is delivered once, max 10/day to the user, use for things they must see later (not chat replies). This is the ONLY way to display a local file (image, pdf, html) in the chat: markdown `` with a local/relative path does NOT resolve to workspace files or attachments and 404s in the browser. `--template` for registry workflows.',
|
|
45202
|
+
description: 'Manage the user\'s OWN TODOforAI tasks (not code `// TODO` comments). Delegate/spin off work: `tfa-cli "prompt"` creates a real TODO with its own agent and chat (`-n` wait for completion, `--no-watch` fire-and-forget, `--project/--agent/--model`, `--resume <id>`, `addmessage <id> "text"`). Inspect: `list [--status open]`, `--inspect <id>`. Edit any todo without opening it: `status <id> DONE|READY|ARCHIVED|POSTPONED|CANCELLED|DELETED` (close, reopen, shelve, trash — `todo restore <id>` undoes a delete) and `todo set <id|-> title=… group=… star=true schedule=<unix-ms> agent=… brand=…` (own todo fields). Both take ONE id, so a loop over `list --json | jq -r \'.items[].id\'` is how you close, retitle, refile or archive MANY todos at once; `project set|settings|groups …` (name, settings, todo groups), `agent create|update <agent> model=… sysmsg=…` (permissions/MCP/device wiring and delete are refused from an agent shell). Surface results into chat: `show <file|-> [todo-id]` (renders by mimetype, private to the todo\'s readers; `--card <name>` via project renderer; `--alias <name>` updates in place, `--alias <todoId>:<name>` updates another todo\'s artifact; `show share <ref>` makes it public and prints the link, `show unshare <ref>` makes it private again), `open <url>` embeds a live preview. Show viewer chrome: `--title` appears in the header; a kebab menu holds file actions, versions and sharing when available. Desktop floating previews add minimize (park in a tab), maximize/restore and close (X); inline blocks have no window controls. For authored HTML, do not duplicate the header/controls or add a rounded, bordered root card: the floating viewer owns the rounded frame. Keep content edge-to-edge; internal layout padding is fine. Pick by where the output lives: read once → plain reply; interactive, this chat only → `html_snippet`; kept/revised later → `show --alias`, then `show share` only if someone outside needs a link; format the user asked for (pptx, docx…) → `show` the file. Notifications: `inbox [needs-you|messages|activity] [--unread]` reads the user\'s bell feed (what finished/failed/needs them while away), `inbox seen [tab]` marks it read; `notify "Title" "message" [--href /t/<id>]` leaves a note in the user\'s Messages tab + phone push (shown as from you); `--to <email>` reaches a member of the current project instead (`project members` lists them, 20/day) — nobody else; same text is delivered once, max 10/day to the user, use for things they must see later (not chat replies). This is the ONLY way to display a local file (image, pdf, html) in the chat: markdown `` with a local/relative path does NOT resolve to workspace files or attachments and 404s in the browser. `--template` for registry workflows. Project files: `memory sync|path [--project <id>]` requires a project and checks out only projects/<id>/{memory,context} and projects/<id>/me/{memory,context}; legacy global files are read-only recovery. Voice commands use `--scope mine|project` (default mine, private within this project; project is shared).',
|
|
45129
45203
|
installCmd: "npm install -g @todoforai/cli",
|
|
45130
45204
|
versionCmd: "tfa-cli --version 2>/dev/null | sed -n 1p",
|
|
45131
45205
|
preinstallCloud: true,
|
|
@@ -45358,7 +45432,7 @@ var tool_catalog_default = {
|
|
|
45358
45432
|
preinstallCloud: true,
|
|
45359
45433
|
label: "Browser",
|
|
45360
45434
|
summary: "Browse websites automatically: click, type, read and screenshot.",
|
|
45361
|
-
description: "Browser automation over CDP (`open`/`snapshot`/`click`/`type`/`fill`/`read`/`screenshot`/`eval`). `connect '<url>'` to a listed live browser (prefer the user's real Chrome), verify with `snapshot`; if empty, `close` (own session only) and reconnect once. NEVER `close --all`. Shared browser: `tab new` for your own tab, `tab <id>` before each command. No URL listed: cloud → browser-manager-cli; PC with display → Chrome `--remote-debugging-port=9222`, attach `--cdp 9222`.",
|
|
45435
|
+
description: "Browser automation over CDP (`open`/`snapshot`/`click`/`type`/`fill`/`read`/`screenshot`/`eval`). `connect '<url>'` to a listed live browser (prefer the user's real Chrome), verify with `snapshot`; if empty, `close` (own session only) and reconnect once. NEVER `close --all`. Shared browser: `tab new` for your own tab, `tab <id>` before each command. No URL listed: cloud → browser-manager-cli; PC with display → Chrome `--remote-debugging-port=9222`, attach `--cdp 9222`. Never `sleep`: `open` returns after load, but `click`/`fill` fail at once if the element isn't there yet — `wait '<sel>'`, `wait --text 'Done'`, `wait --url '**/dash'`, `wait --load networkidle` or `wait '#spinner' --state hidden` first.",
|
|
45362
45436
|
versionCmd: "agent-browser --version 2>/dev/null | sed -n 1p",
|
|
45363
45437
|
preinstallJarvis: true
|
|
45364
45438
|
},
|
|
@@ -45903,6 +45977,7 @@ var tool_catalog_default = {
|
|
|
45903
45977
|
summary: "Post to your Facebook Page and its linked Instagram account, and handle comments.",
|
|
45904
45978
|
statusCmd: `facebook-pages-api whoami 2>/dev/null | sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p' | grep -m10 .`,
|
|
45905
45979
|
loginCmd: "facebook-pages-api auth --page-id <id> --page-token <token>",
|
|
45980
|
+
oauthBroker: true,
|
|
45906
45981
|
credentialPaths: ["~/.config/facebook-pages-api/credentials.json"],
|
|
45907
45982
|
description: "Facebook Pages CLI (Meta Graph API). `auth --page-id <id> --page-token <token>` (or env FB_PAGE_ID/FB_PAGE_TOKEN). `feed`, `comments`, `insights`, `conversations`, `media`, and `request <GET|POST|DELETE> <path> [--data <json>]` = any Graph edge with the page token attached (use instead of curl). Writes are live: `feed create --published false` for tests; `comments reply` posts publicly — get human approval. Follower totals: `whoami --fields followers_count,fan_count` (many classic insights metrics are retired). Instagram (linked professional account, same page token): `ig whoami|media|get|stories|tags|limit`, `ig publish --image_url U | --video_url U [--media_type STORIES] | --carousel U1,U2 [--caption …]` (public URLs; container → wait → publish; `--create-only` = dry run; 100 posts/24h), `ig comments list|replies|create|reply|hide|delete|toggle`, `ig insights [media_id]` (needs instagram_manage_insights). `--help` for subcommands.",
|
|
45908
45983
|
versionCmd: "facebook-pages-api --version 2>/dev/null | sed -n 1p"
|
|
@@ -49895,21 +49970,67 @@ var tool_catalog_default = {
|
|
|
49895
49970
|
},
|
|
49896
49971
|
statusCmd: `zoo user view --format json 2>/dev/null | sed -n 's/.*"email": *"\\([^"]*\\)".*/\\1/p' | grep -m1 .`,
|
|
49897
49972
|
loginCmd: "zoo auth login --web",
|
|
49973
|
+
logoutCmd: "printf 'y\\n' | zoo --host api.zoo.dev auth logout",
|
|
49898
49974
|
loginNeedsPty: true,
|
|
49899
49975
|
deviceCodePattern: "user code: ([A-Z0-9]{4}-[A-Z0-9]{4})",
|
|
49900
|
-
oauthUrlPattern: "(https://api\\.zoo\\.dev/oauth2/device/verify
|
|
49976
|
+
oauthUrlPattern: "(https://api\\.zoo\\.dev/oauth2/device/verify\\?[^\\s']+)",
|
|
49901
49977
|
canStreamLogin: true,
|
|
49902
49978
|
credentialPaths: [
|
|
49903
49979
|
"~/.config/zoo/hosts.toml"
|
|
49904
49980
|
],
|
|
49905
49981
|
description: 'Official Zoo (KittyCAD) CLI. Text-to-CAD: `zoo ml text-to-cad export --output-format step "a 40mm gear"`; convert: `zoo file convert in.obj out/ --output-format step`; mass/volume: `zoo file mass|volume <file>`; KCL: `zoo kcl export|snapshot`; raw: `zoo api /user`. Token alternative: `echo <token> | zoo auth login --with-token`.',
|
|
49906
49982
|
versionCmd: "zoo --version 2>/dev/null | sed -n 1p"
|
|
49983
|
+
},
|
|
49984
|
+
"python-docx": {
|
|
49985
|
+
category: "documents",
|
|
49986
|
+
pkg: "python-docx",
|
|
49987
|
+
pipModules: ["docx"],
|
|
49988
|
+
installer: "pip",
|
|
49989
|
+
label: "Word documents",
|
|
49990
|
+
summary: "Create, read and edit Word documents.",
|
|
49991
|
+
versionCmd: `{ "$HOME/.todoforai/tools/venv/bin/python" -c 'import docx; print(docx.__version__)' || python -c 'import docx; print(docx.__version__)' || python3 -c 'import docx; print(docx.__version__)' || py -c 'import docx; print(docx.__version__)'; } 2>/dev/null`,
|
|
49992
|
+
description: "Python python-docx library. Use python3 to create, save, reopen and edit Office files; no Microsoft account required."
|
|
49993
|
+
},
|
|
49994
|
+
openpyxl: {
|
|
49995
|
+
category: "documents",
|
|
49996
|
+
pkg: "openpyxl",
|
|
49997
|
+
pipModules: ["openpyxl"],
|
|
49998
|
+
installer: "pip",
|
|
49999
|
+
label: "Excel workbooks",
|
|
50000
|
+
summary: "Create, read and edit Excel workbooks.",
|
|
50001
|
+
versionCmd: `{ "$HOME/.todoforai/tools/venv/bin/python" -c 'import openpyxl; print(openpyxl.__version__)' || python -c 'import openpyxl; print(openpyxl.__version__)' || python3 -c 'import openpyxl; print(openpyxl.__version__)' || py -c 'import openpyxl; print(openpyxl.__version__)'; } 2>/dev/null`,
|
|
50002
|
+
description: "Python openpyxl library. Use python3 to create, save, reopen and edit Office files; no Microsoft account required."
|
|
50003
|
+
},
|
|
50004
|
+
"python-pptx": {
|
|
50005
|
+
category: "documents",
|
|
50006
|
+
pkg: "python-pptx",
|
|
50007
|
+
pipModules: ["pptx"],
|
|
50008
|
+
installer: "pip",
|
|
50009
|
+
label: "PowerPoint decks",
|
|
50010
|
+
summary: "Create, read and edit PowerPoint decks.",
|
|
50011
|
+
versionCmd: `{ "$HOME/.todoforai/tools/venv/bin/python" -c 'import pptx; print(pptx.__version__)' || python -c 'import pptx; print(pptx.__version__)' || python3 -c 'import pptx; print(pptx.__version__)' || py -c 'import pptx; print(pptx.__version__)'; } 2>/dev/null`,
|
|
50012
|
+
description: "Python python-pptx library. Use python3 to create, save, reopen and edit Office files; no Microsoft account required."
|
|
50013
|
+
},
|
|
50014
|
+
"modal-h3": {
|
|
50015
|
+
category: "creative",
|
|
50016
|
+
pkg: "@todoforai/modal-h3",
|
|
50017
|
+
installer: "npm",
|
|
50018
|
+
label: "MiniMax-H3 on Modal",
|
|
50019
|
+
summary: "Generate videos with sound using MiniMax-H3 on your own Modal GPUs.",
|
|
50020
|
+
versionCmd: "modal-h3 --version 2>/dev/null | sed -n 1p",
|
|
50021
|
+
statusCmd: `modal-h3 whoami 2>/dev/null | grep '"authenticated": true' >/dev/null && echo authenticated`,
|
|
50022
|
+
loginCmd: "printf '%s' <proxy_token> | modal-h3 auth --token-stdin --fl2va-url <fl2va_url> --ref2va-url <ref2va_url>",
|
|
50023
|
+
credentialPaths: ["~/.config/modal-h3/credentials.json"],
|
|
50024
|
+
description: "MiniMax-H3 text/image/reference-to-video+audio on a self-deployed Modal app (SGLang, 4x H200, scale-to-zero). `modal-h3 generate -p '…' [--first @a.png] [--last …] [--ref-image|--ref-video|--ref-audio <url|@file>] [--duration 4-15] [--ratio 16:9|auto] [--quality high] -o out.mp4` (task inferred, retries 503 during cold start, polls, downloads), `warmup`, `status|download|delete <id>`, `list`, `api <METHOD> <path>`. Deploy once with `modal run modal/serve.py::download` + `modal deploy modal/serve.py`. Costs ~$18/h of 4x H200 while a container is up (cold start minutes, ~75 s per 5 s 768p clip warm); every request wakes a container, `whoami` is offline. Jobs live in container memory. 2K needs `minimax-api regenerate`."
|
|
49907
50025
|
}
|
|
49908
50026
|
};
|
|
49909
50027
|
|
|
49910
50028
|
// ../packages/shared-fbe/src/toolCatalog.ts
|
|
49911
50029
|
var TOOL_CATALOG = tool_catalog_default;
|
|
49912
50030
|
// ../packages/shared-fbe/src/toolInstallCommand.ts
|
|
50031
|
+
var NPM_HOME = '"${TFA_NPM_HOME:-$HOME}"';
|
|
50032
|
+
var NPM_TOOLS = NPM_HOME.replace('}"', '}/.todoforai/tools"');
|
|
50033
|
+
var NPM_LOCAL = NPM_HOME.replace('}"', '}/.local"');
|
|
49913
50034
|
var KEY_OF = new Map(Object.entries(TOOL_CATALOG).map(([k, e]) => [e, k]));
|
|
49914
50035
|
var NODE_MIN_MAJOR = 24;
|
|
49915
50036
|
var NODE_OK = `command -v npm >/dev/null 2>&1 && node -e 'process.exit(+process.versions.node.split(".")[0]>=${NODE_MIN_MAJOR}?0:1)' >/dev/null 2>&1`;
|
|
@@ -50151,7 +50272,7 @@ var package_default = {
|
|
|
50151
50272
|
type: "git",
|
|
50152
50273
|
url: "git+https://github.com/todoforai/cli.git"
|
|
50153
50274
|
},
|
|
50154
|
-
version: "0.1.
|
|
50275
|
+
version: "0.1.67",
|
|
50155
50276
|
type: "module",
|
|
50156
50277
|
bin: {
|
|
50157
50278
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -50251,6 +50372,7 @@ Options:
|
|
|
50251
50372
|
--no-bridge Do not auto-spawn bridge
|
|
50252
50373
|
--json
|
|
50253
50374
|
--detailed | --format-anthropic inspect --json: keep ids/timestamps | Anthropic messages shape
|
|
50375
|
+
--full inspect: no truncation of text, thinking, tool calls and results
|
|
50254
50376
|
--safe Validate API key upfront
|
|
50255
50377
|
--debug, -d
|
|
50256
50378
|
--debug-dump Attach LLM request debug info per turn (requires server grant)
|
|
@@ -50263,8 +50385,9 @@ tfa-cli show — put a file or url into the chat
|
|
|
50263
50385
|
|
|
50264
50386
|
Usage:
|
|
50265
50387
|
tfa-cli show <file|-> [todo-id] Render a file by mimetype (- reads stdin)
|
|
50266
|
-
[--title T] [--alias A] [--mime M] [--card <name>] [--link] [--json]
|
|
50388
|
+
[--title T] [--alias A] [--mime M] [--card <name>] [--link] [--force] [--json]
|
|
50267
50389
|
--link: compact download chip instead of inline render
|
|
50390
|
+
Refuses a likely-truncated image (JPEG/PNG cut before the end marker, WebP shorter than its RIFF size); --force shows it anyway
|
|
50268
50391
|
Prints "<todoId>:<alias|id>". Private: visible to whoever can read the todo.
|
|
50269
50392
|
Re-showing with the same --alias updates the block in place (new version)
|
|
50270
50393
|
--alias <todoId>:<alias> updates that todo's block instead,
|
|
@@ -50279,6 +50402,13 @@ Usage:
|
|
|
50279
50402
|
|
|
50280
50403
|
todo-id defaults to $TODOFORAI_TODO_ID (agent shell) or the last todo this CLI touched.
|
|
50281
50404
|
|
|
50405
|
+
Viewer chrome:
|
|
50406
|
+
--title appears in the header; a kebab menu holds file actions, versions and sharing when available.
|
|
50407
|
+
Desktop floating previews add minimize (park in a tab), maximize/restore and close (X).
|
|
50408
|
+
Inline blocks have no window controls. Do not draw these controls or a duplicate title bar in authored HTML.
|
|
50409
|
+
The floating viewer owns the rounded frame. Keep the HTML root flat, without a border, radius, shadow or outer margin;
|
|
50410
|
+
internal content padding and spacing are fine. Existing files and websites keep their own styling.
|
|
50411
|
+
|
|
50282
50412
|
When to use what:
|
|
50283
50413
|
read once, move on → plain chat reply
|
|
50284
50414
|
interactive, this chat only → html_snippet (can exec/connect/chat back)
|
|
@@ -50342,10 +50472,12 @@ function parseCliArgs() {
|
|
|
50342
50472
|
mime: { type: "string" },
|
|
50343
50473
|
card: { type: "string" },
|
|
50344
50474
|
link: { type: "boolean", default: false },
|
|
50475
|
+
force: { type: "boolean", default: false },
|
|
50345
50476
|
direction: { type: "string" },
|
|
50346
50477
|
url: { type: "string" },
|
|
50347
50478
|
account: { type: "string" },
|
|
50348
50479
|
"from-company": { type: "boolean", default: false },
|
|
50480
|
+
scope: { type: "string" },
|
|
50349
50481
|
"dry-run": { type: "boolean", default: false },
|
|
50350
50482
|
max: { type: "string" },
|
|
50351
50483
|
seed: { type: "string" },
|
|
@@ -50370,6 +50502,7 @@ function parseCliArgs() {
|
|
|
50370
50502
|
"no-edge": { type: "boolean", default: false },
|
|
50371
50503
|
json: { type: "boolean", default: false },
|
|
50372
50504
|
detailed: { type: "boolean", default: false },
|
|
50505
|
+
full: { type: "boolean", default: false },
|
|
50373
50506
|
"format-anthropic": { type: "boolean", default: false },
|
|
50374
50507
|
safe: { type: "boolean", default: false },
|
|
50375
50508
|
debug: { type: "boolean", short: "d", default: false },
|
|
@@ -51268,7 +51401,8 @@ function getFrontendUrl(apiUrl, todoId) {
|
|
|
51268
51401
|
}
|
|
51269
51402
|
|
|
51270
51403
|
// src/inspect.ts
|
|
51271
|
-
var
|
|
51404
|
+
var str = (v) => typeof v === "string" ? v : JSON.stringify(v);
|
|
51405
|
+
var xmlAttr = (v) => JSON.stringify(str(v));
|
|
51272
51406
|
var DROP_IF_EMPTY = new Set(["runMeta", "meta", "attachments", "blocks"]);
|
|
51273
51407
|
var ALWAYS_DROP = new Set(["messageIds", "data"]);
|
|
51274
51408
|
var DETAILED_ONLY = new Set([
|
|
@@ -51349,15 +51483,20 @@ function blockToAnthropic(block, mode, format) {
|
|
|
51349
51483
|
const msg = block.error_message || block.content || "(unknown error)";
|
|
51350
51484
|
return [{ type: "text", text: `[error] ${msg}` }];
|
|
51351
51485
|
}
|
|
51486
|
+
const name = t === "named_tool" ? block.tool_name === "run_shell" ? "bash" : block.tool_name : t;
|
|
51352
51487
|
const inputFields = [];
|
|
51353
51488
|
for (const [k, v] of Object.entries(block)) {
|
|
51354
|
-
if (BLOCK_META_KEYS.has(k))
|
|
51489
|
+
if (BLOCK_META_KEYS.has(k) || k === "args" || t === "named_tool" && k === "tool_name")
|
|
51355
51490
|
continue;
|
|
51356
51491
|
if (v === undefined || v === null || v === "")
|
|
51357
51492
|
continue;
|
|
51358
51493
|
inputFields.push([k, v]);
|
|
51359
51494
|
}
|
|
51360
|
-
const
|
|
51495
|
+
for (const [k, v] of Object.entries(block.args ?? {})) {
|
|
51496
|
+
if (!inputFields.some(([key]) => key === k))
|
|
51497
|
+
inputFields.push([k, v]);
|
|
51498
|
+
}
|
|
51499
|
+
const use = format === "anthropic" ? { type: "tool_use", id: block.id, name, input: Object.fromEntries(inputFields) } : { type: "tool_use", content: `<${name}${inputFields.map(([k, v]) => ` ${k}=${xmlAttr(v)}`).join("")}/>` };
|
|
51361
51500
|
if (format === "compact" && mode !== "default")
|
|
51362
51501
|
use.id = block.id;
|
|
51363
51502
|
const items = [use];
|
|
@@ -51373,8 +51512,9 @@ function blockToAnthropic(block, mode, format) {
|
|
|
51373
51512
|
source.name = att.originalName;
|
|
51374
51513
|
parts.push({ type: isImage ? "image" : "document", source });
|
|
51375
51514
|
}
|
|
51376
|
-
|
|
51377
|
-
|
|
51515
|
+
const text = r.content || r.meta?.text;
|
|
51516
|
+
if (typeof text === "string" && text)
|
|
51517
|
+
parts.push({ type: "text", text });
|
|
51378
51518
|
return parts;
|
|
51379
51519
|
});
|
|
51380
51520
|
const result = { type: "tool_result", tool_use_id: block.id };
|
|
@@ -51391,8 +51531,9 @@ function blockToAnthropic(block, mode, format) {
|
|
|
51391
51531
|
const nameAttr = att.originalName && !isAutoName ? ` name=${xmlAttr(att.originalName)}` : "";
|
|
51392
51532
|
parts.push(`<attachment uri=${xmlAttr(att.uri)}${nameAttr} size=${att.fileSize}/>`);
|
|
51393
51533
|
}
|
|
51394
|
-
|
|
51395
|
-
|
|
51534
|
+
const text = r.content || r.meta?.text;
|
|
51535
|
+
if (typeof text === "string" && text)
|
|
51536
|
+
parts.push(text);
|
|
51396
51537
|
}
|
|
51397
51538
|
const result = { type: "tool_result", content: parts.join(`
|
|
51398
51539
|
`) };
|
|
@@ -51494,12 +51635,14 @@ function applySlice(arr, spec) {
|
|
|
51494
51635
|
throw new Error(`Bad slice: '${spec}'`);
|
|
51495
51636
|
return arr.slice(start, end);
|
|
51496
51637
|
}
|
|
51497
|
-
var
|
|
51498
|
-
${DIM}... (${s.length} chars)${RESET}` : s;
|
|
51638
|
+
var truncAt = (s, n) => s.length > n ? s.slice(0, n) + `
|
|
51639
|
+
${DIM}... (${s.length} chars, --full for all)${RESET}` : s;
|
|
51499
51640
|
var indent = (s, pre) => s.split(`
|
|
51500
51641
|
`).join(`
|
|
51501
51642
|
${pre}`);
|
|
51502
|
-
function printFullChat(todo, frontendUrl, slice, mode = "default", format = "compact") {
|
|
51643
|
+
function printFullChat(todo, frontendUrl, slice, mode = "default", format = "compact", full = false) {
|
|
51644
|
+
const out = (s) => process.stdout.write(s);
|
|
51645
|
+
const trunc = (s, n) => full ? s : truncAt(s, n);
|
|
51503
51646
|
const statusColors = {
|
|
51504
51647
|
DONE: GREEN,
|
|
51505
51648
|
READY: GREEN,
|
|
@@ -51513,34 +51656,34 @@ function printFullChat(todo, frontendUrl, slice, mode = "default", format = "com
|
|
|
51513
51656
|
TODO: CYAN
|
|
51514
51657
|
};
|
|
51515
51658
|
const statusColor = statusColors[todo.status] || DIM;
|
|
51516
|
-
|
|
51659
|
+
out(`${BOLD}TODO${RESET} ${todo.id}
|
|
51517
51660
|
`);
|
|
51518
|
-
|
|
51661
|
+
out(`${DIM}Status:${RESET} ${statusColor}${todo.status}${RESET}
|
|
51519
51662
|
`);
|
|
51520
|
-
|
|
51663
|
+
out(`${DIM}URL:${RESET} ${CYAN}${frontendUrl}${RESET}
|
|
51521
51664
|
`);
|
|
51522
|
-
|
|
51665
|
+
out(`${DIM}Created:${RESET} ${new Date(todo.createdAt).toLocaleString()}
|
|
51523
51666
|
`);
|
|
51524
51667
|
if (todo.agentSettingsId)
|
|
51525
|
-
|
|
51668
|
+
out(`${DIM}Agent:${RESET} ${todo.agentSettingsId}
|
|
51526
51669
|
`);
|
|
51527
51670
|
if (slice)
|
|
51528
|
-
|
|
51671
|
+
out(`${DIM}Slice:${RESET} [${slice}]
|
|
51529
51672
|
`);
|
|
51530
|
-
|
|
51673
|
+
out("─".repeat(60) + `
|
|
51531
51674
|
`);
|
|
51532
51675
|
let messages = todo.messages || [];
|
|
51533
51676
|
if (slice) {
|
|
51534
51677
|
try {
|
|
51535
51678
|
messages = applySlice(messages, slice);
|
|
51536
51679
|
} catch (e) {
|
|
51537
|
-
|
|
51680
|
+
out(`${RED}${e.message}${RESET}
|
|
51538
51681
|
`);
|
|
51539
51682
|
process.exit(2);
|
|
51540
51683
|
}
|
|
51541
51684
|
}
|
|
51542
51685
|
if (!messages.length) {
|
|
51543
|
-
|
|
51686
|
+
out(`${DIM}(no messages)${RESET}
|
|
51544
51687
|
`);
|
|
51545
51688
|
return;
|
|
51546
51689
|
}
|
|
@@ -51551,33 +51694,33 @@ function printFullChat(todo, frontendUrl, slice, mode = "default", format = "com
|
|
|
51551
51694
|
const orig = messages[i];
|
|
51552
51695
|
const ts = mode !== "default" && orig?.createdAt ? ` ${DIM}${new Date(orig.createdAt).toLocaleTimeString()}${RESET}` : "";
|
|
51553
51696
|
const label = msg.role === "user" ? `${CYAN}▶ USER${RESET}` : `${GREEN}◀ ASSISTANT${RESET}`;
|
|
51554
|
-
|
|
51697
|
+
out(`
|
|
51555
51698
|
${label}${ts}
|
|
51556
51699
|
`);
|
|
51557
51700
|
const items = Array.isArray(msg.content) ? msg.content : [{ type: "text", text: msg.content }];
|
|
51558
51701
|
for (const it of items) {
|
|
51559
51702
|
if (it.type === "text") {
|
|
51560
51703
|
if (it.text)
|
|
51561
|
-
|
|
51704
|
+
out(trunc(it.text, 2000) + `
|
|
51562
51705
|
`);
|
|
51563
51706
|
} else if (it.type === "image" || it.type === "document") {
|
|
51564
51707
|
const s = it.source ?? {};
|
|
51565
|
-
|
|
51708
|
+
out(` ${YELLOW}[${it.type}]${RESET} ${DIM}${s.mimeType ?? ""}${RESET} ${s.name ?? ""} ${DIM}(${s.size ?? "?"} bytes)${RESET}
|
|
51566
51709
|
${DIM}${s.uri ?? ""}${RESET}
|
|
51567
51710
|
`);
|
|
51568
51711
|
} else if (it.type === "thinking") {
|
|
51569
51712
|
if (it.thinking)
|
|
51570
|
-
|
|
51713
|
+
out(`${DIM}[thinking]${RESET} ${trunc(it.thinking, 500)}
|
|
51571
51714
|
`);
|
|
51572
51715
|
} else if (it.type === "tool_use") {
|
|
51573
51716
|
toolUseCount++;
|
|
51574
51717
|
if (it.content) {
|
|
51575
|
-
|
|
51718
|
+
out(` ${YELLOW}${trunc(it.content, 200)}${RESET}
|
|
51576
51719
|
`);
|
|
51577
51720
|
} else {
|
|
51578
|
-
const argStr = Object.entries(it.input || {}).map(([k, v]) => `${DIM}${k}=${RESET}${
|
|
51721
|
+
const argStr = Object.entries(it.input || {}).map(([k, v]) => `${DIM}${k}=${RESET}${full ? str(v) : str(v).split(`
|
|
51579
51722
|
`)[0].slice(0, 80)}`).join(" ");
|
|
51580
|
-
|
|
51723
|
+
out(` ${YELLOW}[${it.name}]${RESET} ${argStr}
|
|
51581
51724
|
`);
|
|
51582
51725
|
}
|
|
51583
51726
|
} else if (it.type === "tool_result") {
|
|
@@ -51600,15 +51743,15 @@ ${label}${ts}
|
|
|
51600
51743
|
}).join(`
|
|
51601
51744
|
`);
|
|
51602
51745
|
}
|
|
51603
|
-
|
|
51746
|
+
out(` ${DIM}└─ result:${RESET}${status} ${indent(trunc(bodyStr, 500), ` ${DIM}│${RESET} `)}
|
|
51604
51747
|
`);
|
|
51605
51748
|
}
|
|
51606
51749
|
}
|
|
51607
51750
|
}
|
|
51608
|
-
|
|
51751
|
+
out(`
|
|
51609
51752
|
` + "─".repeat(60) + `
|
|
51610
51753
|
`);
|
|
51611
|
-
|
|
51754
|
+
out(`${DIM}Messages: ${shaped.length} | Tool calls: ${toolUseCount} | Tool errors: ${errorCount}${RESET}
|
|
51612
51755
|
`);
|
|
51613
51756
|
}
|
|
51614
51757
|
|
|
@@ -52616,6 +52759,7 @@ function renderDiff(originalContent, modifiedContent, filePath) {
|
|
|
52616
52759
|
|
|
52617
52760
|
// src/shutdown.ts
|
|
52618
52761
|
var activeRun = null;
|
|
52762
|
+
var stopOnExit = false;
|
|
52619
52763
|
var cleanups = [];
|
|
52620
52764
|
var exiting = null;
|
|
52621
52765
|
var cancelling = null;
|
|
@@ -52623,6 +52767,9 @@ var CANCEL_TIMEOUT_MS = 5000;
|
|
|
52623
52767
|
function setActiveRun(run) {
|
|
52624
52768
|
activeRun = run;
|
|
52625
52769
|
}
|
|
52770
|
+
var stopsRunOnExit = () => stopOnExit;
|
|
52771
|
+
var detachNotice = (todoId) => `Todo keeps running — reattach: tfa-cli --resume ${todoId}
|
|
52772
|
+
`;
|
|
52626
52773
|
function withTimeout(p, ms, fallback) {
|
|
52627
52774
|
let timer;
|
|
52628
52775
|
const timeout = new Promise((r) => {
|
|
@@ -52634,7 +52781,7 @@ function cancelActiveRun(timeoutMs = CANCEL_TIMEOUT_MS) {
|
|
|
52634
52781
|
if (cancelling)
|
|
52635
52782
|
return cancelling;
|
|
52636
52783
|
const pending = activeRun;
|
|
52637
|
-
if (!pending)
|
|
52784
|
+
if (!pending || !stopOnExit)
|
|
52638
52785
|
return Promise.resolve({ ok: false, todoId: null });
|
|
52639
52786
|
const res = { ok: false };
|
|
52640
52787
|
const attempt = (async () => {
|
|
@@ -52660,7 +52807,11 @@ function shutdown(code, reason) {
|
|
|
52660
52807
|
process.stderr.write(`
|
|
52661
52808
|
${reason}
|
|
52662
52809
|
`);
|
|
52663
|
-
if (activeRun) {
|
|
52810
|
+
if (activeRun && !stopOnExit) {
|
|
52811
|
+
const run = await withTimeout(Promise.resolve(activeRun), CANCEL_TIMEOUT_MS, null);
|
|
52812
|
+
if (run)
|
|
52813
|
+
process.stderr.write(detachNotice(run.todoId));
|
|
52814
|
+
} else if (activeRun) {
|
|
52664
52815
|
const { ok, todoId } = await cancelActiveRun();
|
|
52665
52816
|
if (ok)
|
|
52666
52817
|
process.stderr.write(`Stopped todo ${todoId}
|
|
@@ -52772,20 +52923,6 @@ async function watchTodo(ws, todoId, projectId, opts = {}) {
|
|
|
52772
52923
|
diffStoreByWs.set(ws, diffStore);
|
|
52773
52924
|
const diffRendered = new Set;
|
|
52774
52925
|
let approveAll = !!opts.autoApprove;
|
|
52775
|
-
let interruptCount = 0;
|
|
52776
|
-
const origHandler = process.listeners("SIGINT").slice();
|
|
52777
|
-
process.removeAllListeners("SIGINT");
|
|
52778
|
-
process.on("SIGINT", () => {
|
|
52779
|
-
interruptCount++;
|
|
52780
|
-
if (opts.exitOnInterrupt || interruptCount >= 2)
|
|
52781
|
-
return void shutdown(130, "Cancelled by user (Ctrl+C)");
|
|
52782
|
-
process.stderr.write(`
|
|
52783
|
-
${YELLOW}Interrupting... (Ctrl+C again to force exit)${RESET}
|
|
52784
|
-
`);
|
|
52785
|
-
if (opts.interruptOnCancel !== false) {
|
|
52786
|
-
ws.sendInterrupt(projectId, todoId);
|
|
52787
|
-
}
|
|
52788
|
-
});
|
|
52789
52926
|
const pendingBlocks = [];
|
|
52790
52927
|
let approvalPromptActive = false;
|
|
52791
52928
|
let activeApprovalBlocks = [];
|
|
@@ -53008,14 +53145,10 @@ ${YELLOW}Connection lost — reconnecting...${RESET}
|
|
|
53008
53145
|
await cancelActiveRun();
|
|
53009
53146
|
process.exitCode = 1;
|
|
53010
53147
|
if (!opts.suppressCancelNotice) {
|
|
53011
|
-
process.stderr.write(`${YELLOW}Interrupted${RESET}
|
|
53012
|
-
`);
|
|
53148
|
+
process.stderr.write(stopsRunOnExit() ? `${YELLOW}Interrupted${RESET}
|
|
53149
|
+
` : detachNotice(todoId));
|
|
53013
53150
|
}
|
|
53014
53151
|
return false;
|
|
53015
|
-
} finally {
|
|
53016
|
-
process.removeAllListeners("SIGINT");
|
|
53017
|
-
for (const fn of origHandler)
|
|
53018
|
-
process.on("SIGINT", fn);
|
|
53019
53152
|
}
|
|
53020
53153
|
}
|
|
53021
53154
|
|
|
@@ -53584,6 +53717,8 @@ Usage:
|
|
|
53584
53717
|
tfa-cli project settings <field=value>… Project settings (PUT /settings/projects/{id})
|
|
53585
53718
|
tfa-cli project groups List todo groups
|
|
53586
53719
|
tfa-cli project groups set <slug> <field=value>… Create/update a group: name description pinned archived order
|
|
53720
|
+
tfa-cli project groups intake <slug> [on|off|regenerate] [field=value]…
|
|
53721
|
+
Public intake: autoStart maxSubmissions postIpLimit placeholder agentSettingsId
|
|
53587
53722
|
tfa-cli project groups reorder <slug>… Set wall order
|
|
53588
53723
|
tfa-cli project surfaces List the board's card surfaces (live refs shown as cards)
|
|
53589
53724
|
tfa-cli project surfaces set <id> ref=<ref> [title=…] [group=<slug>] [order=N]
|
|
@@ -53748,6 +53883,25 @@ async function projectCommand(api, positionals, args, projectId) {
|
|
|
53748
53883
|
`);
|
|
53749
53884
|
return;
|
|
53750
53885
|
}
|
|
53886
|
+
if (verb === "intake") {
|
|
53887
|
+
const [slug, ...options] = gargs;
|
|
53888
|
+
if (!slug)
|
|
53889
|
+
fail("Usage: tfa-cli project groups intake <slug> [on|off|regenerate] [field=value]…");
|
|
53890
|
+
const action = ["on", "off", "regenerate"].includes(options[0]) ? options.shift() : undefined;
|
|
53891
|
+
if (options.length && action === "regenerate")
|
|
53892
|
+
fail("regenerate does not accept settings");
|
|
53893
|
+
const fields = parseAssignments2(options);
|
|
53894
|
+
const intake = action === "regenerate" ? await api.regenerateProjectGroupIntake(projectId, slug) : action || options.length ? await api.updateProjectGroupIntake(projectId, slug, { ...fields, ...action && { enabled: action === "on" } }) : await api.getProjectGroupIntake(projectId, slug);
|
|
53895
|
+
if (args.json) {
|
|
53896
|
+
console.log(JSON.stringify(intake, null, 2));
|
|
53897
|
+
return;
|
|
53898
|
+
}
|
|
53899
|
+
process.stderr.write(`${slug}: public ${intake.enabled ? "on" : "off"}, ${intake.submissionCount}/${intake.maxSubmissions}, autoStart=${intake.autoStart}
|
|
53900
|
+
`);
|
|
53901
|
+
if (intake.url)
|
|
53902
|
+
console.log(intake.url);
|
|
53903
|
+
return;
|
|
53904
|
+
}
|
|
53751
53905
|
if (verb === "reorder") {
|
|
53752
53906
|
if (!gargs.length)
|
|
53753
53907
|
fail("Usage: tfa-cli project groups reorder <slug>…");
|
|
@@ -53823,6 +53977,11 @@ Usage:
|
|
|
53823
53977
|
tfa-cli brand voice correct "<what is off>" Tell the learner what it got wrong; profile is updated
|
|
53824
53978
|
in one pass and the correction is kept for every re-learn
|
|
53825
53979
|
|
|
53980
|
+
Every voice verb takes --scope mine|project — which voice to teach:
|
|
53981
|
+
mine yours, only in this project (default)
|
|
53982
|
+
project the project's, shared with its members (needs write access)
|
|
53983
|
+
On the same voice name the narrower wins: mine > project.
|
|
53984
|
+
|
|
53826
53985
|
|
|
53827
53986
|
Questions the UI asks (keys for 'answers'):
|
|
53828
53987
|
"What makes you different from competitors?"
|
|
@@ -53921,10 +54080,13 @@ async function voiceDeviceCommand(rest, args) {
|
|
|
53921
54080
|
}
|
|
53922
54081
|
async function voiceCommand(api, projectId, rest, args) {
|
|
53923
54082
|
const [verb, ...vargs] = rest;
|
|
54083
|
+
const scope = args.scope ?? "mine";
|
|
54084
|
+
if (!["project", "mine"].includes(scope))
|
|
54085
|
+
fail(`--scope must be mine or project, got '${scope}'`);
|
|
53924
54086
|
if (verb === "collect" && !vargs[0])
|
|
53925
54087
|
fail("Usage: tfa-cli brand voice collect <channel> [--url U | --account <page-id> | --max N] [--dry-run]");
|
|
53926
54088
|
if (verb === "answers") {
|
|
53927
|
-
const answers = manualAnswersOf((await api.listVoiceSources(projectId)).sources);
|
|
54089
|
+
const answers = manualAnswersOf((await api.listVoiceSources(projectId, scope)).sources);
|
|
53928
54090
|
if (vargs.length) {
|
|
53929
54091
|
for (const a of vargs) {
|
|
53930
54092
|
const i = a.indexOf("=");
|
|
@@ -53932,7 +54094,7 @@ async function voiceCommand(api, projectId, rest, args) {
|
|
|
53932
54094
|
fail(`Expected question=answer, got '${a}'`);
|
|
53933
54095
|
answers[a.slice(0, i)] = a.slice(i + 1);
|
|
53934
54096
|
}
|
|
53935
|
-
await api.collectVoiceSource(projectId, MANUAL_CHANNEL, { samples: manualSamples(answers) });
|
|
54097
|
+
await api.collectVoiceSource(projectId, MANUAL_CHANNEL, { samples: manualSamples(answers), scope });
|
|
53936
54098
|
process.stderr.write(`${GREEN}✅ ${vargs.length} answer(s) saved${RESET}
|
|
53937
54099
|
`);
|
|
53938
54100
|
return;
|
|
@@ -53948,7 +54110,7 @@ async function voiceCommand(api, projectId, rest, args) {
|
|
|
53948
54110
|
return;
|
|
53949
54111
|
}
|
|
53950
54112
|
if (!verb || verb === "show") {
|
|
53951
|
-
const [{ sources, profile }, brand] = await Promise.all([api.listVoiceSources(projectId), api.getBusinessContext(projectId)]);
|
|
54113
|
+
const [{ sources, profile }, brand] = await Promise.all([api.listVoiceSources(projectId, scope), api.getBusinessContext(projectId)]);
|
|
53952
54114
|
if (args.json) {
|
|
53953
54115
|
console.log(JSON.stringify({ brand, profile, sources }, null, 2));
|
|
53954
54116
|
return;
|
|
@@ -53979,12 +54141,12 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
|
|
|
53979
54141
|
const { samples, note } = collectChannel(channel, Number(args.max ?? 40));
|
|
53980
54142
|
if (!samples.length)
|
|
53981
54143
|
fail(`${channel}: nothing usable came back${note ? ` (${note})` : ""}`);
|
|
53982
|
-
const { sources: sources2 } = await api.collectVoiceSource(projectId, channel, { samples });
|
|
54144
|
+
const { sources: sources2 } = await api.collectVoiceSource(projectId, channel, { samples, scope });
|
|
53983
54145
|
process.stderr.write(`${GREEN}✅ ${channel}: ${samples.length} reply pairs stored (${sources2.length} source(s))${RESET}
|
|
53984
54146
|
`);
|
|
53985
54147
|
return;
|
|
53986
54148
|
}
|
|
53987
|
-
const { sources } = await api.collectVoiceSource(projectId, channel, { url: args.url, account: args.account });
|
|
54149
|
+
const { sources } = await api.collectVoiceSource(projectId, channel, { url: args.url, account: args.account, scope });
|
|
53988
54150
|
process.stderr.write(`${GREEN}✅ ${channel} collected (${sources.length} source(s))${RESET}
|
|
53989
54151
|
`);
|
|
53990
54152
|
return;
|
|
@@ -53992,7 +54154,7 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
|
|
|
53992
54154
|
if (verb === "remove") {
|
|
53993
54155
|
if (!vargs[0])
|
|
53994
54156
|
fail("Usage: tfa-cli brand voice remove <channel> [--account <id>] (no --account: every source of the channel)");
|
|
53995
|
-
await api.removeVoiceSource(projectId, vargs[0], args.account ? { account: args.account } : { all: true });
|
|
54157
|
+
await api.removeVoiceSource(projectId, vargs[0], { ...args.account ? { account: args.account } : { all: true }, scope });
|
|
53996
54158
|
process.stderr.write(`${GREEN}✅ ${vargs[0]} removed${RESET}
|
|
53997
54159
|
`);
|
|
53998
54160
|
return;
|
|
@@ -54000,7 +54162,7 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
|
|
|
54000
54162
|
if (verb === "learn") {
|
|
54001
54163
|
process.stderr.write(`${DIM}learning voice…${RESET}
|
|
54002
54164
|
`);
|
|
54003
|
-
const res = await api.refineBrandVoice(projectId, args["from-company"] ? "company" : undefined);
|
|
54165
|
+
const res = await api.refineBrandVoice(projectId, args["from-company"] ? "company" : undefined, undefined, scope);
|
|
54004
54166
|
if (!res.profile)
|
|
54005
54167
|
fail(args["from-company"] ? "No brand page to learn from — 'tfa-cli brand create <name>' and fill it first" : "Nothing to learn from — add answers, collect a source, or fill the brand page first");
|
|
54006
54168
|
if (args.json) {
|
|
@@ -54016,11 +54178,11 @@ ${res.profile}
|
|
|
54016
54178
|
const text = vargs.join(" ").trim();
|
|
54017
54179
|
if (!text)
|
|
54018
54180
|
fail('Usage: tfa-cli brand voice correct "<what is off>"');
|
|
54019
|
-
if (!(await api.listVoiceSources(projectId)).profile?.profile)
|
|
54181
|
+
if (!(await api.listVoiceSources(projectId, scope)).profile?.profile)
|
|
54020
54182
|
fail("No voice learned yet — 'tfa-cli brand voice learn' first");
|
|
54021
54183
|
process.stderr.write(`${DIM}applying correction…${RESET}
|
|
54022
54184
|
`);
|
|
54023
|
-
const res = await api.refineBrandVoice(projectId, undefined, text);
|
|
54185
|
+
const res = await api.refineBrandVoice(projectId, undefined, text, scope);
|
|
54024
54186
|
if (!res.profile)
|
|
54025
54187
|
fail("The correction pass returned nothing — try rewording it");
|
|
54026
54188
|
if (args.json) {
|
|
@@ -54047,6 +54209,7 @@ Usage:
|
|
|
54047
54209
|
tfa-cli device access <device> who you shared the device with
|
|
54048
54210
|
tfa-cli device share <device> <email> let a teammate use it (their runs act as THEM)
|
|
54049
54211
|
tfa-cli device unshare <device> <email>
|
|
54212
|
+
tfa-cli device leave <device> remove your own access to a shared device
|
|
54050
54213
|
|
|
54051
54214
|
<device> is an id, name or hostname (unique partial works).
|
|
54052
54215
|
Devices shared with you are marked "shared"; only the owner can rename/share them.
|
|
@@ -54083,6 +54246,12 @@ async function deviceCommand(api, positionals, args) {
|
|
|
54083
54246
|
fail("Usage: tfa-cli device rename <device> <name>");
|
|
54084
54247
|
await api.renameDevice(found.id, value);
|
|
54085
54248
|
process.stderr.write(`${GREEN}✅ ${found.name} → ${value}${RESET}
|
|
54249
|
+
`);
|
|
54250
|
+
return;
|
|
54251
|
+
}
|
|
54252
|
+
if (sub === "leave") {
|
|
54253
|
+
await api.leaveDevice(found.id);
|
|
54254
|
+
process.stderr.write(`${GREEN}✅ Removed ${found.name} from your devices${RESET}
|
|
54086
54255
|
`);
|
|
54087
54256
|
return;
|
|
54088
54257
|
}
|
|
@@ -54132,15 +54301,7 @@ var STATUS_COLOR = {
|
|
|
54132
54301
|
POSTPONED: CYAN,
|
|
54133
54302
|
PAUSED: CYAN
|
|
54134
54303
|
};
|
|
54135
|
-
var CLOSED = new Set(
|
|
54136
|
-
"DONE",
|
|
54137
|
-
"READY_CHECKED",
|
|
54138
|
-
"CANCELLED",
|
|
54139
|
-
"CANCELLED_CHECKED",
|
|
54140
|
-
"ERROR_CHECKED",
|
|
54141
|
-
"ARCHIVED",
|
|
54142
|
-
"DELETED"
|
|
54143
|
-
]);
|
|
54304
|
+
var CLOSED = new Set(CLOSED_STATUSES);
|
|
54144
54305
|
var VALID_STATUS = new Set(Object.values(TodoStatus));
|
|
54145
54306
|
var isOpen = (s) => !CLOSED.has(s);
|
|
54146
54307
|
function printListTodosHelp() {
|
|
@@ -54152,11 +54313,16 @@ Usage:
|
|
|
54152
54313
|
|
|
54153
54314
|
Flags:
|
|
54154
54315
|
-n, --limit <n> Max rows to show (default: 30)
|
|
54155
|
-
--cursor <n> Fetch todos older than this cursor (
|
|
54156
|
-
--
|
|
54316
|
+
--cursor <n> Fetch todos older than this cursor (sort field, epoch ms)
|
|
54317
|
+
--since <t> Only todos with sort field >= t (inclusive)
|
|
54318
|
+
--until <t> Only todos with sort field < t (exclusive)
|
|
54319
|
+
t = epoch ms | YYYY-MM-DD (local midnight) | ISO datetime | 12h / 3d ago
|
|
54320
|
+
--sort <key> activity (lastActivityAt, default) | created (createdAt)
|
|
54321
|
+
--contributor <u> Only todos a member opened/wrote in (id or email; comma = any)
|
|
54322
|
+
--page-size <n> Backend page size per request (default: min(limit, 400), max: 400)
|
|
54157
54323
|
--search <text> Search content/status on the backend
|
|
54158
54324
|
-s, --status <S[,S2]> Filter by status (comma-separated, union).
|
|
54159
|
-
-A, --all Include
|
|
54325
|
+
-A, --all Include closed (DONE, ARCHIVED, DELETED)
|
|
54160
54326
|
--project <id> Project ID (default: current default project)
|
|
54161
54327
|
--json Output { items, nextCursor } as JSON
|
|
54162
54328
|
-h, --help Show this help
|
|
@@ -54167,6 +54333,8 @@ Examples:
|
|
|
54167
54333
|
todoforai-cli list --cursor 1719234567890 # next page
|
|
54168
54334
|
todoforai-cli list --all # include DONE
|
|
54169
54335
|
todoforai-cli list -s RUNNING,REVIEW_REQUESTED
|
|
54336
|
+
todoforai-cli list -A --since 2026-10-11 --until 2026-10-12 # touched that day
|
|
54337
|
+
todoforai-cli list -A --sort created --since 7d --contributor a@b.com
|
|
54170
54338
|
todoforai-cli list --search bug --json | jq '.items[].id'
|
|
54171
54339
|
`);
|
|
54172
54340
|
}
|
|
@@ -54192,6 +54360,25 @@ function cursorValue(value) {
|
|
|
54192
54360
|
}
|
|
54193
54361
|
return n;
|
|
54194
54362
|
}
|
|
54363
|
+
function parseTime(value, name, now = Date.now()) {
|
|
54364
|
+
if (value === undefined)
|
|
54365
|
+
return;
|
|
54366
|
+
const s = String(value).trim();
|
|
54367
|
+
const rel = s.match(/^(\d+)([mhd])$/);
|
|
54368
|
+
let n;
|
|
54369
|
+
if (/^\d+$/.test(s))
|
|
54370
|
+
n = Number(s);
|
|
54371
|
+
else if (rel)
|
|
54372
|
+
n = now - Number(rel[1]) * { m: 60000, h: 3600000, d: 86400000 }[rel[2]];
|
|
54373
|
+
else
|
|
54374
|
+
n = Date.parse(/^\d{4}-\d{2}-\d{2}$/.test(s) ? `${s}T00:00` : s);
|
|
54375
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
54376
|
+
process.stderr.write(`${RED}Error: ${name} must be epoch ms, YYYY-MM-DD, ISO datetime or e.g. 12h / 3d${RESET}
|
|
54377
|
+
`);
|
|
54378
|
+
process.exit(2);
|
|
54379
|
+
}
|
|
54380
|
+
return n;
|
|
54381
|
+
}
|
|
54195
54382
|
function toItemsPage(response) {
|
|
54196
54383
|
if (Array.isArray(response))
|
|
54197
54384
|
return { items: response };
|
|
@@ -54200,8 +54387,25 @@ function toItemsPage(response) {
|
|
|
54200
54387
|
nextCursor: typeof response?.nextCursor === "number" ? response.nextCursor : undefined
|
|
54201
54388
|
};
|
|
54202
54389
|
}
|
|
54203
|
-
function rowTime(t) {
|
|
54204
|
-
return Number(t.lastActivityAt ?? t.createdAt ?? 0);
|
|
54390
|
+
function rowTime(t, sort = "activity") {
|
|
54391
|
+
return Number((sort === "created" ? t.createdAt : t.lastActivityAt ?? t.createdAt) ?? 0);
|
|
54392
|
+
}
|
|
54393
|
+
async function resolveContributors(api, projectId, raw) {
|
|
54394
|
+
const wanted = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
54395
|
+
if (!wanted.some((w) => w.includes("@")))
|
|
54396
|
+
return wanted;
|
|
54397
|
+
const members = await api.getProjectMembers(projectId);
|
|
54398
|
+
return wanted.map((w) => {
|
|
54399
|
+
if (!w.includes("@"))
|
|
54400
|
+
return w;
|
|
54401
|
+
const m = members.find((m2) => m2.email?.toLowerCase() === w.toLowerCase());
|
|
54402
|
+
if (!m) {
|
|
54403
|
+
process.stderr.write(`${RED}Error: no project member with email ${w}${RESET}
|
|
54404
|
+
`);
|
|
54405
|
+
process.exit(2);
|
|
54406
|
+
}
|
|
54407
|
+
return m.id;
|
|
54408
|
+
});
|
|
54205
54409
|
}
|
|
54206
54410
|
function printStatusError(unknown) {
|
|
54207
54411
|
process.stderr.write(`${RED}Error: unknown status ${unknown.map((s) => `'${s}'`).join(", ")}${RESET}
|
|
@@ -54224,6 +54428,10 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54224
54428
|
cursor: { type: "string" },
|
|
54225
54429
|
"page-size": { type: "string" },
|
|
54226
54430
|
search: { type: "string" },
|
|
54431
|
+
since: { type: "string" },
|
|
54432
|
+
until: { type: "string" },
|
|
54433
|
+
sort: { type: "string" },
|
|
54434
|
+
contributor: { type: "string" },
|
|
54227
54435
|
status: { type: "string", short: "s" },
|
|
54228
54436
|
all: { type: "boolean", short: "A", default: false },
|
|
54229
54437
|
project: { type: "string" },
|
|
@@ -54243,9 +54451,20 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54243
54451
|
process.exit(1);
|
|
54244
54452
|
}
|
|
54245
54453
|
const limit = positiveInt(values.limit, "--limit") ?? 30;
|
|
54246
|
-
const pageSize = Math.min(positiveInt(values["page-size"], "--page-size") ?? Math.min(Math.max(limit, 1),
|
|
54454
|
+
const pageSize = Math.min(positiveInt(values["page-size"], "--page-size") ?? Math.min(Math.max(limit, 1), 400), 400);
|
|
54455
|
+
const sort = values.sort === undefined ? "activity" : String(values.sort);
|
|
54456
|
+
if (sort !== "activity" && sort !== "created") {
|
|
54457
|
+
process.stderr.write(`${RED}Error: --sort must be activity or created${RESET}
|
|
54458
|
+
`);
|
|
54459
|
+
process.exit(2);
|
|
54460
|
+
}
|
|
54461
|
+
const since = parseTime(values.since, "--since");
|
|
54462
|
+
const until = parseTime(values.until, "--until");
|
|
54247
54463
|
let cursor = cursorValue(values.cursor);
|
|
54464
|
+
if (until !== undefined)
|
|
54465
|
+
cursor = cursor === undefined ? until : Math.min(cursor, until);
|
|
54248
54466
|
const search = values.search ? String(values.search) : undefined;
|
|
54467
|
+
const contributorIds = values.contributor ? await resolveContributors(api, projectId, String(values.contributor)) : undefined;
|
|
54249
54468
|
const requested = values.status ? String(values.status).split(",").map((s) => s.trim().toUpperCase()).filter(Boolean) : null;
|
|
54250
54469
|
const unknown = requested?.filter((s) => s !== "OPEN" && !VALID_STATUS.has(s)) ?? [];
|
|
54251
54470
|
if (unknown.length)
|
|
@@ -54257,9 +54476,24 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54257
54476
|
let backendNextCursor;
|
|
54258
54477
|
while (rows.length < limit) {
|
|
54259
54478
|
const requestLimit = Math.min(pageSize, limit - rows.length);
|
|
54260
|
-
const page = toItemsPage(await api.listTodos(projectId, {
|
|
54479
|
+
const page = toItemsPage(await api.listTodos(projectId, {
|
|
54480
|
+
limit: requestLimit,
|
|
54481
|
+
cursor,
|
|
54482
|
+
search,
|
|
54483
|
+
since,
|
|
54484
|
+
contributorIds,
|
|
54485
|
+
...!includeClosed && { excludeClosed: true },
|
|
54486
|
+
...sort !== "activity" && { sort }
|
|
54487
|
+
}));
|
|
54261
54488
|
backendNextCursor = page.nextCursor;
|
|
54489
|
+
let exhausted = false;
|
|
54262
54490
|
for (const t of page.items) {
|
|
54491
|
+
if (since !== undefined && rowTime(t) < since) {
|
|
54492
|
+
exhausted = true;
|
|
54493
|
+
break;
|
|
54494
|
+
}
|
|
54495
|
+
if (since !== undefined && rowTime(t, sort) < since)
|
|
54496
|
+
continue;
|
|
54263
54497
|
const status = String(t.status).toUpperCase();
|
|
54264
54498
|
if (!includeClosed && CLOSED.has(status))
|
|
54265
54499
|
continue;
|
|
@@ -54269,14 +54503,18 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54269
54503
|
if (rows.length >= limit)
|
|
54270
54504
|
break;
|
|
54271
54505
|
}
|
|
54506
|
+
if (exhausted) {
|
|
54507
|
+
backendNextCursor = undefined;
|
|
54508
|
+
break;
|
|
54509
|
+
}
|
|
54272
54510
|
if (!backendNextCursor || page.items.length === 0)
|
|
54273
54511
|
break;
|
|
54274
54512
|
cursor = backendNextCursor;
|
|
54275
54513
|
}
|
|
54276
|
-
rows.sort((a, b) => rowTime(b) - rowTime(a));
|
|
54514
|
+
rows.sort((a, b) => rowTime(b, sort) - rowTime(a, sort));
|
|
54277
54515
|
const visible = rows.slice(0, limit);
|
|
54278
54516
|
const hasBufferedRows = rows.length > visible.length;
|
|
54279
|
-
const nextCursor = visible.length && (backendNextCursor !== undefined || hasBufferedRows) ? rowTime(visible[visible.length - 1]) : undefined;
|
|
54517
|
+
const nextCursor = visible.length && (backendNextCursor !== undefined || hasBufferedRows) ? rowTime(visible[visible.length - 1], sort) : undefined;
|
|
54280
54518
|
if (values.json) {
|
|
54281
54519
|
process.stdout.write(JSON.stringify({ items: visible, nextCursor }, null, 2) + `
|
|
54282
54520
|
`);
|
|
@@ -54294,7 +54532,7 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54294
54532
|
for (const t of visible) {
|
|
54295
54533
|
const status = String(t.status).toUpperCase();
|
|
54296
54534
|
const sc = STATUS_COLOR[status] || DIM;
|
|
54297
|
-
const ts = new Date(rowTime(t)).toISOString().slice(0, 16).replace("T", " ");
|
|
54535
|
+
const ts = new Date(rowTime(t, sort)).toISOString().slice(0, 16).replace("T", " ");
|
|
54298
54536
|
const title = String(t.content ?? "").split(`
|
|
54299
54537
|
`)[0].slice(0, 100);
|
|
54300
54538
|
process.stdout.write(`${sc}${status.padEnd(statusW)}${RESET} ${DIM}${ts}${RESET} ${t.id} ${title}
|
|
@@ -55182,21 +55420,26 @@ tfa-cli memory — memory and context folders as git checkouts
|
|
|
55182
55420
|
|
|
55183
55421
|
Usage:
|
|
55184
55422
|
tfa-cli memory sync [--project <id>] Clone, or pull, into:
|
|
55185
|
-
~/.todoforai/{memory,context}
|
|
55186
|
-
~/.todoforai/projects/<id>/{memory,context}
|
|
55423
|
+
~/.todoforai/projects/<id>/{memory,context} the project's (todoforai:projects/<id>/…)
|
|
55424
|
+
~/.todoforai/projects/<id>/me/{memory,context} yours in it (todoforai:projects/<id>/me/…)
|
|
55187
55425
|
tfa-cli memory path [--project <id>] Print the checkout paths
|
|
55188
55426
|
|
|
55189
55427
|
Every change to these folders is committed on the server, from any client. In a checkout
|
|
55190
55428
|
use plain git: log, diff, show, revert, commit, then \`git push\` — the push updates the live
|
|
55191
55429
|
folder. A push behind the server is refused: \`git pull\` first. Files over 5 MB live outside
|
|
55192
|
-
history (upload them instead). Project folders: members with write access push
|
|
55193
|
-
|
|
55430
|
+
history (upload them instead). Project folders: members with write access push; your own in
|
|
55431
|
+
a project (me/) is private to you. The project
|
|
55432
|
+
defaults to $TODOFORAI_PROJECT_ID and is required. Legacy global folders are read-only
|
|
55433
|
+
recovery, not synced.
|
|
55194
55434
|
`);
|
|
55195
55435
|
}
|
|
55196
55436
|
var FOLDERS = ["memory", "context"];
|
|
55197
55437
|
function checkouts(projectId) {
|
|
55198
55438
|
const root = join5(homedir5(), ".todoforai");
|
|
55199
|
-
|
|
55439
|
+
if (!projectId)
|
|
55440
|
+
throw new Error("Select a project with --project <id> or $TODOFORAI_PROJECT_ID.");
|
|
55441
|
+
const project = join5(root, "projects", projectId);
|
|
55442
|
+
const scopes = [[project, `projects/${projectId}/`], [join5(project, "me"), `projects/${projectId}/me/`]];
|
|
55200
55443
|
return scopes.flatMap(([dir, remote]) => FOLDERS.map((f) => [join5(dir, f), `${remote}${f}.git`]));
|
|
55201
55444
|
}
|
|
55202
55445
|
function git(args, cwd) {
|
|
@@ -56157,6 +56400,7 @@ __export(exports_util, {
|
|
|
56157
56400
|
toZod: () => toZod,
|
|
56158
56401
|
stringifyPrimitive: () => stringifyPrimitive,
|
|
56159
56402
|
slugify: () => slugify,
|
|
56403
|
+
sizeCaps: () => sizeCaps,
|
|
56160
56404
|
shallowClone: () => shallowClone,
|
|
56161
56405
|
safeExtend: () => safeExtend,
|
|
56162
56406
|
required: () => required,
|
|
@@ -56399,14 +56643,14 @@ function promiseAllObject(promisesObj) {
|
|
|
56399
56643
|
}
|
|
56400
56644
|
function randomString(length = 10) {
|
|
56401
56645
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
56402
|
-
let
|
|
56646
|
+
let str2 = "";
|
|
56403
56647
|
for (let i = 0;i < length; i++) {
|
|
56404
|
-
|
|
56648
|
+
str2 += chars[Math.floor(Math.random() * chars.length)];
|
|
56405
56649
|
}
|
|
56406
|
-
return
|
|
56650
|
+
return str2;
|
|
56407
56651
|
}
|
|
56408
|
-
function esc(
|
|
56409
|
-
return JSON.stringify(
|
|
56652
|
+
function esc(str2) {
|
|
56653
|
+
return JSON.stringify(str2);
|
|
56410
56654
|
}
|
|
56411
56655
|
function slugify(input) {
|
|
56412
56656
|
return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -56519,8 +56763,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
|
|
|
56519
56763
|
"symbol",
|
|
56520
56764
|
"undefined"
|
|
56521
56765
|
]);
|
|
56522
|
-
function escapeRegex(
|
|
56523
|
-
return
|
|
56766
|
+
function escapeRegex(str2) {
|
|
56767
|
+
return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
56524
56768
|
}
|
|
56525
56769
|
function clone(inst, def, params) {
|
|
56526
56770
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -56731,6 +56975,18 @@ function prefixIssues(path4, issues) {
|
|
|
56731
56975
|
function unwrapMessage(message) {
|
|
56732
56976
|
return typeof message === "string" ? message : message?.message;
|
|
56733
56977
|
}
|
|
56978
|
+
function sizeCaps(defChecks) {
|
|
56979
|
+
const caps = [];
|
|
56980
|
+
for (const ch of defChecks ?? []) {
|
|
56981
|
+
const d = ch._zod.def;
|
|
56982
|
+
if (!/_(length|size)$|_equals$/.test(d.check))
|
|
56983
|
+
break;
|
|
56984
|
+
const max = d.maximum ?? d.length ?? d.size;
|
|
56985
|
+
if (max >= 0)
|
|
56986
|
+
caps.push([max, ch]);
|
|
56987
|
+
}
|
|
56988
|
+
return caps;
|
|
56989
|
+
}
|
|
56734
56990
|
function attachSchema(issues, start, inst) {
|
|
56735
56991
|
var _a;
|
|
56736
56992
|
for (let i = start;i < issues.length; i++) {
|
|
@@ -56771,13 +57027,13 @@ function getSizableOrigin(input) {
|
|
|
56771
57027
|
return "unknown";
|
|
56772
57028
|
}
|
|
56773
57029
|
var highSurrogate = /[\uD800-\uDBFF]/;
|
|
56774
|
-
function codePointLength(
|
|
56775
|
-
const units =
|
|
56776
|
-
if (!highSurrogate.test(
|
|
57030
|
+
function codePointLength(str2) {
|
|
57031
|
+
const units = str2.length;
|
|
57032
|
+
if (!highSurrogate.test(str2))
|
|
56777
57033
|
return units;
|
|
56778
57034
|
let count = units;
|
|
56779
57035
|
for (let i = 0;i < units - 1; i++) {
|
|
56780
|
-
if ((
|
|
57036
|
+
if ((str2.charCodeAt(i) & 64512) === 55296 && (str2.charCodeAt(i + 1) & 64512) === 56320) {
|
|
56781
57037
|
count--;
|
|
56782
57038
|
i++;
|
|
56783
57039
|
}
|
|
@@ -58150,7 +58406,7 @@ ${content.join(`
|
|
|
58150
58406
|
var version = {
|
|
58151
58407
|
major: 4,
|
|
58152
58408
|
minor: 6,
|
|
58153
|
-
patch:
|
|
58409
|
+
patch: 7
|
|
58154
58410
|
};
|
|
58155
58411
|
|
|
58156
58412
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -58899,6 +59155,31 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
|
|
|
58899
59155
|
return payload;
|
|
58900
59156
|
};
|
|
58901
59157
|
});
|
|
59158
|
+
function capSize(inst, size) {
|
|
59159
|
+
const caps = sizeCaps(inst._zod.def.checks);
|
|
59160
|
+
if (!caps.length)
|
|
59161
|
+
return;
|
|
59162
|
+
const cap = Math.min(...caps.map((c3) => c3[0]));
|
|
59163
|
+
const parse2 = inst._zod.parse;
|
|
59164
|
+
inst._zod.parse = (payload, ctx) => {
|
|
59165
|
+
const n = size(payload.value);
|
|
59166
|
+
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
59167
|
+
for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
|
|
59168
|
+
if (n > max && ch._zod.def.when(payload)) {
|
|
59169
|
+
const start = payload.issues.length;
|
|
59170
|
+
ch._zod.check(payload);
|
|
59171
|
+
payload.issues[start].continue = false;
|
|
59172
|
+
attachSchema(payload.issues, start, inst);
|
|
59173
|
+
return payload;
|
|
59174
|
+
}
|
|
59175
|
+
}
|
|
59176
|
+
}
|
|
59177
|
+
return parse2(payload, ctx);
|
|
59178
|
+
};
|
|
59179
|
+
}
|
|
59180
|
+
var arraySize = (input) => Array.isArray(input) ? input.length : -1;
|
|
59181
|
+
var mapSize = (input) => input instanceof Map ? input.size : -1;
|
|
59182
|
+
var setSize = (input) => input instanceof Set ? input.size : -1;
|
|
58902
59183
|
function handleArrayResult(result, final, index) {
|
|
58903
59184
|
if (result.issues.length) {
|
|
58904
59185
|
final.issues.push(...prefixIssues(index, result.issues));
|
|
@@ -58942,6 +59223,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
|
|
|
58942
59223
|
}
|
|
58943
59224
|
return payload;
|
|
58944
59225
|
};
|
|
59226
|
+
capSize(inst, arraySize);
|
|
58945
59227
|
});
|
|
58946
59228
|
function handlePropertyResult(result, final, key, input, optin, optout) {
|
|
58947
59229
|
const isPresent = key in input;
|
|
@@ -59896,6 +60178,7 @@ var $ZodMap = /* @__PURE__ */ $constructor("$ZodMap", (inst, def) => {
|
|
|
59896
60178
|
return Promise.all(proms).then(() => payload);
|
|
59897
60179
|
return payload;
|
|
59898
60180
|
};
|
|
60181
|
+
capSize(inst, mapSize);
|
|
59899
60182
|
});
|
|
59900
60183
|
function handleMapResult(keyResult, valueResult, final, key, input, inst, ctx) {
|
|
59901
60184
|
if (keyResult.issues.length) {
|
|
@@ -59962,6 +60245,7 @@ var $ZodSet = /* @__PURE__ */ $constructor("$ZodSet", (inst, def) => {
|
|
|
59962
60245
|
return Promise.all(proms).then(() => payload);
|
|
59963
60246
|
return payload;
|
|
59964
60247
|
};
|
|
60248
|
+
capSize(inst, setSize);
|
|
59965
60249
|
});
|
|
59966
60250
|
function handleSetResult(result, final) {
|
|
59967
60251
|
if (result.issues.length) {
|
|
@@ -69411,9 +69695,15 @@ function generateNullableCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
69411
69695
|
doc.write(`}`);
|
|
69412
69696
|
return outputVar;
|
|
69413
69697
|
}
|
|
69698
|
+
function generateSizeGate(doc, schema, size) {
|
|
69699
|
+
for (const [max, check] of sizeCaps(schema._zod.def.checks)) {
|
|
69700
|
+
doc.write(`if (${size} > ${numericOperand(max, check._zod.def.check)}) return INVALID;`);
|
|
69701
|
+
}
|
|
69702
|
+
}
|
|
69414
69703
|
function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
69415
69704
|
const def = schema._zod.def;
|
|
69416
69705
|
doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
|
|
69706
|
+
generateSizeGate(doc, schema, `${accessor}.length`);
|
|
69417
69707
|
const outputVar = buildsValue ? newVar(ctx) : null;
|
|
69418
69708
|
const iVar = newVar(ctx);
|
|
69419
69709
|
const elemVar = newVar(ctx);
|
|
@@ -69813,6 +70103,7 @@ function literalPropertyKey(ctx, key) {
|
|
|
69813
70103
|
function generateMapCheck(doc, ctx, schema, accessor) {
|
|
69814
70104
|
const def = schema._zod.def;
|
|
69815
70105
|
doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
|
|
70106
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
69816
70107
|
const outputVar = newVar(ctx);
|
|
69817
70108
|
const kVar = newVar(ctx);
|
|
69818
70109
|
const valVar = newVar(ctx);
|
|
@@ -69829,6 +70120,7 @@ function generateMapCheck(doc, ctx, schema, accessor) {
|
|
|
69829
70120
|
function generateSetCheck(doc, ctx, schema, accessor) {
|
|
69830
70121
|
const def = schema._zod.def;
|
|
69831
70122
|
doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
|
|
70123
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
69832
70124
|
const outputVar = newVar(ctx);
|
|
69833
70125
|
const valVar = newVar(ctx);
|
|
69834
70126
|
doc.write(`const ${outputVar} = new Set();`);
|
|
@@ -74996,6 +75288,21 @@ function preserveCustomPayload(schema, key, knownTags) {
|
|
|
74996
75288
|
return output2;
|
|
74997
75289
|
});
|
|
74998
75290
|
}
|
|
75291
|
+
function defaultOnErrorOptionalStringTag(schema, key) {
|
|
75292
|
+
return exports_external.preprocess((value) => {
|
|
75293
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
75294
|
+
return value;
|
|
75295
|
+
}
|
|
75296
|
+
const record2 = value;
|
|
75297
|
+
const tag = record2[key];
|
|
75298
|
+
if (tag === undefined || tag === null || typeof tag === "string") {
|
|
75299
|
+
return value;
|
|
75300
|
+
}
|
|
75301
|
+
const normalized = { ...record2 };
|
|
75302
|
+
delete normalized[key];
|
|
75303
|
+
return normalized;
|
|
75304
|
+
}, schema);
|
|
75305
|
+
}
|
|
74999
75306
|
function vecSkipError(itemSchema) {
|
|
75000
75307
|
return exports_external.array(itemSchema.catch(skippedItem)).transform((items) => items.filter((item) => item !== skippedItem));
|
|
75001
75308
|
}
|
|
@@ -75014,16 +75321,12 @@ var zWriteTextFileRequest = object({
|
|
|
75014
75321
|
var zReadTextFileRequest = object({
|
|
75015
75322
|
sessionId: zSessionId,
|
|
75016
75323
|
path: string2(),
|
|
75017
|
-
line:
|
|
75324
|
+
line: int().gte(0).max(4294967295, {
|
|
75018
75325
|
error: "Invalid value: Expected uint32 to be <= 4294967295"
|
|
75019
|
-
}).nullish(),
|
|
75020
|
-
|
|
75021
|
-
}),
|
|
75022
|
-
limit: defaultOnError(int().gte(0).max(4294967295, {
|
|
75326
|
+
}).nullish(),
|
|
75327
|
+
limit: int().gte(0).max(4294967295, {
|
|
75023
75328
|
error: "Invalid value: Expected uint32 to be <= 4294967295"
|
|
75024
|
-
}).nullish(),
|
|
75025
|
-
return;
|
|
75026
|
-
}),
|
|
75329
|
+
}).nullish(),
|
|
75027
75330
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
75028
75331
|
return;
|
|
75029
75332
|
})
|
|
@@ -75274,14 +75577,10 @@ var zEnvVariable = object({
|
|
|
75274
75577
|
var zCreateTerminalRequest = object({
|
|
75275
75578
|
sessionId: zSessionId,
|
|
75276
75579
|
command: string2(),
|
|
75277
|
-
args:
|
|
75278
|
-
env:
|
|
75279
|
-
cwd:
|
|
75280
|
-
|
|
75281
|
-
}),
|
|
75282
|
-
outputByteLimit: defaultOnError(number2().nullish(), () => {
|
|
75283
|
-
return;
|
|
75284
|
-
}),
|
|
75580
|
+
args: array(string2()).optional(),
|
|
75581
|
+
env: array(zEnvVariable).optional(),
|
|
75582
|
+
cwd: string2().nullish(),
|
|
75583
|
+
outputByteLimit: number2().nullish(),
|
|
75285
75584
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
75286
75585
|
return;
|
|
75287
75586
|
})
|
|
@@ -75807,10 +76106,8 @@ var zAuthMethodTerminal = object({
|
|
|
75807
76106
|
description: defaultOnError(string2().nullish(), () => {
|
|
75808
76107
|
return;
|
|
75809
76108
|
}),
|
|
75810
|
-
args:
|
|
75811
|
-
env:
|
|
75812
|
-
return;
|
|
75813
|
-
}),
|
|
76109
|
+
args: array(string2()).optional(),
|
|
76110
|
+
env: record(string2(), string2()).optional(),
|
|
75814
76111
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
75815
76112
|
return;
|
|
75816
76113
|
})
|
|
@@ -76508,17 +76805,61 @@ var zRunningStateUpdate = object({
|
|
|
76508
76805
|
return;
|
|
76509
76806
|
})
|
|
76510
76807
|
});
|
|
76511
|
-
var
|
|
76512
|
-
|
|
76808
|
+
var zErrorStopReason = object({
|
|
76809
|
+
error: defaultOnError(zError.nullish(), () => {
|
|
76513
76810
|
return;
|
|
76811
|
+
})
|
|
76812
|
+
});
|
|
76813
|
+
var zIdleStateUpdate = defaultOnErrorOptionalStringTag(preserveCustomPayload(intersection(union([
|
|
76814
|
+
object({
|
|
76815
|
+
stopReason: literal("end_turn")
|
|
76816
|
+
}),
|
|
76817
|
+
object({
|
|
76818
|
+
stopReason: literal("max_tokens")
|
|
76819
|
+
}),
|
|
76820
|
+
object({
|
|
76821
|
+
stopReason: literal("max_turn_requests")
|
|
76514
76822
|
}),
|
|
76823
|
+
object({
|
|
76824
|
+
stopReason: literal("refusal")
|
|
76825
|
+
}),
|
|
76826
|
+
object({
|
|
76827
|
+
stopReason: literal("cancelled")
|
|
76828
|
+
}),
|
|
76829
|
+
zErrorStopReason.and(object({
|
|
76830
|
+
stopReason: literal("error")
|
|
76831
|
+
})),
|
|
76832
|
+
excludeKnownTags(object({
|
|
76833
|
+
stopReason: string2()
|
|
76834
|
+
}), "stopReason", [
|
|
76835
|
+
"cancelled",
|
|
76836
|
+
"end_turn",
|
|
76837
|
+
"error",
|
|
76838
|
+
"max_tokens",
|
|
76839
|
+
"max_turn_requests",
|
|
76840
|
+
"refusal"
|
|
76841
|
+
]),
|
|
76842
|
+
object({
|
|
76843
|
+
stopReason: _null3().optional()
|
|
76844
|
+
})
|
|
76845
|
+
]), object({
|
|
76515
76846
|
usage: defaultOnError(zUsage.nullish(), () => {
|
|
76516
76847
|
return;
|
|
76517
76848
|
}),
|
|
76518
76849
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
76519
76850
|
return;
|
|
76851
|
+
}),
|
|
76852
|
+
stopReason: defaultOnError(string2().nullish(), () => {
|
|
76853
|
+
return;
|
|
76520
76854
|
})
|
|
76521
|
-
})
|
|
76855
|
+
})), "stopReason", [
|
|
76856
|
+
"cancelled",
|
|
76857
|
+
"end_turn",
|
|
76858
|
+
"error",
|
|
76859
|
+
"max_tokens",
|
|
76860
|
+
"max_turn_requests",
|
|
76861
|
+
"refusal"
|
|
76862
|
+
]), "stopReason");
|
|
76522
76863
|
var zRequiresActionStateUpdate = object({
|
|
76523
76864
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
76524
76865
|
return;
|
|
@@ -76901,16 +77242,16 @@ var zMcpServer = union([
|
|
|
76901
77242
|
]);
|
|
76902
77243
|
var zNewSessionRequest = object({
|
|
76903
77244
|
cwd: string2(),
|
|
76904
|
-
additionalDirectories:
|
|
76905
|
-
mcpServers:
|
|
77245
|
+
additionalDirectories: array(string2()).optional(),
|
|
77246
|
+
mcpServers: array(zMcpServer),
|
|
76906
77247
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
76907
77248
|
return;
|
|
76908
77249
|
})
|
|
76909
77250
|
});
|
|
76910
77251
|
var zLoadSessionRequest = object({
|
|
76911
|
-
mcpServers:
|
|
77252
|
+
mcpServers: array(zMcpServer),
|
|
76912
77253
|
cwd: string2(),
|
|
76913
|
-
additionalDirectories:
|
|
77254
|
+
additionalDirectories: array(string2()).optional(),
|
|
76914
77255
|
sessionId: zSessionId,
|
|
76915
77256
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
76916
77257
|
return;
|
|
@@ -76932,8 +77273,8 @@ var zDeleteSessionRequest = object({
|
|
|
76932
77273
|
var zForkSessionRequest = object({
|
|
76933
77274
|
sessionId: zSessionId,
|
|
76934
77275
|
cwd: string2(),
|
|
76935
|
-
additionalDirectories:
|
|
76936
|
-
mcpServers:
|
|
77276
|
+
additionalDirectories: array(string2()).optional(),
|
|
77277
|
+
mcpServers: array(zMcpServer).optional(),
|
|
76937
77278
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
76938
77279
|
return;
|
|
76939
77280
|
})
|
|
@@ -76941,8 +77282,8 @@ var zForkSessionRequest = object({
|
|
|
76941
77282
|
var zResumeSessionRequest = object({
|
|
76942
77283
|
sessionId: zSessionId,
|
|
76943
77284
|
cwd: string2(),
|
|
76944
|
-
additionalDirectories:
|
|
76945
|
-
mcpServers:
|
|
77285
|
+
additionalDirectories: array(string2()).optional(),
|
|
77286
|
+
mcpServers: array(zMcpServer).optional(),
|
|
76946
77287
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
76947
77288
|
return;
|
|
76948
77289
|
})
|
|
@@ -77323,7 +77664,7 @@ var zDidChangeDocumentNotification = object({
|
|
|
77323
77664
|
sessionId: zSessionId,
|
|
77324
77665
|
uri: string2(),
|
|
77325
77666
|
version: number2(),
|
|
77326
|
-
contentChanges:
|
|
77667
|
+
contentChanges: array(zTextDocumentContentChangeEvent),
|
|
77327
77668
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
77328
77669
|
return;
|
|
77329
77670
|
})
|
|
@@ -78402,9 +78743,26 @@ var zGuardElicitationPropertySchemaBoolean = zBooleanPropertySchema.and(object({
|
|
|
78402
78743
|
var zGuardElicitationPropertySchemaArray = zMultiSelectPropertySchema.and(object({ type: literal("array") }));
|
|
78403
78744
|
var zGuardMultiSelectItemsString = zStringMultiSelectItems.and(object({ type: literal("string") }));
|
|
78404
78745
|
var zGuardStateUpdateRunning = zRunningStateUpdate.and(object({ state: literal("running") }));
|
|
78405
|
-
var zGuardStateUpdateIdle = zIdleStateUpdate.and(object({ state: literal("idle") }));
|
|
78746
|
+
var zGuardStateUpdateIdle = zIdleStateUpdate.and(object({ usage: zUsage.nullish() })).and(object({ state: literal("idle") }));
|
|
78406
78747
|
var zGuardStateUpdateRequiresAction = zRequiresActionStateUpdate.and(object({ state: literal("requires_action") }));
|
|
78407
78748
|
var zGuardStateUpdateUnknown = zUnknownStateUpdate.and(object({ state: literal("unknown") }));
|
|
78749
|
+
var zGuardIdleStateUpdateEndTurn = object({
|
|
78750
|
+
stopReason: literal("end_turn")
|
|
78751
|
+
});
|
|
78752
|
+
var zGuardIdleStateUpdateMaxTokens = object({
|
|
78753
|
+
stopReason: literal("max_tokens")
|
|
78754
|
+
});
|
|
78755
|
+
var zGuardIdleStateUpdateMaxTurnRequests = object({
|
|
78756
|
+
stopReason: literal("max_turn_requests")
|
|
78757
|
+
});
|
|
78758
|
+
var zGuardIdleStateUpdateRefusal = object({
|
|
78759
|
+
stopReason: literal("refusal")
|
|
78760
|
+
});
|
|
78761
|
+
var zGuardIdleStateUpdateCancelled = object({
|
|
78762
|
+
stopReason: literal("cancelled")
|
|
78763
|
+
});
|
|
78764
|
+
var zGuardIdleStateUpdateError = zErrorStopReason.and(object({ error: zError.nullish() })).and(object({ stopReason: literal("error") }));
|
|
78765
|
+
var zGuardIdleStateUpdateNone = object({ stopReason: _null3().optional() });
|
|
78408
78766
|
var zGuardCreateElicitationResponseAccept = zElicitationAcceptAction.and(object({ action: literal("accept") }));
|
|
78409
78767
|
var zGuardCreateElicitationResponseDecline = object({
|
|
78410
78768
|
action: literal("decline")
|
|
@@ -79763,6 +80121,7 @@ async function runAcp(apiUrl, apiKey, opts) {
|
|
|
79763
80121
|
|
|
79764
80122
|
// src/shutdown.ts
|
|
79765
80123
|
var activeRun2 = null;
|
|
80124
|
+
var stopOnExit2 = false;
|
|
79766
80125
|
var cleanups2 = [];
|
|
79767
80126
|
var exiting2 = null;
|
|
79768
80127
|
var cancelling2 = null;
|
|
@@ -79770,6 +80129,11 @@ var CANCEL_TIMEOUT_MS2 = 5000;
|
|
|
79770
80129
|
function setActiveRun2(run2) {
|
|
79771
80130
|
activeRun2 = run2;
|
|
79772
80131
|
}
|
|
80132
|
+
function stopRunOnExit() {
|
|
80133
|
+
stopOnExit2 = true;
|
|
80134
|
+
}
|
|
80135
|
+
var detachNotice2 = (todoId) => `Todo keeps running — reattach: tfa-cli --resume ${todoId}
|
|
80136
|
+
`;
|
|
79773
80137
|
function trackStart(ws, projectId, started) {
|
|
79774
80138
|
setActiveRun2(started.then((todoId) => ({ ws, projectId, todoId }), () => null));
|
|
79775
80139
|
}
|
|
@@ -79787,7 +80151,7 @@ function cancelActiveRun2(timeoutMs = CANCEL_TIMEOUT_MS2) {
|
|
|
79787
80151
|
if (cancelling2)
|
|
79788
80152
|
return cancelling2;
|
|
79789
80153
|
const pending = activeRun2;
|
|
79790
|
-
if (!pending)
|
|
80154
|
+
if (!pending || !stopOnExit2)
|
|
79791
80155
|
return Promise.resolve({ ok: false, todoId: null });
|
|
79792
80156
|
const res = { ok: false };
|
|
79793
80157
|
const attempt = (async () => {
|
|
@@ -79813,7 +80177,11 @@ function shutdown2(code, reason) {
|
|
|
79813
80177
|
process.stderr.write(`
|
|
79814
80178
|
${reason}
|
|
79815
80179
|
`);
|
|
79816
|
-
if (activeRun2) {
|
|
80180
|
+
if (activeRun2 && !stopOnExit2) {
|
|
80181
|
+
const run2 = await withTimeout2(Promise.resolve(activeRun2), CANCEL_TIMEOUT_MS2, null);
|
|
80182
|
+
if (run2)
|
|
80183
|
+
process.stderr.write(detachNotice2(run2.todoId));
|
|
80184
|
+
} else if (activeRun2) {
|
|
79817
80185
|
const { ok, todoId } = await cancelActiveRun2();
|
|
79818
80186
|
if (ok)
|
|
79819
80187
|
process.stderr.write(`Stopped todo ${todoId}
|
|
@@ -79843,7 +80211,7 @@ function finish(code) {
|
|
|
79843
80211
|
});
|
|
79844
80212
|
}
|
|
79845
80213
|
function installSignalHandlers() {
|
|
79846
|
-
process.on("SIGINT", () => void shutdown2(130, "
|
|
80214
|
+
process.on("SIGINT", () => void shutdown2(130, "Exited (Ctrl+C)"));
|
|
79847
80215
|
process.on("SIGTERM", () => void shutdown2(143, "Terminated (SIGTERM)"));
|
|
79848
80216
|
process.on("SIGHUP", () => void shutdown2(129, "Hangup (SIGHUP)"));
|
|
79849
80217
|
}
|
|
@@ -80266,17 +80634,17 @@ async function main() {
|
|
|
80266
80634
|
const [, filePath, todoArg] = positionals;
|
|
80267
80635
|
const todoId = todoArg || aliasTodoId(args.alias) || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
|
|
80268
80636
|
if (!filePath || !todoId) {
|
|
80269
|
-
process.stderr.write(`${RED3}Usage: tfa-cli show <file|-> [todo-id] [--title T] [--alias A] [--mime M] [--card <name>] [--link]${RESET2}
|
|
80637
|
+
process.stderr.write(`${RED3}Usage: tfa-cli show <file|-> [todo-id] [--title T] [--alias A] [--mime M] [--card <name>] [--link] [--force]${RESET2}
|
|
80270
80638
|
`);
|
|
80271
80639
|
process.exit(2);
|
|
80272
80640
|
}
|
|
80273
|
-
let
|
|
80641
|
+
let bytes, name, mime;
|
|
80274
80642
|
if (filePath === "-") {
|
|
80275
80643
|
const chunks = [];
|
|
80276
80644
|
for await (const c3 of process.stdin)
|
|
80277
80645
|
chunks.push(c3);
|
|
80278
|
-
|
|
80279
|
-
if (
|
|
80646
|
+
bytes = Buffer.concat(chunks);
|
|
80647
|
+
if (bytes.length === 0) {
|
|
80280
80648
|
process.stderr.write(`${RED3}No data on stdin${RESET2}
|
|
80281
80649
|
`);
|
|
80282
80650
|
process.exit(1);
|
|
@@ -80289,9 +80657,21 @@ async function main() {
|
|
|
80289
80657
|
`);
|
|
80290
80658
|
process.exit(1);
|
|
80291
80659
|
}
|
|
80292
|
-
|
|
80660
|
+
bytes = readFileSync5(abs);
|
|
80661
|
+
mime = getMimeTypeFromFilename(abs) || undefined;
|
|
80293
80662
|
name = path4.basename(filePath);
|
|
80294
80663
|
}
|
|
80664
|
+
const truncated = truncatedImageReason(bytes);
|
|
80665
|
+
if (truncated) {
|
|
80666
|
+
if (!args.force) {
|
|
80667
|
+
process.stderr.write(`${RED3}${name} looks truncated: ${truncated} (${bytes.length} bytes). Re-export it, or pass --force to show it anyway.${RESET2}
|
|
80668
|
+
`);
|
|
80669
|
+
process.exit(1);
|
|
80670
|
+
}
|
|
80671
|
+
process.stderr.write(`${YELLOW2}\u26A0 ${name} looks truncated: ${truncated} \u2014 showing anyway (--force)${RESET2}
|
|
80672
|
+
`);
|
|
80673
|
+
}
|
|
80674
|
+
const blob = new Blob([bytes], { type: mime });
|
|
80295
80675
|
const res = await api2.showFile(todoId, blob, name, {
|
|
80296
80676
|
title: args.title,
|
|
80297
80677
|
alias: args.alias,
|
|
@@ -80494,7 +80874,7 @@ async function main() {
|
|
|
80494
80874
|
}
|
|
80495
80875
|
const mode = args.debug ? "debug" : args.detailed ? "detailed" : "default";
|
|
80496
80876
|
const format = args["format-anthropic"] ? "anthropic" : "compact";
|
|
80497
|
-
printFullChat(todo2, getFrontendUrl(apiUrl, todoId), slice, mode, format);
|
|
80877
|
+
printFullChat(todo2, getFrontendUrl(apiUrl, todoId), slice, mode, format, args.full);
|
|
80498
80878
|
return;
|
|
80499
80879
|
}
|
|
80500
80880
|
if (process.stderr.isTTY)
|
|
@@ -80551,8 +80931,7 @@ async function main() {
|
|
|
80551
80931
|
await watchTodo(ws2, todoId, projectId2, {
|
|
80552
80932
|
json: !!args.json,
|
|
80553
80933
|
autoApprove,
|
|
80554
|
-
agentSettings: agent3
|
|
80555
|
-
exitOnInterrupt: !!args["non-interactive"]
|
|
80934
|
+
agentSettings: agent3
|
|
80556
80935
|
});
|
|
80557
80936
|
if (!args["non-interactive"]) {
|
|
80558
80937
|
process.stderr.write(`
|
|
@@ -80603,7 +80982,7 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
80603
80982
|
cfg.addToHistory(followUp);
|
|
80604
80983
|
await appendTracked(api2, ws2, projectId2, followUp, agent3, todoId);
|
|
80605
80984
|
await linkToSpawningBlock(api2, todoId);
|
|
80606
|
-
await watchTodo(ws2, todoId, projectId2, { json: !!args.json, autoApprove, agentSettings: agent3
|
|
80985
|
+
await watchTodo(ws2, todoId, projectId2, { json: !!args.json, autoApprove, agentSettings: agent3 });
|
|
80607
80986
|
}
|
|
80608
80987
|
if (!args["non-interactive"]) {
|
|
80609
80988
|
await interactiveLoop(ws2, api2, todoId, projectId2, agent3, !!args.json, autoApprove, cfg);
|
|
@@ -80625,6 +81004,7 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
80625
81004
|
if (mayfly) {
|
|
80626
81005
|
process.stderr.write(`${DIM2}Isolated bridge:${RESET2} ${CYAN2}mayfly-${isolatedTodoId}${RESET2}
|
|
80627
81006
|
`);
|
|
81007
|
+
stopRunOnExit();
|
|
80628
81008
|
onShutdown(mayfly.stop);
|
|
80629
81009
|
mayfly.child.on("exit", (code, sig) => {
|
|
80630
81010
|
if (!mayfly.stopped())
|
|
@@ -80764,8 +81144,7 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
80764
81144
|
await watchTodo(ws, actualTodoId, projectId, {
|
|
80765
81145
|
json: !!args.json,
|
|
80766
81146
|
autoApprove,
|
|
80767
|
-
agentSettings: agent2
|
|
80768
|
-
exitOnInterrupt: !!args["non-interactive"]
|
|
81147
|
+
agentSettings: agent2
|
|
80769
81148
|
});
|
|
80770
81149
|
if (!args["non-interactive"]) {
|
|
80771
81150
|
process.stderr.write(`
|