npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lyalindotcom/ai-portal

v0.2.2

Published

Telegram bridge for running local Gemini CLI sessions from any project directory.

Readme

Portal

Portal is a Telegram bridge for local gemini CLI sessions. Run portal in any project directory to control Gemini through Telegram.

Warning

  • Portal is experimental and unsupported.
  • Regular mode typically runs with YOLO approval.
  • YOLO auto-approves actions and can be risky on your machine.
  • Use at your own risk, ideally in disposable/test environments.

Requirements

  • Node.js 18+
  • gemini CLI installed
  • Gemini auth configured once manually on this machine
  • A Telegram bot token

Install

npm i -g @lyalindotcom/ai-portal

Quick start

portal

In an interactive terminal, portal opens a launcher UI with arrow-key selection for:

  • Use last config (one-click launch from last saved startup profile)
  • Start action
  • Session mode
  • Gemini backend
  • Preferred port
  • Owner reset toggle
  • ACP live trace toggle (ACP backend)

Use last config reuses the most recent startup profile (mode/backend/port/trace) saved in global config.

Then in Telegram:

  1. Open chat with your bot
  2. Send /start
  3. Send /connect <PIN> (PIN is shown in terminal)
  4. Send prompts

First run opens interactive setup and stores config in ~/.portal/config.json.

CLI commands

  • portal
  • portal start
  • portal config
  • portal --change
  • portal --reset-owner
  • portal config --reset-owner
  • portal --port 3001
  • portal --mode regular
  • portal --mode assistant
  • portal --backend headless
  • portal --backend acp
  • portal --acp-live-trace
  • portal --no-acp-live-trace
  • portal --ui
  • portal --no-ui

Telegram commands

  • /start
  • /help
  • /connect <PIN>
  • /ping
  • /status
  • /newsession

Session modes

  • regular
    • Default behavior.
    • Commonly YOLO approval mode.
  • assistant
    • Read-only assistant mode.
    • Requires GEMINI_API_KEY.
    • Requires Gemini auth type set to API key in ~/.gemini/settings.json.

Gemini backends

  • headless (default)
    • Runs one-shot Gemini CLI commands (gemini -p ...) per request.
    • Uses CLI --resume for session continuity.
  • acp (experimental)
    • Runs Gemini CLI in --experimental-acp mode with a persistent ACP transport.
    • Streams tool/message events into Portal logs.
    • Falls back to a new ACP session if loadSession fails.
    • Optional Telegram live trace stream can be toggled with --acp-live-trace / --no-acp-live-trace.

