shadok-ai 0.8.16 → 0.8.18

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/README.md CHANGED
@@ -1,10 +1,44 @@
1
- # shadok-ai
1
+ <div align="center">
2
2
 
3
- A **web cockpit that drives multiple real Claude Code sessions in parallel** —
4
- each channel is one `claude` process, on your Claude subscription (not the API).
5
- Pilot them from a browser **and** from **Telegram** (one topic = one agent),
6
- with git-worktree isolation, agent profiles, a secret vault, a diff panel,
7
- scheduled prompts, and quota gauges.
3
+ <h1>shadok-ai</h1>
4
+
5
+ <h3>Many Claude Codes. One cockpit. And it wrote itself.</h3>
6
+
7
+ <p>Run <b>real</b> Claude Code sessions side by side, each in its own git worktree,
8
+ on the Claude subscription you already pay for. When one of them has a question,
9
+ you answer it with a click — in the browser, or from Telegram when you are not
10
+ at the desk.</p>
11
+
12
+ <p>
13
+ <a href="https://www.npmjs.com/package/shadok-ai"><img alt="npm" src="https://img.shields.io/npm/v/shadok-ai?color=F5A524&amp;label=npm"></a>
14
+ <a href="LICENSE"><img alt="licence AGPL-3.0" src="https://img.shields.io/badge/licence-AGPL--3.0-7C6CF0"></a>
15
+ <a href="https://shadok.ai"><img alt="shadok.ai" src="https://img.shields.io/badge/shadok.ai-F5F0E6?labelColor=08090D"></a>
16
+ </p>
17
+
18
+ <p><code>npx shadok-ai</code></p>
19
+
20
+ <a href="https://shadok.ai"><img width="720" alt="The cockpit: three agents running in parallel, one of them asking a multiple-choice question that is answered with a click, then the diff it wrote" src="https://shadok.ai/demo.gif"></a>
21
+
22
+ </div>
23
+
24
+ **It does not stop at one.** An agent can start other agents — each with its own
25
+ worktree, branch and role — and it is told when one of its own finishes, blocks on
26
+ a question, or dies. Nothing else wakes it.
27
+
28
+ **It keeps working when you are not there.** Mirror an agent to a Telegram topic
29
+ and answer from your phone, one topic per agent. Put a prompt on a schedule with a
30
+ **guard that runs without the model**: prints nothing, nothing happened, zero
31
+ tokens spent.
32
+
33
+ **It stays on a leash.** A profile is a role plus guardrails — forbid `git commit`
34
+ and the agent cannot commit. Secrets live in a vault outside your repository and
35
+ are injected only into the agents that need them. Quota gauges show the 5h and 7d
36
+ windows, and can hold an agent back when it is burning faster than the window
37
+ elapses.
38
+
39
+ **It wrote itself.** shadok-ai is largely built by agents running inside shadok-ai,
40
+ in their own worktrees, landed as reviewed pull requests. The section below is what
41
+ that produced.
8
42
 
9
43
  ## Quick start
10
44
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadok-ai",
3
- "version": "0.8.16",
3
+ "version": "0.8.18",
4
4
  "main": "dist/session.js",
