diff --git a/packages/security/src/index.ts b/packages/security/src/index.ts index ce9b305..47f11ce 100644 --- a/packages/security/src/index.ts +++ b/packages/security/src/index.ts @@ -32,7 +32,9 @@ export const riskFromText = (text: string): RiskLevel => { export const redactSecrets = (input: string) => { return input - .replace(/sk-[A-Za-z0-9_-]+/g, "[REDACTED_OPENAI_KEY]") + .replace(/sk-ant-[A-Za-z0-9_-]+/g, "[REDACTED_ANTHROPIC_KEY]") + .replace(/sk-(?!ant-)[A-Za-z0-9_-]+/g, "[REDACTED_OPENAI_KEY]") + .replace(/ghp_[A-Za-z0-9_]+/g, "[REDACTED_GITHUB_TOKEN]") .replace(/AIza[A-Za-z0-9_-]+/g, "[REDACTED_GOOGLE_KEY]") .replace(/Bearer\s+[A-Za-z0-9._-]+/g, "Bearer [REDACTED_TOKEN]"); }; diff --git a/packages/security/src/security_redact.test.ts b/packages/security/src/security_redact.test.ts new file mode 100644 index 0000000..0e75162 --- /dev/null +++ b/packages/security/src/security_redact.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from "vitest"; +import { redactSecrets } from "./index.js"; + +describe("redactSecrets", () => { + it("redacts OpenAI keys", () => { + const input = "Here is my key: sk-abcdefghijklmnopqrstuvwxyz0123456789ABCD"; + const redacted = redactSecrets(input); + expect(redacted).toBe("Here is my key: [REDACTED_OPENAI_KEY]"); + }); + + it("redacts Anthropic keys specifically", () => { + const input = "Anthropic key: sk-ant-api01-abcdefghijklmnopqrstuvwxyz0123456789ABCD-xyz123"; + const redacted = redactSecrets(input); + expect(redacted).toBe("Anthropic key: [REDACTED_ANTHROPIC_KEY]"); + }); + + it("distinguishes between OpenAI and Anthropic keys", () => { + const input = "OpenAI: sk-openai123, Anthropic: sk-ant-anthropic456"; + const redacted = redactSecrets(input); + expect(redacted).toBe("OpenAI: [REDACTED_OPENAI_KEY], Anthropic: [REDACTED_ANTHROPIC_KEY]"); + }); + + it("redacts GitHub tokens", () => { + const input = "My GitHub token is ghp_1234567890abcdefghijklmnopqrstuvwxyzABCD"; + const redacted = redactSecrets(input); + expect(redacted).toBe("My GitHub token is [REDACTED_GITHUB_TOKEN]"); + }); + + it("redacts Google keys", () => { + const input = "Google API key: AIzaSyD-1234567890abcdefghijklmnopqrstuvwxyz"; + const redacted = redactSecrets(input); + expect(redacted).toBe("Google API key: [REDACTED_GOOGLE_KEY]"); + }); + + it("redacts Bearer tokens", () => { + const input = "Authorization: Bearer my-secret-token.12345"; + const redacted = redactSecrets(input); + expect(redacted).toBe("Authorization: Bearer [REDACTED_TOKEN]"); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 607a2a9..933d76a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3148,6 +3148,7 @@ packages: uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true vite-node@2.1.9: diff --git a/src/cognitive/cli.py b/src/cognitive/cli.py index 2f2443b..b550e8b 100644 --- a/src/cognitive/cli.py +++ b/src/cognitive/cli.py @@ -9,6 +9,13 @@ from .service import MissionExecutorService +# ANSI color codes for terminal output +COLOR_GREEN = "\033[92m" +COLOR_RED = "\033[91m" +COLOR_YELLOW = "\033[93m" +COLOR_CYAN = "\033[96m" +COLOR_RESET = "\033[0m" + def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="JeanBot Python mission runner") @@ -53,7 +60,7 @@ async def run_shell(args: argparse.Namespace): while True: try: - line = input("\njeanbot> ").strip() + line = input(f"\n{COLOR_CYAN}jeanbot>{COLOR_RESET} ").strip() if not line: continue if line.lower() in ("exit", "quit"): @@ -62,19 +69,46 @@ async def run_shell(args: argparse.Namespace): history.append(line) if line.lower() == "help": - print("Commands:") + print(f"{COLOR_CYAN}Commands:{COLOR_RESET}") print(" help Show this help") print(" history Show command history") + print(" status Show status of the last mission") + print(" plan Show the plan of the last mission") print(" exit | quit Exit shell") print(" Plan and execute a mission") print(" refine Refine the last mission result with feedback") continue if line.lower() == "history": + print(f"{COLOR_CYAN}Command History:{COLOR_RESET}") for i, cmd in enumerate(history, 1): print(f" {i:3} {cmd}") continue + if line.lower() == "status": + if not last_result: + print(f"{COLOR_YELLOW}No mission has been run yet.{COLOR_RESET}") + continue + metrics = last_result.metrics + status_color = COLOR_GREEN if last_result.status == "completed" else COLOR_RED + print(f"{COLOR_CYAN}Mission Status:{COLOR_RESET}") + print(f" ID: {last_result.mission_id}") + print(f" Status: {status_color}{last_result.status}{COLOR_RESET}") + print(f" Progress: {metrics.get('completed_steps', 0)}/{metrics.get('total_steps', 0)} steps") + print(f" Average Score: {metrics.get('average_score', 0)}") + continue + + if line.lower() == "plan": + if not last_result: + print(f"{COLOR_YELLOW}No mission has been run yet.{COLOR_RESET}") + continue + print(f"{COLOR_CYAN}Mission Plan:{COLOR_RESET}") + for report in last_result.step_reports: + diag = report.diagnostics + status_icon = f"{COLOR_GREEN}✓{COLOR_RESET}" if diag and diag.failure_class == "none" else f"{COLOR_RED}✗{COLOR_RESET}" + print(f" {status_icon} {report.step_id}: {report.summary}") + continue + if line.lower().startswith("refine "): if not last_result: print("Nothing to refine. Run a mission first.") @@ -97,15 +131,21 @@ async def run_shell(args: argparse.Namespace): "mode": args.mode, } - print(f"Executing: {title}") + print(f"{COLOR_YELLOW}Executing:{COLOR_RESET} {title}") last_result = await service.execute_payload(payload) - print(f"\nStatus: {last_result.status}") - print(f"Summary: {last_result.verification_summary}") + status_color = COLOR_GREEN if last_result.status == "completed" else COLOR_RED + print(f"\n{COLOR_CYAN}Status:{COLOR_RESET} {status_color}{last_result.status}{COLOR_RESET}") + print(f"{COLOR_CYAN}Summary:{COLOR_RESET} {last_result.verification_summary}") if last_result.artifacts: - print(f"Artifacts: {len(last_result.artifacts)}") + print(f"{COLOR_CYAN}Artifacts ({len(last_result.artifacts)}):{COLOR_RESET}") for artifact in last_result.artifacts: - print(f" - {artifact.title}: {artifact.path}") + try: + rel_path = Path(artifact.path).relative_to(Path(args.workspace_root).absolute()) + except ValueError: + # Fallback if path is not relative to workspace_root + rel_path = artifact.path + print(f" - {artifact.title}: {rel_path}") except KeyboardInterrupt: print("\nInterrupt received, type 'exit' to quit.") diff --git a/workspace/users/{userId}/.jeanbot/context.md b/workspace/users/{userId}/.jeanbot/context.md index 207eb92..3d90ce4 100644 --- a/workspace/users/{userId}/.jeanbot/context.md +++ b/workspace/users/{userId}/.jeanbot/context.md @@ -1,7 +1,7 @@ # JeanBot User Context -- Current mission: Smoke test -- Updated at: 2026-03-13T21:07:03.733Z -- Completed steps: Inspect workspace files | Load and update memory context | Run policy and risk review | Decompose objective into steps | Create safety checkpoint | Handle finance-sensitive workflows | Synthesize final mission result | Track status and coordination | Synthesize final mission result | Clarify mission constraints | Produce mission documentation +- Current mission: API mission +- Updated at: 2026-05-26T01:30:35.720Z +- Completed steps: Inspect workspace files | Load and update memory context | Run policy and risk review | Decompose objective into steps | Synthesize final mission result | Track status and coordination | Synthesize final mission result | Clarify mission constraints | Produce mission documentation - In-progress steps: none - Upcoming steps: none \ No newline at end of file