@cjhyy/code-shell-core 0.6.0-rc.16 → 0.6.0-rc.18

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.
Files changed (64) hide show
  1. package/THIRD_PARTY_NOTICES.md +206 -0
  2. package/dist/automation/scheduler.d.ts +13 -7
  3. package/dist/automation/scheduler.js +116 -37
  4. package/dist/cc-orchestrator/agent-adapter.d.ts +2 -0
  5. package/dist/cc-orchestrator/agent-adapter.js +7 -1
  6. package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -0
  7. package/dist/cc-orchestrator/external-agent-driver.js +102 -51
  8. package/dist/cli/agent-server-stdio.js +2 -0
  9. package/dist/credentials/access.d.ts +56 -0
  10. package/dist/credentials/access.js +183 -0
  11. package/dist/credentials/index.d.ts +1 -0
  12. package/dist/credentials/index.js +1 -0
  13. package/dist/credentials/inject-credential-tool.js +5 -5
  14. package/dist/credentials/use-credential-tool.d.ts +8 -1
  15. package/dist/credentials/use-credential-tool.js +55 -45
  16. package/dist/engine/engine.d.ts +3 -0
  17. package/dist/engine/engine.js +40 -13
  18. package/dist/engine/image-policy.d.ts +6 -0
  19. package/dist/engine/image-policy.js +17 -6
  20. package/dist/engine/input-attachments.d.ts +13 -0
  21. package/dist/engine/input-attachments.js +255 -0
  22. package/dist/engine/model-facade.d.ts +5 -2
  23. package/dist/engine/model-facade.js +4 -4
  24. package/dist/engine/parse-task.d.ts +10 -0
  25. package/dist/engine/parse-task.js +5 -0
  26. package/dist/engine/streaming-tool-queue.d.ts +11 -7
  27. package/dist/engine/streaming-tool-queue.js +11 -7
  28. package/dist/engine/turn-loop.d.ts +4 -0
  29. package/dist/engine/turn-loop.js +106 -25
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +2 -2
  32. package/dist/logging/sanitize-messages.d.ts +10 -2
  33. package/dist/logging/sanitize-messages.js +21 -6
  34. package/dist/preset/index.js +10 -9
  35. package/dist/protocol/chat-session-manager.d.ts +1 -0
  36. package/dist/protocol/chat-session-manager.js +18 -2
  37. package/dist/protocol/chat-session.d.ts +3 -0
  38. package/dist/protocol/chat-session.js +1 -0
  39. package/dist/protocol/client.d.ts +9 -4
  40. package/dist/protocol/client.js +18 -1
  41. package/dist/protocol/server.d.ts +12 -0
  42. package/dist/protocol/server.js +116 -38
  43. package/dist/protocol/types.d.ts +37 -0
  44. package/dist/runtime/spawn-common.js +10 -0
  45. package/dist/tool-system/builtin/drive-claude-code.d.ts +1 -0
  46. package/dist/tool-system/builtin/drive-claude-code.js +137 -18
  47. package/dist/tool-system/builtin/index.d.ts +8 -3
  48. package/dist/tool-system/builtin/index.js +15 -13
  49. package/dist/tool-system/builtin/powershell.d.ts +5 -2
  50. package/dist/tool-system/builtin/powershell.js +11 -7
  51. package/dist/tool-system/builtin/read.js +114 -5
  52. package/dist/tool-system/builtin/view-image.js +9 -0
  53. package/dist/tool-system/executor.d.ts +1 -5
  54. package/dist/tool-system/executor.js +94 -115
  55. package/dist/tool-system/mcp-manager.d.ts +2 -0
  56. package/dist/tool-system/mcp-manager.js +23 -8
  57. package/dist/tool-system/path-policy.js +13 -0
  58. package/dist/tool-system/permission.d.ts +28 -7
  59. package/dist/tool-system/permission.js +130 -49
  60. package/dist/tool-system/registry.js +11 -4
  61. package/dist/tool-system/tool-result-redaction.d.ts +7 -0
  62. package/dist/tool-system/tool-result-redaction.js +48 -0
  63. package/dist/types.d.ts +23 -4
  64. package/package.json +4 -3
@@ -6,64 +6,115 @@ export function runWithLines(adapter, lines, exitCode) {
6
6
  const parsed = adapter.parseResult(lines);
7
7
  return { ...parsed, exitCode, lines };
8
8
  }
