@task0/cli 0.7.0 → 0.8.0
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/dist/main.js +753 -616
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -492,6 +492,90 @@ var init_task_state = __esm({
|
|
|
492
492
|
}
|
|
493
493
|
});
|
|
494
494
|
|
|
495
|
+
// ../../packages/shared/dist/node/paths.js
|
|
496
|
+
import fs5 from "fs";
|
|
497
|
+
import os2 from "os";
|
|
498
|
+
import path5 from "path";
|
|
499
|
+
function task0Home() {
|
|
500
|
+
const override = process.env.TASK0_HOME;
|
|
501
|
+
if (override && override.length > 0)
|
|
502
|
+
return override;
|
|
503
|
+
return path5.join(os2.homedir(), ".task0");
|
|
504
|
+
}
|
|
505
|
+
function profilesRoot() {
|
|
506
|
+
return path5.join(task0Home(), "profiles");
|
|
507
|
+
}
|
|
508
|
+
function currentProfileName() {
|
|
509
|
+
const fromEnv = process.env.TASK0_PROFILE?.trim();
|
|
510
|
+
if (fromEnv && PROFILE_NAME_RE.test(fromEnv))
|
|
511
|
+
return fromEnv;
|
|
512
|
+
const fromFile = readCurrentProfileFile();
|
|
513
|
+
if (fromFile && PROFILE_NAME_RE.test(fromFile))
|
|
514
|
+
return fromFile;
|
|
515
|
+
return DEFAULT_PROFILE_NAME;
|
|
516
|
+
}
|
|
517
|
+
function readCurrentProfileFile() {
|
|
518
|
+
const file = path5.join(profilesRoot(), CURRENT_PROFILE_FILE);
|
|
519
|
+
if (!fs5.existsSync(file))
|
|
520
|
+
return null;
|
|
521
|
+
try {
|
|
522
|
+
return fs5.readFileSync(file, "utf-8").trim() || null;
|
|
523
|
+
} catch {
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function currentProfileFilePath() {
|
|
528
|
+
return path5.join(profilesRoot(), CURRENT_PROFILE_FILE);
|
|
529
|
+
}
|
|
530
|
+
function writeCurrentProfile(name) {
|
|
531
|
+
fs5.mkdirSync(profilesRoot(), { recursive: true });
|
|
532
|
+
const file = currentProfileFilePath();
|
|
533
|
+
if (name === null) {
|
|
534
|
+
if (fs5.existsSync(file))
|
|
535
|
+
fs5.unlinkSync(file);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (!PROFILE_NAME_RE.test(name)) {
|
|
539
|
+
throw new Error(`Invalid profile name "${name}". Must match ${PROFILE_NAME_RE}.`);
|
|
540
|
+
}
|
|
541
|
+
fs5.writeFileSync(file, name + "\n", "utf-8");
|
|
542
|
+
}
|
|
543
|
+
function profileDir(name) {
|
|
544
|
+
const resolved = name ?? currentProfileName();
|
|
545
|
+
if (!PROFILE_NAME_RE.test(resolved)) {
|
|
546
|
+
throw new Error(`Invalid profile name "${resolved}". Must match ${PROFILE_NAME_RE}.`);
|
|
547
|
+
}
|
|
548
|
+
return path5.join(profilesRoot(), resolved);
|
|
549
|
+
}
|
|
550
|
+
function listProfileNames() {
|
|
551
|
+
const root = profilesRoot();
|
|
552
|
+
if (!fs5.existsSync(root))
|
|
553
|
+
return [];
|
|
554
|
+
try {
|
|
555
|
+
return fs5.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && PROFILE_NAME_RE.test(entry.name)).map((entry) => entry.name).sort();
|
|
556
|
+
} catch {
|
|
557
|
+
return [];
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function isValidProfileName(name) {
|
|
561
|
+
return PROFILE_NAME_RE.test(name);
|
|
562
|
+
}
|
|
563
|
+
function serverWorkingDir() {
|
|
564
|
+
const fromEnv = process.env.TASK0_SERVER_DIR?.trim();
|
|
565
|
+
if (fromEnv)
|
|
566
|
+
return fromEnv;
|
|
567
|
+
return path5.join(task0Home(), "server");
|
|
568
|
+
}
|
|
569
|
+
var DEFAULT_PROFILE_NAME, CURRENT_PROFILE_FILE, PROFILE_NAME_RE;
|
|
570
|
+
var init_paths = __esm({
|
|
571
|
+
"../../packages/shared/dist/node/paths.js"() {
|
|
572
|
+
"use strict";
|
|
573
|
+
DEFAULT_PROFILE_NAME = "default";
|
|
574
|
+
CURRENT_PROFILE_FILE = ".current";
|
|
575
|
+
PROFILE_NAME_RE = /^[a-zA-Z0-9_-]+$/;
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
|
|
495
579
|
// ../../packages/shared/dist/types/error-report.js
|
|
496
580
|
var ERROR_REPORT_SCHEMA_VERSION;
|
|
497
581
|
var init_error_report = __esm({
|
|
@@ -583,19 +667,12 @@ var init_redact = __esm({
|
|
|
583
667
|
});
|
|
584
668
|
|
|
585
669
|
// ../../packages/shared/dist/node/error-reports.js
|
|
586
|
-
import
|
|
587
|
-
import
|
|
588
|
-
import path5 from "path";
|
|
670
|
+
import fs6 from "fs";
|
|
671
|
+
import path6 from "path";
|
|
589
672
|
import { spawnSync } from "child_process";
|
|
590
673
|
import crypto from "crypto";
|
|
591
|
-
function task0Home() {
|
|
592
|
-
const override = process.env.TASK0_HOME;
|
|
593
|
-
if (override && override.length > 0)
|
|
594
|
-
return override;
|
|
595
|
-
return path5.join(os2.homedir(), ".task0");
|
|
596
|
-
}
|
|
597
674
|
function errorsRoot() {
|
|
598
|
-
return
|
|
675
|
+
return path6.join(profileDir(), "errors");
|
|
599
676
|
}
|
|
600
677
|
function createErrorReportId() {
|
|
601
678
|
return `err_${crypto.randomBytes(4).toString("hex")}`;
|
|
@@ -696,13 +773,13 @@ function buildErrorReport(input) {
|
|
|
696
773
|
function writeErrorReportSync(report, rootOverride) {
|
|
697
774
|
const root = rootOverride ?? errorsRoot();
|
|
698
775
|
const dirName = errorReportDirName(new Date(report.captured_at), report.id);
|
|
699
|
-
const dir =
|
|
700
|
-
|
|
701
|
-
const finalPath =
|
|
702
|
-
const tmpPath =
|
|
776
|
+
const dir = path6.join(root, dirName);
|
|
777
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
778
|
+
const finalPath = path6.join(dir, REPORT_FILENAME);
|
|
779
|
+
const tmpPath = path6.join(dir, TMP_FILENAME);
|
|
703
780
|
const json = JSON.stringify(report, null, 2);
|
|
704
|
-
|
|
705
|
-
|
|
781
|
+
fs6.writeFileSync(tmpPath, json, "utf-8");
|
|
782
|
+
fs6.renameSync(tmpPath, finalPath);
|
|
706
783
|
return { dir, path: finalPath };
|
|
707
784
|
}
|
|
708
785
|
function parseReportDir(name) {
|
|
@@ -719,7 +796,7 @@ function listErrorReports(rootOverride) {
|
|
|
719
796
|
};
|
|
720
797
|
let entries;
|
|
721
798
|
try {
|
|
722
|
-
entries =
|
|
799
|
+
entries = fs6.readdirSync(root, { withFileTypes: true });
|
|
723
800
|
} catch (err) {
|
|
724
801
|
if (err.code === "ENOENT")
|
|
725
802
|
return result;
|
|
@@ -731,11 +808,11 @@ function listErrorReports(rootOverride) {
|
|
|
731
808
|
const parsed = parseReportDir(entry.name);
|
|
732
809
|
if (!parsed)
|
|
733
810
|
continue;
|
|
734
|
-
const dir =
|
|
735
|
-
const file =
|
|
811
|
+
const dir = path6.join(root, entry.name);
|
|
812
|
+
const file = path6.join(dir, REPORT_FILENAME);
|
|
736
813
|
let raw;
|
|
737
814
|
try {
|
|
738
|
-
raw =
|
|
815
|
+
raw = fs6.readFileSync(file, "utf-8");
|
|
739
816
|
} catch {
|
|
740
817
|
result.skipped.unreadable += 1;
|
|
741
818
|
continue;
|
|
@@ -753,7 +830,7 @@ function listErrorReports(rootOverride) {
|
|
|
753
830
|
}
|
|
754
831
|
let size = 0;
|
|
755
832
|
try {
|
|
756
|
-
size =
|
|
833
|
+
size = fs6.statSync(file).size;
|
|
757
834
|
} catch {
|
|
758
835
|
}
|
|
759
836
|
result.reports.push({
|
|
@@ -788,7 +865,7 @@ function resolveErrorReport(query, rootOverride) {
|
|
|
788
865
|
return { kind: "miss", query };
|
|
789
866
|
}
|
|
790
867
|
function readErrorReport(summary) {
|
|
791
|
-
const raw =
|
|
868
|
+
const raw = fs6.readFileSync(summary.path, "utf-8");
|
|
792
869
|
return JSON.parse(raw);
|
|
793
870
|
}
|
|
794
871
|
function pruneErrorReports(opts, rootOverride) {
|
|
@@ -826,7 +903,7 @@ function pruneErrorReports(opts, rootOverride) {
|
|
|
826
903
|
}
|
|
827
904
|
function removeReportDir(dir) {
|
|
828
905
|
try {
|
|
829
|
-
|
|
906
|
+
fs6.rmSync(dir, { recursive: true, force: true });
|
|
830
907
|
return true;
|
|
831
908
|
} catch {
|
|
832
909
|
return false;
|
|
@@ -837,6 +914,7 @@ var init_error_reports = __esm({
|
|
|
837
914
|
"../../packages/shared/dist/node/error-reports.js"() {
|
|
838
915
|
"use strict";
|
|
839
916
|
init_error_report();
|
|
917
|
+
init_paths();
|
|
840
918
|
init_redact();
|
|
841
919
|
REPORT_FILENAME = "report.json";
|
|
842
920
|
TMP_FILENAME = "report.json.tmp";
|
|
@@ -844,9 +922,9 @@ var init_error_reports = __esm({
|
|
|
844
922
|
});
|
|
845
923
|
|
|
846
924
|
// ../../packages/shared/dist/node/file-lock.js
|
|
847
|
-
import
|
|
925
|
+
import fs7 from "fs";
|
|
848
926
|
import os3 from "os";
|
|
849
|
-
import
|
|
927
|
+
import path7 from "path";
|
|
850
928
|
var init_file_lock = __esm({
|
|
851
929
|
"../../packages/shared/dist/node/file-lock.js"() {
|
|
852
930
|
"use strict";
|
|
@@ -862,8 +940,8 @@ var init_tmux = __esm({
|
|
|
862
940
|
});
|
|
863
941
|
|
|
864
942
|
// ../../packages/shared/dist/node/agent-skills.js
|
|
865
|
-
import
|
|
866
|
-
import
|
|
943
|
+
import fs8 from "fs";
|
|
944
|
+
import path8 from "path";
|
|
867
945
|
import yaml3 from "js-yaml";
|
|
868
946
|
function isRecord(value) {
|
|
869
947
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -881,9 +959,9 @@ function extractFrontmatter(raw) {
|
|
|
881
959
|
}
|
|
882
960
|
function readTextIfExists(filePath) {
|
|
883
961
|
try {
|
|
884
|
-
if (!
|
|
962
|
+
if (!fs8.existsSync(filePath))
|
|
885
963
|
return null;
|
|
886
|
-
return
|
|
964
|
+
return fs8.readFileSync(filePath, "utf-8");
|
|
887
965
|
} catch {
|
|
888
966
|
return null;
|
|
889
967
|
}
|
|
@@ -891,13 +969,13 @@ function readTextIfExists(filePath) {
|
|
|
891
969
|
function getSymlinkInfo(...candidatePaths) {
|
|
892
970
|
for (const candidatePath of candidatePaths) {
|
|
893
971
|
try {
|
|
894
|
-
const stat =
|
|
972
|
+
const stat = fs8.lstatSync(candidatePath);
|
|
895
973
|
if (!stat.isSymbolicLink())
|
|
896
974
|
continue;
|
|
897
|
-
const target =
|
|
975
|
+
const target = fs8.readlinkSync(candidatePath);
|
|
898
976
|
return {
|
|
899
977
|
isSymlink: true,
|
|
900
|
-
symlinkTarget:
|
|
978
|
+
symlinkTarget: path8.isAbsolute(target) ? target : path8.resolve(path8.dirname(candidatePath), target)
|
|
901
979
|
};
|
|
902
980
|
} catch {
|
|
903
981
|
}
|
|
@@ -930,7 +1008,7 @@ function normalizeBoolean(value) {
|
|
|
930
1008
|
function sortSkills(skills) {
|
|
931
1009
|
return skills.sort((a, b) => AGENT_ORDER[a.agent] - AGENT_ORDER[b.agent] || SCOPE_ORDER[a.scope] - SCOPE_ORDER[b.scope] || KIND_ORDER[a.kind] - KIND_ORDER[b.kind] || a.name.localeCompare(b.name) || a.filePath.localeCompare(b.filePath));
|
|
932
1010
|
}
|
|
933
|
-
function pushInstructionIfExists(skills, agent2, scope, filePath, name =
|
|
1011
|
+
function pushInstructionIfExists(skills, agent2, scope, filePath, name = path8.basename(filePath), description2 = "", kind = "instruction") {
|
|
934
1012
|
const raw = readTextIfExists(filePath);
|
|
935
1013
|
if (raw === null)
|
|
936
1014
|
return;
|
|
@@ -947,17 +1025,17 @@ function pushInstructionIfExists(skills, agent2, scope, filePath, name = path7.b
|
|
|
947
1025
|
function scanClaudeSkillDir(skills, skillsDir, scope) {
|
|
948
1026
|
let entries = [];
|
|
949
1027
|
try {
|
|
950
|
-
if (!
|
|
1028
|
+
if (!fs8.existsSync(skillsDir))
|
|
951
1029
|
return;
|
|
952
|
-
entries =
|
|
1030
|
+
entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
|
|
953
1031
|
} catch {
|
|
954
1032
|
return;
|
|
955
1033
|
}
|
|
956
1034
|
for (const entry of entries) {
|
|
957
1035
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
958
1036
|
continue;
|
|
959
|
-
const skillDirPath =
|
|
960
|
-
const skillFilePath =
|
|
1037
|
+
const skillDirPath = path8.join(skillsDir, entry.name);
|
|
1038
|
+
const skillFilePath = path8.join(skillDirPath, "SKILL.md");
|
|
961
1039
|
const raw = readTextIfExists(skillFilePath);
|
|
962
1040
|
if (raw === null)
|
|
963
1041
|
continue;
|
|
@@ -978,7 +1056,7 @@ function scanCursorRuleFile(skills, filePath, scope) {
|
|
|
978
1056
|
if (raw === null)
|
|
979
1057
|
return;
|
|
980
1058
|
const frontmatter = extractFrontmatter(raw);
|
|
981
|
-
const baseName =
|
|
1059
|
+
const baseName = path8.basename(filePath, ".mdc");
|
|
982
1060
|
skills.push({
|
|
983
1061
|
agent: "cursor",
|
|
984
1062
|
scope,
|
|
@@ -994,9 +1072,9 @@ function scanCursorRuleFile(skills, filePath, scope) {
|
|
|
994
1072
|
function scanCursorRulesDir(skills, rulesDir, scope) {
|
|
995
1073
|
let entries = [];
|
|
996
1074
|
try {
|
|
997
|
-
if (!
|
|
1075
|
+
if (!fs8.existsSync(rulesDir))
|
|
998
1076
|
return;
|
|
999
|
-
entries =
|
|
1077
|
+
entries = fs8.readdirSync(rulesDir, { withFileTypes: true });
|
|
1000
1078
|
} catch {
|
|
1001
1079
|
return;
|
|
1002
1080
|
}
|
|
@@ -1005,26 +1083,26 @@ function scanCursorRulesDir(skills, rulesDir, scope) {
|
|
|
1005
1083
|
continue;
|
|
1006
1084
|
if (!entry.isFile() && !entry.isSymbolicLink())
|
|
1007
1085
|
continue;
|
|
1008
|
-
scanCursorRuleFile(skills,
|
|
1086
|
+
scanCursorRuleFile(skills, path8.join(rulesDir, entry.name), scope);
|
|
1009
1087
|
}
|
|
1010
1088
|
}
|
|
1011
1089
|
function getProjectAgentSkills(projectPath) {
|
|
1012
|
-
const absProjectPath =
|
|
1090
|
+
const absProjectPath = path8.resolve(projectPath);
|
|
1013
1091
|
let projectStat;
|
|
1014
1092
|
try {
|
|
1015
|
-
projectStat =
|
|
1093
|
+
projectStat = fs8.statSync(absProjectPath);
|
|
1016
1094
|
} catch {
|
|
1017
1095
|
return [];
|
|
1018
1096
|
}
|
|
1019
1097
|
if (!projectStat.isDirectory())
|
|
1020
1098
|
return [];
|
|
1021
1099
|
const skills = [];
|
|
1022
|
-
scanClaudeSkillDir(skills,
|
|
1023
|
-
pushInstructionIfExists(skills, "claude_code", "project",
|
|
1024
|
-
pushInstructionIfExists(skills, "claude_code", "project",
|
|
1025
|
-
pushInstructionIfExists(skills, "codex", "project",
|
|
1026
|
-
scanCursorRulesDir(skills,
|
|
1027
|
-
pushInstructionIfExists(skills, "cursor", "project",
|
|
1100
|
+
scanClaudeSkillDir(skills, path8.join(absProjectPath, ".claude", "skills"), "project");
|
|
1101
|
+
pushInstructionIfExists(skills, "claude_code", "project", path8.join(absProjectPath, "CLAUDE.md"));
|
|
1102
|
+
pushInstructionIfExists(skills, "claude_code", "project", path8.join(absProjectPath, "AGENTS.md"));
|
|
1103
|
+
pushInstructionIfExists(skills, "codex", "project", path8.join(absProjectPath, "AGENTS.md"));
|
|
1104
|
+
scanCursorRulesDir(skills, path8.join(absProjectPath, ".cursor", "rules"), "project");
|
|
1105
|
+
pushInstructionIfExists(skills, "cursor", "project", path8.join(absProjectPath, ".cursorrules"), "Legacy Cursor Rules", "", "rule");
|
|
1028
1106
|
return sortSkills(skills);
|
|
1029
1107
|
}
|
|
1030
1108
|
function getGlobalAgentSkills() {
|
|
@@ -1032,8 +1110,8 @@ function getGlobalAgentSkills() {
|
|
|
1032
1110
|
if (!homeDir)
|
|
1033
1111
|
return [];
|
|
1034
1112
|
const skills = [];
|
|
1035
|
-
scanClaudeSkillDir(skills,
|
|
1036
|
-
scanCursorRulesDir(skills,
|
|
1113
|
+
scanClaudeSkillDir(skills, path8.join(homeDir, ".claude", "skills"), "global");
|
|
1114
|
+
scanCursorRulesDir(skills, path8.join(homeDir, ".cursor", "rules"), "global");
|
|
1037
1115
|
return sortSkills(skills);
|
|
1038
1116
|
}
|
|
1039
1117
|
var AGENT_ORDER, SCOPE_ORDER, KIND_ORDER;
|
|
@@ -1065,6 +1143,7 @@ var init_node = __esm({
|
|
|
1065
1143
|
init_scanner();
|
|
1066
1144
|
init_open_questions();
|
|
1067
1145
|
init_task_state();
|
|
1146
|
+
init_paths();
|
|
1068
1147
|
init_error_reports();
|
|
1069
1148
|
init_redact();
|
|
1070
1149
|
init_file_lock();
|
|
@@ -1089,7 +1168,7 @@ __export(task_state_exports, {
|
|
|
1089
1168
|
withTaskYamlLock: () => withTaskYamlLock,
|
|
1090
1169
|
writeTaskYaml: () => writeTaskYaml
|
|
1091
1170
|
});
|
|
1092
|
-
import
|
|
1171
|
+
import fs15 from "fs";
|
|
1093
1172
|
function readWorkflow(taskYml) {
|
|
1094
1173
|
return readTaskWorkflow(taskYml);
|
|
1095
1174
|
}
|
|
@@ -1098,14 +1177,14 @@ async function updateWorkflow(taskYml, patch) {
|
|
|
1098
1177
|
}
|
|
1099
1178
|
function nextArtifactIndex(taskDir, prefix, ext = "md") {
|
|
1100
1179
|
const pattern = new RegExp(`^${prefix}-(\\d+).*\\.${ext}$`);
|
|
1101
|
-
const entries =
|
|
1180
|
+
const entries = fs15.readdirSync(taskDir);
|
|
1102
1181
|
const indices = entries.map((name) => name.match(pattern)?.[1]).filter((v) => v != null).map(Number);
|
|
1103
1182
|
const next = indices.length > 0 ? Math.max(...indices) + 1 : 1;
|
|
1104
1183
|
return String(next).padStart(2, "0");
|
|
1105
1184
|
}
|
|
1106
1185
|
function latestArtifact(taskDir, pattern) {
|
|
1107
|
-
if (!
|
|
1108
|
-
const matches =
|
|
1186
|
+
if (!fs15.existsSync(taskDir)) return null;
|
|
1187
|
+
const matches = fs15.readdirSync(taskDir).filter((name) => pattern.test(name));
|
|
1109
1188
|
if (matches.length === 0) return null;
|
|
1110
1189
|
matches.sort();
|
|
1111
1190
|
return matches[matches.length - 1] || null;
|
|
@@ -1118,115 +1197,42 @@ var init_task_state2 = __esm({
|
|
|
1118
1197
|
});
|
|
1119
1198
|
|
|
1120
1199
|
// src/main.ts
|
|
1121
|
-
import { Command as
|
|
1122
|
-
import
|
|
1200
|
+
import { Command as Command25 } from "commander";
|
|
1201
|
+
import chalk25 from "chalk";
|
|
1202
|
+
|
|
1203
|
+
// src/core/profile.ts
|
|
1204
|
+
init_node();
|
|
1205
|
+
import fs10 from "fs";
|
|
1123
1206
|
|
|
1124
1207
|
// src/core/config.ts
|
|
1125
1208
|
init_node();
|
|
1126
|
-
import
|
|
1127
|
-
import
|
|
1128
|
-
import path8 from "path";
|
|
1209
|
+
import fs9 from "fs";
|
|
1210
|
+
import path9 from "path";
|
|
1129
1211
|
import yaml4 from "js-yaml";
|
|
1130
|
-
function
|
|
1131
|
-
return
|
|
1132
|
-
process.env.HOME || process.env.USERPROFILE || os4.homedir(),
|
|
1133
|
-
".config",
|
|
1134
|
-
"task0"
|
|
1135
|
-
);
|
|
1136
|
-
}
|
|
1137
|
-
function registryFile() {
|
|
1138
|
-
return path8.join(registryDir(), "config.yml");
|
|
1139
|
-
}
|
|
1140
|
-
function homeStateFile() {
|
|
1141
|
-
return path8.join(task0Home(), "config.yml");
|
|
1212
|
+
function configFile() {
|
|
1213
|
+
return path9.join(profileDir(), "config.yml");
|
|
1142
1214
|
}
|
|
1143
|
-
function
|
|
1144
|
-
return
|
|
1215
|
+
function defaultConfig() {
|
|
1216
|
+
return { sources: [] };
|
|
1145
1217
|
}
|
|
1146
|
-
function
|
|
1147
|
-
|
|
1218
|
+
function loadConfig() {
|
|
1219
|
+
const file = configFile();
|
|
1220
|
+
if (!fs9.existsSync(file)) return defaultConfig();
|
|
1148
1221
|
try {
|
|
1149
|
-
const raw =
|
|
1150
|
-
const
|
|
1151
|
-
return
|
|
1222
|
+
const raw = fs9.readFileSync(file, "utf-8");
|
|
1223
|
+
const data = yaml4.load(raw) ?? {};
|
|
1224
|
+
return {
|
|
1225
|
+
...data,
|
|
1226
|
+
sources: Array.isArray(data?.sources) ? data.sources : []
|
|
1227
|
+
};
|
|
1152
1228
|
} catch {
|
|
1153
|
-
return
|
|
1229
|
+
return defaultConfig();
|
|
1154
1230
|
}
|
|
1155
1231
|
}
|
|
1156
|
-
function writeYamlFile(file, data) {
|
|
1157
|
-
fs8.mkdirSync(path8.dirname(file), { recursive: true });
|
|
1158
|
-
fs8.writeFileSync(file, yaml4.dump(data, { lineWidth: 120 }), "utf-8");
|
|
1159
|
-
}
|
|
1160
|
-
function loadRegistry() {
|
|
1161
|
-
return readYamlFile(registryFile()) ?? {};
|
|
1162
|
-
}
|
|
1163
|
-
function saveRegistry(data) {
|
|
1164
|
-
writeYamlFile(registryFile(), data);
|
|
1165
|
-
}
|
|
1166
|
-
function loadHomeState() {
|
|
1167
|
-
return readYamlFile(homeStateFile()) ?? {};
|
|
1168
|
-
}
|
|
1169
|
-
function saveHomeState(data) {
|
|
1170
|
-
writeYamlFile(homeStateFile(), data);
|
|
1171
|
-
}
|
|
1172
|
-
function loadConfig() {
|
|
1173
|
-
const registry = loadRegistry();
|
|
1174
|
-
const home = loadHomeState();
|
|
1175
|
-
const profiles = registry.profiles && typeof registry.profiles === "object" && !Array.isArray(registry.profiles) ? registry.profiles : void 0;
|
|
1176
|
-
const current = typeof registry.current_profile === "string" && registry.current_profile.length > 0 ? registry.current_profile : void 0;
|
|
1177
|
-
const sources = Array.isArray(home.sources) ? home.sources : Array.isArray(registry.sources) ? registry.sources : [];
|
|
1178
|
-
const agentModels = home.agentModels !== void 0 ? home.agentModels : registry.agentModels;
|
|
1179
|
-
return {
|
|
1180
|
-
sources,
|
|
1181
|
-
...agentModels !== void 0 ? { agentModels } : {},
|
|
1182
|
-
...profiles ? { profiles } : {},
|
|
1183
|
-
...current ? { current_profile: current } : {}
|
|
1184
|
-
};
|
|
1185
|
-
}
|
|
1186
1232
|
function saveConfig(config) {
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
...sources !== void 0 ? { sources } : {},
|
|
1191
|
-
...agentModels !== void 0 ? { agentModels } : {}
|
|
1192
|
-
});
|
|
1193
|
-
}
|
|
1194
|
-
function listProfiles() {
|
|
1195
|
-
return loadConfig().profiles ?? {};
|
|
1196
|
-
}
|
|
1197
|
-
function getProfile(name) {
|
|
1198
|
-
return listProfiles()[name];
|
|
1199
|
-
}
|
|
1200
|
-
function getCurrentProfileName() {
|
|
1201
|
-
return loadConfig().current_profile;
|
|
1202
|
-
}
|
|
1203
|
-
function addProfile(name, entry) {
|
|
1204
|
-
const config = loadConfig();
|
|
1205
|
-
const profiles = { ...config.profiles ?? {} };
|
|
1206
|
-
profiles[name] = entry;
|
|
1207
|
-
config.profiles = profiles;
|
|
1208
|
-
saveConfig(config);
|
|
1209
|
-
}
|
|
1210
|
-
function removeProfile(name) {
|
|
1211
|
-
const config = loadConfig();
|
|
1212
|
-
if (!config.profiles || !(name in config.profiles)) return false;
|
|
1213
|
-
const profiles = { ...config.profiles };
|
|
1214
|
-
delete profiles[name];
|
|
1215
|
-
config.profiles = profiles;
|
|
1216
|
-
if (config.current_profile === name) {
|
|
1217
|
-
delete config.current_profile;
|
|
1218
|
-
}
|
|
1219
|
-
saveConfig(config);
|
|
1220
|
-
return true;
|
|
1221
|
-
}
|
|
1222
|
-
function setCurrentProfile(name) {
|
|
1223
|
-
const config = loadConfig();
|
|
1224
|
-
if (name === null) {
|
|
1225
|
-
delete config.current_profile;
|
|
1226
|
-
} else {
|
|
1227
|
-
config.current_profile = name;
|
|
1228
|
-
}
|
|
1229
|
-
saveConfig(config);
|
|
1233
|
+
const file = configFile();
|
|
1234
|
+
fs9.mkdirSync(path9.dirname(file), { recursive: true });
|
|
1235
|
+
fs9.writeFileSync(file, yaml4.dump(config, { lineWidth: 120 }), "utf-8");
|
|
1230
1236
|
}
|
|
1231
1237
|
function addSource(entry) {
|
|
1232
1238
|
const config = loadConfig();
|
|
@@ -1246,6 +1252,9 @@ function removeSource(name) {
|
|
|
1246
1252
|
saveConfig(config);
|
|
1247
1253
|
return true;
|
|
1248
1254
|
}
|
|
1255
|
+
function getApiUrl() {
|
|
1256
|
+
return loadConfig().api_url;
|
|
1257
|
+
}
|
|
1249
1258
|
|
|
1250
1259
|
// src/core/profile.ts
|
|
1251
1260
|
var ProfileNotFoundError = class extends Error {
|
|
@@ -1284,42 +1293,230 @@ function isProfileSubcommandInvocation(argv) {
|
|
|
1284
1293
|
}
|
|
1285
1294
|
return true;
|
|
1286
1295
|
}
|
|
1287
|
-
function
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1296
|
+
function ensureDefaultProfile() {
|
|
1297
|
+
fs10.mkdirSync(profileDir(DEFAULT_PROFILE_NAME), { recursive: true });
|
|
1298
|
+
fs10.mkdirSync(profilesRoot(), { recursive: true });
|
|
1299
|
+
}
|
|
1300
|
+
function profileExists(name) {
|
|
1301
|
+
if (!isValidProfileName(name)) return false;
|
|
1302
|
+
try {
|
|
1303
|
+
return fs10.statSync(profileDir(name)).isDirectory();
|
|
1304
|
+
} catch {
|
|
1305
|
+
return false;
|
|
1293
1306
|
}
|
|
1294
|
-
return { name: requested, entry };
|
|
1295
1307
|
}
|
|
1296
1308
|
var activeProfileCache = null;
|
|
1297
1309
|
function activateProfile(argv) {
|
|
1310
|
+
ensureDefaultProfile();
|
|
1298
1311
|
const flagValue = parseProfileFlag(argv);
|
|
1299
|
-
let
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1312
|
+
let name;
|
|
1313
|
+
if (flagValue !== null) {
|
|
1314
|
+
if (!isValidProfileName(flagValue) || !profileExists(flagValue)) {
|
|
1315
|
+
if (!isProfileSubcommandInvocation(argv)) {
|
|
1316
|
+
throw new ProfileNotFoundError(flagValue, listProfileNames());
|
|
1317
|
+
}
|
|
1318
|
+
name = DEFAULT_PROFILE_NAME;
|
|
1305
1319
|
} else {
|
|
1306
|
-
|
|
1320
|
+
name = flagValue;
|
|
1321
|
+
}
|
|
1322
|
+
} else {
|
|
1323
|
+
name = currentProfileNameFromEnvOrFile();
|
|
1324
|
+
if (!profileExists(name)) {
|
|
1325
|
+
if (!isProfileSubcommandInvocation(argv)) {
|
|
1326
|
+
throw new ProfileNotFoundError(name, listProfileNames());
|
|
1327
|
+
}
|
|
1328
|
+
name = DEFAULT_PROFILE_NAME;
|
|
1307
1329
|
}
|
|
1308
1330
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1331
|
+
process.env.TASK0_PROFILE = name;
|
|
1332
|
+
const apiUrl = getApiUrl();
|
|
1333
|
+
activeProfileCache = { name, ...apiUrl ? { apiUrl } : {} };
|
|
1334
|
+
if (apiUrl && !process.env.TASK0_API_URL) {
|
|
1335
|
+
process.env.TASK0_API_URL = apiUrl;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
function currentProfileNameFromEnvOrFile() {
|
|
1339
|
+
const fromEnv = process.env.TASK0_PROFILE?.trim();
|
|
1340
|
+
if (fromEnv && isValidProfileName(fromEnv)) return fromEnv;
|
|
1341
|
+
try {
|
|
1342
|
+
const file = `${profilesRoot()}/.current`;
|
|
1343
|
+
if (fs10.existsSync(file)) {
|
|
1344
|
+
const v = fs10.readFileSync(file, "utf-8").trim();
|
|
1345
|
+
if (v && isValidProfileName(v)) return v;
|
|
1313
1346
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1347
|
+
} catch {
|
|
1348
|
+
}
|
|
1349
|
+
return DEFAULT_PROFILE_NAME;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// src/core/migrate-layout.ts
|
|
1353
|
+
init_node();
|
|
1354
|
+
import fs11 from "fs";
|
|
1355
|
+
import os4 from "os";
|
|
1356
|
+
import path10 from "path";
|
|
1357
|
+
import yaml5 from "js-yaml";
|
|
1358
|
+
var MIGRATION_MARKER = ".migrated-v1";
|
|
1359
|
+
function legacyDaemonJson() {
|
|
1360
|
+
return path10.join(task0Home(), "daemon.json");
|
|
1361
|
+
}
|
|
1362
|
+
function legacyErrorsDir() {
|
|
1363
|
+
return path10.join(task0Home(), "errors");
|
|
1364
|
+
}
|
|
1365
|
+
function legacyAgentRunDir() {
|
|
1366
|
+
return path10.join(task0Home(), "agent-run");
|
|
1367
|
+
}
|
|
1368
|
+
function legacyLogsDir() {
|
|
1369
|
+
return path10.join(task0Home(), "logs");
|
|
1370
|
+
}
|
|
1371
|
+
function legacyServerConfigYml() {
|
|
1372
|
+
return path10.join(task0Home(), "config.yml");
|
|
1373
|
+
}
|
|
1374
|
+
function legacyRegistryDir() {
|
|
1375
|
+
return path10.join(process.env.HOME || process.env.USERPROFILE || os4.homedir(), ".config", "task0");
|
|
1376
|
+
}
|
|
1377
|
+
function legacyRegistryFile() {
|
|
1378
|
+
return path10.join(legacyRegistryDir(), "config.yml");
|
|
1379
|
+
}
|
|
1380
|
+
function migrationMarkerPath() {
|
|
1381
|
+
return path10.join(profilesRoot(), MIGRATION_MARKER);
|
|
1382
|
+
}
|
|
1383
|
+
function safeMv(from, to, steps, opts = {}) {
|
|
1384
|
+
if (!fs11.existsSync(from)) return false;
|
|
1385
|
+
if (fs11.existsSync(to)) {
|
|
1386
|
+
steps.push({ kind: "skip", from, to, detail: "destination already exists" });
|
|
1387
|
+
return false;
|
|
1388
|
+
}
|
|
1389
|
+
if (!opts.dryRun) {
|
|
1390
|
+
fs11.mkdirSync(path10.dirname(to), { recursive: true });
|
|
1391
|
+
fs11.renameSync(from, to);
|
|
1392
|
+
}
|
|
1393
|
+
steps.push({ kind: "mv", from, to });
|
|
1394
|
+
return true;
|
|
1395
|
+
}
|
|
1396
|
+
function readYamlSafe(file) {
|
|
1397
|
+
if (!fs11.existsSync(file)) return null;
|
|
1398
|
+
try {
|
|
1399
|
+
const raw = fs11.readFileSync(file, "utf-8");
|
|
1400
|
+
return yaml5.load(raw) ?? null;
|
|
1401
|
+
} catch {
|
|
1402
|
+
return null;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
function extractCliKeysFromLegacyConfig() {
|
|
1406
|
+
const data = readYamlSafe(legacyServerConfigYml());
|
|
1407
|
+
if (!data || typeof data !== "object") return null;
|
|
1408
|
+
const out = {};
|
|
1409
|
+
if (Array.isArray(data.sources)) {
|
|
1410
|
+
const projects = data.sources.filter((s) => s?.type === "project");
|
|
1411
|
+
if (projects.length > 0) out.sources = projects;
|
|
1412
|
+
}
|
|
1413
|
+
if (data.agentModels && typeof data.agentModels === "object") {
|
|
1414
|
+
out.agentModels = data.agentModels;
|
|
1415
|
+
}
|
|
1416
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
1417
|
+
}
|
|
1418
|
+
function reconstructProfilesFromRegistry(steps, opts = {}) {
|
|
1419
|
+
const file = legacyRegistryFile();
|
|
1420
|
+
const data = readYamlSafe(file);
|
|
1421
|
+
if (!data?.profiles || typeof data.profiles !== "object") return;
|
|
1422
|
+
for (const [name, entry] of Object.entries(data.profiles)) {
|
|
1423
|
+
if (!isValidProfileName(name)) continue;
|
|
1424
|
+
const dir = profileDir(name);
|
|
1425
|
+
if (!opts.dryRun) fs11.mkdirSync(dir, { recursive: true });
|
|
1426
|
+
steps.push({ kind: "mkdir", to: dir });
|
|
1427
|
+
const profileConfig = {};
|
|
1428
|
+
if (entry.api_url) profileConfig.api_url = entry.api_url;
|
|
1429
|
+
const profileConfigFile = path10.join(dir, "config.yml");
|
|
1430
|
+
if (!fs11.existsSync(profileConfigFile) && Object.keys(profileConfig).length > 0) {
|
|
1431
|
+
if (!opts.dryRun) {
|
|
1432
|
+
fs11.writeFileSync(profileConfigFile, yaml5.dump(profileConfig, { lineWidth: 120 }), "utf-8");
|
|
1433
|
+
}
|
|
1434
|
+
steps.push({ kind: "write", to: profileConfigFile, detail: `api_url=${entry.api_url}` });
|
|
1435
|
+
}
|
|
1436
|
+
if (entry.task0_home) {
|
|
1437
|
+
const oldDaemonJson = path10.join(entry.task0_home, "daemon.json");
|
|
1438
|
+
const newDaemonJson = path10.join(dir, "daemon.json");
|
|
1439
|
+
if (fs11.existsSync(oldDaemonJson) && !fs11.existsSync(newDaemonJson)) {
|
|
1440
|
+
safeMv(oldDaemonJson, newDaemonJson, steps, opts);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
if (data.current_profile && isValidProfileName(data.current_profile)) {
|
|
1445
|
+
if (!opts.dryRun) writeCurrentProfile(data.current_profile);
|
|
1446
|
+
steps.push({ kind: "write", to: path10.join(profilesRoot(), ".current"), detail: data.current_profile });
|
|
1447
|
+
}
|
|
1448
|
+
const backup = file + ".migrated-bak";
|
|
1449
|
+
if (fs11.existsSync(file) && !fs11.existsSync(backup)) {
|
|
1450
|
+
if (!opts.dryRun) fs11.renameSync(file, backup);
|
|
1451
|
+
steps.push({ kind: "mv", from: file, to: backup, detail: "legacy v0.7.0 registry archived" });
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
function migrateCliLayout(opts = {}) {
|
|
1455
|
+
const result = { ran: false, steps: [], alreadyMigrated: false };
|
|
1456
|
+
if (fs11.existsSync(migrationMarkerPath())) {
|
|
1457
|
+
result.alreadyMigrated = true;
|
|
1458
|
+
return result;
|
|
1459
|
+
}
|
|
1460
|
+
if (!opts.dryRun) fs11.mkdirSync(profilesRoot(), { recursive: true });
|
|
1461
|
+
const defaultDir = profileDir(DEFAULT_PROFILE_NAME);
|
|
1462
|
+
if (!fs11.existsSync(defaultDir)) {
|
|
1463
|
+
if (!opts.dryRun) fs11.mkdirSync(defaultDir, { recursive: true });
|
|
1464
|
+
result.steps.push({ kind: "mkdir", to: defaultDir });
|
|
1465
|
+
}
|
|
1466
|
+
safeMv(legacyDaemonJson(), path10.join(defaultDir, "daemon.json"), result.steps, opts);
|
|
1467
|
+
safeMv(legacyErrorsDir(), path10.join(defaultDir, "errors"), result.steps, opts);
|
|
1468
|
+
safeMv(legacyAgentRunDir(), path10.join(defaultDir, "agent-run"), result.steps, opts);
|
|
1469
|
+
safeMv(legacyLogsDir(), path10.join(defaultDir, "logs"), result.steps, opts);
|
|
1470
|
+
const cliKeys = extractCliKeysFromLegacyConfig();
|
|
1471
|
+
if (cliKeys) {
|
|
1472
|
+
const target = path10.join(defaultDir, "config.yml");
|
|
1473
|
+
if (!fs11.existsSync(target)) {
|
|
1474
|
+
if (!opts.dryRun) {
|
|
1475
|
+
fs11.writeFileSync(target, yaml5.dump(cliKeys, { lineWidth: 120 }), "utf-8");
|
|
1476
|
+
}
|
|
1477
|
+
result.steps.push({ kind: "write", to: target, detail: "extracted CLI keys from legacy config.yml" });
|
|
1478
|
+
}
|
|
1479
|
+
const data = readYamlSafe(legacyServerConfigYml());
|
|
1480
|
+
if (data) {
|
|
1481
|
+
let dirty = false;
|
|
1482
|
+
if (Array.isArray(data.sources)) {
|
|
1483
|
+
const nonProjectOnly = data.sources.filter((s) => s?.type !== "project");
|
|
1484
|
+
if (nonProjectOnly.length !== data.sources.length) {
|
|
1485
|
+
data.sources = nonProjectOnly;
|
|
1486
|
+
dirty = true;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
if (data.agentModels) {
|
|
1490
|
+
delete data.agentModels;
|
|
1491
|
+
dirty = true;
|
|
1492
|
+
}
|
|
1493
|
+
if (dirty) {
|
|
1494
|
+
if (!opts.dryRun) {
|
|
1495
|
+
fs11.writeFileSync(legacyServerConfigYml(), yaml5.dump(data, { lineWidth: 120 }), "utf-8");
|
|
1496
|
+
}
|
|
1497
|
+
result.steps.push({ kind: "extract", from: legacyServerConfigYml(), detail: "stripped CLI-owned keys" });
|
|
1498
|
+
}
|
|
1316
1499
|
}
|
|
1317
1500
|
}
|
|
1501
|
+
if (fs11.existsSync(legacyRegistryFile())) {
|
|
1502
|
+
reconstructProfilesFromRegistry(result.steps, opts);
|
|
1503
|
+
}
|
|
1504
|
+
if (!opts.dryRun) {
|
|
1505
|
+
fs11.writeFileSync(migrationMarkerPath(), (/* @__PURE__ */ new Date()).toISOString() + "\n", "utf-8");
|
|
1506
|
+
}
|
|
1507
|
+
result.ran = result.steps.length > 0;
|
|
1508
|
+
return result;
|
|
1509
|
+
}
|
|
1510
|
+
function resetMigrationMarker() {
|
|
1511
|
+
const marker = migrationMarkerPath();
|
|
1512
|
+
if (!fs11.existsSync(marker)) return false;
|
|
1513
|
+
fs11.unlinkSync(marker);
|
|
1514
|
+
return true;
|
|
1318
1515
|
}
|
|
1319
1516
|
|
|
1320
1517
|
// src/commands/source.ts
|
|
1321
1518
|
import { Command } from "commander";
|
|
1322
|
-
import
|
|
1519
|
+
import path13 from "path";
|
|
1323
1520
|
import chalk from "chalk";
|
|
1324
1521
|
|
|
1325
1522
|
// src/types.ts
|
|
@@ -1327,10 +1524,10 @@ init_task();
|
|
|
1327
1524
|
|
|
1328
1525
|
// src/core/admin-token.ts
|
|
1329
1526
|
init_node();
|
|
1330
|
-
import
|
|
1331
|
-
import
|
|
1527
|
+
import fs12 from "fs";
|
|
1528
|
+
import path11 from "path";
|
|
1332
1529
|
function tokenFile() {
|
|
1333
|
-
return
|
|
1530
|
+
return path11.join(serverWorkingDir(), "admin.token");
|
|
1334
1531
|
}
|
|
1335
1532
|
var cached = null;
|
|
1336
1533
|
var AdminTokenUnavailableError = class extends Error {
|
|
@@ -1351,8 +1548,8 @@ function readAdminToken() {
|
|
|
1351
1548
|
return cached;
|
|
1352
1549
|
}
|
|
1353
1550
|
const file = tokenFile();
|
|
1354
|
-
if (
|
|
1355
|
-
const v =
|
|
1551
|
+
if (fs12.existsSync(file)) {
|
|
1552
|
+
const v = fs12.readFileSync(file, "utf-8").trim();
|
|
1356
1553
|
if (v) {
|
|
1357
1554
|
cached = v;
|
|
1358
1555
|
return cached;
|
|
@@ -1366,40 +1563,37 @@ function adminAuthHeader() {
|
|
|
1366
1563
|
|
|
1367
1564
|
// src/core/daemon-config.ts
|
|
1368
1565
|
init_node();
|
|
1369
|
-
import
|
|
1370
|
-
import
|
|
1371
|
-
function
|
|
1372
|
-
return
|
|
1373
|
-
}
|
|
1374
|
-
function configFile() {
|
|
1375
|
-
return path10.join(configDir(), "daemon.json");
|
|
1566
|
+
import fs13 from "fs";
|
|
1567
|
+
import path12 from "path";
|
|
1568
|
+
function configFile2() {
|
|
1569
|
+
return path12.join(profileDir(), "daemon.json");
|
|
1376
1570
|
}
|
|
1377
1571
|
function daemonConfigPath() {
|
|
1378
|
-
return
|
|
1572
|
+
return configFile2();
|
|
1379
1573
|
}
|
|
1380
1574
|
function readDaemonIdentity() {
|
|
1381
|
-
const file =
|
|
1382
|
-
if (!
|
|
1575
|
+
const file = configFile2();
|
|
1576
|
+
if (!fs13.existsSync(file)) return null;
|
|
1383
1577
|
try {
|
|
1384
|
-
const raw =
|
|
1578
|
+
const raw = fs13.readFileSync(file, "utf-8");
|
|
1385
1579
|
return JSON.parse(raw);
|
|
1386
1580
|
} catch {
|
|
1387
1581
|
return null;
|
|
1388
1582
|
}
|
|
1389
1583
|
}
|
|
1390
1584
|
function writeDaemonIdentity(identity) {
|
|
1391
|
-
const file =
|
|
1392
|
-
|
|
1393
|
-
|
|
1585
|
+
const file = configFile2();
|
|
1586
|
+
fs13.mkdirSync(path12.dirname(file), { recursive: true });
|
|
1587
|
+
fs13.writeFileSync(file, JSON.stringify(identity, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
1394
1588
|
try {
|
|
1395
|
-
|
|
1589
|
+
fs13.chmodSync(file, 384);
|
|
1396
1590
|
} catch {
|
|
1397
1591
|
}
|
|
1398
1592
|
}
|
|
1399
1593
|
function clearDaemonIdentity() {
|
|
1400
|
-
const file =
|
|
1401
|
-
if (!
|
|
1402
|
-
|
|
1594
|
+
const file = configFile2();
|
|
1595
|
+
if (!fs13.existsSync(file)) return false;
|
|
1596
|
+
fs13.unlinkSync(file);
|
|
1403
1597
|
return true;
|
|
1404
1598
|
}
|
|
1405
1599
|
|
|
@@ -1508,8 +1702,8 @@ function requireLocalDaemon() {
|
|
|
1508
1702
|
}
|
|
1509
1703
|
var source = new Command("source").description("Manage task sources");
|
|
1510
1704
|
source.command("add <path>").description("Register a local project on this host's daemon (via hub)").option("-n, --name <name>", "Source name (defaults to directory name)").action(async (inputPath, opts) => {
|
|
1511
|
-
const absPath =
|
|
1512
|
-
const name = opts.name ||
|
|
1705
|
+
const absPath = path13.resolve(inputPath);
|
|
1706
|
+
const name = opts.name || path13.basename(absPath);
|
|
1513
1707
|
const daemonId = requireLocalDaemon();
|
|
1514
1708
|
let resp;
|
|
1515
1709
|
try {
|
|
@@ -1607,9 +1801,9 @@ ${project2}`));
|
|
|
1607
1801
|
|
|
1608
1802
|
// src/commands/project.ts
|
|
1609
1803
|
import { Command as Command2 } from "commander";
|
|
1610
|
-
import
|
|
1611
|
-
import
|
|
1612
|
-
import
|
|
1804
|
+
import fs14 from "fs";
|
|
1805
|
+
import path14 from "path";
|
|
1806
|
+
import yaml6 from "js-yaml";
|
|
1613
1807
|
import chalk2 from "chalk";
|
|
1614
1808
|
|
|
1615
1809
|
// ../../packages/shared/dist/index.js
|
|
@@ -1647,15 +1841,15 @@ project.command("list").description("List registered projects (queries the hub)"
|
|
|
1647
1841
|
});
|
|
1648
1842
|
project.command("init").description("Initialize task0.yml in the current directory").option("-d, --tasks-dir <dir>", "Tasks directory", ".task0/tasks").action((opts) => {
|
|
1649
1843
|
const cwd = process.cwd();
|
|
1650
|
-
const ymlPath =
|
|
1651
|
-
if (
|
|
1844
|
+
const ymlPath = path14.join(cwd, "task0.yml");
|
|
1845
|
+
if (fs14.existsSync(ymlPath)) {
|
|
1652
1846
|
console.error(chalk2.yellow("task0.yml already exists"));
|
|
1653
1847
|
process.exit(1);
|
|
1654
1848
|
}
|
|
1655
1849
|
const config = { kind: "project", object_id: generateObjectId("project"), tasks_dir: opts.tasksDir };
|
|
1656
|
-
|
|
1657
|
-
const tasksDir =
|
|
1658
|
-
|
|
1850
|
+
fs14.writeFileSync(ymlPath, yaml6.dump(config), "utf-8");
|
|
1851
|
+
const tasksDir = path14.join(cwd, opts.tasksDir);
|
|
1852
|
+
fs14.mkdirSync(tasksDir, { recursive: true });
|
|
1659
1853
|
console.log(chalk2.green("Initialized task0 project"));
|
|
1660
1854
|
console.log(` ${ymlPath}`);
|
|
1661
1855
|
console.log(` ${tasksDir}/`);
|
|
@@ -1664,9 +1858,9 @@ project.command("init").description("Initialize task0.yml in the current directo
|
|
|
1664
1858
|
// src/commands/task.ts
|
|
1665
1859
|
import { Command as Command8 } from "commander";
|
|
1666
1860
|
import { execSync } from "child_process";
|
|
1667
|
-
import
|
|
1668
|
-
import
|
|
1669
|
-
import
|
|
1861
|
+
import fs20 from "fs";
|
|
1862
|
+
import path17 from "path";
|
|
1863
|
+
import yaml7 from "js-yaml";
|
|
1670
1864
|
import chalk8 from "chalk";
|
|
1671
1865
|
|
|
1672
1866
|
// src/lib/api.ts
|
|
@@ -1703,18 +1897,18 @@ async function request(method, pathname, body) {
|
|
|
1703
1897
|
}
|
|
1704
1898
|
}
|
|
1705
1899
|
var api = {
|
|
1706
|
-
get: (
|
|
1707
|
-
post: (
|
|
1708
|
-
put: (
|
|
1709
|
-
patch: (
|
|
1710
|
-
del: (
|
|
1900
|
+
get: (path32) => request("GET", path32),
|
|
1901
|
+
post: (path32, body) => request("POST", path32, body ?? {}),
|
|
1902
|
+
put: (path32, body) => request("PUT", path32, body ?? {}),
|
|
1903
|
+
patch: (path32, body) => request("PATCH", path32, body ?? {}),
|
|
1904
|
+
del: (path32) => request("DELETE", path32)
|
|
1711
1905
|
};
|
|
1712
1906
|
|
|
1713
1907
|
// src/commands/task/triage.ts
|
|
1714
1908
|
import { Command as Command3 } from "commander";
|
|
1715
1909
|
import chalk3 from "chalk";
|
|
1716
|
-
import
|
|
1717
|
-
import
|
|
1910
|
+
import fs16 from "fs";
|
|
1911
|
+
import path15 from "path";
|
|
1718
1912
|
|
|
1719
1913
|
// src/core/agent-run-wait.ts
|
|
1720
1914
|
async function getAgentRun(id) {
|
|
@@ -1765,8 +1959,8 @@ init_task_state2();
|
|
|
1765
1959
|
var ISSUE_DETAIL_RE = /^ISSUE-\d+\.md$/;
|
|
1766
1960
|
var TRIAGE_SKILL_NAME = "triage";
|
|
1767
1961
|
function resolveSkillFilePath(projectRoot, skillName) {
|
|
1768
|
-
const p =
|
|
1769
|
-
return
|
|
1962
|
+
const p = path15.join(projectRoot, ".claude", "skills", skillName, "SKILL.md");
|
|
1963
|
+
return fs16.existsSync(p) ? p : null;
|
|
1770
1964
|
}
|
|
1771
1965
|
var triage = new Command3("triage").description("Decompose IDEA into ISSUE.md + ISSUE-NN.md").argument("<objectId>", "Task object_id (tsk_XXXXX)").option("--agent <name>", "Agent (claude-code|codex)", "claude-code").option("--model <id>", "Model id or alias (see `task0 models refresh`)").option("--effort <level>", "Reasoning effort (e.g. low|medium|high)").option("--idea <file>", "IDEA file (default: latest IDEA-NN.md)").option("--force", "Overwrite existing ISSUE files").option("--wait", "Wait for completion").option("--json", "Output JSON").action(async (objectId, opts) => {
|
|
1772
1966
|
try {
|
|
@@ -1785,7 +1979,7 @@ var triage = new Command3("triage").description("Decompose IDEA into ISSUE.md +
|
|
|
1785
1979
|
process.exit(1);
|
|
1786
1980
|
}
|
|
1787
1981
|
for (const name of existingIssues) {
|
|
1788
|
-
|
|
1982
|
+
fs16.rmSync(path15.join(loc.taskDir, name), { force: true });
|
|
1789
1983
|
}
|
|
1790
1984
|
}
|
|
1791
1985
|
if (opts.model || opts.effort) {
|
|
@@ -1823,8 +2017,8 @@ var triage = new Command3("triage").description("Decompose IDEA into ISSUE.md +
|
|
|
1823
2017
|
console.error(chalk3.red(`triage failed: ${final.error || "unknown"}`));
|
|
1824
2018
|
process.exit(1);
|
|
1825
2019
|
}
|
|
1826
|
-
const hasOverview =
|
|
1827
|
-
const issueFiles =
|
|
2020
|
+
const hasOverview = fs16.existsSync(path15.join(loc.taskDir, "ISSUE.md"));
|
|
2021
|
+
const issueFiles = fs16.readdirSync(loc.taskDir).filter((name) => ISSUE_DETAIL_RE.test(name)).sort();
|
|
1828
2022
|
if (!hasOverview || issueFiles.length === 0) {
|
|
1829
2023
|
const missing = [];
|
|
1830
2024
|
if (!hasOverview) missing.push("ISSUE.md");
|
|
@@ -1834,7 +2028,7 @@ var triage = new Command3("triage").description("Decompose IDEA into ISSUE.md +
|
|
|
1834
2028
|
}
|
|
1835
2029
|
let blockingQuestionCount = 0;
|
|
1836
2030
|
for (const name of issueFiles) {
|
|
1837
|
-
const content =
|
|
2031
|
+
const content = fs16.readFileSync(path15.join(loc.taskDir, name), "utf-8");
|
|
1838
2032
|
blockingQuestionCount += countBlockingQuestions(content);
|
|
1839
2033
|
}
|
|
1840
2034
|
await updateWorkflow(loc.taskYml, {
|
|
@@ -1862,8 +2056,8 @@ var triage = new Command3("triage").description("Decompose IDEA into ISSUE.md +
|
|
|
1862
2056
|
}
|
|
1863
2057
|
});
|
|
1864
2058
|
function listIssueArtifacts(taskDir) {
|
|
1865
|
-
if (!
|
|
1866
|
-
return
|
|
2059
|
+
if (!fs16.existsSync(taskDir)) return [];
|
|
2060
|
+
return fs16.readdirSync(taskDir).filter((name) => name === "ISSUE.md" || ISSUE_DETAIL_RE.test(name)).sort();
|
|
1867
2061
|
}
|
|
1868
2062
|
function countBlockingQuestions(md) {
|
|
1869
2063
|
const match = md.match(/## Open Questions\s*\n([\s\S]*?)(\n## |\n*$)/i);
|
|
@@ -1877,13 +2071,13 @@ function countBlockingQuestions(md) {
|
|
|
1877
2071
|
// src/commands/task/exec.ts
|
|
1878
2072
|
import { Command as Command4 } from "commander";
|
|
1879
2073
|
import chalk4 from "chalk";
|
|
1880
|
-
import
|
|
1881
|
-
import
|
|
2074
|
+
import fs17 from "fs";
|
|
2075
|
+
import path16 from "path";
|
|
1882
2076
|
init_task_state2();
|
|
1883
2077
|
var PLAN_EXECUTE_SKILL_NAME = "plan-execute";
|
|
1884
2078
|
function resolveSkillFilePath2(projectRoot, skillName) {
|
|
1885
|
-
const p =
|
|
1886
|
-
return
|
|
2079
|
+
const p = path16.join(projectRoot, ".claude", "skills", skillName, "SKILL.md");
|
|
2080
|
+
return fs17.existsSync(p) ? p : null;
|
|
1887
2081
|
}
|
|
1888
2082
|
var exec = new Command4("exec").description("Execute a plan against the task (cwd = project root; agent sets up its own worktree if needed)").argument("<objectId>", "Task object_id (tsk_XXXXX)").option("--agent <name>", "Agent (claude-code|codex|cursor)", "claude-code").option("--model <id>", "Model id or alias (see `task0 models refresh`)").option("--effort <level>", "Reasoning effort (e.g. low|medium|high)").option("--plan <file>", "Plan file (default: refined plan, else latest PLAN)").option("--no-commit", "Skip commit").option("--wait", "Wait for completion").option("--json", "Output JSON").action(async (objectId, opts) => {
|
|
1889
2083
|
try {
|
|
@@ -1976,7 +2170,7 @@ var summarize = new Command5("summarize").description("Generate a concise title
|
|
|
1976
2170
|
});
|
|
1977
2171
|
|
|
1978
2172
|
// src/commands/task/comment.ts
|
|
1979
|
-
import
|
|
2173
|
+
import fs18 from "fs";
|
|
1980
2174
|
import { Command as Command6 } from "commander";
|
|
1981
2175
|
import chalk6 from "chalk";
|
|
1982
2176
|
var comment = new Command6("comment").description("Manage comments on a task");
|
|
@@ -1986,8 +2180,8 @@ function fail(err) {
|
|
|
1986
2180
|
}
|
|
1987
2181
|
function readBodyFromOpts(opts) {
|
|
1988
2182
|
if (opts.body !== void 0) return opts.body;
|
|
1989
|
-
if (opts.file === "-") return
|
|
1990
|
-
if (opts.file) return
|
|
2183
|
+
if (opts.file === "-") return fs18.readFileSync(0, "utf-8");
|
|
2184
|
+
if (opts.file) return fs18.readFileSync(opts.file, "utf-8");
|
|
1991
2185
|
throw new Error("Provide --body or --file");
|
|
1992
2186
|
}
|
|
1993
2187
|
function preview(body, width = 60) {
|
|
@@ -2086,7 +2280,7 @@ comment.command("delete <cmtId>").description("Delete a comment by its cmt_ id")
|
|
|
2086
2280
|
});
|
|
2087
2281
|
|
|
2088
2282
|
// src/commands/task/description.ts
|
|
2089
|
-
import
|
|
2283
|
+
import fs19 from "fs";
|
|
2090
2284
|
import { Command as Command7 } from "commander";
|
|
2091
2285
|
import chalk7 from "chalk";
|
|
2092
2286
|
var description = new Command7("description").description("Show or update the task description");
|
|
@@ -2096,8 +2290,8 @@ function fail2(err) {
|
|
|
2096
2290
|
}
|
|
2097
2291
|
function readBodyFromOpts2(opts) {
|
|
2098
2292
|
if (opts.body !== void 0) return opts.body;
|
|
2099
|
-
if (opts.file === "-") return
|
|
2100
|
-
if (opts.file) return
|
|
2293
|
+
if (opts.file === "-") return fs19.readFileSync(0, "utf-8");
|
|
2294
|
+
if (opts.file) return fs19.readFileSync(opts.file, "utf-8");
|
|
2101
2295
|
throw new Error("Provide --body or --file (use --file - to read stdin)");
|
|
2102
2296
|
}
|
|
2103
2297
|
description.command("show <taskId>").description("Print the current description (taskId is short id or tsk_)").option("--json", "Output JSON").action(async (taskId, opts) => {
|
|
@@ -2146,12 +2340,12 @@ task.addCommand(comment);
|
|
|
2146
2340
|
task.addCommand(description);
|
|
2147
2341
|
task.command("init <input>").description("Create a task from a description or Linear/GitHub issue URL").action(async (input) => {
|
|
2148
2342
|
const cwd = process.cwd();
|
|
2149
|
-
const projectYml =
|
|
2150
|
-
if (!
|
|
2343
|
+
const projectYml = path17.join(cwd, "task0.yml");
|
|
2344
|
+
if (!fs20.existsSync(projectYml)) {
|
|
2151
2345
|
console.error(chalk8.red("Not a task0 project (task0.yml not found). Run `task0 project init` first."));
|
|
2152
2346
|
process.exit(1);
|
|
2153
2347
|
}
|
|
2154
|
-
const projectConfig =
|
|
2348
|
+
const projectConfig = yaml7.load(fs20.readFileSync(projectYml, "utf-8"));
|
|
2155
2349
|
if (projectConfig.kind !== "project") {
|
|
2156
2350
|
console.error(chalk8.red('Invalid task0.yml: kind is not "project"'));
|
|
2157
2351
|
process.exit(1);
|
|
@@ -2167,38 +2361,38 @@ task.command("init <input>").description("Create a task from a description or Li
|
|
|
2167
2361
|
});
|
|
2168
2362
|
task.command("migrate").description("Add task0.yml to legacy task directories that lack one").option("--dry-run", "Show what would be created without writing").action((opts) => {
|
|
2169
2363
|
const cwd = process.cwd();
|
|
2170
|
-
const projectYml =
|
|
2171
|
-
if (!
|
|
2364
|
+
const projectYml = path17.join(cwd, "task0.yml");
|
|
2365
|
+
if (!fs20.existsSync(projectYml)) {
|
|
2172
2366
|
console.error(chalk8.red("Not a task0 project (task0.yml not found). Run `task0 project init` first."));
|
|
2173
2367
|
process.exit(1);
|
|
2174
2368
|
}
|
|
2175
|
-
const projectConfig =
|
|
2369
|
+
const projectConfig = yaml7.load(fs20.readFileSync(projectYml, "utf-8"));
|
|
2176
2370
|
if (projectConfig.kind !== "project") {
|
|
2177
2371
|
console.error(chalk8.red('Invalid task0.yml: kind is not "project"'));
|
|
2178
2372
|
process.exit(1);
|
|
2179
2373
|
}
|
|
2180
|
-
const tasksDir =
|
|
2181
|
-
if (!
|
|
2374
|
+
const tasksDir = path17.join(cwd, projectConfig.tasks_dir);
|
|
2375
|
+
if (!fs20.existsSync(tasksDir)) {
|
|
2182
2376
|
console.error(chalk8.red(`Tasks directory not found: ${tasksDir}`));
|
|
2183
2377
|
process.exit(1);
|
|
2184
2378
|
}
|
|
2185
|
-
const entries =
|
|
2379
|
+
const entries = fs20.readdirSync(tasksDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2186
2380
|
let migrated = 0;
|
|
2187
2381
|
let skipped = 0;
|
|
2188
2382
|
let seededObjectIds = 0;
|
|
2189
2383
|
for (const name of entries) {
|
|
2190
|
-
const taskDir =
|
|
2191
|
-
const taskYml =
|
|
2192
|
-
if (
|
|
2384
|
+
const taskDir = path17.join(tasksDir, name);
|
|
2385
|
+
const taskYml = path17.join(taskDir, "task0.yml");
|
|
2386
|
+
if (fs20.existsSync(taskYml)) {
|
|
2193
2387
|
skipped++;
|
|
2194
2388
|
try {
|
|
2195
|
-
const raw =
|
|
2389
|
+
const raw = yaml7.load(fs20.readFileSync(taskYml, "utf-8"));
|
|
2196
2390
|
if (raw && raw.kind === "task" && !raw.object_id) {
|
|
2197
2391
|
raw.object_id = generateObjectId("task");
|
|
2198
2392
|
if (opts.dryRun) {
|
|
2199
2393
|
console.log(chalk8.dim(`[dry-run] seed object_id: ${taskYml}`));
|
|
2200
2394
|
} else {
|
|
2201
|
-
|
|
2395
|
+
fs20.writeFileSync(taskYml, yaml7.dump(raw, { lineWidth: 120 }), "utf-8");
|
|
2202
2396
|
console.log(chalk8.green(` seed object_id: ${taskYml}`));
|
|
2203
2397
|
}
|
|
2204
2398
|
seededObjectIds++;
|
|
@@ -2223,7 +2417,7 @@ task.command("migrate").description("Add task0.yml to legacy task directories th
|
|
|
2223
2417
|
if (opts.dryRun) {
|
|
2224
2418
|
console.log(chalk8.dim(`[dry-run] ${taskYml}`));
|
|
2225
2419
|
} else {
|
|
2226
|
-
|
|
2420
|
+
fs20.writeFileSync(taskYml, yaml7.dump(taskConfig), "utf-8");
|
|
2227
2421
|
console.log(chalk8.green(` ${taskYml}`));
|
|
2228
2422
|
}
|
|
2229
2423
|
migrated++;
|
|
@@ -2283,7 +2477,7 @@ task.command("done <id>").description("Write task lifecycle state to task0.yml \
|
|
|
2283
2477
|
try {
|
|
2284
2478
|
const { resolveTaskByObjectId: resolveTaskByObjectId2 } = await Promise.resolve().then(() => (init_task_state2(), task_state_exports));
|
|
2285
2479
|
const loc = resolveTaskByObjectId2(id);
|
|
2286
|
-
const raw =
|
|
2480
|
+
const raw = yaml7.load(fs20.readFileSync(loc.taskYml, "utf-8"));
|
|
2287
2481
|
if (opts.phase) {
|
|
2288
2482
|
const phase = opts.phase.trim();
|
|
2289
2483
|
if (!phase) {
|
|
@@ -2296,7 +2490,7 @@ task.command("done <id>").description("Write task lifecycle state to task0.yml \
|
|
|
2296
2490
|
return;
|
|
2297
2491
|
}
|
|
2298
2492
|
raw.workflow = { ...workflow2, phase };
|
|
2299
|
-
|
|
2493
|
+
fs20.writeFileSync(loc.taskYml, yaml7.dump(raw, { lineWidth: 120 }), "utf-8");
|
|
2300
2494
|
console.log(chalk8.green(`${id} phase: ${phase}`));
|
|
2301
2495
|
return;
|
|
2302
2496
|
}
|
|
@@ -2307,7 +2501,7 @@ task.command("done <id>").description("Write task lifecycle state to task0.yml \
|
|
|
2307
2501
|
raw.status = "done";
|
|
2308
2502
|
const workflow = raw.workflow ?? {};
|
|
2309
2503
|
raw.workflow = { ...workflow, phase: "completed" };
|
|
2310
|
-
|
|
2504
|
+
fs20.writeFileSync(loc.taskYml, yaml7.dump(raw, { lineWidth: 120 }), "utf-8");
|
|
2311
2505
|
console.log(chalk8.green(`${id} marked as done`));
|
|
2312
2506
|
} catch (err) {
|
|
2313
2507
|
console.error(chalk8.red(err.message));
|
|
@@ -2316,33 +2510,33 @@ task.command("done <id>").description("Write task lifecycle state to task0.yml \
|
|
|
2316
2510
|
});
|
|
2317
2511
|
task.command("archive <id>").description("Archive a task (append to tasks.tar, remove from tasks/)").action((id) => {
|
|
2318
2512
|
const cwd = process.cwd();
|
|
2319
|
-
const projectYml =
|
|
2320
|
-
if (!
|
|
2513
|
+
const projectYml = path17.join(cwd, "task0.yml");
|
|
2514
|
+
if (!fs20.existsSync(projectYml)) {
|
|
2321
2515
|
console.error(chalk8.red("Not a task0 project (task0.yml not found)."));
|
|
2322
2516
|
process.exit(1);
|
|
2323
2517
|
}
|
|
2324
|
-
const projectConfig =
|
|
2518
|
+
const projectConfig = yaml7.load(fs20.readFileSync(projectYml, "utf-8"));
|
|
2325
2519
|
if (projectConfig.kind !== "project") {
|
|
2326
2520
|
console.error(chalk8.red('Invalid task0.yml: kind is not "project"'));
|
|
2327
2521
|
process.exit(1);
|
|
2328
2522
|
}
|
|
2329
|
-
const tasksDir =
|
|
2330
|
-
const taskDir =
|
|
2331
|
-
if (!
|
|
2523
|
+
const tasksDir = path17.join(cwd, projectConfig.tasks_dir);
|
|
2524
|
+
const taskDir = path17.join(tasksDir, id);
|
|
2525
|
+
if (!fs20.existsSync(taskDir)) {
|
|
2332
2526
|
console.error(chalk8.red(`Task "${id}" not found at ${taskDir}`));
|
|
2333
2527
|
process.exit(1);
|
|
2334
2528
|
}
|
|
2335
2529
|
const tarFile = tasksDir + ".tar";
|
|
2336
2530
|
archiveTaskToTar(tasksDir, id, tarFile);
|
|
2337
|
-
|
|
2531
|
+
fs20.rmSync(taskDir, { recursive: true });
|
|
2338
2532
|
console.log(chalk8.green(`Archived "${id}" \u2192 ${tarFile}`));
|
|
2339
2533
|
});
|
|
2340
2534
|
function archiveTaskToTar(tasksDir, taskId, tarFile) {
|
|
2341
|
-
if (
|
|
2535
|
+
if (fs20.existsSync(tarFile) && !fs20.statSync(tarFile).isFile()) {
|
|
2342
2536
|
console.error(chalk8.red(`${tarFile} exists but is not a file (likely a leftover directory). Remove it manually first.`));
|
|
2343
2537
|
process.exit(1);
|
|
2344
2538
|
}
|
|
2345
|
-
if (
|
|
2539
|
+
if (fs20.existsSync(tarFile)) {
|
|
2346
2540
|
execSync(`tar -rf ${JSON.stringify(tarFile)} -C ${JSON.stringify(tasksDir)} ${JSON.stringify(taskId)}`);
|
|
2347
2541
|
} else {
|
|
2348
2542
|
execSync(`tar -cf ${JSON.stringify(tarFile)} -C ${JSON.stringify(tasksDir)} ${JSON.stringify(taskId)}`);
|
|
@@ -2352,9 +2546,9 @@ function archiveTaskToTar(tasksDir, taskId, tarFile) {
|
|
|
2352
2546
|
// src/commands/ui.ts
|
|
2353
2547
|
import { Command as Command9 } from "commander";
|
|
2354
2548
|
import { spawn } from "child_process";
|
|
2355
|
-
import
|
|
2549
|
+
import path18 from "path";
|
|
2356
2550
|
import chalk9 from "chalk";
|
|
2357
|
-
var DASHBOARD_DIR =
|
|
2551
|
+
var DASHBOARD_DIR = path18.resolve(
|
|
2358
2552
|
import.meta.dirname,
|
|
2359
2553
|
"..",
|
|
2360
2554
|
"..",
|
|
@@ -2417,31 +2611,6 @@ var AGENT_MODEL_DEFAULTS = {
|
|
|
2417
2611
|
{ id: "sonnet-4-thinking", label: "Sonnet 4 Thinking" }
|
|
2418
2612
|
]
|
|
2419
2613
|
};
|
|
2420
|
-
var AGENT_EFFORT_DEFAULTS = {
|
|
2421
|
-
"claude-code": [
|
|
2422
|
-
{ id: "high", label: "High" },
|
|
2423
|
-
{ id: "max", label: "Max" },
|
|
2424
|
-
{ id: "medium", label: "Medium" },
|
|
2425
|
-
{ id: "low", label: "Low" }
|
|
2426
|
-
],
|
|
2427
|
-
codex: [
|
|
2428
|
-
{ id: "xhigh", label: "Extra High" },
|
|
2429
|
-
{ id: "high", label: "High" },
|
|
2430
|
-
{ id: "medium", label: "Medium" },
|
|
2431
|
-
{ id: "low", label: "Low" }
|
|
2432
|
-
],
|
|
2433
|
-
"cursor-agent": []
|
|
2434
|
-
};
|
|
2435
|
-
var AGENT_DEFAULT_MODEL = {
|
|
2436
|
-
"claude-code": "opus",
|
|
2437
|
-
codex: "gpt-5.4",
|
|
2438
|
-
"cursor-agent": ""
|
|
2439
|
-
};
|
|
2440
|
-
var AGENT_DEFAULT_EFFORT = {
|
|
2441
|
-
"claude-code": "high",
|
|
2442
|
-
codex: "xhigh",
|
|
2443
|
-
"cursor-agent": ""
|
|
2444
|
-
};
|
|
2445
2614
|
function defaultAgentModelFetchCommand(agent2) {
|
|
2446
2615
|
if (agent2 === "cursor-agent")
|
|
2447
2616
|
return "cursor-agent models";
|
|
@@ -2660,12 +2829,12 @@ models.command("default <agent>").description("Get or set default model / effort
|
|
|
2660
2829
|
});
|
|
2661
2830
|
|
|
2662
2831
|
// src/commands/agent-run.ts
|
|
2663
|
-
import
|
|
2664
|
-
import
|
|
2832
|
+
import fs21 from "fs";
|
|
2833
|
+
import path19 from "path";
|
|
2665
2834
|
import { Command as Command11 } from "commander";
|
|
2666
2835
|
import chalk11 from "chalk";
|
|
2667
2836
|
import { spawn as spawn2, spawnSync as spawnSync3 } from "child_process";
|
|
2668
|
-
import
|
|
2837
|
+
import yaml8 from "js-yaml";
|
|
2669
2838
|
var agentRun = new Command11("agent-run").description("Inspect and control agent runs");
|
|
2670
2839
|
agentRun.command("list").description("List active agent runs").option("--json", "Output JSON").option("--task <id>", "Filter by task id").action(async (opts) => {
|
|
2671
2840
|
try {
|
|
@@ -2789,7 +2958,7 @@ profile.command("get <ref>").description("Show one runtime profile by slug or ob
|
|
|
2789
2958
|
console.log(formatProfile(result.runtime));
|
|
2790
2959
|
console.log();
|
|
2791
2960
|
console.log(chalk11.dim("--- exec ---"));
|
|
2792
|
-
console.log(
|
|
2961
|
+
console.log(yaml8.dump(result.runtime.exec, { lineWidth: 100 }));
|
|
2793
2962
|
} catch (err) {
|
|
2794
2963
|
const apiErr = err;
|
|
2795
2964
|
if (apiErr.status === 404) failProfile(`not found: ${ref}`);
|
|
@@ -2799,7 +2968,7 @@ profile.command("get <ref>").description("Show one runtime profile by slug or ob
|
|
|
2799
2968
|
profile.command("create").description("Create a runtime profile from a YAML spec file").requiredOption("--from-file <file>", "YAML spec file with kind, slug, exec").option("--scope <scope>", "Storage scope: user (default) or project", "user").option("--project-root <path>", "Project root (required for --scope project)").action(async (opts) => {
|
|
2800
2969
|
let parsed;
|
|
2801
2970
|
try {
|
|
2802
|
-
parsed =
|
|
2971
|
+
parsed = yaml8.load(fs21.readFileSync(path19.resolve(opts.fromFile), "utf-8"));
|
|
2803
2972
|
} catch (err) {
|
|
2804
2973
|
failProfile(`cannot read ${opts.fromFile}: ${err.message}`);
|
|
2805
2974
|
}
|
|
@@ -2815,14 +2984,14 @@ profile.command("edit <ref>").description("Open the runtime profile YAML in $EDI
|
|
|
2815
2984
|
try {
|
|
2816
2985
|
const result = await api.get(`/api/runtime-profiles/${encodeURIComponent(ref)}`);
|
|
2817
2986
|
if (result.runtime.system) failProfile("cannot edit a system runtime profile");
|
|
2818
|
-
const tmp =
|
|
2819
|
-
|
|
2987
|
+
const tmp = path19.join(process.env.TMPDIR || "/tmp", `task0-runtime-${result.runtime.slug}-${Date.now()}.yml`);
|
|
2988
|
+
fs21.writeFileSync(tmp, yaml8.dump(result.runtime, { lineWidth: 100 }), "utf-8");
|
|
2820
2989
|
const editor = process.env.EDITOR || "vi";
|
|
2821
2990
|
const r = spawnSync3(editor, [tmp], { stdio: "inherit" });
|
|
2822
2991
|
if (r.status !== 0) failProfile(`editor exited with status ${r.status}`);
|
|
2823
|
-
const updated =
|
|
2992
|
+
const updated = yaml8.load(fs21.readFileSync(tmp, "utf-8"));
|
|
2824
2993
|
await api.put(`/api/runtime-profiles/${encodeURIComponent(ref)}`, updated);
|
|
2825
|
-
|
|
2994
|
+
fs21.unlinkSync(tmp);
|
|
2826
2995
|
console.log(chalk11.green(`updated ${ref}`));
|
|
2827
2996
|
} catch (err) {
|
|
2828
2997
|
failProfile(err.message);
|
|
@@ -2842,13 +3011,13 @@ agentRun.addCommand(profile);
|
|
|
2842
3011
|
// src/commands/plan.ts
|
|
2843
3012
|
import { Command as Command12 } from "commander";
|
|
2844
3013
|
import chalk12 from "chalk";
|
|
2845
|
-
import
|
|
2846
|
-
import
|
|
3014
|
+
import fs22 from "fs";
|
|
3015
|
+
import path20 from "path";
|
|
2847
3016
|
init_task_state2();
|
|
2848
3017
|
var PLAN_GENERATE_SKILL_NAME = "plan-generate";
|
|
2849
3018
|
function resolveSkillFilePath3(projectRoot, skillName) {
|
|
2850
|
-
const p =
|
|
2851
|
-
return
|
|
3019
|
+
const p = path20.join(projectRoot, ".claude", "skills", skillName, "SKILL.md");
|
|
3020
|
+
return fs22.existsSync(p) ? p : null;
|
|
2852
3021
|
}
|
|
2853
3022
|
var plan = new Command12("plan").description("Generate and refine plans");
|
|
2854
3023
|
plan.command("generate <objectId>").description("Generate plan(s) from an IDEA file \u2014 supports agent fan-out").option("-a, --agents <list>", "Comma-separated agents (claude-code,codex,cursor)", "codex,claude-code").option("--model <id>", "Model id or alias \u2014 only with a single agent; use `task0 models default` for fan-out").option("--effort <level>", "Reasoning effort \u2014 only with a single agent; use `task0 models default` for fan-out").option("--idea <file>", "IDEA file name (default: latest IDEA-NN.md)").option("--additional-prompt <text>", "Extra prompt content").option("--wait", "Wait for completion").option("--force", "Overwrite existing plan files").option("--json", "Output JSON").action(async (objectId, opts) => {
|
|
@@ -2980,7 +3149,7 @@ var PLAN_REFINE_SKILL_NAME = "plan-refine";
|
|
|
2980
3149
|
plan.command("refine <objectId>").description("Synthesize plan files + ISSUE files into a refined plan").option("--agent <name>", "Agent to run refine (claude-code|codex)", "claude-code").option("--model <id>", "Model id or alias (see `task0 models refresh`)").option("--effort <level>", "Reasoning effort (e.g. low|medium|high)").option("--wait", "Wait for completion").option("--json", "Output JSON").action(async (objectId, opts) => {
|
|
2981
3150
|
try {
|
|
2982
3151
|
const loc = resolveTaskByObjectId(objectId);
|
|
2983
|
-
const files =
|
|
3152
|
+
const files = fs22.readdirSync(loc.taskDir);
|
|
2984
3153
|
const planFiles = files.filter((f) => /^PLAN-\d+-(codex|claude-code|cursor)\.md$/.test(f));
|
|
2985
3154
|
if (planFiles.length === 0) {
|
|
2986
3155
|
console.error(chalk12.red("No PLAN-NN-<agent>.md files found. Run `task0 plan generate` first."));
|
|
@@ -3022,8 +3191,8 @@ plan.command("refine <objectId>").description("Synthesize plan files + ISSUE fil
|
|
|
3022
3191
|
if (!opts.json) console.log(chalk12.dim(`[refine] ${s.status}${s.phase ? " " + s.phase : ""}`));
|
|
3023
3192
|
}
|
|
3024
3193
|
});
|
|
3025
|
-
const refinedPath =
|
|
3026
|
-
const wrote =
|
|
3194
|
+
const refinedPath = path20.join(loc.taskDir, refinedFile);
|
|
3195
|
+
const wrote = fs22.existsSync(refinedPath);
|
|
3027
3196
|
if (final.status === "done" && wrote) {
|
|
3028
3197
|
await updateWorkflow(loc.taskYml, {
|
|
3029
3198
|
phase: "refined",
|
|
@@ -3175,8 +3344,8 @@ import { Command as Command14 } from "commander";
|
|
|
3175
3344
|
import chalk14 from "chalk";
|
|
3176
3345
|
|
|
3177
3346
|
// src/lib/project.ts
|
|
3178
|
-
import
|
|
3179
|
-
import
|
|
3347
|
+
import path21 from "path";
|
|
3348
|
+
import fs23 from "fs";
|
|
3180
3349
|
function resolveProjectName(opts) {
|
|
3181
3350
|
if (opts.project && opts.project.length > 0) return opts.project;
|
|
3182
3351
|
const config = loadConfig();
|
|
@@ -3186,15 +3355,15 @@ function resolveProjectName(opts) {
|
|
|
3186
3355
|
"Cannot resolve project: no registered projects. Use `task0 source add <path>` first, or pass --project <name>."
|
|
3187
3356
|
);
|
|
3188
3357
|
}
|
|
3189
|
-
const cwd =
|
|
3358
|
+
const cwd = fs23.realpathSync(process.cwd());
|
|
3190
3359
|
for (const source2 of projects) {
|
|
3191
3360
|
let sourceAbs;
|
|
3192
3361
|
try {
|
|
3193
|
-
sourceAbs =
|
|
3362
|
+
sourceAbs = fs23.realpathSync(path21.resolve(source2.path));
|
|
3194
3363
|
} catch {
|
|
3195
|
-
sourceAbs =
|
|
3364
|
+
sourceAbs = path21.resolve(source2.path);
|
|
3196
3365
|
}
|
|
3197
|
-
if (cwd === sourceAbs || cwd.startsWith(sourceAbs +
|
|
3366
|
+
if (cwd === sourceAbs || cwd.startsWith(sourceAbs + path21.sep)) {
|
|
3198
3367
|
return source2.name;
|
|
3199
3368
|
}
|
|
3200
3369
|
}
|
|
@@ -3834,12 +4003,12 @@ import chalk17 from "chalk";
|
|
|
3834
4003
|
|
|
3835
4004
|
// src/core/issue/decision.ts
|
|
3836
4005
|
init_node();
|
|
3837
|
-
import
|
|
3838
|
-
import
|
|
4006
|
+
import fs24 from "fs";
|
|
4007
|
+
import path22 from "path";
|
|
3839
4008
|
init_task_state2();
|
|
3840
4009
|
var DECISION_FILE_RE = /^DECISION-(\d+)-([a-z0-9-]+)\.md$/;
|
|
3841
4010
|
function selectBlockingIssues(taskDir, explicitIssue) {
|
|
3842
|
-
const files =
|
|
4011
|
+
const files = fs24.existsSync(taskDir) ? fs24.readdirSync(taskDir) : [];
|
|
3843
4012
|
const issueFiles = files.filter((f) => /^ISSUE-\d+\.md$/.test(f)).sort();
|
|
3844
4013
|
const all = readOpenQuestions(taskDir, issueFiles);
|
|
3845
4014
|
if (!explicitIssue) return all;
|
|
@@ -3943,12 +4112,12 @@ function parseConsolidatedAnswers(md) {
|
|
|
3943
4112
|
return sections;
|
|
3944
4113
|
}
|
|
3945
4114
|
function rewriteIssueWithDecisions(taskDir, issueFile, consolidatedFile) {
|
|
3946
|
-
const issuePath =
|
|
3947
|
-
const consolidatedPath =
|
|
3948
|
-
if (!
|
|
3949
|
-
if (!
|
|
3950
|
-
const issueMd =
|
|
3951
|
-
const consolidatedMd =
|
|
4115
|
+
const issuePath = path22.join(taskDir, issueFile);
|
|
4116
|
+
const consolidatedPath = path22.join(taskDir, consolidatedFile);
|
|
4117
|
+
if (!fs24.existsSync(issuePath)) throw new Error(`${issueFile} not found in ${taskDir}`);
|
|
4118
|
+
if (!fs24.existsSync(consolidatedPath)) throw new Error(`${consolidatedFile} not found in ${taskDir}`);
|
|
4119
|
+
const issueMd = fs24.readFileSync(issuePath, "utf-8");
|
|
4120
|
+
const consolidatedMd = fs24.readFileSync(consolidatedPath, "utf-8");
|
|
3952
4121
|
const answers = parseConsolidatedAnswers(consolidatedMd);
|
|
3953
4122
|
if (answers.length === 0) {
|
|
3954
4123
|
throw new Error(`${consolidatedFile} has no "## Qn" sections; cannot derive Decisions`);
|
|
@@ -3970,7 +4139,7 @@ _Resolved from [${consolidatedFile}](${consolidatedFile}); see that file for rea
|
|
|
3970
4139
|
throw new Error(`${issueFile} has no "## Open Questions" section to replace`);
|
|
3971
4140
|
}
|
|
3972
4141
|
const next = issueMd.replace(openQRe, decisionsSection.trimEnd() + "\n");
|
|
3973
|
-
|
|
4142
|
+
fs24.writeFileSync(issuePath, next, "utf-8");
|
|
3974
4143
|
return { replaced: answers.length };
|
|
3975
4144
|
}
|
|
3976
4145
|
async function propose(opts) {
|
|
@@ -3998,8 +4167,8 @@ async function propose(opts) {
|
|
|
3998
4167
|
const kicks = [];
|
|
3999
4168
|
for (const agent2 of opts.agents) {
|
|
4000
4169
|
const decisionFile = decisionFileName(issue2, agent2);
|
|
4001
|
-
const decisionPath =
|
|
4002
|
-
if (
|
|
4170
|
+
const decisionPath = path22.join(loc.taskDir, decisionFile);
|
|
4171
|
+
if (fs24.existsSync(decisionPath) && !opts.force) {
|
|
4003
4172
|
if (opts.ifNeeded) {
|
|
4004
4173
|
kicks.push({
|
|
4005
4174
|
issue: issue2.file,
|
|
@@ -4074,7 +4243,7 @@ async function propose(opts) {
|
|
|
4074
4243
|
const final = await waitForAgentRun(k.agentRunId);
|
|
4075
4244
|
if (final.status !== "done") {
|
|
4076
4245
|
k.error = final.error || `runtime ${k.agentRunId} ended with ${final.status}`;
|
|
4077
|
-
} else if (!
|
|
4246
|
+
} else if (!fs24.existsSync(path22.join(loc.taskDir, k.decisionFile))) {
|
|
4078
4247
|
k.error = `runtime completed but ${k.decisionFile} was not written`;
|
|
4079
4248
|
}
|
|
4080
4249
|
} catch (err) {
|
|
@@ -4095,7 +4264,7 @@ async function consolidate(opts) {
|
|
|
4095
4264
|
const kicks = [];
|
|
4096
4265
|
const modelOpts = resolveModelOptions(opts.agent, { model: opts.model, effort: opts.effort }, opts.warn);
|
|
4097
4266
|
for (const issue2 of issues) {
|
|
4098
|
-
const files =
|
|
4267
|
+
const files = fs24.readdirSync(loc.taskDir);
|
|
4099
4268
|
const proposalFiles = files.filter((f) => {
|
|
4100
4269
|
const m = f.match(DECISION_FILE_RE);
|
|
4101
4270
|
return !!m && m[1] === issue2.index && m[2] !== "consolidated";
|
|
@@ -4141,7 +4310,7 @@ async function consolidate(opts) {
|
|
|
4141
4310
|
if (opts.wait) {
|
|
4142
4311
|
try {
|
|
4143
4312
|
const final = await waitForAgentRun(agentRunId);
|
|
4144
|
-
const wrote =
|
|
4313
|
+
const wrote = fs24.existsSync(path22.join(loc.taskDir, consolidatedFile));
|
|
4145
4314
|
if (final.status !== "done") {
|
|
4146
4315
|
kick.error = final.error || `runtime ${agentRunId} ended with ${final.status}`;
|
|
4147
4316
|
} else if (!wrote) {
|
|
@@ -4207,7 +4376,7 @@ async function approve(opts) {
|
|
|
4207
4376
|
return { updated };
|
|
4208
4377
|
}
|
|
4209
4378
|
function buildReferenceFiles(taskDir, issue2) {
|
|
4210
|
-
const names =
|
|
4379
|
+
const names = fs24.readdirSync(taskDir);
|
|
4211
4380
|
const refs = [issue2.file];
|
|
4212
4381
|
if (names.includes("ISSUE.md")) refs.push("ISSUE.md");
|
|
4213
4382
|
const ideaFile = `IDEA-${issue2.index}.md`;
|
|
@@ -4315,12 +4484,12 @@ issue.command("approve <taskId>").description("Apply the consolidated decisions
|
|
|
4315
4484
|
});
|
|
4316
4485
|
|
|
4317
4486
|
// src/commands/agent.ts
|
|
4318
|
-
import
|
|
4319
|
-
import
|
|
4487
|
+
import fs25 from "fs";
|
|
4488
|
+
import path23 from "path";
|
|
4320
4489
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
4321
4490
|
import { Command as Command18 } from "commander";
|
|
4322
4491
|
import chalk18 from "chalk";
|
|
4323
|
-
import
|
|
4492
|
+
import yaml9 from "js-yaml";
|
|
4324
4493
|
function statusBadge(s) {
|
|
4325
4494
|
if (s === "working") return chalk18.cyan("\u25CF");
|
|
4326
4495
|
if (s === "error") return chalk18.red("\u25CF");
|
|
@@ -4380,10 +4549,10 @@ agent.command("get <ref>").description("Show one agent by object_id or slug").op
|
|
|
4380
4549
|
}
|
|
4381
4550
|
console.log();
|
|
4382
4551
|
console.log(chalk18.dim("--- spec ---"));
|
|
4383
|
-
console.log(
|
|
4552
|
+
console.log(yaml9.dump(result.agent.spec, { lineWidth: 100 }));
|
|
4384
4553
|
if (result.agent.mcp_config) {
|
|
4385
4554
|
console.log(chalk18.dim("--- mcp_config ---"));
|
|
4386
|
-
console.log(
|
|
4555
|
+
console.log(yaml9.dump(result.agent.mcp_config, { lineWidth: 100 }));
|
|
4387
4556
|
}
|
|
4388
4557
|
} catch (err) {
|
|
4389
4558
|
const apiErr = err;
|
|
@@ -4394,8 +4563,8 @@ agent.command("get <ref>").description("Show one agent by object_id or slug").op
|
|
|
4394
4563
|
agent.command("create").description("Create an agent from a YAML spec file").requiredOption("--from-file <file>", "YAML spec file with at least kind, slug, spec").option("--scope <scope>", "Storage scope: user (default) or project", "user").option("--project-root <path>", "Project root (required for --scope project)").action(async (opts) => {
|
|
4395
4564
|
let parsed;
|
|
4396
4565
|
try {
|
|
4397
|
-
const raw =
|
|
4398
|
-
parsed =
|
|
4566
|
+
const raw = fs25.readFileSync(path23.resolve(opts.fromFile), "utf-8");
|
|
4567
|
+
parsed = yaml9.load(raw);
|
|
4399
4568
|
} catch (err) {
|
|
4400
4569
|
fail5(`cannot read ${opts.fromFile}: ${err.message}`);
|
|
4401
4570
|
}
|
|
@@ -4410,17 +4579,17 @@ agent.command("create").description("Create an agent from a YAML spec file").req
|
|
|
4410
4579
|
agent.command("edit <ref>").description("Open the agent YAML in $EDITOR and save changes").action(async (ref) => {
|
|
4411
4580
|
try {
|
|
4412
4581
|
const result = await api.get(`/api/agents/${encodeURIComponent(ref)}`);
|
|
4413
|
-
const tmp =
|
|
4582
|
+
const tmp = path23.join(
|
|
4414
4583
|
process.env.TMPDIR || "/tmp",
|
|
4415
4584
|
`task0-agent-${result.agent.slug}-${Date.now()}.yml`
|
|
4416
4585
|
);
|
|
4417
|
-
|
|
4586
|
+
fs25.writeFileSync(tmp, yaml9.dump(result.agent, { lineWidth: 100 }), "utf-8");
|
|
4418
4587
|
const editor = process.env.EDITOR || "vi";
|
|
4419
4588
|
const r = spawnSync5(editor, [tmp], { stdio: "inherit" });
|
|
4420
4589
|
if (r.status !== 0) fail5(`editor exited with status ${r.status}`);
|
|
4421
|
-
const updated =
|
|
4590
|
+
const updated = yaml9.load(fs25.readFileSync(tmp, "utf-8"));
|
|
4422
4591
|
await api.put(`/api/agents/${encodeURIComponent(ref)}`, updated);
|
|
4423
|
-
|
|
4592
|
+
fs25.unlinkSync(tmp);
|
|
4424
4593
|
console.log(chalk18.green(`updated ${ref}`));
|
|
4425
4594
|
} catch (err) {
|
|
4426
4595
|
fail5(err.message);
|
|
@@ -4491,7 +4660,7 @@ var mcp = new Command18("mcp").description("Manage per-agent MCP config (mcp_con
|
|
|
4491
4660
|
mcp.command("set <ref>").description("Set mcp_config from a JSON file (replaces existing config)").requiredOption("--from-file <file>", "JSON file containing the mcp_config object").action(async (ref, opts) => {
|
|
4492
4661
|
let parsed;
|
|
4493
4662
|
try {
|
|
4494
|
-
parsed = JSON.parse(
|
|
4663
|
+
parsed = JSON.parse(fs25.readFileSync(path23.resolve(opts.fromFile), "utf-8"));
|
|
4495
4664
|
} catch (err) {
|
|
4496
4665
|
fail5(`cannot parse ${opts.fromFile}: ${err.message}`);
|
|
4497
4666
|
}
|
|
@@ -4640,36 +4809,36 @@ function pickRegisterAuth(flagToken) {
|
|
|
4640
4809
|
|
|
4641
4810
|
// src/core/daemon-rpc-handlers.ts
|
|
4642
4811
|
init_node();
|
|
4643
|
-
import
|
|
4644
|
-
import
|
|
4812
|
+
import fs28 from "fs";
|
|
4813
|
+
import path26 from "path";
|
|
4645
4814
|
|
|
4646
4815
|
// src/core/daemon-agent-run-runner.ts
|
|
4647
4816
|
import { execFileSync } from "child_process";
|
|
4648
|
-
import
|
|
4649
|
-
import
|
|
4817
|
+
import fs27 from "fs";
|
|
4818
|
+
import path25 from "path";
|
|
4650
4819
|
|
|
4651
4820
|
// src/core/daemon-agent-run-dir.ts
|
|
4652
4821
|
init_node();
|
|
4653
|
-
import
|
|
4654
|
-
import
|
|
4822
|
+
import fs26 from "fs";
|
|
4823
|
+
import path24 from "path";
|
|
4655
4824
|
function agentRunRoot() {
|
|
4656
|
-
return process.env.TASK0_DAEMON_AGENT_RUN_DIR ||
|
|
4825
|
+
return process.env.TASK0_DAEMON_AGENT_RUN_DIR || path24.join(profileDir(), "agent-run");
|
|
4657
4826
|
}
|
|
4658
4827
|
function agentRunDir(runId) {
|
|
4659
|
-
return
|
|
4828
|
+
return path24.join(agentRunRoot(), runId);
|
|
4660
4829
|
}
|
|
4661
4830
|
function agentRunStatusPath(runId) {
|
|
4662
|
-
return
|
|
4831
|
+
return path24.join(agentRunDir(runId), "status.json");
|
|
4663
4832
|
}
|
|
4664
4833
|
function ensureAgentRunDir(runId) {
|
|
4665
4834
|
const dir = agentRunDir(runId);
|
|
4666
|
-
|
|
4835
|
+
fs26.mkdirSync(dir, { recursive: true });
|
|
4667
4836
|
return dir;
|
|
4668
4837
|
}
|
|
4669
4838
|
function removeAgentRunDir(runId) {
|
|
4670
4839
|
const dir = agentRunDir(runId);
|
|
4671
|
-
if (!
|
|
4672
|
-
|
|
4840
|
+
if (!fs26.existsSync(dir)) return;
|
|
4841
|
+
fs26.rmSync(dir, { recursive: true, force: true });
|
|
4673
4842
|
}
|
|
4674
4843
|
|
|
4675
4844
|
// src/core/daemon-agent-run-runner.ts
|
|
@@ -4711,9 +4880,9 @@ function killSession2(sessionName) {
|
|
|
4711
4880
|
}
|
|
4712
4881
|
function readStatusFile(runId) {
|
|
4713
4882
|
const filePath = agentRunStatusPath(runId);
|
|
4714
|
-
if (!
|
|
4883
|
+
if (!fs27.existsSync(filePath)) return null;
|
|
4715
4884
|
try {
|
|
4716
|
-
const raw =
|
|
4885
|
+
const raw = fs27.readFileSync(filePath, "utf-8");
|
|
4717
4886
|
const parsed = JSON.parse(raw);
|
|
4718
4887
|
return { raw, parsed };
|
|
4719
4888
|
} catch {
|
|
@@ -4738,22 +4907,22 @@ function launchAgentRun(params) {
|
|
|
4738
4907
|
if (active.has(params.runId)) {
|
|
4739
4908
|
throw Object.assign(new Error(`agent run already active: ${params.runId}`), { code: "already_running" });
|
|
4740
4909
|
}
|
|
4741
|
-
if (!
|
|
4910
|
+
if (!fs27.existsSync(params.workspace)) {
|
|
4742
4911
|
throw Object.assign(new Error(`workspace not found: ${params.workspace}`), { code: "workspace_missing" });
|
|
4743
4912
|
}
|
|
4744
4913
|
const runDir = ensureAgentRunDir(params.runId);
|
|
4745
|
-
const scriptPath =
|
|
4746
|
-
|
|
4914
|
+
const scriptPath = path25.join(runDir, "script.sh");
|
|
4915
|
+
fs27.writeFileSync(scriptPath, params.scriptContent, { mode: 493 });
|
|
4747
4916
|
if (params.promptContent !== void 0) {
|
|
4748
|
-
|
|
4917
|
+
fs27.writeFileSync(path25.join(runDir, "prompt.txt"), params.promptContent, "utf-8");
|
|
4749
4918
|
}
|
|
4750
4919
|
for (const [name, content] of Object.entries(params.auxFiles ?? {})) {
|
|
4751
4920
|
if (name.includes("/") || name.includes("\\") || name === ".." || name === ".") {
|
|
4752
4921
|
throw Object.assign(new Error(`invalid aux file name: ${name}`), { code: "invalid_params" });
|
|
4753
4922
|
}
|
|
4754
|
-
|
|
4923
|
+
fs27.writeFileSync(path25.join(runDir, name), content, "utf-8");
|
|
4755
4924
|
}
|
|
4756
|
-
|
|
4925
|
+
fs27.writeFileSync(
|
|
4757
4926
|
agentRunStatusPath(params.runId),
|
|
4758
4927
|
JSON.stringify({ status: "starting", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
4759
4928
|
"utf-8"
|
|
@@ -4834,15 +5003,15 @@ function ensureString(value, name) {
|
|
|
4834
5003
|
}
|
|
4835
5004
|
function ensureSafeTaskId(value) {
|
|
4836
5005
|
const id = ensureString(value, "taskId");
|
|
4837
|
-
if (id.includes("/") || id.includes("\\") || id.includes("\0") || id === "." || id === ".." ||
|
|
5006
|
+
if (id.includes("/") || id.includes("\\") || id.includes("\0") || id === "." || id === ".." || path26.isAbsolute(id)) {
|
|
4838
5007
|
throw Object.assign(new Error(`invalid taskId: ${id}`), { code: "invalid_params" });
|
|
4839
5008
|
}
|
|
4840
5009
|
return id;
|
|
4841
5010
|
}
|
|
4842
5011
|
function assertContained(rootAbs, resolvedAbs) {
|
|
4843
|
-
const root =
|
|
4844
|
-
const target =
|
|
4845
|
-
if (target !== root && !target.startsWith(root +
|
|
5012
|
+
const root = path26.resolve(rootAbs);
|
|
5013
|
+
const target = path26.resolve(resolvedAbs);
|
|
5014
|
+
if (target !== root && !target.startsWith(root + path26.sep)) {
|
|
4846
5015
|
throw Object.assign(new Error("path escapes containment root"), { code: "invalid_params" });
|
|
4847
5016
|
}
|
|
4848
5017
|
}
|
|
@@ -4884,7 +5053,7 @@ var rpcHandlers = {
|
|
|
4884
5053
|
// missing object_id / id mismatches applied in place on the daemon's FS.
|
|
4885
5054
|
async scan_project(params) {
|
|
4886
5055
|
const rootPath = ensureString(params.rootPath, "rootPath");
|
|
4887
|
-
const name = typeof params.name === "string" && params.name ? params.name :
|
|
5056
|
+
const name = typeof params.name === "string" && params.name ? params.name : path26.basename(rootPath);
|
|
4888
5057
|
const result = scanProjectWithRepair(rootPath, name);
|
|
4889
5058
|
return { tasks: result.tasks, errors: result.errors };
|
|
4890
5059
|
},
|
|
@@ -4895,7 +5064,7 @@ var rpcHandlers = {
|
|
|
4895
5064
|
const filePath = ensureString(params.path, "path");
|
|
4896
5065
|
let stat;
|
|
4897
5066
|
try {
|
|
4898
|
-
stat =
|
|
5067
|
+
stat = fs28.statSync(filePath);
|
|
4899
5068
|
} catch {
|
|
4900
5069
|
throw Object.assign(new Error("file not found"), { code: "not_found" });
|
|
4901
5070
|
}
|
|
@@ -4905,7 +5074,7 @@ var rpcHandlers = {
|
|
|
4905
5074
|
if (stat.size > MAX_FILE_BYTES) {
|
|
4906
5075
|
throw Object.assign(new Error(`file too large (${stat.size} bytes > ${MAX_FILE_BYTES})`), { code: "too_large" });
|
|
4907
5076
|
}
|
|
4908
|
-
const content =
|
|
5077
|
+
const content = fs28.readFileSync(filePath, "utf-8");
|
|
4909
5078
|
return { content, size: stat.size, modifiedAt: stat.mtime.toISOString() };
|
|
4910
5079
|
},
|
|
4911
5080
|
// ---------------------------------------------------------------------
|
|
@@ -4920,15 +5089,15 @@ var rpcHandlers = {
|
|
|
4920
5089
|
},
|
|
4921
5090
|
async project_add(params, ctx) {
|
|
4922
5091
|
const rawPath = ensureString(params.path, "path");
|
|
4923
|
-
const absPath =
|
|
4924
|
-
if (!
|
|
5092
|
+
const absPath = path26.resolve(rawPath);
|
|
5093
|
+
if (!fs28.existsSync(absPath)) {
|
|
4925
5094
|
throw Object.assign(new Error(`path does not exist: ${absPath}`), { code: "not_found" });
|
|
4926
5095
|
}
|
|
4927
|
-
const stat =
|
|
5096
|
+
const stat = fs28.statSync(absPath);
|
|
4928
5097
|
if (!stat.isDirectory()) {
|
|
4929
5098
|
throw Object.assign(new Error(`path is not a directory: ${absPath}`), { code: "invalid_target" });
|
|
4930
5099
|
}
|
|
4931
|
-
const name = optionalString(params.name)?.trim() ||
|
|
5100
|
+
const name = optionalString(params.name)?.trim() || path26.basename(absPath);
|
|
4932
5101
|
const result = scanProjectWithRepair(absPath, name);
|
|
4933
5102
|
addSource({ name, type: "project", path: absPath, enabled: true });
|
|
4934
5103
|
ctx.notifyManifestChanged();
|
|
@@ -4977,24 +5146,24 @@ var rpcHandlers = {
|
|
|
4977
5146
|
if (!project2) {
|
|
4978
5147
|
throw Object.assign(new Error(`project not found: ${projectName}`), { code: "not_found" });
|
|
4979
5148
|
}
|
|
4980
|
-
const projectAbs =
|
|
4981
|
-
const projectConfig = readYaml(
|
|
5149
|
+
const projectAbs = path26.resolve(project2.path);
|
|
5150
|
+
const projectConfig = readYaml(path26.join(projectAbs, "task0.yml"));
|
|
4982
5151
|
if (!projectConfig) {
|
|
4983
5152
|
throw Object.assign(new Error(`invalid project: missing task0.yml at ${projectAbs}`), { code: "invalid_project" });
|
|
4984
5153
|
}
|
|
4985
|
-
const tasksRoot =
|
|
4986
|
-
const taskDir =
|
|
5154
|
+
const tasksRoot = path26.resolve(projectAbs, projectConfig.tasks_dir);
|
|
5155
|
+
const taskDir = path26.resolve(tasksRoot, taskId);
|
|
4987
5156
|
assertContained(tasksRoot, taskDir);
|
|
4988
|
-
if (!
|
|
5157
|
+
if (!fs28.existsSync(taskDir)) {
|
|
4989
5158
|
throw Object.assign(new Error(`task not found: ${taskId}`), { code: "not_found" });
|
|
4990
5159
|
}
|
|
4991
|
-
const taskYml =
|
|
4992
|
-
const
|
|
4993
|
-
if (!
|
|
5160
|
+
const taskYml = path26.join(taskDir, "task0.yml");
|
|
5161
|
+
const yaml11 = readYaml(taskYml);
|
|
5162
|
+
if (!yaml11) {
|
|
4994
5163
|
throw Object.assign(new Error(`task yaml missing or unreadable: ${taskYml}`), { code: "not_found" });
|
|
4995
5164
|
}
|
|
4996
|
-
const files =
|
|
4997
|
-
return { task_dir: taskDir, yaml:
|
|
5165
|
+
const files = fs28.readdirSync(taskDir).filter((name) => name !== "task0.yml");
|
|
5166
|
+
return { task_dir: taskDir, yaml: yaml11, files };
|
|
4998
5167
|
},
|
|
4999
5168
|
// Create a task directory + write task0.yml + write any additional named
|
|
5000
5169
|
// files. Hub decides the taskId and yaml content (slug/object_id are
|
|
@@ -5002,8 +5171,8 @@ var rpcHandlers = {
|
|
|
5002
5171
|
async task_create(params, ctx) {
|
|
5003
5172
|
const projectName = ensureString(params.projectName, "projectName");
|
|
5004
5173
|
const taskId = ensureSafeTaskId(params.taskId);
|
|
5005
|
-
const
|
|
5006
|
-
if (!
|
|
5174
|
+
const yaml11 = params.yaml;
|
|
5175
|
+
if (!yaml11 || typeof yaml11 !== "object") {
|
|
5007
5176
|
throw Object.assign(new Error("yaml (object) is required"), { code: "invalid_params" });
|
|
5008
5177
|
}
|
|
5009
5178
|
const files = params.files ?? {};
|
|
@@ -5011,24 +5180,24 @@ var rpcHandlers = {
|
|
|
5011
5180
|
if (!project2) {
|
|
5012
5181
|
throw Object.assign(new Error(`project not found: ${projectName}`), { code: "not_found" });
|
|
5013
5182
|
}
|
|
5014
|
-
const projectAbs =
|
|
5015
|
-
const projectConfig = readYaml(
|
|
5183
|
+
const projectAbs = path26.resolve(project2.path);
|
|
5184
|
+
const projectConfig = readYaml(path26.join(projectAbs, "task0.yml"));
|
|
5016
5185
|
if (!projectConfig) {
|
|
5017
5186
|
throw Object.assign(new Error(`invalid project: missing task0.yml at ${projectAbs}`), { code: "invalid_project" });
|
|
5018
5187
|
}
|
|
5019
|
-
const tasksRoot =
|
|
5020
|
-
const taskDir =
|
|
5188
|
+
const tasksRoot = path26.resolve(projectAbs, projectConfig.tasks_dir);
|
|
5189
|
+
const taskDir = path26.resolve(tasksRoot, taskId);
|
|
5021
5190
|
assertContained(tasksRoot, taskDir);
|
|
5022
|
-
if (
|
|
5191
|
+
if (fs28.existsSync(taskDir)) {
|
|
5023
5192
|
throw Object.assign(new Error(`task already exists: ${taskId}`), { code: "already_exists" });
|
|
5024
5193
|
}
|
|
5025
|
-
|
|
5026
|
-
writeYaml(
|
|
5194
|
+
fs28.mkdirSync(taskDir, { recursive: true });
|
|
5195
|
+
writeYaml(path26.join(taskDir, "task0.yml"), yaml11);
|
|
5027
5196
|
for (const [name, content] of Object.entries(files)) {
|
|
5028
5197
|
if (name.includes("/") || name.includes("\\") || name === ".." || name === ".") {
|
|
5029
5198
|
throw Object.assign(new Error(`invalid file name: ${name}`), { code: "invalid_params" });
|
|
5030
5199
|
}
|
|
5031
|
-
|
|
5200
|
+
fs28.writeFileSync(path26.join(taskDir, name), content, "utf-8");
|
|
5032
5201
|
}
|
|
5033
5202
|
ctx.notifyManifestChanged();
|
|
5034
5203
|
return { task_dir: taskDir };
|
|
@@ -5040,27 +5209,27 @@ var rpcHandlers = {
|
|
|
5040
5209
|
async task_write_yaml(params, ctx) {
|
|
5041
5210
|
const projectName = ensureString(params.projectName, "projectName");
|
|
5042
5211
|
const taskId = ensureSafeTaskId(params.taskId);
|
|
5043
|
-
const
|
|
5044
|
-
if (!
|
|
5212
|
+
const yaml11 = params.yaml;
|
|
5213
|
+
if (!yaml11 || typeof yaml11 !== "object") {
|
|
5045
5214
|
throw Object.assign(new Error("yaml (object) is required"), { code: "invalid_params" });
|
|
5046
5215
|
}
|
|
5047
5216
|
const project2 = listProjects().find((p) => p.name === projectName);
|
|
5048
5217
|
if (!project2) {
|
|
5049
5218
|
throw Object.assign(new Error(`project not found: ${projectName}`), { code: "not_found" });
|
|
5050
5219
|
}
|
|
5051
|
-
const projectAbs =
|
|
5052
|
-
const projectConfig = readYaml(
|
|
5220
|
+
const projectAbs = path26.resolve(project2.path);
|
|
5221
|
+
const projectConfig = readYaml(path26.join(projectAbs, "task0.yml"));
|
|
5053
5222
|
if (!projectConfig) {
|
|
5054
5223
|
throw Object.assign(new Error(`invalid project: missing task0.yml at ${projectAbs}`), { code: "invalid_project" });
|
|
5055
5224
|
}
|
|
5056
|
-
const tasksRoot =
|
|
5057
|
-
const taskDir =
|
|
5225
|
+
const tasksRoot = path26.resolve(projectAbs, projectConfig.tasks_dir);
|
|
5226
|
+
const taskDir = path26.resolve(tasksRoot, taskId);
|
|
5058
5227
|
assertContained(tasksRoot, taskDir);
|
|
5059
|
-
const taskYml =
|
|
5060
|
-
if (!
|
|
5228
|
+
const taskYml = path26.join(taskDir, "task0.yml");
|
|
5229
|
+
if (!fs28.existsSync(taskYml)) {
|
|
5061
5230
|
throw Object.assign(new Error(`task yaml not found: ${taskYml}`), { code: "not_found" });
|
|
5062
5231
|
}
|
|
5063
|
-
writeYaml(taskYml,
|
|
5232
|
+
writeYaml(taskYml, yaml11);
|
|
5064
5233
|
ctx.notifyManifestChanged();
|
|
5065
5234
|
return { task_dir: taskDir };
|
|
5066
5235
|
},
|
|
@@ -5073,18 +5242,18 @@ var rpcHandlers = {
|
|
|
5073
5242
|
if (!project2) {
|
|
5074
5243
|
throw Object.assign(new Error(`project not found: ${projectName}`), { code: "not_found" });
|
|
5075
5244
|
}
|
|
5076
|
-
const projectAbs =
|
|
5077
|
-
const projectConfig = readYaml(
|
|
5245
|
+
const projectAbs = path26.resolve(project2.path);
|
|
5246
|
+
const projectConfig = readYaml(path26.join(projectAbs, "task0.yml"));
|
|
5078
5247
|
if (!projectConfig) {
|
|
5079
5248
|
throw Object.assign(new Error(`invalid project: missing task0.yml at ${projectAbs}`), { code: "invalid_project" });
|
|
5080
5249
|
}
|
|
5081
|
-
const tasksRoot =
|
|
5082
|
-
const taskDir =
|
|
5250
|
+
const tasksRoot = path26.resolve(projectAbs, projectConfig.tasks_dir);
|
|
5251
|
+
const taskDir = path26.resolve(tasksRoot, taskId);
|
|
5083
5252
|
assertContained(tasksRoot, taskDir);
|
|
5084
|
-
if (!
|
|
5253
|
+
if (!fs28.existsSync(taskDir)) {
|
|
5085
5254
|
throw Object.assign(new Error(`task not found: ${taskId}`), { code: "not_found" });
|
|
5086
5255
|
}
|
|
5087
|
-
|
|
5256
|
+
fs28.rmSync(taskDir, { recursive: true, force: true });
|
|
5088
5257
|
ctx.notifyManifestChanged();
|
|
5089
5258
|
return { task_dir: taskDir, deleted: true };
|
|
5090
5259
|
},
|
|
@@ -5115,9 +5284,9 @@ var rpcHandlers = {
|
|
|
5115
5284
|
const projectSkills = getProjectAgentSkills(workspace);
|
|
5116
5285
|
const globalSkills = getGlobalAgentSkills();
|
|
5117
5286
|
const all = [...projectSkills, ...globalSkills];
|
|
5118
|
-
const resolved =
|
|
5287
|
+
const resolved = path26.resolve(filePath);
|
|
5119
5288
|
const skill = all.find(
|
|
5120
|
-
(s) => s.agent === agent2 &&
|
|
5289
|
+
(s) => s.agent === agent2 && path26.resolve(s.filePath) === resolved
|
|
5121
5290
|
) ?? null;
|
|
5122
5291
|
return { skill };
|
|
5123
5292
|
},
|
|
@@ -5150,16 +5319,16 @@ var rpcHandlers = {
|
|
|
5150
5319
|
|
|
5151
5320
|
// src/core/daemon-service/launchd.ts
|
|
5152
5321
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
5153
|
-
import
|
|
5154
|
-
import
|
|
5322
|
+
import fs30 from "fs";
|
|
5323
|
+
import path28 from "path";
|
|
5155
5324
|
|
|
5156
5325
|
// src/core/daemon-service/binary.ts
|
|
5157
|
-
import
|
|
5326
|
+
import fs29 from "fs";
|
|
5158
5327
|
import { fileURLToPath } from "url";
|
|
5159
5328
|
function resolveTask0Invocation() {
|
|
5160
5329
|
const node = process.execPath;
|
|
5161
5330
|
const argv1 = process.argv[1] ?? fileURLToPath(import.meta.url);
|
|
5162
|
-
const main2 =
|
|
5331
|
+
const main2 = fs29.realpathSync(argv1);
|
|
5163
5332
|
if (!isInstalledBuild(main2) && process.env.TASK0_ALLOW_DEV_SERVICE !== "1") {
|
|
5164
5333
|
throw new Error(
|
|
5165
5334
|
`Refusing to install autostart service pointing at ${main2}.
|
|
@@ -5177,15 +5346,19 @@ function isInstalledBuild(p) {
|
|
|
5177
5346
|
// src/core/daemon-service/paths.ts
|
|
5178
5347
|
init_node();
|
|
5179
5348
|
import os5 from "os";
|
|
5180
|
-
import
|
|
5349
|
+
import path27 from "path";
|
|
5181
5350
|
|
|
5182
5351
|
// src/core/daemon-service/types.ts
|
|
5352
|
+
init_node();
|
|
5183
5353
|
import crypto2 from "crypto";
|
|
5184
5354
|
var BASE_SERVICE_LABEL = "cc.cy0.task0";
|
|
5185
5355
|
function serviceLabel() {
|
|
5186
|
-
const home = process.env.TASK0_HOME;
|
|
5187
|
-
|
|
5188
|
-
|
|
5356
|
+
const home = process.env.TASK0_HOME ?? "";
|
|
5357
|
+
const profile3 = currentProfileName();
|
|
5358
|
+
if ((!home || home.length === 0) && profile3 === DEFAULT_PROFILE_NAME) {
|
|
5359
|
+
return BASE_SERVICE_LABEL;
|
|
5360
|
+
}
|
|
5361
|
+
const hash = crypto2.createHash("sha256").update(`${home}\0${profile3}`).digest("hex").slice(0, 8);
|
|
5189
5362
|
return `${BASE_SERVICE_LABEL}.${hash}`;
|
|
5190
5363
|
}
|
|
5191
5364
|
|
|
@@ -5194,21 +5367,21 @@ function unitPath(scope) {
|
|
|
5194
5367
|
const platform = process.platform;
|
|
5195
5368
|
const label = serviceLabel();
|
|
5196
5369
|
if (platform === "darwin") {
|
|
5197
|
-
return scope === "user" ?
|
|
5370
|
+
return scope === "user" ? path27.join(os5.homedir(), "Library", "LaunchAgents", `${label}.plist`) : path27.join("/", "Library", "LaunchDaemons", `${label}.plist`);
|
|
5198
5371
|
}
|
|
5199
5372
|
if (platform === "linux") {
|
|
5200
|
-
return scope === "user" ?
|
|
5373
|
+
return scope === "user" ? path27.join(os5.homedir(), ".config", "systemd", "user", `${label}.service`) : path27.join("/", "etc", "systemd", "system", `${label}.service`);
|
|
5201
5374
|
}
|
|
5202
5375
|
throw new Error(`Unsupported platform for service install: ${platform}`);
|
|
5203
5376
|
}
|
|
5204
5377
|
function logDir() {
|
|
5205
|
-
return
|
|
5378
|
+
return path27.join(profileDir(), "logs");
|
|
5206
5379
|
}
|
|
5207
5380
|
function logPaths() {
|
|
5208
5381
|
const dir = logDir();
|
|
5209
5382
|
return {
|
|
5210
|
-
out:
|
|
5211
|
-
err:
|
|
5383
|
+
out: path27.join(dir, "daemon.out.log"),
|
|
5384
|
+
err: path27.join(dir, "daemon.err.log")
|
|
5212
5385
|
};
|
|
5213
5386
|
}
|
|
5214
5387
|
|
|
@@ -5283,8 +5456,8 @@ function createLaunchdManager(scope) {
|
|
|
5283
5456
|
const logs = logPaths();
|
|
5284
5457
|
async function install() {
|
|
5285
5458
|
const inv = resolveTask0Invocation();
|
|
5286
|
-
|
|
5287
|
-
|
|
5459
|
+
fs30.mkdirSync(logDir(), { recursive: true });
|
|
5460
|
+
fs30.mkdirSync(path28.dirname(file), { recursive: true });
|
|
5288
5461
|
const body = renderPlist({
|
|
5289
5462
|
node: inv.node,
|
|
5290
5463
|
main: inv.main,
|
|
@@ -5294,7 +5467,7 @@ function createLaunchdManager(scope) {
|
|
|
5294
5467
|
err: logs.err,
|
|
5295
5468
|
task0Env: collectTask0Env()
|
|
5296
5469
|
});
|
|
5297
|
-
|
|
5470
|
+
fs30.writeFileSync(file, body, { mode: 420 });
|
|
5298
5471
|
const bootstrap = run2("launchctl", ["bootstrap", domainTarget(scope), file]);
|
|
5299
5472
|
if (bootstrap.code !== 0) {
|
|
5300
5473
|
const already = /already loaded|service already bootstrapped/i.test(bootstrap.stderr);
|
|
@@ -5320,9 +5493,9 @@ function createLaunchdManager(scope) {
|
|
|
5320
5493
|
}
|
|
5321
5494
|
async function uninstall() {
|
|
5322
5495
|
run2("launchctl", ["bootout", serviceTarget(scope)]);
|
|
5323
|
-
if (
|
|
5496
|
+
if (fs30.existsSync(file)) {
|
|
5324
5497
|
run2("launchctl", ["unload", file]);
|
|
5325
|
-
|
|
5498
|
+
fs30.unlinkSync(file);
|
|
5326
5499
|
}
|
|
5327
5500
|
}
|
|
5328
5501
|
async function start() {
|
|
@@ -5341,7 +5514,7 @@ function createLaunchdManager(scope) {
|
|
|
5341
5514
|
}
|
|
5342
5515
|
}
|
|
5343
5516
|
async function status() {
|
|
5344
|
-
if (!
|
|
5517
|
+
if (!fs30.existsSync(file)) return "absent";
|
|
5345
5518
|
const printed = run2("launchctl", ["print", serviceTarget(scope)]);
|
|
5346
5519
|
if (printed.code !== 0) return "installed";
|
|
5347
5520
|
const out = printed.stdout;
|
|
@@ -5364,8 +5537,8 @@ function createLaunchdManager(scope) {
|
|
|
5364
5537
|
|
|
5365
5538
|
// src/core/daemon-service/systemd.ts
|
|
5366
5539
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
5367
|
-
import
|
|
5368
|
-
import
|
|
5540
|
+
import fs31 from "fs";
|
|
5541
|
+
import path29 from "path";
|
|
5369
5542
|
function shellEscape(s) {
|
|
5370
5543
|
if (!/[\s"\\$]/.test(s)) return s;
|
|
5371
5544
|
return `"${s.replace(/[\\"]/g, (m) => `\\${m}`)}"`;
|
|
@@ -5415,8 +5588,8 @@ function createSystemdManager(scope) {
|
|
|
5415
5588
|
const unitName = `${serviceLabel()}.service`;
|
|
5416
5589
|
async function install() {
|
|
5417
5590
|
const inv = resolveTask0Invocation();
|
|
5418
|
-
|
|
5419
|
-
|
|
5591
|
+
fs31.mkdirSync(logDir(), { recursive: true });
|
|
5592
|
+
fs31.mkdirSync(path29.dirname(file), { recursive: true });
|
|
5420
5593
|
const body = renderUnit({
|
|
5421
5594
|
node: inv.node,
|
|
5422
5595
|
main: inv.main,
|
|
@@ -5426,7 +5599,7 @@ function createSystemdManager(scope) {
|
|
|
5426
5599
|
scope,
|
|
5427
5600
|
task0Env: collectTask0Env2()
|
|
5428
5601
|
});
|
|
5429
|
-
|
|
5602
|
+
fs31.writeFileSync(file, body, { mode: 420 });
|
|
5430
5603
|
const reload = run3("systemctl", [...scopeFlag(scope), "daemon-reload"]);
|
|
5431
5604
|
if (reload.code !== 0) {
|
|
5432
5605
|
throw new Error(`systemctl daemon-reload failed: ${reload.stderr}`);
|
|
@@ -5435,8 +5608,8 @@ function createSystemdManager(scope) {
|
|
|
5435
5608
|
}
|
|
5436
5609
|
async function uninstall() {
|
|
5437
5610
|
run3("systemctl", [...scopeFlag(scope), "disable", "--now", unitName]);
|
|
5438
|
-
if (
|
|
5439
|
-
|
|
5611
|
+
if (fs31.existsSync(file)) {
|
|
5612
|
+
fs31.unlinkSync(file);
|
|
5440
5613
|
}
|
|
5441
5614
|
run3("systemctl", [...scopeFlag(scope), "daemon-reload"]);
|
|
5442
5615
|
}
|
|
@@ -5453,7 +5626,7 @@ function createSystemdManager(scope) {
|
|
|
5453
5626
|
}
|
|
5454
5627
|
}
|
|
5455
5628
|
async function status() {
|
|
5456
|
-
if (!
|
|
5629
|
+
if (!fs31.existsSync(file)) return "absent";
|
|
5457
5630
|
const res = run3("systemctl", [...scopeFlag(scope), "is-active", unitName]);
|
|
5458
5631
|
const out = res.stdout.trim();
|
|
5459
5632
|
if (out === "active") return "running";
|
|
@@ -5489,14 +5662,14 @@ function getServiceManager(scope) {
|
|
|
5489
5662
|
// src/core/cli-version.ts
|
|
5490
5663
|
import { readFileSync } from "fs";
|
|
5491
5664
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5492
|
-
import
|
|
5665
|
+
import path30 from "path";
|
|
5493
5666
|
var cached2 = null;
|
|
5494
5667
|
function readCliVersion() {
|
|
5495
5668
|
if (cached2 !== null) return cached2;
|
|
5496
|
-
const here =
|
|
5669
|
+
const here = path30.dirname(fileURLToPath2(import.meta.url));
|
|
5497
5670
|
const candidates = [
|
|
5498
|
-
|
|
5499
|
-
|
|
5671
|
+
path30.resolve(here, "..", "package.json"),
|
|
5672
|
+
path30.resolve(here, "..", "..", "package.json")
|
|
5500
5673
|
];
|
|
5501
5674
|
for (const candidate of candidates) {
|
|
5502
5675
|
try {
|
|
@@ -5512,73 +5685,6 @@ function readCliVersion() {
|
|
|
5512
5685
|
return cached2;
|
|
5513
5686
|
}
|
|
5514
5687
|
|
|
5515
|
-
// src/core/scaffold-default-agents.ts
|
|
5516
|
-
init_node();
|
|
5517
|
-
import fs29 from "fs";
|
|
5518
|
-
import path29 from "path";
|
|
5519
|
-
import yaml9 from "js-yaml";
|
|
5520
|
-
function userAgentsDir() {
|
|
5521
|
-
return path29.join(task0Home(), "agents");
|
|
5522
|
-
}
|
|
5523
|
-
var NAMES = {
|
|
5524
|
-
"claude-code": "Claude Code",
|
|
5525
|
-
codex: "Codex",
|
|
5526
|
-
"cursor-agent": "Cursor"
|
|
5527
|
-
};
|
|
5528
|
-
var DESCRIPTIONS = {
|
|
5529
|
-
"claude-code": "Anthropic Claude Code CLI, launched inside a tmux session against the task workspace.",
|
|
5530
|
-
codex: "OpenAI Codex CLI, launched with `codex exec` against the task workspace.",
|
|
5531
|
-
"cursor-agent": "Cursor agent CLI (`cursor-agent`), launched against the task workspace."
|
|
5532
|
-
};
|
|
5533
|
-
var STABLE_OBJECT_IDS = {
|
|
5534
|
-
"claude-code": "agt_sysCC",
|
|
5535
|
-
codex: "agt_sysCX",
|
|
5536
|
-
"cursor-agent": "agt_sysCR"
|
|
5537
|
-
};
|
|
5538
|
-
function buildDefaultAgentYaml(provider) {
|
|
5539
|
-
const fetchCommand = defaultAgentModelFetchCommand(provider);
|
|
5540
|
-
const fetchFormat = defaultAgentModelOutputFormat(provider);
|
|
5541
|
-
return {
|
|
5542
|
-
object_id: STABLE_OBJECT_IDS[provider],
|
|
5543
|
-
slug: provider,
|
|
5544
|
-
name: NAMES[provider],
|
|
5545
|
-
description: DESCRIPTIONS[provider],
|
|
5546
|
-
kind: "coding",
|
|
5547
|
-
spec: {
|
|
5548
|
-
agent_provider: provider,
|
|
5549
|
-
model: AGENT_DEFAULT_MODEL[provider],
|
|
5550
|
-
effort: AGENT_DEFAULT_EFFORT[provider],
|
|
5551
|
-
available_models: AGENT_MODEL_DEFAULTS[provider].map((m) => ({ id: m.id, label: m.label })),
|
|
5552
|
-
available_efforts: AGENT_EFFORT_DEFAULTS[provider].map((m) => ({ id: m.id, label: m.label })),
|
|
5553
|
-
...fetchCommand ? { model_fetch_command: fetchCommand } : {},
|
|
5554
|
-
...fetchFormat ? { model_fetch_format: fetchFormat } : {}
|
|
5555
|
-
}
|
|
5556
|
-
};
|
|
5557
|
-
}
|
|
5558
|
-
function isDirectoryEmpty(dir) {
|
|
5559
|
-
if (!fs29.existsSync(dir)) return true;
|
|
5560
|
-
try {
|
|
5561
|
-
return fs29.readdirSync(dir).length === 0;
|
|
5562
|
-
} catch {
|
|
5563
|
-
return true;
|
|
5564
|
-
}
|
|
5565
|
-
}
|
|
5566
|
-
function scaffoldDefaultAgentsIfEmpty() {
|
|
5567
|
-
const dir = userAgentsDir();
|
|
5568
|
-
if (!isDirectoryEmpty(dir)) {
|
|
5569
|
-
return { scaffolded: false, written: [] };
|
|
5570
|
-
}
|
|
5571
|
-
fs29.mkdirSync(dir, { recursive: true });
|
|
5572
|
-
const written = [];
|
|
5573
|
-
for (const provider of AGENT_PROVIDERS) {
|
|
5574
|
-
const file = path29.join(dir, `${provider}.yml`);
|
|
5575
|
-
const body = yaml9.dump(buildDefaultAgentYaml(provider), { lineWidth: 100 });
|
|
5576
|
-
fs29.writeFileSync(file, body, "utf-8");
|
|
5577
|
-
written.push(file);
|
|
5578
|
-
}
|
|
5579
|
-
return { scaffolded: true, written };
|
|
5580
|
-
}
|
|
5581
|
-
|
|
5582
5688
|
// src/commands/daemon.ts
|
|
5583
5689
|
async function dispatchRpc(ws, id, method, params) {
|
|
5584
5690
|
const handler = rpcHandlers[method];
|
|
@@ -5836,12 +5942,6 @@ daemonCmd.command("stop").description("Stop the autostart service via launchctl
|
|
|
5836
5942
|
});
|
|
5837
5943
|
daemonCmd.command("run").description("Run the daemon WebSocket loop in foreground (invoked by the service unit; useful for debugging)").action(async () => {
|
|
5838
5944
|
const identity = loadRequiredIdentity();
|
|
5839
|
-
const scaffold = scaffoldDefaultAgentsIfEmpty();
|
|
5840
|
-
if (scaffold.scaffolded) {
|
|
5841
|
-
console.log(
|
|
5842
|
-
chalk19.green(`Scaffolded ${scaffold.written.length} default agent yml(s) under ~/.task0/agents/`)
|
|
5843
|
-
);
|
|
5844
|
-
}
|
|
5845
5945
|
const wsUrl = identity.server_url.replace(/^http/, "ws").replace(/\/$/, "") + "/ws/daemon";
|
|
5846
5946
|
console.log(chalk19.green(`Starting daemon ${identity.daemon_id} \u2192 ${wsUrl}`));
|
|
5847
5947
|
let reconnectDelay = 1e3;
|
|
@@ -6055,8 +6155,8 @@ function fail7(message, code = 1) {
|
|
|
6055
6155
|
console.error(chalk20.red(message));
|
|
6056
6156
|
process.exit(code);
|
|
6057
6157
|
}
|
|
6058
|
-
async function callServer(
|
|
6059
|
-
const url = `${serverBase2()}${
|
|
6158
|
+
async function callServer(path32, init = {}) {
|
|
6159
|
+
const url = `${serverBase2()}${path32}`;
|
|
6060
6160
|
let auth;
|
|
6061
6161
|
try {
|
|
6062
6162
|
auth = adminAuthHeader();
|
|
@@ -6560,158 +6660,184 @@ automation.command("runs <id>").description("List recent runs for an automation"
|
|
|
6560
6660
|
});
|
|
6561
6661
|
|
|
6562
6662
|
// src/commands/profile.ts
|
|
6563
|
-
|
|
6564
|
-
import
|
|
6565
|
-
import
|
|
6663
|
+
init_node();
|
|
6664
|
+
import fs32 from "fs";
|
|
6665
|
+
import path31 from "path";
|
|
6566
6666
|
import { Command as Command23 } from "commander";
|
|
6567
6667
|
import chalk23 from "chalk";
|
|
6568
|
-
|
|
6668
|
+
import yaml10 from "js-yaml";
|
|
6569
6669
|
function fail9(msg) {
|
|
6570
6670
|
console.error(chalk23.red(msg));
|
|
6571
6671
|
process.exit(1);
|
|
6572
6672
|
}
|
|
6573
|
-
function
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
function readDaemonAt(home) {
|
|
6577
|
-
const file = path30.join(home, "daemon.json");
|
|
6578
|
-
if (!fs30.existsSync(file)) return null;
|
|
6673
|
+
function readDaemonAt(dir) {
|
|
6674
|
+
const file = path31.join(dir, "daemon.json");
|
|
6675
|
+
if (!fs32.existsSync(file)) return null;
|
|
6579
6676
|
try {
|
|
6580
|
-
return JSON.parse(
|
|
6677
|
+
return JSON.parse(fs32.readFileSync(file, "utf-8"));
|
|
6581
6678
|
} catch {
|
|
6582
6679
|
return null;
|
|
6583
6680
|
}
|
|
6584
6681
|
}
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6682
|
+
function readProfileApiUrl(name) {
|
|
6683
|
+
const file = path31.join(profileDir(name), "config.yml");
|
|
6684
|
+
if (!fs32.existsSync(file)) return void 0;
|
|
6685
|
+
try {
|
|
6686
|
+
const data = yaml10.load(fs32.readFileSync(file, "utf-8"));
|
|
6687
|
+
return data?.api_url;
|
|
6688
|
+
} catch {
|
|
6689
|
+
return void 0;
|
|
6690
|
+
}
|
|
6691
|
+
}
|
|
6692
|
+
var profile2 = new Command23("profile").description("Manage CLI profiles (directories under ~/.task0/profiles/)");
|
|
6693
|
+
profile2.command("list").description("List configured profiles (one directory per profile)").action(() => {
|
|
6694
|
+
const names = listProfileNames();
|
|
6695
|
+
const current = currentProfileName();
|
|
6590
6696
|
if (names.length === 0) {
|
|
6591
|
-
console.log("No profiles configured.
|
|
6592
|
-
console.log(chalk23.dim(`(currently running in legacy mode \u2014 TASK0_HOME=${process.env.TASK0_HOME ?? legacyTask0Home()})`));
|
|
6697
|
+
console.log("No profiles configured.");
|
|
6593
6698
|
return;
|
|
6594
6699
|
}
|
|
6595
6700
|
for (const name of names) {
|
|
6596
|
-
const entry = profiles[name];
|
|
6597
6701
|
const marker = name === current ? chalk23.green("\u25CF ") : " ";
|
|
6598
|
-
const
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
if (!current) {
|
|
6602
|
-
console.log(chalk23.dim("\nNo current profile selected. Use `task0 profile use <name>` to activate one."));
|
|
6702
|
+
const apiUrl = readProfileApiUrl(name);
|
|
6703
|
+
const url = apiUrl ? chalk23.dim(` \u2192 ${apiUrl}`) : "";
|
|
6704
|
+
console.log(`${marker}${name}${url}`);
|
|
6603
6705
|
}
|
|
6604
6706
|
});
|
|
6605
|
-
profile2.command("add <name>").description("
|
|
6606
|
-
if (!
|
|
6607
|
-
fail9(`Invalid profile name "${name}". Must match
|
|
6707
|
+
profile2.command("add <name>").description("Create a new profile directory (use `task0 profile use <name>` to activate)").option("--api-url <url>", "API server URL the CLI should call when this profile is active").action((name, opts) => {
|
|
6708
|
+
if (!isValidProfileName(name)) {
|
|
6709
|
+
fail9(`Invalid profile name "${name}". Must match /^[a-zA-Z0-9_-]+$/.`);
|
|
6608
6710
|
}
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
fail9(`Profile "${name}" already exists.`);
|
|
6711
|
+
if (name === DEFAULT_PROFILE_NAME) {
|
|
6712
|
+
fail9(`"${DEFAULT_PROFILE_NAME}" is reserved; it always exists.`);
|
|
6612
6713
|
}
|
|
6613
|
-
const
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
if (path30.resolve(entry2.task0_home) === absHome) {
|
|
6617
|
-
fail9(`Profile "${otherName}" already uses task0_home "${absHome}". Two profiles cannot share a task0_home (service labels collide).`);
|
|
6618
|
-
}
|
|
6619
|
-
}
|
|
6620
|
-
const parent = path30.dirname(absHome);
|
|
6621
|
-
if (!fs30.existsSync(parent)) {
|
|
6622
|
-
fail9(`Parent directory does not exist: ${parent}`);
|
|
6714
|
+
const dir = profileDir(name);
|
|
6715
|
+
if (fs32.existsSync(dir)) {
|
|
6716
|
+
fail9(`Profile "${name}" already exists at ${dir}.`);
|
|
6623
6717
|
}
|
|
6718
|
+
fs32.mkdirSync(dir, { recursive: true });
|
|
6719
|
+
const prev = process.env.TASK0_PROFILE;
|
|
6720
|
+
process.env.TASK0_PROFILE = name;
|
|
6624
6721
|
try {
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
}
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
const legacyHome = legacyTask0Home();
|
|
6632
|
-
const legacy = readDaemonAt(legacyHome);
|
|
6633
|
-
if (legacy) {
|
|
6634
|
-
addProfile("default", {
|
|
6635
|
-
task0_home: legacyHome,
|
|
6636
|
-
...legacy.server_url ? { api_url: legacy.server_url } : {}
|
|
6637
|
-
});
|
|
6638
|
-
console.log(chalk23.dim(`Auto-imported existing ~/.task0 state as profile "default" (use \`task0 profile use default\` to keep using it).`));
|
|
6639
|
-
}
|
|
6640
|
-
}
|
|
6641
|
-
const entry = {
|
|
6642
|
-
task0_home: absHome,
|
|
6643
|
-
...opts.apiUrl ? { api_url: opts.apiUrl } : {}
|
|
6644
|
-
};
|
|
6645
|
-
addProfile(name, entry);
|
|
6646
|
-
fs30.mkdirSync(absHome, { recursive: true });
|
|
6647
|
-
const adopted = readDaemonAt(absHome);
|
|
6648
|
-
if (adopted) {
|
|
6649
|
-
console.log(chalk23.yellow(`warn: ${absHome} already contains daemon state. Profile "${name}" will adopt it.`));
|
|
6722
|
+
const config = loadConfig();
|
|
6723
|
+
if (opts.apiUrl) config.api_url = opts.apiUrl;
|
|
6724
|
+
saveConfig(config);
|
|
6725
|
+
} finally {
|
|
6726
|
+
if (prev === void 0) delete process.env.TASK0_PROFILE;
|
|
6727
|
+
else process.env.TASK0_PROFILE = prev;
|
|
6650
6728
|
}
|
|
6651
6729
|
console.log(chalk23.green(`Added profile "${name}"`));
|
|
6652
|
-
console.log(`
|
|
6653
|
-
if (
|
|
6730
|
+
console.log(` dir: ${dir}`);
|
|
6731
|
+
if (opts.apiUrl) console.log(` api_url: ${opts.apiUrl}`);
|
|
6654
6732
|
console.log(chalk23.dim(`Use \`task0 profile use ${name}\` to activate.`));
|
|
6655
6733
|
});
|
|
6656
|
-
profile2.command("remove <name>").description(
|
|
6657
|
-
|
|
6658
|
-
|
|
6734
|
+
profile2.command("remove <name>").description('Delete a profile directory (refuses to delete "default")').option("-f, --force", `Remove even if it's the current profile (resets current to "default")`).action((name, opts) => {
|
|
6735
|
+
if (!isValidProfileName(name)) {
|
|
6736
|
+
fail9(`Invalid profile name "${name}".`);
|
|
6737
|
+
}
|
|
6738
|
+
if (name === DEFAULT_PROFILE_NAME) {
|
|
6739
|
+
fail9(`"${DEFAULT_PROFILE_NAME}" cannot be removed.`);
|
|
6740
|
+
}
|
|
6741
|
+
const dir = profileDir(name);
|
|
6742
|
+
if (!fs32.existsSync(dir)) {
|
|
6659
6743
|
fail9(`Profile "${name}" not found.`);
|
|
6660
6744
|
}
|
|
6661
|
-
const current =
|
|
6745
|
+
const current = currentProfileName();
|
|
6662
6746
|
if (current === name && !opts.force) {
|
|
6663
|
-
fail9(`Profile "${name}" is current. Re-run with --force to remove it
|
|
6747
|
+
fail9(`Profile "${name}" is current. Re-run with --force to remove it.`);
|
|
6664
6748
|
}
|
|
6665
|
-
|
|
6666
|
-
console.log(chalk23.green(`Removed profile "${name}".`));
|
|
6749
|
+
fs32.rmSync(dir, { recursive: true, force: true });
|
|
6667
6750
|
if (current === name) {
|
|
6668
|
-
|
|
6751
|
+
writeCurrentProfile(null);
|
|
6752
|
+
console.log(chalk23.dim('Current profile reset to "default".'));
|
|
6669
6753
|
}
|
|
6670
|
-
console.log(chalk23.
|
|
6754
|
+
console.log(chalk23.green(`Removed profile "${name}".`));
|
|
6671
6755
|
});
|
|
6672
6756
|
profile2.command("use <name>").description("Set the current profile").action((name) => {
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6757
|
+
if (!isValidProfileName(name)) {
|
|
6758
|
+
fail9(`Invalid profile name "${name}".`);
|
|
6759
|
+
}
|
|
6760
|
+
if (!fs32.existsSync(profileDir(name))) {
|
|
6761
|
+
const names = listProfileNames();
|
|
6762
|
+
fail9(`Profile "${name}" not found. Available: ${names.join(", ")}.`);
|
|
6677
6763
|
}
|
|
6678
|
-
|
|
6764
|
+
writeCurrentProfile(name);
|
|
6679
6765
|
console.log(chalk23.green(`Now using profile "${name}".`));
|
|
6680
6766
|
});
|
|
6681
|
-
profile2.command("current").description(
|
|
6682
|
-
|
|
6683
|
-
if (!current) {
|
|
6684
|
-
console.error(chalk23.dim("No current profile. Set one with `task0 profile use <name>`."));
|
|
6685
|
-
process.exit(1);
|
|
6686
|
-
}
|
|
6687
|
-
console.log(current);
|
|
6767
|
+
profile2.command("current").description('Print the current profile name (always succeeds; defaults to "default")').action(() => {
|
|
6768
|
+
console.log(currentProfileName());
|
|
6688
6769
|
});
|
|
6689
|
-
profile2.command("show [name]").description("Show a profile's configuration and
|
|
6690
|
-
const target = name ??
|
|
6691
|
-
if (!target) {
|
|
6692
|
-
fail9(
|
|
6770
|
+
profile2.command("show [name]").description("Show a profile's configuration and daemon registration").action((name) => {
|
|
6771
|
+
const target = name ?? currentProfileName();
|
|
6772
|
+
if (!isValidProfileName(target)) {
|
|
6773
|
+
fail9(`Invalid profile name "${target}".`);
|
|
6693
6774
|
}
|
|
6694
|
-
const
|
|
6695
|
-
if (!
|
|
6775
|
+
const dir = profileDir(target);
|
|
6776
|
+
if (!fs32.existsSync(dir)) {
|
|
6696
6777
|
fail9(`Profile "${target}" not found.`);
|
|
6697
6778
|
}
|
|
6698
|
-
const current =
|
|
6779
|
+
const current = currentProfileName();
|
|
6780
|
+
const apiUrl = readProfileApiUrl(target);
|
|
6781
|
+
const identity = readDaemonAt(dir);
|
|
6699
6782
|
console.log(`${chalk23.bold(target)}${current === target ? chalk23.green(" (current)") : ""}`);
|
|
6700
|
-
console.log(`
|
|
6701
|
-
console.log(`
|
|
6702
|
-
console.log(`
|
|
6703
|
-
const identity = readDaemonAt(entry.task0_home);
|
|
6783
|
+
console.log(` dir: ${dir}`);
|
|
6784
|
+
console.log(` config: ${path31.join(dir, "config.yml")}`);
|
|
6785
|
+
console.log(` api_url: ${apiUrl ?? chalk23.dim("(unset)")}`);
|
|
6704
6786
|
if (!identity) {
|
|
6705
6787
|
console.log(chalk23.dim(" daemon.json: (not registered yet)"));
|
|
6706
6788
|
return;
|
|
6707
6789
|
}
|
|
6708
6790
|
console.log(` daemon_id: ${identity.daemon_id ?? chalk23.dim("(missing)")}`);
|
|
6709
6791
|
console.log(` daemon.server_url: ${identity.server_url ?? chalk23.dim("(missing)")}`);
|
|
6710
|
-
if (
|
|
6792
|
+
if (apiUrl && identity.server_url && apiUrl !== identity.server_url) {
|
|
6711
6793
|
console.log(chalk23.yellow(` warn: profile.api_url and daemon.json.server_url disagree.`));
|
|
6712
|
-
console.log(chalk23.dim(` Re-register the daemon to align: task0 --profile ${target} daemon register --server ${
|
|
6794
|
+
console.log(chalk23.dim(` Re-register the daemon to align: task0 --profile ${target} daemon register --server ${apiUrl}`));
|
|
6795
|
+
}
|
|
6796
|
+
});
|
|
6797
|
+
|
|
6798
|
+
// src/commands/migrate-layout.ts
|
|
6799
|
+
import { Command as Command24 } from "commander";
|
|
6800
|
+
import chalk24 from "chalk";
|
|
6801
|
+
var migrateLayout = new Command24("migrate-layout").description("Move legacy daemon-side files into the v0.8 ~/.task0/profiles/<name>/ layout").option("-n, --dry-run", "Show what would happen without modifying disk").option("-f, --force", "Reset the migration marker first; useful when the previous run was interrupted").action((opts) => {
|
|
6802
|
+
if (opts.force && !opts.dryRun) {
|
|
6803
|
+
const removed = resetMigrationMarker();
|
|
6804
|
+
if (removed) console.log(chalk24.dim("Reset migration marker."));
|
|
6805
|
+
}
|
|
6806
|
+
const result = migrateCliLayout({ dryRun: opts.dryRun });
|
|
6807
|
+
if (result.alreadyMigrated) {
|
|
6808
|
+
console.log(chalk24.dim("Already migrated \u2014 nothing to do."));
|
|
6809
|
+
console.log(chalk24.dim("Pass --force to rerun."));
|
|
6810
|
+
return;
|
|
6811
|
+
}
|
|
6812
|
+
if (result.steps.length === 0) {
|
|
6813
|
+
console.log("No legacy files found.");
|
|
6814
|
+
return;
|
|
6815
|
+
}
|
|
6816
|
+
if (opts.dryRun) {
|
|
6817
|
+
console.log(chalk24.bold("Dry run \u2014 no changes written:"));
|
|
6818
|
+
} else {
|
|
6819
|
+
console.log(chalk24.bold(`Migrated ${result.steps.length} item(s):`));
|
|
6820
|
+
}
|
|
6821
|
+
for (const step of result.steps) {
|
|
6822
|
+
console.log(` ${formatStep(step)}`);
|
|
6713
6823
|
}
|
|
6714
6824
|
});
|
|
6825
|
+
function formatStep(step) {
|
|
6826
|
+
switch (step.kind) {
|
|
6827
|
+
case "mv":
|
|
6828
|
+
return `mv ${step.from} \u2192 ${step.to}${step.detail ? ` (${step.detail})` : ""}`;
|
|
6829
|
+
case "mkdir":
|
|
6830
|
+
return `mkdir ${step.to}`;
|
|
6831
|
+
case "write":
|
|
6832
|
+
return `write ${step.to}${step.detail ? ` (${step.detail})` : ""}`;
|
|
6833
|
+
case "extract":
|
|
6834
|
+
return `update ${step.from}${step.detail ? ` (${step.detail})` : ""}`;
|
|
6835
|
+
case "skip":
|
|
6836
|
+
return `skip ${step.from ?? step.to}${step.detail ? ` (${step.detail})` : ""}`;
|
|
6837
|
+
case "note":
|
|
6838
|
+
return `note ${step.detail ?? ""}`;
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6715
6841
|
|
|
6716
6842
|
// src/core/error-capture.ts
|
|
6717
6843
|
init_node();
|
|
@@ -6811,7 +6937,7 @@ function captureTopLevel(err, options) {
|
|
|
6811
6937
|
|
|
6812
6938
|
// src/main.ts
|
|
6813
6939
|
var TASK0_VERSION = readCliVersion();
|
|
6814
|
-
var program = new
|
|
6940
|
+
var program = new Command25().name("task0").description("Task-centric control layer for agent workflow").version(TASK0_VERSION).option("--profile <name>", "Use a named profile under ~/.task0/profiles/<name>/ (overrides .current pointer and TASK0_PROFILE env)");
|
|
6815
6941
|
program.addCommand(source);
|
|
6816
6942
|
program.addCommand(project);
|
|
6817
6943
|
program.addCommand(task);
|
|
@@ -6830,12 +6956,23 @@ program.addCommand(userCmd);
|
|
|
6830
6956
|
program.addCommand(error);
|
|
6831
6957
|
program.addCommand(automation);
|
|
6832
6958
|
program.addCommand(profile2);
|
|
6959
|
+
program.addCommand(migrateLayout);
|
|
6833
6960
|
async function main() {
|
|
6961
|
+
try {
|
|
6962
|
+
const result = migrateCliLayout();
|
|
6963
|
+
if (result.ran && process.env.TASK0_LOG_MIGRATION !== "off") {
|
|
6964
|
+
console.error(chalk25.dim(`task0: migrated CLI layout (${result.steps.length} step(s))`));
|
|
6965
|
+
}
|
|
6966
|
+
} catch (err) {
|
|
6967
|
+
console.error(chalk25.yellow(
|
|
6968
|
+
`task0: layout migration failed: ${err instanceof Error ? err.message : String(err)}. Continuing.`
|
|
6969
|
+
));
|
|
6970
|
+
}
|
|
6834
6971
|
try {
|
|
6835
6972
|
activateProfile(process.argv);
|
|
6836
6973
|
} catch (err) {
|
|
6837
6974
|
if (err instanceof ProfileNotFoundError) {
|
|
6838
|
-
console.error(
|
|
6975
|
+
console.error(chalk25.red(err.message));
|
|
6839
6976
|
process.exit(1);
|
|
6840
6977
|
}
|
|
6841
6978
|
throw err;
|