cortask 0.2.37 → 0.2.39
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/dist/index.js +641 -76
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command11 } from "commander";
|
|
5
5
|
import {
|
|
6
|
-
loadConfig as
|
|
7
|
-
getDataDir as
|
|
8
|
-
WorkspaceManager as
|
|
9
|
-
SessionStore,
|
|
10
|
-
EncryptedCredentialStore,
|
|
11
|
-
getOrCreateSecret,
|
|
12
|
-
createProvider,
|
|
6
|
+
loadConfig as loadConfig6,
|
|
7
|
+
getDataDir as getDataDir11,
|
|
8
|
+
WorkspaceManager as WorkspaceManager3,
|
|
9
|
+
SessionStore as SessionStore2,
|
|
10
|
+
EncryptedCredentialStore as EncryptedCredentialStore2,
|
|
11
|
+
getOrCreateSecret as getOrCreateSecret2,
|
|
12
|
+
createProvider as createProvider2,
|
|
13
13
|
AgentRunner,
|
|
14
14
|
builtinTools,
|
|
15
15
|
createCronTool,
|
|
@@ -20,9 +20,12 @@ import {
|
|
|
20
20
|
CronService,
|
|
21
21
|
ArtifactStore,
|
|
22
22
|
installSkillFromGit,
|
|
23
|
-
removeSkill
|
|
23
|
+
removeSkill,
|
|
24
|
+
createSkill,
|
|
25
|
+
updateSkill,
|
|
26
|
+
readSkillFile
|
|
24
27
|
} from "@cortask/core";
|
|
25
|
-
import
|
|
28
|
+
import path11 from "path";
|
|
26
29
|
import readline from "readline";
|
|
27
30
|
|
|
28
31
|
// src/terminal/theme.ts
|
|
@@ -314,11 +317,501 @@ Dashboard URL: ${url}`));
|
|
|
314
317
|
console.log();
|
|
315
318
|
});
|
|
316
319
|
|
|
320
|
+
// src/commands/config.ts
|
|
321
|
+
import { Command as Command4 } from "commander";
|
|
322
|
+
import path4 from "path";
|
|
323
|
+
import { getDataDir as getDataDir4, loadConfig as loadConfig3, saveConfig } from "@cortask/core";
|
|
324
|
+
var configCommand = new Command4("config").description("View and update configuration");
|
|
325
|
+
configCommand.command("show").description("Show current configuration").action(async () => {
|
|
326
|
+
const dataDir = getDataDir4();
|
|
327
|
+
const config = await loadConfig3(path4.join(dataDir, "config.yaml"));
|
|
328
|
+
console.log(theme.heading("\n Agent"));
|
|
329
|
+
console.log(` Max turns: ${theme.info(String(config.agent.maxTurns))}`);
|
|
330
|
+
console.log(` Temperature: ${theme.info(String(config.agent.temperature))}`);
|
|
331
|
+
console.log(` Max tokens: ${theme.info(config.agent.maxTokens ? String(config.agent.maxTokens) : "default")}`);
|
|
332
|
+
console.log(theme.heading("\n Server"));
|
|
333
|
+
console.log(` Host: ${theme.info(config.server.host)}`);
|
|
334
|
+
console.log(` Port: ${theme.info(String(config.server.port))}`);
|
|
335
|
+
console.log(theme.heading("\n Spending"));
|
|
336
|
+
console.log(` Enabled: ${config.spending.enabled ? theme.success("yes") : theme.muted("no")}`);
|
|
337
|
+
console.log(` Max tokens: ${theme.info(config.spending.maxTokens ? String(config.spending.maxTokens) : "unlimited")}`);
|
|
338
|
+
console.log(` Max cost: ${theme.info(config.spending.maxCostUsd ? `$${config.spending.maxCostUsd}` : "unlimited")}`);
|
|
339
|
+
console.log(` Period: ${theme.info(config.spending.period)}`);
|
|
340
|
+
console.log(theme.heading("\n Memory"));
|
|
341
|
+
console.log(` Embedding provider: ${theme.info(config.memory.embeddingProvider)}`);
|
|
342
|
+
if (config.memory.embeddingModel) {
|
|
343
|
+
console.log(` Embedding model: ${theme.info(config.memory.embeddingModel)}`);
|
|
344
|
+
}
|
|
345
|
+
console.log(theme.heading("\n Skills"));
|
|
346
|
+
if (config.skills.dirs.length > 0) {
|
|
347
|
+
for (const dir of config.skills.dirs) {
|
|
348
|
+
console.log(` ${theme.muted("\u2022")} ${theme.info(dir)}`);
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
console.log(` ${theme.muted("No custom skill directories")}`);
|
|
352
|
+
}
|
|
353
|
+
console.log();
|
|
354
|
+
});
|
|
355
|
+
configCommand.command("set").description("Set a configuration value").argument("<key>", "Config key (e.g. agent.maxTurns, server.port, spending.enabled)").argument("<value>", "Config value").action(async (key, value) => {
|
|
356
|
+
const dataDir = getDataDir4();
|
|
357
|
+
const configPath = path4.join(dataDir, "config.yaml");
|
|
358
|
+
const config = await loadConfig3(configPath);
|
|
359
|
+
const parts = key.split(".");
|
|
360
|
+
if (parts.length !== 2) {
|
|
361
|
+
console.error(theme.error("\u2717 Key must be in format section.field (e.g. agent.maxTurns)"));
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
const [section, field] = parts;
|
|
365
|
+
const cfg = config;
|
|
366
|
+
if (!(section in cfg) || typeof cfg[section] !== "object") {
|
|
367
|
+
console.error(theme.error(`\u2717 Unknown config section: ${section}`));
|
|
368
|
+
console.error(theme.muted(" Valid sections: agent, server, spending, memory, skills"));
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
let parsed = value;
|
|
372
|
+
if (value === "true") parsed = true;
|
|
373
|
+
else if (value === "false") parsed = false;
|
|
374
|
+
else if (/^\d+$/.test(value)) parsed = parseInt(value, 10);
|
|
375
|
+
else if (/^\d+\.\d+$/.test(value)) parsed = parseFloat(value);
|
|
376
|
+
cfg[section][field] = parsed;
|
|
377
|
+
await saveConfig(configPath, config);
|
|
378
|
+
console.log(`${theme.success("\u2713")} Set ${theme.command(key)} = ${theme.info(String(parsed))}`);
|
|
379
|
+
});
|
|
380
|
+
configCommand.command("get").description("Get a configuration value").argument("<key>", "Config key (e.g. agent.maxTurns)").action(async (key) => {
|
|
381
|
+
const dataDir = getDataDir4();
|
|
382
|
+
const config = await loadConfig3(path4.join(dataDir, "config.yaml"));
|
|
383
|
+
const parts = key.split(".");
|
|
384
|
+
let current = config;
|
|
385
|
+
for (const part of parts) {
|
|
386
|
+
if (current == null || typeof current !== "object" || !(part in current)) {
|
|
387
|
+
console.error(theme.error(`\u2717 Unknown config key: ${key}`));
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
current = current[part];
|
|
391
|
+
}
|
|
392
|
+
console.log(typeof current === "object" ? JSON.stringify(current, null, 2) : String(current));
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// src/commands/providers.ts
|
|
396
|
+
import { Command as Command5 } from "commander";
|
|
397
|
+
import path5 from "path";
|
|
398
|
+
import {
|
|
399
|
+
getDataDir as getDataDir5,
|
|
400
|
+
loadConfig as loadConfig4,
|
|
401
|
+
saveConfig as saveConfig2,
|
|
402
|
+
EncryptedCredentialStore,
|
|
403
|
+
getOrCreateSecret,
|
|
404
|
+
createProvider,
|
|
405
|
+
AVAILABLE_PROVIDERS,
|
|
406
|
+
getModelDefinitions
|
|
407
|
+
} from "@cortask/core";
|
|
408
|
+
var providersCommand = new Command5("providers").description("Manage LLM providers");
|
|
409
|
+
providersCommand.command("list").description("List all available providers and their status").action(async () => {
|
|
410
|
+
const dataDir = getDataDir5();
|
|
411
|
+
const config = await loadConfig4(path5.join(dataDir, "config.yaml"));
|
|
412
|
+
const secret = await getOrCreateSecret(dataDir);
|
|
413
|
+
const credentialStore = new EncryptedCredentialStore(
|
|
414
|
+
path5.join(dataDir, "credentials.enc.json"),
|
|
415
|
+
secret
|
|
416
|
+
);
|
|
417
|
+
const defaultProvider = config.providers.default;
|
|
418
|
+
for (const p of AVAILABLE_PROVIDERS) {
|
|
419
|
+
const apiKey = await credentialStore.get(`provider.${p.id}.apiKey`);
|
|
420
|
+
const configured = p.requiresApiKey ? !!apiKey : true;
|
|
421
|
+
const isDefault = p.id === defaultProvider;
|
|
422
|
+
const icon = configured ? theme.success("\u2713") : theme.muted("\u25CB");
|
|
423
|
+
const defaultTag = isDefault ? theme.accent(" [default]") : "";
|
|
424
|
+
const providerConfig = config.providers[p.id];
|
|
425
|
+
const model = typeof providerConfig === "object" && providerConfig && "model" in providerConfig ? providerConfig.model : void 0;
|
|
426
|
+
const modelTag = model ? theme.muted(` (${model})`) : "";
|
|
427
|
+
console.log(` ${icon} ${theme.command(p.name)}${defaultTag}${modelTag}`);
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
providersCommand.command("default").description("Set the default provider and optionally model").argument("<provider>", "Provider ID (e.g. anthropic, openai, google)").argument("[model]", "Model ID").action(async (providerId, model) => {
|
|
431
|
+
const dataDir = getDataDir5();
|
|
432
|
+
const configPath = path5.join(dataDir, "config.yaml");
|
|
433
|
+
const config = await loadConfig4(configPath);
|
|
434
|
+
const valid = AVAILABLE_PROVIDERS.find((p) => p.id === providerId);
|
|
435
|
+
if (!valid) {
|
|
436
|
+
console.error(theme.error(`\u2717 Unknown provider: ${providerId}`));
|
|
437
|
+
console.error(theme.muted(" Valid providers: " + AVAILABLE_PROVIDERS.map((p) => p.id).join(", ")));
|
|
438
|
+
process.exit(1);
|
|
439
|
+
}
|
|
440
|
+
config.providers.default = providerId;
|
|
441
|
+
if (model) {
|
|
442
|
+
config.providers[providerId] = {
|
|
443
|
+
...config.providers[providerId],
|
|
444
|
+
model
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
await saveConfig2(configPath, config);
|
|
448
|
+
console.log(`${theme.success("\u2713")} Default provider set to ${theme.command(valid.name)}${model ? ` with model ${theme.info(model)}` : ""}`);
|
|
449
|
+
});
|
|
450
|
+
providersCommand.command("test").description("Test provider credentials with a simple API call").argument("<provider>", "Provider ID").action(async (providerId) => {
|
|
451
|
+
const dataDir = getDataDir5();
|
|
452
|
+
const secret = await getOrCreateSecret(dataDir);
|
|
453
|
+
const credentialStore = new EncryptedCredentialStore(
|
|
454
|
+
path5.join(dataDir, "credentials.enc.json"),
|
|
455
|
+
secret
|
|
456
|
+
);
|
|
457
|
+
const config = await loadConfig4(path5.join(dataDir, "config.yaml"));
|
|
458
|
+
const valid = AVAILABLE_PROVIDERS.find((p) => p.id === providerId);
|
|
459
|
+
if (!valid) {
|
|
460
|
+
console.error(theme.error(`\u2717 Unknown provider: ${providerId}`));
|
|
461
|
+
process.exit(1);
|
|
462
|
+
}
|
|
463
|
+
const apiKey = await credentialStore.get(`provider.${providerId}.apiKey`);
|
|
464
|
+
if (!apiKey && valid.requiresApiKey) {
|
|
465
|
+
console.error(theme.error(`\u2717 No API key set for ${providerId}`));
|
|
466
|
+
console.error(theme.muted(` Set it with: cortask credentials set provider.${providerId}.apiKey YOUR_KEY`));
|
|
467
|
+
process.exit(1);
|
|
468
|
+
}
|
|
469
|
+
console.log(theme.muted(`Testing ${valid.name}...`));
|
|
470
|
+
try {
|
|
471
|
+
const provider = createProvider(providerId, apiKey ?? "");
|
|
472
|
+
const providerConfig = config.providers[providerId];
|
|
473
|
+
const model = typeof providerConfig === "object" && providerConfig && "model" in providerConfig ? providerConfig.model : void 0;
|
|
474
|
+
const defaultModel = getModelDefinitions(providerId)[0]?.id;
|
|
475
|
+
const result = await provider.generateText({
|
|
476
|
+
model: model ?? defaultModel ?? providerId,
|
|
477
|
+
messages: [{ role: "user", content: [{ type: "text", text: "Say hi in exactly one word." }] }],
|
|
478
|
+
maxTokens: 20
|
|
479
|
+
});
|
|
480
|
+
console.log(`${theme.success("\u2713")} Provider responded successfully`);
|
|
481
|
+
console.log(` ${theme.muted("Response:")} ${result.content}`);
|
|
482
|
+
if (result.usage) {
|
|
483
|
+
console.log(` ${theme.muted("Tokens:")} ${result.usage.inputTokens} in / ${result.usage.outputTokens} out`);
|
|
484
|
+
}
|
|
485
|
+
} catch (err) {
|
|
486
|
+
console.error(`${theme.error("\u2717")} Provider test failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// src/commands/sessions.ts
|
|
492
|
+
import { Command as Command6 } from "commander";
|
|
493
|
+
import path6 from "path";
|
|
494
|
+
import { getDataDir as getDataDir6, WorkspaceManager as WorkspaceManager2, SessionStore } from "@cortask/core";
|
|
495
|
+
var sessionsCommand = new Command6("sessions").description("Manage chat sessions");
|
|
496
|
+
sessionsCommand.command("list").description("List sessions for a workspace").requiredOption("-w, --workspace <id>", "Workspace ID").action(async (opts) => {
|
|
497
|
+
const dataDir = getDataDir6();
|
|
498
|
+
const dbPath = path6.join(dataDir, "cortask.db");
|
|
499
|
+
const wm = new WorkspaceManager2(dbPath);
|
|
500
|
+
const workspace = await wm.get(opts.workspace);
|
|
501
|
+
if (!workspace) {
|
|
502
|
+
console.error(theme.error(`\u2717 Workspace not found: ${opts.workspace}`));
|
|
503
|
+
wm.close();
|
|
504
|
+
process.exit(1);
|
|
505
|
+
}
|
|
506
|
+
const sessionStore = new SessionStore(wm.getSessionDbPath(workspace.rootPath));
|
|
507
|
+
const sessions = sessionStore.listSessions();
|
|
508
|
+
if (sessions.length === 0) {
|
|
509
|
+
console.log(theme.muted("No sessions found."));
|
|
510
|
+
} else {
|
|
511
|
+
for (const s of sessions) {
|
|
512
|
+
const date = new Date(s.updatedAt).toLocaleString();
|
|
513
|
+
console.log(` ${theme.muted("\u2022")} ${theme.command(s.title || "Untitled")} ${theme.muted(`(${s.id})`)}`);
|
|
514
|
+
console.log(` ${theme.muted("Updated:")} ${date}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
wm.close();
|
|
518
|
+
});
|
|
519
|
+
sessionsCommand.command("show").description("Show session messages").argument("<id>", "Session ID").requiredOption("-w, --workspace <id>", "Workspace ID").option("-n, --limit <count>", "Number of messages to show", "20").action(async (id, opts) => {
|
|
520
|
+
const dataDir = getDataDir6();
|
|
521
|
+
const dbPath = path6.join(dataDir, "cortask.db");
|
|
522
|
+
const wm = new WorkspaceManager2(dbPath);
|
|
523
|
+
const workspace = await wm.get(opts.workspace);
|
|
524
|
+
if (!workspace) {
|
|
525
|
+
console.error(theme.error(`\u2717 Workspace not found: ${opts.workspace}`));
|
|
526
|
+
wm.close();
|
|
527
|
+
process.exit(1);
|
|
528
|
+
}
|
|
529
|
+
const sessionStore = new SessionStore(wm.getSessionDbPath(workspace.rootPath));
|
|
530
|
+
const session = sessionStore.getSession(id);
|
|
531
|
+
if (!session) {
|
|
532
|
+
console.error(theme.error(`\u2717 Session not found: ${id}`));
|
|
533
|
+
wm.close();
|
|
534
|
+
process.exit(1);
|
|
535
|
+
}
|
|
536
|
+
console.log(theme.heading(`
|
|
537
|
+
${session.title || "Untitled Session"}`));
|
|
538
|
+
console.log(theme.muted(` ID: ${session.id}`));
|
|
539
|
+
console.log(theme.muted(` Created: ${new Date(session.createdAt).toLocaleString()}`));
|
|
540
|
+
console.log();
|
|
541
|
+
const limit = parseInt(opts.limit, 10);
|
|
542
|
+
const messages = session.messages.slice(-limit);
|
|
543
|
+
for (const msg of messages) {
|
|
544
|
+
const role = msg.role === "user" ? theme.accent("you>") : theme.accentBright("cortask>");
|
|
545
|
+
let text = "";
|
|
546
|
+
if (typeof msg.content === "string") {
|
|
547
|
+
text = msg.content;
|
|
548
|
+
} else if (Array.isArray(msg.content)) {
|
|
549
|
+
text = msg.content.filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
|
|
550
|
+
}
|
|
551
|
+
if (text) {
|
|
552
|
+
console.log(`${role} ${text.slice(0, 500)}${text.length > 500 ? "..." : ""}`);
|
|
553
|
+
console.log();
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
wm.close();
|
|
557
|
+
});
|
|
558
|
+
sessionsCommand.command("delete").description("Delete a session").argument("<id>", "Session ID").requiredOption("-w, --workspace <id>", "Workspace ID").action(async (id, opts) => {
|
|
559
|
+
const dataDir = getDataDir6();
|
|
560
|
+
const dbPath = path6.join(dataDir, "cortask.db");
|
|
561
|
+
const wm = new WorkspaceManager2(dbPath);
|
|
562
|
+
const workspace = await wm.get(opts.workspace);
|
|
563
|
+
if (!workspace) {
|
|
564
|
+
console.error(theme.error(`\u2717 Workspace not found: ${opts.workspace}`));
|
|
565
|
+
wm.close();
|
|
566
|
+
process.exit(1);
|
|
567
|
+
}
|
|
568
|
+
const sessionStore = new SessionStore(wm.getSessionDbPath(workspace.rootPath));
|
|
569
|
+
sessionStore.deleteSession(id);
|
|
570
|
+
console.log(`${theme.success("\u2713")} Deleted session ${theme.muted(id)}`);
|
|
571
|
+
wm.close();
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
// src/commands/usage.ts
|
|
575
|
+
import { Command as Command7 } from "commander";
|
|
576
|
+
import path7 from "path";
|
|
577
|
+
import { getDataDir as getDataDir7, UsageStore, ModelStore } from "@cortask/core";
|
|
578
|
+
var usageCommand = new Command7("usage").description("View token usage and costs");
|
|
579
|
+
usageCommand.command("summary").description("Show usage summary for a period").option("-p, --period <period>", "Period: daily, weekly, monthly", "monthly").action(async (opts) => {
|
|
580
|
+
const dataDir = getDataDir7();
|
|
581
|
+
const dbPath = path7.join(dataDir, "cortask.db");
|
|
582
|
+
const modelStore = new ModelStore(dbPath);
|
|
583
|
+
const usageStore = new UsageStore(dbPath, modelStore);
|
|
584
|
+
const period = opts.period;
|
|
585
|
+
const summary = usageStore.getSummary(period);
|
|
586
|
+
console.log(theme.heading(`
|
|
587
|
+
Usage (${period})`));
|
|
588
|
+
console.log(` Requests: ${theme.info(String(summary.recordCount))}`);
|
|
589
|
+
console.log(` Input tokens: ${theme.info(summary.totalInputTokens.toLocaleString())}`);
|
|
590
|
+
console.log(` Output tokens: ${theme.info(summary.totalOutputTokens.toLocaleString())}`);
|
|
591
|
+
console.log(` Total tokens: ${theme.info(summary.totalTokens.toLocaleString())}`);
|
|
592
|
+
console.log(` Total cost: ${theme.info(`$${summary.totalCostUsd.toFixed(4)}`)}`);
|
|
593
|
+
console.log();
|
|
594
|
+
});
|
|
595
|
+
usageCommand.command("history").description("Show daily usage history").option("-d, --days <days>", "Number of days", "14").action(async (opts) => {
|
|
596
|
+
const dataDir = getDataDir7();
|
|
597
|
+
const dbPath = path7.join(dataDir, "cortask.db");
|
|
598
|
+
const modelStore = new ModelStore(dbPath);
|
|
599
|
+
const usageStore = new UsageStore(dbPath, modelStore);
|
|
600
|
+
const days = parseInt(opts.days, 10);
|
|
601
|
+
const history = usageStore.getHistory(days);
|
|
602
|
+
if (history.length === 0) {
|
|
603
|
+
console.log(theme.muted("No usage data found."));
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
console.log(theme.heading(`
|
|
607
|
+
Usage History (last ${days} days)
|
|
608
|
+
`));
|
|
609
|
+
console.log(` ${theme.muted("Date".padEnd(12))} ${theme.muted("Tokens".padStart(12))} ${theme.muted("Cost".padStart(10))}`);
|
|
610
|
+
console.log(` ${theme.muted("\u2500".repeat(34))}`);
|
|
611
|
+
for (const row of history) {
|
|
612
|
+
const date = row.date.padEnd(12);
|
|
613
|
+
const tokens = row.tokens.toLocaleString().padStart(12);
|
|
614
|
+
const cost = `$${row.costUsd.toFixed(4)}`.padStart(10);
|
|
615
|
+
console.log(` ${theme.info(date)} ${tokens} ${theme.muted(cost)}`);
|
|
616
|
+
}
|
|
617
|
+
console.log();
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
// src/commands/models.ts
|
|
621
|
+
import { Command as Command8 } from "commander";
|
|
622
|
+
import path8 from "path";
|
|
623
|
+
import { getDataDir as getDataDir8, ModelStore as ModelStore2, getModelDefinitions as getModelDefinitions2 } from "@cortask/core";
|
|
624
|
+
var modelsCommand = new Command8("models").description("Manage available models");
|
|
625
|
+
modelsCommand.command("available").description("List available models for a provider").argument("<provider>", "Provider ID (e.g. anthropic, openai, google)").action(async (providerId) => {
|
|
626
|
+
const models = getModelDefinitions2(providerId);
|
|
627
|
+
if (models.length === 0) {
|
|
628
|
+
console.log(theme.muted(`No hardcoded models for provider "${providerId}".`));
|
|
629
|
+
console.log(theme.muted("This provider may fetch models dynamically from its API."));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
console.log(theme.heading(`
|
|
633
|
+
Models for ${providerId}
|
|
634
|
+
`));
|
|
635
|
+
for (const m of models) {
|
|
636
|
+
const pricing = m.inputPricePer1m != null ? theme.muted(` ($${m.inputPricePer1m}/$${m.outputPricePer1m} per 1M)`) : "";
|
|
637
|
+
console.log(` ${theme.muted("\u2022")} ${theme.command(m.id)} ${theme.info(m.name)}${pricing}`);
|
|
638
|
+
}
|
|
639
|
+
console.log();
|
|
640
|
+
});
|
|
641
|
+
modelsCommand.command("list").description("List enabled models").option("-p, --provider <provider>", "Filter by provider").action(async (opts) => {
|
|
642
|
+
const dataDir = getDataDir8();
|
|
643
|
+
const dbPath = path8.join(dataDir, "cortask.db");
|
|
644
|
+
const modelStore = new ModelStore2(dbPath);
|
|
645
|
+
const models = modelStore.list(opts.provider);
|
|
646
|
+
if (models.length === 0) {
|
|
647
|
+
console.log(theme.muted("No models enabled."));
|
|
648
|
+
console.log(theme.muted("Enable models with: cortask models enable <provider> <model-id> <label> <input-price> <output-price>"));
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
console.log(theme.heading("\n Enabled Models\n"));
|
|
652
|
+
for (const m of models) {
|
|
653
|
+
console.log(` ${theme.success("\u2713")} ${theme.command(m.label)} ${theme.muted(`(${m.provider}/${m.modelId})`)}`);
|
|
654
|
+
console.log(` ${theme.muted(`$${m.inputPricePer1m}/$${m.outputPricePer1m} per 1M tokens`)}`);
|
|
655
|
+
}
|
|
656
|
+
console.log();
|
|
657
|
+
});
|
|
658
|
+
modelsCommand.command("enable").description("Enable a model").argument("<provider>", "Provider ID").argument("<model-id>", "Model ID").argument("<label>", "Display label").argument("<input-price>", "Input price per 1M tokens (USD)").argument("<output-price>", "Output price per 1M tokens (USD)").action(async (provider, modelId, label, inputPrice, outputPrice) => {
|
|
659
|
+
const dataDir = getDataDir8();
|
|
660
|
+
const dbPath = path8.join(dataDir, "cortask.db");
|
|
661
|
+
const modelStore = new ModelStore2(dbPath);
|
|
662
|
+
const model = modelStore.enable(
|
|
663
|
+
provider,
|
|
664
|
+
modelId,
|
|
665
|
+
label,
|
|
666
|
+
parseFloat(inputPrice),
|
|
667
|
+
parseFloat(outputPrice)
|
|
668
|
+
);
|
|
669
|
+
console.log(`${theme.success("\u2713")} Enabled model ${theme.command(model.label)} ${theme.muted(`(${provider}/${modelId})`)}`);
|
|
670
|
+
});
|
|
671
|
+
modelsCommand.command("disable").description("Disable a model").argument("<provider>", "Provider ID").argument("<model-id>", "Model ID").action(async (provider, modelId) => {
|
|
672
|
+
const dataDir = getDataDir8();
|
|
673
|
+
const dbPath = path8.join(dataDir, "cortask.db");
|
|
674
|
+
const modelStore = new ModelStore2(dbPath);
|
|
675
|
+
modelStore.disable(provider, modelId);
|
|
676
|
+
console.log(`${theme.success("\u2713")} Disabled model ${theme.muted(`${provider}/${modelId}`)}`);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
// src/commands/templates.ts
|
|
680
|
+
import { Command as Command9 } from "commander";
|
|
681
|
+
import path9 from "path";
|
|
682
|
+
import { getDataDir as getDataDir9, TemplateStore } from "@cortask/core";
|
|
683
|
+
var templatesCommand = new Command9("templates").description("Manage prompt templates");
|
|
684
|
+
templatesCommand.command("list").description("List all templates").action(async () => {
|
|
685
|
+
const dataDir = getDataDir9();
|
|
686
|
+
const dbPath = path9.join(dataDir, "cortask.db");
|
|
687
|
+
const store = new TemplateStore(dbPath);
|
|
688
|
+
const templates = store.list();
|
|
689
|
+
if (templates.length === 0) {
|
|
690
|
+
console.log(theme.muted("No templates found."));
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
let currentCategory = "";
|
|
694
|
+
for (const t of templates) {
|
|
695
|
+
if (t.category !== currentCategory) {
|
|
696
|
+
currentCategory = t.category;
|
|
697
|
+
console.log(theme.heading(`
|
|
698
|
+
${currentCategory}`));
|
|
699
|
+
}
|
|
700
|
+
console.log(` ${theme.muted("\u2022")} ${theme.command(t.name)} ${theme.muted(`(${t.id.slice(0, 8)}...)`)}`);
|
|
701
|
+
console.log(` ${theme.muted(t.content.slice(0, 80).replace(/\n/g, " "))}${t.content.length > 80 ? "..." : ""}`);
|
|
702
|
+
}
|
|
703
|
+
console.log();
|
|
704
|
+
});
|
|
705
|
+
templatesCommand.command("create").description("Create a new template").requiredOption("-n, --name <name>", "Template name").requiredOption("-c, --content <content>", "Template content").option("--category <category>", "Category", "General").action(async (opts) => {
|
|
706
|
+
const dataDir = getDataDir9();
|
|
707
|
+
const dbPath = path9.join(dataDir, "cortask.db");
|
|
708
|
+
const store = new TemplateStore(dbPath);
|
|
709
|
+
const template = store.create(opts.name, opts.content, opts.category);
|
|
710
|
+
console.log(`${theme.success("\u2713")} Created template ${theme.command(template.name)} ${theme.muted(`(${template.id.slice(0, 8)}...)`)}`);
|
|
711
|
+
});
|
|
712
|
+
templatesCommand.command("update").description("Update a template").argument("<id>", "Template ID").option("-n, --name <name>", "New name").option("-c, --content <content>", "New content").option("--category <category>", "New category").action(async (id, opts) => {
|
|
713
|
+
const dataDir = getDataDir9();
|
|
714
|
+
const dbPath = path9.join(dataDir, "cortask.db");
|
|
715
|
+
const store = new TemplateStore(dbPath);
|
|
716
|
+
const updates = {};
|
|
717
|
+
if (opts.name) updates.name = opts.name;
|
|
718
|
+
if (opts.content) updates.content = opts.content;
|
|
719
|
+
if (opts.category) updates.category = opts.category;
|
|
720
|
+
if (Object.keys(updates).length === 0) {
|
|
721
|
+
console.error(theme.error("\u2717 No fields to update. Use --name, --content, or --category."));
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
724
|
+
const result = store.update(id, updates);
|
|
725
|
+
if (!result) {
|
|
726
|
+
console.error(theme.error(`\u2717 Template not found: ${id}`));
|
|
727
|
+
process.exit(1);
|
|
728
|
+
}
|
|
729
|
+
console.log(`${theme.success("\u2713")} Updated template ${theme.command(result.name)}`);
|
|
730
|
+
});
|
|
731
|
+
templatesCommand.command("delete").description("Delete a template").argument("<id>", "Template ID").action(async (id) => {
|
|
732
|
+
const dataDir = getDataDir9();
|
|
733
|
+
const dbPath = path9.join(dataDir, "cortask.db");
|
|
734
|
+
const store = new TemplateStore(dbPath);
|
|
735
|
+
store.delete(id);
|
|
736
|
+
console.log(`${theme.success("\u2713")} Deleted template ${theme.muted(id)}`);
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
// src/commands/channels.ts
|
|
740
|
+
import { Command as Command10 } from "commander";
|
|
741
|
+
import path10 from "path";
|
|
742
|
+
import { getDataDir as getDataDir10, loadConfig as loadConfig5 } from "@cortask/core";
|
|
743
|
+
async function fetchGateway(host, port, path12, method = "GET") {
|
|
744
|
+
const res = await fetch(`http://${host}:${port}${path12}`, { method });
|
|
745
|
+
if (!res.ok) {
|
|
746
|
+
const body = await res.text();
|
|
747
|
+
throw new Error(`Gateway returned ${res.status}: ${body}`);
|
|
748
|
+
}
|
|
749
|
+
return res.json();
|
|
750
|
+
}
|
|
751
|
+
var channelsCommand = new Command10("channels").description("Manage messaging channels (requires running gateway)");
|
|
752
|
+
channelsCommand.command("list").description("List channel statuses").action(async () => {
|
|
753
|
+
const dataDir = getDataDir10();
|
|
754
|
+
const config = await loadConfig5(path10.join(dataDir, "config.yaml"));
|
|
755
|
+
try {
|
|
756
|
+
const channels = await fetchGateway(
|
|
757
|
+
config.server.host,
|
|
758
|
+
config.server.port,
|
|
759
|
+
"/api/channels"
|
|
760
|
+
);
|
|
761
|
+
if (channels.length === 0) {
|
|
762
|
+
console.log(theme.muted("No channels available."));
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
for (const ch of channels) {
|
|
766
|
+
const statusIcon = ch.running ? theme.success("\u25CF") : theme.muted("\u25CB");
|
|
767
|
+
const status = ch.running ? theme.success("running") : theme.muted("stopped");
|
|
768
|
+
const auth = ch.authenticated != null ? ch.authenticated ? theme.success(" (authenticated)") : theme.warn(" (not authenticated)") : "";
|
|
769
|
+
console.log(` ${statusIcon} ${theme.command(ch.name)} ${status}${auth}`);
|
|
770
|
+
}
|
|
771
|
+
} catch (err) {
|
|
772
|
+
console.error(theme.error(`\u2717 ${err instanceof Error ? err.message : String(err)}`));
|
|
773
|
+
console.error(theme.muted(" Is the gateway running? Start it with: cortask serve"));
|
|
774
|
+
process.exit(1);
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
channelsCommand.command("start").description("Start a channel").argument("<channel>", "Channel ID (telegram, discord, whatsapp)").action(async (channelId) => {
|
|
778
|
+
const dataDir = getDataDir10();
|
|
779
|
+
const config = await loadConfig5(path10.join(dataDir, "config.yaml"));
|
|
780
|
+
try {
|
|
781
|
+
await fetchGateway(
|
|
782
|
+
config.server.host,
|
|
783
|
+
config.server.port,
|
|
784
|
+
`/api/channels/${channelId}/start`,
|
|
785
|
+
"POST"
|
|
786
|
+
);
|
|
787
|
+
console.log(`${theme.success("\u2713")} Channel ${theme.command(channelId)} started`);
|
|
788
|
+
} catch (err) {
|
|
789
|
+
console.error(theme.error(`\u2717 ${err instanceof Error ? err.message : String(err)}`));
|
|
790
|
+
process.exit(1);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
channelsCommand.command("stop").description("Stop a channel").argument("<channel>", "Channel ID (telegram, discord, whatsapp)").action(async (channelId) => {
|
|
794
|
+
const dataDir = getDataDir10();
|
|
795
|
+
const config = await loadConfig5(path10.join(dataDir, "config.yaml"));
|
|
796
|
+
try {
|
|
797
|
+
await fetchGateway(
|
|
798
|
+
config.server.host,
|
|
799
|
+
config.server.port,
|
|
800
|
+
`/api/channels/${channelId}/stop`,
|
|
801
|
+
"POST"
|
|
802
|
+
);
|
|
803
|
+
console.log(`${theme.success("\u2713")} Channel ${theme.command(channelId)} stopped`);
|
|
804
|
+
} catch (err) {
|
|
805
|
+
console.error(theme.error(`\u2717 ${err instanceof Error ? err.message : String(err)}`));
|
|
806
|
+
process.exit(1);
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
|
|
317
810
|
// src/index.ts
|
|
318
811
|
import { createRequire } from "module";
|
|
319
812
|
var require2 = createRequire(import.meta.url);
|
|
320
813
|
var VERSION = require2("../package.json").version;
|
|
321
|
-
var program = new
|
|
814
|
+
var program = new Command11();
|
|
322
815
|
program.name("cortask").description("Cortask \u2014 Local AI agent with skills, workspaces, and cron").version(VERSION);
|
|
323
816
|
configureHelp(program, VERSION);
|
|
324
817
|
program.command("serve").description("Start the gateway server").option("-p, --port <port>", "Port to listen on", "3777").option("--host <host>", "Host to bind to", "127.0.0.1").action(async (opts) => {
|
|
@@ -331,35 +824,35 @@ program.command("serve").description("Start the gateway server").option("-p, --p
|
|
|
331
824
|
});
|
|
332
825
|
program.command("chat").description("Interactive chat REPL").option("-w, --workspace <path>", "Workspace directory path", ".").action(async (opts) => {
|
|
333
826
|
emitBanner(VERSION);
|
|
334
|
-
const dataDir =
|
|
335
|
-
const configPath =
|
|
336
|
-
const config = await
|
|
337
|
-
const dbPath =
|
|
338
|
-
const workspaceManager = new
|
|
339
|
-
const secret = await
|
|
340
|
-
const credentialStore = new
|
|
341
|
-
|
|
827
|
+
const dataDir = getDataDir11();
|
|
828
|
+
const configPath = path11.join(dataDir, "config.yaml");
|
|
829
|
+
const config = await loadConfig6(configPath);
|
|
830
|
+
const dbPath = path11.join(dataDir, "cortask.db");
|
|
831
|
+
const workspaceManager = new WorkspaceManager3(dbPath);
|
|
832
|
+
const secret = await getOrCreateSecret2(dataDir);
|
|
833
|
+
const credentialStore = new EncryptedCredentialStore2(
|
|
834
|
+
path11.join(dataDir, "credentials.enc.json"),
|
|
342
835
|
secret
|
|
343
836
|
);
|
|
344
837
|
const cronService = new CronService(dbPath);
|
|
345
838
|
const artifactStore = new ArtifactStore();
|
|
346
|
-
const workspacePath =
|
|
839
|
+
const workspacePath = path11.resolve(opts.workspace);
|
|
347
840
|
const workspaces = await workspaceManager.list();
|
|
348
841
|
let workspace = workspaces.find((w) => w.rootPath === workspacePath);
|
|
349
842
|
if (!workspace) {
|
|
350
843
|
workspace = await workspaceManager.create(
|
|
351
|
-
|
|
844
|
+
path11.basename(workspacePath),
|
|
352
845
|
workspacePath
|
|
353
846
|
);
|
|
354
847
|
console.log(
|
|
355
848
|
`${theme.success("\u2713")} Registered workspace: ${theme.command(workspace.name)}`
|
|
356
849
|
);
|
|
357
850
|
}
|
|
358
|
-
const sessionStore = new
|
|
851
|
+
const sessionStore = new SessionStore2(
|
|
359
852
|
workspaceManager.getSessionDbPath(workspacePath)
|
|
360
853
|
);
|
|
361
|
-
const bundledSkillsDir =
|
|
362
|
-
const userSkillsDir =
|
|
854
|
+
const bundledSkillsDir = path11.resolve("skills");
|
|
855
|
+
const userSkillsDir = path11.join(dataDir, "skills");
|
|
363
856
|
const allSkills = await loadSkills(
|
|
364
857
|
bundledSkillsDir,
|
|
365
858
|
userSkillsDir,
|
|
@@ -392,7 +885,7 @@ program.command("chat").description("Interactive chat REPL").option("-w, --works
|
|
|
392
885
|
console.log();
|
|
393
886
|
process.exit(1);
|
|
394
887
|
}
|
|
395
|
-
const provider =
|
|
888
|
+
const provider = createProvider2(providerId, apiKey);
|
|
396
889
|
const model = providerConfig?.model ?? "claude-sonnet-4-5-20250929";
|
|
397
890
|
const sessionId = `cli_${Date.now()}`;
|
|
398
891
|
const runner = new AgentRunner({
|
|
@@ -494,28 +987,28 @@ ${theme.muted("[")}${theme.warn("tool")}${theme.muted(":")} ${theme.command(even
|
|
|
494
987
|
});
|
|
495
988
|
});
|
|
496
989
|
program.command("run").description("Run a one-shot prompt").argument("<prompt>", "The prompt to execute").option("-w, --workspace <path>", "Workspace directory path", ".").action(async (prompt, opts) => {
|
|
497
|
-
const dataDir =
|
|
498
|
-
const configPath =
|
|
499
|
-
const config = await
|
|
500
|
-
const dbPath =
|
|
501
|
-
const workspaceManager = new
|
|
502
|
-
const secret = await
|
|
503
|
-
const credentialStore = new
|
|
504
|
-
|
|
990
|
+
const dataDir = getDataDir11();
|
|
991
|
+
const configPath = path11.join(dataDir, "config.yaml");
|
|
992
|
+
const config = await loadConfig6(configPath);
|
|
993
|
+
const dbPath = path11.join(dataDir, "cortask.db");
|
|
994
|
+
const workspaceManager = new WorkspaceManager3(dbPath);
|
|
995
|
+
const secret = await getOrCreateSecret2(dataDir);
|
|
996
|
+
const credentialStore = new EncryptedCredentialStore2(
|
|
997
|
+
path11.join(dataDir, "credentials.enc.json"),
|
|
505
998
|
secret
|
|
506
999
|
);
|
|
507
1000
|
const cronService = new CronService(dbPath);
|
|
508
1001
|
const artifactStore = new ArtifactStore();
|
|
509
|
-
const workspacePath =
|
|
1002
|
+
const workspacePath = path11.resolve(opts.workspace);
|
|
510
1003
|
const workspaces = await workspaceManager.list();
|
|
511
1004
|
let workspace = workspaces.find((w) => w.rootPath === workspacePath);
|
|
512
1005
|
if (!workspace) {
|
|
513
1006
|
workspace = await workspaceManager.create(
|
|
514
|
-
|
|
1007
|
+
path11.basename(workspacePath),
|
|
515
1008
|
workspacePath
|
|
516
1009
|
);
|
|
517
1010
|
}
|
|
518
|
-
const sessionStore = new
|
|
1011
|
+
const sessionStore = new SessionStore2(
|
|
519
1012
|
workspaceManager.getSessionDbPath(workspacePath)
|
|
520
1013
|
);
|
|
521
1014
|
const providerId = config.providers.default || "anthropic";
|
|
@@ -531,7 +1024,7 @@ program.command("run").description("Run a one-shot prompt").argument("<prompt>",
|
|
|
531
1024
|
);
|
|
532
1025
|
process.exit(1);
|
|
533
1026
|
}
|
|
534
|
-
const provider =
|
|
1027
|
+
const provider = createProvider2(providerId, apiKey);
|
|
535
1028
|
const providerConfig = config.providers[providerId];
|
|
536
1029
|
const model = providerConfig?.model ?? "claude-sonnet-4-5-20250929";
|
|
537
1030
|
const runner = new AgentRunner({
|
|
@@ -562,11 +1055,18 @@ program.command("run").description("Run a one-shot prompt").argument("<prompt>",
|
|
|
562
1055
|
program.addCommand(setupCommand);
|
|
563
1056
|
program.addCommand(statusCommand);
|
|
564
1057
|
program.addCommand(dashboardCommand);
|
|
1058
|
+
program.addCommand(configCommand);
|
|
1059
|
+
program.addCommand(providersCommand);
|
|
1060
|
+
program.addCommand(sessionsCommand);
|
|
1061
|
+
program.addCommand(usageCommand);
|
|
1062
|
+
program.addCommand(modelsCommand);
|
|
1063
|
+
program.addCommand(templatesCommand);
|
|
1064
|
+
program.addCommand(channelsCommand);
|
|
565
1065
|
var ws = program.command("workspaces").description("Manage workspaces");
|
|
566
1066
|
ws.command("list").action(async () => {
|
|
567
|
-
const dataDir =
|
|
568
|
-
const dbPath =
|
|
569
|
-
const wm = new
|
|
1067
|
+
const dataDir = getDataDir11();
|
|
1068
|
+
const dbPath = path11.join(dataDir, "cortask.db");
|
|
1069
|
+
const wm = new WorkspaceManager3(dbPath);
|
|
570
1070
|
const list = await wm.list();
|
|
571
1071
|
if (list.length === 0) {
|
|
572
1072
|
console.log(theme.muted("No workspaces registered."));
|
|
@@ -580,9 +1080,9 @@ ws.command("list").action(async () => {
|
|
|
580
1080
|
wm.close();
|
|
581
1081
|
});
|
|
582
1082
|
ws.command("add").argument("<name>", "Workspace name").argument("<path>", "Workspace directory path").action(async (name, dirPath) => {
|
|
583
|
-
const dataDir =
|
|
584
|
-
const dbPath =
|
|
585
|
-
const wm = new
|
|
1083
|
+
const dataDir = getDataDir11();
|
|
1084
|
+
const dbPath = path11.join(dataDir, "cortask.db");
|
|
1085
|
+
const wm = new WorkspaceManager3(dbPath);
|
|
586
1086
|
const workspace = await wm.create(name, dirPath);
|
|
587
1087
|
console.log(
|
|
588
1088
|
`${theme.success("\u2713")} Created workspace ${theme.command(workspace.name)} ${theme.muted(`(${workspace.id})`)}`
|
|
@@ -591,19 +1091,19 @@ ws.command("add").argument("<name>", "Workspace name").argument("<path>", "Works
|
|
|
591
1091
|
wm.close();
|
|
592
1092
|
});
|
|
593
1093
|
ws.command("remove").argument("<id>", "Workspace ID").action(async (id) => {
|
|
594
|
-
const dataDir =
|
|
595
|
-
const dbPath =
|
|
596
|
-
const wm = new
|
|
1094
|
+
const dataDir = getDataDir11();
|
|
1095
|
+
const dbPath = path11.join(dataDir, "cortask.db");
|
|
1096
|
+
const wm = new WorkspaceManager3(dbPath);
|
|
597
1097
|
await wm.delete(id);
|
|
598
1098
|
console.log(`${theme.success("\u2713")} Removed workspace ${theme.muted(id)}`);
|
|
599
1099
|
wm.close();
|
|
600
1100
|
});
|
|
601
1101
|
var creds = program.command("credentials").description("Manage credentials");
|
|
602
1102
|
creds.command("list").action(async () => {
|
|
603
|
-
const dataDir =
|
|
604
|
-
const secret = await
|
|
605
|
-
const store = new
|
|
606
|
-
|
|
1103
|
+
const dataDir = getDataDir11();
|
|
1104
|
+
const secret = await getOrCreateSecret2(dataDir);
|
|
1105
|
+
const store = new EncryptedCredentialStore2(
|
|
1106
|
+
path11.join(dataDir, "credentials.enc.json"),
|
|
607
1107
|
secret
|
|
608
1108
|
);
|
|
609
1109
|
const keys = await store.list();
|
|
@@ -616,20 +1116,20 @@ creds.command("list").action(async () => {
|
|
|
616
1116
|
}
|
|
617
1117
|
});
|
|
618
1118
|
creds.command("set").argument("<key>", "Credential key (e.g. provider.anthropic.apiKey)").argument("<value>", "Credential value").action(async (key, value) => {
|
|
619
|
-
const dataDir =
|
|
620
|
-
const secret = await
|
|
621
|
-
const store = new
|
|
622
|
-
|
|
1119
|
+
const dataDir = getDataDir11();
|
|
1120
|
+
const secret = await getOrCreateSecret2(dataDir);
|
|
1121
|
+
const store = new EncryptedCredentialStore2(
|
|
1122
|
+
path11.join(dataDir, "credentials.enc.json"),
|
|
623
1123
|
secret
|
|
624
1124
|
);
|
|
625
1125
|
await store.set(key, value);
|
|
626
1126
|
console.log(`${theme.success("\u2713")} Credential ${theme.command(key)} saved.`);
|
|
627
1127
|
});
|
|
628
1128
|
creds.command("remove").argument("<key>", "Credential key").action(async (key) => {
|
|
629
|
-
const dataDir =
|
|
630
|
-
const secret = await
|
|
631
|
-
const store = new
|
|
632
|
-
|
|
1129
|
+
const dataDir = getDataDir11();
|
|
1130
|
+
const secret = await getOrCreateSecret2(dataDir);
|
|
1131
|
+
const store = new EncryptedCredentialStore2(
|
|
1132
|
+
path11.join(dataDir, "credentials.enc.json"),
|
|
633
1133
|
secret
|
|
634
1134
|
);
|
|
635
1135
|
await store.delete(key);
|
|
@@ -637,16 +1137,16 @@ creds.command("remove").argument("<key>", "Credential key").action(async (key) =
|
|
|
637
1137
|
});
|
|
638
1138
|
var sk = program.command("skills").description("Manage skills");
|
|
639
1139
|
sk.command("list").action(async () => {
|
|
640
|
-
const dataDir =
|
|
641
|
-
const secret = await
|
|
642
|
-
const credentialStore = new
|
|
643
|
-
|
|
1140
|
+
const dataDir = getDataDir11();
|
|
1141
|
+
const secret = await getOrCreateSecret2(dataDir);
|
|
1142
|
+
const credentialStore = new EncryptedCredentialStore2(
|
|
1143
|
+
path11.join(dataDir, "credentials.enc.json"),
|
|
644
1144
|
secret
|
|
645
1145
|
);
|
|
646
|
-
const bundledSkillsDir =
|
|
647
|
-
const userSkillsDir =
|
|
648
|
-
const configPath =
|
|
649
|
-
const config = await
|
|
1146
|
+
const bundledSkillsDir = path11.resolve("skills");
|
|
1147
|
+
const userSkillsDir = path11.join(dataDir, "skills");
|
|
1148
|
+
const configPath = path11.join(dataDir, "config.yaml");
|
|
1149
|
+
const config = await loadConfig6(configPath);
|
|
650
1150
|
const allSkills = await loadSkills(
|
|
651
1151
|
bundledSkillsDir,
|
|
652
1152
|
userSkillsDir,
|
|
@@ -665,23 +1165,45 @@ sk.command("list").action(async () => {
|
|
|
665
1165
|
}
|
|
666
1166
|
});
|
|
667
1167
|
sk.command("install").argument("<git-url>", "Git URL of the skill repository").action(async (gitUrl) => {
|
|
668
|
-
const dataDir =
|
|
669
|
-
const userSkillsDir =
|
|
1168
|
+
const dataDir = getDataDir11();
|
|
1169
|
+
const userSkillsDir = path11.join(dataDir, "skills");
|
|
670
1170
|
const result = await installSkillFromGit(gitUrl, userSkillsDir);
|
|
671
1171
|
console.log(
|
|
672
1172
|
`${theme.success("\u2713")} Installed skill ${theme.command(result.name)}`
|
|
673
1173
|
);
|
|
674
1174
|
});
|
|
675
1175
|
sk.command("remove").argument("<name>", "Skill name").action(async (name) => {
|
|
676
|
-
const dataDir =
|
|
677
|
-
const userSkillsDir =
|
|
1176
|
+
const dataDir = getDataDir11();
|
|
1177
|
+
const userSkillsDir = path11.join(dataDir, "skills");
|
|
678
1178
|
await removeSkill(name, userSkillsDir);
|
|
679
1179
|
console.log(`${theme.success("\u2713")} Removed skill ${theme.command(name)}`);
|
|
680
1180
|
});
|
|
1181
|
+
sk.command("create").description("Create a new custom skill").argument("<name>", "Skill name (kebab-case)").requiredOption("-c, --content <content>", "SKILL.md content (or use --file)").action(async (name, opts) => {
|
|
1182
|
+
const dataDir = getDataDir11();
|
|
1183
|
+
const userSkillsDir = path11.join(dataDir, "skills");
|
|
1184
|
+
await createSkill(userSkillsDir, name, opts.content);
|
|
1185
|
+
console.log(`${theme.success("\u2713")} Created skill ${theme.command(name)}`);
|
|
1186
|
+
});
|
|
1187
|
+
sk.command("update").description("Update a custom skill").argument("<name>", "Skill name").requiredOption("-c, --content <content>", "New SKILL.md content").action(async (name, opts) => {
|
|
1188
|
+
const dataDir = getDataDir11();
|
|
1189
|
+
const userSkillsDir = path11.join(dataDir, "skills");
|
|
1190
|
+
await updateSkill(userSkillsDir, name, opts.content);
|
|
1191
|
+
console.log(`${theme.success("\u2713")} Updated skill ${theme.command(name)}`);
|
|
1192
|
+
});
|
|
1193
|
+
sk.command("show").description("Show skill content").argument("<name>", "Skill name").action(async (name) => {
|
|
1194
|
+
const dataDir = getDataDir11();
|
|
1195
|
+
const userSkillsDir = path11.join(dataDir, "skills");
|
|
1196
|
+
const content = await readSkillFile(userSkillsDir, name);
|
|
1197
|
+
if (!content) {
|
|
1198
|
+
console.error(theme.error(`\u2717 Skill not found: ${name}`));
|
|
1199
|
+
process.exit(1);
|
|
1200
|
+
}
|
|
1201
|
+
console.log(content);
|
|
1202
|
+
});
|
|
681
1203
|
var cr = program.command("cron").description("Manage cron jobs");
|
|
682
1204
|
cr.command("list").action(async () => {
|
|
683
|
-
const dataDir =
|
|
684
|
-
const dbPath =
|
|
1205
|
+
const dataDir = getDataDir11();
|
|
1206
|
+
const dbPath = path11.join(dataDir, "cortask.db");
|
|
685
1207
|
const cronService = new CronService(dbPath);
|
|
686
1208
|
const jobs = cronService.list();
|
|
687
1209
|
if (jobs.length === 0) {
|
|
@@ -697,8 +1219,8 @@ cr.command("list").action(async () => {
|
|
|
697
1219
|
cronService.stop();
|
|
698
1220
|
});
|
|
699
1221
|
cr.command("add").requiredOption("-n, --name <name>", "Job name").requiredOption("-s, --schedule <cron>", "Cron expression").requiredOption("-p, --prompt <prompt>", "Prompt to execute").option("-w, --workspace <id>", "Workspace ID").action(async (opts) => {
|
|
700
|
-
const dataDir =
|
|
701
|
-
const dbPath =
|
|
1222
|
+
const dataDir = getDataDir11();
|
|
1223
|
+
const dbPath = path11.join(dataDir, "cortask.db");
|
|
702
1224
|
const cronService = new CronService(dbPath);
|
|
703
1225
|
const job = cronService.add({
|
|
704
1226
|
name: opts.name,
|
|
@@ -713,13 +1235,56 @@ cr.command("add").requiredOption("-n, --name <name>", "Job name").requiredOption
|
|
|
713
1235
|
cronService.stop();
|
|
714
1236
|
});
|
|
715
1237
|
cr.command("remove").argument("<id>", "Job ID").action(async (id) => {
|
|
716
|
-
const dataDir =
|
|
717
|
-
const dbPath =
|
|
1238
|
+
const dataDir = getDataDir11();
|
|
1239
|
+
const dbPath = path11.join(dataDir, "cortask.db");
|
|
718
1240
|
const cronService = new CronService(dbPath);
|
|
719
1241
|
cronService.remove(id);
|
|
720
1242
|
console.log(`${theme.success("\u2713")} Deleted cron job ${theme.muted(id)}`);
|
|
721
1243
|
cronService.stop();
|
|
722
1244
|
});
|
|
1245
|
+
cr.command("update").description("Update a cron job").argument("<id>", "Job ID").option("-n, --name <name>", "New job name").option("-s, --schedule <cron>", "New cron expression").option("-p, --prompt <prompt>", "New prompt").option("--enable", "Enable the job").option("--disable", "Disable the job").action(async (id, opts) => {
|
|
1246
|
+
const dataDir = getDataDir11();
|
|
1247
|
+
const dbPath = path11.join(dataDir, "cortask.db");
|
|
1248
|
+
const cronService = new CronService(dbPath);
|
|
1249
|
+
const updates = {};
|
|
1250
|
+
if (opts.name) updates.name = opts.name;
|
|
1251
|
+
if (opts.schedule) updates.schedule = { type: "cron", expression: opts.schedule };
|
|
1252
|
+
if (opts.prompt) updates.prompt = opts.prompt;
|
|
1253
|
+
if (opts.enable) updates.enabled = true;
|
|
1254
|
+
if (opts.disable) updates.enabled = false;
|
|
1255
|
+
if (Object.keys(updates).length === 0) {
|
|
1256
|
+
console.error(theme.error("\u2717 No fields to update. Use --name, --schedule, --prompt, --enable, or --disable."));
|
|
1257
|
+
cronService.stop();
|
|
1258
|
+
process.exit(1);
|
|
1259
|
+
}
|
|
1260
|
+
const job = cronService.update(id, updates);
|
|
1261
|
+
if (!job) {
|
|
1262
|
+
console.error(theme.error(`\u2717 Cron job not found: ${id}`));
|
|
1263
|
+
cronService.stop();
|
|
1264
|
+
process.exit(1);
|
|
1265
|
+
}
|
|
1266
|
+
console.log(`${theme.success("\u2713")} Updated cron job ${theme.command(job.name)}`);
|
|
1267
|
+
cronService.stop();
|
|
1268
|
+
});
|
|
1269
|
+
cr.command("run").description("Execute a cron job immediately").argument("<id>", "Job ID").action(async (id) => {
|
|
1270
|
+
const dataDir = getDataDir11();
|
|
1271
|
+
const dbPath = path11.join(dataDir, "cortask.db");
|
|
1272
|
+
const cronService = new CronService(dbPath);
|
|
1273
|
+
const job = cronService.getJob(id);
|
|
1274
|
+
if (!job) {
|
|
1275
|
+
console.error(theme.error(`\u2717 Cron job not found: ${id}`));
|
|
1276
|
+
cronService.stop();
|
|
1277
|
+
process.exit(1);
|
|
1278
|
+
}
|
|
1279
|
+
console.log(theme.muted(`Executing job "${job.name}"...`));
|
|
1280
|
+
try {
|
|
1281
|
+
await cronService.runNow(id);
|
|
1282
|
+
console.log(`${theme.success("\u2713")} Job executed successfully`);
|
|
1283
|
+
} catch (err) {
|
|
1284
|
+
console.error(theme.error(`\u2717 ${err instanceof Error ? err.message : String(err)}`));
|
|
1285
|
+
}
|
|
1286
|
+
cronService.stop();
|
|
1287
|
+
});
|
|
723
1288
|
program.command("update").description("Check for updates and optionally install them").option("--check", "Only check, do not install").action(async (opts) => {
|
|
724
1289
|
try {
|
|
725
1290
|
const res = await fetch("https://registry.npmjs.org/cortask/latest");
|