odoo-forge 0.1.0 → 0.1.1
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/package.json +2 -2
- package/src/claude.js +36 -78
- package/src/codex.js +20 -28
- package/src/index.js +58 -128
- package/src/paths.js +7 -15
- package/src/config.js +0 -70
- package/src/runtime.js +0 -27
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "odoo-forge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "CLI installer and updater for Odoo Forge internal 1.0.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"odoo-forge-bundle": "0.1.
|
|
7
|
+
"odoo-forge-bundle": "0.1.1"
|
|
8
8
|
},
|
|
9
9
|
"bin": {
|
|
10
10
|
"odoo-forge": "bin/odoo-forge.js"
|
package/src/claude.js
CHANGED
|
@@ -1,92 +1,50 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
const PLUGIN_NAME = "odoo-forge";
|
|
3
|
+
import { getClaudeConfigPath } from "./paths.js";
|
|
6
4
|
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
fs.
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function renderClaudeMarketplace({
|
|
13
|
-
bundleRoot,
|
|
14
|
-
destinationRoot,
|
|
15
|
-
version,
|
|
16
|
-
}) {
|
|
17
|
-
const marketplaceDir = path.join(destinationRoot, "claude-marketplace");
|
|
18
|
-
const pluginDir = path.join(marketplaceDir, "plugins", PLUGIN_NAME);
|
|
19
|
-
const skillsSource = path.join(bundleRoot, "skills");
|
|
5
|
+
export function readClaudeConfig({ homeDir }) {
|
|
6
|
+
const configPath = getClaudeConfigPath({ homeDir });
|
|
7
|
+
if (!fs.existsSync(configPath)) {
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
20
10
|
|
|
21
|
-
fs.
|
|
22
|
-
|
|
11
|
+
return JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
12
|
+
}
|
|
23
13
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
metadata: {
|
|
30
|
-
description: "Internal marketplace for Odoo Forge.",
|
|
31
|
-
version,
|
|
32
|
-
pluginRoot: "./plugins",
|
|
33
|
-
},
|
|
34
|
-
plugins: [
|
|
35
|
-
{
|
|
36
|
-
name: PLUGIN_NAME,
|
|
37
|
-
description: "Internal Odoo Forge skill suite.",
|
|
38
|
-
source: "./plugins/odoo-forge",
|
|
39
|
-
category: "development",
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
});
|
|
14
|
+
export function writeClaudeConfig({ homeDir, config }) {
|
|
15
|
+
const configPath = getClaudeConfigPath({ homeDir });
|
|
16
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
17
|
+
return configPath;
|
|
18
|
+
}
|
|
43
19
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
20
|
+
export function buildClaudeFlowusServer({ token }) {
|
|
21
|
+
return {
|
|
22
|
+
type: "stdio",
|
|
23
|
+
command: "npx",
|
|
24
|
+
args: ["-y", "flowus-mcp-server@latest"],
|
|
25
|
+
env: {
|
|
26
|
+
FLOWUS_TOKEN: token,
|
|
50
27
|
},
|
|
51
|
-
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
52
30
|
|
|
53
|
-
|
|
31
|
+
export function installClaudeWiring({ homeDir, token }) {
|
|
32
|
+
const currentConfig = readClaudeConfig({ homeDir });
|
|
33
|
+
const nextConfig = {
|
|
34
|
+
...currentConfig,
|
|
54
35
|
mcpServers: {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
command: "odoo-forge",
|
|
58
|
-
args: ["mcp", "flowus"],
|
|
59
|
-
},
|
|
36
|
+
...(currentConfig.mcpServers ?? {}),
|
|
37
|
+
flowus: buildClaudeFlowusServer({ token }),
|
|
60
38
|
},
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
fs.cpSync(skillsSource, path.join(pluginDir, "skills"), { recursive: true });
|
|
39
|
+
};
|
|
40
|
+
const configPath = writeClaudeConfig({ homeDir, config: nextConfig });
|
|
64
41
|
|
|
65
|
-
return
|
|
42
|
+
return {
|
|
43
|
+
configPath,
|
|
44
|
+
};
|
|
66
45
|
}
|
|
67
46
|
|
|
68
|
-
export function
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{
|
|
72
|
-
command: "claude",
|
|
73
|
-
args: ["plugin", "marketplace", "update", MARKETPLACE_NAME],
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
command: "claude",
|
|
77
|
-
args: ["plugin", "update", `${PLUGIN_NAME}@${MARKETPLACE_NAME}`, "--scope", "user"],
|
|
78
|
-
},
|
|
79
|
-
];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return [
|
|
83
|
-
{
|
|
84
|
-
command: "claude",
|
|
85
|
-
args: ["plugin", "marketplace", "add", marketplaceDir],
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
command: "claude",
|
|
89
|
-
args: ["plugin", "install", `${PLUGIN_NAME}@${MARKETPLACE_NAME}`, "--scope", "user"],
|
|
90
|
-
},
|
|
91
|
-
];
|
|
47
|
+
export function readClaudeManagedToken({ homeDir }) {
|
|
48
|
+
const config = readClaudeConfig({ homeDir });
|
|
49
|
+
return config?.mcpServers?.flowus?.env?.FLOWUS_TOKEN ?? null;
|
|
92
50
|
}
|
package/src/codex.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
-
import { getCodexConfigPath
|
|
4
|
+
import { getCodexConfigPath } from "./paths.js";
|
|
5
5
|
|
|
6
6
|
export const CODEX_START_MARKER = "# BEGIN ODOO FORGE FLOWUS";
|
|
7
7
|
export const CODEX_END_MARKER = "# END ODOO FORGE FLOWUS";
|
|
8
8
|
|
|
9
|
-
export function buildCodexManagedBlock() {
|
|
9
|
+
export function buildCodexManagedBlock({ token }) {
|
|
10
10
|
return `${CODEX_START_MARKER}
|
|
11
11
|
[mcp_servers.flowus]
|
|
12
12
|
type = "stdio"
|
|
13
|
-
command = "
|
|
14
|
-
args = ["
|
|
13
|
+
command = "npx"
|
|
14
|
+
args = ["-y", "flowus-mcp-server@latest"]
|
|
15
|
+
env = { FLOWUS_TOKEN = "${token}" }
|
|
15
16
|
${CODEX_END_MARKER}`;
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -33,33 +34,27 @@ export function upsertManagedBlock({
|
|
|
33
34
|
return trimmed ? `${trimmed}\n\n${block}\n` : `${block}\n`;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
function
|
|
37
|
-
|
|
37
|
+
export function readCodexManagedToken({ homeDir }) {
|
|
38
|
+
const configPath = getCodexConfigPath({ homeDir });
|
|
39
|
+
if (!fs.existsSync(configPath)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
} catch {
|
|
46
|
-
fs.rmSync(linkPath, { force: true, recursive: true });
|
|
47
|
-
}
|
|
43
|
+
const content = fs.readFileSync(configPath, "utf8");
|
|
44
|
+
const startIndex = content.indexOf(CODEX_START_MARKER);
|
|
45
|
+
const endIndex = content.indexOf(CODEX_END_MARKER);
|
|
46
|
+
if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
|
|
47
|
+
return null;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
const managedBlock = content.slice(startIndex, endIndex + CODEX_END_MARKER.length);
|
|
51
|
+
const match = managedBlock.match(/FLOWUS_TOKEN\s*=\s*"([^"]+)"/);
|
|
52
|
+
return match?.[1] ?? null;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
export function installCodexWiring({
|
|
55
|
-
homeDir,
|
|
56
|
-
installRoot,
|
|
57
|
-
platform = process.platform,
|
|
58
|
-
}) {
|
|
55
|
+
export function installCodexWiring({ homeDir, token }) {
|
|
59
56
|
const configPath = getCodexConfigPath({ homeDir });
|
|
60
|
-
const linkPath = getCodexSkillsLinkPath({ homeDir });
|
|
61
57
|
const codexDir = path.dirname(configPath);
|
|
62
|
-
const skillsTarget = path.join(installRoot, "current", "skills");
|
|
63
58
|
|
|
64
59
|
fs.mkdirSync(codexDir, { recursive: true });
|
|
65
60
|
const currentConfig = fs.existsSync(configPath) ? fs.readFileSync(configPath, "utf8") : "";
|
|
@@ -67,14 +62,11 @@ export function installCodexWiring({
|
|
|
67
62
|
originalContent: currentConfig,
|
|
68
63
|
startMarker: CODEX_START_MARKER,
|
|
69
64
|
endMarker: CODEX_END_MARKER,
|
|
70
|
-
block: buildCodexManagedBlock(),
|
|
65
|
+
block: buildCodexManagedBlock({ token }),
|
|
71
66
|
});
|
|
72
67
|
fs.writeFileSync(configPath, nextConfig);
|
|
73
68
|
|
|
74
|
-
ensureDirectoryLink({ linkPath, targetPath: skillsTarget, platform });
|
|
75
|
-
|
|
76
69
|
return {
|
|
77
70
|
configPath,
|
|
78
|
-
linkPath,
|
|
79
71
|
};
|
|
80
72
|
}
|
package/src/index.js
CHANGED
|
@@ -2,20 +2,17 @@ import fs from "node:fs";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import readline from "node:readline/promises";
|
|
5
|
-
import { spawn
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
|
+
import { installClaudeWiring, readClaudeManagedToken } from "./claude.js";
|
|
9
|
+
import { installCodexWiring, readCodexManagedToken } from "./codex.js";
|
|
8
10
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "./config.js";
|
|
15
|
-
import { buildClaudeCommandPlan, renderClaudeMarketplace } from "./claude.js";
|
|
16
|
-
import { installCodexWiring } from "./codex.js";
|
|
17
|
-
import { getCurrentRoot, getInstallRoot, getStatePath } from "./paths.js";
|
|
18
|
-
import { installRuntime } from "./runtime.js";
|
|
11
|
+
getAgentsSkillsRoot,
|
|
12
|
+
getClaudeConfigPath,
|
|
13
|
+
getCodexConfigPath,
|
|
14
|
+
getInstalledSkillsPath,
|
|
15
|
+
} from "./paths.js";
|
|
19
16
|
|
|
20
17
|
function printHelp() {
|
|
21
18
|
console.log(`Odoo Forge CLI
|
|
@@ -56,17 +53,6 @@ async function defaultPromptForSecret(message) {
|
|
|
56
53
|
}
|
|
57
54
|
}
|
|
58
55
|
|
|
59
|
-
async function defaultRunCommand(command, args, options = {}) {
|
|
60
|
-
return await new Promise((resolve) => {
|
|
61
|
-
const child = spawn(command, args, {
|
|
62
|
-
stdio: "inherit",
|
|
63
|
-
...options,
|
|
64
|
-
});
|
|
65
|
-
child.on("close", (code) => resolve({ code: code ?? 0 }));
|
|
66
|
-
child.on("error", () => resolve({ code: 1 }));
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
56
|
async function defaultSpawnProcess(command, args, options = {}) {
|
|
71
57
|
return await new Promise((resolve, reject) => {
|
|
72
58
|
const child = spawn(command, args, {
|
|
@@ -78,22 +64,25 @@ async function defaultSpawnProcess(command, args, options = {}) {
|
|
|
78
64
|
});
|
|
79
65
|
}
|
|
80
66
|
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
}
|
|
67
|
+
function installSkills({ bundleRoot, homeDir }) {
|
|
68
|
+
const sourceSkillsDir = path.join(bundleRoot, "skills");
|
|
69
|
+
const targetSkillsDir = getInstalledSkillsPath({ homeDir });
|
|
85
70
|
|
|
86
|
-
|
|
87
|
-
|
|
71
|
+
fs.mkdirSync(getAgentsSkillsRoot({ homeDir }), { recursive: true });
|
|
72
|
+
fs.rmSync(targetSkillsDir, { recursive: true, force: true });
|
|
73
|
+
fs.cpSync(sourceSkillsDir, targetSkillsDir, { recursive: true });
|
|
74
|
+
|
|
75
|
+
return targetSkillsDir;
|
|
88
76
|
}
|
|
89
77
|
|
|
90
|
-
async function ensureFlowusToken({ ctx
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
|
|
78
|
+
async function ensureFlowusToken({ ctx }) {
|
|
79
|
+
const envToken = ctx.env.ODOO_FORGE_FLOWUS_TOKEN ?? ctx.env.FLOWUS_TOKEN;
|
|
80
|
+
const currentToken =
|
|
81
|
+
envToken ??
|
|
82
|
+
readCodexManagedToken({ homeDir: ctx.homeDir }) ??
|
|
83
|
+
readClaudeManagedToken({ homeDir: ctx.homeDir });
|
|
94
84
|
|
|
95
85
|
if (currentToken) {
|
|
96
|
-
writeFlowusToken({ configPath, token: currentToken });
|
|
97
86
|
return currentToken;
|
|
98
87
|
}
|
|
99
88
|
|
|
@@ -102,103 +91,47 @@ async function ensureFlowusToken({ ctx, configPath }) {
|
|
|
102
91
|
if (!token) {
|
|
103
92
|
throw new Error("FlowUS token is required.");
|
|
104
93
|
}
|
|
105
|
-
writeFlowusToken({ configPath, token });
|
|
106
94
|
return token;
|
|
107
95
|
}
|
|
108
96
|
|
|
109
|
-
async function installClaude({ ctx, currentRoot, version, mode }) {
|
|
110
|
-
if (!(await ctx.hasCommand("claude"))) {
|
|
111
|
-
ctx.output.log("Claude CLI not found. Skipping Claude installation.");
|
|
112
|
-
return { enabled: false };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const marketplaceDir = renderClaudeMarketplace({
|
|
116
|
-
bundleRoot: currentRoot,
|
|
117
|
-
destinationRoot: currentRoot,
|
|
118
|
-
version,
|
|
119
|
-
});
|
|
120
|
-
const commandPlan = buildClaudeCommandPlan({ marketplaceDir, mode });
|
|
121
|
-
|
|
122
|
-
for (const step of commandPlan) {
|
|
123
|
-
const result = await ctx.runCommand(step.command, step.args);
|
|
124
|
-
if (result.code !== 0) {
|
|
125
|
-
throw new Error(
|
|
126
|
-
`Claude command failed: ${step.command} ${step.args.join(" ")}`,
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
enabled: true,
|
|
133
|
-
marketplaceDir,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
97
|
function runDoctor(ctx) {
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
env: ctx.env,
|
|
144
|
-
});
|
|
145
|
-
const config = readUserConfig({ configPath });
|
|
146
|
-
const currentRoot = getCurrentRoot({ homeDir: ctx.homeDir, installRoot });
|
|
98
|
+
const skillsPath = getInstalledSkillsPath({ homeDir: ctx.homeDir });
|
|
99
|
+
const codexConfigPath = getCodexConfigPath({ homeDir: ctx.homeDir });
|
|
100
|
+
const claudeConfigPath = getClaudeConfigPath({ homeDir: ctx.homeDir });
|
|
101
|
+
const codexToken = readCodexManagedToken({ homeDir: ctx.homeDir });
|
|
102
|
+
const claudeToken = readClaudeManagedToken({ homeDir: ctx.homeDir });
|
|
147
103
|
|
|
148
104
|
ctx.output.log("Odoo Forge doctor");
|
|
149
|
-
ctx.output.log(`
|
|
150
|
-
ctx.output.log(`
|
|
151
|
-
ctx.output.log(`
|
|
152
|
-
ctx.output.log(`
|
|
153
|
-
ctx.output.log(`
|
|
154
|
-
ctx.output.log(`
|
|
155
|
-
ctx.output.log(
|
|
156
|
-
`Claude marketplace exists: ${fs.existsSync(path.join(currentRoot, "claude-marketplace")) ? "yes" : "no"}`,
|
|
157
|
-
);
|
|
105
|
+
ctx.output.log(`Skills root: ${skillsPath}`);
|
|
106
|
+
ctx.output.log(`Skills installed: ${fs.existsSync(skillsPath) ? "yes" : "no"}`);
|
|
107
|
+
ctx.output.log(`Codex config exists: ${fs.existsSync(codexConfigPath) ? "yes" : "no"}`);
|
|
108
|
+
ctx.output.log(`Codex FlowUS MCP exists: ${codexToken ? "yes" : "no"}`);
|
|
109
|
+
ctx.output.log(`Claude config exists: ${fs.existsSync(claudeConfigPath) ? "yes" : "no"}`);
|
|
110
|
+
ctx.output.log(`Claude FlowUS MCP exists: ${claudeToken ? "yes" : "no"}`);
|
|
111
|
+
ctx.output.log(`FlowUS token synchronized: ${codexToken && claudeToken && codexToken === claudeToken ? "yes" : "no"}`);
|
|
158
112
|
}
|
|
159
113
|
|
|
160
114
|
async function runInstallLike(ctx, mode) {
|
|
161
|
-
const installRoot = getInstallRoot({ homeDir: ctx.homeDir });
|
|
162
|
-
const configPath = getConfigPath({
|
|
163
|
-
homeDir: ctx.homeDir,
|
|
164
|
-
platform: ctx.platform,
|
|
165
|
-
env: ctx.env,
|
|
166
|
-
});
|
|
167
115
|
const bundleRoot = await resolveBundleRoot(ctx.bundleRoot);
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
await ensureFlowusToken({ ctx, configPath });
|
|
171
|
-
const { currentRoot } = installRuntime({
|
|
116
|
+
const token = await ensureFlowusToken({ ctx });
|
|
117
|
+
const skillsPath = installSkills({
|
|
172
118
|
bundleRoot,
|
|
173
|
-
installRoot,
|
|
174
|
-
version: product.version,
|
|
175
119
|
homeDir: ctx.homeDir,
|
|
176
120
|
});
|
|
177
121
|
|
|
178
|
-
installCodexWiring({
|
|
122
|
+
const codexResult = installCodexWiring({
|
|
179
123
|
homeDir: ctx.homeDir,
|
|
180
|
-
|
|
181
|
-
platform: ctx.platform,
|
|
124
|
+
token,
|
|
182
125
|
});
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
currentRoot,
|
|
187
|
-
version: product.version,
|
|
188
|
-
mode,
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
writeState({
|
|
192
|
-
statePath: getStatePath({ homeDir: ctx.homeDir, installRoot }),
|
|
193
|
-
state: {
|
|
194
|
-
version: product.version,
|
|
195
|
-
installedAt: new Date().toISOString(),
|
|
196
|
-
claudeEnabled: claudeResult.enabled,
|
|
197
|
-
},
|
|
126
|
+
const claudeResult = installClaudeWiring({
|
|
127
|
+
homeDir: ctx.homeDir,
|
|
128
|
+
token,
|
|
198
129
|
});
|
|
199
130
|
|
|
200
131
|
ctx.output.log(`${mode} complete.`);
|
|
201
|
-
ctx.output.log(`
|
|
132
|
+
ctx.output.log(`Skills root: ${skillsPath}`);
|
|
133
|
+
ctx.output.log(`Codex config: ${codexResult.configPath}`);
|
|
134
|
+
ctx.output.log(`Claude config: ${claudeResult.configPath}`);
|
|
202
135
|
}
|
|
203
136
|
|
|
204
137
|
async function runLogin(ctx, provider) {
|
|
@@ -206,21 +139,26 @@ async function runLogin(ctx, provider) {
|
|
|
206
139
|
throw new Error(`Unsupported provider: ${provider}`);
|
|
207
140
|
}
|
|
208
141
|
|
|
209
|
-
const configPath = getConfigPath({
|
|
210
|
-
homeDir: ctx.homeDir,
|
|
211
|
-
platform: ctx.platform,
|
|
212
|
-
env: ctx.env,
|
|
213
|
-
});
|
|
214
142
|
const token =
|
|
215
143
|
ctx.env.ODOO_FORGE_FLOWUS_TOKEN ??
|
|
144
|
+
ctx.env.FLOWUS_TOKEN ??
|
|
216
145
|
(await ctx.promptForSecret("Paste your FlowUS token: ")).trim();
|
|
217
146
|
|
|
218
147
|
if (!token) {
|
|
219
148
|
throw new Error("FlowUS token is required.");
|
|
220
149
|
}
|
|
221
150
|
|
|
222
|
-
|
|
223
|
-
|
|
151
|
+
const codexResult = installCodexWiring({
|
|
152
|
+
homeDir: ctx.homeDir,
|
|
153
|
+
token,
|
|
154
|
+
});
|
|
155
|
+
const claudeResult = installClaudeWiring({
|
|
156
|
+
homeDir: ctx.homeDir,
|
|
157
|
+
token,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
ctx.output.log(`Saved FlowUS token to ${codexResult.configPath}`);
|
|
161
|
+
ctx.output.log(`Saved FlowUS token to ${claudeResult.configPath}`);
|
|
224
162
|
}
|
|
225
163
|
|
|
226
164
|
async function runMcp(ctx, provider) {
|
|
@@ -228,16 +166,10 @@ async function runMcp(ctx, provider) {
|
|
|
228
166
|
throw new Error(`Unsupported MCP provider: ${provider}`);
|
|
229
167
|
}
|
|
230
168
|
|
|
231
|
-
const
|
|
232
|
-
homeDir: ctx.homeDir,
|
|
233
|
-
platform: ctx.platform,
|
|
234
|
-
env: ctx.env,
|
|
235
|
-
});
|
|
236
|
-
const config = readUserConfig({ configPath });
|
|
237
|
-
const token = getFlowusToken({ config });
|
|
169
|
+
const token = ctx.env.FLOWUS_TOKEN ?? ctx.env.ODOO_FORGE_FLOWUS_TOKEN;
|
|
238
170
|
|
|
239
171
|
if (!token) {
|
|
240
|
-
throw new Error("Missing FlowUS token.
|
|
172
|
+
throw new Error("Missing FlowUS token. Set FLOWUS_TOKEN before running `odoo-forge mcp flowus`.");
|
|
241
173
|
}
|
|
242
174
|
|
|
243
175
|
return await ctx.spawnProcess("npx", ["-y", "flowus-mcp-server@latest"], {
|
|
@@ -256,9 +188,7 @@ function normalizeContext(overrides = {}) {
|
|
|
256
188
|
bundleRoot: overrides.bundleRoot,
|
|
257
189
|
output: overrides.output ?? console,
|
|
258
190
|
promptForSecret: overrides.promptForSecret ?? defaultPromptForSecret,
|
|
259
|
-
runCommand: overrides.runCommand ?? defaultRunCommand,
|
|
260
191
|
spawnProcess: overrides.spawnProcess ?? defaultSpawnProcess,
|
|
261
|
-
hasCommand: overrides.hasCommand ?? defaultHasCommand,
|
|
262
192
|
};
|
|
263
193
|
}
|
|
264
194
|
|
package/src/paths.js
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
import os from "node:os";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
-
export function
|
|
5
|
-
return path.join(homeDir, ".
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function getCurrentRoot({ homeDir = os.homedir(), installRoot } = {}) {
|
|
9
|
-
return path.join(installRoot ?? getInstallRoot({ homeDir }), "current");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function getVersionsRoot({ homeDir = os.homedir(), installRoot } = {}) {
|
|
13
|
-
return path.join(installRoot ?? getInstallRoot({ homeDir }), "versions");
|
|
4
|
+
export function getCodexConfigPath({ homeDir = os.homedir() } = {}) {
|
|
5
|
+
return path.join(homeDir, ".codex", "config.toml");
|
|
14
6
|
}
|
|
15
7
|
|
|
16
|
-
export function
|
|
17
|
-
return path.join(
|
|
8
|
+
export function getClaudeConfigPath({ homeDir = os.homedir() } = {}) {
|
|
9
|
+
return path.join(homeDir, ".claude.json");
|
|
18
10
|
}
|
|
19
11
|
|
|
20
|
-
export function
|
|
21
|
-
return path.join(homeDir, ".
|
|
12
|
+
export function getAgentsSkillsRoot({ homeDir = os.homedir() } = {}) {
|
|
13
|
+
return path.join(homeDir, ".agents", "skills");
|
|
22
14
|
}
|
|
23
15
|
|
|
24
|
-
export function
|
|
16
|
+
export function getInstalledSkillsPath({ homeDir = os.homedir() } = {}) {
|
|
25
17
|
return path.join(homeDir, ".agents", "skills", "odoo-forge");
|
|
26
18
|
}
|
package/src/config.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
function readJsonFile(filePath) {
|
|
6
|
-
if (!fs.existsSync(filePath)) {
|
|
7
|
-
return {};
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function writeJsonFile(filePath, value) {
|
|
14
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
15
|
-
fs.writeFileSync(filePath, JSON.stringify(value, null, 2));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function getConfigPath({
|
|
19
|
-
homeDir = os.homedir(),
|
|
20
|
-
platform = process.platform,
|
|
21
|
-
env = process.env,
|
|
22
|
-
} = {}) {
|
|
23
|
-
if (platform === "darwin") {
|
|
24
|
-
return path.join(homeDir, "Library", "Application Support", "Odoo Forge", "config.json");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (platform === "win32") {
|
|
28
|
-
const appData = env.APPDATA ?? path.join(homeDir, "AppData", "Roaming");
|
|
29
|
-
return path.win32.join(appData, "Odoo Forge", "config.json");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return path.join(homeDir, ".config", "Odoo Forge", "config.json");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function readUserConfig({ configPath }) {
|
|
36
|
-
return readJsonFile(configPath);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function writeUserConfig({ configPath, config }) {
|
|
40
|
-
writeJsonFile(configPath, config);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function writeFlowusToken({ configPath, token }) {
|
|
44
|
-
const current = readUserConfig({ configPath });
|
|
45
|
-
const next = {
|
|
46
|
-
...current,
|
|
47
|
-
mcp: {
|
|
48
|
-
...(current.mcp ?? {}),
|
|
49
|
-
flowus: {
|
|
50
|
-
...((current.mcp ?? {}).flowus ?? {}),
|
|
51
|
-
FLOWUS_TOKEN: token,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
writeUserConfig({ configPath, config: next });
|
|
57
|
-
return next;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function getFlowusToken({ config }) {
|
|
61
|
-
return config?.mcp?.flowus?.FLOWUS_TOKEN;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function readState({ statePath }) {
|
|
65
|
-
return readJsonFile(statePath);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function writeState({ statePath, state }) {
|
|
69
|
-
writeJsonFile(statePath, state);
|
|
70
|
-
}
|
package/src/runtime.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
import { getCurrentRoot, getVersionsRoot } from "./paths.js";
|
|
5
|
-
|
|
6
|
-
export function installRuntime({
|
|
7
|
-
bundleRoot,
|
|
8
|
-
installRoot,
|
|
9
|
-
version,
|
|
10
|
-
homeDir,
|
|
11
|
-
}) {
|
|
12
|
-
const versionsRoot = getVersionsRoot({ homeDir, installRoot });
|
|
13
|
-
const currentRoot = getCurrentRoot({ homeDir, installRoot });
|
|
14
|
-
const versionRoot = path.join(versionsRoot, version);
|
|
15
|
-
|
|
16
|
-
fs.mkdirSync(versionsRoot, { recursive: true });
|
|
17
|
-
fs.rmSync(versionRoot, { force: true, recursive: true });
|
|
18
|
-
fs.cpSync(bundleRoot, versionRoot, { recursive: true });
|
|
19
|
-
|
|
20
|
-
fs.rmSync(currentRoot, { force: true, recursive: true });
|
|
21
|
-
fs.cpSync(versionRoot, currentRoot, { recursive: true });
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
currentRoot,
|
|
25
|
-
versionRoot,
|
|
26
|
-
};
|
|
27
|
-
}
|