@sna-sdk/core 0.9.9 → 0.9.11
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/dist/core/providers/claude-code.d.ts +32 -1
- package/dist/core/providers/claude-code.js +72 -22
- package/dist/electron/index.cjs +2843 -13
- package/dist/electron/index.d.ts +33 -1
- package/dist/electron/index.js +86 -1
- package/dist/node/index.cjs +732 -11
- package/dist/node/index.d.ts +4 -0
- package/dist/server/standalone.js +67 -21
- package/package.json +1 -1
|
@@ -1,9 +1,40 @@
|
|
|
1
1
|
import { AgentProvider, SpawnOptions, AgentProcess } from './types.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Parse `command -v claude` output to extract the executable path.
|
|
5
|
+
* Handles: direct paths, alias with/without quotes, bare command names.
|
|
6
|
+
* @internal Exported for testing only.
|
|
7
|
+
*/
|
|
8
|
+
declare function parseCommandVOutput(raw: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Validate a Claude CLI path by running `<path> --version`.
|
|
11
|
+
* Adds the binary's directory to PATH so shebang resolution works (nvm/fnm).
|
|
12
|
+
*/
|
|
13
|
+
declare function validateClaudePath(claudePath: string): {
|
|
14
|
+
ok: boolean;
|
|
15
|
+
version?: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Save a validated Claude path to cache for faster startup next time.
|
|
19
|
+
*/
|
|
20
|
+
declare function cacheClaudePath(claudePath: string, cacheDir?: string): void;
|
|
21
|
+
interface ResolveResult {
|
|
22
|
+
path: string;
|
|
23
|
+
version?: string;
|
|
24
|
+
source: "env" | "cache" | "static" | "shell" | "fallback";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve Claude CLI path. Tries: env override → cache → static paths → shell detection.
|
|
28
|
+
* All candidates are validated with `--version` before returning.
|
|
29
|
+
* Consumer apps should call this and handle the `fallback` source (= not found).
|
|
30
|
+
*/
|
|
31
|
+
declare function resolveClaudeCli(opts?: {
|
|
32
|
+
cacheDir?: string;
|
|
33
|
+
}): ResolveResult;
|
|
3
34
|
declare class ClaudeCodeProvider implements AgentProvider {
|
|
4
35
|
readonly name = "claude-code";
|
|
5
36
|
isAvailable(): Promise<boolean>;
|
|
6
37
|
spawn(options: SpawnOptions): AgentProcess;
|
|
7
38
|
}
|
|
8
39
|
|
|
9
|
-
export { ClaudeCodeProvider };
|
|
40
|
+
export { ClaudeCodeProvider, type ResolveResult, cacheClaudePath, parseCommandVOutput, resolveClaudeCli, validateClaudePath };
|
|
@@ -6,38 +6,80 @@ import { fileURLToPath } from "url";
|
|
|
6
6
|
import { writeHistoryJsonl, buildRecalledConversation } from "./cc-history-adapter.js";
|
|
7
7
|
import { logger } from "../../lib/logger.js";
|
|
8
8
|
const SHELL = process.env.SHELL || "/bin/zsh";
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
function parseCommandVOutput(raw) {
|
|
10
|
+
const trimmed = raw.trim();
|
|
11
|
+
if (!trimmed) return "claude";
|
|
12
|
+
const aliasMatch = trimmed.match(/=\s*['"]?([^'"]+?)['"]?\s*$/);
|
|
13
|
+
if (aliasMatch) return aliasMatch[1];
|
|
14
|
+
const pathMatch = trimmed.match(/^(\/\S+)/m);
|
|
15
|
+
if (pathMatch) return pathMatch[1];
|
|
16
|
+
return trimmed;
|
|
17
|
+
}
|
|
18
|
+
function validateClaudePath(claudePath) {
|
|
19
|
+
try {
|
|
20
|
+
const claudeDir = path.dirname(claudePath);
|
|
21
|
+
const env = { ...process.env, PATH: `${claudeDir}:${process.env.PATH ?? ""}` };
|
|
22
|
+
const out = execSync(`"${claudePath}" --version`, { encoding: "utf8", stdio: "pipe", timeout: 1e4, env }).trim();
|
|
23
|
+
return { ok: true, version: out.split("\n")[0].slice(0, 30) };
|
|
24
|
+
} catch {
|
|
25
|
+
return { ok: false };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function cacheClaudePath(claudePath, cacheDir) {
|
|
29
|
+
const dir = cacheDir ?? path.join(process.cwd(), ".sna");
|
|
30
|
+
try {
|
|
31
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
32
|
+
fs.writeFileSync(path.join(dir, "claude-path"), claudePath);
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function resolveClaudeCli(opts) {
|
|
37
|
+
const cacheDir = opts?.cacheDir;
|
|
38
|
+
if (process.env.SNA_CLAUDE_COMMAND) {
|
|
39
|
+
const v = validateClaudePath(process.env.SNA_CLAUDE_COMMAND);
|
|
40
|
+
return { path: process.env.SNA_CLAUDE_COMMAND, version: v.version, source: "env" };
|
|
41
|
+
}
|
|
42
|
+
const cacheFile = cacheDir ? path.join(cacheDir, "claude-path") : path.join(process.cwd(), ".sna/claude-path");
|
|
43
|
+
try {
|
|
44
|
+
const cached = fs.readFileSync(cacheFile, "utf8").trim();
|
|
45
|
+
if (cached) {
|
|
46
|
+
const v = validateClaudePath(cached);
|
|
47
|
+
if (v.ok) return { path: cached, version: v.version, source: "cache" };
|
|
20
48
|
}
|
|
49
|
+
} catch {
|
|
21
50
|
}
|
|
22
|
-
|
|
51
|
+
const staticPaths = [
|
|
23
52
|
"/opt/homebrew/bin/claude",
|
|
24
53
|
"/usr/local/bin/claude",
|
|
25
54
|
`${process.env.HOME}/.local/bin/claude`,
|
|
26
|
-
`${process.env.HOME}/.claude/bin/claude
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
55
|
+
`${process.env.HOME}/.claude/bin/claude`,
|
|
56
|
+
`${process.env.HOME}/.volta/bin/claude`
|
|
57
|
+
];
|
|
58
|
+
for (const p of staticPaths) {
|
|
59
|
+
const v = validateClaudePath(p);
|
|
60
|
+
if (v.ok) {
|
|
61
|
+
cacheClaudePath(p, cacheDir);
|
|
62
|
+
return { path: p, version: v.version, source: "static" };
|
|
32
63
|
}
|
|
33
64
|
}
|
|
34
65
|
try {
|
|
35
66
|
const raw = execSync(`${SHELL} -i -l -c "command -v claude" 2>/dev/null`, { encoding: "utf8", timeout: 5e3 }).trim();
|
|
36
|
-
const
|
|
37
|
-
|
|
67
|
+
const resolved = parseCommandVOutput(raw);
|
|
68
|
+
if (resolved && resolved !== "claude") {
|
|
69
|
+
const v = validateClaudePath(resolved);
|
|
70
|
+
if (v.ok) {
|
|
71
|
+
cacheClaudePath(resolved, cacheDir);
|
|
72
|
+
return { path: resolved, version: v.version, source: "shell" };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
38
75
|
} catch {
|
|
39
|
-
return "claude";
|
|
40
76
|
}
|
|
77
|
+
return { path: "claude", source: "fallback" };
|
|
78
|
+
}
|
|
79
|
+
function resolveClaudePath(cwd) {
|
|
80
|
+
const result = resolveClaudeCli({ cacheDir: path.join(cwd, ".sna") });
|
|
81
|
+
logger.log("agent", `claude path: ${result.source}=${result.path}${result.version ? ` (${result.version})` : ""}`);
|
|
82
|
+
return result.path;
|
|
41
83
|
}
|
|
42
84
|
const _ClaudeCodeProcess = class _ClaudeCodeProcess {
|
|
43
85
|
constructor(proc, options) {
|
|
@@ -473,6 +515,10 @@ class ClaudeCodeProvider {
|
|
|
473
515
|
delete cleanEnv.CLAUDE_CODE_ENTRYPOINT;
|
|
474
516
|
delete cleanEnv.CLAUDE_CODE_SESSION_ACCESS_TOKEN;
|
|
475
517
|
delete cleanEnv.CLAUDE_CODE_OAUTH_TOKEN;
|
|
518
|
+
const claudeDir = path.dirname(claudePath);
|
|
519
|
+
if (claudeDir && claudeDir !== ".") {
|
|
520
|
+
cleanEnv.PATH = `${claudeDir}:${cleanEnv.PATH ?? ""}`;
|
|
521
|
+
}
|
|
476
522
|
const proc = spawn(claudePath, [...claudePrefix, ...args], {
|
|
477
523
|
cwd: options.cwd,
|
|
478
524
|
env: cleanEnv,
|
|
@@ -483,5 +529,9 @@ class ClaudeCodeProvider {
|
|
|
483
529
|
}
|
|
484
530
|
}
|
|
485
531
|
export {
|
|
486
|
-
ClaudeCodeProvider
|
|
532
|
+
ClaudeCodeProvider,
|
|
533
|
+
cacheClaudePath,
|
|
534
|
+
parseCommandVOutput,
|
|
535
|
+
resolveClaudeCli,
|
|
536
|
+
validateClaudePath
|
|
487
537
|
};
|