natroc 0.0.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/AGENTS.md +494 -0
- package/LICENSE +7 -0
- package/README.md +0 -0
- package/install.ps1 +109 -0
- package/install.sh +132 -0
- package/package.json +77 -0
- package/server/dist/agent/agent-loop.d.ts +71 -0
- package/server/dist/agent/agent-loop.js +171 -0
- package/server/dist/agent/agent-loop.js.map +1 -0
- package/server/dist/agent/home-context.d.ts +29 -0
- package/server/dist/agent/home-context.js +134 -0
- package/server/dist/agent/home-context.js.map +1 -0
- package/server/dist/agent/improvement-engine.d.ts +23 -0
- package/server/dist/agent/improvement-engine.js +107 -0
- package/server/dist/agent/improvement-engine.js.map +1 -0
- package/server/dist/agent/tools/index.d.ts +14 -0
- package/server/dist/agent/tools/index.js +85 -0
- package/server/dist/agent/tools/index.js.map +1 -0
- package/server/dist/agent/tools/list-directory.d.ts +2 -0
- package/server/dist/agent/tools/list-directory.js +27 -0
- package/server/dist/agent/tools/list-directory.js.map +1 -0
- package/server/dist/agent/tools/read-file.d.ts +2 -0
- package/server/dist/agent/tools/read-file.js +30 -0
- package/server/dist/agent/tools/read-file.js.map +1 -0
- package/server/dist/agent/tools/run-command.d.ts +2 -0
- package/server/dist/agent/tools/run-command.js +72 -0
- package/server/dist/agent/tools/run-command.js.map +1 -0
- package/server/dist/agent/tools/system-info.d.ts +2 -0
- package/server/dist/agent/tools/system-info.js +28 -0
- package/server/dist/agent/tools/system-info.js.map +1 -0
- package/server/dist/agent/tools/types.d.ts +18 -0
- package/server/dist/agent/tools/types.js +2 -0
- package/server/dist/agent/tools/types.js.map +1 -0
- package/server/dist/agent/tools/util.d.ts +7 -0
- package/server/dist/agent/tools/util.js +24 -0
- package/server/dist/agent/tools/util.js.map +1 -0
- package/server/dist/agent/tools/write-file.d.ts +2 -0
- package/server/dist/agent/tools/write-file.js +25 -0
- package/server/dist/agent/tools/write-file.js.map +1 -0
- package/server/dist/app.d.ts +8 -0
- package/server/dist/app.js +39 -0
- package/server/dist/app.js.map +1 -0
- package/server/dist/auth/password.d.ts +7 -0
- package/server/dist/auth/password.js +20 -0
- package/server/dist/auth/password.js.map +1 -0
- package/server/dist/channels/channel-runtime.d.ts +32 -0
- package/server/dist/channels/channel-runtime.js +484 -0
- package/server/dist/channels/channel-runtime.js.map +1 -0
- package/server/dist/cli/agent-deliver.d.ts +14 -0
- package/server/dist/cli/agent-deliver.js +183 -0
- package/server/dist/cli/agent-deliver.js.map +1 -0
- package/server/dist/cli/args.d.ts +11 -0
- package/server/dist/cli/args.js +57 -0
- package/server/dist/cli/args.js.map +1 -0
- package/server/dist/cli/cli-token.d.ts +1 -0
- package/server/dist/cli/cli-token.js +22 -0
- package/server/dist/cli/cli-token.js.map +1 -0
- package/server/dist/cli/daemon.d.ts +2 -0
- package/server/dist/cli/daemon.js +252 -0
- package/server/dist/cli/daemon.js.map +1 -0
- package/server/dist/cli.d.ts +2 -0
- package/server/dist/cli.js +416 -0
- package/server/dist/cli.js.map +1 -0
- package/server/dist/config/natroc-home.d.ts +13 -0
- package/server/dist/config/natroc-home.js +730 -0
- package/server/dist/config/natroc-home.js.map +1 -0
- package/server/dist/gateway/agent-service.d.ts +16 -0
- package/server/dist/gateway/agent-service.js +261 -0
- package/server/dist/gateway/agent-service.js.map +1 -0
- package/server/dist/gateway/connection.d.ts +38 -0
- package/server/dist/gateway/connection.js +254 -0
- package/server/dist/gateway/connection.js.map +1 -0
- package/server/dist/gateway/gateway.d.ts +79 -0
- package/server/dist/gateway/gateway.js +150 -0
- package/server/dist/gateway/gateway.js.map +1 -0
- package/server/dist/gateway/index.d.ts +8 -0
- package/server/dist/gateway/index.js +26 -0
- package/server/dist/gateway/index.js.map +1 -0
- package/server/dist/gateway/protocol.d.ts +102 -0
- package/server/dist/gateway/protocol.js +63 -0
- package/server/dist/gateway/protocol.js.map +1 -0
- package/server/dist/gateway/rpc/agent.d.ts +3 -0
- package/server/dist/gateway/rpc/agent.js +174 -0
- package/server/dist/gateway/rpc/agent.js.map +1 -0
- package/server/dist/gateway/rpc/agents.d.ts +2 -0
- package/server/dist/gateway/rpc/agents.js +68 -0
- package/server/dist/gateway/rpc/agents.js.map +1 -0
- package/server/dist/gateway/rpc/auth.d.ts +3 -0
- package/server/dist/gateway/rpc/auth.js +180 -0
- package/server/dist/gateway/rpc/auth.js.map +1 -0
- package/server/dist/gateway/rpc/channels.d.ts +2 -0
- package/server/dist/gateway/rpc/channels.js +230 -0
- package/server/dist/gateway/rpc/channels.js.map +1 -0
- package/server/dist/gateway/rpc/conversations.d.ts +3 -0
- package/server/dist/gateway/rpc/conversations.js +36 -0
- package/server/dist/gateway/rpc/conversations.js.map +1 -0
- package/server/dist/gateway/rpc/projects.d.ts +3 -0
- package/server/dist/gateway/rpc/projects.js +49 -0
- package/server/dist/gateway/rpc/projects.js.map +1 -0
- package/server/dist/gateway/rpc/providers.d.ts +3 -0
- package/server/dist/gateway/rpc/providers.js +106 -0
- package/server/dist/gateway/rpc/providers.js.map +1 -0
- package/server/dist/gateway/rpc/usage.d.ts +2 -0
- package/server/dist/gateway/rpc/usage.js +41 -0
- package/server/dist/gateway/rpc/usage.js.map +1 -0
- package/server/dist/gateway/types.d.ts +43 -0
- package/server/dist/gateway/types.js +20 -0
- package/server/dist/gateway/types.js.map +1 -0
- package/server/dist/gateway/ws-server.d.ts +10 -0
- package/server/dist/gateway/ws-server.js +37 -0
- package/server/dist/gateway/ws-server.js.map +1 -0
- package/server/dist/index.d.ts +1 -0
- package/server/dist/index.js +9 -0
- package/server/dist/index.js.map +1 -0
- package/server/dist/local-runtime.d.ts +9 -0
- package/server/dist/local-runtime.js +16 -0
- package/server/dist/local-runtime.js.map +1 -0
- package/server/dist/providers/configured-adapters.d.ts +9 -0
- package/server/dist/providers/configured-adapters.js +34 -0
- package/server/dist/providers/configured-adapters.js.map +1 -0
- package/server/dist/providers/ollama.d.ts +23 -0
- package/server/dist/providers/ollama.js +164 -0
- package/server/dist/providers/ollama.js.map +1 -0
- package/server/dist/providers/openrouter.d.ts +24 -0
- package/server/dist/providers/openrouter.js +201 -0
- package/server/dist/providers/openrouter.js.map +1 -0
- package/server/dist/providers/thinking.d.ts +18 -0
- package/server/dist/providers/thinking.js +58 -0
- package/server/dist/providers/thinking.js.map +1 -0
- package/server/dist/providers/types.d.ts +55 -0
- package/server/dist/providers/types.js +2 -0
- package/server/dist/providers/types.js.map +1 -0
- package/server/dist/routes/schemas.d.ts +51 -0
- package/server/dist/routes/schemas.js +53 -0
- package/server/dist/routes/schemas.js.map +1 -0
- package/server/dist/runtime.d.ts +47 -0
- package/server/dist/runtime.js +29 -0
- package/server/dist/runtime.js.map +1 -0
- package/server/dist/server.d.ts +11 -0
- package/server/dist/server.js +19 -0
- package/server/dist/server.js.map +1 -0
- package/server/dist/storage/agent-repository.d.ts +59 -0
- package/server/dist/storage/agent-repository.js +192 -0
- package/server/dist/storage/agent-repository.js.map +1 -0
- package/server/dist/storage/auth-repository.d.ts +49 -0
- package/server/dist/storage/auth-repository.js +139 -0
- package/server/dist/storage/auth-repository.js.map +1 -0
- package/server/dist/storage/channel-repository.d.ts +152 -0
- package/server/dist/storage/channel-repository.js +413 -0
- package/server/dist/storage/channel-repository.js.map +1 -0
- package/server/dist/storage/conversation-repository.d.ts +63 -0
- package/server/dist/storage/conversation-repository.js +196 -0
- package/server/dist/storage/conversation-repository.js.map +1 -0
- package/server/dist/storage/database.d.ts +11 -0
- package/server/dist/storage/database.js +360 -0
- package/server/dist/storage/database.js.map +1 -0
- package/server/dist/storage/memory-repository.d.ts +70 -0
- package/server/dist/storage/memory-repository.js +279 -0
- package/server/dist/storage/memory-repository.js.map +1 -0
- package/server/dist/storage/project-repository.d.ts +25 -0
- package/server/dist/storage/project-repository.js +67 -0
- package/server/dist/storage/project-repository.js.map +1 -0
- package/server/dist/storage/provider-repository.d.ts +44 -0
- package/server/dist/storage/provider-repository.js +159 -0
- package/server/dist/storage/provider-repository.js.map +1 -0
- package/server/dist/storage/tool-call-repository.d.ts +35 -0
- package/server/dist/storage/tool-call-repository.js +83 -0
- package/server/dist/storage/tool-call-repository.js.map +1 -0
- package/server/dist/storage/usage-repository.d.ts +76 -0
- package/server/dist/storage/usage-repository.js +249 -0
- package/server/dist/storage/usage-repository.js.map +1 -0
- package/server/dist/storage/vault.d.ts +3 -0
- package/server/dist/storage/vault.js +57 -0
- package/server/dist/storage/vault.js.map +1 -0
- package/ui/README.md +0 -0
- package/ui/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/ui/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/ui/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/ui/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/ui/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/ui/dist/assets/index-DKaFmZNO.js +114 -0
- package/ui/dist/assets/index-DOfPcjx3.css +2 -0
- package/ui/dist/index.html +14 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const validKinds = new Set(["memory", "knowledge", "skill"]);
|
|
2
|
+
export function parseImprovementProposalResponse(response) {
|
|
3
|
+
const parsed = safeJsonParse(extractJson(response));
|
|
4
|
+
const proposals = getRecordArray(parsed, "proposals");
|
|
5
|
+
return proposals.flatMap((proposal) => {
|
|
6
|
+
const normalized = normalizeModelProposal(proposal);
|
|
7
|
+
return normalized ? [normalized] : [];
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
export function buildHeuristicImprovementProposals(input) {
|
|
11
|
+
const userMessage = input.userMessage.trim();
|
|
12
|
+
if (!asksToRemember(userMessage)) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
kind: "memory",
|
|
18
|
+
title: "User requested memory",
|
|
19
|
+
content: userMessage
|
|
20
|
+
.replace(/^(tolong\s+)?(ingat|remember)\s+/i, "")
|
|
21
|
+
.trim(),
|
|
22
|
+
reason: "The user explicitly asked the agent to remember this.",
|
|
23
|
+
sourceConversationId: input.conversationId ?? null
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
export async function generateImprovementProposals(input) {
|
|
28
|
+
const transcript = input.messages
|
|
29
|
+
.map((message) => `${message.role.toUpperCase()}: ${message.content}`)
|
|
30
|
+
.join("\n\n");
|
|
31
|
+
const response = await input.adapter.chat({
|
|
32
|
+
message: "Review this conversation and propose useful Natroc improvements.",
|
|
33
|
+
model: input.model,
|
|
34
|
+
messages: [
|
|
35
|
+
{
|
|
36
|
+
role: "system",
|
|
37
|
+
content: 'You are Natroc\'s self-improvement reviewer. Return JSON only. Propose durable memory, knowledge, or skill improvements that would help future sessions. Do not include secrets. If there is nothing durable, return {"proposals":[]}. Shape: {"proposals":[{"kind":"memory|knowledge|skill","title":"short title","content":"durable note","reason":"why this helps"}]}'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
role: "user",
|
|
41
|
+
content: transcript
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
});
|
|
45
|
+
return parseImprovementProposalResponse(response.content).map((proposal) => ({
|
|
46
|
+
...proposal,
|
|
47
|
+
sourceConversationId: proposal.sourceConversationId ?? input.conversationId ?? null
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
function asksToRemember(value) {
|
|
51
|
+
return /\b(ingat|remember|catat|simpan\s+memory|save\s+memory)\b/i.test(value);
|
|
52
|
+
}
|
|
53
|
+
function normalizeModelProposal(value) {
|
|
54
|
+
const kind = readString(value.kind);
|
|
55
|
+
const title = readString(value.title);
|
|
56
|
+
const content = readString(value.content);
|
|
57
|
+
const reason = readString(value.reason);
|
|
58
|
+
if (!validKinds.has(kind) || !content) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
kind,
|
|
63
|
+
title: title || defaultTitle(kind),
|
|
64
|
+
content,
|
|
65
|
+
reason: reason || "Generated by Natroc self-improvement review.",
|
|
66
|
+
sourceConversationId: null
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function defaultTitle(kind) {
|
|
70
|
+
if (kind === "knowledge")
|
|
71
|
+
return "Knowledge improvement";
|
|
72
|
+
if (kind === "skill")
|
|
73
|
+
return "Skill improvement";
|
|
74
|
+
return "Memory improvement";
|
|
75
|
+
}
|
|
76
|
+
function extractJson(value) {
|
|
77
|
+
const fenced = value.match(/```(?:json)?\s*([\s\S]*?)```/i)?.[1];
|
|
78
|
+
const raw = fenced ?? value;
|
|
79
|
+
const first = raw.indexOf("{");
|
|
80
|
+
const last = raw.lastIndexOf("}");
|
|
81
|
+
if (first === -1 || last === -1 || last < first) {
|
|
82
|
+
return "{}";
|
|
83
|
+
}
|
|
84
|
+
return raw.slice(first, last + 1);
|
|
85
|
+
}
|
|
86
|
+
function safeJsonParse(value) {
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(value);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function getRecordArray(value, key) {
|
|
95
|
+
if (!isRecord(value)) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
const maybeArray = value[key];
|
|
99
|
+
return Array.isArray(maybeArray) ? maybeArray.filter(isRecord) : [];
|
|
100
|
+
}
|
|
101
|
+
function readString(value) {
|
|
102
|
+
return typeof value === "string" ? value.trim() : "";
|
|
103
|
+
}
|
|
104
|
+
function isRecord(value) {
|
|
105
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=improvement-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"improvement-engine.js","sourceRoot":"","sources":["../../src/agent/improvement-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AgentTool, ToolContext, ToolMode, ToolResult } from "./types.js";
|
|
2
|
+
declare const ALL_TOOLS: AgentTool[];
|
|
3
|
+
/** Reads the configured tool execution mode from the environment. */
|
|
4
|
+
export declare function getToolMode(): ToolMode;
|
|
5
|
+
/** Resolves the default working directory for tool execution. */
|
|
6
|
+
export declare function getAgentCwd(): string;
|
|
7
|
+
/** Returns the tools the agent may use given the current mode. */
|
|
8
|
+
export declare function getAvailableTools(mode: ToolMode): AgentTool[];
|
|
9
|
+
export declare function findTool(name: string): AgentTool | undefined;
|
|
10
|
+
export declare function executeTool(name: string, args: Record<string, unknown>, mode: ToolMode, context: ToolContext): Promise<ToolResult>;
|
|
11
|
+
/** Builds the tool-usage section of the agent system prompt. */
|
|
12
|
+
export declare function buildToolsPrompt(tools: AgentTool[]): string;
|
|
13
|
+
export { ALL_TOOLS };
|
|
14
|
+
export type { AgentTool, ToolContext, ToolMode, ToolResult } from "./types.js";
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import { listDirectoryTool } from "./list-directory.js";
|
|
3
|
+
import { readFileTool } from "./read-file.js";
|
|
4
|
+
import { runCommandTool } from "./run-command.js";
|
|
5
|
+
import { systemInfoTool } from "./system-info.js";
|
|
6
|
+
import { writeFileTool } from "./write-file.js";
|
|
7
|
+
const ALL_TOOLS = [
|
|
8
|
+
systemInfoTool,
|
|
9
|
+
listDirectoryTool,
|
|
10
|
+
readFileTool,
|
|
11
|
+
writeFileTool,
|
|
12
|
+
runCommandTool,
|
|
13
|
+
];
|
|
14
|
+
/** Reads the configured tool execution mode from the environment. */
|
|
15
|
+
export function getToolMode() {
|
|
16
|
+
return process.env.NATROC_AGENT_TOOL_MODE === "readonly"
|
|
17
|
+
? "readonly"
|
|
18
|
+
: "auto";
|
|
19
|
+
}
|
|
20
|
+
/** Resolves the default working directory for tool execution. */
|
|
21
|
+
export function getAgentCwd() {
|
|
22
|
+
const configured = process.env.NATROC_AGENT_CWD?.trim();
|
|
23
|
+
return configured || os.homedir();
|
|
24
|
+
}
|
|
25
|
+
/** Returns the tools the agent may use given the current mode. */
|
|
26
|
+
export function getAvailableTools(mode) {
|
|
27
|
+
if (mode === "readonly") {
|
|
28
|
+
return ALL_TOOLS.filter((tool) => tool.risk === "low");
|
|
29
|
+
}
|
|
30
|
+
return [...ALL_TOOLS];
|
|
31
|
+
}
|
|
32
|
+
export function findTool(name) {
|
|
33
|
+
return ALL_TOOLS.find((tool) => tool.name === name);
|
|
34
|
+
}
|
|
35
|
+
export async function executeTool(name, args, mode, context) {
|
|
36
|
+
const tool = getAvailableTools(mode).find((item) => item.name === name);
|
|
37
|
+
if (!tool) {
|
|
38
|
+
if (findTool(name)) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
output: `Tool "${name}" is disabled because NATROC_AGENT_TOOL_MODE is set to "readonly".`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { ok: false, output: `Unknown tool "${name}".` };
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
return await tool.execute(args, context);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
output: error instanceof Error
|
|
53
|
+
? `Tool "${name}" failed: ${error.message}`
|
|
54
|
+
: `Tool "${name}" failed.`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Builds the tool-usage section of the agent system prompt. */
|
|
59
|
+
export function buildToolsPrompt(tools) {
|
|
60
|
+
if (tools.length === 0) {
|
|
61
|
+
return [
|
|
62
|
+
"## Computer Control Tools",
|
|
63
|
+
"No tools are currently available.",
|
|
64
|
+
"Do not call tools. Reply in plain text, and explain any limitation if the user's request requires unavailable tools.",
|
|
65
|
+
].join("\n");
|
|
66
|
+
}
|
|
67
|
+
const toolLines = tools
|
|
68
|
+
.map((tool) => `- ${tool.name} [risk: ${tool.risk}]: ${tool.description}\n arguments: ${tool.parameters}`)
|
|
69
|
+
.join("\n");
|
|
70
|
+
return [
|
|
71
|
+
"## Computer Control Tools",
|
|
72
|
+
"You can control this computer by calling tools. Available tools:",
|
|
73
|
+
toolLines,
|
|
74
|
+
"",
|
|
75
|
+
"To call a tool, reply with ONLY a single JSON object and nothing else:",
|
|
76
|
+
'{"tool": "<tool name>", "args": { ... }}',
|
|
77
|
+
"Only call tools listed above. Do not call a tool that is missing from this list or disabled by the current mode.",
|
|
78
|
+
"After a tool runs you receive its result as the next message. You may then call another tool, or, when the task is finished, reply with a normal plain-text answer for the user.",
|
|
79
|
+
"If a tool is unavailable, disabled, or returns an error, do not get stuck trying the same blocked call. Explain the limitation in plain text and answer as far as possible.",
|
|
80
|
+
"A plain-text reply (no JSON tool object) is always treated as your final answer.",
|
|
81
|
+
"Never claim a file was written, a command ran, or a system change happened unless you actually called the tool and saw a successful result.",
|
|
82
|
+
].join("\n");
|
|
83
|
+
}
|
|
84
|
+
export { ALL_TOOLS };
|
|
85
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/agent/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE/C,MAAM,SAAS,GAAgB;IAC7B,cAAc;IACd,iBAAiB;IACjB,YAAY;IACZ,aAAa;IACb,cAAc;CACf,CAAA;AAED,qEAAqE;AACrE,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,UAAU;QACtD,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,MAAM,CAAA;AACZ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,WAAW;IACzB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAA;IACvD,OAAO,UAAU,IAAI,EAAE,CAAC,OAAO,EAAE,CAAA;AACnC,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,iBAAiB,CAAC,IAAc;IAC9C,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,CAAA;IACxD,CAAC;IAED,OAAO,CAAC,GAAG,SAAS,CAAC,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAY,EACZ,IAA6B,EAC7B,IAAc,EACd,OAAoB;IAEpB,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;IAEvE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACnB,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,SAAS,IAAI,oEAAoE;aAC1F,CAAA;QACH,CAAC;QAED,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,IAAI,IAAI,EAAE,CAAA;IACzD,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EACJ,KAAK,YAAY,KAAK;gBACpB,CAAC,CAAC,SAAS,IAAI,aAAa,KAAK,CAAC,OAAO,EAAE;gBAC3C,CAAC,CAAC,SAAS,IAAI,WAAW;SAC/B,CAAA;IACH,CAAC;AACH,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,gBAAgB,CAAC,KAAkB;IACjD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,2BAA2B;YAC3B,mCAAmC;YACnC,sHAAsH;SACvH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACd,CAAC;IAED,MAAM,SAAS,GAAG,KAAK;SACpB,GAAG,CACF,CAAC,IAAI,EAAE,EAAE,CACP,KAAK,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,WAAW,kBAAkB,IAAI,CAAC,UAAU,EAAE,CAC9F;SACA,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,OAAO;QACL,2BAA2B;QAC3B,kEAAkE;QAClE,SAAS;QACT,EAAE;QACF,wEAAwE;QACxE,0CAA0C;QAC1C,kHAAkH;QAClH,kLAAkL;QAClL,6KAA6K;QAC7K,kFAAkF;QAClF,6IAA6I;KAC9I,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACd,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { resolveUserPath } from "./util.js";
|
|
4
|
+
const schema = z.object({
|
|
5
|
+
path: z.string().optional(),
|
|
6
|
+
});
|
|
7
|
+
export const listDirectoryTool = {
|
|
8
|
+
name: "list_directory",
|
|
9
|
+
description: "List the files and folders inside a directory on the computer.",
|
|
10
|
+
parameters: '{ "path": string (optional, defaults to the working directory) }',
|
|
11
|
+
risk: "low",
|
|
12
|
+
execute: async (args, context) => {
|
|
13
|
+
const { path } = schema.parse(args);
|
|
14
|
+
const target = resolveUserPath(path, context.cwd);
|
|
15
|
+
const entries = await readdir(target, { withFileTypes: true });
|
|
16
|
+
const listing = entries
|
|
17
|
+
.map((entry) => `${entry.isDirectory() ? "[dir] " : "[file]"} ${entry.name}`)
|
|
18
|
+
.sort();
|
|
19
|
+
return {
|
|
20
|
+
ok: true,
|
|
21
|
+
output: listing.length > 0
|
|
22
|
+
? `${target}\n${listing.join("\n")}`
|
|
23
|
+
: `${target} (empty directory)`,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=list-directory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-directory.js","sourceRoot":"","sources":["../../../src/agent/tools/list-directory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAE1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAE3C,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAc;IAC1C,IAAI,EAAE,gBAAgB;IACtB,WAAW,EACT,gEAAgE;IAClE,UAAU,EACR,kEAAkE;IACpE,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;QAC/B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnC,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;QAEjD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QAC9D,MAAM,OAAO,GAAG,OAAO;aACpB,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CACR,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAC/D;aACA,IAAI,EAAE,CAAA;QAET,OAAO;YACL,EAAE,EAAE,IAAI;YACR,MAAM,EACJ,OAAO,CAAC,MAAM,GAAG,CAAC;gBAChB,CAAC,CAAC,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBACpC,CAAC,CAAC,GAAG,MAAM,oBAAoB;SACpC,CAAA;IACH,CAAC;CACF,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { resolveUserPath, truncateOutput } from "./util.js";
|
|
4
|
+
const schema = z.object({
|
|
5
|
+
path: z.string().min(1),
|
|
6
|
+
});
|
|
7
|
+
const MAX_FILE_BYTES = 256 * 1024;
|
|
8
|
+
export const readFileTool = {
|
|
9
|
+
name: "read_file",
|
|
10
|
+
description: "Read the text content of a file on the computer.",
|
|
11
|
+
parameters: '{ "path": string (file path, absolute or relative to the working directory) }',
|
|
12
|
+
risk: "low",
|
|
13
|
+
execute: async (args, context) => {
|
|
14
|
+
const { path } = schema.parse(args);
|
|
15
|
+
const target = resolveUserPath(path, context.cwd);
|
|
16
|
+
const info = await stat(target);
|
|
17
|
+
if (!info.isFile()) {
|
|
18
|
+
return { ok: false, output: `${target} is not a file.` };
|
|
19
|
+
}
|
|
20
|
+
if (info.size > MAX_FILE_BYTES) {
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
output: `${target} is too large to read (${info.size} bytes, limit ${MAX_FILE_BYTES} bytes).`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const content = await readFile(target, "utf8");
|
|
27
|
+
return { ok: true, output: truncateOutput(content) };
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=read-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-file.js","sourceRoot":"","sources":["../../../src/agent/tools/read-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAE3D,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAA;AAEF,MAAM,cAAc,GAAG,GAAG,GAAG,IAAI,CAAA;AAEjC,MAAM,CAAC,MAAM,YAAY,GAAc;IACrC,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,kDAAkD;IAC/D,UAAU,EACR,+EAA+E;IACjF,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;QAC/B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnC,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;QAEjD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,CAAA;QAE/B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACnB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAA;QAC1D,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,GAAG,cAAc,EAAE,CAAC;YAC/B,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,GAAG,MAAM,0BAA0B,IAAI,CAAC,IAAI,iBAAiB,cAAc,UAAU;aAC9F,CAAA;QACH,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAE9C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,CAAA;IACtD,CAAC;CACF,CAAA"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { resolveUserPath, truncateOutput } from "./util.js";
|
|
4
|
+
const schema = z.object({
|
|
5
|
+
command: z.string().min(1),
|
|
6
|
+
cwd: z.string().optional(),
|
|
7
|
+
});
|
|
8
|
+
const TIMEOUT_MS = 60_000;
|
|
9
|
+
const MAX_BUFFER = 200_000;
|
|
10
|
+
export const runCommandTool = {
|
|
11
|
+
name: "run_command",
|
|
12
|
+
description: "Run a shell command on the computer and return its stdout, stderr, and exit code. Use this for any system action not covered by another tool.",
|
|
13
|
+
parameters: '{ "command": string, "cwd": string (optional working directory) }',
|
|
14
|
+
risk: "high",
|
|
15
|
+
execute: async (args, context) => {
|
|
16
|
+
const { command, cwd } = schema.parse(args);
|
|
17
|
+
const workingDir = resolveUserPath(cwd, context.cwd);
|
|
18
|
+
return runShellCommand(command, workingDir);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
function runShellCommand(command, cwd) {
|
|
22
|
+
return new Promise((resolvePromise) => {
|
|
23
|
+
const isWindows = process.platform === "win32";
|
|
24
|
+
const shell = isWindows ? "cmd.exe" : "/bin/sh";
|
|
25
|
+
const shellFlag = isWindows ? "/c" : "-c";
|
|
26
|
+
// The full command is passed as a single argument to the shell, so there
|
|
27
|
+
// is no string interpolation and no command-injection surface here.
|
|
28
|
+
const child = spawn(shell, [shellFlag, command], {
|
|
29
|
+
cwd,
|
|
30
|
+
timeout: TIMEOUT_MS,
|
|
31
|
+
});
|
|
32
|
+
let stdout = "";
|
|
33
|
+
let stderr = "";
|
|
34
|
+
child.stdout?.on("data", (chunk) => {
|
|
35
|
+
if (stdout.length < MAX_BUFFER) {
|
|
36
|
+
stdout += chunk.toString();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
child.stderr?.on("data", (chunk) => {
|
|
40
|
+
if (stderr.length < MAX_BUFFER) {
|
|
41
|
+
stderr += chunk.toString();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
child.on("error", (error) => {
|
|
45
|
+
resolvePromise({
|
|
46
|
+
ok: false,
|
|
47
|
+
output: `Failed to run command: ${error.message}`,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
child.on("close", (code, signal) => {
|
|
51
|
+
const parts = [
|
|
52
|
+
`$ ${command}`,
|
|
53
|
+
`(working directory: ${cwd})`,
|
|
54
|
+
`exit code: ${code ?? "null"}${signal ? `, signal: ${signal}` : ""}`,
|
|
55
|
+
];
|
|
56
|
+
if (stdout.trim()) {
|
|
57
|
+
parts.push(`--- stdout ---\n${truncateOutput(stdout.trim())}`);
|
|
58
|
+
}
|
|
59
|
+
if (stderr.trim()) {
|
|
60
|
+
parts.push(`--- stderr ---\n${truncateOutput(stderr.trim())}`);
|
|
61
|
+
}
|
|
62
|
+
if (signal === "SIGTERM") {
|
|
63
|
+
parts.push(`(command timed out after ${TIMEOUT_MS / 1000}s)`);
|
|
64
|
+
}
|
|
65
|
+
resolvePromise({
|
|
66
|
+
ok: code === 0,
|
|
67
|
+
output: parts.join("\n"),
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=run-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-command.js","sourceRoot":"","sources":["../../../src/agent/tools/run-command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
export const systemInfoTool = {
|
|
3
|
+
name: "system_info",
|
|
4
|
+
description: "Get information about the host computer: operating system, architecture, hostname, current user, home directory, working directory, CPU count, memory, and Node.js version.",
|
|
5
|
+
parameters: "{} (no arguments required)",
|
|
6
|
+
risk: "low",
|
|
7
|
+
execute: async (_args, context) => {
|
|
8
|
+
const info = {
|
|
9
|
+
platform: os.platform(),
|
|
10
|
+
arch: os.arch(),
|
|
11
|
+
release: os.release(),
|
|
12
|
+
hostname: os.hostname(),
|
|
13
|
+
user: os.userInfo().username,
|
|
14
|
+
homedir: os.homedir(),
|
|
15
|
+
workingDirectory: context.cwd,
|
|
16
|
+
nodeVersion: process.version,
|
|
17
|
+
cpuCount: os.cpus().length,
|
|
18
|
+
totalMemoryMb: Math.round(os.totalmem() / 1024 / 1024),
|
|
19
|
+
freeMemoryMb: Math.round(os.freemem() / 1024 / 1024),
|
|
20
|
+
uptimeSeconds: Math.round(os.uptime()),
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
ok: true,
|
|
24
|
+
output: JSON.stringify(info, null, 2),
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=system-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system-info.js","sourceRoot":"","sources":["../../../src/agent/tools/system-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AAIxB,MAAM,CAAC,MAAM,cAAc,GAAc;IACvC,IAAI,EAAE,aAAa;IACnB,WAAW,EACT,6KAA6K;IAC/K,UAAU,EAAE,4BAA4B;IACxC,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG;YACX,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE;YACvB,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE;YACf,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE;YACrB,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE;YACvB,IAAI,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ;YAC5B,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE;YACrB,gBAAgB,EAAE,OAAO,CAAC,GAAG;YAC7B,WAAW,EAAE,OAAO,CAAC,OAAO;YAC5B,QAAQ,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM;YAC1B,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;YACtD,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;YACpD,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;SACvC,CAAA;QAED,OAAO;YACL,EAAE,EAAE,IAAI;YACR,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;SACtC,CAAA;IACH,CAAC;CACF,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type ToolRisk = "low" | "high";
|
|
2
|
+
export type ToolMode = "auto" | "readonly";
|
|
3
|
+
export type ToolContext = {
|
|
4
|
+
/** Default working directory used to resolve relative paths. */
|
|
5
|
+
cwd: string;
|
|
6
|
+
};
|
|
7
|
+
export type ToolResult = {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
output: string;
|
|
10
|
+
};
|
|
11
|
+
export type AgentTool = {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
/** Human-readable argument shape, shown to the model in the system prompt. */
|
|
15
|
+
parameters: string;
|
|
16
|
+
risk: ToolRisk;
|
|
17
|
+
execute: (args: Record<string, unknown>, context: ToolContext) => Promise<ToolResult>;
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/agent/tools/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a user-supplied path. Supports `~` expansion and resolves relative
|
|
3
|
+
* paths against the agent working directory.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveUserPath(path: string | undefined, cwd: string): string;
|
|
6
|
+
/** Truncates long tool output so it stays within a reasonable prompt budget. */
|
|
7
|
+
export declare function truncateOutput(value: string, max?: number): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { isAbsolute, resolve } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Resolves a user-supplied path. Supports `~` expansion and resolves relative
|
|
5
|
+
* paths against the agent working directory.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveUserPath(path, cwd) {
|
|
8
|
+
if (!path || !path.trim()) {
|
|
9
|
+
return cwd;
|
|
10
|
+
}
|
|
11
|
+
let trimmed = path.trim();
|
|
12
|
+
if (trimmed === "~" || trimmed.startsWith("~/")) {
|
|
13
|
+
trimmed = resolve(homedir(), trimmed.slice(1).replace(/^\/+/, ""));
|
|
14
|
+
}
|
|
15
|
+
return isAbsolute(trimmed) ? trimmed : resolve(cwd, trimmed);
|
|
16
|
+
}
|
|
17
|
+
/** Truncates long tool output so it stays within a reasonable prompt budget. */
|
|
18
|
+
export function truncateOutput(value, max = 12000) {
|
|
19
|
+
if (value.length <= max) {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
return `${value.slice(0, max)}\n…[output truncated, ${value.length - max} more characters]`;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../../src/agent/tools/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAE/C;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAwB,EACxB,GAAW;IAEX,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,IAAI,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAEzB,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAA;IACpE,CAAC;IAED,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;AAC9D,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,GAAG,GAAG,KAAK;IACvD,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QACxB,OAAO,KAAK,CAAA;IACd,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,yBAC3B,KAAK,CAAC,MAAM,GAAG,GACjB,mBAAmB,CAAA;AACrB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { resolveUserPath } from "./util.js";
|
|
5
|
+
const schema = z.object({
|
|
6
|
+
path: z.string().min(1),
|
|
7
|
+
content: z.string(),
|
|
8
|
+
});
|
|
9
|
+
export const writeFileTool = {
|
|
10
|
+
name: "write_file",
|
|
11
|
+
description: "Create or overwrite a file on the computer with the given text content. Missing parent folders are created automatically.",
|
|
12
|
+
parameters: '{ "path": string, "content": string }',
|
|
13
|
+
risk: "high",
|
|
14
|
+
execute: async (args, context) => {
|
|
15
|
+
const { path, content } = schema.parse(args);
|
|
16
|
+
const target = resolveUserPath(path, context.cwd);
|
|
17
|
+
await mkdir(dirname(target), { recursive: true });
|
|
18
|
+
await writeFile(target, content, "utf8");
|
|
19
|
+
return {
|
|
20
|
+
ok: true,
|
|
21
|
+
output: `Wrote ${content.length} characters to ${target}.`,
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=write-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-file.js","sourceRoot":"","sources":["../../../src/agent/tools/write-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAE3C,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,aAAa,GAAc;IACtC,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,2HAA2H;IAC7H,UAAU,EAAE,uCAAuC;IACnD,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;QAC/B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC5C,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;QAEjD,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACjD,MAAM,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;QAExC,OAAO;YACL,EAAE,EAAE,IAAI;YACR,MAAM,EAAE,SAAS,OAAO,CAAC,MAAM,kBAAkB,MAAM,GAAG;SAC3D,CAAA;IACH,CAAC;CACF,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Fastify from "fastify";
|
|
2
|
+
import type { RuntimeState } from "./runtime.js";
|
|
3
|
+
export type BuildAppOptions = {
|
|
4
|
+
runtime?: RuntimeState;
|
|
5
|
+
};
|
|
6
|
+
export declare function buildApp(options?: BuildAppOptions): Fastify.FastifyInstance<import("node:http").Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>, import("node:http").IncomingMessage, import("node:http").ServerResponse<import("node:http").IncomingMessage>, Fastify.FastifyBaseLogger, Fastify.FastifyTypeProviderDefault> & PromiseLike<Fastify.FastifyInstance<import("node:http").Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>, import("node:http").IncomingMessage, import("node:http").ServerResponse<import("node:http").IncomingMessage>, Fastify.FastifyBaseLogger, Fastify.FastifyTypeProviderDefault>> & {
|
|
7
|
+
__linterBrands: "SafePromiseLike";
|
|
8
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import cors from "@fastify/cors";
|
|
2
|
+
import Fastify from "fastify";
|
|
3
|
+
import { ZodError } from "zod";
|
|
4
|
+
import { createGateway, registerGatewayWebSocket } from "./gateway/index.js";
|
|
5
|
+
export function buildApp(options = {}) {
|
|
6
|
+
const app = Fastify({
|
|
7
|
+
logger: process.env.NODE_ENV !== "test",
|
|
8
|
+
});
|
|
9
|
+
app.setErrorHandler((error, request, reply) => {
|
|
10
|
+
request.log.error(error);
|
|
11
|
+
if (error instanceof ZodError) {
|
|
12
|
+
return reply.code(400).send({
|
|
13
|
+
error: "Invalid request body.",
|
|
14
|
+
issues: error.issues,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return reply.code(500).send({
|
|
18
|
+
error: error instanceof Error ? error.message : "Internal server error.",
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
app.register(cors, {
|
|
22
|
+
origin: true,
|
|
23
|
+
});
|
|
24
|
+
if (options.runtime) {
|
|
25
|
+
registerGatewayWebSocket(app, createGateway(options.runtime));
|
|
26
|
+
}
|
|
27
|
+
app.get("/__natroc__/canvas/", async () => ({
|
|
28
|
+
type: "canvas",
|
|
29
|
+
status: "not_implemented",
|
|
30
|
+
message: "Canvas surface is not yet implemented.",
|
|
31
|
+
}));
|
|
32
|
+
app.get("/__natroc__/a2ui/", async () => ({
|
|
33
|
+
type: "a2ui",
|
|
34
|
+
status: "not_implemented",
|
|
35
|
+
message: "A2UI surface is not yet implemented.",
|
|
36
|
+
}));
|
|
37
|
+
return app;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,eAAe,CAAC;AACjC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAG/B,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAM7E,MAAM,UAAU,QAAQ,CAAC,UAA2B,EAAE;IACpD,MAAM,GAAG,GAAG,OAAO,CAAC;QAClB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM;KACxC,CAAC,CAAC;IAEH,GAAG,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC5C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAEzB,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC1B,KAAK,EAAE,uBAAuB;gBAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;aACrB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB;SACzE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE;QACjB,MAAM,EAAE,IAAI;KACb,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,wBAAwB,CAAC,GAAG,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC1C,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,iBAAiB;QACzB,OAAO,EAAE,wCAAwC;KAClD,CAAC,CAAC,CAAC;IACJ,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QACxC,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,iBAAiB;QACzB,OAAO,EAAE,sCAAsC;KAChD,CAAC,CAAC,CAAC;IAEJ,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Hashes a password with scrypt and a fresh random salt. */
|
|
2
|
+
export declare function hashPassword(password: string): Promise<{
|
|
3
|
+
hash: string;
|
|
4
|
+
salt: string;
|
|
5
|
+
}>;
|
|
6
|
+
/** Verifies a password against a stored scrypt hash using a constant-time compare. */
|
|
7
|
+
export declare function verifyPassword(password: string, hash: string, salt: string): Promise<boolean>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { randomBytes, scrypt, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const scryptAsync = promisify(scrypt);
|
|
4
|
+
const KEY_LENGTH = 64;
|
|
5
|
+
/** Hashes a password with scrypt and a fresh random salt. */
|
|
6
|
+
export async function hashPassword(password) {
|
|
7
|
+
const salt = randomBytes(16).toString("hex");
|
|
8
|
+
const derived = (await scryptAsync(password, salt, KEY_LENGTH));
|
|
9
|
+
return { hash: derived.toString("hex"), salt };
|
|
10
|
+
}
|
|
11
|
+
/** Verifies a password against a stored scrypt hash using a constant-time compare. */
|
|
12
|
+
export async function verifyPassword(password, hash, salt) {
|
|
13
|
+
const derived = (await scryptAsync(password, salt, KEY_LENGTH));
|
|
14
|
+
const expected = Buffer.from(hash, "hex");
|
|
15
|
+
if (expected.length !== derived.length) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return timingSafeEqual(expected, derived);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=password.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password.js","sourceRoot":"","sources":["../../src/auth/password.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;AACrC,MAAM,UAAU,GAAG,EAAE,CAAA;AAErB,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAgB;IAEhB,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC5C,MAAM,OAAO,GAAG,CAAC,MAAM,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,CAAW,CAAA;IAEzE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;AAChD,CAAC;AAED,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAgB,EAChB,IAAY,EACZ,IAAY;IAEZ,MAAM,OAAO,GAAG,CAAC,MAAM,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,CAAW,CAAA;IACzE,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAEzC,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QACvC,OAAO,KAAK,CAAA;IACd,CAAC;IAED,OAAO,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;AAC3C,CAAC"}
|