File uploads

  • Send a document or photo with a caption describing the task.
  • File is downloaded to local sandbox and passed to Gemini with its path.
  • Max size default: 10 MB.
  • image/* uploads are allowed.
  • Gemini can return generated files to Telegram using:
    • <portal_send_file>
    • path: relative/path/from/sandbox.ext
    • caption: Optional caption
    • </portal_send_file>

Runtime behavior

  • Health endpoint: http://localhost:<port>/healthz
  • Runtime dir (per project): ./.portal/
  • Portal injects a runtime Gemini system settings file to enable a safety hook:
    • blocks destructive run_shell_command tool calls before execution
    • logs hook decisions to ./.portal/logs/gemini-hooks.jsonl
  • Stable machine ID + per-run instance ID shown in startup diagnostics and /status.
  • PIN is reused for up to 30 minutes when possible.
  • One active request per chat, one queued request max.
  • 3 invalid PIN attempts shut down the server.
  • Interactions are logged to terminal and JSONL file.
  • ACP backend additionally logs per-event stream updates (tool_call, chunks, permission decisions).
  • ACP live trace in Telegram is rendered as stage updates + boxed tool output previews.
  • On startup, Portal announces its machine/instance ID to owner chat (if owner chat is configured).
  • If Telegram polling conflict is detected (another machine running same token), Portal sends a conflict alert and exits.

Architecture

Portal is a local wrapper around Gemini CLI with Telegram as the remote control surface.

High-level topology

                     Telegram Cloud
                           |
                    getUpdates/send*
                           |
                    +-------------+
                    |  Poller +   |
                    |   Handler   |
                    +------+------+        +----------------------+
                           |               |  Express /healthz    |
                   owner/PIN gate          +----------------------+
                           |
                   +-------v-------+
                   |  Portal Core  |
                   | queue/session |
                   +-------+-------+
                           |
                    spawn gemini CLI
                           |
                   +-------v-------+
                   | Gemini Runner |
                   | headless/acp  |
                   +-------+-------+
                           |
                 .portal/agent_sandbox

Main components

  • CLI entrypoint
    • Parses args, runs first-time config wizard, starts server in current folder.
  • Express health server
    • Serves /healthz for local diagnostics.
  • Telegram client + poller
    • Long-polls getUpdates, handles reconnect/timeout recovery, detects conflict.
  • Telegram handler
    • Enforces owner/PIN gate, slash commands, per-chat queueing, file intake, Gemini calls.
  • Gemini runner
    • headless: executes gemini -p ... --output-format json, resumes sessions with --resume.
    • acp: launches gemini --experimental-acp, keeps one long-lived transport, and streams session updates.
  • Hook bootstrap
    • Generates runtime Gemini system settings and injects BeforeTool safety hook with GEMINI_CLI_SYSTEM_SETTINGS_PATH.
  • State store
    • Persists owner binding, chat->session mapping, PIN metadata.
  • Interaction logger
    • Writes JSONL records and human-readable terminal logs for inbound/outbound/tool activity.

Startup sequence

portal
  |
  v
Load global config (~/.portal/config.json) + env overrides
  |
  v
Resolve project runtime paths (./.portal/*)
  |
  v
Load state.json -> PIN metadata / owner/session state
  |
  v
Generate Gemini hook settings (gemini-system-settings.portal.json)
  |
  v
Start HTTP health server (port fallback if in use)
  |
  v
Register Telegram commands + start poller
  |
  v
Ready (PIN shown, diagnostics streaming)

Startup flow

  1. portal optionally opens an interactive launcher UI (TTY only; can be disabled with --no-ui).
  2. portal resolves config from ~/.portal/config.json + env overrides.
  3. Runtime paths are prepared in project-local ./.portal/.
  4. PIN metadata is loaded; PIN is reused if still valid (30 min TTL).
  5. Gemini hook settings file is generated/updated.
  6. Telegram identity is checked (getMe), commands are registered.
  7. HTTP server starts on requested port or next available port.
  8. Poller starts, bot begins processing Telegram updates.

Message lifecycle

  1. Telegram update arrives through poller.
  2. Handler validates owner and PIN gate (/connect <PIN> if not connected).
  3. Handler applies per-chat backpressure:
    • 1 running request
    • up to 1 queued request
    • extra messages rejected with wait notice.
  4. Optional file intake:
    • document/photo downloaded to ./.portal/agent_sandbox/uploads
    • prompt is augmented with local file path.
  5. Prompt is wrapped with Portal system envelope (regular mode) and sent to Gemini CLI.
  6. Session ID from Gemini response/session is persisted for resume on next prompt.
  7. Markdown-like output is converted to Telegram HTML for proper client formatting.
  8. If response includes <portal_send_file>...</portal_send_file>, Portal uploads files/photos back to Telegram.

Request lifecycle (single message)

Telegram message/photo/doc
  |
  v
Handler: auth + PIN + owner check
  |
  v
Per-chat queue gate (1 running, 1 queued)
  |
  v
Optional file download -> ./agent_sandbox/uploads
  |
  v
Build Gemini prompt (system envelope or assistant mode)
  |
  v
Run Gemini backend:
  - headless: gemini -p ... --output-format json [--resume <session>]
  - acp: persistent ACP session prompt over --experimental-acp transport
  |
  v
Parse result/session_id
  |
  +--> parse <portal_send_file> directives --> upload photo/document
  |
  v
Format response for Telegram HTML and send
  |
  v
Persist updated session/state/logs

Session and state model

  • Chat session continuity:
    • Each Telegram chat keeps its own Gemini session_id.
    • /newsession clears current chat session only.
  • PIN and owner continuity:
    • PIN metadata (issued/expiry/failed attempts) is stored.
    • If server restarts within PIN TTL and owner matches, connection is auto-restored.
  • Owner binding:
    • First successful /connect binds owner user/chat if not already set.

Safety layers

  • User input guard:
    • Destructive command patterns are blocked before Gemini execution.
  • Gemini tool hook guard:
    • Runtime BeforeTool hook blocks destructive run_shell_command calls.
  • Assistant mode controls:
    • Requires GEMINI_API_KEY.
    • Requires Gemini auth selected type gemini-api-key.
    • Forces read-only policy + assistant system prompt.
  • Access controls:
    • Owner-only operation after binding.
    • 3 failed PIN attempts trigger shutdown.

Safety layering

User request
  |
  +--> Portal text guard (destructive intent patterns)
  |
  +--> Gemini execution
         |
         +--> Injected BeforeTool hook (blocks destructive run_shell_command)
         |
         +--> Assistant mode policy (read-only constraints when enabled)

Runtime files

  • Global config:
    • ~/.portal/config.json (telegram credentials, defaults, and last startup profile)
  • Project runtime directory:
    • ./.portal/state.json
    • ./.portal/logs/interactions.jsonl
    • ./.portal/logs/gemini-hooks.jsonl
    • ./.portal/gemini-system-settings.portal.json
    • ./.portal/agent_sandbox/

Environment overrides

  • PORTAL_MACHINE_ID=my-laptop (optional stable ID shown in status/conflict alerts)
  • PORTAL_MODE=regular|assistant
  • PORTAL_GEMINI_BACKEND=headless|acp (default headless)
  • PORTAL_ACP_LIVE_TRACE=1|0 (default 1, only used when backend is acp)
  • PORTAL_GEMINI_HOOKS_ENABLED=1|0 (default 1)
  • PORTAL_GEMINI_HOOKS_SETTINGS_PATH=.portal/gemini-system-settings.portal.json
  • PORTAL_GEMINI_HOOKS_LOG_FILE=.portal/logs/gemini-hooks.jsonl
  • PORTAL_FILE_UPLOADS_ENABLED=1|0
  • PORTAL_FILE_MAX_BYTES=10485760
  • PORTAL_FILE_ALLOWED_EXTENSIONS=.txt,.md,... (applies to non-image files)
  • PORTAL_FILE_UPLOADS_DIR=.portal/agent_sandbox/uploads