seclaw 0.1.15 → 0.1.17

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.
Files changed (2) hide show
  1. package/dist/cli.js +199 -168
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -389,13 +389,204 @@ function readComposioLocalKey() {
389
389
 
390
390
  // src/scaffold.ts
391
391
  import { mkdir, writeFile as writeFile2, cp, rm, readFile as readFile2 } from "fs/promises";
392
- import { existsSync as existsSync3 } from "fs";
393
- import { join as join2, resolve as resolve3 } from "path";
392
+ import { existsSync as existsSync4 } from "fs";
393
+ import { join as join2, resolve as resolve4 } from "path";
394
+
395
+ // src/docker.ts
396
+ import { execa as execa2, execaSync } from "execa";
397
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "fs";
398
+ import { resolve as resolve3 } from "path";
399
+ async function checkDocker() {
400
+ try {
401
+ await execa2("docker", ["version"]);
402
+ } catch {
403
+ return { ok: false, error: "Docker is not installed. Download it at https://docker.com/get-started" };
404
+ }
405
+ try {
406
+ await execa2("docker", ["info"]);
407
+ } catch {
408
+ return { ok: false, error: "Docker is not running. Open Docker Desktop and try again." };
409
+ }
410
+ return { ok: true };
411
+ }
412
+ async function stopExistingSeclaw() {
413
+ try {
414
+ const result = await execa2("docker", [
415
+ "ps",
416
+ "-a",
417
+ "--filter",
418
+ "label=com.docker.compose.service=agent",
419
+ "--format",
420
+ '{{index .Labels "com.docker.compose.project"}}'
421
+ ]);
422
+ const projects = /* @__PURE__ */ new Set();
423
+ for (const line of result.stdout.split("\n").filter(Boolean)) {
424
+ projects.add(line.trim());
425
+ }
426
+ try {
427
+ const portResult = await execa2("docker", [
428
+ "ps",
429
+ "--filter",
430
+ "publish=8288",
431
+ "--format",
432
+ '{{index .Labels "com.docker.compose.project"}}'
433
+ ]);
434
+ for (const line of portResult.stdout.split("\n").filter(Boolean)) {
435
+ projects.add(line.trim());
436
+ }
437
+ } catch {
438
+ }
439
+ for (const project of projects) {
440
+ if (!project) continue;
441
+ try {
442
+ await execa2("docker", ["compose", "-p", project, "down"]);
443
+ } catch {
444
+ }
445
+ }
446
+ } catch {
447
+ }
448
+ }
449
+ async function findRunningSeclaw() {
450
+ const agentResult = await findContainerByLabel("seclaw");
451
+ if (agentResult) return agentResult;
452
+ try {
453
+ const result = await execa2("docker", [
454
+ "ps",
455
+ "--filter",
456
+ "publish=5678",
457
+ "--format",
458
+ "{{.Names}}"
459
+ ]);
460
+ const names = result.stdout.trim();
461
+ if (!names) return null;
462
+ const containerName = names.split("\n")[0];
463
+ try {
464
+ const inspect = await execa2("docker", [
465
+ "inspect",
466
+ containerName,
467
+ "--format",
468
+ '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'
469
+ ]);
470
+ return { dir: inspect.stdout.trim() || null };
471
+ } catch {
472
+ return { dir: null };
473
+ }
474
+ } catch {
475
+ return null;
476
+ }
477
+ }
478
+ async function findContainerByLabel(project) {
479
+ try {
480
+ const result = await execa2("docker", [
481
+ "ps",
482
+ "--filter",
483
+ `label=com.docker.compose.project=${project}`,
484
+ "--format",
485
+ "{{.Names}}"
486
+ ]);
487
+ const names = result.stdout.trim();
488
+ if (!names) return null;
489
+ const containerName = names.split("\n")[0];
490
+ try {
491
+ const inspect = await execa2("docker", [
492
+ "inspect",
493
+ containerName,
494
+ "--format",
495
+ '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'
496
+ ]);
497
+ return { dir: inspect.stdout.trim() || null };
498
+ } catch {
499
+ return { dir: null };
500
+ }
501
+ } catch {
502
+ return null;
503
+ }
504
+ }
505
+ var SECLAW_CONFIG_DIR = resolve3(process.env.HOME || "~", ".seclaw");
506
+ var PROJECT_PATH_FILE = resolve3(SECLAW_CONFIG_DIR, "project-path");
507
+ function saveProjectDir(dir) {
508
+ try {
509
+ mkdirSync(SECLAW_CONFIG_DIR, { recursive: true });
510
+ writeFileSync(PROJECT_PATH_FILE, dir);
511
+ } catch {
512
+ }
513
+ }
514
+ function isValidProjectDir(dir) {
515
+ const composePath = resolve3(dir, "docker-compose.yml");
516
+ if (existsSync3(composePath)) {
517
+ try {
518
+ const content = readFileSync2(composePath, "utf-8");
519
+ if (content.includes("agent") && content.includes("agent-net")) return true;
520
+ } catch {
521
+ }
522
+ }
523
+ const envPath = resolve3(dir, ".env");
524
+ if (existsSync3(envPath)) {
525
+ try {
526
+ const env = readFileSync2(envPath, "utf-8");
527
+ if (env.includes("TELEGRAM_BOT_TOKEN")) return true;
528
+ } catch {
529
+ }
530
+ }
531
+ return false;
532
+ }
533
+ function findProjectDir() {
534
+ try {
535
+ const saved = readFileSync2(PROJECT_PATH_FILE, "utf-8").trim();
536
+ if (saved && isValidProjectDir(saved)) return saved;
537
+ } catch {
538
+ }
539
+ try {
540
+ const result = execaSync("docker", [
541
+ "ps",
542
+ "--filter",
543
+ "label=com.docker.compose.service=agent",
544
+ "--format",
545
+ "{{.Names}}"
546
+ ]);
547
+ const names = result.stdout.trim().split("\n").filter(Boolean);
548
+ for (const name of names) {
549
+ try {
550
+ const inspect = execaSync("docker", [
551
+ "inspect",
552
+ name,
553
+ "--format",
554
+ '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'
555
+ ]);
556
+ const dir = inspect.stdout.trim();
557
+ if (dir && isValidProjectDir(dir)) {
558
+ saveProjectDir(dir);
559
+ return dir;
560
+ }
561
+ } catch {
562
+ }
563
+ }
564
+ } catch {
565
+ }
566
+ const home = process.env.HOME || "~";
567
+ const candidates = [
568
+ process.cwd(),
569
+ resolve3(process.cwd(), ".."),
570
+ home,
571
+ resolve3(home, "seclaw"),
572
+ resolve3(home, "my-agent")
573
+ ];
574
+ for (const dir of candidates) {
575
+ if (isValidProjectDir(dir)) {
576
+ saveProjectDir(dir);
577
+ return dir;
578
+ }
579
+ }
580
+ return null;
581
+ }
582
+
583
+ // src/scaffold.ts
394
584
  async function scaffoldProject(targetDir, answers) {
585
+ saveProjectDir(resolve4(targetDir));
395
586
  const ws = answers.workspacePath || "./shared";
396
587
  const wsSubdirs = ["tasks", "reports", "notes", "drafts", "memory", "config"];
397
588
  for (const sub of wsSubdirs) {
398
- await mkdir(resolve3(targetDir, ws, sub), { recursive: true });
589
+ await mkdir(resolve4(targetDir, ws, sub), { recursive: true });
399
590
  }
400
591
  for (const dir of ["templates", "commander", "agent", "cloudflared"]) {
401
592
  await mkdir(join2(targetDir, dir), { recursive: true });
@@ -602,13 +793,13 @@ done
602
793
  await writeFile2(join2(dir, "tunnel-start.sh"), script, { mode: 493 });
603
794
  }
604
795
  async function copyAgentFiles(dir) {
605
- const agentTemplateSrc = resolve3(import.meta.dirname, "runtime");
796
+ const agentTemplateSrc = resolve4(import.meta.dirname, "runtime");
606
797
  const agentDest = join2(dir, "agent");
607
- if (existsSync3(agentDest)) {
798
+ if (existsSync4(agentDest)) {
608
799
  await rm(agentDest, { recursive: true });
609
800
  }
610
801
  await mkdir(agentDest, { recursive: true });
611
- if (existsSync3(agentTemplateSrc)) {
802
+ if (existsSync4(agentTemplateSrc)) {
612
803
  await cp(agentTemplateSrc, agentDest, { recursive: true });
613
804
  } else {
614
805
  await writeMinimalAgent(agentDest);
@@ -921,13 +1112,13 @@ rules:
921
1112
  await writeFile2(join2(dir, "permissions.yml"), content);
922
1113
  }
923
1114
  async function writeIfMissing(path, content) {
924
- if (!existsSync3(path)) {
1115
+ if (!existsSync4(path)) {
925
1116
  await writeFile2(path, content);
926
1117
  }
927
1118
  }
928
1119
  async function writeSeedFiles(dir, answers, ws = "./shared") {
929
1120
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
930
- const wsDir = resolve3(dir, ws);
1121
+ const wsDir = resolve4(dir, ws);
931
1122
  await writeIfMissing(
932
1123
  join2(wsDir, "memory/learnings.md"),
933
1124
  `# Agent Memory
@@ -1037,165 +1228,6 @@ templates/
1037
1228
  await writeIfMissing(join2(dir, ".dockerignore"), content);
1038
1229
  }
1039
1230
 
1040
- // src/docker.ts
1041
- import { execa as execa2, execaSync } from "execa";
1042
- import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
1043
- import { resolve as resolve4 } from "path";
1044
- async function checkDocker() {
1045
- try {
1046
- await execa2("docker", ["version"]);
1047
- } catch {
1048
- return { ok: false, error: "Docker is not installed. Download it at https://docker.com/get-started" };
1049
- }
1050
- try {
1051
- await execa2("docker", ["info"]);
1052
- } catch {
1053
- return { ok: false, error: "Docker is not running. Open Docker Desktop and try again." };
1054
- }
1055
- return { ok: true };
1056
- }
1057
- async function stopExistingSeclaw() {
1058
- try {
1059
- const result = await execa2("docker", [
1060
- "ps",
1061
- "-a",
1062
- "--filter",
1063
- "label=com.docker.compose.service=agent",
1064
- "--format",
1065
- '{{index .Labels "com.docker.compose.project"}}'
1066
- ]);
1067
- const projects = /* @__PURE__ */ new Set();
1068
- for (const line of result.stdout.split("\n").filter(Boolean)) {
1069
- projects.add(line.trim());
1070
- }
1071
- try {
1072
- const portResult = await execa2("docker", [
1073
- "ps",
1074
- "--filter",
1075
- "publish=8288",
1076
- "--format",
1077
- '{{index .Labels "com.docker.compose.project"}}'
1078
- ]);
1079
- for (const line of portResult.stdout.split("\n").filter(Boolean)) {
1080
- projects.add(line.trim());
1081
- }
1082
- } catch {
1083
- }
1084
- for (const project of projects) {
1085
- if (!project) continue;
1086
- try {
1087
- await execa2("docker", ["compose", "-p", project, "down"]);
1088
- } catch {
1089
- }
1090
- }
1091
- } catch {
1092
- }
1093
- }
1094
- async function findRunningSeclaw() {
1095
- const agentResult = await findContainerByLabel("seclaw");
1096
- if (agentResult) return agentResult;
1097
- try {
1098
- const result = await execa2("docker", [
1099
- "ps",
1100
- "--filter",
1101
- "publish=5678",
1102
- "--format",
1103
- "{{.Names}}"
1104
- ]);
1105
- const names = result.stdout.trim();
1106
- if (!names) return null;
1107
- const containerName = names.split("\n")[0];
1108
- try {
1109
- const inspect = await execa2("docker", [
1110
- "inspect",
1111
- containerName,
1112
- "--format",
1113
- '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'
1114
- ]);
1115
- return { dir: inspect.stdout.trim() || null };
1116
- } catch {
1117
- return { dir: null };
1118
- }
1119
- } catch {
1120
- return null;
1121
- }
1122
- }
1123
- async function findContainerByLabel(project) {
1124
- try {
1125
- const result = await execa2("docker", [
1126
- "ps",
1127
- "--filter",
1128
- `label=com.docker.compose.project=${project}`,
1129
- "--format",
1130
- "{{.Names}}"
1131
- ]);
1132
- const names = result.stdout.trim();
1133
- if (!names) return null;
1134
- const containerName = names.split("\n")[0];
1135
- try {
1136
- const inspect = await execa2("docker", [
1137
- "inspect",
1138
- containerName,
1139
- "--format",
1140
- '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'
1141
- ]);
1142
- return { dir: inspect.stdout.trim() || null };
1143
- } catch {
1144
- return { dir: null };
1145
- }
1146
- } catch {
1147
- return null;
1148
- }
1149
- }
1150
- function findProjectDir() {
1151
- try {
1152
- const result = execaSync("docker", [
1153
- "ps",
1154
- "--filter",
1155
- "label=com.docker.compose.service=agent",
1156
- "--format",
1157
- "{{.Names}}"
1158
- ]);
1159
- const names = result.stdout.trim().split("\n").filter(Boolean);
1160
- for (const name of names) {
1161
- try {
1162
- const inspect = execaSync("docker", [
1163
- "inspect",
1164
- name,
1165
- "--format",
1166
- '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'
1167
- ]);
1168
- const dir = inspect.stdout.trim();
1169
- if (dir && existsSync4(resolve4(dir, "docker-compose.yml"))) {
1170
- return dir;
1171
- }
1172
- } catch {
1173
- }
1174
- }
1175
- } catch {
1176
- }
1177
- const candidates = [
1178
- process.cwd(),
1179
- resolve4(process.cwd(), ".."),
1180
- resolve4(process.env.HOME || "~", "seclaw"),
1181
- resolve4(process.env.HOME || "~", "my-agent")
1182
- ];
1183
- for (const dir of candidates) {
1184
- const composePath = resolve4(dir, "docker-compose.yml");
1185
- if (existsSync4(composePath)) {
1186
- try {
1187
- const content = readFileSync2(composePath, "utf-8");
1188
- if (content.includes("agent") && content.includes("agent-net")) {
1189
- return dir;
1190
- }
1191
- } catch {
1192
- continue;
1193
- }
1194
- }
1195
- }
1196
- return null;
1197
- }
1198
-
1199
1231
  // src/commands/create.ts
1200
1232
  async function create(directory) {
1201
1233
  let targetDir = resolve5(process.cwd(), directory);
@@ -1560,7 +1592,6 @@ async function add(template, options) {
1560
1592
  p3.log.success(
1561
1593
  `Capability '${pc3.bold(templateName)}' installed. ${pc3.cyan(String(installed.capabilities.length))} capabilities active.`
1562
1594
  );
1563
- p3.log.info(`Run ${pc3.cyan("docker compose restart agent")} to apply.`);
1564
1595
  }
1565
1596
  p3.outro(`${pc3.green("Done!")} Template ${pc3.bold(template)} is ready.`);
1566
1597
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seclaw",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Secure autonomous AI agents in 60 seconds",
5
5
  "type": "module",
6
6
  "bin": {