@sigil0/looking-glass 0.1.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/LICENSE +21 -0
- package/README.md +572 -0
- package/dist/agents/coordinator.d.ts +21 -0
- package/dist/agents/coordinator.d.ts.map +1 -0
- package/dist/agents/coordinator.js +171 -0
- package/dist/agents/coordinator.js.map +1 -0
- package/dist/app.d.ts +44 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +215 -0
- package/dist/app.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +419 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +132 -0
- package/dist/config.js.map +1 -0
- package/dist/engine/context.d.ts +6 -0
- package/dist/engine/context.d.ts.map +1 -0
- package/dist/engine/context.js +65 -0
- package/dist/engine/context.js.map +1 -0
- package/dist/engine/engine.d.ts +74 -0
- package/dist/engine/engine.d.ts.map +1 -0
- package/dist/engine/engine.js +573 -0
- package/dist/engine/engine.js.map +1 -0
- package/dist/engine/types.d.ts +35 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +2 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/instructions.d.ts +8 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +60 -0
- package/dist/instructions.js.map +1 -0
- package/dist/model/codex-lb.d.ts +141 -0
- package/dist/model/codex-lb.d.ts.map +1 -0
- package/dist/model/codex-lb.js +471 -0
- package/dist/model/codex-lb.js.map +1 -0
- package/dist/paths.d.ts +7 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +41 -0
- package/dist/paths.js.map +1 -0
- package/dist/scheduler/daemon.d.ts +40 -0
- package/dist/scheduler/daemon.d.ts.map +1 -0
- package/dist/scheduler/daemon.js +189 -0
- package/dist/scheduler/daemon.js.map +1 -0
- package/dist/scheduler/index.d.ts +8 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +6 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/runner.d.ts +18 -0
- package/dist/scheduler/runner.d.ts.map +1 -0
- package/dist/scheduler/runner.js +173 -0
- package/dist/scheduler/runner.js.map +1 -0
- package/dist/scheduler/schedule.d.ts +7 -0
- package/dist/scheduler/schedule.d.ts.map +1 -0
- package/dist/scheduler/schedule.js +94 -0
- package/dist/scheduler/schedule.js.map +1 -0
- package/dist/scheduler/store.d.ts +55 -0
- package/dist/scheduler/store.d.ts.map +1 -0
- package/dist/scheduler/store.js +1203 -0
- package/dist/scheduler/store.js.map +1 -0
- package/dist/scheduler/systemd.d.ts +7 -0
- package/dist/scheduler/systemd.d.ts.map +1 -0
- package/dist/scheduler/systemd.js +97 -0
- package/dist/scheduler/systemd.js.map +1 -0
- package/dist/scheduler/types.d.ts +145 -0
- package/dist/scheduler/types.d.ts.map +1 -0
- package/dist/scheduler/types.js +7 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/security.d.ts +6 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +27 -0
- package/dist/security.js.map +1 -0
- package/dist/storage/artifact-store.d.ts +19 -0
- package/dist/storage/artifact-store.d.ts.map +1 -0
- package/dist/storage/artifact-store.js +47 -0
- package/dist/storage/artifact-store.js.map +1 -0
- package/dist/storage/database.d.ts +5 -0
- package/dist/storage/database.d.ts.map +1 -0
- package/dist/storage/database.js +395 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/storage/session-store.d.ts +114 -0
- package/dist/storage/session-store.d.ts.map +1 -0
- package/dist/storage/session-store.js +731 -0
- package/dist/storage/session-store.js.map +1 -0
- package/dist/tools/agents.d.ts +14 -0
- package/dist/tools/agents.d.ts.map +1 -0
- package/dist/tools/agents.js +35 -0
- package/dist/tools/agents.js.map +1 -0
- package/dist/tools/apply-patch.d.ts +7 -0
- package/dist/tools/apply-patch.d.ts.map +1 -0
- package/dist/tools/apply-patch.js +429 -0
- package/dist/tools/apply-patch.js.map +1 -0
- package/dist/tools/ask.d.ts +8 -0
- package/dist/tools/ask.d.ts.map +1 -0
- package/dist/tools/ask.js +27 -0
- package/dist/tools/ask.js.map +1 -0
- package/dist/tools/bash.d.ts +9 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +98 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +34 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/paths.d.ts +3 -0
- package/dist/tools/paths.d.ts.map +1 -0
- package/dist/tools/paths.js +34 -0
- package/dist/tools/paths.js.map +1 -0
- package/dist/tools/process.d.ts +21 -0
- package/dist/tools/process.d.ts.map +1 -0
- package/dist/tools/process.js +104 -0
- package/dist/tools/process.js.map +1 -0
- package/dist/tools/read.d.ts +9 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +95 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/registry.d.ts +19 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +188 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/safety.d.ts +9 -0
- package/dist/tools/safety.d.ts.map +1 -0
- package/dist/tools/safety.js +199 -0
- package/dist/tools/safety.js.map +1 -0
- package/dist/tools/schedule.d.ts +4 -0
- package/dist/tools/schedule.d.ts.map +1 -0
- package/dist/tools/schedule.js +191 -0
- package/dist/tools/schedule.js.map +1 -0
- package/dist/tools/search.d.ts +16 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +75 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/types.d.ts +54 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +10 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/stdio.d.ts +5 -0
- package/dist/ui/stdio.d.ts.map +1 -0
- package/dist/ui/stdio.js +65 -0
- package/dist/ui/stdio.js.map +1 -0
- package/dist/ui/tui.d.ts +93 -0
- package/dist/ui/tui.d.ts.map +1 -0
- package/dist/ui/tui.js +2246 -0
- package/dist/ui/tui.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Ajv } from "ajv";
|
|
2
|
+
import { toFunctionTool } from "./types.js";
|
|
3
|
+
import { configuredCredentialValues, redactSensitiveText, redactSensitiveValue } from "../security.js";
|
|
4
|
+
const RISK_RANK = {
|
|
5
|
+
read: 0,
|
|
6
|
+
write: 1,
|
|
7
|
+
shell: 2,
|
|
8
|
+
persistent: 3,
|
|
9
|
+
critical: 4,
|
|
10
|
+
};
|
|
11
|
+
function effectiveRisk(tool, args, context) {
|
|
12
|
+
const classified = tool.classifyRisk?.(args, context) ?? tool.risk;
|
|
13
|
+
if (!(classified in RISK_RANK))
|
|
14
|
+
throw new Error(`Tool ${tool.name} returned an invalid risk classification`);
|
|
15
|
+
return RISK_RANK[classified] >= RISK_RANK[tool.risk] ? classified : tool.risk;
|
|
16
|
+
}
|
|
17
|
+
function canonicalValue(value) {
|
|
18
|
+
if (Array.isArray(value))
|
|
19
|
+
return value.map(canonicalValue);
|
|
20
|
+
if (value && typeof value === "object") {
|
|
21
|
+
return Object.fromEntries(Object.entries(value)
|
|
22
|
+
.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
|
|
23
|
+
.map(([key, item]) => [key, canonicalValue(item)]));
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
export function toolApprovalSignature(tool, args) {
|
|
28
|
+
return JSON.stringify(["tool-action", 1, tool, canonicalValue(args)]);
|
|
29
|
+
}
|
|
30
|
+
function utf8Prefix(value, maxBytes) {
|
|
31
|
+
const data = Buffer.from(value);
|
|
32
|
+
let end = Math.min(data.length, Math.max(0, maxBytes));
|
|
33
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
34
|
+
while (end > 0) {
|
|
35
|
+
try {
|
|
36
|
+
return decoder.decode(data.subarray(0, end));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
end -= 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return "";
|
|
43
|
+
}
|
|
44
|
+
function boundToolResult(name, result, context) {
|
|
45
|
+
result = { ...result, output: redactSensitiveText(result.output, configuredCredentialValues(context.config)) };
|
|
46
|
+
const limit = Math.max(1, context.modelOutputBytes ?? context.config.tools.maxOutputBytes);
|
|
47
|
+
if (Buffer.byteLength(result.output) <= limit)
|
|
48
|
+
return result;
|
|
49
|
+
const artifact = (result.artifactUri ? context.artifacts.get(result.artifactUri) : null)
|
|
50
|
+
?? context.artifacts.save(context.sessionId, `${name}-output`, result.output, { tool: name });
|
|
51
|
+
const artifactUri = artifact.uri;
|
|
52
|
+
const suffix = `\n\n[Output truncated to the model budget. Full output: ${artifactUri}]`;
|
|
53
|
+
const output = Buffer.byteLength(suffix) >= limit
|
|
54
|
+
? utf8Prefix(`[Truncated: ${artifactUri}]`, limit)
|
|
55
|
+
: `${utf8Prefix(result.output, limit - Buffer.byteLength(suffix))}${suffix}`;
|
|
56
|
+
return {
|
|
57
|
+
...result,
|
|
58
|
+
output,
|
|
59
|
+
artifactUri,
|
|
60
|
+
truncated: true,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export class ToolDeniedError extends Error {
|
|
64
|
+
constructor(message = "Tool execution denied by user") {
|
|
65
|
+
super(message);
|
|
66
|
+
this.name = "ToolDeniedError";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export class ToolPreflightError extends Error {
|
|
70
|
+
constructor(message) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.name = "ToolPreflightError";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function needsApproval(mode, risk) {
|
|
76
|
+
if (mode === "unrestricted")
|
|
77
|
+
return false;
|
|
78
|
+
if (risk === "critical")
|
|
79
|
+
return true;
|
|
80
|
+
if (mode === "review")
|
|
81
|
+
return risk !== "read";
|
|
82
|
+
return risk === "shell" || risk === "persistent";
|
|
83
|
+
}
|
|
84
|
+
export class ToolRegistry {
|
|
85
|
+
tools = new Map();
|
|
86
|
+
ajv = new Ajv({ allErrors: true, strict: true, allowUnionTypes: true });
|
|
87
|
+
register(tool) {
|
|
88
|
+
if (this.tools.has(tool.name))
|
|
89
|
+
throw new Error(`Tool already registered: ${tool.name}`);
|
|
90
|
+
this.tools.set(tool.name, { tool, validate: this.ajv.compile(tool.parameters) });
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
definitions() {
|
|
94
|
+
return [...this.tools.values()].map(({ tool }) => toFunctionTool(tool));
|
|
95
|
+
}
|
|
96
|
+
get(name) {
|
|
97
|
+
return this.tools.get(name)?.tool ?? null;
|
|
98
|
+
}
|
|
99
|
+
parseArguments(name, raw) {
|
|
100
|
+
const registered = this.tools.get(name);
|
|
101
|
+
if (!registered)
|
|
102
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
103
|
+
let args;
|
|
104
|
+
try {
|
|
105
|
+
args = JSON.parse(raw);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
throw new Error(`Invalid JSON arguments for ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
109
|
+
}
|
|
110
|
+
if (!registered.validate(args)) {
|
|
111
|
+
const detail = this.ajv.errorsText(registered.validate.errors, { separator: "; " });
|
|
112
|
+
throw new Error(`Invalid arguments for ${name}: ${detail}`);
|
|
113
|
+
}
|
|
114
|
+
return args;
|
|
115
|
+
}
|
|
116
|
+
async execute(name, args, context) {
|
|
117
|
+
const registered = this.tools.get(name);
|
|
118
|
+
if (!registered)
|
|
119
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
120
|
+
context.signal.throwIfAborted();
|
|
121
|
+
let risk;
|
|
122
|
+
try {
|
|
123
|
+
risk = effectiveRisk(registered.tool, args, context);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
throw new ToolPreflightError(error instanceof Error ? error.message : String(error));
|
|
127
|
+
}
|
|
128
|
+
const approvalArgs = redactSensitiveValue(args, configuredCredentialValues(context.config));
|
|
129
|
+
let signature;
|
|
130
|
+
let legacySignatures;
|
|
131
|
+
try {
|
|
132
|
+
signature = registered.tool.approvalSignature?.(approvalArgs, context) ?? toolApprovalSignature(name, approvalArgs);
|
|
133
|
+
legacySignatures = registered.tool.legacyApprovalSignatures?.(approvalArgs, context) ?? [];
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
throw new ToolPreflightError(error instanceof Error ? error.message : String(error));
|
|
137
|
+
}
|
|
138
|
+
const mode = context.approvalMode ?? context.config.tools.approval;
|
|
139
|
+
const authorizationSessionId = context.authorizationSessionId ?? context.sessionId;
|
|
140
|
+
const remembered = [signature, ...legacySignatures]
|
|
141
|
+
.some((candidate) => context.sessions.hasCommandApproval(authorizationSessionId, candidate));
|
|
142
|
+
const automatedRemembered = Boolean(context.automated && remembered);
|
|
143
|
+
const execute = async () => {
|
|
144
|
+
return boundToolResult(name, await registered.tool.execute(args, context), context);
|
|
145
|
+
};
|
|
146
|
+
if (context.readOnly && mode !== "unrestricted" && risk !== "read" && !automatedRemembered) {
|
|
147
|
+
throw new ToolDeniedError("Tool execution denied by read-only session policy");
|
|
148
|
+
}
|
|
149
|
+
if (context.automated && (name === "ask_user" || name === "schedule_create") && !automatedRemembered) {
|
|
150
|
+
throw new ToolDeniedError("Tool execution denied by noninteractive scheduled-session policy");
|
|
151
|
+
}
|
|
152
|
+
if (remembered && (!context.automated || automatedRemembered)) {
|
|
153
|
+
context.signal.throwIfAborted();
|
|
154
|
+
return execute();
|
|
155
|
+
}
|
|
156
|
+
if (needsApproval(mode, risk)) {
|
|
157
|
+
if (context.automated) {
|
|
158
|
+
throw new ToolDeniedError("Tool execution requires an interactive or remembered approval");
|
|
159
|
+
}
|
|
160
|
+
const decision = await context.approve({
|
|
161
|
+
tool: name,
|
|
162
|
+
risk,
|
|
163
|
+
summary: registered.tool.summarize(approvalArgs),
|
|
164
|
+
details: [
|
|
165
|
+
JSON.stringify(approvalArgs, null, 2),
|
|
166
|
+
registered.tool.approvalDescription
|
|
167
|
+
? `Always approve scope:\n${registered.tool.approvalDescription(approvalArgs, context)}`
|
|
168
|
+
: "",
|
|
169
|
+
].filter(Boolean).join("\n\n"),
|
|
170
|
+
canAlwaysApprove: true,
|
|
171
|
+
});
|
|
172
|
+
context.signal.throwIfAborted();
|
|
173
|
+
if (decision === "always") {
|
|
174
|
+
const currentSignature = registered.tool.approvalSignature?.(approvalArgs, context);
|
|
175
|
+
if (currentSignature && currentSignature !== signature) {
|
|
176
|
+
throw new ToolPreflightError("Command execution context changed while approval was pending; review it again");
|
|
177
|
+
}
|
|
178
|
+
context.sessions.registerCommandApproval(authorizationSessionId, signature);
|
|
179
|
+
}
|
|
180
|
+
else if (decision !== "once") {
|
|
181
|
+
throw new ToolDeniedError();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
context.signal.throwIfAborted();
|
|
185
|
+
return execute();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolRisk } from "./types.js";
|
|
2
|
+
export declare function bashCommandRisk(command: string): ToolRisk;
|
|
3
|
+
export declare function bashExecutionRisk(command: string): ToolRisk;
|
|
4
|
+
export declare function bashApprovalExecutable(command: string): string | null;
|
|
5
|
+
export declare function isSensitiveMutationPath(path: string): boolean;
|
|
6
|
+
export declare function patchRisk(patch: string): ToolRisk;
|
|
7
|
+
export declare function workspacePatchRisk(workspace: string, patch: string): ToolRisk;
|
|
8
|
+
export declare function shellEnvironment(base?: NodeJS.ProcessEnv, excludedKeys?: Iterable<string>): NodeJS.ProcessEnv;
|
|
9
|
+
//# sourceMappingURL=safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety.d.ts","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAwE3C,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAGzD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAE3D;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA6ErE;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAI7D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CASjD;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,CAG7E;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,GAAE,MAAM,CAAC,UAAwB,EACrC,YAAY,GAAE,QAAQ,CAAC,MAAM,CAAM,GAClC,MAAM,CAAC,UAAU,CAUnB"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
const SHELL_ACCESS_PATH = String.raw `(?:(?:~|\$(?:HOME|\{HOME\})|\/root|\/home\/[^\/\s]+|\.)?\/?\.ssh(?:\/\S*)?|\/?etc\/(?:ssh|sudoers(?:\.d)?|pam\.d|network|netplan|NetworkManager\/system-connections)(?:\/\S*)?|\/?etc\/(?:passwd|shadow|group|gshadow))`;
|
|
3
|
+
const CRITICAL_SHELL_PATTERNS = [
|
|
4
|
+
/(^|[;&|()\s])(?:rm|rmdir|unlink|shred|wipefs|mkfs(?:\.\w+)?|fdisk|parted|dd|truncate)(?:\s|$)/i,
|
|
5
|
+
/(^|[;&|()\s])find\b[^\n;&|]*(?:-delete|-exec\s+(?:rm|rmdir|shred))\b/i,
|
|
6
|
+
/(^|[;&|()\s])(?:chmod|chown|chgrp|setfacl|setcap|visudo)(?:\s|$)/i,
|
|
7
|
+
/(^|[;&|()\s])(?:userdel|usermod|deluser|groupdel|groupmod|passwd|chpasswd)(?:\s|$)/i,
|
|
8
|
+
/(^|[;&|()\s])(?:mount|umount|swapon|swapoff|cryptsetup|losetup|lvm|pvcreate|vgcreate|lvcreate)(?:\s|$)/i,
|
|
9
|
+
/(^|[;&|()\s])(?:reboot|shutdown|poweroff|halt|init|telinit)(?:\s|$)/i,
|
|
10
|
+
/(^|[;&|()\s])systemctl\b[^\n;&|]*\bisolate\b/i,
|
|
11
|
+
/(^|[;&|()\s])systemctl\b[^\n;&|]*(?:stop|restart|reload|disable|mask|kill)\b[^\n;&|]*(?:ssh(?:d)?|network(?:ing)?|systemd-networkd|networkmanager|firewalld|ufw|tailscaled|wg-quick)(?:\.service)?\b/i,
|
|
12
|
+
/(^|[;&|()\s])service\s+(?:ssh(?:d)?|network(?:ing)?|firewalld|ufw|tailscaled)\s+(?:stop|restart|reload)\b/i,
|
|
13
|
+
/(^|[;&|()\s])loginctl\s+terminate-user\b/i,
|
|
14
|
+
/(^|[;&|()\s])(?:kill|killall|pkill)\b[^\n;&|]*(?:ssh(?:d)?|networkmanager|systemd-networkd|tailscaled)\b/i,
|
|
15
|
+
/(^|[;&|()\s])(?:iptables|ip6tables)\b[^\n;&|]*\s-(?:A|D|F|I|N|P|R|X|Z)\b/i,
|
|
16
|
+
/(^|[;&|()\s])nft\s+(?:add|delete|destroy|flush|insert|replace|reset)\b/i,
|
|
17
|
+
/(^|[;&|()\s])ufw\s+(?:allow|deny|delete|disable|enable|insert|reject|reset|route)\b/i,
|
|
18
|
+
/(^|[;&|()\s])firewall-cmd\b[^\n;&|]*(?:--add-|--remove-|--set-|--reload|--complete-reload|--runtime-to-permanent)\b/i,
|
|
19
|
+
/(^|[;&|()\s])ipset\s+(?:add|del|destroy|flush|restore|swap)\b/i,
|
|
20
|
+
/(^|[;&|()\s])tc\s+\S+\s+(?:add|change|delete|replace)\b/i,
|
|
21
|
+
/(^|[;&|()\s])(?:route\s+(?:add|del)|ifconfig\s+\S+\s+\S+)/i,
|
|
22
|
+
/(^|[;&|()\s])nmcli\b[^\n;&|]*(?:\b(?:add|delete|down|modify|off|on|up)\b)/i,
|
|
23
|
+
/(^|[;&|()\s])networkctl\s+(?:edit|reconfigure|reload)\b/i,
|
|
24
|
+
/(^|[;&|()\s])resolvectl\s+(?:default-route|dns|domain|llmnr|mdns|revert)\b/i,
|
|
25
|
+
/(^|[;&|()\s])wg\s+set\b/i,
|
|
26
|
+
/(^|[;&|()\s])wg-quick\s+(?:down|up)\b/i,
|
|
27
|
+
/(^|[;&|()\s])tailscale\s+(?:down|lock|logout|set|switch|up)\b/i,
|
|
28
|
+
/(^|[;&|()\s])sysctl\s+(?:-w\s+)?[\w.-]+=\S+/i,
|
|
29
|
+
/(^|[;&|()\s])hostnamectl\s+set-\S+\b/i,
|
|
30
|
+
/(^|[;&|()\s])ip\s+(?:addr|address|link|route|rule|netns|neigh|tunnel)\s+(?:add|del|delete|replace|set|flush|change)\b/i,
|
|
31
|
+
/(^|[;&|()\s])(?:ssh-keygen|visudo)(?:\s|$)/i,
|
|
32
|
+
/(^|[;&|()\s])(?:terraform|tofu)\s+destroy\b/i,
|
|
33
|
+
/(^|[;&|()\s])(?:aws|gcloud|az|doctl|heroku|pulumi|nomad|vault|consul)\b[^\n;&|]*\b(?:delete|destroy|remove|terminate)\b/i,
|
|
34
|
+
/(^|[;&|()\s])kubectl\b[^\n;&|]*\b(?:delete|drain)\b/i,
|
|
35
|
+
/(^|[;&|()\s])helm\s+uninstall\b/i,
|
|
36
|
+
/(^|[;&|()\s])docker\b[^\n;&|]*\b(?:prune|rm|rmi)\b/i,
|
|
37
|
+
/(^|[;&|()\s])(?:podman|nerdctl)(?:\s+compose)?\s+(?:prune|rm|rmi|down)\b/i,
|
|
38
|
+
/(^|[;&|()\s])git\b[^\n;&|]*\b(?:clean|reset\s+--hard|checkout\s+--|restore|branch\s+-[dD]|tag\s+-d|remote\s+remove)(?:\s|$)/i,
|
|
39
|
+
/(^|[;&|()\s])git(?:(?:\s+-C\s+\S+)|(?:\s+-c\s+\S+)|(?:\s+--(?:git-dir|work-tree)(?:=\S+|\s+\S+))|(?:\s+--\S+))*\s+push\b[^\n;&|]*(?:--delete\b|(?:^|\s)-d(?:\s|$)|--force(?:-with-lease)?\b|--mirror\b|--prune\b|(?:^|\s)-f(?:\s|$)|(?:^|\s)\+\S+|(?:^|\s):\S+)/i,
|
|
40
|
+
/(^|[;&|()\s])gh\b[^\n;&|]*\bdelete\b/i,
|
|
41
|
+
/(^|[;&|()\s])(?:npm|pnpm|yarn)\s+unpublish\b/i,
|
|
42
|
+
/(^|[;&|()\s])(?:apt|apt-get|dnf|yum|pacman|zypper|apk|snap|flatpak)\s+(?:remove|purge|autoremove|uninstall)\b/i,
|
|
43
|
+
/(^|[;&|()\s])curl\b[^\n;&|]*(?:-[Xx]\s*DELETE|--request\s+DELETE)\b/i,
|
|
44
|
+
/(^|[;&|()\s])crontab\s+-r\b/i,
|
|
45
|
+
/(^|[;&|()\s"'])(?:psql|mysql|mariadb|sqlite3|redis-cli|mongosh|mongo)\b[^\n;&|]*\b(?:DELETE\s+FROM|DROP\s+(?:DATABASE|SCHEMA|TABLE|INDEX|VIEW)|TRUNCATE\s+(?:DATABASE|SCHEMA|TABLE))\b/i,
|
|
46
|
+
/(^|[;&|()\s"'])redis-cli\b[^\n;&|]*\b(?:DEL|UNLINK|FLUSHDB|FLUSHALL|GETDEL)\b/i,
|
|
47
|
+
/(^|[;&|()\s"'])(?:mongosh|mongo)\b[^\n;&|]*\b(?:deleteOne|deleteMany|dropDatabase|drop|remove)\s*\(/i,
|
|
48
|
+
new RegExp(String.raw `(?:>{1,2}\|?|<>|&>)\s*["']?${SHELL_ACCESS_PATH}`, "i"),
|
|
49
|
+
new RegExp(String.raw `(^|[;&|()\s])sed\s+-i\b[^\n;&|]*[\s"']${SHELL_ACCESS_PATH}`, "i"),
|
|
50
|
+
new RegExp(String.raw `(^|[;&|()\s])tee\b(?:\s+-\S+)*\s+["']?${SHELL_ACCESS_PATH}["']?(?:\s|$|[;&|])`, "i"),
|
|
51
|
+
new RegExp(String.raw `(^|[;&|()\s])(?:cp|mv|install)\b[^\n;&|]*[\s"']${SHELL_ACCESS_PATH}["']?\s*(?:$|[;&|])`, "i"),
|
|
52
|
+
new RegExp(String.raw `(^|[;&|()\s])(?:cp|mv|install)\b[^\n;&|]*(?:--target-directory(?:=|\s+)|-t\s+)["']?${SHELL_ACCESS_PATH}`, "i"),
|
|
53
|
+
];
|
|
54
|
+
const ACCESS_LOCKOUT_PATCH_PATH = /(?:^|\/)(?:\.ssh|authorized_keys|etc\/(?:ssh|sudoers(?:\.d)?|pam\.d|network|netplan|NetworkManager\/system-connections))(?:\/|$)/i;
|
|
55
|
+
const NON_EXECUTABLE_SHELL_WORDS = new Set([
|
|
56
|
+
".", "!",
|
|
57
|
+
"case", "coproc", "do", "done", "elif", "else", "esac", "fi", "for", "function", "if", "in", "select", "then", "until", "while",
|
|
58
|
+
]);
|
|
59
|
+
function normalizedShellText(command) {
|
|
60
|
+
return command
|
|
61
|
+
.replace(/\\([A-Za-z])/g, "$1")
|
|
62
|
+
.replace(/(["'])([A-Za-z][\w.-]*)\1/g, "$2")
|
|
63
|
+
.replace(/(?<=[A-Za-z])["']{2}(?=[A-Za-z])/g, "")
|
|
64
|
+
.replace(/(^|[;&|()\s])\/(?:usr\/)?s?bin\/(?=[\w.-])/g, "$1");
|
|
65
|
+
}
|
|
66
|
+
export function bashCommandRisk(command) {
|
|
67
|
+
const normalized = normalizedShellText(command);
|
|
68
|
+
return CRITICAL_SHELL_PATTERNS.some((pattern) => pattern.test(normalized)) ? "critical" : "shell";
|
|
69
|
+
}
|
|
70
|
+
export function bashExecutionRisk(command) {
|
|
71
|
+
return bashCommandRisk(command);
|
|
72
|
+
}
|
|
73
|
+
export function bashApprovalExecutable(command) {
|
|
74
|
+
if (!command.trim())
|
|
75
|
+
return null;
|
|
76
|
+
const words = [];
|
|
77
|
+
let word = "";
|
|
78
|
+
let started = false;
|
|
79
|
+
let quote = null;
|
|
80
|
+
let escaped = false;
|
|
81
|
+
const finishWord = () => {
|
|
82
|
+
if (!started)
|
|
83
|
+
return;
|
|
84
|
+
words.push(word);
|
|
85
|
+
word = "";
|
|
86
|
+
started = false;
|
|
87
|
+
};
|
|
88
|
+
const hasExecutable = () => {
|
|
89
|
+
const candidate = words.at(-1);
|
|
90
|
+
return Boolean(candidate && !/^[A-Za-z_][A-Za-z0-9_]*(?:\+)?=/.test(candidate));
|
|
91
|
+
};
|
|
92
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
93
|
+
const character = command[index];
|
|
94
|
+
if (escaped) {
|
|
95
|
+
word += character;
|
|
96
|
+
started = true;
|
|
97
|
+
escaped = false;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (quote === "single") {
|
|
101
|
+
if (character === "'")
|
|
102
|
+
quote = null;
|
|
103
|
+
else
|
|
104
|
+
word += character;
|
|
105
|
+
started = true;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (quote === "double") {
|
|
109
|
+
if (character === '"')
|
|
110
|
+
quote = null;
|
|
111
|
+
else if (character === "\\")
|
|
112
|
+
escaped = true;
|
|
113
|
+
else {
|
|
114
|
+
if (character === "`" || (character === "$" && command[index + 1] === "("))
|
|
115
|
+
return null;
|
|
116
|
+
word += character;
|
|
117
|
+
}
|
|
118
|
+
started = true;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (character === "\\") {
|
|
122
|
+
escaped = true;
|
|
123
|
+
started = true;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (character === "'") {
|
|
127
|
+
quote = "single";
|
|
128
|
+
started = true;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (character === '"') {
|
|
132
|
+
quote = "double";
|
|
133
|
+
started = true;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (/\s/.test(character)) {
|
|
137
|
+
finishWord();
|
|
138
|
+
if (hasExecutable())
|
|
139
|
+
break;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (character === "#" && !started)
|
|
143
|
+
break;
|
|
144
|
+
if (";&|(){}<>`".includes(character)) {
|
|
145
|
+
if (!started)
|
|
146
|
+
return null;
|
|
147
|
+
finishWord();
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
if (character === "$" && command[index + 1] === "(")
|
|
151
|
+
return null;
|
|
152
|
+
word += character;
|
|
153
|
+
started = true;
|
|
154
|
+
}
|
|
155
|
+
if (quote || escaped)
|
|
156
|
+
return null;
|
|
157
|
+
finishWord();
|
|
158
|
+
const executable = words.find((candidate) => !/^[A-Za-z_][A-Za-z0-9_]*(?:\+)?=/.test(candidate));
|
|
159
|
+
if (!executable || /[$*?\[]/.test(executable) || NON_EXECUTABLE_SHELL_WORDS.has(executable))
|
|
160
|
+
return null;
|
|
161
|
+
const systemExecutable = /^\/(?:usr\/)?(?:s?bin)\/([^/]+)$/.exec(executable)?.[1];
|
|
162
|
+
return systemExecutable ?? executable;
|
|
163
|
+
}
|
|
164
|
+
export function isSensitiveMutationPath(path) {
|
|
165
|
+
const normalized = resolve("/", path.replaceAll("\\", "/")).slice(1);
|
|
166
|
+
if (ACCESS_LOCKOUT_PATCH_PATH.test(normalized))
|
|
167
|
+
return true;
|
|
168
|
+
return /(?:^|\/)etc\/(?:passwd|shadow|group|gshadow)$/.test(normalized);
|
|
169
|
+
}
|
|
170
|
+
export function patchRisk(patch) {
|
|
171
|
+
for (const line of patch.split("\n")) {
|
|
172
|
+
if (line.startsWith("*** Delete File:"))
|
|
173
|
+
return "critical";
|
|
174
|
+
const path = /^(?:\*\*\* (?:Add|Update) File:|\*\*\* Move to:)\s*(.+)$/.exec(line)?.[1]?.trim();
|
|
175
|
+
if (path) {
|
|
176
|
+
if (isSensitiveMutationPath(path))
|
|
177
|
+
return "critical";
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return "write";
|
|
181
|
+
}
|
|
182
|
+
export function workspacePatchRisk(workspace, patch) {
|
|
183
|
+
void workspace;
|
|
184
|
+
return patchRisk(patch);
|
|
185
|
+
}
|
|
186
|
+
export function shellEnvironment(base = process.env, excludedKeys = []) {
|
|
187
|
+
const env = { ...base };
|
|
188
|
+
for (const key of Object.keys(env)) {
|
|
189
|
+
if (key.startsWith("BASH_FUNC_"))
|
|
190
|
+
delete env[key];
|
|
191
|
+
}
|
|
192
|
+
for (const key of ["BASH_ENV", "ENV", "SHELLOPTS", "BASHOPTS", "CDPATH", "GLOBIGNORE", "PROMPT_COMMAND", "RIPGREP_CONFIG_PATH"]) {
|
|
193
|
+
delete env[key];
|
|
194
|
+
}
|
|
195
|
+
for (const key of excludedKeys)
|
|
196
|
+
delete env[key];
|
|
197
|
+
return env;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/tools/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA+C5C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,EAAE,CA6JtE"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { isWithin, resolveWorkspacePath } from "./paths.js";
|
|
2
|
+
import { bashCommandRisk } from "./safety.js";
|
|
3
|
+
function formatJob(job) {
|
|
4
|
+
if (!job)
|
|
5
|
+
return "Job not found.";
|
|
6
|
+
const action = job.kind === "reminder" ? job.message : job.kind === "session_prompt" ? job.prompt : job.command;
|
|
7
|
+
return [
|
|
8
|
+
`id: ${job.id}`,
|
|
9
|
+
`kind: ${job.kind}`,
|
|
10
|
+
`schedule: ${job.scheduleKind} ${job.schedule}${job.scheduleKind === "cron" ? ` (${job.timezone})` : ""}`,
|
|
11
|
+
`next: ${job.nextDue === null ? "none" : new Date(job.nextDue).toISOString()}`,
|
|
12
|
+
`state: ${job.enabled ? "enabled" : "disabled"}${job.blockedReason ? `, blocked: ${job.blockedReason}` : ""}`,
|
|
13
|
+
`action: ${action ?? ""}`,
|
|
14
|
+
].join("\n");
|
|
15
|
+
}
|
|
16
|
+
function canManageJob(job, context) {
|
|
17
|
+
if (job.kind === "session_prompt")
|
|
18
|
+
return job.sessionId === context.sessionId;
|
|
19
|
+
if (job.kind === "command") {
|
|
20
|
+
const root = resolveWorkspacePath(context.workspace, ".");
|
|
21
|
+
return job.cwd !== null && isWithin(root, job.cwd);
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
export function createScheduleTools(store) {
|
|
26
|
+
const create = {
|
|
27
|
+
name: "schedule_create",
|
|
28
|
+
description: "Create a persistent reminder, deterministic shell command, or prompt for this persistent session. Use only after an explicit user request.",
|
|
29
|
+
risk: "persistent",
|
|
30
|
+
classifyRisk: (args) => args.kind === "command" && args.command && bashCommandRisk(args.command) === "critical"
|
|
31
|
+
? "critical"
|
|
32
|
+
: "persistent",
|
|
33
|
+
parameters: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
kind: { type: "string", enum: ["reminder", "command", "session_prompt"] },
|
|
37
|
+
schedule_kind: { type: "string", enum: ["once", "cron"] },
|
|
38
|
+
schedule: { type: "string", minLength: 1, description: "ISO timestamp with offset, or exactly five cron fields" },
|
|
39
|
+
timezone: { type: ["string", "null"], description: "IANA timezone for cron schedules" },
|
|
40
|
+
message: { type: ["string", "null"] },
|
|
41
|
+
command: { type: ["string", "null"] },
|
|
42
|
+
prompt: { type: ["string", "null"], description: "Prompt to run later in the current persistent session" },
|
|
43
|
+
cwd: { type: ["string", "null"], description: "Workspace-relative command directory" },
|
|
44
|
+
timeout_ms: { type: ["integer", "null"], minimum: 100, maximum: 3600000 },
|
|
45
|
+
},
|
|
46
|
+
required: ["kind", "schedule_kind", "schedule", "timezone", "message", "command", "prompt", "cwd", "timeout_ms"],
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
},
|
|
49
|
+
summarize: (args) => {
|
|
50
|
+
const action = args.kind === "reminder" ? args.message : args.kind === "session_prompt" ? args.prompt : args.command;
|
|
51
|
+
return `Create ${args.schedule_kind} ${args.kind}: ${action ?? ""} at ${args.schedule}`;
|
|
52
|
+
},
|
|
53
|
+
async execute(args, context) {
|
|
54
|
+
const timezone = args.timezone ?? context.config.scheduler.timezone;
|
|
55
|
+
if (args.kind === "reminder") {
|
|
56
|
+
if (!args.message || args.command || args.prompt || args.cwd || args.timeout_ms) {
|
|
57
|
+
throw new Error("Reminder schedules require message and no command fields");
|
|
58
|
+
}
|
|
59
|
+
const job = store.createReminder({
|
|
60
|
+
message: args.message,
|
|
61
|
+
scheduleKind: args.schedule_kind,
|
|
62
|
+
schedule: args.schedule,
|
|
63
|
+
timezone,
|
|
64
|
+
});
|
|
65
|
+
return { output: `Persistent reminder created.\n${formatJob(job)}` };
|
|
66
|
+
}
|
|
67
|
+
if (args.kind === "session_prompt") {
|
|
68
|
+
if (!args.prompt || args.message || args.command || args.cwd || args.timeout_ms) {
|
|
69
|
+
throw new Error("Session prompt schedules require prompt and no command fields");
|
|
70
|
+
}
|
|
71
|
+
const job = store.createSessionPrompt({
|
|
72
|
+
sessionId: context.sessionId,
|
|
73
|
+
prompt: args.prompt,
|
|
74
|
+
scheduleKind: args.schedule_kind,
|
|
75
|
+
schedule: args.schedule,
|
|
76
|
+
timezone,
|
|
77
|
+
startGraceMs: context.config.scheduler.commandStartGraceMs,
|
|
78
|
+
outputBytes: context.config.scheduler.commandOutputBytes,
|
|
79
|
+
});
|
|
80
|
+
return { output: `Persistent session prompt created.\n${formatJob(job)}` };
|
|
81
|
+
}
|
|
82
|
+
if (!args.command || args.message || args.prompt)
|
|
83
|
+
throw new Error("Command schedules require command and no message or prompt");
|
|
84
|
+
const cwd = args.cwd ? resolveWorkspacePath(context.workspace, args.cwd) : context.workspace;
|
|
85
|
+
const job = store.createCommand({
|
|
86
|
+
command: args.command,
|
|
87
|
+
cwd,
|
|
88
|
+
scheduleKind: args.schedule_kind,
|
|
89
|
+
schedule: args.schedule,
|
|
90
|
+
timezone,
|
|
91
|
+
startGraceMs: context.config.scheduler.commandStartGraceMs,
|
|
92
|
+
timeoutMs: args.timeout_ms ?? context.config.scheduler.commandTimeoutMs,
|
|
93
|
+
outputBytes: context.config.scheduler.commandOutputBytes,
|
|
94
|
+
});
|
|
95
|
+
return { output: `Persistent command created.\n${formatJob(job)}` };
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
const list = {
|
|
99
|
+
name: "schedule_list",
|
|
100
|
+
description: "List persistent schedules and optionally unread scheduler inbox results.",
|
|
101
|
+
risk: "read",
|
|
102
|
+
parameters: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: { include_inbox: { type: "boolean" } },
|
|
105
|
+
required: ["include_inbox"],
|
|
106
|
+
additionalProperties: false,
|
|
107
|
+
},
|
|
108
|
+
summarize: () => "List persistent schedules",
|
|
109
|
+
async execute(args, context) {
|
|
110
|
+
const jobs = store.listJobs().filter((job) => canManageJob(job, context));
|
|
111
|
+
const sections = [jobs.length > 0 ? jobs.map((job) => formatJob(job)).join("\n\n") : "No schedules."];
|
|
112
|
+
if (args.include_inbox) {
|
|
113
|
+
const inbox = store.listInboxForJobs(jobs.map((job) => job.id), { unackedOnly: true, limit: 50 });
|
|
114
|
+
sections.push(inbox.length > 0
|
|
115
|
+
? `Unread inbox:\n${inbox.map((item) => `${item.id}: ${item.message} (${new Date(item.createdAt).toISOString()})`).join("\n")}`
|
|
116
|
+
: "Unread inbox: empty");
|
|
117
|
+
}
|
|
118
|
+
return { output: sections.join("\n\n") };
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
const manage = {
|
|
122
|
+
name: "schedule_manage",
|
|
123
|
+
description: "Pause, resume, delete, run immediately, or acknowledge a persistent schedule item.",
|
|
124
|
+
risk: "persistent",
|
|
125
|
+
classifyRisk: (args) => {
|
|
126
|
+
if (args.action === "delete")
|
|
127
|
+
return "critical";
|
|
128
|
+
const job = store.getJob(args.id);
|
|
129
|
+
if (job?.kind === "command"
|
|
130
|
+
&& (args.action === "resume" || args.action === "run_now" || args.action === "resolve_unknown")
|
|
131
|
+
&& bashCommandRisk(job.command ?? "") === "critical")
|
|
132
|
+
return "critical";
|
|
133
|
+
return "persistent";
|
|
134
|
+
},
|
|
135
|
+
parameters: {
|
|
136
|
+
type: "object",
|
|
137
|
+
properties: {
|
|
138
|
+
action: { type: "string", enum: ["pause", "resume", "delete", "run_now", "ack_inbox", "resolve_unknown"] },
|
|
139
|
+
id: { type: "string", minLength: 1 },
|
|
140
|
+
},
|
|
141
|
+
required: ["action", "id"],
|
|
142
|
+
additionalProperties: false,
|
|
143
|
+
},
|
|
144
|
+
summarize: (args) => {
|
|
145
|
+
if (args.action === "ack_inbox")
|
|
146
|
+
return `Acknowledge scheduler inbox item ${args.id}`;
|
|
147
|
+
const job = store.getJob(args.id);
|
|
148
|
+
if (!job)
|
|
149
|
+
return `${args.action} missing schedule ${args.id}`;
|
|
150
|
+
const action = job.kind === "reminder" ? job.message : job.kind === "session_prompt" ? job.prompt : job.command;
|
|
151
|
+
return [
|
|
152
|
+
`${args.action} ${job.kind} schedule ${job.id}`,
|
|
153
|
+
`${job.scheduleKind}:${job.schedule} (${job.timezone})`,
|
|
154
|
+
job.cwd ? `cwd: ${job.cwd}` : "",
|
|
155
|
+
`Exact action: ${action ?? ""}`,
|
|
156
|
+
].filter(Boolean).join("\n");
|
|
157
|
+
},
|
|
158
|
+
async execute(args, context) {
|
|
159
|
+
if (args.action === "ack_inbox") {
|
|
160
|
+
const id = Number(args.id);
|
|
161
|
+
if (!Number.isSafeInteger(id) || id < 1)
|
|
162
|
+
throw new Error("Inbox id must be a positive integer");
|
|
163
|
+
const item = store.getInbox(id);
|
|
164
|
+
if (!item)
|
|
165
|
+
return { output: `Inbox item ${id} is missing.` };
|
|
166
|
+
const job = store.getJob(item.jobId);
|
|
167
|
+
if (!job || !canManageJob(job, context))
|
|
168
|
+
throw new Error("Scheduler item belongs to another workspace or session");
|
|
169
|
+
return { output: store.ackInbox(id) ? `Acknowledged inbox item ${id}.` : `Inbox item ${id} was already acknowledged or missing.` };
|
|
170
|
+
}
|
|
171
|
+
const job = store.getJob(args.id);
|
|
172
|
+
if (!job)
|
|
173
|
+
throw new Error(`Schedule not found: ${args.id}`);
|
|
174
|
+
if (!canManageJob(job, context))
|
|
175
|
+
throw new Error("Schedule belongs to another workspace or session");
|
|
176
|
+
if (args.action === "pause")
|
|
177
|
+
return { output: `Paused.\n${formatJob(store.pauseJob(args.id))}` };
|
|
178
|
+
if (args.action === "resume")
|
|
179
|
+
return { output: `Resumed.\n${formatJob(store.resumeJob(args.id))}` };
|
|
180
|
+
if (args.action === "resolve_unknown") {
|
|
181
|
+
return { output: `Unknown outcome acknowledged.\n${formatJob(store.acknowledgeUnknown(args.id))}` };
|
|
182
|
+
}
|
|
183
|
+
if (args.action === "delete")
|
|
184
|
+
return { output: `Deleted.\n${formatJob(store.deleteJob(args.id))}` };
|
|
185
|
+
const run = store.runNow(args.id);
|
|
186
|
+
return { output: `Queued occurrence ${run.id} with state ${run.state}.` };
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
return [create, list, manage];
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=schedule.js.map
|