mirror of
https://github.com/ryan4yin/nix-config.git
synced 2026-11-13 12:13:46 +01:00
feat(agents): version the dsh web profile and add a proxied launcher #387
This commit is contained in:
1 parent
975d2668a8
commit
205f7c622b
12 files changed
+226
-3
No files matched your search
@@ -6,3 +6,8 @@ vercel.json
|
||||
cache
|
||||
temp
|
||||
.temp
|
||||
|
||||
# dsh writes its profile files itself (the Settings UI and `dsh plugin`), and
|
||||
# its YAML dumper wraps prose differently from prettier; formatting them here
|
||||
# would be undone on the next write.
|
||||
home/base/tui/agents/dsh/web
|
||||
@@ -254,6 +254,23 @@ kana mode="switch":
|
||||
k3s-test mode="switch":
|
||||
colmena apply {{mode}} --on '@k3s-test-*' --verbose --show-trace
|
||||
|
||||
# =================================================
|
||||
#
|
||||
# AI agent commands
|
||||
#
|
||||
# =================================================
|
||||
|
||||
# Start the dsh web UI without opening a browser, proxied via mihomo's mixed port.
|
||||
[group('agents')]
|
||||
dsh-web:
|
||||
#!/usr/bin/env nu
|
||||
# dsh's web fetch refuses a DNS answer it does not call public, and a fake-ip
|
||||
# resolver answers with 198.18.0.0/15. A proxied hop lets mihomo resolve the
|
||||
# origin instead, which skips that check and leaves fake-ip acceleration
|
||||
# intact. The policy reaches every Node fetch the harness makes, so mihomo
|
||||
# becomes a dependency for LLM, web search and HTTP MCP traffic too.
|
||||
with-env { HTTPS_PROXY: "http://127.0.0.1:7897", HTTP_PROXY: "http://127.0.0.1:7897" } { ^dsh web --no-open }
|
||||
|
||||
# =================================================
|
||||
#
|
||||
# Other useful commands
|
||||
|
||||
@@ -13,6 +13,8 @@ Nix dependency; this module only wires it into place.
|
||||
- `packages.nix`: agent CLIs (`codex`, `opencode2`, `pi`) from the `llm-agents` flake input, plus
|
||||
`pkgs.mcp-grafana` for the Grafana MCP server.
|
||||
- `env.nix`: telemetry and auto-update opt-outs for the agents.
|
||||
- `dsh/`: the dsh `web` profile that lives in `~/.dsh`. See [dsh/README.md](./dsh/README.md) for the
|
||||
link granularity, the tracked members and which hosts import it.
|
||||
|
||||
## Deployed rule targets
|
||||
|
||||
@@ -30,6 +32,3 @@ Out-of-store means an edit to `agents/AGENTS.md` takes effect on the next agent
|
||||
Home Manager switch. The trade-off is that the rules stay writable in the checkout; use a store
|
||||
symlink (`home.file.<target>.source = ../../../../agents/AGENTS.md`) if immutability matters more
|
||||
than fast iteration.
|
||||
|
||||
The module is imported through `home/base/tui`, so it applies to every host that imports
|
||||
`home/linux/gui.nix` or the macOS `home/darwin` stack. Core-only servers do not get it.
|
||||
@@ -0,0 +1,46 @@
|
||||
# dsh
|
||||
|
||||
The dsh (DeepSeek Harness) `web` profile, linked into `~/.dsh/profiles/web` from the checked-in
|
||||
files under [`web/`](./web).
|
||||
|
||||
## Why the link targets the directory
|
||||
|
||||
dsh persists Settings through `cordis.patch.yml` with an atomic write (a random-suffix sibling, then
|
||||
a rename), and that rename replaces a symlinked target instead of writing through to its referent. A
|
||||
file-level link — store or out-of-store — is destroyed by the first Settings save, while a directory
|
||||
link keeps the write inside the checkout. Same reasoning as `home/base/tui/tuios`, one level up.
|
||||
|
||||
| Profile member | Tracked | Role |
|
||||
| --------------------- | ------- | ------------------------------------------ |
|
||||
| `cordis.patch.yml` | yes | the user patch layer the Settings UI edits |
|
||||
| `package.json` | yes | the profile's `dsh.profile.bundles` list |
|
||||
| `pnpm-workspace.yaml` | yes | the profile's pnpm settings |
|
||||
| `cordis.yml` | no | rewritten on every boot |
|
||||
| `node_modules/` | no | written by `dsh plugin --profile web ...` |
|
||||
|
||||
`web/.gitignore` owns the untracked rows. As with the rules links in the parent module, the
|
||||
trade-off is a config that stays writable in the checkout: a Settings change and a commit are the
|
||||
same edit, and the configuration is shared by every host that deploys the profile.
|
||||
|
||||
## Which hosts deploy it
|
||||
|
||||
A host only gets this link by importing the module. `home/linux/gui.nix` and `home/darwin` pull it
|
||||
in through `home/base/tui`, so core-only servers miss it — `idols-ruby` installs dsh from
|
||||
`environment.systemPackages` and imports `home/base/tui/agents/dsh` from its own home module. A new
|
||||
host that ships dsh needs one of those two wirings.
|
||||
|
||||
## Default model route
|
||||
|
||||
`web/cordis.patch.yml` pins `agent-default-model` to the API-key route (`deepseek-official`). The
|
||||
account route (`deepseek-account`) never falls back to an API key, so a host holding only
|
||||
`DEEPSEEK_API_KEY` fails every freshly created session with `ACCOUNT_SIGN_IN_REQUIRED`. A host that
|
||||
signs in instead can select an account model per session, or override the field in its own
|
||||
`$DSH_HOME/cordis.patch.yml`, which the home layer applies last and which therefore wins.
|
||||
|
||||
## Running it
|
||||
|
||||
`just dsh-web` starts the harness without opening a browser and routes its outbound HTTP through
|
||||
mihomo's mixed port. dsh's web fetch refuses a DNS answer it does not call public, and a fake-ip
|
||||
resolver answers with `198.18.0.0/15`; a proxied hop lets mihomo resolve the origin instead, which
|
||||
skips that check and leaves fake-ip acceleration intact. The policy reaches every Node fetch the
|
||||
harness makes, so mihomo becomes a dependency for LLM, web search and HTTP MCP traffic too.
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
# dsh persists Settings through the profile's cordis.patch.yml with an atomic
|
||||
# write (a random-suffix sibling, then a rename), and that rename replaces a
|
||||
# symlinked target instead of writing through to its referent. A file-level
|
||||
# link, store or out-of-store, therefore survives only until the first save.
|
||||
# Linking the whole profile directory keeps the write inside the checkout and
|
||||
# leaves the directory symlink intact. Same reasoning as
|
||||
# `home/base/tui/tuios`, one level up; the checkout path is hardcoded like the
|
||||
# repo's other out-of-store links.
|
||||
profileDir = "${config.home.homeDirectory}/nix-config/home/base/tui/agents/dsh/web";
|
||||
in
|
||||
{
|
||||
# The `web` profile's configuration, owned by hand and by the Settings UI.
|
||||
# Generated members of the profile (cordis.yml, node_modules, lock files) are
|
||||
# gitignored inside that directory rather than tracked.
|
||||
home.file.".dsh/profiles/web".source = config.lib.file.mkOutOfStoreSymlink profileDir;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# Regenerated by dsh on every boot (always the empty root entry list).
|
||||
cordis.yml
|
||||
|
||||
# Written by `dsh plugin --profile web ...`.
|
||||
node_modules/
|
||||
pnpm-lock.yaml
|
||||
|
||||
# Transient siblings of the atomic-write and file-lock protocols.
|
||||
*.tmp
|
||||
*.lock
|
||||
@@ -0,0 +1,77 @@
|
||||
# Your patch layer for this dsh profile, applied after every bundle layer:
|
||||
# a top-level YAML array of loader patch entries (id-targeted config
|
||||
# overrides, disables, and insert lists; `!!js` expressions allowed).
|
||||
- id: ui-settings-general
|
||||
name: "@deepseek-ai/dsh-client-ui-settings-general"
|
||||
config:
|
||||
welcomeNoticeVersion: 2026-09-28.1
|
||||
- id: agent-default-model
|
||||
name: "@deepseek-ai/dsh-agent-default-model"
|
||||
config:
|
||||
provider: deepseek-official
|
||||
model: deepseek-flash
|
||||
reasoningEffort: high
|
||||
- id: ui-settings-account
|
||||
name: "@deepseek-ai/dsh-client-ui-settings-account"
|
||||
config:
|
||||
version: 1
|
||||
step: done
|
||||
purpose: development
|
||||
process: standard
|
||||
completion: completed
|
||||
usage: detailed
|
||||
developerTools: true
|
||||
- id: ui-chat
|
||||
name: "@deepseek-ai/dsh-client-ui-chat"
|
||||
config:
|
||||
transcriptView: standard
|
||||
performanceUsage: detailed
|
||||
- id: ui-settings
|
||||
name: "@deepseek-ai/dsh-client-ui-settings"
|
||||
config:
|
||||
enabled: true
|
||||
- id: locale
|
||||
name: "@deepseek-ai/dsh-client-locale"
|
||||
config:
|
||||
preference: zh
|
||||
- id: agent-preset-registry
|
||||
name: "@deepseek-ai/dsh-agent-preset-registry"
|
||||
config:
|
||||
default: standard
|
||||
selectedDefault: ptc
|
||||
- id: llm-deepseek-account
|
||||
name: "@deepseek-ai/dsh-llm-deepseek-account"
|
||||
config:
|
||||
models:
|
||||
- id: deepseek-flash
|
||||
name: DeepSeek-V41-Flash
|
||||
inputModalities:
|
||||
- text
|
||||
- image
|
||||
systemPromptUpdate: in-history
|
||||
toolUpdate: addition-only
|
||||
contextWindow: 1000000
|
||||
- id: deepseek-v4-pro
|
||||
name: DeepSeek-V4-Pro
|
||||
description: Stronger agentic coding, knowledge, and difficult reasoning; suited to complex or quality-critical tasks at higher cost.
|
||||
contextWindow: 500000
|
||||
inputModalities:
|
||||
- text
|
||||
- id: agent-loop
|
||||
name: "@deepseek-ai/dsh-agent-loop"
|
||||
config:
|
||||
agents: []
|
||||
maxParallelToolCalls: 8
|
||||
- id: subagent
|
||||
name: "@deepseek-ai/dsh-subagent"
|
||||
config:
|
||||
maxActiveSubagents: 3
|
||||
- id: compaction-basic
|
||||
name: "@deepseek-ai/dsh-compaction-basic"
|
||||
config:
|
||||
thresholdRatio: 0.35
|
||||
retainRatio: 0.16
|
||||
headroomTokens: 65536
|
||||
compactionRetries: 1
|
||||
maxOverflowRetries: 1
|
||||
auto: true
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "dsh-profile-web",
|
||||
"private": true,
|
||||
"dependencies": {},
|
||||
"dsh": {
|
||||
"profile": {
|
||||
"bundles": [
|
||||
"@deepseek-ai/dsh-base",
|
||||
"@deepseek-ai/dsh-web-app",
|
||||
"@deepseek-ai/dsh-experimental-auto-review"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
packages:
|
||||
- .
|
||||
|
||||
nodeLinker: hoisted
|
||||
autoInstallPeers: false
|
||||
@@ -2,6 +2,11 @@
|
||||
imports = [
|
||||
../../linux/core.nix
|
||||
../../linux/gui/i3
|
||||
# Ruby installs the dsh CLI system-wide from `hosts/idols-ruby/packages.nix`
|
||||
# while running the core home stack, so it misses the agents module that the
|
||||
# GUI hosts pull in through `home/linux/gui.nix`. Import just the dsh
|
||||
# profile, which keeps its Settings-owned configuration in this repository.
|
||||
../../base/tui/agents/dsh
|
||||
];
|
||||
|
||||
# Headless X11/i3 session for computer use.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
# The link targets the whole profile directory, not a file inside it: dsh's
|
||||
# atomic Settings write replaces a symlinked file on the first save, while a
|
||||
# directory link keeps the write inside the checkout.
|
||||
keys = [ ".dsh/profiles/web" ];
|
||||
source = "/home/tester/nix-config/home/base/tui/agents/dsh/web";
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# The dsh module is evaluated on its own here. Reading `home.file` out of a real
|
||||
# host would force the catppuccin theme derivations, which turns an eval test
|
||||
# into a build, and the profile link is system-independent anyway. The stub
|
||||
# supplies the only two config values the module reads.
|
||||
let
|
||||
module = import ../../../../home/base/tui/agents/dsh/default.nix;
|
||||
evaluated = module {
|
||||
config = {
|
||||
home.homeDirectory = "/home/tester";
|
||||
lib.file.mkOutOfStoreSymlink = path: path;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
keys = builtins.attrNames evaluated.home.file;
|
||||
source = toString evaluated.home.file.".dsh/profiles/web".source;
|
||||
}
|
||||
Reference in new issue
Block a user