No description
Find a file
2025-11-30 00:02:25 +02:00
scripts Move scripts to scripts/ and update loader paths; use remote import_map with --no-config 2025-11-29 23:48:28 +02:00
completions.d.ts Add remote script loader with intelligent completions 2025-11-29 23:22:35 +02:00
COMPLETIONS.md Add remote script loader with intelligent completions 2025-11-29 23:22:35 +02:00
configure_ssh_key.ts Move scripts to scripts/ and update loader paths; use remote import_map with --no-config 2025-11-29 23:48:28 +02:00
deno.json Add remote script loader with intelligent completions 2025-11-29 23:22:35 +02:00
deno.lock Add remote script loader with intelligent completions 2025-11-29 23:22:35 +02:00
import_map.json Move scripts to scripts/ and update loader paths; use remote import_map with --no-config 2025-11-29 23:48:28 +02:00
loader.ts Update script URL construction to reflect new 'scripts/' subdirectory 2025-11-30 00:02:25 +02:00
README.md Add remote script loader with intelligent completions 2025-11-29 23:22:35 +02:00

Remote Script Loader

A lightweight, intelligent script loader for Deno that enables remote script execution with shell completions, version control, and caching.

Features

  • 🚀 Remote Execution - Run scripts directly from Git without cloning
  • 🔄 Version Control - Execute scripts from specific branches or tags (script@v1.0)
  • Smart Completions - Context-aware shell completions with filesystem discovery
  • 💾 Intelligent Caching - HTTP caching with ETag/Last-Modified support
  • 🔒 Fast Daemon - Background daemon for instant completions (< 15ms)
  • 📦 Zero Config - Works out of the box with bash and zsh

Quick Start

Installation

Add to your ~/.bashrc or ~/.zshrc:

# Load remote script loader from main branch
export LOADER_REF=main
eval "$(deno run --allow-net --allow-read --allow-env --allow-run --quiet https://git.kkarolis.lt/Karolis/scripts/raw/branch/main/loader.ts)"

Or pin to a specific version:

# Load from a specific tag
export LOADER_REF=main
eval "$(deno run --allow-net --allow-read --allow-env --allow-run --quiet https://git.kkarolis.lt/Karolis/scripts/raw/tag/v1.0.0/loader.ts)"

Or use a local copy:

# Load from local file (for development)
export LOADER_REF=main
eval "$(deno run --allow-net --allow-read --allow-env --allow-run --quiet /path/to/scripts/loader.ts)"

Using Custom Refs

Set the LOADER_REF environment variable to use a different default branch:

# Use a development branch by default
export LOADER_REF=develop
eval "$(deno run --allow-net --allow-read --allow-env --allow-run --quiet https://git.kkarolis.lt/Karolis/scripts/raw/branch/develop/loader.ts)"

Basic Usage

# List available scripts
scripts

# Run a script
scripts configure_ssh_key user@host --key=~/.ssh/id_rsa

# Run script from a specific branch
scripts configure_ssh_key@develop user@host

# Run script from a specific tag
scripts configure_ssh_key@v1.0.0 user@host

Shell Completions

Press TAB to get intelligent completions:

scripts <TAB>                          # Lists all available scripts
scripts conf<TAB>                      # Completes to configure_ssh_key
scripts configure_ssh_key@<TAB>        # Lists available branches and tags
scripts configure_ssh_key --<TAB>      # Shows script-specific flags
scripts configure_ssh_key --key=<TAB>  # Suggests SSH keys from ~/.ssh/

Configuration

Environment Variables

  • LOADER_REF - Default branch/tag to use (default: main)
  • SCRIPTS_DAEMON_PORT - Force specific port for daemon (default: auto-assign)
  • SCRIPTS_DAEMON_PORT_FILE - Port file location (default: /tmp/scripts-daemon.port)

Examples

# Use staging branch by default
export LOADER_REF=staging

# Force daemon to use specific port
export SCRIPTS_DAEMON_PORT=54321

# Custom port file location
export SCRIPTS_DAEMON_PORT_FILE=~/.cache/scripts-daemon.port

How It Works

  1. Initial Load: When sourced, the loader:

    • Spawns a background daemon for completions
    • Fetches the list of available scripts
    • Defines the scripts shell function
  2. Daemon: A persistent HTTP server that:

    • Caches script lists and branch/tag information (60s TTL)
    • Caches script modules with HTTP ETag/Last-Modified (300s TTL)
    • Provides fast completions (< 15ms after first request)
  3. Execution: When you run a script:

    • Validates the script exists on the remote
    • Downloads and executes it with Deno
    • All script arguments are passed through

Architecture