9
- /** Spawn ONE headless agent run, collect stream-json to exit, return result.
10
- * No time concept — a single turn. Honors AbortSignal (kills the child). */
11
- export function runAgentOnce(adapter, opts, signal) {
12
- return new Promise((resolve, reject) => {
13
- const args = adapter.buildArgs({
14
- prompt: opts.prompt,
15
- resumeSessionId: opts.resumeSessionId,
16
- permissionMode: opts.permissionMode ?? "default",
17
- cwd: opts.cwd,
18
- });
19
- // claude takes the prompt in argv (`-p <prompt>`) and wants stdin closed
20
- // (verified: avoids a 3s wait). codex `exec` reads the prompt from stdin
21
- // (argv ends with `-`), so adapters that set promptViaStdin get a piped
22
- // stdin we write the prompt to.
23
- const viaStdin = adapter.promptViaStdin === true;
24
- const child = spawn(opts.command, args, {
25
- cwd: opts.cwd,
9
+ const codexImageFlagCache = new Map();
10
+ export function detectCodexImageInput(command, cwd) {
11
+ const cached = codexImageFlagCache.get(command);
12
+ if (cached)
13
+ return cached;
14
+ const probe = new Promise((resolve) => {
15
+ const child = spawn(command, ["exec", "--help"], {
16
+ cwd,
26
17
  env: { ...process.env, PATH: pathWithCommonBins() },
27
- // NOT detached: this child is owned by the (long-lived) worker that reads
28
- // its stdout. Detaching orphaned it across a worker/app restart — the
29
- // reader promise then never resolved and the completion notification never
30
- // fired (the "后台任务没返回" bug). Bound to the worker, it lives or dies
31
- // with the process that's actually listening for its result.
32
- detached: false,
33
- stdio: [viaStdin ? "pipe" : "ignore", "pipe", "pipe"],
18
+ windowsHide: true,
19
+ stdio: ["ignore", "pipe", "pipe"],
34
20
  });
35
- if (viaStdin && child.stdin) {
36
- child.stdin.end(opts.prompt);
37
- }
38
- const lines = [];
39
- if (child.stdout) {
40
- const rl = createInterface({ input: child.stdout });
41
- rl.on("line", (line) => lines.push(line));
42
- }
43
- // Not detached → no own process group, so kill the child directly (a
44
- // negative-pid group kill would target the worker's group). claude has no
45
- // long-lived child tree of its own here, so a direct SIGTERM is sufficient.
46
- const onAbort = () => {
21
+ let out = "";
22
+ let settled = false;
23
+ const done = (value) => {
24
+ if (settled)
25
+ return;
26
+ settled = true;
27
+ clearTimeout(timer);
28
+ resolve(value);
29
+ };
30
+ const timer = setTimeout(() => {
47
31
  try {
48
32
  child.kill("SIGTERM");
49
33
  }
50
- catch { /* already gone */ }
51
- };
52
- signal?.addEventListener("abort", onAbort, { once: true });
53
- child.on("error", (err) => {
54
- signal?.removeEventListener("abort", onAbort);
55
- // A missing binary is the most common failure (user hasn't installed the
56
- // CLI, or GUI-launched Electron's PATH misses it). Turn the cryptic
57
- // "spawn codex ENOENT" into something actionable that names the command.
58
- if (err.code === "ENOENT") {
59
- reject(new Error(`未找到命令 "${opts.command}"。请先安装该 CLI 并确保它在 PATH 中(${adapter.kind === "codex" ? "Codex CLI" : "Claude Code CLI"})。`));
60
- return;
34
+ catch {
35
+ // already gone
61
36
  }
62
- reject(err);
37
+ done(false);
38
+ }, 2_000);
39
+ child.stdout?.on("data", (chunk) => {
40
+ out += chunk.toString("utf8");
63
41
  });
64
- child.on("exit", (code) => {
65
- signal?.removeEventListener("abort", onAbort);
66
- resolve(runWithLines(adapter, lines, code));
42
+ child.stderr?.on("data", (chunk) => {
43
+ out += chunk.toString("utf8");
67
44
  });
45
+ child.on("error", () => done(false));
46
+ child.on("exit", () => done(/(?:^|\s)-i(?:,|\s)|--image\b/.test(out)));
47
+ });
48
+ codexImageFlagCache.set(command, probe);
49
+ return probe;
50
+ }
51
+ /** Spawn ONE headless agent run, collect stream-json to exit, return result.
52
+ * No time concept — a single turn. Honors AbortSignal (kills the child). */
53
+ export function runAgentOnce(adapter, opts, signal) {
54
+ return new Promise((resolve, reject) => {
55
+ void (async () => {
56
+ const codexImageInputSupported = adapter.kind === "codex" && (opts.imagePaths?.length ?? 0) > 0
57
+ ? await detectCodexImageInput(opts.command, opts.cwd).catch(() => false)
58
+ : false;
59
+ const args = adapter.buildArgs({
60
+ prompt: opts.prompt,
61
+ resumeSessionId: opts.resumeSessionId,
62
+ permissionMode: opts.permissionMode ?? "default",
63
+ cwd: opts.cwd,
64
+ imagePaths: opts.imagePaths,
65
+ codexImageInputSupported,
66
+ });
67
+ // claude takes the prompt in argv (`-p <prompt>`) and wants stdin closed
68
+ // (verified: avoids a 3s wait). codex `exec` reads the prompt from stdin
69
+ // (argv ends with `-`), so adapters that set promptViaStdin get a piped
70
+ // stdin we write the prompt to.
71
+ const viaStdin = adapter.promptViaStdin === true;
72
+ const child = spawn(opts.command, args, {
73
+ cwd: opts.cwd,
74
+ env: { ...process.env, PATH: pathWithCommonBins() },
75
+ // NOT detached: this child is owned by the (long-lived) worker that reads
76
+ // its stdout. Detaching orphaned it across a worker/app restart — the
77
+ // reader promise then never resolved and the completion notification never
78
+ // fired (the "后台任务没返回" bug). Bound to the worker, it lives or dies
79
+ // with the process that's actually listening for its result.
80
+ detached: false,
81
+ stdio: [viaStdin ? "pipe" : "ignore", "pipe", "pipe"],
82
+ });
83
+ if (viaStdin && child.stdin) {
84
+ child.stdin.end(opts.prompt);
85
+ }
86
+ const lines = [];
87
+ if (child.stdout) {
88
+ const rl = createInterface({ input: child.stdout });
89
+ rl.on("line", (line) => lines.push(line));
90
+ }
91
+ // Not detached → no own process group, so kill the child directly (a
92
+ // negative-pid group kill would target the worker's group). claude has no
93
+ // long-lived child tree of its own here, so a direct SIGTERM is sufficient.
94
+ const onAbort = () => {
95
+ try {
96
+ child.kill("SIGTERM");
97
+ }
98
+ catch {
99
+ /* already gone */
100
+ }
101
+ };
102
+ signal?.addEventListener("abort", onAbort, { once: true });
103
+ child.on("error", (err) => {
104
+ signal?.removeEventListener("abort", onAbort);
105
+ // A missing binary is the most common failure (user hasn't installed the
106
+ // CLI, or GUI-launched Electron's PATH misses it). Turn the cryptic
107
+ // "spawn codex ENOENT" into something actionable that names the command.
108
+ if (err.code === "ENOENT") {
109
+ reject(new Error(`未找到命令 "${opts.command}"。请先安装该 CLI 并确保它在 PATH 中(${adapter.kind === "codex" ? "Codex CLI" : "Claude Code CLI"})。`));
110
+ return;
111
+ }
112
+ reject(err);
113
+ });
114
+ child.on("exit", (code) => {
115
+ signal?.removeEventListener("abort", onAbort);
116
+ resolve(runWithLines(adapter, lines, code));
117
+ });
118
+ })().catch(reject);
68
119
  });
69
120
  }
@@ -50,6 +50,7 @@ import { logger } from "../logging/logger.js";
50
50
  import { cronScheduler } from "../automation/scheduler.js";
51
51
  import { CronStore, defaultCronStorePath } from "../automation/store.js";
52
52
  import { resolveLLMConfigForTag } from "../engine/resolve-llm-config.js";
53
+ import { createIpcCredentialAccess, setDefaultCredentialAccess } from "../credentials/access.js";
53
54
  /**
54
55
  * Resolve per-session agent config: protocol slice overrides win, else fall
55
56
  * back to disk settings.agent.*. Fixes the bug where settings.agent.* never
@@ -274,6 +275,7 @@ cronScheduler.setExecutionEnabled(false);
274
275
  cronScheduler.loadJobs();
275
276
  // ─── Step 5: AgentServer over stdio ──────────────────────────────
276
277
  const stdioTransport = new StdioTransport(process.stdin, process.stdout);
278
+ setDefaultCredentialAccess(createIpcCredentialAccess(stdioTransport));
277
279
  // Cron jobs are persisted by this worker but only main arms/executes their
278
280
  // timers (this worker keeps setExecutionEnabled(false) above). When an AI tool
279
281
  // creates/deletes a cron job here, notify main over stdio so it reloads the
@@ -0,0 +1,56 @@
1
+ import type { Credential, CredentialType } from "./types.js";
2
+ import type { SettingsScope } from "../settings/manager.js";
3
+ import type { Transport } from "../protocol/transport.js";
4
+ export type CredentialAccessScope = "full" | "project";
5
+ export interface CredentialMetadata {
6
+ id: string;
7
+ type: CredentialType;
8
+ label: string;
9
+ autoUseByAI?: boolean;
10
+ autoInjectByAI?: boolean;
11
+ exposeAsEnv?: string;
12
+ meta?: Credential["meta"];
13
+ hasSecret: boolean;
14
+ secretHint?: string;
15
+ }
16
+ export interface CredentialAccess {
17
+ listMasked(cwd: string | undefined, scope: CredentialAccessScope): CredentialMetadata[];
18
+ resolveMeta(cwd: string | undefined, id: string, scope: CredentialAccessScope): CredentialMetadata | undefined;
19
+ envExposures(cwd: string | undefined, scope: CredentialAccessScope): Record<string, string>;
20
+ resolveValue?(req: {
21
+ cwd?: string;
22
+ id: string;
23
+ scope: CredentialAccessScope;
24
+ purpose: "use" | "mcp";
25
+ }): Promise<string>;
26
+ materializeCookie?(req: {
27
+ cwd?: string;
28
+ id: string;
29
+ scope: CredentialAccessScope;
30
+ }): Promise<{
31
+ cookiesFile: string;
32
+ count: number;
33
+ }>;
34
+ }
35
+ export interface CredentialSnapshotEntry {
36
+ cwd?: string;
37
+ full: CredentialMetadata[];
38
+ project: CredentialMetadata[];
39
+ envFull: Record<string, string>;
40
+ envProject: Record<string, string>;
41
+ }
42
+ export interface CredentialSnapshot {
43
+ revision: number;
44
+ entries: CredentialSnapshotEntry[];
45
+ }
46
+ export declare function setDefaultCredentialAccess(access: CredentialAccess | null | undefined): void;
47
+ export declare function getCredentialAccess(): CredentialAccess;
48
+ export declare function createIpcCredentialAccess(transport: Pick<Transport, "send" | "onMessage">): CredentialAccess;
49
+ export declare function credentialAccessScope(scope: SettingsScope | undefined): CredentialAccessScope;
50
+ export declare function isCredentialSecretAvailable(secret: string | undefined): secret is string;
51
+ export declare const localCredentialAccess: CredentialAccess;
52
+ export declare function materializeCookieSecret(credentialId: string, secret: string): {
53
+ cookiesFile: string;
54
+ count: number;
55
+ };
56
+ export declare function sweepStaleCredentialCookieFiles(now?: number): void;
@@ -0,0 +1,183 @@
1
+ import { existsSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
2
+ import { randomUUID } from "node:crypto";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { CredentialStore } from "./store.js";
6
+ import { formatNetscapeCookies, parseCookieJar } from "./cookie-jar.js";
7
+ const COOKIE_FILE_PREFIX = "codeshell-cred-cookie-";
8
+ const COOKIE_FILE_MAX_AGE_MS = 30 * 60 * 1000;
9
+ let defaultCredentialAccess = null;
10
+ export function setDefaultCredentialAccess(access) {
11
+ defaultCredentialAccess = access ?? null;
12
+ }
13
+ export function getCredentialAccess() {
14
+ return defaultCredentialAccess ?? localCredentialAccess;
15
+ }
16
+ export function createIpcCredentialAccess(transport) {
17
+ let snapshot = { revision: 0, entries: [] };
18
+ let nextId = 1;
19
+ const pending = new Map();
20
+ transport.onMessage((msg) => {
21
+ if ("method" in msg && msg.method === "desktop/credentialSnapshot") {
22
+ const params = msg.params;
23
+ if (params && typeof params.revision === "number" && Array.isArray(params.entries)) {
24
+ snapshot = {
25
+ revision: params.revision,
26
+ entries: params.entries,
27
+ };
28
+ }
29
+ return;
30
+ }
31
+ if (!("id" in msg) || "method" in msg)
32
+ return;
33
+ const id = String(msg.id);
34
+ const waiter = pending.get(id);
35
+ if (!waiter)
36
+ return;
37
+ pending.delete(id);
38
+ clearTimeout(waiter.timer);
39
+ if ("error" in msg && msg.error) {
40
+ waiter.reject(new Error(msg.error.message));
41
+ }
42
+ else {
43
+ waiter.resolve(msg.result);
44
+ }
45
+ });
46
+ const request = (method, params) => {
47
+ const id = `cred-${nextId++}`;
48
+ return new Promise((resolve, reject) => {
49
+ const timer = setTimeout(() => {
50
+ pending.delete(id);
51
+ reject(new Error(`${method} timed out`));
52
+ }, 30_000);
53
+ pending.set(id, { resolve, reject, timer });
54
+ transport.send({ jsonrpc: "2.0", id, method, params });
55
+ });
56
+ };
57
+ const entryFor = (cwd) => {
58
+ const key = cwd ?? "";
59
+ return snapshot.entries.find((entry) => (entry.cwd ?? "") === key);
60
+ };
61
+ return {
62
+ listMasked(cwd, scope) {
63
+ const entry = entryFor(cwd);
64
+ if (!entry)
65
+ return [];
66
+ return scope === "project" ? cloneMetadata(entry.project) : cloneMetadata(entry.full);
67
+ },
68
+ resolveMeta(cwd, id, scope) {
69
+ const entry = entryFor(cwd);
70
+ if (!entry)
71
+ return undefined;
72
+ const list = scope === "project" ? entry.project : entry.full;
73
+ return cloneMetadata(list).find((cred) => cred.id === id);
74
+ },
75
+ envExposures(cwd, scope) {
76
+ const entry = entryFor(cwd);
77
+ if (!entry)
78
+ return {};
79
+ return { ...(scope === "project" ? entry.envProject : entry.envFull) };
80
+ },
81
+ async resolveValue(req) {
82
+ const result = (await request("desktop/credentialResolve", req));
83
+ if (typeof result?.value !== "string")
84
+ throw new Error(`credential "${req.id}" is unavailable`);
85
+ return result.value;
86
+ },
87
+ async materializeCookie(req) {
88
+ const result = (await request("desktop/credentialMaterializeCookie", req));
89
+ if (typeof result?.cookiesFile !== "string" || typeof result.count !== "number") {
90
+ throw new Error(`cookie credential "${req.id}" is unavailable`);
91
+ }
92
+ return { cookiesFile: result.cookiesFile, count: result.count };
93
+ },
94
+ };
95
+ }
96
+ export function credentialAccessScope(scope) {
97
+ return scope === "full" || scope === undefined ? "full" : "project";
98
+ }
99
+ export function isCredentialSecretAvailable(secret) {
100
+ return typeof secret === "string" && secret.length > 0 && !secret.startsWith("enc:");
101
+ }
102
+ function toMetadata(cred) {
103
+ const secret = cred.secret;
104
+ const available = isCredentialSecretAvailable(secret);
105
+ const { secret: _secret, ...rest } = cred;
106
+ return {
107
+ ...rest,
108
+ hasSecret: available,
109
+ secretHint: available ? (secret.length > 4 ? `****${secret.slice(-4)}` : "****") : undefined,
110
+ };
111
+ }
112
+ function cloneMetadata(list) {
113
+ return list.map((cred) => ({ ...cred, meta: cred.meta ? { ...cred.meta } : undefined }));
114
+ }
115
+ function storeFor(cwd) {
116
+ return new CredentialStore(cwd);
117
+ }
118
+ export const localCredentialAccess = {
119
+ listMasked(cwd, scope) {
120
+ return storeFor(cwd).list(scope).map(toMetadata);
121
+ },
122
+ resolveMeta(cwd, id, scope) {
123
+ const cred = storeFor(cwd).resolve(id, scope);
124
+ return cred ? toMetadata(cred) : undefined;
125
+ },
126
+ envExposures(cwd, scope) {
127
+ const out = {};
128
+ const creds = storeFor(cwd).list(scope);
129
+ for (const cred of creds) {
130
+ const name = cred.exposeAsEnv?.trim();
131
+ if (name && isCredentialSecretAvailable(cred.secret))
132
+ out[name] = cred.secret;
133
+ }
134
+ return out;
135
+ },
136
+ async resolveValue(req) {
137
+ const cred = storeFor(req.cwd).resolve(req.id, req.scope);
138
+ if (!cred || !isCredentialSecretAvailable(cred.secret)) {
139
+ throw new Error(`credential "${req.id}" is unavailable`);
140
+ }
141
+ return cred.secret;
142
+ },
143
+ async materializeCookie(req) {
144
+ const cred = storeFor(req.cwd).resolve(req.id, req.scope);
145
+ if (!cred || cred.type !== "cookie" || !isCredentialSecretAvailable(cred.secret)) {
146
+ throw new Error(`cookie credential "${req.id}" is unavailable`);
147
+ }
148
+ return materializeCookieSecret(cred.id, cred.secret);
149
+ },
150
+ };
151
+ export function materializeCookieSecret(credentialId, secret) {
152
+ const jar = parseCookieJar(secret);
153
+ if (jar.length === 0)
154
+ throw new Error("cookie jar is empty or invalid");
155
+ const file = join(tmpdir(), `${COOKIE_FILE_PREFIX}${safeFileName(credentialId)}-${process.pid}-${randomUUID()}.txt`);
156
+ writeFileSync(file, formatNetscapeCookies(jar), { mode: 0o600 });
157
+ return { cookiesFile: file, count: jar.length };
158
+ }
159
+ export function sweepStaleCredentialCookieFiles(now = Date.now()) {
160
+ const dir = tmpdir();
161
+ try {
162
+ if (!existsSync(dir))
163
+ return;
164
+ for (const f of readdirSync(dir)) {
165
+ if (!f.startsWith(COOKIE_FILE_PREFIX))
166
+ continue;
167
+ const p = join(dir, f);
168
+ try {
169
+ if (now - statSync(p).mtimeMs > COOKIE_FILE_MAX_AGE_MS)
170
+ rmSync(p, { force: true });
171
+ }
172
+ catch {
173
+ /* skip */
174
+ }
175
+ }
176
+ }
177
+ catch {
178
+ /* best-effort */
179
+ }
180
+ }
181
+ function safeFileName(s) {
182
+ return s.replace(/[^a-zA-Z0-9_.-]/g, "_");
183
+ }
@@ -1,6 +1,7 @@
1
1
  export { CredentialStore } from "./store.js";
2
2
  export type { CredentialScope, MaskedCredential } from "./store.js";
3
3
  export { type EncryptionCipher, PlaintextCipher, setDefaultCredentialCipher, getDefaultCredentialCipher, } from "./cipher.js";
4
+ export { getCredentialAccess, setDefaultCredentialAccess, createIpcCredentialAccess, localCredentialAccess, credentialAccessScope, isCredentialSecretAvailable, materializeCookieSecret, type CredentialAccess, type CredentialAccessScope, type CredentialMetadata, type CredentialSnapshot, type CredentialSnapshotEntry, } from "./access.js";
4
5
  export type { Credential, CredentialType, CredentialStoreFile } from "./types.js";
5
6
  export { formatNetscapeCookies, parseCookieJar, type CookieLike } from "./cookie-jar.js";
6
7
  export { useCredentialToolDef, useCredentialToolDefFor, useCredentialTool, sweepStaleCredentialCookies, } from "./use-credential-tool.js";
@@ -1,5 +1,6 @@
1
1
  export { CredentialStore } from "./store.js";
2
2
  export { PlaintextCipher, setDefaultCredentialCipher, getDefaultCredentialCipher, } from "./cipher.js";
3
+ export { getCredentialAccess, setDefaultCredentialAccess, createIpcCredentialAccess, localCredentialAccess, credentialAccessScope, isCredentialSecretAvailable, materializeCookieSecret, } from "./access.js";
3
4
  export { formatNetscapeCookies, parseCookieJar } from "./cookie-jar.js";
4
5
  export { useCredentialToolDef, useCredentialToolDefFor, useCredentialTool, sweepStaleCredentialCookies, } from "./use-credential-tool.js";
5
6
  export { credentialUseGate } from "./use-gate.js";
@@ -10,9 +10,9 @@
10
10
  * 跨进程:实际的 restoreCookiesToBrowser 在 desktop main(core 够不到 Electron
11
11
  * session),经 `ctx.injectCredentialToBrowser` 回调(宿主注入,镜像 askUser)触发。
12
12
  */
13
- import { CredentialStore } from "./store.js";
14
13
  import { credentialUseGate, } from "./use-gate.js";
15
14
  import { SettingsManager } from "../settings/manager.js";
15
+ import { credentialAccessScope, getCredentialAccess } from "./access.js";
16
16
  const TOOL_NAME = "InjectCredential";
17
17
  const BASE_DESCRIPTION = "Inject a stored COOKIE credential into the built-in browser to restore its " +
18
18
  "login state, so you can then drive the page as that logged-in account with the " +
@@ -65,7 +65,7 @@ function sessionAllowFor(ctx) {
65
65
  // CredentialStore only distinguishes full user+project from project-only.
66
66
  // Isolated engines must be at least as restrictive as project-scoped engines.
67
67
  function credentialScope(scope) {
68
- return scope === "full" || scope === undefined ? "full" : "project";
68
+ return credentialAccessScope(scope);
69
69
  }
70
70
  function readAutoApprove(cwd, scope) {
71
71
  try {
@@ -79,8 +79,8 @@ function readAutoApprove(cwd, scope) {
79
79
  /** 该工具仅在有 cookie 凭证 且 宿主接了注入回调时才可见(BUILTIN_TOOL_GUARDS)。 */
80
80
  export function isInjectCredentialAvailable(cwd, settingsScope) {
81
81
  try {
82
- return new CredentialStore(cwd)
83
- .listMasked(credentialScope(settingsScope))
82
+ return getCredentialAccess()
83
+ .listMasked(cwd, credentialScope(settingsScope))
84
84
  .some((c) => c.type === "cookie");
85
85
  }
86
86
  catch {
@@ -104,7 +104,7 @@ export async function injectCredentialTool(args, ctx) {
104
104
  error: "当前环境无内置浏览器(headless/无面板),无法注入。请改用 UseCredential 取 cookie 走 HTTP 请求。",
105
105
  });
106
106
  }
107
- const cred = new CredentialStore(cwd).resolve(id, scope);
107
+ const cred = getCredentialAccess().resolveMeta(cwd, id, scope);
108
108
  if (!cred) {
109
109
  return json({
110
110
  kind: "error",
@@ -7,7 +7,8 @@
7
7
  * - token/link → `{ kind: "value", value }`
8
8
  * - cookie → 就地写临时 cookies.txt(0600),返回 `{ kind: "cookie", cookiesFile, count }`
9
9
  *
10
- * 取值全部 core 直读 CredentialStore(cookie 值第二期已进库),无跨进程。
10
+ * desktop 下通过 host credential access IPC 按需解析 secret;headless/SDK
11
+ * 仍可使用本地 CredentialStore。
11
12
  * 集中在 core/src/credentials/ 下,只经 ToolDefinition 注册 + ToolContext.askUser 耦合 core,
12
13
  * 满足设计稿 §1「可整块外移」约束。
13
14
  */
@@ -25,6 +26,12 @@ export declare function useCredentialToolDefFor(cwd: string): ToolDefinition;
25
26
  */
26
27
  export declare function sweepStaleCredentialCookies(now?: number): void;
27
28
  export declare function useCredentialTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
29
+ export declare function useCredentialBuiltinTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string | {
30
+ result: string;
31
+ sensitive: true;
32
+ displayResult: string;
33
+ transcriptResult: string;
34
+ }>;
28
35
  /** 测试钩子:清空会话 allow 集(避免跨用例污染)。 */
29
36
  export declare function __resetCredentialSessionAllowForTests(): void;
30
37
  export declare function clearCredentialSessionAllow(sessionId: string): void;