odoo-forge 0.1.0 → 0.1.2

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 CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "odoo-forge",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
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.0"
7
+ "odoo-forge-bundle": "0.1.2"
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
- const MARKETPLACE_NAME = "odoo-forge-marketplace";
5
- const PLUGIN_NAME = "odoo-forge";
3
+ import { getClaudeConfigPath } from "./paths.js";
6
4
 
7
- function writeJson(filePath, value) {
8
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
9
- fs.writeFileSync(filePath, JSON.stringify(value, null, 2));
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.rmSync(marketplaceDir, { force: true, recursive: true });
22
- fs.mkdirSync(pluginDir, { recursive: true });
11
+ return JSON.parse(fs.readFileSync(configPath, "utf8"));
12
+ }
23
13
 
24
- writeJson(path.join(marketplaceDir, ".claude-plugin", "marketplace.json"), {
25
- name: MARKETPLACE_NAME,
26
- owner: {
27
- name: "Odoo Forge Team",
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
- writeJson(path.join(pluginDir, ".claude-plugin", "plugin.json"), {
45
- name: PLUGIN_NAME,
46
- description: "Internal Odoo Forge plugin for Claude Code.",
47
- version,
48
- author: {
49
- name: "Internal Team",
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
- writeJson(path.join(pluginDir, ".mcp.json"), {
31
+ export function installClaudeWiring({ homeDir, token }) {
32
+ const currentConfig = readClaudeConfig({ homeDir });
33
+ const nextConfig = {
34
+ ...currentConfig,
54
35
  mcpServers: {
55
- flowus: {
56
- type: "stdio",
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 marketplaceDir;
42
+ return {
43
+ configPath,
44
+ };
66
45
  }
67
46
 
68
- export function buildClaudeCommandPlan({ marketplaceDir, mode }) {
69
- if (mode === "update") {
70
- return [
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, getCodexSkillsLinkPath } from "./paths.js";
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 = "odoo-forge"
14
- args = ["mcp", "flowus"]
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 ensureDirectoryLink({ linkPath, targetPath, platform = process.platform }) {
37
- fs.mkdirSync(path.dirname(linkPath), { recursive: true });
37
+ export function readCodexManagedToken({ homeDir }) {
38
+ const configPath = getCodexConfigPath({ homeDir });
39
+ if (!fs.existsSync(configPath)) {
40
+ return null;
41
+ }
38
42
 
39
- if (fs.existsSync(linkPath) || fs.lstatSync(path.dirname(linkPath)).isDirectory()) {
40
- try {
41
- const existingTarget = fs.readlinkSync(linkPath);
42
- if (path.resolve(path.dirname(linkPath), existingTarget) === targetPath) {
43
- return;
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
- fs.rmSync(linkPath, { force: true, recursive: true });
51
- fs.symlinkSync(targetPath, linkPath, platform === "win32" ? "junction" : "dir");
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,18 @@ 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, spawnSync } from "node:child_process";
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
- getConfigPath,
10
- getFlowusToken,
11
- readUserConfig,
12
- writeFlowusToken,
13
- writeState,
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
+ getLegacyInstalledSkillsPath,
16
+ } from "./paths.js";
19
17
 
20
18
  function printHelp() {
21
19
  console.log(`Odoo Forge CLI
@@ -56,17 +54,6 @@ async function defaultPromptForSecret(message) {
56
54
  }
57
55
  }
58
56
 
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
57
  async function defaultSpawnProcess(command, args, options = {}) {
71
58
  return await new Promise((resolve, reject) => {
72
59
  const child = spawn(command, args, {
@@ -78,22 +65,33 @@ async function defaultSpawnProcess(command, args, options = {}) {
78
65
  });
79
66
  }
80
67
 
81
- async function defaultHasCommand(command) {
82
- const result = spawnSync(command, ["--version"], { stdio: "ignore" });
83
- return result.status === 0 || result.status === 1;
84
- }
68
+ function installSkills({ bundleRoot, homeDir }) {
69
+ const sourceSkillsDir = path.join(bundleRoot, "skills");
70
+ const targetSkillsDir = getInstalledSkillsPath({ homeDir });
71
+ const legacySkillsDir = getLegacyInstalledSkillsPath({ homeDir });
72
+
73
+ fs.mkdirSync(getAgentsSkillsRoot({ homeDir }), { recursive: true });
74
+ fs.rmSync(legacySkillsDir, { recursive: true, force: true });
75
+
76
+ for (const entry of fs.readdirSync(sourceSkillsDir, { withFileTypes: true })) {
77
+ const sourceEntryPath = path.join(sourceSkillsDir, entry.name);
78
+ const targetEntryPath = path.join(targetSkillsDir, entry.name);
85
79
 
86
- async function readProduct(bundleRoot) {
87
- return JSON.parse(fs.readFileSync(path.join(bundleRoot, "config", "product.json"), "utf8"));
80
+ fs.rmSync(targetEntryPath, { recursive: true, force: true });
81
+ fs.cpSync(sourceEntryPath, targetEntryPath, { recursive: true });
82
+ }
83
+
84
+ return targetSkillsDir;
88
85
  }
89
86
 
90
- async function ensureFlowusToken({ ctx, configPath }) {
91
- const existingConfig = readUserConfig({ configPath });
92
- const envToken = ctx.env.ODOO_FORGE_FLOWUS_TOKEN;
93
- const currentToken = envToken || getFlowusToken({ config: existingConfig });
87
+ async function ensureFlowusToken({ ctx }) {
88
+ const envToken = ctx.env.ODOO_FORGE_FLOWUS_TOKEN ?? ctx.env.FLOWUS_TOKEN;
89
+ const currentToken =
90
+ envToken ??
91
+ readCodexManagedToken({ homeDir: ctx.homeDir }) ??
92
+ readClaudeManagedToken({ homeDir: ctx.homeDir });
94
93
 
95
94
  if (currentToken) {
96
- writeFlowusToken({ configPath, token: currentToken });
97
95
  return currentToken;
98
96
  }
99
97
 
@@ -102,103 +100,47 @@ async function ensureFlowusToken({ ctx, configPath }) {
102
100
  if (!token) {
103
101
  throw new Error("FlowUS token is required.");
104
102
  }
105
- writeFlowusToken({ configPath, token });
106
103
  return token;
107
104
  }
108
105
 
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
106
  function runDoctor(ctx) {
138
- const installRoot = getInstallRoot({ homeDir: ctx.homeDir });
139
- const statePath = getStatePath({ homeDir: ctx.homeDir, installRoot });
140
- const configPath = getConfigPath({
141
- homeDir: ctx.homeDir,
142
- platform: ctx.platform,
143
- env: ctx.env,
144
- });
145
- const config = readUserConfig({ configPath });
146
- const currentRoot = getCurrentRoot({ homeDir: ctx.homeDir, installRoot });
107
+ const skillsPath = getInstalledSkillsPath({ homeDir: ctx.homeDir });
108
+ const codexConfigPath = getCodexConfigPath({ homeDir: ctx.homeDir });
109
+ const claudeConfigPath = getClaudeConfigPath({ homeDir: ctx.homeDir });
110
+ const codexToken = readCodexManagedToken({ homeDir: ctx.homeDir });
111
+ const claudeToken = readClaudeManagedToken({ homeDir: ctx.homeDir });
147
112
 
148
113
  ctx.output.log("Odoo Forge doctor");
149
- ctx.output.log(`Install root: ${installRoot}`);
150
- ctx.output.log(`Install root exists: ${fs.existsSync(installRoot) ? "yes" : "no"}`);
151
- ctx.output.log(`State file exists: ${fs.existsSync(statePath) ? "yes" : "no"}`);
152
- ctx.output.log(`Config file exists: ${fs.existsSync(configPath) ? "yes" : "no"}`);
153
- ctx.output.log(`FlowUS token exists: ${getFlowusToken({ config }) ? "yes" : "no"}`);
154
- ctx.output.log(`Current runtime exists: ${fs.existsSync(currentRoot) ? "yes" : "no"}`);
155
- ctx.output.log(
156
- `Claude marketplace exists: ${fs.existsSync(path.join(currentRoot, "claude-marketplace")) ? "yes" : "no"}`,
157
- );
114
+ ctx.output.log(`Skills root: ${skillsPath}`);
115
+ ctx.output.log(`Skills installed: ${fs.existsSync(skillsPath) ? "yes" : "no"}`);
116
+ ctx.output.log(`Codex config exists: ${fs.existsSync(codexConfigPath) ? "yes" : "no"}`);
117
+ ctx.output.log(`Codex FlowUS MCP exists: ${codexToken ? "yes" : "no"}`);
118
+ ctx.output.log(`Claude config exists: ${fs.existsSync(claudeConfigPath) ? "yes" : "no"}`);
119
+ ctx.output.log(`Claude FlowUS MCP exists: ${claudeToken ? "yes" : "no"}`);
120
+ ctx.output.log(`FlowUS token synchronized: ${codexToken && claudeToken && codexToken === claudeToken ? "yes" : "no"}`);
158
121
  }
159
122
 
160
123
  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
124
  const bundleRoot = await resolveBundleRoot(ctx.bundleRoot);
168
- const product = await readProduct(bundleRoot);
169
-
170
- await ensureFlowusToken({ ctx, configPath });
171
- const { currentRoot } = installRuntime({
125
+ const token = await ensureFlowusToken({ ctx });
126
+ const skillsPath = installSkills({
172
127
  bundleRoot,
173
- installRoot,
174
- version: product.version,
175
128
  homeDir: ctx.homeDir,
176
129
  });
177
130
 
178
- installCodexWiring({
131
+ const codexResult = installCodexWiring({
179
132
  homeDir: ctx.homeDir,
180
- installRoot,
181
- platform: ctx.platform,
182
- });
183
-
184
- const claudeResult = await installClaude({
185
- ctx,
186
- currentRoot,
187
- version: product.version,
188
- mode,
133
+ token,
189
134
  });
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
- },
135
+ const claudeResult = installClaudeWiring({
136
+ homeDir: ctx.homeDir,
137
+ token,
198
138
  });
199
139
 
200
140
  ctx.output.log(`${mode} complete.`);
201
- ctx.output.log(`Install root: ${installRoot}`);
141
+ ctx.output.log(`Skills root: ${skillsPath}`);
142
+ ctx.output.log(`Codex config: ${codexResult.configPath}`);
143
+ ctx.output.log(`Claude config: ${claudeResult.configPath}`);
202
144
  }
203
145
 
204
146
  async function runLogin(ctx, provider) {
@@ -206,21 +148,26 @@ async function runLogin(ctx, provider) {
206
148
  throw new Error(`Unsupported provider: ${provider}`);
207
149
  }
208
150
 
209
- const configPath = getConfigPath({
210
- homeDir: ctx.homeDir,
211
- platform: ctx.platform,
212
- env: ctx.env,
213
- });
214
151
  const token =
215
152
  ctx.env.ODOO_FORGE_FLOWUS_TOKEN ??
153
+ ctx.env.FLOWUS_TOKEN ??
216
154
  (await ctx.promptForSecret("Paste your FlowUS token: ")).trim();
217
155
 
218
156
  if (!token) {
219
157
  throw new Error("FlowUS token is required.");
220
158
  }
221
159
 
222
- writeFlowusToken({ configPath, token });
223
- ctx.output.log(`Saved FlowUS token to ${configPath}`);
160
+ const codexResult = installCodexWiring({
161
+ homeDir: ctx.homeDir,
162
+ token,
163
+ });
164
+ const claudeResult = installClaudeWiring({
165
+ homeDir: ctx.homeDir,
166
+ token,
167
+ });
168
+
169
+ ctx.output.log(`Saved FlowUS token to ${codexResult.configPath}`);
170
+ ctx.output.log(`Saved FlowUS token to ${claudeResult.configPath}`);
224
171
  }
225
172
 
226
173
  async function runMcp(ctx, provider) {
@@ -228,16 +175,10 @@ async function runMcp(ctx, provider) {
228
175
  throw new Error(`Unsupported MCP provider: ${provider}`);
229
176
  }
230
177
 
231
- const configPath = getConfigPath({
232
- homeDir: ctx.homeDir,
233
- platform: ctx.platform,
234
- env: ctx.env,
235
- });
236
- const config = readUserConfig({ configPath });
237
- const token = getFlowusToken({ config });
178
+ const token = ctx.env.FLOWUS_TOKEN ?? ctx.env.ODOO_FORGE_FLOWUS_TOKEN;
238
179
 
239
180
  if (!token) {
240
- throw new Error("Missing FlowUS token. Run `odoo-forge login flowus` first.");
181
+ throw new Error("Missing FlowUS token. Set FLOWUS_TOKEN before running `odoo-forge mcp flowus`.");
241
182
  }
242
183
 
243
184
  return await ctx.spawnProcess("npx", ["-y", "flowus-mcp-server@latest"], {
@@ -256,9 +197,7 @@ function normalizeContext(overrides = {}) {
256
197
  bundleRoot: overrides.bundleRoot,
257
198
  output: overrides.output ?? console,
258
199
  promptForSecret: overrides.promptForSecret ?? defaultPromptForSecret,
259
- runCommand: overrides.runCommand ?? defaultRunCommand,
260
200
  spawnProcess: overrides.spawnProcess ?? defaultSpawnProcess,
261
- hasCommand: overrides.hasCommand ?? defaultHasCommand,
262
201
  };
263
202
  }
264
203
 
package/src/paths.js CHANGED
@@ -1,26 +1,22 @@
1
1
  import os from "node:os";
2
2
  import path from "node:path";
3
3
 
4
- export function getInstallRoot({ homeDir = os.homedir() } = {}) {
5
- return path.join(homeDir, ".odoo-forge");
6
- }
7
-
8
- export function getCurrentRoot({ homeDir = os.homedir(), installRoot } = {}) {
9
- return path.join(installRoot ?? getInstallRoot({ homeDir }), "current");
4
+ export function getCodexConfigPath({ homeDir = os.homedir() } = {}) {
5
+ return path.join(homeDir, ".codex", "config.toml");
10
6
  }
11
7
 
12
- export function getVersionsRoot({ homeDir = os.homedir(), installRoot } = {}) {
13
- return path.join(installRoot ?? getInstallRoot({ homeDir }), "versions");
8
+ export function getClaudeConfigPath({ homeDir = os.homedir() } = {}) {
9
+ return path.join(homeDir, ".claude.json");
14
10
  }
15
11
 
16
- export function getStatePath({ homeDir = os.homedir(), installRoot } = {}) {
17
- return path.join(installRoot ?? getInstallRoot({ homeDir }), "state.json");
12
+ export function getAgentsSkillsRoot({ homeDir = os.homedir() } = {}) {
13
+ return path.join(homeDir, ".agents", "skills");
18
14
  }
19
15
 
20
- export function getCodexConfigPath({ homeDir = os.homedir() } = {}) {
21
- return path.join(homeDir, ".codex", "config.toml");
16
+ export function getInstalledSkillsPath({ homeDir = os.homedir() } = {}) {
17
+ return getAgentsSkillsRoot({ homeDir });
22
18
  }
23
19
 
24
- export function getCodexSkillsLinkPath({ homeDir = os.homedir() } = {}) {
20
+ export function getLegacyInstalledSkillsPath({ homeDir = os.homedir() } = {}) {
25
21
  return path.join(homeDir, ".agents", "skills", "odoo-forge");
26
22
  }
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
- }