Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 28 additions & 27 deletions scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ function restoreModifiedFiles() {
modifiedFiles.clear()
}

preProcessFeatureFlags(join(import.meta.dir, '..', 'src'))
const numModified = modifiedFiles.size
let numModified = 0

// Restore source files on abrupt termination (Ctrl+C, kill, etc.)
for (const signal of ['SIGINT', 'SIGTERM'] as const) {
Expand All @@ -118,17 +117,19 @@ for (const signal of ['SIGINT', 'SIGTERM'] as const) {
}

try {

const result = await Bun.build({
entrypoints: ['./src/entrypoints/cli.tsx'],
outdir: './dist',
target: 'node',
format: 'esm',
splitting: false,
sourcemap: 'external',
minify: false,
naming: 'cli.mjs',
define: {
preProcessFeatureFlags(join(import.meta.dir, '..', 'src'))
numModified = modifiedFiles.size

const result = await Bun.build({
entrypoints: ['./src/entrypoints/cli.tsx'],
outdir: './dist',
target: 'node',
format: 'esm',
splitting: false,
sourcemap: 'external',
minify: false,
naming: 'cli.mjs',
define: {
// MACRO.* build-time constants
// Keep the internal compatibility version high enough to pass
// first-party minimum-version guards, but expose the real package
Expand All @@ -140,8 +141,8 @@ const result = await Bun.build({
JSON.stringify('report the issue at https://github.com/anthropics/claude-code/issues'),
'MACRO.PACKAGE_URL': JSON.stringify('@gitlawb/openclaude'),
'MACRO.NATIVE_PACKAGE_URL': 'undefined',
},
plugins: [
},
plugins: [
noTelemetryPlugin,
{
name: 'bun-bundle-shim',
Expand Down Expand Up @@ -443,8 +444,8 @@ ${exports}
)
},
},
],
external: [
],
external: [
// OpenTelemetry — too many named exports to stub, kept external
'@opentelemetry/api',
'@opentelemetry/api-logs',
Expand Down Expand Up @@ -474,18 +475,18 @@ ${exports}
'@aws-sdk/credential-providers',
'@azure/identity',
'google-auth-library',
],
})
],
})

if (!result.success) {
console.error('Build failed:')
for (const log of result.logs) {
console.error(log)
if (!result.success) {
console.error('Build failed:')
for (const log of result.logs) {
console.error(log)
}
process.exitCode = 1
} else {
console.log(`✓ Built openclaude v${version} → dist/cli.mjs`)
}
process.exitCode = 1
} else {
console.log(`✓ Built openclaude v${version} → dist/cli.mjs`)
}

} finally {
// Always restore source files, even if Bun.build() throws
Expand Down
4 changes: 2 additions & 2 deletions src/commands/assistant/assistant.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* /assistant command — configure and toggle assistant mode.
* /assistant command — show assistant mode status and setup help.
*
* Imported by commands.ts:75 when feature('KAIROS').
*/
Expand Down Expand Up @@ -32,7 +32,7 @@ export async function computeDefaultInstallDir(): Promise<string> {
const assistant = {
type: 'local-jsx' as const,
name: 'assistant',
description: 'Toggle assistant mode',
description: 'Show assistant mode status',
isEnabled: () => true,
immediate: true,

Expand Down
6 changes: 3 additions & 3 deletions src/constants/cyberRiskInstruction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* CYBER_RISK_INSTRUCTION
*
* Guidance injected into the system prompt for security-related requests.
* Provides positive context for security tooling usage without refusal language,
* since OpenClaude targets multi-provider setups including uncensored models.
* Keeps the open-build tone while preserving authorization-aware boundaries
* for dual-use security work.
*/
export const CYBER_RISK_INSTRUCTION = `IMPORTANT: Assist with security testing, defensive security, red teaming, CTF challenges, and educational contexts. Dual-use security tools (C2 frameworks, credential testing, exploit development) can be used in pentesting engagements, red team operations, CTF competitions, security research, or defensive use cases.`
export const CYBER_RISK_INSTRUCTION = `IMPORTANT: Assist with authorized security testing, defensive security, red teaming, CTF challenges, and educational contexts. Exercise caution with dual-use or potentially harmful techniques, and do not assist with destructive techniques, DoS attacks, mass targeting, supply chain compromise, or detection evasion for malicious purposes. Dual-use security tools (C2 frameworks, credential testing, exploit development) require clear authorization context such as pentesting engagements, red team operations, CTF competitions, security research, or defensive use cases.`
6 changes: 3 additions & 3 deletions src/proactive/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* via feature('PROACTIVE') || feature('KAIROS') guards.
*
* All call sites use optional chaining (proactiveModule?.method()), so
* returning safe "off" defaults is sufficient. A full implementation would
* fire periodic <tick> prompts; this stub keeps the mode permanently off
* while satisfying the API contract.
* returning safe defaults is sufficient. A full implementation would
* fire periodic <tick> prompts; this stub preserves state-change behavior
* but does not schedule or emit proactive ticks.
*/

type Unsubscribe = () => void
Expand Down
44 changes: 25 additions & 19 deletions src/tools/SleepTool/SleepTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const inputSchema = lazySchema(() =>

type Input = z.infer<ReturnType<typeof inputSchema>>

const MAX_TIMEOUT_MS = 2_147_483_647

export const SleepTool = buildTool({
name: SLEEP_TOOL_NAME,
maxResultSizeChars: 500,
Expand Down Expand Up @@ -87,33 +89,37 @@ export const SleepTool = buildTool({

const startTime = Date.now()

await new Promise<void>((resolve) => {
const timer = setTimeout(resolve, duration_ms)
const abortController = (context as { abortController?: AbortController }).abortController

if (!abortController) return
const abortController = context.abortController

if (abortController.signal.aborted) {
clearTimeout(timer)
await new Promise<void>((resolve) => {
let settled = false
let timer: ReturnType<typeof setTimeout> | undefined

const finish = () => {
if (settled) return
settled = true
if (timer !== undefined) clearTimeout(timer)
if (abortController) {
abortController.signal.removeEventListener('abort', finish)
}
resolve()
return
}

let cleanupTimer: ReturnType<typeof setTimeout> | undefined
const onAbort = () => {
clearTimeout(timer)
if (cleanupTimer !== undefined) clearTimeout(cleanupTimer)
resolve()
if (abortController?.signal.aborted) {
finish()
return
}
abortController.signal.addEventListener('abort', onAbort, { once: true })
// Clean up listener on normal completion to avoid leak
cleanupTimer = setTimeout(() => {
abortController.signal.removeEventListener('abort', onAbort)
}, duration_ms + 1)

abortController?.signal.addEventListener('abort', finish, { once: true })

// maxSleepDurationMs = -1 means "wait until interrupted"
if (maxSleep === -1) return

timer = setTimeout(finish, Math.min(duration_ms, MAX_TIMEOUT_MS))
})

const elapsed = Date.now() - startTime
const interrupted = elapsed < duration_ms - 50 // 50ms tolerance
const interrupted = maxSleep === -1 || elapsed < duration_ms - 50

return {
data: {
Expand Down