patchwork-os 1.2.0-beta.2.canary.714 → 1.2.0-beta.2.canary.721
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/bridge.js +30 -19
- package/dist/bridge.js.map +1 -1
- package/dist/butler/memoryCard.d.ts +2 -1
- package/dist/butler/memoryCard.js +2 -28
- package/dist/butler/memoryCard.js.map +1 -1
- package/dist/butler/outcomeIngester.js +31 -1
- package/dist/butler/outcomeIngester.js.map +1 -1
- package/dist/butler/outcomeShadowLog.d.ts +23 -0
- package/dist/butler/outcomeShadowLog.js +30 -1
- package/dist/butler/outcomeShadowLog.js.map +1 -1
- package/dist/identity/dashboardSession.d.ts +14 -0
- package/dist/identity/dashboardSession.js +37 -1
- package/dist/identity/dashboardSession.js.map +1 -1
- package/dist/identity/sessionSecretFile.d.ts +45 -0
- package/dist/identity/sessionSecretFile.js +83 -0
- package/dist/identity/sessionSecretFile.js.map +1 -0
- package/dist/index.js +36 -3
- package/dist/index.js.map +1 -1
- package/dist/privacy/boundaryReceipts.d.ts +78 -0
- package/dist/privacy/boundaryReceipts.js +203 -0
- package/dist/privacy/boundaryReceipts.js.map +1 -0
- package/dist/promptSafety.d.ts +29 -0
- package/dist/promptSafety.js +55 -0
- package/dist/promptSafety.js.map +1 -0
- package/dist/recipeRoutes.js +28 -0
- package/dist/recipeRoutes.js.map +1 -1
- package/dist/tools/recentTracesDigest.js +10 -3
- package/dist/tools/recentTracesDigest.js.map +1 -1
- package/package.json +1 -1
|
@@ -89,7 +89,30 @@ export interface ShadowSummary {
|
|
|
89
89
|
unknown: number;
|
|
90
90
|
/** Rows that would have become evidence had the grader been live. */
|
|
91
91
|
wouldCount: number;
|
|
92
|
+
/**
|
|
93
|
+
* Rows per grading reason.
|
|
94
|
+
*
|
|
95
|
+
* The disposition counts alone cannot answer the only question an operator
|
|
96
|
+
* has when `unknown` dominates: is this channel WORKING on errands that are
|
|
97
|
+
* simply still in flight, or is it seeing nothing at all? Those are
|
|
98
|
+
* `open-recent` and `not-observed`, and they collapse into the same
|
|
99
|
+
* `unknown` bucket while calling for opposite responses — wait, versus go
|
|
100
|
+
* and fix the observation path.
|
|
101
|
+
*
|
|
102
|
+
* The rows have always carried `reason`; only the summary discarded it. Same
|
|
103
|
+
* defect as #1469, where the shadow report named a defaulted-classification
|
|
104
|
+
* count and gave no way to find WHICH recipes to go and label.
|
|
105
|
+
*/
|
|
106
|
+
byReason: Record<GradedOutcome["reason"], number>;
|
|
92
107
|
}
|
|
108
|
+
/** Every reason, so a summary always reports the full space rather than only what it saw. */
|
|
109
|
+
export declare const SHADOW_REASONS: ReadonlyArray<GradedOutcome["reason"]>;
|
|
110
|
+
/**
|
|
111
|
+
* Reasons that mean "the observation channel could not answer", as opposed to
|
|
112
|
+
* "it answered, and the errand is not finished yet". Exported so the formatter
|
|
113
|
+
* and its tests cannot drift on which is which.
|
|
114
|
+
*/
|
|
115
|
+
export declare const UNOBSERVED_REASONS: ReadonlyArray<GradedOutcome["reason"]>;
|
|
93
116
|
/**
|
|
94
117
|
* Summarise the shadow ledger — the number this phase exists to produce.
|
|
95
118
|
*
|
|
@@ -104,6 +104,22 @@ export function firstSeenByRef(opts = {}) {
|
|
|
104
104
|
}
|
|
105
105
|
return out;
|
|
106
106
|
}
|
|
107
|
+
/** Every reason, so a summary always reports the full space rather than only what it saw. */
|
|
108
|
+
export const SHADOW_REASONS = [
|
|
109
|
+
"completed",
|
|
110
|
+
"deleted",
|
|
111
|
+
"stale-unactioned",
|
|
112
|
+
"open-recent",
|
|
113
|
+
"not-observed",
|
|
114
|
+
];
|
|
115
|
+
/**
|
|
116
|
+
* Reasons that mean "the observation channel could not answer", as opposed to
|
|
117
|
+
* "it answered, and the errand is not finished yet". Exported so the formatter
|
|
118
|
+
* and its tests cannot drift on which is which.
|
|
119
|
+
*/
|
|
120
|
+
export const UNOBSERVED_REASONS = [
|
|
121
|
+
"not-observed",
|
|
122
|
+
];
|
|
107
123
|
/**
|
|
108
124
|
* Summarise the shadow ledger — the number this phase exists to produce.
|
|
109
125
|
*
|
|
@@ -112,12 +128,14 @@ export function firstSeenByRef(opts = {}) {
|
|
|
112
128
|
* count someone is about to make a decision on.
|
|
113
129
|
*/
|
|
114
130
|
export function summariseShadowLog(opts = {}) {
|
|
131
|
+
const emptyByReason = () => Object.fromEntries(SHADOW_REASONS.map((r) => [r, 0]));
|
|
115
132
|
const empty = {
|
|
116
133
|
total: 0,
|
|
117
134
|
confirmed: 0,
|
|
118
135
|
junk: 0,
|
|
119
136
|
unknown: 0,
|
|
120
137
|
wouldCount: 0,
|
|
138
|
+
byReason: emptyByReason(),
|
|
121
139
|
};
|
|
122
140
|
const p = shadowLogPath(opts.dir);
|
|
123
141
|
if (!existsSync(p))
|
|
@@ -129,12 +147,23 @@ export function summariseShadowLog(opts = {}) {
|
|
|
129
147
|
catch {
|
|
130
148
|
return empty;
|
|
131
149
|
}
|
|
132
|
-
|
|
150
|
+
// Fresh `byReason` rather than sharing `empty`'s. NOT a live bug — `empty` is
|
|
151
|
+
// built per call, so the spread aliasing its object harms nothing today. It is
|
|
152
|
+
// here so that hoisting `empty` to module scope, an obvious tidy-up, cannot
|
|
153
|
+
// silently make every summary cumulative. Deliberately NOT covered by a test:
|
|
154
|
+
// no test can distinguish the two while `empty` stays local, and a test that
|
|
155
|
+
// passes either way would be worse than none.
|
|
156
|
+
const out = { ...empty, byReason: emptyByReason() };
|
|
133
157
|
for (const row of parseShadowRows(text)) {
|
|
134
158
|
out.total++;
|
|
135
159
|
out[row.disposition]++;
|
|
136
160
|
if (row.wouldCountAsEvidence)
|
|
137
161
|
out.wouldCount++;
|
|
162
|
+
// A row whose reason is outside the known space is counted in `total` but
|
|
163
|
+
// not attributed. Silently coercing it into a known bucket would put a
|
|
164
|
+
// number a person acts on behind a guess.
|
|
165
|
+
if (row.reason in out.byReason)
|
|
166
|
+
out.byReason[row.reason]++;
|
|
138
167
|
}
|
|
139
168
|
return out;
|
|
140
169
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outcomeShadowLog.js","sourceRoot":"","sources":["../../src/butler/outcomeShadowLog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIpD;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AAEjE,MAAM,UAAU,aAAa,CAAC,QAAiB;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,aAAa,EAAE,EAAE,mBAAmB,CAAC,CAAC;AACrE,CAAC;AAwBD,qEAAqE;AACrE,MAAM,UAAU,oBAAoB,CAAC,CAAqB;IACxD,OAAO,CAAC,KAAK,SAAS,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAmD,EACnD,OAAyB,EAAE;IAE3B,MAAM,IAAI,GAAqB;QAC7B,GAAG,GAAG;QACN,oBAAoB,EAAE,oBAAoB,CAAC,GAAG,CAAC,WAAW,CAAC;KAC5D,CAAC;IACF,IAAI,CAAC;QACH,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,wEAAwE;QACxE,+BAA+B;IACjC,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAyB,EAAE;IAE3B,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IAC/B,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAC3B,IAAI,GAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAqB,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACpE,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,QAAQ,GAAG,IAAI;YAC3C,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;
|
|
1
|
+
{"version":3,"file":"outcomeShadowLog.js","sourceRoot":"","sources":["../../src/butler/outcomeShadowLog.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,6 +31,20 @@
|
|
|
31
31
|
*/
|
|
32
32
|
export declare const SESSION_COOKIE_NAME = "patchwork_session";
|
|
33
33
|
export declare const SESSION_TTL_MS: number;
|
|
34
|
+
/**
|
|
35
|
+
* Injection point for the Node host. Underscore-prefixed because it is not part
|
|
36
|
+
* of the session API — nothing but the bridge's startup and tests should call it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function __setSessionSecretFallback(secret: string | undefined): void;
|
|
39
|
+
/**
|
|
40
|
+
* Whether a secret is reachable at all, by either route.
|
|
41
|
+
*
|
|
42
|
+
* Exported so the bridge's wiring condition and this module cannot disagree
|
|
43
|
+
* about what "attribution is available" means. They were two separate reads of
|
|
44
|
+
* `process.env` before, which is precisely how one site gains a fallback and
|
|
45
|
+
* its neighbour silently does not.
|
|
46
|
+
*/
|
|
47
|
+
export declare function hasSessionSecret(): boolean;
|
|
34
48
|
/**
|
|
35
49
|
* Member ids that are safe inside a dot-delimited cookie payload.
|
|
36
50
|
*
|
|
@@ -31,8 +31,44 @@
|
|
|
31
31
|
*/
|
|
32
32
|
export const SESSION_COOKIE_NAME = "patchwork_session";
|
|
33
33
|
export const SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
34
|
+
/**
|
|
35
|
+
* Secret supplied by the Node-side host when the environment does not carry
|
|
36
|
+
* one. Set by the bridge from `$PATCHWORK_HOME/.env` — see
|
|
37
|
+
* `sessionSecretFile.ts` for why that read cannot happen in this module.
|
|
38
|
+
*
|
|
39
|
+
* A plain variable, deliberately: no `node:` import may appear here, because
|
|
40
|
+
* the dashboard's Edge middleware imports this file and a build failure there
|
|
41
|
+
* locks every user out of the dashboard.
|
|
42
|
+
*
|
|
43
|
+
* The Edge consumer never calls the setter, so its behaviour is unchanged —
|
|
44
|
+
* `undefined` fallback, environment only.
|
|
45
|
+
*/
|
|
46
|
+
let sessionSecretFallback;
|
|
47
|
+
/**
|
|
48
|
+
* Injection point for the Node host. Underscore-prefixed because it is not part
|
|
49
|
+
* of the session API — nothing but the bridge's startup and tests should call it.
|
|
50
|
+
*/
|
|
51
|
+
export function __setSessionSecretFallback(secret) {
|
|
52
|
+
sessionSecretFallback = secret;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Whether a secret is reachable at all, by either route.
|
|
56
|
+
*
|
|
57
|
+
* Exported so the bridge's wiring condition and this module cannot disagree
|
|
58
|
+
* about what "attribution is available" means. They were two separate reads of
|
|
59
|
+
* `process.env` before, which is precisely how one site gains a fallback and
|
|
60
|
+
* its neighbour silently does not.
|
|
61
|
+
*/
|
|
62
|
+
export function hasSessionSecret() {
|
|
63
|
+
return getSecret() !== "";
|
|
64
|
+
}
|
|
34
65
|
function getSecret() {
|
|
35
|
-
|
|
66
|
+
// Environment wins. An operator who exports the variable is making a
|
|
67
|
+
// deliberate choice, and a file on disk must not silently override it.
|
|
68
|
+
const fromEnv = process.env.DASHBOARD_SESSION_SECRET;
|
|
69
|
+
if (fromEnv !== undefined && fromEnv !== "")
|
|
70
|
+
return fromEnv;
|
|
71
|
+
return sessionSecretFallback ?? "";
|
|
36
72
|
}
|
|
37
73
|
function base64url(bytes) {
|
|
38
74
|
const buf = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboardSession.js","sourceRoot":"","sources":["../../src/identity/dashboardSession.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;AACvD,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,UAAU;AAElE,SAAS,SAAS;IAChB,OAAO,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"dashboardSession.js","sourceRoot":"","sources":["../../src/identity/dashboardSession.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read `DASHBOARD_SESSION_SECRET` out of `$PATCHWORK_HOME/.env` — the file
|
|
3
|
+
* `patchwork init` writes and the bridge never read.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this module exists at all
|
|
6
|
+
*
|
|
7
|
+
* ADR-0020 attribution shipped and was unreachable on most launch paths. The
|
|
8
|
+
* dotenv loader in `src/index.ts` builds its candidates from `import.meta.url`,
|
|
9
|
+
* so it reads `<install-dir>/.env` — a path `npm run install:global` destroys
|
|
10
|
+
* on every reinstall — and never `$PATCHWORK_HOME/.env`. Measured on the live
|
|
11
|
+
* machine 2026-08-24: all three running bridges lacked the variable while the
|
|
12
|
+
* secret sat correctly on disk, so every approval they recorded was
|
|
13
|
+
* unattributed despite the operator having done exactly the right setup.
|
|
14
|
+
*
|
|
15
|
+
* `scripts/start-all.sh --no-tmux` is the one path that works, and only because
|
|
16
|
+
* it sources the file into the shell before launching. launchd, the tmux path
|
|
17
|
+
* and a bare `patchwork start` all leave attribution off.
|
|
18
|
+
*
|
|
19
|
+
* ## Why it reads ONE key rather than loading the file
|
|
20
|
+
*
|
|
21
|
+
* `claudeDriver.ts:295` spawns agent subprocesses with `{ ...process.env }`.
|
|
22
|
+
* Loading this file into the environment would hand `DASHBOARD_PASSWORD` and
|
|
23
|
+
* whatever else it holds to every agent the bridge runs, in order to deliver
|
|
24
|
+
* one unrelated value. So this returns the secret to its caller and writes
|
|
25
|
+
* nothing to `process.env`.
|
|
26
|
+
*
|
|
27
|
+
* ## Why it is not in `dashboardSession.ts`
|
|
28
|
+
*
|
|
29
|
+
* That module is imported by the dashboard's Edge middleware
|
|
30
|
+
* (`dashboard/src/lib/session.ts:43`), where `node:` imports are unavailable.
|
|
31
|
+
* A `node:fs` import there breaks the dashboard build in middleware, which is
|
|
32
|
+
* the one place a failure locks every user out. The read is Node-only, so it
|
|
33
|
+
* lives here and is injected.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Returns the secret, or `undefined` when the file, the key or the value is
|
|
37
|
+
* absent.
|
|
38
|
+
*
|
|
39
|
+
* Fail-soft by construction: an unreadable or malformed file yields
|
|
40
|
+
* `undefined`, which leaves attribution exactly where it was — off, with
|
|
41
|
+
* decisions recorded unattributed. That is the pre-existing behaviour and the
|
|
42
|
+
* correct one, since an absent actor already means "nobody recorded this" and
|
|
43
|
+
* must never be filled in with a guess.
|
|
44
|
+
*/
|
|
45
|
+
export declare function readSessionSecretFromHome(dir?: string): string | undefined;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read `DASHBOARD_SESSION_SECRET` out of `$PATCHWORK_HOME/.env` — the file
|
|
3
|
+
* `patchwork init` writes and the bridge never read.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this module exists at all
|
|
6
|
+
*
|
|
7
|
+
* ADR-0020 attribution shipped and was unreachable on most launch paths. The
|
|
8
|
+
* dotenv loader in `src/index.ts` builds its candidates from `import.meta.url`,
|
|
9
|
+
* so it reads `<install-dir>/.env` — a path `npm run install:global` destroys
|
|
10
|
+
* on every reinstall — and never `$PATCHWORK_HOME/.env`. Measured on the live
|
|
11
|
+
* machine 2026-08-24: all three running bridges lacked the variable while the
|
|
12
|
+
* secret sat correctly on disk, so every approval they recorded was
|
|
13
|
+
* unattributed despite the operator having done exactly the right setup.
|
|
14
|
+
*
|
|
15
|
+
* `scripts/start-all.sh --no-tmux` is the one path that works, and only because
|
|
16
|
+
* it sources the file into the shell before launching. launchd, the tmux path
|
|
17
|
+
* and a bare `patchwork start` all leave attribution off.
|
|
18
|
+
*
|
|
19
|
+
* ## Why it reads ONE key rather than loading the file
|
|
20
|
+
*
|
|
21
|
+
* `claudeDriver.ts:295` spawns agent subprocesses with `{ ...process.env }`.
|
|
22
|
+
* Loading this file into the environment would hand `DASHBOARD_PASSWORD` and
|
|
23
|
+
* whatever else it holds to every agent the bridge runs, in order to deliver
|
|
24
|
+
* one unrelated value. So this returns the secret to its caller and writes
|
|
25
|
+
* nothing to `process.env`.
|
|
26
|
+
*
|
|
27
|
+
* ## Why it is not in `dashboardSession.ts`
|
|
28
|
+
*
|
|
29
|
+
* That module is imported by the dashboard's Edge middleware
|
|
30
|
+
* (`dashboard/src/lib/session.ts:43`), where `node:` imports are unavailable.
|
|
31
|
+
* A `node:fs` import there breaks the dashboard build in middleware, which is
|
|
32
|
+
* the one place a failure locks every user out. The read is Node-only, so it
|
|
33
|
+
* lives here and is injected.
|
|
34
|
+
*/
|
|
35
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
36
|
+
import path from "node:path";
|
|
37
|
+
import { patchworkHome } from "../patchworkHome.js";
|
|
38
|
+
const KEY = "DASHBOARD_SESSION_SECRET";
|
|
39
|
+
/**
|
|
40
|
+
* Returns the secret, or `undefined` when the file, the key or the value is
|
|
41
|
+
* absent.
|
|
42
|
+
*
|
|
43
|
+
* Fail-soft by construction: an unreadable or malformed file yields
|
|
44
|
+
* `undefined`, which leaves attribution exactly where it was — off, with
|
|
45
|
+
* decisions recorded unattributed. That is the pre-existing behaviour and the
|
|
46
|
+
* correct one, since an absent actor already means "nobody recorded this" and
|
|
47
|
+
* must never be filled in with a guess.
|
|
48
|
+
*/
|
|
49
|
+
export function readSessionSecretFromHome(dir) {
|
|
50
|
+
const file = path.join(dir ?? patchworkHome(), ".env");
|
|
51
|
+
if (!existsSync(file))
|
|
52
|
+
return undefined;
|
|
53
|
+
let text;
|
|
54
|
+
try {
|
|
55
|
+
text = readFileSync(file, "utf-8");
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
for (const line of text.split("\n")) {
|
|
61
|
+
const trimmed = line.trim();
|
|
62
|
+
// A commented-out key is not a key. Reading one would silently arm
|
|
63
|
+
// attribution with a value the operator had deliberately disabled.
|
|
64
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
65
|
+
continue;
|
|
66
|
+
const eq = trimmed.indexOf("=");
|
|
67
|
+
if (eq <= 0)
|
|
68
|
+
continue;
|
|
69
|
+
if (trimmed.slice(0, eq).trim() !== KEY)
|
|
70
|
+
continue;
|
|
71
|
+
// Quote handling mirrors the loader in `src/index.ts` so the same file
|
|
72
|
+
// means the same thing to both readers. Deliberately does NOT strip inline
|
|
73
|
+
// comments: a `#` inside a generated hex secret is not a comment, and
|
|
74
|
+
// truncating a secret silently produces a value that verifies nothing.
|
|
75
|
+
const value = trimmed
|
|
76
|
+
.slice(eq + 1)
|
|
77
|
+
.trim()
|
|
78
|
+
.replace(/^["']|["']$/g, "");
|
|
79
|
+
return value === "" ? undefined : value;
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=sessionSecretFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionSecretFile.js","sourceRoot":"","sources":["../../src/identity/sessionSecretFile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,GAAG,GAAG,0BAA0B,CAAC;AAEvC;;;;;;;;;GASG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAY;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,aAAa,EAAE,EAAE,MAAM,CAAC,CAAC;IACvD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAExC,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,mEAAmE;QACnE,mEAAmE;QACnE,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAElD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,EAAE,IAAI,CAAC;YAAE,SAAS;QACtB,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,GAAG;YAAE,SAAS;QAElD,uEAAuE;QACvE,2EAA2E;QAC3E,sEAAsE;QACtE,uEAAuE;QACvE,MAAM,KAAK,GAAG,OAAO;aAClB,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;aACb,IAAI,EAAE;aACN,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC/B,OAAO,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;IAC1C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -297,17 +297,30 @@ if (process.argv[2] === "--help" ||
|
|
|
297
297
|
`Operate\n` +
|
|
298
298
|
` start [--port N] [--workspace <dir>] Start a single bridge (no tmux)\n` +
|
|
299
299
|
` status One-line bridge status (port, uptime)\n` +
|
|
300
|
+
` dashboard Launch the local dashboard\n` +
|
|
301
|
+
` members [list|set-password <id>] Workspace roster + per-member credentials\n` +
|
|
300
302
|
` tools [list|search <q>] [--slim] [--json] List tools the bridge would register\n` +
|
|
301
303
|
` analytics <show|configure|clear|test> Manage opt-in telemetry config\n` +
|
|
302
304
|
` launchd <install|uninstall|status> Manage the macOS auto-start LaunchAgent\n\n` +
|
|
303
305
|
`Diagnose\n` +
|
|
304
306
|
` halts [--window 1h|24h|overnight|7d] Morning summary of recent recipe halts\n` +
|
|
305
307
|
` judgments [--window ...] [--recipe N] Recent judge-step verdicts across runs\n` +
|
|
308
|
+
` privacy receipts [--since-days N] What the LIVE information boundary actually decided\n` +
|
|
306
309
|
` privacy shadow [--since-days N] What a candidate privacy policy would have stopped\n` +
|
|
307
310
|
` suggest [--since-days N] Recipe + unused-tool suggestions\n` +
|
|
308
311
|
` token-efficiency benchmark Measure token cost across tool sets\n` +
|
|
309
312
|
` traces export Bundle approval / recipe / decision traces\n` +
|
|
310
|
-
` print-token [--port N] Print the active bridge auth token\n
|
|
313
|
+
` print-token [--port N] Print the active bridge auth token\n` +
|
|
314
|
+
` runstore <backfill|compare> [--json] Durable run-store maintenance + drift check\n` +
|
|
315
|
+
` shadow-scan [--since <d>] [--json] Reclassification scan over the run log\n\n` +
|
|
316
|
+
`Review\n` +
|
|
317
|
+
` approvals [--window 1h|24h|7d] [--json] Recent approval decisions\n` +
|
|
318
|
+
` approve <callId> / reject <callId> Decide one queued approval\n\n` +
|
|
319
|
+
`Butler\n` +
|
|
320
|
+
` butler shadow [--json] Summarise the graded shadow ledger\n` +
|
|
321
|
+
` butler observe [--stale-after-days N] Discover errands, look up live state, grade\n` +
|
|
322
|
+
` butler ingest [--file <path>|-] Grade a JSON array of observations\n` +
|
|
323
|
+
` butler promote [--dry-run] Fold grades into the trust ledger (flagged)\n\n` +
|
|
311
324
|
`Safety\n` +
|
|
312
325
|
` kill-switch <engage|release|status> Block / resume write-tier tools across bridges\n` +
|
|
313
326
|
` panic [--reason "..."] Shorthand for kill-switch engage\n\n` +
|
|
@@ -319,7 +332,7 @@ if (process.argv[2] === "--help" ||
|
|
|
319
332
|
` --version, -v Print package version\n` +
|
|
320
333
|
` shim stdio↔WebSocket shim (used by MCP clients)\n` +
|
|
321
334
|
` notify <event> Notify a running bridge of a CC hook event\n\n` +
|
|
322
|
-
`Bridge-daemon flags: run \`${binName} --workspace . --help
|
|
335
|
+
`Bridge-daemon flags: run \`${binName} --workspace . --help\` for the full list,\n` +
|
|
323
336
|
`or see https://github.com/Oolab-labs/patchwork-os#readme.\n`);
|
|
324
337
|
process.exit(0);
|
|
325
338
|
}
|
|
@@ -4574,8 +4587,28 @@ if (process.argv[2] === "privacy") {
|
|
|
4574
4587
|
}
|
|
4575
4588
|
process.exit(0);
|
|
4576
4589
|
}
|
|
4590
|
+
if (args[0] === "receipts") {
|
|
4591
|
+
// The ENFORCING ledger's reader. `shadow` answers "what would a candidate
|
|
4592
|
+
// policy have stopped"; this answers "what did the live policy actually
|
|
4593
|
+
// do" — the question the boundary was built to make answerable, and the
|
|
4594
|
+
// one nothing in this repo could answer until now (ADR-0019:88-92).
|
|
4595
|
+
const { summariseBoundaryReceipts, formatBoundaryReceipts } = await import("./privacy/boundaryReceipts.js");
|
|
4596
|
+
const i = args.indexOf("--since-days");
|
|
4597
|
+
const n = i !== -1 ? Number(args[i + 1]) : undefined;
|
|
4598
|
+
const summary = summariseBoundaryReceipts(n !== undefined && Number.isFinite(n) && n > 0
|
|
4599
|
+
? { since: Date.now() - n * 86_400_000 }
|
|
4600
|
+
: {});
|
|
4601
|
+
if (args.includes("--json")) {
|
|
4602
|
+
process.stdout.write(`${JSON.stringify(summary, null, 2)}\n`);
|
|
4603
|
+
}
|
|
4604
|
+
else {
|
|
4605
|
+
process.stdout.write(`${formatBoundaryReceipts(summary)}\n`);
|
|
4606
|
+
}
|
|
4607
|
+
process.exit(0);
|
|
4608
|
+
}
|
|
4577
4609
|
if (args[0] !== "shadow") {
|
|
4578
|
-
process.stderr.write("Usage: patchwork privacy
|
|
4610
|
+
process.stderr.write("Usage: patchwork privacy receipts [--since-days N] [--json]\n" +
|
|
4611
|
+
" patchwork privacy shadow [--since-days N] [--json]\n" +
|
|
4579
4612
|
" patchwork privacy suggest [--json]\n" +
|
|
4580
4613
|
"\n" +
|
|
4581
4614
|
" Observes boundary decisions WITHOUT enforcing them. Configure a\n" +
|