powerbase-cli 0.1.4__tar.gz → 0.2.0__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.
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/PKG-INFO +19 -5
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/README.md +18 -4
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/pyproject.toml +1 -4
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/__init__.py +1 -1
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/agent.py +8 -5
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/branch.py +4 -1
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/context.py +4 -1
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/parser.py +3 -16
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/publish.py +13 -3
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/sandbox.py +20 -2
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/shared.py +3 -15
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/config.py +4 -54
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/session.py +1 -20
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/transport.py +1 -15
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli.egg-info/PKG-INFO +19 -5
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli.egg-info/SOURCES.txt +0 -1
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/tests/test_cli_commands.py +5 -26
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/tests/test_cli_help.py +42 -5
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/tests/test_config.py +1 -15
- powerbase_cli-0.2.0/tests/test_session.py +97 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/tests/test_transport.py +1 -66
- powerbase_cli-0.1.4/src/powerbase_cli/certs/powerbase-test-ca.pem +0 -21
- powerbase_cli-0.1.4/tests/test_session.py +0 -194
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/setup.cfg +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/__main__.py +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/api.py +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/cli.py +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/__init__.py +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/auth.py +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/config_cmd.py +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/database.py +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/instance.py +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/org.py +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli/commands/sql.py +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli.egg-info/dependency_links.txt +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli.egg-info/entry_points.txt +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/src/powerbase_cli.egg-info/top_level.txt +0 -0
- {powerbase_cli-0.1.4 → powerbase_cli-0.2.0}/tests/test_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: powerbase-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: CLI for operating Powerbase console workflows
|
|
5
5
|
Author: Powerbase
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -41,10 +41,12 @@ Database model:
|
|
|
41
41
|
Install from PyPI:
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
python -m pip install powerbase-cli
|
|
44
|
+
python -m pip install --upgrade powerbase-cli==0.2.0
|
|
45
45
|
powerbase --help
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
Version `0.2.x` is the production-pinned release line and always targets `https://console.appbuild.chat`.
|
|
49
|
+
|
|
48
50
|
From the repository root during development:
|
|
49
51
|
|
|
50
52
|
```bash
|
|
@@ -121,7 +123,7 @@ powerbase auth token-set \
|
|
|
121
123
|
--expires-at 1760000000
|
|
122
124
|
```
|
|
123
125
|
|
|
124
|
-
|
|
126
|
+
The CLI is pinned to the production Powerbase console deployment and no longer exposes endpoint override flags.
|
|
125
127
|
|
|
126
128
|
### Check Auth State
|
|
127
129
|
|
|
@@ -240,6 +242,8 @@ powerbase agent login-url --instance-id INSTANCE_ID --provider cursor --json
|
|
|
240
242
|
|
|
241
243
|
### Sandbox Agent Chat And Config
|
|
242
244
|
|
|
245
|
+
Powerbase provides a coding agent so users can build and evolve Powerbase-hosted apps through chat. `powerbase agent chat` is the default implementation interface for that workflow, while the rest of the CLI supports context, validation, and deployment around it. In the current product workflow, successful `agent chat` runs are also the normal path that produces previewable app changes.
|
|
246
|
+
|
|
243
247
|
Send one prompt to the sandbox agent and stream events as JSONL:
|
|
244
248
|
|
|
245
249
|
```bash
|
|
@@ -257,6 +261,9 @@ powerbase agent chat \
|
|
|
257
261
|
feature branch. `powerbase context use-branch ...` only changes the local default value;
|
|
258
262
|
it does not switch the remote sandbox by itself.
|
|
259
263
|
|
|
264
|
+
Treat `powerbase sandbox files ...`, `powerbase sql run`, and `powerbase publish diff` as inspection and validation tools around the Powerbase coding agent. Treat `powerbase publish run` as an explicit deployment action and do not run it unless the user asks to publish, deploy, release, or promote the result.
|
|
265
|
+
Direct `powerbase sandbox files create-file`, `upload`, or `delete` operations can help inspect or adjust the remote project, but they do not by themselves trigger the preview build flow.
|
|
266
|
+
|
|
260
267
|
Read and update opencode provider config:
|
|
261
268
|
|
|
262
269
|
```bash
|
|
@@ -278,11 +285,16 @@ When Openclaw or another LLM agent uses `powerbase`:
|
|
|
278
285
|
- prefer `--json` on all commands
|
|
279
286
|
- run discovery commands before write operations
|
|
280
287
|
- set context for long multi-step tasks
|
|
288
|
+
- treat `powerbase agent chat` as the default implementation interface for Powerbase-hosted apps
|
|
289
|
+
- treat successful `powerbase agent chat` runs as the normal path that produces previewable app changes
|
|
281
290
|
- use `auth status` before workflows that assume login
|
|
282
291
|
- use `auth login --json` to generate a login URL, then stop and hand `login_url` to the user
|
|
283
292
|
- do not have the agent open the browser login URL itself; the user must complete that approval step
|
|
284
293
|
- after the user confirms approval, run `auth wait --login-id ... --json` to complete login
|
|
285
294
|
- if a protected command says authentication is missing or expired, rerun `auth login --json` to generate a fresh login URL
|
|
295
|
+
- use `sandbox files`, `sql run`, and `publish diff` for inspection or validation around the Powerbase coding agent
|
|
296
|
+
- do not assume `sandbox files` write operations will trigger preview by themselves
|
|
297
|
+
- do not run `publish run` unless the user explicitly asks to publish, deploy, release, or promote the result
|
|
286
298
|
- remember that `--json` overrides a saved `config output text` setting for that command
|
|
287
299
|
|
|
288
300
|
Recommended agent sequence:
|
|
@@ -296,7 +308,9 @@ Recommended agent sequence:
|
|
|
296
308
|
7. Use `powerbase database ...` only for the advanced bring-your-own-database path
|
|
297
309
|
8. Set context with `powerbase context use-instance ...`
|
|
298
310
|
9. If you want the sandbox agent preview on a non-main branch, run `powerbase branch switch ...`
|
|
299
|
-
10.
|
|
311
|
+
10. Use `powerbase agent chat ...` as the primary implementation step for Powerbase-hosted app changes
|
|
312
|
+
11. Use `sandbox files`, `sql`, and `publish diff` to inspect or validate the result
|
|
313
|
+
12. Run `publish run` only when the user explicitly wants deployment or publication
|
|
300
314
|
|
|
301
315
|
## Testing
|
|
302
316
|
|
|
@@ -316,7 +330,7 @@ PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src python3 -m py_compile src/powerbase_cli
|
|
|
316
330
|
|
|
317
331
|
A project skill for Cursor/Openclaw-style agent usage lives in:
|
|
318
332
|
|
|
319
|
-
|
|
333
|
+
`./skills/powerbase-cli/SKILL.md`
|
|
320
334
|
|
|
321
335
|
Design and auth review notes live in:
|
|
322
336
|
|
|
@@ -33,10 +33,12 @@ Database model:
|
|
|
33
33
|
Install from PyPI:
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
python -m pip install powerbase-cli
|
|
36
|
+
python -m pip install --upgrade powerbase-cli==0.2.0
|
|
37
37
|
powerbase --help
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
Version `0.2.x` is the production-pinned release line and always targets `https://console.appbuild.chat`.
|
|
41
|
+
|
|
40
42
|
From the repository root during development:
|
|
41
43
|
|
|
42
44
|
```bash
|
|
@@ -113,7 +115,7 @@ powerbase auth token-set \
|
|
|
113
115
|
--expires-at 1760000000
|
|
114
116
|
```
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
The CLI is pinned to the production Powerbase console deployment and no longer exposes endpoint override flags.
|
|
117
119
|
|
|
118
120
|
### Check Auth State
|
|
119
121
|
|
|
@@ -232,6 +234,8 @@ powerbase agent login-url --instance-id INSTANCE_ID --provider cursor --json
|
|
|
232
234
|
|
|
233
235
|
### Sandbox Agent Chat And Config
|
|
234
236
|
|
|
237
|
+
Powerbase provides a coding agent so users can build and evolve Powerbase-hosted apps through chat. `powerbase agent chat` is the default implementation interface for that workflow, while the rest of the CLI supports context, validation, and deployment around it. In the current product workflow, successful `agent chat` runs are also the normal path that produces previewable app changes.
|
|
238
|
+
|
|
235
239
|
Send one prompt to the sandbox agent and stream events as JSONL:
|
|
236
240
|
|
|
237
241
|
```bash
|
|
@@ -249,6 +253,9 @@ powerbase agent chat \
|
|
|
249
253
|
feature branch. `powerbase context use-branch ...` only changes the local default value;
|
|
250
254
|
it does not switch the remote sandbox by itself.
|
|
251
255
|
|
|
256
|
+
Treat `powerbase sandbox files ...`, `powerbase sql run`, and `powerbase publish diff` as inspection and validation tools around the Powerbase coding agent. Treat `powerbase publish run` as an explicit deployment action and do not run it unless the user asks to publish, deploy, release, or promote the result.
|
|
257
|
+
Direct `powerbase sandbox files create-file`, `upload`, or `delete` operations can help inspect or adjust the remote project, but they do not by themselves trigger the preview build flow.
|
|
258
|
+
|
|
252
259
|
Read and update opencode provider config:
|
|
253
260
|
|
|
254
261
|
```bash
|
|
@@ -270,11 +277,16 @@ When Openclaw or another LLM agent uses `powerbase`:
|
|
|
270
277
|
- prefer `--json` on all commands
|
|
271
278
|
- run discovery commands before write operations
|
|
272
279
|
- set context for long multi-step tasks
|
|
280
|
+
- treat `powerbase agent chat` as the default implementation interface for Powerbase-hosted apps
|
|
281
|
+
- treat successful `powerbase agent chat` runs as the normal path that produces previewable app changes
|
|
273
282
|
- use `auth status` before workflows that assume login
|
|
274
283
|
- use `auth login --json` to generate a login URL, then stop and hand `login_url` to the user
|
|
275
284
|
- do not have the agent open the browser login URL itself; the user must complete that approval step
|
|
276
285
|
- after the user confirms approval, run `auth wait --login-id ... --json` to complete login
|
|
277
286
|
- if a protected command says authentication is missing or expired, rerun `auth login --json` to generate a fresh login URL
|
|
287
|
+
- use `sandbox files`, `sql run`, and `publish diff` for inspection or validation around the Powerbase coding agent
|
|
288
|
+
- do not assume `sandbox files` write operations will trigger preview by themselves
|
|
289
|
+
- do not run `publish run` unless the user explicitly asks to publish, deploy, release, or promote the result
|
|
278
290
|
- remember that `--json` overrides a saved `config output text` setting for that command
|
|
279
291
|
|
|
280
292
|
Recommended agent sequence:
|
|
@@ -288,7 +300,9 @@ Recommended agent sequence:
|
|
|
288
300
|
7. Use `powerbase database ...` only for the advanced bring-your-own-database path
|
|
289
301
|
8. Set context with `powerbase context use-instance ...`
|
|
290
302
|
9. If you want the sandbox agent preview on a non-main branch, run `powerbase branch switch ...`
|
|
291
|
-
10.
|
|
303
|
+
10. Use `powerbase agent chat ...` as the primary implementation step for Powerbase-hosted app changes
|
|
304
|
+
11. Use `sandbox files`, `sql`, and `publish diff` to inspect or validate the result
|
|
305
|
+
12. Run `publish run` only when the user explicitly wants deployment or publication
|
|
292
306
|
|
|
293
307
|
## Testing
|
|
294
308
|
|
|
@@ -308,7 +322,7 @@ PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src python3 -m py_compile src/powerbase_cli
|
|
|
308
322
|
|
|
309
323
|
A project skill for Cursor/Openclaw-style agent usage lives in:
|
|
310
324
|
|
|
311
|
-
|
|
325
|
+
`./skills/powerbase-cli/SKILL.md`
|
|
312
326
|
|
|
313
327
|
Design and auth review notes live in:
|
|
314
328
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "powerbase-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "CLI for operating Powerbase console workflows"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -17,8 +17,5 @@ powerbase = "powerbase_cli.cli:main"
|
|
|
17
17
|
[tool.setuptools]
|
|
18
18
|
package-dir = { "" = "src" }
|
|
19
19
|
|
|
20
|
-
[tool.setuptools.package-data]
|
|
21
|
-
powerbase_cli = ["certs/*.pem"]
|
|
22
|
-
|
|
23
20
|
[tool.setuptools.packages.find]
|
|
24
21
|
where = ["src"]
|
|
@@ -115,11 +115,14 @@ def register_agent_commands(subparsers: argparse._SubParsersAction[argparse.Argu
|
|
|
115
115
|
"chat",
|
|
116
116
|
help="Send a prompt to the sandbox agent.",
|
|
117
117
|
description=(
|
|
118
|
-
"Send one prompt to the
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"the
|
|
122
|
-
"
|
|
118
|
+
"Send one prompt to the Powerbase coding agent running in an instance. This is the default "
|
|
119
|
+
"implementation interface when you want to build or evolve a Powerbase-hosted app through chat, "
|
|
120
|
+
"while the rest of the CLI handles context, validation, and deployment around that workflow. "
|
|
121
|
+
"In the current product workflow, successful runs here are also the normal path that produces "
|
|
122
|
+
"previewable app changes. "
|
|
123
|
+
"The automatic preview build follows the sandbox's current branch, so run `powerbase branch switch` "
|
|
124
|
+
"first when you want a feature-branch preview. By default the command collects all streamed events "
|
|
125
|
+
"and prints them as JSON. Use --stream-jsonl to print one JSON event per line as events arrive."
|
|
123
126
|
),
|
|
124
127
|
)
|
|
125
128
|
p.add_argument("--instance-id", help="Instance ID. Falls back to context.json.")
|
|
@@ -67,7 +67,10 @@ def register_branch_commands(subparsers: argparse._SubParsersAction[argparse.Arg
|
|
|
67
67
|
p = branch_sub.add_parser(
|
|
68
68
|
"switch",
|
|
69
69
|
help="Switch the sandbox branch.",
|
|
70
|
-
description=
|
|
70
|
+
description=(
|
|
71
|
+
"Switch the remote sandbox branch and update context.json. Use this before `powerbase agent chat` "
|
|
72
|
+
"when you want the coding agent and preview build to target a specific branch."
|
|
73
|
+
),
|
|
71
74
|
)
|
|
72
75
|
p.add_argument("branch_name", help="Branch slug to switch to.")
|
|
73
76
|
p.add_argument("--instance-id", help="Instance ID. Falls back to context.json.")
|
|
@@ -69,7 +69,10 @@ def register_context_commands(subparsers: argparse._SubParsersAction[argparse.Ar
|
|
|
69
69
|
p = context_sub.add_parser(
|
|
70
70
|
"use-branch",
|
|
71
71
|
help="Set the default branch.",
|
|
72
|
-
description=
|
|
72
|
+
description=(
|
|
73
|
+
"Set the default branch slug, such as main or feature/my-change. This updates only the local default "
|
|
74
|
+
"context value; it does not switch the remote sandbox branch used by previews or `powerbase agent chat`."
|
|
75
|
+
),
|
|
73
76
|
)
|
|
74
77
|
p.add_argument("branch")
|
|
75
78
|
p.set_defaults(handler=lambda args: _save_context_update(args, branch=args.branch))
|
|
@@ -19,10 +19,6 @@ from .sql import register_sql_commands
|
|
|
19
19
|
|
|
20
20
|
GLOBAL_OPTION_ARITY = {
|
|
21
21
|
"--config-dir": 1,
|
|
22
|
-
"--base-url": 1,
|
|
23
|
-
"--anon-key": 1,
|
|
24
|
-
"--ca-cert": 1,
|
|
25
|
-
"--insecure": 0,
|
|
26
22
|
"--json": 0,
|
|
27
23
|
}
|
|
28
24
|
|
|
@@ -62,6 +58,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
62
58
|
prog="powerbase",
|
|
63
59
|
description=(
|
|
64
60
|
"Operate Powerbase console workflows from the command line. "
|
|
61
|
+
"Powerbase supports agent-driven development for Powerbase-hosted apps, with "
|
|
62
|
+
"`powerbase agent chat` as the default implementation interface and the rest of the CLI "
|
|
63
|
+
"covering auth, context, resources, validation, and publish operations. "
|
|
65
64
|
"Default instance creation uses the managed Powerbase database flow; "
|
|
66
65
|
"`powerbase database ...` is the advanced bring-your-own-database path."
|
|
67
66
|
),
|
|
@@ -71,18 +70,6 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
71
70
|
),
|
|
72
71
|
)
|
|
73
72
|
parser.add_argument("--config-dir", help="Override the config directory. Defaults to ~/.config/powerbase.")
|
|
74
|
-
parser.add_argument("--base-url", help="Console base URL. Overrides the built-in default console endpoint.")
|
|
75
|
-
parser.add_argument("--anon-key", help="Supabase anon key used for functions and auth requests. Overrides the built-in default.")
|
|
76
|
-
parser.add_argument(
|
|
77
|
-
"--ca-cert",
|
|
78
|
-
dest="ca_cert_file",
|
|
79
|
-
help="Path to a CA certificate PEM file to trust for HTTPS requests.",
|
|
80
|
-
)
|
|
81
|
-
parser.add_argument(
|
|
82
|
-
"--insecure",
|
|
83
|
-
action="store_true",
|
|
84
|
-
help="Disable TLS certificate verification for HTTPS requests. Use only for testing with self-signed certs.",
|
|
85
|
-
)
|
|
86
73
|
parser.add_argument("--json", action="store_true", help="Print JSON output.")
|
|
87
74
|
|
|
88
75
|
subparsers = parser.add_subparsers(dest="command")
|
|
@@ -25,9 +25,17 @@ def handle_publish_run(args: argparse.Namespace) -> int:
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def register_publish_commands(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
28
|
-
publish = subparsers.add_parser(
|
|
28
|
+
publish = subparsers.add_parser(
|
|
29
|
+
"publish",
|
|
30
|
+
help="Publish commands.",
|
|
31
|
+
description="Inspect publish diffs and publish an instance when the user explicitly wants deployment.",
|
|
32
|
+
)
|
|
29
33
|
publish_sub = publish.add_subparsers(dest="publish_command")
|
|
30
|
-
p = publish_sub.add_parser(
|
|
34
|
+
p = publish_sub.add_parser(
|
|
35
|
+
"diff",
|
|
36
|
+
help="Show publish diff.",
|
|
37
|
+
description="Generate publish diff for the current instance as an inspection step before deployment.",
|
|
38
|
+
)
|
|
31
39
|
p.add_argument("--instance-id", help="Instance ID. Falls back to context.json.")
|
|
32
40
|
p.add_argument("--target", default="prod", help="Publish target. Default: prod")
|
|
33
41
|
p.set_defaults(handler=handle_publish_diff)
|
|
@@ -35,7 +43,9 @@ def register_publish_commands(subparsers: argparse._SubParsersAction[argparse.Ar
|
|
|
35
43
|
"run",
|
|
36
44
|
help="Run publish flow.",
|
|
37
45
|
description=(
|
|
38
|
-
"Publish the current instance.
|
|
46
|
+
"Publish the current instance. Use this only when the user explicitly asks to publish, deploy, "
|
|
47
|
+
"release, or promote the result. If --sql is omitted, the command first runs publish diff and uses "
|
|
48
|
+
"its SQL."
|
|
39
49
|
),
|
|
40
50
|
)
|
|
41
51
|
p.add_argument("--instance-id", help="Instance ID. Falls back to context.json.")
|
|
@@ -48,9 +48,27 @@ def handle_files_delete(args: argparse.Namespace) -> int:
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
def register_sandbox_commands(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
51
|
-
sandbox = subparsers.add_parser(
|
|
51
|
+
sandbox = subparsers.add_parser(
|
|
52
|
+
"sandbox",
|
|
53
|
+
help="Sandbox commands.",
|
|
54
|
+
description=(
|
|
55
|
+
"Inspect or modify the remote sandbox filesystem for a Powerbase instance. Treat this as the "
|
|
56
|
+
"remote inspection and control surface around the Powerbase coding agent, not the default full-app "
|
|
57
|
+
"implementation interface. Direct file operations here do not by themselves trigger the preview "
|
|
58
|
+
"build flow."
|
|
59
|
+
),
|
|
60
|
+
)
|
|
52
61
|
sandbox_sub = sandbox.add_subparsers(dest="sandbox_command")
|
|
53
|
-
files = sandbox_sub.add_parser(
|
|
62
|
+
files = sandbox_sub.add_parser(
|
|
63
|
+
"files",
|
|
64
|
+
help="Sandbox file commands.",
|
|
65
|
+
description=(
|
|
66
|
+
"Read or modify files in the remote Powerbase sandbox. Use these commands for inspection, validation, "
|
|
67
|
+
"and targeted file operations around agent-driven development. Successful `powerbase agent chat` runs "
|
|
68
|
+
"are the normal path for previewable app changes; these file operations do not trigger preview by "
|
|
69
|
+
"themselves."
|
|
70
|
+
),
|
|
71
|
+
)
|
|
54
72
|
files_sub = files.add_subparsers(dest="files_command")
|
|
55
73
|
p = files_sub.add_parser("list", help="List a directory.", description="List sandbox files at one path.")
|
|
56
74
|
p.add_argument("--instance-id", help="Instance ID. Falls back to context.json.")
|
|
@@ -8,7 +8,6 @@ from typing import Any
|
|
|
8
8
|
|
|
9
9
|
from ..api import PowerbaseApi
|
|
10
10
|
from ..config import (
|
|
11
|
-
BUNDLED_CA_CERT_SENTINEL,
|
|
12
11
|
DEFAULT_ANON_KEY,
|
|
13
12
|
DEFAULT_BASE_URL,
|
|
14
13
|
AppConfig,
|
|
@@ -16,8 +15,6 @@ from ..config import (
|
|
|
16
15
|
AuthState,
|
|
17
16
|
ConfigStore,
|
|
18
17
|
ContextState,
|
|
19
|
-
load_bundled_ca_cert,
|
|
20
|
-
merge_config_with_env,
|
|
21
18
|
merge_context_with_env,
|
|
22
19
|
)
|
|
23
20
|
from ..session import SessionManager
|
|
@@ -33,18 +30,11 @@ def build_store(args: argparse.Namespace) -> ConfigStore:
|
|
|
33
30
|
|
|
34
31
|
|
|
35
32
|
def resolve_config(args: argparse.Namespace, store: ConfigStore) -> AppConfig:
|
|
36
|
-
config =
|
|
37
|
-
saved_auth = store.load_auth()
|
|
38
|
-
explicit_ca_cert_file = getattr(args, "ca_cert_file", None) or config.ca_cert_file
|
|
33
|
+
config = store.load_config()
|
|
39
34
|
return AppConfig(
|
|
40
|
-
base_url=
|
|
41
|
-
anon_key=
|
|
35
|
+
base_url=DEFAULT_BASE_URL,
|
|
36
|
+
anon_key=DEFAULT_ANON_KEY,
|
|
42
37
|
output="json" if args.json else config.output,
|
|
43
|
-
tls_insecure=bool(getattr(args, "insecure", False) or config.tls_insecure),
|
|
44
|
-
# Keep explicit CA settings first. The bundled test CA is only the final fallback for the
|
|
45
|
-
# current self-signed test environment and should be removed once the deployment uses a
|
|
46
|
-
# publicly trusted certificate.
|
|
47
|
-
ca_cert_file=explicit_ca_cert_file or (BUNDLED_CA_CERT_SENTINEL if load_bundled_ca_cert() else None),
|
|
48
38
|
)
|
|
49
39
|
|
|
50
40
|
|
|
@@ -60,8 +50,6 @@ def build_api(args: argparse.Namespace) -> tuple[ConfigStore, AppConfig, Context
|
|
|
60
50
|
store,
|
|
61
51
|
config.base_url,
|
|
62
52
|
config.anon_key,
|
|
63
|
-
tls_insecure=config.tls_insecure,
|
|
64
|
-
ca_cert_file=config.ca_cert_file,
|
|
65
53
|
)
|
|
66
54
|
api = PowerbaseApi(PowerbaseTransport(config, session_manager))
|
|
67
55
|
return store, config, context, session_manager, api
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import importlib.resources
|
|
4
3
|
import json
|
|
5
4
|
import os
|
|
6
5
|
from dataclasses import asdict, dataclass
|
|
@@ -12,13 +11,8 @@ try:
|
|
|
12
11
|
except ModuleNotFoundError: # pragma: no cover
|
|
13
12
|
tomllib = None # type: ignore[assignment]
|
|
14
13
|
|
|
15
|
-
DEFAULT_BASE_URL = "https://console.
|
|
14
|
+
DEFAULT_BASE_URL = "https://console.appbuild.chat"
|
|
16
15
|
DEFAULT_ANON_KEY = "reallyreallyreallyreallyverysafe"
|
|
17
|
-
# This bundled CA is only for the self-signed test deployment so `pip install powerbase-cli`
|
|
18
|
-
# can work out of the box. Remove it after the deployed console switches to a publicly trusted
|
|
19
|
-
# TLS certificate, then delete the bundled PEM file and its package-data entry as well.
|
|
20
|
-
BUNDLED_CA_CERT_SENTINEL = "<bundled test CA>"
|
|
21
|
-
BUNDLED_CA_CERT_RESOURCE = "certs/powerbase-test-ca.pem"
|
|
22
16
|
|
|
23
17
|
|
|
24
18
|
def default_config_dir() -> Path:
|
|
@@ -33,8 +27,6 @@ class AppConfig:
|
|
|
33
27
|
base_url: str | None = DEFAULT_BASE_URL
|
|
34
28
|
anon_key: str | None = DEFAULT_ANON_KEY
|
|
35
29
|
output: str = "text"
|
|
36
|
-
tls_insecure: bool = False
|
|
37
|
-
ca_cert_file: str | None = None
|
|
38
30
|
|
|
39
31
|
|
|
40
32
|
@dataclass
|
|
@@ -69,26 +61,6 @@ def _as_path(path: str | Path | None) -> Path:
|
|
|
69
61
|
return Path(path).expanduser()
|
|
70
62
|
|
|
71
63
|
|
|
72
|
-
def env_flag(name: str) -> bool:
|
|
73
|
-
value = os.environ.get(name)
|
|
74
|
-
if value is None:
|
|
75
|
-
return False
|
|
76
|
-
return value.strip().lower() in {"1", "true", "yes", "on"}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def load_bundled_ca_cert() -> str | None:
|
|
80
|
-
# The PEM contents are loaded at runtime so test builds can trust the packaged self-signed CA
|
|
81
|
-
# without asking end users to pass `--ca-cert`. Once the server uses a trusted certificate,
|
|
82
|
-
# this helper and its callers can be removed.
|
|
83
|
-
try:
|
|
84
|
-
resource = importlib.resources.files("powerbase_cli").joinpath(BUNDLED_CA_CERT_RESOURCE)
|
|
85
|
-
if not resource.is_file():
|
|
86
|
-
return None
|
|
87
|
-
return resource.read_text(encoding="utf-8")
|
|
88
|
-
except (FileNotFoundError, ModuleNotFoundError):
|
|
89
|
-
return None
|
|
90
|
-
|
|
91
|
-
|
|
92
64
|
class ConfigStore:
|
|
93
65
|
def __init__(self, base_dir: str | Path | None = None) -> None:
|
|
94
66
|
self.base_dir = _as_path(base_dir)
|
|
@@ -101,31 +73,19 @@ class ConfigStore:
|
|
|
101
73
|
|
|
102
74
|
def load_config(self) -> AppConfig:
|
|
103
75
|
if not self.config_path.exists():
|
|
104
|
-
return AppConfig(
|
|
76
|
+
return AppConfig()
|
|
105
77
|
if tomllib is None:
|
|
106
78
|
raise RuntimeError("tomllib is required to read config.toml")
|
|
107
79
|
data = tomllib.loads(self.config_path.read_text(encoding="utf-8"))
|
|
108
80
|
return AppConfig(
|
|
109
|
-
base_url=data.get("base_url"),
|
|
110
|
-
anon_key=data.get("anon_key"),
|
|
111
81
|
output=data.get("output", "text"),
|
|
112
|
-
tls_insecure=bool(data.get("tls_insecure", False)),
|
|
113
|
-
ca_cert_file=data.get("ca_cert_file"),
|
|
114
82
|
)
|
|
115
83
|
|
|
116
84
|
def save_config(self, config: AppConfig) -> None:
|
|
117
85
|
self.ensure_base_dir()
|
|
118
86
|
lines = []
|
|
119
|
-
if config.base_url and config.base_url != DEFAULT_BASE_URL:
|
|
120
|
-
lines.append(f'base_url = "{config.base_url}"')
|
|
121
|
-
if config.anon_key and config.anon_key != DEFAULT_ANON_KEY:
|
|
122
|
-
lines.append(f'anon_key = "{config.anon_key}"')
|
|
123
87
|
if config.output:
|
|
124
88
|
lines.append(f'output = "{config.output}"')
|
|
125
|
-
if config.tls_insecure:
|
|
126
|
-
lines.append("tls_insecure = true")
|
|
127
|
-
if config.ca_cert_file:
|
|
128
|
-
lines.append(f'ca_cert_file = "{config.ca_cert_file}"')
|
|
129
89
|
self.config_path.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8")
|
|
130
90
|
|
|
131
91
|
def load_auth(self) -> AuthState | None:
|
|
@@ -210,8 +170,8 @@ def env_auth_state() -> AuthState | None:
|
|
|
210
170
|
expires_at = int(expires_at_raw) if expires_at_raw and expires_at_raw.isdigit() else None
|
|
211
171
|
return AuthState(
|
|
212
172
|
source="env",
|
|
213
|
-
base_url=
|
|
214
|
-
anon_key=
|
|
173
|
+
base_url=DEFAULT_BASE_URL,
|
|
174
|
+
anon_key=DEFAULT_ANON_KEY,
|
|
215
175
|
session=AuthSession(
|
|
216
176
|
access_token=access_token,
|
|
217
177
|
refresh_token=refresh_token,
|
|
@@ -220,16 +180,6 @@ def env_auth_state() -> AuthState | None:
|
|
|
220
180
|
)
|
|
221
181
|
|
|
222
182
|
|
|
223
|
-
def merge_config_with_env(config: AppConfig) -> AppConfig:
|
|
224
|
-
return AppConfig(
|
|
225
|
-
base_url=os.environ.get("POWERBASE_BASE_URL") or config.base_url,
|
|
226
|
-
anon_key=os.environ.get("POWERBASE_ANON_KEY") or config.anon_key,
|
|
227
|
-
output=os.environ.get("POWERBASE_OUTPUT") or config.output,
|
|
228
|
-
tls_insecure=env_flag("POWERBASE_TLS_INSECURE") or config.tls_insecure,
|
|
229
|
-
ca_cert_file=os.environ.get("POWERBASE_CA_CERT_FILE") or config.ca_cert_file,
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
|
|
233
183
|
def merge_context_with_env(context: ContextState) -> ContextState:
|
|
234
184
|
return ContextState(
|
|
235
185
|
instance_id=os.environ.get("POWERBASE_INSTANCE_ID") or context.instance_id,
|
|
@@ -11,7 +11,7 @@ from typing import Iterator
|
|
|
11
11
|
from urllib import request
|
|
12
12
|
from urllib.error import HTTPError, URLError
|
|
13
13
|
|
|
14
|
-
from .config import
|
|
14
|
+
from .config import AuthState, ConfigStore, env_auth_state
|
|
15
15
|
|
|
16
16
|
try:
|
|
17
17
|
import fcntl
|
|
@@ -29,15 +29,10 @@ class SessionManager:
|
|
|
29
29
|
store: ConfigStore,
|
|
30
30
|
base_url: str | None,
|
|
31
31
|
anon_key: str | None,
|
|
32
|
-
*,
|
|
33
|
-
tls_insecure: bool = False,
|
|
34
|
-
ca_cert_file: str | None = None,
|
|
35
32
|
) -> None:
|
|
36
33
|
self.store = store
|
|
37
34
|
self.base_url = base_url
|
|
38
35
|
self.anon_key = anon_key
|
|
39
|
-
self.tls_insecure = tls_insecure
|
|
40
|
-
self.ca_cert_file = ca_cert_file
|
|
41
36
|
|
|
42
37
|
def _login_guidance(self) -> str:
|
|
43
38
|
return (
|
|
@@ -47,20 +42,6 @@ class SessionManager:
|
|
|
47
42
|
)
|
|
48
43
|
|
|
49
44
|
def _urlopen(self, req: request.Request):
|
|
50
|
-
if self.tls_insecure:
|
|
51
|
-
context = ssl.create_default_context()
|
|
52
|
-
context.check_hostname = False
|
|
53
|
-
context.verify_mode = ssl.CERT_NONE
|
|
54
|
-
return request.urlopen(req, context=context)
|
|
55
|
-
if self.ca_cert_file:
|
|
56
|
-
if self.ca_cert_file == BUNDLED_CA_CERT_SENTINEL:
|
|
57
|
-
bundled_ca_cert = load_bundled_ca_cert()
|
|
58
|
-
if not bundled_ca_cert:
|
|
59
|
-
raise SessionError("Bundled test CA certificate is unavailable.")
|
|
60
|
-
context = ssl.create_default_context(cadata=bundled_ca_cert)
|
|
61
|
-
else:
|
|
62
|
-
context = ssl.create_default_context(cafile=self.ca_cert_file)
|
|
63
|
-
return request.urlopen(req, context=context)
|
|
64
45
|
return request.urlopen(req)
|
|
65
46
|
|
|
66
47
|
def get_auth_state(self) -> AuthState | None:
|
|
@@ -7,7 +7,7 @@ from typing import Any
|
|
|
7
7
|
from urllib import request
|
|
8
8
|
from urllib.error import HTTPError, URLError
|
|
9
9
|
|
|
10
|
-
from .config import
|
|
10
|
+
from .config import AppConfig
|
|
11
11
|
from .session import SessionError, SessionManager
|
|
12
12
|
|
|
13
13
|
|
|
@@ -35,20 +35,6 @@ class PowerbaseTransport:
|
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
def _urlopen(self, req: request.Request):
|
|
38
|
-
if self.config.tls_insecure:
|
|
39
|
-
context = ssl.create_default_context()
|
|
40
|
-
context.check_hostname = False
|
|
41
|
-
context.verify_mode = ssl.CERT_NONE
|
|
42
|
-
return request.urlopen(req, context=context)
|
|
43
|
-
if self.config.ca_cert_file:
|
|
44
|
-
if self.config.ca_cert_file == BUNDLED_CA_CERT_SENTINEL:
|
|
45
|
-
bundled_ca_cert = load_bundled_ca_cert()
|
|
46
|
-
if not bundled_ca_cert:
|
|
47
|
-
raise ApiError("Bundled test CA certificate is unavailable.")
|
|
48
|
-
context = ssl.create_default_context(cadata=bundled_ca_cert)
|
|
49
|
-
else:
|
|
50
|
-
context = ssl.create_default_context(cafile=self.config.ca_cert_file)
|
|
51
|
-
return request.urlopen(req, context=context)
|
|
52
38
|
return request.urlopen(req)
|
|
53
39
|
|
|
54
40
|
def _build_headers(
|