spexcode 0.6.1 → 0.6.2
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 +97 -149
- package/package.json +1 -1
- package/spec-cli/hooks/harness.sh +26 -6
- package/spec-cli/src/anchors.ts +27 -5
- package/spec-cli/src/cli.ts +171 -28
- package/spec-cli/src/client.ts +112 -13
- package/spec-cli/src/delivery-queue.ts +62 -4
- package/spec-cli/src/execution-trace.ts +444 -0
- package/spec-cli/src/gateway-hub.ts +10 -8
- package/spec-cli/src/gateway.ts +99 -6
- package/spec-cli/src/git.ts +477 -101
- package/spec-cli/src/graph.ts +56 -6
- package/spec-cli/src/graphCache.ts +86 -17
- package/spec-cli/src/graphStream.ts +1 -1
- package/spec-cli/src/guide.ts +93 -4
- package/spec-cli/src/harness-select.ts +2 -2
- package/spec-cli/src/harness.ts +344 -66
- package/spec-cli/src/help.ts +22 -10
- package/spec-cli/src/host-resources.ts +62 -7
- package/spec-cli/src/identity-presets.js +16 -6
- package/spec-cli/src/index.ts +101 -13
- package/spec-cli/src/layout.ts +116 -33
- package/spec-cli/src/lint.ts +37 -7
- package/spec-cli/src/materialize.ts +10 -5
- package/spec-cli/src/plugin-harness.ts +10 -10
- package/spec-cli/src/reviews.ts +57 -28
- package/spec-cli/src/session-execution.ts +68 -0
- package/spec-cli/src/session-files.ts +121 -0
- package/spec-cli/src/session-follow.ts +4 -4
- package/spec-cli/src/session-reparent.ts +33 -0
- package/spec-cli/src/session-timeline.ts +163 -14
- package/spec-cli/src/session-web.ts +135 -0
- package/spec-cli/src/sessions.ts +584 -104
- package/spec-cli/src/uninstall.ts +1 -1
- package/spec-cli/templates/spec/project/.plugins/core/comment-altitude/spec.md +2 -21
- package/spec-cli/templates/spec/project/.plugins/core/spec.md +12 -13
- package/spec-cli/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +5 -22
- package/spec-cli/templates/spec/project/.plugins/prompts/forge-link/spec.md +2 -1
- package/spec-cli/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +4 -6
- package/spec-cli/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +3 -9
- package/spec-dashboard/dist/assets/{App-F9uaAVcH.js → App-B6IbFbGu.js} +2 -2
- package/spec-dashboard/dist/assets/Dashboard-_sDk7Rcg.js +27 -0
- package/spec-dashboard/dist/assets/EvalsPage-BT357Lyt.js +2 -0
- package/spec-dashboard/dist/assets/{IssuesPage-CuKLFhH3.js → IssuesPage-BG9rNOgV.js} +1 -1
- package/spec-dashboard/dist/assets/MobileApp-D9yPMR3A.js +2 -0
- package/spec-dashboard/dist/assets/{Modal-CQgYymmr.js → Modal-B3EcgeA3.js} +1 -1
- package/spec-dashboard/dist/assets/{PageScroll-hT7UTLvD.js → PageScroll-CNZOugWV.js} +1 -1
- package/spec-dashboard/dist/assets/{ProjectsPage-CtXxakF9.js → ProjectsPage-C2CRHmvs.js} +1 -1
- package/spec-dashboard/dist/assets/SessionInterface-DOEjuT3l.js +40 -0
- package/spec-dashboard/dist/assets/{SessionWindow-CixDi4PI.js → SessionWindow-1nYgZ4CL.js} +1 -1
- package/spec-dashboard/dist/assets/Settings-7WeTJn0V.js +1 -0
- package/spec-dashboard/dist/assets/Thread-CEbzAFQn.js +13 -0
- package/spec-dashboard/dist/assets/data-SlvxNA2r.js +1 -0
- package/spec-dashboard/dist/assets/{index-DrVao0Ep.js → index-DLVeOHL-.js} +10 -10
- package/spec-dashboard/dist/assets/index-DYLGfPjx.css +1 -0
- package/spec-dashboard/dist/assets/{launch-BBH02b1v.js → launch-yxCYr64x.js} +29 -29
- package/spec-dashboard/dist/assets/sessionSurface--7l69h4F.js +1 -0
- package/spec-dashboard/dist/index.html +2 -2
- package/spec-eval/src/cli.ts +6 -1
- package/spec-eval/src/evaltab.ts +7 -5
- package/spec-eval/src/freshness.ts +383 -104
- package/spec-eval/src/scenariofresh.ts +2 -2
- package/spec-eval/src/sessioneval.ts +126 -132
- package/spec-dashboard/dist/assets/Dashboard-Ba_jhxp1.js +0 -27
- package/spec-dashboard/dist/assets/EvalsPage-FixoOg_n.js +0 -2
- package/spec-dashboard/dist/assets/MobileApp-CHgEHORJ.js +0 -2
- package/spec-dashboard/dist/assets/SessionInterface-Bpie-9fs.js +0 -39
- package/spec-dashboard/dist/assets/Settings-C2MsucfE.js +0 -1
- package/spec-dashboard/dist/assets/Thread-C6Go8HRh.js +0 -13
- package/spec-dashboard/dist/assets/data-B-RQmit6.js +0 -1
- package/spec-dashboard/dist/assets/index-CsI8DElI.css +0 -1
- package/spec-dashboard/dist/assets/terminalFont-C6oGNKrf.js +0 -1
|
@@ -10,7 +10,7 @@ import { dematerialize } from './materialize.js'
|
|
|
10
10
|
import { gitBinary } from './git.js'
|
|
11
11
|
|
|
12
12
|
// the standard plugin-host folders a host agent scans (in addition to any named in spexcode.json's `harnesses`).
|
|
13
|
-
const DEFAULT_PLUGIN_HOSTS = ['.claude', '.codex', '.
|
|
13
|
+
const DEFAULT_PLUGIN_HOSTS = ['.claude', '.codex', '.adopter-a'] as const
|
|
14
14
|
|
|
15
15
|
// Init and uninstall share one ownership source for generated git hooks: the shipped canonical templates.
|
|
16
16
|
// Exact bytes prove the destination is still our derivative; any user edit withdraws that ownership.
|
|
@@ -5,24 +5,5 @@ status: active
|
|
|
5
5
|
hue: 200
|
|
6
6
|
desc: A config plugin — code comments navigate non-obvious local reasoning; specs own product intent and contract.
|
|
7
7
|
---
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
restated beside each implementation.
|
|
11
|
-
|
|
12
|
-
Before retaining or adding a comment, ask in this order:
|
|
13
|
-
|
|
14
|
-
1. Does it state intent or a contract? Put that fact in the owning spec body if it is not already there, then
|
|
15
|
-
remove the code comment.
|
|
16
|
-
2. Does it explain a non-obvious local decision — ordering, a platform or library behavior, a measured pitfall,
|
|
17
|
-
or why an otherwise plausible implementation is unsafe? Keep a short comment beside the decision. It should
|
|
18
|
-
name the fact the reader cannot recover from code alone, not re-explain the whole feature.
|
|
19
|
-
3. Does it merely translate the next line or restate a type/name? Remove it.
|
|
20
|
-
|
|
21
|
-
Never turn subtraction into amnesia. A measured value, version-specific behavior, rejected alternative, or other
|
|
22
|
-
implementation fact absent from the spec must remain in code unless it is truly product contract, in which case
|
|
23
|
-
move the fact to the spec before deleting the comment. `@@@title - explanation` is reserved for the genuinely
|
|
24
|
-
tricky local reasoning that survives this test; a tag is not a license to repeat the spec.
|
|
25
|
-
|
|
26
|
-
This division gives each information channel one repair path: a changed product promise updates its spec; a changed
|
|
27
|
-
implementation hazard updates its nearby comment; code that makes either false updates both as their distinct facts
|
|
28
|
-
require. The rule complements [[spec-first]]: reading the contract first is what makes redundant code prose visible.
|
|
8
|
+
Specs own intent, invariants, policy, and observable contracts. Comments only navigate non-obvious local decisions.
|
|
9
|
+
`spex guide spec` has the comment-altitude details.
|
|
@@ -6,18 +6,17 @@ hue: 200
|
|
|
6
6
|
desc: A config plugin — the minimal spec-discipline contract folded into every launched agent.
|
|
7
7
|
code:
|
|
8
8
|
---
|
|
9
|
-
|
|
10
|
-
use a noun's `--help` for exact usage. Ordinary Git, shell, editor, branch, and worktree tools remain available;
|
|
11
|
-
SpexCode commands manage only SpexCode state.
|
|
9
|
+
Use noun-first CLI commands; `spex help` is the authoritative command map.
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
Anything a human needs to inspect, whether a file or a local webpage, goes out through `spex session files add` or
|
|
12
|
+
`spex session web add`: never paste an absolute path or `host:port`, and never start a static server yourself.
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
file with `spex eval add`; the reading's `codeSha` must name that commit
|
|
14
|
+
When this session has a clearly running child session (`active` or `parked`), the parent is supervising rather
|
|
15
|
+
than finished: declare `park`, not `done`/`awaiting`, until the child reports a settled state.
|
|
16
|
+
|
|
17
|
+
1. **Spec first:** before governed code, read its spec body with `spex spec owner <path>` or `spex spec search`.
|
|
18
|
+
Update that current-state body with any changed intent.
|
|
19
|
+
2. **Commit before declare:** commit the code and spec it justifies before done or merge; independent intent gets
|
|
20
|
+
its own node.
|
|
21
|
+
3. **Keep the loss signal honest:** run `spex spec lint` (the blocking correctness gate) and `spex eval lint --changed`. Measure changed scenarios
|
|
22
|
+
through the real product, commit the verified tree, then file with `spex eval add`; the reading's `codeSha` must name that commit.
|
|
@@ -6,27 +6,10 @@ hue: 30
|
|
|
6
6
|
desc: A config plugin — the trunk checkout is the fleet's one landing door, so a merge must be trivial by the time it reaches it: sync and resolve in YOUR OWN worktree, land only when your branch already contains the trunk, and wait rather than race for a busy door.
|
|
7
7
|
code:
|
|
8
8
|
---
|
|
9
|
-
## Landing is atomic
|
|
9
|
+
## Landing is atomic
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
milliseconds. Land only the second kind.
|
|
11
|
+
1. In your worktree, merge `<base>` into the branch, resolve conflicts there, and rerun the proof.
|
|
12
|
+
2. Immediately before landing, require `git merge-base --is-ancestor <base> <branch>`; otherwise sync again.
|
|
13
|
+
3. A clean textual merge is not product proof; the synced branch's verification is required.
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
<base>`) right where you work: resolve every conflict there and re-run what proves your work — the tests,
|
|
18
|
-
the scenario you measured. Your worktree is the only place that has your context, your tooling, and nobody
|
|
19
|
-
waiting behind you.
|
|
20
|
-
- **The landing itself must be trivial.** `git merge-base --is-ancestor <base> <branch>` must be TRUE at the
|
|
21
|
-
moment you land: your branch already contains the trunk, so the merge writes a commit and nothing else. If
|
|
22
|
-
it is false — the trunk moved while you were testing — do NOT land: sync again, then land. Resolving
|
|
23
|
-
conflicts *in the shared checkout* is the one forbidden move; it blocks everyone behind a half-merged index,
|
|
24
|
-
and it decides the conflict in the place with the least context about the other side.
|
|
25
|
-
- **A busy door is a wait, not a race.** If the shared checkout is already mid-merge, you cannot land — wait
|
|
26
|
-
and retry, bounded. Never `git merge --abort` someone else's in-progress merge and never resolve their
|
|
27
|
-
conflicts for them: that is their work in flight, and aborting it destroys the part they had already
|
|
28
|
-
reasoned through. If your OWN landing ends up half-merged, abort yours and report rather than leaving the
|
|
29
|
-
trunk mid-state.
|
|
30
|
-
- **An auto-merge is not a tested merge.** A clean merge proves the texts didn't overlap, not that the
|
|
31
|
-
combination works: two branches can each be green and their merge red. That is why the sync happens where
|
|
32
|
-
you can re-run the proof, and why "it merged without conflicts" is never the standard for landing.
|
|
15
|
+
`spex guide spec` has the shared-checkout mid-merge rule.
|
|
@@ -6,4 +6,5 @@ hue: 280
|
|
|
6
6
|
desc: A config plugin — agents link an issue or change request opened through the resolved forge to the spec node it serves via one `Spec: <id>` body line.
|
|
7
7
|
code:
|
|
8
8
|
---
|
|
9
|
-
|
|
9
|
+
Every forge issue or change request body includes `Spec: <node-id>` (comma-separate several). Use each node's
|
|
10
|
+
leaf directory name, not its slash path; `spex graph --json` lists valid ids.
|
|
@@ -6,10 +6,8 @@ hue: 200
|
|
|
6
6
|
desc: A config plugin — keep the project-keyed agent memory free of session- and role-specific facts, so N agents in one folder never inherit a confused identity.
|
|
7
7
|
code:
|
|
8
8
|
---
|
|
9
|
-
## Memory hygiene
|
|
9
|
+
## Memory hygiene
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- **On a non-main worktree** (you are on a `node/<id>` branch, not the main checkout): do **not** record any memory for this session at all. Its work is transient and will merge or close; a durable lesson is recorded later, from main, once it has actually landed.
|
|
15
|
-
- **Even on main, never record a transient ROLE or IDENTITY** — "I am the supervisor", "I'm the coordinator", "I'm the agent doing X". These are per-launch facts, not durable ones. Recording one makes the next launched agent read *itself* as that role, and several agents in one folder dissolve into mutual-supervision confusion (everyone thinks they're the supervisor, everyone watches everyone).
|
|
11
|
+
Project memory is shared by the main checkout and all worktrees. Store only durable cross-session project/user
|
|
12
|
+
facts. Never store this task, transient worktree state, a one-off decision, role, or identity. On a non-main
|
|
13
|
+
`node/<id>` worktree, record no memory at all; land a durable lesson first, then record it from main.
|
|
@@ -6,13 +6,7 @@ hue: 140
|
|
|
6
6
|
desc: A config plugin — a bug fix must first REPRODUCE the failure as a failing eval, then fix, verify, commit, and file the passing eval. The fail→pass pair on one scenario is the fix's proof (the A/B).
|
|
7
7
|
code:
|
|
8
8
|
---
|
|
9
|
-
## Reproduce before you fix
|
|
9
|
+
## Reproduce before you fix
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **A — reproduce (fail).** Find the eval-core scenario whose expected the bug violates (if none fits, ADD one to the node's `eval.md` — a description + the expected correct behaviour), run it, and file the failing reading with evidence that SHOWS the bug: `spex eval add <node> --scenario <s> --fail --note "<what's wrong>"` plus an `--image`/`--video` of the actual broken behaviour. This is not ceremony — reproducing is how you learn what actually breaks, and a fix aimed at an unreproduced bug aims at a guess.
|
|
14
|
-
- **B — fix, verify, commit, then file (pass).** Four moves, in order: make the code honor the spec; run the SAME scenario against your **working tree** until it actually passes — confidence is earned here, before any commit; **commit** the verified tree; only then file the passing reading, `spex eval add <node> --scenario <s> --pass`, so its `codeSha` names the very commit you measured (the sidecar append lands last, on top of it). Confidence and anchoring are two different acts: you test the uncommitted tree, but the reading's sha anchor can only land after the commit — this is NOT "commit first, then test" (blind commits with routine rollbacks are bad git). A git sha names only a commit and an uncommitted change has none, so a reading filed from a dirty tree is born mis-anchored — its `codeSha` is a HEAD that does not contain the change it claims to measure — and freshness later marking it stale is the mis-anchor being correctly exposed, not an engine bug. (A needs no such care: it measures the bug on the old, already-committed HEAD, so its anchor is honest as-is.)
|
|
15
|
-
|
|
16
|
-
The two readings on the same scenario are the **A/B** — the error→correct transition, the fix's proof-of-work. eval already keeps per-scenario reading history, so the pair is durable and navigable end to end: the dashboard annotator flips between A (fail) and B (pass), and a trunk delivery (deliver-port) carries that pair as the evidence the fix is real. Don't skip A because the fix looks obvious — an obvious fix with no reproduced failure leaves the loss signal blind to exactly the regression you just closed.
|
|
17
|
-
|
|
18
|
-
This does not apply to building new intent (there is no prior failure to reproduce) — it is the discipline for **repair**: keep the loss signal honest across a bug's whole lifecycle, not just at the end.
|
|
11
|
+
For a bug fix, the fail→pass pair on one scenario is the repair proof. New intent has no prior failure to
|
|
12
|
+
reproduce; `spex guide eval` has the A/B filing sequence.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Dashboard-
|
|
2
|
-
import{u as N,r as a,j as n,I as D,a as B,P as k,i as z,_ as T,D as M,b as G}from"./index-DrVao0Ep.js";import{a as J,l as q,s as F,p as V}from"./data-B-RQmit6.js";const h=async e=>(e.headers.get("content-type")||"").includes("json")?e.json().catch(()=>null):null,H=5e3,O=(e,r,t)=>({title:typeof(e==null?void 0:e.title)=="string"&&e.title?e.title:r,icon:typeof(e==null?void 0:e.icon)=="string"&&e.icon?e.icon:t});function x(e){if(!e||typeof e!="object")return null;const r=e.id??e.projectId;return r?{id:String(r),identity:O(e.identity||{title:e.name,icon:e.icon},e.name||String(r),"spexcode"),root:typeof e.root=="string"?e.root:"",online:typeof e.online=="boolean"?e.online:null,url:e.url||"",port:e.port??null,gated:!!(e.gated??e.locked??e.hasPassword),configRevision:typeof e.configRevision=="string"?e.configRevision:""}:null}const W=e=>{const r=Array.isArray(e)?e:Array.isArray(e==null?void 0:e.projects)?e.projects:null;return r?r.map(x).filter(Boolean):null},Y=10;function ce(e,r,t=Y){const s=Array.isArray(e)?e:[],o=Math.max(1,Math.ceil(s.length/t)),l=Math.min(Math.max(1,Number.isInteger(r)?r:1),o);return{items:s.slice((l-1)*t,l*t),page:l,pageCount:o}}function Z(e,r,t){var s;if(!e)return t;if(!r)return null;if(r.state==="ok"){const o=(s=r.projects)==null?void 0:s.find(l=>l.id===e);return(o==null?void 0:o.identity)||{title:e,icon:"spexcode"}}return{title:(t==null?void 0:t.title)||e,icon:(t==null?void 0:t.icon)||"spexcode"}}const K=e=>(e==null?void 0:e.state)==="ok"?e.gateway.identity:{title:"Projects",icon:"gateway"},Q=e=>(e==null?void 0:e.title)||"SpexCode",R=(e,r)=>(r==null?void 0:r.state)==="absent"&&e&&e.state!=="absent"?e:r;async function X(){let e;try{e=await fetch("/projects",{cache:"no-store",headers:{Accept:"application/json"}})}catch{return{state:"absent"}}if(e.status===401)return{state:"denied",reason:"admin-login"};if(e.status===403)return{state:"denied",reason:"locked"};if(!e.ok)return{state:"absent"};const r=await h(e),t=W(r);if(!t)return{state:"absent"};const s=r!=null&&r.gateway&&typeof r.gateway=="object"?{identity:O(r.gateway,"Projects","gateway"),revision:typeof r.gateway.revision=="string"?r.gateway.revision:""}:{identity:{title:"Projects",icon:"gateway"},revision:""};return{state:"ok",adminGated:!!(r!=null&&r.adminGated),gateway:s,projects:t}}async function ie(e,{timeoutMs:r=2500}={}){try{const t=await fetch(`/p/${encodeURIComponent(e)}/health`,{cache:"no-store",signal:AbortSignal.timeout(r)});return!t.ok||t.redirected?"unreachable":(await t.text()).trim()==="ok"?"running":"unreachable"}catch{return"unreachable"}}async function C(e,r,t){let s;try{s=await fetch(e,{method:r,headers:{"Content-Type":"application/json",Accept:"application/json"},...r==="PUT"?{body:JSON.stringify({password:t})}:{}})}catch{return{ok:!1,error:"network"}}const o=await h(s)||{};return{ok:s.ok&&o.ok!==!1,status:s.status,...o.error?{error:o.error}:{}}}const le=(e,r)=>C(`/projects/${encodeURIComponent(e)}/password`,"PUT",r),ue=e=>C(`/projects/${encodeURIComponent(e)}/password`,"DELETE"),pe=e=>C("/projects/admin-password","PUT",e),de=()=>C("/projects/admin-password","DELETE");async function ee(e,r){const t=e==="admin"?"/login":`/p/${encodeURIComponent(e.projectId)}/login`;let s;try{s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({password:r})})}catch{return{ok:!1,error:"network"}}return s.status===401?{ok:!1,error:"wrong-password"}:s.status===403?{ok:!1,error:"locked"}:s.ok||s.redirected?{ok:!0}:{ok:!1,error:`http-${s.status}`}}async function fe(e=""){let r;try{const s=e?`?path=${encodeURIComponent(e)}`:"";r=await fetch(`/projects/browse${s}`,{cache:"no-store",headers:{Accept:"application/json"}})}catch{return{ok:!1,error:"network"}}const t=await h(r);return r.ok?typeof(t==null?void 0:t.path)!="string"||!Array.isArray(t==null?void 0:t.entries)?{ok:!1,error:"unexpected answer"}:{ok:!0,path:t.path,parent:typeof t.parent=="string"?t.parent:null,home:typeof t.home=="string"?t.home:t.path,gitRoot:typeof t.gitRoot=="string"?t.gitRoot:null,initialized:!!t.initialized,cataloged:!!t.cataloged,entries:t.entries.filter(s=>s&&typeof s.name=="string"&&typeof s.path=="string").map(s=>({name:s.name,path:s.path,git:!!s.git,initialized:!!s.initialized}))}:{ok:!1,status:r.status,error:(t==null?void 0:t.error)||`http-${r.status}`}}async function he(e,r={}){let t;try{t=await fetch("/projects",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({root:e,...r})})}catch{return{ok:!1,error:"network"}}const s=await h(t);if(!t.ok)return{ok:!1,status:t.status,error:(s==null?void 0:s.error)||`http-${t.status}`,...s!=null&&s.init&&typeof s.init=="object"?{code:s.init.code??null,output:String(s.init.output??"")}:{}};const o=x(s);return o?{ok:!0,project:o,setup:s.setup??null}:{ok:!1,error:"unexpected answer"}}async function je(e){let r;try{r=await fetch(`/projects/${encodeURIComponent(e)}/config`,{cache:"no-store",headers:{Accept:"application/json"}})}catch{return{ok:!1,error:"network"}}const t=await h(r);return r.ok?typeof(t==null?void 0:t.content)!="string"||typeof(t==null?void 0:t.revision)!="string"?{ok:!1,error:"unexpected answer"}:{ok:!0,content:t.content,revision:t.revision}:{ok:!1,status:r.status,error:(t==null?void 0:t.error)||`http-${r.status}`}}async function me(e,r,t){let s;try{s=await fetch(`/projects/${encodeURIComponent(e)}/config`,{method:"PUT",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({content:r,revision:t})})}catch{return{ok:!1,error:"network"}}const o=await h(s);return s.ok?typeof(o==null?void 0:o.content)!="string"||typeof(o==null?void 0:o.revision)!="string"?{ok:!1,error:"unexpected answer"}:{ok:!0,content:o.content,revision:o.revision}:{ok:!1,status:s.status,error:(o==null?void 0:o.error)||`http-${s.status}`}}async function $(e,r,t){let s;try{s=await fetch(e,{method:"PUT",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({icon:r,revision:t})})}catch{return{ok:!1,error:"network"}}const o=await h(s);return s.ok?{ok:!0,...o}:{ok:!1,status:s.status,error:(o==null?void 0:o.error)||`http-${s.status}`}}const ke=(e,r)=>$("/projects/icon",e,r),ge=(e,r,t)=>$(`/projects/${encodeURIComponent(e)}/icon`,r,t);async function U(e,r,t={}){let s;try{s=await fetch(`/projects/${encodeURIComponent(e)}/${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(t)})}catch{return{ok:!1,error:"network"}}const o=await h(s);return s.ok?!o||typeof o!="object"?{ok:!1,error:"unexpected answer"}:{ok:o.ok===!0,code:o.code??null,output:String(o.output??"")}:{ok:!1,status:s.status,error:(o==null?void 0:o.error)||`http-${s.status}`}}const we=(e,r)=>U(e,"init",{harness:r}),ye=e=>U(e,"doctor");async function Pe(e){let r;try{r=await fetch(`/projects/${encodeURIComponent(e)}/serve`,{method:"POST",headers:{Accept:"application/json"}})}catch{return{ok:!1,error:"network"}}const t=await h(r);return r.status===409?{ok:!0,already:!0,project:x(t==null?void 0:t.project)}:r.ok?{ok:!0,project:x(t==null?void 0:t.project)}:{ok:!1,status:r.status,error:(t==null?void 0:t.error)||`http-${r.status}`}}function I({scope:e,projectLabel:r,locked:t,onUnlocked:s}){const o=N(),[l,S]=a.useState(""),[j,A]=a.useState(!1),[g,P]=a.useState(null),m=e==="admin",p=async b=>{if(b.preventDefault(),!l||j)return;A(!0),P(null);const w=await ee(m?"admin":{projectId:e.projectId},l);A(!1),w.ok?(S(""),s()):P(w.error==="wrong-password"?o("credential.wrong"):o("credential.failed"))};return n.jsx("div",{className:"cred-wrap",children:n.jsxs("form",{className:"cred-card",onSubmit:p,children:[n.jsx("div",{className:"cred-brand",children:"$ spexcode"}),n.jsxs("div",{className:"cred-title",children:[n.jsx(D,{name:"lock",size:14,className:"cred-lock"}),t?o("credential.lockedTitle"):m?o("credential.adminTitle"):o("credential.projectTitle",{name:r||e&&e.projectId||""})]}),t?n.jsx("p",{className:"cred-sub",children:o("credential.lockedBody")}):n.jsxs(n.Fragment,{children:[n.jsx("p",{className:"cred-sub",children:o(m?"credential.adminBody":"credential.projectBody")}),g&&n.jsx("div",{className:"cred-err",children:g}),n.jsx("input",{className:"cred-input",type:"password",autoFocus:!0,required:!0,placeholder:"••••••••••","aria-label":o("credential.passwordLabel"),value:l,onChange:b=>S(b.target.value)}),n.jsx("button",{className:"cred-submit",type:"submit",disabled:j||!l,children:o(j?"credential.checking":"credential.unlock")})]})]})})}const te=a.lazy(()=>T(()=>import("./Dashboard-Ba_jhxp1.js").then(e=>e.D),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]))),re=a.lazy(()=>T(()=>import("./MobileApp-CHgEHORJ.js"),__vite__mapDeps([10,1,2,3,4,5,6,11,12]))),se=a.lazy(()=>T(()=>import("./ProjectsPage-CtXxakF9.js"),__vite__mapDeps([13,1,2,7,5,6])));window.addEventListener("vite:preloadError",e=>{const r=String(e.payload);sessionStorage.getItem("spexcode.chunkReload")!==r&&(sessionStorage.setItem("spexcode.chunkReload",r),e.preventDefault(),location.reload())});function oe(){const e=N(),r=B(),[t,s]=a.useState(null),[o,l]=a.useState(!1),S=a.useRef(new Map),j=a.useCallback((u,c)=>{s(J(u,S.current,c))},[]),[A,g]=a.useState(!1),[P,m]=a.useState(null),[p,b]=a.useState(null);a.useEffect(()=>{let u=!0;const c=()=>X().then(y=>{u&&b(L=>R(L,y))}).catch(()=>{u&&b(y=>R(y,{state:"absent"}))});c();const d=setInterval(c,H);return()=>{u=!1,clearInterval(d)}},[]);const w=a.useRef(0),f=a.useCallback(()=>{const u=++w.current;return q().then(c=>{if(!(u!==w.current||!c)){if(c.authRequired){m(c.authRequired);return}m(null),g(!1),j(c.board,!0),c.seal()}}).catch(()=>{u===w.current&&g(!0)})},[j]),v=!k&&!t&&!!p&&p.state!=="absent",E=!k&&!t&&p===null;a.useEffect(()=>{if(v||E)return;f();const u=F({onBoard:(d,y)=>{w.current++,g(!1),j(d,!!(y!=null&&y.authoritative))},onLegacyChange:()=>{f()},onStatus:l}),c=setInterval(()=>{f()},15e3);return()=>{u(),clearInterval(c)}},[f,j,v,E]);const _=t?V(t):null,i=k?Z(k,p,_):v?K(p):_;return a.useEffect(()=>{i&&(document.title=Q(i))},[i==null?void 0:i.title]),a.useEffect(()=>{if(!i)return;const u=v?M:G,c=z(i.icon,u);let d=document.querySelector("link[rel~='icon']");d||(d=document.createElement("link"),d.rel="icon",document.head.appendChild(d)),d.getAttribute("href")!==c&&d.setAttribute("href",c)},[i==null?void 0:i.icon,v]),P&&k?n.jsx(I,{scope:{projectId:k},projectLabel:(i==null?void 0:i.title)||k,onUnlocked:()=>{m(null),f()}}):t?n.jsx(a.Suspense,{fallback:n.jsx("div",{className:"loading",children:e("hud.loading")}),children:r?n.jsx(re,{specs:t.nodes,sessions:t.sessions,issuesStamp:t.issuesStamp,reloadBoard:f}):n.jsx(te,{specs:t.nodes,sessions:t.sessions,issuesStamp:t.issuesStamp,reload:f,identity:i,catalog:p,boardLive:o})}):v?n.jsx(a.Suspense,{fallback:n.jsx("div",{className:"loading",children:e("hud.loading")}),children:n.jsx(se,{})}):P?n.jsx(I,{scope:"admin",locked:P==="locked",onUnlocked:()=>{m(null),f()}}):A&&(k||p&&p.state==="absent")?n.jsxs("div",{className:"loading load-error",children:[n.jsx("span",{children:e("hud.loadError")}),n.jsx("button",{className:"load-retry",onClick:()=>{g(!1),f()},children:e("hud.retry")})]}):n.jsx("div",{className:"loading",children:e("hud.loading")})}const be=Object.freeze(Object.defineProperty({__proto__:null,default:oe},Symbol.toStringTag,{value:"Module"}));export{be as A,H as C,ce as a,I as b,de as c,fe as d,je as e,ye as f,ke as g,he as h,we as i,me as j,ge as k,X as l,Pe as m,ue as n,le as o,ie as p,pe as s};
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Dashboard-_sDk7Rcg.js","assets/index-DLVeOHL-.js","assets/index-DYLGfPjx.css","assets/SessionWindow-1nYgZ4CL.js","assets/Thread-CEbzAFQn.js","assets/PageScroll-CNZOugWV.js","assets/data-SlvxNA2r.js","assets/Modal-B3EcgeA3.js","assets/bindings-BC9vqpYU.js","assets/Dashboard-C5ap-Sga.css","assets/MobileApp-D9yPMR3A.js","assets/launch-yxCYr64x.js","assets/launch-Cp579UoJ.css","assets/ProjectsPage-C2CRHmvs.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{u as N,r as a,j as n,I as D,a as B,P as k,i as z,_ as T,D as M,b as G}from"./index-DLVeOHL-.js";import{a as J,l as q,s as F,p as V}from"./data-SlvxNA2r.js";const h=async e=>(e.headers.get("content-type")||"").includes("json")?e.json().catch(()=>null):null,H=5e3,O=(e,r,t)=>({title:typeof(e==null?void 0:e.title)=="string"&&e.title?e.title:r,icon:typeof(e==null?void 0:e.icon)=="string"&&e.icon?e.icon:t});function x(e){if(!e||typeof e!="object")return null;const r=e.id??e.projectId;return r?{id:String(r),identity:O(e.identity||{title:e.name,icon:e.icon},e.name||String(r),"spexcode"),root:typeof e.root=="string"?e.root:"",online:typeof e.online=="boolean"?e.online:null,url:e.url||"",port:e.port??null,gated:!!(e.gated??e.locked??e.hasPassword),configRevision:typeof e.configRevision=="string"?e.configRevision:""}:null}const W=e=>{const r=Array.isArray(e)?e:Array.isArray(e==null?void 0:e.projects)?e.projects:null;return r?r.map(x).filter(Boolean):null},Y=10;function ce(e,r,t=Y){const s=Array.isArray(e)?e:[],o=Math.max(1,Math.ceil(s.length/t)),l=Math.min(Math.max(1,Number.isInteger(r)?r:1),o);return{items:s.slice((l-1)*t,l*t),page:l,pageCount:o}}function Z(e,r,t){var s;if(!e)return t;if(!r)return null;if(r.state==="ok"){const o=(s=r.projects)==null?void 0:s.find(l=>l.id===e);return(o==null?void 0:o.identity)||{title:e,icon:"spexcode"}}return{title:(t==null?void 0:t.title)||e,icon:(t==null?void 0:t.icon)||"spexcode"}}const K=e=>(e==null?void 0:e.state)==="ok"?e.gateway.identity:{title:"Projects",icon:"gateway"},Q=e=>(e==null?void 0:e.title)||"SpexCode",R=(e,r)=>(r==null?void 0:r.state)==="absent"&&e&&e.state!=="absent"?e:r;async function X(){let e;try{e=await fetch("/projects",{cache:"no-store",headers:{Accept:"application/json"}})}catch{return{state:"absent"}}if(e.status===401)return{state:"denied",reason:"admin-login"};if(e.status===403)return{state:"denied",reason:"locked"};if(!e.ok)return{state:"absent"};const r=await h(e),t=W(r);if(!t)return{state:"absent"};const s=r!=null&&r.gateway&&typeof r.gateway=="object"?{identity:O(r.gateway,"Projects","gateway"),revision:typeof r.gateway.revision=="string"?r.gateway.revision:""}:{identity:{title:"Projects",icon:"gateway"},revision:""};return{state:"ok",adminGated:!!(r!=null&&r.adminGated),gateway:s,projects:t}}async function ie(e,{timeoutMs:r=2500}={}){try{const t=await fetch(`/p/${encodeURIComponent(e)}/health`,{cache:"no-store",signal:AbortSignal.timeout(r)});return!t.ok||t.redirected?"unreachable":(await t.text()).trim()==="ok"?"running":"unreachable"}catch{return"unreachable"}}async function C(e,r,t){let s;try{s=await fetch(e,{method:r,headers:{"Content-Type":"application/json",Accept:"application/json"},...r==="PUT"?{body:JSON.stringify({password:t})}:{}})}catch{return{ok:!1,error:"network"}}const o=await h(s)||{};return{ok:s.ok&&o.ok!==!1,status:s.status,...o.error?{error:o.error}:{}}}const le=(e,r)=>C(`/projects/${encodeURIComponent(e)}/password`,"PUT",r),ue=e=>C(`/projects/${encodeURIComponent(e)}/password`,"DELETE"),pe=e=>C("/projects/admin-password","PUT",e),de=()=>C("/projects/admin-password","DELETE");async function ee(e,r){const t=e==="admin"?"/login":`/p/${encodeURIComponent(e.projectId)}/login`;let s;try{s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({password:r})})}catch{return{ok:!1,error:"network"}}return s.status===401?{ok:!1,error:"wrong-password"}:s.status===403?{ok:!1,error:"locked"}:s.ok||s.redirected?{ok:!0}:{ok:!1,error:`http-${s.status}`}}async function fe(e=""){let r;try{const s=e?`?path=${encodeURIComponent(e)}`:"";r=await fetch(`/projects/browse${s}`,{cache:"no-store",headers:{Accept:"application/json"}})}catch{return{ok:!1,error:"network"}}const t=await h(r);return r.ok?typeof(t==null?void 0:t.path)!="string"||!Array.isArray(t==null?void 0:t.entries)?{ok:!1,error:"unexpected answer"}:{ok:!0,path:t.path,parent:typeof t.parent=="string"?t.parent:null,home:typeof t.home=="string"?t.home:t.path,gitRoot:typeof t.gitRoot=="string"?t.gitRoot:null,initialized:!!t.initialized,cataloged:!!t.cataloged,entries:t.entries.filter(s=>s&&typeof s.name=="string"&&typeof s.path=="string").map(s=>({name:s.name,path:s.path,git:!!s.git,initialized:!!s.initialized}))}:{ok:!1,status:r.status,error:(t==null?void 0:t.error)||`http-${r.status}`}}async function he(e,r={}){let t;try{t=await fetch("/projects",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({root:e,...r})})}catch{return{ok:!1,error:"network"}}const s=await h(t);if(!t.ok)return{ok:!1,status:t.status,error:(s==null?void 0:s.error)||`http-${t.status}`,...s!=null&&s.init&&typeof s.init=="object"?{code:s.init.code??null,output:String(s.init.output??"")}:{}};const o=x(s);return o?{ok:!0,project:o,setup:s.setup??null}:{ok:!1,error:"unexpected answer"}}async function je(e){let r;try{r=await fetch(`/projects/${encodeURIComponent(e)}/config`,{cache:"no-store",headers:{Accept:"application/json"}})}catch{return{ok:!1,error:"network"}}const t=await h(r);return r.ok?typeof(t==null?void 0:t.content)!="string"||typeof(t==null?void 0:t.revision)!="string"?{ok:!1,error:"unexpected answer"}:{ok:!0,content:t.content,revision:t.revision}:{ok:!1,status:r.status,error:(t==null?void 0:t.error)||`http-${r.status}`}}async function me(e,r,t){let s;try{s=await fetch(`/projects/${encodeURIComponent(e)}/config`,{method:"PUT",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({content:r,revision:t})})}catch{return{ok:!1,error:"network"}}const o=await h(s);return s.ok?typeof(o==null?void 0:o.content)!="string"||typeof(o==null?void 0:o.revision)!="string"?{ok:!1,error:"unexpected answer"}:{ok:!0,content:o.content,revision:o.revision}:{ok:!1,status:s.status,error:(o==null?void 0:o.error)||`http-${s.status}`}}async function $(e,r,t){let s;try{s=await fetch(e,{method:"PUT",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({icon:r,revision:t})})}catch{return{ok:!1,error:"network"}}const o=await h(s);return s.ok?{ok:!0,...o}:{ok:!1,status:s.status,error:(o==null?void 0:o.error)||`http-${s.status}`}}const ke=(e,r)=>$("/projects/icon",e,r),ge=(e,r,t)=>$(`/projects/${encodeURIComponent(e)}/icon`,r,t);async function U(e,r,t={}){let s;try{s=await fetch(`/projects/${encodeURIComponent(e)}/${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(t)})}catch{return{ok:!1,error:"network"}}const o=await h(s);return s.ok?!o||typeof o!="object"?{ok:!1,error:"unexpected answer"}:{ok:o.ok===!0,code:o.code??null,output:String(o.output??"")}:{ok:!1,status:s.status,error:(o==null?void 0:o.error)||`http-${s.status}`}}const we=(e,r)=>U(e,"init",{harness:r}),ye=e=>U(e,"doctor");async function Pe(e){let r;try{r=await fetch(`/projects/${encodeURIComponent(e)}/serve`,{method:"POST",headers:{Accept:"application/json"}})}catch{return{ok:!1,error:"network"}}const t=await h(r);return r.status===409?{ok:!0,already:!0,project:x(t==null?void 0:t.project)}:r.ok?{ok:!0,project:x(t==null?void 0:t.project)}:{ok:!1,status:r.status,error:(t==null?void 0:t.error)||`http-${r.status}`}}function I({scope:e,projectLabel:r,locked:t,onUnlocked:s}){const o=N(),[l,S]=a.useState(""),[j,A]=a.useState(!1),[g,P]=a.useState(null),m=e==="admin",p=async b=>{if(b.preventDefault(),!l||j)return;A(!0),P(null);const w=await ee(m?"admin":{projectId:e.projectId},l);A(!1),w.ok?(S(""),s()):P(w.error==="wrong-password"?o("credential.wrong"):o("credential.failed"))};return n.jsx("div",{className:"cred-wrap",children:n.jsxs("form",{className:"cred-card",onSubmit:p,children:[n.jsx("div",{className:"cred-brand",children:"$ spexcode"}),n.jsxs("div",{className:"cred-title",children:[n.jsx(D,{name:"lock",size:14,className:"cred-lock"}),t?o("credential.lockedTitle"):m?o("credential.adminTitle"):o("credential.projectTitle",{name:r||e&&e.projectId||""})]}),t?n.jsx("p",{className:"cred-sub",children:o("credential.lockedBody")}):n.jsxs(n.Fragment,{children:[n.jsx("p",{className:"cred-sub",children:o(m?"credential.adminBody":"credential.projectBody")}),g&&n.jsx("div",{className:"cred-err",children:g}),n.jsx("input",{className:"cred-input",type:"password",autoFocus:!0,required:!0,placeholder:"••••••••••","aria-label":o("credential.passwordLabel"),value:l,onChange:b=>S(b.target.value)}),n.jsx("button",{className:"cred-submit",type:"submit",disabled:j||!l,children:o(j?"credential.checking":"credential.unlock")})]})]})})}const te=a.lazy(()=>T(()=>import("./Dashboard-_sDk7Rcg.js").then(e=>e.D),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]))),re=a.lazy(()=>T(()=>import("./MobileApp-D9yPMR3A.js"),__vite__mapDeps([10,1,2,3,4,5,6,11,7,12]))),se=a.lazy(()=>T(()=>import("./ProjectsPage-C2CRHmvs.js"),__vite__mapDeps([13,1,2,7,5,6])));window.addEventListener("vite:preloadError",e=>{const r=String(e.payload);sessionStorage.getItem("spexcode.chunkReload")!==r&&(sessionStorage.setItem("spexcode.chunkReload",r),e.preventDefault(),location.reload())});function oe(){const e=N(),r=B(),[t,s]=a.useState(null),[o,l]=a.useState(!1),S=a.useRef(new Map),j=a.useCallback((u,c)=>{s(J(u,S.current,c))},[]),[A,g]=a.useState(!1),[P,m]=a.useState(null),[p,b]=a.useState(null);a.useEffect(()=>{let u=!0;const c=()=>X().then(y=>{u&&b(L=>R(L,y))}).catch(()=>{u&&b(y=>R(y,{state:"absent"}))});c();const d=setInterval(c,H);return()=>{u=!1,clearInterval(d)}},[]);const w=a.useRef(0),f=a.useCallback(()=>{const u=++w.current;return q().then(c=>{if(!(u!==w.current||!c)){if(c.authRequired){m(c.authRequired);return}m(null),g(!1),j(c.board,!0),c.seal()}}).catch(()=>{u===w.current&&g(!0)})},[j]),v=!k&&!t&&!!p&&p.state!=="absent",E=!k&&!t&&p===null;a.useEffect(()=>{if(v||E)return;f();const u=F({onBoard:(d,y)=>{w.current++,g(!1),j(d,!!(y!=null&&y.authoritative))},onLegacyChange:()=>{f()},onStatus:l}),c=setInterval(()=>{f()},15e3);return()=>{u(),clearInterval(c)}},[f,j,v,E]);const _=t?V(t):null,i=k?Z(k,p,_):v?K(p):_;return a.useEffect(()=>{i&&(document.title=Q(i))},[i==null?void 0:i.title]),a.useEffect(()=>{if(!i)return;const u=v?M:G,c=z(i.icon,u);let d=document.querySelector("link[rel~='icon']");d||(d=document.createElement("link"),d.rel="icon",document.head.appendChild(d)),d.getAttribute("href")!==c&&d.setAttribute("href",c)},[i==null?void 0:i.icon,v]),P&&k?n.jsx(I,{scope:{projectId:k},projectLabel:(i==null?void 0:i.title)||k,onUnlocked:()=>{m(null),f()}}):t?n.jsx(a.Suspense,{fallback:n.jsx("div",{className:"loading",children:e("hud.loading")}),children:r?n.jsx(re,{specs:t.nodes,sessions:t.sessions,issuesStamp:t.issuesStamp,reloadBoard:f}):n.jsx(te,{specs:t.nodes,sessions:t.sessions,issuesStamp:t.issuesStamp,reload:f,identity:i,catalog:p,boardLive:o})}):v?n.jsx(a.Suspense,{fallback:n.jsx("div",{className:"loading",children:e("hud.loading")}),children:n.jsx(se,{})}):P?n.jsx(I,{scope:"admin",locked:P==="locked",onUnlocked:()=>{m(null),f()}}):A&&(k||p&&p.state==="absent")?n.jsxs("div",{className:"loading load-error",children:[n.jsx("span",{children:e("hud.loadError")}),n.jsx("button",{className:"load-retry",onClick:()=>{g(!1),f()},children:e("hud.retry")})]}):n.jsx("div",{className:"loading",children:e("hud.loading")})}const be=Object.freeze(Object.defineProperty({__proto__:null,default:oe},Symbol.toStringTag,{value:"Module"}));export{be as A,H as C,ce as a,I as b,de as c,fe as d,je as e,ye as f,ke as g,he as h,we as i,me as j,ge as k,X as l,Pe as m,ue as n,le as o,ie as p,pe as s};
|