omnilane 0.7.0
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.
- package/CHANGELOG.md +209 -0
- package/LICENSE +21 -0
- package/README.ja.md +345 -0
- package/README.ko.md +337 -0
- package/README.md +368 -0
- package/README.zh-CN.md +317 -0
- package/README.zh-TW.md +327 -0
- package/SECURITY.md +37 -0
- package/VERSION +1 -0
- package/bin/omnilane +72 -0
- package/completions/_omnilane +114 -0
- package/completions/omnilane.bash +123 -0
- package/local.sh.example +37 -0
- package/package.json +54 -0
- package/routing.local.yaml.example +41 -0
- package/routing.yaml +34 -0
- package/scripts/configure.sh +134 -0
- package/scripts/dispatch.sh +705 -0
- package/scripts/doctor.sh +166 -0
- package/scripts/jobs.sh +802 -0
- package/scripts/lib/common.sh +247 -0
- package/scripts/lib/i18n.sh +104 -0
- package/scripts/lib/job-timeout.pl +109 -0
- package/scripts/lib/job-worker.sh +23 -0
- package/scripts/release-audit.sh +314 -0
- package/scripts/runners/run-claude.sh +37 -0
- package/scripts/runners/run-codex.sh +51 -0
- package/scripts/runners/run-exec.sh +33 -0
- package/scripts/runners/run-gemini.sh +60 -0
- package/scripts/runners/run-grok.sh +56 -0
- package/scripts/runners/run-vote.sh +119 -0
- package/scripts/ui.py +1236 -0
- package/ui/app.js +1106 -0
- package/ui/index.html +192 -0
- package/ui/styles.css +1023 -0
package/SECURITY.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
| Version | Security fixes |
|
|
6
|
+
|---|---|
|
|
7
|
+
| 0.5.x | Supported |
|
|
8
|
+
| 0.4.x and older | Upgrade to the latest release first |
|
|
9
|
+
|
|
10
|
+
## Reporting a vulnerability
|
|
11
|
+
|
|
12
|
+
As of 2026-07-15, this repository does not have GitHub private vulnerability
|
|
13
|
+
reporting enabled. Do not publish exploit details, tokens, prompts, model
|
|
14
|
+
outputs, local paths, or other sensitive evidence in a public issue.
|
|
15
|
+
|
|
16
|
+
Open a minimal [security coordination issue](https://github.com/Seraphim0916/omnilane/issues/new)
|
|
17
|
+
that contains only:
|
|
18
|
+
|
|
19
|
+
- the affected released version;
|
|
20
|
+
- the broad component, such as installer, dispatcher, jobs CLI, or Live UI;
|
|
21
|
+
- a request for a private contact channel.
|
|
22
|
+
|
|
23
|
+
Wait for the maintainer to establish a private channel before sharing the
|
|
24
|
+
reproduction or impact details. Maintainers should enable GitHub private
|
|
25
|
+
vulnerability reporting when a stable private disclosure workflow is ready.
|
|
26
|
+
|
|
27
|
+
Useful reports include a minimal reproduction, expected and actual behavior,
|
|
28
|
+
impact, platform, and a proposed mitigation if available. Remove credentials,
|
|
29
|
+
cookies, provider responses, and proprietary source code.
|
|
30
|
+
|
|
31
|
+
## Security-relevant scope
|
|
32
|
+
|
|
33
|
+
Examples include shell or routing injection, path traversal, unsafe installer
|
|
34
|
+
replacement, cross-user prompt or result disclosure, loopback authentication
|
|
35
|
+
bypass, unsafe symlink handling, and worker processes that survive a declared
|
|
36
|
+
timeout. Model answer quality and upstream provider availability are normally
|
|
37
|
+
not security vulnerabilities in Omnilane itself.
|
package/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.7.0
|
package/bin/omnilane
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
# omnilane — global entrypoint so commands work from any directory.
|
|
4
|
+
|
|
5
|
+
SELF="${BASH_SOURCE[0]}"
|
|
6
|
+
# A relative readlink target (how npm links global bins) resolves against the
|
|
7
|
+
# symlink's own directory, not the caller's cwd.
|
|
8
|
+
while [[ -L "$SELF" ]]; do
|
|
9
|
+
TARGET="$(readlink "$SELF")"
|
|
10
|
+
case "$TARGET" in
|
|
11
|
+
/*) SELF="$TARGET" ;;
|
|
12
|
+
*) SELF="$(dirname "$SELF")/$TARGET" ;;
|
|
13
|
+
esac
|
|
14
|
+
done
|
|
15
|
+
REPO="$(cd "$(dirname "$SELF")/.." && pwd)"
|
|
16
|
+
|
|
17
|
+
cmd="${1:-help}"; shift || true
|
|
18
|
+
case "$cmd" in
|
|
19
|
+
version|--version|-V)
|
|
20
|
+
[[ -r "$REPO/VERSION" ]] || {
|
|
21
|
+
echo "omnilane: VERSION file is unavailable" >&2
|
|
22
|
+
exit 1
|
|
23
|
+
}
|
|
24
|
+
version="$(<"$REPO/VERSION")"
|
|
25
|
+
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || {
|
|
26
|
+
echo "omnilane: invalid VERSION file" >&2
|
|
27
|
+
exit 1
|
|
28
|
+
}
|
|
29
|
+
printf 'omnilane %s\n' "$version"
|
|
30
|
+
;;
|
|
31
|
+
list) exec bash "$REPO/scripts/dispatch.sh" --list ;;
|
|
32
|
+
route|dispatch) exec bash "$REPO/scripts/dispatch.sh" "$@" ;;
|
|
33
|
+
jobs) exec bash "$REPO/scripts/jobs.sh" "$@" ;;
|
|
34
|
+
completion)
|
|
35
|
+
[[ $# -eq 1 ]] || {
|
|
36
|
+
echo "usage: omnilane completion bash|zsh" >&2
|
|
37
|
+
exit 2
|
|
38
|
+
}
|
|
39
|
+
case "$1" in
|
|
40
|
+
bash) cat "$REPO/completions/omnilane.bash" ;;
|
|
41
|
+
zsh) cat "$REPO/completions/_omnilane" ;;
|
|
42
|
+
*) echo "usage: omnilane completion bash|zsh" >&2; exit 2 ;;
|
|
43
|
+
esac
|
|
44
|
+
;;
|
|
45
|
+
release-audit) exec bash "$REPO/scripts/release-audit.sh" "$@" ;;
|
|
46
|
+
doctor) exec bash "$REPO/scripts/doctor.sh" "$@" ;;
|
|
47
|
+
ui)
|
|
48
|
+
command -v python3 >/dev/null 2>&1 || {
|
|
49
|
+
echo "omnilane: ui requires Python 3.9 or newer" >&2
|
|
50
|
+
exit 1
|
|
51
|
+
}
|
|
52
|
+
exec python3 "$REPO/scripts/ui.py" "$@"
|
|
53
|
+
;;
|
|
54
|
+
configure) exec bash "$REPO/scripts/configure.sh" "$@" ;;
|
|
55
|
+
help|--help|-h)
|
|
56
|
+
cat <<'EOF'
|
|
57
|
+
omnilane — one routing table, every harness
|
|
58
|
+
|
|
59
|
+
omnilane version installed release version
|
|
60
|
+
omnilane list effective routing table
|
|
61
|
+
omnilane route [--vendor V] [flags] LANE "TASK"
|
|
62
|
+
dispatch or consult a model
|
|
63
|
+
omnilane jobs [--json] COMMAND [ARGS] list, status, result, stats, wait, or audit
|
|
64
|
+
omnilane completion bash|zsh print a shell completion script
|
|
65
|
+
omnilane release-audit [flags] offline, read-only release gate
|
|
66
|
+
omnilane doctor [--json] read-only local health report
|
|
67
|
+
omnilane ui start|status|url|stop
|
|
68
|
+
omnilane configure interactive lane menu
|
|
69
|
+
EOF
|
|
70
|
+
;;
|
|
71
|
+
*) echo "omnilane: unknown command '$cmd' (try: omnilane help)" >&2; exit 2 ;;
|
|
72
|
+
esac
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#compdef omnilane
|
|
2
|
+
# Zsh completion for omnilane. Reads bounded names only; never dispatches.
|
|
3
|
+
|
|
4
|
+
_omnilane_repo() {
|
|
5
|
+
if [[ -n "${OMNILANE_COMPLETION_REPO:-}" && -r "$OMNILANE_COMPLETION_REPO/routing.yaml" ]]; then
|
|
6
|
+
print -r -- "$OMNILANE_COMPLETION_REPO"
|
|
7
|
+
return 0
|
|
8
|
+
fi
|
|
9
|
+
local exe target
|
|
10
|
+
exe="${commands[omnilane]:-}" || return 1
|
|
11
|
+
while [[ -L "$exe" ]]; do
|
|
12
|
+
target="$(readlink "$exe")" || return 1
|
|
13
|
+
if [[ "$target" = /* ]]; then exe="$target"; else exe="${exe:h}/$target"; fi
|
|
14
|
+
done
|
|
15
|
+
(cd "${exe:h}/.." 2>/dev/null && pwd -P)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
_omnilane_lanes() {
|
|
19
|
+
local repo home file
|
|
20
|
+
local -a files
|
|
21
|
+
repo="$(_omnilane_repo)" || return 0
|
|
22
|
+
home="${OMNILANE_HOME:-$HOME/.omnilane}"
|
|
23
|
+
files=()
|
|
24
|
+
for file in "$home/routing.local.yaml" "$repo/routing.yaml"; do
|
|
25
|
+
[[ -f "$file" && ! -L "$file" ]] && files+=("$file")
|
|
26
|
+
done
|
|
27
|
+
(( ${#files} )) || return 0
|
|
28
|
+
awk '
|
|
29
|
+
/^[a-z][a-z0-9-]*:[[:space:]]/ {
|
|
30
|
+
lane=$1; sub(/:$/, "", lane)
|
|
31
|
+
if (!seen[lane]++) print lane
|
|
32
|
+
}
|
|
33
|
+
' "${files[@]}"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_omnilane_job_ids() {
|
|
37
|
+
local home root dir id count=0
|
|
38
|
+
home="${OMNILANE_HOME:-$HOME/.omnilane}"
|
|
39
|
+
root="$home/jobs"
|
|
40
|
+
[[ -d "$root" && ! -L "$root" ]] || return 0
|
|
41
|
+
for dir in "$root"/*(N/); do
|
|
42
|
+
[[ -L "$dir" ]] && continue
|
|
43
|
+
id="${dir:t}"
|
|
44
|
+
if [[ "$id" =~ '^[0-9]{8}-[0-9]{6}-[0-9]+-[0-9]+$' ]]; then
|
|
45
|
+
print -r -- "$id"
|
|
46
|
+
(( count += 1 ))
|
|
47
|
+
(( count < 1000 )) || break
|
|
48
|
+
fi
|
|
49
|
+
done
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_omnilane() {
|
|
53
|
+
local command="${words[2]:-}" sub sub_index=3
|
|
54
|
+
local -a lanes ids
|
|
55
|
+
if (( CURRENT == 2 )); then
|
|
56
|
+
_values 'command' version list route dispatch jobs doctor release-audit ui configure completion help
|
|
57
|
+
return
|
|
58
|
+
fi
|
|
59
|
+
case "$command" in
|
|
60
|
+
route|dispatch)
|
|
61
|
+
lanes=("${(@f)$(_omnilane_lanes)}")
|
|
62
|
+
_arguments \
|
|
63
|
+
'--background[run in background]' \
|
|
64
|
+
'--dry-run[resolve and print the plan without side effects]' \
|
|
65
|
+
'--mode[execution mode]:mode:(advise work)' \
|
|
66
|
+
'--workdir[target directory]:directory:_directories' \
|
|
67
|
+
'--vendor[vendor override]:vendor:(codex claude grok gemini exec vote)' \
|
|
68
|
+
'--model[model override]:model:' \
|
|
69
|
+
'--effort[reasoning effort]:effort:(low medium high xhigh max)' \
|
|
70
|
+
'--timeout[per-call seconds]:seconds:' \
|
|
71
|
+
'--job-timeout[whole-job seconds]:seconds:' \
|
|
72
|
+
"1:lane:($lanes)" '2:task:'
|
|
73
|
+
;;
|
|
74
|
+
jobs)
|
|
75
|
+
sub="${words[3]:-}"
|
|
76
|
+
if [[ "$sub" == "--json" ]]; then
|
|
77
|
+
sub_index=4
|
|
78
|
+
sub="${words[4]:-}"
|
|
79
|
+
fi
|
|
80
|
+
if (( CURRENT == sub_index )); then
|
|
81
|
+
_values 'job command' list status result tail retry stats wait audit prune help
|
|
82
|
+
elif (( CURRENT == sub_index + 1 )) &&
|
|
83
|
+
[[ "$sub" == status || "$sub" == result || "$sub" == wait ||
|
|
84
|
+
"$sub" == tail || "$sub" == retry ]]; then
|
|
85
|
+
ids=("${(@f)$(_omnilane_job_ids)}")
|
|
86
|
+
_values 'job id' "$ids[@]"
|
|
87
|
+
elif [[ "$sub" == list || "$sub" == status || "$sub" == result ]]; then
|
|
88
|
+
_values 'option' --json
|
|
89
|
+
elif [[ "$sub" == tail ]]; then
|
|
90
|
+
_arguments '--lines[output lines to show]:count:'
|
|
91
|
+
elif [[ "$sub" == retry ]]; then
|
|
92
|
+
_arguments '--background[re-dispatch in the background]'
|
|
93
|
+
elif [[ "$sub" == wait ]]; then
|
|
94
|
+
_arguments '--timeout[maximum wait seconds]:seconds:'
|
|
95
|
+
elif [[ "$sub" == stats || "$sub" == audit ]]; then
|
|
96
|
+
_arguments '--last[maximum jobs]:count:' '--json[versioned metadata output]'
|
|
97
|
+
elif [[ "$sub" == prune ]]; then
|
|
98
|
+
_arguments '--keep[completed jobs to retain]:count:' \
|
|
99
|
+
'--older-than[completed jobs older than N days]:days:' \
|
|
100
|
+
'--apply[delete eligible jobs]'
|
|
101
|
+
fi
|
|
102
|
+
;;
|
|
103
|
+
doctor) _values 'option' --json ;;
|
|
104
|
+
release-audit)
|
|
105
|
+
_arguments '--target[release version]:version:' '--allow-dirty[permit a dirty worktree]' \
|
|
106
|
+
'--require-tag[require an annotated tag at HEAD]' '--manifest[include tracked inventory]' \
|
|
107
|
+
'--json[versioned JSON output]'
|
|
108
|
+
;;
|
|
109
|
+
ui) _values 'ui command' start status url stop ;;
|
|
110
|
+
completion) _values 'shell' bash zsh ;;
|
|
111
|
+
esac
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
(( $+functions[compdef] )) && compdef _omnilane omnilane
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Bash completion for omnilane. This file reads routing/job names only; it never
|
|
2
|
+
# invokes dispatch, provider CLIs, or the executable machine-local overlay.
|
|
3
|
+
|
|
4
|
+
_omnilane_repo() {
|
|
5
|
+
if [[ -n "${OMNILANE_COMPLETION_REPO:-}" && -r "$OMNILANE_COMPLETION_REPO/routing.yaml" ]]; then
|
|
6
|
+
printf '%s\n' "$OMNILANE_COMPLETION_REPO"
|
|
7
|
+
return 0
|
|
8
|
+
fi
|
|
9
|
+
local exe target
|
|
10
|
+
exe="$(command -v omnilane 2>/dev/null)" || return 1
|
|
11
|
+
while [[ -L "$exe" ]]; do
|
|
12
|
+
target="$(readlink "$exe")" || return 1
|
|
13
|
+
case "$target" in
|
|
14
|
+
/*) exe="$target" ;;
|
|
15
|
+
*) exe="$(dirname "$exe")/$target" ;;
|
|
16
|
+
esac
|
|
17
|
+
done
|
|
18
|
+
(cd "$(dirname "$exe")/.." 2>/dev/null && pwd -P)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_omnilane_lanes() {
|
|
22
|
+
local repo home file
|
|
23
|
+
local files=()
|
|
24
|
+
repo="$(_omnilane_repo)" || return 0
|
|
25
|
+
home="${OMNILANE_HOME:-$HOME/.omnilane}"
|
|
26
|
+
for file in "$home/routing.local.yaml" "$repo/routing.yaml"; do
|
|
27
|
+
[[ -f "$file" && ! -L "$file" ]] && files+=("$file")
|
|
28
|
+
done
|
|
29
|
+
[[ ${#files[@]} -gt 0 ]] || return 0
|
|
30
|
+
awk '
|
|
31
|
+
/^[a-z][a-z0-9-]*:[[:space:]]/ {
|
|
32
|
+
lane=$1; sub(/:$/, "", lane)
|
|
33
|
+
if (!seen[lane]++) print lane
|
|
34
|
+
}
|
|
35
|
+
' "${files[@]}"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_omnilane_job_ids() {
|
|
39
|
+
local home root dir id count=0
|
|
40
|
+
home="${OMNILANE_HOME:-$HOME/.omnilane}"
|
|
41
|
+
root="$home/jobs"
|
|
42
|
+
[[ -d "$root" && ! -L "$root" ]] || return 0
|
|
43
|
+
for dir in "$root"/*; do
|
|
44
|
+
[[ -d "$dir" && ! -L "$dir" ]] || continue
|
|
45
|
+
id="${dir##*/}"
|
|
46
|
+
if [[ "$id" =~ ^[0-9]{8}-[0-9]{6}-[0-9]+-[0-9]+$ ]]; then
|
|
47
|
+
printf '%s\n' "$id"
|
|
48
|
+
((count += 1))
|
|
49
|
+
[[ "$count" -lt 1000 ]] || break
|
|
50
|
+
fi
|
|
51
|
+
done
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_omnilane() {
|
|
55
|
+
local cur prev command sub words sub_index reply_line
|
|
56
|
+
COMPREPLY=()
|
|
57
|
+
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
58
|
+
prev="${COMP_WORDS[COMP_CWORD-1]:-}"
|
|
59
|
+
command="${COMP_WORDS[1]:-}"
|
|
60
|
+
if [[ "$COMP_CWORD" -eq 1 ]]; then
|
|
61
|
+
words="version list route dispatch jobs doctor release-audit ui configure completion help"
|
|
62
|
+
else
|
|
63
|
+
case "$command" in
|
|
64
|
+
route|dispatch)
|
|
65
|
+
case "$prev" in
|
|
66
|
+
--mode) words="advise work" ;;
|
|
67
|
+
--vendor) words="codex claude grok gemini exec vote" ;;
|
|
68
|
+
--effort) words="low medium high xhigh max" ;;
|
|
69
|
+
--workdir)
|
|
70
|
+
COMPREPLY=()
|
|
71
|
+
while IFS= read -r reply_line; do
|
|
72
|
+
COMPREPLY+=("$reply_line")
|
|
73
|
+
done < <(compgen -d -- "$cur")
|
|
74
|
+
return ;;
|
|
75
|
+
--model|--timeout|--job-timeout) return ;;
|
|
76
|
+
*) words="--background --dry-run --help --mode --workdir --vendor --model --effort --timeout --job-timeout $(_omnilane_lanes)" ;;
|
|
77
|
+
esac
|
|
78
|
+
;;
|
|
79
|
+
jobs)
|
|
80
|
+
sub="${COMP_WORDS[2]:-}"
|
|
81
|
+
sub_index=2
|
|
82
|
+
if [[ "$sub" == "--json" ]]; then
|
|
83
|
+
sub_index=3
|
|
84
|
+
sub="${COMP_WORDS[3]:-}"
|
|
85
|
+
fi
|
|
86
|
+
if [[ "$COMP_CWORD" -eq "$sub_index" ]]; then
|
|
87
|
+
words="list status result tail retry stats wait audit prune help"
|
|
88
|
+
elif [[ "$COMP_CWORD" -eq $((sub_index + 1)) &&
|
|
89
|
+
( "$sub" == status || "$sub" == result || "$sub" == wait ||
|
|
90
|
+
"$sub" == tail || "$sub" == retry ) ]]; then
|
|
91
|
+
words="$(_omnilane_job_ids)"
|
|
92
|
+
elif [[ "$sub" == list ]]; then
|
|
93
|
+
words="--json"
|
|
94
|
+
elif [[ "$sub" == status || "$sub" == result ]]; then
|
|
95
|
+
words="--json"
|
|
96
|
+
elif [[ "$sub" == tail ]]; then
|
|
97
|
+
words="--lines"
|
|
98
|
+
elif [[ "$sub" == retry ]]; then
|
|
99
|
+
words="--background"
|
|
100
|
+
elif [[ "$sub" == wait ]]; then
|
|
101
|
+
words="--timeout"
|
|
102
|
+
elif [[ "$sub" == stats || "$sub" == audit ]]; then
|
|
103
|
+
words="--last --json"
|
|
104
|
+
elif [[ "$sub" == prune ]]; then
|
|
105
|
+
words="--keep --older-than --apply"
|
|
106
|
+
else
|
|
107
|
+
return
|
|
108
|
+
fi
|
|
109
|
+
;;
|
|
110
|
+
doctor) words="--json" ;;
|
|
111
|
+
release-audit) words="--target --allow-dirty --require-tag --manifest --json" ;;
|
|
112
|
+
ui) words="start status url stop" ;;
|
|
113
|
+
completion) words="bash zsh" ;;
|
|
114
|
+
*) return ;;
|
|
115
|
+
esac
|
|
116
|
+
fi
|
|
117
|
+
COMPREPLY=()
|
|
118
|
+
while IFS= read -r reply_line; do
|
|
119
|
+
COMPREPLY+=("$reply_line")
|
|
120
|
+
done < <(compgen -W "$words" -- "$cur")
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
complete -F _omnilane omnilane
|
package/local.sh.example
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copy to ~/.omnilane/local.sh — sourced by every runner. Never committed.
|
|
2
|
+
# Use it for machine-specific binaries, proxies, and auth. Examples:
|
|
3
|
+
|
|
4
|
+
# Absolute paths when the CLIs are not on the runners' PATH.
|
|
5
|
+
# Keep the ${VAR:-...} form so per-call env overrides still win:
|
|
6
|
+
# CLAUDE_BIN="${CLAUDE_BIN:-/opt/homebrew/bin/claude}"
|
|
7
|
+
# CODEX_BIN="${CODEX_BIN:-$HOME/.local/bin/codex}"
|
|
8
|
+
# GROK_BIN="${GROK_BIN:-/usr/local/bin/grok}"
|
|
9
|
+
# AGY_BIN="${AGY_BIN:-/usr/local/bin/agy}"
|
|
10
|
+
|
|
11
|
+
# Route a vendor CLI through a local proxy:
|
|
12
|
+
# export ANTHROPIC_BASE_URL=http://127.0.0.1:8080
|
|
13
|
+
|
|
14
|
+
# Watchdog cap for EACH CLI invocation (seconds), not a whole-job budget —
|
|
15
|
+
# a retrying vendor (grok) or the vote panel makes several calls. Global default:
|
|
16
|
+
# export OMNILANE_TIMEOUT=600
|
|
17
|
+
# Per-lane override — lane upper-cased with "-" turned into "_". A one-off
|
|
18
|
+
# `--timeout SEC` on the dispatch call still wins over both of these:
|
|
19
|
+
# export OMNILANE_TIMEOUT_TRIAGE=600
|
|
20
|
+
# export OMNILANE_TIMEOUT_HARD_JUDGMENT=1200
|
|
21
|
+
# export OMNILANE_TIMEOUT_LONG_CONTEXT=1800
|
|
22
|
+
|
|
23
|
+
# Grok retries an empty response; keep the explicit cost bound between 1 and 20.
|
|
24
|
+
# export OMNILANE_GROK_MAX_ATTEMPTS=5
|
|
25
|
+
|
|
26
|
+
# Optional whole-job fuse: lock wait + every retry/voter/round share this cap.
|
|
27
|
+
# Disabled by default, except non-Git Codex work automatically reuses its
|
|
28
|
+
# resolved per-call timeout (up to 999999999 seconds). A one-off
|
|
29
|
+
# `--job-timeout SEC` wins over these values.
|
|
30
|
+
# The automatic exception warns and falls back to the existing per-call
|
|
31
|
+
# watchdog path if the bundled Perl supervisor is unavailable; it never makes
|
|
32
|
+
# non-Git work Git-only.
|
|
33
|
+
# For a deep full-repository audit, 2–4 hours is a starting range, not a default:
|
|
34
|
+
# export OMNILANE_JOB_TIMEOUT=10800
|
|
35
|
+
# export OMNILANE_JOB_TIMEOUT_HARD_JUDGMENT=14400
|
|
36
|
+
# Pair it with a per-call guard such as:
|
|
37
|
+
# export OMNILANE_TIMEOUT_HARD_JUDGMENT=1800
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "omnilane",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "One routing table, every harness — classify subtasks into lanes and dispatch each lane to the best vendor's agentic CLI (Codex, Claude, Gemini, Grok) using your existing subscription logins.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"omnilane": "bin/omnilane"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"scripts/",
|
|
11
|
+
"!scripts/__pycache__",
|
|
12
|
+
"!**/*.pyc",
|
|
13
|
+
"completions/",
|
|
14
|
+
"ui/",
|
|
15
|
+
"routing.yaml",
|
|
16
|
+
"routing.local.yaml.example",
|
|
17
|
+
"local.sh.example",
|
|
18
|
+
"VERSION",
|
|
19
|
+
"CHANGELOG.md",
|
|
20
|
+
"README.md",
|
|
21
|
+
"README.zh-TW.md",
|
|
22
|
+
"README.zh-CN.md",
|
|
23
|
+
"README.ja.md",
|
|
24
|
+
"README.ko.md",
|
|
25
|
+
"SECURITY.md"
|
|
26
|
+
],
|
|
27
|
+
"os": [
|
|
28
|
+
"darwin",
|
|
29
|
+
"linux"
|
|
30
|
+
],
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/Seraphim0916/omnilane.git"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/Seraphim0916/omnilane#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/Seraphim0916/omnilane/issues"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"agentic",
|
|
41
|
+
"cli",
|
|
42
|
+
"llm",
|
|
43
|
+
"routing",
|
|
44
|
+
"dispatch",
|
|
45
|
+
"codex",
|
|
46
|
+
"claude",
|
|
47
|
+
"gemini",
|
|
48
|
+
"grok",
|
|
49
|
+
"multi-model",
|
|
50
|
+
"orchestration"
|
|
51
|
+
],
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"author": "Seraphim0916"
|
|
54
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Copy to ~/.omnilane/routing.local.yaml — lines here override routing.yaml.
|
|
2
|
+
# Same format: lane: vendor model effort (first match per lane wins)
|
|
3
|
+
|
|
4
|
+
# Example: send triage to a cheaper local setup and disable the grok lane.
|
|
5
|
+
# triage: codex gpt-5.6-luna low
|
|
6
|
+
# live-search: off - -
|
|
7
|
+
|
|
8
|
+
# consult is a multi-vendor direct-target chain. configure.sh intentionally
|
|
9
|
+
# skips it because that menu writes one candidate per lane. If overriding it,
|
|
10
|
+
# retain every vendor you want to address by name:
|
|
11
|
+
# consult: codex gpt-5.6-sol max | claude claude-opus-4-8 high | grok grok-4.5 - | gemini "Gemini 3.1 Pro (High)" -
|
|
12
|
+
|
|
13
|
+
# ── Starter profiles ─────────────────────────────────────────────
|
|
14
|
+
# Uncomment ONE block that matches what you actually subscribe to.
|
|
15
|
+
# You never need every vendor — unmatched lanes fall back down the chain
|
|
16
|
+
# in routing.yaml automatically.
|
|
17
|
+
|
|
18
|
+
# Profile: Claude-only (no Codex/Gemini/Grok) — route everything to Claude.
|
|
19
|
+
# hardest-coding: claude claude-opus-4-8 high
|
|
20
|
+
# bulk-mechanical: claude claude-sonnet-5 high
|
|
21
|
+
# triage: claude claude-haiku-4-5 -
|
|
22
|
+
# hard-judgment: claude claude-opus-4-8 high
|
|
23
|
+
# ui-draft: claude claude-opus-4-8 high
|
|
24
|
+
# long-context: claude claude-opus-4-8 high
|
|
25
|
+
# fast-agentic: claude claude-sonnet-5 high
|
|
26
|
+
# live-search: off - -
|
|
27
|
+
# coding-overflow: off - -
|
|
28
|
+
|
|
29
|
+
# Profile: Claude Code (Fable 5) as the main loop — let Fable keep judgment/taste,
|
|
30
|
+
# push only coding volume out to Codex.
|
|
31
|
+
# hard-judgment: claude claude-fable-5 high
|
|
32
|
+
# taste-final: claude claude-fable-5 high
|
|
33
|
+
|
|
34
|
+
# Profile: Codex-heavy (Sol main) — keep the hard lanes on Codex, Claude for taste.
|
|
35
|
+
# taste-final: claude claude-opus-4-8 high
|
|
36
|
+
# long-context: codex gpt-5.6-sol high
|
|
37
|
+
|
|
38
|
+
# Profile: no Codex subscription — promote Claude/Gemini into the coding lanes.
|
|
39
|
+
# hardest-coding: claude claude-opus-4-8 high
|
|
40
|
+
# bulk-mechanical: gemini "Gemini 3.5 Flash (High)" -
|
|
41
|
+
# triage: gemini "Gemini 3.5 Flash (Low)" -
|
package/routing.yaml
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# omnilane — single routing table shared by every harness (CC / Codex / Grok / Antigravity)
|
|
2
|
+
# Format: one lane per line -> lane: vendor model effort [| vendor model effort | off]
|
|
3
|
+
# vendor = codex | claude | grok | gemini
|
|
4
|
+
# effort = codex reasoning effort / claude --effort / agy thinking suffix / (grok: ignored)
|
|
5
|
+
# Fallback chain: the first candidate whose vendor CLI is installed wins, so the
|
|
6
|
+
# same table degrades gracefully when you only subscribe to one or two vendors.
|
|
7
|
+
# Override any line in ~/.omnilane/routing.local.yaml (same format; local wins).
|
|
8
|
+
# Provenance (audited 2026-07-12): defaults follow Artificial Analysis data, 2026-07
|
|
9
|
+
# snapshot. Verified against AA site records + vendor pricing pages: Intelligence &
|
|
10
|
+
# Coding indexes and 7:2:1 blended prices all match (AA field price1mBlended7To2To1);
|
|
11
|
+
# coding cost-per-task is chart-only (not independently reconstructed). Prices are
|
|
12
|
+
# standard short-context API tier — on subscription CLIs treat $ as relative ranking.
|
|
13
|
+
# Your own job outcomes (~/.omnilane/jobs/) outrank these priors; edit lanes to match.
|
|
14
|
+
|
|
15
|
+
hardest-coding: codex gpt-5.6-sol xhigh | claude claude-opus-4-8 high # AA coding 78.35; xhigh beats max (77.39) at lower cost — verified
|
|
16
|
+
bulk-mechanical: codex gpt-5.6-terra max | claude claude-sonnet-5 high | gemini "Gemini 3.5 Flash (High)" - # Terra 76.66 ~= Fable-class at a fraction of the cost
|
|
17
|
+
triage: codex gpt-5.6-luna medium | gemini "Gemini 3.5 Flash (Low)" - | claude claude-haiku-4-5 - # high-volume scans
|
|
18
|
+
hard-judgment: codex gpt-5.6-sol max | claude claude-opus-4-8 high # Sol max Intelligence 58.9 — second only to Fable 5 (59.9, fallback composite)
|
|
19
|
+
taste-final: claude claude-opus-4-8 high | codex gpt-5.6-sol max # user-facing prose, prompt/doc polish, Chinese phrasing, style arbitration
|
|
20
|
+
consult: codex gpt-5.6-sol max | claude claude-opus-4-8 high | grok grok-4.5 - | gemini "Gemini 3.1 Pro (High)" - # direct named-model consultation; use --vendor to prevent fallback
|
|
21
|
+
ui-draft: codex gpt-5.6-sol xhigh | claude claude-opus-4-8 high # only with a design system / reference images; open-ended visual taste -> taste-final
|
|
22
|
+
long-context: gemini "Gemini 3.1 Pro (High)" - | claude claude-opus-4-8 high | codex gpt-5.6-sol high # 1M-token synthesis ONLY — never agentic loops (AA agentic 21.4)
|
|
23
|
+
fast-agentic: gemini "Gemini 3.5 Flash (High)" - | codex gpt-5.6-luna high # speed + agentic tool loops
|
|
24
|
+
live-search: grok grok-4.5 - | off # native X/web search lane; no real substitute
|
|
25
|
+
coding-overflow: grok grok-4.5 - | off # codex-quota relief valve: mid-tier coding (AA 72.45); verify factual claims (hallucination 0.54)
|
|
26
|
+
arbitrate: off - - # opinion panel is OPT-IN: it costs one call per voter per round.
|
|
27
|
+
# Enable: `arbitrate: vote codex,claude,grok -` (any 1-4 of codex/claude/grok/gemini)
|
|
28
|
+
# Debate round (each voter rebuts the others): set the effort field to 2.
|
|
29
|
+
# Custom gate: `arbitrate: exec /path/to/script -`
|
|
30
|
+
# Claude Fable 5 (claude-fable-5) is deliberately absent from the defaults: the top Claude tier
|
|
31
|
+
# is usually the MAIN LOOP itself, not a dispatched worker, and it prices above Opus. If you want
|
|
32
|
+
# to route to it anyway, pick it in the configurator or set e.g.
|
|
33
|
+
# taste-final: claude claude-fable-5 high
|
|
34
|
+
# in ~/.omnilane/routing.local.yaml.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
# omnilane interactive lane configurator — writes ~/.omnilane/routing.local.yaml.
|
|
4
|
+
# Pure bash + a tty, no extra dependencies. Prefer editing routing.local.yaml
|
|
5
|
+
# by hand for scripted/non-interactive setups.
|
|
6
|
+
|
|
7
|
+
source "$(dirname "${BASH_SOURCE[0]}")/lib/common.sh"
|
|
8
|
+
source "$(dirname "${BASH_SOURCE[0]}")/lib/i18n.sh"
|
|
9
|
+
|
|
10
|
+
LOCAL_FILE="$OMNILANE_HOME/routing.local.yaml"
|
|
11
|
+
|
|
12
|
+
# Curated suggestions only — "c" always allows free text so new models work.
|
|
13
|
+
CODEX_MODELS=("gpt-5.6-sol" "gpt-5.6-terra" "gpt-5.6-luna")
|
|
14
|
+
CODEX_EFFORTS=("xhigh" "max" "ultra" "high" "medium" "low" "minimal" "none")
|
|
15
|
+
CLAUDE_MODELS=("claude-opus-4-8" "claude-fable-5" "claude-sonnet-5" "claude-haiku-4-5")
|
|
16
|
+
CLAUDE_EFFORTS=("max" "xhigh" "high" "medium" "low" "-")
|
|
17
|
+
GEMINI_MODELS=("Gemini 3.1 Pro (High)" "Gemini 3.1 Pro (Low)" "Gemini 3.5 Flash (High)" "Gemini 3.5 Flash (Medium)" "Gemini 3.5 Flash (Low)")
|
|
18
|
+
GROK_MODELS=("grok-4.5" "grok-4.3")
|
|
19
|
+
|
|
20
|
+
custom_value_is_safe() {
|
|
21
|
+
case "$1" in
|
|
22
|
+
*'$'*|*'`'*|*'"'*|*'\'*|*'#'*|*'|'*|*$'\r'*) return 1 ;;
|
|
23
|
+
*) return 0 ;;
|
|
24
|
+
esac
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
warn_unsafe_value() {
|
|
28
|
+
echo 'omnilane: unsafe custom value (not allowed: $ ` " \ # |)' >&2
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pick() { # title, options... -> prints the chosen value
|
|
32
|
+
local title="$1"; shift
|
|
33
|
+
local opts=("$@") i choice
|
|
34
|
+
echo "$title" >&2
|
|
35
|
+
for i in "${!opts[@]}"; do printf ' %d) %s\n' "$((i + 1))" "${opts[$i]}" >&2; done
|
|
36
|
+
printf '%s\n' "$(msg cfg_custom)" >&2
|
|
37
|
+
while true; do
|
|
38
|
+
read -rp "> " choice || choice=""
|
|
39
|
+
[[ -z "$choice" ]] && { echo "$(msg cfg_aborted)" >&2; exit 1; }
|
|
40
|
+
if [[ "$choice" == "c" ]]; then
|
|
41
|
+
read -rp "$(msg cfg_custom_value)" choice || choice=""
|
|
42
|
+
if [[ -n "$choice" ]] && custom_value_is_safe "$choice"; then
|
|
43
|
+
printf '%s' "$choice"; return
|
|
44
|
+
fi
|
|
45
|
+
[[ -n "$choice" ]] && warn_unsafe_value
|
|
46
|
+
continue
|
|
47
|
+
fi
|
|
48
|
+
if [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= ${#opts[@]} )); then
|
|
49
|
+
printf '%s' "${opts[$((choice - 1))]}"; return
|
|
50
|
+
fi
|
|
51
|
+
echo "$(msgf cfg_pick_or_c "${#opts[@]}")" >&2
|
|
52
|
+
done
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
LANES=()
|
|
56
|
+
while IFS= read -r line; do
|
|
57
|
+
if [[ "$line" =~ ^([a-z][a-z0-9-]*): ]]; then
|
|
58
|
+
lane="${BASH_REMATCH[1]}"
|
|
59
|
+
[[ "$lane" == "consult" ]] || LANES+=("$lane")
|
|
60
|
+
fi
|
|
61
|
+
done < "$OMNILANE_REPO/routing.yaml"
|
|
62
|
+
|
|
63
|
+
echo "$(msg cfg_title)"
|
|
64
|
+
bash "$OMNILANE_REPO/scripts/dispatch.sh" --list
|
|
65
|
+
echo
|
|
66
|
+
|
|
67
|
+
OVERRIDES=()
|
|
68
|
+
while true; do
|
|
69
|
+
echo "$(msg cfg_pick_lane)"
|
|
70
|
+
for i in "${!LANES[@]}"; do printf ' %d) %s\n' "$((i + 1))" "${LANES[$i]}"; done
|
|
71
|
+
read -rp "lane> " n || n=""
|
|
72
|
+
[[ -z "$n" ]] && break
|
|
73
|
+
if ! [[ "$n" =~ ^[0-9]+$ ]] || (( n < 1 || n > ${#LANES[@]} )); then
|
|
74
|
+
echo "$(msgf cfg_pick_range "${#LANES[@]}")"; continue
|
|
75
|
+
fi
|
|
76
|
+
lane="${LANES[$((n - 1))]}"
|
|
77
|
+
vendor="$(pick "$(msgf cfg_vendor_for "$lane")" codex claude grok gemini "vote (multi-model panel)" "exec (your own script/gate)" off)"
|
|
78
|
+
[[ "$vendor" == exec* ]] && vendor="exec"
|
|
79
|
+
[[ "$vendor" == vote* ]] && vendor="vote"
|
|
80
|
+
if [[ "$vendor" == "off" ]]; then
|
|
81
|
+
OVERRIDES+=("$lane: off - -")
|
|
82
|
+
echo "-> $lane: off"; echo; continue
|
|
83
|
+
fi
|
|
84
|
+
case "$vendor" in
|
|
85
|
+
codex) model="$(pick "$(msg cfg_model)" "${CODEX_MODELS[@]}")"; effort="$(pick "$(msg cfg_effort)" "${CODEX_EFFORTS[@]}")" ;;
|
|
86
|
+
claude) model="$(pick "$(msg cfg_model)" "${CLAUDE_MODELS[@]}")"; effort="$(pick "$(msg cfg_effort)" "${CLAUDE_EFFORTS[@]}")" ;;
|
|
87
|
+
gemini) model="$(pick "$(msg cfg_model)" "${GEMINI_MODELS[@]}")"; effort="-" ;;
|
|
88
|
+
grok) model="$(pick "$(msg cfg_model)" "${GROK_MODELS[@]}")"; effort="-" ;;
|
|
89
|
+
vote) while true; do
|
|
90
|
+
count="$(pick "$(msg cfg_voters_count)" "1" "2" "3" "4")"
|
|
91
|
+
[[ "$count" =~ ^[1-4]$ ]] && break
|
|
92
|
+
echo "$(msg cfg_voters_range)" >&2
|
|
93
|
+
done
|
|
94
|
+
model=""; remaining=(codex claude grok gemini)
|
|
95
|
+
for ((s = 1; s <= count; s++)); do
|
|
96
|
+
v="$(pick "$(msgf cfg_voter_n "$s")" "${remaining[@]}")"
|
|
97
|
+
model+="${model:+,}$v"
|
|
98
|
+
next=()
|
|
99
|
+
for r in "${remaining[@]}"; do [[ "$r" == "$v" ]] || next+=("$r"); done
|
|
100
|
+
# next is empty once every vendor is picked; the bare expansion
|
|
101
|
+
# is an unbound-variable error under set -u on Bash 3.2.
|
|
102
|
+
remaining=(${next[@]+"${next[@]}"})
|
|
103
|
+
done
|
|
104
|
+
effort="$(pick "$(msg cfg_rounds)" "1" "2")"
|
|
105
|
+
[[ "$effort" == "1" ]] && effort="-" ;;
|
|
106
|
+
exec) read -rp "$(msg cfg_exec_path)" model || model=""
|
|
107
|
+
[[ -n "$model" ]] || { echo "$(msg cfg_empty_path)"; continue; }
|
|
108
|
+
effort="-" ;;
|
|
109
|
+
*) model="$(pick "$(msg cfg_model)" "custom")"; effort="-" ;;
|
|
110
|
+
esac
|
|
111
|
+
if ! custom_value_is_safe "$vendor" || ! custom_value_is_safe "$model" ||
|
|
112
|
+
! custom_value_is_safe "$effort" ||
|
|
113
|
+
[[ "$vendor" == *[[:space:]]* || "$effort" == *[[:space:]]* ]]; then
|
|
114
|
+
warn_unsafe_value; continue
|
|
115
|
+
fi
|
|
116
|
+
[[ "$model" == *[[:space:]]* ]] && model="\"$model\""
|
|
117
|
+
OVERRIDES+=("$lane: $vendor $model $effort")
|
|
118
|
+
echo "-> $lane: $vendor $model $effort"
|
|
119
|
+
echo
|
|
120
|
+
done
|
|
121
|
+
|
|
122
|
+
[[ ${#OVERRIDES[@]} -gt 0 ]] || { echo "$(msg cfg_no_changes)"; exit 0; }
|
|
123
|
+
|
|
124
|
+
mkdir -p "$OMNILANE_HOME"
|
|
125
|
+
[[ -f "$LOCAL_FILE" ]] && cp "$LOCAL_FILE" "$LOCAL_FILE.bak"
|
|
126
|
+
{
|
|
127
|
+
echo "# written by scripts/configure.sh on $(date +%F) — first match per lane wins"
|
|
128
|
+
printf '%s\n' "${OVERRIDES[@]}"
|
|
129
|
+
# keep earlier customizations below (new lines above shadow same-lane old ones)
|
|
130
|
+
[[ -f "$LOCAL_FILE.bak" ]] && grep -v '^#' "$LOCAL_FILE.bak" || true
|
|
131
|
+
} > "$LOCAL_FILE"
|
|
132
|
+
|
|
133
|
+
echo "$(msgf cfg_wrote "$LOCAL_FILE")"
|
|
134
|
+
bash "$OMNILANE_REPO/scripts/dispatch.sh" --list
|