thepopebot 1.2.75-beta.6 → 1.2.75-beta.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/config/instrumentation.js +3 -3
- package/lib/ai/tools.js +0 -4
- package/lib/code/actions.js +0 -15
- package/lib/db/api-keys.js +19 -58
- package/lib/maintenance.js +0 -2
- package/lib/tools/create-agent-job.js +0 -4
- package/lib/tools/docker.js +19 -15
- package/package.json +1 -1
- package/templates/skills/agent-job-secrets/SKILL.md +4 -4
- package/templates/skills/agent-job-secrets/{agent-job-secrets.js → agent-job-secrets.mjs} +18 -9
|
@@ -70,9 +70,9 @@ export async function register() {
|
|
|
70
70
|
const { startClusterRuntime } = await import('../lib/cluster/runtime.js');
|
|
71
71
|
startClusterRuntime();
|
|
72
72
|
|
|
73
|
-
//
|
|
74
|
-
const { startMaintenanceCron } = await import('../lib/maintenance.js');
|
|
75
|
-
startMaintenanceCron();
|
|
73
|
+
// Maintenance cron disabled — agent job key cleanup will be redesigned
|
|
74
|
+
// const { startMaintenanceCron } = await import('../lib/maintenance.js');
|
|
75
|
+
// startMaintenanceCron();
|
|
76
76
|
|
|
77
77
|
console.log('thepopebot initialized');
|
|
78
78
|
}
|
package/lib/ai/tools.js
CHANGED
|
@@ -54,9 +54,6 @@ const agentChatCodingTool = tool(
|
|
|
54
54
|
const codingAgent = getConfig('CODING_AGENT') || 'claude-code';
|
|
55
55
|
const containerName = `${codingAgent}-headless-${randomUUID().slice(0, 8)}`;
|
|
56
56
|
|
|
57
|
-
const { createAgentJobApiKey } = await import('../db/api-keys.js');
|
|
58
|
-
const { key: agentJobToken } = createAgentJobApiKey(randomUUID());
|
|
59
|
-
|
|
60
57
|
const { runHeadlessContainer, tailContainerLogs, waitForContainer, removeContainer } = await import('../tools/docker.js');
|
|
61
58
|
await runHeadlessContainer({
|
|
62
59
|
containerName,
|
|
@@ -67,7 +64,6 @@ const agentChatCodingTool = tool(
|
|
|
67
64
|
taskPrompt: prompt,
|
|
68
65
|
mode,
|
|
69
66
|
injectSecrets: true,
|
|
70
|
-
agentJobToken,
|
|
71
67
|
});
|
|
72
68
|
|
|
73
69
|
const streamCallback = runtime.configurable.streamCallback;
|
package/lib/code/actions.js
CHANGED
|
@@ -138,12 +138,6 @@ export async function ensureCodeWorkspaceContainer(id) {
|
|
|
138
138
|
const chat = getChatByWorkspaceId(id);
|
|
139
139
|
const injectSecrets = chat?.chatMode === 'agent';
|
|
140
140
|
|
|
141
|
-
let agentJobToken;
|
|
142
|
-
if (injectSecrets) {
|
|
143
|
-
const { createAgentJobApiKey } = await import('../db/api-keys.js');
|
|
144
|
-
({ key: agentJobToken } = createAgentJobApiKey(id));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
141
|
try {
|
|
148
142
|
const { inspectContainer, startContainer, removeContainer, runInteractiveContainer } =
|
|
149
143
|
await import('../tools/docker.js');
|
|
@@ -159,7 +153,6 @@ export async function ensureCodeWorkspaceContainer(id) {
|
|
|
159
153
|
featureBranch: workspace.featureBranch,
|
|
160
154
|
workspaceId: id,
|
|
161
155
|
injectSecrets,
|
|
162
|
-
agentJobToken,
|
|
163
156
|
});
|
|
164
157
|
return { status: 'created' };
|
|
165
158
|
}
|
|
@@ -188,7 +181,6 @@ export async function ensureCodeWorkspaceContainer(id) {
|
|
|
188
181
|
featureBranch: workspace.featureBranch,
|
|
189
182
|
workspaceId: id,
|
|
190
183
|
injectSecrets,
|
|
191
|
-
agentJobToken,
|
|
192
184
|
});
|
|
193
185
|
return { status: 'created' };
|
|
194
186
|
} catch (err) {
|
|
@@ -217,12 +209,6 @@ export async function startInteractiveMode(id) {
|
|
|
217
209
|
const chat = getChatByWorkspaceId(id);
|
|
218
210
|
const injectSecrets = chat?.chatMode === 'agent';
|
|
219
211
|
|
|
220
|
-
let agentJobToken;
|
|
221
|
-
if (injectSecrets) {
|
|
222
|
-
const { createAgentJobApiKey } = await import('../db/api-keys.js');
|
|
223
|
-
({ key: agentJobToken } = createAgentJobApiKey(id));
|
|
224
|
-
}
|
|
225
|
-
|
|
226
212
|
try {
|
|
227
213
|
const { getConfig } = await import('../config.js');
|
|
228
214
|
const agent = getConfig('CODING_AGENT') || 'claude-code';
|
|
@@ -237,7 +223,6 @@ export async function startInteractiveMode(id) {
|
|
|
237
223
|
featureBranch: workspace.featureBranch,
|
|
238
224
|
workspaceId: id,
|
|
239
225
|
injectSecrets,
|
|
240
|
-
agentJobToken,
|
|
241
226
|
});
|
|
242
227
|
|
|
243
228
|
updateContainerName(id, containerName);
|
package/lib/db/api-keys.js
CHANGED
|
@@ -5,9 +5,6 @@ import { settings } from './schema.js';
|
|
|
5
5
|
|
|
6
6
|
const KEY_PREFIX = 'tpb_';
|
|
7
7
|
|
|
8
|
-
// In-memory cache: array of { id, keyHash } or null (not loaded)
|
|
9
|
-
let _cache = null;
|
|
10
|
-
|
|
11
8
|
/**
|
|
12
9
|
* Generate a new API key: tpb_ + 64 hex chars (32 random bytes).
|
|
13
10
|
* @returns {string}
|
|
@@ -25,44 +22,6 @@ export function hashApiKey(key) {
|
|
|
25
22
|
return createHash('sha256').update(key).digest('hex');
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
/**
|
|
29
|
-
* Lazy-load all API key hashes into the in-memory cache.
|
|
30
|
-
*/
|
|
31
|
-
function _ensureCache() {
|
|
32
|
-
if (_cache !== null) return _cache;
|
|
33
|
-
|
|
34
|
-
const db = getDb();
|
|
35
|
-
const ONE_HOUR = 60 * 60 * 1000;
|
|
36
|
-
const cutoff = Date.now() - ONE_HOUR;
|
|
37
|
-
|
|
38
|
-
const rows = db
|
|
39
|
-
.select()
|
|
40
|
-
.from(settings)
|
|
41
|
-
.where(eq(settings.type, 'api_key'))
|
|
42
|
-
.all();
|
|
43
|
-
|
|
44
|
-
const jobKeyRows = db
|
|
45
|
-
.select()
|
|
46
|
-
.from(settings)
|
|
47
|
-
.where(eq(settings.type, 'agent_job_api_key'))
|
|
48
|
-
.all()
|
|
49
|
-
.filter(r => r.lastUsedAt !== null ? r.lastUsedAt > cutoff : r.createdAt > cutoff);
|
|
50
|
-
|
|
51
|
-
_cache = [...rows, ...jobKeyRows].map((row) => {
|
|
52
|
-
const parsed = JSON.parse(row.value);
|
|
53
|
-
return { id: row.id, keyHash: parsed.key_hash, type: row.type };
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
return _cache;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Clear the in-memory cache (call after create/delete).
|
|
61
|
-
*/
|
|
62
|
-
export function invalidateApiKeyCache() {
|
|
63
|
-
_cache = null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
25
|
/**
|
|
67
26
|
* Create a new named API key.
|
|
68
27
|
* @param {string} name - Human-readable name for the key
|
|
@@ -89,7 +48,6 @@ export function createApiKeyRecord(name, createdBy) {
|
|
|
89
48
|
};
|
|
90
49
|
|
|
91
50
|
db.insert(settings).values(record).run();
|
|
92
|
-
invalidateApiKeyCache();
|
|
93
51
|
|
|
94
52
|
return {
|
|
95
53
|
key,
|
|
@@ -143,7 +101,6 @@ export function getApiKey() {
|
|
|
143
101
|
export function deleteApiKeyById(id) {
|
|
144
102
|
const db = getDb();
|
|
145
103
|
db.delete(settings).where(eq(settings.id, id)).run();
|
|
146
|
-
invalidateApiKeyCache();
|
|
147
104
|
}
|
|
148
105
|
|
|
149
106
|
/**
|
|
@@ -152,11 +109,11 @@ export function deleteApiKeyById(id) {
|
|
|
152
109
|
export function deleteApiKey() {
|
|
153
110
|
const db = getDb();
|
|
154
111
|
db.delete(settings).where(eq(settings.type, 'api_key')).run();
|
|
155
|
-
invalidateApiKeyCache();
|
|
156
112
|
}
|
|
157
113
|
|
|
158
114
|
/**
|
|
159
|
-
* Verify a raw API key against
|
|
115
|
+
* Verify a raw API key against stored hashes.
|
|
116
|
+
* Queries the database directly on each call (SQLite is in-process, no caching needed).
|
|
160
117
|
* @param {string} rawKey - Raw API key from request header
|
|
161
118
|
* @returns {object|null} Record if valid, null otherwise
|
|
162
119
|
*/
|
|
@@ -164,27 +121,32 @@ export function verifyApiKey(rawKey) {
|
|
|
164
121
|
if (!rawKey || !rawKey.startsWith(KEY_PREFIX)) return null;
|
|
165
122
|
|
|
166
123
|
const keyHash = hashApiKey(rawKey);
|
|
167
|
-
const
|
|
124
|
+
const db = getDb();
|
|
168
125
|
|
|
169
|
-
|
|
126
|
+
const rows = [
|
|
127
|
+
...db.select().from(settings).where(eq(settings.type, 'api_key')).all(),
|
|
128
|
+
...db.select().from(settings).where(eq(settings.type, 'agent_job_api_key')).all(),
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
if (rows.length === 0) return null;
|
|
170
132
|
|
|
171
133
|
const b = Buffer.from(keyHash, 'hex');
|
|
172
134
|
|
|
173
|
-
for (const
|
|
174
|
-
const
|
|
135
|
+
for (const row of rows) {
|
|
136
|
+
const parsed = JSON.parse(row.value);
|
|
137
|
+
const a = Buffer.from(parsed.key_hash, 'hex');
|
|
175
138
|
if (a.length === b.length && timingSafeEqual(a, b)) {
|
|
176
|
-
// Update last_used_at
|
|
139
|
+
// Update last_used_at
|
|
177
140
|
try {
|
|
178
|
-
const db = getDb();
|
|
179
141
|
const now = Date.now();
|
|
180
142
|
db.update(settings)
|
|
181
143
|
.set({ lastUsedAt: now, updatedAt: now })
|
|
182
|
-
.where(eq(settings.id,
|
|
144
|
+
.where(eq(settings.id, row.id))
|
|
183
145
|
.run();
|
|
184
|
-
} catch {
|
|
185
|
-
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.error('[api-keys] Failed to update last_used_at:', err.message);
|
|
186
148
|
}
|
|
187
|
-
return
|
|
149
|
+
return { id: row.id, keyHash: parsed.key_hash, type: row.type };
|
|
188
150
|
}
|
|
189
151
|
}
|
|
190
152
|
|
|
@@ -193,8 +155,8 @@ export function verifyApiKey(rawKey) {
|
|
|
193
155
|
|
|
194
156
|
/**
|
|
195
157
|
* Create a per-job API key for an agent job container.
|
|
196
|
-
* Stored as type 'agent_job_api_key'.
|
|
197
|
-
* @param {string} jobId - Agent job ID (stored as key column for traceability)
|
|
158
|
+
* Stored as type 'agent_job_api_key'.
|
|
159
|
+
* @param {string} jobId - Agent job or workspace ID (stored as key column for traceability)
|
|
198
160
|
* @returns {{ key: string }} The raw API key to inject into the container
|
|
199
161
|
*/
|
|
200
162
|
export function createAgentJobApiKey(jobId) {
|
|
@@ -210,7 +172,6 @@ export function createAgentJobApiKey(jobId) {
|
|
|
210
172
|
createdAt: now,
|
|
211
173
|
updatedAt: now,
|
|
212
174
|
}).run();
|
|
213
|
-
invalidateApiKeyCache();
|
|
214
175
|
return { key };
|
|
215
176
|
}
|
|
216
177
|
|
package/lib/maintenance.js
CHANGED
|
@@ -2,7 +2,6 @@ import cron from 'node-cron';
|
|
|
2
2
|
import { eq } from 'drizzle-orm';
|
|
3
3
|
import { getDb } from './db/index.js';
|
|
4
4
|
import { settings } from './db/schema.js';
|
|
5
|
-
import { invalidateApiKeyCache } from './db/api-keys.js';
|
|
6
5
|
|
|
7
6
|
const ONE_HOUR = 60 * 60 * 1000;
|
|
8
7
|
|
|
@@ -22,7 +21,6 @@ function cleanExpiredAgentJobKeys() {
|
|
|
22
21
|
for (const id of expiredIds) {
|
|
23
22
|
db.delete(settings).where(eq(settings.id, id)).run();
|
|
24
23
|
}
|
|
25
|
-
invalidateApiKeyCache();
|
|
26
24
|
console.log(`[maintenance] Deleted ${expiredIds.length} expired agent job key(s)`);
|
|
27
25
|
} else {
|
|
28
26
|
console.log(`[maintenance] No expired agent job keys (${rows.length} active)`);
|
|
@@ -3,8 +3,6 @@ import { z } from 'zod';
|
|
|
3
3
|
import { githubApi } from './github.js';
|
|
4
4
|
import { createModel } from '../ai/model.js';
|
|
5
5
|
import { getConfig } from '../config.js';
|
|
6
|
-
import { createAgentJobApiKey } from '../db/api-keys.js';
|
|
7
|
-
|
|
8
6
|
/**
|
|
9
7
|
* Generate a short descriptive title for an agent job using the LLM.
|
|
10
8
|
* Uses structured output to avoid thinking-token leaks with extended-thinking models.
|
|
@@ -93,7 +91,6 @@ async function createAgentJob(agentJobDescription, options = {}) {
|
|
|
93
91
|
|
|
94
92
|
// 6. Launch Docker container locally (fire-and-forget with async cleanup)
|
|
95
93
|
const repoSlug = `${GH_OWNER}/${GH_REPO}`;
|
|
96
|
-
const { key: agentJobToken } = createAgentJobApiKey(agentJobId);
|
|
97
94
|
launchAgentJobContainer({
|
|
98
95
|
agentJobId,
|
|
99
96
|
repo: repoSlug,
|
|
@@ -102,7 +99,6 @@ async function createAgentJob(agentJobDescription, options = {}) {
|
|
|
102
99
|
description: agentJobDescription,
|
|
103
100
|
codingAgent: options.agentBackend,
|
|
104
101
|
llmModel: options.llmModel,
|
|
105
|
-
agentJobToken,
|
|
106
102
|
}).catch(err => {
|
|
107
103
|
console.error(`[agent-job] Failed to launch container for ${agentJobId}:`, err.message);
|
|
108
104
|
});
|
package/lib/tools/docker.js
CHANGED
|
@@ -196,7 +196,7 @@ async function runContainer({ containerName, image, env = [], workingDir, hostCo
|
|
|
196
196
|
* @param {boolean} [options.injectSecrets] - Inject agent job secrets into container env
|
|
197
197
|
* @returns {Promise<{containerId: string, containerName: string}>}
|
|
198
198
|
*/
|
|
199
|
-
async function runInteractiveContainer({ containerName, repo, branch, codingAgent, featureBranch, workspaceId, injectSecrets,
|
|
199
|
+
async function runInteractiveContainer({ containerName, repo, branch, codingAgent, featureBranch, workspaceId, injectSecrets, continueSession = true }) {
|
|
200
200
|
const agent = codingAgent || getConfig('CODING_AGENT') || 'claude-code';
|
|
201
201
|
const version = process.env.THEPOPEBOT_VERSION;
|
|
202
202
|
const image = `stephengpope/thepopebot:coding-agent-${agent}-${version}`;
|
|
@@ -234,11 +234,12 @@ async function runInteractiveContainer({ containerName, repo, branch, codingAgen
|
|
|
234
234
|
if (jobSecrets.length > 0) {
|
|
235
235
|
env.push(`AGENT_JOB_SECRETS=${JSON.stringify(Object.fromEntries(jobSecrets.map(s => [s.key, s.value])))}`);
|
|
236
236
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
237
|
+
// Create per-container API key for agent-secrets access
|
|
238
|
+
const { createAgentJobApiKey } = await import('../db/api-keys.js');
|
|
239
|
+
const { key: agentJobToken } = createAgentJobApiKey(workspaceId || containerName);
|
|
240
|
+
env.push(`AGENT_JOB_TOKEN=${agentJobToken}`);
|
|
241
|
+
const appUrl = getConfig('APP_URL');
|
|
242
|
+
if (appUrl) env.push(`APP_URL=${appUrl}`);
|
|
242
243
|
}
|
|
243
244
|
|
|
244
245
|
const hostConfig = {};
|
|
@@ -399,7 +400,7 @@ function buildAgentAuthEnv(agent) {
|
|
|
399
400
|
* @param {boolean} [options.injectSecrets] - Inject agent job secrets into container env
|
|
400
401
|
* @returns {Promise<{containerId: string, containerName: string}>}
|
|
401
402
|
*/
|
|
402
|
-
async function runHeadlessContainer({ containerName, repo, branch, featureBranch, workspaceId, taskPrompt, mode = 'plan', codingAgent, systemPrompt, continueSession = true, injectSecrets
|
|
403
|
+
async function runHeadlessContainer({ containerName, repo, branch, featureBranch, workspaceId, taskPrompt, mode = 'plan', codingAgent, systemPrompt, continueSession = true, injectSecrets }) {
|
|
403
404
|
const agent = codingAgent || getConfig('CODING_AGENT') || 'claude-code';
|
|
404
405
|
const version = process.env.THEPOPEBOT_VERSION;
|
|
405
406
|
const image = `stephengpope/thepopebot:coding-agent-${agent}-${version}`;
|
|
@@ -446,11 +447,12 @@ async function runHeadlessContainer({ containerName, repo, branch, featureBranch
|
|
|
446
447
|
if (jobSecrets.length > 0) {
|
|
447
448
|
env.push(`AGENT_JOB_SECRETS=${JSON.stringify(Object.fromEntries(jobSecrets.map(s => [s.key, s.value])))}`);
|
|
448
449
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
450
|
+
// Create per-container API key for agent-secrets access
|
|
451
|
+
const { createAgentJobApiKey } = await import('../db/api-keys.js');
|
|
452
|
+
const { key: agentJobToken } = createAgentJobApiKey(workspaceId || containerName);
|
|
453
|
+
env.push(`AGENT_JOB_TOKEN=${agentJobToken}`);
|
|
454
|
+
const appUrl = getConfig('APP_URL');
|
|
455
|
+
if (appUrl) env.push(`APP_URL=${appUrl}`);
|
|
454
456
|
}
|
|
455
457
|
|
|
456
458
|
const hostConfig = {};
|
|
@@ -861,7 +863,7 @@ async function removeVolume(name) {
|
|
|
861
863
|
* @param {string} [options.llmModel] - Model override
|
|
862
864
|
* @returns {Promise<{containerId: string, containerName: string, volumeName: string}>}
|
|
863
865
|
*/
|
|
864
|
-
async function runAgentJobContainer({ agentJobId, repo, branch, title, description, codingAgent, llmModel
|
|
866
|
+
async function runAgentJobContainer({ agentJobId, repo, branch, title, description, codingAgent, llmModel }) {
|
|
865
867
|
const agent = codingAgent || getConfig('CODING_AGENT') || 'claude-code';
|
|
866
868
|
const version = process.env.THEPOPEBOT_VERSION;
|
|
867
869
|
const image = `stephengpope/thepopebot:coding-agent-${agent}-${version}`;
|
|
@@ -899,8 +901,10 @@ async function runAgentJobContainer({ agentJobId, repo, branch, title, descripti
|
|
|
899
901
|
const appUrl = getConfig('APP_URL');
|
|
900
902
|
if (appUrl) env.push(`APP_URL=${appUrl}`);
|
|
901
903
|
|
|
902
|
-
//
|
|
903
|
-
|
|
904
|
+
// Create per-container API key for agent-secrets access
|
|
905
|
+
const { createAgentJobApiKey } = await import('../db/api-keys.js');
|
|
906
|
+
const { key: agentJobToken } = createAgentJobApiKey(agentJobId);
|
|
907
|
+
env.push(`AGENT_JOB_TOKEN=${agentJobToken}`);
|
|
904
908
|
|
|
905
909
|
// Inject agent job secrets (plain secrets as env vars; oauth types are null — agent must fetch via get)
|
|
906
910
|
const { getAllAgentJobSecrets } = await import('../db/config.js');
|
package/package.json
CHANGED
|
@@ -7,14 +7,14 @@ description: List, get, or update agent secrets. Use get for OAuth credentials (
|
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
9
|
# List available secrets (null = must fetch, plain = already in env)
|
|
10
|
-
node skills/agent-job-secrets/agent-job-secrets.
|
|
10
|
+
node skills/agent-job-secrets/agent-job-secrets.mjs
|
|
11
11
|
|
|
12
12
|
# Get a secret value (OAuth credentials are auto-refreshed)
|
|
13
|
-
node skills/agent-job-secrets/agent-job-secrets.
|
|
13
|
+
node skills/agent-job-secrets/agent-job-secrets.mjs get MY_CREDENTIALS
|
|
14
14
|
|
|
15
15
|
# Set/update a secret (plain string or piped value)
|
|
16
|
-
node skills/agent-job-secrets/agent-job-secrets.
|
|
17
|
-
echo "$UPDATED_CREDENTIALS" | node skills/agent-job-secrets/agent-job-secrets.
|
|
16
|
+
node skills/agent-job-secrets/agent-job-secrets.mjs set MY_KEY "value"
|
|
17
|
+
echo "$UPDATED_CREDENTIALS" | node skills/agent-job-secrets/agent-job-secrets.mjs set MY_KEY
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## Notes
|
|
@@ -18,9 +18,8 @@ if (!cmd || cmd === 'list') {
|
|
|
18
18
|
console.log('Available secrets:');
|
|
19
19
|
keys.forEach(k => {
|
|
20
20
|
const fetchRequired = secrets[k] === null;
|
|
21
|
-
console.log(` - ${k}${fetchRequired ? ' (
|
|
21
|
+
console.log(` - ${k}${fetchRequired ? ' (use agent-job-secrets skill to fetch)' : ''}`);
|
|
22
22
|
});
|
|
23
|
-
console.log('\nTo get a value: agent-job-secrets.js get KEY_NAME');
|
|
24
23
|
}
|
|
25
24
|
process.exit(0);
|
|
26
25
|
}
|
|
@@ -31,33 +30,43 @@ if (!apiKey) { console.error('AGENT_JOB_TOKEN not available'); process.exit(1);
|
|
|
31
30
|
if (!appUrl) { console.error('APP_URL not available'); process.exit(1); }
|
|
32
31
|
|
|
33
32
|
if (cmd === 'get') {
|
|
34
|
-
if (!key) { console.error('Usage: agent-job-secrets
|
|
35
|
-
const
|
|
33
|
+
if (!key) { console.error('Usage: agent-job-secrets get KEY_NAME'); process.exit(1); }
|
|
34
|
+
const url = `${appUrl}/api/get-agent-job-secret?key=${encodeURIComponent(key)}`;
|
|
35
|
+
const res = await fetch(url, {
|
|
36
36
|
headers: { 'x-api-key': apiKey },
|
|
37
37
|
});
|
|
38
|
+
if (!res.ok) {
|
|
39
|
+
const body = await res.text();
|
|
40
|
+
console.error(`GET ${url} → ${res.status} ${body}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
38
43
|
const json = await res.json();
|
|
39
|
-
if (!res.ok || json.error) { console.error('Failed:', json.error || res.status); process.exit(1); }
|
|
40
44
|
console.log(json.value);
|
|
41
45
|
process.exit(0);
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
if (cmd === 'set') {
|
|
45
49
|
if (!key) {
|
|
46
|
-
console.error('Usage: agent-job-secrets
|
|
47
|
-
console.error(' echo "value" | agent-job-secrets
|
|
50
|
+
console.error('Usage: agent-job-secrets set KEY_NAME [value]');
|
|
51
|
+
console.error(' echo "value" | agent-job-secrets set KEY_NAME');
|
|
48
52
|
process.exit(1);
|
|
49
53
|
}
|
|
50
54
|
let value = inlineValue;
|
|
51
55
|
if (value === undefined) {
|
|
52
56
|
value = readFileSync('/dev/stdin', 'utf8').trim();
|
|
53
57
|
}
|
|
54
|
-
const
|
|
58
|
+
const url = `${appUrl}/api/set-agent-job-secret`;
|
|
59
|
+
const res = await fetch(url, {
|
|
55
60
|
method: 'POST',
|
|
56
61
|
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
|
|
57
62
|
body: JSON.stringify({ key, value }),
|
|
58
63
|
});
|
|
64
|
+
if (!res.ok) {
|
|
65
|
+
const body = await res.text();
|
|
66
|
+
console.error(`POST ${url} → ${res.status} ${body}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
59
69
|
const json = await res.json();
|
|
60
|
-
if (!res.ok || json.error) { console.error('Failed:', json.error || res.status); process.exit(1); }
|
|
61
70
|
console.log(`Secret "${key}" updated.`);
|
|
62
71
|
process.exit(0);
|
|
63
72
|
}
|