@percher/shared 0.1.9 → 1.0.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/LICENSE +132 -0
- package/dist/brand.d.ts +64 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +67 -0
- package/dist/brand.js.map +1 -0
- package/dist/client-ip-attestation.d.ts +67 -0
- package/dist/client-ip-attestation.d.ts.map +1 -0
- package/dist/client-ip-attestation.js +74 -0
- package/dist/client-ip-attestation.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/legal-changes.d.ts +118 -0
- package/dist/legal-changes.d.ts.map +1 -0
- package/dist/legal-changes.js +135 -0
- package/dist/legal-changes.js.map +1 -0
- package/dist/legal.d.ts +52 -9
- package/dist/legal.d.ts.map +1 -1
- package/dist/legal.js +47 -8
- package/dist/legal.js.map +1 -1
- package/dist/limits.d.ts +8 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +8 -0
- package/dist/limits.js.map +1 -1
- package/dist/oauth-client-name.d.ts +22 -0
- package/dist/oauth-client-name.d.ts.map +1 -0
- package/dist/oauth-client-name.js +37 -0
- package/dist/oauth-client-name.js.map +1 -0
- package/dist/oauth-scopes.d.ts +19 -0
- package/dist/oauth-scopes.d.ts.map +1 -0
- package/dist/oauth-scopes.js +31 -0
- package/dist/oauth-scopes.js.map +1 -0
- package/dist/platform-env.d.ts.map +1 -1
- package/dist/platform-env.js +6 -0
- package/dist/platform-env.js.map +1 -1
- package/dist/platform.d.ts +3 -3
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +3 -3
- package/dist/platform.js.map +1 -1
- package/dist/signup-invite-hash.d.ts +8 -0
- package/dist/signup-invite-hash.d.ts.map +1 -0
- package/dist/signup-invite-hash.js +11 -0
- package/dist/signup-invite-hash.js.map +1 -0
- package/dist/signup-invite.d.ts +23 -0
- package/dist/signup-invite.d.ts.map +1 -0
- package/dist/signup-invite.js +34 -0
- package/dist/signup-invite.js.map +1 -0
- package/dist/timeouts.d.ts +85 -0
- package/dist/timeouts.d.ts.map +1 -1
- package/dist/timeouts.js +84 -0
- package/dist/timeouts.js.map +1 -1
- package/package.json +12 -2
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { LEGAL_DOCUMENT_VERSIONS } from "./legal.js";
|
|
2
|
+
/**
|
|
3
|
+
* Newest last. Empty because version 1.0 is the first publication under
|
|
4
|
+
* Kaizensu AB: there is no earlier version to have changed from, so the chain
|
|
5
|
+
* starts here rather than being backfilled with a notice nobody was owed.
|
|
6
|
+
*/
|
|
7
|
+
export const LEGAL_CHANGES = [];
|
|
8
|
+
/**
|
|
9
|
+
* Header carrying the change ids the signup form actually rendered.
|
|
10
|
+
*
|
|
11
|
+
* Same channel as the terms-acceptance header, for the same reason: the signup
|
|
12
|
+
* flow returns no session when email verification is required, so a follow-up
|
|
13
|
+
* authenticated POST would be refused and the evidence lost. Comma-separated,
|
|
14
|
+
* and never trusted on its own — the server intersects it with what it can
|
|
15
|
+
* independently see was disclosable.
|
|
16
|
+
*/
|
|
17
|
+
export const SIGNUP_DISCLOSURE_HEADER = "x-percher-disclosed-changes";
|
|
18
|
+
/** The notice period the documents promise, in milliseconds. */
|
|
19
|
+
export const MATERIAL_CHANGE_NOTICE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
20
|
+
/** `id` shape: the date half is parsed, so this is a gate and not decoration. */
|
|
21
|
+
export const LEGAL_CHANGE_ID_PATTERN = /^\d{4}-\d{2}-\d{2}-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
22
|
+
/** Versions are `1.0` or `1.0.1`; an editorial change may only move the last component. */
|
|
23
|
+
export const LEGAL_VERSION_PATTERN = /^\d+\.\d+(?:\.\d+)?$/;
|
|
24
|
+
/**
|
|
25
|
+
* The canonical JSON of an entry, for the hash that freezes it after the first
|
|
26
|
+
* send. Keys are emitted in a fixed order rather than insertion order so that
|
|
27
|
+
* reformatting the source cannot invalidate a change already announced.
|
|
28
|
+
*/
|
|
29
|
+
export function canonicalLegalChangeJson(change) {
|
|
30
|
+
return JSON.stringify({
|
|
31
|
+
announceAt: change.announceAt,
|
|
32
|
+
documents: change.documents.map((document) => ({
|
|
33
|
+
from: document.from,
|
|
34
|
+
key: document.key,
|
|
35
|
+
to: document.to,
|
|
36
|
+
})),
|
|
37
|
+
effectiveAt: change.effectiveAt,
|
|
38
|
+
enforcement: change.enforcement,
|
|
39
|
+
id: change.id,
|
|
40
|
+
materiality: change.materiality,
|
|
41
|
+
modifiesService: change.modifiesService === true,
|
|
42
|
+
reason: change.reason,
|
|
43
|
+
summary: { en: change.summary.en, sv: change.summary.sv },
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** Entries whose notice may go out now: material, announced, newest last. */
|
|
47
|
+
export function openMaterialChanges(now, changes = LEGAL_CHANGES) {
|
|
48
|
+
return changes.filter((change) => change.materiality === "material" && Date.parse(change.announceAt) <= now);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The latest declared version of a document, or its current published version
|
|
52
|
+
* when no change has ever named it.
|
|
53
|
+
*/
|
|
54
|
+
export function declaredVersion(key, changes = LEGAL_CHANGES) {
|
|
55
|
+
for (let index = changes.length - 1; index >= 0; index -= 1) {
|
|
56
|
+
const document = changes[index]?.documents.find((entry) => entry.key === key);
|
|
57
|
+
if (document)
|
|
58
|
+
return document.to;
|
|
59
|
+
}
|
|
60
|
+
return LEGAL_DOCUMENT_VERSIONS[key];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Order two `1.0` / `1.0.1` strings. Unparseable input sorts as older, so a
|
|
64
|
+
* version we cannot read never suppresses a notice.
|
|
65
|
+
*/
|
|
66
|
+
export function compareVersions(left, right) {
|
|
67
|
+
// Components are compared as digit strings, never parsed to numbers. The
|
|
68
|
+
// pattern accepts a component of any length, and Number.parseInt collapses
|
|
69
|
+
// anything past the safe-integer range onto the same value: two different
|
|
70
|
+
// versions would then read as equal, and pendingAcknowledgement would clear a
|
|
71
|
+
// banner for a change nobody had acknowledged. Leading zeros are stripped
|
|
72
|
+
// first, so `01` and `1` stay the same version.
|
|
73
|
+
const parse = (value) => {
|
|
74
|
+
if (!LEGAL_VERSION_PATTERN.test(value))
|
|
75
|
+
return null;
|
|
76
|
+
const parts = value.split(".");
|
|
77
|
+
const normalize = (part) => (part ?? "0").replace(/^0+(?=\d)/, "");
|
|
78
|
+
return [normalize(parts[0]), normalize(parts[1]), normalize(parts[2])];
|
|
79
|
+
};
|
|
80
|
+
// Longer digit string wins; equal length falls back to lexicographic order,
|
|
81
|
+
// which is numeric order once both are the same width.
|
|
82
|
+
const order = (a, b) => {
|
|
83
|
+
if (a.length !== b.length)
|
|
84
|
+
return a.length > b.length ? 1 : -1;
|
|
85
|
+
if (a === b)
|
|
86
|
+
return 0;
|
|
87
|
+
return a > b ? 1 : -1;
|
|
88
|
+
};
|
|
89
|
+
const a = parse(left);
|
|
90
|
+
const b = parse(right);
|
|
91
|
+
if (!a)
|
|
92
|
+
return b ? -1 : 0;
|
|
93
|
+
if (!b)
|
|
94
|
+
return 1;
|
|
95
|
+
for (let index = 0; index < 3; index += 1) {
|
|
96
|
+
const difference = order(a[index] ?? "0", b[index] ?? "0");
|
|
97
|
+
if (difference !== 0)
|
|
98
|
+
return difference;
|
|
99
|
+
}
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The change a user has not yet acknowledged, if any.
|
|
104
|
+
*
|
|
105
|
+
* Read by the dashboard banner and by the operator dry-run. Deliberately NOT
|
|
106
|
+
* read by the API's auth middleware: a stale accepted version is never an
|
|
107
|
+
* authorization failure. Extending the 403 to a version comparison would break
|
|
108
|
+
* every customer's deploy pipeline and every agent integration on a chosen
|
|
109
|
+
* date, with the remedy being a browser click a CI runner cannot perform — and
|
|
110
|
+
* it would be stricter than the Terms promise, which make continued use the
|
|
111
|
+
* acceptance. A test in the API asserts that absence.
|
|
112
|
+
*/
|
|
113
|
+
export function pendingAcknowledgement(acceptedTermsVersion, now, changes = LEGAL_CHANGES) {
|
|
114
|
+
if (!acceptedTermsVersion)
|
|
115
|
+
return null;
|
|
116
|
+
for (let index = changes.length - 1; index >= 0; index -= 1) {
|
|
117
|
+
const change = changes[index];
|
|
118
|
+
if (change?.materiality !== "material")
|
|
119
|
+
continue;
|
|
120
|
+
if (Date.parse(change.effectiveAt) > now)
|
|
121
|
+
continue;
|
|
122
|
+
const terms = change.documents.find((entry) => entry.key === "terms");
|
|
123
|
+
if (!terms)
|
|
124
|
+
continue;
|
|
125
|
+
// Accepting a LATER version acknowledges this one. Exact equality looked
|
|
126
|
+
// right and was not: a material change to 1.1 followed by an editorial
|
|
127
|
+
// correction to 1.1.1 leaves someone who accepted 1.1.1 staring at a
|
|
128
|
+
// banner for a change they have demonstrably already agreed to, with no
|
|
129
|
+
// way to clear it, because the click re-stamps the newest version and the
|
|
130
|
+
// comparison keeps failing.
|
|
131
|
+
return compareVersions(acceptedTermsVersion, terms.to) >= 0 ? null : change;
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=legal-changes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legal-changes.js","sourceRoot":"","sources":["../src/legal-changes.ts"],"names":[],"mappings":"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"}
|
package/dist/legal.d.ts
CHANGED
|
@@ -1,15 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Published legal-document versions, shared by the dashboard and the API.
|
|
3
3
|
*
|
|
4
|
-
* Keep these
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Keep these in lock-step with the `**Version:**` headers of the canonical
|
|
5
|
+
* files under legal/ — `scripts/check-legal-versions.mjs` fails review when
|
|
6
|
+
* they drift. The numbering restarts at 1.0 because Kaizensu AB now owns and
|
|
7
|
+
* operates the platform, so these are the company's first published versions;
|
|
8
|
+
* the earlier 2.x sequence belongs to the sole-trader era and does not carry
|
|
9
|
+
* over. The acceptance version is selected by trusted server code, never by a
|
|
10
|
+
* browser header. Changing a value records document evidence; nothing compares
|
|
11
|
+
* or orders these strings, so it neither publishes new terms nor forces
|
|
12
|
+
* re-acceptance.
|
|
7
13
|
*/
|
|
8
14
|
export declare const LEGAL_DOCUMENT_VERSIONS: {
|
|
9
|
-
readonly terms: "
|
|
10
|
-
readonly aup: "
|
|
11
|
-
readonly privacy: "
|
|
15
|
+
readonly terms: "1.0";
|
|
16
|
+
readonly aup: "1.0";
|
|
17
|
+
readonly privacy: "1.0";
|
|
18
|
+
readonly cookies: "1.0";
|
|
19
|
+
readonly dpa: "1.0";
|
|
20
|
+
readonly subprocessors: "1.0";
|
|
21
|
+
readonly dataPortability: "1.0";
|
|
12
22
|
};
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
/**
|
|
24
|
+
* What each published document said at the version above.
|
|
25
|
+
*
|
|
26
|
+
* sha256 over the English then the Swedish file, newline-normalised and
|
|
27
|
+
* NUL-separated, so a checkout's line-ending setting cannot move the value.
|
|
28
|
+
*
|
|
29
|
+
* Without this the version header was the only anchor, and it is one a person
|
|
30
|
+
* has to remember to move. Editing the body of a document and leaving the
|
|
31
|
+
* header alone passed every check: the sync step copied the new bytes to the
|
|
32
|
+
* served copy, no version moved, so no change had to be declared and nobody
|
|
33
|
+
* was notified. That is the whole mechanism defeated by an omission rather
|
|
34
|
+
* than a decision. Now the bytes themselves are the anchor: change a document
|
|
35
|
+
* and the check fails until the change is declared.
|
|
36
|
+
*
|
|
37
|
+
* `bun scripts/check-legal-versions.mjs --print-hashes` prints the current
|
|
38
|
+
* values; never paste one in without the version move and the change entry it
|
|
39
|
+
* belongs to.
|
|
40
|
+
*/
|
|
41
|
+
export declare const LEGAL_DOCUMENT_HASHES: {
|
|
42
|
+
readonly terms: "d412c9e85d7fefa5e3595b47aaea67a5d9f495955e458b2d0b4f43c9215317ce";
|
|
43
|
+
readonly aup: "138ce3bdd9d244a4f31c98955b3c9d59920f32c056608f1135dc8393769a761f";
|
|
44
|
+
readonly privacy: "186910f4f4825d3ac3bbc3340e7bf4babccea33f8b4166a10c892a7012cff175";
|
|
45
|
+
readonly cookies: "9c8250a4ea2fbf701d04cfb145d466ff8ba410e6da92433950f119952ba16bf0";
|
|
46
|
+
readonly dpa: "4c97fd36c886a113477f3eb2c2c0a9b5e03820ec624439906c938bbd40f7a0fb";
|
|
47
|
+
readonly subprocessors: "e7380f2d604814e1f06f44d8c4afcb34143c1c7994dfecdcec4cf1bbc8810363";
|
|
48
|
+
readonly dataPortability: "27a7d3ec0175af8f3f1c67db1a65bf01f74d5139b44d9134caef5dd8a924d775";
|
|
49
|
+
};
|
|
50
|
+
/** Version of the canonical English Terms presented during account acceptance. */
|
|
51
|
+
export declare const CURRENT_TERMS_VERSION: "1.0";
|
|
52
|
+
/** The "Last updated" date shared by the current revision of the documents. */
|
|
53
|
+
export declare const LEGAL_DOCUMENT_LAST_UPDATED = "10 September 2026";
|
|
54
|
+
export interface TermsAcceptanceEvidence {
|
|
55
|
+
version?: string;
|
|
56
|
+
language?: "en" | "sv";
|
|
57
|
+
}
|
|
15
58
|
//# sourceMappingURL=legal.d.ts.map
|
package/dist/legal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legal.d.ts","sourceRoot":"","sources":["../src/legal.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"legal.d.ts","sourceRoot":"","sources":["../src/legal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB;aAClC,KAAK,EAAE,KAAK;aACZ,GAAG,EAAE,KAAK;aACV,OAAO,EAAE,KAAK;aACd,OAAO,EAAE,KAAK;aACd,GAAG,EAAE,KAAK;aACV,aAAa,EAAE,KAAK;aACpB,eAAe,EAAE,KAAK;CACd,CAAC;AAEX;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,qBAAqB;aAChC,KAAK,EAAE,kEAAkE;aACzE,GAAG,EAAE,kEAAkE;aACvE,OAAO,EAAE,kEAAkE;aAC3E,OAAO,EAAE,kEAAkE;aAC3E,GAAG,EAAE,kEAAkE;aACvE,aAAa,EAAE,kEAAkE;aACjF,eAAe,EAAE,kEAAkE;CAC3E,CAAC;AAEX,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,OAAgC,CAAC;AAEnE,+EAA+E;AAC/E,eAAO,MAAM,2BAA2B,sBAAsB,CAAC;AAE/D,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB"}
|
package/dist/legal.js
CHANGED
|
@@ -1,15 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Published legal-document versions, shared by the dashboard and the API.
|
|
3
3
|
*
|
|
4
|
-
* Keep these
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Keep these in lock-step with the `**Version:**` headers of the canonical
|
|
5
|
+
* files under legal/ — `scripts/check-legal-versions.mjs` fails review when
|
|
6
|
+
* they drift. The numbering restarts at 1.0 because Kaizensu AB now owns and
|
|
7
|
+
* operates the platform, so these are the company's first published versions;
|
|
8
|
+
* the earlier 2.x sequence belongs to the sole-trader era and does not carry
|
|
9
|
+
* over. The acceptance version is selected by trusted server code, never by a
|
|
10
|
+
* browser header. Changing a value records document evidence; nothing compares
|
|
11
|
+
* or orders these strings, so it neither publishes new terms nor forces
|
|
12
|
+
* re-acceptance.
|
|
7
13
|
*/
|
|
8
14
|
export const LEGAL_DOCUMENT_VERSIONS = {
|
|
9
|
-
terms: "
|
|
10
|
-
aup: "
|
|
11
|
-
privacy: "
|
|
15
|
+
terms: "1.0",
|
|
16
|
+
aup: "1.0",
|
|
17
|
+
privacy: "1.0",
|
|
18
|
+
cookies: "1.0",
|
|
19
|
+
dpa: "1.0",
|
|
20
|
+
subprocessors: "1.0",
|
|
21
|
+
dataPortability: "1.0",
|
|
12
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* What each published document said at the version above.
|
|
25
|
+
*
|
|
26
|
+
* sha256 over the English then the Swedish file, newline-normalised and
|
|
27
|
+
* NUL-separated, so a checkout's line-ending setting cannot move the value.
|
|
28
|
+
*
|
|
29
|
+
* Without this the version header was the only anchor, and it is one a person
|
|
30
|
+
* has to remember to move. Editing the body of a document and leaving the
|
|
31
|
+
* header alone passed every check: the sync step copied the new bytes to the
|
|
32
|
+
* served copy, no version moved, so no change had to be declared and nobody
|
|
33
|
+
* was notified. That is the whole mechanism defeated by an omission rather
|
|
34
|
+
* than a decision. Now the bytes themselves are the anchor: change a document
|
|
35
|
+
* and the check fails until the change is declared.
|
|
36
|
+
*
|
|
37
|
+
* `bun scripts/check-legal-versions.mjs --print-hashes` prints the current
|
|
38
|
+
* values; never paste one in without the version move and the change entry it
|
|
39
|
+
* belongs to.
|
|
40
|
+
*/
|
|
41
|
+
export const LEGAL_DOCUMENT_HASHES = {
|
|
42
|
+
terms: "d412c9e85d7fefa5e3595b47aaea67a5d9f495955e458b2d0b4f43c9215317ce",
|
|
43
|
+
aup: "138ce3bdd9d244a4f31c98955b3c9d59920f32c056608f1135dc8393769a761f",
|
|
44
|
+
privacy: "186910f4f4825d3ac3bbc3340e7bf4babccea33f8b4166a10c892a7012cff175",
|
|
45
|
+
cookies: "9c8250a4ea2fbf701d04cfb145d466ff8ba410e6da92433950f119952ba16bf0",
|
|
46
|
+
dpa: "4c97fd36c886a113477f3eb2c2c0a9b5e03820ec624439906c938bbd40f7a0fb",
|
|
47
|
+
subprocessors: "e7380f2d604814e1f06f44d8c4afcb34143c1c7994dfecdcec4cf1bbc8810363",
|
|
48
|
+
dataPortability: "27a7d3ec0175af8f3f1c67db1a65bf01f74d5139b44d9134caef5dd8a924d775",
|
|
49
|
+
};
|
|
50
|
+
/** Version of the canonical English Terms presented during account acceptance. */
|
|
13
51
|
export const CURRENT_TERMS_VERSION = LEGAL_DOCUMENT_VERSIONS.terms;
|
|
14
|
-
|
|
52
|
+
/** The "Last updated" date shared by the current revision of the documents. */
|
|
53
|
+
export const LEGAL_DOCUMENT_LAST_UPDATED = "10 September 2026";
|
|
15
54
|
//# sourceMappingURL=legal.js.map
|
package/dist/legal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legal.js","sourceRoot":"","sources":["../src/legal.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"legal.js","sourceRoot":"","sources":["../src/legal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,KAAK,EAAE,KAAK;IACZ,GAAG,EAAE,KAAK;IACV,OAAO,EAAE,KAAK;IACd,OAAO,EAAE,KAAK;IACd,GAAG,EAAE,KAAK;IACV,aAAa,EAAE,KAAK;IACpB,eAAe,EAAE,KAAK;CACd,CAAC;AAEX;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,KAAK,EAAE,kEAAkE;IACzE,GAAG,EAAE,kEAAkE;IACvE,OAAO,EAAE,kEAAkE;IAC3E,OAAO,EAAE,kEAAkE;IAC3E,GAAG,EAAE,kEAAkE;IACvE,aAAa,EAAE,kEAAkE;IACjF,eAAe,EAAE,kEAAkE;CAC3E,CAAC;AAEX,kFAAkF;AAClF,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAC,KAAK,CAAC;AAEnE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,2BAA2B,GAAG,mBAAmB,CAAC"}
|
package/dist/limits.d.ts
CHANGED
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
* extractor, which guards against zip bombs.
|
|
7
7
|
*/
|
|
8
8
|
export declare const MAX_TARBALL_BYTES: number;
|
|
9
|
+
/**
|
|
10
|
+
* Max accepted tarball for an ANONYMOUS deploy (25 MiB). The anonymous lane
|
|
11
|
+
* is a demo sandbox with no account behind it: the whole lane is capped at a
|
|
12
|
+
* handful of live apps, each one costs disk that nobody has agreed to pay for,
|
|
13
|
+
* and no legitimate demo needs 500 MB. Enforced on the streaming save, so a
|
|
14
|
+
* chunked or forged Content-Length cannot walk past it.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MAX_ANON_TARBALL_BYTES: number;
|
|
9
17
|
/**
|
|
10
18
|
* Max accepted inline-source publish size in bytes (25 MB) — the sum of
|
|
11
19
|
* the decoded `content` bytes an assistant passes to `percher_publish` /
|
package/dist/limits.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,QAAoB,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AAEzD;;;;;;GAMG;AAEH;;8BAE8B;AAC9B,eAAO,MAAM,sBAAsB,QAAY,CAAC;AAEhD;;;;;iEAKiE;AACjE,eAAO,MAAM,sBAAsB,QAAY,CAAC;AAEhD;;;;qDAIqD;AACrD,eAAO,MAAM,qBAAqB,QAAqB,CAAC;AAExD;;4BAE4B;AAC5B,eAAO,MAAM,8BAA8B,QAAsB,CAAC;AAElE;yEACyE;AACzE,eAAO,MAAM,iCAAiC,IAAI,CAAC;AAEnD,8DAA8D;AAC9D,eAAO,MAAM,wBAAwB,MAAM,CAAC"}
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,QAAoB,CAAC;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAEvD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AAEzD;;;;;;GAMG;AAEH;;8BAE8B;AAC9B,eAAO,MAAM,sBAAsB,QAAY,CAAC;AAEhD;;;;;iEAKiE;AACjE,eAAO,MAAM,sBAAsB,QAAY,CAAC;AAEhD;;;;qDAIqD;AACrD,eAAO,MAAM,qBAAqB,QAAqB,CAAC;AAExD;;4BAE4B;AAC5B,eAAO,MAAM,8BAA8B,QAAsB,CAAC;AAElE;yEACyE;AACzE,eAAO,MAAM,iCAAiC,IAAI,CAAC;AAEnD,8DAA8D;AAC9D,eAAO,MAAM,wBAAwB,MAAM,CAAC"}
|
package/dist/limits.js
CHANGED
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
* extractor, which guards against zip bombs.
|
|
7
7
|
*/
|
|
8
8
|
export const MAX_TARBALL_BYTES = 500 * 1024 * 1024;
|
|
9
|
+
/**
|
|
10
|
+
* Max accepted tarball for an ANONYMOUS deploy (25 MiB). The anonymous lane
|
|
11
|
+
* is a demo sandbox with no account behind it: the whole lane is capped at a
|
|
12
|
+
* handful of live apps, each one costs disk that nobody has agreed to pay for,
|
|
13
|
+
* and no legitimate demo needs 500 MB. Enforced on the streaming save, so a
|
|
14
|
+
* chunked or forged Content-Length cannot walk past it.
|
|
15
|
+
*/
|
|
16
|
+
export const MAX_ANON_TARBALL_BYTES = 25 * 1024 * 1024;
|
|
9
17
|
/**
|
|
10
18
|
* Max accepted inline-source publish size in bytes (25 MB) — the sum of
|
|
11
19
|
* the decoded `content` bytes an assistant passes to `percher_publish` /
|
package/dist/limits.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzD;;;;;;GAMG;AAEH;;8BAE8B;AAC9B,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhD;;;;;iEAKiE;AACjE,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhD;;;;qDAIqD;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAExD;;4BAE4B;AAC5B,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAElE;yEACyE;AACzE,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC;AAEnD,8DAA8D;AAC9D,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC"}
|
|
1
|
+
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEvD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzD;;;;;;GAMG;AAEH;;8BAE8B;AAC9B,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhD;;;;;iEAKiE;AACjE,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhD;;;;qDAIqD;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAExD;;4BAE4B;AAC5B,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAElE;yEACyE;AACzE,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC;AAEnD,8DAA8D;AAC9D,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Maximum number of Unicode code points shown from an open-DCR client name. */
|
|
2
|
+
export declare const MAX_OAUTH_CLIENT_NAME_DISPLAY_CODE_POINTS = 80;
|
|
3
|
+
/**
|
|
4
|
+
* Normalise an attacker-controlled OAuth Dynamic Client Registration name for
|
|
5
|
+
* display. The stored registration metadata remains verbatim; every UI/email
|
|
6
|
+
* surface calls this at its trust boundary.
|
|
7
|
+
*
|
|
8
|
+
* Unicode `Cf` includes bidi overrides/isolates, Arabic Letter Mark, soft
|
|
9
|
+
* hyphen, zero-width space, deprecated directional controls and tag
|
|
10
|
+
* characters. Those can reorder or invisibly split surrounding trusted copy.
|
|
11
|
+
* Replace them with a space rather than deleting them, so fragments cannot be
|
|
12
|
+
* joined into a new visible word. ZWNJ and ZWJ are the narrow exceptions:
|
|
13
|
+
* they are required for legitimate Persian/Indic text and emoji sequences and
|
|
14
|
+
* do not reorder the rest of the line. C0/C1 controls and lone surrogates are
|
|
15
|
+
* treated the same way.
|
|
16
|
+
*
|
|
17
|
+
* This deliberately does not apply NFKC or an ASCII allowlist. The label is
|
|
18
|
+
* explicitly unverified, and international product names should survive
|
|
19
|
+
* unchanged when they contain no display-control characters.
|
|
20
|
+
*/
|
|
21
|
+
export declare function normaliseOAuthClientName(raw: string | null | undefined): string | null;
|
|
22
|
+
//# sourceMappingURL=oauth-client-name.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-client-name.d.ts","sourceRoot":"","sources":["../src/oauth-client-name.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,eAAO,MAAM,yCAAyC,KAAK,CAAC;AAK5D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAetF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Maximum number of Unicode code points shown from an open-DCR client name. */
|
|
2
|
+
export const MAX_OAUTH_CLIENT_NAME_DISPLAY_CODE_POINTS = 80;
|
|
3
|
+
const ZERO_WIDTH_NON_JOINER = "\u200c";
|
|
4
|
+
const ZERO_WIDTH_JOINER = "\u200d";
|
|
5
|
+
/**
|
|
6
|
+
* Normalise an attacker-controlled OAuth Dynamic Client Registration name for
|
|
7
|
+
* display. The stored registration metadata remains verbatim; every UI/email
|
|
8
|
+
* surface calls this at its trust boundary.
|
|
9
|
+
*
|
|
10
|
+
* Unicode `Cf` includes bidi overrides/isolates, Arabic Letter Mark, soft
|
|
11
|
+
* hyphen, zero-width space, deprecated directional controls and tag
|
|
12
|
+
* characters. Those can reorder or invisibly split surrounding trusted copy.
|
|
13
|
+
* Replace them with a space rather than deleting them, so fragments cannot be
|
|
14
|
+
* joined into a new visible word. ZWNJ and ZWJ are the narrow exceptions:
|
|
15
|
+
* they are required for legitimate Persian/Indic text and emoji sequences and
|
|
16
|
+
* do not reorder the rest of the line. C0/C1 controls and lone surrogates are
|
|
17
|
+
* treated the same way.
|
|
18
|
+
*
|
|
19
|
+
* This deliberately does not apply NFKC or an ASCII allowlist. The label is
|
|
20
|
+
* explicitly unverified, and international product names should survive
|
|
21
|
+
* unchanged when they contain no display-control characters.
|
|
22
|
+
*/
|
|
23
|
+
export function normaliseOAuthClientName(raw) {
|
|
24
|
+
if (!raw)
|
|
25
|
+
return null;
|
|
26
|
+
const cleaned = raw
|
|
27
|
+
.replace(/[\p{Cc}\p{Cf}\p{Cs}]/gu, (character) => character === ZERO_WIDTH_NON_JOINER || character === ZERO_WIDTH_JOINER ? character : " ")
|
|
28
|
+
.replace(/\s+/gu, " ")
|
|
29
|
+
.trim();
|
|
30
|
+
if (cleaned.length === 0)
|
|
31
|
+
return null;
|
|
32
|
+
const codePoints = [...cleaned];
|
|
33
|
+
return codePoints.length > MAX_OAUTH_CLIENT_NAME_DISPLAY_CODE_POINTS
|
|
34
|
+
? `${codePoints.slice(0, MAX_OAUTH_CLIENT_NAME_DISPLAY_CODE_POINTS).join("")}...`
|
|
35
|
+
: cleaned;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=oauth-client-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-client-name.js","sourceRoot":"","sources":["../src/oauth-client-name.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,CAAC,MAAM,yCAAyC,GAAG,EAAE,CAAC;AAE5D,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AACvC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAEnC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAA8B;IACrE,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,OAAO,GAAG,GAAG;SAChB,OAAO,CAAC,wBAAwB,EAAE,CAAC,SAAS,EAAE,EAAE,CAC/C,SAAS,KAAK,qBAAqB,IAAI,SAAS,KAAK,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CACzF;SACA,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,IAAI,EAAE,CAAC;IACV,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;IAChC,OAAO,UAAU,CAAC,MAAM,GAAG,yCAAyC;QAClE,CAAC,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,yCAAyC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK;QACjF,CAAC,CAAC,OAAO,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hosted-MCP OAuth scope copy — the permission text a user reads on the
|
|
3
|
+
* consent screen, and again in the email telling them a client was connected.
|
|
4
|
+
*
|
|
5
|
+
* It lives here because those two surfaces must not drift: the email has to
|
|
6
|
+
* describe the access in the words the user actually consented to, and they
|
|
7
|
+
* are rendered by different processes (Next.js dashboard, API mailer). Two
|
|
8
|
+
* copies of security-relevant permission text is exactly the kind of drift
|
|
9
|
+
* that ends with an email under-describing what a grant can do.
|
|
10
|
+
*
|
|
11
|
+
* An unknown scope falls through to its raw token — nothing is ever silently
|
|
12
|
+
* hidden from the reader.
|
|
13
|
+
*/
|
|
14
|
+
export declare const OAUTH_SCOPE_LABELS: Readonly<Record<string, string>>;
|
|
15
|
+
/** Map a raw scope token to its human label, or the token itself if unknown. */
|
|
16
|
+
export declare function describeOAuthScope(scope: string): string;
|
|
17
|
+
/** Split a space-delimited OAuth scope string into individual tokens. */
|
|
18
|
+
export declare function parseOAuthScopes(scope: string): string[];
|
|
19
|
+
//# sourceMappingURL=oauth-scopes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-scopes.d.ts","sourceRoot":"","sources":["../src/oauth-scopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK/D,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAKxD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hosted-MCP OAuth scope copy — the permission text a user reads on the
|
|
3
|
+
* consent screen, and again in the email telling them a client was connected.
|
|
4
|
+
*
|
|
5
|
+
* It lives here because those two surfaces must not drift: the email has to
|
|
6
|
+
* describe the access in the words the user actually consented to, and they
|
|
7
|
+
* are rendered by different processes (Next.js dashboard, API mailer). Two
|
|
8
|
+
* copies of security-relevant permission text is exactly the kind of drift
|
|
9
|
+
* that ends with an email under-describing what a grant can do.
|
|
10
|
+
*
|
|
11
|
+
* An unknown scope falls through to its raw token — nothing is ever silently
|
|
12
|
+
* hidden from the reader.
|
|
13
|
+
*/
|
|
14
|
+
export const OAUTH_SCOPE_LABELS = {
|
|
15
|
+
"mcp:apps": "View and operate your apps (deploy, env, domains, logs)",
|
|
16
|
+
"mcp:apps.read": "View your apps and their status",
|
|
17
|
+
"mcp:account": "View your account",
|
|
18
|
+
mcp: "Use the hosted Percher MCP tools",
|
|
19
|
+
};
|
|
20
|
+
/** Map a raw scope token to its human label, or the token itself if unknown. */
|
|
21
|
+
export function describeOAuthScope(scope) {
|
|
22
|
+
return OAUTH_SCOPE_LABELS[scope] ?? scope;
|
|
23
|
+
}
|
|
24
|
+
/** Split a space-delimited OAuth scope string into individual tokens. */
|
|
25
|
+
export function parseOAuthScopes(scope) {
|
|
26
|
+
return scope
|
|
27
|
+
.split(/\s+/)
|
|
28
|
+
.map((s) => s.trim())
|
|
29
|
+
.filter((s) => s.length > 0);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=oauth-scopes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-scopes.js","sourceRoot":"","sources":["../src/oauth-scopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAqC;IAClE,UAAU,EAAE,yDAAyD;IACrE,eAAe,EAAE,iCAAiC;IAClD,aAAa,EAAE,mBAAmB;IAClC,GAAG,EAAE,kCAAkC;CACxC,CAAC;AAEF,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,kBAAkB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;AAC5C,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,KAAK;SACT,KAAK,CAAC,KAAK,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-env.d.ts","sourceRoot":"","sources":["../src/platform-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"platform-env.d.ts","sourceRoot":"","sources":["../src/platform-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,MAAM,CA2BxD,CAAC;AAcH;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAItD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGxD"}
|
package/dist/platform-env.js
CHANGED
|
@@ -32,6 +32,12 @@ export const PERCHER_INJECTED_ENV_KEYS = new Set([
|
|
|
32
32
|
// App identity
|
|
33
33
|
"PERCHER_APP_NAME",
|
|
34
34
|
"PERCHER_APP_URL",
|
|
35
|
+
// App id + the identity-assertion verification key, injected into gated
|
|
36
|
+
// (private/password) apps so they can verify `X-Percher-Assertion` (see
|
|
37
|
+
// packages/api/src/site-identity-assertion.ts). Platform-owned like the
|
|
38
|
+
// rest — a user must never set them.
|
|
39
|
+
"PERCHER_APP_ID",
|
|
40
|
+
"PERCHER_IDENTITY_SECRET",
|
|
35
41
|
// Egress proxy (Percher injects these so app code can reach the
|
|
36
42
|
// public internet through the forward proxy — see docs/CLAUDE.md
|
|
37
43
|
// "Outbound HTTPS")
|
package/dist/platform-env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-env.js","sourceRoot":"","sources":["../src/platform-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAwB,IAAI,GAAG,CAAS;IAC5E,yBAAyB;IACzB,UAAU;IACV,MAAM;IACN,sEAAsE;IACtE,kEAAkE;IAClE,8CAA8C;IAC9C,cAAc;IACd,gBAAgB;IAChB,wBAAwB;IACxB,2BAA2B;IAC3B,eAAe;IACf,kBAAkB;IAClB,iBAAiB;IACjB,gEAAgE;IAChE,iEAAiE;IACjE,oBAAoB;IACpB,YAAY;IACZ,aAAa;IACb,UAAU;IACV,oBAAoB;CACrB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,uBAAuB,GAAwB,IAAI,GAAG,CAAS;IACnE,YAAY;IACZ,aAAa;IACb,UAAU;CACX,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,IAAI,yBAAyB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,IAAI,uBAAuB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,IAAI,uBAAuB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;IAC/D,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
1
|
+
{"version":3,"file":"platform-env.js","sourceRoot":"","sources":["../src/platform-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAwB,IAAI,GAAG,CAAS;IAC5E,yBAAyB;IACzB,UAAU;IACV,MAAM;IACN,sEAAsE;IACtE,kEAAkE;IAClE,8CAA8C;IAC9C,cAAc;IACd,gBAAgB;IAChB,wBAAwB;IACxB,2BAA2B;IAC3B,eAAe;IACf,kBAAkB;IAClB,iBAAiB;IACjB,wEAAwE;IACxE,wEAAwE;IACxE,wEAAwE;IACxE,qCAAqC;IACrC,gBAAgB;IAChB,yBAAyB;IACzB,gEAAgE;IAChE,iEAAiE;IACjE,oBAAoB;IACpB,YAAY;IACZ,aAAa;IACb,UAAU;IACV,oBAAoB;CACrB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,uBAAuB,GAAwB,IAAI,GAAG,CAAS;IACnE,YAAY;IACZ,aAAa;IACb,UAAU;CACX,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,IAAI,yBAAyB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,IAAI,uBAAuB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,IAAI,uBAAuB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;IAC/D,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/platform.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* without depending on each other.
|
|
7
7
|
*/
|
|
8
8
|
/** Default PocketBase sidecar image when PERCHER_POCKETBASE_IMAGE is unset. */
|
|
9
|
-
export declare const DEFAULT_POCKETBASE_IMAGE = "percher/pocketbase:0.
|
|
9
|
+
export declare const DEFAULT_POCKETBASE_IMAGE = "percher/pocketbase:0.40.3-hardened.1";
|
|
10
10
|
/** Default app domain suffix when PERCHER_DOMAIN_SUFFIX is unset. */
|
|
11
11
|
export declare const DEFAULT_DOMAIN_SUFFIX = "percher.run";
|
|
12
12
|
/** Default hosted-MCP HTTP port when PERCHER_MCP_HTTP_PORT is unset. */
|
|
@@ -26,6 +26,6 @@ export declare const DEFAULT_MCP_HTTP_PORT = 8787;
|
|
|
26
26
|
* drifted to three different values (51/52/53), and one shipped artifact
|
|
27
27
|
* disagreed with itself on two lines.
|
|
28
28
|
*/
|
|
29
|
-
export declare const MCP_TOOL_COUNT_LOCAL =
|
|
30
|
-
export declare const MCP_TOOL_COUNT_HOSTED =
|
|
29
|
+
export declare const MCP_TOOL_COUNT_LOCAL = 54;
|
|
30
|
+
export declare const MCP_TOOL_COUNT_HOSTED = 23;
|
|
31
31
|
//# sourceMappingURL=platform.d.ts.map
|
package/dist/platform.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,yCAAyC,CAAC;AAE/E,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AAEnD,wEAAwE;AACxE,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,qBAAqB,KAAK,CAAC"}
|
package/dist/platform.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* without depending on each other.
|
|
7
7
|
*/
|
|
8
8
|
/** Default PocketBase sidecar image when PERCHER_POCKETBASE_IMAGE is unset. */
|
|
9
|
-
export const DEFAULT_POCKETBASE_IMAGE = "percher/pocketbase:0.
|
|
9
|
+
export const DEFAULT_POCKETBASE_IMAGE = "percher/pocketbase:0.40.3-hardened.1";
|
|
10
10
|
/** Default app domain suffix when PERCHER_DOMAIN_SUFFIX is unset. */
|
|
11
11
|
export const DEFAULT_DOMAIN_SUFFIX = "percher.run";
|
|
12
12
|
/** Default hosted-MCP HTTP port when PERCHER_MCP_HTTP_PORT is unset. */
|
|
@@ -26,6 +26,6 @@ export const DEFAULT_MCP_HTTP_PORT = 8787;
|
|
|
26
26
|
* drifted to three different values (51/52/53), and one shipped artifact
|
|
27
27
|
* disagreed with itself on two lines.
|
|
28
28
|
*/
|
|
29
|
-
export const MCP_TOOL_COUNT_LOCAL =
|
|
30
|
-
export const MCP_TOOL_COUNT_HOSTED =
|
|
29
|
+
export const MCP_TOOL_COUNT_LOCAL = 54;
|
|
30
|
+
export const MCP_TOOL_COUNT_HOSTED = 23;
|
|
31
31
|
//# sourceMappingURL=platform.js.map
|
package/dist/platform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,wBAAwB,GAAG,
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,wBAAwB,GAAG,sCAAsC,CAAC;AAE/E,qEAAqE;AACrE,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAEnD,wEAAwE;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AACvC,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sha256 hex of a normalized invite code — the value stored in
|
|
3
|
+
* `signup_invites.code_hash`. Server-only, on its own subpath like `./pow`:
|
|
4
|
+
* the signup form imports the header name and code shape from the package
|
|
5
|
+
* index, and `node:crypto` must not reach that client bundle.
|
|
6
|
+
*/
|
|
7
|
+
export declare function hashSignupInviteCode(code: string): string;
|
|
8
|
+
//# sourceMappingURL=signup-invite-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signup-invite-hash.d.ts","sourceRoot":"","sources":["../src/signup-invite-hash.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* sha256 hex of a normalized invite code — the value stored in
|
|
4
|
+
* `signup_invites.code_hash`. Server-only, on its own subpath like `./pow`:
|
|
5
|
+
* the signup form imports the header name and code shape from the package
|
|
6
|
+
* index, and `node:crypto` must not reach that client bundle.
|
|
7
|
+
*/
|
|
8
|
+
export function hashSignupInviteCode(code) {
|
|
9
|
+
return createHash("sha256").update(code, "utf8").digest("hex");
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=signup-invite-hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signup-invite-hash.js","sourceRoot":"","sources":["../src/signup-invite-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beta signup invites. Two processes act on one code and must not drift: the
|
|
3
|
+
* API mints and stores the hash, and the dashboard validates what the form
|
|
4
|
+
* sent. The API consumes the use atomically with account provisioning.
|
|
5
|
+
*
|
|
6
|
+
* The plaintext code is shown to the operator exactly once at mint time and
|
|
7
|
+
* never stored, so the hash is the only thing either side can compare. The
|
|
8
|
+
* hash itself lives in `./signup-invite-hash` (server-only, needs
|
|
9
|
+
* `node:crypto`); this module is safe for the signup form's client bundle.
|
|
10
|
+
*/
|
|
11
|
+
/** Request header the signup form uses to carry the code through BetterAuth. */
|
|
12
|
+
export declare const SIGNUP_INVITE_HEADER = "x-percher-invite-code";
|
|
13
|
+
export declare const SIGNUP_INVITE_CODE_PREFIX = "beta-";
|
|
14
|
+
/** Legacy code shape. Previously issued codes retain their exact hash. */
|
|
15
|
+
export declare const SIGNUP_INVITE_CODE_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
16
|
+
export declare const SIGNUP_INVITE_CODE_LENGTH = 24;
|
|
17
|
+
/**
|
|
18
|
+
* Canonical form of a code as a user might have typed it: whitespace trimmed,
|
|
19
|
+
* case folded. Returns null for anything that is not shaped like a code, so
|
|
20
|
+
* callers never hash (or log) arbitrary input.
|
|
21
|
+
*/
|
|
22
|
+
export declare function normalizeSignupInviteCode(raw: string | null | undefined): string | null;
|
|
23
|
+
//# sourceMappingURL=signup-invite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signup-invite.d.ts","sourceRoot":"","sources":["../src/signup-invite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAE5D,eAAO,MAAM,yBAAyB,UAAU,CAAC;AAEjD,0EAA0E;AAC1E,eAAO,MAAM,2BAA2B,yCAAyC,CAAC;AAClF,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAK5C;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAOvF"}
|