appback-remoteagent 0.13.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/.env.example +39 -0
- package/LICENSE +21 -0
- package/README.md +371 -0
- package/bin/remoteagent.js +2 -0
- package/dist/adapters/claude-adapter.js +78 -0
- package/dist/adapters/codex-adapter.js +241 -0
- package/dist/adapters/provider-adapter.js +1 -0
- package/dist/adapters/shell-adapter.js +44 -0
- package/dist/adapters/windows-shell.js +111 -0
- package/dist/bot.js +2135 -0
- package/dist/config.js +170 -0
- package/dist/index.js +534 -0
- package/dist/secret-helper.js +24 -0
- package/dist/services/agent-memory-service.js +737 -0
- package/dist/services/bot-management-service.js +626 -0
- package/dist/services/bridge-service.js +807 -0
- package/dist/services/local-ui-service.js +533 -0
- package/dist/services/provider-setup-service.js +284 -0
- package/dist/services/remote-shell-service.js +97 -0
- package/dist/store/file-store.js +690 -0
- package/dist/telegram-fetch.js +85 -0
- package/dist/types.js +1 -0
- package/docs/ARCHITECTURE.md +170 -0
- package/docs/COKACDIR_NOTES.md +79 -0
- package/docs/ERROR_NORMALIZATION.md +46 -0
- package/docs/MINI_APP.md +112 -0
- package/docs/MVP.md +108 -0
- package/docs/OPERATIONS.md +181 -0
- package/docs/RELEASING.md +87 -0
- package/docs/SESSION_DIRECTORY_PLAN.md +506 -0
- package/package.json +47 -0
- package/scripts/bump-version.sh +23 -0
- package/scripts/finish-claude-login.sh +48 -0
- package/scripts/install-claude.sh +6 -0
- package/scripts/install-codex.sh +8 -0
- package/scripts/install.ps1 +51 -0
- package/scripts/install.sh +101 -0
- package/scripts/mock-adapter.sh +7 -0
- package/scripts/restart-after-bot-op.sh +118 -0
- package/scripts/selftest-telegram-update.mjs +359 -0
- package/scripts/start-claude-login.sh +4 -0
- package/scripts/start.ps1 +39 -0
- package/scripts/start.sh +54 -0
- package/scripts/stop.ps1 +40 -0
- package/scripts/stop.sh +39 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
# Session Directory Plan
|
|
2
|
+
|
|
3
|
+
## Decision
|
|
4
|
+
|
|
5
|
+
RemoteAgent should move to a hybrid model:
|
|
6
|
+
|
|
7
|
+
- session-owned storage as the source of truth
|
|
8
|
+
- bot-scoped bindings as the entry point
|
|
9
|
+
|
|
10
|
+
In practice:
|
|
11
|
+
|
|
12
|
+
- directories are owned by RemoteAgent sessions
|
|
13
|
+
- each Telegram bot keeps its own chat bindings and defaults
|
|
14
|
+
- a bot/chat points to a current session
|
|
15
|
+
|
|
16
|
+
This matches the core product goal better than bot-owned directories because the main UX target is continuing the same work across:
|
|
17
|
+
|
|
18
|
+
- work PC UI
|
|
19
|
+
- Telegram bot A
|
|
20
|
+
- Telegram bot B
|
|
21
|
+
|
|
22
|
+
without fragmenting one task into separate storage roots.
|
|
23
|
+
|
|
24
|
+
## Why not bot-owned directories?
|
|
25
|
+
|
|
26
|
+
Bot-owned directories make sense for policy isolation, but they are a weaker fit for the primary workflow:
|
|
27
|
+
|
|
28
|
+
1. the same work should survive switching between PC and Telegram
|
|
29
|
+
2. the same work may later move between different Telegram bots
|
|
30
|
+
3. future PC UI should not be forced to pretend to be a bot
|
|
31
|
+
|
|
32
|
+
If storage is bot-owned, the system tends to duplicate:
|
|
33
|
+
|
|
34
|
+
- event history
|
|
35
|
+
- session metadata
|
|
36
|
+
- attachments
|
|
37
|
+
- provider bindings
|
|
38
|
+
|
|
39
|
+
and cross-bot continuation becomes an explicit migration instead of a normal action.
|
|
40
|
+
|
|
41
|
+
## Recommended model
|
|
42
|
+
|
|
43
|
+
### Source of truth
|
|
44
|
+
|
|
45
|
+
The source of truth should be the RemoteAgent session.
|
|
46
|
+
|
|
47
|
+
Each session owns:
|
|
48
|
+
|
|
49
|
+
- workspace metadata
|
|
50
|
+
- provider bindings
|
|
51
|
+
- event history
|
|
52
|
+
- attachments and exports
|
|
53
|
+
- derived session state
|
|
54
|
+
|
|
55
|
+
### Bot responsibility
|
|
56
|
+
|
|
57
|
+
Each Telegram bot should own:
|
|
58
|
+
|
|
59
|
+
- bot identity and policy
|
|
60
|
+
- bot-level defaults
|
|
61
|
+
- mappings from `(platform, bot, chat)` to `sessionId`
|
|
62
|
+
|
|
63
|
+
That gives the desired UX:
|
|
64
|
+
|
|
65
|
+
- users feel each bot is separately managed
|
|
66
|
+
- the underlying work remains session-centric
|
|
67
|
+
|
|
68
|
+
## Target data model
|
|
69
|
+
|
|
70
|
+
### 1. BotProfile
|
|
71
|
+
|
|
72
|
+
Represents one configured Telegram bot.
|
|
73
|
+
|
|
74
|
+
Suggested fields:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
type BotProfile = {
|
|
78
|
+
botId: string; // internal id, usually Telegram bot user id or username
|
|
79
|
+
platform: "telegram";
|
|
80
|
+
username?: string; // e.g. codex_remoteagent_bot
|
|
81
|
+
tokenLabel?: string; // optional human-friendly label, never the secret token
|
|
82
|
+
ownerUserId?: string;
|
|
83
|
+
enabled: boolean;
|
|
84
|
+
defaults: {
|
|
85
|
+
mode: BridgeMode;
|
|
86
|
+
workspace?: string;
|
|
87
|
+
provider?: Provider;
|
|
88
|
+
codexSandboxMode?: CodexSandboxMode;
|
|
89
|
+
};
|
|
90
|
+
policy: {
|
|
91
|
+
allowRemoteShell: boolean;
|
|
92
|
+
privateOnly: boolean;
|
|
93
|
+
};
|
|
94
|
+
createdAt: string;
|
|
95
|
+
updatedAt: string;
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 2. ChannelBinding
|
|
100
|
+
|
|
101
|
+
Represents one external client channel bound to a RemoteAgent session.
|
|
102
|
+
|
|
103
|
+
Suggested fields:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
type ChannelBinding = {
|
|
107
|
+
bindingId: string;
|
|
108
|
+
platform: "telegram" | "pc-ui";
|
|
109
|
+
botId?: string; // required for telegram, omitted for local UI
|
|
110
|
+
chatId: string; // Telegram chat id or PC UI client id
|
|
111
|
+
sessionId: string;
|
|
112
|
+
title?: string;
|
|
113
|
+
state: "active" | "archived";
|
|
114
|
+
boundAt: string;
|
|
115
|
+
updatedAt: string;
|
|
116
|
+
};
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Key change from current model:
|
|
120
|
+
|
|
121
|
+
- today the top-level mapping is effectively `chatId -> sessionId`
|
|
122
|
+
- target model is `(platform, botId, chatId) -> sessionId`
|
|
123
|
+
|
|
124
|
+
This is the critical change needed for multi-bot correctness.
|
|
125
|
+
|
|
126
|
+
### 3. SessionRecord
|
|
127
|
+
|
|
128
|
+
Represents the canonical RemoteAgent session.
|
|
129
|
+
|
|
130
|
+
Suggested fields:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
type SessionRecord = {
|
|
134
|
+
sessionId: string;
|
|
135
|
+
title?: string;
|
|
136
|
+
mode: BridgeMode;
|
|
137
|
+
workspace: string;
|
|
138
|
+
status: "active" | "archived";
|
|
139
|
+
activeChannel?: {
|
|
140
|
+
platform: "telegram" | "pc-ui";
|
|
141
|
+
botId?: string;
|
|
142
|
+
chatId: string;
|
|
143
|
+
};
|
|
144
|
+
codex?: ProviderSession;
|
|
145
|
+
claude?: ProviderSession;
|
|
146
|
+
createdAt: string;
|
|
147
|
+
updatedAt: string;
|
|
148
|
+
archivedAt?: string;
|
|
149
|
+
};
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 4. ProviderSession
|
|
153
|
+
|
|
154
|
+
This stays similar to the current model, but should become session-owned only.
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
type ProviderSession = {
|
|
158
|
+
provider: Provider;
|
|
159
|
+
cwd: string;
|
|
160
|
+
pairedAt: string;
|
|
161
|
+
sessionId?: string; // provider's own id
|
|
162
|
+
model?: string;
|
|
163
|
+
lastUsedAt?: string;
|
|
164
|
+
sandboxMode?: CodexSandboxMode;
|
|
165
|
+
};
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 5. SessionEvent
|
|
169
|
+
|
|
170
|
+
This replaces the current flat JSONL log shape conceptually, even if JSONL stays as the storage format.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
type SessionEvent = {
|
|
174
|
+
eventId: string;
|
|
175
|
+
sessionId: string;
|
|
176
|
+
timestamp: string;
|
|
177
|
+
source: "telegram" | "pc-ui" | "codex" | "claude" | "system";
|
|
178
|
+
direction: "in" | "out" | "system";
|
|
179
|
+
actor?: {
|
|
180
|
+
botId?: string;
|
|
181
|
+
chatId?: string;
|
|
182
|
+
providerSessionId?: string;
|
|
183
|
+
};
|
|
184
|
+
text: string;
|
|
185
|
+
metadata?: Record<string, string | number | boolean | null>;
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Target on-disk layout
|
|
190
|
+
|
|
191
|
+
Today the installed runtime stores most state in:
|
|
192
|
+
|
|
193
|
+
- `state.json`
|
|
194
|
+
- `logs/<sessionId>.jsonl`
|
|
195
|
+
|
|
196
|
+
Target layout should become:
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
~/.remoteagent/
|
|
200
|
+
config/
|
|
201
|
+
bots.json
|
|
202
|
+
channels/
|
|
203
|
+
telegram/
|
|
204
|
+
<bot-id>/
|
|
205
|
+
<chat-id>.json
|
|
206
|
+
pc-ui/
|
|
207
|
+
<client-id>.json
|
|
208
|
+
sessions/
|
|
209
|
+
<session-id>/
|
|
210
|
+
session.json
|
|
211
|
+
events.jsonl
|
|
212
|
+
attachments/
|
|
213
|
+
exports/
|
|
214
|
+
migrations/
|
|
215
|
+
applied.json
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Directory ownership
|
|
219
|
+
|
|
220
|
+
#### `config/bots.json`
|
|
221
|
+
|
|
222
|
+
Stores configured bot profiles and bot-level defaults.
|
|
223
|
+
|
|
224
|
+
#### `channels/telegram/<bot-id>/<chat-id>.json`
|
|
225
|
+
|
|
226
|
+
Stores one Telegram chat binding per bot.
|
|
227
|
+
|
|
228
|
+
This is the main answer to the user's requirement:
|
|
229
|
+
|
|
230
|
+
- Telegram bot A and Telegram bot B are managed separately
|
|
231
|
+
- but they can still point to the same `sessionId`
|
|
232
|
+
|
|
233
|
+
#### `sessions/<session-id>/session.json`
|
|
234
|
+
|
|
235
|
+
Stores canonical session metadata.
|
|
236
|
+
|
|
237
|
+
#### `sessions/<session-id>/events.jsonl`
|
|
238
|
+
|
|
239
|
+
Stores append-only history for that one session.
|
|
240
|
+
|
|
241
|
+
#### `sessions/<session-id>/attachments/`
|
|
242
|
+
|
|
243
|
+
Stores future uploaded files, screenshots, exports, and attachment metadata.
|
|
244
|
+
|
|
245
|
+
## Example
|
|
246
|
+
|
|
247
|
+
One user may have:
|
|
248
|
+
|
|
249
|
+
- `@codex_remoteagent_bot`
|
|
250
|
+
- `@sqream_bot`
|
|
251
|
+
|
|
252
|
+
Both can point at the same session:
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
channels/telegram/codex_remoteagent_bot/8202993989.json -> sessionId=S1
|
|
256
|
+
channels/telegram/sqream_bot/8202993989.json -> sessionId=S1
|
|
257
|
+
sessions/S1/session.json
|
|
258
|
+
sessions/S1/events.jsonl
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
This preserves:
|
|
262
|
+
|
|
263
|
+
- separate bot routing
|
|
264
|
+
- shared work history
|
|
265
|
+
- one workspace and one session identity
|
|
266
|
+
|
|
267
|
+
## Required code changes
|
|
268
|
+
|
|
269
|
+
### A. Types
|
|
270
|
+
|
|
271
|
+
Current relevant file:
|
|
272
|
+
|
|
273
|
+
- `src/types.ts`
|
|
274
|
+
|
|
275
|
+
Required changes:
|
|
276
|
+
|
|
277
|
+
1. add `BotProfile`
|
|
278
|
+
2. replace `ChatBinding` with `ChannelBinding`
|
|
279
|
+
3. add `platform` and `botId`
|
|
280
|
+
4. split session event type from provider response type
|
|
281
|
+
5. add `status` and `activeChannel` to `SessionRecord`
|
|
282
|
+
|
|
283
|
+
### B. Store layer
|
|
284
|
+
|
|
285
|
+
Current relevant file:
|
|
286
|
+
|
|
287
|
+
- `src/store/file-store.ts`
|
|
288
|
+
|
|
289
|
+
Required changes:
|
|
290
|
+
|
|
291
|
+
1. stop using one `state.json` as the only state source
|
|
292
|
+
2. add directory-backed reads and writes for:
|
|
293
|
+
- bot profiles
|
|
294
|
+
- channel bindings
|
|
295
|
+
- session records
|
|
296
|
+
- session event logs
|
|
297
|
+
3. add lookup by:
|
|
298
|
+
- `sessionId`
|
|
299
|
+
- `(platform, botId, chatId)`
|
|
300
|
+
4. add session listing independent of Telegram chats
|
|
301
|
+
5. add migration from legacy `state.json`
|
|
302
|
+
|
|
303
|
+
### C. Bridge service
|
|
304
|
+
|
|
305
|
+
Current relevant file:
|
|
306
|
+
|
|
307
|
+
- `src/services/bridge-service.ts`
|
|
308
|
+
|
|
309
|
+
Required changes:
|
|
310
|
+
|
|
311
|
+
1. route messages through `(platform, botId, chatId)`
|
|
312
|
+
2. treat chat binding as a client pointer, not as session ownership
|
|
313
|
+
3. add APIs for:
|
|
314
|
+
- create session
|
|
315
|
+
- bind channel to existing session
|
|
316
|
+
- switch a bot/chat to another session
|
|
317
|
+
- list sessions for UI and Telegram commands
|
|
318
|
+
4. write all incoming and outgoing traffic as session events
|
|
319
|
+
|
|
320
|
+
### D. Bot runtime
|
|
321
|
+
|
|
322
|
+
Current relevant files:
|
|
323
|
+
|
|
324
|
+
- `src/index.ts`
|
|
325
|
+
- `src/bot.ts`
|
|
326
|
+
- `src/config.ts`
|
|
327
|
+
|
|
328
|
+
Required changes:
|
|
329
|
+
|
|
330
|
+
1. keep multiple Telegram bots running at once
|
|
331
|
+
2. assign each bot a stable `botId`
|
|
332
|
+
3. include that `botId` when calling the bridge
|
|
333
|
+
4. expose future bot-scoped commands like:
|
|
334
|
+
- `/session`
|
|
335
|
+
- `/new`
|
|
336
|
+
- `/switch`
|
|
337
|
+
- `/archive`
|
|
338
|
+
|
|
339
|
+
### E. Local UI
|
|
340
|
+
|
|
341
|
+
Current relevant file:
|
|
342
|
+
|
|
343
|
+
- `src/services/local-ui-service.ts`
|
|
344
|
+
|
|
345
|
+
Required changes:
|
|
346
|
+
|
|
347
|
+
1. list canonical sessions, not chats
|
|
348
|
+
2. show bound channels under a session
|
|
349
|
+
3. open one session and read `events.jsonl`
|
|
350
|
+
4. allow binding or rebinding Telegram chats later
|
|
351
|
+
|
|
352
|
+
## Migration plan
|
|
353
|
+
|
|
354
|
+
### Phase 1. Add the new directory model alongside legacy state
|
|
355
|
+
|
|
356
|
+
Goal:
|
|
357
|
+
|
|
358
|
+
- introduce new store layout without breaking the current bot flow
|
|
359
|
+
|
|
360
|
+
Work:
|
|
361
|
+
|
|
362
|
+
1. create `config/`, `channels/`, and `sessions/`
|
|
363
|
+
2. on startup, read legacy `state.json` if present
|
|
364
|
+
3. materialize equivalent:
|
|
365
|
+
- session directories
|
|
366
|
+
- Telegram channel binding files
|
|
367
|
+
4. keep writing both formats temporarily
|
|
368
|
+
|
|
369
|
+
### Phase 2. Switch reads to the new store
|
|
370
|
+
|
|
371
|
+
Goal:
|
|
372
|
+
|
|
373
|
+
- make directory-backed storage the primary source
|
|
374
|
+
|
|
375
|
+
Work:
|
|
376
|
+
|
|
377
|
+
1. read sessions from `sessions/<id>/session.json`
|
|
378
|
+
2. read Telegram bindings from `channels/telegram/<bot-id>/<chat-id>.json`
|
|
379
|
+
3. read events from per-session `events.jsonl`
|
|
380
|
+
4. stop depending on `state.json` for live behavior
|
|
381
|
+
|
|
382
|
+
### Phase 3. Remove dual-write and archive legacy state
|
|
383
|
+
|
|
384
|
+
Goal:
|
|
385
|
+
|
|
386
|
+
- simplify the runtime after migration is stable
|
|
387
|
+
|
|
388
|
+
Work:
|
|
389
|
+
|
|
390
|
+
1. write migration marker in `migrations/applied.json`
|
|
391
|
+
2. move legacy `state.json` to `state.legacy.json`
|
|
392
|
+
3. keep a recovery path, but no longer write the old format
|
|
393
|
+
|
|
394
|
+
## Telegram command implications
|
|
395
|
+
|
|
396
|
+
Current commands can stay, but semantics should be clarified.
|
|
397
|
+
|
|
398
|
+
### `/startpair codex [path]`
|
|
399
|
+
|
|
400
|
+
Recommended target behavior:
|
|
401
|
+
|
|
402
|
+
- create a new RemoteAgent session if the current bot/chat has no binding
|
|
403
|
+
- otherwise update the current session's Codex provider binding
|
|
404
|
+
|
|
405
|
+
### `/attach codex <thread_id> [path]`
|
|
406
|
+
|
|
407
|
+
Recommended target behavior:
|
|
408
|
+
|
|
409
|
+
- attach the provider binding inside the current session
|
|
410
|
+
- optionally support `--session <session-id>` later
|
|
411
|
+
|
|
412
|
+
### New commands worth adding
|
|
413
|
+
|
|
414
|
+
1. `/session`
|
|
415
|
+
- show the current bound session id and title
|
|
416
|
+
2. `/new [path]`
|
|
417
|
+
- create a new RemoteAgent session and bind this bot/chat to it
|
|
418
|
+
3. `/switch <session-id>`
|
|
419
|
+
- rebind this bot/chat to another existing session
|
|
420
|
+
4. `/sessions`
|
|
421
|
+
- list recent sessions available to this owner
|
|
422
|
+
5. `/archive [session-id]`
|
|
423
|
+
- archive a session without deleting history
|
|
424
|
+
|
|
425
|
+
## Recommendation for implementation order
|
|
426
|
+
|
|
427
|
+
### Step 1
|
|
428
|
+
|
|
429
|
+
Add `botId` into the live routing path first.
|
|
430
|
+
|
|
431
|
+
This fixes the biggest correctness gap introduced by multi-bot support.
|
|
432
|
+
|
|
433
|
+
### Step 2
|
|
434
|
+
|
|
435
|
+
Move from flat `state.json` to:
|
|
436
|
+
|
|
437
|
+
- `channels/telegram/<bot-id>/<chat-id>.json`
|
|
438
|
+
- `sessions/<session-id>/session.json`
|
|
439
|
+
|
|
440
|
+
### Step 3
|
|
441
|
+
|
|
442
|
+
Move logs from `logs/<sessionId>.jsonl` to:
|
|
443
|
+
|
|
444
|
+
- `sessions/<session-id>/events.jsonl`
|
|
445
|
+
|
|
446
|
+
### Step 4
|
|
447
|
+
|
|
448
|
+
Add session listing and session switching commands.
|
|
449
|
+
|
|
450
|
+
### Step 5
|
|
451
|
+
|
|
452
|
+
Teach the local UI to read sessions directly.
|
|
453
|
+
|
|
454
|
+
## Concrete MVP TODO
|
|
455
|
+
|
|
456
|
+
### Data model
|
|
457
|
+
|
|
458
|
+
- [ ] add `BotProfile`
|
|
459
|
+
- [ ] add `ChannelBinding`
|
|
460
|
+
- [ ] extend `SessionRecord` with `status`, `title`, and `activeChannel`
|
|
461
|
+
- [ ] rename `LogEntry` to a session-owned event model
|
|
462
|
+
|
|
463
|
+
### Storage
|
|
464
|
+
|
|
465
|
+
- [ ] add `config/bots.json`
|
|
466
|
+
- [ ] add `channels/telegram/<bot-id>/<chat-id>.json`
|
|
467
|
+
- [ ] add `sessions/<session-id>/session.json`
|
|
468
|
+
- [ ] add `sessions/<session-id>/events.jsonl`
|
|
469
|
+
- [ ] keep legacy import from `state.json`
|
|
470
|
+
|
|
471
|
+
### Runtime
|
|
472
|
+
|
|
473
|
+
- [ ] resolve stable `botId` from each initialized Telegram bot
|
|
474
|
+
- [ ] pass `botId` into every bridge operation
|
|
475
|
+
- [ ] update remote shell guardrails to be bot-aware
|
|
476
|
+
- [ ] update status formatting to show bot-scoped bindings
|
|
477
|
+
|
|
478
|
+
### Commands
|
|
479
|
+
|
|
480
|
+
- [ ] add `/session`
|
|
481
|
+
- [ ] add `/sessions`
|
|
482
|
+
- [ ] add `/new`
|
|
483
|
+
- [ ] add `/switch`
|
|
484
|
+
- [ ] add `/archive`
|
|
485
|
+
|
|
486
|
+
### UI
|
|
487
|
+
|
|
488
|
+
- [ ] list canonical sessions in the local UI
|
|
489
|
+
- [ ] show bound Telegram channels under each session
|
|
490
|
+
- [ ] open and read shared event history
|
|
491
|
+
|
|
492
|
+
### Migration
|
|
493
|
+
|
|
494
|
+
- [ ] auto-migrate legacy `state.json`
|
|
495
|
+
- [ ] dual-write temporarily
|
|
496
|
+
- [ ] cut over to directory-backed reads
|
|
497
|
+
- [ ] remove legacy write path
|
|
498
|
+
|
|
499
|
+
## Final recommendation
|
|
500
|
+
|
|
501
|
+
For RemoteAgent, the right answer is:
|
|
502
|
+
|
|
503
|
+
- manage Telegram bots separately at the binding layer
|
|
504
|
+
- manage work separately at the session layer
|
|
505
|
+
|
|
506
|
+
So the product should feel "bot-specific" from the user's point of view, while the storage model remains session-centric underneath.
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "appback-remoteagent",
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "Personal installable session server for continuing local AI work across PC and Telegram",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"remoteagent": "bin/remoteagent.js",
|
|
9
|
+
"remoteagent-install": "scripts/install.sh",
|
|
10
|
+
"remoteagent-start": "scripts/start.sh",
|
|
11
|
+
"remoteagent-stop": "scripts/stop.sh"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"dev": "tsx watch src/index.ts",
|
|
19
|
+
"start": "node dist/index.js",
|
|
20
|
+
"check": "tsc --noEmit -p tsconfig.json",
|
|
21
|
+
"selftest:telegram": "npm run build && node scripts/selftest-telegram-update.mjs",
|
|
22
|
+
"prepare": "npm run build",
|
|
23
|
+
"version:patch": "npm version --no-git-tag-version patch",
|
|
24
|
+
"version:minor": "npm version --no-git-tag-version minor",
|
|
25
|
+
"version:major": "npm version --no-git-tag-version major"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"dotenv": "^16.6.1",
|
|
29
|
+
"grammy": "^1.38.3"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^24.6.1",
|
|
33
|
+
"tsx": "^4.20.6",
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"bin",
|
|
38
|
+
"dist",
|
|
39
|
+
"scripts",
|
|
40
|
+
"docs",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE",
|
|
43
|
+
".env.example",
|
|
44
|
+
"package.json",
|
|
45
|
+
"tsconfig.json"
|
|
46
|
+
]
|
|
47
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if [ $# -ne 1 ]; then
|
|
5
|
+
echo "Usage: $0 <patch|minor|major>" >&2
|
|
6
|
+
exit 1
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
case "$1" in
|
|
10
|
+
patch|minor|major)
|
|
11
|
+
;;
|
|
12
|
+
*)
|
|
13
|
+
echo "Release type must be one of: patch, minor, major" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
;;
|
|
16
|
+
esac
|
|
17
|
+
|
|
18
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
19
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
20
|
+
cd "$ROOT_DIR"
|
|
21
|
+
|
|
22
|
+
npm version --no-git-tag-version "$1"
|
|
23
|
+
node -p "'RemoteAgent version is now ' + require('./package.json').version"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
source "$HOME/.profile" >/dev/null 2>&1 || true
|
|
4
|
+
|
|
5
|
+
token="${REMOTEAGENT_AUTH_TOKEN:-${CLAUDE_AUTH_TOKEN:-}}"
|
|
6
|
+
if [ -z "$token" ]; then
|
|
7
|
+
echo "Missing token. Usage: /login claude <token>"
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
verify_output="$(timeout 45s env ANTHROPIC_API_KEY="$token" CLAUDE_CODE_SIMPLE=1 claude --bare --print --output-format text "Reply exactly: claude token ok" 2>&1 || true)"
|
|
12
|
+
if ! printf '%s' "$verify_output" | grep -Fq 'claude token ok'; then
|
|
13
|
+
printf '%s
|
|
14
|
+
' "$verify_output"
|
|
15
|
+
echo
|
|
16
|
+
echo "Claude token verification failed."
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
env_file="$HOME/.remoteagent/.env"
|
|
21
|
+
mkdir -p "$(dirname "$env_file")"
|
|
22
|
+
touch "$env_file"
|
|
23
|
+
chmod 600 "$env_file"
|
|
24
|
+
python3 - "$env_file" "$token" <<'PY2'
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
import sys
|
|
27
|
+
path = Path(sys.argv[1])
|
|
28
|
+
token = sys.argv[2]
|
|
29
|
+
lines = path.read_text(encoding='utf8').splitlines() if path.exists() else []
|
|
30
|
+
updated = []
|
|
31
|
+
seen = False
|
|
32
|
+
for line in lines:
|
|
33
|
+
if line.startswith('ANTHROPIC_API_KEY='):
|
|
34
|
+
if not seen:
|
|
35
|
+
updated.append(f'ANTHROPIC_API_KEY={token}')
|
|
36
|
+
seen = True
|
|
37
|
+
continue
|
|
38
|
+
updated.append(line)
|
|
39
|
+
if not seen:
|
|
40
|
+
updated.append(f'ANTHROPIC_API_KEY={token}')
|
|
41
|
+
path.write_text('\n'.join(updated) + '\n', encoding='utf8')
|
|
42
|
+
PY2
|
|
43
|
+
|
|
44
|
+
sudo systemctl restart remoteagent
|
|
45
|
+
printf '%s
|
|
46
|
+
|
|
47
|
+
' "$verify_output"
|
|
48
|
+
echo "Claude token saved to ~/.remoteagent/.env as ANTHROPIC_API_KEY and remoteagent was restarted."
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
$ErrorActionPreference = "Stop"
|
|
2
|
+
|
|
3
|
+
$rootDir = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
|
4
|
+
$dataDir = if ($env:DATA_DIR) { $env:DATA_DIR } else { Join-Path $env:USERPROFILE ".remoteagent" }
|
|
5
|
+
$envFile = Join-Path $dataDir ".env"
|
|
6
|
+
|
|
7
|
+
New-Item -ItemType Directory -Force -Path $dataDir | Out-Null
|
|
8
|
+
New-Item -ItemType Directory -Force -Path (Join-Path $dataDir "logs") | Out-Null
|
|
9
|
+
|
|
10
|
+
if (-not (Test-Path $envFile)) {
|
|
11
|
+
Copy-Item (Join-Path $rootDir ".env.example") $envFile
|
|
12
|
+
Write-Host "Created $envFile"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Set-EnvValue {
|
|
16
|
+
param(
|
|
17
|
+
[string]$Path,
|
|
18
|
+
[string]$Key,
|
|
19
|
+
[string]$Value
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
$content = if (Test-Path $Path) { Get-Content $Path } else { @() }
|
|
23
|
+
$updated = $false
|
|
24
|
+
$result = foreach ($line in $content) {
|
|
25
|
+
if ($line -like "$Key=*") {
|
|
26
|
+
$updated = $true
|
|
27
|
+
"$Key=$Value"
|
|
28
|
+
} else {
|
|
29
|
+
$line
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (-not $updated) {
|
|
33
|
+
$result += "$Key=$Value"
|
|
34
|
+
}
|
|
35
|
+
Set-Content -Path $Path -Value $result
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Set-EnvValue -Path $envFile -Key "SETUP_COMMAND_TIMEOUT_MS" -Value "600000"
|
|
39
|
+
Set-EnvValue -Path $envFile -Key "CODEX_INSTALL_COMMAND" -Value "$rootDir/scripts/install-codex.sh"
|
|
40
|
+
Set-EnvValue -Path $envFile -Key "CLAUDE_INSTALL_COMMAND" -Value "$rootDir/scripts/install-claude.sh"
|
|
41
|
+
Set-EnvValue -Path $envFile -Key "CLAUDE_LOGIN_START_COMMAND" -Value "$rootDir/scripts/start-claude-login.sh"
|
|
42
|
+
Set-EnvValue -Path $envFile -Key "CLAUDE_LOGIN_FINISH_COMMAND" -Value "$rootDir/scripts/finish-claude-login.sh"
|
|
43
|
+
|
|
44
|
+
npm --prefix $rootDir install
|
|
45
|
+
npm --prefix $rootDir run build
|
|
46
|
+
|
|
47
|
+
Write-Host ""
|
|
48
|
+
Write-Host "RemoteAgent is installed."
|
|
49
|
+
Write-Host "Provider install/login hooks were configured in $envFile"
|
|
50
|
+
Write-Host "Set TELEGRAM_BOT_TOKEN or TELEGRAM_BOT_TOKENS in $envFile"
|
|
51
|
+
Write-Host "Start with: $rootDir/scripts/start.ps1"
|