@privos_ai/app-server 0.8.3 → 0.9.0
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 +39 -1
- package/dist/cli/commands/lint.d.ts +8 -0
- package/dist/cli/commands/lint.d.ts.map +1 -0
- package/dist/cli/commands/lint.js +24 -0
- package/dist/cli/commands/lint.js.map +1 -0
- package/dist/cli/commands/publish.d.ts +20 -0
- package/dist/cli/commands/publish.d.ts.map +1 -0
- package/dist/cli/commands/publish.js +365 -0
- package/dist/cli/commands/publish.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +36 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/lib/authorize.d.ts +80 -0
- package/dist/cli/lib/authorize.d.ts.map +1 -0
- package/dist/cli/lib/authorize.js +86 -0
- package/dist/cli/lib/authorize.js.map +1 -0
- package/dist/cli/lib/manifest.d.ts +30 -0
- package/dist/cli/lib/manifest.d.ts.map +1 -0
- package/dist/cli/lib/manifest.js +61 -0
- package/dist/cli/lib/manifest.js.map +1 -0
- package/dist/cli/lib/output.d.ts +66 -0
- package/dist/cli/lib/output.d.ts.map +1 -0
- package/dist/cli/lib/output.js +61 -0
- package/dist/cli/lib/output.js.map +1 -0
- package/dist/cli/lib/package-source.d.ts +63 -0
- package/dist/cli/lib/package-source.d.ts.map +1 -0
- package/dist/cli/lib/package-source.js +219 -0
- package/dist/cli/lib/package-source.js.map +1 -0
- package/dist/cli/lib/portal-client.d.ts +38 -0
- package/dist/cli/lib/portal-client.d.ts.map +1 -0
- package/dist/cli/lib/portal-client.js +87 -0
- package/dist/cli/lib/portal-client.js.map +1 -0
- package/dist/cli/lib/upload.d.ts +54 -0
- package/dist/cli/lib/upload.d.ts.map +1 -0
- package/dist/cli/lib/upload.js +75 -0
- package/dist/cli/lib/upload.js.map +1 -0
- package/dist/manifest-lint-cli.js +5 -16
- package/dist/manifest-lint-cli.js.map +1 -1
- package/dist/relay/relay-client.d.ts +9 -0
- package/dist/relay/relay-client.d.ts.map +1 -1
- package/dist/relay/relay-client.js +64 -0
- package/dist/relay/relay-client.js.map +1 -1
- package/package.json +3 -1
- package/skill/README.md +14 -0
- package/skill/SKILL.md +124 -0
- package/skill/references/errors.md +68 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin fetch wrapper for the `/api/cloud/marketplace/*` publish routes: JSON
|
|
3
|
+
* request/response, retry-after-driven backoff on transient 429/5xx, and a
|
|
4
|
+
* typed {@link PortalError} carrying the Portal's `code` for callers that
|
|
5
|
+
* branch on specific wire-contract error codes (`VERSION_SEMVER_EXISTS`,
|
|
6
|
+
* `PUBLISH_GRANT_EXPIRED`, `PUBLISH_GRANT_MISMATCH`, `LISTING_NOT_BOUND`,
|
|
7
|
+
* `LISTING_UNRESOLVED`, RFC 8628 `slow_down`, …).
|
|
8
|
+
*/
|
|
9
|
+
export declare class PortalError extends Error {
|
|
10
|
+
readonly status: number;
|
|
11
|
+
readonly code?: string | undefined;
|
|
12
|
+
readonly retryAfterMs?: number | undefined;
|
|
13
|
+
constructor(message: string, status: number, code?: string | undefined, retryAfterMs?: number | undefined);
|
|
14
|
+
}
|
|
15
|
+
export type PortalRequestInit = Readonly<{
|
|
16
|
+
method?: string;
|
|
17
|
+
/** Bearer token: publisher token (`pvp_…`), publish grant (`pvg_…`), or none (anonymous create). */
|
|
18
|
+
token?: string;
|
|
19
|
+
body?: unknown;
|
|
20
|
+
}>;
|
|
21
|
+
export type PortalClientOptions = Readonly<{
|
|
22
|
+
origin: string;
|
|
23
|
+
basePath?: string;
|
|
24
|
+
fetchImpl?: typeof fetch;
|
|
25
|
+
maxRetries?: number;
|
|
26
|
+
sleepImpl?: (ms: number) => Promise<void>;
|
|
27
|
+
}>;
|
|
28
|
+
export declare function parseRetryAfterMs(header: string | null): number | undefined;
|
|
29
|
+
export declare class PortalClient {
|
|
30
|
+
private readonly origin;
|
|
31
|
+
private readonly basePath;
|
|
32
|
+
private readonly fetchImpl;
|
|
33
|
+
private readonly maxRetries;
|
|
34
|
+
private readonly sleepImpl;
|
|
35
|
+
constructor(options: PortalClientOptions);
|
|
36
|
+
request<T>(path: string, init?: PortalRequestInit): Promise<T>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=portal-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portal-client.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/portal-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,qBAAa,WAAY,SAAQ,KAAK;aAGpB,MAAM,EAAE,MAAM;aACd,IAAI,CAAC,EAAE,MAAM;aACb,YAAY,CAAC,EAAE,MAAM;gBAHrC,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,YAAA,EACb,YAAY,CAAC,EAAE,MAAM,YAAA;CAKtC;AAED,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oGAAoG;IACpG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CACf,CAAC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC,CAAC;AASH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAM3E;AAED,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;gBAE9C,OAAO,EAAE,mBAAmB;IAQlC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,CAAC,CAAC;CAoCxE"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin fetch wrapper for the `/api/cloud/marketplace/*` publish routes: JSON
|
|
3
|
+
* request/response, retry-after-driven backoff on transient 429/5xx, and a
|
|
4
|
+
* typed {@link PortalError} carrying the Portal's `code` for callers that
|
|
5
|
+
* branch on specific wire-contract error codes (`VERSION_SEMVER_EXISTS`,
|
|
6
|
+
* `PUBLISH_GRANT_EXPIRED`, `PUBLISH_GRANT_MISMATCH`, `LISTING_NOT_BOUND`,
|
|
7
|
+
* `LISTING_UNRESOLVED`, RFC 8628 `slow_down`, …).
|
|
8
|
+
*/
|
|
9
|
+
export class PortalError extends Error {
|
|
10
|
+
status;
|
|
11
|
+
code;
|
|
12
|
+
retryAfterMs;
|
|
13
|
+
constructor(message, status, code, retryAfterMs) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.status = status;
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.retryAfterMs = retryAfterMs;
|
|
18
|
+
this.name = 'PortalError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const DEFAULT_BASE_PATH = '/api/cloud/marketplace';
|
|
22
|
+
const DEFAULT_MAX_RETRIES = 5;
|
|
23
|
+
function defaultSleep(ms) {
|
|
24
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
25
|
+
}
|
|
26
|
+
export function parseRetryAfterMs(header) {
|
|
27
|
+
if (!header)
|
|
28
|
+
return undefined;
|
|
29
|
+
if (/^\d+$/.test(header))
|
|
30
|
+
return Number(header) * 1000;
|
|
31
|
+
const parsed = Date.parse(header);
|
|
32
|
+
if (Number.isNaN(parsed))
|
|
33
|
+
return undefined;
|
|
34
|
+
return Math.max(0, parsed - Date.now());
|
|
35
|
+
}
|
|
36
|
+
export class PortalClient {
|
|
37
|
+
origin;
|
|
38
|
+
basePath;
|
|
39
|
+
fetchImpl;
|
|
40
|
+
maxRetries;
|
|
41
|
+
sleepImpl;
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.origin = options.origin.replace(/\/+$/, '');
|
|
44
|
+
this.basePath = options.basePath ?? DEFAULT_BASE_PATH;
|
|
45
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
46
|
+
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
47
|
+
this.sleepImpl = options.sleepImpl ?? defaultSleep;
|
|
48
|
+
}
|
|
49
|
+
async request(path, init = {}) {
|
|
50
|
+
const method = init.method ?? 'GET';
|
|
51
|
+
const url = `${this.origin}${this.basePath}${path}`;
|
|
52
|
+
let attempt = 0;
|
|
53
|
+
for (;;) {
|
|
54
|
+
const response = await this.fetchImpl(url, {
|
|
55
|
+
method,
|
|
56
|
+
headers: {
|
|
57
|
+
...(init.token ? { Authorization: `Bearer ${init.token}` } : {}),
|
|
58
|
+
...(init.body === undefined ? {} : { 'Content-Type': 'application/json' }),
|
|
59
|
+
},
|
|
60
|
+
body: init.body === undefined ? undefined : JSON.stringify(init.body),
|
|
61
|
+
});
|
|
62
|
+
const text = await response.text();
|
|
63
|
+
let parsed;
|
|
64
|
+
try {
|
|
65
|
+
parsed = text ? JSON.parse(text) : {};
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
parsed = { raw: text.slice(0, 500) };
|
|
69
|
+
}
|
|
70
|
+
if (response.ok)
|
|
71
|
+
return parsed;
|
|
72
|
+
const record = parsed;
|
|
73
|
+
const code = typeof record?.code === 'string' ? record.code : undefined;
|
|
74
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get('retry-after'));
|
|
75
|
+
const transient = response.status === 429 || (response.status >= 500 && response.status < 600);
|
|
76
|
+
if (transient && attempt < this.maxRetries) {
|
|
77
|
+
const backoffMs = retryAfterMs ?? Math.min(30_000, 1_000 * 2 ** attempt);
|
|
78
|
+
await this.sleepImpl(backoffMs);
|
|
79
|
+
attempt += 1;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const message = typeof record?.message === 'string' ? record.message : `${method} ${path} failed (${response.status})`;
|
|
83
|
+
throw new PortalError(message, response.status, code, retryAfterMs);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=portal-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portal-client.js","sourceRoot":"","sources":["../../../src/cli/lib/portal-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { PortalClient } from './portal-client.js';
|
|
2
|
+
/** Portal's `express.json()` body limit is 10 MB; base64 inflates bytes ~1.37x, so 5 MB parts stay well under it. */
|
|
3
|
+
export declare const DEFAULT_PART_SIZE_BYTES: number;
|
|
4
|
+
export type UploadSession = Readonly<{
|
|
5
|
+
uploadId: string;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function createUploadSession(client: PortalClient, token: string, listingId: string, request: Readonly<{
|
|
8
|
+
fileName: string;
|
|
9
|
+
totalBytes: number;
|
|
10
|
+
paths: readonly string[];
|
|
11
|
+
}>): Promise<UploadSession>;
|
|
12
|
+
/**
|
|
13
|
+
* Uploads `buffer` as base64 parts, one at a time (sequential — never
|
|
14
|
+
* concurrent), retrying transient 5xx/429 per-part via {@link PortalClient}'s
|
|
15
|
+
* own retry-after backoff.
|
|
16
|
+
*/
|
|
17
|
+
export declare function uploadPartsSequentially(client: PortalClient, token: string, uploadId: string, buffer: Buffer, partSizeBytes?: number, onProgress?: (part: number, totalParts: number) => void): Promise<number>;
|
|
18
|
+
export type CompleteUploadResult = Readonly<{
|
|
19
|
+
sha256: string;
|
|
20
|
+
uploadId: string;
|
|
21
|
+
manifestDigest: string;
|
|
22
|
+
manifest: Record<string, unknown>;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function completeUpload(client: PortalClient, token: string, uploadId: string): Promise<CompleteUploadResult>;
|
|
25
|
+
export type CreatedVersion = Readonly<{
|
|
26
|
+
id: string;
|
|
27
|
+
semver: string;
|
|
28
|
+
state: string;
|
|
29
|
+
}>;
|
|
30
|
+
export declare function createVersion(client: PortalClient, token: string, listingId: string, request: Readonly<{
|
|
31
|
+
semver: string;
|
|
32
|
+
uploadId: string;
|
|
33
|
+
changelog: string;
|
|
34
|
+
}>): Promise<CreatedVersion>;
|
|
35
|
+
export declare function submitVersion(client: PortalClient, token: string, listingId: string, versionId: string): Promise<void>;
|
|
36
|
+
export type ListingVersion = Readonly<{
|
|
37
|
+
id: string;
|
|
38
|
+
semver: string;
|
|
39
|
+
state: string;
|
|
40
|
+
}> & Record<string, unknown>;
|
|
41
|
+
export type ListingDetail = Readonly<{
|
|
42
|
+
listing: Record<string, unknown>;
|
|
43
|
+
versions: readonly ListingVersion[];
|
|
44
|
+
events?: readonly Record<string, unknown>[];
|
|
45
|
+
}>;
|
|
46
|
+
export declare function getListingDetail(client: PortalClient, token: string, listingId: string): Promise<ListingDetail>;
|
|
47
|
+
export type WaitForPreflightOptions = Readonly<{
|
|
48
|
+
timeoutMs?: number;
|
|
49
|
+
pollMs?: number;
|
|
50
|
+
sleepImpl?: (ms: number) => Promise<void>;
|
|
51
|
+
}>;
|
|
52
|
+
/** Polls listing detail up to `timeoutMs` (default 60 s) while the version sits in `PREFLIGHT_PENDING`. */
|
|
53
|
+
export declare function waitForPreflight(client: PortalClient, token: string, listingId: string, versionId: string, options?: WaitForPreflightOptions): Promise<ListingVersion | undefined>;
|
|
54
|
+
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/upload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** Portal's `express.json()` body limit is 10 MB; base64 inflates bytes ~1.37x, so 5 MB parts stay well under it. */
|
|
2
|
+
export const DEFAULT_PART_SIZE_BYTES = 5 * 1024 * 1024;
|
|
3
|
+
export async function createUploadSession(client, token, listingId, request) {
|
|
4
|
+
const response = await client.request(`/creator/listings/${listingId}/uploads`, {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
token,
|
|
7
|
+
body: {
|
|
8
|
+
fileName: request.fileName,
|
|
9
|
+
totalBytes: request.totalBytes,
|
|
10
|
+
kind: 'APP_SOURCE',
|
|
11
|
+
paths: request.paths,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
const upload = response.upload;
|
|
15
|
+
const uploadId = upload?.id;
|
|
16
|
+
if (typeof uploadId !== 'string' || !uploadId)
|
|
17
|
+
throw new Error('Portal did not return an upload session id.');
|
|
18
|
+
return { uploadId };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Uploads `buffer` as base64 parts, one at a time (sequential — never
|
|
22
|
+
* concurrent), retrying transient 5xx/429 per-part via {@link PortalClient}'s
|
|
23
|
+
* own retry-after backoff.
|
|
24
|
+
*/
|
|
25
|
+
export async function uploadPartsSequentially(client, token, uploadId, buffer, partSizeBytes = DEFAULT_PART_SIZE_BYTES, onProgress) {
|
|
26
|
+
const totalParts = Math.max(1, Math.ceil(buffer.length / partSizeBytes));
|
|
27
|
+
for (let index = 0; index < totalParts; index += 1) {
|
|
28
|
+
const start = index * partSizeBytes;
|
|
29
|
+
const end = Math.min(buffer.length, start + partSizeBytes);
|
|
30
|
+
const chunk = buffer.subarray(start, end);
|
|
31
|
+
await client.request(`/creator/uploads/${uploadId}/parts/${index + 1}`, {
|
|
32
|
+
method: 'PUT',
|
|
33
|
+
token,
|
|
34
|
+
body: { dataBase64: chunk.toString('base64') },
|
|
35
|
+
});
|
|
36
|
+
onProgress?.(index + 1, totalParts);
|
|
37
|
+
}
|
|
38
|
+
return totalParts;
|
|
39
|
+
}
|
|
40
|
+
export async function completeUpload(client, token, uploadId) {
|
|
41
|
+
const response = await client.request(`/creator/uploads/${uploadId}/complete`, { method: 'POST', token, body: {} });
|
|
42
|
+
return {
|
|
43
|
+
sha256: String(response.sha256),
|
|
44
|
+
uploadId: String(response.uploadId ?? uploadId),
|
|
45
|
+
manifestDigest: String(response.manifestDigest),
|
|
46
|
+
manifest: response.manifest ?? {},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export async function createVersion(client, token, listingId, request) {
|
|
50
|
+
const response = await client.request(`/creator/listings/${listingId}/versions`, { method: 'POST', token, body: request });
|
|
51
|
+
const version = response.version;
|
|
52
|
+
return { id: String(version.id), semver: String(version.semver), state: String(version.state) };
|
|
53
|
+
}
|
|
54
|
+
export async function submitVersion(client, token, listingId, versionId) {
|
|
55
|
+
await client.request(`/creator/listings/${listingId}/submit`, { method: 'POST', token, body: { versionId } });
|
|
56
|
+
}
|
|
57
|
+
export async function getListingDetail(client, token, listingId) {
|
|
58
|
+
return client.request(`/creator/listings/${listingId}`, { method: 'GET', token });
|
|
59
|
+
}
|
|
60
|
+
/** Polls listing detail up to `timeoutMs` (default 60 s) while the version sits in `PREFLIGHT_PENDING`. */
|
|
61
|
+
export async function waitForPreflight(client, token, listingId, versionId, options = {}) {
|
|
62
|
+
const timeoutMs = options.timeoutMs ?? 60_000;
|
|
63
|
+
const pollMs = options.pollMs ?? 1_000;
|
|
64
|
+
const sleepImpl = options.sleepImpl ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
65
|
+
const deadline = Date.now() + timeoutMs;
|
|
66
|
+
let detail = await getListingDetail(client, token, listingId);
|
|
67
|
+
let version = detail.versions.find((item) => item.id === versionId);
|
|
68
|
+
while (version?.state === 'PREFLIGHT_PENDING' && Date.now() < deadline) {
|
|
69
|
+
await sleepImpl(pollMs);
|
|
70
|
+
detail = await getListingDetail(client, token, listingId);
|
|
71
|
+
version = detail.versions.find((item) => item.id === versionId);
|
|
72
|
+
}
|
|
73
|
+
return version;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../../src/cli/lib/upload.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
try {
|
|
8
|
-
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
console.error(JSON.stringify({ valid: false, errors: [`Unable to read valid JSON from ${manifestPath}`] }, null, 2));
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
const result = lintManifest(manifest);
|
|
15
|
-
console.log(JSON.stringify({ manifestPath, ...result }, null, 2));
|
|
16
|
-
if (!result.valid)
|
|
17
|
-
process.exit(1);
|
|
2
|
+
// Compatibility alias for `privos-app lint`: kept as its own bin entry
|
|
3
|
+
// (`privos-app-lint`) with byte-identical output and exit code, delegating
|
|
4
|
+
// to the same implementation so the two can never drift.
|
|
5
|
+
import { runLint } from './cli/commands/lint.js';
|
|
6
|
+
process.exit(runLint(process.argv.slice(2)));
|
|
18
7
|
//# sourceMappingURL=manifest-lint-cli.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-lint-cli.js","sourceRoot":"","sources":["../src/manifest-lint-cli.ts"],"names":[],"mappings":";AACA,
|
|
1
|
+
{"version":3,"file":"manifest-lint-cli.js","sourceRoot":"","sources":["../src/manifest-lint-cli.ts"],"names":[],"mappings":";AACA,uEAAuE;AACvE,2EAA2E;AAC3E,yDAAyD;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -129,6 +129,15 @@ export interface RelayClientOptions {
|
|
|
129
129
|
oauthTimeoutMs?: number;
|
|
130
130
|
/** Timeout waiting for WebSocket `open` after create (default 15s). */
|
|
131
131
|
openHandshakeTimeoutMs?: number;
|
|
132
|
+
/**
|
|
133
|
+
* Interval for client-initiated keepalive pings (default 30s). Each tick pings
|
|
134
|
+
* the Hub and, if the previous ping got no pong before the next tick, terminates
|
|
135
|
+
* the socket so the reconnect path restores it — this is what lets a
|
|
136
|
+
* long-running app self-heal a silently dead (half-open) connection without an
|
|
137
|
+
* external healthcheck. Set to `0` to disable (the app then relies solely on
|
|
138
|
+
* server-driven pings and OS timeouts, matching pre-keepalive behavior).
|
|
139
|
+
*/
|
|
140
|
+
keepAliveIntervalMs?: number;
|
|
132
141
|
/** Injected for tests. */
|
|
133
142
|
fetchImpl?: typeof fetch;
|
|
134
143
|
WebSocketImpl?: typeof WebSocket;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-client.d.ts","sourceRoot":"","sources":["../../src/relay/relay-client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"relay-client.d.ts","sourceRoot":"","sources":["../../src/relay/relay-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,14 @@ const BACKOFF_MS = [1_000, 2_000, 5_000, 10_000, 30_000];
|
|
|
12
12
|
const DEFAULT_PAIRING_TIMEOUT_MS = 30_000;
|
|
13
13
|
const DEFAULT_OAUTH_TIMEOUT_MS = 15_000;
|
|
14
14
|
const DEFAULT_OPEN_HANDSHAKE_TIMEOUT_MS = 15_000;
|
|
15
|
+
// Client-initiated keepalive. A live socket whose peer has silently gone away
|
|
16
|
+
// (half-open: no FIN/RST ever arrives — laptop sleep, NAT/idle drop, a Hub that
|
|
17
|
+
// vanished) keeps `readyState === OPEN` forever, so the process stays up while
|
|
18
|
+
// dispatch is dead and nothing triggers the reconnect path. A periodic ping that
|
|
19
|
+
// expects a pong within one interval detects that, terminates the dead socket,
|
|
20
|
+
// and lets the existing `close` → backoff reconnect restore service in-process —
|
|
21
|
+
// no external healthcheck or process restart required.
|
|
22
|
+
const DEFAULT_KEEPALIVE_INTERVAL_MS = 30_000;
|
|
15
23
|
function sortedUniqueStrings(value, field) {
|
|
16
24
|
if (!Array.isArray(value) || value.some((item) => typeof item !== 'string' || !item)) {
|
|
17
25
|
throw new Error(`Pairing response ${field} is invalid.`);
|
|
@@ -608,12 +616,14 @@ export function connectRelay(opts) {
|
|
|
608
616
|
DEFAULT_MAX_MESSAGE_BYTES;
|
|
609
617
|
const oauthTimeoutMs = opts.oauthTimeoutMs ?? DEFAULT_OAUTH_TIMEOUT_MS;
|
|
610
618
|
const openHandshakeTimeoutMs = opts.openHandshakeTimeoutMs ?? DEFAULT_OPEN_HANDSHAKE_TIMEOUT_MS;
|
|
619
|
+
const keepAliveIntervalMs = opts.keepAliveIntervalMs ?? DEFAULT_KEEPALIVE_INTERVAL_MS;
|
|
611
620
|
const maxBufferedBytes = runtime.getLimits().maxBufferedBytes;
|
|
612
621
|
let stopped = false;
|
|
613
622
|
let activeWs = null;
|
|
614
623
|
let wsAuthenticated = false;
|
|
615
624
|
let reconnectTimer = null;
|
|
616
625
|
let openHandshakeTimer = null;
|
|
626
|
+
let keepAliveTimer = null;
|
|
617
627
|
let backoffIndex = 0;
|
|
618
628
|
let connectGeneration = 0;
|
|
619
629
|
let oauthAbort = null;
|
|
@@ -642,6 +652,12 @@ export function connectRelay(opts) {
|
|
|
642
652
|
openHandshakeTimer = null;
|
|
643
653
|
}
|
|
644
654
|
};
|
|
655
|
+
const clearKeepAliveTimer = () => {
|
|
656
|
+
if (keepAliveTimer) {
|
|
657
|
+
clearInterval(keepAliveTimer);
|
|
658
|
+
keepAliveTimer = null;
|
|
659
|
+
}
|
|
660
|
+
};
|
|
645
661
|
const scheduleReconnect = () => {
|
|
646
662
|
if (stopped)
|
|
647
663
|
return;
|
|
@@ -720,6 +736,11 @@ export function connectRelay(opts) {
|
|
|
720
736
|
}
|
|
721
737
|
// close handler schedules reconnect when generation still current
|
|
722
738
|
}, openHandshakeTimeoutMs);
|
|
739
|
+
// Liveness for this socket's keepalive: reset to true whenever the Hub
|
|
740
|
+
// answers our ping (or pings us), false when we send a ping and are
|
|
741
|
+
// still waiting. A tick that finds it already false means a full
|
|
742
|
+
// interval passed with no pong — the peer is gone.
|
|
743
|
+
let isAlive = true;
|
|
723
744
|
ws.on('open', () => {
|
|
724
745
|
clearOpenHandshakeTimer();
|
|
725
746
|
if (stopped || generation !== connectGeneration) {
|
|
@@ -738,6 +759,43 @@ export function connectRelay(opts) {
|
|
|
738
759
|
connectedOnce = true;
|
|
739
760
|
connectedResolve?.();
|
|
740
761
|
}
|
|
762
|
+
// Start client-initiated keepalive for this connection. Only one
|
|
763
|
+
// socket is ever active, so clear any prior interval first.
|
|
764
|
+
clearKeepAliveTimer();
|
|
765
|
+
if (keepAliveIntervalMs > 0) {
|
|
766
|
+
isAlive = true;
|
|
767
|
+
keepAliveTimer = setInterval(() => {
|
|
768
|
+
if (stopped || generation !== connectGeneration) {
|
|
769
|
+
clearKeepAliveTimer();
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
if (!isAlive) {
|
|
773
|
+
// No pong within a full interval: the socket is half-open.
|
|
774
|
+
// Terminate to force a `close` event, which runs the
|
|
775
|
+
// existing backoff reconnect — self-healing in-process.
|
|
776
|
+
log('relay.keepalive_timeout', { generation });
|
|
777
|
+
try {
|
|
778
|
+
if (typeof ws.terminate === 'function')
|
|
779
|
+
ws.terminate();
|
|
780
|
+
else
|
|
781
|
+
ws.close();
|
|
782
|
+
}
|
|
783
|
+
catch {
|
|
784
|
+
/* ignore */
|
|
785
|
+
}
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
isAlive = false;
|
|
789
|
+
try {
|
|
790
|
+
ws.ping();
|
|
791
|
+
}
|
|
792
|
+
catch {
|
|
793
|
+
/* ignore */
|
|
794
|
+
}
|
|
795
|
+
}, keepAliveIntervalMs);
|
|
796
|
+
// Don't let the keepalive interval alone keep the process alive.
|
|
797
|
+
keepAliveTimer.unref?.();
|
|
798
|
+
}
|
|
741
799
|
});
|
|
742
800
|
ws.on('message', (raw) => {
|
|
743
801
|
void handleMessage(ws, generation, raw).catch((err) => {
|
|
@@ -749,6 +807,7 @@ export function connectRelay(opts) {
|
|
|
749
807
|
});
|
|
750
808
|
});
|
|
751
809
|
ws.on('ping', () => {
|
|
810
|
+
isAlive = true;
|
|
752
811
|
try {
|
|
753
812
|
ws.pong();
|
|
754
813
|
}
|
|
@@ -756,8 +815,12 @@ export function connectRelay(opts) {
|
|
|
756
815
|
/* ignore */
|
|
757
816
|
}
|
|
758
817
|
});
|
|
818
|
+
ws.on('pong', () => {
|
|
819
|
+
isAlive = true;
|
|
820
|
+
});
|
|
759
821
|
ws.on('close', (code) => {
|
|
760
822
|
clearOpenHandshakeTimer();
|
|
823
|
+
clearKeepAliveTimer();
|
|
761
824
|
log('relay.disconnected', { code, generation });
|
|
762
825
|
if (activeWs === ws) {
|
|
763
826
|
activeWs = null;
|
|
@@ -1000,6 +1063,7 @@ export function connectRelay(opts) {
|
|
|
1000
1063
|
wsAuthenticated = false;
|
|
1001
1064
|
clearReconnectTimer();
|
|
1002
1065
|
clearOpenHandshakeTimer();
|
|
1066
|
+
clearKeepAliveTimer();
|
|
1003
1067
|
connectGeneration += 1;
|
|
1004
1068
|
oauthAbort?.abort();
|
|
1005
1069
|
oauthAbort = null;
|