appback-remoteagent 0.13.4 → 0.13.5
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/bot.js +4 -0
- package/dist/secret-helper.js +23 -5
- package/dist/services/agent-memory-service.js +39 -2
- package/package.json +1 -1
package/dist/bot.js
CHANGED
|
@@ -1571,6 +1571,10 @@ function formatSecretHelp() {
|
|
|
1571
1571
|
"```text",
|
|
1572
1572
|
"node \"$REMOTEAGENT_SECRET_BIN\" get <KEY>",
|
|
1573
1573
|
"```",
|
|
1574
|
+
"If an agent obtains a new secret value such as an OAuth refresh token, it can delegate storage without printing the value:",
|
|
1575
|
+
"```text",
|
|
1576
|
+
"printf '%s' \"$VALUE\" | node \"$REMOTEAGENT_SECRET_BIN\" set <KEY>",
|
|
1577
|
+
"```",
|
|
1574
1578
|
].join("\n");
|
|
1575
1579
|
}
|
|
1576
1580
|
function formatRuntimeOptions() {
|
package/dist/secret-helper.js
CHANGED
|
@@ -2,17 +2,28 @@
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
|
-
import { readSecretValue } from "./services/agent-memory-service.js";
|
|
6
|
-
function main() {
|
|
5
|
+
import { readSecretValue, writeSecretValue } from "./services/agent-memory-service.js";
|
|
6
|
+
async function main() {
|
|
7
7
|
const [command, key] = process.argv.slice(2);
|
|
8
|
-
if (command
|
|
9
|
-
process.stderr.write("Usage: secret-helper get <KEY>\n");
|
|
8
|
+
if (!["get", "set"].includes(command ?? "") || !key) {
|
|
9
|
+
process.stderr.write("Usage: secret-helper get <KEY>\n secret-helper set <KEY> # reads value from stdin\n");
|
|
10
10
|
process.exitCode = 2;
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
13
|
const dataDir = process.env.REMOTEAGENT_DATA_DIR?.trim()
|
|
14
14
|
|| process.env.DATA_DIR?.trim()
|
|
15
15
|
|| path.join(os.homedir(), ".remoteagent");
|
|
16
|
+
if (command === "set") {
|
|
17
|
+
const value = await readStdin();
|
|
18
|
+
if (!value) {
|
|
19
|
+
process.stderr.write("Secret value was empty.\n");
|
|
20
|
+
process.exitCode = 2;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
writeSecretValue(dataDir, key.trim(), value);
|
|
24
|
+
process.stdout.write(`Stored secret: ${key.trim()}\n`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
16
27
|
const value = readSecretValue(dataDir, key.trim());
|
|
17
28
|
if (!value) {
|
|
18
29
|
process.stderr.write(`Secret was not found: ${key}\n`);
|
|
@@ -21,4 +32,11 @@ function main() {
|
|
|
21
32
|
}
|
|
22
33
|
process.stdout.write(value);
|
|
23
34
|
}
|
|
24
|
-
|
|
35
|
+
async function readStdin() {
|
|
36
|
+
const chunks = [];
|
|
37
|
+
for await (const chunk of process.stdin) {
|
|
38
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
39
|
+
}
|
|
40
|
+
return Buffer.concat(chunks).toString("utf8").trim();
|
|
41
|
+
}
|
|
42
|
+
void main();
|
|
@@ -338,8 +338,16 @@ export class AgentMemoryService {
|
|
|
338
338
|
? ["Document index:", ...docs.map((doc) => `- ${doc.keyword}: ${doc.targetPath}${doc.note ? ` (${doc.note})` : ""}`)].join("\n")
|
|
339
339
|
: undefined,
|
|
340
340
|
secrets.length > 0
|
|
341
|
-
? [
|
|
342
|
-
|
|
341
|
+
? [
|
|
342
|
+
"Secret keys available through `node \"$REMOTEAGENT_SECRET_BIN\" get <KEY>`:",
|
|
343
|
+
...secrets.map((key) => `- ${key}`),
|
|
344
|
+
"If you generate a new secret value such as an OAuth refresh token, store it without printing it:",
|
|
345
|
+
"`printf '%s' \"$VALUE\" | node \"$REMOTEAGENT_SECRET_BIN\" set <KEY>`",
|
|
346
|
+
].join("\n")
|
|
347
|
+
: [
|
|
348
|
+
"Secrets can be stored without printing values:",
|
|
349
|
+
"`printf '%s' \"$VALUE\" | node \"$REMOTEAGENT_SECRET_BIN\" set <KEY>`",
|
|
350
|
+
].join("\n"),
|
|
343
351
|
artifacts.length > 0
|
|
344
352
|
? ["Recent session artifacts:", ...artifacts.map((artifact) => `- ${artifact.id} ${artifact.kind}: ${artifact.path}`)].join("\n")
|
|
345
353
|
: undefined,
|
|
@@ -735,3 +743,32 @@ export function readSecretValue(dataDir, key) {
|
|
|
735
743
|
return undefined;
|
|
736
744
|
}
|
|
737
745
|
}
|
|
746
|
+
export function writeSecretValue(dataDir, key, value) {
|
|
747
|
+
if (!/^[A-Z0-9_.-]{1,80}$/.test(key)) {
|
|
748
|
+
throw new Error("Secret key must be 1-80 chars using A-Z, 0-9, dot, underscore, or dash.");
|
|
749
|
+
}
|
|
750
|
+
const filePath = path.join(dataDir, "managed", "secrets.json");
|
|
751
|
+
fsSync.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
752
|
+
let secrets = {};
|
|
753
|
+
try {
|
|
754
|
+
const raw = fsSync.readFileSync(filePath, "utf8");
|
|
755
|
+
secrets = JSON.parse(raw);
|
|
756
|
+
}
|
|
757
|
+
catch {
|
|
758
|
+
secrets = {};
|
|
759
|
+
}
|
|
760
|
+
const now = new Date().toISOString();
|
|
761
|
+
secrets[key] = {
|
|
762
|
+
key,
|
|
763
|
+
value,
|
|
764
|
+
createdAt: secrets[key]?.createdAt ?? now,
|
|
765
|
+
updatedAt: now,
|
|
766
|
+
};
|
|
767
|
+
fsSync.writeFileSync(filePath, `${JSON.stringify(secrets, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
768
|
+
try {
|
|
769
|
+
fsSync.chmodSync(filePath, 0o600);
|
|
770
|
+
}
|
|
771
|
+
catch {
|
|
772
|
+
// Best effort only; some platforms do not support chmod.
|
|
773
|
+
}
|
|
774
|
+
}
|