@tiens.nguyen/gonext-local-worker 1.0.177 → 1.0.178
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/gonext-local-worker.mjs +3 -0
- package/gonext-repl.mjs +3 -1
- package/gonext_agent_chat.py +53 -13
- package/package.json +1 -1
package/gonext-local-worker.mjs
CHANGED
|
@@ -1799,6 +1799,9 @@ async function runAgentChatJob(job) {
|
|
|
1799
1799
|
return Array.isArray(parsed?.workspaces) ? parsed.workspaces : [];
|
|
1800
1800
|
})
|
|
1801
1801
|
.catch(() => []),
|
|
1802
|
+
// The `gonext` terminal REPL's launch folder — download_file/unzip_file output
|
|
1803
|
+
// lands here (must be a registered workspace; the python side re-validates).
|
|
1804
|
+
activeWorkspace: payload?.activeWorkspace ?? "",
|
|
1802
1805
|
});
|
|
1803
1806
|
// 30 min max for an agent run: multi-step ReAct on a 14B/31B with a cold prompt
|
|
1804
1807
|
// cache — or a remote Ollama box on slow GPU cold-loading a big model — can run
|
package/gonext-repl.mjs
CHANGED
|
@@ -199,7 +199,9 @@ async function runAgentTurn(history) {
|
|
|
199
199
|
const res = await fetch(`${apiBase}/api/worker/agent-ask`, {
|
|
200
200
|
method: "POST",
|
|
201
201
|
headers: { "Content-Type": "application/json", "X-Worker-Key": workerKey },
|
|
202
|
-
|
|
202
|
+
// cwd = this terminal's folder → agent puts download/unzip output here (when it's
|
|
203
|
+
// a registered workspace).
|
|
204
|
+
body: JSON.stringify({ messages: history, cwd: resolve(process.cwd()) }),
|
|
203
205
|
});
|
|
204
206
|
const data = await res.json().catch(() => ({}));
|
|
205
207
|
if (!res.ok) {
|
package/gonext_agent_chat.py
CHANGED
|
@@ -1158,6 +1158,17 @@ def _rag_workdir(source_key: str) -> str:
|
|
|
1158
1158
|
return base
|
|
1159
1159
|
|
|
1160
1160
|
|
|
1161
|
+
def _rag_base_dir(source_key: str) -> str:
|
|
1162
|
+
"""Where a URL's downloaded/unzipped files should live. Prefer the active terminal
|
|
1163
|
+
workspace (the folder the `gonext` REPL was opened from) so files land in the
|
|
1164
|
+
user's project; otherwise fall back to the per-URL cache dir under ~/.gonext."""
|
|
1165
|
+
import os
|
|
1166
|
+
if _WS_ACTIVE:
|
|
1167
|
+
os.makedirs(_WS_ACTIVE, exist_ok=True)
|
|
1168
|
+
return _WS_ACTIVE
|
|
1169
|
+
return _rag_workdir(source_key)
|
|
1170
|
+
|
|
1171
|
+
|
|
1161
1172
|
def _rag_download(url: str, dest_path: str = "") -> tuple:
|
|
1162
1173
|
"""Download a public URL with SSRF + size guards — IDEMPOTENT per URL. Every URL
|
|
1163
1174
|
maps to a deterministic work dir (~/.gonext/rag-work/<urlHash>/); relative
|
|
@@ -1168,7 +1179,7 @@ def _rag_download(url: str, dest_path: str = "") -> tuple:
|
|
|
1168
1179
|
src = _rag_gdrive_direct((url or "").strip())
|
|
1169
1180
|
_rag_assert_safe_url(src)
|
|
1170
1181
|
key = _rag_source_key(url)
|
|
1171
|
-
workdir =
|
|
1182
|
+
workdir = _rag_base_dir(key)
|
|
1172
1183
|
dest_path = os.path.expanduser((dest_path or "").strip())
|
|
1173
1184
|
if not dest_path:
|
|
1174
1185
|
name = os.path.basename(src.split("?")[0]) or "download.bin"
|
|
@@ -1176,21 +1187,23 @@ def _rag_download(url: str, dest_path: str = "") -> tuple:
|
|
|
1176
1187
|
name += ".zip"
|
|
1177
1188
|
dest_path = os.path.join(workdir, name)
|
|
1178
1189
|
elif not os.path.isabs(dest_path):
|
|
1179
|
-
# Contain relative paths in the
|
|
1190
|
+
# Contain relative paths in the work dir — models pass bare names like
|
|
1180
1191
|
# "project.zip" which would otherwise land in the worker's cwd (the home dir).
|
|
1181
1192
|
dest_path = os.path.join(workdir, dest_path)
|
|
1182
1193
|
# Cache hit 1: the exact target already exists.
|
|
1183
1194
|
if os.path.isfile(dest_path) and os.path.getsize(dest_path) > 0:
|
|
1184
1195
|
return dest_path, True
|
|
1185
|
-
# Cache hit 2: this URL was downloaded before under a different name —
|
|
1186
|
-
#
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1196
|
+
# Cache hit 2: this URL was downloaded before under a different name — ONLY safe in
|
|
1197
|
+
# the per-URL cache dir (one URL per dir). In an active workspace the folder holds
|
|
1198
|
+
# many unrelated files, so a bare "any zip = this URL" match would be wrong.
|
|
1199
|
+
if not _WS_ACTIVE:
|
|
1200
|
+
try:
|
|
1201
|
+
for fn in sorted(os.listdir(workdir)):
|
|
1202
|
+
p = os.path.join(workdir, fn)
|
|
1203
|
+
if os.path.isfile(p) and fn.lower().endswith(".zip") and os.path.getsize(p) > 0:
|
|
1204
|
+
return p, True
|
|
1205
|
+
except OSError:
|
|
1206
|
+
pass
|
|
1194
1207
|
os.makedirs(os.path.dirname(dest_path) or ".", exist_ok=True)
|
|
1195
1208
|
req = urllib.request.Request(src, headers={"User-Agent": "gonext-rag/1.0"})
|
|
1196
1209
|
total = 0
|
|
@@ -1220,8 +1233,15 @@ def _rag_resolve_zip(zip_path: str) -> str:
|
|
|
1220
1233
|
if os.path.isfile(zp):
|
|
1221
1234
|
return zp
|
|
1222
1235
|
if not os.path.isabs(zp):
|
|
1236
|
+
base = os.path.basename(zp)
|
|
1237
|
+
# Look in the active terminal workspace first (that's where downloads land now),
|
|
1238
|
+
# then fall back to the per-URL cache dirs.
|
|
1239
|
+
if _WS_ACTIVE:
|
|
1240
|
+
cand = os.path.join(_WS_ACTIVE, base)
|
|
1241
|
+
if os.path.isfile(cand):
|
|
1242
|
+
return cand
|
|
1223
1243
|
hits = _glob.glob(os.path.join(
|
|
1224
|
-
os.path.expanduser("~"), ".gonext", "rag-work", "*",
|
|
1244
|
+
os.path.expanduser("~"), ".gonext", "rag-work", "*", base))
|
|
1225
1245
|
if hits:
|
|
1226
1246
|
return hits[0]
|
|
1227
1247
|
return zp
|
|
@@ -1424,6 +1444,12 @@ def _rag_parse_location(loc: str):
|
|
|
1424
1444
|
# require _ws_write_allowed (workspace root + not inside .git/).
|
|
1425
1445
|
_WS_ROOTS: list = [] # [{"name": str, "path": realpath str, "allowRun": bool}]
|
|
1426
1446
|
|
|
1447
|
+
# The active terminal workspace: the folder the `gonext` REPL was launched from
|
|
1448
|
+
# (passed through as payload.activeWorkspace). When set, download_file/unzip_file put
|
|
1449
|
+
# their output HERE instead of the shared ~/.gonext/rag-work cache, so files land in
|
|
1450
|
+
# the user's actual project folder. Only honoured if it's a registered workspace root.
|
|
1451
|
+
_WS_ACTIVE: str = ""
|
|
1452
|
+
|
|
1427
1453
|
|
|
1428
1454
|
def _rag_read_allowed(path: str) -> str:
|
|
1429
1455
|
"""Resolve `path` and ensure it stays within a safe root (worker cwd, temp dir,
|
|
@@ -1741,7 +1767,7 @@ def run_agent_chat(cfg):
|
|
|
1741
1767
|
# Registered on the Mac via `gonext-local-worker workspace add <path>`; the worker
|
|
1742
1768
|
# passes the registry through cfg. Only these roots are writable — the security
|
|
1743
1769
|
# boundary for the coding tools.
|
|
1744
|
-
global _WS_ROOTS
|
|
1770
|
+
global _WS_ROOTS, _WS_ACTIVE
|
|
1745
1771
|
_WS_ROOTS = []
|
|
1746
1772
|
for w in (cfg.get("workspaces") or []):
|
|
1747
1773
|
try:
|
|
@@ -1756,6 +1782,20 @@ def run_agent_chat(cfg):
|
|
|
1756
1782
|
except Exception: # noqa: BLE001
|
|
1757
1783
|
continue
|
|
1758
1784
|
_WS_AVAILABLE = bool(_WS_ROOTS)
|
|
1785
|
+
# Active terminal workspace (the `gonext` REPL's launch folder) — download_file /
|
|
1786
|
+
# unzip_file put their output here. Only honoured if it's a registered workspace
|
|
1787
|
+
# root, so it can't redirect writes to an arbitrary folder.
|
|
1788
|
+
_WS_ACTIVE = ""
|
|
1789
|
+
try:
|
|
1790
|
+
import os as _os
|
|
1791
|
+
aw = _os.path.realpath(_os.path.expanduser(str(cfg.get("activeWorkspace") or "")))
|
|
1792
|
+
if aw and _os.path.isdir(aw) and any(
|
|
1793
|
+
aw == w["path"] or aw.startswith(w["path"] + _os.sep) for w in _WS_ROOTS
|
|
1794
|
+
):
|
|
1795
|
+
_WS_ACTIVE = aw
|
|
1796
|
+
_log(f"active workspace: {aw} (download/unzip output → here)")
|
|
1797
|
+
except Exception: # noqa: BLE001
|
|
1798
|
+
_WS_ACTIVE = ""
|
|
1759
1799
|
if _WS_AVAILABLE:
|
|
1760
1800
|
_log(f"workspaces: {[(w['name'], w['path'], w['allowRun']) for w in _WS_ROOTS]}")
|
|
1761
1801
|
# Coding tasks are edit→test→fix loops — 5 steps is too tight. Bump the default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gonext-local-worker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.178",
|
|
4
4
|
"description": "Polls GoNext cloud API for async local LLM jobs and runs them against Ollama/OpenAI-compatible servers on this Mac",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|