privateer-agent 0.1.1 → 0.3.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 +143 -398
- package/bin/privateer-tui +86 -0
- package/bin/privateer.mjs +17 -6
- package/bin/pv +28 -0
- package/package.json +26 -23
- package/src/auth/privateer.ts +128 -1
- package/src/boot.ts +43 -0
- package/src/bridge/engineAdapter.ts +182 -0
- package/src/cli/chat.ts +243 -0
- package/src/config/paths.ts +24 -44
- package/src/config/permissionMode.ts +5 -0
- package/src/crypto/outboxSeal.ts +61 -0
- package/src/daemon/index.ts +405 -0
- package/src/daemon/ipc.ts +127 -0
- package/src/engine/errors.ts +95 -45
- package/src/engine/router.ts +11 -165
- package/src/ext/permissionGate.ts +216 -0
- package/src/main.ts +32 -0
- package/src/permissions/classify.ts +172 -0
- package/src/permissions/gate.ts +13 -11
- package/src/permissions/mode.ts +9 -1
- package/src/permissions/{uiGate.ts → modeGate.ts} +22 -6
- package/src/providers/account.ts +170 -0
- package/src/providers/catalog.ts +73 -61
- package/src/providers/genModelsJson.ts +97 -0
- package/src/remote/relayClient.ts +91 -4
- package/src/remote/remoteBridge.ts +152 -0
- package/src/routines/cron.ts +109 -0
- package/src/routines/delivery.ts +184 -0
- package/src/routines/schema.ts +84 -0
- package/src/routines/store.ts +248 -0
- package/src/routines/toolSelect.ts +50 -0
- package/src/routines/trigger.ts +41 -0
- package/src/session.ts +84 -251
- package/src/tools/routine.ts +129 -0
- package/src/tools/saveAttachment.ts +39 -42
- package/src/tools/sendFile.ts +75 -0
- package/src/util/attachmentStore.ts +18 -35
- package/src/util/redact.ts +33 -3
- package/LICENSE +0 -21
- package/src/agents/loader.ts +0 -49
- package/src/commands/custom.ts +0 -75
- package/src/commands/registry.ts +0 -499
- package/src/components/AgentGroupView.tsx +0 -104
- package/src/components/App.tsx +0 -1420
- package/src/components/ApprovalPrompt.tsx +0 -38
- package/src/components/Banner.tsx +0 -76
- package/src/components/Markdown.tsx +0 -183
- package/src/components/ModeHint.tsx +0 -40
- package/src/components/ModelPicker.tsx +0 -269
- package/src/components/Onboarding.tsx +0 -203
- package/src/components/PlanConfirm.tsx +0 -37
- package/src/components/PrivateerLogin.tsx +0 -109
- package/src/components/PromptInput.tsx +0 -602
- package/src/components/RewindPicker.tsx +0 -69
- package/src/components/Root.tsx +0 -95
- package/src/components/SessionPicker.tsx +0 -64
- package/src/components/StatusBar.tsx +0 -131
- package/src/components/TodoPanel.tsx +0 -36
- package/src/components/ToolCallView.tsx +0 -109
- package/src/components/Transcript.tsx +0 -203
- package/src/components/figures.ts +0 -14
- package/src/components/promptModel.ts +0 -73
- package/src/components/spinnerVerbs.ts +0 -46
- package/src/components/theme.ts +0 -55
- package/src/components/types.ts +0 -34
- package/src/components/useTeeShield.ts +0 -104
- package/src/components/useTerminalWidth.ts +0 -24
- package/src/components/useZdrShield.ts +0 -126
- package/src/config/load.ts +0 -115
- package/src/config/schema.ts +0 -94
- package/src/context/outputStyles.ts +0 -42
- package/src/context/projectInfo.ts +0 -59
- package/src/context/systemPrompt.ts +0 -167
- package/src/engine/QueryEngine.ts +0 -399
- package/src/hooks/engine.ts +0 -155
- package/src/main.tsx +0 -167
- package/src/mcp/client.ts +0 -236
- package/src/mcp/oauth.ts +0 -245
- package/src/memory/auto.ts +0 -146
- package/src/memory/checkpoints.ts +0 -227
- package/src/memory/store.ts +0 -127
- package/src/providers/attestation.ts +0 -149
- package/src/providers/capabilities.ts +0 -104
- package/src/providers/models.ts +0 -183
- package/src/providers/registry.ts +0 -71
- package/src/providers/resolve.ts +0 -78
- package/src/tools/bash.ts +0 -98
- package/src/tools/context.ts +0 -114
- package/src/tools/edit.ts +0 -67
- package/src/tools/exec.ts +0 -60
- package/src/tools/glob.ts +0 -39
- package/src/tools/grep.ts +0 -86
- package/src/tools/index.ts +0 -69
- package/src/tools/memory.ts +0 -53
- package/src/tools/processRegistry.ts +0 -77
- package/src/tools/read.ts +0 -42
- package/src/tools/task.ts +0 -52
- package/src/tools/todo.ts +0 -36
- package/src/tools/todoStore.ts +0 -31
- package/src/tools/walk.ts +0 -44
- package/src/tools/web.ts +0 -145
- package/src/tools/write.ts +0 -40
- package/src/util/images.ts +0 -356
- package/src/util/limit.ts +0 -32
- package/src/version.ts +0 -13
|
@@ -1,54 +1,39 @@
|
|
|
1
1
|
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join, extname } from "node:path";
|
|
4
|
-
import type { Attachment, Modality } from "./images.ts";
|
|
5
4
|
|
|
6
|
-
//
|
|
7
|
-
// "#n" reference the
|
|
5
|
+
// Holds the decoded bytes of files the app sends down over the relay, in a private
|
|
6
|
+
// scratch dir keyed by the "#n" reference the model sees, so the save_attachment tool
|
|
7
|
+
// can write one back out to a real path on demand. Ported/adapted from tree-cli
|
|
8
|
+
// (which persisted paste/drop bytes); here the source is inbound relay attachments.
|
|
9
|
+
|
|
8
10
|
export interface StoredAttachment {
|
|
9
11
|
n: number;
|
|
10
12
|
path: string; // absolute scratch-file path holding the decoded bytes
|
|
11
13
|
mediaType: string;
|
|
12
|
-
|
|
13
|
-
origin: string; // the token the user referenced (for display/debugging)
|
|
14
|
+
name: string; // original filename from the app
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
// Keeps the decoded bytes of every attachment seen this session in a private scratch
|
|
17
|
-
// dir, keyed by its "[Kind #n]" reference, so the `save_attachment` tool can write one
|
|
18
|
-
// back out to a real path on demand.
|
|
19
|
-
//
|
|
20
|
-
// This exists to close the macOS file-promise drop gap: the bytes we capture at
|
|
21
|
-
// paste-time (in resolveAttachments) are the only durable copy — the terminal's
|
|
22
|
-
// …/T/drop-XXXXXX/ file is a volatile stub that may already be gone or truncated by the
|
|
23
|
-
// time the agent goes looking. We copy what we captured into a place we control, once,
|
|
24
|
-
// instead of trusting that path twice.
|
|
25
17
|
export class AttachmentStore {
|
|
26
18
|
private dir: string | null = null;
|
|
27
19
|
private readonly byN = new Map<number, StoredAttachment>();
|
|
20
|
+
private nextN = 1;
|
|
28
21
|
|
|
29
22
|
private ensureDir(): string {
|
|
30
|
-
// mkdtemp gives a 0700 dir; created lazily so a session that never
|
|
31
|
-
//
|
|
23
|
+
// mkdtemp gives a 0700 dir; created lazily so a session that never receives an
|
|
24
|
+
// attachment leaves no temp files behind.
|
|
32
25
|
if (!this.dir) this.dir = mkdtempSync(join(tmpdir(), "privateer-att-"));
|
|
33
26
|
return this.dir;
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
// Persist an attachment's bytes
|
|
37
|
-
//
|
|
38
|
-
register(
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
const stored: StoredAttachment = {
|
|
45
|
-
n: att.n,
|
|
46
|
-
path: file,
|
|
47
|
-
mediaType: att.mediaType,
|
|
48
|
-
modality: att.modality,
|
|
49
|
-
origin: att.path,
|
|
50
|
-
};
|
|
51
|
-
this.byN.set(att.n, stored);
|
|
29
|
+
// Persist an inbound attachment's bytes, assign it the next ref number, and return
|
|
30
|
+
// the stored record (its ref + scratch path).
|
|
31
|
+
register(file: { name: string; mediaType: string; base64: string }): StoredAttachment {
|
|
32
|
+
const n = this.nextN++;
|
|
33
|
+
const path = join(this.ensureDir(), `att-${n}${extname(file.name) || ""}`);
|
|
34
|
+
writeFileSync(path, Buffer.from(file.base64, "base64"));
|
|
35
|
+
const stored: StoredAttachment = { n, path, mediaType: file.mediaType, name: file.name };
|
|
36
|
+
this.byN.set(n, stored);
|
|
52
37
|
return stored;
|
|
53
38
|
}
|
|
54
39
|
|
|
@@ -56,13 +41,11 @@ export class AttachmentStore {
|
|
|
56
41
|
return this.byN.get(n);
|
|
57
42
|
}
|
|
58
43
|
|
|
59
|
-
//
|
|
60
|
-
// model asks for one that isn't there.
|
|
44
|
+
// Held reference numbers, ascending — for a helpful error when a missing one is asked for.
|
|
61
45
|
refs(): number[] {
|
|
62
46
|
return [...this.byN.keys()].sort((a, b) => a - b);
|
|
63
47
|
}
|
|
64
48
|
|
|
65
|
-
// Remove the scratch dir (e.g. on session exit). Safe to call when nothing was stored.
|
|
66
49
|
cleanup(): void {
|
|
67
50
|
if (!this.dir) return;
|
|
68
51
|
rmSync(this.dir, { recursive: true, force: true });
|
package/src/util/redact.ts
CHANGED
|
@@ -6,11 +6,22 @@
|
|
|
6
6
|
const PLACEHOLDER = "«redacted»";
|
|
7
7
|
|
|
8
8
|
// Common API-key shapes, masked even when we don't have the exact value on hand:
|
|
9
|
-
// OpenAI `sk-…`, Anthropic `sk-ant-…`, OpenRouter `sk-or-v1-…`,
|
|
10
|
-
//
|
|
9
|
+
// OpenAI `sk-…`, Anthropic `sk-ant-…`, OpenRouter `sk-or-v1-…`, Google `AIza…`,
|
|
10
|
+
// xAI `xai-…`, Groq `gsk_…`, Z.ai/Zhipu `<32 hex>.<suffix>`, Cerebras `csk-…`,
|
|
11
|
+
// Venice `vapi_…`, Fireworks `fw_…`, and bare
|
|
12
|
+
// "Bearer <token>" / "x-api-key: <token>" / "x-goog-api-key: <token>" header
|
|
13
|
+
// fragments. (Mistral and Together keys are prefix-less, so they rely on
|
|
14
|
+
// exact-value masking and the header patterns.)
|
|
11
15
|
const KEY_PATTERNS: RegExp[] = [
|
|
12
16
|
/\bsk-(ant|or|proj|live|test)?-?[A-Za-z0-9_-]{16,}\b/g,
|
|
13
|
-
/\
|
|
17
|
+
/\bAIza[A-Za-z0-9_-]{20,}\b/g,
|
|
18
|
+
/\bxai-[A-Za-z0-9_-]{16,}\b/g,
|
|
19
|
+
/\bgsk_[A-Za-z0-9_-]{16,}\b/g,
|
|
20
|
+
/\bcsk-[A-Za-z0-9_-]{16,}\b/g,
|
|
21
|
+
/\bvapi_[A-Za-z0-9_-]{16,}\b/g,
|
|
22
|
+
/\bfw_[A-Za-z0-9_-]{16,}\b/g,
|
|
23
|
+
/\b[a-f0-9]{32}\.[A-Za-z0-9]{10,}\b/g,
|
|
24
|
+
/\b(authorization|x-api-key|x-goog-api-key)\b\s*[:=]\s*(bearer\s+)?["']?[A-Za-z0-9_\-.]{16,}["']?/gi,
|
|
14
25
|
];
|
|
15
26
|
|
|
16
27
|
// Exact secret strings to mask, gathered from the resolved config + environment.
|
|
@@ -26,8 +37,27 @@ export function collectSecrets(providers?: Record<string, { apiKey?: string } |
|
|
|
26
37
|
"OPENROUTER_API_KEY",
|
|
27
38
|
"ANTHROPIC_API_KEY",
|
|
28
39
|
"OPENAI_API_KEY",
|
|
40
|
+
"GOOGLE_GENERATIVE_AI_API_KEY",
|
|
41
|
+
"GEMINI_API_KEY",
|
|
42
|
+
"GOOGLE_API_KEY",
|
|
43
|
+
"XAI_API_KEY",
|
|
44
|
+
"GROQ_API_KEY",
|
|
45
|
+
"MISTRAL_API_KEY",
|
|
46
|
+
"ZAI_API_KEY",
|
|
47
|
+
"Z_AI_API_KEY",
|
|
48
|
+
"MOONSHOT_API_KEY",
|
|
49
|
+
"CEREBRAS_API_KEY",
|
|
50
|
+
"FIREWORKS_API_KEY",
|
|
51
|
+
"TOGETHER_API_KEY",
|
|
52
|
+
"TOGETHER_AI_API_KEY",
|
|
53
|
+
"DEEPSEEK_API_KEY",
|
|
54
|
+
"MINIMAX_API_KEY",
|
|
55
|
+
"DASHSCOPE_API_KEY",
|
|
56
|
+
"QWEN_API_KEY",
|
|
29
57
|
"NEAR_AI_API_KEY",
|
|
30
58
|
"NEARAI_API_KEY",
|
|
59
|
+
"TINFOIL_API_KEY",
|
|
60
|
+
"VENICE_API_KEY",
|
|
31
61
|
])
|
|
32
62
|
add(process.env[k]);
|
|
33
63
|
return [...out];
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Patrick
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/src/agents/loader.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { globalPaths, projectPaths } from "../config/paths.ts";
|
|
4
|
-
import { walkFiles } from "../tools/walk.ts";
|
|
5
|
-
import { parseFrontmatter } from "../commands/custom.ts";
|
|
6
|
-
|
|
7
|
-
// A user-defined sub-agent loaded from .privateer/agents/<name>.md. The body is the
|
|
8
|
-
// agent's instructions (system prompt); frontmatter narrows its tools and model.
|
|
9
|
-
export interface AgentDefinition {
|
|
10
|
-
name: string;
|
|
11
|
-
description: string;
|
|
12
|
-
tools?: string[]; // tool names the agent may use (default: read/glob/grep)
|
|
13
|
-
model?: string; // optional "provider:model" override
|
|
14
|
-
prompt: string; // the agent's instructions
|
|
15
|
-
scope: "project" | "user";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function loadFromDir(dir: string, scope: "project" | "user"): AgentDefinition[] {
|
|
19
|
-
if (!existsSync(dir)) return [];
|
|
20
|
-
const out: AgentDefinition[] = [];
|
|
21
|
-
for (const rel of walkFiles(dir)) {
|
|
22
|
-
if (!rel.endsWith(".md")) continue;
|
|
23
|
-
const { meta, body } = parseFrontmatter(readFileSync(join(dir, rel), "utf8"));
|
|
24
|
-
out.push({
|
|
25
|
-
name: meta.name || rel.replace(/\.md$/, "").split("/").join(":"),
|
|
26
|
-
description: meta.description ?? `custom ${scope} sub-agent`,
|
|
27
|
-
tools: meta.tools
|
|
28
|
-
?.split(",")
|
|
29
|
-
.map((s) => s.trim())
|
|
30
|
-
.filter(Boolean),
|
|
31
|
-
model: meta.model,
|
|
32
|
-
prompt: body.trim(),
|
|
33
|
-
scope,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
return out;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// User (~/.privateer) then project (./.privateer) agents; project overrides user.
|
|
40
|
-
export function loadAgents(cwd: string = process.cwd()): AgentDefinition[] {
|
|
41
|
-
const byName = new Map<string, AgentDefinition>();
|
|
42
|
-
for (const a of loadFromDir(globalPaths().agents, "user")) byName.set(a.name, a);
|
|
43
|
-
for (const a of loadFromDir(projectPaths(cwd).agents, "project")) byName.set(a.name, a);
|
|
44
|
-
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function findAgent(name: string, cwd: string = process.cwd()): AgentDefinition | undefined {
|
|
48
|
-
return loadAgents(cwd).find((a) => a.name === name);
|
|
49
|
-
}
|
package/src/commands/custom.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { globalPaths, projectPaths } from "../config/paths.ts";
|
|
4
|
-
import { walkFiles } from "../tools/walk.ts";
|
|
5
|
-
|
|
6
|
-
// A user-authored slash command loaded from a markdown file under
|
|
7
|
-
// .privateer/commands/. The body is a prompt template; frontmatter is optional.
|
|
8
|
-
export interface CustomCommand {
|
|
9
|
-
name: string; // file path minus .md, separators → ":" (e.g. "git/pr.md" → "git:pr")
|
|
10
|
-
description: string;
|
|
11
|
-
argumentHint?: string;
|
|
12
|
-
allowedTools?: string[];
|
|
13
|
-
model?: string;
|
|
14
|
-
body: string;
|
|
15
|
-
scope: "project" | "user";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Minimal YAML-ish frontmatter: a leading `---` block of `key: value` lines.
|
|
19
|
-
export function parseFrontmatter(raw: string): { meta: Record<string, string>; body: string } {
|
|
20
|
-
const lines = raw.split("\n");
|
|
21
|
-
if (lines[0]?.trim() !== "---") return { meta: {}, body: raw };
|
|
22
|
-
const meta: Record<string, string> = {};
|
|
23
|
-
let i = 1;
|
|
24
|
-
for (; i < lines.length; i++) {
|
|
25
|
-
if (lines[i].trim() === "---") {
|
|
26
|
-
i++;
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
const m = lines[i].match(/^([A-Za-z0-9_-]+)\s*:\s*(.*)$/);
|
|
30
|
-
if (m) meta[m[1].toLowerCase()] = m[2].trim();
|
|
31
|
-
}
|
|
32
|
-
return { meta, body: lines.slice(i).join("\n").replace(/^\n+/, "") };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function loadFromDir(dir: string, scope: "project" | "user"): CustomCommand[] {
|
|
36
|
-
if (!existsSync(dir)) return [];
|
|
37
|
-
const out: CustomCommand[] = [];
|
|
38
|
-
for (const rel of walkFiles(dir)) {
|
|
39
|
-
if (!rel.endsWith(".md")) continue;
|
|
40
|
-
const { meta, body } = parseFrontmatter(readFileSync(join(dir, rel), "utf8"));
|
|
41
|
-
out.push({
|
|
42
|
-
name: rel.replace(/\.md$/, "").split("/").join(":"),
|
|
43
|
-
description: meta.description ?? `custom ${scope} command`,
|
|
44
|
-
argumentHint: meta["argument-hint"],
|
|
45
|
-
allowedTools: meta["allowed-tools"]
|
|
46
|
-
?.split(",")
|
|
47
|
-
.map((s) => s.trim())
|
|
48
|
-
.filter(Boolean),
|
|
49
|
-
model: meta.model,
|
|
50
|
-
body: body.trim(),
|
|
51
|
-
scope,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
return out;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Load custom commands from user (~/.privateer) then project (./.privateer);
|
|
58
|
-
// a project command overrides a user command of the same name.
|
|
59
|
-
export function loadCustomCommands(cwd: string = process.cwd()): CustomCommand[] {
|
|
60
|
-
const byName = new Map<string, CustomCommand>();
|
|
61
|
-
for (const c of loadFromDir(globalPaths().commands, "user")) byName.set(c.name, c);
|
|
62
|
-
for (const c of loadFromDir(projectPaths(cwd).commands, "project")) byName.set(c.name, c);
|
|
63
|
-
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Expand a command body against its arguments: $1..$9 are positional words,
|
|
67
|
-
// $ARGUMENTS and $@ are the full argument string.
|
|
68
|
-
export function expandCommand(cmd: CustomCommand, argString: string): string {
|
|
69
|
-
const args = argString.trim();
|
|
70
|
-
const parts = args.length ? args.split(/\s+/) : [];
|
|
71
|
-
return cmd.body
|
|
72
|
-
.replace(/\$([1-9])/g, (_, d: string) => parts[Number(d) - 1] ?? "")
|
|
73
|
-
.replace(/\$ARGUMENTS\b/g, args)
|
|
74
|
-
.replace(/\$@/g, args);
|
|
75
|
-
}
|