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.
Files changed (46) hide show
  1. package/.env.example +39 -0
  2. package/LICENSE +21 -0
  3. package/README.md +371 -0
  4. package/bin/remoteagent.js +2 -0
  5. package/dist/adapters/claude-adapter.js +78 -0
  6. package/dist/adapters/codex-adapter.js +241 -0
  7. package/dist/adapters/provider-adapter.js +1 -0
  8. package/dist/adapters/shell-adapter.js +44 -0
  9. package/dist/adapters/windows-shell.js +111 -0
  10. package/dist/bot.js +2135 -0
  11. package/dist/config.js +170 -0
  12. package/dist/index.js +534 -0
  13. package/dist/secret-helper.js +24 -0
  14. package/dist/services/agent-memory-service.js +737 -0
  15. package/dist/services/bot-management-service.js +626 -0
  16. package/dist/services/bridge-service.js +807 -0
  17. package/dist/services/local-ui-service.js +533 -0
  18. package/dist/services/provider-setup-service.js +284 -0
  19. package/dist/services/remote-shell-service.js +97 -0
  20. package/dist/store/file-store.js +690 -0
  21. package/dist/telegram-fetch.js +85 -0
  22. package/dist/types.js +1 -0
  23. package/docs/ARCHITECTURE.md +170 -0
  24. package/docs/COKACDIR_NOTES.md +79 -0
  25. package/docs/ERROR_NORMALIZATION.md +46 -0
  26. package/docs/MINI_APP.md +112 -0
  27. package/docs/MVP.md +108 -0
  28. package/docs/OPERATIONS.md +181 -0
  29. package/docs/RELEASING.md +87 -0
  30. package/docs/SESSION_DIRECTORY_PLAN.md +506 -0
  31. package/package.json +47 -0
  32. package/scripts/bump-version.sh +23 -0
  33. package/scripts/finish-claude-login.sh +48 -0
  34. package/scripts/install-claude.sh +6 -0
  35. package/scripts/install-codex.sh +8 -0
  36. package/scripts/install.ps1 +51 -0
  37. package/scripts/install.sh +101 -0
  38. package/scripts/mock-adapter.sh +7 -0
  39. package/scripts/restart-after-bot-op.sh +118 -0
  40. package/scripts/selftest-telegram-update.mjs +359 -0
  41. package/scripts/start-claude-login.sh +4 -0
  42. package/scripts/start.ps1 +39 -0
  43. package/scripts/start.sh +54 -0
  44. package/scripts/stop.ps1 +40 -0
  45. package/scripts/stop.sh +39 -0
  46. package/tsconfig.json +20 -0
