@a1hvdy/cc-openclaw 0.27.11 → 0.27.13
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.
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolveChatCwd — working directory for an openai-compat (Telegram chat) session.
|
|
3
|
+
*
|
|
4
|
+
* Default: a neutral per-session tmpdir, so the claude CLI does NOT load
|
|
5
|
+
* CLAUDE.md / git state / project context (keeps chat turns cheap and clean —
|
|
6
|
+
* this is the deliberate v0.x choice).
|
|
7
|
+
*
|
|
8
|
+
* Opt-in (v0.27.12): set CC_OPENCLAW_CHAT_CWD to an existing directory and chat
|
|
9
|
+
* sessions run there — the CLI then loads that project's CLAUDE.md + git context
|
|
10
|
+
* like the terminal CLI does, and the CLI's "cwd reset" lands on the project dir
|
|
11
|
+
* instead of a tmpdir. This closes the chat-vs-terminal project-context parity
|
|
12
|
+
* gap as an explicit, per-deployment opt-in, so the default stays zero-token-
|
|
13
|
+
* overhead. A blank/unset/nonexistent value falls back to the tmpdir, so a
|
|
14
|
+
* misconfigured path can never break session creation.
|
|
15
|
+
*
|
|
16
|
+
* Pure (deps injectable) for unit-testing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveChatCwd(sessionName: string, opts?: {
|
|
19
|
+
cwdOverride?: string;
|
|
20
|
+
dirExists?: (p: string) => boolean;
|
|
21
|
+
tmpDir?: string;
|
|
22
|
+
}): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolveChatCwd — working directory for an openai-compat (Telegram chat) session.
|
|
3
|
+
*
|
|
4
|
+
* Default: a neutral per-session tmpdir, so the claude CLI does NOT load
|
|
5
|
+
* CLAUDE.md / git state / project context (keeps chat turns cheap and clean —
|
|
6
|
+
* this is the deliberate v0.x choice).
|
|
7
|
+
*
|
|
8
|
+
* Opt-in (v0.27.12): set CC_OPENCLAW_CHAT_CWD to an existing directory and chat
|
|
9
|
+
* sessions run there — the CLI then loads that project's CLAUDE.md + git context
|
|
10
|
+
* like the terminal CLI does, and the CLI's "cwd reset" lands on the project dir
|
|
11
|
+
* instead of a tmpdir. This closes the chat-vs-terminal project-context parity
|
|
12
|
+
* gap as an explicit, per-deployment opt-in, so the default stays zero-token-
|
|
13
|
+
* overhead. A blank/unset/nonexistent value falls back to the tmpdir, so a
|
|
14
|
+
* misconfigured path can never break session creation.
|
|
15
|
+
*
|
|
16
|
+
* Pure (deps injectable) for unit-testing.
|
|
17
|
+
*/
|
|
18
|
+
import * as os from 'node:os';
|
|
19
|
+
import * as path from 'node:path';
|
|
20
|
+
import * as fs from 'node:fs';
|
|
21
|
+
export function resolveChatCwd(sessionName, opts = {}) {
|
|
22
|
+
const override = (opts.cwdOverride ?? process.env.CC_OPENCLAW_CHAT_CWD ?? '').trim();
|
|
23
|
+
const dirExists = opts.dirExists ??
|
|
24
|
+
((p) => {
|
|
25
|
+
try {
|
|
26
|
+
return fs.statSync(p).isDirectory();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
if (override && dirExists(override))
|
|
33
|
+
return override;
|
|
34
|
+
const base = opts.tmpDir ?? os.tmpdir();
|
|
35
|
+
return path.join(base, `openclaw-compat-${sessionName}`);
|
|
36
|
+
}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import * as fs from 'node:fs';
|
|
9
9
|
import * as path from 'node:path';
|
|
10
|
-
import * as os from 'node:os';
|
|
11
10
|
import { randomUUID } from 'node:crypto';
|
|
12
11
|
import { resolveEngineAndModel } from '../models.js';
|
|
13
12
|
import { OPENAI_COMPAT_DEFAULT_MODEL, OPENAI_COMPAT_AUTO_COMPACT_THRESHOLD, RESUME_FRESHNESS_MS, } from '../constants.js';
|
|
@@ -30,6 +29,7 @@ import { getTtsAutoMode } from '../lib/config.js';
|
|
|
30
29
|
// `TTS_RULE` extracted to `./tts-rule.ts` 2026-05-13 — pure-string constant.
|
|
31
30
|
import { TTS_RULE } from './tts-rule.js';
|
|
32
31
|
import { AUTONOMY_RULE, withAskUserSuppressed } from './autonomy-rule.js';
|
|
32
|
+
import { resolveChatCwd } from './chat-cwd.js';
|
|
33
33
|
import { extractUserMessage, } from './message-extractor.js';
|
|
34
34
|
import { handleNonStreaming } from './non-streaming-handler.js';
|
|
35
35
|
import { handleStreaming } from './streaming-handler.js';
|
|
@@ -258,10 +258,12 @@ export async function handleChatCompletion(manager, body, headers, res) {
|
|
|
258
258
|
// Create session if needed
|
|
259
259
|
const needsCreate = !sessionExists || extracted.isNewConversation;
|
|
260
260
|
if (needsCreate) {
|
|
261
|
-
// OpenAI-compat sessions are API proxies, not coding sessions.
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
|
|
261
|
+
// OpenAI-compat sessions are API proxies, not coding sessions. By default
|
|
262
|
+
// use a neutral empty temp dir so the CLI doesn't load CLAUDE.md, git state,
|
|
263
|
+
// or project context from wherever `serve` was started. v0.27.12: opt into a
|
|
264
|
+
// real project dir via CC_OPENCLAW_CHAT_CWD to load project context like the
|
|
265
|
+
// terminal CLI (see resolveChatCwd). Default unchanged = zero token overhead.
|
|
266
|
+
const sessionCwd = resolveChatCwd(sessionName);
|
|
265
267
|
if (!fs.existsSync(sessionCwd))
|
|
266
268
|
fs.mkdirSync(sessionCwd, { recursive: true });
|
|
267
269
|
const sessionConfig = {
|
|
@@ -15,6 +15,17 @@ interface SendOptions {
|
|
|
15
15
|
onEvent?: (event: StreamEvent) => void;
|
|
16
16
|
onChunk?: (chunk: string) => void;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Decide whether a freshly-constructed SessionManager should reap orphaned PIDs.
|
|
20
|
+
*
|
|
21
|
+
* The orphan reaper reads the SHARED ~/.openclaw/session-pids.json and SIGKILLs
|
|
22
|
+
* any live CLI process listed there. That is correct at gateway boot, but lethal
|
|
23
|
+
* under a test runner: a test that does `new SessionManager()` would read the
|
|
24
|
+
* LIVE gateway's pid file and kill its prewarm/session subprocesses. So skip
|
|
25
|
+
* reaping when running under vitest / NODE_ENV=test. CC_OPENCLAW_ORPHAN_REAP
|
|
26
|
+
* forces it: '1' = always reap (even in test), '0' = never reap. Pure for testing.
|
|
27
|
+
*/
|
|
28
|
+
export declare function shouldReapOrphans(env?: NodeJS.ProcessEnv): boolean;
|
|
18
29
|
export declare class SessionManager {
|
|
19
30
|
private sessions;
|
|
20
31
|
private _pendingSessions;
|
|
@@ -60,6 +60,25 @@ import { resolveTurnTimeoutMs, } from '../lib/env-overrides.js';
|
|
|
60
60
|
import * as trajectory from '../lib/trajectory.js';
|
|
61
61
|
import { getGatewayUrl, getGatewayKey, getAnthropicApiKey, getOpenaiApiKey, getGeminiApiKey, getGeminiBin, getCodexBin, getCursorBin, } from '../lib/config.js';
|
|
62
62
|
// ─── SessionManager ──────────────────────────────────────────────────────────
|
|
63
|
+
/**
|
|
64
|
+
* Decide whether a freshly-constructed SessionManager should reap orphaned PIDs.
|
|
65
|
+
*
|
|
66
|
+
* The orphan reaper reads the SHARED ~/.openclaw/session-pids.json and SIGKILLs
|
|
67
|
+
* any live CLI process listed there. That is correct at gateway boot, but lethal
|
|
68
|
+
* under a test runner: a test that does `new SessionManager()` would read the
|
|
69
|
+
* LIVE gateway's pid file and kill its prewarm/session subprocesses. So skip
|
|
70
|
+
* reaping when running under vitest / NODE_ENV=test. CC_OPENCLAW_ORPHAN_REAP
|
|
71
|
+
* forces it: '1' = always reap (even in test), '0' = never reap. Pure for testing.
|
|
72
|
+
*/
|
|
73
|
+
export function shouldReapOrphans(env = process.env) {
|
|
74
|
+
const flag = env.CC_OPENCLAW_ORPHAN_REAP;
|
|
75
|
+
if (flag === '1')
|
|
76
|
+
return true;
|
|
77
|
+
if (flag === '0')
|
|
78
|
+
return false;
|
|
79
|
+
const inTest = env.VITEST === 'true' || env.NODE_ENV === 'test';
|
|
80
|
+
return !inTest;
|
|
81
|
+
}
|
|
63
82
|
export class SessionManager {
|
|
64
83
|
sessions = new Map();
|
|
65
84
|
_pendingSessions = new Map();
|
|
@@ -105,8 +124,11 @@ export class SessionManager {
|
|
|
105
124
|
}
|
|
106
125
|
// Load persisted session registry from disk
|
|
107
126
|
this.persistedSessions = loadPersistedSessions();
|
|
108
|
-
// Clean up orphaned child processes from a previous unclean exit
|
|
109
|
-
|
|
127
|
+
// Clean up orphaned child processes from a previous unclean exit.
|
|
128
|
+
// Gated so a test constructing SessionManager can't kill the live gateway's
|
|
129
|
+
// subprocesses via the shared session-pids.json (see shouldReapOrphans).
|
|
130
|
+
if (shouldReapOrphans())
|
|
131
|
+
this._cleanupOrphanedPids();
|
|
110
132
|
// Debounced async writer — at most one write per 5 seconds on hot paths
|
|
111
133
|
this._debouncedSave = makeDebounced(() => savePersistedSessionsAsync(this.persistedSessions, this.logger), DEBOUNCED_SAVE_MS);
|
|
112
134
|
// Start TTL cleanup timer
|