5
5
  "scripts": {
6
6
  "test": "node --import tsx --test test/*.ts context/*/test/*.test.mjs",
package/public/index.html CHANGED
@@ -679,6 +679,12 @@
679
679
  .prof-drift { margin-left: 8px; font-size: 11px; color: var(--amber); }
680
680
  /* À qui ce secret est injecté. Discret : c'est une information de contexte,
681
681
  pas une alerte — contrairement à sec-orphan juste en dessous. */
682
+ /* Le pont ESM n'a jamais chargé : le dire plutôt que d'afficher un écran
683
+ amputé sans explication. */
684
+ .bridge-dead {
685
+ position: fixed; left: 50%; transform: translateX(-50%); bottom: 12px; z-index: 200;
686
+ background: #e2564a; color: #fff; padding: 8px 14px; border-radius: 8px; font-size: 13px;
687
+ }
682
688
  .sec-users { margin-left: 8px; font-size: 11px; color: var(--text-dim); }
683
689
  .sec-orphan { margin-left: 8px; font-size: 11px; color: var(--amber); font-family: var(--sans, inherit); }
684
690
  /* Profile picker for a running agent (the tab's context menu). */
@@ -1985,8 +1991,71 @@
1985
1991
  window.arcSegments = arcSegments;
1986
1992
  window.dialTitle = dialTitle;
1987
1993
  window.SWEEP_DEG = SWEEP_DEG;
1994
+ // Les bouchons du script classique sont maintenant remplacés par les vraies
1995
+ // fonctions : tout ce qui a pu être peint entre-temps est incomplet.
1996
+ window.dispatchEvent(new Event("shadok-bridge-ready"));
1988
1997
  </script>
1989
1998
  <script nonce="__CSP_NONCE__">
1999
+ // ── Bouchons du pont ESM (invariant 10) ─────────────────────────────────
2000
+ // Le bloc module ci-dessus est DIFFÉRÉ : il s'exécute après le parsing,
2001
+ // alors que ce script-ci tourne PENDANT et câble déjà les boutons.
2002
+ // Un clic dans cet intervalle appelait un window.<fn> inexistant — c'est
2003
+ // ainsi que `profileBadges` a levé sur un clic Secrets, à cinq lignes d'un
2004
+ // appel voisin correctement gardé. Garder chaque site d'appel était un
2005
+ // combat perdu : le suivant oubliait. Un bouchon neutre par nom, remplacé à
2006
+ // l'arrivée du module, rend tous les sites sûrs par construction.
2007
+ // test/esm-bridge.test.ts verrouille la correspondance nom ↔ bouchon.
2008
+ const BRIDGE_STUBS = {
2009
+ profileBlurb: () => "",
2010
+ profileBadges: () => [],
2011
+ defaultAgentName: (_p, cwd) => String(cwd ?? "").split("/").filter(Boolean).pop() || "agent",
2012
+ hasReadonlyPreset: () => false,
2013
+ applyReadonlyPreset: (deny) => deny,
2014
+ isManagedProfile: () => false,
2015
+ profileSaveBody: (form) => ({ ...form }),
2016
+ secretUsers: () => [],
2017
+ extractLiveText: () => "",
2018
+ livePreviewDecision: () => null,
2019
+ echoAuthor: () => "",
2020
+ notifyState: () => ({ color: null, badge: "", blink: false }),
2021
+ dialPos: () => 0,
2022
+ dialColor: () => "",
2023
+ arcSegments: () => [],
2024
+ dialTitle: () => "",
2025
+ bubblePlacement: () => null,
2026
+ unionRect: () => null,
2027
+ visibleSteps: (steps) => steps ?? [],
2028
+ };
2029
+ for (const [name, stub] of Object.entries(BRIDGE_STUBS)) {
2030
+ if (typeof window[name] !== "function") { stub.__stub = true; window[name] = stub; }
2031
+ }
2032
+ // Un bouchon empêche le plantage, il ne remplace pas la fonction : si le
2033
+ // module n'arrive JAMAIS (un des six fichiers en échec, une coupure), l'écran
2034
+ // resterait silencieusement amputé — pas de badges, pas d'aperçu en direct,
2035
+ // pas de notifications. Une dégradation muette est pire que l'erreur qu'elle
2036
+ // masque : on le dit.
2037
+ window.addEventListener("load", () => setTimeout(() => {
2038
+ const dead = Object.keys(BRIDGE_STUBS).filter((n) => window[n] && window[n].__stub);
2039
+ if (!dead.length) return;
2040
+ console.error("shadok-ai: le pont ESM n'a pas chargé — " + dead.join(", "));
2041
+ const bar = document.createElement("div");
2042
+ bar.className = "bridge-dead";
2043
+ bar.textContent = "⚠ This page did not load fully — some panels will look empty. Reload it.";
2044
+ document.body.appendChild(bar);
2045
+ }, 3000));
2046
+ // Un écran peint pendant que les bouchons étaient en place est incomplet
2047
+ // (une carte sans badges, une jauge à plat). Le module signale son arrivée :
2048
+ // on repeint ce qui est à l'écran, plutôt que de laisser un rendu dégradé.
2049
+ window.addEventListener("shadok-bridge-ready", () => {
2050
+ try {
2051
+ if (typeof renderProfileGrid === "function" && $("profileGrid")) renderProfileGrid(false);
2052
+ if (typeof renderProfilesList === "function" && !$("profilesOverlay").hidden) renderProfilesList();
2053
+ if (typeof renderSecrets === "function" && !$("secretsOverlay").hidden) renderSecrets();
2054
+ } catch {
2055
+ /* une repeinte ratée ne doit pas casser le chargement */
2056
+ }
2057
+ });
2058
+
1990
2059
  const $ = (id) => document.getElementById(id);
1991
2060
  const tabbar = $("tabbar"), newTabBtn = $("newTab"), newGroupBtn = $("newGroup");
1992
2061
  const ungroupedEl = $("ungrouped"), groupsEl = $("groups");