Skip to content

Commit c83c48f

Browse files
rustyconoverclaude
andcommitted
feat(transport): github:// + github-auto:// run a worker from a GitHub release
Two POSIX-only LOCATION schemes that download, verify, cache, and run a VGI worker straight from a GitHub release over the existing subprocess transport — a DX convenience for "no container runtime needed" (overlaps oci://). - github://owner/repo@tag/asset[#sha256=][#path=]: explicit asset + optional digest pin (enforced even on cache hit). - github-auto://owner/repo@tag[/prefix]: deterministic convention {prefix=repo}-{tag}-{DuckDB-platform}.tar.gz, verified against the published .sha256 sidecar (no manual pin needed). Pipeline: authenticated GitHub API resolve ($GITHUB_TOKEN; CDN downloads reuse HttpGetBytes) -> SHA256 verify before parsing -> full-tree USTAR extraction with tar-slip sanitization -> macOS ad-hoc codesign -> flock'd content-digest-keyed atomic-directory cache under ${XDG_CACHE_HOME:-~/.cache}/vgi/releases -> spawn the resolved entrypoint (cwd inherited; publisher builds relocatable). Resolution runs through one ResolveWorkerPath() helper at both spawn sites (EnsureWorkerSpawned + AttemptUnaryRpc); worker_path stays the github:// LOCATION so the pool keys symmetrically. SHA256-only in v1 (cosign provenance deferred); no allowlist (consistent with bare-path/oci://); .zip and eviction deferred. New: vgi_github.{cpp,hpp}, vgi_github_functions.{cpp,hpp} (vgi_github_cache() / vgi_github_cache_flush() diagnostics), vgi_github_cache_dir setting, scheme helpers in vgi_transport, docs/github-transport.md. Tested e2e against Query-farm/vgi-units@v0.1.1 (convert/units_version/supported_units all run via the downloaded worker). C++ scheme unit tests + SQL errors.test (offline) + download.test (gated by VGI_GITHUB_NETWORK_TESTS). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent cacf7c8 commit c83c48f

15 files changed

Lines changed: 1496 additions & 3 deletions

CLAUDE.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ rotation. **Worker API (vgi-python):** subclass `CopyToFunction`, implement `wri
383383
| `vgi_multi_branch_scans` | BOOLEAN | true | Rewrite multi-branch VGI table scans into `LogicalSetOperation(UNION_ALL, ...)` via the optimizer extension. Set to false to disable the rewrite — multi-branch tables then refuse at bind with a clear `BinderException`. Emergency-rollback knob. See [docs/multi_branch.md](docs/multi_branch.md) |
384384
| `vgi_trust_empty_kinds` | BOOLEAN | true | Trust worker assertions that `estimated_object_count[kind] == 0` means the kind is empty (skip `catalog_schema_contents_*` RPC). Set to false to force every RPC to fire — debug escape hatch for diagnosing worker bugs |
385385
| `vgi_secret_default_ttl_seconds` | BIGINT | 300 | Default cache TTL for credentials fetched from an Orchard remote secret provider, when the server suggests none. Further capped per-credential by the credential's own expiry. Read at `ATTACH`, frozen per-provider. See *Remote Secret Provider* |
386+
| `vgi_github_cache_dir` | VARCHAR | `""` | Cache directory for worker binaries downloaded from GitHub releases (`github://` / `github-auto://`). Empty → `${XDG_CACHE_HOME:-~/.cache}/vgi/releases`. Must be on an exec-capable filesystem. See [docs/github-transport.md](docs/github-transport.md) |
386387

387388
Catalogs may register additional settings at `ATTACH` time (e.g., `greeting`, `multiplier`).
388389

@@ -404,13 +405,14 @@ Catalogs may register additional settings at `ATTACH` time (e.g., `greeting`, `m
404405

405406
## Transports
406407

407-
`LOCATION` accepts five schemes:
408+
`LOCATION` accepts these schemes:
408409

409410
- bare command path → **subprocess** transport (default; pooled per-DuckDB-process)
410411
- `http://...` / `https://...`**HTTP** transport
411412
- `unix:///path/to/sock`**AF_UNIX** transport against a worker started out-of-band
412413
- `launch:<argv>`**AF_UNIX** transport with the worker spawned via the launcher
413414
- `oci://<image>[:tag]` (or `docker://` alias) → **container** transport: the worker runs inside an OCI container via the host runtime (docker/podman/nerdctl/Apple `container`), wired over stdin/stdout like a subprocess and pooled the same way. See [docs/container-transport.md](docs/container-transport.md)
415+
- `github://owner/repo@tag/asset[#sha256=][#path=]` and `github-auto://owner/repo@tag[/prefix]`**GitHub-release** transport (POSIX-only): download a worker executable from a GitHub release, SHA256-verify it, extract the full archive into `${XDG_CACHE_HOME:-~/.cache}/vgi/releases` (cross-process flock + atomic dir install), and run it over the subprocess transport. `github://` names the asset explicitly (+ optional `#sha256=` pin, enforced even on cache hit); `github-auto://` builds `{prefix=repo}-{tag}-{DuckDB-platform}.tar.gz` and verifies against the published `.sha256` sidecar. A DX convenience (overlaps `oci://`); SHA256-only (cosign provenance deferred); no allowlist; runs the binary with cwd inherited (publisher must build relocatable). See [docs/github-transport.md](docs/github-transport.md)
414416

415417
The `launch:` and `unix://` paths share one warm worker process across every DuckDB instance that points at the same `(cmd, args, cwd, VGI_RPC_*-env)` tuple — coordinated system-wide via per-hash flock + AF_UNIX socket. See `docs/launcher-protocol.md` for the wire-protocol contract (state-dir layout, hash inputs, lockfile semantics) shared with the Python reference launcher in `vgi-rpc/vgi_rpc/launcher.py`.
416418

@@ -425,6 +427,8 @@ The `launch:` and `unix://` paths share one warm worker process across every Duc
425427
| `vgi_worker_pool()` | Table | Diagnostic: list **subprocess**-pooled workers (worker_path, pid, age_seconds). Returns no rows for `launch:` / `unix://` transports — see *Transports* section. |
426428
| `vgi_worker_pool_stats()` | Table | Diagnostic: hit/miss statistics by worker_path. Subprocess pool only. |
427429
| `vgi_worker_pool_flush()` | Table | Clear all subprocess-pooled workers; returns one row with the count flushed (`flushed`). Has no effect on `launch:` / `unix://` workers. |
430+
| `vgi_github_cache()` | Table | Diagnostic: one row per worker binary cached from a `github://` / `github-auto://` release LOCATION. Columns: `owner`, `repo`, `tag`, `asset`, `digest` (archive SHA256), `dir` (extracted directory), `entrypoint`, `age_seconds`. See [docs/github-transport.md](docs/github-transport.md) |
431+
| `vgi_github_cache_flush()` | Table | Delete the on-disk GitHub-release worker cache; returns one row with the count of cached releases removed (`flushed`). |
428432
| `vgi_clear_cache()` | Table | Clear cached catalog metadata (schemas, tables, functions, statistics) for all attached VGI catalogs |
429433
| `vgi_oauth_identity()` | Table | OIDC identity per attached VGI catalog: `catalog_name`, `origin`, `authenticated`, `sub`, `email`, `name`, `issuer`, `claims` (JSON). Claims carry the full decoded id_token payload — reach provider-specific fields via e.g. `claims->>'$.preferred_username'` for Entra, `claims->>'$.hd'` for Google Workspace, etc. |
430434
| `vgi_table_branches()` | Table | Diagnostic: one row per branch per VGI table across every attached VGI catalog. Columns: `catalog_name`, `schema_name`, `table_name`, `branch_index`, `function_name`, `positional_arguments` (JSON), `named_arguments` (JSON), `branch_filter`, `table_required_extensions` (LIST). Used to introspect multi-branch tables. See [docs/multi_branch.md](docs/multi_branch.md). |
@@ -447,6 +451,8 @@ The `launch:` and `unix://` paths share one warm worker process across every Duc
447451
| `vgi_subprocess.cpp` | SubProcess/Pipe RAII, `WaitForReadable()` with EINTR retry, `GetCatalogTimeout()` |
448452
| `vgi_worker_pool.cpp` | `VgiWorkerPool` singleton, background cleanup thread |
449453
| `vgi_worker_pool_functions.cpp` | Pool diagnostic SQL functions |
454+
| `vgi_github.cpp` | `github://` / `github-auto://` transport: coordinate parsing, `github-auto://` convention name-building from `DuckDB::Platform()`, authenticated GitHub API GET (+ reuse of `HttpGetBytes` for CDN downloads), SHA256 verify-before-extract, full-tree USTAR extractor with tar-slip sanitization, and the flock'd content-digest-keyed atomic-directory cache. Exposes `ResolveWorkerPath()` (called at both spawn sites: `EnsureWorkerSpawned` + `AttemptUnaryRpc`). See [docs/github-transport.md](docs/github-transport.md) |
455+
| `vgi_github_functions.cpp` | `vgi_github_cache()` / `vgi_github_cache_flush()` SQL diagnostics |
450456
| `vgi_table_function.cpp` | Direct `vgi_table_function()` SQL function |
451457
| `vgi_table_function_impl.cpp` | Shared table function logic (bind/init/scan) |
452458
| `vgi_scalar_function_impl.cpp` | Scalar function bind/execute with dynamic types and const params |

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ src/vgi_shm_segment.cpp
118118
src/vgi_cancel_dispatcher.cpp
119119
src/vgi_worker_pool.cpp
120120
src/vgi_worker_pool_functions.cpp
121+
src/vgi_github.cpp
122+
src/vgi_github_functions.cpp
121123
src/vgi_wasm_async_pool.cpp
122124
src/vgi_table_statistics_function.cpp
123125
src/vgi_clear_cache.cpp

docs/github-transport.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# GitHub-release worker transport (`github://` / `github-auto://`)
2+
3+
Run a VGI worker straight from a GitHub release — no container runtime, no manual
4+
download. The extension fetches the release asset, verifies its SHA256, extracts it
5+
into a per-user cache, and runs the worker over the ordinary subprocess transport.
6+
7+
This is a **developer-experience convenience**. It overlaps `oci://` (which already
8+
gives registry distribution, signing, and isolation); the one thing it adds is "no
9+
container runtime needed." For locked-down / enterprise supply-chain control, prefer
10+
publishing an image and using `oci://` against your own registry.
11+
12+
## The two schemes
13+
14+
```sql
15+
-- Explicit: any repo, you name the asset and (recommended) pin its digest.
16+
ATTACH 'u' AS u (TYPE vgi,
17+
LOCATION 'github://Query-farm/vgi-units@v0.1.1/vgi-units-v0.1.1-osx_arm64.tar.gz#sha256=78168f…');
18+
19+
-- Auto: the publisher follows a naming convention; one line works on every platform.
20+
ATTACH 'u' AS u (TYPE vgi, LOCATION 'github-auto://Query-farm/vgi-units@v0.1.1');
21+
```
22+
23+
Both accept the same LOCATION wherever a worker path is accepted: `ATTACH`,
24+
`vgi_table_function(...)`, `vgi_catalogs(...)`.
25+
26+
### `github://owner/repo@tag/asset[#sha256=<hex>][#path=<member>]`
27+
- `asset` is the exact release asset filename (the segment after the last `/`; tags
28+
may themselves contain `/`).
29+
- `#sha256=<hex>` pins the downloaded asset's digest. **Strongly recommended** — it is
30+
the integrity guarantee, and it is enforced even on a cache hit.
31+
- `#path=<member>` selects which file inside a multi-file archive is the worker
32+
executable (only needed if auto-selection is ambiguous — see below).
33+
34+
### `github-auto://owner/repo@tag[/prefix]`
35+
Builds the asset name from a fixed convention and verifies it against the published
36+
`.sha256` sidecar (so no manual pin is needed):
37+
38+
```
39+
{prefix}-{tag}-{platform}.tar.gz (prefix defaults to the repo name)
40+
```
41+
42+
`{platform}` is DuckDB's own platform string (`osx_arm64`, `linux_amd64`,
43+
`linux_arm64`, `linux_amd64_musl`, …). The digest is read from the first sidecar that
44+
exists among `{stem}.sha256` (GoReleaser style) or `{asset}.sha256`.
45+
46+
It is a **deterministic convention**, not a heuristic: it constructs *one* exact name
47+
and fails with a clear "asset not found, available: …" listing if the publisher didn't
48+
follow it.
49+
50+
## Publisher contract
51+
52+
To support `github-auto://`, publish per-platform assets named
53+
`{repo}-{tag}-{platform}.tar.gz` (platform = the DuckDB platform string) plus a
54+
`.sha256` sidecar. The reference `Query-farm/vgi-units` release (a GoReleaser pipeline)
55+
is the model: `vgi-units-v0.1.1-osx_arm64.tar.gz` + `vgi-units-v0.1.1-osx_arm64.sha256`,
56+
each tarball containing the executable + README + LICENSE.
57+
58+
**Build the worker relocatable.** The worker runs from wherever it was cached, with
59+
the caller's working directory inherited (we do *not* `chdir` into the cache). So:
60+
- link shared libraries with rpath `@loader_path` (macOS) / `$ORIGIN` (Linux);
61+
- resolve data files relative to the executable (`/proc/self/exe`,
62+
`_NSGetExecutablePath`), never relative to the current directory.
63+
64+
If the worker is a single self-contained binary (static Go/Rust, etc.), nothing extra
65+
is required.
66+
67+
## Verification
68+
69+
- **SHA256 is enforced**: `github://` against the `#sha256=` pin, `github-auto://`
70+
against the `.sha256` sidecar. The archive bytes are verified *before* the tar
71+
parser runs, so extraction only ever sees trusted input. A mismatch throws.
72+
- **No allowlist**: consistent with every other LOCATION scheme (bare path runs any
73+
executable, `oci://` runs any image). The trust boundary is whoever writes the
74+
`ATTACH`. Note this is the first scheme that fetches and execs *remote* native code
75+
from a string — pin digests and treat LOCATION strings in shared catalogs/configs
76+
accordingly.
77+
- **Sigstore/cosign provenance** is a deferred follow-up (the reference release already
78+
ships `.cosign.bundle` sidecars).
79+
80+
## Caching
81+
82+
- Cache dir: `${XDG_CACHE_HOME:-~/.cache}/vgi/releases` (override with the
83+
`vgi_github_cache_dir` setting). It must be on an **exec-capable** filesystem (not a
84+
`noexec` runtime/tmp mount).
85+
- Keyed by **content digest** — two attaches of the same release share one cached,
86+
immutable extracted tree, across concurrent DuckDB processes (coordinate-keyed
87+
`flock`, atomic directory install).
88+
- macOS arm64: the extracted binary is ad-hoc code-signed (`codesign --deep -s -`) so
89+
unsigned Mach-O / nested dylibs aren't `SIGKILL`ed at exec.
90+
- No automatic eviction in v1 — clear with `SELECT * FROM vgi_github_cache_flush();`
91+
(or `rm -rf` the cache dir).
92+
93+
## Diagnostics
94+
95+
```sql
96+
SELECT * FROM vgi_github_cache(); -- owner, repo, tag, asset, digest, dir, entrypoint, age_seconds
97+
SELECT * FROM vgi_github_cache_flush(); -- clears the cache; returns the count removed
98+
```
99+
100+
## Formats & limits
101+
102+
- Supported assets: bare executable, single-file `.zst`/`.gz`, and `.tar.gz` /
103+
`.tar.zst` archives (decompressed in-process; the full tree is extracted with
104+
tar-slip path sanitization). The single executable-bit member is the entrypoint;
105+
use `#path=<member>` to disambiguate.
106+
- `.zip` is not supported (publish `.tar.gz`).
107+
- **POSIX-only.** On non-POSIX builds these schemes throw a clear error at ATTACH.
108+
- Authenticated GitHub API access: set `$GITHUB_TOKEN` (or `$GH_TOKEN`) to avoid the
109+
60-request/hour unauthenticated limit and to reach private repos. Asset *downloads*
110+
use the pre-signed CDN URL with no Authorization header.

src/include/vgi_github.hpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// © Copyright 2025, 2026 Query Farm LLC - https://query.farm
2+
//
3+
// github:// and github-auto:// LOCATION schemes — download a worker executable
4+
// from a GitHub release, verify it (SHA256), extract the full archive into a
5+
// cross-process cache directory, and return the absolute path of the worker
6+
// entrypoint so it can run over the ordinary subprocess transport.
7+
//
8+
// A developer-experience convenience (overlaps oci://; the one thing it adds is
9+
// "no container runtime needed"). SHA256 integrity only in v1 — sigstore/cosign
10+
// provenance is a deferred follow-up. POSIX-only (the cache uses flock/rename);
11+
// the dispatch throws on non-POSIX builds before reaching here.
12+
//
13+
// See docs/github-transport.md and the design plan.
14+
15+
#pragma once
16+
17+
#include <cstdint>
18+
#include <string>
19+
#include <vector>
20+
21+
namespace duckdb {
22+
23+
class ClientContext;
24+
25+
namespace vgi {
26+
27+
// Parsed coordinates of a github(-auto):// location. Exposed for unit tests.
28+
struct GithubCoords {
29+
std::string owner;
30+
std::string repo;
31+
std::string tag;
32+
std::string asset; // explicit (github://) or convention-built (github-auto://)
33+
std::string expected_sha256; // lowercase hex; empty if not pinned / not yet fetched
34+
std::string member_hint; // #path=<member> override; empty if absent
35+
bool is_auto = false; // true for github-auto://
36+
};
37+
38+
// Parse a `github://owner/repo@tag/asset[#sha256=<hex>][#path=<member>]` location.
39+
// Throws std::invalid_argument on a malformed/empty field.
40+
GithubCoords ParseGithubLocation(const std::string &location);
41+
42+
// Parse a `github-auto://owner/repo@tag[/prefix]` location and build the asset
43+
// name from the convention `{prefix=repo}-{tag}-{platform}.tar.gz`. `platform` is
44+
// the DuckDB platform string (e.g. "osx_arm64"). The returned coords have `asset`
45+
// filled and `expected_sha256` empty (the `.sha256` sidecar is fetched later, at
46+
// resolve time). Throws std::invalid_argument on a malformed/empty field.
47+
GithubCoords ParseGithubAutoLocation(const std::string &location, const std::string &platform);
48+
49+
// Spawn-site helper: returns `location` unchanged unless it is a github:// /
50+
// github-auto:// scheme, in which case it resolves to the local cached entrypoint
51+
// (downloading on a cache miss). Cheap for non-github locations (one prefix
52+
// check). Call this right before SpawnWorker() at every spawn site so the raw
53+
// scheme string is never handed to fork/exec. POSIX-only for github schemes; a
54+
// github location on a non-POSIX build throws.
55+
std::string ResolveWorkerPath(const std::string &location, ClientContext &context);
56+
57+
// Resolve a github:// / github-auto:// LOCATION to a local, cached, verified,
58+
// executable worker entrypoint (absolute path). On a cache miss this fetches the
59+
// release metadata, downloads the asset, verifies its SHA256, and extracts the
60+
// full archive tree atomically into the per-user cache. Cross-process safe
61+
// (coordinate-keyed flock). Throws IOException/InvalidInputException on failure.
62+
std::string ResolveGithubWorker(const std::string &location, ClientContext &context);
63+
64+
// Row of the vgi_github_cache() diagnostic.
65+
struct GithubCacheEntry {
66+
std::string owner;
67+
std::string repo;
68+
std::string tag;
69+
std::string asset;
70+
std::string digest; // archive SHA256 (lowercase hex)
71+
std::string dir; // extracted directory
72+
std::string entrypoint; // absolute path of the worker binary
73+
int64_t age_seconds = 0;
74+
};
75+
76+
// Enumerate the on-disk cache (reads each <digest>/.meta sidecar). Never throws;
77+
// returns an empty list if the cache dir is absent. `context` supplies the
78+
// configured cache dir (vgi_github_cache_dir setting).
79+
std::vector<GithubCacheEntry> ListGithubCache(ClientContext &context);
80+
81+
// Remove the entire cache directory; returns the number of cached releases (top-
82+
// level <digest> dirs) removed. Never throws.
83+
int64_t FlushGithubCache(ClientContext &context);
84+
85+
} // namespace vgi
86+
} // namespace duckdb
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// © Copyright 2025, 2026 Query Farm LLC - https://query.farm
2+
#pragma once
3+
4+
namespace duckdb {
5+
6+
class ExtensionLoader;
7+
8+
namespace vgi {
9+
10+
//! Register the vgi_github_cache() table function (list cached release workers).
11+
void RegisterVgiGithubCacheFunction(ExtensionLoader &loader);
12+
13+
//! Register the vgi_github_cache_flush() table function (clear the release cache).
14+
void RegisterVgiGithubCacheFlushFunction(ExtensionLoader &loader);
15+
16+
} // namespace vgi
17+
} // namespace duckdb

src/include/vgi_transport.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,22 @@ std::string StripLaunchScheme(const std::string &location);
6161
// std::invalid_argument if the location is not a container location.
6262
std::string StripContainerScheme(const std::string &location);
6363

64+
// GitHub-release locations. `github://` downloads a named release asset and runs
65+
// it over the subprocess transport; `github-auto://` builds the asset name from a
66+
// fixed convention (see vgi_github.hpp). These are distinct schemes — note that
67+
// `github-auto://` does NOT start with `github://`, so the predicates don't
68+
// overlap. Both are POSIX-only (the cache uses flock/rename); the dispatch throws
69+
// on other builds. Unlike `oci://ghcr.io/...` (GitHub's *container* registry),
70+
// these fetch *release assets*.
71+
bool IsGithubLocation(const std::string &worker_path);
72+
bool IsGithubAutoLocation(const std::string &worker_path);
73+
74+
// Strip the `github://` / `github-auto://` scheme prefix, preserving the
75+
// case-sensitive remainder (owner/repo are case-sensitive) INCLUDING any
76+
// `#sha256=`/`#path=` fragment — the coordinate parser in vgi_github.cpp needs it.
77+
// Throws std::invalid_argument if the location doesn't carry the expected prefix.
78+
std::string StripGithubScheme(const std::string &location);
79+
std::string StripGithubAutoScheme(const std::string &location);
80+
6481
} // namespace vgi
6582
} // namespace duckdb

