@khal-os/types 1.0.18 → 1.0.24
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/dist/index.cjs +5 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +149 -1
- package/dist/index.d.ts +149 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
BUG_REPORTER_CLIENT_SCHEMA_VERSION: () => BUG_REPORTER_CLIENT_SCHEMA_VERSION,
|
|
23
24
|
KhalAppEntrySchema: () => KhalAppEntrySchema,
|
|
24
25
|
KhalAppManifestSchema: () => KhalAppManifestSchema,
|
|
25
26
|
ROLE_HIERARCHY: () => ROLE_HIERARCHY,
|
|
@@ -27,6 +28,9 @@ __export(index_exports, {
|
|
|
27
28
|
});
|
|
28
29
|
module.exports = __toCommonJS(index_exports);
|
|
29
30
|
|
|
31
|
+
// src/bug-reporter.ts
|
|
32
|
+
var BUG_REPORTER_CLIENT_SCHEMA_VERSION = 1;
|
|
33
|
+
|
|
30
34
|
// src/manifest.ts
|
|
31
35
|
var import_zod = require("zod");
|
|
32
36
|
var KhalPermission = import_zod.z.enum([
|
|
@@ -123,6 +127,7 @@ function validateManifest(raw) {
|
|
|
123
127
|
var ROLE_HIERARCHY = ["member", "platform-dev", "platform-admin", "platform-owner"];
|
|
124
128
|
// Annotate the CommonJS export names for ESM import in node:
|
|
125
129
|
0 && (module.exports = {
|
|
130
|
+
BUG_REPORTER_CLIENT_SCHEMA_VERSION,
|
|
126
131
|
KhalAppEntrySchema,
|
|
127
132
|
KhalAppManifestSchema,
|
|
128
133
|
ROLE_HIERARCHY,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/manifest.ts","../src/roles.ts"],"sourcesContent":["export type { KhalAuth } from './auth';\nexport type {\n\tAppDeployConfig,\n\tAppDesktopConfig,\n\tAppEnvVar,\n\tAppManifest,\n\tAppManifestView,\n\tAppServiceConfig,\n\tAppTauriConfig,\n\tKhalAppEntry,\n\tKhalAppManifest,\n\tKhalPermission,\n\tKhalServiceSpec,\n\tKhalWindowSpec,\n\tSandboxConfig,\n\tSandboxMount,\n\tSandboxResourceSpec,\n\tServiceHealthConfig,\n} from './manifest';\nexport {\n\tKhalAppEntrySchema,\n\tKhalAppManifestSchema,\n\tvalidateManifest,\n} from './manifest';\nexport type { ConnectionState } from './nats';\nexport type { Role } from './roles';\nexport { ROLE_HIERARCHY } from './roles';\n","import { z } from 'zod';\nimport type { Role } from './roles';\n\n/** Desktop integration metadata for an app. */\nexport interface AppDesktopConfig {\n\t/** Path to the app icon (relative to the public directory). */\n\ticon: string;\n\t/** Categories for desktop launcher grouping. */\n\tcategories: string[];\n\t/** Short description shown in the desktop launcher. */\n\tcomment: string;\n}\n\n/** A single view within an app manifest. */\nexport interface AppManifestView {\n\t/** Unique view identifier within the app. */\n\tid: string;\n\t/** Human-readable label for the view. */\n\tlabel: string;\n\t/** Permission string required to access this view. */\n\tpermission: string;\n\t/** Minimum role level required. */\n\tminRole: Role;\n\t/** NATS subject segment after `khal.<orgId>.` for this view's services. */\n\tnatsPrefix?: string;\n\t/** Default window dimensions. */\n\tdefaultSize: { width: number; height: number };\n\t/** Relative path to the view's React component. */\n\tcomponent: string;\n}\n\n/** Health check configuration for a service. */\nexport interface ServiceHealthConfig {\n\t/** Check type: tcp (connect to port), http (GET endpoint), command (run shell). */\n\ttype: 'tcp' | 'http' | 'command';\n\t/** Target: port number for tcp, URL for http, shell command for command. */\n\ttarget: string | number;\n\t/** Check interval in milliseconds (default: 30000). */\n\tinterval?: number;\n\t/** Timeout in milliseconds (default: 5000). */\n\ttimeout?: number;\n}\n\n/** Service declaration within an app manifest. */\nexport interface AppServiceConfig {\n\t/** Service name (must be unique across the app). */\n\tname: string;\n\t/** Shell command to start the service (alternative to entry). */\n\tcommand?: string;\n\t/** Entry point file path relative to the package root. */\n\tentry?: string;\n\t/** Runtime environment. */\n\truntime?: 'node' | 'python';\n\t/** Health check configuration. */\n\thealth?: ServiceHealthConfig;\n\t/** Restart policy. */\n\trestart?: 'always' | 'on-failure' | 'never';\n\t/** Ports the service binds to internally. Khal assigns proxy ports. */\n\tports?: number[];\n}\n\n/** Environment variable declaration for app configuration. */\nexport interface AppEnvVar {\n\t/** Variable name (e.g., \"API_KEY\"). */\n\tkey: string;\n\t/** Human-readable description shown in the config UI. */\n\tdescription: string;\n\t/** Whether the variable is required for the app to run. */\n\trequired: boolean;\n\t/** Default value if not configured. */\n\tdefault?: string;\n\t/** Value type — affects config UI rendering and validation. */\n\ttype?: 'string' | 'number' | 'boolean' | 'secret' | 'url';\n\t/** Storage: 'config' for plain ConfigMap, 'vault' for Kubernetes Secret. */\n\tvisibility?: 'config' | 'vault';\n}\n\n/** Kubernetes deployment configuration for apps with backends. */\nexport interface AppDeployConfig {\n\t/** Dockerfile path relative to app root (default: \"Dockerfile\"). */\n\tdockerfile?: string;\n\t/** Build args passed to docker build. */\n\tbuildArgs?: Record<string, string>;\n\t/** Container port the app listens on. */\n\tport?: number;\n\t/** Resource requests and limits for the pod. */\n\tresources?: {\n\t\trequests?: { cpu?: string; memory?: string };\n\t\tlimits?: { cpu?: string; memory?: string };\n\t};\n\t/** Replica count (default: 1). */\n\treplicas?: number;\n\t/** Health check path for k8s readiness/liveness probes. */\n\thealthPath?: string;\n\t/** Ingress configuration for per-app routing. */\n\tingress?: {\n\t\t/** Subdomain prefix: <value>.apps.<domain>. Defaults to app id. */\n\t\tsubdomain?: string;\n\t\t/** Additional path prefixes to route to this app. */\n\t\tpathPrefixes?: string[];\n\t};\n\t/** Horizontal Pod Autoscaler configuration. */\n\tautoscaling?: {\n\t\tenabled: boolean;\n\t\tminReplicas?: number;\n\t\tmaxReplicas?: number;\n\t\ttargetCPU?: number;\n\t};\n\t/** Environment sources injected at runtime from k8s resources. */\n\tenvFrom?: Array<{ secretRef?: string; configMapRef?: string }>;\n}\n\n/** Sandbox resource specification for per-user container provisioning. */\nexport interface SandboxResourceSpec {\n\t/** CPU request (e.g., \"4\"). */\n\tcpu: string;\n\t/** Memory request (e.g., \"4Gi\"). */\n\tmemory: string;\n}\n\n/** Volume mount configuration for sandbox containers. */\nexport interface SandboxMount {\n\t/** Mount path inside the sandbox container. */\n\tmountPath: string;\n\t/** Source type: 'files' mounts user's Files PVC, 'pvc' mounts a named PVC. */\n\tsource: 'files' | 'pvc';\n\t/** PVC name (required when source is 'pvc'). */\n\tpvcName?: string;\n\t/** Read-only mount. Default: false. */\n\treadOnly?: boolean;\n}\n\n/**\n * Sandbox configuration for apps that require a per-user container.\n * When present on an app manifest, installing the app triggers sandbox\n * provisioning via `os.sandbox.create`.\n */\nexport interface SandboxConfig {\n\t/** Enable sandbox provisioning on install. */\n\tenabled: boolean;\n\t/** Base container image (e.g., \"khal-os/sandbox-ubuntu:latest\"). */\n\timage?: string;\n\t/** Resource requests for the sandbox pod. */\n\trequests?: SandboxResourceSpec;\n\t/** Resource limits for the sandbox pod (burstable). */\n\tlimits?: SandboxResourceSpec;\n\t/** Volume mounts inside the sandbox. */\n\tmounts?: SandboxMount[];\n\t/** Whether the sandbox user gets sudo access. Default: true. */\n\tsudoers?: boolean;\n\t/** NATS subject prefix for sandbox communication (default: \"os.sandbox\"). */\n\tnatsPrefix?: string;\n}\n\n/** Tauri standalone export configuration. */\nexport interface AppTauriConfig {\n\t/** Whether this app supports standalone Tauri export. */\n\texportable: boolean;\n\t/** Path to src-tauri/ directory (default: \"./src-tauri\"). */\n\ttauriDir?: string;\n\t/** App name for the exported binary. */\n\tappName?: string;\n\t/** App icon path relative to app root. */\n\ticon?: string;\n\t/** Window configuration for standalone mode. */\n\twindow?: { width?: number; height?: number; title?: string };\n}\n\n/**\n * Full app manifest — the type for `manifest.ts` files in KhalOS app packages.\n *\n * Every app package must export a default manifest conforming to this shape.\n * Use `defineManifest()` for compile-time validation and autocomplete.\n *\n * The JSON equivalent (`khal-app.json`) uses the same shape and is the\n * language-agnostic install-time contract that the marketplace reads.\n */\nexport interface AppManifest {\n\t/** Unique app identifier (must match the package directory name). */\n\tid: string;\n\t/** One or more views the app exposes. */\n\tviews: AppManifestView[];\n\t/** Desktop integration configuration. */\n\tdesktop: AppDesktopConfig;\n\t/** Backend services this app runs. Optional — pure UI apps have no services. */\n\tservices?: AppServiceConfig[];\n\n\t// ── v2 fields (all optional for backward compatibility) ──\n\n\t/** Schema version for forward compatibility (default: 1). */\n\tschemaVersion?: number;\n\t/** Human-readable app name. */\n\tname?: string;\n\t/** Semantic version of the app. */\n\tversion?: string;\n\t/** Short description for the marketplace listing. */\n\tdescription?: string;\n\t/** Author name or organization. */\n\tauthor?: string;\n\t/** SPDX license identifier. */\n\tlicense?: string;\n\t/** Source repository URL. */\n\trepository?: string;\n\t/** Minimum KhalOS host version required. */\n\tminHostVersion?: string;\n\t/** Environment variables the app needs — auto-generates config UI. */\n\tenv?: AppEnvVar[];\n\t/** Native Kubernetes deployment configuration. */\n\tdeploy?: AppDeployConfig;\n\t/** Tauri standalone export configuration. */\n\ttauri?: AppTauriConfig;\n\t/** URL to the app's pre-built ESM bundle for runtime loading by desktop. */\n\tbundleUrl?: string;\n\t/** Sandbox configuration — when present, installing this app provisions a per-user container. */\n\tsandbox?: SandboxConfig;\n}\n\n// ── Pack Contract Types (for standalone pack-* repos) ──\n\nexport const KhalPermission = z.enum([\n\t'nats:publish',\n\t'nats:subscribe',\n\t'files:read',\n\t'files:write',\n\t'pty:spawn',\n\t'http:fetch',\n\t'system:clipboard',\n\t'system:notifications',\n]);\nexport type KhalPermission = z.infer<typeof KhalPermission>;\n\nexport const KhalServiceSpec = z.object({\n\tname: z.string(),\n\tcommand: z.string().optional(),\n\tentry: z.string().optional(),\n\truntime: z.enum(['node', 'bun', 'python']).optional(),\n\tports: z.array(z.number()).optional(),\n\thealth: z\n\t\t.object({\n\t\t\ttype: z.enum(['tcp', 'http', 'command']),\n\t\t\ttarget: z.union([z.string(), z.number()]),\n\t\t\tinterval: z.number().optional(),\n\t\t\ttimeout: z.number().optional(),\n\t\t})\n\t\t.optional(),\n});\nexport type KhalServiceSpec = z.infer<typeof KhalServiceSpec>;\n\nexport const KhalWindowSpec = z.object({\n\tid: z.string(),\n\ttitle: z.string().optional(),\n\twidth: z.number().default(800),\n\theight: z.number().default(600),\n\tresizable: z.boolean().default(true),\n});\nexport type KhalWindowSpec = z.infer<typeof KhalWindowSpec>;\n\n/** A single app entry within a bundle pack manifest. */\nexport const KhalAppEntrySchema = z.object({\n\t/** Unique app identifier within the bundle. */\n\tid: z.string(),\n\t/** Human-readable app name. */\n\tname: z.string(),\n\t/** Frontend package reference for this app. */\n\tfrontend: z\n\t\t.object({\n\t\t\tpackage: z.string(),\n\t\t})\n\t\t.optional(),\n});\nexport type KhalAppEntry = z.infer<typeof KhalAppEntrySchema>;\n\nexport const KhalAppManifestSchema = z\n\t.object({\n\t\t$schema: z.string().optional(),\n\t\tid: z.string(),\n\t\tname: z.string(),\n\t\tversion: z.string(),\n\t\ticon: z.string(),\n\t\tdescription: z.string(),\n\t\tauthor: z.string(),\n\t\tpermissions: z.array(KhalPermission),\n\t\tservices: z.array(KhalServiceSpec).optional(),\n\t\twindows: z.array(KhalWindowSpec).optional(),\n\t\tfrontend: z\n\t\t\t.object({\n\t\t\t\tpackage: z.string(),\n\t\t\t\tentry: z.string().default('default'),\n\t\t\t})\n\t\t\t.optional(),\n\t\tbackend: z\n\t\t\t.object({\n\t\t\t\timage: z.string(),\n\t\t\t\thelmChart: z.string().optional(),\n\t\t\t\tenv: z.record(z.string(), z.string()),\n\t\t\t\tports: z.array(z.number()),\n\t\t\t})\n\t\t\t.optional(),\n\t\t/** Relative path to the pre-built ESM bundle within the package (e.g., \"dist/bundle.mjs\"). */\n\t\tbundlePath: z.string().optional(),\n\t\t/** Sandbox configuration for per-user container provisioning. */\n\t\tsandbox: z\n\t\t\t.object({\n\t\t\t\tenabled: z.boolean(),\n\t\t\t\timage: z.string().optional(),\n\t\t\t\trequests: z\n\t\t\t\t\t.object({ cpu: z.string(), memory: z.string() })\n\t\t\t\t\t.optional(),\n\t\t\t\tlimits: z\n\t\t\t\t\t.object({ cpu: z.string(), memory: z.string() })\n\t\t\t\t\t.optional(),\n\t\t\t\tmounts: z\n\t\t\t\t\t.array(\n\t\t\t\t\t\tz.object({\n\t\t\t\t\t\t\tmountPath: z.string(),\n\t\t\t\t\t\t\tsource: z.enum(['files', 'pvc']),\n\t\t\t\t\t\t\tpvcName: z.string().optional(),\n\t\t\t\t\t\t\treadOnly: z.boolean().optional(),\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\t.optional(),\n\t\t\t\tsudoers: z.boolean().optional(),\n\t\t\t\tnatsPrefix: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t\t/**\n\t\t * When apps is present, the root frontend field is ignored —\n\t\t * each entry has its own frontend.\n\t\t */\n\t\tapps: z.array(KhalAppEntrySchema).optional(),\n\t})\n\t.strict();\n\nexport type KhalAppManifest = z.infer<typeof KhalAppManifestSchema>;\n\n/** Validate a raw object as KhalAppManifest. Throws ZodError with detailed messages on failure. */\nexport function validateManifest(raw: unknown): KhalAppManifest {\n\treturn KhalAppManifestSchema.parse(raw);\n}\n","/** Canonical role hierarchy from least to most privileged. */\nexport const ROLE_HIERARCHY = ['member', 'platform-dev', 'platform-admin', 'platform-owner'] as const;\nexport type Role = (typeof ROLE_HIERARCHY)[number];\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/bug-reporter.ts","../src/manifest.ts","../src/roles.ts"],"sourcesContent":["export type { KhalAuth } from './auth';\nexport type {\n\tBugSubmissionTicket,\n\tCaptureSurface,\n\tConsoleEntry,\n\tIdentityStamp,\n\tNetworkEntry,\n\tRedactedCaptureBundle,\n\tRedactionSummary,\n} from './bug-reporter';\nexport { BUG_REPORTER_CLIENT_SCHEMA_VERSION } from './bug-reporter';\nexport type {\n\tAppDeployConfig,\n\tAppDesktopConfig,\n\tAppEnvVar,\n\tAppManifest,\n\tAppManifestView,\n\tAppServiceConfig,\n\tAppTauriConfig,\n\tKhalAppEntry,\n\tKhalAppManifest,\n\tKhalPermission,\n\tKhalServiceSpec,\n\tKhalWindowSpec,\n\tSandboxConfig,\n\tSandboxMount,\n\tSandboxResourceSpec,\n\tServiceHealthConfig,\n} from './manifest';\nexport {\n\tKhalAppEntrySchema,\n\tKhalAppManifestSchema,\n\tvalidateManifest,\n} from './manifest';\nexport type { ConnectionState } from './nats';\nexport type { Role } from './roles';\nexport { ROLE_HIERARCHY } from './roles';\n","/**\n * Shared contract for the khal-os bug reporter.\n *\n * The shell extractor in `repos/desktop` produces a `RedactedCaptureBundle`\n * after running its non-bypassable redactor; the platform submission handler\n * in `repos/platform` accepts it and re-runs a defense-in-depth redaction\n * pass before minting a GitHub issue via the platform-held GitHub App.\n *\n * Both ends of that contract import these types from `@khal-os/types`, so a\n * future seam-extraction (typed package boundary, separate processes, etc.)\n * is a typed refactor rather than a wire-format rewrite.\n */\n\n/** Where the user invoked Cmd+Shift+B. Used for the 60-day drag-decision review. */\nexport type CaptureSurface =\n\t| 'app-window'\n\t| 'top-bar'\n\t| 'wallpaper'\n\t| 'notification'\n\t| 'modal'\n\t| 'agent';\n\n/**\n * Identity stamp captured at the moment of invocation. Servers MUST verify\n * that the stamped `userId`/`sessionId`/`customerId` match the authenticated\n * session before accepting the bundle. Cross-tenant mismatches are paged.\n */\nexport interface IdentityStamp {\n\t/** Authenticated user that pressed Cmd+Shift+B. */\n\tuserId: string;\n\t/** Active session id at capture time. Server validates against current session. */\n\tsessionId: string;\n\t/** Customer/tenant of the focused window. Cross-customer focus is hard-refused. */\n\tcustomerId: string;\n\t/** App id of the focused window (e.g. `terminal`, `files`, `genie`). */\n\tappId: string;\n\t/** Pod selector for server-side log fetch (last 5 minutes scoped to this pod). */\n\tpodSelector: string;\n\t/** Route within the focused app at capture time. */\n\troute: string;\n\t/** ISO-8601 timestamp the user confirmed the capture. */\n\tcapturedAt: string;\n\t/** Surface from which Cmd+Shift+B was triggered. */\n\tsurface: CaptureSurface;\n}\n\n/** A single console entry, body already redacted. */\nexport interface ConsoleEntry {\n\tlevel: 'log' | 'info' | 'warn' | 'error' | 'debug';\n\t/** ISO-8601 timestamp. */\n\ttimestamp: string;\n\t/** Redacted message text. The redactor strips JWTs/tokens/auth headers/etc. */\n\tmessage: string;\n\t/** Optional source location (file:line:col), if the runtime supplied one. */\n\tsource?: string;\n}\n\n/** A single network request/response, headers and bodies already redacted. */\nexport interface NetworkEntry {\n\tmethod: string;\n\t/** Redacted URL — query strings have known-sensitive params masked. */\n\turl: string;\n\tstatus?: number;\n\t/** ISO-8601 timestamp request started. */\n\tstartedAt: string;\n\t/** Duration in milliseconds, if the response completed. */\n\tdurationMs?: number;\n\t/** Redacted request headers. Auth-related headers are stripped, not masked. */\n\trequestHeaders: Record<string, string>;\n\t/** Redacted response headers. */\n\tresponseHeaders?: Record<string, string>;\n\t/**\n\t * Redacted, size-capped request body excerpt. May be truncated; truncation\n\t * is reflected in `requestBodyTruncated` and the count in `redactionSummary`.\n\t */\n\trequestBody?: string;\n\trequestBodyTruncated?: boolean;\n\tresponseBody?: string;\n\tresponseBodyTruncated?: boolean;\n}\n\n/**\n * Counts of what the redactor removed or masked, by category. Values are NEVER\n * the redacted strings themselves — the modal renders this as\n * \"3 auth headers stripped, 2 JWTs masked\" and shows no underlying content.\n */\nexport interface RedactionSummary {\n\t/** Auth-style headers stripped (Authorization, Cookie, X-Api-Key, Proxy-Authorization, X-Auth-*). */\n\tauthHeadersStripped: number;\n\t/** JWTs masked (eyJ-prefixed three-segment tokens). */\n\tjwtsMasked: number;\n\t/** Provider tokens masked (sk-, pk-, ghp_, github_pat_, AKIA…, xox[baprs]-). */\n\ttokensMasked: number;\n\t/** Password input values stripped. */\n\tpasswordInputsStripped: number;\n\t/** Cookies reduced to name-only (values stripped). */\n\tcookiesNameOnly: number;\n\t/** localStorage entries stripped. */\n\tlocalStorageStripped: number;\n\t/** sessionStorage entries stripped. */\n\tsessionStorageStripped: number;\n\t/** Email addresses masked. */\n\temailsMasked: number;\n\t/** Network request/response bodies truncated by the size cap. */\n\tbodiesTruncated: number;\n\t/** Anything the modal user explicitly click-to-redacted on top of automatic passes. */\n\tmanualRedactions: number;\n}\n\n/**\n * The redacted capture bundle the shell extractor returns. Submission to the\n * platform endpoint pairs this with a server-issued `BugSubmissionTicket`.\n */\nexport interface RedactedCaptureBundle {\n\t/** Stable client-side bundle id (UUID). */\n\tbundleId: string;\n\tstamp: IdentityStamp;\n\t/** Redacted DOM snapshot of the focused window only. */\n\tdom: string;\n\t/** Last-N console entries, redacted. */\n\tconsole: ConsoleEntry[];\n\t/** Last-N network entries, redacted. */\n\tnetwork: NetworkEntry[];\n\t/** Focused-window screenshot, base64-encoded PNG. */\n\tscreenshotPng: string;\n\t/** Last 5 minutes of pod logs scoped to `stamp.podSelector`, redacted. */\n\tpodLogTail: string;\n\tredactionSummary: RedactionSummary;\n\t/**\n\t * SHA-256 hex of the canonicalized bundle (everything except this field\n\t * and the screenshot bytes). The audit log records this so a leaked bundle\n\t * can be tied back to a specific submission without storing its contents.\n\t */\n\tbundleHash: string;\n\t/**\n\t * Schema version this bundle was produced against. Increment on any\n\t * structural change to the contract; the platform handler rejects bundles\n\t * whose version it does not understand.\n\t */\n\tclientSchemaVersion: 1;\n}\n\n/**\n * Server-issued, single-use, opaque token authorising one bundle submission.\n * The shell requests a ticket immediately before submit; the platform burns\n * it on first use. Re-submitting the same ticket is rejected and audited.\n */\nexport interface BugSubmissionTicket {\n\t/** Opaque token. Format is server-defined; clients MUST treat as a string. */\n\tticket: string;\n\t/** ISO-8601 expiration. Tickets are short-lived (single-digit minutes). */\n\texpiresAt: string;\n}\n\n/** Current schema version emitted by clients. */\nexport const BUG_REPORTER_CLIENT_SCHEMA_VERSION = 1 as const;\n","import { z } from 'zod';\nimport type { Role } from './roles';\n\n/** Desktop integration metadata for an app. */\nexport interface AppDesktopConfig {\n\t/** Path to the app icon (relative to the public directory). */\n\ticon: string;\n\t/** Categories for desktop launcher grouping. */\n\tcategories: string[];\n\t/** Short description shown in the desktop launcher. */\n\tcomment: string;\n}\n\n/** A single view within an app manifest. */\nexport interface AppManifestView {\n\t/** Unique view identifier within the app. */\n\tid: string;\n\t/** Human-readable label for the view. */\n\tlabel: string;\n\t/** Permission string required to access this view. */\n\tpermission: string;\n\t/** Minimum role level required. */\n\tminRole: Role;\n\t/** NATS subject segment after `khal.<orgId>.` for this view's services. */\n\tnatsPrefix?: string;\n\t/** Default window dimensions. */\n\tdefaultSize: { width: number; height: number };\n\t/** Relative path to the view's React component. */\n\tcomponent: string;\n}\n\n/** Health check configuration for a service. */\nexport interface ServiceHealthConfig {\n\t/** Check type: tcp (connect to port), http (GET endpoint), command (run shell). */\n\ttype: 'tcp' | 'http' | 'command';\n\t/** Target: port number for tcp, URL for http, shell command for command. */\n\ttarget: string | number;\n\t/** Check interval in milliseconds (default: 30000). */\n\tinterval?: number;\n\t/** Timeout in milliseconds (default: 5000). */\n\ttimeout?: number;\n}\n\n/** Service declaration within an app manifest. */\nexport interface AppServiceConfig {\n\t/** Service name (must be unique across the app). */\n\tname: string;\n\t/** Shell command to start the service (alternative to entry). */\n\tcommand?: string;\n\t/** Entry point file path relative to the package root. */\n\tentry?: string;\n\t/** Runtime environment. */\n\truntime?: 'node' | 'python';\n\t/** Health check configuration. */\n\thealth?: ServiceHealthConfig;\n\t/** Restart policy. */\n\trestart?: 'always' | 'on-failure' | 'never';\n\t/** Ports the service binds to internally. Khal assigns proxy ports. */\n\tports?: number[];\n}\n\n/** Environment variable declaration for app configuration. */\nexport interface AppEnvVar {\n\t/** Variable name (e.g., \"API_KEY\"). */\n\tkey: string;\n\t/** Human-readable description shown in the config UI. */\n\tdescription: string;\n\t/** Whether the variable is required for the app to run. */\n\trequired: boolean;\n\t/** Default value if not configured. */\n\tdefault?: string;\n\t/** Value type — affects config UI rendering and validation. */\n\ttype?: 'string' | 'number' | 'boolean' | 'secret' | 'url';\n\t/** Storage: 'config' for plain ConfigMap, 'vault' for Kubernetes Secret. */\n\tvisibility?: 'config' | 'vault';\n}\n\n/** Kubernetes deployment configuration for apps with backends. */\nexport interface AppDeployConfig {\n\t/** Dockerfile path relative to app root (default: \"Dockerfile\"). */\n\tdockerfile?: string;\n\t/** Build args passed to docker build. */\n\tbuildArgs?: Record<string, string>;\n\t/** Container port the app listens on. */\n\tport?: number;\n\t/** Resource requests and limits for the pod. */\n\tresources?: {\n\t\trequests?: { cpu?: string; memory?: string };\n\t\tlimits?: { cpu?: string; memory?: string };\n\t};\n\t/** Replica count (default: 1). */\n\treplicas?: number;\n\t/** Health check path for k8s readiness/liveness probes. */\n\thealthPath?: string;\n\t/** Ingress configuration for per-app routing. */\n\tingress?: {\n\t\t/** Subdomain prefix: <value>.apps.<domain>. Defaults to app id. */\n\t\tsubdomain?: string;\n\t\t/** Additional path prefixes to route to this app. */\n\t\tpathPrefixes?: string[];\n\t};\n\t/** Horizontal Pod Autoscaler configuration. */\n\tautoscaling?: {\n\t\tenabled: boolean;\n\t\tminReplicas?: number;\n\t\tmaxReplicas?: number;\n\t\ttargetCPU?: number;\n\t};\n\t/** Environment sources injected at runtime from k8s resources. */\n\tenvFrom?: Array<{ secretRef?: string; configMapRef?: string }>;\n}\n\n/** Sandbox resource specification for per-user container provisioning. */\nexport interface SandboxResourceSpec {\n\t/** CPU request (e.g., \"4\"). */\n\tcpu: string;\n\t/** Memory request (e.g., \"4Gi\"). */\n\tmemory: string;\n}\n\n/** Volume mount configuration for sandbox containers. */\nexport interface SandboxMount {\n\t/** Mount path inside the sandbox container. */\n\tmountPath: string;\n\t/** Source type: 'files' mounts user's Files PVC, 'pvc' mounts a named PVC. */\n\tsource: 'files' | 'pvc';\n\t/** PVC name (required when source is 'pvc'). */\n\tpvcName?: string;\n\t/** Read-only mount. Default: false. */\n\treadOnly?: boolean;\n}\n\n/**\n * Sandbox configuration for apps that require a per-user container.\n * When present on an app manifest, installing the app triggers sandbox\n * provisioning via `os.sandbox.create`.\n */\nexport interface SandboxConfig {\n\t/** Enable sandbox provisioning on install. */\n\tenabled: boolean;\n\t/** Base container image (e.g., \"khal-os/sandbox-ubuntu:latest\"). */\n\timage?: string;\n\t/** Resource requests for the sandbox pod. */\n\trequests?: SandboxResourceSpec;\n\t/** Resource limits for the sandbox pod (burstable). */\n\tlimits?: SandboxResourceSpec;\n\t/** Volume mounts inside the sandbox. */\n\tmounts?: SandboxMount[];\n\t/** Whether the sandbox user gets sudo access. Default: true. */\n\tsudoers?: boolean;\n\t/** NATS subject prefix for sandbox communication (default: \"os.sandbox\"). */\n\tnatsPrefix?: string;\n}\n\n/** Tauri standalone export configuration. */\nexport interface AppTauriConfig {\n\t/** Whether this app supports standalone Tauri export. */\n\texportable: boolean;\n\t/** Path to src-tauri/ directory (default: \"./src-tauri\"). */\n\ttauriDir?: string;\n\t/** App name for the exported binary. */\n\tappName?: string;\n\t/** App icon path relative to app root. */\n\ticon?: string;\n\t/** Window configuration for standalone mode. */\n\twindow?: { width?: number; height?: number; title?: string };\n}\n\n/**\n * Full app manifest — the type for `manifest.ts` files in KhalOS app packages.\n *\n * Every app package must export a default manifest conforming to this shape.\n * Use `defineManifest()` for compile-time validation and autocomplete.\n *\n * The JSON equivalent (`khal-app.json`) uses the same shape and is the\n * language-agnostic install-time contract that the marketplace reads.\n */\nexport interface AppManifest {\n\t/** Unique app identifier (must match the package directory name). */\n\tid: string;\n\t/** One or more views the app exposes. */\n\tviews: AppManifestView[];\n\t/** Desktop integration configuration. */\n\tdesktop: AppDesktopConfig;\n\t/** Backend services this app runs. Optional — pure UI apps have no services. */\n\tservices?: AppServiceConfig[];\n\n\t// ── v2 fields (all optional for backward compatibility) ──\n\n\t/** Schema version for forward compatibility (default: 1). */\n\tschemaVersion?: number;\n\t/** Human-readable app name. */\n\tname?: string;\n\t/** Semantic version of the app. */\n\tversion?: string;\n\t/** Short description for the marketplace listing. */\n\tdescription?: string;\n\t/** Author name or organization. */\n\tauthor?: string;\n\t/** SPDX license identifier. */\n\tlicense?: string;\n\t/** Source repository URL. */\n\trepository?: string;\n\t/** Minimum KhalOS host version required. */\n\tminHostVersion?: string;\n\t/** Environment variables the app needs — auto-generates config UI. */\n\tenv?: AppEnvVar[];\n\t/** Native Kubernetes deployment configuration. */\n\tdeploy?: AppDeployConfig;\n\t/** Tauri standalone export configuration. */\n\ttauri?: AppTauriConfig;\n\t/** URL to the app's pre-built ESM bundle for runtime loading by desktop. */\n\tbundleUrl?: string;\n\t/** Sandbox configuration — when present, installing this app provisions a per-user container. */\n\tsandbox?: SandboxConfig;\n}\n\n// ── Pack Contract Types (for standalone pack-* repos) ──\n\nexport const KhalPermission = z.enum([\n\t'nats:publish',\n\t'nats:subscribe',\n\t'files:read',\n\t'files:write',\n\t'pty:spawn',\n\t'http:fetch',\n\t'system:clipboard',\n\t'system:notifications',\n]);\nexport type KhalPermission = z.infer<typeof KhalPermission>;\n\nexport const KhalServiceSpec = z.object({\n\tname: z.string(),\n\tcommand: z.string().optional(),\n\tentry: z.string().optional(),\n\truntime: z.enum(['node', 'bun', 'python']).optional(),\n\tports: z.array(z.number()).optional(),\n\thealth: z\n\t\t.object({\n\t\t\ttype: z.enum(['tcp', 'http', 'command']),\n\t\t\ttarget: z.union([z.string(), z.number()]),\n\t\t\tinterval: z.number().optional(),\n\t\t\ttimeout: z.number().optional(),\n\t\t})\n\t\t.optional(),\n});\nexport type KhalServiceSpec = z.infer<typeof KhalServiceSpec>;\n\nexport const KhalWindowSpec = z.object({\n\tid: z.string(),\n\ttitle: z.string().optional(),\n\twidth: z.number().default(800),\n\theight: z.number().default(600),\n\tresizable: z.boolean().default(true),\n});\nexport type KhalWindowSpec = z.infer<typeof KhalWindowSpec>;\n\n/** A single app entry within a bundle pack manifest. */\nexport const KhalAppEntrySchema = z.object({\n\t/** Unique app identifier within the bundle. */\n\tid: z.string(),\n\t/** Human-readable app name. */\n\tname: z.string(),\n\t/** Frontend package reference for this app. */\n\tfrontend: z\n\t\t.object({\n\t\t\tpackage: z.string(),\n\t\t})\n\t\t.optional(),\n});\nexport type KhalAppEntry = z.infer<typeof KhalAppEntrySchema>;\n\nexport const KhalAppManifestSchema = z\n\t.object({\n\t\t$schema: z.string().optional(),\n\t\tid: z.string(),\n\t\tname: z.string(),\n\t\tversion: z.string(),\n\t\ticon: z.string(),\n\t\tdescription: z.string(),\n\t\tauthor: z.string(),\n\t\tpermissions: z.array(KhalPermission),\n\t\tservices: z.array(KhalServiceSpec).optional(),\n\t\twindows: z.array(KhalWindowSpec).optional(),\n\t\tfrontend: z\n\t\t\t.object({\n\t\t\t\tpackage: z.string(),\n\t\t\t\tentry: z.string().default('default'),\n\t\t\t})\n\t\t\t.optional(),\n\t\tbackend: z\n\t\t\t.object({\n\t\t\t\timage: z.string(),\n\t\t\t\thelmChart: z.string().optional(),\n\t\t\t\tenv: z.record(z.string(), z.string()),\n\t\t\t\tports: z.array(z.number()),\n\t\t\t})\n\t\t\t.optional(),\n\t\t/** Relative path to the pre-built ESM bundle within the package (e.g., \"dist/bundle.mjs\"). */\n\t\tbundlePath: z.string().optional(),\n\t\t/** Sandbox configuration for per-user container provisioning. */\n\t\tsandbox: z\n\t\t\t.object({\n\t\t\t\tenabled: z.boolean(),\n\t\t\t\timage: z.string().optional(),\n\t\t\t\trequests: z\n\t\t\t\t\t.object({ cpu: z.string(), memory: z.string() })\n\t\t\t\t\t.optional(),\n\t\t\t\tlimits: z\n\t\t\t\t\t.object({ cpu: z.string(), memory: z.string() })\n\t\t\t\t\t.optional(),\n\t\t\t\tmounts: z\n\t\t\t\t\t.array(\n\t\t\t\t\t\tz.object({\n\t\t\t\t\t\t\tmountPath: z.string(),\n\t\t\t\t\t\t\tsource: z.enum(['files', 'pvc']),\n\t\t\t\t\t\t\tpvcName: z.string().optional(),\n\t\t\t\t\t\t\treadOnly: z.boolean().optional(),\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\t.optional(),\n\t\t\t\tsudoers: z.boolean().optional(),\n\t\t\t\tnatsPrefix: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t\t/**\n\t\t * When apps is present, the root frontend field is ignored —\n\t\t * each entry has its own frontend.\n\t\t */\n\t\tapps: z.array(KhalAppEntrySchema).optional(),\n\t})\n\t.strict();\n\nexport type KhalAppManifest = z.infer<typeof KhalAppManifestSchema>;\n\n/** Validate a raw object as KhalAppManifest. Throws ZodError with detailed messages on failure. */\nexport function validateManifest(raw: unknown): KhalAppManifest {\n\treturn KhalAppManifestSchema.parse(raw);\n}\n","/** Canonical role hierarchy from least to most privileged. */\nexport const ROLE_HIERARCHY = ['member', 'platform-dev', 'platform-admin', 'platform-owner'] as const;\nexport type Role = (typeof ROLE_HIERARCHY)[number];\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -7,7 +7,155 @@ interface KhalAuth {
|
|
|
7
7
|
role: string;
|
|
8
8
|
permissions: string[];
|
|
9
9
|
loading: boolean;
|
|
10
|
+
/** WorkOS-provided profile fields propagated via the platform JWT. All
|
|
11
|
+
* optional — callers MUST fall back to `userId`/`email` when absent. */
|
|
12
|
+
email?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
picture?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Shared contract for the khal-os bug reporter.
|
|
19
|
+
*
|
|
20
|
+
* The shell extractor in `repos/desktop` produces a `RedactedCaptureBundle`
|
|
21
|
+
* after running its non-bypassable redactor; the platform submission handler
|
|
22
|
+
* in `repos/platform` accepts it and re-runs a defense-in-depth redaction
|
|
23
|
+
* pass before minting a GitHub issue via the platform-held GitHub App.
|
|
24
|
+
*
|
|
25
|
+
* Both ends of that contract import these types from `@khal-os/types`, so a
|
|
26
|
+
* future seam-extraction (typed package boundary, separate processes, etc.)
|
|
27
|
+
* is a typed refactor rather than a wire-format rewrite.
|
|
28
|
+
*/
|
|
29
|
+
/** Where the user invoked Cmd+Shift+B. Used for the 60-day drag-decision review. */
|
|
30
|
+
type CaptureSurface = 'app-window' | 'top-bar' | 'wallpaper' | 'notification' | 'modal' | 'agent';
|
|
31
|
+
/**
|
|
32
|
+
* Identity stamp captured at the moment of invocation. Servers MUST verify
|
|
33
|
+
* that the stamped `userId`/`sessionId`/`customerId` match the authenticated
|
|
34
|
+
* session before accepting the bundle. Cross-tenant mismatches are paged.
|
|
35
|
+
*/
|
|
36
|
+
interface IdentityStamp {
|
|
37
|
+
/** Authenticated user that pressed Cmd+Shift+B. */
|
|
38
|
+
userId: string;
|
|
39
|
+
/** Active session id at capture time. Server validates against current session. */
|
|
40
|
+
sessionId: string;
|
|
41
|
+
/** Customer/tenant of the focused window. Cross-customer focus is hard-refused. */
|
|
42
|
+
customerId: string;
|
|
43
|
+
/** App id of the focused window (e.g. `terminal`, `files`, `genie`). */
|
|
44
|
+
appId: string;
|
|
45
|
+
/** Pod selector for server-side log fetch (last 5 minutes scoped to this pod). */
|
|
46
|
+
podSelector: string;
|
|
47
|
+
/** Route within the focused app at capture time. */
|
|
48
|
+
route: string;
|
|
49
|
+
/** ISO-8601 timestamp the user confirmed the capture. */
|
|
50
|
+
capturedAt: string;
|
|
51
|
+
/** Surface from which Cmd+Shift+B was triggered. */
|
|
52
|
+
surface: CaptureSurface;
|
|
53
|
+
}
|
|
54
|
+
/** A single console entry, body already redacted. */
|
|
55
|
+
interface ConsoleEntry {
|
|
56
|
+
level: 'log' | 'info' | 'warn' | 'error' | 'debug';
|
|
57
|
+
/** ISO-8601 timestamp. */
|
|
58
|
+
timestamp: string;
|
|
59
|
+
/** Redacted message text. The redactor strips JWTs/tokens/auth headers/etc. */
|
|
60
|
+
message: string;
|
|
61
|
+
/** Optional source location (file:line:col), if the runtime supplied one. */
|
|
62
|
+
source?: string;
|
|
63
|
+
}
|
|
64
|
+
/** A single network request/response, headers and bodies already redacted. */
|
|
65
|
+
interface NetworkEntry {
|
|
66
|
+
method: string;
|
|
67
|
+
/** Redacted URL — query strings have known-sensitive params masked. */
|
|
68
|
+
url: string;
|
|
69
|
+
status?: number;
|
|
70
|
+
/** ISO-8601 timestamp request started. */
|
|
71
|
+
startedAt: string;
|
|
72
|
+
/** Duration in milliseconds, if the response completed. */
|
|
73
|
+
durationMs?: number;
|
|
74
|
+
/** Redacted request headers. Auth-related headers are stripped, not masked. */
|
|
75
|
+
requestHeaders: Record<string, string>;
|
|
76
|
+
/** Redacted response headers. */
|
|
77
|
+
responseHeaders?: Record<string, string>;
|
|
78
|
+
/**
|
|
79
|
+
* Redacted, size-capped request body excerpt. May be truncated; truncation
|
|
80
|
+
* is reflected in `requestBodyTruncated` and the count in `redactionSummary`.
|
|
81
|
+
*/
|
|
82
|
+
requestBody?: string;
|
|
83
|
+
requestBodyTruncated?: boolean;
|
|
84
|
+
responseBody?: string;
|
|
85
|
+
responseBodyTruncated?: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Counts of what the redactor removed or masked, by category. Values are NEVER
|
|
89
|
+
* the redacted strings themselves — the modal renders this as
|
|
90
|
+
* "3 auth headers stripped, 2 JWTs masked" and shows no underlying content.
|
|
91
|
+
*/
|
|
92
|
+
interface RedactionSummary {
|
|
93
|
+
/** Auth-style headers stripped (Authorization, Cookie, X-Api-Key, Proxy-Authorization, X-Auth-*). */
|
|
94
|
+
authHeadersStripped: number;
|
|
95
|
+
/** JWTs masked (eyJ-prefixed three-segment tokens). */
|
|
96
|
+
jwtsMasked: number;
|
|
97
|
+
/** Provider tokens masked (sk-, pk-, ghp_, github_pat_, AKIA…, xox[baprs]-). */
|
|
98
|
+
tokensMasked: number;
|
|
99
|
+
/** Password input values stripped. */
|
|
100
|
+
passwordInputsStripped: number;
|
|
101
|
+
/** Cookies reduced to name-only (values stripped). */
|
|
102
|
+
cookiesNameOnly: number;
|
|
103
|
+
/** localStorage entries stripped. */
|
|
104
|
+
localStorageStripped: number;
|
|
105
|
+
/** sessionStorage entries stripped. */
|
|
106
|
+
sessionStorageStripped: number;
|
|
107
|
+
/** Email addresses masked. */
|
|
108
|
+
emailsMasked: number;
|
|
109
|
+
/** Network request/response bodies truncated by the size cap. */
|
|
110
|
+
bodiesTruncated: number;
|
|
111
|
+
/** Anything the modal user explicitly click-to-redacted on top of automatic passes. */
|
|
112
|
+
manualRedactions: number;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The redacted capture bundle the shell extractor returns. Submission to the
|
|
116
|
+
* platform endpoint pairs this with a server-issued `BugSubmissionTicket`.
|
|
117
|
+
*/
|
|
118
|
+
interface RedactedCaptureBundle {
|
|
119
|
+
/** Stable client-side bundle id (UUID). */
|
|
120
|
+
bundleId: string;
|
|
121
|
+
stamp: IdentityStamp;
|
|
122
|
+
/** Redacted DOM snapshot of the focused window only. */
|
|
123
|
+
dom: string;
|
|
124
|
+
/** Last-N console entries, redacted. */
|
|
125
|
+
console: ConsoleEntry[];
|
|
126
|
+
/** Last-N network entries, redacted. */
|
|
127
|
+
network: NetworkEntry[];
|
|
128
|
+
/** Focused-window screenshot, base64-encoded PNG. */
|
|
129
|
+
screenshotPng: string;
|
|
130
|
+
/** Last 5 minutes of pod logs scoped to `stamp.podSelector`, redacted. */
|
|
131
|
+
podLogTail: string;
|
|
132
|
+
redactionSummary: RedactionSummary;
|
|
133
|
+
/**
|
|
134
|
+
* SHA-256 hex of the canonicalized bundle (everything except this field
|
|
135
|
+
* and the screenshot bytes). The audit log records this so a leaked bundle
|
|
136
|
+
* can be tied back to a specific submission without storing its contents.
|
|
137
|
+
*/
|
|
138
|
+
bundleHash: string;
|
|
139
|
+
/**
|
|
140
|
+
* Schema version this bundle was produced against. Increment on any
|
|
141
|
+
* structural change to the contract; the platform handler rejects bundles
|
|
142
|
+
* whose version it does not understand.
|
|
143
|
+
*/
|
|
144
|
+
clientSchemaVersion: 1;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Server-issued, single-use, opaque token authorising one bundle submission.
|
|
148
|
+
* The shell requests a ticket immediately before submit; the platform burns
|
|
149
|
+
* it on first use. Re-submitting the same ticket is rejected and audited.
|
|
150
|
+
*/
|
|
151
|
+
interface BugSubmissionTicket {
|
|
152
|
+
/** Opaque token. Format is server-defined; clients MUST treat as a string. */
|
|
153
|
+
ticket: string;
|
|
154
|
+
/** ISO-8601 expiration. Tickets are short-lived (single-digit minutes). */
|
|
155
|
+
expiresAt: string;
|
|
10
156
|
}
|
|
157
|
+
/** Current schema version emitted by clients. */
|
|
158
|
+
declare const BUG_REPORTER_CLIENT_SCHEMA_VERSION: 1;
|
|
11
159
|
|
|
12
160
|
/** Canonical role hierarchy from least to most privileged. */
|
|
13
161
|
declare const ROLE_HIERARCHY: readonly ["member", "platform-dev", "platform-admin", "platform-owner"];
|
|
@@ -374,4 +522,4 @@ declare function validateManifest(raw: unknown): KhalAppManifest;
|
|
|
374
522
|
/** Connection states reported by the WS relay. */
|
|
375
523
|
type ConnectionState = 'connected' | 'reconnecting' | 'disconnected' | 'auth_expired' | 'version_mismatch';
|
|
376
524
|
|
|
377
|
-
export { type AppDeployConfig, type AppDesktopConfig, type AppEnvVar, type AppManifest, type AppManifestView, type AppServiceConfig, type AppTauriConfig, type ConnectionState, type KhalAppEntry, KhalAppEntrySchema, type KhalAppManifest, KhalAppManifestSchema, type KhalAuth, KhalPermission, KhalServiceSpec, KhalWindowSpec, ROLE_HIERARCHY, type Role, type SandboxConfig, type SandboxMount, type SandboxResourceSpec, type ServiceHealthConfig, validateManifest };
|
|
525
|
+
export { type AppDeployConfig, type AppDesktopConfig, type AppEnvVar, type AppManifest, type AppManifestView, type AppServiceConfig, type AppTauriConfig, BUG_REPORTER_CLIENT_SCHEMA_VERSION, type BugSubmissionTicket, type CaptureSurface, type ConnectionState, type ConsoleEntry, type IdentityStamp, type KhalAppEntry, KhalAppEntrySchema, type KhalAppManifest, KhalAppManifestSchema, type KhalAuth, KhalPermission, KhalServiceSpec, KhalWindowSpec, type NetworkEntry, ROLE_HIERARCHY, type RedactedCaptureBundle, type RedactionSummary, type Role, type SandboxConfig, type SandboxMount, type SandboxResourceSpec, type ServiceHealthConfig, validateManifest };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,155 @@ interface KhalAuth {
|
|
|
7
7
|
role: string;
|
|
8
8
|
permissions: string[];
|
|
9
9
|
loading: boolean;
|
|
10
|
+
/** WorkOS-provided profile fields propagated via the platform JWT. All
|
|
11
|
+
* optional — callers MUST fall back to `userId`/`email` when absent. */
|
|
12
|
+
email?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
picture?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Shared contract for the khal-os bug reporter.
|
|
19
|
+
*
|
|
20
|
+
* The shell extractor in `repos/desktop` produces a `RedactedCaptureBundle`
|
|
21
|
+
* after running its non-bypassable redactor; the platform submission handler
|
|
22
|
+
* in `repos/platform` accepts it and re-runs a defense-in-depth redaction
|
|
23
|
+
* pass before minting a GitHub issue via the platform-held GitHub App.
|
|
24
|
+
*
|
|
25
|
+
* Both ends of that contract import these types from `@khal-os/types`, so a
|
|
26
|
+
* future seam-extraction (typed package boundary, separate processes, etc.)
|
|
27
|
+
* is a typed refactor rather than a wire-format rewrite.
|
|
28
|
+
*/
|
|
29
|
+
/** Where the user invoked Cmd+Shift+B. Used for the 60-day drag-decision review. */
|
|
30
|
+
type CaptureSurface = 'app-window' | 'top-bar' | 'wallpaper' | 'notification' | 'modal' | 'agent';
|
|
31
|
+
/**
|
|
32
|
+
* Identity stamp captured at the moment of invocation. Servers MUST verify
|
|
33
|
+
* that the stamped `userId`/`sessionId`/`customerId` match the authenticated
|
|
34
|
+
* session before accepting the bundle. Cross-tenant mismatches are paged.
|
|
35
|
+
*/
|
|
36
|
+
interface IdentityStamp {
|
|
37
|
+
/** Authenticated user that pressed Cmd+Shift+B. */
|
|
38
|
+
userId: string;
|
|
39
|
+
/** Active session id at capture time. Server validates against current session. */
|
|
40
|
+
sessionId: string;
|
|
41
|
+
/** Customer/tenant of the focused window. Cross-customer focus is hard-refused. */
|
|
42
|
+
customerId: string;
|
|
43
|
+
/** App id of the focused window (e.g. `terminal`, `files`, `genie`). */
|
|
44
|
+
appId: string;
|
|
45
|
+
/** Pod selector for server-side log fetch (last 5 minutes scoped to this pod). */
|
|
46
|
+
podSelector: string;
|
|
47
|
+
/** Route within the focused app at capture time. */
|
|
48
|
+
route: string;
|
|
49
|
+
/** ISO-8601 timestamp the user confirmed the capture. */
|
|
50
|
+
capturedAt: string;
|
|
51
|
+
/** Surface from which Cmd+Shift+B was triggered. */
|
|
52
|
+
surface: CaptureSurface;
|
|
53
|
+
}
|
|
54
|
+
/** A single console entry, body already redacted. */
|
|
55
|
+
interface ConsoleEntry {
|
|
56
|
+
level: 'log' | 'info' | 'warn' | 'error' | 'debug';
|
|
57
|
+
/** ISO-8601 timestamp. */
|
|
58
|
+
timestamp: string;
|
|
59
|
+
/** Redacted message text. The redactor strips JWTs/tokens/auth headers/etc. */
|
|
60
|
+
message: string;
|
|
61
|
+
/** Optional source location (file:line:col), if the runtime supplied one. */
|
|
62
|
+
source?: string;
|
|
63
|
+
}
|
|
64
|
+
/** A single network request/response, headers and bodies already redacted. */
|
|
65
|
+
interface NetworkEntry {
|
|
66
|
+
method: string;
|
|
67
|
+
/** Redacted URL — query strings have known-sensitive params masked. */
|
|
68
|
+
url: string;
|
|
69
|
+
status?: number;
|
|
70
|
+
/** ISO-8601 timestamp request started. */
|
|
71
|
+
startedAt: string;
|
|
72
|
+
/** Duration in milliseconds, if the response completed. */
|
|
73
|
+
durationMs?: number;
|
|
74
|
+
/** Redacted request headers. Auth-related headers are stripped, not masked. */
|
|
75
|
+
requestHeaders: Record<string, string>;
|
|
76
|
+
/** Redacted response headers. */
|
|
77
|
+
responseHeaders?: Record<string, string>;
|
|
78
|
+
/**
|
|
79
|
+
* Redacted, size-capped request body excerpt. May be truncated; truncation
|
|
80
|
+
* is reflected in `requestBodyTruncated` and the count in `redactionSummary`.
|
|
81
|
+
*/
|
|
82
|
+
requestBody?: string;
|
|
83
|
+
requestBodyTruncated?: boolean;
|
|
84
|
+
responseBody?: string;
|
|
85
|
+
responseBodyTruncated?: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Counts of what the redactor removed or masked, by category. Values are NEVER
|
|
89
|
+
* the redacted strings themselves — the modal renders this as
|
|
90
|
+
* "3 auth headers stripped, 2 JWTs masked" and shows no underlying content.
|
|
91
|
+
*/
|
|
92
|
+
interface RedactionSummary {
|
|
93
|
+
/** Auth-style headers stripped (Authorization, Cookie, X-Api-Key, Proxy-Authorization, X-Auth-*). */
|
|
94
|
+
authHeadersStripped: number;
|
|
95
|
+
/** JWTs masked (eyJ-prefixed three-segment tokens). */
|
|
96
|
+
jwtsMasked: number;
|
|
97
|
+
/** Provider tokens masked (sk-, pk-, ghp_, github_pat_, AKIA…, xox[baprs]-). */
|
|
98
|
+
tokensMasked: number;
|
|
99
|
+
/** Password input values stripped. */
|
|
100
|
+
passwordInputsStripped: number;
|
|
101
|
+
/** Cookies reduced to name-only (values stripped). */
|
|
102
|
+
cookiesNameOnly: number;
|
|
103
|
+
/** localStorage entries stripped. */
|
|
104
|
+
localStorageStripped: number;
|
|
105
|
+
/** sessionStorage entries stripped. */
|
|
106
|
+
sessionStorageStripped: number;
|
|
107
|
+
/** Email addresses masked. */
|
|
108
|
+
emailsMasked: number;
|
|
109
|
+
/** Network request/response bodies truncated by the size cap. */
|
|
110
|
+
bodiesTruncated: number;
|
|
111
|
+
/** Anything the modal user explicitly click-to-redacted on top of automatic passes. */
|
|
112
|
+
manualRedactions: number;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The redacted capture bundle the shell extractor returns. Submission to the
|
|
116
|
+
* platform endpoint pairs this with a server-issued `BugSubmissionTicket`.
|
|
117
|
+
*/
|
|
118
|
+
interface RedactedCaptureBundle {
|
|
119
|
+
/** Stable client-side bundle id (UUID). */
|
|
120
|
+
bundleId: string;
|
|
121
|
+
stamp: IdentityStamp;
|
|
122
|
+
/** Redacted DOM snapshot of the focused window only. */
|
|
123
|
+
dom: string;
|
|
124
|
+
/** Last-N console entries, redacted. */
|
|
125
|
+
console: ConsoleEntry[];
|
|
126
|
+
/** Last-N network entries, redacted. */
|
|
127
|
+
network: NetworkEntry[];
|
|
128
|
+
/** Focused-window screenshot, base64-encoded PNG. */
|
|
129
|
+
screenshotPng: string;
|
|
130
|
+
/** Last 5 minutes of pod logs scoped to `stamp.podSelector`, redacted. */
|
|
131
|
+
podLogTail: string;
|
|
132
|
+
redactionSummary: RedactionSummary;
|
|
133
|
+
/**
|
|
134
|
+
* SHA-256 hex of the canonicalized bundle (everything except this field
|
|
135
|
+
* and the screenshot bytes). The audit log records this so a leaked bundle
|
|
136
|
+
* can be tied back to a specific submission without storing its contents.
|
|
137
|
+
*/
|
|
138
|
+
bundleHash: string;
|
|
139
|
+
/**
|
|
140
|
+
* Schema version this bundle was produced against. Increment on any
|
|
141
|
+
* structural change to the contract; the platform handler rejects bundles
|
|
142
|
+
* whose version it does not understand.
|
|
143
|
+
*/
|
|
144
|
+
clientSchemaVersion: 1;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Server-issued, single-use, opaque token authorising one bundle submission.
|
|
148
|
+
* The shell requests a ticket immediately before submit; the platform burns
|
|
149
|
+
* it on first use. Re-submitting the same ticket is rejected and audited.
|
|
150
|
+
*/
|
|
151
|
+
interface BugSubmissionTicket {
|
|
152
|
+
/** Opaque token. Format is server-defined; clients MUST treat as a string. */
|
|
153
|
+
ticket: string;
|
|
154
|
+
/** ISO-8601 expiration. Tickets are short-lived (single-digit minutes). */
|
|
155
|
+
expiresAt: string;
|
|
10
156
|
}
|
|
157
|
+
/** Current schema version emitted by clients. */
|
|
158
|
+
declare const BUG_REPORTER_CLIENT_SCHEMA_VERSION: 1;
|
|
11
159
|
|
|
12
160
|
/** Canonical role hierarchy from least to most privileged. */
|
|
13
161
|
declare const ROLE_HIERARCHY: readonly ["member", "platform-dev", "platform-admin", "platform-owner"];
|
|
@@ -374,4 +522,4 @@ declare function validateManifest(raw: unknown): KhalAppManifest;
|
|
|
374
522
|
/** Connection states reported by the WS relay. */
|
|
375
523
|
type ConnectionState = 'connected' | 'reconnecting' | 'disconnected' | 'auth_expired' | 'version_mismatch';
|
|
376
524
|
|
|
377
|
-
export { type AppDeployConfig, type AppDesktopConfig, type AppEnvVar, type AppManifest, type AppManifestView, type AppServiceConfig, type AppTauriConfig, type ConnectionState, type KhalAppEntry, KhalAppEntrySchema, type KhalAppManifest, KhalAppManifestSchema, type KhalAuth, KhalPermission, KhalServiceSpec, KhalWindowSpec, ROLE_HIERARCHY, type Role, type SandboxConfig, type SandboxMount, type SandboxResourceSpec, type ServiceHealthConfig, validateManifest };
|
|
525
|
+
export { type AppDeployConfig, type AppDesktopConfig, type AppEnvVar, type AppManifest, type AppManifestView, type AppServiceConfig, type AppTauriConfig, BUG_REPORTER_CLIENT_SCHEMA_VERSION, type BugSubmissionTicket, type CaptureSurface, type ConnectionState, type ConsoleEntry, type IdentityStamp, type KhalAppEntry, KhalAppEntrySchema, type KhalAppManifest, KhalAppManifestSchema, type KhalAuth, KhalPermission, KhalServiceSpec, KhalWindowSpec, type NetworkEntry, ROLE_HIERARCHY, type RedactedCaptureBundle, type RedactionSummary, type Role, type SandboxConfig, type SandboxMount, type SandboxResourceSpec, type ServiceHealthConfig, validateManifest };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/bug-reporter.ts
|
|
2
|
+
var BUG_REPORTER_CLIENT_SCHEMA_VERSION = 1;
|
|
3
|
+
|
|
1
4
|
// src/manifest.ts
|
|
2
5
|
import { z } from "zod";
|
|
3
6
|
var KhalPermission = z.enum([
|
|
@@ -93,6 +96,7 @@ function validateManifest(raw) {
|
|
|
93
96
|
// src/roles.ts
|
|
94
97
|
var ROLE_HIERARCHY = ["member", "platform-dev", "platform-admin", "platform-owner"];
|
|
95
98
|
export {
|
|
99
|
+
BUG_REPORTER_CLIENT_SCHEMA_VERSION,
|
|
96
100
|
KhalAppEntrySchema,
|
|
97
101
|
KhalAppManifestSchema,
|
|
98
102
|
ROLE_HIERARCHY,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/manifest.ts","../src/roles.ts"],"sourcesContent":["import { z } from 'zod';\nimport type { Role } from './roles';\n\n/** Desktop integration metadata for an app. */\nexport interface AppDesktopConfig {\n\t/** Path to the app icon (relative to the public directory). */\n\ticon: string;\n\t/** Categories for desktop launcher grouping. */\n\tcategories: string[];\n\t/** Short description shown in the desktop launcher. */\n\tcomment: string;\n}\n\n/** A single view within an app manifest. */\nexport interface AppManifestView {\n\t/** Unique view identifier within the app. */\n\tid: string;\n\t/** Human-readable label for the view. */\n\tlabel: string;\n\t/** Permission string required to access this view. */\n\tpermission: string;\n\t/** Minimum role level required. */\n\tminRole: Role;\n\t/** NATS subject segment after `khal.<orgId>.` for this view's services. */\n\tnatsPrefix?: string;\n\t/** Default window dimensions. */\n\tdefaultSize: { width: number; height: number };\n\t/** Relative path to the view's React component. */\n\tcomponent: string;\n}\n\n/** Health check configuration for a service. */\nexport interface ServiceHealthConfig {\n\t/** Check type: tcp (connect to port), http (GET endpoint), command (run shell). */\n\ttype: 'tcp' | 'http' | 'command';\n\t/** Target: port number for tcp, URL for http, shell command for command. */\n\ttarget: string | number;\n\t/** Check interval in milliseconds (default: 30000). */\n\tinterval?: number;\n\t/** Timeout in milliseconds (default: 5000). */\n\ttimeout?: number;\n}\n\n/** Service declaration within an app manifest. */\nexport interface AppServiceConfig {\n\t/** Service name (must be unique across the app). */\n\tname: string;\n\t/** Shell command to start the service (alternative to entry). */\n\tcommand?: string;\n\t/** Entry point file path relative to the package root. */\n\tentry?: string;\n\t/** Runtime environment. */\n\truntime?: 'node' | 'python';\n\t/** Health check configuration. */\n\thealth?: ServiceHealthConfig;\n\t/** Restart policy. */\n\trestart?: 'always' | 'on-failure' | 'never';\n\t/** Ports the service binds to internally. Khal assigns proxy ports. */\n\tports?: number[];\n}\n\n/** Environment variable declaration for app configuration. */\nexport interface AppEnvVar {\n\t/** Variable name (e.g., \"API_KEY\"). */\n\tkey: string;\n\t/** Human-readable description shown in the config UI. */\n\tdescription: string;\n\t/** Whether the variable is required for the app to run. */\n\trequired: boolean;\n\t/** Default value if not configured. */\n\tdefault?: string;\n\t/** Value type — affects config UI rendering and validation. */\n\ttype?: 'string' | 'number' | 'boolean' | 'secret' | 'url';\n\t/** Storage: 'config' for plain ConfigMap, 'vault' for Kubernetes Secret. */\n\tvisibility?: 'config' | 'vault';\n}\n\n/** Kubernetes deployment configuration for apps with backends. */\nexport interface AppDeployConfig {\n\t/** Dockerfile path relative to app root (default: \"Dockerfile\"). */\n\tdockerfile?: string;\n\t/** Build args passed to docker build. */\n\tbuildArgs?: Record<string, string>;\n\t/** Container port the app listens on. */\n\tport?: number;\n\t/** Resource requests and limits for the pod. */\n\tresources?: {\n\t\trequests?: { cpu?: string; memory?: string };\n\t\tlimits?: { cpu?: string; memory?: string };\n\t};\n\t/** Replica count (default: 1). */\n\treplicas?: number;\n\t/** Health check path for k8s readiness/liveness probes. */\n\thealthPath?: string;\n\t/** Ingress configuration for per-app routing. */\n\tingress?: {\n\t\t/** Subdomain prefix: <value>.apps.<domain>. Defaults to app id. */\n\t\tsubdomain?: string;\n\t\t/** Additional path prefixes to route to this app. */\n\t\tpathPrefixes?: string[];\n\t};\n\t/** Horizontal Pod Autoscaler configuration. */\n\tautoscaling?: {\n\t\tenabled: boolean;\n\t\tminReplicas?: number;\n\t\tmaxReplicas?: number;\n\t\ttargetCPU?: number;\n\t};\n\t/** Environment sources injected at runtime from k8s resources. */\n\tenvFrom?: Array<{ secretRef?: string; configMapRef?: string }>;\n}\n\n/** Sandbox resource specification for per-user container provisioning. */\nexport interface SandboxResourceSpec {\n\t/** CPU request (e.g., \"4\"). */\n\tcpu: string;\n\t/** Memory request (e.g., \"4Gi\"). */\n\tmemory: string;\n}\n\n/** Volume mount configuration for sandbox containers. */\nexport interface SandboxMount {\n\t/** Mount path inside the sandbox container. */\n\tmountPath: string;\n\t/** Source type: 'files' mounts user's Files PVC, 'pvc' mounts a named PVC. */\n\tsource: 'files' | 'pvc';\n\t/** PVC name (required when source is 'pvc'). */\n\tpvcName?: string;\n\t/** Read-only mount. Default: false. */\n\treadOnly?: boolean;\n}\n\n/**\n * Sandbox configuration for apps that require a per-user container.\n * When present on an app manifest, installing the app triggers sandbox\n * provisioning via `os.sandbox.create`.\n */\nexport interface SandboxConfig {\n\t/** Enable sandbox provisioning on install. */\n\tenabled: boolean;\n\t/** Base container image (e.g., \"khal-os/sandbox-ubuntu:latest\"). */\n\timage?: string;\n\t/** Resource requests for the sandbox pod. */\n\trequests?: SandboxResourceSpec;\n\t/** Resource limits for the sandbox pod (burstable). */\n\tlimits?: SandboxResourceSpec;\n\t/** Volume mounts inside the sandbox. */\n\tmounts?: SandboxMount[];\n\t/** Whether the sandbox user gets sudo access. Default: true. */\n\tsudoers?: boolean;\n\t/** NATS subject prefix for sandbox communication (default: \"os.sandbox\"). */\n\tnatsPrefix?: string;\n}\n\n/** Tauri standalone export configuration. */\nexport interface AppTauriConfig {\n\t/** Whether this app supports standalone Tauri export. */\n\texportable: boolean;\n\t/** Path to src-tauri/ directory (default: \"./src-tauri\"). */\n\ttauriDir?: string;\n\t/** App name for the exported binary. */\n\tappName?: string;\n\t/** App icon path relative to app root. */\n\ticon?: string;\n\t/** Window configuration for standalone mode. */\n\twindow?: { width?: number; height?: number; title?: string };\n}\n\n/**\n * Full app manifest — the type for `manifest.ts` files in KhalOS app packages.\n *\n * Every app package must export a default manifest conforming to this shape.\n * Use `defineManifest()` for compile-time validation and autocomplete.\n *\n * The JSON equivalent (`khal-app.json`) uses the same shape and is the\n * language-agnostic install-time contract that the marketplace reads.\n */\nexport interface AppManifest {\n\t/** Unique app identifier (must match the package directory name). */\n\tid: string;\n\t/** One or more views the app exposes. */\n\tviews: AppManifestView[];\n\t/** Desktop integration configuration. */\n\tdesktop: AppDesktopConfig;\n\t/** Backend services this app runs. Optional — pure UI apps have no services. */\n\tservices?: AppServiceConfig[];\n\n\t// ── v2 fields (all optional for backward compatibility) ──\n\n\t/** Schema version for forward compatibility (default: 1). */\n\tschemaVersion?: number;\n\t/** Human-readable app name. */\n\tname?: string;\n\t/** Semantic version of the app. */\n\tversion?: string;\n\t/** Short description for the marketplace listing. */\n\tdescription?: string;\n\t/** Author name or organization. */\n\tauthor?: string;\n\t/** SPDX license identifier. */\n\tlicense?: string;\n\t/** Source repository URL. */\n\trepository?: string;\n\t/** Minimum KhalOS host version required. */\n\tminHostVersion?: string;\n\t/** Environment variables the app needs — auto-generates config UI. */\n\tenv?: AppEnvVar[];\n\t/** Native Kubernetes deployment configuration. */\n\tdeploy?: AppDeployConfig;\n\t/** Tauri standalone export configuration. */\n\ttauri?: AppTauriConfig;\n\t/** URL to the app's pre-built ESM bundle for runtime loading by desktop. */\n\tbundleUrl?: string;\n\t/** Sandbox configuration — when present, installing this app provisions a per-user container. */\n\tsandbox?: SandboxConfig;\n}\n\n// ── Pack Contract Types (for standalone pack-* repos) ──\n\nexport const KhalPermission = z.enum([\n\t'nats:publish',\n\t'nats:subscribe',\n\t'files:read',\n\t'files:write',\n\t'pty:spawn',\n\t'http:fetch',\n\t'system:clipboard',\n\t'system:notifications',\n]);\nexport type KhalPermission = z.infer<typeof KhalPermission>;\n\nexport const KhalServiceSpec = z.object({\n\tname: z.string(),\n\tcommand: z.string().optional(),\n\tentry: z.string().optional(),\n\truntime: z.enum(['node', 'bun', 'python']).optional(),\n\tports: z.array(z.number()).optional(),\n\thealth: z\n\t\t.object({\n\t\t\ttype: z.enum(['tcp', 'http', 'command']),\n\t\t\ttarget: z.union([z.string(), z.number()]),\n\t\t\tinterval: z.number().optional(),\n\t\t\ttimeout: z.number().optional(),\n\t\t})\n\t\t.optional(),\n});\nexport type KhalServiceSpec = z.infer<typeof KhalServiceSpec>;\n\nexport const KhalWindowSpec = z.object({\n\tid: z.string(),\n\ttitle: z.string().optional(),\n\twidth: z.number().default(800),\n\theight: z.number().default(600),\n\tresizable: z.boolean().default(true),\n});\nexport type KhalWindowSpec = z.infer<typeof KhalWindowSpec>;\n\n/** A single app entry within a bundle pack manifest. */\nexport const KhalAppEntrySchema = z.object({\n\t/** Unique app identifier within the bundle. */\n\tid: z.string(),\n\t/** Human-readable app name. */\n\tname: z.string(),\n\t/** Frontend package reference for this app. */\n\tfrontend: z\n\t\t.object({\n\t\t\tpackage: z.string(),\n\t\t})\n\t\t.optional(),\n});\nexport type KhalAppEntry = z.infer<typeof KhalAppEntrySchema>;\n\nexport const KhalAppManifestSchema = z\n\t.object({\n\t\t$schema: z.string().optional(),\n\t\tid: z.string(),\n\t\tname: z.string(),\n\t\tversion: z.string(),\n\t\ticon: z.string(),\n\t\tdescription: z.string(),\n\t\tauthor: z.string(),\n\t\tpermissions: z.array(KhalPermission),\n\t\tservices: z.array(KhalServiceSpec).optional(),\n\t\twindows: z.array(KhalWindowSpec).optional(),\n\t\tfrontend: z\n\t\t\t.object({\n\t\t\t\tpackage: z.string(),\n\t\t\t\tentry: z.string().default('default'),\n\t\t\t})\n\t\t\t.optional(),\n\t\tbackend: z\n\t\t\t.object({\n\t\t\t\timage: z.string(),\n\t\t\t\thelmChart: z.string().optional(),\n\t\t\t\tenv: z.record(z.string(), z.string()),\n\t\t\t\tports: z.array(z.number()),\n\t\t\t})\n\t\t\t.optional(),\n\t\t/** Relative path to the pre-built ESM bundle within the package (e.g., \"dist/bundle.mjs\"). */\n\t\tbundlePath: z.string().optional(),\n\t\t/** Sandbox configuration for per-user container provisioning. */\n\t\tsandbox: z\n\t\t\t.object({\n\t\t\t\tenabled: z.boolean(),\n\t\t\t\timage: z.string().optional(),\n\t\t\t\trequests: z\n\t\t\t\t\t.object({ cpu: z.string(), memory: z.string() })\n\t\t\t\t\t.optional(),\n\t\t\t\tlimits: z\n\t\t\t\t\t.object({ cpu: z.string(), memory: z.string() })\n\t\t\t\t\t.optional(),\n\t\t\t\tmounts: z\n\t\t\t\t\t.array(\n\t\t\t\t\t\tz.object({\n\t\t\t\t\t\t\tmountPath: z.string(),\n\t\t\t\t\t\t\tsource: z.enum(['files', 'pvc']),\n\t\t\t\t\t\t\tpvcName: z.string().optional(),\n\t\t\t\t\t\t\treadOnly: z.boolean().optional(),\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\t.optional(),\n\t\t\t\tsudoers: z.boolean().optional(),\n\t\t\t\tnatsPrefix: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t\t/**\n\t\t * When apps is present, the root frontend field is ignored —\n\t\t * each entry has its own frontend.\n\t\t */\n\t\tapps: z.array(KhalAppEntrySchema).optional(),\n\t})\n\t.strict();\n\nexport type KhalAppManifest = z.infer<typeof KhalAppManifestSchema>;\n\n/** Validate a raw object as KhalAppManifest. Throws ZodError with detailed messages on failure. */\nexport function validateManifest(raw: unknown): KhalAppManifest {\n\treturn KhalAppManifestSchema.parse(raw);\n}\n","/** Canonical role hierarchy from least to most privileged. */\nexport const ROLE_HIERARCHY = ['member', 'platform-dev', 'platform-admin', 'platform-owner'] as const;\nexport type Role = (typeof ROLE_HIERARCHY)[number];\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/bug-reporter.ts","../src/manifest.ts","../src/roles.ts"],"sourcesContent":["/**\n * Shared contract for the khal-os bug reporter.\n *\n * The shell extractor in `repos/desktop` produces a `RedactedCaptureBundle`\n * after running its non-bypassable redactor; the platform submission handler\n * in `repos/platform` accepts it and re-runs a defense-in-depth redaction\n * pass before minting a GitHub issue via the platform-held GitHub App.\n *\n * Both ends of that contract import these types from `@khal-os/types`, so a\n * future seam-extraction (typed package boundary, separate processes, etc.)\n * is a typed refactor rather than a wire-format rewrite.\n */\n\n/** Where the user invoked Cmd+Shift+B. Used for the 60-day drag-decision review. */\nexport type CaptureSurface =\n\t| 'app-window'\n\t| 'top-bar'\n\t| 'wallpaper'\n\t| 'notification'\n\t| 'modal'\n\t| 'agent';\n\n/**\n * Identity stamp captured at the moment of invocation. Servers MUST verify\n * that the stamped `userId`/`sessionId`/`customerId` match the authenticated\n * session before accepting the bundle. Cross-tenant mismatches are paged.\n */\nexport interface IdentityStamp {\n\t/** Authenticated user that pressed Cmd+Shift+B. */\n\tuserId: string;\n\t/** Active session id at capture time. Server validates against current session. */\n\tsessionId: string;\n\t/** Customer/tenant of the focused window. Cross-customer focus is hard-refused. */\n\tcustomerId: string;\n\t/** App id of the focused window (e.g. `terminal`, `files`, `genie`). */\n\tappId: string;\n\t/** Pod selector for server-side log fetch (last 5 minutes scoped to this pod). */\n\tpodSelector: string;\n\t/** Route within the focused app at capture time. */\n\troute: string;\n\t/** ISO-8601 timestamp the user confirmed the capture. */\n\tcapturedAt: string;\n\t/** Surface from which Cmd+Shift+B was triggered. */\n\tsurface: CaptureSurface;\n}\n\n/** A single console entry, body already redacted. */\nexport interface ConsoleEntry {\n\tlevel: 'log' | 'info' | 'warn' | 'error' | 'debug';\n\t/** ISO-8601 timestamp. */\n\ttimestamp: string;\n\t/** Redacted message text. The redactor strips JWTs/tokens/auth headers/etc. */\n\tmessage: string;\n\t/** Optional source location (file:line:col), if the runtime supplied one. */\n\tsource?: string;\n}\n\n/** A single network request/response, headers and bodies already redacted. */\nexport interface NetworkEntry {\n\tmethod: string;\n\t/** Redacted URL — query strings have known-sensitive params masked. */\n\turl: string;\n\tstatus?: number;\n\t/** ISO-8601 timestamp request started. */\n\tstartedAt: string;\n\t/** Duration in milliseconds, if the response completed. */\n\tdurationMs?: number;\n\t/** Redacted request headers. Auth-related headers are stripped, not masked. */\n\trequestHeaders: Record<string, string>;\n\t/** Redacted response headers. */\n\tresponseHeaders?: Record<string, string>;\n\t/**\n\t * Redacted, size-capped request body excerpt. May be truncated; truncation\n\t * is reflected in `requestBodyTruncated` and the count in `redactionSummary`.\n\t */\n\trequestBody?: string;\n\trequestBodyTruncated?: boolean;\n\tresponseBody?: string;\n\tresponseBodyTruncated?: boolean;\n}\n\n/**\n * Counts of what the redactor removed or masked, by category. Values are NEVER\n * the redacted strings themselves — the modal renders this as\n * \"3 auth headers stripped, 2 JWTs masked\" and shows no underlying content.\n */\nexport interface RedactionSummary {\n\t/** Auth-style headers stripped (Authorization, Cookie, X-Api-Key, Proxy-Authorization, X-Auth-*). */\n\tauthHeadersStripped: number;\n\t/** JWTs masked (eyJ-prefixed three-segment tokens). */\n\tjwtsMasked: number;\n\t/** Provider tokens masked (sk-, pk-, ghp_, github_pat_, AKIA…, xox[baprs]-). */\n\ttokensMasked: number;\n\t/** Password input values stripped. */\n\tpasswordInputsStripped: number;\n\t/** Cookies reduced to name-only (values stripped). */\n\tcookiesNameOnly: number;\n\t/** localStorage entries stripped. */\n\tlocalStorageStripped: number;\n\t/** sessionStorage entries stripped. */\n\tsessionStorageStripped: number;\n\t/** Email addresses masked. */\n\temailsMasked: number;\n\t/** Network request/response bodies truncated by the size cap. */\n\tbodiesTruncated: number;\n\t/** Anything the modal user explicitly click-to-redacted on top of automatic passes. */\n\tmanualRedactions: number;\n}\n\n/**\n * The redacted capture bundle the shell extractor returns. Submission to the\n * platform endpoint pairs this with a server-issued `BugSubmissionTicket`.\n */\nexport interface RedactedCaptureBundle {\n\t/** Stable client-side bundle id (UUID). */\n\tbundleId: string;\n\tstamp: IdentityStamp;\n\t/** Redacted DOM snapshot of the focused window only. */\n\tdom: string;\n\t/** Last-N console entries, redacted. */\n\tconsole: ConsoleEntry[];\n\t/** Last-N network entries, redacted. */\n\tnetwork: NetworkEntry[];\n\t/** Focused-window screenshot, base64-encoded PNG. */\n\tscreenshotPng: string;\n\t/** Last 5 minutes of pod logs scoped to `stamp.podSelector`, redacted. */\n\tpodLogTail: string;\n\tredactionSummary: RedactionSummary;\n\t/**\n\t * SHA-256 hex of the canonicalized bundle (everything except this field\n\t * and the screenshot bytes). The audit log records this so a leaked bundle\n\t * can be tied back to a specific submission without storing its contents.\n\t */\n\tbundleHash: string;\n\t/**\n\t * Schema version this bundle was produced against. Increment on any\n\t * structural change to the contract; the platform handler rejects bundles\n\t * whose version it does not understand.\n\t */\n\tclientSchemaVersion: 1;\n}\n\n/**\n * Server-issued, single-use, opaque token authorising one bundle submission.\n * The shell requests a ticket immediately before submit; the platform burns\n * it on first use. Re-submitting the same ticket is rejected and audited.\n */\nexport interface BugSubmissionTicket {\n\t/** Opaque token. Format is server-defined; clients MUST treat as a string. */\n\tticket: string;\n\t/** ISO-8601 expiration. Tickets are short-lived (single-digit minutes). */\n\texpiresAt: string;\n}\n\n/** Current schema version emitted by clients. */\nexport const BUG_REPORTER_CLIENT_SCHEMA_VERSION = 1 as const;\n","import { z } from 'zod';\nimport type { Role } from './roles';\n\n/** Desktop integration metadata for an app. */\nexport interface AppDesktopConfig {\n\t/** Path to the app icon (relative to the public directory). */\n\ticon: string;\n\t/** Categories for desktop launcher grouping. */\n\tcategories: string[];\n\t/** Short description shown in the desktop launcher. */\n\tcomment: string;\n}\n\n/** A single view within an app manifest. */\nexport interface AppManifestView {\n\t/** Unique view identifier within the app. */\n\tid: string;\n\t/** Human-readable label for the view. */\n\tlabel: string;\n\t/** Permission string required to access this view. */\n\tpermission: string;\n\t/** Minimum role level required. */\n\tminRole: Role;\n\t/** NATS subject segment after `khal.<orgId>.` for this view's services. */\n\tnatsPrefix?: string;\n\t/** Default window dimensions. */\n\tdefaultSize: { width: number; height: number };\n\t/** Relative path to the view's React component. */\n\tcomponent: string;\n}\n\n/** Health check configuration for a service. */\nexport interface ServiceHealthConfig {\n\t/** Check type: tcp (connect to port), http (GET endpoint), command (run shell). */\n\ttype: 'tcp' | 'http' | 'command';\n\t/** Target: port number for tcp, URL for http, shell command for command. */\n\ttarget: string | number;\n\t/** Check interval in milliseconds (default: 30000). */\n\tinterval?: number;\n\t/** Timeout in milliseconds (default: 5000). */\n\ttimeout?: number;\n}\n\n/** Service declaration within an app manifest. */\nexport interface AppServiceConfig {\n\t/** Service name (must be unique across the app). */\n\tname: string;\n\t/** Shell command to start the service (alternative to entry). */\n\tcommand?: string;\n\t/** Entry point file path relative to the package root. */\n\tentry?: string;\n\t/** Runtime environment. */\n\truntime?: 'node' | 'python';\n\t/** Health check configuration. */\n\thealth?: ServiceHealthConfig;\n\t/** Restart policy. */\n\trestart?: 'always' | 'on-failure' | 'never';\n\t/** Ports the service binds to internally. Khal assigns proxy ports. */\n\tports?: number[];\n}\n\n/** Environment variable declaration for app configuration. */\nexport interface AppEnvVar {\n\t/** Variable name (e.g., \"API_KEY\"). */\n\tkey: string;\n\t/** Human-readable description shown in the config UI. */\n\tdescription: string;\n\t/** Whether the variable is required for the app to run. */\n\trequired: boolean;\n\t/** Default value if not configured. */\n\tdefault?: string;\n\t/** Value type — affects config UI rendering and validation. */\n\ttype?: 'string' | 'number' | 'boolean' | 'secret' | 'url';\n\t/** Storage: 'config' for plain ConfigMap, 'vault' for Kubernetes Secret. */\n\tvisibility?: 'config' | 'vault';\n}\n\n/** Kubernetes deployment configuration for apps with backends. */\nexport interface AppDeployConfig {\n\t/** Dockerfile path relative to app root (default: \"Dockerfile\"). */\n\tdockerfile?: string;\n\t/** Build args passed to docker build. */\n\tbuildArgs?: Record<string, string>;\n\t/** Container port the app listens on. */\n\tport?: number;\n\t/** Resource requests and limits for the pod. */\n\tresources?: {\n\t\trequests?: { cpu?: string; memory?: string };\n\t\tlimits?: { cpu?: string; memory?: string };\n\t};\n\t/** Replica count (default: 1). */\n\treplicas?: number;\n\t/** Health check path for k8s readiness/liveness probes. */\n\thealthPath?: string;\n\t/** Ingress configuration for per-app routing. */\n\tingress?: {\n\t\t/** Subdomain prefix: <value>.apps.<domain>. Defaults to app id. */\n\t\tsubdomain?: string;\n\t\t/** Additional path prefixes to route to this app. */\n\t\tpathPrefixes?: string[];\n\t};\n\t/** Horizontal Pod Autoscaler configuration. */\n\tautoscaling?: {\n\t\tenabled: boolean;\n\t\tminReplicas?: number;\n\t\tmaxReplicas?: number;\n\t\ttargetCPU?: number;\n\t};\n\t/** Environment sources injected at runtime from k8s resources. */\n\tenvFrom?: Array<{ secretRef?: string; configMapRef?: string }>;\n}\n\n/** Sandbox resource specification for per-user container provisioning. */\nexport interface SandboxResourceSpec {\n\t/** CPU request (e.g., \"4\"). */\n\tcpu: string;\n\t/** Memory request (e.g., \"4Gi\"). */\n\tmemory: string;\n}\n\n/** Volume mount configuration for sandbox containers. */\nexport interface SandboxMount {\n\t/** Mount path inside the sandbox container. */\n\tmountPath: string;\n\t/** Source type: 'files' mounts user's Files PVC, 'pvc' mounts a named PVC. */\n\tsource: 'files' | 'pvc';\n\t/** PVC name (required when source is 'pvc'). */\n\tpvcName?: string;\n\t/** Read-only mount. Default: false. */\n\treadOnly?: boolean;\n}\n\n/**\n * Sandbox configuration for apps that require a per-user container.\n * When present on an app manifest, installing the app triggers sandbox\n * provisioning via `os.sandbox.create`.\n */\nexport interface SandboxConfig {\n\t/** Enable sandbox provisioning on install. */\n\tenabled: boolean;\n\t/** Base container image (e.g., \"khal-os/sandbox-ubuntu:latest\"). */\n\timage?: string;\n\t/** Resource requests for the sandbox pod. */\n\trequests?: SandboxResourceSpec;\n\t/** Resource limits for the sandbox pod (burstable). */\n\tlimits?: SandboxResourceSpec;\n\t/** Volume mounts inside the sandbox. */\n\tmounts?: SandboxMount[];\n\t/** Whether the sandbox user gets sudo access. Default: true. */\n\tsudoers?: boolean;\n\t/** NATS subject prefix for sandbox communication (default: \"os.sandbox\"). */\n\tnatsPrefix?: string;\n}\n\n/** Tauri standalone export configuration. */\nexport interface AppTauriConfig {\n\t/** Whether this app supports standalone Tauri export. */\n\texportable: boolean;\n\t/** Path to src-tauri/ directory (default: \"./src-tauri\"). */\n\ttauriDir?: string;\n\t/** App name for the exported binary. */\n\tappName?: string;\n\t/** App icon path relative to app root. */\n\ticon?: string;\n\t/** Window configuration for standalone mode. */\n\twindow?: { width?: number; height?: number; title?: string };\n}\n\n/**\n * Full app manifest — the type for `manifest.ts` files in KhalOS app packages.\n *\n * Every app package must export a default manifest conforming to this shape.\n * Use `defineManifest()` for compile-time validation and autocomplete.\n *\n * The JSON equivalent (`khal-app.json`) uses the same shape and is the\n * language-agnostic install-time contract that the marketplace reads.\n */\nexport interface AppManifest {\n\t/** Unique app identifier (must match the package directory name). */\n\tid: string;\n\t/** One or more views the app exposes. */\n\tviews: AppManifestView[];\n\t/** Desktop integration configuration. */\n\tdesktop: AppDesktopConfig;\n\t/** Backend services this app runs. Optional — pure UI apps have no services. */\n\tservices?: AppServiceConfig[];\n\n\t// ── v2 fields (all optional for backward compatibility) ──\n\n\t/** Schema version for forward compatibility (default: 1). */\n\tschemaVersion?: number;\n\t/** Human-readable app name. */\n\tname?: string;\n\t/** Semantic version of the app. */\n\tversion?: string;\n\t/** Short description for the marketplace listing. */\n\tdescription?: string;\n\t/** Author name or organization. */\n\tauthor?: string;\n\t/** SPDX license identifier. */\n\tlicense?: string;\n\t/** Source repository URL. */\n\trepository?: string;\n\t/** Minimum KhalOS host version required. */\n\tminHostVersion?: string;\n\t/** Environment variables the app needs — auto-generates config UI. */\n\tenv?: AppEnvVar[];\n\t/** Native Kubernetes deployment configuration. */\n\tdeploy?: AppDeployConfig;\n\t/** Tauri standalone export configuration. */\n\ttauri?: AppTauriConfig;\n\t/** URL to the app's pre-built ESM bundle for runtime loading by desktop. */\n\tbundleUrl?: string;\n\t/** Sandbox configuration — when present, installing this app provisions a per-user container. */\n\tsandbox?: SandboxConfig;\n}\n\n// ── Pack Contract Types (for standalone pack-* repos) ──\n\nexport const KhalPermission = z.enum([\n\t'nats:publish',\n\t'nats:subscribe',\n\t'files:read',\n\t'files:write',\n\t'pty:spawn',\n\t'http:fetch',\n\t'system:clipboard',\n\t'system:notifications',\n]);\nexport type KhalPermission = z.infer<typeof KhalPermission>;\n\nexport const KhalServiceSpec = z.object({\n\tname: z.string(),\n\tcommand: z.string().optional(),\n\tentry: z.string().optional(),\n\truntime: z.enum(['node', 'bun', 'python']).optional(),\n\tports: z.array(z.number()).optional(),\n\thealth: z\n\t\t.object({\n\t\t\ttype: z.enum(['tcp', 'http', 'command']),\n\t\t\ttarget: z.union([z.string(), z.number()]),\n\t\t\tinterval: z.number().optional(),\n\t\t\ttimeout: z.number().optional(),\n\t\t})\n\t\t.optional(),\n});\nexport type KhalServiceSpec = z.infer<typeof KhalServiceSpec>;\n\nexport const KhalWindowSpec = z.object({\n\tid: z.string(),\n\ttitle: z.string().optional(),\n\twidth: z.number().default(800),\n\theight: z.number().default(600),\n\tresizable: z.boolean().default(true),\n});\nexport type KhalWindowSpec = z.infer<typeof KhalWindowSpec>;\n\n/** A single app entry within a bundle pack manifest. */\nexport const KhalAppEntrySchema = z.object({\n\t/** Unique app identifier within the bundle. */\n\tid: z.string(),\n\t/** Human-readable app name. */\n\tname: z.string(),\n\t/** Frontend package reference for this app. */\n\tfrontend: z\n\t\t.object({\n\t\t\tpackage: z.string(),\n\t\t})\n\t\t.optional(),\n});\nexport type KhalAppEntry = z.infer<typeof KhalAppEntrySchema>;\n\nexport const KhalAppManifestSchema = z\n\t.object({\n\t\t$schema: z.string().optional(),\n\t\tid: z.string(),\n\t\tname: z.string(),\n\t\tversion: z.string(),\n\t\ticon: z.string(),\n\t\tdescription: z.string(),\n\t\tauthor: z.string(),\n\t\tpermissions: z.array(KhalPermission),\n\t\tservices: z.array(KhalServiceSpec).optional(),\n\t\twindows: z.array(KhalWindowSpec).optional(),\n\t\tfrontend: z\n\t\t\t.object({\n\t\t\t\tpackage: z.string(),\n\t\t\t\tentry: z.string().default('default'),\n\t\t\t})\n\t\t\t.optional(),\n\t\tbackend: z\n\t\t\t.object({\n\t\t\t\timage: z.string(),\n\t\t\t\thelmChart: z.string().optional(),\n\t\t\t\tenv: z.record(z.string(), z.string()),\n\t\t\t\tports: z.array(z.number()),\n\t\t\t})\n\t\t\t.optional(),\n\t\t/** Relative path to the pre-built ESM bundle within the package (e.g., \"dist/bundle.mjs\"). */\n\t\tbundlePath: z.string().optional(),\n\t\t/** Sandbox configuration for per-user container provisioning. */\n\t\tsandbox: z\n\t\t\t.object({\n\t\t\t\tenabled: z.boolean(),\n\t\t\t\timage: z.string().optional(),\n\t\t\t\trequests: z\n\t\t\t\t\t.object({ cpu: z.string(), memory: z.string() })\n\t\t\t\t\t.optional(),\n\t\t\t\tlimits: z\n\t\t\t\t\t.object({ cpu: z.string(), memory: z.string() })\n\t\t\t\t\t.optional(),\n\t\t\t\tmounts: z\n\t\t\t\t\t.array(\n\t\t\t\t\t\tz.object({\n\t\t\t\t\t\t\tmountPath: z.string(),\n\t\t\t\t\t\t\tsource: z.enum(['files', 'pvc']),\n\t\t\t\t\t\t\tpvcName: z.string().optional(),\n\t\t\t\t\t\t\treadOnly: z.boolean().optional(),\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\t.optional(),\n\t\t\t\tsudoers: z.boolean().optional(),\n\t\t\t\tnatsPrefix: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t\t/**\n\t\t * When apps is present, the root frontend field is ignored —\n\t\t * each entry has its own frontend.\n\t\t */\n\t\tapps: z.array(KhalAppEntrySchema).optional(),\n\t})\n\t.strict();\n\nexport type KhalAppManifest = z.infer<typeof KhalAppManifestSchema>;\n\n/** Validate a raw object as KhalAppManifest. Throws ZodError with detailed messages on failure. */\nexport function validateManifest(raw: unknown): KhalAppManifest {\n\treturn KhalAppManifestSchema.parse(raw);\n}\n","/** Canonical role hierarchy from least to most privileged. */\nexport const ROLE_HIERARCHY = ['member', 'platform-dev', 'platform-admin', 'platform-owner'] as const;\nexport type Role = (typeof ROLE_HIERARCHY)[number];\n"],"mappings":"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","names":[]}
|