cli4ai 1.2.0 → 1.2.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/README.md +39 -0
- package/dist/bin.d.ts +6 -0
- package/dist/bin.js +105 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +335 -0
- package/dist/commands/add.d.ts +11 -0
- package/dist/commands/add.js +464 -0
- package/dist/commands/browse.d.ts +4 -0
- package/dist/commands/browse.js +382 -0
- package/dist/commands/config.d.ts +10 -0
- package/dist/commands/config.js +121 -0
- package/dist/commands/info.d.ts +9 -0
- package/dist/commands/info.js +125 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.js +458 -0
- package/dist/commands/list.d.ts +10 -0
- package/dist/commands/list.js +76 -0
- package/dist/commands/mcp-config.d.ts +10 -0
- package/dist/commands/mcp-config.js +49 -0
- package/dist/commands/remotes.d.ts +22 -0
- package/dist/commands/remotes.js +196 -0
- package/dist/commands/remove.d.ts +8 -0
- package/dist/commands/remove.js +61 -0
- package/dist/commands/routines.d.ts +29 -0
- package/dist/commands/routines.js +363 -0
- package/dist/commands/run.d.ts +12 -0
- package/dist/commands/run.js +104 -0
- package/dist/commands/scheduler.d.ts +27 -0
- package/dist/commands/scheduler.js +350 -0
- package/dist/commands/search.d.ts +9 -0
- package/dist/commands/search.js +162 -0
- package/dist/commands/secrets.d.ts +28 -0
- package/dist/commands/secrets.js +236 -0
- package/dist/commands/serve.d.ts +13 -0
- package/dist/commands/serve.js +49 -0
- package/dist/commands/start.d.ts +8 -0
- package/dist/commands/start.js +27 -0
- package/dist/commands/update.d.ts +17 -0
- package/dist/commands/update.js +210 -0
- package/dist/core/config.d.ts +91 -0
- package/dist/core/config.js +738 -0
- package/dist/core/execute.d.ts +51 -0
- package/dist/core/execute.js +475 -0
- package/dist/core/link.d.ts +39 -0
- package/dist/core/link.js +214 -0
- package/dist/core/lockfile.d.ts +63 -0
- package/dist/core/lockfile.js +140 -0
- package/dist/core/manifest.d.ts +96 -0
- package/dist/core/manifest.js +224 -0
- package/dist/core/registry.d.ts +74 -0
- package/dist/core/registry.js +116 -0
- package/dist/core/remote-client.d.ts +98 -0
- package/dist/core/remote-client.js +252 -0
- package/dist/core/remotes.d.ts +88 -0
- package/dist/core/remotes.js +206 -0
- package/dist/core/routine-engine.d.ts +124 -0
- package/dist/core/routine-engine.js +699 -0
- package/dist/core/routines.d.ts +36 -0
- package/dist/core/routines.js +132 -0
- package/dist/core/scheduler-daemon.d.ts +10 -0
- package/dist/core/scheduler-daemon.js +77 -0
- package/dist/core/scheduler.d.ts +131 -0
- package/dist/core/scheduler.js +492 -0
- package/dist/core/secrets.d.ts +48 -0
- package/dist/core/secrets.js +384 -0
- package/dist/lib/cli.d.ts +84 -0
- package/dist/lib/cli.js +216 -0
- package/dist/mcp/adapter.d.ts +35 -0
- package/dist/mcp/adapter.js +94 -0
- package/dist/mcp/config-gen.d.ts +31 -0
- package/dist/mcp/config-gen.js +75 -0
- package/dist/mcp/server.d.ts +41 -0
- package/dist/mcp/server.js +296 -0
- package/dist/server/service.d.ts +85 -0
- package/dist/server/service.js +304 -0
- package/package.json +6 -3
- package/src/bin.ts +0 -118
- package/src/cli.ts +0 -412
- package/src/commands/add.ts +0 -562
- package/src/commands/browse.ts +0 -449
- package/src/commands/config.ts +0 -154
- package/src/commands/info.ts +0 -133
- package/src/commands/init.ts +0 -514
- package/src/commands/list.ts +0 -95
- package/src/commands/mcp-config.ts +0 -69
- package/src/commands/remotes.ts +0 -253
- package/src/commands/remove.ts +0 -78
- package/src/commands/routines.ts +0 -427
- package/src/commands/run.ts +0 -127
- package/src/commands/scheduler.ts +0 -438
- package/src/commands/search.ts +0 -185
- package/src/commands/secrets.ts +0 -292
- package/src/commands/serve.ts +0 -66
- package/src/commands/start.ts +0 -40
- package/src/commands/update.ts +0 -252
- package/src/core/config.ts +0 -845
- package/src/core/execute.ts +0 -569
- package/src/core/link.ts +0 -246
- package/src/core/lockfile.ts +0 -187
- package/src/core/manifest.ts +0 -327
- package/src/core/registry.ts +0 -165
- package/src/core/remote-client.ts +0 -419
- package/src/core/remotes.ts +0 -268
- package/src/core/routine-engine.ts +0 -895
- package/src/core/routines.ts +0 -171
- package/src/core/scheduler-daemon.ts +0 -94
- package/src/core/scheduler.ts +0 -606
- package/src/core/secrets.ts +0 -430
- package/src/lib/cli.ts +0 -261
- package/src/mcp/adapter.ts +0 -131
- package/src/mcp/config-gen.ts +0 -106
- package/src/mcp/server.ts +0 -365
- package/src/server/service.ts +0 -434
package/src/core/routines.ts
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Routine discovery and resolution.
|
|
3
|
-
*
|
|
4
|
-
* Routines live in:
|
|
5
|
-
* - local: <project>/.cli4ai/routines
|
|
6
|
-
* - global: ~/.cli4ai/routines
|
|
7
|
-
*
|
|
8
|
-
* Resolution order:
|
|
9
|
-
* - local before global (unless globalOnly)
|
|
10
|
-
* - within a scope: .routine.yaml > .routine.yml > .routine.json > .routine.sh
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { existsSync, readdirSync, statSync, readFileSync } from 'fs';
|
|
14
|
-
import { resolve } from 'path';
|
|
15
|
-
import { parse as parseYaml } from 'yaml';
|
|
16
|
-
import { ensureCli4aiHome, ensureLocalDir, ROUTINES_DIR, LOCAL_ROUTINES_DIR } from './config.js';
|
|
17
|
-
import { validateScheduleConfig, type RoutineSchedule } from './routine-engine.js';
|
|
18
|
-
|
|
19
|
-
export type RoutineKind = 'yaml' | 'json' | 'bash';
|
|
20
|
-
export type RoutineScope = 'local' | 'global';
|
|
21
|
-
|
|
22
|
-
export interface RoutineInfo {
|
|
23
|
-
name: string;
|
|
24
|
-
kind: RoutineKind;
|
|
25
|
-
scope: RoutineScope;
|
|
26
|
-
path: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface ResolveRoutineOptions {
|
|
30
|
-
globalOnly?: boolean;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const ROUTINE_FILES = [
|
|
34
|
-
{ kind: 'yaml' as const, suffix: '.routine.yaml' },
|
|
35
|
-
{ kind: 'yaml' as const, suffix: '.routine.yml' },
|
|
36
|
-
{ kind: 'json' as const, suffix: '.routine.json' },
|
|
37
|
-
{ kind: 'bash' as const, suffix: '.routine.sh' }
|
|
38
|
-
] as const;
|
|
39
|
-
|
|
40
|
-
const NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
41
|
-
|
|
42
|
-
export function validateRoutineName(name: string): void {
|
|
43
|
-
if (!NAME_PATTERN.test(name)) {
|
|
44
|
-
throw new Error(`Invalid routine name: ${name}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function listRoutinesInDir(dir: string, scope: RoutineScope): RoutineInfo[] {
|
|
49
|
-
if (!existsSync(dir)) return [];
|
|
50
|
-
|
|
51
|
-
const results: RoutineInfo[] = [];
|
|
52
|
-
|
|
53
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
54
|
-
if (!entry.isFile()) continue;
|
|
55
|
-
|
|
56
|
-
for (const def of ROUTINE_FILES) {
|
|
57
|
-
if (!entry.name.endsWith(def.suffix)) continue;
|
|
58
|
-
|
|
59
|
-
const name = entry.name.slice(0, -def.suffix.length);
|
|
60
|
-
if (!NAME_PATTERN.test(name)) continue;
|
|
61
|
-
|
|
62
|
-
const fullPath = resolve(dir, entry.name);
|
|
63
|
-
|
|
64
|
-
// Best-effort: ensure it's a regular file
|
|
65
|
-
try {
|
|
66
|
-
if (!statSync(fullPath).isFile()) continue;
|
|
67
|
-
} catch {
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
results.push({
|
|
72
|
-
name,
|
|
73
|
-
kind: def.kind,
|
|
74
|
-
scope,
|
|
75
|
-
path: fullPath
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return results;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function getLocalRoutines(projectDir: string): RoutineInfo[] {
|
|
84
|
-
ensureLocalDir(projectDir);
|
|
85
|
-
const dir = resolve(projectDir, LOCAL_ROUTINES_DIR);
|
|
86
|
-
return listRoutinesInDir(dir, 'local');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function getGlobalRoutines(): RoutineInfo[] {
|
|
90
|
-
ensureCli4aiHome();
|
|
91
|
-
return listRoutinesInDir(ROUTINES_DIR, 'global');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function resolveRoutine(name: string, projectDir: string, options: ResolveRoutineOptions = {}): RoutineInfo | null {
|
|
95
|
-
validateRoutineName(name);
|
|
96
|
-
|
|
97
|
-
const candidates: Array<{ scope: RoutineScope; baseDir: string }> = options.globalOnly
|
|
98
|
-
? [{ scope: 'global', baseDir: ROUTINES_DIR }]
|
|
99
|
-
: [
|
|
100
|
-
{ scope: 'local', baseDir: resolve(projectDir, LOCAL_ROUTINES_DIR) },
|
|
101
|
-
{ scope: 'global', baseDir: ROUTINES_DIR }
|
|
102
|
-
];
|
|
103
|
-
|
|
104
|
-
for (const { scope, baseDir } of candidates) {
|
|
105
|
-
for (const def of ROUTINE_FILES) {
|
|
106
|
-
const routinePath = resolve(baseDir, `${name}${def.suffix}`);
|
|
107
|
-
if (existsSync(routinePath)) {
|
|
108
|
-
return { name, kind: def.kind, scope, path: routinePath };
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
117
|
-
// SCHEDULED ROUTINES
|
|
118
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
119
|
-
|
|
120
|
-
export interface ScheduledRoutineInfo extends RoutineInfo {
|
|
121
|
-
schedule: RoutineSchedule;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Get all routines that have a schedule defined.
|
|
126
|
-
* Only JSON routines can have schedules (bash scripts cannot).
|
|
127
|
-
* Searches both local (if projectDir provided) and global routines.
|
|
128
|
-
*/
|
|
129
|
-
export function getScheduledRoutines(projectDir?: string): ScheduledRoutineInfo[] {
|
|
130
|
-
const results: ScheduledRoutineInfo[] = [];
|
|
131
|
-
const seen = new Set<string>();
|
|
132
|
-
|
|
133
|
-
// Collect all structured routines (YAML and JSON - bash scripts cannot have schedules)
|
|
134
|
-
const allRoutines: RoutineInfo[] = [];
|
|
135
|
-
|
|
136
|
-
if (projectDir) {
|
|
137
|
-
allRoutines.push(...getLocalRoutines(projectDir).filter(r => r.kind === 'yaml' || r.kind === 'json'));
|
|
138
|
-
}
|
|
139
|
-
allRoutines.push(...getGlobalRoutines().filter(r => r.kind === 'yaml' || r.kind === 'json'));
|
|
140
|
-
|
|
141
|
-
for (const routine of allRoutines) {
|
|
142
|
-
// Skip if we've already processed a routine with this name (local takes precedence)
|
|
143
|
-
if (seen.has(routine.name)) continue;
|
|
144
|
-
seen.add(routine.name);
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
const content = readFileSync(routine.path, 'utf-8');
|
|
148
|
-
const data = routine.kind === 'yaml' ? parseYaml(content) : JSON.parse(content);
|
|
149
|
-
|
|
150
|
-
if (data.schedule && typeof data.schedule === 'object') {
|
|
151
|
-
// Check if schedule is enabled (defaults to true)
|
|
152
|
-
const enabled = (data.schedule as Record<string, unknown>).enabled !== false;
|
|
153
|
-
if (!enabled) continue;
|
|
154
|
-
|
|
155
|
-
try {
|
|
156
|
-
const schedule = validateScheduleConfig(data.schedule, routine.path);
|
|
157
|
-
results.push({
|
|
158
|
-
...routine,
|
|
159
|
-
schedule
|
|
160
|
-
});
|
|
161
|
-
} catch {
|
|
162
|
-
// Skip routines with invalid schedule configs (don't crash the scheduler)
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
} catch {
|
|
166
|
-
// Skip invalid JSON files
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return results;
|
|
171
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
/**
|
|
3
|
-
* Scheduler daemon entry point.
|
|
4
|
-
*
|
|
5
|
-
* This script runs as a background process and manages scheduled routine execution.
|
|
6
|
-
* It's spawned by `cli4ai scheduler start` with detached mode.
|
|
7
|
-
*
|
|
8
|
-
* Usage: npx tsx scheduler-daemon.ts [--project-dir <dir>]
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { Scheduler, writeDaemonPid, removeDaemonPid, appendSchedulerLog, SCHEDULER_LOG_FILE, ensureSchedulerDirs } from './scheduler.js';
|
|
12
|
-
import { createWriteStream } from 'fs';
|
|
13
|
-
import { ensureCli4aiHome } from './config.js';
|
|
14
|
-
|
|
15
|
-
// Parse arguments
|
|
16
|
-
const args = process.argv.slice(2);
|
|
17
|
-
let projectDir: string | undefined;
|
|
18
|
-
|
|
19
|
-
for (let i = 0; i < args.length; i++) {
|
|
20
|
-
if (args[i] === '--project-dir' && args[i + 1]) {
|
|
21
|
-
projectDir = args[i + 1];
|
|
22
|
-
i++;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Ensure directories exist
|
|
27
|
-
ensureCli4aiHome();
|
|
28
|
-
ensureSchedulerDirs();
|
|
29
|
-
|
|
30
|
-
// Redirect stdout/stderr to log file (when running detached)
|
|
31
|
-
if (process.env.CLI4AI_DAEMON === 'true') {
|
|
32
|
-
const logStream = createWriteStream(SCHEDULER_LOG_FILE, { flags: 'a' });
|
|
33
|
-
|
|
34
|
-
// Redirect console output
|
|
35
|
-
const originalLog = console.log;
|
|
36
|
-
const originalError = console.error;
|
|
37
|
-
|
|
38
|
-
console.log = (...args) => {
|
|
39
|
-
const message = args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ');
|
|
40
|
-
logStream.write(`[${new Date().toISOString()}] [STDOUT] ${message}\n`);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
console.error = (...args) => {
|
|
44
|
-
const message = args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ');
|
|
45
|
-
logStream.write(`[${new Date().toISOString()}] [STDERR] ${message}\n`);
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// Handle uncaught errors
|
|
49
|
-
process.on('uncaughtException', (err) => {
|
|
50
|
-
appendSchedulerLog('error', `Uncaught exception: ${err.message}\n${err.stack}`);
|
|
51
|
-
process.exit(1);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
process.on('unhandledRejection', (reason) => {
|
|
55
|
-
appendSchedulerLog('error', `Unhandled rejection: ${reason}`);
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Write PID file
|
|
60
|
-
writeDaemonPid(process.pid);
|
|
61
|
-
appendSchedulerLog('info', `Daemon started with PID ${process.pid}`);
|
|
62
|
-
|
|
63
|
-
// Create and start scheduler
|
|
64
|
-
const scheduler = new Scheduler({ projectDir });
|
|
65
|
-
|
|
66
|
-
// Handle shutdown signals
|
|
67
|
-
let shuttingDown = false;
|
|
68
|
-
|
|
69
|
-
async function shutdown(signal: string): Promise<void> {
|
|
70
|
-
if (shuttingDown) return;
|
|
71
|
-
shuttingDown = true;
|
|
72
|
-
|
|
73
|
-
appendSchedulerLog('info', `Received ${signal}, shutting down...`);
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
await scheduler.stop();
|
|
77
|
-
removeDaemonPid();
|
|
78
|
-
appendSchedulerLog('info', 'Daemon stopped gracefully');
|
|
79
|
-
} catch (err) {
|
|
80
|
-
appendSchedulerLog('error', `Error during shutdown: ${err instanceof Error ? err.message : String(err)}`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
process.exit(0);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
87
|
-
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
88
|
-
|
|
89
|
-
// Start the scheduler
|
|
90
|
-
scheduler.start().catch(err => {
|
|
91
|
-
appendSchedulerLog('error', `Failed to start scheduler: ${err instanceof Error ? err.message : String(err)}`);
|
|
92
|
-
removeDaemonPid();
|
|
93
|
-
process.exit(1);
|
|
94
|
-
});
|