silicon-cli 1.0.9__tar.gz → 1.0.12__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. {silicon_cli-1.0.9/silicon_cli.egg-info → silicon_cli-1.0.12}/PKG-INFO +121 -14
  2. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/README.md +118 -12
  3. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/pyproject.toml +1 -1
  4. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/__init__.py +1 -1
  5. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/cli.py +70 -7
  6. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/config.py +27 -4
  7. silicon_cli-1.0.12/silicon_cli/docker_runtime.py +831 -0
  8. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/glassagent.py +1 -1
  9. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/process.py +18 -2
  10. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/registry.py +67 -5
  11. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/stemcell.py +68 -10
  12. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/sync.py +163 -13
  13. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/update.py +35 -4
  14. {silicon_cli-1.0.9 → silicon_cli-1.0.12/silicon_cli.egg-info}/PKG-INFO +121 -14
  15. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli.egg-info/SOURCES.txt +3 -1
  16. silicon_cli-1.0.12/tests/test_docker_runtime.py +300 -0
  17. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/LICENSE +0 -0
  18. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/setup.cfg +0 -0
  19. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/interface_cli.py +0 -0
  20. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli/ui.py +0 -0
  21. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli.egg-info/dependency_links.txt +0 -0
  22. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli.egg-info/entry_points.txt +0 -0
  23. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli.egg-info/requires.txt +0 -0
  24. {silicon_cli-1.0.9 → silicon_cli-1.0.12}/silicon_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: silicon-cli
3
- Version: 1.0.9
3
+ Version: 1.0.12
4
4
  Summary: Silicon CLI — create, run, and manage your silicon instances.
5
5
  Author: Saket
6
6
  Project-URL: Homepage, https://github.com/saket1225/silicon-cli
@@ -13,6 +13,7 @@ Requires-Python: >=3.9
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
  Requires-Dist: certifi>=2024.2.2
16
+ Dynamic: license-file
16
17
 
17
18
  # silicon-cli
18
19
 
@@ -21,7 +22,7 @@ package is named `silicon-cli`, and the code lives here in this `silicon-cli`
21
22
  repo.
22
23
 
23
24
  The command manages silicon instances on a machine: create them from the
