ai-cli-toolkit 0.2.0__py3-none-any.whl

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.
ai_cli/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """ai-cli: Unified AI CLI wrapper for Claude, Codex, Copilot, and Gemini."""
2
+
3
+ __version__ = "0.2.0"
ai_cli/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Entry point for python -m ai_cli."""
2
+
3
+ from ai_cli.main import main
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(main())
Binary file
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env zsh
2
+ set -euo pipefail
3
+
4
+ die() { print -u2 -- "Error: $*"; exit 1; }
5
+
6
+ usage() {
7
+ cat <<'USAGE'
8
+ remote-tty-wrapper -H user@host [-s session] [--ssh-opt "..."] <mode> [mode args]
9
+
10
+ Global:
11
+ -H, --host user@host required
12
+ -s, --session NAME tmux session name (default: sshwrap)
13
+ --ssh-opt "..." extra ssh option (repeatable)
14
+
15
+ Modes:
16
+ start [--init 'CMD'] ensure tmux session exists; optionally run CMD inside it
17
+ shell [--init 'CMD'] attach to tmux session; optionally run CMD inside it first
18
+ send CMD... send one command line into the tmux session
19
+ send -- 'CMD1' -- 'CMD2' send multiple command lines (separator is literal --)
20
+ close kill the tmux session
21
+
22
+ Examples:
23
+ remote-tty-wrapper -H example@192.168.1.117 start
24
+ remote-tty-wrapper -H example@192.168.1.117 shell
25
+ remote-tty-wrapper -H example@192.168.1.117 shell --init 'source ~/miniconda3/bin/activate; conda activate bot-refactor/conda'
26
+ remote-tty-wrapper -H example@192.168.1.117 send 'cd bot-refactor'
27
+ remote-tty-wrapper -H example@192.168.1.117 send -- 'cd bot-refactor' -- 'pwd' -- 'python -V'
28
+ USAGE
29
+ }
30
+
31
+ REMOTE_USER_HOST="${REMOTE_USER_HOST:-}"
32
+ SESSION="sshwrap"
33
+ MODE=""
34
+ INIT_CMD=""
35
+
36
+ typeset -a SSH_OPTS
37
+ SSH_OPTS=(-o PermitLocalCommand=no -o ServerAliveInterval=30 -o ServerAliveCountMax=3)
38
+
39
+ # ---- parse globals ----
40
+ while (( $# )); do
41
+ case "$1" in
42
+ -H|--host) shift; (( $# )) || die "Missing --host"; REMOTE_USER_HOST="$1"; shift;;
43
+ -s|--session) shift; (( $# )) || die "Missing --session"; SESSION="$1"; shift;;
44
+ --ssh-opt) shift; (( $# )) || die "Missing --ssh-opt"; SSH_OPTS+=("$1"); shift;;
45
+ -h|--help) usage; exit 0;;
46
+ start|shell|send|close) MODE="$1"; shift; break;;
47
+ *) die "Unknown option or missing mode: $1 (use --help)";;
48
+ esac
49
+ done
50
+
51
+ [[ -n "$REMOTE_USER_HOST" ]] || die "No host set (-H user@host)"
52
+ [[ -n "$MODE" ]] || die "No mode (use --help)"
53
+
54
+ # ---- helper: run a shell snippet on remote with argv preserved ----
55
+ ssh_sh() {
56
+ ssh "${SSH_OPTS[@]}" "$REMOTE_USER_HOST" sh -s -- "$@"
57
+ }
58
+
59
+ ensure_tmux_session() {
60
+ ssh_sh "$SESSION" <<'SH'
61
+ sess="$1"
62
+ command -v tmux >/dev/null 2>&1 || { echo "tmux not found on remote" >&2; exit 127; }
63
+ tmux has-session -t "$sess" 2>/dev/null || tmux new-session -d -s "$sess"
64
+ SH
65
+ }
66
+
67
+ tmux_send_line() {
68
+ ssh_sh "$SESSION" "$1" <<'SH'
69
+ sess="$1"
70
+ line="$2"
71
+ tmux has-session -t "$sess" 2>/dev/null || tmux new-session -d -s "$sess"
72
+ tmux send-keys -t "$sess" -l -- "$line"
73
+ tmux send-keys -t "$sess" Enter
74
+ SH
75
+ }
76
+ typeset -a REMAINING_ARGS
77
+ parse_init() {
78
+ INIT_CMD=""
79
+ REMAINING_ARGS=()
80
+ if (( $# >= 2 )) && [[ "$1" == "--init" ]]; then
81
+ INIT_CMD="$2"
82
+ shift 2
83
+ fi
84
+ REMAINING_ARGS=("$@")
85
+ }
86
+
87
+ do_start() {
88
+ parse_init "$@"
89
+ (( ${#REMAINING_ARGS[@]} == 0 )) || die "start takes no extra args (use --init '...')"
90
+
91
+ ensure_tmux_session
92
+ [[ -n "$INIT_CMD" ]] && tmux_send_line "$INIT_CMD"
93
+ }
94
+
95
+ do_shell() {
96
+ parse_init "$@"
97
+ (( ${#REMAINING_ARGS[@]} == 0 )) || die "shell takes no extra args (use --init '...')"
98
+
99
+ [[ -r /dev/tty && -w /dev/tty ]] || die "shell requires a real terminal (/dev/tty unavailable). Use start/send."
100
+
101
+ local remote_cmd
102
+ remote_cmd="tmux has-session -t $(printf %q "$SESSION") 2>/dev/null || tmux new-session -d -s $(printf %q "$SESSION")"
103
+ if [[ -n "$INIT_CMD" ]]; then
104
+ remote_cmd="$remote_cmd; tmux send-keys -t $(printf %q "$SESSION") -l -- $(printf %q "$INIT_CMD"); tmux send-keys -t $(printf %q "$SESSION") Enter"
105
+ fi
106
+ remote_cmd="$remote_cmd; tmux attach -t $(printf %q "$SESSION")"
107
+
108
+ exec </dev/tty >/dev/tty 2>&1 \
109
+ ssh "${SSH_OPTS[@]}" -o RequestTTY=force "$REMOTE_USER_HOST" "$remote_cmd"
110
+ }
111
+
112
+ do_send() {
113
+ ensure_tmux_session
114
+ (( $# > 0 )) || die "send requires a command (or: send -- 'CMD1' -- 'CMD2' ...)"
115
+
116
+ if [[ "$1" == "--" ]]; then
117
+ shift
118
+ local cur=""
119
+ while (( $# )); do
120
+ if [[ "$1" == "--" ]]; then
121
+ [[ -n "$cur" ]] && tmux_send_line "$cur"
122
+ cur=""
123
+ shift
124
+ continue
125
+ fi
126
+ if [[ -z "$cur" ]]; then
127
+ cur="$1"
128
+ else
129
+ cur="$cur $1"
130
+ fi
131
+ shift
132
+ done
133
+ [[ -n "$cur" ]] && tmux_send_line "$cur"
134
+ else
135
+ tmux_send_line "$*"
136
+ fi
137
+ }
138
+
139
+ do_close() {
140
+ (( $# == 0 )) || die "close takes no arguments"
141
+ ssh_sh "$SESSION" <<'SH'
142
+ sess="$1"
143
+ command -v tmux >/dev/null 2>&1 || exit 0
144
+ tmux kill-session -t "$sess" 2>/dev/null || true
145
+ SH
146
+ }
147
+
148
+ case "$MODE" in
149
+ start) do_start "$@";;
150
+ shell) do_shell "$@";;
151
+ send) do_send "$@";;
152
+ close) do_close "$@";;
153
+ esac
ai_cli/ca.py ADDED
@@ -0,0 +1,175 @@
1
+ """CA certificate bootstrap and optional trust-store installation.
2
+
3
+ Handles generating mitmproxy CA certificates on first run, and optionally
4
+ installing them into the macOS or Linux system trust store.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import random
10
+ import shutil
11
+ import subprocess
12
+ import time
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from ai_cli.log import append_log, fmt_cmd
17
+
18
+ DEFAULT_CA_PATH = "~/.mitmproxy/mitmproxy-ca-cert.pem"
19
+
20
+
21
+ def _stop_process(proc: subprocess.Popen[Any]) -> None:
22
+ """Terminate a subprocess, escalating to kill after timeout."""
23
+ if proc.poll() is not None:
24
+ return
25
+ proc.terminate()
26
+ try:
27
+ proc.wait(timeout=3)
28
+ except subprocess.TimeoutExpired:
29
+ proc.kill()
30
+
31
+
32
+ def bootstrap_ca_cert(
33
+ ca_path: Path,
34
+ mitmdump_bin: str,
35
+ log_path: Path,
36
+ ) -> bool:
37
+ """Ensure mitmproxy CA cert exists at *ca_path*.
38
+
39
+ If missing, runs a short-lived mitmdump process to generate CA material.
40
+ Returns True if cert is available after bootstrap.
41
+ """
42
+ if ca_path.is_file():
43
+ return True
44
+
45
+ confdir = ca_path.parent
46
+ generated_path = confdir / "mitmproxy-ca-cert.pem"
47
+ try:
48
+ confdir.mkdir(parents=True, exist_ok=True)
49
+ except OSError as exc:
50
+ append_log(log_path, f"Failed to create CA directory {confdir}: {exc}")
51
+ return False
52
+
53
+ append_log(log_path, f"CA cert missing at {ca_path}. Bootstrapping with mitmdump.")
54
+
55
+ for _ in range(3):
56
+ port = random.randint(39000, 49000)
57
+ bootstrap_cmd = [
58
+ mitmdump_bin,
59
+ "--quiet",
60
+ "--set",
61
+ f"confdir={confdir}",
62
+ "--listen-host",
63
+ "127.0.0.1",
64
+ "-p",
65
+ str(port),
66
+ ]
67
+ append_log(log_path, f"CA bootstrap command: {fmt_cmd(bootstrap_cmd)}")
68
+
69
+ try:
70
+ bootstrap_proc = subprocess.Popen(
71
+ bootstrap_cmd,
72
+ stdin=subprocess.DEVNULL,
73
+ stdout=subprocess.DEVNULL,
74
+ stderr=subprocess.DEVNULL,
75
+ start_new_session=True,
76
+ )
77
+ except OSError as exc:
78
+ append_log(log_path, f"Failed to start bootstrap mitmdump: {exc}")
79
+ continue
80
+
81
+ time.sleep(0.6)
82
+ _stop_process(bootstrap_proc)
83
+ if generated_path.is_file() or ca_path.is_file():
84
+ break
85
+
86
+ if generated_path.is_file() and generated_path != ca_path:
87
+ try:
88
+ shutil.copy2(generated_path, ca_path)
89
+ except OSError as exc:
90
+ append_log(
91
+ log_path, f"Failed to copy generated CA cert to {ca_path}: {exc}"
92
+ )
93
+
94
+ if ca_path.is_file():
95
+ append_log(log_path, f"CA cert available at {ca_path}.")
96
+ return True
97
+
98
+ append_log(log_path, f"CA bootstrap failed. Expected cert at {ca_path}.")
99
+ return False
100
+
101
+
102
+ def install_ca_macos(ca_path: Path, log_path: Path) -> bool:
103
+ """Install CA cert into the macOS system keychain (requires sudo)."""
104
+ if not ca_path.is_file():
105
+ append_log(log_path, f"CA cert not found at {ca_path}")
106
+ return False
107
+
108
+ cmd = [
109
+ "sudo",
110
+ "security",
111
+ "add-trusted-cert",
112
+ "-d",
113
+ "-r",
114
+ "trustRoot",
115
+ "-k",
116
+ "/Library/Keychains/System.keychain",
117
+ str(ca_path),
118
+ ]
119
+ append_log(log_path, f"Installing CA to macOS keychain: {fmt_cmd(cmd)}")
120
+ try:
121
+ result = subprocess.run(cmd, check=False, capture_output=True, text=True)
122
+ if result.returncode == 0:
123
+ append_log(log_path, "CA cert installed to macOS system keychain.")
124
+ return True
125
+ append_log(
126
+ log_path,
127
+ f"CA install failed (exit={result.returncode}): {result.stderr.strip()}",
128
+ )
129
+ except OSError as exc:
130
+ append_log(log_path, f"CA install failed: {exc}")
131
+ return False
132
+
133
+
134
+ def install_ca_linux(ca_path: Path, log_path: Path) -> bool:
135
+ """Install CA cert into the Linux system trust store."""
136
+ if not ca_path.is_file():
137
+ append_log(log_path, f"CA cert not found at {ca_path}")
138
+ return False
139
+
140
+ # Try Debian/Ubuntu style
141
+ dest_dir = Path("/usr/local/share/ca-certificates")
142
+ update_cmd = "update-ca-certificates"
143
+ if not dest_dir.exists():
144
+ # Try RHEL/Fedora style
145
+ dest_dir = Path("/etc/pki/ca-trust/source/anchors")
146
+ update_cmd = "update-ca-trust"
147
+
148
+ if not dest_dir.exists():
149
+ append_log(log_path, "No known CA trust directory found on this system.")
150
+ return False
151
+
152
+ dest = dest_dir / "mitmproxy-ca-cert.crt"
153
+ try:
154
+ result = subprocess.run(
155
+ ["sudo", "cp", str(ca_path), str(dest)],
156
+ check=False,
157
+ capture_output=True,
158
+ text=True,
159
+ )
160
+ if result.returncode != 0:
161
+ append_log(log_path, f"Failed to copy CA cert: {result.stderr.strip()}")
162
+ return False
163
+ result = subprocess.run(
164
+ ["sudo", update_cmd],
165
+ check=False,
166
+ capture_output=True,
167
+ text=True,
168
+ )
169
+ if result.returncode == 0:
170
+ append_log(log_path, f"CA cert installed via {update_cmd}.")
171
+ return True
172
+ append_log(log_path, f"{update_cmd} failed: {result.stderr.strip()}")
173
+ except OSError as exc:
174
+ append_log(log_path, f"CA install failed: {exc}")
175
+ return False