ticlawk 0.1.12-dev.0
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/LICENSE +15 -0
- package/README.md +426 -0
- package/agent-freeway.mjs +2 -0
- package/assets/ticlawk-concept.svg +137 -0
- package/bin/agent-freeway.mjs +4 -0
- package/bin/ticlawk.mjs +594 -0
- package/cc-watcher.mjs +3 -0
- package/package.json +72 -0
- package/scripts/postinstall.mjs +61 -0
- package/src/adapters/telegram/index.mjs +359 -0
- package/src/adapters/ticlawk/api.mjs +360 -0
- package/src/adapters/ticlawk/cards.mjs +149 -0
- package/src/adapters/ticlawk/credentials.mjs +25 -0
- package/src/adapters/ticlawk/index.mjs +1229 -0
- package/src/adapters/ticlawk/wake-client.mjs +204 -0
- package/src/core/adapter-registry.mjs +50 -0
- package/src/core/argv.mjs +38 -0
- package/src/core/bindings/store.mjs +81 -0
- package/src/core/bus.mjs +91 -0
- package/src/core/config.mjs +203 -0
- package/src/core/daemon-install.mjs +246 -0
- package/src/core/diagnostics.mjs +79 -0
- package/src/core/events/worker-events.mjs +80 -0
- package/src/core/executables.mjs +106 -0
- package/src/core/host-id.mjs +48 -0
- package/src/core/http.mjs +65 -0
- package/src/core/logger.mjs +34 -0
- package/src/core/media/inbound.mjs +127 -0
- package/src/core/media/outbound.mjs +163 -0
- package/src/core/profiles.mjs +173 -0
- package/src/core/runtime-contract.mjs +68 -0
- package/src/core/runtime-env.mjs +9 -0
- package/src/core/runtime-registry.mjs +93 -0
- package/src/core/runtime-support.mjs +197 -0
- package/src/core/setup-readiness.mjs +86 -0
- package/src/core/store/json-file-store.mjs +47 -0
- package/src/core/ticlawk-control.mjs +92 -0
- package/src/core/uninstall.mjs +142 -0
- package/src/core/update-state.mjs +62 -0
- package/src/core/update.mjs +178 -0
- package/src/runtimes/claude-code/index.mjs +363 -0
- package/src/runtimes/claude-code/session.mjs +388 -0
- package/src/runtimes/claude-code/transcripts.mjs +206 -0
- package/src/runtimes/codex/index.mjs +306 -0
- package/src/runtimes/codex/session.mjs +750 -0
- package/src/runtimes/openclaw/gateway.mjs +269 -0
- package/src/runtimes/openclaw/identity.mjs +34 -0
- package/src/runtimes/openclaw/index.mjs +228 -0
- package/src/runtimes/openclaw/inflight.mjs +46 -0
- package/src/runtimes/openclaw/target.mjs +57 -0
- package/src/runtimes/opencode/index.mjs +318 -0
- package/src/runtimes/opencode/session.mjs +413 -0
- package/src/runtimes/pi/index.mjs +287 -0
- package/src/runtimes/pi/session.mjs +423 -0
- package/ticlawk.mjs +260 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex runtime entry point.
|
|
3
|
+
*
|
|
4
|
+
* Worker-first runtime surface: create sessions, run turns, stream events,
|
|
5
|
+
* and discover local sessions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
9
|
+
import { basename } from 'node:path';
|
|
10
|
+
import {
|
|
11
|
+
createCodexSession,
|
|
12
|
+
runCodexPrompt,
|
|
13
|
+
streamCodexPrompt,
|
|
14
|
+
waitForCodexSessionById,
|
|
15
|
+
discoverCodexSessions,
|
|
16
|
+
CODEX_SESSIONS_DIR,
|
|
17
|
+
CODEX_MAX_AGE_MS,
|
|
18
|
+
getCodexRuntimeHealth,
|
|
19
|
+
requireCodexPath,
|
|
20
|
+
} from './session.mjs';
|
|
21
|
+
import { buildCodexInputFromInbound } from '../../core/media/inbound.mjs';
|
|
22
|
+
import { normalizeOutboundMedia } from '../../core/media/outbound.mjs';
|
|
23
|
+
import { emitWorkerEvent } from '../../core/events/worker-events.mjs';
|
|
24
|
+
import {
|
|
25
|
+
shouldStreamRuntime,
|
|
26
|
+
createDeltaAggregator,
|
|
27
|
+
sendAdapterMessage,
|
|
28
|
+
sendResult,
|
|
29
|
+
reportSubprocessFailure,
|
|
30
|
+
terminalRuntimeFailure,
|
|
31
|
+
updateBindingRuntimeMeta,
|
|
32
|
+
isRuntimeGatewayFailure,
|
|
33
|
+
} from '../../core/runtime-support.mjs';
|
|
34
|
+
|
|
35
|
+
export const codexRuntime = {
|
|
36
|
+
name: 'codex',
|
|
37
|
+
|
|
38
|
+
// Spawn `codex exec` to create a new session; wait for it to appear in
|
|
39
|
+
// ~/.codex/sessions/**. Returns { sessionId, cwd, path }. `path` is the
|
|
40
|
+
// jsonl transcript file location.
|
|
41
|
+
async createSession({ cwd, text, codexPath }) {
|
|
42
|
+
const sessionId = await createCodexSession({ cwd, message: text, codexPath });
|
|
43
|
+
const session = await waitForCodexSessionById(sessionId);
|
|
44
|
+
return {
|
|
45
|
+
sessionId,
|
|
46
|
+
cwd: session?.cwd || cwd,
|
|
47
|
+
path: session?.path || null,
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
runTurn({ sessionId, cwd, codexPath }, text, opts = {}) {
|
|
52
|
+
return runCodexPrompt({
|
|
53
|
+
sessionId,
|
|
54
|
+
cwd,
|
|
55
|
+
message: text,
|
|
56
|
+
codexPath,
|
|
57
|
+
input: opts.input,
|
|
58
|
+
timeoutMs: opts.timeoutMs,
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
runTurnStream({ sessionId, cwd, codexPath }, text, opts = {}) {
|
|
63
|
+
return streamCodexPrompt({
|
|
64
|
+
sessionId,
|
|
65
|
+
cwd,
|
|
66
|
+
message: text,
|
|
67
|
+
codexPath,
|
|
68
|
+
input: opts.input,
|
|
69
|
+
timeoutMs: opts.timeoutMs,
|
|
70
|
+
onEvent: opts.onEvent,
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Enumerate local Codex sessions by walking ~/.codex/sessions/**.
|
|
75
|
+
// Each entry has { sessionId, cwd, path } from the session_meta line.
|
|
76
|
+
async listLocalSessions() {
|
|
77
|
+
return discoverCodexSessions();
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
async resolveBinding(payload) {
|
|
81
|
+
const requestedCwd = String(payload?.cwd || payload?.workdir || payload?.projectDir || '').trim();
|
|
82
|
+
const codexPath = requireCodexPath(payload?.codexPath || payload?.runtimePath);
|
|
83
|
+
const codexVersion = getCodexRuntimeHealth(codexPath).version;
|
|
84
|
+
|
|
85
|
+
if (payload?.sessionId) {
|
|
86
|
+
const sessions = await this.listLocalSessions();
|
|
87
|
+
const session = sessions.find((entry) => entry.sessionId === payload.sessionId);
|
|
88
|
+
if (!session) {
|
|
89
|
+
throw new Error('codex session not found locally');
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
runtime: this.name,
|
|
93
|
+
displayName: payload.name || basename(session.cwd) || 'Codex',
|
|
94
|
+
runtimeMeta: {
|
|
95
|
+
sessionId: session.sessionId,
|
|
96
|
+
workdir: session.cwd,
|
|
97
|
+
cwd: session.cwd,
|
|
98
|
+
path: session.path || null,
|
|
99
|
+
runtimePath: codexPath,
|
|
100
|
+
codexPath,
|
|
101
|
+
codexVersion,
|
|
102
|
+
rotatePending: false,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!requestedCwd) {
|
|
108
|
+
throw new Error('cwd or sessionId is required for codex binding');
|
|
109
|
+
}
|
|
110
|
+
if (!existsSync(requestedCwd)) {
|
|
111
|
+
throw new Error(`codex cwd not found locally: ${requestedCwd}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
runtime: this.name,
|
|
116
|
+
displayName: payload.name || basename(requestedCwd) || 'Codex',
|
|
117
|
+
runtimeMeta: {
|
|
118
|
+
sessionId: null,
|
|
119
|
+
workdir: requestedCwd,
|
|
120
|
+
cwd: requestedCwd,
|
|
121
|
+
path: null,
|
|
122
|
+
runtimePath: codexPath,
|
|
123
|
+
codexPath,
|
|
124
|
+
codexVersion,
|
|
125
|
+
rotatePending: false,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
async health(meta = {}) {
|
|
131
|
+
return getCodexRuntimeHealth(meta?.codexPath || meta?.runtimePath);
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
async deliverTurn(inbound, ctx) {
|
|
135
|
+
const binding = ctx.getBinding(inbound.bindingId);
|
|
136
|
+
if (!binding) return false;
|
|
137
|
+
const adapter = ctx.adapter;
|
|
138
|
+
const meta = binding.runtimeMeta || {};
|
|
139
|
+
|
|
140
|
+
if (!meta.cwd || !existsSync(meta.cwd)) {
|
|
141
|
+
await sendAdapterMessage(adapter, binding, {
|
|
142
|
+
type: 'assistant',
|
|
143
|
+
text: `⚠️ Codex cwd not found: ${meta.cwd || '(missing)'}`,
|
|
144
|
+
media: [],
|
|
145
|
+
replyToMessageId: inbound.messageId || null,
|
|
146
|
+
});
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const codexInput = inbound.action === 'image'
|
|
151
|
+
? await buildCodexInputFromInbound(inbound, 'codex')
|
|
152
|
+
: null;
|
|
153
|
+
const message = inbound.action === 'image'
|
|
154
|
+
? (codexInput.find((item) => item?.type === 'text')?.text || inbound.text || '(image attached)')
|
|
155
|
+
: inbound.text;
|
|
156
|
+
|
|
157
|
+
const shouldRotate = !meta.sessionId || meta.rotatePending;
|
|
158
|
+
const deltaAggregator = createDeltaAggregator({
|
|
159
|
+
flushDelta: async ({ text, sessionId, turnId, cwd }) => {
|
|
160
|
+
await emitWorkerEvent({
|
|
161
|
+
adapter,
|
|
162
|
+
binding,
|
|
163
|
+
agent: this.name,
|
|
164
|
+
sessionId: sessionId || meta.sessionId || binding.id,
|
|
165
|
+
turnId: turnId || null,
|
|
166
|
+
cwd: cwd || meta.cwd,
|
|
167
|
+
replyToMessageId: inbound.messageId || null,
|
|
168
|
+
event: {
|
|
169
|
+
hook_event_name: 'worker.message.delta',
|
|
170
|
+
worker_event_name: 'worker.message.delta',
|
|
171
|
+
delta: text,
|
|
172
|
+
},
|
|
173
|
+
logger: ctx.logger,
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
try {
|
|
178
|
+
const runtimeCodexPath = requireCodexPath(meta.codexPath || meta.runtimePath);
|
|
179
|
+
const runtimeCodexVersion = getCodexRuntimeHealth(runtimeCodexPath).version || meta.codexVersion || null;
|
|
180
|
+
const result = (shouldRotate || inbound.action === 'image' || shouldStreamRuntime(this.name, this))
|
|
181
|
+
? await this.runTurnStream({ sessionId: shouldRotate ? null : meta.sessionId, cwd: meta.cwd, codexPath: runtimeCodexPath }, message, {
|
|
182
|
+
input: codexInput,
|
|
183
|
+
onEvent: async (event) => {
|
|
184
|
+
if (event?.type === 'turn.started') {
|
|
185
|
+
await emitWorkerEvent({
|
|
186
|
+
adapter,
|
|
187
|
+
binding,
|
|
188
|
+
agent: this.name,
|
|
189
|
+
sessionId: event.sessionId || meta.sessionId || binding.id,
|
|
190
|
+
turnId: event.turnId || null,
|
|
191
|
+
cwd: meta.cwd,
|
|
192
|
+
replyToMessageId: inbound.messageId || null,
|
|
193
|
+
event: {
|
|
194
|
+
hook_event_name: 'worker.turn.start',
|
|
195
|
+
worker_event_name: 'worker.turn.start',
|
|
196
|
+
},
|
|
197
|
+
logger: ctx.logger,
|
|
198
|
+
});
|
|
199
|
+
} else if (event?.type === 'message.delta' && event.text) {
|
|
200
|
+
deltaAggregator.push(event.text, {
|
|
201
|
+
sessionId: event.sessionId || meta.sessionId || binding.id,
|
|
202
|
+
turnId: event.turnId || null,
|
|
203
|
+
cwd: meta.cwd,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
: await this.runTurn({ sessionId: meta.sessionId, cwd: meta.cwd, codexPath: runtimeCodexPath }, message, {
|
|
209
|
+
input: codexInput,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
await deltaAggregator.flush();
|
|
213
|
+
const nextBinding = await updateBindingRuntimeMeta(ctx, binding, {
|
|
214
|
+
sessionId: result?.sessionId || meta.sessionId,
|
|
215
|
+
cwd: result?.cwd || meta.cwd,
|
|
216
|
+
path: result?.path || meta.path || null,
|
|
217
|
+
runtimePath: runtimeCodexPath,
|
|
218
|
+
codexPath: runtimeCodexPath,
|
|
219
|
+
codexVersion: runtimeCodexVersion,
|
|
220
|
+
rotatePending: false,
|
|
221
|
+
lastRotatedAt: shouldRotate ? new Date().toISOString() : meta.lastRotatedAt,
|
|
222
|
+
}, { status: 'connected' });
|
|
223
|
+
await sendResult(adapter, nextBinding, inbound, {
|
|
224
|
+
...result,
|
|
225
|
+
media: normalizeOutboundMedia(result),
|
|
226
|
+
});
|
|
227
|
+
await emitWorkerEvent({
|
|
228
|
+
adapter,
|
|
229
|
+
binding: nextBinding,
|
|
230
|
+
agent: this.name,
|
|
231
|
+
sessionId: result?.sessionId || meta.sessionId || binding.id,
|
|
232
|
+
turnId: result?.turnId || null,
|
|
233
|
+
cwd: result?.cwd || meta.cwd,
|
|
234
|
+
replyToMessageId: inbound.messageId || null,
|
|
235
|
+
event: {
|
|
236
|
+
hook_event_name: 'Stop',
|
|
237
|
+
worker_event_name: 'worker.turn.complete',
|
|
238
|
+
},
|
|
239
|
+
logger: ctx.logger,
|
|
240
|
+
});
|
|
241
|
+
return true;
|
|
242
|
+
} catch (err) {
|
|
243
|
+
await deltaAggregator.flush().catch(() => {});
|
|
244
|
+
const failureInfo = err?.info || {
|
|
245
|
+
ok: false,
|
|
246
|
+
kind: 'exit-error',
|
|
247
|
+
errorMessage: err?.message || 'Codex failed',
|
|
248
|
+
durationMs: 0,
|
|
249
|
+
};
|
|
250
|
+
let failureBinding = binding;
|
|
251
|
+
if (isRuntimeGatewayFailure(failureInfo)) {
|
|
252
|
+
failureBinding = await updateBindingRuntimeMeta(ctx, binding, {
|
|
253
|
+
rotatePending: true,
|
|
254
|
+
lastGatewayFailureAt: new Date().toISOString(),
|
|
255
|
+
lastGatewayFailureReason: failureInfo.errorMessage || err?.message || 'Codex app-server error',
|
|
256
|
+
}, { status: 'degraded' }).catch(() => binding);
|
|
257
|
+
}
|
|
258
|
+
await emitWorkerEvent({
|
|
259
|
+
adapter,
|
|
260
|
+
binding: failureBinding,
|
|
261
|
+
agent: this.name,
|
|
262
|
+
sessionId: meta.sessionId || binding.id,
|
|
263
|
+
turnId: failureInfo?.turnId || null,
|
|
264
|
+
cwd: meta.cwd,
|
|
265
|
+
replyToMessageId: inbound.messageId || null,
|
|
266
|
+
event: {
|
|
267
|
+
hook_event_name: 'worker.turn.error',
|
|
268
|
+
worker_event_name: 'worker.turn.error',
|
|
269
|
+
error: err?.message || 'Codex failed',
|
|
270
|
+
},
|
|
271
|
+
logger: ctx.logger,
|
|
272
|
+
});
|
|
273
|
+
await reportSubprocessFailure({
|
|
274
|
+
adapter,
|
|
275
|
+
binding: failureBinding,
|
|
276
|
+
inbound,
|
|
277
|
+
runtimeName: 'Codex',
|
|
278
|
+
info: failureInfo,
|
|
279
|
+
});
|
|
280
|
+
return terminalRuntimeFailure(err?.message || 'Codex failed');
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
async reconcileAfterRestart(binding, ctx) {
|
|
285
|
+
const meta = binding.runtimeMeta || {};
|
|
286
|
+
await emitWorkerEvent({
|
|
287
|
+
adapter: ctx.adapter,
|
|
288
|
+
binding,
|
|
289
|
+
agent: this.name,
|
|
290
|
+
sessionId: meta.sessionId || binding.id,
|
|
291
|
+
cwd: meta.cwd || '',
|
|
292
|
+
event: {
|
|
293
|
+
hook_event_name: 'Stop',
|
|
294
|
+
worker_event_name: 'worker.turn.complete',
|
|
295
|
+
reason: 'connector.restart.reconcile',
|
|
296
|
+
},
|
|
297
|
+
logger: ctx.logger,
|
|
298
|
+
});
|
|
299
|
+
return 1;
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
sessionsDir: CODEX_SESSIONS_DIR,
|
|
303
|
+
maxAgeMs: CODEX_MAX_AGE_MS,
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
export default codexRuntime;
|