trackops 1.0.1 → 2.0.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/README.md +292 -272
- package/bin/trackops.js +108 -50
- package/lib/config.js +267 -38
- package/lib/control.js +534 -480
- package/lib/env.js +244 -0
- package/lib/i18n.js +61 -53
- package/lib/init.js +170 -47
- package/lib/locale.js +63 -0
- package/lib/opera-bootstrap.js +1075 -0
- package/lib/opera.js +524 -125
- package/lib/preferences.js +74 -0
- package/lib/registry.js +27 -13
- package/lib/release.js +56 -0
- package/lib/resources.js +42 -0
- package/lib/runtime-state.js +144 -0
- package/lib/server.js +1004 -521
- package/lib/skills.js +148 -124
- package/lib/workspace.js +260 -0
- package/locales/en.json +418 -132
- package/locales/es.json +418 -132
- package/package.json +8 -9
- package/scripts/postinstall-locale.js +21 -0
- package/scripts/skills-marketplace-smoke.js +124 -0
- package/scripts/smoke-tests.js +570 -0
- package/scripts/sync-skill-version.js +21 -0
- package/scripts/validate-skill.js +89 -0
- package/skills/trackops/SKILL.md +89 -0
- package/skills/trackops/agents/openai.yaml +3 -0
- package/skills/trackops/references/activation.md +73 -0
- package/skills/trackops/references/troubleshooting.md +49 -0
- package/skills/trackops/references/workflow.md +26 -0
- package/skills/trackops/scripts/bootstrap-trackops.js +203 -0
- package/skills/trackops/skill.json +29 -0
- package/templates/opera/agent.md +10 -9
- package/templates/opera/architecture/dependency-graph.md +24 -0
- package/templates/opera/architecture/runtime-automation.md +24 -0
- package/templates/opera/architecture/runtime-operations.md +34 -0
- package/templates/opera/en/agent.md +27 -0
- package/templates/opera/en/architecture/dependency-graph.md +24 -0
- package/templates/opera/en/architecture/runtime-automation.md +24 -0
- package/templates/opera/en/architecture/runtime-operations.md +34 -0
- package/templates/opera/en/genesis.md +79 -0
- package/templates/opera/en/references/autonomy-and-recovery.md +23 -0
- package/templates/opera/en/references/opera-cycle.md +62 -0
- package/templates/opera/en/registry.md +28 -0
- package/templates/opera/en/reviews/delivery-audit.md +18 -0
- package/templates/opera/en/reviews/integration-audit.md +18 -0
- package/templates/opera/en/router.md +49 -0
- package/templates/opera/genesis.md +79 -94
- package/templates/opera/reviews/delivery-audit.md +18 -0
- package/templates/opera/reviews/integration-audit.md +18 -0
- package/templates/opera/router.md +15 -5
- package/templates/skills/changelog-updater/locales/en/SKILL.md +11 -0
- package/templates/skills/commiter/locales/en/SKILL.md +11 -0
- package/templates/skills/opera-contract-auditor/SKILL.md +38 -0
- package/templates/skills/opera-contract-auditor/locales/en/SKILL.md +38 -0
- package/templates/skills/opera-policy-guard/SKILL.md +26 -0
- package/templates/skills/opera-policy-guard/locales/en/SKILL.md +26 -0
- package/templates/skills/project-starter-skill/SKILL.md +89 -164
- package/templates/skills/project-starter-skill/locales/en/SKILL.md +104 -0
- package/ui/css/panels.css +956 -953
- package/ui/index.html +1 -1
- package/ui/js/api.js +211 -194
- package/ui/js/app.js +200 -199
- package/ui/js/i18n.js +14 -0
- package/ui/js/onboarding.js +439 -437
- package/ui/js/state.js +130 -129
- package/ui/js/utils.js +175 -172
- package/ui/js/views/board.js +255 -254
- package/ui/js/views/execution.js +256 -256
- package/ui/js/views/insights.js +340 -339
- package/ui/js/views/overview.js +366 -361
- package/ui/js/views/settings.js +340 -202
- package/ui/js/views/sidebar.js +131 -132
- package/ui/js/views/skills.js +163 -162
- package/ui/js/views/tasks.js +406 -405
- package/ui/js/views/topbar.js +239 -183
- package/templates/etapa/agent.md +0 -26
- package/templates/etapa/genesis.md +0 -94
- package/templates/etapa/references/autonomy-and-recovery.md +0 -117
- package/templates/etapa/references/etapa-cycle.md +0 -193
- package/templates/etapa/registry.md +0 -28
- package/templates/etapa/router.md +0 -39
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const config = require("./config");
|
|
4
|
+
const runtimeState = require("./runtime-state");
|
|
5
|
+
const { setLocale, t } = require("./i18n");
|
|
6
|
+
const { normalizeLocale } = require("./locale");
|
|
7
|
+
|
|
8
|
+
function formatLocaleSource(source) {
|
|
9
|
+
return t(`locale.source.${String(source || "").trim()}`) || source || t("locale.none");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function resolveProjectLocale(root) {
|
|
13
|
+
const context = config.resolveWorkspaceContext(root || process.cwd());
|
|
14
|
+
if (!context) return null;
|
|
15
|
+
try {
|
|
16
|
+
return config.getLocale(config.loadControl(context));
|
|
17
|
+
} catch (_error) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function cmdLocale(args = [], root) {
|
|
23
|
+
const sub = args[0];
|
|
24
|
+
const projectLocale = resolveProjectLocale(root);
|
|
25
|
+
const doctor = runtimeState.doctorLocale(projectLocale);
|
|
26
|
+
setLocale(doctor.effectiveLocale);
|
|
27
|
+
|
|
28
|
+
if (sub === "get" || !sub) {
|
|
29
|
+
console.log(`${t("locale.effective")}: ${doctor.effectiveLocale}`);
|
|
30
|
+
console.log(`${t("locale.source")}: ${formatLocaleSource(doctor.source)}`);
|
|
31
|
+
console.log(`${t("locale.global")}: ${doctor.globalLocale || t("locale.none")}`);
|
|
32
|
+
if (doctor.projectLocale) {
|
|
33
|
+
console.log(`${t("locale.project")}: ${doctor.projectLocale}`);
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (sub === "set") {
|
|
39
|
+
const nextLocale = normalizeLocale(args[1]);
|
|
40
|
+
if (!nextLocale) {
|
|
41
|
+
throw new Error(t("locale.invalid", { value: String(args[1] || "") }));
|
|
42
|
+
}
|
|
43
|
+
runtimeState.writeRuntimeState({ locale: nextLocale, localeSource: "manual" });
|
|
44
|
+
setLocale(nextLocale);
|
|
45
|
+
console.log(t("locale.updated", { locale: nextLocale }));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(t("cli.usage.locale"));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function cmdDoctor(args = [], root) {
|
|
53
|
+
const sub = args[0];
|
|
54
|
+
if (sub !== "locale") {
|
|
55
|
+
console.log(t("cli.usage.doctor"));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const projectLocale = resolveProjectLocale(root);
|
|
60
|
+
const doctor = runtimeState.doctorLocale(projectLocale);
|
|
61
|
+
setLocale(doctor.effectiveLocale);
|
|
62
|
+
console.log(`${t("locale.effective")}: ${doctor.effectiveLocale}`);
|
|
63
|
+
console.log(`${t("locale.source")}: ${formatLocaleSource(doctor.source)}`);
|
|
64
|
+
console.log(`${t("locale.global")}: ${doctor.globalLocale || t("locale.none")}`);
|
|
65
|
+
console.log(`${t("locale.project")}: ${doctor.projectLocale || t("locale.none")}`);
|
|
66
|
+
console.log(`${t("locale.env")}: ${doctor.envLocale || t("locale.none")}`);
|
|
67
|
+
console.log(`${t("locale.system")}: ${doctor.systemLocale}`);
|
|
68
|
+
console.log(`${t("locale.runtimeFile")}: ${doctor.runtimeFile}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
cmdLocale,
|
|
73
|
+
cmdDoctor,
|
|
74
|
+
};
|
package/lib/registry.js
CHANGED
|
@@ -4,6 +4,7 @@ const fs = require("fs");
|
|
|
4
4
|
const os = require("os");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
|
|
7
|
+
const config = require("./config");
|
|
7
8
|
const { t } = require("./i18n");
|
|
8
9
|
|
|
9
10
|
const REGISTRY_DIR = path.join(os.homedir(), ".codex", "trackops");
|
|
@@ -39,9 +40,11 @@ function ensureRegistryDir() {
|
|
|
39
40
|
function loadRegistry() {
|
|
40
41
|
ensureRegistryDir();
|
|
41
42
|
if (!fs.existsSync(REGISTRY_FILE)) {
|
|
42
|
-
return { version:
|
|
43
|
+
return { version: 2, updatedAt: nowIso(), projects: [] };
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
+
const registry = JSON.parse(fs.readFileSync(REGISTRY_FILE, "utf8"));
|
|
46
|
+
if (!registry.version) registry.version = 1;
|
|
47
|
+
return registry;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
function saveRegistry(registry) {
|
|
@@ -51,19 +54,21 @@ function saveRegistry(registry) {
|
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
function isProjectInstalled(rootDir) {
|
|
54
|
-
|
|
57
|
+
const context = config.resolveWorkspaceContext(rootDir);
|
|
58
|
+
return Boolean(context && fs.existsSync(context.controlFile));
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
function inspectProject(rootDir) {
|
|
58
|
-
const
|
|
59
|
-
const
|
|
62
|
+
const context = config.resolveWorkspaceContext(rootDir) || config.createLegacyContext(rootDir);
|
|
63
|
+
const root = context.workspaceRoot;
|
|
64
|
+
const controlStateFile = context.controlFile;
|
|
60
65
|
|
|
61
66
|
if (!fs.existsSync(controlStateFile)) {
|
|
62
67
|
throw new Error(`Project '${root}' does not have trackops installed.`);
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
let packageJson = {};
|
|
66
|
-
const packageFile =
|
|
71
|
+
const packageFile = context.packageFile;
|
|
67
72
|
if (fs.existsSync(packageFile)) {
|
|
68
73
|
packageJson = JSON.parse(fs.readFileSync(packageFile, "utf8"));
|
|
69
74
|
}
|
|
@@ -76,6 +81,10 @@ function inspectProject(rootDir) {
|
|
|
76
81
|
id: `${slugify(projectName) || "project"}-${shortHash(root)}`,
|
|
77
82
|
name: projectName,
|
|
78
83
|
root,
|
|
84
|
+
workspaceRoot: root,
|
|
85
|
+
appRoot: context.appRoot,
|
|
86
|
+
opsRoot: context.opsRoot,
|
|
87
|
+
layout: context.layout,
|
|
79
88
|
packageName: packageJson.name || null,
|
|
80
89
|
controlStateFile,
|
|
81
90
|
registeredAt: nowIso(),
|
|
@@ -86,7 +95,7 @@ function inspectProject(rootDir) {
|
|
|
86
95
|
function registerProject(rootDir) {
|
|
87
96
|
const registry = loadRegistry();
|
|
88
97
|
const entry = inspectProject(rootDir);
|
|
89
|
-
const existingIndex = registry.projects.findIndex((p) => p.root === entry.
|
|
98
|
+
const existingIndex = registry.projects.findIndex((p) => (p.workspaceRoot || p.root) === entry.workspaceRoot);
|
|
90
99
|
|
|
91
100
|
if (existingIndex >= 0) {
|
|
92
101
|
registry.projects[existingIndex] = {
|
|
@@ -101,13 +110,14 @@ function registerProject(rootDir) {
|
|
|
101
110
|
|
|
102
111
|
registry.projects.sort((a, b) => a.name.localeCompare(b.name));
|
|
103
112
|
saveRegistry(registry);
|
|
104
|
-
return registry.projects.find((p) => p.root === entry.
|
|
113
|
+
return registry.projects.find((p) => (p.workspaceRoot || p.root) === entry.workspaceRoot);
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
function unregisterProject(rootDir) {
|
|
108
|
-
const
|
|
117
|
+
const context = config.resolveWorkspaceContext(rootDir) || config.createLegacyContext(rootDir);
|
|
118
|
+
const root = context.workspaceRoot;
|
|
109
119
|
const registry = loadRegistry();
|
|
110
|
-
registry.projects = registry.projects.filter((p) => p.root !== root);
|
|
120
|
+
registry.projects = registry.projects.filter((p) => (p.workspaceRoot || p.root) !== root);
|
|
111
121
|
saveRegistry(registry);
|
|
112
122
|
return registry;
|
|
113
123
|
}
|
|
@@ -125,7 +135,9 @@ function resolveProject(projectRef, fallbackRoot) {
|
|
|
125
135
|
|
|
126
136
|
if (!projectRef) {
|
|
127
137
|
if (fallbackRoot) {
|
|
128
|
-
|
|
138
|
+
const resolved = config.resolveWorkspaceContext(fallbackRoot);
|
|
139
|
+
const target = resolved ? resolved.workspaceRoot : path.resolve(fallbackRoot);
|
|
140
|
+
return projects.find((p) => (p.workspaceRoot || p.root) === target) || null;
|
|
129
141
|
}
|
|
130
142
|
return projects[0] || null;
|
|
131
143
|
}
|
|
@@ -133,7 +145,7 @@ function resolveProject(projectRef, fallbackRoot) {
|
|
|
133
145
|
const normalizedRef = path.resolve(projectRef);
|
|
134
146
|
return (
|
|
135
147
|
projects.find((p) => p.id === projectRef) ||
|
|
136
|
-
projects.find((p) => p.root === normalizedRef) ||
|
|
148
|
+
projects.find((p) => (p.workspaceRoot || p.root) === normalizedRef) ||
|
|
137
149
|
null
|
|
138
150
|
);
|
|
139
151
|
}
|
|
@@ -155,7 +167,9 @@ function cmdList() {
|
|
|
155
167
|
projects.forEach((project, index) => {
|
|
156
168
|
console.log(`${index + 1}. ${project.name}`);
|
|
157
169
|
console.log(` id: ${project.id}`);
|
|
158
|
-
console.log(` root: ${project.root}`);
|
|
170
|
+
console.log(` root: ${project.workspaceRoot || project.root}`);
|
|
171
|
+
console.log(` app: ${project.appRoot}`);
|
|
172
|
+
console.log(` ops: ${project.opsRoot}`);
|
|
159
173
|
console.log(` available: ${project.available ? "yes" : "no"}`);
|
|
160
174
|
});
|
|
161
175
|
}
|
package/lib/release.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require("child_process");
|
|
4
|
+
const config = require("./config");
|
|
5
|
+
|
|
6
|
+
function git(args, cwd) {
|
|
7
|
+
return spawnSync("git", args, { cwd, encoding: "utf8" });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function runGit(args, cwd, errorMessage) {
|
|
11
|
+
const result = git(args, cwd);
|
|
12
|
+
if (result.status !== 0) {
|
|
13
|
+
throw new Error(result.stderr || result.stdout || errorMessage);
|
|
14
|
+
}
|
|
15
|
+
return result.stdout.trim();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseArgs(args = []) {
|
|
19
|
+
return {
|
|
20
|
+
push: args.includes("--push"),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function cmdRelease(root, args = []) {
|
|
25
|
+
const context = config.ensureContext(root);
|
|
26
|
+
if (context.layout !== "split") {
|
|
27
|
+
throw new Error("trackops release requires a split workspace.");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const options = parseArgs(args);
|
|
31
|
+
const status = runGit(["status", "--porcelain"], context.workspaceRoot, "git status failed");
|
|
32
|
+
if (context.publish.requireCleanWorktree && status.trim()) {
|
|
33
|
+
throw new Error("trackops release requires a clean git worktree.");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const currentBranch = runGit(["branch", "--show-current"], context.workspaceRoot, "git branch failed");
|
|
37
|
+
if (currentBranch !== context.branches.development) {
|
|
38
|
+
throw new Error(`trackops release must run from '${context.branches.development}'.`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const splitCommit = runGit(
|
|
42
|
+
["subtree", "split", "--prefix", context.publish.sourceDir, context.branches.development],
|
|
43
|
+
context.workspaceRoot,
|
|
44
|
+
"git subtree split failed",
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
runGit(["branch", "-f", context.branches.publish, splitCommit], context.workspaceRoot, "git branch update failed");
|
|
48
|
+
|
|
49
|
+
if (options.push) {
|
|
50
|
+
runGit(["push", "origin", context.branches.publish, "--force-with-lease"], context.workspaceRoot, "git push failed");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(`Release branch '${context.branches.publish}' updated from '${context.branches.development}'.`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { cmdRelease };
|
package/lib/resources.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const { normalizeLocale } = require("./locale");
|
|
7
|
+
|
|
8
|
+
function existing(filePath) {
|
|
9
|
+
return filePath && fs.existsSync(filePath) ? filePath : null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function resolveLocalizedFile(baseDir, locale, relativePath) {
|
|
13
|
+
const normalized = normalizeLocale(locale) || "es";
|
|
14
|
+
return (
|
|
15
|
+
existing(path.join(baseDir, "locales", normalized, relativePath)) ||
|
|
16
|
+
existing(path.join(baseDir, normalized, relativePath)) ||
|
|
17
|
+
existing(path.join(baseDir, relativePath)) ||
|
|
18
|
+
existing(path.join(baseDir, "locales", "es", relativePath)) ||
|
|
19
|
+
existing(path.join(baseDir, "es", relativePath))
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveLocalizedDir(baseDir, locale, relativePath = "") {
|
|
24
|
+
const normalized = normalizeLocale(locale) || "es";
|
|
25
|
+
return (
|
|
26
|
+
existing(path.join(baseDir, "locales", normalized, relativePath)) ||
|
|
27
|
+
existing(path.join(baseDir, normalized, relativePath)) ||
|
|
28
|
+
existing(path.join(baseDir, relativePath)) ||
|
|
29
|
+
existing(path.join(baseDir, "locales", "es", relativePath)) ||
|
|
30
|
+
existing(path.join(baseDir, "es", relativePath))
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolveSkillFile(templatesDir, skillName, locale) {
|
|
35
|
+
return resolveLocalizedFile(path.join(templatesDir, skillName), locale, "SKILL.md");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
resolveLocalizedFile,
|
|
40
|
+
resolveLocalizedDir,
|
|
41
|
+
resolveSkillFile,
|
|
42
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const { detectSystemLocale, isInteractive, normalizeLocale, promptForLocale } = require("./locale");
|
|
8
|
+
|
|
9
|
+
function getRuntimeHome() {
|
|
10
|
+
return process.env.TRACKOPS_BOOTSTRAP_HOME || os.homedir();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getRuntimeDir() {
|
|
14
|
+
return path.join(getRuntimeHome(), ".trackops");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getRuntimeFile() {
|
|
18
|
+
return path.join(getRuntimeDir(), "runtime.json");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function defaultRuntimeState() {
|
|
22
|
+
return {
|
|
23
|
+
locale: null,
|
|
24
|
+
localeSource: null,
|
|
25
|
+
skill: null,
|
|
26
|
+
skillVersion: null,
|
|
27
|
+
runtimePackage: "trackops",
|
|
28
|
+
runtimeVersion: null,
|
|
29
|
+
bootstrapPolicy: null,
|
|
30
|
+
supportedAgentsV1: [],
|
|
31
|
+
verifiedAt: null,
|
|
32
|
+
verifiedWith: null,
|
|
33
|
+
executable: null,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeRuntimeState(state) {
|
|
38
|
+
const base = defaultRuntimeState();
|
|
39
|
+
const next = { ...base, ...(state || {}) };
|
|
40
|
+
next.locale = normalizeLocale(next.locale);
|
|
41
|
+
next.localeSource = next.localeSource || null;
|
|
42
|
+
next.supportedAgentsV1 = Array.isArray(next.supportedAgentsV1) ? next.supportedAgentsV1 : [];
|
|
43
|
+
return next;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readRuntimeState() {
|
|
47
|
+
const file = getRuntimeFile();
|
|
48
|
+
if (!fs.existsSync(file)) return defaultRuntimeState();
|
|
49
|
+
try {
|
|
50
|
+
return normalizeRuntimeState(JSON.parse(fs.readFileSync(file, "utf8")));
|
|
51
|
+
} catch (_error) {
|
|
52
|
+
return defaultRuntimeState();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function writeRuntimeState(patch) {
|
|
57
|
+
const file = getRuntimeFile();
|
|
58
|
+
const state = normalizeRuntimeState({ ...readRuntimeState(), ...(patch || {}) });
|
|
59
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
60
|
+
fs.writeFileSync(file, `${JSON.stringify(state, null, 2)}\n`, "utf8");
|
|
61
|
+
return state;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getGlobalLocale() {
|
|
65
|
+
return readRuntimeState().locale || null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveLocaleState(options = {}) {
|
|
69
|
+
const runtime = readRuntimeState();
|
|
70
|
+
const explicit = normalizeLocale(options.explicitLocale);
|
|
71
|
+
if (explicit) return { locale: explicit, source: "explicit", runtime };
|
|
72
|
+
|
|
73
|
+
const project = normalizeLocale(options.projectLocale);
|
|
74
|
+
if (project) return { locale: project, source: "project", runtime };
|
|
75
|
+
|
|
76
|
+
const global = normalizeLocale(runtime.locale);
|
|
77
|
+
if (global) return { locale: global, source: "global", runtime };
|
|
78
|
+
|
|
79
|
+
const envLocale = normalizeLocale(process.env.TRACKOPS_LOCALE);
|
|
80
|
+
if (envLocale) return { locale: envLocale, source: "env", runtime };
|
|
81
|
+
|
|
82
|
+
const system = detectSystemLocale();
|
|
83
|
+
return { locale: system || "es", source: "system", runtime };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function ensureGlobalLocale(options = {}) {
|
|
87
|
+
const current = readRuntimeState();
|
|
88
|
+
const explicit = normalizeLocale(options.preferredLocale);
|
|
89
|
+
if (explicit) {
|
|
90
|
+
return {
|
|
91
|
+
locale: explicit,
|
|
92
|
+
source: "explicit",
|
|
93
|
+
runtime: writeRuntimeState({ locale: explicit, localeSource: "explicit" }),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (normalizeLocale(current.locale)) {
|
|
98
|
+
return { locale: current.locale, source: "global", runtime: current };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let locale = null;
|
|
102
|
+
let source = null;
|
|
103
|
+
if (options.interactive !== false && isInteractive()) {
|
|
104
|
+
locale = await promptForLocale(detectSystemLocale());
|
|
105
|
+
source = "prompt";
|
|
106
|
+
} else {
|
|
107
|
+
locale = detectSystemLocale() || "es";
|
|
108
|
+
source = "system";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
locale,
|
|
113
|
+
source,
|
|
114
|
+
runtime: writeRuntimeState({ locale, localeSource: source }),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function doctorLocale(projectLocale = null, explicitLocale = null) {
|
|
119
|
+
const resolved = resolveLocaleState({ explicitLocale, projectLocale });
|
|
120
|
+
const runtime = readRuntimeState();
|
|
121
|
+
return {
|
|
122
|
+
effectiveLocale: resolved.locale || "es",
|
|
123
|
+
source: resolved.source,
|
|
124
|
+
projectLocale: normalizeLocale(projectLocale),
|
|
125
|
+
globalLocale: normalizeLocale(runtime.locale),
|
|
126
|
+
envLocale: normalizeLocale(process.env.TRACKOPS_LOCALE),
|
|
127
|
+
systemLocale: detectSystemLocale() || "es",
|
|
128
|
+
runtimeFile: getRuntimeFile(),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = {
|
|
133
|
+
getRuntimeHome,
|
|
134
|
+
getRuntimeDir,
|
|
135
|
+
getRuntimeFile,
|
|
136
|
+
defaultRuntimeState,
|
|
137
|
+
normalizeRuntimeState,
|
|
138
|
+
readRuntimeState,
|
|
139
|
+
writeRuntimeState,
|
|
140
|
+
getGlobalLocale,
|
|
141
|
+
resolveLocaleState,
|
|
142
|
+
ensureGlobalLocale,
|
|
143
|
+
doctorLocale,
|
|
144
|
+
};
|