┌─────────────────────────────────────────────────────────────┐
│                        Shell (bash/zsh)                      │
│  scripts configure_ssh_key --key=~/.ssh/id_rsa <TAB>         │
└──────────────┬──────────────────────────────────────────────┘
               │
               ├─ Execution: Direct to Deno
               │     │
               │     └─> Deno runs remote script
               │
               └─ Completion: Query daemon
                     │
                     ▼
          ┌──────────────────────┐
          │  Daemon (port 54xxx) │
          │  ─────────────────── │
          │  • Caches metadata   │
          │  • Caches modules    │
          │  • HTTP conditional  │
          │    requests (304)    │
          └──────────────────────┘
                     │
                     ├─> Script List API
                     ├─> Branches/Tags API
                     └─> Script Module Import

Writing Scripts

See COMPLETIONS.md for a comprehensive guide on adding intelligent completions to your scripts.

Minimal Example

#!/usr/bin/env -S deno run --allow-all
import type { CompletionContext } from "./completions.d.ts";

// Export completion function (optional)
export function getCompletions(ctx: CompletionContext): string[] {
  return ["--verbose", "--help"];
}

// Your script logic
console.log("Hello from remote script!");

Using Dependencies

For remote scripts to work, use explicit JSR imports instead of bare specifiers:

// ❌ Won't work for remote scripts
import { parseArgs } from "@std/cli/parse-args";

// ✅ Use JSR imports for remote execution
import { parseArgs } from "jsr:@std/cli@^1.0.0/parse-args";

This ensures dependencies are resolved when the script is executed remotely via URL.

Async Completions

export async function getCompletions(ctx: CompletionContext): Promise<string[]> {
  const { current } = ctx;
  
  if (current.startsWith("--key=")) {
    // Discover SSH keys asynchronously
    const keys: string[] = [];
    for await (const entry of Deno.readDir(`${Deno.env.get("HOME")}/.ssh`)) {
      if (entry.isFile && entry.name.startsWith("id_")) {
        keys.push(`--key=~/.ssh/${entry.name}`);
      }
    }
    return keys;
  }
  
  return ["--key=", "--help"];
}

Performance

Caching Strategy

Resource Cache Duration Strategy
Script List 60 seconds In-memory
Branches/Tags 60 seconds In-memory
Script Modules 300 seconds HTTP ETag/Last-Modified

Benchmarks

  • First completion: ~120ms (network + parse)
  • Cached completion: ~14ms (8x faster)
  • Daemon startup: ~100ms
  • Loader script: Exits immediately after spawning daemon

Troubleshooting

Daemon Not Starting

Check if the daemon is running:

ps aux | grep "loader.ts.*daemon"
cat /tmp/scripts-daemon.port
curl http://127.0.0.1:$(cat /tmp/scripts-daemon.port)/health

Restart the daemon:

pkill -f "loader.ts.*daemon"
eval "$(deno run --allow-net --allow-read --allow-env --allow-run --quiet loader.ts)"

Completions Not Working

  1. Verify daemon is running (see above)
  2. Check completion function is exported:
    deno eval 'const m = await import("file:///path/to/script.ts"); console.log(typeof m.getCompletions)'
    
  3. Test completion function directly:
    deno eval 'const m = await import("file:///path/to/script.ts"); console.log(await m.getCompletions({args: [], current: "--", position: 0}))'
    

Slow Completions

  • Check network latency to Git server
  • Verify caching is working (second request should be faster)
  • Consider increasing cache TTL in daemon code
  • Avoid expensive operations in completion functions

Scripts Not Found

# Check script exists in repository
scripts

# Verify URL is accessible
curl -I https://git.kkarolis.lt/Karolis/scripts/raw/branch/main/script-name.ts

# Try with explicit ref
scripts script-name@main

Advanced Usage

Multiple Script Repositories

You can have multiple loader instances with different configurations:

# Personal scripts
export LOADER_REF=main
eval "$(deno run --allow-net --allow-read --allow-env --allow-run --quiet https://git.example.com/me/scripts/raw/branch/main/loader.ts)"
alias myscripts='scripts'

# Work scripts  
export LOADER_REF=production
eval "$(deno run --allow-net --allow-read --allow-env --allow-run --quiet https://git.company.com/team/scripts/raw/branch/production/loader.ts)"
alias workscripts='scripts'

Development Workflow

# Use local loader for testing
cd ~/scripts
export LOADER_REF=main
eval "$(deno run --allow-net --allow-read --allow-env --allow-run --quiet ./loader.ts)"

# Test your script
scripts my-new-script --help

# Push and use remote version
git push
export LOADER_REF=main
eval "$(deno run --allow-net --allow-read --allow-env --allow-run --quiet https://git.kkarolis.lt/Karolis/scripts/raw/branch/main/loader.ts)"
scripts my-new-script@main --help

Files in This Repository

  • loader.ts - Main loader script with daemon
  • completions.d.ts - TypeScript definitions for completion functions
  • COMPLETIONS.md - Comprehensive guide for script developers
  • configure_ssh_key.ts - Example script with advanced completions
  • README.md - This file

Requirements

  • Deno 1.30+ installed
  • Bash or Zsh shell
  • Network access to Git server
  • curl for health checks (in completion functions)

License

MIT