getbased-agent-stack 0.5.2__tar.gz → 0.5.4__tar.gz

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 (24) hide show
  1. {getbased_agent_stack-0.5.2/src/getbased_agent_stack.egg-info → getbased_agent_stack-0.5.4}/PKG-INFO +7 -6
  2. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/README.md +4 -3
  3. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/pyproject.toml +3 -3
  4. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack/__init__.py +1 -1
  5. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack/cli.py +21 -3
  6. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack/mcp_configs.py +4 -3
  7. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack/units.py +16 -1
  8. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4/src/getbased_agent_stack.egg-info}/PKG-INFO +7 -6
  9. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack.egg-info/requires.txt +2 -2
  10. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/tests/test_cli.py +7 -1
  11. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/tests/test_units.py +3 -1
  12. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/LICENSE +0 -0
  13. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/setup.cfg +0 -0
  14. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack/env_file.py +0 -0
  15. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack/systemd/getbased-dashboard.service +0 -0
  16. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack/systemd/getbased-rag.service +0 -0
  17. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack.egg-info/SOURCES.txt +0 -0
  18. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack.egg-info/dependency_links.txt +0 -0
  19. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack.egg-info/entry_points.txt +0 -0
  20. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/src/getbased_agent_stack.egg-info/top_level.txt +0 -0
  21. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/tests/test_env_file.py +0 -0
  22. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/tests/test_integration.py +0 -0
  23. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/tests/test_mcp_configs.py +0 -0
  24. {getbased_agent_stack-0.5.2 → getbased_agent_stack-0.5.4}/tests/test_systemd_units.py +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: getbased-agent-stack
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: One-command install of the full getbased agent stack — getbased-mcp + getbased-rag + getbased-dashboard
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Requires-Python: >=3.10
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
- Requires-Dist: getbased-mcp>=0.2.5
9
+ Requires-Dist: getbased-mcp>=0.2.6
10
10
  Requires-Dist: getbased-rag>=0.7.2
11
- Requires-Dist: getbased-dashboard>=0.6.2
11
+ Requires-Dist: getbased-dashboard>=0.6.4
12
12
  Provides-Extra: full
13
13
  Requires-Dist: getbased-rag[full]>=0.7.2; extra == "full"
14
14
  Provides-Extra: test
@@ -79,7 +79,7 @@ getbased-stack init --yes
79
79
 
80
80
  The wizard (~30 seconds):
81
81
 
