@mirrowel/opencode-openshare 0.2.1 → 0.3.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -26
- package/ci/README.md +30 -14
- package/ci/openshare.js +461 -61
- package/dist/binding-queue.js +2 -2
- package/dist/commands.js +8 -8
- package/dist/config.d.ts +34 -14
- package/dist/config.js +108 -33
- package/dist/contract.d.ts +5 -5
- package/dist/contract.js +5 -5
- package/dist/core.d.ts +12 -1
- package/dist/core.js +388 -138
- package/dist/dialogs.d.ts +9 -10
- package/dist/dialogs.js +25 -28
- package/dist/redact.d.ts +46 -0
- package/dist/redact.js +130 -0
- package/dist/settings.d.ts +10 -7
- package/dist/settings.js +136 -54
- package/dist/share-flow.d.ts +30 -6
- package/dist/share-flow.js +73 -18
- package/dist/tui.js +251 -59
- package/dist/ui-prefs.js +6 -6
- package/dist/v2-server.js +5 -6
- package/package.json +1 -1
- package/src/binding-queue.ts +2 -2
- package/src/commands.ts +8 -8
- package/src/config.ts +112 -29
- package/src/contract.ts +5 -5
- package/src/core.ts +364 -137
- package/src/dialogs.tsx +29 -32
- package/src/redact.ts +134 -0
- package/src/settings.ts +132 -52
- package/src/share-flow.ts +76 -20
- package/src/sidebar.tsx +7 -7
- package/src/tui.tsx +22 -24
- package/src/ui-prefs.ts +6 -6
- package/src/v2-server.ts +5 -6
package/ci/openshare.js
CHANGED
|
@@ -35,11 +35,13 @@ var DEFAULTS = {
|
|
|
35
35
|
debounceMs: 1e4,
|
|
36
36
|
completedDelayMs: 5000,
|
|
37
37
|
modelLabel: "$Provider/$Model",
|
|
38
|
-
|
|
38
|
+
ttlHours: 90 * 24,
|
|
39
39
|
authLogin: "",
|
|
40
40
|
authPassword: "",
|
|
41
41
|
spoolDir: "",
|
|
42
42
|
urlFile: "",
|
|
43
|
+
urlFileFormat: "url",
|
|
44
|
+
configSet: "",
|
|
43
45
|
announceStderr: true
|
|
44
46
|
};
|
|
45
47
|
var CONFIG_NAME = "openshare.config.json";
|
|
@@ -78,6 +80,76 @@ function configPaths(pluginDir, env = process.env) {
|
|
|
78
80
|
function configHomePath(env = process.env) {
|
|
79
81
|
return join(globalConfigDir(env), CONFIG_NAME);
|
|
80
82
|
}
|
|
83
|
+
function configSourceMap(pluginDir, env = process.env) {
|
|
84
|
+
const emptyMeansUnset = new Set(["workerUrl", "createToken", "urlFile", "spoolDir"]);
|
|
85
|
+
const known = new Set([...Object.keys(DEFAULTS), "shareMode", "servers"]);
|
|
86
|
+
const sources = {};
|
|
87
|
+
const envKeys = {
|
|
88
|
+
workerUrl: env.OPENSHARE_WORKER_URL,
|
|
89
|
+
createToken: env.OPENSHARE_CREATE_TOKEN,
|
|
90
|
+
debounceMs: env.OPENSHARE_DEBOUNCE_MS,
|
|
91
|
+
completedDelayMs: env.OPENSHARE_COMPLETED_DELAY_MS,
|
|
92
|
+
modelLabel: env.OPENSHARE_MODEL_LABEL,
|
|
93
|
+
ttlHours: env.OPENSHARE_TTL_HOURS,
|
|
94
|
+
ttlDays: env.OPENSHARE_TTL_DAYS,
|
|
95
|
+
authLogin: env.OPENSHARE_AUTH_LOGIN,
|
|
96
|
+
authPassword: env.OPENSHARE_AUTH_PASSWORD,
|
|
97
|
+
urlFile: env.OPENSHARE_URL_FILE,
|
|
98
|
+
urlFileFormat: env.OPENSHARE_URL_FILE_FORMAT,
|
|
99
|
+
configSet: env.OPENSHARE_CONFIG_SET,
|
|
100
|
+
spoolDir: env.OPENSHARE_SPOOL_DIR,
|
|
101
|
+
announceStderr: env.OPENSHARE_ANNOUNCE_STDERR,
|
|
102
|
+
shareMode: env.OPENSHARE_SHARE_MODE
|
|
103
|
+
};
|
|
104
|
+
for (const [key, value] of Object.entries(envKeys)) {
|
|
105
|
+
if (value !== undefined && value !== "")
|
|
106
|
+
sources[key] = "environment";
|
|
107
|
+
}
|
|
108
|
+
if (sources.ttlDays && !sources.ttlHours)
|
|
109
|
+
sources.ttlHours = sources.ttlDays;
|
|
110
|
+
delete sources.ttlDays;
|
|
111
|
+
if (env.OPENSHARE_CONFIG_JSON) {
|
|
112
|
+
try {
|
|
113
|
+
const raw = JSON.parse(env.OPENSHARE_CONFIG_JSON);
|
|
114
|
+
if (raw && typeof raw === "object") {
|
|
115
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
116
|
+
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean")
|
|
117
|
+
continue;
|
|
118
|
+
if (sources[key] === "environment")
|
|
119
|
+
continue;
|
|
120
|
+
if (key === "servers" || known.has(key))
|
|
121
|
+
sources[key] = "OPENSHARE_CONFIG_JSON";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} catch {}
|
|
125
|
+
}
|
|
126
|
+
for (const path of configPaths(pluginDir, env)) {
|
|
127
|
+
let raw;
|
|
128
|
+
try {
|
|
129
|
+
raw = JSON.parse(readFileSync(path, "utf8"));
|
|
130
|
+
if (!raw || typeof raw !== "object")
|
|
131
|
+
continue;
|
|
132
|
+
} catch {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const label = basename(path) === LOCAL_NAME ? `openshare.local.json (${dirname(path).split(/[\\/]/).pop()})` : `${basename(path)} (${dirname(path).split(/[\\/]/).pop()})`;
|
|
136
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
137
|
+
if (key === "servers") {
|
|
138
|
+
if (sources.servers === undefined && normalizeServers(value))
|
|
139
|
+
sources.servers = label;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (sources[key] !== undefined || !known.has(key))
|
|
143
|
+
continue;
|
|
144
|
+
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean")
|
|
145
|
+
continue;
|
|
146
|
+
if (typeof value === "string" && value === "" && emptyMeansUnset.has(key))
|
|
147
|
+
continue;
|
|
148
|
+
sources[key] = label;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return sources;
|
|
152
|
+
}
|
|
81
153
|
function num(value, fallback) {
|
|
82
154
|
const parsed = Number(value);
|
|
83
155
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
@@ -104,6 +176,8 @@ function normalizeServers(raw) {
|
|
|
104
176
|
entry.adminToken = e.adminToken;
|
|
105
177
|
if (e.active === true)
|
|
106
178
|
entry.active = true;
|
|
179
|
+
if (e.anonymous === true)
|
|
180
|
+
entry.anonymous = true;
|
|
107
181
|
out[url] = entry;
|
|
108
182
|
}
|
|
109
183
|
return Object.keys(out).length ? out : undefined;
|
|
@@ -155,26 +229,53 @@ function loadConfig(pluginDir, options) {
|
|
|
155
229
|
const cfg = { ...DEFAULTS };
|
|
156
230
|
const emptyMeansUnset = new Set(["workerUrl", "createToken", "urlFile", "spoolDir"]);
|
|
157
231
|
const known = new Set([...Object.keys(DEFAULTS), "shareMode"]);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
232
|
+
const applyObject = (raw) => {
|
|
233
|
+
if (!raw || typeof raw !== "object")
|
|
234
|
+
return;
|
|
235
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
236
|
+
if (key === "servers") {
|
|
237
|
+
const servers = normalizeServers(value);
|
|
238
|
+
if (servers)
|
|
239
|
+
cfg.servers = mergeServerGroups(cfg.servers, servers);
|
|
162
240
|
continue;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
continue;
|
|
241
|
+
}
|
|
242
|
+
if (key === "redact") {
|
|
243
|
+
if (value && typeof value === "object" && Array.isArray(value.patterns)) {
|
|
244
|
+
const pats = value.patterns.filter((x) => typeof x === "string" && x.length > 0 && x.length < 500).slice(0, 50);
|
|
245
|
+
cfg.redact = { patterns: pats };
|
|
169
246
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (key === "meta") {
|
|
250
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
251
|
+
const clean = {};
|
|
252
|
+
for (const [mk, mv] of Object.entries(value)) {
|
|
253
|
+
if (typeof mv === "string" && mv.length <= 500 && mk.length <= 64)
|
|
254
|
+
clean[mk] = mv;
|
|
255
|
+
}
|
|
256
|
+
if (Object.keys(clean).length)
|
|
257
|
+
cfg.meta = { ...cfg.meta, ...clean };
|
|
258
|
+
}
|
|
259
|
+
continue;
|
|
177
260
|
}
|
|
261
|
+
if (key === "ttlDays") {
|
|
262
|
+
const d = Math.floor(Number(value));
|
|
263
|
+
if (Number.isFinite(d) && d > 0)
|
|
264
|
+
cfg.ttlHours = d * 24;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (!known.has(key))
|
|
268
|
+
continue;
|
|
269
|
+
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean")
|
|
270
|
+
continue;
|
|
271
|
+
if (typeof value === "string" && value === "" && emptyMeansUnset.has(key))
|
|
272
|
+
continue;
|
|
273
|
+
cfg[key] = value;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
for (const path of [...configPaths(pluginDir)].reverse()) {
|
|
277
|
+
try {
|
|
278
|
+
applyObject(JSON.parse(readFileSync(path, "utf8")));
|
|
178
279
|
} catch (e) {
|
|
179
280
|
if (existsSync(path))
|
|
180
281
|
console.error(`[openshare] config file ${path} could not be parsed - ignored:`, e?.message ?? e);
|
|
@@ -197,6 +298,13 @@ function loadConfig(pluginDir, options) {
|
|
|
197
298
|
cfg.shareMode = mode;
|
|
198
299
|
}
|
|
199
300
|
const env = process.env;
|
|
301
|
+
if (env.OPENSHARE_CONFIG_JSON) {
|
|
302
|
+
try {
|
|
303
|
+
applyObject(JSON.parse(env.OPENSHARE_CONFIG_JSON));
|
|
304
|
+
} catch (e) {
|
|
305
|
+
console.error("[openshare] OPENSHARE_CONFIG_JSON could not be parsed - ignored:", e?.message ?? e);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
200
308
|
if (env.OPENSHARE_WORKER_URL)
|
|
201
309
|
cfg.workerUrl = env.OPENSHARE_WORKER_URL;
|
|
202
310
|
if (env.OPENSHARE_CREATE_TOKEN)
|
|
@@ -207,14 +315,20 @@ function loadConfig(pluginDir, options) {
|
|
|
207
315
|
cfg.completedDelayMs = num(env.OPENSHARE_COMPLETED_DELAY_MS, cfg.completedDelayMs);
|
|
208
316
|
if (env.OPENSHARE_MODEL_LABEL !== undefined)
|
|
209
317
|
cfg.modelLabel = env.OPENSHARE_MODEL_LABEL;
|
|
318
|
+
if (env.OPENSHARE_TTL_HOURS)
|
|
319
|
+
cfg.ttlHours = num(env.OPENSHARE_TTL_HOURS, cfg.ttlHours);
|
|
210
320
|
if (env.OPENSHARE_TTL_DAYS)
|
|
211
|
-
cfg.
|
|
321
|
+
cfg.ttlHours = Math.max(1, Math.floor(num(env.OPENSHARE_TTL_DAYS, 90) * 24));
|
|
212
322
|
if (env.OPENSHARE_AUTH_LOGIN !== undefined)
|
|
213
323
|
cfg.authLogin = env.OPENSHARE_AUTH_LOGIN;
|
|
214
324
|
if (env.OPENSHARE_AUTH_PASSWORD !== undefined)
|
|
215
325
|
cfg.authPassword = env.OPENSHARE_AUTH_PASSWORD;
|
|
216
326
|
if (env.OPENSHARE_URL_FILE !== undefined)
|
|
217
327
|
cfg.urlFile = env.OPENSHARE_URL_FILE;
|
|
328
|
+
if (env.OPENSHARE_URL_FILE_FORMAT === "json" || env.OPENSHARE_URL_FILE_FORMAT === "url")
|
|
329
|
+
cfg.urlFileFormat = env.OPENSHARE_URL_FILE_FORMAT;
|
|
330
|
+
if (env.OPENSHARE_CONFIG_SET)
|
|
331
|
+
cfg.configSet = env.OPENSHARE_CONFIG_SET;
|
|
218
332
|
if (env.OPENSHARE_SPOOL_DIR)
|
|
219
333
|
cfg.spoolDir = env.OPENSHARE_SPOOL_DIR;
|
|
220
334
|
if (env.OPENSHARE_ANNOUNCE_STDERR !== undefined)
|
|
@@ -355,6 +469,7 @@ function resolveShareMode(cfg, input) {
|
|
|
355
469
|
|
|
356
470
|
// src/core.ts
|
|
357
471
|
import { appendFileSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
472
|
+
import { homedir } from "node:os";
|
|
358
473
|
import { createHash } from "node:crypto";
|
|
359
474
|
import { isAbsolute, join as join2 } from "node:path";
|
|
360
475
|
|
|
@@ -574,6 +689,109 @@ function queueBindingWrite(sid, fn) {
|
|
|
574
689
|
return run;
|
|
575
690
|
}
|
|
576
691
|
|
|
692
|
+
// src/redact.ts
|
|
693
|
+
var BUILTINS = [
|
|
694
|
+
{ kind: "aws-key", re: /\bAKIA[0-9A-Z]{16}\b/g },
|
|
695
|
+
{ kind: "gh-token", re: /\bgh[pousr]_[A-Za-z0-9]{36,255}\b/g },
|
|
696
|
+
{ kind: "gh-token", re: /\bgithub_pat_[A-Za-z0-9_]{22,255}\b/g },
|
|
697
|
+
{ kind: "api-key", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g },
|
|
698
|
+
{ kind: "slack-token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g },
|
|
699
|
+
{ kind: "google-key", re: /\bAIza[0-9A-Za-z_-]{35}\b/g },
|
|
700
|
+
{ kind: "bearer", re: /\bBearer\s+[A-Za-z0-9._~+/=-]{20,}/gi },
|
|
701
|
+
{ kind: "private-key", re: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g },
|
|
702
|
+
{ kind: "env-secret", re: /(?<![A-Za-z0-9_])(?:export\s+)?[A-Z0-9_]*(?:API_?KEY|TOKEN|SECRET|PASSWORD|PASSWD|PASS|PWD|CREDENTIALS?)[A-Z0-9_]*\s*=\s*\S+/g },
|
|
703
|
+
{ kind: "conn-string", re: /\b[a-z][a-z0-9+.-]*:\/\/[^:@/\s"']+:[^@/\s"']{4,}@/gi }
|
|
704
|
+
];
|
|
705
|
+
function compile(patterns) {
|
|
706
|
+
if (!Array.isArray(patterns))
|
|
707
|
+
return [];
|
|
708
|
+
const out = [];
|
|
709
|
+
for (const p of patterns) {
|
|
710
|
+
if (typeof p !== "string" || !p)
|
|
711
|
+
continue;
|
|
712
|
+
try {
|
|
713
|
+
out.push({ kind: "user", re: new RegExp(p, "g") });
|
|
714
|
+
} catch {}
|
|
715
|
+
}
|
|
716
|
+
return out;
|
|
717
|
+
}
|
|
718
|
+
function tildePath(p, home) {
|
|
719
|
+
if (!p || !home || p.length <= home.length)
|
|
720
|
+
return p;
|
|
721
|
+
const norm = (s) => s.replace(/[\\/]+/g, "/").replace(/\/$/, "");
|
|
722
|
+
const np = norm(p);
|
|
723
|
+
const nh = norm(home);
|
|
724
|
+
if (np === nh)
|
|
725
|
+
return "~";
|
|
726
|
+
if (np.startsWith(nh + "/"))
|
|
727
|
+
return "~" + np.slice(nh.length);
|
|
728
|
+
return p;
|
|
729
|
+
}
|
|
730
|
+
var PATH_KEYS = new Set(["file", "path", "filePath", "cwd", "directory", "root"]);
|
|
731
|
+
function redactItem(item, opts) {
|
|
732
|
+
const flat = JSON.stringify(item) ?? "null";
|
|
733
|
+
const needRedact = opts.enabled && opts.rules.some((r) => {
|
|
734
|
+
r.re.lastIndex = 0;
|
|
735
|
+
return r.re.test(flat);
|
|
736
|
+
});
|
|
737
|
+
const needPaths = !!opts.home && flat.includes(opts.home);
|
|
738
|
+
if (!needRedact && !needPaths)
|
|
739
|
+
return { item, hits: [] };
|
|
740
|
+
const clone = JSON.parse(flat);
|
|
741
|
+
const stats = { hits: [], changed: false };
|
|
742
|
+
walk(clone, opts, stats);
|
|
743
|
+
return { item: clone, hits: stats.hits };
|
|
744
|
+
}
|
|
745
|
+
function walk(node, opts, stats, key) {
|
|
746
|
+
if (Array.isArray(node)) {
|
|
747
|
+
for (let i = 0;i < node.length; i++) {
|
|
748
|
+
const v = node[i];
|
|
749
|
+
if (typeof v === "string")
|
|
750
|
+
node[i] = applyString(v, opts, stats, key);
|
|
751
|
+
else
|
|
752
|
+
walk(v, opts, stats, key);
|
|
753
|
+
}
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
if (node && typeof node === "object") {
|
|
757
|
+
for (const [k, v] of Object.entries(node)) {
|
|
758
|
+
if (typeof v === "string")
|
|
759
|
+
node[k] = applyString(v, opts, stats, k);
|
|
760
|
+
else
|
|
761
|
+
walk(v, opts, stats, k);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function applyString(v, opts, stats, key) {
|
|
766
|
+
let out = v;
|
|
767
|
+
if (opts.enabled) {
|
|
768
|
+
for (const r of opts.rules) {
|
|
769
|
+
r.re.lastIndex = 0;
|
|
770
|
+
out = out.replace(r.re, (full) => {
|
|
771
|
+
if (stats.hits.length < 8 && !stats.hits.some((h) => h.kind === r.kind)) {
|
|
772
|
+
stats.hits.push({ kind: r.kind, sample: full.slice(0, 60) });
|
|
773
|
+
}
|
|
774
|
+
stats.changed = true;
|
|
775
|
+
if (r.kind === "conn-string") {
|
|
776
|
+
const at = full.lastIndexOf("@");
|
|
777
|
+
const colon = full.indexOf(":", full.indexOf("://") + 3) + 1;
|
|
778
|
+
if (at > colon && colon > 0)
|
|
779
|
+
return full.slice(0, colon) + "[redacted:conn-string]" + full.slice(at);
|
|
780
|
+
}
|
|
781
|
+
return `[redacted:${r.kind}]`;
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (key && PATH_KEYS.has(key) && opts.home && out.includes(opts.home)) {
|
|
786
|
+
out = tildePath(out, opts.home);
|
|
787
|
+
stats.changed = true;
|
|
788
|
+
}
|
|
789
|
+
return out;
|
|
790
|
+
}
|
|
791
|
+
function activeRules(userPatterns) {
|
|
792
|
+
return [...BUILTINS, ...compile(userPatterns)];
|
|
793
|
+
}
|
|
794
|
+
|
|
577
795
|
// src/core.ts
|
|
578
796
|
var DEFAULT_POLICY = {
|
|
579
797
|
mode: "persistent",
|
|
@@ -626,6 +844,19 @@ function redact(info) {
|
|
|
626
844
|
delete metadata[METADATA_KEY];
|
|
627
845
|
return { ...info, metadata };
|
|
628
846
|
}
|
|
847
|
+
function stripLocalFacts(info) {
|
|
848
|
+
if (!info || typeof info !== "object")
|
|
849
|
+
return info;
|
|
850
|
+
const out = { ...info };
|
|
851
|
+
let changed = false;
|
|
852
|
+
for (const k of ["directory", "worktree"]) {
|
|
853
|
+
if (out[k] !== undefined) {
|
|
854
|
+
delete out[k];
|
|
855
|
+
changed = true;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return changed ? out : info;
|
|
859
|
+
}
|
|
629
860
|
function shareFlag(info) {
|
|
630
861
|
const raw = info?.metadata?.[METADATA_KEY];
|
|
631
862
|
if (!raw || typeof raw !== "object")
|
|
@@ -637,8 +868,10 @@ function shareFlag(info) {
|
|
|
637
868
|
const opts = {};
|
|
638
869
|
if (optsRaw && typeof optsRaw === "object") {
|
|
639
870
|
const o = optsRaw;
|
|
640
|
-
if (typeof o.
|
|
641
|
-
opts.
|
|
871
|
+
if (typeof o.ttlHours === "number" && o.ttlHours > 0)
|
|
872
|
+
opts.ttlHours = Math.floor(o.ttlHours);
|
|
873
|
+
else if (typeof o.ttlDays === "number" && o.ttlDays > 0)
|
|
874
|
+
opts.ttlHours = Math.floor(o.ttlDays * 24);
|
|
642
875
|
if (typeof o.authLogin === "string")
|
|
643
876
|
opts.authLogin = o.authLogin;
|
|
644
877
|
if (typeof o.authPassword === "string")
|
|
@@ -649,6 +882,8 @@ function shareFlag(info) {
|
|
|
649
882
|
return { enabled: true, opts: Object.keys(opts).length ? opts : undefined };
|
|
650
883
|
}
|
|
651
884
|
var liveTargets = [];
|
|
885
|
+
var statsProvider = null;
|
|
886
|
+
var redactionLedger = new Map;
|
|
652
887
|
function createEngine(deps) {
|
|
653
888
|
let cfg = loadConfig(deps.pluginDir, deps.options);
|
|
654
889
|
const mode = resolveShareMode(cfg, deps);
|
|
@@ -721,40 +956,98 @@ function createEngine(deps) {
|
|
|
721
956
|
};
|
|
722
957
|
seedCredentials();
|
|
723
958
|
const ENROLL_RETRY_MS = 60000;
|
|
959
|
+
const setsFetched = new Set;
|
|
960
|
+
const adoptServerSuggestions = async (url, token) => {
|
|
961
|
+
if (setsFetched.has(url))
|
|
962
|
+
return;
|
|
963
|
+
setsFetched.add(url);
|
|
964
|
+
try {
|
|
965
|
+
const r = await fetch(`${url}/api/token/sets`, {
|
|
966
|
+
headers: { authorization: `Bearer ${token}` },
|
|
967
|
+
signal: AbortSignal.timeout(8000)
|
|
968
|
+
});
|
|
969
|
+
const body = r.ok ? await r.json().catch(() => null) : null;
|
|
970
|
+
const sets = body?.sets ?? {};
|
|
971
|
+
const pick = sets[cfg.configSet ?? ""] ?? Object.values(sets)[0];
|
|
972
|
+
if (!pick)
|
|
973
|
+
return;
|
|
974
|
+
const wantModel = typeof pick.modelLabel === "string" && !pick.modelLabel ? undefined : pick.modelLabel;
|
|
975
|
+
const wantMode = pick.shareMode === "all" || pick.shareMode === "manual" ? pick.shareMode : undefined;
|
|
976
|
+
const src = configSourceMap(deps.pluginDir ?? "");
|
|
977
|
+
const modelUnset = cfg.modelLabel === DEFAULTS.modelLabel && !src.modelLabel;
|
|
978
|
+
const modeUnset = cfg.shareMode === undefined && !src.shareMode;
|
|
979
|
+
const adoptModel = wantModel && modelUnset;
|
|
980
|
+
const adoptMode = wantMode && modeUnset;
|
|
981
|
+
if (!adoptModel && !adoptMode)
|
|
982
|
+
return;
|
|
983
|
+
const home = configHomePath();
|
|
984
|
+
if (!home)
|
|
985
|
+
return;
|
|
986
|
+
let doc = {};
|
|
987
|
+
try {
|
|
988
|
+
doc = JSON.parse(readFileSync2(home, "utf8"));
|
|
989
|
+
if (!doc || typeof doc !== "object")
|
|
990
|
+
doc = {};
|
|
991
|
+
} catch {}
|
|
992
|
+
let dirty = false;
|
|
993
|
+
if (adoptModel && doc.modelLabel === undefined) {
|
|
994
|
+
doc.modelLabel = wantModel;
|
|
995
|
+
dirty = true;
|
|
996
|
+
log(`${url}: adopted modelLabel from the server config set`);
|
|
997
|
+
}
|
|
998
|
+
if (adoptMode && doc.shareMode === undefined) {
|
|
999
|
+
doc.shareMode = wantMode;
|
|
1000
|
+
dirty = true;
|
|
1001
|
+
log(`${url}: adopted shareMode from the server config set`);
|
|
1002
|
+
}
|
|
1003
|
+
if (dirty)
|
|
1004
|
+
writeFileSync2(home, JSON.stringify(doc, null, 2) + `
|
|
1005
|
+
`);
|
|
1006
|
+
} catch {}
|
|
1007
|
+
};
|
|
724
1008
|
const ensureCredential = async (url) => {
|
|
725
1009
|
const c = credOf(url);
|
|
726
1010
|
const target = targets.find((t) => t.url === url);
|
|
727
|
-
if (c.credential)
|
|
1011
|
+
if (c.credential) {
|
|
1012
|
+
adoptServerSuggestions(url, c.credential);
|
|
728
1013
|
return c.credential;
|
|
1014
|
+
}
|
|
729
1015
|
if (Date.now() - c.lastEnrollAt < ENROLL_RETRY_MS)
|
|
730
1016
|
return "";
|
|
731
1017
|
c.lastEnrollAt = Date.now();
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
const r = await fetch(`${url}/api/enroll`, {
|
|
739
|
-
method: "POST",
|
|
740
|
-
headers: { "content-type": "application/json" },
|
|
741
|
-
body: JSON.stringify({ code }),
|
|
742
|
-
signal: AbortSignal.timeout(1e4)
|
|
743
|
-
});
|
|
744
|
-
const b = r.ok ? await r.json().catch(() => null) : null;
|
|
745
|
-
if (b?.ok && typeof b.token === "string") {
|
|
746
|
-
c.credential = b.token;
|
|
1018
|
+
const code = target?.entry.code ?? "";
|
|
1019
|
+
if (!code) {
|
|
1020
|
+
try {
|
|
1021
|
+
const h = await fetch(`${url}/health`, { signal: AbortSignal.timeout(8000) });
|
|
1022
|
+
const hb = h.ok ? await h.json().catch(() => null) : null;
|
|
1023
|
+
if (hb?.auth?.mode === "public") {
|
|
747
1024
|
c.error = "";
|
|
748
|
-
|
|
749
|
-
log(`enrolled at ${url} (user ${b.userId} "${b.label}")${savedTo ? " - token saved" : ""}`);
|
|
750
|
-
return c.credential;
|
|
1025
|
+
return "";
|
|
751
1026
|
}
|
|
752
|
-
c.error =
|
|
753
|
-
}
|
|
754
|
-
c.error =
|
|
755
|
-
} else {
|
|
756
|
-
c.error = `server health unreachable (HTTP ${h.status})`;
|
|
1027
|
+
c.error = "private server - onboard via /openshare (needs an access code)";
|
|
1028
|
+
} catch (e) {
|
|
1029
|
+
c.error = `server unreachable: ${e?.message || e}`;
|
|
757
1030
|
}
|
|
1031
|
+
logError(`no credential for ${url}: ${c.error}`);
|
|
1032
|
+
return "";
|
|
1033
|
+
}
|
|
1034
|
+
try {
|
|
1035
|
+
const r = await fetch(`${url}/api/enroll`, {
|
|
1036
|
+
method: "POST",
|
|
1037
|
+
headers: { "content-type": "application/json" },
|
|
1038
|
+
body: JSON.stringify({ code }),
|
|
1039
|
+
signal: AbortSignal.timeout(1e4)
|
|
1040
|
+
});
|
|
1041
|
+
const b = r.ok ? await r.json().catch(() => null) : null;
|
|
1042
|
+
if (b?.ok && typeof b.token === "string") {
|
|
1043
|
+
c.credential = b.token;
|
|
1044
|
+
c.error = "";
|
|
1045
|
+
const savedTo = saveServerToken(url, c.credential);
|
|
1046
|
+
log(`enrolled at ${url} (user ${b.userId} "${b.label}")${savedTo ? " - token saved" : ""}`);
|
|
1047
|
+
adoptServerSuggestions(url, c.credential);
|
|
1048
|
+
return c.credential;
|
|
1049
|
+
}
|
|
1050
|
+
c.error = `enroll refused: ${b?.error ?? `HTTP ${r.status}`}`;
|
|
758
1051
|
} catch (e) {
|
|
759
1052
|
c.error = `enrollment failed: ${e?.message || e}`;
|
|
760
1053
|
}
|
|
@@ -1250,8 +1543,15 @@ function createEngine(deps) {
|
|
|
1250
1543
|
if (mode === "all" && cfg.urlFile !== URL_FILE_NONE) {
|
|
1251
1544
|
const file = cfg.urlFile ? isAbsolute(cfg.urlFile) ? cfg.urlFile : join2(deps.directory ?? process.cwd(), cfg.urlFile) : join2(deps.directory ?? process.cwd(), "openshare.urls.txt");
|
|
1252
1545
|
try {
|
|
1253
|
-
|
|
1254
|
-
|
|
1546
|
+
const line = cfg.urlFileFormat === "json" ? JSON.stringify({
|
|
1547
|
+
id: r.shareId,
|
|
1548
|
+
url,
|
|
1549
|
+
status: st.lastSentStatus ?? "waiting",
|
|
1550
|
+
gated: (st.opts?.authLogin ?? cfg.authLogin ?? "") !== ""
|
|
1551
|
+
}) + `
|
|
1552
|
+
` : url + `
|
|
1553
|
+
`;
|
|
1554
|
+
appendFileSync(file, line);
|
|
1255
1555
|
if (cfg.announceStderr && (file !== lastUrlFile || !urlFileAnnounced)) {
|
|
1256
1556
|
urlFileAnnounced = true;
|
|
1257
1557
|
lastUrlFile = file;
|
|
@@ -1275,7 +1575,20 @@ function createEngine(deps) {
|
|
|
1275
1575
|
}
|
|
1276
1576
|
return { authLogin: login, authPassword: password };
|
|
1277
1577
|
};
|
|
1278
|
-
const ttlFor = (st) => st.opts?.
|
|
1578
|
+
const ttlFor = (st) => st.opts?.ttlHours ?? cfg.ttlHours;
|
|
1579
|
+
const metaFor = () => {
|
|
1580
|
+
if (!cfg.meta || typeof cfg.meta !== "object")
|
|
1581
|
+
return;
|
|
1582
|
+
const out = {};
|
|
1583
|
+
let n = 0;
|
|
1584
|
+
for (const [k, v] of Object.entries(cfg.meta)) {
|
|
1585
|
+
if (n >= 20)
|
|
1586
|
+
break;
|
|
1587
|
+
out[k] = v.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (m, name) => process.env[name] ?? m);
|
|
1588
|
+
n++;
|
|
1589
|
+
}
|
|
1590
|
+
return Object.keys(out).length ? out : undefined;
|
|
1591
|
+
};
|
|
1279
1592
|
const applyOpts = (st, opts) => {
|
|
1280
1593
|
if (!opts)
|
|
1281
1594
|
return;
|
|
@@ -1287,11 +1600,11 @@ function createEngine(deps) {
|
|
|
1287
1600
|
if (!r.shareId || !r.secret || !r.wsReady)
|
|
1288
1601
|
continue;
|
|
1289
1602
|
const attach = { id: r.shareId, secret: r.secret, ...authFrame(st) };
|
|
1290
|
-
if (st.opts?.
|
|
1291
|
-
attach.
|
|
1603
|
+
if (st.opts?.ttlHours)
|
|
1604
|
+
attach.ttlHours = st.opts.ttlHours;
|
|
1292
1605
|
const sent = wsSend(r, { op: "init", attach });
|
|
1293
1606
|
if (sent)
|
|
1294
|
-
log(`opts re-synced to ${url.slice(0, 24)}${st.opts?.
|
|
1607
|
+
log(`opts re-synced to ${url.slice(0, 24)}${st.opts?.ttlHours ? ` (ttl ${st.opts.ttlHours}h)` : ""}`);
|
|
1295
1608
|
}
|
|
1296
1609
|
};
|
|
1297
1610
|
const deleteAbandoned = (r) => {
|
|
@@ -1487,7 +1800,7 @@ function createEngine(deps) {
|
|
|
1487
1800
|
if (r.initWasAttach) {
|
|
1488
1801
|
wsSend(r, { op: "init", attach: { id: r.shareId, secret: r.secret, ...authFrame(st) } });
|
|
1489
1802
|
} else {
|
|
1490
|
-
wsSend(r, { op: "init", create: { sessionID: st.sid,
|
|
1803
|
+
wsSend(r, { op: "init", create: { sessionID: st.sid, ttlHours: ttlFor(st), ...authFrame(st), ...cfg.configSet ? { set: cfg.configSet } : {}, ...metaFor() ? { meta: metaFor() } : {} } });
|
|
1491
1804
|
}
|
|
1492
1805
|
r.initTimer = setTimeout(() => {
|
|
1493
1806
|
if (seq !== r.socketSeq)
|
|
@@ -1658,7 +1971,7 @@ function createEngine(deps) {
|
|
|
1658
1971
|
r.shareId = null;
|
|
1659
1972
|
r.secret = null;
|
|
1660
1973
|
r.ackedRev = 0;
|
|
1661
|
-
wsSend(r, { op: "init", create: { sessionID: st.sid,
|
|
1974
|
+
wsSend(r, { op: "init", create: { sessionID: st.sid, ttlHours: ttlFor(st), ...authFrame(st), ...cfg.configSet ? { set: cfg.configSet } : {}, ...metaFor() ? { meta: metaFor() } : {} } });
|
|
1662
1975
|
return;
|
|
1663
1976
|
}
|
|
1664
1977
|
if (r.initWasAttach && typeof frame.rev === "number" && frame.rev < r.ackedRev) {
|
|
@@ -1667,7 +1980,7 @@ function createEngine(deps) {
|
|
|
1667
1980
|
r.shareId = null;
|
|
1668
1981
|
r.secret = null;
|
|
1669
1982
|
r.ackedRev = 0;
|
|
1670
|
-
wsSend(r, { op: "init", create: { sessionID: st.sid,
|
|
1983
|
+
wsSend(r, { op: "init", create: { sessionID: st.sid, ttlHours: ttlFor(st), ...authFrame(st), ...cfg.configSet ? { set: cfg.configSet } : {}, ...metaFor() ? { meta: metaFor() } : {} } });
|
|
1671
1984
|
return;
|
|
1672
1985
|
}
|
|
1673
1986
|
const wasFanoutCreate = !r.initWasAttach && st.everConnected;
|
|
@@ -1699,9 +2012,10 @@ function createEngine(deps) {
|
|
|
1699
2012
|
applyPolicy(frame.policy, r.url);
|
|
1700
2013
|
r.intentionalClose = false;
|
|
1701
2014
|
touch(st);
|
|
1702
|
-
|
|
2015
|
+
const ackTtl = typeof frame.ttlHours === "number" ? frame.ttlHours : typeof frame.ttlDays === "number" ? frame.ttlDays * 24 : undefined;
|
|
2016
|
+
if (typeof ackTtl === "number") {
|
|
1703
2017
|
const wanted = ttlFor(st);
|
|
1704
|
-
const clamp = wanted != null &&
|
|
2018
|
+
const clamp = wanted != null && ackTtl !== wanted ? { requested: wanted, effective: ackTtl } : null;
|
|
1705
2019
|
if (JSON.stringify(clamp) !== JSON.stringify(st.ttlClamped)) {
|
|
1706
2020
|
st.ttlClamped = clamp;
|
|
1707
2021
|
scheduleBindingWrite(st);
|
|
@@ -1711,6 +2025,28 @@ function createEngine(deps) {
|
|
|
1711
2025
|
st.lastError = null;
|
|
1712
2026
|
scheduleBindingWrite(st);
|
|
1713
2027
|
}
|
|
2028
|
+
const wantedGate = (st.opts?.authLogin ?? cfg.authLogin ?? "") !== "" && (st.opts?.authPassword ?? cfg.authPassword ?? "") !== "";
|
|
2029
|
+
if (wantedGate && frame.auth === false && !r.warnedNoGate) {
|
|
2030
|
+
r.warnedNoGate = true;
|
|
2031
|
+
logError(`${r.url}: read auth requested but the page has NO gate - check the server's auth handling`);
|
|
2032
|
+
} else if (frame.auth === true) {
|
|
2033
|
+
r.warnedNoGate = false;
|
|
2034
|
+
}
|
|
2035
|
+
if (credOf(r.url).credential) {
|
|
2036
|
+
const priv = {};
|
|
2037
|
+
const dir = deps.directory ?? "";
|
|
2038
|
+
if (dir)
|
|
2039
|
+
priv.directory = dir;
|
|
2040
|
+
if (deps.worktree)
|
|
2041
|
+
priv.worktree = String(deps.worktree);
|
|
2042
|
+
try {
|
|
2043
|
+
priv.home = homedir();
|
|
2044
|
+
} catch {}
|
|
2045
|
+
if (Object.keys(priv).length) {
|
|
2046
|
+
const seq = ++nextSeq;
|
|
2047
|
+
wsSend(r, { op: "sync", items: [{ type: "pmeta", sessionID: st.sid, data: priv }], status: undefined, seq });
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
1714
2050
|
announceUrl(st, r);
|
|
1715
2051
|
if (!st.wroteBinding) {
|
|
1716
2052
|
chain = chain.then(() => writeBinding(st)).catch(() => {});
|
|
@@ -1921,6 +2257,15 @@ function createEngine(deps) {
|
|
|
1921
2257
|
}).catch((e) => logError("flush error", e?.message || e));
|
|
1922
2258
|
}, wait);
|
|
1923
2259
|
};
|
|
2260
|
+
const homeDir = (() => {
|
|
2261
|
+
try {
|
|
2262
|
+
return homedir();
|
|
2263
|
+
} catch {
|
|
2264
|
+
return "";
|
|
2265
|
+
}
|
|
2266
|
+
})();
|
|
2267
|
+
const userPatterns = cfg.redact?.patterns;
|
|
2268
|
+
const redactionRules = activeRules(userPatterns);
|
|
1924
2269
|
const enqueue = (st, entry) => {
|
|
1925
2270
|
if (!st || !entry)
|
|
1926
2271
|
return;
|
|
@@ -1937,6 +2282,22 @@ function createEngine(deps) {
|
|
|
1937
2282
|
}
|
|
1938
2283
|
if (h)
|
|
1939
2284
|
st.sentHash.set(entry.key, h);
|
|
2285
|
+
{
|
|
2286
|
+
const gate = authFrame(st);
|
|
2287
|
+
const gated = gate.authLogin !== "" && gate.authPassword !== "";
|
|
2288
|
+
const pass = redactItem(entry.item, { rules: redactionRules, home: homeDir, enabled: !gated });
|
|
2289
|
+
if (pass.item !== entry.item)
|
|
2290
|
+
entry = { ...entry, item: pass.item };
|
|
2291
|
+
if (pass.hits.length) {
|
|
2292
|
+
const led = redactionLedger.get(st.sid) ?? { count: 0, samples: [] };
|
|
2293
|
+
led.count += pass.hits.length;
|
|
2294
|
+
for (const hit of pass.hits) {
|
|
2295
|
+
if (led.samples.length < 5 && !led.samples.some((x) => x.kind === hit.kind))
|
|
2296
|
+
led.samples.push(hit);
|
|
2297
|
+
}
|
|
2298
|
+
redactionLedger.set(st.sid, led);
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
1940
2301
|
st.queue.set(entry.key, entry.item);
|
|
1941
2302
|
indexMessage(entry);
|
|
1942
2303
|
if (debugEvents)
|
|
@@ -1945,6 +2306,24 @@ function createEngine(deps) {
|
|
|
1945
2306
|
ensureConnected(st);
|
|
1946
2307
|
scheduleFlush(st);
|
|
1947
2308
|
};
|
|
2309
|
+
statsProvider = (sid) => {
|
|
2310
|
+
const st = sessions.get(sid);
|
|
2311
|
+
let messages = 0;
|
|
2312
|
+
let toolCalls = 0;
|
|
2313
|
+
if (st) {
|
|
2314
|
+
for (const [, merged] of st.merged) {
|
|
2315
|
+
messages++;
|
|
2316
|
+
const content = merged.content;
|
|
2317
|
+
if (Array.isArray(content))
|
|
2318
|
+
toolCalls += content.filter((c) => !!c && c.type === "tool").length;
|
|
2319
|
+
}
|
|
2320
|
+
for (const it of st.queue.values()) {
|
|
2321
|
+
if (it?.type === "message")
|
|
2322
|
+
messages++;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
return { messages, toolCalls };
|
|
2326
|
+
};
|
|
1948
2327
|
const msgIndex = new Map;
|
|
1949
2328
|
const tombstoned = new Set;
|
|
1950
2329
|
const indexMessage = (entry) => {
|
|
@@ -2103,6 +2482,23 @@ function createEngine(deps) {
|
|
|
2103
2482
|
svelte: "svelte"
|
|
2104
2483
|
};
|
|
2105
2484
|
const FILE_TOOLS = new Set(["read", "write", "edit", "patch"]);
|
|
2485
|
+
const refineEmbeddedLanguage = (meta, text) => {
|
|
2486
|
+
if (meta.language !== "html")
|
|
2487
|
+
return false;
|
|
2488
|
+
const head = text.slice(0, 4000);
|
|
2489
|
+
if (/<[a-zA-Z][^>\n]*>/.test(head))
|
|
2490
|
+
return false;
|
|
2491
|
+
let refined;
|
|
2492
|
+
if (/^[ \t]*[.#][A-Za-z][\w-]*[^\n{]*\{/m.test(head) && !/\b(function|=>|return)\b/.test(head)) {
|
|
2493
|
+
refined = "css";
|
|
2494
|
+
} else if (/\b(function|const|let|var|return|if|for|while|=>|addEventListener|document\.|window\.)\b/.test(head) || /[;{}]\s*$/m.test(head)) {
|
|
2495
|
+
refined = "javascript";
|
|
2496
|
+
}
|
|
2497
|
+
if (!refined || meta.language === refined)
|
|
2498
|
+
return false;
|
|
2499
|
+
meta.language = refined;
|
|
2500
|
+
return true;
|
|
2501
|
+
};
|
|
2106
2502
|
const stampFileLanguages = () => {
|
|
2107
2503
|
for (const m of msgIndex.values()) {
|
|
2108
2504
|
if (m.role !== "assistant")
|
|
@@ -2129,6 +2525,10 @@ function createEngine(deps) {
|
|
|
2129
2525
|
mutated = true;
|
|
2130
2526
|
}
|
|
2131
2527
|
}
|
|
2528
|
+
if (part.name === "read" && typeof state.output === "string" && state.output.length > 0) {
|
|
2529
|
+
if (refineEmbeddedLanguage(meta, state.output))
|
|
2530
|
+
mutated = true;
|
|
2531
|
+
}
|
|
2132
2532
|
if (typeof meta.lineStart !== "number" && part.name === "read") {
|
|
2133
2533
|
const off = state.input?.offset;
|
|
2134
2534
|
if (typeof off === "number" && off >= 0 && off > 0) {
|
|
@@ -2367,7 +2767,7 @@ function createEngine(deps) {
|
|
|
2367
2767
|
const backfillSession = async (st, client, sid, info) => {
|
|
2368
2768
|
const sessionInfoValue = info ?? await sessionInfo(client, sid);
|
|
2369
2769
|
if (sessionInfoValue)
|
|
2370
|
-
enqueue(st, itemFor("session", redact(sessionInfoValue), sid));
|
|
2770
|
+
enqueue(st, itemFor("session", stripLocalFacts(redact(sessionInfoValue)), sid));
|
|
2371
2771
|
let modelSeen = false;
|
|
2372
2772
|
const consume = (messages) => {
|
|
2373
2773
|
for (const message of messages) {
|
|
@@ -2407,7 +2807,7 @@ function createEngine(deps) {
|
|
|
2407
2807
|
if (messages.length === 0 && !info) {
|
|
2408
2808
|
const fetched = await sessionInfo(client, st.sid);
|
|
2409
2809
|
if (fetched)
|
|
2410
|
-
enqueue(st, itemFor("session", redact(fetched), st.sid));
|
|
2810
|
+
enqueue(st, itemFor("session", stripLocalFacts(redact(fetched)), st.sid));
|
|
2411
2811
|
}
|
|
2412
2812
|
if (typeof client.session.list === "function") {
|
|
2413
2813
|
try {
|
|
@@ -2485,7 +2885,7 @@ function createEngine(deps) {
|
|
|
2485
2885
|
const st = sessions.get(parent);
|
|
2486
2886
|
if (st?.activated && !st.closed) {
|
|
2487
2887
|
if (info)
|
|
2488
|
-
enqueue(st, itemFor("session", redact(info), sid));
|
|
2888
|
+
enqueue(st, itemFor("session", stripLocalFacts(redact(info)), sid));
|
|
2489
2889
|
}
|
|
2490
2890
|
return;
|
|
2491
2891
|
}
|
|
@@ -2496,7 +2896,7 @@ function createEngine(deps) {
|
|
|
2496
2896
|
if (flag.opts)
|
|
2497
2897
|
applyOpts(st, flag.opts);
|
|
2498
2898
|
if (info)
|
|
2499
|
-
enqueue(st, itemFor("session", redact(info), sid));
|
|
2899
|
+
enqueue(st, itemFor("session", stripLocalFacts(redact(info)), sid));
|
|
2500
2900
|
if (t === "session.updated")
|
|
2501
2901
|
chain = chain.then(() => refreshOptOuts(st)).catch(() => {});
|
|
2502
2902
|
return;
|
|
@@ -2548,7 +2948,7 @@ function createEngine(deps) {
|
|
|
2548
2948
|
if (!st)
|
|
2549
2949
|
return;
|
|
2550
2950
|
if (info)
|
|
2551
|
-
enqueue(st, itemFor("session", redact(info), sid));
|
|
2951
|
+
enqueue(st, itemFor("session", stripLocalFacts(redact(info)), sid));
|
|
2552
2952
|
if (t === "session.updated" && st.activated && !st.closed) {
|
|
2553
2953
|
chain = chain.then(() => refreshOptOuts(st)).catch(() => {});
|
|
2554
2954
|
}
|