@dawn-ai/core 0.6.0 → 0.8.0
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 +1 -1
- package/dist/capabilities/built-in/workspace.d.ts.map +1 -1
- package/dist/capabilities/built-in/workspace.js +18 -105
- package/dist/capabilities/permission-gate.d.ts +13 -0
- package/dist/capabilities/permission-gate.d.ts.map +1 -0
- package/dist/capabilities/permission-gate.js +94 -0
- package/dist/capabilities/types.d.ts +2 -1
- package/dist/capabilities/types.d.ts.map +1 -1
- package/dist/capabilities/workspace-fs.d.ts +22 -0
- package/dist/capabilities/workspace-fs.d.ts.map +1 -0
- package/dist/capabilities/workspace-fs.js +41 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Filesystem-based route discovery, app config loading, state-field resolution, and typegen primitives that the Dawn CLI builds on.
|
|
8
8
|
|
|
9
|
-
This is an internal Dawn workspace package
|
|
9
|
+
This is an internal Dawn workspace package, part of [Dawn — the TypeScript meta-framework for LangGraph](https://github.com/cacheplane/dawnai). For documentation, see [dawnai.org/docs](https://dawnai.org/docs/getting-started).
|
|
10
10
|
|
|
11
11
|
## License
|
|
12
12
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../src/capabilities/built-in/workspace.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../src/capabilities/built-in/workspace.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,aAAa,CAAA;AA6GvE,wBAAgB,qBAAqB,IAAI,gBAAgB,CAoBxD"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join, relative, resolve, sep } from "node:path";
|
|
3
|
-
import { suggestedCommandPattern, suggestedPathPattern } from "@dawn-ai/permissions";
|
|
4
3
|
import { localExec, localFilesystem } from "@dawn-ai/workspace";
|
|
5
|
-
import { interrupt } from "@langchain/langgraph";
|
|
6
4
|
import { z } from "zod";
|
|
5
|
+
import { gateBashOp } from "../permission-gate.js";
|
|
6
|
+
import { createWorkspaceFs } from "../workspace-fs.js";
|
|
7
7
|
const WORKSPACE_DIRNAME = "workspace";
|
|
8
8
|
/**
|
|
9
9
|
* Resolve the workspace root relative to the given app root. In production
|
|
@@ -21,90 +21,18 @@ const RUN_BASH_INPUT = z.object({ command: z.string().min(1) });
|
|
|
21
21
|
function backendContext(workspaceRoot, signal) {
|
|
22
22
|
return { signal, workspaceRoot };
|
|
23
23
|
}
|
|
24
|
-
async function gatePathOp(permissions, operation, absPath, workspaceRoot) {
|
|
25
|
-
// If permissions store is absent, allow (legacy behavior — capability used without permissions context).
|
|
26
|
-
if (!permissions)
|
|
27
|
-
return { allowed: true };
|
|
28
|
-
if (permissions.mode === "bypass")
|
|
29
|
-
return { allowed: true };
|
|
30
|
-
const insideWorkspace = absPath === workspaceRoot || absPath.startsWith(workspaceRoot + sep);
|
|
31
|
-
// Inside workspace: always allow silently.
|
|
32
|
-
if (insideWorkspace)
|
|
33
|
-
return { allowed: true };
|
|
34
|
-
// Outside workspace: consult the store.
|
|
35
|
-
const decision = permissions.match(operation, absPath);
|
|
36
|
-
if (decision === "allow")
|
|
37
|
-
return { allowed: true };
|
|
38
|
-
if (decision === "deny") {
|
|
39
|
-
return { allowed: false, reason: `Permission denied by user: ${absPath}` };
|
|
40
|
-
}
|
|
41
|
-
// decision === "unknown"
|
|
42
|
-
if (permissions.mode === "non-interactive") {
|
|
43
|
-
return { allowed: false, reason: `Permission denied (fail-closed): ${absPath}` };
|
|
44
|
-
}
|
|
45
|
-
// Interactive: emit LangGraph interrupt and await user decision.
|
|
46
|
-
const result = await emitPermissionInterrupt({
|
|
47
|
-
kind: "path",
|
|
48
|
-
operation,
|
|
49
|
-
path: absPath,
|
|
50
|
-
permissions,
|
|
51
|
-
});
|
|
52
|
-
if (result === "deny") {
|
|
53
|
-
return { allowed: false, reason: `Permission denied by user: ${absPath}` };
|
|
54
|
-
}
|
|
55
|
-
return { allowed: true };
|
|
56
|
-
}
|
|
57
|
-
async function gateBashOp(permissions, command) {
|
|
58
|
-
if (!permissions)
|
|
59
|
-
return { allowed: true };
|
|
60
|
-
if (permissions.mode === "bypass")
|
|
61
|
-
return { allowed: true };
|
|
62
|
-
const decision = permissions.match("bash", command);
|
|
63
|
-
if (decision === "allow")
|
|
64
|
-
return { allowed: true };
|
|
65
|
-
if (decision === "deny") {
|
|
66
|
-
return { allowed: false, reason: `Permission denied by user: ${command}` };
|
|
67
|
-
}
|
|
68
|
-
if (permissions.mode === "non-interactive") {
|
|
69
|
-
return { allowed: false, reason: `Permission denied (fail-closed): ${command}` };
|
|
70
|
-
}
|
|
71
|
-
const result = await emitPermissionInterrupt({
|
|
72
|
-
kind: "command",
|
|
73
|
-
command,
|
|
74
|
-
permissions,
|
|
75
|
-
});
|
|
76
|
-
if (result === "deny") {
|
|
77
|
-
return { allowed: false, reason: `Permission denied by user: ${command}` };
|
|
78
|
-
}
|
|
79
|
-
return { allowed: true };
|
|
80
|
-
}
|
|
81
|
-
async function emitPermissionInterrupt(args) {
|
|
82
|
-
const interruptId = `perm-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
83
|
-
const suggestedPattern = args.kind === "command"
|
|
84
|
-
? suggestedCommandPattern(args.command ?? "")
|
|
85
|
-
: suggestedPathPattern(args.path ?? "");
|
|
86
|
-
const payload = {
|
|
87
|
-
interruptId,
|
|
88
|
-
type: "permission-request",
|
|
89
|
-
kind: args.kind,
|
|
90
|
-
detail: args.kind === "command"
|
|
91
|
-
? { command: args.command ?? "", suggestedPattern }
|
|
92
|
-
: {
|
|
93
|
-
operation: args.operation ?? "readFile",
|
|
94
|
-
path: args.path ?? "",
|
|
95
|
-
suggestedPattern,
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
const decision = interrupt(payload);
|
|
99
|
-
if (decision === "deny")
|
|
100
|
-
return "deny";
|
|
101
|
-
if (decision === "always") {
|
|
102
|
-
const tool = args.kind === "command" ? "bash" : (args.operation ?? "readFile");
|
|
103
|
-
await args.permissions.addAllow(tool, suggestedPattern);
|
|
104
|
-
}
|
|
105
|
-
return "allow";
|
|
106
|
-
}
|
|
107
24
|
function buildWorkspaceTools(workspaceRoot, fs, exec, permissions) {
|
|
25
|
+
// Agent tools run inside the graph, so the handle may surface the
|
|
26
|
+
// interactive LangGraph permission interrupt.
|
|
27
|
+
function handleFor(signal) {
|
|
28
|
+
return createWorkspaceFs({
|
|
29
|
+
workspaceRoot,
|
|
30
|
+
backend: fs,
|
|
31
|
+
permissions,
|
|
32
|
+
signal,
|
|
33
|
+
interruptCapable: true,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
108
36
|
const readFile = {
|
|
109
37
|
name: "readFile",
|
|
110
38
|
description: "Read a UTF-8 file from the workspace.",
|
|
@@ -112,19 +40,15 @@ function buildWorkspaceTools(workspaceRoot, fs, exec, permissions) {
|
|
|
112
40
|
overridable: true,
|
|
113
41
|
run: async (input, ctx) => {
|
|
114
42
|
const { path } = READ_FILE_INPUT.parse(input);
|
|
43
|
+
const handle = handleFor(ctx.signal);
|
|
115
44
|
const absPath = resolve(workspaceRoot, path);
|
|
116
|
-
const gate = await gatePathOp(permissions, "readFile", absPath, workspaceRoot);
|
|
117
|
-
if (!gate.allowed) {
|
|
118
|
-
throw new Error(gate.reason);
|
|
119
|
-
}
|
|
120
|
-
const bctx = backendContext(workspaceRoot, ctx.signal);
|
|
121
45
|
const rel = relative(workspaceRoot, absPath);
|
|
122
46
|
// NOTE: must match SUBDIR ("tool-outputs") in @dawn-ai/langchain offload-store.ts
|
|
123
47
|
const isToolOutput = rel === "tool-outputs" || rel.startsWith(`tool-outputs${sep}`);
|
|
124
|
-
const data = await
|
|
48
|
+
const data = await handle.readFile(path, isToolOutput ? { maxBytes: Number.POSITIVE_INFINITY } : undefined);
|
|
125
49
|
if (isToolOutput && fs.touchFile) {
|
|
126
50
|
try {
|
|
127
|
-
await fs.touchFile(absPath,
|
|
51
|
+
await fs.touchFile(absPath, backendContext(workspaceRoot, ctx.signal));
|
|
128
52
|
}
|
|
129
53
|
catch {
|
|
130
54
|
/* touch is best-effort; never fail a read because of it */
|
|
@@ -140,12 +64,7 @@ function buildWorkspaceTools(workspaceRoot, fs, exec, permissions) {
|
|
|
140
64
|
overridable: true,
|
|
141
65
|
run: async (input, ctx) => {
|
|
142
66
|
const { path, content } = WRITE_FILE_INPUT.parse(input);
|
|
143
|
-
const
|
|
144
|
-
const gate = await gatePathOp(permissions, "writeFile", absPath, workspaceRoot);
|
|
145
|
-
if (!gate.allowed) {
|
|
146
|
-
throw new Error(gate.reason);
|
|
147
|
-
}
|
|
148
|
-
const result = await fs.writeFile(absPath, content, backendContext(workspaceRoot, ctx.signal));
|
|
67
|
+
const result = await handleFor(ctx.signal).writeFile(path, content);
|
|
149
68
|
return `wrote ${result.bytesWritten} bytes to ${path}`;
|
|
150
69
|
},
|
|
151
70
|
};
|
|
@@ -156,13 +75,7 @@ function buildWorkspaceTools(workspaceRoot, fs, exec, permissions) {
|
|
|
156
75
|
overridable: true,
|
|
157
76
|
run: async (input, ctx) => {
|
|
158
77
|
const { path } = LIST_DIR_INPUT.parse(input);
|
|
159
|
-
|
|
160
|
-
const gate = await gatePathOp(permissions, "listDir", absPath, workspaceRoot);
|
|
161
|
-
if (!gate.allowed) {
|
|
162
|
-
throw new Error(gate.reason);
|
|
163
|
-
}
|
|
164
|
-
const entries = await fs.listDir(absPath, backendContext(workspaceRoot, ctx.signal));
|
|
165
|
-
return [...entries];
|
|
78
|
+
return [...(await handleFor(ctx.signal).listDir(path))];
|
|
166
79
|
},
|
|
167
80
|
};
|
|
168
81
|
const runBash = {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PermissionsStore } from "@dawn-ai/permissions";
|
|
2
|
+
export type PathOperation = "readFile" | "writeFile" | "listDir";
|
|
3
|
+
export type GateResult = {
|
|
4
|
+
allowed: true;
|
|
5
|
+
} | {
|
|
6
|
+
allowed: false;
|
|
7
|
+
reason: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function gatePathOp(permissions: PermissionsStore | undefined, operation: PathOperation, absPath: string, workspaceRoot: string, opts?: {
|
|
10
|
+
readonly interruptCapable?: boolean;
|
|
11
|
+
}): Promise<GateResult>;
|
|
12
|
+
export declare function gateBashOp(permissions: PermissionsStore | undefined, command: string): Promise<GateResult>;
|
|
13
|
+
//# sourceMappingURL=permission-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-gate.d.ts","sourceRoot":"","sources":["../../src/capabilities/permission-gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAI5D,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAA;AAEhE,MAAM,MAAM,UAAU,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/E,wBAAsB,UAAU,CAC9B,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7C,OAAO,CAAC,UAAU,CAAC,CAwCrB;AAED,wBAAsB,UAAU,CAC9B,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,CAAC,CAqBrB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { sep } from "node:path";
|
|
2
|
+
import { suggestedCommandPattern, suggestedPathPattern } from "@dawn-ai/permissions";
|
|
3
|
+
import { interrupt } from "@langchain/langgraph";
|
|
4
|
+
export async function gatePathOp(permissions, operation, absPath, workspaceRoot, opts) {
|
|
5
|
+
// If permissions store is absent, allow (legacy behavior — capability used without permissions context).
|
|
6
|
+
if (!permissions)
|
|
7
|
+
return { allowed: true };
|
|
8
|
+
if (permissions.mode === "bypass")
|
|
9
|
+
return { allowed: true };
|
|
10
|
+
const insideWorkspace = absPath === workspaceRoot || absPath.startsWith(workspaceRoot + sep);
|
|
11
|
+
// Inside workspace: always allow silently.
|
|
12
|
+
if (insideWorkspace)
|
|
13
|
+
return { allowed: true };
|
|
14
|
+
// Outside workspace: consult the store.
|
|
15
|
+
const decision = permissions.match(operation, absPath);
|
|
16
|
+
if (decision === "allow")
|
|
17
|
+
return { allowed: true };
|
|
18
|
+
if (decision === "deny") {
|
|
19
|
+
return { allowed: false, reason: `Permission denied by user: ${absPath}` };
|
|
20
|
+
}
|
|
21
|
+
// decision === "unknown"
|
|
22
|
+
if (permissions.mode === "non-interactive") {
|
|
23
|
+
return { allowed: false, reason: `Permission denied (fail-closed): ${absPath}` };
|
|
24
|
+
}
|
|
25
|
+
if (opts?.interruptCapable === false) {
|
|
26
|
+
return {
|
|
27
|
+
allowed: false,
|
|
28
|
+
reason: `Permission denied: ${absPath} is outside the workspace and interactive ` +
|
|
29
|
+
`permission prompts are not available in this execution context. ` +
|
|
30
|
+
`Add an allow rule for "${operation}" to the permissions config in dawn.config.ts.`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Interactive: emit LangGraph interrupt and await user decision.
|
|
34
|
+
const result = await emitPermissionInterrupt({
|
|
35
|
+
kind: "path",
|
|
36
|
+
operation,
|
|
37
|
+
path: absPath,
|
|
38
|
+
permissions,
|
|
39
|
+
});
|
|
40
|
+
if (result === "deny") {
|
|
41
|
+
return { allowed: false, reason: `Permission denied by user: ${absPath}` };
|
|
42
|
+
}
|
|
43
|
+
return { allowed: true };
|
|
44
|
+
}
|
|
45
|
+
export async function gateBashOp(permissions, command) {
|
|
46
|
+
if (!permissions)
|
|
47
|
+
return { allowed: true };
|
|
48
|
+
if (permissions.mode === "bypass")
|
|
49
|
+
return { allowed: true };
|
|
50
|
+
const decision = permissions.match("bash", command);
|
|
51
|
+
if (decision === "allow")
|
|
52
|
+
return { allowed: true };
|
|
53
|
+
if (decision === "deny") {
|
|
54
|
+
return { allowed: false, reason: `Permission denied by user: ${command}` };
|
|
55
|
+
}
|
|
56
|
+
if (permissions.mode === "non-interactive") {
|
|
57
|
+
return { allowed: false, reason: `Permission denied (fail-closed): ${command}` };
|
|
58
|
+
}
|
|
59
|
+
const result = await emitPermissionInterrupt({
|
|
60
|
+
kind: "command",
|
|
61
|
+
command,
|
|
62
|
+
permissions,
|
|
63
|
+
});
|
|
64
|
+
if (result === "deny") {
|
|
65
|
+
return { allowed: false, reason: `Permission denied by user: ${command}` };
|
|
66
|
+
}
|
|
67
|
+
return { allowed: true };
|
|
68
|
+
}
|
|
69
|
+
async function emitPermissionInterrupt(args) {
|
|
70
|
+
const interruptId = `perm-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
71
|
+
const suggestedPattern = args.kind === "command"
|
|
72
|
+
? suggestedCommandPattern(args.command ?? "")
|
|
73
|
+
: suggestedPathPattern(args.path ?? "");
|
|
74
|
+
const payload = {
|
|
75
|
+
interruptId,
|
|
76
|
+
type: "permission-request",
|
|
77
|
+
kind: args.kind,
|
|
78
|
+
detail: args.kind === "command"
|
|
79
|
+
? { command: args.command ?? "", suggestedPattern }
|
|
80
|
+
: {
|
|
81
|
+
operation: args.operation ?? "readFile",
|
|
82
|
+
path: args.path ?? "",
|
|
83
|
+
suggestedPattern,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
const decision = interrupt(payload);
|
|
87
|
+
if (decision === "deny")
|
|
88
|
+
return "deny";
|
|
89
|
+
if (decision === "always") {
|
|
90
|
+
const tool = args.kind === "command" ? "bash" : (args.operation ?? "readFile");
|
|
91
|
+
await args.permissions.addAllow(tool, suggestedPattern);
|
|
92
|
+
}
|
|
93
|
+
return "allow";
|
|
94
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PermissionsStore } from "@dawn-ai/permissions";
|
|
2
|
-
import type { DawnAgent } from "@dawn-ai/sdk";
|
|
2
|
+
import type { DawnAgent, WorkspaceFs } from "@dawn-ai/sdk";
|
|
3
3
|
import type { ExecBackend, FilesystemBackend } from "@dawn-ai/workspace";
|
|
4
4
|
import type { ResolvedStateField, RouteManifest } from "../types.js";
|
|
5
5
|
export interface CapabilityMarkerContext {
|
|
@@ -20,6 +20,7 @@ export interface DawnToolDefinition {
|
|
|
20
20
|
readonly run: (input: unknown, context: {
|
|
21
21
|
readonly middleware?: Readonly<Record<string, unknown>>;
|
|
22
22
|
readonly signal: AbortSignal;
|
|
23
|
+
readonly fs?: WorkspaceFs;
|
|
23
24
|
}) => Promise<unknown> | unknown;
|
|
24
25
|
readonly schema?: unknown;
|
|
25
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/capabilities/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/capabilities/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEpE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAA;IACrC,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,SAAS,CAAA;IAC1C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAA;QACvC,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAA;KAC5B,CAAA;IACD,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAA;IACvC,4IAA4I;IAC5I,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,CACZ,KAAK,EAAE,OAAO,EACd,OAAO,EAAE;QACP,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;QACvD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;QAI5B,QAAQ,CAAC,EAAE,CAAC,EAAE,WAAW,CAAA;KAC1B,KACE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,mBAAmB,CAAA;IACvC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,MAAM,CAAA;CACtE;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;IAChC,QAAQ,CAAC,SAAS,EAAE,CAClB,KAAK,EAAE,sBAAsB,KAC1B,QAAQ,CAAC,uBAAuB,CAAC,GAAG,aAAa,CAAC,uBAAuB,CAAC,CAAA;CAChF;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;IAClD,QAAQ,CAAC,WAAW,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;IACxD,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;IACxC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAA;CAC/D;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACzF,QAAQ,CAAC,IAAI,EAAE,CACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,uBAAuB,KAC7B,OAAO,CAAC,sBAAsB,CAAC,CAAA;CACrC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PermissionsStore } from "@dawn-ai/permissions";
|
|
2
|
+
import type { WorkspaceFs } from "@dawn-ai/sdk";
|
|
3
|
+
import type { FilesystemBackend } from "@dawn-ai/workspace";
|
|
4
|
+
export interface CreateWorkspaceFsOptions {
|
|
5
|
+
readonly workspaceRoot: string;
|
|
6
|
+
readonly backend: FilesystemBackend;
|
|
7
|
+
readonly permissions: PermissionsStore | undefined;
|
|
8
|
+
readonly signal: AbortSignal;
|
|
9
|
+
/**
|
|
10
|
+
* Whether this execution context can surface the interactive LangGraph
|
|
11
|
+
* permission interrupt (true inside agent-route tool execution; false for
|
|
12
|
+
* workflow/graph entries, which run outside the graph).
|
|
13
|
+
*/
|
|
14
|
+
readonly interruptCapable: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build the author-facing sandboxed filesystem handle (`ctx.fs`). Paths are
|
|
18
|
+
* workspace-relative; every call runs the same permission gate as the
|
|
19
|
+
* agent-facing workspace tools.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createWorkspaceFs(opts: CreateWorkspaceFsOptions): WorkspaceFs;
|
|
22
|
+
//# sourceMappingURL=workspace-fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-fs.d.ts","sourceRoot":"","sources":["../../src/capabilities/workspace-fs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAG3D,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;IACnC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,GAAG,SAAS,CAAA;IAClD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAA;CACnC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,WAAW,CAoC7E"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { gatePathOp } from "./permission-gate.js";
|
|
3
|
+
/**
|
|
4
|
+
* Build the author-facing sandboxed filesystem handle (`ctx.fs`). Paths are
|
|
5
|
+
* workspace-relative; every call runs the same permission gate as the
|
|
6
|
+
* agent-facing workspace tools.
|
|
7
|
+
*/
|
|
8
|
+
export function createWorkspaceFs(opts) {
|
|
9
|
+
const bctx = { signal: opts.signal, workspaceRoot: opts.workspaceRoot };
|
|
10
|
+
async function gate(operation, path) {
|
|
11
|
+
const absPath = resolve(opts.workspaceRoot, path);
|
|
12
|
+
const result = await gatePathOp(opts.permissions, operation, absPath, opts.workspaceRoot, {
|
|
13
|
+
interruptCapable: opts.interruptCapable,
|
|
14
|
+
});
|
|
15
|
+
if (!result.allowed)
|
|
16
|
+
throw new Error(result.reason);
|
|
17
|
+
return absPath;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
async readFile(path, readOpts) {
|
|
21
|
+
return opts.backend.readFile(await gate("readFile", path), bctx, readOpts);
|
|
22
|
+
},
|
|
23
|
+
async readBinaryFile(path, readOpts) {
|
|
24
|
+
// Check backend capability before gating so users are never prompted to
|
|
25
|
+
// approve a read that will immediately fail.
|
|
26
|
+
const { readBinaryFile } = opts.backend;
|
|
27
|
+
if (!readBinaryFile) {
|
|
28
|
+
throw new Error("The configured filesystem backend does not support binary reads (readBinaryFile). " +
|
|
29
|
+
"localFilesystem supports it; custom backends must implement it.");
|
|
30
|
+
}
|
|
31
|
+
const absPath = await gate("readFile", path);
|
|
32
|
+
return readBinaryFile.call(opts.backend, absPath, bctx, readOpts);
|
|
33
|
+
},
|
|
34
|
+
async writeFile(path, content) {
|
|
35
|
+
return opts.backend.writeFile(await gate("writeFile", path), content, bctx);
|
|
36
|
+
},
|
|
37
|
+
async listDir(path = ".") {
|
|
38
|
+
return [...(await opts.backend.listDir(await gate("listDir", path), bctx))];
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export { createWorkspaceMarker } from "./capabilities/built-in/workspace.js";
|
|
|
8
8
|
export type { AppliedContribution, ApplyResult, CapabilityError, CapabilityRegistry, } from "./capabilities/registry.js";
|
|
9
9
|
export { applyCapabilities, createCapabilityRegistry } from "./capabilities/registry.js";
|
|
10
10
|
export type { CapabilityContribution, CapabilityMarker, CapabilityMarkerContext, DawnToolDefinition, PromptFragment, StreamTransformer, StreamTransformerInput, StreamTransformerOutput, } from "./capabilities/types.js";
|
|
11
|
+
export type { CreateWorkspaceFsOptions } from "./capabilities/workspace-fs.js";
|
|
12
|
+
export { createWorkspaceFs } from "./capabilities/workspace-fs.js";
|
|
11
13
|
export { loadDawnConfig } from "./config.js";
|
|
12
14
|
export { discoverRoutes } from "./discovery/discover-routes.js";
|
|
13
15
|
export { assertDawnRoutesDir, findDawnApp } from "./discovery/find-dawn-app.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAA;AAC3E,YAAY,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAA;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAC5E,YAAY,EACV,mBAAmB,EACnB,WAAW,EACX,eAAe,EACf,kBAAkB,GACnB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACxF,YAAY,EACV,sBAAsB,EACtB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC/E,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,GAChB,MAAM,+BAA+B,CAAA;AACtC,YAAY,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAA;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAA;AACpE,YAAY,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAA;AACjF,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAA;AAC7E,YAAY,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAA;AAC9E,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AACnF,YAAY,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAA;AAC3E,YAAY,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAA;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAC5E,YAAY,EACV,mBAAmB,EACnB,WAAW,EACX,eAAe,EACf,kBAAkB,GACnB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACxF,YAAY,EACV,sBAAsB,EACtB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,yBAAyB,CAAA;AAChC,YAAY,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAA;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC/E,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,GAChB,MAAM,+BAA+B,CAAA;AACtC,YAAY,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAA;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAA;AACpE,YAAY,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAA;AACjF,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAA;AAC7E,YAAY,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAA;AAC9E,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AACnF,YAAY,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { createSkillsMarker } from "./capabilities/built-in/skills.js";
|
|
|
4
4
|
export { createSubagentsMarker } from "./capabilities/built-in/subagents.js";
|
|
5
5
|
export { createWorkspaceMarker } from "./capabilities/built-in/workspace.js";
|
|
6
6
|
export { applyCapabilities, createCapabilityRegistry } from "./capabilities/registry.js";
|
|
7
|
+
export { createWorkspaceFs } from "./capabilities/workspace-fs.js";
|
|
7
8
|
export { loadDawnConfig } from "./config.js";
|
|
8
9
|
export { discoverRoutes } from "./discovery/discover-routes.js";
|
|
9
10
|
export { assertDawnRoutesDir, findDawnApp } from "./discovery/find-dawn-app.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dawn-ai/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"tsx": "^4.8.1",
|
|
35
35
|
"typescript": "5.8.3",
|
|
36
36
|
"zod": "^4.4.3",
|
|
37
|
-
"@dawn-ai/
|
|
38
|
-
"@dawn-ai/
|
|
39
|
-
"@dawn-ai/
|
|
40
|
-
"@dawn-ai/sqlite-storage": "0.
|
|
37
|
+
"@dawn-ai/permissions": "0.8.0",
|
|
38
|
+
"@dawn-ai/sdk": "0.8.0",
|
|
39
|
+
"@dawn-ai/workspace": "0.8.0",
|
|
40
|
+
"@dawn-ai/sqlite-storage": "0.8.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@langchain/langgraph-checkpoint": "^1.0.2"
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@langchain/langgraph-checkpoint": "^1.0.2",
|
|
47
47
|
"@types/node": "25.6.0",
|
|
48
|
-
"@dawn-ai/config-typescript": "0.
|
|
49
|
-
"@dawn-ai/sqlite-storage": "0.
|
|
48
|
+
"@dawn-ai/config-typescript": "0.8.0",
|
|
49
|
+
"@dawn-ai/sqlite-storage": "0.8.0"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
52
|
"build": "tsc -b tsconfig.json",
|