82
- 1. Prompts for your `GETBASED_TOKEN` (skip if you don't use sync; `--yes` skips)
82
+ 1. Prompts for your `GETBASED_TOKEN` and `GETBASED_AGENT_CONTEXT_KEY` (skip if you don't use Agent Access; `--yes` keeps current values and lets you set them later with `getbased-stack set ...`)
83
83
  2. Generates a rag API key if one doesn't exist
84
84
  3. Writes `~/.config/getbased/env` (mode 0600) — the shared config file
85
85
  4. Installs systemd user units for rag + dashboard, enables them, starts them
@@ -122,7 +122,8 @@ sudo loginctl enable-linger $USER
122
122
 
123
123
  ```bash
124
124
  getbased-stack status # env file, unit state, linger
125
- getbased-stack set GETBASED_TOKEN=new # rotate the token
125
+ getbased-stack set GETBASED_TOKEN=new
126
+ getbased-stack set GETBASED_AGENT_CONTEXT_KEY=new
126
127
  getbased-stack install # re-apply unit files after package upgrade
127
128
  getbased-stack uninstall # stop + disable + remove units
128
129
  ```
@@ -149,7 +150,7 @@ sync GW getbased-rag ◀──────────────┘ g
149
150
 
150
151
  The MCP holds no persistent state; it's a thin translator between MCP tool calls and two HTTP backends:
151
152
 
152
- - `sync.getbased.health/api/context` — read-only lab summary pushed by your PWA session (via Agent Access token)
153
+ - `sync.getbased.health/api/context` — encrypted lab context pushed by your PWA session (authorized by Agent Access token, decrypted locally with Agent Context key)
153
154
  - `localhost:8322` (getbased-rag) — your local research library
154
155
 
155
156
  The dashboard is likewise stateless — it proxies rag for Knowledge operations, imports `getbased_mcp` to introspect env/config, and spawns the MCP binary on demand to verify it works.
@@ -60,7 +60,7 @@ getbased-stack init --yes
60
60
 
61
61
  The wizard (~30 seconds):
62
62
 
63
- 1. Prompts for your `GETBASED_TOKEN` (skip if you don't use sync; `--yes` skips)
63
+ 1. Prompts for your `GETBASED_TOKEN` and `GETBASED_AGENT_CONTEXT_KEY` (skip if you don't use Agent Access; `--yes` keeps current values and lets you set them later with `getbased-stack set ...`)
64
64
  2. Generates a rag API key if one doesn't exist
65
65
  3. Writes `~/.config/getbased/env` (mode 0600) — the shared config file
66
66
  4. Installs systemd user units for rag + dashboard, enables them, starts them
@@ -103,7 +103,8 @@ sudo loginctl enable-linger $USER
103
103
 
104
104
  ```bash
105
105
  getbased-stack status # env file, unit state, linger
106
- getbased-stack set GETBASED_TOKEN=new # rotate the token
106
+ getbased-stack set GETBASED_TOKEN=new
107
+ getbased-stack set GETBASED_AGENT_CONTEXT_KEY=new
107
108
  getbased-stack install # re-apply unit files after package upgrade
108
109
  getbased-stack uninstall # stop + disable + remove units
109
110
  ```
@@ -130,7 +131,7 @@ sync GW getbased-rag ◀──────────────┘ g
130
131
 
131
132
  The MCP holds no persistent state; it's a thin translator between MCP tool calls and two HTTP backends:
132
133
 
133
- - `sync.getbased.health/api/context` — read-only lab summary pushed by your PWA session (via Agent Access token)
134
+ - `sync.getbased.health/api/context` — encrypted lab context pushed by your PWA session (authorized by Agent Access token, decrypted locally with Agent Context key)
134
135
  - `localhost:8322` (getbased-rag) — your local research library
135
136
 
136
137
  The dashboard is likewise stateless — it proxies rag for Knowledge operations, imports `getbased_mcp` to introspect env/config, and spawns the MCP binary on demand to verify it works.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "getbased-agent-stack"
7
- version = "0.5.2"
7
+ version = "0.5.4"
8
8
  description = "One-command install of the full getbased agent stack — getbased-mcp + getbased-rag + getbased-dashboard"
9
9
  readme = "README.md"
10
10
  license = "AGPL-3.0-or-later"
@@ -12,9 +12,9 @@ requires-python = ">=3.10"
12
12
  # Pulls every sibling package. Bump this meta when a sibling protocol
13
13
  # bump requires coordinated release.
14
14
  dependencies = [
15
- "getbased-mcp>=0.2.5",
15
+ "getbased-mcp>=0.2.6",
16
16
  "getbased-rag>=0.7.2",
17
- "getbased-dashboard>=0.6.2",
17
+ "getbased-dashboard>=0.6.4",
18
18
  ]
19
19
 
20
20
  [project.optional-dependencies]
@@ -5,4 +5,4 @@ plus a small CLI that proxies to the real binaries. Everything
5
5
  interesting lives in the sibling repos.
6
6
  """
7
7
 
8
- __version__ = "0.4.1"
8
+ __version__ = "0.5.4"
@@ -100,20 +100,28 @@ def cmd_init(args: argparse.Namespace) -> int:
100
100
  )
101
101
  print()
102
102
 
103
- # 1. token (optional)
103
+ # 1. Agent Access secrets (optional)
104
104
  existing = env_file.read_env_file()
105
105
  current_token = existing.get("GETBASED_TOKEN", "")
106
+ current_context_key = existing.get("GETBASED_AGENT_CONTEXT_KEY", "")
106
107
  masked = "****" + current_token[-4:] if current_token else "(unset)"
107
- print(f"[1/4] getbased sync token (current: {masked})")
108
+ key_masked = "****" + current_context_key[-4:] if current_context_key else "(unset)"
109
+ print(f"[1/4] Agent Access token + context key (token: {masked}, key: {key_masked})")
108
110
  if non_interactive:
109
111
  token = current_token
110
- print(" keeping current value (set with `getbased-stack set GETBASED_TOKEN=…` later).")
112
+ context_key = current_context_key
113
+ print(" keeping current values (set with `getbased-stack set GETBASED_TOKEN=…` and `getbased-stack set GETBASED_AGENT_CONTEXT_KEY=…` later).")
111
114
  else:
112
115
  token = _prompt(
113
116
  "Paste GETBASED_TOKEN (press Enter to keep current / skip)",
114
117
  default=current_token,
115
118
  secret=True,
116
119
  )
120
+ context_key = _prompt(
121
+ "Paste GETBASED_AGENT_CONTEXT_KEY (press Enter to keep current / skip)",
122
+ default=current_context_key,
123
+ secret=True,
124
+ )
117
125
 
118
126
  # 2. API key
119
127
  key_path = Path(existing.get("LENS_API_KEY_FILE", str(_default_api_key_file())))
@@ -131,6 +139,8 @@ def cmd_init(args: argparse.Namespace) -> int:
131
139
  merged["GETBASED_STACK_MANAGED"] = "1"
132
140
  if token:
133
141
  merged["GETBASED_TOKEN"] = token
142
+ if context_key:
143
+ merged["GETBASED_AGENT_CONTEXT_KEY"] = context_key
134
144
  merged["LENS_API_KEY_FILE"] = str(key_path)
135
145
  merged.setdefault("LENS_URL", "http://127.0.0.1:8322")
136
146
  path = env_file.write_env_file(merged)
@@ -153,6 +163,14 @@ def cmd_init(args: argparse.Namespace) -> int:
153
163
  _print_linger_hint(strict=False)
154
164
 
155
165
  # 6. MCP config pointers
166
+ print("\nNext steps for Agent Access:")
167
+ print(" 1. In getbased, enable Cross-device Sync and Agent Access.")
168
+ print(" 2. Copy both values from Settings → Agent Access:")
169
+ print(" GETBASED_TOKEN and GETBASED_AGENT_CONTEXT_KEY.")
170
+ print(" 3. Save them here with:")
171
+ print(" getbased-stack set GETBASED_TOKEN=...")
172
+ print(" getbased-stack set GETBASED_AGENT_CONTEXT_KEY=...")
173
+
156
174
  print("\nConfigure your MCP client(s):")
157
175
  for client in mcp_configs.SUPPORTED_CLIENTS:
158
176
  print(f" getbased-stack mcp-config {client}")
@@ -133,9 +133,10 @@ def emit_hermes(resolver: Callable[[str], "str | None"] = shutil.which) -> str:
133
133
  lines = [
134
134
  "# Hermes Agent MCP configuration snippet for ~/.hermes/config.yaml",
135
135
  "# See https://github.com/hermes-agent/hermes-agent for the full config schema.",
136
- "# The getbased stack's shared env file carries GETBASED_TOKEN + rag URL +",
137
- "# api key path; only the opt-in flag belongs in Hermes's config.",
138
- "# (If your Hermes config already sets GETBASED_TOKEN / LENS_* explicitly,",
136
+ "# The getbased stack's shared env file carries GETBASED_TOKEN,",
137
+ "# GETBASED_AGENT_CONTEXT_KEY, rag URL, and api key path; only the",
138
+ "# opt-in flag belongs in Hermes's config.",
139
+ "# (If your Hermes config already sets GETBASED_TOKEN / GETBASED_AGENT_CONTEXT_KEY / LENS_* explicitly,",
139
140
  "# drop the env block entirely — the Python loader honors existing env.)",
140
141
  ]
141
142
  if warning:
@@ -108,6 +108,17 @@ class UnitManager:
108
108
  args.extend(SERVICE_NAMES)
109
109
  return self._shell(args)
110
110
 
111
+ def restart(self) -> CommandResult:
112
+ """Restart bundled services after an upgrade/reinstall.
113
+
114
+ `uv tool install` replaces files inside the tool environment. If the
115
+ services are already running, `systemctl enable --now …` is a no-op and
116
+ the old Python processes can keep serving from a half-replaced venv.
117
+ Restarting makes repeated `curl | bash` installs safe and starts the
118
+ units when they were previously inactive.
119
+ """
120
+ return self._shell(["systemctl", "--user", "restart", *SERVICE_NAMES])
121
+
111
122
  def disable(self, now: bool = True) -> CommandResult:
112
123
  args = ["systemctl", "--user", "disable"]
113
124
  if now:
@@ -154,7 +165,11 @@ class UnitManager:
154
165
  else:
155
166
  log.append("enabled " + ", ".join(SERVICE_NAMES))
156
167
  if start:
157
- log.append("started " + ", ".join(SERVICE_NAMES))
168
+ r = self.restart()
169
+ if r.returncode != 0:
170
+ log.append(f"restart FAILED: {r.stderr.strip()}")
171
+ else:
172
+ log.append("restarted " + ", ".join(SERVICE_NAMES))
158
173
  return log
159
174
 
160
175
  def uninstall(self) -> "list[str]":
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: getbased-agent-stack
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: One-command install of the full getbased agent stack — getbased-mcp + getbased-rag + getbased-dashboard
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Requires-Python: >=3.10
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
- Requires-Dist: getbased-mcp>=0.2.5
9
+ Requires-Dist: getbased-mcp>=0.2.6
10
10
  Requires-Dist: getbased-rag>=0.7.2
11
- Requires-Dist: getbased-dashboard>=0.6.2
11
+ Requires-Dist: getbased-dashboard>=0.6.4
12
12
  Provides-Extra: full
13
13
  Requires-Dist: getbased-rag[full]>=0.7.2; extra == "full"
14
14
  Provides-Extra: test
@@ -79,7 +79,7 @@ getbased-stack init --yes
79
79
 
80
80
  The wizard (~30 seconds):
81
81
 
82
- 1. Prompts for your `GETBASED_TOKEN` (skip if you don't use sync; `--yes` skips)
82
+ 1. Prompts for your `GETBASED_TOKEN` and `GETBASED_AGENT_CONTEXT_KEY` (skip if you don't use Agent Access; `--yes` keeps current values and lets you set them later with `getbased-stack set ...`)
83
83
  2. Generates a rag API key if one doesn't exist
84
84
  3. Writes `~/.config/getbased/env` (mode 0600) — the shared config file
85
85
  4. Installs systemd user units for rag + dashboard, enables them, starts them
@@ -122,7 +122,8 @@ sudo loginctl enable-linger $USER
122
122
 
123
123
  ```bash
124
124
  getbased-stack status # env file, unit state, linger
125
- getbased-stack set GETBASED_TOKEN=new # rotate the token
125
+ getbased-stack set GETBASED_TOKEN=new
126
+ getbased-stack set GETBASED_AGENT_CONTEXT_KEY=new
126
127
  getbased-stack install # re-apply unit files after package upgrade
127
128
  getbased-stack uninstall # stop + disable + remove units
128
129
  ```
@@ -149,7 +150,7 @@ sync GW getbased-rag ◀──────────────┘ g
149
150
 
150
151
  The MCP holds no persistent state; it's a thin translator between MCP tool calls and two HTTP backends:
151
152
 
152
- - `sync.getbased.health/api/context` — read-only lab summary pushed by your PWA session (via Agent Access token)
153
+ - `sync.getbased.health/api/context` — encrypted lab context pushed by your PWA session (authorized by Agent Access token, decrypted locally with Agent Context key)
153
154
  - `localhost:8322` (getbased-rag) — your local research library
154
155
 
155
156
  The dashboard is likewise stateless — it proxies rag for Knowledge operations, imports `getbased_mcp` to introspect env/config, and spawns the MCP binary on demand to verify it works.
@@ -1,6 +1,6 @@
1
- getbased-mcp>=0.2.5
1
+ getbased-mcp>=0.2.6
2
2
  getbased-rag>=0.7.2
3
- getbased-dashboard>=0.6.2
3
+ getbased-dashboard>=0.6.4
4
4
 
5
5
  [full]
6
6
  getbased-rag[full]>=0.7.2
@@ -96,10 +96,14 @@ def test_install_writes_units_and_enables(stack_home, fake_shell):
96
96
  assert (unit_dir / "getbased-rag.service").exists()
97
97
  assert (unit_dir / "getbased-dashboard.service").exists()
98
98
 
99
- # daemon-reload and enable --now both ran
99
+ # daemon-reload, enable --now, and restart all run. The explicit restart
100
+ # matters for re-running install.sh over an active stack after uv replaces
101
+ # the tool environment: old Python processes must not keep serving from a
102
+ # half-replaced venv.
100
103
  all_calls = [" ".join(cmd) for cmd in fake_shell]
101
104
  assert any("daemon-reload" in c for c in all_calls)
102
105
  assert any("enable --now" in c for c in all_calls)
106
+ assert any("restart getbased-rag.service getbased-dashboard.service" in c for c in all_calls)
103
107
 
104
108
 
105
109
  def test_install_no_enable_flag(stack_home, fake_shell):
@@ -313,6 +317,8 @@ def test_init_yes_flag_skips_all_prompts(stack_home, fake_shell, monkeypatch):
313
317
  assert rc == 0
314
318
  # Banner reflects the mode so the user sees what happened
315
319
  assert "non-interactive" in out.lower()
320
+ assert "GETBASED_TOKEN and GETBASED_AGENT_CONTEXT_KEY" in out
321
+ assert "getbased-stack set GETBASED_AGENT_CONTEXT_KEY" in out
316
322
  # Env file + units still land
317
323
  assert env_file.env_file_path().exists()
318
324
  assert (stack_home / "config" / "systemd" / "user" / "getbased-rag.service").exists()
@@ -145,11 +145,13 @@ def test_install_full_sequence(tmp_path):
145
145
  # systemctl called in the expected order
146
146
  assert shell.calls[0] == ["systemctl", "--user", "daemon-reload"]
147
147
  assert shell.calls[1] == ["systemctl", "--user", "enable", "--now", *SERVICE_NAMES]
148
+ assert shell.calls[2] == ["systemctl", "--user", "restart", *SERVICE_NAMES]
148
149
  # Log mentions what happened
149
150
  joined = "\n".join(log)
150
151
  assert "wrote" in joined
151
152
  assert "enabled" in joined
152
- assert "started" in joined
153
+ assert "restarted" in joined
154
+ assert not any(line.startswith("started ") for line in log)
153
155
 
154
156
 
155
157
  def test_install_daemon_reload_failure_short_circuits(tmp_path):