taskmonkey-cli 0.10.0 → 0.11.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/package.json +1 -1
- package/src/commands/pull.js +11 -7
- package/src/commands/sync.js +60 -2
- package/src/commands/watch.js +10 -0
package/package.json
CHANGED
package/src/commands/pull.js
CHANGED
|
@@ -36,17 +36,21 @@ export async function pull() {
|
|
|
36
36
|
written++;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
// Write docs if available
|
|
39
|
+
// Write docs if available. The server returns relative paths that may
|
|
40
|
+
// contain subdirectories (e.g. cms/contao.md), so we need to mkdir -p
|
|
41
|
+
// the dirname of each full target path, not just the top-level folder.
|
|
40
42
|
if (docs?.files) {
|
|
41
43
|
for (const [name, content] of Object.entries(docs.files)) {
|
|
42
|
-
// shared/ files go to shared/,
|
|
43
|
-
const
|
|
44
|
+
// shared/ files go to shared/, everything else to docs/
|
|
45
|
+
const isShared = name.startsWith('shared/');
|
|
46
|
+
const baseDir = isShared
|
|
44
47
|
? join(config._configDir, 'shared')
|
|
45
48
|
: join(config._configDir, 'docs');
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
const relName = isShared ? name.substring(7) : name;
|
|
50
|
+
const fullPath = join(baseDir, relName);
|
|
51
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
52
|
+
writeFileSync(fullPath, content);
|
|
53
|
+
console.log(chalk.gray(` ${isShared ? 'shared' : 'docs'}/${relName}`));
|
|
50
54
|
written++;
|
|
51
55
|
}
|
|
52
56
|
}
|
package/src/commands/sync.js
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import { readdirSync, readFileSync } from 'fs';
|
|
2
|
-
import { join, relative } from 'path';
|
|
1
|
+
import { readdirSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join, relative, dirname } from 'path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
import { loadConfig } from '../config.js';
|
|
6
6
|
import { createClient } from '../lib/api.js';
|
|
7
7
|
|
|
8
|
+
// Skip runtime dirs and the local CLI metadata when collecting files to upload.
|
|
9
|
+
const SKIP_TOP_LEVEL = new Set(['logs', 'tmp', '.claude', '.git', 'node_modules', 'docs', 'shared']);
|
|
10
|
+
|
|
8
11
|
function collectFiles(dir, base = dir) {
|
|
9
12
|
const files = {};
|
|
10
13
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
11
14
|
|
|
12
15
|
for (const entry of entries) {
|
|
16
|
+
if (dir === base && SKIP_TOP_LEVEL.has(entry.name)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
13
19
|
const fullPath = join(dir, entry.name);
|
|
14
20
|
if (entry.isDirectory()) {
|
|
15
21
|
Object.assign(files, collectFiles(fullPath, base));
|
|
@@ -22,6 +28,55 @@ function collectFiles(dir, base = dir) {
|
|
|
22
28
|
return files;
|
|
23
29
|
}
|
|
24
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Pulls logs/ and tmp/ from the server into the local working copy so the
|
|
33
|
+
* developer can read them with normal file tools (cat, grep, Claude, etc.).
|
|
34
|
+
*
|
|
35
|
+
* Text files come back as utf-8 strings; binary files come back as base64
|
|
36
|
+
* under `binary_files`. We write both verbatim.
|
|
37
|
+
*
|
|
38
|
+
* Failures here are non-fatal — the sync itself was already successful.
|
|
39
|
+
*/
|
|
40
|
+
export async function pullRuntime(tenantDir, { quiet = false } = {}) {
|
|
41
|
+
const client = createClient();
|
|
42
|
+
let runtime;
|
|
43
|
+
try {
|
|
44
|
+
runtime = await client.get('/api/tenant/runtime');
|
|
45
|
+
} catch (err) {
|
|
46
|
+
if (!quiet) {
|
|
47
|
+
console.log(chalk.gray(` (runtime pull skipped: ${err.message})`));
|
|
48
|
+
}
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let written = 0;
|
|
53
|
+
const writeOne = (relPath, content, isBase64) => {
|
|
54
|
+
const fullPath = join(tenantDir, relPath);
|
|
55
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
56
|
+
if (isBase64) {
|
|
57
|
+
writeFileSync(fullPath, Buffer.from(content, 'base64'));
|
|
58
|
+
} else {
|
|
59
|
+
writeFileSync(fullPath, content);
|
|
60
|
+
}
|
|
61
|
+
written++;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Some servers serialize empty PHP arrays as JSON [] instead of {}.
|
|
65
|
+
// Treat plain arrays as empty since they have no path-keyed entries.
|
|
66
|
+
const asMap = (v) => (v && typeof v === 'object' && !Array.isArray(v)) ? v : {};
|
|
67
|
+
for (const [relPath, content] of Object.entries(asMap(runtime.files))) {
|
|
68
|
+
writeOne(relPath, content, false);
|
|
69
|
+
}
|
|
70
|
+
for (const [relPath, content] of Object.entries(asMap(runtime.binary_files))) {
|
|
71
|
+
writeOne(relPath, content, true);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (written > 0 && !quiet) {
|
|
75
|
+
console.log(chalk.gray(` ↓ ${written} runtime files (logs/, tmp/)`));
|
|
76
|
+
}
|
|
77
|
+
return written;
|
|
78
|
+
}
|
|
79
|
+
|
|
25
80
|
export async function sync(options = {}) {
|
|
26
81
|
const config = loadConfig();
|
|
27
82
|
if (!config) {
|
|
@@ -88,6 +143,9 @@ export async function sync(options = {}) {
|
|
|
88
143
|
console.log(chalk.red(` ${err.path}: ${err.error}`));
|
|
89
144
|
}
|
|
90
145
|
}
|
|
146
|
+
|
|
147
|
+
// Mirror server-side logs/ and tmp/ into the working copy for debugging.
|
|
148
|
+
await pullRuntime(tenantDir);
|
|
91
149
|
} catch (err) {
|
|
92
150
|
spinner.fail(err.message);
|
|
93
151
|
process.exit(1);
|
package/src/commands/watch.js
CHANGED
|
@@ -20,9 +20,19 @@ export async function watch() {
|
|
|
20
20
|
let syncTimer = null;
|
|
21
21
|
const pendingDeletes = [];
|
|
22
22
|
|
|
23
|
+
// Watch only .php files, and explicitly ignore the runtime dirs that
|
|
24
|
+
// sync() writes back into the working copy — otherwise pullRuntime would
|
|
25
|
+
// re-trigger sync in an endless loop.
|
|
23
26
|
const watcher = chokidar.watch(join(tenantDir, '**/*.php'), {
|
|
24
27
|
ignoreInitial: true,
|
|
25
28
|
awaitWriteFinish: { stabilityThreshold: 300 },
|
|
29
|
+
ignored: [
|
|
30
|
+
/(^|[\/\\])logs[\/\\]/,
|
|
31
|
+
/(^|[\/\\])tmp[\/\\]/,
|
|
32
|
+
/(^|[\/\\])\.claude[\/\\]/,
|
|
33
|
+
/(^|[\/\\])\.git[\/\\]/,
|
|
34
|
+
/(^|[\/\\])node_modules[\/\\]/,
|
|
35
|
+
],
|
|
26
36
|
});
|
|
27
37
|
|
|
28
38
|
watcher.on('add', (path) => {
|