opensandbox-cli 0.1.1__tar.gz → 1.1.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.
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/.gitignore +18 -0
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/PKG-INFO +69 -14
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/README.md +64 -9
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/pyproject.toml +8 -6
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/__init__.py +1 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/__main__.py +1 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/client.py +1 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/commands/__init__.py +1 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/commands/command.py +31 -5
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/commands/config_cmd.py +1 -1
- opensandbox_cli-1.1.0/src/opensandbox_cli/commands/credential_vault.py +306 -0
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/commands/devops.py +1 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/commands/diagnostics.py +12 -6
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/commands/egress.py +1 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/commands/file.py +55 -7
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/commands/sandbox.py +16 -2
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/commands/skills.py +1 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/config.py +1 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/main.py +3 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/output.py +1 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/skill_registry.py +29 -0
- opensandbox_cli-1.1.0/src/opensandbox_cli/skills/opensandbox-credential-vault.md +136 -0
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/skills/opensandbox-file-operations.md +4 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/skills/opensandbox-sandbox-lifecycle.md +4 -0
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/skills/opensandbox-sandbox-troubleshooting.md +8 -11
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/utils.py +1 -1
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/LICENSE +0 -0
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/py.typed +0 -0
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/skills/opensandbox-command-execution.md +0 -0
- {opensandbox_cli-0.1.1 → opensandbox_cli-1.1.0}/src/opensandbox_cli/skills/opensandbox-network-egress.md +0 -0
|
@@ -269,6 +269,19 @@ nbdist/
|
|
|
269
269
|
generated/
|
|
270
270
|
**/generated/**
|
|
271
271
|
|
|
272
|
+
# Fast Sandbox FastPath gRPC stubs are committed intentionally (deterministic protoc
|
|
273
|
+
# output; the server package needs them at install time, so they are checked
|
|
274
|
+
# in rather than regenerated).
|
|
275
|
+
!server/opensandbox_server/services/fast_sandbox/generated/
|
|
276
|
+
!server/opensandbox_server/services/fast_sandbox/generated/**
|
|
277
|
+
server/opensandbox_server/services/fast_sandbox/generated/__pycache__/
|
|
278
|
+
|
|
279
|
+
# execd eBPF kernel BTF dumps (bpf2go build-time only, never committed —
|
|
280
|
+
# see components/execd/pkg/ebpf/prog/audit.bpf.c; regenerate with:
|
|
281
|
+
# bpftool btf dump file /sys/kernel/btf/vmlinux format c > .../vmlinux.h)
|
|
282
|
+
components/execd/pkg/ebpf/prog/vmlinux.h
|
|
283
|
+
components/execd/pkg/ebpf/prog/vmlinux_*.h
|
|
284
|
+
|
|
272
285
|
# gVisor runtime binaries (downloaded dynamically)
|
|
273
286
|
kubernetes/test/kind/gvisor/runsc
|
|
274
287
|
kubernetes/test/kind/gvisor/containerd-shim-runsc-v1
|
|
@@ -276,3 +289,8 @@ bin/
|
|
|
276
289
|
obj/
|
|
277
290
|
|
|
278
291
|
.qoder/
|
|
292
|
+
/.vs
|
|
293
|
+
|
|
294
|
+
# fast-sandbox pinned source checkout (materialized by
|
|
295
|
+
# manifests/release/build-fast-sandbox.sh from manifests/third-party/fast-sandbox.commit)
|
|
296
|
+
.fast-sandbox/
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: opensandbox-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: OpenSandbox CLI - Command-line interface for managing sandboxes
|
|
5
5
|
Project-URL: Homepage, https://open-sandbox.ai
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
6
|
+
Project-URL: Repository, https://github.com/opensandbox-group/OpenSandbox
|
|
7
7
|
Project-URL: Documentation, https://open-sandbox.ai
|
|
8
|
-
Project-URL: Issues, https://github.com/
|
|
8
|
+
Project-URL: Issues, https://github.com/opensandbox-group/OpenSandbox/issues
|
|
9
9
|
Author-email: OpenSandbox Team <ninan.nn@alibaba-inc.com>
|
|
10
10
|
License: Apache License
|
|
11
11
|
Version 2.0, January 2004
|
|
@@ -224,7 +224,7 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
224
224
|
Classifier: Typing :: Typed
|
|
225
225
|
Requires-Python: >=3.10
|
|
226
226
|
Requires-Dist: click<9.0,>=8.1.0
|
|
227
|
-
Requires-Dist: opensandbox<
|
|
227
|
+
Requires-Dist: opensandbox<2.0.0,>=1.1.0
|
|
228
228
|
Requires-Dist: pyyaml<7.0,>=6.0
|
|
229
229
|
Requires-Dist: rich<14.0,>=13.0.0
|
|
230
230
|
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
|
|
@@ -238,6 +238,7 @@ Description-Content-Type: text/markdown
|
|
|
238
238
|
- run commands inside a sandbox
|
|
239
239
|
- read and modify sandbox files
|
|
240
240
|
- inspect runtime egress policy
|
|
241
|
+
- manage sandbox-local Credential Vault state
|
|
241
242
|
- collect low-level diagnostics
|
|
242
243
|
- install OpenSandbox-specific skills for coding agents
|
|
243
244
|
|
|
@@ -282,6 +283,7 @@ opensandbox-server
|
|
|
282
283
|
osb config init
|
|
283
284
|
osb config set connection.domain localhost:8080
|
|
284
285
|
osb config set connection.protocol http
|
|
286
|
+
osb config set connection.api_key <your-api-key>
|
|
285
287
|
osb config show -o json
|
|
286
288
|
```
|
|
287
289
|
|
|
@@ -379,6 +381,12 @@ osb sandbox create \
|
|
|
379
381
|
--volumes-file volumes.json
|
|
380
382
|
```
|
|
381
383
|
|
|
384
|
+
Create with Credential Vault proxy enabled:
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
osb sandbox create --image python:3.12 --network-policy-file network-policy.json --credential-proxy -o json
|
|
388
|
+
```
|
|
389
|
+
|
|
382
390
|
### List and inspect sandboxes
|
|
383
391
|
|
|
384
392
|
```bash
|
|
@@ -390,6 +398,17 @@ osb sandbox metrics <sandbox-id>
|
|
|
390
398
|
osb sandbox metrics <sandbox-id> --watch -o raw
|
|
391
399
|
```
|
|
392
400
|
|
|
401
|
+
### Pause a sandbox
|
|
402
|
+
|
|
403
|
+
Pause is asynchronous. `Pause request accepted` confirms that the server accepted
|
|
404
|
+
the request, not that the sandbox has reached the `Paused` state. Poll the sandbox
|
|
405
|
+
until the transition finishes:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
osb sandbox pause <sandbox-id>
|
|
409
|
+
osb sandbox get <sandbox-id> -o json
|
|
410
|
+
```
|
|
411
|
+
|
|
393
412
|
### Expose a service
|
|
394
413
|
|
|
395
414
|
```bash
|
|
@@ -412,6 +431,17 @@ osb command status <sandbox-id> <execution-id> -o json
|
|
|
412
431
|
osb command logs <sandbox-id> <execution-id> -o json
|
|
413
432
|
```
|
|
414
433
|
|
|
434
|
+
By default the payload after `--` is joined into one shell command string, so
|
|
435
|
+
pipelines, redirection, and `$VAR` expansion work as in a terminal. Add `--argv`
|
|
436
|
+
to pass the arguments to the executable as a literal argv list (no shell) when
|
|
437
|
+
values such as `$HOME`, quotes, embedded spaces, or empty strings must reach the
|
|
438
|
+
process unchanged. `--argv` needs a sandbox image whose execd accepts argv
|
|
439
|
+
requests:
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
osb command run <sandbox-id> -o raw --argv -- python3 -c "import sys; print(sys.argv[1:])" "a b" '$HOME' "x'y" ""
|
|
443
|
+
```
|
|
444
|
+
|
|
415
445
|
Persistent shell session:
|
|
416
446
|
|
|
417
447
|
```bash
|
|
@@ -433,6 +463,11 @@ osb file replace <sandbox-id> /workspace/app.py --old old --new new -o json
|
|
|
433
463
|
osb file chmod <sandbox-id> /workspace/script.sh --mode 755 -o json
|
|
434
464
|
```
|
|
435
465
|
|
|
466
|
+
Downloads replace regular files only on success; a failed or interrupted
|
|
467
|
+
download preserves any existing regular file. Existing devices and named pipes
|
|
468
|
+
receive data directly. Stdout aliases (such as `/dev/stdout`) stream only the file
|
|
469
|
+
bytes, without a success message. See the [CLI guide](../docs/cli/index.md#work-with-files).
|
|
470
|
+
|
|
436
471
|
### Manage runtime egress policy
|
|
437
472
|
|
|
438
473
|
Inspect current policy:
|
|
@@ -453,26 +488,46 @@ If you are debugging connectivity, verify behavior with an actual command:
|
|
|
453
488
|
osb command run <sandbox-id> -o raw -- curl -I https://pypi.org
|
|
454
489
|
```
|
|
455
490
|
|
|
491
|
+
### Manage Credential Vault
|
|
492
|
+
|
|
493
|
+
Credential Vault operations call the sandbox egress sidecar through the Python SDK.
|
|
494
|
+
Create the sandbox with `--credential-proxy` and an explicit network policy before
|
|
495
|
+
writing vault state.
|
|
496
|
+
|
|
497
|
+
```bash
|
|
498
|
+
osb credential-vault create <sandbox-id> --file vault.yaml -o json
|
|
499
|
+
osb credential-vault get <sandbox-id> -o json
|
|
500
|
+
osb credential-vault patch <sandbox-id> --file mutation.yaml -o json
|
|
501
|
+
osb credential-vault credential list <sandbox-id> -o json
|
|
502
|
+
osb credential-vault binding list <sandbox-id> -o json
|
|
503
|
+
osb credential-vault delete <sandbox-id> -o json
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
Use `--file -` to read a JSON/YAML payload from stdin. Do not pass plaintext
|
|
507
|
+
credential values as command-line flags; keep them in the payload stream or file.
|
|
508
|
+
|
|
456
509
|
### Collect diagnostics
|
|
457
510
|
|
|
458
511
|
Use the stable diagnostics commands for API-backed log and event descriptors.
|
|
459
512
|
|
|
460
513
|
```bash
|
|
461
|
-
osb diagnostics events <sandbox-id> --scope lifecycle -o raw
|
|
462
514
|
osb diagnostics events <sandbox-id> --scope runtime -o raw
|
|
515
|
+
osb diagnostics events <sandbox-id> --scope all -o raw
|
|
463
516
|
osb diagnostics logs <sandbox-id> --scope container -o raw
|
|
464
|
-
osb diagnostics logs <sandbox-id> --scope
|
|
517
|
+
osb diagnostics logs <sandbox-id> --scope all -o json
|
|
465
518
|
osb diagnostics events <sandbox-id> --scope runtime -o json
|
|
466
519
|
osb diagnostics logs <sandbox-id> --scope container -o yaml
|
|
467
520
|
```
|
|
468
521
|
|
|
469
|
-
`--scope` is required for stable diagnostics.
|
|
470
|
-
`container` for logs, and `
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
522
|
+
`--scope` is required for stable diagnostics. The built-in server supports
|
|
523
|
+
`container` and `all` for logs, and `runtime` and `all` for events. It returns
|
|
524
|
+
`DIAGNOSTICS_SCOPE_UNSUPPORTED` for unavailable scopes, including lifecycle events.
|
|
525
|
+
Best-effort scopes may include a `warnings` field when the backend can only
|
|
526
|
+
provide a subset. Raw output prints inline
|
|
527
|
+
diagnostic text, or the content URL when diagnostics are delivered as a
|
|
528
|
+
temporary URL. Structured CLI output follows the SDK/Python field style, for
|
|
529
|
+
example `content_url`, `content_length`, and `expires_at`. Older server builds
|
|
530
|
+
may still return `DIAGNOSTICS_NOT_IMPLEMENTED` for scoped diagnostics.
|
|
476
531
|
|
|
477
532
|
Legacy DevOps diagnostics remain experimental. Prefer `osb diagnostics logs/events`
|
|
478
533
|
for stable API-backed log and event collection.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
- run commands inside a sandbox
|
|
7
7
|
- read and modify sandbox files
|
|
8
8
|
- inspect runtime egress policy
|
|
9
|
+
- manage sandbox-local Credential Vault state
|
|
9
10
|
- collect low-level diagnostics
|
|
10
11
|
- install OpenSandbox-specific skills for coding agents
|
|
11
12
|
|
|
@@ -50,6 +51,7 @@ opensandbox-server
|
|
|
50
51
|
osb config init
|
|
51
52
|
osb config set connection.domain localhost:8080
|
|
52
53
|
osb config set connection.protocol http
|
|
54
|
+
osb config set connection.api_key <your-api-key>
|
|
53
55
|
osb config show -o json
|
|
54
56
|
```
|
|
55
57
|
|
|
@@ -147,6 +149,12 @@ osb sandbox create \
|
|
|
147
149
|
--volumes-file volumes.json
|
|
148
150
|
```
|
|
149
151
|
|
|
152
|
+
Create with Credential Vault proxy enabled:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
osb sandbox create --image python:3.12 --network-policy-file network-policy.json --credential-proxy -o json
|
|
156
|
+
```
|
|
157
|
+
|
|
150
158
|
### List and inspect sandboxes
|
|
151
159
|
|
|
152
160
|
```bash
|
|
@@ -158,6 +166,17 @@ osb sandbox metrics <sandbox-id>
|
|
|
158
166
|
osb sandbox metrics <sandbox-id> --watch -o raw
|
|
159
167
|
```
|
|
160
168
|
|
|
169
|
+
### Pause a sandbox
|
|
170
|
+
|
|
171
|
+
Pause is asynchronous. `Pause request accepted` confirms that the server accepted
|
|
172
|
+
the request, not that the sandbox has reached the `Paused` state. Poll the sandbox
|
|
173
|
+
until the transition finishes:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
osb sandbox pause <sandbox-id>
|
|
177
|
+
osb sandbox get <sandbox-id> -o json
|
|
178
|
+
```
|
|
179
|
+
|
|
161
180
|
### Expose a service
|
|
162
181
|
|
|
163
182
|
```bash
|
|
@@ -180,6 +199,17 @@ osb command status <sandbox-id> <execution-id> -o json
|
|
|
180
199
|
osb command logs <sandbox-id> <execution-id> -o json
|
|
181
200
|
```
|
|
182
201
|
|
|
202
|
+
By default the payload after `--` is joined into one shell command string, so
|
|
203
|
+
pipelines, redirection, and `$VAR` expansion work as in a terminal. Add `--argv`
|
|
204
|
+
to pass the arguments to the executable as a literal argv list (no shell) when
|
|
205
|
+
values such as `$HOME`, quotes, embedded spaces, or empty strings must reach the
|
|
206
|
+
process unchanged. `--argv` needs a sandbox image whose execd accepts argv
|
|
207
|
+
requests:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
osb command run <sandbox-id> -o raw --argv -- python3 -c "import sys; print(sys.argv[1:])" "a b" '$HOME' "x'y" ""
|
|
211
|
+
```
|
|
212
|
+
|
|
183
213
|
Persistent shell session:
|
|
184
214
|
|
|
185
215
|
```bash
|
|
@@ -201,6 +231,11 @@ osb file replace <sandbox-id> /workspace/app.py --old old --new new -o json
|
|
|
201
231
|
osb file chmod <sandbox-id> /workspace/script.sh --mode 755 -o json
|
|
202
232
|
```
|
|
203
233
|
|
|
234
|
+
Downloads replace regular files only on success; a failed or interrupted
|
|
235
|
+
download preserves any existing regular file. Existing devices and named pipes
|
|
236
|
+
receive data directly. Stdout aliases (such as `/dev/stdout`) stream only the file
|
|
237
|
+
bytes, without a success message. See the [CLI guide](../docs/cli/index.md#work-with-files).
|
|
238
|
+
|
|
204
239
|
### Manage runtime egress policy
|
|
205
240
|
|
|
206
241
|
Inspect current policy:
|
|
@@ -221,26 +256,46 @@ If you are debugging connectivity, verify behavior with an actual command:
|
|
|
221
256
|
osb command run <sandbox-id> -o raw -- curl -I https://pypi.org
|
|
222
257
|
```
|
|
223
258
|
|
|
259
|
+
### Manage Credential Vault
|
|
260
|
+
|
|
261
|
+
Credential Vault operations call the sandbox egress sidecar through the Python SDK.
|
|
262
|
+
Create the sandbox with `--credential-proxy` and an explicit network policy before
|
|
263
|
+
writing vault state.
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
osb credential-vault create <sandbox-id> --file vault.yaml -o json
|
|
267
|
+
osb credential-vault get <sandbox-id> -o json
|
|
268
|
+
osb credential-vault patch <sandbox-id> --file mutation.yaml -o json
|
|
269
|
+
osb credential-vault credential list <sandbox-id> -o json
|
|
270
|
+
osb credential-vault binding list <sandbox-id> -o json
|
|
271
|
+
osb credential-vault delete <sandbox-id> -o json
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Use `--file -` to read a JSON/YAML payload from stdin. Do not pass plaintext
|
|
275
|
+
credential values as command-line flags; keep them in the payload stream or file.
|
|
276
|
+
|
|
224
277
|
### Collect diagnostics
|
|
225
278
|
|
|
226
279
|
Use the stable diagnostics commands for API-backed log and event descriptors.
|
|
227
280
|
|
|
228
281
|
```bash
|
|
229
|
-
osb diagnostics events <sandbox-id> --scope lifecycle -o raw
|
|
230
282
|
osb diagnostics events <sandbox-id> --scope runtime -o raw
|
|
283
|
+
osb diagnostics events <sandbox-id> --scope all -o raw
|
|
231
284
|
osb diagnostics logs <sandbox-id> --scope container -o raw
|
|
232
|
-
osb diagnostics logs <sandbox-id> --scope
|
|
285
|
+
osb diagnostics logs <sandbox-id> --scope all -o json
|
|
233
286
|
osb diagnostics events <sandbox-id> --scope runtime -o json
|
|
234
287
|
osb diagnostics logs <sandbox-id> --scope container -o yaml
|
|
235
288
|
```
|
|
236
289
|
|
|
237
|
-
`--scope` is required for stable diagnostics.
|
|
238
|
-
`container` for logs, and `
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
290
|
+
`--scope` is required for stable diagnostics. The built-in server supports
|
|
291
|
+
`container` and `all` for logs, and `runtime` and `all` for events. It returns
|
|
292
|
+
`DIAGNOSTICS_SCOPE_UNSUPPORTED` for unavailable scopes, including lifecycle events.
|
|
293
|
+
Best-effort scopes may include a `warnings` field when the backend can only
|
|
294
|
+
provide a subset. Raw output prints inline
|
|
295
|
+
diagnostic text, or the content URL when diagnostics are delivered as a
|
|
296
|
+
temporary URL. Structured CLI output follows the SDK/Python field style, for
|
|
297
|
+
example `content_url`, `content_length`, and `expires_at`. Older server builds
|
|
298
|
+
may still return `DIAGNOSTICS_NOT_IMPLEMENTED` for scoped diagnostics.
|
|
244
299
|
|
|
245
300
|
Legacy DevOps diagnostics remain experimental. Prefer `osb diagnostics logs/events`
|
|
246
301
|
for stable API-backed log and event collection.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2026
|
|
1
|
+
# Copyright 2026 The OpenSandbox Authors.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -42,7 +42,9 @@ classifiers = [
|
|
|
42
42
|
"Typing :: Typed",
|
|
43
43
|
]
|
|
44
44
|
dependencies = [
|
|
45
|
-
|
|
45
|
+
# `command run --argv` passes a list to commands.run; the SDK accepts a
|
|
46
|
+
# list there from 0.1.17. Release the CLI only after that SDK is published.
|
|
47
|
+
"opensandbox>=1.1.0,<2.0.0",
|
|
46
48
|
"click>=8.1.0,<9.0",
|
|
47
49
|
"rich>=13.0.0,<14.0",
|
|
48
50
|
"pyyaml>=6.0,<7.0",
|
|
@@ -51,9 +53,9 @@ dependencies = [
|
|
|
51
53
|
|
|
52
54
|
[project.urls]
|
|
53
55
|
Homepage = "https://open-sandbox.ai"
|
|
54
|
-
Repository = "https://github.com/
|
|
56
|
+
Repository = "https://github.com/opensandbox-group/OpenSandbox"
|
|
55
57
|
Documentation = "https://open-sandbox.ai"
|
|
56
|
-
Issues = "https://github.com/
|
|
58
|
+
Issues = "https://github.com/opensandbox-group/OpenSandbox/issues"
|
|
57
59
|
|
|
58
60
|
[project.scripts]
|
|
59
61
|
opensandbox = "opensandbox_cli.main:cli"
|
|
@@ -64,8 +66,8 @@ source = "vcs"
|
|
|
64
66
|
|
|
65
67
|
[tool.hatch.version.raw-options]
|
|
66
68
|
root = ".."
|
|
67
|
-
tag_regex = "^
|
|
68
|
-
git_describe_command = 'git describe --dirty --tags --long --match "
|
|
69
|
+
tag_regex = "^release-(?P<version>\\d+\\.\\d+\\.\\d+(?:[\\.\\w\\+\\-]*)?)$"
|
|
70
|
+
git_describe_command = 'git describe --dirty --tags --long --match "release-*"'
|
|
69
71
|
fallback_version = "0.1.1"
|
|
70
72
|
|
|
71
73
|
[tool.hatch.build]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2026
|
|
1
|
+
# Copyright 2026 The OpenSandbox Authors.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -51,17 +51,28 @@ def _run_command(
|
|
|
51
51
|
workdir: str | None,
|
|
52
52
|
timeout: timedelta | None,
|
|
53
53
|
output_format: str | None,
|
|
54
|
+
argv: bool = False,
|
|
54
55
|
) -> None:
|
|
55
56
|
"""Shared implementation for ``command run``.
|
|
56
57
|
|
|
57
58
|
Mode contract:
|
|
58
59
|
- foreground (default): stream output directly, allow only ``-o raw``
|
|
59
60
|
- background (``--background``): return a tracked execution object, allow structured output
|
|
61
|
+
|
|
62
|
+
Payload contract:
|
|
63
|
+
- default: join the arguments into one shell command string (works against
|
|
64
|
+
every deployed execd)
|
|
65
|
+
- ``--argv``: send the arguments as a literal argv list, no shell; needs an
|
|
66
|
+
execd that accepts the ``argv`` request field
|
|
60
67
|
"""
|
|
61
68
|
allowed = ("table", "json", "yaml") if background else ("raw",)
|
|
62
69
|
fallback = "table" if background else "raw"
|
|
63
70
|
prepare_output(obj, output_format, allowed=allowed, fallback=fallback)
|
|
64
|
-
|
|
71
|
+
payload: str | list[str]
|
|
72
|
+
if argv:
|
|
73
|
+
payload = list(command)
|
|
74
|
+
else:
|
|
75
|
+
payload = " ".join(shlex.quote(arg) for arg in command)
|
|
65
76
|
sandbox = obj.connect_sandbox(sandbox_id)
|
|
66
77
|
|
|
67
78
|
try:
|
|
@@ -72,7 +83,7 @@ def _run_command(
|
|
|
72
83
|
)
|
|
73
84
|
|
|
74
85
|
if background:
|
|
75
|
-
execution = sandbox.commands.run(
|
|
86
|
+
execution = sandbox.commands.run(payload, opts=opts)
|
|
76
87
|
obj.output.success_panel(
|
|
77
88
|
{
|
|
78
89
|
"execution_id": execution.id,
|
|
@@ -99,7 +110,7 @@ def _run_command(
|
|
|
99
110
|
sys.stderr.flush()
|
|
100
111
|
|
|
101
112
|
handlers = ExecutionHandlersSync(on_stdout=on_stdout, on_stderr=on_stderr)
|
|
102
|
-
execution = sandbox.commands.run(
|
|
113
|
+
execution = sandbox.commands.run(payload, opts=opts, handlers=handlers)
|
|
103
114
|
|
|
104
115
|
# Ensure terminal prompt starts on a new line
|
|
105
116
|
if last_text and not last_text.endswith("\n"):
|
|
@@ -124,13 +135,25 @@ def _handle_execution_error(obj: ClientContext, execution) -> None:
|
|
|
124
135
|
@command_group.command(
|
|
125
136
|
"run",
|
|
126
137
|
help="Run a command in a sandbox. Use `--` before the sandbox command payload.",
|
|
127
|
-
epilog=
|
|
138
|
+
epilog=(
|
|
139
|
+
"Separator rule: use `--` before the sandbox command payload.\n\n"
|
|
140
|
+
"By default the payload is joined into one shell command string. Add "
|
|
141
|
+
"`--argv` to pass the arguments to the executable as-is (no shell), so "
|
|
142
|
+
"literal `$HOME`, quotes, and empty strings are preserved; this needs a "
|
|
143
|
+
"sandbox image whose execd accepts argv requests."
|
|
144
|
+
),
|
|
128
145
|
)
|
|
129
146
|
@click.argument("sandbox_id")
|
|
130
147
|
@click.argument("command", nargs=-1, required=True)
|
|
131
148
|
@click.option("-d", "--background", is_flag=True, default=False, help="Run in background.")
|
|
132
149
|
@click.option("-w", "--workdir", default=None, help="Working directory.")
|
|
133
150
|
@click.option("-t", "--timeout", type=DURATION, default=None, help="Command timeout (e.g. 30s, 5m).")
|
|
151
|
+
@click.option(
|
|
152
|
+
"--argv",
|
|
153
|
+
is_flag=True,
|
|
154
|
+
default=False,
|
|
155
|
+
help="Pass the payload as a literal argv list (no shell). Requires an argv-capable execd.",
|
|
156
|
+
)
|
|
134
157
|
@output_option("table", "json", "yaml", "raw")
|
|
135
158
|
@click.pass_obj
|
|
136
159
|
@handle_errors
|
|
@@ -141,12 +164,14 @@ def command_run(
|
|
|
141
164
|
background: bool,
|
|
142
165
|
workdir: str | None,
|
|
143
166
|
timeout: timedelta | None,
|
|
167
|
+
argv: bool,
|
|
144
168
|
output_format: str | None,
|
|
145
169
|
) -> None:
|
|
146
170
|
"""Run a command in a sandbox.
|
|
147
171
|
|
|
148
172
|
Default mode streams output directly. Add ``--background`` to return a
|
|
149
173
|
tracked execution object instead. Use ``--`` before the sandbox command payload.
|
|
174
|
+
The payload is sent as a shell command string unless ``--argv`` is given.
|
|
150
175
|
"""
|
|
151
176
|
_run_command(
|
|
152
177
|
obj,
|
|
@@ -156,6 +181,7 @@ def command_run(
|
|
|
156
181
|
workdir,
|
|
157
182
|
timeout,
|
|
158
183
|
output_format,
|
|
184
|
+
argv=argv,
|
|
159
185
|
)
|
|
160
186
|
|
|
161
187
|
|