diff options
Diffstat (limited to 'src/subcommand.zig')
| -rw-r--r-- | src/subcommand.zig | 70 |
1 files changed, 67 insertions, 3 deletions
diff --git a/src/subcommand.zig b/src/subcommand.zig index 0c04ce8..1372d72 100644 --- a/src/subcommand.zig +++ b/src/subcommand.zig @@ -77,6 +77,10 @@ pub fn dispatch( try runBootstrapSubcommand(allocator, io, environ_map, panto_executable_path, .{ .force = force }); return .done; } + if (std.mem.eql(u8, sub, "update")) { + try runUpdateSubcommand(allocator, io, environ_map, panto_executable_path); + return .done; + } if (std.mem.eql(u8, sub, "sessions")) { try runSessionsSubcommand(allocator, io, environ_map); return .done; @@ -116,6 +120,7 @@ fn printHelp(io: Io) !void { \\ Forget a stored OAuth token. \\ panto bootstrap [--force] \\ Run the luarocks bootstrap and exit. + \\ panto update Install/update the rocks in extensions.rocks. \\ panto lua [args...] Drop into the embedded Lua interpreter. \\ panto help Show this message. \\ @@ -124,9 +129,10 @@ fn printHelp(io: Io) !void { \\ $XDG_CONFIG_HOME/panto/config.toml (user) \\ ./.panto/config.toml (project) \\ Define providers under [providers.<name>], pick a default with - \\ [defaults] model = "<provider>:<alias>", and gate tools/extensions - \\ with [tools]/[extensions] allow/deny globs. Model aliases (wire name, - \\ reasoning, max_tokens, pricing) live in models.toml. + \\ [defaults] model = "<provider>:<alias>", and gate extensions with + \\ [extensions] allow/deny globs (plus [extensions] paths/rocks to add + \\ sources). Model aliases (wire name, reasoning, max_tokens, pricing) + \\ live in models.toml. \\ \\Environment: \\ OPENAI_API_KEY, ANTHROPIC_API_KEY Consumed by the default providers. @@ -268,6 +274,64 @@ fn runBootstrapSubcommand( } // --------------------------------------------------------------------------- +// `panto update` +// --------------------------------------------------------------------------- + +/// `panto update` — (re)install every rock listed in `extensions.rocks`, +/// unconditionally. This is the one place we intentionally hit luarocks (and +/// the network): startup only installs a rock that is missing, so changing a +/// pin within an already-satisfied range, or forcing a re-resolve, is done +/// here. Analogous to `bootstrap`, but for user rocks rather than batteries. +fn runUpdateSubcommand( + allocator: Allocator, + io: Io, + environ_map: *const std.process.Environ.Map, + panto_executable_path: []const u8, +) !void { + var cwd_buf: [std.fs.max_path_bytes]u8 = undefined; + const cwd_n = try std.process.currentPath(io, &cwd_buf); + const cwd = cwd_buf[0..cwd_n]; + + var cfg = config_file.load(allocator, io, environ_map, cwd) catch |err| { + std.log.err("panto update: failed to load config ({t})", .{err}); + return err; + }; + defer cfg.deinit(); + + var out_buf: [1024]u8 = undefined; + var out_file = std.Io.File.stdout().writer(io, &out_buf); + const out = &out_file.interface; + + if (cfg.ext_rocks.len == 0) { + try out.writeAll("panto update: no extensions.rocks configured\n"); + try out_file.flush(); + return; + } + + const L = c.luaL_newstate() orelse return error.LuaInitFailed; + defer c.lua_close(L); + c.luaL_openlibs(L); + const rt = try luarocks_runtime.bootstrap(allocator, io, environ_map, L, panto_executable_path); + defer rt.deinit(); + + var installed: usize = 0; + var failed: usize = 0; + for (cfg.ext_rocks) |spec| { + std.log.info("panto update: installing '{s}'", .{spec.value}); + luarocks_runtime.installRock(rt, allocator, spec.value) catch |err| { + std.log.err("panto update: failed to install '{s}': {t}", .{ spec.value, err }); + failed += 1; + continue; + }; + installed += 1; + } + + try out.print("panto update: {d} rock(s) installed, {d} failed\n", .{ installed, failed }); + try out_file.flush(); + if (failed > 0) return error.LuarocksInstallFailed; +} + +// --------------------------------------------------------------------------- // `panto sessions` // --------------------------------------------------------------------------- |
