clearotron 0.3.1-beta.3 → 0.3.1
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/build-info.json +2 -2
- package/driver/CHANGELOG.md +83 -0
- package/driver/citation-census.json +3 -3
- package/driver/common-law-receipts.mjs +11 -1
- package/driver/connotation-search.mjs +56 -5
- package/driver/contract-e3-backlog.mjs +31 -31
- package/driver/contract-vocabulary.mjs +59 -58
- package/driver/engine/mcp/gather-config.mjs +1 -1
- package/driver/gateway.mjs +29 -2
- package/driver/matter-frame-record.mjs +54 -1
- package/driver/package.json +1 -1
- package/driver/portal-service.mjs +43 -4
- package/driver/stages.mjs +17 -3
- package/driver/suite-census.json +44 -8
- package/driver/verify.mjs +83 -7
- package/mcp-server/CHANGELOG.md +19 -0
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
- package/portal-ui/dist/assets/{index-C-QysgZM.js → index-ChIQsMYp.js} +209 -103
- package/portal-ui/dist/assets/{index-CaSZbEMb.css → index-DBIs21e4.css} +22 -0
- package/portal-ui/dist/index.html +2 -2
- package/portal-ui/package.json +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/scripts/citation-line-check.mjs +53 -2
- package/scripts/deprecate-below.mjs +146 -0
- package/scripts/release-approve-parked.mjs +151 -0
- package/scripts/test-run.mjs +24 -1
- package/shared/connect-clients.mjs +52 -16
- package/shared/driver-dir.mjs +1 -1
|
@@ -11629,6 +11629,11 @@ function decodeStatus(status, body) {
|
|
|
11629
11629
|
errors: errorsOf(body),
|
|
11630
11630
|
detail: refused
|
|
11631
11631
|
};
|
|
11632
|
+
if (refused) return {
|
|
11633
|
+
kind: "gate",
|
|
11634
|
+
message: "That could not be done. Nothing was changed.",
|
|
11635
|
+
detail: refused
|
|
11636
|
+
};
|
|
11632
11637
|
const msg = errorsOf(body)[0] ?? "This action could not be completed.";
|
|
11633
11638
|
return /version|conflict/i.test(msg) && !/confirmation|plan again|re-confirm/i.test(msg) ? {
|
|
11634
11639
|
kind: "conflict",
|
|
@@ -11794,6 +11799,33 @@ function decodeAccess(b) {
|
|
|
11794
11799
|
}];
|
|
11795
11800
|
});
|
|
11796
11801
|
}
|
|
11802
|
+
var decodeSteps = (raw) => asArray(raw).flatMap((x) => {
|
|
11803
|
+
const s = asRecord(x);
|
|
11804
|
+
const text = asString(s["text"]);
|
|
11805
|
+
if (!text) return [];
|
|
11806
|
+
const hint = asString(s["hint"]);
|
|
11807
|
+
const c = s["copy"] == null ? null : asRecord(s["copy"]);
|
|
11808
|
+
let copy;
|
|
11809
|
+
if (c) {
|
|
11810
|
+
const kind = asString(c["kind"]);
|
|
11811
|
+
if (kind === "block" && asString(c["text"])) copy = {
|
|
11812
|
+
kind,
|
|
11813
|
+
text: asString(c["text"])
|
|
11814
|
+
};
|
|
11815
|
+
else if (kind === "secret" && asString(c["label"]) && asString(c["template"]) && asString(c["slot"]) && asString(c["template"]).includes(asString(c["slot"]))) copy = {
|
|
11816
|
+
kind,
|
|
11817
|
+
label: asString(c["label"]),
|
|
11818
|
+
template: asString(c["template"]),
|
|
11819
|
+
slot: asString(c["slot"])
|
|
11820
|
+
};
|
|
11821
|
+
else return [];
|
|
11822
|
+
}
|
|
11823
|
+
return [{
|
|
11824
|
+
text,
|
|
11825
|
+
...hint ? { hint } : {},
|
|
11826
|
+
...copy ? { copy } : {}
|
|
11827
|
+
}];
|
|
11828
|
+
});
|
|
11797
11829
|
function decodePerson(r) {
|
|
11798
11830
|
return {
|
|
11799
11831
|
email: asString(r["email"]) ?? "",
|
|
@@ -12089,33 +12121,12 @@ var api = {
|
|
|
12089
12121
|
...asString(r["sub"]) ? { sub: asString(r["sub"]) } : {},
|
|
12090
12122
|
...asString(r["verifiedOn"]) ? { verifiedOn: asString(r["verifiedOn"]) } : {},
|
|
12091
12123
|
...asString(r["by"]) ? { by: asString(r["by"]) } : {},
|
|
12092
|
-
steps:
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
let copy;
|
|
12099
|
-
if (c) {
|
|
12100
|
-
const kind = asString(c["kind"]);
|
|
12101
|
-
if (kind === "block" && asString(c["text"])) copy = {
|
|
12102
|
-
kind,
|
|
12103
|
-
text: asString(c["text"])
|
|
12104
|
-
};
|
|
12105
|
-
else if (kind === "secret" && asString(c["label"]) && asString(c["template"]) && asString(c["slot"]) && asString(c["template"]).includes(asString(c["slot"]))) copy = {
|
|
12106
|
-
kind,
|
|
12107
|
-
label: asString(c["label"]),
|
|
12108
|
-
template: asString(c["template"]),
|
|
12109
|
-
slot: asString(c["slot"])
|
|
12110
|
-
};
|
|
12111
|
-
else return [];
|
|
12112
|
-
}
|
|
12113
|
-
return [{
|
|
12114
|
-
text,
|
|
12115
|
-
...hint ? { hint } : {},
|
|
12116
|
-
...copy ? { copy } : {}
|
|
12117
|
-
}];
|
|
12118
|
-
}),
|
|
12124
|
+
steps: decodeSteps(r["steps"]),
|
|
12125
|
+
...r["door"] === "sign-in" || r["door"] === "key" ? { door: r["door"] } : Object.hasOwn(r, "door") ? { door: null } : {},
|
|
12126
|
+
...!(r["door"] === "sign-in" || r["door"] === "key") && Object.hasOwn(r, "altSteps") ? (() => {
|
|
12127
|
+
const a = decodeSteps(r["altSteps"]);
|
|
12128
|
+
return a.length ? { altSteps: a } : {};
|
|
12129
|
+
})() : {},
|
|
12119
12130
|
launch: (() => {
|
|
12120
12131
|
const u = asString(asRecord(r["launch"])["url"]);
|
|
12121
12132
|
return u && /^https:\/\//.test(u) ? { url: u } : null;
|
|
@@ -13714,11 +13725,34 @@ var RANK = {
|
|
|
13714
13725
|
paused: 2,
|
|
13715
13726
|
queued: 3
|
|
13716
13727
|
};
|
|
13728
|
+
var STOPPED = new Set(["failed", "cancelled"]);
|
|
13729
|
+
var whenOf = (r) => {
|
|
13730
|
+
for (const raw of [r.issuedAt, r.date]) {
|
|
13731
|
+
if (!raw) continue;
|
|
13732
|
+
const t = new Date(raw).getTime();
|
|
13733
|
+
if (!Number.isNaN(t)) return t;
|
|
13734
|
+
}
|
|
13735
|
+
return null;
|
|
13736
|
+
};
|
|
13737
|
+
var withinWindow = (r, now, days) => {
|
|
13738
|
+
const at = whenOf(r);
|
|
13739
|
+
return at === null || now - at <= days * 24 * 60 * 60 * 1e3;
|
|
13740
|
+
};
|
|
13741
|
+
var failuresNewestFirst = (a, b) => {
|
|
13742
|
+
const at = whenOf(a), bt = whenOf(b);
|
|
13743
|
+
if (at === null && bt === null) return 0;
|
|
13744
|
+
if (at === null) return -1;
|
|
13745
|
+
if (bt === null) return 1;
|
|
13746
|
+
return newestFirst(a, b);
|
|
13747
|
+
};
|
|
13717
13748
|
function inFlight(runs) {
|
|
13718
|
-
return runs.filter((r) => r.state !== "delivered" && !r.
|
|
13749
|
+
return runs.filter((r) => r.state !== "delivered" && !STOPPED.has(r.state)).slice().sort((a, b) => (RANK[a.state] ?? 9) - (RANK[b.state] ?? 9));
|
|
13750
|
+
}
|
|
13751
|
+
function recentFailures(runs, { now = Date.now(), days = 7 } = {}) {
|
|
13752
|
+
return runs.filter((r) => STOPPED.has(r.state) && !r.acked && withinWindow(r, now, days)).slice().sort(failuresNewestFirst);
|
|
13719
13753
|
}
|
|
13720
|
-
function acknowledged(runs) {
|
|
13721
|
-
return runs.filter((r) => r.state
|
|
13754
|
+
function acknowledged(runs, { now = Date.now(), days = 7 } = {}) {
|
|
13755
|
+
return runs.filter((r) => STOPPED.has(r.state) && r.acked === true && withinWindow(r, now, days)).slice().sort(failuresNewestFirst);
|
|
13722
13756
|
}
|
|
13723
13757
|
function readStamps(rows) {
|
|
13724
13758
|
const identity = (r) => [
|
|
@@ -13877,11 +13911,14 @@ function Home({ ctx }) {
|
|
|
13877
13911
|
const allRuns = result?.kind === "ok" ? result.value : [];
|
|
13878
13912
|
const runs = (0, import_react.useMemo)(() => runsFor(allRuns, ctx.owner), [allRuns, ctx.owner]);
|
|
13879
13913
|
const rows = (0, import_react.useMemo)(() => inFlight(runs), [runs]);
|
|
13914
|
+
const stopped = (0, import_react.useMemo)(() => recentFailures(runs), [runs]);
|
|
13915
|
+
const [showStopped, setShowStopped] = (0, import_react.useState)(false);
|
|
13880
13916
|
const acked = (0, import_react.useMemo)(() => acknowledged(runs), [runs]);
|
|
13881
13917
|
const [showAcked, setShowAcked] = (0, import_react.useState)(false);
|
|
13882
13918
|
const cards = (0, import_react.useMemo)(() => active(rows), [rows]);
|
|
13883
13919
|
const queue = (0, import_react.useMemo)(() => waiting(rows), [rows]);
|
|
13884
13920
|
const stamps = (0, import_react.useMemo)(() => readStamps(rows), [rows]);
|
|
13921
|
+
const stoppedStamps = (0, import_react.useMemo)(() => readStamps(stopped), [stopped]);
|
|
13885
13922
|
const ackedStamps = (0, import_react.useMemo)(() => readStamps(acked), [acked]);
|
|
13886
13923
|
const done = (0, import_react.useMemo)(() => recentlyFinished(runs, void 0, 3), [runs]);
|
|
13887
13924
|
const recent = (0, import_react.useMemo)(() => done.map(lastFinishedOf).filter((r) => r !== null), [done]);
|
|
@@ -13933,6 +13970,28 @@ function Home({ ctx }) {
|
|
|
13933
13970
|
onChanged: reload,
|
|
13934
13971
|
stamps
|
|
13935
13972
|
}) : null,
|
|
13973
|
+
stopped.length ? (0, import_jsx_runtime.jsxs)("div", {
|
|
13974
|
+
className: "home2-stopped",
|
|
13975
|
+
children: [(0, import_jsx_runtime.jsxs)("button", {
|
|
13976
|
+
type: "button",
|
|
13977
|
+
className: "home2-stopped-toggle",
|
|
13978
|
+
"aria-expanded": showStopped,
|
|
13979
|
+
onClick: () => setShowStopped((v) => !v),
|
|
13980
|
+
children: [
|
|
13981
|
+
stopped.length,
|
|
13982
|
+
" stopped recently",
|
|
13983
|
+
showStopped ? "" : " — show"
|
|
13984
|
+
]
|
|
13985
|
+
}), showStopped ? (0, import_jsx_runtime.jsx)("div", {
|
|
13986
|
+
className: "home2-cards",
|
|
13987
|
+
children: stopped.map((r) => (0, import_jsx_runtime.jsx)(Card, {
|
|
13988
|
+
run: r,
|
|
13989
|
+
ctx,
|
|
13990
|
+
onChanged: reload,
|
|
13991
|
+
stamp: stoppedStamps.get(r.runId) ?? null
|
|
13992
|
+
}, r.runId))
|
|
13993
|
+
}) : null]
|
|
13994
|
+
}) : null,
|
|
13936
13995
|
acked.length ? (0, import_jsx_runtime.jsxs)("div", {
|
|
13937
13996
|
className: "home2-acked",
|
|
13938
13997
|
children: [(0, import_jsx_runtime.jsxs)("button", {
|
|
@@ -13972,7 +14031,7 @@ function Home({ ctx }) {
|
|
|
13972
14031
|
total: finishedCount,
|
|
13973
14032
|
ctx
|
|
13974
14033
|
}) : null,
|
|
13975
|
-
answer === "ok" && !cards.length && !queue.length && !recent.length ? (0, import_jsx_runtime.jsx)(FirstRun$1, { onNew: canRun(ctx.me) ? () => ctx.go("/portal/new") : null }) : null,
|
|
14034
|
+
answer === "ok" && !cards.length && !queue.length && !recent.length && !stopped.length && !acked.length ? (0, import_jsx_runtime.jsx)(FirstRun$1, { onNew: canRun(ctx.me) ? () => ctx.go("/portal/new") : null }) : null,
|
|
13976
14035
|
usageRes?.kind === "ok" ? (0, import_jsx_runtime.jsx)("p", {
|
|
13977
14036
|
className: "home2-limits mono",
|
|
13978
14037
|
children: limitLine(usageRes.value.today, usageRes.value.dailyRuns)
|
|
@@ -21521,74 +21580,103 @@ function StepsPanel({ offer, measuring = false, state = AT_REST, onBlock, onSecr
|
|
|
21521
21580
|
className: "steps-name",
|
|
21522
21581
|
children: offer.name
|
|
21523
21582
|
}),
|
|
21524
|
-
(0, import_jsx_runtime.jsx)("
|
|
21525
|
-
className: "steps",
|
|
21526
|
-
children:
|
|
21527
|
-
|
|
21528
|
-
|
|
21529
|
-
|
|
21530
|
-
|
|
21531
|
-
|
|
21532
|
-
|
|
21533
|
-
|
|
21534
|
-
|
|
21535
|
-
|
|
21536
|
-
|
|
21537
|
-
|
|
21538
|
-
|
|
21539
|
-
|
|
21540
|
-
|
|
21541
|
-
|
|
21542
|
-
|
|
21543
|
-
|
|
21544
|
-
|
|
21545
|
-
|
|
21546
|
-
|
|
21547
|
-
|
|
21548
|
-
|
|
21549
|
-
|
|
21550
|
-
type: "button",
|
|
21551
|
-
className: `btn-primary${flashed ? " is-copied" : ""}`,
|
|
21552
|
-
"data-step": i,
|
|
21553
|
-
tabIndex: measuring ? -1 : void 0,
|
|
21554
|
-
onClick: () => onSecret?.(i, c),
|
|
21555
|
-
children: flashed ? (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)(Icon, {
|
|
21556
|
-
name: "check",
|
|
21557
|
-
size: 15
|
|
21558
|
-
}), "Copied"] }) : landed ? "Copy again" : c.label
|
|
21559
|
-
})
|
|
21560
|
-
}),
|
|
21561
|
-
landed ? (0, import_jsx_runtime.jsxs)("div", {
|
|
21562
|
-
className: "landed",
|
|
21563
|
-
children: [(0, import_jsx_runtime.jsx)("div", {
|
|
21564
|
-
className: "landed-label",
|
|
21565
|
-
children: "On your clipboard now"
|
|
21566
|
-
}), landed.split("\n").map((line, n) => (0, import_jsx_runtime.jsx)("div", {
|
|
21567
|
-
className: "mono",
|
|
21568
|
-
children: line
|
|
21569
|
-
}, n))]
|
|
21570
|
-
}) : null,
|
|
21571
|
-
!measuring && state.revealedAt === i && state.revealed ? (0, import_jsx_runtime.jsxs)("div", {
|
|
21572
|
-
className: "landed",
|
|
21573
|
-
children: [(0, import_jsx_runtime.jsx)("p", {
|
|
21574
|
-
className: "ai-said",
|
|
21575
|
-
children: "Your browser would not let us copy it. Copy this by hand — it is yours, and it will not be shown again."
|
|
21576
|
-
}), (0, import_jsx_runtime.jsx)("pre", {
|
|
21577
|
-
className: "ai-pre",
|
|
21578
|
-
children: state.revealed
|
|
21579
|
-
})]
|
|
21580
|
-
}) : null
|
|
21581
|
-
] }) : null,
|
|
21582
|
-
s.hint ? (0, import_jsx_runtime.jsx)("div", {
|
|
21583
|
-
className: "step-hint",
|
|
21584
|
-
children: (0, import_jsx_runtime.jsx)(Marked, { text: s.hint })
|
|
21585
|
-
}) : null
|
|
21586
|
-
] }) }, i);
|
|
21587
|
-
})
|
|
21588
|
-
})
|
|
21583
|
+
offer.door === null && offer.altSteps?.length ? (0, import_jsx_runtime.jsx)("p", {
|
|
21584
|
+
className: "steps-unknown",
|
|
21585
|
+
children: "We could not check how this Clearotron lets an assistant in, so both ways are below. Try the first — your assistant will tell you if it needs the other."
|
|
21586
|
+
}) : null,
|
|
21587
|
+
(0, import_jsx_runtime.jsx)(StepList, {
|
|
21588
|
+
steps: offer.steps,
|
|
21589
|
+
offset: 0,
|
|
21590
|
+
measuring,
|
|
21591
|
+
state,
|
|
21592
|
+
onBlock,
|
|
21593
|
+
onSecret
|
|
21594
|
+
}),
|
|
21595
|
+
offer.door === null && offer.altSteps?.length ? (0, import_jsx_runtime.jsxs)("div", {
|
|
21596
|
+
className: "steps-alt",
|
|
21597
|
+
children: [(0, import_jsx_runtime.jsx)("h4", {
|
|
21598
|
+
className: "steps-alt-head",
|
|
21599
|
+
children: "If your assistant asks to be let in another way"
|
|
21600
|
+
}), (0, import_jsx_runtime.jsx)(StepList, {
|
|
21601
|
+
steps: offer.altSteps,
|
|
21602
|
+
offset: offer.steps.length,
|
|
21603
|
+
measuring,
|
|
21604
|
+
state,
|
|
21605
|
+
onBlock,
|
|
21606
|
+
onSecret
|
|
21607
|
+
})]
|
|
21608
|
+
}) : null
|
|
21589
21609
|
]
|
|
21590
21610
|
});
|
|
21591
21611
|
}
|
|
21612
|
+
function StepList({ steps, offset, measuring = false, state = AT_REST, onBlock, onSecret }) {
|
|
21613
|
+
return (0, import_jsx_runtime.jsx)("ol", {
|
|
21614
|
+
className: "steps",
|
|
21615
|
+
children: steps.map((s, n) => {
|
|
21616
|
+
const i = n + offset;
|
|
21617
|
+
const c = s.copy;
|
|
21618
|
+
const flashed = !measuring && state.copiedAt === i;
|
|
21619
|
+
const landed = measuring && c?.kind === "secret" ? fill(c, mask("")) : state.landedAt === i ? state.landed : null;
|
|
21620
|
+
return (0, import_jsx_runtime.jsx)("li", { children: (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
21621
|
+
(0, import_jsx_runtime.jsx)("div", {
|
|
21622
|
+
className: "step-text",
|
|
21623
|
+
children: (0, import_jsx_runtime.jsx)(Marked, { text: s.text })
|
|
21624
|
+
}),
|
|
21625
|
+
c?.kind === "block" ? (0, import_jsx_runtime.jsxs)("div", {
|
|
21626
|
+
className: "codeblock",
|
|
21627
|
+
children: [(0, import_jsx_runtime.jsx)("pre", { children: c.text }), (0, import_jsx_runtime.jsx)("button", {
|
|
21628
|
+
type: "button",
|
|
21629
|
+
className: `btn-ghost${flashed ? " is-copied" : ""}`,
|
|
21630
|
+
"data-step": i,
|
|
21631
|
+
tabIndex: measuring ? -1 : void 0,
|
|
21632
|
+
onClick: () => onBlock?.(i, c.text),
|
|
21633
|
+
children: flashed ? "✓ Copied" : "Copy"
|
|
21634
|
+
})]
|
|
21635
|
+
}) : null,
|
|
21636
|
+
c?.kind === "secret" ? (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
21637
|
+
(0, import_jsx_runtime.jsx)("div", {
|
|
21638
|
+
className: "secret-btn",
|
|
21639
|
+
children: (0, import_jsx_runtime.jsx)("button", {
|
|
21640
|
+
type: "button",
|
|
21641
|
+
className: `btn-primary${flashed ? " is-copied" : ""}`,
|
|
21642
|
+
"data-step": i,
|
|
21643
|
+
tabIndex: measuring ? -1 : void 0,
|
|
21644
|
+
onClick: () => onSecret?.(i, c),
|
|
21645
|
+
children: flashed ? (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)(Icon, {
|
|
21646
|
+
name: "check",
|
|
21647
|
+
size: 15
|
|
21648
|
+
}), "Copied"] }) : landed ? "Copy again" : c.label
|
|
21649
|
+
})
|
|
21650
|
+
}),
|
|
21651
|
+
landed ? (0, import_jsx_runtime.jsxs)("div", {
|
|
21652
|
+
className: "landed",
|
|
21653
|
+
children: [(0, import_jsx_runtime.jsx)("div", {
|
|
21654
|
+
className: "landed-label",
|
|
21655
|
+
children: "On your clipboard now"
|
|
21656
|
+
}), landed.split("\n").map((line, n) => (0, import_jsx_runtime.jsx)("div", {
|
|
21657
|
+
className: "mono",
|
|
21658
|
+
children: line
|
|
21659
|
+
}, n))]
|
|
21660
|
+
}) : null,
|
|
21661
|
+
!measuring && state.revealedAt === i && state.revealed ? (0, import_jsx_runtime.jsxs)("div", {
|
|
21662
|
+
className: "landed",
|
|
21663
|
+
children: [(0, import_jsx_runtime.jsx)("p", {
|
|
21664
|
+
className: "ai-said",
|
|
21665
|
+
children: "Your browser would not let us copy it. Copy this by hand — it is yours, and it will not be shown again."
|
|
21666
|
+
}), (0, import_jsx_runtime.jsx)("pre", {
|
|
21667
|
+
className: "ai-pre",
|
|
21668
|
+
children: state.revealed
|
|
21669
|
+
})]
|
|
21670
|
+
}) : null
|
|
21671
|
+
] }) : null,
|
|
21672
|
+
s.hint ? (0, import_jsx_runtime.jsx)("div", {
|
|
21673
|
+
className: "step-hint",
|
|
21674
|
+
children: (0, import_jsx_runtime.jsx)(Marked, { text: s.hint })
|
|
21675
|
+
}) : null
|
|
21676
|
+
] }) }, i);
|
|
21677
|
+
})
|
|
21678
|
+
});
|
|
21679
|
+
}
|
|
21592
21680
|
function NotOnline() {
|
|
21593
21681
|
return (0, import_jsx_runtime.jsxs)("div", {
|
|
21594
21682
|
className: "notice ai-none",
|
|
@@ -23807,6 +23895,7 @@ function PeopleAccess({ ctx }) {
|
|
|
23807
23895
|
}) }) : v.people.map((p) => (0, import_jsx_runtime.jsx)(Row, {
|
|
23808
23896
|
person: p,
|
|
23809
23897
|
you: p.email.toLowerCase() === ctx.me.email.toLowerCase(),
|
|
23898
|
+
canModify: !v.localSignIn,
|
|
23810
23899
|
onModify: () => ctx.go(`${MODIFY_PERSON.path}?email=${encodeURIComponent(p.email)}`)
|
|
23811
23900
|
}, p.email)) })]
|
|
23812
23901
|
})
|
|
@@ -23937,7 +24026,7 @@ function Observed({ result }) {
|
|
|
23937
24026
|
]
|
|
23938
24027
|
});
|
|
23939
24028
|
}
|
|
23940
|
-
function Row({ person, you, onModify }) {
|
|
24029
|
+
function Row({ person, you, canModify, onModify }) {
|
|
23941
24030
|
const chips = accessChips(person.access);
|
|
23942
24031
|
const viewOnly = !person.permissions.run && !person.permissions.manage;
|
|
23943
24032
|
return (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
@@ -23988,8 +24077,9 @@ function Row({ person, you, onModify }) {
|
|
|
23988
24077
|
children: you ? null : (0, import_jsx_runtime.jsx)("button", {
|
|
23989
24078
|
type: "button",
|
|
23990
24079
|
className: "pill",
|
|
24080
|
+
disabled: !canModify,
|
|
23991
24081
|
style: {
|
|
23992
|
-
cursor: "pointer",
|
|
24082
|
+
cursor: canModify ? "pointer" : "default",
|
|
23993
24083
|
fontSize: 12
|
|
23994
24084
|
},
|
|
23995
24085
|
onClick: onModify,
|
|
@@ -24103,7 +24193,7 @@ function GiveAccess({ ctx }) {
|
|
|
24103
24193
|
setBusy(false);
|
|
24104
24194
|
}
|
|
24105
24195
|
};
|
|
24106
|
-
const failure = result && !isOk(result) ? result.kind === "conflict" ? "This Clearotron now signs in one person, so nobody can be added. Nothing was saved." : result.kind === "notFound" ? "You can only give access to what you have access to yourself. Nothing was saved." : result.kind === "reject" ? result.errors.join(" ") : saveFailureText(result, "That could not be saved. Nothing was changed.") : null;
|
|
24196
|
+
const failure = result && !isOk(result) ? result.kind === "gate" && result.detail?.code === "local_sign_in" ? "This Clearotron now signs in one person, so nobody can be added. Nothing was saved." : result.kind === "conflict" ? "This Clearotron now signs in one person, so nobody can be added. Nothing was saved." : result.kind === "notFound" ? "You can only give access to what you have access to yourself. Nothing was saved." : result.kind === "reject" ? result.errors.join(" ") : saveFailureText(result, "That could not be saved. Nothing was changed.") : null;
|
|
24107
24197
|
return (0, import_jsx_runtime.jsx)("div", {
|
|
24108
24198
|
className: "screen",
|
|
24109
24199
|
children: (0, import_jsx_runtime.jsxs)("div", {
|
|
@@ -24408,6 +24498,7 @@ function ModifyAccess({ ctx }) {
|
|
|
24408
24498
|
}, person.email);
|
|
24409
24499
|
}
|
|
24410
24500
|
function Form({ ctx, person, view }) {
|
|
24501
|
+
const readOnlyPeople = view.localSignIn;
|
|
24411
24502
|
const held = heldBy(person);
|
|
24412
24503
|
const [permissions, setPermissions] = (0, import_react.useState)(person.permissions);
|
|
24413
24504
|
const [chosen, setChosen] = (0, import_react.useState)(held);
|
|
@@ -24476,7 +24567,7 @@ function Form({ ctx, person, view }) {
|
|
|
24476
24567
|
setBusy(false);
|
|
24477
24568
|
}
|
|
24478
24569
|
};
|
|
24479
|
-
const failure = (r) => r === null || isOk(r) ? null : r.kind === "conflict" ? "This Clearotron now signs in one person, so nobody else can be changed. Nothing was saved." : r.kind === "notFound" ? "That is not access you can change. Nothing was saved." : r.kind === "reject" ? r.errors.join(" ") : saveFailureText(r, "That could not be saved. Nothing was changed.");
|
|
24570
|
+
const failure = (r) => r === null || isOk(r) ? null : r.kind === "gate" && r.detail?.code === "local_sign_in" ? "This Clearotron now signs in one person, so nobody else can be changed. Nothing was saved." : r.kind === "conflict" ? "This Clearotron now signs in one person, so nobody else can be changed. Nothing was saved." : r.kind === "notFound" ? "That is not access you can change. Nothing was saved." : r.kind === "reject" ? r.errors.join(" ") : saveFailureText(r, "That could not be saved. Nothing was changed.");
|
|
24480
24571
|
const problem = failure(saved) ?? failure(gone);
|
|
24481
24572
|
const loses = person.access.length ? person.access.map((a) => a.kind === "everything" ? "everything on this Clearotron" : a.name ?? a.key) : [];
|
|
24482
24573
|
const holdsKey = (person.keys ?? 0) > 0;
|
|
@@ -24640,6 +24731,21 @@ function Form({ ctx, person, view }) {
|
|
|
24640
24731
|
] })]
|
|
24641
24732
|
})
|
|
24642
24733
|
}) : null,
|
|
24734
|
+
readOnlyPeople ? (0, import_jsx_runtime.jsx)("div", {
|
|
24735
|
+
className: "notice quiet",
|
|
24736
|
+
style: { marginBottom: 14 },
|
|
24737
|
+
children: (0, import_jsx_runtime.jsxs)("p", {
|
|
24738
|
+
style: {
|
|
24739
|
+
margin: 0,
|
|
24740
|
+
fontSize: 13.5,
|
|
24741
|
+
color: "var(--text-muted)"
|
|
24742
|
+
},
|
|
24743
|
+
children: [(0, import_jsx_runtime.jsx)("b", {
|
|
24744
|
+
style: { color: "var(--text-strong)" },
|
|
24745
|
+
children: "This Clearotron signs in one person: you."
|
|
24746
|
+
}), " Nobody else can be changed or removed here. To hold more than one person, put it behind a login system such as your company single sign-on."]
|
|
24747
|
+
})
|
|
24748
|
+
}) : null,
|
|
24643
24749
|
(0, import_jsx_runtime.jsxs)("div", {
|
|
24644
24750
|
style: {
|
|
24645
24751
|
display: "flex",
|
|
@@ -24650,7 +24756,7 @@ function Form({ ctx, person, view }) {
|
|
|
24650
24756
|
(0, import_jsx_runtime.jsx)("button", {
|
|
24651
24757
|
type: "button",
|
|
24652
24758
|
className: "btn-primary",
|
|
24653
|
-
disabled: !dirty || busy || leftWithNothing,
|
|
24759
|
+
disabled: !dirty || busy || leftWithNothing || readOnlyPeople,
|
|
24654
24760
|
onClick: () => void save(),
|
|
24655
24761
|
children: busy && !confirming ? "Saving…" : "Save changes"
|
|
24656
24762
|
}),
|
|
@@ -24768,10 +24874,10 @@ function Form({ ctx, person, view }) {
|
|
|
24768
24874
|
type: "button",
|
|
24769
24875
|
className: "pill",
|
|
24770
24876
|
style: {
|
|
24771
|
-
cursor: "pointer",
|
|
24877
|
+
cursor: readOnlyPeople ? "default" : "pointer",
|
|
24772
24878
|
fontSize: 12
|
|
24773
24879
|
},
|
|
24774
|
-
disabled: busy,
|
|
24880
|
+
disabled: busy || readOnlyPeople,
|
|
24775
24881
|
onClick: () => setConfirming(true),
|
|
24776
24882
|
children: whole ? domain ? `Remove everyone at ${domain}` : "Remove from Clearotron" : `Remove from ${[...held.orgs].map(orgName).join(", ") || "here"}`
|
|
24777
24883
|
}), whole ? null : (0, import_jsx_runtime.jsx)("p", {
|
|
@@ -2082,6 +2082,28 @@ input.filter {
|
|
|
2082
2082
|
Quiet on purpose. These are runs the reader has already dealt with, and the whole point of the
|
|
2083
2083
|
feature is that they stop competing with live work for attention — a panel styled like the queue
|
|
2084
2084
|
would put them back in the fight it just took them out of. */
|
|
2085
|
+
/* WHAT STOPPED RECENTLY — louder than acknowledged, quieter than live work.
|
|
2086
|
+
The count carries the page's only standing signal that something failed, so it is readable rather
|
|
2087
|
+
than faint; the fold below it is the part that keeps the dashboard about what is happening now. */
|
|
2088
|
+
.home2-stopped { margin-top: 18px; }
|
|
2089
|
+
.home2-stopped-toggle {
|
|
2090
|
+
padding: 0; background: none; border: 0; cursor: pointer; font: inherit; font-size: 13.5px;
|
|
2091
|
+
font-weight: 600; color: var(--text-strong); text-decoration: underline;
|
|
2092
|
+
}
|
|
2093
|
+
.home2-stopped-toggle:hover { color: var(--text-accent); }
|
|
2094
|
+
.home2-stopped .home2-cards { margin-top: 11px; }
|
|
2095
|
+
|
|
2096
|
+
/* THE UNKNOWN DOOR — a sentence the reader must not skip, and the alternative under its own heading.
|
|
2097
|
+
Both are quiet relative to the steps themselves: the primary set is still what to try first. */
|
|
2098
|
+
.steps-unknown {
|
|
2099
|
+
margin: 0 0 12px; font-size: 12.5px; line-height: 1.5; color: var(--text-muted);
|
|
2100
|
+
border-left: 2px solid var(--border-hairline); padding-left: 10px;
|
|
2101
|
+
}
|
|
2102
|
+
.steps-alt { margin-top: 20px; padding-top: 14px; border-top: 1px solid var(--border-hairline); }
|
|
2103
|
+
.steps-alt-head {
|
|
2104
|
+
margin: 0 0 10px; font-size: 12.5px; font-weight: 600; letter-spacing: .02em; color: var(--text-muted);
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2085
2107
|
.home2-acked { margin-top: 13px; }
|
|
2086
2108
|
.home2-acked-toggle {
|
|
2087
2109
|
padding: 0; background: none; border: 0; cursor: pointer; font: inherit; font-size: 12.5px;
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
-->
|
|
50
50
|
<link rel="preconnect" href="https://api.fontshare.com" crossorigin />
|
|
51
51
|
<link href="https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700,900&display=swap" rel="stylesheet" />
|
|
52
|
-
<script type="module" crossorigin src="/portal/assets/index-
|
|
53
|
-
<link rel="stylesheet" crossorigin href="/portal/assets/index-
|
|
52
|
+
<script type="module" crossorigin src="/portal/assets/index-ChIQsMYp.js"></script>
|
|
53
|
+
<link rel="stylesheet" crossorigin href="/portal/assets/index-DBIs21e4.css">
|
|
54
54
|
</head>
|
|
55
55
|
<body>
|
|
56
56
|
<div id="root"></div>
|
package/portal-ui/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "portal-ui",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.3.1
|
|
5
|
+
"version": "0.3.1",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
7
7
|
"description": "The unified trademark portal UI. One address, one login: who you are decides what you see. Built as a static bundle, served by driver/portal-service.mjs — the browser never reaches profile-service or recipe-service.",
|
|
8
8
|
"engines": {
|
|
@@ -713,12 +713,63 @@ const OWN_NUMBER = /^\s*(?:at\s+\d|:\d)/;
|
|
|
713
713
|
// The cost of naming a file is that a REAL bare citation added there also escapes. That is the same trade
|
|
714
714
|
// the reference-strip signatures make for the same reason, and it is preferable to a pattern, which would
|
|
715
715
|
// quietly widen to files nobody considered.
|
|
716
|
-
|
|
716
|
+
// ── EXEMPT FROM THE RATCHET, AND ONLY WHERE SOMETHING STRONGER CHECKS THE SAME CITATIONS ───────────
|
|
717
|
+
//
|
|
718
|
+
// The ratchet refuses a NEW bare citation, which is right everywhere it is the only check. It is not the
|
|
719
|
+
// only check on `contract-e3-backlog.mjs`: `contract-audit.test.mjs` reads each backlog row's own
|
|
720
|
+
// evidence, finds where that dictation now starts, and refuses a row whose number points away from it.
|
|
721
|
+
// That is a stronger guarantee than "a symbol sits beside the number" — it verifies the number itself.
|
|
722
|
+
//
|
|
723
|
+
// THE TWO ARE IN DIRECT CONFLICT ON THAT FILE, and the conflict is not hypothetical: an edit to
|
|
724
|
+
// `stages.mjs` moves those rows, the evidence guard then DEMANDS they be repointed, and every repointed
|
|
725
|
+
// row reads to the ratchet as a newly added bare citation. Under both rules at once the only permitted
|
|
726
|
+
// change to that file is no change at all. Measured on the change that found this: 31 rows repointed at
|
|
727
|
+
// the evidence guard's own instruction, 31 refused by the ratchet.
|
|
728
|
+
//
|
|
729
|
+
// So the file is exempt from the weaker check and stays under the stronger one. Anything NOT covered by
|
|
730
|
+
// an evidence guard does not belong on this list — the exemption is earned by being checked better, not
|
|
731
|
+
// by being inconvenient.
|
|
732
|
+
//
|
|
733
|
+
// `contract-vocabulary.mjs` is the same conflict one file over, and it arrives by the same route. Each
|
|
734
|
+
// INNER_CODES row carries a `mints` array naming the line that WRITES its code, and
|
|
735
|
+
// `contract-audit.test.mjs` re-reads every one of them on every run: it checks the cited line still
|
|
736
|
+
// mints that code, and when it does not it prints where the code is NOW and refuses. That arm's own
|
|
737
|
+
// message says "Do not delete the citation: it is what makes the ruling checkable rather than merely
|
|
738
|
+
// written down" — so the ratchet's remedy, dropping the number, is the one repair that file forbids.
|
|
739
|
+
//
|
|
740
|
+
// Measured on the change that added this: a 41-line insertion into `connotation-search.mjs` moved 12
|
|
741
|
+
// mint sites, the audit demanded all 12 be repointed, and the ratchet then read all 12 as newly added
|
|
742
|
+
// bare citations. Under both rules at once the only permitted change is no change at all.
|
|
743
|
+
//
|
|
744
|
+
// The alternative considered and rejected: teach the audit's `m.split(":")` to accept `file:line symbol`
|
|
745
|
+
// and tag the 12. It widens a parser every INNER_CODES row depends on, to buy a weaker guarantee than
|
|
746
|
+
// the one already in place — the audit verifies the NUMBER, which is what a symbol only approximates.
|
|
747
|
+
//
|
|
748
|
+
// SO THE EXEMPTION IS SCOPED TO THE LINES THE AUDIT ACTUALLY COVERS, not to the file. Measured on this
|
|
749
|
+
// tree: `contract-vocabulary.mjs` carries 117 numbered citations, 21 of them inside `mints` arrays and 96
|
|
750
|
+
// outside. A file-wide entry would have taken the ratchet off all 117 to protect 21 — and the 96 are
|
|
751
|
+
// precisely the prose and `site:` citations nothing else checks. Every `mints` array is written on one
|
|
752
|
+
// line, so a `when` pattern matching that line is exact rather than approximate.
|
|
753
|
+
//
|
|
754
|
+
// An entry is either a bare path (the whole file, for the two cases where the whole file earns it) or
|
|
755
|
+
// `{ file, when }`, where `when` must match the LINE the citation sits on. A `when` that stops matching
|
|
756
|
+
// re-arms the ratchet over those lines, which is the failure direction worth having.
|
|
757
|
+
export const RATCHET_EXEMPT = [
|
|
758
|
+
"driver/test/a-new-citation-carries-something-that-can-be-checked.test.mjs",
|
|
759
|
+
"driver/contract-e3-backlog.mjs",
|
|
760
|
+
// The `mints` lines only: `contract-audit.test.mjs` verifies each of those numbers against the line
|
|
761
|
+
// that writes the code. Everything else in this file stays under the ratchet.
|
|
762
|
+
{ file: "driver/contract-vocabulary.mjs", when: /mints:\s*\[/ },
|
|
763
|
+
];
|
|
764
|
+
|
|
765
|
+
/** Whether a citation on `text` in `file` is exempt from the ratchet. PURE. */
|
|
766
|
+
export const ratchetExempt = (file, text) => RATCHET_EXEMPT.some((e) =>
|
|
767
|
+
(typeof e === "string" ? e === file : e.file === file && e.when.test(String(text ?? ""))));
|
|
717
768
|
|
|
718
769
|
export function newBareCitations(addedLines) {
|
|
719
770
|
const out = [];
|
|
720
771
|
for (const { file, line, text } of addedLines ?? []) {
|
|
721
|
-
if (
|
|
772
|
+
if (ratchetExempt(file, text)) continue;
|
|
722
773
|
for (const m of String(text ?? "").matchAll(CITE_RE)) {
|
|
723
774
|
// The same discriminator the corpus scan uses: a second `:number` closing a paren is a V8 stack
|
|
724
775
|
// frame, not a citation. Reading them as citations would refuse a fixture that captured a trace.
|