neoagent 2.1.18-beta.34 → 2.1.18-beta.36

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.
@@ -10,6 +10,9 @@ const flowDescriptionEl = document.querySelector('#flowDescription');
10
10
  const primaryActionEl = document.querySelector('#primaryAction');
11
11
  const secondaryActionEl = document.querySelector('#secondaryAction');
12
12
  const openAppEl = document.querySelector('#openApp');
13
+ const disconnectEl = document.querySelector('#disconnect');
14
+ const checkUpdateEl = document.querySelector('#checkUpdate');
15
+ const downloadEl = document.querySelector('#download');
13
16
 
14
17
  const STATUS_LABELS = {
15
18
  connected: 'Connected',
@@ -186,68 +189,38 @@ async function refresh() {
186
189
  updateFlow();
187
190
  }
188
191
 
189
- primaryActionEl.addEventListener('click', async () => {
190
- try {
191
- setMessage('');
192
- await runAction(primaryActionEl.dataset.action);
193
- } catch (error) {
194
- setMessage(error.message, 'error');
195
- }
196
- });
197
-
198
- secondaryActionEl.addEventListener('click', async () => {
199
- try {
200
- setMessage('');
201
- await runAction(secondaryActionEl.dataset.action);
202
- } catch (error) {
203
- setMessage(error.message, 'error');
204
- }
205
- });
206
-
207
- openAppEl.addEventListener('click', async () => {
208
- try {
209
- setMessage('');
210
- await runAction('openApp');
211
- } catch (error) {
212
- setMessage(error.message, 'error');
213
- }
214
- });
192
+ function bindAsyncClick(element, handler) {
193
+ element.addEventListener('click', async () => {
194
+ try {
195
+ setMessage('');
196
+ await handler();
197
+ } catch (error) {
198
+ setMessage(error.message, 'error');
199
+ }
200
+ });
201
+ }
215
202
 
216
203
  serverUrlEl.addEventListener('input', updateFlow);
217
204
 
218
- document.querySelector('#disconnect').addEventListener('click', async () => {
219
- try {
220
- setMessage('');
221
- await send('disconnect');
222
- await refresh();
223
- setMessage('Disconnected.', 'success');
224
- } catch (error) {
225
- setMessage(error.message, 'error');
226
- }
205
+ bindAsyncClick(primaryActionEl, () => runAction(primaryActionEl.dataset.action));
206
+ bindAsyncClick(secondaryActionEl, () => runAction(secondaryActionEl.dataset.action));
207
+ bindAsyncClick(openAppEl, () => runAction('openApp'));
208
+ bindAsyncClick(disconnectEl, async () => {
209
+ await send('disconnect');
210
+ await refresh();
211
+ setMessage('Disconnected.', 'success');
227
212
  });
228
-
229
- document.querySelector('#checkUpdate').addEventListener('click', async () => {
230
- try {
231
- setMessage('');
232
- const result = await send('checkForUpdates', { serverUrl: effectiveServerUrl() });
233
- setMessage(
234
- result.updateAvailable
235
- ? `Update available: ${result.currentVersion} -> ${result.latestVersion}.`
236
- : `Current version ${result.currentVersion} is up to date.`,
237
- result.updateAvailable ? '' : 'success',
238
- );
239
- } catch (error) {
240
- setMessage(error.message, 'error');
241
- }
213
+ bindAsyncClick(checkUpdateEl, async () => {
214
+ const result = await send('checkForUpdates', { serverUrl: effectiveServerUrl() });
215
+ setMessage(
216
+ result.updateAvailable
217
+ ? `Update available: ${result.currentVersion} -> ${result.latestVersion}.`
218
+ : `Current version ${result.currentVersion} is up to date.`,
219
+ result.updateAvailable ? '' : 'success',
220
+ );
242
221
  });
243
-
244
- document.querySelector('#download').addEventListener('click', async () => {
245
- try {
246
- setMessage('');
247
- await send('openDownload', { serverUrl: effectiveServerUrl() });
248
- } catch (error) {
249
- setMessage(error.message, 'error');
250
- }
222
+ bindAsyncClick(downloadEl, async () => {
223
+ await send('openDownload', { serverUrl: effectiveServerUrl() });
251
224
  });
252
225
 
253
226
  chrome.runtime.onMessage.addListener((message) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.1.18-beta.34",
3
+ "version": "2.1.18-beta.36",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: Catbox & Litterbox File Uploader
3
+ description: Upload local files to Catbox for permanent sharing or Litterbox for temporary sharing. Use when you need a public link for logs, screenshots, archives, media, or other local artifacts.
4
+ version: 1.0.0
5
+ author: Microck
6
+ upstream: https://github.com/Microck/opendots-microck/tree/main/skills/catbox-upload-skill
7
+ ---
8
+
9
+ # Catbox & Litterbox File Uploader
10
+
11
+ Use this skill when the user wants a direct public URL for a local file.
12
+
13
+ - Prefer `litterbox` for temporary sharing of logs, builds, screenshots, and one-off artifacts.
14
+ - Prefer `catbox` for permanent hosting.
15
+ - Return the final URL plainly, and for `litterbox` also mention the selected expiry window.
16
+
17
+ ## Command
18
+
19
+ Run the bundled uploader script:
20
+
21
+ ```bash
22
+ bash ~/.neoagent/agent-data/skills/productivity-catbox-upload-skill/scripts/upload.sh /path/to/file
23
+ ```
24
+
25
+ ## Arguments
26
+
27
+ - `file_path` (required): local file to upload
28
+ - `--service litterbox|catbox` (optional): defaults to `litterbox`
29
+ - `--time 1h|12h|24h|72h` (optional): only used for `litterbox`, defaults to `24h`
30
+ - `--userhash HASH` (optional): Catbox account hash for uploads tied to a Catbox account
31
+
32
+ ## Examples
33
+
34
+ Temporary upload with the default 24-hour expiry:
35
+
36
+ ```bash
37
+ bash ~/.neoagent/agent-data/skills/productivity-catbox-upload-skill/scripts/upload.sh ./logs/server.log
38
+ ```
39
+
40
+ Temporary upload that expires after 1 hour:
41
+
42
+ ```bash
43
+ bash ~/.neoagent/agent-data/skills/productivity-catbox-upload-skill/scripts/upload.sh ./build/output.zip --time 1h
44
+ ```
45
+
46
+ Permanent upload to Catbox:
47
+
48
+ ```bash
49
+ bash ~/.neoagent/agent-data/skills/productivity-catbox-upload-skill/scripts/upload.sh ./artifacts/screenshot.png --service catbox
50
+ ```
51
+
52
+ Permanent upload associated with a Catbox account:
53
+
54
+ ```bash
55
+ bash ~/.neoagent/agent-data/skills/productivity-catbox-upload-skill/scripts/upload.sh ./artifacts/screenshot.png --service catbox --userhash YOUR_HASH
56
+ ```
57
+
58
+ ## Expected Output
59
+
60
+ The script prints exactly one URL on success, for example:
61
+
62
+ ```text
63
+ https://litterbox.catbox.moe/abc123.png
64
+ ```
65
+
66
+ or:
67
+
68
+ ```text
69
+ https://files.catbox.moe/abc123.png
70
+ ```
71
+
72
+ ## Presenting Results
73
+
74
+ When the upload succeeds:
75
+
76
+ - show the returned URL clearly
77
+ - mention whether it is temporary or permanent
78
+ - if using `litterbox`, include the expiration window
79
+
80
+ Example response:
81
+
82
+ ```text
83
+ File uploaded successfully.
84
+
85
+ Link: https://litterbox.catbox.moe/abc123.png
86
+ Expires in: 24 hours
87
+ ```
88
+
89
+ ## Limits
90
+
91
+ - `litterbox`: up to 1 GB, temporary, supports `1h`, `12h`, `24h`, and `72h`
92
+ - `catbox`: up to 200 MB, permanent
93
+
94
+ ## Troubleshooting
95
+
96
+ - If the file does not exist or is unreadable, verify the path first.
97
+ - If the upload fails, surface the exact error from the script.
98
+ - If `curl` is missing, install it before retrying.
99
+ - If Catbox rejects the file, the file may exceed the service limit or violate Catbox content rules.
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ cat >&2 <<'EOF'
6
+ Usage: upload.sh <file_path> [--service litterbox|catbox] [--time 1h|12h|24h|72h] [--userhash HASH]
7
+
8
+ Options:
9
+ --service Upload target. Defaults to litterbox.
10
+ --time Litterbox retention window. Defaults to 24h.
11
+ --userhash Optional Catbox account hash for uploads associated with an account.
12
+ -h, --help Show this help text.
13
+
14
+ Limits:
15
+ litterbox: 1 GB
16
+ catbox: 200 MB
17
+ EOF
18
+ }
19
+
20
+ fail() {
21
+ printf 'Error: %s\n' "$1" >&2
22
+ exit 1
23
+ }
24
+
25
+ require_value() {
26
+ local option="$1"
27
+ local value="${2-}"
28
+ [[ -n "$value" ]] || fail "$option requires a value"
29
+ }
30
+
31
+ SERVICE="litterbox"
32
+ TIME="24h"
33
+ USERHASH=""
34
+ FILE_PATH=""
35
+
36
+ while [[ $# -gt 0 ]]; do
37
+ case "$1" in
38
+ --service)
39
+ require_value "$1" "${2-}"
40
+ SERVICE="$2"
41
+ shift 2
42
+ ;;
43
+ --time)
44
+ require_value "$1" "${2-}"
45
+ TIME="$2"
46
+ shift 2
47
+ ;;
48
+ --userhash)
49
+ require_value "$1" "${2-}"
50
+ USERHASH="$2"
51
+ shift 2
52
+ ;;
53
+ -h|--help)
54
+ usage
55
+ exit 0
56
+ ;;
57
+ --)
58
+ shift
59
+ break
60
+ ;;
61
+ -*)
62
+ usage
63
+ fail "unknown option: $1"
64
+ ;;
65
+ *)
66
+ [[ -z "$FILE_PATH" ]] || fail "only one file path is supported"
67
+ FILE_PATH="$1"
68
+ shift
69
+ ;;
70
+ esac
71
+ done
72
+
73
+ if [[ $# -gt 0 ]]; then
74
+ [[ -z "$FILE_PATH" ]] || fail "only one file path is supported"
75
+ FILE_PATH="$1"
76
+ shift
77
+ fi
78
+
79
+ [[ $# -eq 0 ]] || fail "only one file path is supported"
80
+
81
+ [[ -n "$FILE_PATH" ]] || {
82
+ usage
83
+ fail "file path is required"
84
+ }
85
+
86
+ command -v curl >/dev/null 2>&1 || fail "curl is not installed"
87
+ [[ -f "$FILE_PATH" ]] || fail "file not found: $FILE_PATH"
88
+ [[ -r "$FILE_PATH" ]] || fail "file is not readable: $FILE_PATH"
89
+
90
+ case "$SERVICE" in
91
+ litterbox|catbox)
92
+ ;;
93
+ *)
94
+ fail "service must be either 'litterbox' or 'catbox'"
95
+ ;;
96
+ esac
97
+
98
+ case "$TIME" in
99
+ 1h|12h|24h|72h)
100
+ ;;
101
+ *)
102
+ fail "time must be one of: 1h, 12h, 24h, 72h"
103
+ ;;
104
+ esac
105
+
106
+ FILE_SIZE_BYTES="$(wc -c < "$FILE_PATH" | tr -d '[:space:]')"
107
+ LITTERBOX_LIMIT_BYTES=1073741824
108
+ CATBOX_LIMIT_BYTES=209715200
109
+
110
+ if [[ "$SERVICE" == "litterbox" ]] && (( FILE_SIZE_BYTES > LITTERBOX_LIMIT_BYTES )); then
111
+ fail "litterbox uploads are limited to 1 GB"
112
+ fi
113
+
114
+ if [[ "$SERVICE" == "catbox" ]] && (( FILE_SIZE_BYTES > CATBOX_LIMIT_BYTES )); then
115
+ fail "catbox uploads are limited to 200 MB"
116
+ fi
117
+
118
+ CURL_FILE_PATH="${FILE_PATH//;/\\;}"
119
+
120
+ if [[ "$SERVICE" == "litterbox" ]]; then
121
+ ENDPOINT="https://litterbox.catbox.moe/resources/internals/api.php"
122
+ RESPONSE="$(
123
+ curl --silent --show-error --fail \
124
+ --user-agent 'NeoAgent catbox-upload-skill/1.0' \
125
+ -F 'reqtype=fileupload' \
126
+ -F "time=$TIME" \
127
+ -F "fileToUpload=@$CURL_FILE_PATH" \
128
+ "$ENDPOINT"
129
+ )"
130
+ else
131
+ ENDPOINT="https://catbox.moe/user/api.php"
132
+ if [[ -n "$USERHASH" ]]; then
133
+ RESPONSE="$(
134
+ curl --silent --show-error --fail \
135
+ --user-agent 'NeoAgent catbox-upload-skill/1.0' \
136
+ -F 'reqtype=fileupload' \
137
+ -F "userhash=$USERHASH" \
138
+ -F "fileToUpload=@$CURL_FILE_PATH" \
139
+ "$ENDPOINT"
140
+ )"
141
+ else
142
+ RESPONSE="$(
143
+ curl --silent --show-error --fail \
144
+ --user-agent 'NeoAgent catbox-upload-skill/1.0' \
145
+ -F 'reqtype=fileupload' \
146
+ -F "fileToUpload=@$CURL_FILE_PATH" \
147
+ "$ENDPOINT"
148
+ )"
149
+ fi
150
+ fi
151
+
152
+ RESPONSE="$(printf '%s' "$RESPONSE" | tr -d '\r\n')"
153
+ [[ "$RESPONSE" =~ ^https?:// ]] || fail "upload failed: $RESPONSE"
154
+
155
+ printf '%s\n' "$RESPONSE"
@@ -10,6 +10,7 @@ const { logRequestSummary } = require('../utils/logger');
10
10
  const { getSessionSecret } = require('../services/account/session_secret');
11
11
 
12
12
  const sessionsDb = new Sqlite(`${DATA_DIR}/sessions.db`);
13
+ const LEGACY_SESSION_EXPIRE_FALLBACK = 0;
13
14
 
14
15
  function ensureSessionStoreSchema(db) {
15
16
  const table = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'sessions'").get();
@@ -35,11 +36,10 @@ function ensureSessionStoreSchema(db) {
35
36
  db.exec('CREATE TABLE sessions (sid PRIMARY KEY, sess, expire)');
36
37
 
37
38
  if (hasSid && hasSess) {
38
- // TODO: Verify whether better-sqlite3-session-store treats expire=0 as already expired or non-expiring.
39
39
  const expireExpr = expireColumn ? expireColumn : 'NULL';
40
40
  db.exec(`
41
41
  INSERT OR REPLACE INTO sessions (sid, sess, expire)
42
- SELECT sid, sess, COALESCE(${expireExpr}, 0) AS expire
42
+ SELECT sid, sess, COALESCE(${expireExpr}, ${LEGACY_SESSION_EXPIRE_FALLBACK}) AS expire
43
43
  FROM ${legacyTableName}
44
44
  WHERE sid IS NOT NULL
45
45
  `);
@@ -138,6 +138,13 @@ function applyHttpMiddleware(app, { secureCookies, sessionMiddleware, validateOr
138
138
  || path === '/api/browser-extension/pairing/request'
139
139
  || /^\/api\/browser-extension\/pairing\/[^/]+\/claim$/i.test(path);
140
140
  };
141
+ const requestPath = (req) => req.originalUrl || req.url || req.path || '';
142
+ const applyOnlyToRecordingChunk = (handler) => (req, res, next) => (
143
+ isRecordingChunkPath(requestPath(req)) ? handler(req, res, next) : next()
144
+ );
145
+ const skipRecordingChunk = (handler) => (req, res, next) => (
146
+ isRecordingChunkPath(requestPath(req)) ? next() : handler(req, res, next)
147
+ );
141
148
 
142
149
  if (secureCookies) {
143
150
  app.set('trust proxy', 1);
@@ -184,24 +191,9 @@ function applyHttpMiddleware(app, { secureCookies, sessionMiddleware, validateOr
184
191
 
185
192
  next();
186
193
  });
187
- app.use((req, res, next) => {
188
- if (isRecordingChunkPath(req.originalUrl || req.url || req.path)) {
189
- return rawRecordingChunkBody(req, res, next);
190
- }
191
- return next();
192
- });
193
- app.use((req, res, next) => {
194
- if (isRecordingChunkPath(req.originalUrl || req.url || req.path)) {
195
- return next();
196
- }
197
- return jsonBody(req, res, next);
198
- });
199
- app.use((req, res, next) => {
200
- if (isRecordingChunkPath(req.originalUrl || req.url || req.path)) {
201
- return next();
202
- }
203
- return urlencodedBody(req, res, next);
204
- });
194
+ app.use(applyOnlyToRecordingChunk(rawRecordingChunkBody));
195
+ app.use(skipRecordingChunk(jsonBody));
196
+ app.use(skipRecordingChunk(urlencodedBody));
205
197
  app.use(sessionMiddleware);
206
198
  }
207
199
 
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"425cfb54d01a9472b3e81d9e76fd63a4a44cfb
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "2957006624" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "1390596255" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });