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 +40 -6
- package/package.json +1 -1
- package/public/index.html +69 -0
package/README.md
CHANGED
|
@@ -1,10 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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&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
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");
|