@privos_ai/app-server 0.8.4 → 0.10.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.
Files changed (70) hide show
  1. package/README.md +76 -1
  2. package/dist/cli/commands/lint.d.ts +8 -0
  3. package/dist/cli/commands/lint.d.ts.map +1 -0
  4. package/dist/cli/commands/lint.js +24 -0
  5. package/dist/cli/commands/lint.js.map +1 -0
  6. package/dist/cli/commands/publish.d.ts +20 -0
  7. package/dist/cli/commands/publish.d.ts.map +1 -0
  8. package/dist/cli/commands/publish.js +365 -0
  9. package/dist/cli/commands/publish.js.map +1 -0
  10. package/dist/cli/index.d.ts +3 -0
  11. package/dist/cli/index.d.ts.map +1 -0
  12. package/dist/cli/index.js +36 -0
  13. package/dist/cli/index.js.map +1 -0
  14. package/dist/cli/lib/authorize.d.ts +80 -0
  15. package/dist/cli/lib/authorize.d.ts.map +1 -0
  16. package/dist/cli/lib/authorize.js +86 -0
  17. package/dist/cli/lib/authorize.js.map +1 -0
  18. package/dist/cli/lib/manifest.d.ts +30 -0
  19. package/dist/cli/lib/manifest.d.ts.map +1 -0
  20. package/dist/cli/lib/manifest.js +61 -0
  21. package/dist/cli/lib/manifest.js.map +1 -0
  22. package/dist/cli/lib/output.d.ts +66 -0
  23. package/dist/cli/lib/output.d.ts.map +1 -0
  24. package/dist/cli/lib/output.js +61 -0
  25. package/dist/cli/lib/output.js.map +1 -0
  26. package/dist/cli/lib/package-source.d.ts +63 -0
  27. package/dist/cli/lib/package-source.d.ts.map +1 -0
  28. package/dist/cli/lib/package-source.js +219 -0
  29. package/dist/cli/lib/package-source.js.map +1 -0
  30. package/dist/cli/lib/portal-client.d.ts +38 -0
  31. package/dist/cli/lib/portal-client.d.ts.map +1 -0
  32. package/dist/cli/lib/portal-client.js +87 -0
  33. package/dist/cli/lib/portal-client.js.map +1 -0
  34. package/dist/cli/lib/upload.d.ts +54 -0
  35. package/dist/cli/lib/upload.d.ts.map +1 -0
  36. package/dist/cli/lib/upload.js +75 -0
  37. package/dist/cli/lib/upload.js.map +1 -0
  38. package/dist/index.d.ts +6 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +2 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/manifest-lint-cli.js +5 -16
  43. package/dist/manifest-lint-cli.js.map +1 -1
  44. package/dist/relay/relay-client.d.ts.map +1 -1
  45. package/dist/relay/relay-client.js +25 -5
  46. package/dist/relay/relay-client.js.map +1 -1
  47. package/dist/runtime.d.ts +38 -0
  48. package/dist/runtime.d.ts.map +1 -1
  49. package/dist/runtime.js +92 -5
  50. package/dist/runtime.js.map +1 -1
  51. package/dist/ui/asset-filename-rule.d.ts +18 -0
  52. package/dist/ui/asset-filename-rule.d.ts.map +1 -0
  53. package/dist/ui/asset-filename-rule.js +39 -0
  54. package/dist/ui/asset-filename-rule.js.map +1 -0
  55. package/dist/ui/assets-manifest.d.ts +19 -0
  56. package/dist/ui/assets-manifest.d.ts.map +1 -0
  57. package/dist/ui/assets-manifest.js +128 -0
  58. package/dist/ui/assets-manifest.js.map +1 -0
  59. package/dist/ui/serve-built-ui.d.ts +28 -0
  60. package/dist/ui/serve-built-ui.d.ts.map +1 -0
  61. package/dist/ui/serve-built-ui.js +131 -0
  62. package/dist/ui/serve-built-ui.js.map +1 -0
  63. package/dist/ui/shell-watchdog.d.ts +12 -0
  64. package/dist/ui/shell-watchdog.d.ts.map +1 -0
  65. package/dist/ui/shell-watchdog.js +25 -0
  66. package/dist/ui/shell-watchdog.js.map +1 -0
  67. package/package.json +3 -1
  68. package/skill/README.md +14 -0
  69. package/skill/SKILL.md +124 -0
  70. package/skill/references/errors.md +68 -0
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Content-hashed asset filename rule shared by the SDK (build-time validation,
3
+ * `readAsset` lookups) and the Hub route that re-serves these files. A filename
4
+ * must start with an alnum, may contain `.`/`_`/`-` up to a trailing `-<hash>`
5
+ * of at least 8 chars, then one of the allowed extensions. No `/`, no `..`, no
6
+ * `.map` — content hashing itself is guaranteed by the assets manifest listing
7
+ * a file, not by this regex alone.
8
+ */
9
+ export const MCP_UI_ASSET_FILENAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*-[A-Za-z0-9_-]{8,}\.(js|css|svg|json|woff|woff2|ttf|png|jpg|jpeg|gif|webp|avif|ico|wasm|gz)$/;
10
+ /** Extensions a split-build UI is ever allowed to publish under `assets/`. */
11
+ export const MCP_UI_ASSET_EXTENSIONS = [
12
+ 'js',
13
+ 'css',
14
+ 'svg',
15
+ 'json',
16
+ 'woff',
17
+ 'woff2',
18
+ 'ttf',
19
+ 'png',
20
+ 'jpg',
21
+ 'jpeg',
22
+ 'gif',
23
+ 'webp',
24
+ 'avif',
25
+ 'ico',
26
+ 'wasm',
27
+ 'gz',
28
+ ];
29
+ /**
30
+ * `ui://<appSlug>/assets/` — the app-level (not per-entry-point) URI prefix
31
+ * every split asset is addressed under, per the wire contract.
32
+ */
33
+ export function deriveAssetUriPrefix(appSlug) {
34
+ if (typeof appSlug !== 'string' || appSlug.trim().length === 0) {
35
+ throw new Error('deriveAssetUriPrefix requires a non-empty appSlug');
36
+ }
37
+ return `ui://${appSlug}/assets/`;
38
+ }
39
+ //# sourceMappingURL=asset-filename-rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-filename-rule.js","sourceRoot":"","sources":["../../src/ui/asset-filename-rule.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GACpC,yHAAyH,CAAC;AAE3H,8EAA8E;AAC9E,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACtC,IAAI;IACJ,KAAK;IACL,KAAK;IACL,MAAM;IACN,MAAM;IACN,OAAO;IACP,KAAK;IACL,KAAK;IACL,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;IACN,IAAI;CACK,CAAC;AAIX;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAe;IACnD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,QAAQ,OAAO,UAAU,CAAC;AAClC,CAAC"}
@@ -0,0 +1,19 @@
1
+ export interface AssetManifestEntry {
2
+ name: string;
3
+ size: number;
4
+ type: string;
5
+ }
6
+ export interface AssetsManifest {
7
+ files: AssetManifestEntry[];
8
+ }
9
+ export declare function mimeTypeForAssetFile(fileName: string): string;
10
+ /**
11
+ * Build `{ files: [{ name, size, type }] }` for `distDir/assets` — the union
12
+ * of Vite's `.vite/manifest.json` entries and a directory scan (lazy/CSS-only
13
+ * chunks and fonts never appear in the manifest). Every file actually present
14
+ * under `assets/` is validated: filename rule + extension allowlist, no
15
+ * `.map`, size ≤ 2 MB. Throws with the full list of offenders otherwise —
16
+ * this is boot-time validation, never a partial/best-effort manifest.
17
+ */
18
+ export declare function buildAssetsManifest(distDir: string): AssetsManifest;
19
+ //# sourceMappingURL=assets-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets-manifest.d.ts","sourceRoot":"","sources":["../../src/ui/assets-manifest.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,kBAAkB,EAAE,CAAC;CAC5B;AAwBD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG7D;AA4DD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CA4CnE"}
@@ -0,0 +1,128 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { MCP_UI_ASSET_FILENAME_RE } from './asset-filename-rule.js';
4
+ /** Matches the wire contract's per-asset cap; the SDK refuses larger at build time. */
5
+ const MAX_ASSET_BYTES = 2 * 1024 * 1024;
6
+ const MIME_BY_EXTENSION = {
7
+ js: 'text/javascript',
8
+ css: 'text/css',
9
+ svg: 'image/svg+xml',
10
+ json: 'application/json',
11
+ woff: 'font/woff',
12
+ woff2: 'font/woff2',
13
+ ttf: 'font/ttf',
14
+ png: 'image/png',
15
+ jpg: 'image/jpeg',
16
+ jpeg: 'image/jpeg',
17
+ gif: 'image/gif',
18
+ webp: 'image/webp',
19
+ avif: 'image/avif',
20
+ ico: 'image/x-icon',
21
+ wasm: 'application/wasm',
22
+ gz: 'application/gzip',
23
+ };
24
+ export function mimeTypeForAssetFile(fileName) {
25
+ const ext = fileName.slice(fileName.lastIndexOf('.') + 1).toLowerCase();
26
+ return MIME_BY_EXTENSION[ext] ?? 'application/octet-stream';
27
+ }
28
+ /** Vite manifest paths are distDir-relative, e.g. `assets/index-H.js`. */
29
+ function assetFileNameFromManifestPath(relativePath) {
30
+ if (typeof relativePath !== 'string')
31
+ return undefined;
32
+ const normalized = relativePath.replace(/\\/g, '/');
33
+ const prefix = 'assets/';
34
+ return normalized.startsWith(prefix) ? normalized.slice(prefix.length) : undefined;
35
+ }
36
+ function readViteManifestFileNames(distDir) {
37
+ const manifestPath = path.join(distDir, '.vite', 'manifest.json');
38
+ const names = new Set();
39
+ if (!fs.existsSync(manifestPath))
40
+ return names;
41
+ let raw;
42
+ try {
43
+ raw = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
44
+ }
45
+ catch (err) {
46
+ throw new Error(`serveBuiltUi: failed to parse Vite manifest at ${manifestPath}: ${err.message}`);
47
+ }
48
+ if (!raw || typeof raw !== 'object')
49
+ return names;
50
+ for (const chunk of Object.values(raw)) {
51
+ if (!chunk || typeof chunk !== 'object')
52
+ continue;
53
+ const file = assetFileNameFromManifestPath(chunk.file);
54
+ if (file)
55
+ names.add(file);
56
+ if (Array.isArray(chunk.css)) {
57
+ for (const entry of chunk.css) {
58
+ const name = assetFileNameFromManifestPath(entry);
59
+ if (name)
60
+ names.add(name);
61
+ }
62
+ }
63
+ if (Array.isArray(chunk.assets)) {
64
+ for (const entry of chunk.assets) {
65
+ const name = assetFileNameFromManifestPath(entry);
66
+ if (name)
67
+ names.add(name);
68
+ }
69
+ }
70
+ }
71
+ return names;
72
+ }
73
+ function scanAssetsDirectory(assetsDir) {
74
+ const names = new Set();
75
+ if (!fs.existsSync(assetsDir))
76
+ return names;
77
+ for (const entry of fs.readdirSync(assetsDir)) {
78
+ if (fs.statSync(path.join(assetsDir, entry)).isFile())
79
+ names.add(entry);
80
+ }
81
+ return names;
82
+ }
83
+ /**
84
+ * Build `{ files: [{ name, size, type }] }` for `distDir/assets` — the union
85
+ * of Vite's `.vite/manifest.json` entries and a directory scan (lazy/CSS-only
86
+ * chunks and fonts never appear in the manifest). Every file actually present
87
+ * under `assets/` is validated: filename rule + extension allowlist, no
88
+ * `.map`, size ≤ 2 MB. Throws with the full list of offenders otherwise —
89
+ * this is boot-time validation, never a partial/best-effort manifest.
90
+ */
91
+ export function buildAssetsManifest(distDir) {
92
+ const assetsDir = path.join(distDir, 'assets');
93
+ const fileNames = new Set([
94
+ ...readViteManifestFileNames(distDir),
95
+ ...scanAssetsDirectory(assetsDir),
96
+ ]);
97
+ const offenders = [];
98
+ const files = [];
99
+ for (const name of [...fileNames].sort()) {
100
+ const filePath = path.join(assetsDir, name);
101
+ if (name.endsWith('.map')) {
102
+ offenders.push(`${name}: sourcemaps must not be published under assets/`);
103
+ continue;
104
+ }
105
+ if (!MCP_UI_ASSET_FILENAME_RE.test(name)) {
106
+ offenders.push(`${name}: does not match the content-hashed filename rule`);
107
+ continue;
108
+ }
109
+ let stat;
110
+ try {
111
+ stat = fs.statSync(filePath);
112
+ }
113
+ catch {
114
+ offenders.push(`${name}: referenced by the build but missing from ${assetsDir}`);
115
+ continue;
116
+ }
117
+ if (stat.size > MAX_ASSET_BYTES) {
118
+ offenders.push(`${name}: ${stat.size} bytes exceeds the ${MAX_ASSET_BYTES} byte per-asset limit`);
119
+ continue;
120
+ }
121
+ files.push({ name, size: stat.size, type: mimeTypeForAssetFile(name) });
122
+ }
123
+ if (offenders.length > 0) {
124
+ throw new Error(`serveBuiltUi: invalid files under ${assetsDir}:\n${offenders.map((o) => ` - ${o}`).join('\n')}`);
125
+ }
126
+ return { files };
127
+ }
128
+ //# sourceMappingURL=assets-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets-manifest.js","sourceRoot":"","sources":["../../src/ui/assets-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { UiAssetContent } from '../runtime.js';
2
+ import { type AssetsManifest } from './assets-manifest.js';
3
+ export interface ServeBuiltUiOptions {
4
+ /** Absolute path to the Vite build output directory containing `index.html`, `assets/`, `.vite/manifest.json`. */
5
+ distDir: string;
6
+ /** The app's manifest id — used to derive `ui://<appSlug>/assets/…` URIs. */
7
+ appSlug: string;
8
+ }
9
+ export interface ServeBuiltUi {
10
+ /** Cached at construction: meta + watchdog + relative asset tags. */
11
+ renderHtml(): string;
12
+ /** `null` for an unrecognized/unlisted/traversal-unsafe URI. Cached after first successful read. */
13
+ readAsset(uri: string): UiAssetContent | null;
14
+ /** `{ files: [{ name, size, type }] }`, cached at construction. */
15
+ readAssetsManifest(): AssetsManifest;
16
+ /** `ui://<appSlug>/assets/` */
17
+ assetUriPrefix: string;
18
+ }
19
+ /**
20
+ * Serve a Vite-built (`base: './'`) app UI over MCP `resources/read`: the
21
+ * shell HTML (opt-in relay meta + inline boot watchdog) plus the hashed
22
+ * `assets/` files it references, split out of the HTML payload. Boot-time
23
+ * validation (asset filenames, extensions, size, no sourcemaps) and the shell's
24
+ * relative-asset-path assertion both throw at construction — a misconfigured
25
+ * build must fail loudly here, never serve a blank frame in production.
26
+ */
27
+ export declare function serveBuiltUi(options: ServeBuiltUiOptions): ServeBuiltUi;
28
+ //# sourceMappingURL=serve-built-ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-built-ui.d.ts","sourceRoot":"","sources":["../../src/ui/serve-built-ui.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAUhF,MAAM,WAAW,mBAAmB;IACnC,kHAAkH;IAClH,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC5B,qEAAqE;IACrE,UAAU,IAAI,MAAM,CAAC;IACrB,oGAAoG;IACpG,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAAC;IAC9C,mEAAmE;IACnE,kBAAkB,IAAI,cAAc,CAAC;IACrC,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAgDvE"}
@@ -0,0 +1,131 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { deriveAssetUriPrefix } from './asset-filename-rule.js';
4
+ import { buildAssetsManifest } from './assets-manifest.js';
5
+ import { MCP_UI_SHELL_WATCHDOG_SCRIPT } from './shell-watchdog.js';
6
+ const TEXT_ASSET_EXTENSIONS = new Set(['js', 'css', 'svg', 'json']);
7
+ const PRIVOS_UI_ASSETS_META = '<meta name="privos-ui-assets" content="relay">';
8
+ const PRIVOS_UI_ASSETS_META_RE = /<meta\b[^>]*\bname\s*=\s*["']privos-ui-assets["'][^>]*\bcontent\s*=\s*["']relay["']|<meta\b[^>]*\bcontent\s*=\s*["']relay["'][^>]*\bname\s*=\s*["']privos-ui-assets["']/i;
9
+ const ASSET_TAG_RE = /<(script|link)\b[^>]*>/gi;
10
+ const SRC_OR_HREF_RE = /\b(?:src|href)\s*=\s*(["'])(.*?)\1/i;
11
+ /**
12
+ * Serve a Vite-built (`base: './'`) app UI over MCP `resources/read`: the
13
+ * shell HTML (opt-in relay meta + inline boot watchdog) plus the hashed
14
+ * `assets/` files it references, split out of the HTML payload. Boot-time
15
+ * validation (asset filenames, extensions, size, no sourcemaps) and the shell's
16
+ * relative-asset-path assertion both throw at construction — a misconfigured
17
+ * build must fail loudly here, never serve a blank frame in production.
18
+ */
19
+ export function serveBuiltUi(options) {
20
+ const { distDir, appSlug } = options;
21
+ const assetUriPrefix = deriveAssetUriPrefix(appSlug);
22
+ const assetsDir = path.join(distDir, 'assets');
23
+ // Boot-time validation of every file under assets/ — throws with the full
24
+ // offender list on filename-rule / extension / size / sourcemap violations.
25
+ const manifest = buildAssetsManifest(distDir);
26
+ const manifestFiles = new Map(manifest.files.map((file) => [file.name, file]));
27
+ const assetsDirRealpath = fs.existsSync(assetsDir) ? fs.realpathSync(assetsDir) : undefined;
28
+ const html = renderShellHtml(distDir);
29
+ const assetCache = new Map();
30
+ return {
31
+ renderHtml() {
32
+ return html;
33
+ },
34
+ readAsset(uri) {
35
+ const cached = assetCache.get(uri);
36
+ if (cached)
37
+ return cached;
38
+ if (!uri.startsWith(assetUriPrefix))
39
+ return null;
40
+ const fileName = uri.slice(assetUriPrefix.length);
41
+ const entry = manifestFiles.get(fileName);
42
+ if (!entry || !assetsDirRealpath)
43
+ return null;
44
+ const filePath = path.join(assetsDir, fileName);
45
+ let realFilePath;
46
+ try {
47
+ realFilePath = fs.realpathSync(filePath);
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ if (!isContainedIn(assetsDirRealpath, realFilePath))
53
+ return null;
54
+ const ext = fileName.slice(fileName.lastIndexOf('.') + 1).toLowerCase();
55
+ const resource = TEXT_ASSET_EXTENSIONS.has(ext)
56
+ ? { uri, mimeType: entry.type, text: fs.readFileSync(realFilePath, 'utf8') }
57
+ : { uri, mimeType: entry.type, blob: fs.readFileSync(realFilePath).toString('base64') };
58
+ assetCache.set(uri, resource);
59
+ return resource;
60
+ },
61
+ readAssetsManifest() {
62
+ return manifest;
63
+ },
64
+ assetUriPrefix,
65
+ };
66
+ }
67
+ function isContainedIn(dirRealpath, fileRealpath) {
68
+ const relative = path.relative(dirRealpath, fileRealpath);
69
+ return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
70
+ }
71
+ function renderShellHtml(distDir) {
72
+ const indexPath = path.join(distDir, 'index.html');
73
+ let raw;
74
+ try {
75
+ raw = fs.readFileSync(indexPath, 'utf8');
76
+ }
77
+ catch (err) {
78
+ throw new Error(`serveBuiltUi: cannot read ${indexPath}: ${err.message}`);
79
+ }
80
+ assertRelativeAssetTags(raw, indexPath);
81
+ let html = raw;
82
+ if (!PRIVOS_UI_ASSETS_META_RE.test(html)) {
83
+ html = injectIntoHead(html, ` ${PRIVOS_UI_ASSETS_META}\n`, { atStart: false });
84
+ }
85
+ html = injectIntoHead(html, ` <script>${MCP_UI_SHELL_WATCHDOG_SCRIPT}</script>\n`, { atStart: true });
86
+ return html;
87
+ }
88
+ /**
89
+ * Every `<script src>` / `<link href>` in the shell must be relative
90
+ * (`./assets/…` or `assets/…`). An absolute or external reference means the
91
+ * app was built without Vite `base: './'` and would resolve against the
92
+ * wrong origin once the tab renders it — fail construction, not the frame.
93
+ */
94
+ function assertRelativeAssetTags(html, indexPath) {
95
+ const offenders = [];
96
+ ASSET_TAG_RE.lastIndex = 0;
97
+ let tagMatch;
98
+ while ((tagMatch = ASSET_TAG_RE.exec(html))) {
99
+ const tag = tagMatch[0];
100
+ const refMatch = SRC_OR_HREF_RE.exec(tag);
101
+ if (!refMatch)
102
+ continue;
103
+ const ref = refMatch[2];
104
+ if (ref && (ref.startsWith('./assets/') || ref.startsWith('assets/')))
105
+ continue;
106
+ offenders.push(tag.length > 120 ? `${tag.slice(0, 117)}...` : tag);
107
+ }
108
+ if (offenders.length > 0) {
109
+ throw new Error(`serveBuiltUi: ${indexPath} has non-relative asset references — build with Vite base: './':\n${offenders
110
+ .map((o) => ` - ${o}`)
111
+ .join('\n')}`);
112
+ }
113
+ }
114
+ function injectIntoHead(html, snippet, opts) {
115
+ if (opts.atStart) {
116
+ const headOpen = /<head[^>]*>/i.exec(html);
117
+ if (headOpen) {
118
+ const insertAt = headOpen.index + headOpen[0].length;
119
+ return `${html.slice(0, insertAt)}\n${snippet}${html.slice(insertAt)}`;
120
+ }
121
+ }
122
+ else {
123
+ const headCloseIdx = html.search(/<\/head>/i);
124
+ if (headCloseIdx !== -1) {
125
+ return html.slice(0, headCloseIdx) + snippet + html.slice(headCloseIdx);
126
+ }
127
+ }
128
+ // No <head> tag found in a well-formed Vite build — fall back to prepending.
129
+ return snippet + html;
130
+ }
131
+ //# sourceMappingURL=serve-built-ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-built-ui.js","sourceRoot":"","sources":["../../src/ui/serve-built-ui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Inline boot watchdog injected at the top of `<head>`. No dependencies, runs
3
+ * before any other script/link tag so its capture-phase `error` listener is
4
+ * registered before those tags start loading. Two failure signals:
5
+ * - a script/link `error` event (asset 404/network failure), or
6
+ * - a 10 s timeout without `window.__privosUiBooted` being set by the app's
7
+ * own bootstrap once it renders.
8
+ * Either one replaces `#root` (or `document.body`) with a visible retry panel
9
+ * and notifies the host frame via `postMessage` so the Hub can log it.
10
+ */
11
+ export declare const MCP_UI_SHELL_WATCHDOG_SCRIPT = "(function(){\nfunction fail(){\nif(window.__privosUiBooted)return;\nvar root=document.getElementById('root')||document.body;\nif(root){root.innerHTML='<div style=\"font:14px system-ui;padding:24px;text-align:center\">App assets unavailable \u2014 <button onclick=\"location.reload()\">Retry</button></div>';}\ntry{parent.postMessage({method:'ui/asset-load-failed'},'*');}catch(e){}\n}\nwindow.addEventListener('error',function(e){\nvar t=e&&e.target;\nif(!t||(t.tagName!=='SCRIPT'&&t.tagName!=='LINK'))return;\nfail();\n},true);\nsetTimeout(fail,10000);\n})();";
12
+ //# sourceMappingURL=shell-watchdog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-watchdog.d.ts","sourceRoot":"","sources":["../../src/ui/shell-watchdog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,qjBAanC,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Inline boot watchdog injected at the top of `<head>`. No dependencies, runs
3
+ * before any other script/link tag so its capture-phase `error` listener is
4
+ * registered before those tags start loading. Two failure signals:
5
+ * - a script/link `error` event (asset 404/network failure), or
6
+ * - a 10 s timeout without `window.__privosUiBooted` being set by the app's
7
+ * own bootstrap once it renders.
8
+ * Either one replaces `#root` (or `document.body`) with a visible retry panel
9
+ * and notifies the host frame via `postMessage` so the Hub can log it.
10
+ */
11
+ export const MCP_UI_SHELL_WATCHDOG_SCRIPT = `(function(){
12
+ function fail(){
13
+ if(window.__privosUiBooted)return;
14
+ var root=document.getElementById('root')||document.body;
15
+ if(root){root.innerHTML='<div style="font:14px system-ui;padding:24px;text-align:center">App assets unavailable — <button onclick="location.reload()">Retry</button></div>';}
16
+ try{parent.postMessage({method:'ui/asset-load-failed'},'*');}catch(e){}
17
+ }
18
+ window.addEventListener('error',function(e){
19
+ var t=e&&e.target;
20
+ if(!t||(t.tagName!=='SCRIPT'&&t.tagName!=='LINK'))return;
21
+ fail();
22
+ },true);
23
+ setTimeout(fail,10000);
24
+ })();`;
25
+ //# sourceMappingURL=shell-watchdog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-watchdog.js","sourceRoot":"","sources":["../../src/ui/shell-watchdog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG;;;;;;;;;;;;;MAatC,CAAC"}
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@privos_ai/app-server",
3
- "version": "0.8.4",
3
+ "version": "0.10.0",
4
4
  "description": "Business-agnostic PrivOS MCP App server runtime — Direct HTTP + Relay WebSocket",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "bin": {
9
+ "privos-app": "./dist/cli/index.js",
9
10
  "privos-app-lint": "./dist/manifest-lint-cli.js"
10
11
  },
11
12
  "exports": {
@@ -40,6 +41,7 @@
40
41
  },
41
42
  "files": [
42
43
  "dist",
44
+ "skill",
43
45
  "README.md",
44
46
  "LICENSE"
45
47
  ],
@@ -0,0 +1,14 @@
1
+ # `privos-app-publish` Claude Skill
2
+
3
+ This directory is the packaged Claude skill for publishing PrivOS MCP apps
4
+ to the Marketplace with the `privos-app` CLI (shipped in this package's
5
+ `bin`). It is not loaded automatically — `create-privos-mcp-app` copies it
6
+ into a generated app's `.claude/skills/privos-app-publish/` at scaffold time.
7
+
8
+ - `SKILL.md` — when to use, preconditions, exact commands, `--json` event
9
+ reading, approval-URL handoff, token-mode rules, exit codes.
10
+ - `references/errors.md` — full error-code table and remediation.
11
+
12
+ Keep this content in sync with the actual CLI flags/behavior in
13
+ `src/cli/commands/publish.ts` and `src/cli/lib/*.ts` — it is generated from
14
+ that source, not hand-invented.
package/skill/SKILL.md ADDED
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: privos-app-publish
3
+ description: Publish a PrivOS MCP app to the Marketplace using the `privos-app` CLI (package, lint, upload, version, submit) with either interactive browser approval or a CI publisher token.
4
+ user-invocable: true
5
+ when_to_use: "Invoke when the user asks to publish, release, or ship a new version of an MCP app to the PrivOS Marketplace."
6
+ category: publishing
7
+ keywords: [privos-app, marketplace, publish, mcp-app, cli]
8
+ license: MIT
9
+ argument-hint: "[app directory]"
10
+ metadata:
11
+ author: privos
12
+ version: "1.0.0"
13
+ ---
14
+
15
+ # PrivOS App Publish Skill
16
+
17
+ Runs `privos-app publish` correctly from an MCP app folder. The CLI (shipped
18
+ in `@privos_ai/app-server`, bin `privos-app`) is the only supported publish
19
+ path — it is a scripted `npm publish`-shaped flow: package → lint → authorize
20
+ → upload → version → submit. Do not hand-drive Portal API calls or resurrect
21
+ the retired `.mjs` publish scripts.
22
+
23
+ ## When to Use
24
+
25
+ - The user asks to publish, release, or ship a new version of an MCP app to
26
+ the PrivOS Marketplace.
27
+ - The user asks to check why a publish failed (see `references/errors.md`).
28
+
29
+ Not for: editing listing content, pricing, screenshots, or Stripe Connect
30
+ (all Creator Studio, web-only) — the CLI never touches those.
31
+
32
+ ## Preconditions (verify before running)
33
+
34
+ 1. **Clean git tree.** `privos-app publish` refuses a dirty working tree
35
+ unless `--allow-dirty` is passed. Prefer committing first; only use
36
+ `--allow-dirty` when the user explicitly wants an uncommitted snapshot
37
+ packaged.
38
+ 2. **Version bumped in BOTH files.** `privos-app.json` `version` and
39
+ `package.json` `version` must be equal (the CLI fast-fails otherwise,
40
+ `MANIFEST_IDENTITY_MISMATCH`) and must be a semver that has never been
41
+ published for this listing before — re-publishing an existing semver fails
42
+ with `VERSION_SEMVER_EXISTS`. Always bump both fields together.
43
+ 3. **Manifest passes structural lint.** Run `privos-app lint` (or let
44
+ `publish` run it — it lints as step 1) before attempting a full publish.
45
+ 4. **Catalog check.** Confirm this is genuinely a new version of an existing
46
+ listing or a legitimately new app — do not re-run publish blindly after a
47
+ failure without reading the error.
48
+
49
+ ## Exact Commands
50
+
51
+ Run from the app's root directory (where `privos-app.json` lives):
52
+
53
+ ```bash
54
+ # Preview only — packages the archive, prints its sha256, does not authorize or upload
55
+ npx privos-app publish --dry-run
56
+
57
+ # Interactive (default): prints an approval URL, waits for browser approval
58
+ npx privos-app publish
59
+
60
+ # Non-interactive / CI: requires PRIVOS_PUBLISHER_TOKEN in the environment
61
+ PRIVOS_PUBLISHER_TOKEN=pvp_xxx npx privos-app publish --yes
62
+
63
+ # Machine-readable event stream (one NDJSON object per line on stdout)
64
+ npx privos-app publish --json
65
+ ```
66
+
67
+ Useful flags: `--listing <slug>` (when the listing can't be resolved from the
68
+ manifest name), `--changelog <text>` / `--changelog-file <path>`,
69
+ `--allow-dirty`, `--portal <origin>` (default `https://portal.privos.io`),
70
+ `--machine-label <text>` (shown on the approval page, never the hostname by
71
+ default), `--cwd <path>`, `-h/--help`.
72
+
73
+ ## Reading `--json` Output
74
+
75
+ Each line is one JSON object with an `event` field, emitted in this order:
76
+ `lint` → `package` → (`authorization_token` | `authorization_device` →
77
+ `authorization_approved`) → `upload_progress` (repeated) → `upload_complete`
78
+ → `version_created` → `submitted` → `status`. A run that fails emits `error`
79
+ with `code` and `message` instead of continuing. Never print or log a `grant`
80
+ or token value beyond what the CLI itself already masks — the CLI does not
81
+ emit raw secrets in any event.
82
+
83
+ ## Handing the Approval URL to the User
84
+
85
+ **The agent cannot click the approval link.** On `authorization_device`,
86
+ print the `verificationUrl` and `userCode` verbatim to the user and ask them
87
+ to open it in their own browser and log in to `client.privos.io` — do not
88
+ attempt to fetch, curl, or "visit" the URL yourself. The CLI polls
89
+ automatically after printing it; do not re-run `publish` while it is waiting.
90
+ If the user says they approved but the CLI is still waiting, tell them to
91
+ recheck they approved the correct listing/version shown on the page —
92
+ approval is not automatic on click, it depends on identity match.
93
+
94
+ ## Token Mode (CI / Non-Interactive)
95
+
96
+ `PRIVOS_PUBLISHER_TOKEN` must come from the environment only — never ask the
97
+ user to paste a `pvp_…` token into chat, never write one into a command
98
+ argument, file, or log. If the user wants CI mode but has no token yet, tell
99
+ them to generate one from Creator Studio (a step-up action: email code or
100
+ magic link, plus TOTP if their account has 2FA) and export it in their own
101
+ shell/CI secret store before re-running. Token mode only works after the
102
+ listing's first version was approved once interactively (see
103
+ `LISTING_NOT_BOUND` below).
104
+
105
+ ## Exit Codes
106
+
107
+ | Code | Meaning |
108
+ |---|---|
109
+ | 0 | Submitted (or reached a benign non-blocking status; see `references/errors.md`) |
110
+ | 2 | Blocked by policy (manifest invalid, dirty tree, denied archive path, semver reused, preflight failed) |
111
+ | 3 | Authorization denied, expired, or already consumed |
112
+ | 4 | Network or Portal error |
113
+ | 5 | Usage error (bad flags, cancelled prompt, not a git repository) |
114
+
115
+ ## After Submit
116
+
117
+ The CLI has no `status` or `whoami` command. Once `submitted` prints, review
118
+ progress (`PREFLIGHT_PENDING` → `SCAN` → `AI_REVIEW` → human `APPROVE` →
119
+ build) is tracked only in Creator Studio on `client.privos.io`. Tell the user
120
+ to check there — do not invent a polling loop or guess at review state.
121
+
122
+ See `references/errors.md` for the full error-code table and remediation,
123
+ including `VERSION_SEMVER_EXISTS`, `PREFLIGHT_FAILED` vs
124
+ `PREFLIGHT_BLOCKED_INFRA` vs incomplete listing content.