@pantheon.ai/agents 0.0.5 → 0.0.7
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 +889 -849
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -1,240 +1,620 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import * as process$1 from "node:process";
|
|
3
4
|
import { createCommand } from "commander";
|
|
4
|
-
import
|
|
5
|
+
import * as fs from "node:fs";
|
|
5
6
|
import path from "node:path";
|
|
6
|
-
import * as process$1 from "node:process";
|
|
7
|
-
import readline from "node:readline/promises";
|
|
8
7
|
import { multistream, pino, transport } from "pino";
|
|
9
|
-
import * as fs$1 from "node:fs";
|
|
10
8
|
import { Kysely, MysqlDialect } from "kysely";
|
|
11
9
|
import { createPool } from "mysql2";
|
|
12
10
|
import z$1, { z } from "zod";
|
|
11
|
+
import readline from "node:readline/promises";
|
|
12
|
+
import expandTilde from "expand-tilde";
|
|
13
13
|
|
|
14
14
|
//#region \0rolldown/runtime.js
|
|
15
15
|
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
16
16
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
17
17
|
|
|
18
18
|
//#endregion
|
|
19
|
-
//#region
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
19
|
+
//#region ../../node_modules/dotenv/package.json
|
|
20
|
+
var require_package = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
21
|
+
module.exports = {
|
|
22
|
+
"name": "dotenv",
|
|
23
|
+
"version": "17.2.4",
|
|
24
|
+
"description": "Loads environment variables from .env file",
|
|
25
|
+
"main": "lib/main.js",
|
|
26
|
+
"types": "lib/main.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./lib/main.d.ts",
|
|
30
|
+
"require": "./lib/main.js",
|
|
31
|
+
"default": "./lib/main.js"
|
|
32
|
+
},
|
|
33
|
+
"./config": "./config.js",
|
|
34
|
+
"./config.js": "./config.js",
|
|
35
|
+
"./lib/env-options": "./lib/env-options.js",
|
|
36
|
+
"./lib/env-options.js": "./lib/env-options.js",
|
|
37
|
+
"./lib/cli-options": "./lib/cli-options.js",
|
|
38
|
+
"./lib/cli-options.js": "./lib/cli-options.js",
|
|
39
|
+
"./package.json": "./package.json"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"dts-check": "tsc --project tests/types/tsconfig.json",
|
|
43
|
+
"lint": "standard",
|
|
44
|
+
"pretest": "npm run lint && npm run dts-check",
|
|
45
|
+
"test": "tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000",
|
|
46
|
+
"test:coverage": "tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
|
|
47
|
+
"prerelease": "npm test",
|
|
48
|
+
"release": "standard-version"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git://github.com/motdotla/dotenv.git"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://github.com/motdotla/dotenv#readme",
|
|
55
|
+
"funding": "https://dotenvx.com",
|
|
56
|
+
"keywords": [
|
|
57
|
+
"dotenv",
|
|
58
|
+
"env",
|
|
59
|
+
".env",
|
|
60
|
+
"environment",
|
|
61
|
+
"variables",
|
|
62
|
+
"config",
|
|
63
|
+
"settings"
|
|
64
|
+
],
|
|
65
|
+
"readmeFilename": "README.md",
|
|
66
|
+
"license": "BSD-2-Clause",
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/node": "^18.11.3",
|
|
69
|
+
"decache": "^4.6.2",
|
|
70
|
+
"sinon": "^14.0.1",
|
|
71
|
+
"standard": "^17.0.0",
|
|
72
|
+
"standard-version": "^9.5.0",
|
|
73
|
+
"tap": "^19.2.0",
|
|
74
|
+
"typescript": "^4.8.4"
|
|
75
|
+
},
|
|
76
|
+
"engines": { "node": ">=12" },
|
|
77
|
+
"browser": { "fs": false }
|
|
78
|
+
};
|
|
79
|
+
}));
|
|
78
80
|
|
|
79
81
|
//#endregion
|
|
80
|
-
//#region
|
|
81
|
-
var
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
//#region ../../node_modules/dotenv/lib/main.js
|
|
83
|
+
var require_main = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
84
|
+
const fs$1 = __require("fs");
|
|
85
|
+
const path$1 = __require("path");
|
|
86
|
+
const os = __require("os");
|
|
87
|
+
const crypto = __require("crypto");
|
|
88
|
+
const version = require_package().version;
|
|
89
|
+
const TIPS = [
|
|
90
|
+
"🔐 encrypt with Dotenvx: https://dotenvx.com",
|
|
91
|
+
"🔐 prevent committing .env to code: https://dotenvx.com/precommit",
|
|
92
|
+
"🔐 prevent building .env in docker: https://dotenvx.com/prebuild",
|
|
93
|
+
"📡 add observability to secrets: https://dotenvx.com/ops",
|
|
94
|
+
"👥 sync secrets across teammates & machines: https://dotenvx.com/ops",
|
|
95
|
+
"🗂️ backup and recover secrets: https://dotenvx.com/ops",
|
|
96
|
+
"✅ audit secrets and track compliance: https://dotenvx.com/ops",
|
|
97
|
+
"🔄 add secrets lifecycle management: https://dotenvx.com/ops",
|
|
98
|
+
"🔑 add access controls to secrets: https://dotenvx.com/ops",
|
|
99
|
+
"🛠️ run anywhere with `dotenvx run -- yourcommand`",
|
|
100
|
+
"⚙️ specify custom .env file path with { path: '/custom/path/.env' }",
|
|
101
|
+
"⚙️ enable debug logging with { debug: true }",
|
|
102
|
+
"⚙️ override existing env vars with { override: true }",
|
|
103
|
+
"⚙️ suppress all logs with { quiet: true }",
|
|
104
|
+
"⚙️ write to custom object with { processEnv: myObject }",
|
|
105
|
+
"⚙️ load multiple .env files with { path: ['.env.local', '.env'] }"
|
|
106
|
+
];
|
|
107
|
+
function _getRandomTip() {
|
|
108
|
+
return TIPS[Math.floor(Math.random() * TIPS.length)];
|
|
87
109
|
}
|
|
88
|
-
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
110
|
+
function parseBoolean(value) {
|
|
111
|
+
if (typeof value === "string") return ![
|
|
112
|
+
"false",
|
|
113
|
+
"0",
|
|
114
|
+
"no",
|
|
115
|
+
"off",
|
|
116
|
+
""
|
|
117
|
+
].includes(value.toLowerCase());
|
|
118
|
+
return Boolean(value);
|
|
94
119
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
let updatePromise;
|
|
98
|
-
try {
|
|
99
|
-
this.logger.info(`Starting task ${id}`);
|
|
100
|
-
const branchId = await startTask(task);
|
|
101
|
-
this.logger.info(`Started task ${id} at branch ${branchId}`);
|
|
102
|
-
updatePromise = this.updateTask({
|
|
103
|
-
status: "running",
|
|
104
|
-
id,
|
|
105
|
-
...task,
|
|
106
|
-
branch_id: branchId,
|
|
107
|
-
started_at: now
|
|
108
|
-
});
|
|
109
|
-
} catch (e) {
|
|
110
|
-
this.logger.error("Failed to start task: %s", getErrorMessage(e));
|
|
111
|
-
updatePromise = this.updateTask({
|
|
112
|
-
status: "failed",
|
|
113
|
-
id,
|
|
114
|
-
...task,
|
|
115
|
-
started_at: now,
|
|
116
|
-
ended_at: /* @__PURE__ */ new Date(),
|
|
117
|
-
error: getErrorMessage(e)
|
|
118
|
-
}).then(() => Promise.reject(e));
|
|
119
|
-
}
|
|
120
|
-
await updatePromise;
|
|
120
|
+
function supportsAnsi() {
|
|
121
|
+
return process.stdout.isTTY;
|
|
121
122
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.logger.error(`Can't fail a task that is not running. [task = ${task.id}, status = ${task.status}]`);
|
|
125
|
-
throw new Error("Can't fail a task that is not running");
|
|
126
|
-
}
|
|
127
|
-
await this.updateTask({
|
|
128
|
-
...task,
|
|
129
|
-
status: "failed",
|
|
130
|
-
ended_at: /* @__PURE__ */ new Date(),
|
|
131
|
-
error
|
|
132
|
-
});
|
|
123
|
+
function dim(text) {
|
|
124
|
+
return supportsAnsi() ? `\x1b[2m${text}\x1b[0m` : text;
|
|
133
125
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
126
|
+
const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
|
|
127
|
+
function parse(src) {
|
|
128
|
+
const obj = {};
|
|
129
|
+
let lines = src.toString();
|
|
130
|
+
lines = lines.replace(/\r\n?/gm, "\n");
|
|
131
|
+
let match;
|
|
132
|
+
while ((match = LINE.exec(lines)) != null) {
|
|
133
|
+
const key = match[1];
|
|
134
|
+
let value = match[2] || "";
|
|
135
|
+
value = value.trim();
|
|
136
|
+
const maybeQuote = value[0];
|
|
137
|
+
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
|
|
138
|
+
if (maybeQuote === "\"") {
|
|
139
|
+
value = value.replace(/\\n/g, "\n");
|
|
140
|
+
value = value.replace(/\\r/g, "\r");
|
|
141
|
+
}
|
|
142
|
+
obj[key] = value;
|
|
138
143
|
}
|
|
139
|
-
|
|
140
|
-
...task,
|
|
141
|
-
status: "completed",
|
|
142
|
-
ended_at: /* @__PURE__ */ new Date(),
|
|
143
|
-
output
|
|
144
|
-
});
|
|
144
|
+
return obj;
|
|
145
145
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
function _parseVault(options) {
|
|
147
|
+
options = options || {};
|
|
148
|
+
const vaultPath = _vaultPath(options);
|
|
149
|
+
options.path = vaultPath;
|
|
150
|
+
const result = DotenvModule.configDotenv(options);
|
|
151
|
+
if (!result.parsed) {
|
|
152
|
+
const err = /* @__PURE__ */ new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
|
|
153
|
+
err.code = "MISSING_DATA";
|
|
154
|
+
throw err;
|
|
150
155
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
+
const keys = _dotenvKey(options).split(",");
|
|
157
|
+
const length = keys.length;
|
|
158
|
+
let decrypted;
|
|
159
|
+
for (let i = 0; i < length; i++) try {
|
|
160
|
+
const attrs = _instructions(result, keys[i].trim());
|
|
161
|
+
decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
|
|
162
|
+
break;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (i + 1 >= length) throw error;
|
|
165
|
+
}
|
|
166
|
+
return DotenvModule.parse(decrypted);
|
|
156
167
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
//#endregion
|
|
160
|
-
//#region src/providers/task-list-tidb-provider.ts
|
|
161
|
-
var TaskListTidbProvider = class extends TaskListProvider {
|
|
162
|
-
db;
|
|
163
|
-
constructor(agentName, logger) {
|
|
164
|
-
super(agentName, logger);
|
|
165
|
-
this.db = new Kysely({ dialect: new MysqlDialect({ pool: createPool({
|
|
166
|
-
uri: process.env.DATABASE_URL,
|
|
167
|
-
supportBigNumbers: true,
|
|
168
|
-
bigNumberStrings: true
|
|
169
|
-
}) }) });
|
|
168
|
+
function _warn(message) {
|
|
169
|
+
console.error(`[dotenv@${version}][WARN] ${message}`);
|
|
170
170
|
}
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
function _debug(message) {
|
|
172
|
+
console.log(`[dotenv@${version}][DEBUG] ${message}`);
|
|
173
173
|
}
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
function _log(message) {
|
|
175
|
+
console.log(`[dotenv@${version}] ${message}`);
|
|
176
176
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (
|
|
180
|
-
return
|
|
177
|
+
function _dotenvKey(options) {
|
|
178
|
+
if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) return options.DOTENV_KEY;
|
|
179
|
+
if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) return process.env.DOTENV_KEY;
|
|
180
|
+
return "";
|
|
181
181
|
}
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
function _instructions(result, dotenvKey) {
|
|
183
|
+
let uri;
|
|
184
|
+
try {
|
|
185
|
+
uri = new URL(dotenvKey);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (error.code === "ERR_INVALID_URL") {
|
|
188
|
+
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
|
|
189
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
190
|
+
throw err;
|
|
191
|
+
}
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
const key = uri.password;
|
|
195
|
+
if (!key) {
|
|
196
|
+
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Missing key part");
|
|
197
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
198
|
+
throw err;
|
|
199
|
+
}
|
|
200
|
+
const environment = uri.searchParams.get("environment");
|
|
201
|
+
if (!environment) {
|
|
202
|
+
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Missing environment part");
|
|
203
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
204
|
+
throw err;
|
|
205
|
+
}
|
|
206
|
+
const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
|
|
207
|
+
const ciphertext = result.parsed[environmentKey];
|
|
208
|
+
if (!ciphertext) {
|
|
209
|
+
const err = /* @__PURE__ */ new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
|
|
210
|
+
err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
ciphertext,
|
|
215
|
+
key
|
|
216
|
+
};
|
|
184
217
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
218
|
+
function _vaultPath(options) {
|
|
219
|
+
let possibleVaultPath = null;
|
|
220
|
+
if (options && options.path && options.path.length > 0) if (Array.isArray(options.path)) {
|
|
221
|
+
for (const filepath of options.path) if (fs$1.existsSync(filepath)) possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
222
|
+
} else possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
223
|
+
else possibleVaultPath = path$1.resolve(process.cwd(), ".env.vault");
|
|
224
|
+
if (fs$1.existsSync(possibleVaultPath)) return possibleVaultPath;
|
|
225
|
+
return null;
|
|
191
226
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (taskItem == null) return null;
|
|
195
|
-
return taskItemSchema.parse(taskItem);
|
|
227
|
+
function _resolveHome(envPath) {
|
|
228
|
+
return envPath[0] === "~" ? path$1.join(os.homedir(), envPath.slice(1)) : envPath;
|
|
196
229
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (
|
|
201
|
-
|
|
230
|
+
function _configVault(options) {
|
|
231
|
+
const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
232
|
+
const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
233
|
+
if (debug || !quiet) _log("Loading env from encrypted .env.vault");
|
|
234
|
+
const parsed = DotenvModule._parseVault(options);
|
|
235
|
+
let processEnv = process.env;
|
|
236
|
+
if (options && options.processEnv != null) processEnv = options.processEnv;
|
|
237
|
+
DotenvModule.populate(processEnv, parsed, options);
|
|
238
|
+
return { parsed };
|
|
202
239
|
}
|
|
203
|
-
|
|
204
|
-
|
|
240
|
+
function configDotenv(options) {
|
|
241
|
+
const dotenvPath = path$1.resolve(process.cwd(), ".env");
|
|
242
|
+
let encoding = "utf8";
|
|
243
|
+
let processEnv = process.env;
|
|
244
|
+
if (options && options.processEnv != null) processEnv = options.processEnv;
|
|
245
|
+
let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
246
|
+
let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
247
|
+
if (options && options.encoding) encoding = options.encoding;
|
|
248
|
+
else if (debug) _debug("No encoding is specified. UTF-8 is used by default");
|
|
249
|
+
let optionPaths = [dotenvPath];
|
|
250
|
+
if (options && options.path) if (!Array.isArray(options.path)) optionPaths = [_resolveHome(options.path)];
|
|
251
|
+
else {
|
|
252
|
+
optionPaths = [];
|
|
253
|
+
for (const filepath of options.path) optionPaths.push(_resolveHome(filepath));
|
|
254
|
+
}
|
|
255
|
+
let lastError;
|
|
256
|
+
const parsedAll = {};
|
|
257
|
+
for (const path of optionPaths) try {
|
|
258
|
+
const parsed = DotenvModule.parse(fs$1.readFileSync(path, { encoding }));
|
|
259
|
+
DotenvModule.populate(parsedAll, parsed, options);
|
|
260
|
+
} catch (e) {
|
|
261
|
+
if (debug) _debug(`Failed to load ${path} ${e.message}`);
|
|
262
|
+
lastError = e;
|
|
263
|
+
}
|
|
264
|
+
const populated = DotenvModule.populate(processEnv, parsedAll, options);
|
|
265
|
+
debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
|
|
266
|
+
quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
|
|
267
|
+
if (debug || !quiet) {
|
|
268
|
+
const keysCount = Object.keys(populated).length;
|
|
269
|
+
const shortPaths = [];
|
|
270
|
+
for (const filePath of optionPaths) try {
|
|
271
|
+
const relative = path$1.relative(process.cwd(), filePath);
|
|
272
|
+
shortPaths.push(relative);
|
|
273
|
+
} catch (e) {
|
|
274
|
+
if (debug) _debug(`Failed to load ${filePath} ${e.message}`);
|
|
275
|
+
lastError = e;
|
|
276
|
+
}
|
|
277
|
+
_log(`injecting env (${keysCount}) from ${shortPaths.join(",")} ${dim(`-- tip: ${_getRandomTip()}`)}`);
|
|
278
|
+
}
|
|
279
|
+
if (lastError) return {
|
|
280
|
+
parsed: parsedAll,
|
|
281
|
+
error: lastError
|
|
282
|
+
};
|
|
283
|
+
else return { parsed: parsedAll };
|
|
205
284
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
285
|
+
function config(options) {
|
|
286
|
+
if (_dotenvKey(options).length === 0) return DotenvModule.configDotenv(options);
|
|
287
|
+
const vaultPath = _vaultPath(options);
|
|
288
|
+
if (!vaultPath) {
|
|
289
|
+
_warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
|
|
290
|
+
return DotenvModule.configDotenv(options);
|
|
291
|
+
}
|
|
292
|
+
return DotenvModule._configVault(options);
|
|
214
293
|
}
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
294
|
+
function decrypt(encrypted, keyStr) {
|
|
295
|
+
const key = Buffer.from(keyStr.slice(-64), "hex");
|
|
296
|
+
let ciphertext = Buffer.from(encrypted, "base64");
|
|
297
|
+
const nonce = ciphertext.subarray(0, 12);
|
|
298
|
+
const authTag = ciphertext.subarray(-16);
|
|
299
|
+
ciphertext = ciphertext.subarray(12, -16);
|
|
300
|
+
try {
|
|
301
|
+
const aesgcm = crypto.createDecipheriv("aes-256-gcm", key, nonce);
|
|
302
|
+
aesgcm.setAuthTag(authTag);
|
|
303
|
+
return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
|
|
304
|
+
} catch (error) {
|
|
305
|
+
const isRange = error instanceof RangeError;
|
|
306
|
+
const invalidKeyLength = error.message === "Invalid key length";
|
|
307
|
+
const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
|
|
308
|
+
if (isRange || invalidKeyLength) {
|
|
309
|
+
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
|
|
310
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
311
|
+
throw err;
|
|
312
|
+
} else if (decryptionFailed) {
|
|
313
|
+
const err = /* @__PURE__ */ new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
|
|
314
|
+
err.code = "DECRYPTION_FAILED";
|
|
315
|
+
throw err;
|
|
316
|
+
} else throw error;
|
|
317
|
+
}
|
|
228
318
|
}
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
319
|
+
function populate(processEnv, parsed, options = {}) {
|
|
320
|
+
const debug = Boolean(options && options.debug);
|
|
321
|
+
const override = Boolean(options && options.override);
|
|
322
|
+
const populated = {};
|
|
323
|
+
if (typeof parsed !== "object") {
|
|
324
|
+
const err = /* @__PURE__ */ new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
|
|
325
|
+
err.code = "OBJECT_REQUIRED";
|
|
326
|
+
throw err;
|
|
327
|
+
}
|
|
328
|
+
for (const key of Object.keys(parsed)) if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
|
|
329
|
+
if (override === true) {
|
|
330
|
+
processEnv[key] = parsed[key];
|
|
331
|
+
populated[key] = parsed[key];
|
|
332
|
+
}
|
|
333
|
+
if (debug) if (override === true) _debug(`"${key}" is already defined and WAS overwritten`);
|
|
334
|
+
else _debug(`"${key}" is already defined and was NOT overwritten`);
|
|
335
|
+
} else {
|
|
336
|
+
processEnv[key] = parsed[key];
|
|
337
|
+
populated[key] = parsed[key];
|
|
338
|
+
}
|
|
339
|
+
return populated;
|
|
232
340
|
}
|
|
233
|
-
|
|
341
|
+
const DotenvModule = {
|
|
342
|
+
configDotenv,
|
|
343
|
+
_configVault,
|
|
344
|
+
_parseVault,
|
|
345
|
+
config,
|
|
346
|
+
decrypt,
|
|
347
|
+
parse,
|
|
348
|
+
populate
|
|
349
|
+
};
|
|
350
|
+
module.exports.configDotenv = DotenvModule.configDotenv;
|
|
351
|
+
module.exports._configVault = DotenvModule._configVault;
|
|
352
|
+
module.exports._parseVault = DotenvModule._parseVault;
|
|
353
|
+
module.exports.config = DotenvModule.config;
|
|
354
|
+
module.exports.decrypt = DotenvModule.decrypt;
|
|
355
|
+
module.exports.parse = DotenvModule.parse;
|
|
356
|
+
module.exports.populate = DotenvModule.populate;
|
|
357
|
+
module.exports = DotenvModule;
|
|
358
|
+
}));
|
|
234
359
|
|
|
235
360
|
//#endregion
|
|
236
|
-
//#region
|
|
237
|
-
var
|
|
361
|
+
//#region ../../node_modules/dotenv/lib/env-options.js
|
|
362
|
+
var require_env_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
363
|
+
const options = {};
|
|
364
|
+
if (process.env.DOTENV_CONFIG_ENCODING != null) options.encoding = process.env.DOTENV_CONFIG_ENCODING;
|
|
365
|
+
if (process.env.DOTENV_CONFIG_PATH != null) options.path = process.env.DOTENV_CONFIG_PATH;
|
|
366
|
+
if (process.env.DOTENV_CONFIG_QUIET != null) options.quiet = process.env.DOTENV_CONFIG_QUIET;
|
|
367
|
+
if (process.env.DOTENV_CONFIG_DEBUG != null) options.debug = process.env.DOTENV_CONFIG_DEBUG;
|
|
368
|
+
if (process.env.DOTENV_CONFIG_OVERRIDE != null) options.override = process.env.DOTENV_CONFIG_OVERRIDE;
|
|
369
|
+
if (process.env.DOTENV_CONFIG_DOTENV_KEY != null) options.DOTENV_KEY = process.env.DOTENV_CONFIG_DOTENV_KEY;
|
|
370
|
+
module.exports = options;
|
|
371
|
+
}));
|
|
372
|
+
|
|
373
|
+
//#endregion
|
|
374
|
+
//#region ../../node_modules/dotenv/lib/cli-options.js
|
|
375
|
+
var require_cli_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
376
|
+
const re = /^dotenv_config_(encoding|path|quiet|debug|override|DOTENV_KEY)=(.+)$/;
|
|
377
|
+
module.exports = function optionMatcher(args) {
|
|
378
|
+
const options = args.reduce(function(acc, cur) {
|
|
379
|
+
const matches = cur.match(re);
|
|
380
|
+
if (matches) acc[matches[1]] = matches[2];
|
|
381
|
+
return acc;
|
|
382
|
+
}, {});
|
|
383
|
+
if (!("quiet" in options)) options.quiet = "true";
|
|
384
|
+
return options;
|
|
385
|
+
};
|
|
386
|
+
}));
|
|
387
|
+
|
|
388
|
+
//#endregion
|
|
389
|
+
//#region ../../node_modules/dotenv/config.js
|
|
390
|
+
(function() {
|
|
391
|
+
require_main().config(Object.assign({}, require_env_options(), require_cli_options()(process.argv)));
|
|
392
|
+
})();
|
|
393
|
+
|
|
394
|
+
//#endregion
|
|
395
|
+
//#region package.json
|
|
396
|
+
var version = "0.0.7";
|
|
397
|
+
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region src/schemas/task-list.ts
|
|
400
|
+
const taskItemSchema = z.discriminatedUnion("status", [
|
|
401
|
+
z.object({
|
|
402
|
+
status: z.literal("pending"),
|
|
403
|
+
id: z.string(),
|
|
404
|
+
task: z.string(),
|
|
405
|
+
project_id: z.uuid(),
|
|
406
|
+
base_branch_id: z.uuid(),
|
|
407
|
+
queued_at: z.coerce.date()
|
|
408
|
+
}),
|
|
409
|
+
z.object({
|
|
410
|
+
status: z.literal("running"),
|
|
411
|
+
id: z.string(),
|
|
412
|
+
task: z.string(),
|
|
413
|
+
project_id: z.uuid(),
|
|
414
|
+
base_branch_id: z.uuid(),
|
|
415
|
+
branch_id: z.uuid(),
|
|
416
|
+
queued_at: z.coerce.date(),
|
|
417
|
+
started_at: z.coerce.date()
|
|
418
|
+
}),
|
|
419
|
+
z.object({
|
|
420
|
+
status: z.literal("completed"),
|
|
421
|
+
id: z.string(),
|
|
422
|
+
task: z.string(),
|
|
423
|
+
project_id: z.uuid(),
|
|
424
|
+
base_branch_id: z.uuid(),
|
|
425
|
+
branch_id: z.uuid(),
|
|
426
|
+
queued_at: z.coerce.date(),
|
|
427
|
+
started_at: z.coerce.date(),
|
|
428
|
+
ended_at: z.coerce.date(),
|
|
429
|
+
output: z.string()
|
|
430
|
+
}),
|
|
431
|
+
z.object({
|
|
432
|
+
status: z.literal("failed"),
|
|
433
|
+
id: z.string(),
|
|
434
|
+
task: z.string(),
|
|
435
|
+
project_id: z.uuid(),
|
|
436
|
+
base_branch_id: z.uuid(),
|
|
437
|
+
branch_id: z.uuid().nullable().optional(),
|
|
438
|
+
queued_at: z.coerce.date(),
|
|
439
|
+
started_at: z.coerce.date().nullable().optional(),
|
|
440
|
+
ended_at: z.coerce.date().nullable().optional(),
|
|
441
|
+
error: z.string()
|
|
442
|
+
}),
|
|
443
|
+
z.object({
|
|
444
|
+
status: z.literal("cancelled"),
|
|
445
|
+
id: z.string(),
|
|
446
|
+
task: z.string(),
|
|
447
|
+
project_id: z.uuid(),
|
|
448
|
+
queued_at: z.coerce.date(),
|
|
449
|
+
cancelled_at: z.coerce.date()
|
|
450
|
+
})
|
|
451
|
+
]);
|
|
452
|
+
|
|
453
|
+
//#endregion
|
|
454
|
+
//#region src/utils/get-error-message.ts
|
|
455
|
+
function getErrorMessage(error) {
|
|
456
|
+
return String(error?.message ?? error);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
//#endregion
|
|
460
|
+
//#region src/providers/task-list-provider.ts
|
|
461
|
+
var TaskListProvider = class {
|
|
462
|
+
logger;
|
|
463
|
+
agentName;
|
|
464
|
+
constructor(agentName, logger) {
|
|
465
|
+
this.agentName = agentName;
|
|
466
|
+
this.logger = logger;
|
|
467
|
+
}
|
|
468
|
+
async createTask(params) {
|
|
469
|
+
return await this.insertTask({
|
|
470
|
+
status: "pending",
|
|
471
|
+
...params,
|
|
472
|
+
queued_at: /* @__PURE__ */ new Date()
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
async startTask({ status, id, ...task }, startTask) {
|
|
476
|
+
const now = /* @__PURE__ */ new Date();
|
|
477
|
+
let updatePromise;
|
|
478
|
+
try {
|
|
479
|
+
this.logger.info(`Starting task ${id}`);
|
|
480
|
+
const branchId = await startTask(task);
|
|
481
|
+
this.logger.info(`Started task ${id} at branch ${branchId}`);
|
|
482
|
+
updatePromise = this.updateTask({
|
|
483
|
+
status: "running",
|
|
484
|
+
id,
|
|
485
|
+
...task,
|
|
486
|
+
branch_id: branchId,
|
|
487
|
+
started_at: now
|
|
488
|
+
});
|
|
489
|
+
} catch (e) {
|
|
490
|
+
this.logger.error("Failed to start task: %s", getErrorMessage(e));
|
|
491
|
+
updatePromise = this.updateTask({
|
|
492
|
+
status: "failed",
|
|
493
|
+
id,
|
|
494
|
+
...task,
|
|
495
|
+
started_at: now,
|
|
496
|
+
ended_at: /* @__PURE__ */ new Date(),
|
|
497
|
+
error: getErrorMessage(e)
|
|
498
|
+
}).then(() => Promise.reject(e));
|
|
499
|
+
}
|
|
500
|
+
await updatePromise;
|
|
501
|
+
}
|
|
502
|
+
async failTask(task, error) {
|
|
503
|
+
if (task.status !== "running") {
|
|
504
|
+
this.logger.error(`Can't fail a task that is not running. [task = ${task.id}, status = ${task.status}]`);
|
|
505
|
+
throw new Error("Can't fail a task that is not running");
|
|
506
|
+
}
|
|
507
|
+
await this.updateTask({
|
|
508
|
+
...task,
|
|
509
|
+
status: "failed",
|
|
510
|
+
ended_at: /* @__PURE__ */ new Date(),
|
|
511
|
+
error
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
async completeTask(task, output) {
|
|
515
|
+
if (task.status !== "running") {
|
|
516
|
+
this.logger.error(`Can't complete a task that is not running. [task = ${task.id}, status = ${task.status}]`);
|
|
517
|
+
throw new Error("Can't complete a task that is not running");
|
|
518
|
+
}
|
|
519
|
+
await this.updateTask({
|
|
520
|
+
...task,
|
|
521
|
+
status: "completed",
|
|
522
|
+
ended_at: /* @__PURE__ */ new Date(),
|
|
523
|
+
output
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
async cancelTask(task) {
|
|
527
|
+
if (task.status !== "pending") {
|
|
528
|
+
this.logger.error(`Can't cancel a task that is not pending. [task = ${task.id}, status = ${task.status}]`);
|
|
529
|
+
throw new Error("Can't cancel a task that is not pending");
|
|
530
|
+
}
|
|
531
|
+
await this.updateTask({
|
|
532
|
+
...task,
|
|
533
|
+
status: "cancelled",
|
|
534
|
+
cancelled_at: /* @__PURE__ */ new Date()
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/providers/task-list-tidb-provider.ts
|
|
541
|
+
var TaskListTidbProvider = class extends TaskListProvider {
|
|
542
|
+
db;
|
|
543
|
+
constructor(agentName, logger) {
|
|
544
|
+
super(agentName, logger);
|
|
545
|
+
this.db = new Kysely({ dialect: new MysqlDialect({ pool: createPool({
|
|
546
|
+
uri: process.env.DATABASE_URL,
|
|
547
|
+
supportBigNumbers: true,
|
|
548
|
+
bigNumberStrings: true
|
|
549
|
+
}) }) });
|
|
550
|
+
}
|
|
551
|
+
async close() {
|
|
552
|
+
await this.db.destroy();
|
|
553
|
+
}
|
|
554
|
+
selectTask() {
|
|
555
|
+
return this.db.selectFrom("task").selectAll().where("agent", "=", this.agentName);
|
|
556
|
+
}
|
|
557
|
+
async getAgentConfig(projectId) {
|
|
558
|
+
const config = await this.db.selectFrom("agent_project_config").selectAll().where("project_id", "=", projectId).where("agent", "=", this.agentName).executeTakeFirst();
|
|
559
|
+
if (config == null) return null;
|
|
560
|
+
return config;
|
|
561
|
+
}
|
|
562
|
+
async getAgentConfigs() {
|
|
563
|
+
return await this.db.selectFrom("agent_project_config").selectAll().where("agent", "=", this.agentName).execute();
|
|
564
|
+
}
|
|
565
|
+
async setAgentConfig({ skills, ...config }) {
|
|
566
|
+
await this.db.insertInto("agent_project_config").values({
|
|
567
|
+
agent: this.agentName,
|
|
568
|
+
skills: JSON.stringify(skills),
|
|
569
|
+
...config
|
|
570
|
+
}).execute();
|
|
571
|
+
}
|
|
572
|
+
async getTask(taskId) {
|
|
573
|
+
const taskItem = await this.selectTask().where("id", "=", taskId).executeTakeFirst();
|
|
574
|
+
if (taskItem == null) return null;
|
|
575
|
+
return taskItemSchema.parse(taskItem);
|
|
576
|
+
}
|
|
577
|
+
async getTasks({ status, order_by, order_direction = "asc" } = {}) {
|
|
578
|
+
let builder = this.selectTask();
|
|
579
|
+
if (status && status.length > 0) builder = builder.where("status", "in", status);
|
|
580
|
+
if (order_by) builder = builder.orderBy(order_by, order_direction);
|
|
581
|
+
return (await builder.execute()).map((item) => taskItemSchema.parse(item));
|
|
582
|
+
}
|
|
583
|
+
async listAgentNames() {
|
|
584
|
+
return (await this.db.selectFrom("task").select("agent").distinct().execute()).map((item) => item.agent);
|
|
585
|
+
}
|
|
586
|
+
async updateTask(taskItem) {
|
|
587
|
+
const { id, started_at, ended_at, queued_at, ...rest } = taskItem;
|
|
588
|
+
await this.db.updateTable("task").set({
|
|
589
|
+
started_at,
|
|
590
|
+
ended_at,
|
|
591
|
+
queued_at,
|
|
592
|
+
...rest
|
|
593
|
+
}).where("id", "=", id).where("agent", "=", this.agentName).execute();
|
|
594
|
+
}
|
|
595
|
+
async insertTask(taskItem) {
|
|
596
|
+
const { started_at, ended_at, queued_at, ...rest } = taskItem;
|
|
597
|
+
const { insertId } = await this.db.insertInto("task").values({
|
|
598
|
+
agent: this.agentName,
|
|
599
|
+
started_at,
|
|
600
|
+
ended_at,
|
|
601
|
+
queued_at,
|
|
602
|
+
...rest
|
|
603
|
+
}).executeTakeFirstOrThrow();
|
|
604
|
+
return {
|
|
605
|
+
...taskItem,
|
|
606
|
+
id: String(insertId)
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
async deleteTask(taskId) {
|
|
610
|
+
const result = await this.db.deleteFrom("task").where("id", "=", taskId).where("agent", "=", this.agentName).executeTakeFirst();
|
|
611
|
+
return Number(result.numDeletedRows ?? 0) > 0;
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
//#endregion
|
|
616
|
+
//#region ../sdk/src/lib/stream-parser.ts
|
|
617
|
+
var StreamResponseParser = class {
|
|
238
618
|
options;
|
|
239
619
|
constructor(options) {
|
|
240
620
|
this.options = options;
|
|
@@ -1473,7 +1853,7 @@ async function startPendingTask(provider, task, logger) {
|
|
|
1473
1853
|
async function runAgent(name, options, logger) {
|
|
1474
1854
|
const agentDir = path.join(options.dataDir, "agents", name);
|
|
1475
1855
|
const pidFile = path.join(agentDir, "pid");
|
|
1476
|
-
await fs
|
|
1856
|
+
await fs.promises.mkdir(agentDir, { recursive: true });
|
|
1477
1857
|
await assertsSingleton(logger, pidFile);
|
|
1478
1858
|
await startTaskListLoop(name, { loopInterval: options.loopInterval }, logger);
|
|
1479
1859
|
}
|
|
@@ -1510,8 +1890,7 @@ async function configAgent(name, options) {
|
|
|
1510
1890
|
projectId: options.projectId,
|
|
1511
1891
|
branchId: options.rootBranchId,
|
|
1512
1892
|
agent: "codex",
|
|
1513
|
-
prompt: `You must follow these instructions to setup base branch for role "${options.role}":
|
|
1514
|
-
0. Configure Git credentials via ~/.setup-git.sh
|
|
1893
|
+
prompt: `You must follow these instructions to setup base branch for agent role "${options.role}":
|
|
1515
1894
|
1. Clone the main branch from https://github.com/pingcap-inc/pantheon-agents to a temporary directory.
|
|
1516
1895
|
2. Copy the pantheon-agents/roles/${options.role}/AGENTS.md to \`<workspace>/AGENTS.md\`.
|
|
1517
1896
|
3. Copy the pantheon-agents/roles/${options.role}/skills directory to \`<workspace>/.codex/skills\` if exists.
|
|
@@ -1641,488 +2020,241 @@ async function showTasksForAgents(options) {
|
|
|
1641
2020
|
return (await showTasks(agent, {
|
|
1642
2021
|
status: options.status,
|
|
1643
2022
|
orderBy: options.orderBy,
|
|
1644
|
-
orderDirection: options.orderDirection
|
|
1645
|
-
})).map((task) => ({
|
|
1646
|
-
agent,
|
|
1647
|
-
task
|
|
1648
|
-
}));
|
|
1649
|
-
})).then((groups) => groups.flat());
|
|
1650
|
-
const orderKey = options.orderBy ?? "queued_at";
|
|
1651
|
-
const orderDirection = options.orderDirection ?? "desc";
|
|
1652
|
-
tasksWithAgent.sort((a, b) => {
|
|
1653
|
-
const aValue = a.task[orderKey];
|
|
1654
|
-
const bValue = b.task[orderKey];
|
|
1655
|
-
const aTime = aValue instanceof Date ? aValue.getTime() : 0;
|
|
1656
|
-
const bTime = bValue instanceof Date ? bValue.getTime() : 0;
|
|
1657
|
-
return orderDirection === "asc" ? aTime - bTime : bTime - aTime;
|
|
1658
|
-
});
|
|
1659
|
-
if (options.limit != null) return tasksWithAgent.slice(0, options.limit);
|
|
1660
|
-
return tasksWithAgent;
|
|
1661
|
-
}
|
|
1662
|
-
function formatRelativeTime(value) {
|
|
1663
|
-
if (!value) return "";
|
|
1664
|
-
const diffMs = Date.now() - value.getTime();
|
|
1665
|
-
const absMs = Math.abs(diffMs);
|
|
1666
|
-
const tense = diffMs >= 0 ? "ago" : "from now";
|
|
1667
|
-
const seconds = Math.floor(absMs / 1e3);
|
|
1668
|
-
const minutes = Math.floor(seconds / 60);
|
|
1669
|
-
const hours = Math.floor(minutes / 60);
|
|
1670
|
-
const days = Math.floor(hours / 24);
|
|
1671
|
-
if (days > 0) return `${days}d ${tense}`;
|
|
1672
|
-
if (hours > 0) return `${hours}h ${tense}`;
|
|
1673
|
-
if (minutes > 0) return `${minutes}m ${tense}`;
|
|
1674
|
-
return `${seconds}s ${tense}`;
|
|
1675
|
-
}
|
|
1676
|
-
function formatDate(value) {
|
|
1677
|
-
return value ? value.toISOString() : "";
|
|
1678
|
-
}
|
|
1679
|
-
function truncateText(value, maxLength) {
|
|
1680
|
-
if (value.length <= maxLength) return value;
|
|
1681
|
-
return `${value.slice(0, Math.max(0, maxLength - 1))}…`;
|
|
1682
|
-
}
|
|
1683
|
-
function colorText(value, colorCode) {
|
|
1684
|
-
return `\u001b[${colorCode}m${value}\u001b[0m`;
|
|
1685
|
-
}
|
|
1686
|
-
function formatStatus(status, useColor) {
|
|
1687
|
-
if (!useColor) return status;
|
|
1688
|
-
switch (status) {
|
|
1689
|
-
case "pending": return colorText(status, 33);
|
|
1690
|
-
case "running": return colorText(status, 36);
|
|
1691
|
-
case "completed": return colorText(status, 32);
|
|
1692
|
-
case "failed": return colorText(status, 31);
|
|
1693
|
-
case "cancelled": return colorText(status, 90);
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
function formatConciseTaskLine(options) {
|
|
1697
|
-
const { agent, task, maxTaskLength, useColor } = options;
|
|
1698
|
-
let timestamp = task.queued_at;
|
|
1699
|
-
if (task.status === "running") timestamp = task.started_at;
|
|
1700
|
-
if (task.status === "completed" || task.status === "failed") timestamp = task.ended_at;
|
|
1701
|
-
if (task.status === "cancelled") timestamp = task.cancelled_at ?? task.queued_at;
|
|
1702
|
-
const statusText = formatStatus(task.status, useColor);
|
|
1703
|
-
const timeText = formatRelativeTime(timestamp);
|
|
1704
|
-
const taskText = truncateText(task.task, maxTaskLength);
|
|
1705
|
-
return [
|
|
1706
|
-
agent,
|
|
1707
|
-
statusText,
|
|
1708
|
-
task.id,
|
|
1709
|
-
timeText,
|
|
1710
|
-
taskText
|
|
1711
|
-
].filter((part) => part !== "").join(" ");
|
|
1712
|
-
}
|
|
1713
|
-
function formatTaskTableRow(options) {
|
|
1714
|
-
const { agent, task, maxTaskLength } = options;
|
|
1715
|
-
const branchId = "branch_id" in task ? task.branch_id ?? "" : "";
|
|
1716
|
-
const startedAt = "started_at" in task ? task.started_at : void 0;
|
|
1717
|
-
const endedAt = "ended_at" in task ? task.ended_at : void 0;
|
|
1718
|
-
return {
|
|
1719
|
-
agent,
|
|
1720
|
-
id: task.id,
|
|
1721
|
-
status: task.status,
|
|
1722
|
-
project_id: task.project_id,
|
|
1723
|
-
branch_id: branchId,
|
|
1724
|
-
queued_at: formatDate(task.queued_at),
|
|
1725
|
-
started_at: formatDate(startedAt),
|
|
1726
|
-
ended_at: formatDate(endedAt),
|
|
1727
|
-
task: truncateText(task.task, maxTaskLength)
|
|
1728
|
-
};
|
|
1729
|
-
}
|
|
1730
|
-
async function assertsSingleton(logger, pidFile) {
|
|
1731
|
-
try {
|
|
1732
|
-
const pid = await fs$1.promises.readFile(pidFile, "utf-8");
|
|
1733
|
-
process.kill(parseInt(pid), 0);
|
|
1734
|
-
console.error("Failed to assert singleton agent process:");
|
|
1735
|
-
process.exit(1);
|
|
1736
|
-
} catch (e) {
|
|
1737
|
-
await fs$1.promises.writeFile(pidFile, process.pid.toString());
|
|
1738
|
-
process.on("exit", () => {
|
|
1739
|
-
fs$1.promises.rm(pidFile);
|
|
1740
|
-
});
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
//#endregion
|
|
1745
|
-
//#region ../../node_modules/dotenv/package.json
|
|
1746
|
-
var require_package = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1747
|
-
module.exports = {
|
|
1748
|
-
"name": "dotenv",
|
|
1749
|
-
"version": "17.2.4",
|
|
1750
|
-
"description": "Loads environment variables from .env file",
|
|
1751
|
-
"main": "lib/main.js",
|
|
1752
|
-
"types": "lib/main.d.ts",
|
|
1753
|
-
"exports": {
|
|
1754
|
-
".": {
|
|
1755
|
-
"types": "./lib/main.d.ts",
|
|
1756
|
-
"require": "./lib/main.js",
|
|
1757
|
-
"default": "./lib/main.js"
|
|
1758
|
-
},
|
|
1759
|
-
"./config": "./config.js",
|
|
1760
|
-
"./config.js": "./config.js",
|
|
1761
|
-
"./lib/env-options": "./lib/env-options.js",
|
|
1762
|
-
"./lib/env-options.js": "./lib/env-options.js",
|
|
1763
|
-
"./lib/cli-options": "./lib/cli-options.js",
|
|
1764
|
-
"./lib/cli-options.js": "./lib/cli-options.js",
|
|
1765
|
-
"./package.json": "./package.json"
|
|
1766
|
-
},
|
|
1767
|
-
"scripts": {
|
|
1768
|
-
"dts-check": "tsc --project tests/types/tsconfig.json",
|
|
1769
|
-
"lint": "standard",
|
|
1770
|
-
"pretest": "npm run lint && npm run dts-check",
|
|
1771
|
-
"test": "tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000",
|
|
1772
|
-
"test:coverage": "tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
|
|
1773
|
-
"prerelease": "npm test",
|
|
1774
|
-
"release": "standard-version"
|
|
1775
|
-
},
|
|
1776
|
-
"repository": {
|
|
1777
|
-
"type": "git",
|
|
1778
|
-
"url": "git://github.com/motdotla/dotenv.git"
|
|
1779
|
-
},
|
|
1780
|
-
"homepage": "https://github.com/motdotla/dotenv#readme",
|
|
1781
|
-
"funding": "https://dotenvx.com",
|
|
1782
|
-
"keywords": [
|
|
1783
|
-
"dotenv",
|
|
1784
|
-
"env",
|
|
1785
|
-
".env",
|
|
1786
|
-
"environment",
|
|
1787
|
-
"variables",
|
|
1788
|
-
"config",
|
|
1789
|
-
"settings"
|
|
1790
|
-
],
|
|
1791
|
-
"readmeFilename": "README.md",
|
|
1792
|
-
"license": "BSD-2-Clause",
|
|
1793
|
-
"devDependencies": {
|
|
1794
|
-
"@types/node": "^18.11.3",
|
|
1795
|
-
"decache": "^4.6.2",
|
|
1796
|
-
"sinon": "^14.0.1",
|
|
1797
|
-
"standard": "^17.0.0",
|
|
1798
|
-
"standard-version": "^9.5.0",
|
|
1799
|
-
"tap": "^19.2.0",
|
|
1800
|
-
"typescript": "^4.8.4"
|
|
1801
|
-
},
|
|
1802
|
-
"engines": { "node": ">=12" },
|
|
1803
|
-
"browser": { "fs": false }
|
|
1804
|
-
};
|
|
1805
|
-
}));
|
|
1806
|
-
|
|
1807
|
-
//#endregion
|
|
1808
|
-
//#region ../../node_modules/dotenv/lib/main.js
|
|
1809
|
-
var require_main = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1810
|
-
const fs = __require("fs");
|
|
1811
|
-
const path$1 = __require("path");
|
|
1812
|
-
const os = __require("os");
|
|
1813
|
-
const crypto = __require("crypto");
|
|
1814
|
-
const version = require_package().version;
|
|
1815
|
-
const TIPS = [
|
|
1816
|
-
"🔐 encrypt with Dotenvx: https://dotenvx.com",
|
|
1817
|
-
"🔐 prevent committing .env to code: https://dotenvx.com/precommit",
|
|
1818
|
-
"🔐 prevent building .env in docker: https://dotenvx.com/prebuild",
|
|
1819
|
-
"📡 add observability to secrets: https://dotenvx.com/ops",
|
|
1820
|
-
"👥 sync secrets across teammates & machines: https://dotenvx.com/ops",
|
|
1821
|
-
"🗂️ backup and recover secrets: https://dotenvx.com/ops",
|
|
1822
|
-
"✅ audit secrets and track compliance: https://dotenvx.com/ops",
|
|
1823
|
-
"🔄 add secrets lifecycle management: https://dotenvx.com/ops",
|
|
1824
|
-
"🔑 add access controls to secrets: https://dotenvx.com/ops",
|
|
1825
|
-
"🛠️ run anywhere with `dotenvx run -- yourcommand`",
|
|
1826
|
-
"⚙️ specify custom .env file path with { path: '/custom/path/.env' }",
|
|
1827
|
-
"⚙️ enable debug logging with { debug: true }",
|
|
1828
|
-
"⚙️ override existing env vars with { override: true }",
|
|
1829
|
-
"⚙️ suppress all logs with { quiet: true }",
|
|
1830
|
-
"⚙️ write to custom object with { processEnv: myObject }",
|
|
1831
|
-
"⚙️ load multiple .env files with { path: ['.env.local', '.env'] }"
|
|
1832
|
-
];
|
|
1833
|
-
function _getRandomTip() {
|
|
1834
|
-
return TIPS[Math.floor(Math.random() * TIPS.length)];
|
|
1835
|
-
}
|
|
1836
|
-
function parseBoolean(value) {
|
|
1837
|
-
if (typeof value === "string") return ![
|
|
1838
|
-
"false",
|
|
1839
|
-
"0",
|
|
1840
|
-
"no",
|
|
1841
|
-
"off",
|
|
1842
|
-
""
|
|
1843
|
-
].includes(value.toLowerCase());
|
|
1844
|
-
return Boolean(value);
|
|
1845
|
-
}
|
|
1846
|
-
function supportsAnsi() {
|
|
1847
|
-
return process.stdout.isTTY;
|
|
1848
|
-
}
|
|
1849
|
-
function dim(text) {
|
|
1850
|
-
return supportsAnsi() ? `\x1b[2m${text}\x1b[0m` : text;
|
|
1851
|
-
}
|
|
1852
|
-
const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
|
|
1853
|
-
function parse(src) {
|
|
1854
|
-
const obj = {};
|
|
1855
|
-
let lines = src.toString();
|
|
1856
|
-
lines = lines.replace(/\r\n?/gm, "\n");
|
|
1857
|
-
let match;
|
|
1858
|
-
while ((match = LINE.exec(lines)) != null) {
|
|
1859
|
-
const key = match[1];
|
|
1860
|
-
let value = match[2] || "";
|
|
1861
|
-
value = value.trim();
|
|
1862
|
-
const maybeQuote = value[0];
|
|
1863
|
-
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
|
|
1864
|
-
if (maybeQuote === "\"") {
|
|
1865
|
-
value = value.replace(/\\n/g, "\n");
|
|
1866
|
-
value = value.replace(/\\r/g, "\r");
|
|
1867
|
-
}
|
|
1868
|
-
obj[key] = value;
|
|
1869
|
-
}
|
|
1870
|
-
return obj;
|
|
1871
|
-
}
|
|
1872
|
-
function _parseVault(options) {
|
|
1873
|
-
options = options || {};
|
|
1874
|
-
const vaultPath = _vaultPath(options);
|
|
1875
|
-
options.path = vaultPath;
|
|
1876
|
-
const result = DotenvModule.configDotenv(options);
|
|
1877
|
-
if (!result.parsed) {
|
|
1878
|
-
const err = /* @__PURE__ */ new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
|
|
1879
|
-
err.code = "MISSING_DATA";
|
|
1880
|
-
throw err;
|
|
1881
|
-
}
|
|
1882
|
-
const keys = _dotenvKey(options).split(",");
|
|
1883
|
-
const length = keys.length;
|
|
1884
|
-
let decrypted;
|
|
1885
|
-
for (let i = 0; i < length; i++) try {
|
|
1886
|
-
const attrs = _instructions(result, keys[i].trim());
|
|
1887
|
-
decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
|
|
1888
|
-
break;
|
|
1889
|
-
} catch (error) {
|
|
1890
|
-
if (i + 1 >= length) throw error;
|
|
1891
|
-
}
|
|
1892
|
-
return DotenvModule.parse(decrypted);
|
|
1893
|
-
}
|
|
1894
|
-
function _warn(message) {
|
|
1895
|
-
console.error(`[dotenv@${version}][WARN] ${message}`);
|
|
1896
|
-
}
|
|
1897
|
-
function _debug(message) {
|
|
1898
|
-
console.log(`[dotenv@${version}][DEBUG] ${message}`);
|
|
1899
|
-
}
|
|
1900
|
-
function _log(message) {
|
|
1901
|
-
console.log(`[dotenv@${version}] ${message}`);
|
|
1902
|
-
}
|
|
1903
|
-
function _dotenvKey(options) {
|
|
1904
|
-
if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) return options.DOTENV_KEY;
|
|
1905
|
-
if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) return process.env.DOTENV_KEY;
|
|
1906
|
-
return "";
|
|
1907
|
-
}
|
|
1908
|
-
function _instructions(result, dotenvKey) {
|
|
1909
|
-
let uri;
|
|
1910
|
-
try {
|
|
1911
|
-
uri = new URL(dotenvKey);
|
|
1912
|
-
} catch (error) {
|
|
1913
|
-
if (error.code === "ERR_INVALID_URL") {
|
|
1914
|
-
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
|
|
1915
|
-
err.code = "INVALID_DOTENV_KEY";
|
|
1916
|
-
throw err;
|
|
1917
|
-
}
|
|
1918
|
-
throw error;
|
|
1919
|
-
}
|
|
1920
|
-
const key = uri.password;
|
|
1921
|
-
if (!key) {
|
|
1922
|
-
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Missing key part");
|
|
1923
|
-
err.code = "INVALID_DOTENV_KEY";
|
|
1924
|
-
throw err;
|
|
1925
|
-
}
|
|
1926
|
-
const environment = uri.searchParams.get("environment");
|
|
1927
|
-
if (!environment) {
|
|
1928
|
-
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Missing environment part");
|
|
1929
|
-
err.code = "INVALID_DOTENV_KEY";
|
|
1930
|
-
throw err;
|
|
1931
|
-
}
|
|
1932
|
-
const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
|
|
1933
|
-
const ciphertext = result.parsed[environmentKey];
|
|
1934
|
-
if (!ciphertext) {
|
|
1935
|
-
const err = /* @__PURE__ */ new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
|
|
1936
|
-
err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
|
|
1937
|
-
throw err;
|
|
1938
|
-
}
|
|
1939
|
-
return {
|
|
1940
|
-
ciphertext,
|
|
1941
|
-
key
|
|
1942
|
-
};
|
|
1943
|
-
}
|
|
1944
|
-
function _vaultPath(options) {
|
|
1945
|
-
let possibleVaultPath = null;
|
|
1946
|
-
if (options && options.path && options.path.length > 0) if (Array.isArray(options.path)) {
|
|
1947
|
-
for (const filepath of options.path) if (fs.existsSync(filepath)) possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
1948
|
-
} else possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
1949
|
-
else possibleVaultPath = path$1.resolve(process.cwd(), ".env.vault");
|
|
1950
|
-
if (fs.existsSync(possibleVaultPath)) return possibleVaultPath;
|
|
1951
|
-
return null;
|
|
1952
|
-
}
|
|
1953
|
-
function _resolveHome(envPath) {
|
|
1954
|
-
return envPath[0] === "~" ? path$1.join(os.homedir(), envPath.slice(1)) : envPath;
|
|
1955
|
-
}
|
|
1956
|
-
function _configVault(options) {
|
|
1957
|
-
const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
1958
|
-
const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
1959
|
-
if (debug || !quiet) _log("Loading env from encrypted .env.vault");
|
|
1960
|
-
const parsed = DotenvModule._parseVault(options);
|
|
1961
|
-
let processEnv = process.env;
|
|
1962
|
-
if (options && options.processEnv != null) processEnv = options.processEnv;
|
|
1963
|
-
DotenvModule.populate(processEnv, parsed, options);
|
|
1964
|
-
return { parsed };
|
|
1965
|
-
}
|
|
1966
|
-
function configDotenv(options) {
|
|
1967
|
-
const dotenvPath = path$1.resolve(process.cwd(), ".env");
|
|
1968
|
-
let encoding = "utf8";
|
|
1969
|
-
let processEnv = process.env;
|
|
1970
|
-
if (options && options.processEnv != null) processEnv = options.processEnv;
|
|
1971
|
-
let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
1972
|
-
let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
1973
|
-
if (options && options.encoding) encoding = options.encoding;
|
|
1974
|
-
else if (debug) _debug("No encoding is specified. UTF-8 is used by default");
|
|
1975
|
-
let optionPaths = [dotenvPath];
|
|
1976
|
-
if (options && options.path) if (!Array.isArray(options.path)) optionPaths = [_resolveHome(options.path)];
|
|
1977
|
-
else {
|
|
1978
|
-
optionPaths = [];
|
|
1979
|
-
for (const filepath of options.path) optionPaths.push(_resolveHome(filepath));
|
|
1980
|
-
}
|
|
1981
|
-
let lastError;
|
|
1982
|
-
const parsedAll = {};
|
|
1983
|
-
for (const path of optionPaths) try {
|
|
1984
|
-
const parsed = DotenvModule.parse(fs.readFileSync(path, { encoding }));
|
|
1985
|
-
DotenvModule.populate(parsedAll, parsed, options);
|
|
1986
|
-
} catch (e) {
|
|
1987
|
-
if (debug) _debug(`Failed to load ${path} ${e.message}`);
|
|
1988
|
-
lastError = e;
|
|
1989
|
-
}
|
|
1990
|
-
const populated = DotenvModule.populate(processEnv, parsedAll, options);
|
|
1991
|
-
debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
|
|
1992
|
-
quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
|
|
1993
|
-
if (debug || !quiet) {
|
|
1994
|
-
const keysCount = Object.keys(populated).length;
|
|
1995
|
-
const shortPaths = [];
|
|
1996
|
-
for (const filePath of optionPaths) try {
|
|
1997
|
-
const relative = path$1.relative(process.cwd(), filePath);
|
|
1998
|
-
shortPaths.push(relative);
|
|
1999
|
-
} catch (e) {
|
|
2000
|
-
if (debug) _debug(`Failed to load ${filePath} ${e.message}`);
|
|
2001
|
-
lastError = e;
|
|
2002
|
-
}
|
|
2003
|
-
_log(`injecting env (${keysCount}) from ${shortPaths.join(",")} ${dim(`-- tip: ${_getRandomTip()}`)}`);
|
|
2004
|
-
}
|
|
2005
|
-
if (lastError) return {
|
|
2006
|
-
parsed: parsedAll,
|
|
2007
|
-
error: lastError
|
|
2008
|
-
};
|
|
2009
|
-
else return { parsed: parsedAll };
|
|
2010
|
-
}
|
|
2011
|
-
function config(options) {
|
|
2012
|
-
if (_dotenvKey(options).length === 0) return DotenvModule.configDotenv(options);
|
|
2013
|
-
const vaultPath = _vaultPath(options);
|
|
2014
|
-
if (!vaultPath) {
|
|
2015
|
-
_warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
|
|
2016
|
-
return DotenvModule.configDotenv(options);
|
|
2017
|
-
}
|
|
2018
|
-
return DotenvModule._configVault(options);
|
|
2019
|
-
}
|
|
2020
|
-
function decrypt(encrypted, keyStr) {
|
|
2021
|
-
const key = Buffer.from(keyStr.slice(-64), "hex");
|
|
2022
|
-
let ciphertext = Buffer.from(encrypted, "base64");
|
|
2023
|
-
const nonce = ciphertext.subarray(0, 12);
|
|
2024
|
-
const authTag = ciphertext.subarray(-16);
|
|
2025
|
-
ciphertext = ciphertext.subarray(12, -16);
|
|
2026
|
-
try {
|
|
2027
|
-
const aesgcm = crypto.createDecipheriv("aes-256-gcm", key, nonce);
|
|
2028
|
-
aesgcm.setAuthTag(authTag);
|
|
2029
|
-
return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
|
|
2030
|
-
} catch (error) {
|
|
2031
|
-
const isRange = error instanceof RangeError;
|
|
2032
|
-
const invalidKeyLength = error.message === "Invalid key length";
|
|
2033
|
-
const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
|
|
2034
|
-
if (isRange || invalidKeyLength) {
|
|
2035
|
-
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
|
|
2036
|
-
err.code = "INVALID_DOTENV_KEY";
|
|
2037
|
-
throw err;
|
|
2038
|
-
} else if (decryptionFailed) {
|
|
2039
|
-
const err = /* @__PURE__ */ new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
|
|
2040
|
-
err.code = "DECRYPTION_FAILED";
|
|
2041
|
-
throw err;
|
|
2042
|
-
} else throw error;
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
function populate(processEnv, parsed, options = {}) {
|
|
2046
|
-
const debug = Boolean(options && options.debug);
|
|
2047
|
-
const override = Boolean(options && options.override);
|
|
2048
|
-
const populated = {};
|
|
2049
|
-
if (typeof parsed !== "object") {
|
|
2050
|
-
const err = /* @__PURE__ */ new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
|
|
2051
|
-
err.code = "OBJECT_REQUIRED";
|
|
2052
|
-
throw err;
|
|
2053
|
-
}
|
|
2054
|
-
for (const key of Object.keys(parsed)) if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
|
|
2055
|
-
if (override === true) {
|
|
2056
|
-
processEnv[key] = parsed[key];
|
|
2057
|
-
populated[key] = parsed[key];
|
|
2058
|
-
}
|
|
2059
|
-
if (debug) if (override === true) _debug(`"${key}" is already defined and WAS overwritten`);
|
|
2060
|
-
else _debug(`"${key}" is already defined and was NOT overwritten`);
|
|
2061
|
-
} else {
|
|
2062
|
-
processEnv[key] = parsed[key];
|
|
2063
|
-
populated[key] = parsed[key];
|
|
2064
|
-
}
|
|
2065
|
-
return populated;
|
|
2023
|
+
orderDirection: options.orderDirection
|
|
2024
|
+
})).map((task) => ({
|
|
2025
|
+
agent,
|
|
2026
|
+
task
|
|
2027
|
+
}));
|
|
2028
|
+
})).then((groups) => groups.flat());
|
|
2029
|
+
const orderKey = options.orderBy ?? "queued_at";
|
|
2030
|
+
const orderDirection = options.orderDirection ?? "desc";
|
|
2031
|
+
tasksWithAgent.sort((a, b) => {
|
|
2032
|
+
const aValue = a.task[orderKey];
|
|
2033
|
+
const bValue = b.task[orderKey];
|
|
2034
|
+
const aTime = aValue instanceof Date ? aValue.getTime() : 0;
|
|
2035
|
+
const bTime = bValue instanceof Date ? bValue.getTime() : 0;
|
|
2036
|
+
return orderDirection === "asc" ? aTime - bTime : bTime - aTime;
|
|
2037
|
+
});
|
|
2038
|
+
if (options.limit != null) return tasksWithAgent.slice(0, options.limit);
|
|
2039
|
+
return tasksWithAgent;
|
|
2040
|
+
}
|
|
2041
|
+
function formatRelativeTime(value) {
|
|
2042
|
+
if (!value) return "";
|
|
2043
|
+
const diffMs = Date.now() - value.getTime();
|
|
2044
|
+
const absMs = Math.abs(diffMs);
|
|
2045
|
+
const tense = diffMs >= 0 ? "ago" : "from now";
|
|
2046
|
+
const seconds = Math.floor(absMs / 1e3);
|
|
2047
|
+
const minutes = Math.floor(seconds / 60);
|
|
2048
|
+
const hours = Math.floor(minutes / 60);
|
|
2049
|
+
const days = Math.floor(hours / 24);
|
|
2050
|
+
if (days > 0) return `${days}d ${tense}`;
|
|
2051
|
+
if (hours > 0) return `${hours}h ${tense}`;
|
|
2052
|
+
if (minutes > 0) return `${minutes}m ${tense}`;
|
|
2053
|
+
return `${seconds}s ${tense}`;
|
|
2054
|
+
}
|
|
2055
|
+
function formatDate(value) {
|
|
2056
|
+
return value ? value.toISOString() : "";
|
|
2057
|
+
}
|
|
2058
|
+
function truncateText(value, maxLength) {
|
|
2059
|
+
if (value.length <= maxLength) return value;
|
|
2060
|
+
return `${value.slice(0, Math.max(0, maxLength - 1))}…`;
|
|
2061
|
+
}
|
|
2062
|
+
function colorText(value, colorCode) {
|
|
2063
|
+
return `\u001b[${colorCode}m${value}\u001b[0m`;
|
|
2064
|
+
}
|
|
2065
|
+
function formatStatus(status, useColor) {
|
|
2066
|
+
if (!useColor) return status;
|
|
2067
|
+
switch (status) {
|
|
2068
|
+
case "pending": return colorText(status, 33);
|
|
2069
|
+
case "running": return colorText(status, 36);
|
|
2070
|
+
case "completed": return colorText(status, 32);
|
|
2071
|
+
case "failed": return colorText(status, 31);
|
|
2072
|
+
case "cancelled": return colorText(status, 90);
|
|
2066
2073
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2074
|
+
}
|
|
2075
|
+
function formatConciseTaskLine(options) {
|
|
2076
|
+
const { agent, task, maxTaskLength, useColor } = options;
|
|
2077
|
+
let timestamp = task.queued_at;
|
|
2078
|
+
if (task.status === "running") timestamp = task.started_at;
|
|
2079
|
+
if (task.status === "completed" || task.status === "failed") timestamp = task.ended_at;
|
|
2080
|
+
if (task.status === "cancelled") timestamp = task.cancelled_at ?? task.queued_at;
|
|
2081
|
+
const statusText = formatStatus(task.status, useColor);
|
|
2082
|
+
const timeText = formatRelativeTime(timestamp);
|
|
2083
|
+
const taskText = truncateText(task.task, maxTaskLength);
|
|
2084
|
+
return [
|
|
2085
|
+
agent,
|
|
2086
|
+
statusText,
|
|
2087
|
+
task.id,
|
|
2088
|
+
timeText,
|
|
2089
|
+
taskText
|
|
2090
|
+
].filter((part) => part !== "").join(" ");
|
|
2091
|
+
}
|
|
2092
|
+
function formatTaskTableRow(options) {
|
|
2093
|
+
const { agent, task, maxTaskLength } = options;
|
|
2094
|
+
const branchId = "branch_id" in task ? task.branch_id ?? "" : "";
|
|
2095
|
+
const startedAt = "started_at" in task ? task.started_at : void 0;
|
|
2096
|
+
const endedAt = "ended_at" in task ? task.ended_at : void 0;
|
|
2097
|
+
return {
|
|
2098
|
+
agent,
|
|
2099
|
+
id: task.id,
|
|
2100
|
+
status: task.status,
|
|
2101
|
+
project_id: task.project_id,
|
|
2102
|
+
branch_id: branchId,
|
|
2103
|
+
queued_at: formatDate(task.queued_at),
|
|
2104
|
+
started_at: formatDate(startedAt),
|
|
2105
|
+
ended_at: formatDate(endedAt),
|
|
2106
|
+
task: truncateText(task.task, maxTaskLength)
|
|
2075
2107
|
};
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2108
|
+
}
|
|
2109
|
+
async function assertsSingleton(logger, pidFile) {
|
|
2110
|
+
try {
|
|
2111
|
+
const pid = await fs.promises.readFile(pidFile, "utf-8");
|
|
2112
|
+
process.kill(parseInt(pid), 0);
|
|
2113
|
+
console.error("Failed to assert singleton agent process:");
|
|
2114
|
+
process.exit(1);
|
|
2115
|
+
} catch (e) {
|
|
2116
|
+
await fs.promises.writeFile(pidFile, process.pid.toString());
|
|
2117
|
+
process.on("exit", () => {
|
|
2118
|
+
fs.promises.rm(pidFile);
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2085
2122
|
|
|
2086
2123
|
//#endregion
|
|
2087
|
-
//#region
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
}));
|
|
2124
|
+
//#region src/cli/commands/add-task.ts
|
|
2125
|
+
function createAddTaskCommand(version) {
|
|
2126
|
+
return createCommand("pantheon-agents add-task").version(version).description("Add a task to an agent").argument("<name>", "The name of the agent.").argument("<project-id>", "The project id of the agent.").argument("<task-prompt>", "The prompt of the task.").action(async function() {
|
|
2127
|
+
const [name, projectId, taskPrompt] = this.args;
|
|
2128
|
+
await addTask(name, {
|
|
2129
|
+
projectId,
|
|
2130
|
+
prompt: taskPrompt
|
|
2131
|
+
});
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2098
2134
|
|
|
2099
2135
|
//#endregion
|
|
2100
|
-
//#region
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
};
|
|
2112
|
-
}));
|
|
2136
|
+
//#region src/cli/commands/config.ts
|
|
2137
|
+
function createConfigAgentCommand(version) {
|
|
2138
|
+
return createCommand("pantheon-agents config").version(version).description("Configure agent for pantheon project").argument("<name>", "The name of the agent.").argument("<role>", "The role of the agent.").argument("<project-id>", "The project id of the agent.").option("--skills <skills>", "The skills of the agent. Multiple values are separated by comma.", (val) => val.split(",").map((s) => s.trim()).filter((s) => s !== ""), []).option("--execute-agent <agent>", "The execute agent of the agent.", "codex").option("--root-branch-id <branchId>", "The root branch id of the agent. Default to project root branch id.").option("--no-bootstrap", "Prevent bootstrap base branch for agent. Use the root branch as base branch.").action(async function() {
|
|
2139
|
+
const [name, role, projectId] = this.args;
|
|
2140
|
+
await configAgent(name, {
|
|
2141
|
+
role,
|
|
2142
|
+
projectId,
|
|
2143
|
+
...this.opts()
|
|
2144
|
+
});
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2113
2147
|
|
|
2114
2148
|
//#endregion
|
|
2115
|
-
//#region
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2149
|
+
//#region src/cli/commands/delete-task.ts
|
|
2150
|
+
function createDeleteTaskCommand(version) {
|
|
2151
|
+
return createCommand("pantheon-agents delete-task").version(version).description("Delete a task for an agent").argument("<name>", "The name of the agent.").argument("<task-id>", "The id of the task.").action(async function() {
|
|
2152
|
+
const [name, taskId] = this.args;
|
|
2153
|
+
const rl = readline.createInterface({
|
|
2154
|
+
input: process$1.stdin,
|
|
2155
|
+
output: process$1.stdout
|
|
2156
|
+
});
|
|
2157
|
+
try {
|
|
2158
|
+
if ((await rl.question(`Type the task id (${taskId}) to confirm deletion: `)).trim() !== taskId) {
|
|
2159
|
+
console.error("Confirmation failed. Task id did not match.");
|
|
2160
|
+
process$1.exit(1);
|
|
2161
|
+
}
|
|
2162
|
+
if ((await rl.question("Type DELETE to permanently remove this task: ")).trim() !== "DELETE") {
|
|
2163
|
+
console.error("Confirmation failed. Aborting deletion.");
|
|
2164
|
+
process$1.exit(1);
|
|
2165
|
+
}
|
|
2166
|
+
const deletedTask = await deleteTask(name, taskId);
|
|
2167
|
+
if (!deletedTask) {
|
|
2168
|
+
console.error(`Task ${taskId} not found for agent ${name}.`);
|
|
2169
|
+
process$1.exit(1);
|
|
2170
|
+
}
|
|
2171
|
+
console.log(`Deleted task ${taskId} for agent ${name}. Status was ${deletedTask.status}.`);
|
|
2172
|
+
} finally {
|
|
2173
|
+
rl.close();
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2119
2177
|
|
|
2120
2178
|
//#endregion
|
|
2121
|
-
//#region
|
|
2122
|
-
|
|
2179
|
+
//#region src/cli/commands/run.ts
|
|
2180
|
+
function createRunAgentCommand(version) {
|
|
2181
|
+
return createCommand("pantheon-agents run").version(version).description("Start a pantheon agents").argument("<name>", "The name of the agent.").option("--data-dir [dir]", "Data directory.", expandTilde("~/.pantheon-agents")).option("--mcp-port", "The port of the MCP server. Defaults to a random port.").option("--loop-interval <seconds>", "The interval of the loop in seconds. Defaults to 5.", (val) => parseInt(val, 10), 5).action(async function() {
|
|
2182
|
+
const [name] = this.args;
|
|
2183
|
+
const options = this.opts();
|
|
2184
|
+
const logFileTransport = transport({
|
|
2185
|
+
target: "pino-roll",
|
|
2186
|
+
options: {
|
|
2187
|
+
file: path.join(options.dataDir, "agents", name, "logs", "log"),
|
|
2188
|
+
frequency: "daily",
|
|
2189
|
+
mkdir: true
|
|
2190
|
+
}
|
|
2191
|
+
});
|
|
2192
|
+
const prettyTransport = transport({
|
|
2193
|
+
target: "pino-pretty",
|
|
2194
|
+
options: {
|
|
2195
|
+
colorize: true,
|
|
2196
|
+
ignore: "pid,hostname,level-label"
|
|
2197
|
+
}
|
|
2198
|
+
});
|
|
2199
|
+
await runAgent(name, options, pino({
|
|
2200
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
2201
|
+
formatters: { level(label) {
|
|
2202
|
+
return { level: label.toUpperCase() };
|
|
2203
|
+
} }
|
|
2204
|
+
}, multistream([{ stream: logFileTransport }, { stream: prettyTransport }])));
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2123
2207
|
|
|
2124
2208
|
//#endregion
|
|
2125
|
-
//#region src/cli/
|
|
2209
|
+
//#region src/cli/commands/show-config.ts
|
|
2210
|
+
function createShowConfigCommand(version) {
|
|
2211
|
+
return createCommand("pantheon-agents show-config").version(version).description("Show agent config for a project").argument("<name>", "The name of the agent.").argument("[project-id]", "The project id.").option("--json", "Output config as JSON.").action(async function() {
|
|
2212
|
+
const [name, projectId] = this.args;
|
|
2213
|
+
const options = this.opts();
|
|
2214
|
+
if (projectId) {
|
|
2215
|
+
const config = await showAgentConfig(name, projectId);
|
|
2216
|
+
if (!config) {
|
|
2217
|
+
console.error(`No config found for agent ${name} and project ${projectId}.`);
|
|
2218
|
+
process$1.exit(1);
|
|
2219
|
+
}
|
|
2220
|
+
if (options.json) {
|
|
2221
|
+
console.log(JSON.stringify(config, null, 2));
|
|
2222
|
+
return;
|
|
2223
|
+
}
|
|
2224
|
+
console.table([{
|
|
2225
|
+
agent: config.agent,
|
|
2226
|
+
project_id: config.project_id,
|
|
2227
|
+
base_branch_id: config.base_branch_id,
|
|
2228
|
+
role: config.role,
|
|
2229
|
+
execute_agent: config.execute_agent,
|
|
2230
|
+
prototype_url: config.prototype_url,
|
|
2231
|
+
skills: Array.isArray(config.skills) ? config.skills.join(", ") : String(config.skills)
|
|
2232
|
+
}]);
|
|
2233
|
+
return;
|
|
2234
|
+
}
|
|
2235
|
+
const configs = await showAgentConfigs(name);
|
|
2236
|
+
if (!configs.length) {
|
|
2237
|
+
console.error(`No configs found for agent ${name}.`);
|
|
2238
|
+
process$1.exit(1);
|
|
2239
|
+
}
|
|
2240
|
+
if (options.json) {
|
|
2241
|
+
console.log(JSON.stringify(configs, null, 2));
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
console.table(configs.map((config) => ({
|
|
2245
|
+
agent: config.agent,
|
|
2246
|
+
project_id: config.project_id,
|
|
2247
|
+
base_branch_id: config.base_branch_id,
|
|
2248
|
+
role: config.role,
|
|
2249
|
+
execute_agent: config.execute_agent,
|
|
2250
|
+
prototype_url: config.prototype_url,
|
|
2251
|
+
skills: Array.isArray(config.skills) ? config.skills.join(", ") : String(config.skills)
|
|
2252
|
+
})));
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
//#endregion
|
|
2257
|
+
//#region src/cli/constants.ts
|
|
2126
2258
|
const taskStatuses = [
|
|
2127
2259
|
"pending",
|
|
2128
2260
|
"running",
|
|
@@ -2136,205 +2268,113 @@ const orderByFields = [
|
|
|
2136
2268
|
"ended_at"
|
|
2137
2269
|
];
|
|
2138
2270
|
const orderDirections = ["asc", "desc"];
|
|
2271
|
+
|
|
2272
|
+
//#endregion
|
|
2273
|
+
//#region src/cli/utils/parse.ts
|
|
2139
2274
|
function parseCommaList(value) {
|
|
2140
2275
|
return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
|
|
2141
2276
|
}
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
await addTask(name, {
|
|
2155
|
-
projectId,
|
|
2156
|
-
prompt: taskPrompt
|
|
2157
|
-
});
|
|
2158
|
-
});
|
|
2159
|
-
const deleteTaskCommand = createCommand("pantheon-agents delete-task").version(version).description("Delete a task for an agent").argument("<name>", "The name of the agent.").argument("<task-id>", "The id of the task.").action(async function() {
|
|
2160
|
-
const [name, taskId] = this.args;
|
|
2161
|
-
const rl = readline.createInterface({
|
|
2162
|
-
input: process$1.stdin,
|
|
2163
|
-
output: process$1.stdout
|
|
2164
|
-
});
|
|
2165
|
-
try {
|
|
2166
|
-
if ((await rl.question(`Type the task id (${taskId}) to confirm deletion: `)).trim() !== taskId) {
|
|
2167
|
-
console.error("Confirmation failed. Task id did not match.");
|
|
2277
|
+
|
|
2278
|
+
//#endregion
|
|
2279
|
+
//#region src/cli/commands/show-tasks.ts
|
|
2280
|
+
function createShowTasksCommand(version) {
|
|
2281
|
+
return createCommand("pantheon-agents show-tasks").version(version).description("Show tasks for an agent").argument("[name]", "The name of the agent.").option("--agents <names>", "Comma-separated agent names to query in one call.", parseCommaList, []).option("--all", "Show tasks for all agents.").option("--status <status>", "Filter tasks by status. Multiple values are separated by comma.", parseCommaList, []).option("--order-by <field>", "Order by queued_at, started_at, or ended_at.", "queued_at").option("--order-direction <direction>", "Order direction: asc or desc.", "desc").option("--limit <number>", "Limit the number of tasks shown.", (val) => parseInt(val, 10)).option("--max-task-length <number>", "Maximum task length for table output.", (val) => parseInt(val, 10), 120).option("--full-task", "Do not truncate task text in output.").option("--concise", "Output concise one-line entries.", true).option("--no-color", "Disable colored output.").option("--json", "Output tasks as JSON.").action(async function() {
|
|
2282
|
+
const [name] = this.args;
|
|
2283
|
+
const options = this.opts();
|
|
2284
|
+
const agentNames = /* @__PURE__ */ new Set();
|
|
2285
|
+
if (name) agentNames.add(name);
|
|
2286
|
+
options.agents.forEach((agent) => agentNames.add(agent));
|
|
2287
|
+
if (options.all && agentNames.size > 0) {
|
|
2288
|
+
console.error("Use either a specific agent name or --all, not both.");
|
|
2168
2289
|
process$1.exit(1);
|
|
2169
2290
|
}
|
|
2170
|
-
if (
|
|
2171
|
-
console.error("
|
|
2291
|
+
if (!options.all && agentNames.size === 0) {
|
|
2292
|
+
console.error("Provide an agent name, --agents, or --all.");
|
|
2172
2293
|
process$1.exit(1);
|
|
2173
2294
|
}
|
|
2174
|
-
const
|
|
2175
|
-
if (
|
|
2176
|
-
console.error(`
|
|
2295
|
+
const invalidStatuses = options.status.filter((status) => !taskStatuses.includes(status));
|
|
2296
|
+
if (invalidStatuses.length > 0) {
|
|
2297
|
+
console.error(`Invalid status values: ${invalidStatuses.join(", ")}. Allowed: ${taskStatuses.join(", ")}.`);
|
|
2177
2298
|
process$1.exit(1);
|
|
2178
2299
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
const [name, projectId] = this.args;
|
|
2186
|
-
const options = this.opts();
|
|
2187
|
-
if (projectId) {
|
|
2188
|
-
const config = await showAgentConfig(name, projectId);
|
|
2189
|
-
if (!config) {
|
|
2190
|
-
console.error(`No config found for agent ${name} and project ${projectId}.`);
|
|
2300
|
+
if (!orderByFields.includes(options.orderBy)) {
|
|
2301
|
+
console.error(`Invalid order-by value: ${options.orderBy}. Allowed: ${orderByFields.join(", ")}.`);
|
|
2302
|
+
process$1.exit(1);
|
|
2303
|
+
}
|
|
2304
|
+
if (!orderDirections.includes(options.orderDirection)) {
|
|
2305
|
+
console.error(`Invalid order-direction value: ${options.orderDirection}. Allowed: ${orderDirections.join(", ")}.`);
|
|
2191
2306
|
process$1.exit(1);
|
|
2192
2307
|
}
|
|
2308
|
+
if (options.limit != null && Number.isNaN(options.limit)) {
|
|
2309
|
+
console.error("Invalid limit value. Must be a number.");
|
|
2310
|
+
process$1.exit(1);
|
|
2311
|
+
}
|
|
2312
|
+
if (Number.isNaN(options.maxTaskLength) || options.maxTaskLength <= 0) {
|
|
2313
|
+
console.error("Invalid max-task-length value. Must be a positive number.");
|
|
2314
|
+
process$1.exit(1);
|
|
2315
|
+
}
|
|
2316
|
+
if (options.fullTask && options.maxTaskLength) options.maxTaskLength = Number.POSITIVE_INFINITY;
|
|
2317
|
+
if (options.json && options.concise) {
|
|
2318
|
+
console.error("Use either --json or --concise, not both.");
|
|
2319
|
+
process$1.exit(1);
|
|
2320
|
+
}
|
|
2321
|
+
const limitedTasks = await showTasksForAgents({
|
|
2322
|
+
agents: Array.from(agentNames),
|
|
2323
|
+
allAgents: options.all,
|
|
2324
|
+
status: options.status,
|
|
2325
|
+
orderBy: options.orderBy,
|
|
2326
|
+
orderDirection: options.orderDirection,
|
|
2327
|
+
limit: options.limit
|
|
2328
|
+
});
|
|
2193
2329
|
if (options.json) {
|
|
2194
|
-
|
|
2330
|
+
const payload = limitedTasks.map(({ agent, task }) => ({
|
|
2331
|
+
agent,
|
|
2332
|
+
...task
|
|
2333
|
+
}));
|
|
2334
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
2195
2335
|
return;
|
|
2196
2336
|
}
|
|
2197
|
-
|
|
2198
|
-
agent
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
}]);
|
|
2206
|
-
return;
|
|
2207
|
-
}
|
|
2208
|
-
const configs = await showAgentConfigs(name);
|
|
2209
|
-
if (!configs.length) {
|
|
2210
|
-
console.error(`No configs found for agent ${name}.`);
|
|
2211
|
-
process$1.exit(1);
|
|
2212
|
-
}
|
|
2213
|
-
if (options.json) {
|
|
2214
|
-
console.log(JSON.stringify(configs, null, 2));
|
|
2215
|
-
return;
|
|
2216
|
-
}
|
|
2217
|
-
console.table(configs.map((config) => ({
|
|
2218
|
-
agent: config.agent,
|
|
2219
|
-
project_id: config.project_id,
|
|
2220
|
-
base_branch_id: config.base_branch_id,
|
|
2221
|
-
role: config.role,
|
|
2222
|
-
execute_agent: config.execute_agent,
|
|
2223
|
-
prototype_url: config.prototype_url,
|
|
2224
|
-
skills: Array.isArray(config.skills) ? config.skills.join(", ") : String(config.skills)
|
|
2225
|
-
})));
|
|
2226
|
-
});
|
|
2227
|
-
const runAgentCommand = createCommand("pantheon-agents run").version(version).description("Start a pantheon agents").argument("<name>", "The name of the agent.").option("--data-dir [dir]", "Data directory.", expandTilde("~/.pantheon-agents")).option("--mcp-port", "The port of the MCP server. Defaults to a random port.").option("--loop-interval <seconds>", "The interval of the loop in seconds. Defaults to 5.", (val) => parseInt(val, 10), 5).action(async function() {
|
|
2228
|
-
const [name] = this.args;
|
|
2229
|
-
const options = this.opts();
|
|
2230
|
-
const logFileTransport = transport({
|
|
2231
|
-
target: "pino-roll",
|
|
2232
|
-
options: {
|
|
2233
|
-
file: path.join(options.dataDir, "agents", name, "logs", "log"),
|
|
2234
|
-
frequency: "daily",
|
|
2235
|
-
mkdir: true
|
|
2236
|
-
}
|
|
2237
|
-
});
|
|
2238
|
-
const prettyTransport = transport({
|
|
2239
|
-
target: "pino-pretty",
|
|
2240
|
-
options: {
|
|
2241
|
-
colorize: true,
|
|
2242
|
-
ignore: "pid,hostname,level-label"
|
|
2337
|
+
if (options.concise) {
|
|
2338
|
+
for (const { agent, task } of limitedTasks) console.log(formatConciseTaskLine({
|
|
2339
|
+
agent,
|
|
2340
|
+
task,
|
|
2341
|
+
maxTaskLength: options.maxTaskLength,
|
|
2342
|
+
useColor: options.color
|
|
2343
|
+
}));
|
|
2344
|
+
return;
|
|
2243
2345
|
}
|
|
2244
|
-
|
|
2245
|
-
await runAgent(name, options, pino({
|
|
2246
|
-
timestamp: pino.stdTimeFunctions.isoTime,
|
|
2247
|
-
formatters: { level(label) {
|
|
2248
|
-
return { level: label.toUpperCase() };
|
|
2249
|
-
} }
|
|
2250
|
-
}, multistream([{ stream: logFileTransport }, { stream: prettyTransport }])));
|
|
2251
|
-
});
|
|
2252
|
-
const showTasksCommand = createCommand("pantheon-agents show-tasks").version(version).description("Show tasks for an agent").argument("[name]", "The name of the agent.").option("--agents <names>", "Comma-separated agent names to query in one call.", parseCommaList, []).option("--all", "Show tasks for all agents.").option("--status <status>", "Filter tasks by status. Multiple values are separated by comma.", parseCommaList, []).option("--order-by <field>", "Order by queued_at, started_at, or ended_at.", "queued_at").option("--order-direction <direction>", "Order direction: asc or desc.", "desc").option("--limit <number>", "Limit the number of tasks shown.", (val) => parseInt(val, 10)).option("--max-task-length <number>", "Maximum task length for table output.", (val) => parseInt(val, 10), 120).option("--full-task", "Do not truncate task text in output.").option("--concise", "Output concise one-line entries.", true).option("--no-color", "Disable colored output.").option("--json", "Output tasks as JSON.").action(async function() {
|
|
2253
|
-
const [name] = this.args;
|
|
2254
|
-
const options = this.opts();
|
|
2255
|
-
const agentNames = /* @__PURE__ */ new Set();
|
|
2256
|
-
if (name) agentNames.add(name);
|
|
2257
|
-
options.agents.forEach((agent) => agentNames.add(agent));
|
|
2258
|
-
if (options.all && agentNames.size > 0) {
|
|
2259
|
-
console.error("Use either a specific agent name or --all, not both.");
|
|
2260
|
-
process$1.exit(1);
|
|
2261
|
-
}
|
|
2262
|
-
if (!options.all && agentNames.size === 0) {
|
|
2263
|
-
console.error("Provide an agent name, --agents, or --all.");
|
|
2264
|
-
process$1.exit(1);
|
|
2265
|
-
}
|
|
2266
|
-
const invalidStatuses = options.status.filter((status) => !taskStatuses.includes(status));
|
|
2267
|
-
if (invalidStatuses.length > 0) {
|
|
2268
|
-
console.error(`Invalid status values: ${invalidStatuses.join(", ")}. Allowed: ${taskStatuses.join(", ")}.`);
|
|
2269
|
-
process$1.exit(1);
|
|
2270
|
-
}
|
|
2271
|
-
if (!orderByFields.includes(options.orderBy)) {
|
|
2272
|
-
console.error(`Invalid order-by value: ${options.orderBy}. Allowed: ${orderByFields.join(", ")}.`);
|
|
2273
|
-
process$1.exit(1);
|
|
2274
|
-
}
|
|
2275
|
-
if (!orderDirections.includes(options.orderDirection)) {
|
|
2276
|
-
console.error(`Invalid order-direction value: ${options.orderDirection}. Allowed: ${orderDirections.join(", ")}.`);
|
|
2277
|
-
process$1.exit(1);
|
|
2278
|
-
}
|
|
2279
|
-
if (options.limit != null && Number.isNaN(options.limit)) {
|
|
2280
|
-
console.error("Invalid limit value. Must be a number.");
|
|
2281
|
-
process$1.exit(1);
|
|
2282
|
-
}
|
|
2283
|
-
if (Number.isNaN(options.maxTaskLength) || options.maxTaskLength <= 0) {
|
|
2284
|
-
console.error("Invalid max-task-length value. Must be a positive number.");
|
|
2285
|
-
process$1.exit(1);
|
|
2286
|
-
}
|
|
2287
|
-
if (options.fullTask && options.maxTaskLength) options.maxTaskLength = Number.POSITIVE_INFINITY;
|
|
2288
|
-
if (options.json && options.concise) {
|
|
2289
|
-
console.error("Use either --json or --concise, not both.");
|
|
2290
|
-
process$1.exit(1);
|
|
2291
|
-
}
|
|
2292
|
-
const limitedTasks = await showTasksForAgents({
|
|
2293
|
-
agents: Array.from(agentNames),
|
|
2294
|
-
allAgents: options.all,
|
|
2295
|
-
status: options.status,
|
|
2296
|
-
orderBy: options.orderBy,
|
|
2297
|
-
orderDirection: options.orderDirection,
|
|
2298
|
-
limit: options.limit
|
|
2299
|
-
});
|
|
2300
|
-
if (options.json) {
|
|
2301
|
-
const payload = limitedTasks.map(({ agent, task }) => ({
|
|
2302
|
-
agent,
|
|
2303
|
-
...task
|
|
2304
|
-
}));
|
|
2305
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
2306
|
-
return;
|
|
2307
|
-
}
|
|
2308
|
-
if (options.concise) {
|
|
2309
|
-
for (const { agent, task } of limitedTasks) console.log(formatConciseTaskLine({
|
|
2346
|
+
const rows = limitedTasks.map(({ agent, task }) => formatTaskTableRow({
|
|
2310
2347
|
agent,
|
|
2311
2348
|
task,
|
|
2312
|
-
maxTaskLength: options.maxTaskLength
|
|
2313
|
-
useColor: options.color
|
|
2349
|
+
maxTaskLength: options.maxTaskLength
|
|
2314
2350
|
}));
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
console.
|
|
2324
|
-
}
|
|
2351
|
+
console.table(rows);
|
|
2352
|
+
console.log(`${limitedTasks.length} task(s) shown.`);
|
|
2353
|
+
});
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
//#endregion
|
|
2357
|
+
//#region src/cli/utils/env.ts
|
|
2358
|
+
function warnIfMissingEnv(keys) {
|
|
2359
|
+
for (const key of keys) if (!process.env[key]) console.error(`${key} environment variable is not set.`);
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
//#endregion
|
|
2363
|
+
//#region src/cli/index.ts
|
|
2364
|
+
warnIfMissingEnv(["PANTHEON_API_KEY", "DATABASE_URL"]);
|
|
2365
|
+
const commands = {
|
|
2366
|
+
"add-task": createAddTaskCommand(version),
|
|
2367
|
+
config: createConfigAgentCommand(version),
|
|
2368
|
+
"delete-task": createDeleteTaskCommand(version),
|
|
2369
|
+
run: createRunAgentCommand(version),
|
|
2370
|
+
"show-config": createShowConfigCommand(version),
|
|
2371
|
+
"show-tasks": createShowTasksCommand(version)
|
|
2372
|
+
};
|
|
2325
2373
|
function printCommandHelpAndExit(command) {
|
|
2326
2374
|
console.error(`Invalid command: ${command}. Supported commands: ${Object.keys(commands).join(", ")}.`);
|
|
2327
|
-
console.error(
|
|
2375
|
+
console.error(" Run pantheon-agents help <command> for more information.");
|
|
2328
2376
|
process$1.exit(1);
|
|
2329
2377
|
}
|
|
2330
|
-
const commands = {
|
|
2331
|
-
"add-task": addTaskCommand,
|
|
2332
|
-
config: configAgentCommand,
|
|
2333
|
-
"delete-task": deleteTaskCommand,
|
|
2334
|
-
run: runAgentCommand,
|
|
2335
|
-
"show-config": showConfigCommand,
|
|
2336
|
-
"show-tasks": showTasksCommand
|
|
2337
|
-
};
|
|
2338
2378
|
if (process$1.argv[2] === "help") {
|
|
2339
2379
|
const command = process$1.argv[3];
|
|
2340
2380
|
if (command in commands) commands[command].help({ error: false });
|