src/vgi_extension.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
#include "vgi_table_branches_function.hpp"
8888
#include "vgi_function_arguments_function.hpp"
8989
#include "vgi_clear_cache.hpp"
90+
#include "vgi_github_functions.hpp"
9091
#include "vgi_worker_pool_functions.hpp"
9192

9293
#include "duckdb/catalog/catalog_transaction.hpp"
@@ -2771,6 +2772,14 @@ static void LoadInternal(ExtensionLoader &loader) {
27712772
config.AddExtensionOption("vgi_worker_pool_max", "Default per-path pool limit for VGI workers (0 = disabled)",
27722773
LogicalType::BIGINT, Value::BIGINT(256));
27732774

2775+
// Cache directory for worker binaries downloaded via github:// / github-auto://
2776+
// LOCATIONs. Empty (default) → ${XDG_CACHE_HOME:-~/.cache}/vgi/releases. Must be
2777+
// on an exec-capable filesystem (not a noexec runtime/tmp mount).
2778+
config.AddExtensionOption("vgi_github_cache_dir",
2779+
"Cache directory for worker binaries downloaded from GitHub releases "
2780+
"(github:// / github-auto:// LOCATIONs); empty = ${XDG_CACHE_HOME:-~/.cache}/vgi/releases",
2781+
LogicalType::VARCHAR, Value(""));
2782+
27742783
// Eager-load thresholds. Per-kind: a schema's estimated_object_count[kind]
27752784
// is compared against the corresponding value here; below or equal triggers
27762785
// a single bulk LoadEntries() instead of per-name single-entry RPCs. Read
@@ -2880,6 +2889,8 @@ static void LoadInternal(ExtensionLoader &loader) {
28802889
vgi::RegisterVgiWorkerPoolFunction(loader);
28812890
vgi::RegisterVgiWorkerPoolStatsFunction(loader);
28822891
vgi::RegisterVgiWorkerPoolFlushFunction(loader);
2892+
vgi::RegisterVgiGithubCacheFunction(loader);
2893+
vgi::RegisterVgiGithubCacheFlushFunction(loader);
28832894

28842895
// Register table statistics diagnostic function
28852896
vgi::RegisterVgiTableStatisticsFunction(loader);

0 commit comments

Comments
 (0)