taskmonkey-cli 0.5.1 → 0.6.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/tm.js +1 -0
- package/package.json +1 -1
- package/src/commands/pull.js +26 -0
- package/src/commands/sync.js +32 -8
- package/src/commands/watch.js +39 -9
package/bin/tm.js
CHANGED
package/package.json
CHANGED
package/src/commands/pull.js
CHANGED
|
@@ -338,6 +338,32 @@ Jeder Handler bekommt einen Kontext mit:
|
|
|
338
338
|
\\GuzzleHttp\\Client // Alternative HTTP-Library
|
|
339
339
|
\`\`\`
|
|
340
340
|
|
|
341
|
+
## Database Gateway (Remote-SQL)
|
|
342
|
+
|
|
343
|
+
Für Zugriff auf externe Datenbanken (Contao, WordPress, etc.) gibt es das Database Gateway System.
|
|
344
|
+
Der Server hat SSH-Zugang zu Remote-Servern und deployt ein temporäres PHP-Gateway.
|
|
345
|
+
|
|
346
|
+
**Workflow in einem Tool/Prompt:**
|
|
347
|
+
1. \`deployGateway\` mit \`project\` und optional \`allow_write: true\`
|
|
348
|
+
2. \`executeQuery\` / \`listTables\` / \`describeTable\`
|
|
349
|
+
3. \`removeGateway\` am Ende
|
|
350
|
+
|
|
351
|
+
**Projekte konfigurieren:** \`database_connections.php\`
|
|
352
|
+
\`\`\`php
|
|
353
|
+
'database_connections' => [
|
|
354
|
+
'meine_website' => [
|
|
355
|
+
'host' => 'server.example.com',
|
|
356
|
+
'user' => 'ssh-user',
|
|
357
|
+
'port' => 22,
|
|
358
|
+
'key' => '~/.ssh/id_rsa', // Auf dem Server, nicht lokal!
|
|
359
|
+
'web_root' => '/var/www/html',
|
|
360
|
+
'cms' => 'contao',
|
|
361
|
+
],
|
|
362
|
+
],
|
|
363
|
+
\`\`\`
|
|
364
|
+
|
|
365
|
+
Siehe \`docs/DatabaseGateway.md\` für die vollständige Dokumentation inkl. SQL-Beispiele und CMS-Tabellen.
|
|
366
|
+
|
|
341
367
|
## Conversation Tests
|
|
342
368
|
|
|
343
369
|
Definiere Tests um Prompt-Änderungen automatisch zu validieren:
|
package/src/commands/sync.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readdirSync, readFileSync
|
|
1
|
+
import { readdirSync, readFileSync } from 'fs';
|
|
2
2
|
import { join, relative } from 'path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import ora from 'ora';
|
|
@@ -22,7 +22,7 @@ function collectFiles(dir, base = dir) {
|
|
|
22
22
|
return files;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export async function sync() {
|
|
25
|
+
export async function sync(options = {}) {
|
|
26
26
|
const config = loadConfig();
|
|
27
27
|
if (!config) {
|
|
28
28
|
console.error(chalk.red('Not logged in. Run `tm login` first.'));
|
|
@@ -30,22 +30,46 @@ export async function sync() {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const tenantDir = join(config._configDir, config.tenant_path || '.');
|
|
33
|
+
const deleteRemote = options.delete || false;
|
|
33
34
|
|
|
34
35
|
const spinner = ora('Collecting files...').start();
|
|
35
36
|
|
|
36
|
-
const
|
|
37
|
-
const
|
|
37
|
+
const localFiles = collectFiles(tenantDir);
|
|
38
|
+
const localPaths = Object.keys(localFiles);
|
|
38
39
|
|
|
39
|
-
if (
|
|
40
|
+
if (localPaths.length === 0) {
|
|
40
41
|
spinner.warn('No .php files found');
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
const client = createClient();
|
|
46
|
+
|
|
47
|
+
// Delete remote files that don't exist locally
|
|
48
|
+
if (deleteRemote) {
|
|
49
|
+
spinner.text = 'Comparing with server...';
|
|
50
|
+
try {
|
|
51
|
+
const remoteList = await client.get('/api/tenant/files');
|
|
52
|
+
const remotePaths = (remoteList.files || []).map(f => f.path);
|
|
53
|
+
const toDelete = remotePaths.filter(p => !localPaths.includes(p));
|
|
54
|
+
|
|
55
|
+
if (toDelete.length > 0) {
|
|
56
|
+
const result = await client.post('/api/tenant/delete-files', { files: toDelete });
|
|
57
|
+
spinner.stop();
|
|
58
|
+
console.log(chalk.red(`✗ ${result.deleted || 0} files deleted on server`));
|
|
59
|
+
for (const file of result.files || []) {
|
|
60
|
+
console.log(chalk.red(` - ${file}`));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
spinner.stop();
|
|
65
|
+
console.error(chalk.yellow(` Delete check failed: ${err.message}`));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
spinner.text = `Syncing ${localPaths.length} files...`;
|
|
45
70
|
|
|
46
71
|
try {
|
|
47
|
-
const
|
|
48
|
-
const result = await client.post('/api/tenant/sync', { files });
|
|
72
|
+
const result = await client.post('/api/tenant/sync', { files: localFiles });
|
|
49
73
|
|
|
50
74
|
spinner.stop();
|
|
51
75
|
|
package/src/commands/watch.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import chokidar from 'chokidar';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { loadConfig } from '../config.js';
|
|
4
|
+
import { createClient } from '../lib/api.js';
|
|
4
5
|
import { sync } from './sync.js';
|
|
5
|
-
import { join } from 'path';
|
|
6
|
+
import { join, relative } from 'path';
|
|
6
7
|
|
|
7
8
|
export async function watch() {
|
|
8
9
|
const config = loadConfig();
|
|
@@ -16,28 +17,57 @@ export async function watch() {
|
|
|
16
17
|
console.log(chalk.cyan('👀 Watching'), tenantDir);
|
|
17
18
|
console.log(chalk.gray(' Ctrl+C to stop\n'));
|
|
18
19
|
|
|
19
|
-
let
|
|
20
|
+
let syncTimer = null;
|
|
21
|
+
const pendingDeletes = [];
|
|
20
22
|
|
|
21
23
|
const watcher = chokidar.watch(join(tenantDir, '**/*.php'), {
|
|
22
24
|
ignoreInitial: true,
|
|
23
25
|
awaitWriteFinish: { stabilityThreshold: 300 },
|
|
24
26
|
});
|
|
25
27
|
|
|
26
|
-
watcher.on('
|
|
27
|
-
console.log(chalk.
|
|
28
|
+
watcher.on('add', (path) => {
|
|
29
|
+
console.log(chalk.green(` + ${relative(tenantDir, path)}`));
|
|
30
|
+
debouncedSync();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
watcher.on('change', (path) => {
|
|
34
|
+
console.log(chalk.yellow(` ~ ${relative(tenantDir, path)}`));
|
|
35
|
+
debouncedSync();
|
|
36
|
+
});
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
watcher.on('unlink', (path) => {
|
|
39
|
+
const relPath = relative(tenantDir, path);
|
|
40
|
+
console.log(chalk.red(` - ${relPath}`));
|
|
41
|
+
pendingDeletes.push(relPath);
|
|
42
|
+
debouncedSync();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
function debouncedSync() {
|
|
46
|
+
if (syncTimer) clearTimeout(syncTimer);
|
|
47
|
+
syncTimer = setTimeout(async () => {
|
|
32
48
|
try {
|
|
49
|
+
// Delete files first
|
|
50
|
+
if (pendingDeletes.length > 0) {
|
|
51
|
+
const toDelete = [...pendingDeletes];
|
|
52
|
+
pendingDeletes.length = 0;
|
|
53
|
+
try {
|
|
54
|
+
const client = createClient();
|
|
55
|
+
const result = await client.post('/api/tenant/delete-files', { files: toDelete });
|
|
56
|
+
if (result.deleted > 0) {
|
|
57
|
+
console.log(chalk.red(` ✓ ${result.deleted} deleted on server`));
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error(chalk.red(` Delete error: ${err.message}`));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Then sync remaining files
|
|
33
64
|
await sync();
|
|
34
65
|
} catch (err) {
|
|
35
66
|
console.error(chalk.red(` Sync error: ${err.message}`));
|
|
36
67
|
}
|
|
37
68
|
}, 500);
|
|
38
|
-
}
|
|
69
|
+
}
|
|
39
70
|
|
|
40
|
-
// Keep process alive
|
|
41
71
|
process.on('SIGINT', () => {
|
|
42
72
|
watcher.close();
|
|
43
73
|
console.log(chalk.gray('\nStopped.'));
|