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
package/bin/ticlawk.mjs
ADDED
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { request } from 'node:http';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { installProcessDiagnostics } from '../src/core/diagnostics.mjs';
|
|
6
|
+
import {
|
|
7
|
+
AF_ADAPTER_KEY,
|
|
8
|
+
getConfiguredAdapter,
|
|
9
|
+
loadPersistentConfig,
|
|
10
|
+
getStreamingConfigView,
|
|
11
|
+
LEGACY_TICLAWK_API_KEY,
|
|
12
|
+
normalizeAdapterConfigTarget,
|
|
13
|
+
normalizeAdapterName,
|
|
14
|
+
normalizeRuntimeConfigTarget,
|
|
15
|
+
normalizeStreamingTarget,
|
|
16
|
+
persistConfig,
|
|
17
|
+
SUPPORTED_ADAPTERS,
|
|
18
|
+
TICLAWK_CONNECTOR_API_KEY,
|
|
19
|
+
TICLAWK_CONNECTOR_WS_URL,
|
|
20
|
+
} from '../src/core/config.mjs';
|
|
21
|
+
import { parseOptionArgs } from '../src/core/argv.mjs';
|
|
22
|
+
import {
|
|
23
|
+
activateProfile,
|
|
24
|
+
getActiveProfile,
|
|
25
|
+
listProfiles,
|
|
26
|
+
} from '../src/core/profiles.mjs';
|
|
27
|
+
import { getAdapterAuthHelp, runAdapterAuth } from '../src/core/adapter-registry.mjs';
|
|
28
|
+
import { runTiclawkConnect } from '../src/core/ticlawk-control.mjs';
|
|
29
|
+
import {
|
|
30
|
+
RUNTIME_DEFINITIONS,
|
|
31
|
+
getRuntimeDefinition,
|
|
32
|
+
getRuntimeDisplayName,
|
|
33
|
+
getRuntimeExecutableDefinitions,
|
|
34
|
+
normalizeServiceType,
|
|
35
|
+
} from '../src/core/runtime-registry.mjs';
|
|
36
|
+
import { runSelfUpdate, readPkgVersion } from '../src/core/update.mjs';
|
|
37
|
+
import { getUninstallHelp, runSelfUninstall } from '../src/core/uninstall.mjs';
|
|
38
|
+
import { getInstallDaemonHelp, runInstallDaemon } from '../src/core/daemon-install.mjs';
|
|
39
|
+
import { runSetupReadiness } from '../src/core/setup-readiness.mjs';
|
|
40
|
+
|
|
41
|
+
const TICLAWK_PORT = Number(process.env.FEED_RELAY_PORT || 8741);
|
|
42
|
+
|
|
43
|
+
function getConfigKeyUsage() {
|
|
44
|
+
const runtimeKeys = getRuntimeExecutableDefinitions().map((runtime) => runtime.executableCliKey);
|
|
45
|
+
return ['adapter', 'streaming...', ...runtimeKeys, 'telegram.bot-token', 'ticlawk.connector-api-key', 'ticlawk.api-url', 'ticlawk.connector-ws-url'].join('|');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getRuntimeConnectUsageLines() {
|
|
49
|
+
return RUNTIME_DEFINITIONS
|
|
50
|
+
.filter((runtime) => !runtime.aliases.includes(''))
|
|
51
|
+
.map((runtime) => {
|
|
52
|
+
if (runtime.connect?.locator === 'agentId') {
|
|
53
|
+
return ` ticlawk connect [--adapter <adapter>] --type ${runtime.name} --agent-id <id> [--name <name>] [--runtime-path <path>] [--switch-user]`;
|
|
54
|
+
}
|
|
55
|
+
return ` ticlawk connect [--adapter <adapter>] --type ${runtime.name} [--session-id <id>] --workdir <dir> [--name <name>] [--runtime-path <path>] [--switch-user]`;
|
|
56
|
+
})
|
|
57
|
+
.join('\n');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getRuntimeConfigExamples() {
|
|
61
|
+
return getRuntimeExecutableDefinitions()
|
|
62
|
+
.map((runtime) => ` ticlawk config set ${runtime.executableCliKey} /path/to/${runtime.name} # advanced/manual`)
|
|
63
|
+
.join('\n');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getRuntimeConnectExamples() {
|
|
67
|
+
return [
|
|
68
|
+
' ticlawk connect --adapter telegram --workdir /path/to/project',
|
|
69
|
+
' ticlawk connect --adapter telegram --session-id <claude-session-id>',
|
|
70
|
+
...RUNTIME_DEFINITIONS
|
|
71
|
+
.filter((runtime) => !runtime.aliases.includes(''))
|
|
72
|
+
.slice(0, 4)
|
|
73
|
+
.map((runtime) => {
|
|
74
|
+
if (runtime.connect?.locator === 'agentId') {
|
|
75
|
+
return ` ticlawk connect --adapter telegram --type ${runtime.name} --agent-id main`;
|
|
76
|
+
}
|
|
77
|
+
return ` ticlawk connect --adapter telegram --type ${runtime.name} --workdir /path/to/project`;
|
|
78
|
+
}),
|
|
79
|
+
].join('\n');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getUsageText() {
|
|
83
|
+
const configKeys = getConfigKeyUsage();
|
|
84
|
+
return `ticlawk
|
|
85
|
+
|
|
86
|
+
Usage:
|
|
87
|
+
ticlawk help
|
|
88
|
+
ticlawk help <command>
|
|
89
|
+
ticlawk start
|
|
90
|
+
ticlawk health
|
|
91
|
+
ticlawk config
|
|
92
|
+
ticlawk config get <${configKeys}>
|
|
93
|
+
ticlawk config set <${configKeys}> <value>
|
|
94
|
+
ticlawk auth <adapter> [adapter-auth-args...]
|
|
95
|
+
ticlawk connect [ticlawk] [codex|claude|opencode|openclaw|pi] [runtime args...]
|
|
96
|
+
ticlawk connect [--adapter <adapter>] [--session-id <id>] --workdir <dir> [--switch-user]
|
|
97
|
+
${getRuntimeConnectUsageLines()}
|
|
98
|
+
ticlawk profile list
|
|
99
|
+
ticlawk profile current
|
|
100
|
+
ticlawk profile use ticlawk:<user-id>
|
|
101
|
+
ticlawk install-daemon
|
|
102
|
+
ticlawk update [-y]
|
|
103
|
+
ticlawk uninstall [-y]
|
|
104
|
+
ticlawk version
|
|
105
|
+
|
|
106
|
+
Commands:
|
|
107
|
+
auth store adapter-specific auth/setup input locally
|
|
108
|
+
connect connect the selected adapter to a local runtime
|
|
109
|
+
profile list or switch saved local identities
|
|
110
|
+
install-daemon install or refresh the background daemon
|
|
111
|
+
update update the npm package and refresh the daemon
|
|
112
|
+
uninstall remove service and legacy install files, preserving ~/.ticlawk
|
|
113
|
+
|
|
114
|
+
Config examples:
|
|
115
|
+
ticlawk config get adapter
|
|
116
|
+
ticlawk config set adapter telegram
|
|
117
|
+
ticlawk config set adapter ticlawk
|
|
118
|
+
ticlawk config set streaming off # turn streaming off for all runtimes
|
|
119
|
+
ticlawk config set streaming.codex default # clear the codex-specific override and inherit the global streaming setting
|
|
120
|
+
${getRuntimeConfigExamples()}
|
|
121
|
+
ticlawk config set telegram.bot-token <bot-token> # advanced/manual
|
|
122
|
+
ticlawk config set ticlawk.connector-api-key <key> # advanced/manual
|
|
123
|
+
ticlawk config set ticlawk.api-url <api-url> # advanced/manual
|
|
124
|
+
ticlawk config set ticlawk.connector-ws-url <url> # advanced/manual
|
|
125
|
+
|
|
126
|
+
Examples:
|
|
127
|
+
ticlawk connect
|
|
128
|
+
ticlawk connect ticlawk
|
|
129
|
+
ticlawk connect ticlawk codex
|
|
130
|
+
ticlawk connect ticlawk openclaw --agent-id main
|
|
131
|
+
ticlawk auth telegram --bot-token <bot-token>
|
|
132
|
+
${getRuntimeConnectExamples()}
|
|
133
|
+
`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getAuthHelp() {
|
|
137
|
+
return `ticlawk auth
|
|
138
|
+
|
|
139
|
+
Use \`auth\` to store adapter-specific auth/setup input locally.
|
|
140
|
+
Adapter-specific args live after \`--\` so new adapters can define their own flags
|
|
141
|
+
without changing the top-level CLI.
|
|
142
|
+
|
|
143
|
+
Examples:
|
|
144
|
+
ticlawk auth telegram --bot-token <bot-token>
|
|
145
|
+
|
|
146
|
+
Per-adapter help:
|
|
147
|
+
ticlawk auth telegram --help
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function getConnectHelp() {
|
|
152
|
+
return `ticlawk connect
|
|
153
|
+
|
|
154
|
+
Use \`connect\` to connect the selected adapter to a local runtime.
|
|
155
|
+
For ticlawk, connect starts the QR pairing flow when no local credential exists.
|
|
156
|
+
Run \`ticlawk connect\` from a project directory to auto-detect
|
|
157
|
+
Codex, Claude Code, opencode, and pi, then choose the runtime in ticlawk.
|
|
158
|
+
By default connect records the runtime binary found in your current terminal.
|
|
159
|
+
Use \`--runtime-path <path>\` only when you need to override that discovery.
|
|
160
|
+
|
|
161
|
+
Examples:
|
|
162
|
+
ticlawk connect
|
|
163
|
+
ticlawk connect ticlawk
|
|
164
|
+
ticlawk connect ticlawk codex
|
|
165
|
+
ticlawk connect ticlawk openclaw --agent-id main
|
|
166
|
+
${getRuntimeConnectExamples()}
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function printUsage() {
|
|
171
|
+
console.log(getUsageText());
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function printHelp(helpPath, args = {}) {
|
|
175
|
+
const [head] = helpPath;
|
|
176
|
+
if (!head) {
|
|
177
|
+
printUsage();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (head === 'auth') {
|
|
181
|
+
const adapterValue = helpPath[1] || args._?.[1];
|
|
182
|
+
if (adapterValue) {
|
|
183
|
+
console.log(getAdapterAuthHelp(requireAdapter(adapterValue)));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
console.log(getAuthHelp());
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (head === 'connect') {
|
|
190
|
+
console.log(getConnectHelp());
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (head === 'uninstall') {
|
|
194
|
+
console.log(getUninstallHelp());
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (head === 'install-daemon') {
|
|
198
|
+
console.log(getInstallDaemonHelp());
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
printUsage();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function formatStreamingValue(value, inherited = false) {
|
|
205
|
+
const text = value ? 'on' : 'off';
|
|
206
|
+
return inherited ? `default(${text})` : text;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function parseConfigBoolean(value) {
|
|
210
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
211
|
+
if (['1', 'true', 'on', 'yes'].includes(normalized)) return '1';
|
|
212
|
+
if (['0', 'false', 'off', 'no'].includes(normalized)) return '0';
|
|
213
|
+
if (normalized === 'default') return null;
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function printConfigView(config = loadPersistentConfig()) {
|
|
218
|
+
console.log(`adapter=${getConfiguredAdapter(config)}`);
|
|
219
|
+
const view = getStreamingConfigView(config);
|
|
220
|
+
console.log(`streaming=${formatStreamingValue(view.streaming.value, view.streaming.inherited)}`);
|
|
221
|
+
for (const runtime of RUNTIME_DEFINITIONS) {
|
|
222
|
+
const key = `streaming.${runtime.name}`;
|
|
223
|
+
if (!view[key]) continue;
|
|
224
|
+
console.log(`${key}=${formatStreamingValue(view[key].value, view[key].inherited)}`);
|
|
225
|
+
}
|
|
226
|
+
for (const runtime of getRuntimeExecutableDefinitions()) {
|
|
227
|
+
console.log(`${runtime.executableCliKey}=${config[runtime.executableConfigKey] || ''}`);
|
|
228
|
+
}
|
|
229
|
+
console.log(`telegram.bot-token=${config.TELEGRAM_BOT_TOKEN ? 'set' : 'unset'}`);
|
|
230
|
+
console.log(`ticlawk.connector-api-key=${config[TICLAWK_CONNECTOR_API_KEY] || config[LEGACY_TICLAWK_API_KEY] ? 'set' : 'unset'}`);
|
|
231
|
+
console.log(`ticlawk.api-url=${config.TICLAWK_API_URL || ''}`);
|
|
232
|
+
console.log(`ticlawk.connector-ws-url=${config[TICLAWK_CONNECTOR_WS_URL] || ''}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function getLocal(path) {
|
|
236
|
+
return new Promise((resolve, reject) => {
|
|
237
|
+
const req = request(
|
|
238
|
+
{ host: '127.0.0.1', port: TICLAWK_PORT, path, method: 'GET' },
|
|
239
|
+
(res) => {
|
|
240
|
+
let data = '';
|
|
241
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
242
|
+
res.on('end', () => {
|
|
243
|
+
try {
|
|
244
|
+
resolve({
|
|
245
|
+
statusCode: res.statusCode || 0,
|
|
246
|
+
body: data ? JSON.parse(data) : {},
|
|
247
|
+
});
|
|
248
|
+
} catch (err) {
|
|
249
|
+
reject(err);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
req.on('error', reject);
|
|
255
|
+
req.end();
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function formatSecretConfigValue(configKey, value) {
|
|
260
|
+
if (configKey === 'TELEGRAM_BOT_TOKEN' || configKey === TICLAWK_CONNECTOR_API_KEY || configKey === LEGACY_TICLAWK_API_KEY) {
|
|
261
|
+
return value ? 'set' : 'unset';
|
|
262
|
+
}
|
|
263
|
+
return value || '';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function requireAdapter(value) {
|
|
267
|
+
const adapterId = normalizeAdapterName(value);
|
|
268
|
+
if (!adapterId) {
|
|
269
|
+
const supported = SUPPORTED_ADAPTERS.join(', ');
|
|
270
|
+
console.error(`invalid adapter: ${value || ''} (supported: ${supported})`);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
return adapterId;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function getConnectAdapter(value) {
|
|
277
|
+
if (value === undefined || value === null || value === '') {
|
|
278
|
+
return getConfiguredAdapter();
|
|
279
|
+
}
|
|
280
|
+
return requireAdapter(value);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function buildConnectPayload(args) {
|
|
284
|
+
const positionalAdapter = normalizeAdapterName(args._?.[1]) ? args._[1] : null;
|
|
285
|
+
const positionalRuntime = positionalAdapter ? args._?.[2] : args._?.[1];
|
|
286
|
+
const serviceType = normalizeServiceType(args.type || positionalRuntime);
|
|
287
|
+
const explicitWorkdir = args.workdir || args.cwd || args['project-dir'];
|
|
288
|
+
const workdir = explicitWorkdir || (positionalAdapter ? resolve('.') : null);
|
|
289
|
+
const hasExplicitAdapter = Boolean(args.adapter || positionalAdapter);
|
|
290
|
+
const adapter = (!serviceType && !hasExplicitAdapter)
|
|
291
|
+
? 'ticlawk'
|
|
292
|
+
: getConnectAdapter(args.adapter || positionalAdapter);
|
|
293
|
+
if (!serviceType) {
|
|
294
|
+
if (adapter === 'ticlawk' && !args.type && !positionalRuntime) {
|
|
295
|
+
return {
|
|
296
|
+
adapter,
|
|
297
|
+
autoRuntime: true,
|
|
298
|
+
switchUser: Boolean(args['switch-user']),
|
|
299
|
+
...(workdir ? { workdir } : {}),
|
|
300
|
+
...(args.name ? { name: args.name } : {}),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
console.error(`invalid runtime type: ${args.type || positionalRuntime || ''}`);
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const runtimeDefinition = getRuntimeDefinition(serviceType);
|
|
308
|
+
if (runtimeDefinition?.connect?.locator === 'agentId') {
|
|
309
|
+
return {
|
|
310
|
+
adapter,
|
|
311
|
+
serviceType,
|
|
312
|
+
agentId: args['agent-id'],
|
|
313
|
+
...(workdir ? { workdir } : {}),
|
|
314
|
+
...(args['runtime-path'] ? { runtimePath: resolve(args['runtime-path']) } : {}),
|
|
315
|
+
name: args.name || args['agent-id'],
|
|
316
|
+
switchUser: Boolean(args['switch-user']),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
adapter,
|
|
322
|
+
serviceType,
|
|
323
|
+
sessionId: args['session-id'],
|
|
324
|
+
switchUser: Boolean(args['switch-user']),
|
|
325
|
+
...(workdir ? { workdir } : {}),
|
|
326
|
+
...(args.name ? { name: args.name } : {}),
|
|
327
|
+
...(args['runtime-path'] ? { runtimePath: resolve(args['runtime-path']) } : {}),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function validateConnectPayload(payload) {
|
|
332
|
+
if (payload.autoRuntime) return;
|
|
333
|
+
const runtimeDefinition = getRuntimeDefinition(payload.serviceType);
|
|
334
|
+
if (runtimeDefinition?.connect?.locator === 'agentId' && !payload.agentId) {
|
|
335
|
+
console.error(`--agent-id is required for ${getRuntimeDisplayName(payload.serviceType)} connect`);
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
if (runtimeDefinition?.connect?.locator !== 'agentId' && !payload.sessionId && !payload.workdir) {
|
|
339
|
+
const runtimeName = getRuntimeDisplayName(payload.serviceType);
|
|
340
|
+
console.error(`either --session-id or --workdir is required for ${runtimeName} connect`);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function printCommandResult(result) {
|
|
346
|
+
if (result?.body?.help) {
|
|
347
|
+
console.log(result.body.help);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
console.log(JSON.stringify(result?.body || {}, null, 2));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function formatProfileIdentity(profile) {
|
|
354
|
+
const meta = profile.meta || {};
|
|
355
|
+
const parts = [
|
|
356
|
+
profile.ref,
|
|
357
|
+
meta.emailMasked || meta.email_masked || '',
|
|
358
|
+
meta.phoneMasked || meta.phone_masked || '',
|
|
359
|
+
].filter(Boolean);
|
|
360
|
+
return parts.join(' ');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function parseProfileUseRef(value) {
|
|
364
|
+
const raw = String(value || '').trim();
|
|
365
|
+
const [adapter, ...rest] = raw.split(':');
|
|
366
|
+
const userId = rest.join(':');
|
|
367
|
+
if (!adapter || !userId) return null;
|
|
368
|
+
return { adapter, userId };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async function main() {
|
|
372
|
+
const rawArgv = process.argv.slice(2);
|
|
373
|
+
const args = parseOptionArgs(rawArgv);
|
|
374
|
+
const command = args._[0] || 'start';
|
|
375
|
+
|
|
376
|
+
if (command === 'help' || args.help || args.h) {
|
|
377
|
+
const helpPath = command === 'help' ? args._.slice(1) : args._;
|
|
378
|
+
printHelp(helpPath, args);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (command === 'version' || args.version) {
|
|
383
|
+
console.log(readPkgVersion());
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (command === 'start') {
|
|
388
|
+
installProcessDiagnostics();
|
|
389
|
+
const { startTiclawk } = await import('../ticlawk.mjs');
|
|
390
|
+
await startTiclawk();
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (command === 'health') {
|
|
395
|
+
const res = await getLocal('/health');
|
|
396
|
+
console.log(JSON.stringify(res.body, null, 2));
|
|
397
|
+
process.exitCode = res.statusCode >= 400 ? 1 : 0;
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (command === 'config') {
|
|
402
|
+
const subcommand = args._[1];
|
|
403
|
+
const config = loadPersistentConfig();
|
|
404
|
+
if (!subcommand) {
|
|
405
|
+
printConfigView(config);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (subcommand === 'get') {
|
|
410
|
+
const key = args._[2];
|
|
411
|
+
if (key === 'adapter') {
|
|
412
|
+
console.log(getConfiguredAdapter(config));
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const adapterTarget = normalizeAdapterConfigTarget(key);
|
|
416
|
+
if (adapterTarget) {
|
|
417
|
+
console.log(formatSecretConfigValue(adapterTarget.configKey, config[adapterTarget.configKey]));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const runtimeTarget = normalizeRuntimeConfigTarget(key);
|
|
421
|
+
if (runtimeTarget) {
|
|
422
|
+
console.log(config[runtimeTarget.configKey] || '');
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const target = normalizeStreamingTarget(key);
|
|
426
|
+
if (!target) {
|
|
427
|
+
console.error(`unknown config key: ${key || ''}`);
|
|
428
|
+
process.exit(1);
|
|
429
|
+
}
|
|
430
|
+
const view = getStreamingConfigView();
|
|
431
|
+
const current = view[target.key];
|
|
432
|
+
console.log(formatStreamingValue(current.value, current.inherited));
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (subcommand === 'set') {
|
|
437
|
+
const key = args._[2];
|
|
438
|
+
const rawValue = args._[3];
|
|
439
|
+
if (key === 'adapter') {
|
|
440
|
+
if (!rawValue) {
|
|
441
|
+
console.error('config set adapter requires a value');
|
|
442
|
+
process.exit(1);
|
|
443
|
+
}
|
|
444
|
+
const adapterId = requireAdapter(rawValue);
|
|
445
|
+
persistConfig({ [AF_ADAPTER_KEY]: adapterId });
|
|
446
|
+
console.log(`adapter=${adapterId}`);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const adapterTarget = normalizeAdapterConfigTarget(key);
|
|
450
|
+
if (adapterTarget) {
|
|
451
|
+
if (!rawValue) {
|
|
452
|
+
console.error(`config set ${key} requires a value`);
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
persistConfig({ [adapterTarget.configKey]: rawValue });
|
|
456
|
+
console.log(`${adapterTarget.key}=${formatSecretConfigValue(adapterTarget.configKey, rawValue)}`);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const runtimeTarget = normalizeRuntimeConfigTarget(key);
|
|
460
|
+
if (runtimeTarget) {
|
|
461
|
+
if (!rawValue) {
|
|
462
|
+
console.error(`config set ${key} requires a value`);
|
|
463
|
+
process.exit(1);
|
|
464
|
+
}
|
|
465
|
+
persistConfig({ [runtimeTarget.configKey]: rawValue === 'default' ? '' : rawValue });
|
|
466
|
+
console.log(`${runtimeTarget.key}=${rawValue === 'default' ? '' : rawValue}`);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const target = normalizeStreamingTarget(key);
|
|
470
|
+
if (!target) {
|
|
471
|
+
console.error(`unknown config key: ${key || ''}`);
|
|
472
|
+
process.exit(1);
|
|
473
|
+
}
|
|
474
|
+
if (!rawValue) {
|
|
475
|
+
console.error('config set requires a value');
|
|
476
|
+
process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
const parsedValue = parseConfigBoolean(rawValue);
|
|
479
|
+
if (parsedValue === undefined) {
|
|
480
|
+
console.error(`invalid config value: ${rawValue}`);
|
|
481
|
+
process.exit(1);
|
|
482
|
+
}
|
|
483
|
+
persistConfig({ [target.configKey]: parsedValue });
|
|
484
|
+
const view = getStreamingConfigView(loadPersistentConfig());
|
|
485
|
+
const currentKey = target.key;
|
|
486
|
+
const current = view[currentKey];
|
|
487
|
+
console.log(`${currentKey}=${formatStreamingValue(current.value, current.inherited)}`);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
console.error(`unknown config subcommand: ${subcommand}`);
|
|
492
|
+
process.exit(1);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (command === 'profile') {
|
|
496
|
+
const subcommand = args._[1] || 'list';
|
|
497
|
+
const active = getActiveProfile();
|
|
498
|
+
if (subcommand === 'current') {
|
|
499
|
+
console.log(active?.ref || 'none');
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (subcommand === 'list') {
|
|
503
|
+
const profiles = listProfiles();
|
|
504
|
+
console.log(`Active profile: ${active?.ref || 'none'}`);
|
|
505
|
+
if (!profiles.length) {
|
|
506
|
+
console.log('No saved profiles.');
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
for (const profile of profiles) {
|
|
510
|
+
const marker = active?.ref === profile.ref ? '*' : ' ';
|
|
511
|
+
console.log(`${marker} ${formatProfileIdentity(profile)}`);
|
|
512
|
+
}
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
if (subcommand === 'use') {
|
|
516
|
+
const target = parseProfileUseRef(args._[2]);
|
|
517
|
+
if (!target) {
|
|
518
|
+
console.error('profile use requires a profile ref like ticlawk:<user-id>');
|
|
519
|
+
process.exit(1);
|
|
520
|
+
}
|
|
521
|
+
const activated = activateProfile(target.adapter, target.userId);
|
|
522
|
+
console.log(`Active profile: ${activated.ref}`);
|
|
523
|
+
console.log('Restart ticlawk for the daemon to use the selected profile.');
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
console.error(`unknown profile subcommand: ${subcommand}`);
|
|
527
|
+
process.exit(1);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (command === 'update') {
|
|
531
|
+
await runSelfUpdate(Boolean(args.y || args.yes));
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (command === 'install-daemon') {
|
|
536
|
+
if (args.help || args.h) {
|
|
537
|
+
console.log(getInstallDaemonHelp());
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
await runInstallDaemon();
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (command === 'setup-readiness') {
|
|
545
|
+
await runSetupReadiness({ ensureDaemon: Boolean(args['ensure-daemon']) });
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (command === 'uninstall') {
|
|
550
|
+
if (args.help || args.h) {
|
|
551
|
+
console.log(getUninstallHelp());
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
await runSelfUninstall(Boolean(args.y || args.yes));
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (command === 'auth') {
|
|
559
|
+
if (args.help || args.h) {
|
|
560
|
+
printHelp(['auth'], args);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const adapterId = requireAdapter(args._[1]);
|
|
564
|
+
const adapterArgv = rawArgv.slice(2);
|
|
565
|
+
const res = await runAdapterAuth(adapterId, adapterArgv);
|
|
566
|
+
printCommandResult(res);
|
|
567
|
+
process.exitCode = res.statusCode >= 400 ? 1 : 0;
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (command === 'connect') {
|
|
572
|
+
if (args.help || args.h) {
|
|
573
|
+
console.log(getConnectHelp());
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const payload = buildConnectPayload(args);
|
|
577
|
+
validateConnectPayload(payload);
|
|
578
|
+
const res = await runTiclawkConnect(payload);
|
|
579
|
+
printCommandResult(res);
|
|
580
|
+
if (res.statusCode < 400) {
|
|
581
|
+
await runSetupReadiness({ ensureDaemon: true });
|
|
582
|
+
}
|
|
583
|
+
process.exitCode = res.statusCode >= 400 ? 1 : 0;
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
printUsage();
|
|
588
|
+
process.exit(1);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
main().catch((err) => {
|
|
592
|
+
console.error(err.message || String(err));
|
|
593
|
+
process.exit(1);
|
|
594
|
+
});
|
package/cc-watcher.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ticlawk",
|
|
3
|
+
"version": "0.1.12-dev.0",
|
|
4
|
+
"description": "Connect local agent harnesses to Ticlawk, Telegram, and other mobile clients.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "ticlawk.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./ticlawk.mjs",
|
|
9
|
+
"./package.json": "./package.json"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"ticlawk": "bin/ticlawk.mjs",
|
|
13
|
+
"agent-freeway": "bin/agent-freeway.mjs"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"assets/",
|
|
17
|
+
"bin/",
|
|
18
|
+
"scripts/postinstall.mjs",
|
|
19
|
+
"src/",
|
|
20
|
+
"cc-watcher.mjs",
|
|
21
|
+
"ticlawk.mjs",
|
|
22
|
+
"agent-freeway.mjs",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
34
|
+
"start": "node ./bin/ticlawk.mjs start",
|
|
35
|
+
"health": "node ./bin/ticlawk.mjs health",
|
|
36
|
+
"dev": "echo 'Configure ~/.ticlawk/.config, then: npm run start'",
|
|
37
|
+
"generate:usage-docs": "node scripts/generate-usage-docs.mjs",
|
|
38
|
+
"verify:public-package": "bash scripts/verify-ticlawk-public.sh"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/darthjaja6/ticlawk.git"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"ticlawk",
|
|
46
|
+
"claude-code",
|
|
47
|
+
"claude",
|
|
48
|
+
"anthropic",
|
|
49
|
+
"openclaw",
|
|
50
|
+
"codex",
|
|
51
|
+
"agent",
|
|
52
|
+
"ai-agent",
|
|
53
|
+
"telegram",
|
|
54
|
+
"telegram-bot",
|
|
55
|
+
"chatops",
|
|
56
|
+
"remote-agent",
|
|
57
|
+
"mobile",
|
|
58
|
+
"bridge",
|
|
59
|
+
"gateway"
|
|
60
|
+
],
|
|
61
|
+
"author": "darthjaja6",
|
|
62
|
+
"license": "ISC",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/darthjaja6/ticlawk/issues"
|
|
65
|
+
},
|
|
66
|
+
"homepage": "https://github.com/darthjaja6/ticlawk#readme",
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"dotenv": "^17.4.2",
|
|
69
|
+
"qrcode-terminal": "^0.12.0",
|
|
70
|
+
"ws": "^8.20.0"
|
|
71
|
+
}
|
|
72
|
+
}
|