tep 0.11.0 → 0.11.1

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 (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -16
  3. data/bin/tep +62 -6
  4. data/lib/tep/version.rb +1 -1
  5. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01e96933540bed51cfb5ef9b6edd91154c63d60bf2469eece6e1fbfff42815d5
4
- data.tar.gz: 47005866b1f57933b4724c19eb1e387b2d3f0d9e52003cc0d155795ed522977a
3
+ metadata.gz: ed267cb0d611fda2cb1339ea9751dc723c8c8b2eda6c24b10f4ac3e19f0bb530
4
+ data.tar.gz: f1e52b916e06975569e4c1fd50ab2759c167fdc9209d8f4066a24c4da98284aa
5
5
  SHA512:
6
- metadata.gz: 58cbe7986525f4eedd43a6b2e603dca7545a8b7d46d0b241a175e451bbcb9ee8d29e1de0e7b8d323f8cee75d93f4ce24a1f8344593affad0534c71f182f7f442
7
- data.tar.gz: 0f31fe5463c71bef2ee00a1507ff2b9a4c45c49023810e3e2a206e10737b6f794bf89c2b09d1eebf3c23b05321f143bc652b117cf4493e4bf259c2f14bf3861e
6
+ metadata.gz: 980964ad55476805b33d78dc1623d777ee8b0d1205a9b779201ebb8bf71d9a6a5ecd7a2e1966372be11d8583778b026385dfaac56ef2b6d22f5989da34257f39
7
+ data.tar.gz: b0cceac7ec078c154af037c8484ad269a539dfb39c9a98805c7989846d2a6a0dc4be044cb7ae9053856deb9c4281888fe8f786ba7d9c9c22dd2786208f5f58c6
data/README.md CHANGED
@@ -7,9 +7,10 @@
7
7
  A Sinatra-flavoured web framework that compiles to a native binary
8
8
  via [Spinel][spinel].
9
9
 
10
- > **Current release:** [v0.10.0](https://github.com/OriPekelman/tep/releases/tag/v0.10.0)
11
- > — the Proxy + OpenAI-server + Events batteries on top of the agentic
12
- > surface (Auth, Broadcast, Presence, LiveView, MCP).
10
+ > **Current release:** [v0.11.0](https://github.com/OriPekelman/tep/releases/tag/v0.11.0)
11
+ > — now on [RubyGems](https://rubygems.org/gems/tep) (`gem install tep`):
12
+ > TLS, HTTP caching, connection pooling, PG raise-on-error + embeddings,
13
+ > on top of the agentic surface (Auth, Broadcast, Presence, LiveView, MCP).
13
14
  > Pre-alpha; API still in motion.
14
15
 
15
16
  > **Why Tep exists.** Two complementary goals:
@@ -39,22 +40,25 @@ via [Spinel][spinel].
39
40
 
40
41
  ## Quick start
41
42
 
42
- ```sh
43
- # 1. install Spinel
44
- git clone https://github.com/matz/spinel
45
- cd spinel && make all
46
- export PATH="$PWD:$PATH"
43
+ **A new project (the simple path).** The Spinel toolbelt
44
+ ([bundler-spinel](https://github.com/OriPekelman/spinelgems)) scaffolds a
45
+ project and provisions the Spinel compiler for you:
47
46
 
48
- # 2. install Tep
49
- git clone https://github.com/OriPekelman/tep
50
- cd tep && make all
51
- ./examples/hello -p 4567
47
+ ```sh
48
+ gem install bundler-spinel
49
+ spinel-compat init my_app && cd my_app
50
+ ./bin/build # ensures tep, vendors deps, provisions Spinel, compiles → ./app
51
+ ./app -p 4567
52
52
  ```
53
53
 
54
- Your own app:
54
+ `spinel-compat init` writes a `Gemfile` (`gem "tep"`), an `app.rb`, and a
55
+ `bin/build`. The build step provisions a pinned Spinel compiler (cached under
56
+ `~/.cache/spinel`), vendors dependencies where Spinel can follow them, and
57
+ compiles `app.rb` to a native binary — no Ruby runtime in the result.
58
+
59
+ Your `app.rb` is plain Sinatra-flavoured Ruby:
55
60
 
56
61
  ```ruby
57
- # hello.rb
58
62
  require 'sinatra'
59
63
 
60
64
  get '/' do
@@ -66,10 +70,35 @@ get '/hi/:name' do
66
70
  end
67
71
  ```
68
72
 
73
+ **Already have a Sinatra app?** Scaffold a project and use your source as its
74
+ `app.rb` — the build reports any Spinel-incompatible patterns:
75
+
69
76
  ```sh
70
- tep build hello.rb # -> ./hello (~80 KB binary, no Ruby runtime)
71
- ./hello -p 4567
77
+ gem install bundler-spinel
78
+ spinel-compat init my_app && cd my_app
79
+ cp /path/to/your_app.rb app.rb
80
+ ./bin/build # → ./app (or a clear report of what won't translate)
81
+ ```
82
+
83
+ The `tep` translator is also on RubyGems on its own (`gem install tep`) for use
84
+ in your own build scripts, once a Spinel engine is on `$SPINEL` / PATH.
85
+
86
+ <details>
87
+ <summary><b>From source</b> (hacking on tep itself, or no bundler)</summary>
88
+
89
+ ```sh
90
+ # 1. Spinel (the AOT compiler)
91
+ git clone https://github.com/matz/spinel && cd spinel && make all
92
+ export PATH="$PWD:$PATH"
93
+
94
+ # 2. tep
95
+ git clone https://github.com/OriPekelman/tep && cd tep && make all
96
+ ./examples/hello -p 4567
97
+
98
+ # your own app
99
+ tep build hello.rb # → ./hello (~80 KB binary, no Ruby runtime)
72
100
  ```
101
+ </details>
73
102
 
74
103
  The translator (`bin/tep`) needs CRuby >= 3.2 with the `prism` gem
75
104
  installed (a development-only dependency — `bundle install` brings it
data/bin/tep CHANGED
@@ -55,14 +55,23 @@ require "json"
55
55
 
56
56
  # Resolve spinel: explicit env var wins, then a sibling
57
57
  # `../spinel/spinel` checkout (matches the typical dev layout where
58
- # tep and spinel are checked out side by side), then plain `spinel`
59
- # on PATH. The Makefile exports SPINEL=spinel by default; treat the
60
- # literal placeholder as unset so the sibling fallback still fires.
58
+ # tep and spinel are checked out side by side), then the engine
59
+ # provisioned by `spinel-compat install-engine` (spinelgems) at
60
+ # `~/.cache/spinel/current/spinel`, then plain `spinel` on PATH. The
61
+ # Makefile exports SPINEL=spinel by default; treat the literal
62
+ # placeholder as unset so the fallbacks still fire.
61
63
  def self.locate_spinel
62
64
  e = ENV["SPINEL"]
63
65
  return e if e && !e.empty? && e != "spinel"
64
66
  sibling = File.expand_path("../../spinel/spinel", __dir__)
65
67
  return sibling if File.executable?(sibling)
68
+ # Engine provisioned by `spinel-compat install-engine`. Honor
69
+ # SPINEL_CACHE like bundler-spinel does so a relocated cache resolves
70
+ # for both tools (default ~/.cache/spinel; `current` -> the pinned rev).
71
+ cache_root = ENV["SPINEL_CACHE"]
72
+ cache_root = "~/.cache/spinel" if cache_root.nil? || cache_root.empty?
73
+ provisioned = File.expand_path(cache_root + "/current/spinel")
74
+ return provisioned if File.executable?(provisioned)
66
75
  "spinel"
67
76
  end
68
77
  SPINEL = locate_spinel
@@ -1928,13 +1937,13 @@ end
1928
1937
  def tep_ext_subs
1929
1938
  ext_path = File.join(REPO_ROOT, "spinel-ext.json")
1930
1939
  return {} unless File.exist?(ext_path)
1940
+ entries = JSON.parse(File.read(ext_path))
1931
1941
  subs = {}
1932
- JSON.parse(File.read(ext_path)).each do |entry|
1942
+ entries.each do |entry|
1933
1943
  placeholder = entry["placeholder"]
1934
1944
  env_var = placeholder.delete("@") # @TEP_SPHTTP_O@ -> TEP_SPHTTP_O
1935
1945
  if entry["source"]
1936
- default_o = File.expand_path(entry["source"].sub(/\.c\z/, ".o"), REPO_ROOT)
1937
- subs[placeholder] = ENV.fetch(env_var, default_o)
1946
+ subs[placeholder] = resolve_ext_o(entry, entries, env_var)
1938
1947
  else
1939
1948
  cflags = ENV.fetch(env_var, "")
1940
1949
  libs_var = env_var.sub(/_CFLAGS\z/, "_LIBS")
@@ -1946,6 +1955,53 @@ def tep_ext_subs
1946
1955
  subs
1947
1956
  end
1948
1957
 
1958
+ # Resolve a helper `.o`. An explicit env override or a prebuilt `make helper`
1959
+ # artifact wins; otherwise build it on demand. The published gem ships the C
1960
+ # *sources* but no `.o` (a `gem install tep` user has no `make helper` step), so
1961
+ # without this `tep build` fails at link with `cannot find lib/tep/<x>.o`.
1962
+ # Compiles into a writable per-gem cache (the installed gem's lib/ is often
1963
+ # read-only). Returns the `.o` path, or "" for an optional ext whose system dep
1964
+ # is missing (required exts are fatal).
1965
+ def resolve_ext_o(entry, entries, env_var)
1966
+ if (o = ENV[env_var]) && !o.empty?
1967
+ return o if File.exist?(o)
1968
+ end
1969
+ prebuilt = File.expand_path(entry["source"].sub(/\.c\z/, ".o"), REPO_ROOT)
1970
+ return prebuilt if File.exist?(prebuilt)
1971
+
1972
+ src = File.expand_path(entry["source"], REPO_ROOT)
1973
+ fatal("ext source missing: #{entry["source"]}") unless File.exist?(src)
1974
+
1975
+ # own cflags + any same-name sibling's pkg_config include path (the split
1976
+ # @MOD_O@ / @MOD_CFLAGS@ pair — e.g. pg's libpq lives on @TEP_PG_CFLAGS@).
1977
+ cflags = Array(entry["cflags"]).dup
1978
+ missing = nil
1979
+ entries.each do |sib|
1980
+ next unless sib["name"] == entry["name"] && sib["pkg_config"]
1981
+ pc = `pkg-config --cflags #{sib["pkg_config"]} 2>/dev/null`
1982
+ $?.success? ? cflags.concat(pc.split) : (missing = sib["pkg_config"])
1983
+ end
1984
+ if missing
1985
+ return "" if entry["optional"]
1986
+ fatal("ext '#{entry["name"]}' needs #{missing} — install its -dev headers")
1987
+ end
1988
+
1989
+ cache = File.join(ENV["HOME"] || Dir.tmpdir, ".cache", "tep", "ext", File.basename(REPO_ROOT))
1990
+ FileUtils.mkdir_p(cache)
1991
+ out = File.join(cache, File.basename(prebuilt))
1992
+ return out if File.exist?(out) && File.mtime(out) >= File.mtime(src)
1993
+
1994
+ warn "[tep] building ext '#{entry["name"]}' on demand (#{entry["source"]})"
1995
+ if system(ENV.fetch("CC", "cc"), *cflags, "-c", src, "-o", out) && File.exist?(out)
1996
+ out
1997
+ elsif entry["optional"]
1998
+ warn "[tep] optional ext '#{entry["name"]}' failed to build — disabling"
1999
+ ""
2000
+ else
2001
+ fatal("failed to build required ext '#{entry["name"]}' (#{entry["source"]})")
2002
+ end
2003
+ end
2004
+
1949
2005
  # Split a Sinatra-style source on `__END__` and parse the trailing
1950
2006
  # `@@ name` blocks into a {name => template_body} hash.
1951
2007
  def split_inline_views(raw)
data/lib/tep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Tep
2
- VERSION = "0.11.0"
2
+ VERSION = "0.11.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tep
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ori Pekelman