propr-cli 0.8.3

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 (64) hide show
  1. package/README.md +549 -0
  2. package/dist/api/agentTank.js +27 -0
  3. package/dist/api/agents.js +201 -0
  4. package/dist/api/client.js +284 -0
  5. package/dist/api/errors.js +145 -0
  6. package/dist/api/implement.js +147 -0
  7. package/dist/api/index.js +26 -0
  8. package/dist/api/logs.js +59 -0
  9. package/dist/api/plans.js +160 -0
  10. package/dist/api/relay.js +73 -0
  11. package/dist/api/repos.js +243 -0
  12. package/dist/api/settings.js +219 -0
  13. package/dist/api/system.js +53 -0
  14. package/dist/api/tasks.js +140 -0
  15. package/dist/api/todos.js +77 -0
  16. package/dist/api/types.js +6 -0
  17. package/dist/assets/.env.example +183 -0
  18. package/dist/assets/env.example.txt +198 -0
  19. package/dist/commands/agentCommands.js +405 -0
  20. package/dist/commands/checkCommands.js +384 -0
  21. package/dist/commands/implementCommands.js +178 -0
  22. package/dist/commands/index.js +22 -0
  23. package/dist/commands/initCommands.js +167 -0
  24. package/dist/commands/initStack.js +193 -0
  25. package/dist/commands/logCommands.js +170 -0
  26. package/dist/commands/planCommands.js +552 -0
  27. package/dist/commands/relayCommands.js +149 -0
  28. package/dist/commands/repoCommands.js +526 -0
  29. package/dist/commands/settingCommands.js +237 -0
  30. package/dist/commands/stackCommands.js +86 -0
  31. package/dist/commands/startCommand.js +36 -0
  32. package/dist/commands/systemCommands.js +221 -0
  33. package/dist/commands/tankCommands.js +55 -0
  34. package/dist/commands/taskCommands.js +554 -0
  35. package/dist/commands/todoCommands.js +620 -0
  36. package/dist/commands/uiDocsCommands.js +69 -0
  37. package/dist/config/ConfigManager.js +360 -0
  38. package/dist/config/index.js +8 -0
  39. package/dist/config/types.js +16 -0
  40. package/dist/index.js +276 -0
  41. package/dist/orchestrator/format.js +31 -0
  42. package/dist/orchestrator/index.js +102 -0
  43. package/dist/orchestrator/manifest.json +16 -0
  44. package/dist/orchestrator/orchestrator.mjs +798 -0
  45. package/dist/orchestrator/types.js +10 -0
  46. package/dist/tui/StartApp.js +175 -0
  47. package/dist/tui/app.js +9 -0
  48. package/dist/tui/render.js +87 -0
  49. package/dist/utils/envFile.js +65 -0
  50. package/dist/utils/index.js +8 -0
  51. package/dist/utils/io.js +186 -0
  52. package/dist/utils/parseState.js +14 -0
  53. package/dist/utils/resolveProject.js +50 -0
  54. package/dist/vendor/shared/demoMode.js +6 -0
  55. package/dist/vendor/shared/events.js +30 -0
  56. package/dist/vendor/shared/githubAuthMode.js +35 -0
  57. package/dist/vendor/shared/index.js +15 -0
  58. package/dist/vendor/shared/labelUtils.js +32 -0
  59. package/dist/vendor/shared/modelDefinitions.js +146 -0
  60. package/dist/vendor/shared/reviewPrompt.js +18 -0
  61. package/dist/vendor/shared/usageTypes.js +13 -0
  62. package/dist/vendor/shared/userWhitelist.js +30 -0
  63. package/dist/vendor/shared/validateRelayUrl.js +21 -0
  64. package/package.json +31 -0
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Local repository initialization command.
3
+ *
4
+ * Scaffolds the .propr directory used by ProPR agent execution containers.
5
+ */
6
+ import { Command } from "commander";
7
+ import path from "path";
8
+ import { chmod, mkdir, writeFile } from "fs/promises";
9
+ import { existsSync } from "fs";
10
+ import { printOutput } from "../utils/io.js";
11
+ import { createInitStackCommand } from "./initStack.js";
12
+ const SETUP_SH = `#!/usr/bin/env bash
13
+ set -euo pipefail
14
+
15
+ # This hook runs before each ProPR implementation execution.
16
+ # Available environment variables:
17
+ # PROPR_WORKSPACE Mounted repository path inside the agent container
18
+ # PROPR_CACHE_DIR Writable cache directory outside the repository
19
+ # PROPR_AGENT_TYPE claude, codex, antigravity, opencode, or vibe
20
+
21
+ cd "$PROPR_WORKSPACE"
22
+
23
+ if [ -f ".propr/package.json" ]; then
24
+ export npm_config_cache="$PROPR_CACHE_DIR/npm"
25
+ mkdir -p "$npm_config_cache"
26
+
27
+ cd ".propr"
28
+ if [ -f "package-lock.json" ]; then
29
+ npm ci
30
+ else
31
+ npm install
32
+ fi
33
+ fi
34
+
35
+ # Example for Alpine-based agent images:
36
+ # sudo apk add --no-cache jq
37
+ `;
38
+ const PACKAGE_JSON = `{
39
+ "name": "propr-repo-tools",
40
+ "private": true,
41
+ "version": "0.0.0",
42
+ "description": "Repository-local tools installed before ProPR agent executions",
43
+ "dependencies": {}
44
+ }
45
+ `;
46
+ const GITIGNORE = `node_modules/
47
+ cache/
48
+ .cache/
49
+ `;
50
+ const README = `# ProPR Repository Setup
51
+
52
+ This directory configures repository-local setup for ProPR agent executions.
53
+
54
+ Before each implementation execution, ProPR runs:
55
+
56
+ \`\`\`bash
57
+ .propr/setup.sh
58
+ \`\`\`
59
+
60
+ Use \`.propr/package.json\` for npm packages that agents need while working in this repository.
61
+ Use \`sudo apk add --no-cache <package>\` in \`setup.sh\` for Alpine system packages.
62
+
63
+ Runtime caches and installed packages should stay out of commits.
64
+ `;
65
+ const SCAFFOLD_FILES = [
66
+ { relativePath: "setup.sh", content: SETUP_SH, mode: 0o755 },
67
+ { relativePath: "package.json", content: PACKAGE_JSON },
68
+ { relativePath: ".gitignore", content: GITIGNORE },
69
+ { relativePath: "README.md", content: README },
70
+ ];
71
+ export async function scaffoldProprDirectory(options = {}) {
72
+ const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
73
+ const proprDir = path.join(cwd, ".propr");
74
+ const result = {
75
+ directory: proprDir,
76
+ created: [],
77
+ skipped: [],
78
+ overwritten: [],
79
+ };
80
+ await mkdir(proprDir, { recursive: true });
81
+ for (const file of SCAFFOLD_FILES) {
82
+ const filePath = path.join(proprDir, file.relativePath);
83
+ const exists = existsSync(filePath);
84
+ if (exists && !options.force) {
85
+ result.skipped.push(file.relativePath);
86
+ continue;
87
+ }
88
+ await writeFile(filePath, file.content, "utf-8");
89
+ if (file.mode !== undefined) {
90
+ await chmod(filePath, file.mode);
91
+ }
92
+ if (exists) {
93
+ result.overwritten.push(file.relativePath);
94
+ }
95
+ else {
96
+ result.created.push(file.relativePath);
97
+ }
98
+ }
99
+ return result;
100
+ }
101
+ function displayInitResult(result) {
102
+ console.log(`Initialized ProPR repository setup at: ${result.directory}`);
103
+ if (result.created.length > 0) {
104
+ console.log(`Created: ${result.created.join(", ")}`);
105
+ }
106
+ if (result.overwritten.length > 0) {
107
+ console.log(`Overwritten: ${result.overwritten.join(", ")}`);
108
+ }
109
+ if (result.skipped.length > 0) {
110
+ console.log(`Skipped existing files: ${result.skipped.join(", ")}`);
111
+ console.log("Use --force to overwrite existing scaffold files.");
112
+ }
113
+ console.log("");
114
+ console.log("Add npm packages with:");
115
+ console.log(" cd .propr && npm install <package>");
116
+ console.log("");
117
+ console.log("Add system packages by editing .propr/setup.sh:");
118
+ console.log(" sudo apk add --no-cache <package>");
119
+ }
120
+ async function runRepoScaffold(options) {
121
+ try {
122
+ const result = await scaffoldProprDirectory({ force: options.force });
123
+ if (printOutput(result, !!options.json)) {
124
+ return;
125
+ }
126
+ displayInitResult(result);
127
+ }
128
+ catch (error) {
129
+ console.error(`Error initializing .propr directory: ${error.message}`);
130
+ process.exit(1);
131
+ }
132
+ }
133
+ export function createInitCommand() {
134
+ const command = new Command("init");
135
+ // Default action (no subcommand) scaffolds the .propr repo files — preserved
136
+ // for backward compatibility.
137
+ command
138
+ .description("Scaffold local setup. Default/`repo`: .propr files. `stack`: a stack root.")
139
+ .option("-f, --force", "Overwrite existing scaffold files")
140
+ .option("-j, --json", "Output result as JSON")
141
+ .addHelpText("after", `
142
+ Subcommands:
143
+ $ propr init Scaffold .propr repository setup files (default)
144
+ $ propr init repo Same as the default — scaffold .propr files
145
+ $ propr init stack Scaffold a local stack root (.env, data/, logs/, repos/)
146
+
147
+ Examples:
148
+ $ propr init
149
+ $ propr init --force
150
+ $ propr init stack
151
+ `)
152
+ .action(async (options) => {
153
+ await runRepoScaffold(options);
154
+ });
155
+ // Explicit alias for the default repo scaffold.
156
+ command
157
+ .command("repo")
158
+ .description("Scaffold .propr repository setup files in the current directory")
159
+ .option("-f, --force", "Overwrite existing scaffold files")
160
+ .option("-j, --json", "Output result as JSON")
161
+ .action(async (options) => {
162
+ await runRepoScaffold(options);
163
+ });
164
+ // New control-plane stack scaffold.
165
+ command.addCommand(createInitStackCommand());
166
+ return command;
167
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * `propr init stack`
3
+ *
4
+ * Scaffolds a local control-plane stack root: copies .env from .env.example,
5
+ * creates data/, logs/ and repos/, detects host agent-credential directories and
6
+ * records them as HOST_*_DIR, and saves the stack root to the CLI config so the
7
+ * other control-plane commands can find it.
8
+ */
9
+ import { Command } from "commander";
10
+ import { existsSync, copyFileSync, chmodSync, mkdirSync, readFileSync, appendFileSync } from "node:fs";
11
+ import { fileURLToPath } from "node:url";
12
+ import { dirname, join, resolve } from "node:path";
13
+ import { homedir } from "node:os";
14
+ import { createConfigManager } from "../config/index.js";
15
+ /** Resolve the bundled .env.example, falling back to a repo checkout. */
16
+ function resolveEnvExample() {
17
+ const here = dirname(fileURLToPath(import.meta.url));
18
+ // Bundled copy is renamed to avoid npm's .env* exclusion from tarballs.
19
+ const bundled = join(here, "..", "assets", "env.example.txt");
20
+ if (existsSync(bundled))
21
+ return bundled;
22
+ const bundledLegacy = join(here, "..", "assets", ".env.example");
23
+ if (existsSync(bundledLegacy))
24
+ return bundledLegacy;
25
+ let dir = here;
26
+ for (let i = 0; i < 8; i += 1) {
27
+ const candidate = join(dir, ".env.example");
28
+ if (existsSync(candidate))
29
+ return candidate;
30
+ const parent = dirname(dir);
31
+ if (parent === dir)
32
+ break;
33
+ dir = parent;
34
+ }
35
+ return undefined;
36
+ }
37
+ /** Detect host agent-credential directories that exist on this machine. */
38
+ function detectCredentials() {
39
+ const home = homedir();
40
+ const candidates = [
41
+ { envKey: "HOST_CLAUDE_DIR", path: join(home, ".claude") },
42
+ { envKey: "HOST_CODEX_DIR", path: join(home, ".codex") },
43
+ { envKey: "HOST_ANTIGRAVITY_DIR", path: join(home, ".gemini") },
44
+ { envKey: "HOST_OPENCODE_XDG_DIR", path: join(home, ".config", "opencode") },
45
+ { envKey: "HOST_OPENCODE_DATA_DIR", path: join(home, ".local", "share", "opencode") },
46
+ { envKey: "HOST_VIBE_DIR", path: join(home, ".vibe") },
47
+ ];
48
+ // Only keep dirs that exist and are safe as Docker bind mounts (no ':').
49
+ return candidates.filter((c) => existsSync(c.path) && !c.path.includes(":"));
50
+ }
51
+ export async function scaffoldStack(options = {}) {
52
+ const rootDir = resolve(options.root ?? process.cwd());
53
+ const envPath = join(rootDir, ".env");
54
+ const result = {
55
+ rootDir,
56
+ envCreated: false,
57
+ envSkipped: false,
58
+ envBackedUp: false,
59
+ dirsCreated: [],
60
+ detected: [],
61
+ credentialsAppended: false,
62
+ pendingCredentials: [],
63
+ };
64
+ mkdirSync(rootDir, { recursive: true });
65
+ // 1. data/logs/repos directories
66
+ for (const sub of ["data", "logs", "repos"]) {
67
+ const dir = join(rootDir, sub);
68
+ if (!existsSync(dir)) {
69
+ mkdirSync(dir, { recursive: true });
70
+ result.dirsCreated.push(sub);
71
+ }
72
+ }
73
+ // 2. .env from .env.example
74
+ if (existsSync(envPath) && !options.force) {
75
+ result.envSkipped = true;
76
+ }
77
+ else {
78
+ const example = resolveEnvExample();
79
+ if (!example) {
80
+ throw new Error("Could not locate .env.example. Run `npm run build` in packages/cli, or run from a ProPR source checkout.");
81
+ }
82
+ if (options.force && existsSync(envPath)) {
83
+ const bakPath = `${envPath}.bak`;
84
+ copyFileSync(envPath, bakPath);
85
+ try {
86
+ chmodSync(bakPath, 0o600);
87
+ }
88
+ catch { /* best-effort */ }
89
+ result.envBackedUp = true;
90
+ }
91
+ copyFileSync(example, envPath);
92
+ try {
93
+ chmodSync(envPath, 0o600);
94
+ }
95
+ catch {
96
+ // Best-effort — may fail on Windows or non-owned files.
97
+ }
98
+ result.envCreated = true;
99
+ }
100
+ // 3. Detect credential dirs and record any not already present in .env.
101
+ // When the .env was pre-existing (not created this run) and --force was
102
+ // not given, collect but do NOT write — the caller should surface the
103
+ // suggestions without silently mutating a user-managed file.
104
+ const detected = detectCredentials();
105
+ const envContent = readFileSync(envPath, "utf-8");
106
+ const toAppend = detected.filter((c) => {
107
+ const re = new RegExp(`^\\s*(export\\s+)?${c.envKey}\\s*=`, "m");
108
+ return !re.test(envContent);
109
+ });
110
+ if (toAppend.length > 0 && !result.envSkipped) {
111
+ const block = "\n# --- Host agent-credential directories (detected by `propr init stack`) ---\n" +
112
+ toAppend.map((c) => `${c.envKey}=${c.path}`).join("\n") +
113
+ "\n";
114
+ appendFileSync(envPath, block, "utf-8");
115
+ result.credentialsAppended = true;
116
+ }
117
+ else {
118
+ result.credentialsAppended = false;
119
+ }
120
+ result.detected = detected;
121
+ result.pendingCredentials = toAppend;
122
+ // 4. Persist the stack root so other commands can find it.
123
+ const configManager = await createConfigManager();
124
+ await configManager.setStackRoot(rootDir);
125
+ return result;
126
+ }
127
+ function displayResult(result) {
128
+ console.log(`Initialized ProPR stack at: ${result.rootDir}`);
129
+ if (result.dirsCreated.length > 0) {
130
+ console.log(`Created directories: ${result.dirsCreated.join(", ")}`);
131
+ }
132
+ if (result.envCreated && result.envBackedUp) {
133
+ console.log("Overwrote .env from .env.example (previous saved to .env.bak)");
134
+ }
135
+ else if (result.envCreated) {
136
+ console.log("Created .env from .env.example");
137
+ }
138
+ else if (result.envSkipped) {
139
+ console.log("Kept existing .env (use --force to overwrite)");
140
+ }
141
+ if (result.detected.length > 0) {
142
+ console.log("");
143
+ console.log("Detected agent credentials on this host:");
144
+ for (const c of result.detected) {
145
+ console.log(` ${c.envKey}=${c.path}`);
146
+ }
147
+ if (!result.credentialsAppended && result.pendingCredentials.length > 0) {
148
+ console.log("");
149
+ console.log("The following credentials are not in .env yet. Add them manually or re-run with --force:");
150
+ for (const c of result.pendingCredentials) {
151
+ console.log(` ${c.envKey}=${c.path}`);
152
+ }
153
+ }
154
+ }
155
+ else {
156
+ console.log("");
157
+ console.log("No agent credential directories detected (~/.claude, ~/.codex, ~/.gemini, ~/.config/opencode, ~/.vibe).");
158
+ console.log("Log in with an agent CLI on this host, then re-run `propr init stack`.");
159
+ }
160
+ console.log("");
161
+ console.log("Next steps:");
162
+ console.log(" 1. Review and edit .env (GitHub credentials, ports, etc.)");
163
+ console.log(" 2. propr check # verify the environment");
164
+ console.log(" 3. propr start # launch the stack");
165
+ }
166
+ /** Creates the `init stack` subcommand. */
167
+ export function createInitStackCommand() {
168
+ return new Command("stack")
169
+ .description("Scaffold a local stack root (.env, data/, logs/, repos/) and detect agent credentials")
170
+ .option("--root <dir>", "Stack root directory (default: current directory)")
171
+ .option("-f, --force", "Overwrite an existing .env")
172
+ .option("-j, --json", "Output result as JSON")
173
+ .addHelpText("after", `
174
+ Examples:
175
+ $ propr init stack
176
+ $ propr init stack --root ~/propr
177
+ $ propr init stack --force
178
+ `)
179
+ .action(async (options) => {
180
+ try {
181
+ const result = await scaffoldStack({ root: options.root, force: options.force });
182
+ if (options.json) {
183
+ console.log(JSON.stringify(result, null, 2));
184
+ return;
185
+ }
186
+ displayResult(result);
187
+ }
188
+ catch (error) {
189
+ console.error(`Error initializing stack: ${error.message}`);
190
+ process.exit(1);
191
+ }
192
+ });
193
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * LLM Log Management Commands
3
+ *
4
+ * CLI commands for viewing LLM execution logs.
5
+ * Provides the `log` command group with the `list` subcommand.
6
+ */
7
+ import { Command } from "commander";
8
+ import { listLlmLogs } from "../api/index.js";
9
+ import { printOutput } from "../utils/index.js";
10
+ /**
11
+ * Truncates a string to a maximum length.
12
+ */
13
+ function truncate(str, maxLen) {
14
+ if (!str)
15
+ return "-";
16
+ if (str.length <= maxLen)
17
+ return str;
18
+ return str.substring(0, maxLen - 3) + "...";
19
+ }
20
+ /**
21
+ * Formats a number with commas for readability.
22
+ */
23
+ function formatNumber(num) {
24
+ if (num === null || num === undefined)
25
+ return "-";
26
+ return num.toLocaleString();
27
+ }
28
+ /**
29
+ * Formats tokens count (input + output).
30
+ */
31
+ function formatTokens(input, output) {
32
+ const inputStr = input !== null && input !== undefined ? formatNumber(input) : "-";
33
+ const outputStr = output !== null && output !== undefined ? formatNumber(output) : "-";
34
+ if (inputStr === "-" && outputStr === "-") {
35
+ return "-";
36
+ }
37
+ return `${inputStr}/${outputStr}`;
38
+ }
39
+ /**
40
+ * Formats cost in USD.
41
+ */
42
+ function formatCost(cost) {
43
+ if (cost === null || cost === undefined)
44
+ return "-";
45
+ if (cost < 0.01) {
46
+ return `$${cost.toFixed(4)}`;
47
+ }
48
+ return `$${cost.toFixed(2)}`;
49
+ }
50
+ /**
51
+ * Displays a table of LLM logs with clean formatting.
52
+ */
53
+ function displayLogsTable(logs) {
54
+ if (logs.length === 0) {
55
+ console.log("No logs found.");
56
+ return;
57
+ }
58
+ const typeWidth = Math.max("Type".length, ...logs.map((l) => truncate(l.executionType, 15).length));
59
+ const modelWidth = Math.max("Model".length, ...logs.map((l) => truncate(l.modelName, 25).length));
60
+ const tokensWidth = Math.max("Tokens (In/Out)".length, ...logs.map((l) => formatTokens(l.inputTokens, l.outputTokens).length));
61
+ const costWidth = Math.max("Cost".length, ...logs.map((l) => formatCost(l.costUsd).length));
62
+ const header = [
63
+ "Type".padEnd(typeWidth),
64
+ "Model".padEnd(modelWidth),
65
+ "Tokens (In/Out)".padEnd(tokensWidth),
66
+ "Cost".padEnd(costWidth),
67
+ ].join(" ");
68
+ console.log(header);
69
+ console.log("-".repeat(header.length));
70
+ for (const log of logs) {
71
+ const row = [
72
+ truncate(log.executionType, 15).padEnd(typeWidth),
73
+ truncate(log.modelName, 25).padEnd(modelWidth),
74
+ formatTokens(log.inputTokens, log.outputTokens).padEnd(tokensWidth),
75
+ formatCost(log.costUsd).padEnd(costWidth),
76
+ ].join(" ");
77
+ console.log(row);
78
+ }
79
+ }
80
+ /**
81
+ * Creates the `log` command group.
82
+ */
83
+ export function createLogCommand() {
84
+ const log = new Command("log")
85
+ .description("View LLM execution logs")
86
+ .addHelpText("after", `
87
+ Examples:
88
+ $ propr log list # List recent logs
89
+ $ propr log list -m claude-sonnet-4-20250514 # Filter by model
90
+ $ propr log list --failed # Show failures only
91
+ `);
92
+ // log list
93
+ log
94
+ .command("list")
95
+ .description("List LLM execution logs for auditing, debugging, and cost analysis")
96
+ .option("-l, --limit <limit>", "Maximum number of logs to show", "50")
97
+ .option("-m, --model <model>", "Filter by model name")
98
+ .option("-t, --type <type>", "Filter by execution type")
99
+ .option("--page <page>", "Page number for pagination", "1")
100
+ .option("--success", "Show only successful executions")
101
+ .option("--failed", "Show only failed executions")
102
+ .option("--agent <alias>", "Filter by agent alias")
103
+ .option("--draft <draftId>", "Filter by draft/plan ID")
104
+ .option("-j, --json", "Output as JSON for programmatic use")
105
+ .addHelpText("after", `
106
+ Examples:
107
+ $ propr log list # List recent logs
108
+ $ propr log list -l 100 --page 2 # Paginated results
109
+ $ propr log list -m claude-sonnet-4-20250514 # Filter by model
110
+ $ propr log list --failed # Show failures only
111
+ $ propr log list --draft abc123 # Filter by plan ID
112
+ $ propr log list --agent my-claude # Filter by agent
113
+ $ propr log list --json # JSON output
114
+ `)
115
+ .action(async (options) => {
116
+ try {
117
+ const listOptions = {};
118
+ const limit = parseInt(options.limit, 10);
119
+ if (!isNaN(limit) && limit > 0) {
120
+ listOptions.limit = Math.min(limit, 100);
121
+ }
122
+ const page = parseInt(options.page, 10);
123
+ if (!isNaN(page) && page > 0) {
124
+ listOptions.page = page;
125
+ }
126
+ if (options.model) {
127
+ listOptions.model = options.model;
128
+ }
129
+ if (options.type) {
130
+ listOptions.executionType = options.type;
131
+ }
132
+ if (options.success) {
133
+ listOptions.success = true;
134
+ }
135
+ else if (options.failed) {
136
+ listOptions.success = false;
137
+ }
138
+ if (options.agent) {
139
+ listOptions.agentAlias = options.agent;
140
+ }
141
+ if (options.draft) {
142
+ listOptions.draftId = options.draft;
143
+ }
144
+ const result = await listLlmLogs(listOptions);
145
+ if (printOutput(result, options.json ?? false)) {
146
+ return;
147
+ }
148
+ console.log("Fetching LLM logs...");
149
+ console.log("");
150
+ displayLogsTable(result.logs);
151
+ console.log("");
152
+ console.log(`Showing ${result.logs.length} of ${result.pagination.total} log(s) (page ${result.pagination.page}/${result.pagination.totalPages})`);
153
+ if (result.pagination.hasNextPage) {
154
+ console.log(`Use --page ${result.pagination.page + 1} to see more results`);
155
+ }
156
+ }
157
+ catch (error) {
158
+ const errorMessage = error.message;
159
+ if (errorMessage.includes("401") ||
160
+ errorMessage.includes("unauthorized")) {
161
+ console.error("Error: Unauthorized. Please run 'propr login' first.");
162
+ }
163
+ else {
164
+ console.error(`Error listing logs: ${errorMessage}`);
165
+ }
166
+ process.exit(1);
167
+ }
168
+ });
169
+ return log;
170
+ }