appback-remoteagent 0.15.4 → 0.15.5

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/dist/bot.js CHANGED
@@ -2258,6 +2258,9 @@ print(text)
2258
2258
  const preview = stdout.trim();
2259
2259
  return preview || undefined;
2260
2260
  }
2261
+ const TELEGRAM_FILE_DOWNLOAD_ATTEMPTS = 4;
2262
+ const TELEGRAM_FILE_DOWNLOAD_TIMEOUT_SECONDS = 120;
2263
+ const TELEGRAM_FILE_DOWNLOAD_CONNECT_TIMEOUT_SECONDS = 20;
2261
2264
  async function downloadTelegramFile(botToken, botId, chatId, fileId, preferredName) {
2262
2265
  const file = await callTelegramApi(botToken, "getFile", {
2263
2266
  file_id: fileId,
@@ -2270,20 +2273,43 @@ async function downloadTelegramFile(botToken, botId, chatId, fileId, preferredNa
2270
2273
  const extension = path.extname(file.file_path) || path.extname(preferredName ?? "") || ".bin";
2271
2274
  const basename = path.basename(preferredName ?? file.file_path, path.extname(preferredName ?? file.file_path));
2272
2275
  const outputPath = path.join(directory, `${Date.now()}-${safePathSegment(basename)}-${randomUUID()}${extension}`);
2276
+ const partialPath = `${outputPath}.part`;
2273
2277
  const fileUrl = new URL(file.file_path, `https://api.telegram.org/file/bot${botToken}/`).toString();
2274
- const { stderr } = await execFileAsync("curl", [
2275
- "-fL",
2276
- "-sS",
2277
- "--max-time",
2278
- "60",
2279
- "-o",
2280
- outputPath,
2281
- fileUrl,
2282
- ]);
2283
- if (stderr?.trim()) {
2284
- console.error(`curl stderr for Telegram file download: ${stderr.trim()}`);
2278
+ let lastError;
2279
+ for (let attempt = 1; attempt <= TELEGRAM_FILE_DOWNLOAD_ATTEMPTS; attempt += 1) {
2280
+ try {
2281
+ const { stderr } = await execFileAsync("curl", [
2282
+ "-fL",
2283
+ "-sS",
2284
+ "--connect-timeout",
2285
+ String(TELEGRAM_FILE_DOWNLOAD_CONNECT_TIMEOUT_SECONDS),
2286
+ "--max-time",
2287
+ String(TELEGRAM_FILE_DOWNLOAD_TIMEOUT_SECONDS),
2288
+ "-C",
2289
+ "-",
2290
+ "-o",
2291
+ partialPath,
2292
+ fileUrl,
2293
+ ]);
2294
+ if (stderr?.trim()) {
2295
+ console.error(`curl stderr for Telegram file download: ${stderr.trim()}`);
2296
+ }
2297
+ await fs.rename(partialPath, outputPath);
2298
+ return { path: outputPath };
2299
+ }
2300
+ catch (error) {
2301
+ lastError = error;
2302
+ if (attempt < TELEGRAM_FILE_DOWNLOAD_ATTEMPTS) {
2303
+ const partialSize = await fs.stat(partialPath).then((stat) => stat.size).catch(() => 0);
2304
+ console.error(`Telegram file download attempt ${attempt}/${TELEGRAM_FILE_DOWNLOAD_ATTEMPTS} failed; ` +
2305
+ `retrying with resume from ${partialSize} bytes.`);
2306
+ await sleep(Math.min(5000, attempt * 1000));
2307
+ continue;
2308
+ }
2309
+ }
2285
2310
  }
2286
- return { path: outputPath };
2311
+ await fs.rm(partialPath, { force: true }).catch(() => undefined);
2312
+ throw lastError instanceof Error ? lastError : new Error("Telegram file download failed.");
2287
2313
  }
2288
2314
  function safePathSegment(value) {
2289
2315
  const safe = value.replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "");
@@ -11,6 +11,7 @@ RemoteAgent production is operated from one canonical Git history:
11
11
 
12
12
  Do not treat stale server paths or duplicate worktrees as authoritative.
13
13
  Code changes should be committed and pushed to `origin/main`, published to npm, and then server 30/26 should be updated from that npm version.
14
+ Installing a local tarball directly on a server is an emergency hotfix only; it is not a completed production release until the same version is published to npm and both targets are reconciled to that npm version.
14
15
 
15
16
  ## Product shape in operations
16
17
 
@@ -134,6 +135,7 @@ If a temporary branch is unavoidable, merge it back to `main`, push it, and depl
134
135
 
135
136
  A task is not done until commit and push both happened.
136
137
  A production deployment is not done until server 30 and server 26 are running the published npm version and the runtime path has been verified.
138
+ If npm publish fails with `403`, do not keep retrying or switch to source deployment. Confirm the npm token has publish rights for the unscoped `appback-remoteagent` package, then publish. See `docs/RELEASING.md`.
137
139
 
138
140
  See `docs/RELEASING.md` for the detailed versioning rules.
139
141
 
package/docs/RELEASING.md CHANGED
@@ -28,7 +28,7 @@ A release is only considered complete when all of the following are done in orde
28
28
  4. Run `npm run build`
29
29
  5. Commit the completed work
30
30
  6. Push `main` to `origin/main`
31
- 7. Publish `appback-remoteagent@<version>` to npm from server 30's npm credentials
31
+ 7. Publish `appback-remoteagent@<version>` to npm using a token/account that is proven to have publish rights for this unscoped package
32
32
  8. Install that exact npm version on server 30 and server 26
33
33
  9. Restart `remoteagent.service` on each target if runtime code changed
34
34
  10. Verify the relevant runtime path, logs, or Telegram behavior on each target
@@ -71,27 +71,74 @@ npm pack
71
71
 
72
72
  Use this package-specific path for `appback-remoteagent`.
73
73
 
74
- Do not publish this package from machine 21 with the `21token`. That token is scoped for `@appbackhub` packages and cannot publish the unscoped `appback-remoteagent` package. It can authenticate but `npm publish` fails with:
74
+ Direct `npm publish` is blocked by `prepublishOnly`.
75
+ The only normal publish command is:
76
+
77
+ ```bash
78
+ npm run release:publish
79
+ ```
80
+
81
+ Do not publish this package with the machine 21 `21token`.
82
+ That token can authenticate as `appbackhub`, but it does not currently have publish rights for the unscoped `appback-remoteagent` package.
83
+ It fails at publish time with:
75
84
 
76
85
  ```text
77
86
  403 Forbidden - You may not perform that action with these credentials.
78
87
  ```
79
88
 
80
- The successful publish path is:
89
+ Before publishing, always verify both identity and package ownership:
81
90
 
82
91
  ```bash
83
- VERSION=0.14.0
84
- npm pack
85
- scp appback-remoteagent-$VERSION.tgz au2223@192.168.0.30:/tmp/appback-remoteagent-$VERSION.tgz
86
- ssh au2223@192.168.0.30 "bash -lc 'npm publish /tmp/appback-remoteagent-$VERSION.tgz'"
92
+ npm whoami
93
+ npm owner ls appback-remoteagent
94
+ ```
95
+
96
+ Authentication alone is not enough.
97
+ If `npm whoami` works but `npm publish` returns `403`, stop and get a publish-capable npm token instead of retrying with the same token.
98
+
99
+ The guarded registry publish path is:
100
+
101
+ ```bash
102
+ npm run release:publish
87
103
  npm view appback-remoteagent version
88
- ssh au2223@192.168.0.30 'bash -lc "npm view appback-remoteagent version"'
89
- rm -f appback-remoteagent-$VERSION.tgz
90
- ssh au2223@192.168.0.30 "rm -f /tmp/appback-remoteagent-$VERSION.tgz"
91
104
  ```
92
105
 
93
106
  If registry caching briefly returns the previous version after publish, wait a few seconds and rerun `npm view appback-remoteagent version` before installing to servers.
94
107
 
108
+ ## Emergency tarball install
109
+
110
+ An emergency tarball install is allowed only to restore a broken runtime when npm publish is blocked.
111
+ It is not a completed release and must be reported as a temporary hotfix.
112
+
113
+ Rules:
114
+
115
+ - do not call it "npm deployed" or "released"
116
+ - do not delete runtime state
117
+ - do not use source checkout deployment
118
+ - confirm no active provider process is running before restart
119
+ - install the exact local package tarball with `npm install -g /tmp/appback-remoteagent-<version>.tgz`
120
+ - follow up by publishing the same version to npm once a publish-capable token is available
121
+
122
+ Emergency server 30 flow:
123
+
124
+ ```bash
125
+ VERSION=0.14.0
126
+ npm pack
127
+ scp appback-remoteagent-$VERSION.tgz au2223@192.168.0.30:/tmp/
128
+ ssh au2223@192.168.0.30 "VERSION=$VERSION bash -s" <<'REMOTE'
129
+ set -euo pipefail
130
+ export PATH="/home/au2223/.local/bin:/home/au2223/.nvm/versions/node/v22.22.0/bin:$PATH"
131
+ pgrep -af 'codex|claude' || true
132
+ npm install -g "/tmp/appback-remoteagent-$VERSION.tgz"
133
+ remoteagent-install
134
+ sudo -n systemctl restart remoteagent
135
+ systemctl is-active remoteagent
136
+ node -p 'require("/home/au2223/.nvm/versions/node/v22.22.0/lib/node_modules/appback-remoteagent/package.json").version'
137
+ journalctl -u remoteagent --since '2 minutes ago' --no-pager
138
+ REMOTE
139
+ rm -f appback-remoteagent-$VERSION.tgz
140
+ ```
141
+
95
142
  ## Target deployment sequence
96
143
 
97
144
  Server 30 uses a systemd service:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appback-remoteagent",
3
- "version": "0.15.4",
3
+ "version": "0.15.5",
4
4
  "description": "Personal installable session server for continuing local AI work across PC and Telegram",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,6 +20,8 @@
20
20
  "check": "tsc --noEmit -p tsconfig.json",
21
21
  "selftest:telegram": "npm run build && node scripts/selftest-telegram-update.mjs",
22
22
  "prepare": "npm run build",
23
+ "prepublishOnly": "node scripts/prepublish-guard.mjs",
24
+ "release:publish": "bash scripts/release-publish.sh",
23
25
  "version:patch": "npm version --no-git-tag-version patch",
24
26
  "version:minor": "npm version --no-git-tag-version minor",
25
27
  "version:major": "npm version --no-git-tag-version major"
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+
3
+ if (process.env.REMOTEAGENT_PUBLISH_GUARD_OK === "1") {
4
+ process.exit(0);
5
+ }
6
+
7
+ console.error([
8
+ "Direct npm publish is blocked for appback-remoteagent.",
9
+ "",
10
+ "Use the documented release path instead:",
11
+ " npm run release:publish",
12
+ "",
13
+ "Reason:",
14
+ "- direct publish has repeatedly used the wrong npm token",
15
+ "- appback-remoteagent must be published only through the guarded script",
16
+ "- emergency tarball installs are not completed releases",
17
+ ].join("\n"));
18
+
19
+ process.exit(1);
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ cd "$ROOT"
6
+
7
+ PACKAGE_NAME="$(node -p 'require("./package.json").name')"
8
+ VERSION="$(node -p 'require("./package.json").version')"
9
+
10
+ if [[ "$PACKAGE_NAME" != "appback-remoteagent" ]]; then
11
+ echo "Unexpected package name: $PACKAGE_NAME" >&2
12
+ exit 1
13
+ fi
14
+
15
+ if [[ -n "$(git status --porcelain)" ]]; then
16
+ echo "Working tree is not clean. Commit and push before publishing." >&2
17
+ git status --short >&2
18
+ exit 1
19
+ fi
20
+
21
+ echo "Publishing $PACKAGE_NAME@$VERSION"
22
+ echo
23
+ echo "npm identity:"
24
+ npm whoami
25
+ echo
26
+ echo "npm owners:"
27
+ npm owner ls "$PACKAGE_NAME"
28
+ echo
29
+
30
+ npm run check
31
+ npm run build
32
+
33
+ TARBALL="$(npm pack --silent)"
34
+ cleanup() {
35
+ rm -f "$TARBALL"
36
+ }
37
+ trap cleanup EXIT
38
+
39
+ echo
40
+ echo "Publishing tarball: $TARBALL"
41
+ REMOTEAGENT_PUBLISH_GUARD_OK=1 npm publish "$TARBALL" --access public
42
+
43
+ echo
44
+ echo "Verifying registry version:"
45
+ npm view "$PACKAGE_NAME" version