@privos_ai/app-server 0.8.4 → 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/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,86 @@
|
|
|
1
|
+
import { PortalError } from './portal-client.js';
|
|
2
|
+
function normalizeGrant(raw) {
|
|
3
|
+
return {
|
|
4
|
+
value: String(raw.token),
|
|
5
|
+
expiresAt: String(raw.expiresAt),
|
|
6
|
+
listingId: String(raw.listingId),
|
|
7
|
+
listingSlug: String(raw.listingSlug),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* `POST /publish-authorizations` — anonymous (interactive/device flow) or
|
|
12
|
+
* bearer `pvp_…` (publisher token, CI). A token whose scope covers the
|
|
13
|
+
* resolved listing and whose `manifestName` is already bound returns
|
|
14
|
+
* `{ grant, listing }` inline (`kind: 'grant'`); otherwise the Portal issues
|
|
15
|
+
* a device code (`kind: 'device'`). `409 LISTING_NOT_BOUND` / `409
|
|
16
|
+
* LISTING_UNRESOLVED` surface as {@link PortalError} for the caller to map
|
|
17
|
+
* to the documented exit codes and remediation text.
|
|
18
|
+
*/
|
|
19
|
+
export async function createAuthorization(client, request, token) {
|
|
20
|
+
const body = {
|
|
21
|
+
manifestName: request.manifestName,
|
|
22
|
+
semver: request.semver,
|
|
23
|
+
archiveSha256: request.archiveSha256,
|
|
24
|
+
archiveBytes: request.archiveBytes,
|
|
25
|
+
cliVersion: request.cliVersion,
|
|
26
|
+
...(request.listingSlug ? { listingSlug: request.listingSlug } : {}),
|
|
27
|
+
...(request.machineLabel ? { machineLabel: request.machineLabel } : {}),
|
|
28
|
+
};
|
|
29
|
+
const response = await client.request('/publish-authorizations', { method: 'POST', token, body });
|
|
30
|
+
if (response.grant) {
|
|
31
|
+
return {
|
|
32
|
+
kind: 'grant',
|
|
33
|
+
grant: normalizeGrant(response.grant),
|
|
34
|
+
listing: response.listing,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
kind: 'device',
|
|
39
|
+
deviceCode: String(response.deviceCode),
|
|
40
|
+
userCode: String(response.userCode),
|
|
41
|
+
verificationUrl: String(response.verificationUrl),
|
|
42
|
+
expiresIn: Number(response.expiresIn),
|
|
43
|
+
interval: Number(response.interval),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** `POST /publish-authorizations/token` — RFC 8628 polling; a `slow_down` error surfaces as a `PortalError` with that code. */
|
|
47
|
+
export async function pollAuthorization(client, deviceCode) {
|
|
48
|
+
const response = await client.request('/publish-authorizations/token', { method: 'POST', body: { deviceCode } });
|
|
49
|
+
const state = String(response.state);
|
|
50
|
+
if (state === 'APPROVED') {
|
|
51
|
+
return { state: 'APPROVED', grant: normalizeGrant(response.grant) };
|
|
52
|
+
}
|
|
53
|
+
return { state: state };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Polls until a terminal state, honoring `slow_down` by backing off (RFC
|
|
57
|
+
* 8628 §3.5) and stopping once the authorization's own `expiresIn` window
|
|
58
|
+
* elapses. While `APPROVED`, the Portal returns the same grant on every
|
|
59
|
+
* poll, so a lost response mid-poll is recoverable by design — this loop
|
|
60
|
+
* simply returns on the first non-`PENDING` state it observes.
|
|
61
|
+
*/
|
|
62
|
+
export async function waitForApproval(client, deviceCode, options) {
|
|
63
|
+
const sleepImpl = options.sleepImpl ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
64
|
+
let intervalMs = options.intervalMs;
|
|
65
|
+
const start = Date.now();
|
|
66
|
+
const deadline = start + options.expiresInMs;
|
|
67
|
+
for (;;) {
|
|
68
|
+
if (Date.now() >= deadline)
|
|
69
|
+
return { state: 'EXPIRED' };
|
|
70
|
+
await sleepImpl(intervalMs);
|
|
71
|
+
try {
|
|
72
|
+
const outcome = await pollAuthorization(client, deviceCode);
|
|
73
|
+
if (outcome.state !== 'PENDING')
|
|
74
|
+
return outcome;
|
|
75
|
+
options.onPending?.(Date.now() - start);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (error instanceof PortalError && error.code?.toLowerCase() === 'slow_down') {
|
|
79
|
+
intervalMs += 5_000;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=authorize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../../src/cli/lib/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAsC/D,SAAS,cAAc,CAAC,GAA4B;IACnD,OAAO;QACN,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;QAChC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;QAChC,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC;KACpC,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAoB,EAAE,OAA6B,EAAE,KAAc;IAC5G,MAAM,IAAI,GAA4B;QACrC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvE,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAA0B,yBAAyB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3H,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QACpB,OAAO;YACN,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,KAAgC,CAAC;YAChE,OAAO,EAAE,QAAQ,CAAC,OAAsD;SACxE,CAAC;IACH,CAAC;IACD,OAAO;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QACvC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACnC,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;QACjD,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QACrC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;KACnC,CAAC;AACH,CAAC;AAED,+HAA+H;AAC/H,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAoB,EAAE,UAAkB;IAC/E,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAA0B,+BAA+B,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1I,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;QAC1B,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,KAAgC,CAAC,EAAE,CAAC;IAChG,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAsD,EAAE,CAAC;AAC1E,CAAC;AASD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAoB,EAAE,UAAkB,EAAE,OAA+B;IAC9G,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACjH,IAAI,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IACpC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC;IAE7C,SAAS,CAAC;QACT,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;YAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QACxD,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC;QAC5B,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YAC5D,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;gBAAE,OAAO,OAAO,CAAC;YAChD,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,KAAK,YAAY,WAAW,IAAI,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC;gBAC/E,UAAU,IAAI,KAAK,CAAC;gBACpB,SAAS;YACV,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ManifestLintResult } from '../../manifest-tools.js';
|
|
2
|
+
/** Raised when `privos-app.json` or `package.json` cannot be located or parsed. */
|
|
3
|
+
export declare class ManifestLoadError extends Error {
|
|
4
|
+
}
|
|
5
|
+
export type LoadedManifest = Readonly<{
|
|
6
|
+
manifestPath: string;
|
|
7
|
+
manifest: Record<string, unknown>;
|
|
8
|
+
}>;
|
|
9
|
+
export type LoadedPackageJson = Readonly<{
|
|
10
|
+
packagePath: string;
|
|
11
|
+
pkg: Record<string, unknown>;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function loadManifest(cwd: string, manifestFile?: string): LoadedManifest;
|
|
14
|
+
export declare function loadPackageJson(cwd: string): LoadedPackageJson;
|
|
15
|
+
/**
|
|
16
|
+
* `privos-app.json` and `package.json` must agree on the identity fields the
|
|
17
|
+
* Portal binds a publish grant to (`name`, `version`). This is a CLI-side
|
|
18
|
+
* fast-fail check; the Portal independently verifies `manifest.name` /
|
|
19
|
+
* `manifest.version` on `complete` (`409 PUBLISH_GRANT_MISMATCH`).
|
|
20
|
+
*/
|
|
21
|
+
export declare function assertIdentityAgreement(manifest: Record<string, unknown>, pkg: Record<string, unknown>): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Structure-only lint. `result.canonicalManifestHash` is the SDK's structural
|
|
24
|
+
* digest (key-sorted JSON, `localeCompare`) and is NOT the Portal's canonical
|
|
25
|
+
* digest (which may differ in comparator and Zod-applied defaults). It must
|
|
26
|
+
* never be sent to the Portal; the Portal computes and returns the
|
|
27
|
+
* authoritative `manifestDigest` on upload `complete`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function structuralLint(manifest: unknown): ManifestLintResult;
|
|
30
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/manifest.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAEhF,mFAAmF;AACnF,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAE/C,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7B,CAAC,CAAC;AAqBH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,SAAoB,GAAG,cAAc,CAG1F;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAG9D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CASjH;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,kBAAkB,CAEpE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { lintManifest } from '../../manifest-tools.js';
|
|
4
|
+
/** Raised when `privos-app.json` or `package.json` cannot be located or parsed. */
|
|
5
|
+
export class ManifestLoadError extends Error {
|
|
6
|
+
}
|
|
7
|
+
function readJsonObject(filePath) {
|
|
8
|
+
let raw;
|
|
9
|
+
try {
|
|
10
|
+
raw = fs.readFileSync(filePath, 'utf8');
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new ManifestLoadError(`Unable to read ${filePath}`);
|
|
14
|
+
}
|
|
15
|
+
let parsed;
|
|
16
|
+
try {
|
|
17
|
+
parsed = JSON.parse(raw);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
throw new ManifestLoadError(`Unable to read valid JSON from ${filePath}`);
|
|
21
|
+
}
|
|
22
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
23
|
+
throw new ManifestLoadError(`${filePath} must contain a JSON object`);
|
|
24
|
+
}
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
export function loadManifest(cwd, manifestFile = 'privos-app.json') {
|
|
28
|
+
const manifestPath = path.resolve(cwd, manifestFile);
|
|
29
|
+
return { manifestPath, manifest: readJsonObject(manifestPath) };
|
|
30
|
+
}
|
|
31
|
+
export function loadPackageJson(cwd) {
|
|
32
|
+
const packagePath = path.resolve(cwd, 'package.json');
|
|
33
|
+
return { packagePath, pkg: readJsonObject(packagePath) };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* `privos-app.json` and `package.json` must agree on the identity fields the
|
|
37
|
+
* Portal binds a publish grant to (`name`, `version`). This is a CLI-side
|
|
38
|
+
* fast-fail check; the Portal independently verifies `manifest.name` /
|
|
39
|
+
* `manifest.version` on `complete` (`409 PUBLISH_GRANT_MISMATCH`).
|
|
40
|
+
*/
|
|
41
|
+
export function assertIdentityAgreement(manifest, pkg) {
|
|
42
|
+
const errors = [];
|
|
43
|
+
if (manifest.name !== pkg.name) {
|
|
44
|
+
errors.push(`privos-app.json "name" (${JSON.stringify(manifest.name)}) does not match package.json "name" (${JSON.stringify(pkg.name)})`);
|
|
45
|
+
}
|
|
46
|
+
if (manifest.version !== pkg.version) {
|
|
47
|
+
errors.push(`privos-app.json "version" (${JSON.stringify(manifest.version)}) does not match package.json "version" (${JSON.stringify(pkg.version)})`);
|
|
48
|
+
}
|
|
49
|
+
return errors;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Structure-only lint. `result.canonicalManifestHash` is the SDK's structural
|
|
53
|
+
* digest (key-sorted JSON, `localeCompare`) and is NOT the Portal's canonical
|
|
54
|
+
* digest (which may differ in comparator and Zod-applied defaults). It must
|
|
55
|
+
* never be sent to the Portal; the Portal computes and returns the
|
|
56
|
+
* authoritative `manifestDigest` on upload `complete`.
|
|
57
|
+
*/
|
|
58
|
+
export function structuralLint(manifest) {
|
|
59
|
+
return lintManifest(manifest);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/cli/lib/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,YAAY,EAA2B,MAAM,yBAAyB,CAAC;AAEhF,mFAAmF;AACnF,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG;AAY/C,SAAS,cAAc,CAAC,QAAgB;IACvC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACJ,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,iBAAiB,CAAC,kBAAkB,QAAQ,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,iBAAiB,CAAC,kCAAkC,QAAQ,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,iBAAiB,CAAC,GAAG,QAAQ,6BAA6B,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,MAAiC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,YAAY,GAAG,iBAAiB;IACzE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACrD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,CAAC,YAAY,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IAC1C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACtD,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAiC,EAAE,GAA4B;IACtG,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,QAAQ,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,yCAAyC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3I,CAAC;IACD,IAAI,QAAQ,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,8BAA8B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,4CAA4C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACvJ,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,QAAiB;IAC/C,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human vs `--json` (NDJSON) rendering for `privos-app publish`, plus secret
|
|
3
|
+
* masking. Grants are never rendered by any event — callers must not pass a
|
|
4
|
+
* grant value into a {@link PublishEvent}. Tokens are only ever shown masked.
|
|
5
|
+
*/
|
|
6
|
+
export type PublishEvent = Readonly<{
|
|
7
|
+
event: 'lint';
|
|
8
|
+
ok: boolean;
|
|
9
|
+
manifestPath: string;
|
|
10
|
+
}> | Readonly<{
|
|
11
|
+
event: 'package';
|
|
12
|
+
archivePath: string;
|
|
13
|
+
bytes: number;
|
|
14
|
+
sha256: string;
|
|
15
|
+
entryCount: number;
|
|
16
|
+
}> | Readonly<{
|
|
17
|
+
event: 'authorization_token';
|
|
18
|
+
maskedToken: string;
|
|
19
|
+
}> | Readonly<{
|
|
20
|
+
event: 'authorization_device';
|
|
21
|
+
verificationUrl: string;
|
|
22
|
+
userCode: string;
|
|
23
|
+
expiresIn: number;
|
|
24
|
+
}> | Readonly<{
|
|
25
|
+
event: 'authorization_waiting';
|
|
26
|
+
elapsedMs: number;
|
|
27
|
+
}> | Readonly<{
|
|
28
|
+
event: 'authorization_approved';
|
|
29
|
+
listingId: string;
|
|
30
|
+
listingSlug: string;
|
|
31
|
+
}> | Readonly<{
|
|
32
|
+
event: 'authorization_denied';
|
|
33
|
+
}> | Readonly<{
|
|
34
|
+
event: 'authorization_expired';
|
|
35
|
+
}> | Readonly<{
|
|
36
|
+
event: 'upload_progress';
|
|
37
|
+
part: number;
|
|
38
|
+
totalParts: number;
|
|
39
|
+
}> | Readonly<{
|
|
40
|
+
event: 'upload_complete';
|
|
41
|
+
sha256: string;
|
|
42
|
+
manifestDigest: string;
|
|
43
|
+
}> | Readonly<{
|
|
44
|
+
event: 'version_created';
|
|
45
|
+
versionId: string;
|
|
46
|
+
semver: string;
|
|
47
|
+
}> | Readonly<{
|
|
48
|
+
event: 'submitted';
|
|
49
|
+
versionId: string;
|
|
50
|
+
}> | Readonly<{
|
|
51
|
+
event: 'status';
|
|
52
|
+
state: string;
|
|
53
|
+
message?: string;
|
|
54
|
+
}> | Readonly<{
|
|
55
|
+
event: 'error';
|
|
56
|
+
code?: string;
|
|
57
|
+
message: string;
|
|
58
|
+
}>;
|
|
59
|
+
export declare class Reporter {
|
|
60
|
+
private readonly jsonMode;
|
|
61
|
+
constructor(jsonMode: boolean);
|
|
62
|
+
emit(event: PublishEvent): void;
|
|
63
|
+
}
|
|
64
|
+
/** Shows only enough of a secret to identify it in logs; never the full value. */
|
|
65
|
+
export declare function maskSecret(value: string): string;
|
|
66
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/output.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,YAAY,GACrB,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,GAC9D,QAAQ,CAAC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,GACtG,QAAQ,CAAC;IAAE,KAAK,EAAE,qBAAqB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,GAC/D,QAAQ,CAAC;IAAE,KAAK,EAAE,sBAAsB,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GACzG,QAAQ,CAAC;IAAE,KAAK,EAAE,uBAAuB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GAC/D,QAAQ,CAAC;IAAE,KAAK,EAAE,wBAAwB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,GACrF,QAAQ,CAAC;IAAE,KAAK,EAAE,sBAAsB,CAAA;CAAE,CAAC,GAC3C,QAAQ,CAAC;IAAE,KAAK,EAAE,uBAAuB,CAAA;CAAE,CAAC,GAC5C,QAAQ,CAAC;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,GACxE,QAAQ,CAAC;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,GAC9E,QAAQ,CAAC;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GACzE,QAAQ,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GACnD,QAAQ,CAAC;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAC9D,QAAQ,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEhE,qBAAa,QAAQ;IACR,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,OAAO;IAE9C,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;CAO/B;AA4CD,kFAAkF;AAClF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGhD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export class Reporter {
|
|
2
|
+
jsonMode;
|
|
3
|
+
constructor(jsonMode) {
|
|
4
|
+
this.jsonMode = jsonMode;
|
|
5
|
+
}
|
|
6
|
+
emit(event) {
|
|
7
|
+
if (this.jsonMode) {
|
|
8
|
+
process.stdout.write(`${JSON.stringify(event)}\n`);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
process.stdout.write(`${formatHumanLine(event)}\n`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function formatHumanLine(event) {
|
|
15
|
+
switch (event.event) {
|
|
16
|
+
case 'lint':
|
|
17
|
+
return event.ok ? `✔ manifest ${event.manifestPath} valid` : `✖ manifest ${event.manifestPath} invalid`;
|
|
18
|
+
case 'package':
|
|
19
|
+
return `✔ archive ${event.archivePath} (${event.bytes} bytes, ${event.entryCount} entries, sha256:${event.sha256})`;
|
|
20
|
+
case 'authorization_token':
|
|
21
|
+
return `→ authorizing with publisher token ${event.maskedToken}`;
|
|
22
|
+
case 'authorization_device':
|
|
23
|
+
return [
|
|
24
|
+
'→ Approve this publish in your browser:',
|
|
25
|
+
` ${event.verificationUrl}`,
|
|
26
|
+
` user code: ${event.userCode}`,
|
|
27
|
+
` (waiting… expires in ${Math.round(event.expiresIn / 60)} min)`,
|
|
28
|
+
].join('\n');
|
|
29
|
+
case 'authorization_waiting':
|
|
30
|
+
return ' (waiting for approval…)';
|
|
31
|
+
case 'authorization_approved':
|
|
32
|
+
return `✔ approved → listing ${event.listingSlug}`;
|
|
33
|
+
case 'authorization_denied':
|
|
34
|
+
return '✖ publish authorization was denied';
|
|
35
|
+
case 'authorization_expired':
|
|
36
|
+
return '✖ publish authorization expired';
|
|
37
|
+
case 'upload_progress':
|
|
38
|
+
return ` uploaded ${event.part}/${event.totalParts} parts`;
|
|
39
|
+
case 'upload_complete':
|
|
40
|
+
return `✔ uploaded · manifest digest ${event.manifestDigest}`;
|
|
41
|
+
case 'version_created':
|
|
42
|
+
return `✔ version ${event.semver} created`;
|
|
43
|
+
case 'submitted':
|
|
44
|
+
return '✔ submitted';
|
|
45
|
+
case 'status':
|
|
46
|
+
return event.message ? `→ status: ${event.state} (${event.message})` : `→ status: ${event.state}`;
|
|
47
|
+
case 'error':
|
|
48
|
+
return `✖ ${event.message}`;
|
|
49
|
+
default: {
|
|
50
|
+
const exhaustive = event;
|
|
51
|
+
return JSON.stringify(exhaustive);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Shows only enough of a secret to identify it in logs; never the full value. */
|
|
56
|
+
export function maskSecret(value) {
|
|
57
|
+
if (value.length <= 8)
|
|
58
|
+
return '***';
|
|
59
|
+
return `${value.slice(0, 8)}…(${value.length} chars)`;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../../src/cli/lib/output.ts"],"names":[],"mappings":"AAqBA,MAAM,OAAO,QAAQ;IACS;IAA7B,YAA6B,QAAiB;QAAjB,aAAQ,GAAR,QAAQ,CAAS;IAAG,CAAC;IAElD,IAAI,CAAC,KAAmB;QACvB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnD,OAAO;QACR,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC;CACD;AAED,SAAS,eAAe,CAAC,KAAmB;IAC3C,QAAQ,KAAK,CAAC,KAAK,EAAE,CAAC;QACrB,KAAK,MAAM;YACV,OAAO,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,KAAK,CAAC,YAAY,QAAQ,CAAC,CAAC,CAAC,cAAc,KAAK,CAAC,YAAY,UAAU,CAAC;QACzG,KAAK,SAAS;YACb,OAAO,aAAa,KAAK,CAAC,WAAW,KAAK,KAAK,CAAC,KAAK,WAAW,KAAK,CAAC,UAAU,oBAAoB,KAAK,CAAC,MAAM,GAAG,CAAC;QACrH,KAAK,qBAAqB;YACzB,OAAO,sCAAsC,KAAK,CAAC,WAAW,EAAE,CAAC;QAClE,KAAK,sBAAsB;YAC1B,OAAO;gBACN,yCAAyC;gBACzC,OAAO,KAAK,CAAC,eAAe,EAAE;gBAC9B,kBAAkB,KAAK,CAAC,QAAQ,EAAE;gBAClC,0BAA0B,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,OAAO;aACjE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,KAAK,uBAAuB;YAC3B,OAAO,2BAA2B,CAAC;QACpC,KAAK,wBAAwB;YAC5B,OAAO,wBAAwB,KAAK,CAAC,WAAW,EAAE,CAAC;QACpD,KAAK,sBAAsB;YAC1B,OAAO,oCAAoC,CAAC;QAC7C,KAAK,uBAAuB;YAC3B,OAAO,iCAAiC,CAAC;QAC1C,KAAK,iBAAiB;YACrB,OAAO,cAAc,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,UAAU,QAAQ,CAAC;QAC7D,KAAK,iBAAiB;YACrB,OAAO,gCAAgC,KAAK,CAAC,cAAc,EAAE,CAAC;QAC/D,KAAK,iBAAiB;YACrB,OAAO,aAAa,KAAK,CAAC,MAAM,UAAU,CAAC;QAC5C,KAAK,WAAW;YACf,OAAO,aAAa,CAAC;QACtB,KAAK,QAAQ;YACZ,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,KAAK,CAAC,KAAK,MAAM,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,aAAa,KAAK,CAAC,KAAK,EAAE,CAAC;QACpG,KAAK,OAAO;YACX,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;QAC7B,OAAO,CAAC,CAAC,CAAC;YACT,MAAM,UAAU,GAAU,KAAK,CAAC;YAChC,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACnC,CAAC;IACF,CAAC;AACF,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,MAAM,SAAS,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy-violation codes surfaced by {@link packageSource}. Every code maps to
|
|
3
|
+
* CLI exit code 2 ("blocked by policy") except `NOT_GIT_REPOSITORY` and
|
|
4
|
+
* `GIT_ARCHIVE_FAILED`, which the caller treats as environment/usage problems.
|
|
5
|
+
*/
|
|
6
|
+
export type PackagePolicyCode = 'NOT_GIT_REPOSITORY' | 'DIRTY_TREE' | 'CREDENTIAL_FILE_FOUND' | 'GIT_ARCHIVE_FAILED' | 'INVALID_ZIP' | 'MISSING_REQUIRED_ENTRY' | 'DENIED_PATH_IN_ARCHIVE' | 'ENTRY_LIMIT_EXCEEDED' | 'FILE_SIZE_EXCEEDED' | 'TOTAL_SIZE_EXCEEDED';
|
|
7
|
+
export declare class PackagePolicyError extends Error {
|
|
8
|
+
readonly code: PackagePolicyCode;
|
|
9
|
+
readonly details: readonly string[];
|
|
10
|
+
constructor(message: string, code: PackagePolicyCode, details?: readonly string[]);
|
|
11
|
+
}
|
|
12
|
+
export type PackageSourceOptions = Readonly<{
|
|
13
|
+
/** Directory the CLI was invoked from; resolved up to the git worktree root. */
|
|
14
|
+
cwd: string;
|
|
15
|
+
/** Package identity used to name the archive: `<outputDir>/<safeName>-<version>.zip`. */
|
|
16
|
+
name: string;
|
|
17
|
+
version: string;
|
|
18
|
+
/** Refuse a dirty tree unless true (mirrors `scripts/package-source.sh`). */
|
|
19
|
+
allowDirty: boolean;
|
|
20
|
+
/** Relative to the git worktree root. Defaults to `dist-source`. */
|
|
21
|
+
outputDir?: string;
|
|
22
|
+
}>;
|
|
23
|
+
export type ZipEntry = Readonly<{
|
|
24
|
+
name: string;
|
|
25
|
+
uncompressedSize: number;
|
|
26
|
+
isDirectory: boolean;
|
|
27
|
+
}>;
|
|
28
|
+
export type PackageSourceResult = Readonly<{
|
|
29
|
+
archivePath: string;
|
|
30
|
+
sha256: string;
|
|
31
|
+
bytes: number;
|
|
32
|
+
/** All central-directory entries, including directory entries (`unzip -Z1` parity). */
|
|
33
|
+
entries: readonly string[];
|
|
34
|
+
/** File entries only (directories filtered out) — the `paths` sent to the Portal upload session. */
|
|
35
|
+
filePaths: readonly string[];
|
|
36
|
+
/** `git rev-parse HEAD` of the worktree the archive was produced from. */
|
|
37
|
+
gitRevision: string;
|
|
38
|
+
repoRoot: string;
|
|
39
|
+
}>;
|
|
40
|
+
export declare const ENTRY_LIMIT = 20000;
|
|
41
|
+
export declare const MAX_FILE_BYTES: number;
|
|
42
|
+
export declare const MAX_TOTAL_BYTES: number;
|
|
43
|
+
export declare function isDeniedArchiveEntry(entryName: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Reads entry names + uncompressed sizes from a ZIP's central directory —
|
|
46
|
+
* the same source `unzip -Z1` reads from, never from `git ls-tree` (a git
|
|
47
|
+
* tree cannot reflect `.gitattributes export-ignore` or `--allow-dirty`
|
|
48
|
+
* untracked additions the way the produced archive does).
|
|
49
|
+
*/
|
|
50
|
+
export declare function readZipCentralDirectoryEntries(buffer: Buffer): ZipEntry[];
|
|
51
|
+
/** Pure policy evaluation over an already-read entry list — kept separate from I/O for fast unit tests. */
|
|
52
|
+
export declare function evaluateArchiveEntries(entries: readonly ZipEntry[]): void;
|
|
53
|
+
/** Recursively scans the worktree for credential-like files, mirroring the `find` guard in `scripts/package-source.sh`. */
|
|
54
|
+
export declare function findCredentialLikeFiles(root: string): string[];
|
|
55
|
+
/**
|
|
56
|
+
* Packages the git worktree at `options.cwd` into a zip archive, applying the
|
|
57
|
+
* same safety policy as `scripts/package-source.sh` (dirty-tree refusal,
|
|
58
|
+
* credential-file scan, denied-path/entry-count/size limits, required root
|
|
59
|
+
* entries), and reads the produced archive's entries from its ZIP central
|
|
60
|
+
* directory. Throws {@link PackagePolicyError} on any violation.
|
|
61
|
+
*/
|
|
62
|
+
export declare function packageSource(options: PackageSourceOptions): PackageSourceResult;
|
|
63
|
+
//# sourceMappingURL=package-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-source.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/package-source.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAC1B,oBAAoB,GACpB,YAAY,GACZ,uBAAuB,GACvB,oBAAoB,GACpB,aAAa,GACb,wBAAwB,GACxB,wBAAwB,GACxB,sBAAsB,GACtB,oBAAoB,GACpB,qBAAqB,CAAC;AAEzB,qBAAa,kBAAmB,SAAQ,KAAK;aACC,IAAI,EAAE,iBAAiB;aAAkB,OAAO,EAAE,SAAS,MAAM,EAAE;gBAApG,OAAO,EAAE,MAAM,EAAkB,IAAI,EAAE,iBAAiB,EAAkB,OAAO,GAAE,SAAS,MAAM,EAAO;CAIrH;AAED,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC3C,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,UAAU,EAAE,OAAO,CAAC;IACpB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAElG,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,oGAAoG;IACpG,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAEH,eAAO,MAAM,WAAW,QAAS,CAAC;AAClC,eAAO,MAAM,cAAc,QAAmB,CAAC;AAC/C,eAAO,MAAM,eAAe,QAAoB,CAAC;AASjD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAQ/D;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmCzE;AAED,2GAA2G;AAC3G,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,CAkBzE;AAED,2HAA2H;AAC3H,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAmC9D;AAOD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAqFhF"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
export class PackagePolicyError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
details;
|
|
9
|
+
constructor(message, code, details = []) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.details = details;
|
|
13
|
+
this.name = 'PackagePolicyError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export const ENTRY_LIMIT = 20_000;
|
|
17
|
+
export const MAX_FILE_BYTES = 50 * 1024 * 1024;
|
|
18
|
+
export const MAX_TOTAL_BYTES = 200 * 1024 * 1024;
|
|
19
|
+
const REQUIRED_ROOT_ENTRIES = ['privos-app.json', 'Dockerfile'];
|
|
20
|
+
// Mirrors the awk rules in `scripts/package-source.sh`'s `bad_entries` check.
|
|
21
|
+
const DENIED_DIR_PATTERN = /(^|\/)(node_modules|dist|dist-source|\.recyclebin|\.git)(\/|$)|(^|\/)\.privos\/skills(\/|$)/;
|
|
22
|
+
const DENIED_ENV_PATTERN = /(^|\/)\.env(\.|$)/;
|
|
23
|
+
const PARENT_TRAVERSAL_PATTERN = /\.\./;
|
|
24
|
+
const CREDENTIAL_ENTRY_PATTERN = /(^|\/)id_rsa|\.pem$|\.key$/;
|
|
25
|
+
export function isDeniedArchiveEntry(entryName) {
|
|
26
|
+
return (DENIED_DIR_PATTERN.test(entryName)
|
|
27
|
+
|| DENIED_ENV_PATTERN.test(entryName)
|
|
28
|
+
|| PARENT_TRAVERSAL_PATTERN.test(entryName)
|
|
29
|
+
|| CREDENTIAL_ENTRY_PATTERN.test(entryName)
|
|
30
|
+
|| entryName.toLowerCase().includes('credentials'));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Reads entry names + uncompressed sizes from a ZIP's central directory —
|
|
34
|
+
* the same source `unzip -Z1` reads from, never from `git ls-tree` (a git
|
|
35
|
+
* tree cannot reflect `.gitattributes export-ignore` or `--allow-dirty`
|
|
36
|
+
* untracked additions the way the produced archive does).
|
|
37
|
+
*/
|
|
38
|
+
export function readZipCentralDirectoryEntries(buffer) {
|
|
39
|
+
const EOCD_SIGNATURE = 0x06054b50;
|
|
40
|
+
const CENTRAL_DIRECTORY_SIGNATURE = 0x02014b50;
|
|
41
|
+
const EOCD_MIN_SIZE = 22;
|
|
42
|
+
const MAX_COMMENT_SIZE = 65_535;
|
|
43
|
+
let eocdOffset = -1;
|
|
44
|
+
const searchFloor = Math.max(0, buffer.length - EOCD_MIN_SIZE - MAX_COMMENT_SIZE);
|
|
45
|
+
for (let offset = buffer.length - EOCD_MIN_SIZE; offset >= searchFloor; offset -= 1) {
|
|
46
|
+
if (buffer.readUInt32LE(offset) === EOCD_SIGNATURE) {
|
|
47
|
+
eocdOffset = offset;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (eocdOffset < 0)
|
|
52
|
+
throw new PackagePolicyError('Archive is not a valid ZIP file (no end-of-central-directory record found).', 'INVALID_ZIP');
|
|
53
|
+
const totalEntries = buffer.readUInt16LE(eocdOffset + 10);
|
|
54
|
+
const centralDirectoryOffset = buffer.readUInt32LE(eocdOffset + 16);
|
|
55
|
+
const entries = [];
|
|
56
|
+
let offset = centralDirectoryOffset;
|
|
57
|
+
for (let index = 0; index < totalEntries; index += 1) {
|
|
58
|
+
if (offset + 46 > buffer.length || buffer.readUInt32LE(offset) !== CENTRAL_DIRECTORY_SIGNATURE) {
|
|
59
|
+
throw new PackagePolicyError('Archive central directory is corrupt or truncated.', 'INVALID_ZIP');
|
|
60
|
+
}
|
|
61
|
+
const uncompressedSize = buffer.readUInt32LE(offset + 24);
|
|
62
|
+
const nameLength = buffer.readUInt16LE(offset + 28);
|
|
63
|
+
const extraLength = buffer.readUInt16LE(offset + 30);
|
|
64
|
+
const commentLength = buffer.readUInt16LE(offset + 32);
|
|
65
|
+
const nameStart = offset + 46;
|
|
66
|
+
const name = buffer.toString('utf8', nameStart, nameStart + nameLength);
|
|
67
|
+
entries.push({ name, uncompressedSize, isDirectory: name.endsWith('/') });
|
|
68
|
+
offset = nameStart + nameLength + extraLength + commentLength;
|
|
69
|
+
}
|
|
70
|
+
return entries;
|
|
71
|
+
}
|
|
72
|
+
/** Pure policy evaluation over an already-read entry list — kept separate from I/O for fast unit tests. */
|
|
73
|
+
export function evaluateArchiveEntries(entries) {
|
|
74
|
+
const names = entries.map((entry) => entry.name);
|
|
75
|
+
for (const required of REQUIRED_ROOT_ENTRIES) {
|
|
76
|
+
if (!names.includes(required)) {
|
|
77
|
+
throw new PackagePolicyError(`Archive is missing required root entry: ${required}`, 'MISSING_REQUIRED_ENTRY', [required]);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const denied = names.filter(isDeniedArchiveEntry);
|
|
81
|
+
if (denied.length > 0) {
|
|
82
|
+
throw new PackagePolicyError('Unsafe or denied paths found in source archive.', 'DENIED_PATH_IN_ARCHIVE', denied);
|
|
83
|
+
}
|
|
84
|
+
if (entries.length > ENTRY_LIMIT) {
|
|
85
|
+
throw new PackagePolicyError(`Archive holds ${entries.length} entries; marketplace limit is ${ENTRY_LIMIT}.`, 'ENTRY_LIMIT_EXCEEDED');
|
|
86
|
+
}
|
|
87
|
+
const oversizedFiles = entries.filter((entry) => !entry.isDirectory && entry.uncompressedSize > MAX_FILE_BYTES).map((entry) => entry.name);
|
|
88
|
+
if (oversizedFiles.length > 0) {
|
|
89
|
+
throw new PackagePolicyError(`Archive contains file(s) exceeding the ${MAX_FILE_BYTES} byte per-file limit.`, 'FILE_SIZE_EXCEEDED', oversizedFiles);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Recursively scans the worktree for credential-like files, mirroring the `find` guard in `scripts/package-source.sh`. */
|
|
93
|
+
export function findCredentialLikeFiles(root) {
|
|
94
|
+
const skipDirectories = new Set(['.git', 'node_modules', 'dist-source']);
|
|
95
|
+
const matches = [];
|
|
96
|
+
const walk = (dir) => {
|
|
97
|
+
let dirEntries;
|
|
98
|
+
try {
|
|
99
|
+
dirEntries = fs.readdirSync(dir, { withFileTypes: true });
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const entry of dirEntries) {
|
|
105
|
+
const fullPath = path.join(dir, entry.name);
|
|
106
|
+
if (entry.isSymbolicLink())
|
|
107
|
+
continue;
|
|
108
|
+
if (entry.isDirectory()) {
|
|
109
|
+
if (skipDirectories.has(entry.name))
|
|
110
|
+
continue;
|
|
111
|
+
walk(fullPath);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (!entry.isFile())
|
|
115
|
+
continue;
|
|
116
|
+
const name = entry.name;
|
|
117
|
+
const lowerName = name.toLowerCase();
|
|
118
|
+
const isDottedEnv = name.startsWith('.env.') && name !== '.env.example';
|
|
119
|
+
const isPem = name.endsWith('.pem');
|
|
120
|
+
const isKeyFile = name.endsWith('.key');
|
|
121
|
+
const isIdRsa = name.startsWith('id_rsa');
|
|
122
|
+
const isCredentialsFile = lowerName.includes('credentials');
|
|
123
|
+
if (isDottedEnv || isPem || isKeyFile || isIdRsa || isCredentialsFile) {
|
|
124
|
+
matches.push(path.relative(root, fullPath));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
walk(root);
|
|
129
|
+
return matches;
|
|
130
|
+
}
|
|
131
|
+
function runGit(args, cwd, env) {
|
|
132
|
+
const result = spawnSync('git', args, { cwd, env: env ?? process.env, encoding: 'utf8', maxBuffer: 1024 * 1024 * 1024 });
|
|
133
|
+
return { stdout: result.stdout ?? '', stderr: result.stderr ?? '', status: result.status };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Packages the git worktree at `options.cwd` into a zip archive, applying the
|
|
137
|
+
* same safety policy as `scripts/package-source.sh` (dirty-tree refusal,
|
|
138
|
+
* credential-file scan, denied-path/entry-count/size limits, required root
|
|
139
|
+
* entries), and reads the produced archive's entries from its ZIP central
|
|
140
|
+
* directory. Throws {@link PackagePolicyError} on any violation.
|
|
141
|
+
*/
|
|
142
|
+
export function packageSource(options) {
|
|
143
|
+
const toplevel = runGit(['rev-parse', '--show-toplevel'], options.cwd);
|
|
144
|
+
if (toplevel.status !== 0) {
|
|
145
|
+
throw new PackagePolicyError(`Not inside a git repository: ${options.cwd}`, 'NOT_GIT_REPOSITORY');
|
|
146
|
+
}
|
|
147
|
+
const repoRoot = toplevel.stdout.trim();
|
|
148
|
+
const headRevision = runGit(['rev-parse', 'HEAD'], repoRoot);
|
|
149
|
+
if (headRevision.status !== 0) {
|
|
150
|
+
throw new PackagePolicyError('Unable to resolve HEAD (repository has no commits).', 'NOT_GIT_REPOSITORY');
|
|
151
|
+
}
|
|
152
|
+
const gitRevision = headRevision.stdout.trim();
|
|
153
|
+
if (!options.allowDirty) {
|
|
154
|
+
const status = runGit(['status', '--porcelain', '--untracked-files=all'], repoRoot);
|
|
155
|
+
if (status.stdout.trim().length > 0) {
|
|
156
|
+
throw new PackagePolicyError('Refusing to package a dirty tree. Commit intended source files, or inspect them and use --allow-dirty.', 'DIRTY_TREE');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const credentialFiles = findCredentialLikeFiles(repoRoot);
|
|
160
|
+
if (credentialFiles.length > 0) {
|
|
161
|
+
throw new PackagePolicyError('Unsafe credential-like files are present in the working tree. Move or remove them before packaging.', 'CREDENTIAL_FILE_FOUND', credentialFiles);
|
|
162
|
+
}
|
|
163
|
+
const outputDir = options.outputDir ?? 'dist-source';
|
|
164
|
+
const outputDirAbsolute = path.join(repoRoot, outputDir);
|
|
165
|
+
fs.mkdirSync(outputDirAbsolute, { recursive: true });
|
|
166
|
+
const safeName = options.name.replaceAll('/', '-');
|
|
167
|
+
const archivePath = path.join(outputDirAbsolute, `${safeName}-${options.version}.zip`);
|
|
168
|
+
let treeish = 'HEAD';
|
|
169
|
+
let tempIndexDir;
|
|
170
|
+
try {
|
|
171
|
+
if (options.allowDirty) {
|
|
172
|
+
tempIndexDir = fs.mkdtempSync(path.join(os.tmpdir(), 'privos-app-package-source-'));
|
|
173
|
+
const indexPath = path.join(tempIndexDir, 'index');
|
|
174
|
+
const gitEnv = { ...process.env, GIT_INDEX_FILE: indexPath };
|
|
175
|
+
const readTree = runGit(['read-tree', 'HEAD'], repoRoot, gitEnv);
|
|
176
|
+
if (readTree.status !== 0)
|
|
177
|
+
throw new PackagePolicyError(`git read-tree failed: ${readTree.stderr.trim()}`, 'GIT_ARCHIVE_FAILED');
|
|
178
|
+
const addAll = runGit(['add', '--all'], repoRoot, gitEnv);
|
|
179
|
+
if (addAll.status !== 0)
|
|
180
|
+
throw new PackagePolicyError(`git add --all failed: ${addAll.stderr.trim()}`, 'GIT_ARCHIVE_FAILED');
|
|
181
|
+
const writeTree = runGit(['write-tree'], repoRoot, gitEnv);
|
|
182
|
+
if (writeTree.status !== 0)
|
|
183
|
+
throw new PackagePolicyError(`git write-tree failed: ${writeTree.stderr.trim()}`, 'GIT_ARCHIVE_FAILED');
|
|
184
|
+
treeish = writeTree.stdout.trim();
|
|
185
|
+
}
|
|
186
|
+
fs.rmSync(archivePath, { force: true });
|
|
187
|
+
const archiveResult = runGit(['archive', '--format=zip', `--output=${archivePath}`, treeish], repoRoot);
|
|
188
|
+
if (archiveResult.status !== 0) {
|
|
189
|
+
throw new PackagePolicyError(`git archive failed: ${archiveResult.stderr.trim()}`, 'GIT_ARCHIVE_FAILED');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
if (tempIndexDir)
|
|
194
|
+
fs.rmSync(tempIndexDir, { recursive: true, force: true });
|
|
195
|
+
}
|
|
196
|
+
const archiveBuffer = fs.readFileSync(archivePath);
|
|
197
|
+
try {
|
|
198
|
+
const zipEntries = readZipCentralDirectoryEntries(archiveBuffer);
|
|
199
|
+
evaluateArchiveEntries(zipEntries);
|
|
200
|
+
if (archiveBuffer.length > MAX_TOTAL_BYTES) {
|
|
201
|
+
throw new PackagePolicyError(`Archive is ${archiveBuffer.length} bytes; marketplace limit is ${MAX_TOTAL_BYTES} bytes.`, 'TOTAL_SIZE_EXCEEDED');
|
|
202
|
+
}
|
|
203
|
+
const sha256 = crypto.createHash('sha256').update(archiveBuffer).digest('hex');
|
|
204
|
+
return {
|
|
205
|
+
archivePath,
|
|
206
|
+
sha256,
|
|
207
|
+
bytes: archiveBuffer.length,
|
|
208
|
+
entries: zipEntries.map((entry) => entry.name),
|
|
209
|
+
filePaths: zipEntries.filter((entry) => !entry.isDirectory).map((entry) => entry.name),
|
|
210
|
+
gitRevision,
|
|
211
|
+
repoRoot,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
fs.rmSync(archivePath, { force: true });
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=package-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-source.js","sourceRoot":"","sources":["../../../src/cli/lib/package-source.ts"],"names":[],"mappings":"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"}
|