package/dist/config.js ADDED
@@ -0,0 +1,170 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+ import dotenv from "dotenv";
6
+ dotenv.config();
7
+ const defaultDataDir = path.resolve(process.env.DATA_DIR?.trim() || path.join(os.homedir(), ".remoteagent"));
8
+ const installedEnvPath = path.join(defaultDataDir, ".env");
9
+ if (fs.existsSync(installedEnvPath)) {
10
+ dotenv.config({ path: installedEnvPath, override: true });
11
+ }
12
+ const VALID_MODES = new Set(["codex", "claude"]);
13
+ const VALID_CODEX_SANDBOX_MODES = new Set(["read-only", "workspace-write", "danger-full-access"]);
14
+ function readRequired(name) {
15
+ const value = process.env[name]?.trim();
16
+ if (!value) {
17
+ throw new Error(`Missing required environment variable: ${name}`);
18
+ }
19
+ return value;
20
+ }
21
+ function readTelegramBotTokens() {
22
+ const multiValue = process.env.TELEGRAM_BOT_TOKENS?.trim();
23
+ if (multiValue) {
24
+ const tokens = multiValue
25
+ .split(/[\r\n,]+/)
26
+ .map((value) => value.trim())
27
+ .filter(Boolean);
28
+ if (tokens.length === 0) {
29
+ throw new Error("Invalid TELEGRAM_BOT_TOKENS: no non-empty tokens found");
30
+ }
31
+ return [...new Set(tokens)];
32
+ }
33
+ return [readRequired("TELEGRAM_BOT_TOKEN")];
34
+ }
35
+ function readTelegramBotUsernames() {
36
+ const raw = process.env.TELEGRAM_BOT_USERNAMES?.trim();
37
+ if (!raw) {
38
+ return [];
39
+ }
40
+ return raw
41
+ .split(/[\r\n,]+/)
42
+ .map((value) => value.trim())
43
+ .filter(Boolean);
44
+ }
45
+ function readOptional(name) {
46
+ const value = process.env[name]?.trim();
47
+ return value ? value : undefined;
48
+ }
49
+ function readMode(name, fallback) {
50
+ const value = process.env[name]?.trim();
51
+ if (!value) {
52
+ return fallback;
53
+ }
54
+ if (!VALID_MODES.has(value)) {
55
+ throw new Error(`Invalid ${name}: ${value}`);
56
+ }
57
+ return value;
58
+ }
59
+ function readTimeout(name, fallback) {
60
+ const value = process.env[name]?.trim();
61
+ if (!value) {
62
+ return fallback;
63
+ }
64
+ const parsed = Number.parseInt(value, 10);
65
+ if (!Number.isFinite(parsed) || parsed <= 0) {
66
+ throw new Error(`Invalid ${name}: ${value}`);
67
+ }
68
+ return parsed;
69
+ }
70
+ function readNonNegativeTimeout(name, fallback) {
71
+ const value = process.env[name]?.trim();
72
+ if (!value) {
73
+ return fallback;
74
+ }
75
+ const parsed = Number.parseInt(value, 10);
76
+ if (!Number.isFinite(parsed) || parsed < 0) {
77
+ throw new Error(`Invalid ${name}: ${value}`);
78
+ }
79
+ return parsed;
80
+ }
81
+ function readOptionalNonNegativeInteger(name) {
82
+ const value = process.env[name]?.trim();
83
+ if (!value) {
84
+ return undefined;
85
+ }
86
+ const parsed = Number.parseInt(value, 10);
87
+ if (!Number.isFinite(parsed) || parsed < 0) {
88
+ throw new Error(`Invalid ${name}: ${value}`);
89
+ }
90
+ return parsed;
91
+ }
92
+ function readBoolean(name, fallback) {
93
+ const value = process.env[name]?.trim().toLowerCase();
94
+ if (!value) {
95
+ return fallback;
96
+ }
97
+ if (["1", "true", "yes", "on"].includes(value)) {
98
+ return true;
99
+ }
100
+ if (["0", "false", "no", "off"].includes(value)) {
101
+ return false;
102
+ }
103
+ throw new Error(`Invalid ${name}: ${value}`);
104
+ }
105
+ function readPort(name, fallback) {
106
+ const value = process.env[name]?.trim();
107
+ if (!value) {
108
+ return fallback;
109
+ }
110
+ const parsed = Number.parseInt(value, 10);
111
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
112
+ throw new Error(`Invalid ${name}: ${value}`);
113
+ }
114
+ return parsed;
115
+ }
116
+ function readCodexSandboxMode(name) {
117
+ const value = process.env[name]?.trim();
118
+ if (!value) {
119
+ return undefined;
120
+ }
121
+ if (!VALID_CODEX_SANDBOX_MODES.has(value)) {
122
+ throw new Error(`Invalid ${name}: ${value}`);
123
+ }
124
+ return value;
125
+ }
126
+ const codexCommand = readOptional("CODEX_COMMAND");
127
+ const claudeCommand = readOptional("CLAUDE_COMMAND");
128
+ const telegramBotTokens = readTelegramBotTokens();
129
+ const telegramBotUsernames = readTelegramBotUsernames();
130
+ export const config = {
131
+ telegramBotTokens,
132
+ telegramBotUsernames,
133
+ telegramCommandMenuEnabled: readBoolean("TELEGRAM_COMMAND_MENU_ENABLED", false),
134
+ telegramPollingBackoffMinMs: readTimeout("TELEGRAM_POLLING_BACKOFF_MIN_MS", 60_000),
135
+ telegramPollingBackoffMaxMs: readTimeout("TELEGRAM_POLLING_BACKOFF_MAX_MS", 900_000),
136
+ telegramPollingMaxConcurrency: readTimeout("TELEGRAM_POLLING_MAX_CONCURRENCY", 3),
137
+ telegramOwnerId: readOptional("TELEGRAM_OWNER_ID"),
138
+ telegramMessageBatchMs: readNonNegativeTimeout("TELEGRAM_MESSAGE_BATCH_MS", 1500),
139
+ telegramAutoProgressMaxTurns: readOptionalNonNegativeInteger("TELEGRAM_AUTO_PROGRESS_MAX_TURNS") ?? 6,
140
+ telegramEmptyResponseRetries: readOptionalNonNegativeInteger("TELEGRAM_EMPTY_RESPONSE_RETRIES") ?? 1,
141
+ telegramRetryableErrorRetries: readOptionalNonNegativeInteger("TELEGRAM_RETRYABLE_ERROR_RETRIES") ?? 2,
142
+ telegramRetryableErrorDelayMs: readNonNegativeTimeout("TELEGRAM_RETRYABLE_ERROR_DELAY_MS", 5_000),
143
+ telegramUntaggedIntentRetries: readOptionalNonNegativeInteger("TELEGRAM_UNTAGGED_INTENT_RETRIES") ?? 2,
144
+ artifactCleanupEnabled: readBoolean("ARTIFACT_CLEANUP_ENABLED", true),
145
+ artifactRetentionDays: readTimeout("ARTIFACT_RETENTION_DAYS", 30),
146
+ artifactCleanupIntervalMs: readTimeout("ARTIFACT_CLEANUP_INTERVAL_MS", 86_400_000),
147
+ dataDir: defaultDataDir,
148
+ defaultMode: readMode("DEFAULT_MODE", "codex"),
149
+ defaultWorkspace: path.resolve(process.env.DEFAULT_WORKSPACE?.trim() || os.homedir()),
150
+ workspaceRoot: path.resolve(process.env.WORKSPACE_ROOT?.trim() || path.join(os.homedir(), "workspaces", "remoteagent")),
151
+ commandTimeoutMs: readTimeout("COMMAND_TIMEOUT_MS", 600_000),
152
+ setupCommandTimeoutMs: readTimeout("SETUP_COMMAND_TIMEOUT_MS", 600_000),
153
+ codexBin: readOptional("CODEX_BIN") || "codex",
154
+ codexSandboxMode: readCodexSandboxMode("CODEX_SANDBOX_MODE"),
155
+ codexInstallCommand: readOptional("CODEX_INSTALL_COMMAND"),
156
+ claudeBin: readOptional("CLAUDE_BIN") || "claude",
157
+ claudePermissionMode: readOptional("CLAUDE_PERMISSION_MODE") || "bypassPermissions",
158
+ claudeInstallCommand: readOptional("CLAUDE_INSTALL_COMMAND"),
159
+ claudeLoginStartCommand: readOptional("CLAUDE_LOGIN_START_COMMAND"),
160
+ claudeLoginFinishCommand: readOptional("CLAUDE_LOGIN_FINISH_COMMAND"),
161
+ botRestartServiceName: readOptional("REMOTEAGENT_SERVICE_NAME") || "remoteagent",
162
+ botRestartHelperPath: readOptional("BOT_RESTART_HELPER_PATH") || path.resolve(process.cwd(), "scripts", "restart-after-bot-op.sh"),
163
+ localUiEnabled: readBoolean("LOCAL_UI_ENABLED", true),
164
+ localUiHost: readOptional("LOCAL_UI_HOST") || "127.0.0.1",
165
+ localUiPort: readPort("LOCAL_UI_PORT", 3794),
166
+ commands: {
167
+ codex: codexCommand,
168
+ claude: claudeCommand,
169
+ },
170
+ };