@wpmoo/odoo 0.8.68 → 0.9.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/bin/wpmoo.js +10 -0
- package/package.json +11 -44
- package/LICENSE +0 -22
- package/README.md +0 -510
- package/dist/addons-yaml.js +0 -59
- package/dist/args.js +0 -259
- package/dist/cli.js +0 -1002
- package/dist/cockpit/command-palette.js +0 -19
- package/dist/cockpit/command-registry.js +0 -91
- package/dist/cockpit/daily-prompts.js +0 -177
- package/dist/cockpit/menu.js +0 -88
- package/dist/cockpit/safety.js +0 -22
- package/dist/compose-layout.js +0 -118
- package/dist/daily-actions.js +0 -190
- package/dist/doctor.js +0 -519
- package/dist/environment-context.js +0 -10
- package/dist/environment-version.js +0 -5
- package/dist/environment.js +0 -136
- package/dist/external-assets.js +0 -153
- package/dist/external-templates.js +0 -86
- package/dist/git.js +0 -98
- package/dist/github.js +0 -87
- package/dist/help.js +0 -151
- package/dist/menu-navigation.js +0 -67
- package/dist/module-actions.js +0 -114
- package/dist/odoo-versions.js +0 -1
- package/dist/path-validation.js +0 -50
- package/dist/prompt-copy.js +0 -8
- package/dist/prompt-repositories.js +0 -34
- package/dist/repo-actions.js +0 -158
- package/dist/repo-url.js +0 -27
- package/dist/repository-preflight.js +0 -46
- package/dist/safe-reset.js +0 -217
- package/dist/scaffold.js +0 -161
- package/dist/source-actions.js +0 -65
- package/dist/source-manifest.js +0 -338
- package/dist/status.js +0 -239
- package/dist/templates.js +0 -754
- package/dist/types.js +0 -1
- package/dist/update-check.js +0 -106
- package/dist/version.js +0 -19
- package/docs/assets/patreon-donate.png +0 -0
- package/docs/assets/wpmoo-banner.png +0 -0
- package/docs/external-resources.md +0 -136
- package/docs/generated-environment-verification.md +0 -140
- package/docs/handoff.md +0 -29
package/dist/status.js
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import { access, readdir, readFile, stat } from 'node:fs/promises';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { detectComposeLayout, readEnvFile, selectedComposeEnvironment } from './compose-layout.js';
|
|
4
|
-
import { defaultOdooVersion, markerPath } from './environment.js';
|
|
5
|
-
import { isValidPathSegment, validateRepoPath } from './path-validation.js';
|
|
6
|
-
const validSourceTypes = ['private', 'oca', 'external'];
|
|
7
|
-
function normalizeSourceType(sourceType) {
|
|
8
|
-
if (typeof sourceType === 'string' && validSourceTypes.includes(sourceType)) {
|
|
9
|
-
return sourceType;
|
|
10
|
-
}
|
|
11
|
-
return 'private';
|
|
12
|
-
}
|
|
13
|
-
function sourceRepoPath(target, sourceType, path) {
|
|
14
|
-
return join(target, 'odoo/custom/src', sourceType, path);
|
|
15
|
-
}
|
|
16
|
-
async function pathExists(path) {
|
|
17
|
-
try {
|
|
18
|
-
await access(path);
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
function errorMessage(error) {
|
|
26
|
-
return error instanceof Error ? error.message : String(error);
|
|
27
|
-
}
|
|
28
|
-
function isRecord(value) {
|
|
29
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
30
|
-
}
|
|
31
|
-
function parseMetadata(content) {
|
|
32
|
-
const parsed = JSON.parse(content);
|
|
33
|
-
if (!isRecord(parsed)) {
|
|
34
|
-
throw new Error('metadata is not an object');
|
|
35
|
-
}
|
|
36
|
-
return parsed;
|
|
37
|
-
}
|
|
38
|
-
function sourceRepoPathsFromMetadata(metadata) {
|
|
39
|
-
const sourceRepoPaths = [];
|
|
40
|
-
const sourceRepoLocations = [];
|
|
41
|
-
const invalidSourceRepoPaths = [];
|
|
42
|
-
if (!Array.isArray(metadata.sourceRepos)) {
|
|
43
|
-
return { sourceRepoPaths, sourceRepoLocations, invalidSourceRepoPaths };
|
|
44
|
-
}
|
|
45
|
-
for (const repo of metadata.sourceRepos) {
|
|
46
|
-
const path = repo && typeof repo.path === 'string' ? repo.path.trim() : '';
|
|
47
|
-
if (!path)
|
|
48
|
-
continue;
|
|
49
|
-
if (!isValidPathSegment(path)) {
|
|
50
|
-
invalidSourceRepoPaths.push(path);
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
const sourceType = normalizeSourceType(typeof repo.sourceType === 'string' ? repo.sourceType : undefined);
|
|
54
|
-
const normalizedPath = validateRepoPath(path);
|
|
55
|
-
sourceRepoPaths.push(normalizedPath);
|
|
56
|
-
sourceRepoLocations.push({ sourceType, path: normalizedPath });
|
|
57
|
-
}
|
|
58
|
-
return { sourceRepoPaths, sourceRepoLocations, invalidSourceRepoPaths };
|
|
59
|
-
}
|
|
60
|
-
async function missingCoreFiles(target, odooVersion) {
|
|
61
|
-
const missing = [];
|
|
62
|
-
const checks = [
|
|
63
|
-
{ label: 'moo', path: join(target, 'moo') },
|
|
64
|
-
{ label: 'README.md', path: join(target, 'README.md') },
|
|
65
|
-
{ label: 'AGENTS.md', path: join(target, 'AGENTS.md') },
|
|
66
|
-
{ label: 'scripts/', path: join(target, 'scripts'), mustBeDirectory: true },
|
|
67
|
-
];
|
|
68
|
-
for (const check of checks) {
|
|
69
|
-
if (!(await pathExists(check.path))) {
|
|
70
|
-
missing.push(check.label);
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
if (check.mustBeDirectory) {
|
|
74
|
-
const fileStat = await stat(check.path);
|
|
75
|
-
if (!fileStat.isDirectory())
|
|
76
|
-
missing.push(check.label);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const env = await readEnvFile(target);
|
|
80
|
-
const composeLayout = await detectComposeLayout(target, {
|
|
81
|
-
odooVersions: [odooVersion],
|
|
82
|
-
envName: selectedComposeEnvironment(env),
|
|
83
|
-
});
|
|
84
|
-
missing.push(...composeLayout.missingFiles);
|
|
85
|
-
return { missing, composeFiles: composeLayout.files, composeErrors: composeLayout.errors };
|
|
86
|
-
}
|
|
87
|
-
async function countModuleCandidatesInRepoPath(path) {
|
|
88
|
-
if (!(await pathExists(path)))
|
|
89
|
-
return 0;
|
|
90
|
-
const rootStat = await stat(path);
|
|
91
|
-
if (!rootStat.isDirectory())
|
|
92
|
-
return 0;
|
|
93
|
-
let count = 0;
|
|
94
|
-
const stack = [path];
|
|
95
|
-
while (stack.length > 0) {
|
|
96
|
-
const current = stack.pop();
|
|
97
|
-
if (!current)
|
|
98
|
-
continue;
|
|
99
|
-
const entries = await readdir(current, { withFileTypes: true });
|
|
100
|
-
let hasManifest = false;
|
|
101
|
-
for (const entry of entries) {
|
|
102
|
-
if (entry.isFile() && entry.name === '__manifest__.py') {
|
|
103
|
-
hasManifest = true;
|
|
104
|
-
}
|
|
105
|
-
else if (entry.isDirectory()) {
|
|
106
|
-
stack.push(join(current, entry.name));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (hasManifest)
|
|
110
|
-
count += 1;
|
|
111
|
-
}
|
|
112
|
-
return count;
|
|
113
|
-
}
|
|
114
|
-
function summaryText(status) {
|
|
115
|
-
if (status.kind === 'no_environment')
|
|
116
|
-
return 'No WPMoo environment detected.';
|
|
117
|
-
if (status.kind === 'invalid_metadata')
|
|
118
|
-
return 'Environment metadata is invalid.';
|
|
119
|
-
const prefix = status.missingCoreFiles.length > 0 ||
|
|
120
|
-
status.invalidSourceRepoPaths.length > 0 ||
|
|
121
|
-
status.composeErrors.length > 0
|
|
122
|
-
? 'Environment needs attention'
|
|
123
|
-
: 'Environment ready';
|
|
124
|
-
return `${prefix}: Odoo ${status.odooVersion}, source repos ${status.sourceRepoCount}, module candidates ${status.moduleCandidateCount}.`;
|
|
125
|
-
}
|
|
126
|
-
function isStatusHealthy(status) {
|
|
127
|
-
if (status.kind !== 'environment')
|
|
128
|
-
return false;
|
|
129
|
-
return (status.missingCoreFiles.length === 0 &&
|
|
130
|
-
status.invalidSourceRepoPaths.length === 0 &&
|
|
131
|
-
status.composeErrors.length === 0);
|
|
132
|
-
}
|
|
133
|
-
export function environmentStatusJson(status) {
|
|
134
|
-
return {
|
|
135
|
-
schemaVersion: 1,
|
|
136
|
-
command: 'status',
|
|
137
|
-
ok: isStatusHealthy(status),
|
|
138
|
-
status,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
export async function getEnvironmentStatus(target) {
|
|
142
|
-
const metadataFullPath = join(target, markerPath);
|
|
143
|
-
if (!(await pathExists(metadataFullPath))) {
|
|
144
|
-
return {
|
|
145
|
-
kind: 'no_environment',
|
|
146
|
-
target,
|
|
147
|
-
metadataPath: markerPath,
|
|
148
|
-
recommendedNextAction: 'Run npx @wpmoo/odoo create ...',
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
let metadata;
|
|
152
|
-
try {
|
|
153
|
-
const content = await readFile(metadataFullPath, 'utf8');
|
|
154
|
-
metadata = parseMetadata(content);
|
|
155
|
-
}
|
|
156
|
-
catch (error) {
|
|
157
|
-
return {
|
|
158
|
-
kind: 'invalid_metadata',
|
|
159
|
-
target,
|
|
160
|
-
metadataPath: markerPath,
|
|
161
|
-
metadataError: errorMessage(error),
|
|
162
|
-
recommendedNextAction: 'Fix .wpmoo/odoo.json or run npx @wpmoo/odoo reset from a valid environment.',
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
const odooVersion = typeof metadata.odooVersion === 'string' && metadata.odooVersion.trim()
|
|
166
|
-
? metadata.odooVersion.trim()
|
|
167
|
-
: defaultOdooVersion;
|
|
168
|
-
const { sourceRepoPaths, sourceRepoLocations, invalidSourceRepoPaths } = sourceRepoPathsFromMetadata(metadata);
|
|
169
|
-
const repoRoots = sourceRepoLocations.map(({ sourceType, path }) => sourceRepoPath(target, sourceType, path));
|
|
170
|
-
let moduleCandidateCount = 0;
|
|
171
|
-
for (const repoRoot of repoRoots) {
|
|
172
|
-
moduleCandidateCount += await countModuleCandidatesInRepoPath(repoRoot);
|
|
173
|
-
}
|
|
174
|
-
const { missing: missingFiles, composeFiles, composeErrors, } = await missingCoreFiles(target, odooVersion);
|
|
175
|
-
let recommendedNextAction = 'Run npx @wpmoo/odoo doctor for deep checks or ./moo start.';
|
|
176
|
-
if (invalidSourceRepoPaths.length > 0) {
|
|
177
|
-
recommendedNextAction =
|
|
178
|
-
'Fix invalid source repo paths in .wpmoo/odoo.json, then run npx @wpmoo/odoo doctor.';
|
|
179
|
-
}
|
|
180
|
-
else if (missingFiles.length > 0) {
|
|
181
|
-
recommendedNextAction = 'Run npx @wpmoo/odoo reset, then npx @wpmoo/odoo doctor.';
|
|
182
|
-
}
|
|
183
|
-
else if (composeErrors.length > 0) {
|
|
184
|
-
recommendedNextAction = 'Fix compose layout errors, then run npx @wpmoo/odoo doctor.';
|
|
185
|
-
}
|
|
186
|
-
else if (sourceRepoPaths.length === 0) {
|
|
187
|
-
recommendedNextAction = 'Run npx @wpmoo/odoo add-repo ...';
|
|
188
|
-
}
|
|
189
|
-
return {
|
|
190
|
-
kind: 'environment',
|
|
191
|
-
target,
|
|
192
|
-
metadataPath: markerPath,
|
|
193
|
-
odooVersion,
|
|
194
|
-
sourceRepoCount: sourceRepoPaths.length,
|
|
195
|
-
sourceRepoPaths,
|
|
196
|
-
invalidSourceRepoPaths,
|
|
197
|
-
moduleCandidateCount,
|
|
198
|
-
composeFiles,
|
|
199
|
-
composeErrors,
|
|
200
|
-
missingCoreFiles: missingFiles,
|
|
201
|
-
recommendedNextAction,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
export function renderEnvironmentStatusSummary(status) {
|
|
205
|
-
return summaryText(status);
|
|
206
|
-
}
|
|
207
|
-
export function renderEnvironmentStatus(status) {
|
|
208
|
-
const lines = [`Status: ${summaryText(status)}`];
|
|
209
|
-
if (status.kind === 'no_environment') {
|
|
210
|
-
lines.push(`Metadata: missing ${status.metadataPath}`);
|
|
211
|
-
lines.push(`Next: ${status.recommendedNextAction}`);
|
|
212
|
-
return lines.join('\n');
|
|
213
|
-
}
|
|
214
|
-
if (status.kind === 'invalid_metadata') {
|
|
215
|
-
lines.push(`Metadata: invalid ${status.metadataPath}`);
|
|
216
|
-
lines.push(`Error: ${status.metadataError}`);
|
|
217
|
-
lines.push(`Next: ${status.recommendedNextAction}`);
|
|
218
|
-
return lines.join('\n');
|
|
219
|
-
}
|
|
220
|
-
lines.push(`Metadata: ${status.metadataPath}`);
|
|
221
|
-
lines.push(`Odoo: ${status.odooVersion}`);
|
|
222
|
-
lines.push(`Compose files: ${status.composeFiles.length > 0 ? status.composeFiles.join(', ') : '(missing)'}`);
|
|
223
|
-
if (status.composeErrors.length > 0) {
|
|
224
|
-
lines.push(`Compose errors: ${status.composeErrors.join(', ')}`);
|
|
225
|
-
}
|
|
226
|
-
lines.push(`Source repos: ${status.sourceRepoCount}`);
|
|
227
|
-
lines.push(`Source repo paths: ${status.sourceRepoPaths.length > 0 ? status.sourceRepoPaths.join(', ') : '(none configured)'}`);
|
|
228
|
-
if (status.invalidSourceRepoPaths.length > 0) {
|
|
229
|
-
lines.push(`Invalid source repo paths: ${status.invalidSourceRepoPaths.join(', ')}`);
|
|
230
|
-
}
|
|
231
|
-
lines.push(`Module candidates: ${status.moduleCandidateCount}`);
|
|
232
|
-
lines.push(`Missing core files: ${status.missingCoreFiles.length > 0 ? status.missingCoreFiles.join(', ') : '(none)'}`);
|
|
233
|
-
lines.push(`Next: ${status.recommendedNextAction}`);
|
|
234
|
-
return lines.join('\n');
|
|
235
|
-
}
|
|
236
|
-
export async function renderEnvironmentStatusForTarget(target) {
|
|
237
|
-
const status = await getEnvironmentStatus(target);
|
|
238
|
-
return renderEnvironmentStatus(status);
|
|
239
|
-
}
|