space-architect 2.0.1 → 2.0.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +26 -4
- data/lib/space_core/cli/run.rb +16 -2
- data/lib/space_core/oci_packer.rb +4 -0
- data/lib/space_core/oci_runner.rb +22 -6
- data/lib/space_core/space.rb +6 -0
- data/lib/space_core/templates/oci/dockerfile.erb +15 -12
- data/lib/space_core/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7251e017d780c7fc4accd3d4ee367d2b5063bb4d0c1692c0c69bc81a228c9fa7
|
|
4
|
+
data.tar.gz: ad25da0cf6025716953685d7483e86898368170967a29465625335337f8427a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: baf43e94bcd35987bb133f3958709bfc4083851732ed2d47ef72b25b92e042cfb72c2594f1067189e9581c554140cd2d6145dc7d607f9137931250522ba445dc
|
|
7
|
+
data.tar.gz: 0ecd90d05ea7dc4c3832fea90f7af76650b1c8279ced9e7e48242b825aaeef8e7291d6dc9b4dacd6bfac00ad647ecca887a2f27abb3823181287885ccede7210
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.2] - 2026-07-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Declarative runtime env forwarding for `space run`.** A space can declare
|
|
13
|
+
`run.env:` in `space.yaml` — a list of host env vars forwarded into the
|
|
14
|
+
container as bare `-e VAR` passthrough (value never lands in argv/`ps`), on top
|
|
15
|
+
of the always-on substrate auth vars. `space run --env VAR` (repeatable) adds
|
|
16
|
+
more ad hoc. A requested-but-unset var warns on stderr instead of failing
|
|
17
|
+
opaquely in-guest. This lets credentialed payloads (e.g. a Fireworks/OpenAI key)
|
|
18
|
+
run via a plain `space run <cmd>` with no wall of flags, without baking secrets.
|
|
19
|
+
|
|
8
20
|
## [2.0.1] - 2026-07-01
|
|
9
21
|
|
|
10
22
|
Fast-follow completing the `pack.persist:` story from 2.0.0.
|
|
@@ -316,6 +328,7 @@ the **BREAKING** items under Changed. The repository is now a monorepo; the
|
|
|
316
328
|
lanes, worktrees, and headless `claude -p` dispatch, with variant sets and the
|
|
317
329
|
claude-code and opencode harnesses.
|
|
318
330
|
|
|
331
|
+
[2.0.2]: https://github.com/jetpks/space-architect/compare/v2.0.1...v2.0.2
|
|
319
332
|
[2.0.1]: https://github.com/jetpks/space-architect/compare/v2.0.0...v2.0.1
|
|
320
333
|
[2.0.0]: https://github.com/jetpks/space-architect/compare/v1.3.0...v2.0.0
|
|
321
334
|
[1.3.0]: https://github.com/jetpks/space-architect/compare/v1.2.0...v1.3.0
|
data/README.md
CHANGED
|
@@ -158,6 +158,19 @@ variables that are actually set — `ANTHROPIC_API_KEY`, `CLAUDE_CODE_OAUTH_TOKE
|
|
|
158
158
|
`ANTHROPIC_BASE_URL` — with `-e` at run time, so credentials live in your shell,
|
|
159
159
|
never in the image. 🗝️
|
|
160
160
|
|
|
161
|
+
**Forwarding payload credentials.** Beyond the always-on auth trio, a space can declare
|
|
162
|
+
`run.env:` in `space.yaml` — a list of host env var names forwarded into the container at
|
|
163
|
+
run time. You can also pass `space run --env VAR` (repeatable) for ad hoc additions. All
|
|
164
|
+
forwarding is bare `-e VAR` passthrough: values never appear in argv, `ps`, or the image.
|
|
165
|
+
A requested-but-unset var warns on stderr instead of silently failing inside the guest.
|
|
166
|
+
|
|
167
|
+
```yaml
|
|
168
|
+
run:
|
|
169
|
+
env: # run-time: host var names, forwarded as bare -e VAR
|
|
170
|
+
- FIREWORKS_API_KEY
|
|
171
|
+
- OPENAI_API_KEY
|
|
172
|
+
```
|
|
173
|
+
|
|
161
174
|
**Declaring provisioning & persistence** — two optional keys in `space.yaml`:
|
|
162
175
|
|
|
163
176
|
```yaml
|
|
@@ -169,10 +182,19 @@ pack:
|
|
|
169
182
|
- /root/.local/state
|
|
170
183
|
```
|
|
171
184
|
|
|
172
|
-
`provision` scripts must live under the space root and
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
185
|
+
`provision` scripts must live under the space root and be executable. Each script is
|
|
186
|
+
copied into the image individually (`COPY <script> /space/<script>`) and then invoked
|
|
187
|
+
(`RUN /space/<script>`) **before** the full space tree lands and **before** the gem is
|
|
188
|
+
installed. This ordering is the cache-hygiene guarantee: editing any other space file
|
|
189
|
+
leaves the provision and gem-install layers cached, so a rebuild completes in seconds
|
|
190
|
+
instead of minutes. Scripts must therefore be self-contained — they run with only the
|
|
191
|
+
base system layers and any earlier provision scripts' outputs; they cannot read other
|
|
192
|
+
space files or call `architect`/`space`. Scripts must have the executable bit set; COPY
|
|
193
|
+
preserves the mode from the build context.
|
|
194
|
+
|
|
195
|
+
`persist` paths must be absolute; `space run` bind-mounts each one from
|
|
196
|
+
`<space>/.state<path>` on the host (created on first run) so a container's mutable state
|
|
197
|
+
survives across runs. Both are validated at pack time.
|
|
176
198
|
|
|
177
199
|
Like everything else, these are reachable as `architect space pack|build|run`
|
|
178
200
|
from inside a project.
|
data/lib/space_core/cli/run.rb
CHANGED
|
@@ -9,21 +9,35 @@ class Run < BaseCommand
|
|
|
9
9
|
|
|
10
10
|
argument :command, type: :array, required: false, desc: "Command to run in the container (default: login shell)"
|
|
11
11
|
option :tty, type: :boolean, default: nil, desc: "Force interactive TTY (default: auto-detect)"
|
|
12
|
+
option :env, type: :array, desc: "Host env var to forward into the container (repeatable; adds to run.env)"
|
|
12
13
|
|
|
13
|
-
def call(command: [], tty: nil, **opts)
|
|
14
|
+
def call(command: [], tty: nil, env: [], **opts)
|
|
14
15
|
setup_terminal(**opts.slice(:color, :colors))
|
|
15
16
|
handle_errors do
|
|
16
17
|
result = store.current.bind do |space|
|
|
17
|
-
runner = Space::Core::OciRunner.new(
|
|
18
|
+
runner = Space::Core::OciRunner.new(
|
|
19
|
+
space: space, interactive: tty.nil? ? CLI.tty?(out) : tty, env_vars: env
|
|
20
|
+
)
|
|
18
21
|
runner.command(command).fmap { |argv| { argv: argv, runner: runner } }
|
|
19
22
|
end
|
|
20
23
|
render(result) do |r|
|
|
21
24
|
r[:runner].host_dirs.each { |d| FileUtils.mkdir_p(d) }
|
|
25
|
+
warn_missing_env(r[:runner].missing_env)
|
|
22
26
|
terminal.say "Running: #{r[:argv].join(' ')}"
|
|
23
27
|
out.flush # Kernel.exec replaces the process without flushing buffered IO
|
|
24
28
|
Kernel.exec(*r[:argv])
|
|
25
29
|
end
|
|
26
30
|
end
|
|
27
31
|
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# A requested var absent from the host env is almost always a forgotten export;
|
|
36
|
+
# forwarding silently omits it and the payload fails opaquely in-guest. Warn, don't fail.
|
|
37
|
+
def warn_missing_env(missing)
|
|
38
|
+
return if missing.empty?
|
|
39
|
+
|
|
40
|
+
terminal.error("Warning: requested env not set on host, not forwarded: #{missing.join(', ')}")
|
|
41
|
+
end
|
|
28
42
|
end
|
|
29
43
|
end
|
|
@@ -84,6 +84,10 @@ module Space::Core
|
|
|
84
84
|
[entrypoint_content].pack("m0")
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
def local_space_architect?
|
|
88
|
+
space.path.join("repos/space-architect").directory?
|
|
89
|
+
end
|
|
90
|
+
|
|
87
91
|
def render_template(name)
|
|
88
92
|
template_path = TEMPLATE_DIR.join(name)
|
|
89
93
|
ERB.new(template_path.read, trim_mode: "-").result(binding)
|
|
@@ -7,12 +7,15 @@ module Space::Core
|
|
|
7
7
|
class OciRunner
|
|
8
8
|
include Dry::Monads[:result]
|
|
9
9
|
|
|
10
|
+
# Always-forwarded substrate auth (the in-image architect/claude reach for these).
|
|
11
|
+
# Payload-specific vars are declared per-space via `run.env:` or the --env flag.
|
|
10
12
|
AUTH_ENV = %w[ANTHROPIC_API_KEY CLAUDE_CODE_OAUTH_TOKEN ANTHROPIC_BASE_URL].freeze
|
|
11
13
|
|
|
12
|
-
def initialize(space:, env: ENV.to_h, interactive: true)
|
|
14
|
+
def initialize(space:, env: ENV.to_h, interactive: true, env_vars: [])
|
|
13
15
|
@space = space
|
|
14
16
|
@env = env
|
|
15
17
|
@interactive = interactive
|
|
18
|
+
@env_vars = (space.run_env + Array(env_vars)).map(&:to_s)
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def image
|
|
@@ -36,7 +39,7 @@ module Space::Core
|
|
|
36
39
|
argv = [
|
|
37
40
|
"container", "run", "--rm",
|
|
38
41
|
*(@interactive ? ["-i", "-t"] : []),
|
|
39
|
-
*
|
|
42
|
+
*env_flags,
|
|
40
43
|
*mount_flags,
|
|
41
44
|
image,
|
|
42
45
|
*extra
|
|
@@ -44,17 +47,30 @@ module Space::Core
|
|
|
44
47
|
Success(argv)
|
|
45
48
|
end
|
|
46
49
|
|
|
50
|
+
# Explicitly-requested vars (run.env: + --env) that are unset/empty in the host
|
|
51
|
+
# env, so the caller can warn — silence here would let a missing key fail opaquely
|
|
52
|
+
# inside the guest. AUTH_ENV is opportunistic and intentionally excluded.
|
|
53
|
+
def missing_env
|
|
54
|
+
@env_vars.uniq.reject { |var| present?(var) }
|
|
55
|
+
end
|
|
56
|
+
|
|
47
57
|
private
|
|
48
58
|
|
|
49
59
|
attr_reader :space, :env, :interactive
|
|
50
60
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
# Bare `-e VAR` passthrough (no =value on the command line, so the secret never
|
|
62
|
+
# lands in argv/ps): substrate auth first, then the space's declared payload vars.
|
|
63
|
+
def env_flags
|
|
64
|
+
(AUTH_ENV + @env_vars).uniq.each_with_object([]) do |var, flags|
|
|
65
|
+
flags.push("-e", var) if present?(var)
|
|
55
66
|
end
|
|
56
67
|
end
|
|
57
68
|
|
|
69
|
+
def present?(var)
|
|
70
|
+
val = env[var]
|
|
71
|
+
val && !val.empty?
|
|
72
|
+
end
|
|
73
|
+
|
|
58
74
|
def mount_flags
|
|
59
75
|
mounts.each_with_object([]) do |(host, guest), flags|
|
|
60
76
|
flags.push("-v", "#{host}:#{guest}")
|
data/lib/space_core/space.rb
CHANGED
|
@@ -56,6 +56,12 @@ module Space::Core
|
|
|
56
56
|
Array(data.dig("pack", "persist")).map(&:to_s)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
# Host env vars to forward into the container at `space run` (bare passthrough).
|
|
60
|
+
# Declared per-space so payloads needing credentials run without a wall of flags.
|
|
61
|
+
def run_env
|
|
62
|
+
Array(data.dig("run", "env")).map(&:to_s)
|
|
63
|
+
end
|
|
64
|
+
|
|
59
65
|
def architect
|
|
60
66
|
data["project"]
|
|
61
67
|
end
|
|
@@ -30,18 +30,6 @@ RUN apt-get update -qq \
|
|
|
30
30
|
# Claude Code CLI (https://claude.ai/install.sh)
|
|
31
31
|
RUN curl -fsSL https://claude.ai/install.sh | bash
|
|
32
32
|
|
|
33
|
-
# Copy the space tree into the image (filtered by Dockerfile.dockerignore alongside this Dockerfile)
|
|
34
|
-
COPY . /space
|
|
35
|
-
|
|
36
|
-
# Install space-architect gem: prefer in-space checkout for a pinned version, fall back to RubyGems
|
|
37
|
-
RUN if [ -d /space/repos/space-architect ]; then \
|
|
38
|
-
cd /space/repos/space-architect \
|
|
39
|
-
&& rake build \
|
|
40
|
-
&& gem install --no-document pkg/*.gem; \
|
|
41
|
-
else \
|
|
42
|
-
gem install --no-document space-architect; \
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
33
|
# Write entrypoint: sets git safe.directory, execs requested command or a login shell
|
|
46
34
|
RUN echo '<%= entrypoint_b64 %>' | base64 -d > /entrypoint.sh \
|
|
47
35
|
&& chmod +x /entrypoint.sh
|
|
@@ -52,10 +40,25 @@ RUN printf 'export PATH="/usr/local/bundle/bin:/root/.local/bin:$PATH"\n' \
|
|
|
52
40
|
<% if provision_scripts.any? -%>
|
|
53
41
|
|
|
54
42
|
# Space-declared build-time provisioning (pack.provision)
|
|
43
|
+
# Each script is COPY'd individually — a space-content edit elsewhere does not bust its cache layer.
|
|
55
44
|
<% provision_scripts.each do |script| -%>
|
|
45
|
+
COPY <%= script %> /space/<%= script %>
|
|
56
46
|
RUN /space/<%= script %>
|
|
57
47
|
<% end -%>
|
|
58
48
|
<% end -%>
|
|
49
|
+
|
|
50
|
+
# Install space-architect gem
|
|
51
|
+
<% if local_space_architect? -%>
|
|
52
|
+
COPY repos/space-architect /space/repos/space-architect
|
|
53
|
+
RUN cd /space/repos/space-architect \
|
|
54
|
+
&& rake build \
|
|
55
|
+
&& gem install --no-document pkg/*.gem
|
|
56
|
+
<% else -%>
|
|
57
|
+
RUN gem install --no-document space-architect
|
|
58
|
+
<% end -%>
|
|
59
|
+
|
|
60
|
+
# Copy the space tree into the image (filtered by Dockerfile.dockerignore alongside this Dockerfile)
|
|
61
|
+
COPY . /space
|
|
59
62
|
<% if persist_paths.any? -%>
|
|
60
63
|
|
|
61
64
|
# Seed snapshot: capture baked content of persisted dirs so a first-run (empty) mount is seeded from it
|
data/lib/space_core/version.rb
CHANGED