@1claw/cli 0.57.0 → 0.58.1
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/README.md +13 -2
- package/dist/bundled-templates/shared/index.html +109 -0
- package/dist/bundled-templates/shared/spawn_chat_ui.py +343 -0
- package/dist/bundled-templates/shared/start-with-chat-ui.sh +33 -0
- package/dist/bundled-templates/templates/agentkit/Dockerfile +3 -0
- package/dist/bundled-templates/templates/agentkit/agent.py +1 -1
- package/dist/bundled-templates/templates/agentkit/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/agno/Dockerfile +3 -0
- package/dist/bundled-templates/templates/agno/agent.py +1 -1
- package/dist/bundled-templates/templates/agno/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/agno/requirements.txt +1 -1
- package/dist/bundled-templates/templates/coder/Dockerfile +3 -0
- package/dist/bundled-templates/templates/coder/agent.py +1 -1
- package/dist/bundled-templates/templates/coder/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/crewai/Dockerfile +3 -0
- package/dist/bundled-templates/templates/crewai/agent.py +1 -1
- package/dist/bundled-templates/templates/crewai/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/elizaos/Dockerfile +3 -0
- package/dist/bundled-templates/templates/elizaos/agent.ts +1 -1
- package/dist/bundled-templates/templates/elizaos/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/langchain/Dockerfile +3 -0
- package/dist/bundled-templates/templates/langchain/agent.py +1 -1
- package/dist/bundled-templates/templates/langchain/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/llamaindex/Dockerfile +3 -0
- package/dist/bundled-templates/templates/llamaindex/agent.py +1 -1
- package/dist/bundled-templates/templates/llamaindex/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/mastra/Dockerfile +4 -1
- package/dist/bundled-templates/templates/mastra/agent.ts +1 -1
- package/dist/bundled-templates/templates/mastra/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/openai-agents/Dockerfile +3 -0
- package/dist/bundled-templates/templates/openai-agents/agent.py +1 -1
- package/dist/bundled-templates/templates/openai-agents/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/pydantic-ai/Dockerfile +3 -0
- package/dist/bundled-templates/templates/pydantic-ai/agent.py +1 -1
- package/dist/bundled-templates/templates/pydantic-ai/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/smolagents/Dockerfile +3 -0
- package/dist/bundled-templates/templates/smolagents/README.md +1 -1
- package/dist/bundled-templates/templates/smolagents/SECURITY.md +17 -0
- package/dist/bundled-templates/templates/smolagents/agent.py +11 -3
- package/dist/bundled-templates/templates/smolagents/entrypoint.sh +2 -2
- package/dist/bundled-templates/templates/typescript-sdk/Dockerfile +4 -1
- package/dist/bundled-templates/templates/typescript-sdk/README.md +3 -0
- package/dist/bundled-templates/templates/typescript-sdk/agent.ts +164 -15
- package/dist/bundled-templates/templates/typescript-sdk/entrypoint.sh +2 -2
- package/dist/src/commands/agent.js +58 -0
- package/dist/src/commands/agent.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +20 -21
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/platform.js +90 -2
- package/dist/src/commands/platform.js.map +1 -1
- package/dist/src/commands/proxy.d.ts.map +1 -1
- package/dist/src/commands/proxy.js +7 -23
- package/dist/src/commands/proxy.js.map +1 -1
- package/dist/src/commands/spawn.d.ts.map +1 -1
- package/dist/src/commands/spawn.js +39 -19
- package/dist/src/commands/spawn.js.map +1 -1
- package/dist/src/lib/agent-key.d.ts +13 -0
- package/dist/src/lib/agent-key.d.ts.map +1 -0
- package/dist/src/lib/agent-key.js +45 -0
- package/dist/src/lib/agent-key.js.map +1 -0
- package/dist/src/lib/docker-client.js +1 -1
- package/dist/src/lib/docker-client.js.map +1 -1
- package/package.json +1 -1
|
@@ -110,6 +110,6 @@ Credentials stay in the host daemon — this container never sees secret values.
|
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
if __name__ == "__main__":
|
|
113
|
-
port = int(os.environ.get("
|
|
113
|
+
port = int(os.environ.get("AGENT_INTERNAL_PORT", "3001"))
|
|
114
114
|
print(f"LangChain agent listening on http://0.0.0.0:{port}")
|
|
115
115
|
app.run(host="0.0.0.0", port=port)
|
|
@@ -92,6 +92,6 @@ Credentials stay in the host daemon — this container never sees secret values.
|
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
if __name__ == "__main__":
|
|
95
|
-
port = int(os.environ.get("
|
|
95
|
+
port = int(os.environ.get("AGENT_INTERNAL_PORT", "3001"))
|
|
96
96
|
print(f"LlamaIndex agent listening on http://0.0.0.0:{port}")
|
|
97
97
|
app.run(host="0.0.0.0", port=port)
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
FROM node:22-slim
|
|
2
2
|
|
|
3
3
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
4
|
-
curl tini && \
|
|
4
|
+
curl tini python3 && \
|
|
5
5
|
rm -rf /var/lib/apt/lists/*
|
|
6
6
|
|
|
7
7
|
WORKDIR /app
|
|
8
8
|
|
|
9
|
+
COPY shared /app/shared
|
|
10
|
+
RUN chmod +x /app/shared/start-with-chat-ui.sh
|
|
11
|
+
|
|
9
12
|
COPY package.json tsconfig.json ./
|
|
10
13
|
RUN npm install --omit=dev 2>/dev/null || npm install
|
|
11
14
|
|
|
@@ -63,7 +63,7 @@ async function handleChat(message: string): Promise<string> {
|
|
|
63
63
|
return text;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
const PORT = parseInt(process.env.
|
|
66
|
+
const PORT = parseInt(process.env.AGENT_INTERNAL_PORT ?? "3001", 10);
|
|
67
67
|
|
|
68
68
|
const server = createServer(async (req, res) => {
|
|
69
69
|
if (req.url === "/health" && req.method === "GET") {
|
|
@@ -93,6 +93,6 @@ Credentials stay in the host daemon — this container never sees secret values.
|
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
if __name__ == "__main__":
|
|
96
|
-
port = int(os.environ.get("
|
|
96
|
+
port = int(os.environ.get("AGENT_INTERNAL_PORT", "3001"))
|
|
97
97
|
print(f"OpenAI Agents SDK agent listening on http://0.0.0.0:{port}")
|
|
98
98
|
app.run(host="0.0.0.0", port=port)
|
|
@@ -105,6 +105,6 @@ Credentials stay in the host daemon — this container never sees secret values.
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
if __name__ == "__main__":
|
|
108
|
-
port = int(os.environ.get("
|
|
108
|
+
port = int(os.environ.get("AGENT_INTERNAL_PORT", "3001"))
|
|
109
109
|
print(f"Pydantic AI agent listening on http://0.0.0.0:{port}")
|
|
110
110
|
app.run(host="0.0.0.0", port=port)
|
|
@@ -16,7 +16,7 @@ Or with your own API key:
|
|
|
16
16
|
|
|
17
17
|
## What's included
|
|
18
18
|
|
|
19
|
-
- **agent.py** —
|
|
19
|
+
- **agent.py** — ToolCallingAgent by default (set `ONECLAW_SMOLAGENTS_CODE_MODE=1` for CodeAgent)
|
|
20
20
|
- **Flask server** on port 3000 with `/chat` and `/health` endpoints
|
|
21
21
|
- **Shroud integration** — LLM calls are routed through Shroud for inspection
|
|
22
22
|
- **MCP server** — started automatically for tool access to 1Claw vault
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# smolagents template security
|
|
2
|
+
|
|
3
|
+
This template defaults to HuggingFace `ToolCallingAgent`, which only invokes
|
|
4
|
+
registered tools. It does **not** execute arbitrary Python.
|
|
5
|
+
|
|
6
|
+
`CodeAgent` (LLM-generated Python) is available behind
|
|
7
|
+
`ONECLAW_SMOLAGENTS_CODE_MODE=1`. Treat that mode as high-trust / development
|
|
8
|
+
only:
|
|
9
|
+
|
|
10
|
+
- Run it only inside the 1Claw spawn container (non-root, network-scoped).
|
|
11
|
+
- Do not expose the `/chat` port to the public internet.
|
|
12
|
+
- Route LLM traffic through Shroud (`ONECLAW_LLM_VIA_SHROUD=true`).
|
|
13
|
+
- Never deploy CodeAgent mode on a host that holds production credentials
|
|
14
|
+
outside the vault.
|
|
15
|
+
|
|
16
|
+
The 1Claw host injects secrets via the daemon; the container should not
|
|
17
|
+
receive raw API keys.
|
|
@@ -7,13 +7,20 @@ The container never sees raw API keys — they are injected by the host daemon.
|
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
9
|
from flask import Flask, request, jsonify
|
|
10
|
-
from smolagents import CodeAgent, tool, OpenAIServerModel
|
|
10
|
+
from smolagents import CodeAgent, ToolCallingAgent, tool, OpenAIServerModel
|
|
11
11
|
|
|
12
12
|
app = Flask(__name__)
|
|
13
13
|
|
|
14
14
|
SHROUD_URL = os.environ.get("ONECLAW_SHROUD_URL", "https://shroud.1claw.xyz")
|
|
15
15
|
LLM_VIA_SHROUD = os.environ.get("ONECLAW_LLM_VIA_SHROUD", "").lower() == "true"
|
|
16
16
|
MODEL = os.environ.get("ONECLAW_SHROUD_MODEL", "gpt-4o-mini")
|
|
17
|
+
# CodeAgent executes LLM-generated Python. Off by default; only enable inside
|
|
18
|
+
# an isolated container with a human-set env flag.
|
|
19
|
+
CODE_MODE = os.environ.get("ONECLAW_SMOLAGENTS_CODE_MODE", "").lower() in (
|
|
20
|
+
"1",
|
|
21
|
+
"true",
|
|
22
|
+
"yes",
|
|
23
|
+
)
|
|
17
24
|
|
|
18
25
|
|
|
19
26
|
@tool
|
|
@@ -44,7 +51,8 @@ def create_agent():
|
|
|
44
51
|
api_key="shroud-injected",
|
|
45
52
|
)
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
agent_cls = CodeAgent if CODE_MODE else ToolCallingAgent
|
|
55
|
+
return agent_cls(
|
|
48
56
|
tools=[hello_world, list_env_vars],
|
|
49
57
|
model=model,
|
|
50
58
|
)
|
|
@@ -92,6 +100,6 @@ Credentials stay in the host daemon — this container never sees secret values.
|
|
|
92
100
|
|
|
93
101
|
|
|
94
102
|
if __name__ == "__main__":
|
|
95
|
-
port = int(os.environ.get("
|
|
103
|
+
port = int(os.environ.get("AGENT_INTERNAL_PORT", "3001"))
|
|
96
104
|
print(f"smolagents agent listening on http://0.0.0.0:{port}")
|
|
97
105
|
app.run(host="0.0.0.0", port=port)
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
FROM node:22-slim
|
|
2
2
|
|
|
3
3
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
4
|
-
curl tini && \
|
|
4
|
+
curl tini python3 && \
|
|
5
5
|
rm -rf /var/lib/apt/lists/*
|
|
6
6
|
|
|
7
7
|
WORKDIR /app
|
|
8
8
|
|
|
9
|
+
COPY shared /app/shared
|
|
10
|
+
RUN chmod +x /app/shared/start-with-chat-ui.sh
|
|
11
|
+
|
|
9
12
|
COPY package.json tsconfig.json ./
|
|
10
13
|
RUN npm install --omit=dev 2>/dev/null || npm install
|
|
11
14
|
|
|
@@ -5,9 +5,12 @@ Plain TypeScript agent using [@1claw/sdk](https://www.npmjs.com/package/@1claw/s
|
|
|
5
5
|
## Quick start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
+
# spawn starts the host daemon automatically (no separate `1claw daemon start` needed)
|
|
8
9
|
1claw spawn typescript-sdk --agent-key ocv_YOUR_KEY
|
|
9
10
|
```
|
|
10
11
|
|
|
12
|
+
Key-only `ocv_…` auth is supported — spawn resolves your agent ID via Vault token exchange and stores Shroud credentials in the host daemon. The container routes LLM calls through the daemon `/proxy` endpoint; it never sees raw keys.
|
|
13
|
+
|
|
11
14
|
## What's included
|
|
12
15
|
|
|
13
16
|
- `agent.ts` — TypeScript agent with `@1claw/sdk` client, AI SDK tool calling, and `/chat` + `/health` endpoints
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Plain TypeScript agent using @1claw/sdk for vault access and signing,
|
|
3
3
|
* with Vercel AI SDK for LLM interaction through Shroud.
|
|
4
4
|
*
|
|
5
|
-
* LLM calls
|
|
6
|
-
*
|
|
5
|
+
* LLM calls route through the host daemon's /proxy when ONECLAW_SHROUD_SECRET
|
|
6
|
+
* is set (spawn/init cloud mode). The container never sees agent or provider keys.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { request as httpRequest } from "node:http";
|
|
9
10
|
import { createServer } from "node:http";
|
|
10
11
|
import { OneclawClient } from "@1claw/sdk";
|
|
11
12
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
@@ -15,7 +16,12 @@ import { z } from "zod";
|
|
|
15
16
|
const SHROUD_URL = process.env.ONECLAW_SHROUD_URL ?? "https://shroud.1claw.xyz";
|
|
16
17
|
const LLM_VIA_SHROUD = process.env.ONECLAW_LLM_VIA_SHROUD === "true";
|
|
17
18
|
const MODEL = process.env.ONECLAW_SHROUD_MODEL ?? "gpt-4o-mini";
|
|
18
|
-
const PORT = parseInt(process.env.
|
|
19
|
+
const PORT = parseInt(process.env.AGENT_INTERNAL_PORT ?? "3001", 10);
|
|
20
|
+
const DAEMON_SOCKET = process.env.ONECLAW_DAEMON_SOCKET ?? "/run/1claw/daemon.sock";
|
|
21
|
+
const SECRET_PREFIX = process.env.ONECLAW_SECRET_PREFIX ?? "";
|
|
22
|
+
const SHROUD_SECRET = process.env.ONECLAW_SHROUD_SECRET ?? "";
|
|
23
|
+
const SHROUD_API_KEY_SECRET = process.env.ONECLAW_SHROUD_API_KEY_SECRET ?? "";
|
|
24
|
+
const SHROUD_PROVIDER = process.env.ONECLAW_SHROUD_PROVIDER ?? "openai";
|
|
19
25
|
|
|
20
26
|
const conversationHistory: { role: "user" | "assistant"; content: string }[] = [];
|
|
21
27
|
|
|
@@ -25,9 +31,144 @@ const claw = new OneclawClient({
|
|
|
25
31
|
baseUrl: process.env.ONECLAW_BASE_URL ?? "https://api.1claw.xyz",
|
|
26
32
|
});
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
:
|
|
34
|
+
function daemonRequest(
|
|
35
|
+
method: string,
|
|
36
|
+
path: string,
|
|
37
|
+
body?: unknown,
|
|
38
|
+
): Promise<{ status: number; body: Record<string, unknown> | null }> {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const payload = body ? JSON.stringify(body) : undefined;
|
|
41
|
+
const req = httpRequest(
|
|
42
|
+
{
|
|
43
|
+
socketPath: DAEMON_SOCKET,
|
|
44
|
+
path,
|
|
45
|
+
method,
|
|
46
|
+
headers: {
|
|
47
|
+
"Content-Type": "application/json",
|
|
48
|
+
...(SECRET_PREFIX ? { "X-Secret-Prefix": SECRET_PREFIX } : {}),
|
|
49
|
+
...(payload ? { "Content-Length": Buffer.byteLength(payload) } : {}),
|
|
50
|
+
},
|
|
51
|
+
timeout: 120_000,
|
|
52
|
+
},
|
|
53
|
+
(res) => {
|
|
54
|
+
const chunks: Buffer[] = [];
|
|
55
|
+
res.on("data", (c) => chunks.push(c as Buffer));
|
|
56
|
+
res.on("end", () => {
|
|
57
|
+
const text = Buffer.concat(chunks).toString("utf-8");
|
|
58
|
+
let parsed: Record<string, unknown> | null = null;
|
|
59
|
+
try {
|
|
60
|
+
parsed = text ? (JSON.parse(text) as Record<string, unknown>) : null;
|
|
61
|
+
} catch {
|
|
62
|
+
parsed = { raw: text };
|
|
63
|
+
}
|
|
64
|
+
resolve({ status: res.statusCode ?? 0, body: parsed });
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
req.on("error", reject);
|
|
69
|
+
req.on("timeout", () => req.destroy(new Error("daemon request timed out")));
|
|
70
|
+
if (payload) req.write(payload);
|
|
71
|
+
req.end();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Route outbound LLM HTTP through the host daemon (injects X-Shroud-Agent-Key). */
|
|
76
|
+
async function proxyViaDaemon(url: string, init?: RequestInit): Promise<Response> {
|
|
77
|
+
if (!SHROUD_SECRET) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
"Shroud is enabled but ONECLAW_SHROUD_SECRET is missing. Re-run spawn with --agent-key ocv_... so the daemon can inject credentials.",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const headers: Record<string, string> = {};
|
|
84
|
+
if (init?.headers) {
|
|
85
|
+
const h = init.headers;
|
|
86
|
+
if (h instanceof Headers) {
|
|
87
|
+
h.forEach((v, k) => {
|
|
88
|
+
headers[k] = v;
|
|
89
|
+
});
|
|
90
|
+
} else if (Array.isArray(h)) {
|
|
91
|
+
for (const [k, v] of h) headers[k] = v;
|
|
92
|
+
} else {
|
|
93
|
+
Object.assign(headers, h as Record<string, string>);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
delete headers.authorization;
|
|
97
|
+
delete headers.Authorization;
|
|
98
|
+
headers["X-Shroud-Provider"] = headers["X-Shroud-Provider"] ?? SHROUD_PROVIDER;
|
|
99
|
+
|
|
100
|
+
let body: string | undefined;
|
|
101
|
+
if (typeof init?.body === "string") {
|
|
102
|
+
body = init.body;
|
|
103
|
+
} else if (init?.body != null) {
|
|
104
|
+
body = String(init.body);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const proxyBody: Record<string, unknown> = {
|
|
108
|
+
secretName: SHROUD_SECRET,
|
|
109
|
+
url,
|
|
110
|
+
method: init?.method ?? "GET",
|
|
111
|
+
headers,
|
|
112
|
+
body,
|
|
113
|
+
};
|
|
114
|
+
if (SHROUD_API_KEY_SECRET) {
|
|
115
|
+
proxyBody.injectSecrets = [SHROUD_API_KEY_SECRET];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const r = await daemonRequest("POST", "/proxy", proxyBody);
|
|
119
|
+
if (r.status !== 200) {
|
|
120
|
+
const detail =
|
|
121
|
+
(r.body && (r.body.error as string)) ||
|
|
122
|
+
(r.body ? JSON.stringify(r.body) : "unknown daemon error");
|
|
123
|
+
throw new Error(`Daemon proxy failed (${r.status}): ${detail}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const upstreamStatus = (r.body?.status as number) ?? 502;
|
|
127
|
+
const upstreamBody =
|
|
128
|
+
typeof r.body?.body === "string"
|
|
129
|
+
? r.body.body
|
|
130
|
+
: JSON.stringify(r.body?.body ?? {});
|
|
131
|
+
|
|
132
|
+
let errorMessage: string | undefined;
|
|
133
|
+
if (upstreamStatus >= 400) {
|
|
134
|
+
try {
|
|
135
|
+
const parsed = JSON.parse(upstreamBody) as {
|
|
136
|
+
error?: string | { message?: string };
|
|
137
|
+
message?: string;
|
|
138
|
+
};
|
|
139
|
+
if (typeof parsed.error === "string") {
|
|
140
|
+
errorMessage = parsed.error;
|
|
141
|
+
} else if (parsed.error && typeof parsed.error.message === "string") {
|
|
142
|
+
errorMessage = parsed.error.message;
|
|
143
|
+
} else if (typeof parsed.message === "string") {
|
|
144
|
+
errorMessage = parsed.message;
|
|
145
|
+
}
|
|
146
|
+
} catch {
|
|
147
|
+
errorMessage = upstreamBody.slice(0, 400);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (errorMessage) {
|
|
152
|
+
throw new Error(errorMessage);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const responseHeaders = (r.body?.headers as Record<string, string>) ?? {
|
|
156
|
+
"Content-Type": "application/json",
|
|
157
|
+
};
|
|
158
|
+
return new Response(upstreamBody, {
|
|
159
|
+
status: upstreamStatus,
|
|
160
|
+
headers: responseHeaders,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const openai =
|
|
165
|
+
LLM_VIA_SHROUD && SHROUD_SECRET
|
|
166
|
+
? createOpenAI({
|
|
167
|
+
baseURL: `${SHROUD_URL.replace(/\/+$/, "")}/v1`,
|
|
168
|
+
apiKey: "daemon-injected",
|
|
169
|
+
fetch: proxyViaDaemon as typeof fetch,
|
|
170
|
+
})
|
|
171
|
+
: null;
|
|
31
172
|
|
|
32
173
|
const listSecretsTool = tool({
|
|
33
174
|
description: "List secrets in the 1Claw vault (names only, no values)",
|
|
@@ -37,12 +178,14 @@ const listSecretsTool = tool({
|
|
|
37
178
|
const vaultId = process.env.ONECLAW_VAULT_ID;
|
|
38
179
|
if (!vaultId) return "No vault configured.";
|
|
39
180
|
const resp = await claw.secrets.list(vaultId, prefix);
|
|
40
|
-
const secrets = (resp as
|
|
181
|
+
const secrets = (resp as { data?: { secrets?: Array<{ path: string; type?: string }> } })
|
|
182
|
+
.data?.secrets ?? [];
|
|
41
183
|
return secrets.length
|
|
42
|
-
? secrets.map((s
|
|
184
|
+
? secrets.map((s) => ` ${s.path} (${s.type ?? "generic"})`).join("\n")
|
|
43
185
|
: "No secrets found.";
|
|
44
|
-
} catch (e:
|
|
45
|
-
|
|
186
|
+
} catch (e: unknown) {
|
|
187
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
188
|
+
return `Error: ${msg}`;
|
|
46
189
|
}
|
|
47
190
|
},
|
|
48
191
|
});
|
|
@@ -61,7 +204,7 @@ const envTool = tool({
|
|
|
61
204
|
|
|
62
205
|
async function handleChat(message: string): Promise<string> {
|
|
63
206
|
if (!openai) {
|
|
64
|
-
return "No LLM configured.
|
|
207
|
+
return "No LLM configured. Re-run spawn with --agent-key ocv_... (and optional --llm-api-key). The host daemon injects Shroud credentials.";
|
|
65
208
|
}
|
|
66
209
|
|
|
67
210
|
conversationHistory.push({ role: "user", content: message });
|
|
@@ -84,7 +227,12 @@ const server = createServer(async (req, res) => {
|
|
|
84
227
|
if (req.url === "/health" && req.method === "GET") {
|
|
85
228
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
86
229
|
res.end(
|
|
87
|
-
JSON.stringify({
|
|
230
|
+
JSON.stringify({
|
|
231
|
+
status: "ok",
|
|
232
|
+
framework: "typescript-sdk",
|
|
233
|
+
llm_wired: LLM_VIA_SHROUD && !!SHROUD_SECRET,
|
|
234
|
+
daemon_socket: DAEMON_SOCKET,
|
|
235
|
+
}),
|
|
88
236
|
);
|
|
89
237
|
return;
|
|
90
238
|
}
|
|
@@ -92,7 +240,7 @@ const server = createServer(async (req, res) => {
|
|
|
92
240
|
if (req.url === "/chat" && req.method === "POST") {
|
|
93
241
|
const chunks: Buffer[] = [];
|
|
94
242
|
for await (const chunk of req) chunks.push(chunk as Buffer);
|
|
95
|
-
const body = JSON.parse(Buffer.concat(chunks).toString());
|
|
243
|
+
const body = JSON.parse(Buffer.concat(chunks).toString()) as { message?: string };
|
|
96
244
|
const message = (body.message ?? "").trim();
|
|
97
245
|
|
|
98
246
|
if (!message) {
|
|
@@ -105,9 +253,10 @@ const server = createServer(async (req, res) => {
|
|
|
105
253
|
const response = await handleChat(message);
|
|
106
254
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
107
255
|
res.end(JSON.stringify({ response, framework: "typescript-sdk" }));
|
|
108
|
-
} catch (e:
|
|
256
|
+
} catch (e: unknown) {
|
|
257
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
109
258
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
110
|
-
res.end(JSON.stringify({ error:
|
|
259
|
+
res.end(JSON.stringify({ error: msg }));
|
|
111
260
|
}
|
|
112
261
|
return;
|
|
113
262
|
}
|
|
@@ -1738,4 +1738,62 @@ accountsCommand
|
|
|
1738
1738
|
}
|
|
1739
1739
|
});
|
|
1740
1740
|
registerAgentBindingCommands(agentCommand);
|
|
1741
|
+
const automationAgentCommand = agentCommand
|
|
1742
|
+
.command("automation")
|
|
1743
|
+
.description("Agent-scoped automation commands (requires agent JWT)");
|
|
1744
|
+
automationAgentCommand
|
|
1745
|
+
.command("create <agent-id> <name>")
|
|
1746
|
+
.description("Create a simple automation for the calling agent (manual/webhook; log/notify/memory/wait steps)")
|
|
1747
|
+
.option("--trigger <type>", "manual or webhook", "manual")
|
|
1748
|
+
.option("--workflow <json-or-file>", "Workflow spec JSON or @file path", '{"steps":[{"type":"log","message":"hello"}]}')
|
|
1749
|
+
.option("--auto-trigger", "Trigger immediately after create (manual only)")
|
|
1750
|
+
.option("--token <jwt>", "Agent JWT (or set ONECLAW_AGENT_TOKEN)")
|
|
1751
|
+
.option("--api-key <key>", "Agent API key to exchange (or ONECLAW_AGENT_API_KEY)")
|
|
1752
|
+
.option("--json", "Output as JSON")
|
|
1753
|
+
.action(async (agentId, name, opts) => {
|
|
1754
|
+
try {
|
|
1755
|
+
let agentToken = opts.token ?? process.env.ONECLAW_AGENT_TOKEN ?? "";
|
|
1756
|
+
if (!agentToken) {
|
|
1757
|
+
const apiKey = opts.apiKey ?? process.env.ONECLAW_AGENT_API_KEY ?? "";
|
|
1758
|
+
if (!apiKey) {
|
|
1759
|
+
throw new Error("Provide --token, ONECLAW_AGENT_TOKEN, --api-key, or ONECLAW_AGENT_API_KEY");
|
|
1760
|
+
}
|
|
1761
|
+
const exchanged = await api("/auth/agent-token", {
|
|
1762
|
+
method: "POST",
|
|
1763
|
+
body: { agent_id: agentId, api_key: apiKey },
|
|
1764
|
+
token: undefined,
|
|
1765
|
+
});
|
|
1766
|
+
agentToken = exchanged.access_token;
|
|
1767
|
+
}
|
|
1768
|
+
let workflowSpec;
|
|
1769
|
+
if (opts.workflow.startsWith("@")) {
|
|
1770
|
+
workflowSpec = JSON.parse(readFileSync(opts.workflow.slice(1), "utf8"));
|
|
1771
|
+
}
|
|
1772
|
+
else {
|
|
1773
|
+
workflowSpec = JSON.parse(opts.workflow);
|
|
1774
|
+
}
|
|
1775
|
+
const result = await api(`/agents/${agentId}/automations`, {
|
|
1776
|
+
method: "POST",
|
|
1777
|
+
token: agentToken,
|
|
1778
|
+
body: {
|
|
1779
|
+
name,
|
|
1780
|
+
trigger_type: opts.trigger,
|
|
1781
|
+
workflow_spec: workflowSpec,
|
|
1782
|
+
auto_trigger: Boolean(opts.autoTrigger),
|
|
1783
|
+
},
|
|
1784
|
+
});
|
|
1785
|
+
const automation = (result.automation ?? result);
|
|
1786
|
+
if (opts.json) {
|
|
1787
|
+
printJson(automation);
|
|
1788
|
+
return;
|
|
1789
|
+
}
|
|
1790
|
+
printSuccess(`Agent automation created: ${String(automation.name ?? name)} (${String(automation.id ?? "")})`);
|
|
1791
|
+
if (typeof result.webhook_url === "string") {
|
|
1792
|
+
printKeyValue([["Webhook URL", result.webhook_url]]);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
catch (err) {
|
|
1796
|
+
handleError(err);
|
|
1797
|
+
}
|
|
1798
|
+
});
|
|
1741
1799
|
//# sourceMappingURL=agent.js.map
|