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
package/bin/trackops.js
CHANGED
|
@@ -1,75 +1,127 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const config = require("../lib/config");
|
|
5
|
+
const runtimeState = require("../lib/runtime-state");
|
|
6
|
+
const { setLocale, t } = require("../lib/i18n");
|
|
7
|
+
const pkg = require("../package.json");
|
|
5
8
|
|
|
6
9
|
const command = process.argv[2];
|
|
7
10
|
const args = process.argv.slice(3);
|
|
8
11
|
|
|
12
|
+
function initCliLocale() {
|
|
13
|
+
let projectLocale = null;
|
|
14
|
+
try {
|
|
15
|
+
const context = config.resolveWorkspaceContext();
|
|
16
|
+
if (context) {
|
|
17
|
+
projectLocale = config.getLocale(config.loadControl(context));
|
|
18
|
+
}
|
|
19
|
+
} catch (_error) {
|
|
20
|
+
projectLocale = null;
|
|
21
|
+
}
|
|
22
|
+
const doctor = runtimeState.doctorLocale(projectLocale);
|
|
23
|
+
setLocale(doctor.effectiveLocale);
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
function resolveRoot() {
|
|
10
|
-
const
|
|
11
|
-
if (!
|
|
12
|
-
console.error("
|
|
27
|
+
const context = config.resolveWorkspaceContext();
|
|
28
|
+
if (!context) {
|
|
29
|
+
console.error(t("cli.error.noWorkspace"));
|
|
13
30
|
process.exit(1);
|
|
14
31
|
}
|
|
15
|
-
return
|
|
32
|
+
return context.workspaceRoot;
|
|
16
33
|
}
|
|
17
34
|
|
|
18
|
-
function run() {
|
|
35
|
+
async function run() {
|
|
36
|
+
initCliLocale();
|
|
19
37
|
try {
|
|
20
|
-
switch (command) {
|
|
21
|
-
case "init":
|
|
22
|
-
require("../lib/init").cmdInit(args);
|
|
23
|
-
break;
|
|
24
|
-
|
|
25
|
-
case "status":
|
|
26
|
-
require("../lib/control").cmdStatus(resolveRoot());
|
|
27
|
-
break;
|
|
28
|
-
|
|
29
|
-
case "next":
|
|
30
|
-
require("../lib/control").cmdNext(resolveRoot());
|
|
31
|
-
break;
|
|
32
|
-
|
|
33
|
-
case "sync":
|
|
34
|
-
require("../lib/control").cmdSync(resolveRoot());
|
|
35
|
-
break;
|
|
36
|
-
|
|
37
|
-
case "dashboard":
|
|
38
|
-
require("../lib/server").run();
|
|
38
|
+
switch (command) {
|
|
39
|
+
case "init":
|
|
40
|
+
await require("../lib/init").cmdInit(args);
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
case "status":
|
|
44
|
+
require("../lib/control").cmdStatus(resolveRoot());
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
case "next":
|
|
48
|
+
require("../lib/control").cmdNext(resolveRoot());
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
case "sync":
|
|
52
|
+
require("../lib/control").cmdSync(resolveRoot());
|
|
53
|
+
break;
|
|
54
|
+
|
|
55
|
+
case "dashboard":
|
|
56
|
+
await require("../lib/server").run(args);
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
case "refresh-repo":
|
|
60
|
+
require("../lib/control").cmdRefreshRepo(resolveRoot(), args);
|
|
61
|
+
break;
|
|
62
|
+
|
|
63
|
+
case "install-hooks":
|
|
64
|
+
require("../lib/control").cmdInstallHooks(resolveRoot());
|
|
65
|
+
break;
|
|
66
|
+
|
|
67
|
+
case "task":
|
|
68
|
+
require("../lib/control").cmdTask(resolveRoot(), args);
|
|
69
|
+
break;
|
|
70
|
+
|
|
71
|
+
case "register":
|
|
72
|
+
require("../lib/registry").cmdRegister(config.resolveProjectRoot() || process.cwd());
|
|
39
73
|
break;
|
|
40
|
-
|
|
41
|
-
case "
|
|
42
|
-
require("../lib/
|
|
74
|
+
|
|
75
|
+
case "projects":
|
|
76
|
+
require("../lib/registry").cmdList();
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case "workspace": {
|
|
80
|
+
const workspace = require("../lib/workspace");
|
|
81
|
+
const sub = args[0];
|
|
82
|
+
const root = config.resolveProjectRoot() || process.cwd();
|
|
83
|
+
if (sub === "status") workspace.cmdStatus(root);
|
|
84
|
+
else if (sub === "migrate") workspace.cmdMigrate(root, args.slice(1));
|
|
85
|
+
else console.log(t("cli.usage.workspace"));
|
|
43
86
|
break;
|
|
87
|
+
}
|
|
44
88
|
|
|
45
|
-
case "
|
|
46
|
-
require("../lib/
|
|
89
|
+
case "env": {
|
|
90
|
+
const env = require("../lib/env");
|
|
91
|
+
const sub = args[0];
|
|
92
|
+
const root = config.resolveProjectRoot() || process.cwd();
|
|
93
|
+
if (sub === "status") env.cmdStatus(root);
|
|
94
|
+
else if (sub === "sync") env.cmdSync(root);
|
|
95
|
+
else console.log(t("cli.usage.env"));
|
|
47
96
|
break;
|
|
97
|
+
}
|
|
48
98
|
|
|
49
|
-
case "
|
|
50
|
-
require("../lib/
|
|
99
|
+
case "release":
|
|
100
|
+
require("../lib/release").cmdRelease(config.resolveProjectRoot() || process.cwd(), args);
|
|
51
101
|
break;
|
|
52
102
|
|
|
53
|
-
case "
|
|
54
|
-
require("../lib/
|
|
103
|
+
case "locale":
|
|
104
|
+
require("../lib/preferences").cmdLocale(args, config.resolveProjectRoot() || process.cwd());
|
|
55
105
|
break;
|
|
56
106
|
|
|
57
|
-
case "
|
|
58
|
-
require("../lib/
|
|
107
|
+
case "doctor":
|
|
108
|
+
require("../lib/preferences").cmdDoctor(args, config.resolveProjectRoot() || process.cwd());
|
|
59
109
|
break;
|
|
60
110
|
|
|
61
111
|
case "opera": {
|
|
62
112
|
const opera = require("../lib/opera");
|
|
63
113
|
const sub = args[0];
|
|
64
114
|
const root = config.resolveProjectRoot() || process.cwd();
|
|
65
|
-
if (sub === "install") opera.cmdInstall(root, args.slice(1));
|
|
115
|
+
if (sub === "install") await opera.cmdInstall(root, args.slice(1));
|
|
116
|
+
else if (sub === "bootstrap") await opera.cmdBootstrap(root, args.slice(1));
|
|
117
|
+
else if (sub === "handoff") opera.cmdHandoff(root, args.slice(1));
|
|
66
118
|
else if (sub === "status") opera.cmdStatus(root);
|
|
67
119
|
else if (sub === "configure") opera.cmdConfigure(root, args.slice(1));
|
|
68
|
-
else if (sub === "upgrade") opera.cmdUpgrade(root);
|
|
69
|
-
else { console.log("
|
|
120
|
+
else if (sub === "upgrade") opera.cmdUpgrade(root, args.slice(1));
|
|
121
|
+
else { console.log(t("cli.usage.opera")); }
|
|
70
122
|
break;
|
|
71
123
|
}
|
|
72
|
-
|
|
124
|
+
|
|
73
125
|
case "skill": {
|
|
74
126
|
const skills = require("../lib/skills");
|
|
75
127
|
const sub = args[0];
|
|
@@ -78,10 +130,16 @@ function run() {
|
|
|
78
130
|
else if (sub === "list") skills.cmdList(root);
|
|
79
131
|
else if (sub === "remove") skills.cmdRemove(root, args[1]);
|
|
80
132
|
else if (sub === "catalog") skills.cmdCatalog();
|
|
81
|
-
else { console.log("
|
|
133
|
+
else { console.log(t("cli.usage.skill")); }
|
|
82
134
|
break;
|
|
83
135
|
}
|
|
84
136
|
|
|
137
|
+
case "version":
|
|
138
|
+
case "--version":
|
|
139
|
+
case "-v":
|
|
140
|
+
console.log(pkg.version);
|
|
141
|
+
break;
|
|
142
|
+
|
|
85
143
|
case "help":
|
|
86
144
|
case "--help":
|
|
87
145
|
case "-h":
|
|
@@ -90,14 +148,14 @@ function run() {
|
|
|
90
148
|
break;
|
|
91
149
|
|
|
92
150
|
default:
|
|
93
|
-
console.error(
|
|
94
|
-
console.error("
|
|
151
|
+
console.error(t("cli.error.unknownCommand", { command }));
|
|
152
|
+
console.error(t("cli.error.runHelp"));
|
|
95
153
|
process.exit(1);
|
|
96
154
|
}
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.error(error.message);
|
|
99
|
-
process.exit(1);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
run();
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error(error.message);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
run();
|
package/lib/config.js
CHANGED
|
@@ -2,57 +2,263 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
|
+
const { normalizeLocale } = require("./locale");
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
const DEFAULT_PHASE_IDS = ["O", "P", "E", "R", "A"];
|
|
8
|
+
const DEFAULT_PHASE_LABELS = {
|
|
9
|
+
es: {
|
|
10
|
+
O: "Orquestar",
|
|
11
|
+
P: "Probar",
|
|
12
|
+
E: "Estructurar",
|
|
13
|
+
R: "Refinar",
|
|
14
|
+
A: "Automatizar",
|
|
15
|
+
},
|
|
16
|
+
en: {
|
|
17
|
+
O: "Orchestrate",
|
|
18
|
+
P: "Prove",
|
|
19
|
+
E: "Establish",
|
|
20
|
+
R: "Refine",
|
|
21
|
+
A: "Automate",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
13
24
|
|
|
14
25
|
const DEFAULT_LOCALE = "es";
|
|
26
|
+
const WORKSPACE_MANIFEST = ".trackops-workspace.json";
|
|
27
|
+
const DEFAULT_APP_DIR = "app";
|
|
28
|
+
const DEFAULT_OPS_DIR = "ops";
|
|
29
|
+
const DEFAULT_DEV_BRANCH = "develop";
|
|
30
|
+
const DEFAULT_PUBLISH_BRANCH = "master";
|
|
15
31
|
|
|
16
|
-
function
|
|
32
|
+
function buildDefaultPhases(locale) {
|
|
33
|
+
const normalized = normalizeLocale(locale) || DEFAULT_LOCALE;
|
|
34
|
+
const labels = DEFAULT_PHASE_LABELS[normalized] || DEFAULT_PHASE_LABELS[DEFAULT_LOCALE];
|
|
35
|
+
return DEFAULT_PHASE_IDS.map((id, index) => ({
|
|
36
|
+
id,
|
|
37
|
+
label: labels[id],
|
|
38
|
+
index: index + 1,
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const DEFAULT_PHASES = buildDefaultPhases(DEFAULT_LOCALE);
|
|
43
|
+
|
|
44
|
+
function fileExists(filePath) {
|
|
45
|
+
try {
|
|
46
|
+
return fs.existsSync(filePath);
|
|
47
|
+
} catch (_error) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function readJson(filePath, fallback = null) {
|
|
53
|
+
if (!fileExists(filePath)) return fallback;
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
56
|
+
} catch (_error) {
|
|
57
|
+
return fallback;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function createSplitContext(workspaceRoot, manifest = {}) {
|
|
62
|
+
const appDir = manifest.appDir || DEFAULT_APP_DIR;
|
|
63
|
+
const opsDir = manifest.opsDir || DEFAULT_OPS_DIR;
|
|
64
|
+
const workspace = path.resolve(workspaceRoot);
|
|
65
|
+
const appRoot = path.join(workspace, appDir);
|
|
66
|
+
const opsRoot = path.join(workspace, opsDir);
|
|
67
|
+
const env = manifest.env || {};
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
layout: "split",
|
|
71
|
+
workspaceRoot: workspace,
|
|
72
|
+
root: workspace,
|
|
73
|
+
projectRoot: workspace,
|
|
74
|
+
appRoot,
|
|
75
|
+
opsRoot,
|
|
76
|
+
manifestFile: path.join(workspace, WORKSPACE_MANIFEST),
|
|
77
|
+
packageFile: path.join(appRoot, "package.json"),
|
|
78
|
+
controlFile: path.join(opsRoot, "project_control.json"),
|
|
79
|
+
runtimeFile: path.join(opsRoot, ".tmp", "project-control-runtime.json"),
|
|
80
|
+
docs: {
|
|
81
|
+
taskPlan: path.join(opsRoot, "task_plan.md"),
|
|
82
|
+
progress: path.join(opsRoot, "progress.md"),
|
|
83
|
+
findings: path.join(opsRoot, "findings.md"),
|
|
84
|
+
},
|
|
85
|
+
paths: {
|
|
86
|
+
taskPlan: path.join(opsRoot, "task_plan.md"),
|
|
87
|
+
progress: path.join(opsRoot, "progress.md"),
|
|
88
|
+
findings: path.join(opsRoot, "findings.md"),
|
|
89
|
+
architectureDir: path.join(opsRoot, "architecture"),
|
|
90
|
+
hooksDir: path.join(opsRoot, ".githooks"),
|
|
91
|
+
tmpDir: path.join(opsRoot, ".tmp"),
|
|
92
|
+
bootstrapDir: path.join(opsRoot, "bootstrap"),
|
|
93
|
+
contractDir: path.join(opsRoot, "contract"),
|
|
94
|
+
contractFile: path.join(opsRoot, "contract", "operating-contract.json"),
|
|
95
|
+
policyDir: path.join(opsRoot, "policy"),
|
|
96
|
+
autonomyPolicyFile: path.join(opsRoot, "policy", "autonomy.json"),
|
|
97
|
+
reviewsDir: path.join(opsRoot, "reviews"),
|
|
98
|
+
skillsDir: path.join(opsRoot, ".agents", "skills"),
|
|
99
|
+
registryPath: path.join(opsRoot, ".agents", "skills", "_registry.md"),
|
|
100
|
+
agentHubDir: path.join(opsRoot, ".agent", "hub"),
|
|
101
|
+
genesisFile: path.join(opsRoot, "genesis.md"),
|
|
102
|
+
},
|
|
103
|
+
env: {
|
|
104
|
+
rootFile: path.join(workspace, env.rootFile || ".env"),
|
|
105
|
+
exampleFile: path.join(workspace, env.exampleFile || ".env.example"),
|
|
106
|
+
appBridgeFile: path.join(workspace, env.appBridgeFile || path.join(appDir, ".env")),
|
|
107
|
+
appExampleBridgeFile: path.join(appRoot, ".env.example"),
|
|
108
|
+
bridgeMode: env.bridgeMode || "symlink-or-copy",
|
|
109
|
+
},
|
|
110
|
+
branches: {
|
|
111
|
+
development: manifest.branches?.development || DEFAULT_DEV_BRANCH,
|
|
112
|
+
publish: manifest.branches?.publish || DEFAULT_PUBLISH_BRANCH,
|
|
113
|
+
},
|
|
114
|
+
publish: {
|
|
115
|
+
mode: manifest.publish?.mode || "subtree-flatten",
|
|
116
|
+
sourceDir: manifest.publish?.sourceDir || appDir,
|
|
117
|
+
includeRootFiles: Array.isArray(manifest.publish?.includeRootFiles)
|
|
118
|
+
? manifest.publish.includeRootFiles
|
|
119
|
+
: [".env.example"],
|
|
120
|
+
requireCleanWorktree: manifest.publish?.requireCleanWorktree !== false,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function createLegacyContext(rootDir) {
|
|
126
|
+
const root = path.resolve(rootDir);
|
|
127
|
+
return {
|
|
128
|
+
layout: "legacy",
|
|
129
|
+
workspaceRoot: root,
|
|
130
|
+
root,
|
|
131
|
+
projectRoot: root,
|
|
132
|
+
appRoot: root,
|
|
133
|
+
opsRoot: root,
|
|
134
|
+
manifestFile: null,
|
|
135
|
+
packageFile: path.join(root, "package.json"),
|
|
136
|
+
controlFile: path.join(root, "project_control.json"),
|
|
137
|
+
runtimeFile: path.join(root, ".tmp", "project-control-runtime.json"),
|
|
138
|
+
docs: {
|
|
139
|
+
taskPlan: path.join(root, "task_plan.md"),
|
|
140
|
+
progress: path.join(root, "progress.md"),
|
|
141
|
+
findings: path.join(root, "findings.md"),
|
|
142
|
+
},
|
|
143
|
+
paths: {
|
|
144
|
+
taskPlan: path.join(root, "task_plan.md"),
|
|
145
|
+
progress: path.join(root, "progress.md"),
|
|
146
|
+
findings: path.join(root, "findings.md"),
|
|
147
|
+
architectureDir: path.join(root, "architecture"),
|
|
148
|
+
hooksDir: path.join(root, ".githooks"),
|
|
149
|
+
tmpDir: path.join(root, ".tmp"),
|
|
150
|
+
bootstrapDir: path.join(root, "bootstrap"),
|
|
151
|
+
contractDir: path.join(root, "contract"),
|
|
152
|
+
contractFile: path.join(root, "contract", "operating-contract.json"),
|
|
153
|
+
policyDir: path.join(root, "policy"),
|
|
154
|
+
autonomyPolicyFile: path.join(root, "policy", "autonomy.json"),
|
|
155
|
+
reviewsDir: path.join(root, "reviews"),
|
|
156
|
+
skillsDir: path.join(root, ".agents", "skills"),
|
|
157
|
+
registryPath: path.join(root, ".agents", "skills", "_registry.md"),
|
|
158
|
+
agentHubDir: path.join(root, ".agent", "hub"),
|
|
159
|
+
genesisFile: path.join(root, "genesis.md"),
|
|
160
|
+
},
|
|
161
|
+
env: {
|
|
162
|
+
rootFile: path.join(root, ".env"),
|
|
163
|
+
exampleFile: path.join(root, ".env.example"),
|
|
164
|
+
appBridgeFile: path.join(root, ".env"),
|
|
165
|
+
appExampleBridgeFile: path.join(root, ".env.example"),
|
|
166
|
+
bridgeMode: "none",
|
|
167
|
+
},
|
|
168
|
+
branches: {
|
|
169
|
+
development: DEFAULT_DEV_BRANCH,
|
|
170
|
+
publish: DEFAULT_PUBLISH_BRANCH,
|
|
171
|
+
},
|
|
172
|
+
publish: {
|
|
173
|
+
mode: "legacy",
|
|
174
|
+
sourceDir: ".",
|
|
175
|
+
includeRootFiles: [".env.example"],
|
|
176
|
+
requireCleanWorktree: true,
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function resolveWorkspaceContext(startDir) {
|
|
17
182
|
let dir = path.resolve(startDir || process.cwd());
|
|
18
183
|
const root = path.parse(dir).root;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
184
|
+
let legacyCandidate = null;
|
|
185
|
+
|
|
186
|
+
while (true) {
|
|
187
|
+
const manifestFile = path.join(dir, WORKSPACE_MANIFEST);
|
|
188
|
+
if (fileExists(manifestFile)) {
|
|
189
|
+
return createSplitContext(dir, readJson(manifestFile, {}) || {});
|
|
22
190
|
}
|
|
191
|
+
|
|
192
|
+
if (!legacyCandidate && fileExists(path.join(dir, "project_control.json"))) {
|
|
193
|
+
legacyCandidate = dir;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (dir === root) break;
|
|
23
197
|
dir = path.dirname(dir);
|
|
24
198
|
}
|
|
199
|
+
|
|
200
|
+
if (legacyCandidate) {
|
|
201
|
+
return createLegacyContext(legacyCandidate);
|
|
202
|
+
}
|
|
203
|
+
|
|
25
204
|
return null;
|
|
26
205
|
}
|
|
27
206
|
|
|
28
|
-
function
|
|
29
|
-
return
|
|
207
|
+
function ensureContext(contextOrRoot) {
|
|
208
|
+
if (!contextOrRoot) return resolveWorkspaceContext(process.cwd());
|
|
209
|
+
if (typeof contextOrRoot === "object" && contextOrRoot.workspaceRoot) return contextOrRoot;
|
|
210
|
+
const resolved = resolveWorkspaceContext(contextOrRoot);
|
|
211
|
+
if (resolved) return resolved;
|
|
212
|
+
return createLegacyContext(contextOrRoot);
|
|
30
213
|
}
|
|
31
214
|
|
|
32
|
-
function
|
|
33
|
-
|
|
215
|
+
function resolveProjectRoot(startDir) {
|
|
216
|
+
const context = resolveWorkspaceContext(startDir);
|
|
217
|
+
return context ? context.workspaceRoot : null;
|
|
34
218
|
}
|
|
35
219
|
|
|
36
|
-
function
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
220
|
+
function controlFilePath(contextOrRoot) {
|
|
221
|
+
return ensureContext(contextOrRoot).controlFile;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function runtimeFilePath(contextOrRoot) {
|
|
225
|
+
return ensureContext(contextOrRoot).runtimeFile;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function docFilePaths(contextOrRoot) {
|
|
229
|
+
return ensureContext(contextOrRoot).docs;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function envFilePaths(contextOrRoot) {
|
|
233
|
+
return ensureContext(contextOrRoot).env;
|
|
42
234
|
}
|
|
43
235
|
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
236
|
+
function packageFilePath(contextOrRoot) {
|
|
237
|
+
return ensureContext(contextOrRoot).packageFile;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function workspaceManifestPath(contextOrRoot) {
|
|
241
|
+
return ensureContext(contextOrRoot).manifestFile;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function isDefaultPhaseShape(phases) {
|
|
245
|
+
if (!Array.isArray(phases) || phases.length !== DEFAULT_PHASE_IDS.length) return false;
|
|
246
|
+
return phases.every((phase, index) => phase?.id === DEFAULT_PHASE_IDS[index]);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function getPhases(control, localeOverride) {
|
|
250
|
+
const locale = normalizeLocale(localeOverride || control.meta?.locale) || DEFAULT_LOCALE;
|
|
251
|
+
if (Array.isArray(control.meta?.phases) && control.meta.phases.length > 0) {
|
|
252
|
+
if (isDefaultPhaseShape(control.meta.phases)) {
|
|
253
|
+
return buildDefaultPhases(locale);
|
|
254
|
+
}
|
|
49
255
|
return control.meta.phases;
|
|
50
256
|
}
|
|
51
|
-
return
|
|
257
|
+
return buildDefaultPhases(locale);
|
|
52
258
|
}
|
|
53
259
|
|
|
54
260
|
function getLocale(control) {
|
|
55
|
-
return control.meta?.locale || DEFAULT_LOCALE;
|
|
261
|
+
return normalizeLocale(control.meta?.locale) || DEFAULT_LOCALE;
|
|
56
262
|
}
|
|
57
263
|
|
|
58
264
|
function isOperaInstalled(control) {
|
|
@@ -63,35 +269,58 @@ function getOperaVersion(control) {
|
|
|
63
269
|
return control.meta?.opera?.version || null;
|
|
64
270
|
}
|
|
65
271
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
function getEtapaVersion(control) { return getOperaVersion(control); }
|
|
69
|
-
|
|
70
|
-
function loadControl(root) {
|
|
71
|
-
const filePath = controlFilePath(root);
|
|
272
|
+
function loadControl(contextOrRoot) {
|
|
273
|
+
const filePath = controlFilePath(contextOrRoot);
|
|
72
274
|
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
73
275
|
}
|
|
74
276
|
|
|
75
|
-
function saveControl(
|
|
277
|
+
function saveControl(contextOrRoot, control) {
|
|
76
278
|
control.meta = control.meta || {};
|
|
77
279
|
control.meta.updatedAt = new Date().toISOString();
|
|
78
|
-
const filePath = controlFilePath(
|
|
280
|
+
const filePath = controlFilePath(contextOrRoot);
|
|
281
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
79
282
|
fs.writeFileSync(filePath, JSON.stringify(control, null, 2) + "\n", "utf8");
|
|
80
283
|
}
|
|
81
284
|
|
|
285
|
+
function loadWorkspaceManifest(contextOrRoot) {
|
|
286
|
+
const context = ensureContext(contextOrRoot);
|
|
287
|
+
if (!context.manifestFile || !fileExists(context.manifestFile)) return null;
|
|
288
|
+
return readJson(context.manifestFile, null);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function saveWorkspaceManifest(contextOrRoot, manifest) {
|
|
292
|
+
const context = ensureContext(contextOrRoot);
|
|
293
|
+
const manifestFile = context.manifestFile || path.join(context.workspaceRoot, WORKSPACE_MANIFEST);
|
|
294
|
+
fs.writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
295
|
+
}
|
|
296
|
+
|
|
82
297
|
module.exports = {
|
|
83
298
|
DEFAULT_PHASES,
|
|
299
|
+
DEFAULT_PHASE_IDS,
|
|
84
300
|
DEFAULT_LOCALE,
|
|
301
|
+
DEFAULT_APP_DIR,
|
|
302
|
+
DEFAULT_OPS_DIR,
|
|
303
|
+
DEFAULT_DEV_BRANCH,
|
|
304
|
+
DEFAULT_PUBLISH_BRANCH,
|
|
305
|
+
WORKSPACE_MANIFEST,
|
|
306
|
+
buildDefaultPhases,
|
|
307
|
+
createSplitContext,
|
|
308
|
+
createLegacyContext,
|
|
309
|
+
resolveWorkspaceContext,
|
|
85
310
|
resolveProjectRoot,
|
|
311
|
+
ensureContext,
|
|
86
312
|
controlFilePath,
|
|
87
313
|
runtimeFilePath,
|
|
88
314
|
docFilePaths,
|
|
315
|
+
envFilePaths,
|
|
316
|
+
packageFilePath,
|
|
317
|
+
workspaceManifestPath,
|
|
318
|
+
loadWorkspaceManifest,
|
|
319
|
+
saveWorkspaceManifest,
|
|
89
320
|
getPhases,
|
|
90
321
|
getLocale,
|
|
91
322
|
isOperaInstalled,
|
|
92
323
|
getOperaVersion,
|
|
93
|
-
isEtapaInstalled,
|
|
94
|
-
getEtapaVersion,
|
|
95
324
|
loadControl,
|
|
96
325
|
saveControl,
|
|
97
326
|
};
|