lark-coding-assistant 0.2.6 → 0.2.8

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hook-entry.ts","../src/agents/stop-event.ts","../src/agents/types.ts","../src/daemon/client.ts","../src/core/errors.ts","../src/core/paths.ts"],"sourcesContent":["import { appendFile } from 'node:fs/promises';\nimport { normalizeSessionStartEvent, normalizeStopEvent } from './agents/stop-event.js';\nimport { normalizeAgentId } from './agents/types.js';\nimport { requestDaemon } from './daemon/client.js';\nimport { resolveAppPaths } from './core/paths.js';\n\nconst socket = process.env.LARK_CODING_ASSISTANT_SOCKET;\nconst sessionId = process.env.LARK_CODING_ASSISTANT_SESSION_ID;\nconst agent = normalizeAgentId(process.env.LARK_CODING_ASSISTANT_AGENT ?? '');\n\ntry {\n if (!socket || !sessionId) throw new Error('missing bridge hook environment');\n const payload = JSON.parse(await readStdin()) as unknown;\n const started = agent ? normalizeSessionStartEvent(sessionId, agent, payload) : undefined;\n const completed = normalizeStopEvent(sessionId, payload);\n if (!started && !completed) throw new Error('invalid lifecycle hook payload');\n const result = await requestDaemon(socket, started\n ? { method: 'agentSessionStarted', candidate: started }\n : { method: 'turnComplete', candidate: completed as NonNullable<typeof completed> }, 3_000);\n if (!result.ok) throw new Error(result.error);\n} catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const paths = resolveAppPaths();\n await appendFile(paths.logFile, `${new Date().toISOString()} Stop hook delivery failed: ${message}\\n`, { mode: 0o600 })\n .catch(() => undefined);\n}\n\nasync function readStdin(): Promise<string> {\n process.stdin.setEncoding('utf8');\n let value = '';\n for await (const chunk of process.stdin) value += chunk;\n return value;\n}\n","import type { AgentId, AgentSessionStartedCandidate, TurnCompleteCandidate } from './types.js';\n\nexport function normalizeSessionStartEvent(\n bridgeSessionId: string,\n agent: AgentId,\n value: unknown,\n): AgentSessionStartedCandidate | undefined {\n if (!bridgeSessionId || !value || typeof value !== 'object') return undefined;\n const event = value as Record<string, unknown>;\n if (event.hook_event_name !== 'SessionStart') return undefined;\n const agentSessionId = stringField(event, 'session_id');\n const cwd = stringField(event, 'cwd');\n if (!agentSessionId || !cwd) return undefined;\n return { sessionId: bridgeSessionId, agent, agentSessionId, cwd, source: stringField(event, 'source') };\n}\n\nexport function validSessionStartCandidate(value: unknown): value is AgentSessionStartedCandidate {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Record<string, unknown>;\n return typeof candidate.sessionId === 'string' && candidate.sessionId.length > 0\n && (candidate.agent === 'codex' || candidate.agent === 'traex' || candidate.agent === 'claude')\n && typeof candidate.agentSessionId === 'string' && candidate.agentSessionId.length > 0\n && typeof candidate.cwd === 'string' && candidate.cwd.length > 0;\n}\n\nexport function normalizeStopEvent(\n bridgeSessionId: string,\n value: unknown,\n): TurnCompleteCandidate | undefined {\n if (!bridgeSessionId || !value || typeof value !== 'object') return undefined;\n const event = value as Record<string, unknown>;\n if (event.hook_event_name !== 'Stop') return undefined;\n const eventId = stringField(event, 'turn_id') ?? stringField(event, 'prompt_id');\n const agentSessionId = stringField(event, 'session_id');\n const cwd = stringField(event, 'cwd');\n const lastAssistantMessage = stringField(event, 'last_assistant_message')?.trim();\n if (!eventId || !agentSessionId || !cwd || !lastAssistantMessage) return undefined;\n return { sessionId: bridgeSessionId, eventId, agentSessionId, cwd, lastAssistantMessage };\n}\n\nexport function validTurnCompleteCandidate(value: unknown): value is TurnCompleteCandidate {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Record<string, unknown>;\n return ['sessionId', 'eventId', 'agentSessionId', 'cwd', 'lastAssistantMessage']\n .every((key) => typeof candidate[key] === 'string' && (candidate[key] as string).length > 0);\n}\n\nfunction stringField(value: Record<string, unknown>, key: string): string | undefined {\n const field = value[key];\n return typeof field === 'string' && field.length > 0 ? field : undefined;\n}\n","import type { AppConfig } from '../core/model.js';\nimport type { ScreenDetection } from '../screen/detector.js';\n\nexport const AGENT_IDS = ['codex', 'traex', 'claude'] as const;\nexport type AgentId = typeof AGENT_IDS[number];\nexport type LegacyAgentId = 'trae-cli' | 'claude-code';\n\nexport function normalizeAgentId(value: string): AgentId | undefined {\n if (value === 'trae-cli') return 'traex';\n if (value === 'claude-code') return 'claude';\n return (AGENT_IDS as readonly string[]).includes(value) ? value as AgentId : undefined;\n}\n\nexport type AgentResume =\n | { mode: 'picker'; all?: boolean }\n | { mode: 'last' }\n | { mode: 'session'; sessionId: string };\n\nexport interface ResumeOptions {\n resume?: boolean | string;\n resumeLast?: boolean;\n resumeAll?: boolean;\n}\n\nexport interface TurnCompleteCandidate {\n sessionId: string;\n eventId: string;\n agentSessionId: string;\n cwd: string;\n lastAssistantMessage: string;\n}\n\nexport interface AgentSessionStartedCandidate {\n sessionId: string;\n agent: AgentId;\n agentSessionId: string;\n cwd: string;\n source?: string;\n}\n\nexport interface LaunchInput {\n resume?: AgentResume;\n stopHookCommand: string;\n}\n\nexport interface AgentAdapter {\n id: AgentId;\n displayName: string;\n groupOrder: number;\n binary(config: AppConfig): string;\n versionArgs: readonly string[];\n buildLaunchArgs(input: LaunchInput): string[];\n detectScreen(raw: string, paneAlive: boolean, cursor?: { x: number; y: number }): ScreenDetection;\n}\n","import { randomUUID } from 'node:crypto';\nimport { createConnection } from 'node:net';\nimport type { DaemonRequestInput, DaemonResult } from './protocol.js';\nimport { AppError, systemErrorCode } from '../core/errors.js';\n\nexport function requestDaemon(\n socketPath: string,\n request: DaemonRequestInput,\n timeoutMs = 5000,\n): Promise<DaemonResult> {\n return new Promise((resolve, reject) => {\n const id = randomUUID();\n const socket = createConnection(socketPath);\n let buffer = '';\n const timer = setTimeout(() => {\n socket.destroy();\n reject(new AppError('REQUEST_TIMEOUT', 'daemon request timed out'));\n }, timeoutMs);\n socket.setEncoding('utf8');\n socket.once('connect', () => socket.write(`${JSON.stringify({ ...request, id })}\\n`));\n socket.on('data', (chunk) => {\n buffer += chunk;\n const newline = buffer.indexOf('\\n');\n if (newline === -1) return;\n clearTimeout(timer);\n socket.destroy();\n try {\n resolve(JSON.parse(buffer.slice(0, newline)) as DaemonResult);\n } catch (error) {\n reject(error);\n }\n });\n socket.once('error', (error) => {\n clearTimeout(timer);\n const code = systemErrorCode(error);\n if (code === 'ENOENT' || code === 'ECONNREFUSED' || code === 'ECONNRESET' || code === 'EPIPE') {\n reject(new AppError('DAEMON_UNAVAILABLE', 'unable to connect to daemon', {}, { cause: error }));\n return;\n }\n reject(error);\n });\n });\n}\n","export const APP_ERROR_CODES = [\n 'SESSION_EXISTS',\n 'SESSION_STARTING',\n 'SESSION_START_TIMEOUT',\n 'AGENT_SESSION_IN_USE',\n 'AGENT_EXITED_DURING_STARTUP',\n 'AGENT_IDENTITY_TIMEOUT',\n 'SESSION_NOT_FOUND',\n 'INVALID_SESSION_NAME',\n 'NOT_INITIALIZED',\n 'INVALID_CWD',\n 'BINARY_NOT_FOUND',\n 'INVALID_OPTIONS',\n 'INVALID_RESUME',\n 'START_FAILED',\n 'DAEMON_UNAVAILABLE',\n 'DAEMON_UNRESPONSIVE',\n 'REQUEST_TIMEOUT',\n 'UNKNOWN',\n] as const;\n\nexport type AppErrorCode = typeof APP_ERROR_CODES[number];\n\nexport type ErrorContext = Record<string, string | number | boolean | undefined>;\n\nexport class AppError extends Error {\n readonly code: AppErrorCode;\n readonly context: ErrorContext;\n\n constructor(\n code: AppErrorCode,\n message: string,\n context: ErrorContext = {},\n options: { cause?: unknown } = {},\n ) {\n super(message, options);\n this.name = 'AppError';\n this.code = code;\n this.context = context;\n }\n}\n\nexport function isAppError(error: unknown): error is AppError {\n return error instanceof AppError;\n}\n\nexport function isAppErrorCode(value: unknown): value is AppErrorCode {\n return typeof value === 'string' && APP_ERROR_CODES.includes(value as AppErrorCode);\n}\n\nexport function asAppError(\n error: unknown,\n code: AppErrorCode = 'UNKNOWN',\n context: ErrorContext = {},\n): AppError {\n if (isAppError(error)) {\n if (Object.keys(context).length === 0) return error;\n return new AppError(error.code, error.message, { ...context, ...error.context }, { cause: error });\n }\n return new AppError(code, errorMessage(error), context, { cause: error });\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function serializeAppError(error: unknown): {\n error: string;\n errorCode?: AppErrorCode;\n errorContext?: ErrorContext;\n} {\n if (!isAppError(error)) return { error: errorMessage(error) };\n const errorContext = Object.fromEntries(\n Object.entries(error.context).filter(([, value]) => value !== undefined),\n );\n return {\n error: error.message,\n errorCode: error.code,\n ...(Object.keys(errorContext).length > 0 ? { errorContext } : {}),\n };\n}\n\nexport function systemErrorCode(error: unknown): string | undefined {\n return error instanceof Error && 'code' in error && typeof error.code === 'string'\n ? error.code\n : undefined;\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport interface AppPaths {\n root: string;\n config: string;\n secrets: string;\n state: string;\n logsDir: string;\n logFile: string;\n runtimeDir: string;\n socket: string;\n pid: string;\n}\n\nexport function resolveAppPaths(root = process.env.LARK_CODING_ASSISTANT_HOME): AppPaths {\n const base = root || join(homedir(), '.lark-coding-assistant');\n return {\n root: base,\n config: join(base, 'config.json'),\n secrets: join(base, 'secrets.json'),\n state: join(base, 'state.json'),\n logsDir: join(base, 'logs'),\n logFile: join(base, 'logs', 'assistant.log'),\n runtimeDir: join(base, 'runtime'),\n socket: join(base, 'runtime', 'daemon.sock'),\n pid: join(base, 'runtime', 'daemon.pid'),\n };\n}\n"],"mappings":"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","names":["agent","socket"]}
1
+ {"version":3,"sources":["../src/hook-entry.ts","../src/agents/stop-event.ts","../src/agents/types.ts","../src/daemon/client.ts","../src/core/errors.ts","../src/core/paths.ts"],"sourcesContent":["import { appendFile } from 'node:fs/promises';\nimport { normalizeSessionStartEvent, normalizeStopEvent } from './agents/stop-event.js';\nimport { normalizeAgentId } from './agents/types.js';\nimport { requestDaemon } from './daemon/client.js';\nimport { resolveAppPaths } from './core/paths.js';\n\nconst socket = process.env.LARK_CODING_ASSISTANT_SOCKET;\nconst sessionId = process.env.LARK_CODING_ASSISTANT_SESSION_ID;\nconst agent = normalizeAgentId(process.env.LARK_CODING_ASSISTANT_AGENT ?? '');\n\ntry {\n if (!socket || !sessionId) throw new Error('missing bridge hook environment');\n const payload = JSON.parse(await readStdin()) as unknown;\n const started = agent ? normalizeSessionStartEvent(sessionId, agent, payload) : undefined;\n const completed = normalizeStopEvent(sessionId, payload);\n if (!started && !completed) throw new Error('invalid lifecycle hook payload');\n const result = await requestDaemon(socket, started\n ? { method: 'agentSessionStarted', candidate: started }\n : { method: 'turnComplete', candidate: completed as NonNullable<typeof completed> }, 3_000);\n if (!result.ok) throw new Error(result.error);\n} catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const paths = resolveAppPaths();\n await appendFile(paths.logFile, `${new Date().toISOString()} Stop hook delivery failed: ${message}\\n`, { mode: 0o600 })\n .catch(() => undefined);\n}\n\nasync function readStdin(): Promise<string> {\n process.stdin.setEncoding('utf8');\n let value = '';\n for await (const chunk of process.stdin) value += chunk;\n return value;\n}\n","import type { AgentId, AgentSessionStartedCandidate, TurnCompleteCandidate } from './types.js';\n\nexport function normalizeSessionStartEvent(\n bridgeSessionId: string,\n agent: AgentId,\n value: unknown,\n): AgentSessionStartedCandidate | undefined {\n if (!bridgeSessionId || !value || typeof value !== 'object') return undefined;\n const event = value as Record<string, unknown>;\n if (event.hook_event_name !== 'SessionStart') return undefined;\n const agentSessionId = stringField(event, 'session_id');\n const cwd = stringField(event, 'cwd');\n if (!agentSessionId || !cwd) return undefined;\n return { sessionId: bridgeSessionId, agent, agentSessionId, cwd, source: stringField(event, 'source') };\n}\n\nexport function validSessionStartCandidate(value: unknown): value is AgentSessionStartedCandidate {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Record<string, unknown>;\n return typeof candidate.sessionId === 'string' && candidate.sessionId.length > 0\n && (candidate.agent === 'codex' || candidate.agent === 'traex' || candidate.agent === 'claude')\n && typeof candidate.agentSessionId === 'string' && candidate.agentSessionId.length > 0\n && typeof candidate.cwd === 'string' && candidate.cwd.length > 0;\n}\n\nexport function normalizeStopEvent(\n bridgeSessionId: string,\n value: unknown,\n): TurnCompleteCandidate | undefined {\n if (!bridgeSessionId || !value || typeof value !== 'object') return undefined;\n const event = value as Record<string, unknown>;\n if (event.hook_event_name !== 'Stop') return undefined;\n const eventId = stringField(event, 'turn_id') ?? stringField(event, 'prompt_id');\n const agentSessionId = stringField(event, 'session_id');\n const cwd = stringField(event, 'cwd');\n const lastAssistantMessage = stringField(event, 'last_assistant_message')?.trim();\n if (!eventId || !agentSessionId || !cwd || !lastAssistantMessage) return undefined;\n return { sessionId: bridgeSessionId, eventId, agentSessionId, cwd, lastAssistantMessage };\n}\n\nexport function validTurnCompleteCandidate(value: unknown): value is TurnCompleteCandidate {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Record<string, unknown>;\n return ['sessionId', 'eventId', 'agentSessionId', 'cwd', 'lastAssistantMessage']\n .every((key) => typeof candidate[key] === 'string' && (candidate[key] as string).length > 0);\n}\n\nfunction stringField(value: Record<string, unknown>, key: string): string | undefined {\n const field = value[key];\n return typeof field === 'string' && field.length > 0 ? field : undefined;\n}\n","import type { AppConfig } from '../core/model.js';\nimport type { ScreenDetection } from '../screen/detector.js';\n\nexport const AGENT_IDS = ['codex', 'traex', 'claude'] as const;\nexport type AgentId = typeof AGENT_IDS[number];\nexport type LegacyAgentId = 'trae-cli' | 'claude-code';\n\nexport function normalizeAgentId(value: string): AgentId | undefined {\n if (value === 'trae-cli') return 'traex';\n if (value === 'claude-code') return 'claude';\n return (AGENT_IDS as readonly string[]).includes(value) ? value as AgentId : undefined;\n}\n\nexport type AgentResume =\n | { mode: 'picker'; all?: boolean }\n | { mode: 'last' }\n | { mode: 'session'; sessionId: string };\n\nexport interface ResumeOptions {\n resume?: boolean | string;\n resumeLast?: boolean;\n resumeAll?: boolean;\n}\n\nexport interface TurnCompleteCandidate {\n sessionId: string;\n eventId: string;\n agentSessionId: string;\n cwd: string;\n lastAssistantMessage: string;\n}\n\nexport interface AgentSessionStartedCandidate {\n sessionId: string;\n agent: AgentId;\n agentSessionId: string;\n cwd: string;\n source?: string;\n}\n\nexport interface LaunchInput {\n resume?: AgentResume;\n stopHookCommand: string;\n}\n\nexport interface AgentAdapter {\n id: AgentId;\n displayName: string;\n groupOrder: number;\n binary(config: AppConfig): string;\n versionArgs: readonly string[];\n buildLaunchArgs(input: LaunchInput): string[];\n detectScreen(raw: string, paneAlive: boolean, cursor?: { x: number; y: number }): ScreenDetection;\n}\n","import { randomUUID } from 'node:crypto';\nimport { createConnection } from 'node:net';\nimport type { DaemonRequestInput, DaemonResult } from './protocol.js';\nimport { AppError, systemErrorCode } from '../core/errors.js';\n\nexport function requestDaemon(\n socketPath: string,\n request: DaemonRequestInput,\n timeoutMs = 5000,\n): Promise<DaemonResult> {\n return new Promise((resolve, reject) => {\n const id = randomUUID();\n const socket = createConnection(socketPath);\n let buffer = '';\n const timer = setTimeout(() => {\n socket.destroy();\n reject(new AppError('REQUEST_TIMEOUT', 'daemon request timed out'));\n }, timeoutMs);\n socket.setEncoding('utf8');\n socket.once('connect', () => socket.write(`${JSON.stringify({ ...request, id })}\\n`));\n socket.on('data', (chunk) => {\n buffer += chunk;\n const newline = buffer.indexOf('\\n');\n if (newline === -1) return;\n clearTimeout(timer);\n socket.destroy();\n try {\n resolve(JSON.parse(buffer.slice(0, newline)) as DaemonResult);\n } catch (error) {\n reject(error);\n }\n });\n socket.once('error', (error) => {\n clearTimeout(timer);\n const code = systemErrorCode(error);\n if (code === 'ENOENT' || code === 'ECONNREFUSED' || code === 'ECONNRESET' || code === 'EPIPE') {\n reject(new AppError('DAEMON_UNAVAILABLE', 'unable to connect to daemon', {}, { cause: error }));\n return;\n }\n reject(error);\n });\n });\n}\n","export const APP_ERROR_CODES = [\n 'SESSION_EXISTS',\n 'SESSION_STARTING',\n 'SESSION_START_TIMEOUT',\n 'AGENT_SESSION_IN_USE',\n 'AGENT_EXITED',\n 'AGENT_EXITED_DURING_STARTUP',\n 'AGENT_IDENTITY_TIMEOUT',\n 'SESSION_NOT_FOUND',\n 'INVALID_SESSION_NAME',\n 'NOT_INITIALIZED',\n 'INVALID_CWD',\n 'BINARY_NOT_FOUND',\n 'INVALID_OPTIONS',\n 'INVALID_RESUME',\n 'START_FAILED',\n 'DAEMON_UNAVAILABLE',\n 'DAEMON_UNRESPONSIVE',\n 'REQUEST_TIMEOUT',\n 'UNKNOWN',\n] as const;\n\nexport type AppErrorCode = typeof APP_ERROR_CODES[number];\n\nexport type ErrorContext = Record<string, string | number | boolean | undefined>;\n\nexport class AppError extends Error {\n readonly code: AppErrorCode;\n readonly context: ErrorContext;\n\n constructor(\n code: AppErrorCode,\n message: string,\n context: ErrorContext = {},\n options: { cause?: unknown } = {},\n ) {\n super(message, options);\n this.name = 'AppError';\n this.code = code;\n this.context = context;\n }\n}\n\nexport function isAppError(error: unknown): error is AppError {\n return error instanceof AppError;\n}\n\nexport function isAppErrorCode(value: unknown): value is AppErrorCode {\n return typeof value === 'string' && APP_ERROR_CODES.includes(value as AppErrorCode);\n}\n\nexport function asAppError(\n error: unknown,\n code: AppErrorCode = 'UNKNOWN',\n context: ErrorContext = {},\n): AppError {\n if (isAppError(error)) {\n if (Object.keys(context).length === 0) return error;\n return new AppError(error.code, error.message, { ...context, ...error.context }, { cause: error });\n }\n return new AppError(code, errorMessage(error), context, { cause: error });\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function serializeAppError(error: unknown): {\n error: string;\n errorCode?: AppErrorCode;\n errorContext?: ErrorContext;\n} {\n if (!isAppError(error)) return { error: errorMessage(error) };\n const errorContext = Object.fromEntries(\n Object.entries(error.context).filter(([, value]) => value !== undefined),\n );\n return {\n error: error.message,\n errorCode: error.code,\n ...(Object.keys(errorContext).length > 0 ? { errorContext } : {}),\n };\n}\n\nexport function systemErrorCode(error: unknown): string | undefined {\n return error instanceof Error && 'code' in error && typeof error.code === 'string'\n ? error.code\n : undefined;\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport interface AppPaths {\n root: string;\n config: string;\n secrets: string;\n state: string;\n logsDir: string;\n logFile: string;\n runtimeDir: string;\n socket: string;\n pid: string;\n}\n\nexport function resolveAppPaths(root = process.env.LARK_CODING_ASSISTANT_HOME): AppPaths {\n const base = root || join(homedir(), '.lark-coding-assistant');\n return {\n root: base,\n config: join(base, 'config.json'),\n secrets: join(base, 'secrets.json'),\n state: join(base, 'state.json'),\n logsDir: join(base, 'logs'),\n logFile: join(base, 'logs', 'assistant.log'),\n runtimeDir: join(base, 'runtime'),\n socket: join(base, 'runtime', 'daemon.sock'),\n pid: join(base, 'runtime', 'daemon.pid'),\n };\n}\n"],"mappings":"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","names":["agent","socket"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lark-coding-assistant",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Control local coding-agent CLI sessions from Feishu or Lark",
5
5
  "type": "module",
6
6
  "bin": {