24
- [silicon-stemcell](https://github.com/unlikefraction/silicon-stemcell) base,
25
+ [silicon-stemcell](https://github.com/teamofsilicons/silicon-stemcell) base,
25
26
  start/stop them under an auto-restart watchdog, stream logs, and back them up to
26
27
  Glass. It reads the same `~/.silicon/registry.json`, so existing installs carry
27
28
  over unchanged.
@@ -46,6 +47,8 @@ silicon restart <target> Restart silicon(s). target = name, *, all, 1,2,4, o
46
47
  silicon agent <start|stop|status> [name] Manage the per-silicon glass agent
47
48
  silicon status [name] Show instance status
48
49
  silicon browser [name] Open a headed browser for login
50
+ silicon browser-profile setup <token> Create a Steel browser profile through Glass
51
+ silicon browser-profile finish <token> <session_id> [before_ids_csv] Finish profile setup
49
52
  silicon debug [name] Tail a running instance's logs
50
53
  silicon attach [path] Register an existing silicon directory
51
54
  silicon pull [api_token] Pull a Glass team or silicon into local folders
@@ -53,6 +56,14 @@ silicon push [name] [now|stop] Daily 23:59 GMT backups to Glass (now = one-sho
53
56
  silicon backup [name] [now|stop] Alias for silicon push
54
57
  silicon update <target> Update silicon(s) from the latest stemcell
55
58
  silicon list List all instances
59
+ silicon docker init [--root ~/silicons] [--image teamofsilicons/silicon-runtime:latest]
60
+ Install/check Docker and enable one-container-per-Silicon runtime
61
+ silicon docker doctor Check/repair Docker runtime setup
62
+ silicon docker login [claude|codex|all]
63
+ Set up shared Claude/Codex auth for Docker silicons
64
+ silicon docker compose Print generated Compose file path
65
+ silicon claude [args...] Run Claude Code with shared Docker auth
66
+ silicon codex [args...] Run Codex with shared Docker auth
56
67
  silicon script update Update this CLI itself
57
68
  silicon help Show help
58
69
  ```
@@ -63,21 +74,117 @@ silicon help Show help
63
74
  | --- | --- | --- |
64
75
  | `SILICON_HOME` | `~/.silicon` | registry + CLI state |
65
76
  | `GLASS_SERVER_URL` | `https://glass.teamofsilicons.com` | Glass sync server (pull/push) |
66
- | `SILICON_STEMCELL_REPO` | `unlikefraction/silicon-stemcell` | base for `new` |
67
- | `SILICON_GLASS_CLI_REPO` | `unlikefraction/glass` | glass backup CLI |
77
+ | `SILICON_STEMCELL_REPO` | `teamofsilicons/silicon-stemcell` | base for `new` |
78
+ | `SILICON_GLASS_CLI_REPO` | `teamofsilicons/glass` | glass backup CLI |
68
79
  | `SILICON_PYTHON` | `python3` | interpreter used to run a silicon's `main.py` |
69
80
  | `SILICON_INTERFACE_CLI_PACKAGE` | `@teamofsilicons/silicon-interface-cli` | npm package used to install the Silicon Interface CLI |
70
81
  | `SILICON_INTERFACE_CLI_TARBALL` | versioned npm tarball | fallback package URL if registry metadata is briefly unavailable |
71
82
  | `SILICON_INTERFACE_CLI_SOURCE` | *(empty)* | local package dir or `silicon-interface.mjs` path for dev installs |
72
83
  | `SILICON_INTERFACE_CLI_SKIP` | *(empty)* | set to `1` to skip interface CLI setup |
73
84
  | `SILICON_INTERFACE_DAEMON_SKIP` | *(empty)* | set to `1` to install the CLI without starting its listener daemon |
85
+ | `SILICON_RUNTIME` | *(empty)* | default pull uses Docker; set to `local` to opt out |
86
+ | `SILICON_RUNTIME_IMAGE` | `teamofsilicons/silicon-runtime:latest` | runtime image for Docker-backed silicons |
87
+ | `SILICON_DOCKER_ROOT` | `~/silicons` | Docker-backed instance root |
88
+ | `SILICON_DOCKER_COMPOSE` | `<root>/compose.yml` | generated Compose file path |
89
+ | `SILICON_DOCKER_SHARED_HOME` | `<root>/.shared-home` | VM-wide Claude/Codex auth home mounted into every container |
90
+ | `SILICON_DOCKER_SUDO` | *(empty)* | set to `1` to run Docker commands through `sudo docker` |
91
+ | `SILICON_DOCKER_AUTO_INSTALL` | *(empty)* | set to `1` to allow non-interactive Docker install attempts |
92
+
93
+ ## Docker runtime
94
+
95
+ Docker mode keeps the existing `silicon` command but runs each Silicon in its own
96
+ container. Mutable instance state lives on the host under `~/silicons/<name>` and
97
+ is bind-mounted at `/silicon` inside the container. Provider secrets are still
98
+ Glass-managed: the container stores only the Silicon's `.glass.json` key and the
99
+ stemcell fetches provider keys from Glass on boot.
100
+
101
+ Claude Code and Codex account state is shared across all Docker-backed silicons
102
+ on the VM. The shared auth home defaults to `~/silicons/.shared-home` and is
103
+ mounted into every container. During an interactive `silicon pull`, the CLI asks
104
+ whether to set up Claude Code, Codex, or both before installing the team.
105
+
106
+ On a fresh Linux server, the host only needs Python and this CLI. `silicon pull`
107
+ checks Docker Engine, Docker Compose v2, the daemon, current-user access, runtime
108
+ config, and the runtime image. When Docker is missing on Linux, it can install
109
+ Docker Engine through Docker's official `get.docker.com` installer, start the
110
+ daemon, and continue.
111
+
112
+ ```bash
113
+ pip install silicon-cli
114
+ silicon pull sct_live_...
115
+ ```
116
+
117
+ To run the same checks explicitly:
118
+
119
+ ```bash
120
+ silicon docker bootstrap --root ~/silicons --image teamofsilicons/silicon-runtime:latest
121
+ silicon docker doctor
122
+ ```
123
+
124
+ To set up or repair shared Claude/Codex login manually:
125
+
126
+ ```bash
127
+ silicon docker login # asks which accounts to set up
128
+ silicon docker login claude # Claude Code only
129
+ silicon docker login codex # Codex only
130
+ silicon docker login all # both
131
+ ```
132
+
133
+ To use those same shared accounts directly from the VM without installing Claude
134
+ or Codex on the host:
135
+
136
+ ```bash
137
+ silicon claude
138
+ silicon codex
139
+ silicon claude --version
140
+ silicon codex --version
141
+ ```
142
+
143
+ The runtime image contains the Silicon runtime dependencies: Python tooling, git,
144
+ Node, Silicon Browser, Silicon Interface CLI, Claude Code, and Codex. Each
145
+ container creates `/silicon/.venv` and installs that Silicon's `requirements.txt`
146
+ inside the mounted instance folder, so per-Silicon Python dependencies do not
147
+ pollute the host.
148
+
149
+ To force the older host-local install path:
150
+
151
+ ```bash
152
+ SILICON_RUNTIME=local silicon pull sct_live_...
153
+ ```
154
+
155
+ After that, the normal commands continue to work:
156
+
157
+ ```bash
158
+ silicon list
159
+ silicon start all
160
+ silicon stop ada # stops the Silicon process; container stays up
161
+ silicon stop --full ada # stops the container
162
+ silicon debug ada
163
+ silicon update ada
164
+ silicon backup ada now
165
+ ```
166
+
167
+ The generated Compose file is written to `~/silicons/compose.yml`. You can inspect
168
+ it with:
169
+
170
+ ```bash
171
+ silicon docker compose
172
+ ```
173
+
174
+ Build the runtime image from this repo:
175
+
176
+ ```bash
177
+ docker build -f docker/runtime/Dockerfile -t teamofsilicons/silicon-runtime:latest .
178
+ ```
74
179
 
75
180
  ## Silicon Interface CLI
76
181
 
77
- `silicon new`, `silicon install`, and `silicon pull` also set up the
78
- Silicon Interface CLI in the silicon folder when Node 22+ is available.
79
- When a Glass `.glass.json` is present, setup also starts the background listener
80
- daemon so the silicon receives live conversation frames without polling.
182
+ For Docker-backed installs, the Silicon Interface CLI is installed inside the
183
+ runtime image/container. For host-local installs, `silicon new`, `silicon install`,
184
+ and `silicon pull` set up the Silicon Interface CLI in the silicon folder when
185
+ Node 22+ is available. When a Glass `.glass.json` is present, setup also starts
186
+ the background listener daemon so the silicon receives live conversation frames
187
+ without polling.
81
188
 
82
189
  `silicon pull` is token-native. Generate a team setup token from Glass >
83
190
  Silicons > Team setup token, then run:
@@ -92,8 +199,8 @@ silicon pull sct_live_...
92
199
 
93
200
  The command validates the token with Glass, mints one local silicon API key per
94
201
  team silicon, creates one folder per silicon, hydrates the stemcell, writes
95
- `.glass.json`, `.env`, and `env.py`, registers each instance, starts each
96
- Silicon Interface daemon, and starts each silicon process.
202
+ `.glass.json`, `.env`, and `env.py`, registers each instance, regenerates Compose
203
+ for Docker-backed installs, and starts each silicon process.
97
204
 
98
205
  During team pull, setup asks for the default brain/fallback settings once. You
99
206
  can apply those settings to every silicon, or select specific silicons that
@@ -102,9 +209,9 @@ Older per-silicon `scs_live_...` tokens still pull just that one silicon.
102
209
 
103
210
  Provider API keys for voice, browser profiles, billing, and architecture
104
211
  generation are configured on the Glass backend. After the silicon API token is
105
- validated, `silicon pull` reads the team's provider-key metadata from Glass,
106
- shows each provider as saved/missing without returning plaintext secrets, and
107
- asks whether the pulled silicon(s) should use the Glass-managed keys. A
212
+ validated, `silicon pull` reads the team's provider-key metadata from Glass and
213
+ automatically marks the pulled silicon(s) to use Glass-managed keys when every
214
+ provider is available. Plaintext secrets are never returned to the CLI. A
108
215
  Glass-pulled silicon only stores its Glass API token plus a local marker that
109
216
  provider keys come from Glass.
110
217
 
@@ -5,7 +5,7 @@ package is named `silicon-cli`, and the code lives here in this `silicon-cli`
5
5
  repo.
6
6
 
7
7
  The command manages silicon instances on a machine: create them from the
8
- [silicon-stemcell](https://github.com/unlikefraction/silicon-stemcell) base,
8
+ [silicon-stemcell](https://github.com/teamofsilicons/silicon-stemcell) base,
9
9
  start/stop them under an auto-restart watchdog, stream logs, and back them up to
10
10
  Glass. It reads the same `~/.silicon/registry.json`, so existing installs carry
11
11
  over unchanged.
@@ -30,6 +30,8 @@ silicon restart <target> Restart silicon(s). target = name, *, all, 1,2,4, o
30
30
  silicon agent <start|stop|status> [name] Manage the per-silicon glass agent
31
31
  silicon status [name] Show instance status
32
32
  silicon browser [name] Open a headed browser for login
33
+ silicon browser-profile setup <token> Create a Steel browser profile through Glass
34
+ silicon browser-profile finish <token> <session_id> [before_ids_csv] Finish profile setup
33
35
  silicon debug [name] Tail a running instance's logs
34
36
  silicon attach [path] Register an existing silicon directory
35
37
  silicon pull [api_token] Pull a Glass team or silicon into local folders
@@ -37,6 +39,14 @@ silicon push [name] [now|stop] Daily 23:59 GMT backups to Glass (now = one-sho
37
39
  silicon backup [name] [now|stop] Alias for silicon push
38
40
  silicon update <target> Update silicon(s) from the latest stemcell
39
41
  silicon list List all instances
42
+ silicon docker init [--root ~/silicons] [--image teamofsilicons/silicon-runtime:latest]
43
+ Install/check Docker and enable one-container-per-Silicon runtime
44
+ silicon docker doctor Check/repair Docker runtime setup
45
+ silicon docker login [claude|codex|all]
46
+ Set up shared Claude/Codex auth for Docker silicons
47
+ silicon docker compose Print generated Compose file path
48
+ silicon claude [args...] Run Claude Code with shared Docker auth
49
+ silicon codex [args...] Run Codex with shared Docker auth
40
50
  silicon script update Update this CLI itself
41
51
  silicon help Show help
42
52
  ```
@@ -47,21 +57,117 @@ silicon help Show help
47
57
  | --- | --- | --- |
48
58
  | `SILICON_HOME` | `~/.silicon` | registry + CLI state |
49
59
  | `GLASS_SERVER_URL` | `https://glass.teamofsilicons.com` | Glass sync server (pull/push) |
50
- | `SILICON_STEMCELL_REPO` | `unlikefraction/silicon-stemcell` | base for `new` |
51
- | `SILICON_GLASS_CLI_REPO` | `unlikefraction/glass` | glass backup CLI |
60
+ | `SILICON_STEMCELL_REPO` | `teamofsilicons/silicon-stemcell` | base for `new` |
61
+ | `SILICON_GLASS_CLI_REPO` | `teamofsilicons/glass` | glass backup CLI |
52
62
  | `SILICON_PYTHON` | `python3` | interpreter used to run a silicon's `main.py` |
53
63
  | `SILICON_INTERFACE_CLI_PACKAGE` | `@teamofsilicons/silicon-interface-cli` | npm package used to install the Silicon Interface CLI |
54
64
  | `SILICON_INTERFACE_CLI_TARBALL` | versioned npm tarball | fallback package URL if registry metadata is briefly unavailable |
55
65
  | `SILICON_INTERFACE_CLI_SOURCE` | *(empty)* | local package dir or `silicon-interface.mjs` path for dev installs |
56
66
  | `SILICON_INTERFACE_CLI_SKIP` | *(empty)* | set to `1` to skip interface CLI setup |
57
67
  | `SILICON_INTERFACE_DAEMON_SKIP` | *(empty)* | set to `1` to install the CLI without starting its listener daemon |
68
+ | `SILICON_RUNTIME` | *(empty)* | default pull uses Docker; set to `local` to opt out |
69
+ | `SILICON_RUNTIME_IMAGE` | `teamofsilicons/silicon-runtime:latest` | runtime image for Docker-backed silicons |
70
+ | `SILICON_DOCKER_ROOT` | `~/silicons` | Docker-backed instance root |
71
+ | `SILICON_DOCKER_COMPOSE` | `<root>/compose.yml` | generated Compose file path |
72
+ | `SILICON_DOCKER_SHARED_HOME` | `<root>/.shared-home` | VM-wide Claude/Codex auth home mounted into every container |
73
+ | `SILICON_DOCKER_SUDO` | *(empty)* | set to `1` to run Docker commands through `sudo docker` |
74
+ | `SILICON_DOCKER_AUTO_INSTALL` | *(empty)* | set to `1` to allow non-interactive Docker install attempts |
75
+
76
+ ## Docker runtime
77
+
78
+ Docker mode keeps the existing `silicon` command but runs each Silicon in its own
79
+ container. Mutable instance state lives on the host under `~/silicons/<name>` and
80
+ is bind-mounted at `/silicon` inside the container. Provider secrets are still
81
+ Glass-managed: the container stores only the Silicon's `.glass.json` key and the
82
+ stemcell fetches provider keys from Glass on boot.
83
+
84
+ Claude Code and Codex account state is shared across all Docker-backed silicons
85
+ on the VM. The shared auth home defaults to `~/silicons/.shared-home` and is
86
+ mounted into every container. During an interactive `silicon pull`, the CLI asks
87
+ whether to set up Claude Code, Codex, or both before installing the team.
88
+
89
+ On a fresh Linux server, the host only needs Python and this CLI. `silicon pull`
90
+ checks Docker Engine, Docker Compose v2, the daemon, current-user access, runtime
91
+ config, and the runtime image. When Docker is missing on Linux, it can install
92
+ Docker Engine through Docker's official `get.docker.com` installer, start the
93
+ daemon, and continue.
94
+
95
+ ```bash
96
+ pip install silicon-cli
97
+ silicon pull sct_live_...
98
+ ```
99
+
100
+ To run the same checks explicitly:
101
+
102
+ ```bash
103
+ silicon docker bootstrap --root ~/silicons --image teamofsilicons/silicon-runtime:latest
104
+ silicon docker doctor
105
+ ```
106
+
107
+ To set up or repair shared Claude/Codex login manually:
108
+
109
+ ```bash
110
+ silicon docker login # asks which accounts to set up
111
+ silicon docker login claude # Claude Code only
112
+ silicon docker login codex # Codex only
113
+ silicon docker login all # both
114
+ ```
115
+
116
+ To use those same shared accounts directly from the VM without installing Claude
117
+ or Codex on the host:
118
+
119
+ ```bash
120
+ silicon claude
121
+ silicon codex
122
+ silicon claude --version
123
+ silicon codex --version
124
+ ```
125
+
126
+ The runtime image contains the Silicon runtime dependencies: Python tooling, git,
127
+ Node, Silicon Browser, Silicon Interface CLI, Claude Code, and Codex. Each
128
+ container creates `/silicon/.venv` and installs that Silicon's `requirements.txt`
129
+ inside the mounted instance folder, so per-Silicon Python dependencies do not
130
+ pollute the host.
131
+
132
+ To force the older host-local install path:
133
+
134
+ ```bash
135
+ SILICON_RUNTIME=local silicon pull sct_live_...
136
+ ```
137
+
138
+ After that, the normal commands continue to work:
139
+
140
+ ```bash
141
+ silicon list
142
+ silicon start all
143
+ silicon stop ada # stops the Silicon process; container stays up
144
+ silicon stop --full ada # stops the container
145
+ silicon debug ada
146
+ silicon update ada
147
+ silicon backup ada now
148
+ ```
149
+
150
+ The generated Compose file is written to `~/silicons/compose.yml`. You can inspect
151
+ it with:
152
+
153
+ ```bash
154
+ silicon docker compose
155
+ ```
156
+
157
+ Build the runtime image from this repo:
158
+
159
+ ```bash
160
+ docker build -f docker/runtime/Dockerfile -t teamofsilicons/silicon-runtime:latest .
161
+ ```
58
162
 
59
163
  ## Silicon Interface CLI
60
164
 
61
- `silicon new`, `silicon install`, and `silicon pull` also set up the
62
- Silicon Interface CLI in the silicon folder when Node 22+ is available.
63
- When a Glass `.glass.json` is present, setup also starts the background listener
64
- daemon so the silicon receives live conversation frames without polling.
165
+ For Docker-backed installs, the Silicon Interface CLI is installed inside the
166
+ runtime image/container. For host-local installs, `silicon new`, `silicon install`,
167
+ and `silicon pull` set up the Silicon Interface CLI in the silicon folder when
168
+ Node 22+ is available. When a Glass `.glass.json` is present, setup also starts
169
+ the background listener daemon so the silicon receives live conversation frames
170
+ without polling.
65
171
 
66
172
  `silicon pull` is token-native. Generate a team setup token from Glass >
67
173
  Silicons > Team setup token, then run:
@@ -76,8 +182,8 @@ silicon pull sct_live_...
76
182
 
77
183
  The command validates the token with Glass, mints one local silicon API key per
78
184
  team silicon, creates one folder per silicon, hydrates the stemcell, writes
79
- `.glass.json`, `.env`, and `env.py`, registers each instance, starts each
80
- Silicon Interface daemon, and starts each silicon process.
185
+ `.glass.json`, `.env`, and `env.py`, registers each instance, regenerates Compose
186
+ for Docker-backed installs, and starts each silicon process.
81
187
 
82
188
  During team pull, setup asks for the default brain/fallback settings once. You
83
189
  can apply those settings to every silicon, or select specific silicons that
@@ -86,9 +192,9 @@ Older per-silicon `scs_live_...` tokens still pull just that one silicon.
86
192
 
87
193
  Provider API keys for voice, browser profiles, billing, and architecture
88
194
  generation are configured on the Glass backend. After the silicon API token is
89
- validated, `silicon pull` reads the team's provider-key metadata from Glass,
90
- shows each provider as saved/missing without returning plaintext secrets, and
91
- asks whether the pulled silicon(s) should use the Glass-managed keys. A
195
+ validated, `silicon pull` reads the team's provider-key metadata from Glass and
196
+ automatically marks the pulled silicon(s) to use Glass-managed keys when every
197
+ provider is available. Plaintext secrets are never returned to the CLI. A
92
198
  Glass-pulled silicon only stores its Glass API token plus a local marker that
93
199
  provider keys come from Glass.
94
200
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "silicon-cli"
7
- version = "1.0.9"
7
+ version = "1.0.12"
8
8
  description = "Silicon CLI — create, run, and manage your silicon instances."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,2 +1,2 @@
1
1
  """Silicon CLI — manage silicon instances. Python port of the original bash manager."""
2
- __version__ = "1.0.9"
2
+ __version__ = "1.0.10"
@@ -5,12 +5,13 @@ import subprocess
5
5
  import sys
6
6
  from pathlib import Path
7
7
 
8
- from . import glassagent, process, registry, stemcell, sync, ui, update
8
+ from . import docker_runtime, glassagent, process, registry, stemcell, sync, ui, update
9
9
  from .config import python_run_cmd
10
10
 
11
11
  COMMANDS = ["start", "stop", "restart", "status", "browser", "debug", "attach",
12
12
  "pull", "push", "backup", "update", "update-check", "check-update",
13
- "list", "install", "new", "help", "script", "agent"]
13
+ "browser-profile", "list", "install", "new", "help", "script", "agent",
14
+ "docker", "claude", "codex"]
14
15
 
15
16
 
16
17
  # ----------------------------------------------------------------- commands
@@ -24,17 +25,21 @@ def cmd_list() -> None:
24
25
  print(f" {ui.DIM}{'#':<4}{'NAME':<22}{'STATUS':<10}PATH{ui.RESET}")
25
26
  print(f" {ui.DIM}{'---':<4}{'----':<22}{'------':<10}----{ui.RESET}")
26
27
  for i in rows:
27
- if process.is_running(i.pid_file):
28
+ if process.install_is_running(i):
28
29
  pid = process.get_pid(i.pid_file)
29
30
  status = f"{ui.GREEN}● running{ui.RESET}"
30
- extra = f" {ui.DIM}(PID {pid}){ui.RESET}"
31
+ extra = f" {ui.DIM}(Docker){ui.RESET}" if i.is_docker else f" {ui.DIM}(PID {pid}){ui.RESET}"
31
32
  else:
32
- status, extra = f"{ui.DIM}○ stopped{ui.RESET}", ""
33
+ status = f"{ui.DIM}○ stopped{ui.RESET}"
34
+ extra = f" {ui.DIM}(Docker){ui.RESET}" if i.is_docker else ""
33
35
  print(f" {i.index + 1:<4}{i.name:<22}{status}{extra} {ui.DIM}{i.path}{ui.RESET}")
34
36
  print()
35
37
 
36
38
 
37
39
  def _print_status(inst) -> None:
40
+ if inst.is_docker:
41
+ docker_runtime.print_status(inst)
42
+ return
38
43
  if process.is_running(inst.pid_file):
39
44
  pid = process.get_pid(inst.pid_file)
40
45
  print(f"\n{ui.BOLD}{inst.name}{ui.RESET} {ui.GREEN}● running{ui.RESET} (PID {pid})")
@@ -60,13 +65,19 @@ def cmd_status(target: str | None) -> None:
60
65
 
61
66
  def cmd_browser(target: str | None) -> None:
62
67
  inst = registry.resolve_one(target)
68
+ if inst.is_docker:
69
+ docker_runtime.run_silicon(inst, ["browser", inst.name])
70
+ return
63
71
  ui.info(f"Opening browser for '{inst.name}'...")
64
- subprocess.run([python_run_cmd(), "main.py", "browser"], cwd=inst.path)
72
+ subprocess.run([python_run_cmd(inst.path), "main.py", "browser"], cwd=inst.path)
65
73
 
66
74
 
67
75
  def cmd_debug(target: str | None) -> None:
68
76
  inst = registry.resolve_one(target)
69
- if not process.is_running(inst.pid_file):
77
+ if inst.is_docker:
78
+ docker_runtime.debug(inst)
79
+ return
80
+ if not process.install_is_running(inst):
70
81
  ui.error(f"'{inst.name}' is not running. Start it first with: silicon start {inst.name}")
71
82
  sys.exit(1)
72
83
  log_file = Path(inst.path) / ".silicon.log"
@@ -121,6 +132,11 @@ def cmd_agent(subcmd: str | None, target: str | None) -> None:
121
132
  ui.error("Usage: silicon agent <start|stop|status> [name]")
122
133
  sys.exit(1)
123
134
  inst = registry.resolve_one(target)
135
+ if inst.is_docker:
136
+ code = docker_runtime.run_silicon(inst, ["agent", subcmd, inst.name])
137
+ if code:
138
+ sys.exit(code)
139
+ return
124
140
  if subcmd == "start":
125
141
  glassagent.start(inst.path)
126
142
  elif subcmd == "stop":
@@ -137,6 +153,28 @@ def cmd_agent(subcmd: str | None, target: str | None) -> None:
137
153
 
138
154
 
139
155
  def cmd_new(target: str | None) -> None:
156
+ if docker_runtime.enabled():
157
+ docker_runtime.ensure_ready(auto_init=False, install=True, pull_image=True, quiet=True)
158
+ docker_target = docker_runtime.target_path(target)
159
+ stemcell.hydrate(
160
+ str(docker_target),
161
+ install_deps=False,
162
+ setup_interface=False,
163
+ register_install=False,
164
+ )
165
+ name = docker_target.name
166
+ sj = docker_target / "silicon.json"
167
+ if sj.exists():
168
+ import json
169
+
170
+ try:
171
+ data = json.loads(sj.read_text())
172
+ name = (data.get("address") or data.get("name") or name).strip() or name
173
+ except Exception:
174
+ pass
175
+ docker_runtime.register_instance(name, docker_target)
176
+ ui.info(f"Run 'silicon start {name}' when you're ready.")
177
+ return
140
178
  if target:
141
179
  stemcell.hydrate(target)
142
180
  return
@@ -163,6 +201,10 @@ def cmd_help() -> None:
163
201
  silicon agent <start|stop|status> [name] Manage glass agent
164
202
  silicon status [name] Show instance status
165
203
  silicon browser [name] Open headed browser for login
204
+ silicon browser-profile setup <token>
205
+ Create a Steel browser profile through Glass
206
+ silicon browser-profile finish <token> <session_id> [before_ids_csv]
207
+ Finish a browser profile setup session
166
208
  silicon debug [name] Attach to running instance (live logs)
167
209
  silicon attach [path] Register an existing silicon instance
168
210
  silicon pull [api_token] Pull a Glass team or silicon into local folders
@@ -174,6 +216,11 @@ def cmd_help() -> None:
174
216
  silicon update check [name] Trigger this silicon's system update check now
175
217
  silicon update-check [name] Trigger this silicon's system update check now
176
218
  silicon list List all instances
219
+ silicon docker init Install/check Docker and enable Docker-backed installs
220
+ silicon docker doctor Repair/check Docker runtime setup
221
+ silicon docker login Set up shared Claude/Codex auth for Docker silicons
222
+ silicon claude [args...] Run Claude Code with shared Docker auth
223
+ silicon codex [args...] Run Codex with shared Docker auth
177
224
  silicon script update Update the silicon CLI itself
178
225
  silicon install Install a new instance
179
226
  silicon help Show this help
@@ -220,8 +267,24 @@ def main(argv: list[str] | None = None) -> None:
220
267
  cmd_status(a1)
221
268
  elif cmd == "browser":
222
269
  cmd_browser(a1)
270
+ elif cmd == "browser-profile":
271
+ if a1 == "setup":
272
+ sync.browser_profile_setup(a2)
273
+ elif a1 == "finish":
274
+ sync.browser_profile_finish(
275
+ a2,
276
+ argv[3] if len(argv) > 3 else None,
277
+ argv[4] if len(argv) > 4 else None,
278
+ )
279
+ else:
280
+ ui.error("Usage: silicon browser-profile <setup|finish> ...")
281
+ sys.exit(1)
223
282
  elif cmd == "debug":
224
283
  cmd_debug(a1)
284
+ elif cmd == "docker":
285
+ docker_runtime.cmd_docker(argv[1:])
286
+ elif cmd in ("claude", "codex"):
287
+ docker_runtime.run_shared_tool(cmd, argv[1:])
225
288
  elif cmd == "attach":
226
289
  cmd_attach(a1)
227
290
  elif cmd == "pull":
@@ -15,12 +15,12 @@ CLI_SOURCE_FILE = REGISTRY_DIR / "cli-source" # where `silicon script update` r
15
15
  GLASS_SERVER_URL = os.environ.get("GLASS_SERVER_URL", "https://glass.teamofsilicons.com").rstrip("/")
16
16
 
17
17
  # Stemcell — the base every new silicon is hydrated from.
18
- STEMCELL_REPO = os.environ.get("SILICON_STEMCELL_REPO", "unlikefraction/silicon-stemcell")
18
+ STEMCELL_REPO = os.environ.get("SILICON_STEMCELL_REPO", "teamofsilicons/silicon-stemcell")
19
19
  STEMCELL_GIT_URL = f"https://github.com/{STEMCELL_REPO}.git"
20
20
  STEMCELL_ZIP_URL = f"https://github.com/{STEMCELL_REPO}/archive/refs/heads/main.zip"
21
21
 
22
22
  # Glass CLI (used by pull/push for backups).
23
- GLASS_CLI_REPO = os.environ.get("SILICON_GLASS_CLI_REPO", "unlikefraction/glass")
23
+ GLASS_CLI_REPO = os.environ.get("SILICON_GLASS_CLI_REPO", "teamofsilicons/glass")
24
24
 
25
25
  # Silicon Interface CLI. During local development, silicon-cli will auto-detect
26
26
  # a sibling silicon-interface checkout; in production this package spec is used.
@@ -41,6 +41,29 @@ SILICON_INTERFACE_DAEMON_SKIP = os.environ.get("SILICON_INTERFACE_DAEMON_SKIP",
41
41
  }
42
42
 
43
43
 
44
- def python_run_cmd() -> str:
45
- """The interpreter used to RUN a silicon's main.py (not this CLI's venv)."""
44
+ def venv_python(path: str | os.PathLike) -> str | None:
45
+ """The silicon's own .venv interpreter, if one exists."""
46
+ sub = "Scripts/python.exe" if os.name == "nt" else "bin/python"
47
+ cand = Path(path) / ".venv" / sub
48
+ return str(cand) if cand.exists() else None
49
+
50
+
51
+ def base_python_cmd() -> str:
52
+ """The interpreter used to CREATE a silicon's venv (not this CLI's venv)."""
46
53
  return os.environ.get("SILICON_PYTHON") or shutil.which("python3") or shutil.which("python") or "python3"
54
+
55
+
56
+ def python_run_cmd(path: str | os.PathLike | None = None) -> str:
57
+ """The interpreter used to RUN a silicon's code (not this CLI's venv).
58
+
59
+ SILICON_PYTHON always wins; otherwise prefer the silicon's own .venv —
60
+ system interpreters are often externally managed (PEP 668) and never
61
+ received the silicon's dependencies.
62
+ """
63
+ if os.environ.get("SILICON_PYTHON"):
64
+ return os.environ["SILICON_PYTHON"]
65
+ if path:
66
+ venv = venv_python(path)
67
+ if venv:
68
+ return venv
69
+ return shutil.which("python3") or shutil.which("python") or "python3"