@rubytech/create-maxy-code 0.1.219 → 0.1.222
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/dist/index.js +46 -1
- package/package.json +1 -1
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js +60 -23
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.d.ts +4 -1
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +17 -5
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/__tests__/action-provenance-gate.test.ts +58 -31
- package/payload/platform/lib/graph-write/src/index.ts +16 -7
- package/payload/platform/plugins/admin/PLUGIN.md +2 -3
- package/payload/platform/plugins/admin/__tests__/admin-can-enumerate.test.sh +129 -0
- package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +1 -2
- package/payload/platform/plugins/admin/hooks/lib/hook-emit.sh +3 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js +41 -9
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/references/publish-site-routing.md +44 -0
- package/payload/platform/plugins/admin/skills/admin-user-management/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +11 -4
- package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +2 -2
- package/payload/platform/plugins/contacts/PLUGIN.md +2 -2
- package/payload/platform/plugins/docs/references/internals.md +1 -1
- package/payload/platform/plugins/docs/references/platform.md +2 -2
- package/payload/platform/plugins/docs/references/troubleshooting.md +7 -0
- package/payload/platform/plugins/memory/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/references/graph-primitives.md +2 -2
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.d.ts +2 -1
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.d.ts.map +1 -1
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.js +27 -21
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.js.map +1 -1
- package/payload/platform/scripts/check-no-esm-require.mjs +3 -0
- package/payload/platform/scripts/setup-account.sh +0 -15
- package/payload/platform/services/claude-session-manager/dist/auth-snapshot.d.ts +4 -0
- package/payload/platform/services/claude-session-manager/dist/auth-snapshot.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/auth-snapshot.js +50 -0
- package/payload/platform/services/claude-session-manager/dist/auth-snapshot.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +19 -2
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +39 -19
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +24 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +100 -11
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts +8 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +7 -2
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -1
- package/payload/server/public/assets/{AdminShell-BcxEZnpu.js → AdminShell-BiEwDSRL.js} +1 -1
- package/payload/server/public/assets/{Checkbox-DL-HdT29.js → Checkbox-COiFXiXL.js} +1 -1
- package/payload/server/public/assets/{admin-DMo5DWKH.js → admin-Ca9ckLZx.js} +1 -1
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BoUw4oYz.js → architectureDiagram-Q4EWVU46-DI6JZI13.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DOscnO3m.js → blockDiagram-DXYQGD6D-dgDvswLd.js} +1 -1
- package/payload/server/public/assets/{brand-SZW-27T7.css → brand-CAMMU5lz.css} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-aZ1IuZ_F.js → c4Diagram-AHTNJAMY-odaGY4GZ.js} +1 -1
- package/payload/server/public/assets/channel-DfwLbKck.js +1 -0
- package/payload/server/public/assets/{chunk-336JU56O-DS3ksXBG.js → chunk-336JU56O-BpbY8F27.js} +2 -2
- package/payload/server/public/assets/{chunk-426QAEUC-BLRn2STO.js → chunk-426QAEUC-BWbVhEVC.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-BrhOvJ01.js → chunk-4TB4RGXK-C3IfRdvA.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-BZZwZX5Q.js → chunk-5FUZZQ4R-CSRMBAGr.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-YXZKnPCz.js → chunk-5PVQY5BW-CGAe6GJJ.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-DAMCJHKQ.js → chunk-EDXVE4YY-BnXC8cYQ.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-DsD95Bqn.js → chunk-ENJZ2VHE-7qfDalAB.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DxtrRClx.js → chunk-ICPOFSXX-TRvVGIDw.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-CN4kLK-H.js → chunk-OYMX7WX6-Cfv8vBvI.js} +1 -1
- package/payload/server/public/assets/{chunk-U2HBQHQK-BPTv3Oj1.js → chunk-U2HBQHQK-BfLvbEyu.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-DxBry33v.js → chunk-X2U36JSP-Cj3k-dYC.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-BZ4N75oy.js → chunk-YZCP3GAM-CygW6I0h.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-Dqr161Q1.js → chunk-ZZ45TVLE-CBF3hFJH.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CE0XZQre.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-Bj41Y3XD.js +1 -0
- package/payload/server/public/assets/clone-DTd5nONj.js +1 -0
- package/payload/server/public/assets/{dagre-DtZ0wKkH.js → dagre-BTGABQJX.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-BPKhT-ZX.js → dagre-KV5264BT-B2lbkuGd.js} +1 -1
- package/payload/server/public/assets/{data-D6IQdm0R.js → data-CHdaTjgn.js} +1 -1
- package/payload/server/public/assets/{diagram-5BDNPKRD-CATnafi7.js → diagram-5BDNPKRD-BUK4LqT8.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-ChZYQfji.js → diagram-G4DWMVQ6-e49HrvxL.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DHW6kYd3.js → diagram-MMDJMWI5-k4Kx_qWe.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CnS4Zci8.js → diagram-TYMM5635-DzavOUdK.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-DKYv2YWp.js → erDiagram-SMLLAGMA-CJKecRhi.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BWvoZWlh.js → flowDiagram-DWJPFMVM-GM7H1TDt.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CpCb-xKy.js → ganttDiagram-T4ZO3ILL-kDEA1A-3.js} +1 -1
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CvHrDKQ-.js → gitGraphDiagram-UUTBAWPF-jVZZdeg2.js} +1 -1
- package/payload/server/public/assets/{graph-labels-CWJy0O4w.js → graph-labels-CavkO4DG.js} +1 -1
- package/payload/server/public/assets/{graph-PkZKVkWD.js → graph-tqCqSngu.js} +1 -1
- package/payload/server/public/assets/{graphlib-DjnyHqGy.js → graphlib-DFU2rJiY.js} +1 -1
- package/payload/server/public/assets/{infoDiagram-42DDH7IO-D_MywcVn.js → infoDiagram-42DDH7IO-CFxVoRBy.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-N9m-f-Eb.js → ishikawaDiagram-UXIWVN3A-CDvZs3Ua.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DE0q7uSM.js → journeyDiagram-VCZTEJTY-DUI1m9vW.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-Dj5hb9Bq.js → kanban-definition-6JOO6SKY-C_jSVK7W.js} +1 -1
- package/payload/server/public/assets/{line-C6ph0i4M.js → line-DfwJeo6J.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-BrfmEvCA.js → mermaid-parser.core-BN-2kBfU.js} +1 -1
- package/payload/server/public/assets/{mermaid.core-DYnHKfDv.js → mermaid.core-C6amhHAQ.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-DaSaZfhR.js → mindmap-definition-QFDTVHPH-eTKDlw3c.js} +1 -1
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-BAu9Ezbv.js → pieDiagram-DEJITSTG-DNX5VeMZ.js} +1 -1
- package/payload/server/public/assets/{public-UKgkJxKv.js → public-BADBLzOZ.js} +3 -3
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-DRsbL1Xd.js → quadrantDiagram-34T5L4WZ-BFnmYiHX.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-B9QcsXV6.js → requirementDiagram-MS252O5E-DvHvTyzu.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DsZbvIlD.js → sankeyDiagram-XADWPNL6-CMFnNT1G.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BlT4O2N8.js → sequenceDiagram-FGHM5R23-BYon1ouU.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-JFgk1K3Y.js → stateDiagram-FHFEXIEX-n6l3hcuJ.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CS46-Mhi.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DOsHw1ip.js → timeline-definition-GMOUNBTQ-DFPFHrQF.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-JDGvwvIk.js → useSelectionMode-iqIyL24g.js} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CQWIhqZY.js → vennDiagram-DHZGUBPP-DZLturYa.js} +1 -1
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-D5Fh7YVL.js → wardleyDiagram-NUSXRM2D-BBt2pdjM.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-C-tINQEs.js → xychartDiagram-5P7HB3ND-DOZxLvEi.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +6 -6
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +49 -6
- package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-admin-tool-gate.test.sh +0 -109
- package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-base64-guard.test.sh +0 -204
- package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-memory-write-passthrough.test.sh +0 -118
- package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +0 -337
- package/payload/server/public/assets/channel-CzcSNxDm.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DFOM3vCn.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-HplDfaL1.js +0 -1
- package/payload/server/public/assets/clone-G-2rqnSR.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-NXz27DaV.js +0 -1
- /package/payload/server/public/assets/{brand-SzZmKKjz.js → brand-DfykKss4.js} +0 -0
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Data — Maxy</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/data-
|
|
8
|
+
<script type="module" crossorigin src="/assets/data-CHdaTjgn.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-DD-I1_y5.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/brand-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/graph-labels-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/AdminShell-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/brand-
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/brand-DfykKss4.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/graph-labels-CavkO4DG.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/AdminShell-BiEwDSRL.js">
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/brand-CAMMU5lz.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="root"></div>
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Graph — Maxy</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/graph-
|
|
8
|
+
<script type="module" crossorigin src="/assets/graph-tqCqSngu.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-DD-I1_y5.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/brand-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/graph-labels-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/AdminShell-
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/Checkbox-
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/brand-
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/brand-DfykKss4.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/graph-labels-CavkO4DG.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/AdminShell-BiEwDSRL.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/Checkbox-COiFXiXL.js">
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/brand-CAMMU5lz.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Maxy</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/admin-
|
|
8
|
+
<script type="module" crossorigin src="/assets/admin-Ca9ckLZx.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-DD-I1_y5.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/brand-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/AdminShell-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/useSelectionMode-
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/Checkbox-
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/brand-
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/brand-DfykKss4.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/AdminShell-BiEwDSRL.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/useSelectionMode-iqIyL24g.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/Checkbox-COiFXiXL.js">
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/brand-CAMMU5lz.css">
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/admin-CWMpccrR.css">
|
|
16
16
|
<link rel="stylesheet" href="/brand-defaults.css">
|
|
17
17
|
</head>
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Maxy</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/public-
|
|
8
|
+
<script type="module" crossorigin src="/assets/public-BADBLzOZ.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-DD-I1_y5.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/brand-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/useSelectionMode-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/Checkbox-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/brand-
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/brand-DfykKss4.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/useSelectionMode-iqIyL24g.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/Checkbox-COiFXiXL.js">
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/brand-CAMMU5lz.css">
|
|
14
14
|
<link rel="stylesheet" href="/brand-defaults.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
package/payload/server/server.js
CHANGED
|
@@ -824,13 +824,10 @@ import { monitorEventLoopDelay } from "perf_hooks";
|
|
|
824
824
|
// app/lib/agent-slug-pattern.ts
|
|
825
825
|
var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
|
|
826
826
|
|
|
827
|
-
// server/routes/health.ts
|
|
828
|
-
import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
|
|
829
|
-
import { createConnection as createConnection2 } from "net";
|
|
830
|
-
|
|
831
827
|
// app/lib/claude-auth.ts
|
|
832
828
|
import { readFileSync, writeFileSync } from "fs";
|
|
833
829
|
import { sep } from "path";
|
|
830
|
+
import { lock } from "proper-lockfile";
|
|
834
831
|
if (!CLAUDE_CREDENTIALS_FILE.startsWith(MAXY_DIR + sep)) {
|
|
835
832
|
console.error(
|
|
836
833
|
`[claude-auth] WARN cross-brand-path-detected resolved=${CLAUDE_CREDENTIALS_FILE} brand=${BRAND_NAME} maxyDir=${MAXY_DIR}`
|
|
@@ -929,11 +926,39 @@ function logRefreshOutcome(outcome) {
|
|
|
929
926
|
);
|
|
930
927
|
}
|
|
931
928
|
async function doRefresh() {
|
|
929
|
+
const pre = readCredentials();
|
|
930
|
+
if (!pre) return { status: "missing" };
|
|
931
|
+
if (!pre.refreshToken) return { status: "dead", expiresAt: pre.expiresAt };
|
|
932
|
+
let release = null;
|
|
933
|
+
try {
|
|
934
|
+
release = await lock(CLAUDE_CREDENTIALS_FILE, {
|
|
935
|
+
retries: { retries: 8, minTimeout: 100, maxTimeout: 1e3, factor: 1.5 },
|
|
936
|
+
stale: 3e4
|
|
937
|
+
});
|
|
938
|
+
} catch (err) {
|
|
939
|
+
console.error(
|
|
940
|
+
`[auth-refresh] op=lock-acquire-failed err=${err instanceof Error ? err.message : String(err)}`
|
|
941
|
+
);
|
|
942
|
+
logRefreshOutcome("fail-network");
|
|
943
|
+
return { status: "expired", expiresAt: pre.expiresAt };
|
|
944
|
+
}
|
|
945
|
+
console.log(`[auth-refresh] op=lock-acquired pid=${process.pid} path=${CLAUDE_CREDENTIALS_FILE}`);
|
|
946
|
+
try {
|
|
947
|
+
return await doRefreshLocked();
|
|
948
|
+
} finally {
|
|
949
|
+
await release().catch(() => {
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
async function doRefreshLocked() {
|
|
932
954
|
const creds = readCredentials();
|
|
933
955
|
if (!creds) return { status: "missing" };
|
|
934
956
|
if (!creds.refreshToken) return { status: "dead", expiresAt: creds.expiresAt };
|
|
935
957
|
const now = Date.now();
|
|
936
958
|
if (creds.expiresAt - now > EXPIRING_THRESHOLD_MS) {
|
|
959
|
+
console.log(
|
|
960
|
+
`[auth-refresh] op=skipped-fresh expiresAt=${new Date(creds.expiresAt).toISOString()}`
|
|
961
|
+
);
|
|
937
962
|
return { status: "ok", expiresAt: creds.expiresAt };
|
|
938
963
|
}
|
|
939
964
|
let res;
|
|
@@ -989,13 +1014,26 @@ async function doRefresh() {
|
|
|
989
1014
|
expiresIn: typeof expiresIn === "number" && expiresIn > 0 ? expiresIn : void 0
|
|
990
1015
|
});
|
|
991
1016
|
if (newExpiresAt) {
|
|
992
|
-
console.log(`[auth-refresh]
|
|
1017
|
+
console.log(`[auth-refresh] op=renewed expiresAt=${new Date(newExpiresAt).toISOString()}`);
|
|
993
1018
|
} else {
|
|
994
|
-
console.warn("[auth-refresh] write-back
|
|
1019
|
+
console.warn("[auth-refresh] op=renewed-write-back-failed");
|
|
995
1020
|
}
|
|
996
1021
|
logRefreshOutcome("ok");
|
|
997
1022
|
return { status: "ok", expiresAt: newExpiresAt ?? Date.now() + 3600 * 1e3 };
|
|
998
1023
|
}
|
|
1024
|
+
function startAuthHealthHeartbeat(intervalMs = 5 * 60 * 1e3) {
|
|
1025
|
+
const tick = () => {
|
|
1026
|
+
const h = computeAuthHealth();
|
|
1027
|
+
const expiresIn = h.expiresAt != null ? Math.round((h.expiresAt - Date.now()) / 1e3) : null;
|
|
1028
|
+
console.log(
|
|
1029
|
+
`[auth-health] brand=${BRAND_NAME} status=${h.status} expiresIn=${expiresIn != null ? expiresIn + "s" : "n/a"}`
|
|
1030
|
+
);
|
|
1031
|
+
};
|
|
1032
|
+
tick();
|
|
1033
|
+
const handle = setInterval(tick, intervalMs);
|
|
1034
|
+
if (typeof handle.unref === "function") handle.unref();
|
|
1035
|
+
return () => clearInterval(handle);
|
|
1036
|
+
}
|
|
999
1037
|
async function ensureAuth() {
|
|
1000
1038
|
const health = assessAuthHealth();
|
|
1001
1039
|
if (health.status === "ok" || health.status === "missing" || health.status === "dead") {
|
|
@@ -1004,6 +1042,10 @@ async function ensureAuth() {
|
|
|
1004
1042
|
return refreshAccessToken();
|
|
1005
1043
|
}
|
|
1006
1044
|
|
|
1045
|
+
// server/routes/health.ts
|
|
1046
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
|
|
1047
|
+
import { createConnection as createConnection2 } from "net";
|
|
1048
|
+
|
|
1007
1049
|
// app/lib/network.ts
|
|
1008
1050
|
import { networkInterfaces } from "os";
|
|
1009
1051
|
function getLanIp() {
|
|
@@ -15161,6 +15203,7 @@ var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
|
15161
15203
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
15162
15204
|
var httpServer = serve({ fetch: app41.fetch, port, hostname });
|
|
15163
15205
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
15206
|
+
startAuthHealthHeartbeat();
|
|
15164
15207
|
{
|
|
15165
15208
|
const loopHist = monitorEventLoopDelay({ resolution: 50 });
|
|
15166
15209
|
loopHist.enable();
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Task 571 — the orchestration-only block has been removed. Admin's standard
|
|
3
|
-
# tool surface (Write/Edit/MultiEdit/Bash/WebFetch/WebSearch) is callable
|
|
4
|
-
# directly from the admin seat; delegation is a doctrine bias enforced in
|
|
5
|
-
# IDENTITY.md, not a platform refusal. This test pins the post-571 contract:
|
|
6
|
-
#
|
|
7
|
-
# - Admin-direct Bash/Edit/Write/WebFetch all return exit 0 with no
|
|
8
|
-
# `reason=orchestration-only` or `reason=specialist-owned` line.
|
|
9
|
-
# - The resource-specific hook blocks (code-dir, entitlement, account.json,
|
|
10
|
-
# pending-actions, base64-guard, approval-queue) are unaffected and live
|
|
11
|
-
# in their own dedicated tests.
|
|
12
|
-
#
|
|
13
|
-
# Reintroducing the orchestration-only block requires a doctrine flip — this
|
|
14
|
-
# test fails first and surfaces the change loudly.
|
|
15
|
-
|
|
16
|
-
set -u
|
|
17
|
-
|
|
18
|
-
HOOK="$(cd "$(dirname "$0")/.." && pwd)/pre-tool-use.sh"
|
|
19
|
-
if [[ ! -x "$HOOK" ]]; then
|
|
20
|
-
echo "FAIL: $HOOK not executable" >&2
|
|
21
|
-
exit 1
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
TMPFILES=()
|
|
25
|
-
cleanup() {
|
|
26
|
-
for f in "${TMPFILES[@]:-}"; do
|
|
27
|
-
[[ -n "$f" ]] && rm -f "$f" 2>/dev/null || true
|
|
28
|
-
done
|
|
29
|
-
}
|
|
30
|
-
trap cleanup EXIT
|
|
31
|
-
|
|
32
|
-
PASS=0; FAIL=0
|
|
33
|
-
pass() { PASS=$((PASS+1)); echo "PASS: $1"; }
|
|
34
|
-
fail() { FAIL=$((FAIL+1)); echo "FAIL: $1" >&2; }
|
|
35
|
-
|
|
36
|
-
SESSION_TRANSCRIPT="/tmp/sess/44283ae1-2b82-4bd7-b8af-99a11141ed4b.jsonl"
|
|
37
|
-
|
|
38
|
-
# run_admin_direct <tool> <expected_rc> <label>
|
|
39
|
-
# Pipes a minimal PreToolUse payload to the hook with MAXY_SESSION_ROLE=admin
|
|
40
|
-
# (the realistic admin spawn shape) and asserts the rc and that no
|
|
41
|
-
# orchestration-only / specialist-owned refusal line appears on stderr.
|
|
42
|
-
run_admin_direct() {
|
|
43
|
-
local tool="$1" expected_rc="$2" label="$3"
|
|
44
|
-
local input_json
|
|
45
|
-
input_json=$(python3 -c '
|
|
46
|
-
import json, sys
|
|
47
|
-
print(json.dumps({
|
|
48
|
-
"hook_event_name": "PreToolUse",
|
|
49
|
-
"tool_name": sys.argv[1],
|
|
50
|
-
"tool_input": {"file_path": "/tmp/x", "content": "x", "command": "echo hi", "url": "https://example.com"},
|
|
51
|
-
"transcript_path": sys.argv[2],
|
|
52
|
-
}, separators=(",", ":")))
|
|
53
|
-
' "$tool" "$SESSION_TRANSCRIPT")
|
|
54
|
-
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
55
|
-
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
56
|
-
printf '%s' "$input_json" | \
|
|
57
|
-
env -u MAXY_SPECIALIST MAXY_SESSION_ROLE=admin \
|
|
58
|
-
bash "$HOOK" admin >"$stdout_file" 2>"$stderr_file"
|
|
59
|
-
local rc=$?
|
|
60
|
-
if [[ "$rc" -ne "$expected_rc" ]]; then
|
|
61
|
-
fail "$label: expected exit $expected_rc, got $rc. Stderr: $(cat "$stderr_file")"
|
|
62
|
-
return
|
|
63
|
-
fi
|
|
64
|
-
if grep -qE 'reason=orchestration-only|reason=specialist-owned|reason=missing-marker' "$stderr_file"; then
|
|
65
|
-
fail "$label: stderr carries a removed-gate reason line. Got: $(cat "$stderr_file")"
|
|
66
|
-
return
|
|
67
|
-
fi
|
|
68
|
-
pass "$label"
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
# ── Admin-direct passthrough (the Task 571 regression boundary) ──────────
|
|
72
|
-
run_admin_direct Bash 0 "Test 1: admin-direct Bash passes through"
|
|
73
|
-
run_admin_direct Edit 0 "Test 2: admin-direct Edit passes through (path /tmp/x is outside protected dirs)"
|
|
74
|
-
run_admin_direct Write 0 "Test 3: admin-direct Write passes through"
|
|
75
|
-
run_admin_direct MultiEdit 0 "Test 4: admin-direct MultiEdit passes through"
|
|
76
|
-
run_admin_direct WebFetch 0 "Test 5: admin-direct WebFetch passes through"
|
|
77
|
-
run_admin_direct WebSearch 0 "Test 6: admin-direct WebSearch passes through"
|
|
78
|
-
run_admin_direct Read 0 "Test 7: admin-direct Read passes through"
|
|
79
|
-
|
|
80
|
-
# ── Resource-specific blocks unaffected: protected code-dir Write still refuses ──
|
|
81
|
-
# Verifies the deletion was surgical — code-dir-protection-ui still fires.
|
|
82
|
-
PROTECTED_INPUT=$(python3 -c '
|
|
83
|
-
import json, sys
|
|
84
|
-
print(json.dumps({
|
|
85
|
-
"hook_event_name": "PreToolUse",
|
|
86
|
-
"tool_name": "Write",
|
|
87
|
-
"tool_input": {"file_path": "/srv/maxy/platform/ui/app/page.tsx", "content": "x"},
|
|
88
|
-
"transcript_path": sys.argv[1],
|
|
89
|
-
}, separators=(",", ":")))
|
|
90
|
-
' "$SESSION_TRANSCRIPT")
|
|
91
|
-
PROT_STDOUT=$(mktemp); TMPFILES+=("$PROT_STDOUT")
|
|
92
|
-
PROT_STDERR=$(mktemp); TMPFILES+=("$PROT_STDERR")
|
|
93
|
-
printf '%s' "$PROTECTED_INPUT" | \
|
|
94
|
-
env -u MAXY_SPECIALIST MAXY_SESSION_ROLE=admin \
|
|
95
|
-
bash "$HOOK" admin >"$PROT_STDOUT" 2>"$PROT_STDERR"
|
|
96
|
-
PROT_RC=$?
|
|
97
|
-
if [[ "$PROT_RC" -eq 2 ]] && grep -q 'cannot modify platform UI source' "$PROT_STDERR"; then
|
|
98
|
-
pass "Test 8: admin-direct Write to platform/ui/ still blocked (resource-specific block intact)"
|
|
99
|
-
else
|
|
100
|
-
fail "Test 8: expected platform/ui code-dir block, got rc=$PROT_RC stdout=$(cat "$PROT_STDOUT") stderr=$(cat "$PROT_STDERR")"
|
|
101
|
-
fi
|
|
102
|
-
|
|
103
|
-
echo
|
|
104
|
-
echo "──────── pre-tool-use admin-tool-gate test summary (post-Task 571) ────────"
|
|
105
|
-
echo "PASS: $PASS"
|
|
106
|
-
echo "FAIL: $FAIL"
|
|
107
|
-
|
|
108
|
-
[[ "$FAIL" -gt 0 ]] && exit 1
|
|
109
|
-
exit 0
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Regression test for the base64 context-overflow guard.
|
|
3
|
-
#
|
|
4
|
-
# Covers two PreToolUse rejection paths in pre-tool-use.sh:
|
|
5
|
-
#
|
|
6
|
-
# 1. Bash producer guard — `tool_input.command` invoking `base64` (encode
|
|
7
|
-
# direction) or `xxd -p` is rejected; `base64 -d|-D|--decode` is allowed.
|
|
8
|
-
# 2. Write/Edit consumer guard — `tool_input.content` (Write) or
|
|
9
|
-
# `tool_input.new_string` (Edit) carrying `data:<mime>;base64,<≥4096 chars>`
|
|
10
|
-
# is rejected; small inline data URIs and plain content are allowed.
|
|
11
|
-
#
|
|
12
|
-
# Plus a fail-open case (malformed stdin → exit 0 silent) to pin the contract
|
|
13
|
-
# established by the playwright-file-guard test (terminal-stdin guard + parse-
|
|
14
|
-
# error fail-open).
|
|
15
|
-
|
|
16
|
-
set -u
|
|
17
|
-
|
|
18
|
-
HOOK="$(cd "$(dirname "$0")/.." && pwd)/pre-tool-use.sh"
|
|
19
|
-
if [[ ! -x "$HOOK" ]]; then
|
|
20
|
-
echo "FAIL: $HOOK not executable" >&2
|
|
21
|
-
exit 1
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
TMPFILES=()
|
|
25
|
-
cleanup_test_state() {
|
|
26
|
-
for f in "${TMPFILES[@]:-}"; do
|
|
27
|
-
[[ -n "$f" ]] && rm -f "$f" 2>/dev/null || true
|
|
28
|
-
done
|
|
29
|
-
}
|
|
30
|
-
trap cleanup_test_state EXIT
|
|
31
|
-
|
|
32
|
-
PASS=0
|
|
33
|
-
FAIL=0
|
|
34
|
-
pass() { echo "PASS: $1"; PASS=$((PASS + 1)); }
|
|
35
|
-
fail() { echo "FAIL: $1" >&2; FAIL=$((FAIL + 1)); }
|
|
36
|
-
|
|
37
|
-
# Helper: run hook with Bash tool_input.command and assert exit code + stderr.
|
|
38
|
-
run_bash() {
|
|
39
|
-
local command_text="$1"; local expected_rc="$2"; local stderr_pattern="$3"; local label="$4"
|
|
40
|
-
local input_json
|
|
41
|
-
# Build the input JSON via python3 so command_text with quotes / specials is safe.
|
|
42
|
-
input_json=$(python3 -c '
|
|
43
|
-
import json, sys
|
|
44
|
-
print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Bash", "tool_input": {"command": sys.argv[1]}, "transcript_path": "/tmp/sess/parent.jsonl", "parent_tool_use_id": "toolu_test_subagent"}, separators=(",", ":")))
|
|
45
|
-
' "$command_text")
|
|
46
|
-
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
47
|
-
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
48
|
-
printf '%s' "$input_json" | bash "$HOOK" admin >"$stdout_file" 2>"$stderr_file"
|
|
49
|
-
local rc=$?
|
|
50
|
-
if [[ "$rc" -ne "$expected_rc" ]]; then
|
|
51
|
-
fail "$label: expected exit $expected_rc, got $rc. Stderr: $(cat "$stderr_file")"
|
|
52
|
-
return
|
|
53
|
-
fi
|
|
54
|
-
if [[ -n "$stderr_pattern" ]] && ! grep -qE "$stderr_pattern" "$stderr_file"; then
|
|
55
|
-
fail "$label: stderr missing pattern '$stderr_pattern'. Got: $(cat "$stderr_file")"
|
|
56
|
-
return
|
|
57
|
-
fi
|
|
58
|
-
pass "$label"
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
# Helper: run hook with Write tool_input.content and assert exit code + stderr.
|
|
62
|
-
run_write() {
|
|
63
|
-
local content="$1"; local expected_rc="$2"; local stderr_pattern="$3"; local label="$4"
|
|
64
|
-
local input_json
|
|
65
|
-
input_json=$(python3 -c '
|
|
66
|
-
import json, sys
|
|
67
|
-
print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Write", "tool_input": {"file_path": "/tmp/test.html", "content": sys.argv[1]}, "transcript_path": "/tmp/sess/parent.jsonl", "parent_tool_use_id": "toolu_test_subagent"}, separators=(",", ":")))
|
|
68
|
-
' "$content")
|
|
69
|
-
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
70
|
-
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
71
|
-
printf '%s' "$input_json" | bash "$HOOK" admin >"$stdout_file" 2>"$stderr_file"
|
|
72
|
-
local rc=$?
|
|
73
|
-
if [[ "$rc" -ne "$expected_rc" ]]; then
|
|
74
|
-
fail "$label: expected exit $expected_rc, got $rc. Stderr: $(cat "$stderr_file")"
|
|
75
|
-
return
|
|
76
|
-
fi
|
|
77
|
-
if [[ -n "$stderr_pattern" ]] && ! grep -qE "$stderr_pattern" "$stderr_file"; then
|
|
78
|
-
fail "$label: stderr missing pattern '$stderr_pattern'. Got: $(cat "$stderr_file")"
|
|
79
|
-
return
|
|
80
|
-
fi
|
|
81
|
-
pass "$label"
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
# Helper: run hook with Edit tool_input.new_string and assert exit code.
|
|
85
|
-
run_edit() {
|
|
86
|
-
local new_string="$1"; local expected_rc="$2"; local stderr_pattern="$3"; local label="$4"
|
|
87
|
-
local input_json
|
|
88
|
-
input_json=$(python3 -c '
|
|
89
|
-
import json, sys
|
|
90
|
-
print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Edit", "tool_input": {"file_path": "/tmp/test.html", "old_string": "OLD", "new_string": sys.argv[1]}, "transcript_path": "/tmp/sess/parent.jsonl", "parent_tool_use_id": "toolu_test_subagent"}, separators=(",", ":")))
|
|
91
|
-
' "$new_string")
|
|
92
|
-
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
93
|
-
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
94
|
-
printf '%s' "$input_json" | bash "$HOOK" admin >"$stdout_file" 2>"$stderr_file"
|
|
95
|
-
local rc=$?
|
|
96
|
-
if [[ "$rc" -ne "$expected_rc" ]]; then
|
|
97
|
-
fail "$label: expected exit $expected_rc, got $rc. Stderr: $(cat "$stderr_file")"
|
|
98
|
-
return
|
|
99
|
-
fi
|
|
100
|
-
if [[ -n "$stderr_pattern" ]] && ! grep -qE "$stderr_pattern" "$stderr_file"; then
|
|
101
|
-
fail "$label: stderr missing pattern '$stderr_pattern'. Got: $(cat "$stderr_file")"
|
|
102
|
-
return
|
|
103
|
-
fi
|
|
104
|
-
pass "$label"
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
# Generate a base64-character blob >= 4096 chars (data-URI body trigger).
|
|
108
|
-
LARGE_B64=$(python3 -c "print('A' * 5000)")
|
|
109
|
-
|
|
110
|
-
# ───────── Bash producer guard ──────────────────────────────────────────────
|
|
111
|
-
run_bash "echo hello world" 0 "" \
|
|
112
|
-
"Test 1: bare Bash command (no base64) allowed"
|
|
113
|
-
|
|
114
|
-
run_bash "ls -la" 0 "" \
|
|
115
|
-
"Test 2: ls -la (no base64 token) allowed"
|
|
116
|
-
|
|
117
|
-
run_bash "base64 /tmp/foo.png" 2 '\[pre-tool-use\] guard=base64-tool-result.*tool=Bash.*reason=base64-encoder.*action=reject' \
|
|
118
|
-
"Test 3: 'base64 file' (encode) rejected"
|
|
119
|
-
|
|
120
|
-
run_bash "cat foo.png | base64" 2 '\[pre-tool-use\] guard=base64-tool-result.*action=reject' \
|
|
121
|
-
"Test 4: 'cat | base64' (encode pipeline) rejected"
|
|
122
|
-
|
|
123
|
-
run_bash "cat foo.png|base64 -w0" 2 '\[pre-tool-use\] guard=base64-tool-result.*action=reject' \
|
|
124
|
-
"Test 5: 'base64 -w0' (encode with line-wrap flag) rejected"
|
|
125
|
-
|
|
126
|
-
run_bash "xxd -p file.bin" 2 '\[pre-tool-use\] guard=base64-tool-result.*reason=xxd-plain-hex.*action=reject' \
|
|
127
|
-
"Test 6: 'xxd -p' (plain hex encode) rejected"
|
|
128
|
-
|
|
129
|
-
run_bash "base64 -d input.b64 > output.bin" 0 "" \
|
|
130
|
-
"Test 7: 'base64 -d' (decode direction) allowed"
|
|
131
|
-
|
|
132
|
-
run_bash "base64 --decode < x.b64 > y.bin" 0 "" \
|
|
133
|
-
"Test 8: 'base64 --decode' (decode long-form) allowed"
|
|
134
|
-
|
|
135
|
-
run_bash "echo Zm9v | base64 -d" 0 "" \
|
|
136
|
-
"Test 9: 'base64 -d' decode pipeline allowed"
|
|
137
|
-
|
|
138
|
-
run_bash "echo '--debug-base64-foo'" 0 "" \
|
|
139
|
-
"Test 10: 'base64' substring inside flag name does NOT false-match"
|
|
140
|
-
|
|
141
|
-
run_bash "ls mybase64tool" 0 "" \
|
|
142
|
-
"Test 11: 'base64' substring inside identifier does NOT false-match"
|
|
143
|
-
|
|
144
|
-
run_bash "cat in.b64 | base64 -d > /tmp/foo.bin; cat /tmp/bar.png | base64" 2 '\[pre-tool-use\] guard=base64-tool-result.*reason=base64-encoder.*action=reject' \
|
|
145
|
-
"Test 11b: compound (decode ; encode) rejects encoder segment (per-segment scan)"
|
|
146
|
-
|
|
147
|
-
run_bash "echo data | base64 -d > x.bin && cat y.png | base64 > y.b64" 2 '\[pre-tool-use\] guard=base64-tool-result.*action=reject' \
|
|
148
|
-
"Test 11c: compound (decode && encode) rejects encoder segment"
|
|
149
|
-
|
|
150
|
-
run_bash "base64 -d in.b64 > out.bin; base64 -d in2.b64 > out2.bin" 0 "" \
|
|
151
|
-
"Test 11d: compound (decode ; decode) allowed"
|
|
152
|
-
|
|
153
|
-
# ───────── Write/Edit consumer guard ────────────────────────────────────────
|
|
154
|
-
run_write "<html><body>hello world</body></html>" 0 "" \
|
|
155
|
-
"Test 12: Write small HTML content (no data URI) allowed"
|
|
156
|
-
|
|
157
|
-
run_write "<img src='data:image/png;base64,AAAA'>" 0 "" \
|
|
158
|
-
"Test 13: Write content with small inline data URI (<4096 chars) allowed"
|
|
159
|
-
|
|
160
|
-
run_write "<img src='data:image/png;base64,${LARGE_B64}'>" 2 '\[pre-tool-use\] guard=base64-write-content.*action=reject' \
|
|
161
|
-
"Test 14: Write content with large inline data URI (>4096 chars) rejected"
|
|
162
|
-
|
|
163
|
-
run_edit "<img src='data:image/png;base64,${LARGE_B64}'>" 2 '\[pre-tool-use\] guard=base64-write-content.*action=reject' \
|
|
164
|
-
"Test 15: Edit new_string with large inline data URI rejected"
|
|
165
|
-
|
|
166
|
-
run_edit "<p>just text replacement</p>" 0 "" \
|
|
167
|
-
"Test 16: Edit new_string with plain text allowed"
|
|
168
|
-
|
|
169
|
-
# ───────── Fail-open / structural ───────────────────────────────────────────
|
|
170
|
-
STDOUT_FILE=$(mktemp); STDERR_FILE=$(mktemp); TMPFILES+=("$STDOUT_FILE" "$STDERR_FILE")
|
|
171
|
-
printf '%s' 'not json at all { ' | bash "$HOOK" admin >"$STDOUT_FILE" 2>"$STDERR_FILE"
|
|
172
|
-
RC=$?
|
|
173
|
-
if [[ "$RC" -ne 0 ]]; then
|
|
174
|
-
fail "Test 17: malformed stdin should fail open (exit 0), got $RC. Stderr: $(cat "$STDERR_FILE")"
|
|
175
|
-
else
|
|
176
|
-
pass "Test 17: malformed stdin → silent passthrough (fail-open)"
|
|
177
|
-
fi
|
|
178
|
-
|
|
179
|
-
# Terminal-stdin guard preserved (no -t 0 test runs in test harness; assert
|
|
180
|
-
# the guard line exists in source).
|
|
181
|
-
if ! grep -q '\[ -t 0 \]' "$HOOK"; then
|
|
182
|
-
fail "Test 18: terminal stdin guard missing from hook source"
|
|
183
|
-
else
|
|
184
|
-
pass "Test 18: terminal stdin guard present in source"
|
|
185
|
-
fi
|
|
186
|
-
|
|
187
|
-
# Pre-existing guards still active — entitlement file edit still rejected.
|
|
188
|
-
ENT_JSON=$(python3 -c 'import json; print(json.dumps({"hook_event_name":"PreToolUse","tool_name":"Write","tool_input":{"file_path":"/srv/entitlement.json","content":"{\"tier\":\"max\"}"},"transcript_path":"/tmp/sess/parent.jsonl","parent_tool_use_id":"toolu_test_subagent"}, separators=(",", ":")))')
|
|
189
|
-
STDOUT_FILE=$(mktemp); STDERR_FILE=$(mktemp); TMPFILES+=("$STDOUT_FILE" "$STDERR_FILE")
|
|
190
|
-
printf '%s' "$ENT_JSON" | bash "$HOOK" admin >"$STDOUT_FILE" 2>"$STDERR_FILE"
|
|
191
|
-
RC=$?
|
|
192
|
-
if [[ "$RC" -ne 2 ]]; then
|
|
193
|
-
fail "Test 19: pre-existing entitlement guard regressed (expected exit 2, got $RC)"
|
|
194
|
-
else
|
|
195
|
-
pass "Test 19: pre-existing entitlement guard still rejects entitlement.json"
|
|
196
|
-
fi
|
|
197
|
-
|
|
198
|
-
echo
|
|
199
|
-
echo "──────── pre-tool-use base64 guard test summary ────────"
|
|
200
|
-
echo "PASS: $PASS"
|
|
201
|
-
echo "FAIL: $FAIL"
|
|
202
|
-
|
|
203
|
-
[[ "$FAIL" -gt 0 ]] && exit 1
|
|
204
|
-
exit 0
|
package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-memory-write-passthrough.test.sh
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Task 225 — passthrough regression test for memory-write / memory-update
|
|
3
|
-
# under the admin agent's pre-tool-use.sh.
|
|
4
|
-
#
|
|
5
|
-
# The Task 213→222 chain previously gated both writers at the top of the
|
|
6
|
-
# admin branch with an exit-2 block (admin direct) and an exit-0 allow
|
|
7
|
-
# (Task-subagent). Task 225 reversed the doctrine: admin holds the
|
|
8
|
-
# writers in its own surface and the case-block was deleted. This test
|
|
9
|
-
# pins the new behaviour so a future re-introduction of the block
|
|
10
|
-
# cannot land silently.
|
|
11
|
-
#
|
|
12
|
-
# Five cases:
|
|
13
|
-
# 1. mcp__plugin_memory_memory__memory-write admin-direct → exit 0, no rejection stderr
|
|
14
|
-
# 2. mcp__plugin_memory_memory__memory-update admin-direct → exit 0, no rejection stderr
|
|
15
|
-
# 3. mcp__plugin_memory_memory__memory-search admin-direct → exit 0, no rejection stderr (read tool control)
|
|
16
|
-
# 4. mcp__memory__memory-write admin-direct → exit 0 (pre-209 namespace, falls through harmlessly)
|
|
17
|
-
# 5. malformed JSON stdin → exit 0 (no crash; case-block removal does not break fall-through)
|
|
18
|
-
|
|
19
|
-
set -u
|
|
20
|
-
|
|
21
|
-
HOOK="$(cd "$(dirname "$0")/.." && pwd)/pre-tool-use.sh"
|
|
22
|
-
if [[ ! -x "$HOOK" ]]; then
|
|
23
|
-
echo "FAIL: $HOOK not executable" >&2
|
|
24
|
-
exit 1
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
TMPFILES=()
|
|
28
|
-
cleanup() {
|
|
29
|
-
for f in "${TMPFILES[@]:-}"; do
|
|
30
|
-
[[ -n "$f" ]] && rm -f "$f" 2>/dev/null || true
|
|
31
|
-
done
|
|
32
|
-
}
|
|
33
|
-
trap cleanup EXIT
|
|
34
|
-
|
|
35
|
-
PASS=0
|
|
36
|
-
FAIL=0
|
|
37
|
-
pass() { echo "PASS: $1"; PASS=$((PASS + 1)); }
|
|
38
|
-
fail() { echo "FAIL: $1" >&2; FAIL=$((FAIL + 1)); }
|
|
39
|
-
|
|
40
|
-
# Admin-direct calls land with a non-subagent transcript path. A synthetic
|
|
41
|
-
# empty file in a temp dir is enough — the hook no longer reads the path
|
|
42
|
-
# for the deleted writers' case-block, but other gates may still touch it.
|
|
43
|
-
ADMIN_TRANSCRIPT=$(mktemp); TMPFILES+=("$ADMIN_TRANSCRIPT")
|
|
44
|
-
: > "$ADMIN_TRANSCRIPT"
|
|
45
|
-
|
|
46
|
-
run_with_tool() {
|
|
47
|
-
local tool="$1"
|
|
48
|
-
local sid="$2"
|
|
49
|
-
local transcript_path="${3:-}"
|
|
50
|
-
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
51
|
-
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
52
|
-
python3 -c '
|
|
53
|
-
import json, sys
|
|
54
|
-
payload = {
|
|
55
|
-
"hook_event_name": "PreToolUse",
|
|
56
|
-
"session_id": sys.argv[1],
|
|
57
|
-
"tool_name": sys.argv[2],
|
|
58
|
-
"tool_input": {"name":"Smalleys","accountId":"acct-x","scope":"shared"},
|
|
59
|
-
}
|
|
60
|
-
if sys.argv[3]:
|
|
61
|
-
payload["transcript_path"] = sys.argv[3]
|
|
62
|
-
print(json.dumps(payload, separators=(",", ":")))
|
|
63
|
-
' "$sid" "$tool" "$transcript_path" | bash "$HOOK" admin >"$stdout_file" 2>"$stderr_file"
|
|
64
|
-
HOOK_RC=$?
|
|
65
|
-
HOOK_STDERR=$(cat "$stderr_file")
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
assert_passthrough() {
|
|
69
|
-
local label="$1"
|
|
70
|
-
if [[ "$HOOK_RC" -ne 0 ]]; then
|
|
71
|
-
fail "${label}: expected exit 0, got $HOOK_RC stderr: $HOOK_STDERR"
|
|
72
|
-
return
|
|
73
|
-
fi
|
|
74
|
-
if echo "$HOOK_STDERR" | grep -qF "does not write to the graph directly"; then
|
|
75
|
-
fail "${label}: stderr carries deleted-block rejection message: $HOOK_STDERR"
|
|
76
|
-
return
|
|
77
|
-
fi
|
|
78
|
-
pass "${label}: exit 0, no rejection stderr"
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# --- 1. memory-write admin-direct passthrough ---------------------------
|
|
82
|
-
run_with_tool "mcp__plugin_memory_memory__memory-write" "sess-write-1" "$ADMIN_TRANSCRIPT"
|
|
83
|
-
assert_passthrough "admin-direct memory-write"
|
|
84
|
-
|
|
85
|
-
# --- 2. memory-update admin-direct passthrough --------------------------
|
|
86
|
-
run_with_tool "mcp__plugin_memory_memory__memory-update" "sess-update-1" "$ADMIN_TRANSCRIPT"
|
|
87
|
-
assert_passthrough "admin-direct memory-update"
|
|
88
|
-
|
|
89
|
-
# --- 3. memory-search admin-direct passthrough (read tool control) -----
|
|
90
|
-
run_with_tool "mcp__plugin_memory_memory__memory-search" "sess-search-1" "$ADMIN_TRANSCRIPT"
|
|
91
|
-
assert_passthrough "admin-direct memory-search (read tool control)"
|
|
92
|
-
|
|
93
|
-
# --- 4. pre-209 namespace mcp__memory__memory-write ---------------------
|
|
94
|
-
# The bare mcp__memory__ namespace was never in the admin runtime surface
|
|
95
|
-
# after Task 203's plugin_ prefix rename; this case pins that it still
|
|
96
|
-
# falls through harmlessly after the case-block removal.
|
|
97
|
-
run_with_tool "mcp__memory__memory-write" "sess-old-ns-1" "$ADMIN_TRANSCRIPT"
|
|
98
|
-
assert_passthrough "pre-209 namespace mcp__memory__memory-write"
|
|
99
|
-
|
|
100
|
-
# --- 5. malformed JSON stdin — hook must not crash ----------------------
|
|
101
|
-
stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
102
|
-
stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
103
|
-
echo "not-valid-json{{{" | bash "$HOOK" admin >"$stdout_file" 2>"$stderr_file"
|
|
104
|
-
HOOK_RC=$?
|
|
105
|
-
HOOK_STDERR=$(cat "$stderr_file")
|
|
106
|
-
if [[ "$HOOK_RC" -ne 0 ]]; then
|
|
107
|
-
fail "malformed payload: expected exit 0, got $HOOK_RC stderr: $HOOK_STDERR"
|
|
108
|
-
elif echo "$HOOK_STDERR" | grep -qF "does not write to the graph directly"; then
|
|
109
|
-
fail "malformed payload: stderr carries deleted-block rejection message: $HOOK_STDERR"
|
|
110
|
-
else
|
|
111
|
-
pass "malformed payload: exit 0, hook does not crash"
|
|
112
|
-
fi
|
|
113
|
-
|
|
114
|
-
# --- Summary -----------------------------------------------------------
|
|
115
|
-
echo "---"
|
|
116
|
-
echo "PASSED: $PASS FAILED: $FAIL"
|
|
117
|
-
[[ "$FAIL" -eq 0 ]] || exit 1
|
|
118
|
-
exit 0
|